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