1 /*
2 * Copyright (C) 2023 Huawei Device Co., Ltd.
3 * Licensed under the Apache License, Version 2.0 (the "License");
4 * you may not use this file except in compliance with the License.
5 * You may obtain a copy of the License at
6 *
7 * http://www.apache.org/licenses/LICENSE-2.0
8 *
9 * Unless required by applicable law or agreed to in writing, software
10 * distributed under the License is distributed on an "AS IS" BASIS,
11 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
12 * See the License for the specific language governing permissions and
13 * limitations under the License.
14 */
15
16 #include "tester_capi.h"
17 #include "native_avcodec_videoencoder.h"
18 #include "native_avcodec_videodecoder.h"
19 #include "native_window.h"
20 #include "surface.h"
21 #include "hcodec_log.h"
22
23 using namespace std;
24 using namespace OHOS::MediaAVCodec;
25
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)26 void TesterCapi::OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
27 {
28 LOGI(">>");
29 }
30
OnStreamChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)31 void TesterCapi::OnStreamChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
32 {
33 LOGI(">>");
34 }
35
OnNeedInputData(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,void * userData)36 void TesterCapi::OnNeedInputData(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData)
37 {
38 TesterCapi* tester = static_cast<TesterCapi*>(userData);
39 if (tester == nullptr) {
40 return;
41 }
42 lock_guard<mutex> lk(tester->inputMtx_);
43 tester->inputList_.emplace_back(index, data);
44 tester->inputCond_.notify_all();
45 }
46
OnNewOutputData(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,OH_AVCodecBufferAttr * attr,void * userData)47 void TesterCapi::OnNewOutputData(
48 OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr, void *userData)
49 {
50 TesterCapi* tester = static_cast<TesterCapi*>(userData);
51 if (tester == nullptr || attr == nullptr) {
52 return;
53 }
54 lock_guard<mutex> lk(tester->outputMtx_);
55 tester->outputList_.emplace_back(index, data, *attr);
56 tester->outputCond_.notify_all();
57 }
58
Create()59 bool TesterCapi::Create()
60 {
61 const char* mime = (opt_.protocol == H264) ? OH_AVCODEC_MIMETYPE_VIDEO_AVC : OH_AVCODEC_MIMETYPE_VIDEO_HEVC;
62 auto begin = std::chrono::steady_clock::now();
63 codec_ = opt_.isEncoder ? OH_VideoEncoder_CreateByMime(mime) : OH_VideoDecoder_CreateByMime(mime);
64 if (codec_ == nullptr) {
65 LOGE("Create failed");
66 return false;
67 }
68 CostRecorder::Instance().Update(begin,
69 opt_.isEncoder ? "OH_VideoEncoder_CreateByMime" : "OH_VideoDecoder_CreateByMime");
70 return true;
71 }
72
SetCallback()73 bool TesterCapi::SetCallback()
74 {
75 OH_AVCodecAsyncCallback cb {
76 &TesterCapi::OnError,
77 &TesterCapi::OnStreamChanged,
78 &TesterCapi::OnNeedInputData,
79 &TesterCapi::OnNewOutputData,
80 };
81 auto begin = std::chrono::steady_clock::now();
82 OH_AVErrCode ret = opt_.isEncoder ? OH_VideoEncoder_SetCallback(codec_, cb, this) :
83 OH_VideoDecoder_SetCallback(codec_, cb, this);
84 if (ret != AV_ERR_OK) {
85 LOGE("SetCallback failed");
86 return false;
87 }
88 CostRecorder::Instance().Update(begin,
89 opt_.isEncoder ? "OH_VideoEncoder_SetCallback" : "OH_VideoDecoder_SetCallback");
90 return true;
91 }
92
Start()93 bool TesterCapi::Start()
94 {
95 auto begin = std::chrono::steady_clock::now();
96 OH_AVErrCode ret = opt_.isEncoder ? OH_VideoEncoder_Start(codec_) :
97 OH_VideoDecoder_Start(codec_);
98 if (ret != AV_ERR_OK) {
99 LOGE("Start failed");
100 return false;
101 }
102 CostRecorder::Instance().Update(begin,
103 opt_.isEncoder ? "OH_VideoEncoder_Start" : "OH_VideoDecoder_Start");
104 return true;
105 }
106
Stop()107 bool TesterCapi::Stop()
108 {
109 auto begin = std::chrono::steady_clock::now();
110 OH_AVErrCode ret = opt_.isEncoder ? OH_VideoEncoder_Stop(codec_) :
111 OH_VideoDecoder_Stop(codec_);
112 if (ret != AV_ERR_OK) {
113 LOGE("Stop failed");
114 return false;
115 }
116 CostRecorder::Instance().Update(begin,
117 opt_.isEncoder ? "OH_VideoEncoder_Stop" : "OH_VideoDecoder_Stop");
118 return true;
119 }
120
Release()121 bool TesterCapi::Release()
122 {
123 auto begin = std::chrono::steady_clock::now();
124 OH_AVErrCode ret = opt_.isEncoder ? OH_VideoEncoder_Destroy(codec_) :
125 OH_VideoDecoder_Destroy(codec_);
126 if (ret != AV_ERR_OK) {
127 LOGE("Destroy failed");
128 return false;
129 }
130 CostRecorder::Instance().Update(begin,
131 opt_.isEncoder ? "OH_VideoEncoder_Destroy" : "OH_VideoDecoder_Destroy");
132 return true;
133 }
134
Flush()135 bool TesterCapi::Flush()
136 {
137 auto begin = std::chrono::steady_clock::now();
138 OH_AVErrCode ret = opt_.isEncoder ? OH_VideoEncoder_Flush(codec_) :
139 OH_VideoDecoder_Flush(codec_);
140 if (ret != AV_ERR_OK) {
141 LOGE("Flush failed");
142 return false;
143 }
144 CostRecorder::Instance().Update(begin,
145 opt_.isEncoder ? "OH_VideoEncoder_Flush" : "OH_VideoDecoder_Flush");
146 return true;
147 }
148
ClearAllBuffer()149 void TesterCapi::ClearAllBuffer()
150 {
151 {
152 lock_guard<mutex> lk(inputMtx_);
153 inputList_.clear();
154 }
155 {
156 lock_guard<mutex> lk(outputMtx_);
157 outputList_.clear();
158 }
159 }
160
ConfigureEncoder()161 bool TesterCapi::ConfigureEncoder()
162 {
163 auto fmt = shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
164 IF_TRUE_RETURN_VAL_WITH_MSG(fmt == nullptr, false, "OH_AVFormat_Create failed");
165 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_WIDTH, opt_.dispW);
166 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_HEIGHT, opt_.dispH);
167 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_PIXEL_FORMAT, opt_.pixFmt);
168 OH_AVFormat_SetDoubleValue(fmt.get(), OH_MD_KEY_FRAME_RATE, opt_.frameRate);
169 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_RANGE_FLAG, opt_.rangeFlag);
170 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_COLOR_PRIMARIES, opt_.primary);
171 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_TRANSFER_CHARACTERISTICS, opt_.transfer);
172 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_MATRIX_COEFFICIENTS, opt_.matrix);
173
174 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_I_FRAME_INTERVAL, opt_.iFrameInterval);
175 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_PROFILE, opt_.profile);
176 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_VIDEO_ENCODE_BITRATE_MODE, opt_.rateMode);
177 OH_AVFormat_SetLongValue(fmt.get(), OH_MD_KEY_BITRATE, opt_.bitRate);
178 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_QUALITY, opt_.quality);
179
180 auto begin = std::chrono::steady_clock::now();
181 OH_AVErrCode ret = OH_VideoEncoder_Configure(codec_, fmt.get());
182 if (ret != AV_ERR_OK) {
183 LOGE("ConfigureEncoder failed");
184 return false;
185 }
186 CostRecorder::Instance().Update(begin, "OH_VideoEncoder_Configure");
187 return true;
188 }
189
CreateInputSurface()190 bool TesterCapi::CreateInputSurface()
191 {
192 OHNativeWindow *window = nullptr;
193 auto begin = std::chrono::steady_clock::now();
194 OH_AVErrCode ret = OH_VideoEncoder_GetSurface(codec_, &window);
195 if (ret != AV_ERR_OK || window == nullptr) {
196 LOGE("CreateInputSurface failed");
197 return false;
198 }
199 surface_ = window->surface;
200 if (surface_ == nullptr) {
201 LOGE("surface in OHNativeWindow is null");
202 return false;
203 }
204 CostRecorder::Instance().Update(begin, "OH_VideoEncoder_GetSurface");
205 // if we dont decrease here, the OHNativeWindow will never be destroyed
206 OH_NativeWindow_DestroyNativeWindow(window);
207 return true;
208 }
209
NotifyEos()210 bool TesterCapi::NotifyEos()
211 {
212 auto begin = std::chrono::steady_clock::now();
213 OH_AVErrCode ret = OH_VideoEncoder_NotifyEndOfStream(codec_);
214 if (ret != AV_ERR_OK) {
215 LOGE("NotifyEos failed");
216 return false;
217 }
218 CostRecorder::Instance().Update(begin, "OH_VideoEncoder_NotifyEndOfStream");
219 return true;
220 }
221
RequestIDR()222 bool TesterCapi::RequestIDR()
223 {
224 auto fmt = shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
225 IF_TRUE_RETURN_VAL_WITH_MSG(fmt == nullptr, false, "OH_AVFormat_Create failed");
226 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_REQUEST_I_FRAME, true);
227
228 auto begin = std::chrono::steady_clock::now();
229 OH_AVErrCode ret = OH_VideoEncoder_SetParameter(codec_, fmt.get());
230 if (ret != AV_ERR_OK) {
231 LOGE("RequestIDR failed");
232 return false;
233 }
234 CostRecorder::Instance().Update(begin, "OH_VideoEncoder_SetParameter");
235 return true;
236 }
237
GetInputFormat()238 bool TesterCapi::GetInputFormat()
239 {
240 if (!opt_.isEncoder) {
241 return true;
242 }
243 auto begin = std::chrono::steady_clock::now();
244 OH_AVFormat *fmt = OH_VideoEncoder_GetInputDescription(codec_);
245 if (fmt == nullptr) {
246 LOGE("GetInputFormat failed");
247 return false;
248 }
249 CostRecorder::Instance().Update(begin, "OH_VideoEncoder_GetInputDescription");
250 inputFmt_ = shared_ptr<OH_AVFormat>(fmt, OH_AVFormat_Destroy);
251 return true;
252 }
253
GetOutputFormat()254 bool TesterCapi::GetOutputFormat()
255 {
256 auto begin = std::chrono::steady_clock::now();
257 OH_AVFormat *fmt = opt_.isEncoder ? OH_VideoEncoder_GetOutputDescription(codec_) :
258 OH_VideoDecoder_GetOutputDescription(codec_);
259 if (fmt == nullptr) {
260 LOGE("GetOutputFormat failed");
261 return false;
262 }
263 CostRecorder::Instance().Update(begin,
264 opt_.isEncoder ? "OH_VideoEncoder_GetOutputDescription" : "OH_VideoDecoder_GetOutputDescription");
265 OH_AVFormat_Destroy(fmt);
266 return true;
267 }
268
GetInputStride()269 optional<uint32_t> TesterCapi::GetInputStride()
270 {
271 int32_t stride = 0;
272 if (OH_AVFormat_GetIntValue(inputFmt_.get(), "stride", &stride)) {
273 return stride;
274 } else {
275 return nullopt;
276 }
277 }
278
GetInputIndex(Span & span)279 std::optional<uint32_t> TesterCapi::GetInputIndex(Span& span)
280 {
281 uint32_t inputIdx;
282 OH_AVMemory* mem;
283 {
284 unique_lock<mutex> lk(inputMtx_);
285 if (opt_.timeout == -1) {
286 inputCond_.wait(lk, [this] {
287 return !inputList_.empty();
288 });
289 } else {
290 bool ret = inputCond_.wait_for(lk, chrono::milliseconds(opt_.timeout), [this] {
291 return !inputList_.empty();
292 });
293 if (!ret) {
294 LOGE("time out");
295 return nullopt;
296 }
297 }
298 std::tie(inputIdx, mem) = inputList_.front();
299 inputList_.pop_front();
300 }
301 if (mem == nullptr) {
302 LOGE("null OH_AVMemory");
303 return nullopt;
304 }
305 char *dstVa = reinterpret_cast<char*>(OH_AVMemory_GetAddr(mem));
306 int size = OH_AVMemory_GetSize(mem);
307 if (dstVa == nullptr || size <= 0) {
308 LOGE("invalid va or size");
309 return nullopt;
310 }
311 span.va = dstVa;
312 span.capacity = static_cast<size_t>(size);
313 return inputIdx;
314 }
315
QueueInput(uint32_t idx,OH_AVCodecBufferAttr attr)316 bool TesterCapi::QueueInput(uint32_t idx, OH_AVCodecBufferAttr attr)
317 {
318 auto begin = std::chrono::steady_clock::now();
319 OH_AVErrCode err = opt_.isEncoder ? OH_VideoEncoder_PushInputData(codec_, idx, attr) :
320 OH_VideoDecoder_PushInputData(codec_, idx, attr);
321 if (err != AV_ERR_OK) {
322 LOGE("QueueInputBuffer failed");
323 return false;
324 }
325 CostRecorder::Instance().Update(begin,
326 opt_.isEncoder ? "OH_VideoEncoder_PushInputData" : "OH_VideoDecoder_PushInputData");
327 return true;
328 }
329
GetOutputIndex()330 std::optional<uint32_t> TesterCapi::GetOutputIndex()
331 {
332 uint32_t outIdx;
333 OH_AVCodecBufferAttr attr;
334 {
335 unique_lock<mutex> lk(outputMtx_);
336 if (opt_.timeout == -1) {
337 outputCond_.wait(lk, [this] {
338 return !outputList_.empty();
339 });
340 } else {
341 bool waitRes = outputCond_.wait_for(lk, chrono::milliseconds(opt_.timeout), [this] {
342 return !outputList_.empty();
343 });
344 if (!waitRes) {
345 LOGE("time out");
346 return nullopt;
347 }
348 }
349 std::tie(outIdx, std::ignore, attr) = outputList_.front();
350 outputList_.pop_front();
351 }
352 if (attr.flags & AVCODEC_BUFFER_FLAGS_EOS) {
353 LOGI("output eos, quit loop");
354 return nullopt;
355 }
356 return outIdx;
357 }
358
ReturnOutput(uint32_t idx)359 bool TesterCapi::ReturnOutput(uint32_t idx)
360 {
361 OH_AVErrCode err;
362 string apiName;
363 auto begin = std::chrono::steady_clock::now();
364 if (opt_.isEncoder) {
365 err = OH_VideoEncoder_FreeOutputData(codec_, idx);
366 apiName = "OH_VideoEncoder_FreeOutputData";
367 } else {
368 if (opt_.isBufferMode) {
369 err = OH_VideoDecoder_FreeOutputData(codec_, idx);
370 apiName = "OH_VideoDecoder_FreeOutputData";
371 } else {
372 err = OH_VideoDecoder_RenderOutputData(codec_, idx);
373 apiName = "OH_VideoDecoder_RenderOutputData";
374 }
375 }
376 if (err != AV_ERR_OK) {
377 LOGE("%{public}s failed", apiName.c_str());
378 return false;
379 }
380 CostRecorder::Instance().Update(begin, apiName);
381 return true;
382 }
383
SetOutputSurface(sptr<Surface> & surface)384 bool TesterCapi::SetOutputSurface(sptr<Surface>& surface)
385 {
386 OHNativeWindow *window = CreateNativeWindowFromSurface(&surface);
387 if (window == nullptr) {
388 LOGE("CreateNativeWindowFromSurface failed");
389 return false;
390 }
391 auto begin = std::chrono::steady_clock::now();
392 OH_AVErrCode err = OH_VideoDecoder_SetSurface(codec_, window);
393 if (err != AV_ERR_OK) {
394 LOGE("OH_VideoDecoder_SetSurface failed");
395 return false;
396 }
397 CostRecorder::Instance().Update(begin, "OH_VideoDecoder_SetSurface");
398 return true;
399 }
400
ConfigureDecoder()401 bool TesterCapi::ConfigureDecoder()
402 {
403 auto fmt = shared_ptr<OH_AVFormat>(OH_AVFormat_Create(), OH_AVFormat_Destroy);
404 IF_TRUE_RETURN_VAL_WITH_MSG(fmt == nullptr, false, "OH_AVFormat_Create failed");
405 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_WIDTH, opt_.dispW);
406 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_HEIGHT, opt_.dispH);
407 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_PIXEL_FORMAT, opt_.pixFmt);
408 OH_AVFormat_SetDoubleValue(fmt.get(), OH_MD_KEY_FRAME_RATE, opt_.frameRate);
409 OH_AVFormat_SetIntValue(fmt.get(), OH_MD_KEY_ROTATION, opt_.rotation);
410
411 auto begin = std::chrono::steady_clock::now();
412 OH_AVErrCode ret = OH_VideoDecoder_Configure(codec_, fmt.get());
413 if (ret != AV_ERR_OK) {
414 LOGE("OH_VideoDecoder_Configure failed");
415 return false;
416 }
417 CostRecorder::Instance().Update(begin, "OH_VideoDecoder_Configure");
418 return true;
419 }