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