ffmpeg编程示例-解码h264

提供一个可以运行的ffmpeg工程:https://gitee.com/qiuguolu1108/ffmpeg-study

ffmpeg -i Jasmine.flv -vcodec libx264 -an -f h264 Jasmine.h264

进入工程中的player目录,使用上述命令,生成待处理的Jasmine.h264文件。

#include "spdlog/spdlog.h"

extern "C" {
#include "libavcodec/avcodec.h"
}

#define INBUF_SIZE 4096

char error_buf[1024] = {0};
char *av_get_err(int err_num) {
  av_strerror(err_num, error_buf, sizeof(error_buf));
  return error_buf;
}

void print_video_format(const AVFrame *frame) {
  SPDLOG_INFO("width: {}", frame->width);
  SPDLOG_INFO("height: {}", frame->height);
  SPDLOG_INFO("format: {}", frame->format);
}

void decode(AVCodecContext *dec_ctx, AVFrame *frame, AVPacket *pkt,
            FILE *yuv_file) {
  int ret = avcodec_send_packet(dec_ctx, pkt);
  if (ret == AVERROR(EAGAIN)) {
    SPDLOG_WARN(
        "Receive_frame and send_packet both returned EAGAIN, which is an API "
        "violation.");
  } else if (ret < 0) {
    SPDLOG_WARN(
        "Error submitting the packet to the decoder, err:%s, pkt_size:%d\n",
        av_get_err(ret), pkt->size);
    return;
  }

  while (ret >= 0) {
    ret = avcodec_receive_frame(dec_ctx, frame);
    if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
      return;
    } else if (ret < 0) {
      SPDLOG_ERROR("Error during decoding");
    }

    static int s_print_format = 0;
    if (s_print_format == 0) {
      s_print_format = 1;
      print_video_format(frame);
    }

    // linesize[]代表每行的字节数量,所以每行的偏移是linesize[]
    for (int j = 0; j < frame->height; j++) {
      fwrite(frame->data[0] + j * frame->linesize[0], 1, frame->width,
             yuv_file);
    }

    for (int j = 0; j < frame->height / 2; j++) {
      fwrite(frame->data[1] + j * frame->linesize[1], 1, frame->width / 2,
             yuv_file);
    }

    for (int j = 0; j < frame->height / 2; j++) {
      fwrite(frame->data[2] + j * frame->linesize[2], 1, frame->width / 2,
             yuv_file);
    }
  }
}

int main(int argc, char **argv) {
  const char *h264_file_name = "Jasmine.h264";
  const char *yuv_file_name = "Jasmine.yuv";

  FILE *h264_file = fopen(h264_file_name, "rb");
  if (!h264_file) {
    SPDLOG_ERROR("Could not open {}", h264_file_name);
  }

  FILE *yuv_file = fopen(yuv_file_name, "wb");
  if (!yuv_file) {
    SPDLOG_ERROR("Could not open {}", yuv_file_name);
  }

  // AV_INPUT_BUFFER_PADDING_SIZE
  // 在输入比特流结尾的要求附加分配字节的数量上进行解码
  uint8_t h264_buf[INBUF_SIZE + AV_INPUT_BUFFER_PADDING_SIZE];
  memset(h264_buf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE);

  // find the H264 video decoder
  const AVCodec *video_codec = avcodec_find_decoder(AV_CODEC_ID_H264);
  if (!video_codec) {
    SPDLOG_ERROR("Codec not found");
  }

  AVCodecParserContext *parser = av_parser_init(video_codec->id);
  if (!parser) {
    SPDLOG_ERROR("parser not found");
  }

  AVCodecContext *video_codec_ctx = avcodec_alloc_context3(video_codec);
  if (!video_codec_ctx) {
    SPDLOG_ERROR("Could not allocate video codec context");
  }

  /* open it */
  if (avcodec_open2(video_codec_ctx, video_codec, NULL) < 0) {
    SPDLOG_ERROR("Could not open codec");
  }

  uint8_t *data = h264_buf;
  size_t data_size = 0;

  AVPacket *pkt = av_packet_alloc();
  if (!pkt) {
    SPDLOG_ERROR("Could not allocate video packet");
  }

  AVFrame *frame = av_frame_alloc();
  if (!frame) {
    SPDLOG_ERROR("Could not allocate video frame");
  }

  while (!feof(h264_file)) {
    /* read raw data from the input file */
    data_size = fread(h264_buf, 1, INBUF_SIZE, h264_file);
    if (!data_size) break;

    /* use the parser to split the data into frames */
    data = h264_buf;
    while (data_size > 0) {
      int ret =
          av_parser_parse2(parser, video_codec_ctx, &pkt->data, &pkt->size,
                           data, data_size, AV_NOPTS_VALUE, AV_NOPTS_VALUE, 0);
      if (ret < 0) {
        SPDLOG_ERROR("Error while parsing\n");
      }
      data += ret;
      data_size -= ret;

      if (pkt->size) decode(video_codec_ctx, frame, pkt, yuv_file);
    }
  }

  /* flush the decoder */
  decode(video_codec_ctx, frame, NULL, yuv_file);

  fclose(h264_file);
  fclose(yuv_file);

  av_parser_close(parser);
  avcodec_free_context(&video_codec_ctx);
  av_frame_free(&frame);
  av_packet_free(&pkt);

  SPDLOG_INFO("video decode successful");

  return 0;
}
[2021-09-14 21:37:28.285] [info] [main.cpp:16] width: 1280
[2021-09-14 21:37:28.287] [info] [main.cpp:17] height: 720
[2021-09-14 21:37:28.287] [info] [main.cpp:18] format: 0
[2021-09-14 21:37:29.256] [info] [main.cpp:152] video decode successful

使用如下命令播放生成的yuv文件:

ffplay -pixel_format yuv420p -video_size 1280*720 -framerate 25 Jasmine.yuv
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值