FFmpeg_3.2.4+SDL_2.0.5学习(1)音视频解码帧及显示/播放数据

论坛 期权论坛 脚本     
匿名网站用户   2020-12-19 15:35   11   0

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;
}
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的函数

/*{
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。

代码下载

分享到 :
0 人收藏
您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

积分:1136255
帖子:227251
精华:0
期权论坛 期权论坛
发布
内容

下载期权论坛手机APP