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