音视频入门系列-FFmpeg篇(解码)

从本篇开始,我们正式进入FFmpeg代码的学习。本片文章结合代码的形式介绍下FFmpeg的解码流程和SDL2播放流程。

1. 开发环境
◆ FFmpeg 开发库下载地址(自行选定版本号,-shared 为开发库):

 https://github.com/BtbN/FFmpeg-Builds/releases

◆ SDL 开发库下载地址(选择Development Libraries):

 https://www.libsdl.org/download-2.0.php

2.音视频解码
音视频解码流程,以及相关的 ffmpeg 代码如下(文末附代码):

3.音视频播放

Windows平台使用SDL2来播放音视频,调用流程及相关代码如下(文末附代码):

4.视频解码代码如下:

/* 
  author:八小时码字员 
  file:vPlayer_sdl2.cpp
 */
#include "vPlayer_sdl2.h"
#include "output_log.h"
 
#include <stdio.h>
#include <iostream>
using namespace std;
 
#define __STDC_CONSTANT_MACROS
extern "C"
{
  #include <libavcodec/avcodec.h>
  #include <libavformat/avformat.h>
  #include <libswscale/swscale.h>
  #include <libavutil/imgutils.h>
  #include <SDL.h>
}
 
static int g_frame_rate = 1;
static int g_sfp_refresh_thread_exit = 0;
static int g_sfp_refresh_thread_pause = 0;
 
#define SFM_REFRESH_EVENT (SDL_USEREVENT+1)
#define SFM_BREAK_EVENT (SDL_USEREVENT+2)
 
typedef struct FFmpeg_V_Param_T
{
  AVFormatContext *pFormatCtx;
  AVCodecContext *pCodecCtx;
  SwsContext *pSwsCtx;
  int video_index;
}FFmpeg_V_Param;
 
typedef struct SDL_Param_T
{
  SDL_Window *p_sdl_window;
  SDL_Renderer *p_sdl_renderer;
  SDL_Texture *p_sdl_texture;
  SDL_Rect sdl_rect;
  SDL_Thread *p_sdl_thread;
}SDL_Param;
 
/*
  return value:zero(success) non-zero(failure)
*/
int init_ffmpeg(FFmpeg_V_Param* p_ffmpeg_param, char* filePath)
{
  //init FFmpeg_V_Param
  p_ffmpeg_param->pFormatCtx = avformat_alloc_context();
  const AVCodec *pCodec = NULL;
 
  //do global initialization of network libraries
  avformat_network_init();
 
  //open input stream
  if (avformat_open_input(&(p_ffmpeg_param->pFormatCtx), filePath, NULL, NULL) != 0)
  {
    output_log(LOG_ERROR, "avformat_open_input error");
    return -1;
  }
  
  //find stream info
  if (avformat_find_stream_info(p_ffmpeg_param->pFormatCtx, NULL) < 0)
  {
    output_log(LOG_ERROR, "avformat_find_stream_info error");
    return -1;
  }
 
  //get video pCodecParms, codec and frame rate
  for (int i = 0; i < p_ffmpeg_param->pFormatCtx->nb_streams; i++)
  {
    AVStream *pStream = p_ffmpeg_param->pFormatCtx->streams[i];
    if (pStream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
    {
      pCodec = avcodec_find_decoder(pStream->codecpar->codec_id);
      p_ffmpeg_param->pCodecCtx = avcodec_alloc_context3(pCodec);
      avcodec_parameters_to_context(p_ffmpeg_param->pCodecCtx, pStream->codecpar);
      g_frame_rate = pStream->avg_frame_rate.num / pStream->avg_frame_rate.den;
      p_ffmpeg_param->video_index = i;
    }
  }
  if (!p_ffmpeg_param->pCodecCtx)
  {
    output_log(LOG_ERROR, "could not find video codecCtx");
    return -1;
  }
 
  //open codec
  if (avcodec_open2(p_ffmpeg_param->pCodecCtx, pCodec, NULL))
  {
    output_log(LOG_ERROR, "avcodec_open2 error");
    return -1;
  }
 
  //get scale pixelformat context
  p_ffmpeg_param->pSwsCtx = sws_getContext(p_ffmpeg_param->pCodecCtx->width,
    p_ffmpeg_param->pCodecCtx->height, p_ffmpeg_param->pCodecCtx->pix_fmt,
    p_ffmpeg_param->pCodecCtx->width, p_ffmpeg_param->pCodecCtx->height,
    AV_PIX_FMT_YUV420P,  SWS_BICUBIC, NULL, NULL, NULL);
 
  av_dump_format(p_ffmpeg_param->pFormatCtx, p_ffmpeg_param->video_index, filePath, 0);
 
  return 0;
}
 
/*
  return value:zero(success) non-zero(failure)
*/
int release_ffmpeg(FFmpeg_V_Param* p_ffmpeg_param)
{
  if (!p_ffmpeg_param)
    return -1;
 
  //realse scale pixelformat context
  if (p_ffmpeg_param->pSwsCtx)
    sws_freeContext(p_ffmpeg_param->pSwsCtx);
 
  //close codec
  if (p_ffmpeg_param->pCodecCtx)
    avcodec_close(p_ffmpeg_param->pCodecCtx);
 
  //close input stream
  if (p_ffmpeg_param->pFormatCtx)
    avformat_close_input(&(p_ffmpeg_param->pFormatCtx));
 
  //free AVCodecContext
  if (p_ffmpeg_param->pCodecCtx)
    avcodec_free_context(&(p_ffmpeg_param->pCodecCtx));
 
  //free AVFormatContext
  if (p_ffmpeg_param->pFormatCtx)
    avformat_free_context(p_ffmpeg_param->pFormatCtx);
 
  //free FFmpeg_V_Param
  delete p_ffmpeg_param;
  p_ffmpeg_param = NULL;
 
  return 0;
}
 
int sfp_refresh_thread(void* opaque)
{
  g_sfp_refresh_thread_exit = 0;
  g_sfp_refresh_thread_pause = 0;
  while (!g_sfp_refresh_thread_exit)
  {
    if (!g_sfp_refresh_thread_pause)
    {
      SDL_Event sdl_event;
      sdl_event.type = SFM_REFRESH_EVENT;
      SDL_PushEvent(&sdl_event);
    }
    SDL_Delay(1000 / g_frame_rate);
  }
  g_sfp_refresh_thread_exit = 0;
  g_sfp_refresh_thread_pause = 0;
  SDL_Event sdl_event;
  sdl_event.type = SFM_BREAK_EVENT;
  SDL_PushEvent(&sdl_event);
  return 0;
}
 
int init_sdl2(SDL_Param_T *p_sdl_param, int screen_w, int screen_h)
{
  if (SDL_Init(SDL_INIT_AUDIO | SDL_INIT_VIDEO | SDL_INIT_TIMER))
  {
    output_log(LOG_ERROR, "SDL_Init error");
    return -1;
  }
  p_sdl_param->p_sdl_window = SDL_CreateWindow("vPlayer_sdl", SDL_WINDOWPOS_UNDEFINED,
    SDL_WINDOWPOS_UNDEFINED, screen_w, screen_h, SDL_WINDOW_OPENGL);
  if (!p_sdl_param->p_sdl_window)
  {
    output_log(LOG_ERROR, "SDL_CreateWindow error");
    return -1;
  }
  p_sdl_param->p_sdl_renderer = SDL_CreateRenderer(p_sdl_param->p_sdl_window, -1, 0);
  p_sdl_param->p_sdl_texture = SDL_CreateTexture(p_sdl_param->p_sdl_renderer, SDL_PIXELFORMAT_IYUV,
    SDL_TEXTUREACCESS_STREAMING, screen_w, screen_h);
  p_sdl_param->sdl_rect.x = 0;
  p_sdl_param->sdl_rect.y = 0;
  p_sdl_param->sdl_rect.w = screen_w;
  p_sdl_param->sdl_rect.h = screen_h;
  p_sdl_param->p_sdl_thread = SDL_CreateThread(sfp_refresh_thread, NULL, NULL);
 
  return 0;
}
 
int release_sdl2(SDL_Param_T *p_sdl_param)
{
  SDL_DestroyTexture(p_sdl_param->p_sdl_texture);
  SDL_DestroyRenderer(p_sdl_param->p_sdl_renderer);
  SDL_DestroyWindow(p_sdl_param->p_sdl_window);
  SDL_Quit();
  return 0;
}
 
int vPlayer_sdl2(char* filePath)
{
  //ffmpeg param
  FFmpeg_V_Param *p_ffmpeg_param = NULL;
  AVPacket *packet = NULL;
  AVFrame *pFrame = NULL, *pFrameYUV = NULL;
  int out_buffer_size = 0;
  unsigned char* out_buffer = 0;
  
  //sdl param
  SDL_Param_T *p_sdl_param = NULL;
  SDL_Event sdl_event;
 
  int ret = 0;
 
  //init ffmpeg
  p_ffmpeg_param = new FFmpeg_V_Param();
  memset(p_ffmpeg_param, 0, sizeof(FFmpeg_V_Param));
  if (init_ffmpeg(p_ffmpeg_param, filePath))
  {
    ret = -1;
    goto end;
  }
  packet = av_packet_alloc();
  pFrame = av_frame_alloc();
  pFrameYUV = av_frame_alloc();
  out_buffer_size = av_image_get_buffer_size(AV_PIX_FMT_YUV420P,
    p_ffmpeg_param->pCodecCtx->width, p_ffmpeg_param->pCodecCtx->height, 1);
  out_buffer = (unsigned char*)av_malloc(out_buffer_size);
  av_image_fill_arrays(pFrameYUV->data, pFrameYUV->linesize, out_buffer,
    p_ffmpeg_param->pCodecCtx->pix_fmt, 
    p_ffmpeg_param->pCodecCtx->width, p_ffmpeg_param->pCodecCtx->height, 1);
  //init sdl2
  p_sdl_param = new SDL_Param_T();
  memset(p_sdl_param, 0, sizeof(SDL_Param_T));
  if (init_sdl2(p_sdl_param, p_ffmpeg_param->pCodecCtx->width, p_ffmpeg_param->pCodecCtx->height))
  {
    ret = -1;
    goto end;
  }
 
  //demuxing and show
  while (true)
  {
    int temp_ret = 0;
    SDL_WaitEvent(&sdl_event);
    if (sdl_event.type == SFM_REFRESH_EVENT)
    {
      while (true)
      {
        if (av_read_frame(p_ffmpeg_param->pFormatCtx, packet) < 0)
        {
          g_sfp_refresh_thread_exit = 1;
          break;
        }
        if (packet->stream_index == p_ffmpeg_param->video_index)
        {
          break;
        }
      }
      if (avcodec_send_packet(p_ffmpeg_param->pCodecCtx, packet))
        g_sfp_refresh_thread_exit = 1;
      
      do
      {
        temp_ret = avcodec_receive_frame(p_ffmpeg_param->pCodecCtx, pFrame);
        if (temp_ret == AVERROR_EOF)
        {
          g_sfp_refresh_thread_exit = 1;
          break;
        }
        if (temp_ret == 0)
        {
          sws_scale(p_ffmpeg_param->pSwsCtx, (const unsigned char* const*)pFrame->data,
            pFrame->linesize, 0, p_ffmpeg_param->pCodecCtx->height, pFrameYUV->data,
            pFrameYUV->linesize);
 
          SDL_UpdateTexture(p_sdl_param->p_sdl_texture, &(p_sdl_param->sdl_rect),
            pFrameYUV->data[0], pFrameYUV->linesize[0]);
 
          SDL_RenderClear(p_sdl_param->p_sdl_renderer);
          SDL_RenderCopy(p_sdl_param->p_sdl_renderer, p_sdl_param->p_sdl_texture,
            NULL, &(p_sdl_param->sdl_rect));
          SDL_RenderPresent(p_sdl_param->p_sdl_renderer);
        }
      } while (temp_ret != AVERROR(EAGAIN));
      
      //av_packet_unref(packet);
    }
    else if (sdl_event.type == SFM_BREAK_EVENT)
    {
      break;
    }
    else if (sdl_event.type == SDL_KEYDOWN)
    {
      if (sdl_event.key.keysym.sym == SDLK_SPACE)
        g_sfp_refresh_thread_pause = !g_sfp_refresh_thread_pause;
      if (sdl_event.key.keysym.sym == SDLK_q)
        g_sfp_refresh_thread_exit = 1;
    }
    else if (sdl_event.type == SDL_QUIT)
    {
      g_sfp_refresh_thread_exit = 1;
    }
  }
 
end:
  release_ffmpeg(p_ffmpeg_param);
  av_packet_free(&packet);
  av_frame_free(&pFrame);
  av_frame_free(&pFrameYUV);
  release_sdl2(p_sdl_param);
  return ret;
}

/* 
  author:八小时码字员 
  file:output_log.cpp
 */
#include <stdio.h>
#include <stdarg.h>
 
#define MAX_BUF_LEN 1024
 
int g_log_debug_flag = 1;
int g_log_info_flag = 1;
int g_log_warnning_flag = 1;
int g_log_error_flag = 1;
 
enum LOG_LEVEL
  {
    LOG_DEBUG,
    LOG_INFO,
    LOG_WARNING,
    LOG_ERROR
  };
 
void set_log_flag(int log_debug_flag, int log_info_flag, int log_warnning_flag, 
  int log_error_flag)
{
  g_log_debug_flag = log_debug_flag;
  g_log_info_flag = log_info_flag;
  g_log_warnning_flag = log_warnning_flag;
  g_log_error_flag = log_error_flag;
}
 
void output_log(LOG_LEVEL log_level, const char* fmt, ...)
{
  va_list args;
  va_start(args, fmt);
 
  char buf[MAX_BUF_LEN] = { 0 };
  vsnprintf(buf, MAX_BUF_LEN - 1, fmt, args);
 
  switch (log_level)
  {
  case LOG_DEBUG:
    if (g_log_debug_flag)
      printf("[Log-Debug]:%s\n", buf);
    break;
  case LOG_INFO:
    if (g_log_info_flag)
      printf("[Log-Info]:%s\n", buf);
    break;
  case LOG_WARNING:
    if (g_log_warnning_flag)
      printf("[Log-Warnning]:%s\n", buf);
    break;
  case LOG_ERROR:
    if (g_log_error_flag)
      printf("[Log-Error]:%s\n", buf);
    break;
  default:
    break;
  }
 
  va_end(args);
}

原文 音视频入门系列-FFmpeg篇(解码)_ffmpeg -strict -2_八小时码字员的博客-CSDN博客

★文末名片可以免费领取音视频开发学习资料,内容包括(FFmpeg ,webRTC ,rtmp ,hls ,rtsp ,ffplay ,srs)以及音视频学习路线图等等。

见下方!↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓↓

 

猜你喜欢

转载自blog.csdn.net/yinshipin007/article/details/134785407