FFMPEG 解码过程初步学习

发布于:2024-05-30 ⋅ 阅读:(122) ⋅ 点赞:(0)

1. 视频文件解码过程

解码过程

在这里插入图片描述

步骤如下:

  1. 视频文件(封装格式,MP4/FLV/AVI 等)获取视频格式信息等
  2. 解复用为Stream 流, 准备解码用的Codec
  3. 将Stream 流 使用解码器解为Raw 格式针
    在这里插入图片描述
    数据结构:
    AVFormatContext: 整个过程非常重要的数据结果,通过avformat_open_input 从文件获取format 信息,一般封装信息在这一步就都可以获取到了,在通过avformat_find_stream_info 接口,获取读入视频文件中的stream 信息(这里的stream 一般包含视频流、音频流、字幕流(subtitile) 等)
    AVCodecContext: 解码过程中需要使用的codec context, 在avcodec_send_packet 和avcodec_receive_frame 中都要使用的数据结构,里面包含了解码必要的信息,比如图像width, height, format 等信息
    AVPacket: 从文件里面读取出来的包数据
    AVFrame: avcodec_send_packet 将AVPacket 数据送给指定的解码器后,通过avcodec_receive_frame 获取到解码后的帧数据封装在AVFrame 中

1.1 音视频格式填充:

int ret = avformat_open_input(&format_ctx, filename, nullptr, nullptr);  // 打开输入文件并将格式上下文赋给 format_ctx
// 填充stream 信息
if (avformat_find_stream_info(format_ctx, NULL) < 0) {
    std::cout << "no stream in files : " << std::endl;
    return -1;
}


// find the video stream information
ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, nullptr, 0);
if (ret < 0) {
    fprintf(stderr, "Cannot find a video stream in the input file\n");
    return -1;
}
video_stream_index = ret;
video_stream = format_ctx->streams[video_stream_index];

 ret = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, nullptr, 0);
if (ret < 0) {
    std::cout<< "no audio stream " << ret << std::endl;
} else {
    audio_stream_index = ret;
    audio_stream = format_ctx->streams[audio_stream_index];
}

1.2 准备解码用的codec

const AVCodec * video_codec = avcodec_find_decoder(video_stream->codecpar->codec_id);
if (video_codec == nullptr) {
    std::cout << "we not found the code: " << video_decoder->id << std::endl;
} else {
    std::cout << "we found the video codec: " << video_codec->name << std::endl;
}
const AVCodec * audio_codec = avcodec_find_decoder(audio_stream->codecpar->codec_id);
if (audio_codec == nullptr) {
    std::cout << "we not found the code: " << audio_decoder->id << std::endl;
} else {
    std::cout << "we found the audio codec: " << audio_codec->name << std::endl;
}

// codec context for decoder 
AVCodecContext* video_dec_context = avcodec_alloc_context3(video_codec);
if (!video_dec_context) {
    std::cout << "video dec context alloc failed" << std::endl;
}
if ((ret = avcodec_parameters_to_context(video_dec_context, video_stream->codecpar)) < 0) {
    std::cerr << "codec copy failed" << std::endl;
}
std::cout<< "video format width: " << video_dec_context->width << std::endl;
std::cout<< "video format height: " << video_dec_context->height << std::endl;
std::cout << "video pixel format: " << video_dec_context->pix_fmt << std::endl;
video_width = video_dec_context->width;
video_height = video_dec_context->height;
video_fmt = video_dec_context->pix_fmt;
// open codec
ret = avcodec_open2(video_dec_context, video_codec, NULL);
if (ret < 0) {
    std::cerr << "video codec open failed" << std::endl;
}

Audio 的流程和video 的流程相近:
通过stream 信息->AVCodec->AVCodecContext , 再通过AVCodecContext 打开解码器(avcodec_open2)

1.3 解码过程:

在打开解码器后,创建AVPacket AVFrame
AVPacket 是解码前的包, AVFrame 是解码后的帧

AVPacket *packet;
packet = av_packet_alloc();

AVFrame* frame = av_frame_alloc();

while(1) {
  // 从里面获取一个packet
  ret1 = av_read_frame(format_ctx, packet);
  if (ret1 < 0) {
      break;
  } else {
      /*
      if (packet->stream_index == video_stream_index) {
          std::cout << "Video: pts: " << packet->pts << " dts: " << packet->dts <<" duration: " << packet->duration << std::endl;
      } else {
          if (packet->stream_index != -1 && audio_stream_index == packet->stream_index) {
              std::cout << "Audio: pts: " << packet->pts << " dts: " << packet->dts <<" duration: " << packet->duration << std::endl;
          }
      }
      */
      if (packet->stream_index == video_stream_index) {
          int result = decode_packet(video_dec_context, packet);
          if (result == 0) {
              //video_frame_count++;
               

          }

      } else {
          decode_packet(audio_dec_context, packet);
          av_frame_unref(frame);
      }

  }
  av_packet_unref(packet);

}
// 解包过程
static int decode_packet(AVCodecContext *dec, const AVPacket *pkt)
{
  int ret = 0;

  // submit the packet to the decoder
  ret = avcodec_send_packet(dec, pkt);
  if (ret < 0) {
      fprintf(stderr, "Error submitting a packet for decoding (%d)\n", ret);
      return ret;
  }

  // get all the available frames from the decoder
  while (ret >= 0) {
      ret = avcodec_receive_frame(dec, frame);
      if (ret < 0) {
          // those two return values are special and mean there is no output
          // frame available, but there were no errors during decoding
          if (ret == AVERROR_EOF || ret == AVERROR(EAGAIN))
              return 0;

          // fprintf(stderr, "Error during decoding (%s)\n", av_err2str(ret));
          std::cerr << "Error during decoding " << ret << std::endl;
          return ret;
      }

      // // write the frame data to output file
      if (dec->codec->type == AVMEDIA_TYPE_VIDEO) {
          static int video_frame_count = 0;
          /* copy decoded frame to destination buffer:
          * this is required since rawvideo expects non aligned data */
          // std::cout << "frame size: " << frame->linesize << std::endl;
          av_image_copy2(video_dst_data, video_dst_linesize,
                      frame->data, frame->linesize,
                      video_fmt, video_width, video_height);

          /* write to rawvideo file */
          size_t size =  fwrite(video_dst_data[0], 1, video_dst_bufsize, video_dst_file);
          if (size == 0) {
              std::cerr << "write failed" << std::endl;
              return -1;
          } else {
              
              video_frame_count++;
              std::cout << " write to video frame count: " << video_frame_count << std::endl;
          }
          
      }
           //ret = output_video_frame(frame);
      // else
      //     ret = output_audio_frame(frame);

      av_frame_unref(frame);
  }

  return ret;
}

网站公告

今日签到

点亮在社区的每一天
去签到