diff options
Diffstat (limited to 'media/filters')
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 31 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 16 | ||||
-rw-r--r-- | media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc | 93 | ||||
-rw-r--r-- | media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h | 49 | ||||
-rw-r--r-- | media/filters/h264_parser.cc | 7 | ||||
-rw-r--r-- | media/filters/h264_parser.h | 12 | ||||
-rw-r--r-- | media/filters/h265_parser.cc | 159 | ||||
-rw-r--r-- | media/filters/h265_parser.h | 151 | ||||
-rw-r--r-- | media/filters/h265_parser_unittest.cc | 43 | ||||
-rw-r--r-- | media/filters/stream_parser_factory.cc | 13 |
10 files changed, 565 insertions, 9 deletions
diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc index bbc6079..f6ed205 100644 --- a/media/filters/ffmpeg_demuxer.cc +++ b/media/filters/ffmpeg_demuxer.cc @@ -34,6 +34,10 @@ #include "media/filters/webvtt_util.h" #include "media/formats/webm/webm_crypto_helpers.h" +#if defined(ENABLE_HEVC_DEMUXING) +#include "media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h" +#endif + namespace media { static base::Time ExtractTimelineOffset(AVFormatContext* format_context) { @@ -548,6 +552,11 @@ void FFmpegDemuxerStream::InitBitstreamConverter() { if (stream_->codec->codec_id == AV_CODEC_ID_H264) { bitstream_converter_.reset( new FFmpegH264ToAnnexBBitstreamConverter(stream_->codec)); +#if defined(ENABLE_HEVC_DEMUXING) + } else if (stream_->codec->codec_id == AV_CODEC_ID_HEVC) { + bitstream_converter_.reset( + new FFmpegH265ToAnnexBBitstreamConverter(stream_->codec)); +#endif } else if (stream_->codec->codec_id == AV_CODEC_ID_AAC) { bitstream_converter_.reset( new FFmpegAACBitstreamConverter(stream_->codec)); @@ -996,6 +1005,28 @@ void FFmpegDemuxer::OnFindStreamInfoDone(const PipelineStatusCB& status_cb, if (video_stream) continue; + +#if defined(ENABLE_HEVC_DEMUXING) + if (stream->codec->codec_id == AV_CODEC_ID_HEVC) { + // If ffmpeg is built without HEVC parser/decoder support, it will be + // able to demux HEVC based solely on container-provided information, + // but unable to get some of the parameters without parsing the stream + // (e.g. coded size needs to be read from SPS, pixel format is typically + // deduced from decoder config in hvcC box). These are not really needed + // when using external decoder (e.g. hardware decoder), so override them + // here, to make sure this translates into a valid VideoDecoderConfig. + if (stream->codec->coded_width == 0 && + stream->codec->coded_height == 0) { + DCHECK(stream->codec->width > 0); + DCHECK(stream->codec->height > 0); + stream->codec->coded_width = stream->codec->width; + stream->codec->coded_height = stream->codec->height; + } + if (stream->codec->pix_fmt == AV_PIX_FMT_NONE) { + stream->codec->pix_fmt = PIX_FMT_YUV420P; + } + } +#endif // Log the codec detected, whether it is supported or not. UMA_HISTOGRAM_SPARSE_SLOWLY("Media.DetectedVideoCodec", codec_context->codec_id); diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc index ac3ad97..21158c2 100644 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ b/media/filters/ffmpeg_demuxer_unittest.cc @@ -1072,4 +1072,20 @@ TEST_F(FFmpegDemuxerTest, NaturalSizeWithPASP) { #endif +#if defined(ENABLE_HEVC_DEMUXING) +TEST_F(FFmpegDemuxerTest, HEVC_in_MP4_container) { + CreateDemuxer("bear-hevc-frag.mp4"); + InitializeDemuxer(); + + DemuxerStream* video = demuxer_->GetStream(DemuxerStream::VIDEO); + ASSERT_TRUE(video); + + video->Read(NewReadCB(FROM_HERE, 3569, 66733, true)); + message_loop_.Run(); + + video->Read(NewReadCB(FROM_HERE, 1042, 200200, false)); + message_loop_.Run(); +} +#endif + } // namespace media diff --git a/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc b/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc new file mode 100644 index 0000000..a643f53 --- /dev/null +++ b/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc @@ -0,0 +1,93 @@ +// Copyright 2015 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 "media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h" + +#include "base/logging.h" +#include "media/base/decrypt_config.h" +#include "media/ffmpeg/ffmpeg_common.h" +#include "media/formats/mp4/avc.h" +#include "media/formats/mp4/box_definitions.h" +#include "media/formats/mp4/hevc.h" + +namespace media { + +FFmpegH265ToAnnexBBitstreamConverter::FFmpegH265ToAnnexBBitstreamConverter( + AVCodecContext* stream_codec_context) + : stream_codec_context_(stream_codec_context) { + CHECK(stream_codec_context_); +} + +FFmpegH265ToAnnexBBitstreamConverter::~FFmpegH265ToAnnexBBitstreamConverter() {} + +bool FFmpegH265ToAnnexBBitstreamConverter::ConvertPacket(AVPacket* packet) { + DVLOG(3) << __FUNCTION__; + if (packet == NULL || !packet->data) + return false; + + // Calculate the needed output buffer size. + if (!hevc_config_) { + if (!stream_codec_context_->extradata || + stream_codec_context_->extradata_size <= 0) { + DVLOG(1) << "HEVCDecoderConfiguration not found, no extra codec data"; + return false; + } + + hevc_config_.reset(new mp4::HEVCDecoderConfigurationRecord()); + + if (!hevc_config_->Parse( + stream_codec_context_->extradata, + stream_codec_context_->extradata_size)) { + DVLOG(1) << "Parsing HEVCDecoderConfiguration failed"; + return false; + } + } + + std::vector<uint8> input_frame; + std::vector<SubsampleEntry> subsamples; + // TODO(servolk): Performance could be improved here, by reducing unnecessary + // data copying, but first annex b conversion code needs to be refactored to + // allow that (see crbug.com/455379). + input_frame.insert(input_frame.end(), + packet->data, packet->data + packet->size); + int nalu_size_len = hevc_config_->lengthSizeMinusOne + 1; + if (!mp4::AVC::ConvertFrameToAnnexB(nalu_size_len, &input_frame, + &subsamples)) { + DVLOG(1) << "AnnexB conversion failed"; + return false; + } + + if (packet->flags & AV_PKT_FLAG_KEY) { + RCHECK(mp4::HEVC::InsertParamSetsAnnexB(*hevc_config_.get(), + &input_frame, &subsamples)); + DVLOG(4) << "Inserted HEVC decoder params"; + } + + uint32 output_packet_size = input_frame.size(); + + if (output_packet_size == 0) + return false; // Invalid input packet. + + // Allocate new packet for the output. + AVPacket dest_packet; + if (av_new_packet(&dest_packet, output_packet_size) != 0) + return false; // Memory allocation failure. + + // This is a bit tricky: since the interface does not allow us to replace + // the pointer of the old packet with a new one, we will initially copy the + // metadata from old packet to new bigger packet. + av_packet_copy_props(&dest_packet, packet); + + // Proceed with the conversion of the actual in-band NAL units, leave room + // for configuration in the beginning. + memcpy(dest_packet.data, &input_frame[0], input_frame.size()); + + // At the end we must destroy the old packet. + av_free_packet(packet); + *packet = dest_packet; // Finally, replace the values in the input packet. + + return true; +} + +} // namespace media diff --git a/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h b/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h new file mode 100644 index 0000000..5892f42 --- /dev/null +++ b/media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h @@ -0,0 +1,49 @@ +// Copyright 2015 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_FILTERS_FFMPEG_H265_TO_ANNEX_B_BITSTREAM_CONVERTER_H_ +#define MEDIA_FILTERS_FFMPEG_H265_TO_ANNEX_B_BITSTREAM_CONVERTER_H_ + +#include "base/basictypes.h" +#include "base/memory/scoped_ptr.h" +#include "media/base/media_export.h" +#include "media/filters/ffmpeg_bitstream_converter.h" +#include "media/formats/mp4/hevc.h" + +// Forward declarations for FFmpeg datatypes used. +struct AVCodecContext; +struct AVPacket; + +namespace media { + +// Bitstream converter that converts H.265 bitstream based FFmpeg packets into +// H.265 Annex B bytestream format. +class MEDIA_EXPORT FFmpegH265ToAnnexBBitstreamConverter + : public FFmpegBitstreamConverter { + public: + // The |stream_codec_context| will be used during conversion and should be the + // AVCodecContext for the stream sourcing these packets. A reference to + // |stream_codec_context| is retained, so it must outlive this class. + explicit FFmpegH265ToAnnexBBitstreamConverter( + AVCodecContext* stream_codec_context); + + ~FFmpegH265ToAnnexBBitstreamConverter() override; + + // FFmpegBitstreamConverter implementation. + bool ConvertPacket(AVPacket* packet) override; + + private: + scoped_ptr<mp4::HEVCDecoderConfigurationRecord> hevc_config_; + + // Variable to hold a pointer to memory where we can access the global + // data from the FFmpeg file format's global headers. + AVCodecContext* stream_codec_context_; + + DISALLOW_COPY_AND_ASSIGN(FFmpegH265ToAnnexBBitstreamConverter); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_FFMPEG_H265_TO_ANNEX_B_BITSTREAM_CONVERTER_H_ + diff --git a/media/filters/h264_parser.cc b/media/filters/h264_parser.cc index 22d420b..fe2a443 100644 --- a/media/filters/h264_parser.cc +++ b/media/filters/h264_parser.cc @@ -213,6 +213,7 @@ bool H264Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) { off_t annexb_start_code_size = 0; if (!FindStartCodeInClearRanges(stream_, bytes_left_, + encrypted_ranges_, &nalu_start_off, &annexb_start_code_size)) { DVLOG(4) << "Could not find start code, end of stream?"; return false; @@ -238,6 +239,7 @@ bool H264Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) { off_t next_start_code_size = 0; off_t nalu_size_without_start_code = 0; if (!FindStartCodeInClearRanges(nalu_data, max_nalu_data_size, + encrypted_ranges_, &nalu_size_without_start_code, &next_start_code_size)) { nalu_size_without_start_code = max_nalu_data_size; @@ -250,9 +252,10 @@ bool H264Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) { bool H264Parser::FindStartCodeInClearRanges( const uint8* data, off_t data_size, + const Ranges<const uint8*>& encrypted_ranges, off_t* offset, off_t* start_code_size) { - if (encrypted_ranges_.size() == 0) + if (encrypted_ranges.size() == 0) return FindStartCode(data, data_size, offset, start_code_size); DCHECK_GE(data_size, 0); @@ -270,7 +273,7 @@ bool H264Parser::FindStartCodeInClearRanges( Ranges<const uint8*> start_code_range; start_code_range.Add(start_code, start_code_end + 1); - if (encrypted_ranges_.IntersectionWith(start_code_range).size() > 0) { + if (encrypted_ranges.IntersectionWith(start_code_range).size() > 0) { // The start code is inside an encrypted section so we need to scan // for another start code. *start_code_size = 0; diff --git a/media/filters/h264_parser.h b/media/filters/h264_parser.h index b8dde50..36467ba 100644 --- a/media/filters/h264_parser.h +++ b/media/filters/h264_parser.h @@ -341,6 +341,12 @@ class MEDIA_EXPORT H264Parser { static bool FindStartCode(const uint8* data, off_t data_size, off_t* offset, off_t* start_code_size); + // Wrapper for FindStartCode() that skips over start codes that + // may appear inside of |encrypted_ranges_|. + // Returns true if a start code was found. Otherwise returns false. + static bool FindStartCodeInClearRanges(const uint8* data, off_t data_size, + const Ranges<const uint8*>& ranges, + off_t* offset, off_t* start_code_size); H264Parser(); ~H264Parser(); @@ -406,12 +412,6 @@ class MEDIA_EXPORT H264Parser { // - the size in bytes of the start code is returned in |*start_code_size|. bool LocateNALU(off_t* nalu_size, off_t* start_code_size); - // Wrapper for FindStartCode() that skips over start codes that - // may appear inside of |encrypted_ranges_|. - // Returns true if a start code was found. Otherwise returns false. - bool FindStartCodeInClearRanges(const uint8* data, off_t data_size, - off_t* offset, off_t* start_code_size); - // Exp-Golomb code parsing as specified in chapter 9.1 of the spec. // Read one unsigned exp-Golomb code from the stream and return in |*val|. Result ReadUE(int* val); diff --git a/media/filters/h265_parser.cc b/media/filters/h265_parser.cc new file mode 100644 index 0000000..30a8233 --- /dev/null +++ b/media/filters/h265_parser.cc @@ -0,0 +1,159 @@ +// Copyright 2015 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 "base/logging.h" +#include "base/memory/scoped_ptr.h" +#include "base/stl_util.h" + +#include "media/base/decrypt_config.h" +#include "media/filters/h265_parser.h" + +namespace media { + +#define READ_BITS_OR_RETURN(num_bits, out) \ + do { \ + int _out; \ + if (!br_.ReadBits(num_bits, &_out)) { \ + DVLOG(1) \ + << "Error in stream: unexpected EOS while trying to read " #out; \ + return kInvalidStream; \ + } \ + *out = _out; \ + } while (0) + +#define TRUE_OR_RETURN(a) \ + do { \ + if (!(a)) { \ + DVLOG(1) << "Error in stream: invalid value, expected " << #a; \ + return kInvalidStream; \ + } \ + } while (0) + +H265NALU::H265NALU() { + memset(this, 0, sizeof(*this)); +} + +H265Parser::H265Parser() { + Reset(); +} + +H265Parser::~H265Parser() { +} + +void H265Parser::Reset() { + stream_ = NULL; + bytes_left_ = 0; + encrypted_ranges_.clear(); +} + +void H265Parser::SetStream(const uint8* stream, off_t stream_size) { + std::vector<SubsampleEntry> subsamples; + SetEncryptedStream(stream, stream_size, subsamples); +} + +void H265Parser::SetEncryptedStream( + const uint8* stream, off_t stream_size, + const std::vector<SubsampleEntry>& subsamples) { + DCHECK(stream); + DCHECK_GT(stream_size, 0); + + stream_ = stream; + bytes_left_ = stream_size; + + encrypted_ranges_.clear(); + const uint8* start = stream; + const uint8* stream_end = stream_ + bytes_left_; + for (size_t i = 0; i < subsamples.size() && start < stream_end; ++i) { + start += subsamples[i].clear_bytes; + + const uint8* end = std::min(start + subsamples[i].cypher_bytes, stream_end); + encrypted_ranges_.Add(start, end); + start = end; + } +} + +bool H265Parser::LocateNALU(off_t* nalu_size, off_t* start_code_size) { + // Find the start code of next NALU. + off_t nalu_start_off = 0; + off_t annexb_start_code_size = 0; + + if (!H264Parser::FindStartCodeInClearRanges(stream_, bytes_left_, + encrypted_ranges_, + &nalu_start_off, + &annexb_start_code_size)) { + DVLOG(4) << "Could not find start code, end of stream?"; + return false; + } + + // Move the stream to the beginning of the NALU (pointing at the start code). + stream_ += nalu_start_off; + bytes_left_ -= nalu_start_off; + + const uint8* nalu_data = stream_ + annexb_start_code_size; + off_t max_nalu_data_size = bytes_left_ - annexb_start_code_size; + if (max_nalu_data_size <= 0) { + DVLOG(3) << "End of stream"; + return false; + } + + // Find the start code of next NALU; + // if successful, |nalu_size_without_start_code| is the number of bytes from + // after previous start code to before this one; + // if next start code is not found, it is still a valid NALU since there + // are some bytes left after the first start code: all the remaining bytes + // belong to the current NALU. + off_t next_start_code_size = 0; + off_t nalu_size_without_start_code = 0; + if (!H264Parser::FindStartCodeInClearRanges(nalu_data, max_nalu_data_size, + encrypted_ranges_, + &nalu_size_without_start_code, + &next_start_code_size)) { + nalu_size_without_start_code = max_nalu_data_size; + } + *nalu_size = nalu_size_without_start_code + annexb_start_code_size; + *start_code_size = annexb_start_code_size; + return true; +} + +H265Parser::Result H265Parser::AdvanceToNextNALU(H265NALU* nalu) { + off_t start_code_size; + off_t nalu_size_with_start_code; + if (!LocateNALU(&nalu_size_with_start_code, &start_code_size)) { + DVLOG(4) << "Could not find next NALU, bytes left in stream: " + << bytes_left_; + return kEOStream; + } + + nalu->data = stream_ + start_code_size; + nalu->size = nalu_size_with_start_code - start_code_size; + DVLOG(4) << "NALU found: size=" << nalu_size_with_start_code; + + // Initialize bit reader at the start of found NALU. + if (!br_.Initialize(nalu->data, nalu->size)) + return kEOStream; + + // Move parser state to after this NALU, so next time AdvanceToNextNALU + // is called, we will effectively be skipping it; + // other parsing functions will use the position saved + // in bit reader for parsing, so we don't have to remember it here. + stream_ += nalu_size_with_start_code; + bytes_left_ -= nalu_size_with_start_code; + + // Read NALU header, skip the forbidden_zero_bit, but check for it. + int data; + READ_BITS_OR_RETURN(1, &data); + TRUE_OR_RETURN(data == 0); + + READ_BITS_OR_RETURN(6, &nalu->nal_unit_type); + READ_BITS_OR_RETURN(6, &nalu->nuh_layer_id); + READ_BITS_OR_RETURN(3, &nalu->nuh_temporal_id_plus1); + + DVLOG(4) << "NALU type: " << static_cast<int>(nalu->nal_unit_type) + << " at: " << reinterpret_cast<const void*>(nalu->data) + << " size: " << nalu->size; + + return kOk; +} + +} // namespace media diff --git a/media/filters/h265_parser.h b/media/filters/h265_parser.h new file mode 100644 index 0000000..f3cf713 --- /dev/null +++ b/media/filters/h265_parser.h @@ -0,0 +1,151 @@ +// Copyright 2015 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. +// +// This file contains an implementation of an H265 Annex-B video stream parser. + +#ifndef MEDIA_FILTERS_H265_PARSER_H_ +#define MEDIA_FILTERS_H265_PARSER_H_ + +#include <sys/types.h> + +#include <map> +#include <vector> + +#include "base/basictypes.h" +#include "base/macros.h" +#include "media/base/media_export.h" +#include "media/base/ranges.h" +#include "media/filters/h264_bit_reader.h" +#include "media/filters/h264_parser.h" + +namespace media { + +struct SubsampleEntry; + +// For explanations of each struct and its members, see H.265 specification +// at http://www.itu.int/rec/T-REC-H.265. +struct MEDIA_EXPORT H265NALU { + H265NALU(); + + // NAL Unit types are taken from Table 7-1 of HEVC/H265 standard + // http://www.itu.int/rec/T-REC-H.265-201410-I/en + enum Type { + TRAIL_N = 0, + TRAIL_R = 1, + TSA_N = 2, + TSA_R = 3, + STSA_N = 4, + STSA_R = 5, + RADL_N = 6, + RADL_R = 7, + RASL_N = 8, + RASL_R = 9, + RSV_VCL_N10 = 10, + RSV_VCL_R11 = 11, + RSV_VCL_N12 = 12, + RSV_VCL_R13 = 13, + RSV_VCL_N14 = 14, + RSV_VCL_R15 = 15, + BLA_W_LP = 16, + BLA_W_RADL = 17, + BLA_N_LP = 18, + IDR_W_RADL = 19, + IDR_N_LP = 20, + CRA_NUT = 21, + RSV_IRAP_VCL22 = 22, + RSV_IRAP_VCL23 = 23, + RSV_VCL24 = 24, + RSV_VCL25 = 25, + RSV_VCL26 = 26, + RSV_VCL27 = 27, + RSV_VCL28 = 28, + RSV_VCL29 = 29, + RSV_VCL30 = 30, + RSV_VCL31 = 31, + VPS_NUT = 32, + SPS_NUT = 33, + PPS_NUT = 34, + AUD_NUT = 35, + EOS_NUT = 36, + EOB_NUT = 37, + FD_NUT = 38, + PREFIX_SEI_NUT = 39, + SUFFIX_SEI_NUT = 40, + RSV_NVCL41 = 41, + RSV_NVCL42 = 42, + RSV_NVCL43 = 43, + RSV_NVCL44 = 44, + RSV_NVCL45 = 45, + RSV_NVCL46 = 46, + RSV_NVCL47 = 47, + }; + + // After (without) start code; we don't own the underlying memory + // and a shallow copy should be made when copying this struct. + const uint8* data; + off_t size; // From after start code to start code of next NALU (or EOS). + + int nal_unit_type; + int nuh_layer_id; + int nuh_temporal_id_plus1; +}; + +// Class to parse an Annex-B H.265 stream. +class MEDIA_EXPORT H265Parser { + public: + enum Result { + kOk, + kInvalidStream, // error in stream + kUnsupportedStream, // stream not supported by the parser + kEOStream, // end of stream + }; + + H265Parser(); + ~H265Parser(); + + void Reset(); + // Set current stream pointer to |stream| of |stream_size| in bytes, + // |stream| owned by caller. + // |subsamples| contains information about what parts of |stream| are + // encrypted. + void SetStream(const uint8* stream, off_t stream_size); + void SetEncryptedStream(const uint8* stream, off_t stream_size, + const std::vector<SubsampleEntry>& subsamples); + + // Read the stream to find the next NALU, identify it and return + // that information in |*nalu|. This advances the stream to the beginning + // of this NALU, but not past it, so subsequent calls to NALU-specific + // parsing functions (ParseSPS, etc.) will parse this NALU. + // If the caller wishes to skip the current NALU, it can call this function + // again, instead of any NALU-type specific parse functions below. + Result AdvanceToNextNALU(H265NALU* nalu); + + private: + // Move the stream pointer to the beginning of the next NALU, + // i.e. pointing at the next start code. + // Return true if a NALU has been found. + // If a NALU is found: + // - its size in bytes is returned in |*nalu_size| and includes + // the start code as well as the trailing zero bits. + // - the size in bytes of the start code is returned in |*start_code_size|. + bool LocateNALU(off_t* nalu_size, off_t* start_code_size); + + // Pointer to the current NALU in the stream. + const uint8* stream_; + + // Bytes left in the stream after the current NALU. + off_t bytes_left_; + + H264BitReader br_; + + // Ranges of encrypted bytes in the buffer passed to + // SetEncryptedStream(). + Ranges<const uint8*> encrypted_ranges_; + + DISALLOW_COPY_AND_ASSIGN(H265Parser); +}; + +} // namespace media + +#endif // MEDIA_FILTERS_H265_PARSER_H_ diff --git a/media/filters/h265_parser_unittest.cc b/media/filters/h265_parser_unittest.cc new file mode 100644 index 0000000..c590b50 --- /dev/null +++ b/media/filters/h265_parser_unittest.cc @@ -0,0 +1,43 @@ +// Copyright 2015 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 "base/files/memory_mapped_file.h" +#include "base/logging.h" +#include "media/base/test_data_util.h" +#include "media/filters/h265_parser.h" +#include "testing/gtest/include/gtest/gtest.h" + +namespace media { + +TEST(H265ParserTest, RawHevcStreamFileParsing) { + base::FilePath file_path = GetTestDataFilePath("bear.hevc"); + // Number of NALUs in the test stream to be parsed. + const int num_nalus = 35; + + base::MemoryMappedFile stream; + ASSERT_TRUE(stream.Initialize(file_path)) + << "Couldn't open stream file: " << file_path.MaybeAsASCII(); + + H265Parser parser; + parser.SetStream(stream.data(), stream.length()); + + // Parse until the end of stream/unsupported stream/error in stream is found. + int num_parsed_nalus = 0; + while (true) { + H265NALU nalu; + H265Parser::Result res = parser.AdvanceToNextNALU(&nalu); + if (res == H265Parser::kEOStream) { + DVLOG(1) << "Number of successfully parsed NALUs before EOS: " + << num_parsed_nalus; + ASSERT_EQ(num_nalus, num_parsed_nalus); + return; + } + ASSERT_EQ(res, H265Parser::kOk); + + ++num_parsed_nalus; + DVLOG(4) << "Found NALU " << nalu.nal_unit_type; + } +} + +} // namespace media diff --git a/media/filters/stream_parser_factory.cc b/media/filters/stream_parser_factory.cc index 83054f3..fc3257b 100644 --- a/media/filters/stream_parser_factory.cc +++ b/media/filters/stream_parser_factory.cc @@ -52,7 +52,8 @@ struct CodecInfo { HISTOGRAM_EAC3, HISTOGRAM_MP3, HISTOGRAM_OPUS, - HISTOGRAM_MAX = HISTOGRAM_OPUS // Must be equal to largest logged entry. + HISTOGRAM_HEVC, + HISTOGRAM_MAX = HISTOGRAM_HEVC // Must be equal to largest logged entry. }; const char* pattern; @@ -148,6 +149,12 @@ static const CodecInfo kH264AVC1CodecInfo = { "avc1.*", CodecInfo::VIDEO, NULL, CodecInfo::HISTOGRAM_H264 }; static const CodecInfo kH264AVC3CodecInfo = { "avc3.*", CodecInfo::VIDEO, NULL, CodecInfo::HISTOGRAM_H264 }; +#if defined(ENABLE_HEVC_DEMUXING) +static const CodecInfo kHEVCHEV1CodecInfo = { "hev1.*", CodecInfo::VIDEO, NULL, + CodecInfo::HISTOGRAM_HEVC }; +static const CodecInfo kHEVCHVC1CodecInfo = { "hvc1.*", CodecInfo::VIDEO, NULL, + CodecInfo::HISTOGRAM_HEVC }; +#endif static const CodecInfo kMPEG4AACCodecInfo = { "mp4a.40.*", CodecInfo::AUDIO, &ValidateMP4ACodecID, CodecInfo::HISTOGRAM_MPEG4AAC }; @@ -158,6 +165,10 @@ static const CodecInfo kMPEG2AACLCCodecInfo = { "mp4a.67", CodecInfo::AUDIO, static const CodecInfo* kVideoMP4Codecs[] = { &kH264AVC1CodecInfo, &kH264AVC3CodecInfo, +#if defined(ENABLE_HEVC_DEMUXING) + &kHEVCHEV1CodecInfo, + &kHEVCHVC1CodecInfo, +#endif &kMPEG4AACCodecInfo, &kMPEG2AACLCCodecInfo, NULL |