基于新版FFmpeg(FFmpeg 6.1)的音视频复用(不涉及编解码)

这篇具有很好参考价值的文章主要介绍了基于新版FFmpeg(FFmpeg 6.1)的音视频复用(不涉及编解码)。希望对大家有所帮助。如果存在错误或未考虑完全的地方,请大家不吝赐教,您也可以点击"举报违法"按钮提交疑问。

1 项目中使用的FFmpeg函数介绍

FFmpeg库常用函数介绍(一)-CSDN博客

FFmpeg库常用函数介绍(二)-CSDN博客

FFmpeg库常用函数介绍(三)-CSDN博客

2 介绍

这篇文章介绍的是基于新版FFmpeg(FFmpeg 6.1)的音视频复用器的实现,可以实现音频和视频文件复用为一个视频文件,具体功能如下表所示。

输入视频文件

输入音频文件

输出视频文件

input.h264

input.aac

output.mp4 (avi、mkv、wmv等)

input.h264

input.mp3

input.mp4

input.mp3

input.mp4

input.aac

input.mp4

input.mp4

…等等…

3 代码逻辑

  1. 根据输出文件的格式选择是否开启比特流过滤器(AAC_ADTS_TO_ASC和H264_AVCC_TO_ANNEXB宏)。例如,输出格式为avi,就需要开启H264_AVCC_TO_ANNEXB(置为1);
  2. 打开输入音视频文件,创建并初始化输入AVFormatContext,创建输出AVFormatContext;
  3. 根据输入视频文件的视频流创建输出文件的视频流,拷贝编解码器参数;
  4. 根据输入音频文件的音频流创建输出文件的音频流,拷贝编解码器参数;
  5. 打开输出文件,写入文件头;
  6. 根据过滤器的开启情况创建并初始化对应比特流过滤器;
  7. 根据av_compare_ts的输出判断先读取音频还是视频文件,然后读取帧;
  8. 时间戳转换、送入过滤器过滤、交错写入;
  9. 所有帧写完后写入文件尾;

4 问题汇总

4.1 没有pts

有的码流没有pts,例如原始的H.264码流,因此需要自己手动设置pts。pts是以输入流时间基表示的ffmpeg内部时间。以输入流时间基表示的意思是有几个输入流时间基。ffmpeg内部时间是AV_TIME_BASE (1000000),换算关系是1s = 1000000。

计算过程是首先计算出ffmpeg内部时间表示的两帧之间的间隔:

int frame_duration = AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);

1 / av_q2d(in_stream->r_frame_rate)表示的是以秒表示的间隔,AV_TIME_BASE / av_q2d(in_stream->r_frame_rate)表示的是ffmpeg内部时间表示的间隔。

接着就是算出真正的pts,也就是以输入流时间基表示的ffmpeg内部时间。

pkt.pts = frame_index * frame_duration / (av_q2d(in_stream->time_base) * AV_TIME_BASE);

frame_index * frame_duration表示当前帧以ffmpeg内部时间表示的显示时间。(av_q2d(in_stream->time_base) * AV_TIME_BASE)表示输入流时间基以ffmpeg内部时间表示的结果。二者相除表示以输入流时间基表示的ffmpeg内部时间,也就是真正的pts。

4.2 二倍速问题

写完代码后,使用没有pts的码流进行测试,发现画面变成了二倍速,并且视频长度也减半了,猜测是pts的设置有问题。最终将pts乘以2解决了问题,但是目前还不知道原理是什么。

//解决2倍速问题...

pkt.pts *= 2;

4.3 packet里的stream_index的设置

输出文件的音视频流来自不同的文件,因此packet中流的索引与输出文件中流的索引可能不匹配,可能出现packet中音频帧和视频帧所对应的stream_index是一样的的情况。因此将packet中的音频或视频帧与输出流的音视频流的索引匹配上。

pkt.stream_index = out_stream->index;

5 代码

#include <stdio.h>
extern "C"
{
    #include "libavformat/avformat.h"
    #include "libavcodec/bsf.h"
};

#define AAC_ADTS_TO_ASC 0
#define H264_AVCC_TO_ANNEXB 0

void release_context(AVFormatContext *in_fmt_ctx1, AVFormatContext *in_fmt_ctx2, AVFormatContext *out_fmt_ctx, AVPacket *pkt1, AVPacket *pkt2,
AVBSFContext *bsf_ctx1, AVBSFContext *bsf_ctx2)
{
    if (out_fmt_ctx && !((out_fmt_ctx->oformat->flags) & AVFMT_NOFILE))
    {
        avio_close(out_fmt_ctx->pb);
    }

    avformat_close_input(&in_fmt_ctx1);
    avformat_close_input(&in_fmt_ctx2);
    avformat_free_context(out_fmt_ctx);
    av_packet_unref(pkt1);
    av_packet_unref(pkt2);
    av_bsf_free(&bsf_ctx1);
    av_bsf_free(&bsf_ctx2);
}

int main(int argc, char *argv[])
{
    if (argc < 4)
    {
        printf("argument error, caller should pass 3 filenames as arguments, for example, \"./main input_video.h264 input_audio.aac output_video.mp4\"\n");
        return -1;
    }

    const char *in_filename_video = argv[1];
    const char *in_filename_audio = argv[2];
    const char *out_filename_video = argv[3];

    AVFormatContext *in_fmt_ctx_video = NULL, *in_fmt_ctx_audio = NULL, *out_fmt_ctx = NULL;
    AVPacket pkt, pkt_filtered;
    memset(&pkt, 0, sizeof(pkt));
    memset(&pkt_filtered, 0, sizeof(pkt_filtered));
    AVBSFContext *bsf_ctx_video = NULL, *bsf_ctx_audio = NULL;

    int64_t ts_video = 0, ts_audio = 0;
    AVRational time_base_in_video, time_base_in_audio;

    int in_video_index = -1, in_audio_index = -1, out_video_index = -1, out_audio_index = -1;

    //打开输入视频,初始化AVFormatContext
    if (avformat_open_input(&in_fmt_ctx_video, in_filename_video, NULL, NULL) < 0)
    {
        printf("failed to open input video file\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }
    //寻找输入视频流信息
    if (avformat_find_stream_info(in_fmt_ctx_video, NULL) < 0)
    {
        printf("failed to find input video stream info\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }

    //格式化输出输入流信息
    av_dump_format(in_fmt_ctx_video, 0, in_filename_video, 0);

    //打开输入音频,初始化AVFormatContext
    if (avformat_open_input(&in_fmt_ctx_audio, in_filename_audio, NULL, NULL) < 0)
    {
        printf("failed to open input audio file\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }
    //寻找输入音频流信息
    if (avformat_find_stream_info(in_fmt_ctx_audio, NULL) < 0)
    {
        printf("failed to find input audio stream info\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }

    //格式化输出输入流信息
    av_dump_format(in_fmt_ctx_audio, 0, in_filename_audio, 0);

    //分配输出AVFormatContext
    if (avformat_alloc_output_context2(&out_fmt_ctx, NULL, NULL, out_filename_video) < 0)
    {
        printf("failed to alloc output AVFormatContext\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }

    //遍历输入视频,寻找视频流,为输出AVFormatContext创建新流,拷贝编解码器参数
    for (int i = 0; i < in_fmt_ctx_video->nb_streams; i++)
    {
        if (in_fmt_ctx_video->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            AVStream *in_stream = in_fmt_ctx_video->streams[i];
            time_base_in_video = in_stream->time_base;
            //创建流
            AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
            if (!out_stream)
            {
                printf("failed to create new stream for output AVFormatContext\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }
            //拷贝编解码器参数
            if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0)
            {
                printf("failed to copy codec parameters form input video to output video\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }
            //自动选择符合输出格式的码流类型
            out_stream->codecpar->codec_tag = 0;
            //输入视频流索引
            in_video_index = i;
            //输出视频流索引
            out_video_index = 0;
            break;
        }
    }

    //遍历输入音频,寻找音频流,为输出AVFormatContext创建新流,拷贝编解码器参数
    for (int i = 0; i < in_fmt_ctx_audio->nb_streams; i++)
    {
        if (in_fmt_ctx_audio->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            AVStream *in_stream = in_fmt_ctx_audio->streams[i];
            time_base_in_audio = in_stream->time_base;
            //创建流
            AVStream *out_stream = avformat_new_stream(out_fmt_ctx, NULL);
            if (!out_stream)
            {
                printf("failed to create new stream for output AVFormatContext\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }
            //拷贝编解码器参数
            if (avcodec_parameters_copy(out_stream->codecpar, in_stream->codecpar) < 0)
            {
                printf("failed to copy codec parameters form input audio to output video\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }
            //自动选择符合输出格式的码流类型
            out_stream->codecpar->codec_tag = 0;
            //输入音频流索引
            in_audio_index = i;
            //输出音频流索引
            out_audio_index = 1;
            break;
        }
    }

    //格式化输出输出音视频流信息
    av_dump_format(out_fmt_ctx, 0, out_filename_video, 1);

    //打开输出文件
    if (!(out_fmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        if (avio_open2(&out_fmt_ctx->pb, out_filename_video, AVIO_FLAG_WRITE, NULL, NULL) < 0)
        {
            printf("failed to open output file\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
    }

    //写入文件头
    if (avformat_write_header(out_fmt_ctx, NULL) < 0)
    {
        printf("failed to write header to the output file\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }

    #if AAC_ADTS_TO_ASC
        //获取比特流过滤器
        const AVBitStreamFilter *bsf_audio = av_bsf_get_by_name("aac_adtstoasc");
    #endif

    #if H264_AVCC_TO_ANNEXB
        //获取比特流过滤器
        const AVBitStreamFilter *bsf_video = av_bsf_get_by_name("h264_mp4toannexb");
    #endif

    #if AAC_ADTS_TO_ASC
        //分配比特流过滤器上下文AVBSFContext
        if (av_bsf_alloc(bsf_audio, &bsf_ctx_audio) < 0)
        {
            printf("failed to alloc AVBSFContext\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
        //拷贝编解码器参数
        if (avcodec_parameters_copy(bsf_ctx_audio->par_in, in_fmt_ctx_audio->streams[in_audio_index]->codecpar) < 0)
        {
            printf("failed to copy codec parameters from input audio to the bi stream filter context\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
        //初始化AVBSFContext
        if (av_bsf_init(bsf_ctx_audio) < 0)
        {
            printf("failed to init AVBSFContext\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
    #endif

    #if H264_AVCC_TO_ANNEXB
        //分配比特流过滤器上下文AVBSFContext
        if (av_bsf_alloc(bsf_video, &bsf_ctx_video) < 0)
        {
            printf("failed to alloc AVBSFContext\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
        //拷贝编解码器参数
        if (avcodec_parameters_copy(bsf_ctx_video->par_in, in_fmt_ctx_video->streams[in_video_index]->codecpar) < 0)
        {
            printf("failed to copy codec parameters from input video to the bit stream filter context\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
        //初始化AVBSFContext
        if (av_bsf_init(bsf_ctx_video) < 0)
        {
            printf("failed to init AVBSFContext\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }
    #endif

    int frame_index = 0;
    AVFormatContext *fmt_ctx_v_or_a = NULL;
    while (true)
    {
        AVStream *in_stream = NULL, *out_stream = NULL;
        //比较时间戳,以判断先处理并写入音频还是视频帧
        int ret = av_compare_ts(ts_video, time_base_in_video, ts_audio, time_base_in_audio);
        switch (ret)
        {
            case -1:
            case 0:
            {
                fmt_ctx_v_or_a = in_fmt_ctx_video;
                in_stream = in_fmt_ctx_video->streams[in_video_index];
                out_stream = out_fmt_ctx->streams[out_video_index];
                break;
            }
            case 1:
            {
                fmt_ctx_v_or_a = in_fmt_ctx_audio;
                in_stream = in_fmt_ctx_audio->streams[in_audio_index];
                out_stream = out_fmt_ctx->streams[out_audio_index];
                break;
            }
            default:
            {
                printf("undefined result\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }
        }

        //读取音视频帧
        ret = av_read_frame(fmt_ctx_v_or_a, &pkt);
        if (ret < 0)
        {
            if (ret == AVERROR_EOF)
            {
                break;
            }

            printf("failed to read frame from input file\n");
            release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
            return -1;
        }

        //读取的是输入的视频文件
        //如果读取到的帧不是视频帧则重新读取
        if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            if (pkt.stream_index != in_video_index)
            {
                av_packet_unref(&pkt);
                continue;
            }
        }
        //读取的是输入的音频文件
        //如果读取到的不是音频帧则重新读取
        else if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            if (pkt.stream_index != in_audio_index)
            {
                av_packet_unref(&pkt);
                continue;
            }
        }

        //有的码流没有pts,例如原始的H.264码流
        //因此需要自己手动设置pts
        if (pkt.pts == AV_NOPTS_VALUE)
        {
            //两帧之间的间隔
            int frame_duration = AV_TIME_BASE / av_q2d(in_stream->r_frame_rate);
            //计算pts以输入流时间基表示的ffmpeg内部时间
            pkt.pts = frame_index * frame_duration / (av_q2d(in_stream->time_base) * AV_TIME_BASE);
            //解决2倍速问题...
            pkt.pts *= 2;
            //计算duration以输入流时间基表示的ffmpeg内部时间
            pkt.duration = frame_duration / (av_q2d(in_stream->time_base) * AV_TIME_BASE);
            pkt.dts = pkt.pts;
            frame_index++;
        }

        if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
        {
            ts_video = pkt.pts;
        }
        else if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
        {
            ts_audio = pkt.pts;
        }

        //时间戳转换
        pkt.pts = av_rescale_q_rnd(pkt.pts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
        pkt.dts = av_rescale_q_rnd(pkt.dts, in_stream->time_base, out_stream->time_base, (AVRounding)(AV_ROUND_INF | AV_ROUND_PASS_MINMAX));
        pkt.duration = av_rescale_q_rnd(pkt.duration, in_stream->time_base, out_stream->time_base, (AVRounding)AV_ROUND_INF);
        //输出文件的音视频流来自不同的文件,因此packet中流的索引与输出文件中流的索引可能不匹配,可能出现packet中音频帧和视频帧所对应的stream_index是一样的的情况
        //因此将packet中的音频或视频帧与输出流的音视频流的索引匹配上
        pkt.stream_index = out_stream->index;

        AVBSFContext *bsf_ctx = NULL;

        #if AAC_ADTS_TO_ASC
            if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO)
            {
                bsf_ctx = bsf_ctx_audio;
            }
        #endif

        #if H264_AVCC_TO_ANNEXB
            if (in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
            {
                bsf_ctx = bsf_ctx_video;
            }
        #endif

        if ((AAC_ADTS_TO_ASC && in_stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) || (H264_AVCC_TO_ANNEXB && in_stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO))
        {                
            //将packet送入过滤器
            int ans = av_bsf_send_packet(bsf_ctx, &pkt);
            if (ans < 0)
            {
                //需要多个packet才能过滤
                if (ans == AVERROR(EAGAIN))
                {
                    av_packet_unref(&pkt);
                    continue;
                }
                printf("failed to send packet to filter\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }

            //一个输入packet可能产生多个输出packet
            do
            {
                ans = av_bsf_receive_packet(bsf_ctx, &pkt_filtered);
                if (ans < 0 && ans != AVERROR(EAGAIN))
                {
                    if (ans == AVERROR_EOF)
                    {
                        break;
                    }
                    else
                    {
                        printf("failed to receive packet from filter\n");
                        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                        return -1;
                    }
                }
                //交错写入
                if (av_interleaved_write_frame(out_fmt_ctx, &pkt_filtered) < 0)
                {
                    printf("failed to write frame to the output file\n");
                    release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                    return -1;                    
                }
                av_packet_unref(&pkt_filtered);
            } while (ans == AVERROR(EAGAIN));
        }
        else
        {
            //交错写入
            if (av_interleaved_write_frame(out_fmt_ctx, &pkt) < 0)
            {
                printf("failed to write frame to the output file\n");
                release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
                return -1;
            }
        }

        av_packet_unref(&pkt);
    }

    //写入文件尾
    if (av_write_trailer(out_fmt_ctx) < 0)
    {
        printf("failed to write tail to the output file\n");
        release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
        return -1;
    }

    release_context(in_fmt_ctx_video, in_fmt_ctx_audio, out_fmt_ctx, &pkt, &pkt_filtered, bsf_ctx_video, bsf_ctx_audio);
    return 0;
}

项目代码及使用方法:FFmpeg_Learning_Projects/Mux_Audio_Video at master · zn111111/FFmpeg_Learning_Projects (github.com)文章来源地址https://www.toymoban.com/news/detail-798315.html

到了这里,关于基于新版FFmpeg(FFmpeg 6.1)的音视频复用(不涉及编解码)的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!

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

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

相关文章

  • 哔哩哔哩视频合并 B站缓存视频合并 安卓版 音视频合并 基于ffmpeg

    此软件是为了帮助网友合并哔哩哔哩缓存视频,Android上将bilibili缓存视频合并导出为mp4,你可以将它理解为一个专用的格式工厂,并不涉及破解相关内容,仅仅用于学习技术交流,严禁用于商业用途,如有侵权请联系我删档,对你带来困惑和不便我深感抱歉。 合并(导出)B站缓

    2024年02月02日
    浏览(51)
  • 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日
    浏览(44)
  • ffmpeg@音视频工具@音视频合并

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

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

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

    2024年02月12日
    浏览(40)
  • 音视频 ffmpeg命令提取音视频数据

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

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

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

    2024年04月25日
    浏览(67)
  • 音视频 ffmpeg视频裁剪

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

    2024年02月10日
    浏览(35)
  • 音视频 ffmpeg命令视频录制(Windows)

    先安装dshow软件 Screen Capturer Recorder, 项目地址:https://sourceforge.net/projects/screencapturer/files/ 然后查看可用设备名字:ffmpeg -list_devices true -f dshow -i dummy 录制视频(默认参数) 录制声音(默认参数) 同时录制声音和视频(默认参数) 查看视频录制的可选参数 查看视频录制的可

    2024年02月10日
    浏览(47)
  • [一]ffmpeg音视频解码

    (1)下载FFmpeg源码(v3.3.6) 下载地址( http://www.ffmpeg.org/download.html#releases ) (2)下载NDK(r14b) 下载地址( https://developer.android.google.cn/ndk/downloads/index.html) (3)编写Android编译脚本 (1)用ftp上传到Ubuntu中 (2)解压Ffmpeg(tar -zxvf ffmpeg-3.3.6.tar.gz) (3)解压NDK(unzip andro

    2024年01月19日
    浏览(38)
  • ffmpeg解决bilibili下载的音视频分离问题,将音视频一键合成

    1:到FFmpeg下载安装包,我安装的是windows 下载打包文件 2:解压文件到本地 以下为解压后的文件视图 3:点击进去第一个bin文件,复制该目录,安装到本地环境变量中 然后点击确定进行保存设置 4:ctrl+r打开运行,输入cmd打开命令窗口,输入ffmpeg -version查看版本 安装成功 将所

    2024年02月11日
    浏览(32)

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

支付宝扫一扫打赏

博客赞助

微信扫一扫打赏

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

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

二维码1

领取红包

二维码2

领红包