1/*
2 * Copyright (c) 2020-2021 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 "test_play_file_h265.h"
17
18#include "codec_interface.h"
19#include "securec.h"
20
21#include <chrono>
22#include <iostream>
23#include <thread>
24
25using namespace std;
26
27void PlayManager::Display(CODEC_HANDLETYPE decoderHdl)
28{
29    OutputInfo outInfo = {};
30    CodecBufferInfo outBuf = {};
31    outInfo.bufferCnt = 1;
32    outInfo.buffers = &outBuf;
33    constexpr uint32_t timeoutMs = 100; // timeout:100ms
34    int32_t ret = CodecDequeueOutput(decoderHdl, timeoutMs, nullptr, &outInfo);
35    if (ret == CODEC_ERR_FRAME_BUF_EMPTY) {
36        DEMO_LOG("CodecDequeueOutput deque empty.(ret=%d)", ret);
37        return;
38    } else if (ret != 0) {
39        DEMO_LOG("CodecDequeueOutput error.(ret=%d)", ret);
40        return;
41    }
42
43    ret = Write2VideoDevice(outInfo);
44    if (ret != 0) {
45        DEMO_LOG("Write video device failed.");
46    }
47
48    frameCnt_++;
49    ret = CodecQueueOutput(decoderHdl, &outInfo, timeoutMs, -1);
50    if (ret != 0) {
51        DEMO_LOG("CodecQueueOutput failed.(ret=%d)", ret);
52        return;
53    }
54}
55
56PlayManager::PlayManager()
57{
58    InitVideoOutput();
59}
60
61PlayManager::~PlayManager()
62{
63    DeinitVideoOutput();
64}
65
66int32_t PlayManager::InitVideoOutput()
67{
68    int32_t ret = HalPlayerSysInit();
69    if (ret != 0) {
70        DEMO_LOG("hal system init failed.");
71        return DEMO_ERR;
72    }
73
74    ret = HalPlayerVoInit(&voHdl_);
75    if (ret != 0) {
76        DEMO_LOG("video output init failed.");
77        return DEMO_ERR;
78    }
79
80    ret = HalStartVideoOutput(voHdl_);
81    if (ret != 0) {
82        DEMO_LOG("video output start failed.");
83        return DEMO_ERR;
84    }
85
86    return DEMO_OK;
87}
88
89int32_t PlayManager::DeinitVideoOutput()
90{
91    int32_t ret = HalStopVideoOutput(voHdl_);
92    if (ret != 0) {
93        DEMO_LOG("video output start failed.");
94        return DEMO_ERR;
95    }
96
97    HalPlayerVoDeinit(voHdl_);
98
99    return DEMO_OK;
100}
101
102int32_t PlayManager::Write2VideoDevice(OutputInfo &outputInfo)
103{
104    int32_t x = 200;
105    int32_t y = 200;
106    int32_t w = 960;
107    int32_t h = 540;
108
109    HalVideoOutputAttr voAttr = {x, y, w, h, 0};
110    int32_t ret = HalConfigVideoOutput(voHdl_, voAttr);
111    if (ret != 0) {
112        DEMO_LOG("HalConfigVideoOutput failed.");
113        return DEMO_ERR;
114    }
115
116    ret = HalWriteVo(voHdl_, outputInfo.vendorPrivate);
117    if (ret != 0) {
118        DEMO_LOG("HalWriteVo failed.");
119        return DEMO_ERR;
120    }
121
122    return DEMO_OK;
123}
124
125DecodeManager::DecodeManager()
126{
127    DecoderCreate();
128}
129
130DecodeManager::~DecodeManager()
131{
132    DecoderDestroy();
133}
134
135int32_t DecodeManager::DecoderCreate()
136{
137    int index = 0;
138    Param param[PARAM_MAX_NUM];
139    AvCodecMime mime = MEDIA_MIMETYPE_VIDEO_HEVC;
140    param[index].key = KEY_MIMETYPE;
141    param[index].val = (void *)&mime;
142    param[index].size = sizeof(AvCodecMime);
143    index++;
144    uint32_t width = 1920;
145    param[index].key = KEY_WIDTH;
146    param[index].val = (void *)&width;
147    param[index].size = sizeof(uint32_t);
148    index++;
149    uint32_t height = 1080;
150    param[index].key = KEY_HEIGHT;
151    param[index].val = (void *)&height;
152    param[index].size = sizeof(uint32_t);
153    index++;
154    uint32_t bufSize = 0;
155    param[index].key = KEY_BUFFERSIZE;
156    param[index].val = (void *)&bufSize;
157    param[index].size = sizeof(uint32_t);
158    index++;
159    CodecType domain = VIDEO_DECODER;
160    param[index].key = KEY_CODEC_TYPE;
161    param[index].val = (void *)&domain;
162    param[index].size = sizeof(CodecType);
163    index++;
164
165    int32_t ret = CodecInit();
166    if (ret != 0) {
167        DEMO_LOG("Codec init failed.(ret=%d)", ret);
168        return DEMO_ERR;
169    }
170
171    ret = CodecCreate("codec.avc.soft.decoder", param, index, &decoderHdl_);
172    if (ret != 0) {
173        DEMO_LOG("Codec create failed.(ret=%d)", ret);
174        return DEMO_ERR;
175    }
176
177    ret = CodecStart(decoderHdl_);
178    if (ret != 0) {
179        DEMO_LOG("Codec start failed.(ret=%d)", ret);
180        return DEMO_ERR;
181    }
182
183    return DEMO_OK;
184}
185
186void DecodeManager::DecoderDestroy()
187{
188    CodecStop(decoderHdl_);
189    CodecDestroy(decoderHdl_);
190}
191
192int32_t DecodeManager::DecodePack(uint8_t *addr, uint32_t len, uint64_t timeStampUs)
193{
194    InputInfo inputData = {};
195    CodecBufferInfo inBufInfo = {};
196    uint32_t timeoutMs = 100; // 100ms timeout
197    int32_t ret = CodecDequeInput(decoderHdl_, timeoutMs, &inputData);
198    if (ret != 0) {
199        DEMO_LOG("CodecDequeInput error.(ret=%d)", ret);
200        return DEMO_ERR;
201    }
202
203    inBufInfo.addr = addr;
204    inBufInfo.length = len;
205
206    inputData.bufferCnt = 1;
207    inputData.buffers = &inBufInfo;
208    inputData.pts = timeStampUs;
209    inputData.flag = 0;
210    while ((ret = CodecQueueInput(decoderHdl_, &inputData, timeoutMs)) == CODEC_ERR_STREAM_BUF_FULL) {
211        this_thread::sleep_for(chrono::milliseconds(timeoutMs));
212    }
213    if (ret != 0) {
214        DEMO_LOG("CodecQueueInput error.(ret=%d)", ret);
215        return DEMO_ERR;
216    }
217    frameCnt_++;
218    return DEMO_OK;
219}
220
221static int32_t ReadH265File(ifstream &file, char *buf, int32_t len)
222{
223    file.read(buf, len);
224    if (file) {
225        return len;
226    } else {
227        return file.gcount();
228    }
229}
230
231static int32_t GetNal(const uint8_t *bsData, int32_t dataSize, int32_t *startCodeSize, int32_t *nalSize)
232{
233    int32_t curNalOffset = 0;
234    int32_t nextNalOffset = 0;
235    int32_t offset;
236    uint8_t byte0 = 0;
237    uint8_t byte1 = 1;
238    uint8_t offset1 = 1;
239    uint8_t offset2 = 2;
240    uint8_t offset3 = 3;
241
242    for (offset = 0; offset < dataSize; offset++) {
243        if ((bsData[offset] == byte0) && (bsData[offset + offset1] == byte0) && (bsData[offset + offset2] == byte1)) {
244            *startCodeSize = 3; // h265 start frame length: 3->001
245            curNalOffset = offset;
246            break;
247        } else if ((bsData[offset] == byte0) && (bsData[offset + offset1] == byte0) &&
248                   (bsData[offset + offset2] == byte0) && (bsData[offset + offset3] == byte1)) {
249            *startCodeSize = 4; // h265 start frame length: 4->0001
250            curNalOffset = offset;
251            break;
252        }
253    }
254
255    if (offset == dataSize) {
256        return -1;
257    }
258
259    for (offset = curNalOffset + offset3; offset < dataSize; offset++) {
260        if ((bsData[offset] == byte0) && (bsData[offset + offset1] == byte0) && (bsData[offset + offset2] == byte1)) {
261            nextNalOffset = offset;
262            break;
263        } else if ((bsData[offset] == byte0) && (bsData[offset + offset1] == byte0) &&
264                   (bsData[offset + offset2] == byte0) && (bsData[offset + offset3] == 0x01)) {
265            nextNalOffset = offset;
266            break;
267        }
268    }
269
270    if (offset == dataSize) {
271        *nalSize = dataSize - curNalOffset;
272    } else {
273        *nalSize = nextNalOffset - curNalOffset;
274    }
275
276    return curNalOffset;
277}
278
279static void DisplayThrd(PlayManager *playMng, CODEC_HANDLETYPE decoderHdl, bool *stop, uint32_t timeStep)
280{
281    while (!(*stop)) {
282        this_thread::sleep_for(chrono::microseconds(timeStep));
283        playMng->Display(decoderHdl);
284    }
285    DEMO_LOG("play end.");
286}
287
288int main()
289{
290    DEMO_LOG("Demo begin");
291
292    PlayManager playMng;
293    DEMO_LOG("Init video output succeed.");
294
295    DecodeManager decodeMng;
296    CODEC_HANDLETYPE decoderHdl = decodeMng.GetHdl();
297    DEMO_LOG("Init decoder succeed.");
298
299    cout << "Type in file path:";
300    string fileName;
301    getline(cin, fileName);
302    if (fileName.empty()) {
303        fileName.assign("/tmp/1080P.h265");
304    }
305    ifstream file(fileName, ios::binary | ios::ate);
306    if (!file) {
307        DEMO_LOG("Openfile %s failed.", fileName.c_str());
308        return 0;
309    }
310    int32_t fileSize = file.tellg();
311    file.seekg(0, file.beg);
312    DEMO_LOG("Open file succeed.(size=%d)", fileSize);
313
314    constexpr uint32_t bufSize = 1024 * 1024;
315    uint8_t *buf = (uint8_t *)malloc(bufSize);
316    if (buf == nullptr) {
317        DEMO_LOG("Memory not enough.");
318        return 0;
319    }
320
321    constexpr uint32_t timeStep = 33333; // 33333us, 30fps
322    bool displayStop = false;
323    thread displayThrd(DisplayThrd, &playMng, decoderHdl, &displayStop, timeStep);
324    uint64_t timeStamp = 0;
325    int32_t bufLen = 0;
326    while (file) {
327        bufLen += ReadH265File(file, static_cast<char *>(buf + bufLen), bufSize - bufLen);
328
329        int32_t frameSize;
330        int32_t codeSize;
331        int32_t offset = 0;
332        offset = GetNal(buf, bufLen, &codeSize, &frameSize);
333        if (offset < 0) {
334            DEMO_LOG("Get nal frame failed.");
335            break;
336        }
337        int32_t ret = decodeMng.DecodePack(buf + offset, frameSize, timeStamp);
338        timeStamp += timeStep;
339        int result = memmove_s(buf, bufSize, buf + offset + frameSize, bufLen - offset - frameSize);
340        if (result != 0) {
341            DEMO_LOG("memmove_s failed.");
342            break;
343        }
344        bufLen = bufLen - offset - frameSize;
345        if (ret != DEMO_OK) {
346            DEMO_LOG("Decode one frame failed.");
347            continue;
348        }
349    }
350    while (playMng.GetCnt() != decodeMng.GetCnt()) {
351        this_thread::sleep_for(chrono::seconds(1)); // sleep 1s
352    }
353    displayStop = true;
354    displayThrd.join();
355    if (buf != nullptr) {
356        free(buf);
357    }
358
359    DEMO_LOG("Demo end");
360}
361