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