| /* |
| * Copyright (C) 2020 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 "PassthroughTrackTranscoder" |
| |
| #include <android-base/logging.h> |
| #include <media/PassthroughTrackTranscoder.h> |
| |
| namespace android { |
| |
| PassthroughTrackTranscoder::BufferPool::~BufferPool() { |
| for (auto it = mAddressSizeMap.begin(); it != mAddressSizeMap.end(); ++it) { |
| delete[] it->first; |
| } |
| } |
| |
| uint8_t* PassthroughTrackTranscoder::BufferPool::getBufferWithSize(size_t minimumBufferSize) |
| NO_THREAD_SAFETY_ANALYSIS { |
| std::unique_lock lock(mMutex); |
| |
| // Wait if maximum number of buffers are allocated but none are free. |
| while (mAddressSizeMap.size() >= mMaxBufferCount && mFreeBufferMap.empty() && !mAborted) { |
| mCondition.wait(lock); |
| } |
| |
| if (mAborted) { |
| return nullptr; |
| } |
| |
| // Check if the free list contains a large enough buffer. |
| auto it = mFreeBufferMap.lower_bound(minimumBufferSize); |
| if (it != mFreeBufferMap.end()) { |
| uint8_t* buffer = it->second; |
| mFreeBufferMap.erase(it); |
| return buffer; |
| } |
| |
| // If the maximum buffer count is reached, remove an existing free buffer. |
| if (mAddressSizeMap.size() >= mMaxBufferCount) { |
| auto it = mFreeBufferMap.begin(); |
| mAddressSizeMap.erase(it->second); |
| delete[] it->second; |
| mFreeBufferMap.erase(it); |
| } |
| |
| // Allocate a new buffer. |
| uint8_t* buffer = new (std::nothrow) uint8_t[minimumBufferSize]; |
| if (buffer == nullptr) { |
| LOG(ERROR) << "Unable to allocate new buffer of size: " << minimumBufferSize; |
| return nullptr; |
| } |
| |
| // Add the buffer to the tracking set. |
| mAddressSizeMap.emplace(buffer, minimumBufferSize); |
| return buffer; |
| } |
| |
| void PassthroughTrackTranscoder::BufferPool::returnBuffer(uint8_t* buffer) { |
| std::scoped_lock lock(mMutex); |
| |
| if (buffer == nullptr || mAddressSizeMap.find(buffer) == mAddressSizeMap.end()) { |
| LOG(WARNING) << "Ignoring untracked buffer " << buffer; |
| return; |
| } |
| |
| mFreeBufferMap.emplace(mAddressSizeMap[buffer], buffer); |
| mCondition.notify_one(); |
| } |
| |
| void PassthroughTrackTranscoder::BufferPool::abort() { |
| std::scoped_lock lock(mMutex); |
| mAborted = true; |
| mCondition.notify_all(); |
| } |
| |
| media_status_t PassthroughTrackTranscoder::configureDestinationFormat( |
| const std::shared_ptr<AMediaFormat>& destinationFormat __unused) { |
| // Called by MediaTrackTranscoder. Passthrough doesn't care about destination so just return ok. |
| return AMEDIA_OK; |
| } |
| |
| media_status_t PassthroughTrackTranscoder::runTranscodeLoop(bool* stopped) { |
| MediaSampleInfo info; |
| std::shared_ptr<MediaSample> sample; |
| bool eosReached = false; |
| |
| // Notify the track format as soon as we start. It's same as the source format. |
| notifyTrackFormatAvailable(); |
| |
| MediaSample::OnSampleReleasedCallback bufferReleaseCallback = |
| [bufferPool = mBufferPool](MediaSample* sample) { |
| bufferPool->returnBuffer(const_cast<uint8_t*>(sample->buffer)); |
| }; |
| |
| // Move samples until EOS is reached or transcoding is stopped. |
| while (mStopRequest != STOP_NOW && !eosReached) { |
| media_status_t status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &info); |
| |
| if (status == AMEDIA_OK) { |
| uint8_t* buffer = mBufferPool->getBufferWithSize(info.size); |
| if (buffer == nullptr) { |
| if (mStopRequest == STOP_NOW) { |
| break; |
| } |
| |
| LOG(ERROR) << "Unable to get buffer from pool"; |
| return AMEDIA_ERROR_UNKNOWN; |
| } |
| |
| sample = MediaSample::createWithReleaseCallback( |
| buffer, 0 /* offset */, 0 /* bufferId */, bufferReleaseCallback); |
| |
| status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, buffer, info.size); |
| if (status != AMEDIA_OK) { |
| LOG(ERROR) << "Unable to read next sample data. Aborting transcode."; |
| return status; |
| } |
| |
| } else if (status == AMEDIA_ERROR_END_OF_STREAM) { |
| sample = std::make_shared<MediaSample>(); |
| eosReached = true; |
| } else { |
| LOG(ERROR) << "Unable to get next sample info. Aborting transcode."; |
| return status; |
| } |
| |
| sample->info = info; |
| onOutputSampleAvailable(sample); |
| |
| if (mStopRequest == STOP_ON_SYNC && info.flags & SAMPLE_FLAG_SYNC_SAMPLE) { |
| break; |
| } |
| } |
| |
| if (mStopRequest != NONE && !eosReached) { |
| *stopped = true; |
| } |
| return AMEDIA_OK; |
| } |
| |
| void PassthroughTrackTranscoder::abortTranscodeLoop() { |
| if (mStopRequest == STOP_NOW) { |
| mBufferPool->abort(); |
| } |
| } |
| |
| std::shared_ptr<AMediaFormat> PassthroughTrackTranscoder::getOutputFormat() const { |
| return mSourceFormat; |
| } |
| } // namespace android |