diff --git a/packager/app/packager_main.cc b/packager/app/packager_main.cc index 060b5d895b..284dc95a8f 100644 --- a/packager/app/packager_main.cc +++ b/packager/app/packager_main.cc @@ -168,10 +168,6 @@ class RemuxJob : public base::SimpleThread { ~RemuxJob() override {} - void AddMuxer(std::unique_ptr mux) { - muxers_.push_back(std::move(mux)); - } - Demuxer* demuxer() { return demuxer_.get(); } Status status() { return status_; } @@ -182,7 +178,6 @@ class RemuxJob : public base::SimpleThread { } std::unique_ptr demuxer_; - std::vector> muxers_; Status status_; DISALLOW_COPY_AND_ASSIGN(RemuxJob); @@ -227,15 +222,15 @@ bool StreamInfoToTextMediaInfo(const StreamDescriptor& stream_descriptor, return true; } -std::unique_ptr CreateOutputMuxer(const MuxerOptions& options, +std::shared_ptr CreateOutputMuxer(const MuxerOptions& options, MediaContainerName container) { if (container == CONTAINER_WEBM) { - return std::unique_ptr(new webm::WebMMuxer(options)); + return std::shared_ptr(new webm::WebMMuxer(options)); } else if (container == CONTAINER_MPEG2TS) { - return std::unique_ptr(new mp2t::TsMuxer(options)); + return std::shared_ptr(new mp2t::TsMuxer(options)); } else { DCHECK_EQ(container, CONTAINER_MOV); - return std::unique_ptr(new mp4::MP4Muxer(options)); + return std::shared_ptr(new mp4::MP4Muxer(options)); } } @@ -300,6 +295,7 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, if (stream_iter->input != previous_input) { // New remux job needed. Create demux and job thread. std::unique_ptr demuxer(new Demuxer(stream_iter->input)); + demuxer->set_dump_stream_info(FLAGS_dump_stream_info); if (FLAGS_enable_widevine_decryption || FLAGS_enable_fixed_key_decryption) { std::unique_ptr key_source(CreateDecryptionKeySource()); @@ -307,23 +303,15 @@ bool CreateRemuxJobs(const StreamDescriptorList& stream_descriptors, return false; 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))); previous_input = stream_iter->input; + // Skip setting up muxers if output is not needed. + if (stream_iter->output.empty()) + continue; } DCHECK(!remux_jobs->empty()); - std::unique_ptr muxer( + std::shared_ptr muxer( CreateOutputMuxer(stream_muxer_options, stream_iter->output_format)); 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) muxer->SetMuxerListener(std::move(muxer_listener)); - if (!AddStreamToMuxer(remux_jobs->back()->demuxer()->streams(), - stream_iter->stream_selector, - stream_iter->language, - muxer.get())) { + auto* demuxer = remux_jobs->back()->demuxer(); + const std::string& stream_selector = stream_iter->stream_selector; + Status status = demuxer->SetHandler(stream_selector, std::move(muxer)); + if (!status.ok()) { + LOG(ERROR) << "Demuxer::SetHandler failed " << status; 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& job : *remux_jobs) { + Status status = job->demuxer()->Initialize(); + if (!status.ok()) { + LOG(ERROR) << "Failed to initialize processing graph " << status; + return false; + } + } return true; } diff --git a/packager/app/packager_util.cc b/packager/app/packager_util.cc index bf46af06a4..13158ebf2b 100644 --- a/packager/app/packager_util.cc +++ b/packager/app/packager_util.cc @@ -17,15 +17,12 @@ #include "packager/base/logging.h" #include "packager/base/strings/string_number_conversions.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/playready_key_source.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/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, false, @@ -38,12 +35,6 @@ DEFINE_bool(dump_stream_info, false, "Dump demuxed stream info."); namespace shaka { namespace media { -void DumpStreamInfo(const std::vector>& 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 CreateSigner() { std::unique_ptr signer; @@ -196,63 +187,5 @@ bool GetMpdOptions(bool on_demand_profile, MpdOptions* mpd_options) { return true; } -MediaStream* FindFirstStreamOfType( - const std::vector>& streams, - StreamType stream_type) { - for (const std::unique_ptr& stream : streams) { - if (stream->info()->stream_type() == stream_type) - return stream.get(); - } - return nullptr; -} -MediaStream* FindFirstVideoStream( - const std::vector>& streams) { - return FindFirstStreamOfType(streams, kStreamVideo); -} -MediaStream* FindFirstAudioStream( - const std::vector>& streams) { - return FindFirstStreamOfType(streams, kStreamAudio); -} - -bool AddStreamToMuxer(const std::vector>& 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 shaka diff --git a/packager/app/packager_util.h b/packager/app/packager_util.h index ec1abad0dd..684ced2ffd 100644 --- a/packager/app/packager_util.h +++ b/packager/app/packager_util.h @@ -6,13 +6,12 @@ // // Packager utility functions. -#ifndef APP_PACKAGER_UTIL_H_ -#define APP_PACKAGER_UTIL_H_ +#ifndef PACKAGER_APP_PACKAGER_UTIL_H_ +#define PACKAGER_APP_PACKAGER_UTIL_H_ #include + #include -#include -#include DECLARE_bool(dump_stream_info); @@ -23,13 +22,8 @@ struct MpdOptions; namespace media { class KeySource; -class MediaStream; -class Muxer; struct MuxerOptions; -/// Print all the stream info for the provided strings to standard output. -void DumpStreamInfo(const std::vector>& streams); - /// Create KeySource based on provided command line options for content /// encryption. Also fetches keys. /// @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. 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>& streams, - const std::string& stream_selector, - const std::string& language_override, - Muxer* muxer); - } // namespace media } // namespace shaka -#endif // APP_PACKAGER_UTIL_H_ +#endif // PACKAGER_APP_PACKAGER_UTIL_H_ diff --git a/packager/app/test/packager_test.py b/packager/app/test/packager_test.py index 51e86d9944..aa16c2d0e1 100755 --- a/packager/app/test/packager_test.py +++ b/packager/app/test/packager_test.py @@ -92,6 +92,22 @@ class PackagerAppTest(unittest.TestCase): self._DiffGold(self.output[1], 'bear-640x360-v-golden.mp4') 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. def testPackageVideoAudioText(self): audio_video_streams = self._GetStreams(['audio', 'video']) @@ -438,6 +454,7 @@ class PackagerAppTest(unittest.TestCase): def _GetStreams(self, stream_descriptors, + language_override=None, output_format=None, live=False, test_files=None): @@ -466,9 +483,6 @@ class PackagerAppTest(unittest.TestCase): 'input=%s,stream=%s,init_segment=%s-init.mp4,' 'segment_template=%s-$Number$.m4s' % (test_file, stream_descriptor, output_prefix, output_prefix)) - if output_format: - stream += ',format=%s' % output_format - streams.append(stream) self.output.append(output_prefix) else: output = '%s.%s' % ( @@ -476,10 +490,12 @@ class PackagerAppTest(unittest.TestCase): self._GetExtension(stream_descriptor, output_format)) stream = ('input=%s,stream=%s,output=%s' % (test_file, stream_descriptor, output)) - if output_format: - stream += ',format=%s' % output_format - streams.append(stream) self.output.append(output) + if output_format: + stream += ',format=%s' % output_format + if language_override: + stream += ',lang=%s' % language_override + streams.append(stream) return streams def _GetExtension(self, stream_descriptor, output_format): diff --git a/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4 new file mode 100644 index 0000000000..d50176a494 Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-por-BR-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-a-por-golden.mp4 b/packager/app/test/testdata/bear-640x360-a-por-golden.mp4 new file mode 100644 index 0000000000..d50176a494 Binary files /dev/null and b/packager/app/test/testdata/bear-640x360-a-por-golden.mp4 differ diff --git a/packager/app/test/testdata/bear-640x360-av-por-BR-golden.mpd b/packager/app/test/testdata/bear-640x360-av-por-BR-golden.mpd new file mode 100644 index 0000000000..30687a598c --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-av-por-BR-golden.mpd @@ -0,0 +1,23 @@ + + + + + + + output_video.mp4 + + + + + + + + + output_audio.mp4 + + + + + + + diff --git a/packager/app/test/testdata/bear-640x360-av-por-golden.mpd b/packager/app/test/testdata/bear-640x360-av-por-golden.mpd new file mode 100644 index 0000000000..30687a598c --- /dev/null +++ b/packager/app/test/testdata/bear-640x360-av-por-golden.mpd @@ -0,0 +1,23 @@ + + + + + + + output_video.mp4 + + + + + + + + + output_audio.mp4 + + + + + + + diff --git a/packager/media/base/demuxer.cc b/packager/media/base/demuxer.cc index ccd71cae6e..93e90e1962 100644 --- a/packager/media/base/demuxer.cc +++ b/packager/media/base/demuxer.cc @@ -6,12 +6,14 @@ #include "packager/media/base/demuxer.h" +#include + #include "packager/base/bind.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/key_source.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/file/file.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 // set here is arbitrary though. 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 media { Demuxer::Demuxer(const std::string& file_name) - : file_name_(file_name), - media_file_(NULL), - init_event_received_(false), - container_name_(CONTAINER_UNKNOWN), - buffer_(new uint8_t[kBufSize]), - cancelled_(false) { -} + : file_name_(file_name), buffer_(new uint8_t[kBufSize]) {} Demuxer::~Demuxer() { if (media_file_) @@ -51,9 +79,74 @@ void Demuxer::SetKeySource(std::unique_ptr 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 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 local_sample) + : track_id(local_track_id), sample(local_sample) {} + +Demuxer::QueuedSample::~QueuedSample() {} + +Status Demuxer::InitializeParser() { DCHECK(!media_file_); - DCHECK(!init_event_received_); + DCHECK(!all_streams_ready_); LOG(INFO) << "Initialize Demuxer for file '" << file_name_ << "'."; @@ -105,34 +198,65 @@ Status Demuxer::Initialize() { // Handle trailing 'moov'. if (container_name_ == CONTAINER_MOV) static_cast(parser_.get())->LoadMoov(file_name_); - if (!parser_->Parse(buffer_.get(), bytes_read)) { - init_parsing_status_ = - Status(error::PARSER_FAILURE, "Cannot parse media file " + file_name_); + return Status(error::PARSER_FAILURE, + "Cannot parse media file " + file_name_); } - - // 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_; + return Status::OK; } void Demuxer::ParserInitEvent( const std::vector>& stream_infos) { - init_event_received_ = true; - for (const std::shared_ptr& stream_info : stream_infos) - streams_.emplace_back(new MediaStream(stream_info, this)); -} + if (dump_stream_info_) { + printf("\nFile \"%s\":\n", file_name_.c_str()); + 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, - std::shared_ptr local_sample) - : track_id(local_track_id), sample(local_sample) {} -Demuxer::QueuedSample::~QueuedSample() {} + int base_stream_index = 0; + bool video_handler_set = + output_handlers().find(kBaseVideoOutputStreamIndex) != + output_handlers().end(); + bool audio_handler_set = + output_handlers().find(kBaseAudioOutputStreamIndex) != + output_handlers().end(); + for (const std::shared_ptr& 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, const std::shared_ptr& sample) { - if (!init_event_received_) { + if (!all_streams_ready_) { if (queued_samples_.size() >= kQueuedSamplesLimit) { LOG(ERROR) << "Queued samples limit reached: " << kQueuedSamplesLimit; return false; @@ -152,46 +276,19 @@ bool Demuxer::NewSampleEvent(uint32_t track_id, bool Demuxer::PushSample(uint32_t track_id, const std::shared_ptr& sample) { - for (const std::unique_ptr& stream : streams_) { - if (track_id == stream->info()->track_id()) { - Status status = stream->PushSample(sample); - if (!status.ok()) - LOG(ERROR) << "Demuxer::PushSample failed with " << status; - return status.ok(); - } + auto stream_index_iter = track_id_to_stream_index_map_.find(track_id); + if (stream_index_iter == track_id_to_stream_index_map_.end()) { + LOG(ERROR) << "Track " << track_id << " not found."; + return false; } - LOG(ERROR) << "Track " << track_id << " not found."; - return false; -} - -Status Demuxer::Run() { - Status status; - - LOG(INFO) << "Demuxer::Run() on file '" << file_name_ << "'."; - - // Start the streams. - for (const std::unique_ptr& stream : streams_) { - status = stream->Start(MediaStream::kPush); - if (!status.ok()) - return status; + if (stream_index_iter->second == kInvalidStreamIndex) + return true; + Status status = DispatchMediaSample(stream_index_iter->second, sample); + if (!status.ok()) { + LOG(ERROR) << "Failed to process sample " << stream_index_iter->second + << " " << status; } - - 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& sample = MediaSample::CreateEOSBuffer(); - for (const std::unique_ptr& stream : streams_) { - status = stream->PushSample(sample); - if (!status.ok()) - return status; - } - } - return status; + return status.ok(); } Status Demuxer::Parse() { @@ -199,11 +296,6 @@ Status Demuxer::Parse() { DCHECK(parser_); 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); if (bytes_read == 0) { if (!parser_->Flush()) @@ -219,9 +311,5 @@ Status Demuxer::Parse() { "Cannot parse media file " + file_name_); } -void Demuxer::Cancel() { - cancelled_ = true; -} - } // namespace media } // namespace shaka diff --git a/packager/media/base/demuxer.h b/packager/media/base/demuxer.h index 48ea2418b8..d2527e0fea 100644 --- a/packager/media/base/demuxer.h +++ b/packager/media/base/demuxer.h @@ -13,6 +13,7 @@ #include "packager/base/compiler_specific.h" #include "packager/media/base/container_names.h" +#include "packager/media/base/media_handler.h" #include "packager/media/base/status.h" namespace shaka { @@ -28,7 +29,7 @@ class StreamInfo; /// Demuxer is responsible for extracting elementary stream samples from a /// media file, e.g. an ISO BMFF file. -class Demuxer { +class Demuxer : public MediaHandler { public: /// @param file_name specifies the input source. It uses prefix matching to /// create a proper File object. The user can extend File to support @@ -42,36 +43,52 @@ class Demuxer { /// demuxed. void SetKeySource(std::unique_ptr 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 /// the Data to Muxer until Eof. 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 /// status of type CANCELLED. 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>& streams() { - return streams_; - } - /// @return Container name (type). Value is CONTAINER_UNKNOWN if the demuxer /// is not initialized. 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 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 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: Demuxer(const Demuxer&) = delete; Demuxer& operator=(const Demuxer&) = delete; @@ -84,6 +101,11 @@ class Demuxer { std::shared_ptr 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. void ParserInitEvent(const std::vector>& streams); // 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, const std::shared_ptr& sample); + // Read from the source and send it to the parser. + Status Parse(); + std::string file_name_; - File* media_file_; - bool init_event_received_; - Status init_parsing_status_; + File* media_file_ = nullptr; + // A stream is considered ready after receiving the stream info. + bool all_streams_ready_ = false; // Queued samples received in NewSampleEvent() before ParserInitEvent(). std::deque queued_samples_; std::unique_ptr parser_; - std::vector> streams_; - MediaContainerName container_name_; + // TrackId -> StreamIndex map. + std::map 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 stream_indexes_; + // StreamIndex -> language_override map. + std::map language_overrides_; + MediaContainerName container_name_ = CONTAINER_UNKNOWN; std::unique_ptr buffer_; std::unique_ptr key_source_; - bool cancelled_; + bool cancelled_ = false; + // Whether to dump stream info when it is received. + bool dump_stream_info_ = false; }; } // namespace media diff --git a/packager/media/base/media_base.gyp b/packager/media/base/media_base.gyp index 608d46bb9e..5ecc2fb0b7 100644 --- a/packager/media/base/media_base.gyp +++ b/packager/media/base/media_base.gyp @@ -59,8 +59,6 @@ 'media_parser.h', 'media_sample.cc', 'media_sample.h', - 'media_stream.cc', - 'media_stream.h', 'muxer.cc', 'muxer.h', 'muxer_options.cc', diff --git a/packager/media/base/media_handler.h b/packager/media/base/media_handler.h index 560549aa0f..8c44c6f6b1 100644 --- a/packager/media/base/media_handler.h +++ b/packager/media/base/media_handler.h @@ -172,6 +172,10 @@ class MediaHandler { int num_input_streams() const { return num_input_streams_; } int next_output_stream_index() const { return next_output_stream_index_; } + const std::map, int>>& + output_handlers() { + return output_handlers_; + } private: MediaHandler(const MediaHandler&) = delete; diff --git a/packager/media/base/media_stream.cc b/packager/media/base/media_stream.cc deleted file mode 100644 index ef7f55d3c2..0000000000 --- a/packager/media/base/media_stream.cc +++ /dev/null @@ -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 info, Demuxer* demuxer) - : info_(info), demuxer_(demuxer), muxer_(NULL), state_(kIdle) {} - -MediaStream::~MediaStream() {} - -Status MediaStream::PullSample(std::shared_ptr* 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& 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 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 diff --git a/packager/media/base/media_stream.h b/packager/media/base/media_stream.h deleted file mode 100644 index 0335b68a3b..0000000000 --- a/packager/media/base/media_stream.h +++ /dev/null @@ -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 -#include - -#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 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& sample); - - /// Pull sample from Demuxer (triggered by Muxer). - Status PullSample(std::shared_ptr* sample); - - Demuxer* demuxer() { return demuxer_; } - Muxer* muxer() { return muxer_; } - const std::shared_ptr 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 info_; - Demuxer* demuxer_; - Muxer* muxer_; - State state_; - // An internal buffer to store samples temporarily. - std::deque> samples_; -}; - -} // namespace media -} // namespace shaka - -#endif // MEDIA_BASE_MEDIA_STREAM_H_ diff --git a/packager/media/base/muxer.cc b/packager/media/base/muxer.cc index 194fa1d0db..d3cda19f4a 100644 --- a/packager/media/base/muxer.cc +++ b/packager/media/base/muxer.cc @@ -10,14 +10,12 @@ #include "packager/media/base/fourccs.h" #include "packager/media/base/media_sample.h" -#include "packager/media/base/media_stream.h" namespace shaka { namespace media { Muxer::Muxer(const MuxerOptions& options) : options_(options), - initialized_(false), encryption_key_source_(NULL), max_sd_pixels_(0), max_hd_pixels_(0), @@ -47,46 +45,6 @@ void Muxer::SetKeySource(KeySource* encryption_key_source, 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::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 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() { cancelled_ = true; } @@ -100,26 +58,21 @@ void Muxer::SetProgressListener( progress_listener_ = std::move(progress_listener); } -Status Muxer::AddSample(const MediaStream* stream, - std::shared_ptr sample) { - DCHECK(std::find(streams_.begin(), streams_.end(), stream) != streams_.end()); - - if (!initialized_) { - Status status = Initialize(); - if (!status.ok()) - return status; - initialized_ = true; +Status Muxer::Process(std::unique_ptr stream_data) { + Status status; + switch (stream_data->stream_data_type) { + case StreamDataType::kStreamInfo: + streams_.push_back(std::move(stream_data->stream_info)); + return InitializeMuxer(); + case StreamDataType::kMediaSample: + return DoAddSample(stream_data->media_sample); + default: + VLOG(3) << "Stream data type " + << static_cast(stream_data->stream_data_type) << " ignored."; + break; } - if (sample->end_of_stream()) { - // EOS sample should be sent only when the sample was pushed from Demuxer - // 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); + // No dispatch for muxer. + return Status::OK; } } // namespace media diff --git a/packager/media/base/muxer.h b/packager/media/base/muxer.h index ae518cc575..9811b3c169 100644 --- a/packager/media/base/muxer.h +++ b/packager/media/base/muxer.h @@ -14,6 +14,7 @@ #include "packager/base/time/clock.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/status.h" #include "packager/media/event/muxer_listener.h" @@ -29,7 +30,7 @@ class MediaStream; /// Muxer is responsible for taking elementary stream samples and producing /// media containers. An optional KeySource can be provided to Muxer /// to generate encrypted outputs. -class Muxer { +class Muxer : public MediaHandler { public: explicit Muxer(const MuxerOptions& options); virtual ~Muxer(); @@ -65,12 +66,6 @@ class Muxer { double crypto_period_duration_in_seconds, 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 /// status of type CANCELLED. void Cancel(); @@ -83,7 +78,9 @@ class Muxer { /// @param progress_listener should not be NULL. void SetProgressListener(std::unique_ptr progress_listener); - const std::vector& streams() const { return streams_; } + const std::vector>& streams() const { + return streams_; + } /// Inject clock, mainly used for testing. /// The injected clock will be used to generate the creation time-stamp and @@ -96,6 +93,13 @@ class Muxer { } protected: + /// @name MediaHandler implementation overrides. + /// @{ + Status InitializeInternal() override { return Status::OK; } + Status Process(std::unique_ptr stream_data) override; + Status FlushStream(int input_stream_index) override { return Finalize(); } + /// @} + const MuxerOptions& options() const { return options_; } KeySource* encryption_key_source() { return encryption_key_source_; @@ -113,25 +117,17 @@ class Muxer { FourCC protection_scheme() const { return protection_scheme_; } private: - friend class MediaStream; // Needed to access AddSample. - - // Add new media sample. - Status AddSample(const MediaStream* stream, - std::shared_ptr sample); - // Initialize the muxer. - virtual Status Initialize() = 0; + virtual Status InitializeMuxer() = 0; // Final clean up. virtual Status Finalize() = 0; // AddSample implementation. - virtual Status DoAddSample(const MediaStream* stream, - std::shared_ptr sample) = 0; + virtual Status DoAddSample(std::shared_ptr sample) = 0; MuxerOptions options_; - bool initialized_; - std::vector streams_; + std::vector> streams_; KeySource* encryption_key_source_; uint32_t max_sd_pixels_; uint32_t max_hd_pixels_; diff --git a/packager/media/formats/mp2t/ts_muxer.cc b/packager/media/formats/mp2t/ts_muxer.cc index 75ea2cfd88..db688c728c 100644 --- a/packager/media/formats/mp2t/ts_muxer.cc +++ b/packager/media/formats/mp2t/ts_muxer.cc @@ -17,15 +17,14 @@ const uint32_t kTsTimescale = 90000; TsMuxer::TsMuxer(const MuxerOptions& muxer_options) : Muxer(muxer_options) {} TsMuxer::~TsMuxer() {} -Status TsMuxer::Initialize() { +Status TsMuxer::InitializeMuxer() { if (streams().size() > 1u) return Status(error::MUXER_FAILURE, "Cannot handle more than one streams."); segmenter_.reset(new TsSegmenter(options(), muxer_listener())); - Status status = - segmenter_->Initialize(*streams()[0]->info(), encryption_key_source(), - max_sd_pixels(), max_hd_pixels(), - max_uhd1_pixels(), clear_lead_in_seconds()); + Status status = segmenter_->Initialize( + *streams()[0], encryption_key_source(), max_sd_pixels(), max_hd_pixels(), + max_uhd1_pixels(), clear_lead_in_seconds()); FireOnMediaStartEvent(); return status; } @@ -35,16 +34,15 @@ Status TsMuxer::Finalize() { return segmenter_->Finalize(); } -Status TsMuxer::DoAddSample(const MediaStream* stream, - std::shared_ptr sample) { +Status TsMuxer::DoAddSample(std::shared_ptr sample) { return segmenter_->AddSample(sample); } void TsMuxer::FireOnMediaStartEvent() { if (!muxer_listener()) return; - muxer_listener()->OnMediaStart(options(), *streams().front()->info(), - kTsTimescale, MuxerListener::kContainerWebM); + muxer_listener()->OnMediaStart(options(), *streams().front(), kTsTimescale, + MuxerListener::kContainerWebM); } void TsMuxer::FireOnMediaEndEvent() { diff --git a/packager/media/formats/mp2t/ts_muxer.h b/packager/media/formats/mp2t/ts_muxer.h index d36b423686..3308023e23 100644 --- a/packager/media/formats/mp2t/ts_muxer.h +++ b/packager/media/formats/mp2t/ts_muxer.h @@ -24,10 +24,9 @@ class TsMuxer : public Muxer { private: // Muxer implementation. - Status Initialize() override; + Status InitializeMuxer() override; Status Finalize() override; - Status DoAddSample(const MediaStream* stream, - std::shared_ptr sample) override; + Status DoAddSample(std::shared_ptr sample) override; void FireOnMediaStartEvent(); void FireOnMediaEndEvent(); diff --git a/packager/media/formats/mp2t/ts_segmenter.h b/packager/media/formats/mp2t/ts_segmenter.h index 2a0496dc95..47ff1e7e9f 100644 --- a/packager/media/formats/mp2t/ts_segmenter.h +++ b/packager/media/formats/mp2t/ts_segmenter.h @@ -8,7 +8,6 @@ #define PACKAGER_MEDIA_FORMATS_MP2T_TS_SEGMENTER_H_ #include -#include "packager/media/base/media_stream.h" #include "packager/media/base/muxer_options.h" #include "packager/media/base/status.h" #include "packager/media/file/file.h" diff --git a/packager/media/formats/mp2t/ts_writer.h b/packager/media/formats/mp2t/ts_writer.h index d40ce95ddb..66f96039fa 100644 --- a/packager/media/formats/mp2t/ts_writer.h +++ b/packager/media/formats/mp2t/ts_writer.h @@ -12,7 +12,6 @@ #include #include -#include "packager/media/base/media_stream.h" #include "packager/media/file/file.h" #include "packager/media/file/file_closer.h" #include "packager/media/formats/mp2t/continuity_counter.h" @@ -21,6 +20,9 @@ namespace shaka { namespace media { + +class StreamInfo; + namespace mp2t { /// This class takes PesPackets, encapsulates them into TS packets, and write diff --git a/packager/media/formats/mp4/mp4_muxer.cc b/packager/media/formats/mp4/mp4_muxer.cc index 7d135bf844..67f08f9e3f 100644 --- a/packager/media/formats/mp4/mp4_muxer.cc +++ b/packager/media/formats/mp4/mp4_muxer.cc @@ -13,7 +13,6 @@ #include "packager/media/base/fourccs.h" #include "packager/media/base/key_source.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/codecs/es_descriptor.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() {} -Status MP4Muxer::Initialize() { +Status MP4Muxer::InitializeMuxer() { DCHECK(!streams().empty()); std::unique_ptr ftyp(new FileType); @@ -91,10 +90,9 @@ Status MP4Muxer::Initialize() { ftyp->major_brand = FOURCC_dash; ftyp->compatible_brands.push_back(FOURCC_iso6); ftyp->compatible_brands.push_back(FOURCC_mp41); - if (streams().size() == 1 && - streams()[0]->info()->stream_type() == kStreamVideo) { + if (streams().size() == 1 && streams()[0]->stream_type() == kStreamVideo) { const FourCC codec_fourcc = CodecToFourCC( - static_cast(streams()[0]->info().get())->codec()); + static_cast(streams()[0].get())->codec()); if (codec_fourcc != FOURCC_NULL) ftyp->compatible_brands.push_back(codec_fourcc); } @@ -115,22 +113,18 @@ Status MP4Muxer::Initialize() { trex.track_id = trak.header.track_id; trex.default_sample_description_index = 1; - switch (streams()[i]->info()->stream_type()) { + switch (streams()[i]->stream_type()) { case kStreamVideo: - GenerateVideoTrak( - static_cast(streams()[i]->info().get()), - &trak, - i + 1); + GenerateVideoTrak(static_cast(streams()[i].get()), + &trak, i + 1); break; case kStreamAudio: - GenerateAudioTrak( - static_cast(streams()[i]->info().get()), - &trak, - i + 1); + GenerateAudioTrak(static_cast(streams()[i].get()), + &trak, i + 1); break; default: 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; } -Status MP4Muxer::DoAddSample(const MediaStream* stream, - std::shared_ptr sample) { +Status MP4Muxer::DoAddSample(std::shared_ptr sample) { DCHECK(segmenter_); - return segmenter_->AddSample(stream, sample); + return segmenter_->AddSample(*streams()[0], sample); } void MP4Muxer::InitializeTrak(const StreamInfo* info, Track* trak) { @@ -355,9 +348,7 @@ void MP4Muxer::FireOnMediaStartEvent() { DCHECK(!streams().empty()) << "Media started without a stream."; const uint32_t timescale = segmenter_->GetReferenceTimeScale(); - muxer_listener()->OnMediaStart(options(), - *streams().front()->info(), - timescale, + muxer_listener()->OnMediaStart(options(), *streams().front(), timescale, MuxerListener::kContainerMp4); } diff --git a/packager/media/formats/mp4/mp4_muxer.h b/packager/media/formats/mp4/mp4_muxer.h index 4b2508aed0..5a16a2ce52 100644 --- a/packager/media/formats/mp4/mp4_muxer.h +++ b/packager/media/formats/mp4/mp4_muxer.h @@ -35,10 +35,9 @@ class MP4Muxer : public Muxer { private: // Muxer implementation overrides. - Status Initialize() override; + Status InitializeMuxer() override; Status Finalize() override; - Status DoAddSample(const MediaStream* stream, - std::shared_ptr sample) override; + Status DoAddSample(std::shared_ptr sample) override; // Generate Audio/Video Track box. void InitializeTrak(const StreamInfo* info, Track* trak); diff --git a/packager/media/formats/mp4/multi_segment_segmenter.cc b/packager/media/formats/mp4/multi_segment_segmenter.cc index 2319b5e469..0ddff48301 100644 --- a/packager/media/formats/mp4/multi_segment_segmenter.cc +++ b/packager/media/formats/mp4/multi_segment_segmenter.cc @@ -11,7 +11,6 @@ #include "packager/base/strings/string_number_conversions.h" #include "packager/base/strings/string_util.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_util.h" #include "packager/media/event/muxer_listener.h" diff --git a/packager/media/formats/mp4/segmenter.cc b/packager/media/formats/mp4/segmenter.cc index 7553c4690d..2ce2f417a1 100644 --- a/packager/media/formats/mp4/segmenter.cc +++ b/packager/media/formats/mp4/segmenter.cc @@ -13,7 +13,6 @@ #include "packager/media/base/buffer_writer.h" #include "packager/media/base/key_source.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_util.h" #include "packager/media/base/video_stream_info.h" @@ -162,16 +161,17 @@ Segmenter::Segmenter(const MuxerOptions& options, Segmenter::~Segmenter() {} -Status Segmenter::Initialize(const std::vector& streams, - MuxerListener* muxer_listener, - ProgressListener* progress_listener, - KeySource* encryption_key_source, - uint32_t max_sd_pixels, - uint32_t max_hd_pixels, - uint32_t max_uhd1_pixels, - double clear_lead_in_seconds, - double crypto_period_duration_in_seconds, - FourCC protection_scheme) { +Status Segmenter::Initialize( + const std::vector>& streams, + MuxerListener* muxer_listener, + ProgressListener* progress_listener, + KeySource* encryption_key_source, + uint32_t max_sd_pixels, + uint32_t max_hd_pixels, + uint32_t max_uhd1_pixels, + double clear_lead_in_seconds, + double crypto_period_duration_in_seconds, + FourCC protection_scheme) { DCHECK_LT(0u, streams.size()); muxer_listener_ = muxer_listener; progress_listener_ = progress_listener; @@ -184,22 +184,19 @@ Status Segmenter::Initialize(const std::vector& streams, const bool kInitialEncryptionInfo = true; for (uint32_t i = 0; i < streams.size(); ++i) { - stream_map_[streams[i]] = i; 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). if (sidx_->reference_id == 0) sidx_->reference_id = i + 1; } if (!encryption_key_source) { - fragmenters_[i].reset( - new Fragmenter(streams[i]->info(), &moof_->tracks[i])); + fragmenters_[i].reset(new Fragmenter(streams[i], &moof_->tracks[i])); continue; } - KeySource::TrackType track_type = - GetTrackTypeForEncryption(*streams[i]->info(), max_sd_pixels, - max_hd_pixels, max_uhd1_pixels); + KeySource::TrackType track_type = GetTrackTypeForEncryption( + *streams[i], max_sd_pixels, max_hd_pixels, max_uhd1_pixels); SampleDescription& description = moov_->tracks[i].media.information.sample_table.description; ProtectionPattern pattern = @@ -224,12 +221,11 @@ Status Segmenter::Initialize(const std::vector& streams, } fragmenters_[i].reset(new KeyRotationFragmenter( - moof_.get(), streams[i]->info(), &moof_->tracks[i], - encryption_key_source, track_type, - crypto_period_duration_in_seconds * streams[i]->info()->time_scale(), - clear_lead_in_seconds * streams[i]->info()->time_scale(), - protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block, - muxer_listener_)); + moof_.get(), streams[i], &moof_->tracks[i], encryption_key_source, + track_type, + crypto_period_duration_in_seconds * streams[i]->time_scale(), + clear_lead_in_seconds * streams[i]->time_scale(), protection_scheme, + pattern.crypt_byte_block, pattern.skip_byte_block, muxer_listener_)); continue; } @@ -262,10 +258,9 @@ Status Segmenter::Initialize(const std::vector& streams, } fragmenters_[i].reset(new EncryptingFragmenter( - streams[i]->info(), &moof_->tracks[i], std::move(encryption_key), - clear_lead_in_seconds * streams[i]->info()->time_scale(), - protection_scheme, pattern.crypt_byte_block, pattern.skip_byte_block, - muxer_listener_)); + streams[i], &moof_->tracks[i], std::move(encryption_key), + clear_lead_in_seconds * streams[i]->time_scale(), protection_scheme, + pattern.crypt_byte_block, pattern.skip_byte_block, muxer_listener_)); } if (options_.mp4_use_decoding_timestamp_in_timeline) { @@ -276,10 +271,10 @@ Status Segmenter::Initialize(const std::vector& streams, // Choose the first stream if there is no VIDEO. if (sidx_->reference_id == 0) sidx_->reference_id = 1; - sidx_->timescale = streams[GetReferenceStreamId()]->info()->time_scale(); + sidx_->timescale = streams[GetReferenceStreamId()]->time_scale(); // 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. moov_->header.timescale = sidx_->timescale; @@ -320,12 +315,10 @@ Status Segmenter::Finalize() { return DoFinalize(); } -Status Segmenter::AddSample(const MediaStream* stream, +Status Segmenter::AddSample(const StreamInfo& stream_info, std::shared_ptr sample) { - // Find the fragmenter for this stream. - DCHECK(stream); - DCHECK(stream_map_.find(stream) != stream_map_.end()); - uint32_t stream_id = stream_map_[stream]; + // TODO(kqyang): Stream id should be passed in. + const uint32_t stream_id = 0; Fragmenter* fragmenter = fragmenters_[stream_id].get(); // 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; 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) { finalize_fragment = true; } } bool finalize_segment = false; 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) { finalize_segment = true; finalize_fragment = true; diff --git a/packager/media/formats/mp4/segmenter.h b/packager/media/formats/mp4/segmenter.h index aa3274a63c..6279b32ad4 100644 --- a/packager/media/formats/mp4/segmenter.h +++ b/packager/media/formats/mp4/segmenter.h @@ -23,9 +23,9 @@ struct MuxerOptions; class BufferWriter; class KeySource; class MediaSample; -class MediaStream; class MuxerListener; class ProgressListener; +class StreamInfo; namespace mp4 { @@ -69,7 +69,7 @@ class Segmenter { /// @param protection_scheme specifies the protection scheme: 'cenc', 'cens', /// 'cbc1', 'cbcs'. /// @return OK on success, an error status otherwise. - Status Initialize(const std::vector& streams, + Status Initialize(const std::vector>& streams, MuxerListener* muxer_listener, ProgressListener* progress_listener, KeySource* encryption_key_source, @@ -85,11 +85,9 @@ class Segmenter { Status Finalize(); /// 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. /// @return OK on success, an error status otherwise. - Status AddSample(const MediaStream* stream, + Status AddSample(const StreamInfo& stream_Info, std::shared_ptr sample); /// @return true if there is an initialization range, while setting @a offset @@ -145,7 +143,6 @@ class Segmenter { std::unique_ptr sidx_; std::vector> fragmenters_; std::vector segment_durations_; - std::map stream_map_; MuxerListener* muxer_listener_; ProgressListener* progress_listener_; uint64_t progress_target_; diff --git a/packager/media/formats/mp4/single_segment_segmenter.cc b/packager/media/formats/mp4/single_segment_segmenter.cc index 4d8a3b6f9d..048b9a7fcf 100644 --- a/packager/media/formats/mp4/single_segment_segmenter.cc +++ b/packager/media/formats/mp4/single_segment_segmenter.cc @@ -9,7 +9,6 @@ #include #include "packager/media/base/buffer_writer.h" -#include "packager/media/base/media_stream.h" #include "packager/media/base/muxer_options.h" #include "packager/media/event/muxer_listener.h" #include "packager/media/event/progress_listener.h" diff --git a/packager/media/formats/webm/multi_segment_segmenter.cc b/packager/media/formats/webm/multi_segment_segmenter.cc index 337746f16e..d085641f8b 100644 --- a/packager/media/formats/webm/multi_segment_segmenter.cc +++ b/packager/media/formats/webm/multi_segment_segmenter.cc @@ -6,7 +6,6 @@ #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_util.h" #include "packager/media/base/stream_info.h" diff --git a/packager/media/formats/webm/segmenter.cc b/packager/media/formats/webm/segmenter.cc index ea376ff165..354725fcc9 100644 --- a/packager/media/formats/webm/segmenter.cc +++ b/packager/media/formats/webm/segmenter.cc @@ -9,7 +9,6 @@ #include "packager/base/time/time.h" #include "packager/media/base/audio_stream_info.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_util.h" #include "packager/media/base/stream_info.h" diff --git a/packager/media/formats/webm/two_pass_single_segment_segmenter.cc b/packager/media/formats/webm/two_pass_single_segment_segmenter.cc index bced213c50..cc3e1ce885 100644 --- a/packager/media/formats/webm/two_pass_single_segment_segmenter.cc +++ b/packager/media/formats/webm/two_pass_single_segment_segmenter.cc @@ -9,7 +9,6 @@ #include #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/stream_info.h" #include "packager/media/file/file_util.h" diff --git a/packager/media/formats/webm/webm_muxer.cc b/packager/media/formats/webm/webm_muxer.cc index 0191121c24..d4ac7bc043 100644 --- a/packager/media/formats/webm/webm_muxer.cc +++ b/packager/media/formats/webm/webm_muxer.cc @@ -8,7 +8,6 @@ #include "packager/media/base/fourccs.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/formats/webm/mkv_writer.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() {} -Status WebMMuxer::Initialize() { +Status WebMMuxer::InitializeMuxer() { CHECK_EQ(streams().size(), 1U); if (crypto_period_duration_in_seconds() > 0) { @@ -50,7 +49,7 @@ Status WebMMuxer::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(), max_hd_pixels(), max_uhd1_pixels(), clear_lead_in_seconds()); @@ -73,10 +72,8 @@ Status WebMMuxer::Finalize() { return Status::OK; } -Status WebMMuxer::DoAddSample(const MediaStream* stream, - std::shared_ptr sample) { +Status WebMMuxer::DoAddSample(std::shared_ptr sample) { DCHECK(segmenter_); - DCHECK(stream == streams()[0]); return segmenter_->AddSample(sample); } @@ -86,9 +83,9 @@ void WebMMuxer::FireOnMediaStartEvent() { DCHECK(!streams().empty()) << "Media started without a stream."; - const uint32_t timescale = streams().front()->info()->time_scale(); - muxer_listener()->OnMediaStart(options(), *streams().front()->info(), - timescale, MuxerListener::kContainerWebM); + const uint32_t timescale = streams().front()->time_scale(); + muxer_listener()->OnMediaStart(options(), *streams().front(), timescale, + MuxerListener::kContainerWebM); } void WebMMuxer::FireOnMediaEndEvent() { diff --git a/packager/media/formats/webm/webm_muxer.h b/packager/media/formats/webm/webm_muxer.h index 5d869949b8..ff7e3b13bb 100644 --- a/packager/media/formats/webm/webm_muxer.h +++ b/packager/media/formats/webm/webm_muxer.h @@ -24,10 +24,9 @@ class WebMMuxer : public Muxer { private: // Muxer implementation overrides. - Status Initialize() override; + Status InitializeMuxer() override; Status Finalize() override; - Status DoAddSample(const MediaStream* stream, - std::shared_ptr sample) override; + Status DoAddSample(std::shared_ptr sample) override; void FireOnMediaStartEvent(); void FireOnMediaEndEvent(); diff --git a/packager/media/test/packager_test.cc b/packager/media/test/packager_test.cc index d89e33eff4..a8ae1be2d9 100644 --- a/packager/media/test/packager_test.cc +++ b/packager/media/test/packager_test.cc @@ -13,7 +13,6 @@ #include "packager/media/base/demuxer.h" #include "packager/media/base/fixed_key_source.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_util.h" #include "packager/media/base/stream_info.h" @@ -50,7 +49,6 @@ const bool kSingleSegment = true; const bool kMultipleSegments = false; const bool kEnableEncryption = true; const bool kDisableEncryption = false; -const char kNoLanguageOverride[] = ""; // Encryption constants. const char kKeyIdHex[] = "e5007e6e9dcd5ac095202ed3758382cd"; @@ -63,24 +61,6 @@ const uint32_t kMaxSDPixels = 640 * 480; const uint32_t kMaxHDPixels = 1920 * 1080; const uint32_t kMaxUHD1Pixels = 4096 * 2160; -MediaStream* FindFirstStreamOfType( - const std::vector>& streams, - StreamType stream_type) { - for (const std::unique_ptr& stream : streams) { - if (stream->info()->stream_type() == stream_type) - return stream.get(); - } - return nullptr; -} -MediaStream* FindFirstVideoStream( - const std::vector>& streams) { - return FindFirstStreamOfType(streams, kStreamVideo); -} -MediaStream* FindFirstAudioStream( - const std::vector>& streams) { - return FindFirstStreamOfType(streams, kStreamAudio); -} - } // namespace class FakeClock : public base::Clock { @@ -114,8 +94,7 @@ class PackagerTestBasic : public ::testing::TestWithParam { const std::string& video_output, const std::string& audio_output, bool single_segment, - bool enable_encryption, - const std::string& override_language); + bool enable_encryption); void Decrypt(const std::string& input, const std::string& video_output, @@ -157,59 +136,46 @@ void PackagerTestBasic::Remux(const std::string& input, const std::string& video_output, const std::string& audio_output, bool single_segment, - bool enable_encryption, - const std::string& language_override) { + bool enable_encryption) { CHECK(!video_output.empty() || !audio_output.empty()); Demuxer demuxer(GetFullPath(input)); - ASSERT_OK(demuxer.Initialize()); std::unique_ptr encryption_key_source( FixedKeySource::CreateFromHexStrings(kKeyIdHex, kKeyHex, "", "")); DCHECK(encryption_key_source); - std::unique_ptr muxer_video; + std::shared_ptr muxer_video; if (!video_output.empty()) { muxer_video.reset( new mp4::MP4Muxer(SetupOptions(video_output, single_segment))); 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) { muxer_video->SetKeySource(encryption_key_source.get(), kMaxSDPixels, kMaxHDPixels, kMaxUHD1Pixels, kClearLeadInSeconds, kCryptoDurationInSeconds, FOURCC_cenc); } + ASSERT_OK(demuxer.SetHandler("video", muxer_video)); } - std::unique_ptr muxer_audio; + std::shared_ptr muxer_audio; if (!audio_output.empty()) { muxer_audio.reset( new mp4::MP4Muxer(SetupOptions(audio_output, single_segment))); 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) { muxer_audio->SetKeySource(encryption_key_source.get(), kMaxSDPixels, kMaxHDPixels, kMaxUHD1Pixels, kClearLeadInSeconds, kCryptoDurationInSeconds, FOURCC_cenc); } + ASSERT_OK(demuxer.SetHandler("audio", muxer_audio)); } + ASSERT_OK(demuxer.Initialize()); // Start remuxing process. ASSERT_OK(demuxer.Run()); } @@ -224,25 +190,20 @@ void PackagerTestBasic::Decrypt(const std::string& input, FixedKeySource::CreateFromHexStrings(kKeyIdHex, kKeyHex, "", "")); ASSERT_TRUE(decryption_key_source); demuxer.SetKeySource(std::move(decryption_key_source)); - ASSERT_OK(demuxer.Initialize()); - std::unique_ptr muxer; - MediaStream* stream(NULL); + std::shared_ptr muxer; if (!video_output.empty()) { muxer.reset( new mp4::MP4Muxer(SetupOptions(video_output, true))); - stream = FindFirstVideoStream(demuxer.streams()); } if (!audio_output.empty()) { muxer.reset( new mp4::MP4Muxer(SetupOptions(audio_output, true))); - stream = FindFirstAudioStream(demuxer.streams()); } ASSERT_TRUE(muxer); - ASSERT_TRUE(stream != NULL); - ASSERT_TRUE(stream->info()->is_encrypted()); muxer->set_clock(&fake_clock_); - muxer->AddStream(stream); + ASSERT_OK(demuxer.SetHandler("0", muxer)); + ASSERT_OK(demuxer.Initialize()); ASSERT_OK(demuxer.Run()); } @@ -252,8 +213,7 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedVideo) { kOutputVideo, kOutputNone, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); } TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedAudio) { @@ -261,8 +221,7 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentUnencryptedAudio) { kOutputNone, kOutputAudio, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); } TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedVideo) { @@ -270,8 +229,7 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedVideo) { kOutputVideo, kOutputNone, kSingleSegment, - kEnableEncryption, - kNoLanguageOverride)); + kEnableEncryption)); ASSERT_NO_FATAL_FAILURE(Decrypt(kOutputVideo, kOutputVideo2, @@ -283,76 +241,13 @@ TEST_P(PackagerTestBasic, MP4MuxerSingleSegmentEncryptedAudio) { kOutputNone, kOutputAudio, kSingleSegment, - kEnableEncryption, - kNoLanguageOverride)); + kEnableEncryption)); ASSERT_NO_FATAL_FAILURE(Decrypt(kOutputAudio, kOutputNone, 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 { public: @@ -363,15 +258,13 @@ class PackagerTest : public PackagerTestBasic { kOutputVideo, kOutputNone, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); ASSERT_NO_FATAL_FAILURE(Remux(GetParam(), kOutputNone, kOutputAudio, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); } }; @@ -382,8 +275,7 @@ TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencryptedVideoAgain) { kOutputVideo2, kOutputNone, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2)); } @@ -394,8 +286,7 @@ TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencryptedAudioAgain) { kOutputNone, kOutputAudio2, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); EXPECT_TRUE(ContentsEqual(kOutputAudio, kOutputAudio2)); } @@ -404,8 +295,7 @@ TEST_P(PackagerTest, MP4MuxerSingleSegmentUnencryptedSeparateAudioVideo) { kOutputVideo2, kOutputAudio2, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); // Compare the output with single muxer output. They should match. EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2)); @@ -417,8 +307,7 @@ TEST_P(PackagerTest, MP4MuxerMultiSegmentsUnencryptedVideo) { kOutputVideo2, kOutputNone, kMultipleSegments, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); // Find and concatenates the segments. const std::string kOutputVideoSegmentsCombined = @@ -452,8 +341,7 @@ TEST_P(PackagerTest, MP4MuxerMultiSegmentsUnencryptedVideo) { kOutputVideo2, kOutputNone, kSingleSegment, - kDisableEncryption, - kNoLanguageOverride)); + kDisableEncryption)); EXPECT_TRUE(ContentsEqual(kOutputVideo, kOutputVideo2)); }