当前位置: 首页 > news >正文

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.lryc.cn/news/616248.html

相关文章:

  • Spring AOP 底层实现(面试重点难点)
  • AQS(AbstractQueuedSynchronizer)底层源码实现与设计思想
  • 前端路由:Hash 模式与 History 模式深度解析
  • Java Stream流详解:从基础语法到实战应用
  • Rust 实战四 | Traui2+Vue3+Rspack 开发桌面应用:通配符掩码计算器
  • 【算法题】:和为N的连续正数序列
  • 数学建模:控制预测类问题
  • Python 获取对象信息的所有方法
  • matlab实现随机森林算法
  • Doubletrouble靶机练习
  • 点击速度测试:一款放大操作差距的互动挑战游戏
  • #Datawhale AI夏令营#第三期全球AI攻防挑战赛(AIGC技术-图像方向)
  • 如何用分析方法解决工作中的问题?
  • 检索召回率优化探究五(BGE-M3 混合检索):基于LangChain0.3 集成Milvu2.5 向量数据库构建的智能问答系统
  • Linux系统编程Day11 -- 进程属性和常见进程
  • 学习模板元编程(3)enable_if
  • 【树状数组】Dynamic Range Sum Queries
  • [激光原理与应用-221]:设计 - 皮秒紫外激光器 - 常见技术难题、原因与解决方案
  • Linux-静态配置ip地址
  • 【无标题】命名管道(Named Pipe)是一种在操作系统中用于**进程间通信(IPC)** 的机制
  • 深度解析Linux设备树(DTS):设计原理、实现框架与实例分析
  • 基于Qt/QML 5.14和YOLOv8的工业异常检测Demo:冲压点智能识别
  • 线程池的核心线程数与最大线程数怎么设置
  • 基于FFmpeg的B站视频下载处理
  • 简要介绍交叉编译工具arm-none-eabi、arm-linux-gnueabi与arm-linux-gnueabihf
  • 【iOS】JSONModel源码学习
  • 2025.8.10总结
  • mpv core_thread pipeline
  • 第16届蓝桥杯Scratch选拔赛初级及中级(STEMA)2025年4月13日真题
  • ARM保留的标准中断处理程序入口和外设中断处理程序入口介绍