video.cpp 22 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585
  1. #include "video.h"
  2. #include "packet.h"
  3. #include "frame.h"
  4. #include "player.h"
  5. static int queue_picture(player_stat_t *is, AVFrame *src_frame, double pts, double duration, int64_t pos)
  6. {
  7. frame_t *vp = NULL;
  8. if (!(vp = frame_queue_peek_writable(&is->video_frm_queue, !is->bread_finished))) {
  9. return -1;
  10. }
  11. vp->sar = src_frame->sample_aspect_ratio;
  12. vp->uploaded = 0;
  13. vp->width = src_frame->width;
  14. vp->height = src_frame->height;
  15. vp->format = src_frame->format;
  16. vp->pts = pts;
  17. vp->duration = duration;
  18. vp->pos = pos;
  19. //vp->serial = serial;
  20. //set_default_window_size(vp->width, vp->height, vp->sar);
  21. // 将AVFrame拷入队列相应位置
  22. av_frame_move_ref(vp->frame, src_frame);
  23. // 更新队列计数及写索引
  24. frame_queue_push(&is->video_frm_queue);
  25. return 0;
  26. }
  27. // 从packet_queue中取一个packet,解码生成frame
  28. static int video_decode_frame(AVCodecContext *p_codec_ctx, packet_queue_t *p_pkt_queue, AVFrame *frame, CMediaHostApi* hostapi)
  29. {
  30. int ret;
  31. while (0 == p_pkt_queue->abort_flag)
  32. {
  33. AVPacket pkt = {0};
  34. while (0 == p_pkt_queue->abort_flag)
  35. {
  36. // 3. 从解码器接收frame
  37. // 3.1 一个视频packet含一个视频frame
  38. // 解码器缓存一定数量的packet后,才有解码后的frame输出
  39. // frame输出顺序是按pts的顺序,如IBBPBBP
  40. // frame->pkt_pos变量是此frame对应的packet在视频文件中的偏移地址,值同pkt.pos
  41. ret = avcodec_receive_frame(p_codec_ctx, frame);
  42. if (ret < 0)
  43. {
  44. if (ret == AVERROR_EOF)
  45. {
  46. hostapi->Debug(MEDIA_LOG_DEBUG, "video avcodec_receive_frame(): the decoder has been fully flushed.");
  47. avcodec_flush_buffers(p_codec_ctx);
  48. return 0;
  49. }
  50. else if (ret == AVERROR(EAGAIN))
  51. {
  52. //hostapi->Debug("video avcodec_receive_frame(): output is not available in this state - " "user must try to send new input");
  53. break;
  54. }
  55. else
  56. {
  57. hostapi->Debug(MEDIA_LOG_DEBUG, "video avcodec_receive_frame(): other errors.");
  58. continue;
  59. }
  60. }
  61. else
  62. {
  63. frame->pts = frame->best_effort_timestamp;
  64. //frame->pts = frame->pkt_dts;
  65. return 1; // 成功解码得到一个视频帧,则返回
  66. }
  67. }
  68. // 1. 取出一个packet。使用pkt对应的serial赋值给d->pkt_serial
  69. if (packet_queue_get(p_pkt_queue, &pkt, true) < 0){
  70. return -1;
  71. }
  72. if (pkt.data == NULL)
  73. {
  74. // 复位解码器内部状态/刷新内部缓冲区。
  75. avcodec_flush_buffers(p_codec_ctx);
  76. }
  77. else
  78. {
  79. // 2. 将packet发送给解码器
  80. // 发送packet的顺序是按dts递增的顺序,如IPBBPBB
  81. // pkt.pos变量可以标识当前packet在视频文件中的地址偏移
  82. if (avcodec_send_packet(p_codec_ctx, &pkt) == AVERROR(EAGAIN)){
  83. hostapi->Debug(MEDIA_LOG_DEBUG, "receive_frame and send_packet both returned EAGAIN, which is an API violation.");
  84. }
  85. av_packet_unref(&pkt);
  86. }
  87. }
  88. }
  89. // 将视频包解码得到视频帧,然后写入picture队列
  90. static int video_decode_thread(void *arg)
  91. {
  92. player_stat_t *is = (player_stat_t *)arg;
  93. AVFrame *p_frame = av_frame_alloc();
  94. double pts = 0.0;
  95. double duration = 0.0;
  96. int ret=0;
  97. int got_picture = 0;
  98. if (p_frame == NULL){
  99. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "av_frame_alloc() for p_frame failed.");
  100. return AVERROR(ENOMEM);
  101. }
  102. while (0 == is->abort_request)
  103. {
  104. AVRational tb = is->p_video_stream[is->index]->time_base;
  105. AVRational frame_rate = av_guess_frame_rate(is->p_fmt_ctx[is->index], is->p_video_stream[is->index], NULL);
  106. got_picture = video_decode_frame(is->p_vcodec_ctx[is->index], &is->video_pkt_queue, p_frame, is->rvc_hostapi);
  107. if (got_picture < 0){
  108. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "video_decode_frame < 0, goto end");
  109. goto exit;
  110. }
  111. AVRational tbdata{ frame_rate.den, frame_rate.num };
  112. duration = (frame_rate.num && frame_rate.den ? av_q2d(tbdata) : 0); // 当前帧播放时长
  113. //duration = (frame_rate.num && frame_rate.den ? av_q2d((AVRational){frame_rate.den, frame_rate.num}) : 0); // 当前帧播放时长
  114. pts = (p_frame->pts == AV_NOPTS_VALUE) ? NAN : p_frame->pts * av_q2d(tb); // 当前帧显示时间戳
  115. ret = queue_picture(is, p_frame, pts, duration, p_frame->pkt_pos); // 将当前帧压入frame_queue
  116. av_frame_unref(p_frame);
  117. if (ret < 0){
  118. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "queue_picture return -1, goto end.");
  119. is->abort_request = 1;
  120. goto exit;
  121. }
  122. }
  123. exit:
  124. av_frame_free(&p_frame);
  125. //SDL_Delay(100);
  126. //is->abort_request = 1;
  127. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "video decode thread exit, thread id is %u, and is->abort_request = %d", SDL_ThreadID(), is->abort_request);
  128. return 0;
  129. }
  130. // 根据视频时钟与同步时钟(如音频时钟)的差值,校正delay值,使视频时钟追赶或等待同步时钟
  131. // 输入参数delay是上一帧播放时长,即上一帧播放后应延时多长时间后再播放当前帧,通过调节此值来调节当前帧播放快慢
  132. // 返回值delay是将输入参数delay经校正后得到的值
  133. static double compute_target_delay(double delay, player_stat_t *is)
  134. {
  135. double sync_threshold, diff = 0;
  136. /* update delay to follow master synchronisation source */
  137. /* if video is slave, we try to correct big delays by
  138. duplicating or deleting a frame */
  139. // 视频时钟与同步时钟(如音频时钟)的差异,时钟值是上一帧pts值(实为:上一帧pts + 上一帧至今流逝的时间差)
  140. diff = get_clock(&is->video_clk) - get_clock(&is->audio_clk);
  141. // delay是上一帧播放时长:当前帧(待播放的帧)播放时间与上一帧播放时间差理论值
  142. // diff是视频时钟与同步时钟的差值
  143. /* skip or repeat frame. We take into account the
  144. delay to compute the threshold. I still don't know
  145. if it is the best guess */
  146. // 若delay < AV_SYNC_THRESHOLD_MIN,则同步域值为AV_SYNC_THRESHOLD_MIN
  147. // 若delay > AV_SYNC_THRESHOLD_MAX,则同步域值为AV_SYNC_THRESHOLD_MAX
  148. // 若AV_SYNC_THRESHOLD_MIN < delay < AV_SYNC_THRESHOLD_MAX,则同步域值为delay
  149. sync_threshold = FFMAX(AV_SYNC_THRESHOLD_MIN, FFMIN(AV_SYNC_THRESHOLD_MAX, delay));
  150. if (!isnan(diff))
  151. {
  152. if (diff <= -sync_threshold) // 视频时钟落后于同步时钟,且超过同步域值
  153. delay = FFMAX(0, delay + diff); // 当前帧播放时刻落后于同步时钟(delay+diff<0)则delay=0(视频追赶,立即播放),否则delay=delay+diff
  154. else if (diff >= sync_threshold && delay > AV_SYNC_FRAMEDUP_THRESHOLD) // 视频时钟超前于同步时钟,且超过同步域值,但上一帧播放时长超长
  155. delay = delay + diff; // 仅仅校正为delay=delay+diff,主要是AV_SYNC_FRAMEDUP_THRESHOLD参数的作用
  156. else if (diff >= sync_threshold) // 视频时钟超前于同步时钟,且超过同步域值
  157. delay = 2 * delay; // 视频播放要放慢脚步,delay扩大至2倍
  158. }
  159. //is->rvc_hostapi->Debug("video: delay=%0.3f A-V=%f", delay, -diff);
  160. return delay;
  161. }
  162. static double vp_duration(player_stat_t *is, frame_t *vp, frame_t *nextvp) {
  163. if (vp->serial == nextvp->serial)
  164. {
  165. double duration = nextvp->pts - vp->pts;
  166. if (isnan(duration) || duration <= 0)
  167. return vp->duration;
  168. else
  169. return duration;
  170. } else {
  171. return 0.0;
  172. }
  173. }
  174. static void update_video_pts(player_stat_t *is, double pts, int64_t pos, int serial) {
  175. /* update current video pts */
  176. set_clock(&is->video_clk, pts, serial); // 更新vidclock
  177. //-sync_clock_to_slave(&is->extclk, &is->vidclk); // 将extclock同步到vidclock
  178. }
  179. static void video_display(player_stat_t *is)
  180. {
  181. frame_t *vp = NULL;
  182. vp = frame_queue_peek_last(&is->video_frm_queue);
  183. // 图像转换:p_frm_raw->data ==> p_frm_yuv->data
  184. // 将源图像中一片连续的区域经过处理后更新到目标图像对应区域,处理的图像区域必须逐行连续
  185. // plane: 如YUV有Y、U、V三个plane,RGB有R、G、B三个plane
  186. // slice: 图像中一片连续的行,必须是连续的,顺序由顶部到底部或由底部到顶部
  187. // stride/pitch: 一行图像所占的字节数,Stride=BytesPerPixel*Width+Padding,注意对齐
  188. // AVFrame.*data[]: 每个数组元素指向对应plane
  189. // AVFrame.linesize[]: 每个数组元素表示对应plane中一行图像所占的字节数
  190. sws_scale(is->img_convert_ctx[is->index], // sws context
  191. (const uint8_t *const *)vp->frame->data,// src slice
  192. vp->frame->linesize, // src stride
  193. 0, // src slice y
  194. is->p_vcodec_ctx[is->index]->height, // src slice height
  195. is->p_frm_yuv[is->index]->data, // dst planes
  196. is->p_frm_yuv[is->index]->linesize // dst strides
  197. );
  198. // 使用新的YUV像素数据更新SDL_Rect
  199. SDL_UpdateYUVTexture(is->sdl_video.texture, // sdl texture
  200. &is->sdl_video.rect, // sdl rect
  201. is->p_frm_yuv[is->index]->data[0], // y plane
  202. is->p_frm_yuv[is->index]->linesize[0], // y pitch
  203. is->p_frm_yuv[is->index]->data[1], // u plane
  204. is->p_frm_yuv[is->index]->linesize[1], // u pitch
  205. is->p_frm_yuv[is->index]->data[2], // v plane
  206. is->p_frm_yuv[is->index]->linesize[2] // v pitch
  207. );
  208. // 使用特定颜色清空当前渲染目标
  209. SDL_RenderClear(is->sdl_video.renderer);
  210. // 使用部分图像数据(texture)更新当前渲染目标
  211. SDL_RenderCopy(is->sdl_video.renderer, // sdl renderer
  212. is->sdl_video.texture, // sdl texture
  213. NULL, // src rect, if NULL copy texture
  214. &is->sdl_video.rect // dst rect
  215. );
  216. // 执行渲染,更新屏幕显示
  217. SDL_RenderPresent(is->sdl_video.renderer);
  218. }
  219. /* called to display each frame */
  220. static void video_refresh(void *opaque, double *remaining_time)
  221. {
  222. player_stat_t *is = (player_stat_t *)opaque;
  223. double time;
  224. static bool first_frame = true;
  225. retry:
  226. if (frame_queue_nb_remaining(&is->video_frm_queue) == 0) // 所有帧已显示
  227. {
  228. // nothing to do, no picture to display in the queue
  229. return;
  230. }
  231. double last_duration, duration, delay;
  232. frame_t *vp, *lastvp;
  233. /* dequeue the picture */
  234. lastvp = frame_queue_peek_last(&is->video_frm_queue); // 上一帧:上次已显示的帧
  235. vp = frame_queue_peek(&is->video_frm_queue); // 当前帧:当前待显示的帧
  236. // lastvp和vp不是同一播放序列(一个seek会开始一个新播放序列),将frame_timer更新为当前时间
  237. if (first_frame)
  238. {
  239. is->frame_timer = av_gettime_relative() / 1000000.0;
  240. first_frame = false;
  241. }
  242. // 暂停处理:不停播放上一帧图像
  243. if (is->paused)
  244. goto display;
  245. /* compute nominal last_duration */
  246. last_duration = vp_duration(is, lastvp, vp); // 上一帧播放时长:vp->pts - lastvp->pts
  247. delay = compute_target_delay(last_duration, is); // 根据视频时钟和同步时钟的差值,计算delay值
  248. time= av_gettime_relative()/1000000.0;
  249. // 当前帧播放时刻(is->frame_timer+delay)大于当前时刻(time),表示播放时刻未到
  250. if (time < is->frame_timer + delay) {
  251. // 播放时刻未到,则更新刷新时间remaining_time为当前时刻到下一播放时刻的时间差
  252. *remaining_time = FFMIN(is->frame_timer + delay - time, *remaining_time);
  253. // 播放时刻未到,则不播放,直接返回
  254. return;
  255. }
  256. // 更新frame_timer值
  257. is->frame_timer += delay;
  258. // 校正frame_timer值:若frame_timer落后于当前系统时间太久(超过最大同步域值),则更新为当前系统时间
  259. if (delay > 0 && time - is->frame_timer > AV_SYNC_THRESHOLD_MAX)
  260. {
  261. is->frame_timer = time;
  262. }
  263. SDL_LockMutex(is->video_frm_queue.frame_mutex);
  264. if (!isnan(vp->pts))
  265. {
  266. update_video_pts(is, vp->pts, vp->pos, vp->serial); // 更新视频时钟:时间戳、时钟时间
  267. }
  268. SDL_UnlockMutex(is->video_frm_queue.frame_mutex);
  269. // 是否要丢弃未能及时播放的视频帧
  270. if (frame_queue_nb_remaining(&is->video_frm_queue) > 1) // 队列中未显示帧数>1(只有一帧则不考虑丢帧)
  271. {
  272. frame_t *nextvp = frame_queue_peek_next(&is->video_frm_queue); // 下一帧:下一待显示的帧
  273. duration = vp_duration(is, vp, nextvp); // 当前帧vp播放时长 = nextvp->pts - vp->pts
  274. // 当前帧vp未能及时播放,即下一帧播放时刻(is->frame_timer+duration)小于当前系统时刻(time)
  275. if (time > is->frame_timer + duration)
  276. {
  277. frame_queue_next(&is->video_frm_queue); // 删除上一帧已显示帧,即删除lastvp,读指针加1(从lastvp更新到vp)
  278. goto retry;
  279. }
  280. }
  281. // 删除当前读指针元素,读指针+1。若未丢帧,读指针从lastvp更新到vp;若有丢帧,读指针从vp更新到nextvp
  282. frame_queue_next(&is->video_frm_queue);
  283. display:
  284. video_display(is); // 取出当前帧vp(若有丢帧是nextvp)进行播放
  285. }
  286. static int video_playing_thread(void *arg)
  287. {
  288. player_stat_t *is = (player_stat_t *)arg;
  289. double remaining_time = 0.0;
  290. while (0 == is->abort_request){
  291. if (remaining_time > 0.0){
  292. av_usleep((unsigned)(remaining_time * 1000000.0));
  293. }
  294. remaining_time = REFRESH_RATE;
  295. // 立即显示当前帧,或延时remaining_time后再显示
  296. video_refresh(is, &remaining_time);
  297. }
  298. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "video playing thread exit, thread id is %u, and is->abort_request = %d", SDL_ThreadID(), is->abort_request);
  299. return 0;
  300. }
  301. static Uint32 get_video_playing_wind_flag(eWindType_t eType)
  302. {
  303. Uint32 uFlag = SDL_WINDOW_OPENGL|SDL_WINDOW_BORDERLESS|SDL_WINDOW_ALWAYS_ON_TOP|SDL_WINDOW_POPUP_MENU;
  304. //if (eType == eFullScreen_Type){
  305. // uFlag |= SDL_WINDOW_FULLSCREEN;
  306. //}
  307. return uFlag;
  308. }
  309. static int open_video_playing(void* arg)
  310. {
  311. player_stat_t* is = (player_stat_t*)arg;
  312. int ret;
  313. int buf_size;
  314. uint8_t* buffer = NULL;
  315. SDL_Surface* IconSurface = NULL;
  316. for (size_t index = 0; index < is->uFilesCount; index++) {
  317. is->p_frm_yuv[index] = av_frame_alloc();
  318. if (is->p_frm_yuv[index] == NULL) {
  319. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "av_frame_alloc() for p_frm_raw failed");
  320. return -1;
  321. }
  322. int iplay_video_width = is->p_vcodec_ctx[index]->width;
  323. int iplay_video_height = is->p_vcodec_ctx[index]->height;
  324. if (eFullScreen_Type == is->eWindType) {
  325. iplay_video_width = is->iDisplayWidth;
  326. iplay_video_height = is->iDisplayHeight;
  327. }
  328. // 为AVFrame.*data[]手工分配缓冲区,用于存储sws_scale()中目的帧视频数据
  329. buf_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
  330. iplay_video_width,
  331. iplay_video_height,
  332. 1
  333. );
  334. // buffer将作为p_frm_yuv的视频数据缓冲区
  335. buffer = (uint8_t*)av_malloc(buf_size);
  336. if (buffer == NULL) {
  337. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "av_malloc() for buffer failed");
  338. return -1;
  339. }
  340. // 使用给定参数设定p_frm_yuv->data和p_frm_yuv->linesize
  341. ret = av_image_fill_arrays(is->p_frm_yuv[index]->data, // dst data[]
  342. is->p_frm_yuv[index]->linesize, // dst linesize[]
  343. buffer, // src buffer
  344. AV_PIX_FMT_YUV420P, // pixel format
  345. iplay_video_width, // width
  346. iplay_video_height, // height
  347. 1 // align
  348. );
  349. if (ret < 0) {
  350. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "av_image_fill_arrays() failed %d", ret);
  351. return -1;;
  352. }
  353. // A2. 初始化SWS context,用于后续图像转换
  354. // 此处第6个参数使用的是FFmpeg中的像素格式,对比参考注释B3
  355. // FFmpeg中的像素格式AV_PIX_FMT_YUV420P对应SDL中的像素格式SDL_PIXELFORMAT_IYUV
  356. // 如果解码后得到图像的不被SDL支持,不进行图像转换的话,SDL是无法正常显示图像的
  357. // 如果解码后得到图像的能被SDL支持,则不必进行图像转换
  358. // 这里为了编码简便,统一转换为SDL支持的格式AV_PIX_FMT_YUV420P==>SDL_PIXELFORMAT_IYUV
  359. is->img_convert_ctx[index] = sws_getContext(is->p_vcodec_ctx[index]->width, // src width
  360. is->p_vcodec_ctx[index]->height, // src height
  361. is->p_vcodec_ctx[index]->pix_fmt, // src format
  362. iplay_video_width, // dst width
  363. iplay_video_height, // dst height
  364. AV_PIX_FMT_YUV420P, // dst format
  365. SWS_BICUBIC, // flags
  366. NULL, // src filter
  367. NULL, // dst filter
  368. NULL // param
  369. );
  370. if (is->img_convert_ctx[index] == NULL) {
  371. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "sws_getContext() failed");
  372. return -1;
  373. }
  374. else {
  375. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "%s:%d is->img_convert_ctx[%d] = 0x%0x", __FUNCTION__, __LINE__, index, is->img_convert_ctx[index]);
  376. }
  377. // SDL_Rect赋值
  378. is->sdl_video.rect.x = 0;
  379. is->sdl_video.rect.y = 0;
  380. is->sdl_video.rect.w = iplay_video_width;
  381. is->sdl_video.rect.h = iplay_video_height;
  382. Uint32 uWindFlag = get_video_playing_wind_flag(is->eWindType);
  383. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "%s:%d %d %d %d %d", __FUNCTION__, __LINE__, is->iDisplayCx,
  384. is->iDisplayCy,
  385. is->sdl_video.rect.w,
  386. is->sdl_video.rect.h);
  387. // 1. 创建SDL窗口,SDL 2.0支持多窗口
  388. // SDL_Window即运行程序后弹出的视频窗口,同SDL 1.x中的SDL_Surface
  389. is->sdl_video.window = SDL_CreateWindow("rvc media player",
  390. is->iDisplayCx,
  391. is->iDisplayCy,
  392. is->sdl_video.rect.w,
  393. is->sdl_video.rect.h,
  394. uWindFlag
  395. );
  396. if (is->sdl_video.window == NULL) {
  397. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "SDL_CreateWindow() failed: %s.", SDL_GetError());
  398. return -1;
  399. }
  400. if (eFullScreen_Type == is->eWindType) {
  401. SDL_SetWindowFullscreen(is->sdl_video.window, SDL_WINDOW_FULLSCREEN_DESKTOP);
  402. }
  403. int cx = 0, cy = 0;
  404. SDL_GetWindowPosition(is->sdl_video.window, &cx, &cy);
  405. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "window flag is 0x%0x, cx = %d, cy = %d.", SDL_GetWindowFlags(is->sdl_video.window), cx, cy);
  406. if (NULL != is->piconpath) {
  407. IconSurface = SDL_LoadBMP(is->piconpath);
  408. if (NULL == IconSurface) {
  409. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "SDL_LoadBMP(%s) failed: %s", is->piconpath, SDL_GetError());
  410. }
  411. else {
  412. SDL_SetWindowIcon(is->sdl_video.window, IconSurface);
  413. SDL_FreeSurface(IconSurface);
  414. }
  415. }
  416. // 2. 创建SDL_Renderer
  417. // SDL_Renderer:渲染器
  418. is->sdl_video.renderer = SDL_CreateRenderer(is->sdl_video.window, -1, 0);
  419. if (is->sdl_video.renderer == NULL)
  420. {
  421. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "SDL_CreateRenderer() failed: %s", SDL_GetError());
  422. return -1;
  423. }
  424. // 3. 创建SDL_Texture
  425. // 一个SDL_Texture对应一帧YUV数据,同SDL 1.x中的SDL_Overlay
  426. is->sdl_video.texture = SDL_CreateTexture(is->sdl_video.renderer,
  427. SDL_PIXELFORMAT_IYUV,
  428. SDL_TEXTUREACCESS_STREAMING,
  429. is->sdl_video.rect.w,
  430. is->sdl_video.rect.h
  431. );
  432. if (is->sdl_video.texture == NULL)
  433. {
  434. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "SDL_CreateTexture() failed: %s", SDL_GetError());
  435. return -1;
  436. }
  437. SDL_Thread* video_playing = SDL_CreateThread(video_playing_thread, "video playing thread", is);
  438. if (NULL == video_playing) {
  439. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "SDL_Create video playing thread failed: %s.", SDL_GetError());
  440. return -1;
  441. }
  442. else {
  443. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "create %s success, and thread id is %u.", SDL_GetThreadName(video_playing), SDL_GetThreadID(video_playing));
  444. }
  445. }
  446. return 0;
  447. }
  448. static int open_video_stream(player_stat_t *is)
  449. {
  450. AVCodecParameters* p_codec_par = NULL;
  451. AVCodec* p_codec = NULL;
  452. AVCodecContext* p_codec_ctx = NULL;
  453. int ret = -1;
  454. for (size_t index = 0; index < is->uFilesCount; index++){
  455. AVStream* p_stream = is->p_video_stream[index];
  456. // 1. 为视频流构建解码器AVCodecContext
  457. // 1.1 获取解码器参数AVCodecParameters
  458. p_codec_par = p_stream->codecpar;
  459. // 1.2 获取解码器
  460. p_codec = avcodec_find_decoder(p_codec_par->codec_id);
  461. if (p_codec == NULL) {
  462. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "Cann't find codec!");
  463. return ret;
  464. }
  465. // 1.3 构建解码器AVCodecContext
  466. // 1.3.1 p_codec_ctx初始化:分配结构体,使用p_codec初始化相应成员为默认值
  467. p_codec_ctx = avcodec_alloc_context3(p_codec);
  468. if (p_codec_ctx == NULL) {
  469. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "avcodec_alloc_context3() failed");
  470. return ret;
  471. }
  472. // 1.3.2 p_codec_ctx初始化:p_codec_par ==> p_codec_ctx,初始化相应成员
  473. ret = avcodec_parameters_to_context(p_codec_ctx, p_codec_par);
  474. if (ret < 0) {
  475. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "avcodec_parameters_to_context() failed");
  476. return ret;
  477. }
  478. // 1.3.3 p_codec_ctx初始化:使用p_codec初始化p_codec_ctx,初始化完成
  479. ret = avcodec_open2(p_codec_ctx, p_codec, NULL);
  480. if (ret < 0) {
  481. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "avcodec_open2() failed %d", ret);
  482. return ret;
  483. }
  484. is->p_vcodec_ctx[index] = p_codec_ctx;
  485. }
  486. // 2. 创建视频解码线程
  487. SDL_Thread* video_decode = SDL_CreateThread(video_decode_thread, "video decode thread", is);
  488. if (NULL == video_decode) {
  489. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "SDL_Create video decode thread failed: %s.", SDL_GetError());
  490. return -1;
  491. }
  492. else {
  493. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "create %s success, and thread id is %u.", SDL_GetThreadName(video_decode), SDL_GetThreadID(video_decode));
  494. }
  495. return 0;
  496. }
  497. int open_video(player_stat_t *is)
  498. {
  499. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "enter open_video()");
  500. open_video_stream(is);
  501. open_video_playing(is);
  502. is->rvc_hostapi->Debug(MEDIA_LOG_DEBUG, "exit open_video()");
  503. return 0;
  504. }