简单流程
-
创建format context(AVFormatContext):
avformat_alloc_context -
打开文件流(路径):
avformat_open_input -
寻找流信息:
avformat_find_stream_info -
获取音视频流的索引值:
formatContext->streams[i]->codecpar->codec_type == (isVideoStream ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO) -
获取音视频流(AVStream):
m_formatContext->streams[m_audioStreamIndex] -
解析音视频数据帧(AVPacket):
av_read_frame -
获取extra data(AVPacket):
av_bitstream_filter_filter
AVFormatContext
简介
在使用FFMPEG进行开发的时候,AVFormatContext是一个贯穿始终的数据结构,很多函数都要用到它作为参数。
常用属性
-
AVIOContext *pb:输入数据的缓存
-
unsigned int nb_streams:视音频流的个数
-
AVStream **streams:视音频流
-
char filename[1024]:文件名
-
int64_t duration:时长(单位:微秒us,转换为秒需要除以1000000)
-
int bit_rate:比特率(单位bps,转换为kbps需要除以1000)
-
AVDictionary *metadata:元数据
用法
通过路径初始化
- (AVFormatContext *)createFormatContextbyFilePath:(NSString *)filePath {
if (filePath == nil) {
log4cplus_error(kModuleName, "%s: file path is NULL",__func__);
return NULL;
}
AVFormatContext *formatContext = NULL;
AVDictionary *opts = NULL;
av_dict_set(&opts, "timeout", "1000000", 0);//设置超时1秒
formatContext = avformat_alloc_context();
BOOL isSuccess = avformat_open_input(&formatContext, [filePath cStringUsingEncoding:NSUTF8StringEncoding], NULL, &opts) < 0 ? NO : YES;
av_dict_free(&opts);
if (!isSuccess) {
if (formatContext) {
avformat_free_context(formatContext);
}
return NULL;
}
if (avformat_find_stream_info(formatContext, NULL) < 0) {
avformat_close_input(&formatContext);
return NULL;
}
return formatContext;
}
AVStream
简介
AVStream是存储每一个视频/音频流信息的结构体
属性
-
int index:标识该视频/音频流
-
AVCodecContext *codec:指向该视频/音频流的AVCodecContext(它们是一一对应的关系)
-
AVRational time_base:时基。通过该值可以把PTS,DTS转化为真正的时间。FFMPEG其他结构体中也有这个字段,但是根据我的经验,只有AVStream中的time_base是可用的。PTS*time_base=真正的时间
-
int64_t duration:该视频/音频流长度
-
AVDictionary *metadata:元数据信息
-
AVRational avg_frame_rate:帧率(注:对视频来说,这个挺重要的)
-
AVPacket attached_pic:附带的图片。比如说一些MP3,AAC音频文件附带的专辑封面。
用法
查找AVStream的下标
- (int)getAVStreamIndexWithFormatContext:(AVFormatContext *)formatContext isVideoStream:(BOOL)isVideoStream {
int avStreamIndex = -1;
for (int i = 0; i < formatContext->nb_streams; i++) {
if ((isVideoStream ? AVMEDIA_TYPE_VIDEO : AVMEDIA_TYPE_AUDIO) == formatContext->streams[i]->codecpar->codec_type) {
avStreamIndex = i;
}
}
if (avStreamIndex == -1) {
log4cplus_error(kModuleName, "%s: Not find video stream",__func__);
return NULL;
}else {
return avStreamIndex;
}
}
通过下标获取视频流
// Get video stream
AVStream *videoStream = m_formatContext->streams[m_videoStreamIndex];
m_video_width = videoStream->codecpar->width;
m_video_height = videoStream->codecpar->height;
m_video_fps = GetAVStreamFPSTimeBase(videoStream);
通过AVMediaType判断是音频还是视频(通常会在构建的时候将之转为其他名字避免和iOS原生冲突如FfmpegaVMediaType)
stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO
通过AVCodecID判断需要支持的格式
AVCodecID codecID = stream->codecpar->codec_id;
log4cplus_info(kModuleName, "%s: Current video codec format is %s",__func__, avcodec_find_decoder(codecID)->name);
// 目前只支持H264、H265(HEVC iOS11)编码格式的视频文件
if ((codecID != AV_CODEC_ID_H264 && codecID != AV_CODEC_ID_HEVC) || (codecID == AV_CODEC_ID_HEVC && [[UIDevice currentDevice].systemVersion floatValue] < 11.0)) {
log4cplus_error(kModuleName, "%s: Not suuport the codec",__func__);
return NO;
}
通过判断需要
AVPacket
简介
AVPacket是存储压缩编码数据相关信息的结构体
属性
typedef struct AVPacket {
int64_t pts; // 显示时间戳
int64_t dts; // 解码时间戳
int stream_index; // Packet所在stream的index
int flags; // 标志,其中最低为1表示该数据是一个关键帧
AVPacketSideData *side_data;
int side_data_elems;
int64_t duration; // 数据的时长,以所属媒体流的时间基准为单位
int64_t pos; // 数据在媒体流中的位置,未知则值为-1
uint8_t *data; // 指向保存压缩数据的指针,这就是AVPacket实际的数据。(例如对于H.264来说。1个AVPacket的data通常对应一个NAL。因此在使用FFMPEG进行视音频处理的时候,常常可以将得到的AVPacket的data数据直接写成文件,从而得到视音频的码流文件。)
int size; // 容器提供的一些附加数据
AVBufferRef *buf; // 用来管理data指针引用的数据缓存的
} AVPacket;
用法
AVPacket packet;
av_init_packet(&packet); // 初始化packet的值为默认值,该函数不会影响data引用的数据缓存空间和size,需要单独处理
int size = av_read_frame(formatContext, &packet); // 从媒体流中读取帧填充到填充到Packet的数据缓存空间
if (size < 0 || packet.size < 0) { // 解析完成
}
av_packet_unref(&packet); // 释放packet,包括其data引用的数据缓存