summaryrefslogtreecommitdiffstats
path: root/media/webm
diff options
context:
space:
mode:
authoracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-29 16:37:46 +0000
committeracolwell@chromium.org <acolwell@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98>2011-06-29 16:37:46 +0000
commit020fba32dc0e07d855d644bd575d88365b7e2deb (patch)
tree73cce4387cebbc4497b746f3641a60b66e0ff3be /media/webm
parent8914a69a5973071c57b47d4a0c6c43b7330ff9b1 (diff)
downloadchromium_src-020fba32dc0e07d855d644bd575d88365b7e2deb.zip
chromium_src-020fba32dc0e07d855d644bd575d88365b7e2deb.tar.gz
chromium_src-020fba32dc0e07d855d644bd575d88365b7e2deb.tar.bz2
Adding ChunkDemuxer implementation.
BUG=86536 TEST=ChunkDemuxerTest.* Review URL: http://codereview.chromium.org/7203002 git-svn-id: svn://svn.chromium.org/chrome/trunk/src@90966 0039d316-1c4b-4281-b951-d872f2087c98
Diffstat (limited to 'media/webm')
-rw-r--r--media/webm/cluster_builder.cc128
-rw-r--r--media/webm/cluster_builder.h55
-rw-r--r--media/webm/webm_cluster_parser.cc133
-rw-r--r--media/webm/webm_cluster_parser.h65
-rw-r--r--media/webm/webm_constants.h70
-rw-r--r--media/webm/webm_info_parser.cc75
-rw-r--r--media/webm/webm_info_parser.h46
-rw-r--r--media/webm/webm_parser.cc485
-rw-r--r--media/webm/webm_parser.h46
-rw-r--r--media/webm/webm_tracks_parser.cc123
-rw-r--r--media/webm/webm_tracks_parser.h61
11 files changed, 1287 insertions, 0 deletions
diff --git a/media/webm/cluster_builder.cc b/media/webm/cluster_builder.cc
new file mode 100644
index 0000000..eb5fbfb
--- /dev/null
+++ b/media/webm/cluster_builder.cc
@@ -0,0 +1,128 @@
+// Copyright (c) 2011 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/webm/cluster_builder.h"
+
+#include "base/logging.h"
+#include "media/base/data_buffer.h"
+
+namespace media {
+
+static const uint8 kClusterHeader[] = {
+ 0x1F, 0x43, 0xB6, 0x75, // CLUSTER ID
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // cluster(size = 0)
+ 0xE7, // Timecode ID
+ 0x88, // timecode(size=8)
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timecode value
+};
+
+const int kClusterHeaderSize = sizeof(kClusterHeader);
+const int kClusterSizeOffset = 4;
+const int kClusterTimecodeOffset = 14;
+
+static const uint8 kSimpleBlockHeader[] = {
+ 0xA3, // SimpleBlock ID
+ 0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0)
+};
+
+const int kSimpleBlockHeaderSize = sizeof(kSimpleBlockHeader);
+const int kSimpleBlockSizeOffset = 1;
+
+const int kInitialBufferSize = 32768;
+
+Cluster::Cluster(const uint8* data, int size) : data_(data), size_(size) {}
+Cluster::~Cluster() {}
+
+ClusterBuilder::ClusterBuilder() { Reset(); }
+
+void ClusterBuilder::SetClusterTimecode(int64 cluster_timecode) {
+ DCHECK_EQ(cluster_timecode_, -1);
+
+ cluster_timecode_ = cluster_timecode;
+
+ // Write the timecode into the header.
+ uint8* buf = buffer_.get() + kClusterTimecodeOffset;
+ for (int i = 7; i >= 0; --i) {
+ buf[i] = cluster_timecode & 0xff;
+ cluster_timecode >>= 8;
+ }
+}
+
+void ClusterBuilder::AddSimpleBlock(int track_num, int64 timecode, int flags,
+ const uint8* data, int size) {
+ DCHECK_GE(track_num, 0);
+ DCHECK_LE(track_num, 126);
+ DCHECK_GE(flags, 0);
+ DCHECK_LE(flags, 0xff);
+ DCHECK(data);
+ DCHECK_GT(size, 0);
+ DCHECK_NE(cluster_timecode_, -1);
+
+ int64 timecode_delta = timecode - cluster_timecode_;
+ DCHECK_GE(timecode_delta, -32768);
+ DCHECK_LE(timecode_delta, 32767);
+
+ int block_size = 4 + size;
+ int bytes_needed = kSimpleBlockHeaderSize + block_size;
+ if (bytes_needed > (buffer_size_ - bytes_used_))
+ ExtendBuffer(bytes_needed);
+
+ uint8* buf = buffer_.get() + bytes_used_;
+ int block_offset = bytes_used_;
+ memcpy(buf, kSimpleBlockHeader, kSimpleBlockHeaderSize);
+ UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
+ buf += kSimpleBlockHeaderSize;
+
+ buf[0] = 0x80 | (track_num & 0x7F);
+ buf[1] = (timecode_delta >> 8) & 0xff;
+ buf[2] = timecode_delta & 0xff;
+ buf[3] = flags & 0xff;
+ memcpy(buf + 4, data, size);
+
+ bytes_used_ += bytes_needed;
+}
+
+Cluster* ClusterBuilder::Finish() {
+ DCHECK_NE(cluster_timecode_, -1);
+
+ UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8));
+
+ scoped_ptr<Cluster> ret(new Cluster(buffer_.release(), bytes_used_));
+ Reset();
+ return ret.release();
+}
+
+void ClusterBuilder::Reset() {
+ buffer_size_ = kInitialBufferSize;
+ buffer_.reset(new uint8[buffer_size_]);
+ memcpy(buffer_.get(), kClusterHeader, kClusterHeaderSize);
+ bytes_used_ = kClusterHeaderSize;
+ cluster_timecode_ = -1;
+}
+
+void ClusterBuilder::ExtendBuffer(int bytes_needed) {
+ int new_buffer_size = 2 * buffer_size_;
+
+ while ((new_buffer_size - bytes_used_) < bytes_needed)
+ new_buffer_size *= 2;
+
+ scoped_array<uint8> new_buffer(new uint8[new_buffer_size]);
+
+ memcpy(new_buffer.get(), buffer_.get(), bytes_used_);
+ buffer_.reset(new_buffer.release());
+ buffer_size_ = new_buffer_size;
+}
+
+void ClusterBuilder::UpdateUInt64(int offset, int64 value) {
+ DCHECK_LE(offset + 7, buffer_size_);
+ uint8* buf = buffer_.get() + offset;
+
+ // Fill the last 7 bytes of size field in big-endian order.
+ for (int i = 7; i > 0; i--) {
+ buf[i] = value & 0xff;
+ value >>= 8;
+ }
+}
+
+} // namespace media
diff --git a/media/webm/cluster_builder.h b/media/webm/cluster_builder.h
new file mode 100644
index 0000000..3ff30ab
--- /dev/null
+++ b/media/webm/cluster_builder.h
@@ -0,0 +1,55 @@
+// Copyright (c) 2011 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_WEBM_CLUSTER_BUILDER_H_
+#define MEDIA_WEBM_CLUSTER_BUILDER_H_
+
+#include "base/basictypes.h"
+#include "base/scoped_ptr.h"
+#include "media/base/buffers.h"
+
+namespace media {
+
+class Cluster {
+ public:
+ // Takes ownership of |data|
+ Cluster(const uint8* data, int size);
+ ~Cluster();
+
+ const uint8* data() const { return data_.get(); }
+ int size() const { return size_; }
+
+ private:
+ scoped_array<const uint8> data_;
+ int size_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(Cluster);
+};
+
+class ClusterBuilder {
+ public:
+ ClusterBuilder();
+
+ void SetClusterTimecode(int64 cluster_timecode);
+ void AddSimpleBlock(int track_num, int64 timecode, int flags,
+ const uint8* data, int size);
+
+ Cluster* Finish();
+
+ private:
+ void Reset();
+ void ExtendBuffer(int bytes_needed);
+ void UpdateUInt64(int offset, int64 value);
+
+ scoped_array<uint8> buffer_;
+ int buffer_size_;
+ int bytes_used_;
+ int64 cluster_timecode_;
+
+ DISALLOW_COPY_AND_ASSIGN(ClusterBuilder);
+};
+
+} // namespace media
+
+#endif // MEDIA_WEBM_CLUSTER_BUILDER_H_
diff --git a/media/webm/webm_cluster_parser.cc b/media/webm/webm_cluster_parser.cc
new file mode 100644
index 0000000..8eb045c
--- /dev/null
+++ b/media/webm/webm_cluster_parser.cc
@@ -0,0 +1,133 @@
+// Copyright (c) 2011 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/webm/webm_cluster_parser.h"
+
+#include "base/logging.h"
+#include "media/base/data_buffer.h"
+#include "media/webm/webm_constants.h"
+
+namespace media {
+
+static Buffer* CreateBuffer(const uint8* data, size_t size) {
+ scoped_array<uint8> buf(new uint8[size]);
+ memcpy(buf.get(), data, size);
+ return new DataBuffer(buf.release(), size);
+}
+
+WebMClusterParser::WebMClusterParser(int64 timecode_scale,
+ int audio_track_num,
+ base::TimeDelta audio_default_duration,
+ int video_track_num,
+ base::TimeDelta video_default_duration)
+ : timecode_multiplier_(timecode_scale / 1000.0),
+ audio_track_num_(audio_track_num),
+ audio_default_duration_(audio_default_duration),
+ video_track_num_(video_track_num),
+ video_default_duration_(video_default_duration),
+ last_block_timecode_(-1),
+ cluster_timecode_(-1) {
+}
+
+WebMClusterParser::~WebMClusterParser() {}
+
+int WebMClusterParser::Parse(const uint8* buf, int size) {
+ last_block_timecode_ = -1;
+ cluster_timecode_ = -1;
+ audio_buffers_.clear();
+ video_buffers_.clear();
+
+ return WebMParseListElement(buf, size, kWebMIdCluster, 1, this);
+}
+
+bool WebMClusterParser::OnListStart(int id) {
+ if (id == kWebMIdCluster)
+ cluster_timecode_ = -1;
+
+ return true;
+}
+
+bool WebMClusterParser::OnListEnd(int id) {
+ if (id == kWebMIdCluster)
+ cluster_timecode_ = -1;
+
+ return true;
+}
+
+bool WebMClusterParser::OnUInt(int id, int64 val) {
+ if (id == kWebMIdTimecode) {
+ if (cluster_timecode_ != -1)
+ return false;
+
+ cluster_timecode_ = val;
+ }
+
+ return true;
+}
+
+bool WebMClusterParser::OnFloat(int id, double val) {
+ VLOG(1) << "Unexpected float element with ID " << std::hex << id;
+ return false;
+}
+
+bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
+ VLOG(1) << "Unexpected binary element with ID " << std::hex << id;
+ return false;
+}
+
+bool WebMClusterParser::OnString(int id, const std::string& str) {
+ VLOG(1) << "Unexpected string element with ID " << std::hex << id;
+ return false;
+}
+
+bool WebMClusterParser::OnSimpleBlock(int track_num, int timecode,
+ int flags,
+ const uint8* data, int size) {
+ if (cluster_timecode_ == -1) {
+ VLOG(1) << "Got SimpleBlock before cluster timecode.";
+ return false;
+ }
+
+ if (timecode < 0) {
+ VLOG(1) << "Got SimpleBlock with negative timecode offset " << timecode;
+ return false;
+ }
+
+ if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
+ VLOG(1) << "Got SimpleBlock with a timecode before the previous block.";
+ return false;
+ }
+
+ last_block_timecode_ = timecode;
+
+ base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
+ (cluster_timecode_ + timecode) * timecode_multiplier_);
+
+ scoped_refptr<Buffer> buffer(CreateBuffer(data, size));
+ buffer->SetTimestamp(timestamp);
+ BufferQueue* queue = NULL;
+
+ if (track_num == audio_track_num_) {
+ buffer->SetDuration(audio_default_duration_);
+ queue = &audio_buffers_;
+ } else if (track_num == video_track_num_) {
+ buffer->SetDuration(video_default_duration_);
+ queue = &video_buffers_;
+ } else {
+ VLOG(1) << "Unexpected track number " << track_num;
+ return false;
+ }
+
+ if (!queue->empty() &&
+ buffer->GetTimestamp() == queue->back()->GetTimestamp()) {
+ VLOG(1) << "Got SimpleBlock timecode is not strictly monotonically "
+ << "increasing for track " << track_num;
+ return false;
+ }
+
+ queue->push_back(buffer);
+ return true;
+}
+
+} // namespace media
diff --git a/media/webm/webm_cluster_parser.h b/media/webm/webm_cluster_parser.h
new file mode 100644
index 0000000..98c5854
--- /dev/null
+++ b/media/webm/webm_cluster_parser.h
@@ -0,0 +1,65 @@
+// Copyright (c) 2011 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_WEBM_WEBM_CLUSTER_PARSER_H_
+#define MEDIA_WEBM_WEBM_CLUSTER_PARSER_H_
+
+#include <deque>
+
+#include "base/scoped_ptr.h"
+#include "media/base/buffers.h"
+#include "media/webm/webm_parser.h"
+
+namespace media {
+
+class WebMClusterParser : public WebMParserClient {
+ public:
+ typedef std::deque<scoped_refptr<Buffer> > BufferQueue;
+
+ WebMClusterParser(int64 timecode_scale,
+ int audio_track_num,
+ base::TimeDelta audio_default_duration,
+ int video_track_num,
+ base::TimeDelta video_default_duration);
+ virtual ~WebMClusterParser();
+
+ // Parses a WebM cluster element in |buf|.
+ //
+ // Returns the number of bytes parsed on success. Returns -1
+ // if a parse error occurs.
+ int Parse(const uint8* buf, int size);
+
+ const BufferQueue& audio_buffers() const { return audio_buffers_; }
+ const BufferQueue& video_buffers() const { return video_buffers_; }
+
+ private:
+ // WebMParserClient methods.
+ virtual bool OnListStart(int id);
+ virtual bool OnListEnd(int id);
+ virtual bool OnUInt(int id, int64 val);
+ virtual bool OnFloat(int id, double val);
+ virtual bool OnBinary(int id, const uint8* data, int size);
+ virtual bool OnString(int id, const std::string& str);
+ virtual bool OnSimpleBlock(int track_num, int timecode, int flags,
+ const uint8* data, int size);
+
+ double timecode_multiplier_; // Multiplier used to convert timecodes into
+ // microseconds.
+ int audio_track_num_;
+ base::TimeDelta audio_default_duration_;
+ int video_track_num_;
+ base::TimeDelta video_default_duration_;
+
+ int64 last_block_timecode_;
+
+ int64 cluster_timecode_;
+ BufferQueue audio_buffers_;
+ BufferQueue video_buffers_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WebMClusterParser);
+};
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_CLUSTER_PARSER_H_
diff --git a/media/webm/webm_constants.h b/media/webm/webm_constants.h
new file mode 100644
index 0000000..aa6dda0
--- /dev/null
+++ b/media/webm/webm_constants.h
@@ -0,0 +1,70 @@
+// Copyright (c) 2011 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_WEBM_WEBM_CONSTANTS_H_
+#define MEDIA_WEBM_WEBM_CONSTANTS_H_
+
+namespace media {
+
+// WebM element IDs.
+// This is a subset of the IDs in the Matroska spec.
+// http://www.matroska.org/technical/specs/index.html
+const int kWebMIdAspectRatioType = 0x54B3;
+const int kWebMIdAudio = 0xE1;
+const int kWebMIdBitDepth = 0x6264;
+const int kWebMIdBlock = 0xA1;
+const int kWebMIdBlockGroup = 0xA0;
+const int kWebMIdChannels = 0x9F;
+const int kWebMIdCluster = 0x1f43b675;
+const int kWebMIdCodecID = 0x86;
+const int kWebMIdCodecName = 0x258688;
+const int kWebMIdCodecPrivate = 0x63A2;
+const int kWebMIdDateUTC = 0x4461;
+const int kWebMIdDefaultDuration = 0x23E383;
+const int kWebMIdDisplayHeight = 0x54BA;
+const int kWebMIdDisplayUnit = 0x54B2;
+const int kWebMIdDisplayWidth = 0x54B0;
+const int kWebMIdDuration = 0x4489;
+const int kWebMIdFlagDefault = 0x88;
+const int kWebMIdFlagEnabled = 0xB9;
+const int kWebMIdFlagForced = 0x55AA;
+const int kWebMIdFlagInterlaced = 0x9A;
+const int kWebMIdFlagLacing = 0x9C;
+const int kWebMIdInfo = 0x1549A966;
+const int kWebMIdLanguage = 0x22B59C;
+const int kWebMIdMuxingApp = 0x4D80;
+const int kWebMIdName = 0x536E;
+const int kWebMIdOutputSamplingFrequency = 0x78B5;
+const int kWebMIdPixelCropBottom = 0x54AA;
+const int kWebMIdPixelCropLeft = 0x54CC;
+const int kWebMIdPixelCropRight = 0x54DD;
+const int kWebMIdPixelCropTop = 0x54BB;
+const int kWebMIdPixelHeight = 0xBA;
+const int kWebMIdPixelWidth = 0xB0;
+const int kWebMIdSamplingFrequency = 0xB5;
+const int kWebMIdSegmentUID = 0x73A4;
+const int kWebMIdSimpleBlock = 0xA3;
+const int kWebMIdStereoMode = 0x53B8;
+const int kWebMIdTimecode = 0xE7;
+const int kWebMIdTimecodeScale = 0x2AD7B1;
+const int kWebMIdTitle = 0x7BA9;
+const int kWebMIdTrackEntry = 0xAE;
+const int kWebMIdTrackNumber = 0xD7;
+const int kWebMIdTrackType = 0x83;
+const int kWebMIdTrackUID = 0x73C5;
+const int kWebMIdTracks = 0x1654AE6B;
+const int kWebMIdVideo = 0xE0;
+const int kWebMIdWritingApp = 0x5741;
+
+// Default timecode scale if the TimecodeScale element is
+// not specified in the INFO element.
+const int kWebMDefaultTimecodeScale = 1000000;
+
+// Values for TrackType element.
+const int kWebMTrackTypeVideo = 1;
+const int kWebMTrackTypeAudio = 2;
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_CONSTANTS_H_
diff --git a/media/webm/webm_info_parser.cc b/media/webm/webm_info_parser.cc
new file mode 100644
index 0000000..41e1a25
--- /dev/null
+++ b/media/webm/webm_info_parser.cc
@@ -0,0 +1,75 @@
+// Copyright (c) 2011 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/webm/webm_info_parser.h"
+
+#include "base/logging.h"
+#include "media/webm/webm_constants.h"
+
+namespace media {
+
+WebMInfoParser::WebMInfoParser()
+ : timecode_scale_(-1),
+ duration_(-1) {
+}
+
+WebMInfoParser::~WebMInfoParser() {}
+
+int WebMInfoParser::Parse(const uint8* buf, int size) {
+ return WebMParseListElement(buf, size, kWebMIdInfo, 1, this);
+}
+
+bool WebMInfoParser::OnListStart(int id) { return true; }
+
+bool WebMInfoParser::OnListEnd(int id) {
+ if (id == kWebMIdInfo && timecode_scale_ == -1) {
+ // Set timecode scale to default value if it isn't present in
+ // the Info element.
+ timecode_scale_ = kWebMDefaultTimecodeScale;
+ }
+ return true;
+}
+
+bool WebMInfoParser::OnUInt(int id, int64 val) {
+ if (id != kWebMIdTimecodeScale)
+ return true;
+
+ if (timecode_scale_ != -1) {
+ VLOG(1) << "Multiple values for id " << std::hex << id << " specified";
+ return false;
+ }
+
+ timecode_scale_ = val;
+ return true;
+}
+
+bool WebMInfoParser::OnFloat(int id, double val) {
+ if (id != kWebMIdDuration) {
+ VLOG(1) << "Unexpected float for id" << std::hex << id;
+ return false;
+ }
+
+ if (duration_ != -1) {
+ VLOG(1) << "Multiple values for duration.";
+ return false;
+ }
+
+ duration_ = val;
+ return true;
+}
+
+bool WebMInfoParser::OnBinary(int id, const uint8* data, int size) {
+ return true;
+}
+
+bool WebMInfoParser::OnString(int id, const std::string& str) {
+ return true;
+}
+
+bool WebMInfoParser::OnSimpleBlock(int track_num, int timecode, int flags,
+ const uint8* data, int size) {
+ return false;
+}
+
+} // namespace media
diff --git a/media/webm/webm_info_parser.h b/media/webm/webm_info_parser.h
new file mode 100644
index 0000000..6391f8b
--- /dev/null
+++ b/media/webm/webm_info_parser.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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_WEBM_WEBM_INFO_PARSER_H_
+#define MEDIA_WEBM_WEBM_INFO_PARSER_H_
+
+#include "media/webm/webm_parser.h"
+
+namespace media {
+
+// Parser for WebM Info element.
+class WebMInfoParser : public WebMParserClient {
+ public:
+ WebMInfoParser();
+ virtual ~WebMInfoParser();
+
+ // Parses a WebM Info element in |buf|.
+ //
+ // Returns the number of bytes parsed on success. Returns -1
+ // on error.
+ int Parse(const uint8* buf, int size);
+
+ int64 timecode_scale() const { return timecode_scale_; }
+ double duration() const { return duration_; }
+
+ private:
+ // WebMParserClient methods
+ virtual bool OnListStart(int id);
+ virtual bool OnListEnd(int id);
+ virtual bool OnUInt(int id, int64 val);
+ virtual bool OnFloat(int id, double val);
+ virtual bool OnBinary(int id, const uint8* data, int size);
+ virtual bool OnString(int id, const std::string& str);
+ virtual bool OnSimpleBlock(int track_num, int timecode, int flags,
+ const uint8* data, int size);
+
+ int64 timecode_scale_;
+ double duration_;
+
+ DISALLOW_COPY_AND_ASSIGN(WebMInfoParser);
+};
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_INFO_PARSER_H_
diff --git a/media/webm/webm_parser.cc b/media/webm/webm_parser.cc
new file mode 100644
index 0000000..42d826c1
--- /dev/null
+++ b/media/webm/webm_parser.cc
@@ -0,0 +1,485 @@
+// Copyright (c) 2011 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/webm/webm_parser.h"
+
+// This file contains code to parse WebM file elements. It was created
+// from information in the Matroska spec.
+// http://www.matroska.org/technical/specs/index.html
+
+#include <iomanip>
+
+#include "base/logging.h"
+#include "media/webm/webm_constants.h"
+
+namespace media {
+
+// Maximum depth of WebM elements. Some WebM elements are lists of
+// other elements. This limits the number levels of recursion allowed.
+static const int kMaxLevelDepth = 6;
+
+enum ElementType {
+ LIST,
+ UINT,
+ FLOAT,
+ BINARY,
+ STRING,
+ SBLOCK,
+ SKIP,
+};
+
+struct ElementIdInfo {
+ int level_;
+ ElementType type_;
+ int id_;
+};
+
+struct ListElementInfo {
+ int id_;
+ const ElementIdInfo* id_info_;
+ int id_info_size_;
+};
+
+// The following are tables indicating what IDs are valid sub-elements
+// of particular elements. If an element is encountered that doesn't
+// appear in the list, a parsing error is signalled. Some elements are
+// marked as SKIP because they are valid, but we don't care about them
+// right now.
+static const ElementIdInfo kClusterIds[] = {
+ {2, UINT, kWebMIdTimecode},
+ {2, SBLOCK, kWebMIdSimpleBlock},
+ {2, LIST, kWebMIdBlockGroup},
+};
+
+static const ElementIdInfo kInfoIds[] = {
+ {2, SKIP, kWebMIdSegmentUID},
+ {2, UINT, kWebMIdTimecodeScale},
+ {2, FLOAT, kWebMIdDuration},
+ {2, SKIP, kWebMIdDateUTC},
+ {2, SKIP, kWebMIdTitle},
+ {2, SKIP, kWebMIdMuxingApp},
+ {2, SKIP, kWebMIdWritingApp},
+};
+
+static const ElementIdInfo kTracksIds[] = {
+ {2, LIST, kWebMIdTrackEntry},
+};
+
+static const ElementIdInfo kTrackEntryIds[] = {
+ {3, UINT, kWebMIdTrackNumber},
+ {3, SKIP, kWebMIdTrackUID},
+ {3, UINT, kWebMIdTrackType},
+ {3, SKIP, kWebMIdFlagEnabled},
+ {3, SKIP, kWebMIdFlagDefault},
+ {3, SKIP, kWebMIdFlagForced},
+ {3, UINT, kWebMIdFlagLacing},
+ {3, UINT, kWebMIdDefaultDuration},
+ {3, SKIP, kWebMIdName},
+ {3, SKIP, kWebMIdLanguage},
+ {3, STRING, kWebMIdCodecID},
+ {3, BINARY, kWebMIdCodecPrivate},
+ {3, SKIP, kWebMIdCodecName},
+ {3, LIST, kWebMIdVideo},
+ {3, LIST, kWebMIdAudio},
+};
+
+static const ElementIdInfo kVideoIds[] = {
+ {4, SKIP, kWebMIdFlagInterlaced},
+ {4, SKIP, kWebMIdStereoMode},
+ {4, UINT, kWebMIdPixelWidth},
+ {4, UINT, kWebMIdPixelHeight},
+ {4, SKIP, kWebMIdPixelCropBottom},
+ {4, SKIP, kWebMIdPixelCropTop},
+ {4, SKIP, kWebMIdPixelCropLeft},
+ {4, SKIP, kWebMIdPixelCropRight},
+ {4, SKIP, kWebMIdDisplayWidth},
+ {4, SKIP, kWebMIdDisplayHeight},
+ {4, SKIP, kWebMIdDisplayUnit},
+ {4, SKIP, kWebMIdAspectRatioType},
+};
+
+static const ElementIdInfo kAudioIds[] = {
+ {4, SKIP, kWebMIdSamplingFrequency},
+ {4, SKIP, kWebMIdOutputSamplingFrequency},
+ {4, UINT, kWebMIdChannels},
+ {4, SKIP, kWebMIdBitDepth},
+};
+
+static const ElementIdInfo kClustersOnly[] = {
+ {1, LIST, kWebMIdCluster},
+};
+
+static const ListElementInfo kListElementInfo[] = {
+ { kWebMIdCluster, kClusterIds, sizeof(kClusterIds) },
+ { kWebMIdInfo, kInfoIds, sizeof(kInfoIds) },
+ { kWebMIdTracks, kTracksIds, sizeof(kTracksIds) },
+ { kWebMIdTrackEntry, kTrackEntryIds, sizeof(kTrackEntryIds) },
+ { kWebMIdVideo, kVideoIds, sizeof(kVideoIds) },
+ { kWebMIdAudio, kAudioIds, sizeof(kAudioIds) },
+};
+
+// Number of elements in kListElementInfo.
+const int kListElementInfoCount =
+ sizeof(kListElementInfo) / sizeof(ListElementInfo);
+
+WebMParserClient::~WebMParserClient() {}
+
+// Parses an element header id or size field. These fields are variable length
+// encoded. The first byte indicates how many bytes the field occupies.
+// |buf| - The buffer to parse.
+// |size| - The number of bytes in |buf|
+// |max_bytes| - The maximum number of bytes the field can be. ID fields
+// set this to 4 & element size fields set this to 8. If the
+// first byte indicates a larger field size than this it is a
+// parser error.
+// |mask_first_byte| - For element size fields the field length encoding bits
+// need to be masked off. This parameter is true for
+// element size fields and is false for ID field values.
+//
+// Returns: The number of bytes parsed on success. -1 on error.
+static int ParseWebMElementHeaderField(const uint8* buf, int size,
+ int max_bytes, bool mask_first_byte,
+ int64* num) {
+ DCHECK(buf);
+ DCHECK(num);
+
+ if (size <= 0)
+ return -1;
+
+ int mask = 0x80;
+ uint8 ch = buf[0];
+ int extra_bytes = -1;
+ for (int i = 0; i < max_bytes; ++i) {
+ if ((ch & mask) == mask) {
+ *num = mask_first_byte ? ch & ~mask : ch;
+ extra_bytes = i;
+ break;
+ }
+ mask >>= 1;
+ }
+
+ if ((extra_bytes == -1) || ((1 + extra_bytes) > size))
+ return -1;
+
+ int bytes_used = 1;
+
+ for (int i = 0; i < extra_bytes; ++i)
+ *num = (*num << 8) | (0xff & buf[bytes_used++]);
+
+ return bytes_used;
+}
+
+// Parses an element header & returns the ID and element size.
+//
+// Returns: The number of bytes parsed on success. -1 on error.
+// |*id| contains the element ID on success & undefined on error.
+// |*element_size| contains the element size on success & undefined on error.
+static int ParseWebMElementHeader(const uint8* buf, int size,
+ int* id, int64* element_size) {
+ DCHECK(buf);
+ DCHECK_GE(size, 0);
+ DCHECK(id);
+ DCHECK(element_size);
+
+ if (size == 0)
+ return 0;
+
+ int64 tmp;
+ int num_id_bytes = ParseWebMElementHeaderField(buf, size, 4, false, &tmp);
+
+ if (num_id_bytes <= 0)
+ return num_id_bytes;
+
+ *id = static_cast<int>(tmp);
+
+ int num_size_bytes = ParseWebMElementHeaderField(buf + num_id_bytes,
+ size - num_id_bytes,
+ 8, true, &tmp);
+
+ if (num_size_bytes <= 0)
+ return num_size_bytes;
+
+ *element_size = tmp;
+ return num_id_bytes + num_size_bytes;
+}
+
+// Finds ElementIdInfo for a specific ID.
+static const ElementIdInfo* FindIdInfo(int id,
+ const ElementIdInfo* id_info,
+ int id_info_size) {
+ int count = id_info_size / sizeof(*id_info);
+ for (int i = 0; i < count; ++i) {
+ if (id == id_info[i].id_)
+ return &id_info[i];
+ }
+
+ return NULL;
+}
+
+// Finds ListElementInfo for a specific ID.
+static const ListElementInfo* FindListInfo(int id) {
+ for (int i = 0; i < kListElementInfoCount; ++i) {
+ if (id == kListElementInfo[i].id_)
+ return &kListElementInfo[i];
+ }
+
+ return NULL;
+}
+
+static int ParseSimpleBlock(const uint8* buf, int size,
+ WebMParserClient* client) {
+ if (size < 4)
+ return -1;
+
+ // Return an error if the trackNum > 127. We just aren't
+ // going to support large track numbers right now.
+ if ((buf[0] & 0x80) != 0x80) {
+ VLOG(1) << "TrackNumber over 127 not supported";
+ return -1;
+ }
+
+ int track_num = buf[0] & 0x7f;
+ int timecode = buf[1] << 8 | buf[2];
+ int flags = buf[3] & 0xff;
+ int lacing = (flags >> 1) & 0x3;
+
+ if (lacing != 0) {
+ VLOG(1) << "Lacing " << lacing << " not supported yet.";
+ return -1;
+ }
+
+ // Sign extend negative timecode offsets.
+ if (timecode & 0x8000)
+ timecode |= (-1 << 16);
+
+ const uint8* frame_data = buf + 4;
+ int frame_size = size - (frame_data - buf);
+ if (!client->OnSimpleBlock(track_num, timecode, flags,
+ frame_data, frame_size)) {
+ return -1;
+ }
+
+ return size;
+}
+
+static int ParseElements(const ElementIdInfo* id_info,
+ int id_info_size,
+ const uint8* buf, int size, int level,
+ WebMParserClient* client);
+
+static int ParseElementList(const uint8* buf, int size,
+ int id, int level,
+ WebMParserClient* client) {
+ const ListElementInfo* list_info = FindListInfo(id);
+
+ if (!list_info) {
+ VLOG(1) << "Failed to find list info for ID " << std::hex << id;
+ return -1;
+ }
+
+ if (!client->OnListStart(id))
+ return -1;
+
+ int res = ParseElements(list_info->id_info_,
+ list_info->id_info_size_,
+ buf, size,
+ level + 1,
+ client);
+
+ if (res < 0)
+ return -1;
+
+ if (!client->OnListEnd(id))
+ return -1;
+
+ DCHECK_EQ(res, size);
+ return res;
+}
+
+static int ParseUInt(const uint8* buf, int size, int id,
+ WebMParserClient* client) {
+ if ((size <= 0) || (size > 8))
+ return -1;
+
+ // Read in the big-endian integer.
+ int64 value = 0;
+ for (int i = 0; i < size; ++i)
+ value = (value << 8) | buf[i];
+
+ if (!client->OnUInt(id, value))
+ return -1;
+
+ return size;
+}
+
+static int ParseFloat(const uint8* buf, int size, int id,
+ WebMParserClient* client) {
+
+ if ((size != 4) && (size != 8))
+ return -1;
+
+ double value = -1;
+
+ // Read the bytes from big-endian form into a native endian integer.
+ int64 tmp = 0;
+ for (int i = 0; i < size; ++i)
+ tmp = (tmp << 8) | buf[i];
+
+ // Use a union to convert the integer bit pattern into a floating point
+ // number.
+ if (size == 4) {
+ union {
+ int32 src;
+ float dst;
+ } tmp2;
+ tmp2.src = static_cast<int32>(tmp);
+ value = tmp2.dst;
+ } else if (size == 8) {
+ union {
+ int64 src;
+ double dst;
+ } tmp2;
+ tmp2.src = tmp;
+ value = tmp2.dst;
+ } else {
+ return -1;
+ }
+
+ if (!client->OnFloat(id, value))
+ return -1;
+
+ return size;
+}
+
+static int ParseElements(const ElementIdInfo* id_info,
+ int id_info_size,
+ const uint8* buf, int size, int level,
+ WebMParserClient* client) {
+ DCHECK_GE(id_info_size, 0);
+ DCHECK_GE(size, 0);
+ DCHECK_GE(level, 0);
+
+ const uint8* cur = buf;
+ int cur_size = size;
+ int used = 0;
+
+ if (level > kMaxLevelDepth)
+ return -1;
+
+ while (cur_size > 0) {
+ int id;
+ int64 element_size;
+ int res = ParseWebMElementHeader(cur, cur_size, &id, &element_size);
+
+ if (res < 0)
+ return res;
+
+ if (res == 0)
+ break;
+
+ cur += res;
+ cur_size -= res;
+ used += res;
+
+ // Check to see if the element is larger than the remaining data.
+ if (element_size > cur_size)
+ return -1;
+
+ const ElementIdInfo* info = FindIdInfo(id, id_info, id_info_size);
+
+ if (info == NULL) {
+ VLOG(1) << "No info for ID " << std::hex << id;
+
+ // TODO(acolwell): Change this to return -1 after the API has solidified.
+ // We don't want to allow elements we don't recognize.
+ cur += element_size;
+ cur_size -= element_size;
+ used += element_size;
+ continue;
+ }
+
+ if (info->level_ != level) {
+ VLOG(1) << "ID " << std::hex << id << std::dec << " at level "
+ << level << " instead of " << info->level_;
+ return -1;
+ }
+
+ switch(info->type_) {
+ case SBLOCK:
+ if (ParseSimpleBlock(cur, element_size, client) <= 0)
+ return -1;
+ break;
+ case LIST:
+ if (ParseElementList(cur, element_size, id, level, client) < 0)
+ return -1;
+ break;
+ case UINT:
+ if (ParseUInt(cur, element_size, id, client) <= 0)
+ return -1;
+ break;
+ case FLOAT:
+ if (ParseFloat(cur, element_size, id, client) <= 0)
+ return -1;
+ break;
+ case BINARY:
+ if (!client->OnBinary(id, cur, element_size))
+ return -1;
+ break;
+ case STRING:
+ if (!client->OnString(id,
+ std::string(reinterpret_cast<const char*>(cur),
+ element_size)))
+ return -1;
+ break;
+ case SKIP:
+ // Do nothing.
+ break;
+ default:
+ VLOG(1) << "Unhandled id type " << info->type_;
+ return -1;
+ };
+
+ cur += element_size;
+ cur_size -= element_size;
+ used += element_size;
+ }
+
+ return used;
+}
+
+// Parses a single list element that matches |id|. This method fails if the
+// buffer points to an element that does not match |id|.
+int WebMParseListElement(const uint8* buf, int size, int id,
+ int level, WebMParserClient* client) {
+ if (size == 0)
+ return -1;
+
+ const uint8* cur = buf;
+ int cur_size = size;
+
+ int element_id = 0;
+ int64 element_size = 0;
+ int res = ParseWebMElementHeader(cur, cur_size, &element_id, &element_size);
+
+ if (res <= 0)
+ return res;
+
+ cur += res;
+ cur_size -= res;
+
+ if (element_id != id || element_size > cur_size)
+ return -1;
+
+ res = ParseElementList(cur, element_size, element_id, level, client);
+
+ if (res < 0)
+ return -1;
+
+ cur += res;
+ cur_size -= res;
+
+ return size - cur_size;
+}
+
+} // namespace media
diff --git a/media/webm/webm_parser.h b/media/webm/webm_parser.h
new file mode 100644
index 0000000..0716b80
--- /dev/null
+++ b/media/webm/webm_parser.h
@@ -0,0 +1,46 @@
+// Copyright (c) 2011 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_WEBM_WEBM_PARSER_H_
+#define MEDIA_WEBM_WEBM_PARSER_H_
+
+#include <string>
+
+#include "base/basictypes.h"
+
+namespace media {
+
+// Interface for receiving WebM parser events.
+//
+// Each method is called when an element of the specified type is parsed.
+// The ID of the element that was parsed is given along with the value
+// stored in the element. List elements generate calls at the start and
+// end of the list. Any pointers passed to these methods are only guaranteed
+// to be valid for the life of that call. Each method returns a bool that
+// indicates whether the parsed data is valid. If false is returned
+// then the parse is immediately terminated and an error is reported by the
+// parser.
+class WebMParserClient {
+ public:
+ virtual ~WebMParserClient();
+
+ virtual bool OnListStart(int id) = 0;
+ virtual bool OnListEnd(int id) = 0;
+ virtual bool OnUInt(int id, int64 val) = 0;
+ virtual bool OnFloat(int id, double val) = 0;
+ virtual bool OnBinary(int id, const uint8* data, int size) = 0;
+ virtual bool OnString(int id, const std::string& str) = 0;
+ virtual bool OnSimpleBlock(int track_num, int timecode,
+ int flags,
+ const uint8* data, int size) = 0;
+};
+
+// Parses a single list element that matches |id|. This method fails if the
+// buffer points to an element that does not match |id|.
+int WebMParseListElement(const uint8* buf, int size, int id,
+ int level, WebMParserClient* client);
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_PARSER_H_
diff --git a/media/webm/webm_tracks_parser.cc b/media/webm/webm_tracks_parser.cc
new file mode 100644
index 0000000..29b4f33
--- /dev/null
+++ b/media/webm/webm_tracks_parser.cc
@@ -0,0 +1,123 @@
+// Copyright (c) 2011 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/webm/webm_tracks_parser.h"
+
+#include "base/logging.h"
+#include "media/webm/webm_constants.h"
+
+namespace media {
+
+WebMTracksParser::WebMTracksParser(int64 timecode_scale)
+ : timecode_scale_(timecode_scale),
+ track_type_(-1),
+ track_num_(-1),
+ track_default_duration_(-1),
+ audio_track_num_(-1),
+ audio_default_duration_(base::TimeDelta::FromMicroseconds(-1)),
+ video_track_num_(-1),
+ video_default_duration_(base::TimeDelta::FromMicroseconds(-1)) {
+}
+
+WebMTracksParser::~WebMTracksParser() {}
+
+int WebMTracksParser::Parse(const uint8* buf, int size) {
+ return WebMParseListElement(buf, size, kWebMIdTracks, 1, this);
+}
+
+
+bool WebMTracksParser::OnListStart(int id) {
+ if (id == kWebMIdTrackEntry) {
+ track_type_ = -1;
+ track_num_ = -1;
+ track_default_duration_ = -1;
+ }
+
+ return true;
+}
+
+bool WebMTracksParser::OnListEnd(int id) {
+ if (id == kWebMIdTrackEntry) {
+ if (track_type_ == -1 || track_num_ == -1) {
+ VLOG(1) << "Missing TrackEntry data"
+ << " TrackType " << track_type_
+ << " TrackNum " << track_num_;
+ return false;
+ }
+
+ // Convert nanoseconds to base::TimeDelta.
+ base::TimeDelta default_duration = base::TimeDelta::FromMicroseconds(
+ track_default_duration_ / 1000.0);
+
+ if (track_type_ == kWebMTrackTypeVideo) {
+ video_track_num_ = track_num_;
+ video_default_duration_ = default_duration;
+ } else if (track_type_ == kWebMTrackTypeAudio) {
+ audio_track_num_ = track_num_;
+ audio_default_duration_ = default_duration;
+ } else {
+ VLOG(1) << "Unexpected TrackType " << track_type_;
+ return false;
+ }
+
+ track_type_ = -1;
+ track_num_ = -1;
+ }
+
+ return true;
+}
+
+bool WebMTracksParser::OnUInt(int id, int64 val) {
+ int64* dst = NULL;
+
+ switch (id) {
+ case kWebMIdTrackNumber:
+ dst = &track_num_;
+ break;
+ case kWebMIdTrackType:
+ dst = &track_type_;
+ break;
+ case kWebMIdDefaultDuration:
+ dst = &track_default_duration_;
+ break;
+ default:
+ return true;
+ }
+
+ if (*dst != -1) {
+ VLOG(1) << "Multiple values for id " << std::hex << id << " specified";
+ return false;
+ }
+
+ *dst = val;
+ return true;
+}
+
+bool WebMTracksParser::OnFloat(int id, double val) {
+ VLOG(1) << "Unexpected float for id" << std::hex << id;
+ return false;
+}
+
+bool WebMTracksParser::OnBinary(int id, const uint8* data, int size) {
+ return true;
+}
+
+bool WebMTracksParser::OnString(int id, const std::string& str) {
+ if (id != kWebMIdCodecID)
+ return false;
+
+ if (str != "A_VORBIS" && str != "V_VP8") {
+ VLOG(1) << "Unexpected CodecID " << str;
+ return false;
+ }
+
+ return true;
+}
+
+bool WebMTracksParser::OnSimpleBlock(int track_num, int timecode, int flags,
+ const uint8* data, int size) {
+ return false;
+}
+
+} // namespace media
diff --git a/media/webm/webm_tracks_parser.h b/media/webm/webm_tracks_parser.h
new file mode 100644
index 0000000..a1d0081
--- /dev/null
+++ b/media/webm/webm_tracks_parser.h
@@ -0,0 +1,61 @@
+// Copyright (c) 2011 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_WEBM_WEBM_TRACKS_PARSER_H_
+#define MEDIA_WEBM_WEBM_TRACKS_PARSER_H_
+
+#include "media/webm/webm_parser.h"
+
+#include "base/time.h"
+
+namespace media {
+
+// Parser for WebM Tracks element.
+class WebMTracksParser : public WebMParserClient {
+ public:
+ WebMTracksParser(int64 timecode_scale);
+ virtual ~WebMTracksParser();
+
+ // Parses a WebM Tracks element in |buf|.
+ //
+ // Returns the number of bytes parsed on success. Returns -1
+ // on error.
+ int Parse(const uint8* buf, int size);
+
+ int64 audio_track_num() const { return audio_track_num_; }
+ base::TimeDelta audio_default_duration() const {
+ return audio_default_duration_;
+ }
+
+ int64 video_track_num() const { return video_track_num_; }
+ base::TimeDelta video_default_duration() const {
+ return video_default_duration_;
+ }
+
+ private:
+ // WebMParserClient methods
+ virtual bool OnListStart(int id);
+ virtual bool OnListEnd(int id);
+ virtual bool OnUInt(int id, int64 val);
+ virtual bool OnFloat(int id, double val);
+ virtual bool OnBinary(int id, const uint8* data, int size);
+ virtual bool OnString(int id, const std::string& str);
+ virtual bool OnSimpleBlock(int track_num, int timecode, int flags,
+ const uint8* data, int size);
+ int64 timecode_scale_;
+
+ int64 track_type_;
+ int64 track_num_;
+ int64 track_default_duration_;
+ int64 audio_track_num_;
+ base::TimeDelta audio_default_duration_;
+ int64 video_track_num_;
+ base::TimeDelta video_default_duration_;
+
+ DISALLOW_IMPLICIT_CONSTRUCTORS(WebMTracksParser);
+};
+
+} // namespace media
+
+#endif // MEDIA_WEBM_WEBM_TRACKS_PARSER_H_