使用ffmpeg访问系统音频设备alsa驱动,从音频设备中读取音频流,读出的是pcm数据,然后编码推流到rtmp服务器。
关于流媒体服务器的搭建,以及配置windows使得虚拟机可以访问到pc的音频或视频设备,在之前的文章中有专门介绍。
#include <string>
#include <iostream>
using namespace std;
extern "C"
{
#include <libavcodec/avcodec.h>
#include <libavformat/avformat.h>
#include <libavdevice/avdevice.h>
#include <libswscale/swscale.h>
#include <libavutil/opt.h>
#include <libavcodec/avcodec.h>
#include <libavutil/channel_layout.h>
#include <libavutil/common.h>
#include <libavutil/imgutils.h>
#include <libavutil/mathematics.h>
#include <libavutil/samplefmt.h>
#include <libavutil/frame.h>
#include <libavformat/avformat.h>
#include <libswscale/swscale.h>
}
//8000 11025 12000 16000 22050 24000 32000 44100 48000 64000 88200 96000
void ErrorFunc(int errNum)
{
char buf[1024] = {0};
av_strerror(errNum, buf, sizeof(buf));
cout << "@@@" << buf << "@@@" << endl;
exit(1);
}
int main(void)
{
av_register_all();
avcodec_register_all();
avformat_network_init();
avdevice_register_all();
AVInputFormat *ifmt_a = NULL;
ifmt_a = av_find_input_format("alsa");
if (ifmt_a == NULL)
{
printf("无法找到设备\n");
}
// av_dict_set(&options, "")
AVFormatContext *ic_a = NULL;
AVDictionary *options = NULL;
// av_dict_set(&options, "video_size", "1920x1080", 0);//sample_rate
// av_dict_set(&options, "framerate", "30", 0);
av_dict_set(&options, "sample_rate", "48000", 0);//只能是48000不支持改动
av_dict_set(&options, "channels", "2", 0);//无法改,不支持改动
//null:131072 sysdefault:3760 pulse:512 default:512 front:64 dsnoop:4096(正常) hw:64(鬼声) plughw:64(鬼声)
//null:131072但要是1024 sysdefault:3760(能听到,有噪音,无缓存,) pulse:512(听不到,无缓存) default:512(听不到,无缓存) front:64 dsnoop:4096(清晰,无缓存)
int re = avformat_open_input(&ic_a, "dsnoop", ifmt_a, &options);
if (re != 0)
{
printf("无法打开输入流\n");
ErrorFunc(re);
}
re = avformat_find_stream_info(ic_a, NULL);
if (re != 0)
{
printf("找不到流信息\n");
ErrorFunc(re);
}
int audio_index = -1;
for (int i = 0; i < ic_a->nb_streams; i++)
{
if (ic_a->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO) //AVMEDIA_TYPE_VIDEO
{
// ic->streams[i]->codecpar
audio_index = i;
}
}
if (audio_index == -1)
{
printf("找不到视频流\n");
}
av_dump_format(ic_a, audio_index, "dsnoop", 0);//第四个参数填1则段错误
printf("here\n");
AVCodec *encodec_a = NULL;
encodec_a = avcodec_find_encoder_by_name("libfdk_aac");
if (encodec_a == NULL)
{
printf("找不到编码器\n");
exit(1);
}
AVCodecContext *encodec_ctx_a = NULL;
encodec_ctx_a = avcodec_alloc_context3(encodec_a);
if (encodec_ctx_a == NULL)
{
printf("申请编码器上下文失败\n");
exit(1);
}
encodec_ctx_a->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
encodec_ctx_a->channel_layout = AV_CH_LAYOUT_STEREO;
encodec_ctx_a->channels = 2;
encodec_ctx_a->sample_rate = 48000;
encodec_ctx_a->sample_fmt = AV_SAMPLE_FMT_S16;
// encodec_ctx_a->framerate = (AVRational){44100, 128};
// encodec_ctx_a->time_base = (AVRational){128, 44100};
re = avcodec_open2(encodec_ctx_a, encodec_a, NULL);
if (re != 0)
{
printf("打开编码器失败\n");
ErrorFunc(re);
}
AVFormatContext *oc_a = NULL;
char *URL_a = "rtmp://127.0.0.1/live";
re = avformat_alloc_output_context2(&oc_a, NULL, "flv", URL_a);
AVStream *o_stream_a = NULL;
o_stream_a = avformat_new_stream(oc_a, NULL); //第二个参数
if (o_stream_a == NULL)
{
printf("fail to new stream\n");
ErrorFunc(re);
}
avcodec_parameters_from_context(o_stream_a->codecpar, encodec_ctx_a);
// av_dump_format(oc_a, 0, URL_a, 1);
re = avio_open(&oc_a->pb, URL_a, AVIO_FLAG_WRITE);
if (re < 0)
{
printf("打开网络IO失败\n");
ErrorFunc(re);
}
re = avformat_write_header(oc_a, NULL);
AVPacket read_pkt_a;
av_init_packet(&read_pkt_a); //?
AVFrame *p_pcm = NULL;
p_pcm = av_frame_alloc();
p_pcm->format = AV_SAMPLE_FMT_S16;
p_pcm->nb_samples = 1024; //1024/30
p_pcm->channels = 2;
p_pcm->channel_layout = AV_CH_LAYOUT_STEREO;
re = av_frame_get_buffer(p_pcm, 0);
if (re != 0)
{
printf("fail to get buffer for p_pcm\n");
ErrorFunc(re);
}
AVPacket send_pkt_a;
av_init_packet(&send_pkt_a);
int count_a = 0;
while (1)
{
av_init_packet(&read_pkt_a);
re = av_read_frame(ic_a, &read_pkt_a);
if (re < 0)
{
printf("read_frame error\n");
ErrorFunc(re);
}
// printf("here_mem\n");
// p_pcm->data = read_pkt_a.data;
printf("read_pkt_a.size = %d\n", read_pkt_a.size);
memcpy(p_pcm->data[0], read_pkt_a.data, 1024*2*2);
// read_pkt_a.side_data
// printf("here_mem_2\n");
count_a++;
p_pcm->pts = count_a;
re = avcodec_send_frame(encodec_ctx_a, p_pcm);
if (re < 0)
{
printf("fail to send encode frame error = %d\n", re);
ErrorFunc(re);
}
re = avcodec_receive_packet(encodec_ctx_a, &send_pkt_a);
if (re == AVERROR(EAGAIN) || re == AVERROR_EOF)
{
// av_init_packet(&send_pkt);
// av_packet_unref(&send_pkt);
// av_packet_unref(&send_pkt);
// printf("here_unref\n");
//记录前50帧都进入这里,说明缓冲了50帧,以后每个send,对应一个receive
continue;
}
else if (re < 0)
{
printf("encode receive packet error = ret\n", re);
ErrorFunc(re);
}
{
send_pkt_a.pts = av_rescale_q(send_pkt_a.pts, encodec_ctx_a->time_base, o_stream_a->time_base);
send_pkt_a.dts = av_rescale_q(send_pkt_a.dts, encodec_ctx_a->time_base, o_stream_a->time_base);
send_pkt_a.duration = av_rescale_q(send_pkt_a.duration, encodec_ctx_a->time_base, o_stream_a->time_base);
printf("size = %d\n", send_pkt_a.size);
re = av_interleaved_write_frame(oc_a, &send_pkt_a);
if (re != 0)
{
printf("write network err = %d\n", re);
ErrorFunc(re);
}
printf("write\n");
// av_packet_unref(&send_pkt);
}
}
}
![](https://i-blog.csdnimg.cn/blog_migrate/0d6108b524cdd0722dbddcd12096157e.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/1c1655332b870f79ad91e0b03a52d7df.jpeg)
![](https://i-blog.csdnimg.cn/blog_migrate/0d6108b524cdd0722dbddcd12096157e.jpeg)