FFmpeg的hello world
ffmpeg的hello world并不是打印hello world,而是打印视频的信息。比如他的容器格式、长度、分辨率、音频通道,最后我们将会解码一些帧并将他们保存为图片
ffmpeg的架构
首先先了解一下ffmpeg的架构以及他的组件如何与其他组件进行通信,如图是视频解码的过程:
首先,我们需要将视频文件加载到AVFormatContext结构体,实际上,它并没有全部加载而是只读取了他的头部信息。
一旦我们加载了容器的最小标头,我们就能够访问他的流(也就是通常所说的视频和音频数据),每路流可以通过AVStream这个结构体进行访问。
假设我们的视频有两路流,分别是aac编码的音频流和h264编码的avc视频流。从每路流中我们可以提取中一片片的数据包,并加载到avpacket中。
这些数据包包含的就是已经被编码过的视频数据,为了进行解码,我们需要将这些数据传递给对应的解码器AVCodec。
AVCodec解码后,将封装到AVFrame中,这些数据就是经过解码后的帧数据。
代码演练
首先,我们需要申请AVFormatContent的内存用于存储视频文件的信息。
AVFormatContext *pFormatContext = avformat_alloc_context();
接下来,我们将通过打开视频文件,并读取他的头部信息,并将视频的最小信息赋值给AVFormatContext结构体(通常这个时候codec还是未打开的状态)。实现这个操作的函数avformat_open_input
,这个参数需要传递四个参数:AVFormatContext
、文件名称和两个可选参数:输入格式AVInputFormat
(如果为NULL,ffmpeg会猜测它的格式) 以及AVDictionary
(用来给解封装器的参数)。
avformat_open_input(&pFormatContext, filename, NULL, NULL);
打开后,就能够打印出这个视频文件所包含的信息。
printf("Format %s, duration %lld us", pFormatContext->iformat->long_name,
pFormatContext->duration);
接下来,为了能访问流信息,我们需要从媒体中读取数据。这里需要用函数avformat_find_stream_info
来实现。操作后,pFormatContext->nb_streams
会保存着视频文件中包含流的数量,当要访问某路流的时候,pFormatContext->streams[i]
就能实现对流的访问,并保存在AVStream
结构体中。
avformat_find_stream_info(pFormatContext, NULL);
for (int i = 0; i < pFormatContext->nb_streams; i++)
{
//
}
对于每路流,我们需要保存他的编码器参数,这里用到了AVCodecParameters,
这一结构体。
AVCodecParameters *pLocalCodecParameters = pFormatContext->streams[i]->codecpar;
保存后,就可以打印一些codec的信息。
// specific for video and audio
if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_VIDEO) {
printf("Video Codec: resolution %d x %d", pLocalCodecParameters->width, pLocalCodecParameters->height);
} else if (pLocalCodecParameters->codec_type == AVMEDIA_TYPE_AUDIO) {
printf("Audio Codec: %d channels, sample rate %d", pLocalCodecParameters->channels, pLocalCodecParameters->sample_rate);
}
// general
printf("\tCodec %s ID %d bit_rate %lld", pLocalCodec->long_name, pLocalCodec->id, pCodecParameters->bit_rate);
有了codec信息后,我们需要将这些信息放入申请的AVCodecContext
结构体中。这里用到了avcodec_paramete