Saída de áudio com processamento de vídeo com opencv

Estou processando vídeo com opencv, mas ao mesmo tempo preciso reproduzir áudio e simplesmente controlá-lo, como o número do quadro alto ou atual.

Eu acho que deveria criar um processo paralelo com o ffmpeg, mas não sei como fazer isso. Você pode explicar o que fazer?

Ou conhece outra solução?

Acho que o ffmpeg deve ser usado para reproduzir áudio e SDL para vídeo nesse caso.

Depois de abrir o arquivo com o OpenCV e processar o quadro, você pode usar o OpenCV -> SDL para exibi-lo enquanto recupera os frameworks de áudio através do ffmpeg e reproduzi-los com o SDL.

Aqui está uma boa coleção de tutoriais do ffmpeg / SDL!

Eu também encontrei um bom post que mostra como capturar frameworks de um arquivo de vídeo usando o ffmpeg, armazená-los no OpenCV cv::Mat e exibir o resultado em uma janela do OpenCV. Mas desta forma você não pode reproduzir áudio, já que o OpenCV não lida com isso.

Você pode estar interessado em ler este post também: Como evitar um atraso crescente com o ffmpeg entre dados de som e vídeo bruto?

EDITAR:

Passei as últimas 4 horas codificando um protótipo para demonstrar como é feito. Esta demonstração lê frameworks de vídeo através do OpenCV (para que você possa processá-los) e áudio através do ffmpeg , e o SDL é usado para reproduzir ambos! Existem 2 limitações nesta demo que você deve estar ciente: 1 – assume que você está trabalhando com uma imagem OpenCV carregada como BGR (24bits), e 2 – áudio e vídeo não estão sendo sincronizados! Sim, saí com algum trabalho para você fazer (yeeeey). Mas não entre em pânico, a página 6 tem algumas ideias!

É importante sincronizar áudio e vídeo, porque você estará fazendo algum processamento nos frameworks, e isso certamente fará com que o vídeo e o áudio saiam de sincronia muito rápido, já que estão sendo reproduzidos independentemente um do outro.

Os tutoriais do ffmpeg que eu sugeri acima são muito importantes para entender o código, um monte de código desta demonstração veio de lá . Eles mostram como lidar com o SDL e como ler pacotes de streams de áudio / vídeo.

 #include  #include  extern "C" { #include  #include  #include  #include  } #include  #include  //#include  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_audio2(aCodecCtx, (int16_t*)audio_buf, &data_size, audio_pkt_data, audio_pkt_size); 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 (av_open_input_file(&pFormatCtx, filename, NULL, 0, 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 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 == CODEC_TYPE_VIDEO && videoStream < 0) { videoStream = i; } if (pFormatCtx->streams[i]->codec->codec_type == CODEC_TYPE_AUDIO && 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!\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!\n"); exit(-1); // Codec not found } // Open codec if (avcodec_open(pCodecCtx, pCodec) < 0) { fprintf(stderr, "Unsupported codec!\n"); exit(-1); // Could not open codec } } int main(int argc, char* argv[]) { if (argc < 2) { std::cout << "Usage: " << argv[0] << "  

No meu Mac eu compilei com:

 g++ ffmpeg_snd.cpp -o ffmpeg_snd -D_GNU_SOURCE=1 -D_THREAD_SAFE -I/usr/local/include/opencv -I/usr/local/include -I/usr/local/include/SDL -Wl,-framework,Cocoa -L/usr/local/lib -lopencv_core -lopencv_imgproc -lopencv_highgui -lopencv_ml -lopencv_video -lopencv_features2d -lopencv_calib3d -lopencv_objdetect -lopencv_contrib -lopencv_legacy -lopencv_flann -lSDLmain -lSDL -L/usr/local/lib -lavfilter -lavcodec -lavformat -I/usr/local/Cellar/ffmpeg/HEAD/include/libavcodec -I/usr/local/Cellar/ffmpeg/HEAD/include/libavformat