音视频开发系列(10):基于qt的音频推流

这篇具有很好参考价值的文章主要介绍了音视频开发系列(10):基于qt的音频推流。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

今天分享一下利用qt录制音频,然后再利用ffmpeg推流到nginx服务器,最后再利用vlc进行拉流的demo。

首先介绍一下如何利用qt来进行音频的录制,qt的音频录制主要利用qt的QAudioFormat先进行音频信息的配置。主要需要配置以下的信息:

QAudioFormat fmt;
	fmt.setSampleRate(sampleRate);// 采样率, 一秒采集音频样本数量,常设置为44100
	fmt.setChannelCount(channels);  // 音频通道数
	fmt.setSampleSize(16); //一个音频数据大小
	fmt.setCodec("audio/pcm"); //编码方式,大多声卡只支持pcm,也可以通过获取参数得到声卡支持参数
	fmt.setByteOrder(QAudioFormat::LittleEndian); // 小端 存储还是大端存储
	fmt.setSampleType(QAudioFormat::UnSignedInt); // 数据类型,对应的是16位

然后使用QAudioDeviceInfo来获取是否支持改设置信息,如果不支持的话就取其最近的配置。

	QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
	if (!info.isFormatSupported(fmt))
	{
		cout << "Audio format not support!" << endl;
		fmt = info.nearestFormat(fmt);
	}

然后再利用QIODevice开始录制音频,具体的读取方式如下所示。

		//一次读取一帧音频 由于这种方式读取不准确,所以仅用来进行判断
		if (input->bytesReady() < readSize)
		{
			QThread::msleep(1);
			continue;
		}

        int size = 0;
		while (size != readSize)
		{
			int len = io->read(buf + size, readSize - size);
			if (len < 0)break;
			size += len;
		}
		if (size != readSize)continue;

到这里利用qt进行音频的录制就完成了,接下来是利用ffmpeg进行推流,推流的调用的函数和之前视频推流调用的api一致,只是一些参数的配置进行了改变。

由于推流时音频的格式是AV_SAMPLE_FMT_FLTP,与qt采集到的格式AV_SAMPLE_FMT_S16不一致,所以需要对采集到的音频进行重采样。

这边利用的是ffmpeg的api函数来进行重采样,首先需要初始化重采样的上下文,主要利用

swr_alloc_set_opts()函数,设置参数如下:

	SwrContext *asc = NULL;
	asc = swr_alloc_set_opts(asc,
		    av_get_default_channel_layout(channels), AV_SAMPLE_FMT_FLTP, sampleRate,               //输出格式
		av_get_default_channel_layout(channels), AV_SAMPLE_FMT_S16, sampleRate,//输入格式	
		0,0);
	if (!asc)
	{	
		cout << "swr_alloc_set_opts failed!"<<endl;
		getchar();
		return -1;
	}

主要是配置输出和输入的参数,接着利用swr_init()初始化该上下文。

然后分配好音频重采样的输出空间,配置信息如下:

	AVFrame *pcm = av_frame_alloc();
	pcm->format = outSampleFmt;
	pcm->channels = channels;
	pcm->channel_layout = av_get_default_channel_layout(channels);
	pcm->nb_samples = 1024;//一帧音频一通道的采样数量
	ret = av_frame_get_buffer(pcm, 0); //给pcm分配存储空间
	if (ret != 0)
	{
		char err[1024] = { 0 };
		av_strerror(ret, err, sizeof(err) - 1);
		cout << err << endl;
		getchar();
		return -1;
	}

然后利用swr_convert()函数进行转化将转化后的数据放入pcm中。

接下来需要对音频的pts进行运算,主要运算如下:

		//pts运算
		//nb_sample/sample_rate =一帧音频的秒数 
		//time_base pts=sec * timebase.den 
		pcm->pts = apts;
		apts += av_rescale_q(pcm->nb_samples, {1,sampleRate},ac->time_base);

接下来就是对重采样后的音频进行推流,这边与之前视频推流的一致,具体的参数配置可以参考我下面分享的代码:

#include <QtCore/QCoreApplication>
#include <QAudioInput>
#include <QThread>
#include <iostream>
extern "C"
{
#include "libswresample/swresample.h"
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
}
#pragma comment(lib,"avformat.lib")
#pragma comment(lib,"swresample.lib")
#pragma comment(lib,"avutil.lib")
#pragma comment(lib, "avcodec.lib")
using namespace std;
int main(int argc, char *argv[])
{
	QCoreApplication a(argc, argv);

	//注册所有的编解码器
	avcodec_register_all();
	//注册所有的封装器
	av_register_all();
	//注册所有的网络协议
	avformat_network_init();
	char *outUrl = "rtmp://192.168.198.128/live";
	int sampleRate = 44100;
	int channels = 2;
	int sampleByte = 2;
	AVSampleFormat inSampleFmt = AV_SAMPLE_FMT_S16;
	AVSampleFormat outSampleFmt = AV_SAMPLE_FMT_FLTP;
	///1 qt音频开始录制 
	QAudioFormat fmt;
	//采样频率
	fmt.setSampleRate(sampleRate);
	//通道数量
	fmt.setChannelCount(channels);
	//样本大小
	fmt.setSampleSize(sampleByte *8);
	//格式
	fmt.setCodec("audio/pcm");
	//字节序
	fmt.setByteOrder(QAudioFormat::LittleEndian);
	fmt.setSampleType(QAudioFormat::UnSignedInt);
	QAudioDeviceInfo info = QAudioDeviceInfo::defaultInputDevice();
	if (!info.isFormatSupported(fmt))
	{
		cout << "Audio format not support!" << endl;
		fmt = info.nearestFormat(fmt);
	}
	cout << "Audio format success" << endl;

	QAudioInput *input = new QAudioInput(fmt);
	//开始录制音频
	QIODevice *io= input->start();

	///2 音频重采样
	SwrContext *asc = NULL;
	asc = swr_alloc_set_opts(asc,
		    av_get_default_channel_layout(channels), AV_SAMPLE_FMT_FLTP, sampleRate,               //输出格式
		av_get_default_channel_layout(channels), AV_SAMPLE_FMT_S16, sampleRate,//输入格式	
		0,0);
	if (!asc)
	{	
		cout << "swr_alloc_set_opts failed!"<<endl;
		getchar();
		return -1;
	}
	int ret = swr_init(asc);
	if (ret != 0)
	{
		char err[1024] = { 0 };
		av_strerror(ret, err, sizeof(err) - 1);
		cout << err << endl;
		getchar();
		return -1;
	}
	cout << "音频重采样 上下文初始化成功" << endl;

	///3 音频重采样输出空间分配
	AVFrame *pcm = av_frame_alloc();
	pcm->format = outSampleFmt;
	pcm->channels = channels;
	pcm->channel_layout = av_get_default_channel_layout(channels);
	pcm->nb_samples = 1024;//一帧音频一通道的采样数量
	ret = av_frame_get_buffer(pcm, 0); //给pcm分配存储空间
	if (ret != 0)
	{
		char err[1024] = { 0 };
		av_strerror(ret, err, sizeof(err) - 1);
		cout << err << endl;
		getchar();
		return -1;
	}

	///4 初始化音频编码器
	AVCodec *codec = avcodec_find_encoder(AV_CODEC_ID_AAC);
	if (!codec)
	{
		cout << "avcodec_find_encoder failed!" << endl;
		getchar();
		return -1;
	}
	//音频编码器上下文
	AVCodecContext *ac = avcodec_alloc_context3(codec);
	if (!ac)
	{
		cout << "avcodec_alloc_context3 failed!" << endl;
		getchar();
		return -1;
	}
	cout << "avcodec_alloc_context3 success!" << endl;
	
	ac->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
	ac->thread_count = 8;

	//音频的参数
	ac->bit_rate = 40000;
	ac->sample_rate = sampleRate;
	ac->sample_fmt = AV_SAMPLE_FMT_FLTP;
	ac->channels = channels;
	ac->channel_layout = av_get_default_channel_layout(channels);
	//打开编码器
	ret = avcodec_open2(ac, 0, 0);
	if (ret != 0)
	{
		char err[1024] = { 0 };
		av_strerror(ret, err, sizeof(err) - 1);
		cout << err << endl;
		getchar();
		return -1;
	}
	cout << "avcodec_open2 success!" << endl;

	///5 封装器和音频流配置
	//a.创建输出封装器上下文
	AVFormatContext *ic = NULL;

	ret = avformat_alloc_output_context2(&ic, 0, "flv", outUrl);
	if (ret != 0)
	{
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		cout << buf << endl;
		getchar();
		return -1;
	}
	cout << "avformat_alloc_output_context2 success!" << endl;
	//b.添加音频流
	AVStream *as = avformat_new_stream(ic, NULL);
	if (!as)
	{
		throw exception("avformat_new_stream failed!");
	}
	cout << "avformat_new_stream success!" << endl;
	as->codecpar->codec_tag = 0;
	//从编码器复制参数
	avcodec_parameters_from_context(as->codecpar, ac);
	av_dump_format(ic, 0, outUrl, 1);

	///6 打开rtmp的网络输出io
	ret = avio_open(&ic->pb, outUrl, AVIO_FLAG_WRITE);
	if (ret != 0)
	{
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		cout << buf << endl;
		getchar();
		return -1;
	}
	//写入封装头
	ret = avformat_write_header(ic, NULL);
	if (ret != 0)
	{
		char buf[1024] = { 0 };
		av_strerror(ret, buf, sizeof(buf) - 1);
		cout << buf << endl;
		getchar();
		return -1;
	}
	cout << "avformat_write_header success!" << endl;

	//一次读取一帧音频的字节数
	int readSize = pcm->nb_samples*channels*sampleByte;
	char *buf = new char[readSize];
	int apts = 0;
	AVPacket pkt = {0};
	for (;;)
	{
		//一次读取一帧音频
		if (input->bytesReady() < readSize)
		{
			QThread::msleep(1);
			continue;
		}
		int size = 0;
		while (size != readSize)
		{
			int len = io->read(buf + size, readSize - size);
			if (len < 0)break;
			size += len;
		}
		if (size != readSize)continue;

		const uint8_t *indata[AV_NUM_DATA_POINTERS] = { 0 };
		indata[0] = (uint8_t *)buf;
		//已经读取一帧源数据
		//重采样数据
		int len = swr_convert(asc, pcm->data, pcm->nb_samples,//输出参数,输出存储地址,样本数
			indata, pcm->nb_samples
			);

		//pts运算
		//nb_sample/sample_rate =一帧音频的秒数 
		//time_base pts=sec * timebase.den 
		pcm->pts = apts;
		apts += av_rescale_q(pcm->nb_samples, {1,sampleRate},ac->time_base);

		int ret = avcodec_send_frame(ac, pcm);
		if (ret != 0)continue;

		av_packet_unref(&pkt);
		ret = avcodec_receive_packet(ac, &pkt);
		cout << "avcodec_receive_packet   " << ret << endl;
		if (ret != 0)continue;
		cout << pkt.size << " " << flush;

		//推流
		pkt.pts = av_rescale_q(pkt.pts, ac->time_base, as->time_base);
		pkt.dts = av_rescale_q(pkt.dts, ac->time_base, as->time_base);
		pkt.duration= av_rescale_q(pkt.duration, ac->time_base, as->time_base);
		ret = av_interleaved_write_frame(ic, &pkt);
		if (ret == 0)
		{
			cout << "#" << flush;
		}
	}
	delete buf;
	getchar();
	return a.exec();
}

然后在这边做个分享,由于我在第一次在函数 avcodec_receive_packe()没有接收返回值,所以导致出现dump的情况,根据下面的打印可以发现第一次接收数据的时候返回的是-11,说明从缓存区获取到的数据是有问题的,下面还对其进行推流就会出现错误的情况,具体的情况分析可以参考这篇文章关于FFmpeg编码时,avcodec_receive_packet返回-11的解决办法_小小菜鸟少少烦恼的博客-CSDN博客_avcodec_receive_packet

音视频开发系列(10):基于qt的音频推流

 文章来源地址https://www.toymoban.com/news/detail-433942.html

到了这里,关于音视频开发系列(10):基于qt的音频推流的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 音视频开发:音频编码原理+采集+编码实战

    消除冗余信息,压缩量最大,也叫有损压缩 剔除人耳听觉范围外的音频信号20Hz以下和20000Hz以上; 去除被掩蔽的音频信号,信号的遮蔽可以分为频域遮蔽和时域遮蔽; 频域遮蔽效应 屏蔽70分贝以下,20HZ以下,20000HZ以上 屏蔽分贝小,频率小的声音 两个频率相近发出的声音,

    2024年02月05日
    浏览(61)
  • Android 音视频开发—MediaPlayer音频与视频的播放介绍

    Android多媒体中的——MediaPlayer,我们可以通过这个API来播放音频和视频该类是Androd多媒体框架中的一个重要组件,通过该类,我们可以以最小的步骤来获取,解码和播放音视频。 它支持三种不同的媒体来源: 本地资源 内部的URI,比如你可以通过ContentResolver来获取 外部URL(流

    2024年02月10日
    浏览(60)
  • Qt音视频开发45-音视频类结构体参数的设计

    视频监控内核组件重构和完善花了一年多时间,整个组件个人认为设计的最好的部分就是各种结构体参数的设计,而且分门别类,有枚举值,也有窗体相关的结构体参数,解码相关的结构体参数,同时将部分常用的结构体参数的获取和设置单独提供了函数,参阅海康大华等大

    2024年02月05日
    浏览(75)
  • 安卓音视频开发(3)—— AudioTrack两种方式播放pcm音频

    前言 之前学习了AudioRecord录制pcm音频,与之对应的就是AudioTrack播放pcm音频(MediaPlayer、SoundPool有其他应用场景),它有两种数据加载模式(MODE_STATIC、MODE_STREAM)。 模式 MODE_STATIC :这种模式下,一次将所有的数据放入一个固定的buffer,然后写入到AudioTrack中,后续就不用继续

    2023年04月22日
    浏览(53)
  • Qt/C++音视频开发46-音视频同步保存到MP4

    用ffmpeg单独做视频保存不难,单独做音频保存也不难,难的是音视频同步保存到MP4中,重点是音视频要同步,其实这也不难,只要播放那边音视频同步后的数据,写入到文件即可。最难的是在播放过程中不断随机的切换播放进度,而且还会暂停播放、暂停录制的情况出现,这

    2024年02月17日
    浏览(71)
  • Qt之基于QMediaPlayer的音视频播放器(支持常见音视频格式)

    Qt自带了一个Media Player的例子,如下图所示: 但是运行这个例子机会发现,连最基本的MP4格式视频都播放不了。因为QMediaPlayer是个壳(也可以叫框架),依赖本地解码器,视频这块默认基本上就播放个MP4,甚至连MP4都不能播放,如果要支持其他格式需要下载k-lite或者LAVFilter

    2024年02月02日
    浏览(70)
  • Qt音视频开发38-ffmpeg视频暂停录制的设计

    基本上各种播放器提供的录制视频接口,都是只有开始录制和结束录制两个,当然一般用的最多的也是这两个接口,但是实际使用过程中,还有一种可能需要中途暂停录制,暂停以后再次继续录制,将中间部分视频不需要录制,跳过这部分不需要的视频,而且录制的视频文件

    2023年04月20日
    浏览(75)
  • 音视频开发之旅——音频基础概念、交叉编译原理和实践(LAME的交叉编译)(Android)

    本文章已授权微信公众号郭霖(guolin_blog)转载。 本文主要讲解的是 音频基础概念 、 交叉编译原理和实践(LAME的交叉编译) ,是基于 Android平台 ,示例代码如下所示: AndroidAudioDemo 另外, iOS平台 也有相关的文章,如下所示: 音视频开发之旅——音频基础概念、交叉编译

    2024年04月25日
    浏览(85)
  • FFMpeg-3、基于QT实现音视频播放显示

    1、音视频播放的基础知识 内容来自雷神博客 1、在Windows平台下的视频播放技术主要有以下三种:GDI,Direct3D和OpenGL;音频播放技术主要是DirectSound。 SDL本身并不具有播放显示的功能,它只是封装了底层播放显示的代码 记录三种视频显示技术:GDI,Direct3D,OpenGL。其中Direct3D包

    2024年02月03日
    浏览(66)
  • 音视频开发-ffmpeg介绍-系列一

    目录 一.简介 FFmpeg框架的基本组成包含: 二. FFmpeg框架梳理音视频的流程​编辑 基本概念: 三.ffmpeg、ffplay、ffprobe区别      4.1 ffmpeg是用于转码的应用程序  4.2 fffplay是用于播放的应用程序       4.3 ffprobe是用于查看文件格式的应用程序      4.4 ffmpeg是用于转码的应用程

    2024年02月16日
    浏览(46)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包