Update README markdown

Change-Id: I2a840de742d67672035ef9d13191beffa2e60fdf
This commit is contained in:
KongQun Yang 2014-06-27 12:07:43 -07:00 committed by Gerrit Code Review
parent 5a6d95b330
commit 6651ae1c3d
1 changed files with 196 additions and 144 deletions

340
README.md
View File

@ -1,69 +1,76 @@
Media packaging SDK is intended for C++ programmers writing DASH packager application with common encryption support. Media packaging SDK is intended for C++ programmers writing DASH packager application with common encryption support.
This document provides the information needed to create a DASH packager that is able to remux and encrypt a video into fragmented ISO BMFF format with CENC support. The DASH packaging API is also designed in such a way for easy extension to more source and destination formats. This document provides the information needed to create a DASH packager that is able to remux and encrypt a video into fragmented ISO BMFF format with common encryption (CENC) support. The DASH packaging API is also designed in such a way for easy extension to more source and destination formats.
# Setting up for development # # Setting up for development #
1. Packager source is managed by Git at https://www.github.com/google/edash-packager. We use gclient tool from Chromium to manage third_party libraries. You will need Git and Subversion (for third_party libraries) installed on your machine to access the source code. 1. Packager source is managed by Git at https://www.github.com/google/edash-packager. We use gclient tool from Chromium to manage third party libraries. You will need Git (v1.7.5 or above) and Subversion (for third party libraries) installed on your machine to access the source code.
2. Pull gclient and ninja from Chrome Depot Tools: 2. Install Chromium depot tools which contains gclient and ninja
```Shell
git clone https://chromium.googlesource.com/chromium/tools/depot_tools.git See http://www.chromium.org/developers/how-tos/install-depot-tools for details.
```
Add depot_tools to your PATH, for example, in linux:
```Shell
$ export PATH="$PATH":`pwd`/depot_tools
```
You may want to add this to your .bashrc file or your shell's equivalent so that you dont need to reset your $PATH manually each time you open a new shell.
3. Get the source 3. Get the source
```Shell
mkdir packager ```Shell
cd packager mkdir packager
gclient config https://www.github.com/google/edash-packager.git --name=src cd packager
gclient sync gclient config https://www.github.com/google/edash-packager.git --name=src
``` gclient sync
```
4. Build 4. Build
We use ninja to build our code:
``` We use ninja, which is much faster than make, to build our code:
ninja -C src/out/{Debug/Release} [Module] ```Shell
``` cd src
Module is optional. If not specified, build all, e.g. ninja -C out/{Debug,Release} [Module]
``` ```
ninja -C src/out/Debug # build all modules in Debug mode Module is optional. If not specified, build all, e.g.
ninja -C src/out/Release # build all modules in Release mode ```Shell
ninja -C src/out/Release mp4 # build mp4 module in Debug mode ninja -C out/Debug # build all modules in Debug mode
``` ninja -C out/Release # build all modules in Release mode
Refer to ninja manual for details.<br> ninja -C out/Release mp4 # build mp4 module in Debug mode
We also provide a mechanism to change build configurations, for example, developers can change build system to “make” by overriding GYP_GENERATORS using gyp_packager.py script, i.e. ```
```Shell Refer to ninja manual for details.
GYP_GENERATORS='make' src/gyp_packager.py
``` We also provide a mechanism to change build configurations, for example, developers can change build system to “make” by overriding *GYP_GENERATORS* using *gyp_packager.py* script, i.e.
Another example, developers can also enable clang by overriding GYP_DEFINE. ```Shell
```Shell GYP_GENERATORS='make' ./gyp_packager.py
GYP_DEFINES='clang=1' src/gyp_packager.py ```
``` Another example, developers can also enable clang by overriding *GYP_DEFINE*.
Take note that clang needs to be setup for the first time if it is not setup yet. ```Shell
```Shell GYP_DEFINES='clang=1' ./gyp_packager.py
src/tools/clang/scripts/update.sh ```
``` Take note that clang needs to be setup for the first time if it is not setup yet.
```Shell
tools/clang/scripts/update.sh
```
5. Updating the code
Update your current branch with *git pull* followed by *gclient sync*. Note that if you are not on a branch, *git pull* will not work, and you will need to use *git fetch* instead.
6. Contributing
See https://github.com/google/edash-packager/blob/master/CONTRIBUTING.md for details.
#Design overview# #Design overview#
Major modules are described below: Major modules are described below:
Demuxer is responsible for extracting elementary stream samples from a multimedia file, e.g. an ISO BMFF file. The demuxed streams can be fed into a muxer to generate multimedia files. For encrypted media, the user should implement the abstract DecryptorSource interface, which is used by Demuxer to decrypt the streams. The concrete DecryptorSource implementation wraps the fetching and management of decryption keys. Demuxer is responsible for extracting elementary stream samples from a multimedia file, e.g. an ISO BMFF file. The demuxed streams can be fed into a muxer to generate multimedia files. For encrypted media, the users should implement the abstract DecryptorSource interface, which is used by Demuxer to decrypt the streams. The concrete DecryptorSource implementation wraps the fetching and management of decryption keys.
Demuxer reads from source through the File interface. A concrete LocalFile class is already implemented. The user may also implements his own File class if they want to read/write using a different kinds of protocol, e.g. network storage, http etc. Demuxer reads from source through the File interface. A concrete LocalFile class is already implemented. The users may also implement their own File class if they want to read/write using a different kinds of protocol, e.g. network storage, http etc.
Muxer is responsible for taking elementary stream samples and producing media segments. An optional EncryptorSource can be provided to Muxer to generate encrypted outputs. Muxer writes to output using the same File interface as Demuxer. Muxer is responsible for taking elementary stream samples and producing media segments. An optional EncryptionKeySource can be provided to Muxer to generate encrypted outputs. Muxer writes to output using the same File interface as Demuxer.
Demuxer and Muxer are connected using MediaStream. MediaStream wraps the elementary streams and is responsible for the interaction between Demuxer and Muxer. A demuxer can transmits multiple MediaStreams; similarly, A muxer is able to accept and mux multiple MediaStreams, not necessarily from the same Demuxer. Demuxer and Muxer are connected using MediaStream. MediaStream wraps the elementary streams and is responsible for the interaction between Demuxer and Muxer. A demuxer can transmits multiple MediaStreams; similarly, A muxer is able to accept and mux multiple MediaStreams, not necessarily from the same Demuxer.
MPDBuilder is responsible for the creation of Media Presentation Description as specified in ISO/IEC 23009-1 DASH MPD spec. MpdBuilder is responsible for the creation of Media Presentation Description as specified in ISO/IEC 23009-1 DASH MPD spec.
The only source format supported for now is ISO BMFF; the only output format supported right now is fragmented ISO BMFF with CENC. Support for more formats will be added soon. Supported source formats: ISO BMFF (both fragmented and non-fragmented), IPTV; the only output format supported right now is fragmented ISO BMFF with CENC. Support for more formats will be added soon.
##Creating DecryptorSource## ##Creating DecryptorSource##
@ -72,22 +79,48 @@ Not implemented yet. Not needed for now.
##Creating Demuxer## ##Creating Demuxer##
```C++ ```C++
// Create a demuxer from |input_media_file|. At this moment we support // Create a demuxer from |input_media_file|.
// MP4 (ISO BMFF) only. // |input_media_file| could be any supported files, e.g. if the users have
// |input_media_file| could be any supported files, e.g. if the user has // implemented a network file interface with prefix “network”,
// implemented a network file interface with prefix “network”, // |input_media_file| with value “network://xxxx” would open a network
// |input_media_file| with value “network://xxxx” would open a network // file automatically.
// file automatically. // |decryptor_source| should be NULL if the input media is not encrypted.
// |decryptor_source| should be NULL if the input media is not encrypted. Demuxer demuxer(input_media_file, decryptor_source);
Demuxer demuxer(input_media_file, decryptor_source);
``` ```
##Creating MpdBuilder## ##Creating MpdBuilder##
```C++ ```C++
MpdBuilder mpd_builder(MpdBuiler::kStatic); // kStatic profile for VOD. // |mpd_type| indicates whether the mpd should be for VOD or live profile.
// |mpd_options| contains a set of configurable options. See below for details.
MpdBuilder mpd_builder(mpd_type, mpd_options);
```
Mpd Options
```C++
// Specifies, in seconds, a common duration used in the definition of the MPD
// Representation data rate.
mpd_options.min_buffer_time = 5.0;
// The below options are for live profile only.
// Offset with respect to the wall clock time for MPD availabilityStartTime
// and availabilityEndTime values, in seconds.
mpd_options.availability_time_offset = 10.0;
// Indicates to the player how often to refresh the media presentations, in seconds.
mpd_options.minimum_update_period = 5.0;
// Guranteed duration of the time shifting buffer, in seconds.
mpd_options.time_shift_buffer_depth = 1800.0;
// Specifies a delay, in seconds, to be added to the media presentation time.
mpd_options.suggested_presentation_delay = 0.0;
```
Using MpdBuilder Instance Using MpdBuilder Instance
mpd_builder.AddBaseUrl(“http://foo.com/bar”); // Add a <BaseURL> element. ```C++
mpd_builder.AddBaseUrl("http://foo.com/bar"); // Add a <BaseURL> element.
AdaptationSet* video_adaptation_set = mpd_builder.AddAdaptationSet(); AdaptationSet* video_adaptation_set = mpd_builder.AddAdaptationSet();
// Create MediaInfo object. // Create MediaInfo object.
@ -98,115 +131,117 @@ assert(representation); // Make sure it succeeded.
// operations are required to generate a valid MPD. // operations are required to generate a valid MPD.
std::cout << mpd_builder.ToString() << std::endl; // Print the MPD to stdout. std::cout << mpd_builder.ToString() << std::endl; // Print the MPD to stdout.
```
MpdWriter: MpdBuilder Wrapper class MpdWriter: MpdBuilder Wrapper class
```C++
// Get file names with MediaInfo protobuf, i.e. media_info_files (array). // Get file names with MediaInfo protobuf, i.e. media_info_files (array).
MpdWrite mpd_writer; MpdWrite mpd_writer;
for (size_t i = 0; i < n; ++i) for (size_t i = 0; i < n; ++i)
mpd_writer.AddFile(media_info_files[i]); mpd_writer.AddFile(media_info_files[i]);
mpd_writer.WriteMpdToFile(“output_file_name.mpd”); mpd_writer.WriteMpdToFile("output_file_name.mpd");
``` ```
##Creating Muxer## ##Creating Muxer##
```C++ ```C++
// See below for muxer options. // See below for muxer options.
MuxerOptions options; MuxerOptions muxer_options;
// Create a MP4Muxer with options specified by |options|. // Create a MP4Muxer with options specified by |options|.
mp4::MP4Muxer muxer(options); mp4::MP4Muxer muxer(muxer_options);
``` ```
###Muxer Options:### Muxer Options
```C++ ```C++
// Generate a single segment for each media presentation. This option // Generate a single segment for each media presentation. This option
// should be set for on demand profile. // should be set for on demand profile.
options.single_segment = true; muxer_options.single_segment = true;
// Segment duration in seconds. If single_segment is specified, this parameter // Segment duration in seconds. If single_segment is specified, this parameter
// sets the duration of a subsegment; otherwise, this parameter sets the // sets the duration of a subsegment; otherwise, this parameter sets the
// duration of a segment. A segment can contain one to many fragments. // duration of a segment. A segment can contain one to many fragments.
options.segment_duration = 10.0; muxer_options.segment_duration = 10.0;
// Fragment duration in seconds. Should not be larger than the segment // Fragment duration in seconds. Should not be larger than the segment
// duration. // duration.
options.fragment_duration = 2.0; muxer_options.fragment_duration = 2.0;
// Force segments to begin with stream access points. Segment duration may // Force segments to begin with stream access points. Segment duration may
// not be exactly what asked by segment_duration. // not be exactly what asked by segment_duration.
options.segment_sap_aligned = true; muxer_options.segment_sap_aligned = true;
// Force fragments to begin with stream access points. Fragment duration // Force fragments to begin with stream access points. Fragment duration
// may not be exactly what asked by segment_duration. Imply // may not be exactly what asked by segment_duration. Imply
// segment_sap_aligned. // segment_sap_aligned.
options.fragment_sap_aligned = true; muxer_options.fragment_sap_aligned = true;
// Set to true to normalize the presentation timestamps to start from zero. // Set to true to normalize the presentation timestamps to start from zero.
options.normalize_presentation_timestamp = true; muxer_options.normalize_presentation_timestamp = true;
// For ISO BMFF only. // For ISO BMFF only.
// Set the number of subsegments in each SIDX box. If 0, a single SIDX box // Set the number of subsegments in each SIDX box. If 0, a single SIDX box
// is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer // is used per segment. If -1, no SIDX box is used. Otherwise, the Muxer
// will pack N subsegments in the root SIDX of the segment, with // will pack N subsegments in the root SIDX of the segment, with
// segment_duration/N/fragment_duration fragments per subsegment. // segment_duration/N/fragment_duration fragments per subsegment.
options.num_subsegments_per_sidx = 1; muxer_options.num_subsegments_per_sidx = 1;
// Output file name. If segment_template is not specified, the Muxer // Output file name. If segment_template is not specified, the Muxer
// generates this single output file with all segments concatenated; // generates this single output file with all segments concatenated;
// Otherwise, it specifies the init segment name. // Otherwise, it specifies the init segment name.
options.output_file_name = …; muxer_options.output_file_name = …;
// Specify output segment name pattern for generated segments. It can // Specify output segment name pattern for generated segments. It can
// furthermore be configured by using a subset of the SegmentTemplate // furthermore be configured by using a subset of the SegmentTemplate
// identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time. // identifiers: $RepresentationID$, $Number$, $Bandwidth$ and $Time$.
// Optional. // Optional.
options.segment_template = …; muxer_options.segment_template = …;
// Specify the temporary file for on demand media file creation. // Specify the temporary directory for intermediate files.
options.temp_file_name = …; muxer_options.temp_dir = …;
// User specified bit rate for the media stream. If zero, the muxer will
// attempt to estimate.
muxer_options.bandwidth = 0;
``` ```
##Creating EncryptorSource## ##Creating EncryptionKeySource##
```C++ ```C++
// An EncryptorSource is optional. The stream wont be encrypted if an // An EncryptionKeySource is optional. The stream wont be encrypted if an
// EncryptorSource is not provided. // EncryptionKeySource is not provided.
// There are several different EncryptorSource implementations.
``` ```
###FixedEncryptorSource### ###WidevineEncryptionKeySource###
```C++ ```C++
// Users can use FixedEncryptorSource if they have encryption keys already. // Users may use WidevineEncryptionKeySource to fetch keys from Widevine
scoped_ptr<EncryptorSource> encryptor_source(
new FixedEncryptorSource(key_id, key, pssh));
```
###WidevineEncryptorSource###
```C++
// Users may also use WidevineEncryptorSource to fetch keys from Widevine
// common encryption server. // common encryption server.
// A request signer is required to sign the common encryption request. // A request signer is required to sign the common encryption request.
scoped_ptr<RequestSigner> signer( scoped_ptr<RequestSigner> signer(
RsaRequestSigner::CreateSigner(signer, pkcs1_rsa_private_key)); RsaRequestSigner::CreateSigner(signer, pkcs1_rsa_private_key));
if (!signer) { … } if (!signer) { … }
scoped_ptr<EncryptorSource> encryptor_source(new WidevineEncryptorSource( scoped_ptr<WidevineEncryptionKeySource> widevine_encryption_key_source(
server_url, content_id, track_type, signer.Pass())); new WidevineEncryptionKeySource(
``` key_server_url, content_id, track_type, policy, signer.Pass()));
After creating the encryptor source, // Intialize widevine encryption key source.
status = widevine_encryption_key_source->Initialize();
```C++
// Intialize encryptor source.
status = encryptor_source->Initialize();
if (!status.ok()) { … } if (!status.ok()) { … }
// Set encryptor source to muxer. // Set encryption key source to muxer.
muxer->SetEncryptorSource(encryptor_source.get(), clear_lead); // |max_sd_pixels| defines the threshold to determine whether a video track
// should be considered as SD or HD and accordingly, whether SD key or HD key
// should be used to encrypt the stream.
// |clear_lead| specifies clear lead duration in seconds.
// |crypto_period_duration| if not zero, enable key rotation with specified
// crypto period.
muxer->SetEncryptionKeySource(
widevine_encryption_key_source.get(), max_sd_pixels,
clear_lead, crypto_period_duration);
``` ```
##Connecting Demuxer and Muxer## ##Connecting Demuxer and Muxer##
@ -238,58 +273,75 @@ Doxygen API documentation coming soon.
#Driver Program Sample Usage# #Driver Program Sample Usage#
Sample driver programs packager_main and mpd_generator are written using the SDK. Some sample usages: Sample driver programs **packager** and **mpd_generator** are written using the SDK.
Some sample usages:
Run the program without arguments will display the help page with the list of command line arguments: Run the program without arguments will display the help page with the list of command line arguments:
```Shell ```Shell
packager_main packager
``` ```
Dump stream info: Dump stream info:
```Shell ```Shell
packager_main sintel.mp4 --dump_stream_info packager input=sintel.mp4 --dump_stream_info
``` ```
Demux audio from the input and generate a fragmented mp4: Demux audio from the input and generate a fragmented mp4:
```Shell ```Shell
packager_main sintel.mp4 --stream audio --output "fragmented_sintel.mp4" packager input=sintel.mp4,stream=audio,output=fragmented_sintel.mp4
``` ```
Demux the first stream from the input and generate a fragmented mp4: Demux streams from the input and generates a mpd with on-demand profile along with fragmented mp4:
```Shell ```Shell
packager_main sintel.mp4 --stream 0 --output "fragmented_sintel.mp4" packager \
input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \
input=sintel.mp4,stream=video,output=sintel_video.mp4 \
--profile on-demand \
--mpd_output sintel_vod.mpd
``` ```
Demux video from the input and generate an encrypted fragmented mp4 using widevine encryption with RSA signing key file "widevine_test_private.der": You may also generate mpd with live profile. Here is an example with IPTV input streams:
```Shell ```Shell
packager_main sintel.mp4 \ packager \
--stream video \ 'input=udp://224.1.1.5:5003,stream=audio,init_segment=live-audio.mp4,segment_template=live-audio-$Number$.mp4,bandwidth=130000' \
--output "encrypted_sintel.mp4" \ 'input=udp://224.1.1.5:5003,stream=video,init_segment=live-video-sd.mp4,segment_template=live-video-sd-$Number$.mp4,bandwidth=2000000' \
'input=udp://224.1.1.5:5002,stream=video,init_segment=live-video-hd.mp4,segment_template=live-video-hd-$Number$.mp4,bandwidth=5000000' \
--profile live \
--mpd_output live.mpd
```
Demux video from the input and generate an encrypted fragmented mp4 using widevine encryption with RSA signing key file *widevine_test_private.der*:
```Shell
packager input=sintel.mp4,stream=video,output=encrypted_sintel.mp4 \
--enable_widevine_encryption \ --enable_widevine_encryption \
--server_url "license.uat.widevine.com/cenc/getcontentkey/widevine_test" \ --key_server_url "https://license.uat.widevine.com/cenc/getcontentkey/widevine_test" \
--content_id "content_sintel" \ --content_id "content_sintel" \
--signer "widevine_test" \ --signer "widevine_test" \
--rsa_signing_key_path "widevine_test_private.der" --rsa_signing_key_path "widevine_test_private.der"
``` ```
The program also supports AES signing. The program also supports AES signing. Here is an example with encryption key rotates every 1800 seconds:
```Shell ```Shell
packager_main sintel.mp4 \ packager input=sintel.mp4,stream=video,output=encrypted_sintel.mp4 \
--stream video \
--output "encrypted_sintel.mp4" \
--enable_widevine_encryption \ --enable_widevine_encryption \
--server_url "license.uat.widevine.com/cenc/getcontentkey/widevine_test" \ --key_server_url "https://license.uat.widevine.com/cenc/getcontentkey/widevine_test" \
--content_id "content_sintel" \ --content_id "content_sintel" \
--signer "widevine_test" \ --signer "widevine_test" \
--aes_signing_key "1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9" \ --aes_signing_key "1ae8ccd0e7985cc0b6203a55855a1034afc252980e970ca90e5202689f947ab9" \
--aes_signing_iv "d58ce954203b7c9a9a9d467f59839249" --aes_signing_iv "d58ce954203b7c9a9a9d467f59839249" \
--crypto_period_duration 1800
``` ```
By default, packager_main will output MediaInfo files. “.media_info” file extension is appended to the output media files. The MediaInfo files can be passed to mpd_generator to generate an MPD. mpd_generator uses MpdWriter. The program can be told to generate MediaInfo files, which can be fed to **mpd_generate** to generate the mpd file.
```Shell ```Shell
packager \
input=sintel.mp4,stream=video,output=sintel_video.mp4 \
input=sintel.mp4,stream=audio,output=sintel_audio.mp4 \
--output_media_info
mpd_generator \ mpd_generator \
--base_urls "http://foo.com/bar1,http://foo.com/bar2" \ --input "sintel_video.mp4.media_info,sintel_audio.mp4.media_info" \
--input "encrypted_sintel.mp4.media_info" \ --output "sintel.mpd"
--output "example.mpd"
``` ```