> ## Documentation Index
> Fetch the complete documentation index at: https://docs.livepeer.org/llms.txt
> Use this file to discover all available pages before exploring further.

# Video and livestream on Livepeer

> How video transcoding and livestreaming work on the Livepeer network: RTMP ingest, ABR HLS output, self-hosted gateways, and the Livepeer REST API and SDKs.

export const CenteredContainer = ({children, maxWidth = "800px", padding = "0", preset = "default", width = "", minWidth = "", marginRight = "", marginBottom = "", textAlign = "", style = {}, className = "", ...rest}) => {
  const presets = {
    default: {},
    fitContent: {
      width: "fit-content",
      minWidth: "fit-content"
    },
    readable70: {
      width: "70%",
      minWidth: "fit-content"
    },
    readable80: {
      width: "80%",
      minWidth: "fit-content"
    },
    readable90: {
      width: "90%"
    },
    wide900: {
      maxWidth: "900px"
    }
  };
  const presetStyle = presets[preset] || presets.default;
  return <div className={className} style={{
    maxWidth: presetStyle.maxWidth || maxWidth,
    margin: "0 auto",
    padding: padding,
    ...presetStyle.width ? {
      width: presetStyle.width
    } : {},
    ...presetStyle.minWidth ? {
      minWidth: presetStyle.minWidth
    } : {},
    ...width ? {
      width
    } : {},
    ...minWidth ? {
      minWidth
    } : {},
    ...marginRight ? {
      marginRight
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...textAlign ? {
      textAlign
    } : {},
    ...style
  }} {...rest}>
      {children}
    </div>;
};

export const CustomDivider = ({color = "var(--lp-color-border-default)", middleText = "", spacing = "default", style = {}, className = "", ...rest}) => {
  const spacingPresets = {
    default: {
      margin: "24px 0"
    },
    overlap: {
      margin: "-1rem 0 -1rem 0"
    },
    tight: {
      margin: "0 0 -1rem 0"
    },
    section: {
      margin: "0 0 -2rem 0"
    },
    sectionOverlap: {
      margin: "-1rem 0 -2rem 0"
    },
    deepOverlap: {
      margin: "-1rem 0 -1.5rem 0"
    }
  };
  const spacingStyle = spacingPresets[spacing] || spacingPresets.default;
  return <div role="separator" aria-orientation="horizontal" className={className} style={{
    display: "flex",
    alignItems: "center",
    ...spacingStyle,
    fontSize: style?.fontSize || "16px",
    height: "fit-content",
    ...style
  }} {...rest}>
      <span style={{
    marginRight: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
      </span>
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      {middleText && <>
          <Icon icon="circle" size={2} />
          <span style={{
    margin: "0 8px",
    fontWeight: "bold",
    color: color,
    opacity: 0.7
  }}>
            {middleText}
          </span>
          <Icon icon="circle" size={2} />
        </>}
      <div style={{
    flex: 1,
    height: "1px",
    background: "var(--lp-color-border-default)",
    opacity: 0.4
  }}></div>
      <span style={{
    marginLeft: "var(--lp-spacing-px-8)",
    opacity: 0.2
  }}>
        <span style={{
    display: "inline-block",
    transform: "scaleX(-1)"
  }}>
          <Icon icon="/snippets/assets/logos/Livepeer-Logo-Symbol-Theme.svg" />
        </span>
      </span>
    </div>;
};

export const TableCell = ({children, align = "left", header = false, style = {}, className = "", ...rest}) => {
  const Component = header ? "th" : "td";
  return <Component className={className} style={{
    padding: "0.75rem 1rem",
    textAlign: align,
    border: header ? "none" : "1px solid var(--lp-color-border-default)",
    ...style
  }} {...rest}>
      {children}
    </Component>;
};

export const TableRow = ({children, header = false, hover = false, style = {}, className = "", ...rest}) => {
  const rowId = `table-row-${Math.random().toString(36).substr(2, 9)}`;
  return <>
      {hover && <style>{`
          #${rowId}:hover {
            background-color: var(--lp-color-bg-card);
          }
        `}</style>}
      <tr id={rowId} className={className} style={{
    ...header && ({
      backgroundColor: "var(--lp-color-accent-strong)",
      color: "var(--lp-color-on-accent)",
      fontWeight: "bold"
    }),
    ...style
  }} {...rest}>
        {children}
      </tr>
    </>;
};

export const StyledTable = ({children, variant = "default", style = {}, className = "", ...rest}) => {
  const wrapperVariants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)",
      overflow: "hidden"
    },
    bordered: {
      border: "2px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-page)",
      overflow: "hidden"
    },
    minimal: {
      border: "none",
      backgroundColor: "transparent",
      overflow: "visible"
    }
  };
  return <div data-docs-styled-table-shell className={className} style={{
    width: "100%",
    padding: 0,
    margin: 0,
    ...wrapperVariants[variant],
    ...style
  }} {...rest}>
      <table data-docs-styled-table style={{
    width: "100%",
    borderCollapse: "collapse",
    borderSpacing: 0,
    margin: 0,
    backgroundColor: "transparent"
  }}>
        {children}
      </table>
    </div>;
};

<CenteredContainer preset="readable90">
  <Tip>Video is the original Livepeer use case and the most mature part of the network. Orchestrators transcode RTMP into ABR HLS using GPU hardware, paid per pixel through Probabilistic Micropayments.</Tip>
</CenteredContainer>

<CustomDivider />

Livepeer provides GPU-backed video transcoding and delivery for both live streams and recorded video. An encoder pushes a single-quality RTMP stream to a Gateway node; the network's Orchestrators transcode it into multiple adaptive bitrate renditions and return the output as HLS for viewers.

Video workloads use LPT stake-weighted routing and round-based rewards. This is the protocol's original economic model, running since 2018.

<CustomDivider />

## Video workload types

**Livestreams** accept a continuous RTMP push from OBS, FFmpeg, or any RTMP-capable encoder. The Gateway segments the stream, routes segments to Orchestrators for transcoding, and serves the transcoded output as adaptive bitrate HLS. Sub-second WebRTC playback is available for latency-sensitive applications.

**VOD assets** accept an uploaded video file. The network transcodes it to multiple renditions. The asset is playable via HLS or short-form MP4.

**Multistream** simultaneously pushes a live stream to additional RTMP destinations (YouTube Live, Twitch, custom RTMP servers) alongside Livepeer transcoding.

**Recording** archives a livestream as a VOD asset when the stream ends. The recording is available for playback after transcoding completes.

<CustomDivider />

## Access paths

Two paths exist for video developers, depending on how much infrastructure you want to operate.

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Path</TableCell>
      <TableCell header>What it provides</TableCell>
      <TableCell header>Requirements</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>**Livepeer REST API + SDKs**</TableCell>
      <TableCell>Managed stream lifecycle, asset storage, playback URLs, webhooks, access control, viewership metrics</TableCell>
      <TableCell>API key. No infrastructure to operate.</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>**Self-hosted Gateway (go-livepeer)**</TableCell>
      <TableCell>Direct network access with custom Orchestrator selection, pricing control, and RTMP-to-HLS without a managed API layer</TableCell>
      <TableCell>go-livepeer binary, ETH on Arbitrum One for TicketBroker deposit, Arbitrum RPC endpoint</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

The REST API is the faster path: create a stream with four lines of code and get an RTMP ingest URL and HLS playback URL. The self-hosted Gateway gives full pricing and routing control but requires operating a node and funding a payment deposit.

<CustomDivider />

## REST API and SDKs

The Livepeer REST API covers streams, assets, webhooks, playback, access control, and viewership metrics. SDKs wrap the API in TypeScript, Python, Go, and React:

<Tabs>
  <Tab title="TypeScript">
    ```typescript icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    import { Livepeer } from 'livepeer';

    const client = new Livepeer({ apiKey: process.env.LIVEPEER_API_KEY });

    const { stream } = await client.stream.create({
      name: 'my-stream',
      profiles: [
        { name: '720p', bitrate: 3000000, fps: 30, width: 1280, height: 720 },
        { name: '360p', bitrate: 1000000, fps: 30, width: 640, height: 360 },
      ],
    });

    console.log(stream.rtmpIngestUrl); // push here from OBS / FFmpeg
    console.log(stream.playbackId);    // use in @livepeer/react Player
    ```
  </Tab>

  <Tab title="Python">
    ```python icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
    from livepeer import Livepeer
    import os

    client = Livepeer(api_key=os.environ['LIVEPEER_API_KEY'])

    result = client.stream.create(request={'name': 'my-stream'})
    print(result.stream.rtmp_ingest_url)
    print(result.stream.playback_id)
    ```
  </Tab>
</Tabs>

The `@livepeer/react` package provides a `Player` component for HLS and WebRTC playback and a `Broadcast` component for browser-based WHIP publishing:

```bash icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
npm install @livepeer/react
```

<CustomDivider />

## Self-hosted Gateway

Running a go-livepeer broadcaster node gives direct network access. The node accepts RTMP on port 1935, segments the stream, routes segments to Orchestrators for transcoding, and serves HLS on port 8935.

```bash icon="terminal" theme={"theme":{"light":"github-light","dark":"dark-plus"}}
livepeer \
  -broadcaster \
  -network arbitrum-one-mainnet \
  -ethUrl <ARBITRUM_RPC_URL> \
  -ethKeystorePath /path/to/keystore \
  -maxPricePerUnit 0 \
  -rtmpAddr 127.0.0.1:1935 \
  -httpAddr 127.0.0.1:8935
```

The Gateway's ETH deposit in TicketBroker on Arbitrum One must be funded before jobs are accepted. Orchestrators check the deposit before processing segments.

Self-hosted Gateways give you control over Orchestrator selection (price filtering, explicit inclusion/exclusion), payment parameters, and routing. This path is relevant when operating at scale where per-pixel cost matters, or when you need to run alongside custom middleware.

See [local Gateway setup](/v2/developers/guides/local-development/local-Gateway) for the step-by-step development path.

<CustomDivider />

## Transport protocols

| Layer                          | Protocol      | Direction               |
| ------------------------------ | ------------- | ----------------------- |
| Live ingest                    | RTMP          | Encoder to Gateway      |
| VOD and standard live playback | HLS (ABR)     | Gateway/CDN to player   |
| Low-latency live playback      | WebRTC (WHEP) | Gateway to browser      |
| Browser publish                | WebRTC (WHIP) | Browser to Gateway      |
| Segment submission (internal)  | HTTP/HTTPS    | Gateway to Orchestrator |

RTMP is the standard ingest format. HLS carries 5-15 seconds of glass-to-glass latency for live streams due to segment buffering. WebRTC provides sub-second latency; the `@livepeer/react` Player uses WHEP automatically for live streams when the Gateway supports it.

<CustomDivider />

## Pricing

Video transcoding on the Livepeer Network is priced in wei per pixel. Total output pixels across all rendition profiles per segment, multiplied by the Orchestrator's price, gives the expected payment for that segment.

Orchestrators set their price via `-pricePerUnit`. Gateways set a maximum acceptable price via `-maxPricePerUnit`. Orchestrators above the maximum are excluded from selection.

The Livepeer Explorer at `explorer.livepeer.org` shows historical per-pixel prices across the active Orchestrator set.

<CustomDivider />

The [video overview](/v2/developers/build/video/overview) in the Build section covers ingest configuration, transcoding profiles, VOD upload, and player integration in detail.
