C++ 解码音频和视频并处理这两个流——ffmpeg、sdl、opencv

C++ 解码音频和视频并处理这两个流——ffmpeg、sdl、opencv,c++,opencv,ffmpeg,sdl,C++,Opencv,Ffmpeg,Sdl,我的目标是独立地处理mpeg-2文件的音频和视频,并在两个流上保持同步。视频的持续时间最多约为1或2分钟 首先,遵循这个“opencv阅读视频(并执行此过程)”,ffmpeg播放音频,SDL播放这两个视频”听起来很完美。考虑到最近ffmpeg命名的变化,我对代码做了一些修改。在64位机器上使用cmake进行编译是可以的。打开编解码器时出现错误“不支持的编解码器[3]”。 代码如下 其次,我正在寻找处理两个流上的同步性的代码 #包括“opencv/highgui.h” #包括“opencv/cv

我的目标是独立地处理mpeg-2文件的音频和视频,并在两个流上保持同步。视频的持续时间最多约为1或2分钟

  • 首先,遵循这个“opencv阅读视频(并执行此过程)”,ffmpeg播放音频,SDL播放这两个视频”听起来很完美。考虑到最近ffmpeg命名的变化,我对代码做了一些修改。在64位机器上使用cmake进行编译是可以的。打开编解码器时出现错误“不支持的编解码器[3]”。 代码如下

  • 其次,我正在寻找处理两个流上的同步性的代码


  • #包括“opencv/highgui.h”
    #包括“opencv/cv.h”
    #ifndef INT64_C
    #定义INT64_C(C)(C#LL)
    #定义UINT64_C(C)(C#ULL)
    #恩迪夫
    外部“C”{
    #包括
    #包括
    #包括
    #包括
    }
    #包括
    #包括
    #包括
    使用名称空间cv;
    #定义SDL_音频_缓冲区_大小1024
    typedef结构包队列
    {
    AVPacketList*第一次包装,*最后一次包装;
    int nb_包;
    整数大小;
    SDL_互斥体*互斥体;
    SDL_cond*cond;
    }打包队列;
    包队列音频Q;
    int audioStream=-1;
    int videoStream=-1;
    int-quit=0;
    SDL_表面*屏幕=空;
    SDL_Surface*Surface=NULL;
    AVFormatContext*pFormatCtx=NULL;
    AVCodecContext*aCodecCtx=NULL;
    AVCodecContext*pCodecCtx=NULL;
    无效显示框(IplImage*img){
    如果(!屏幕){
    屏幕=SDL_设置视频模式(img->width,img->height,0,0);
    如果(!屏幕){
    fprintf(stderr,“SDL:无法设置视频模式-正在退出\n”);
    出口(1);
    }
    }
    //假设IplImage打包为BGR 24位
    SDL_Surface*Surface=SDL_CreateRGBSurfaceFrom((void*)img->imageData,
    img->width,
    img->高度,
    img->深度*img->n通道,
    img->widthStep,
    0xff0000,0x00ff00,0x0000ff,0
    );
    SDL_BlitSurface(表面,0,屏幕,0);
    SDL_翻转(屏幕);
    }
    无效数据包队列初始化(数据包队列*q){
    memset(q,0,sizeof(PacketQueue));
    q->mutex=SDL_CreateMutex();
    q->cond=SDL_CreateCond();
    }
    int packet_queue_put(PacketQueue*q,AVPacket*pkt){
    AVPacketList*pkt1;
    如果(av_dup_数据包(pkt)<0){
    返回-1;
    }
    pkt1=(AVPacketList*)av_malloc(sizeof(AVPacketList));
    //pkt1=(AVPacketList*)malloc(sizeof(AVPacketList));
    如果(!pkt1)返回-1;
    pkt1->pkt=*pkt;
    pkt1->next=NULL;
    SDL_锁互斥(q->mutex);
    如果(!q->last\u pkt)
    q->first_pkt=pkt1;
    其他的
    q->last\u pkt->next=pkt1;
    q->last_pkt=pkt1;
    q->nb_数据包++;
    q->size+=pkt1->pkt.size;
    SDL炣condition(q->cond);
    SDL_解锁互斥(q->互斥);
    返回0;
    }
    静态int数据包队列获取(数据包队列*q,AVPacket*pkt,int块){
    AVPacketList*pkt1;
    int ret;
    SDL_锁互斥(q->mutex);
    对于(;;){
    如果(退出){
    ret=-1;
    打破
    }
    pkt1=q->first\u pkt;
    if(pkt1){
    q->first\U pkt=pkt1->next;
    如果(!q->first\u pkt)
    q->last_pkt=NULL;
    q->nb_数据包--;
    q->size-=pkt1->pkt.size;
    *pkt=pkt1->pkt;
    无av_(pkt1);
    //免费(pkt1);
    ret=1;
    打破
    }
    else如果(!块){
    ret=0;
    打破
    }
    否则{
    SDL_CondWait(q->cond,q->mutex);
    }
    }
    SDL_解锁互斥(q->互斥);
    返回ret;
    }
    int audio_decode_帧(AVCODECTCONTEXT*aCodecCtx,uint8_t*audio_buf,int buf_大小){
    静态数据包;
    静态uint8_t*音频_pkt_数据=NULL;
    静态int-audio\u-pkt\u-size=0;
    int len1,数据大小;
    对于(;;){
    同时(音频大小>0){
    数据大小=基本大小;
    len1=avcodec\U decode\U audio3(aCodecCtx,(int16\u t*)音频\u buf和数据\u大小和pkt);
    if(len1<0){
    //如果出现错误,跳过帧
    音频大小=0;
    打破
    }
    音频数据+=len1;
    音频大小-=len1;
    如果(数据大小为0){
    如果(音频索引>=音频大小){
    //我们已经发送了所有数据;获取更多信息
    音频大小=音频解码帧(aCodecCtx、音频大小、大小(音频大小));
    如果(音频大小<0){
    //如果出现错误,则输出静音
    音频大小=1024;//任意?
    memset(音频大小,0,音频大小);
    }
    否则{
    音频大小=音频大小;
    }
    音频索引=0;
    }
    len1=音频大小-音频索引;
    如果(len1>len)
    len1=len;
    memcpy(流(uint8_t*)音频+音频索引,len1);
    len-=len1;
    流+=len1;
    音频索引+=len1;
    }
    }
    无效设置\u ffmpeg(字符*文件名)
    {
    if(avformat\u open\u输入(&pFormatCtx,filename,NULL,NULL)!=0){
    fprintf(stderr,“FFmpeg无法打开文件%s!\n”,文件名);
    出口(-1);
    }
    如果(av查找流信息(pFormatCtx)<0){
    fprintf(stderr,“FFmpeg无法检索流信息!\n”);
    出口(-1);
    }
    //将有关文件的信息转储到标准错误
    av_转储_格式(pFormatCtx,0,文件名,0);
    //查找第一个视频流
    int i=0;
    对于(i;inb_流;i++){
    如果(pFormatCtx->streams[i]->codec->codec\u type==AVMEDIA\u type\u VIDEO&&videoStream<0){
    视频流=i;
    }
    如果(pFormatCtx->streams[i]->codec->codec\u type==AVMEDIA\u type\u视频和音频流<0){
    音频流=i;
    }
    }
    如果(视频流==-1){
    fprintf(stderr,“在%s!\n中找不到视频流”,文件名);
    出口(-1);
    }
    如果(音频流==-1){
    fprintf(标准,“无音频流
    
    #include "opencv/highgui.h"
    #include "opencv/cv.h"
    
    #ifndef INT64_C
    #define INT64_C(c) (c ## LL)
    #define UINT64_C(c) (c ## ULL)
    #endif
    
    extern "C"{
    #include <SDL/SDL.h>
    #include <SDL/SDL_thread.h>
    #include <libavcodec/avcodec.h>
    #include <libavformat/avformat.h>
    }
    
    #include <iostream>
    #include <stdio.h>
    #include <malloc.h>
    
    using namespace cv;
    
    #define SDL_AUDIO_BUFFER_SIZE 1024
    
    typedef struct PacketQueue
    {
       AVPacketList *first_pkt, *last_pkt;
       int nb_packets;
       int size;
       SDL_mutex *mutex;
       SDL_cond *cond;
    } PacketQueue;
    PacketQueue audioq;
    
    int audioStream = -1;
    int videoStream = -1;
    int quit = 0;
    
    SDL_Surface* screen = NULL;
    SDL_Surface* surface = NULL;
    
    AVFormatContext* pFormatCtx = NULL;
    AVCodecContext* aCodecCtx = NULL;
    AVCodecContext* pCodecCtx = NULL;
    
    void show_frame(IplImage* img){
       if (!screen){
          screen = SDL_SetVideoMode(img->width, img->height, 0, 0);
          if (!screen){
             fprintf(stderr, "SDL: could not set video mode - exiting\n");
             exit(1);
          }
       }
       // Assuming IplImage packed as BGR 24bits
       SDL_Surface* surface = SDL_CreateRGBSurfaceFrom((void*)img->imageData,
                                                       img->width,
                                                       img->height,
                                                       img->depth * img->nChannels,
                                                       img->widthStep,
                                                       0xff0000, 0x00ff00, 0x0000ff, 0
                                                      );
    
       SDL_BlitSurface(surface, 0, screen, 0);
       SDL_Flip(screen);
    }
    
    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 = (AVPacketList*) av_malloc(sizeof(AVPacketList));
       //pkt1 = (AVPacketList*) 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;
    }
    
    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);
             //free(pkt1);
             ret = 1;
             break;
          }
    
          else if (!block){
             ret = 0;
             break;
          }
          else{
             SDL_CondWait(q->cond, q->mutex);
          }
       }
    
       SDL_UnlockMutex(q->mutex);
       return ret;
    }
    
    int audio_decode_frame(AVCodecContext *aCodecCtx, uint8_t *audio_buf, int buf_size){
       static AVPacket pkt;
       static uint8_t *audio_pkt_data = NULL;
       static int audio_pkt_size = 0;
    
       int len1, data_size;
    
       for (;;){
          while (audio_pkt_size > 0){
             data_size = buf_size;
             len1 = avcodec_decode_audio3(aCodecCtx, (int16_t*)audio_buf, &data_size, &pkt);
             if (len1 < 0){
                // if error, skip frame
                audio_pkt_size = 0;
                break;
             }
             audio_pkt_data += len1;
             audio_pkt_size -= len1;
             if (data_size <= 0){
                // No data yet, get more frames
                continue;
             }
             // We have data, return it and come back for more later
             return data_size;
         }
    
         if (pkt.data)
            av_free_packet(&pkt);
         if (quit) return -1;
         if (packet_queue_get(&audioq, &pkt, 1) < 0) return -1;
         audio_pkt_data = pkt.data;
         audio_pkt_size = pkt.size;
      }
    }
    
    void audio_callback(void *userdata, Uint8 *stream, int len){
      AVCodecContext *aCodecCtx = (AVCodecContext *)userdata;
      int len1, audio_size;
    
      static uint8_t audio_buf[(AVCODEC_MAX_AUDIO_FRAME_SIZE * 3) / 2];
      static unsigned int audio_buf_size = 0;
      static unsigned int audio_buf_index = 0;
    
      while (len > 0){
         if (audio_buf_index >= audio_buf_size){
            // We have already sent all our data; get more
            audio_size = audio_decode_frame(aCodecCtx, audio_buf, sizeof(audio_buf));
            if(audio_size < 0){
               // If error, output silence
               audio_buf_size = 1024; // arbitrary?
               memset(audio_buf, 0, audio_buf_size);
            }
            else{
               audio_buf_size = audio_size;
            }
            audio_buf_index = 0;
        }
    
        len1 = audio_buf_size - audio_buf_index;
        if (len1 > len)
           len1 = len;
        memcpy(stream, (uint8_t *)audio_buf + audio_buf_index, len1);
        len -= len1;
        stream += len1;
        audio_buf_index += len1;
      }
    }
    
         void setup_ffmpeg(char* filename)
         {
            if (avformat_open_input(&pFormatCtx, filename, NULL, NULL) != 0){
               fprintf(stderr, "FFmpeg failed to open file %s!\n", filename);
               exit(-1);
            }
    
            if (av_find_stream_info(pFormatCtx) < 0){
               fprintf(stderr, "FFmpeg failed to retrieve stream info!\n");
               exit(-1);
            }
    
            // Dump information about file onto standard error
            av_dump_format(pFormatCtx, 0, filename, 0);
    
            // Find the first video stream
            int i = 0;
            for (i; i < pFormatCtx->nb_streams; i++){
               if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && videoStream < 0){
                  videoStream = i;
               }
    
               if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && audioStream < 0){
                  audioStream = i;
               }
            }
    
            if (videoStream == -1){
               fprintf(stderr, "No video stream found in %s!\n", filename);
               exit(-1);
            }
    
            if (audioStream == -1){
               fprintf(stderr, "No audio stream found in %s!\n", filename);
               exit(-1);
            }
    
            // Get a pointer to the codec context for the audio stream
            aCodecCtx = pFormatCtx->streams[audioStream]->codec;
    
            // Set audio settings from codec info
            SDL_AudioSpec wanted_spec;
            wanted_spec.freq = aCodecCtx->sample_rate;
            wanted_spec.format = AUDIO_S16SYS;
            wanted_spec.channels = aCodecCtx->channels;
            wanted_spec.silence = 0;
            wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
            wanted_spec.callback = audio_callback;
            wanted_spec.userdata = aCodecCtx;
    
            SDL_AudioSpec spec;
            if (SDL_OpenAudio(&wanted_spec, &spec) < 0){
               fprintf(stderr, "SDL_OpenAudio: %s\n", SDL_GetError());
               exit(-1);
            }
    
            AVCodec* aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
            if (!aCodec){
               fprintf(stderr, "Unsupported codec [1]!\n");
               exit(-1);
            }
            avcodec_open(aCodecCtx, aCodec);
    
            // audio_st = pFormatCtx->streams[index]
            packet_queue_init(&audioq);
            SDL_PauseAudio(0);
    
            // Get a pointer to the codec context for the video stream
            pCodecCtx = pFormatCtx->streams[videoStream]->codec;
    
            // Find the decoder for the video stream
            AVCodec* pCodec = avcodec_find_decoder(pCodecCtx->codec_id);
            if (pCodec == NULL){
               fprintf(stderr, "Unsupported codec [2]!\n");
               exit(-1); // Codec not found
            }
    
            // Open codec
            if (avcodec_open(pCodecCtx, pCodec) < 0){
               fprintf(stderr, "Unsupported codec [3]!\n");
               exit(-1); // Could not open codec
            }
         }
    
    
         int main(int argc, char* argv[])
         {
            if (argc < 2){
                std::cout << "Usage: " << argv[0] << " <video>" << std::endl;
                return -1;
            }
    
            av_register_all();
    
            // Init SDL
            if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER))
            {
               fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
               return -1;
            }
    
            // Init ffmpeg and setup some SDL stuff related to Audio
            setup_ffmpeg(argv[1]);
    
            VideoCapture cap(argv[1]);
            if (!cap.isOpened()){
               std::cout << "Failed to load file!" << std::endl;
               return -1;
            }
    
            AVPacket packet;
            while (av_read_frame(pFormatCtx, &packet) >= 0)
            {
               if (packet.stream_index == videoStream)
               {
                  // Actually this is were SYNC between audio/video would happen.
                  // Right now I assume that every VIDEO packet contains an entire video frame, and that's not true. A video frame can be made by multiple packets!
                  // But for the time being, assume 1 video frame == 1 video packet,
                  // so instead of reading the frame through ffmpeg, I read it through OpenCV.
    
                  Mat frame;
                  cap >> frame; // get a new frame from camera
    
                  // do some processing on the frame, either as a Mat or as IplImage.
                  // For educational purposes, applying a lame grayscale conversion
                  IplImage ipl_frame = frame;
                  for (int i = 0; i < ipl_frame.width * ipl_frame.height * ipl_frame.nChannels; i += ipl_frame.nChannels)
                  {
                     ipl_frame.imageData[i] = (ipl_frame.imageData[i] + ipl_frame.imageData[i+1] + ipl_frame.imageData[i+2])/3;   //B
                     ipl_frame.imageData[i+1] = (ipl_frame.imageData[i] + ipl_frame.imageData[i+1] + ipl_frame.imageData[i+2])/3; //G
                     ipl_frame.imageData[i+2] = (ipl_frame.imageData[i] + ipl_frame.imageData[i+1] + ipl_frame.imageData[i+2])/3; //R
                  }
    
                  // Display it on SDL window
                  show_frame(&ipl_frame);
    
                  av_free_packet(&packet);
               }
               else if (packet.stream_index == audioStream)
               {
                  packet_queue_put(&audioq, &packet);
               }
               else
               {
                  av_free_packet(&packet);
               }
    
               SDL_Event event;
               SDL_PollEvent(&event);
               switch (event.type)
               {
               case SDL_QUIT:
                  SDL_FreeSurface(surface);
                  SDL_Quit();
                  break;
    
               default:
                  break;
               }
            }
    
            // the camera will be deinitialized automatically in VideoCapture destructor
    
            // Close the codec
            avcodec_close(pCodecCtx);
    
            // Close the video file
            av_close_input_file(pFormatCtx);
    
            return 0;
         }
    
    if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && audioStream < 0){
              audioStream = i;
    }