blob: fb394d23a5637fe07de99f0aac641c2a6d1c31e0 [file] [log] [blame]
// Copyright 2016 The Fuchsia Authors. All rights reserved.
// Use of this source code is governed by a BSD-style license that can be
// found in the LICENSE file.
#ifndef GARNET_BIN_MEDIAPLAYER_FFMPEG_AV_IO_CONTEXT_H_
#define GARNET_BIN_MEDIAPLAYER_FFMPEG_AV_IO_CONTEXT_H_
#include <condition_variable>
#include <mutex>
#include "garnet/bin/mediaplayer/demux/reader.h"
#include "lib/async/cpp/task.h"
#include "lib/fxl/logging.h"
#include "lib/fxl/synchronization/thread_annotations.h"
extern "C" {
#include "libavformat/avio.h"
}
namespace media_player {
struct AVIOContextDeleter {
void operator()(AVIOContext* context) const;
};
using AvIoContextPtr = std::unique_ptr<AVIOContext, AVIOContextDeleter>;
class AvIoContext {
public:
// Creates an ffmpeg avio_context for a given reader.
static Result Create(std::shared_ptr<Reader> reader,
AvIoContextPtr* context_ptr_out,
async_dispatcher_t* dispatcher);
};
// 'Opaque' context bound to ffmpeg AVIOContext.
//
// AvIoContextOpaque is instantiated when an AVIOContext is created and is bound
// to the AVIOContext via the 'opaque' field. AvIoContextOpaque's purpose is to
// translate read and seek requests from ffmpeg into terms that make sense for
// the framework. The principal issue is that ffmpeg issues synchronous read and
// seek requests (static Read and Seek below), and the framework exposes these
// capabilities as an asynchronous request (Reader::ReadAt).
//
// AvIoContextOpaque implements synchronous Read requests by issuing an
// asynchronous request and waiting for the callback to be invoked. The wait is
// done using a mutex and a condition_variable. There's no attempt to pump and
// message queues during the wait, so the the ReadAt callback will be on a
// different thread than the synchronous request.
class AvIoContextOpaque {
public:
// Performs a read operation using the signature required for avio.
static int Read(void* opaque, uint8_t* buf, int buf_size);
// Performs a seek operation using the signature required for avio.
static int64_t Seek(void* opaque, int64_t offset, int whence);
~AvIoContextOpaque();
private:
AvIoContextOpaque(std::shared_ptr<Reader> reader,
async_dispatcher_t* dispatcher);
// Indicates whether the reader can seek
bool can_seek() { return can_seek_; }
// Performs a synchronous read.
int Read(uint8_t* buffer, size_t bytes_to_read);
// Performs a synchronous seek.
int64_t Seek(int64_t offset, int whence);
// TODO(US-452): Re-enable thread safety analysis once unique_lock has proper
// annotations.
void WaitForCallback() FXL_NO_THREAD_SAFETY_ANALYSIS {
std::unique_lock<std::mutex> locker(mutex_);
while (!callback_happened_) {
condition_variable_.wait(locker);
}
callback_happened_ = false;
}
void CallbackComplete() {
std::lock_guard<std::mutex> locker(mutex_);
FXL_DCHECK(!callback_happened_);
callback_happened_ = true;
condition_variable_.notify_all();
}
std::shared_ptr<Reader> reader_;
Result describe_result_;
int64_t size_;
bool can_seek_;
int64_t position_ = 0;
std::mutex mutex_;
std::condition_variable condition_variable_;
bool callback_happened_ FXL_GUARDED_BY(mutex_) = false;
// For posting calls to FIDL thread.
async_dispatcher_t* dispatcher_;
friend class AvIoContext;
};
} // namespace media_player
#endif // GARNET_BIN_MEDIAPLAYER_FFMPEG_AV_IO_CONTEXT_H_