FastASR+FFmpeg(音视频开发+语音识别)

一、音视频处理基本梳理

1.多媒体文件的理解

FastASR+FFmpeg,音视频开发+语音识别

1.1 结构分析

多媒体文件本质上可以理解为一个容器容器里有很多流每种流是由不同编码器编码的在众多包中包含着多个帧(帧在音视频的处理中是最小单位)

1.2 封装格式

封装格式(也叫容器) 就是将已经编码压缩好的视频流、音频流及字幕流按照一定的方案放到一个文件中,便于*放播**软件*放播**。一般来说,视频文件的后缀就是它的封装格式。封装格式不一样,后缀名也就不一样(xxx.mp4 xxx.flv)。

1.3 音视频同步

Audio Master:同步视频到音频

Video Master: 同步音频到视频

External Clock Master: 同步音频和视频到外部时钟

1.4 音视频录制原理

FastASR+FFmpeg,音视频开发+语音识别

1.5 音视频*放播**原理

FastASR+FFmpeg,音视频开发+语音识别

1.6 音视频*放播**原理

FastASR+FFmpeg,音视频开发+语音识别

2.音视频的基础概念

2.1 声音

对自然界的声音进行采样,采样就是在时间轴上对信号进行数字化信号,即按照一定时间间隔t在模拟信号x(t)上逐点采取其瞬时值。采样率越高,声音的还原程度越高,质量就越好,同时占用空间会变大。

量化: 用有限个幅度近似原来连续的幅度值把模拟信号的连续幅度变成有限数量的有一定间隔的离散值。【采样值的精确度取决于它用多小位来表示,这就是量化。例如8位量化可以表示256个不同的值,而CD质量的16位可以表示65536个值,范围-32769-32767】

我们来算下这个值:

FastASR+FFmpeg,音视频开发+语音识别

FastASR+FFmpeg,音视频开发+语音识别

编码: 安装一定的规律把量化后的值用二进制数字表示,然后转化成二值或多值的数字信号流。这样得到的数字信号可以通过电缆,卫星通信等数字线路传输。接收端与上述过程相反。

编码如何理解:

我们在学校上学的时候应该听老师讲过哈夫曼编码,道理其实都一样。采用某种形式将某个值变成唯一的,有效的编码可以提高安全性、压缩数据等有效功效。

PCM: 上面数字化的过程又称为脉冲编码调制,通常我们说音频的裸数据格式就是脉冲编码调制数据。描述一段PCM数据需要4量化指标:采样率、位深度、字节序、声道数。

采样率: 每秒钟采样多少次,以Hz为单位。

FastASR+FFmpeg,音视频开发+语音识别

位深度(Bit-depth):表示用多小个二进制位来描述采样数据,一般位16bit。

字节序:表示音频PCM数据存储的字节序是大端存储还是小端存储,为了数据处理效率高效,通常采用小端存储。

声道数(channel number):当前PCM文件中包含的声道数是单声道还是双声道

比特率:每秒传输的bit数,单位为bps(Bit Per Second)。间接衡量声音质量的一个标准。没有压缩的音频数据的比特率=采样频率*采样精度*通道数。

码率: 压缩后的音频数据的比特率。码率越大,压缩效率越低,音质越好,压缩后数据越大。码率=音频文件大小/时长。

FastASR+FFmpeg,音视频开发+语音识别

帧:每次编码的采样单元数。比如MP3通常是1152个采样点作为一个编码单元,AAC通常是1024个采样点作为一个编码单元。

帧长:可以指每帧*放播**的持续时间。每帧持续时间(秒)=每帧采样点数/采样频率(HZ)。也可以指压缩后每帧的数据长度。

音频编码:主要作用是将音频采样数据(PCM等)压缩成为音频码流,从而降低音频的数据量,偏于存储和传输。

FastASR+FFmpeg,音视频开发+语音识别

2.2图像

图像是客观对象的一种相似性的、生动性的描述或写真,是人类社会活动中最常用的信息载体。或者说图像是客观对象的一种表示,它包含了被描述对象的有关信息。它是人们最主要的信息源。

像素:屏幕显示是把有效面积化为很多个小格子,每个格子只显示一种颜色,是成像的最小元素,因此就叫做"像素"。

分辨率:屏幕在长度和宽度这两个方向上各有多少个像素,就叫做分辨率,一般用A x B来表示。分辨率越高,每个像素的面积越小,显示效果就越平滑细腻。

RGB表示图像:8bit表示一个子像素: 取值范围[0~255] 或者 [00~FF]。例如图像格式RGBA_8888,表示4个8bit表示一个像素,而RGB_565用5+6+5bit表示一个像素。一张1280*720的RGBA_8888格式的图片大小=1280 x 720 x 32bit。所以每一张图像的裸数据都是很大的。一部90分钟的电影,没秒25帧: 90 * 60 * 25 * 1280 * 720 * 32 bit = 463.48G。

YUV表示图像:YUV,是另外一种颜色编码方法,视频的裸数据一般使用 YUV 数据格式表示。Y 表示明亮度,也称灰度 值(灰阶值)。UY 表示色度,均表示影响的色彩和饱和度,用于指定像素的颜色。

亮度:需要透过 RGB 输入信号建立,方式为将 RGB 信号的特定部分(g 分量信号)叠加到一起。

色度:定义了颜色的色调和饱和度,分别用 Cr、Cb 表示,(C 代表分量(是 component 的缩写))。Cr 反映 RGB 输入信号红色部分与 RGB 信号亮度值之间的差异。Cb 反映 RGB 输入信号蓝色部分与 RGB 信号亮度值之间的差异。

2.3视频

由于人类眼睛的特殊结构,画面快速切换时,画面会有残留( 视觉暂留),感觉起来就是连贯的动作。所以 ,视频就是由一系列图片构成的。

视频码率:指视频文件在单位时间内使用的数据流量,也叫码流率。码率越大,说明单位时间内取样率越大,数据流精度就越高。

视频帧率:通常说一个视频的25帧,指的就是这个视频帧率,即1秒中会显示25帧。帧率越高,给人的视觉就越流畅。

视频分辨率:分辨率就是我们常说的640x480分辨率、1920x1080分辨率,分辨率影响视频图像的大小。

帧:帧不需要参考其他画面而生成,解码时仅靠自己就重构完整图像。

视频的编码:编码的目的就是为了压缩, 让各种视频的体积变得更小,有利于存储和传输。国际上主流制定视频编解码技术的组织有两个,一个是“国际电联(ITU-T)”,它制定的标准 有 H.261、H.263、H.263+、H.264 等,另一个是“国际标准化组织(ISO)”它制定的标准有 MPEG-1、MPEG-2、 MPEG-4 等。

FastASR+FFmpeg,音视频开发+语音识别

相关学习资料推荐,点击下方链接免费报名,先码住不迷路~】

音视频免费学习地址:FFmpeg/WebRTC/RTMP/NDK/Android音视频流媒体高级开发

【免费分享】音视频学习资料包、大厂面试题、技术视频和学习路线图,资料包括(C/C++,Linux,FFmpeg webRTC rtmp hls rtsp ffplay srs 等等)有需要的可以点击 788280672 加群免费领取~

FastASR+FFmpeg,音视频开发+语音识别

3.常用的音视频处理第三方库

3.2.1基本概念

FFmpeg(Fast Forward MPEG)是全球领先的多媒体框架,能够解码(decode)、编码(encode)、转码(transcode)、复用(mux)、解复用(demux)、流化(stream)、滤波(filter)和*放播**几乎人类和机器创造的所有多媒体文件。

3.2.2 FFmpeg 的主要基本组成

FFmpeg的封装模块AVFormat:AVFormat实现了多媒体领域绝大数媒体封装格式,包括封装和解封装,如MP4、FLV、KV、 TS等文件封装格式,RTMP、RTSP、MMS、HLS等网络协议封装格式。FFmepg是否支持某种媒体封装格式取决于编译时是否包含了该格式的封装库。

FFmpeg的编解码模块AVCodec:AVCodec包括大多数常用的编解码格式,既支持编码也支持解码。除了支持MPEG4、AAC、MJPEG等自带的媒体格式也支持H.264(x264编码器)、H.265(X265编码器)、MP3(libMP3lame编码器)

FFmepg的滤镜模块AVFilter:AVFilter库提供了一个通用的音频、视频、字幕等滤镜处理框架。在AVFilter中,滤镜框架可以有多个输入和多个输出。

FFmpeg的视频图像转换计算模块swscale:swscale模块提供了高级别的图像转换API,它能够对图像进行缩放和像素格式转换。

FFmpeg的音频转换计算模块swresample:swresample提供了音频重采样API,支持音频采样、音频通道布局、布局调整。

3.2.3 FFmpeg 的优劣

高可移植性:可以在Linux、Mac、Windows等系统上编译、运行以及通过FATE(FFMPEG自动化测试环境)测试。

高性能:专门针对X86、arm、MIPS、ppc等大多数主流的处理器提供了对应的汇编级的优化实现。

高度安全: FFMPEG官方对代码审查总是考虑安全性,而且一旦发布的版本中有安全性的Bug都会尽快的修复并更新发布版本。

高度易用性:FFMPEG提供的API都有相关的注释,且官方也有对应的说明文档

支持的格式多样性:FFMPEG支持很多媒体格式的解码、编码、复用、解复用等功能,不管是很老的格式,还是比较新的格式均有不错的支持

无法识别有空格的文件名

FFMPEG编码时,时间戳只需要指定AVFrame的pts字段

3.2.4 FFmpeg的安装配置

FastASR+FFmpeg,音视频开发+语音识别

3.2.5 FFmpeg的命令行使用

FastASR+FFmpeg,音视频开发+语音识别

FastASR+FFmpeg,音视频开发+语音识别

二、视频中提取音频

1.FFmpeg

通过命令行

ffmpeg -i 视频文件路径 -vn 音频文件全路径 -hide_banner

参数说明

-vn 从视频中提取音频

-ab 指定编码比特率(一些常见的比特率 96k、128k、192k、256k、320k

-ar 采样率(22050、441000、48000)

-ac 声道数

-f 音频格式(通常会自动识别)

示例:

FastASR+FFmpeg,音视频开发+语音识别

a

通过提供的API

bool AVInterface::extractAudio(const char* src, const char* dstDir)
{
 
	if (NULL == src || NULL == dstDir)
	{
		printf("Ffmpeg::extractAudio[ERROR]::无效参数,请检查文件路径是否正确\n");
		return false;
	}
 
	int ret = 0;
 
	// 预存原文件路径
	const char* src_fileName = src;
 
	// 1.获取媒体文件的全局上下文信息
 
	// 1.1 定义 AVFormatContext 容器
	AVFormatContext* pFormatCtx = NULL;      // AVFormatContext描述了一个媒体文件或者媒体流构成的基本信息
	pFormatCtx = avformat_alloc_context();   // 为 pFormatCtx 申请内存
 
	// 1.2 打开媒体文件,并且读取媒体文件的头信息放入pFormatCtx中
	ret = avformat_open_input(&pFormatCtx, src_fileName, NULL, NULL);
	if (ret < 0)
	{
		printf("Ffmpeg::extractAudio[ERROR]::打开媒体流文件失败\n");
		return false;
	}
 
	// 2.探测流出信息
 
	// 2.1 探寻文件中是否存在信息流,如果存在则将多媒体文件信息流放到pFormatCtx
	ret = avformat_find_stream_info(pFormatCtx, NULL);
	if (ret < 0)
	{
		printf("Ffmpeg::extractAudio[ERROR]::文件中不存在信息流\n");
		return false;
	}
 
	av_dump_format(pFormatCtx, 0, src_fileName, 0);    // 打印封装格式和流信息
 
	// 2.2 查找文件信息流中是否存在音频流(我们只需要提取音频),并获取到音频流在信息流中的索引
	int audio_stream_index = -1;
	audio_stream_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
	if (-1 == audio_stream_index)
	{
		printf("Ffmpeg::extractAudio[ERROR]::文件中不存在音频流\n");
		return false;
	}
 
	// 3.输出容器的定义
	AVFormatContext* pFormatCtx_out = NULL;    // 输出格式的上下文信息  
	const AVOutputFormat*  pFormatOut = NULL;        // 输出的封装格式
	AVPacket packet;                         
 
	// 输出文件路径
	char szFilename[256] = { 0 };
	snprintf(szFilename, sizeof(szFilename), "%s/ffmpeg-music.aac", dstDir);
 
	// 3.1 初始化容器
 
	// 初始化一些基础的信息
	av_init_packet(&packet);                 
 
	// 给 pFormatCtx_out 动态分配内存,并且会根据文件名初始化一些基础信息
	avformat_alloc_output_context2(&pFormatCtx_out, NULL, NULL, szFilename);  
 
	// 得到封装格式 AAC
	pFormatOut = pFormatCtx_out->oformat;
 
 
	// 4.读取音频流,并且将输入流的格式拷贝到输出流的格式中
	
	for (int i = 0; i < pFormatCtx->nb_streams; ++i)   // nb_streams 流的个数
	{
		
		// 流的结构体,封存了一些流相关的信息
		AVStream* out_stream = NULL;               // 输出流
		AVStream* in_stream  = pFormatCtx->streams[i];             // 输入流
		AVCodecParameters* in_codeper = in_stream->codecpar;   // 编解码器
		
 
		// 只取音频流
		if (in_codeper->codec_type == AVMEDIA_TYPE_AUDIO)
		{
			// 建立输出流
			out_stream = avformat_new_stream(pFormatCtx_out, NULL);
			if (NULL == out_stream)
			{
				printf("Ffmpeg::extractAudio::[ERROR]建立输出流失败\n");
				return false;
			}
 
			// 拷贝编码参数,如果需要转码请不要直接拷贝
			// 这里只需要做音频的提取,对转码要求不高
			ret = avcodec_parameters_copy(out_stream->codecpar, in_codeper); // 将输入流的编码拷贝到输出流
			if (ret < 0)
			{
				printf("Ffmpeg::extractAudio::[ERROR]拷贝编码失败\n");
				return false;
			}
 
			out_stream->codecpar->codec_tag = 0;
			break;  // 拿到音频流就可以直接退出循环,这里我们只需要音频流
		}
	}
 
	av_dump_format(pFormatCtx_out, 0, szFilename, 1);
 
	// 解复用器,如果没有指定就使用pb
	if (!(pFormatCtx->flags & AVFMT_NOFILE))
	{
		ret = avio_open(&pFormatCtx_out->pb, szFilename, AVIO_FLAG_WRITE); // 读写
		if (ret < 0)
		{
			printf("Ffmpeg::extractAudio::[ERROR]创建AVIOContext对象:打开文件失败\n");
			return false;
		}
	}
	
	
	// 写入媒体文件头部
	ret = avformat_write_header(pFormatCtx_out, NULL);
	if (ret < 0)
	{
		printf("Ffmpeg::extractAudio::[ERROR]写入媒体头部失败\n");
		return false;
	}
 
 
	// 逐帧提取音频
	AVPacket* pkt = av_packet_alloc();
	while (av_read_frame(pFormatCtx, &packet) >=0 )
	{
		AVStream* in_stream  = NULL;
		AVStream* out_stream = NULL;
		in_stream = pFormatCtx->streams[pkt->stream_index];
		out_stream = pFormatCtx_out->streams[pkt->stream_index];
 
		if (packet.stream_index == audio_stream_index)
		{
 
			packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_INF|AV_ROUND_PASS_MINMAX));
			packet.dts = packet.pts;
			packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base);
			packet.pos = -1;
			packet.stream_index = 0;
 
			// 将包写到输出媒体文件
			av_interleaved_write_frame(pFormatCtx_out, &packet);
			// 减少引用计数,防止造成内存泄漏
			av_packet_unref(&packet);
		}
	}
 
 
	// 写入尾部信息
	av_write_trailer(pFormatCtx_out);
 
	// 释放
	av_packet_free(&pkt);
	avio_close(pFormatCtx_out->pb);
	avformat_close_input(&pFormatCtx);
	
 
    return true;
}

FastASR+FFmpeg,音视频开发+语音识别

3.性能对比

FastASR+FFmpeg,音视频开发+语音识别

三、视频文件中提取图片

1.FFmpeg

通过命令行

ffmpeg -i 视频文件名 -r 帧率 -f 输出格式 输出文件名

示例: ffmpeg -i video.mp4 -r 1 -f image2 image-%3d.png

参数说明:

-r 帧率(一秒内导出多少张图像,默认25)

-f 代表输出格式(image2实际上是image2序列的意思)

示例:

FastASR+FFmpeg,音视频开发+语音识别

FastASR+FFmpeg,音视频开发+语音识别

通过提供的API

bool AVInterface::extracPictrue(const char* src, const char* dstDir, int num)
{
 
 
    if(NULL == src || NULL == dstDir)
    {
        printf("Ffmpeg::extracPictrue[ERROR]::无效参数,请检查文件路径是否正确\n");
        return false;
    }
 
    int ret = 0;
    
    // 预存原文件路径
    const char* src_fileName = src;
    
    // 1.获取媒体文件的全局上下文信息
    
    // 1.1 定义 AVFormatContext 容器
    AVFormatContext* pFormatCtx = NULL;       // AVFormatContext描述了一个媒体文件或者媒体流构成的基本信息
    pFormatCtx = avformat_alloc_context();    // 为pFormatCtx申请内存
 
    // 1.2 打开媒体文件,并且读取媒体文件的头信息放入pFormatCtx中
    ret = avformat_open_input(&pFormatCtx, src_fileName, NULL, NULL);
    if(ret < 0)
    {
        printf("Ffmpeg::extracPictrue[ERROR]::打开媒体流文件失败\n");
        return false;
    }
 
 
    // 2.探测流信息
    
    // 2.1 探寻文件中是否存在信息流,如果存在则将多媒体文件信息流放到pFormatCtx中
    ret = avformat_find_stream_info(pFormatCtx, NULL);
    if(ret < 0)
    {
        printf("Ffmpeg::extracPictrue[ERROR]::文件中不存在信息流\n");
        return false;
    }
 
    av_dump_format(pFormatCtx, 0, src_fileName, 0);      // 可以打印查看
 
    // 2.2 查找文件信息流中是否存在视频流(这里我们需要提取图片),并获取到视频流在信息流中的索引
    int vecdio_stream_index = -1;
    vecdio_stream_index = av_find_best_stream(pFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if(-1 == vecdio_stream_index)
    {
        printf("Ffmpeg::extracPictrue[ERROR]::文件中不存在视频流\n");
        return false;
    }   // ----------> 丛林方法1
    
    
    // 3.找到对应的解码器:音视频文件是压缩之后的,我们要对文件内容进行处理,就必须先解码
    
    // 3.1 定*解义**码器的容器
    AVCodecContext* pCodeCtx = NULL;          // AVCodecContext描述编解码器的结构,包含了众多解码器的基本信息
    const AVCodec* pCodec = NULL;                   // AVCodec 存储解码器的信息
    
	pCodeCtx = avcodec_alloc_context3(NULL);  // 初始化解码器上下文
 
    // 3.2 查找解码器
    AVStream* pStream = pFormatCtx->streams[vecdio_stream_index]; // 在众多解码器找到视频处理的上下文信息
    pCodec = avcodec_find_decoder(pStream->codecpar->codec_id);          // 根据视频流获取视频解码器的基本信息
    if(NULL == pCodec)
    {
        printf("未发现视频编码器\n");
        return false;
    }
 
	// 初始化解码器上下文
	ret = avcodec_parameters_to_context(pCodeCtx, pStream->codecpar);
	if (ret < 0)
	{
		printf("初始化解码器上下文失败\n");
		return false;
	}
 
    // 3.3 打开解码器
    ret = avcodec_open2(pCodeCtx, pCodec, NULL);
    if(ret < 0)
    {
        printf("无法打开编解码\n");
        return false;
    }
 
 
	AVFrame* pFrame = NULL;
	pFrame = av_frame_alloc();
	if (NULL == pFrame)
	{
		printf("av_frame_alloc is error\n");
		return false;
	}
 
	int index = 0;
 
	AVPacket avpkt;
 
	while (av_read_frame(pFormatCtx, &avpkt) >= 0)
	{
		if (avpkt.stream_index == vecdio_stream_index)
		{
			ret = avcodec_send_packet(pCodeCtx, &avpkt);
			if (ret < 0)
			{
				continue;
			}
 
			while (avcodec_receive_frame(pCodeCtx, pFrame) == 0)
			{
				SaveFramePicture(pFrame, dstDir, index);
			}
			index++;
 
			if (index == num)
			{
				break;
			}
		}
 
		av_packet_unref(&avpkt);
	}
 
    avcodec_close(pCodeCtx);
    avformat_close_input(&pFormatCtx);
 
    return true;
 
}
 
 
bool AVInterface::SaveFramePicture(AVFrame* pFrame, const char* dstDir, int index)
{
    char szFilename[256] = {0};
    snprintf(szFilename, sizeof(szFilename), "%s/ffmpeg-%d.png", dstDir, index);
 
    int ret = 0;
 
	int width  = pFrame->width;
	int height = pFrame->height;
 
    // 1.初始化图片封装格式的结构体
    AVCodecContext*  pCodeCtx = NULL;
    AVFormatContext* pFormatCtx = NULL;
    pFormatCtx = avformat_alloc_context(); 
    
    // 2.设置封装格式
	// MJPEG格式:按照25帧/秒速度使用JPEG算法压缩视频信号,完成动态视频的压缩 --> 视频文件使用MJPEG进行解压
    pFormatCtx->oformat = av_guess_format("mjpeg", NULL, NULL);  // 用于从已经注册的输出格式中寻找最匹配的输出格式
 
    // 3.创建AVIOContext对象:打开文件  
    ret = avio_open(&pFormatCtx->pb, szFilename, AVIO_FLAG_READ_WRITE); // 读写方式
    if(ret < 0)
    {
        printf("avio_open is error");
        return false;
    }
 
 
    // 构建一个新的stream
    AVStream* pAVStream = NULL;
    pAVStream = avformat_new_stream(pFormatCtx, 0);
    if(pAVStream == NULL)
    {
        printf("avformat_new_stream\n");
        return false;
    }
 
    
    AVCodecParameters* parameters = NULL;                    // 编码器参数的结构体
    parameters = pAVStream->codecpar;                        // 设置编码器 mjpeg
    parameters->codec_id = pFormatCtx->oformat->video_codec; // 视频流
    parameters->codec_type = AVMEDIA_TYPE_VIDEO;             // 编码类型
    //parameters->format = AV_PIX_FMT_BGR24;                 // 指定图片的显示样式
	parameters->format = AV_PIX_FMT_YUVJ420P;                // YUV 解压缩显示样式都是YUV
    parameters->width  = pFrame->width;                      // 指定图片的宽度
    parameters->height = pFrame->height;                     // 显示图片的高度
    
 
    // 找到相应的解码器
    const AVCodec* pCodec = avcodec_find_encoder(pAVStream->codecpar->codec_id);
    if(NULL == pCodec)
    {
        printf("avcodec_find_encoder is error\n");
        return false;
    }
 
    // 初始化解码器上下文
    pCodeCtx = avcodec_alloc_context3(pCodec);
    if(NULL == pCodeCtx)
    {
        printf("avcodec_alloc_context3 is error\n");
        return false;
    }
 
    // 设置解码器的参数
    //ret = avcodec_parameters_to_context(pCodeCtx, pAVStream->codecpar);
	ret = avcodec_parameters_to_context(pCodeCtx, parameters);
	if(ret < 0)
    {
        printf("avcodec_parameters_to_context is error\n");
        return false;
    }
 
	AVRational avrational = {1, 25};       
	pCodeCtx->time_base = avrational;
 
	// 打开编解码器
    ret = avcodec_open2(pCodeCtx, pCodec, NULL);
    if(ret < 0)
    {
        printf("avcodec_open2 is error\n");
        return false;
    }
    
	
    // 封装格式的头部信息写入
    ret = avformat_write_header(pFormatCtx, NULL);
    if(ret < 0)
    {
        printf("avformat_write_header is error\n");
        return false;
    }
    
    // 给AVPacket分配足够大的空间
    int y_size = width * height;    // 分辨率
    AVPacket pkt;
    av_new_packet(&pkt, y_size * 3);
 
    // 编码数据
    ret = avcodec_send_frame(pCodeCtx, pFrame);
    if(ret < 0)
    {
        printf("avcodec_send_frame is error\n");
        return false;
    }
 
    // 得到解码之后的数据
    ret = avcodec_receive_packet(pCodeCtx, &pkt);
    if(ret < 0)
    {
        printf("avcodec_receive_packet is error\n");
        return false;
    }
 
    ret = av_write_frame(pFormatCtx, &pkt);
    if(ret < 0)
    {
        printf("av_write_frame is error\n");
        return false;
    }
 
 
	av_packet_unref(&pkt);
	av_write_trailer(pFormatCtx);
	avcodec_close(pCodeCtx);
	avio_close(pFormatCtx->pb);
	avformat_free_context(pFormatCtx);
 
    return true;
}

FastASR+FFmpeg,音视频开发+语音识别

3.性能对比

FastASR+FFmpeg,音视频开发+语音识别

四、音频文件中提取文字

1.百度智能云语音识别

FastASR+FFmpeg,音视频开发+语音识别

1.1百度智能云的优劣

FastASR+FFmpeg,音视频开发+语音识别

1.2 百度智能云安装配置

FastASR+FFmpeg,音视频开发+语音识别

1.4百度智能云使用示例

FastASR+FFmpeg,音视频开发+语音识别

SpeechRecognition开源离线语音识别

SpeechRecognition,是google出的,专注于语音向文本的转换。wit 和 apiai 提供了一些超出基本语音识别的内置功能,如识别讲话者意图的自然语言处理功能。

​​​​​SpeechRecognition的优/劣

FastASR+FFmpeg,音视频开发+语音识别

SpeechRecognition安装配置

FastASR+FFmpeg,音视频开发+语音识别

SpeechRecognition使用示例

FastASR+FFmpeg,音视频开发+语音识别

FastASR语音识别

这是一个用C++实现ASR推理的项目,它的依赖很少,安装也很简单,推理速度很快。支持的模型是由Google的Transformer模型中优化而来,数据集是开源。Wennetspeech(1000+小时)或阿里私有数据集(60000+小时),所以识别效果有很好,可以媲美许多商用的ASR软件。

流式模型:模拟的输入是语音流,并实时返回语音识别的结果,但是准确率会降低些。

FastASR+FFmpeg,音视频开发+语音识别

非流式模型:每次识别是以句子为单位,所以实时性会差一些,但是准确率会高一些。

FastASR+FFmpeg,音视频开发+语音识别

上面提到的这些模型都是基于深度学习框架(paddlepaddle和pytorch)实现的,本身的性能很不错,在个人电脑上运行,也能满足实时性要求(时长为10s的语言,推理视觉小于10s,即可满足实时性)。

FastASR的优/劣

语言优势:由于C++和Python不同,是编译型语言,编译器会根据编译选项针对不同的平台的CPU进行优化,更合适在不同CPU平台上面部署,充分利用CPU的计算资源。

实现独立:不依赖于现有的深度学习框架如pytorch、paddle、tensorflow等

依赖少:项目仅使用了两个第三方libfftw和libopenblas,并无其它依赖,所以在各个平台的可以移植性很好,通用性很强。

FastASR+FFmpeg,音视频开发+语音识别

  • 缺少量化和压缩模型
  • 支持C++ 和python

FastASR安装配置

  1. 依赖安装库 libfftw3

sudo apt-get install libfftw3-dev libfftw3-single3

安装依赖库 libopenblas

sudo apt-get install libopenblas-dev

安装python环境

sudo apt-get install python3 python3-dev

*载下**最新版的源码

git clone https://github.com/chenkui164/FastASR.git

编译最新版本的源码

cd FastASR/

mkdir build

cd build

cmake -DCMAKE_BUILD_TYPE=Release ..

make

编译python的whl安装包

cd FastASRpython -m build

cd FastASRpython -m build

*载下**预训练模型

paraformer预训练模型*载下**

cd ../models/paraformer_cli

1.从modelscope官网*载下**预训练模型

wget --user-agent="Mozilla/5.0" -c "https://www.modelscope.cn/api/v1/models/damo/speech_paraformer-large_asr_nat-zh-cn-16k-common-vocab8404-pytorch/repo?Revision=v1.0.4&FilePath=model.pb"

重命名

mv repo\?Revision\=v1.0.4\&FilePath\=model.pb model.pb

将用于Python的模型转换为C++的

../scripts/paraformer_convert.py model.pb

通过md5检查是否等于 c77bce5758ebdc28a9024460e48602

md5sum -b wenet_params.bin

K2_rnnt2预训练模型*载下**

cd ../models/k2_rnnt2_cli

1.从huggingface官网*载下**预训练模型

wget -c https://huggingface.co/luomingshuang/icefall_asr_wenetspeech_pruned_transducer_stateless2/resolve/main/exp/pretrained_epoch_10_avg_2.pt

2.将用于Python的模型转换为C++的

../scripts/k2_rnnt2_convert.py pretrained_epoch_10_avg_2.pt

3.通过md5检查是否等于 33a941f3c1a20a5adfb6f18006c11513

md5sum -b wenet_params.bin

PaddleSpeech预训练模型*载下**

1.从PaddleSpeech官网*载下**预训练模型

wget -c https://paddlespeech.bj.bcebos.com/s2t/wenetspeech/asr1_conformer_wenetspeech_ckpt_0.1.1.model.tar.gz

2.将压缩包解压wenetspeech目录下

mkdir wenetspeech

tar -xzvf asr1_conformer_wenetspeech_ckpt_0.1.1.model.tar.gz -C wenetspeech

3.将用于Python的模型转换为C++的

../scripts/paddlespeech_convert.py wenetspeech/exp/conformer/checkpoints/wenetspeech.pdparams

4.md5检查是否等于 9cfcf11ee70cb9423528b1f66a87eafd

md5sum -b wenet_params.bin

流模式预训练模型*载下**

cd ../models/paddlespeech_stream

从PaddleSpeech官网*载下**预训练模型

wget -c https://paddlespeech.bj.bcebos.com/s2t/wenetspeech/asr1/asr1_chunk_conformer_wenetspeech_ckpt_1.0.0a.model.tar.gz

2.将压缩包解压wenetspeech目录下

mkdir wenetspeech

tar -xzvf asr1_chunk_conformer_wenetspeech_ckpt_1.0.0a.model.tar.gz -C wenetspeech

3.将用于Python的模型转化为C++的

../scripts/paddlespeech_convert.py wenetspeech/exp/chunk_conformer/checkpoints/avg_10.pdparams

4.md5检查是否等于 367a285d43442ecfd9c9e5f5e1145b84

md5sum -b wenet_params.bin

​​​​​​​ FastASR使用示例

#include <iostream>
#include <win_func.h>
#include <Audio.h>
#include <Model.h>
#include <string.h>
 
using namespace std;
 
bool externContext(const char* src, const char* dst)
{
 
    Audio audio(0);           // 申请一个音频处理的对象
    audio.loadwav(src);       // 加载文件
    audio.disp();             // 分析格式
 
    // Model* mm = create_model("/home/chen/FastASR/models/k2_rnnt2_cli", 2); // 创建一个预训练模型
    Model* mm = create_model("/home/chen/FastASR/models/paraformer_cli", 3);
    audio.split();           // 解析文件
    
    float* buff = NULL;      // fftw3数据分析
    int len = 0;
    int flag = false;
    char buf[1024];
    
    // 一行一行的取出内容
    FILE* fp = NULL;
    fp = fopen(dst, "w+");
    if(NULL == fp)
    {
        printf("打开文件失败\n");
    }
    
    printf("0.---------------------->\n");
 
    while(audio.fetch(buff, len , flag) > 0)
    {
        printf("1.---------------------->\n");
 
        mm->reset();
        string msg = mm->forward(buff, len, flag);
 
        memset(buf, 0, sizeof(buf));
        snprintf(buf, sizeof(buf), "%s", msg.c_str());
        fseek(fp, 0, SEEK_END);
        fprintf(fp, "%s\n", buf);
        fflush(fp);
 
        printf("2.--------------------->\n");
    }
 
    printf("3.------------------------>\n");
    
    return true;
 
}
 
int main(void)
{
 
    externContext("./long.wav", "./Context.txt");
 
    return 0;
}

flags:= -I ./include
flags+= -L ./lib -lfastasr -lfftw3 -lfftw3f -lblas  -lwebrtcvad
src_cpp=$(wildcard ./*.cpp)
 
debug:
	g++ -g $(src_cpp) -omain $(flags) -std=c++11

FastASR+FFmpeg,音视频开发+语音识别

原文 FastASR+FFmpeg(音视频开发+语音识别)_ffmpeg语音识别_陈达书的博客-CSDN博客