FFmpeg处理流程

embedded/2025/3/17 22:18:33/

结构体

AVFormatContext

作用:管理媒体文件的封装格式上下文,存储文件格式、流信息、I/O 操作等元数据。
关键字段

AVInputFormat *iformat;   // 输入格式(如MP4、FLV)
AVStream **streams;       // 音视频流数组
int nb_streams;           // 流数量
int64_t duration;         // 总时长(微秒)

初始化:avformat_alloc_context()avformat_open_input()

AVStream

作用:表示单个音视频流,包含编解码参数和时间基准。
关键字段:

AVCodecParameters *codecpar;  // 编解码参数(如分辨率、采样率)
AVRational time_base;         // 时间基(如1/30表示30fps)

初始化:avformat_new_stream()

AVCodec

AVCodecContext

作用:编解码器上下文,存储编解码参数(如码率、帧率、像素格式)。
关键字段:

enum AVCodecID codec_id;      // 编解码器ID(如H.264、AAC)
int width, height;            // 视频分辨率
enum AVPixelFormat pix_fmt;   // 像素格式(如YUV420P)
AVRational time_base;         // 编码器时间基

初始化:avcodec_alloc_context3(),avcodec_parameters_to_context()

AVPacket

作用:存储编码后的压缩数据(如H.264数据包)。
关键字段:

uint8_t *data;       // 压缩数据指针
int size;            // 数据大小
int64_t pts, dts;    // 显示和解码时间戳

初始化:av_packet_alloc()av_packet_unref()

AVFrame

作用:存储解码后的原始数据(如YUV像素数据或PCM音频样本)。
关键字段:

uint8_t *data[AV_NUM_DATA_POINTERS]; // 数据指针(如Y、U、V分量)
int linesize[AV_NUM_DATA_POINTERS];  // 每行字节数
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

例子

#include <iostream>
#include <memory>// 使用 RAII 管理指针(可选,但推荐)
template<typename T, void(*Deleter)(T*)>
struct FFmpegResource {T* ptr = nullptr;FFmpegResource(T* p = nullptr) : ptr(p) {}~FFmpegResource() { if (ptr) Deleter(ptr); }
};using AVFormatContextPtr = FFmpegResource<AVFormatContext, avformat_close_input>;
using AVCodecContextPtr = FFmpegResource<AVCodecContext, avcodec_free_context>;
using SwsContextPtr = FFmpegResource<SwsContext, sws_freeContext>;
using AVFramePtr = FFmpegResource<AVFrame, av_frame_free>;
using AVPacketPtr = FFmpegResource<AVPacket, av_packet_free>;int main() {AVFormatContext *srcCtx = nullptr;AVCodecContext *srcDecCtx = nullptr, *encCtx = nullptr;SwsContext *swsCtx = nullptr;AVFrame *decFrame = nullptr, *encFrame = nullptr;AVPacket *pkt = nullptr;AVFormatContext *outputCtx = nullptr;int ret = 0;// 错误处理标签#define CHECK_ERROR(cond, msg, cleanup_label) \if ((cond)) { \std::cerr << (msg) << ": " << av_err2str(ret) << std::endl; \goto cleanup_label; \}// ============ 打开输入文件 ============ret = avformat_open_input(&srcCtx, srcPath.toStdString().c_str(), nullptr, nullptr);CHECK_ERROR(ret < 0, "打开视频文件失败", cleanup);ret = avformat_find_stream_info(srcCtx, nullptr);CHECK_ERROR(ret < 0, "获取视频流信息失败", cleanup);// ============ 初始化视频解码器 ============const AVCodec *srcDec = nullptr;int streamIndex = av_find_best_stream(srcCtx, AVMEDIA_TYPE_VIDEO, -1, -1, &srcDec, 0);CHECK_ERROR(streamIndex < 0, "查找视频流失败", cleanup);srcDecCtx = avcodec_alloc_context3(srcDec);CHECK_ERROR(!srcDecCtx, "分配解码器上下文失败", cleanup);ret = avcodec_parameters_to_context(srcDecCtx, srcCtx->streams[streamIndex]->codecpar);CHECK_ERROR(ret < 0, "拷贝解码器参数失败", cleanup);ret = avcodec_open2(srcDecCtx, srcDec, nullptr);CHECK_ERROR(ret < 0, "打开解码器失败", cleanup);// ============ 初始化视频编码器 ============const AVCodec *srcEnc = avcodec_find_encoder(srcCtx->streams[streamIndex]->codecpar->codec_id);CHECK_ERROR(!srcEnc, "查找编码器失败", cleanup);encCtx = avcodec_alloc_context3(srcEnc);CHECK_ERROR(!encCtx, "分配编码器上下文失败", cleanup);// 配置编码参数encCtx->width = width;encCtx->height = height;encCtx->pix_fmt = AV_PIX_FMT_YUV420P;encCtx->time_base = {1, 30};encCtx->gop_size = 12;
//    encCtx->bit_rate = 4000000; 不设置码率encCtx->profile = FF_PROFILE_H264_HIGH;encCtx->level = 40;encCtx->max_b_frames = 2;encCtx->color_range = AVCOL_RANGE_MPEG; // 颜色范围(tv)encCtx->color_primaries = AVCOL_PRI_BT709; // 颜色标准encCtx->color_trc = AVCOL_TRC_BT709;  // 颜色传输特性encCtx->colorspace = AVCOL_SPC_BT709; // 颜色空间// 设置CRF模式与参数调整encCtx->flags |= AV_CODEC_FLAG_QSCALE;// 启用量化参数控制av_opt_set_int(encCtx->priv_data, "crf", 18, AV_OPT_SEARCH_CHILDREN);// 0-51,18为视觉无损av_opt_set(encCtx->priv_data, "preset", "veryslow", AV_OPT_SEARCH_CHILDREN);  // 牺牲时间换取质量
//    av_opt_set(encCtx->priv_data, "tune", "film", AV_OPT_SEARCH_CHILDREN);  // 电影类用film,动画用animationret = avcodec_open2(encCtx, srcEnc, nullptr);CHECK_ERROR(ret < 0, "打开编码器失败", cleanup);// ============ 创建缩放上下文 ============swsCtx = sws_getContext(/* 参数保持原逻辑 */);CHECK_ERROR(!swsCtx, "创建缩放上下文失败", cleanup);// ============ 准备输出文件 ============ret = avformat_alloc_output_context2(&outputCtx, nullptr, nullptr, destPath.toStdString().c_str());CHECK_ERROR(ret < 0, "创建输出上下文失败", cleanup);AVStream *outStream = avformat_new_stream(outputCtx, nullptr);CHECK_ERROR(!outStream, "创建输出流失败", cleanup);ret = avcodec_parameters_from_context(outStream->codecpar, encCtx);CHECK_ERROR(ret < 0, "拷贝编码器参数到输出流失败", cleanup);// 显式设置输出流时间基与编码器一致 outStream->time_base = encCtx->time_base;if (!(outputCtx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&outputCtx->pb, destPath.toStdString().c_str(), AVIO_FLAG_WRITE);CHECK_ERROR(ret < 0, "打开输出文件失败", cleanup);}ret = avformat_write_header(outputCtx, nullptr);CHECK_ERROR(ret < 0, "写入文件头失败", cleanup);// ============ 帧处理循环 ============decFrame = av_frame_alloc();encFrame = av_frame_alloc();pkt = av_packet_alloc();CHECK_ERROR(!decFrame || !encFrame || !pkt, "分配帧/包失败", cleanup);while (av_read_frame(srcCtx, pkt) >= 0) {if (pkt->stream_index != streamIndex) {av_packet_unref(pkt);continue;}// 解码if ((ret = avcodec_send_packet(srcDecCtx, pkt)) < 0) {cout << "读取包失败: " << av_err2str(ret) << endl;}while (ret >= 0) {ret = avcodec_receive_frame(srcDecCtx, decFrame);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {cout << "读取帧失败: " << av_err2str(ret) << endl;}cout << "解码帧 pts: " << decFrame->pts << endl;// 缩放sws_scale(swsCtx, decFrame->data, decFrame->linesize,0, srcDecCtx->height, encFrame->data, encFrame->linesize);encFrame->pts = av_rescale_q(decFrame->pts, srcCtx->streams[streamIndex]->time_base, encCtx->time_base);// 编码AVPacket *encPkt = av_packet_alloc();if ((ret = avcodec_send_frame(encCtx, encFrame)) < 0) {cout << "发送帧到编码器失败: " << av_err2str(ret) << endl;}while (ret >= 0) {ret = avcodec_receive_packet(encCtx, encPkt);if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) {break;} else if (ret < 0) {cout << "编码器输出包失败: " << av_err2str(ret) << endl;}// 写入输出文件av_packet_rescale_ts(encPkt, encCtx->time_base, outStream->time_base);cout << "编码: " << encPkt->pts << endl;av_interleaved_write_frame(outputCtx, encPkt);av_packet_unref(encPkt);}}}// ============ 刷新编码器缓冲区  ============avcodec_send_frame(encCtx, nullptr); // 发送空帧刷新while (true) {AVPacket encPkt;av_init_packet(&encPkt);ret = avcodec_receive_packet(encCtx, &encPkt);if (ret == AVERROR_EOF || ret < 0) break;av_packet_rescale_ts(&encPkt, encCtx->time_base, outStream->time_base);av_interleaved_write_frame(outputCtx, &encPkt);av_packet_unref(&encPkt);}// ============ 写入文件尾 ============ret = av_write_trailer(outputCtx);CHECK_ERROR(ret < 0, "写入文件尾失败", cleanup);// ============ 资源释放 ============
cleanup:avformat_close_input(&srcCtx);avcodec_free_context(&srcDecCtx);avcodec_free_context(&encCtx);sws_freeContext(swsCtx);av_frame_free(&decFrame);av_frame_free(&encFrame);av_packet_free(&pkt);if (outputCtx && !(outputCtx->oformat->flags & AVFMT_NOFILE)) {avio_closep(&outputCtx->pb);}avformat_free_context(outputCtx);return ret;
}

http://www.ppmy.cn/embedded/173439.html

相关文章

AI 游戏的创新与挑战都有哪些?

一、AI 游戏的创新 &#xff08;一&#xff09;技术突破 AI 技术的不断进步为游戏产业带来了新的机遇。深度学习、强化学习等技术的应用&#xff0c;使得游戏中的 AI 角色能够展现出更加智能的行为和决策能力。例如&#xff0c;通过强化学习&#xff0c;AI 可以在游戏中自主学…

3ds Max 鼠标与快捷键组合操作指南

以下是 3ds Max 鼠标与快捷键组合操作指南&#xff0c;按功能分类整理&#xff0c;涵盖 视图控制、对象操作、建模工具 等核心场景。自用记录&#xff0c;不喜勿喷。 一、视图控制&#xff08;鼠标核心操作&#xff09; 操作快捷键鼠标动作功能说明平移视图鼠标中键拖动按住中…

PCDN 与边缘计算的结合​

在数字化时代&#xff0c;PCDN 与边缘计算作为两项前沿技术&#xff0c;正悄然改变着网络生态格局。PCDN&#xff0c;即点到点内容分发网络&#xff0c;它借助众多用户的闲置带宽与计算资源&#xff0c;构建起庞大的分布式网络&#xff0c;能有效提升内容传输效率。边缘计算则是…

涨薪技术|Kubernetes(k8s)之Pod生命周期(下)

上次推文我们学习了Pod生命周期&#xff08;上&#xff09;知识&#xff1a;相位、创建与终止、初始化容器&#xff0c;今天继续分享完余下的3个知识&#xff1a;钩子函数、容器探测、重启策略。 01钩子函数 钩子函数能够感知自身生命周期中的事件&#xff0c;并在相应的时刻…

谷歌手机LEA流程

谷歌手机LEA流程 连接管理首次连接手机回连 业务管理音乐业务通话业务 链路切换管理 本篇文章简单介绍了谷歌手机使用LE Audio连接TWS耳机中的实现细节&#xff0c;强调了持续广播机制、业务差异化处理、链路切换逻辑及加密安全性。核心目标是优化低功耗音频连接的稳定性和资源…

PyTorch 深度学习实战(11):强化学习与深度 Q 网络(DQN)

在之前的文章中&#xff0c;我们介绍了神经网络、卷积神经网络&#xff08;CNN&#xff09;、循环神经网络&#xff08;RNN&#xff09;、Transformer 等多种深度学习模型&#xff0c;并应用于图像分类、文本分类、时间序列预测等任务。本文将介绍强化学习的基本概念&#xff0…

基于Alpine构建MySQL 10.11.11镜像的完整教程

基于Alpine构建MySQL 10.11.11镜像的完整教程 在容器化技术日益流行的今天&#xff0c;使用Docker构建高效、轻量级的MySQL镜像成为许多开发者的需求。本教程将详细介绍如何基于Alpine Linux构建MySQL 10.11.11镜像&#xff0c;Alpine Linux以其小巧的体积和快速的启动速度而闻…

目前市场上主流的机器视觉的框架有哪些?他们的特点及优劣

目前市场上主流的机器视觉框架和工具可以分为商业软件、开源工具和深度学习框架三大类。以下是它们的总结及特点对比&#xff1a; 1. ​商业软件 (1) ​Halcon (MVTec) ​特点&#xff1a; 专注于工业机器视觉&#xff0c;提供高精度、高效率的算法。支持复杂的工业应用&…