Implements Demuxer and Muxer media handlers
- Also sets up the packaging and verify it works. Some of the changes are temporary to get the integration going. Change-Id: I0cf6c379d185e157808acabb9ef58ff93d4a39ae
This commit is contained in:
parent
12c61d6792
commit
8f2cd6da91
|
@ -168,10 +168,6 @@ class RemuxJob : public base::SimpleThread {
|
||||||
|
|
||||||
~RemuxJob() override {}
|
~RemuxJob() override {}
|
||||||
|
|
||||||
void AddMuxer(std::unique_ptr<Muxer> mux) {
|
|
||||||
muxers_.push_back(std::move(mux));
|
|
||||||
}
|
|
||||||
|
|
||||||
Demuxer* demuxer() { return demuxer_.get(); }
|
Demuxer* demuxer() { return demuxer_.get(); }
|
||||||
Status status() { return status_; }
|
Status status() { return status_; }
|
||||||
|
|
||||||
|
@ -182,7 +178,6 @@ class RemuxJob : public base::SimpleThread {
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Demuxer> demuxer_;
|
std::unique_ptr<Demuxer> demuxer_;
|
||||||
std::vector<std::unique_ptr<Muxer>> muxers_;
|
|
||||||
Status status_;
|
Status status_;
|
||||||
|
|
||||||
DISALLOW_COPY_AND_ASSIGN(RemuxJob);
|
DISALLOW_COPY_AND_ASSIGN(RemuxJob);
|
||||||
|
@ -227,15 +222,15 @@ bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor,
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Muxer> CreateOutputMuxer(const MuxerOptions& options,
|
std::shared_ptr<Muxer> CreateOutputMuxer(const MuxerOptions& options,
|
||||||
MediaContainerName container) {
|
MediaContainerName container) {
|
||||||
if (container == CONTAINER_WEBM) {
|
if (container == CONTAINER_WEBM) {
|
||||||
return std::unique_ptr<Muxer>(new webm::WebMMuxer(options));
|
return std::shared_ptr<Muxer>(new webm::WebMMuxer(options));
|
||||||
} else if (container == CONTAINER_MPEG2TS) {
|
} else if (container == CONTAINER_MPEG2TS) {
|
||||||
return std::unique_ptr<Muxer>(new mp2t::TsMuxer(options));
|
return std::shared_ptr<Muxer>(new mp2t::TsMuxer(options));
|
||||||
} else {
|
} else {
|
||||||
DCHECK_EQ(container, CONTAINER_MOV);
|
DCHECK_EQ(container, CONTAINER_MOV);
|
||||||
return std::unique_ptr<Muxer>(new mp4::MP4Muxer(options));
|
return std::shared_ptr<Muxer>(new mp4::MP4Muxer(options));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -300,6 +295,7 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
|
||||||
if (stream_iter->input != previous_input) {
|
if (stream_iter->input != previous_input) {
|
||||||
// New remux job needed. Create demux and job thread.
|
// New remux job needed. Create demux and job thread.
|
||||||
std::unique_ptr<Demuxer> demuxer(new Demuxer(stream_iter->input));
|
std::unique_ptr<Demuxer> demuxer(new Demuxer(stream_iter->input));
|
||||||
|
demuxer->set_dump_stream_info(FLAGS_dump_stream_info);
|
||||||
if (FLAGS_enable_widevine_decryption ||
|
if (FLAGS_enable_widevine_decryption ||
|
||||||
FLAGS_enable_fixed_key_decryption) {
|
FLAGS_enable_fixed_key_decryption) {
|
||||||
std::unique_ptr<KeySource> key_source(CreateDecryptionKeySource());
|
std::unique_ptr<KeySource> key_source(CreateDecryptionKeySource());
|
||||||
|
@ -307,23 +303,15 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
|
||||||
return false;
|
return false;
|
||||||
demuxer->SetKeySource(std::move(key_source));
|
demuxer->SetKeySource(std::move(key_source));
|
||||||
}
|
}
|
||||||
Status status = demuxer->Initialize();
|
|
||||||
if (!status.ok()) {
|
|
||||||
LOG(ERROR) << "Demuxer failed to initialize: " << status.ToString();
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
if (FLAGS_dump_stream_info) {
|
|
||||||
printf("\nFile \"%s\":\n", stream_iter->input.c_str());
|
|
||||||
DumpStreamInfo(demuxer->streams());
|
|
||||||
if (stream_iter->output.empty())
|
|
||||||
continue; // just need stream info.
|
|
||||||
}
|
|
||||||
remux_jobs->emplace_back(new RemuxJob(std::move(demuxer)));
|
remux_jobs->emplace_back(new RemuxJob(std::move(demuxer)));
|
||||||
previous_input = stream_iter->input;
|
previous_input = stream_iter->input;
|
||||||
|
// Skip setting up muxers if output is not needed.
|
||||||
|
if (stream_iter->output.empty())
|
||||||
|
continue;
|
||||||
}
|
}
|
||||||
DCHECK(!remux_jobs->empty());
|
DCHECK(!remux_jobs->empty());
|
||||||
|
|
||||||
std::unique_ptr<Muxer> muxer(
|
std::shared_ptr<Muxer> muxer(
|
||||||
CreateOutputMuxer(stream_muxer_options, stream_iter->output_format));
|
CreateOutputMuxer(stream_muxer_options, stream_iter->output_format));
|
||||||
if (FLAGS_use_fake_clock_for_muxer) muxer->set_clock(fake_clock);
|
if (FLAGS_use_fake_clock_for_muxer) muxer->set_clock(fake_clock);
|
||||||
|
|
||||||
|
@ -373,15 +361,25 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors,
|
||||||
if (muxer_listener)
|
if (muxer_listener)
|
||||||
muxer->SetMuxerListener(std::move(muxer_listener));
|
muxer->SetMuxerListener(std::move(muxer_listener));
|
||||||
|
|
||||||
if (!AddStreamToMuxer(remux_jobs->back()->demuxer()->streams(),
|
auto* demuxer = remux_jobs->back()->demuxer();
|
||||||
stream_iter->stream_selector,
|
const std::string& stream_selector = stream_iter->stream_selector;
|
||||||
stream_iter->language,
|
Status status = demuxer->SetHandler(stream_selector, std::move(muxer));
|
||||||
muxer.get())) {
|
if (!status.ok()) {
|
||||||
|
LOG(ERROR) << "Demuxer::SetHandler failed " << status;
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
remux_jobs->back()->AddMuxer(std::move(muxer));
|
if (!stream_iter->language.empty())
|
||||||
|
demuxer->SetLanguageOverride(stream_selector, stream_iter->language);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
// Initialize processing graph.
|
||||||
|
for (const std::unique_ptr<RemuxJob>& job : *remux_jobs) {
|
||||||
|
Status status = job->demuxer()->Initialize();
|
||||||
|
if (!status.ok()) {
|
||||||
|
LOG(ERROR) << "Failed to initialize processing graph " << status;
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -17,15 +17,12 @@
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/media/base/fixed_key_source.h"
|
#include "packager/media/base/fixed_key_source.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/playready_key_source.h"
|
#include "packager/media/base/playready_key_source.h"
|
||||||
#include "packager/media/base/request_signer.h"
|
#include "packager/media/base/request_signer.h"
|
||||||
#include "packager/media/base/stream_info.h"
|
|
||||||
#include "packager/media/base/widevine_key_source.h"
|
#include "packager/media/base/widevine_key_source.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/mpd/base/mpd_builder.h"
|
#include "packager/mpd/base/mpd_options.h"
|
||||||
|
|
||||||
DEFINE_bool(mp4_use_decoding_timestamp_in_timeline,
|
DEFINE_bool(mp4_use_decoding_timestamp_in_timeline,
|
||||||
false,
|
false,
|
||||||
|
@ -38,12 +35,6 @@ DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info.");
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
void DumpStreamInfo(const std::vector<std::unique_ptr<MediaStream>>& streams) {
|
|
||||||
printf("Found %zu stream(s).\n", streams.size());
|
|
||||||
for (size_t i = 0; i < streams.size(); ++i)
|
|
||||||
printf("Stream [%zu] %s\n", i, streams[i]->info()->ToString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
std::unique_ptr<RequestSigner> CreateSigner() {
|
std::unique_ptr<RequestSigner> CreateSigner() {
|
||||||
std::unique_ptr<RequestSigner> signer;
|
std::unique_ptr<RequestSigner> signer;
|
||||||
|
|
||||||
|
@ -196,63 +187,5 @@ bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options) {
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
MediaStream* FindFirstStreamOfType(
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams,
|
|
||||||
StreamType stream_type) {
|
|
||||||
for (const std::unique_ptr<MediaStream>& stream : streams) {
|
|
||||||
if (stream->info()->stream_type() == stream_type)
|
|
||||||
return stream.get();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
MediaStream* FindFirstVideoStream(
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams) {
|
|
||||||
return FindFirstStreamOfType(streams, kStreamVideo);
|
|
||||||
}
|
|
||||||
MediaStream* FindFirstAudioStream(
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams) {
|
|
||||||
return FindFirstStreamOfType(streams, kStreamAudio);
|
|
||||||
}
|
|
||||||
|
|
||||||
bool AddStreamToMuxer(const std::vector<std::unique_ptr<MediaStream>>& streams,
|
|
||||||
const std::string& stream_selector,
|
|
||||||
const std::string& language_override,
|
|
||||||
Muxer* muxer) {
|
|
||||||
DCHECK(muxer);
|
|
||||||
|
|
||||||
MediaStream* stream = nullptr;
|
|
||||||
if (stream_selector == "video") {
|
|
||||||
stream = FindFirstVideoStream(streams);
|
|
||||||
} else if (stream_selector == "audio") {
|
|
||||||
stream = FindFirstAudioStream(streams);
|
|
||||||
} else {
|
|
||||||
// Expect stream_selector to be a zero based stream id.
|
|
||||||
size_t stream_id;
|
|
||||||
if (!base::StringToSizeT(stream_selector, &stream_id) ||
|
|
||||||
stream_id >= streams.size()) {
|
|
||||||
LOG(ERROR) << "Invalid argument --stream=" << stream_selector << "; "
|
|
||||||
<< "should be 'audio', 'video', or a number within [0, "
|
|
||||||
<< streams.size() - 1 << "].";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
stream = streams[stream_id].get();
|
|
||||||
DCHECK(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
// This could occur only if stream_selector=audio|video and the corresponding
|
|
||||||
// stream does not exist in the input.
|
|
||||||
if (!stream) {
|
|
||||||
LOG(ERROR) << "No " << stream_selector << " stream found in the input.";
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!language_override.empty()) {
|
|
||||||
stream->info()->set_language(language_override);
|
|
||||||
}
|
|
||||||
|
|
||||||
muxer->AddStream(stream);
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -6,13 +6,12 @@
|
||||||
//
|
//
|
||||||
// Packager utility functions.
|
// Packager utility functions.
|
||||||
|
|
||||||
#ifndef APP_PACKAGER_UTIL_H_
|
#ifndef PACKAGER_APP_PACKAGER_UTIL_H_
|
||||||
#define APP_PACKAGER_UTIL_H_
|
#define PACKAGER_APP_PACKAGER_UTIL_H_
|
||||||
|
|
||||||
#include <gflags/gflags.h>
|
#include <gflags/gflags.h>
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <string>
|
|
||||||
#include <vector>
|
|
||||||
|
|
||||||
DECLARE_bool(dump_stream_info);
|
DECLARE_bool(dump_stream_info);
|
||||||
|
|
||||||
|
@ -23,13 +22,8 @@ struct MpdOptions;
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
class KeySource;
|
class KeySource;
|
||||||
class MediaStream;
|
|
||||||
class Muxer;
|
|
||||||
struct MuxerOptions;
|
struct MuxerOptions;
|
||||||
|
|
||||||
/// Print all the stream info for the provided strings to standard output.
|
|
||||||
void DumpStreamInfo(const std::vector<std::unique_ptr<MediaStream>>& streams);
|
|
||||||
|
|
||||||
/// Create KeySource based on provided command line options for content
|
/// Create KeySource based on provided command line options for content
|
||||||
/// encryption. Also fetches keys.
|
/// encryption. Also fetches keys.
|
||||||
/// @return A std::unique_ptr containing a new KeySource, or nullptr if
|
/// @return A std::unique_ptr containing a new KeySource, or nullptr if
|
||||||
|
@ -48,21 +42,7 @@ bool GetMuxerOptions(MuxerOptions* muxer_options);
|
||||||
/// Fill MpdOptions members using provided command line options.
|
/// Fill MpdOptions members using provided command line options.
|
||||||
bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options);
|
bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options);
|
||||||
|
|
||||||
/// Select and add a stream from a provided set to a muxer.
|
|
||||||
/// @param streams contains the set of MediaStreams from which to select.
|
|
||||||
/// @param stream_selector is a string containing one of the following values:
|
|
||||||
/// "audio" to select the first audio track, "video" to select the first
|
|
||||||
/// video track, or a decimal number indicating which track number to
|
|
||||||
/// select (start at "1").
|
|
||||||
/// @param language_override is a string which, if non-empty, overrides the
|
|
||||||
/// stream's language metadata.
|
|
||||||
/// @return true if successful, false otherwise.
|
|
||||||
bool AddStreamToMuxer(const std::vector<std::unique_ptr<MediaStream>>& streams,
|
|
||||||
const std::string& stream_selector,
|
|
||||||
const std::string& language_override,
|
|
||||||
Muxer* muxer);
|
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
||||||
#endif // APP_PACKAGER_UTIL_H_
|
#endif // PACKAGER_APP_PACKAGER_UTIL_H_
|
||||||
|
|
|
@ -92,6 +92,22 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
|
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
|
||||||
self._DiffGold(self.mpd_output, 'bear-640x360-av-golden.mpd')
|
self._DiffGold(self.mpd_output, 'bear-640x360-av-golden.mpd')
|
||||||
|
|
||||||
|
def testPackageAudioVideoWithLanguageOverride(self):
|
||||||
|
self.packager.Package(
|
||||||
|
self._GetStreams(['audio', 'video'], language_override='por-BR'),
|
||||||
|
self._GetFlags())
|
||||||
|
self._DiffGold(self.output[0], 'bear-640x360-a-por-golden.mp4')
|
||||||
|
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
|
||||||
|
self._DiffGold(self.mpd_output, 'bear-640x360-av-por-golden.mpd')
|
||||||
|
|
||||||
|
def testPackageAudioVideoWithLanguageOverrideWithSubtag(self):
|
||||||
|
self.packager.Package(
|
||||||
|
self._GetStreams(['audio', 'video'], language_override='por-BR'),
|
||||||
|
self._GetFlags())
|
||||||
|
self._DiffGold(self.output[0], 'bear-640x360-a-por-BR-golden.mp4')
|
||||||
|
self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4')
|
||||||
|
self._DiffGold(self.mpd_output, 'bear-640x360-av-por-BR-golden.mpd')
|
||||||
|
|
||||||
# Package all video, audio, and text.
|
# Package all video, audio, and text.
|
||||||
def testPackageVideoAudioText(self):
|
def testPackageVideoAudioText(self):
|
||||||
audio_video_streams = self._GetStreams(['audio', 'video'])
|
audio_video_streams = self._GetStreams(['audio', 'video'])
|
||||||
|
@ -438,6 +454,7 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
|
|
||||||
def _GetStreams(self,
|
def _GetStreams(self,
|
||||||
stream_descriptors,
|
stream_descriptors,
|
||||||
|
language_override=None,
|
||||||
output_format=None,
|
output_format=None,
|
||||||
live=False,
|
live=False,
|
||||||
test_files=None):
|
test_files=None):
|
||||||
|
@ -466,9 +483,6 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
'input=%s,stream=%s,init_segment=%s-init.mp4,'
|
'input=%s,stream=%s,init_segment=%s-init.mp4,'
|
||||||
'segment_template=%s-$Number$.m4s' %
|
'segment_template=%s-$Number$.m4s' %
|
||||||
(test_file, stream_descriptor, output_prefix, output_prefix))
|
(test_file, stream_descriptor, output_prefix, output_prefix))
|
||||||
if output_format:
|
|
||||||
stream += ',format=%s' % output_format
|
|
||||||
streams.append(stream)
|
|
||||||
self.output.append(output_prefix)
|
self.output.append(output_prefix)
|
||||||
else:
|
else:
|
||||||
output = '%s.%s' % (
|
output = '%s.%s' % (
|
||||||
|
@ -476,10 +490,12 @@ class PackagerAppTest(unittest.TestCase):
|
||||||
self._GetExtension(stream_descriptor, output_format))
|
self._GetExtension(stream_descriptor, output_format))
|
||||||
stream = ('input=%s,stream=%s,output=%s' %
|
stream = ('input=%s,stream=%s,output=%s' %
|
||||||
(test_file, stream_descriptor, output))
|
(test_file, stream_descriptor, output))
|
||||||
|
self.output.append(output)
|
||||||
if output_format:
|
if output_format:
|
||||||
stream += ',format=%s' % output_format
|
stream += ',format=%s' % output_format
|
||||||
|
if language_override:
|
||||||
|
stream += ',lang=%s' % language_override
|
||||||
streams.append(stream)
|
streams.append(stream)
|
||||||
self.output.append(output)
|
|
||||||
return streams
|
return streams
|
||||||
|
|
||||||
def _GetExtension(self, stream_descriptor, output_format):
|
def _GetExtension(self, stream_descriptor, output_format):
|
||||||
|
|
Binary file not shown.
Binary file not shown.
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generated with https://github.com/google/shaka-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" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.76317S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
|
||||||
|
<Representation id="0" bandwidth="882040" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>output_video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="815-882" timescale="30000">
|
||||||
|
<Initialization range="0-814"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" contentType="audio" lang="pt-BR" subsegmentAlignment="true">
|
||||||
|
<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="749-816" timescale="44100">
|
||||||
|
<Initialization range="0-748"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -0,0 +1,23 @@
|
||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<!--Generated with https://github.com/google/shaka-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" profiles="urn:mpeg:dash:profile:isoff-on-demand:2011" minBufferTime="PT2S" type="static" mediaPresentationDuration="PT2.76317S">
|
||||||
|
<Period id="0">
|
||||||
|
<AdaptationSet id="0" contentType="video" width="640" height="360" frameRate="30000/1001" subsegmentAlignment="true" par="16:9">
|
||||||
|
<Representation id="0" bandwidth="882040" codecs="avc1.64001e" mimeType="video/mp4" sar="1:1">
|
||||||
|
<BaseURL>output_video.mp4</BaseURL>
|
||||||
|
<SegmentBase indexRange="815-882" timescale="30000">
|
||||||
|
<Initialization range="0-814"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
<AdaptationSet id="1" contentType="audio" lang="pt-BR" subsegmentAlignment="true">
|
||||||
|
<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="749-816" timescale="44100">
|
||||||
|
<Initialization range="0-748"/>
|
||||||
|
</SegmentBase>
|
||||||
|
</Representation>
|
||||||
|
</AdaptationSet>
|
||||||
|
</Period>
|
||||||
|
</MPD>
|
|
@ -6,12 +6,14 @@
|
||||||
|
|
||||||
#include "packager/media/base/demuxer.h"
|
#include "packager/media/base/demuxer.h"
|
||||||
|
|
||||||
|
#include <algorithm>
|
||||||
|
|
||||||
#include "packager/base/bind.h"
|
#include "packager/base/bind.h"
|
||||||
#include "packager/base/logging.h"
|
#include "packager/base/logging.h"
|
||||||
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/media/base/decryptor_source.h"
|
#include "packager/media/base/decryptor_source.h"
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/media/formats/mp2t/mp2t_media_parser.h"
|
#include "packager/media/formats/mp2t/mp2t_media_parser.h"
|
||||||
|
@ -28,19 +30,45 @@ const size_t kBufSize = 0x200000; // 2MB
|
||||||
// samples before seeing init_event, something is not right. The number
|
// samples before seeing init_event, something is not right. The number
|
||||||
// set here is arbitrary though.
|
// set here is arbitrary though.
|
||||||
const size_t kQueuedSamplesLimit = 10000;
|
const size_t kQueuedSamplesLimit = 10000;
|
||||||
|
const int kInvalidStreamIndex = -1;
|
||||||
|
const int kBaseVideoOutputStreamIndex = 0x100;
|
||||||
|
const int kBaseAudioOutputStreamIndex = 0x200;
|
||||||
|
|
||||||
|
std::string GetStreamLabel(int stream_index) {
|
||||||
|
switch (stream_index) {
|
||||||
|
case kBaseVideoOutputStreamIndex:
|
||||||
|
return "video";
|
||||||
|
case kBaseAudioOutputStreamIndex:
|
||||||
|
return "audio";
|
||||||
|
default:
|
||||||
|
return base::IntToString(stream_index);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
bool GetStreamIndex(const std::string& stream_label, int* stream_index) {
|
||||||
|
DCHECK(stream_index);
|
||||||
|
if (stream_label == "video") {
|
||||||
|
*stream_index = kBaseVideoOutputStreamIndex;
|
||||||
|
} else if (stream_label == "audio") {
|
||||||
|
*stream_index = kBaseAudioOutputStreamIndex;
|
||||||
|
} else {
|
||||||
|
// Expect stream_label to be a zero based stream id.
|
||||||
|
if (!base::StringToInt(stream_label, stream_index)) {
|
||||||
|
LOG(ERROR) << "Invalid argument --stream=" << stream_label << "; "
|
||||||
|
<< "should be 'audio', 'video', or a number";
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
Demuxer::Demuxer(const std::string& file_name)
|
Demuxer::Demuxer(const std::string& file_name)
|
||||||
: file_name_(file_name),
|
: file_name_(file_name), buffer_(new uint8_t[kBufSize]) {}
|
||||||
media_file_(NULL),
|
|
||||||
init_event_received_(false),
|
|
||||||
container_name_(CONTAINER_UNKNOWN),
|
|
||||||
buffer_(new uint8_t[kBufSize]),
|
|
||||||
cancelled_(false) {
|
|
||||||
}
|
|
||||||
|
|
||||||
Demuxer::~Demuxer() {
|
Demuxer::~Demuxer() {
|
||||||
if (media_file_)
|
if (media_file_)
|
||||||
|
@ -51,9 +79,74 @@ void Demuxer::SetKeySource(std::unique_ptr<KeySource> key_source) {
|
||||||
key_source_ = std::move(key_source);
|
key_source_ = std::move(key_source);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Demuxer::Initialize() {
|
Status Demuxer::Run() {
|
||||||
|
LOG(INFO) << "Demuxer::Run() on file '" << file_name_ << "'.";
|
||||||
|
Status status = InitializeParser();
|
||||||
|
// ParserInitEvent callback is called after a few calls to Parse(), which sets
|
||||||
|
// up the streams. Only after that, we can verify the outputs below.
|
||||||
|
while (!all_streams_ready_ && status.ok())
|
||||||
|
status.Update(Parse());
|
||||||
|
// If no output is defined, then return success after receiving all stream
|
||||||
|
// info.
|
||||||
|
if (all_streams_ready_ && output_handlers().empty())
|
||||||
|
return Status::OK;
|
||||||
|
// Check if all specified outputs exists.
|
||||||
|
for (const auto& pair : output_handlers()) {
|
||||||
|
if (std::find(stream_indexes_.begin(), stream_indexes_.end(), pair.first) ==
|
||||||
|
stream_indexes_.end()) {
|
||||||
|
LOG(ERROR) << "Invalid argument, stream=" << GetStreamLabel(pair.first)
|
||||||
|
<< " not available.";
|
||||||
|
return Status(error::INVALID_ARGUMENT, "Stream not available");
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
while (!cancelled_ && status.ok())
|
||||||
|
status.Update(Parse());
|
||||||
|
if (cancelled_ && status.ok())
|
||||||
|
return Status(error::CANCELLED, "Demuxer run cancelled");
|
||||||
|
|
||||||
|
if (status.error_code() == error::END_OF_STREAM) {
|
||||||
|
for (int stream_index : stream_indexes_) {
|
||||||
|
status = FlushStream(stream_index);
|
||||||
|
if (!status.ok())
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
return Status::OK;
|
||||||
|
}
|
||||||
|
return status;
|
||||||
|
}
|
||||||
|
|
||||||
|
void Demuxer::Cancel() {
|
||||||
|
cancelled_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
|
Status Demuxer::SetHandler(const std::string& stream_label,
|
||||||
|
std::shared_ptr<MediaHandler> handler) {
|
||||||
|
int stream_index = kInvalidStreamIndex;
|
||||||
|
if (!GetStreamIndex(stream_label, &stream_index)) {
|
||||||
|
return Status(error::INVALID_ARGUMENT,
|
||||||
|
"Invalid stream: " + stream_label);
|
||||||
|
}
|
||||||
|
return MediaHandler::SetHandler(stream_index, std::move(handler));
|
||||||
|
}
|
||||||
|
|
||||||
|
void Demuxer::SetLanguageOverride(const std::string& stream_label,
|
||||||
|
const std::string& language_override) {
|
||||||
|
int stream_index = kInvalidStreamIndex;
|
||||||
|
if (!GetStreamIndex(stream_label, &stream_index))
|
||||||
|
LOG(WARNING) << "Invalid stream for language override " << stream_label;
|
||||||
|
language_overrides_[stream_index] = language_override;
|
||||||
|
}
|
||||||
|
|
||||||
|
Demuxer::QueuedSample::QueuedSample(uint32_t local_track_id,
|
||||||
|
std::shared_ptr<MediaSample> local_sample)
|
||||||
|
: track_id(local_track_id), sample(local_sample) {}
|
||||||
|
|
||||||
|
Demuxer::QueuedSample::~QueuedSample() {}
|
||||||
|
|
||||||
|
Status Demuxer::InitializeParser() {
|
||||||
DCHECK(!media_file_);
|
DCHECK(!media_file_);
|
||||||
DCHECK(!init_event_received_);
|
DCHECK(!all_streams_ready_);
|
||||||
|
|
||||||
LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'.";
|
LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'.";
|
||||||
|
|
||||||
|
@ -105,34 +198,65 @@ Status Demuxer::Initialize() {
|
||||||
// Handle trailing 'moov'.
|
// Handle trailing 'moov'.
|
||||||
if (container_name_ == CONTAINER_MOV)
|
if (container_name_ == CONTAINER_MOV)
|
||||||
static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
|
static_cast<mp4::MP4MediaParser*>(parser_.get())->LoadMoov(file_name_);
|
||||||
|
|
||||||
if (!parser_->Parse(buffer_.get(), bytes_read)) {
|
if (!parser_->Parse(buffer_.get(), bytes_read)) {
|
||||||
init_parsing_status_ =
|
return Status(error::PARSER_FAILURE,
|
||||||
Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_);
|
"Cannot parse media file " + file_name_);
|
||||||
}
|
}
|
||||||
|
return Status::OK;
|
||||||
// Parse until init event received or on error.
|
|
||||||
while (!init_event_received_ && init_parsing_status_.ok())
|
|
||||||
init_parsing_status_ = Parse();
|
|
||||||
// Defer error reporting if init completed successfully.
|
|
||||||
return init_event_received_ ? Status::OK : init_parsing_status_;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void Demuxer::ParserInitEvent(
|
void Demuxer::ParserInitEvent(
|
||||||
const std::vector<std::shared_ptr<StreamInfo>>& stream_infos) {
|
const std::vector<std::shared_ptr<StreamInfo>>& stream_infos) {
|
||||||
init_event_received_ = true;
|
if (dump_stream_info_) {
|
||||||
for (const std::shared_ptr<StreamInfo>& stream_info : stream_infos)
|
printf("\nFile \"%s\":\n", file_name_.c_str());
|
||||||
streams_.emplace_back(new MediaStream(stream_info, this));
|
printf("Found %zu stream(s).\n", stream_infos.size());
|
||||||
|
for (size_t i = 0; i < stream_infos.size(); ++i)
|
||||||
|
printf("Stream [%zu] %s\n", i, stream_infos[i]->ToString().c_str());
|
||||||
}
|
}
|
||||||
|
|
||||||
Demuxer::QueuedSample::QueuedSample(uint32_t local_track_id,
|
int base_stream_index = 0;
|
||||||
std::shared_ptr<MediaSample> local_sample)
|
bool video_handler_set =
|
||||||
: track_id(local_track_id), sample(local_sample) {}
|
output_handlers().find(kBaseVideoOutputStreamIndex) !=
|
||||||
Demuxer::QueuedSample::~QueuedSample() {}
|
output_handlers().end();
|
||||||
|
bool audio_handler_set =
|
||||||
|
output_handlers().find(kBaseAudioOutputStreamIndex) !=
|
||||||
|
output_handlers().end();
|
||||||
|
for (const std::shared_ptr<StreamInfo>& stream_info : stream_infos) {
|
||||||
|
int stream_index = base_stream_index;
|
||||||
|
if (video_handler_set && stream_info->stream_type() == kStreamVideo) {
|
||||||
|
stream_index = kBaseVideoOutputStreamIndex;
|
||||||
|
// Only for the first video stream.
|
||||||
|
video_handler_set = false;
|
||||||
|
}
|
||||||
|
if (audio_handler_set && stream_info->stream_type() == kStreamAudio) {
|
||||||
|
stream_index = kBaseAudioOutputStreamIndex;
|
||||||
|
// Only for the first audio stream.
|
||||||
|
audio_handler_set = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
const bool handler_set =
|
||||||
|
output_handlers().find(stream_index) != output_handlers().end();
|
||||||
|
if (handler_set) {
|
||||||
|
track_id_to_stream_index_map_[stream_info->track_id()] = stream_index;
|
||||||
|
stream_indexes_.push_back(stream_index);
|
||||||
|
auto iter = language_overrides_.find(stream_index);
|
||||||
|
if (iter != language_overrides_.end() &&
|
||||||
|
stream_info->stream_type() != kStreamVideo) {
|
||||||
|
stream_info->set_language(iter->second);
|
||||||
|
}
|
||||||
|
DispatchStreamInfo(stream_index, stream_info);
|
||||||
|
} else {
|
||||||
|
track_id_to_stream_index_map_[stream_info->track_id()] =
|
||||||
|
kInvalidStreamIndex;
|
||||||
|
}
|
||||||
|
++base_stream_index;
|
||||||
|
}
|
||||||
|
all_streams_ready_ = true;
|
||||||
|
}
|
||||||
|
|
||||||
bool Demuxer::NewSampleEvent(uint32_t track_id,
|
bool Demuxer::NewSampleEvent(uint32_t track_id,
|
||||||
const std::shared_ptr<MediaSample>& sample) {
|
const std::shared_ptr<MediaSample>& sample) {
|
||||||
if (!init_event_received_) {
|
if (!all_streams_ready_) {
|
||||||
if (queued_samples_.size() >= kQueuedSamplesLimit) {
|
if (queued_samples_.size() >= kQueuedSamplesLimit) {
|
||||||
LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
|
LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit;
|
||||||
return false;
|
return false;
|
||||||
|
@ -152,46 +276,19 @@ bool Demuxer::NewSampleEvent(uint32_t track_id,
|
||||||
|
|
||||||
bool Demuxer::PushSample(uint32_t track_id,
|
bool Demuxer::PushSample(uint32_t track_id,
|
||||||
const std::shared_ptr<MediaSample>& sample) {
|
const std::shared_ptr<MediaSample>& sample) {
|
||||||
for (const std::unique_ptr<MediaStream>& stream : streams_) {
|
auto stream_index_iter = track_id_to_stream_index_map_.find(track_id);
|
||||||
if (track_id == stream->info()->track_id()) {
|
if (stream_index_iter == track_id_to_stream_index_map_.end()) {
|
||||||
Status status = stream->PushSample(sample);
|
|
||||||
if (!status.ok())
|
|
||||||
LOG(ERROR) << "Demuxer::PushSample failed with " << status;
|
|
||||||
return status.ok();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
LOG(ERROR) << "Track " << track_id << " not found.";
|
LOG(ERROR) << "Track " << track_id << " not found.";
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
if (stream_index_iter->second == kInvalidStreamIndex)
|
||||||
Status Demuxer::Run() {
|
return true;
|
||||||
Status status;
|
Status status = DispatchMediaSample(stream_index_iter->second, sample);
|
||||||
|
if (!status.ok()) {
|
||||||
LOG(INFO) << "Demuxer::Run() on file '" << file_name_ << "'.";
|
LOG(ERROR) << "Failed to process sample " << stream_index_iter->second
|
||||||
|
<< " " << status;
|
||||||
// Start the streams.
|
|
||||||
for (const std::unique_ptr<MediaStream>& stream : streams_) {
|
|
||||||
status = stream->Start(MediaStream::kPush);
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
return status.ok();
|
||||||
while (!cancelled_ && (status = Parse()).ok())
|
|
||||||
continue;
|
|
||||||
|
|
||||||
if (cancelled_ && status.ok())
|
|
||||||
return Status(error::CANCELLED, "Demuxer run cancelled");
|
|
||||||
|
|
||||||
if (status.error_code() == error::END_OF_STREAM) {
|
|
||||||
// Push EOS sample to muxer to indicate end of stream.
|
|
||||||
const std::shared_ptr<MediaSample>& sample = MediaSample::CreateEOSBuffer();
|
|
||||||
for (const std::unique_ptr<MediaStream>& stream : streams_) {
|
|
||||||
status = stream->PushSample(sample);
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return status;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Demuxer::Parse() {
|
Status Demuxer::Parse() {
|
||||||
|
@ -199,11 +296,6 @@ Status Demuxer::Parse() {
|
||||||
DCHECK(parser_);
|
DCHECK(parser_);
|
||||||
DCHECK(buffer_);
|
DCHECK(buffer_);
|
||||||
|
|
||||||
// Return early and avoid call Parse(...) again if it has already failed at
|
|
||||||
// the initialization.
|
|
||||||
if (!init_parsing_status_.ok())
|
|
||||||
return init_parsing_status_;
|
|
||||||
|
|
||||||
int64_t bytes_read = media_file_->Read(buffer_.get(), kBufSize);
|
int64_t bytes_read = media_file_->Read(buffer_.get(), kBufSize);
|
||||||
if (bytes_read == 0) {
|
if (bytes_read == 0) {
|
||||||
if (!parser_->Flush())
|
if (!parser_->Flush())
|
||||||
|
@ -219,9 +311,5 @@ Status Demuxer::Parse() {
|
||||||
"Cannot parse media file " + file_name_);
|
"Cannot parse media file " + file_name_);
|
||||||
}
|
}
|
||||||
|
|
||||||
void Demuxer::Cancel() {
|
|
||||||
cancelled_ = true;
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
} // namespace shaka
|
} // namespace shaka
|
||||||
|
|
|
@ -13,6 +13,7 @@
|
||||||
|
|
||||||
#include "packager/base/compiler_specific.h"
|
#include "packager/base/compiler_specific.h"
|
||||||
#include "packager/media/base/container_names.h"
|
#include "packager/media/base/container_names.h"
|
||||||
|
#include "packager/media/base/media_handler.h"
|
||||||
#include "packager/media/base/status.h"
|
#include "packager/media/base/status.h"
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
|
@ -28,7 +29,7 @@ class StreamInfo;
|
||||||
|
|
||||||
/// Demuxer is responsible for extracting elementary stream samples from a
|
/// Demuxer is responsible for extracting elementary stream samples from a
|
||||||
/// media file, e.g. an ISO BMFF file.
|
/// media file, e.g. an ISO BMFF file.
|
||||||
class Demuxer {
|
class Demuxer : public MediaHandler {
|
||||||
public:
|
public:
|
||||||
/// @param file_name specifies the input source. It uses prefix matching to
|
/// @param file_name specifies the input source. It uses prefix matching to
|
||||||
/// create a proper File object. The user can extend File to support
|
/// create a proper File object. The user can extend File to support
|
||||||
|
@ -42,36 +43,52 @@ class Demuxer {
|
||||||
/// demuxed.
|
/// demuxed.
|
||||||
void SetKeySource(std::unique_ptr<KeySource> key_source);
|
void SetKeySource(std::unique_ptr<KeySource> key_source);
|
||||||
|
|
||||||
/// Initialize the Demuxer. Calling other public methods of this class
|
|
||||||
/// without this method returning OK, results in an undefined behavior.
|
|
||||||
/// This method primes the demuxer by parsing portions of the media file to
|
|
||||||
/// extract stream information.
|
|
||||||
/// @return OK on success.
|
|
||||||
Status Initialize();
|
|
||||||
|
|
||||||
/// Drive the remuxing from demuxer side (push). Read the file and push
|
/// Drive the remuxing from demuxer side (push). Read the file and push
|
||||||
/// the Data to Muxer until Eof.
|
/// the Data to Muxer until Eof.
|
||||||
Status Run();
|
Status Run();
|
||||||
|
|
||||||
/// Read from the source and send it to the parser.
|
|
||||||
Status Parse();
|
|
||||||
|
|
||||||
/// Cancel a demuxing job in progress. Will cause @a Run to exit with an error
|
/// Cancel a demuxing job in progress. Will cause @a Run to exit with an error
|
||||||
/// status of type CANCELLED.
|
/// status of type CANCELLED.
|
||||||
void Cancel();
|
void Cancel();
|
||||||
|
|
||||||
/// @return Streams in the media container being demuxed. The caller cannot
|
|
||||||
/// add or remove streams from the returned vector, but the caller is
|
|
||||||
/// allowed to change the internal state of the streams in the vector
|
|
||||||
/// through MediaStream APIs.
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams() {
|
|
||||||
return streams_;
|
|
||||||
}
|
|
||||||
|
|
||||||
/// @return Container name (type). Value is CONTAINER_UNKNOWN if the demuxer
|
/// @return Container name (type). Value is CONTAINER_UNKNOWN if the demuxer
|
||||||
/// is not initialized.
|
/// is not initialized.
|
||||||
MediaContainerName container_name() { return container_name_; }
|
MediaContainerName container_name() { return container_name_; }
|
||||||
|
|
||||||
|
/// Set the handler for the specified stream.
|
||||||
|
/// @param stream_label can be 'audio', 'video', or stream number (zero
|
||||||
|
/// based).
|
||||||
|
/// @param handler is the handler for the specified stream.
|
||||||
|
Status SetHandler(const std::string& stream_label,
|
||||||
|
std::shared_ptr<MediaHandler> handler);
|
||||||
|
|
||||||
|
/// Override the language in the specified stream. If the specified stream is
|
||||||
|
/// a video stream or invalid, this function is a no-op.
|
||||||
|
/// @param stream_label can be 'audio', 'video', or stream number (zero
|
||||||
|
/// based).
|
||||||
|
/// @param language_override is the new language.
|
||||||
|
void SetLanguageOverride(const std::string& stream_label,
|
||||||
|
const std::string& language_override);
|
||||||
|
|
||||||
|
void set_dump_stream_info(bool dump_stream_info) {
|
||||||
|
dump_stream_info_ = dump_stream_info;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
/// @name MediaHandler implementation overrides.
|
||||||
|
/// @{
|
||||||
|
Status InitializeInternal() override { return Status::OK; }
|
||||||
|
Status Process(std::unique_ptr<StreamData> stream_data) override {
|
||||||
|
return Status(error::INTERNAL_ERROR,
|
||||||
|
"Demuxer should not be the downstream handler.");
|
||||||
|
}
|
||||||
|
bool ValidateOutputStreamIndex(int stream_index) const override {
|
||||||
|
// We don't know if the stream is valid or not when setting up the graph.
|
||||||
|
// Will validate the stream index later when stream info is available.
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
/// @}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
Demuxer(const Demuxer&) = delete;
|
Demuxer(const Demuxer&) = delete;
|
||||||
Demuxer& operator=(const Demuxer&) = delete;
|
Demuxer& operator=(const Demuxer&) = delete;
|
||||||
|
@ -84,6 +101,11 @@ class Demuxer {
|
||||||
std::shared_ptr<MediaSample> sample;
|
std::shared_ptr<MediaSample> sample;
|
||||||
};
|
};
|
||||||
|
|
||||||
|
// Initialize the parser. This method primes the demuxer by parsing portions
|
||||||
|
// of the media file to extract stream information.
|
||||||
|
// @return OK on success.
|
||||||
|
Status InitializeParser();
|
||||||
|
|
||||||
// Parser init event.
|
// Parser init event.
|
||||||
void ParserInitEvent(const std::vector<std::shared_ptr<StreamInfo>>& streams);
|
void ParserInitEvent(const std::vector<std::shared_ptr<StreamInfo>>& streams);
|
||||||
// Parser new sample event handler. Queues the samples if init event has not
|
// Parser new sample event handler. Queues the samples if init event has not
|
||||||
|
@ -95,18 +117,29 @@ class Demuxer {
|
||||||
bool PushSample(uint32_t track_id,
|
bool PushSample(uint32_t track_id,
|
||||||
const std::shared_ptr<MediaSample>& sample);
|
const std::shared_ptr<MediaSample>& sample);
|
||||||
|
|
||||||
|
// Read from the source and send it to the parser.
|
||||||
|
Status Parse();
|
||||||
|
|
||||||
std::string file_name_;
|
std::string file_name_;
|
||||||
File* media_file_;
|
File* media_file_ = nullptr;
|
||||||
bool init_event_received_;
|
// A stream is considered ready after receiving the stream info.
|
||||||
Status init_parsing_status_;
|
bool all_streams_ready_ = false;
|
||||||
// Queued samples received in NewSampleEvent() before ParserInitEvent().
|
// Queued samples received in NewSampleEvent() before ParserInitEvent().
|
||||||
std::deque<QueuedSample> queued_samples_;
|
std::deque<QueuedSample> queued_samples_;
|
||||||
std::unique_ptr<MediaParser> parser_;
|
std::unique_ptr<MediaParser> parser_;
|
||||||
std::vector<std::unique_ptr<MediaStream>> streams_;
|
// TrackId -> StreamIndex map.
|
||||||
MediaContainerName container_name_;
|
std::map<uint32_t, int> track_id_to_stream_index_map_;
|
||||||
|
// The list of stream indexes in the above map (in the same order as the input
|
||||||
|
// stream info vector).
|
||||||
|
std::vector<int> stream_indexes_;
|
||||||
|
// StreamIndex -> language_override map.
|
||||||
|
std::map<int, std::string> language_overrides_;
|
||||||
|
MediaContainerName container_name_ = CONTAINER_UNKNOWN;
|
||||||
std::unique_ptr<uint8_t[]> buffer_;
|
std::unique_ptr<uint8_t[]> buffer_;
|
||||||
std::unique_ptr<KeySource> key_source_;
|
std::unique_ptr<KeySource> key_source_;
|
||||||
bool cancelled_;
|
bool cancelled_ = false;
|
||||||
|
// Whether to dump stream info when it is received.
|
||||||
|
bool dump_stream_info_ = false;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -59,8 +59,6 @@
|
||||||
'media_parser.h',
|
'media_parser.h',
|
||||||
'media_sample.cc',
|
'media_sample.cc',
|
||||||
'media_sample.h',
|
'media_sample.h',
|
||||||
'media_stream.cc',
|
|
||||||
'media_stream.h',
|
|
||||||
'muxer.cc',
|
'muxer.cc',
|
||||||
'muxer.h',
|
'muxer.h',
|
||||||
'muxer_options.cc',
|
'muxer_options.cc',
|
||||||
|
|
|
@ -172,6 +172,10 @@ class MediaHandler {
|
||||||
|
|
||||||
int num_input_streams() const { return num_input_streams_; }
|
int num_input_streams() const { return num_input_streams_; }
|
||||||
int next_output_stream_index() const { return next_output_stream_index_; }
|
int next_output_stream_index() const { return next_output_stream_index_; }
|
||||||
|
const std::map<int, std::pair<std::shared_ptr<MediaHandler>, int>>&
|
||||||
|
output_handlers() {
|
||||||
|
return output_handlers_;
|
||||||
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
MediaHandler(const MediaHandler&) = delete;
|
MediaHandler(const MediaHandler&) = delete;
|
||||||
|
|
|
@ -1,111 +0,0 @@
|
||||||
// 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
|
|
||||||
|
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
|
|
||||||
#include "packager/base/logging.h"
|
|
||||||
#include "packager/base/strings/stringprintf.h"
|
|
||||||
#include "packager/media/base/demuxer.h"
|
|
||||||
#include "packager/media/base/media_sample.h"
|
|
||||||
#include "packager/media/base/muxer.h"
|
|
||||||
#include "packager/media/base/stream_info.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
MediaStream::MediaStream(std::shared_ptr<StreamInfo> info, Demuxer* demuxer)
|
|
||||||
: info_(info), demuxer_(demuxer), muxer_(NULL), state_(kIdle) {}
|
|
||||||
|
|
||||||
MediaStream::~MediaStream() {}
|
|
||||||
|
|
||||||
Status MediaStream::PullSample(std::shared_ptr<MediaSample>* sample) {
|
|
||||||
DCHECK(state_ == kPulling || state_ == kIdle);
|
|
||||||
|
|
||||||
// Trigger a new parse in demuxer if no more samples.
|
|
||||||
while (samples_.empty()) {
|
|
||||||
Status status = demuxer_->Parse();
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
*sample = samples_.front();
|
|
||||||
samples_.pop_front();
|
|
||||||
return Status::OK;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status MediaStream::PushSample(const std::shared_ptr<MediaSample>& sample) {
|
|
||||||
switch (state_) {
|
|
||||||
case kIdle:
|
|
||||||
case kPulling:
|
|
||||||
samples_.push_back(sample);
|
|
||||||
return Status::OK;
|
|
||||||
case kDisconnected:
|
|
||||||
return Status::OK;
|
|
||||||
case kPushing:
|
|
||||||
return muxer_->AddSample(this, sample);
|
|
||||||
default:
|
|
||||||
NOTREACHED() << "Unexpected State " << state_;
|
|
||||||
return Status::UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
void MediaStream::Connect(Muxer* muxer) {
|
|
||||||
DCHECK(muxer);
|
|
||||||
DCHECK(!muxer_);
|
|
||||||
state_ = kConnected;
|
|
||||||
muxer_ = muxer;
|
|
||||||
}
|
|
||||||
|
|
||||||
Status MediaStream::Start(MediaStreamOperation operation) {
|
|
||||||
DCHECK(demuxer_);
|
|
||||||
DCHECK(operation == kPush || operation == kPull);
|
|
||||||
|
|
||||||
switch (state_) {
|
|
||||||
case kIdle:
|
|
||||||
// Disconnect the stream if it is not connected to a muxer.
|
|
||||||
state_ = kDisconnected;
|
|
||||||
samples_.clear();
|
|
||||||
return Status::OK;
|
|
||||||
case kConnected:
|
|
||||||
state_ = (operation == kPush) ? kPushing : kPulling;
|
|
||||||
if (operation == kPush) {
|
|
||||||
// Push samples in the queue to muxer if there is any.
|
|
||||||
while (!samples_.empty()) {
|
|
||||||
Status status = muxer_->AddSample(this, samples_.front());
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
samples_.pop_front();
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
// We need to disconnect all its peer streams which are not connected
|
|
||||||
// to a muxer.
|
|
||||||
for (size_t i = 0; i < demuxer_->streams().size(); ++i) {
|
|
||||||
Status status = demuxer_->streams()[i]->Start(operation);
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Status::OK;
|
|
||||||
case kPulling:
|
|
||||||
DCHECK(operation == kPull);
|
|
||||||
return Status::OK;
|
|
||||||
default:
|
|
||||||
NOTREACHED() << "Unexpected State " << state_;
|
|
||||||
return Status::UNKNOWN;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
const std::shared_ptr<StreamInfo> MediaStream::info() const {
|
|
||||||
return info_;
|
|
||||||
}
|
|
||||||
|
|
||||||
std::string MediaStream::ToString() const {
|
|
||||||
return base::StringPrintf("state: %d\n samples in the queue: %zu\n %s",
|
|
||||||
state_, samples_.size(), info_->ToString().c_str());
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
} // namespace shaka
|
|
|
@ -1,80 +0,0 @@
|
||||||
// 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
|
|
||||||
|
|
||||||
#ifndef MEDIA_BASE_MEDIA_STREAM_H_
|
|
||||||
#define MEDIA_BASE_MEDIA_STREAM_H_
|
|
||||||
|
|
||||||
#include <deque>
|
|
||||||
#include <memory>
|
|
||||||
|
|
||||||
#include "packager/media/base/status.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
|
||||||
namespace media {
|
|
||||||
|
|
||||||
class Demuxer;
|
|
||||||
class Muxer;
|
|
||||||
class MediaSample;
|
|
||||||
class StreamInfo;
|
|
||||||
|
|
||||||
/// MediaStream connects Demuxer to Muxer. It is an abstraction for a media
|
|
||||||
/// elementary stream.
|
|
||||||
class MediaStream {
|
|
||||||
public:
|
|
||||||
enum MediaStreamOperation {
|
|
||||||
kPush,
|
|
||||||
kPull,
|
|
||||||
};
|
|
||||||
/// Create MediaStream from StreamInfo and Demuxer.
|
|
||||||
/// @param demuxer cannot be NULL.
|
|
||||||
MediaStream(std::shared_ptr<StreamInfo> info, Demuxer* demuxer);
|
|
||||||
~MediaStream();
|
|
||||||
|
|
||||||
/// Connect the stream to Muxer.
|
|
||||||
/// @param muxer cannot be NULL.
|
|
||||||
void Connect(Muxer* muxer);
|
|
||||||
|
|
||||||
/// Start the stream for pushing or pulling.
|
|
||||||
Status Start(MediaStreamOperation operation);
|
|
||||||
|
|
||||||
/// Push sample to Muxer (triggered by Demuxer).
|
|
||||||
Status PushSample(const std::shared_ptr<MediaSample>& sample);
|
|
||||||
|
|
||||||
/// Pull sample from Demuxer (triggered by Muxer).
|
|
||||||
Status PullSample(std::shared_ptr<MediaSample>* sample);
|
|
||||||
|
|
||||||
Demuxer* demuxer() { return demuxer_; }
|
|
||||||
Muxer* muxer() { return muxer_; }
|
|
||||||
const std::shared_ptr<StreamInfo> info() const;
|
|
||||||
|
|
||||||
/// @return a human-readable string describing |*this|.
|
|
||||||
std::string ToString() const;
|
|
||||||
|
|
||||||
private:
|
|
||||||
MediaStream(const MediaStream&) = delete;
|
|
||||||
MediaStream& operator=(const MediaStream&) = delete;
|
|
||||||
|
|
||||||
// State transition diagram available @ http://goo.gl/ThJQbl.
|
|
||||||
enum State {
|
|
||||||
kIdle,
|
|
||||||
kConnected,
|
|
||||||
kDisconnected,
|
|
||||||
kPushing,
|
|
||||||
kPulling,
|
|
||||||
};
|
|
||||||
|
|
||||||
std::shared_ptr<StreamInfo> info_;
|
|
||||||
Demuxer* demuxer_;
|
|
||||||
Muxer* muxer_;
|
|
||||||
State state_;
|
|
||||||
// An internal buffer to store samples temporarily.
|
|
||||||
std::deque<std::shared_ptr<MediaSample>> samples_;
|
|
||||||
};
|
|
||||||
|
|
||||||
} // namespace media
|
|
||||||
} // namespace shaka
|
|
||||||
|
|
||||||
#endif // MEDIA_BASE_MEDIA_STREAM_H_
|
|
|
@ -10,14 +10,12 @@
|
||||||
|
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
Muxer::Muxer(const MuxerOptions& options)
|
Muxer::Muxer(const MuxerOptions& options)
|
||||||
: options_(options),
|
: options_(options),
|
||||||
initialized_(false),
|
|
||||||
encryption_key_source_(NULL),
|
encryption_key_source_(NULL),
|
||||||
max_sd_pixels_(0),
|
max_sd_pixels_(0),
|
||||||
max_hd_pixels_(0),
|
max_hd_pixels_(0),
|
||||||
|
@ -47,46 +45,6 @@ void Muxer::SetKeySource(KeySource* encryption_key_source,
|
||||||
protection_scheme_ = protection_scheme;
|
protection_scheme_ = protection_scheme;
|
||||||
}
|
}
|
||||||
|
|
||||||
void Muxer::AddStream(MediaStream* stream) {
|
|
||||||
DCHECK(stream);
|
|
||||||
stream->Connect(this);
|
|
||||||
streams_.push_back(stream);
|
|
||||||
}
|
|
||||||
|
|
||||||
Status Muxer::Run() {
|
|
||||||
DCHECK(!streams_.empty());
|
|
||||||
|
|
||||||
Status status;
|
|
||||||
// Start the streams.
|
|
||||||
for (std::vector<MediaStream*>::iterator it = streams_.begin();
|
|
||||||
it != streams_.end();
|
|
||||||
++it) {
|
|
||||||
status = (*it)->Start(MediaStream::kPull);
|
|
||||||
if (!status.ok())
|
|
||||||
return status;
|
|
||||||
}
|
|
||||||
|
|
||||||
uint32_t current_stream_id = 0;
|
|
||||||
while (status.ok()) {
|
|
||||||
if (cancelled_)
|
|
||||||
return Status(error::CANCELLED, "muxer run cancelled");
|
|
||||||
|
|
||||||
std::shared_ptr<MediaSample> sample;
|
|
||||||
status = streams_[current_stream_id]->PullSample(&sample);
|
|
||||||
if (!status.ok())
|
|
||||||
break;
|
|
||||||
status = AddSample(streams_[current_stream_id], sample);
|
|
||||||
|
|
||||||
// Switch to next stream if the current stream is ready for fragmentation.
|
|
||||||
if (status.error_code() == error::FRAGMENT_FINALIZED) {
|
|
||||||
current_stream_id = (current_stream_id + 1) % streams_.size();
|
|
||||||
status.Clear();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
// Finalize the muxer after reaching end of stream.
|
|
||||||
return status.error_code() == error::END_OF_STREAM ? Finalize() : status;
|
|
||||||
}
|
|
||||||
|
|
||||||
void Muxer::Cancel() {
|
void Muxer::Cancel() {
|
||||||
cancelled_ = true;
|
cancelled_ = true;
|
||||||
}
|
}
|
||||||
|
@ -100,26 +58,21 @@ void Muxer::SetProgressListener(
|
||||||
progress_listener_ = std::move(progress_listener);
|
progress_listener_ = std::move(progress_listener);
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Muxer::AddSample(const MediaStream* stream,
|
Status Muxer::Process(std::unique_ptr<StreamData> stream_data) {
|
||||||
std::shared_ptr<MediaSample> sample) {
|
Status status;
|
||||||
DCHECK(std::find(streams_.begin(), streams_.end(), stream) != streams_.end());
|
switch (stream_data->stream_data_type) {
|
||||||
|
case StreamDataType::kStreamInfo:
|
||||||
if (!initialized_) {
|
streams_.push_back(std::move(stream_data->stream_info));
|
||||||
Status status = Initialize();
|
return InitializeMuxer();
|
||||||
if (!status.ok())
|
case StreamDataType::kMediaSample:
|
||||||
return status;
|
return DoAddSample(stream_data->media_sample);
|
||||||
initialized_ = true;
|
default:
|
||||||
|
VLOG(3) << "Stream data type "
|
||||||
|
<< static_cast<int>(stream_data->stream_data_type) << " ignored.";
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
if (sample->end_of_stream()) {
|
// No dispatch for muxer.
|
||||||
// EOS sample should be sent only when the sample was pushed from Demuxer
|
return Status::OK;
|
||||||
// to Muxer. In this case, there should be only one stream in Muxer.
|
|
||||||
DCHECK_EQ(1u, streams_.size());
|
|
||||||
return Finalize();
|
|
||||||
} else if (sample->is_encrypted()) {
|
|
||||||
LOG(ERROR) << "Unable to multiplex encrypted media sample";
|
|
||||||
return Status(error::INTERNAL_ERROR, "Encrypted media sample.");
|
|
||||||
}
|
|
||||||
return DoAddSample(stream, sample);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
} // namespace media
|
} // namespace media
|
||||||
|
|
|
@ -14,6 +14,7 @@
|
||||||
|
|
||||||
#include "packager/base/time/clock.h"
|
#include "packager/base/time/clock.h"
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
|
#include "packager/media/base/media_handler.h"
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/status.h"
|
#include "packager/media/base/status.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
|
@ -29,7 +30,7 @@ class MediaStream;
|
||||||
/// Muxer is responsible for taking elementary stream samples and producing
|
/// Muxer is responsible for taking elementary stream samples and producing
|
||||||
/// media containers. An optional KeySource can be provided to Muxer
|
/// media containers. An optional KeySource can be provided to Muxer
|
||||||
/// to generate encrypted outputs.
|
/// to generate encrypted outputs.
|
||||||
class Muxer {
|
class Muxer : public MediaHandler {
|
||||||
public:
|
public:
|
||||||
explicit Muxer(const MuxerOptions& options);
|
explicit Muxer(const MuxerOptions& options);
|
||||||
virtual ~Muxer();
|
virtual ~Muxer();
|
||||||
|
@ -65,12 +66,6 @@ class Muxer {
|
||||||
double crypto_period_duration_in_seconds,
|
double crypto_period_duration_in_seconds,
|
||||||
FourCC protection_scheme);
|
FourCC protection_scheme);
|
||||||
|
|
||||||
/// Add video/audio stream.
|
|
||||||
void AddStream(MediaStream* stream);
|
|
||||||
|
|
||||||
/// Drive the remuxing from muxer side (pull).
|
|
||||||
Status Run();
|
|
||||||
|
|
||||||
/// Cancel a muxing job in progress. Will cause @a Run to exit with an error
|
/// Cancel a muxing job in progress. Will cause @a Run to exit with an error
|
||||||
/// status of type CANCELLED.
|
/// status of type CANCELLED.
|
||||||
void Cancel();
|
void Cancel();
|
||||||
|
@ -83,7 +78,9 @@ class Muxer {
|
||||||
/// @param progress_listener should not be NULL.
|
/// @param progress_listener should not be NULL.
|
||||||
void SetProgressListener(std::unique_ptr<ProgressListener> progress_listener);
|
void SetProgressListener(std::unique_ptr<ProgressListener> progress_listener);
|
||||||
|
|
||||||
const std::vector<MediaStream*>& streams() const { return streams_; }
|
const std::vector<std::shared_ptr<StreamInfo>>& streams() const {
|
||||||
|
return streams_;
|
||||||
|
}
|
||||||
|
|
||||||
/// Inject clock, mainly used for testing.
|
/// Inject clock, mainly used for testing.
|
||||||
/// The injected clock will be used to generate the creation time-stamp and
|
/// The injected clock will be used to generate the creation time-stamp and
|
||||||
|
@ -96,6 +93,13 @@ class Muxer {
|
||||||
}
|
}
|
||||||
|
|
||||||
protected:
|
protected:
|
||||||
|
/// @name MediaHandler implementation overrides.
|
||||||
|
/// @{
|
||||||
|
Status InitializeInternal() override { return Status::OK; }
|
||||||
|
Status Process(std::unique_ptr<StreamData> stream_data) override;
|
||||||
|
Status FlushStream(int input_stream_index) override { return Finalize(); }
|
||||||
|
/// @}
|
||||||
|
|
||||||
const MuxerOptions& options() const { return options_; }
|
const MuxerOptions& options() const { return options_; }
|
||||||
KeySource* encryption_key_source() {
|
KeySource* encryption_key_source() {
|
||||||
return encryption_key_source_;
|
return encryption_key_source_;
|
||||||
|
@ -113,25 +117,17 @@ class Muxer {
|
||||||
FourCC protection_scheme() const { return protection_scheme_; }
|
FourCC protection_scheme() const { return protection_scheme_; }
|
||||||
|
|
||||||
private:
|
private:
|
||||||
friend class MediaStream; // Needed to access AddSample.
|
|
||||||
|
|
||||||
// Add new media sample.
|
|
||||||
Status AddSample(const MediaStream* stream,
|
|
||||||
std::shared_ptr<MediaSample> sample);
|
|
||||||
|
|
||||||
// Initialize the muxer.
|
// Initialize the muxer.
|
||||||
virtual Status Initialize() = 0;
|
virtual Status InitializeMuxer() = 0;
|
||||||
|
|
||||||
// Final clean up.
|
// Final clean up.
|
||||||
virtual Status Finalize() = 0;
|
virtual Status Finalize() = 0;
|
||||||
|
|
||||||
// AddSample implementation.
|
// AddSample implementation.
|
||||||
virtual Status DoAddSample(const MediaStream* stream,
|
virtual Status DoAddSample(std::shared_ptr<MediaSample> sample) = 0;
|
||||||
std::shared_ptr<MediaSample> sample) = 0;
|
|
||||||
|
|
||||||
MuxerOptions options_;
|
MuxerOptions options_;
|
||||||
bool initialized_;
|
std::vector<std::shared_ptr<StreamInfo>> streams_;
|
||||||
std::vector<MediaStream*> streams_;
|
|
||||||
KeySource* encryption_key_source_;
|
KeySource* encryption_key_source_;
|
||||||
uint32_t max_sd_pixels_;
|
uint32_t max_sd_pixels_;
|
||||||
uint32_t max_hd_pixels_;
|
uint32_t max_hd_pixels_;
|
||||||
|
|
|
@ -17,14 +17,13 @@ const uint32_t kTsTimescale = 90000;
|
||||||
TsMuxer::TsMuxer(const MuxerOptions& muxer_options) : Muxer(muxer_options) {}
|
TsMuxer::TsMuxer(const MuxerOptions& muxer_options) : Muxer(muxer_options) {}
|
||||||
TsMuxer::~TsMuxer() {}
|
TsMuxer::~TsMuxer() {}
|
||||||
|
|
||||||
Status TsMuxer::Initialize() {
|
Status TsMuxer::InitializeMuxer() {
|
||||||
if (streams().size() > 1u)
|
if (streams().size() > 1u)
|
||||||
return Status(error::MUXER_FAILURE, "Cannot handle more than one streams.");
|
return Status(error::MUXER_FAILURE, "Cannot handle more than one streams.");
|
||||||
|
|
||||||
segmenter_.reset(new TsSegmenter(options(), muxer_listener()));
|
segmenter_.reset(new TsSegmenter(options(), muxer_listener()));
|
||||||
Status status =
|
Status status = segmenter_->Initialize(
|
||||||
segmenter_->Initialize(*streams()[0]->info(), encryption_key_source(),
|
*streams()[0], encryption_key_source(), max_sd_pixels(), max_hd_pixels(),
|
||||||
max_sd_pixels(), max_hd_pixels(),
|
|
||||||
max_uhd1_pixels(), clear_lead_in_seconds());
|
max_uhd1_pixels(), clear_lead_in_seconds());
|
||||||
FireOnMediaStartEvent();
|
FireOnMediaStartEvent();
|
||||||
return status;
|
return status;
|
||||||
|
@ -35,16 +34,15 @@ Status TsMuxer::Finalize() {
|
||||||
return segmenter_->Finalize();
|
return segmenter_->Finalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status TsMuxer::DoAddSample(const MediaStream* stream,
|
Status TsMuxer::DoAddSample(std::shared_ptr<MediaSample> sample) {
|
||||||
std::shared_ptr<MediaSample> sample) {
|
|
||||||
return segmenter_->AddSample(sample);
|
return segmenter_->AddSample(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TsMuxer::FireOnMediaStartEvent() {
|
void TsMuxer::FireOnMediaStartEvent() {
|
||||||
if (!muxer_listener())
|
if (!muxer_listener())
|
||||||
return;
|
return;
|
||||||
muxer_listener()->OnMediaStart(options(), *streams().front()->info(),
|
muxer_listener()->OnMediaStart(options(), *streams().front(), kTsTimescale,
|
||||||
kTsTimescale, MuxerListener::kContainerWebM);
|
MuxerListener::kContainerWebM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void TsMuxer::FireOnMediaEndEvent() {
|
void TsMuxer::FireOnMediaEndEvent() {
|
||||||
|
|
|
@ -24,10 +24,9 @@ class TsMuxer : public Muxer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Muxer implementation.
|
// Muxer implementation.
|
||||||
Status Initialize() override;
|
Status InitializeMuxer() override;
|
||||||
Status Finalize() override;
|
Status Finalize() override;
|
||||||
Status DoAddSample(const MediaStream* stream,
|
Status DoAddSample(std::shared_ptr<MediaSample> sample) override;
|
||||||
std::shared_ptr<MediaSample> sample) override;
|
|
||||||
|
|
||||||
void FireOnMediaStartEvent();
|
void FireOnMediaStartEvent();
|
||||||
void FireOnMediaEndEvent();
|
void FireOnMediaEndEvent();
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
#define PACKAGER_MEDIA_FORMATS_MP2T_TS_SEGMENTER_H_
|
#define PACKAGER_MEDIA_FORMATS_MP2T_TS_SEGMENTER_H_
|
||||||
|
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/status.h"
|
#include "packager/media/base/status.h"
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
|
|
|
@ -12,7 +12,6 @@
|
||||||
#include <memory>
|
#include <memory>
|
||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/file/file.h"
|
#include "packager/media/file/file.h"
|
||||||
#include "packager/media/file/file_closer.h"
|
#include "packager/media/file/file_closer.h"
|
||||||
#include "packager/media/formats/mp2t/continuity_counter.h"
|
#include "packager/media/formats/mp2t/continuity_counter.h"
|
||||||
|
@ -21,6 +20,9 @@
|
||||||
|
|
||||||
namespace shaka {
|
namespace shaka {
|
||||||
namespace media {
|
namespace media {
|
||||||
|
|
||||||
|
class StreamInfo;
|
||||||
|
|
||||||
namespace mp2t {
|
namespace mp2t {
|
||||||
|
|
||||||
/// This class takes PesPackets, encapsulates them into TS packets, and write
|
/// This class takes PesPackets, encapsulates them into TS packets, and write
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/video_stream_info.h"
|
#include "packager/media/base/video_stream_info.h"
|
||||||
#include "packager/media/codecs/es_descriptor.h"
|
#include "packager/media/codecs/es_descriptor.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
|
@ -82,7 +81,7 @@ FourCC CodecToFourCC(Codec codec) {
|
||||||
MP4Muxer::MP4Muxer(const MuxerOptions& options) : Muxer(options) {}
|
MP4Muxer::MP4Muxer(const MuxerOptions& options) : Muxer(options) {}
|
||||||
MP4Muxer::~MP4Muxer() {}
|
MP4Muxer::~MP4Muxer() {}
|
||||||
|
|
||||||
Status MP4Muxer::Initialize() {
|
Status MP4Muxer::InitializeMuxer() {
|
||||||
DCHECK(!streams().empty());
|
DCHECK(!streams().empty());
|
||||||
|
|
||||||
std::unique_ptr<FileType> ftyp(new FileType);
|
std::unique_ptr<FileType> ftyp(new FileType);
|
||||||
|
@ -91,10 +90,9 @@ Status MP4Muxer::Initialize() {
|
||||||
ftyp->major_brand = FOURCC_dash;
|
ftyp->major_brand = FOURCC_dash;
|
||||||
ftyp->compatible_brands.push_back(FOURCC_iso6);
|
ftyp->compatible_brands.push_back(FOURCC_iso6);
|
||||||
ftyp->compatible_brands.push_back(FOURCC_mp41);
|
ftyp->compatible_brands.push_back(FOURCC_mp41);
|
||||||
if (streams().size() == 1 &&
|
if (streams().size() == 1 && streams()[0]->stream_type() == kStreamVideo) {
|
||||||
streams()[0]->info()->stream_type() == kStreamVideo) {
|
|
||||||
const FourCC codec_fourcc = CodecToFourCC(
|
const FourCC codec_fourcc = CodecToFourCC(
|
||||||
static_cast<VideoStreamInfo*>(streams()[0]->info().get())->codec());
|
static_cast<VideoStreamInfo*>(streams()[0].get())->codec());
|
||||||
if (codec_fourcc != FOURCC_NULL)
|
if (codec_fourcc != FOURCC_NULL)
|
||||||
ftyp->compatible_brands.push_back(codec_fourcc);
|
ftyp->compatible_brands.push_back(codec_fourcc);
|
||||||
}
|
}
|
||||||
|
@ -115,22 +113,18 @@ Status MP4Muxer::Initialize() {
|
||||||
trex.track_id = trak.header.track_id;
|
trex.track_id = trak.header.track_id;
|
||||||
trex.default_sample_description_index = 1;
|
trex.default_sample_description_index = 1;
|
||||||
|
|
||||||
switch (streams()[i]->info()->stream_type()) {
|
switch (streams()[i]->stream_type()) {
|
||||||
case kStreamVideo:
|
case kStreamVideo:
|
||||||
GenerateVideoTrak(
|
GenerateVideoTrak(static_cast<VideoStreamInfo*>(streams()[i].get()),
|
||||||
static_cast<VideoStreamInfo*>(streams()[i]->info().get()),
|
&trak, i + 1);
|
||||||
&trak,
|
|
||||||
i + 1);
|
|
||||||
break;
|
break;
|
||||||
case kStreamAudio:
|
case kStreamAudio:
|
||||||
GenerateAudioTrak(
|
GenerateAudioTrak(static_cast<AudioStreamInfo*>(streams()[i].get()),
|
||||||
static_cast<AudioStreamInfo*>(streams()[i]->info().get()),
|
&trak, i + 1);
|
||||||
&trak,
|
|
||||||
i + 1);
|
|
||||||
break;
|
break;
|
||||||
default:
|
default:
|
||||||
NOTIMPLEMENTED() << "Not implemented for stream type: "
|
NOTIMPLEMENTED() << "Not implemented for stream type: "
|
||||||
<< streams()[i]->info()->stream_type();
|
<< streams()[i]->stream_type();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -167,10 +161,9 @@ Status MP4Muxer::Finalize() {
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status MP4Muxer::DoAddSample(const MediaStream* stream,
|
Status MP4Muxer::DoAddSample(std::shared_ptr<MediaSample> sample) {
|
||||||
std::shared_ptr<MediaSample> sample) {
|
|
||||||
DCHECK(segmenter_);
|
DCHECK(segmenter_);
|
||||||
return segmenter_->AddSample(stream, sample);
|
return segmenter_->AddSample(*streams()[0], sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) {
|
void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) {
|
||||||
|
@ -355,9 +348,7 @@ void MP4Muxer::FireOnMediaStartEvent() {
|
||||||
DCHECK(!streams().empty()) << "Media started without a stream.";
|
DCHECK(!streams().empty()) << "Media started without a stream.";
|
||||||
|
|
||||||
const uint32_t timescale = segmenter_->GetReferenceTimeScale();
|
const uint32_t timescale = segmenter_->GetReferenceTimeScale();
|
||||||
muxer_listener()->OnMediaStart(options(),
|
muxer_listener()->OnMediaStart(options(), *streams().front(), timescale,
|
||||||
*streams().front()->info(),
|
|
||||||
timescale,
|
|
||||||
MuxerListener::kContainerMp4);
|
MuxerListener::kContainerMp4);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -35,10 +35,9 @@ class MP4Muxer : public Muxer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Muxer implementation overrides.
|
// Muxer implementation overrides.
|
||||||
Status Initialize() override;
|
Status InitializeMuxer() override;
|
||||||
Status Finalize() override;
|
Status Finalize() override;
|
||||||
Status DoAddSample(const MediaStream* stream,
|
Status DoAddSample(std::shared_ptr<MediaSample> sample) override;
|
||||||
std::shared_ptr<MediaSample> sample) override;
|
|
||||||
|
|
||||||
// Generate Audio/Video Track box.
|
// Generate Audio/Video Track box.
|
||||||
void InitializeTrak(const StreamInfo* info, Track* trak);
|
void InitializeTrak(const StreamInfo* info, Track* trak);
|
||||||
|
|
|
@ -11,7 +11,6 @@
|
||||||
#include "packager/base/strings/string_number_conversions.h"
|
#include "packager/base/strings/string_number_conversions.h"
|
||||||
#include "packager/base/strings/string_util.h"
|
#include "packager/base/strings/string_util.h"
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
#include "packager/media/base/key_source.h"
|
#include "packager/media/base/key_source.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/media/base/video_stream_info.h"
|
#include "packager/media/base/video_stream_info.h"
|
||||||
|
@ -162,7 +161,8 @@ Segmenter::Segmenter(const MuxerOptions& options,
|
||||||
|
|
||||||
Segmenter::~Segmenter() {}
|
Segmenter::~Segmenter() {}
|
||||||
|
|
||||||
Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
Status Segmenter::Initialize(
|
||||||
|
const std::vector<std::shared_ptr<StreamInfo>>& streams,
|
||||||
MuxerListener* muxer_listener,
|
MuxerListener* muxer_listener,
|
||||||
ProgressListener* progress_listener,
|
ProgressListener* progress_listener,
|
||||||
KeySource* encryption_key_source,
|
KeySource* encryption_key_source,
|
||||||
|
@ -184,22 +184,19 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
const bool kInitialEncryptionInfo = true;
|
const bool kInitialEncryptionInfo = true;
|
||||||
|
|
||||||
for (uint32_t i = 0; i < streams.size(); ++i) {
|
for (uint32_t i = 0; i < streams.size(); ++i) {
|
||||||
stream_map_[streams[i]] = i;
|
|
||||||
moof_->tracks[i].header.track_id = i + 1;
|
moof_->tracks[i].header.track_id = i + 1;
|
||||||
if (streams[i]->info()->stream_type() == kStreamVideo) {
|
if (streams[i]->stream_type() == kStreamVideo) {
|
||||||
// Use the first video stream as the reference stream (which is 1-based).
|
// Use the first video stream as the reference stream (which is 1-based).
|
||||||
if (sidx_->reference_id == 0)
|
if (sidx_->reference_id == 0)
|
||||||
sidx_->reference_id = i + 1;
|
sidx_->reference_id = i + 1;
|
||||||
}
|
}
|
||||||
if (!encryption_key_source) {
|
if (!encryption_key_source) {
|
||||||
fragmenters_[i].reset(
|
fragmenters_[i].reset(new Fragmenter(streams[i], &moof_->tracks[i]));
|
||||||
new Fragmenter(streams[i]->info(), &moof_->tracks[i]));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
KeySource::TrackType track_type =
|
KeySource::TrackType track_type = GetTrackTypeForEncryption(
|
||||||
GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels,
|
*streams[i], max_sd_pixels, max_hd_pixels, max_uhd1_pixels);
|
||||||
max_hd_pixels, max_uhd1_pixels);
|
|
||||||
SampleDescription& description =
|
SampleDescription& description =
|
||||||
moov_->tracks[i].media.information.sample_table.description;
|
moov_->tracks[i].media.information.sample_table.description;
|
||||||
ProtectionPattern pattern =
|
ProtectionPattern pattern =
|
||||||
|
@ -224,12 +221,11 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
}
|
}
|
||||||
|
|
||||||
fragmenters_[i].reset(new KeyRotationFragmenter(
|
fragmenters_[i].reset(new KeyRotationFragmenter(
|
||||||
moof_.get(), streams[i]->info(), &moof_->tracks[i],
|
moof_.get(), streams[i], &moof_->tracks[i], encryption_key_source,
|
||||||
encryption_key_source, track_type,
|
track_type,
|
||||||
crypto_period_duration_in_seconds * streams[i]->info()->time_scale(),
|
crypto_period_duration_in_seconds * streams[i]->time_scale(),
|
||||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
clear_lead_in_seconds * streams[i]->time_scale(), protection_scheme,
|
||||||
protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block,
|
pattern.crypt_byte_block, pattern.skip_byte_block, muxer_listener_));
|
||||||
muxer_listener_));
|
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -262,10 +258,9 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
}
|
}
|
||||||
|
|
||||||
fragmenters_[i].reset(new EncryptingFragmenter(
|
fragmenters_[i].reset(new EncryptingFragmenter(
|
||||||
streams[i]->info(), &moof_->tracks[i], std::move(encryption_key),
|
streams[i], &moof_->tracks[i], std::move(encryption_key),
|
||||||
clear_lead_in_seconds * streams[i]->info()->time_scale(),
|
clear_lead_in_seconds * streams[i]->time_scale(), protection_scheme,
|
||||||
protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block,
|
pattern.crypt_byte_block, pattern.skip_byte_block, muxer_listener_));
|
||||||
muxer_listener_));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (options_.mp4_use_decoding_timestamp_in_timeline) {
|
if (options_.mp4_use_decoding_timestamp_in_timeline) {
|
||||||
|
@ -276,10 +271,10 @@ Status Segmenter::Initialize(const std::vector<MediaStream*>& streams,
|
||||||
// Choose the first stream if there is no VIDEO.
|
// Choose the first stream if there is no VIDEO.
|
||||||
if (sidx_->reference_id == 0)
|
if (sidx_->reference_id == 0)
|
||||||
sidx_->reference_id = 1;
|
sidx_->reference_id = 1;
|
||||||
sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale();
|
sidx_->timescale = streams[GetReferenceStreamId()]->time_scale();
|
||||||
|
|
||||||
// Use media duration as progress target.
|
// Use media duration as progress target.
|
||||||
progress_target_ = streams[GetReferenceStreamId()]->info()->duration();
|
progress_target_ = streams[GetReferenceStreamId()]->duration();
|
||||||
|
|
||||||
// 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;
|
||||||
|
@ -320,12 +315,10 @@ Status Segmenter::Finalize() {
|
||||||
return DoFinalize();
|
return DoFinalize();
|
||||||
}
|
}
|
||||||
|
|
||||||
Status Segmenter::AddSample(const MediaStream* stream,
|
Status Segmenter::AddSample(const StreamInfo& stream_info,
|
||||||
std::shared_ptr<MediaSample> sample) {
|
std::shared_ptr<MediaSample> sample) {
|
||||||
// Find the fragmenter for this stream.
|
// TODO(kqyang): Stream id should be passed in.
|
||||||
DCHECK(stream);
|
const uint32_t stream_id = 0;
|
||||||
DCHECK(stream_map_.find(stream) != stream_map_.end());
|
|
||||||
uint32_t stream_id = stream_map_[stream];
|
|
||||||
Fragmenter* fragmenter = fragmenters_[stream_id].get();
|
Fragmenter* fragmenter = fragmenters_[stream_id].get();
|
||||||
|
|
||||||
// Set default sample duration if it has not been set yet.
|
// Set default sample duration if it has not been set yet.
|
||||||
|
@ -341,14 +334,14 @@ Status Segmenter::AddSample(const MediaStream* stream,
|
||||||
|
|
||||||
bool finalize_fragment = false;
|
bool finalize_fragment = false;
|
||||||
if (fragmenter->fragment_duration() >=
|
if (fragmenter->fragment_duration() >=
|
||||||
options_.fragment_duration * stream->info()->time_scale()) {
|
options_.fragment_duration * stream_info.time_scale()) {
|
||||||
if (sample->is_key_frame() || !options_.fragment_sap_aligned) {
|
if (sample->is_key_frame() || !options_.fragment_sap_aligned) {
|
||||||
finalize_fragment = true;
|
finalize_fragment = true;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
bool finalize_segment = false;
|
bool finalize_segment = false;
|
||||||
if (segment_durations_[stream_id] >=
|
if (segment_durations_[stream_id] >=
|
||||||
options_.segment_duration * stream->info()->time_scale()) {
|
options_.segment_duration * stream_info.time_scale()) {
|
||||||
if (sample->is_key_frame() || !options_.segment_sap_aligned) {
|
if (sample->is_key_frame() || !options_.segment_sap_aligned) {
|
||||||
finalize_segment = true;
|
finalize_segment = true;
|
||||||
finalize_fragment = true;
|
finalize_fragment = true;
|
||||||
|
|
|
@ -23,9 +23,9 @@ struct MuxerOptions;
|
||||||
class BufferWriter;
|
class BufferWriter;
|
||||||
class KeySource;
|
class KeySource;
|
||||||
class MediaSample;
|
class MediaSample;
|
||||||
class MediaStream;
|
|
||||||
class MuxerListener;
|
class MuxerListener;
|
||||||
class ProgressListener;
|
class ProgressListener;
|
||||||
|
class StreamInfo;
|
||||||
|
|
||||||
namespace mp4 {
|
namespace mp4 {
|
||||||
|
|
||||||
|
@ -69,7 +69,7 @@ class Segmenter {
|
||||||
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
/// @param protection_scheme specifies the protection scheme: 'cenc', 'cens',
|
||||||
/// 'cbc1', 'cbcs'.
|
/// 'cbc1', 'cbcs'.
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
Status Initialize(const std::vector<MediaStream*>& streams,
|
Status Initialize(const std::vector<std::shared_ptr<StreamInfo>>& streams,
|
||||||
MuxerListener* muxer_listener,
|
MuxerListener* muxer_listener,
|
||||||
ProgressListener* progress_listener,
|
ProgressListener* progress_listener,
|
||||||
KeySource* encryption_key_source,
|
KeySource* encryption_key_source,
|
||||||
|
@ -85,11 +85,9 @@ class Segmenter {
|
||||||
Status Finalize();
|
Status Finalize();
|
||||||
|
|
||||||
/// Add sample to the indicated stream.
|
/// Add sample to the indicated stream.
|
||||||
/// @param stream points to the stream to which the sample belongs. It cannot
|
|
||||||
/// be NULL.
|
|
||||||
/// @param sample points to the sample to be added.
|
/// @param sample points to the sample to be added.
|
||||||
/// @return OK on success, an error status otherwise.
|
/// @return OK on success, an error status otherwise.
|
||||||
Status AddSample(const MediaStream* stream,
|
Status AddSample(const StreamInfo& stream_Info,
|
||||||
std::shared_ptr<MediaSample> sample);
|
std::shared_ptr<MediaSample> sample);
|
||||||
|
|
||||||
/// @return true if there is an initialization range, while setting @a offset
|
/// @return true if there is an initialization range, while setting @a offset
|
||||||
|
@ -145,7 +143,6 @@ class Segmenter {
|
||||||
std::unique_ptr<SegmentIndex> sidx_;
|
std::unique_ptr<SegmentIndex> sidx_;
|
||||||
std::vector<std::unique_ptr<Fragmenter>> fragmenters_;
|
std::vector<std::unique_ptr<Fragmenter>> fragmenters_;
|
||||||
std::vector<uint64_t> segment_durations_;
|
std::vector<uint64_t> segment_durations_;
|
||||||
std::map<const MediaStream*, uint32_t> stream_map_;
|
|
||||||
MuxerListener* muxer_listener_;
|
MuxerListener* muxer_listener_;
|
||||||
ProgressListener* progress_listener_;
|
ProgressListener* progress_listener_;
|
||||||
uint64_t progress_target_;
|
uint64_t progress_target_;
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "packager/media/base/buffer_writer.h"
|
#include "packager/media/base/buffer_writer.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/event/muxer_listener.h"
|
#include "packager/media/event/muxer_listener.h"
|
||||||
#include "packager/media/event/progress_listener.h"
|
#include "packager/media/event/progress_listener.h"
|
||||||
|
|
|
@ -6,7 +6,6 @@
|
||||||
|
|
||||||
#include "packager/media/formats/webm/multi_segment_segmenter.h"
|
#include "packager/media/formats/webm/multi_segment_segmenter.h"
|
||||||
|
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include "packager/base/time/time.h"
|
#include "packager/base/time/time.h"
|
||||||
#include "packager/media/base/audio_stream_info.h"
|
#include "packager/media/base/audio_stream_info.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
|
|
|
@ -9,7 +9,6 @@
|
||||||
#include <algorithm>
|
#include <algorithm>
|
||||||
|
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer_options.h"
|
#include "packager/media/base/muxer_options.h"
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
#include "packager/media/file/file_util.h"
|
#include "packager/media/file/file_util.h"
|
||||||
|
|
|
@ -8,7 +8,6 @@
|
||||||
|
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
#include "packager/media/base/media_sample.h"
|
#include "packager/media/base/media_sample.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
#include "packager/media/formats/webm/mkv_writer.h"
|
#include "packager/media/formats/webm/mkv_writer.h"
|
||||||
#include "packager/media/formats/webm/multi_segment_segmenter.h"
|
#include "packager/media/formats/webm/multi_segment_segmenter.h"
|
||||||
|
@ -22,7 +21,7 @@ namespace webm {
|
||||||
WebMMuxer::WebMMuxer(const MuxerOptions& options) : Muxer(options) {}
|
WebMMuxer::WebMMuxer(const MuxerOptions& options) : Muxer(options) {}
|
||||||
WebMMuxer::~WebMMuxer() {}
|
WebMMuxer::~WebMMuxer() {}
|
||||||
|
|
||||||
Status WebMMuxer::Initialize() {
|
Status WebMMuxer::InitializeMuxer() {
|
||||||
CHECK_EQ(streams().size(), 1U);
|
CHECK_EQ(streams().size(), 1U);
|
||||||
|
|
||||||
if (crypto_period_duration_in_seconds() > 0) {
|
if (crypto_period_duration_in_seconds() > 0) {
|
||||||
|
@ -50,7 +49,7 @@ Status WebMMuxer::Initialize() {
|
||||||
}
|
}
|
||||||
|
|
||||||
Status initialized = segmenter_->Initialize(
|
Status initialized = segmenter_->Initialize(
|
||||||
std::move(writer), streams()[0]->info().get(), progress_listener(),
|
std::move(writer), streams()[0].get(), progress_listener(),
|
||||||
muxer_listener(), encryption_key_source(), max_sd_pixels(),
|
muxer_listener(), encryption_key_source(), max_sd_pixels(),
|
||||||
max_hd_pixels(), max_uhd1_pixels(), clear_lead_in_seconds());
|
max_hd_pixels(), max_uhd1_pixels(), clear_lead_in_seconds());
|
||||||
|
|
||||||
|
@ -73,10 +72,8 @@ Status WebMMuxer::Finalize() {
|
||||||
return Status::OK;
|
return Status::OK;
|
||||||
}
|
}
|
||||||
|
|
||||||
Status WebMMuxer::DoAddSample(const MediaStream* stream,
|
Status WebMMuxer::DoAddSample(std::shared_ptr<MediaSample> sample) {
|
||||||
std::shared_ptr<MediaSample> sample) {
|
|
||||||
DCHECK(segmenter_);
|
DCHECK(segmenter_);
|
||||||
DCHECK(stream == streams()[0]);
|
|
||||||
return segmenter_->AddSample(sample);
|
return segmenter_->AddSample(sample);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -86,9 +83,9 @@ void WebMMuxer::FireOnMediaStartEvent() {
|
||||||
|
|
||||||
DCHECK(!streams().empty()) << "Media started without a stream.";
|
DCHECK(!streams().empty()) << "Media started without a stream.";
|
||||||
|
|
||||||
const uint32_t timescale = streams().front()->info()->time_scale();
|
const uint32_t timescale = streams().front()->time_scale();
|
||||||
muxer_listener()->OnMediaStart(options(), *streams().front()->info(),
|
muxer_listener()->OnMediaStart(options(), *streams().front(), timescale,
|
||||||
timescale, MuxerListener::kContainerWebM);
|
MuxerListener::kContainerWebM);
|
||||||
}
|
}
|
||||||
|
|
||||||
void WebMMuxer::FireOnMediaEndEvent() {
|
void WebMMuxer::FireOnMediaEndEvent() {
|
||||||
|
|
|
@ -24,10 +24,9 @@ class WebMMuxer : public Muxer {
|
||||||
|
|
||||||
private:
|
private:
|
||||||
// Muxer implementation overrides.
|
// Muxer implementation overrides.
|
||||||
Status Initialize() override;
|
Status InitializeMuxer() override;
|
||||||
Status Finalize() override;
|
Status Finalize() override;
|
||||||
Status DoAddSample(const MediaStream* stream,
|
Status DoAddSample(std::shared_ptr<MediaSample> sample) override;
|
||||||
std::shared_ptr<MediaSample> sample) override;
|
|
||||||
|
|
||||||
void FireOnMediaStartEvent();
|
void FireOnMediaStartEvent();
|
||||||
void FireOnMediaEndEvent();
|
void FireOnMediaEndEvent();
|
||||||
|
|
|
@ -13,7 +13,6 @@
|
||||||
#include "packager/media/base/demuxer.h"
|
#include "packager/media/base/demuxer.h"
|
||||||
#include "packager/media/base/fixed_key_source.h"
|
#include "packager/media/base/fixed_key_source.h"
|
||||||
#include "packager/media/base/fourccs.h"
|
#include "packager/media/base/fourccs.h"
|
||||||
#include "packager/media/base/media_stream.h"
|
|
||||||
#include "packager/media/base/muxer.h"
|
#include "packager/media/base/muxer.h"
|
||||||
#include "packager/media/base/muxer_util.h"
|
#include "packager/media/base/muxer_util.h"
|
||||||
#include "packager/media/base/stream_info.h"
|
#include "packager/media/base/stream_info.h"
|
||||||
|
@ -50,7 +49,6 @@ const bool kSingleSegment = true;
|
||||||
const bool kMultipleSegments = false;
|
const bool kMultipleSegments = false;
|
||||||
const bool kEnableEncryption = true;
|
const bool kEnableEncryption = true;
|
||||||
const bool kDisableEncryption = false;
|
const bool kDisableEncryption = false;
|
||||||
const char kNoLanguageOverride[] = "";
|
|
||||||
|
|
||||||
// Encryption constants.
|
// Encryption constants.
|
||||||
const char kKeyIdHex[] = "e5007e6e9dcd5ac095202ed3758382cd";
|
const char kKeyIdHex[] = "e5007e6e9dcd5ac095202ed3758382cd";
|
||||||
|
@ -63,24 +61,6 @@ const uint32_t kMaxSDPixels = 640 * 480;
|
||||||
const uint32_t kMaxHDPixels = 1920 * 1080;
|
const uint32_t kMaxHDPixels = 1920 * 1080;
|
||||||
const uint32_t kMaxUHD1Pixels = 4096 * 2160;
|
const uint32_t kMaxUHD1Pixels = 4096 * 2160;
|
||||||
|
|
||||||
MediaStream* FindFirstStreamOfType(
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams,
|
|
||||||
StreamType stream_type) {
|
|
||||||
for (const std::unique_ptr<MediaStream>& stream : streams) {
|
|
||||||
if (stream->info()->stream_type() == stream_type)
|
|
||||||
return stream.get();
|
|
||||||
}
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
MediaStream* FindFirstVideoStream(
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams) {
|
|
||||||
return FindFirstStreamOfType(streams, kStreamVideo);
|
|
||||||
}
|
|
||||||
MediaStream* FindFirstAudioStream(
|
|
||||||
const std::vector<std::unique_ptr<MediaStream>>& streams) {
|
|
||||||
return FindFirstStreamOfType(streams, kStreamAudio);
|
|
||||||
}
|
|
||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
class FakeClock : public base::Clock {
|
class FakeClock : public base::Clock {
|
||||||
|
@ -114,8 +94,7 @@ class PackagerTestBasic : public ::testing::TestWithParam<const char*> {
|
||||||
const std::string& video_output,
|
const std::string& video_output,
|
||||||
const std::string& audio_output,
|
const std::string& audio_output,
|
||||||
bool single_segment,
|
bool single_segment,
|
||||||
bool enable_encryption,
|
bool enable_encryption);
|
||||||
const std::string& override_language);
|
|
||||||
|
|
||||||
void Decrypt(const std::string& input,
|
void Decrypt(const std::string& input,
|
||||||
const std::string& video_output,
|
const std::string& video_output,
|
||||||
|
@ -157,59 +136,46 @@ void PackagerTestBasic::Remux(const std::string& input,
|
||||||
const std::string& video_output,
|
const std::string& video_output,
|
||||||
const std::string& audio_output,
|
const std::string& audio_output,
|
||||||
bool single_segment,
|
bool single_segment,
|
||||||
bool enable_encryption,
|
bool enable_encryption) {
|
||||||
const std::string& language_override) {
|
|
||||||
CHECK(!video_output.empty() || !audio_output.empty());
|
CHECK(!video_output.empty() || !audio_output.empty());
|
||||||
|
|
||||||
Demuxer demuxer(GetFullPath(input));
|
Demuxer demuxer(GetFullPath(input));
|
||||||
ASSERT_OK(demuxer.Initialize());
|
|
||||||
|
|
||||||
std::unique_ptr<KeySource> encryption_key_source(
|
std::unique_ptr<KeySource> encryption_key_source(
|
||||||
FixedKeySource::CreateFromHexStrings(kKeyIdHex, kKeyHex, "", ""));
|
FixedKeySource::CreateFromHexStrings(kKeyIdHex, kKeyHex, "", ""));
|
||||||
DCHECK(encryption_key_source);
|
DCHECK(encryption_key_source);
|
||||||
|
|
||||||
std::unique_ptr<Muxer> muxer_video;
|
std::shared_ptr<Muxer> muxer_video;
|
||||||
if (!video_output.empty()) {
|
if (!video_output.empty()) {
|
||||||
muxer_video.reset(
|
muxer_video.reset(
|
||||||
new mp4::MP4Muxer(SetupOptions(video_output, single_segment)));
|
new mp4::MP4Muxer(SetupOptions(video_output, single_segment)));
|
||||||
muxer_video->set_clock(&fake_clock_);
|
muxer_video->set_clock(&fake_clock_);
|
||||||
|
|
||||||
MediaStream* stream = FindFirstVideoStream(demuxer.streams());
|
|
||||||
if (!language_override.empty()) {
|
|
||||||
stream->info()->set_language(language_override);
|
|
||||||
ASSERT_EQ(language_override, stream->info()->language());
|
|
||||||
}
|
|
||||||
muxer_video->AddStream(stream);
|
|
||||||
|
|
||||||
if (enable_encryption) {
|
if (enable_encryption) {
|
||||||
muxer_video->SetKeySource(encryption_key_source.get(),
|
muxer_video->SetKeySource(encryption_key_source.get(),
|
||||||
kMaxSDPixels, kMaxHDPixels,
|
kMaxSDPixels, kMaxHDPixels,
|
||||||
kMaxUHD1Pixels, kClearLeadInSeconds,
|
kMaxUHD1Pixels, kClearLeadInSeconds,
|
||||||
kCryptoDurationInSeconds, FOURCC_cenc);
|
kCryptoDurationInSeconds, FOURCC_cenc);
|
||||||
}
|
}
|
||||||
|
ASSERT_OK(demuxer.SetHandler("video", muxer_video));
|
||||||
}
|
}
|
||||||
|
|
||||||
std::unique_ptr<Muxer> muxer_audio;
|
std::shared_ptr<Muxer> muxer_audio;
|
||||||
if (!audio_output.empty()) {
|
if (!audio_output.empty()) {
|
||||||
muxer_audio.reset(
|
muxer_audio.reset(
|
||||||
new mp4::MP4Muxer(SetupOptions(audio_output, single_segment)));
|
new mp4::MP4Muxer(SetupOptions(audio_output, single_segment)));
|
||||||
muxer_audio->set_clock(&fake_clock_);
|
muxer_audio->set_clock(&fake_clock_);
|
||||||
|
|
||||||
MediaStream* stream = FindFirstAudioStream(demuxer.streams());
|
|
||||||
if (!language_override.empty()) {
|
|
||||||
stream->info()->set_language(language_override);
|
|
||||||
ASSERT_EQ(language_override, stream->info()->language());
|
|
||||||
}
|
|
||||||
muxer_audio->AddStream(stream);
|
|
||||||
|
|
||||||
if (enable_encryption) {
|
if (enable_encryption) {
|
||||||
muxer_audio->SetKeySource(encryption_key_source.get(),
|
muxer_audio->SetKeySource(encryption_key_source.get(),
|
||||||
kMaxSDPixels, kMaxHDPixels,
|
kMaxSDPixels, kMaxHDPixels,
|
||||||
kMaxUHD1Pixels, kClearLeadInSeconds,
|
kMaxUHD1Pixels, kClearLeadInSeconds,
|
||||||
kCryptoDurationInSeconds, FOURCC_cenc);
|
kCryptoDurationInSeconds, FOURCC_cenc);
|
||||||
}
|
}
|
||||||
|
ASSERT_OK(demuxer.SetHandler("audio", muxer_audio));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
ASSERT_OK(demuxer.Initialize());
|
||||||
// Start remuxing process.
|
// Start remuxing process.
|
||||||
ASSERT_OK(demuxer.Run());
|
ASSERT_OK(demuxer.Run());
|
||||||
}
|
}
|
||||||
|
@ -224,25 +190,20 @@ void PackagerTestBasic::Decrypt(const std::string& input,
|
||||||
FixedKeySource::CreateFromHexStrings(kKeyIdHex, kKeyHex, "", ""));
|
FixedKeySource::CreateFromHexStrings(kKeyIdHex, kKeyHex, "", ""));
|
||||||
ASSERT_TRUE(decryption_key_source);
|
ASSERT_TRUE(decryption_key_source);
|
||||||
demuxer.SetKeySource(std::move(decryption_key_source));
|
demuxer.SetKeySource(std::move(decryption_key_source));
|
||||||
ASSERT_OK(demuxer.Initialize());
|
|
||||||
|
|
||||||
std::unique_ptr<Muxer> muxer;
|
std::shared_ptr<Muxer> muxer;
|
||||||
MediaStream* stream(NULL);
|
|
||||||
if (!video_output.empty()) {
|
if (!video_output.empty()) {
|
||||||
muxer.reset(
|
muxer.reset(
|
||||||
new mp4::MP4Muxer(SetupOptions(video_output, true)));
|
new mp4::MP4Muxer(SetupOptions(video_output, true)));
|
||||||
stream = FindFirstVideoStream(demuxer.streams());
|
|
||||||
}
|
}
|
||||||
if (!audio_output.empty()) {
|
if (!audio_output.empty()) {
|
||||||
muxer.reset(
|
muxer.reset(
|
||||||
new mp4::MP4Muxer(SetupOptions(audio_output, true)));
|
new mp4::MP4Muxer(SetupOptions(audio_output, true)));
|
||||||
stream = FindFirstAudioStream(demuxer.streams());
|
|
||||||
}
|
}
|
||||||
ASSERT_TRUE(muxer);
|
ASSERT_TRUE(muxer);
|
||||||
ASSERT_TRUE(stream != NULL);
|
|
||||||
ASSERT_TRUE(stream->info()->is_encrypted());
|
|
||||||
muxer->set_clock(&fake_clock_);
|
muxer->set_clock(&fake_clock_);
|
||||||
muxer->AddStream(stream);
|
ASSERT_OK(demuxer.SetHandler("0", muxer));
|
||||||
|
ASSERT_OK(demuxer.Initialize());
|
||||||
|
|
||||||
ASSERT_OK(demuxer.Run());
|
ASSERT_OK(demuxer.Run());
|
||||||
}
|
}
|
||||||
|
@ -252,8 +213,7 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedVideo) {
|
||||||
kOutputVideo,
|
kOutputVideo,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedAudio) {
|
TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedAudio) {
|
||||||
|
@ -261,8 +221,7 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedAudio) {
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kOutputAudio,
|
kOutputAudio,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedVideo) {
|
TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedVideo) {
|
||||||
|
@ -270,8 +229,7 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedVideo) {
|
||||||
kOutputVideo,
|
kOutputVideo,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kEnableEncryption,
|
kEnableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(Decrypt(kOutputVideo,
|
ASSERT_NO_FATAL_FAILURE(Decrypt(kOutputVideo,
|
||||||
kOutputVideo2,
|
kOutputVideo2,
|
||||||
|
@ -283,76 +241,13 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedAudio) {
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kOutputAudio,
|
kOutputAudio,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kEnableEncryption,
|
kEnableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(Decrypt(kOutputAudio,
|
ASSERT_NO_FATAL_FAILURE(Decrypt(kOutputAudio,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kOutputAudio2));
|
kOutputAudio2));
|
||||||
}
|
}
|
||||||
|
|
||||||
TEST_P(PackagerTestBasic, MP4MuxerLanguageWithoutSubtag) {
|
|
||||||
ASSERT_NO_FATAL_FAILURE(Remux(GetParam(),
|
|
||||||
kOutputNone,
|
|
||||||
kOutputAudio,
|
|
||||||
kSingleSegment,
|
|
||||||
kDisableEncryption,
|
|
||||||
"por"));
|
|
||||||
|
|
||||||
Demuxer demuxer(GetFullPath(kOutputAudio));
|
|
||||||
ASSERT_OK(demuxer.Initialize());
|
|
||||||
|
|
||||||
MediaStream* stream = FindFirstAudioStream(demuxer.streams());
|
|
||||||
ASSERT_EQ("por", stream->info()->language());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(PackagerTestBasic, MP4MuxerLanguageWithSubtag) {
|
|
||||||
ASSERT_NO_FATAL_FAILURE(Remux(GetParam(),
|
|
||||||
kOutputNone,
|
|
||||||
kOutputAudio,
|
|
||||||
kSingleSegment,
|
|
||||||
kDisableEncryption,
|
|
||||||
"por-BR"));
|
|
||||||
|
|
||||||
Demuxer demuxer(GetFullPath(kOutputAudio));
|
|
||||||
ASSERT_OK(demuxer.Initialize());
|
|
||||||
|
|
||||||
MediaStream* stream = FindFirstAudioStream(demuxer.streams());
|
|
||||||
ASSERT_EQ("por", stream->info()->language());
|
|
||||||
}
|
|
||||||
|
|
||||||
TEST_P(PackagerTestBasic, GetTrackTypeForEncryption) {
|
|
||||||
Demuxer demuxer(GetFullPath(GetParam()));
|
|
||||||
ASSERT_OK(demuxer.Initialize());
|
|
||||||
|
|
||||||
MediaStream* video_stream = FindFirstVideoStream(demuxer.streams());
|
|
||||||
MediaStream* audio_stream = FindFirstAudioStream(demuxer.streams());
|
|
||||||
|
|
||||||
// Typical resolution constraints should set the resolution in the SD range
|
|
||||||
KeySource::TrackType track_type = GetTrackTypeForEncryption(
|
|
||||||
*video_stream->info(), kMaxSDPixels, kMaxHDPixels, kMaxUHD1Pixels);
|
|
||||||
ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("SD"), track_type);
|
|
||||||
|
|
||||||
// Setting the max SD value to 1 should set the resolution in the HD range
|
|
||||||
track_type = GetTrackTypeForEncryption(
|
|
||||||
*video_stream->info(), 1, kMaxHDPixels, kMaxUHD1Pixels);
|
|
||||||
ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("HD"), track_type);
|
|
||||||
|
|
||||||
// Setting the max HD value to 2 should set the resolution in the UHD1 range
|
|
||||||
track_type = GetTrackTypeForEncryption(
|
|
||||||
*video_stream->info(), 1, 2, kMaxUHD1Pixels);
|
|
||||||
ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("UHD1"), track_type);
|
|
||||||
|
|
||||||
// Setting the max UHD1 value to 3 should set the resolution in the UHD2 range
|
|
||||||
track_type = GetTrackTypeForEncryption(
|
|
||||||
*video_stream->info(), 1, 2, 3);
|
|
||||||
ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("UHD2"), track_type);
|
|
||||||
|
|
||||||
// Audio stream should always set the track_type to AUDIO
|
|
||||||
track_type = GetTrackTypeForEncryption(
|
|
||||||
*audio_stream->info(), kMaxSDPixels, kMaxHDPixels, kMaxUHD1Pixels);
|
|
||||||
ASSERT_EQ(FixedKeySource::GetTrackTypeFromString("AUDIO"), track_type);
|
|
||||||
}
|
|
||||||
|
|
||||||
class PackagerTest : public PackagerTestBasic {
|
class PackagerTest : public PackagerTestBasic {
|
||||||
public:
|
public:
|
||||||
|
@ -363,15 +258,13 @@ class PackagerTest : public PackagerTestBasic {
|
||||||
kOutputVideo,
|
kOutputVideo,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
|
|
||||||
ASSERT_NO_FATAL_FAILURE(Remux(GetParam(),
|
ASSERT_NO_FATAL_FAILURE(Remux(GetParam(),
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kOutputAudio,
|
kOutputAudio,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
|
@ -382,8 +275,7 @@ TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencryptedVideoAgain) {
|
||||||
kOutputVideo2,
|
kOutputVideo2,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2));
|
EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -394,8 +286,7 @@ TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencryptedAudioAgain) {
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kOutputAudio2,
|
kOutputAudio2,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
EXPECT_TRUE(ContentsEqual(kOutputAudio, kOutputAudio2));
|
EXPECT_TRUE(ContentsEqual(kOutputAudio, kOutputAudio2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -404,8 +295,7 @@ TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencryptedSeparateAudioVideo) {
|
||||||
kOutputVideo2,
|
kOutputVideo2,
|
||||||
kOutputAudio2,
|
kOutputAudio2,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
|
|
||||||
// Compare the output with single muxer output. They should match.
|
// Compare the output with single muxer output. They should match.
|
||||||
EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2));
|
EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2));
|
||||||
|
@ -417,8 +307,7 @@ TEST_P(PackagerTest, MP4MuxerMultiSegmentsUnencryptedVideo) {
|
||||||
kOutputVideo2,
|
kOutputVideo2,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kMultipleSegments,
|
kMultipleSegments,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
|
|
||||||
// Find and concatenates the segments.
|
// Find and concatenates the segments.
|
||||||
const std::string kOutputVideoSegmentsCombined =
|
const std::string kOutputVideoSegmentsCombined =
|
||||||
|
@ -452,8 +341,7 @@ TEST_P(PackagerTest, MP4MuxerMultiSegmentsUnencryptedVideo) {
|
||||||
kOutputVideo2,
|
kOutputVideo2,
|
||||||
kOutputNone,
|
kOutputNone,
|
||||||
kSingleSegment,
|
kSingleSegment,
|
||||||
kDisableEncryption,
|
kDisableEncryption));
|
||||||
kNoLanguageOverride));
|
|
||||||
EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2));
|
EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in New Issue