FFmpeg处理流程

打印 上一主题 下一主题

主题 928|帖子 928|积分 2784

结构体

AVFormatContext

作用:管理媒体文件的封装格式上下文,存储文件格式、流信息、I/O 操作等元数据。
关键字段
  1. AVInputFormat *iformat;   // 输入格式(如MP4、FLV)
  2. AVStream **streams;       // 音视频流数组
  3. int nb_streams;           // 流数量
  4. int64_t duration;         // 总时长(微秒)
复制代码
初始化:avformat_alloc_context(),avformat_open_input()
AVStream

作用:表现单个音视频流,包含编解码参数和时间基准。
关键字段:
  1. AVCodecParameters *codecpar;  // 编解码参数(如分辨率、采样率)
  2. AVRational time_base;         // 时间基(如1/30表示30fps)
复制代码
初始化:avformat_new_stream()
AVCodec

AVCodecContext

作用:编解码器上下文,存储编解码参数(如码率、帧率、像素格式)。
关键字段:
  1. enum AVCodecID codec_id;      // 编解码器ID(如H.264、AAC)
  2. int width, height;            // 视频分辨率
  3. enum AVPixelFormat pix_fmt;   // 像素格式(如YUV420P)
  4. AVRational time_base;         // 编码器时间基
复制代码
初始化:avcodec_alloc_context3(),avcodec_parameters_to_context()
AVPacket

作用:存储编码后的压缩数据(如H.264数据包)。
关键字段:
  1. uint8_t *data;       // 压缩数据指针
  2. int size;            // 数据大小
  3. int64_t pts, dts;    // 显示和解码时间戳
复制代码
初始化:av_packet_alloc(),av_packet_unref()
AVFrame

作用:存储解码后的原始数据(如YUV像素数据或PCM音频样本)。
关键字段:
  1. uint8_t *data[AV_NUM_DATA_POINTERS]; // 数据指针(如Y、U、V分量)
  2. int linesize[AV_NUM_DATA_POINTERS];  // 每行字节数
  3. int width, height;                    // 视频分辨率
复制代码
初始化:av_frame_alloc(),av_frame_free()
SwsContext

作用:图像格式转换上下文(如YUV转RGB)。
初始化:sws_getContext(),烧毁:sws_freeContext()
SwrContext

作用:音频重采样上下文(如48kHz转44.1kHz)。
初始化:swr_alloc_set_opts(),烧毁:swr_free()
API

avformat_open_input
avformat_find_stream_info
av_find_best_stream
avcodec_alloc_context3
avcodec_parameters_to_context
avcodec_open2
avcodec_find_encoder
av_opt_set_int
sws_getContext
avformat_alloc_output_context2
avformat_new_stream
avcodec_parameters_from_context
avio_open
avformat_write_header
av_frame_alloc
av_frame_get_buffer
av_packet_alloc
av_read_frame
avcodec_send_packet
avcodec_receive_frame
sws_scale
av_rescale_q
avcodec_send_frame
avcodec_receive_packet
av_packet_rescale_ts
av_interleaved_write_frame
av_packet_unref
av_write_trailer
例子

  1. #include <iostream>
  2. #include <memory>
  3. // 使用 RAII 管理指针(可选,但推荐)
  4. template<typename T, void(*Deleter)(T*)>
  5. struct FFmpegResource {
  6.     T* ptr = nullptr;
  7.     FFmpegResource(T* p = nullptr) : ptr(p) {}
  8.     ~FFmpegResource() { if (ptr) Deleter(ptr); }
  9. };
  10. using AVFormatContextPtr = FFmpegResource<AVFormatContext, avformat_close_input>;
  11. using AVCodecContextPtr = FFmpegResource<AVCodecContext, avcodec_free_context>;
  12. using SwsContextPtr = FFmpegResource<SwsContext, sws_freeContext>;
  13. using AVFramePtr = FFmpegResource<AVFrame, av_frame_free>;
  14. using AVPacketPtr = FFmpegResource<AVPacket, av_packet_free>;
  15. int main() {
  16.     AVFormatContext *srcCtx = nullptr;
  17.     AVCodecContext *srcDecCtx = nullptr, *encCtx = nullptr;
  18.     SwsContext *swsCtx = nullptr;
  19.     AVFrame *decFrame = nullptr, *encFrame = nullptr;
  20.     AVPacket *pkt = nullptr;
  21.     AVFormatContext *outputCtx = nullptr;
  22.     int ret = 0;
  23.     // 错误处理标签
  24.     #define CHECK_ERROR(cond, msg, cleanup_label) \
  25.         if ((cond)) { \
  26.             std::cerr << (msg) << ": " << av_err2str(ret) << std::endl; \
  27.             goto cleanup_label; \
  28.         }
  29.     // ============ 打开输入文件 ============
  30.     ret = avformat_open_input(&srcCtx, srcPath.toStdString().c_str(), nullptr, nullptr);
  31.     CHECK_ERROR(ret < 0, "打开视频文件失败", cleanup);
  32.     ret = avformat_find_stream_info(srcCtx, nullptr);
  33.     CHECK_ERROR(ret < 0, "获取视频流信息失败", cleanup);
  34.     // ============ 初始化视频解码器 ============
  35.     const AVCodec *srcDec = nullptr;
  36.     int streamIndex = av_find_best_stream(srcCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &srcDec, 0);
  37.     CHECK_ERROR(streamIndex < 0, "查找视频流失败", cleanup);
  38.     srcDecCtx = avcodec_alloc_context3(srcDec);
  39.     CHECK_ERROR(!srcDecCtx, "分配解码器上下文失败", cleanup);
  40.     ret = avcodec_parameters_to_context(srcDecCtx, srcCtx->streams[streamIndex]->codecpar);
  41.     CHECK_ERROR(ret < 0, "拷贝解码器参数失败", cleanup);
  42.     ret = avcodec_open2(srcDecCtx, srcDec, nullptr);
  43.     CHECK_ERROR(ret < 0, "打开解码器失败", cleanup);
  44.     // ============ 初始化视频编码器 ============
  45.     const AVCodec *srcEnc = avcodec_find_encoder(srcCtx->streams[streamIndex]->codecpar->codec_id);
  46.     CHECK_ERROR(!srcEnc, "查找编码器失败", cleanup);
  47.     encCtx = avcodec_alloc_context3(srcEnc);
  48.     CHECK_ERROR(!encCtx, "分配编码器上下文失败", cleanup);
  49.     // 配置编码参数
  50.     encCtx->width = width;
  51.     encCtx->height = height;
  52.     encCtx->pix_fmt = AV_PIX_FMT_YUV420P;
  53.     encCtx->time_base = {1, 30};
  54.     encCtx->gop_size = 12;
  55. //    encCtx->bit_rate = 4000000; 不设置码率
  56.     encCtx->profile = FF_PROFILE_H264_HIGH;
  57.     encCtx->level = 40;
  58.     encCtx->max_b_frames = 2;
  59.     encCtx->color_range = AVCOL_RANGE_MPEG; // 颜色范围(tv)
  60.     encCtx->color_primaries = AVCOL_PRI_BT709; // 颜色标准
  61.     encCtx->color_trc = AVCOL_TRC_BT709;  // 颜色传输特性
  62.     encCtx->colorspace = AVCOL_SPC_BT709; // 颜色空间
  63.     // 设置CRF模式与参数调整
  64.     encCtx->flags |= AV_CODEC_FLAG_QSCALE;// 启用量化参数控制
  65.     av_opt_set_int(encCtx->priv_data, "crf", 18, AV_OPT_SEARCH_CHILDREN);// 0-51,18为视觉无损
  66.     av_opt_set(encCtx->priv_data, "preset", "veryslow", AV_OPT_SEARCH_CHILDREN);  // 牺牲时间换取质量
  67. //    av_opt_set(encCtx->priv_data, "tune", "film", AV_OPT_SEARCH_CHILDREN);  // 电影类用film,动画用animation
  68.     ret = avcodec_open2(encCtx, srcEnc, nullptr);
  69.     CHECK_ERROR(ret < 0, "打开编码器失败", cleanup);
  70.     // ============ 创建缩放上下文 ============
  71.     swsCtx = sws_getContext(/* 参数保持原逻辑 */);
  72.     CHECK_ERROR(!swsCtx, "创建缩放上下文失败", cleanup);
  73.     // ============ 准备输出文件 ============
  74.     ret = avformat_alloc_output_context2(&outputCtx, nullptr, nullptr, destPath.toStdString().c_str());
  75.     CHECK_ERROR(ret < 0, "创建输出上下文失败", cleanup);
  76.     AVStream *outStream = avformat_new_stream(outputCtx, nullptr);
  77.     CHECK_ERROR(!outStream, "创建输出流失败", cleanup);
  78.     ret = avcodec_parameters_from_context(outStream->codecpar, encCtx);
  79.     CHECK_ERROR(ret < 0, "拷贝编码器参数到输出流失败", cleanup);
  80.     // 显式设置输出流时间基与编码器一致
  81.     outStream->time_base = encCtx->time_base;
  82.     if (!(outputCtx->oformat->flags & AVFMT_NOFILE)) {
  83.         ret = avio_open(&outputCtx->pb, destPath.toStdString().c_str(), AVIO_FLAG_WRITE);
  84.         CHECK_ERROR(ret < 0, "打开输出文件失败", cleanup);
  85.     }
  86.     ret = avformat_write_header(outputCtx, nullptr);
  87.     CHECK_ERROR(ret < 0, "写入文件头失败", cleanup);
  88.     // ============ 帧处理循环 ============
  89.     decFrame = av_frame_alloc();
  90.     encFrame = av_frame_alloc();
  91.     pkt = av_packet_alloc();
  92.     CHECK_ERROR(!decFrame || !encFrame || !pkt, "分配帧/包失败", cleanup);
  93.     while (av_read_frame(srcCtx, pkt) >= 0) {
  94.         if (pkt->stream_index != streamIndex) {
  95.             av_packet_unref(pkt);
  96.             continue;
  97.         }
  98.          // 解码
  99.         if ((ret = avcodec_send_packet(srcDecCtx, pkt)) < 0) {
  100.             cout << "读取包失败: " << av_err2str(ret) << endl;
  101.         }
  102.         while (ret >= 0) {
  103.             ret = avcodec_receive_frame(srcDecCtx, decFrame);
  104.             if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
  105.                 break;
  106.             } else if (ret < 0) {
  107.                 cout << "读取帧失败: " << av_err2str(ret) << endl;
  108.             }
  109.             cout << "解码帧 pts: " << decFrame->pts << endl;
  110.             // 缩放
  111.             sws_scale(swsCtx, decFrame->data, decFrame->linesize,
  112.                       0, srcDecCtx->height, encFrame->data, encFrame->linesize);
  113.             encFrame->pts = av_rescale_q(decFrame->pts, srcCtx->streams[streamIndex]->time_base, encCtx->time_base);
  114.             // 编码
  115.             AVPacket *encPkt = av_packet_alloc();
  116.             if ((ret = avcodec_send_frame(encCtx, encFrame)) < 0) {
  117.                 cout << "发送帧到编码器失败: " << av_err2str(ret) << endl;
  118.             }
  119.             while (ret >= 0) {
  120.                 ret = avcodec_receive_packet(encCtx, encPkt);
  121.                 if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {
  122.                     break;
  123.                 } else if (ret < 0) {
  124.                     cout << "编码器输出包失败: " << av_err2str(ret) << endl;
  125.                 }
  126.                 // 写入输出文件
  127.                 av_packet_rescale_ts(encPkt, encCtx->time_base, outStream->time_base);
  128.                 cout << "编码: " << encPkt->pts << endl;
  129.                 av_interleaved_write_frame(outputCtx, encPkt);
  130.                 av_packet_unref(encPkt);
  131.             }
  132.         }
  133.     }
  134.     // ============ 刷新编码器缓冲区  ============
  135.     avcodec_send_frame(encCtx, nullptr); // 发送空帧刷新
  136.     while (true) {
  137.         AVPacket encPkt;
  138.         av_init_packet(&encPkt);
  139.         ret = avcodec_receive_packet(encCtx, &encPkt);
  140.         if (ret == AVERROR_EOF || ret < 0) break;
  141.         av_packet_rescale_ts(&encPkt, encCtx->time_base, outStream->time_base);
  142.         av_interleaved_write_frame(outputCtx, &encPkt);
  143.         av_packet_unref(&encPkt);
  144.     }
  145.     // ============ 写入文件尾 ============
  146.     ret = av_write_trailer(outputCtx);
  147.     CHECK_ERROR(ret < 0, "写入文件尾失败", cleanup);
  148. // ============ 资源释放 ============
  149. cleanup:
  150.     avformat_close_input(&srcCtx);
  151.     avcodec_free_context(&srcDecCtx);
  152.     avcodec_free_context(&encCtx);
  153.     sws_freeContext(swsCtx);
  154.     av_frame_free(&decFrame);
  155.     av_frame_free(&encFrame);
  156.     av_packet_free(&pkt);
  157.     if (outputCtx && !(outputCtx->oformat->flags & AVFMT_NOFILE)) {
  158.         avio_closep(&outputCtx->pb);
  159.     }
  160.     avformat_free_context(outputCtx);
  161.     return ret;
  162. }
复制代码
免责声明:如果侵犯了您的权益,请联系站长,我们会及时删除侵权内容,谢谢合作!更多信息从访问主页:qidao123.com:ToB企服之家,中国第一个企服评测及商务社交产业平台。
回复

使用道具 举报

0 个回复

倒序浏览

快速回复

您需要登录后才可以回帖 登录 or 立即注册

本版积分规则

徐锦洪

金牌会员
这个人很懒什么都没写!
快速回复 返回顶部 返回列表