0

Added IsEndOfStream and IsDiscontiguous flags to buffers.

Review URL: http://codereview.chromium.org/27120

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@10406 0039d316-1c4b-4281-b951-d872f2087c98
This commit is contained in:
ralphl@chromium.org
2009-02-25 23:53:16 +00:00
parent 24062acc46
commit a28e0e48a6
8 changed files with 169 additions and 109 deletions

@ -1,4 +1,4 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Copyright (c) 2008-2009 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.
@ -36,20 +36,61 @@ namespace media {
class StreamSample : public base::RefCountedThreadSafe<StreamSample> {
public:
// Returns the timestamp of this buffer in microseconds.
virtual base::TimeDelta GetTimestamp() const = 0;
base::TimeDelta GetTimestamp() const {
return timestamp_;
}
// Returns the duration of this buffer in microseconds.
virtual base::TimeDelta GetDuration() const = 0;
base::TimeDelta GetDuration() const {
return duration_;
}
// Indicates that the sample is the last one in the stream.
bool IsEndOfStream() const {
return end_of_stream_;
}
// Indicates that this sample is discontinuous from the previous one, for
// example, following a seek.
bool IsDiscontinuous() const {
return discontinuous_;
}
// Sets the timestamp of this buffer in microseconds.
virtual void SetTimestamp(const base::TimeDelta& timestamp) = 0;
void SetTimestamp(const base::TimeDelta& timestamp) {
timestamp_ = timestamp;
}
// Sets the duration of this buffer in microseconds.
virtual void SetDuration(const base::TimeDelta& duration) = 0;
void SetDuration(const base::TimeDelta& duration) {
duration_ = duration;
}
// Sets the value returned by IsEndOfStream().
void SetEndOfStream(bool end_of_stream) {
end_of_stream_ = end_of_stream;
}
// Sets the value returned by IsDiscontinuous().
void SetDiscontinuous(bool discontinuous) {
discontinuous_ = discontinuous;
}
protected:
friend class base::RefCountedThreadSafe<StreamSample>;
StreamSample()
: end_of_stream_(false),
discontinuous_(false) {
}
virtual ~StreamSample() {}
base::TimeDelta timestamp_;
base::TimeDelta duration_;
bool end_of_stream_;
bool discontinuous_;
private:
DISALLOW_COPY_AND_ASSIGN(StreamSample);
};
@ -134,8 +175,8 @@ class VideoFrame : public StreamSample {
//
// TODO(scherkus): rethink the Assignable interface -- it's a bit kludgy.
template <class BufferType>
class Assignable :
public base::RefCountedThreadSafe< Assignable<BufferType> > {
class Assignable
: public base::RefCountedThreadSafe< Assignable<BufferType> > {
public:
// Assigns a buffer to the owner.
virtual void SetBuffer(BufferType* buffer) = 0;
@ -146,7 +187,7 @@ class Assignable :
// TODO(scherkus): figure out a solution to friending a template.
// See http://www.comeaucomputing.com/techtalk/templates/#friendclassT for
// an explanation.
//protected:
// protected:
// friend class base::RefCountedThreadSafe< Assignable<class T> >;
virtual ~Assignable() {}
};

@ -1,4 +1,4 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Copyright (c) 2008-2009 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.
@ -12,34 +12,18 @@ DataBuffer::DataBuffer(char* data, size_t buffer_size, size_t data_size,
const base::TimeDelta& duration)
: data_(data),
buffer_size_(buffer_size),
data_size_(data_size),
timestamp_(timestamp),
duration_(duration) {
data_size_(data_size) {
DCHECK(data);
DCHECK(buffer_size >= 0);
DCHECK(data_size <= buffer_size);
SetTimestamp(timestamp);
SetDuration(duration);
}
DataBuffer::~DataBuffer() {
delete [] data_;
}
base::TimeDelta DataBuffer::GetTimestamp() const {
return timestamp_;
}
void DataBuffer::SetTimestamp(const base::TimeDelta& timestamp) {
timestamp_ = timestamp;
}
base::TimeDelta DataBuffer::GetDuration() const {
return duration_;
}
void DataBuffer::SetDuration(const base::TimeDelta& duration) {
duration_ = duration;
}
const char* DataBuffer::GetData() const {
return data_;
}

@ -1,4 +1,4 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Copyright (c) 2008-2009 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.
@ -19,12 +19,6 @@ class DataBuffer : public WritableBuffer {
DataBuffer(char* data, size_t buffer_size, size_t data_size,
const base::TimeDelta& timestamp, const base::TimeDelta& duration);
// StreamSample implementation.
virtual base::TimeDelta GetTimestamp() const;
virtual void SetTimestamp(const base::TimeDelta& timestamp);
virtual base::TimeDelta GetDuration() const;
virtual void SetDuration(const base::TimeDelta& duration);
// Buffer implementation.
virtual const char* GetData() const;
virtual size_t GetDataSize() const;
@ -41,8 +35,6 @@ class DataBuffer : public WritableBuffer {
char* data_;
size_t buffer_size_;
size_t data_size_;
base::TimeDelta timestamp_;
base::TimeDelta duration_;
};
} // namespace media

@ -1,4 +1,4 @@
// Copyright (c) 2006-2008 The Chromium Authors. All rights reserved.
// Copyright (c) 2008-2009 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.
@ -34,6 +34,8 @@ TEST(DataBufferTest, Basic) {
// Test StreamSample implementation.
EXPECT_TRUE(kTimestampA == buffer->GetTimestamp());
EXPECT_TRUE(kDurationA == buffer->GetDuration());
EXPECT_FALSE(buffer->IsEndOfStream());
EXPECT_FALSE(buffer->IsDiscontinuous());
buffer->SetTimestamp(kTimestampB);
buffer->SetDuration(kDurationB);
EXPECT_TRUE(kTimestampB == buffer->GetTimestamp());
@ -51,4 +53,13 @@ TEST(DataBufferTest, Basic) {
EXPECT_EQ(kNewDataSize, copied + 1);
buffer->SetDataSize(kNewDataSize);
EXPECT_EQ(kNewDataSize, buffer->GetDataSize());
buffer->SetEndOfStream(true);
EXPECT_TRUE(buffer->IsEndOfStream());
buffer->SetEndOfStream(false);
EXPECT_FALSE(buffer->IsEndOfStream());
buffer->SetDiscontinuous(true);
EXPECT_TRUE(buffer->IsDiscontinuous());
buffer->SetDiscontinuous(false);
EXPECT_FALSE(buffer->IsDiscontinuous());
}

@ -45,7 +45,8 @@ struct MockFilterConfig {
uncompressed_audio_mime_type(mime_type::kUncompressedAudio),
compressed_video_mime_type(mime_type::kH264AnnexB),
uncompressed_video_mime_type(mime_type::kUncompressedVideo),
frame_duration(base::TimeDelta::FromMicroseconds(33333)) {
frame_duration(base::TimeDelta::FromMicroseconds(33333)),
media_duration(base::TimeDelta::FromSeconds(5)) {
}
MockDataSourceBehavior data_source_behavior;
@ -58,10 +59,11 @@ struct MockFilterConfig {
std::string compressed_video_mime_type;
std::string uncompressed_video_mime_type;
base::TimeDelta frame_duration;
base::TimeDelta media_duration;
};
class MockDataSource : public media::DataSource {
class MockDataSource : public DataSource {
public:
static FilterFactory* CreateFactory(const MockFilterConfig* config) {
return new FilterFactoryImpl1<MockDataSource,
@ -123,8 +125,8 @@ class MockDataSource : public media::DataSource {
}
private:
friend class media::FilterFactoryImpl1<MockDataSource,
const MockFilterConfig*>;
friend class FilterFactoryImpl1<MockDataSource,
const MockFilterConfig*>;
explicit MockDataSource(const MockFilterConfig* config)
: behavior_(config->data_source_behavior) {
@ -165,7 +167,7 @@ class MockDemuxer : public Demuxer {
virtual void Stop() {}
// Implementation of Demuxer.
virtual bool Initialize(media::DataSource* data_source) {
virtual bool Initialize(DataSource* data_source) {
host_->InitializationComplete();
return true;
}
@ -177,7 +179,7 @@ class MockDemuxer : public Demuxer {
return 1;
}
virtual media::DemuxerStream* GetStream(int stream_id) {
virtual DemuxerStream* GetStream(int stream_id) {
switch (stream_id) {
case 0:
return &mock_audio_stream_;
@ -193,7 +195,7 @@ class MockDemuxer : public Demuxer {
}
private:
friend class media::FilterFactoryImpl1<MockDemuxer, const MockFilterConfig*>;
friend class FilterFactoryImpl1<MockDemuxer, const MockFilterConfig*>;
explicit MockDemuxer(const MockFilterConfig* config)
: config_(config),
@ -214,7 +216,7 @@ class MockDemuxer : public Demuxer {
// Implementation of DemuxerStream.
virtual const MediaFormat* GetMediaFormat() {
return &media_format_; // TODO(ralphl): implement
return &media_format_;
}
virtual void Read(Assignable<Buffer>* buffer) {
@ -266,8 +268,7 @@ class MockAudioDecoder : public AudioDecoder {
}
private:
friend class media::FilterFactoryImpl1<MockAudioDecoder,
const MockFilterConfig*>;
friend class FilterFactoryImpl1<MockAudioDecoder, const MockFilterConfig*>;
explicit MockAudioDecoder(const MockFilterConfig* config) {
media_format_.SetAsString(MediaFormat::kMimeType,
@ -306,8 +307,7 @@ class MockAudioRenderer : public AudioRenderer {
virtual void SetVolume(float volume) {}
private:
friend class media::FilterFactoryImpl1<MockAudioRenderer,
const MockFilterConfig*>;
friend class FilterFactoryImpl1<MockAudioRenderer, const MockFilterConfig*>;
explicit MockAudioRenderer(const MockFilterConfig* config) {}
@ -323,44 +323,54 @@ class MockVideoFrame : public VideoFrame {
explicit MockVideoFrame(const MockFilterConfig* config,
base::TimeDelta timestamp)
: config_(config),
surface_locked_(false),
timestamp_(timestamp),
duration_(config->frame_duration) {
surface_locked_(false) {
SetTimestamp(timestamp);
SetDuration(config->frame_duration);
size_t y_byte_count = config_->video_width * config_->video_height;
size_t uv_byte_count = y_byte_count / 4;
surface_.format = config_->video_surface_format;
surface_.width = config_->video_width;
surface_.height = config_->video_height;
surface_.planes = 3;
surface_.data[0] = new char[y_byte_count];
surface_.data[1] = new char[uv_byte_count];
surface_.data[2] = new char[uv_byte_count];
surface_.strides[0] = config_->video_width;
surface_.strides[1] = config_->video_width / 2;
surface_.strides[2] = config_->video_width / 2;
memset(surface_.data[0], 0, y_byte_count);
memset(surface_.data[1], 0x80, uv_byte_count);
memset(surface_.data[2], 0x80, uv_byte_count);
int64 num_white_pixels = y_byte_count *
timestamp.InMicroseconds() /
config_->media_duration.InMicroseconds();
if (num_white_pixels > y_byte_count) {
ADD_FAILURE();
num_white_pixels = y_byte_count;
}
if (num_white_pixels < 0) {
ADD_FAILURE();
num_white_pixels = 0;
}
memset(surface_.data[0], 0xFF, static_cast<size_t>(num_white_pixels));
}
virtual ~MockVideoFrame() {}
virtual base::TimeDelta GetTimestamp() const {
return timestamp_;
}
virtual base::TimeDelta GetDuration() const {
return duration_;
}
virtual void SetTimestamp(const base::TimeDelta& timestamp) {
timestamp_ = timestamp;
}
virtual void SetDuration(const base::TimeDelta& duration) {
duration_ = duration;
virtual ~MockVideoFrame() {
delete[] surface_.data[0];
delete[] surface_.data[1];
delete[] surface_.data[2];
}
virtual bool Lock(VideoSurface* surface) {
EXPECT_FALSE(surface_locked_);
if (surface_locked_) {
memset(surface, 0, sizeof(*surface));
return false;
}
surface_locked_ = true;
surface->format = config_->video_surface_format;
surface->width = config_->video_width;
surface->height = config_->video_height;
// TODO(ralphl): mock the data for video surfaces too.
surface->planes = 3;
surface->data[0] = NULL;
surface->data[1] = NULL;
surface->data[2] = NULL;
surface->strides[0] = 0;
surface->strides[1] = 0;
surface->strides[2] = 0;
return false;
DCHECK(sizeof(*surface) == sizeof(surface_));
memcpy(surface, &surface_, sizeof(*surface));
return true;
}
virtual void Unlock() {
@ -371,8 +381,7 @@ class MockVideoFrame : public VideoFrame {
private:
const MockFilterConfig* config_;
bool surface_locked_;
base::TimeDelta timestamp_;
base::TimeDelta duration_;
VideoSurface surface_;
DISALLOW_COPY_AND_ASSIGN(MockVideoFrame);
};
@ -407,8 +416,7 @@ class MockVideoDecoder : public VideoDecoder {
}
private:
friend class media::FilterFactoryImpl1<MockVideoDecoder,
const MockFilterConfig*>;
friend class FilterFactoryImpl1<MockVideoDecoder, const MockFilterConfig*>;
explicit MockVideoDecoder(const MockFilterConfig* config)
: config_(config) {
@ -419,10 +427,15 @@ class MockVideoDecoder : public VideoDecoder {
}
void DoRead(Assignable<VideoFrame>* buffer) {
VideoFrame* frame = new MockVideoFrame(config_, mock_frame_time_);
mock_frame_time_ += config_->frame_duration;
buffer->SetBuffer(frame);
buffer->OnAssignment();
if (mock_frame_time_ < config_->media_duration) {
VideoFrame* frame = new MockVideoFrame(config_, mock_frame_time_);
mock_frame_time_ += config_->frame_duration;
if (mock_frame_time_ >= config_->media_duration) {
frame->SetEndOfStream(true);
}
buffer->SetBuffer(frame);
buffer->OnAssignment();
}
buffer->Release();
}
@ -459,8 +472,7 @@ class MockVideoRenderer : public VideoRenderer {
}
private:
friend class media::FilterFactoryImpl1<MockVideoRenderer,
const MockFilterConfig*>;
friend class FilterFactoryImpl1<MockVideoRenderer, const MockFilterConfig*>;
explicit MockVideoRenderer(const MockFilterConfig* config)
: config_(config) {
@ -474,7 +486,6 @@ class MockVideoRenderer : public VideoRenderer {
};
//------------------------------------------------------------------------------
// Simple class that derives from the WaitableEvent class. The event remains
// in the reset state until the initialization complete callback is called from

@ -31,6 +31,9 @@ class TestVideoRenderer : public VideoRendererBase {
scoped_refptr<VideoFrame> frame;
GetCurrentFrame(&frame);
if (frame.get()) {
VideoSurface video_surface;
EXPECT_TRUE(frame->Lock(&video_surface));
frame->Unlock();
if (frame != last_frame_) {
++unique_frames_;
last_frame_ = frame;

@ -178,24 +178,18 @@ void VideoRendererBase::OnAssignment(VideoFrame* video_frame) {
{
AutoLock auto_lock(lock_);
if (IsRunning()) {
// TODO(ralphl): if (!preroll_complete_ && EndOfStream) call_init = true
// and preroll_complete_ = true.
// TODO(ralphl): If(Seek()) then discard but we don't have SeekFrame().
if (false) {
// TODO(ralphl): this is the seek() logic.
if (video_frame->IsDiscontinuous()) {
DiscardAllFrames();
++number_of_reads_needed_;
PostSubmitReadsTask();
} else {
if (UpdateQueue(host_->GetPipelineStatus()->GetInterpolatedTime(),
video_frame)) {
request_repaint = preroll_complete_;
}
if (!preroll_complete_ && queue_.size() == number_of_frames_) {
preroll_complete_ = true;
call_initialized = true;
request_repaint = true;
}
}
if (UpdateQueue(host_->GetPipelineStatus()->GetInterpolatedTime(),
video_frame)) {
request_repaint = preroll_complete_;
}
if (!preroll_complete_ && (queue_.size() == number_of_frames_ ||
video_frame->IsEndOfStream())) {
preroll_complete_ = true;
call_initialized = true;
request_repaint = true;
}
}
}

@ -52,3 +52,27 @@ TEST(VideoRenderer, CreateTestRenderer) {
EXPECT_LT(test_renderer->unique_frames(), num_expected_frames + 3);
}
TEST(VideoRenderer, SingleVideoFrame) {
base::TimeDelta test_time = base::TimeDelta::FromMilliseconds(100);
std::string url("");
PipelineImpl p;
scoped_refptr<TestVideoRenderer> test_renderer = new TestVideoRenderer();
MockFilterConfig config;
config.media_duration = config.frame_duration;
scoped_refptr<FilterFactoryCollection> c = new FilterFactoryCollection();
c->AddFactory(MockDataSource::CreateFactory(&config));
c->AddFactory(MockDemuxer::CreateFactory(&config));
c->AddFactory(MockAudioDecoder::CreateFactory(&config));
c->AddFactory(MockAudioRenderer::CreateFactory(&config));
c->AddFactory(MockVideoDecoder::CreateFactory(&config));
c->AddFactory(new InstanceFilterFactory<TestVideoRenderer>(test_renderer));
media::InitializationHelper h;
h.Start(&p, c, url);
h.TimedWait(base::TimeDelta::FromSeconds(1));
EXPECT_TRUE(p.IsInitialized());
p.SetPlaybackRate(1.0f);
PlatformThread::Sleep(static_cast<int>(test_time.InMilliseconds()));
p.Stop();
EXPECT_EQ(test_renderer->unique_frames(), 1u);
EXPECT_EQ(test_renderer->paint_called(), 1u);
}