FFMPeg 多线程 简易播放器功能实现(非视音同步)

FFMPeg 多线程 简易播放器功能实现(非视音同步)

视频播放主要原理基于多线程和队列进行解耦

废话不多说直接来Code

#include <stdio.h>
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libswscale/swscale.h>
#include <SDL.h>
#include <SDL_thread.h>
#include <libswresample/swresample.h>
#include <libavutil/avutil.h>

//自定义事件
//刷新画面
#define SFM_REFRESH_EVENT  (SDL_USEREVENT + 1)
#define VIDEO_PICTURE_QUEUE_SIZE  2

#define SDL_AUDIO_BUFFER_SIZE 1024
#define MAX_AUDIO_FRAME_SIZE 192000


AVFormatContext * fmtCtx;
int audioIndex,videoIndex;
AVStream * audioSm = NULL;
AVStream *videoSm = NULL;

AVCodec * audioCodec = NULL;
AVCodec * videoCodec = NULL;
AVCodecContext* audioCodecCtx = NULL;
AVCodecContext* videoCodecCtx = NULL;

AVFrame * videoFrame = NULL;
AVFrame * pFrameYUV= NULL;
AVFrame * audioFrame;
unsigned char * out_buffer;

struct SwsContext * swsCtx;


SDL_Window * screen;
SDL_Renderer * render;
SDL_Texture * texture;
SDL_Rect rect;

//SDL_Event event;

typedef struct PacketQueue {
  AVPacketList *first_pkt, *last_pkt;
  int nb_packets;
  int size;
  SDL_mutex *mutex;
  SDL_cond *cond;
} PacketQueue;


typedef struct VideoPicture {
  AVFrame *frame;
  int width, height; /* source height & width */
  int allocated;
} VideoPicture;

SDL_cond *vpCon;
SDL_mutex *vpMutex;
VideoPicture vp[VIDEO_PICTURE_QUEUE_SIZE];
int vpIndex = -1;
int vpSize = 0;

unsigned char *audioBuff = NULL;
int audioIndex = 0;
int audioSize = 0;
int audioLastIndex = 0;
SDL_mutex *lock;
SDL_cond *con;
SwrContext *swr;
struct SwrContext *au_convert_ctx;
unsigned int audioLen = 0;
unsigned char *audioChunk = NULL;
unsigned char *audioPos = NULL;
SDL_AudioSpec audioSpec;
SDL_AudioSpec audioSpec1;
int out_buffer_size = -1;

PacketQueue videoQ;
PacketQueue audioQ;


void packet_queue_init(PacketQueue *q) {
  memset(q, 0, sizeof(PacketQueue));
  q->mutex = SDL_CreateMutex();
  q->cond = SDL_CreateCond();
}

int packet_queue_put(PacketQueue *q, AVPacket *pkt) {

  AVPacketList *pkt1;
  if(av_dup_packet(pkt) < 0) {
    return -1;
  }
  pkt1 = av_malloc(sizeof(AVPacketList));
  if (!pkt1)
    return -1;
  pkt1->pkt = *pkt;
  pkt1->next = NULL;

  SDL_LockMutex(q->mutex);

  if (!q->last_pkt)
    q->first_pkt = pkt1;
  else
    q->last_pkt->next = pkt1;
  q->last_pkt = pkt1;
  q->nb_packets++;
  q->size += pkt1->pkt.size;
  SDL_CondSignal(q->cond);

  SDL_UnlockMutex(q->mutex);
  return 0;
}
int quit = 0;
static int packet_queue_get(PacketQueue *q, AVPacket *pkt, int block)
{
  AVPacketList *pkt1;
  int ret;

  SDL_LockMutex(q->mutex);

  for(;;) {

    if(quit) {
      ret = -1;
      break;
    }

    pkt1 = q->first_pkt;
    if (pkt1) {
      q->first_pkt = pkt1->next;
      if (!q->first_pkt)
	q->last_pkt = NULL;
      q->nb_packets--;
      q->size -= pkt1->pkt.size;
      *pkt = pkt1->pkt;
      av_free(pkt1);
      ret = 1;
      break;
    } else if (!block) {
      ret = 0;
      break;
    } else {
      SDL_CondWait(q->cond, q->mutex);
    }
  }
  SDL_UnlockMutex(q->mutex);
  return ret;
}


void audioCallBack(void *userdata, Uint8 *stream, int len){
      SDL_memset(stream, 0, len);

    if (audioLen == 0)
        return;

    len = (len>audioLen?audioLen:len);

    SDL_MixAudio(stream,audioPos,len,SDL_MIX_MAXVOLUME);

    audioPos += len;
    audioLen -= len;


 //   SDL_Delay(10);



}

queneVP(AVFrame *f){
    SDL_LockMutex(vpMutex);
    if((vpIndex+1)>=VIDEO_PICTURE_QUEUE_SIZE){
        SDL_CondWait(vpCon,vpMutex);
    }
    vpIndex++;
    vp[vpIndex].frame = f;

    SDL_UnlockMutex(vpMutex);

}


int thread_exit=0;
//Thread
int sfp_refresh_thread(void *opaque)
{
	while (thread_exit==0) {
		SDL_Event event;
		event.type = SFM_REFRESH_EVENT;
		SDL_PushEvent(&event);
		//Wait 40 ms
		SDL_Delay(40);
	}
	return 0;
}


int ComponentOpen(){

   char * url = "D:/电影/9.mp4";
   int ret = 0;
   ret = avformat_open_input(&fmtCtx,url,NULL,NULL);
   if(ret<0){
       printf("open the file %s failed",url);
       return -1;
   }

   ret = avformat_find_stream_info(fmtCtx,NULL);
   if(ret<0){
       printf("find stream info failed");
       return -1;
   }

   ret =  av_find_best_stream(fmtCtx,AVMEDIA_TYPE_VIDEO,-1,-1,NULL,1);
   if(ret<0){
     printf("find video stream index failed");
     return -1;
   }else{
       videoIndex = ret;
       videoSm = fmtCtx->streams[videoIndex];
   }


   ret =  av_find_best_stream(fmtCtx,AVMEDIA_TYPE_AUDIO,-1,-1,NULL,1);
   if(ret<0){
     printf("find audio stream index failed");
     return -1;
   }else{
       audioIndex = ret;
       audioSm = fmtCtx->streams[audioIndex];
   }



   videoCodec = avcodec_find_decoder(videoSm->codecpar->codec_id);
   if(!videoCodec){
         printf("find video decoder failed");
         return -1;
   }

   videoCodecCtx = avcodec_alloc_context3(videoCodec);
   if(!videoCodecCtx){
         printf("alloc video decoder CTX failed");
         return -1;
   }


   audioCodec = avcodec_find_decoder(audioSm->codecpar->codec_id);
   if(!audioCodec){
         printf("find audio decoder failed");
         return -1;
   }

   audioCodecCtx = avcodec_alloc_context3(audioCodec);
   if(!audioCodecCtx){
         printf("alloc audio decoder CTX failed");
         return -1;
   }

   if(avcodec_parameters_to_context(videoCodecCtx,videoSm->codecpar)<0){
       fprintf(stderr,"copy parameters to context failed!");
       return -1;
   }


   ret = avcodec_open2(videoCodecCtx,videoCodec,NULL);
   if(ret<0){
        printf("video decoder open failed!");
        return -1;

   }

   if(avcodec_parameters_to_context(audioCodecCtx,audioSm->codecpar)<0){
       fprintf(stderr,"copy parameters to context failed!");
       return -1;
   }


   ret = avcodec_open2(audioCodecCtx,audioCodec,NULL);
   if(ret<0){
        printf("audio decoder open failed!");
        return -1;

   }

    return 1;
}


int displayVideo(AVFrame * frame){
            SDL_UpdateYUVTexture(texture, &rect,
                frame->data[0], frame->linesize[0],
                frame->data[1], frame->linesize[1],
                frame->data[2], frame->linesize[2]);

			//fprintf(stdout,"121................");
            SDL_RenderClear( render );
            SDL_RenderCopy( render, texture,  NULL, &rect);
            SDL_RenderPresent( render );
			//fprintf(stdout,"122................");

            //SDL_Delay(40);
}

doDisplayVideo(){

    SDL_LockMutex(vpMutex);
    if(vpIndex>=0){
        displayVideo(vp[vpIndex].frame);
        vpIndex-- ;

    }
    SDL_CondSignal(vpCon);
    SDL_UnlockMutex(vpMutex);
}

doQueneVp(){

    while(1){
        int ret = 0;
        AVPacket * pkt  = av_packet_alloc();
         ret =     packet_queue_get(&videoQ,pkt,0);

        if(ret <=0){
            continue;
        }

        ret = avcodec_send_packet(videoCodecCtx,pkt);
        if(ret < 0){
            printf("Error sending a packet for decoding\n");
            exit(1);
        }
        av_packet_unref(pkt);

        ret = avcodec_receive_frame(videoCodecCtx,videoFrame);
        if(ret == 0 ){
             ret = sws_scale(swsCtx,videoFrame->data,videoFrame->linesize,0,videoCodecCtx->height,pFrameYUV->data,pFrameYUV->linesize);
             queneVP(pFrameYUV);
        }

    }

}


int readFrame(){
   SDL_Event event;
   AVPacket *pkt = av_packet_alloc();
   int ret = 0;
   packet_queue_init(&videoQ);
   packet_queue_init(&audioQ);
   while(1){
		 if(av_read_frame(fmtCtx,pkt)>=0){
              if(pkt->stream_index == videoIndex){
                    packet_queue_put(&videoQ,pkt);
              }

              if(pkt->stream_index == audioIndex){
                     packet_queue_put(&audioQ,av_packet_clone(pkt));

              }

		    }

   }
    av_free_packet(pkt);

}

initVideoFrame(){
    videoFrame = av_frame_alloc();
    pFrameYUV=av_frame_alloc();
	out_buffer=(unsigned char *)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P,  videoCodecCtx->width, videoCodecCtx->height,1));
	av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize,out_buffer,
    AV_PIX_FMT_YUV420P,videoCodecCtx->width, videoCodecCtx->height,1);
    swsCtx = sws_getContext(videoCodecCtx->width,videoCodecCtx->height,videoCodecCtx->pix_fmt,videoCodecCtx->width,videoCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL, NULL);
}

initSDLForVideo(){
    if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
		fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
		return -1;
	}
	int screenW;
    int screenH;

    screenW=videoCodecCtx->width;
    screenH = videoCodecCtx->height;

    rect.x=0;
	rect.y=0;
	rect.w=screenW;
	rect.h=screenH;

    screen = SDL_CreateWindow("my first video",SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,screenW,screenH,SDL_WINDOW_OPENGL);
    render = SDL_CreateRenderer(screen,-1,0);
    texture = SDL_CreateTexture(render,SDL_PIXELFORMAT_IYUV,SDL_TEXTUREACCESS_STREAMING,screenW,screenH);
}


closeVideo(){
    avformat_close_input(fmtCtx);
	av_frame_free(&pFrameYUV);
	av_frame_free(&videoFrame);
	av_frame_free(&audioFrame);
	avcodec_close(videoCodecCtx);
}



initAudio(){

    uint64_t out_chn_layout = AV_CH_LAYOUT_STEREO;  //通道布局 输出双声道
    enum AVSampleFormat out_sample_fmt=AV_SAMPLE_FMT_S16; //声音格式
    int out_sample_rate=44100;   //采样率
    int out_nb_samples = -1;
    int out_channels = -1;        //通道数



    uint64_t in_chn_layout = -1;  //通道布局

    audioFrame = av_frame_alloc();

    out_nb_samples = audioCodecCtx->frame_size;
    out_channels = av_get_channel_layout_nb_channels(out_chn_layout);
    out_buffer_size = av_samples_get_buffer_size(NULL, out_channels, out_nb_samples,  out_sample_fmt, 1);
    audioBuff = (unsigned char *)av_malloc(MAX_AUDIO_FRAME_SIZE*2); //双声道
    printf("-------->out_buffer_size is %d\n",out_buffer_size);
    in_chn_layout = av_get_default_channel_layout(audioCodecCtx->channels);



    audioSpec.callback=audioCallBack;
    audioSpec.channels=audioCodecCtx->channels;
    audioSpec.format = AUDIO_S16SYS;
    audioSpec.silence = 0;
    audioSpec.freq= audioCodecCtx->sample_rate;
    audioSpec.userdata = audioCodecCtx;
    audioSpec.samples = SDL_AUDIO_BUFFER_SIZE;

    au_convert_ctx=swr_alloc_set_opts(NULL,
    out_chn_layout,                                /*out*/
    out_sample_fmt,                              /*out*/
    out_sample_rate,                             /*out*/
    in_chn_layout,                                  /*in*/
    audioCodecCtx->sample_fmt ,               /*in*/
    audioCodecCtx->sample_rate,               /*in*/
    0,
    NULL);
    swr_init(au_convert_ctx);
    SDL_OpenAudio(&audioSpec,NULL);
    SDL_PauseAudio(0);
}


doAudioPlay(){

                while(1){
                    int ret =0;

                    AVPacket * pkt  = av_packet_alloc();
                    ret =     packet_queue_get(&audioQ,pkt,0);

                    if(ret <=0){
                        continue;
                    }
                    ret = avcodec_send_packet(audioCodecCtx,pkt);
                    if(ret < 0){
                        printf("Error sending a packet for decoding\n");
                        exit(1);
                    }
                    av_packet_unref(pkt);

                    ret = avcodec_receive_frame(audioCodecCtx,audioFrame);


                    if(ret==0){

                          int nb = swr_convert(au_convert_ctx,&audioBuff, MAX_AUDIO_FRAME_SIZE,(const uint8_t **)audioFrame->data , audioFrame->nb_samples);
                          av_samples_get_buffer_size(NULL,audioCodecCtx->channels,audioFrame->nb_samples,audioCodecCtx->sample_fmt,1);

                          while(audioLen>0){
                            SDL_Delay(1);

                          }
                          audioChunk = (unsigned char *)audioBuff;
                          audioPos = audioChunk;
                          audioLen = out_buffer_size;

                    }
                }


}


int main(int args,char * argv[]){


    ComponentOpen();
    initAudio();
    initVideoFrame();
    initSDLForVideo();
    vpMutex = SDL_CreateMutex();
    vpCon = SDL_CreateCond();
    SDL_Thread *video_tid = SDL_CreateThread(sfp_refresh_thread,"videoRefresh",NULL);
    SDL_Thread *readFrame_tid = SDL_CreateThread(readFrame,"readFrame",NULL);
    SDL_Thread *doQueneVp_tid = SDL_CreateThread(doQueneVp,"doQueneVp",NULL);
    SDL_Thread *doAudioPlay_tid = SDL_CreateThread(doAudioPlay,"doAudioPlay",NULL);




    SDL_Event event;
    for(;;){
       SDL_WaitEvent(&event);
       if(event.type==SFM_REFRESH_EVENT){
          doDisplayVideo();
       }

    }

   printf("the video is end ....");
   SDL_Quit();
   closeVideo();
   avformat_close_input(&fmtCtx);
}

参考文献:雷霄骅 大神FFMpeg系列
FFMpeg 官方例子
FFMPEG 旅行例子
编写工具:codeblocks,c语言,sdl2,ffmpeg lib
操作系统:win10 64位

如有转载请附上来源谢谢!

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值