Adaptive Streaming
Overview
Adaptive Bitrate (ABR) streaming delivers video at the optimal quality for each viewer’s bandwidth and device. Instead of a single file, an ABR package contains multiple encoded variants at different resolutions and bitrates, wrapped in a manifest that players use to switch between quality levels in real time.
Transcodely supports two streaming protocols:
| Protocol | Manifest | Segment Format | Compatibility |
|---|---|---|---|
| HLS | .m3u8 | fMP4 (CMAF) or .ts | Apple devices, most browsers, OTT players |
| DASH | .mpd | fMP4 (CMAF) | Android, smart TVs, web players |
Creating an HLS Output
An HLS output packages multiple video variants into a master playlist with variant playlists for each quality level. Use the hls output type with an array of video variants:
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Create
-H "Content-Type: application/json"
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: {{ORG_ID}}"
-d '{
"input_origin_id": "ori_input12345",
"input_path": "uploads/source.mp4",
"output_origin_id": "ori_output6789",
"outputs": [
{
"type": "hls",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "standard" }
],
"segments": {
"duration": 6,
"gop_alignment": "aligned"
}
}
]
}'const job = await client.jobs.create({
inputOriginId: "ori_input12345",
inputPath: "uploads/source.mp4",
outputOriginId: "ori_output6789",
outputs: [
{
type: OutputFormat.HLS,
video: [
{ codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_1080P, quality: QualityTier.STANDARD },
{ codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_720P, quality: QualityTier.STANDARD },
{ codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_480P, quality: QualityTier.STANDARD },
],
segments: {
duration: 6,
gopAlignment: GOPAlignmentMode.GOP_ALIGNMENT_MODE_ALIGNED,
},
},
],
});job = client.jobs.create(
input_origin_id="ori_input12345",
input_path="uploads/source.mp4",
output_origin_id="ori_output6789",
outputs=[{
"type": "hls",
"video": [
{"codec": "h264", "resolution": "1080p", "quality": "standard"},
{"codec": "h264", "resolution": "720p", "quality": "standard"},
{"codec": "h264", "resolution": "480p", "quality": "standard"},
],
"segments": {
"duration": 6,
"gop_alignment": "aligned",
},
}],
)job, err := client.Jobs.Create(ctx, &transcodely.JobCreateParams{
InputOriginId: proto.String("ori_input12345"),
InputPath: proto.String("uploads/source.mp4"),
OutputOriginId: proto.String("ori_output6789"),
Outputs: []*transcodely.OutputSpec{{
Type: transcodely.OutputFormatHLS,
Video: []*transcodely.VideoVariant{
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution1080P, Quality: transcodely.QualityTierStandard},
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution720P, Quality: transcodely.QualityTierStandard},
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution480P, Quality: transcodely.QualityTierStandard},
},
Segments: &transcodely.SegmentConfig{
Duration: proto.Int32(6),
GopAlignment: transcodely.GOPAlignmentAligned,
},
}},
})This produces a directory structure like:
job_abc123def456/out_xyz789/hls/
master.m3u8 # Master playlist
h264_1080p.m3u8 # Variant playlist (1080p)
h264_720p.m3u8 # Variant playlist (720p)
h264_480p.m3u8 # Variant playlist (480p)
init_0.mp4 # Init segment (1080p)
init_1.mp4 # Init segment (720p)
init_2.mp4 # Init segment (480p)
segment_0_00001.m4s # Media segments...Creating a DASH Output
DASH mirrors the HLS setup with a few key differences: set the output type to dash, and Transcodely writes an .mpd manifest (rather than the HLS .m3u8 master and variant playlists) backed by fMP4/CMAF segments. This example also uses a shorter 4-second segment duration:
curl -X POST https://api.transcodely.com/transcodely.v1.JobService/Create
-H "Content-Type: application/json"
-H "Authorization: Bearer {{API_KEY}}"
-H "X-Organization-ID: {{ORG_ID}}"
-d '{
"input_origin_id": "ori_input12345",
"input_path": "uploads/source.mp4",
"output_origin_id": "ori_output6789",
"outputs": [
{
"type": "dash",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "standard" }
],
"segments": {
"duration": 4
}
}
]
}'const job = await client.jobs.create({
inputOriginId: "ori_input12345",
inputPath: "uploads/source.mp4",
outputOriginId: "ori_output6789",
outputs: [
{
type: OutputFormat.DASH,
video: [
{ codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_1080P, quality: QualityTier.STANDARD },
{ codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_720P, quality: QualityTier.STANDARD },
{ codec: VideoCodec.H264, resolution: Resolution.RESOLUTION_480P, quality: QualityTier.STANDARD },
],
segments: {
duration: 4,
},
},
],
});job = client.jobs.create(
input_origin_id="ori_input12345",
input_path="uploads/source.mp4",
output_origin_id="ori_output6789",
outputs=[{
"type": "dash",
"video": [
{"codec": "h264", "resolution": "1080p", "quality": "standard"},
{"codec": "h264", "resolution": "720p", "quality": "standard"},
{"codec": "h264", "resolution": "480p", "quality": "standard"},
],
"segments": {
"duration": 4,
},
}],
)job, err := client.Jobs.Create(ctx, &transcodely.JobCreateParams{
InputOriginId: proto.String("ori_input12345"),
InputPath: proto.String("uploads/source.mp4"),
OutputOriginId: proto.String("ori_output6789"),
Outputs: []*transcodely.OutputSpec{{
Type: transcodely.OutputFormatDASH,
Video: []*transcodely.VideoVariant{
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution1080P, Quality: transcodely.QualityTierStandard},
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution720P, Quality: transcodely.QualityTierStandard},
{Codec: transcodely.VideoCodecH264, Resolution: transcodely.Resolution480P, Quality: transcodely.QualityTierStandard},
},
Segments: &transcodely.SegmentConfig{
Duration: proto.Int32(4),
},
}},
})ABR Ladder Examples
An ABR ladder defines which resolution and bitrate combinations to include. Here are common ladder configurations:
Standard Ladder (Broad Compatibility)
Best for general web and mobile delivery. Three H.264 rungs step down from 1080p to 480p, plus a low-frame-rate 480p floor at 24 fps to hold up on the most constrained connections:
{
"type": "hls",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "economy" },
{ "codec": "h264", "resolution": "480p", "quality": "economy", "framerate": 24 }
]
}Premium Ladder (High Quality)
For OTT platforms and premium content. Five H.264 rungs span 4K (2160p) down to a 480p floor, with the top two at the premium quality tier for the sharpest picture on large screens:
{
"type": "hls",
"video": [
{ "codec": "h264", "resolution": "2160p", "quality": "premium" },
{ "codec": "h264", "resolution": "1080p", "quality": "premium" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "economy", "framerate": 24 }
]
}Multi-Codec Ladder
Serve modern codecs to capable devices while keeping an H.264 fallback. This ladder pairs two AV1 rungs (1080p, 720p) with three H.264 rungs (1080p, 720p, 480p economy):
{
"type": "hls",
"video": [
{ "codec": "av1", "resolution": "1080p", "quality": "standard" },
{ "codec": "av1", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" },
{ "codec": "h264", "resolution": "480p", "quality": "economy" }
]
}Players that support AV1 will select those variants for better quality at lower bitrates. Others will fall back to H.264.
Segment Configuration
Segment settings control how the video is split into chunks for streaming. These affect startup latency, seeking precision, and CDN efficiency.
| Parameter | Default | Range | Description |
|---|---|---|---|
duration | 6 | 1—30 | Segment duration in seconds |
gop_alignment | aligned | aligned, fixed | How keyframes align with segments |
gop_size | — | 1—10 | Fixed GOP size in seconds (only with fixed alignment) |
Segment Duration Trade-offs
| Duration | Startup Latency | CDN Efficiency | Seeking Precision |
|---|---|---|---|
| 2s | Fast | More requests | Precise |
| 4s | Good balance | Moderate | Good |
| 6s (default) | Moderate | Efficient | Moderate |
| 10s | Slower | Most efficient | Coarse |
GOP Alignment
Aligned mode (default and recommended) sets the GOP size equal to the segment duration. This ensures each segment starts with a keyframe, enabling clean switching between quality levels:
{
"segments": {
"duration": 6,
"gop_alignment": "aligned"
}
}Fixed mode uses a specific GOP size independent of segment duration. This is useful for ad insertion (DAI) workflows that require keyframes at specific intervals:
{
"segments": {
"duration": 6,
"gop_alignment": "fixed",
"gop_size": 2
}
}HLS Configuration
Customize HLS-specific behavior with the hls config object:
{
"type": "hls",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" }
],
"hls": {
"manifest": "index",
"segment_format": "fmp4",
"playlist_type": "vod",
"variant_pattern": "video_{resolution}"
},
"segments": {
"duration": 6
}
}| Parameter | Default | Description |
|---|---|---|
manifest | master | Master playlist filename (without .m3u8) |
segment_format | fmp4 | fmp4 (CMAF, recommended) or ts (legacy) |
playlist_type | vod | vod (complete playlist) or event (append-only) |
variant_pattern | {codec}_{resolution} | Pattern for variant playlist filenames |
Note: The
tssegment format only supports H.264. Usefmp4for H.265, VP9, and AV1 codecs.
DASH Configuration
Customize DASH manifest naming:
{
"type": "dash",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" }
],
"dash": {
"manifest": "stream"
}
}| Parameter | Default | Description |
|---|---|---|
manifest | manifest | MPD filename (without .mpd) |
DASH always uses fMP4 (CMAF) segments regardless of other settings.
Multi-Audio Tracks
For streaming outputs, you can include multiple audio language tracks:
{
"type": "hls",
"video": [
{ "codec": "h264", "resolution": "1080p", "quality": "standard" },
{ "codec": "h264", "resolution": "720p", "quality": "standard" }
],
"audio": [
{ "language": "eng", "label": "English", "source_track": 0, "is_default": true },
{ "language": "spa", "label": "Spanish", "source_track": 1 },
{ "language": "fra", "label": "French", "source_track": 2 }
]
}Audio tracks use ISO 639-2 three-letter language codes. The source_track field maps to audio track indices in the input file (0-based).
Per-Variant Pricing
Each variant in an ABR ladder has its own cost calculated from the variant’s codec, resolution, framerate, and quality tier. You can inspect per-variant pricing in the job response:
{
"outputs": [
{
"variant_pricing": [
{ "index": 0, "resolution": "1080p", "codec": "h264", "estimated_cost": 0.106 },
{ "index": 1, "resolution": "720p", "codec": "h264", "estimated_cost": 0.0795 },
{ "index": 2, "resolution": "480p", "codec": "h264", "estimated_cost": 0.04 }
]
}
]
}Use delayed jobs to review per-variant costs before encoding begins.
Recommended Configurations
| Use Case | Type | Codecs | Segment Duration |
|---|---|---|---|
| Mobile-first streaming | HLS | H.264 | 6s |
| Cross-platform OTT | HLS + DASH | H.264, H.265 | 4s |
| Premium VOD | HLS | H.264, AV1 | 6s |
| Low-latency preview | HLS | H.264 | 2s |
| Ad-supported content | HLS | H.264 | 6s (fixed GOP 2s) |