Add version information in generated outputs

For mp4 outputs, a metadata box with version information is added to
moov box.

For mpd outputs, a text comment with version information is added in
the beginning of mpd file.

Issue #60

Change-Id: I783ba370781c0a8f77c910ff1172bad2e7edff75
This commit is contained in:
KongQun Yang 2015-12-21 15:10:17 -08:00
parent 9466582d9a
commit e0040a4910
51 changed files with 681 additions and 268 deletions

View File

@ -1,35 +1,34 @@
#!/usr/bin/env python #!/usr/bin/python
# #
# Copyright 2014 Google Inc. All rights reserved. # Copyright 2014 Google Inc. All rights reserved.
# #
# Use of this source code is governed by a BSD-style # Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at # license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd # https://developers.google.com/open-source/licenses/bsd
# """This script wraps gyp and sets up build environments.
# This script is a wrapper for Packager that adds some support for how GYP
# is invoked by Packager. Build instructions:
#
# Build instructions: 1. Setup gyp: ./gyp_packager.py or use gclient runhooks
#
# 1. Setup gyp: ./gyp_packager.py clang is enabled by default, which can be disabled by overriding
# GYP_DEFINE environment variable, i.e.
# clang is not enabled by default, which can be enabled by overriding "GYP_DEFINES='clang=0' gclient runhooks".
# GYP_DEFINE environment variable, i.e.
# "GYP_DEFINES='clang=1' ./gyp_packager.py". Ninja is the default build system. User can also change to make by
# overriding GYP_GENERATORS to make, i.e.
# Ninja is the default build system. User can also change to make by "GYP_GENERATORS='make' gclient runhooks".
# overriding GYP_GENERATORS to make, i.e.
# "GYP_GENERATORS='make' ./gyp_packager.py". 2. The first step generates the make files but does not start the
# build process. Ninja is the default build system. Refer to Ninja
# 2. The first step generates the make files but does not start the manual on how to do the build.
# build process. Ninja is the default build system. Refer to Ninja
# manual on how to do the build. Common syntaxes: ninja -C out/{Debug/Release} [Module]
# Module is optional. If not specified, build everything.
# Common syntaxes: ninja -C out/{Debug/Release} [Module]
# Module is optional. If not specified, build everything. Step 1 is only required if there is any gyp file change. Otherwise, you
# may just run ninja.
# Step 1 is only required if there is any gyp file change. Otherwise, you """
# may just run ninja.
import os import os
import sys import sys
@ -37,11 +36,14 @@ import sys
checkout_dir = os.path.dirname(os.path.realpath(__file__)) checkout_dir = os.path.dirname(os.path.realpath(__file__))
src_dir = os.path.join(checkout_dir, 'packager') src_dir = os.path.join(checkout_dir, 'packager')
# Workaround the dynamic path.
# pylint: disable=g-import-not-at-top,g-bad-import-order
sys.path.insert(0, os.path.join(src_dir, 'build')) sys.path.insert(0, os.path.join(src_dir, 'build'))
import gyp_helper # Workaround the dynamic path. pylint: disable-msg=F0401 import gyp_helper
sys.path.insert(0, os.path.join(src_dir, 'tools', 'gyp', 'pylib')) sys.path.insert(0, os.path.join(src_dir, 'tools', 'gyp', 'pylib'))
import gyp # Workaround the dynamic path. pylint: disable-msg=F0401 import gyp
if __name__ == '__main__': if __name__ == '__main__':
args = sys.argv[1:] args = sys.argv[1:]
@ -58,12 +60,15 @@ if __name__ == '__main__':
args.extend(['-I' + os.path.join(src_dir, 'build', 'common.gypi')]) args.extend(['-I' + os.path.join(src_dir, 'build', 'common.gypi')])
# Set these default GYP_DEFINES if user does not set the value explicitly. # Set these default GYP_DEFINES if user does not set the value explicitly.
_DEFAULT_DEFINES = {'test_isolation_mode': 'noop', 'use_glib': 0, _DEFAULT_DEFINES = {'test_isolation_mode': 'noop',
'use_openssl': 1, 'use_x11': 0, 'use_glib': 0,
'linux_use_gold_binary': 0, 'linux_use_gold_flags': 0} 'use_openssl': 1,
'use_x11': 0,
'linux_use_gold_binary': 0,
'linux_use_gold_flags': 0}
gyp_defines = (os.environ['GYP_DEFINES'] if os.environ.get('GYP_DEFINES') gyp_defines = (os.environ['GYP_DEFINES'] if os.environ.get('GYP_DEFINES') else
else '') '')
for key in _DEFAULT_DEFINES: for key in _DEFAULT_DEFINES:
if key not in gyp_defines: if key not in gyp_defines:
gyp_defines += ' {0}={1}'.format(key, _DEFAULT_DEFINES[key]) gyp_defines += ' {0}={1}'.format(key, _DEFAULT_DEFINES[key])

View File

@ -11,6 +11,7 @@
#include "packager/base/strings/string_split.h" #include "packager/base/strings/string_split.h"
#include "packager/base/strings/stringprintf.h" #include "packager/base/strings/stringprintf.h"
#include "packager/mpd/util/mpd_writer.h" #include "packager/mpd/util/mpd_writer.h"
#include "packager/version/version.h"
namespace edash_packager { namespace edash_packager {
namespace { namespace {
@ -88,7 +89,9 @@ int MpdMain(int argc, char** argv) {
ExitStatus status = CheckRequiredFlags(); ExitStatus status = CheckRequiredFlags();
if (status != kSuccess) { if (status != kSuccess) {
google::ShowUsageWithFlags(argv[0]); std::string version_string =
base::StringPrintf("mpd_generator version %s", kPackagerVersion);
google::ShowUsageWithFlags(version_string.c_str());
return status; return status;
} }

View File

@ -36,6 +36,7 @@
#include "packager/mpd/base/media_info.pb.h" #include "packager/mpd/base/media_info.pb.h"
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
#include "packager/mpd/base/simple_mpd_notifier.h" #include "packager/mpd/base/simple_mpd_notifier.h"
#include "packager/version/version.h"
DEFINE_bool(use_fake_clock_for_muxer, DEFINE_bool(use_fake_clock_for_muxer,
false, false,
@ -45,37 +46,36 @@ DEFINE_bool(use_fake_clock_for_muxer,
namespace { namespace {
const char kUsage[] = const char kUsage[] =
"Packager driver program. Sample Usage:\n" "Packager driver program. Usage:\n\n"
"%s [flags] <stream_descriptor> ...\n" "%s [flags] <stream_descriptor> ...\n"
"stream_descriptor consists of comma separated field_name/value pairs:\n" "stream_descriptor consists of comma separated field_name/value pairs:\n"
"field_name=value,[field_name=value,]...\n" "field_name=value,[field_name=value,]...\n"
"Supported field names are as follows:\n" "Supported field names are as follows:\n"
" - input (in): Required input/source media file path or network stream " " - input (in): Required input/source media file path or network stream\n"
" URL.\n" " URL.\n"
" - stream_selector (stream): Required field with value 'audio', 'video', " " - stream_selector (stream): Required field with value 'audio',\n"
"or stream number (zero based).\n" " 'video', or stream number (zero based).\n"
" - output (out): Required output file (single file) or initialization " " - output (out): Required output file (single file) or initialization\n"
" file path (multiple file).\n" " file path (multiple file).\n"
" - segment_template (segment): Optional value which specifies the " " - segment_template (segment): Optional value which specifies the\n"
"naming pattern for the segment files, and that the stream should be " " naming pattern for the segment files, and that the stream should be\n"
"split into multiple files. Its presence should be consistent across " " split into multiple files. Its presence should be consistent across\n"
" streams.\n" " streams.\n"
" - bandwidth (bw): Optional value which contains a user-specified " " - bandwidth (bw): Optional value which contains a user-specified\n"
"content bit rate for the stream, in bits/sec. If specified, this value is " " content bit rate for the stream, in bits/sec. If specified, this\n"
"propagated to the $Bandwidth$ template parameter for segment names. " " value is propagated to the $Bandwidth$ template parameter for\n"
"If not specified, its value may be estimated.\n" " segment names. If not specified, its value may be estimated.\n"
" - language (lang): Optional value which contains a user-specified " " - language (lang): Optional value which contains a user-specified\n"
"language tag. If specified, this value overrides any language metadata " " language tag. If specified, this value overrides any language\n"
"in the input track.\n" " metadata in the input track.\n"
" - output_format (format): Optional value which specifies the format " " - output_format (format): Optional value which specifies the format\n"
"of the output files (MP4 or WebM). If not specified, it will be " " of the output files (MP4 or WebM). If not specified, it will be\n"
" derived from the file extension of the output file.\n"; " derived from the file extension of the output file.\n";
const char kMediaInfoSuffix[] = ".media_info"; const char kMediaInfoSuffix[] = ".media_info";
enum ExitStatus { enum ExitStatus {
kSuccess = 0, kSuccess = 0,
kNoArgument,
kArgumentValidationFailed, kArgumentValidationFailed,
kPackagingFailed, kPackagingFailed,
kInternalError, kInternalError,
@ -441,8 +441,10 @@ int PackagerMain(int argc, char** argv) {
google::SetUsageMessage(base::StringPrintf(kUsage, argv[0])); google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
google::ParseCommandLineFlags(&argc, &argv, true); google::ParseCommandLineFlags(&argc, &argv, true);
if (argc < 2) { if (argc < 2) {
google::ShowUsageWithFlags(argv[0]); std::string version_string =
return kNoArgument; base::StringPrintf("edash-packager version %s", kPackagerVersion);
google::ShowUsageWithFlags(version_string.c_str());
return kSuccess;
} }
if (!ValidateWidevineCryptoFlags() || !ValidateFixedCryptoFlags()) if (!ValidateWidevineCryptoFlags() || !ValidateFixedCryptoFlags())

View File

@ -25,6 +25,16 @@
#include "packager/mpd/base/mpd_builder.h" #include "packager/mpd/base/mpd_builder.h"
DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info."); DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info.");
DEFINE_bool(override_version_string,
false,
"Override packager version string in the generated outputs with "
"--test_version_string if it is set to true. Should be used for "
"testing only.");
DEFINE_string(test_version_string,
"",
"Packager version string for testing. Ignored if "
"--override_version_string is false. Should be used for testing "
"only.");
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -146,6 +156,8 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned; muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned;
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx; muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
muxer_options->temp_dir = FLAGS_temp_dir; muxer_options->temp_dir = FLAGS_temp_dir;
if (FLAGS_override_version_string)
muxer_options->packager_version_string = FLAGS_test_version_string;
return true; return true;
} }
@ -158,6 +170,8 @@ bool GetMpdOptions(MpdOptions* mpd_options) {
mpd_options->time_shift_buffer_depth = FLAGS_time_shift_buffer_depth; mpd_options->time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
mpd_options->suggested_presentation_delay = mpd_options->suggested_presentation_delay =
FLAGS_suggested_presentation_delay; FLAGS_suggested_presentation_delay;
if (FLAGS_override_version_string)
mpd_options->packager_version_string = FLAGS_test_version_string;
return true; return true;
} }

View File

@ -31,6 +31,13 @@ class PackagerApp(object):
cmd = [self.binary, input_str, '--dump_stream_info'] cmd = [self.binary, input_str, '--dump_stream_info']
return subprocess.check_output(cmd) return subprocess.check_output(cmd)
def Version(self):
output = subprocess.check_output([self.binary])
# The output should of the form:
# edash-packager version xxx: Description...
# We consider everything before ':' part of version.
return output.split(':')[0]
def Package(self, streams, flags=None): def Package(self, streams, flags=None):
if flags is None: if flags is None:
flags = [] flags = []

View File

@ -38,6 +38,11 @@ class PackagerAppTest(unittest.TestCase):
def testBuildingCode(self): def testBuildingCode(self):
self.assertEqual(0, self.packager.BuildSrc()) self.assertEqual(0, self.packager.BuildSrc())
def testVersion(self):
self.assertRegexpMatches(
self.packager.Version(), '^edash-packager version '
'((?P<tag>[\w\.]+)-)?(?P<hash>[a-f\d]+)-(debug|release)$')
def testDumpStreamInfo(self): def testDumpStreamInfo(self):
test_file = os.path.join(self.test_data_dir, 'bear-640x360.mp4') test_file = os.path.join(self.test_data_dir, 'bear-640x360.mp4')
stream_info = self.packager.DumpStreamInfo(test_file) stream_info = self.packager.DumpStreamInfo(test_file)
@ -346,6 +351,10 @@ class PackagerAppTest(unittest.TestCase):
# Use fake clock, so output can be compared. # Use fake clock, so output can be compared.
if use_fake_clock: if use_fake_clock:
flags.append('--use_fake_clock_for_muxer') flags.append('--use_fake_clock_for_muxer')
# Override packager version string for testing.
flags += ['--override_version_string', '--test_version_string',
'<tag>-<hash>-<test>']
return flags return flags
def _CompareWithGold(self, test_output, golden_file_name): def _CompareWithGold(self, test_output, golden_file_name):

View File

@ -1,4 +1,4 @@
bandwidth: 128728 bandwidth: 129127
audio_info { audio_info {
codec: "mp4a.40.2" codec: "mp4a.40.2"
sampling_frequency: 44100 sampling_frequency: 44100
@ -8,11 +8,11 @@ audio_info {
} }
init_range { init_range {
begin: 0 begin: 0
end: 816 end: 954
} }
index_range { index_range {
begin: 817 begin: 955
end: 884 end: 1022
} }
media_file_name: "place_holder" media_file_name: "place_holder"
media_duration_seconds: 2.7631745 media_duration_seconds: 2.7631745

View File

@ -1,28 +1,29 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period> <Period>
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="885151" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001"> <Representation id="0" bandwidth="885555" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/> <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>
<BaseURL>output_video.mp4</BaseURL> <BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="941-1008" timescale="30000"> <SegmentBase indexRange="1079-1146" timescale="30000">
<Initialization range="0-940"/> <Initialization range="0-1078"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true"> <AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="128728" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/> <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>
<BaseURL>output_audio.mp4</BaseURL> <BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="817-884" timescale="44100"> <SegmentBase indexRange="955-1022" timescale="44100">
<Initialization range="0-816"/> <Initialization range="0-954"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period> <Period>
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
@ -6,10 +7,10 @@
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>
<Representation id="0" bandwidth="885151" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001"> <Representation id="0" bandwidth="885555" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
<BaseURL>output_video.mp4</BaseURL> <BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="941-1008" timescale="30000"> <SegmentBase indexRange="1079-1146" timescale="30000">
<Initialization range="0-940"/> <Initialization range="0-1078"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
@ -18,11 +19,11 @@
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>
<Representation id="1" bandwidth="128728" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <Representation id="1" bandwidth="129127" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>output_audio.mp4</BaseURL> <BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="817-884" timescale="44100"> <SegmentBase indexRange="955-1022" timescale="44100">
<Initialization range="0-816"/> <Initialization range="0-954"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -1,20 +1,21 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period> <Period>
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="881637" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001"> <Representation id="0" bandwidth="882040" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
<BaseURL>output_video.mp4</BaseURL> <BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="677-744" timescale="30000"> <SegmentBase indexRange="815-882" timescale="30000">
<Initialization range="0-676"/> <Initialization range="0-814"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
<AdaptationSet id="1" contentType="audio" subsegmentAlignment="true"> <AdaptationSet id="1" contentType="audio" subsegmentAlignment="true">
<Representation id="1" bandwidth="126087" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <Representation id="1" bandwidth="126487" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>output_audio.mp4</BaseURL> <BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="611-678" timescale="44100"> <SegmentBase indexRange="749-816" timescale="44100">
<Initialization range="0-610"/> <Initialization range="0-748"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
<Period start="PT0S"> <Period start="PT0S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
<Period start="PT0S"> <Period start="PT0S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
<Period start="PT0S"> <Period start="PT0S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
<Period start="PT0S"> <Period start="PT0S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="dynamic" profiles="urn:mpeg:dash:profile:isoff-live:2011" availabilityStartTime="place_holder" minimumUpdatePeriod="PT5S" timeShiftBufferDepth="PT1800S">
<Period start="PT0S"> <Period start="PT0S">
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.763174533843994S">
<Period> <Period>
<AdaptationSet id="0" contentType="text"> <AdaptationSet id="0" contentType="text">
@ -7,19 +8,19 @@
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
<AdaptationSet id="1" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <AdaptationSet id="1" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="1" bandwidth="881637" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001"> <Representation id="1" bandwidth="882040" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
<BaseURL>output_video.mp4</BaseURL> <BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="677-744" timescale="30000"> <SegmentBase indexRange="815-882" timescale="30000">
<Initialization range="0-676"/> <Initialization range="0-814"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>
<AdaptationSet id="2" contentType="audio" subsegmentAlignment="true"> <AdaptationSet id="2" contentType="audio" subsegmentAlignment="true">
<Representation id="2" bandwidth="126087" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100"> <Representation id="2" bandwidth="126487" codecs="mp4a.40.2" mimeType="audio/mp4" audioSamplingRate="44100">
<AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/> <AudioChannelConfiguration schemeIdUri="urn:mpeg:dash:23003:3:audio_channel_configuration:2011" value="2"/>
<BaseURL>output_audio.mp4</BaseURL> <BaseURL>output_audio.mp4</BaseURL>
<SegmentBase indexRange="611-678" timescale="44100"> <SegmentBase indexRange="749-816" timescale="44100">
<Initialization range="0-610"/> <Initialization range="0-748"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -1,15 +1,16 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.7694332599639893S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.7694332599639893S">
<Period> <Period>
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="264624" codecs="hev1.1.6.L63.80" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001"> <Representation id="0" bandwidth="265022" codecs="hev1.1.6.L63.80" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
<ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/> <ContentProtection value="cenc" schemeIdUri="urn:mpeg:dash:mp4protection:2011" cenc:default_KID="31323334-3536-3738-3930-313233343536"/>
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed"> <ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh> <cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
</ContentProtection> </ContentProtection>
<BaseURL>output_video.mp4</BaseURL> <BaseURL>output_video.mp4</BaseURL>
<SegmentBase indexRange="2721-2764" timescale="30000"> <SegmentBase indexRange="2859-2902" timescale="30000">
<Initialization range="0-2720"/> <Initialization range="0-2858"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -1,4 +1,4 @@
bandwidth: 885151 bandwidth: 885555
video_info { video_info {
codec: "avc1.64001e" codec: "avc1.64001e"
width: 640 width: 640
@ -11,11 +11,11 @@ video_info {
} }
init_range { init_range {
begin: 0 begin: 0
end: 940 end: 1078
} }
index_range { index_range {
begin: 941 begin: 1079
end: 1008 end: 1146
} }
media_file_name: "place_holder" media_file_name: "place_holder"
media_duration_seconds: 2.7360666 media_duration_seconds: 2.7360666

View File

@ -1,11 +1,12 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.7360665798187256S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT2.7360665798187256S">
<Period> <Period>
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9"> <AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
<Representation id="0" bandwidth="881637" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001"> <Representation id="0" bandwidth="882040" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1" width="640" height="360" frameRate="30000/1001">
<BaseURL>output_0.mp4</BaseURL> <BaseURL>output_0.mp4</BaseURL>
<SegmentBase indexRange="677-744" timescale="30000"> <SegmentBase indexRange="815-882" timescale="30000">
<Initialization range="0-676"/> <Initialization range="0-814"/>
</SegmentBase> </SegmentBase>
</Representation> </Representation>
</AdaptationSet> </AdaptationSet>

View File

@ -1,4 +1,5 @@
<?xml version="1.0" encoding="UTF-8"?> <?xml version="1.0" encoding="UTF-8"?>
<!--Generated with https://github.com/google/edash-packager version <tag>-<hash>-<test>-->
<MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT0S"> <MPD xmlns="urn:mpeg:dash:schema:mpd:2011" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xlink="http://www.w3.org/1999/xlink" xsi:schemaLocation="urn:mpeg:dash:schema:mpd:2011 DASH-MPD.xsd" xmlns:cenc="urn:mpeg:cenc:2013" minBufferTime="PT2S" type="static" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" mediaPresentationDuration="PT0S">
<Period> <Period>
<AdaptationSet id="0" contentType="text"> <AdaptationSet id="0" contentType="text">

View File

@ -84,6 +84,7 @@
'../../third_party/boringssl/boringssl.gyp:boringssl', '../../third_party/boringssl/boringssl.gyp:boringssl',
'../../third_party/curl/curl.gyp:libcurl', '../../third_party/curl/curl.gyp:libcurl',
'../../third_party/libxml/libxml.gyp:libxml', '../../third_party/libxml/libxml.gyp:libxml',
'../../version/version.gyp:version',
], ],
}, },
{ {

View File

@ -5,6 +5,7 @@
// https://developers.google.com/open-source/licenses/bsd // https://developers.google.com/open-source/licenses/bsd
#include "packager/media/base/muxer_options.h" #include "packager/media/base/muxer_options.h"
#include "packager/version/version.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -16,7 +17,8 @@ MuxerOptions::MuxerOptions()
segment_sap_aligned(false), segment_sap_aligned(false),
fragment_sap_aligned(false), fragment_sap_aligned(false),
num_subsegments_per_sidx(0), num_subsegments_per_sidx(0),
bandwidth(0) {} bandwidth(0),
packager_version_string(kPackagerVersion) {}
MuxerOptions::~MuxerOptions() {} MuxerOptions::~MuxerOptions() {}
} // namespace media } // namespace media

View File

@ -66,6 +66,9 @@ struct MuxerOptions {
/// User-specified bit rate for the media stream. If zero, the muxer will /// User-specified bit rate for the media stream. If zero, the muxer will
/// attempt to estimate. /// attempt to estimate.
uint32_t bandwidth; uint32_t bandwidth;
/// Specify the version string to be embedded in the output files.
std::string packager_version_string;
}; };
} // namespace media } // namespace media

View File

@ -63,6 +63,11 @@ bool IsIvSizeValid(size_t iv_size) {
// bit(5) Reserved // 0 // bit(5) Reserved // 0
const uint8_t kDdtsExtraData[] = {0xe4, 0x7c, 0, 4, 0, 0x0f, 0}; const uint8_t kDdtsExtraData[] = {0xe4, 0x7c, 0, 4, 0, 0x0f, 0};
// ID3v2 header: http://id3.org/id3v2.4.0-structure
const uint32_t kID3v2HeaderSize = 10;
const char kID3v2Identifier[] = "ID3";
const uint16_t kID3v2Version = 0x0400; // id3v2.4.0
// Utility functions to check if the 64bit integers can fit in 32bit integer. // Utility functions to check if the 64bit integers can fit in 32bit integer.
bool IsFitIn32Bits(uint64_t a) { bool IsFitIn32Bits(uint64_t a) {
return a <= std::numeric_limits<uint32_t>::max(); return a <= std::numeric_limits<uint32_t>::max();
@ -89,6 +94,36 @@ namespace edash_packager {
namespace media { namespace media {
namespace mp4 { namespace mp4 {
namespace {
TrackType FourCCToTrackType(FourCC fourcc) {
switch (fourcc) {
case FOURCC_VIDE:
return kVideo;
case FOURCC_SOUN:
return kAudio;
case FOURCC_TEXT:
return kText;
default:
return kInvalid;
}
}
FourCC TrackTypeToFourCC(TrackType track_type) {
switch (track_type) {
case kVideo:
return FOURCC_VIDE;
case kAudio:
return FOURCC_SOUN;
case kText:
return FOURCC_TEXT;
default:
return FOURCC_NULL;
}
}
} // namespace
FileType::FileType() : major_brand(FOURCC_NULL), minor_version(0) {} FileType::FileType() : major_brand(FOURCC_NULL), minor_version(0) {}
FileType::~FileType() {} FileType::~FileType() {}
FourCC FileType::BoxType() const { return FOURCC_FTYP; } FourCC FileType::BoxType() const { return FOURCC_FTYP; }
@ -967,44 +1002,37 @@ uint32_t Edit::ComputeSizeInternal() {
return HeaderSize() + list.ComputeSize(); return HeaderSize() + list.ComputeSize();
} }
HandlerReference::HandlerReference() : type(kInvalid) {} HandlerReference::HandlerReference() : handler_type(FOURCC_NULL) {}
HandlerReference::~HandlerReference() {} HandlerReference::~HandlerReference() {}
FourCC HandlerReference::BoxType() const { return FOURCC_HDLR; } FourCC HandlerReference::BoxType() const { return FOURCC_HDLR; }
bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) { bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) {
FourCC hdlr_type = FOURCC_NULL;
std::vector<uint8_t> handler_name; std::vector<uint8_t> handler_name;
if (!buffer->Reading()) { if (!buffer->Reading()) {
if (type == kVideo) { switch (handler_type) {
hdlr_type = FOURCC_VIDE; case FOURCC_VIDE:
handler_name.assign(kVideoHandlerName, handler_name.assign(kVideoHandlerName,
kVideoHandlerName + arraysize(kVideoHandlerName)); kVideoHandlerName + arraysize(kVideoHandlerName));
} else if (type == kAudio) { break;
hdlr_type = FOURCC_SOUN; case FOURCC_SOUN:
handler_name.assign(kAudioHandlerName, handler_name.assign(kAudioHandlerName,
kAudioHandlerName + arraysize(kAudioHandlerName)); kAudioHandlerName + arraysize(kAudioHandlerName));
} else if (type == kText) { break;
hdlr_type = FOURCC_TEXT; case FOURCC_TEXT:
handler_name.assign(kTextHandlerName, handler_name.assign(kTextHandlerName,
kTextHandlerName + arraysize(kTextHandlerName)); kTextHandlerName + arraysize(kTextHandlerName));
} else { break;
case FOURCC_ID32:
break;
default:
NOTIMPLEMENTED(); NOTIMPLEMENTED();
return false; return false;
} }
} }
RCHECK(ReadWriteHeaderInternal(buffer) && RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->IgnoreBytes(4) && // predefined. buffer->IgnoreBytes(4) && // predefined.
buffer->ReadWriteFourCC(&hdlr_type)); buffer->ReadWriteFourCC(&handler_type));
if (buffer->Reading()) { if (!buffer->Reading()) {
// Note: for reading, remaining fields in box ignored.
if (hdlr_type == FOURCC_VIDE) {
type = kVideo;
} else if (hdlr_type == FOURCC_SOUN) {
type = kAudio;
} else {
type = kInvalid;
}
} else {
RCHECK(buffer->IgnoreBytes(12) && // reserved. RCHECK(buffer->IgnoreBytes(12) && // reserved.
buffer->ReadWriteVector(&handler_name, handler_name.size())); buffer->ReadWriteVector(&handler_name, handler_name.size()));
} }
@ -1013,16 +1041,161 @@ bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) {
uint32_t HandlerReference::ComputeSizeInternal() { uint32_t HandlerReference::ComputeSizeInternal() {
uint32_t box_size = HeaderSize() + kFourCCSize + 16; // 16 bytes Reserved uint32_t box_size = HeaderSize() + kFourCCSize + 16; // 16 bytes Reserved
if (type == kVideo) { switch (handler_type) {
case FOURCC_VIDE:
box_size += sizeof(kVideoHandlerName); box_size += sizeof(kVideoHandlerName);
} else if (type == kAudio) { break;
case FOURCC_SOUN:
box_size += sizeof(kAudioHandlerName); box_size += sizeof(kAudioHandlerName);
} else { break;
case FOURCC_TEXT:
box_size += sizeof(kTextHandlerName); box_size += sizeof(kTextHandlerName);
break;
case FOURCC_ID32:
break;
default:
NOTIMPLEMENTED();
} }
return box_size; return box_size;
} }
bool Language::ReadWrite(BoxBuffer* buffer) {
if (buffer->Reading()) {
// Read language codes into temp first then use BitReader to read the
// values. ISO-639-2/T language code: unsigned int(5)[3] language (2 bytes).
std::vector<uint8_t> temp;
RCHECK(buffer->ReadWriteVector(&temp, 2));
BitReader bit_reader(&temp[0], 2);
bit_reader.SkipBits(1);
char language[3];
for (int i = 0; i < 3; ++i) {
CHECK(bit_reader.ReadBits(5, &language[i]));
language[i] += 0x60;
}
code.assign(language, 3);
} else {
// Set up default language if it is not set.
const char kUndefinedLanguage[] = "und";
if (code.empty())
code = kUndefinedLanguage;
DCHECK_EQ(code.size(), 3u);
// Lang format: bit(1) pad, unsigned int(5)[3] language.
uint16_t lang = 0;
for (int i = 0; i < 3; ++i)
lang |= (code[i] - 0x60) << ((2 - i) * 5);
RCHECK(buffer->ReadWriteUInt16(&lang));
}
return true;
}
uint32_t Language::ComputeSize() const {
// ISO-639-2/T language code: unsigned int(5)[3] language (2 bytes).
return 2;
}
bool PrivFrame::ReadWrite(BoxBuffer* buffer) {
FourCC fourcc = FOURCC_PRIV;
RCHECK(buffer->ReadWriteFourCC(&fourcc));
if (fourcc != FOURCC_PRIV) {
VLOG(1) << "Skip unrecognized id3 frame during read: "
<< FourCCToString(fourcc);
return true;
}
uint32_t frame_size = owner.size() + 1 + value.size();
// size should be encoded as synchsafe integer, which is not support here.
// We don't expect frame_size to be larger than 0x7F. Synchsafe integers less
// than 0x7F is encoded in the same way as normal integer.
DCHECK_LT(frame_size, 0x7Fu);
uint16_t flags = 0;
RCHECK(buffer->ReadWriteUInt32(&frame_size) &&
buffer->ReadWriteUInt16(&flags));
if (buffer->Reading()) {
std::string str;
RCHECK(buffer->ReadWriteString(&str, frame_size));
// |owner| is null terminated.
size_t pos = str.find('\0');
RCHECK(pos < str.size());
owner = str.substr(0, pos);
value = str.substr(pos + 1);
} else {
uint8_t byte = 0; // Null terminating byte between owner and value.
RCHECK(buffer->ReadWriteString(&owner, owner.size()) &&
buffer->ReadWriteUInt8(&byte) &&
buffer->ReadWriteString(&value, value.size()));
}
return true;
}
uint32_t PrivFrame::ComputeSize() const {
if (owner.empty() && value.empty())
return 0;
const uint32_t kFourCCSize = 4;
return kFourCCSize + sizeof(uint32_t) + sizeof(uint16_t) + owner.size() + 1 +
value.size();
}
ID3v2::ID3v2() {}
ID3v2::~ID3v2() {}
FourCC ID3v2::BoxType() const { return FOURCC_ID32; }
bool ID3v2::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
language.ReadWrite(buffer));
// Read/Write ID3v2 header
std::string id3v2_identifier = kID3v2Identifier;
uint16_t version = kID3v2Version;
// We only support PrivateFrame in ID3.
uint32_t data_size = private_frame.ComputeSize();
// size should be encoded as synchsafe integer, which is not support here.
// We don't expect data_size to be larger than 0x7F. Synchsafe integers less
// than 0x7F is encoded in the same way as normal integer.
DCHECK_LT(data_size, 0x7Fu);
uint8_t flags = 0;
RCHECK(buffer->ReadWriteString(&id3v2_identifier, id3v2_identifier.size()) &&
buffer->ReadWriteUInt16(&version) &&
buffer->ReadWriteUInt8(&flags) &&
buffer->ReadWriteUInt32(&data_size));
RCHECK(private_frame.ReadWrite(buffer));
return true;
}
uint32_t ID3v2::ComputeSizeInternal() {
uint32_t private_frame_size = private_frame.ComputeSize();
// Skip ID3v2 box generation if there is no private frame.
return private_frame_size == 0 ? 0 : HeaderSize() + language.ComputeSize() +
kID3v2HeaderSize +
private_frame_size;
}
Metadata::Metadata() {}
Metadata::~Metadata() {}
FourCC Metadata::BoxType() const {
return FOURCC_META;
}
bool Metadata::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() &&
buffer->ReadWriteChild(&handler) &&
buffer->TryReadWriteChild(&id3v2));
return true;
}
uint32_t Metadata::ComputeSizeInternal() {
uint32_t id3v2_size = id3v2.ComputeSize();
// Skip metadata box generation if there is no metadata box.
return id3v2_size == 0 ? 0
: HeaderSize() + handler.ComputeSize() + id3v2_size;
}
CodecConfigurationRecord::CodecConfigurationRecord() : box_type(FOURCC_NULL) {} CodecConfigurationRecord::CodecConfigurationRecord() : box_type(FOURCC_NULL) {}
CodecConfigurationRecord::~CodecConfigurationRecord() {} CodecConfigurationRecord::~CodecConfigurationRecord() {}
FourCC CodecConfigurationRecord::BoxType() const { FourCC CodecConfigurationRecord::BoxType() const {
@ -1369,9 +1542,7 @@ uint32_t WVTTSampleEntry::ComputeSizeInternal() {
} }
MediaHeader::MediaHeader() MediaHeader::MediaHeader()
: creation_time(0), modification_time(0), timescale(0), duration(0) { : creation_time(0), modification_time(0), timescale(0), duration(0) {}
language[0] = 0;
}
MediaHeader::~MediaHeader() {} MediaHeader::~MediaHeader() {}
FourCC MediaHeader::BoxType() const { return FOURCC_MDHD; } FourCC MediaHeader::BoxType() const { return FOURCC_MDHD; }
@ -1382,42 +1553,16 @@ bool MediaHeader::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) && RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) &&
buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) && buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) &&
buffer->ReadWriteUInt32(&timescale) && buffer->ReadWriteUInt32(&timescale) &&
buffer->ReadWriteUInt64NBytes(&duration, num_bytes)); buffer->ReadWriteUInt64NBytes(&duration, num_bytes) &&
language.ReadWrite(buffer) &&
if (buffer->Reading()) { buffer->IgnoreBytes(2)); // predefined.
// Read language codes into temp first then use BitReader to read the
// values. ISO-639-2/T language code: unsigned int(5)[3] language (2 bytes).
std::vector<uint8_t> temp;
RCHECK(buffer->ReadWriteVector(&temp, 2));
BitReader bit_reader(&temp[0], 2);
bit_reader.SkipBits(1);
for (int i = 0; i < 3; ++i) {
CHECK(bit_reader.ReadBits(5, &language[i]));
language[i] += 0x60;
}
language[3] = '\0';
} else {
// Set up default language if it is not set.
const char kUndefinedLanguage[] = "und";
if (language[0] == 0)
strcpy(language, kUndefinedLanguage);
// Lang format: bit(1) pad, unsigned int(5)[3] language.
uint16_t lang = 0;
for (int i = 0; i < 3; ++i)
lang |= (language[i] - 0x60) << ((2 - i) * 5);
RCHECK(buffer->ReadWriteUInt16(&lang));
}
RCHECK(buffer->IgnoreBytes(2)); // predefined.
return true; return true;
} }
uint32_t MediaHeader::ComputeSizeInternal() { uint32_t MediaHeader::ComputeSizeInternal() {
version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1; version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1;
return HeaderSize() + sizeof(timescale) + return HeaderSize() + sizeof(timescale) +
sizeof(uint32_t) * (1 + version) * 3 + 2 + // 2 bytes language. sizeof(uint32_t) * (1 + version) * 3 + language.ComputeSize() +
2; // 2 bytes predefined. 2; // 2 bytes predefined.
} }
@ -1580,24 +1725,30 @@ FourCC Media::BoxType() const { return FOURCC_MDIA; }
bool Media::ReadWriteInternal(BoxBuffer* buffer) { bool Media::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) && RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header) && buffer->ReadWriteChild(&header));
buffer->ReadWriteChild(&handler));
if (buffer->Reading()) { if (buffer->Reading()) {
RCHECK(buffer->ReadWriteChild(&handler));
// Maddeningly, the HandlerReference box specifies how to parse the // Maddeningly, the HandlerReference box specifies how to parse the
// SampleDescription box, making the latter the only box (of those that we // SampleDescription box, making the latter the only box (of those that we
// support) which cannot be parsed correctly on its own (or even with // support) which cannot be parsed correctly on its own (or even with
// information from its strict ancestor tree). We thus copy the handler type // information from its strict ancestor tree). We thus copy the handler type
// to the sample description box *before* parsing it to provide this // to the sample description box *before* parsing it to provide this
// information while parsing. // information while parsing.
information.sample_table.description.type = handler.type; information.sample_table.description.type =
FourCCToTrackType(handler.handler_type);
} else { } else {
DCHECK_EQ(information.sample_table.description.type, handler.type); handler.handler_type =
TrackTypeToFourCC(information.sample_table.description.type);
RCHECK(handler.handler_type != FOURCC_NULL);
RCHECK(buffer->ReadWriteChild(&handler));
} }
RCHECK(buffer->ReadWriteChild(&information)); RCHECK(buffer->ReadWriteChild(&information));
return true; return true;
} }
uint32_t Media::ComputeSizeInternal() { uint32_t Media::ComputeSizeInternal() {
handler.handler_type =
TrackTypeToFourCC(information.sample_table.description.type);
return HeaderSize() + header.ComputeSize() + handler.ComputeSize() + return HeaderSize() + header.ComputeSize() + handler.ComputeSize() +
information.ComputeSize(); information.ComputeSize();
} }
@ -1702,6 +1853,7 @@ bool Movie::ReadWriteInternal(BoxBuffer* buffer) {
RCHECK(ReadWriteHeaderInternal(buffer) && RCHECK(ReadWriteHeaderInternal(buffer) &&
buffer->PrepareChildren() && buffer->PrepareChildren() &&
buffer->ReadWriteChild(&header) && buffer->ReadWriteChild(&header) &&
buffer->TryReadWriteChild(&metadata) &&
buffer->TryReadWriteChild(&extends)); buffer->TryReadWriteChild(&extends));
if (buffer->Reading()) { if (buffer->Reading()) {
BoxReader* reader = buffer->reader(); BoxReader* reader = buffer->reader();
@ -1718,8 +1870,8 @@ bool Movie::ReadWriteInternal(BoxBuffer* buffer) {
} }
uint32_t Movie::ComputeSizeInternal() { uint32_t Movie::ComputeSizeInternal() {
uint32_t box_size = uint32_t box_size = HeaderSize() + header.ComputeSize() +
HeaderSize() + header.ComputeSize() + extends.ComputeSize(); metadata.ComputeSize() + extends.ComputeSize();
for (uint32_t i = 0; i < tracks.size(); ++i) for (uint32_t i = 0; i < tracks.size(); ++i)
box_size += tracks[i].ComputeSize(); box_size += tracks[i].ComputeSize();
for (uint32_t i = 0; i < pssh.size(); ++i) for (uint32_t i = 0; i < pssh.size(); ++i)

View File

@ -222,7 +222,41 @@ struct Edit : Box {
struct HandlerReference : FullBox { struct HandlerReference : FullBox {
DECLARE_BOX_METHODS(HandlerReference); DECLARE_BOX_METHODS(HandlerReference);
TrackType type; FourCC handler_type;
};
struct Language {
bool ReadWrite(BoxBuffer* buffer);
uint32_t ComputeSize() const;
std::string code;
};
/// Implemented per http://id3.org/id3v2.4.0-frames.
struct PrivFrame {
bool ReadWrite(BoxBuffer* buffer);
uint32_t ComputeSize() const;
std::string owner;
std::string value;
};
/// Implemented per http://mp4ra.org/specs.html#id3v2 and
/// http://id3.org/id3v2.4.0-structure.
struct ID3v2 : FullBox {
DECLARE_BOX_METHODS(ID3v2);
Language language;
/// We only support PrivateFrame in ID3. Other frames are ignored.
PrivFrame private_frame;
};
struct Metadata : FullBox {
DECLARE_BOX_METHODS(Metadata);
HandlerReference handler;
ID3v2 id3v2;
}; };
struct CodecConfigurationRecord : Box { struct CodecConfigurationRecord : Box {
@ -422,8 +456,7 @@ struct MediaHeader : FullBox {
uint64_t modification_time; uint64_t modification_time;
uint32_t timescale; uint32_t timescale;
uint64_t duration; uint64_t duration;
// 3-char language code + 1 null terminating char. Language language;
char language[4];
}; };
struct VideoMediaHeader : FullBox { struct VideoMediaHeader : FullBox {
@ -519,6 +552,7 @@ struct Movie : Box {
DECLARE_BOX_METHODS(Movie); DECLARE_BOX_METHODS(Movie);
MovieHeader header; MovieHeader header;
Metadata metadata; // Used to hold version information.
MovieExtends extends; MovieExtends extends;
std::vector<Track> tracks; std::vector<Track> tracks;
std::vector<ProtectionSystemSpecificHeader> pssh; std::vector<ProtectionSystemSpecificHeader> pssh;

View File

@ -180,7 +180,24 @@ inline bool operator==(const Edit& lhs, const Edit& rhs) {
inline bool operator==(const HandlerReference& lhs, inline bool operator==(const HandlerReference& lhs,
const HandlerReference& rhs) { const HandlerReference& rhs) {
return lhs.type == rhs.type; return lhs.handler_type == rhs.handler_type;
}
inline bool operator==(const Language& lhs,
const Language& rhs) {
return lhs.code == rhs.code;
}
inline bool operator==(const PrivFrame& lhs, const PrivFrame& rhs) {
return lhs.owner == rhs.owner && lhs.value == rhs.value;
}
inline bool operator==(const ID3v2& lhs, const ID3v2& rhs) {
return lhs.language == rhs.language && lhs.private_frame == rhs.private_frame;
}
inline bool operator==(const Metadata& lhs, const Metadata& rhs) {
return lhs.handler == rhs.handler && lhs.id3v2 == rhs.id3v2;
} }
inline bool operator==(const CodecConfigurationRecord& lhs, inline bool operator==(const CodecConfigurationRecord& lhs,
@ -252,7 +269,7 @@ inline bool operator==(const MediaHeader& lhs, const MediaHeader& rhs) {
return lhs.creation_time == rhs.creation_time && return lhs.creation_time == rhs.creation_time &&
lhs.modification_time == rhs.modification_time && lhs.modification_time == rhs.modification_time &&
lhs.timescale == rhs.timescale && lhs.duration == rhs.duration && lhs.timescale == rhs.timescale && lhs.duration == rhs.duration &&
strcmp(lhs.language, rhs.language) == 0; lhs.language == rhs.language;
} }
inline bool operator==(const VideoMediaHeader& lhs, inline bool operator==(const VideoMediaHeader& lhs,

View File

@ -286,11 +286,29 @@ class BoxDefinitionsTestGeneral : public testing::Test {
void Modify(Edit* edts) { Modify(&edts->list); } void Modify(Edit* edts) { Modify(&edts->list); }
void Fill(HandlerReference* hdlr) { void Fill(HandlerReference* hdlr) { hdlr->handler_type = FOURCC_VIDE; }
hdlr->type = kSampleDescriptionTrackType;
void Modify(HandlerReference* hdlr) { hdlr->handler_type = FOURCC_SOUN; }
void Fill(ID3v2* id3v2) {
id3v2->language.code = "eng";
id3v2->private_frame.owner = "edash-packager";
id3v2->private_frame.value = "version 1.2.0-debug";
} }
void Modify(HandlerReference* hdlr) { hdlr->type = kAudio; } void Modify(ID3v2* id3v2) {
id3v2->language.code = "fre";
id3v2->private_frame.value = "version 1.3.1-release";
}
void Fill(Metadata* metadata) {
metadata->handler.handler_type = FOURCC_ID32;
Fill(&metadata->id3v2);
}
void Modify(Metadata* metadata) {
Modify(&metadata->id3v2);
}
void Fill(PixelAspectRatio* pasp) { void Fill(PixelAspectRatio* pasp) {
pasp->h_spacing = 5; pasp->h_spacing = 5;
@ -517,14 +535,14 @@ class BoxDefinitionsTestGeneral : public testing::Test {
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1; static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1;
mdhd->timescale = 50000; mdhd->timescale = 50000;
mdhd->duration = 250000; mdhd->duration = 250000;
strcpy(mdhd->language, "abc"); mdhd->language.code = "abc";
mdhd->version = 1; mdhd->version = 1;
} }
void Modify(MediaHeader* mdhd) { void Modify(MediaHeader* mdhd) {
mdhd->creation_time = 2; mdhd->creation_time = 2;
mdhd->modification_time = std::numeric_limits<uint32_t>::max(); mdhd->modification_time = std::numeric_limits<uint32_t>::max();
strcpy(mdhd->language, "und"); mdhd->language.code = "und";
mdhd->version = 0; mdhd->version = 0;
} }
@ -633,6 +651,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
void Fill(Movie* moov) { void Fill(Movie* moov) {
Fill(&moov->header); Fill(&moov->header);
Fill(&moov->metadata);
Fill(&moov->extends); Fill(&moov->extends);
moov->tracks.resize(2); moov->tracks.resize(2);
Fill(&moov->tracks[0]); Fill(&moov->tracks[0]);
@ -854,6 +873,8 @@ class BoxDefinitionsTestGeneral : public testing::Test {
Modify(&vttc->cue_payload); Modify(&vttc->cue_payload);
} }
bool IsOptional(const ID3v2* box) { return true; }
bool IsOptional(const Metadata* box) { return true; }
bool IsOptional(const SampleAuxiliaryInformationOffset* box) { return true; } bool IsOptional(const SampleAuxiliaryInformationOffset* box) { return true; }
bool IsOptional(const SampleAuxiliaryInformationSize* box) { return true; } bool IsOptional(const SampleAuxiliaryInformationSize* box) { return true; }
bool IsOptional(const SampleEncryption* box) { return true; } bool IsOptional(const SampleEncryption* box) { return true; }
@ -881,8 +902,9 @@ class BoxDefinitionsTestGeneral : public testing::Test {
scoped_ptr<BufferWriter> buffer_; scoped_ptr<BufferWriter> buffer_;
}; };
typedef testing::Types< // GTEST support a maximum of 50 types in the template list, so we have to
FileType, // break it into two groups.
typedef testing::Types<FileType,
SegmentType, SegmentType,
ProtectionSystemSpecificHeader, ProtectionSystemSpecificHeader,
SampleAuxiliaryInformationOffset, SampleAuxiliaryInformationOffset,
@ -897,10 +919,13 @@ typedef testing::Types<
EditList, EditList,
Edit, Edit,
HandlerReference, HandlerReference,
ID3v2,
Metadata,
CodecConfigurationRecord, CodecConfigurationRecord,
PixelAspectRatio, PixelAspectRatio,
VideoSampleEntry, VideoSampleEntry,
ElementaryStreamDescriptor, ElementaryStreamDescriptor,
DTSSpecific,
AudioSampleEntry, AudioSampleEntry,
WebVTTConfigurationBox, WebVTTConfigurationBox,
WebVTTSourceLabelBox, WebVTTSourceLabelBox,
@ -914,8 +939,9 @@ typedef testing::Types<
ChunkLargeOffset, ChunkLargeOffset,
ChunkOffset, ChunkOffset,
SyncSample, SyncSample,
SampleTable, SampleTable>
MediaHeader, Boxes;
typedef testing::Types<MediaHeader,
VideoMediaHeader, VideoMediaHeader,
SoundMediaHeader, SoundMediaHeader,
SubtitleMediaHeader, SubtitleMediaHeader,
@ -931,11 +957,7 @@ typedef testing::Types<
Movie, Movie,
TrackFragmentDecodeTime, TrackFragmentDecodeTime,
MovieFragmentHeader, MovieFragmentHeader,
TrackFragmentHeader> Boxes; TrackFragmentHeader,
// GTEST support a maximum of 50 types in the template list, so we have to
// break it into two groups.
typedef testing::Types<
TrackFragmentRun, TrackFragmentRun,
TrackFragment, TrackFragment,
MovieFragment, MovieFragment,
@ -949,8 +971,8 @@ typedef testing::Types<
CuePayloadBox, CuePayloadBox,
VTTEmptyCueBox, VTTEmptyCueBox,
VTTAdditionalTextBox, VTTAdditionalTextBox,
VTTCueBox, VTTCueBox>
DTSSpecific> Boxes2; Boxes2;
TYPED_TEST_CASE_P(BoxDefinitionsTestGeneral); TYPED_TEST_CASE_P(BoxDefinitionsTestGeneral);
@ -1021,6 +1043,20 @@ INSTANTIATE_TYPED_TEST_CASE_P(BoxDefinitionTypedTests2,
// Test other cases of box input. // Test other cases of box input.
class BoxDefinitionsTest : public BoxDefinitionsTestGeneral<Box> {}; class BoxDefinitionsTest : public BoxDefinitionsTestGeneral<Box> {};
TEST_F(BoxDefinitionsTest, MediaHandlerType) {
Media media;
Fill(&media);
// Clear handler type. When this box is written, it will derive handler type
// from sample table description.
media.handler.handler_type = FOURCC_NULL;
media.information.sample_table.description.type = kVideo;
media.Write(this->buffer_.get());
Media media_readback;
ASSERT_TRUE(ReadBack(&media_readback));
ASSERT_EQ(FOURCC_VIDE, media_readback.handler.handler_type);
}
TEST_F(BoxDefinitionsTest, DTSSampleEntry) { TEST_F(BoxDefinitionsTest, DTSSampleEntry) {
AudioSampleEntry entry; AudioSampleEntry entry;
entry.format = FOURCC_DTSE; entry.format = FOURCC_DTSE;

View File

@ -14,6 +14,8 @@ namespace mp4 {
// TODO(rkuroiwa): Make these case sensitive. e.g. FOURCC_avc1. // TODO(rkuroiwa): Make these case sensitive. e.g. FOURCC_avc1.
enum FourCC { enum FourCC {
FOURCC_NULL = 0, FOURCC_NULL = 0,
FOURCC_ID32 = 0x49443332,
FOURCC_PRIV = 0x50524956,
FOURCC_AVC1 = 0x61766331, FOURCC_AVC1 = 0x61766331,
FOURCC_AVCC = 0x61766343, FOURCC_AVCC = 0x61766343,
FOURCC_BLOC = 0x626C6F63, FOURCC_BLOC = 0x626C6F63,

View File

@ -327,7 +327,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
RCHECK(desc_idx > 0); RCHECK(desc_idx > 0);
desc_idx -= 1; // BMFF descriptor index is one-based desc_idx -= 1; // BMFF descriptor index is one-based
if (track->media.handler.type == kAudio) { if (samp_descr.type == kAudio) {
RCHECK(!samp_descr.audio_entries.empty()); RCHECK(!samp_descr.audio_entries.empty());
// It is not uncommon to find otherwise-valid files with incorrect sample // It is not uncommon to find otherwise-valid files with incorrect sample
@ -428,7 +428,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
duration, duration,
codec, codec,
AudioStreamInfo::GetCodecString(codec, audio_object_type), AudioStreamInfo::GetCodecString(codec, audio_object_type),
track->media.header.language, track->media.header.language.code,
entry.samplesize, entry.samplesize,
num_channels, num_channels,
sampling_frequency, sampling_frequency,
@ -439,7 +439,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
is_encrypted)); is_encrypted));
} }
if (track->media.handler.type == kVideo) { if (samp_descr.type == kVideo) {
RCHECK(!samp_descr.video_entries.empty()); RCHECK(!samp_descr.video_entries.empty());
if (desc_idx >= samp_descr.video_entries.size()) if (desc_idx >= samp_descr.video_entries.size())
desc_idx = 0; desc_idx = 0;
@ -527,8 +527,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted; DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
streams.push_back(new VideoStreamInfo( streams.push_back(new VideoStreamInfo(
track->header.track_id, timescale, duration, video_codec, track->header.track_id, timescale, duration, video_codec,
codec_string, track->media.header.language, coded_width, coded_height, codec_string, track->media.header.language.code, coded_width,
pixel_width, pixel_height, coded_height, pixel_width, pixel_height,
0, // trick_play_rate 0, // trick_play_rate
nalu_length_size, vector_as_array(&entry.codec_config_record.data), nalu_length_size, vector_as_array(&entry.codec_config_record.data),
entry.codec_config_record.data.size(), is_encrypted)); entry.codec_config_record.data.size(), is_encrypted));

View File

@ -187,13 +187,12 @@ void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) {
trak->media.header.timescale = info->time_scale(); trak->media.header.timescale = info->time_scale();
trak->media.header.duration = 0; trak->media.header.duration = 0;
if (!info->language().empty()) { if (!info->language().empty()) {
const size_t language_size = arraysize(trak->media.header.language) - 1; // ISO-639-2/T language code should be 3 characters..
if (info->language().size() != language_size) { if (info->language().size() != 3) {
LOG(WARNING) << "'" << info->language() << "' is not a valid ISO-639-2 " LOG(WARNING) << "'" << info->language() << "' is not a valid ISO-639-2 "
<< "language code, ignoring."; << "language code, ignoring.";
} else { } else {
memcpy(trak->media.header.language, info->language().c_str(), trak->media.header.language.code = info->language();
language_size + 1);
} }
} }
} }
@ -217,8 +216,6 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
trak->header.width = video_info->width() * sample_aspect_ratio * 0x10000; trak->header.width = video_info->width() * sample_aspect_ratio * 0x10000;
trak->header.height = video_info->height() * 0x10000; trak->header.height = video_info->height() * 0x10000;
trak->media.handler.type = kVideo;
VideoSampleEntry video; VideoSampleEntry video;
video.format = VideoCodecToFourCC(video_info->codec()); video.format = VideoCodecToFourCC(video_info->codec());
video.width = video_info->width(); video.width = video_info->width();
@ -241,7 +238,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
InitializeTrak(audio_info, trak); InitializeTrak(audio_info, trak);
trak->header.volume = 0x100; trak->header.volume = 0x100;
trak->media.handler.type = kAudio;
AudioSampleEntry audio; AudioSampleEntry audio;
audio.format = AudioCodecToFourCC(audio_info->codec()); audio.format = AudioCodecToFourCC(audio_info->codec());

View File

@ -238,6 +238,13 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
// Use the reference stream's time scale as movie time scale. // Use the reference stream's time scale as movie time scale.
moov_->header.timescale = sidx_->timescale; moov_->header.timescale = sidx_->timescale;
moof_->header.sequence_number = 1; moof_->header.sequence_number = 1;
// Fill in version information.
moov_->metadata.handler.handler_type = FOURCC_ID32;
moov_->metadata.id3v2.language.code = "eng";
moov_->metadata.id3v2.private_frame.owner =
"https://github.com/google/edash-packager";
moov_->metadata.id3v2.private_frame.value = options_.packager_version_string;
return DoInitialize(); return DoInitialize();
} }

View File

@ -13,6 +13,7 @@
#include "packager/base/memory/ref_counted.h" #include "packager/base/memory/ref_counted.h"
#include "packager/base/memory/scoped_ptr.h" #include "packager/base/memory/scoped_ptr.h"
#include "packager/media/base/status.h" #include "packager/media/base/status.h"
#include "packager/media/formats/mp4/box_definitions.h"
namespace edash_packager { namespace edash_packager {
namespace media { namespace media {
@ -30,11 +31,6 @@ namespace mp4 {
class Fragmenter; class Fragmenter;
struct FileType;
struct Movie;
struct MovieFragment;
struct SegmentIndex;
/// This class defines the Segmenter which is responsible for organizing /// This class defines the Segmenter which is responsible for organizing
/// fragments into segments/subsegments and package them into a MP4 file. /// fragments into segments/subsegments and package them into a MP4 file.
/// Inherited by MultiSegmentSegmenter and SingleSegmentSegmenter. /// Inherited by MultiSegmentSegmenter and SingleSegmentSegmenter.

View File

@ -494,7 +494,13 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
} }
DCHECK(doc); DCHECK(doc);
xmlDocSetRootElement(doc.get(), mpd.Release()); std::string version_string =
"Generated with https://github.com/google/edash-packager version " +
mpd_options_.packager_version_string;
xml::scoped_xml_ptr<xmlNode> comment(
xmlNewDocComment(doc.get(), BAD_CAST version_string.c_str()));
xmlDocSetRootElement(doc.get(), comment.get());
xmlAddSibling(comment.release(), mpd.Release());
return doc.release(); return doc.release();
} }

View File

@ -174,6 +174,8 @@ class DynamicMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kDynamic> {
// current time. // current time.
void SetUp() override { void SetUp() override {
mpd_.availability_start_time_ = "2011-12-25T12:30:00"; mpd_.availability_start_time_ = "2011-12-25T12:30:00";
// Override packager version string for testing.
mpd_.mpd_options_.packager_version_string = "<tag>-<hash>-<test>";
} }
MpdOptions* mutable_mpd_options() { return &mpd_.mpd_options_; } MpdOptions* mutable_mpd_options() { return &mpd_.mpd_options_; }
@ -1776,6 +1778,8 @@ TEST_F(StaticMpdBuilderTest, Text) {
TEST_F(DynamicMpdBuilderTest, CheckMpdAttributes) { TEST_F(DynamicMpdBuilderTest, CheckMpdAttributes) {
static const char kExpectedOutput[] = static const char kExpectedOutput[] =
"<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n"
"<!--Generated with https://github.com/google/edash-packager "
"version <tag>-<hash>-<test>-->\n"
"<MPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\" " "<MPD xmlns=\"urn:mpeg:dash:schema:mpd:2011\" "
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" " "xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" " "xmlns:xlink=\"http://www.w3.org/1999/xlink\" "

View File

@ -7,6 +7,10 @@
#ifndef MPD_BASE_MPD_OPTIONS_H_ #ifndef MPD_BASE_MPD_OPTIONS_H_
#define MPD_BASE_MPD_OPTIONS_H_ #define MPD_BASE_MPD_OPTIONS_H_
#include <string>
#include "packager/version/version.h"
namespace edash_packager { namespace edash_packager {
/// Defines Mpd Options. /// Defines Mpd Options.
@ -17,7 +21,8 @@ struct MpdOptions {
// TODO(tinskip): Set min_buffer_time in unit tests rather than here. // TODO(tinskip): Set min_buffer_time in unit tests rather than here.
min_buffer_time(2.0), min_buffer_time(2.0),
time_shift_buffer_depth(0), time_shift_buffer_depth(0),
suggested_presentation_delay(0) {} suggested_presentation_delay(0),
packager_version_string(kPackagerVersion) {}
~MpdOptions() {}; ~MpdOptions() {};
@ -26,6 +31,7 @@ struct MpdOptions {
double min_buffer_time; double min_buffer_time;
double time_shift_buffer_depth; double time_shift_buffer_depth;
double suggested_presentation_delay; double suggested_presentation_delay;
std::string packager_version_string;
}; };
} // namespace edash_packager } // namespace edash_packager

View File

@ -60,6 +60,7 @@
'../base/base.gyp:base', '../base/base.gyp:base',
'../media/file/file.gyp:file', '../media/file/file.gyp:file',
'../third_party/libxml/libxml.gyp:libxml', '../third_party/libxml/libxml.gyp:libxml',
'../version/version.gyp:version',
'media_info_proto', 'media_info_proto',
], ],
'export_dependent_settings': [ 'export_dependent_settings': [

View File

@ -0,0 +1,40 @@
#!/usr/bin/python
#
# Copyright 2015 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
"""This script is used to generate version string for packager."""
import subprocess
# To support python version before 2.7, which does not have
# subprocess.check_output.
if 'check_output' not in dir(subprocess):
def check_output_implementation(*popenargs, **kwargs):
"""Implement check_output if it is not available."""
if 'stdout' in kwargs:
raise ValueError('stdout argument not allowed, it will be overridden.')
process = subprocess.Popen(stdout=subprocess.PIPE, *popenargs, **kwargs)
output, unused_err = process.communicate()
retcode = process.poll()
if retcode:
cmd = kwargs.get('args')
if cmd is None:
cmd = popenargs[0]
raise subprocess.CalledProcessError(retcode, cmd)
return output
subprocess.check_output = check_output_implementation
if __name__ == '__main__':
version_tag = subprocess.check_output(['git', 'tag', '--points-at', 'HEAD'
]).rstrip()
version_hash = subprocess.check_output(['git', 'rev-parse', '--short', 'HEAD'
]).rstrip()
if version_tag:
print '{0}-{1}'.format(version_tag, version_hash)
else:
print version_hash

View File

@ -0,0 +1,23 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
#include "packager/version/version.h"
namespace edash_packager {
#if defined(PACKAGER_VERSION)
// PACKAGER_VERSION is generated in gyp file using script
// generate_version_string.py.
#if defined(NDEBUG)
const char kPackagerVersion[] = PACKAGER_VERSION "-release";
#else
const char kPackagerVersion[] = PACKAGER_VERSION "-debug";
#endif // #if defined(NDEBUG)
#else
const char kPackagerVersion[] = "";
#endif // #if defined(PACKAGER_VERSION)
} // namespace edash_packager

View File

@ -0,0 +1,24 @@
# Copyright 2015 Google Inc. All rights reserved.
#
# Use of this source code is governed by a BSD-style
# license that can be found in the LICENSE file or at
# https://developers.google.com/open-source/licenses/bsd
{
'includes': [
'../common.gypi',
],
'targets': [
{
'target_name': 'version',
'type': '<(component)',
'defines': [
'PACKAGER_VERSION="<!(python generate_version_string.py)"',
],
'sources': [
'version.cc',
'version.h',
],
},
],
}

View File

@ -0,0 +1,11 @@
// Copyright 2015 Google Inc. All rights reserved.
//
// Use of this source code is governed by a BSD-style
// license that can be found in the LICENSE file or at
// https://developers.google.com/open-source/licenses/bsd
namespace edash_packager {
extern const char kPackagerVersion[];
} // namespace edash_packager