首页 > 其他分享 >ffmpeg合并音频和视频

ffmpeg合并音频和视频

时间:2023-07-29 11:01:23浏览次数:33  
标签:视频 ffmpeg index 音频 codec av dts NULL

ffmpeg合并音频和视频

命令行

ffmpeg -i video.m4s -i audio.m4s -acodec copy -vcodec copy out.mp4

使用ffmpeg的api


extern "C" {
#include "libavformat/avformat.h"
#include "libavutil/dict.h"
#include "libavutil/opt.h"
#include "libavutil/timestamp.h"
#include "libswscale/swscale.h"
#include "libswresample/swresample.h"
#include "libavutil/imgutils.h"
#include "libavcodec/avcodec.h"
}


extern "C"
int mergeVideo(const char* audio_src, const char* video_src, const char* dst) {
    int ret = 0;
    printf("mergeVideo enter\n");
    printf("%s\n", audio_src);
    printf("%s\n", video_src);
    printf("%s\n", dst);

    // 注册FFmpeg库中的所有编解码器、格式和协议(高版本不再需要注册)
//     av_register_all();

    // 创建输入音频和视频文件的AVFormatContext
    AVFormatContext *inputAudioFormatCtx = NULL;
    AVFormatContext *inputVideoFormatCtx = NULL;
    if (avformat_open_input(&inputAudioFormatCtx, audio_src, NULL, NULL) != 0 ||
        avformat_find_stream_info(inputAudioFormatCtx, NULL) < 0) {
        printf("无法打开音频输入文件\n");
        return -1;
    }
    if (avformat_open_input(&inputVideoFormatCtx, video_src, NULL, NULL) != 0 ||
        avformat_find_stream_info(inputVideoFormatCtx, NULL) < 0) {
        printf("无法打开视频输入文件\n");
        return -1;
    }

    // 创建输出文件的AVFormatContext
    AVFormatContext *outputFormatCtx = NULL;
//     if (avformat_alloc_output_context2(&outputFormatCtx, NULL, "mpegts", dst) < 0) {
    if (avformat_alloc_output_context2(&outputFormatCtx, NULL, NULL, dst) < 0) {
        printf("无法创建输出文件\n");
        return -1;
    }

    // 遍历输入音频流
    int audio_index = av_find_best_stream(inputAudioFormatCtx, AVMEDIA_TYPE_AUDIO, -1, -1, NULL, 0);
    AVStream* st1 = inputAudioFormatCtx->streams[audio_index];

//     const AVCodec* codec = NULL;
//     codec = avcodec_find_decoder(st1->codecpar->codec_id);
//     if (!codec)
//     {
//         fprintf(stderr, "Codec not found\n");
//         exit(1);
//     }
//     AVCodecContext* codec_ctx = NULL;
//     codec_ctx = avcodec_alloc_context3(codec);
//     if (!codec_ctx)
//     {
//         exit(1);
//     }
//     avcodec_parameters_to_context(codec_ctx, inputAudioFormatCtx->streams[audio_index]->codecpar);
//     if ((ret = avcodec_open2(codec_ctx, codec, NULL) < 0))
//     {
//         return -1;
//     }

    // 遍历输入视频流
    int video_index = av_find_best_stream(inputVideoFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    AVStream* st2 = inputVideoFormatCtx->streams[video_index];

//     const AVCodec* codec2 = NULL;
//     codec2 = avcodec_find_decoder(st2->codecpar->codec_id);
//     if (!codec2)
//     {
//         fprintf(stderr, "Codec not found\n");
//         exit(1);
//     }
//     AVCodecContext* codec_ctx2 = NULL;
//     codec_ctx2 = avcodec_alloc_context3(codec2);
//     if (!codec_ctx2)
//     {
//         exit(1);
//     }
//     avcodec_parameters_to_context(codec_ctx2, inputVideoFormatCtx->streams[video_index]->codecpar);
//     if ((ret = avcodec_open2(codec_ctx2, codec2, NULL) < 0))
//     {
//         return -1;
//     }

    // 创建输出视频流
    // 每创建一个流,ctx->nb_streams数量就会加1
    AVStream* out1 = avformat_new_stream(outputFormatCtx, NULL);
    if (!out1)
    {
        return -1;
    }
    //复制配置信息
    AVCodecParameters* codecpar = inputAudioFormatCtx->streams[audio_index]->codecpar;
    avcodec_parameters_copy(out1->codecpar, codecpar);
    out1->codecpar->codec_tag = 0;

    //创建输出视频流
    AVStream* out2 = avformat_new_stream(outputFormatCtx, NULL);
    if (!out2)
    {
        return -1;
    }
    //复制配置信息
    AVCodecParameters* codecpar2 = inputVideoFormatCtx->streams[video_index]->codecpar;
    avcodec_parameters_copy(out2->codecpar, codecpar2);
    out2->codecpar->codec_tag = 0;

    // 打开输出文件
    if (!(outputFormatCtx->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&outputFormatCtx->pb, dst, AVIO_FLAG_WRITE) < 0) {
            printf("无法打开输出文件\n");
            return -1;
        }
    }

    // 写入文件头
    if (avformat_write_header(outputFormatCtx, NULL) < 0) {
        printf("无法写入文件头\n");
        return -1;
    }

    // 复制音频和视频数据
    AVPacket pkt1, pkt2;
    int ret1 = 0, ret2 = 0;
    // 音视频合成
    while (1) {
        if (ret1 < 0 && ret2 < 0) {
            break;
        }
//         printf("ret1:%d\tret2:%d\tst->cur_dts:%ld\tst2->cur_dts:%ld\tpkt1.dts:%ld\tpkt2.dts:%ld\ttime_base:%f %f\tdst:%s\t\n",
//                ret1, ret2, st1->cur_dts, st2->cur_dts,  pkt1.dts, pkt2.dts, av_q2d(st1->time_base), av_q2d(st2->time_base), dst);
//         printf("avg_frame_rate:%f %f\tr_frame_rate:%f %f\tduration:%ld %ld\n", av_q2d(st1->avg_frame_rate),
//                av_q2d(st1->r_frame_rate), av_q2d(st2->avg_frame_rate), av_q2d(st2->r_frame_rate), pkt1.duration, pkt2.duration);
        // 根据时间轮流写音频和视频,音频和视频在现实世界长度相同不意味着存储的帧数相同
        // PTS:Presentation Time Stamp。PTS 主要用于度量解码后的视频帧什么时候被显示出来。
        // DTS:Decode Time Stamp。DTS 主要是标识读入内存中的Bit流在什么时候开始送入解码器中进行解码。
        // 有时读到的数据包的pts是空的,所以使用dts比较时间
        // android上st1没有cur_dts成员,可以使用pkt.pts,但是裸流pts和dts都是空的
        // 可能需要计算pts和dts并对pkt进行设置,这个可能有帮助:https://zhuanlan.zhihu.com/p/468346396
//         if ((st1->cur_dts < st2->cur_dts && ret1 >= 0) || ret2 < 0) {
        if ((pkt1.dts < pkt2.dts && ret1 >= 0) || ret2 < 0) {
            // 音频时间落后,或视频已写完时写音频
            ret1 = av_read_frame(inputAudioFormatCtx, &pkt1);
            if (ret1 < 0) {
                continue;
            }
            if (pkt1.stream_index == audio_index) {
                pkt1.stream_index = 0; // 设置音频流索引
                av_packet_rescale_ts(&pkt1, st1->time_base, out1->time_base);
                pkt1.pos = -1;
                ret = av_interleaved_write_frame(outputFormatCtx, &pkt1); // 写入音频包
                if (ret < 0) {
                    break;
                }
            }else {
                av_packet_unref(&pkt1);
            }
        } else {
            // 视频时间落后,或音频已写完时写视频
            ret2 = av_read_frame(inputVideoFormatCtx, &pkt2);
            if (ret2 < 0) {
                continue;
            }
            if (pkt2.stream_index == video_index) {
                pkt2.stream_index = 1; // 设置视频流索引
                    // 单纯的音频或视频文件可能使用的都是0通道,既有音频又有视频的情况,不能使用同一个通道
                av_packet_rescale_ts(&pkt2, st2->time_base, out2->time_base);   // 重新设置时间基,否则可能导致新视频播放时间不准
                pkt2.pos = -1;
                ret = av_interleaved_write_frame(outputFormatCtx, &pkt2); // 写入视频包
                if (ret < 0) {
                    break;
                }
            }else {
                av_packet_unref(&pkt2);
            }
        }
    }

    // 写入文件尾
    av_write_trailer(outputFormatCtx);

    // 释放资源
    avformat_close_input(&inputAudioFormatCtx);
    avformat_close_input(&inputVideoFormatCtx);
    avformat_free_context(outputFormatCtx);

    return ret;
}

CMakeLists.txt:

cmake_minimum_required(VERSION 3.0)

project(merge LANGUAGES CXX)

set(CMAKE_CXX_STANDARD 11)
set(CMAKE_CXX_COMPILER g++)

set(CMAKE_BUILD_TYPE "RELEASE")
# set(CMAKE_BUILD_TYPE "DEBUG")


# set(FFMPEG_LIBS_DIR /usr/lib/x86_64-linux-gnu)
# set(FFMPEG_HEADERS_DIR /usr/include/x86_64-linux-gnu)

set(FFMPEG_LIBS_DIR /home/light/FFmpeg-master-linux/FFmpeg-master/build/x86-64/include)
set(FFMPEG_HEADERS_DIR /home/light/FFmpeg-master-linux/FFmpeg-master/build/x86-64/lib)


include_directories(${FFMPEG_HEADERS_DIR})
link_directories(${FFMPEG_LIBS_DIR})
set(FFMPEG_LIBS libavcodec.so libavformat.so libswscale.so libavdevice.so libavutil.so)


include_directories(include/)
aux_source_directory(src/ srcfiles)


add_executable(merge main.cpp ${srcfiles})
target_link_libraries(merge ${FFMPEG_LIBS})

install(TARGETS merge RUNTIME DESTINATION bin)

标签:视频,ffmpeg,index,音频,codec,av,dts,NULL
From: https://www.cnblogs.com/minding/p/17589444.html

相关文章

  • ffmpeg 编译安装android和linux
    ffmpeg编译安装android和linux下载:https://github.com/FFmpeg/FFmpeghttps://www.ffmpeg.org/download.htmlenvirenmentndk:https://github.com/android/ndk/wiki/Unsupported-Downloadssudoapt-getinstallbuild-essentialpkg-configsudoapt-getintalllibx264-dev......
  • 推荐短视频流量掘金付费进群系统源码-私域变现工具
    视频流量掘金付费进群系统源码ThinkPHP框架开发,百分百可搭建!近期爆火的流量掘金,自动化成交进群系统项目详细拆解,演示地址:runruncode.com/thinkphp/19493.html 不知道大家有没有听过,半自动挂机、流量掘金、流量变现、9.9自动进群系统等相关关键词的项目。 最近这套玩法非......
  • 北大AI公开课13讲全链接+最强干货盘点:视频+笔记+文字实录
    视频地址:北大AI公开课专栏笔记:北大AI公开课刚刚结束的北大AI公开课,由北大人工智能创新中心主任雷鸣组织,以把听者培养为“懂产业的AI人才”为主旨,邀请了13位顶级人工智能产业专家,分别从智能驾驶、智能医疗、智能金融、智能家居、硬件等10多个AI+行业,向学生和从业者全面介绍了......
  • 高效的视频渲染——H264与H265编码
    需要先安装Quicktime现在的主流还是H264但在不久后的将来H265会发展起来的两种常用的渲染方式这种方式,渲染的时候AE是不能工作的所以一般用第二种方式:添加到ME里面......
  • 利用Redis实现向量相似度搜索:解决文本、图像和音频之间的相似度匹配问题
    在自然语言处理领域,有一个常见且重要的任务就是文本相似度搜索。文本相似度搜索是指根据用户输入的一段文本,从数据库中找出与之最相似或最相关的一段或多段文本。它可以应用在很多场景中,例如问答系统、推荐系统、搜索引擎等。比如,当用户在知乎上提出一个问题时,系统就可以从知乎上......
  • Microsoft Speech SDK 5.1 微软的文字转音频 ( 8KHZ 16比特 )
    下载安装 SpeechSDK5.1下载地址: http://www.microsoft.com/en-us/download/details.aspx?id=10121详细的看这篇 https://www.cnblogs.com/hailexuexi/p/17588586.htmlC#示例直接保存到wav文件并存为8KHZ  16比特 语音格式privatevoidbtnSave_Click(objectsen......
  • TSINGSEE青犀视频汇聚融合平台EasyCVR的中性化版本如何配置?
    TSINGSEE青犀视频监控管理平台EasyCVR能在复杂的网络环境中,将分散的各类视频资源进行统一汇聚、整合、集中管理,实现视频资源的鉴权管理、按需调阅、全网分发、智能分析等,平台融合性强、开放度高、部署轻快,在智慧工地、智慧园区、智慧工厂、智慧码头、智慧水利等场景中有着广泛的应......
  • TSINGSEE青犀视频监控管理平台EasyNVR如何配置鉴权?
    视频监控汇聚平台EasyNVR是基于RTSP/Onvif协议的视频平台,可支持将接入的视频流进行全平台、全终端的分发,分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。为了满足用户的集成与二次开发需求,我们也提供了丰富的API接口供用户调用。有需要的用户可参照官方接口文......
  • TSINGSEE青犀视频监控管理平台EasyNVR如何配置鉴权?
    视频监控汇聚平台EasyNVR是基于RTSP/Onvif协议的视频平台,可支持将接入的视频流进行全平台、全终端的分发,分发的视频流包括RTSP、RTMP、HTTP-FLV、WS-FLV、HLS、WebRTC等格式。为了满足用户的集成与二次开发需求,我们也提供了丰富的API接口供用户调用。有需要的用户可参照官方接口文......
  • 最全springcloudAlibaba视频笔记-第三章Nacos Config服务配置中心
    NacosConfig服务配置中心课程视频:https://www.bilibili.com/video/BV1VW4y1o7n5本课程使用的是目前最新版本2022.0.0.0-RC2。基于SpringBoot3.0与JDK20的开发环境。集群中每一台主机的配置文件都是相同的,对配置文件的更新维护就成为了一个棘手的问题。此时就出现了配置中心......