解码音频和视频并处理两个流 - ffmpeg,sdl,opencv

时间:2012-02-24 10:41:51

标签: c++ opencv ffmpeg sdl

我的目标是独立进行mpeg-2文件的音频和视频,并保持两个流程的同步性。视频的持续时间最长为1或2分钟。

  1. 首先,在这个post“opencv用于阅读视频(并完成此过程)之后,ffmpeg用于音频,而SDL用于播放两种”声音完美。考虑到最近的ffmpeg命名更改,我对代码做了一些修改。在64位机器上使用cmake进行编译很好。打开编解码器时出现错误“不支持的编解码器[3]”。 代码如下。

  2. 其次,我在寻找处理两种流程同步性的代码。


  3. #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;
         }
    

2 个答案:

答案 0 :(得分:1)

我解决了“不支持的编解码器”错误。在以下行中使用AVMEDIA_TYPE_VIDEO重新AVMEDIA_TYPE_AUDIO

if (pFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO && audioStream < 0){
          audioStream = i;
}

由于uchar opencv像素格式,灰度转换不正确。 我还有同步不工作......有什么帮助吗?

由于

答案 1 :(得分:1)