blob: bd5590151fdf83319be9fb9cfbc7a0f5799cf7f1 [file] [log] [blame]
// Copyright (c) 2011 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_glue.h"
#include "base/logging.h"
#include "base/stringprintf.h"
#include "media/base/filters.h"
#include "media/ffmpeg/ffmpeg_common.h"
namespace media {
static FFmpegURLProtocol* ToProtocol(void* data) {
return reinterpret_cast<FFmpegURLProtocol*>(data);
}
// FFmpeg protocol interface.
static int OpenContext(URLContext* h, const char* filename, int flags) {
FFmpegURLProtocol* protocol;
FFmpegGlue::GetInstance()->GetProtocol(filename, &protocol);
if (!protocol)
return AVERROR(EIO);
h->priv_data = protocol;
h->flags = URL_RDONLY;
h->is_streamed = protocol->IsStreaming();
return 0;
}
static int ReadContext(URLContext* h, unsigned char* buf, int size) {
FFmpegURLProtocol* protocol = ToProtocol(h->priv_data);
int result = protocol->Read(size, buf);
if (result < 0)
result = AVERROR(EIO);
return result;
}
#if LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(52, 68, 0)
static int WriteContext(URLContext* h, const unsigned char* buf, int size) {
#else
static int WriteContext(URLContext* h, unsigned char* buf, int size) {
#endif
// We don't support writing.
return AVERROR(EIO);
}
static int64 SeekContext(URLContext* h, int64 offset, int whence) {
FFmpegURLProtocol* protocol = ToProtocol(h->priv_data);
int64 new_offset = AVERROR(EIO);
switch (whence) {
case SEEK_SET:
if (protocol->SetPosition(offset))
protocol->GetPosition(&new_offset);
break;
case SEEK_CUR:
int64 pos;
if (!protocol->GetPosition(&pos))
break;
if (protocol->SetPosition(pos + offset))
protocol->GetPosition(&new_offset);
break;
case SEEK_END:
int64 size;
if (!protocol->GetSize(&size))
break;
if (protocol->SetPosition(size + offset))
protocol->GetPosition(&new_offset);
break;
case AVSEEK_SIZE:
protocol->GetSize(&new_offset);
break;
default:
NOTREACHED();
}
if (new_offset < 0)
new_offset = AVERROR(EIO);
return new_offset;
}
static int CloseContext(URLContext* h) {
h->priv_data = NULL;
return 0;
}
static int LockManagerOperation(void** lock, enum AVLockOp op) {
switch (op) {
case AV_LOCK_CREATE:
*lock = new base::Lock();
if (!*lock)
return 1;
return 0;
case AV_LOCK_OBTAIN:
static_cast<base::Lock*>(*lock)->Acquire();
return 0;
case AV_LOCK_RELEASE:
static_cast<base::Lock*>(*lock)->Release();
return 0;
case AV_LOCK_DESTROY:
delete static_cast<base::Lock*>(*lock);
*lock = NULL;
return 0;
}
return 1;
}
// Use the HTTP protocol to avoid any file path separator issues.
static const char kProtocol[] = "http";
// Fill out our FFmpeg protocol definition.
static URLProtocol kFFmpegURLProtocol = {
kProtocol,
&OpenContext,
&ReadContext,
&WriteContext,
&SeekContext,
&CloseContext,
};
FFmpegGlue::FFmpegGlue() {
// Before doing anything disable logging as it interferes with layout tests.
av_log_set_level(AV_LOG_QUIET);
// Register our protocol glue code with FFmpeg.
avcodec_init();
av_register_protocol2(&kFFmpegURLProtocol, sizeof(kFFmpegURLProtocol));
av_lockmgr_register(&LockManagerOperation);
// Now register the rest of FFmpeg.
av_register_all();
}
FFmpegGlue::~FFmpegGlue() {
av_lockmgr_register(NULL);
}
// static
FFmpegGlue* FFmpegGlue::GetInstance() {
return Singleton<FFmpegGlue>::get();
}
// static
URLProtocol* FFmpegGlue::url_protocol() {
return &kFFmpegURLProtocol;
}
std::string FFmpegGlue::AddProtocol(FFmpegURLProtocol* protocol) {
base::AutoLock auto_lock(lock_);
std::string key = GetProtocolKey(protocol);
if (protocols_.find(key) == protocols_.end()) {
protocols_[key] = protocol;
}
return key;
}
void FFmpegGlue::RemoveProtocol(FFmpegURLProtocol* protocol) {
base::AutoLock auto_lock(lock_);
for (ProtocolMap::iterator cur, iter = protocols_.begin();
iter != protocols_.end();) {
cur = iter;
iter++;
if (cur->second == protocol)
protocols_.erase(cur);
}
}
void FFmpegGlue::GetProtocol(const std::string& key,
FFmpegURLProtocol** protocol) {
base::AutoLock auto_lock(lock_);
ProtocolMap::iterator iter = protocols_.find(key);
if (iter == protocols_.end()) {
*protocol = NULL;
return;
}
*protocol = iter->second;
}
std::string FFmpegGlue::GetProtocolKey(FFmpegURLProtocol* protocol) {
// Use the FFmpegURLProtocol's memory address to generate the unique string.
// This also has the nice property that adding the same FFmpegURLProtocol
// reference will not generate duplicate entries.
return base::StringPrintf("%s://%p", kProtocol, static_cast<void*>(protocol));
}
} // namespace media