Remove webm which is not really supported yet

Change-Id: Idc94f8c193c07e93c3c910a95f8576a10a65a177
This commit is contained in:
Kongqun Yang 2014-03-14 14:00:08 -07:00
parent d90ca489f9
commit d2b38ce118
33 changed files with 0 additions and 5800 deletions

View File

@ -1,175 +0,0 @@
// Copyright (c) 2012 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)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // timecode value
};
static const uint8 kSimpleBlockHeader[] = {
0xA3, // SimpleBlock ID
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // SimpleBlock(size = 0)
};
static const uint8 kBlockGroupHeader[] = {
0xA0, // BlockGroup ID
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // BlockGroup(size = 0)
0x9B, // BlockDuration ID
0x88, // BlockDuration(size = 8)
0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // duration
0xA1, // Block ID
0x01, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, // Block(size = 0)
};
enum {
kClusterSizeOffset = 4,
kClusterTimecodeOffset = 14,
kSimpleBlockSizeOffset = 1,
kBlockGroupSizeOffset = 1,
kBlockGroupDurationOffset = 11,
kBlockGroupBlockSizeOffset = 20,
kInitialBufferSize = 32768,
};
Cluster::Cluster(scoped_ptr<uint8[]> data, int size)
: data_(data.Pass()), size_(size) {}
Cluster::~Cluster() {}
ClusterBuilder::ClusterBuilder() { Reset(); }
ClusterBuilder::~ClusterBuilder() {}
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) {
int block_size = size + 4;
int bytes_needed = sizeof(kSimpleBlockHeader) + 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, sizeof(kSimpleBlockHeader));
UpdateUInt64(block_offset + kSimpleBlockSizeOffset, block_size);
buf += sizeof(kSimpleBlockHeader);
WriteBlock(buf, track_num, timecode, flags, data, size);
bytes_used_ += bytes_needed;
}
void ClusterBuilder::AddBlockGroup(int track_num, int64 timecode, int duration,
int flags, const uint8* data, int size) {
int block_size = size + 4;
int bytes_needed = sizeof(kBlockGroupHeader) + block_size;
int block_group_size = bytes_needed - 9;
if (bytes_needed > (buffer_size_ - bytes_used_))
ExtendBuffer(bytes_needed);
uint8* buf = buffer_.get() + bytes_used_;
int block_group_offset = bytes_used_;
memcpy(buf, kBlockGroupHeader, sizeof(kBlockGroupHeader));
UpdateUInt64(block_group_offset + kBlockGroupSizeOffset, block_group_size);
UpdateUInt64(block_group_offset + kBlockGroupDurationOffset, duration);
UpdateUInt64(block_group_offset + kBlockGroupBlockSizeOffset, block_size);
buf += sizeof(kBlockGroupHeader);
// Make sure the 4 most-significant bits are 0.
// http://www.matroska.org/technical/specs/index.html#block_structure
flags &= 0x0f;
WriteBlock(buf, track_num, timecode, flags, data, size);
bytes_used_ += bytes_needed;
}
void ClusterBuilder::WriteBlock(uint8* buf, 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);
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);
}
scoped_ptr<Cluster> ClusterBuilder::Finish() {
DCHECK_NE(cluster_timecode_, -1);
UpdateUInt64(kClusterSizeOffset, bytes_used_ - (kClusterSizeOffset + 8));
scoped_ptr<Cluster> ret(new Cluster(buffer_.Pass(), bytes_used_));
Reset();
return ret.Pass();
}
void ClusterBuilder::Reset() {
buffer_size_ = kInitialBufferSize;
buffer_.reset(new uint8[buffer_size_]);
memcpy(buffer_.get(), kClusterHeader, sizeof(kClusterHeader));
bytes_used_ = sizeof(kClusterHeader);
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_ptr<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

View File

@ -1,59 +0,0 @@
// Copyright (c) 2012 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/memory/scoped_ptr.h"
#include "media/base/buffers.h"
namespace media {
class Cluster {
public:
Cluster(scoped_ptr<uint8[]> data, int size);
~Cluster();
const uint8* data() const { return data_.get(); }
int size() const { return size_; }
private:
scoped_ptr<uint8[]> data_;
int size_;
DISALLOW_IMPLICIT_CONSTRUCTORS(Cluster);
};
class ClusterBuilder {
public:
ClusterBuilder();
~ClusterBuilder();
void SetClusterTimecode(int64 cluster_timecode);
void AddSimpleBlock(int track_num, int64 timecode, int flags,
const uint8* data, int size);
void AddBlockGroup(int track_num, int64 timecode, int duration, int flags,
const uint8* data, int size);
scoped_ptr<Cluster> Finish();
private:
void Reset();
void ExtendBuffer(int bytes_needed);
void UpdateUInt64(int offset, int64 value);
void WriteBlock(uint8* buf, int track_num, int64 timecode, int flags,
const uint8* data, int size);
scoped_ptr<uint8[]> buffer_;
int buffer_size_;
int bytes_used_;
int64 cluster_timecode_;
DISALLOW_COPY_AND_ASSIGN(ClusterBuilder);
};
} // namespace media
#endif // MEDIA_WEBM_CLUSTER_BUILDER_H_

View File

@ -1,211 +0,0 @@
// Copyright (c) 2013 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/tracks_builder.h"
#include "media/webm/webm_constants.h"
namespace media {
// Returns size of an integer, formatted using Matroska serialization.
static int GetUIntMkvSize(uint64 value) {
if (value < 0x07FULL)
return 1;
if (value < 0x03FFFULL)
return 2;
if (value < 0x01FFFFFULL)
return 3;
if (value < 0x0FFFFFFFULL)
return 4;
if (value < 0x07FFFFFFFFULL)
return 5;
if (value < 0x03FFFFFFFFFFULL)
return 6;
if (value < 0x01FFFFFFFFFFFFULL)
return 7;
return 8;
}
// Returns the minimium size required to serialize an integer value.
static int GetUIntSize(uint64 value) {
if (value < 0x0100ULL)
return 1;
if (value < 0x010000ULL)
return 2;
if (value < 0x01000000ULL)
return 3;
if (value < 0x0100000000ULL)
return 4;
if (value < 0x010000000000ULL)
return 5;
if (value < 0x01000000000000ULL)
return 6;
if (value < 0x0100000000000000ULL)
return 7;
return 8;
}
static int MasterElementSize(int element_id, int payload_size) {
return GetUIntSize(element_id) + GetUIntMkvSize(payload_size) + payload_size;
}
static int IntElementSize(int element_id, int value) {
return GetUIntSize(element_id) + 1 + GetUIntSize(value);
}
static int StringElementSize(int element_id, const std::string& value) {
return GetUIntSize(element_id) +
GetUIntMkvSize(value.length()) +
value.length();
}
static void SerializeInt(uint8** buf_ptr, int* buf_size_ptr,
int64 value, int size) {
uint8*& buf = *buf_ptr;
int& buf_size = *buf_size_ptr;
for (int idx = 1; idx <= size; ++idx) {
*buf++ = static_cast<uint8>(value >> ((size - idx) * 8));
--buf_size;
}
}
static void WriteElementId(uint8** buf, int* buf_size, int element_id) {
SerializeInt(buf, buf_size, element_id, GetUIntSize(element_id));
}
static void WriteUInt(uint8** buf, int* buf_size, uint64 value) {
const int size = GetUIntMkvSize(value);
value |= (1ULL << (size * 7)); // Matroska formatting
SerializeInt(buf, buf_size, value, size);
}
static void WriteMasterElement(uint8** buf, int* buf_size,
int element_id, int payload_size) {
WriteElementId(buf, buf_size, element_id);
WriteUInt(buf, buf_size, payload_size);
}
static void WriteIntElement(uint8** buf, int* buf_size,
int element_id, int value) {
WriteElementId(buf, buf_size, element_id);
const int size = GetUIntSize(value);
WriteUInt(buf, buf_size, size);
SerializeInt(buf, buf_size, value, size);
}
static void WriteStringElement(uint8** buf_ptr, int* buf_size_ptr,
int element_id, const std::string& value) {
uint8*& buf = *buf_ptr;
int& buf_size = *buf_size_ptr;
WriteElementId(&buf, &buf_size, element_id);
const uint64 size = value.length();
WriteUInt(&buf, &buf_size, size);
memcpy(buf, value.data(), size);
buf += size;
buf_size -= size;
}
TracksBuilder::TracksBuilder() {}
TracksBuilder::~TracksBuilder() {}
void TracksBuilder::AddTrack(
int track_num,
int track_type,
const std::string& codec_id,
const std::string& name,
const std::string& language) {
tracks_.push_back(Track(track_num, track_type, codec_id, name, language));
}
std::vector<uint8> TracksBuilder::Finish() {
// Allocate the storage
std::vector<uint8> buffer;
buffer.resize(GetTracksSize());
// Populate the storage with a tracks header
WriteTracks(&buffer[0], buffer.size());
return buffer;
}
int TracksBuilder::GetTracksSize() const {
return MasterElementSize(kWebMIdTracks, GetTracksPayloadSize());
}
int TracksBuilder::GetTracksPayloadSize() const {
int payload_size = 0;
for (TrackList::const_iterator itr = tracks_.begin();
itr != tracks_.end(); ++itr) {
payload_size += itr->GetSize();
}
return payload_size;
}
void TracksBuilder::WriteTracks(uint8* buf, int buf_size) const {
WriteMasterElement(&buf, &buf_size, kWebMIdTracks, GetTracksPayloadSize());
for (TrackList::const_iterator itr = tracks_.begin();
itr != tracks_.end(); ++itr) {
itr->Write(&buf, &buf_size);
}
}
TracksBuilder::Track::Track(int track_num, int track_type,
const std::string& codec_id,
const std::string& name,
const std::string& language)
: track_num_(track_num),
track_type_(track_type),
codec_id_(codec_id),
name_(name),
language_(language) {
}
int TracksBuilder::Track::GetSize() const {
return MasterElementSize(kWebMIdTrackEntry, GetPayloadSize());
}
int TracksBuilder::Track::GetPayloadSize() const {
int size = 0;
size += IntElementSize(kWebMIdTrackNumber, track_num_);
size += IntElementSize(kWebMIdTrackType, track_type_);
if (!codec_id_.empty())
size += StringElementSize(kWebMIdCodecID, codec_id_);
if (!name_.empty())
size += StringElementSize(kWebMIdName, name_);
if (!language_.empty())
size += StringElementSize(kWebMIdLanguage, language_);
return size;
}
void TracksBuilder::Track::Write(uint8** buf, int* buf_size) const {
WriteMasterElement(buf, buf_size, kWebMIdTrackEntry, GetPayloadSize());
WriteIntElement(buf, buf_size, kWebMIdTrackNumber, track_num_);
WriteIntElement(buf, buf_size, kWebMIdTrackType, track_type_);
if (!codec_id_.empty())
WriteStringElement(buf, buf_size, kWebMIdCodecID, codec_id_);
if (!name_.empty())
WriteStringElement(buf, buf_size, kWebMIdName, name_);
if (!language_.empty())
WriteStringElement(buf, buf_size, kWebMIdLanguage, language_);
}
} // namespace media

View File

@ -1,56 +0,0 @@
// Copyright (c) 2013 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_TRACKS_BUILDER_H_
#define MEDIA_WEBM_TRACKS_BUILDER_H_
#include <list>
#include <string>
#include <vector>
#include "base/basictypes.h"
namespace media {
class TracksBuilder {
public:
TracksBuilder();
~TracksBuilder();
void AddTrack(int track_num, int track_type, const std::string& codec_id,
const std::string& name, const std::string& language);
std::vector<uint8> Finish();
private:
int GetTracksSize() const;
int GetTracksPayloadSize() const;
void WriteTracks(uint8* buffer, int buffer_size) const;
class Track {
public:
Track(int track_num, int track_type, const std::string& codec_id,
const std::string& name, const std::string& language);
int GetSize() const;
void Write(uint8** buf, int* buf_size) const;
private:
int GetPayloadSize() const;
int track_num_;
int track_type_;
std::string codec_id_;
std::string name_;
std::string language_;
};
typedef std::list<Track> TrackList;
TrackList tracks_;
DISALLOW_COPY_AND_ASSIGN(TracksBuilder);
};
} // namespace media
#endif // MEDIA_WEBM_TRACKS_BUILDER_H_

View File

@ -1,112 +0,0 @@
// Copyright (c) 2013 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_audio_client.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/channel_layout.h"
#include "media/webm/webm_constants.h"
namespace media {
WebMAudioClient::WebMAudioClient(const LogCB& log_cb)
: log_cb_(log_cb) {
Reset();
}
WebMAudioClient::~WebMAudioClient() {
}
void WebMAudioClient::Reset() {
channels_ = -1;
samples_per_second_ = -1;
output_samples_per_second_ = -1;
}
bool WebMAudioClient::InitializeConfig(
const std::string& codec_id, const std::vector<uint8>& codec_private,
bool is_encrypted, AudioDecoderConfig* config) {
DCHECK(config);
AudioCodec audio_codec = kUnknownAudioCodec;
if (codec_id == "A_VORBIS") {
audio_codec = kCodecVorbis;
} else {
MEDIA_LOG(log_cb_) << "Unsupported audio codec_id " << codec_id;
return false;
}
if (samples_per_second_ <= 0)
return false;
// Set channel layout default if a Channels element was not present.
if (channels_ == -1)
channels_ = 1;
ChannelLayout channel_layout = GuessChannelLayout(channels_);
if (channel_layout == CHANNEL_LAYOUT_UNSUPPORTED) {
MEDIA_LOG(log_cb_) << "Unsupported channel count " << channels_;
return false;
}
int samples_per_second = samples_per_second_;
if (output_samples_per_second_ > 0)
samples_per_second = output_samples_per_second_;
const uint8* extra_data = NULL;
size_t extra_data_size = 0;
if (codec_private.size() > 0) {
extra_data = &codec_private[0];
extra_data_size = codec_private.size();
}
config->Initialize(
audio_codec, kSampleFormatPlanarF32, channel_layout,
samples_per_second, extra_data, extra_data_size, is_encrypted, true);
return config->IsValidConfig();
}
bool WebMAudioClient::OnUInt(int id, int64 val) {
if (id == kWebMIdChannels) {
if (channels_ != -1) {
MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
<< " specified. (" << channels_ << " and " << val
<< ")";
return false;
}
channels_ = val;
}
return true;
}
bool WebMAudioClient::OnFloat(int id, double val) {
double* dst = NULL;
switch (id) {
case kWebMIdSamplingFrequency:
dst = &samples_per_second_;
break;
case kWebMIdOutputSamplingFrequency:
dst = &output_samples_per_second_;
break;
default:
return true;
}
if (val <= 0)
return false;
if (*dst != -1) {
MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
<< " specified (" << *dst << " and " << val << ")";
return false;
}
*dst = val;
return true;
}
} // namespace media

View File

@ -1,52 +0,0 @@
// Copyright (c) 2013 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_AUDIO_CLIENT_H_
#define MEDIA_WEBM_WEBM_AUDIO_CLIENT_H_
#include <string>
#include <vector>
#include "media/base/media_log.h"
#include "media/webm/webm_parser.h"
namespace media {
class AudioDecoderConfig;
// Helper class used to parse an Audio element inside a TrackEntry element.
class WebMAudioClient : public WebMParserClient {
public:
explicit WebMAudioClient(const LogCB& log_cb);
virtual ~WebMAudioClient();
// Reset this object's state so it can process a new audio track element.
void Reset();
// Initialize |config| with the data in |codec_id|, |codec_private|,
// |is_encrypted| and the fields parsed from the last audio track element this
// object was used to parse.
// Returns true if |config| was successfully initialized.
// Returns false if there was unexpected values in the provided parameters or
// audio track element fields.
bool InitializeConfig(const std::string& codec_id,
const std::vector<uint8>& codec_private,
bool is_encrypted,
AudioDecoderConfig* config);
private:
// WebMParserClient implementation.
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnFloat(int id, double val) OVERRIDE;
LogCB log_cb_;
int channels_;
double samples_per_second_;
double output_samples_per_second_;
DISALLOW_COPY_AND_ASSIGN(WebMAudioClient);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_AUDIO_CLIENT_H_

View File

@ -1,420 +0,0 @@
// Copyright (c) 2012 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 <vector>
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "media/base/buffers.h"
#include "media/base/decrypt_config.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_crypto_helpers.h"
namespace media {
WebMClusterParser::TextTrackIterator::TextTrackIterator(
const TextTrackMap& text_track_map) :
iterator_(text_track_map.begin()),
iterator_end_(text_track_map.end()) {
}
WebMClusterParser::TextTrackIterator::TextTrackIterator(
const TextTrackIterator& rhs) :
iterator_(rhs.iterator_),
iterator_end_(rhs.iterator_end_) {
}
WebMClusterParser::TextTrackIterator::~TextTrackIterator() {
}
bool WebMClusterParser::TextTrackIterator::operator()(
int* track_num,
const BufferQueue** buffers) {
if (iterator_ == iterator_end_) {
*track_num = 0;
*buffers = NULL;
return false;
}
*track_num = iterator_->first;
*buffers = &iterator_->second.buffers();
++iterator_;
return true;
}
WebMClusterParser::WebMClusterParser(
int64 timecode_scale, int audio_track_num, int video_track_num,
const WebMTracksParser::TextTracks& text_tracks,
const std::set<int64>& ignored_tracks,
const std::string& audio_encryption_key_id,
const std::string& video_encryption_key_id,
const LogCB& log_cb)
: timecode_multiplier_(timecode_scale / 1000.0),
ignored_tracks_(ignored_tracks),
audio_encryption_key_id_(audio_encryption_key_id),
video_encryption_key_id_(video_encryption_key_id),
parser_(kWebMIdCluster, this),
last_block_timecode_(-1),
block_data_size_(-1),
block_duration_(-1),
block_add_id_(-1),
block_additional_data_size_(-1),
cluster_timecode_(-1),
cluster_start_time_(kNoTimestamp()),
cluster_ended_(false),
audio_(audio_track_num, false),
video_(video_track_num, true),
log_cb_(log_cb) {
for (WebMTracksParser::TextTracks::const_iterator it = text_tracks.begin();
it != text_tracks.end();
++it) {
text_track_map_.insert(std::make_pair(it->first, Track(it->first, false)));
}
}
WebMClusterParser::~WebMClusterParser() {}
void WebMClusterParser::Reset() {
last_block_timecode_ = -1;
cluster_timecode_ = -1;
cluster_start_time_ = kNoTimestamp();
cluster_ended_ = false;
parser_.Reset();
audio_.Reset();
video_.Reset();
ResetTextTracks();
}
int WebMClusterParser::Parse(const uint8* buf, int size) {
audio_.Reset();
video_.Reset();
ResetTextTracks();
int result = parser_.Parse(buf, size);
if (result < 0) {
cluster_ended_ = false;
return result;
}
cluster_ended_ = parser_.IsParsingComplete();
if (cluster_ended_) {
// If there were no buffers in this cluster, set the cluster start time to
// be the |cluster_timecode_|.
if (cluster_start_time_ == kNoTimestamp()) {
DCHECK_GT(cluster_timecode_, -1);
cluster_start_time_ = base::TimeDelta::FromMicroseconds(
cluster_timecode_ * timecode_multiplier_);
}
// Reset the parser if we're done parsing so that
// it is ready to accept another cluster on the next
// call.
parser_.Reset();
last_block_timecode_ = -1;
cluster_timecode_ = -1;
}
return result;
}
WebMClusterParser::TextTrackIterator
WebMClusterParser::CreateTextTrackIterator() const {
return TextTrackIterator(text_track_map_);
}
WebMParserClient* WebMClusterParser::OnListStart(int id) {
if (id == kWebMIdCluster) {
cluster_timecode_ = -1;
cluster_start_time_ = kNoTimestamp();
} else if (id == kWebMIdBlockGroup) {
block_data_.reset();
block_data_size_ = -1;
block_duration_ = -1;
} else if (id == kWebMIdBlockAdditions) {
block_add_id_ = -1;
block_additional_data_.reset();
block_additional_data_size_ = -1;
}
return this;
}
bool WebMClusterParser::OnListEnd(int id) {
if (id != kWebMIdBlockGroup)
return true;
// Make sure the BlockGroup actually had a Block.
if (block_data_size_ == -1) {
MEDIA_LOG(log_cb_) << "Block missing from BlockGroup.";
return false;
}
bool result = ParseBlock(false, block_data_.get(), block_data_size_,
block_additional_data_.get(),
block_additional_data_size_, block_duration_);
block_data_.reset();
block_data_size_ = -1;
block_duration_ = -1;
block_add_id_ = -1;
block_additional_data_.reset();
block_additional_data_size_ = -1;
return result;
}
bool WebMClusterParser::OnUInt(int id, int64 val) {
int64* dst;
switch (id) {
case kWebMIdTimecode:
dst = &cluster_timecode_;
break;
case kWebMIdBlockDuration:
dst = &block_duration_;
break;
case kWebMIdBlockAddID:
dst = &block_add_id_;
break;
default:
return true;
}
if (*dst != -1)
return false;
*dst = val;
return true;
}
bool WebMClusterParser::ParseBlock(bool is_simple_block, const uint8* buf,
int size, const uint8* additional,
int additional_size, int duration) {
if (size < 4)
return false;
// Return an error if the trackNum > 127. We just aren't
// going to support large track numbers right now.
if (!(buf[0] & 0x80)) {
MEDIA_LOG(log_cb_) << "TrackNumber over 127 not supported";
return false;
}
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) {
MEDIA_LOG(log_cb_) << "Lacing " << lacing << " is not supported yet.";
return false;
}
// Sign extend negative timecode offsets.
if (timecode & 0x8000)
timecode |= ~0xffff;
const uint8* frame_data = buf + 4;
int frame_size = size - (frame_data - buf);
return OnBlock(is_simple_block, track_num, timecode, duration, flags,
frame_data, frame_size, additional, additional_size);
}
bool WebMClusterParser::OnBinary(int id, const uint8* data, int size) {
switch (id) {
case kWebMIdSimpleBlock:
return ParseBlock(true, data, size, NULL, -1, -1);
case kWebMIdBlock:
if (block_data_) {
MEDIA_LOG(log_cb_) << "More than 1 Block in a BlockGroup is not "
"supported.";
return false;
}
block_data_.reset(new uint8[size]);
memcpy(block_data_.get(), data, size);
block_data_size_ = size;
return true;
case kWebMIdBlockAdditional: {
uint64 block_add_id = base::HostToNet64(block_add_id_);
if (block_additional_data_) {
// TODO(vigneshv): Technically, more than 1 BlockAdditional is allowed
// as per matroska spec. But for now we don't have a use case to
// support parsing of such files. Take a look at this again when such a
// case arises.
MEDIA_LOG(log_cb_) << "More than 1 BlockAdditional in a BlockGroup is "
"not supported.";
return false;
}
// First 8 bytes of side_data in DecoderBuffer is the BlockAddID
// element's value in Big Endian format. This is done to mimic ffmpeg
// demuxer's behavior.
block_additional_data_size_ = size + sizeof(block_add_id);
block_additional_data_.reset(new uint8[block_additional_data_size_]);
memcpy(block_additional_data_.get(), &block_add_id,
sizeof(block_add_id));
memcpy(block_additional_data_.get() + 8, data, size);
return true;
}
default:
return true;
}
}
bool WebMClusterParser::OnBlock(bool is_simple_block, int track_num,
int timecode,
int block_duration,
int flags,
const uint8* data, int size,
const uint8* additional, int additional_size) {
DCHECK_GE(size, 0);
if (cluster_timecode_ == -1) {
MEDIA_LOG(log_cb_) << "Got a block before cluster timecode.";
return false;
}
// TODO(acolwell): Should relative negative timecode offsets be rejected? Or
// only when the absolute timecode is negative? See http://crbug.com/271794
if (timecode < 0) {
MEDIA_LOG(log_cb_) << "Got a block with negative timecode offset "
<< timecode;
return false;
}
if (last_block_timecode_ != -1 && timecode < last_block_timecode_) {
MEDIA_LOG(log_cb_)
<< "Got a block with a timecode before the previous block.";
return false;
}
Track* track = NULL;
std::string encryption_key_id;
if (track_num == audio_.track_num()) {
track = &audio_;
encryption_key_id = audio_encryption_key_id_;
} else if (track_num == video_.track_num()) {
track = &video_;
encryption_key_id = video_encryption_key_id_;
} else if (ignored_tracks_.find(track_num) != ignored_tracks_.end()) {
return true;
} else if (Track* const text_track = FindTextTrack(track_num)) {
if (is_simple_block) // BlockGroup is required for WebVTT cues
return false;
if (block_duration < 0) // not specified
return false;
track = text_track;
} else {
MEDIA_LOG(log_cb_) << "Unexpected track number " << track_num;
return false;
}
last_block_timecode_ = timecode;
base::TimeDelta timestamp = base::TimeDelta::FromMicroseconds(
(cluster_timecode_ + timecode) * timecode_multiplier_);
// The first bit of the flags is set when a SimpleBlock contains only
// keyframes. If this is a Block, then inspection of the payload is
// necessary to determine whether it contains a keyframe or not.
// http://www.matroska.org/technical/specs/index.html
bool is_keyframe =
is_simple_block ? (flags & 0x80) != 0 : track->IsKeyframe(data, size);
scoped_refptr<StreamParserBuffer> buffer =
StreamParserBuffer::CopyFrom(data, size, additional, additional_size,
is_keyframe);
// Every encrypted Block has a signal byte and IV prepended to it. Current
// encrypted WebM request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
if (!encryption_key_id.empty()) {
scoped_ptr<DecryptConfig> config(WebMCreateDecryptConfig(
data, size,
reinterpret_cast<const uint8*>(encryption_key_id.data()),
encryption_key_id.size()));
if (!config)
return false;
buffer->set_decrypt_config(config.Pass());
}
buffer->set_timestamp(timestamp);
if (cluster_start_time_ == kNoTimestamp())
cluster_start_time_ = timestamp;
if (block_duration >= 0) {
buffer->set_duration(base::TimeDelta::FromMicroseconds(
block_duration * timecode_multiplier_));
}
return track->AddBuffer(buffer);
}
WebMClusterParser::Track::Track(int track_num, bool is_video)
: track_num_(track_num),
is_video_(is_video) {
}
WebMClusterParser::Track::~Track() {}
bool WebMClusterParser::Track::AddBuffer(
const scoped_refptr<StreamParserBuffer>& buffer) {
DVLOG(2) << "AddBuffer() : " << track_num_
<< " ts " << buffer->timestamp().InSecondsF()
<< " dur " << buffer->duration().InSecondsF()
<< " kf " << buffer->IsKeyframe()
<< " size " << buffer->data_size();
buffers_.push_back(buffer);
return true;
}
void WebMClusterParser::Track::Reset() {
buffers_.clear();
}
bool WebMClusterParser::Track::IsKeyframe(const uint8* data, int size) const {
// For now, assume that all blocks are keyframes for datatypes other than
// video. This is a valid assumption for Vorbis, WebVTT, & Opus.
if (!is_video_)
return true;
// Make sure the block is big enough for the minimal keyframe header size.
if (size < 7)
return false;
// The LSb of the first byte must be a 0 for a keyframe.
// http://tools.ietf.org/html/rfc6386 Section 19.1
if ((data[0] & 0x01) != 0)
return false;
// Verify VP8 keyframe startcode.
// http://tools.ietf.org/html/rfc6386 Section 19.1
if (data[3] != 0x9d || data[4] != 0x01 || data[5] != 0x2a)
return false;
return true;
}
void WebMClusterParser::ResetTextTracks() {
for (TextTrackMap::iterator it = text_track_map_.begin();
it != text_track_map_.end();
++it) {
it->second.Reset();
}
}
WebMClusterParser::Track*
WebMClusterParser::FindTextTrack(int track_num) {
const TextTrackMap::iterator it = text_track_map_.find(track_num);
if (it == text_track_map_.end())
return NULL;
return &it->second;
}
} // namespace media

View File

@ -1,155 +0,0 @@
// Copyright (c) 2012 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 <map>
#include <set>
#include <string>
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
#include "media/base/media_log.h"
#include "media/base/stream_parser_buffer.h"
#include "media/webm/webm_parser.h"
#include "media/webm/webm_tracks_parser.h"
namespace media {
class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
private:
// Helper class that manages per-track state.
class Track {
public:
Track(int track_num, bool is_video);
~Track();
int track_num() const { return track_num_; }
const std::deque<scoped_refptr<StreamParserBuffer> >& buffers() const {
return buffers_;
}
bool AddBuffer(const scoped_refptr<StreamParserBuffer>& buffer);
// Clears all buffer state.
void Reset();
// Helper function used to inspect block data to determine if the
// block is a keyframe.
// |data| contains the bytes in the block.
// |size| indicates the number of bytes in |data|.
bool IsKeyframe(const uint8* data, int size) const;
private:
int track_num_;
std::deque<scoped_refptr<StreamParserBuffer> > buffers_;
bool is_video_;
};
typedef std::map<int, Track> TextTrackMap;
public:
typedef std::deque<scoped_refptr<StreamParserBuffer> > BufferQueue;
class MEDIA_EXPORT TextTrackIterator {
public:
explicit TextTrackIterator(const TextTrackMap& text_track_map);
TextTrackIterator(const TextTrackIterator& rhs);
~TextTrackIterator();
// To visit each text track. If the iterator is exhausted, it returns
// as parameters the values 0 and NULL, and the function returns false.
// Otherwise, it returns the buffers for the associated track, and the
// function returns true.
bool operator()(int* track_num, const BufferQueue** buffers);
private:
TextTrackIterator& operator=(const TextTrackIterator&);
TextTrackMap::const_iterator iterator_;
const TextTrackMap::const_iterator iterator_end_;
};
WebMClusterParser(int64 timecode_scale,
int audio_track_num,
int video_track_num,
const WebMTracksParser::TextTracks& text_tracks,
const std::set<int64>& ignored_tracks,
const std::string& audio_encryption_key_id,
const std::string& video_encryption_key_id,
const LogCB& log_cb);
virtual ~WebMClusterParser();
// Resets the parser state so it can accept a new cluster.
void Reset();
// Parses a WebM cluster element in |buf|.
//
// Returns -1 if the parse fails.
// Returns 0 if more data is needed.
// Returns the number of bytes parsed on success.
int Parse(const uint8* buf, int size);
base::TimeDelta cluster_start_time() const { return cluster_start_time_; }
const BufferQueue& audio_buffers() const { return audio_.buffers(); }
const BufferQueue& video_buffers() const { return video_.buffers(); }
// Returns an iterator object, allowing each text track to be visited.
TextTrackIterator CreateTextTrackIterator() const;
// Returns true if the last Parse() call stopped at the end of a cluster.
bool cluster_ended() const { return cluster_ended_; }
private:
// WebMParserClient methods.
virtual WebMParserClient* OnListStart(int id) OVERRIDE;
virtual bool OnListEnd(int id) OVERRIDE;
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE;
bool ParseBlock(bool is_simple_block, const uint8* buf, int size,
const uint8* additional, int additional_size, int duration);
bool OnBlock(bool is_simple_block, int track_num, int timecode, int duration,
int flags, const uint8* data, int size,
const uint8* additional, int additional_size);
// Resets the Track objects associated with each text track.
void ResetTextTracks();
// Search for the indicated track_num among the text tracks. Returns NULL
// if that track num is not a text track.
Track* FindTextTrack(int track_num);
double timecode_multiplier_; // Multiplier used to convert timecodes into
// microseconds.
std::set<int64> ignored_tracks_;
std::string audio_encryption_key_id_;
std::string video_encryption_key_id_;
WebMListParser parser_;
int64 last_block_timecode_;
scoped_ptr<uint8[]> block_data_;
int block_data_size_;
int64 block_duration_;
int64 block_add_id_;
scoped_ptr<uint8[]> block_additional_data_;
int block_additional_data_size_;
int64 cluster_timecode_;
base::TimeDelta cluster_start_time_;
bool cluster_ended_;
Track audio_;
Track video_;
TextTrackMap text_track_map_;
LogCB log_cb_;
DISALLOW_IMPLICIT_CONSTRUCTORS(WebMClusterParser);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_CLUSTER_PARSER_H_

View File

@ -1,533 +0,0 @@
// Copyright (c) 2012 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 <algorithm>
#include "base/bind.h"
#include "base/logging.h"
#include "media/base/decrypt_config.h"
#include "media/webm/cluster_builder.h"
#include "media/webm/webm_cluster_parser.h"
#include "media/webm/webm_constants.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::_;
namespace media {
enum {
kTimecodeScale = 1000000, // Timecode scale for millisecond timestamps.
kAudioTrackNum = 1,
kVideoTrackNum = 2,
kTextTrackNum = 3,
};
struct BlockInfo {
int track_num;
int timestamp;
int duration;
bool use_simple_block;
};
static const BlockInfo kDefaultBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
{ kVideoTrackNum, 33, 34, true },
{ kAudioTrackNum, 46, 23, true },
{ kVideoTrackNum, 67, 33, false },
{ kAudioTrackNum, 69, 23, false },
{ kVideoTrackNum, 100, 33, false },
};
static const uint8 kEncryptedFrame[] = {
0x01, // Block is encrypted
0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08 // IV
};
static scoped_ptr<Cluster> CreateCluster(int timecode,
const BlockInfo* block_info,
int block_count) {
ClusterBuilder cb;
cb.SetClusterTimecode(0);
for (int i = 0; i < block_count; i++) {
uint8 data[] = { 0x00 };
if (block_info[i].use_simple_block) {
cb.AddSimpleBlock(block_info[i].track_num,
block_info[i].timestamp,
0, data, sizeof(data));
continue;
}
CHECK_GE(block_info[i].duration, 0);
cb.AddBlockGroup(block_info[i].track_num,
block_info[i].timestamp,
block_info[i].duration,
0, data, sizeof(data));
}
return cb.Finish();
}
// Creates a Cluster with one encrypted Block. |bytes_to_write| is number of
// bytes of the encrypted frame to write.
static scoped_ptr<Cluster> CreateEncryptedCluster(int bytes_to_write) {
CHECK_GT(bytes_to_write, 0);
CHECK_LE(bytes_to_write, static_cast<int>(sizeof(kEncryptedFrame)));
ClusterBuilder cb;
cb.SetClusterTimecode(0);
cb.AddSimpleBlock(kVideoTrackNum, 0, 0, kEncryptedFrame, bytes_to_write);
return cb.Finish();
}
static bool VerifyBuffers(const WebMClusterParser::BufferQueue& audio_buffers,
const WebMClusterParser::BufferQueue& video_buffers,
const WebMClusterParser::BufferQueue& text_buffers,
const BlockInfo* block_info,
int block_count) {
size_t audio_offset = 0;
size_t video_offset = 0;
size_t text_offset = 0;
for (int i = 0; i < block_count; i++) {
const WebMClusterParser::BufferQueue* buffers = NULL;
size_t* offset;
if (block_info[i].track_num == kAudioTrackNum) {
buffers = &audio_buffers;
offset = &audio_offset;
} else if (block_info[i].track_num == kVideoTrackNum) {
buffers = &video_buffers;
offset = &video_offset;
} else if (block_info[i].track_num == kTextTrackNum) {
buffers = &text_buffers;
offset = &text_offset;
} else {
LOG(ERROR) << "Unexpected track number " << block_info[i].track_num;
return false;
}
if (*offset >= buffers->size())
return false;
scoped_refptr<StreamParserBuffer> buffer = (*buffers)[(*offset)++];
EXPECT_EQ(buffer->timestamp().InMilliseconds(), block_info[i].timestamp);
if (!block_info[i].use_simple_block)
EXPECT_NE(buffer->duration(), kNoTimestamp());
if (buffer->duration() != kNoTimestamp())
EXPECT_EQ(buffer->duration().InMilliseconds(), block_info[i].duration);
}
return true;
}
static bool VerifyBuffers(const scoped_ptr<WebMClusterParser>& parser,
const BlockInfo* block_info,
int block_count) {
typedef WebMClusterParser::TextTrackIterator TextTrackIterator;
TextTrackIterator text_it = parser->CreateTextTrackIterator();
int text_track_num;
const WebMClusterParser::BufferQueue* text_buffers;
while (text_it(&text_track_num, &text_buffers))
break;
const WebMClusterParser::BufferQueue no_text_buffers;
if (text_buffers == NULL)
text_buffers = &no_text_buffers;
return VerifyBuffers(parser->audio_buffers(),
parser->video_buffers(),
*text_buffers,
block_info,
block_count);
}
static bool VerifyTextBuffers(
const scoped_ptr<WebMClusterParser>& parser,
const BlockInfo* block_info_ptr,
int block_count,
int text_track_num,
const WebMClusterParser::BufferQueue& text_buffers) {
const BlockInfo* const block_info_end = block_info_ptr + block_count;
typedef WebMClusterParser::BufferQueue::const_iterator TextBufferIter;
TextBufferIter buffer_iter = text_buffers.begin();
const TextBufferIter buffer_end = text_buffers.end();
while (block_info_ptr != block_info_end) {
const BlockInfo& block_info = *block_info_ptr++;
if (block_info.track_num != text_track_num)
continue;
EXPECT_FALSE(block_info.use_simple_block);
EXPECT_FALSE(buffer_iter == buffer_end);
const scoped_refptr<StreamParserBuffer> buffer = *buffer_iter++;
EXPECT_EQ(buffer->timestamp().InMilliseconds(), block_info.timestamp);
EXPECT_EQ(buffer->duration().InMilliseconds(), block_info.duration);
}
EXPECT_TRUE(buffer_iter == buffer_end);
return true;
}
static bool VerifyEncryptedBuffer(
scoped_refptr<StreamParserBuffer> buffer) {
EXPECT_TRUE(buffer->decrypt_config());
EXPECT_EQ(static_cast<unsigned long>(DecryptConfig::kDecryptionKeySize),
buffer->decrypt_config()->iv().length());
const uint8* data = buffer->data();
return data[0] & kWebMFlagEncryptedFrame;
}
static void AppendToEnd(const WebMClusterParser::BufferQueue& src,
WebMClusterParser::BufferQueue* dest) {
for (WebMClusterParser::BufferQueue::const_iterator itr = src.begin();
itr != src.end(); ++itr) {
dest->push_back(*itr);
}
}
class WebMClusterParserTest : public testing::Test {
public:
WebMClusterParserTest()
: parser_(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
WebMTracksParser::TextTracks(),
std::set<int64>(),
std::string(),
std::string(),
LogCB())) {}
protected:
scoped_ptr<WebMClusterParser> parser_;
};
TEST_F(WebMClusterParserTest, Reset) {
InSequence s;
int block_count = arraysize(kDefaultBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
// Send slightly less than the full cluster so all but the last block is
// parsed.
int result = parser_->Parse(cluster->data(), cluster->size() - 1);
EXPECT_GT(result, 0);
EXPECT_LT(result, cluster->size());
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count - 1));
parser_->Reset();
// Now parse a whole cluster to verify that all the blocks will get parsed.
result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(result, cluster->size());
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, ParseClusterWithSingleCall) {
int block_count = arraysize(kDefaultBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
ASSERT_TRUE(VerifyBuffers(parser_, kDefaultBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, ParseClusterWithMultipleCalls) {
int block_count = arraysize(kDefaultBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kDefaultBlockInfo, block_count));
WebMClusterParser::BufferQueue audio_buffers;
WebMClusterParser::BufferQueue video_buffers;
const WebMClusterParser::BufferQueue no_text_buffers;
const uint8* data = cluster->data();
int size = cluster->size();
int default_parse_size = 3;
int parse_size = std::min(default_parse_size, size);
while (size > 0) {
int result = parser_->Parse(data, parse_size);
ASSERT_GE(result, 0);
ASSERT_LE(result, parse_size);
if (result == 0) {
// The parser needs more data so increase the parse_size a little.
parse_size += default_parse_size;
parse_size = std::min(parse_size, size);
continue;
}
AppendToEnd(parser_->audio_buffers(), &audio_buffers);
AppendToEnd(parser_->video_buffers(), &video_buffers);
parse_size = default_parse_size;
data += result;
size -= result;
}
ASSERT_TRUE(VerifyBuffers(audio_buffers, video_buffers,
no_text_buffers, kDefaultBlockInfo,
block_count));
}
// Verify that both BlockGroups with the BlockDuration before the Block
// and BlockGroups with the BlockDuration after the Block are supported
// correctly.
// Note: Raw bytes are use here because ClusterBuilder only generates
// one of these scenarios.
TEST_F(WebMClusterParserTest, ParseBlockGroup) {
const BlockInfo kBlockInfo[] = {
{ kAudioTrackNum, 0, 23, false },
{ kVideoTrackNum, 33, 34, false },
};
int block_count = arraysize(kBlockInfo);
const uint8 kClusterData[] = {
0x1F, 0x43, 0xB6, 0x75, 0x9B, // Cluster(size=27)
0xE7, 0x81, 0x00, // Timecode(size=1, value=0)
// BlockGroup with BlockDuration before Block.
0xA0, 0x8A, // BlockGroup(size=10)
0x9B, 0x81, 0x17, // BlockDuration(size=1, value=23)
0xA1, 0x85, 0x81, 0x00, 0x00, 0x00, 0xaa, // Block(size=5, track=1, ts=0)
// BlockGroup with BlockDuration after Block.
0xA0, 0x8A, // BlockGroup(size=10)
0xA1, 0x85, 0x82, 0x00, 0x21, 0x00, 0x55, // Block(size=5, track=2, ts=33)
0x9B, 0x81, 0x22, // BlockDuration(size=1, value=34)
};
const int kClusterSize = sizeof(kClusterData);
int result = parser_->Parse(kClusterData, kClusterSize);
EXPECT_EQ(result, kClusterSize);
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, ParseSimpleBlockAndBlockGroupMixture) {
const BlockInfo kBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, false },
{ kVideoTrackNum, 33, 34, true },
{ kAudioTrackNum, 46, 23, false },
{ kVideoTrackNum, 67, 33, false },
};
int block_count = arraysize(kBlockInfo);
scoped_ptr<Cluster> cluster(CreateCluster(0, kBlockInfo, block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
ASSERT_TRUE(VerifyBuffers(parser_, kBlockInfo, block_count));
}
TEST_F(WebMClusterParserTest, IgnoredTracks) {
std::set<int64> ignored_tracks;
ignored_tracks.insert(kTextTrackNum);
parser_.reset(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
WebMTracksParser::TextTracks(),
ignored_tracks,
std::string(),
std::string(),
LogCB()));
const BlockInfo kInputBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
{ kVideoTrackNum, 33, 33, true },
{ kTextTrackNum, 33, 99, true },
{ kAudioTrackNum, 46, 23, true },
{ kVideoTrackNum, 67, 33, true },
};
int input_block_count = arraysize(kInputBlockInfo);
const BlockInfo kOutputBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
{ kVideoTrackNum, 33, 33, true },
{ kAudioTrackNum, 46, 23, true },
{ kVideoTrackNum, 67, 33, true },
};
int output_block_count = arraysize(kOutputBlockInfo);
scoped_ptr<Cluster> cluster(
CreateCluster(0, kInputBlockInfo, input_block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
ASSERT_TRUE(VerifyBuffers(parser_, kOutputBlockInfo, output_block_count));
}
TEST_F(WebMClusterParserTest, ParseTextTracks) {
typedef WebMTracksParser::TextTracks TextTracks;
TextTracks text_tracks;
WebMTracksParser::TextTrackInfo text_track_info;
text_track_info.kind = kTextSubtitles;
text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
text_track_info));
parser_.reset(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
text_tracks,
std::set<int64>(),
std::string(),
std::string(),
LogCB()));
const BlockInfo kInputBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
{ kVideoTrackNum, 33, 33, true },
{ kTextTrackNum, 33, 42, false },
{ kAudioTrackNum, 46, 23, true },
{ kTextTrackNum, 55, 44, false },
{ kVideoTrackNum, 67, 33, true },
};
int input_block_count = arraysize(kInputBlockInfo);
scoped_ptr<Cluster> cluster(
CreateCluster(0, kInputBlockInfo, input_block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
ASSERT_TRUE(VerifyBuffers(parser_, kInputBlockInfo, input_block_count));
}
TEST_F(WebMClusterParserTest, TextTracksSimpleBlock) {
typedef WebMTracksParser::TextTracks TextTracks;
TextTracks text_tracks;
WebMTracksParser::TextTrackInfo text_track_info;
text_track_info.kind = kTextSubtitles;
text_tracks.insert(std::make_pair(TextTracks::key_type(kTextTrackNum),
text_track_info));
parser_.reset(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
text_tracks,
std::set<int64>(),
std::string(),
std::string(),
LogCB()));
const BlockInfo kInputBlockInfo[] = {
{ kTextTrackNum, 33, 42, true },
};
int input_block_count = arraysize(kInputBlockInfo);
scoped_ptr<Cluster> cluster(
CreateCluster(0, kInputBlockInfo, input_block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_LT(result, 0);
}
TEST_F(WebMClusterParserTest, ParseMultipleTextTracks) {
typedef WebMTracksParser::TextTracks TextTracks;
TextTracks text_tracks;
WebMTracksParser::TextTrackInfo text_track_info;
const int kSubtitleTextTrackNum = kTextTrackNum;
const int kCaptionTextTrackNum = kTextTrackNum + 1;
text_track_info.kind = kTextSubtitles;
text_tracks.insert(std::make_pair(TextTracks::key_type(kSubtitleTextTrackNum),
text_track_info));
text_track_info.kind = kTextCaptions;
text_tracks.insert(std::make_pair(TextTracks::key_type(kCaptionTextTrackNum),
text_track_info));
parser_.reset(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
text_tracks,
std::set<int64>(),
std::string(),
std::string(),
LogCB()));
const BlockInfo kInputBlockInfo[] = {
{ kAudioTrackNum, 0, 23, true },
{ kAudioTrackNum, 23, 23, true },
{ kVideoTrackNum, 33, 33, true },
{ kSubtitleTextTrackNum, 33, 42, false },
{ kAudioTrackNum, 46, 23, true },
{ kCaptionTextTrackNum, 55, 44, false },
{ kVideoTrackNum, 67, 33, true },
{ kSubtitleTextTrackNum, 67, 33, false },
};
int input_block_count = arraysize(kInputBlockInfo);
scoped_ptr<Cluster> cluster(
CreateCluster(0, kInputBlockInfo, input_block_count));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
WebMClusterParser::TextTrackIterator text_it =
parser_->CreateTextTrackIterator();
int text_track_num;
const WebMClusterParser::BufferQueue* text_buffers;
while (text_it(&text_track_num, &text_buffers)) {
const WebMTracksParser::TextTracks::const_iterator find_result =
text_tracks.find(text_track_num);
ASSERT_TRUE(find_result != text_tracks.end());
ASSERT_TRUE(VerifyTextBuffers(parser_, kInputBlockInfo, input_block_count,
text_track_num, *text_buffers));
}
}
TEST_F(WebMClusterParserTest, ParseEncryptedBlock) {
scoped_ptr<Cluster> cluster(CreateEncryptedCluster(sizeof(kEncryptedFrame)));
parser_.reset(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
WebMTracksParser::TextTracks(),
std::set<int64>(),
std::string(),
"video_key_id",
LogCB()));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
ASSERT_EQ(1UL, parser_->video_buffers().size());
scoped_refptr<StreamParserBuffer> buffer = parser_->video_buffers()[0];
EXPECT_TRUE(VerifyEncryptedBuffer(buffer));
}
TEST_F(WebMClusterParserTest, ParseBadEncryptedBlock) {
scoped_ptr<Cluster> cluster(
CreateEncryptedCluster(sizeof(kEncryptedFrame) - 1));
parser_.reset(new WebMClusterParser(kTimecodeScale,
kAudioTrackNum,
kVideoTrackNum,
WebMTracksParser::TextTracks(),
std::set<int64>(),
std::string(),
"video_key_id",
LogCB()));
int result = parser_->Parse(cluster->data(), cluster->size());
EXPECT_EQ(-1, result);
}
} // namespace media

View File

@ -1,14 +0,0 @@
// Copyright (c) 2013 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_constants.h"
namespace media {
const char kWebMCodecSubtitles[] = "D_WEBVTT/SUBTITLES";
const char kWebMCodecCaptions[] = "D_WEBVTT/CAPTIONS";
const char kWebMCodecDescriptions[] = "D_WEBVTT/DESCRIPTIONS";
const char kWebMCodecMetadata[] = "D_WEBVTT/METADATA";
} // namespace media

View File

@ -1,225 +0,0 @@
// Copyright (c) 2012 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_
#include "base/basictypes.h"
#include "media/base/media_export.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 kWebMIdAESSettingsCipherMode = 0x47E8;
const int kWebMIdAlphaMode = 0x53C0;
const int kWebMIdAspectRatioType = 0x54B3;
const int kWebMIdAttachedFile = 0x61A7;
const int kWebMIdAttachmentLink = 0x7446;
const int kWebMIdAttachments = 0x1941A469;
const int kWebMIdAudio = 0xE1;
const int kWebMIdBitDepth = 0x6264;
const int kWebMIdBlock = 0xA1;
const int kWebMIdBlockAddID = 0xEE;
const int kWebMIdBlockAdditions = 0x75A1;
const int kWebMIdBlockAdditional = 0xA5;
const int kWebMIdBlockDuration = 0x9B;
const int kWebMIdBlockGroup = 0xA0;
const int kWebMIdBlockMore = 0xA6;
const int kWebMIdChannels = 0x9F;
const int kWebMIdChapCountry = 0x437E;
const int kWebMIdChapLanguage = 0x437C;
const int kWebMIdChapProcess = 0x6944;
const int kWebMIdChapProcessCodecID = 0x6955;
const int kWebMIdChapProcessCommand = 0x6911;
const int kWebMIdChapProcessData = 0x6933;
const int kWebMIdChapProcessPrivate = 0x450D;
const int kWebMIdChapProcessTime = 0x6922;
const int kWebMIdChapString = 0x85;
const int kWebMIdChapterAtom = 0xB6;
const int kWebMIdChapterDisplay = 0x80;
const int kWebMIdChapterFlagEnabled = 0x4598;
const int kWebMIdChapterFlagHidden = 0x98;
const int kWebMIdChapterPhysicalEquiv = 0x63C3;
const int kWebMIdChapters = 0x1043A770;
const int kWebMIdChapterSegmentEditionUID = 0x6EBC;
const int kWebMIdChapterSegmentUID = 0x6E67;
const int kWebMIdChapterTimeEnd = 0x92;
const int kWebMIdChapterTimeStart = 0x91;
const int kWebMIdChapterTrack = 0x8F;
const int kWebMIdChapterTrackNumber = 0x89;
const int kWebMIdChapterTranslate = 0x6924;
const int kWebMIdChapterTranslateCodec = 0x69BF;
const int kWebMIdChapterTranslateEditionUID = 0x69FC;
const int kWebMIdChapterTranslateID = 0x69A5;
const int kWebMIdChapterUID = 0x73C4;
const int kWebMIdCluster = 0x1F43B675;
const int kWebMIdCodecDecodeAll = 0xAA;
const int kWebMIdCodecID = 0x86;
const int kWebMIdCodecName = 0x258688;
const int kWebMIdCodecPrivate = 0x63A2;
const int kWebMIdCodecState = 0xA4;
const int kWebMIdColorSpace = 0x2EB524;
const int kWebMIdContentCompAlgo = 0x4254;
const int kWebMIdContentCompression = 0x5034;
const int kWebMIdContentCompSettings = 0x4255;
const int kWebMIdContentEncAESSettings = 0x47E7;
const int kWebMIdContentEncAlgo = 0x47E1;
const int kWebMIdContentEncKeyID = 0x47E2;
const int kWebMIdContentEncoding = 0x6240;
const int kWebMIdContentEncodingOrder = 0x5031;
const int kWebMIdContentEncodings = 0x6D80;
const int kWebMIdContentEncodingScope = 0x5032;
const int kWebMIdContentEncodingType = 0x5033;
const int kWebMIdContentEncryption = 0x5035;
const int kWebMIdContentSigAlgo = 0x47E5;
const int kWebMIdContentSigHashAlgo = 0x47E6;
const int kWebMIdContentSigKeyID = 0x47E4;
const int kWebMIdContentSignature = 0x47E3;
const int kWebMIdCRC32 = 0xBF;
const int kWebMIdCueBlockNumber = 0x5378;
const int kWebMIdCueClusterPosition = 0xF1;
const int kWebMIdCueCodecState = 0xEA;
const int kWebMIdCuePoint = 0xBB;
const int kWebMIdCueReference = 0xDB;
const int kWebMIdCueRefTime = 0x96;
const int kWebMIdCues = 0x1C53BB6B;
const int kWebMIdCueTime = 0xB3;
const int kWebMIdCueTrack = 0xF7;
const int kWebMIdCueTrackPositions = 0xB7;
const int kWebMIdDateUTC = 0x4461;
const int kWebMIdDefaultDuration = 0x23E383;
const int kWebMIdDisplayHeight = 0x54BA;
const int kWebMIdDisplayUnit = 0x54B2;
const int kWebMIdDisplayWidth = 0x54B0;
const int kWebMIdDocType = 0x4282;
const int kWebMIdDocTypeReadVersion = 0x4285;
const int kWebMIdDocTypeVersion = 0x4287;
const int kWebMIdDuration = 0x4489;
const int kWebMIdEBMLHeader = 0x1A45DFA3;
const int kWebMIdEBMLMaxIDLength = 0x42F2;
const int kWebMIdEBMLMaxSizeLength = 0x42F3;
const int kWebMIdEBMLReadVersion = 0x42F7;
const int kWebMIdEBMLVersion = 0x4286;
const int kWebMIdEditionEntry = 0x45B9;
const int kWebMIdEditionFlagDefault = 0x45DB;
const int kWebMIdEditionFlagHidden = 0x45BD;
const int kWebMIdEditionFlagOrdered = 0x45DD;
const int kWebMIdEditionUID = 0x45BC;
const int kWebMIdFileData = 0x465C;
const int kWebMIdFileDescription = 0x467E;
const int kWebMIdFileMimeType = 0x4660;
const int kWebMIdFileName = 0x466E;
const int kWebMIdFileUID = 0x46AE;
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 kWebMIdJoinBlocks = 0xE9;
const int kWebMIdLaceNumber = 0xCC;
const int kWebMIdLanguage = 0x22B59C;
const int kWebMIdMaxBlockAdditionId = 0x55EE;
const int kWebMIdMaxCache = 0x6DF8;
const int kWebMIdMinCache = 0x6DE7;
const int kWebMIdMuxingApp = 0x4D80;
const int kWebMIdName = 0x536E;
const int kWebMIdNextFilename = 0x3E83BB;
const int kWebMIdNextUID = 0x3EB923;
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 kWebMIdPosition = 0xA7;
const int kWebMIdPrevFilename = 0x3C83AB;
const int kWebMIdPrevSize = 0xAB;
const int kWebMIdPrevUID = 0x3CB923;
const int kWebMIdReferenceBlock = 0xFB;
const int kWebMIdReferencePriority = 0xFA;
const int kWebMIdSamplingFrequency = 0xB5;
const int kWebMIdSeek = 0x4DBB;
const int kWebMIdSeekHead = 0x114D9B74;
const int kWebMIdSeekID = 0x53AB;
const int kWebMIdSeekPosition = 0x53AC;
const int kWebMIdSegment = 0x18538067;
const int kWebMIdSegmentFamily = 0x4444;
const int kWebMIdSegmentFilename = 0x7384;
const int kWebMIdSegmentUID = 0x73A4;
const int kWebMIdSilentTrackNumber = 0x58D7;
const int kWebMIdSilentTracks = 0x5854;
const int kWebMIdSimpleBlock = 0xA3;
const int kWebMIdSimpleTag = 0x67C8;
const int kWebMIdSlices = 0x8E;
const int kWebMIdStereoMode = 0x53B8;
const int kWebMIdTag = 0x7373;
const int kWebMIdTagAttachmentUID = 0x63C6;
const int kWebMIdTagBinary = 0x4485;
const int kWebMIdTagChapterUID = 0x63C4;
const int kWebMIdTagDefault = 0x4484;
const int kWebMIdTagEditionUID = 0x63C9;
const int kWebMIdTagLanguage = 0x447A;
const int kWebMIdTagName = 0x45A3;
const int kWebMIdTags = 0x1254C367;
const int kWebMIdTagString = 0x4487;
const int kWebMIdTagTrackUID = 0x63C5;
const int kWebMIdTargets = 0x63C0;
const int kWebMIdTargetType = 0x63CA;
const int kWebMIdTargetTypeValue = 0x68CA;
const int kWebMIdTimecode = 0xE7;
const int kWebMIdTimecodeScale = 0x2AD7B1;
const int kWebMIdTimeSlice = 0xE8;
const int kWebMIdTitle = 0x7BA9;
const int kWebMIdTrackCombinePlanes = 0xE3;
const int kWebMIdTrackEntry = 0xAE;
const int kWebMIdTrackJoinUID = 0xED;
const int kWebMIdTrackNumber = 0xD7;
const int kWebMIdTrackOperation = 0xE2;
const int kWebMIdTrackOverlay = 0x6FAB;
const int kWebMIdTrackPlane = 0xE4;
const int kWebMIdTrackPlaneType = 0xE6;
const int kWebMIdTrackPlaneUID = 0xE5;
const int kWebMIdTracks = 0x1654AE6B;
const int kWebMIdTrackTimecodeScale = 0x23314F;
const int kWebMIdTrackTranslate = 0x6624;
const int kWebMIdTrackTranslateCodec = 0x66BF;
const int kWebMIdTrackTranslateEditionUID = 0x66FC;
const int kWebMIdTrackTranslateTrackID = 0x66A5;
const int kWebMIdTrackType = 0x83;
const int kWebMIdTrackUID = 0x73C5;
const int kWebMIdVideo = 0xE0;
const int kWebMIdVoid = 0xEC;
const int kWebMIdWritingApp = 0x5741;
const int64 kWebMReservedId = 0x1FFFFFFF;
const int64 kWebMUnknownSize = GG_LONGLONG(0x00FFFFFFFFFFFFFF);
const uint8 kWebMFlagKeyframe = 0x80;
// Current encrypted WebM request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
const uint8 kWebMFlagEncryptedFrame = 0x1;
const int kWebMIvSize = 8;
const int kWebMSignalByteSize = 1;
// Current specification for WebVTT embedded in WebM
// http://wiki.webmproject.org/webm-metadata/temporal-metadata/webvtt-in-webm
const int kWebMTrackTypeVideo = 1;
const int kWebMTrackTypeAudio = 2;
const int kWebMTrackTypeSubtitlesOrCaptions = 0x11;
const int kWebMTrackTypeDescriptionsOrMetadata = 0x21;
MEDIA_EXPORT extern const char kWebMCodecSubtitles[];
MEDIA_EXPORT extern const char kWebMCodecCaptions[];
MEDIA_EXPORT extern const char kWebMCodecDescriptions[];
MEDIA_EXPORT extern const char kWebMCodecMetadata[];
} // namespace media
#endif // MEDIA_WEBM_WEBM_CONSTANTS_H_

View File

@ -1,28 +0,0 @@
// Copyright (c) 2012 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 "media/webm/webm_content_encodings.h"
namespace media {
ContentEncoding::ContentEncoding()
: order_(kOrderInvalid),
scope_(kScopeInvalid),
type_(kTypeInvalid),
encryption_algo_(kEncAlgoInvalid),
cipher_mode_(kCipherModeInvalid) {
}
ContentEncoding::~ContentEncoding() {}
void ContentEncoding::SetEncryptionKeyId(const uint8* encryption_key_id,
int size) {
DCHECK(encryption_key_id);
DCHECK_GT(size, 0);
encryption_key_id_.assign(reinterpret_cast<const char*>(encryption_key_id),
size);
}
} // namespace media

View File

@ -1,88 +0,0 @@
// Copyright (c) 2012 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_CONTENT_ENCODINGS_H_
#define MEDIA_WEBM_WEBM_CONTENT_ENCODINGS_H_
#include <string>
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
namespace media {
class MEDIA_EXPORT ContentEncoding {
public:
// The following enum definitions are based on the ContentEncoding element
// specified in the Matroska spec.
static const int kOrderInvalid = -1;
enum Scope {
kScopeInvalid = 0,
kScopeAllFrameContents = 1,
kScopeTrackPrivateData = 2,
kScopeNextContentEncodingData = 4,
kScopeMax = 7,
};
enum Type {
kTypeInvalid = -1,
kTypeCompression = 0,
kTypeEncryption = 1,
};
enum EncryptionAlgo {
kEncAlgoInvalid = -1,
kEncAlgoNotEncrypted = 0,
kEncAlgoDes = 1,
kEncAlgo3des = 2,
kEncAlgoTwofish = 3,
kEncAlgoBlowfish = 4,
kEncAlgoAes = 5,
};
enum CipherMode {
kCipherModeInvalid = 0,
kCipherModeCtr = 1,
};
ContentEncoding();
~ContentEncoding();
int64 order() const { return order_; }
void set_order(int64 order) { order_ = order; }
Scope scope() const { return scope_; }
void set_scope(Scope scope) { scope_ = scope; }
Type type() const { return type_; }
void set_type(Type type) { type_ = type; }
EncryptionAlgo encryption_algo() const { return encryption_algo_; }
void set_encryption_algo(EncryptionAlgo encryption_algo) {
encryption_algo_ = encryption_algo;
}
const std::string& encryption_key_id() const { return encryption_key_id_; }
void SetEncryptionKeyId(const uint8* encryption_key_id, int size);
CipherMode cipher_mode() const { return cipher_mode_; }
void set_cipher_mode(CipherMode mode) { cipher_mode_ = mode; }
private:
int64 order_;
Scope scope_;
Type type_;
EncryptionAlgo encryption_algo_;
std::string encryption_key_id_;
CipherMode cipher_mode_;
DISALLOW_COPY_AND_ASSIGN(ContentEncoding);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_CONTENT_ENCODINGS_H_

View File

@ -1,265 +0,0 @@
// Copyright (c) 2012 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_content_encodings_client.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "media/webm/webm_constants.h"
namespace media {
WebMContentEncodingsClient::WebMContentEncodingsClient(const LogCB& log_cb)
: log_cb_(log_cb),
content_encryption_encountered_(false),
content_encodings_ready_(false) {
}
WebMContentEncodingsClient::~WebMContentEncodingsClient() {
STLDeleteElements(&content_encodings_);
}
const ContentEncodings& WebMContentEncodingsClient::content_encodings() const {
DCHECK(content_encodings_ready_);
return content_encodings_;
}
WebMParserClient* WebMContentEncodingsClient::OnListStart(int id) {
if (id == kWebMIdContentEncodings) {
DCHECK(!cur_content_encoding_.get());
DCHECK(!content_encryption_encountered_);
STLDeleteElements(&content_encodings_);
content_encodings_ready_ = false;
return this;
}
if (id == kWebMIdContentEncoding) {
DCHECK(!cur_content_encoding_.get());
DCHECK(!content_encryption_encountered_);
cur_content_encoding_.reset(new ContentEncoding());
return this;
}
if (id == kWebMIdContentEncryption) {
DCHECK(cur_content_encoding_.get());
if (content_encryption_encountered_) {
MEDIA_LOG(log_cb_) << "Unexpected multiple ContentEncryption.";
return NULL;
}
content_encryption_encountered_ = true;
return this;
}
if (id == kWebMIdContentEncAESSettings) {
DCHECK(cur_content_encoding_.get());
return this;
}
// This should not happen if WebMListParser is working properly.
DCHECK(false);
return NULL;
}
// Mandatory occurrence restriction is checked in this function. Multiple
// occurrence restriction is checked in OnUInt and OnBinary.
bool WebMContentEncodingsClient::OnListEnd(int id) {
if (id == kWebMIdContentEncodings) {
// ContentEncoding element is mandatory. Check this!
if (content_encodings_.empty()) {
MEDIA_LOG(log_cb_) << "Missing ContentEncoding.";
return false;
}
content_encodings_ready_ = true;
return true;
}
if (id == kWebMIdContentEncoding) {
DCHECK(cur_content_encoding_.get());
//
// Specify default values to missing mandatory elements.
//
if (cur_content_encoding_->order() == ContentEncoding::kOrderInvalid) {
// Default value of encoding order is 0, which should only be used on the
// first ContentEncoding.
if (!content_encodings_.empty()) {
MEDIA_LOG(log_cb_) << "Missing ContentEncodingOrder.";
return false;
}
cur_content_encoding_->set_order(0);
}
if (cur_content_encoding_->scope() == ContentEncoding::kScopeInvalid)
cur_content_encoding_->set_scope(ContentEncoding::kScopeAllFrameContents);
if (cur_content_encoding_->type() == ContentEncoding::kTypeInvalid)
cur_content_encoding_->set_type(ContentEncoding::kTypeCompression);
// Check for elements valid in spec but not supported for now.
if (cur_content_encoding_->type() == ContentEncoding::kTypeCompression) {
MEDIA_LOG(log_cb_) << "ContentCompression not supported.";
return false;
}
// Enforce mandatory elements without default values.
DCHECK(cur_content_encoding_->type() == ContentEncoding::kTypeEncryption);
if (!content_encryption_encountered_) {
MEDIA_LOG(log_cb_) << "ContentEncodingType is encryption but"
<< " ContentEncryption is missing.";
return false;
}
content_encodings_.push_back(cur_content_encoding_.release());
content_encryption_encountered_ = false;
return true;
}
if (id == kWebMIdContentEncryption) {
DCHECK(cur_content_encoding_.get());
// Specify default value for elements that are not present.
if (cur_content_encoding_->encryption_algo() ==
ContentEncoding::kEncAlgoInvalid) {
cur_content_encoding_->set_encryption_algo(
ContentEncoding::kEncAlgoNotEncrypted);
}
return true;
}
if (id == kWebMIdContentEncAESSettings) {
if (cur_content_encoding_->cipher_mode() ==
ContentEncoding::kCipherModeInvalid)
cur_content_encoding_->set_cipher_mode(ContentEncoding::kCipherModeCtr);
return true;
}
// This should not happen if WebMListParser is working properly.
DCHECK(false);
return false;
}
// Multiple occurrence restriction and range are checked in this function.
// Mandatory occurrence restriction is checked in OnListEnd.
bool WebMContentEncodingsClient::OnUInt(int id, int64 val) {
DCHECK(cur_content_encoding_.get());
if (id == kWebMIdContentEncodingOrder) {
if (cur_content_encoding_->order() != ContentEncoding::kOrderInvalid) {
MEDIA_LOG(log_cb_) << "Unexpected multiple ContentEncodingOrder.";
return false;
}
if (val != static_cast<int64>(content_encodings_.size())) {
// According to the spec, encoding order starts with 0 and counts upwards.
MEDIA_LOG(log_cb_) << "Unexpected ContentEncodingOrder.";
return false;
}
cur_content_encoding_->set_order(val);
return true;
}
if (id == kWebMIdContentEncodingScope) {
if (cur_content_encoding_->scope() != ContentEncoding::kScopeInvalid) {
MEDIA_LOG(log_cb_) << "Unexpected multiple ContentEncodingScope.";
return false;
}
if (val == ContentEncoding::kScopeInvalid ||
val > ContentEncoding::kScopeMax) {
MEDIA_LOG(log_cb_) << "Unexpected ContentEncodingScope.";
return false;
}
if (val & ContentEncoding::kScopeNextContentEncodingData) {
MEDIA_LOG(log_cb_) << "Encoded next ContentEncoding is not supported.";
return false;
}
cur_content_encoding_->set_scope(static_cast<ContentEncoding::Scope>(val));
return true;
}
if (id == kWebMIdContentEncodingType) {
if (cur_content_encoding_->type() != ContentEncoding::kTypeInvalid) {
MEDIA_LOG(log_cb_) << "Unexpected multiple ContentEncodingType.";
return false;
}
if (val == ContentEncoding::kTypeCompression) {
MEDIA_LOG(log_cb_) << "ContentCompression not supported.";
return false;
}
if (val != ContentEncoding::kTypeEncryption) {
MEDIA_LOG(log_cb_) << "Unexpected ContentEncodingType " << val << ".";
return false;
}
cur_content_encoding_->set_type(static_cast<ContentEncoding::Type>(val));
return true;
}
if (id == kWebMIdContentEncAlgo) {
if (cur_content_encoding_->encryption_algo() !=
ContentEncoding::kEncAlgoInvalid) {
MEDIA_LOG(log_cb_) << "Unexpected multiple ContentEncAlgo.";
return false;
}
if (val < ContentEncoding::kEncAlgoNotEncrypted ||
val > ContentEncoding::kEncAlgoAes) {
MEDIA_LOG(log_cb_) << "Unexpected ContentEncAlgo " << val << ".";
return false;
}
cur_content_encoding_->set_encryption_algo(
static_cast<ContentEncoding::EncryptionAlgo>(val));
return true;
}
if (id == kWebMIdAESSettingsCipherMode) {
if (cur_content_encoding_->cipher_mode() !=
ContentEncoding::kCipherModeInvalid) {
MEDIA_LOG(log_cb_) << "Unexpected multiple AESSettingsCipherMode.";
return false;
}
if (val != ContentEncoding::kCipherModeCtr) {
MEDIA_LOG(log_cb_) << "Unexpected AESSettingsCipherMode " << val << ".";
return false;
}
cur_content_encoding_->set_cipher_mode(
static_cast<ContentEncoding::CipherMode>(val));
return true;
}
// This should not happen if WebMListParser is working properly.
DCHECK(false);
return false;
}
// Multiple occurrence restriction is checked in this function. Mandatory
// restriction is checked in OnListEnd.
bool WebMContentEncodingsClient::OnBinary(int id, const uint8* data, int size) {
DCHECK(cur_content_encoding_.get());
DCHECK(data);
DCHECK_GT(size, 0);
if (id == kWebMIdContentEncKeyID) {
if (!cur_content_encoding_->encryption_key_id().empty()) {
MEDIA_LOG(log_cb_) << "Unexpected multiple ContentEncKeyID";
return false;
}
cur_content_encoding_->SetEncryptionKeyId(data, size);
return true;
}
// This should not happen if WebMListParser is working properly.
DCHECK(false);
return false;
}
} // namespace media

View File

@ -1,50 +0,0 @@
// Copyright (c) 2012 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_CONTENT_ENCODINGS_CLIENT_H_
#define MEDIA_WEBM_WEBM_CONTENT_ENCODINGS_CLIENT_H_
#include <vector>
#include "base/callback.h"
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
#include "media/base/media_log.h"
#include "media/webm/webm_content_encodings.h"
#include "media/webm/webm_parser.h"
namespace media {
typedef std::vector<ContentEncoding*> ContentEncodings;
// Parser for WebM ContentEncodings element.
class MEDIA_EXPORT WebMContentEncodingsClient : public WebMParserClient {
public:
explicit WebMContentEncodingsClient(const LogCB& log_cb);
virtual ~WebMContentEncodingsClient();
const ContentEncodings& content_encodings() const;
// WebMParserClient methods
virtual WebMParserClient* OnListStart(int id) OVERRIDE;
virtual bool OnListEnd(int id) OVERRIDE;
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE;
private:
LogCB log_cb_;
scoped_ptr<ContentEncoding> cur_content_encoding_;
bool content_encryption_encountered_;
ContentEncodings content_encodings_;
// |content_encodings_| is ready. For debugging purpose.
bool content_encodings_ready_;
DISALLOW_COPY_AND_ASSIGN(WebMContentEncodingsClient);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_CONTENT_ENCODINGS_CLIENT_H_

View File

@ -1,238 +0,0 @@
// Copyright (c) 2012 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/bind.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_content_encodings_client.h"
#include "media/webm/webm_parser.h"
#include "testing/gtest/include/gtest/gtest.h"
namespace media {
class WebMContentEncodingsClientTest : public testing::Test {
public:
WebMContentEncodingsClientTest()
: client_(LogCB()),
parser_(kWebMIdContentEncodings, &client_) {}
void ParseAndExpectToFail(const uint8* buf, int size) {
int result = parser_.Parse(buf, size);
EXPECT_EQ(-1, result);
}
protected:
WebMContentEncodingsClient client_;
WebMListParser parser_;
};
TEST_F(WebMContentEncodingsClientTest, EmptyContentEncodings) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x80, // ContentEncodings (size = 0)
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
TEST_F(WebMContentEncodingsClientTest, EmptyContentEncoding) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x83, // ContentEncodings (size = 3)
0x63, 0x40, 0x80, // ContentEncoding (size = 0)
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
TEST_F(WebMContentEncodingsClientTest, SingleContentEncoding) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0xA1, // ContentEncodings (size = 33)
0x62, 0x40, 0x9e, // ContentEncoding (size = 30)
0x50, 0x31, 0x81, 0x00, // ContentEncodingOrder (size = 1)
0x50, 0x32, 0x81, 0x01, // ContentEncodingScope (size = 1)
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x8F, // ContentEncryption (size = 15)
0x47, 0xE1, 0x81, 0x05, // ContentEncAlgo (size = 1)
0x47, 0xE2, 0x88, // ContentEncKeyID (size = 8)
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
};
int size = sizeof(kContentEncodings);
int result = parser_.Parse(kContentEncodings, size);
ASSERT_EQ(size, result);
const ContentEncodings& content_encodings = client_.content_encodings();
ASSERT_EQ(1u, content_encodings.size());
ASSERT_TRUE(content_encodings[0]);
EXPECT_EQ(0, content_encodings[0]->order());
EXPECT_EQ(ContentEncoding::kScopeAllFrameContents,
content_encodings[0]->scope());
EXPECT_EQ(ContentEncoding::kTypeEncryption, content_encodings[0]->type());
EXPECT_EQ(ContentEncoding::kEncAlgoAes,
content_encodings[0]->encryption_algo());
EXPECT_EQ(8u, content_encodings[0]->encryption_key_id().size());
}
TEST_F(WebMContentEncodingsClientTest, MultipleContentEncoding) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0xC2, // ContentEncodings (size = 66)
0x62, 0x40, 0x9e, // ContentEncoding (size = 30)
0x50, 0x31, 0x81, 0x00, // ContentEncodingOrder (size = 1)
0x50, 0x32, 0x81, 0x03, // ContentEncodingScope (size = 1)
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x8F, // ContentEncryption (size = 15)
0x47, 0xE1, 0x81, 0x05, // ContentEncAlgo (size = 1)
0x47, 0xE2, 0x88, // ContentEncKeyID (size = 8)
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
0x62, 0x40, 0x9e, // ContentEncoding (size = 30)
0x50, 0x31, 0x81, 0x01, // ContentEncodingOrder (size = 1)
0x50, 0x32, 0x81, 0x03, // ContentEncodingScope (size = 1)
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x8F, // ContentEncryption (size = 15)
0x47, 0xE1, 0x81, 0x01, // ContentEncAlgo (size = 1)
0x47, 0xE2, 0x88, // ContentEncKeyID (size = 8)
0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB, 0xBB,
};
int size = sizeof(kContentEncodings);
int result = parser_.Parse(kContentEncodings, size);
ASSERT_EQ(size, result);
const ContentEncodings& content_encodings = client_.content_encodings();
ASSERT_EQ(2u, content_encodings.size());
for (int i = 0; i < 2; ++i) {
ASSERT_TRUE(content_encodings[i]);
EXPECT_EQ(i, content_encodings[i]->order());
EXPECT_EQ(ContentEncoding::kScopeAllFrameContents |
ContentEncoding::kScopeTrackPrivateData,
content_encodings[i]->scope());
EXPECT_EQ(ContentEncoding::kTypeEncryption, content_encodings[i]->type());
EXPECT_EQ(!i ? ContentEncoding::kEncAlgoAes : ContentEncoding::kEncAlgoDes,
content_encodings[i]->encryption_algo());
EXPECT_EQ(8u, content_encodings[i]->encryption_key_id().size());
}
}
TEST_F(WebMContentEncodingsClientTest, DefaultValues) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x8A, // ContentEncodings (size = 10)
0x62, 0x40, 0x87, // ContentEncoding (size = 7)
// ContentEncodingOrder missing
// ContentEncodingScope missing
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x80, // ContentEncryption (size = 0)
// ContentEncAlgo missing
};
int size = sizeof(kContentEncodings);
int result = parser_.Parse(kContentEncodings, size);
ASSERT_EQ(size, result);
const ContentEncodings& content_encodings = client_.content_encodings();
ASSERT_EQ(1u, content_encodings.size());
ASSERT_TRUE(content_encodings[0]);
EXPECT_EQ(0, content_encodings[0]->order());
EXPECT_EQ(ContentEncoding::kScopeAllFrameContents,
content_encodings[0]->scope());
EXPECT_EQ(ContentEncoding::kTypeEncryption, content_encodings[0]->type());
EXPECT_EQ(ContentEncoding::kEncAlgoNotEncrypted,
content_encodings[0]->encryption_algo());
EXPECT_TRUE(content_encodings[0]->encryption_key_id().empty());
}
TEST_F(WebMContentEncodingsClientTest, ContentEncodingsClientReuse) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0xA1, // ContentEncodings (size = 33)
0x62, 0x40, 0x9e, // ContentEncoding (size = 30)
0x50, 0x31, 0x81, 0x00, // ContentEncodingOrder (size = 1)
0x50, 0x32, 0x81, 0x01, // ContentEncodingScope (size = 1)
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x8F, // ContentEncryption (size = 15)
0x47, 0xE1, 0x81, 0x05, // ContentEncAlgo (size = 1)
0x47, 0xE2, 0x88, // ContentEncKeyID (size = 8)
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
};
int size = sizeof(kContentEncodings);
// Parse for the first time.
int result = parser_.Parse(kContentEncodings, size);
ASSERT_EQ(size, result);
// Parse again.
parser_.Reset();
result = parser_.Parse(kContentEncodings, size);
ASSERT_EQ(size, result);
const ContentEncodings& content_encodings = client_.content_encodings();
ASSERT_EQ(1u, content_encodings.size());
ASSERT_TRUE(content_encodings[0]);
EXPECT_EQ(0, content_encodings[0]->order());
EXPECT_EQ(ContentEncoding::kScopeAllFrameContents,
content_encodings[0]->scope());
EXPECT_EQ(ContentEncoding::kTypeEncryption, content_encodings[0]->type());
EXPECT_EQ(ContentEncoding::kEncAlgoAes,
content_encodings[0]->encryption_algo());
EXPECT_EQ(8u, content_encodings[0]->encryption_key_id().size());
}
TEST_F(WebMContentEncodingsClientTest, InvalidContentEncodingOrder) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x8E, // ContentEncodings (size = 14)
0x62, 0x40, 0x8B, // ContentEncoding (size = 11)
0x50, 0x31, 0x81, 0xEE, // ContentEncodingOrder (size = 1), invalid
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x80, // ContentEncryption (size = 0)
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
TEST_F(WebMContentEncodingsClientTest, InvalidContentEncodingScope) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x8E, // ContentEncodings (size = 14)
0x62, 0x40, 0x8B, // ContentEncoding (size = 11)
0x50, 0x32, 0x81, 0xEE, // ContentEncodingScope (size = 1), invalid
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x80, // ContentEncryption (size = 0)
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
TEST_F(WebMContentEncodingsClientTest, InvalidContentEncodingType) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x8E, // ContentEncodings (size = 14)
0x62, 0x40, 0x8B, // ContentEncoding (size = 11)
0x50, 0x33, 0x81, 0x00, // ContentEncodingType (size = 1), invalid
0x50, 0x35, 0x80, // ContentEncryption (size = 0)
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
// ContentEncodingType is encryption but no ContentEncryption present.
TEST_F(WebMContentEncodingsClientTest, MissingContentEncryption) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x87, // ContentEncodings (size = 7)
0x62, 0x40, 0x84, // ContentEncoding (size = 4)
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
// ContentEncryption missing
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
TEST_F(WebMContentEncodingsClientTest, InvalidContentEncAlgo) {
const uint8 kContentEncodings[] = {
0x6D, 0x80, 0x99, // ContentEncodings (size = 25)
0x62, 0x40, 0x96, // ContentEncoding (size = 22)
0x50, 0x33, 0x81, 0x01, // ContentEncodingType (size = 1)
0x50, 0x35, 0x8F, // ContentEncryption (size = 15)
0x47, 0xE1, 0x81, 0xEE, // ContentEncAlgo (size = 1), invalid
0x47, 0xE2, 0x88, // ContentEncKeyID (size = 8)
0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA, 0xAA,
};
int size = sizeof(kContentEncodings);
ParseAndExpectToFail(kContentEncodings, size);
}
} // namespace media

View File

@ -1,60 +0,0 @@
// Copyright (c) 2013 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_crypto_helpers.h"
#include "base/logging.h"
#include "base/sys_byteorder.h"
#include "media/base/decrypt_config.h"
#include "media/webm/webm_constants.h"
namespace media {
namespace {
// Generates a 16 byte CTR counter block. The CTR counter block format is a
// CTR IV appended with a CTR block counter. |iv| is an 8 byte CTR IV.
// |iv_size| is the size of |iv| in btyes. Returns a string of
// kDecryptionKeySize bytes.
std::string GenerateWebMCounterBlock(const uint8* iv, int iv_size) {
std::string counter_block(reinterpret_cast<const char*>(iv), iv_size);
counter_block.append(DecryptConfig::kDecryptionKeySize - iv_size, 0);
return counter_block;
}
} // namespace anonymous
scoped_ptr<DecryptConfig> WebMCreateDecryptConfig(
const uint8* data, int data_size,
const uint8* key_id, int key_id_size) {
if (data_size < kWebMSignalByteSize) {
DVLOG(1) << "Got a block from an encrypted stream with no data.";
return scoped_ptr<DecryptConfig>();
}
uint8 signal_byte = data[0];
int frame_offset = sizeof(signal_byte);
// Setting the DecryptConfig object of the buffer while leaving the
// initialization vector empty will tell the decryptor that the frame is
// unencrypted.
std::string counter_block;
if (signal_byte & kWebMFlagEncryptedFrame) {
if (data_size < kWebMSignalByteSize + kWebMIvSize) {
DVLOG(1) << "Got an encrypted block with not enough data " << data_size;
return scoped_ptr<DecryptConfig>();
}
counter_block = GenerateWebMCounterBlock(data + frame_offset, kWebMIvSize);
frame_offset += kWebMIvSize;
}
scoped_ptr<DecryptConfig> config(new DecryptConfig(
std::string(reinterpret_cast<const char*>(key_id), key_id_size),
counter_block,
frame_offset,
std::vector<SubsampleEntry>()));
return config.Pass();
}
} // namespace media

View File

@ -1,32 +0,0 @@
// Copyright (c) 2013 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_CRYPTO_HELPERS_H_
#define MEDIA_WEBM_WEBM_CRYPTO_HELPERS_H_
#include "base/basictypes.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/decoder_buffer.h"
namespace media {
// TODO(xhwang): Figure out the init data type appropriately once it's spec'ed.
// See https://www.w3.org/Bugs/Public/show_bug.cgi?id=19096 for more
// information.
const char kWebMEncryptInitDataType[] = "video/webm";
// Returns an initialized DecryptConfig, which can be sent to the Decryptor if
// the stream has potentially encrypted frames. Every encrypted Block has a
// signal byte, and if the frame is encrypted, an initialization vector
// prepended to the frame. Leaving the IV empty will tell the decryptor that the
// frame is unencrypted. Returns NULL if |data| is invalid. Current encrypted
// WebM request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
scoped_ptr<DecryptConfig> WebMCreateDecryptConfig(
const uint8* data, int data_size,
const uint8* key_id, int key_id_size);
} // namespace media
#endif // MEDIA_WEBM_WEBM_CRYPT_HELPERS_H_

View File

@ -1,84 +0,0 @@
// 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 {
// Default timecode scale if the TimecodeScale element is
// not specified in the INFO element.
static const int kWebMDefaultTimecodeScale = 1000000;
WebMInfoParser::WebMInfoParser()
: timecode_scale_(-1),
duration_(-1) {
}
WebMInfoParser::~WebMInfoParser() {}
int WebMInfoParser::Parse(const uint8* buf, int size) {
timecode_scale_ = -1;
duration_ = -1;
WebMListParser parser(kWebMIdInfo, this);
int result = parser.Parse(buf, size);
if (result <= 0)
return result;
// For now we do all or nothing parsing.
return parser.IsParsingComplete() ? result : 0;
}
WebMParserClient* WebMInfoParser::OnListStart(int id) { return this; }
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) {
DVLOG(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) {
DVLOG(1) << "Unexpected float for id" << std::hex << id;
return false;
}
if (duration_ != -1) {
DVLOG(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;
}
} // namespace media

View File

@ -1,47 +0,0 @@
// Copyright (c) 2012 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 "base/compiler_specific.h"
#include "media/base/media_export.h"
#include "media/webm/webm_parser.h"
namespace media {
// Parser for WebM Info element.
class MEDIA_EXPORT WebMInfoParser : public WebMParserClient {
public:
WebMInfoParser();
virtual ~WebMInfoParser();
// Parses a WebM Info element in |buf|.
//
// Returns -1 if the parse fails.
// Returns 0 if more data is needed.
// Returns the number of bytes parsed on success.
int Parse(const uint8* buf, int size);
int64 timecode_scale() const { return timecode_scale_; }
double duration() const { return duration_; }
private:
// WebMParserClient methods
virtual WebMParserClient* OnListStart(int id) OVERRIDE;
virtual bool OnListEnd(int id) OVERRIDE;
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnFloat(int id, double val) OVERRIDE;
virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE;
virtual bool OnString(int id, const std::string& str) OVERRIDE;
int64 timecode_scale_;
double duration_;
DISALLOW_COPY_AND_ASSIGN(WebMInfoParser);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_INFO_PARSER_H_

View File

@ -1,943 +0,0 @@
// Copyright (c) 2012 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
// This file contains code for encrypted WebM. Current WebM
// encrypted request for comments specification is here
// http://wiki.webmproject.org/encryption/webm-encryption-rfc
#include <iomanip>
#include "base/logging.h"
#include "media/webm/webm_constants.h"
namespace media {
enum ElementType {
UNKNOWN,
LIST, // Referred to as Master Element in the Matroska spec.
UINT,
FLOAT,
BINARY,
STRING,
SKIP,
};
struct ElementIdInfo {
ElementType type_;
int id_;
};
struct ListElementInfo {
int id_;
int level_;
const ElementIdInfo* id_info_;
int id_info_count_;
};
// 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 kEBMLHeaderIds[] = {
{UINT, kWebMIdEBMLVersion},
{UINT, kWebMIdEBMLReadVersion},
{UINT, kWebMIdEBMLMaxIDLength},
{UINT, kWebMIdEBMLMaxSizeLength},
{STRING, kWebMIdDocType},
{UINT, kWebMIdDocTypeVersion},
{UINT, kWebMIdDocTypeReadVersion},
};
static const ElementIdInfo kSegmentIds[] = {
{LIST, kWebMIdSeekHead},
{LIST, kWebMIdInfo},
{LIST, kWebMIdCluster},
{LIST, kWebMIdTracks},
{LIST, kWebMIdCues},
{LIST, kWebMIdAttachments},
{LIST, kWebMIdChapters},
{LIST, kWebMIdTags},
};
static const ElementIdInfo kSeekHeadIds[] = {
{LIST, kWebMIdSeek},
};
static const ElementIdInfo kSeekIds[] = {
{BINARY, kWebMIdSeekID},
{UINT, kWebMIdSeekPosition},
};
static const ElementIdInfo kInfoIds[] = {
{BINARY, kWebMIdSegmentUID},
{STRING, kWebMIdSegmentFilename},
{BINARY, kWebMIdPrevUID},
{STRING, kWebMIdPrevFilename},
{BINARY, kWebMIdNextUID},
{STRING, kWebMIdNextFilename},
{BINARY, kWebMIdSegmentFamily},
{LIST, kWebMIdChapterTranslate},
{UINT, kWebMIdTimecodeScale},
{FLOAT, kWebMIdDuration},
{BINARY, kWebMIdDateUTC},
{STRING, kWebMIdTitle},
{STRING, kWebMIdMuxingApp},
{STRING, kWebMIdWritingApp},
};
static const ElementIdInfo kChapterTranslateIds[] = {
{UINT, kWebMIdChapterTranslateEditionUID},
{UINT, kWebMIdChapterTranslateCodec},
{BINARY, kWebMIdChapterTranslateID},
};
static const ElementIdInfo kClusterIds[] = {
{BINARY, kWebMIdSimpleBlock},
{UINT, kWebMIdTimecode},
{LIST, kWebMIdSilentTracks},
{UINT, kWebMIdPosition},
{UINT, kWebMIdPrevSize},
{LIST, kWebMIdBlockGroup},
};
static const ElementIdInfo kSilentTracksIds[] = {
{UINT, kWebMIdSilentTrackNumber},
};
static const ElementIdInfo kBlockGroupIds[] = {
{BINARY, kWebMIdBlock},
{LIST, kWebMIdBlockAdditions},
{UINT, kWebMIdBlockDuration},
{UINT, kWebMIdReferencePriority},
{BINARY, kWebMIdReferenceBlock},
{BINARY, kWebMIdCodecState},
{LIST, kWebMIdSlices},
};
static const ElementIdInfo kBlockAdditionsIds[] = {
{LIST, kWebMIdBlockMore},
};
static const ElementIdInfo kBlockMoreIds[] = {
{UINT, kWebMIdBlockAddID},
{BINARY, kWebMIdBlockAdditional},
};
static const ElementIdInfo kSlicesIds[] = {
{LIST, kWebMIdTimeSlice},
};
static const ElementIdInfo kTimeSliceIds[] = {
{UINT, kWebMIdLaceNumber},
};
static const ElementIdInfo kTracksIds[] = {
{LIST, kWebMIdTrackEntry},
};
static const ElementIdInfo kTrackEntryIds[] = {
{UINT, kWebMIdTrackNumber},
{UINT, kWebMIdTrackUID},
{UINT, kWebMIdTrackType},
{UINT, kWebMIdFlagEnabled},
{UINT, kWebMIdFlagDefault},
{UINT, kWebMIdFlagForced},
{UINT, kWebMIdFlagLacing},
{UINT, kWebMIdMinCache},
{UINT, kWebMIdMaxCache},
{UINT, kWebMIdDefaultDuration},
{FLOAT, kWebMIdTrackTimecodeScale},
{UINT, kWebMIdMaxBlockAdditionId},
{STRING, kWebMIdName},
{STRING, kWebMIdLanguage},
{STRING, kWebMIdCodecID},
{BINARY, kWebMIdCodecPrivate},
{STRING, kWebMIdCodecName},
{UINT, kWebMIdAttachmentLink},
{UINT, kWebMIdCodecDecodeAll},
{UINT, kWebMIdTrackOverlay},
{LIST, kWebMIdTrackTranslate},
{LIST, kWebMIdVideo},
{LIST, kWebMIdAudio},
{LIST, kWebMIdTrackOperation},
{LIST, kWebMIdContentEncodings},
};
static const ElementIdInfo kTrackTranslateIds[] = {
{UINT, kWebMIdTrackTranslateEditionUID},
{UINT, kWebMIdTrackTranslateCodec},
{BINARY, kWebMIdTrackTranslateTrackID},
};
static const ElementIdInfo kVideoIds[] = {
{UINT, kWebMIdFlagInterlaced},
{UINT, kWebMIdStereoMode},
{UINT, kWebMIdAlphaMode},
{UINT, kWebMIdPixelWidth},
{UINT, kWebMIdPixelHeight},
{UINT, kWebMIdPixelCropBottom},
{UINT, kWebMIdPixelCropTop},
{UINT, kWebMIdPixelCropLeft},
{UINT, kWebMIdPixelCropRight},
{UINT, kWebMIdDisplayWidth},
{UINT, kWebMIdDisplayHeight},
{UINT, kWebMIdDisplayUnit},
{UINT, kWebMIdAspectRatioType},
{BINARY, kWebMIdColorSpace},
};
static const ElementIdInfo kAudioIds[] = {
{FLOAT, kWebMIdSamplingFrequency},
{FLOAT, kWebMIdOutputSamplingFrequency},
{UINT, kWebMIdChannels},
{UINT, kWebMIdBitDepth},
};
static const ElementIdInfo kTrackOperationIds[] = {
{LIST, kWebMIdTrackCombinePlanes},
{LIST, kWebMIdJoinBlocks},
};
static const ElementIdInfo kTrackCombinePlanesIds[] = {
{LIST, kWebMIdTrackPlane},
};
static const ElementIdInfo kTrackPlaneIds[] = {
{UINT, kWebMIdTrackPlaneUID},
{UINT, kWebMIdTrackPlaneType},
};
static const ElementIdInfo kJoinBlocksIds[] = {
{UINT, kWebMIdTrackJoinUID},
};
static const ElementIdInfo kContentEncodingsIds[] = {
{LIST, kWebMIdContentEncoding},
};
static const ElementIdInfo kContentEncodingIds[] = {
{UINT, kWebMIdContentEncodingOrder},
{UINT, kWebMIdContentEncodingScope},
{UINT, kWebMIdContentEncodingType},
{LIST, kWebMIdContentCompression},
{LIST, kWebMIdContentEncryption},
};
static const ElementIdInfo kContentCompressionIds[] = {
{UINT, kWebMIdContentCompAlgo},
{BINARY, kWebMIdContentCompSettings},
};
static const ElementIdInfo kContentEncryptionIds[] = {
{LIST, kWebMIdContentEncAESSettings},
{UINT, kWebMIdContentEncAlgo},
{BINARY, kWebMIdContentEncKeyID},
{BINARY, kWebMIdContentSignature},
{BINARY, kWebMIdContentSigKeyID},
{UINT, kWebMIdContentSigAlgo},
{UINT, kWebMIdContentSigHashAlgo},
};
static const ElementIdInfo kContentEncAESSettingsIds[] = {
{UINT, kWebMIdAESSettingsCipherMode},
};
static const ElementIdInfo kCuesIds[] = {
{LIST, kWebMIdCuePoint},
};
static const ElementIdInfo kCuePointIds[] = {
{UINT, kWebMIdCueTime},
{LIST, kWebMIdCueTrackPositions},
};
static const ElementIdInfo kCueTrackPositionsIds[] = {
{UINT, kWebMIdCueTrack},
{UINT, kWebMIdCueClusterPosition},
{UINT, kWebMIdCueBlockNumber},
{UINT, kWebMIdCueCodecState},
{LIST, kWebMIdCueReference},
};
static const ElementIdInfo kCueReferenceIds[] = {
{UINT, kWebMIdCueRefTime},
};
static const ElementIdInfo kAttachmentsIds[] = {
{LIST, kWebMIdAttachedFile},
};
static const ElementIdInfo kAttachedFileIds[] = {
{STRING, kWebMIdFileDescription},
{STRING, kWebMIdFileName},
{STRING, kWebMIdFileMimeType},
{BINARY, kWebMIdFileData},
{UINT, kWebMIdFileUID},
};
static const ElementIdInfo kChaptersIds[] = {
{LIST, kWebMIdEditionEntry},
};
static const ElementIdInfo kEditionEntryIds[] = {
{UINT, kWebMIdEditionUID},
{UINT, kWebMIdEditionFlagHidden},
{UINT, kWebMIdEditionFlagDefault},
{UINT, kWebMIdEditionFlagOrdered},
{LIST, kWebMIdChapterAtom},
};
static const ElementIdInfo kChapterAtomIds[] = {
{UINT, kWebMIdChapterUID},
{UINT, kWebMIdChapterTimeStart},
{UINT, kWebMIdChapterTimeEnd},
{UINT, kWebMIdChapterFlagHidden},
{UINT, kWebMIdChapterFlagEnabled},
{BINARY, kWebMIdChapterSegmentUID},
{UINT, kWebMIdChapterSegmentEditionUID},
{UINT, kWebMIdChapterPhysicalEquiv},
{LIST, kWebMIdChapterTrack},
{LIST, kWebMIdChapterDisplay},
{LIST, kWebMIdChapProcess},
};
static const ElementIdInfo kChapterTrackIds[] = {
{UINT, kWebMIdChapterTrackNumber},
};
static const ElementIdInfo kChapterDisplayIds[] = {
{STRING, kWebMIdChapString},
{STRING, kWebMIdChapLanguage},
{STRING, kWebMIdChapCountry},
};
static const ElementIdInfo kChapProcessIds[] = {
{UINT, kWebMIdChapProcessCodecID},
{BINARY, kWebMIdChapProcessPrivate},
{LIST, kWebMIdChapProcessCommand},
};
static const ElementIdInfo kChapProcessCommandIds[] = {
{UINT, kWebMIdChapProcessTime},
{BINARY, kWebMIdChapProcessData},
};
static const ElementIdInfo kTagsIds[] = {
{LIST, kWebMIdTag},
};
static const ElementIdInfo kTagIds[] = {
{LIST, kWebMIdTargets},
{LIST, kWebMIdSimpleTag},
};
static const ElementIdInfo kTargetsIds[] = {
{UINT, kWebMIdTargetTypeValue},
{STRING, kWebMIdTargetType},
{UINT, kWebMIdTagTrackUID},
{UINT, kWebMIdTagEditionUID},
{UINT, kWebMIdTagChapterUID},
{UINT, kWebMIdTagAttachmentUID},
};
static const ElementIdInfo kSimpleTagIds[] = {
{STRING, kWebMIdTagName},
{STRING, kWebMIdTagLanguage},
{UINT, kWebMIdTagDefault},
{STRING, kWebMIdTagString},
{BINARY, kWebMIdTagBinary},
};
#define LIST_ELEMENT_INFO(id, level, id_info) \
{ (id), (level), (id_info), arraysize(id_info) }
static const ListElementInfo kListElementInfo[] = {
LIST_ELEMENT_INFO(kWebMIdCluster, 1, kClusterIds),
LIST_ELEMENT_INFO(kWebMIdEBMLHeader, 0, kEBMLHeaderIds),
LIST_ELEMENT_INFO(kWebMIdSegment, 0, kSegmentIds),
LIST_ELEMENT_INFO(kWebMIdSeekHead, 1, kSeekHeadIds),
LIST_ELEMENT_INFO(kWebMIdSeek, 2, kSeekIds),
LIST_ELEMENT_INFO(kWebMIdInfo, 1, kInfoIds),
LIST_ELEMENT_INFO(kWebMIdChapterTranslate, 2, kChapterTranslateIds),
LIST_ELEMENT_INFO(kWebMIdSilentTracks, 2, kSilentTracksIds),
LIST_ELEMENT_INFO(kWebMIdBlockGroup, 2, kBlockGroupIds),
LIST_ELEMENT_INFO(kWebMIdBlockAdditions, 3, kBlockAdditionsIds),
LIST_ELEMENT_INFO(kWebMIdBlockMore, 4, kBlockMoreIds),
LIST_ELEMENT_INFO(kWebMIdSlices, 3, kSlicesIds),
LIST_ELEMENT_INFO(kWebMIdTimeSlice, 4, kTimeSliceIds),
LIST_ELEMENT_INFO(kWebMIdTracks, 1, kTracksIds),
LIST_ELEMENT_INFO(kWebMIdTrackEntry, 2, kTrackEntryIds),
LIST_ELEMENT_INFO(kWebMIdTrackTranslate, 3, kTrackTranslateIds),
LIST_ELEMENT_INFO(kWebMIdVideo, 3, kVideoIds),
LIST_ELEMENT_INFO(kWebMIdAudio, 3, kAudioIds),
LIST_ELEMENT_INFO(kWebMIdTrackOperation, 3, kTrackOperationIds),
LIST_ELEMENT_INFO(kWebMIdTrackCombinePlanes, 4, kTrackCombinePlanesIds),
LIST_ELEMENT_INFO(kWebMIdTrackPlane, 5, kTrackPlaneIds),
LIST_ELEMENT_INFO(kWebMIdJoinBlocks, 4, kJoinBlocksIds),
LIST_ELEMENT_INFO(kWebMIdContentEncodings, 3, kContentEncodingsIds),
LIST_ELEMENT_INFO(kWebMIdContentEncoding, 4, kContentEncodingIds),
LIST_ELEMENT_INFO(kWebMIdContentCompression, 5, kContentCompressionIds),
LIST_ELEMENT_INFO(kWebMIdContentEncryption, 5, kContentEncryptionIds),
LIST_ELEMENT_INFO(kWebMIdContentEncAESSettings, 6, kContentEncAESSettingsIds),
LIST_ELEMENT_INFO(kWebMIdCues, 1, kCuesIds),
LIST_ELEMENT_INFO(kWebMIdCuePoint, 2, kCuePointIds),
LIST_ELEMENT_INFO(kWebMIdCueTrackPositions, 3, kCueTrackPositionsIds),
LIST_ELEMENT_INFO(kWebMIdCueReference, 4, kCueReferenceIds),
LIST_ELEMENT_INFO(kWebMIdAttachments, 1, kAttachmentsIds),
LIST_ELEMENT_INFO(kWebMIdAttachedFile, 2, kAttachedFileIds),
LIST_ELEMENT_INFO(kWebMIdChapters, 1, kChaptersIds),
LIST_ELEMENT_INFO(kWebMIdEditionEntry, 2, kEditionEntryIds),
LIST_ELEMENT_INFO(kWebMIdChapterAtom, 3, kChapterAtomIds),
LIST_ELEMENT_INFO(kWebMIdChapterTrack, 4, kChapterTrackIds),
LIST_ELEMENT_INFO(kWebMIdChapterDisplay, 4, kChapterDisplayIds),
LIST_ELEMENT_INFO(kWebMIdChapProcess, 4, kChapProcessIds),
LIST_ELEMENT_INFO(kWebMIdChapProcessCommand, 5, kChapProcessCommandIds),
LIST_ELEMENT_INFO(kWebMIdTags, 1, kTagsIds),
LIST_ELEMENT_INFO(kWebMIdTag, 2, kTagIds),
LIST_ELEMENT_INFO(kWebMIdTargets, 3, kTargetsIds),
LIST_ELEMENT_INFO(kWebMIdSimpleTag, 3, kSimpleTagIds),
};
// 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;
if (size == 0)
return 0;
int mask = 0x80;
uint8 ch = buf[0];
int extra_bytes = -1;
bool all_ones = false;
for (int i = 0; i < max_bytes; ++i) {
if ((ch & mask) != 0) {
mask = ~mask & 0xff;
*num = mask_first_byte ? ch & mask : ch;
all_ones = (ch & mask) == mask;
extra_bytes = i;
break;
}
mask = 0x80 | mask >> 1;
}
if (extra_bytes == -1)
return -1;
// Return 0 if we need more data.
if ((1 + extra_bytes) > size)
return 0;
int bytes_used = 1;
for (int i = 0; i < extra_bytes; ++i) {
ch = buf[bytes_used++];
all_ones &= (ch == 0xff);
*num = (*num << 8) | ch;
}
if (all_ones)
*num = kint64max;
return bytes_used;
}
int WebMParseElementHeader(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 = 0;
int num_id_bytes = ParseWebMElementHeaderField(buf, size, 4, false, &tmp);
if (num_id_bytes <= 0)
return num_id_bytes;
if (tmp == kint64max)
tmp = kWebMReservedId;
*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;
if (tmp == kint64max)
tmp = kWebMUnknownSize;
*element_size = tmp;
DVLOG(3) << "WebMParseElementHeader() : id " << std::hex << *id << std::dec
<< " size " << *element_size;
return num_id_bytes + num_size_bytes;
}
// Finds ElementType for a specific ID.
static ElementType FindIdType(int id,
const ElementIdInfo* id_info,
int id_info_count) {
// Check for global element IDs that can be anywhere.
if (id == kWebMIdVoid || id == kWebMIdCRC32)
return SKIP;
for (int i = 0; i < id_info_count; ++i) {
if (id == id_info[i].id_)
return id_info[i].type_;
}
return UNKNOWN;
}
// Finds ListElementInfo for a specific ID.
static const ListElementInfo* FindListInfo(int id) {
for (size_t i = 0; i < arraysize(kListElementInfo); ++i) {
if (id == kListElementInfo[i].id_)
return &kListElementInfo[i];
}
return NULL;
}
static int FindListLevel(int id) {
const ListElementInfo* list_info = FindListInfo(id);
if (list_info)
return list_info->level_;
return -1;
}
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 ParseBinary(const uint8* buf, int size, int id,
WebMParserClient* client) {
return client->OnBinary(id, buf, size) ? size : -1;
}
static int ParseString(const uint8* buf, int size, int id,
WebMParserClient* client) {
const uint8* end = static_cast<const uint8*>(memchr(buf, '\0', size));
int length = (end != NULL) ? static_cast<int>(end - buf) : size;
std::string str(reinterpret_cast<const char*>(buf), length);
return client->OnString(id, str) ? size : -1;
}
static int ParseNonListElement(ElementType type, int id, int64 element_size,
const uint8* buf, int size,
WebMParserClient* client) {
DCHECK_GE(size, element_size);
int result = -1;
switch(type) {
case LIST:
NOTIMPLEMENTED();
result = -1;
break;
case UINT:
result = ParseUInt(buf, element_size, id, client);
break;
case FLOAT:
result = ParseFloat(buf, element_size, id, client);
break;
case BINARY:
result = ParseBinary(buf, element_size, id, client);
break;
case STRING:
result = ParseString(buf, element_size, id, client);
break;
case SKIP:
result = element_size;
break;
default:
DVLOG(1) << "Unhandled ID type " << type;
return -1;
};
DCHECK_LE(result, size);
return result;
}
WebMParserClient::WebMParserClient() {}
WebMParserClient::~WebMParserClient() {}
WebMParserClient* WebMParserClient::OnListStart(int id) {
DVLOG(1) << "Unexpected list element start with ID " << std::hex << id;
return NULL;
}
bool WebMParserClient::OnListEnd(int id) {
DVLOG(1) << "Unexpected list element end with ID " << std::hex << id;
return false;
}
bool WebMParserClient::OnUInt(int id, int64 val) {
DVLOG(1) << "Unexpected unsigned integer element with ID " << std::hex << id;
return false;
}
bool WebMParserClient::OnFloat(int id, double val) {
DVLOG(1) << "Unexpected float element with ID " << std::hex << id;
return false;
}
bool WebMParserClient::OnBinary(int id, const uint8* data, int size) {
DVLOG(1) << "Unexpected binary element with ID " << std::hex << id;
return false;
}
bool WebMParserClient::OnString(int id, const std::string& str) {
DVLOG(1) << "Unexpected string element with ID " << std::hex << id;
return false;
}
WebMListParser::WebMListParser(int id, WebMParserClient* client)
: state_(NEED_LIST_HEADER),
root_id_(id),
root_level_(FindListLevel(id)),
root_client_(client) {
DCHECK_GE(root_level_, 0);
DCHECK(client);
}
WebMListParser::~WebMListParser() {}
void WebMListParser::Reset() {
ChangeState(NEED_LIST_HEADER);
list_state_stack_.clear();
}
int WebMListParser::Parse(const uint8* buf, int size) {
DCHECK(buf);
if (size < 0 || state_ == PARSE_ERROR || state_ == DONE_PARSING_LIST)
return -1;
if (size == 0)
return 0;
const uint8* cur = buf;
int cur_size = size;
int bytes_parsed = 0;
while (cur_size > 0 && state_ != PARSE_ERROR && state_ != DONE_PARSING_LIST) {
int element_id = 0;
int64 element_size = 0;
int result = WebMParseElementHeader(cur, cur_size, &element_id,
&element_size);
if (result < 0)
return result;
if (result == 0)
return bytes_parsed;
switch(state_) {
case NEED_LIST_HEADER: {
if (element_id != root_id_) {
ChangeState(PARSE_ERROR);
return -1;
}
// Only allow Segment & Cluster to have an unknown size.
if (element_size == kWebMUnknownSize &&
(element_id != kWebMIdSegment) &&
(element_id != kWebMIdCluster)) {
ChangeState(PARSE_ERROR);
return -1;
}
ChangeState(INSIDE_LIST);
if (!OnListStart(root_id_, element_size))
return -1;
break;
}
case INSIDE_LIST: {
int header_size = result;
const uint8* element_data = cur + header_size;
int element_data_size = cur_size - header_size;
if (element_size < element_data_size)
element_data_size = element_size;
result = ParseListElement(header_size, element_id, element_size,
element_data, element_data_size);
DCHECK_LE(result, header_size + element_data_size);
if (result < 0) {
ChangeState(PARSE_ERROR);
return -1;
}
if (result == 0)
return bytes_parsed;
break;
}
case DONE_PARSING_LIST:
case PARSE_ERROR:
// Shouldn't be able to get here.
NOTIMPLEMENTED();
break;
}
cur += result;
cur_size -= result;
bytes_parsed += result;
}
return (state_ == PARSE_ERROR) ? -1 : bytes_parsed;
}
bool WebMListParser::IsParsingComplete() const {
return state_ == DONE_PARSING_LIST;
}
void WebMListParser::ChangeState(State new_state) {
state_ = new_state;
}
int WebMListParser::ParseListElement(int header_size,
int id, int64 element_size,
const uint8* data, int size) {
DCHECK_GT(list_state_stack_.size(), 0u);
ListState& list_state = list_state_stack_.back();
DCHECK(list_state.element_info_);
const ListElementInfo* element_info = list_state.element_info_;
ElementType id_type =
FindIdType(id, element_info->id_info_, element_info->id_info_count_);
// Unexpected ID.
if (id_type == UNKNOWN) {
if (list_state.size_ != kWebMUnknownSize ||
!IsSiblingOrAncestor(list_state.id_, id)) {
DVLOG(1) << "No ElementType info for ID 0x" << std::hex << id;
return -1;
}
// We've reached the end of a list of unknown size. Update the size now that
// we know it and dispatch the end of list calls.
list_state.size_ = list_state.bytes_parsed_;
if (!OnListEnd())
return -1;
// Check to see if all open lists have ended.
if (list_state_stack_.size() == 0)
return 0;
list_state = list_state_stack_.back();
}
// Make sure the whole element can fit inside the current list.
int64 total_element_size = header_size + element_size;
if (list_state.size_ != kWebMUnknownSize &&
list_state.size_ < list_state.bytes_parsed_ + total_element_size) {
return -1;
}
if (id_type == LIST) {
list_state.bytes_parsed_ += header_size;
if (!OnListStart(id, element_size))
return -1;
return header_size;
}
// Make sure we have the entire element before trying to parse a non-list
// element.
if (size < element_size)
return 0;
int bytes_parsed = ParseNonListElement(id_type, id, element_size,
data, size, list_state.client_);
DCHECK_LE(bytes_parsed, size);
// Return if an error occurred or we need more data.
// Note: bytes_parsed is 0 for a successful parse of a size 0 element. We
// need to check the element_size to disambiguate the "need more data" case
// from a successful parse.
if (bytes_parsed < 0 || (bytes_parsed == 0 && element_size != 0))
return bytes_parsed;
int result = header_size + bytes_parsed;
list_state.bytes_parsed_ += result;
// See if we have reached the end of the current list.
if (list_state.bytes_parsed_ == list_state.size_) {
if (!OnListEnd())
return -1;
}
return result;
}
bool WebMListParser::OnListStart(int id, int64 size) {
const ListElementInfo* element_info = FindListInfo(id);
if (!element_info)
return false;
int current_level = root_level_ + list_state_stack_.size() - 1;
if (current_level + 1 != element_info->level_)
return false;
WebMParserClient* current_list_client = NULL;
if (!list_state_stack_.empty()) {
// Make sure the new list doesn't go past the end of the current list.
ListState current_list_state = list_state_stack_.back();
if (current_list_state.size_ != kWebMUnknownSize &&
current_list_state.size_ < current_list_state.bytes_parsed_ + size)
return false;
current_list_client = current_list_state.client_;
} else {
current_list_client = root_client_;
}
WebMParserClient* new_list_client = current_list_client->OnListStart(id);
if (!new_list_client)
return false;
ListState new_list_state = { id, size, 0, element_info, new_list_client };
list_state_stack_.push_back(new_list_state);
if (size == 0)
return OnListEnd();
return true;
}
bool WebMListParser::OnListEnd() {
int lists_ended = 0;
for (; !list_state_stack_.empty(); ++lists_ended) {
const ListState& list_state = list_state_stack_.back();
if (list_state.bytes_parsed_ != list_state.size_)
break;
list_state_stack_.pop_back();
int64 bytes_parsed = list_state.bytes_parsed_;
WebMParserClient* client = NULL;
if (!list_state_stack_.empty()) {
// Update the bytes_parsed_ for the parent element.
list_state_stack_.back().bytes_parsed_ += bytes_parsed;
client = list_state_stack_.back().client_;
} else {
client = root_client_;
}
if (!client->OnListEnd(list_state.id_))
return false;
}
DCHECK_GE(lists_ended, 1);
if (list_state_stack_.empty())
ChangeState(DONE_PARSING_LIST);
return true;
}
bool WebMListParser::IsSiblingOrAncestor(int id_a, int id_b) const {
DCHECK((id_a == kWebMIdSegment) || (id_a == kWebMIdCluster));
if (id_a == kWebMIdCluster) {
// kWebMIdCluster siblings.
for (size_t i = 0; i < arraysize(kSegmentIds); i++) {
if (kSegmentIds[i].id_ == id_b)
return true;
}
}
// kWebMIdSegment siblings.
return ((id_b == kWebMIdSegment) || (id_b == kWebMIdEBMLHeader));
}
} // namespace media

View File

@ -1,158 +0,0 @@
// Copyright (c) 2012 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 <vector>
#include "base/basictypes.h"
#include "media/base/media_export.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 (except for OnListStart)
// returns a bool that indicates whether the parsed data is valid. OnListStart
// returns a pointer to a WebMParserClient object, which should be used to
// handle elements parsed out of the list being started. If false (or NULL by
// OnListStart) is returned then the parse is immediately terminated and an
// error is reported by the parser.
class MEDIA_EXPORT WebMParserClient {
public:
virtual ~WebMParserClient();
virtual WebMParserClient* 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);
protected:
WebMParserClient();
DISALLOW_COPY_AND_ASSIGN(WebMParserClient);
};
struct ListElementInfo;
// Parses a WebM list element and all of its children. This
// class supports incremental parsing of the list so Parse()
// can be called multiple times with pieces of the list.
// IsParsingComplete() will return true once the entire list has
// been parsed.
class MEDIA_EXPORT WebMListParser {
public:
// |id| - Element ID of the list we intend to parse.
// |client| - Called as different elements in the list are parsed.
WebMListParser(int id, WebMParserClient* client);
~WebMListParser();
// Resets the state of the parser so it can start parsing a new list.
void Reset();
// Parses list data contained in |buf|.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int Parse(const uint8* buf, int size);
// Returns true if the entire list has been parsed.
bool IsParsingComplete() const;
private:
enum State {
NEED_LIST_HEADER,
INSIDE_LIST,
DONE_PARSING_LIST,
PARSE_ERROR,
};
struct ListState {
int id_;
int64 size_;
int64 bytes_parsed_;
const ListElementInfo* element_info_;
WebMParserClient* client_;
};
void ChangeState(State new_state);
// Parses a single element in the current list.
//
// |header_size| - The size of the element header
// |id| - The ID of the element being parsed.
// |element_size| - The size of the element body.
// |data| - Pointer to the element contents.
// |size| - Number of bytes in |data|
// |client| - Client to pass the parsed data to.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int ParseListElement(int header_size,
int id, int64 element_size,
const uint8* data, int size);
// Called when starting to parse a new list.
//
// |id| - The ID of the new list.
// |size| - The size of the new list.
// |client| - The client object to notify that a new list is being parsed.
//
// Returns true if this list can be started in the current context. False
// if starting this list causes some sort of parse error.
bool OnListStart(int id, int64 size);
// Called when the end of the current list has been reached. This may also
// signal the end of the current list's ancestors if the current list happens
// to be at the end of its parent.
//
// Returns true if no errors occurred while ending this list(s).
bool OnListEnd();
// Checks to see if |id_b| is a sibling or ancestor of |id_a|.
bool IsSiblingOrAncestor(int id_a, int id_b) const;
State state_;
// Element ID passed to the constructor.
const int root_id_;
// Element level for |root_id_|. Used to verify that elements appear at
// the correct level.
const int root_level_;
// WebMParserClient to handle the root list.
WebMParserClient* const root_client_;
// Stack of state for all the lists currently being parsed. Lists are
// added and removed from this stack as they are parsed.
std::vector<ListState> list_state_stack_;
DISALLOW_COPY_AND_ASSIGN(WebMListParser);
};
// Parses an element header & returns the ID and element size.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
// |*id| contains the element ID on success and is undefined otherwise.
// |*element_size| contains the element size on success and is undefined
// otherwise.
int MEDIA_EXPORT WebMParseElementHeader(const uint8* buf, int size,
int* id, int64* element_size);
} // namespace media
#endif // MEDIA_WEBM_WEBM_PARSER_H_

View File

@ -1,395 +0,0 @@
// Copyright (c) 2012 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 "media/webm/webm_constants.h"
#include "media/webm/webm_parser.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::ReturnNull;
using ::testing::StrictMock;
using ::testing::_;
namespace media {
enum { kBlockCount = 5 };
class MockWebMParserClient : public WebMParserClient {
public:
virtual ~MockWebMParserClient() {}
// WebMParserClient methods.
MOCK_METHOD1(OnListStart, WebMParserClient*(int));
MOCK_METHOD1(OnListEnd, bool(int));
MOCK_METHOD2(OnUInt, bool(int, int64));
MOCK_METHOD2(OnFloat, bool(int, double));
MOCK_METHOD3(OnBinary, bool(int, const uint8*, int));
MOCK_METHOD2(OnString, bool(int, const std::string&));
};
class WebMParserTest : public testing::Test {
protected:
StrictMock<MockWebMParserClient> client_;
};
static scoped_ptr<Cluster> CreateCluster(int block_count) {
ClusterBuilder cb;
cb.SetClusterTimecode(0);
for (int i = 0; i < block_count; i++) {
uint8 data[] = { 0x00 };
cb.AddSimpleBlock(0, i, 0, data, sizeof(data));
}
return cb.Finish();
}
static void CreateClusterExpectations(int block_count,
bool is_complete_cluster,
MockWebMParserClient* client) {
InSequence s;
EXPECT_CALL(*client, OnListStart(kWebMIdCluster)).WillOnce(Return(client));
EXPECT_CALL(*client, OnUInt(kWebMIdTimecode, 0))
.WillOnce(Return(true));
for (int i = 0; i < block_count; i++) {
EXPECT_CALL(*client, OnBinary(kWebMIdSimpleBlock, _, _))
.WillOnce(Return(true));
}
if (is_complete_cluster)
EXPECT_CALL(*client, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
}
TEST_F(WebMParserTest, EmptyCluster) {
const uint8 kEmptyCluster[] = {
0x1F, 0x43, 0xB6, 0x75, 0x80 // CLUSTER (size = 0)
};
int size = sizeof(kEmptyCluster);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
WebMListParser parser(kWebMIdCluster, &client_);
int result = parser.Parse(kEmptyCluster, size);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, EmptyClusterInSegment) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5)
0x1F, 0x43, 0xB6, 0x75, 0x80, // CLUSTER (size = 0)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_));
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
// Test the case where a non-list child element has a size
// that is beyond the end of the parent.
TEST_F(WebMParserTest, ChildNonListLargerThanParent) {
const uint8 kBuffer[] = {
0x1F, 0x43, 0xB6, 0x75, 0x81, // CLUSTER (size = 1)
0xE7, 0x81, 0x01, // Timecode (size=1, value=1)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_));
WebMListParser parser(kWebMIdCluster, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
// Test the case where a list child element has a size
// that is beyond the end of the parent.
TEST_F(WebMParserTest, ChildListLargerThanParent) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 5)
0x1F, 0x43, 0xB6, 0x75, 0x81, 0x11 // CLUSTER (size = 1)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_));
WebMListParser parser(kWebMIdSegment, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
// Expecting to parse a Cluster, but get a Segment.
TEST_F(WebMParserTest, ListIdDoesNotMatch) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x80, // SEGMENT (size = 0)
};
int size = sizeof(kBuffer);
WebMListParser parser(kWebMIdCluster, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, InvalidElementInList) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x82, // SEGMENT (size = 2)
0xAE, 0x80, // TrackEntry (size = 0)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_));
WebMListParser parser(kWebMIdSegment, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, VoidAndCRC32InList) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x99, // SEGMENT (size = 25)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3)
0x1F, 0x43, 0xB6, 0x75, 0x8A, // CLUSTER (size = 10)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
0xBF, 0x83, 0x00, 0x00, 0x00, // CRC32 (size = 3)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&client_));
EXPECT_CALL(client_, OnListStart(kWebMIdCluster)).WillOnce(Return(&client_));
EXPECT_CALL(client_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ParseListElementWithSingleCall) {
scoped_ptr<Cluster> cluster(CreateCluster(kBlockCount));
CreateClusterExpectations(kBlockCount, true, &client_);
WebMListParser parser(kWebMIdCluster, &client_);
int result = parser.Parse(cluster->data(), cluster->size());
EXPECT_EQ(cluster->size(), result);
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ParseListElementWithMultipleCalls) {
scoped_ptr<Cluster> cluster(CreateCluster(kBlockCount));
CreateClusterExpectations(kBlockCount, true, &client_);
const uint8* data = cluster->data();
int size = cluster->size();
int default_parse_size = 3;
WebMListParser parser(kWebMIdCluster, &client_);
int parse_size = std::min(default_parse_size, size);
while (size > 0) {
int result = parser.Parse(data, parse_size);
ASSERT_GE(result, 0);
ASSERT_LE(result, parse_size);
if (result == 0) {
// The parser needs more data so increase the parse_size a little.
EXPECT_FALSE(parser.IsParsingComplete());
parse_size += default_parse_size;
parse_size = std::min(parse_size, size);
continue;
}
parse_size = default_parse_size;
data += result;
size -= result;
EXPECT_EQ((size == 0), parser.IsParsingComplete());
}
EXPECT_TRUE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, Reset) {
InSequence s;
scoped_ptr<Cluster> cluster(CreateCluster(kBlockCount));
// First expect all but the last block.
CreateClusterExpectations(kBlockCount - 1, false, &client_);
// Now expect all blocks.
CreateClusterExpectations(kBlockCount, true, &client_);
WebMListParser parser(kWebMIdCluster, &client_);
// Send slightly less than the full cluster so all but the last block is
// parsed.
int result = parser.Parse(cluster->data(), cluster->size() - 1);
EXPECT_GT(result, 0);
EXPECT_LT(result, cluster->size());
EXPECT_FALSE(parser.IsParsingComplete());
parser.Reset();
// Now parse a whole cluster to verify that all the blocks will get parsed.
result = parser.Parse(cluster->data(), cluster->size());
EXPECT_EQ(result, cluster->size());
EXPECT_TRUE(parser.IsParsingComplete());
}
// Test the case where multiple clients are used for different lists.
TEST_F(WebMParserTest, MultipleClients) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x94, // SEGMENT (size = 20)
0x16, 0x54, 0xAE, 0x6B, 0x85, // TRACKS (size = 5)
0xAE, 0x83, // TRACKENTRY (size = 3)
0xD7, 0x81, 0x01, // TRACKNUMBER (size = 1)
0x1F, 0x43, 0xB6, 0x75, 0x85, // CLUSTER (size = 5)
0xEC, 0x83, 0x00, 0x00, 0x00, // Void (size = 3)
};
int size = sizeof(kBuffer);
StrictMock<MockWebMParserClient> c1_;
StrictMock<MockWebMParserClient> c2_;
StrictMock<MockWebMParserClient> c3_;
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(Return(&c1_));
EXPECT_CALL(c1_, OnListStart(kWebMIdTracks)).WillOnce(Return(&c2_));
EXPECT_CALL(c2_, OnListStart(kWebMIdTrackEntry)).WillOnce(Return(&c3_));
EXPECT_CALL(c3_, OnUInt(kWebMIdTrackNumber, 1)).WillOnce(Return(true));
EXPECT_CALL(c2_, OnListEnd(kWebMIdTrackEntry)).WillOnce(Return(true));
EXPECT_CALL(c1_, OnListEnd(kWebMIdTracks)).WillOnce(Return(true));
EXPECT_CALL(c1_, OnListStart(kWebMIdCluster)).WillOnce(Return(&c2_));
EXPECT_CALL(c1_, OnListEnd(kWebMIdCluster)).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdSegment)).WillOnce(Return(true));
WebMListParser parser(kWebMIdSegment, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
// Test the case where multiple clients are used for different lists.
TEST_F(WebMParserTest, InvalidClient) {
const uint8 kBuffer[] = {
0x18, 0x53, 0x80, 0x67, 0x85, // SEGMENT (size = 20)
0x16, 0x54, 0xAE, 0x6B, 0x80, // TRACKS (size = 5)
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdSegment)).WillOnce(ReturnNull());
WebMListParser parser(kWebMIdSegment, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(-1, result);
EXPECT_FALSE(parser.IsParsingComplete());
}
TEST_F(WebMParserTest, ReservedIds) {
const uint8 k1ByteReservedId[] = { 0xFF, 0x81 };
const uint8 k2ByteReservedId[] = { 0x7F, 0xFF, 0x81 };
const uint8 k3ByteReservedId[] = { 0x3F, 0xFF, 0xFF, 0x81 };
const uint8 k4ByteReservedId[] = { 0x1F, 0xFF, 0xFF, 0xFF, 0x81 };
const uint8* kBuffers[] = {
k1ByteReservedId,
k2ByteReservedId,
k3ByteReservedId,
k4ByteReservedId
};
for (size_t i = 0; i < arraysize(kBuffers); i++) {
int id;
int64 element_size;
int buffer_size = 2 + i;
EXPECT_EQ(buffer_size, WebMParseElementHeader(kBuffers[i], buffer_size,
&id, &element_size));
EXPECT_EQ(id, kWebMReservedId);
EXPECT_EQ(element_size, 1);
}
}
TEST_F(WebMParserTest, ReservedSizes) {
const uint8 k1ByteReservedSize[] = { 0xA3, 0xFF };
const uint8 k2ByteReservedSize[] = { 0xA3, 0x7F, 0xFF };
const uint8 k3ByteReservedSize[] = { 0xA3, 0x3F, 0xFF, 0xFF };
const uint8 k4ByteReservedSize[] = { 0xA3, 0x1F, 0xFF, 0xFF, 0xFF };
const uint8 k5ByteReservedSize[] = { 0xA3, 0x0F, 0xFF, 0xFF, 0xFF, 0xFF };
const uint8 k6ByteReservedSize[] = { 0xA3, 0x07, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF };
const uint8 k7ByteReservedSize[] = { 0xA3, 0x03, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF };
const uint8 k8ByteReservedSize[] = { 0xA3, 0x01, 0xFF, 0xFF, 0xFF, 0xFF, 0xFF,
0xFF, 0xFF };
const uint8* kBuffers[] = {
k1ByteReservedSize,
k2ByteReservedSize,
k3ByteReservedSize,
k4ByteReservedSize,
k5ByteReservedSize,
k6ByteReservedSize,
k7ByteReservedSize,
k8ByteReservedSize
};
for (size_t i = 0; i < arraysize(kBuffers); i++) {
int id;
int64 element_size;
int buffer_size = 2 + i;
EXPECT_EQ(buffer_size, WebMParseElementHeader(kBuffers[i], buffer_size,
&id, &element_size));
EXPECT_EQ(id, 0xA3);
EXPECT_EQ(element_size, kWebMUnknownSize);
}
}
TEST_F(WebMParserTest, ZeroPaddedStrings) {
const uint8 kBuffer[] = {
0x1A, 0x45, 0xDF, 0xA3, 0x91, // EBMLHEADER (size = 17)
0x42, 0x82, 0x80, // DocType (size = 0)
0x42, 0x82, 0x81, 0x00, // DocType (size = 1) ""
0x42, 0x82, 0x81, 'a', // DocType (size = 1) "a"
0x42, 0x82, 0x83, 'a', 0x00, 0x00 // DocType (size = 3) "a"
};
int size = sizeof(kBuffer);
InSequence s;
EXPECT_CALL(client_, OnListStart(kWebMIdEBMLHeader))
.WillOnce(Return(&client_));
EXPECT_CALL(client_, OnString(kWebMIdDocType, "")).WillOnce(Return(true));
EXPECT_CALL(client_, OnString(kWebMIdDocType, "")).WillOnce(Return(true));
EXPECT_CALL(client_, OnString(kWebMIdDocType, "a")).WillOnce(Return(true));
EXPECT_CALL(client_, OnString(kWebMIdDocType, "a")).WillOnce(Return(true));
EXPECT_CALL(client_, OnListEnd(kWebMIdEBMLHeader)).WillOnce(Return(true));
WebMListParser parser(kWebMIdEBMLHeader, &client_);
int result = parser.Parse(kBuffer, size);
EXPECT_EQ(size, result);
EXPECT_TRUE(parser.IsParsingComplete());
}
} // namespace media

View File

@ -1,329 +0,0 @@
// Copyright (c) 2012 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_stream_parser.h"
#include <string>
#include "base/callback.h"
#include "base/logging.h"
#include "base/stl_util.h"
#include "media/webm/webm_cluster_parser.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_content_encodings.h"
#include "media/webm/webm_crypto_helpers.h"
#include "media/webm/webm_info_parser.h"
#include "media/webm/webm_tracks_parser.h"
namespace media {
WebMStreamParser::WebMStreamParser()
: state_(kWaitingForInit),
waiting_for_buffers_(false) {
}
WebMStreamParser::~WebMStreamParser() {
STLDeleteValues(&text_track_map_);
}
void WebMStreamParser::Init(const InitCB& init_cb,
const NewConfigCB& config_cb,
const NewBuffersCB& new_buffers_cb,
const NewTextBuffersCB& text_cb,
const NeedKeyCB& need_key_cb,
const AddTextTrackCB& add_text_track_cb,
const NewMediaSegmentCB& new_segment_cb,
const base::Closure& end_of_segment_cb,
const LogCB& log_cb) {
DCHECK_EQ(state_, kWaitingForInit);
DCHECK(init_cb_.is_null());
DCHECK(!init_cb.is_null());
DCHECK(!config_cb.is_null());
DCHECK(!new_buffers_cb.is_null());
DCHECK(!text_cb.is_null());
DCHECK(!need_key_cb.is_null());
DCHECK(!new_segment_cb.is_null());
DCHECK(!end_of_segment_cb.is_null());
ChangeState(kParsingHeaders);
init_cb_ = init_cb;
config_cb_ = config_cb;
new_buffers_cb_ = new_buffers_cb;
text_cb_ = text_cb;
need_key_cb_ = need_key_cb;
add_text_track_cb_ = add_text_track_cb;
new_segment_cb_ = new_segment_cb;
end_of_segment_cb_ = end_of_segment_cb;
log_cb_ = log_cb;
}
void WebMStreamParser::Flush() {
DCHECK_NE(state_, kWaitingForInit);
byte_queue_.Reset();
if (state_ != kParsingClusters)
return;
cluster_parser_->Reset();
}
bool WebMStreamParser::Parse(const uint8* buf, int size) {
DCHECK_NE(state_, kWaitingForInit);
if (state_ == kError)
return false;
byte_queue_.Push(buf, size);
int result = 0;
int bytes_parsed = 0;
const uint8* cur = NULL;
int cur_size = 0;
byte_queue_.Peek(&cur, &cur_size);
while (cur_size > 0) {
State oldState = state_;
switch (state_) {
case kParsingHeaders:
result = ParseInfoAndTracks(cur, cur_size);
break;
case kParsingClusters:
result = ParseCluster(cur, cur_size);
break;
case kWaitingForInit:
case kError:
return false;
}
if (result < 0) {
ChangeState(kError);
return false;
}
if (state_ == oldState && result == 0)
break;
DCHECK_GE(result, 0);
cur += result;
cur_size -= result;
bytes_parsed += result;
}
byte_queue_.Pop(bytes_parsed);
return true;
}
void WebMStreamParser::ChangeState(State new_state) {
DVLOG(1) << "ChangeState() : " << state_ << " -> " << new_state;
state_ = new_state;
}
int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
DVLOG(2) << "ParseInfoAndTracks()";
DCHECK(data);
DCHECK_GT(size, 0);
const uint8* cur = data;
int cur_size = size;
int bytes_parsed = 0;
int id;
int64 element_size;
int result = WebMParseElementHeader(cur, cur_size, &id, &element_size);
if (result <= 0)
return result;
switch (id) {
case kWebMIdEBMLHeader:
case kWebMIdSeekHead:
case kWebMIdVoid:
case kWebMIdCRC32:
case kWebMIdCues:
case kWebMIdChapters:
if (cur_size < (result + element_size)) {
// We don't have the whole element yet. Signal we need more data.
return 0;
}
// Skip the element.
return result + element_size;
break;
case kWebMIdSegment:
// Just consume the segment header.
return result;
break;
case kWebMIdInfo:
// We've found the element we are looking for.
break;
default: {
MEDIA_LOG(log_cb_) << "Unexpected element ID 0x" << std::hex << id;
return -1;
}
}
WebMInfoParser info_parser;
result = info_parser.Parse(cur, cur_size);
if (result <= 0)
return result;
cur += result;
cur_size -= result;
bytes_parsed += result;
WebMTracksParser tracks_parser(log_cb_, add_text_track_cb_.is_null());
result = tracks_parser.Parse(cur, cur_size);
if (result <= 0)
return result;
bytes_parsed += result;
base::TimeDelta duration = kInfiniteDuration();
if (info_parser.duration() > 0) {
double mult = info_parser.timecode_scale() / 1000.0;
int64 duration_in_us = info_parser.duration() * mult;
duration = base::TimeDelta::FromMicroseconds(duration_in_us);
}
const AudioDecoderConfig& audio_config = tracks_parser.audio_decoder_config();
if (audio_config.is_encrypted())
FireNeedKey(tracks_parser.audio_encryption_key_id());
const VideoDecoderConfig& video_config = tracks_parser.video_decoder_config();
if (video_config.is_encrypted())
FireNeedKey(tracks_parser.video_encryption_key_id());
if (!config_cb_.Run(audio_config, video_config)) {
DVLOG(1) << "New config data isn't allowed.";
return -1;
}
typedef WebMTracksParser::TextTracks TextTracks;
const TextTracks& text_tracks = tracks_parser.text_tracks();
for (TextTracks::const_iterator itr = text_tracks.begin();
itr != text_tracks.end(); ++itr) {
const WebMTracksParser::TextTrackInfo& text_track_info = itr->second;
// TODO(matthewjheaney): verify that WebVTT uses ISO 639-2 for lang
scoped_ptr<TextTrack> text_track =
add_text_track_cb_.Run(text_track_info.kind,
text_track_info.name,
text_track_info.language);
// Assume ownership of pointer, and cache the text track object, for use
// later when we have text track buffers. (The text track objects are
// deallocated in the dtor for this class.)
if (text_track)
text_track_map_.insert(std::make_pair(itr->first, text_track.release()));
}
cluster_parser_.reset(new WebMClusterParser(
info_parser.timecode_scale(),
tracks_parser.audio_track_num(),
tracks_parser.video_track_num(),
text_tracks,
tracks_parser.ignored_tracks(),
tracks_parser.audio_encryption_key_id(),
tracks_parser.video_encryption_key_id(),
log_cb_));
ChangeState(kParsingClusters);
if (!init_cb_.is_null()) {
init_cb_.Run(true, duration);
init_cb_.Reset();
}
return bytes_parsed;
}
int WebMStreamParser::ParseCluster(const uint8* data, int size) {
if (!cluster_parser_)
return -1;
int id;
int64 element_size;
int result = WebMParseElementHeader(data, size, &id, &element_size);
if (result <= 0)
return result;
if (id == kWebMIdCluster)
waiting_for_buffers_ = true;
// TODO(matthewjheaney): implement support for chapters
if (id == kWebMIdCues || id == kWebMIdChapters) {
if (size < (result + element_size)) {
// We don't have the whole element yet. Signal we need more data.
return 0;
}
// Skip the element.
return result + element_size;
}
if (id == kWebMIdEBMLHeader) {
ChangeState(kParsingHeaders);
return 0;
}
int bytes_parsed = cluster_parser_->Parse(data, size);
if (bytes_parsed <= 0)
return bytes_parsed;
const BufferQueue& audio_buffers = cluster_parser_->audio_buffers();
const BufferQueue& video_buffers = cluster_parser_->video_buffers();
bool cluster_ended = cluster_parser_->cluster_ended();
if (waiting_for_buffers_ &&
cluster_parser_->cluster_start_time() != kNoTimestamp()) {
new_segment_cb_.Run();
waiting_for_buffers_ = false;
}
if ((!audio_buffers.empty() || !video_buffers.empty()) &&
!new_buffers_cb_.Run(audio_buffers, video_buffers)) {
return -1;
}
WebMClusterParser::TextTrackIterator text_track_iter =
cluster_parser_->CreateTextTrackIterator();
int text_track_num;
const BufferQueue* text_buffers;
while (text_track_iter(&text_track_num, &text_buffers)) {
TextTrackMap::iterator find_result = text_track_map_.find(text_track_num);
if (find_result == text_track_map_.end())
continue;
TextTrack* const text_track = find_result->second;
if (!text_buffers->empty() && !text_cb_.Run(text_track, *text_buffers))
return -1;
}
if (cluster_ended)
end_of_segment_cb_.Run();
return bytes_parsed;
}
void WebMStreamParser::FireNeedKey(const std::string& key_id) {
int key_id_size = key_id.size();
DCHECK_GT(key_id_size, 0);
scoped_ptr<uint8[]> key_id_array(new uint8[key_id_size]);
memcpy(key_id_array.get(), key_id.data(), key_id_size);
need_key_cb_.Run(kWebMEncryptInitDataType, key_id_array.Pass(), key_id_size);
}
} // namespace media

View File

@ -1,98 +0,0 @@
// Copyright (c) 2012 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_STREAM_PARSER_H_
#define MEDIA_WEBM_WEBM_STREAM_PARSER_H_
#include <map>
#include "base/callback_forward.h"
#include "base/memory/ref_counted.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/buffers.h"
#include "media/base/byte_queue.h"
#include "media/base/stream_parser.h"
#include "media/base/video_decoder_config.h"
namespace media {
class WebMClusterParser;
class WebMStreamParser : public StreamParser {
public:
WebMStreamParser();
virtual ~WebMStreamParser();
// StreamParser implementation.
virtual void Init(const InitCB& init_cb, const NewConfigCB& config_cb,
const NewBuffersCB& new_buffers_cb,
const NewTextBuffersCB& text_cb,
const NeedKeyCB& need_key_cb,
const AddTextTrackCB& add_text_track_cb,
const NewMediaSegmentCB& new_segment_cb,
const base::Closure& end_of_segment_cb,
const LogCB& log_cb) OVERRIDE;
virtual void Flush() OVERRIDE;
virtual bool Parse(const uint8* buf, int size) OVERRIDE;
private:
enum State {
kWaitingForInit,
kParsingHeaders,
kParsingClusters,
kError
};
void ChangeState(State new_state);
// Parses WebM Header, Info, Tracks elements. It also skips other level 1
// elements that are not used right now. Once the Info & Tracks elements have
// been parsed, this method will transition the parser from PARSING_HEADERS to
// PARSING_CLUSTERS.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int ParseInfoAndTracks(const uint8* data, int size);
// Incrementally parses WebM cluster elements. This method also skips
// CUES elements if they are encountered since we currently don't use the
// data in these elements.
//
// Returns < 0 if the parse fails.
// Returns 0 if more data is needed.
// Returning > 0 indicates success & the number of bytes parsed.
int ParseCluster(const uint8* data, int size);
// Fire needkey event through the |need_key_cb_|.
void FireNeedKey(const std::string& key_id);
State state_;
InitCB init_cb_;
NewConfigCB config_cb_;
NewBuffersCB new_buffers_cb_;
NewTextBuffersCB text_cb_;
NeedKeyCB need_key_cb_;
AddTextTrackCB add_text_track_cb_;
typedef std::map<int, TextTrack* > TextTrackMap;
TextTrackMap text_track_map_;
NewMediaSegmentCB new_segment_cb_;
base::Closure end_of_segment_cb_;
LogCB log_cb_;
// True if a new cluster id has been seen, but no audio or video buffers have
// been parsed yet.
bool waiting_for_buffers_;
scoped_ptr<WebMClusterParser> cluster_parser_;
ByteQueue byte_queue_;
DISALLOW_COPY_AND_ASSIGN(WebMStreamParser);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_STREAM_PARSER_H_

View File

@ -1,284 +0,0 @@
// Copyright (c) 2012 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 "base/strings/string_util.h"
#include "media/base/buffers.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_content_encodings.h"
namespace media {
static TextKind CodecIdToTextKind(const std::string& codec_id) {
if (codec_id == kWebMCodecSubtitles)
return kTextSubtitles;
if (codec_id == kWebMCodecCaptions)
return kTextCaptions;
if (codec_id == kWebMCodecDescriptions)
return kTextDescriptions;
if (codec_id == kWebMCodecMetadata)
return kTextMetadata;
return kTextNone;
}
WebMTracksParser::WebMTracksParser(const LogCB& log_cb, bool ignore_text_tracks)
: track_type_(-1),
track_num_(-1),
audio_track_num_(-1),
video_track_num_(-1),
ignore_text_tracks_(ignore_text_tracks),
log_cb_(log_cb),
audio_client_(log_cb),
video_client_(log_cb) {
}
WebMTracksParser::~WebMTracksParser() {}
int WebMTracksParser::Parse(const uint8* buf, int size) {
track_type_ =-1;
track_num_ = -1;
track_name_.clear();
track_language_.clear();
audio_track_num_ = -1;
audio_decoder_config_ = AudioDecoderConfig();
video_track_num_ = -1;
video_decoder_config_ = VideoDecoderConfig();
text_tracks_.clear();
ignored_tracks_.clear();
WebMListParser parser(kWebMIdTracks, this);
int result = parser.Parse(buf, size);
if (result <= 0)
return result;
// For now we do all or nothing parsing.
return parser.IsParsingComplete() ? result : 0;
}
WebMParserClient* WebMTracksParser::OnListStart(int id) {
if (id == kWebMIdContentEncodings) {
DCHECK(!track_content_encodings_client_.get());
track_content_encodings_client_.reset(
new WebMContentEncodingsClient(log_cb_));
return track_content_encodings_client_->OnListStart(id);
}
if (id == kWebMIdTrackEntry) {
track_type_ = -1;
track_num_ = -1;
track_name_.clear();
track_language_.clear();
codec_id_ = "";
codec_private_.clear();
audio_client_.Reset();
video_client_.Reset();
return this;
}
if (id == kWebMIdAudio)
return &audio_client_;
if (id == kWebMIdVideo)
return &video_client_;
return this;
}
bool WebMTracksParser::OnListEnd(int id) {
if (id == kWebMIdContentEncodings) {
DCHECK(track_content_encodings_client_.get());
return track_content_encodings_client_->OnListEnd(id);
}
if (id == kWebMIdTrackEntry) {
if (track_type_ == -1 || track_num_ == -1) {
MEDIA_LOG(log_cb_) << "Missing TrackEntry data for "
<< " TrackType " << track_type_
<< " TrackNum " << track_num_;
return false;
}
if (track_type_ != kWebMTrackTypeAudio &&
track_type_ != kWebMTrackTypeVideo &&
track_type_ != kWebMTrackTypeSubtitlesOrCaptions &&
track_type_ != kWebMTrackTypeDescriptionsOrMetadata) {
MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
return false;
}
TextKind text_track_kind = kTextNone;
if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions) {
text_track_kind = CodecIdToTextKind(codec_id_);
if (text_track_kind == kTextNone) {
MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
<< " TrackNum " << track_num_;
return false;
}
if (text_track_kind != kTextSubtitles &&
text_track_kind != kTextCaptions) {
MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
<< " TrackNum " << track_num_;
return false;
}
} else if (track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
text_track_kind = CodecIdToTextKind(codec_id_);
if (text_track_kind == kTextNone) {
MEDIA_LOG(log_cb_) << "Missing TrackEntry CodecID"
<< " TrackNum " << track_num_;
return false;
}
if (text_track_kind != kTextDescriptions &&
text_track_kind != kTextMetadata) {
MEDIA_LOG(log_cb_) << "Wrong TrackEntry CodecID"
<< " TrackNum " << track_num_;
return false;
}
}
std::string encryption_key_id;
if (track_content_encodings_client_) {
DCHECK(!track_content_encodings_client_->content_encodings().empty());
// If we have multiple ContentEncoding in one track. Always choose the
// key id in the first ContentEncoding as the key id of the track.
encryption_key_id = track_content_encodings_client_->
content_encodings()[0]->encryption_key_id();
}
if (track_type_ == kWebMTrackTypeAudio) {
if (audio_track_num_ == -1) {
audio_track_num_ = track_num_;
audio_encryption_key_id_ = encryption_key_id;
DCHECK(!audio_decoder_config_.IsValidConfig());
if (!audio_client_.InitializeConfig(
codec_id_, codec_private_, !audio_encryption_key_id_.empty(),
&audio_decoder_config_)) {
return false;
}
} else {
MEDIA_LOG(log_cb_) << "Ignoring audio track " << track_num_;
ignored_tracks_.insert(track_num_);
}
} else if (track_type_ == kWebMTrackTypeVideo) {
if (video_track_num_ == -1) {
video_track_num_ = track_num_;
video_encryption_key_id_ = encryption_key_id;
DCHECK(!video_decoder_config_.IsValidConfig());
if (!video_client_.InitializeConfig(
codec_id_, codec_private_, !video_encryption_key_id_.empty(),
&video_decoder_config_)) {
return false;
}
} else {
MEDIA_LOG(log_cb_) << "Ignoring video track " << track_num_;
ignored_tracks_.insert(track_num_);
}
} else if (track_type_ == kWebMTrackTypeSubtitlesOrCaptions ||
track_type_ == kWebMTrackTypeDescriptionsOrMetadata) {
if (ignore_text_tracks_) {
MEDIA_LOG(log_cb_) << "Ignoring text track " << track_num_;
ignored_tracks_.insert(track_num_);
} else {
TextTrackInfo& text_track_info = text_tracks_[track_num_];
text_track_info.kind = text_track_kind;
text_track_info.name = track_name_;
text_track_info.language = track_language_;
}
} else {
MEDIA_LOG(log_cb_) << "Unexpected TrackType " << track_type_;
return false;
}
track_type_ = -1;
track_num_ = -1;
track_name_.clear();
track_language_.clear();
codec_id_ = "";
codec_private_.clear();
track_content_encodings_client_.reset();
audio_client_.Reset();
video_client_.Reset();
return true;
}
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;
default:
return true;
}
if (*dst != -1) {
MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
<< " specified";
return false;
}
*dst = val;
return true;
}
bool WebMTracksParser::OnFloat(int id, double val) {
return true;
}
bool WebMTracksParser::OnBinary(int id, const uint8* data, int size) {
if (id == kWebMIdCodecPrivate) {
if (!codec_private_.empty()) {
MEDIA_LOG(log_cb_) << "Multiple CodecPrivate fields in a track.";
return false;
}
codec_private_.assign(data, data + size);
return true;
}
return true;
}
bool WebMTracksParser::OnString(int id, const std::string& str) {
if (id == kWebMIdCodecID) {
if (!codec_id_.empty()) {
MEDIA_LOG(log_cb_) << "Multiple CodecID fields in a track";
return false;
}
codec_id_ = str;
return true;
}
if (id == kWebMIdName) {
track_name_ = str;
return true;
}
if (id == kWebMIdLanguage) {
track_language_ = str;
return true;
}
return true;
}
} // namespace media

View File

@ -1,108 +0,0 @@
// Copyright (c) 2012 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 <map>
#include <set>
#include <string>
#include <vector>
#include "base/compiler_specific.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_log.h"
#include "media/base/text_track.h"
#include "media/base/video_decoder_config.h"
#include "media/webm/webm_audio_client.h"
#include "media/webm/webm_content_encodings_client.h"
#include "media/webm/webm_parser.h"
#include "media/webm/webm_video_client.h"
namespace media {
// Parser for WebM Tracks element.
class MEDIA_EXPORT WebMTracksParser : public WebMParserClient {
public:
explicit WebMTracksParser(const LogCB& log_cb, bool ignore_text_tracks);
virtual ~WebMTracksParser();
// Parses a WebM Tracks element in |buf|.
//
// Returns -1 if the parse fails.
// Returns 0 if more data is needed.
// Returns the number of bytes parsed on success.
int Parse(const uint8* buf, int size);
int64 audio_track_num() const { return audio_track_num_; }
int64 video_track_num() const { return video_track_num_; }
const std::set<int64>& ignored_tracks() const { return ignored_tracks_; }
const std::string& audio_encryption_key_id() const {
return audio_encryption_key_id_;
}
const AudioDecoderConfig& audio_decoder_config() {
return audio_decoder_config_;
}
const std::string& video_encryption_key_id() const {
return video_encryption_key_id_;
}
const VideoDecoderConfig& video_decoder_config() {
return video_decoder_config_;
}
struct TextTrackInfo {
TextKind kind;
std::string name;
std::string language;
};
typedef std::map<int64, TextTrackInfo> TextTracks;
const TextTracks& text_tracks() const {
return text_tracks_;
}
private:
// WebMParserClient implementation.
virtual WebMParserClient* OnListStart(int id) OVERRIDE;
virtual bool OnListEnd(int id) OVERRIDE;
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnFloat(int id, double val) OVERRIDE;
virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE;
virtual bool OnString(int id, const std::string& str) OVERRIDE;
int64 track_type_;
int64 track_num_;
std::string track_name_;
std::string track_language_;
std::string codec_id_;
std::vector<uint8> codec_private_;
scoped_ptr<WebMContentEncodingsClient> track_content_encodings_client_;
int64 audio_track_num_;
int64 video_track_num_;
bool ignore_text_tracks_;
TextTracks text_tracks_;
std::set<int64> ignored_tracks_;
std::string audio_encryption_key_id_;
std::string video_encryption_key_id_;
LogCB log_cb_;
WebMAudioClient audio_client_;
AudioDecoderConfig audio_decoder_config_;
WebMVideoClient video_client_;
VideoDecoderConfig video_decoder_config_;
DISALLOW_COPY_AND_ASSIGN(WebMTracksParser);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_TRACKS_PARSER_H_

View File

@ -1,125 +0,0 @@
// Copyright (c) 2013 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 "media/webm/tracks_builder.h"
#include "media/webm/webm_constants.h"
#include "media/webm/webm_tracks_parser.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
using ::testing::Return;
using ::testing::_;
namespace media {
static const int kTypeSubtitlesOrCaptions = 0x11;
static const int kTypeDescriptionsOrMetadata = 0x21;
class WebMTracksParserTest : public testing::Test {
public:
WebMTracksParserTest() {}
};
static void VerifyTextTrackInfo(const uint8* buffer,
int buffer_size,
TextKind text_kind,
const std::string& name,
const std::string& language) {
scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(LogCB(), false));
int result = parser->Parse(buffer, buffer_size);
EXPECT_GT(result, 0);
EXPECT_EQ(result, buffer_size);
const WebMTracksParser::TextTracks& text_tracks = parser->text_tracks();
EXPECT_EQ(text_tracks.size(), WebMTracksParser::TextTracks::size_type(1));
const WebMTracksParser::TextTracks::const_iterator itr = text_tracks.begin();
EXPECT_EQ(itr->first, 1); // track num
const WebMTracksParser::TextTrackInfo& info = itr->second;
EXPECT_EQ(info.kind, text_kind);
EXPECT_TRUE(info.name == name);
EXPECT_TRUE(info.language == language);
}
TEST_F(WebMTracksParserTest, SubtitleNoNameNoLang) {
InSequence s;
TracksBuilder tb;
tb.AddTrack(1, kWebMTrackTypeSubtitlesOrCaptions,
kWebMCodecSubtitles, "", "");
const std::vector<uint8> buf = tb.Finish();
VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "", "");
}
TEST_F(WebMTracksParserTest, SubtitleYesNameNoLang) {
InSequence s;
TracksBuilder tb;
tb.AddTrack(1, kWebMTrackTypeSubtitlesOrCaptions,
kWebMCodecSubtitles, "Spock", "");
const std::vector<uint8> buf = tb.Finish();
VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "Spock", "");
}
TEST_F(WebMTracksParserTest, SubtitleNoNameYesLang) {
InSequence s;
TracksBuilder tb;
tb.AddTrack(1, kWebMTrackTypeSubtitlesOrCaptions,
kWebMCodecSubtitles, "", "eng");
const std::vector<uint8> buf = tb.Finish();
VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "", "eng");
}
TEST_F(WebMTracksParserTest, SubtitleYesNameYesLang) {
InSequence s;
TracksBuilder tb;
tb.AddTrack(1, kWebMTrackTypeSubtitlesOrCaptions,
kWebMCodecSubtitles, "Picard", "fre");
const std::vector<uint8> buf = tb.Finish();
VerifyTextTrackInfo(&buf[0], buf.size(), kTextSubtitles, "Picard", "fre");
}
TEST_F(WebMTracksParserTest, IgnoringTextTracks) {
InSequence s;
TracksBuilder tb;
tb.AddTrack(1, kWebMTrackTypeSubtitlesOrCaptions,
kWebMCodecSubtitles, "Subtitles", "fre");
tb.AddTrack(2, kWebMTrackTypeSubtitlesOrCaptions,
kWebMCodecSubtitles, "Commentary", "fre");
const std::vector<uint8> buf = tb.Finish();
scoped_ptr<WebMTracksParser> parser(new WebMTracksParser(LogCB(), true));
int result = parser->Parse(&buf[0], buf.size());
EXPECT_GT(result, 0);
EXPECT_EQ(result, static_cast<int>(buf.size()));
EXPECT_EQ(parser->text_tracks().size(), 0u);
const std::set<int64>& ignored_tracks = parser->ignored_tracks();
EXPECT_TRUE(ignored_tracks.find(1) != ignored_tracks.end());
EXPECT_TRUE(ignored_tracks.find(2) != ignored_tracks.end());
// Test again w/o ignoring the test tracks.
parser.reset(new WebMTracksParser(LogCB(), false));
result = parser->Parse(&buf[0], buf.size());
EXPECT_GT(result, 0);
EXPECT_EQ(parser->ignored_tracks().size(), 0u);
EXPECT_EQ(parser->text_tracks().size(), 2u);
}
} // namespace media

View File

@ -1,163 +0,0 @@
// Copyright (c) 2013 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_video_client.h"
#include "media/base/video_decoder_config.h"
#include "media/webm/webm_constants.h"
namespace media {
WebMVideoClient::WebMVideoClient(const LogCB& log_cb)
: log_cb_(log_cb) {
Reset();
}
WebMVideoClient::~WebMVideoClient() {
}
void WebMVideoClient::Reset() {
pixel_width_ = -1;
pixel_height_ = -1;
crop_bottom_ = -1;
crop_top_ = -1;
crop_left_ = -1;
crop_right_ = -1;
display_width_ = -1;
display_height_ = -1;
display_unit_ = -1;
alpha_mode_ = -1;
}
bool WebMVideoClient::InitializeConfig(
const std::string& codec_id, const std::vector<uint8>& codec_private,
bool is_encrypted, VideoDecoderConfig* config) {
DCHECK(config);
VideoCodec video_codec = kUnknownVideoCodec;
VideoCodecProfile profile = VIDEO_CODEC_PROFILE_UNKNOWN;
if (codec_id == "V_VP8") {
video_codec = kCodecVP8;
profile = VP8PROFILE_MAIN;
} else if (codec_id == "V_VP9") {
video_codec = kCodecVP9;
profile = VP9PROFILE_MAIN;
} else {
MEDIA_LOG(log_cb_) << "Unsupported video codec_id " << codec_id;
return false;
}
VideoFrame::Format format =
(alpha_mode_ == 1) ? VideoFrame::YV12A : VideoFrame::YV12;
if (pixel_width_ <= 0 || pixel_height_ <= 0)
return false;
// Set crop and display unit defaults if these elements are not present.
if (crop_bottom_ == -1)
crop_bottom_ = 0;
if (crop_top_ == -1)
crop_top_ = 0;
if (crop_left_ == -1)
crop_left_ = 0;
if (crop_right_ == -1)
crop_right_ = 0;
if (display_unit_ == -1)
display_unit_ = 0;
gfx::Size coded_size(pixel_width_, pixel_height_);
gfx::Rect visible_rect(crop_top_, crop_left_,
pixel_width_ - (crop_left_ + crop_right_),
pixel_height_ - (crop_top_ + crop_bottom_));
gfx::Size natural_size = coded_size;
if (display_unit_ == 0) {
if (display_width_ <= 0)
display_width_ = pixel_width_;
if (display_height_ <= 0)
display_height_ = pixel_height_;
natural_size = gfx::Size(display_width_, display_height_);
} else if (display_unit_ == 3) {
if (display_width_ <= 0 || display_height_ <= 0)
return false;
natural_size = gfx::Size(display_width_, display_height_);
} else {
MEDIA_LOG(log_cb_) << "Unsupported display unit type " << display_unit_;
return false;
}
const uint8* extra_data = NULL;
size_t extra_data_size = 0;
if (codec_private.size() > 0) {
extra_data = &codec_private[0];
extra_data_size = codec_private.size();
}
config->Initialize(
video_codec, profile, format, coded_size, visible_rect, natural_size,
extra_data, extra_data_size, is_encrypted, true);
return config->IsValidConfig();
}
bool WebMVideoClient::OnUInt(int id, int64 val) {
int64* dst = NULL;
switch (id) {
case kWebMIdPixelWidth:
dst = &pixel_width_;
break;
case kWebMIdPixelHeight:
dst = &pixel_height_;
break;
case kWebMIdPixelCropTop:
dst = &crop_top_;
break;
case kWebMIdPixelCropBottom:
dst = &crop_bottom_;
break;
case kWebMIdPixelCropLeft:
dst = &crop_left_;
break;
case kWebMIdPixelCropRight:
dst = &crop_right_;
break;
case kWebMIdDisplayWidth:
dst = &display_width_;
break;
case kWebMIdDisplayHeight:
dst = &display_height_;
break;
case kWebMIdDisplayUnit:
dst = &display_unit_;
break;
case kWebMIdAlphaMode:
dst = &alpha_mode_;
break;
default:
return true;
}
if (*dst != -1) {
MEDIA_LOG(log_cb_) << "Multiple values for id " << std::hex << id
<< " specified (" << *dst << " and " << val << ")";
return false;
}
*dst = val;
return true;
}
bool WebMVideoClient::OnBinary(int id, const uint8* data, int size) {
// Accept binary fields we don't care about for now.
return true;
}
bool WebMVideoClient::OnFloat(int id, double val) {
// Accept float fields we don't care about for now.
return true;
}
} // namespace media

View File

@ -1,61 +0,0 @@
// Copyright (c) 2013 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_VIDEO_CLIENT_H_
#define MEDIA_WEBM_WEBM_VIDEO_CLIENT_H_
#include <string>
#include <vector>
#include "media/base/media_log.h"
#include "media/webm/webm_parser.h"
namespace media {
class VideoDecoderConfig;
// Helper class used to parse a Video element inside a TrackEntry element.
class WebMVideoClient : public WebMParserClient {
public:
explicit WebMVideoClient(const LogCB& log_cb);
virtual ~WebMVideoClient();
// Reset this object's state so it can process a new video track element.
void Reset();
// Initialize |config| with the data in |codec_id|, |codec_private|,
// |is_encrypted| and the fields parsed from the last video track element this
// object was used to parse.
// Returns true if |config| was successfully initialized.
// Returns false if there was unexpected values in the provided parameters or
// video track element fields. The contents of |config| are undefined in this
// case and should not be relied upon.
bool InitializeConfig(const std::string& codec_id,
const std::vector<uint8>& codec_private,
bool is_encrypted,
VideoDecoderConfig* config);
private:
// WebMParserClient implementation.
virtual bool OnUInt(int id, int64 val) OVERRIDE;
virtual bool OnBinary(int id, const uint8* data, int size) OVERRIDE;
virtual bool OnFloat(int id, double val) OVERRIDE;
LogCB log_cb_;
int64 pixel_width_;
int64 pixel_height_;
int64 crop_bottom_;
int64 crop_top_;
int64 crop_left_;
int64 crop_right_;
int64 display_width_;
int64 display_height_;
int64 display_unit_;
int64 alpha_mode_;
DISALLOW_COPY_AND_ASSIGN(WebMVideoClient);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_VIDEO_CLIENT_H_

View File

@ -1,78 +0,0 @@
// Copyright (c) 2013 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_webvtt_parser.h"
namespace media {
void WebMWebVTTParser::Parse(const uint8* payload, int payload_size,
std::string* id,
std::string* settings,
std::string* content) {
WebMWebVTTParser parser(payload, payload_size);
parser.Parse(id, settings, content);
}
WebMWebVTTParser::WebMWebVTTParser(const uint8* payload, int payload_size)
: ptr_(payload),
ptr_end_(payload + payload_size) {
}
void WebMWebVTTParser::Parse(std::string* id,
std::string* settings,
std::string* content) {
ParseLine(id);
ParseLine(settings);
content->assign(ptr_, ptr_end_);
}
bool WebMWebVTTParser::GetByte(uint8* byte) {
if (ptr_ >= ptr_end_)
return false; // indicates end-of-stream
*byte = *ptr_++;
return true;
}
void WebMWebVTTParser::UngetByte() {
--ptr_;
}
void WebMWebVTTParser::ParseLine(std::string* line) {
line->clear();
// Consume characters from the stream, until we reach end-of-line.
// The WebVTT spec states that lines may be terminated in any of the following
// three ways:
// LF
// CR
// CR LF
// The spec is here:
// http://wiki.webmproject.org/webm-metadata/temporal-metadata/webvtt-in-webm
enum {
kLF = '\x0A',
kCR = '\x0D'
};
for (;;) {
uint8 byte;
if (!GetByte(&byte) || byte == kLF)
return;
if (byte == kCR) {
if (GetByte(&byte) && byte != kLF)
UngetByte();
return;
}
line->push_back(byte);
}
}
} // namespace media

View File

@ -1,49 +0,0 @@
// Copyright (c) 2013 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_WEBVTT_PARSER_H_
#define MEDIA_WEBM_WEBM_WEBVTT_PARSER_H_
#include <string>
#include "base/basictypes.h"
#include "media/base/media_export.h"
namespace media {
class MEDIA_EXPORT WebMWebVTTParser {
public:
// Utility function to parse the WebVTT cue from a byte stream.
static void Parse(const uint8* payload, int payload_size,
std::string* id,
std::string* settings,
std::string* content);
private:
// The payload is the embedded WebVTT cue, stored in a WebM block.
// The parser treats this as a UTF-8 byte stream.
WebMWebVTTParser(const uint8* payload, int payload_size);
// Parse the cue identifier, settings, and content from the stream.
void Parse(std::string* id, std::string* settings, std::string* content);
// Remove a byte from the stream, advancing the stream pointer.
// Returns true if a character was returned; false means "end of stream".
bool GetByte(uint8* byte);
// Backup the stream pointer.
void UngetByte();
// Parse a line of text from the stream.
void ParseLine(std::string* line);
// Represents the portion of the stream that has not been consumed yet.
const uint8* ptr_;
const uint8* const ptr_end_;
DISALLOW_COPY_AND_ASSIGN(WebMWebVTTParser);
};
} // namespace media
#endif // MEDIA_WEBM_WEBM_WEBVTT_PARSER_H_

View File

@ -1,105 +0,0 @@
// Copyright (c) 2013 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_webvtt_parser.h"
#include "testing/gmock/include/gmock/gmock.h"
#include "testing/gtest/include/gtest/gtest.h"
using ::testing::InSequence;
namespace media {
typedef std::vector<uint8> Cue;
static Cue EncodeCue(const std::string& id,
const std::string& settings,
const std::string& content) {
const std::string result = id + '\n' + settings + '\n' + content;
const uint8* const buf = reinterpret_cast<const uint8*>(result.data());
return Cue(buf, buf + result.length());
}
static void DecodeCue(const Cue& cue,
std::string* id,
std::string* settings,
std::string* content) {
WebMWebVTTParser::Parse(&cue[0], static_cast<int>(cue.size()),
id, settings, content);
}
class WebMWebVTTParserTest : public testing::Test {
public:
WebMWebVTTParserTest() {}
};
TEST_F(WebMWebVTTParserTest, Blank) {
InSequence s;
const Cue cue = EncodeCue("", "", "Subtitle");
std::string id, settings, content;
DecodeCue(cue, &id, &settings, &content);
EXPECT_EQ(id, "");
EXPECT_EQ(settings, "");
EXPECT_EQ(content, "Subtitle");
}
TEST_F(WebMWebVTTParserTest, Id) {
InSequence s;
for (int i = 1; i <= 9; ++i) {
const std::string idsrc(1, '0'+i);
const Cue cue = EncodeCue(idsrc, "", "Subtitle");
std::string id, settings, content;
DecodeCue(cue, &id, &settings, &content);
EXPECT_EQ(id, idsrc);
EXPECT_EQ(settings, "");
EXPECT_EQ(content, "Subtitle");
}
}
TEST_F(WebMWebVTTParserTest, Settings) {
InSequence s;
enum { kSettingsCount = 4 };
const char* const settings_str[kSettingsCount] = {
"vertical:lr",
"line:50%",
"position:42%",
"vertical:rl line:42% position:100%" };
for (int i = 0; i < kSettingsCount; ++i) {
const Cue cue = EncodeCue("", settings_str[i], "Subtitle");
std::string id, settings, content;
DecodeCue(cue, &id, &settings, &content);
EXPECT_EQ(id, "");
EXPECT_EQ(settings, settings_str[i]);
EXPECT_EQ(content, "Subtitle");
}
}
TEST_F(WebMWebVTTParserTest, Content) {
InSequence s;
enum { kContentCount = 4 };
const char* const content_str[kContentCount] = {
"Subtitle",
"Another Subtitle",
"Yet Another Subtitle",
"Another Subtitle\nSplit Across Two Lines" };
for (int i = 0; i < kContentCount; ++i) {
const Cue cue = EncodeCue("", "", content_str[i]);
std::string id, settings, content;
DecodeCue(cue, &id, &settings, &content);
EXPECT_EQ(id, "");
EXPECT_EQ(settings, "");
EXPECT_EQ(content, content_str[i]);
}
}
} // namespace media