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:
@ -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);
|
||||
}
|
||||
|
Reference in New Issue
Block a user