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

# Solution Providers

> Ecosystem solution providers and products building on Livepeer.

export const solutionIcons = {
  studio: 'film-canister',
  daydream: 'camera-movie',
  embody: 'user-robot',
  streamplace: 'projector',
  frameworks: 'clapperboard-play'
};

export const embodyInfra = [{
  icon: 'plug',
  label: 'api'
}, {
  icon: 'cloud',
  label: 'saas'
}];

export const streamplaceInfra = [{
  icon: 'toolbox',
  label: 'sdk'
}];

export const frameworksInfra = [{
  icon: 'server',
  label: 'self-hosted'
}, {
  icon: 'cloud',
  label: 'saas'
}, {
  icon: 'torii-gate',
  label: 'gateway'
}];

export const studioInfra = [{
  icon: 'plug',
  label: 'api'
}, {
  icon: 'cloud',
  label: 'saas'
}, {
  icon: 'torii-gate',
  label: 'gateway'
}];

export const daydreamInfra = [{
  icon: 'torii-gate',
  label: 'gateway'
}, {
  icon: 'server',
  label: 'self-hosted'
}, {
  icon: 'toolbox',
  label: 'sdk'
}];

export const embodyBadges = [{
  color: 'purple',
  label: 'AI'
}, {
  color: 'green',
  label: 'Realtime AI'
}, {
  color: 'red',
  label: 'Agents'
}];

export const streamplaceBadges = [{
  color: 'blue',
  label: 'Video'
}];

export const frameworksBadges = [{
  color: 'blue',
  label: 'Video'
}];

export const studioBadges = [{
  color: 'blue',
  label: 'Video'
}];

export const daydreamBadges = [{
  color: 'blue',
  label: 'Video'
}, {
  color: 'purple',
  label: 'AI'
}, {
  color: 'green',
  label: 'Realtime AI'
}];

export const SolutionItem = ({title, iconWrapper = null, description = null, divider = true, className = '', style = {}, ...rest}) => {
  const containerStyle = {
    paddingBottom: divider ? "var(--lp-spacing-3)" : "var(--lp-spacing-1)",
    ...style
  };
  const spanStyle = {
    display: 'flex',
    flexWrap: 'wrap',
    alignItems: 'center',
    gap: "var(--lp-spacing-2)"
  };
  const descriptionStyle = {
    fontSize: '0.9rem',
    color: 'var(--text-secondary)',
    marginTop: "var(--lp-spacing-1)"
  };
  const dividerStyle = {
    marginTop: "var(--lp-spacing-3)",
    borderBottom: '1px solid var(--lp-color-border-default)'
  };
  return <div style={containerStyle} {...rest}>
      <div>
        <span style={spanStyle}>{title}</span>
        {iconWrapper}
      </div>
      {description && <div style={descriptionStyle}>{description}</div>}
      {divider && <div style={dividerStyle} />}
    </div>;
};

export const BorderedBox = ({children, variant = "default", padding = "var(--lp-spacing-4)", borderRadius = "var(--lp-spacing-px-8)", margin = "", accentBar = "", style = {}, className = "", ...rest}) => {
  const variants = {
    default: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    accent: {
      border: "1px solid var(--lp-color-accent)",
      backgroundColor: "var(--lp-color-bg-card)"
    },
    muted: {
      border: "1px solid var(--lp-color-border-default)",
      backgroundColor: "transparent"
    }
  };
  const accentBarColors = {
    accent: "var(--lp-color-accent)",
    positive: "var(--green-9)"
  };
  return <div data-docs-bordered-box="" data-accent-bar={accentBarColors[accentBar] ? "" : undefined} className={className} style={{
    ...variants[variant],
    padding: padding,
    borderRadius: borderRadius,
    ...margin ? {
      margin
    } : {},
    ...accentBarColors[accentBar] ? {
      position: "relative",
      '--accent-bar-color': accentBarColors[accentBar]
    } : {},
    ...style
  }} {...rest}>
      {children}
    </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>;
};

export const DoubleIconLink = ({label = '', labelColor, href = '#', text = '', iconLeft = 'github', iconLeftColor, iconRight = 'arrow-up-right', iconRightColor = 'var(--lp-color-accent)', className = '', style = {}, ...rest}) => {
  return <span className={className} style={{
    whiteSpace: 'nowrap',
    display: 'inline-flex',
    alignItems: 'center',
    gap: "var(--lp-spacing-1)",
    marginLeft: '0.3rem',
    ...style
  }} {...rest}>
      {text && <span style={{
    marginRight: 8
  }}>{text}</span>}
      <Icon icon={iconLeft} color={iconLeftColor} />
      <a href={href} style={{
    color: {
      labelColor
    }
  }}>
        {label}
      </a>
      <div style={{
    marginRight: '0.3rem'
  }}>
        <Icon icon={iconRight} size={12} color={iconRightColor} />
      </div>
    </span>;
};

export const LinkIcon = ({href, target = '_blank', rel = 'noopener noreferrer', style = {}, className = '', icon = 'arrow-up-right-from-square', size = 12, ...iconProps}) => {
  return <a href={href} target={target} rel={rel} className={className} style={{
    borderBottom: 'none',
    textDecoration: 'none',
    ...style
  }}>
      <Icon icon={icon} size={size} {...iconProps} />
    </a>;
};

export const SearchTable = ({TableComponent = null, tableTitle = null, headerList = [], itemsList = [], monospaceColumns = [], margin, searchPlaceholder = 'Search...', searchColumns = [], categoryColumn = 'Category', filterColumns = [], columnWidths = {}, contentFitColumns = [], columnVariant = {}, categoryBadges = [], textIcons = [], showSeparators = false, separatorColumn = null, boldFirstColumn = true, className = '', style = {}}) => {
  const allFilterCols = [categoryColumn, ...filterColumns];
  const [query, setQuery] = useState('');
  const [selections, setSelections] = useState(() => {
    const init = {};
    allFilterCols.forEach(col => {
      init[col] = 'All';
    });
    return init;
  });
  const safeHeaderList = Array.isArray(headerList) ? headerList : [];
  const safeItemsList = Array.isArray(itemsList) ? itemsList : [];
  const safeMonospaceColumns = Array.isArray(monospaceColumns) ? monospaceColumns : [];
  const safeSearchColumns = Array.isArray(searchColumns) ? searchColumns : [];
  const activeColumns = safeSearchColumns.length ? safeSearchColumns : safeHeaderList;
  const normalizedQuery = query.trim().toLowerCase();
  const badgeColorMap = {};
  categoryBadges.forEach(b => {
    badgeColorMap[b.label.toLowerCase()] = b.color;
  });
  const textIconMap = {};
  textIcons.forEach(t => {
    textIconMap[t.label.toLowerCase()] = t.icon;
  });
  const getOptionsForColumn = (colName, colIndex) => {
    let scoped = safeItemsList;
    for (let i = 0; i < colIndex; i++) {
      const prevCol = allFilterCols[i];
      const prevSel = selections[prevCol];
      if (prevSel !== 'All') {
        scoped = scoped.filter(item => String(item?.[prevCol] || '') === prevSel);
      }
    }
    return [...new Set(scoped.map(item => String(item?.[colName] || '')).filter(Boolean))].sort((a, b) => a.localeCompare(b, 'en', {
      sensitivity: 'base'
    }));
  };
  const filteredItems = safeItemsList.filter(item => allFilterCols.every(col => {
    const sel = selections[col];
    return sel === 'All' || String(item?.[col] || '') === sel;
  }));
  const searchedItems = !normalizedQuery ? filteredItems : filteredItems.filter(item => activeColumns.some(column => {
    const value = (item?.[column] ?? item?.[String(column).toLowerCase()]) ?? '';
    return String(value).toLowerCase().includes(normalizedQuery);
  }));
  const sortedItems = [...searchedItems].sort((a, b) => {
    for (const col of allFilterCols) {
      const cmp = String(a[col] || '').localeCompare(String(b[col] || ''), 'en', {
        sensitivity: 'base'
      });
      if (cmp !== 0) return cmp;
    }
    return 0;
  });
  const firstColumnName = safeHeaderList[0];
  const renderVariant = (value, variant, item, header) => {
    if (variant === 'bold' && typeof value === 'string') {
      return <strong>{value}</strong>;
    }
    if (variant === 'badge' && typeof value === 'string') {
      const colorName = badgeColorMap[value.toLowerCase()];
      if (colorName) {
        return <Badge color={colorName}>{value}</Badge>;
      }
    }
    if (variant === 'textIcon' && typeof value === 'string') {
      const icon = textIconMap[value.toLowerCase()];
      if (icon) {
        return <span style={{
          display: 'inline-flex',
          alignItems: 'center',
          gap: '0.35rem'
        }}>
            {icon} {value}
          </span>;
      }
    }
    if (variant === 'addressWrapper' && typeof value === 'string') {
      const href = item?.[`_${header}Href`] ?? item?._addressHref;
      return <div style={{
        display: 'flex',
        alignItems: 'center',
        gap: '0.35rem',
        width: '100%',
        minWidth: 0
      }}>
          <CopyText text={value} style={{
        flex: 1
      }} />
          {href && <LinkIcon href={href} color="var(--lp-color-accent)" />}
        </div>;
    }
    return value;
  };
  const displayItems = sortedItems.map(item => {
    const out = {
      ...item,
      _sepKey: String(item[separatorColumn || categoryColumn] || '')
    };
    for (const header of safeHeaderList) {
      if (columnVariant[header] && out[header] !== undefined) {
        out[header] = renderVariant(out[header], columnVariant[header], item, header);
      }
    }
    if (boldFirstColumn && firstColumnName && !columnVariant[firstColumnName] && typeof out[firstColumnName] === 'string') {
      out[firstColumnName] = <strong>{out[firstColumnName]}</strong>;
    }
    return out;
  });
  const withSeparators = [];
  let lastSep = '';
  displayItems.forEach(item => {
    const sepKey = item._sepKey || '';
    if (showSeparators && sepKey && sepKey !== lastSep) {
      withSeparators.push({
        __separator: true,
        [safeHeaderList[0]]: sepKey.toUpperCase()
      });
      lastSep = sepKey;
    }
    withSeparators.push(item);
  });
  const selectStyle = {
    minWidth: '150px',
    padding: '8px 12px',
    borderRadius: '8px',
    border: '1px solid var(--lp-color-border-default)',
    background: 'var(--lp-color-bg-page)',
    color: 'var(--lp-color-text-secondary)'
  };
  const updateSelection = (col, colIndex, value) => {
    const next = {
      ...selections,
      [col]: value
    };
    for (let i = colIndex + 1; i < allFilterCols.length; i++) {
      next[allFilterCols[i]] = 'All';
    }
    setSelections(next);
  };
  return <div className={className} style={style}>
      <div style={{
    marginBottom: "var(--lp-spacing-2)",
    display: 'flex',
    flexWrap: 'wrap',
    gap: "var(--lp-spacing-2)",
    alignItems: 'center'
  }}>
        <input type="text" value={query} placeholder={searchPlaceholder} onChange={e => setQuery(e.target.value)} aria-label="Filter table rows" style={{
    width: '100%',
    maxWidth: '420px',
    padding: '8px 12px',
    borderRadius: '8px',
    border: '1px solid var(--lp-color-border-default)',
    background: 'var(--lp-color-bg-page)',
    color: 'var(--lp-color-text-secondary)'
  }} />
        {allFilterCols.map((col, colIndex) => {
    const options = getOptionsForColumn(col, colIndex);
    if (options.length === 0) return null;
    const parentLabel = colIndex > 0 && selections[allFilterCols[colIndex - 1]] !== 'All' ? selections[allFilterCols[colIndex - 1]] : col.toLowerCase() + 's';
    return <select key={col} value={selections[col]} onChange={e => updateSelection(col, colIndex, e.target.value)} aria-label={`Filter by ${col}`} style={selectStyle}>
              <option value="All">All {parentLabel}</option>
              {options.map(o => <option key={o} value={o}>
                  {o}
                </option>)}
            </select>;
  })}
      </div>

      {typeof TableComponent === 'function' ? <TableComponent tableTitle={tableTitle} headerList={safeHeaderList} itemsList={withSeparators} monospaceColumns={safeMonospaceColumns} columnWidths={columnWidths} contentFitColumns={contentFitColumns} showSeparators={showSeparators} margin={margin} /> : <Warning>SearchTable requires a `TableComponent` prop.</Warning>}
    </div>;
};

export const DynamicTable = ({tableTitle = null, headerList = [], itemsList = [], monospaceColumns = [], columnWidths = {}, contentFitColumns = [], showSeparators = false, margin, className = "", style = {}, ...rest}) => {
  if (!headerList.length) {
    return <div>No headers provided</div>;
  }
  const safeContentFitColumns = Array.isArray(contentFitColumns) ? contentFitColumns : [];
  const usesContentFitColumns = safeContentFitColumns.length > 0;
  const isContentFitColumn = header => safeContentFitColumns.includes(header);
  const getColumnStyle = header => {
    const widthStyle = columnWidths[header] ? {
      width: columnWidths[header],
      minWidth: columnWidths[header],
      maxWidth: columnWidths[header]
    } : {};
    const contentFitStyle = !columnWidths[header] && isContentFitColumn(header) ? {
      width: "1%",
      whiteSpace: "nowrap"
    } : {};
    return {
      ...contentFitStyle,
      ...widthStyle
    };
  };
  return <div className={className} style={style} {...rest}>
      {tableTitle && <div style={{
    fontStyle: "italic",
    margin: 0
  }}>
          <strong>{tableTitle}</strong>
        </div>}
      <div style={{
    overflowX: "auto",
    ...margin != null && ({
      margin
    })
  }} role="region" tabIndex={0} aria-label={tableTitle ? `Scrollable table: ${tableTitle}` : "Scrollable table"}>
        <table data-docs-dynamic-table style={{
    width: "100%",
    tableLayout: usesContentFitColumns ? "auto" : "fixed",
    borderCollapse: "collapse",
    fontSize: "0.9rem",
    marginTop: 0
  }}>
          <thead>
            <tr style={{
    backgroundColor: "var(--lp-color-accent)",
    color: "var(--lp-color-on-accent)",
    borderBottom: "1px solid var(--lp-color-border-default)"
  }}>
              {headerList.map((header, index) => <th key={index} style={{
    padding: "10px 8px",
    textAlign: "left",
    fontWeight: "600",
    color: "var(--lp-color-on-accent)",
    ...getColumnStyle(header)
  }}>
                  {header}
                </th>)}
            </tr>
          </thead>
          <tbody>
            {itemsList.filter(item => showSeparators || !item?.__separator).map((item, rowIndex) => item?.__separator ? <tr key={rowIndex} style={{
    backgroundColor: "var(--lp-color-accent)",
    color: "var(--lp-color-on-accent)",
    borderBottom: "1px solid var(--lp-color-accent)"
  }}>
                  <td colSpan={headerList.length} style={{
    padding: "6px 8px",
    fontWeight: "700",
    color: "var(--lp-color-on-accent)",
    letterSpacing: "0.01em"
  }}>
                    {(item[headerList[0]] ?? item.Category) ?? "Category"}
                  </td>
                </tr> : <tr key={rowIndex} style={{
    borderBottom: "1px solid var(--lp-color-border-default)"
  }}>
                  {headerList.map((header, colIndex) => {
    const value = (item[header] ?? item[header.toLowerCase()]) ?? "-";
    const isMonospace = monospaceColumns.includes(colIndex);
    return <td key={colIndex} style={{
      padding: "8px 8px",
      fontFamily: isMonospace ? "monospace" : "inherit",
      wordWrap: "break-word",
      overflowWrap: "break-word",
      ...getColumnStyle(header)
    }}>
                        {isMonospace ? <code>{value}</code> : value}
                      </td>;
  })}
                </tr>)}
          </tbody>
        </table>
      </div>
    </div>;
};

export const IconBadgeWrapper = ({items = [], iconColor, size = 14, gap = "var(--lp-spacing-3)", margin = '0.25rem 0 0.5rem', style = {}, className = '', ...rest}) => {
  const wrapperStyle = {
    display: 'flex',
    flexWrap: 'wrap',
    alignItems: 'center',
    gap,
    margin
  };
  const tagStyle = {
    display: 'inline-flex',
    alignItems: 'center',
    gap: "var(--lp-spacing-1)",
    fontSize: `${size}px`,
    color: 'var(--lp-color-text-primary)'
  };
  return <div className={className} style={{
    ...wrapperStyle,
    ...style
  }} {...rest}>
      {items.map((item, i) => <span key={i} style={tagStyle}>
          <Icon icon={item.icon} size={size} color={iconColor || 'currentColor'} />
          {item.label}
        </span>)}
    </div>;
};

export const BadgeWrapper = ({badges, children, gap = '0.4rem', margin = '0.5rem 0 1.5rem 0', style = {}, className = '', ...rest}) => {
  const defaultStyle = {
    display: 'flex',
    flexWrap: 'wrap',
    alignItems: 'center',
    gap,
    margin
  };
  return <div className={className} style={{
    ...defaultStyle,
    ...style
  }} {...rest}>
      {badges ? badges.map((b, i) => <Badge key={i} color={b.color}>
              {b.label}
            </Badge>) : children}
    </div>;
};

export const Subtitle = ({style = {}, text, children, variant = 'default', fontSize = '', fontWeight = '', fontStyle = '', marginTop = '', marginBottom = '', color = '', className = '', ...rest}) => {
  const renderInlineCode = (value, keyPrefix) => {
    return value.split(/(`[^`]+`)/g).map((segment, index) => {
      if (segment.startsWith('`') && segment.endsWith('`')) {
        return <code key={`${keyPrefix}-code-${index}`}>{segment.slice(1, -1)}</code>;
      }
      return segment;
    });
  };
  const renderInlineMarkup = (value, keyPrefix = 'subtitle') => {
    if (typeof value !== 'string') {
      return value;
    }
    return value.split(/(\*\*[\s\S]+?\*\*)/g).map((segment, index) => {
      if (segment.startsWith('**') && segment.endsWith('**')) {
        const inner = segment.slice(2, -2);
        return <strong key={`${keyPrefix}-strong-${index}`}>
            {renderInlineCode(inner, `${keyPrefix}-strong-${index}`)}
          </strong>;
      }
      return renderInlineCode(segment, `${keyPrefix}-${index}`);
    });
  };
  const renderContent = (value, keyPrefix) => {
    if (Array.isArray(value)) {
      return value.map((item, index) => renderContent(item, `${keyPrefix}-${index}`));
    }
    return renderInlineMarkup(value, keyPrefix);
  };
  const variants = {
    default: {
      fontSize: '1rem',
      fontStyle: 'italic',
      color: 'var(--lp-color-accent)',
      marginBottom: 0
    },
    changelog: {
      fontSize: '0.8rem',
      fontStyle: 'normal',
      fontWeight: 700,
      color: 'var(--lp-color-text-primary)',
      marginBottom: 0
    }
  };
  const base = variants[variant] || variants.default;
  return <span className={className} style={{
    ...base,
    ...fontSize ? {
      fontSize
    } : {},
    ...fontWeight ? {
      fontWeight
    } : {},
    ...fontStyle ? {
      fontStyle
    } : {},
    ...marginTop ? {
      marginTop
    } : {},
    ...marginBottom ? {
      marginBottom
    } : {},
    ...color ? {
      color
    } : {},
    ...style
  }} {...rest}>
      {renderContent(text, 'text')}
      {renderContent(children, 'children')}
    </span>;
};

export const CopyText = ({text, label, className = '', style = {}, ...rest}) => {
  const handleCopy = () => {
    navigator.clipboard.writeText(text);
  };
  return <span className={className} style={{
    display: 'flex',
    alignItems: 'center',
    padding: '0.2rem 0.4rem',
    borderRadius: "4px",
    fontSize: '0.85rem',
    fontFamily: 'monospace',
    backgroundColor: 'var(--lp-color-bg-card)',
    border: '1px solid var(--lp-color-border-default)',
    minWidth: 0,
    overflow: 'hidden',
    ...style
  }} {...rest}>
      {label && <strong style={{
    flexShrink: 0,
    marginRight: "var(--lp-spacing-2)"
  }}>{label}</strong>}
      <span style={{
    overflow: 'hidden',
    textOverflow: 'ellipsis',
    whiteSpace: 'nowrap',
    flex: 1,
    minWidth: 0
  }}>
        {text}
      </span>
      <button onClick={handleCopy} style={{
    background: 'none',
    border: 'none',
    cursor: 'pointer',
    padding: '0 0 0 0.4rem',
    display: 'inline-flex',
    alignItems: 'center',
    color: 'var(--lp-color-text-secondary)',
    flexShrink: 0
  }} title="Copy to clipboard">
        <svg width="14" height="14" viewBox="0 0 24 24" fill="none" stroke="currentColor" strokeWidth="2">
          <rect x="9" y="9" width="13" height="13" rx="2" ry="2"></rect>
          <path d="M5 15H4a2 2 0 0 1-2-2V4a2 2 0 0 1 2-2h9a2 2 0 0 1 2 2v1"></path>
        </svg>
      </button>
    </span>;
};

export const CustomCardTitle = ({icon, title, variant = "card", iconSize, style = {}, className = "", ...rest}) => {
  const variants = {
    card: {
      display: 'flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)",
      marginBottom: "var(--lp-spacing-3)",
      color: 'var(--lp-color-text-primary)',
      fontSize: '1rem',
      fontWeight: 600
    },
    accordion: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: "var(--lp-spacing-2)"
    },
    tab: {
      display: 'inline-flex',
      alignItems: 'center',
      gap: '0.4rem',
      fontSize: '0.875rem'
    }
  };
  const sizes = {
    card: 20,
    accordion: 18,
    tab: 14
  };
  const size = iconSize || sizes[variant] || 20;
  const baseStyle = variants[variant] || variants.card;
  return variant === 'card' ? <div className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </div> : <span className={className} style={{
    ...baseStyle,
    ...style
  }} {...rest}>
      {typeof icon === 'string' ? <Icon icon={icon} size={size} color="var(--lp-color-accent)" /> : icon}
      {title}
    </span>;
};

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

<Tip> <Subtitle variant="changelog" style={{marginBottom: "0.2rem"}}>**Are you a Solution Provider?**</Subtitle> Submit a PR to the <DoubleIconLink label="Livepeer Docs Github" href="https://github.com/livepeer/docs" newline={false} iconRightColor="var(--hero-text)" /> to add your solution here!</Tip>

Livepeer Solutions are ready-to-use video and AI products built on the Livepeer Protocol. Stream live video, run AI pipelines, deploy interactive avatars, or add decentralised video to your app - each product handles the infrastructure so you can focus on building.

<CustomDivider style={{margin: "-0.5rem 0 -2.5rem 0"}} />

## All Solutions

<SearchTable
  TableComponent={DynamicTable}
  headerList={["Solution", "Category", "Infra", "Focus", "Links"]}
  searchPlaceholder="Search solutions..."
  searchColumns={["Solution", "Focus", "_category"]}
  categoryColumn="_category"
  itemsList={[
{
  Solution: <strong>Livepeer Studio</strong>,
  Category: <BadgeWrapper badges={studioBadges} />,
  Infra: <IconBadgeWrapper items={studioInfra} iconColor="var(--accent)" size={12} />,
  Focus: "Live streaming, VOD, developer APIs",
  Links: <span><a href="/solutions/livepeer-studio/overview">Docs</a> · <a href="https://livepeer.studio">App</a></span>,
  _category: "Video",
},
{
  Solution: <strong>Daydream</strong>,
  Category: <BadgeWrapper badges={daydreamBadges} />,
  Infra: <IconBadgeWrapper items={daydreamInfra} iconColor="var(--accent)" size={12} />,
  Focus: "Real-time AI video, world models",
  Links: <span><a href="/solutions/daydream/overview">Docs</a> · <a href="https://daydream.live">App</a></span>,
  _category: "Video, AI",
},
{
  Solution: <strong>Frameworks</strong>,
  Category: <BadgeWrapper badges={frameworksBadges} />,
  Infra: <IconBadgeWrapper items={frameworksInfra} iconColor="var(--accent)" size={12} />,
  Focus: "Self-hosted video infrastructure",
  Links: <span><a href="/solutions/frameworks/overview">Docs</a> · <a href="https://app.frameworks.network">App</a></span>,
  _category: "Video",
},
{
  Solution: <strong>Embody</strong>,
  Category: <BadgeWrapper badges={embodyBadges} />,
  Infra: <IconBadgeWrapper items={embodyInfra} iconColor="var(--accent)" size={12} />,
  Focus: "Interactive 3D AI avatars",
  Links: <span><a href="/solutions/embody/overview">Docs</a> · <a href="https://embody.zone">App</a></span>,
  _category: "AI, Agents",
},
{
  Solution: <strong>Streamplace</strong>,
  Category: <BadgeWrapper badges={streamplaceBadges} />,
  Infra: <IconBadgeWrapper items={streamplaceInfra} iconColor="var(--accent)" size={12} />,
  Focus: "Decentralised social video",
  Links: <span><a href="/solutions/streamplace/overview">Docs</a> · <a href="https://stream.place">App</a></span>,
  _category: "Video",
},
]}
/>

<CustomDivider style={{margin: "0 0 -2.5rem 0"}} />

## By Infrastructure Type

<AccordionGroup>
  <Accordion title="API" icon="plug">
    * **Livepeer Studio** - REST API, TypeScript/Go/Python SDKs ([docs](https://docs.livepeer.org))
    * **Embody** - API for avatar sessions and GPU inference
  </Accordion>

  <Accordion title="SaaS (hosted)" icon="cloud">
    * **Livepeer Studio** - Managed Gateway, dashboard, billing ([Livepeer.studio](https://livepeer.studio))
    * **Frameworks** - Hosted ingest and transcoding tier ([app.frameworks.network](https://app.frameworks.network))
    * **Embody** - Hosted avatar platform ([embody.zone](https://embody.zone))
  </Accordion>

  <Accordion title="Self-hosted" icon="server">
    * **Frameworks** - Full MistServer stack, deploy on your own infrastructure ([docs](https://docs.frameworks.network))
    * **Daydream** - Run pipelines locally or on your own GPU ([GitHub](https://github.com/daydreamlive))
  </Accordion>

  <Accordion title="Gateway" icon="torii-gate">
    * **Livepeer Studio** - Managed Gateway routing to the Orchestrator network
    * **Daydream** - Daydream Gateway for real-time AI inference jobs
    * **Frameworks** - Skipper routing layer for ingest and processing nodes
  </Accordion>

  <Accordion title="SDK" icon="toolbox">
    * **Daydream** - SDK and node-based workflow tools ([docs](https://docs.daydream.live))
    * **Streamplace** - `@streamplace/sdk` for decentralised video and provenance ([GitHub](https://github.com/streamplace/streamplace))
    * **Livepeer Studio** - React Player, Broadcast components, `livepeer` npm package
  </Accordion>
</AccordionGroup>

<CustomDivider style={{margin: "0 0 -2.5rem 0"}} />

## By Category

<BorderedBox variant="accent">
  <Tabs>
    <Tab title="Video" icon="video">
      <BadgeWrapper badges={[{ color: 'blue', label: 'Video' }]} />

      <SolutionItem title={<><Icon icon={solutionIcons.studio} color="var(--text)"/><LinkArrow href="https://livepeer.studio" label="Livepeer Studio" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={studioInfra} iconColor="var(--accent)" size={12} />} description="Live streaming, VOD, transcoding API" />

      <SolutionItem title={<><Icon icon={solutionIcons.daydream} color="var(--text)"/><LinkArrow href="https://daydream.live" label="Daydream" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={daydreamInfra} iconColor="var(--accent)" size={12} />} description="Real-time AI video generation and style transfer" />

      <SolutionItem title={<><Icon icon={solutionIcons.frameworks} color="var(--text)"/><LinkArrow href="https://app.frameworks.network" label="Frameworks" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={frameworksInfra} iconColor="var(--accent)" size={12} />} description="Full-stack self-hosted video (MistServer)" />

      <SolutionItem title={<><Icon icon={solutionIcons.streamplace} color="var(--text)"/><LinkArrow href="https://stream.place" label="Streamplace" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={streamplaceInfra} iconColor="var(--accent)" size={12} />} description="Decentralised social video with provenance" divider={false} />
    </Tab>

    <Tab title="AI" icon="microchip-ai">
      <BadgeWrapper badges={[{ color: 'purple', label: 'AI' }, { color: 'green', label: 'Realtime AI' }]} />

      <SolutionItem title={<><Icon icon={solutionIcons.daydream} color="var(--text)"/><LinkArrow href="https://daydream.live" label="Daydream" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={daydreamInfra} iconColor="var(--accent)" size={12} />} description="Real-time AI video, world models, style transfer" />

      <SolutionItem title={<><Icon icon={solutionIcons.embody} color="var(--text)"/><LinkArrow href="https://embody.zone" label="Embody" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={embodyInfra} iconColor="var(--accent)" size={12} />} description="Interactive 3D AI avatars (Unreal Engine)" divider={false} />
    </Tab>

    <Tab title="Agents" icon="robot">
      <BadgeWrapper badges={[{ color: 'red', label: 'Agents' }]} />

      <SolutionItem title={<><Icon icon={solutionIcons.embody} color="var(--text)"/><LinkArrow href="https://embody.zone" label="Embody" newline={false} /></>} iconWrapper={<IconBadgeWrapper items={embodyInfra} iconColor="var(--accent)" size={12} />} description="Interactive 3D AI avatars (Unreal Engine)" divider={false} />
    </Tab>
  </Tabs>
</BorderedBox>

<CustomDivider style={{margin: "0 0 -2.5rem 0"}} />

## Build on Livepeer

Have a product idea that uses Livepeer's video or AI infrastructure? The ecosystem grows through community proposals and open-source contributions.

<CardGroup cols={2}>
  <Card title={<CustomCardTitle icon="comments" title="Developer Guide" />} href="/v2/developers" arrow>
    Learn how to build custom AI pielines and integrate your own models.
  </Card>

  <Card title={<CustomCardTitle icon="github" title="Gateway Applications" />} href="/v2/gateways" arrow>
    Build a production application on your own Gateway pipeline.
  </Card>

  <Card title={<CustomCardTitle icon="comments" title="Livepeer Forum" />} href="https://forum.livepeer.org/" arrow>
    Propose ideas, discuss with the community, and find collaborators.
  </Card>

  <Card title={<CustomCardTitle icon="github" title="Livepeer GitHub" />} href="https://github.com/livepeer/" arrow>
    Open-source repos, SDKs, and protocol code to build your ideas on.
  </Card>
</CardGroup>
