一、FFmpeg简介

1. ffmpeg/ffplay/ffprobe

1.1 概念

  1. ffmpeg: Hyper fast Audio and Video encoder 超快音视频编码器
  2. ffplay: Simple media player 简单媒体播放器
  3. ffprobe: Simple multimedia streams analyzer简单多媒体流分析器

1.2 帮助文档

  1. ffmpeg
    ◼基本信息:ffmpeg -h
    ◼高级信息:ffmpeg -h long
    ◼所有信息:ffmpeg -h full

  2. ffplay:ffplay -h

  3. ffprobe:ffprobe -h

2. ffmpeg八大模块

  1. AVUtil:核心工具库,许多其他模块都会依赖该库做一些基本的音视频处理操作,如log信息、版本信息等。
  2. AVFormat:文件格式和协议库,封装了Protocol层和Demuxer、Muxer层
  3. AVCodec:编解码库,封装了Codec库,默认不会添加libx264、libfdk_aac等三方库的,但可以插件形式添加,然后提供统一接口
  4. AVFilter:音视频滤镜库,该模块提供了包括音频特效和视频特效的处理
  5. AVDevice:输入输出设备库,音/视频的输入输出需要确保该模块已经打开
  6. SwrRessample:该模块可用于音频重采样,可以对数字音频进行声道数、数据格式、采样率等多种基本信息的转换。
  7. SWScale:该模块是将图像进行格式转换的模块,比如,可以将YUV的数据转换为RGB的数据,缩放尺寸由1280x720变为800x480。
  8. PostProc:该模块可用于进行后期处理,当我们使用AVFilter的时 候需要打开该模块的开关,因为Filter中会使用到该模块的一些基 础函数。

二、常用命令

1. ffplay命令

1.1 播放控制

1.2 参数选项

ffplay所有参数

主要选项

◼ -x width:强制显示宽度
◼ -y height:强制显示高度
◼ -video_size size: 设置显示帧存储WxH格式,仅用于YUV等未包含尺寸的视频
◼ -pixel_format format:设置像素格式。
◼ -fs:以全屏模式启动。
◼ -an/vn/sn:禁用音频/视频/字幕
◼ -ss pos:设置的时间进行拖动,时间单位:‘12:03:45’ 12h 03m 45s, ‘23.189’ 23.189s
◼ -t duration: 设置播放视频/音频长度,时间单位如 -ss选项

◼ -t bytes:按照字节进行定位拖动(0=off 1=on -1=auto)
◼ -seek_interval interval: 自定义左/右定位拖动间隔,默认10s
◼ -nodisp:关闭图形化显示窗口,视频将不显示
◼ -noborder:无边框窗口
◼ -volume vol:设置起始音量,范围0~100
◼ -f fmt:强制使用设置的格式进行解析。如-f s16le
◼ -window_title title: 设置窗口标题,默认文件名
◼ -loop number:始值播放循环次数
◼ -showmode mode:设置显示模式,可用 的模式值:0显示视频(默认),1显示音频波形,2显示音频频谱
◼ -vf filtergraph:设置视频滤镜
◼ -af filtergraph:设置音频滤镜

高级选项

◼ -stats:打印多个回放统计信息,包括显示流持续时间,编解码器参数, 流中的当前位置,以及音频/视频同步差值。默认启用状,禁用-nostats
◼ -fast: 非标准化规范的多媒体兼容优化。
◼ -genpts:生成pts。
◼ -sync type同步类型:将主时钟设置为audio(默认)/video/external, 如type=ext
◼ -ast audio_stream_specifier:指定音频流索引, 如-ast 3 播放流索引为3的音频流
◼ -vst video_stream_specifier:指定视频流索引, 如-vst 4播放流索引为4的视频流
◼ -sst subtitle_stream_specifier:指定字幕流索引, 如-sst 5播放流索引5的字幕流
◼ -autoexit 视频播放完毕后退出。

◼ -exitonkeydown 键盘按下任何键退出播放
◼ -exitonmousedown 鼠标按下任何键退出播放
◼ -codec:media_specifier codec_name 强制使用设置的多媒体解码器, media_specifier可用值为a(音频), v(视频)和s字幕。比如- codec:v h264_qsv 强制视频采用h264_qsv解码
◼ -acodec codec_name 强制使用设置的音频解码器进行音频解码
◼ -vcodec codec_name 强制使用设置的视频解码器进行视频解码
◼ -scodec codec_name 强制使用设置的字幕解码器进行字幕解码
◼ -autorotate 根据文件元数据自动旋转视频。值为0或1 ,默认为1。
◼ -framedrop 如果视频不同步则丢弃视频帧。当主时钟非视频时钟时 默认开启。若需禁用则使用 -noframedrop
◼ -infbuf 不限制输入缓冲区大小。尽可能快地从输入中读取尽可能多的数据。
播放实时流时默认启用,如果未及时读取数据,则可能会丢弃数据。此选项
将不限制缓冲区的大小。若需禁用则使用-noinfbuf

1.3 播放命令

  1. 播放YUV数据
ffplay -pix_format yuv420p -video_size 320x240 -framerate 5 -i yuv420p_320x240.yuv
  1. 播放RBG数据
ffplay -pix_format rtb24 -video_size 320x240 -framerate 5 -i rgb24_320x240.rgb
ffplay -pix_format rtb24 -video_size 320x240 -i rgb24_320x240.rgb
  1. 播放PCM数据
ffplay -ar 48000 -ac 2 -f32le 48000_2_f32l2.pcm

1.4 简单过滤器

过滤器所有命令

# 视频旋转
ffplay -i test.mp4 -vf transpose=1
# 视频反转
ffplay test.mp4 -vf hflip
ffplay test.mp4 -vf vflip
# 视频旋转和反转
ffplay test.mp4 -vf hflip,transpose=1
# 音频变速播放
ffplay -i test.mp4 -af atempo=2
# 视频变速播放
ffplay -i test.mp4 -vf setpts=PTS/2
# 音视频同时变速
ffplay -i test.mp4 -vf setpts=PTS/2 -af atempo=2

2. ffmpeg命令

2.1 基本信息查询命令


查看具体分类所支持的参数:

ffmpeg -h type=name如:
ffmpeg -h echoder=libx264

2.2 提取音视频

  1. 提取音频
#保留编码格式
ffmpeg -i test.mp4 -acodec copy -vn audio.mp4   #保留封装格式
ffmpeg -i test.mp4 -acodec copy -vn test.aac    #保留编码格式
ffmpeg -i test.mp4 -acodec libmp3lame -vn test.mp3  #强制格式
#提取原始数据
ffmpeg -i test.mp3 -ar 44100 -ac 2 -f s16le test.pcm
ffmpeg -i test.mp3 -ar 44100 -ac 2 -sample_fmt s16 test.wav     #采样格式
ffmpeg -i test.mp3 -ar 44100 -ac 2 -codec:a pacm_s16le  test.wav#编码格式
  1. 提取视频
#保留编码格式
ffmpeg -i test.mp4 -acodec copy -aan audio.mp4  #保留封装格式
ffmpeg -i test.mp4 -vcodec copy -an test.h264   #保留编码h264
ffmpeg i test.mp4 -vcodec libx264 -an test.h264 #强制格式
#提取原始数据
ffmpeg -i test.mp4 -t 3 -pix_fmt yuv420p -s 320x240 test.yuv    #提取3s 320x240的yuv420p数据
ffmpeg -i test.mp3 -t 3 -pix_fmt rgb24 -s 320x240 test.rgb      #提取rgb数据
ffmpeg -s 320x240 -pix_fmt yuv420p -i test.yuv -pix_fmt rgb24 test.rgb  #yuv转rgb数据

2.3 格式转封装

#保持编码格式
ffmpeg -i test.mp4 -vcodec copy -acodec copy test.ts
ffmpeg -i test.mp4 -codec copy test.ts#改变编码格式
ffmpeg -i test.mp4 =vcodec libx265 -acodec libmp3lame test.mkv#修改帧率
ffmpeg -i test.mp4 -r 15 out.mp4#修改码率
ffmpeg -i test.mp4 -b 400k test.mkv #音视频同时被重新编码
Ffmpeg -i test.mp4 -b:v 400k -b:a 192k test.mp4ffmpeg -i test.mp4 -b:v 400k -acodec copy test.mkv   #视频重新编码
ffmpeg -i test.mp4 -b:a 192k -vcodec copy test.mp4  #音频重新编码#修改视频分辨率
ffmpeg -i test.mp4 -s 320x240 test.mp4#修改音频采样率
ffmpeg -i test.mp4 -ar 48000 test.mp4

2.4 裁剪/合并

  1. 裁剪视频
ffmpeg -i test.mp4 -ss 00:00:10 -t 10 -codec copy 1.mp4
ffmpeg -i test.mp4 -ss 00:00:10 =t 20 -vcodec libx264 -acodec aac 2.mp4
  1. 合并视频
#mp4List.txt文件
file 'test_ss1.mp4'
file 'test_ss2.mp4'
ffmpeg -f concat -i mp4List.txt -codec copy test.mp4
ffmpeg -i "concat:1.ts|2.ts|3.ts" -codec copy out_ts.mp4 #仅限于ts等部分封装格式

注意:合并的视频分辨率可以不同,编码格式必须统一,音频参数(ar/ac/sample_fmt)和编码格式都必须统一。

2.5 图片/视频转换

  1. 视频转图片
#视频转jpeg
ffmpeg -i test.mp4 -f image2 image_%3d.jpeg             #每帧自己的参数
ffmpeg -i test.mp4 -t 5 -s 640x360 -r 15 image%03d.jpeg #指定转换的参数
#视频转gif
ffmpeg -i test.mp4 -t 5 -r 1 image1.gif
ffmpeg -i test.mp4 -t 5 -r 25 -s 640x360 image2.gif
  1. 图片转视频
ffmpeg -i image_%3d.jpeg test_img.mp4
ffmpeg -f gif -i image2.gif image2.mp4

2.6 录制音视频

  1. 安装 Screen Capturer Recorder
  2. ffmpeg -list_devices true -f dshow -i dummy 查看可用设备的名字
[dshow @ 03221440] DirectShow video devices (some may be both video and audio devices)
[dshow @ 03221440]  "Integrated Camera"                      ##笔记本摄像头
[dshow @ 03221440]     Alternative name "@device_pnp_\\?\usb#vid_04f2&pid_b61e&mi_00#6&10400b6&0&0000#{65e8773d-8f56-11d0-a3b9-00a0c9223196}\global"
[dshow @ 03221440]  "screen-capture-recorder"                ##录屏
[dshow @ 03221440]     Alternative name "@device_sw_{860BB310-5D01-11D0-BD3B-00A0C911CE86}\{4EA6930A-2C8A-4AE6-A561-56E4B5044439}"
[dshow @ 03221440] DirectShow audio devices
[dshow @ 03221440]  "麦克风阵列 (Realtek(R) Audio)"           ##麦克风
[dshow @ 03221440]     Alternative name "@device_cm_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\wave_{D3AF43CB-ECC4-410E-925D-E850F1C96C66}"
[dshow @ 03221440]  "virtual-audio-capturer"             ##本机声音
[dshow @ 03221440]     Alternative name "@device_sw_{33D9A762-90C8-11D0-BD43-00A0C911CE86}\{8E14549B-DB61-4309-AFA1-3578E927E935}"

默认参数

#录制视频
ffmpeg -f dshow -i video="screen-capture-recorder" v-out.mp4     #录制屏幕
ffmpeg -f dshow -i video="Integrated Camera" -y v-out2.flv           #摄像头#录制音频
ffmpeg -f dshow -i audio="virtual-audio-capturer" a-out.aac          #系统声音
ffmpeg -f dshow -i audio="virtual-audio-capturer" -f dshow -i audio="麦克风阵列 (Realtek(R) Audio)" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 a-out2.aac           #系统+麦克风声音#同时录制视频和音频
ffmpeg -f dshow -i audio="麦克风 (Realtek Audio)" -f dshow -i audio="virtual-
audio-capturer" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f
dshow -i video="screen-capture-recorder" -y av-out.flv

可选参数

  1. 查看录制视频 可选参数
    ffmpeg -f dshow -list_options true -i video="screen-capture-recorder"
[dshow @ 032d1440] DirectShow video device options (from video devices)
[dshow @ 032d1440]  Pin "Capture" (alternative pin name "1")
[dshow @ 032d1440]   pixel_format=bgr0  min s=1x1 fps=0.02 max s=1280x720 fps=30
[dshow @ 032d1440]   pixel_format=bgr0  min s=1x1 fps=0.02 max s=1280x720 fps=30
[dshow @ 032d1440]   pixel_format=bgr24  min s=1x1 fps=0.02 max s=1280x720 fps=30
[dshow @ 032d1440]   pixel_format=rgb555le  min s=1x1 fps=0.02 max s=1280x720 fps=30
[dshow @ 032d1440]   pixel_format=rgb555le  min s=1x1 fps=0.02 max s=1280x720 fps=30
[dshow @ 032d1440]   pixel_format=rgb8  min s=1x1 fps=0.02 max s=1280x720 fps=30
[dshow @ 032d1440]   pixel_format=yuv420p  min s=1x1 fps=0.02 max s=1280x720 fps=30
  1. 查看录制音频 可选参数
    ffmpeg -f dshow -list_options true -i audio="virtual-audio-capturer“
[dshow @ 01471440] DirectShow audio only device options (from audio devices)
[dshow @ 01471440]  Pin "Capture Virtual Audio Pin" (alternative pin name "1")
[dshow @ 01471440]   min ch=2 bits=16 rate= 48000 max ch=2 bits=16 rate= 48000

录制音视频


#指定参数录制
ffmpeg -f dshow -i audio="麦克风阵列 (Realtek(R) Audio)" -f dshow -i audio="virtual-audio-capturer" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f dshow -video_size 1920x1080 -framerate 15 -pixel_format yuv420p -i video="screen-capture- recorder" -vcodec h264_qsv -b:v 3M -y av-out.flvffmpeg -f dshow -i audio="麦克风阵列 (Realtek(R) Audio)" -f dshow -i audio="virtual-audio-capturer" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f dshow -i
video="screen-capture-recorder" -vcodec h264_qsv -b:v 3M -r 15 -y av- out2.mp4ffmpeg -f dshow -i audio="麦克风阵列 (Realtek(R) Audio)" -f dshow -i audio="virtual-audio-capturer" -filter_complex amix=inputs=2:duration=first:dropout_transition=2 -f dshow -framerate 15 -pixel_format yuv420p -i video="screen-capture-recorder" -vcodec h264_qsv -b:v 3M -r 15 -y av-out3.mp4

2.7 直播命令

#拉流
ffmpeg -i rtmp://58.200.131.2:1935/livetv/cctv1 -c copy test_rtmp.mp4
#推流
ffmpeg -re -i test_rtmp.mp4 -c copy -f flv rtmp//:localhost/live/room

2.8 过滤器

  1. 裁剪
ffmpeg -i test.mp4 -vf crop=iw/3:ih:0:0 out.mp4     #视频裁剪宽度原来的1/3
ffmpeg -i test.mp4 -vf crop=iw/3:ih:iw/3:0 out.mp4 #从1/3处裁剪宽度原来1/3
ffplay -i test.mp4 -vf crop=iw/3:ih:iw/3:0         #预览裁剪的结果
  1. 水印
  2. 画中画
  3. 多宫格

三、FFmpeg初级使用

1. ffmpeg数据结构

AVFormatContext:封装格式上下文,保存了音视频文件封装格式相关信息。

struct AVFormatContext {iformat:      输入媒体的AVInputFormatnb_streams:    输入媒体的AVStream 个数streams:     输入媒体的AVStream []数组duration:      输入媒体的时长(以微秒为单位),计算方式可以参考 av_dump_format()函数。bit_rate:       输入媒体的码率
};

AVInputFormat / AVOutoutFormat:每种封装格式(MP4/FLV等)对应一个该结构体

struct AVInputFormat {name:           封装格式名称extensions:    封装格式的扩展名id:          封装格式ID一些封装格式处理的接口函数,比如read_packet()
}

AVStream:视频文件中每个音/视频流对应一个该结构体

struct AVStream {index:               标识该视频/音频流time_base:          该流的时基,PTS*time_base=真正的时间(秒)avg_frame_rate:    该流的帧率duration:           该视频/音频流长度codecpar:           编解码器参数属性
}

AVCodecContext:编解码器上下文结构体,保存了音视频编解码相关信息

struct AVCodecContext {codec:         编解码器的AVCodecwidth, height:   图像的宽高(只针对视频)pix_fmt:       像素格式(只针对视频)sample_rate:    采样率(只针对音频)channels:        声道数(只针对音频)sample_fmt:  采样格式(只针对音频)
}

AVCodec:每种音视频编解码器(如h264)对应一个该结构体

struct AVCodec {name: 编解码器名称type:  编解码器类型id:    编解码器ID 一些编解码的接口函数,比如int (*decode)()
}

AVPacket:存储一阵压缩数据

struct AVPakcet {pts:         显示时间戳dts:            解码时间戳data:           压缩编码数据size:          压缩编码数据大小pos:            数据的偏移地址stream_index: 所属的AVStream
}

AVFrame:存储一阵解码后的像素/采样数据

struct AVFrame {data:         解码后的图像像素数据(音频采样数据)linesize:        对视频来说是图像中一行像素的大小;对音频来说是整个音频帧的大小width, height: 图像的宽高(只针对视频)key_frame:     是否为关键帧(只针对视频) 。pict_type:      帧类型(只针对视频) 。例如I/P/Bsample_rate:    音频采样率(只针对音频)nb_samples:    音频每通道采样数(只针对音频)pts:            显示时间戳
}

1.1 ffmpeg内存模型

从现有的packet拷贝到另一个packet的时候,有两种情况:

  1. 两个Packet的buf引用的是同一数据缓存空间,这时候要注意数据缓存空间的释放问题;
  2. 两个Packet的buf引用不同的数据缓存空间,每个Packet都有数据缓存空间的copy;

实际上AVPacket中采用的是第一种内存模型。

对于多个AVPacket / AVFrame共享同一个缓存空间,ffmpeg使用的引用计数的机制:

  1. 初始化引用计数为0,只有真正分配AVBuffer的时候,引用计数初始化为1;
  2. 当有新的Packet引用共享的缓存空间时,就将引用计数+1
  3. 当释放了引用共享空间的Packet,就将引用计数-1;引用计数为0时,就释放掉引用的缓存空间AVBuffer。

1.2 AVPacket和AVFrame

AVPacket API

//分配 / 释放AVPacket
AVPacket *av_packet_alloc(void);        //内部不会分配buf空间
void av_packet_free(AVPacket **pkt);    //内部会调用av_packet_unref(); //初始化AVPacket
void av_init_packet(AVPacket *pkt);     //会初始化pkt内部的buf_ref指向NULL,误用会导致内存泄漏//new一个packet并分配AVPacket中的buf内存,引用计数初始化为1
int av_new_packet(AVPacket *pkt, int size);//增加 / 减少 / 转移引用计数
int av_packet_ref(AVPacket *dst, const AVPacket *src);  //增加引用计数
void av_packet_unref(AVPacket *pkt);                    //减少引用计数,调用unref函数次数必须>=ref函数次数,否则会导致内存泄漏
void av_packet_move_ref(AVPacket *dst, AVPacket *src);  //转移引用计数,内部调用了av_init_packet()//复制引用计数av_packet_alloc()+av_packet_ref()
AVPacket *av_packet_clone(const AVPacket *src);     //内部调用了av_packet_alloc()+av_packet_ref()

AVFrame API

//分配 / 释放AVFrame
AVFrame *av_frame_alloc(void); / void av_frame_free(AVFrame **frame);// 增加 / 减少 / 转移引用计数
int av_frame_ref(AVFrame *dst, const AVFrame *src);     //增加引用计数
void av_frame_unref(AVFrame *frame);                    //减少引用计数
void av_frame_move_ref(AVFrame *dst, AVFrame *src);     //转移引用计数//根据AVFrame分配内存
int av_frame_get_buffer(AVFrame *frame, int align);//等于av_frame_alloc()+av_frame_r
AVFrame *av_frame_clone(const AVFrame *src);  ef()

2. 音视频处理流程



复用器/解复用器:视频/音频/字幕流等按照以顶的规则组合/拆分而成得到mp4/flv等视频文件,复用器如mp4/flv
编解码器:视频:YUV图像<——>H264帧,音频:PCM音频<——>AAC帧

2.1 初始化相关

//注册设备
avdevice_register_all();//初始化网络库以及网络加密协议相关 的库(比如openssl)
avformat_network_init();

2.2 格式封装相关

//申请一个AVFormatContext并初始化 / 释放AVFormatContext
avformat_alloc_context(); / avformat_Free_context();//打开输入音视频文件 / 关闭输入音视频文件
avformat_open_input(); / avformat_close_input();//获取音视频文件信息
avformat_find_stream_info()//读取音视频包
avformat_read_frame();定位文件
avformat_seek_file();
av_seek_frame();

2.3 编解码器相关

//分配 / 释放解码器上下文
avcodec_alloc_context3(); / avcodec_free_context();//根据ID / 名字查找解码器
avcodec_find_decoder(); / avcodec_find_decoder_by_name();//打开编解码器 / 关闭解码器
avcodec_open2(); /avcodec_close();//发送编码数据包
avcodec_send_packet();//接受编码后数据
avcodec_receive_frame();

根据ID / 名字查找编码器的区别:ffmpeg内部编码器是交互存储,同种编码方式有多种编码器,不同编码器之间编码方式ID是相同的,但编码器名字不同。通过ID会默认找到第一个编码器,通过名字查找则指定编码器。

2.3 文件操作


void ffmpeg_test() {av_log_set_level(AV_LOG_DEBUG);AVIODirContext *dir_ctx = NULL;AVIODirEntry *next = NULL;//打开目录avio_open_dir(&dir_ctx, "/Users/mac/AVTest", NULL);//获取目录信息int count = 0;while (avio_read_dir(dir_ctx, &next)) {       //打印目录信息if(!next) {break;}av_log(NULL, AV_LOG_DEBUG, "%s    %lld\n", next->name, next->size);}//删除文件avpriv_io_delete("/Users/mac/AVTest/test.flv");//重命名文件avpriv_io_move("/Users/mac/AVTest/test.pcm", "/Users/mac/AVTest/tt.pcm");
}

2.4 抽取音频

  1. 相关API
//输出文件的流信息
void av_dump_format(AVFormatContext *ic,int index,const char *url,int is_output);
//找到最合适的流数据
int av_find_best_stream(AVFormatContext *ic,enum AVMediaType type,int wanted_stream_nb,int related_stream,AVCodec **decoder_ret,int flags);
  1. 实例
void cut_audio() {AVFormatContext *fmt_ctx = NULL;const char *src_path = "/Users/mac/AVTest/test.mp4";const char *dst_path = "/Users/mac/AVTest/test.aac";FILE *dst_fd = NULL;AVPacket *packet NULL;int ret = -1;/*输出文件的信息*/avdevice_register_all();//打开音频文件avformat_open_input(&fmt_ctx, src_path, NULL, NULL);av_dump_format(fmt_ctx, 0, src_path, 0);//选择最合适的流ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);//读取数据流dst_fd = fopen(dst_path, "wb+");packet = av_packet_alloc();av_init_packet(packet);while (0 == av_read_frame(fmt_ctx, packet)) {//转存入文件char szAdtsHeader[ADTS_HEADER_LEN] = {0};adts_header(szAdtsHeader, packet->size);fwrite(szAdtsHeader, 1, ADTS_HEADER_LEN, dst_fd);fwrite(packet->data, 1, packet->size, dst_fd);}fclose(dst_fd);avformat_close_input(&fmt_ctx);av_packet_free(&packet);
}

2.5 抽取视频

#include <stdio.h>
#include <libavutil/log.h>
#include <libavformat/avio.h>
#include <libavformat/avformat.h>#ifndef AV_WB32
#   define AV_WB32(p, val) do {                 \uint32_t d = (val);                     \((uint8_t*)(p))[3] = (d);               \((uint8_t*)(p))[2] = (d)>>8;            \((uint8_t*)(p))[1] = (d)>>16;           \((uint8_t*)(p))[0] = (d)>>24;           \} while(0)
#endif#ifndef AV_RB16
#   define AV_RB16(x)                           \((((const uint8_t*)(x))[0] << 8) |          \((const uint8_t*)(x))[1])
#endif//根据NALU_type不同添加不同的startcode
static int alloc_and_copy(AVPacket *out,const uint8_t *sps_pps, uint32_t sps_pps_size,const uint8_t *in, uint32_t in_size)
{//判断startcode的字节数:若out中有数据,则说明当前不是第一帧,即非sps/pps/IDR帧;若size为0,则说明当前为第一帧,startcode为4byteuint32_t offset         = out->size;uint8_t nal_header_size = offset ? 3 : 4;int err;//扩充out的空间:NALU_size + SPS_size + startcode_size//alloc_and_copy(out, spspps_pkt->data, spspps_pkt->size, buf, nal_size);err = av_grow_packet(out, sps_pps_size + in_size + nal_header_size);//若是sps/ppsif (sps_pps) {//拷贝 sps/pps的data到out_datamemcpy(out->data + offset, sps_pps, sps_pps_size);}//拷贝 packet中的size追加到out中sps/pps数据及预留的startcode位置的后面memcpy(out->data + sps_pps_size + nal_header_size + offset, in, in_size);//设置00000001if (!offset) {AV_WB32(out->data + sps_pps_size, 1);} else {    //设置000001(out->data + offset + sps_pps_size)[0] =(out->data + offset + sps_pps_size)[1] = 0;(out->data + offset + sps_pps_size)[2] = 1;}return 0;
}//从codec中获取sps/pps数据
//h264_extradata_to_annexb(fmt_ctx->stream[in->stream_index]->codec->extradata, in->stream_index]->codec->extradata——size, &spspps_pkt, AV_INPUT_BUFFER_PADDING_SIZE);
int h264_extradata_to_annexb(const uint8_t *codec_extradata, const int codec_extradata_size, AVPacket *out_extradata, int padding)
{uint16_t unit_size;uint64_t total_size                 = 0;uint8_t *out                        = NULL, unit_nb, sps_done = 0,sps_seen                   = 0, pps_seen = 0, sps_offset = 0, pps_offset = 0;//跳过无用的前4byteconst uint8_t *extradata            = codec_extradata + 4;static const uint8_t nalu_header[4] = { 0, 0, 0, 1 };//获取第一个字节的后2bit:sps/pps所占用的空间int length_size = (*extradata++ & 0x3) + 1;sps_offset = pps_offset = -1;//获取第二个字节的后5bit:sps/pps的个数,一般一个sps/ppsunit_nb = *extradata++ & 0x1f;if (!unit_nb) {goto pps;}else {sps_offset = 0;sps_seen = 1;}//遍历每一个sps/ppswhile (unit_nb--) {int err;//获取sps/pps的长度unit_size   = AV_RB16(extradata);//添加startcode的长度total_size += unit_size + 4;//对空间进行扩展if ((err = av_reallocp(&out, total_size + padding)) < 0)return err;//拷贝startcodememcpy(out + total_size - unit_size - 4, nalu_header, 4);//拷贝sps/pps数据memcpy(out + total_size - unit_size, extradata + 2, unit_size);extradata += 2 + unit_size;
pps:if (!unit_nb && !sps_done++) {unit_nb = *extradata++; /* number of pps unit(s) */if (unit_nb) {pps_offset = total_size;pps_seen = 1;}}}if (out)memset(out + total_size, 0, padding);//返回获取的数据out_extradata->data      = out;out_extradata->size      = total_size;return length_size;
}int h264_mp4toannexb(AVFormatContext *fmt_ctx, AVPacket *in, FILE *dst_fd)
{AVPacket *out = NULL;AVPacket spspps_pkt;int len;uint8_t unit_type;int32_t nal_size;uint32_t cumul_size    = 0;const uint8_t *buf;const uint8_t *buf_end;int            buf_size;int ret = 0, i;out = av_packet_alloc();buf      = in->data;buf_size = in->size;buf_end  = in->data + in->size;do {//获取data的前4个byte并转为大端,代表NALU的大小for (nal_size = 0, i = 0; i<4; i++)nal_size = (nal_size << 8) | buf[i];buf += 4; //跳过NALU_size//第一个字节为NALU_header:后5bit代表NALU_typeunit_type = *buf & 0x1f;//若当前帧是关键帧if (unit_type == 5)//获取sps/pps的数据h264_extradata_to_annexb(fmt_ctx->stream[in->stream_index]->codec->extradata, &spspps_pkt, AV_INPUT_BUFFER_PADDING_SIZE);//添加startcodealloc_and_copy(out, spspps_pkt->data, spspps_pkt->size, buf, nal_size);} else {//直接添加startcodealloc_and_copy(out, NULL, 0, buf, nal_size);}//将annexb格式的NALU写到输出文件fwrite( out->data, 1, out->size, dst_fd);fflush(dst_fd);//获取当前从pakcet中读出了多少数据数据:NALU+NALU_sizebuf        += nal_size;cumul_size += nal_size + 4;//若没有读完,说明不只存了一帧} while (cumul_size < buf_size);return ret;
}int main()
{int video_stream_index = -1;AVFormatContext *fmt_ctx = NULL;AVPacket pkt;/*register all formats and codec*/av_register_all();FILE *dst_fd = fopen(dst_filename, "test.mp4");/*open input media file, and allocate format context*/avformat_open_input(&fmt_ctx, src_filename, NULL, NULL);/*dump input information*/av_dump_format(fmt_ctx, 0, src_filename, 0);/*initialize packet*/av_init_packet(&pkt);pkt.data = NULL;pkt.size = 0;/*find best video stream*/video_stream_index = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);/*read frames from media file*/while(av_read_frame(fmt_ctx, &pkt) >=0 ){if(pkt.stream_index == video_stream_index){h264_mp4toannexb(fmt_ctx, &pkt, dst_fd);}//release pkt->dataav_packet_unref(&pkt);}/*close input media file*/avformat_close_input(&fmt_ctx);if(dst_fd) {fclose(dst_fd);}return 0;
}

2.6 格式转换

  1. API
//打开/释放输出文件
avformat_alloc_output_context2();
avformat_free_context();
//创建新的流数据
avformat_new_stream();
//复制流数据的编解码参数
avcodec_parameters_copy();//写入多媒体文件头
avformat_write_header();
//写入流数据
av_write_frame();av_interleaved_write_fream();
//写入流数据尾部
av_write_trailer();
  1. 实例
/*将mp4转成flv格式*/#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>int main(int argc, char **argv)
{AVOutputFormat *ofmt = NULL;  // 输出格式AVFormatContext *ifmt_ctx = NULL, *ofmt_ctx = NULL; // 输入、输出是上下文环境AVPacket pkt;const char *in_filename, *out_filename;int ret, i;int stream_index = 0;int *stream_mapping = NULL; // 数组用于存放输出文件流的Indexint stream_mapping_size = 0; // 输入文件中流的总数量if (argc < 3) {printf("usage: %s input output\n""API example program to remux a media file with libavformat and libavcodec.\n""The output format is guessed according to the file extension.\n""\n", argv[0]);return 1;}in_filename  = argv[1];out_filename = argv[2];// 打开输入文件为ifmt_ctx分配内存if ((ret = avformat_open_input(&ifmt_ctx, in_filename, 0, 0)) < 0) {fprintf(stderr, "Could not open input file '%s'", in_filename);goto end;}// 检索输入文件的流信息if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0) {fprintf(stderr, "Failed to retrieve input stream information");goto end;}// 打印输入文件相关信息av_dump_format(ifmt_ctx, 0, in_filename, 0);// 为输出上下文环境分配内存avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, out_filename);if (!ofmt_ctx) {fprintf(stderr, "Could not create output context\n");ret = AVERROR_UNKNOWN;goto end;}// 输入文件流的数量stream_mapping_size = ifmt_ctx->nb_streams;// 分配stream_mapping_size段内存,每段内存大小是sizeof(*stream_mapping)stream_mapping = av_mallocz_array(stream_mapping_size, sizeof(*stream_mapping));if (!stream_mapping) {ret = AVERROR(ENOMEM);goto end;}// 输出文件格式ofmt = ofmt_ctx->oformat;// 遍历输入文件中的每一路流,对于每一路流都要创建一个新的流进行输出for (i = 0; i < ifmt_ctx->nb_streams; i++) {AVStream *out_stream; // 输出流AVStream *in_stream = ifmt_ctx->streams[i]; // 输入流AVCodecParameters *in_codecpar = in_stream->codecpar; // 输入流的编解码参数// 只保留音频、视频、字幕流,其他的流不需要if (in_codecpar->codec_type != AVMEDIA_TYPE_AUDIO &&in_codecpar->codec_type != AVMEDIA_TYPE_VIDEO &&in_codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {stream_mapping[i] = -1;continue;}// 对于输出的流的index重写编号stream_mapping[i] = stream_index++;// 创建一个对应的输出流out_stream = avformat_new_stream(ofmt_ctx, NULL);if (!out_stream) {fprintf(stderr, "Failed allocating output stream\n");ret = AVERROR_UNKNOWN;goto end;}// 直接将输入流的编解码参数拷贝到输出流中ret = avcodec_parameters_copy(out_stream->codecpar, in_codecpar);if (ret < 0) {fprintf(stderr, "Failed to copy codec parameters\n");goto end;}out_stream->codecpar->codec_tag = 0;}// 打印要输出的多媒体文件的详细信息av_dump_format(ofmt_ctx, 0, out_filename, 1);if (!(ofmt->flags & AVFMT_NOFILE)) {ret = avio_open(&ofmt_ctx->pb, out_filename, AVIO_FLAG_WRITE);if (ret < 0) {fprintf(stderr, "Could not open output file '%s'", out_filename);goto end;}}// 写入新的多媒体文件的头ret = avformat_write_header(ofmt_ctx, NULL);if (ret < 0) {fprintf(stderr, "Error occurred when opening output file\n");goto end;}while (1) {AVStream *in_stream, *out_stream;// 循环读取每一帧数据ret = av_read_frame(ifmt_ctx, &pkt);if (ret < 0) // 读取完后退出循环break;in_stream  = ifmt_ctx->streams[pkt.stream_index];if (pkt.stream_index >= stream_mapping_size ||stream_mapping[pkt.stream_index] < 0) {av_packet_unref(&pkt);continue;}pkt.stream_index = stream_mapping[pkt.stream_index]; // 按照输出流的index给pkt重新编号out_stream = ofmt_ctx->streams[pkt.stream_index]; // 根据pkt的stream_index获取对应的输出流// 对pts、dts、duration进行时间基转换,不同格式时间基都不一样,不转换会导致音视频同步问题pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX);pkt.duration = av_rescale_q(pkt.duration, in_stream->time_base, out_stream->time_base);pkt.pos = -1;// 将处理好的pkt写入输出文件ret = av_interleaved_write_frame(ofmt_ctx, &pkt);if (ret < 0) {fprintf(stderr, "Error muxing packet\n");break;}av_packet_unref(&pkt);}// 写入新的多媒体文件尾av_write_trailer(ofmt_ctx);
end:avformat_close_input(&ifmt_ctx);/* close output */if (ofmt_ctx && !(ofmt->flags & AVFMT_NOFILE))avio_closep(&ofmt_ctx->pb);avformat_free_context(ofmt_ctx);av_freep(&stream_mapping);if (ret < 0 && ret != AVERROR_EOF) {fprintf(stderr, "Error occurred: %s\n", av_err2str(ret));return 1;}return 0;

四、ffmpeg处理常见问题

1. 像素尺寸有问题

1.视频画面跳来跳去且画面渲染的不完整

2. 画面渲染的不完整

2. 像素格式有问题

  1. 颜色偏红/绿/蓝,应该是RGB分量排序问题,RGB格式不对
  2. 画面乱闪完全看不清楚,应该是像素格式问题,是不是YUV错选成RGB了

一、FFmpeg简介相关推荐

  1. FFmpeg简介(一)

    FFmpeg简介 ffmpeg是一个非常快的视频/音频转换器,其也可以现场抓取音频/视频源,并在任意采样率.尺寸之间调整视频,以及提供多种高品质的滤镜系统. FFmpeg涉及的基本概念 分辨率: 分辨 ...

  2. 【网络通信 -- 直播】FFMPEG 简介与常用命令总结

    [网络通信 -- 直播]FFMPEG 简介与常用命令总结 [1]FFMPEG 简介 FFmpeg,项目的名称来自 MPEG 视频编码标准,"FF"代表"Fast Forw ...

  3. FFmpeg简介、下载与安装以及'ffmpeg' 不是内部或外部命令,也不是可运行的程序 或批处理文件**(Win10)

    一.FFmpeg简介 FFmpeg是一套可以用来记录.转换数字音频.视频,并能将其转化为流的开源计算机程序.采用LGPL或GPL许可证.它提供了录制.转换以及流化音视频的完整解决方案.它包含了非常先进 ...

  4. 1 FFmpeg从入门到精通-FFmpeg简介

    1 FFmpeg从入门到精通-FFmpeg简介 2 FFmpeg从入门到精通-FFmpeg工具使用基础 3 FFmpeg从入门到精通-FFmpeg转封装 4 FFmpeg从入门到精通-FFmpeg转码 ...

  5. FFmpeg入门详解之14:FFmpeg简介

    FFmpeg简介 ffmpeg是一个跨平台的音视频处理库,为了跨平台那么就需要编译出适用于各个平台的ffmpeg库. 一般在windows平台下会进行windows开发和安卓开发,所以本文的目的就是在 ...

  6. 【FFmpeg学习笔记一】FFmpeg简介

    Fmpeg简介 一.FFmpeg简介 二.FFmpeg函数库 三.FFmpeg用例 四.相关链接 一.FFmpeg简介 FFmpeg是一款自由软件,核心功能就是音视频编解码,其中的"FF&q ...

  7. FFmpeg简介及在vc2010下编译步骤

    FFmpeg是一个开源的多媒体库,最新版本是2.4.3,它的License是LGPL或GPL.FFmpeg可以用来记录.转换数字音频.视频,并能将其转换为流的开源计算机程序.它包括了音/视频编码库li ...

  8. duilib vs2015 安装_FFmpeg视频播放器开发-FFmpeg简介与项目环境搭建(一)

    前言:视频开发库有很多,例如微软的DirectShow:开源库OpenCV,当然OpenCV主要是图像处理,视频部分还是用的ffmpeg, 而且无法解码音频:SDL:大华和海康都有自己的库等等.音视频 ...

  9. FFmpeg简介,下载

    代码: FFmpeg制作解码器_asiwxy的博客-CSDN博客 1. 概述 FFmpeg 全名是Fast Forward MPEG(Moving Picture Experts Group),是一个 ...

最新文章

  1. 如何运维能让网站稳定高效--稳定篇
  2. java怎么获取文本里的值_怎么获取到text中的文本,或者title中的值
  3. 一起学设计模式-策略模式
  4. 计算机辅助药物设计局限,计算机辅助药物设计高效低耗
  5. 【LeetCode】405 Convert a Number to Hexadecimal (java实现)
  6. 格拉布斯(Grubbs)准则法
  7. mac端本地项目上传到码云
  8. excel高级筛选怎么用_神!Excel高级筛选原来如此好用
  9. 17.Unity3D商业游戏源码研究-变身吧主公-战斗-StateBase,PlayerBase
  10. 页面底部悬浮广告或关注公众号代码
  11. 保姆级教程:群晖nas内网穿透之ipv6/ipv4 ddns+frp双管齐下!
  12. 《孩子,为你自己读书》(Yanlz+Unity+SteamVR+云技术+5G+AI+VR云游戏+多多图书馆+志存当高远+读万卷书如行万里路+术业有专攻+读书是对思想的一种升华+立钻哥哥++==)
  13. 本Blog被http://www.dotnetnukeblogs.com/ 收录
  14. c语言中完美立方的程序,完美立方,完全立方公式
  15. Jenkins安装、配置、构建、脚本、配置邮箱、上传fir
  16. 【23考研】计算机择校信息库-浙江高校计算机相关专业22专业目录分类汇总(按专业课分类汇总)
  17. 佳能《万物共生》艺术装置落地侨福芳草地
  18. linux培训_达内linux培训:7招教运维释放Linux操作系统的空间
  19. 在数字化转型共创中,IBM重新找到成长的路径
  20. 20160325系统集成管理工程师(test2)

热门文章

  1. Android开发之锁屏显示Activity
  2. OCR应用案例--商场室内导航
  3. CSDN 昵称和ID修改指引
  4. windows Android Studio连接手机无法识别为安卓设备
  5. 判断圆形三角形面积大小
  6. 创作手机短片技巧总结
  7. 华为鸿蒙国人希望,中兴抢先发布系统,截胡华为鸿蒙系统,配置要求不高,国人:骄傲...
  8. mysql无法启动 1067
  9. Android Studio设置字体大小
  10. 学习LINUX运维决心书