Skip to content

Commit

Permalink
Checking in media::FFmpegGlue and some common FFmpeg code.
Browse files Browse the repository at this point in the history
FFmpegGlue acts as an adapter between FFmpeg's URLProtocol and the media::DataSource interface, allowing us to use media::DataSource implementations for handling FFmpeg's IO.

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

git-svn-id: svn://svn.chromium.org/chrome/trunk/src@11345 0039d316-1c4b-4281-b951-d872f2087c98
  • Loading branch information
scherkus@chromium.org committed Mar 10, 2009
1 parent dafd23c commit 048539b
Show file tree
Hide file tree
Showing 9 changed files with 656 additions and 10 deletions.
1 change: 1 addition & 0 deletions media/DEPS
Original file line number Diff line number Diff line change
@@ -1,2 +1,3 @@
include_rules = [
"+third_party/ffmpeg/include",
]
35 changes: 29 additions & 6 deletions media/base/mock_media_filters.h
Original file line number Diff line number Diff line change
Expand Up @@ -37,6 +37,7 @@ 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),
Expand All @@ -52,6 +53,7 @@ struct MockFilterConfig {
}

MockDataSourceBehavior data_source_behavior;
char data_source_value;
bool has_video;
size_t video_width;
size_t video_height;
Expand All @@ -76,7 +78,16 @@ class MockDataSource : public DataSource {

explicit MockDataSource(const MockFilterConfig* config)
: config_(config),
position_(0) {
position_(0),
deleted_(NULL) {
}

MockDataSource(const MockFilterConfig* config, bool* deleted)
: config_(config),
position_(0),
deleted_(deleted) {
EXPECT_TRUE(deleted);
EXPECT_FALSE(*deleted);
}

// Implementation of MediaFilter.
Expand Down Expand Up @@ -121,7 +132,7 @@ class MockDataSource : public DataSource {
if (size < read) {
read = size;
}
memset(data, 0, read);
memset(data, config_->data_source_value, read);
return read;
}

Expand All @@ -131,8 +142,6 @@ 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;
}
Expand All @@ -141,12 +150,22 @@ class MockDataSource : public DataSource {
}

virtual bool GetSize(int64* size_out) {
*size_out = config_->media_total_bytes;
if (config_->media_total_bytes >= 0) {
*size_out = config_->media_total_bytes;
return true;
}
return false;
}

// Simple position getter for unit testing.
int64 position() const { return position_; }

private:
virtual ~MockDataSource() {}
virtual ~MockDataSource() {
if (deleted_) {
*deleted_ = true;
}
}

void TaskBehavior() {
switch (config_->data_source_behavior) {
Expand All @@ -166,6 +185,10 @@ 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);
};

Expand Down
20 changes: 18 additions & 2 deletions media/build/media.vcproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Configuration
Name="Debug|Win32"
ConfigurationType="4"
InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops"
InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
Expand Down Expand Up @@ -69,7 +69,7 @@
<Configuration
Name="Release|Win32"
ConfigurationType="4"
InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops"
InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
Expand Down Expand Up @@ -212,6 +212,22 @@
RelativePath="..\filters\audio_renderer_impl.h"
>
</File>
<File
RelativePath="..\filters\ffmpeg_common.cc"
>
</File>
<File
RelativePath="..\filters\ffmpeg_common.h"
>
</File>
<File
RelativePath="..\filters\ffmpeg_glue.cc"
>
</File>
<File
RelativePath="..\filters\ffmpeg_glue.h"
>
</File>
<File
RelativePath="..\filters\file_data_source.cc"
>
Expand Down
8 changes: 6 additions & 2 deletions media/build/media_unittests.vcproj
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@
<Configuration
Name="Debug|Win32"
ConfigurationType="1"
InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
InheritedPropertySheets="$(SolutionDir)..\build\debug.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
Expand Down Expand Up @@ -79,7 +79,7 @@
<Configuration
Name="Release|Win32"
ConfigurationType="1"
InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops"
InheritedPropertySheets="$(SolutionDir)..\build\release.vsprops;$(SolutionDir)..\testing\using_gtest.vsprops;$(SolutionDir)..\third_party\ffmpeg\using_ffmpeg.vsprops"
>
<Tool
Name="VCPreBuildEventTool"
Expand Down Expand Up @@ -191,6 +191,10 @@
<Filter
Name="filters"
>
<File
RelativePath="..\filters\ffmpeg_glue_unittest.cc"
>
</File>
<File
RelativePath="..\filters\file_data_source_unittest.cc"
>
Expand Down
18 changes: 18 additions & 0 deletions media/filters/ffmpeg_common.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
// 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
33 changes: 33 additions & 0 deletions media/filters/ffmpeg_common.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,33 @@
// 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
162 changes: 162 additions & 0 deletions media/filters/ffmpeg_glue.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,162 @@
// 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
Loading

0 comments on commit 048539b

Please sign in to comment.