实战--虚拟机用ffmpeg采集音频设备,并用rtmp推流

简介: 实战--虚拟机用ffmpeg采集音频设备,并用rtmp推流

使用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);
        }
    }
}


thxchtb3wcn3k_d11fc1ff3db9431d8ad7575e9297ae48.png

相关文章
|
29天前
|
编解码 vr&ar 内存技术
FFmpeg常用命令行讲解及实战一(三)
FFmpeg常用命令行讲解及实战一
42 0
|
29天前
|
编解码
FFmpeg常用命令行讲解及实战一(二)
FFmpeg常用命令行讲解及实战一
23 0
|
29天前
|
编解码 分布式计算 网络协议
FFmpeg常用命令行讲解及实战一(一)
FFmpeg常用命令行讲解及实战一
22 0
|
2月前
|
设计模式 存储 缓存
【ffmpeg C++ 播放器优化实战】优化你的视频播放器:使用策略模式和单例模式进行视频优化
【ffmpeg C++ 播放器优化实战】优化你的视频播放器:使用策略模式和单例模式进行视频优化
58 0
|
2月前
|
存储 Kubernetes 云计算
云计算基础与实战:从虚拟机到容器化应用
云计算基础与实战:从虚拟机到容器化应用
33 0
|
3月前
|
编解码 网络协议 Unix
相较于ffmpeg我更倾向于使用socket实现推流工作
相较于ffmpeg我更倾向于使用socket实现推流工作
32 0
|
4月前
[音视频 ffmpeg] 复用推流
[音视频 ffmpeg] 复用推流
|
4月前
【音视频 ffmpeg 】直播推流QT框架搭建
【音视频 ffmpeg 】直播推流QT框架搭建
|
4月前
|
网络协议 应用服务中间件 Linux
【音视频 ffmpeg 学习】 RTMP推流 mp4文件
【音视频 ffmpeg 学习】 RTMP推流 mp4文件
|
4月前
|
应用服务中间件 nginx Windows
ffmpeg推流到nginx服务器,并使用vlc播放rtmp视频
ffmpeg推流到nginx服务器,并使用vlc播放rtmp视频