1 /*
2  * Copyright (C) 2019 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 "C2Encoder"
19 
20 #include <memory>
21 
22 #include "C2Encoder.h"
23 
createCodec2Component(string compName,AMediaFormat * format)24 int32_t C2Encoder::createCodec2Component(string compName, AMediaFormat *format) {
25     ALOGV("In %s", __func__);
26     mListener.reset(new (std::nothrow) CodecListener(
27             [this](std::list<std::unique_ptr<C2Work>> &workItems) { handleWorkDone(workItems); }));
28     if (!mListener) return -1;
29 
30     const char *mime = nullptr;
31     AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime);
32     if (!mime) {
33         ALOGE("Error in AMediaFormat_getString");
34         return -1;
35     }
36     // Configure the plugin with Input properties
37     std::vector<C2Param *> configParam;
38     if (!strncmp(mime, "audio/", 6)) {
39         mIsAudioEncoder = true;
40         int32_t numChannels;
41         if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_SAMPLE_RATE, &mSampleRate)) {
42             ALOGE("AMEDIAFORMAT_KEY_SAMPLE_RATE not set");
43             return -1;
44         }
45         if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_CHANNEL_COUNT, &numChannels)) {
46             ALOGE("AMEDIAFORMAT_KEY_CHANNEL_COUNT not set");
47             return -1;
48         }
49         C2StreamSampleRateInfo::input sampleRateInfo(0u, mSampleRate);
50         C2StreamChannelCountInfo::input channelCountInfo(0u, numChannels);
51         configParam.push_back(&sampleRateInfo);
52         configParam.push_back(&channelCountInfo);
53     } else {
54         mIsAudioEncoder = false;
55         if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_WIDTH, &mWidth)) {
56             ALOGE("AMEDIAFORMAT_KEY_WIDTH not set");
57             return -1;
58         }
59         if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_HEIGHT, &mHeight)) {
60             ALOGE("AMEDIAFORMAT_KEY_HEIGHT not set");
61             return -1;
62         }
63         C2StreamPictureSizeInfo::input inputSize(0u, mWidth, mHeight);
64         configParam.push_back(&inputSize);
65 
66         if (!AMediaFormat_getInt32(format, AMEDIAFORMAT_KEY_FRAME_RATE, &mFrameRate) ||
67             (mFrameRate <= 0)) {
68             mFrameRate = KDefaultFrameRate;
69         }
70     }
71 
72     int64_t sTime = mStats->getCurTime();
73     if (mClient->CreateComponentByName(compName.c_str(), mListener, &mComponent, &mClient) !=
74         C2_OK) {
75         ALOGE("Create component failed for %s", compName.c_str());
76         return -1;
77     }
78     std::vector<std::unique_ptr<C2SettingResult>> failures;
79     int32_t status = mComponent->config(configParam, C2_DONT_BLOCK, &failures);
80     if (failures.size() != 0) {
81         ALOGE("Invalid Configuration");
82         return -1;
83     }
84 
85     status |= mComponent->start();
86     int64_t eTime = mStats->getCurTime();
87     int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
88     mStats->setInitTime(timeTaken);
89     return status;
90 }
91 
92 // In encoder components, fetch the size of input buffer allocated
getInputMaxBufSize()93 int32_t C2Encoder::getInputMaxBufSize() {
94     int32_t bitStreamInfo[1] = {0};
95     std::vector<std::unique_ptr<C2Param>> inParams;
96     c2_status_t status = mComponent->query({}, {C2StreamMaxBufferSizeInfo::input::PARAM_TYPE},
97                                            C2_DONT_BLOCK, &inParams);
98     if (status != C2_OK && inParams.size() == 0) {
99         ALOGE("Query MaxBufferSizeInfo failed => %d", status);
100         return status;
101     } else {
102         size_t offset = sizeof(C2Param);
103         for (size_t i = 0; i < inParams.size(); ++i) {
104             C2Param *param = inParams[i].get();
105             bitStreamInfo[i] = *(int32_t *)((uint8_t *)param + offset);
106         }
107     }
108     mInputMaxBufSize = bitStreamInfo[0];
109     if (mInputMaxBufSize < 0) {
110         ALOGE("Invalid mInputMaxBufSize %d\n", mInputMaxBufSize);
111         return -1;
112     }
113     return status;
114 }
115 
encodeFrames(ifstream & eleStream,size_t inputBufferSize)116 int32_t C2Encoder::encodeFrames(ifstream &eleStream, size_t inputBufferSize) {
117     ALOGV("In %s", __func__);
118     int32_t frameSize = 0;
119     if (!mIsAudioEncoder) {
120         frameSize = mWidth * mHeight * 3 / 2;
121     } else {
122         frameSize = DEFAULT_AUDIO_FRAME_SIZE;
123         if (getInputMaxBufSize() != 0) return -1;
124         if (frameSize > mInputMaxBufSize) {
125             frameSize = mInputMaxBufSize;
126         }
127     }
128     int32_t numFrames = (inputBufferSize + frameSize - 1) / frameSize;
129     // Temporary buffer to read data from the input file
130     std::unique_ptr<char[]> data(new (std::nothrow) char[frameSize]);
131     if (!data) {
132         ALOGE("Insufficient memory to read from input file");
133         return -1;
134     }
135 
136     typedef std::unique_lock<std::mutex> ULock;
137     uint64_t presentationTimeUs = 0;
138     size_t offset = 0;
139     c2_status_t status = C2_OK;
140 
141     mStats->setStartTime();
142     while (numFrames > 0) {
143         std::unique_ptr<C2Work> work;
144         // Prepare C2Work
145         {
146             ULock l(mQueueLock);
147             if (mWorkQueue.empty()) mQueueCondition.wait_for(l, MAX_RETRY * TIME_OUT);
148             if (!mWorkQueue.empty()) {
149                 mStats->addInputTime();
150                 work.swap(mWorkQueue.front());
151                 mWorkQueue.pop_front();
152             } else {
153                 cout << "Wait for generating C2Work exceeded timeout" << endl;
154                 return -1;
155             }
156         }
157 
158         if (mIsAudioEncoder) {
159             presentationTimeUs = mNumInputFrame * frameSize * (1000000 / mSampleRate);
160         } else {
161             presentationTimeUs = mNumInputFrame * (1000000 / mFrameRate);
162         }
163         uint32_t flags = 0;
164         if (numFrames == 1) flags |= C2FrameData::FLAG_END_OF_STREAM;
165 
166         work->input.flags = (C2FrameData::flags_t)flags;
167         work->input.ordinal.timestamp = presentationTimeUs;
168         work->input.ordinal.frameIndex = mNumInputFrame;
169         work->input.buffers.clear();
170 
171         if (inputBufferSize - offset < frameSize) {
172             frameSize = inputBufferSize - offset;
173         }
174         eleStream.read(data.get(), frameSize);
175         if (eleStream.gcount() != frameSize) {
176             ALOGE("read() from file failed. Incorrect bytes read");
177             return -1;
178         }
179         offset += frameSize;
180 
181         if (frameSize) {
182             if (mIsAudioEncoder) {
183                 std::shared_ptr<C2LinearBlock> block;
184                 status = mLinearPool->fetchLinearBlock(
185                         frameSize, {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
186                 if (status != C2_OK || !block) {
187                     cout << "fetchLinearBlock failed : " << status << endl;
188                     return status;
189                 }
190                 C2WriteView view = block->map().get();
191                 if (view.error() != C2_OK) {
192                     cout << "C2LinearBlock::map() failed : " << view.error() << endl;
193                     return view.error();
194                 }
195 
196                 memcpy(view.base(), data.get(), frameSize);
197                 work->input.buffers.emplace_back(new (std::nothrow) LinearBuffer(block));
198             } else {
199                 std::shared_ptr<C2GraphicBlock> block;
200                 status = mGraphicPool->fetchGraphicBlock(
201                         mWidth, mHeight, HAL_PIXEL_FORMAT_YV12,
202                         {C2MemoryUsage::CPU_READ, C2MemoryUsage::CPU_WRITE}, &block);
203                 if (status != C2_OK || !block) {
204                     cout << "fetchGraphicBlock failed : " << status << endl;
205                     return status;
206                 }
207                 C2GraphicView view = block->map().get();
208                 if (view.error() != C2_OK) {
209                     cout << "C2GraphicBlock::map() failed : " << view.error() << endl;
210                     return view.error();
211                 }
212 
213                 uint8_t *pY = view.data()[C2PlanarLayout::PLANE_Y];
214                 uint8_t *pU = view.data()[C2PlanarLayout::PLANE_U];
215                 uint8_t *pV = view.data()[C2PlanarLayout::PLANE_V];
216                 memcpy(pY, data.get(), mWidth * mHeight);
217                 memcpy(pU, data.get() + mWidth * mHeight, (mWidth * mHeight >> 2));
218                 memcpy(pV, data.get() + (mWidth * mHeight * 5 >> 2), mWidth * mHeight >> 2);
219                 work->input.buffers.emplace_back(new (std::nothrow) GraphicBuffer(block));
220             }
221             mStats->addFrameSize(frameSize);
222         }
223 
224         work->worklets.clear();
225         work->worklets.emplace_back(new (std::nothrow) C2Worklet);
226 
227         std::list<std::unique_ptr<C2Work>> items;
228         items.push_back(std::move(work));
229         // queue() invokes process() function of C2 Plugin.
230         status = mComponent->queue(&items);
231         if (status != C2_OK) {
232             ALOGE("queue failed");
233             return status;
234         }
235         ALOGV("Frame #%d size = %d queued", mNumInputFrame, frameSize);
236         numFrames--;
237         mNumInputFrame++;
238     }
239     return status;
240 }
241 
deInitCodec()242 void C2Encoder::deInitCodec() {
243     ALOGV("In %s", __func__);
244     if (!mComponent) return;
245 
246     int64_t sTime = mStats->getCurTime();
247     mComponent->stop();
248     mComponent->release();
249     mComponent = nullptr;
250     int64_t eTime = mStats->getCurTime();
251     int64_t timeTaken = mStats->getTimeDiff(sTime, eTime);
252     mStats->setDeInitTime(timeTaken);
253 }
254 
dumpStatistics(string inputReference,int64_t durationUs,string componentName,string statsFile)255 void C2Encoder::dumpStatistics(string inputReference, int64_t durationUs, string componentName,
256                                string statsFile) {
257     string operation = "c2encode";
258     string mode = "async";
259     mStats->dumpStatistics(operation, inputReference, durationUs, componentName, mode, statsFile);
260 }
261 
resetEncoder()262 void C2Encoder::resetEncoder() {
263     mIsAudioEncoder = false;
264     mNumInputFrame = 0;
265     mEos = false;
266     if (mStats) mStats->reset();
267 }
268