音视频项目—基于FFmpeg和SDL的音视频播放器解析(四)

介绍

在本系列,我打算花大篇幅讲解我的 gitee 项目音视频播放器,在这个项目,您可以学到音视频解封装,解码,SDL渲染相关的知识。您对源代码感兴趣的话,请查看基于FFmpeg和SDL的音视频播放器

如果您不理解本文,可参考我的前一篇文章音视频项目—基于FFmpeg和SDL的音视频播放器解析(三)

解析

上文中,我们还没有解析完 demuxthread 的公有成员函数,我们今天接着讲。

这篇文章总共要讲 2 个函数,分别有六个函数 Init,Run,AudioCodecParameters,VideoCodecParameters,AudioStreamTimebase,VideoStreamTimebase。今天这篇文章将前两个。

Init:
int DemuxThread::Init(const string& url) {
	int ret = 0;
	this->url = url;

	ifmt_ctx = avformat_alloc_context();

	ret = avformat_open_input(&ifmt_ctx, url.c_str(), NULL, NULL);
	if (ret < 0) {
		perror("demuxthread: avformat_open_input failed");
		return -1;
	}

	ret = avformat_find_stream_info(ifmt_ctx, NULL);
	if (ret < 0) {
		perror("demuxthread: avformat_find_stream_info failed");
			return -1;
	}

	for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
		if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
			video_index = i;
			break;
		}
	}
	if (video_index < 0 ) {
		perror("demuxthread: avformat video stream index failed");
		return -1;
	}
	for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
		if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {
			audio_index = i;
			break;
		}
	}
	if (audio_index < 0) {
		perror("demuxthread: avformat audio stream index failed");
		return -1;
	}
	return 0;
}

这行代码看起来很长,不过大家别慌,我解析过后就会很好理解。

我们可以看到开头的三个函数 avformat_alloc_context,avformat_open_input,avformat_find_stream_info,这三个函数是分别是分配 AVFormatContext,打开输入地址,查找流信息,这是墨守成规的,不用深究。

接着,我们看到下面的代码:

for (unsigned int i = 0; i < ifmt_ctx->nb_streams; i++) {
		if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO) {
			video_index = i;
			break;
		}
}
if (video_index < 0 ) {
	perror("demuxthread: avformat video stream index failed");
	return -1;
}

里面的逻辑就是通过遍历,找到 AVFormatContext 中的视频流位置。

还有一种写法也可以:

video_index = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);

后面的 audio_index 同理。

Run:
void DemuxThread::Run() {
	int ret = 0;
	AVPacket pack;
	while (abort != 1){
		ret = av_read_frame(ifmt_ctx, &pack);
		if (ret < 0) {
			perror("demuxthread: av_read_frame failed");
			break;
		}
		if (pack.stream_index == audio_index) {
			audio_packet_q->Push(&pack);
		}else if (pack.stream_index == video_index) {
			video_packet_q->Push(&pack);
		}else{
			av_packet_unref(&pack);
		}
	}
}

Run 函数是线程运行的函数。

我们可以看一下,开始如果标识位 abort 不等于 1,意思为线程可以运行。接着开始读帧,av_read_frame,将 AVPacket 的数据储存起来。

然后开始判断 packet 的类型,如果 packet 的 index 和 Init 函数的 video_index 一致,则放入存储 video包的队列 video_packet_q 中,音频同理。如果都不是,则取消 packet 的数据引用。

好了,今天就先解析这么多,下一篇文章会将剩余的函数解析完的。

欲知后事如何,请听下回分解。

你可能感兴趣的:(音视频,ffmpeg)