FFmpeg+SDL实现简易的rtsp视频流直播

参考链接: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:[email protected]: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

猜你喜欢

转载自blog.csdn.net/zouwm12/article/details/89434356