一、前言
我在瑞芯微的RK3588上实现录播功能,也在x86虚拟虚拟机上面实现这个功能其中的逻辑与实现方法基本相同。这里以x86上面的功能做相关的介绍。
时间戳说明:
PTS:是指一个帧应当被展示给用户的时间点,决定了帧在播放时的顺序。
作用:用于同步音视频,确保每一帧在正确的时间显示。
应用场景:在视频播放过程中,PTS帮助播放器确定每一帧应该何时显示。
DTS:是指一个帧应当被解码的时间点,决定了帧的解码顺序。
作用:用于告知解码器何时应该解码某个帧
索引说明:
多媒体文件中具有不同的数据流,比如视频流,音频流,字幕流等。为了区分这些流引入一个概念就是索引。
索引的作用:
1:识别不同类型的数据流
2:同步处理,主要做的工作是将视频帧和音频帧样本对齐,通过流索引,播放器就可以准确的找到相应的视频帧和音频帧,确保按照正确的顺序和时间戳进行解码和播放
3:选择性处理,可以根据需要选择的处理对应的流。
二、ffmpeg安装
ffmpeg版本:4.4
系统:ubuntu系统
1:安装依赖
sudo apt-get install autoconf automake build-essential libass-dev libfdk-aac-dev \
libmp3lame-dev libopus-dev libtool pkg-config texinfo wget yasm
2:拉取源码
wget https://ffmpeg.org/releases/ffmpeg-4.4.tar.bz2
3:解压
tar -xjf ffmpeg-4.4.tar.bz2
cd ffmpeg-4.4
4:配置
./configure --prefix=/usr/local --enable-gpl --enable-libfdk-aac --enable-libmp3lame --enable-nonfree
5:编译与安装
make -j$(nproc)
sudo make install
6:配置环境变量
Vim ~/.bashrc
文件末尾加上内容:
export PATH=$PATH:/usr/local/bin
export LD_LIBRARY_PATH=$LD_LIBRARY_PATH:/usr/local/lib
然后保存并关闭文件,然后执行命令使配置生效。
source ~/.bashrc
7:验证版本
ffmpeg -version
在Ubuntu中有一些是自己带有ffmpeg的其中的版本是ffmpeg版本是4.2.7(我遇到的是这样),然后安装4.4的验证版本之后也是4.2.7,然后调用程序出出现版本不匹配的问题,使用中就需要自己设置连接的库的位置了。
三、主函数设计流程
1:打开输入流
int avformat_open_input(AVFormatContext **ps, const char *url, ff_const59 AVInputFormat *fmt, AVDictionary **options)
函数参数:
AVFormatContext **ps:ps 是指向 AVFormatContext* 的指针,即一个输出参数,用户可以自己通过 avformat_alloc_context() 分配好 AVFormatContext,然后传。
char *url:打开的输入流地址,可以是本地路径(如 "video.mp4"),也可以是网络地址(RTSP流地址)
ff_const59 AVInputFormat *fmt:强制指定输入格式(比如 av_find_input_format("mpegts"));若为 NULL,则 FFmpeg 自动检测输入格式。
AVDictionary **options :参数设置
为了防止数据丢失和确保数据传输的稳定性,需要给到参数配属。
设置其传输模式,超时时间,最大延迟数,pts的生成。
// 设置RTSP选项优化接收数据流防止数据包丢失
av_dict_set(&options, "rtsp_transport", "tcp", 0);
av_dict_set(&options, "stimeout", "5000000", 0); // 设置RTSP超时时间(5秒)
av_dict_set(&options, "max_delay", "500000", 0); // 设置最大延迟(500ms)
av_dict_set(&options, "fflags", "+genpts", 0);// 强制生成PTS(若流中缺少)
2:获取流信息
int avformat_find_stream_info(AVFormatContext *ic, AVDictionary **options);
函数参数:
AVFormatContext *ic:代表媒体文件的句柄
AVDictionary **options:如果非 NULL,则为一个长度为 ic->nb_streams 的数组,每个元素是一个指向字典的指针,这些字典包含与对应流相关的编解码器选项。在返回时,每个字典会被填充未找到的选项。
3:寻找音视频流的索引
一般视频的索引是0,音频的是1
其中查询到的索引标志位编号:
视频:AVMEDIA_TYPE_VIDEO
音频:AVMEDIA_TYPE_AUDIO
判断是否是这个的标志是在上下文中的codec_type
input_ctx->streams[i]->codecpar->codec_type
4:获取视频参数
通过av_guess_frame_rate函数来获取原始视频的的帧率和码率这样就可以实现更好的处理原始数据,而不是规定好固定的码率和帧率。
AVRational av_guess_frame_rate(AVFormatContext *ctx, AVStream *stream, AVFrame *frame);
- 这个函数是猜测视频帧率该函数会尝试从多个来源获取帧率信息:
- 从 AVStream 的 r_frame_rate 字段(推荐帧率)
- 从 AVStream 的 avg_frame_rate 字段(平均帧率)
- 从编解码器上下文(codecpar 或 codec)中提取帧率
- 如果以上都不可用,则尝试根据时间戳(pts/dts)进行估算
音频的参数获取:
AVStream *audio_stream = input_ctx->streams[audio_index];
AVCodecParameters *audio_par = audio_stream->codecpar;
audio_par->sample_rate采样率
audio_par->frame_size帧大小
audio_par->bit_rate码率
5:查询关键帧
(AV_PKT_FLAG_KEY标志)就是关键帧
6:创建输出上下文
int avformat_alloc_output_context2(AVFormatContext **ctx, ff_const59 AVOutputFormat *oformat,const char *format_name, const char *filename);
*ctx: 双重指针,指向将要被创建的 AVFormatContext。如果创建失败,则设置为 NULL。
oformat: 指向 AVOutputFormat 的指针,指定用于分配上下文的输出格式。如果为 NULL,则会尝试使用 format_name 和 filename 来确定格式。
format_name: 输出格式的名字(例如 "mp4" 或 "flv")。如果为 NULL,则尝试从 filename 推断格式。
filename: 文件名,可能包含扩展名,可以帮助推断出输出格式。可以为 NULL。
7:配置输出流
使用avformat_new_stream函数创建输出流
8:复制编解码参数比如分辨率,编码格式等
int avcodec_parameters_copy(AVCodecParameters *dst, const AVCodecParameters *src);