Commit 4614245a authored by chcunningham's avatar chcunningham Committed by Commit bot

Parsing of encoded duration for unencrypted opus streams.

BUG=396634

Review URL: https://codereview.chromium.org/883403002

Cr-Commit-Position: refs/heads/master@{#315395}
parent 089a7dad
......@@ -554,6 +554,8 @@ test("media_unittests") {
"formats/common/offset_byte_queue_unittest.cc",
"formats/webm/cluster_builder.cc",
"formats/webm/cluster_builder.h",
"formats/webm/opus_packet_builder.cc",
"formats/webm/opus_packet_builder.h",
"formats/webm/tracks_builder.cc",
"formats/webm/tracks_builder.h",
"formats/webm/webm_cluster_parser_unittest.cc",
......
......@@ -8,6 +8,7 @@
#include <sstream>
#include <string>
#include "base/logging.h"
#include "base/memory/ref_counted.h"
#include "base/memory/scoped_ptr.h"
#include "media/base/media_export.h"
......@@ -36,6 +37,11 @@ class LogHelper {
#define MEDIA_LOG(log_cb) LogHelper(log_cb).stream()
// Logs only while count < max. Increments count for each log. Use LAZY_STREAM
// to avoid wasteful evaluation of subsequent stream arguments.
#define LIMITED_MEDIA_LOG(log_cb, count, max) \
LAZY_STREAM(MEDIA_LOG(log_cb), (count) < (max) && ((count)++ || true))
class MEDIA_EXPORT MediaLog : public base::RefCountedThreadSafe<MediaLog> {
public:
// Convert various enums to strings.
......
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#include "base/logging.h"
#include "media/formats/webm/opus_packet_builder.h"
#include "media/formats/webm/webm_cluster_parser.h"
namespace media {
OpusPacket::OpusPacket(uint8_t config, uint8_t frame_count, bool is_VBR) {
DCHECK_GE(config, 0);
DCHECK_LT(config, kNumPossibleOpusConfigs);
DCHECK_GE(frame_count, kMinOpusPacketFrameCount);
DCHECK_LE(frame_count, kMaxOpusPacketFrameCount);
duration_ms_ = frame_count *
WebMClusterParser::kOpusFrameDurationsMu[config] /
static_cast<float>(1000);
uint8_t frame_count_code;
uint8_t frame_count_byte;
if (frame_count == 1) {
frame_count_code = 0;
} else if (frame_count == 2) {
frame_count_code = is_VBR ? 2 : 1;
} else {
frame_count_code = 3;
frame_count_byte = (is_VBR ? 1 << 7 : 0) | frame_count;
}
// All opus packets must have TOC byte.
uint8_t opus_toc_byte = (config << 3) | frame_count_code;
data_.push_back(opus_toc_byte);
// For code 3 packets, the number of frames is signaled in the "frame
// count byte".
if (frame_count_code == 3) {
data_.push_back(frame_count_byte);
}
// Packet will only conform to layout specification for the TOC byte
// and optional frame count bytes appended above. This last byte
// is purely dummy padding where frame size data or encoded data might
// otherwise start.
data_.push_back(static_cast<uint8_t>(0));
}
OpusPacket::~OpusPacket() {
}
const uint8_t* OpusPacket::data() const {
return &(data_[0]);
}
int OpusPacket::size() const {
return data_.size();
}
double OpusPacket::duration_ms() const {
return duration_ms_;
}
ScopedVector<OpusPacket> BuildAllOpusPackets() {
ScopedVector<OpusPacket> opus_packets;
for (int frame_count = kMinOpusPacketFrameCount;
frame_count <= kMaxOpusPacketFrameCount; frame_count++) {
for (int opus_config_num = 0; opus_config_num < kNumPossibleOpusConfigs;
opus_config_num++) {
bool is_VBR = false;
opus_packets.push_back(
new OpusPacket(opus_config_num, frame_count, is_VBR));
if (frame_count >= 2) {
// Add another packet with VBR flag toggled. For frame counts >= 2,
// VBR triggers changes to packet framing.
is_VBR = true;
opus_packets.push_back(
new OpusPacket(opus_config_num, frame_count, is_VBR));
}
}
}
return opus_packets.Pass();
}
} // namespace media
// Copyright (c) 2015 The Chromium Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef MEDIA_FORMATS_WEBM_OPUS_PACKET_BUILDER_H_
#define MEDIA_FORMATS_WEBM_OPUS_PACKET_BUILDER_H_
#include <vector>
#include "base/memory/scoped_ptr.h"
#include "base/memory/scoped_vector.h"
namespace media {
// From Opus RFC. See https://tools.ietf.org/html/rfc6716#page-14
enum OpusConstants {
kNumPossibleOpusConfigs = 32,
kMinOpusPacketFrameCount = 1,
kMaxOpusPacketFrameCount = 48
};
class OpusPacket {
public:
OpusPacket(uint8_t config, uint8_t frame_count, bool is_VBR);
~OpusPacket();
const uint8_t* data() const;
int size() const;
double duration_ms() const;
private:
std::vector<uint8_t> data_;
double duration_ms_;
DISALLOW_COPY_AND_ASSIGN(OpusPacket);
};
// Builds an exhaustive collection of Opus packet configurations.
ScopedVector<OpusPacket> BuildAllOpusPackets();
} // namespace media
#endif // MEDIA_FORMATS_WEBM_OPUS_PACKET_BUILDER_H_
This diff is collapsed.
......@@ -11,6 +11,7 @@
#include <string>
#include "base/memory/scoped_ptr.h"
#include "media/base/audio_decoder_config.h"
#include "media/base/media_export.h"
#include "media/base/media_log.h"
#include "media/base/stream_parser.h"
......@@ -28,13 +29,17 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
// Arbitrarily-chosen numbers to estimate the duration of a buffer if none is
// set and there is not enough information to get a better estimate.
// TODO(wolenetz/acolwell): Parse audio codebook to determine missing audio
// frame durations. See http://crbug.com/351166.
enum {
kDefaultAudioBufferDurationInMs = 23, // Common 1k samples @44.1kHz
kDefaultVideoBufferDurationInMs = 42 // Low 24fps to reduce stalls
};
// Opus packets encode the duration and other parameters in the 5 most
// significant bits of the first byte. The index in this array corresponds
// to the duration of each frame of the packet in microseconds. See
// https://tools.ietf.org/html/rfc6716#page-14
static const uint16_t kOpusFrameDurationsMu[];
private:
// Helper class that manages per-track state.
class Track {
......@@ -87,7 +92,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
// 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;
bool IsKeyframe(const uint8_t* data, int size) const;
base::TimeDelta default_duration() const { return default_duration_; }
......@@ -143,6 +148,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
const std::set<int64>& ignored_tracks,
const std::string& audio_encryption_key_id,
const std::string& video_encryption_key_id,
const AudioCodec audio_codec_,
const LogCB& log_cb);
~WebMClusterParser() override;
......@@ -154,7 +160,7 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
// 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);
int Parse(const uint8_t* buf, int size);
base::TimeDelta cluster_start_time() const { return cluster_start_time_; }
......@@ -194,14 +200,24 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
WebMParserClient* OnListStart(int id) override;
bool OnListEnd(int id) override;
bool OnUInt(int id, int64 val) override;
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 OnBinary(int id, const uint8_t* data, int size) override;
bool ParseBlock(bool is_simple_block,
const uint8_t* buf,
int size,
const uint8_t* additional,
int additional_size,
int duration,
int64 discard_padding);
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,
bool OnBlock(bool is_simple_block,
int track_num,
int timecode,
int duration,
int flags,
const uint8_t* data,
int size,
const uint8_t* additional,
int additional_size,
int64 discard_padding);
// Resets the Track objects associated with each text track.
......@@ -227,21 +243,40 @@ class MEDIA_EXPORT WebMClusterParser : public WebMParserClient {
// if that track num is not a text track.
Track* FindTextTrack(int track_num);
// Attempts to read the duration from the encoded audio data, returning as
// TimeDelta or kNoTimestamp() if duration cannot be retrieved. This obviously
// violates layering rules, but is useful for MSE to know duration in cases
// where it isn't explicitly given and cannot be calculated for Blocks at the
// end of a Cluster (the next Cluster in playback-order may not be the next
// Cluster we parse, so we can't simply use the delta of the first Block in
// the next Cluster). Avoid calling if encrypted; may produce unexpected
// output. See implementation for supported codecs.
base::TimeDelta TryGetEncodedAudioDuration(const uint8_t* data, int size);
// Reads Opus packet header to determine packet duration. Duration returned
// as TimeDelta or kNoTimestamp() upon failure to read duration from packet.
base::TimeDelta ReadOpusDuration(const uint8_t* data, int size);
// Tracks the number of MEDIA_LOGs made in process of reading encoded
// duration. Useful to prevent log spam.
int num_duration_errors_;
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_;
const AudioCodec audio_codec_;
WebMListParser parser_;
int64 last_block_timecode_;
scoped_ptr<uint8[]> block_data_;
scoped_ptr<uint8_t[]> block_data_;
int block_data_size_;
int64 block_duration_;
int64 block_add_id_;
scoped_ptr<uint8[]> block_additional_data_;
scoped_ptr<uint8_t[]> block_additional_data_;
// Must be 0 if |block_additional_data_| is null. Must be > 0 if
// |block_additional_data_| is NOT null.
int block_additional_data_size_;
......
......@@ -240,6 +240,7 @@ int WebMStreamParser::ParseInfoAndTracks(const uint8* data, int size) {
tracks_parser.ignored_tracks(),
tracks_parser.audio_encryption_key_id(),
tracks_parser.video_encryption_key_id(),
audio_config.codec(),
log_cb_));
if (!init_cb_.is_null())
......
......@@ -1257,6 +1257,8 @@
'formats/common/offset_byte_queue_unittest.cc',
'formats/webm/cluster_builder.cc',
'formats/webm/cluster_builder.h',
'formats/webm/opus_packet_builder.cc',
'formats/webm/opus_packet_builder.h',
'formats/webm/tracks_builder.cc',
'formats/webm/tracks_builder.h',
'formats/webm/webm_cluster_parser_unittest.cc',
......
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment