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:
parent
9466582d9a
commit
e0040a4910
|
@ -1,35 +1,34 @@
|
|||
#!/usr/bin/env python
|
||||
#!/usr/bin/python
|
||||
#
|
||||
# Copyright 2014 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 a wrapper for Packager that adds some support for how GYP
|
||||
# is invoked by Packager.
|
||||
#
|
||||
# Build instructions:
|
||||
#
|
||||
# 1. Setup gyp: ./gyp_packager.py
|
||||
#
|
||||
# clang is not enabled by default, which can be enabled by overriding
|
||||
# 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.
|
||||
# "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
|
||||
# manual on how to do the build.
|
||||
#
|
||||
# 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.
|
||||
"""This script wraps gyp and sets up build environments.
|
||||
|
||||
Build instructions:
|
||||
|
||||
1. Setup gyp: ./gyp_packager.py or use gclient runhooks
|
||||
|
||||
clang is enabled by default, which can be disabled by overriding
|
||||
GYP_DEFINE environment variable, i.e.
|
||||
"GYP_DEFINES='clang=0' gclient runhooks".
|
||||
|
||||
Ninja is the default build system. User can also change to make by
|
||||
overriding GYP_GENERATORS to make, i.e.
|
||||
"GYP_GENERATORS='make' gclient runhooks".
|
||||
|
||||
2. The first step generates the make files but does not start the
|
||||
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.
|
||||
|
||||
Step 1 is only required if there is any gyp file change. Otherwise, you
|
||||
may just run ninja.
|
||||
"""
|
||||
|
||||
import os
|
||||
import sys
|
||||
|
@ -37,11 +36,14 @@ import sys
|
|||
checkout_dir = os.path.dirname(os.path.realpath(__file__))
|
||||
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'))
|
||||
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'))
|
||||
import gyp # Workaround the dynamic path. pylint: disable-msg=F0401
|
||||
import gyp
|
||||
|
||||
if __name__ == '__main__':
|
||||
args = sys.argv[1:]
|
||||
|
@ -58,12 +60,15 @@ if __name__ == '__main__':
|
|||
args.extend(['-I' + os.path.join(src_dir, 'build', 'common.gypi')])
|
||||
|
||||
# Set these default GYP_DEFINES if user does not set the value explicitly.
|
||||
_DEFAULT_DEFINES = {'test_isolation_mode': 'noop', 'use_glib': 0,
|
||||
'use_openssl': 1, 'use_x11': 0,
|
||||
'linux_use_gold_binary': 0, 'linux_use_gold_flags': 0}
|
||||
_DEFAULT_DEFINES = {'test_isolation_mode': 'noop',
|
||||
'use_glib': 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')
|
||||
else '')
|
||||
gyp_defines = (os.environ['GYP_DEFINES'] if os.environ.get('GYP_DEFINES') else
|
||||
'')
|
||||
for key in _DEFAULT_DEFINES:
|
||||
if key not in gyp_defines:
|
||||
gyp_defines += ' {0}={1}'.format(key, _DEFAULT_DEFINES[key])
|
||||
|
|
|
@ -11,6 +11,7 @@
|
|||
#include "packager/base/strings/string_split.h"
|
||||
#include "packager/base/strings/stringprintf.h"
|
||||
#include "packager/mpd/util/mpd_writer.h"
|
||||
#include "packager/version/version.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace {
|
||||
|
@ -88,7 +89,9 @@ int MpdMain(int argc, char** argv) {
|
|||
|
||||
ExitStatus status = CheckRequiredFlags();
|
||||
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;
|
||||
}
|
||||
|
||||
|
|
|
@ -36,6 +36,7 @@
|
|||
#include "packager/mpd/base/media_info.pb.h"
|
||||
#include "packager/mpd/base/mpd_builder.h"
|
||||
#include "packager/mpd/base/simple_mpd_notifier.h"
|
||||
#include "packager/version/version.h"
|
||||
|
||||
DEFINE_bool(use_fake_clock_for_muxer,
|
||||
false,
|
||||
|
@ -45,37 +46,36 @@ DEFINE_bool(use_fake_clock_for_muxer,
|
|||
|
||||
namespace {
|
||||
const char kUsage[] =
|
||||
"Packager driver program. Sample Usage:\n"
|
||||
"Packager driver program. Usage:\n\n"
|
||||
"%s [flags] <stream_descriptor> ...\n"
|
||||
"stream_descriptor consists of comma separated field_name/value pairs:\n"
|
||||
"field_name=value,[field_name=value,]...\n"
|
||||
"Supported field names are as follows:\n"
|
||||
" - input (in): Required input/source media file path or network stream "
|
||||
"URL.\n"
|
||||
" - stream_selector (stream): Required field with value 'audio', 'video', "
|
||||
"or stream number (zero based).\n"
|
||||
" - output (out): Required output file (single file) or initialization "
|
||||
"file path (multiple file).\n"
|
||||
" - segment_template (segment): Optional value which specifies the "
|
||||
"naming pattern for the segment files, and that the stream should be "
|
||||
"split into multiple files. Its presence should be consistent across "
|
||||
"streams.\n"
|
||||
" - bandwidth (bw): Optional value which contains a user-specified "
|
||||
"content bit rate for the stream, in bits/sec. If specified, this value is "
|
||||
"propagated to the $Bandwidth$ template parameter for segment names. "
|
||||
"If not specified, its value may be estimated.\n"
|
||||
" - language (lang): Optional value which contains a user-specified "
|
||||
"language tag. If specified, this value overrides any language metadata "
|
||||
"in the input track.\n"
|
||||
" - output_format (format): Optional value which specifies the format "
|
||||
"of the output files (MP4 or WebM). If not specified, it will be "
|
||||
"derived from the file extension of the output file.\n";
|
||||
" - input (in): Required input/source media file path or network stream\n"
|
||||
" URL.\n"
|
||||
" - stream_selector (stream): Required field with value 'audio',\n"
|
||||
" 'video', or stream number (zero based).\n"
|
||||
" - output (out): Required output file (single file) or initialization\n"
|
||||
" file path (multiple file).\n"
|
||||
" - segment_template (segment): Optional value which specifies the\n"
|
||||
" naming pattern for the segment files, and that the stream should be\n"
|
||||
" split into multiple files. Its presence should be consistent across\n"
|
||||
" streams.\n"
|
||||
" - bandwidth (bw): Optional value which contains a user-specified\n"
|
||||
" content bit rate for the stream, in bits/sec. If specified, this\n"
|
||||
" value is propagated to the $Bandwidth$ template parameter for\n"
|
||||
" segment names. If not specified, its value may be estimated.\n"
|
||||
" - language (lang): Optional value which contains a user-specified\n"
|
||||
" language tag. If specified, this value overrides any language\n"
|
||||
" metadata in the input track.\n"
|
||||
" - output_format (format): Optional value which specifies the format\n"
|
||||
" of the output files (MP4 or WebM). If not specified, it will be\n"
|
||||
" derived from the file extension of the output file.\n";
|
||||
|
||||
const char kMediaInfoSuffix[] = ".media_info";
|
||||
|
||||
enum ExitStatus {
|
||||
kSuccess = 0,
|
||||
kNoArgument,
|
||||
kArgumentValidationFailed,
|
||||
kPackagingFailed,
|
||||
kInternalError,
|
||||
|
@ -441,8 +441,10 @@ int PackagerMain(int argc, char** argv) {
|
|||
google::SetUsageMessage(base::StringPrintf(kUsage, argv[0]));
|
||||
google::ParseCommandLineFlags(&argc, &argv, true);
|
||||
if (argc < 2) {
|
||||
google::ShowUsageWithFlags(argv[0]);
|
||||
return kNoArgument;
|
||||
std::string version_string =
|
||||
base::StringPrintf("edash-packager version %s", kPackagerVersion);
|
||||
google::ShowUsageWithFlags(version_string.c_str());
|
||||
return kSuccess;
|
||||
}
|
||||
|
||||
if (!ValidateWidevineCryptoFlags() || !ValidateFixedCryptoFlags())
|
||||
|
|
|
@ -25,6 +25,16 @@
|
|||
#include "packager/mpd/base/mpd_builder.h"
|
||||
|
||||
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 media {
|
||||
|
@ -146,6 +156,8 @@ bool GetMuxerOptions(MuxerOptions* muxer_options) {
|
|||
muxer_options->fragment_sap_aligned = FLAGS_fragment_sap_aligned;
|
||||
muxer_options->num_subsegments_per_sidx = FLAGS_num_subsegments_per_sidx;
|
||||
muxer_options->temp_dir = FLAGS_temp_dir;
|
||||
if (FLAGS_override_version_string)
|
||||
muxer_options->packager_version_string = FLAGS_test_version_string;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
@ -158,6 +170,8 @@ bool GetMpdOptions(MpdOptions* mpd_options) {
|
|||
mpd_options->time_shift_buffer_depth = FLAGS_time_shift_buffer_depth;
|
||||
mpd_options->suggested_presentation_delay =
|
||||
FLAGS_suggested_presentation_delay;
|
||||
if (FLAGS_override_version_string)
|
||||
mpd_options->packager_version_string = FLAGS_test_version_string;
|
||||
return true;
|
||||
}
|
||||
|
||||
|
|
|
@ -31,6 +31,13 @@ class PackagerApp(object):
|
|||
cmd = [self.binary, input_str, '--dump_stream_info']
|
||||
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):
|
||||
if flags is None:
|
||||
flags = []
|
||||
|
|
|
@ -38,6 +38,11 @@ class PackagerAppTest(unittest.TestCase):
|
|||
def testBuildingCode(self):
|
||||
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):
|
||||
test_file = os.path.join(self.test_data_dir, 'bear-640x360.mp4')
|
||||
stream_info = self.packager.DumpStreamInfo(test_file)
|
||||
|
@ -346,6 +351,10 @@ class PackagerAppTest(unittest.TestCase):
|
|||
# Use fake clock, so output can be compared.
|
||||
if use_fake_clock:
|
||||
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
|
||||
|
||||
def _CompareWithGold(self, test_output, golden_file_name):
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
bandwidth: 128728
|
||||
bandwidth: 129127
|
||||
audio_info {
|
||||
codec: "mp4a.40.2"
|
||||
sampling_frequency: 44100
|
||||
|
@ -8,11 +8,11 @@ audio_info {
|
|||
}
|
||||
init_range {
|
||||
begin: 0
|
||||
end: 816
|
||||
end: 954
|
||||
}
|
||||
index_range {
|
||||
begin: 817
|
||||
end: 884
|
||||
begin: 955
|
||||
end: 1022
|
||||
}
|
||||
media_file_name: "place_holder"
|
||||
media_duration_seconds: 2.7631745
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,28 +1,29 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<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 schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<BaseURL>output_video.mp4</BaseURL>
|
||||
<SegmentBase indexRange="941-1008" timescale="30000">
|
||||
<Initialization range="0-940"/>
|
||||
<SegmentBase indexRange="1079-1146" timescale="30000">
|
||||
<Initialization range="0-1078"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<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"/>
|
||||
<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">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<BaseURL>output_audio.mp4</BaseURL>
|
||||
<SegmentBase indexRange="817-884" timescale="44100">
|
||||
<Initialization range="0-816"/>
|
||||
<SegmentBase indexRange="955-1022" timescale="44100">
|
||||
<Initialization range="0-954"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<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">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</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>
|
||||
<SegmentBase indexRange="941-1008" timescale="30000">
|
||||
<Initialization range="0-940"/>
|
||||
<SegmentBase indexRange="1079-1146" timescale="30000">
|
||||
<Initialization range="0-1078"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
@ -18,11 +19,11 @@
|
|||
<ContentProtection schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</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"/>
|
||||
<BaseURL>output_audio.mp4</BaseURL>
|
||||
<SegmentBase indexRange="817-884" timescale="44100">
|
||||
<Initialization range="0-816"/>
|
||||
<SegmentBase indexRange="955-1022" timescale="44100">
|
||||
<Initialization range="0-954"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
|
|
@ -1,20 +1,21 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<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>
|
||||
<SegmentBase indexRange="677-744" timescale="30000">
|
||||
<Initialization range="0-676"/>
|
||||
<SegmentBase indexRange="815-882" timescale="30000">
|
||||
<Initialization range="0-814"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<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"/>
|
||||
<BaseURL>output_audio.mp4</BaseURL>
|
||||
<SegmentBase indexRange="611-678" timescale="44100">
|
||||
<Initialization range="0-610"/>
|
||||
<SegmentBase indexRange="749-816" timescale="44100">
|
||||
<Initialization range="0-748"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period start="PT0S">
|
||||
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" segmentAlignment="true" par="16:9">
|
||||
|
|
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<AdaptationSet id="0" contentType="text">
|
||||
|
@ -7,19 +8,19 @@
|
|||
</Representation>
|
||||
</AdaptationSet>
|
||||
<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>
|
||||
<SegmentBase indexRange="677-744" timescale="30000">
|
||||
<Initialization range="0-676"/>
|
||||
<SegmentBase indexRange="815-882" timescale="30000">
|
||||
<Initialization range="0-814"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
<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"/>
|
||||
<BaseURL>output_audio.mp4</BaseURL>
|
||||
<SegmentBase indexRange="611-678" timescale="44100">
|
||||
<Initialization range="0-610"/>
|
||||
<SegmentBase indexRange="749-816" timescale="44100">
|
||||
<Initialization range="0-748"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
|
Binary file not shown.
|
@ -1,15 +1,16 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<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 schemeIdUri="urn:uuid:edef8ba9-79d6-4ace-a3c8-27dcd51d21ed">
|
||||
<cenc:pssh>AAAAMHBzc2gAAAAA7e+LqXnWSs6jyCfc1R0h7QAAABAxMjM0NTY3ODkwMTIzNDU2</cenc:pssh>
|
||||
</ContentProtection>
|
||||
<BaseURL>output_video.mp4</BaseURL>
|
||||
<SegmentBase indexRange="2721-2764" timescale="30000">
|
||||
<Initialization range="0-2720"/>
|
||||
<SegmentBase indexRange="2859-2902" timescale="30000">
|
||||
<Initialization range="0-2858"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
|
Binary file not shown.
|
@ -1,4 +1,4 @@
|
|||
bandwidth: 885151
|
||||
bandwidth: 885555
|
||||
video_info {
|
||||
codec: "avc1.64001e"
|
||||
width: 640
|
||||
|
@ -11,11 +11,11 @@ video_info {
|
|||
}
|
||||
init_range {
|
||||
begin: 0
|
||||
end: 940
|
||||
end: 1078
|
||||
}
|
||||
index_range {
|
||||
begin: 941
|
||||
end: 1008
|
||||
begin: 1079
|
||||
end: 1146
|
||||
}
|
||||
media_file_name: "place_holder"
|
||||
media_duration_seconds: 2.7360666
|
||||
|
|
Binary file not shown.
|
@ -1,11 +1,12 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<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>
|
||||
<SegmentBase indexRange="677-744" timescale="30000">
|
||||
<Initialization range="0-676"/>
|
||||
<SegmentBase indexRange="815-882" timescale="30000">
|
||||
<Initialization range="0-814"/>
|
||||
</SegmentBase>
|
||||
</Representation>
|
||||
</AdaptationSet>
|
||||
|
|
Binary file not shown.
Binary file not shown.
Binary file not shown.
|
@ -1,4 +1,5 @@
|
|||
<?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">
|
||||
<Period>
|
||||
<AdaptationSet id="0" contentType="text">
|
||||
|
|
|
@ -84,6 +84,7 @@
|
|||
'../../third_party/boringssl/boringssl.gyp:boringssl',
|
||||
'../../third_party/curl/curl.gyp:libcurl',
|
||||
'../../third_party/libxml/libxml.gyp:libxml',
|
||||
'../../version/version.gyp:version',
|
||||
],
|
||||
},
|
||||
{
|
||||
|
|
|
@ -5,6 +5,7 @@
|
|||
// https://developers.google.com/open-source/licenses/bsd
|
||||
|
||||
#include "packager/media/base/muxer_options.h"
|
||||
#include "packager/version/version.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -16,7 +17,8 @@ MuxerOptions::MuxerOptions()
|
|||
segment_sap_aligned(false),
|
||||
fragment_sap_aligned(false),
|
||||
num_subsegments_per_sidx(0),
|
||||
bandwidth(0) {}
|
||||
bandwidth(0),
|
||||
packager_version_string(kPackagerVersion) {}
|
||||
MuxerOptions::~MuxerOptions() {}
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -66,6 +66,9 @@ struct MuxerOptions {
|
|||
/// User-specified bit rate for the media stream. If zero, the muxer will
|
||||
/// attempt to estimate.
|
||||
uint32_t bandwidth;
|
||||
|
||||
/// Specify the version string to be embedded in the output files.
|
||||
std::string packager_version_string;
|
||||
};
|
||||
|
||||
} // namespace media
|
||||
|
|
|
@ -63,6 +63,11 @@ bool IsIvSizeValid(size_t iv_size) {
|
|||
// bit(5) Reserved // 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.
|
||||
bool IsFitIn32Bits(uint64_t a) {
|
||||
return a <= std::numeric_limits<uint32_t>::max();
|
||||
|
@ -89,6 +94,36 @@ namespace edash_packager {
|
|||
namespace media {
|
||||
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() {}
|
||||
FourCC FileType::BoxType() const { return FOURCC_FTYP; }
|
||||
|
@ -967,44 +1002,37 @@ uint32_t Edit::ComputeSizeInternal() {
|
|||
return HeaderSize() + list.ComputeSize();
|
||||
}
|
||||
|
||||
HandlerReference::HandlerReference() : type(kInvalid) {}
|
||||
HandlerReference::HandlerReference() : handler_type(FOURCC_NULL) {}
|
||||
HandlerReference::~HandlerReference() {}
|
||||
FourCC HandlerReference::BoxType() const { return FOURCC_HDLR; }
|
||||
|
||||
bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) {
|
||||
FourCC hdlr_type = FOURCC_NULL;
|
||||
std::vector<uint8_t> handler_name;
|
||||
if (!buffer->Reading()) {
|
||||
if (type == kVideo) {
|
||||
hdlr_type = FOURCC_VIDE;
|
||||
switch (handler_type) {
|
||||
case FOURCC_VIDE:
|
||||
handler_name.assign(kVideoHandlerName,
|
||||
kVideoHandlerName + arraysize(kVideoHandlerName));
|
||||
} else if (type == kAudio) {
|
||||
hdlr_type = FOURCC_SOUN;
|
||||
break;
|
||||
case FOURCC_SOUN:
|
||||
handler_name.assign(kAudioHandlerName,
|
||||
kAudioHandlerName + arraysize(kAudioHandlerName));
|
||||
} else if (type == kText) {
|
||||
hdlr_type = FOURCC_TEXT;
|
||||
break;
|
||||
case FOURCC_TEXT:
|
||||
handler_name.assign(kTextHandlerName,
|
||||
kTextHandlerName + arraysize(kTextHandlerName));
|
||||
} else {
|
||||
break;
|
||||
case FOURCC_ID32:
|
||||
break;
|
||||
default:
|
||||
NOTIMPLEMENTED();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->IgnoreBytes(4) && // predefined.
|
||||
buffer->ReadWriteFourCC(&hdlr_type));
|
||||
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 {
|
||||
buffer->ReadWriteFourCC(&handler_type));
|
||||
if (!buffer->Reading()) {
|
||||
RCHECK(buffer->IgnoreBytes(12) && // reserved.
|
||||
buffer->ReadWriteVector(&handler_name, handler_name.size()));
|
||||
}
|
||||
|
@ -1013,16 +1041,161 @@ bool HandlerReference::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
|
||||
uint32_t HandlerReference::ComputeSizeInternal() {
|
||||
uint32_t box_size = HeaderSize() + kFourCCSize + 16; // 16 bytes Reserved
|
||||
if (type == kVideo) {
|
||||
switch (handler_type) {
|
||||
case FOURCC_VIDE:
|
||||
box_size += sizeof(kVideoHandlerName);
|
||||
} else if (type == kAudio) {
|
||||
break;
|
||||
case FOURCC_SOUN:
|
||||
box_size += sizeof(kAudioHandlerName);
|
||||
} else {
|
||||
break;
|
||||
case FOURCC_TEXT:
|
||||
box_size += sizeof(kTextHandlerName);
|
||||
break;
|
||||
case FOURCC_ID32:
|
||||
break;
|
||||
default:
|
||||
NOTIMPLEMENTED();
|
||||
}
|
||||
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() {}
|
||||
FourCC CodecConfigurationRecord::BoxType() const {
|
||||
|
@ -1369,9 +1542,7 @@ uint32_t WVTTSampleEntry::ComputeSizeInternal() {
|
|||
}
|
||||
|
||||
MediaHeader::MediaHeader()
|
||||
: creation_time(0), modification_time(0), timescale(0), duration(0) {
|
||||
language[0] = 0;
|
||||
}
|
||||
: creation_time(0), modification_time(0), timescale(0), duration(0) {}
|
||||
MediaHeader::~MediaHeader() {}
|
||||
FourCC MediaHeader::BoxType() const { return FOURCC_MDHD; }
|
||||
|
||||
|
@ -1382,42 +1553,16 @@ bool MediaHeader::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
RCHECK(buffer->ReadWriteUInt64NBytes(&creation_time, num_bytes) &&
|
||||
buffer->ReadWriteUInt64NBytes(&modification_time, num_bytes) &&
|
||||
buffer->ReadWriteUInt32(×cale) &&
|
||||
buffer->ReadWriteUInt64NBytes(&duration, num_bytes));
|
||||
|
||||
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);
|
||||
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.
|
||||
buffer->ReadWriteUInt64NBytes(&duration, num_bytes) &&
|
||||
language.ReadWrite(buffer) &&
|
||||
buffer->IgnoreBytes(2)); // predefined.
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t MediaHeader::ComputeSizeInternal() {
|
||||
version = IsFitIn32Bits(creation_time, modification_time, duration) ? 0 : 1;
|
||||
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.
|
||||
}
|
||||
|
||||
|
@ -1580,24 +1725,30 @@ FourCC Media::BoxType() const { return FOURCC_MDIA; }
|
|||
bool Media::ReadWriteInternal(BoxBuffer* buffer) {
|
||||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->PrepareChildren() &&
|
||||
buffer->ReadWriteChild(&header) &&
|
||||
buffer->ReadWriteChild(&handler));
|
||||
buffer->ReadWriteChild(&header));
|
||||
if (buffer->Reading()) {
|
||||
RCHECK(buffer->ReadWriteChild(&handler));
|
||||
// Maddeningly, the HandlerReference box specifies how to parse the
|
||||
// SampleDescription box, making the latter the only box (of those that we
|
||||
// support) which cannot be parsed correctly on its own (or even with
|
||||
// information from its strict ancestor tree). We thus copy the handler type
|
||||
// to the sample description box *before* parsing it to provide this
|
||||
// information while parsing.
|
||||
information.sample_table.description.type = handler.type;
|
||||
information.sample_table.description.type =
|
||||
FourCCToTrackType(handler.handler_type);
|
||||
} 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));
|
||||
return true;
|
||||
}
|
||||
|
||||
uint32_t Media::ComputeSizeInternal() {
|
||||
handler.handler_type =
|
||||
TrackTypeToFourCC(information.sample_table.description.type);
|
||||
return HeaderSize() + header.ComputeSize() + handler.ComputeSize() +
|
||||
information.ComputeSize();
|
||||
}
|
||||
|
@ -1702,6 +1853,7 @@ bool Movie::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
RCHECK(ReadWriteHeaderInternal(buffer) &&
|
||||
buffer->PrepareChildren() &&
|
||||
buffer->ReadWriteChild(&header) &&
|
||||
buffer->TryReadWriteChild(&metadata) &&
|
||||
buffer->TryReadWriteChild(&extends));
|
||||
if (buffer->Reading()) {
|
||||
BoxReader* reader = buffer->reader();
|
||||
|
@ -1718,8 +1870,8 @@ bool Movie::ReadWriteInternal(BoxBuffer* buffer) {
|
|||
}
|
||||
|
||||
uint32_t Movie::ComputeSizeInternal() {
|
||||
uint32_t box_size =
|
||||
HeaderSize() + header.ComputeSize() + extends.ComputeSize();
|
||||
uint32_t box_size = HeaderSize() + header.ComputeSize() +
|
||||
metadata.ComputeSize() + extends.ComputeSize();
|
||||
for (uint32_t i = 0; i < tracks.size(); ++i)
|
||||
box_size += tracks[i].ComputeSize();
|
||||
for (uint32_t i = 0; i < pssh.size(); ++i)
|
||||
|
|
|
@ -222,7 +222,41 @@ struct Edit : Box {
|
|||
struct HandlerReference : FullBox {
|
||||
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 {
|
||||
|
@ -422,8 +456,7 @@ struct MediaHeader : FullBox {
|
|||
uint64_t modification_time;
|
||||
uint32_t timescale;
|
||||
uint64_t duration;
|
||||
// 3-char language code + 1 null terminating char.
|
||||
char language[4];
|
||||
Language language;
|
||||
};
|
||||
|
||||
struct VideoMediaHeader : FullBox {
|
||||
|
@ -519,6 +552,7 @@ struct Movie : Box {
|
|||
DECLARE_BOX_METHODS(Movie);
|
||||
|
||||
MovieHeader header;
|
||||
Metadata metadata; // Used to hold version information.
|
||||
MovieExtends extends;
|
||||
std::vector<Track> tracks;
|
||||
std::vector<ProtectionSystemSpecificHeader> pssh;
|
||||
|
|
|
@ -180,7 +180,24 @@ inline bool operator==(const Edit& lhs, const Edit& rhs) {
|
|||
|
||||
inline bool operator==(const HandlerReference& lhs,
|
||||
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,
|
||||
|
@ -252,7 +269,7 @@ inline bool operator==(const MediaHeader& lhs, const MediaHeader& rhs) {
|
|||
return lhs.creation_time == rhs.creation_time &&
|
||||
lhs.modification_time == rhs.modification_time &&
|
||||
lhs.timescale == rhs.timescale && lhs.duration == rhs.duration &&
|
||||
strcmp(lhs.language, rhs.language) == 0;
|
||||
lhs.language == rhs.language;
|
||||
}
|
||||
|
||||
inline bool operator==(const VideoMediaHeader& lhs,
|
||||
|
|
|
@ -286,11 +286,29 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
|
||||
void Modify(Edit* edts) { Modify(&edts->list); }
|
||||
|
||||
void Fill(HandlerReference* hdlr) {
|
||||
hdlr->type = kSampleDescriptionTrackType;
|
||||
void Fill(HandlerReference* hdlr) { hdlr->handler_type = FOURCC_VIDE; }
|
||||
|
||||
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) {
|
||||
pasp->h_spacing = 5;
|
||||
|
@ -517,14 +535,14 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
static_cast<uint64_t>(std::numeric_limits<uint32_t>::max()) + 1;
|
||||
mdhd->timescale = 50000;
|
||||
mdhd->duration = 250000;
|
||||
strcpy(mdhd->language, "abc");
|
||||
mdhd->language.code = "abc";
|
||||
mdhd->version = 1;
|
||||
}
|
||||
|
||||
void Modify(MediaHeader* mdhd) {
|
||||
mdhd->creation_time = 2;
|
||||
mdhd->modification_time = std::numeric_limits<uint32_t>::max();
|
||||
strcpy(mdhd->language, "und");
|
||||
mdhd->language.code = "und";
|
||||
mdhd->version = 0;
|
||||
}
|
||||
|
||||
|
@ -633,6 +651,7 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
|
||||
void Fill(Movie* moov) {
|
||||
Fill(&moov->header);
|
||||
Fill(&moov->metadata);
|
||||
Fill(&moov->extends);
|
||||
moov->tracks.resize(2);
|
||||
Fill(&moov->tracks[0]);
|
||||
|
@ -854,6 +873,8 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
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 SampleAuxiliaryInformationSize* box) { return true; }
|
||||
bool IsOptional(const SampleEncryption* box) { return true; }
|
||||
|
@ -881,8 +902,9 @@ class BoxDefinitionsTestGeneral : public testing::Test {
|
|||
scoped_ptr<BufferWriter> buffer_;
|
||||
};
|
||||
|
||||
typedef testing::Types<
|
||||
FileType,
|
||||
// GTEST support a maximum of 50 types in the template list, so we have to
|
||||
// break it into two groups.
|
||||
typedef testing::Types<FileType,
|
||||
SegmentType,
|
||||
ProtectionSystemSpecificHeader,
|
||||
SampleAuxiliaryInformationOffset,
|
||||
|
@ -897,10 +919,13 @@ typedef testing::Types<
|
|||
EditList,
|
||||
Edit,
|
||||
HandlerReference,
|
||||
ID3v2,
|
||||
Metadata,
|
||||
CodecConfigurationRecord,
|
||||
PixelAspectRatio,
|
||||
VideoSampleEntry,
|
||||
ElementaryStreamDescriptor,
|
||||
DTSSpecific,
|
||||
AudioSampleEntry,
|
||||
WebVTTConfigurationBox,
|
||||
WebVTTSourceLabelBox,
|
||||
|
@ -914,8 +939,9 @@ typedef testing::Types<
|
|||
ChunkLargeOffset,
|
||||
ChunkOffset,
|
||||
SyncSample,
|
||||
SampleTable,
|
||||
MediaHeader,
|
||||
SampleTable>
|
||||
Boxes;
|
||||
typedef testing::Types<MediaHeader,
|
||||
VideoMediaHeader,
|
||||
SoundMediaHeader,
|
||||
SubtitleMediaHeader,
|
||||
|
@ -931,11 +957,7 @@ typedef testing::Types<
|
|||
Movie,
|
||||
TrackFragmentDecodeTime,
|
||||
MovieFragmentHeader,
|
||||
TrackFragmentHeader> Boxes;
|
||||
|
||||
// GTEST support a maximum of 50 types in the template list, so we have to
|
||||
// break it into two groups.
|
||||
typedef testing::Types<
|
||||
TrackFragmentHeader,
|
||||
TrackFragmentRun,
|
||||
TrackFragment,
|
||||
MovieFragment,
|
||||
|
@ -949,8 +971,8 @@ typedef testing::Types<
|
|||
CuePayloadBox,
|
||||
VTTEmptyCueBox,
|
||||
VTTAdditionalTextBox,
|
||||
VTTCueBox,
|
||||
DTSSpecific> Boxes2;
|
||||
VTTCueBox>
|
||||
Boxes2;
|
||||
|
||||
TYPED_TEST_CASE_P(BoxDefinitionsTestGeneral);
|
||||
|
||||
|
@ -1021,6 +1043,20 @@ INSTANTIATE_TYPED_TEST_CASE_P(BoxDefinitionTypedTests2,
|
|||
// Test other cases of box input.
|
||||
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) {
|
||||
AudioSampleEntry entry;
|
||||
entry.format = FOURCC_DTSE;
|
||||
|
|
|
@ -14,6 +14,8 @@ namespace mp4 {
|
|||
// TODO(rkuroiwa): Make these case sensitive. e.g. FOURCC_avc1.
|
||||
enum FourCC {
|
||||
FOURCC_NULL = 0,
|
||||
FOURCC_ID32 = 0x49443332,
|
||||
FOURCC_PRIV = 0x50524956,
|
||||
FOURCC_AVC1 = 0x61766331,
|
||||
FOURCC_AVCC = 0x61766343,
|
||||
FOURCC_BLOC = 0x626C6F63,
|
||||
|
|
|
@ -327,7 +327,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
RCHECK(desc_idx > 0);
|
||||
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());
|
||||
|
||||
// It is not uncommon to find otherwise-valid files with incorrect sample
|
||||
|
@ -428,7 +428,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
duration,
|
||||
codec,
|
||||
AudioStreamInfo::GetCodecString(codec, audio_object_type),
|
||||
track->media.header.language,
|
||||
track->media.header.language.code,
|
||||
entry.samplesize,
|
||||
num_channels,
|
||||
sampling_frequency,
|
||||
|
@ -439,7 +439,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
is_encrypted));
|
||||
}
|
||||
|
||||
if (track->media.handler.type == kVideo) {
|
||||
if (samp_descr.type == kVideo) {
|
||||
RCHECK(!samp_descr.video_entries.empty());
|
||||
if (desc_idx >= samp_descr.video_entries.size())
|
||||
desc_idx = 0;
|
||||
|
@ -527,8 +527,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) {
|
|||
DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted;
|
||||
streams.push_back(new VideoStreamInfo(
|
||||
track->header.track_id, timescale, duration, video_codec,
|
||||
codec_string, track->media.header.language, coded_width, coded_height,
|
||||
pixel_width, pixel_height,
|
||||
codec_string, track->media.header.language.code, coded_width,
|
||||
coded_height, pixel_width, pixel_height,
|
||||
0, // trick_play_rate
|
||||
nalu_length_size, vector_as_array(&entry.codec_config_record.data),
|
||||
entry.codec_config_record.data.size(), is_encrypted));
|
||||
|
|
|
@ -187,13 +187,12 @@ void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) {
|
|||
trak->media.header.timescale = info->time_scale();
|
||||
trak->media.header.duration = 0;
|
||||
if (!info->language().empty()) {
|
||||
const size_t language_size = arraysize(trak->media.header.language) - 1;
|
||||
if (info->language().size() != language_size) {
|
||||
// ISO-639-2/T language code should be 3 characters..
|
||||
if (info->language().size() != 3) {
|
||||
LOG(WARNING) << "'" << info->language() << "' is not a valid ISO-639-2 "
|
||||
<< "language code, ignoring.";
|
||||
} else {
|
||||
memcpy(trak->media.header.language, info->language().c_str(),
|
||||
language_size + 1);
|
||||
trak->media.header.language.code = info->language();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -217,8 +216,6 @@ void MP4Muxer::GenerateVideoTrak(const VideoStreamInfo* video_info,
|
|||
trak->header.width = video_info->width() * sample_aspect_ratio * 0x10000;
|
||||
trak->header.height = video_info->height() * 0x10000;
|
||||
|
||||
trak->media.handler.type = kVideo;
|
||||
|
||||
VideoSampleEntry video;
|
||||
video.format = VideoCodecToFourCC(video_info->codec());
|
||||
video.width = video_info->width();
|
||||
|
@ -241,7 +238,6 @@ void MP4Muxer::GenerateAudioTrak(const AudioStreamInfo* audio_info,
|
|||
InitializeTrak(audio_info, trak);
|
||||
|
||||
trak->header.volume = 0x100;
|
||||
trak->media.handler.type = kAudio;
|
||||
|
||||
AudioSampleEntry audio;
|
||||
audio.format = AudioCodecToFourCC(audio_info->codec());
|
||||
|
|
|
@ -238,6 +238,13 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
|||
// Use the reference stream's time scale as movie time scale.
|
||||
moov_->header.timescale = sidx_->timescale;
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -13,6 +13,7 @@
|
|||
#include "packager/base/memory/ref_counted.h"
|
||||
#include "packager/base/memory/scoped_ptr.h"
|
||||
#include "packager/media/base/status.h"
|
||||
#include "packager/media/formats/mp4/box_definitions.h"
|
||||
|
||||
namespace edash_packager {
|
||||
namespace media {
|
||||
|
@ -30,11 +31,6 @@ namespace mp4 {
|
|||
|
||||
class Fragmenter;
|
||||
|
||||
struct FileType;
|
||||
struct Movie;
|
||||
struct MovieFragment;
|
||||
struct SegmentIndex;
|
||||
|
||||
/// This class defines the Segmenter which is responsible for organizing
|
||||
/// fragments into segments/subsegments and package them into a MP4 file.
|
||||
/// Inherited by MultiSegmentSegmenter and SingleSegmentSegmenter.
|
||||
|
|
|
@ -494,7 +494,13 @@ xmlDocPtr MpdBuilder::GenerateMpd() {
|
|||
}
|
||||
|
||||
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();
|
||||
}
|
||||
|
||||
|
|
|
@ -174,6 +174,8 @@ class DynamicMpdBuilderTest : public MpdBuilderTest<MpdBuilder::kDynamic> {
|
|||
// current time.
|
||||
void SetUp() override {
|
||||
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_; }
|
||||
|
@ -1776,6 +1778,8 @@ TEST_F(StaticMpdBuilderTest, Text) {
|
|||
TEST_F(DynamicMpdBuilderTest, CheckMpdAttributes) {
|
||||
static const char kExpectedOutput[] =
|
||||
"<?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\" "
|
||||
"xmlns:xsi=\"http://www.w3.org/2001/XMLSchema-instance\" "
|
||||
"xmlns:xlink=\"http://www.w3.org/1999/xlink\" "
|
||||
|
|
|
@ -7,6 +7,10 @@
|
|||
#ifndef MPD_BASE_MPD_OPTIONS_H_
|
||||
#define MPD_BASE_MPD_OPTIONS_H_
|
||||
|
||||
#include <string>
|
||||
|
||||
#include "packager/version/version.h"
|
||||
|
||||
namespace edash_packager {
|
||||
|
||||
/// Defines Mpd Options.
|
||||
|
@ -17,7 +21,8 @@ struct MpdOptions {
|
|||
// TODO(tinskip): Set min_buffer_time in unit tests rather than here.
|
||||
min_buffer_time(2.0),
|
||||
time_shift_buffer_depth(0),
|
||||
suggested_presentation_delay(0) {}
|
||||
suggested_presentation_delay(0),
|
||||
packager_version_string(kPackagerVersion) {}
|
||||
|
||||
~MpdOptions() {};
|
||||
|
||||
|
@ -26,6 +31,7 @@ struct MpdOptions {
|
|||
double min_buffer_time;
|
||||
double time_shift_buffer_depth;
|
||||
double suggested_presentation_delay;
|
||||
std::string packager_version_string;
|
||||
};
|
||||
|
||||
} // namespace edash_packager
|
||||
|
|
|
@ -60,6 +60,7 @@
|
|||
'../base/base.gyp:base',
|
||||
'../media/file/file.gyp:file',
|
||||
'../third_party/libxml/libxml.gyp:libxml',
|
||||
'../version/version.gyp:version',
|
||||
'media_info_proto',
|
||||
],
|
||||
'export_dependent_settings': [
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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',
|
||||
],
|
||||
},
|
||||
],
|
||||
}
|
|
@ -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
|
Loading…
Reference in New Issue