summaryrefslogtreecommitdiffstats
path: root/media/filters
diff options
context:
space:
mode:
Diffstat (limited to 'media/filters')
-rw-r--r--media/filters/ffmpeg_demuxer.cc31
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc16
-rw-r--r--media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.cc93
-rw-r--r--media/filters/ffmpeg_h265_to_annex_b_bitstream_converter.h49
-rw-r--r--media/filters/h264_parser.cc7
-rw-r--r--media/filters/h264_parser.h12
-rw-r--r--media/filters/h265_parser.cc159
-rw-r--r--media/filters/h265_parser.h151
-rw-r--r--media/filters/h265_parser_unittest.cc43
-rw-r--r--media/filters/stream_parser_factory.cc13
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