参考链接:https://blog.csdn.net/leixiaohua1020/article/details/8652605
本文基于雷霄骅博士的文章,将原来支持播放本地文件修改为支持rtsp视频流
单单修改avformat_open_input中的url值,发现出现了大量的错误打印
error while decoding MB 44 66,bytestream -5
concealing 245 DC,245AC,245MV errors in P frame
max delay reached,need to consume packet
RTP:missed 58 packets
从打印中,可以看出丢包了,原因是原代码是单线程处理,先获取packet,再进行解析,当数据量较大,就出现了来不及解析的现象,所以丢包,导致视频出现丢帧卡顿。因此将代码修改为多线程,并替换了新的接口,实现了视频流播放。
详细代码如下:基于ffmpeg4.1和SDL2.0
#include <stdio.h>
#define __STDC_CONSTANT_MACROS
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
#include <libavutil/imgutils.h>
#include <SDL2/SDL.h>
};
#include <unistd.h>
#include <pthread.h>
#include <list>
#include <signal.h>
using namespace std;
//mutex
pthread_mutex_t g_video_pkt_mtx;
list<AVPacket> g_video_ptk_list;
int g_video_index = -1;
int g_process_run = 1;
//Refresh Event
#define SFM_REFRESH_EVENT (SDL_USEREVENT + 1)
#define SFM_BREAK_EVENT (SDL_USEREVENT + 2)
int thread_exit=0;
int thread_pause=0;
void *recv_packet_thread(void *arg)
{
AVFormatContext *pFormatCtx = (AVFormatContext *)arg;
AVPacket packet;
while(g_process_run)
{
if(av_read_frame(pFormatCtx, &packet)<0)
thread_exit=1;
if(packet.stream_index == g_video_index)
{
pthread_mutex_lock(&g_video_pkt_mtx);
if(g_video_ptk_list.size() >= 100)
{
g_video_ptk_list.clear();
printf("list is too long,clear\n");
}
g_video_ptk_list.push_back(packet);
pthread_mutex_unlock(&g_video_pkt_mtx);
}
}
return NULL;
}
int sfp_refresh_thread(void *opaque){
thread_exit=0;
thread_pause=0;
while (!thread_exit) {
if(!thread_pause){
SDL_Event event;
event.type = SFM_REFRESH_EVENT;
SDL_PushEvent(&event);
}
//SDL_Delay(40);
}
thread_exit=0;
thread_pause=0;
//Break
SDL_Event event;
event.type = SFM_BREAK_EVENT;
SDL_PushEvent(&event);
return 0;
}
static void signal_handler(int signal)
{
g_process_run = 0;
printf("quit\n");
}
int main(int argc, char* argv[])
{
AVFormatContext *pFormatCtx;
int i;
AVCodecContext *pCodecCtx;
AVCodec *pCodec;
AVFrame *pFrame,*pFrameYUV;
unsigned char *out_buffer;
AVPacket packet;
int ret, got_picture;
//------------SDL----------------
int screen_w,screen_h;
SDL_Window *screen;
SDL_Renderer* sdlRenderer;
SDL_Texture* sdlTexture;
SDL_Rect sdlRect;
SDL_Thread *video_tid;
SDL_Event event;
struct SwsContext *img_convert_ctx;
signal(SIGINT, signal_handler);
//char filepath[]="bigbuckbunny_480x272.h264";
char filepath[]="rtsp://admin:ms123456@192.168.2.137:554";
avformat_network_init();
pFormatCtx = avformat_alloc_context();
if(avformat_open_input(&pFormatCtx,filepath,NULL,NULL)!=0){
printf("Couldn't open input stream.\n");
return -1;
}
if(avformat_find_stream_info(pFormatCtx,NULL)<0){
printf("Couldn't find stream information.\n");
return -1;
}
g_video_index = -1;
for(i=0; i<pFormatCtx->nb_streams; i++)
if(pFormatCtx->streams[i]->codecpar->codec_type==AVMEDIA_TYPE_VIDEO){
g_video_index = i;
break;
}
if(g_video_index == -1){
printf("Didn't find a video stream.\n");
return -1;
}
pCodec = avcodec_find_decoder(pFormatCtx->streams[g_video_index]->codecpar->codec_id);
if(pCodec == NULL){
printf("Codec not found.\n");
return -1;
}
pCodecCtx = avcodec_alloc_context3(pCodec);
avcodec_parameters_to_context(pCodecCtx, pFormatCtx->streams[g_video_index]->codecpar);
if(avcodec_open2(pCodecCtx, pCodec,NULL) < 0){
printf("Could not open codec.\n");
return -1;
}
pFrame=av_frame_alloc();
pFrameYUV=av_frame_alloc();
out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, pCodecCtx->width, pCodecCtx->height,1));
av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
AV_PIX_FMT_YUV420P,pCodecCtx->width, pCodecCtx->height,1);
//Output Info-----------------------------
printf("---------------- File Information ---------------\n");
av_dump_format(pFormatCtx,0,filepath,0);
printf("-------------------------------------------------\n");
img_convert_ctx = sws_getContext(pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->width, pCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
printf( "Could not initialize SDL - %s\n", SDL_GetError());
return -1;
}
//SDL 2.0 Support for multiple windows
screen_w = pCodecCtx->width;
screen_h = pCodecCtx->height;
screen = SDL_CreateWindow("Simplest ffmpeg player's Window", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
screen_w, screen_h,SDL_WINDOW_OPENGL);
if(!screen) {
printf("SDL: could not create window - exiting:%s\n",SDL_GetError());
return -1;
}
sdlRenderer = SDL_CreateRenderer(screen, -1, 0);
//IYUV: Y + U + V (3 planes)
//YV12: Y + V + U (3 planes)
sdlTexture = SDL_CreateTexture(sdlRenderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING,pCodecCtx->width,pCodecCtx->height);
sdlRect.x=0;
sdlRect.y=0;
sdlRect.w=screen_w;
sdlRect.h=screen_h;
video_tid = SDL_CreateThread(sfp_refresh_thread,NULL,NULL);
//------------SDL End------------
//Event Loop
pthread_t recv_thread = 0;
if(pthread_create(&recv_thread,NULL,recv_packet_thread,(void *)pFormatCtx) < 0)
{
printf("pthread_create failed\n");
goto END;
}
pthread_detach(recv_thread);
pthread_mutex_init(&g_video_pkt_mtx,NULL);
while(g_process_run)
{
//Wait
SDL_WaitEvent(&event);
if(event.type==SFM_REFRESH_EVENT)
{
pthread_mutex_lock(&g_video_pkt_mtx);
if(!g_video_ptk_list.empty())
{
packet = g_video_ptk_list.front();
g_video_ptk_list.pop_front();
pthread_mutex_unlock(&g_video_pkt_mtx);
// av_read_frame(pFormatCtx, &packet);
ret = avcodec_send_packet(pCodecCtx, &packet);
if (ret < 0) {
fprintf(stderr, "Error sending a packet for decoding\n");
continue;
}
while (ret >= 0) {
ret = avcodec_receive_frame(pCodecCtx, pFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF){
break;
}
else if (ret < 0) {
fprintf(stderr, "Error during decoding\n");
break;
}
av_packet_unref(&packet);
sws_scale(img_convert_ctx, (const unsigned char* const*)pFrame->data, pFrame->linesize, 0, pCodecCtx->height, pFrameYUV->data, pFrameYUV->linesize);
//SDL---------------------------
SDL_UpdateTexture( sdlTexture, NULL, pFrameYUV->data[0], pFrameYUV->linesize[0] );
SDL_RenderClear( sdlRenderer );
//SDL_RenderCopy( sdlRenderer, sdlTexture, &sdlRect, &sdlRect );
SDL_RenderCopy( sdlRenderer, sdlTexture, NULL, NULL);
SDL_RenderPresent( sdlRenderer );
//SDL End-----------------------
}
}
else
{
pthread_mutex_unlock(&g_video_pkt_mtx);
}
}
else if(event.type==SDL_KEYDOWN){
//Pause
if(event.key.keysym.sym==SDLK_SPACE)
thread_pause=!thread_pause;
}else if(event.type==SDL_QUIT){
thread_exit=1;
}else if(event.type==SFM_BREAK_EVENT){
break;
}
}
END:
sws_freeContext(img_convert_ctx);
SDL_Quit();
//--------------
av_frame_free(&pFrameYUV);
av_frame_free(&pFrame);
avcodec_close(pCodecCtx);
avformat_close_input(&pFormatCtx);
pthread_mutex_destroy(&g_video_pkt_mtx);
g_video_ptk_list.clear();
return 0;
}
SRC := simplest_ffmpeg_player.cpp
OBJ := $(SRC:.o=.cpp)
INCLUDE := -I /home/share/lib_so/ffmpeg4.1/include -I /usr/include/
LIB := -lpthread -L /home/share/lib_so/ffmpeg4.1/lib -lavformat -lavcodec -lavutil -lswscale -L /usr/lib/i386-linux-gnu/ -lSDL2main -lSDL2
CFLAGS:=
demo:$(OBJ)
g++ -o $@ $< $(INCLUDE) $(LIB) $(CFLAGS)
.PHONY:clean
clean:
-rm -rf demo *.o