GraphicBufferSource.cpp revision 656e86250cd68f7f362c50a4bc92a865e9deacbe
1/* 2 * Copyright (C) 2013 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17#define LOG_TAG "GraphicBufferSource" 18//#define LOG_NDEBUG 0 19#include <utils/Log.h> 20 21#include <GraphicBufferSource.h> 22 23#include <OMX_Core.h> 24#include <media/stagefright/foundation/ADebug.h> 25 26#include <MetadataBufferType.h> 27#include <ui/GraphicBuffer.h> 28 29namespace android { 30 31static const bool EXTRA_CHECK = true; 32 33 34GraphicBufferSource::GraphicBufferSource(OMXNodeInstance* nodeInstance, 35 uint32_t bufferWidth, uint32_t bufferHeight, uint32_t bufferCount) : 36 mInitCheck(UNKNOWN_ERROR), 37 mNodeInstance(nodeInstance), 38 mExecuting(false), 39 mNumFramesAvailable(0), 40 mEndOfStream(false), 41 mEndOfStreamSent(false) { 42 43 ALOGV("GraphicBufferSource w=%u h=%u c=%u", 44 bufferWidth, bufferHeight, bufferCount); 45 46 if (bufferWidth == 0 || bufferHeight == 0) { 47 ALOGE("Invalid dimensions %ux%u", bufferWidth, bufferHeight); 48 mInitCheck = BAD_VALUE; 49 return; 50 } 51 52 String8 name("GraphicBufferSource"); 53 54 mBufferQueue = new BufferQueue(true); 55 mBufferQueue->setConsumerName(name); 56 mBufferQueue->setDefaultBufferSize(bufferWidth, bufferHeight); 57 mBufferQueue->setSynchronousMode(true); 58 mBufferQueue->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | 59 GRALLOC_USAGE_HW_TEXTURE); 60 61 mInitCheck = mBufferQueue->setMaxAcquiredBufferCount(bufferCount); 62 if (mInitCheck != NO_ERROR) { 63 ALOGE("Unable to set BQ max acquired buffer count to %u: %d", 64 bufferCount, mInitCheck); 65 return; 66 } 67 68 // Note that we can't create an sp<...>(this) in a ctor that will not keep a 69 // reference once the ctor ends, as that would cause the refcount of 'this' 70 // dropping to 0 at the end of the ctor. Since all we need is a wp<...> 71 // that's what we create. 72 wp<BufferQueue::ConsumerListener> listener; 73 listener = static_cast<BufferQueue::ConsumerListener*>(this); 74 75 sp<BufferQueue::ConsumerListener> proxy; 76 proxy = new BufferQueue::ProxyConsumerListener(listener); 77 78 mInitCheck = mBufferQueue->consumerConnect(proxy); 79 if (mInitCheck != NO_ERROR) { 80 ALOGE("Error connecting to BufferQueue: %s (%d)", 81 strerror(-mInitCheck), mInitCheck); 82 return; 83 } 84 85 CHECK(mInitCheck == NO_ERROR); 86} 87 88GraphicBufferSource::~GraphicBufferSource() { 89 ALOGV("~GraphicBufferSource"); 90 if (mBufferQueue != NULL) { 91 status_t err = mBufferQueue->consumerDisconnect(); 92 if (err != NO_ERROR) { 93 ALOGW("consumerDisconnect failed: %d", err); 94 } 95 } 96} 97 98void GraphicBufferSource::omxExecuting() { 99 Mutex::Autolock autoLock(mMutex); 100 ALOGV("--> executing; avail=%d, codec vec size=%zd", 101 mNumFramesAvailable, mCodecBuffers.size()); 102 CHECK(!mExecuting); 103 mExecuting = true; 104 105 // Start by loading up as many buffers as possible. We want to do this, 106 // rather than just submit the first buffer, to avoid a degenerate case: 107 // if all BQ buffers arrive before we start executing, and we only submit 108 // one here, the other BQ buffers will just sit until we get notified 109 // that the codec buffer has been released. We'd then acquire and 110 // submit a single additional buffer, repeatedly, never using more than 111 // one codec buffer simultaneously. (We could instead try to submit 112 // all BQ buffers whenever any codec buffer is freed, but if we get the 113 // initial conditions right that will never be useful.) 114 while (mNumFramesAvailable) { 115 if (!fillCodecBuffer_l()) { 116 ALOGV("stop load with frames available (codecAvail=%d)", 117 isCodecBufferAvailable_l()); 118 break; 119 } 120 } 121 122 ALOGV("done loading initial frames, avail=%d", mNumFramesAvailable); 123 124 // If EOS has already been signaled, and there are no more frames to 125 // submit, try to send EOS now as well. 126 if (mEndOfStream && mNumFramesAvailable == 0) { 127 submitEndOfInputStream_l(); 128 } 129} 130 131void GraphicBufferSource::omxLoaded(){ 132 Mutex::Autolock autoLock(mMutex); 133 ALOGV("--> loaded"); 134 CHECK(mExecuting); 135 136 ALOGV("Dropped down to loaded, avail=%d eos=%d eosSent=%d", 137 mNumFramesAvailable, mEndOfStream, mEndOfStreamSent); 138 139 // Codec is no longer executing. Discard all codec-related state. 140 mCodecBuffers.clear(); 141 // TODO: scan mCodecBuffers to verify that all mGraphicBuffer entries 142 // are null; complain if not 143 144 mExecuting = false; 145} 146 147void GraphicBufferSource::addCodecBuffer(OMX_BUFFERHEADERTYPE* header) { 148 Mutex::Autolock autoLock(mMutex); 149 150 if (mExecuting) { 151 // This should never happen -- buffers can only be allocated when 152 // transitioning from "loaded" to "idle". 153 ALOGE("addCodecBuffer: buffer added while executing"); 154 return; 155 } 156 157 ALOGV("addCodecBuffer h=%p size=%lu p=%p", 158 header, header->nAllocLen, header->pBuffer); 159 CodecBuffer codecBuffer; 160 codecBuffer.mHeader = header; 161 mCodecBuffers.add(codecBuffer); 162} 163 164void GraphicBufferSource::codecBufferEmptied(OMX_BUFFERHEADERTYPE* header) { 165 Mutex::Autolock autoLock(mMutex); 166 167 CHECK(mExecuting); // could this happen if app stop()s early? 168 169 int cbi = findMatchingCodecBuffer_l(header); 170 if (cbi < 0) { 171 // This should never happen. 172 ALOGE("codecBufferEmptied: buffer not recognized (h=%p)", header); 173 return; 174 } 175 176 ALOGV("codecBufferEmptied h=%p size=%lu filled=%lu p=%p", 177 header, header->nAllocLen, header->nFilledLen, 178 header->pBuffer); 179 CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); 180 181 // header->nFilledLen may not be the original value, so we can't compare 182 // that to zero to see of this was the EOS buffer. Instead we just 183 // see if the GraphicBuffer reference was null, which should only ever 184 // happen for EOS. 185 if (codecBuffer.mGraphicBuffer == NULL) { 186 CHECK(mEndOfStream && mEndOfStreamSent); 187 // No GraphicBuffer to deal with, no additional input or output is 188 // expected, so just return. 189 return; 190 } 191 192 if (EXTRA_CHECK) { 193 // Pull the graphic buffer handle back out of the buffer, and confirm 194 // that it matches expectations. 195 OMX_U8* data = header->pBuffer; 196 buffer_handle_t bufferHandle; 197 memcpy(&bufferHandle, data + 4, sizeof(buffer_handle_t)); 198 if (bufferHandle != codecBuffer.mGraphicBuffer->handle) { 199 // should never happen 200 ALOGE("codecBufferEmptied: buffer's handle is %p, expected %p", 201 bufferHandle, codecBuffer.mGraphicBuffer->handle); 202 CHECK(!"codecBufferEmptied: mismatched buffer"); 203 } 204 } 205 206 // Find matching entry in our cached copy of the BufferQueue slots. 207 // If we find a match, release that slot. If we don't, the BufferQueue 208 // has dropped that GraphicBuffer, and there's nothing for us to release. 209 int id = codecBuffer.mBuf; 210 if (mBufferSlot[id] != NULL && 211 mBufferSlot[id]->handle == codecBuffer.mGraphicBuffer->handle) { 212 ALOGV("cbi %d matches bq slot %d, handle=%p", 213 cbi, id, mBufferSlot[id]->handle); 214 215 mBufferQueue->releaseBuffer(id, codecBuffer.mFrameNumber, 216 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); 217 } else { 218 ALOGV("codecBufferEmptied: no match for emptied buffer in cbi %d", 219 cbi); 220 } 221 222 // Mark the codec buffer as available by clearing the GraphicBuffer ref. 223 codecBuffer.mGraphicBuffer = NULL; 224 225 if (mNumFramesAvailable) { 226 // Fill this codec buffer. 227 CHECK(!mEndOfStreamSent); 228 ALOGV("buffer freed, %d frames avail (eos=%d)", 229 mNumFramesAvailable, mEndOfStream); 230 fillCodecBuffer_l(); 231 } else if (mEndOfStream) { 232 // No frames available, but EOS is pending, so use this buffer to 233 // send that. 234 ALOGV("buffer freed, EOS pending"); 235 submitEndOfInputStream_l(); 236 } 237 return; 238} 239 240bool GraphicBufferSource::fillCodecBuffer_l() { 241 CHECK(mExecuting && mNumFramesAvailable > 0); 242 243 int cbi = findAvailableCodecBuffer_l(); 244 if (cbi < 0) { 245 // No buffers available, bail. 246 ALOGV("fillCodecBuffer_l: no codec buffers, avail now %d", 247 mNumFramesAvailable); 248 return false; 249 } 250 251 ALOGV("fillCodecBuffer_l: acquiring buffer, avail=%d", 252 mNumFramesAvailable); 253 BufferQueue::BufferItem item; 254 status_t err = mBufferQueue->acquireBuffer(&item, 0); 255 if (err == BufferQueue::NO_BUFFER_AVAILABLE) { 256 // shouldn't happen 257 ALOGW("fillCodecBuffer_l: frame was not available"); 258 return false; 259 } else if (err != OK) { 260 // now what? fake end-of-stream? 261 ALOGW("fillCodecBuffer_l: acquireBuffer returned err=%d", err); 262 return false; 263 } 264 265 mNumFramesAvailable--; 266 267 // Wait for it to become available. 268 err = item.mFence->waitForever("GraphicBufferSource::fillCodecBuffer_l"); 269 if (err != OK) { 270 ALOGW("failed to wait for buffer fence: %d", err); 271 // keep going 272 } 273 274 // If this is the first time we're seeing this buffer, add it to our 275 // slot table. 276 if (item.mGraphicBuffer != NULL) { 277 ALOGV("fillCodecBuffer_l: setting mBufferSlot %d", item.mBuf); 278 mBufferSlot[item.mBuf] = item.mGraphicBuffer; 279 } 280 281 err = submitBuffer_l(item, cbi); 282 if (err != OK) { 283 ALOGV("submitBuffer_l failed, releasing bq buf %d", item.mBuf); 284 mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, 285 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); 286 } else { 287 ALOGV("buffer submitted (bq %d, cbi %d)", item.mBuf, cbi); 288 } 289 290 return true; 291} 292 293status_t GraphicBufferSource::signalEndOfInputStream() { 294 Mutex::Autolock autoLock(mMutex); 295 ALOGV("signalEndOfInputStream: exec=%d avail=%d eos=%d", 296 mExecuting, mNumFramesAvailable, mEndOfStream); 297 298 if (mEndOfStream) { 299 ALOGE("EOS was already signaled"); 300 return INVALID_OPERATION; 301 } 302 303 // Set the end-of-stream flag. If no frames are pending from the 304 // BufferQueue, and a codec buffer is available, and we're executing, 305 // we initiate the EOS from here. Otherwise, we'll let 306 // codecBufferEmptied() (or omxExecuting) do it. 307 // 308 // Note: if there are no pending frames and all codec buffers are 309 // available, we *must* submit the EOS from here or we'll just 310 // stall since no future events are expected. 311 mEndOfStream = true; 312 313 if (mExecuting && mNumFramesAvailable == 0) { 314 submitEndOfInputStream_l(); 315 } 316 317 return OK; 318} 319 320status_t GraphicBufferSource::submitBuffer_l( 321 const BufferQueue::BufferItem &item, int cbi) { 322 ALOGV("submitBuffer_l cbi=%d", cbi); 323 CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); 324 codecBuffer.mGraphicBuffer = mBufferSlot[item.mBuf]; 325 codecBuffer.mBuf = item.mBuf; 326 codecBuffer.mFrameNumber = item.mFrameNumber; 327 328 OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; 329 CHECK(header->nAllocLen >= 4 + sizeof(buffer_handle_t)); 330 OMX_U8* data = header->pBuffer; 331 const OMX_U32 type = kMetadataBufferTypeGrallocSource; 332 buffer_handle_t handle = codecBuffer.mGraphicBuffer->handle; 333 memcpy(data, &type, 4); 334 memcpy(data + 4, &handle, sizeof(buffer_handle_t)); 335 336 status_t err = mNodeInstance->emptyDirectBuffer(header, 0, 337 4 + sizeof(buffer_handle_t), OMX_BUFFERFLAG_ENDOFFRAME, 338 item.mTimestamp / 1000); 339 if (err != OK) { 340 ALOGW("WARNING: emptyDirectBuffer failed: 0x%x", err); 341 codecBuffer.mGraphicBuffer = NULL; 342 return err; 343 } 344 345 ALOGV("emptyDirectBuffer succeeded, h=%p p=%p bufhandle=%p", 346 header, header->pBuffer, handle); 347 return OK; 348} 349 350void GraphicBufferSource::submitEndOfInputStream_l() { 351 CHECK(mEndOfStream); 352 if (mEndOfStreamSent) { 353 ALOGV("EOS already sent"); 354 return; 355 } 356 357 int cbi = findAvailableCodecBuffer_l(); 358 if (cbi < 0) { 359 ALOGV("submitEndOfInputStream_l: no codec buffers available"); 360 return; 361 } 362 363 // We reject any additional incoming graphic buffers, so there's no need 364 // to stick a placeholder into codecBuffer.mGraphicBuffer to mark it as 365 // in-use. 366 CodecBuffer& codecBuffer(mCodecBuffers.editItemAt(cbi)); 367 368 OMX_BUFFERHEADERTYPE* header = codecBuffer.mHeader; 369 if (EXTRA_CHECK) { 370 // Guard against implementations that don't check nFilledLen. 371 size_t fillLen = 4 + sizeof(buffer_handle_t); 372 CHECK(header->nAllocLen >= fillLen); 373 OMX_U8* data = header->pBuffer; 374 memset(data, 0xcd, fillLen); 375 } 376 377 uint64_t timestamp = 0; // does this matter? 378 379 status_t err = mNodeInstance->emptyDirectBuffer(header, /*offset*/ 0, 380 /*length*/ 0, OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_EOS, 381 timestamp); 382 if (err != OK) { 383 ALOGW("emptyDirectBuffer EOS failed: 0x%x", err); 384 } else { 385 ALOGV("submitEndOfInputStream_l: buffer submitted, header=%p cbi=%d", 386 header, cbi); 387 mEndOfStreamSent = true; 388 } 389} 390 391int GraphicBufferSource::findAvailableCodecBuffer_l() { 392 CHECK(mCodecBuffers.size() > 0); 393 394 for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) { 395 if (mCodecBuffers[i].mGraphicBuffer == NULL) { 396 return i; 397 } 398 } 399 return -1; 400} 401 402int GraphicBufferSource::findMatchingCodecBuffer_l( 403 const OMX_BUFFERHEADERTYPE* header) { 404 for (int i = (int)mCodecBuffers.size() - 1; i>= 0; --i) { 405 if (mCodecBuffers[i].mHeader == header) { 406 return i; 407 } 408 } 409 return -1; 410} 411 412// BufferQueue::ConsumerListener callback 413void GraphicBufferSource::onFrameAvailable() { 414 Mutex::Autolock autoLock(mMutex); 415 416 ALOGV("onFrameAvailable exec=%d avail=%d", 417 mExecuting, mNumFramesAvailable); 418 419 if (mEndOfStream) { 420 // This should only be possible if a new buffer was queued after 421 // EOS was signaled, i.e. the app is misbehaving. 422 ALOGW("onFrameAvailable: EOS is set, ignoring frame"); 423 424 BufferQueue::BufferItem item; 425 status_t err = mBufferQueue->acquireBuffer(&item, 0); 426 if (err == OK) { 427 mBufferQueue->releaseBuffer(item.mBuf, item.mFrameNumber, 428 EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, item.mFence); 429 } 430 return; 431 } 432 433 mNumFramesAvailable++; 434 435 if (mExecuting) { 436 fillCodecBuffer_l(); 437 } 438} 439 440// BufferQueue::ConsumerListener callback 441void GraphicBufferSource::onBuffersReleased() { 442 Mutex::Autolock lock(mMutex); 443 444 uint32_t slotMask; 445 if (mBufferQueue->getReleasedBuffers(&slotMask) != NO_ERROR) { 446 ALOGW("onBuffersReleased: unable to get released buffer set"); 447 slotMask = 0xffffffff; 448 } 449 450 ALOGV("onBuffersReleased: 0x%08x", slotMask); 451 452 for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { 453 if ((slotMask & 0x01) != 0) { 454 mBufferSlot[i] = NULL; 455 } 456 slotMask >>= 1; 457 } 458} 459 460} // namespace android 461