通过FFMPEG给视频加字幕

这篇具有很好参考价值的文章主要介绍了通过FFMPEG给视频加字幕。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

字幕简介

我们在观看电影或短视频时,一般在视频下发会出现字幕,有些视频还会配中英双语字幕。字幕可以帮助观看人更好的了解视频内容。字幕分为以下三种类型:
外挂字幕:独立的字幕文件,播放视频时把视频和字幕放入同一路径下,也可以在播放器中选择外挂字幕。这样在播放视频时就可以看到字幕内容。常见字幕格式srt、vtt、ass等。
软字幕:也叫内挂字幕、封装字幕、字幕流等。通过某种技术将外挂字幕与视频文件打包在一起成一个文件。视频文件也可以同时封装多个字幕文件,播放时通过播放器选择所需字幕或不显示字幕。在需要时,还可以将字幕分离出来,修改后再打包进去。
硬字幕:将字幕内容覆盖叠加到视频画面上。这种字幕与视频画面溶于一体,具有最佳兼容性,只要能播放视频就能显示字幕。缺点是字幕占据视频画面,无法隐藏,破坏原有视频内容。且不可编辑更改。

FFMPEG命令行添加字幕

添加软字幕

ffmpeg -i demo.mp4 -i ass=subtitle.ass -c copy output.mkv
#demo.mp4 原始视频
#subtitle.ass 字幕文件
#output.mkv 输出视频

添加硬字幕

ffmpeg -i demo.mp4 -vf ass=subtitle.ass output.mp4

字幕格式转换

ffmpeg -i src.srt out.vtt
ffmpeg -i src.srt out.ass

代码方式添加字幕

添加软字幕

QString m_videoPath;    //原始视频文件
QString m_srtPath;      //字幕文件
QString m_destPath;     //输出视频文件
AVFormatContext* vfmt = NULL;   //源
AVFormatContext* sfmt = NULL;   //字幕
AVFormatContext* ofmt = NULL;   //输出
void SoftSubtitle()
{
    av_register_all();  //初始化
    avcodec_register_all(); //注册编解码器
    int ret = 0;
    // 打开视频流
    if (avformat_open_input(&vfmt, m_videoPath.toStdString().c_str(), NULL, NULL) < 0) {
        printf("avformat_open_input failed");
        return;
    }
    //读取媒体文件的数据包以获取流信息  查看是否有流信息
    if (avformat_find_stream_info(vfmt, NULL) < 0) {
        printf("avformat_find_stream_info");
        releaseInternal();
        return;
    }
    //分配输出的AVFormatContext
    if ((avformat_alloc_output_context2(&ofmt, NULL, NULL, m_destPath.toStdString().c_str())) < 0) {
        printf("avformat_alloc_output_context2() failed");
        releaseInternal();
        return;
    }
    int in_video_index = -1, in_audio_index = -1;   //源文件视频/音频流索引
    int ou_video_index = -1, ou_audio_index = -1;
    for (int i = 0; i < vfmt->nb_streams; i++) {
        AVStream* stream = vfmt->streams[i];
        if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            in_video_index = i;
            AVStream* newstream = avformat_new_stream(ofmt, NULL);  //创建一个流对象
            avcodec_parameters_copy(newstream->codecpar, stream->codecpar); //把源文件流复制给输出文件流
            newstream->codecpar->codec_tag = 0;
            ou_video_index = newstream->index;
        }
        else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            AVStream* newstream = avformat_new_stream(ofmt, NULL);
            avcodec_parameters_copy(newstream->codecpar, stream->codecpar);
            newstream->codecpar->codec_tag = 0;
            in_audio_index = i;
            ou_audio_index = newstream->index;
        }
    }
    if (!(ofmt->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&ofmt->pb, m_destPath.toStdString().c_str(), AVIO_FLAG_WRITE) < 0) {
            printf("avio_open failed");
            releaseInternal();
            return;
        }
    }

    // 打开字幕流
    /** 遇到问题:调用avformat_open_input()时提示"avformat_open_input failed -1094995529(Invalid data found when processing input)"
     *  分析原因:编译ffmpeg库是没有将对应的字幕解析器添加进去比如(ff_ass_demuxer,ff_ass_muxer)
     *  解决方案:添加对应的编译参数
     */
    if ((ret = avformat_open_input(&sfmt, m_srtPath.toStdString().c_str(), NULL, NULL)) < 0) {
        char errorBuf[256] = { 0 };
        av_strerror(ret, errorBuf, sizeof(errorBuf));
        printf("avformat_open_input failed %d(%s)", ret, errorBuf);
        return;
    }
    if ((ret = avformat_find_stream_info(sfmt, NULL)) < 0) {
        char errorBuf[256] = { 0 };
        av_strerror(ret, errorBuf, sizeof(errorBuf));
        printf("avformat_find_stream_info %d(%s)", ret, errorBuf);
        releaseInternal();
        return;
    }
    //获取字幕文件字幕流索引
    int subTitleIndex = av_find_best_stream(sfmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0);
    if (subTitleIndex < 0) {
        printf("not find subtitle stream 0");
        releaseInternal();
        return;
    }
    //创建一个输出字幕流 并把源字幕流复制给它
    AVStream* nstream = avformat_new_stream(ofmt, NULL);
    ret = avcodec_parameters_copy(nstream->codecpar, sfmt->streams[subTitleIndex]->codecpar);
    nstream->codecpar->codec_tag = 0;
    int ou_subtitle_index = nstream->index; //输出字幕流索引值

    //写头文件
    if (avformat_write_header(ofmt, NULL) < 0) {
        printf("avformat_write_header failed");
        releaseInternal();
        return;
    }
    //打印媒体信息  将 AVFormatContext 中的媒体信息转存到输出
    av_dump_format(ofmt, 0, m_destPath.toStdString().c_str(), 1);

    /** 遇到问题:封装后生成的mkv文件字幕无法显示,封装时提示"[matroska @ 0x10381c000] Starting new cluster due to timestamp"
     *  分析原因:通过和ffmpeg.c中源码进行比对,后发现mvk对字幕写入的顺序有要求
     *  解决方案:将字幕写入放到音视频之前
     */
    //创建AVPacket
    AVPacket* inpkt2 = av_packet_alloc();
    while (av_read_frame(sfmt, inpkt2) >= 0) {  //读一帧字幕数据
        AVStream* srcstream = sfmt->streams[0];
        AVStream* dststream = ofmt->streams[ou_subtitle_index];
        //用于将AVPacket中各种时间值从一种时间基转换为另一种时间基
        av_packet_rescale_ts(inpkt2, srcstream->time_base, dststream->time_base);   
        inpkt2->stream_index = ou_subtitle_index;
        inpkt2->pos = -1;
        printf("pts %d", inpkt2->pts);
        if (av_write_frame(ofmt, inpkt2) < 0) { //输出一帧字幕数据
            printf("subtitle av_write_frame failed");
            releaseInternal();
            av_packet_unref(inpkt2);
            return;
        }
    }
    //释放AVPacket
    av_packet_unref(inpkt2);

    AVPacket* inpkt = av_packet_alloc();
    while (av_read_frame(vfmt, inpkt) >= 0) {
        if (inpkt->stream_index == in_video_index) {    //读的是视频数据
            AVStream* srcstream = vfmt->streams[in_video_index];
            AVStream* dststream = ofmt->streams[ou_video_index];
            av_packet_rescale_ts(inpkt, srcstream->time_base, dststream->time_base);
            inpkt->stream_index = ou_video_index;
            printf("inpkt %d", inpkt->pts);
            if (av_write_frame(ofmt, inpkt) < 0) {  //写视频数据到
                printf("video av_write_frame failed");
                av_packet_unref(inpkt);
                releaseInternal();
                return;
            }
        }
        else if (inpkt->stream_index == in_audio_index) {   //读的是音频数据
            AVStream* srcstream = vfmt->streams[in_audio_index];
            AVStream* dststream = ofmt->streams[ou_audio_index];
            av_packet_rescale_ts(inpkt, srcstream->time_base, dststream->time_base);
            inpkt->stream_index = ou_audio_index;
            if (av_write_frame(ofmt, inpkt) < 0) {  //写视频数据到
                printf("audio av_write_frame failed");
                av_packet_unref(inpkt);
                releaseInternal();
                return;
            }
        }
    }
    av_packet_unref(inpkt);

    //字幕、视音频数据全部读写完毕
    //输出文件尾
    av_write_trailer(ofmt);
    if (vfmt) {
        avformat_close_input(&vfmt);
        avformat_free_context(vfmt);
    }
    if (sfmt) {
        avformat_close_input(&sfmt);
        avformat_free_context(sfmt);
    }
    if (ofmt) {
        avformat_close_input(&ofmt);
        avformat_free_context(ofmt);
    }
}

添加硬字幕

QString m_videoPath;
QString m_srtPath;
QString m_fontsConf;    
QString m_destPath;
AVFormatContext* vfmt = NULL;   //源文件
AVFormatContext* sfmt = NULL;   //字幕文件
AVFormatContext* ofmt = NULL;   //输出文件
int in_video_index, in_audio_index;
int ou_video_index, ou_audio_index;
AVCodecContext* de_video_ctx = NULL;
AVCodecContext* en_video_ctx = NULL;
AVFrame* de_frame = NULL;
AVFilterGraph* graph = NULL;
AVFilterContext* src_filter_ctx = NULL;
AVFilterContext* sink_filter_ctx = NULL;

//编码
void doEncodec(AVFrame* frame)
{
    int ret = avcodec_send_frame(en_video_ctx, frame);
    while (true) {
        AVPacket* pkt = av_packet_alloc();
        ret = avcodec_receive_packet(en_video_ctx, pkt);
        if (ret < 0) {
            av_packet_unref(pkt);
            break;
        }

        // 写入数据
        av_packet_rescale_ts(pkt, en_video_ctx->time_base, ofmt->streams[ou_video_index]->time_base);
        pkt->stream_index = ou_video_index;
        //printf("video pts %d(%s)", pkt->pts, av_ts2timestr(pkt->pts, &ofmt->streams[ou_video_index]->time_base));
        av_write_frame(ofmt, pkt);

        av_packet_unref(pkt);
    }
}
//解码
void doDecodec(AVPacket* pkt)
{
    if (!de_frame) {
        de_frame = av_frame_alloc();
    }
    int ret = avcodec_send_packet(de_video_ctx, pkt);
    while (true) {
        ret = avcodec_receive_frame(de_video_ctx, de_frame);
        if (ret == AVERROR_EOF) {
            // 说明已经没有数据了;清空
            //解码成功送入滤镜进行处理
            if ((ret = av_buffersrc_add_frame_flags(src_filter_ctx, NULL, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
                //printf("av_buffersrc_add_frame_flags failed");
                break;
            }
            break;
        }
        else if (ret < 0) {
            break;
        }

        //解码成功送入滤镜进行处理
        if ((ret = av_buffersrc_add_frame_flags(src_filter_ctx, de_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {
            //printf("av_buffersrc_add_frame_flags failed");
            break;
        }

        while (true) {
            AVFrame* enframe = av_frame_alloc();
            ret = av_buffersink_get_frame(sink_filter_ctx, enframe);    //从FilterGraph中取出一个AVFrame
            if (ret == AVERROR_EOF) {
                // 说明结束了
                //printf("avfilter endeof");
                // 清空编码器
                doEncodec(NULL);
                // 释放内存
                av_frame_unref(enframe);
            }
            else if (ret < 0) {
                // 释放内存
                av_frame_unref(enframe);
                break;
            }

            // 进行重新编码
            doEncodec(enframe);
            // 释放内存
            av_frame_unref(enframe);
        }
    }
}
//初始化过滤器
bool initFilterGraph(QString srtPath)
{
    graph = avfilter_graph_alloc();     //为FilterGraph分配内存
    int ret = 0;
    AVStream* stream = vfmt->streams[in_video_index]; //源视频解码器
    // 输入滤镜
    const AVFilter* src_filter = avfilter_get_by_name("buffer");
    char desc[400];
    sprintf(desc, "video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d", stream->codecpar->width, stream->codecpar->height, stream->codecpar->format, stream->time_base.num, stream->time_base.den, stream->codecpar->sample_aspect_ratio.num, stream->codecpar->sample_aspect_ratio.den);
    ret = avfilter_graph_create_filter(&src_filter_ctx, src_filter, "in", desc, NULL, graph);  //创建并向FilterGraph中添加一个Filter
    if (ret < 0) {
        //printf("init src filter failed");
        return false;
    }

    // 输出滤镜
    enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE };
    AVBufferSinkParams* buffersink_params;
    buffersink_params = av_buffersink_params_alloc();
    buffersink_params->pixel_fmts = pix_fmts;
    const AVFilter* sink_filter = avfilter_get_by_name("buffersink");
    ret = avfilter_graph_create_filter(&sink_filter_ctx, sink_filter, "out", NULL, buffersink_params, graph);
    //av_free(buffersink_params);
    if (ret < 0) {
        char errorBuf[256] = { 0 };
        av_strerror(ret, errorBuf, sizeof(errorBuf));
        //printf("buffersink init failed");
        return false;
    }
     // 滤镜描述符
    char filter_des[400];
    //sprintf(filter_des, "drawbox=x=100:y=100:w=100:h=100:color=pink@0.5");    //加一个方框 可以运行
    //sprintf(filter_des, "drawtext=fontfile=arial.ttf:fontcolor=white:fontsize=30:text='Aispeech'");
    sprintf(filter_des, "subtitles=filename='123.srt'");        //只能使用当前路径的srt文件
    srtPath.replace(":", "\\:");
    //QString srtPath = QString("D\\:\\123.srt");
    //sprintf(filter_des, "subtitles=filename='%s'", srtPath.toStdString().c_str());        //只能存在当前路径
    AVFilterInOut* inputs = avfilter_inout_alloc();
    AVFilterInOut* ouputs = avfilter_inout_alloc();
    inputs->name = av_strdup("out");
    inputs->filter_ctx = sink_filter_ctx;
    inputs->next = NULL;
    inputs->pad_idx = 0;

    ouputs->name = av_strdup("in");
    ouputs->filter_ctx = src_filter_ctx;
    ouputs->next = NULL;
    ouputs->pad_idx = 0;

    ret = avfilter_graph_parse_ptr(graph, filter_des, &inputs, &ouputs, NULL);  //将一串通过字符串描述的Graph添加到FilterGraph中
    if (ret < 0) {
        char errorBuf[256] = { 0 };
        av_strerror(ret, errorBuf,sizeof(errorBuf));
        return false;
    }

    av_buffersink_set_frame_size(sink_filter_ctx, en_video_ctx->frame_size);

    // 初始化滤镜
    if (avfilter_graph_config(graph, NULL) < 0) {   //检查FilterGraph的配置
        //printf("avfilter_graph_config failed");
        return false;
    }

    avfilter_inout_free(&inputs);
    avfilter_inout_free(&ouputs);

    return true;
}
void HardSubtitle()
{
    av_register_all();
    avcodec_register_all();
    const char* ver = av_version_info();
    int ret = 0;
    // 打开视频流
    if (avformat_open_input(&vfmt, m_videoPath.toStdString().c_str(), NULL, NULL) < 0) {
        printf("avformat_open_input failed");
        return;
    }
    //判断文件是否有音视频流
    if (avformat_find_stream_info(vfmt, NULL) < 0) {
        printf("avformat_find_stream_info");
        releaseInternal();
        return;
    }
    //分配输出的AVFormatContext
    if ((ret = avformat_alloc_output_context2(&ofmt, NULL, NULL, m_destPath.toStdString().c_str())) < 0) {
        printf("avformat_alloc_output_context2 failed");
        return;
    }

    for (int i = 0; i < vfmt->nb_streams; i++) {
        AVStream* sstream = vfmt->streams[i];
        if (sstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            in_video_index = i;
            // 添加新的视频流
            AVStream* nstream = avformat_new_stream(ofmt, NULL);
            ou_video_index = nstream->index;

            // 由于视频需要添加字幕,所以需要重新编解码,但是编码信息和源文件中一样
            AVCodec* codec = avcodec_find_decoder(sstream->codecpar->codec_id);
            if (!codec) {
                printf("not surport codec!");
                releaseInternal();
                return;
            }
            de_video_ctx = avcodec_alloc_context3(codec);
            if (!de_video_ctx) {
                printf("avcodec_alloc_context3 failed");
                releaseInternal();
                return;
            }
            // 设置解码参数,从源文件拷贝
            avcodec_parameters_to_context(de_video_ctx, sstream->codecpar);
            // 初始化解码器上下文
            if (avcodec_open2(de_video_ctx, codec, NULL) < 0) {
                printf("avcodec_open2 failed");
                releaseInternal();
                return;
            }

            // 创建编码器
            AVCodec* encodec = avcodec_find_encoder(sstream->codecpar->codec_id);
            if (!encodec) {
                printf("not surport encodec!");
                releaseInternal();
                return;
            }
            en_video_ctx = avcodec_alloc_context3(encodec);
            if (!en_video_ctx) {
                printf("avcodec_alloc_context3 failed");
                releaseInternal();
                return;
            }

            // 设置编码相关参数
            /** 遇到问题:生成视频前面1秒钟是灰色的
             *  分析原因:直接从源视频流拷贝编码参数到新的编码上下文中(即通过avcodec_parameters_to_context(en_video_ctx, sstream->codecpar);)而部分重要编码参数(如帧率,时间基)并不在codecpar
             *  中,所以导致参数缺失
             *  解决方案:额外设置时间基和帧率参数
             */
            avcodec_parameters_to_context(en_video_ctx, sstream->codecpar);
            // 设置帧率
            int fps = sstream->r_frame_rate.num;
            //en_video_ctx->framerate = (AVRational){ fps,1 };
            en_video_ctx->framerate.num = fps;
            en_video_ctx->framerate.den = 1;
            // 设置时间基;
            en_video_ctx->time_base = sstream->time_base;
            // I帧间隔,决定了压缩率
            en_video_ctx->gop_size = 12;
            if (ofmt->oformat->flags & AVFMT_GLOBALHEADER) {
                en_video_ctx->flags = AV_CODEC_FLAG_GLOBAL_HEADER;
            }
            // 初始化编码器上下文
            if (avcodec_open2(en_video_ctx, encodec, NULL) < 0) {
                printf("avcodec_open2 failed");
                releaseInternal();
                return;
            }

            // 设置视频流相关参数
            avcodec_parameters_from_context(nstream->codecpar, en_video_ctx);
            nstream->codecpar->codec_tag = 0;

        }
        else if (sstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
            // 音频直接进行流拷贝
            in_audio_index = i;
            AVStream* nstream = avformat_new_stream(ofmt, NULL);
            avcodec_parameters_copy(nstream->codecpar, sstream->codecpar);
            ou_audio_index = nstream->index;
            nstream->codecpar->codec_tag = 0;
        }
    }

    if (in_video_index == -1) {
        printf("not has video stream");
        releaseInternal();
        return;
    }

    if (!(ofmt->flags & AVFMT_NOFILE)) {
        if (avio_open(&ofmt->pb, m_destPath.toStdString().c_str(), AVIO_FLAG_WRITE) < 0) {
            printf("avio_open() failed");
            releaseInternal();
            return;
        }
    }
    //打印媒体信息  将 AVFormatContext 中的媒体信息转存到输出
    av_dump_format(ofmt, -1, m_destPath.toStdString().c_str(), 1);

    // 写入头文件
    if (avformat_write_header(ofmt, NULL) < 0) {
        printf("avformat_write_header failed");
        releaseInternal();
        return;
    }

    // 初始化过滤器
    if (!initFilterGraph(m_srtPath)) {
        printf("");
        releaseInternal();
        return;
    }

    AVPacket* inpkt = av_packet_alloc();
    while (av_read_frame(vfmt, inpkt) >= 0) {
        if (inpkt->stream_index == in_video_index) {
            doDecodec(inpkt);
        }
        else if (inpkt->stream_index == in_audio_index) {
            // 进行时间基的转换
            av_packet_rescale_ts(inpkt, vfmt->streams[in_audio_index]->time_base, ofmt->streams[ou_audio_index]->time_base);
            inpkt->stream_index = ou_audio_index;
            //printf("audio pts %d(%s)", inpkt->pts, av_ts2timestr(inpkt->pts, &ofmt->streams[ou_audio_index]->time_base));
            av_write_frame(ofmt, inpkt);
        }
    }
    av_packet_unref(inpkt);

    printf("finish !");
    doDecodec(NULL);
    av_write_trailer(ofmt);
    if (vfmt) {
        avformat_close_input(&vfmt);
        avformat_free_context(vfmt);
    }
    if (sfmt) {
        avformat_close_input(&sfmt);
        avformat_free_context(sfmt);
    }
    if (ofmt) {
        avformat_close_input(&ofmt);
        avformat_free_context(ofmt);
    }
    releaseInternal();
}

注意:“:”在FFMPEG中有其他用途,因此在传入路径时使用相对路径或者加转义字符“\”
链接: 源码下载文章来源地址https://www.toymoban.com/news/detail-730531.html

到了这里,关于通过FFMPEG给视频加字幕的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处: 如若内容造成侵权/违法违规/事实不符,请点击违法举报进行投诉反馈,一经查实,立即删除!

领支付宝红包 赞助服务器费用

相关文章

  • 使用FFmpeg将本地文件通过UDP推流的音视频

    推流是指将音视频数据通过网络传输到指定的目标端,而FFmpeg是一个功能强大的跨平台多媒体处理工具,可以用于音视频编解码、转码、处理等操作。本文将介绍如何使用FFmpeg将本地文件通过UDP协议进行推流,实现音视频数据的传输。 首先,需要确保已经安装了FFmpeg工具。如

    2024年03月19日
    浏览(69)
  • Qt/C++音视频开发50-不同ffmpeg版本之间的差异处理

    ffmpeg的版本众多,从2010年开始计算的项目的话,基本上还在使用的有ffmpeg2/3/4/5/6,最近几年版本彪的比较厉害,直接4/5/6,大版本之间接口有一些变化,特别是一些废弃接口被彻底删除了,而网络上的各种文章几乎都是ffmpeg3左右为主的,所以本人在写这个全功能播放组件的时

    2024年02月14日
    浏览(56)
  • QtAV:基于Qt和FFmpeg的跨平台高性能音视频播放框架

    目录 一.简介 1.特性 2.支持的平台 3.简单易用的接口 二.编译 1.下载依赖包 2.开始编译 2.1克隆 2.2修改配置文件 2.3编译 三.试用 官网地址:http://www.qtav.org/ Github地址:https://github.com/wang-bin/QtAV ●支持大部分播放功能 ●播放、暂停、播放速度、快进快退、字幕、音量、声道、音

    2024年01月22日
    浏览(161)
  • 用Qt开发的ffmpeg流媒体播放器,支持截图、录像,支持音视频播放,支持本地文件播放、网络流播放

    本工程qt用的版本是5.8-32位,ffmpeg用的版本是较新的5.1版本。它支持TCP或UDP方式拉取实时流,实时流我采用的是监控摄像头的RTSP流。音频播放采用的是QAudioOutput,视频经ffmpeg解码并由YUV转RGB后是在QOpenGLWidget下进行渲染显示。本工程的代码有注释,可以通过本博客查看代码或者

    2024年02月03日
    浏览(105)
  • 音视频 FFmpeg音视频处理流程

    推荐一个零声学院项目课,个人觉得老师讲得不错,分享给大家: 零声白金学习卡(含基础架构/高性能存储/golang云原生/音视频/Linux内核) https://xxetb.xet.tech/s/VsFMs

    2024年02月12日
    浏览(69)
  • ffmpeg@音视频工具@音视频合并

    FFmpeg中文网 (github.net.cn) FFmpeg 是一款强大的开源跨平台音视频处理工具集,它包含了一系列命令行工具以及用于音频和视频编码解码、格式转换、抓取、流化等功能的库。FFmpeg 支持多种视频、音频格式和编解码器,能够进行音视频的压缩、封装、转码、分割、合并、过滤、抓

    2024年03月17日
    浏览(79)
  • 【FFmpeg】音视频录制 ① ( 查询系统中 ffmpeg 可录制的音视频输入设备 | 使用 ffmpeg 命令录制音视频数据 | 录制视频数据命令 |录制音频数据| 同时录制音频和视频数据命令 )

    在 Windows 系统中 , 使用 ffmpeg 命令 录制 音视频 , 需要先获取 系统的 音视频设备 信息 , 录制 音视频 本质上是从 系统音视频设备 中获取数据 ; 执行 命令 , 可以获取 系统中 ffmpeg 可用的 DirectShow 音视频输入设备 ; 命令参数解析 : -list_devices true : 列出所有 ffmpeg 的 指定类型的可

    2024年04月25日
    浏览(92)
  • 音视频 ffmpeg命令提取音视频数据

    保留封装格式 提取视频 提取音频 推荐一个零声学院项目课,个人觉得老师讲得不错,分享给大家: 零声白金学习卡(含基础架构/高性能存储/golang云原生/音视频/Linux内核) https://xxetb.xet.tech/s/VsFMs

    2024年02月10日
    浏览(62)
  • 音视频 ffmpeg视频裁剪

    将输入视频帧的宽度和高度从x和y值表示的位置裁剪到指定的宽度和高度;x和y是输出的左上角坐标,协调系统的中心是输入视频帧的左上角。 如果使用了可选的keep_aspect参数,将会改变输出SAR(样本宽比)以补偿新的DAR(显示长宽比) 推荐一个零声学院项目课,个人觉得老师讲得不

    2024年02月10日
    浏览(52)
  • ffmpeg系列学习——FFmpeg的音视频处理

    1.音视频的采样率、采样位深度和声道数 音频和视频的采样率、采样位深度和声道数是媒体文件中的重要参数,它们会直接影响到音视频的质量和文件大小。下面对它们进行详细解释: 采样率 采样率指音频每秒钟采样的次数,用赫兹(Hz)表示。采样率越高,音频的还原度越

    2024年02月04日
    浏览(64)

觉得文章有用就打赏一下文章作者

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

请作者喝杯咖啡吧~博客赞助

支付宝扫一扫领取红包,优惠每天领

二维码1

领取红包

二维码2

领红包