• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2020 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_NDEBUG 0
18 #define LOG_TAG "PassthroughTrackTranscoder"
19 
20 #include <android-base/logging.h>
21 #include <media/PassthroughTrackTranscoder.h>
22 #include <sys/prctl.h>
23 
24 namespace android {
25 
~BufferPool()26 PassthroughTrackTranscoder::BufferPool::~BufferPool() {
27     for (auto it = mAddressSizeMap.begin(); it != mAddressSizeMap.end(); ++it) {
28         delete[] it->first;
29     }
30 }
31 
getBufferWithSize(size_t minimumBufferSize)32 uint8_t* PassthroughTrackTranscoder::BufferPool::getBufferWithSize(size_t minimumBufferSize)
33         NO_THREAD_SAFETY_ANALYSIS {
34     std::unique_lock lock(mMutex);
35 
36     // Wait if maximum number of buffers are allocated but none are free.
37     while (mAddressSizeMap.size() >= mMaxBufferCount && mFreeBufferMap.empty() && !mAborted) {
38         mCondition.wait(lock);
39     }
40 
41     if (mAborted) {
42         return nullptr;
43     }
44 
45     // Check if the free list contains a large enough buffer.
46     auto it = mFreeBufferMap.lower_bound(minimumBufferSize);
47     if (it != mFreeBufferMap.end()) {
48         uint8_t* buffer = it->second;
49         mFreeBufferMap.erase(it);
50         return buffer;
51     }
52 
53     // If the maximum buffer count is reached, remove an existing free buffer.
54     if (mAddressSizeMap.size() >= mMaxBufferCount) {
55         auto it = mFreeBufferMap.begin();
56         mAddressSizeMap.erase(it->second);
57         delete[] it->second;
58         mFreeBufferMap.erase(it);
59     }
60 
61     // Allocate a new buffer.
62     uint8_t* buffer = new (std::nothrow) uint8_t[minimumBufferSize];
63     if (buffer == nullptr) {
64         LOG(ERROR) << "Unable to allocate new buffer of size: " << minimumBufferSize;
65         return nullptr;
66     }
67 
68     // Add the buffer to the tracking set.
69     mAddressSizeMap.emplace(buffer, minimumBufferSize);
70     return buffer;
71 }
72 
returnBuffer(uint8_t * buffer)73 void PassthroughTrackTranscoder::BufferPool::returnBuffer(uint8_t* buffer) {
74     std::scoped_lock lock(mMutex);
75 
76     if (buffer == nullptr || mAddressSizeMap.find(buffer) == mAddressSizeMap.end()) {
77         LOG(WARNING) << "Ignoring untracked buffer " << buffer;
78         return;
79     }
80 
81     mFreeBufferMap.emplace(mAddressSizeMap[buffer], buffer);
82     mCondition.notify_one();
83 }
84 
abort()85 void PassthroughTrackTranscoder::BufferPool::abort() {
86     std::scoped_lock lock(mMutex);
87     mAborted = true;
88     mCondition.notify_all();
89 }
90 
configureDestinationFormat(const std::shared_ptr<AMediaFormat> & destinationFormat __unused)91 media_status_t PassthroughTrackTranscoder::configureDestinationFormat(
92         const std::shared_ptr<AMediaFormat>& destinationFormat __unused) {
93     // Called by MediaTrackTranscoder. Passthrough doesn't care about destination so just return ok.
94     return AMEDIA_OK;
95 }
96 
runTranscodeLoop(bool * stopped)97 media_status_t PassthroughTrackTranscoder::runTranscodeLoop(bool* stopped) {
98     prctl(PR_SET_NAME, (unsigned long)"PassthruThread", 0, 0, 0);
99 
100     MediaSampleInfo info;
101     std::shared_ptr<MediaSample> sample;
102     bool eosReached = false;
103 
104     // Notify the track format as soon as we start. It's same as the source format.
105     notifyTrackFormatAvailable();
106 
107     MediaSample::OnSampleReleasedCallback bufferReleaseCallback =
108             [bufferPool = mBufferPool](MediaSample* sample) {
109                 bufferPool->returnBuffer(const_cast<uint8_t*>(sample->buffer));
110             };
111 
112     // Move samples until EOS is reached or transcoding is stopped.
113     while (mStopRequest != STOP_NOW && !eosReached) {
114         media_status_t status = mMediaSampleReader->getSampleInfoForTrack(mTrackIndex, &info);
115 
116         if (status == AMEDIA_OK) {
117             uint8_t* buffer = mBufferPool->getBufferWithSize(info.size);
118             if (buffer == nullptr) {
119                 if (mStopRequest == STOP_NOW) {
120                     break;
121                 }
122 
123                 LOG(ERROR) << "Unable to get buffer from pool";
124                 return AMEDIA_ERROR_UNKNOWN;
125             }
126 
127             sample = MediaSample::createWithReleaseCallback(
128                     buffer, 0 /* offset */, 0 /* bufferId */, bufferReleaseCallback);
129 
130             status = mMediaSampleReader->readSampleDataForTrack(mTrackIndex, buffer, info.size);
131             if (status != AMEDIA_OK) {
132                 LOG(ERROR) << "Unable to read next sample data. Aborting transcode.";
133                 return status;
134             }
135 
136         } else if (status == AMEDIA_ERROR_END_OF_STREAM) {
137             sample = std::make_shared<MediaSample>();
138             eosReached = true;
139         } else {
140             LOG(ERROR) << "Unable to get next sample info. Aborting transcode.";
141             return status;
142         }
143 
144         sample->info = info;
145         onOutputSampleAvailable(sample);
146 
147         if (mStopRequest == STOP_ON_SYNC && info.flags & SAMPLE_FLAG_SYNC_SAMPLE) {
148             break;
149         }
150     }
151 
152     if (mStopRequest != NONE && !eosReached) {
153         *stopped = true;
154     }
155     return AMEDIA_OK;
156 }
157 
abortTranscodeLoop()158 void PassthroughTrackTranscoder::abortTranscodeLoop() {
159     if (mStopRequest == STOP_NOW) {
160         mBufferPool->abort();
161     }
162 }
163 
getOutputFormat() const164 std::shared_ptr<AMediaFormat> PassthroughTrackTranscoder::getOutputFormat() const {
165     return mSourceFormat;
166 }
167 }  // namespace android
168