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