/* * Copyright (C) 2011 The Android Open Source Project * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ //#define LOG_NDEBUG 0 #define LOG_TAG "SurfaceMediaSource" #include #include #include #include #include #include #include #include #include #include #include #include #include namespace android { SurfaceMediaSource::SurfaceMediaSource(uint32_t bufferWidth, uint32_t bufferHeight) : mWidth(bufferWidth), mHeight(bufferHeight), mCurrentSlot(BufferQueue::INVALID_BUFFER_SLOT), mNumPendingBuffers(0), mCurrentTimestamp(0), mFrameRate(30), mStarted(false), mNumFramesReceived(0), mNumFramesEncoded(0), mFirstFrameTimestamp(0), mMaxAcquiredBufferCount(4), // XXX double-check the default mUseAbsoluteTimestamps(false) { ALOGV("SurfaceMediaSource"); if (bufferWidth == 0 || bufferHeight == 0) { ALOGE("Invalid dimensions %dx%d", bufferWidth, bufferHeight); } BufferQueue::createBufferQueue(&mProducer, &mConsumer); mConsumer->setDefaultBufferSize(bufferWidth, bufferHeight); mConsumer->setConsumerUsageBits(GRALLOC_USAGE_HW_VIDEO_ENCODER | GRALLOC_USAGE_HW_TEXTURE); sp composer(ComposerService::getComposerService()); // Note that we can't create an sp<...>(this) in a ctor that will not keep a // reference once the ctor ends, as that would cause the refcount of 'this' // dropping to 0 at the end of the ctor. Since all we need is a wp<...> // that's what we create. wp listener = static_cast(this); sp proxy = new BufferQueue::ProxyConsumerListener(listener); status_t err = mConsumer->consumerConnect(proxy, false); if (err != NO_ERROR) { ALOGE("SurfaceMediaSource: error connecting to BufferQueue: %s (%d)", strerror(-err), err); } } SurfaceMediaSource::~SurfaceMediaSource() { ALOGV("~SurfaceMediaSource"); CHECK(!mStarted); } nsecs_t SurfaceMediaSource::getTimestamp() { ALOGV("getTimestamp"); Mutex::Autolock lock(mMutex); return mCurrentTimestamp; } void SurfaceMediaSource::setFrameAvailableListener( const sp& listener) { ALOGV("setFrameAvailableListener"); Mutex::Autolock lock(mMutex); mFrameAvailableListener = listener; } void SurfaceMediaSource::dump(String8& result) const { char buffer[1024]; dump(result, "", buffer, 1024); } void SurfaceMediaSource::dump( String8& result, const char* /* prefix */, char* buffer, size_t /* SIZE */) const { Mutex::Autolock lock(mMutex); result.append(buffer); mConsumer->dump(result, ""); } status_t SurfaceMediaSource::setFrameRate(int32_t fps) { ALOGV("setFrameRate"); Mutex::Autolock lock(mMutex); const int MAX_FRAME_RATE = 60; if (fps < 0 || fps > MAX_FRAME_RATE) { return BAD_VALUE; } mFrameRate = fps; return OK; } bool SurfaceMediaSource::isMetaDataStoredInVideoBuffers() const { ALOGV("isMetaDataStoredInVideoBuffers"); return true; } int32_t SurfaceMediaSource::getFrameRate( ) const { ALOGV("getFrameRate"); Mutex::Autolock lock(mMutex); return mFrameRate; } status_t SurfaceMediaSource::start(MetaData *params) { ALOGV("start"); Mutex::Autolock lock(mMutex); CHECK(!mStarted); mStartTimeNs = 0; int64_t startTimeUs; int32_t bufferCount = 0; if (params) { if (params->findInt64(kKeyTime, &startTimeUs)) { mStartTimeNs = startTimeUs * 1000; } if (!params->findInt32(kKeyNumBuffers, &bufferCount)) { ALOGE("Failed to find the advertised buffer count"); return UNKNOWN_ERROR; } if (bufferCount <= 1) { ALOGE("bufferCount %d is too small", bufferCount); return BAD_VALUE; } mMaxAcquiredBufferCount = bufferCount; } CHECK_GT(mMaxAcquiredBufferCount, 1); status_t err = mConsumer->setMaxAcquiredBufferCount(mMaxAcquiredBufferCount); if (err != OK) { return err; } mNumPendingBuffers = 0; mStarted = true; return OK; } status_t SurfaceMediaSource::setMaxAcquiredBufferCount(size_t count) { ALOGV("setMaxAcquiredBufferCount(%d)", count); Mutex::Autolock lock(mMutex); CHECK_GT(count, 1); mMaxAcquiredBufferCount = count; return OK; } status_t SurfaceMediaSource::setUseAbsoluteTimestamps() { ALOGV("setUseAbsoluteTimestamps"); Mutex::Autolock lock(mMutex); mUseAbsoluteTimestamps = true; return OK; } status_t SurfaceMediaSource::stop() { ALOGV("stop"); Mutex::Autolock lock(mMutex); if (!mStarted) { return OK; } mStarted = false; mFrameAvailableCondition.signal(); while (mNumPendingBuffers > 0) { ALOGI("Still waiting for %d buffers to be returned.", mNumPendingBuffers); #if DEBUG_PENDING_BUFFERS for (size_t i = 0; i < mPendingBuffers.size(); ++i) { ALOGI("%d: %p", i, mPendingBuffers.itemAt(i)); } #endif mMediaBuffersAvailableCondition.wait(mMutex); } mMediaBuffersAvailableCondition.signal(); return mConsumer->consumerDisconnect(); } sp SurfaceMediaSource::getFormat() { ALOGV("getFormat"); Mutex::Autolock lock(mMutex); sp meta = new MetaData; meta->setInt32(kKeyWidth, mWidth); meta->setInt32(kKeyHeight, mHeight); // The encoder format is set as an opaque colorformat // The encoder will later find out the actual colorformat // from the GL Frames itself. meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatAndroidOpaque); meta->setInt32(kKeyStride, mWidth); meta->setInt32(kKeySliceHeight, mHeight); meta->setInt32(kKeyFrameRate, mFrameRate); meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); return meta; } // Pass the data to the MediaBuffer. Pass in only the metadata // The metadata passed consists of two parts: // 1. First, there is an integer indicating that it is a GRAlloc // source (kMetadataBufferTypeGrallocSource) // 2. This is followed by the buffer_handle_t that is a handle to the // GRalloc buffer. The encoder needs to interpret this GRalloc handle // and encode the frames. // -------------------------------------------------------------- // | kMetadataBufferTypeGrallocSource | sizeof(buffer_handle_t) | // -------------------------------------------------------------- // Note: Call only when you have the lock static void passMetadataBuffer(MediaBuffer **buffer, buffer_handle_t bufferHandle) { *buffer = new MediaBuffer(4 + sizeof(buffer_handle_t)); char *data = (char *)(*buffer)->data(); if (data == NULL) { ALOGE("Cannot allocate memory for metadata buffer!"); return; } OMX_U32 type = kMetadataBufferTypeGrallocSource; memcpy(data, &type, 4); memcpy(data + 4, &bufferHandle, sizeof(buffer_handle_t)); ALOGV("handle = %p, , offset = %d, length = %d", bufferHandle, (*buffer)->range_length(), (*buffer)->range_offset()); } status_t SurfaceMediaSource::read( MediaBuffer **buffer, const ReadOptions * /* options */) { ALOGV("read"); Mutex::Autolock lock(mMutex); *buffer = NULL; while (mStarted && mNumPendingBuffers == mMaxAcquiredBufferCount) { mMediaBuffersAvailableCondition.wait(mMutex); } // Update the current buffer info // TODO: mCurrentSlot can be made a bufferstate since there // can be more than one "current" slots. BufferQueue::BufferItem item; // If the recording has started and the queue is empty, then just // wait here till the frames come in from the client side while (mStarted) { status_t err = mConsumer->acquireBuffer(&item, 0); if (err == BufferQueue::NO_BUFFER_AVAILABLE) { // wait for a buffer to be queued mFrameAvailableCondition.wait(mMutex); } else if (err == OK) { err = item.mFence->waitForever("SurfaceMediaSource::read"); if (err) { ALOGW("read: failed to wait for buffer fence: %d", err); } // First time seeing the buffer? Added it to the SMS slot if (item.mGraphicBuffer != NULL) { mSlots[item.mBuf].mGraphicBuffer = item.mGraphicBuffer; } mSlots[item.mBuf].mFrameNumber = item.mFrameNumber; // check for the timing of this buffer if (mNumFramesReceived == 0 && !mUseAbsoluteTimestamps) { mFirstFrameTimestamp = item.mTimestamp; // Initial delay if (mStartTimeNs > 0) { if (item.mTimestamp < mStartTimeNs) { // This frame predates start of record, discard mConsumer->releaseBuffer( item.mBuf, item.mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); continue; } mStartTimeNs = item.mTimestamp - mStartTimeNs; } } item.mTimestamp = mStartTimeNs + (item.mTimestamp - mFirstFrameTimestamp); mNumFramesReceived++; break; } else { ALOGE("read: acquire failed with error code %d", err); return ERROR_END_OF_STREAM; } } // If the loop was exited as a result of stopping the recording, // it is OK if (!mStarted) { ALOGV("Read: SurfaceMediaSource is stopped. Returning ERROR_END_OF_STREAM."); return ERROR_END_OF_STREAM; } mCurrentSlot = item.mBuf; // First time seeing the buffer? Added it to the SMS slot if (item.mGraphicBuffer != NULL) { mSlots[item.mBuf].mGraphicBuffer = item.mGraphicBuffer; } mSlots[item.mBuf].mFrameNumber = item.mFrameNumber; mCurrentBuffers.push_back(mSlots[mCurrentSlot].mGraphicBuffer); int64_t prevTimeStamp = mCurrentTimestamp; mCurrentTimestamp = item.mTimestamp; mNumFramesEncoded++; // Pass the data to the MediaBuffer. Pass in only the metadata passMetadataBuffer(buffer, mSlots[mCurrentSlot].mGraphicBuffer->handle); (*buffer)->setObserver(this); (*buffer)->add_ref(); (*buffer)->meta_data()->setInt64(kKeyTime, mCurrentTimestamp / 1000); ALOGV("Frames encoded = %d, timestamp = %lld, time diff = %lld", mNumFramesEncoded, mCurrentTimestamp / 1000, mCurrentTimestamp / 1000 - prevTimeStamp / 1000); ++mNumPendingBuffers; #if DEBUG_PENDING_BUFFERS mPendingBuffers.push_back(*buffer); #endif ALOGV("returning mbuf %p", *buffer); return OK; } static buffer_handle_t getMediaBufferHandle(MediaBuffer *buffer) { // need to convert to char* for pointer arithmetic and then // copy the byte stream into our handle buffer_handle_t bufferHandle; memcpy(&bufferHandle, (char*)(buffer->data()) + 4, sizeof(buffer_handle_t)); return bufferHandle; } void SurfaceMediaSource::signalBufferReturned(MediaBuffer *buffer) { ALOGV("signalBufferReturned"); bool foundBuffer = false; Mutex::Autolock lock(mMutex); buffer_handle_t bufferHandle = getMediaBufferHandle(buffer); for (size_t i = 0; i < mCurrentBuffers.size(); i++) { if (mCurrentBuffers[i]->handle == bufferHandle) { mCurrentBuffers.removeAt(i); foundBuffer = true; break; } } if (!foundBuffer) { ALOGW("returned buffer was not found in the current buffer list"); } for (int id = 0; id < BufferQueue::NUM_BUFFER_SLOTS; id++) { if (mSlots[id].mGraphicBuffer == NULL) { continue; } if (bufferHandle == mSlots[id].mGraphicBuffer->handle) { ALOGV("Slot %d returned, matches handle = %p", id, mSlots[id].mGraphicBuffer->handle); mConsumer->releaseBuffer(id, mSlots[id].mFrameNumber, EGL_NO_DISPLAY, EGL_NO_SYNC_KHR, Fence::NO_FENCE); buffer->setObserver(0); buffer->release(); foundBuffer = true; break; } } if (!foundBuffer) { CHECK(!"signalBufferReturned: bogus buffer"); } #if DEBUG_PENDING_BUFFERS for (size_t i = 0; i < mPendingBuffers.size(); ++i) { if (mPendingBuffers.itemAt(i) == buffer) { mPendingBuffers.removeAt(i); break; } } #endif --mNumPendingBuffers; mMediaBuffersAvailableCondition.broadcast(); } // Part of the BufferQueue::ConsumerListener void SurfaceMediaSource::onFrameAvailable() { ALOGV("onFrameAvailable"); sp listener; { // scope for the lock Mutex::Autolock lock(mMutex); mFrameAvailableCondition.broadcast(); listener = mFrameAvailableListener; } if (listener != NULL) { ALOGV("actually calling onFrameAvailable"); listener->onFrameAvailable(); } } // SurfaceMediaSource hijacks this event to assume // the prodcuer is disconnecting from the BufferQueue // and that it should stop the recording void SurfaceMediaSource::onBuffersReleased() { ALOGV("onBuffersReleased"); Mutex::Autolock lock(mMutex); mFrameAvailableCondition.signal(); for (int i = 0; i < BufferQueue::NUM_BUFFER_SLOTS; i++) { mSlots[i].mGraphicBuffer = 0; } } void SurfaceMediaSource::onSidebandStreamChanged() { ALOG_ASSERT(false, "SurfaceMediaSource can't consume sideband streams"); } } // end of namespace android