> ## 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.

# Transport Protocols

> The transport protocols used in Livepeer: RTMP for ingest, HLS for delivery, WebRTC for low-latency playback and broadcast, and trickle for real-time AI.

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>;
};

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 LinkArrow = ({href, label, description, newline = true, borderColor, className = '', style = {}, ...rest}) => {
  const linkArrowStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    justifyContent: 'center',
    gap: "var(--lp-spacing-1)",
    width: 'fit-content',
    ...borderColor && ({
      borderColor
    })
  };
  return <span className={className} style={style} {...rest}>
      {newline && <br />}
      <span style={linkArrowStyle}>
        <a href={href} target="_blank" rel="noopener noreferrer">
          {label}
        </a>
        <Icon icon="arrow-up-right" size={14} color="var(--lp-color-accent)" />
      </span>
      {description && description}
      {description && <div style={{
    height: "var(--lp-spacing-3)"
  }} />}
    </span>;
};

<CenteredContainer preset="readable90">
  <Tip>Livepeer uses different protocols at different points in the pipeline. RTMP arrives from the encoder; HLS leaves for the viewer; WebRTC connects the browser player; trickle connects real-time AI components internally.</Tip>
</CenteredContainer>

***

Livepeer's video pipeline spans four distinct transport layers. The choice of protocol at each layer is determined by the requirements of that layer: encoder compatibility at ingest, CDN delivery at playback, sub-second latency in the browser, and persistent low-latency framing for real-time AI.

<CustomDivider />

## Protocol by Layer

<StyledTable variant="bordered">
  <thead>
    <TableRow header>
      <TableCell header>Layer</TableCell>
      <TableCell header>Protocol</TableCell>
      <TableCell header>Direction</TableCell>
    </TableRow>
  </thead>

  <tbody>
    <TableRow>
      <TableCell>Live ingest from encoder</TableCell>
      <TableCell>RTMP</TableCell>
      <TableCell>Encoder → Gateway</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>VOD and standard live playback</TableCell>
      <TableCell>HLS (ABR)</TableCell>
      <TableCell>Gateway/CDN → Player</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Low-latency live playback</TableCell>
      <TableCell>WebRTC (WHEP)</TableCell>
      <TableCell>Gateway → Browser player</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Browser publish (Broadcast component)</TableCell>
      <TableCell>WebRTC (WHIP)</TableCell>
      <TableCell>Browser → Gateway</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Real-time AI frame pipeline</TableCell>
      <TableCell>Trickle (HTTP/1.1)</TableCell>
      <TableCell>Gateway ↔ Orchestrator ↔ ai-runner</TableCell>
    </TableRow>

    <TableRow>
      <TableCell>Segment submission (transcoding)</TableCell>
      <TableCell>HTTP/HTTPS</TableCell>
      <TableCell>Gateway → Orchestrator</TableCell>
    </TableRow>
  </tbody>
</StyledTable>

<CustomDivider />

## RTMP (Ingest)

RTMP (Real-Time Messaging Protocol) is the standard input format for live streams. OBS, FFmpeg, and all professional encoders support it. go-livepeer accepts RTMP on port 1935 by default (`-rtmpAddr` flag).

The stream name is taken from the first path segment of the RTMP URL: `rtmp://localhost/stream/<name>`. The HLS output manifest appears at `http://localhost:8935/stream/<name>.m3u8`. A stream key can be appended to the RTMP URL to prevent unauthorised playback.

RTMP is an ingest-only protocol in modern Livepeer deployments. Playback goes through HLS or WebRTC.

See <LinkArrow href="/v2/developers/guides/transport/trickle-protocol" label="RTMP" newline={false} /> for flag reference and stream key configuration.

<CustomDivider />

## HLS (Playback)

HLS (HTTP Live Streaming) is the standard delivery format for VOD assets and standard live streams. Livepeer outputs ABR HLS: a top-level manifest listing multiple rendition playlists, one per transcoding profile. Players (including the `@livepeer/react` Player) select the appropriate rendition based on available bandwidth.

HLS carries 5-15 seconds of glass-to-glass latency for live streams due to segment buffering. This is acceptable for event streams and VOD but not for interactive scenarios.

See <LinkArrow href="/v2/developers/guides/transport/trickle-protocol" label="HLS" newline={false} /> for manifest structure and ABR configuration.

<CustomDivider />

## WebRTC (Low-Latency Live)

WebRTC provides sub-second glass-to-glass latency for live streams. The `@livepeer/react` Player uses WHEP (WebRTC-HTTP Egress Protocol) for low-latency playback and the Broadcast component uses WHIP (WebRTC-HTTP Ingestion Protocol) for browser-based publishing.

WHIP was standardised by the IETF as RFC 9725 in March 2025. It provides a single HTTP POST for WebRTC session establishment, replacing the proprietary signalling protocols previously required for WebRTC ingest.

WebRTC requires ICE server infrastructure (STUN/TURN) for NAT traversal. Livepeer's managed Gateway infrastructure handles this; self-hosted Gateways require ICE server configuration.

See <LinkArrow href="/v2/developers/guides/transport/trickle-protocol" label="WebRTC" newline={false} /> for WHIP/WHEP endpoint details and ICE configuration.

<CustomDivider />

## Trickle (Real-Time AI)

The trickle protocol (`j0sh/http-trickle`) is an HTTP/1.1-based streaming transport for real-time AI frame pipelines. It splits a media stream into sequential short segments delivered as soon as they are produced. The Gateway opens a persistent HTTP connection to the Orchestrator; the Orchestrator forwards frames to the ai-runner over a second trickle connection.

Trickle is an internal protocol between Livepeer components. Developers interact with it indirectly via the `live-video-to-video` pipeline endpoint and the PyTrickle `StreamServer` interface.

See <LinkArrow href="/v2/developers/build/ai-and-agents/realtime-ai/overview" label="Real-Time AI Overview" newline={false} /> for the Cascade architecture that uses trickle.

<CustomDivider />

## Related Pages

<CardGroup cols={2}>
  <Card title="RTMP" icon="broadcast" href="/v2/developers/guides/transport/trickle-protocol" arrow horizontal>
    go-livepeer RTMP ingest: flags, stream keys, and webhook authentication.
  </Card>

  <Card title="HLS" icon="film" href="/v2/developers/guides/transport/trickle-protocol" arrow horizontal>
    HLS output format, ABR manifest structure, and segment delivery.
  </Card>

  <Card title="WebRTC" icon="zap" href="/v2/developers/guides/transport/trickle-protocol" arrow horizontal>
    WHIP/WHEP endpoints, ICE configuration, and low-latency playback.
  </Card>

  <Card title="Real-Time AI Overview" icon="video" href="/v2/developers/build/ai-and-agents/realtime-ai/overview" arrow horizontal>
    Cascade architecture and the trickle-based frame pipeline.
  </Card>
</CardGroup>
