What do Netflix use to create videos?

If you have ever wondered what tools, protocols and encryption Netflix use a quick peak into one of their mp4′s shows that they use a lot of the usual tools in their workflow. Note that this sample was derived from the Chrome browser and there are likely different variations for different devices.

For the first chunk / preview
1. The container is an mp4 for avc1
2. The video is packaged using GPAC aka MP4Box (GPAC0.5.1-DEV-rev4944M)
3. Video is H.264 and audio aac
4. H.264 is encoded using x264, settings
x264 – core 118 r234 d84818a – H.264/MPEG-4 AVC codec – Copyleft 2003-2011 – http://www.videolan.org/x264.html – options: cabac=1 ref=3 deblock=1:0:0 analyse=0×1:0×111 me=umh subme=7 psy=1 psy_rd=1.00:0.00 mixed_ref=1 me_range=16 chroma_me=1 trellis=2 8x8dct=0 cqm=0 deadzone=21,11 fast_pskip=0 chroma_qp_offset=-2 threads=12 sliced_threads=0 nr=0 decimate=1 interlaced=0 bluray_compat=0 constrained_intra=0 bframes=2 b_pyramid=0 b_adapt=2 b_bias=0 direct=3 weightb=1 open_gop=0 weightp=2 keyint=250 keyint_min=25 scenecut=40 intra_refresh=0 rc_lookahead=40 rc=2pass mbtree=1 bitrate=600 ratetol=1.0 qcomp=0.50 qpmin=6 qpmax=51 qpstep=4 cplxblur=20.0 qblur=0.5 ip_ratio=1.40 aq=1:1.00

For other chunks
1. mp4/dash
2. Netflix encryption – NetflixPstrm
3. Created with Netflix Media Library Version 80.0.158
4. AAC-LC audio
mp4a – MP4 Audio Description) at 8 (87 bytes)
| | | | | | | Channels: 2
| | | | | | | Sample Size: 16
| | | | | | | Sample Rate: 24000.0
| | | | | | | (esds – Extended Sample Description) at 28 (51 bytes)
| | | | | | | | Audio Type: 64
| | | | | | | | Buffer Size: 579
| | | | | | | | Bitrate: 65587
| | | | | | | | Max Bitrate: 107720
| | | | | | | | Audio Specifc Config:
| | | | | | | | Audio Object Type: 2 – AAC LC (Low Complexity)
| | | | | | | | Sampling Frequency: 24000
| | | | | | | | Channel Config: 2 channels – Left, Right
| | | | | | | | SBR Present: 0
5. A DASH index / SIDX box
6. AVC video
(avcC – AVC Configuration) at 78 (56 bytes)
| | | | | | | | Version: 1
| | | | | | | | Profile: 77 (Main)
| | | | | | | | Profile Compatibility: 64 (Main)
| | | | | | | | Level: 3.0
| | | | | | | | NAL Unit Length: 4
| | | | | | | | Sequence Set Count: 1
| | | | | | | | NAL unit type: 7
| | | | | | | | Profile: 77 (Main)
| | | | | | | | Level: 3.0
| | | | | | | | SPS ID: 0
| | | | | | | | Max Frame Num: 16
| | | | | | | | Max Ref Frames: 3
| | | | | | | | Chroma Format: 1 (4:2:0)
| | | | | | | | Bit Depth: 8 (luma), 8 (chroma)
| | | | | | | | Macroblock Width: 32 (512 pixels luma, 256 pixels chroma)
| | | | | | | | Map Unit Height: 24 (384 pixels luma, 192 pixels chroma)
| | | | | | | | VUI parameters present: 1
| | | | | | | | Sample Aspect Ratio: 4:3
| | | | | | | | Timing Info: num_units_in_tick=1001, time_scale=48000, fixed_frame_rate_flag=1
| | | | | | | | NAL/HRD present! (
7. Common Encryption for PlayReady, Widevine and a custom system not registered on the DASH list, assume this is Netflix specific encryption
| pssh – Protection System Specific Header at 372 (702 bytes)
| | System ID: 9A04F079-9840-4286-AB92-E65BE0885F95
| | Version: 0
| | Data Size: 670
| | Header: ��16AESCTRAAAAANQDUfcAAAAAAAAAAA==OgsMEY/8lsI=true
| pssh – Protection System Specific Header at 1074 (52 bytes)
| | System ID: EDEF8BA9-79D6-4ACE-A3C8-27DCD51D21ED
| | Version: 0
| | Data Size: 20
| | Header:��Q
| pssh – Protection System Specific Header at 1126 (76 bytes)
| | System ID: 29701FE4-3CC7-4A34-8C5B-AE90C7439A47
| | Version: 0
| | Data Size: 44
| | Header: ��Q�r����ԗ���P\i���
EH%M�<�^

Encryption is using an IV size of 8 which is best for cross platform compatibility
The KeyID looks quite short:

libx265 encode test, how low can you go for 720P

testHere is a really quick encoding test showing some of the outputs using libx265, by encoding test means it is certainly not an accurate representation of best quality comparison but what is does show is not too bad playback at 187kbps video (which is very low 720P!)

Some details of the source (some nature footage off youtube):

Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'Planet Earth - Amazing nature scenery (1080p HD)-6v2L2UGZJAM.mp4':
  Metadata:
    major_brand     : mp42
    minor_version   : 0
    compatible_brands: isommp42
    creation_time   : 2014-04-25 01:07:55
Duration: 00:13:28.45, start: 0.000000, bitrate: 2292 kb/s

Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 2097 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 59.94 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 192 kb/s (default)
    Metadata:
      creation_time   : 2014-04-25 01:09:08
      handler_name    : IsoMedia File Produced by Google, 5-11-2011

And the result:

Metadata:
    major_brand     : isom
    minor_version   : 512
    compatible_brands: isomiso2mp41
    encoder         : Lavf56.3.100
  Duration: 00:13:28.50, start: 0.046440, bitrate: 324 kb/s
    Stream #0:0(und): Video: hevc (Main) (hev1 / 0x31766568), yuv420p(tv), 1280x720 [SAR 1:1 DAR 16:9], 187 kb/s, 29.97 fps, 29.97 tbr, 30k tbn, 29.97 tbc (default)
    Metadata:
      handler_name    : VideoHandler
    Stream #0:1(und): Audio: aac (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)
    Metadata:
      handler_name    : SoundHandler

And to get the idea I have joined together the source and the result here which you can see a quick demo, the one on the left is the source and the right is the result. Now the one on the right is obviously not as good but you are comparing 2000kbps with <200kbps so it is not too bad.

 

 

 

Publishing SMPTE bars for testing with ffmpeg

Sometimes you just want a general source for testing a new encoding config and don’t want to have to specify a source. For this the smptebars filter is very useful.

Here is how to push it out as an example to Wowza:

ffmpeg -re -f lavfi -i smptebars  -s 640×360 -g 25 -c:v libx264 -b:v 500k -an -f flv rtmp://wowserver.com/live/myStream

or for HD

ffmpeg -re -f lavfi -i smptehdbars  -s 1920×1080 -g 25 -c:v libx264 -b:v 4500k -an -f flv rtmp://wowserver.com/live/myStream

Using a webcam on linux and pushing to Wowza

Note: This is still a work in progress

Setup:

  1. Standard USB webcam connected to Ubuntu 12
  2. Latest build of ffmpeg from the repo with libx264
  3. Wowza media server 3.6 hosted on Amazon EC2 (with Cloudfront CDN)

Check available devices with v42l

Get the current video format: v4l2-ctl -V

Format Video Capture:
Width/Height : 640/480
Pixel Format : ‘YUYV’
Field : None
Bytes per Line: 1280
Size Image : 614400
Colorspace : SRGB

v4l2-ctl –list-formats-ext

 

Start encoding

ffmpeg -re -f v4l2 -r 25 -s 640×480 -i /dev/video0 -b:v 300k -c:v libx264 -g 15 -pix_fmt yuv420p -an -f flv rtmp://wowzaserver/live/myStream

Useful script for splitting videos

Source: http://grapsus.net/blog/post/A-script-for-splitting-videos-using-ffmpeg

#!/bin/bash

# Written by Alexis Bezverkhyy <alexis@grapsus.net> in 2011
# This is free and unencumbered software released into the public domain.
# For more information, please refer to <http://unlicense.org/>

function usage {
        echo "Usage : ffsplit.sh input.file chunk-duration [output-filename-format]"
        echo -e "\t - input file may be any kind of file reconginzed by ffmpeg"
        echo -e "\t - chunk duration must be in seconds"
        echo -e "\t - output filename format must be printf-like, for example myvideo-part-%04d.avi"
        echo -e "\t - if no output filename format is given, it will be computed\
 automatically from input filename"
}

IN_FILE="$1"
OUT_FILE_FORMAT="$3"
typeset -i CHUNK_LEN
CHUNK_LEN="$2"

DURATION_HMS=$(ffmpeg -i "$IN_FILE" 2>&1 | grep Duration | cut -f 4 -d ' ')
DURATION_H=$(echo "$DURATION_HMS" | cut -d ':' -f 1)
DURATION_M=$(echo "$DURATION_HMS" | cut -d ':' -f 2)
DURATION_S=$(echo "$DURATION_HMS" | cut -d ':' -f 3 | cut -d '.' -f 1)
let "DURATION = ( DURATION_H * 60 + DURATION_M ) * 60 + DURATION_S"

if [ "$DURATION" = '0' ] ; then
        echo "Invalid input video"
        usage
        exit 1
fi

if [ "$CHUNK_LEN" = "0" ] ; then
        echo "Invalid chunk size"
        usage
        exit 2
fi

if [ -z "$OUT_FILE_FORMAT" ] ; then
        FILE_EXT=$(echo "$IN_FILE" | sed 's/^.*\.\([a-zA-Z0-9]\+\)$/\1/')
        FILE_NAME=$(echo "$IN_FILE" | sed 's/^\(.*\)\.[a-zA-Z0-9]\+$/\1/')
        OUT_FILE_FORMAT="${FILE_NAME}-%03d.${FILE_EXT}"
        echo "Using default output file format : $OUT_FILE_FORMAT"
fi

N='1'
OFFSET='0'
let 'N_FILES = DURATION / CHUNK_LEN + 1'

while [ "$OFFSET" -lt "$DURATION" ] ; do
        OUT_FILE=$(printf "$OUT_FILE_FORMAT" "$N")
        echo "writing $OUT_FILE ($N/$N_FILES)..."
        ffmpeg -i "$IN_FILE" -vcodec copy -acodec copy -ss "$OFFSET" -t "$CHUNK_LEN" "$OUT_FILE"
        let "N = N + 1"
        let "OFFSET = OFFSET + CHUNK_LEN"
done

Looping over an input with ffmpeg

This example shows a simple way to loop over an input file using the lavfi filter and also the -re option which is essential as it only encodes at realtime of the file instead of as fast as possible. This is a useful technique if you want to simulate a live stream for testing.

 

ffmpeg -f lavfi -re -i movie=golf_680_640x360.mp4:loop=0 -acodec libfaac -b:a 64k -pix_fmt yuv420p -vcodec libx264 -x264opts level=41 -r 25 -profile:v baseline -b:v 1500k -maxrate 2000k -force_key_frames 50 -s 640×360 -map 0 -flags -global_header -f segment -segment_list index_1500.m3u8 -segment_time 10 -segment_format mpeg_ts -segment_list_type m3u8 segment%05d.ts

 

 

Useful script for multi-rate HLS output from ffmpeg

#!/bin/bash
VIDSOURCE=”$1RESOLUTION=”854x480”
BITRATE1=”800000BITRATE2=”600000BITRATE3=”400000”
 
AUDIO_OPTS=”-c:a libfaac -b:a 160000 -ac 2VIDEO_OPTS1=”-s $RESOLUTION -c:v libx264 -b:v $BITRATE1 -vprofilebaseline -preset medium -x264opts level=41VIDEO_OPTS2=”-s $RESOLUTION -c:v libx264 -b:v $BITRATE2 -vprofile
baseline -preset medium -x264opts level=41VIDEO_OPTS3=”-s $RESOLUTION -c:v libx264 -b:v $BITRATE3 -vprofile
baseline -preset medium -x264opts level=41OUTPUT_HLS=”-hls_time 3 -hls_list_size 10 -hls_wrap 30 -start_number 1ffmpeg -i$VIDSOURCE-y -threads 4
              $AUDIO_OPTS $VIDEO_OPTS1 $OUTPUT_HLS stream_hi.m3u8
              $AUDIO_OPTS $VIDEO_OPTS2 $OUTPUT_HLS stream_med.m3u8
              $AUDIO_OPTS $VIDEO_OPTS3 $OUTPUT_HLS stream_low.m3u8

Credit to: jeisom@gmail.com