From fddeb1feb1140d680426d0b7a0966fd9fe3c97b6 Mon Sep 17 00:00:00 2001 From: Kongqun Yang Date: Mon, 17 Mar 2014 11:36:41 -0700 Subject: [PATCH] Support for multiple audio/video tracks in a file Remove --audio and --video command line options and replaced with --stream. User can use --stream=video (default) to pull the first video track from the source or --stream=audio to pull the first audio track. The user may also use --stream={stream_id}, 0-based stream id to pull the stream, e.g. --stream=0 for the first stream. Change-Id: Ie1f93c2cc80a160a496b1d43ae3a658263d30cfc --- app/muxer_flags.h | 7 ++++-- app/packager_main.cc | 28 ++++++++++++++++------ media/mp4/mp4_media_parser.cc | 42 ++++++++------------------------- media/mp4/mp4_media_parser.h | 7 ------ media/mp4/track_run_iterator.cc | 33 ++++++++++++++++---------- media/mp4/track_run_iterator.h | 6 ++--- 6 files changed, 60 insertions(+), 63 deletions(-) diff --git a/app/muxer_flags.h b/app/muxer_flags.h index 97aa7ffaae..3313e72df1 100644 --- a/app/muxer_flags.h +++ b/app/muxer_flags.h @@ -11,8 +11,11 @@ #include -DEFINE_bool(audio, false, "Add the first audio stream to muxer."); -DEFINE_bool(video, false, "Add the first video stream to muxer."); +DEFINE_string(stream, + "video", + "Add the specified stream to muxer. Allowed values, 'video' - " + "the first video stream; or 'audio' - the first audio stream; or " + "zero based stream id."); DEFINE_double(clear_lead, 10.0, "Clear lead in seconds if encryption is enabled."); diff --git a/app/packager_main.cc b/app/packager_main.cc index 455f4c5f6b..5f80965262 100644 --- a/app/packager_main.cc +++ b/app/packager_main.cc @@ -12,6 +12,7 @@ #include "app/widevine_encryption_flags.h" #include "base/file_util.h" #include "base/logging.h" +#include "base/strings/string_number_conversions.h" #include "base/strings/stringprintf.h" #include "media/base/demuxer.h" #include "media/base/fixed_encryptor_source.h" @@ -130,16 +131,29 @@ MediaStream* FindFirstAudioStream(const std::vector& streams) { bool AddStreamToMuxer(const std::vector& streams, Muxer* muxer) { DCHECK(muxer); - if (!FLAGS_video && !FLAGS_audio) { - LOG(ERROR) << "Required: --audio or --video."; - return false; + MediaStream* stream = NULL; + if (FLAGS_stream == "video") { + stream = FindFirstVideoStream(streams); + } else if (FLAGS_stream == "audio") { + stream = FindFirstAudioStream(streams); + } else { + // Expect FLAGS_stream to be a zero based stream id. + size_t stream_id; + if (!base::StringToSizeT(FLAGS_stream, &stream_id) || + stream_id >= streams.size()) { + LOG(ERROR) << "Invalid argument --stream=" << FLAGS_stream << "; " + << "should be 'audio', 'video', or a number within [0, " + << streams.size() - 1 << "]."; + return false; + } + stream = streams[stream_id]; + DCHECK(stream); } - MediaStream* stream = FLAGS_video ? FindFirstVideoStream(streams) - : FindFirstAudioStream(streams); + // This could occur only if FLAGS_stream=audio|video and the corresponding + // stream does not exist in the input. if (!stream) { - LOG(ERROR) << "Cannot find a " << (FLAGS_video ? "video" : "audio") - << " stream to mux."; + LOG(ERROR) << "No " << FLAGS_stream << " stream found in the input."; return false; } Status status = muxer->AddStream(stream); diff --git a/media/mp4/mp4_media_parser.cc b/media/mp4/mp4_media_parser.cc index 03f38d6b5d..9a6b4ab69b 100644 --- a/media/mp4/mp4_media_parser.cc +++ b/media/mp4/mp4_media_parser.cc @@ -29,15 +29,7 @@ namespace media { namespace mp4 { MP4MediaParser::MP4MediaParser() - : state_(kWaitingForInit), - moof_head_(0), - mdat_tail_(0), - has_audio_(false), - has_video_(false), - audio_track_id_(0), - video_track_id_(0), - is_audio_track_encrypted_(false), - is_video_track_encrypted_(false) {} + : state_(kWaitingForInit), moof_head_(0), mdat_tail_(0) {} MP4MediaParser::~MP4MediaParser() {} @@ -147,9 +139,6 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { RCHECK(moov_->Parse(reader)); runs_.reset(); - has_audio_ = false; - has_video_ = false; - std::vector > streams; for (std::vector::const_iterator track = moov_->tracks.begin(); @@ -200,8 +189,6 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { desc_idx -= 1; // BMFF descriptor index is one-based if (track->media.handler.type == kAudio) { - RCHECK(!has_audio_); - RCHECK(!samp_descr.audio_entries.empty()); // It is not uncommon to find otherwise-valid files with incorrect sample @@ -249,8 +236,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { return false; } - is_audio_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; - DVLOG(1) << "is_audio_track_encrypted_: " << is_audio_track_encrypted_; + bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted; + DVLOG(1) << "is_audio_track_encrypted_: " << is_encrypted; streams.push_back(new AudioStreamInfo( track->header.track_id, timescale, @@ -263,14 +250,10 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { sampling_frequency, extra_data.size() ? &extra_data[0] : NULL, extra_data.size(), - is_audio_track_encrypted_)); - has_audio_ = true; - audio_track_id_ = track->header.track_id; + is_encrypted)); } if (track->media.handler.type == kVideo) { - RCHECK(!has_video_); - RCHECK(!samp_descr.video_entries.empty()); if (desc_idx >= samp_descr.video_entries.size()) desc_idx = 0; @@ -290,8 +273,8 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { entry.avcc.profile_compatibility, entry.avcc.avc_level); - is_video_track_encrypted_ = entry.sinf.info.track_encryption.is_encrypted; - DVLOG(1) << "is_video_track_encrypted_: " << is_video_track_encrypted_; + bool is_encrypted = entry.sinf.info.track_encryption.is_encrypted; + DVLOG(1) << "is_video_track_encrypted_: " << is_encrypted; streams.push_back(new VideoStreamInfo(track->header.track_id, timescale, duration, @@ -303,9 +286,7 @@ bool MP4MediaParser::ParseMoov(BoxReader* reader) { entry.avcc.length_size, &entry.avcc.data[0], entry.avcc.data.size(), - is_video_track_encrypted_)); - has_video_ = true; - video_track_id_ = track->header.track_id; + is_encrypted)); } } @@ -372,11 +353,8 @@ bool MP4MediaParser::EnqueueSample(bool* err) { if (!buf_size) return false; - bool audio = has_audio_ && audio_track_id_ == runs_->track_id(); - bool video = has_video_ && video_track_id_ == runs_->track_id(); - - // Skip this entire track if it's not one we're interested in - if (!audio && !video) + // Skip this entire track if it is not audio nor video. + if (!runs_->is_audio() && !runs_->is_video()) runs_->AdvanceRun(); // Attempt to cache the auxiliary information first. Aux info is usually @@ -431,7 +409,7 @@ bool MP4MediaParser::EnqueueSample(bool* err) { stream_sample->set_pts(runs_->cts()); stream_sample->set_duration(runs_->duration()); - DVLOG(3) << "Pushing frame: aud=" << audio + DVLOG(3) << "Pushing frame: " << ", key=" << runs_->is_keyframe() << ", dur=" << runs_->duration() << ", dts=" << runs_->dts() diff --git a/media/mp4/mp4_media_parser.h b/media/mp4/mp4_media_parser.h index 49c667134a..a744efc368 100644 --- a/media/mp4/mp4_media_parser.h +++ b/media/mp4/mp4_media_parser.h @@ -89,13 +89,6 @@ class MP4MediaParser : public MediaParser { scoped_ptr moov_; scoped_ptr runs_; - bool has_audio_; - bool has_video_; - uint32 audio_track_id_; - uint32 video_track_id_; - bool is_audio_track_encrypted_; - bool is_video_track_encrypted_; - DISALLOW_COPY_AND_ASSIGN(MP4MediaParser); }; diff --git a/media/mp4/track_run_iterator.cc b/media/mp4/track_run_iterator.cc index cecc953f24..c8b83ced5d 100644 --- a/media/mp4/track_run_iterator.cc +++ b/media/mp4/track_run_iterator.cc @@ -30,7 +30,7 @@ struct TrackRunInfo { int64 start_dts; int64 sample_start_offset; - bool is_audio; + TrackType track_type; const AudioSampleEntry* audio_description; const VideoSampleEntry* video_description; @@ -48,7 +48,9 @@ TrackRunInfo::TrackRunInfo() timescale(-1), start_dts(-1), sample_start_offset(-1), - is_audio(false), + track_type(kInvalid), + audio_description(NULL), + video_description(NULL), aux_info_start_offset(-1), aux_info_default_size(0), aux_info_total_size(0) {} @@ -203,15 +205,15 @@ bool TrackRunIterator::Init() { RCHECK(desc_idx > 0); // Descriptions are one-indexed in the file. desc_idx -= 1; - tri.is_audio = (stsd.type == kAudio); - if (tri.is_audio) { + tri.track_type = stsd.type; + if (tri.track_type == kAudio) { RCHECK(!stsd.audio_entries.empty()); if (desc_idx > stsd.audio_entries.size()) desc_idx = 0; tri.audio_description = &stsd.audio_entries[desc_idx]; // We don't support encrypted non-fragmented mp4 for now. RCHECK(!tri.audio_description->sinf.info.track_encryption.is_encrypted); - } else { + } else if (tri.track_type == kVideo) { RCHECK(!stsd.video_entries.empty()); if (desc_idx > stsd.video_entries.size()) desc_idx = 0; @@ -303,13 +305,13 @@ bool TrackRunIterator::Init(const MovieFragment& moof) { tri.start_dts = run_start_dts; tri.sample_start_offset = trun.data_offset; - tri.is_audio = (stsd.type == kAudio); - if (tri.is_audio) { + tri.track_type = stsd.type; + if (tri.track_type == kAudio) { RCHECK(!stsd.audio_entries.empty()); if (desc_idx > stsd.audio_entries.size()) desc_idx = 0; tri.audio_description = &stsd.audio_entries[desc_idx]; - } else { + } else if (tri.track_type == kVideo) { RCHECK(!stsd.video_entries.empty()); if (desc_idx > stsd.video_entries.size()) desc_idx = 0; @@ -469,7 +471,12 @@ int TrackRunIterator::aux_info_size() const { bool TrackRunIterator::is_audio() const { DCHECK(IsRunValid()); - return run_itr_->is_audio; + return run_itr_->track_type == kAudio; +} + +bool TrackRunIterator::is_video() const { + DCHECK(IsRunValid()); + return run_itr_->track_type == kVideo; } const AudioSampleEntry& TrackRunIterator::audio_description() const { @@ -479,7 +486,7 @@ const AudioSampleEntry& TrackRunIterator::audio_description() const { } const VideoSampleEntry& TrackRunIterator::video_description() const { - DCHECK(!is_audio()); + DCHECK(is_video()); DCHECK(run_itr_->video_description); return *run_itr_->video_description; } @@ -517,14 +524,16 @@ bool TrackRunIterator::is_keyframe() const { const TrackEncryption& TrackRunIterator::track_encryption() const { if (is_audio()) return audio_description().sinf.info.track_encryption; + DCHECK(is_video()); return video_description().sinf.info.track_encryption; } scoped_ptr TrackRunIterator::GetDecryptConfig() { size_t sample_idx = sample_itr_ - run_itr_->samples.begin(); - DCHECK(sample_idx < cenc_info_.size()); + DCHECK_LT(sample_idx, cenc_info_.size()); const FrameCENCInfo& cenc_info = cenc_info_[sample_idx]; - DCHECK(is_encrypted() && !AuxInfoNeedsToBeCached()); + DCHECK(is_encrypted()); + DCHECK(!AuxInfoNeedsToBeCached()); const size_t total_size_of_subsamples = cenc_info.GetTotalSizeOfSubsamples(); if (total_size_of_subsamples != 0 && diff --git a/media/mp4/track_run_iterator.h b/media/mp4/track_run_iterator.h index fa09fecd18..ff3bd726c3 100644 --- a/media/mp4/track_run_iterator.h +++ b/media/mp4/track_run_iterator.h @@ -78,13 +78,13 @@ class TrackRunIterator { int aux_info_size() const; bool is_encrypted() const; bool is_audio() const; + bool is_video() const; /// @} - /// @name Only one is valid, based on the value of is_audio(). - /// @{ + /// Only valid if is_audio() is true. const AudioSampleEntry& audio_description() const; + /// Only valid if is_video() is true. const VideoSampleEntry& video_description() const; - /// @} /// @name Properties of the current sample. Only valid if IsSampleValid(). /// @{