• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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