From 86369efc306467fc2369737d760fdbfba162bf04 Mon Sep 17 00:00:00 2001 From: Jacob Trimble Date: Thu, 31 Mar 2016 12:48:16 -0700 Subject: [PATCH] Add support for H.265 in MPEG2-TS demuxer. This also changes the H.264 parser to correctly determine access units. Closes #46 Issue #47 Change-Id: I69a8c47ebf4fe35cef0592997460158b3131084e --- README.md | 2 +- packager/media/filters/nalu_reader.cc | 11 + packager/media/filters/nalu_reader.h | 9 + packager/media/formats/mp2t/es_parser_h264.cc | 340 ++++-------------- packager/media/formats/mp2t/es_parser_h264.h | 69 +--- packager/media/formats/mp2t/es_parser_h265.cc | 173 +++++++++ packager/media/formats/mp2t/es_parser_h265.h | 62 ++++ packager/media/formats/mp2t/es_parser_h26x.cc | 296 +++++++++++++++ packager/media/formats/mp2t/es_parser_h26x.h | 108 ++++++ packager/media/formats/mp2t/mp2t.gyp | 5 + .../media/formats/mp2t/mp2t_media_parser.cc | 10 + .../mp2t/mp2t_media_parser_unittest.cc | 33 +- packager/media/test/data/README | 1 + packager/media/test/data/bear-640x360-hevc.ts | Bin 0 -> 155476 bytes 14 files changed, 771 insertions(+), 348 deletions(-) create mode 100644 packager/media/formats/mp2t/es_parser_h265.cc create mode 100644 packager/media/formats/mp2t/es_parser_h265.h create mode 100644 packager/media/formats/mp2t/es_parser_h26x.cc create mode 100644 packager/media/formats/mp2t/es_parser_h26x.h create mode 100644 packager/media/test/data/bear-640x360-hevc.ts diff --git a/README.md b/README.md index d38edd9a1e..0592b30168 100644 --- a/README.md +++ b/README.md @@ -9,7 +9,7 @@ Current supported codecs: | Codecs | ISO-BMFF | WebM | MPEG2-TS | WVM | |:-----------------:|:------------:|:------------:|:------------:|:-----------:| | H264 (AVC) | I / O | - | I | I | -| H265 (HEVC) | I / O | - | - | - | +| H265 (HEVC) | I / O | - | I | - | | VP8 | I / O | I / O | - | - | | VP9 | I / O | I / O | - | - | | AAC | I / O | - | I | I | diff --git a/packager/media/filters/nalu_reader.cc b/packager/media/filters/nalu_reader.cc index 335789280d..d0dfa2ee87 100644 --- a/packager/media/filters/nalu_reader.cc +++ b/packager/media/filters/nalu_reader.cc @@ -88,6 +88,10 @@ bool Nalu::InitializeFromH264(const uint8_t* data, uint64_t size) { is_video_slice_ = (type_ >= Nalu::H264_NonIDRSlice && type_ <= Nalu::H264_IDRSlice); + can_start_access_unit_ = + (is_video_slice_ || type_ == Nalu::H264_AUD || type_ == Nalu::H264_SPS || + type_ == Nalu::H264_PPS || type_ == Nalu::H264_SEIMessage || + (type_ >= Nalu::H264_PrefixNALUnit && type_ <= Nalu::H264_Reserved18)); return true; } @@ -150,6 +154,13 @@ bool Nalu::InitializeFromH265(const uint8_t* data, uint64_t size) { } is_video_slice_ = type_ >= Nalu::H265_TRAIL_N && type_ <= Nalu::H265_CRA_NUT; + can_start_access_unit_ = + nuh_layer_id_ == 0 && + (is_video_slice_ || type_ == Nalu::H265_AUD || type_ == Nalu::H265_VPS || + type_ == Nalu::H265_SPS || type_ == Nalu::H265_PPS || + type_ == Nalu::H265_PREFIX_SEI || + (type_ >= Nalu::H265_RSV_NVCL41 && type_ <= Nalu::H265_RSV_NVCL44) || + (type_ >= Nalu::H265_UNSPEC48 && type_ <= Nalu::H265_UNSPEC55)); return true; } diff --git a/packager/media/filters/nalu_reader.h b/packager/media/filters/nalu_reader.h index 64f570c04f..454677aa4c 100644 --- a/packager/media/filters/nalu_reader.h +++ b/packager/media/filters/nalu_reader.h @@ -36,7 +36,9 @@ class Nalu { H264_EOSeq = 10, H264_FillerData = 12, H264_SPSExtension = 13, + H264_PrefixNALUnit = 14, H264_SubsetSPS = 15, + H264_DepthParameterSet = 16, H264_Reserved17 = 17, H264_Reserved18 = 18, H264_CodedSliceExtension = 20, @@ -70,7 +72,12 @@ class Nalu { H265_EOS = 36, H265_EOB = 37, + H265_PREFIX_SEI = 39, + H265_RSV_NVCL41 = 41, + H265_RSV_NVCL44 = 44, + H265_UNSPEC48 = 48, + H265_UNSPEC55 = 55, }; enum CodecType { kH264, @@ -96,6 +103,7 @@ class Nalu { int type() const { return type_; } bool is_video_slice() const { return is_video_slice_; } + bool can_start_access_unit() const { return can_start_access_unit_; } private: bool InitializeFromH264(const uint8_t* data, uint64_t size); @@ -115,6 +123,7 @@ class Nalu { int nuh_temporal_id_; int type_; bool is_video_slice_; + bool can_start_access_unit_; // Don't use DISALLOW_COPY_AND_ASSIGN since it is just numbers and a pointer // it does not own. This allows Nalus to be stored in a vector. diff --git a/packager/media/formats/mp2t/es_parser_h264.cc b/packager/media/formats/mp2t/es_parser_h264.cc index 6e94fc6960..a34f298a3a 100644 --- a/packager/media/formats/mp2t/es_parser_h264.cc +++ b/packager/media/formats/mp2t/es_parser_h264.cc @@ -7,9 +7,7 @@ #include #include "packager/base/logging.h" -#include "packager/base/numerics/safe_conversions.h" #include "packager/media/base/media_sample.h" -#include "packager/media/base/offset_byte_queue.h" #include "packager/media/base/timestamp.h" #include "packager/media/base/video_stream_info.h" #include "packager/media/filters/avc_decoder_configuration.h" @@ -21,314 +19,98 @@ namespace edash_packager { namespace media { namespace mp2t { -namespace { - -// An AUD NALU is at least 4 bytes: -// 3 bytes for the start code + 1 byte for the NALU type. -const int kMinAUDSize = 4; - -} // anonymous namespace - EsParserH264::EsParserH264(uint32_t pid, const NewStreamInfoCB& new_stream_info_cb, const EmitSampleCB& emit_sample_cb) - : EsParser(pid), + : EsParserH26x(Nalu::kH264, pid, emit_sample_cb), new_stream_info_cb_(new_stream_info_cb), - emit_sample_cb_(emit_sample_cb), - es_queue_(new media::OffsetByteQueue()), - h264_parser_(new H264Parser()), - current_access_unit_pos_(0), - next_access_unit_pos_(0), - stream_converter_(new H264ByteToUnitStreamConverter), decoder_config_check_pending_(false), - pending_sample_duration_(0), - waiting_for_key_frame_(true) { -} + h264_parser_(new H264Parser()) {} -EsParserH264::~EsParserH264() { -} - -bool EsParserH264::Parse(const uint8_t* buf, - int size, - int64_t pts, - int64_t dts) { - // Note: Parse is invoked each time a PES packet has been reassembled. - // Unfortunately, a PES packet does not necessarily map - // to an h264 access unit, although the HLS recommendation is to use one PES - // for each access unit (but this is just a recommendation and some streams - // do not comply with this recommendation). - - // HLS recommendation: "In AVC video, you should have both a DTS and a - // PTS in each PES header". - // However, some streams do not comply with this recommendation. - DVLOG_IF(1, pts == kNoTimestamp) << "Each video PES should have a PTS"; - if (pts != kNoTimestamp) { - TimingDesc timing_desc; - timing_desc.pts = pts; - timing_desc.dts = (dts != kNoTimestamp) ? dts : pts; - - // Link the end of the byte queue with the incoming timing descriptor. - timing_desc_list_.push_back( - std::pair(es_queue_->tail(), timing_desc)); - } - - // Add the incoming bytes to the ES queue. - es_queue_->Push(buf, size); - return ParseInternal(); -} - -void EsParserH264::Flush() { - DVLOG(1) << "EsParserH264::Flush"; - - if (FindAUD(¤t_access_unit_pos_)) { - // Simulate an additional AUD to force emitting the last access unit - // which is assumed to be complete at this point. - uint8_t aud[] = {0x00, 0x00, 0x01, 0x09}; - es_queue_->Push(aud, sizeof(aud)); - ParseInternal(); - } - - if (pending_sample_) { - // Flush pending sample. - DCHECK(pending_sample_duration_); - pending_sample_->set_duration(pending_sample_duration_); - emit_sample_cb_.Run(pid(), pending_sample_); - pending_sample_ = scoped_refptr(); - } -} +EsParserH264::~EsParserH264() {} void EsParserH264::Reset() { DVLOG(1) << "EsParserH264::Reset"; - es_queue_.reset(new media::OffsetByteQueue()); h264_parser_.reset(new H264Parser()); - current_access_unit_pos_ = 0; - next_access_unit_pos_ = 0; - timing_desc_list_.clear(); last_video_decoder_config_ = scoped_refptr(); decoder_config_check_pending_ = false; - pending_sample_ = scoped_refptr(); - pending_sample_duration_ = 0; - waiting_for_key_frame_ = true; + EsParserH26x::Reset(); } -bool EsParserH264::FindAUD(int64_t* stream_pos) { - while (true) { - const uint8_t* es; - int size; - es_queue_->PeekAt(*stream_pos, &es, &size); - - // Find a start code and move the stream to the start code parser position. - uint64_t start_code_offset; - uint8_t start_code_size; - bool start_code_found = NaluReader::FindStartCode( - es, size, &start_code_offset, &start_code_size); - *stream_pos += start_code_offset; - - // No H264 start code found or NALU type not available yet. - if (!start_code_found || - start_code_offset + start_code_size >= static_cast(size)) { - return false; - } - - // Exit the parser loop when an AUD is found. - // Note: NALU header for an AUD: - // - ref_idc must be 0 - // - type must be Nalu::H264_AUD - if (es[start_code_offset + start_code_size] == Nalu::H264_AUD) +bool EsParserH264::ProcessNalu(const Nalu& nalu, + bool* is_key_frame, + int* pps_id_for_access_unit) { + switch (nalu.type()) { + case Nalu::H264_AUD: { + DVLOG(LOG_LEVEL_ES) << "Nalu: AUD"; break; - - // The current NALU is not an AUD, skip the start code - // and continue parsing the stream. - *stream_pos += start_code_size; - } - - return true; -} - -bool EsParserH264::ParseInternal() { - DCHECK_LE(es_queue_->head(), current_access_unit_pos_); - DCHECK_LE(current_access_unit_pos_, next_access_unit_pos_); - DCHECK_LE(next_access_unit_pos_, es_queue_->tail()); - - // Find the next AUD located at or after |current_access_unit_pos_|. This is - // needed since initially |current_access_unit_pos_| might not point to - // an AUD. - // Discard all the data before the updated |current_access_unit_pos_| - // since it won't be used again. - bool aud_found = FindAUD(¤t_access_unit_pos_); - es_queue_->Trim(current_access_unit_pos_); - if (next_access_unit_pos_ < current_access_unit_pos_) - next_access_unit_pos_ = current_access_unit_pos_; - - // Resume parsing later if no AUD was found. - if (!aud_found) - return true; - - // Find the next AUD to make sure we have a complete access unit. - if (next_access_unit_pos_ < current_access_unit_pos_ + kMinAUDSize) { - next_access_unit_pos_ = current_access_unit_pos_ + kMinAUDSize; - DCHECK_LE(next_access_unit_pos_, es_queue_->tail()); - } - if (!FindAUD(&next_access_unit_pos_)) - return true; - - // At this point, we know we have a full access unit. - bool is_key_frame = false; - int pps_id_for_access_unit = -1; - - const uint8_t* es; - int size; - es_queue_->PeekAt(current_access_unit_pos_, &es, &size); - int access_unit_size = base::checked_cast( - next_access_unit_pos_ - current_access_unit_pos_); - DCHECK_LE(access_unit_size, size); - NaluReader reader(Nalu::kH264, kIsAnnexbByteStream, es, access_unit_size); - - while (true) { - Nalu nalu; - bool is_eos = false; - switch (reader.Advance(&nalu)) { - case NaluReader::kOk: - break; - case NaluReader::kEOStream: - is_eos = true; - break; - default: + } + case Nalu::H264_SPS: { + DVLOG(LOG_LEVEL_ES) << "Nalu: SPS"; + int sps_id; + if (h264_parser_->ParseSps(nalu, &sps_id) != H264Parser::kOk) return false; - } - if (is_eos) + decoder_config_check_pending_ = true; break; - - switch (nalu.type()) { - case Nalu::H264_AUD: { - DVLOG(LOG_LEVEL_ES) << "Nalu: AUD"; - break; - } - case Nalu::H264_SPS: { - DVLOG(LOG_LEVEL_ES) << "Nalu: SPS"; - int sps_id; - if (h264_parser_->ParseSps(nalu, &sps_id) != H264Parser::kOk) + } + case Nalu::H264_PPS: { + DVLOG(LOG_LEVEL_ES) << "Nalu: PPS"; + int pps_id; + if (h264_parser_->ParsePps(nalu, &pps_id) != H264Parser::kOk) { + // Allow PPS parsing to fail if waiting for SPS. + if (last_video_decoder_config_) return false; + } else { decoder_config_check_pending_ = true; - break; } - case Nalu::H264_PPS: { - DVLOG(LOG_LEVEL_ES) << "Nalu: PPS"; - int pps_id; - if (h264_parser_->ParsePps(nalu, &pps_id) != H264Parser::kOk) { - // Allow PPS parsing to fail if waiting for SPS. - if (last_video_decoder_config_) - return false; - } else { - decoder_config_check_pending_ = true; - } - break; - } - case Nalu::H264_IDRSlice: - case Nalu::H264_NonIDRSlice: { - is_key_frame = (nalu.type() == Nalu::H264_IDRSlice); - DVLOG(LOG_LEVEL_ES) << "Nalu: slice IDR=" << is_key_frame; - H264SliceHeader shdr; - if (h264_parser_->ParseSliceHeader(nalu, &shdr) != H264Parser::kOk) { - // Only accept an invalid SPS/PPS at the beginning when the stream - // does not necessarily start with an SPS/PPS/IDR. - if (last_video_decoder_config_) - return false; - } else { - pps_id_for_access_unit = shdr.pic_parameter_set_id; - } - break; - } - default: { - DVLOG(LOG_LEVEL_ES) << "Nalu: " << nalu.type(); + break; + } + case Nalu::H264_IDRSlice: + case Nalu::H264_NonIDRSlice: { + *is_key_frame = (nalu.type() == Nalu::H264_IDRSlice); + DVLOG(LOG_LEVEL_ES) << "Nalu: slice IDR=" << is_key_frame; + H264SliceHeader shdr; + if (h264_parser_->ParseSliceHeader(nalu, &shdr) != H264Parser::kOk) { + // Only accept an invalid SPS/PPS at the beginning when the stream + // does not necessarily start with an SPS/PPS/IDR. + if (last_video_decoder_config_) + return false; + } else { + *pps_id_for_access_unit = shdr.pic_parameter_set_id; } + break; + } + default: { + DVLOG(LOG_LEVEL_ES) << "Nalu: " << nalu.type(); } } - if (waiting_for_key_frame_) { - waiting_for_key_frame_ = !is_key_frame; - } - if (!waiting_for_key_frame_) { - // Emit a frame and move the stream to the next AUD position. - RCHECK(EmitFrame(current_access_unit_pos_, access_unit_size, - is_key_frame, pps_id_for_access_unit)); - } - current_access_unit_pos_ = next_access_unit_pos_; - es_queue_->Trim(current_access_unit_pos_); - return true; } -bool EsParserH264::EmitFrame(int64_t access_unit_pos, - int access_unit_size, - bool is_key_frame, - int pps_id) { - // Get the access unit timing info. - TimingDesc current_timing_desc = {kNoTimestamp, kNoTimestamp}; - while (!timing_desc_list_.empty() && - timing_desc_list_.front().first <= access_unit_pos) { - current_timing_desc = timing_desc_list_.front().second; - timing_desc_list_.pop_front(); - } - if (current_timing_desc.pts == kNoTimestamp) - return false; +bool EsParserH264::UpdateVideoDecoderConfig(int pps_id) { + // Update the video decoder configuration if needed. + if (!decoder_config_check_pending_) + return true; - // Emit a frame. - DVLOG(LOG_LEVEL_ES) << "Emit frame: stream_pos=" << current_access_unit_pos_ - << " size=" << access_unit_size; - int es_size; - const uint8_t* es; - es_queue_->PeekAt(current_access_unit_pos_, &es, &es_size); - CHECK_GE(es_size, access_unit_size); - - // Convert frame to unit stream format. - std::vector converted_frame; - if (!stream_converter_->ConvertByteStreamToNalUnitStream( - es, access_unit_size, &converted_frame)) { - DLOG(ERROR) << "Failure to convert video frame to unit stream format."; - return false; + const H264Pps* pps = h264_parser_->GetPps(pps_id); + const H264Sps* sps; + if (!pps) { + // Only accept an invalid PPS at the beginning when the stream + // does not necessarily start with an SPS/PPS/IDR. + // In this case, the initial frames are conveyed to the upper layer with + // an invalid VideoDecoderConfig and it's up to the upper layer + // to process this kind of frame accordingly. + return last_video_decoder_config_ == nullptr; + } else { + sps = h264_parser_->GetSps(pps->seq_parameter_set_id); + if (!sps) + return false; + decoder_config_check_pending_ = false; } - if (decoder_config_check_pending_) { - // Update the video decoder configuration if needed. - const H264Pps* pps = h264_parser_->GetPps(pps_id); - if (!pps) { - // Only accept an invalid PPS at the beginning when the stream - // does not necessarily start with an SPS/PPS/IDR. - // In this case, the initial frames are conveyed to the upper layer with - // an invalid VideoDecoderConfig and it's up to the upper layer - // to process this kind of frame accordingly. - if (last_video_decoder_config_) - return false; - } else { - const H264Sps* sps = h264_parser_->GetSps(pps->seq_parameter_set_id); - if (!sps) - return false; - RCHECK(UpdateVideoDecoderConfig(sps)); - decoder_config_check_pending_ = false; - } - } - - // Create the media sample, emitting always the previous sample after - // calculating its duration. - scoped_refptr media_sample = MediaSample::CopyFrom( - converted_frame.data(), converted_frame.size(), is_key_frame); - media_sample->set_dts(current_timing_desc.dts); - media_sample->set_pts(current_timing_desc.pts); - if (pending_sample_) { - DCHECK_GT(media_sample->dts(), pending_sample_->dts()); - pending_sample_duration_ = media_sample->dts() - pending_sample_->dts(); - pending_sample_->set_duration(pending_sample_duration_); - emit_sample_cb_.Run(pid(), pending_sample_); - } - pending_sample_ = media_sample; - - return true; -} - -bool EsParserH264::UpdateVideoDecoderConfig(const H264Sps* sps) { std::vector decoder_config_record; - if (!stream_converter_->GetDecoderConfigurationRecord( + if (!stream_converter()->GetDecoderConfigurationRecord( &decoder_config_record)) { DLOG(ERROR) << "Failure to construct an AVCDecoderConfigurationRecord"; return false; diff --git a/packager/media/formats/mp2t/es_parser_h264.h b/packager/media/formats/mp2t/es_parser_h264.h index e56ca0f5b5..924119fff9 100644 --- a/packager/media/formats/mp2t/es_parser_h264.h +++ b/packager/media/formats/mp2t/es_parser_h264.h @@ -7,21 +7,14 @@ #include -#include -#include - #include "packager/base/callback.h" -#include "packager/base/compiler_specific.h" #include "packager/base/memory/scoped_ptr.h" -#include "packager/media/formats/mp2t/es_parser.h" +#include "packager/media/formats/mp2t/es_parser_h26x.h" namespace edash_packager { namespace media { -class H264ByteToUnitStreamConverter; class H264Parser; -class OffsetByteQueue; -struct H264Sps; namespace mp2t { @@ -30,74 +23,34 @@ namespace mp2t { // Mpeg2 TS spec: "2.14 Carriage of Rec. ITU-T H.264 | ISO/IEC 14496-10 video" // "Each AVC access unit shall contain an access unit delimiter NAL Unit;" // -class EsParserH264 : public EsParser { +class EsParserH264 : public EsParserH26x { public: EsParserH264(uint32_t pid, const NewStreamInfoCB& new_stream_info_cb, const EmitSampleCB& emit_sample_cb); ~EsParserH264() override; - // EsParser implementation overrides. - bool Parse(const uint8_t* buf, int size, int64_t pts, int64_t dts) override; - void Flush() override; + // EsParserH26x implementation override. void Reset() override; private: - struct TimingDesc { - int64_t dts; - int64_t pts; - }; - - // Find the AUD located at or after |*stream_pos|. - // Return true if an AUD is found. - // If found, |*stream_pos| corresponds to the position of the AUD start code - // in the stream. Otherwise, |*stream_pos| corresponds to the last position - // of the start code parser. - bool FindAUD(int64_t* stream_pos); - - // Resumes the H264 ES parsing. - // Return true if successful. - bool ParseInternal(); - - // Emit a frame whose position in the ES queue starts at |access_unit_pos|. - // Returns true if successful, false if no PTS is available for the frame. - bool EmitFrame(int64_t access_unit_pos, - int access_unit_size, - bool is_key_frame, - int pps_id); + // Processes a NAL unit found in ParseInternal. The @a pps_id_for_access_unit + // value will be passed to UpdateVideoDecoderConfig. + bool ProcessNalu(const Nalu& nalu, + bool* is_key_frame, + int* pps_id_for_access_unit) override; // Update the video decoder config based on an H264 SPS. // Return true if successful. - bool UpdateVideoDecoderConfig(const H264Sps* sps); + bool UpdateVideoDecoderConfig(int sps_id) override; - // Callbacks to pass the stream configuration and the frames. + // Callback to pass the stream configuration. NewStreamInfoCB new_stream_info_cb_; - EmitSampleCB emit_sample_cb_; - // Bytes of the ES stream that have not been emitted yet. - scoped_ptr es_queue_; - std::list > timing_desc_list_; - - // H264 parser state. - // - |current_access_unit_pos_| is pointing to an annexB syncword - // representing the first NALU of an H264 access unit. - scoped_ptr h264_parser_; - int64_t current_access_unit_pos_; - int64_t next_access_unit_pos_; - - // Filter to convert H.264 Annex B byte stream to unit stream. - scoped_ptr stream_converter_; - - // Last video decoder config. scoped_refptr last_video_decoder_config_; bool decoder_config_check_pending_; - // Frame for which we do not yet have a duration. - scoped_refptr pending_sample_; - uint64_t pending_sample_duration_; - - // Indicates whether waiting for first key frame. - bool waiting_for_key_frame_; + scoped_ptr h264_parser_; }; } // namespace mp2t diff --git a/packager/media/formats/mp2t/es_parser_h265.cc b/packager/media/formats/mp2t/es_parser_h265.cc new file mode 100644 index 0000000000..985231889a --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_h265.cc @@ -0,0 +1,173 @@ +// Copyright 2016 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/formats/mp2t/es_parser_h265.h" + +#include + +#include "packager/base/logging.h" +#include "packager/media/base/media_sample.h" +#include "packager/media/base/offset_byte_queue.h" +#include "packager/media/base/timestamp.h" +#include "packager/media/base/video_stream_info.h" +#include "packager/media/filters/hevc_decoder_configuration.h" +#include "packager/media/filters/h265_parser.h" +#include "packager/media/filters/h26x_byte_to_unit_stream_converter.h" +#include "packager/media/formats/mp2t/mp2t_common.h" + +namespace edash_packager { +namespace media { +namespace mp2t { + +EsParserH265::EsParserH265(uint32_t pid, + const NewStreamInfoCB& new_stream_info_cb, + const EmitSampleCB& emit_sample_cb) + : EsParserH26x(Nalu::kH265, pid, emit_sample_cb), + new_stream_info_cb_(new_stream_info_cb), + decoder_config_check_pending_(false), + h265_parser_(new H265Parser()) {} + +EsParserH265::~EsParserH265() {} + +void EsParserH265::Reset() { + DVLOG(1) << "EsParserH265::Reset"; + h265_parser_.reset(new H265Parser()); + last_video_decoder_config_ = scoped_refptr(); + decoder_config_check_pending_ = false; + EsParserH26x::Reset(); +} + +bool EsParserH265::ProcessNalu(const Nalu& nalu, + bool* is_key_frame, + int* pps_id_for_access_unit) { + switch (nalu.type()) { + case Nalu::H265_AUD: { + DVLOG(LOG_LEVEL_ES) << "Nalu: AUD"; + break; + } + case Nalu::H265_SPS: { + DVLOG(LOG_LEVEL_ES) << "Nalu: SPS"; + int sps_id; + if (h265_parser_->ParseSps(nalu, &sps_id) != H265Parser::kOk) + return false; + decoder_config_check_pending_ = true; + break; + } + case Nalu::H265_PPS: { + DVLOG(LOG_LEVEL_ES) << "Nalu: PPS"; + int pps_id; + if (h265_parser_->ParsePps(nalu, &pps_id) != H265Parser::kOk) { + // Allow PPS parsing to fail if waiting for SPS. + if (last_video_decoder_config_) + return false; + } else { + decoder_config_check_pending_ = true; + } + break; + } + default: { + if (nalu.is_video_slice()) { + *is_key_frame = nalu.type() == Nalu::H265_IDR_W_RADL || + nalu.type() == Nalu::H265_IDR_N_LP; + DVLOG(LOG_LEVEL_ES) << "Nalu: slice KeyFrame=" << is_key_frame; + H265SliceHeader shdr; + if (h265_parser_->ParseSliceHeader(nalu, &shdr) != H265Parser::kOk) { + // Only accept an invalid SPS/PPS at the beginning when the stream + // does not necessarily start with an SPS/PPS/IDR. + if (last_video_decoder_config_) + return false; + } else { + *pps_id_for_access_unit = shdr.pic_parameter_set_id; + } + } else { + DVLOG(LOG_LEVEL_ES) << "Nalu: " << nalu.type(); + } + } + } + + return true; +} + +bool EsParserH265::UpdateVideoDecoderConfig(int pps_id) { + // Update the video decoder configuration if needed. + if (!decoder_config_check_pending_) + return true; + + const H265Pps* pps = h265_parser_->GetPps(pps_id); + const H265Sps* sps; + if (!pps) { + // Only accept an invalid PPS at the beginning when the stream + // does not necessarily start with an SPS/PPS/IDR. + // In this case, the initial frames are conveyed to the upper layer with + // an invalid VideoDecoderConfig and it's up to the upper layer + // to process this kind of frame accordingly. + return last_video_decoder_config_ == nullptr; + } else { + sps = h265_parser_->GetSps(pps->seq_parameter_set_id); + if (!sps) + return false; + decoder_config_check_pending_ = false; + } + + std::vector decoder_config_record; + HEVCDecoderConfiguration decoder_config; + if (!stream_converter()->GetDecoderConfigurationRecord( + &decoder_config_record) || + !decoder_config.Parse(decoder_config_record)) { + DLOG(ERROR) << "Failure to construct an HEVCDecoderConfigurationRecord"; + return false; + } + + if (last_video_decoder_config_) { + if (last_video_decoder_config_->extra_data() != decoder_config_record) { + // Video configuration has changed. Issue warning. + // TODO(tinskip): Check the nature of the configuration change. Only + // minor configuration changes (such as frame ordering) can be handled + // gracefully by decoders without notification. Major changes (such as + // video resolution changes) should be treated as errors. + LOG(WARNING) << "H.265 decoder configuration has changed."; + last_video_decoder_config_->set_extra_data(decoder_config_record); + } + return true; + } + + uint32_t coded_width = 0; + uint32_t coded_height = 0; + uint32_t pixel_width = 0; + uint32_t pixel_height = 0; + if (!ExtractResolutionFromSps(*sps, &coded_width, &coded_height, &pixel_width, + &pixel_height)) { + LOG(ERROR) << "Failed to parse SPS."; + return false; + } + + last_video_decoder_config_ = scoped_refptr( + new VideoStreamInfo( + pid(), + kMpeg2Timescale, + kInfiniteDuration, + kCodecHVC1, + decoder_config.GetCodecString(kCodecHVC1), + std::string(), + coded_width, + coded_height, + pixel_width, + pixel_height, + 0, + H26xByteToUnitStreamConverter::kUnitStreamNaluLengthSize, + decoder_config_record.data(), + decoder_config_record.size(), + false)); + + // Video config notification. + new_stream_info_cb_.Run(last_video_decoder_config_); + + return true; +} + +} // namespace mp2t +} // namespace media +} // namespace edash_packager diff --git a/packager/media/formats/mp2t/es_parser_h265.h b/packager/media/formats/mp2t/es_parser_h265.h new file mode 100644 index 0000000000..a4173e5b87 --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_h265.h @@ -0,0 +1,62 @@ +// Copyright 2016 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_FORMATS_MP2T_ES_PARSER_H265_H_ +#define MEDIA_FORMATS_MP2T_ES_PARSER_H265_H_ + +#include + +#include +#include + +#include "packager/base/callback.h" +#include "packager/base/compiler_specific.h" +#include "packager/base/memory/scoped_ptr.h" +#include "packager/media/formats/mp2t/es_parser_h26x.h" + +namespace edash_packager { +namespace media { + +class H265Parser; + +namespace mp2t { + +class EsParserH265 : public EsParserH26x { + public: + EsParserH265(uint32_t pid, + const NewStreamInfoCB& new_stream_info_cb, + const EmitSampleCB& emit_sample_cb); + ~EsParserH265() override; + + // EsParserH26x implementation override. + void Reset() override; + + private: + // Processes a NAL unit found in ParseInternal. The @a pps_id_for_access_unit + // value will be passed to UpdateVideoDecoderConfig. + bool ProcessNalu(const Nalu& nalu, + bool* is_key_frame, + int* pps_id_for_access_unit) override; + + // Update the video decoder config based on an H264 SPS. + // Return true if successful. + bool UpdateVideoDecoderConfig(int sps_id) override; + + // Callback to pass the stream configuration. + NewStreamInfoCB new_stream_info_cb_; + + // Last video decoder config. + scoped_refptr last_video_decoder_config_; + bool decoder_config_check_pending_; + + scoped_ptr h265_parser_; +}; + +} // namespace mp2t +} // namespace media +} // namespace edash_packager + +#endif // MEDIA_FORMATS_MP2T_ES_PARSER_H265_H_ diff --git a/packager/media/formats/mp2t/es_parser_h26x.cc b/packager/media/formats/mp2t/es_parser_h26x.cc new file mode 100644 index 0000000000..dcc24d28b9 --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_h26x.cc @@ -0,0 +1,296 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#include "packager/media/formats/mp2t/es_parser_h26x.h" + +#include + +#include "packager/base/logging.h" +#include "packager/base/numerics/safe_conversions.h" +#include "packager/media/base/media_sample.h" +#include "packager/media/base/offset_byte_queue.h" +#include "packager/media/base/timestamp.h" +#include "packager/media/base/video_stream_info.h" +#include "packager/media/filters/h264_byte_to_unit_stream_converter.h" +#include "packager/media/filters/h265_byte_to_unit_stream_converter.h" +#include "packager/media/formats/mp2t/mp2t_common.h" + +namespace edash_packager { +namespace media { +namespace mp2t { + +namespace { + +H26xByteToUnitStreamConverter* CreateStreamConverter(Nalu::CodecType type) { + if (type == Nalu::kH264) { + return new H264ByteToUnitStreamConverter(); + } else { + DCHECK_EQ(Nalu::kH265, type); + return new H265ByteToUnitStreamConverter(); + } +} + +} // anonymous namespace + +EsParserH26x::EsParserH26x(Nalu::CodecType type, + uint32_t pid, + const EmitSampleCB& emit_sample_cb) + : EsParser(pid), + emit_sample_cb_(emit_sample_cb), + type_(type), + es_queue_(new media::OffsetByteQueue()), + current_access_unit_pos_(0), + found_access_unit_(false), + stream_converter_(CreateStreamConverter(type)), + pending_sample_duration_(0), + waiting_for_key_frame_(true) { +} + +EsParserH26x::~EsParserH26x() {} + +bool EsParserH26x::Parse(const uint8_t* buf, + int size, + int64_t pts, + int64_t dts) { + // Note: Parse is invoked each time a PES packet has been reassembled. + // Unfortunately, a PES packet does not necessarily map + // to an h264/h265 access unit, although the HLS recommendation is to use one + // PES for each access unit (but this is just a recommendation and some + // streams do not comply with this recommendation). + + // HLS recommendation: "In AVC video, you should have both a DTS and a + // PTS in each PES header". + // However, some streams do not comply with this recommendation. + DVLOG_IF(1, pts == kNoTimestamp) << "Each video PES should have a PTS"; + if (pts != kNoTimestamp) { + TimingDesc timing_desc; + timing_desc.pts = pts; + timing_desc.dts = (dts != kNoTimestamp) ? dts : pts; + + // Link the end of the byte queue with the incoming timing descriptor. + timing_desc_list_.push_back( + std::pair(es_queue_->tail(), timing_desc)); + } + + // Add the incoming bytes to the ES queue. + es_queue_->Push(buf, size); + + // Skip to the first access unit. + if (!found_access_unit_) { + if (!FindNextAccessUnit(current_access_unit_pos_, + ¤t_access_unit_pos_)) { + return true; + } + es_queue_->Trim(current_access_unit_pos_); + found_access_unit_ = true; + } + + return ParseInternal(); +} + +void EsParserH26x::Flush() { + DVLOG(1) << "EsParserH26x::Flush"; + + // Simulate an additional AUD to force emitting the last access unit + // which is assumed to be complete at this point. + if (type_ == Nalu::kH264) { + uint8_t aud[] = {0x00, 0x00, 0x01, 0x09}; + es_queue_->Push(aud, sizeof(aud)); + } else { + DCHECK_EQ(Nalu::kH265, type_); + uint8_t aud[] = {0x00, 0x00, 0x01, 0x46, 0x01}; + es_queue_->Push(aud, sizeof(aud)); + } + ParseInternal(); + + if (pending_sample_) { + // Flush pending sample. + DCHECK(pending_sample_duration_); + pending_sample_->set_duration(pending_sample_duration_); + emit_sample_cb_.Run(pid(), pending_sample_); + pending_sample_ = scoped_refptr(); + } +} + +void EsParserH26x::Reset() { + es_queue_.reset(new media::OffsetByteQueue()); + current_access_unit_pos_ = 0; + found_access_unit_ = false; + timing_desc_list_.clear(); + pending_sample_ = scoped_refptr(); + pending_sample_duration_ = 0; + waiting_for_key_frame_ = true; +} + +bool EsParserH26x::FindNextAccessUnit(int64_t stream_pos, + int64_t* next_unit_pos) { + // TODO(modmaker): Avoid re-parsing by saving old position. + // Every access unit must have a VCL entry and defines the end of the access + // unit. Track it to return on the element after it so we get the whole + // access unit. + bool seen_vcl_nalu = false; + while (true) { + const uint8_t* es; + int size; + es_queue_->PeekAt(stream_pos, &es, &size); + + // Find a start code. + uint64_t start_code_offset; + uint8_t start_code_size; + bool start_code_found = NaluReader::FindStartCode( + es, size, &start_code_offset, &start_code_size); + stream_pos += start_code_offset; + + // No start code found or NALU type not available yet. + if (!start_code_found || + start_code_offset + start_code_size >= static_cast(size)) { + return false; + } + + Nalu nalu; + const uint8_t* nalu_ptr = es + start_code_offset + start_code_size; + size_t nalu_size = size - (start_code_offset + start_code_size); + if (nalu.Initialize(type_, nalu_ptr, nalu_size)) { + // ITU H.264 sec. 7.4.1.2.3 + // H264: The first of the NAL units with |can_start_access_unit() == true| + // after the last VCL NAL unit of a primary coded picture specifies the + // start of a new access unit. |nuh_layer_id()| is for H265 only; it is + // included below for ease of computation (the value is always 0). + // ITU H.265 sec. 7.4.2.4.4 + // H265: The first of the NAL units with |can_start_access_unit() == true| + // after the last VCL NAL unit preceding firstBlPicNalUnit (the first + // VCL NAL unit of a coded picture with nuh_layer_id equal to 0), if + // any, specifies the start of a new access unit. + // TODO(modmaker): This does not handle nuh_layer_id != 0 correctly. + // AUD VCL SEI VCL* VPS VCL + // | Current method splits here. + // | Should split here. + // If we are searching for the first access unit, then stop at the first + // NAL unit that can start an access unit. + if ((seen_vcl_nalu || !found_access_unit_) && + nalu.can_start_access_unit()) { + break; + } + bool is_vcl_nalu = nalu.is_video_slice() && nalu.nuh_layer_id() == 0; + seen_vcl_nalu |= is_vcl_nalu; + } + + // The current NALU is not an AUD, skip the start code + // and continue parsing the stream. + stream_pos += start_code_size; + } + + *next_unit_pos = stream_pos; + return true; +} + +bool EsParserH26x::ParseInternal() { + DCHECK_LE(es_queue_->head(), current_access_unit_pos_); + DCHECK_LE(current_access_unit_pos_, es_queue_->tail()); + + // Resume parsing later if no AUD was found. + int64_t access_unit_end; + if (!FindNextAccessUnit(current_access_unit_pos_, &access_unit_end)) + return true; + + // At this point, we know we have a full access unit. + bool is_key_frame = false; + int pps_id_for_access_unit = -1; + + const uint8_t* es; + int size; + es_queue_->PeekAt(current_access_unit_pos_, &es, &size); + int access_unit_size = base::checked_cast( + access_unit_end - current_access_unit_pos_); + DCHECK_LE(access_unit_size, size); + NaluReader reader(type_, kIsAnnexbByteStream, es, access_unit_size); + + // TODO(modmaker): Consider combining with FindNextAccessUnit to avoid + // scanning the data twice. + while (true) { + Nalu nalu; + bool is_eos = false; + switch (reader.Advance(&nalu)) { + case NaluReader::kOk: + break; + case NaluReader::kEOStream: + is_eos = true; + break; + default: + return false; + } + if (is_eos) + break; + + if (!ProcessNalu(nalu, &is_key_frame, &pps_id_for_access_unit)) + return false; + } + + if (waiting_for_key_frame_) { + waiting_for_key_frame_ = !is_key_frame; + } + if (!waiting_for_key_frame_) { + // Emit a frame and move the stream to the next AUD position. + RCHECK(EmitFrame(current_access_unit_pos_, access_unit_size, + is_key_frame, pps_id_for_access_unit)); + } + current_access_unit_pos_ = access_unit_end; + es_queue_->Trim(current_access_unit_pos_); + + return true; +} + +bool EsParserH26x::EmitFrame(int64_t access_unit_pos, + int access_unit_size, + bool is_key_frame, + int pps_id) { + // Get the access unit timing info. + TimingDesc current_timing_desc = {kNoTimestamp, kNoTimestamp}; + while (!timing_desc_list_.empty() && + timing_desc_list_.front().first <= access_unit_pos) { + current_timing_desc = timing_desc_list_.front().second; + timing_desc_list_.pop_front(); + } + if (current_timing_desc.pts == kNoTimestamp) + return false; + + // Emit a frame. + DVLOG(LOG_LEVEL_ES) << "Emit frame: stream_pos=" << current_access_unit_pos_ + << " size=" << access_unit_size; + int es_size; + const uint8_t* es; + es_queue_->PeekAt(current_access_unit_pos_, &es, &es_size); + CHECK_GE(es_size, access_unit_size); + + // Convert frame to unit stream format. + std::vector converted_frame; + if (!stream_converter_->ConvertByteStreamToNalUnitStream( + es, access_unit_size, &converted_frame)) { + DLOG(ERROR) << "Failure to convert video frame to unit stream format."; + return false; + } + + // Update the video decoder configuration if needed. + RCHECK(UpdateVideoDecoderConfig(pps_id)); + + // Create the media sample, emitting always the previous sample after + // calculating its duration. + scoped_refptr media_sample = MediaSample::CopyFrom( + converted_frame.data(), converted_frame.size(), is_key_frame); + media_sample->set_dts(current_timing_desc.dts); + media_sample->set_pts(current_timing_desc.pts); + if (pending_sample_) { + DCHECK_GT(media_sample->dts(), pending_sample_->dts()); + pending_sample_duration_ = media_sample->dts() - pending_sample_->dts(); + pending_sample_->set_duration(pending_sample_duration_); + emit_sample_cb_.Run(pid(), pending_sample_); + } + pending_sample_ = media_sample; + + return true; +} + +} // namespace mp2t +} // namespace media +} // namespace edash_packager diff --git a/packager/media/formats/mp2t/es_parser_h26x.h b/packager/media/formats/mp2t/es_parser_h26x.h new file mode 100644 index 0000000000..4edff45d33 --- /dev/null +++ b/packager/media/formats/mp2t/es_parser_h26x.h @@ -0,0 +1,108 @@ +// Copyright 2014 The Chromium Authors. All rights reserved. +// Use of this source code is governed by a BSD-style license that can be +// found in the LICENSE file. + +#ifndef MEDIA_FORMATS_MP2T_ES_PARSER_H26x_H_ +#define MEDIA_FORMATS_MP2T_ES_PARSER_H26x_H_ + +#include + +#include + +#include "packager/base/callback.h" +#include "packager/base/compiler_specific.h" +#include "packager/base/memory/scoped_ptr.h" +#include "packager/media/filters/nalu_reader.h" +#include "packager/media/formats/mp2t/es_parser.h" + +namespace edash_packager { +namespace media { + +class H26xByteToUnitStreamConverter; +class OffsetByteQueue; + +namespace mp2t { + +// A base class for common code between the H.264/H.265 es parsers. +class EsParserH26x : public EsParser { + public: + EsParserH26x(Nalu::CodecType type, + uint32_t pid, + const EmitSampleCB& emit_sample_cb); + ~EsParserH26x() override; + + // EsParser implementation overrides. + bool Parse(const uint8_t* buf, int size, int64_t pts, int64_t dts) override; + void Flush() override; + void Reset() override; + + protected: + const H26xByteToUnitStreamConverter* stream_converter() const { + return stream_converter_.get(); + } + + private: + struct TimingDesc { + int64_t dts; + int64_t pts; + }; + + // Processes a NAL unit found in ParseInternal. The @a pps_id_for_access_unit + // value will be passed to UpdateVideoDecoderConfig. + virtual bool ProcessNalu(const Nalu& nalu, + bool* is_key_frame, + int* pps_id_for_access_unit) = 0; + + // Update the video decoder config. + // Return true if successful. + virtual bool UpdateVideoDecoderConfig(int pps_id) = 0; + + // Find the start of the next access unit staring at |stream_pos|. + // Return true if the end is found. + // If found, |*next_unit_start| contains the start of the next access unit. + // Otherwise, |*next_unit_start| is unchanged. + bool FindNextAccessUnit(int64_t stream_pos, int64_t* next_unit_start); + + // Resumes the H264 ES parsing. + // Return true if successful. + bool ParseInternal(); + + // Emit a frame whose position in the ES queue starts at |access_unit_pos|. + // Returns true if successful, false if no PTS is available for the frame. + bool EmitFrame(int64_t access_unit_pos, + int access_unit_size, + bool is_key_frame, + int pps_id); + + // Callback to pass the frames. + EmitSampleCB emit_sample_cb_; + + // The type of stream being parsed. + Nalu::CodecType type_; + + // Bytes of the ES stream that have not been emitted yet. + scoped_ptr es_queue_; + std::list> timing_desc_list_; + + // Parser state. + // - |current_access_unit_pos_| is pointing to an annexB syncword + // representing the first NALU of an access unit. + int64_t current_access_unit_pos_; + bool found_access_unit_; + + // Filter to convert H.264/H.265 Annex B byte stream to unit stream. + scoped_ptr stream_converter_; + + // Frame for which we do not yet have a duration. + scoped_refptr pending_sample_; + uint64_t pending_sample_duration_; + + // Indicates whether waiting for first key frame. + bool waiting_for_key_frame_; +}; + +} // namespace mp2t +} // namespace media +} // namespace edash_packager + +#endif diff --git a/packager/media/formats/mp2t/mp2t.gyp b/packager/media/formats/mp2t/mp2t.gyp index 7c1fb57e6e..9a4df07541 100644 --- a/packager/media/formats/mp2t/mp2t.gyp +++ b/packager/media/formats/mp2t/mp2t.gyp @@ -19,6 +19,10 @@ 'es_parser_adts.h', 'es_parser_h264.cc', 'es_parser_h264.h', + 'es_parser_h265.cc', + 'es_parser_h265.h', + 'es_parser_h26x.cc', + 'es_parser_h26x.h', 'es_parser.h', 'mp2t_media_parser.cc', 'mp2t_media_parser.h', @@ -45,6 +49,7 @@ ], 'dependencies': [ '../../base/media_base.gyp:media_base', + '../../filters/filters.gyp:filters', # TODO(rkuroiwa): AACAudioSpecificConfig is used to create ADTS. # Break this dependency on mp4 by moving it to media/filters. '../../formats/mp4/mp4.gyp:mp4', diff --git a/packager/media/formats/mp2t/mp2t_media_parser.cc b/packager/media/formats/mp2t/mp2t_media_parser.cc index b2c76002bc..60bae5734d 100644 --- a/packager/media/formats/mp2t/mp2t_media_parser.cc +++ b/packager/media/formats/mp2t/mp2t_media_parser.cc @@ -12,6 +12,7 @@ #include "packager/media/formats/mp2t/es_parser.h" #include "packager/media/formats/mp2t/es_parser_adts.h" #include "packager/media/formats/mp2t/es_parser_h264.h" +#include "packager/media/formats/mp2t/es_parser_h265.h" #include "packager/media/formats/mp2t/mp2t_common.h" #include "packager/media/formats/mp2t/ts_packet.h" #include "packager/media/formats/mp2t/ts_section.h" @@ -28,6 +29,7 @@ enum StreamType { kStreamTypeMpeg1Audio = 0x3, kStreamTypeAAC = 0xf, kStreamTypeAVC = 0x1b, + kStreamTypeHEVC = 0x24, }; class PidState { @@ -300,6 +302,14 @@ void Mp2tMediaParser::RegisterPes(int pmt_pid, base::Unretained(this)), base::Bind(&Mp2tMediaParser::OnEmitSample, base::Unretained(this)))); + } else if (stream_type == kStreamTypeHEVC) { + es_parser.reset( + new EsParserH265( + pes_pid, + base::Bind(&Mp2tMediaParser::OnNewStreamInfo, + base::Unretained(this)), + base::Bind(&Mp2tMediaParser::OnEmitSample, + base::Unretained(this)))); } else if (stream_type == kStreamTypeAAC) { es_parser.reset( new EsParserAdts( diff --git a/packager/media/formats/mp2t/mp2t_media_parser_unittest.cc b/packager/media/formats/mp2t/mp2t_media_parser_unittest.cc index 79daf6c104..229bb6f920 100644 --- a/packager/media/formats/mp2t/mp2t_media_parser_unittest.cc +++ b/packager/media/formats/mp2t/mp2t_media_parser_unittest.cc @@ -73,15 +73,12 @@ class Mp2tMediaParserTest : public testing::Test { bool OnNewSample(uint32_t track_id, const scoped_refptr& sample) { - std::string stream_type; StreamMap::const_iterator stream = stream_map_.find(track_id); if (stream != stream_map_.end()) { if (stream->second->stream_type() == kStreamAudio) { ++audio_frame_count_; - stream_type = "audio"; } else if (stream->second->stream_type() == kStreamVideo) { ++video_frame_count_; - stream_type = "video"; if (video_min_dts_ == kNoTimestamp) video_min_dts_ = sample->dts(); // Verify timestamps are increasing. @@ -121,20 +118,36 @@ class Mp2tMediaParserTest : public testing::Test { } }; -TEST_F(Mp2tMediaParserTest, UnalignedAppend17) { +TEST_F(Mp2tMediaParserTest, UnalignedAppend17_H264) { // Test small, non-segment-aligned appends. ParseMpeg2TsFile("bear-640x360.ts", 17); - EXPECT_EQ(video_frame_count_, 79); + EXPECT_EQ(79, video_frame_count_); EXPECT_TRUE(parser_->Flush()); - EXPECT_EQ(video_frame_count_, 82); + EXPECT_EQ(82, video_frame_count_); } -TEST_F(Mp2tMediaParserTest, UnalignedAppend512) { +TEST_F(Mp2tMediaParserTest, UnalignedAppend512_H264) { // Test small, non-segment-aligned appends. ParseMpeg2TsFile("bear-640x360.ts", 512); - EXPECT_EQ(video_frame_count_, 79); + EXPECT_EQ(79, video_frame_count_); EXPECT_TRUE(parser_->Flush()); - EXPECT_EQ(video_frame_count_, 82); + EXPECT_EQ(82, video_frame_count_); +} + +TEST_F(Mp2tMediaParserTest, UnalignedAppend17_H265) { + // Test small, non-segment-aligned appends. + ParseMpeg2TsFile("bear-640x360-hevc.ts", 17); + EXPECT_EQ(79, video_frame_count_); + EXPECT_TRUE(parser_->Flush()); + EXPECT_EQ(82, video_frame_count_); +} + +TEST_F(Mp2tMediaParserTest, UnalignedAppend512_H265) { + // Test small, non-segment-aligned appends. + ParseMpeg2TsFile("bear-640x360-hevc.ts", 512); + EXPECT_EQ(79, video_frame_count_); + EXPECT_TRUE(parser_->Flush()); + EXPECT_EQ(82, video_frame_count_); } TEST_F(Mp2tMediaParserTest, TimestampWrapAround) { @@ -143,7 +156,7 @@ TEST_F(Mp2tMediaParserTest, TimestampWrapAround) { // wrap around in the Mpeg2 TS stream. ParseMpeg2TsFile("bear-640x360_ptswraparound.ts", 512); EXPECT_TRUE(parser_->Flush()); - EXPECT_EQ(video_frame_count_, 82); + EXPECT_EQ(82, video_frame_count_); EXPECT_LT(video_min_dts_, static_cast(1) << 33); EXPECT_GT(video_max_dts_, static_cast(1) << 33); } diff --git a/packager/media/test/data/README b/packager/media/test/data/README index 1d35ae6a6c..3e3a474b03 100644 --- a/packager/media/test/data/README +++ b/packager/media/test/data/README @@ -29,6 +29,7 @@ vorbis-packet-3 - timestamp: 2902ms, duration: 0ms bear-640x360.ts - AVC + AAC encode, multiplexed into an MPEG2-TS container. bear-640x360_ptswraparound.ts - Same as bear-640x360.ts, with a timestamp wrap-around in the middle, created with the below command: ffmpeg -itsoffset 95442 -i bear-640x360.ts -c:v copy -c:a copy -muxdelay 0 bear-640x360_ptswraparound.ts +bear-640x360-hevc.ts - HEVC + AAC encode, multiplexed into an MPEG2-TS container. // ISO-BMFF streams. bear-1280x720.mp4 - AVC + AAC encode, mulitplexed into an ISOBMFF container. diff --git a/packager/media/test/data/bear-640x360-hevc.ts b/packager/media/test/data/bear-640x360-hevc.ts new file mode 100644 index 0000000000000000000000000000000000000000..14ad463f719d3e679056e4448d78cf25c0f557e3 GIT binary patch literal 155476 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