FFmpeg实现音视频转码

以下是基于 FFmpeg 库实现 MP4 转码的详细步骤(以 C 语言为例):

一、环境准备

集成 FFmpeg 库
· 编译 FFmpeg 生成动态库(avformat、avcodec、avutil、swscale、swresample等)
· 在 SDK 项目中配置头文件路径和库文件链接
核心数据结构
· AVFormatContext:封装格式上下文(输入 / 输出文件)
· AVCodecContext:编解码器上下文
· AVCodec:编码器 / 解码器
· AVPacket:存储压缩的音视频数据
· AVFrame:存储未压缩的音视频数据

二、转码核心步骤

1. 初始化 FFmpeg

// 注册所有组件(旧版本需要,新版本可省略)
av_register_all();
// 初始化网络模块(如需网络流)
avformat_network_init();

2. 打开输入文件

AVFormatContext *input_ctx = NULL;
const char *input_path = "input.mp4";// 打开输入文件并读取封装格式信息
int ret = avformat_open_input(&input_ctx, input_path, NULL, NULL);
if (ret != 0) {// 错误处理:av_strerror(ret, err_msg, sizeof(err_msg))return -1;
}
// 读取流信息(音视频轨道等)
ret = avformat_find_stream_info(input_ctx, NULL);
if (ret < 0) {// 错误处理avformat_close_input(&input_ctx);return -1;
}

3. 查找输入流的编码器

// 查找视频流和音频流的索引
int video_stream_idx = av_find_best_stream(input_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
int audio_stream_idx = av_find_best_stream(input_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);// 获取输入视频流和解码器
AVStream *input_video_stream = input_ctx->streams[video_stream_idx];
AVCodec *input_video_codec = avcodec_find_decoder(input_video_stream->codecpar->codec_id);
AVCodecContext *input_video_ctx = avcodec_alloc_context3(input_video_codec);
avcodec_parameters_to_context(input_video_ctx, input_video_stream->codecpar);
avcodec_open2(input_video_ctx, input_video_codec, NULL); // 打开解码器// 音频流同理

4. 创建输出文件上下文

AVFormatContext *output_ctx = NULL;
const char *output_path = "output.mp4";// 分配输出上下文(根据输出路径自动推断封装格式)
avformat_alloc_output_context2(&output_ctx, NULL, NULL, output_path);
if (!output_ctx) {// 错误处理return -1;
}// 打开输出文件(本地文件用avio_open)
if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {ret = avio_open(&output_ctx->pb, output_path, AVIO_FLAG_WRITE);if (ret < 0) {// 错误处理return -1;}
}

5. 配置输出流编码器
以视频流为例(音频流类似):

// 创建输出视频流
AVStream *output_video_stream = avformat_new_stream(output_ctx, NULL);
// 设置输出编码器(如H.264)
AVCodec *output_video_codec = avcodec_find_encoder(AV_CODEC_ID_H264);
AVCodecContext *output_video_ctx = avcodec_alloc_context3(output_video_codec);// 配置编码器参数
output_video_ctx->width = input_video_ctx->width; // 宽度
output_video_ctx->height = input_video_ctx->height; // 高度
output_video_ctx->pix_fmt = output_video_codec->pix_fmts[0]; // 像素格式(如YUV420P)
output_video_ctx->bit_rate = 2000000; // 比特率(2Mbps)
output_video_ctx->time_base = (AVRational){1, 25}; // 时间基(帧率25fps)
output_video_ctx->framerate = (AVRational){25, 1};// 打开输出编码器
ret = avcodec_open2(output_video_ctx, output_video_codec, NULL);
if (ret < 0) {// 错误处理return -1;
}// 将编码器参数复制到输出流
avcodec_parameters_from_context(output_video_stream->codecpar, output_video_ctx);
output_video_stream->time_base = output_video_ctx->time_base;

6. 写入输出文件头

ret = avformat_write_header(output_ctx, NULL);
if (ret < 0) {// 错误处理return -1;
}

7. 转码主循环

AVPacket *pkt = av_packet_alloc(); // 输入数据包
AVFrame *frame = av_frame_alloc(); // 解码后的帧
AVFrame *output_frame = av_frame_alloc(); // 编码前的帧(可能需要格式转换)while (av_read_frame(input_ctx, pkt) >= 0) { // 读取输入数据包if (pkt->stream_index == video_stream_idx) {// 解码视频avcodec_send_packet(input_video_ctx, pkt);while (avcodec_receive_frame(input_video_ctx, frame) == 0) {// 格式转换(如像素格式转换)sws_scale(/* 转换参数 */);// 编码avcodec_send_frame(output_video_ctx, output_frame);while (avcodec_receive_packet(output_video_ctx, pkt) == 0) {// 调整时间戳pkt->stream_index = output_video_stream->index;av_packet_rescale_ts(pkt, input_video_stream->time_base, output_video_stream->time_base);// 写入输出文件av_interleaved_write_frame(output_ctx, pkt);av_packet_unref(pkt);}}} else if (pkt->stream_index == audio_stream_idx) {// 音频转码(类似视频流程,使用swresample处理音频格式)}av_packet_unref(pkt);
}// 刷新编码器缓存
avcodec_send_frame(output_video_ctx, NULL);
while (avcodec_receive_packet(output_video_ctx, pkt) == 0) {av_interleaved_write_frame(output_ctx, pkt);av_packet_unref(pkt);
}

8. 清理资源

// 写入文件尾
av_write_trailer(output_ctx);// 释放上下文
avcodec_free_context(&input_video_ctx);
avcodec_free_context(&output_video_ctx);
avformat_close_input(&input_ctx);
if (!(output_ctx->oformat->flags & AVFMT_NOFILE)) {avio_closep(&output_ctx->pb);
}
avformat_free_context(output_ctx);// 释放帧和数据包
av_frame_free(&frame);
av_frame_free(&output_frame);
av_packet_free(&pkt);

三、关键注意事项
错误处理:每个 FFmpeg API 调用都需检查返回值,使用av_strerror()解析错误信息。
格式转换:
· 视频:使用sws_scale()转换像素格式 / 分辨率
· 音频:使用swr_convert()转换采样率 / 声道数
时间戳同步:转码时需通过av_packet_rescale_ts()调整时间戳,避免音视频不同步。
编码器参数优化:
· 视频:可设置crf参数(恒定质量模式)替代固定比特率
· 音频:设置采样率(如 44100Hz)和声道数(如立体声)
线程安全:FFmpeg 部分 API 非线程安全,多线程转码需加锁或使用独立上下文。

四、SDK 封装建议

提供高层 API:如transcode_mp4(const char* input, const char* output, TranscodeParam* param)
支持参数配置:分辨率、比特率、帧率、编码格式等
回调机制:转码进度、错误信息通过回调函数通知上层
跨平台适配:处理 Windows/Linux/macOS 的库依赖和路径问题
通过以上步骤,可构建一个基础的 MP4 转码 SDK,实际开发中需根据需求扩展功能(如多格式支持、硬件加速等)。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/2747.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

09 【C++ 初阶】C/C++内存管理

文章目录前言1. C/C内存分布2. C语言中动态内存管理方式malloc&#xff1a;calloc&#xff1a;realloc&#xff1a;free&#xff1a;3. C内存管理方式3.1 定位new表达式(placement-new)3.2 new/delete3.2.1 new和delete操作自定义类型3.2.2 new和delete操作内置类型3.2.3 new和…

Datawhale+AI夏令营_让AI读懂财报PDF task2深入赛题笔记

1.深入理解baseline方案 1.1 赛题任务 项目背景 本次赛题的核心目标是打造一个能看懂图片、读懂文字、并将两者关联起来思考的AI助手&#xff0c;构建一个先进的智能问答系统&#xff0c;以应对真实世界中复杂的、图文混排的信息环境。 (1)让AI模型能够阅读并理解包含大量图标、…

OpenAI开源大模型 GPT-OSS 开放权重语言模型解析:技术特性、部署应用及产业影响

注&#xff1a;此文章内容均节选自充电了么创始人&#xff0c;CEO兼CTO陈敬雷老师的新书《GPT多模态大模型与AI Agent智能体》&#xff08;跟我一起学人工智能&#xff09;【陈敬雷编著】【清华大学出版社】 清华《GPT多模态大模型与AI Agent智能体》书籍配套视频课程【陈敬雷…

前端懒加载技术全面解析

懒加载(Lazy Loading)是一种优化前端性能的重要技术,核心思想是延迟加载非关键资源,只在需要时加载它们。 一、懒加载的基本原理 懒加载的核心思想是通过以下方式优化性能: 减少初始加载实践: 只加载首屏所需资源 节省带宽和内存: 避免加载用户可能不会查看的内容 提高…

【渲染流水线】[几何阶段]-[图元装配]以UnityURP为例

【从UnityURP开始探索游戏渲染】专栏-直达 前情提要 【渲染流水线】主线索引-从数据到图像以UnityURP为例-CSDN博客 图元装配负责将离散顶点组装成完整几何图元&#xff08;如点、线、三角形、三角形条带&#xff09; &#xff08;对渲染的探索是个持续不断完善的过程&#x…

Vue3的简单学习

一、创建应用&#xff08;createApp&#xff09;Vue3 中通过 createApp 函数创建应用实例&#xff0c;替代了 Vue2 的 new Vue()。知识点&#xff1a;createApp(App) 创建应用实例&#xff0c;mount(#app) 挂载到 DOM。应用实例可链式调用配置&#xff08;如全局组件、指令等&a…

v-model双向绑定指令

文章目录前言v-model.lazy 延迟同步v-model.trim 去掉空格前言 v-model指令是Vue.js中实现双向数据绑定的一种重要机制。它可以将表单控件的值与Vue.js实例中的数据进行双向绑定&#xff0c;即当表单控件的值发生变化时&#xff0c;Vue.js实例中的数据也会随之更新&#xff0c…

使用Prometheus + Grafana + node_exporter实现Linux服务器性能监控

监控与告警系统部署&#xff1a;项目示例: "使用Prometheus Grafana node_exporter实现Linux服务器性能监控"描述: 在目标服务器部署node_exporter采集系统指标(CPU, 内存, 磁盘, 网络)。部署配置Prometheus Server&#xff0c;抓取node_exporter数据。部署配置Gra…

2025 蓝桥杯C/C++国B 部分题解

P12836 [蓝桥杯 2025 国 B] 翻倍 题目描述 给定 nnn 个正整数 A1,A2,…,AnA_1, A_2, \ldots, A_nA1​,A2​,…,An​&#xff0c;每次操作可以选择任意一个数翻倍。 请输出让序列单调不下降&#xff0c;也就是每个数都不小于上一个数&#xff0c;最少需要操作多少次&#xff1f;…

在 Mac 上安装 IntelliJ IDEA

在 Mac 上安装 IntelliJ IDEA 步骤。下面我将提供两种最主流的安装方法&#xff0c;并附上详细的图文解说式步骤。 安装前的准备&#xff1a;选择版本 首先&#xff0c;你需要决定安装哪个版本的 IntelliJ IDEA。 版本Ultimate (旗舰版)Community (社区版)价格付费 (提供 30…

云原生时代的 Linux:容器、虚拟化与分布式的基石

&#x1f4dd;个人主页&#x1f339;&#xff1a;慌ZHANG-CSDN博客 &#x1f339;&#x1f339;期待您的关注 &#x1f339;&#x1f339; 在云计算与容器化快速发展的今天&#xff0c;Linux 已经不再只是服务器上的操作系统&#xff0c;而是整个云原生生态的底层基石。无论是运…

【普中STM32精灵开发攻略】--第 14 章 动态数码管实验

(1)实验平台&#xff1a; 普中STM32精灵开发板​​​https://item.taobao.com/item.htm?id739076227953(2)资料下载&#xff1a;普中科技-各型号产品资料下载链接 前面章节我们已经介绍了如何使用 STM32 单片机控制IO 输出。本章将介绍数码管显示&#xff0c;在实际应用中通常…