FFmpeg解复用器(解封装)简单测试【2】

解复用器(解封装)的相关函数

avformat_alloc_context();负责申请一个AVFormatContext结构的内存,并进行简单初始化
avformat_free_context();释放该结构里的所有东西以及该结构本身
avformat_close_input();关闭解复用器。关闭后就不再需要使用avformat_free_context 进行释放。
avformat_open_input();打开输入视频文件
avformat_find_stream_info();获取视频文件信息
av_read_frame(); 读取音视频包
avformat_seek_file(); 定位文件
av_seek_frame():定位帧

流程

在这里插入图片描述

测试

#include <iostream>
#include <thread>
using namespace std;

extern "C"
{
#include "libavformat/avformat.h"
#include "libavcodec/avcodec.h"
}
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib,"avcodec.lib")


static double r2d(AVRational r)
{
	return r.den == 0 ? 0 : (double)r.num / (double)r.den;
}

void XSleep(int ms)
{
	//C++ 11 
	chrono::milliseconds du(ms);//延迟ms
	this_thread::sleep_for(du);
}

int main(int argc, char* argv[])
{
//解封装
cout << " test Demux FFmpeg club" << endl;

	const char* path = "1.mp4";

	//1、初始化封装库
	av_register_all();

	//初始化网络库(可以打开rtsp rtmp http 协议的流媒体视频)
	//avformat_network_init();

	//参数设置
	AVDictionary* opts = NULL;

	//设置rtsp流已tcp协议打开
	//av_dict_set(&opts, "rtsp_transport", "tcp", 0);
	//设置网络延迟时间
	//av_dict_set(&opts, "max_delay", "500", 0);

	//解封装上下文
	AVFormatContext* ic = NULL;

	int re = avformat_open_input(
				&ic, //上下文结构体:保存音视频的构成和基本信息
				path, //音视频文件路径
				0, //自动选择解封器
				&opts//参数设置,比如rtsp的延迟设置
				);
	if (re != 0)//0为成功打开文件
	{
		char buf[1024] = {0};
		av_strerror(re, buf, sizeof(buf) - 1);//打印错误原因
		cout << " open--" << path << "--failed! : " << buf << endl;
		getchar();
		return -1;
	}
	cout << "open--" << path << "--success! : " << endl;


	//获取音视频流信息,包含了视频和音频 
	//mp4在这里flv有所不同
	re = avformat_find_stream_info(ic, 0);

	//AV_TIME_BASE是时间的计数百万, ic->duration / AV_TIME_BASE是秒。
	//打印总时长 ms
	int total_time = ic->duration / (AV_TIME_BASE/1000);
	cout << "total time : " << total_time << " ms" << endl;

	//打印视频流的详细信息
	av_dump_format(ic,0,path,0);

	//音视频索引
	int videoStream = 0;
	int audioStream = 1;

	//获取音视频流信息(遍历,函数获取),ic->nb_streams是媒体流的数量
	for (int i = 0; i < ic->nb_streams; i++)
	{
		AVStream* as = ic->streams[i];
		//as->codecpar->codec_type 中保存流的类别
		//音频
		if (as->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			audioStream = i;
			cout << i << " : 音频信息" << endl;
			cout << "sample_rate = " << as->codecpar->sample_rate << endl;
			//cout << "format = " << as->codecpar->format << endl;//AVSampleFormat
			cout << "channnels = " << as->codecpar->channels << endl;
			//cout << "codec_id = " << as->codecpar->codec_id << endl;//AVCodecID
			cout << "audio fps = " << r2d(as->avg_frame_rate) << endl;//存储浮点数是按照分子分母保存的
			//音频一帧数据 是 一定量的样本数 单通道
			cout<< "frame_size = " << as->codecpar->frame_size << endl;
			//fps = sample_rate/frame_size
		}
		//视频
		else if (as->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
		{
			videoStream = i;
			cout << i <<" : 视频信息" << endl;
			cout << "width = " << as->codecpar->width << endl;
			cout << "height = " << as->codecpar->height << endl;
			//帧率 fps 分数转换
			cout << "vedio fps = " << r2d(as->avg_frame_rate) << endl;
			//打印时长
			int as_duration =  (as->duration) * av_q2d(as->time_base);
			cout << "视频的总时长为: " << as_duration / 3600 << ":" << as_duration % 3600 / 60 << ":" << as_duration % 60 << endl;
			//av_q2d;

		}
	}

	//获取视频流 另一种方法
	//videoStream = av_find_best_stream(ic, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
	//ic->streams[videoStream];

	//获取一个包的数据
	AVPacket* pkt = av_packet_alloc();//分配包的内存(堆上),需要手动释放
	int pkt_count = 0;
	int print_max_count = 10;//打印10个包
	cout << "*****av_read_frame start*****" << endl;

	while (1)
	{
		int ret = av_read_frame(ic, pkt);
		if (ret < 0)
		{
			cout << "******av_read_frame end*****" << endl;
			break;//开辟空间失败,就不需要再释放,即减少引用计数

			//循环播放,跳转帧的位置
			//int ms = 100;//根据时间基数转换位置
			//long long pos = (double)ms / 1000 * r2d(ic->streams[pkt->stream_index]->time_base);
			//av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD | AVSEEK_FLAG_FRAME);//往后找一帧,且找到关键帧
			//getchar();
			//continue;
		}
		if (pkt_count++ < print_max_count)
		{
			if (pkt->stream_index == audioStream)
			{
				//显示时间
				cout << "******audio pts :"<< pkt->pts << endl;
				//显示为ms,方便同步
				cout << pkt->pts * r2d(ic->streams[pkt->stream_index]->time_base) * 1000 << endl;
				//解码时间
				cout << "******audio dts :" << pkt->dts << endl;
				cout << "******audio size :" << pkt->size << endl;
				cout << "******audio pos :" << pkt->pos << endl;
				cout << endl;
			}
			else if (pkt->stream_index == videoStream)
			{
				cout << "******video pts :" << pkt->pts << endl;
				//显示为ms
				cout << pkt->pts * r2d(ic->streams[pkt->stream_index]->time_base) * 1000 << endl;
				cout << "******video dts :" << pkt->dts << endl;
				cout << "******video size :" << pkt->size << endl;
				cout << "******video pos :" << pkt->pos << endl;
				cout << endl;
			}
			else
				cout << "unknow stream_index" << pkt->stream_index << endl;
		}
		//每次拿到包的数据后减少引用计数
		av_packet_unref(pkt);
	}
	//最后释放包的内存
	av_packet_free(&pkt);
	
	if (ic)
	{
		//释放封装上下文,并且把ic置为0
		avformat_close_input(&ic);
	}

	getchar();
	return 0;
}

在这里插入图片描述

最近更新

  1. TCP协议是安全的吗?

    2024-06-07 02:14:01       16 阅读
  2. 阿里云服务器执行yum,一直下载docker-ce-stable失败

    2024-06-07 02:14:01       16 阅读
  3. 【Python教程】压缩PDF文件大小

    2024-06-07 02:14:01       15 阅读
  4. 通过文章id递归查询所有评论(xml)

    2024-06-07 02:14:01       18 阅读

热门阅读

  1. Kotlin 特色 sealed 关键字

    2024-06-07 02:14:01       8 阅读
  2. Kotlin 中,扩展函数(Extension Functions)

    2024-06-07 02:14:01       7 阅读
  3. docker安装mysql8和mysql5.7

    2024-06-07 02:14:01       8 阅读
  4. 问题:对象流仅读取一个对象

    2024-06-07 02:14:01       7 阅读
  5. Puppeteer用途

    2024-06-07 02:14:01       7 阅读
  6. perl: URI::rtsp 是用来处理RTSP协议的的URI的模块。

    2024-06-07 02:14:01       9 阅读
  7. js将元素滚动到可见区域

    2024-06-07 02:14:01       8 阅读
  8. 中介子方程

    2024-06-07 02:14:01       7 阅读
  9. LE Audio音频广播新功能Auracast介绍

    2024-06-07 02:14:01       10 阅读
  10. Git | SSH 密钥连接到 GitHub

    2024-06-07 02:14:01       10 阅读
  11. lsof 命令

    2024-06-07 02:14:01       8 阅读