直播系统的组成部分一:音视频采集—音频采集部分
文章分为以下几个部分:
1.前言
2.获取音频输入设备的名称
3.代码示例
前言
在直播系统当中,推流之前,数据采集不止要采集视频数据,还要采集音频数据,然后将画面和音频同步进行封装,再逐帧进行推流,经过流媒体服务器转发之后,最终呈现在用户面前的才是能看到画面也能听到声音的效果。篇文章先来介绍如何采集音频数据。
获取音频输入设备的名称
之前的文章讲过如何获取摄像头的设备名称:
http://www.lindasoft.com/view/article/details?articleId=655
不过麦克风的设备名称中好多都带有中文,当运行代码或运行命令查看的时候,显示的结果往往像下图这样:
汉字部分是一串乱码,造成这个问题的原因是ffmpeg用的编码和Windows终端不一样,Windows终端用的是GBK,ffmpeg用的是UTF-8。
怎么解决这一问题呢?我们可以通过运行命令并把运行的结果信息输出重定向到文本文件中。命令如下:
ffmpeg -list_devices true -f dshow -i dummy 2>E:/out.txt
打开文本文件你会发现,汉字就能正常显示了:
如图我的音频输入设备叫:麦克风阵列 (Realtek High Definition Audio)。设备名称接下来会用到。
代码示例
获取音频和之前的获取视频的demo可以对比着看,有些地方比较相似。代码中关键处做了注释,下面通过代码来了解一下整体流程和代码逻辑:
#include <stdio.h>
#include <QString>
#include <QDebug>
extern "C"
{
#include "libavcodec/avcodec.h"
#include "libavformat/avformat.h"
#include "libavutil/pixfmt.h"
#include "libswscale/swscale.h"
#include "libavdevice/avdevice.h"
}
//获取麦克风音频
void getVoice()
{
AVFormatContext *aFormatCtx;//包含了封装格式的数据。
AVCodecContext *aCodecCtx;//码流数据的解码方式相关数据。
AVCodec *aCodec;//AVCodecContext中所包含的解码器信息。
AVFrame *aFrame;//存储压缩编码数据相关信息的结构体。
AVPacket *packet;//解码后的数据
uint8_t *out_buffer;
AVInputFormat *ifmt;
int audioStream, i;
int ret, got_frame;
av_register_all(); //初始化FFMPEG 调用了这个才能正常适用编码器和解码器
avdevice_register_all();//初始化设备
printf("aaaaaa\n");
aFormatCtx = avformat_alloc_context();
//打开麦克风,这里用到音频输入设备的设备名称
QString audioDeviceName = QStringLiteral("audio=麦克风阵列 (Realtek High Definition Audio)");
ifmt = av_find_input_format("dshow");
if(avformat_open_input(&aFormatCtx,audioDeviceName.toUtf8().data(),ifmt,NULL)!=0){
//fprintf(stderr,"Couldn't open input stream.(无法打开输入流)");
printf("Couldn't open input stream.\n");
return;
}
else
{
printf("open input stream OK.\n");
}
audioStream = -1;
///循环查找视频中包含的流信息,直到找到视频类型的流
///便将其记录下来 保存到audioStream变量中
///这里我们现在只处理视频流 音频流先不管他
for (i = 0; i < aFormatCtx->nb_streams; i++) {
if (aFormatCtx->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) {//一定注意这个地方不要和视频流的类型弄混了
audioStream = i;
}
}
///如果audioStream为-1 说明没有找到音频流
if (audioStream == -1) {
printf("Didn't find a video stream.\n");
return;
}
else
{
printf("find a video stream.\n");
}
///查找解码器
aCodecCtx = aFormatCtx->streams[audioStream]->codec;
aCodec = avcodec_find_decoder(aCodecCtx->codec_id);
if (aCodec == NULL) {
printf("Codec not found.\n");
return;
}
else
{
printf("Codec is found.\n");
}
printf("bbbbbbb\n");
///打开解码器
if (avcodec_open2(aCodecCtx, aCodec, NULL) < 0) {
printf("Could not open codec.\n");
return;
}
else
{
printf("打开解码器\n");
}
aFrame = av_frame_alloc();
packet=(AVPacket *)av_malloc(sizeof(AVPacket));
FILE *fp_pcm=fopen("output.pcm","wb");
///这里打印出音频的信息
qDebug()<<"audio info:";
qDebug()<<"audio info:"<<aCodecCtx->sample_fmt<<aCodecCtx->bit_rate<<aCodecCtx->sample_rate<<aCodecCtx->channels;
float Time = 0;
for(int i=0;;i++)
{
if (Time > 10) break; //就采集10秒
if(av_read_frame(aFormatCtx, packet) < 0)
{
break;
}
if(packet->stream_index==audioStream)
{
//编码
ret = avcodec_decode_audio4(aCodecCtx, aFrame, &got_frame, packet);
if(ret < 0)
{
fprintf(stderr,"Audio Error.");
return;
}
//转换数据,写入文件
if (got_frame)
{
int pcmSize = av_samples_get_buffer_size(NULL,aCodecCtx->channels, aFrame->nb_samples,aCodecCtx->sample_fmt, 1);
uint8_t * pcmBuffer = aFrame->data[0];
float useTime = aFrame->nb_samples * 1.0 / aCodecCtx->sample_rate;
Time += useTime;
qDebug()<<i<<Time<<useTime;
fwrite(pcmBuffer,1,pcmSize,fp_pcm); //写入文件
}
}
av_free_packet(packet);
}
printf("END\n");
fclose(fp_pcm);
av_free(out_buffer);
avcodec_close(aCodecCtx);
avformat_close_input(&aFormatCtx);
}
int main()
{
getVoice();
return 0;
}
{{ cmt.username }}
{{ cmt.content }}
{{ cmt.commentDate | formatDate('YYYY.MM.DD hh:mm') }}