diff options
author | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-10 18:41:52 +0000 |
---|---|---|
committer | scherkus@chromium.org <scherkus@chromium.org@0039d316-1c4b-4281-b951-d872f2087c98> | 2009-03-10 18:41:52 +0000 |
commit | b4fc9c198c85039e15907ccbf99f4d7259d2f085 (patch) | |
tree | 6db743d01149a07b1443f91f7a8ee39a859341ff | |
parent | 11e53f69d052482f9a34b4d67ea6e531a7c0edaf (diff) | |
download | chromium_src-b4fc9c198c85039e15907ccbf99f4d7259d2f085.zip chromium_src-b4fc9c198c85039e15907ccbf99f4d7259d2f085.tar.gz chromium_src-b4fc9c198c85039e15907ccbf99f4d7259d2f085.tar.bz2 |
Revert "Checking in media::FFmpegDemuxer and tests."
Review URL: http://codereview.chromium.org/43032
git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11350 0039d316-1c4b-4281-b951-d872f2087c98
-rw-r--r-- | media/DEPS | 1 | ||||
-rw-r--r-- | media/base/mock_media_filters.h | 35 | ||||
-rw-r--r-- | media/base/pipeline.h | 7 | ||||
-rw-r--r-- | media/build/media.vcproj | 28 | ||||
-rw-r--r-- | media/build/media_unittests.vcproj | 12 | ||||
-rw-r--r-- | media/filters/ffmpeg_common.cc | 18 | ||||
-rw-r--r-- | media/filters/ffmpeg_common.h | 33 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.cc | 281 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer.h | 127 | ||||
-rw-r--r-- | media/filters/ffmpeg_demuxer_unittest.cc | 360 | ||||
-rw-r--r-- | media/filters/ffmpeg_glue.cc | 162 | ||||
-rw-r--r-- | media/filters/ffmpeg_glue.h | 80 | ||||
-rw-r--r-- | media/filters/ffmpeg_glue_unittest.cc | 309 |
13 files changed, 11 insertions, 1442 deletions
@@ -1,3 +1,2 @@ include_rules = [ - "+third_party/ffmpeg/include", ] diff --git a/media/base/mock_media_filters.h b/media/base/mock_media_filters.h index 0620eff..079c681 100644 --- a/media/base/mock_media_filters.h +++ b/media/base/mock_media_filters.h @@ -37,7 +37,6 @@ enum MockDataSourceBehavior { struct MockFilterConfig { MockFilterConfig() : data_source_behavior(MOCK_DATA_SOURCE_NORMAL_INIT), - data_source_value('!'), has_video(true), video_width(1280u), video_height(720u), @@ -53,7 +52,6 @@ struct MockFilterConfig { } MockDataSourceBehavior data_source_behavior; - char data_source_value; bool has_video; size_t video_width; size_t video_height; @@ -78,16 +76,7 @@ class MockDataSource : public DataSource { explicit MockDataSource(const MockFilterConfig* config) : config_(config), - position_(0), - deleted_(NULL) { - } - - MockDataSource(const MockFilterConfig* config, bool* deleted) - : config_(config), - position_(0), - deleted_(deleted) { - EXPECT_TRUE(deleted); - EXPECT_FALSE(*deleted); + position_(0) { } // Implementation of MediaFilter. @@ -132,7 +121,7 @@ class MockDataSource : public DataSource { if (size < read) { read = size; } - memset(data, config_->data_source_value, read); + memset(data, 0, read); return read; } @@ -142,6 +131,8 @@ class MockDataSource : public DataSource { } virtual bool SetPosition(int64 position) { + EXPECT_GE(position, 0u); + EXPECT_LE(position, config_->media_total_bytes); if (position < 0u || position > config_->media_total_bytes) { return false; } @@ -150,22 +141,12 @@ class MockDataSource : public DataSource { } virtual bool GetSize(int64* size_out) { - if (config_->media_total_bytes >= 0) { - *size_out = config_->media_total_bytes; - return true; - } + *size_out = config_->media_total_bytes; return false; } - // Simple position getter for unit testing. - int64 position() const { return position_; } - private: - virtual ~MockDataSource() { - if (deleted_) { - *deleted_ = true; - } - } + virtual ~MockDataSource() {} void TaskBehavior() { switch (config_->data_source_behavior) { @@ -185,10 +166,6 @@ class MockDataSource : public DataSource { int64 position_; MediaFormat media_format_; - // Set to true inside the destructor. Used in FFmpegGlue unit tests for - // testing proper reference counting. - bool* deleted_; - DISALLOW_COPY_AND_ASSIGN(MockDataSource); }; diff --git a/media/base/pipeline.h b/media/base/pipeline.h index 58883cc..da3dfc4 100644 --- a/media/base/pipeline.h +++ b/media/base/pipeline.h @@ -31,12 +31,7 @@ enum PipelineError { PIPELINE_ERROR_REQUIRED_FILTER_MISSING, PIPELINE_ERROR_OUT_OF_MEMORY, PIPELINE_ERROR_COULD_NOT_RENDER, - PIPELINE_ERROR_READ, - - // Demuxer related errors. - DEMUXER_ERROR_COULD_NOT_OPEN, - DEMUXER_ERROR_COULD_NOT_PARSE, - DEMUXER_ERROR_NO_SUPPORTED_STREAMS, + PIPELINE_ERROR_READ }; // Base class for Pipeline class which allows for read-only access to members. diff --git a/media/build/media.vcproj b/media/build/media.vcproj index 37b7977..d0b081f 100644 --- a/media/build/media.vcproj +++ b/media/build/media.vcproj @@ -18,7 +18,7 @@ <Configuration Name="Debug|Win32" ConfigurationType="4" - InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops" + InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops" > <Tool Name="VCPreBuildEventTool" @@ -69,7 +69,7 @@ <Configuration Name="Release|Win32" ConfigurationType="4" - InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops" + InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops" > <Tool Name="VCPreBuildEventTool" @@ -213,30 +213,6 @@ > </File> <File - RelativePath="..\filters\ffmpeg_common.cc" - > - </File> - <File - RelativePath="..\filters\ffmpeg_common.h" - > - </File> - <File - RelativePath="..\filters\ffmpeg_demuxer.cc" - > - </File> - <File - RelativePath="..\filters\ffmpeg_demuxer.h" - > - </File> - <File - RelativePath="..\filters\ffmpeg_glue.cc" - > - </File> - <File - RelativePath="..\filters\ffmpeg_glue.h" - > - </File> - <File RelativePath="..\filters\file_data_source.cc" > </File> diff --git a/media/build/media_unittests.vcproj b/media/build/media_unittests.vcproj index 4e18337..db414e7 100644 --- a/media/build/media_unittests.vcproj +++ b/media/build/media_unittests.vcproj @@ -18,7 +18,7 @@ <Configuration Name="Debug|Win32" ConfigurationType="1" - InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops" + InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops" > <Tool Name="VCPreBuildEventTool" @@ -79,7 +79,7 @@ <Configuration Name="Release|Win32" ConfigurationType="1" - InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops" + InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops" > <Tool Name="VCPreBuildEventTool" @@ -192,14 +192,6 @@ Name="filters" > <File - RelativePath="..\filters\ffmpeg_demuxer_unittest.cc" - > - </File> - <File - RelativePath="..\filters\ffmpeg_glue_unittest.cc" - > - </File> - <File RelativePath="..\filters\file_data_source_unittest.cc" > </File> diff --git a/media/filters/ffmpeg_common.cc b/media/filters/ffmpeg_common.cc deleted file mode 100644 index 80ace45..0000000 --- a/media/filters/ffmpeg_common.cc +++ /dev/null @@ -1,18 +0,0 @@ -// Copyright (c) 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. - -#include "media/filters/ffmpeg_common.h" - -namespace media { - -const char kFFmpegCodecID[] = "FFmpegCodecID"; - -namespace mime_type { - -const char kFFmpegAudio[] = "audio/x-ffmpeg"; -const char kFFmpegVideo[] = "video/x-ffmpeg"; - -} // namespace mime_type - -} // namespace media diff --git a/media/filters/ffmpeg_common.h b/media/filters/ffmpeg_common.h deleted file mode 100644 index baddbb1..0000000 --- a/media/filters/ffmpeg_common.h +++ /dev/null @@ -1,33 +0,0 @@ -// Copyright (c) 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. - -// Used for FFmpeg error codes. -#include <cerrno> - -#include "base/compiler_specific.h" - -// Include FFmpeg header files. -extern "C" { -// Temporarily disable possible loss of data warning. -// TODO(scherkus): fix and upstream the compiler warnings. -MSVC_PUSH_DISABLE_WARNING(4244); -#include "third_party/ffmpeg/include/libavcodec/avcodec.h" -#include "third_party/ffmpeg/include/libavformat/avformat.h" -MSVC_POP_WARNING(); -} // extern "C" - -namespace media { - -// MediaFormat key identifying the CodecID. -extern const char kFFmpegCodecID[]; - -// FFmpeg MIME types. -namespace mime_type { - -extern const char kFFmpegAudio[]; -extern const char kFFmpegVideo[]; - -} // namespace mime_type - -} // namespace media diff --git a/media/filters/ffmpeg_demuxer.cc b/media/filters/ffmpeg_demuxer.cc deleted file mode 100644 index 4344147..0000000 --- a/media/filters/ffmpeg_demuxer.cc +++ /dev/null @@ -1,281 +0,0 @@ -// Copyright (c) 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. - -#include "base/string_util.h" -#include "base/time.h" -#include "media/base/filter_host.h" -#include "media/filters/ffmpeg_common.h" -#include "media/filters/ffmpeg_demuxer.h" -#include "media/filters/ffmpeg_glue.h" - -namespace media { - -// -// AVPacketBuffer -// -class AVPacketBuffer : public Buffer { - public: - AVPacketBuffer(AVPacket* packet, const base::TimeDelta& timestamp, - const base::TimeDelta& duration) - : packet_(packet) { - DCHECK(packet); - SetTimestamp(timestamp); - SetDuration(duration); - } - - virtual ~AVPacketBuffer() { - av_free_packet(packet_.get()); - } - - // Buffer implementation. - virtual const char* GetData() const { - return reinterpret_cast<const char*>(packet_->data); - } - - virtual size_t GetDataSize() const { - return static_cast<size_t>(packet_->size); - } - - private: - scoped_ptr<AVPacket> packet_; - - DISALLOW_COPY_AND_ASSIGN(AVPacketBuffer); -}; - - -// -// FFmpegDemuxerStream -// -FFmpegDemuxerStream::FFmpegDemuxerStream(FFmpegDemuxer* demuxer, - const AVStream& stream) - : demuxer_(demuxer) { - DCHECK(demuxer_); - - // Determine our media format. - switch (stream.codec->codec_type) { - case CODEC_TYPE_AUDIO: - media_format_.SetAsString(MediaFormat::kMimeType, - mime_type::kFFmpegAudio); - media_format_.SetAsInteger(MediaFormat::kChannels, - stream.codec->channels); - media_format_.SetAsInteger(MediaFormat::kSampleRate, - stream.codec->sample_rate); - break; - case CODEC_TYPE_VIDEO: - media_format_.SetAsString(MediaFormat::kMimeType, - mime_type::kFFmpegVideo); - media_format_.SetAsInteger(MediaFormat::kHeight, - stream.codec->height); - media_format_.SetAsInteger(MediaFormat::kWidth, - stream.codec->width); - break; - default: - NOTREACHED(); - break; - } - int codec_id = static_cast<int>(stream.codec->codec_id); - media_format_.SetAsInteger(kFFmpegCodecID, codec_id); - - // Calculate the time base and duration in microseconds. - int64 time_base_us = static_cast<int64>(av_q2d(stream.time_base) * - base::Time::kMicrosecondsPerSecond); - int64 duration_us = static_cast<int64>(time_base_us * stream.duration); - time_base_ = base::TimeDelta::FromMicroseconds(time_base_us); - duration_ = base::TimeDelta::FromMicroseconds(duration_us); -} - -FFmpegDemuxerStream::~FFmpegDemuxerStream() { - // Since |input_queue_| and |output_queue_| use scoped_refptr everything - // should get released. -} - -bool FFmpegDemuxerStream::HasPendingReads() { - AutoLock auto_lock(lock_); - return !output_queue_.empty(); -} - -void FFmpegDemuxerStream::EnqueuePacket(AVPacket* packet) { - base::TimeDelta timestamp = time_base_ * packet->pts; - base::TimeDelta duration = time_base_ * packet->duration; - Buffer* buffer = new AVPacketBuffer(packet, timestamp, duration); - DCHECK(buffer); - { - AutoLock auto_lock(lock_); - input_queue_.push_back(buffer); - } - FulfillPendingReads(); -} - -const MediaFormat* FFmpegDemuxerStream::GetMediaFormat() { - return &media_format_; -} - -void FFmpegDemuxerStream::Read(Assignable<Buffer>* buffer) { - DCHECK(buffer); - { - AutoLock auto_lock(lock_); - output_queue_.push_back(scoped_refptr< Assignable<Buffer> >(buffer)); - } - if (FulfillPendingReads()) { - demuxer_->ScheduleDemux(); - } -} - -bool FFmpegDemuxerStream::FulfillPendingReads() { - bool pending_reads = false; - while (true) { - scoped_refptr<Buffer> buffer_in; - scoped_refptr< Assignable<Buffer> > buffer_out; - { - AutoLock auto_lock(lock_); - pending_reads = !output_queue_.empty(); - if (input_queue_.empty() || output_queue_.empty()) { - break; - } - buffer_in = input_queue_.front(); - buffer_out = output_queue_.front(); - input_queue_.pop_front(); - output_queue_.pop_front(); - } - buffer_out->SetBuffer(buffer_in); - buffer_out->OnAssignment(); - } - return pending_reads; -} - - -// -// FFmpegDemuxer -// -FFmpegDemuxer::FFmpegDemuxer() - : demuxing_(false), - format_context_(NULL) { -} - -FFmpegDemuxer::~FFmpegDemuxer() { - if (format_context_) { - av_free(format_context_); - } - while (!streams_.empty()) { - delete streams_.back(); - streams_.pop_back(); - } -} - -void FFmpegDemuxer::ScheduleDemux() { - if (!demuxing_) { - demuxing_ = true; - host_->PostTask(NewRunnableMethod(this, &FFmpegDemuxer::Demux)); - } -} - -void FFmpegDemuxer::Stop() { - // TODO(scherkus): implement Stop(). - NOTIMPLEMENTED(); -} - -bool FFmpegDemuxer::Initialize(DataSource* data_source) { - // In order to get FFmpeg to use |data_source| for file IO we must transfer - // ownership via FFmpegGlue. We'll add |data_source| to FFmpegGlue and pass - // the resulting key to FFmpeg. FFmpeg will pass the key to FFmpegGlue which - // will take care of attaching |data_source| to an FFmpeg context. After - // we finish initializing the FFmpeg context we can remove |data_source| from - // FFmpegGlue. - // - // Refer to media/filters/ffmpeg_glue.h for details. - - // Add our data source and get our unique key. - std::string key = FFmpegGlue::get()->AddDataSource(data_source); - - // Open FFmpeg AVFormatContext. - DCHECK(!format_context_); - int result = av_open_input_file(&format_context_, key.c_str(), NULL, 0, NULL); - - // Remove our data source. - FFmpegGlue::get()->RemoveDataSource(data_source); - - if (result < 0) { - host_->Error(DEMUXER_ERROR_COULD_NOT_OPEN); - return false; - } - - // Fully initialize AVFormatContext by parsing the stream a little. - result = av_find_stream_info(format_context_); - if (result < 0) { - host_->Error(DEMUXER_ERROR_COULD_NOT_PARSE); - return false; - } - - // Create demuxer streams for all supported streams. - base::TimeDelta max_duration; - for (size_t i = 0; i < format_context_->nb_streams; ++i) { - CodecType codec_type = format_context_->streams[i]->codec->codec_type; - if (codec_type == CODEC_TYPE_AUDIO || codec_type == CODEC_TYPE_VIDEO) { - AVStream* stream = format_context_->streams[i]; - FFmpegDemuxerStream* demuxer_stream - = new FFmpegDemuxerStream(this, *stream); - DCHECK(demuxer_stream); - streams_.push_back(demuxer_stream); - max_duration = std::max(max_duration, demuxer_stream->duration()); - } - } - if (streams_.empty()) { - host_->Error(DEMUXER_ERROR_NO_SUPPORTED_STREAMS); - return false; - } - - // We have at least one supported stream, set the duration and notify we're - // done initializing. - host_->SetDuration(max_duration); - host_->InitializationComplete(); - return true; -} - -size_t FFmpegDemuxer::GetNumberOfStreams() { - return streams_.size(); -} - -DemuxerStream* FFmpegDemuxer::GetStream(int stream) { - DCHECK(stream >= 0); - DCHECK(stream < static_cast<int>(streams_.size())); - return streams_[stream]; -} - -void FFmpegDemuxer::Demux() { - DCHECK(demuxing_); - - // Loop until we've satisfied every stream. - while (StreamsHavePendingReads()) { - // Allocate and read an AVPacket from the media. - scoped_ptr<AVPacket> packet(new AVPacket()); - int result = av_read_frame(format_context_, packet.get()); - if (result < 0) { - // TODO(scherkus): handle end of stream by marking Buffer with the end of - // stream flag. - NOTIMPLEMENTED(); - break; - } - - // Queue the packet with the appropriate stream. - DCHECK(packet->stream_index >= 0); - DCHECK(packet->stream_index < static_cast<int>(streams_.size())); - FFmpegDemuxerStream* demuxer_stream = streams_[packet->stream_index]; - demuxer_stream->EnqueuePacket(packet.release()); - } - - // Finished demuxing. - demuxing_ = false; -} - -bool FFmpegDemuxer::StreamsHavePendingReads() { - StreamVector::iterator iter; - for (iter = streams_.begin(); iter != streams_.end(); ++iter) { - if ((*iter)->HasPendingReads()) { - return true; - } - } - return false; -} - -} // namespace media diff --git a/media/filters/ffmpeg_demuxer.h b/media/filters/ffmpeg_demuxer.h deleted file mode 100644 index 895dd65..0000000 --- a/media/filters/ffmpeg_demuxer.h +++ /dev/null @@ -1,127 +0,0 @@ -// Copyright (c) 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. - -// Implements the Demuxer interface using FFmpeg's libavformat. At this time -// will support demuxing any audio/video format thrown at it. The streams -// output mime types audio/x-ffmpeg and video/x-ffmpeg and include an integer -// key FFmpegCodecID which contains the CodecID enumeration value. The CodecIDs -// can be used to create and initialize the corresponding FFmpeg decoder. -// -// FFmpegDemuxer sets the duration of pipeline during initialization by using -// the duration of the longest audio/video stream. -// -// NOTE: since FFmpegDemuxer reads packets sequentially without seeking, media -// files with very large drift between audio/video streams may result in -// excessive memory consumption. - -#ifndef MEDIA_FILTERS_FFMPEG_DEMUXER_H_ -#define MEDIA_FILTERS_FFMPEG_DEMUXER_H_ - -#include <deque> -#include <vector> - -#include "base/lock.h" -#include "media/base/buffers.h" -#include "media/base/factory.h" -#include "media/base/filters.h" -#include "media/base/media_format.h" - -// FFmpeg forward declarations. -struct AVCodecContext; -struct AVBitStreamFilterContext; -struct AVFormatContext; -struct AVPacket; -struct AVStream; -enum CodecID; - -namespace media { - -class FFmpegDemuxer; - -class FFmpegDemuxerStream : public DemuxerStream { - public: - // Maintains a reference to |demuxer| and initializes itself using information - // inside |stream|. - FFmpegDemuxerStream(FFmpegDemuxer* demuxer, const AVStream& stream); - - virtual ~FFmpegDemuxerStream(); - - // Returns true is this stream has pending reads, false otherwise. - bool HasPendingReads(); - - // Enqueues and takes ownership over the given AVPacket. - void EnqueuePacket(AVPacket* packet); - - // Returns the duration of this stream. - base::TimeDelta duration() { return duration_; } - - // DemuxerStream implementation. - virtual const MediaFormat* GetMediaFormat(); - virtual void Read(Assignable<Buffer>* buffer); - - private: - // Returns true if there are still pending reads. - bool FulfillPendingReads(); - - FFmpegDemuxer* demuxer_; - MediaFormat media_format_; - base::TimeDelta time_base_; - base::TimeDelta duration_; - Lock lock_; - - typedef std::deque< scoped_refptr<Buffer> > InputQueue; - InputQueue input_queue_; - - typedef std::deque< scoped_refptr< Assignable<Buffer> > > OutputQueue; - OutputQueue output_queue_; - - DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxerStream); -}; - -class FFmpegDemuxer : public Demuxer { - public: - // FilterFactory provider. - static FilterFactory* CreateFilterFactory() { - return new FilterFactoryImpl0<FFmpegDemuxer>(); - } - - // Called by FFmpegDemuxerStreams to schedule a Demux() task. - void ScheduleDemux(); - - // MediaFilter implementation. - virtual void Stop(); - - // Demuxer implementation. - virtual bool Initialize(DataSource* data_source); - virtual size_t GetNumberOfStreams(); - virtual DemuxerStream* GetStream(int stream_id); - - private: - // Only allow a factory to create this class. - friend class FilterFactoryImpl0<FFmpegDemuxer>; - FFmpegDemuxer(); - virtual ~FFmpegDemuxer(); - - // Demuxing task scheduled by streams. - void Demux(); - - // Returns true if any of the streams have pending reads. - bool StreamsHavePendingReads(); - - // Flag to prevent multiple Demux() tasks from being scheduled. - bool demuxing_; - - // FFmpeg context handle. - AVFormatContext* format_context_; - - // Vector of streams. - typedef std::vector<FFmpegDemuxerStream*> StreamVector; - StreamVector streams_; - - DISALLOW_COPY_AND_ASSIGN(FFmpegDemuxer); -}; - -} // namespace media - -#endif // MEDIA_FILTERS_FFMPEG_DEMUXER_H_ diff --git a/media/filters/ffmpeg_demuxer_unittest.cc b/media/filters/ffmpeg_demuxer_unittest.cc deleted file mode 100644 index 2c4ecbe..0000000 --- a/media/filters/ffmpeg_demuxer_unittest.cc +++ /dev/null @@ -1,360 +0,0 @@ -// Copyright (c) 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. - -#include <deque> - -#include "media/base/filter_host.h" -#include "media/base/filters.h" -#include "media/base/mock_filter_host.h" -#include "media/base/mock_media_filters.h" -#include "media/filters/ffmpeg_common.h" -#include "media/filters/ffmpeg_demuxer.h" -#include "testing/gtest/include/gtest/gtest.h" - -// FFmpeg mocks to remove dependency on having the DLLs present. -extern "C" { -static const size_t kMaxStreams = 3; -static AVFormatContext g_format; -static AVStream g_streams[kMaxStreams]; -static AVCodecContext g_audio_codec; -static AVCodecContext g_video_codec; -static AVCodecContext g_data_codec; -struct AVPacket g_packet; - -// FFmpeg return codes for various functions. -static int g_av_open_input_file = 0; -static int g_av_find_stream_info = 0; -static int g_av_read_frame = 0; - -// Counts the number of packets "allocated" by av_read_frame and "released" by -// av_free_packet. This should always be zero after everything is cleaned up. -static int g_oustanding_packets = 0; - -int av_open_input_file(AVFormatContext** format, const char* filename, - AVInputFormat* input_format, int buffer_size, - AVFormatParameters* parameters) { - EXPECT_FALSE(input_format) << "AVInputFormat should be NULL."; - EXPECT_FALSE(buffer_size) << "buffer_size should be 0."; - EXPECT_FALSE(parameters) << "AVFormatParameters should be NULL."; - if (g_av_open_input_file < 0) { - *format = NULL; - } else { - *format = &g_format; - } - return g_av_open_input_file; -} - -int av_find_stream_info(AVFormatContext* format) { - EXPECT_EQ(&g_format, format); - return g_av_find_stream_info; -} - -void av_free(void* ptr) { - EXPECT_EQ(&g_format, ptr); -} - -// Our packet destroying function. -void DestructPacket(AVPacket* packet) { - --g_oustanding_packets; -} - -int av_read_frame(AVFormatContext* format, AVPacket* packet) { - EXPECT_EQ(&g_format, format); - memcpy(packet, &g_packet, sizeof(g_packet)); - packet->destruct = &DestructPacket; - if (g_av_read_frame == 0) { - ++g_oustanding_packets; - } - return g_av_read_frame; -} - -} // extern "C" - -using namespace media; - -namespace { - -void InitializeFFmpegMocks() { - // Initialize function return codes. - g_av_open_input_file = 0; - g_av_find_stream_info = 0; - g_av_read_frame = 0; - - // Initialize AVFormatContext structure. - memset(&g_format, 0, sizeof(g_format)); - - // Initialize AVStream structures. - for (size_t i = 0; i < kMaxStreams; ++i) { - memset(&g_streams[i], 0, sizeof(g_streams[i])); - g_streams[i].time_base.den = 1 * base::Time::kMicrosecondsPerSecond; - g_streams[i].time_base.num = 1; - } - - // Initialize AVCodexContext structures. - memset(&g_audio_codec, 0, sizeof(g_audio_codec)); - g_audio_codec.codec_type = CODEC_TYPE_AUDIO; - g_audio_codec.codec_id = CODEC_ID_VORBIS; - g_audio_codec.channels = 2; - g_audio_codec.sample_rate = 44100; - - memset(&g_video_codec, 0, sizeof(g_video_codec)); - g_video_codec.codec_type = CODEC_TYPE_VIDEO; - g_video_codec.codec_id = CODEC_ID_THEORA; - g_video_codec.height = 720; - g_video_codec.width = 1280; - - memset(&g_data_codec, 0, sizeof(g_data_codec)); - g_data_codec.codec_type = CODEC_TYPE_DATA; - g_data_codec.codec_id = CODEC_ID_NONE; - - // Initialize AVPacket structure. - memset(&g_packet, 0, sizeof(g_packet)); -} - -// Simple implementation of Assignable<Buffer> that lets us poke at values. -class TestBuffer : public Assignable<Buffer> { - public: - TestBuffer() : assigned_(false) {} - virtual ~TestBuffer() {} - - // Assignable<Buffer> implementation. - virtual void SetBuffer(Buffer* buffer) { - buffer_ = buffer; - } - - void OnAssignment() { - EXPECT_FALSE(assigned_); - assigned_ = true; - } - - // Mock getters/setters. - Buffer* buffer() { return buffer_; } - bool assigned() { return assigned_; } - - private: - scoped_refptr<Buffer> buffer_; - bool assigned_; -}; - -} // namespace - -TEST(FFmpegDemuxerTest, InitializeFailure) { - InitializeFFmpegMocks(); - - // Get FFmpegDemuxer's filter factory. - scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory(); - - // Should only accept application/octet-stream type. - MediaFormat media_format; - media_format.SetAsString(MediaFormat::kMimeType, "foo/x-bar"); - scoped_refptr<Demuxer> demuxer(factory->Create<Demuxer>(&media_format)); - ASSERT_FALSE(demuxer); - media_format.Clear(); - media_format.SetAsString(MediaFormat::kMimeType, - mime_type::kApplicationOctetStream); - demuxer = factory->Create<Demuxer>(&media_format); - ASSERT_TRUE(demuxer); - - // Prepare a filter host and data source for the demuxer. - MockPipeline pipeline; - scoped_ptr< MockFilterHost<Demuxer> > filter_host; - filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer)); - MockFilterConfig config; - scoped_refptr<MockDataSource> data_source(new MockDataSource(&config)); - - // Simulate av_open_input_fail failing. - g_av_open_input_file = AVERROR_IO; - g_av_find_stream_info = 0; - EXPECT_FALSE(demuxer->Initialize(data_source)); - EXPECT_FALSE(filter_host->IsInitialized()); - EXPECT_EQ(DEMUXER_ERROR_COULD_NOT_OPEN, pipeline.GetError()); - - // Simulate av_find_stream_info failing. - g_av_open_input_file = 0; - g_av_find_stream_info = AVERROR_IO; - demuxer = factory->Create<Demuxer>(&media_format); - filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer)); - EXPECT_FALSE(demuxer->Initialize(data_source)); - EXPECT_FALSE(filter_host->IsInitialized()); - EXPECT_EQ(DEMUXER_ERROR_COULD_NOT_PARSE, pipeline.GetError()); - - // Simulate media with no parseable streams. - InitializeFFmpegMocks(); - demuxer = factory->Create<Demuxer>(&media_format); - filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer)); - EXPECT_FALSE(demuxer->Initialize(data_source)); - EXPECT_FALSE(filter_host->IsInitialized()); - EXPECT_EQ(DEMUXER_ERROR_NO_SUPPORTED_STREAMS, pipeline.GetError()); - - // Simulate media with a data stream but no audio or video streams. - g_format.nb_streams = 1; - g_format.streams[0] = &g_streams[0]; - g_streams[0].codec = &g_data_codec; - g_streams[0].duration = 10; - demuxer = factory->Create<Demuxer>(&media_format); - filter_host.reset(new MockFilterHost<Demuxer>(&pipeline, demuxer)); - EXPECT_FALSE(demuxer->Initialize(data_source)); - EXPECT_FALSE(filter_host->IsInitialized()); - EXPECT_EQ(DEMUXER_ERROR_NO_SUPPORTED_STREAMS, pipeline.GetError()); -} - -TEST(FFmpegDemuxerTest, InitializeStreams) { - // Simulate media with a data stream, a video stream and audio stream. - InitializeFFmpegMocks(); - g_format.nb_streams = 3; - g_format.streams[0] = &g_streams[0]; - g_format.streams[1] = &g_streams[1]; - g_format.streams[2] = &g_streams[2]; - g_streams[0].duration = 1000; - g_streams[0].codec = &g_data_codec; - g_streams[1].duration = 100; - g_streams[1].codec = &g_video_codec; - g_streams[2].duration = 10; - g_streams[2].codec = &g_audio_codec; - - // Create our pipeline. - MockPipeline pipeline; - - // Create our data source. - MockFilterConfig config; - scoped_refptr<MockDataSource> data_source = new MockDataSource(&config); - MockFilterHost<DataSource> filter_host_a(&pipeline, data_source); - EXPECT_TRUE(data_source->Initialize("foo")); - EXPECT_TRUE(filter_host_a.IsInitialized()); - - // Create our demuxer. - scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory(); - scoped_refptr<Demuxer> demuxer - = factory->Create<Demuxer>(data_source->GetMediaFormat()); - EXPECT_TRUE(demuxer); - MockFilterHost<Demuxer> filter_host_b(&pipeline, demuxer); - EXPECT_TRUE(demuxer->Initialize(data_source)); - EXPECT_TRUE(filter_host_b.IsInitialized()); - EXPECT_EQ(PIPELINE_OK, pipeline.GetError()); - - // Since we ignore data streams, the duration should be equal to the video - // stream's duration. - EXPECT_EQ(g_streams[1].duration, pipeline.GetDuration().InMicroseconds()); - - // Verify that 2 out of 3 streams were created. - EXPECT_EQ(2, demuxer->GetNumberOfStreams()); - - // First stream should be video. - DemuxerStream* stream = demuxer->GetStream(0); - ASSERT_TRUE(stream); - const MediaFormat* stream_format = stream->GetMediaFormat(); - std::string mime_type; - int result; - EXPECT_TRUE(stream_format->GetAsString(MediaFormat::kMimeType, &mime_type)); - EXPECT_STREQ(mime_type::kFFmpegVideo, mime_type.c_str()); - EXPECT_TRUE(stream_format->GetAsInteger(kFFmpegCodecID, &result)); - EXPECT_EQ(CODEC_ID_THEORA, static_cast<CodecID>(result)); - EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kHeight, &result)); - EXPECT_EQ(g_video_codec.height, result); - EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kWidth, &result)); - EXPECT_EQ(g_video_codec.width, result); - - // Second stream should be audio. - stream = demuxer->GetStream(1); - ASSERT_TRUE(stream); - stream_format = stream->GetMediaFormat(); - EXPECT_TRUE(stream_format->GetAsString(MediaFormat::kMimeType, &mime_type)); - EXPECT_STREQ(mime_type::kFFmpegAudio, mime_type.c_str()); - EXPECT_TRUE(stream_format->GetAsInteger(kFFmpegCodecID, &result)); - EXPECT_EQ(CODEC_ID_VORBIS, static_cast<CodecID>(result)); - EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kChannels, &result)); - EXPECT_EQ(g_audio_codec.channels, result); - EXPECT_TRUE(stream_format->GetAsInteger(MediaFormat::kSampleRate, &result)); - EXPECT_EQ(g_audio_codec.sample_rate, result); -} - -TEST(FFmpegDemuxerTest, Read) { - // Prepare some test data. - const int kAudio = 0; - const int kVideo = 1; - const size_t kDataSize = 4; - uint8 audio_data[kDataSize] = {0, 1, 2, 3}; - uint8 video_data[kDataSize] = {4, 5, 6, 7}; - - // Simulate media with a an audio stream and video stream. - InitializeFFmpegMocks(); - g_format.nb_streams = 2; - g_format.streams[kAudio] = &g_streams[kAudio]; - g_format.streams[kVideo] = &g_streams[kVideo]; - g_streams[kAudio].duration = 10; - g_streams[kAudio].codec = &g_audio_codec; - g_streams[kVideo].duration = 10; - g_streams[kVideo].codec = &g_video_codec; - - // Create our pipeline. - MockPipeline pipeline; - - // Create our data source. - MockFilterConfig config; - scoped_refptr<MockDataSource> data_source = new MockDataSource(&config); - MockFilterHost<DataSource> filter_host_a(&pipeline, data_source); - EXPECT_TRUE(data_source->Initialize("foo")); - EXPECT_TRUE(filter_host_a.IsInitialized()); - - // Create our demuxer. - scoped_refptr<FilterFactory> factory = FFmpegDemuxer::CreateFilterFactory(); - scoped_refptr<Demuxer> demuxer - = factory->Create<Demuxer>(data_source->GetMediaFormat()); - EXPECT_TRUE(demuxer); - MockFilterHost<Demuxer> filter_host_b(&pipeline, demuxer); - EXPECT_TRUE(demuxer->Initialize(data_source)); - EXPECT_TRUE(filter_host_b.IsInitialized()); - EXPECT_EQ(PIPELINE_OK, pipeline.GetError()); - - // Verify both streams were created. - EXPECT_EQ(2, demuxer->GetNumberOfStreams()); - - // Get our streams. - DemuxerStream* audio_stream = demuxer->GetStream(kAudio); - DemuxerStream* video_stream = demuxer->GetStream(kVideo); - ASSERT_TRUE(audio_stream); - ASSERT_TRUE(video_stream); - - // Prepare our test audio packet. - g_packet.stream_index = kAudio; - g_packet.data = audio_data; - g_packet.size = kDataSize; - - // Attempt a read from the audio stream and run the message loop until done. - scoped_refptr<TestBuffer> buffer(new TestBuffer()); - audio_stream->Read(buffer); - pipeline.RunAllTasks(); - EXPECT_TRUE(buffer->assigned()); - EXPECT_TRUE(buffer->buffer()); - EXPECT_EQ(audio_data, (uint8*)buffer->buffer()->GetData()); - EXPECT_EQ(kDataSize, buffer->buffer()->GetDataSize()); - - // Prepare our test video packet. - g_packet.stream_index = kVideo; - g_packet.data = video_data; - g_packet.size = kDataSize; - - // Attempt a read from the video stream and run the message loop until done. - buffer = new TestBuffer(); - video_stream->Read(buffer); - pipeline.RunAllTasks(); - EXPECT_TRUE(buffer->assigned()); - EXPECT_TRUE(buffer->buffer()); - EXPECT_EQ(video_data, (uint8*)buffer->buffer()->GetData()); - EXPECT_EQ(kDataSize, buffer->buffer()->GetDataSize()); - - // Simulate end of stream. - g_av_read_frame = AVERROR_IO; - - // Attempt a read from the audio stream and run the message loop until done. - buffer = new TestBuffer(); - audio_stream->Read(buffer); - pipeline.RunAllTasks(); - EXPECT_FALSE(buffer->assigned()); - EXPECT_FALSE(buffer->buffer()); - - // Manually release buffer, which should release any remaining AVPackets. - buffer = NULL; - EXPECT_EQ(0, g_oustanding_packets); -} diff --git a/media/filters/ffmpeg_glue.cc b/media/filters/ffmpeg_glue.cc deleted file mode 100644 index 0d3957b..0000000 --- a/media/filters/ffmpeg_glue.cc +++ /dev/null @@ -1,162 +0,0 @@ -// Copyright (c) 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. - -#include "base/string_util.h" -#include "media/base/filters.h" -#include "media/filters/ffmpeg_common.h" -#include "media/filters/ffmpeg_glue.h" - -namespace { - -// FFmpeg protocol interface. -int OpenContext(URLContext* h, const char* filename, int flags) { - scoped_refptr<media::DataSource> data_source; - media::FFmpegGlue::get()->GetDataSource(filename, &data_source); - if (!data_source) - return AVERROR_IO; - - data_source->AddRef(); - h->priv_data = data_source; - h->flags = URL_RDONLY; - // TODO(scherkus): data source should be able to tell us if we're streaming. - h->is_streamed = FALSE; - return 0; -} - -int ReadContext(URLContext* h, unsigned char* buf, int size) { - media::DataSource* data_source = - reinterpret_cast<media::DataSource*>(h->priv_data); - int result = data_source->Read(buf, size); - if (result < 0) - result = AVERROR_IO; - return result; -} - -int WriteContext(URLContext* h, unsigned char* buf, int size) { - // We don't support writing. - return AVERROR_IO; -} - -offset_t SeekContext(URLContext* h, offset_t offset, int whence) { - media::DataSource* data_source = - reinterpret_cast<media::DataSource*>(h->priv_data); - offset_t new_offset = AVERROR_IO; - switch (whence) { - case SEEK_SET: - if (data_source->SetPosition(offset)) - data_source->GetPosition(&new_offset); - break; - - case SEEK_CUR: - int64 pos; - if (!data_source->GetPosition(&pos)) - break; - if (data_source->SetPosition(pos + offset)) - data_source->GetPosition(&new_offset); - break; - - case SEEK_END: - int64 size; - if (!data_source->GetSize(&size)) - break; - if (data_source->SetPosition(size + offset)) - data_source->GetPosition(&new_offset); - break; - - case AVSEEK_SIZE: - data_source->GetSize(&new_offset); - break; - - default: - NOTREACHED(); - } - if (new_offset < 0) - new_offset = AVERROR_IO; - return new_offset; -} - -int CloseContext(URLContext* h) { - media::DataSource* data_source = - reinterpret_cast<media::DataSource*>(h->priv_data); - data_source->Release(); - h->priv_data = NULL; - return 0; -} - -} // namespace - -//------------------------------------------------------------------------------ - -namespace media { - -// Use the HTTP protocol to avoid any file path separator issues. -static const char kProtocol[] = "http"; - -// Fill out our FFmpeg protocol definition. -static URLProtocol kFFmpegProtocol = { - kProtocol, - &OpenContext, - &ReadContext, - &WriteContext, - &SeekContext, - &CloseContext, -}; - -FFmpegGlue::FFmpegGlue() { - // Register our protocol glue code with FFmpeg. - avcodec_init(); - register_protocol(&kFFmpegProtocol); - - // Now register the rest of FFmpeg. - av_register_all(); -} - -FFmpegGlue::~FFmpegGlue() { - DataSourceMap::iterator iter = data_sources_.begin(); - while (iter != data_sources_.end()) { - DataSource* data_source = iter->second; - iter = data_sources_.erase(iter); - } -} - -std::string FFmpegGlue::AddDataSource(DataSource* data_source) { - AutoLock auto_lock(lock_); - std::string key = GetDataSourceKey(data_source); - if (data_sources_.find(key) == data_sources_.end()) { - data_sources_[key] = data_source; - } - return key; -} - -void FFmpegGlue::RemoveDataSource(DataSource* data_source) { - AutoLock auto_lock(lock_); - DataSourceMap::iterator iter = data_sources_.begin(); - while (iter != data_sources_.end()) { - if (iter->second == data_source) { - iter = data_sources_.erase(iter); - } else { - ++iter; - } - } -} - -void FFmpegGlue::GetDataSource(const std::string& key, - scoped_refptr<DataSource>* data_source) { - AutoLock auto_lock(lock_); - DataSourceMap::iterator iter = data_sources_.find(key); - if (iter == data_sources_.end()) { - *data_source = NULL; - return; - } - *data_source = iter->second; -} - -std::string FFmpegGlue::GetDataSourceKey(DataSource* data_source) { - // Use the DataSource's memory address to generate the unique string. This - // also has the nice property that adding the same DataSource reference will - // not generate duplicate entries. - return StringPrintf("%s://0x%lx", kProtocol, static_cast<void*>(data_source)); -} - -} // namespace media diff --git a/media/filters/ffmpeg_glue.h b/media/filters/ffmpeg_glue.h deleted file mode 100644 index 0db1ce8..0000000 --- a/media/filters/ffmpeg_glue.h +++ /dev/null @@ -1,80 +0,0 @@ -// Copyright (c) 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. - -// FFmpegGlue is an adapter for FFmpeg's URLProtocol interface that allows us to -// use a DataSource implementation with FFmpeg. For convenience we use FFmpeg's -// av_open_input_file function, which analyzes the filename given to it and -// automatically initializes the appropriate URLProtocol. -// -// Since the DataSource is already open by time we call av_open_input_file, we -// need a way for av_open_input_file to find the correct DataSource instance. -// The solution is to maintain a map of "filenames" to DataSource instances, -// where filenames are actually just a unique identifier. For simplicity, -// FFmpegGlue is registered as an HTTP handler and generates filenames based on -// the memory address of the DataSource, i.e., http://0xc0bf4870. Since there -// may be multiple FFmpegDemuxers active at one time, FFmpegGlue is a -// thread-safe singleton. -// -// Usage: FFmpegDemuxer adds the DataSource to FFmpegGlue's map and is given a -// filename to pass to av_open_input_file. FFmpegDemuxer calls -// av_open_input_file with the filename, which results in FFmpegGlue returning -// the DataSource as a URLProtocol instance to FFmpeg. Since FFmpegGlue is only -// needed for opening files, when av_open_input_file returns FFmpegDemuxer -// removes the DataSource from FFmpegGlue's map. - -#ifndef MEDIA_FILTERS_FFMPEG_GLUE_H_ -#define MEDIA_FILTERS_FFMPEG_GLUE_H_ - -#include <map> -#include <string> - -#include "base/lock.h" -#include "base/singleton.h" - -// FFmpeg forward declarations. -struct URLContext; -typedef int64 offset_t; - -namespace media { - -class DataSource; - -class FFmpegGlue : public Singleton<FFmpegGlue> { - public: - // Adds a DataSource to the FFmpeg glue layer and returns a unique string that - // can be passed to FFmpeg to identify the data source. - std::string AddDataSource(DataSource* data_source); - - // Removes a DataSource from the FFmpeg glue layer. Using strings from - // previously added DataSources will no longer work. - void RemoveDataSource(DataSource* data_source); - - // Assigns the DataSource identified with by the given key to |data_source|, - // or assigns NULL if no such DataSource could be found. - void GetDataSource(const std::string& key, - scoped_refptr<DataSource>* data_source); - - private: - // Only allow Singleton to create and delete FFmpegGlue. - friend struct DefaultSingletonTraits<FFmpegGlue>; - FFmpegGlue(); - virtual ~FFmpegGlue(); - - // Returns the unique key for this data source, which can be passed to - // av_open_input_file as the filename. - std::string GetDataSourceKey(DataSource* data_source); - - // Mutual exclusion while adding/removing items from the map. - Lock lock_; - - // Map between keys and DataSource references. - typedef std::map< std::string, scoped_refptr<DataSource> > DataSourceMap; - DataSourceMap data_sources_; - - DISALLOW_COPY_AND_ASSIGN(FFmpegGlue); -}; - -} // namespace media - -#endif // MEDIA_FILTERS_FFMPEG_GLUE_H_ diff --git a/media/filters/ffmpeg_glue_unittest.cc b/media/filters/ffmpeg_glue_unittest.cc deleted file mode 100644 index d1d2a83..0000000 --- a/media/filters/ffmpeg_glue_unittest.cc +++ /dev/null @@ -1,309 +0,0 @@ -// Copyright (c) 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. - -#include "media/base/filters.h" -#include "media/base/mock_media_filters.h" -#include "media/filters/ffmpeg_common.h" -#include "media/filters/ffmpeg_glue.h" -#include "testing/gtest/include/gtest/gtest.h" - -// FFmpeg mocks to remove dependency on having the DLLs present. -extern "C" { -static bool g_avcodec_init = false; -static URLProtocol* g_protocol = NULL; -static bool g_av_register_all = false; - -void avcodec_init() { - EXPECT_FALSE(g_avcodec_init); - g_avcodec_init = true; -} - -int register_protocol(URLProtocol* protocol) { - EXPECT_FALSE(g_protocol); - g_protocol = protocol; - return 0; -} - -void av_register_all() { - EXPECT_FALSE(g_av_register_all); - g_av_register_all = true; -} -} // extern "C" - -TEST(FFmpegGlueTest, InitializeFFmpeg) { - // Singleton should initialize FFmpeg. - media::FFmpegGlue* glue = media::FFmpegGlue::get(); - EXPECT_TRUE(glue); - EXPECT_TRUE(g_avcodec_init); - EXPECT_TRUE(g_protocol); - EXPECT_TRUE(g_av_register_all); - - // Make sure URLProtocol was filled out correctly. - EXPECT_STREQ("http", g_protocol->name); - EXPECT_TRUE(g_protocol->url_close); - EXPECT_TRUE(g_protocol->url_open); - EXPECT_TRUE(g_protocol->url_read); - EXPECT_TRUE(g_protocol->url_seek); - EXPECT_TRUE(g_protocol->url_write); -} - -TEST(FFmpegGlueTest, AddRemoveGetDataSource) { - // Prepare testing data. - media::FFmpegGlue* glue = media::FFmpegGlue::get(); - - // Create our data sources and add them to the glue layer. - bool deleted_a = false; - bool deleted_b = false; - media::MockFilterConfig config_a; - media::MockFilterConfig config_b; - scoped_refptr<media::MockDataSource> data_source_a - = new media::MockDataSource(&config_a, &deleted_a); - scoped_refptr<media::MockDataSource> data_source_b - = new media::MockDataSource(&config_b, &deleted_b); - - // Make sure the keys are unique. - std::string key_a = glue->AddDataSource(data_source_a); - std::string key_b = glue->AddDataSource(data_source_b); - EXPECT_EQ(0, key_a.find("http://")); - EXPECT_EQ(0, key_b.find("http://")); - EXPECT_NE(key_a, key_b); - - // Our keys should return our data sources. - scoped_refptr<media::DataSource> data_source_c; - scoped_refptr<media::DataSource> data_source_d; - glue->GetDataSource(key_a, &data_source_c); - glue->GetDataSource(key_b, &data_source_d); - EXPECT_EQ(data_source_a, data_source_c); - EXPECT_EQ(data_source_b, data_source_d); - - // Adding the same DataSource should create the same key and not add an extra - // reference. - std::string key_a2 = glue->AddDataSource(data_source_a); - EXPECT_EQ(key_a, key_a2); - glue->GetDataSource(key_a2, &data_source_c); - EXPECT_EQ(data_source_a, data_source_c); - - // Removes the data sources and then releases our references. They should be - // deleted. - glue->RemoveDataSource(data_source_a); - glue->GetDataSource(key_a, &data_source_c); - EXPECT_FALSE(data_source_c); - glue->GetDataSource(key_b, &data_source_d); - EXPECT_EQ(data_source_b, data_source_d); - glue->RemoveDataSource(data_source_b); - glue->GetDataSource(key_b, &data_source_d); - EXPECT_FALSE(data_source_d); - EXPECT_FALSE(deleted_a); - EXPECT_FALSE(deleted_b); - data_source_a = NULL; - data_source_b = NULL; - EXPECT_TRUE(deleted_a); - EXPECT_TRUE(deleted_b); -} - -TEST(FFmpegGlueTest, OpenClose) { - // Prepare testing data. - media::FFmpegGlue* glue = media::FFmpegGlue::get(); - - // Create our data source and add them to the glue layer. - bool deleted = false; - media::MockFilterConfig config; - scoped_refptr<media::MockDataSource> data_source - = new media::MockDataSource(&config, &deleted); - std::string key = glue->AddDataSource(data_source); - - // Prepare FFmpeg URLContext structure. - URLContext context; - memset(&context, 0, sizeof(context)); - - // Test opening a URLContext with a data source that doesn't exist. - EXPECT_EQ(AVERROR_IO, g_protocol->url_open(&context, "foobar", 0)); - - // Test opening a URLContext with our data source. - EXPECT_EQ(0, g_protocol->url_open(&context, key.c_str(), 0)); - EXPECT_EQ(URL_RDONLY, context.flags); - EXPECT_EQ(data_source, context.priv_data); - EXPECT_FALSE(context.is_streamed); - - // Remove the data source from the glue layer, releasing a reference. - glue->RemoveDataSource(data_source); - EXPECT_FALSE(deleted); - - // Remove our own reference -- URLContext should maintain a reference. - data_source = NULL; - EXPECT_FALSE(deleted); - - // Close the URLContext, which should release the final reference. - EXPECT_EQ(0, g_protocol->url_close(&context)); - EXPECT_TRUE(deleted); -} - -TEST(FFmpegGlueTest, ReadingWriting) { - // Prepare testing data. - media::FFmpegGlue* glue = media::FFmpegGlue::get(); - const size_t kBufferSize = 16; - unsigned char buffer[kBufferSize]; - - // Configure MockDataSource to be 8 characters long and fill reads with - // periods. Therefore our expected string should be a character of 8 periods. - const size_t kExpectedSize = 8; - media::MockFilterConfig config; - config.media_total_bytes = kExpectedSize; - config.data_source_value = '.'; - const char kExpected[] = "........"; - COMPILE_ASSERT(kExpectedSize == (arraysize(kExpected) - 1), string_length); - - // Create our data source and add them to the glue layer. - bool deleted = false; - scoped_refptr<media::MockDataSource> data_source - = new media::MockDataSource(&config, &deleted); - std::string key = glue->AddDataSource(data_source); - - // Open our data source and then remove it from the glue layer. - URLContext context; - memset(&context, 0, sizeof(context)); - EXPECT_EQ(0, g_protocol->url_open(&context, key.c_str(), 0)); - glue->RemoveDataSource(data_source); - EXPECT_FALSE(deleted); - - // Writing should always fail. - EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, NULL, 0)); - EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, buffer, 0)); - EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, buffer, -1)); - EXPECT_EQ(AVERROR_IO, g_protocol->url_write(&context, buffer, kBufferSize)); - EXPECT_EQ(0, data_source->position()); - - // Reading should return same amount of bytes if <= kExpectedSize. - EXPECT_EQ(0, g_protocol->url_read(&context, buffer, 0)); - EXPECT_EQ(kExpectedSize / 2, - g_protocol->url_read(&context, buffer, kExpectedSize / 2)); - EXPECT_EQ(kExpectedSize, - g_protocol->url_read(&context, buffer, kExpectedSize)); - buffer[kExpectedSize] = '\0'; - EXPECT_STREQ(kExpected, reinterpret_cast<char*>(buffer)); - - // Test reading more than kExpectedSize for simulating EOF. - EXPECT_EQ(kExpectedSize, g_protocol->url_read(&context, buffer, kBufferSize)); - buffer[kExpectedSize] = '\0'; - EXPECT_STREQ(kExpected, reinterpret_cast<char*>(buffer)); - - // Close our data source. - EXPECT_EQ(0, g_protocol->url_close(&context)); - EXPECT_FALSE(deleted); - - // Remove our own reference, which should release the final reference. - data_source = NULL; - EXPECT_TRUE(deleted); -} - -TEST(FFmpegGlueTest, Seeking) { - // Prepare testing data. - media::FFmpegGlue* glue = media::FFmpegGlue::get(); - const int64 kSize = 32; - - // Create our data source and add them to the glue layer. - bool deleted = false; - media::MockFilterConfig config; - config.media_total_bytes = kSize; - scoped_refptr<media::MockDataSource> data_source - = new media::MockDataSource(&config, &deleted); - std::string key = glue->AddDataSource(data_source); - - // Open our data source and then remove it from the glue layer. - URLContext context; - memset(&context, 0, sizeof(context)); - EXPECT_EQ(0, g_protocol->url_open(&context, key.c_str(), 0)); - glue->RemoveDataSource(data_source); - EXPECT_FALSE(deleted); - - // Test SEEK_SET operations. - config.media_total_bytes = -1; - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, SEEK_SET)); - - config.media_total_bytes = kSize; - EXPECT_TRUE(data_source->SetPosition(0)); - EXPECT_EQ(0, g_protocol->url_seek(&context, 0, SEEK_SET)); - EXPECT_TRUE(data_source->SetPosition(5)); - EXPECT_EQ(0, g_protocol->url_seek(&context, 0, SEEK_SET)); - EXPECT_EQ(0, data_source->position()); - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, -5, SEEK_SET)); - EXPECT_EQ(0, data_source->position()); - EXPECT_EQ(kSize, g_protocol->url_seek(&context, kSize, SEEK_SET)); - EXPECT_EQ(kSize, data_source->position()); - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, kSize+1, SEEK_SET)); - EXPECT_EQ(kSize, data_source->position()); - - // Test SEEK_CUR operations. - config.media_total_bytes = -1; - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, SEEK_CUR)); - - config.media_total_bytes = kSize; - EXPECT_TRUE(data_source->SetPosition(0)); - EXPECT_EQ(0, g_protocol->url_seek(&context, 0, SEEK_CUR)); - EXPECT_TRUE(data_source->SetPosition(5)); - EXPECT_EQ(5, g_protocol->url_seek(&context, 0, SEEK_CUR)); - EXPECT_EQ(0, g_protocol->url_seek(&context, -5, SEEK_CUR)); - EXPECT_EQ(0, data_source->position()); - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, -1, SEEK_CUR)); - EXPECT_EQ(kSize, g_protocol->url_seek(&context, kSize, SEEK_CUR)); - EXPECT_EQ(kSize, data_source->position()); - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 1, SEEK_CUR)); - EXPECT_EQ(kSize, data_source->position()); - - // Test SEEK_END operations. - config.media_total_bytes = -1; - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, SEEK_END)); - - config.media_total_bytes = kSize; - EXPECT_TRUE(data_source->SetPosition(0)); - EXPECT_EQ(kSize, g_protocol->url_seek(&context, 0, SEEK_END)); - EXPECT_EQ(kSize, data_source->position()); - EXPECT_EQ(kSize-5, g_protocol->url_seek(&context, -5, SEEK_END)); - EXPECT_EQ(kSize-5, data_source->position()); - EXPECT_EQ(0, g_protocol->url_seek(&context, -kSize, SEEK_END)); - EXPECT_EQ(0, data_source->position()); - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 1, SEEK_END)); - EXPECT_EQ(0, data_source->position()); - - // Test AVSEEK_SIZE operation. - config.media_total_bytes = -1; - EXPECT_EQ(AVERROR_IO, g_protocol->url_seek(&context, 0, AVSEEK_SIZE)); - - config.media_total_bytes = kSize; - EXPECT_TRUE(data_source->SetPosition(0)); - EXPECT_EQ(kSize, g_protocol->url_seek(&context, 0, AVSEEK_SIZE)); - - // Close our data source. - EXPECT_EQ(0, g_protocol->url_close(&context)); - EXPECT_FALSE(deleted); - - // Remove our own reference, which should release the final reference. - data_source = NULL; - EXPECT_TRUE(deleted); -} - -TEST(FFmpegGlueTest, Destructor) { - // Prepare testing data. - media::FFmpegGlue* glue = media::FFmpegGlue::get(); - - // We use a static bool since ~FFmpegGlue() will set it to true sometime - // after this function exits. - static bool deleted = false; - - // Create our data source and add them to the glue layer. - media::MockFilterConfig config; - scoped_refptr<media::MockDataSource> data_source - = new media::MockDataSource(&config, &deleted); - std::string key = glue->AddDataSource(data_source); - - // Remove our own reference. - data_source = NULL; - EXPECT_FALSE(deleted); - - // ~FFmpegGlue() will be called when this unit test finishes execution. By - // leaving something inside FFmpegGlue's map we get to test our cleanup code. - // - // MockDataSource will be holding onto a bad MockFilterConfig pointer at this - // point but since no one is calling it everything will be ok. -} |