Android播放视频从解码到显示实质也是BufferQueue的生产消费的过程,如下图所示:
其中生产者是Surface,消费者是SurfaceFlinger。
本文主要针对Surface进行分析,理清ANativeWindow 和 Surface之间的关系。
ANativeWindow的定义如下:
// 代码位置 frameworks/native/libs/nativewindow/include/system/window.h
...
struct ANativeWindow
{
...
int (*dequeueBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer** buffer);
...
int (*queueBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer);
...
int (*query)(const struct ANativeWindow* window,
int what, int* value);
...
int (*perform)(struct ANativeWindow* window,
int operation, ... );
...
int (*cancelBuffer_DEPRECATED)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer);
...
int (*dequeueBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer** buffer, int* fenceFd);
...
int (*queueBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer, int fenceFd);
...
int (*cancelBuffer)(struct ANativeWindow* window,
struct ANativeWindowBuffer* buffer, int fenceFd);
}
...
static inline int native_window_set_buffer_count(
struct ANativeWindow* window,
size_t bufferCount)
{
return window->perform(window, NATIVE_WINDOW_SET_BUFFER_COUNT, bufferCount);
}
...
static inline int native_window_dequeue_buffer_and_wait(ANativeWindow *anw,
struct ANativeWindowBuffer** anb) {
return anw->dequeueBuffer_DEPRECATED(anw, anb);
}
...
Surface的定义如下:
// frameworks/native/libs/gui/include/gui/Surface.h
...
class Surface
: public ANativeObjectBase
{
public:
...
可见Surface是ANativeWindow的子类
再看如下代码:
// frameworks/native/libs/gui/Surface.cpp
...
Surface::Surface(const sp& bufferProducer, bool controlledByApp)
: mGraphicBufferProducer(bufferProducer),
mCrop(Rect::EMPTY_RECT),
mBufferAge(0),
mGenerationNumber(0),
mSharedBufferMode(false),
mAutoRefresh(false),
mSharedBufferSlot(BufferItem::INVALID_BUFFER_SLOT),
mSharedBufferHasBeenQueued(false),
mQueriedSupportedTimestamps(false),
mFrameTimestampsSupportsPresent(false),
mEnableFrameTimestamps(false),
mFrameEventHistory(std::make_unique()) {
// Initialize the ANativeWindow function pointers.
ANativeWindow::setSwapInterval = hook_setSwapInterval;
ANativeWindow::dequeueBuffer = hook_dequeueBuffer;
ANativeWindow::cancelBuffer = hook_cancelBuffer;
ANativeWindow::queueBuffer = hook_queueBuffer;
ANativeWindow::query = hook_query;
ANativeWindow::perform = hook_perform;
ANativeWindow::dequeueBuffer_DEPRECATED = hook_dequeueBuffer_DEPRECATED;
ANativeWindow::cancelBuffer_DEPRECATED = hook_cancelBuffer_DEPRECATED;
ANativeWindow::lockBuffer_DEPRECATED = hook_lockBuffer_DEPRECATED;
ANativeWindow::queueBuffer_DEPRECATED = hook_queueBuffer_DEPRECATED;
...
Surface构造时,会对ANativeWindow定义的那些函数进行初始化,hook_xxx表示钩子函数,说明ANativeWindow真正的实现是在Surface里面。
以MediaCodec为例分析一下申请解码输出buffer到送显示的过程,这两个过程也是生产者dequeue(申请buffer)和queue(送显示)的过程。
Dequeue
// frameworks/av/media/libstagefright/ACodec.cpp
...
status_t ACodec::allocateOutputBuffersFromNativeWindow() {
// This method only handles the non-metadata mode (or simulating legacy
// mode with metadata, which is transparent to ACodec).
CHECK(!storingMetadataInDecodedBuffers());
OMX_U32 bufferCount, bufferSize, minUndequeuedBuffers;
status_t err = configureOutputBuffersFromNativeWindow(
&bufferCount, &bufferSize, &minUndequeuedBuffers, true /* preregister */);
if (err != 0)
return err;
mNumUndequeuedBuffers = minUndequeuedBuffers;
static_cast(mNativeWindow.get())
->getIGraphicBufferProducer()->allowAllocation(true);
ALOGV("[%s] Allocating %u buffers from a native window of size %u on "
"output port",
mComponentName.c_str(), bufferCount, bufferSize);
// Dequeue buffers and send them to OMX
for (OMX_U32 i = 0; i < bufferCount; i++) {
ANativeWindowBuffer *buf;
int fenceFd;
err = mNativeWindow->dequeueBuffer(mNativeWindow.get(), &buf, &fenceFd);
if (err != 0) {
ALOGE("dequeueBuffer failed: %s (%d)", strerror(-err), -err);
break;
}
sp graphicBuffer(GraphicBuffer::from(buf));
BufferInfo info;
info.mStatus = BufferInfo::OWNED_BY_US;
info.mFenceFd = fenceFd;
info.mIsReadFence = false;
info.mRenderInfo = NULL;
info.mGraphicBuffer = graphicBuffer;
info.mNewGraphicBuffer = false;
// TODO: We shouln't need to create MediaCodecBuffer. In metadata mode
// OMX doesn't use the shared memory buffer, but some code still
// access info.mData. Create an ABuffer as a placeholder.
info.mData = new MediaCodecBuffer(mOutputFormat, new ABuffer(bufferSize));
info.mCodecData = info.mData;
...
// frameworks/native/libs/gui/Surface.cpp
...
int Surface::hook_dequeueBuffer(ANativeWindow* window,
ANativeWindowBuffer** buffer, int* fenceFd) {
Surface* c = getSelf(window);
return c->dequeueBuffer(buffer, fenceFd);
}
...
int Surface::dequeueBuffer(android_native_buffer_t** buffer, int* fenceFd) {
ATRACE_CALL();
ALOGV("Surface::dequeueBuffer");
uint32_t reqWidth;
uint32_t reqHeight;
PixelFormat reqFormat;
uint64_t reqUsage;
bool enableFrameTimestamps;
{
Mutex::Autolock lock(mMutex);
if (mReportRemovedBuffers) {
mRemovedBuffers.clear();
}
reqWidth = mReqWidth ? mReqWidth : mUserWidth;
reqHeight = mReqHeight ? mReqHeight : mUserHeight;
reqFormat = mReqFormat;
reqUsage = mReqUsage;
...
FrameEventHistoryDelta frameTimestamps;
status_t result = mGraphicBufferProducer->dequeueBuffer(&buf, &fence, reqWidth,
reqHeight, reqFormat,
reqUsage, &mBufferAge,
enableFrameTimestamps ?
&frameTimestamps:
nullptr);
...
ACodec中allocateOutputBuffersFromNativeWindow调用mNativeWindow->dequeueBuffer,通过Surface的hook_dequeueBuffer最终调用到Surface的dequeueBuffer,最后mGraphicBufferProducer->dequeueBuffer。这个mGraphicBufferProducer的具体实现就是一个BufferQueue,到此可以知道解码申请输出缓存的时候是通过Surface从BufferQueue中dequeue具体数目的匿名共享buffer进行解码显示轮转。
Queue
// frameworks/av/media/libstagefright/ACodec.cpp
...
void ACodec::BaseState::onOutputBufferDrained(const sp &msg) {
IOMX::buffer_id bufferID;
CHECK(msg->findInt32("buffer-id", (int32_t*)&bufferID));
sp obj;
CHECK(msg->findObject("buffer", &obj));
sp buffer = static_cast(obj.get());
int32_t discarded = 0;
msg->findInt32("discarded", &discarded);
...
info->mData = buffer;
int32_t render;
if (mCodec->mNativeWindow != NULL
&& msg->findInt32("render", &render) && render != 0
&& !discarded && buffer->size() != 0) {
ATRACE_NAME("render");
// The client wants this buffer to be rendered.
android_native_rect_t crop;
if (buffer->format()->findRect("crop", &crop.left, &crop.top, &crop.right, &crop.bottom)) {
// NOTE: native window uses extended right-bottom coordinate
++crop.right;
++crop.bottom;
if (memcmp(&crop, &mCodec->mLastNativeWindowCrop, sizeof(crop)) != 0) {
mCodec->mLastNativeWindowCrop = crop;
status_t err = native_window_set_crop(mCodec->mNativeWindow.get(), &crop);
ALOGW_IF(err != NO_ERROR, "failed to set crop: %d", err);
}
}
...
info->checkReadFence("onOutputBufferDrained before queueBuffer");
err = mCodec->mNativeWindow->queueBuffer(
mCodec->mNativeWindow.get(), info->mGraphicBuffer.get(), info->mFenceFd);
...
// frameworks/native/libs/gui/Surface.cpp
...
int Surface::hook_queueBuffer(ANativeWindow* window,
ANativeWindowBuffer* buffer, int fenceFd) {
Surface* c = getSelf(window);
return c->queueBuffer(buffer, fenceFd);
}
...
int Surface::queueBuffer(android_native_buffer_t* buffer, int fenceFd) {
ATRACE_CALL();
ALOGV("Surface::queueBuffer");
Mutex::Autolock lock(mMutex);
int64_t timestamp;
bool isAutoTimestamp = false;
...
nsecs_t now = systemTime();
status_t err = mGraphicBufferProducer->queueBuffer(i, input, &output);
mLastQueueDuration = systemTime() - now;
...
ACodec的onOutputBufferDrained调用mNativeWindow->queueBuffer,通过Surface的hook_queueBuffer最终调用到Surface的queueBuffer,最后调用mGraphicBufferProducer->queueBuffer完成向BufferQueue送显示帧的过程。
以上代码均来源于Android Pie工程,通过这几段代码期望能大体了解Android视频解码到现实的基本流程
小结: