欢迎访问我的博客原文:https://lightfish.cn/2018-12-20-ffmpeg-primer
前言
本文以 ffmpeg 工具,讲述如何认识音视频编程,你可以了解到常见视频格式的大概样子,一步步学会如何使用 ffmpeg 的 C 语言 API
本文重于动手实践,代码仓库:mpegUtil
笔者的开发环境:Arch Linux 4.19.12, ffmpeg version n4.1
解码过程总览
以下是解码流程图,逆向即是编码流程
本文是音视频编程入门篇,先略过传输协议层,主要讲格式层与编解码层的编程例子。
写在最前面的日志处理
边编程边执行,查看日志输出,是最直接的反馈,以感受学习的进度。对于 ffmpeg 的日志,需要提前这样处理:
/* log.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <libavutil/log.h>
// 定义输出日志的函数,留白给使用者实现
extern void Ffmpeglog(int , char*);
static void log_callback(void *avcl, int level, const char *fmt, va_list vl)
{
(void) avcl;
char log[1024] = {
0};
int n = vsnprintf(log, 1024, fmt, vl);
if (n > 0 && log[n - 1] == '\n')
log[n - 1] = 0;
if (strlen(log) == 0)
return;
Ffmpeglog(level, log);
}
void set_log_callback()
{
// 给 av 解码器注册日志回调函数
av_log_set_callback(log_callback);
}
/* main.c */
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
void Ffmpeglog(int l, char* t) {
if(l <= AV_LOG_INFO)
fprintf(stdout, "%s\n", t);
}
ffmpeg 有不同等级的日志,本文只需使用 AV_LOG_INFO
即可。
第一步,查看音视频格式信息
料理食材的第一步,得先懂得食材的来源和特性。
- 来源,互联网在线观看(http/rtmp)、播放设备上存储的视频文件(file)。
- 格式,如何查看视频文件的格式呢,以下有 unix 命令行示例,至于 windows 系统,查看文件属性即可。
# linux 上查看视频文件信息
ffmpeg -i example.mp4
以某个mp4文件为例,输出:
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'example.mp4':
Metadata:
major_brand : isom
minor_version : 512
compatible_brands: isomiso2avc1mp41
encoder : Wxmm_900012345
Duration: 00:00:58.21, start: 0.000000, bitrate: 541 kb/s
Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 368x640, 487 kb/s, 24 fps, 24 tbr, 12288 tbn, 48 tbc (default)
Metadata:
handler_name : VideoHandler
Stream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 48 kb/s (default)
Metadata:
handler_name : SoundHandler
根据命令输出信息,视频文件中有两个 stream, 即 video 与 audio,视频流与音频流。
- stream 0, 是视频数据,编码格式为h264,24 fps 意为 24 frame per second,即每秒24帧,比特率487 kb/s,
- stream 1, 是音频数据,编码格式为acc,采样率44100 Hz,比特率48 kb/s
【编程实操】读取音视频流的格式信息
在互联网场景中,在线观看视频才是常见需求,那么,计算机如何读取视频流的信息呢,下面以 ffmpeg 代码讲述
/* C代码例子,省略了处理错误的逻辑 */
AVFormatContext *fmt_ctx = NULL; // AV 格式上下文
AVIOContext *avio_ctx = NULL; // AV IO 上下文
unsigned char *avio_ctx_buffer = NULL; // input buffer
fmt_ctx = avformat_alloc_context();// 获得 AV format 句柄
avio_ctx_buffer