# Changelog URL: https://developers.cloudflare.com/stream/changelog/ import { ProductReleaseNotes } from "~/components"; {/* */} --- # Get started URL: https://developers.cloudflare.com/stream/get-started/ :::note[Before you get started:] You must first [create a Cloudflare account](/fundamentals/account/create-account/) and [create an API token](/fundamentals/api/get-started/create-token/) to begin using Stream. ::: * [Upload your first video](/stream/get-started#upload-your-first-video) * [Start your first live stream](/stream/get-started#start-your-first-live-stream) ## Upload your first video ### Step 1: Upload an example video from a public URL You can upload videos directly from the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/stream) or using the API. To use the API, replace the `API_TOKEN` and `ACCOUNT_ID` values with your credentials in the example below. ```bash title="Upload a video using the API" curl \ -X POST \ -d '{"url":"https://storage.googleapis.com/stream-example-bucket/video.mp4","meta":{"name":"My First Stream Video"}}' \ -H "Authorization: Bearer " \ https://api.cloudflare.com/client/v4/accounts//stream/copy ``` ### Step 2: Wait until the video is ready to stream Because Stream must download and process the video, the video might not be available for a few seconds depending on the length of your video. You should poll the Stream API until `readyToStream` is `true`, or use [webhooks](/stream/manage-video-library/using-webhooks/) to be notified when a video is ready for streaming. Use the video UID from the first step to poll the video: ```bash title="Request" curl \ -H "Authorization: Bearer " \ https://api.cloudflare.com/client/v4/accounts//stream/ ``` ```json title="Response" {6} { "result": { "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch", "thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg", "readyToStream": true, "status": { "state": "ready" }, "meta": { "downloaded-from": "https://storage.googleapis.com/stream-example-bucket/video.mp4", "name": "My First Stream Video" }, "created": "2020-10-16T20:20:17.872170843Z", "size": 9032701, //... }, "success": true, "errors": [], "messages": [] } ``` ### Step 3: Play the video in your website or app Videos uploaded to Stream can be played on any device and platform, from websites to native apps. See [Play videos](/stream/viewing-videos) for details and examples of video playback across platforms. To play video on your website with the [Stream Player](/stream/viewing-videos/using-the-stream-player/), copy the `uid` of the video from the request above, along with your unique customer code, and replace `` and `` in the embed code below: ```html ``` The embed code above can also be found in the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/stream).
### Next steps * [Edit your video](/stream/edit-videos/) and add captions or watermarks * [Customize the Stream player](/stream/viewing-videos/using-the-stream-player/) ## Start your first live stream ### Step 1: Create a live input You can create a live input via the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/stream/inputs/create) or using the API. To use the API, replace the `API_TOKEN` and `ACCOUNT_ID` values with your credentials in the example below. ```bash title="Request" curl -X POST \ -H "Authorization: Bearer " \ -D '{"meta": {"name":"test stream"},"recording": { "mode": "automatic" }}' \ https://api.cloudflare.com/client/v4/accounts//stream/live_inputs ``` ```json title="Response" { "uid": "f256e6ea9341d51eea64c9454659e576", "rtmps": { "url": "rtmps://live.cloudflare.com:443/live/", "streamKey": "MTQ0MTcjM3MjI1NDE3ODIyNTI1MjYyMjE4NTI2ODI1NDcxMzUyMzcf256e6ea9351d51eea64c9454659e576" }, "created": "2021-09-23T05:05:53.451415Z", "modified": "2021-09-23T05:05:53.451415Z", "meta": { "name": "test stream" }, "status": null, "recording": { "mode": "automatic", "requireSignedURLs": false, "allowedOrigins": null } } ``` ### Step 2: Copy the RTMPS URL and key, and use them with your live streaming application. We recommend using [Open Broadcaster Software (OBS)](https://obsproject.com/) to get started. ### Step 3: Play the live stream in your website or app Live streams can be played on any device and platform, from websites to native apps, using the same video players as videos uploaded to Stream. See [Play videos](/stream/viewing-videos) for details and examples of video playback across platforms. To play the live stream you just started on your website with the [Stream Player](/stream/viewing-videos/using-the-stream-player/), copy the `uid` of the live input from the request above, along with your unique customer code, and replace `` and `` in the embed code below: ```html ``` The embed code above can also be found in the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/stream). ### Next steps * [Secure your stream](/stream/viewing-videos/securing-your-stream/) * [View live viewer counts](/stream/getting-analytics/live-viewer-count/) ## Accessibility considerations To make your video content more accessible, include [captions](/stream/edit-videos/adding-captions/) and [high-quality audio recording](https://www.w3.org/WAI/media/av/av-content/). --- # FAQ URL: https://developers.cloudflare.com/stream/faq/ import { GlossaryTooltip } from "~/components" ## Stream ### What formats and quality levels are delivered through Cloudflare Stream? Cloudflare decides on which bitrate, resolution, and codec is best for you. We deliver all videos to industry standard H264 codec. We use a few different adaptive streaming levels from 360p to 1080p to ensure smooth streaming for your audience watching on different devices and bandwidth constraints. ### Can I download original video files from Stream? You cannot download the *exact* input file that you uploaded. However, depending on your use case, you can use the [Downloadable Videos](/stream/viewing-videos/download-videos/) feature to get encoded MP4s for use cases like offline viewing. ### Is there a limit to the amount of videos I can upload? * By default, a video upload can be at most 30 GB. * By default, you can have up to 120 videos queued or being encoded simultaneously. Videos in the `ready` status are playable but may still be encoding certain quality levels until the `pctComplete` reaches 100. Videos in the `error`, `ready`, or `pendingupload` state do not count toward this limit. If you need the concurrency limit raised, [contact Cloudflare support](/support/contacting-cloudflare-support/) explaining your use case and why you would like the limit raised. :::note The limit to the number of videos only applies to videos being uploaded to Cloudflare Stream. This limit is not related to the number of end users streaming videos. ::: * An account cannot upload videos if the total video duration exceeds the video storage capacity purchased. Limits apply to Direct Creator Uploads at the time of upload URL creation. Uploads over these limits will receive a [429 (Too Many Requests)](/support/troubleshooting/http-status-codes/4xx-client-error/error-429/) or [413 (Payload too large)](/support/troubleshooting/http-status-codes/4xx-client-error/error-413/) HTTP status codes with more information in the response body. Please write to Cloudflare support or your customer success manager for higher limits. ### Can I embed videos on Stream even if my domain is not on Cloudflare? Yes. Stream videos can be embedded on any domain, even domains not on Cloudflare. ### What input file formats are supported? Users can upload video in the following file formats: MP4, MKV, MOV, AVI, FLV, MPEG-2 TS, MPEG-2 PS, MXF, LXF, GXF, 3GP, WebM, MPG, QuickTime ### Does Stream support High Dynamic Range (HDR) video content? When HDR videos are uploaded to Stream, they are re-encoded and delivered in SDR format, to ensure compatibility with the widest range of viewing devices. ### What frame rates (FPS) are supported? Cloudflare Stream supports video file uploads for any FPS, however videos will be re-encoded for 70 FPS playback. If the original video file has a frame rate lower than 70 FPS, Stream will re-encode at the original frame rate. If the frame rate is variable we will drop frames (for example if there are more than 1 frames within 1/30 seconds, we will drop the extra frames within that period). ### What browsers does Stream work on? You can embed the Stream player on the following platforms: | Browser | Version | | ------- | ----------------------------------- | | Chrome | Supported since Chrome version 88+ | | Firefox | Supported since Firefox version 87+ | | Edge | Supported since Edge 89+ | | Safari | Supported since Safari version 14+ | | Opera | Supported since Opera version 75+ | :::note[Note] Cloudflare Stream is not available on Chromium, as Chromium does not support H.264 videos. ::: | Mobile Platform | Version | | --------------------- | ------------------------------------------------------------------------ | | Chrome on Android | Supported on Chrome 90 | | UC Browser on Android | Supported on version 12.12+ | | Samsung Internet | Supported on 13+ | | Safari on iOS | Supported on iOS 13.4+. Speed selector supported when not in fullscreen. | ### What are the recommended upload settings for video uploads? If you are producing a brand new file for Cloudflare Stream, we recommend you use the following settings: * MP4 containers, AAC audio codec, H264 video codec, 30 or below frames per second * moov atom should be at the front of the file (Fast Start) * H264 progressive scan (no interlacing) * H264 high profile * Closed GOP * Content should be encoded and uploaded in the same frame rate it was recorded * Mono or Stereo audio (Stream will mix audio tracks with more than 2 channels down to stereo) Below are bitrate recommendations for encoding new videos for Stream: | Resolution | Recommended bitrate | | ---------- | ------------------- | | 1080p | 8 Mbps | | 720p | 4.8 Mbps | | 480p | 2.4 Mbps | | 360p | 1 Mbps | ### If I cancel my stream subscription, are the videos deleted? Videos are removed if the subscription is not renewed within 30 days. ### I use Content Security Policy (CSP) on my website. What domains do I need to add to which directives? If your website uses Content Security Policy (CSP) directives, depending on your configuration, you may need to add Cloudflare Stream's domains to particular directives, in order to allow videos to be viewed or uploaded by your users. If you use the provided [Stream Player](/stream/viewing-videos/using-the-stream-player/), `videodelivery.net` and `*.cloudflarestream.com` must be included in the `frame-src` or `default-src` directive to allow the player's ` ``` Refer to the [Using the Stream Player](/stream/viewing-videos/using-the-stream-player/) for more information. --- # Video.js URL: https://developers.cloudflare.com/stream/examples/video-js/ ```html ``` Refer to the [Video.js documentation](https://docs.videojs.com/) for more information. --- # Shaka Player URL: https://developers.cloudflare.com/stream/examples/shaka-player/ First, create a video element, using the poster attribute to set a preview thumbnail image. Refer to [Display thumbnails](/stream/viewing-videos/displaying-thumbnails/) for instructions on how to generate a thumbnail image using Cloudflare Stream. ```html ``` Then listen for `DOMContentLoaded` event, create a new instance of Shaka Player, and load the manifest URI. ```javascript // Replace the manifest URI with an HLS or DASH manifest from Cloudflare Stream const manifestUri = 'https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd'; document.addEventListener('DOMContentLoaded', () => { const video = document.getElementById('video'); const player = new shaka.Player(video); await player.load(manifestUri); }); ``` Refer to the [Shaka Player documentation](https://github.com/shaka-project/shaka-player) for more information. --- # SRT playback URL: https://developers.cloudflare.com/stream/examples/srt_playback/ import { Render } from "~/components"; Copy the **SRT Playback URL** for your live input from the [Stream Dashboard](https://dash.cloudflare.com/?to=/:account/stream/inputs) or the [Stream API](/stream/stream-live/start-stream-live/#use-the-api), and paste it into the URL below, replacing ``: ```sh title="SRT playback with ffplay" ffplay -analyzeduration 1 -fflags -nobuffer -probesize 32 -sync ext '' ``` For more, refer to [Play live video in native apps with less than one second latency](/stream/viewing-videos/using-own-player/#play-live-video-in-native-apps-with-less-than-1-second-latency). --- # Vidstack URL: https://developers.cloudflare.com/stream/examples/vidstack/ ## Installation There's a few options to choose from when getting started with Vidstack, follow any of the links below to get setup. You can replace the player `src` with `https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8` to test Cloudflare Stream. * [Angular](https://www.vidstack.io/docs/player/getting-started/installation/angular?provider=video) * [React](https://www.vidstack.io/docs/player/getting-started/installation/react?provider=video) * [Svelte](https://www.vidstack.io/docs/player/getting-started/installation/svelte?provider=video) * [Vue](https://www.vidstack.io/docs/player/getting-started/installation/vue?provider=video) * [Solid](https://www.vidstack.io/docs/player/getting-started/installation/solid?provider=video) * [Web Components](https://www.vidstack.io/docs/player/getting-started/installation/web-components?provider=video) * [CDN](https://www.vidstack.io/docs/player/getting-started/installation/cdn?provider=video) ## Examples Feel free to check out [Vidstack Examples](https://github.com/vidstack/examples) for building with various JS frameworks and styling options (e.g., CSS or Tailwind CSS). --- # Stream WordPress plugin URL: https://developers.cloudflare.com/stream/examples/wordpress/ Before you begin, ensure Cloudflare Stream is enabled on your account and that you have a [Cloudflare API key](/fundamentals/api/get-started/keys/). ## Configure the Cloudflare Stream WordPress plugin 1. Log in to your WordPress account. 2. Download the **Cloudflare Stream plugin**. 3. Expand the **Settings** menu from the navigation menu and select **Cloudflare Stream**. 4. On the **Cloudflare Stream settings** page, enter your email, account ID, and API key. ## Upload video with Cloudflare Stream WordPress plugin After configuring the Stream Plugin in WordPress, you can upload videos directly to Stream from WordPress. To upload a video using the Stream plugin: 1. Navigate to the **Add New Post** page in WordPress. 2. Select the **Add Block** icon. 3. Enter **Stream** in the search bar to search for the Cloudflare Stream Video plugin. 4. Select **Cloudflare Stream Video** to add the **Stream** block to your post. 5. Select **Upload** button to choose the video to upload. --- # GraphQL Analytics API URL: https://developers.cloudflare.com/stream/getting-analytics/fetching-bulk-analytics/ Stream provides analytics about both live video and video uploaded to Stream, via the GraphQL API described below, as well as in the [Stream dashboard](https://dash.cloudflare.com/?to=/:account/stream/analytics). The Stream Analytics API uses the Cloudflare GraphQL Analytics API, which can be used across many Cloudflare products. For more about GraphQL, rate limits, filters, and sorting, refer to the [Cloudflare GraphQL Analytics API docs](/analytics/graphql-api). ## Getting started 1. [Generate a Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens) with the **Account Analytics** permission. 2. Use a GraphQL client of your choice to make your first query. [Postman](https://www.postman.com/) has a built-in GraphQL client which can help you run your first query and introspect the GraphQL schema to understand what is possible. Refer to the sections below for available metrics, dimensions, fields, and example queries. ## Server side analytics Stream collects data about the number of minutes of video delivered to viewers for all live and on-demand videos played via HLS or DASH, regardless of whether or not you use the [Stream Player](/stream/viewing-videos/using-the-stream-player/). ### Filters and Dimensions | Field | Description | | ------------------- | -------------------------------------------------------------------------------------------------------- | | `date` | Date | | `datetime` | DateTime | | `uid` | UID of the video | | `clientCountryName` | ISO 3166 alpha2 country code from the client who viewed the video | | `creator` | The [Creator ID](/stream/manage-video-library/creator-id/) associated with individual videos, if present | Some filters, like `date`, can be used with operators, such as `gt` (greater than) and `lt` (less than), as shown in the example query below. For more advanced filtering options, refer to [filtering](/analytics/graphql-api/features/filtering/). ### Metrics | Node | Field | Description | | ----------------------------------- | --------------- | -------------------------- | | `streamMinutesViewedAdaptiveGroups` | `minutesViewed` | Minutes of video delivered | ### Example #### Get minutes viewed by country ```graphql graphql-api-explorer title="GraphQL request" query StreamGetMinutesExample($accountTag: string!, $start: Date, $end: Date) { viewer { accounts(filter: { accountTag: $accountTag }) { streamMinutesViewedAdaptiveGroups( filter: { date_geq: $start, date_lt: $end } orderBy: [sum_minutesViewed_DESC] limit: 100 ) { sum { minutesViewed } dimensions { uid clientCountryName } } } } } ``` ```json title="GraphQL response" { "data": { "viewer": { "accounts": [ { "streamMinutesViewedAdaptiveGroups": [ { "dimensions": { "clientCountryName": "US", "uid": "73c514082b154945a753d0011e9d7525" }, "sum": { "minutesViewed": 2234 } }, { "dimensions": { "clientCountryName": "CN", "uid": "73c514082b154945a753d0011e9d7525" }, "sum": { "minutesViewed": 700 } }, { "dimensions": { "clientCountryName": "IN", "uid": "73c514082b154945a753d0011e9d7525" }, "sum": { "minutesViewed": 553 } } ] } ] } }, "errors": null } ``` ## Pagination GraphQL API supports seek pagination: using filters, you can specify the last video UID so the response only includes data for videos after the last video UID. The query below will return data for 2 videos that follow video UID `5646153f8dea17f44d542a42e76cfd`: ```graphql graphql-api-explorer='{"uId": "5646153f8dea17f44d542a42e76cfd"}' title="GraphQL query" query StreamPaginationExample( $accountTag: string! $start: Date $end: Date $uId: string ) { viewer { accounts(filter: { accountTag: $accountTag }) { videoPlaybackEventsAdaptiveGroups( filter: { date_geq: $start, date_lt: $end, uid_gt: $uId } orderBy: [uid_ASC] limit: 2 ) { count sum { timeViewedMinutes } dimensions { uid } } } } } ``` Here are the steps to implementing pagination: 1. Call the first query without uid_gt filter to get the first set of videos 2. Grab the last video UID from the response from the first query 3. Call next query by specifying uid_gt property and set it to the last video UID. This will return the next set of videos For more on pagination, refer to the [Cloudflare GraphQL Analytics API docs](/analytics/graphql-api/features/pagination/). ## Limitations - The maximum query interval in a single query is 31 days - The maximum data retention period is 90 days --- # Analytics URL: https://developers.cloudflare.com/stream/getting-analytics/ Stream provides server-side analytics that can be used to: * Identify most viewed video content in your app or platform. * Identify where content is viewed from and when it is viewed. * Understand which creators on your platform are publishing the most viewed content, and analyze trends. You can access data via the [Stream dashboard](https://dash.cloudflare.com/?to=/:account/stream/analytics) or via the [GraphQL Analytics API](/stream/getting-analytics/fetching-bulk-analytics). Users will need the **Analytics** permission to access analytics via Dash or GraphQL. --- # Get live viewer counts URL: https://developers.cloudflare.com/stream/getting-analytics/live-viewer-count/ The Stream player has full support for live viewer counts by default. To get the viewer count for live videos for use with third party players, make a `GET` request to the `/views` endpoint. ```bash https://customer-.cloudflarestream.com//views ``` Below is a response for a live video with several active viewers: ```json { "liveViewers": 113 } ``` --- # Search for videos URL: https://developers.cloudflare.com/stream/manage-video-library/searching/ You can search for videos by name through the Stream API by adding a `search` query parameter to the [list media files](/api/resources/stream/methods/list/) endpoint. ## What you will need To make API requests you will need a [Cloudflare API token](https://www.cloudflare.com/a/account/my-account) and your Cloudflare [account ID](https://www.cloudflare.com/a/overview/). ## cURL example This example lists media where the name matches `puppy.mp4`. ```bash curl -X GET "https://api.cloudflare.com/client/v4/accounts//stream?search=puppy" \ -H "Authorization: Bearer " \ -H "Content-Type: application/json" ``` --- # Manage creators URL: https://developers.cloudflare.com/stream/manage-video-library/creator-id/ You can set the creator field with an internal user ID at the time a tokenized upload URL is requested. When the video is uploaded, the creator property is automatically set to the internal user ID which can be used for analytics data or when searching for videos by a specific creator. For basic uploads, you will need to add the Creator ID after you upload the video. ## Upload from URL ```bash curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/copy" \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --data '{"url":"https://example.com/myvideo.mp4","creator": "","thumbnailTimestampPct":0.529241,"allowedOrigins":["example.com"],"requireSignedURLs":true,"watermark":{"uid":"ea95132c15732412d22c1476fa83f27a"}}' ``` **Response** ```json null {35} { "success": true, "errors": [], "messages": [], "result": { "allowedOrigins": ["example.com"], "created": "2014-01-02T02:20:00Z", "duration": 300, "input": { "height": 1080, "width": 1920 }, "maxDurationSeconds": 300, "meta": {}, "modified": "2014-01-02T02:20:00Z", "uploadExpiry": "2014-01-02T02:20:00Z", "playback": { "hls": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8", "dash": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd" }, "preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch", "readyToStream": true, "requireSignedURLs": true, "size": 4190963, "status": { "state": "ready", "pctComplete": "100.000000", "errorReasonCode": "", "errorReasonText": "" }, "thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg", "thumbnailTimestampPct": 0.529241, "creator": "", "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "liveInput": "fc0a8dc887b16759bfd9ad922230a014", "uploaded": "2014-01-02T02:20:00Z", "watermark": { "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "size": 29472, "height": 600, "width": 400, "created": "2014-01-02T02:20:00Z", "downloadedFrom": "https://company.com/logo.png", "name": "Marketing Videos", "opacity": 0.75, "padding": 0.1, "scale": 0.1, "position": "center" } } } ``` ## Set default creators for videos You can associate videos with a single creator by setting a default creator ID value, which you can later use for searching for videos by creator ID or for analytics data. ```bash curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/live_inputs" \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --data '{"DefaultCreator":"1234"}' ``` If you have multiple creators who start live streams, [create a live input](/stream/get-started/#step-1-create-a-live-input) for each creator who will live stream and then set a `DefaultCreator` value per input. Setting the default creator ID for each input ensures that any recorded videos streamed from the creator's input will inherit the `DefaultCreator` value. At this time, you can only manage the default creator ID values via the API. ## Update creator in existing videos To update the creator property in existing videos, make a `POST` request to the video object endpoint with a JSON payload specifying the creator property as show in the example below. ```bash curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/" \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --data '{"creator":"test123"}' ``` ## Direct creator upload ```bash curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/direct_upload" \ --header "Authorization: Bearer " \ --header "Content-Type: application/json" \ --data '{"maxDurationSeconds":300,"expiry":"2021-01-02T02:20:00Z","creator": "", "thumbnailTimestampPct":0.529241,"allowedOrigins":["example.com"],"requireSignedURLs":true,"watermark":{"uid":"ea95132c15732412d22c1476fa83f27a"}}' ``` **Response** ```json null {8} { "success": true, "errors": [], "messages": [], "result": { "uploadURL": "www.example.com/samplepath", "uid": "ea95132c15732412d22c1476fa83f27a", "creator": "", "watermark": { "uid": "ea95132c15732412d22c1476fa83f27a", "size": 29472, "height": 600, "width": 400, "created": "2014-01-02T02:20:00Z", "downloadedFrom": "https://company.com/logo.png", "name": "Marketing Videos", "opacity": 0.75, "padding": 0.1, "scale": 0.1, "position": "center" } } } ``` ## Get videos by Creator ID ```bash curl "https://api.cloudflare.com/client/v4/accounts/{account_id}/stream?after=2014-01-02T02:20:00Z&before=2014-01-02T02:20:00Z&include_counts=false&creator=&limit=undefined&asc=false&status=downloading,queued,inprogress,ready,error" \ --header "Authorization: Bearer " ``` **Response** ```json null {36} { "success": true, "errors": [], "messages": [], "result": [ { "allowedOrigins": ["example.com"], "created": "2014-01-02T02:20:00Z", "duration": 300, "input": { "height": 1080, "width": 1920 }, "maxDurationSeconds": 300, "meta": {}, "modified": "2014-01-02T02:20:00Z", "uploadExpiry": "2014-01-02T02:20:00Z", "playback": { "hls": "https://customer-.cloudflarestream.com/ea95132c15732412d22c1476fa83f27a/manifest/video.m3u8", "dash": "https://customer-.cloudflarestream.com/ea95132c15732412d22c1476fa83f27a/manifest/video.mpd" }, "preview": "https://customer-.cloudflarestream.com/ea95132c15732412d22c1476fa83f27a/watch", "readyToStream": true, "requireSignedURLs": true, "size": 4190963, "status": { "state": "ready", "pctComplete": "100.000000", "errorReasonCode": "", "errorReasonText": "" }, "thumbnail": "https://customer-.cloudflarestream.com/ea95132c15732412d22c1476fa83f27a/thumbnails/thumbnail.jpg", "thumbnailTimestampPct": 0.529241, "creator": "some-creator-id", "uid": "ea95132c15732412d22c1476fa83f27a", "liveInput": "fc0a8dc887b16759bfd9ad922230a014", "uploaded": "2014-01-02T02:20:00Z", "watermark": { "uid": "ea95132c15732412d22c1476fa83f27a", "size": 29472, "height": 600, "width": 400, "created": "2014-01-02T02:20:00Z", "downloadedFrom": "https://company.com/logo.png", "name": "Marketing Videos", "opacity": 0.75, "padding": 0.1, "scale": 0.1, "position": "center" } } ], "total": "35586", "range": "1000" } ``` ## tus Add the Creator ID via the `Upload-Creator` header. For more information, refer to [Resumable and large files (tus)](/stream/uploading-videos/resumable-uploads/#set-creator-property). ## Query by Creator ID with GraphQL After you set the creator property, you can use the [GraphQL API](/analytics/graphql-api/) to filter by a specific creator. Refer to [Fetching bulk analytics](/stream/getting-analytics/fetching-bulk-analytics) for more information about available metrics and filters. --- # Use webhooks URL: https://developers.cloudflare.com/stream/manage-video-library/using-webhooks/ import { Details } from "~/components" Webhooks notify your service when videos successfully finish processing and are ready to stream or if your video enters an error state. ## Subscribe to webhook notifications To subscribe to receive webhook notifications on your service or modify an existing subscription, you will need a [Cloudflare API token](https://dash.cloudflare.com/profile/api-tokens). The webhook notification URL must include the protocol. Only `http://` or `https://` is supported. ```bash curl -X PUT --header 'Authorization: Bearer ' \ https://api.cloudflare.com/client/v4/accounts//stream/webhook \ --data '{"notificationUrl":""}' ``` ```json title="Example response" { "result": { "notificationUrl": "http://www.your-service-webhook-handler.com", "modified": "2019-01-01T01:02:21.076571Z" "secret": "85011ed3a913c6ad5f9cf6c5573cc0a7" }, "success": true, "errors": [], "messages": [] } ``` ## Notifications When a video on your account finishes processing, you will receive a `POST` request notification with information about the video. Note the `status` field indicates whether the video processing finished successfully. ```javascript title="Example POST request body sent in response to successful encoding" { "uid": "dd5d531a12de0c724bd1275a3b2bc9c6", "readyToStream": true, "status": { "state": "ready" }, "meta": {}, "created": "2019-01-01T01:00:00.474936Z", "modified": "2019-01-01T01:02:21.076571Z", // ... } ``` When a video is done processing and all quality levels are encoded, the `state` field returns a `ready` state. The `ready` state can be useful if picture quality is important to you, and you only want to enable video playback when the highest quality levels are available. If higher quality renditions are still processing, videos may sometimes return the `state` field as `ready` and an additional `pctComplete` state that is not `100`. When `pctComplete` reaches `100`, all quality resolutions are available for the video. When at least one quality level is encoded and ready to be streamed, the `readyToStream` value returns `true`. ## Error codes If a video could not process successfully, the `state` field returns `error`, and the `errReasonCode` returns one of the values listed below. * `ERR_NON_VIDEO` – The upload is not a video. * `ERR_DURATION_EXCEED_CONSTRAINT` – The video duration exceeds the constraints defined in the direct creator upload. * `ERR_FETCH_ORIGIN_ERROR` – The video failed to download from the URL. * `ERR_MALFORMED_VIDEO` – The video is a valid file but contains corrupt data that cannot be recovered. * `ERR_DURATION_TOO_SHORT` – The video's duration is shorter than 0.1 seconds. * `ERR_UNKNOWN` – If Stream cannot automatically determine why the video returned an error, the `ERR_UNKNOWN` code will be used. In addition to the `state` field, a video's `readyToStream` field must also be `true` for a video to play. ```bash title="Example error response" {2,4,7} { "readyToStream": true, "status": { "state": "error", "step": "encoding", "pctComplete": "39", "errReasonCode": "ERR_MALFORMED_VIDEO", "errReasonText": "The video was deemed to be corrupted or malformed.", } } ```
```json { "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "creator": null, "thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg", "thumbnailTimestampPct": 0, "readyToStream": true, "status": { "state": "ready", "pctComplete": "39.000000", "errorReasonCode": "", "errorReasonText": "" }, "meta": { "filename": "small.mp4", "filetype": "video/mp4", "name": "small.mp4", "relativePath": "null", "type": "video/mp4" }, "created": "2022-06-30T17:53:12.512033Z", "modified": "2022-06-30T17:53:21.774299Z", "size": 383631, "preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch", "allowedOrigins": [], "requireSignedURLs": false, "uploaded": "2022-06-30T17:53:12.511981Z", "uploadExpiry": "2022-07-01T17:53:12.511973Z", "maxSizeBytes": null, "maxDurationSeconds": null, "duration": 5.5, "input": { "width": 560, "height": 320 }, "playback": { "hls": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8", "dash": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd" }, "watermark": null } ```
## Verify webhook authenticity Cloudflare Stream will sign the webhook requests sent to your notification URLs and include the signature of each request in the `Webhook-Signature` HTTP header. This allows your application to verify the webhook requests are sent by Stream. To verify a signature, you need to retrieve your webhook signing secret. This value is returned in the API response when you create or retrieve the webhook. To verify the signature, get the value of the `Webhook-Signature` header, which will look similar to the example below. `Webhook-Signature: time=1230811200,sig1=60493ec9388b44585a29543bcf0de62e377d4da393246a8b1c901d0e3e672404` ### 1. Parse the signature Retrieve the `Webhook-Signature` header from the webhook request and split the string using the `,` character. Split each value again using the `=` character. The value for `time` is the current [UNIX time](https://en.wikipedia.org/wiki/Unix_time) when the server sent the request. `sig1` is the signature of the request body. At this point, you should discard requests with timestamps that are too old for your application. ### 2. Create the signature source string Prepare the signature source string and concatenate the following strings: * Value of the `time` field for example `1230811200` * Character `.` * Webhook request body (complete with newline characters, if applicable) Every byte in the request body must remain unaltered for successful signature verification. ### 3. Create the expected signature Compute an HMAC with the SHA256 function (HMAC-SHA256) using your webhook secret and the source string from step 2. This step depends on the programming language used by your application. Cloudflare's signature will be encoded to hex. ### 4. Compare expected and actual signatures Compare the signature in the request header to the expected signature. Preferably, use a constant-time comparison function to compare the signatures. If the signatures match, you can trust that Cloudflare sent the webhook. ## Limitations * Webhooks will only be sent after video processing is complete, and the body will indicate whether the video processing succeeded or failed. * Only one webhook subscription is allowed per-account. ## Examples **Golang** Using [crypto/hmac](https://golang.org/pkg/crypto/hmac/#pkg-overview): ```go package main import ( "crypto/hmac" "crypto/sha256" "encoding/hex" "log" ) func main() { secret := []byte("secret from the Cloudflare API") message := []byte("string from step 2") hash := hmac.New(sha256.New, secret) hash.Write(message) hashToCheck := hex.EncodeToString(hash.Sum(nil)) log.Println(hashToCheck) } ``` **Node.js** ```js var crypto = require('crypto'); var key = 'secret from the Cloudflare API'; var message = 'string from step 2'; var hash = crypto.createHmac('sha256', key).update(message); hash.digest('hex'); ``` **Ruby** ```ruby require 'openssl' key = 'secret from the Cloudflare API' message = 'string from step 2' OpenSSL::HMAC.hexdigest('sha256', key, message) ``` **In JavaScript (for example, to use in Cloudflare Workers)** ```javascript const key = 'secret from the Cloudflare API'; const message = 'string from step 2'; const getUtf8Bytes = str => new Uint8Array( [...decodeURIComponent(encodeURIComponent(str))].map(c => c.charCodeAt(0)) ); const keyBytes = getUtf8Bytes(key); const messageBytes = getUtf8Bytes(message); const cryptoKey = await crypto.subtle.importKey( 'raw', keyBytes, { name: 'HMAC', hash: 'SHA-256' }, true, ['sign'] ); const sig = await crypto.subtle.sign('HMAC', cryptoKey, messageBytes); [...new Uint8Array(sig)].map(b => b.toString(16).padStart(2, '0')).join(''); ``` --- # Add custom ingest domains URL: https://developers.cloudflare.com/stream/stream-live/custom-domains/ With custom ingest domains, you can configure your RTMPS feeds to use an ingest URL that you specify instead of using `live.cloudflare.com.` 1. Log in to your [Cloudflare dashboard](https://dash.cloudflare.com) and select your account. 2. Click **Stream** > **Live Inputs**. 3. Click the **Settings** button above the list. The **Custom Input Domains** page displays. 4. Under **Domain**, add your domain and click **Add domain**. 5. At your DNS provider, add a CNAME record that points to `live.cloudflare.com`. If your DNS provider is Cloudflare, this step is done automatically. If you are using Cloudflare for DNS, ensure the [**Proxy status**](/dns/proxy-status/) of your ingest domain is **DNS only** (grey-clouded). ## Delete a custom domain 1. From the **Custom Input Domains** page under **Hostnames**, locate the domain. 2. Click the menu icon under **Action**. Click **Delete**. --- # Download live stream videos URL: https://developers.cloudflare.com/stream/stream-live/download-stream-live-videos/ You can enable downloads for live stream videos from the Cloudflare dashboard. Videos are available for download after they enter the **Ready** state. :::note Downloadable MP4s are only available for live recordings under four hours. Live recordings exceeding four hours can be played at a later time but cannot be downloaded as an MP4. ::: 1. Log in to your [Cloudflare dashboard](https://dash.cloudflare.com) and select your account. 2. Click **Stream** > **Live Inputs**. 3. Click a live input from the list to select it. 4. Under **Videos created by live input**, locate your video and click to select it. 5. Under **Settings**, select **Enable MP4 Downloads**. 6. Click **Save**. You will see a progress bar as the video generates a download link. 7. When the download link is ready, under **Download URL**, copy the URL and enter it in a browser to download the video. --- # DVR for Live URL: https://developers.cloudflare.com/stream/stream-live/dvr-for-live/ Stream Live supports "DVR mode" on an opt-in basis to allow viewers to rewind, resume, and fast-forward a live broadcast. To enable DVR mode, add the `dvrEnabled=true` query parameter to the Stream Player embed source or the HLS manifest URL. ## Stream Player ``` html title="Stream Player embed format"
``` When DVR mode is enabled the Stream Player will: - Show a timeline the viewer can scrub/seek, similar to watching an on-demand video. The timeline will automatically scale to show the growing duration of the broadcast while it is live. - The "LIVE" indicator will show grey if the viewer is behind the live edge or red if they are watching the latest content. Clicking that indicator will jump forward to the live edge. - If the viewer pauses the player, it will resume playback from that time instead of jumping forward to the live edge. ## HLS manifest for custom players ``` text title="HLS manifest URL format" https://customer-.cloudflarestream.com//manifest/video.m3u8?dvrEnabled=true ``` Custom players using a DVR-capable HLS manifest may need additional configuration to surface helpful controls or information. Refer to your player library for additional information. ## Video ID or Input ID Stream Live allows loading the Player or HLS manifest by Video ID or Live Input ID. Refer to [Watch a live stream](/stream/stream-live/watch-live-stream/) for how to retrieve these URLs and compare these options. There are additional considerations when using DVR mode: **Recommended:** Use DVR Mode on a Video ID URL: - When the player loads, it will start playing the active broadcast if it is still live or play the recording if the broadcast has concluded. DVR Mode on a Live Input ID URL: - When the player loads, it will start playing the currently live broadcast if there is one (refer to [Live Input Status](/stream/stream-live/watch-live-stream/#live-input-status)). - If the viewer is still watching _after the broadcast ends,_ they can continue to watch. However, if the player or manifest is then reloaded, it will show the latest broadcast or "Stream has not yet started" (`HTTP 204`). Past broadcasts are not available by Live Input ID. ## Known Limitations - When using DVR Mode and a player/manifest created using a Live Input ID, the player may stall when trying to switch quality levels if a viewer is still watching after a broadcast has concluded. - Performance may be degraded for DVR-enabled broadcasts longer than three hours. Manifests are limited to a maxiumum of 7,200 segments. Segment length is determined by the keyframe interval, also called GOP size. - DVR Mode relies on Version 8 of the HLS manifest specification. Stream uses HLS Version 6 in all other contexts. HLS v8 offers extremely broad compatibility but may not work with certain old player libraries or older devices. - DVR Mode is not available for DASH manifests. --- # Stream live video URL: https://developers.cloudflare.com/stream/stream-live/ Cloudflare Stream lets you or your users [stream live video](https://www.cloudflare.com/learning/video/what-is-live-streaming/), and play live video in your website or app, without managing and configuring any of your own infrastructure. ## How Stream works Stream handles video streaming end-to-end, from ingestion through delivery. 1. For each live stream, you create a unique live input, either using the Stream Dashboard or API. 2. Each live input has a unique Stream Key, that you provide to the creator who is streaming live video. 3. Creators use this Stream Key to broadcast live video to Cloudflare Stream, over either RTMPS or SRT. 4. Cloudflare Stream encodes this live video at multiple resolutions and delivers it to viewers, using Cloudflare's Global Network. You can play video on your website using the [Stream Player](/stream/viewing-videos/using-the-stream-player/) or using [any video player that supports HLS or DASH](/stream/viewing-videos/using-own-player/). ![Diagram the explains the live stream workflow](~/assets/images/stream/live-stream-workflow.png) ## RTMP reconnections As long as your streaming software reconnects, Stream Live will continue to ingest and stream your live video. Make sure the streaming software you use to push RTMP feeds automatically reconnects if the connection breaks. Some apps like OBS reconnect automatically while other apps like FFmpeg require custom configuration. ## Bitrate estimates at each quality level (bitrate ladder) Cloudflare Stream transcodes and makes live streams available to viewers at multiple quality levels. This is commonly referred to as [Adaptive Bitrate Streaming (ABR)](https://www.cloudflare.com/learning/video/what-is-adaptive-bitrate-streaming). With ABR, client video players need to be provided with estimates of how much bandwidth will be needed to play each quality level (ex: 1080p). Stream creates and updates these estimates dynamically by analyzing the bitrate of your users' live streams. This ensures that live video plays at the highest quality a viewer has adequate bandwidth to play, even in cases where the broadcaster's software or hardware provides incomplete or inaccurate information about the bitrate of their live content. ### How it works If a live stream contains content with low visual complexity, like a slideshow presentation, the bandwidth estimates provided in the HLS and DASH manifests will be lower —  a stream like this has a low bitrate and requires relatively little bandwidth, even at high resolution. This ensures that as many viewers as possible view the highest quality level. Conversely, if a live stream contains content with high visual complexity, like live sports with motion and camera panning, the bandwidth estimates provided in the manifest will be higher — a stream like this has a high bitrate and requires more bandwidth. This ensures that viewers with inadequate bandwidth switch down to a lower quality level, and their playback does not buffer. ### How you benefit If you're building a creator platform or any application where your end users create their own live streams, your end users likely use streaming software or hardware that you cannot control. In practice, these live streaming setups often send inaccurate or incomplete information about the bitrate of a given live stream, or are misconfigured by end users. Stream adapts based on the live video that we actually receive, rather than blindly trusting the advertised bitrate. This means that even in cases where your end users' settings are less than ideal, client video players will still receive the most accurate bitrate estimates possible, ensuring the highest quality video playback for your viewers, while avoiding pushing configuration complexity back onto your users. ## Transition from live playback to a recording Recordings are available for live streams within 60 seconds after a live stream ends. You can check a video's status to determine if it's ready to view by making a [`GET` request to the `stream` endpoint](/stream/stream-live/watch-live-stream/#use-the-api) and viewing the `state` or by [using the Cloudflare dashboard](/stream/stream-live/watch-live-stream/#use-the-dashboard). After the live stream ends, you can [replay live stream recordings](/stream/stream-live/replay-recordings/) in the `ready` state by using one of the playback URLs. ## Billing Stream Live is billed identically to the rest of Cloudflare Stream. * You pay $5 per 1000 minutes of recorded video. * You pay $1 per 1000 minutes of delivered video. All Stream Live videos are automatically recorded. There is no additional cost for encoding and packaging live videos. --- # Live Instant Clipping URL: https://developers.cloudflare.com/stream/stream-live/live-instant-clipping/ Stream supports generating clips of live streams and recordings so creators and viewers alike can highlight short, engaging pieces of a longer broadcast or recording. Live instant clips can be created by end users and do not result in additional storage fees or new entries in the video library. :::note[Note:] Clipping works differently for uploaded / on-demand videos. For more information, refer to [Clip videos](/stream/edit-videos/video-clipping/). ::: ## Prerequisites When configuring a [Live input](/stream/stream-live/start-stream-live/), ensure "Live Playback and Recording" (`mode`) is enabled. API keys are not needed to generate a preview or clip, but are needed to create Live Inputs. Live instant clips are generated dynamically from the recording of a live stream. When generating clips manifests or MP4s, always reference the Video ID, not the Live Input ID. If the recording is deleted, the instant clip will no longer be available. ## Preview manifest To help users replay and seek recent content, request a preview manifest by adding a `duration` parameter to the HLS manifest URL: ```txt title="Preview Manifest" https://customer-.cloudflarestream.com//manifest/video.m3u8?duration=5m ``` * `duration` string duration of the preview, up to 5 minutes as either a number of seconds ("30s") or minutes ("3m") When the preview manifest is delivered, inspect the headers for two properties: * `preview-start-seconds` float seconds into the start of the live stream or recording that the preview manifest starts. Useful in applications that allow a user to select a range from the preview because the clip will need to reference its offset from the *broadcast* start time, not the *preview* start time. * `stream-media-id` string the video ID of the live stream or recording. Useful in applications that render the player using an *input* ID because the clip URL should reference the *video* ID. This manifest can be played and seeked using any HLS-compatible player. ### Reading headers Reading headers when loading a manifest requires adjusting how players handle the response. For example, if using [HLS.js](https://github.com/video-dev/hls.js) and the default loader, override the `pLoader` (playlist loader) class: ```js let currentPreviewStart; let currentPreviewVideoID; // Override the pLoader (playlist loader) to read the manifest headers: class pLoader extends Hls.DefaultConfig.loader { constructor(config) { super(config); var load = this.load.bind(this); this.load = function (context, config, callbacks) { if (context.type == 'manifest') { var onSuccess = callbacks.onSuccess; // copy the existing onSuccess handler to fire it later. callbacks.onSuccess = function (response, stats, context, networkDetails) { // The fourth argument here is undocumented in HLS.js but contains // the response object for the manifest fetch, which gives us headers: currentPreviewStart = parseFloat(networkDetails.getResponseHeader('preview-start-seconds')); // Save the start time of the preview manifest currentPreviewVideoID = networkDetails.getResponseHeader('stream-media-id'); // Save the video ID in case the preview was loaded with an input ID onSuccess(response, stats, context); // And fire the exisint success handler. }; } load(context, config, callbacks); }; } } // Specify the new loader class when setting up HLS const hls = new Hls({ pLoader: pLoader, }); ``` ## Clip manifest To play a clip of a live stream or recording, request a clip manifest with a duration and a start time, relative to the start of the live stream. ```txt title="Clip Manifest" https://customer-.cloudflarestream.com//manifest/clip.m3u8?time=600s&duration=30s ``` * `time` string start time of the clip in seconds, from the start of the live stream or recording * `duration` string duration of the clip in seconds, up to 60 seconds max This manifest can be played and seeked using any HLS-compatible player. ## Clip MP4 download An MP4 of the clip can also be generated dynamically to be saved and shared on other platforms. ```txt title="Clip MP4 Download" https://customer-.cloudflarestream.com//clip.mp4?time=600s&duration=30s&filename=clip.mp4 ``` * `time` string start time of the clip in seconds, from the start of the live stream or recording (example: "500s") * `duration` string duration of the clip in seconds, up to 60 seconds max (example: "60s") * `filename` string *(optional)* a filename for the clip --- # Record and replay live streams URL: https://developers.cloudflare.com/stream/stream-live/replay-recordings/ Live streams are automatically recorded, and available instantly once a live stream ends. To get a list of recordings for a given input ID, make a [`GET` request to `/live_inputs//videos`](/api/resources/stream/subresources/live_inputs/methods/get/) and filter for videos where `state` is set to `ready`: ```bash title="Request" curl -X GET \ -H "Authorization: Bearer " \ https://dash.cloudflare.com/api/v4/accounts//stream/live_inputs//videos ``` ```json title="Response" {10} { "result": [ ... { "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg", "thumbnailTimestampPct": 0, "readyToStream": true, "status": { "state": "ready", "pctComplete": "100.000000", "errorReasonCode": "", "errorReasonText": "" }, "meta": { "name": "Stream Live Test 22 Sep 21 22:12 UTC" }, "created": "2021-09-22T22:12:53.587306Z", "modified": "2021-09-23T00:14:05.591333Z", "size": 0, "preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch", "allowedOrigins": [], "requireSignedURLs": false, "uploaded": "2021-09-22T22:12:53.587288Z", "uploadExpiry": null, "maxSizeBytes": null, "maxDurationSeconds": null, "duration": 7272, "input": { "width": 640, "height": 360 }, "playback": { "hls": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8", "dash": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd" }, "watermark": null, "liveInput": "34036a0695ab5237ce757ac53fd158a2" } ], "success": true, "errors": [], "messages": [] } ``` --- # Simulcast (restream) videos URL: https://developers.cloudflare.com/stream/stream-live/simulcasting/ import { Render } from "~/components" Simulcasting lets you forward your live stream to third-party platforms such as Twitch, YouTube, Facebook, Twitter, and more. You can simulcast to up to 50 concurrent destinations from each live input. To begin simulcasting, select an input and add one or more Outputs. ## Add an Output using the API Add an Output to start retransmitting live video. You can add or remove Outputs at any time during a broadcast to start and stop retransmitting. ```bash title="Request" curl -X POST \ --data '{"url": "rtmp://a.rtmp.youtube.com/live2","streamKey": ""}' \ -H "Authorization: Bearer " \ https://api.cloudflare.com/client/v4/accounts//stream/live_inputs//outputs ``` ```json title="Response" { "result": { "uid": "6f8339ed45fe87daa8e7f0fe4e4ef776", "url": "rtmp://a.rtmp.youtube.com/live2", "streamKey": "" }, "success": true, "errors": [], "messages": [] } ``` ## Control when you start and stop simulcasting You can enable and disable individual live outputs via the [API](/api/resources/stream/subresources/live_inputs/subresources/outputs/methods/update/) or [Stream dashboard](https://dash.cloudflare.com/?to=/:account/stream/inputs), allowing you to: * Start a live stream, but wait to start simulcasting to YouTube and Twitch until right before the content begins. * Stop simulcasting before the live stream ends, to encourage viewers to transition from a third-party service like YouTube or Twitch to a direct live stream. * Give your own users manual control over when they go live to specific simulcasting destinations. When a live output is disabled, video is not simulcast to the live output, even when actively streaming to the corresponding live input. By default, all live outputs are enabled. ### Enable outputs from the dashboard: 1. From Live Inputs in the [Cloudflare dashboard](https://dash.cloudflare.com/?to=/:account/stream/inputs), select an input from the list. 2. Under **Outputs** > **Enabled**, set the toggle to enabled or disabled. ## Manage outputs | Command | Method | Endpoint | | ------------------------------------------------------------------------ | -------- | ------------------------------------------------------------------------ | | [List outputs](/api/resources/stream/subresources/live_inputs/methods/list/) | `GET` | `accounts/:account_identifier/stream/live_inputs` | | [Delete outputs](/api/resources/stream/subresources/live_inputs/methods/delete/) | `DELETE` | `accounts/:account_identifier/stream/live_inputs/:live_input_identifier` | | [List All Outputs Associated With A Specified Live Input](/api/resources/stream/subresources/live_inputs/subresources/outputs/methods/list/) | `GET` | `/accounts/{account_id}/stream/live_inputs/{live_input_identifier}/outputs` | | [Delete An Output](/api/resources/stream/subresources/live_inputs/subresources/outputs/methods/delete/) | `DELETE` | `/accounts/{account_id}/stream/live_inputs/{live_input_identifier}/outputs/{output_identifier}` | If the associated live input is already retransmitting to this output when you make the `DELETE` request, that output will be disconnected within 30 seconds. --- # Watch a live stream URL: https://developers.cloudflare.com/stream/stream-live/watch-live-stream/ import { Render } from "~/components" When a [Live Input](/stream/stream-live/start-stream-live/) begins receiving a broadcast, a new video is automatically created if the input's `mode` property is set to `automatic`. To watch, Stream offers a built-in Player or you use a custom player with the HLS and DASH manifests. ## View by Live Input ID or Video ID Whether you use the Stream Player or a custom player with a manifest, you can reference the Live Input ID or a specific Video ID. The main difference is what happens when a broadcast concludes. Use a Live Input ID in instances where a player should always show the active broadcast, if there is one, or a "Stream has not started" message if the input is idle. This option is best for cases where a page is dedicated to a creator, channel, or recurring program. The Live Input ID is provisioned for you when you create the input; it will not change. Use a Video ID in instances where a player should be used to display a single broadcast or its recording once the broadcast has concluded. This option is best for cases where a page is dedicated to a one-time event, specific episode/occurance, or date. There is a _new_ Video ID generated for each broadcast _when it starts._ Using DVR mode, explained below, there are additional considerations. Stream's URLs are all templatized for easy generation: **Stream built-in Player URL format:** ``` https://customer-.cloudflarestream.com//iframe ``` A full embed code can be generated in Dash or with the API. **HLS Manifest URL format:** ``` https://customer-.cloudflarestream.com//manifest/video.m3u8 ``` You can also retrieve the embed code or manifest URLs from Dash or the API. ## Use the dashboard To get the Stream built-in player embed code or HLS Manifest URL for a custom player: 1. Log in to your [Cloudflare dashboard](https://dash.cloudflare.com) and select your account. 2. Select **Stream** > **Live Inputs**. 3. Select a live input from the list. 4. Locate the **Embed** and **HLS Manifest URL** beneath the video. 5. Determine which option to use and then select **Click to copy** beneath your choice. The embed code or manifest URL retrieved in Dash will reference the Live Input ID. ## Use the API To retrieve the player code or manifest URLs via the API, fetch the Live Input's list of videos: ```bash title="Request" curl -X GET \ -H "Authorization: Bearer " \ https://api.cloudflare.com/client/v4/accounts//stream/live_inputs//videos ``` A live input will have multiple videos associated with it, one for each broadcast. If there is an active broadcast, the first video in the response will have a `live-inprogress` status. Other videos in the response represent recordings which can be played on-demand. Each video in the response, including the active broadcast if there is one, contains the HLS and DASH URLs and a link to the Stream player. Noteworthy properties include: - `preview` -- Link to the Stream player to watch - `playback`.`hls` -- HLS Manifest - `playback`.`dash` -- DASH Manifest In the example below, the state of the live video is `live-inprogress` and the state for previously recorded video is `ready`. ```json title="Response" {4,7,21,28,32,46} { "result": [ { "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg", "status": { "state": "live-inprogress", "errorReasonCode": "", "errorReasonText": "" }, "meta": { "name": "Stream Live Test 23 Sep 21 05:44 UTC" }, "created": "2021-09-23T05:44:30.453838Z", "modified": "2021-09-23T05:44:30.453838Z", "size": 0, "preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch", ... "playback": { "hls": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8", "dash": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd" }, ... }, { "uid": "6b9e68b07dfee8cc2d116e4c51d6a957", "thumbnail": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/thumbnails/thumbnail.jpg", "thumbnailTimestampPct": 0, "readyToStream": true, "status": { "state": "ready", "pctComplete": "100.000000", "errorReasonCode": "", "errorReasonText": "" }, "meta": { "name": "CFTV Staging 22 Sep 21 22:12 UTC" }, "created": "2021-09-22T22:12:53.587306Z", "modified": "2021-09-23T00:14:05.591333Z", "size": 0, "preview": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/watch", ... "playback": { "hls": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.m3u8", "dash": "https://customer-f33zs165nr7gyfy4.cloudflarestream.com/6b9e68b07dfee8cc2d116e4c51d6a957/manifest/video.mpd" }, } ], } ``` These will reference the Video ID. ## Live input status You can check whether a live input is currently streaming and what its active video ID is by making a request to its `lifecycle` endpoint. The Stream player does this automatically to show a note when the input is idle. Custom players may require additional support. ```bash curl -X GET \ -H "Authorization: Bearer " \ https://customer-.cloudflarestream.com//lifecycle ``` In the example below, the response indicates the `ID` is for an input with an active `videoUID`. The `live` status value indicates the input is actively streaming. ```json { "isInput": true, "videoUID": "55b9b5ce48c3968c6b514c458959d6a", "live": true } ``` ```json { "isInput": true, "videoUID": null, "live": false } ``` When viewing a live stream via the live input ID, the `requireSignedURLs` and `allowedOrigins` options in the live input recording settings are used. These settings are independent of the video-level settings. ## Live stream recording playback After a live stream ends, a recording is automatically generated and available within 60 seconds. To ensure successful video viewing and playback, keep the following in mind: * If a live stream ends while a viewer is watching, viewers using the Stream player should wait 60 seconds and then reload the player to view the recording of the live stream. * After a live stream ends, you can check the status of the recording via the API. When the video state is `ready`, you can use one of the manifest URLs to stream the recording. While the recording of the live stream is generating, the video may report as `not-found` or `not-started`. If you are not using the Stream player for live stream recordings, refer to [Record and replay live streams](/stream/stream-live/replay-recordings/) for more information on how to replay a live stream recording. --- # Receive Live Webhooks URL: https://developers.cloudflare.com/stream/stream-live/webhooks/ import { AvailableNotifications } from "~/components" Stream Live offers webhooks to notify your service when an Input connects, disconnects, or encounters an error with Stream Live. ## Subscribe to Stream Live Webhooks 1. Log in to your Cloudflare account and click **Notifications**. 2. From the **Notifications** page, click the **Destinations** tab. 3. On the **Destinations** page under **Webhooks**, click **Create**. 4. Enter the information for your webhook and click **Save and Test**. 5. To create the notification, from the **Notifications** page, click the **All Notifications** tab. 6. Next to **Notifications**, click **Add**. 7. Under the list of products, locate **Stream** and click **Select**. 8. Enter a name and optional description. 9. Under **Webhooks**, click **Add webhook** and click your newly created webhook. 10. Click **Next**. 11. By default, you will receive webhook notifications for all Live Inputs. If you only wish to receive webhooks for certain inputs, enter a comma-delimited list of Input IDs in the text field. 12. When you are done, click **Create**.

```json title="Example webhook payload" { "name": "Live Webhook Test", "text": "Notification type: Stream Live Input\nInput ID: eb222fcca08eeb1ae84c981ebe8aeeb6\nEvent type: live_input.disconnected\nUpdated at: 2022-01-13T11:43:41.855717910Z", "data": { "notification_name": "Stream Live Input", "input_id": "eb222fcca08eeb1ae84c981ebe8aeeb6", "event_type": "live_input.disconnected", "updated_at": "2022-01-13T11:43:41.855717910Z" }, "ts": 1642074233 } ``` The `event_type` property of the data object will either be `live_input.connected`, `live_input.disconnected`, or `live_input.errored`. If there are issues detected with the input, the `event_type` will be `live_input.errored`. Additional data will be under the `live_input_errored` json key and will include a `code` with one of the values listed below. ## Error codes * `ERR_GOP_OUT_OF_RANGE` – The input GOP size or keyframe interval is out of range. * `ERR_UNSUPPORTED_VIDEO_CODEC` – The input video codec is unsupported for the protocol used. * `ERR_UNSUPPORTED_AUDIO_CODEC` – The input audio codec is unsupported for the protocol used. * `ERR_STORAGE_QUOTA_EXHAUSTED` – The account storage quota has been exceeded. Delete older content or purcahse additional storage. * `ERR_MISSING_SUBSCRIPTION` – Unauthorized to start a live stream. Check subscription or log into Dash for details. ```json title="Example live_input.errored webhook payload" { "name": "Live Webhook Test", "text": "Notification type: Stream Live Input\nInput ID: 2c28dd2cc444cb77578c4840b51e43a8\nEvent type: live_input.errored\nUpdated at: 2024-07-09T18:07:51.077371662Z\nError Code: ERR_GOP_OUT_OF_RANGE\nError Message: Input GOP size or keyframe interval is out of range.\nVideo Codec: \nAudio Codec: ", "data": { "notification_name": "Stream Live Input", "input_id": "eb222fcca08eeb1ae84c981ebe8aeeb6", "event_type": "live_input.errored", "updated_at": "2024-07-09T18:07:51.077371662Z", "live_input_errored": { "error": { "code": "ERR_GOP_OUT_OF_RANGE", "message": "Input GOP size or keyframe interval is out of range." }, "video_codec": "", "audio_codec": "" } }, "ts": 1720548474, } ``` --- # Start a live stream URL: https://developers.cloudflare.com/stream/stream-live/start-stream-live/ import { InlineBadge, Render, Badge } from "~/components"; After you subscribe to Stream, you can create Live Inputs in Dash or via the API. Broadcast to your new Live Input using RTMPS or SRT. SRT supports newer video codecs and makes using accessibility features, such as captions and multiple audio tracks, easier. **First time live streaming?** You will need software to send your video to Cloudflare. [Learn how to go live on Stream using OBS Studio](/stream/examples/obs-from-scratch/). ## Use the dashboard **Step 1:** [Create a live input via the Stream Dashboard](https://dash.cloudflare.com/?to=/:account/stream/inputs/create). ![Create live input field from dashboard](~/assets/images/stream/create-live-input-from-stream-dashboard.png) **Step 2:** Copy the RTMPS URL and key, and use them with your live streaming application. We recommend using [Open Broadcaster Software (OBS)](https://obsproject.com/) to get started. ![Example of RTMPS URL field](~/assets/images/stream/copy-rtmps-url-from-stream-dashboard.png) **Step 3:** Go live and preview your live stream in the Stream Dashboard In the Stream Dashboard, within seconds of going live, you will see a preview of what your viewers will see. To add live video playback to your website or app, refer to [Play videos](/stream/viewing-videos). ## Use the API To start a live stream programmatically, make a `POST` request to the `/live_inputs` endpoint: ```bash title="Request" curl -X POST \ --header "Authorization: Bearer " \ --data '{"meta": {"name":"test stream"},"recording": { "mode": "automatic" }}' \ https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/live_inputs ``` ```json title="Response" { "uid": "f256e6ea9341d51eea64c9454659e576", "rtmps": { "url": "rtmps://live.cloudflare.com:443/live/", "streamKey": "MTQ0MTcjM3MjI1NDE3ODIyNTI1MjYyMjE4NTI2ODI1NDcxMzUyMzcf256e6ea9351d51eea64c9454659e576" }, "created": "2021-09-23T05:05:53.451415Z", "modified": "2021-09-23T05:05:53.451415Z", "meta": { "name": "test stream" }, "status": null, "recording": { "mode": "automatic", "requireSignedURLs": false, "allowedOrigins": null, "hideLiveViewerCount": false }, "deleteRecordingAfterDays": null, "preferLowLatency": false } ``` #### Optional API parameters [API Reference Docs for `/live_inputs`](/api/resources/stream/subresources/live_inputs/methods/create/) - `preferLowLatency` boolean default: `false` - When set to true, this live input will be enabled for the beta Low-Latency HLS pipeline. The Stream built-in player will automatically use LL-HLS when possible. (Recording `mode` property must also be set to `automatic`.) - `deleteRecordingAfterDays` integer default: `null` (any) - Specifies a date and time when the recording, not the input, will be deleted. This property applies from the time the recording is made available and ready to stream. After the recording is deleted, it is no longer viewable and no longer counts towards storage for billing. Minimum value is `30`, maximum value is `1096`. When the stream ends, a `scheduledDeletion` timestamp is calculated using the `deleteRecordingAfterDays` value if present. Note that if the value is added to a live input while a stream is live, the property will only apply to future streams. - `timeoutSeconds` integer default: `0` - The `timeoutSeconds` property specifies how long a live feed can be disconnected before it results in a new video being created. The following four properties are nested under the `recording` object. - `mode` string default: `off` - When the mode property is set to `automatic`, the live stream will be automatically available for viewing using HLS/DASH. In addition, the live stream will be automatically recorded for later replays. By default, recording mode is set to `off`, and the input will not be recorded or available for playback. - `requireSignedURLs` boolean default: `false` - The `requireSignedURLs` property indicates if signed URLs are required to view the video. This setting is applied by default to all videos recorded from the input. In addition, if viewing a video via the live input ID, this field takes effect over any video-level settings. - `allowedOrigins` integer default: `null` (any) - The `allowedOrigins` property can optionally be invoked to provide a list of allowed origins. This setting is applied by default to all videos recorded from the input. In addition, if viewing a video via the live input ID, this field takes effect over any video-level settings. - `hideLiveViewerCount` boolean default: `false` - Restrict access to the live viewer count and remove the value from the player. ## Manage live inputs You can update live inputs by making a `PUT` request: ```bash title="Request" curl --request PUT \ https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/live_inputs/{input_id} \ --header "Authorization: Bearer " \ --data '{"meta": {"name":"test stream 1"},"recording": { "mode": "automatic", "timeoutSeconds": 10 }}' ``` Delete a live input by making a `DELETE` request: ```bash title="Request" curl --request DELETE \ https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/live_inputs/{input_id} \ --header "Authorization: Bearer " ``` ## Recommendations, requirements and limitations ### Recommendations - Your creators should use an appropriate bitrate for their live streams, typically well under 12Mbps (12000Kbps). High motion, high frame rate content typically should use a higher bitrate, while low motion content like slide presentations should use a lower bitrate. - Your creators should use a [GOP duration](https://en.wikipedia.org/wiki/Group_of_pictures) (keyframe interval) of between 2 to 8 seconds. The default in most encoding software and hardware, including Open Broadcaster Software (OBS), is within this range. Setting a lower GOP duration will reduce latency for viewers, while also reducing encoding efficiency. Setting a higher GOP duration will improve encoding efficiency, while increasing latency for viewers. This is a tradeoff inherent to video encoding, and not a limitation of Cloudflare Stream. - When possible, select CBR (constant bitrate) instead of VBR (variable bitrate) as CBR helps to ensure a stable streaming experience while preventing buffering and interruptions. #### Low-Latency HLS broadcast recommendations - For lowest latency, use a GOP size (keyframe interval) of 1 or 2 seconds. - Broadcast to the RTMP endpoint if possible. - If using OBS, select the "ultra low" latency profile. ### Requirements - Closed GOPs are required. This means that if there are any B frames in the video, they should always refer to frames within the same GOP. This setting is the default in most encoding software and hardware, including [OBS Studio](https://obsproject.com/). - Stream Live only supports H.264 video and AAC audio codecs as inputs. This requirement does not apply to inputs that are relayed to Stream Connect outputs. Stream Live supports ADTS but does not presently support LATM. - Clients must be configured to reconnect when a disconnection occurs. Stream Live is designed to handle reconnection gracefully by continuing the live stream. ### Limitations - Watermarks cannot yet be used with live videos. - If a live video exceeds seven days in length, the recording will be truncated to seven days. Only the first seven days of live video content will be recorded. --- # Transform videos URL: https://developers.cloudflare.com/stream/transform-videos/ Media Transformations let you optimize and manipulate videos stored _outside_ of the Cloudflare Stream. Transformed videos and images are served from one of your zones on Cloudflare. To transform a video or image, you must [enable transformations](/stream/transform-videos/#getting-started) for your zone. If your zone already has Image Transformations enabled, you can also optimize videos with Media Transformations. ## Getting started You can dynamically optimize and generate still images from videos that are stored _outside_ of Cloudflare Stream with Media Transformations. Cloudflare will automatically cache every transformed video or image on our global network so that you store only the original image at your origin. To enable transformations on your zone: 1. Log in to the [Cloudflare dashboard](https://dash.cloudflare.com/login) and select your account. 2. Go to **Stream** > **Transformations**. 3. Locate the specific zone where you want to enable transformations. 4. Select **Enable** for zone. ## Transform a video by URL You can convert and resize videos by requesting them via a specially-formatted URL, without writing any code. The URL format is: ``` https://example.com/cdn-cgi/media// ``` - `example.com`: Your website or zone on Cloudflare, with Transformations enabled. - `/cdn-cgi/media/`: A prefix that identifies a special path handled by Cloudflare's built-in media transformation service. - ``: A comma-separated list of options. Refer to the available options below. - ``: A full URL (starting with `https://` or `http://`) of the original asset to resize. For example, this URL will source an HD video from an R2 bucket, shorten it, crop and resize it as a square, and remove the audio. ``` https://example.com/cdn-cgi/media/mode=video,time=5s,duration=5s,width=500,height=500,fit=crop,audio=false/https://pub-8613b7f94d6146408add8fefb52c52e8.r2.dev/aus-mobile-demo.mp4 ``` The result is an MP4 that can be used in an HTML video element without a player library. ## Options ### `mode` Specifies the kind of output to generate. - `video`: Outputs an H.264/AAC optimized MP4 file. - `frame`: Outputs a still image. - `spritesheet`: Outputs a JPEG with multiple frames. ### `time` Specifies when to start extracting the output in the input file. Depends on `mode`: - When `mode` is `spritesheet` or `video`, specifies the timestamp where the output will start. - When `mode` is `frame`, specifies the timestamp from which to extract the still image. - Formats as a time string, for example: 5s, 2m - Acceptable range: 0 – 30s - Default: 0 ### `duration` The duration of the output video or spritesheet. Depends on `mode`: - When `mode` is `video`, specifies the duration of the output. - When `mode` is `spritesheet`, specifies the time range from which to select frames. - Acceptable range: 1s - 60s (or 1m) - Default: input duration or 30 seconds, whichever is shorter ### `fit` In combination with `width` and `height`, specifies how to resize and crop the output. If the output is resized, it will always resize proportionally so content is not stretched. - `contain`: Respecting aspect ratio, scales a video up or down to be entirely contained within output dimensions. - `scale-down`: Same as contain, but downscales to fit only. Do not upscale. - `cover`: Respecting aspect ratio, scales a video up or down to entirely cover the output dimensions, with a center-weighted crop of the remainder. ### `height` Specifies maximum height of the output in pixels. Exact behavior depends on `fit`. - Acceptable range: 10-2000 pixels ### `width` Specifies the maximum width of the image in pixels. Exact behavior depends on `fit`. - Acceptable range: 10-2000 pixels ### `audio` When `mode` is `video`, specifies whether or not to include the source audio in the output. - `true`: Includes source audio. - `false`: Output will be silent. - Default: `true` ### `format` If `mode` is `frame`, specifies the image output format. - Acceptable options: `jpg`, `png` ## Source video requirements Input video must be less than 100MB. Input video should be an MP4 with H.264 encoded video and AAC or MP3 encoded audio. Other formats may work but are untested. ## Limitations Media Transformations are currently in beta. During this period: - Transformations are available for all enabled zones free-of-charge. - Restricting allowed origins for transformations are coming soon. - Outputs from Media Transformations will be cached, but if they must be regenerated, the origin fetch is not cached and may result in subsequent requests to the origin asset. ## Pricing Media Transformations will be free for all customers while in beta. After that, Media Transforamtions and Image Transformations will use the same subscriptions and usage metrics. - Generating a still frame (single image) from a video counts as 1 transformation. - Generating an optimized video counts as 1 transformation _per second of the output_ video. - Each unique transformation is only billed once per month. - All Media and Image Transformations cost $0.50 per 1,000 monthly unique transformation operations, with a free monthly allocation of 5,000. --- # Define source origin URL: https://developers.cloudflare.com/stream/transform-videos/sources/ When optimizing remote videos, you can specify which origins can be used as the source for transformed videos. By default, Cloudflare accepts only source videos from the zone where your transformations are served. On this page, you will learn how to define and manage the origins for the source videos that you want to optimize. :::note The allowed origins setting applies to requests from Cloudflare Workers. If you use a Worker to optimize remote videos via a `fetch()` subrequest, then this setting may conflict with existing logic that handles source videos. ::: ## Configure origins To get started, you must have [transformations enabled on your zone](/stream/transform-videos/#getting-started). In the Cloudflare dashboard, go to **Stream** > **Transformations** and select the zone where you want to serve transformations. In **Sources**, you can configure the origins for transformations on your zone. ![Enable allowed origins from the Cloudflare dashboard](~/assets/images/images/allowed-origins.png) ## Allow source videos only from allowed origins You can restrict source videos to **allowed origins**, which applies transformations only to source videos from a defined list. By default, your accepted sources are set to **allowed origins**. Cloudflare will always allow source videos from the same zone where your transformations are served. If you request a transformation with a source video from outside your **allowed origins**, then the video will be rejected. For example, if you serve transformations on your zone `a.com` and do not define any additional origins, then `a.com/video.mp4` can be used as a source video, but `b.com/video.mp4` will return an error. To define a new origin: 1. From **Sources**, select **Add origin**. 2. Under **Domain**, specify the domain for the source video. Only valid web URLs will be accepted. ![Add the origin for source videos in the Cloudflare dashboard](~/assets/images/images/add-origin.png) When you add a root domain, subdomains are not accepted. In other words, if you add `b.com`, then source videos from `media.b.com` will be rejected. To support individual subdomains, define an additional origin such as `media.b.com`. If you add only `media.b.com` and not the root domain, then source videos from the root domain (`b.com`) and other subdomains (`cdn.b.com`) will be rejected. To support all subdomains, use the `*` wildcard at the beginning of the root domain. For example, `*.b.com` will accept source videos from the root domain (like `b.com/video.mp4`) as well as from subdomains (like `media.b.com/video.mp4` or `cdn.b.com/video.mp4`). 3. Optionally, you can specify the **Path** for the source video. If no path is specified, then source videos from all paths on this domain are accepted. Cloudflare checks whether the defined path is at the beginning of the source path. If the defined path is not present at the beginning of the path, then the source video will be rejected. For example, if you define an origin with domain `b.com` and path `/themes`, then `b.com/themes/video.mp4` will be accepted but `b.com/media/themes/video.mp4` will be rejected. 4. Select **Add**. Your origin will now appear in your list of allowed origins. 5. Select **Save**. These changes will take effect immediately. When you configure **allowed origins**, only the initial URL of the source video is checked. Any redirects, including URLs that leave your zone, will be followed, and the resulting video will be transformed. If you change your accepted sources to **any origin**, then your list of sources will be cleared and reset to default. ## Allow source videos from any origin When your accepted sources are set to **any origin**, any publicly available video can be used as the source video for transformations on this zone. **Any origin** is less secure and may allow third parties to serve transformations on your zone. --- # Direct creator uploads URL: https://developers.cloudflare.com/stream/uploading-videos/direct-creator-uploads/ Direct creator uploads let your end users upload videos directly to Cloudflare Stream without exposing your API token to clients. - If your video is a [basic upload](/stream/uploading-videos/direct-creator-uploads/#basic-uploads) under 200 MB and users do not need resumable uploads, generate a URL that accepts an HTTP post request. - If your video is over 200 MB or if you need to allow users to [resume interrupted uploads](/stream/uploading-videos/direct-creator-uploads/#resumable-uploads), generate a URL using the tus protocol. In either case, you must specify a maximum duration to reserve for the user's upload to ensure it can be accommodated within your available storage. ## Basic uploads Use this option if your users upload videos under 200 MB, and you do not need to allow resumable uploads. 1. Generate a unique, one-time upload URL using the [Direct upload API](/api/resources/stream/subresources/direct_upload/methods/create/). ```sh title="Generate upload" curl https://api.cloudflare.com/client/v4/accounts/{account_id}/stream/direct_upload \ --header 'Authorization: Bearer ' \ --data '{ "maxDurationSeconds": 3600 }' ``` {/* */} ```json output {3} { "result": { "uploadURL": "https://upload.videodelivery.net/f65014bc6ff5419ea86e7972a047ba22", "uid": "f65014bc6ff5419ea86e7972a047ba22" }, "success": true, "errors": [], "messages": [] } ``` 2. With the `uploadURL` from the previous step, users can upload video files that are limited to 200 MB in size. Refer to the example request below. {/* */} ```bash {3} title="Upload a video to the unique one-time upload URL" curl --request POST \ --form file=@/Users/mickie/Downloads/example_video.mp4 \ https://upload.videodelivery.net/f65014bc6ff5419ea86e7972a047ba22 ``` A successful upload will receive a `200` HTTP status code response. If the upload does not meet the upload constraints defined at time of creation or is larger than 200 MB in size, you will receive a `4xx` HTTP status code response. ## Resumable uploads 1. Create your own API endpoint that returns an upload URL. The example below shows how to build a Worker to get a URL you can use to upload your video. The one-time upload URL is returned in the `Location` header of the response, not in the response body. ```javascript {23} title="Example API endpoint" export async function onRequest(context) { const { request, env } = context; const { CLOUDFLARE_ACCOUNT_ID, CLOUDFLARE_API_TOKEN } = env; const endpoint = `https://api.cloudflare.com/client/v4/accounts/${CLOUDFLARE_ACCOUNT_ID}/stream?direct_user=true`; const response = await fetch(endpoint, { method: "POST", headers: { Authorization: `bearer ${CLOUDFLARE_API_TOKEN}`, "Tus-Resumable": "1.0.0", "Upload-Length": request.headers.get("Upload-Length"), "Upload-Metadata": request.headers.get("Upload-Metadata"), }, }); const destination = response.headers.get("Location"); return new Response(null, { headers: { "Access-Control-Expose-Headers": "Location", "Access-Control-Allow-Headers": "*", "Access-Control-Allow-Origin": "*", Location: destination, }, }); } ``` 2. Use this API endpoint **directly** in your tus client. A common mistake is to extract the upload URL from your new API endpoint, and use this directly. See below for a complete example of how to use the API from Step 1 with the uppy tus client. ```html {35} title="Upload a video using the uppy tus client"
    ``` For more details on using tus and example client code, refer to [Resumable and large files (tus)](/stream/uploading-videos/resumable-uploads/). ## Upload-Metadata header syntax You can apply the [same constraints](/api/resources/stream/subresources/direct_upload/methods/create/) as Direct Creator Upload via basic upload when using tus. To do so, you must pass the `expiry` and `maxDurationSeconds` as part of the `Upload-Metadata` request header as part of the first request (made by the Worker in the example above.) The `Upload-Metadata` values are ignored from subsequent requests that do the actual file upload. The `Upload-Metadata` header should contain key-value pairs. The keys are text and the values should be encoded in base64. Separate the key and values by a space, _not_ an equal sign. To join multiple key-value pairs, include a comma with no additional spaces. In the example below, the `Upload-Metadata` header is instructing Stream to only accept uploads with max video duration of 10 minutes, uploaded prior to the expiry timestamp, and to make this video private: `'Upload-Metadata: maxDurationSeconds NjAw,requiresignedurls,expiry MjAyNC0wMi0yN1QwNzoyMDo1MFo='` `NjAw` is the base64 encoded value for "600" (or 10 minutes). `MjAyNC0wMi0yN1QwNzoyMDo1MFo=` is the base64 encoded value for "2024-02-27T07:20:50Z" (an RFC3339 format timestamp) ## Track upload progress After the creation of a unique one-time upload URL, you may wish to retain the unique identifier (`uid`) returned in the response to track the progress of a user's upload. You can do that two ways: - [Search for a video](/stream/manage-video-library/searching/) with the UID to check the status. - [Create a webhook subscription](/stream/manage-video-library/using-webhooks/) to receive notifications about the video status. These notifications include the video's UID. ## Billing considerations Direct Creator Upload links count towards your storage limit even if your users have not yet uploaded video to this URL. If the link expires before it is used or the upload cannot be processed, the storage reservation will be released. Otherwise, once the upload is encoded, its true duration will be counted toward storage and the reservation will be released. For a detailed breakdown of pricing and example scenarios, refer to [Pricing](/stream/pricing/). --- # Upload videos URL: https://developers.cloudflare.com/stream/uploading-videos/ Before you upload your video, review the options for uploading a video, supported formats, and recommendations. ## Upload options | Upload method | When to use | |---------------|-------------| |[Stream Dashboard](https://dash.cloudflare.com/?to=/:account/stream)| Upload videos from the Stream Dashboard without writing any code. |[Upload with a link](/stream/uploading-videos/upload-via-link/)| Upload videos using a link, such as an S3 bucket or content management system. |[Upload video file](/stream/uploading-videos/upload-video-file/)| Upload videos stored on a computer. |[Direct creator uploads](/stream/uploading-videos/direct-creator-uploads/)| Allows end users of your website or app to upload videos directly to Cloudflare Stream. ## Supported video formats :::note Files must be less than 30 GB, and content should be encoded and uploaded in the same frame rate it was recorded. ::: - MP4 - MKV - MOV - AVI - FLV - MPEG-2 TS - MPEG-2 PS - MXF - LXF - GXF - 3GP - WebM - MPG - Quicktime ## Recommendations for on-demand videos - Optional but ideal settings: - MP4 containers - AAC audio codec - H264 video codec - 60 or fewer frames per second - Closed GOP (_Only required for live streaming._) - Mono or Stereo audio. Stream will mix audio tracks with more than two channels down to stereo. --- # Player API URL: https://developers.cloudflare.com/stream/uploading-videos/player-api/ Attributes are added in the `` tag without quotes, as you can see below: ``` ``` Multiple attributes can be used together, added one after each other like this: ``` ``` ## Supported attributes * `autoplay` boolean * Tells the browser to immediately start downloading the video and play it as soon as it can. Note that mobile browsers generally do not support this attribute, the user must tap the screen to begin video playback. Please consider mobile users or users with Internet usage limits as some users do not have unlimited Internet access before using this attribute. :::note To disable video autoplay, the `autoplay` attribute needs to be removed altogether as this attribute. Setting `autoplay="false"` will not work; the video will autoplay if the attribute is there in the `` tag. In addition, some browsers now prevent videos with audio from playing automatically. You may add the `mute` attribute to allow your videos to autoplay. For more information, see [new video policies for iOS](https://webkit.org/blog/6784/new-video-policies-for-ios/). ::: * `controls` boolean * Shows the default video controls such as buttons for play/pause, volume controls. You may choose to build buttons and controls that work with the player. [See an example.](/stream/viewing-videos/using-own-player/) * `height` integer * The height of the video's display area, in CSS pixels. * `loop` boolean * A Boolean attribute; if included in the HTML tag, player will, automatically seek back to the start upon reaching the end of the video. * `muted` boolean * A Boolean attribute which indicates the default setting of the audio contained in the video. If set, the audio will be initially silenced. * `preload` string | null * This enumerated attribute is intended to provide a hint to the browser about what the author thinks will lead to the best user experience. You may choose to include this attribute as a boolean attribute without a value, or you may specify the value `preload="auto"` to preload the beginning of the video. Not including the attribute or using `preload="metadata"` will just load the metadata needed to start video playback when requested. :::note The `