- Introduced a new Dockerfile for building the WHIP server with Go and FFmpeg. - Implemented the WHIPHandler for managing WHIP publish sessions and generating URLs for clients. - Added pionProvider for handling WebRTC sessions using the pion/webrtc library. - Created the main WHIP server logic to handle SDP offers and manage active publishing streams. - Implemented HTTP handlers for publishing, deleting, and retrieving stream information. - Added support for SDP generation for FFmpeg consumption.
167 lines
5.2 KiB
Go
167 lines
5.2 KiB
Go
package api
|
|
|
|
import (
|
|
"fmt"
|
|
"net/http"
|
|
"strings"
|
|
"time"
|
|
|
|
cfgstore "github.com/datarhei/core/v16/config/store"
|
|
"github.com/datarhei/core/v16/whip"
|
|
|
|
"github.com/labstack/echo/v4"
|
|
)
|
|
|
|
// WHIPHandler provides HTTP API access to the WHIP server channel information.
|
|
type WHIPHandler struct {
|
|
whip whip.Server
|
|
config cfgstore.Store
|
|
}
|
|
|
|
// NewWHIP returns a new WHIPHandler backed by the provided WHIP server.
|
|
func NewWHIP(whip whip.Server, config cfgstore.Store) *WHIPHandler {
|
|
return &WHIPHandler{
|
|
whip: whip,
|
|
config: config,
|
|
}
|
|
}
|
|
|
|
// WHIPChannel represents a currently active WHIP publish session.
|
|
type WHIPChannel struct {
|
|
// Name is the stream identifier used in the WHIP URL path.
|
|
Name string `json:"name" jsonschema:"minLength=1"`
|
|
// PublishedAt is the RFC 3339 timestamp when the publisher connected.
|
|
PublishedAt time.Time `json:"published_at"`
|
|
}
|
|
|
|
// WHIPURLs holds the publish URL (for OBS) and the internal SDP relay URL (for FFmpeg).
|
|
type WHIPURLs struct {
|
|
// PublishURL is the URL to enter in OBS → Settings → Stream → Server.
|
|
// Format: http://<public_host>:<port>/whip/<stream_key>
|
|
PublishURL string `json:"publish_url"`
|
|
// SDPURL is the internal FFmpeg-readable relay URL.
|
|
// Format: http://localhost:<port>/whip/<stream_key>/sdp
|
|
SDPURL string `json:"sdp_url"`
|
|
// StreamKey is the key component used in both URLs.
|
|
StreamKey string `json:"stream_key"`
|
|
}
|
|
|
|
// whipPublicBase returns "http://<host>:<port>" derived from the active config.
|
|
func (h *WHIPHandler) whipPublicBase() (string, string, error) {
|
|
if h.config == nil {
|
|
return "", "", fmt.Errorf("config not available")
|
|
}
|
|
cfg := h.config.GetActive()
|
|
addr := cfg.WHIP.Address // e.g. ":8555" or "0.0.0.0:8555"
|
|
|
|
// Extract port from address.
|
|
port := addr
|
|
if idx := strings.LastIndex(addr, ":"); idx >= 0 {
|
|
port = addr[idx+1:]
|
|
}
|
|
|
|
// Pick public host.
|
|
host := "localhost"
|
|
if len(cfg.Host.Name) > 0 && cfg.Host.Name[0] != "" {
|
|
host = cfg.Host.Name[0]
|
|
}
|
|
|
|
return fmt.Sprintf("http://%s:%s", host, port),
|
|
fmt.Sprintf("http://localhost:%s", port),
|
|
nil
|
|
}
|
|
|
|
// GetURL returns the WHIP publish URL for a given stream key.
|
|
// @Summary Get WHIP publish URL for a stream key
|
|
// @Description Returns the URL to configure in OBS and the internal SDP relay URL for FFmpeg.
|
|
// @Tags v16.?.?
|
|
// @ID whip-3-get-url
|
|
// @Produce json
|
|
// @Param name path string true "Stream key"
|
|
// @Success 200 {object} WHIPURLs
|
|
// @Security ApiKeyAuth
|
|
// @Router /api/v3/whip/{name}/url [get]
|
|
func (h *WHIPHandler) GetURL(c echo.Context) error {
|
|
name := c.Param("name")
|
|
if name == "" {
|
|
return c.JSON(http.StatusBadRequest, map[string]string{"error": "stream key is required"})
|
|
}
|
|
|
|
publicBase, localBase, err := h.whipPublicBase()
|
|
if err != nil {
|
|
return c.JSON(http.StatusServiceUnavailable, map[string]string{"error": err.Error()})
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, WHIPURLs{
|
|
PublishURL: fmt.Sprintf("%s/whip/%s", publicBase, name),
|
|
SDPURL: fmt.Sprintf("%s/whip/%s/sdp", localBase, name),
|
|
StreamKey: name,
|
|
})
|
|
}
|
|
|
|
// GetServerURL returns the base WHIP server URL (without a stream key).
|
|
// @Summary Get WHIP server base URL
|
|
// @Description Returns the base publish URL and configuration so the UI can display it in a WHIP Server panel.
|
|
// @Tags v16.?.?
|
|
// @ID whip-3-get-server-url
|
|
// @Produce json
|
|
// @Success 200 {object} WHIPServerInfo
|
|
// @Security ApiKeyAuth
|
|
// @Router /api/v3/whip/url [get]
|
|
func (h *WHIPHandler) GetServerURL(c echo.Context) error {
|
|
publicBase, localBase, err := h.whipPublicBase()
|
|
if err != nil {
|
|
return c.JSON(http.StatusServiceUnavailable, map[string]string{"error": err.Error()})
|
|
}
|
|
|
|
var token string
|
|
if h.config != nil {
|
|
token = h.config.GetActive().WHIP.Token
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, WHIPServerInfo{
|
|
BasePublishURL: fmt.Sprintf("%s/whip/", publicBase),
|
|
BaseSDPURL: fmt.Sprintf("%s/whip/", localBase),
|
|
HasToken: token != "",
|
|
ExampleOBS: fmt.Sprintf("%s/whip/<stream-key>", publicBase),
|
|
InputAddressTemplate: "{whip:name=<stream-key>}",
|
|
})
|
|
}
|
|
|
|
// WHIPServerInfo holds the base URLs and metadata shown in the WHIP Server panel.
|
|
type WHIPServerInfo struct {
|
|
// BasePublishURL is the base URL for OBS. Append the stream key.
|
|
BasePublishURL string `json:"base_publish_url"`
|
|
// BaseSDPURL is the base internal SDP relay URL.
|
|
BaseSDPURL string `json:"base_sdp_url"`
|
|
// HasToken indicates whether a bearer token is required.
|
|
HasToken bool `json:"has_token"`
|
|
// ExampleOBS shows an example URL with placeholder stream key.
|
|
ExampleOBS string `json:"example_obs_url"`
|
|
// InputAddressTemplate is the process input address template for WHIP.
|
|
InputAddressTemplate string `json:"input_address_template"`
|
|
}
|
|
|
|
// ListChannels lists all currently active WHIP publishers.
|
|
// @Summary List all publishing WHIP streams
|
|
// @Description List all currently active WHIP (WebRTC HTTP Ingestion Protocol) streams.
|
|
// @Tags v16.?.?
|
|
// @ID whip-3-list-channels
|
|
// @Produce json
|
|
// @Success 200 {array} WHIPChannel
|
|
// @Security ApiKeyAuth
|
|
// @Router /api/v3/whip [get]
|
|
func (h *WHIPHandler) ListChannels(c echo.Context) error {
|
|
channels := h.whip.Channels()
|
|
|
|
list := make([]WHIPChannel, 0, len(channels.Publisher))
|
|
for name, since := range channels.Publisher {
|
|
list = append(list, WHIPChannel{
|
|
Name: name,
|
|
PublishedAt: since,
|
|
})
|
|
}
|
|
|
|
return c.JSON(http.StatusOK, list)
|
|
}
|