实战--虚拟机用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

相关文章
|
1月前
|
应用服务中间件 Linux nginx
FFmpeg学习笔记(一):实现rtsp推流rtmp以及ffplay完成拉流操作
这篇博客介绍了如何使用FFmpeg实现RTSP推流到RTMP服务器,并使用ffplay进行拉流操作,包括在Windows和Linux系统下的命令示例,以及如何通过HTML页面显示视频流。
443 0
|
1月前
|
网络协议 应用服务中间件 nginx
FFmpeg错误笔记(一):nginx-rtmp-module推流出现 Server error: Already publishing
这篇文章讨论了在使用nginx-rtmp-module进行RTMP推流时遇到的“Server error: Already publishing”错误,分析了错误原因,并提供了详细的解决办法,包括修改nginx配置文件和终止异常的TCP连接。
149 0
FFmpeg错误笔记(一):nginx-rtmp-module推流出现 Server error: Already publishing
|
1月前
FFmpeg学习笔记(二):多线程rtsp推流和ffplay拉流操作,并储存为多路avi格式的视频
这篇博客主要介绍了如何使用FFmpeg进行多线程RTSP推流和ffplay拉流操作,以及如何将视频流保存为多路AVI格式的视频文件。
194 0
|
3月前
|
编解码 Java Android开发
FFmpeg开发笔记(四十五)使用SRT Streamer开启APP直播推流
​SRT Streamer是一个安卓手机端的开源SRT协议直播推流框架,可用于RTMP直播和SRT直播。SRT Streamer支持的视频编码包括H264、H265等等,支持的音频编码包括AAC、OPUS等等,可谓功能强大的APP直播框架。另一款APP直播框架RTMP Streamer支持RTMP直播和RTSP直播,不支持SRT协议的直播。而本文讲述的SRT Streamer支持RTMP直播和SRT直播,不支持RTSP协议的直播。有关RTMP Streamer的说明参见之前的文章《使用RTMP Streamer开启APP直播推流》,下面介绍如何使用SRT Streamer开启手机直播。
81 4
FFmpeg开发笔记(四十五)使用SRT Streamer开启APP直播推流
|
4月前
|
视频直播 Windows
FFmpeg开发笔记(四十一)结合OBS与MediaMTX实现SRT直播推流
《FFmpeg开发实战》书中介绍了直播中的RTSP、RTMP和SRT协议,SRT提供更低延迟和稳定性。FFmpeg从4.0版起支持SRT,OBS Studio和MediaMTX等工具也已支持。在Windows环境下,通过集成libsrt的FFmpeg,可以建立SRT直播系统。MediaMTX日志显示SRT服务监听8890端口,OBS Studio设置SRT推流至"publish:live"。ffplay和VLC通过"read:live"拉流成功,验证了SRT推拉流功能。更多详情见《FFmpeg开发实战:从零基础到短视频上线》。
174 2
FFmpeg开发笔记(四十一)结合OBS与MediaMTX实现SRT直播推流
|
4月前
|
Web App开发 缓存 编解码
FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址
《FFmpeg开发实战》书中介绍了轻量级流媒体服务器MediaMTX,适合测试RTSP/RTMP协议,但不适用于复杂直播场景。SRS是一款强大的开源流媒体服务器,支持多种协议,起初为RTMP,现扩展至HLS、SRT等。在FFmpeg 6.1之前,推送给SRS的HEVC流不受支持。要播放RTMP流,Android应用可使用ExoPlayer,需在`build.gradle`导入ExoPlayer及RTMP扩展,并根据URL类型创建MediaSource。若SRS播放黑屏,需在配置文件中开启`gop_cache`以缓存关键帧。
157 2
FFmpeg开发笔记(三十八)APP如何访问SRS推流的RTMP直播地址
|
4月前
|
应用服务中间件 Linux nginx
FFmpeg开发笔记(四十)Nginx集成rtmp模块实现RTMP推拉流
《FFmpeg开发实战》书中介绍了如何使用FFmpeg向网络推流,简单流媒体服务器MediaMTX不适用于复杂业务。nginx-rtmp是Nginx的RTMP模块,提供基本流媒体服务。要在Linux上集成rtmp,需从官方下载nginx和nginx-rtmp-module源码,解压后在nginx目录配置并添加rtmp模块,编译安装。配置nginx.conf启用RTMP服务,监听1935端口。使用ffmpeg推流测试,如能通过VLC播放,表明nginx-rtmp运行正常。更多详情见书本。
122 0
FFmpeg开发笔记(四十)Nginx集成rtmp模块实现RTMP推拉流
|
4月前
|
缓存 监控 Java
Java虚拟机(JVM)性能调优实战指南
在追求软件开发卓越的征途中,Java虚拟机(JVM)性能调优是一个不可或缺的环节。本文将通过具体的数据和案例,深入探讨JVM性能调优的理论基础与实践技巧,旨在为广大Java开发者提供一套系统化的性能优化方案。文章首先剖析了JVM内存管理机制的工作原理,然后通过对比分析不同垃圾收集器的适用场景及性能表现,为读者揭示了选择合适垃圾回收策略的数据支持。接下来,结合线程管理和JIT编译优化等高级话题,文章详细阐述了如何利用现代JVM提供的丰富工具进行问题诊断和性能监控。最后,通过实际案例分析,展示了性能调优过程中可能遇到的挑战及应对策略,确保读者能够将理论运用于实践,有效提升Java应用的性能。 【
203 10
|
3月前
|
机器学习/深度学习 编解码 API
【机器学习】FFmpeg+Whisper:二阶段法视频理解(video-to-text)大模型实战
【机器学习】FFmpeg+Whisper:二阶段法视频理解(video-to-text)大模型实战
56 0
|
1月前
|
Linux 开发工具 Android开发
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
ijkplayer是由Bilibili基于FFmpeg3.4研发并开源的播放器,适用于Android和iOS,支持本地视频及网络流媒体播放。本文详细介绍如何在新版Android Studio中导入并使用ijkplayer库,包括Gradle版本及配置更新、导入编译好的so文件以及添加直播链接播放代码等步骤,帮助开发者顺利进行App调试与开发。更多FFmpeg开发知识可参考《FFmpeg开发实战:从零基础到短视频上线》。
115 2
FFmpeg开发笔记(六十)使用国产的ijkplayer播放器观看网络视频
下一篇
无影云桌面