代码语言:javascript复制
int OpenAVFile(const char* szFileName)
{
char errbuf[256] = { 0 };
int iRes = 0;
int vindex = -1;
AVFormatContext* pFmtCtx = NULL;
AVCodecContext* vCodecCtx = NULL;
AVCodec* vCodec = NULL;
AVPacket* pkt = NULL;
AVFrame* pfe = NULL;
AVFrame* YUV = NULL;
uint8_t* buf = NULL;
struct SwsContext* img_ctx = NULL;
SDL_Window* window = NULL;
SDL_Renderer* renderer = NULL;
SDL_Texture* texture = NULL;
SDL_Rect rect= { 0 };
av_register_all();
if(SDL_Init(SDL_INIT_VIDEO) != 0)
return ERROR_INIT;
pFmtCtx = avformat_alloc_context();
if ((iRes = avformat_open_input(&pFmtCtx, szFileName, NULL, NULL)) != 0)
return ERROR_OPEN;
if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
return ERROR_FIND;
av_dump_format(pFmtCtx, -1, szFileName, NULL);
for (int i = 0; i < pFmtCtx->nb_streams; i)
{
if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
vindex = i;
}
if (vindex == -1)
return ERROR_FIND;
vCodecCtx = avcodec_alloc_context3(NULL);
if(avcodec_parameters_to_context(vCodecCtx, pFmtCtx->streams[vindex]->codecpar) < 0)
return ERROR_COPY;
vCodec = avcodec_find_decoder(vCodecCtx->codec_id);
if (!vCodec)
return ERROR_FIND;
if(avcodec_open2(vCodecCtx, vCodec, NULL) != 0)
return ERROR_OPEN;
window = SDL_CreateWindow("video", SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED, 680, 540, SDL_WINDOW_OPENGL);
renderer = SDL_CreateRenderer(window, -1, 0);
texture = SDL_CreateTexture(renderer, SDL_PIXELFORMAT_IYUV, SDL_TEXTUREACCESS_STREAMING, vCodecCtx->width, vCodecCtx->height);
if (!window || !renderer || !texture)
return ERROR_CREATE;
rect.w = vCodecCtx->width;
rect.h = vCodecCtx->height;
img_ctx = sws_getContext(vCodecCtx->width, vCodecCtx->height, vCodecCtx->pix_fmt,
vCodecCtx->width, vCodecCtx->height, AV_PIX_FMT_YUV420P, SWS_BICUBIC, NULL, NULL,NULL);
if (!img_ctx)
return ERROR_GET;
pkt = av_packet_alloc();
pfe = av_frame_alloc();
YUV = av_frame_alloc();
buf = (uint8_t*)av_malloc(av_image_get_buffer_size(AV_PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height,1));
av_image_fill_arrays(YUV->data, YUV->linesize, buf, AV_PIX_FMT_YUV420P, vCodecCtx->width, vCodecCtx->height, 1);
while (av_read_frame(pFmtCtx, pkt) >= 0)
{
if (pkt->stream_index == vindex)
{
if ((iRes = avcodec_send_packet(vCodecCtx, pkt)) != 0)
av_strerror(iRes, errbuf, 256);//return -5;
if ((iRes= avcodec_receive_frame(vCodecCtx, pfe)) != 0)
av_strerror(iRes, errbuf, 256); //return -6;
sws_scale(img_ctx, pfe->data, pfe->linesize, 0, vCodecCtx->height, YUV->data, YUV->linesize);
SDL_UpdateTexture(texture, &rect, YUV->data[0], YUV->linesize[0]);
SDL_RenderClear(renderer);
SDL_RenderCopy(renderer, texture, NULL, NULL);
SDL_RenderPresent(renderer);
}
}
av_free(buf);
av_frame_free(&YUV);
av_frame_free(&pfe);
av_packet_free(&pkt);
sws_freeContext(img_ctx);
SDL_DestroyTexture(texture);
SDL_DestroyRenderer(renderer);
SDL_DestroyWindow(window);
SDL_Quit();
avcodec_free_context(&vCodecCtx);
avformat_close_input(&pFmtCtx);
avformat_free_context(pFmtCtx);
return 0;
}
代码语言:javascript复制uint8_t* g_buf = NULL;
int g_MaxLen = 0;
int g_CurPos = 0;
void aCallback(void *userdata, Uint8 * stream, int len)
{
SDL_memset(stream, 0, len);//important
SDL_MixAudio(stream, g_buf, len, SDL_MIX_MAXVOLUME);//memcpy(stream, g_buf, len);
g_CurPos = len;
g_MaxLen -= len;
}
int OpenAVFile(const char* szFileName)
{
char errbuf[256] = { 0 };
int iRes = 0;
int aindex = -1;
AVFormatContext* pFmtCtx = NULL;
AVCodecContext* aCodecCtx = NULL;
AVCodec* aCodec = NULL;
AVPacket* pkt = NULL;
AVFrame* pfe = NULL;
uint8_t* buf = NULL;
struct SwrContext* audio_ctx = NULL;
SDL_AudioSpec want = { 0 };
SDL_AudioSpec recv = { 0 };
av_register_all();
if (SDL_Init(SDL_INIT_AUDIO) != 0)
return ERROR_INIT;
pFmtCtx = avformat_alloc_context();
if ((iRes = avformat_open_input(&pFmtCtx, szFileName, NULL, NULL)) != 0)
return ERROR_OPEN;
if (avformat_find_stream_info(pFmtCtx, NULL) < 0)
return ERROR_FIND;
av_dump_format(pFmtCtx, -1, szFileName, NULL);
for (int i = 0; i < pFmtCtx->nb_streams; i)
{
if (pFmtCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
aindex = i;
}
if (aindex == -1)
return ERROR_FIND;
aCodecCtx = avcodec_alloc_context3(NULL);
if (avcodec_parameters_to_context(aCodecCtx, pFmtCtx->streams[aindex]->codecpar) < 0)
return ERROR_COPY;
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if (!aCodec)
return ERROR_FIND;
if (avcodec_open2(aCodecCtx, aCodec, NULL) != 0)
return ERROR_OPEN;
want.callback = aCallback;
//want.channels = 1;//设为单声道就不用装换pannel了
want.channels = aCodecCtx->channels;
want.format = AUDIO_S16SYS;//ffmpeg的fmt与SDL的不一样,但是可以自己计算转换
want.freq = aCodecCtx->sample_rate;
want.samples = aCodecCtx->frame_size;
want.silence = 0;
want.userdata = aCodecCtx;
SDL_OpenAudio(&want, &recv);
//audio_ctx = swr_alloc();
audio_ctx = swr_alloc_set_opts(NULL, av_get_default_channel_layout(recv.channels), AV_SAMPLE_FMT_S16, recv.freq,
av_get_default_channel_layout(aCodecCtx->channels), aCodecCtx->sample_fmt, aCodecCtx->sample_rate, 0, NULL);
swr_init(audio_ctx);//必须要
pkt = av_packet_alloc();
pfe = av_frame_alloc();
g_buf = (uint8_t*)av_malloc(av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1));
while (av_read_frame(pFmtCtx, pkt) >= 0)
{
if (pkt->stream_index == aindex)
{
if ((iRes = avcodec_send_packet(aCodecCtx, pkt)) != 0)
av_strerror(iRes, errbuf, 256);//return -5;
if ((iRes = avcodec_receive_frame(aCodecCtx, pfe)) != 0)
av_strerror(iRes, errbuf, 256); //return -6;
//单声道
//memset(g_buf, 0, 10240);
//memcpy(g_buf, pfe->data[0], pfe->linesize[0]);
//g_CurPos = 0;
//g_MaxLen = pfe->linesize[0];
swr_convert(audio_ctx, &g_buf, av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1),
(const uint8_t **)pfe->data, pfe->nb_samples);
g_CurPos = 0;
g_MaxLen = av_samples_get_buffer_size(NULL, recv.channels, recv.samples, AV_SAMPLE_FMT_S16, 1);
SDL_PauseAudio(0);
while (g_MaxLen > 0)
SDL_Delay(1);
}
}
av_free(buf);
av_frame_free(&pfe);
av_packet_free(&pkt);
swr_free(&audio_ctx);
SDL_Quit();
avcodec_free_context(&aCodecCtx);
avformat_close_input(&pFmtCtx);
avformat_free_context(pFmtCtx);
return 0;
}
视频的重要处理: sws_getContext,获得转换上下文
av_image_get_buffer_size,获得(转换后)图片大小
av_image_fill_arrays,将自定义内存块绑定到输出的AVFrame中
sws_scale,转换
音频的重要处理:
av_get_default_channel_layout,根据声道数获取默认的声道布局
swr_alloc_set_opts,获取转换上下文
swr_init,获取到上下文后必须初始化
av_samples_get_buffer_size,计算(参数格式的)音频数据的大小
swr_convert,转换 对于音频的相关参数,FFmpeg和SDL中的channels,sample_rate...等等这些"数值"都是可以直接互相赋值的。但是format,FFmpeg和SDL就各自有自己的定义。
下面是我自己做的由AVSampleFormat转SDL_AudioFormat的函数
代码语言:javascript复制/*{
AV_SAMPLE_FMT_NONE = -1,
AV_SAMPLE_FMT_U8, ///< unsigned 8 bits
AV_SAMPLE_FMT_S16, ///< signed 16 bits
AV_SAMPLE_FMT_S32, ///< signed 32 bits
AV_SAMPLE_FMT_FLT, ///< float
AV_SAMPLE_FMT_DBL, ///< double
AV_SAMPLE_FMT_U8P, ///< unsigned 8 bits, planar
AV_SAMPLE_FMT_S16P, ///< signed 16 bits, planar
AV_SAMPLE_FMT_S32P, ///< signed 32 bits, planar
AV_SAMPLE_FMT_FLTP, ///< float, planar
AV_SAMPLE_FMT_DBLP, ///< double, planar
AV_SAMPLE_FMT_S64, ///< signed 64 bits
AV_SAMPLE_FMT_S64P, ///< signed 64 bits, planar
AV_SAMPLE_FMT_NB ///< Number of sample formats. DO NOT USE if linking dynamically
}*/
/* verbatim
---------------------- - sample is signed if set
||
|| ---------- - sample is bigendian if set
|| ||
|| || -- - sample is float if set
|| || ||
|| || || -- - sample bit size-- -
|| || || | |
15 14 13 12 11 10 09 08 07 06 05 04 03 02 01 00
endverbatim
*
* There are macros in SDL 2.0 and later to query these bits.
*/
SDL_AudioFormat GetFmt(AVSampleFormat avfmt)
{
SDL_AudioFormat res = 0;
bool isp = false;
bool iss = false;
bool isf = false;
bool isd = false;
int bits = 0;
switch (avfmt)
{
case AV_SAMPLE_FMT_NONE://// = -1,
break;
case AV_SAMPLE_FMT_U8: ///< unsigned 8 bits
bits = 8;
break;
case AV_SAMPLE_FMT_S16: ///< signed 16 bits
bits = 16;
iss = true;
break;
case AV_SAMPLE_FMT_S32: ///< signed 32 bits
bits = 32;
iss = true;
break;
case AV_SAMPLE_FMT_FLT: ///< float
isf = true;
break;
case AV_SAMPLE_FMT_DBL: ///< double
isd = true;
break;
case AV_SAMPLE_FMT_U8P: ///< unsigned 8 bits, planar
bits = 8;
isp = true;
break;
case AV_SAMPLE_FMT_S16P: ///< signed 16 bits, planar
bits = 16;
iss = true;
isp = true;
break;
case AV_SAMPLE_FMT_S32P: ///< signed 32 bits, planar
bits = 32;
iss = true;
isp = true;
break;
case AV_SAMPLE_FMT_FLTP: ///< float, planar
isf = true;
isp = true;
break;
case AV_SAMPLE_FMT_DBLP: ///< double, planar
isd = true;
isp = true;
break;
case AV_SAMPLE_FMT_S64: ///< signed 64 bits
bits = 64;
iss = true;
break;
case AV_SAMPLE_FMT_S64P: ///< signed 64 bits, planar
bits = 64;
iss = true;
isp = true;
break;
case AV_SAMPLE_FMT_NB: ///< Number of sample formats. DO NOT USE if linking dynamically
break;
}
if (iss)
res |= 1 << 15;
if (isf)
res |= 1 << 8;
bits &= 255;
res |= bits;
return res;
}
例子代码中为了简便,SDL播放音频的format就用AUDIO_S16SYS,对应FFmpeg就是AV_SAMPLE_FMT_S16,都标识signed 16bit。注意SDL中音频数据没有planner。
代码下载