ffmpeg取rtsp流音频数据保存声音为wav文件

本来不是什么难搞的问题,代码写完了,音频流信息中的详细信息,具体代码表现为

format_ctx->streams[audio_stream_index]->codecpar是空指针。

这个查了一圈也没人给出正确答案,实际上是由于我自己编译的ffmpeg时候,开启的选项的导致的。把音频解码器相关的给禁掉了。重新开启相关编译选项,编译ffmpeg后,一切正常。

具体的选项为:

ffmpeg 交叉编译

./configure --prefix=../arm-ffmpegbuild \
--enable-shared \
--enable-libmp3lame \
 --enable-libx264 \
 --enable-gpl \
 --disable-asm \
 --enable-version3 \
 --enable-libmp3lame \
 --enable-libx264 \
 --enable-libvpx \
 --enable-nonfree \
 --cross-prefix=aarch64-linux- \
 --target-os=linux \
 --extra-cflags="-I /opt/ffmpeg_test_make/lame-3.100/lamebuild/include" \
 --extra-ldflags="-L /opt/ffmpeg_test_make/lame-3.100/lamebuild/lib" \
 --enable-cross-compile \
 --enable-small \
 --arch=arm64 \
 --enable-decoder=h264 \
 --enable-parser=h264 \
 --enable-demuxer=rtsp \
 --extra-ldflags="-L ../x264build/lib" \
 --extra-cflags="-I ../x264build/include"
 
 lame交叉编译
 ./configure \
 --host=aarch64-linux \
 --prefix=/opt/ffmpeg_test_make/lame-3.100/lamebuild \
 cc=aarch64-linux-gcc 

话不多说上代码:


bool FfpDecoderWav::dump_wav(std::string rtsp_url, std::string file_path) {
    AVDictionary *format_options = NULL;
    av_dict_set(&format_options, "rtsp_transport", "tcp", 0); // 以tcp的方式打开,
    av_register_all();
    avformat_network_init();
    // 打开 RTSP 流
    int reconnect_times = 3;
    AVFormatContext *format_ctx = NULL;
    bool online = false;
    while (reconnect_times-- > 0) {
        if (format_ctx != NULL) {
            avformat_close_input(&format_ctx);
            format_ctx = NULL;
        }
        format_ctx = avformat_alloc_context();
        if (avformat_open_input(&format_ctx, rtsp_url.c_str(), NULL, &format_options) != 0) {
            Logger::error("open rtsp url:{} faile", rtsp_url);
            // std::this_thread::sleep_for(std::chrono::milliseconds(500));
            usleep(100000);
        } else {
            online = true;
            break;
        }
    }
    av_dict_free(&format_options); // 释放 format_options
    if (!online) {
        return false;
    }
    Logger::info("open rtsp url:{} for wav success", rtsp_url);
    // 查找音频流
    int audio_stream_index = -1;

    if (avformat_find_stream_info(format_ctx, NULL) < 0) {
        Logger::info("can not avformat_find_stream_info url:{}", rtsp_url);
        return false;
    }
    AVCodec *codec = NULL;
    audio_stream_index = av_find_best_stream(format_ctx, AVMEDIA_TYPE_AUDIO, -1, -1, &codec, 0);

    std::cout << "codec name :" << codec->name << std::endl;
    std::cout << "codec long_name :" << codec->long_name << std::endl;
    std::cout << "codec AVMediaType :" << (int)codec->type << std::endl;
    std::cout << "codec AVCodecID :" << (int)codec->id << std::endl;

    if (audio_stream_index < 0 || codec == NULL) {
        Logger::info("can not find sound stream rtsp url:{}", rtsp_url);
        return false;
    }
    Logger::info("find sound stream success index:{}", audio_stream_index);

    av_dump_format(format_ctx, 0, rtsp_url.c_str(), 0);
    bool had_audio_code = true;
    SwrContext *swr_ctx = NULL;
    AVCodecContext *codec_ctx = avcodec_alloc_context3(codec);

    if (format_ctx->streams[audio_stream_index]->codecpar) {
        Logger::info("avcodec_alloc_context3 success channels={}", codec_ctx->channels);
        Logger::info("avcodec_alloc_context3 success sample_rate={}", codec_ctx->sample_rate);
        // std::cout << "had codecpar inf" << std::endl;
        // printf("had codecpar inf\n");
        avcodec_parameters_to_context(codec_ctx, format_ctx->streams[audio_stream_index]->codecpar);
        if (avcodec_open2(codec_ctx, codec, NULL) < 0) {
            Logger::info("avcodec_open2 error rtsp url:{}", rtsp_url);
            return false;
        }
        Logger::info("avcodec_open2 success channels={}", codec_ctx->channels);
        Logger::info("avcodec_open2 success sample_rate={}", codec_ctx->sample_rate);
        // 创建重采样上下文

        swr_ctx = swr_alloc_set_opts(NULL, NUM_CHANNELS, AV_SAMPLE_FMT_S16, SAMPLE_RATE, codec_ctx->channels,
                                     codec_ctx->sample_fmt, codec_ctx->sample_rate, 0, NULL);
        Logger::info("swr_alloc_set_opts success");
        if (!swr_ctx || swr_init(swr_ctx) < 0) {
            // Logger::info("swr_init error rtsp url:{}", rtsp_url);
            return false;
        }
    } else {
        printf("cdecpar is nullodecpar is nullodecpar is nullodecpar is null\n");
        std::cout << "codecpar is null" << std::endl;
        had_audio_code = false;
    }

    // 创建输出 WAV 文件
    std::ofstream wav_file(file_path.c_str(), std::ios::binary);
    if (!wav_file) {
        // Logger::info("fopen local_path save wav failed path:{}", file_path);
        return false;
    }
    // Logger::info("open wav_file success");
    //  写入 WAV 文件头
    WAVHeader wav_header;
    unsigned int file_size = sizeof(wav_header);
    // Logger::info("wav_header size:{}", file_size);
    wav_file.write((const char *)&wav_header, file_size);
    time_t start_time = time(NULL);
    AVPacket packet;
    int ret = 0;
    int count = 1000;
    while (true) {
        if (ret = av_read_frame(format_ctx, &packet) < 0) {
            // Logger::info("av_read_frame failed: {}", ret);
            break;
        }
        time_t current_time = time(NULL);
        time_t duration = current_time - start_time;
        if (duration > 60) {
            // Logger::info("save sound end by 20 s time");
            break;
        }
        if (packet.stream_index == audio_stream_index) {

            if (!had_audio_code) {
                wav_file.write((char *)packet.data, packet.size);
                std::cout << "write sws data codecpar inf insfsjfjaslkjfas" << std::endl;
                printf(" wav_file.write((char *)packet.data, packet.size);\n");
                continue;
            }
            AVFrame *frame = av_frame_alloc();
            if (avcodec_send_packet(codec_ctx, &packet) >= 0 && avcodec_receive_frame(codec_ctx, frame) >= 0) {
                uint8_t *out_buffer[NUM_CHANNELS];
                int out_samples = 0;
                int out_size = 0;
                for (int i = 0; i < NUM_CHANNELS; i++) {
                    out_buffer[i] = (uint8_t *)malloc(frame->nb_samples * 2 * sizeof(uint8_t));
                }
                out_samples = swr_convert(swr_ctx, out_buffer, frame->nb_samples, (const uint8_t **)frame->data,
                                          frame->nb_samples);
                out_size = out_samples * NUM_CHANNELS * 2;
                wav_file.write(reinterpret_cast<char *>(out_buffer[0]), out_size);
                // std::cout << "write sws data codecpar inf" << std::endl;
                // printf(" wav_file.write(reinterpret_cast<char *>(out_buffer[0]), out_siz22;\n");
                for (int i = 0; i < NUM_CHANNELS; i++) {
                    free(out_buffer[i]);
                }
            }
            av_frame_free(&frame);
        }
        av_packet_unref(&packet);
    }
    // 更新 WAV 文件头中的数据大小
    uint32_t subchunk2Size = static_cast<unsigned int>(wav_file.tellp()) - 44;
    uint32_t chunkSize = subchunk2Size + 36;
    wav_file.seekp(4, std::ios::beg);
    wav_file.write(reinterpret_cast<char *>(&chunkSize), 4);

    wav_file.seekp(40, std::ios::beg);
    wav_file.write(reinterpret_cast<char *>(&subchunk2Size), 4);
    // 关闭文件
    wav_file.close();
    // 释放资源
    avcodec_close(codec_ctx);
    avcodec_free_context(&codec_ctx);
    avformat_close_input(&format_ctx);
    swr_free(&swr_ctx);

    // Logger::info("save local_path  wav success path:{}", file_path);

    return true;
}

wav格式的数据头文件:

struct WAVHeader {
    char chunkID[4] = {'R', 'I', 'F', 'F'};
    uint32_t chunkSize = 0;
    char format[4] = {'W', 'A', 'V', 'E'};
    char subchunk1ID[4] = {'f', 'm', 't', ' '};
    uint32_t subchunk1Size = 16;
    uint16_t audioFormat = 1;
    uint16_t numChannels = NUM_CHANNELS;
    uint32_t sampleRate = SAMPLE_RATE;
    uint32_t byteRate = SAMPLE_RATE * NUM_CHANNELS * 16 / 8;
    uint16_t blockAlign = 4;
    uint16_t bitsPerSample = 16;
    char subchunk2ID[4] = {'d', 'a', 't', 'a'};
    uint32_t subchunk2Size = 4;
};

最后,就是wav注意的地方,一共是两个值:

chunkSize 和subchunk2Size

// 更新 WAV 文件头中的数据大小

也就是说:subchunk2Size是出去wav文件头部数据意外的数据长度。

即文件总长度减去头部长度44个字节。

chunkSize=subchunk2Size+36

具体为什么,可以查看wav格式的说明。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:/a/887129.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

Pikachu-暴力破解-验证码绕过(on client)

访问页面&#xff0c; 从burpsuite 上看到返回的源代码&#xff1b; 验证码生成时通过 createCode 方法生成&#xff0c;在前端页面生成&#xff1b; 同时也是在前端做的校验&#xff1b; 直接验证&#xff1b;F12 -- 网络&#xff0c;随便输入个账号、密码、验证码&#xff0…

C初阶(八)选择结构(分支结构)--if、else、switch

前言&#xff1a; C语言是用来解决问题的&#xff0c;除了必要的数据输入与输出&#xff08;见前文&#xff09;&#xff0c;还要有逻辑结构。其中基本可以归为三类&#xff1a;顺序结构、选择结构、循环结构。今天&#xff0c;杰哥提笔写的是关于选择结构&#xff08;又叫“分…

CSP-J Day 5 模拟赛补题报告

姓名&#xff1a;王胤皓&#xff0c;校区&#xff1a;和谐校区&#xff0c;考试时间&#xff1a; 2024 2024 2024 年 10 10 10 月 5 5 5 日 9 : 00 : 00 9:00:00 9:00:00~ 12 : 30 : 00 12:30:00 12:30:00&#xff0c;学号&#xff1a; S 07738 S07738 S07738 请关注作者的…

9.30学习记录(补)

手撕线程池: 1.进程:进程就是运行中的程序 2.线程的最大数量取决于CPU的核数 3.创建线程 thread t1; 在使用多线程时&#xff0c;由于线程是由上至下走的&#xff0c;所以主程序要等待线程全部执行完才能结束否则就会发生报错。通过thread.join()来实现 但是如果在一个比…

CentOS 替换 yum源 经验分享

视频教程在bilibili:CentOS 替换 yum源 经验分享_哔哩哔哩_bilibili问题原因 解决方法 1. 进入镜像目录 [rootlocalhost ~]# cd /etc/yum.repos.d/ 2.备份文件 [rootlocalhost yum.repos.d]# rename repo bak * 3.寻找阿里镜像源复制 https://developer.aliyun.com/mirror/ …

Redis基础三(redis的高级配置)

Redis进阶配置 一、Redis持久化操作 ​ 持久化就是把内存的数据写到磁盘中去&#xff0c;防止服务宕机了内存数据丢失。&#xff08;Redis 数据都放在内存中。如果机器挂掉&#xff0c;内存的数据就不存在。所以需要做持久化&#xff0c;将内存中的数据保存在磁盘&#xff0c…

聊聊Mysql的MVCC

1 什么是MVCC&#xff1f; MVCC&#xff0c;是Multiversion Concurrency Control的缩写&#xff0c;翻译过来是多版本并发控制&#xff0c;和数据库锁一样&#xff0c;他也是一种并发控制的解决方案。 我们知道&#xff0c;在数据库中&#xff0c;对数据的操作主要有2种&#…

分享9个论文写作中强化观点三要素的奇技淫巧

学境思源&#xff0c;一键生成论文初稿&#xff1a; AcademicIdeas - 学境思源AI论文写作 在学术写作中&#xff0c;强化观点的表达至关重要&#xff0c;它不仅能够提升论文的说服力&#xff0c;还能使论点更加明确和有力。为了帮助作者更有效地传达观点&#xff0c;本文将分享…

Leetcode 1631. 最小体力消耗路径

1.题目基本信息 1.1.题目描述 你准备参加一场远足活动。给你一个二维 rows x columns 的地图 heights &#xff0c;其中 heights[row][col] 表示格子 (row, col) 的高度。一开始你在最左上角的格子 (0, 0) &#xff0c;且你希望去最右下角的格子 (rows-1, columns-1) &#x…

【Godot4.3】复合路径类myPath

概述 之前编写过一个基于指令绘图的类交myPoint&#xff0c;但是只涉及折线段生成。这次我基于SVG的<path>标签路径指令的启发&#xff0c;实现了一个能够获得连续绘制的直线段、圆弧和贝塞尔复合路径的类型myPath。 可以使用绘图指令方法或字符串形式的绘图指令解析来…

MATLAB|基于多主体主从博弈的区域综合能源系统低碳经济优化调度

目录 主要内容 程序亮点&#xff1a; 模型研究 一、综合能源模型 二、主从博弈框架 部分代码 结果一览 下载链接 主要内容 程序参考文献《基于多主体主从博弈的区域综合能源系统低碳经济优化调度》&#xff0c;采用了区域综合能源系统多主体博弈协同优化方…

【重学 MySQL】五十二、MySQL8 新特性:计算列

【重学 MySQL】五十二、MySQL8 新特性&#xff1a;计算列 定义特性用法应用场景注意事项 在MySQL8中&#xff0c;计算列是一项引入的新特性&#xff0c;它为数据处理和分析提供了更大的灵活性和便捷性。 定义 计算列是指根据数据库中其他列的值通过计算得出的新列&#xff0c…

反调试—1

IsDebuggerPresent() CheckRemoteDebuggerPresent() 其内部实际调用NtQueryInformationProcess() bool _stdcall ThreadCall() {while (true){BOOL pbDebuggerPresent FALSE;CheckRemoteDebuggerPresent(GetCurrentProcess(), &pbDebuggerPresent);if (pbDebuggerPres…

Leetcode: 0011-0020题速览

Leetcode: 0011-0020题速览 本文材料来自于LeetCode solutions in any programming language | 多种编程语言实现 LeetCode、《剑指 Offer&#xff08;第 2 版&#xff09;》、《程序员面试金典&#xff08;第 6 版&#xff09;》题解 遵从开源协议为知识共享 版权归属-相同方式…

【持续更新中】MMDetection3训练自己的数据集常见报错解决

博主近来跑自己数据集需要对比试验&#xff0c;故选择了MMDetection3这一算法整合详细的框架&#xff0c;遇到了较多问题在此处留作记录&#xff0c;若你也有相应的问题可以在评论区提出与解决方法。会持续更新&#xff0c;同时欢迎批评指正。 0.ModuleNotFoundError: No modu…

微信小程序hbuilderx+uniapp+Android 新农村综合风貌旅游展示平台

目录 项目介绍支持以下技术栈&#xff1a;具体实现截图HBuilderXuniappmysql数据库与主流编程语言java类核心代码部分展示登录的业务流程的顺序是&#xff1a;数据库设计性能分析操作可行性技术可行性系统安全性数据完整性软件测试详细视频演示源码获取方式 项目介绍 小程序端…

索尼MDR-M1:超宽频的音频盛宴,打造沉浸式音乐体验

在音乐的世界里&#xff0c;每一次技术的突破都意味着全新的听觉体验。 索尼&#xff0c;作为音频技术的先锋&#xff0c;再次以其最新力作——MDR-M1封闭式监听耳机&#xff0c;引领了音乐界的新潮流。 这款耳机以其超宽频播放和卓越的隔音性能&#xff0c;为音乐爱好者和专…

多模态—图文匹配

可能最近大家已经发现了chatgpt可以根据自己的描述生成图片&#xff0c;其实这就是一个图文匹配的问题&#xff0c;可以理解为这是一个多模态的问题。 在模型训练时我们需要N个图片和N个文本对进行训练&#xff0c;文本通过text encoder形成文本语义向量&#xff0c;text enco…

【Python】Streamlit:为数据科学与机器学习打造的简易应用框架

Streamlit 是一个开源的 Python 库&#xff0c;专为数据科学家和机器学习开发者设计&#xff0c;旨在快速构建数据应用。通过简单的 Python 脚本&#xff0c;开发者无需掌握前端技术&#xff0c;即可将数据分析和模型结果转化为直观、交互式的 Web 应用。其简洁的 API 设计使得…

NVIDIA NVLink-C2C

NVIDIA NVLink-C2C 文章目录 前言一、介绍1. 用于定制芯片集成的超快芯片互连技术2. 构建半定制芯片设计3. 使用 NVLink-C2C 技术的产品 二、NVLink-C2C 技术优势1. 高带宽2. 低延迟3. 低功率和高密度4. 行业标准协议 前言 将 NVLink 扩展至芯片级集成 一、介绍 1. 用于定制芯…