dec_test.c 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564
  1. // SPDX-License-Identifier: GPL-2.0
  2. /*
  3. * Copyright (C) 2021 StarFive Technology Co., Ltd.
  4. */
  5. #include <stdio.h>
  6. #include <unistd.h>
  7. #include <signal.h>
  8. #include <getopt.h>
  9. #include <fcntl.h>
  10. #include "libavformat/avformat.h"
  11. #include <OMX_Core.h>
  12. #include <OMX_Component.h>
  13. #include <OMX_Video.h>
  14. #include <OMX_IndexExt.h>
  15. #include <sys/time.h>
  16. #include <sys/types.h>
  17. #include <sys/ipc.h>
  18. #include <sys/msg.h>
  19. #define OMX_INIT_STRUCTURE(a) \
  20. memset(&(a), 0, sizeof(a)); \
  21. (a).nSize = sizeof(a); \
  22. (a).nVersion.nVersion = 1; \
  23. (a).nVersion.s.nVersionMajor = 1; \
  24. (a).nVersion.s.nVersionMinor = 1; \
  25. (a).nVersion.s.nRevision = 1; \
  26. (a).nVersion.s.nStep = 1
  27. typedef struct Message
  28. {
  29. long msg_type;
  30. OMX_S32 msg_flag;
  31. OMX_BUFFERHEADERTYPE *pBuffer;
  32. } Message;
  33. typedef struct DecodeTestContext
  34. {
  35. OMX_HANDLETYPE hComponentDecoder;
  36. char sOutputFilePath[256];
  37. char sInputFilePath[256];
  38. char sOutputFormat[64];
  39. OMX_U32 ScaleWidth;
  40. OMX_U32 ScaleHeight;
  41. OMX_BUFFERHEADERTYPE *pInputBufferArray[64];
  42. OMX_BUFFERHEADERTYPE *pOutputBufferArray[64];
  43. AVFormatContext *avContext;
  44. OMX_STATETYPE comState;
  45. int msgid;
  46. } DecodeTestContext;
  47. DecodeTestContext *decodeTestContext;
  48. static OMX_S32 FillInputBuffer(DecodeTestContext *decodeTestContext, OMX_BUFFERHEADERTYPE *pInputBuffer);
  49. static OMX_BOOL disableEVnt = OMX_FALSE;
  50. static OMX_BOOL useNormal = OMX_FALSE;
  51. static OMX_BOOL justQuit = OMX_FALSE;
  52. static OMX_ERRORTYPE event_handler(
  53. OMX_HANDLETYPE hComponent,
  54. OMX_PTR pAppData,
  55. OMX_EVENTTYPE eEvent,
  56. OMX_U32 nData1,
  57. OMX_U32 nData2,
  58. OMX_PTR pEventData)
  59. {
  60. DecodeTestContext *pDecodeTestContext = (DecodeTestContext *)pAppData;
  61. switch (eEvent)
  62. {
  63. case OMX_EventPortSettingsChanged:
  64. {
  65. OMX_PARAM_PORTDEFINITIONTYPE pOutputPortDefinition;
  66. OMX_INIT_STRUCTURE(pOutputPortDefinition);
  67. pOutputPortDefinition.nPortIndex = 1;
  68. OMX_GetParameter(pDecodeTestContext->hComponentDecoder, OMX_IndexParamPortDefinition, &pOutputPortDefinition);
  69. OMX_U32 nOutputBufferSize = pOutputPortDefinition.nBufferSize;
  70. OMX_U32 nOutputBufferCount = pOutputPortDefinition.nBufferCountMin;
  71. printf("allocate %u output buffers size %u\r\n", nOutputBufferCount, nOutputBufferSize);
  72. printf("enable output port and alloc buffer\n");
  73. printf("======================================\r\n");
  74. printf("out put resolution [%dx%d]\r\n", pOutputPortDefinition.format.video.nFrameWidth, pOutputPortDefinition.format.video.nFrameHeight);
  75. printf("======================================\r\n");
  76. OMX_SendCommand(pDecodeTestContext->hComponentDecoder, OMX_CommandPortEnable, 1, NULL);
  77. for (int i = 0; i < nOutputBufferCount; i++)
  78. {
  79. OMX_BUFFERHEADERTYPE *pBuffer = NULL;
  80. OMX_AllocateBuffer(hComponent, &pBuffer, 1, NULL, nOutputBufferSize);
  81. pDecodeTestContext->pOutputBufferArray[i] = pBuffer;
  82. OMX_FillThisBuffer(hComponent, pBuffer);
  83. }
  84. }
  85. break;
  86. case OMX_EventBufferFlag:
  87. {
  88. Message data;
  89. data.msg_type = 1;
  90. data.msg_flag = -1;
  91. if (msgsnd(pDecodeTestContext->msgid, (void *)&data, sizeof(data) - sizeof(data.msg_type), 0) == -1)
  92. {
  93. fprintf(stderr, "msgsnd failed\n");
  94. }
  95. }
  96. break;
  97. case OMX_EventCmdComplete:
  98. {
  99. switch ((OMX_COMMANDTYPE)(nData1))
  100. {
  101. case OMX_CommandStateSet:
  102. {
  103. pDecodeTestContext->comState = (OMX_STATETYPE)(nData2);
  104. }
  105. case OMX_CommandPortDisable:
  106. {
  107. if (nData2 == 1)
  108. disableEVnt = OMX_TRUE;
  109. }
  110. break;
  111. default:
  112. break;
  113. }
  114. }
  115. break;
  116. case OMX_EventError:
  117. {
  118. printf("receive err event %d %d\n", nData1, nData2);
  119. switch (nData1)
  120. {
  121. case OMX_ErrorInsufficientResources:
  122. printf("out for memory for buffer, stop decode!\n");
  123. break;
  124. case OMX_ErrorInvalidState:
  125. printf("decoder component into invalid state!\n");
  126. pDecodeTestContext->comState = OMX_StateInvalid;
  127. break;
  128. default:
  129. break;
  130. }
  131. Message data;
  132. data.msg_type = 1;
  133. data.msg_flag = -1;
  134. if (msgsnd(decodeTestContext->msgid, (void *)&data, sizeof(data) - sizeof(data.msg_type), 0) == -1)
  135. {
  136. fprintf(stderr, "msgsnd failed\n");
  137. }
  138. justQuit = OMX_TRUE;
  139. }
  140. break;
  141. default:
  142. break;
  143. }
  144. return OMX_ErrorNone;
  145. }
  146. static void help()
  147. {
  148. printf("video_dec_test - omx video hardware decode unit test case\r\n\r\n");
  149. printf("Usage:\r\n\r\n");
  150. printf("./video_dec_test -i <input file> input file\r\n");
  151. printf(" -o <output file> output file\r\n");
  152. printf(" -f <format> i420/nv12/nv21\r\n");
  153. printf(" --scaleW=<width> (optional) scale width down. ceil8(width/8) <= scaledW <= width\r\n");
  154. printf(" --scaleH=<heitht> (optional) scale height down, ceil8(height/8) <= scaledH <= height\r\n\r\n");
  155. printf("./video_dec_test --help: show this message\r\n");
  156. }
  157. static OMX_ERRORTYPE fill_output_buffer_done_handler(
  158. OMX_HANDLETYPE hComponent,
  159. OMX_PTR pAppData,
  160. OMX_BUFFERHEADERTYPE *pBuffer)
  161. {
  162. DecodeTestContext *pDecodeTestContext = (DecodeTestContext *)pAppData;
  163. Message data;
  164. data.msg_type = 1;
  165. if ((pBuffer->nFlags) & (OMX_BUFFERFLAG_EOS == OMX_BUFFERFLAG_EOS))
  166. {
  167. data.msg_flag = -1;
  168. }
  169. else
  170. {
  171. data.msg_flag = 1;
  172. data.pBuffer = pBuffer;
  173. }
  174. if (msgsnd(pDecodeTestContext->msgid, (void *)&data, sizeof(data) - sizeof(data.msg_type), 0) == -1)
  175. {
  176. fprintf(stderr, "msgsnd failed\n");
  177. }
  178. return OMX_ErrorNone;
  179. }
  180. static OMX_ERRORTYPE empty_buffer_done_handler(
  181. OMX_HANDLETYPE hComponent,
  182. OMX_PTR pAppData,
  183. OMX_BUFFERHEADERTYPE *pBuffer)
  184. {
  185. DecodeTestContext *pDecodeTestContext = (DecodeTestContext *)pAppData;
  186. Message data;
  187. data.msg_type = 1;
  188. data.msg_flag = 0;
  189. data.pBuffer = pBuffer;
  190. if (msgsnd(pDecodeTestContext->msgid, (void *)&data, sizeof(data) - sizeof(data.msg_type), 0) == -1)
  191. {
  192. fprintf(stderr, "msgsnd failed\n");
  193. }
  194. return OMX_ErrorNone;
  195. }
  196. static void signal_handle(int sig)
  197. {
  198. printf("[%s,%d]: receive sig=%d \n", __FUNCTION__, __LINE__, sig);
  199. Message data;
  200. data.msg_type = 1;
  201. data.msg_flag = -1;
  202. if (msgsnd(decodeTestContext->msgid, (void *)&data, sizeof(data) - sizeof(data.msg_type), 0) == -1)
  203. {
  204. fprintf(stderr, "msgsnd failed\n");
  205. }
  206. justQuit = OMX_TRUE;
  207. }
  208. static OMX_S32 FillInputBuffer(DecodeTestContext *decodeTestContext, OMX_BUFFERHEADERTYPE *pInputBuffer)
  209. {
  210. AVFormatContext *avFormatContext = decodeTestContext->avContext;
  211. AVPacket *avpacket;
  212. OMX_S32 error;
  213. avpacket = av_packet_alloc();
  214. error = av_read_frame(avFormatContext, avpacket);
  215. if (error < 0)
  216. {
  217. if (error == AVERROR_EOF || avFormatContext->pb->eof_reached == OMX_TRUE)
  218. {
  219. pInputBuffer->nFlags = 0x1;
  220. pInputBuffer->nFilledLen = 0;
  221. return 0;
  222. }
  223. else
  224. {
  225. printf("%s:%d failed to av_read_frame, error: %s\n",
  226. __FUNCTION__, __LINE__, av_err2str(error));
  227. return 0;
  228. }
  229. }
  230. pInputBuffer->nFlags = 0x10;
  231. pInputBuffer->nFilledLen = avpacket->size;
  232. if(!pInputBuffer->pBuffer || !avpacket->data)
  233. return 0;
  234. memcpy(pInputBuffer->pBuffer, avpacket->data, avpacket->size);
  235. return avpacket->size;
  236. }
  237. int main(int argc, char **argv)
  238. {
  239. printf("=============================\r\n");
  240. OMX_S32 error;
  241. FILE *fb = NULL;
  242. decodeTestContext = malloc(sizeof(DecodeTestContext));
  243. memset(decodeTestContext, 0, sizeof(DecodeTestContext));
  244. OMX_S32 msgid = -1;
  245. msgid = msgget(IPC_PRIVATE, 0666 | IPC_CREAT);
  246. if (msgid < 0)
  247. {
  248. perror("get ipc_id error");
  249. return -1;
  250. }
  251. decodeTestContext->msgid = msgid;
  252. struct option longOpt[] = {
  253. {"output", required_argument, NULL, 'o'},
  254. {"input", required_argument, NULL, 'i'},
  255. {"format", required_argument, NULL, 'f'},
  256. {"scaleW", required_argument, NULL, 'w'},
  257. {"scaleH", required_argument, NULL, 'h'},
  258. {"normal", no_argument, NULL, 'n'},
  259. {"help", no_argument, NULL, '0'},
  260. {NULL, no_argument, NULL, 0},
  261. };
  262. char *shortOpt = "i:o:f:w:h:n";
  263. OMX_U32 c;
  264. OMX_S32 l;
  265. if (argc == 0)
  266. {
  267. help();
  268. return -1;
  269. }
  270. while ((c = getopt_long(argc, argv, shortOpt, longOpt, (int *)&l)) != -1)
  271. {
  272. switch (c)
  273. {
  274. case 'i':
  275. printf("input: %s\r\n", optarg);
  276. if (access(optarg, R_OK) != -1)
  277. {
  278. memcpy(decodeTestContext->sInputFilePath, optarg, strlen(optarg));
  279. }
  280. else
  281. {
  282. printf("input file not exist!\r\n");
  283. return -1;
  284. }
  285. break;
  286. case 'o':
  287. printf("output: %s\r\n", optarg);
  288. memcpy(decodeTestContext->sOutputFilePath, optarg, strlen(optarg));
  289. break;
  290. case 'f':
  291. printf("format: %s\r\n", optarg);
  292. memcpy(decodeTestContext->sOutputFormat, optarg, strlen(optarg));
  293. break;
  294. case 'w':
  295. printf("ScaleWidth: %s\r\n", optarg);
  296. decodeTestContext->ScaleWidth = atoi(optarg);
  297. break;
  298. case 'h':
  299. printf("ScaleHeight: %s\r\n", optarg);
  300. decodeTestContext->ScaleHeight = atoi(optarg);
  301. break;
  302. case 'n':
  303. printf("use normal buffer\r\n");
  304. useNormal = OMX_TRUE;
  305. break;
  306. case '0':
  307. default:
  308. help();
  309. return -1;
  310. }
  311. }
  312. if (decodeTestContext == NULL)
  313. {
  314. help();
  315. return -1;
  316. }
  317. /*ffmpeg init*/
  318. printf("init ffmpeg\r\n");
  319. AVFormatContext *avContext = NULL;
  320. AVCodecParameters *codecParameters = NULL;
  321. AVInputFormat *fmt = NULL;
  322. OMX_S32 videoIndex;
  323. if ((avContext = avformat_alloc_context()) == NULL)
  324. {
  325. printf("avformat_alloc_context fail\r\n");
  326. return -1;
  327. }
  328. avContext->flags |= AV_CODEC_FLAG_TRUNCATED;
  329. printf("avformat_open_input\r\n");
  330. if ((error = avformat_open_input(&avContext, decodeTestContext->sInputFilePath, fmt, NULL)))
  331. {
  332. printf("%s:%d failed to av_open_input_file error(%s), %s\n",
  333. __FILE__, __LINE__, av_err2str(error), decodeTestContext->sInputFilePath);
  334. avformat_free_context(avContext);
  335. return -1;
  336. }
  337. printf("avformat_find_stream_info\r\n");
  338. if ((error = avformat_find_stream_info(avContext, NULL)) < 0)
  339. {
  340. printf("%s:%d failed to avformat_find_stream_info. error(%s)\n",
  341. __FUNCTION__, __LINE__, av_err2str(error));
  342. avformat_close_input(&avContext);
  343. avformat_free_context(avContext);
  344. return -1;
  345. }
  346. printf("av_find_best_stream\r\n");
  347. videoIndex = av_find_best_stream(avContext, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
  348. if (videoIndex < 0)
  349. {
  350. printf("%s:%d failed to av_find_best_stream.\n", __FUNCTION__, __LINE__);
  351. return -1;
  352. }
  353. printf("video index = %d\r\n", videoIndex);
  354. decodeTestContext->avContext = avContext;
  355. /*get video info*/
  356. codecParameters = avContext->streams[videoIndex]->codecpar;
  357. printf("codec_id = %d, width = %d, height = %d\r\n", (int)codecParameters->codec_id,
  358. codecParameters->width, codecParameters->height);
  359. /*omx init*/
  360. OMX_HANDLETYPE hComponentDecoder = NULL;
  361. OMX_CALLBACKTYPE callbacks;
  362. int ret = OMX_ErrorNone;
  363. signal(SIGINT, signal_handle);
  364. printf("init omx\r\n");
  365. ret = OMX_Init();
  366. if (ret != OMX_ErrorNone)
  367. {
  368. printf("[%s,%d]: run OMX_Init failed. ret is %d \n", __FUNCTION__, __LINE__, ret);
  369. avformat_close_input(&avContext);
  370. avformat_free_context(avContext);
  371. return 1;
  372. }
  373. callbacks.EventHandler = event_handler;
  374. callbacks.FillBufferDone = fill_output_buffer_done_handler;
  375. callbacks.EmptyBufferDone = empty_buffer_done_handler;
  376. printf("get handle\r\n");
  377. if (codecParameters->codec_id == AV_CODEC_ID_H264)
  378. {
  379. if (useNormal)
  380. OMX_GetHandle(&hComponentDecoder, "OMX.sf.video_decoder.avc", decodeTestContext, &callbacks);
  381. else
  382. OMX_GetHandle(&hComponentDecoder, "OMX.sf.video_decoder.avc.internal", decodeTestContext, &callbacks);
  383. }
  384. else if (codecParameters->codec_id == AV_CODEC_ID_HEVC)
  385. {
  386. if (useNormal)
  387. OMX_GetHandle(&hComponentDecoder, "OMX.sf.video_decoder.hevc", decodeTestContext, &callbacks);
  388. else
  389. OMX_GetHandle(&hComponentDecoder, "OMX.sf.video_decoder.hevc.internal", decodeTestContext, &callbacks);
  390. }
  391. if (hComponentDecoder == NULL)
  392. {
  393. printf("could not get handle\r\n");
  394. OMX_Deinit();
  395. avformat_close_input(&avContext);
  396. avformat_free_context(avContext);
  397. return 0;
  398. }
  399. decodeTestContext->hComponentDecoder = hComponentDecoder;
  400. OMX_PARAM_PORTDEFINITIONTYPE pInputPortDefinition;
  401. OMX_INIT_STRUCTURE(pInputPortDefinition);
  402. pInputPortDefinition.nPortIndex = 0;
  403. OMX_GetParameter(hComponentDecoder, OMX_IndexParamPortDefinition, &pInputPortDefinition);
  404. pInputPortDefinition.format.video.nFrameWidth = codecParameters->width;
  405. pInputPortDefinition.format.video.nFrameHeight = codecParameters->height;
  406. OMX_SetParameter(hComponentDecoder, OMX_IndexParamPortDefinition, &pInputPortDefinition);
  407. OMX_PARAM_PORTDEFINITIONTYPE pOutputPortDefinition;
  408. OMX_INIT_STRUCTURE(pOutputPortDefinition);
  409. pOutputPortDefinition.nPortIndex = 1;
  410. OMX_GetParameter(decodeTestContext->hComponentDecoder, OMX_IndexParamPortDefinition, &pOutputPortDefinition);
  411. pOutputPortDefinition.format.video.nFrameWidth
  412. = decodeTestContext->ScaleWidth? decodeTestContext->ScaleWidth:codecParameters->width;
  413. pOutputPortDefinition.format.video.nFrameHeight
  414. = decodeTestContext->ScaleHeight? decodeTestContext->ScaleHeight:codecParameters->height;
  415. if (strstr(decodeTestContext->sOutputFormat, "nv12") != NULL)
  416. {
  417. pOutputPortDefinition.format.video.eColorFormat = OMX_COLOR_FormatYUV420SemiPlanar;
  418. }
  419. else if (strstr(decodeTestContext->sOutputFormat, "nv21") != NULL)
  420. {
  421. pOutputPortDefinition.format.video.eColorFormat = OMX_COLOR_FormatYVU420SemiPlanar;
  422. }
  423. else if (strstr(decodeTestContext->sOutputFormat, "i420") != NULL)
  424. {
  425. pOutputPortDefinition.format.video.eColorFormat = OMX_COLOR_FormatYUV420Planar;
  426. }
  427. else
  428. {
  429. printf("Unsupported color format!\r\n");
  430. goto end;
  431. }
  432. OMX_SetParameter(hComponentDecoder, OMX_IndexParamPortDefinition, &pOutputPortDefinition);
  433. disableEVnt = OMX_FALSE;
  434. OMX_SendCommand(hComponentDecoder, OMX_CommandPortDisable, 1, NULL);
  435. printf("wait for output port disable\r\n");
  436. while (!disableEVnt && !justQuit);
  437. if (justQuit)
  438. goto end;
  439. printf("output port disabled\r\n");
  440. OMX_SendCommand(hComponentDecoder, OMX_CommandStateSet, OMX_StateIdle, NULL);
  441. /*Alloc input buffer*/
  442. OMX_U32 nInputWidth = codecParameters->width;
  443. OMX_U32 nInputHeight = codecParameters->height;
  444. OMX_U32 nInputBufferSize = nInputWidth * nInputHeight * 2;
  445. OMX_U32 nInputBufferCount = pInputPortDefinition.nBufferCountActual;
  446. for (int i = 0; i < nInputBufferCount; i++)
  447. {
  448. OMX_BUFFERHEADERTYPE *pBuffer = NULL;
  449. OMX_AllocateBuffer(hComponentDecoder, &pBuffer, 0, NULL, nInputBufferSize);
  450. decodeTestContext->pInputBufferArray[i] = pBuffer;
  451. }
  452. printf("wait for Component idle\r\n");
  453. while (decodeTestContext->comState != OMX_StateIdle && !justQuit);
  454. if (justQuit)
  455. goto end;
  456. printf("Component in idle\r\n");
  457. for (int i = 0; i < nInputBufferCount; i++)
  458. {
  459. /*Fill Input Buffer*/
  460. FillInputBuffer(decodeTestContext, decodeTestContext->pInputBufferArray[i]);
  461. OMX_EmptyThisBuffer(hComponentDecoder, decodeTestContext->pInputBufferArray[i]);
  462. }
  463. fb = fopen(decodeTestContext->sOutputFilePath, "wb+");
  464. if (!fb)
  465. {
  466. fprintf(stderr, "output file open err or no output file patch %d\n", errno);
  467. goto end;
  468. }
  469. printf("start process\r\n");
  470. OMX_SendCommand(hComponentDecoder, OMX_CommandStateSet, OMX_StateExecuting, NULL);
  471. /*wait until decode finished*/
  472. Message data;
  473. while (OMX_TRUE)
  474. {
  475. if (msgrcv(msgid, (void *)&data, BUFSIZ, 0, 0) == -1)
  476. {
  477. fprintf(stderr, "msgrcv failed with errno: %d\n", errno);
  478. goto end;
  479. }
  480. switch (data.msg_flag)
  481. {
  482. case 0:
  483. {
  484. OMX_BUFFERHEADERTYPE *pBuffer = data.pBuffer;
  485. FillInputBuffer(decodeTestContext, pBuffer);
  486. OMX_EmptyThisBuffer(decodeTestContext->hComponentDecoder, pBuffer);
  487. }
  488. break;
  489. case 1:
  490. {
  491. OMX_BUFFERHEADERTYPE *pBuffer = data.pBuffer;
  492. fwrite(pBuffer->pBuffer, 1, pBuffer->nFilledLen, fb);
  493. if ((pBuffer->nFlags) & (OMX_BUFFERFLAG_EOS == OMX_BUFFERFLAG_EOS))
  494. {
  495. goto end;
  496. }
  497. else
  498. {
  499. OMX_FillThisBuffer(decodeTestContext->hComponentDecoder, pBuffer);
  500. }
  501. }
  502. break;
  503. case 2:
  504. break;
  505. default:
  506. goto end;
  507. }
  508. }
  509. end:
  510. /*free resource*/
  511. if (decodeTestContext->comState == OMX_StateExecuting)
  512. {
  513. OMX_SendCommand(hComponentDecoder, OMX_CommandStateSet, OMX_StateIdle, NULL);
  514. printf("wait for Component idle\r\n");
  515. while (decodeTestContext->comState != OMX_StateIdle);
  516. printf("Component in idle\r\n");
  517. }
  518. OMX_FreeHandle(hComponentDecoder);
  519. OMX_Deinit();
  520. if (fb)
  521. fclose(fb);
  522. avformat_close_input(&avContext);
  523. avformat_free_context(avContext);
  524. }