1 /*
2 * Copyright (C) 2025 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 #include "videodec_sample.h"
16 #include <arpa/inet.h>
17 #include <sys/time.h>
18 #include <utility>
19 using namespace OHOS;
20 using namespace OHOS::Media;
21 using namespace std;
22 namespace {
23 constexpr int32_t DEFAULT_W = 1920;
24 constexpr int32_t DEFAULT_H = 1080;
25 constexpr uint32_t SURFACE_SWITCH_FRAME = 9;
26 const uint32_t FC_H264[] = {139107, 1114, 474, 253, 282, 146, 197, 90, 108, 3214, 301, 77, 51, 43,
27 234, 210, 143, 108, 139107, 1114, 474, 253, 282, 146, 197, 90, 108};
28 constexpr uint32_t FC_LENGTH_H264 = sizeof(FC_H264) / sizeof(uint32_t);
29 constexpr string_view formatChangeInputFilePath = "/data/test/media/format_change_testseq.h264";
30 constexpr string_view outputSurfacePath = "/data/test/media/out.rgba";
31 } // namespace
32
OnError(OH_AVCodec * codec,int32_t errorCode,void * userData)33 static void OnError(OH_AVCodec *codec, int32_t errorCode, void *userData)
34 {
35 (void)codec;
36 (void)errorCode;
37 (void)userData;
38 cout << "Error received, errorCode:" << errorCode << endl;
39 }
40
OnOutputFormatChanged(OH_AVCodec * codec,OH_AVFormat * format,void * userData)41 static void OnOutputFormatChanged(OH_AVCodec *codec, OH_AVFormat *format, void *userData)
42 {
43 (void)codec;
44 (void)format;
45 (void)userData;
46 cout << "OnOutputFormatChanged received" << endl;
47 }
48
OnInputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,void * userData)49 static void OnInputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, void *userData)
50 {
51 (void)codec;
52 VDecSignal *signal = static_cast<VDecSignal *>(userData);
53 unique_lock<mutex> lock(signal->inMutex_);
54 signal->inQueue_.push(index);
55 signal->inBufferQueue_.push(data);
56 signal->inCond_.notify_all();
57 }
58
OnOutputBufferAvailable(OH_AVCodec * codec,uint32_t index,OH_AVMemory * data,OH_AVCodecBufferAttr * attr,void * userData)59 static void OnOutputBufferAvailable(OH_AVCodec *codec, uint32_t index, OH_AVMemory *data, OH_AVCodecBufferAttr *attr,
60 void *userData)
61 {
62 (void)codec;
63 VDecSignal *signal = static_cast<VDecSignal *>(userData);
64 if (attr) {
65 unique_lock<mutex> lock(signal->outMutex_);
66 signal->outQueue_.push(index);
67 signal->outBufferQueue_.push(data);
68 signal->attrQueue_.push(*attr);
69 signal->outCond_.notify_all();
70 } else {
71 cout << "OnOutputBufferAvailable error, attr is nullptr!" << endl;
72 }
73 }
74
TestConsumerListener(sptr<Surface> cs,std::string_view name)75 TestConsumerListener::TestConsumerListener(sptr<Surface> cs, std::string_view name) : cs_(cs)
76 {
77 outDir_ = std::make_unique<std::ofstream>();
78 outDir_->open(name.data(), std::ios::out | std::ios::binary);
79 }
80
~TestConsumerListener()81 TestConsumerListener::~TestConsumerListener()
82 {
83 if (outDir_ != nullptr) {
84 outDir_->close();
85 }
86 }
87
OnBufferAvailable()88 void TestConsumerListener::OnBufferAvailable()
89 {
90 sptr<SurfaceBuffer> buffer;
91 int32_t flushFence;
92 cs_->AcquireBuffer(buffer, flushFence, timestamp_, damage_);
93 (void)outDir_->write(reinterpret_cast<char *>(buffer->GetVirAddr()), buffer->GetSize());
94 cs_->ReleaseBuffer(buffer, -1);
95 }
96
GetSurface(std::string outputPath)97 static sptr<Surface> GetSurface(std::string outputPath)
98 {
99 sptr<Surface> cs = Surface::CreateSurfaceAsConsumer();
100 sptr<IBufferConsumerListener> listener = new TestConsumerListener(cs, outputPath);
101 cs->RegisterConsumerListener(listener);
102 auto p = cs->GetProducer();
103 sptr<Surface> ps = Surface::CreateSurfaceAsProducer(p);
104 return ps;
105 }
106
~VDecFuzzSample()107 VDecFuzzSample::~VDecFuzzSample()
108 {
109 if (videoDec_ != nullptr) {
110 OH_VideoDecoder_Destroy(videoDec_);
111 }
112 if (format_ != nullptr) {
113 OH_AVFormat_Destroy(format_);
114 }
115 }
116
SetParameter()117 int32_t VDecFuzzSample::SetParameter()
118 {
119 if (videoDec_ == nullptr) {
120 cout << "codec is nullptr" << endl;
121 return AV_ERR_UNKNOWN;
122 }
123 if (!isSurfaceMode) {
124 return AV_ERR_OK;
125 }
126 OH_AVFormat *format = OH_AVFormat_Create();
127 if (format == nullptr) {
128 cout << "create format failed" << endl;
129 return AV_ERR_UNKNOWN;
130 }
131 OH_AVFormat_SetIntValue(format, OH_MD_KEY_PIXEL_FORMAT, 1);
132 OH_AVFormat_SetIntValue(format, OH_MD_KEY_ROTATION, 0);
133 OH_AVFormat_SetIntValue(format, OH_MD_KEY_SCALING_MODE, 0);
134 int32_t ret = OH_VideoDecoder_SetParameter(videoDec_, format);
135 if (ret != AV_ERR_OK) {
136 cout << "set parameter failed" << endl;
137 return AV_ERR_UNKNOWN;
138 }
139 cout << "set parameter success" << endl;
140 return AV_ERR_OK;
141 }
142
ProceFunc()143 int32_t VDecFuzzSample::ProceFunc()
144 {
145 videoDec_ = OH_VideoDecoder_CreateByName("OH.Media.Codec.Decoder.Video.AVC");
146 if (videoDec_ == nullptr) {
147 cout << "create codec failed" << endl;
148 return AV_ERR_UNKNOWN;
149 }
150 signal_ = make_shared<VDecSignal>();
151 cb_ = {&OnError, &OnOutputFormatChanged, &OnInputBufferAvailable, &OnOutputBufferAvailable};
152 int32_t ret = OH_VideoDecoder_SetCallback(videoDec_, cb_, signal_.get());
153 if (ret != AV_ERR_OK) {
154 return AV_ERR_UNKNOWN;
155 }
156 format_ = OH_AVFormat_Create();
157 OH_AVFormat_SetIntValue(format_, OH_MD_KEY_WIDTH, DEFAULT_W);
158 OH_AVFormat_SetIntValue(format_, OH_MD_KEY_HEIGHT, DEFAULT_H);
159 ret = OH_VideoDecoder_Configure(videoDec_, format_);
160 if (ret != AV_ERR_OK) {
161 cout << "configure codec failed" << endl;
162 return AV_ERR_UNKNOWN;
163 }
164 if (isSurfaceMode) {
165 cout << "surface mode, create output surface" << endl;
166 surface_ = GetSurface(std::string(outputSurfacePath));
167 OHNativeWindow *nativeWindow = CreateNativeWindowFromSurface(&surface_);
168 ret = OH_VideoDecoder_SetSurface(videoDec_, nativeWindow);
169 if (ret != AV_ERR_OK) {
170 return AV_ERR_UNKNOWN;
171 }
172 } else {
173 cout << "buffer mode" << endl;
174 }
175 ret = OH_VideoDecoder_Start(videoDec_);
176 if (ret != AV_ERR_OK) {
177 cout << "start codec failed" << endl;
178 return AV_ERR_UNKNOWN;
179 }
180 ret = SetParameter();
181 if (ret != AV_ERR_OK) {
182 return AV_ERR_UNKNOWN;
183 }
184 isRunning_.store(true);
185 return AV_ERR_OK;
186 }
187
PrepareResource()188 void VDecFuzzSample::PrepareResource()
189 {
190 testFile_ = std::make_unique<std::ifstream>();
191 testFile_->open(formatChangeInputFilePath, std::ios::in | std::ios::binary);
192 if (!testFile_->is_open()) {
193 cout << "open input file failed" << endl;
194 isRunning_.store(false);
195 (void)OH_VideoDecoder_Stop(videoDec_);
196 testFile_->close();
197 testFile_.reset();
198 testFile_ = nullptr;
199 return;
200 }
201 }
202
FormatChangeInputFunc()203 void VDecFuzzSample::FormatChangeInputFunc()
204 {
205 PrepareResource();
206 while (true) {
207 if (!isRunning_.load()) {
208 return;
209 }
210 unique_lock<mutex> lock(signal_->inMutex_);
211 signal_->inCond_.wait(lock, [this]() { return (signal_->inQueue_.size() > 0 || !isRunning_.load()); });
212 if (!isRunning_.load()) {
213 return;
214 }
215 uint32_t index = signal_->inQueue_.front();
216 auto buffer = signal_->inBufferQueue_.front();
217 OH_AVCodecBufferAttr info = {0, 0, 0, AVCODEC_BUFFER_FLAGS_EOS};
218 if (frameCount_ < FC_LENGTH_H264) {
219 info.size = FC_H264[frameCount_];
220 char *fileBuffer = static_cast<char *>(malloc(sizeof(char) * info.size + 1));
221 (void)testFile_->read(fileBuffer, info.size);
222 if (memcpy_s(OH_AVMemory_GetAddr(buffer), OH_AVMemory_GetSize(buffer), fileBuffer, info.size) != EOK) {
223 free(fileBuffer);
224 isRunning_.store(false);
225 break;
226 }
227 free(fileBuffer);
228 info.flags = AVCODEC_BUFFER_FLAGS_NONE;
229 if (isFirstFrame_) {
230 info.flags = AVCODEC_BUFFER_FLAGS_CODEC_DATA;
231 isFirstFrame_ = false;
232 }
233 OH_VideoDecoder_PushInputData(videoDec_, index, info);
234 int32_t ret = SwitchSurface();
235 if (ret != AV_ERR_OK) {
236 isRunning_.store(false);
237 break;
238 }
239 frameCount_++;
240 } else {
241 OH_VideoDecoder_PushInputData(videoDec_, index, info);
242 break;
243 }
244 signal_->inQueue_.pop();
245 signal_->inBufferQueue_.pop();
246 }
247 if (testFile_ != nullptr) {
248 testFile_->close();
249 }
250 }
251
OutputFunc()252 void VDecFuzzSample::OutputFunc()
253 {
254 while (true) {
255 if (!isRunning_.load()) {
256 cout << "stop, exit" << endl;
257 break;
258 }
259 unique_lock<mutex> lock(signal_->outMutex_);
260 signal_->outCond_.wait(lock, [this]() { return (signal_->outQueue_.size() > 0 || !isRunning_.load()); });
261 if (!isRunning_.load()) {
262 cout << "wait to stop, exit" << endl;
263 break;
264 }
265 uint32_t index = signal_->outQueue_.front();
266 OH_AVCodecBufferAttr attr = signal_->attrQueue_.front();
267 OH_AVMemory *data = signal_->outBufferQueue_.front();
268 if (outFile_ != nullptr && attr.size != 0 && data != nullptr && OH_AVMemory_GetAddr(data) != nullptr) {
269 outFile_->write(reinterpret_cast<char *>(OH_AVMemory_GetAddr(data)), attr.size);
270 }
271 if (attr.flags == AVCODEC_BUFFER_FLAGS_EOS) {
272 cout << "decode eos, write frame:" << frameCount_ << endl;
273 isRunning_.store(false);
274 }
275 signal_->outBufferQueue_.pop();
276 signal_->attrQueue_.pop();
277 signal_->outQueue_.pop();
278 if (surface_) {
279 OH_VideoDecoder_RenderOutputData(videoDec_, index);
280 } else {
281 OH_VideoDecoder_FreeOutputData(videoDec_, index);
282 }
283 }
284 if (outFile_ != nullptr) {
285 outFile_->close();
286 }
287 }
288
SwitchSurface()289 int32_t VDecFuzzSample::SwitchSurface()
290 {
291 if (videoDec_ == nullptr) {
292 cout << "codec is nullptr" << endl;
293 return AV_ERR_UNKNOWN;
294 }
295 if (!isSurfaceMode) {
296 return AV_ERR_OK;
297 }
298 if (surface_ == nullptr) {
299 return AV_ERR_OK;
300 }
301 if (frameCount_ != SURFACE_SWITCH_FRAME) {
302 return AV_ERR_OK;
303 }
304 cout << "create new surface" << endl;
305 surface_ = GetSurface(std::string("/data/test/media/out_new.rgba"));
306 OHNativeWindow *nativeWindow = CreateNativeWindowFromSurface(&surface_);
307 int32_t ret = OH_VideoDecoder_SetSurface(videoDec_, nativeWindow);
308 if (ret != AV_ERR_OK) {
309 cout << "switch surface failed" << endl;
310 return AV_ERR_UNKNOWN;
311 }
312 cout << "switch surface success" << endl;
313 return AV_ERR_OK;
314 }
315
CheckCodecStatus()316 int32_t VDecFuzzSample::CheckCodecStatus()
317 {
318 if (videoDec_ == nullptr) {
319 cout << "codec is nullptr" << endl;
320 return AV_ERR_UNKNOWN;
321 }
322 int32_t ret = OH_VideoDecoder_Flush(videoDec_);
323 if (ret != AV_ERR_OK) {
324 cout << "flush codec failed" << endl;
325 return AV_ERR_UNKNOWN;
326 }
327 OH_AVFormat *format = OH_AVFormat_Create();
328 if (format == nullptr) {
329 cout << "create format failed" << endl;
330 return AV_ERR_UNKNOWN;
331 }
332 format = OH_VideoDecoder_GetOutputDescription(videoDec_);
333 if (format == nullptr) {
334 cout << "get output description failed" << endl;
335 return AV_ERR_UNKNOWN;
336 }
337 ret = OH_VideoDecoder_Start(videoDec_);
338 if (ret != AV_ERR_OK) {
339 cout << "start codec failed" << endl;
340 return AV_ERR_UNKNOWN;
341 }
342 ret = OH_VideoDecoder_Stop(videoDec_);
343 if (ret != AV_ERR_OK) {
344 cout << "stop codec failed" << endl;
345 return AV_ERR_UNKNOWN;
346 }
347 ret = OH_VideoDecoder_Start(videoDec_);
348 if (ret != AV_ERR_OK) {
349 cout << "start codec failed" << endl;
350 return AV_ERR_UNKNOWN;
351 }
352 ret = OH_VideoDecoder_Reset(videoDec_);
353 if (ret != AV_ERR_OK) {
354 cout << "reset codec failed" << endl;
355 return AV_ERR_UNKNOWN;
356 }
357 return AV_ERR_OK;
358 }
359
RunVideoDec()360 void VDecFuzzSample::RunVideoDec()
361 {
362 int32_t ret = ProceFunc();
363 if (ret != AV_ERR_OK) {
364 isRunning_.store(false);
365 return;
366 }
367
368 isRunning_.store(true);
369
370 inputLoop_ = make_unique<thread>(&VDecFuzzSample::FormatChangeInputFunc, this);
371
372 outputLoop_ = make_unique<thread>(&VDecFuzzSample::OutputFunc, this);
373
374 while (isRunning_.load()) {
375 sleep(1); // sleep 1s
376 }
377
378 isRunning_.store(false);
379 if (inputLoop_ != nullptr && inputLoop_->joinable()) {
380 unique_lock<mutex> lock(signal_->inMutex_);
381 signal_->inCond_.notify_all();
382 lock.unlock();
383 inputLoop_->join();
384 }
385 if (outputLoop_ != nullptr && outputLoop_->joinable()) {
386 unique_lock<mutex> lock(signal_->outMutex_);
387 signal_->outCond_.notify_all();
388 lock.unlock();
389 outputLoop_->join();
390 }
391 ret = CheckCodecStatus();
392 if (ret != AV_ERR_OK) {
393 cout << "codec status switch failed" << endl;
394 return;
395 }
396 }
397