C++ 解码音频和视频并处理这两个流——ffmpeg、sdl、opencv
我的目标是独立地处理mpeg-2文件的音频和视频,并在两个流上保持同步。视频的持续时间最多约为1或2分钟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
#包括“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;
}