FFMPEG 解码过程初步学习

1. 视频文件解码过程

解码过程

在这里插入图片描述

步骤如下:

  1. 视频文件(封装格式,MP4/FLV/AVI 等)获取视频格式信息等
  2. 解复用为Stream 流, 准备解码用的Codec
  3. 将Stream 流 使用解码器解为Raw 格式针

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;
}

相关推荐

  1. FFmpeg学习记录(三)—— ffmpeg解码实战

    2024-05-26 00:40:44       9 阅读
  2. [FFmpeg学习]初级的SDL播放mp4测试

    2024-05-26 00:40:44       14 阅读
  3. FFmpeg零基础学习(四)——CPU视频解码 重新编码

    2024-05-26 00:40:44       39 阅读
  4. ffmpeg学习

    2024-05-26 00:40:44       6 阅读
  5. Kafka初步学习

    2024-05-26 00:40:44       7 阅读

最近更新

  1. TCP协议是安全的吗?

    2024-05-26 00:40:44       18 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-05-26 00:40:44       19 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-05-26 00:40:44       19 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-05-26 00:40:44       20 阅读

热门阅读

  1. RabbitMQ01-liunx下安装及用户权限分配

    2024-05-26 00:40:44       9 阅读
  2. 物联网系统

    2024-05-26 00:40:44       10 阅读
  3. Python库之`lxml`的高级用法深度解析

    2024-05-26 00:40:44       14 阅读
  4. Math对象是什么?有什么用?

    2024-05-26 00:40:44       15 阅读
  5. C++ socket epoll IO多路复用

    2024-05-26 00:40:44       12 阅读
  6. python --创建固定字符串长度,先进先出

    2024-05-26 00:40:44       13 阅读
  7. 消息队列之Pulsar、kafaka、rabbitmq、mqtt、RocketMQ

    2024-05-26 00:40:44       11 阅读
  8. Excel超级表的注意事项

    2024-05-26 00:40:44       12 阅读