`
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<ANativeWindow, Surface, RefBase>
-
{
-
public:
-
-
...
可见Surface是ANativeWindow的子类
再看如下代码:
-
// frameworks/native/libs/gui/Surface.cpp
-
-
...
-
-
Surface::
Surface(
const sp<IGraphicBufferProducer>& 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<ProducerFrameEventHistory>()) {
-
// 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<Surface*>(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(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<AMessage> &msg) {
-
IOMX::buffer_id bufferID;
-
CHECK(msg->
findInt32(
"buffer-id", (
int32_t*)&bufferID));
-
sp<RefBase> obj;
-
CHECK(msg->
findObject(
"buffer", &obj));
-
sp<MediaCodecBuffer> buffer =
static_cast<MediaCodecBuffer *>(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视频解码到现实的基本流程
小结:
- ANativeWindow是android的本地窗口,Surface是ANativeWindow的子类,也是ANativeWindow的具体实现。
- IGraphicBufferProducer的具体实现实质就是BufferQueue,创建Surface的时候作为传参。
- 解码和显示共享内存的方式可以节省内存,减小解码到显示消耗时间。