summaryrefslogtreecommitdiffstats
diff options
context:
space:
mode:
-rw-r--r--media/DEPS1
-rw-r--r--media/base/mock_media_filters.h35
-rw-r--r--media/base/pipeline.h7
-rw-r--r--media/build/media.vcproj28
-rw-r--r--media/build/media_unittests.vcproj12
-rw-r--r--media/filters/ffmpeg_common.cc18
-rw-r--r--media/filters/ffmpeg_common.h33
-rw-r--r--media/filters/ffmpeg_demuxer.cc281
-rw-r--r--media/filters/ffmpeg_demuxer.h127
-rw-r--r--media/filters/ffmpeg_demuxer_unittest.cc360
-rw-r--r--media/filters/ffmpeg_glue.cc162
-rw-r--r--media/filters/ffmpeg_glue.h80
-rw-r--r--media/filters/ffmpeg_glue_unittest.cc309
13 files changed, 11 insertions, 1442 deletions
diff --git a/media/DEPS b/media/DEPS
index 2b2d7d8..48e8875 100644
--- a/media/DEPS
+++ b/media/DEPS
@@ -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.
-}