程序说明:共三个文件,
MyPlayerV2.c: 定义了两个线程,一个解码并将解码后的数据存放至队列;另外一个每40毫秒取一次队列里面的数据并显示。
video_frame_queue.h: 定义了一个队列,里面存放解码后的frame数据,并实现了基本的进/出队列操作。
Makefile:编译
存在的问题:
1. 窗口播放几秒后,失去焦点,导致画面变黑白,后续通过SDL Event解决;
2. 画面偶尔有闪屏现象, 暂不确定原因。
MyPlayerV2.c:
/*
* A simple player with FFMPEG4.0 and SDL2.0.8.
* Created by LiuWei@20180530
* 1. Only support video decoder, not support audio and subtitle.
* 2. Based on Producer-Consumer thread model.
*/
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <sys/stat.h>
#include <pthread.h>
#include <SDL2/SDL.h>
#include "video_frame_queue.h"
static AVFormatContext *fmt_ctx = NULL;
static AVStream *video_stream = NULL;
static AVCodec *dec = NULL;
static AVCodecContext *video_dec_ctx = NULL;
static int video_stream_index = -1;
static int end_of_video = 0;
/* The queue which stores uncompressed frame data */
avframe_q frame_q;
/* Output uncompressed frame, then push into queue */
static void *producer_thread(void *arg)
{
int ret, ret2, try_times = 0;
AVPacket pkt;
AVFrame *video_frame = av_frame_alloc();
if(!video_frame) {
printf("producer_thread(): Could not allocate frame\n");
return;
}
/* Initialize packet, send data to NULL, let the demuxer fill it */
av_init_packet(&pkt);
pkt.data = NULL;
pkt.size = 0;
/* Read frames */
while(av_read_frame(fmt_ctx, &pkt) >= 0) { /* The packet must be freed with av_packet_unref() */
if(pkt.stream_index != video_stream_index) {
av_packet_unref(&pkt);
continue;
}
ret = avcodec_send_packet(video_dec_ctx, &pkt);
if(ret < 0) {
av_packet_unref(&pkt);
continue;
}
do {
ret = avcodec_receive_frame(video_dec_ctx, video_frame);
if(ret < 0)
break;
else if(ret == 0) { /* Got a frame successfully */
try_times = 0;
/* It will return false if queue is full.
* Try it later. At most 3 times. */
ret2 = enqueue(&frame_q, video_frame);
while(ret2 == RET_FALSE && try_times < 3) {
SDL_Delay(40