【FFmpeg学习】视频变慢处理

视频慢动作处理是个比较常用的操作,可以在播放的时候处理,这里我们考虑把视频修改为慢动作,使用ffmpeg命令,可以这样

ffmpeg -i test.mp4 -vf "setpts=5*PTS" -an test_slow3.mp4

这里把视频放慢了5倍,生成的文件大小也变大了几倍。

怎么用程序来实现呢,参考一下GPT给出的code,

cpp 复制代码
#include <iostream>
#include <string>
#include <cstdlib>
#include <cstring>
extern "C" {
#include <libavformat/avformat.h>
#include <libavcodec/avcodec.h>
#include <libavutil/opt.h>
}

// 慢速播放的速度因子
const double SLOWDOWN_FACTOR = 2.0;

int main(int argc, char* argv[]) {
    if (argc < 3) {
        std::cout << "Usage: " << argv[0] << " input_file output_file" << std::endl;
        return 1;
    }

    const std::string inputFileName = argv[1];
    const std::string outputFileName = argv[2];

    av_register_all();

    AVFormatContext* inputFormatContext = nullptr;
    if (avformat_open_input(&inputFormatContext, inputFileName.c_str(), nullptr, nullptr) != 0) {
        std::cerr << "Failed to open input file: " << inputFileName << std::endl;
        return 1;
    }

    if (avformat_find_stream_info(inputFormatContext, nullptr) < 0) {
        std::cerr << "Failed to retrieve input stream information" << std::endl;
        avformat_close_input(&inputFormatContext);
        return 1;
    }

    AVFormatContext* outputFormatContext = nullptr;
    if (avformat_alloc_output_context2(&outputFormatContext, nullptr, nullptr, outputFileName.c_str()) < 0) {
        std::cerr << "Failed to allocate output format context" << std::endl;
        avformat_close_input(&inputFormatContext);
        return 1;
    }

    for (unsigned int i = 0; i < inputFormatContext->nb_streams; ++i) {
        AVStream* inputStream = inputFormatContext->streams[i];
        AVStream* outputStream = avformat_new_stream(outputFormatContext, nullptr);
        if (!outputStream) {
            std::cerr << "Failed to allocate output stream" << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return 1;
        }

        if (avcodec_parameters_copy(outputStream->codecpar, inputStream->codecpar) < 0) {
            std::cerr << "Failed to copy codec parameters" << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return 1;
        }

        outputStream->time_base = inputStream->time_base;
    }

    if (!(outputFormatContext->oformat->flags & AVFMT_NOFILE)) {
        if (avio_open(&outputFormatContext->pb, outputFileName.c_str(), AVIO_FLAG_WRITE) < 0) {
            std::cerr << "Failed to open output file: " << outputFileName << std::endl;
            avformat_close_input(&inputFormatContext);
            avformat_free_context(outputFormatContext);
            return 1;
        }
    }

    if (avformat_write_header(outputFormatContext, nullptr) < 0) {
        std::cerr << "Failed to write output file header" << std::endl;
        avformat_close_input(&inputFormatContext);
        avformat_free_context(outputFormatContext);
        return 1;
    }

    AVPacket packet;
    while (av_read_frame(inputFormatContext, &packet) >= 0) {
        AVStream* outputStream = outputFormatContext->streams[packet.stream_index];

        // 慢速播放的时间戳调整
        packet.pts *= static_cast<int64_t>(SLOWDOWN_FACTOR);
        packet.dts *= static_cast<int64_t>(SLOWDOWN_FACTOR);

        packet.duration = static_cast<int>(packet.duration * SLOWDOWN_FACTOR);

        av_interleaved_write_frame(outputFormatContext, &packet);
        av_packet_unref(&packet);
    }

    av_write_trailer(outputFormatContext);

    avformat_close_input(&inputFormatContext);
    avformat_free_context(outputFormatContext);

    return 0;
}

这个代码执行后并没有实现变慢,参考一下,进行修改后可以实现慢动作处理,如下

cpp 复制代码
int slow() {
    std::string filename = "test.mp4";     // 输入MP4文件名
    std::string outputFilename = "test_slow.mp4";  // 输出图片文件名

    AVFormatContext* ofmt_ctx = nullptr;
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, outputFilename.c_str());
    const AVOutputFormat* ofmt = ofmt_ctx->oformat;

    AVFormatContext* formatContext = nullptr;
    if (avformat_open_input(&formatContext, filename.c_str(), nullptr, nullptr) != 0) {
        std::cerr << "Error opening input file" << std::endl;
        return -1;
    }

    if (avformat_find_stream_info(formatContext, nullptr) < 0) {
        std::cerr << "Error finding stream information" << std::endl;
        avformat_close_input(&formatContext);
        return -1;
    }

    const AVCodec* codec = nullptr;
    int videoStreamIndex = -1;

    for (unsigned int i = 0; i < formatContext->nb_streams; i++) {
        if (formatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
            videoStreamIndex = i;
            codec = avcodec_find_decoder(formatContext->streams[i]->codecpar->codec_id);

            //
            AVStream* out_stream = avformat_new_stream(ofmt_ctx, NULL);
            avcodec_parameters_copy(out_stream->codecpar, formatContext->streams[i]->codecpar);
            out_stream->codecpar->codec_tag = 0;

            break;
        }
    }

    avio_open(&ofmt_ctx->pb, outputFilename.c_str(), AVIO_FLAG_WRITE);
    avformat_write_header(ofmt_ctx, NULL);

 

    AVPacket packet;
    av_init_packet(&packet);

    // 计算目标时间戳
    int64_t targetTimestamp = targetSecond * AV_TIME_BASE;

    // 查找目标时间戳所对应的帧
    AVFrame* frame = av_frame_alloc();
    bool foundTargetFrame = false;
    int count = 0;
    while (av_read_frame(formatContext, &packet) >= 0) {
        if (packet.stream_index == videoStreamIndex) {
            AVStream* inputStream = formatContext->streams[packet.stream_index];
            AVStream* outputStream = ofmt_ctx->streams[packet.stream_index];

            // 计算新的时间戳
            cout << "1, pts=" << packet.pts << endl;
            packet.pts = av_rescale_q_rnd(packet.pts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF);
            packet.dts = av_rescale_q_rnd(packet.dts, inputStream->time_base, outputStream->time_base, AV_ROUND_NEAR_INF);
            packet.duration = av_rescale_q(packet.duration, inputStream->time_base, outputStream->time_base);
            packet.pos = -1;
            cout << "2, pts=" << packet.pts << endl;

            // 慢速播放的时间戳调整
            packet.pts *= 5;
            packet.dts *= 5;
            packet.duration *= 5;
  //          av_log(NULL, AV_LOG_INFO, "...av_write_frame(ofmt_ctx, packet);\n");
            int ret = av_write_frame(ofmt_ctx, &packet);
            if (ret < 0) {
                std::cerr << "Error av_write_frame" << std::endl;
            }
            count++;

        }

        av_packet_unref(&packet);
    }

    av_write_trailer(ofmt_ctx);
 
    return 1;
}

通过pts的修改来实现

相关推荐
神奇霸王龙3 小时前
国产音乐视频 Prompt 三段式屠夫榜:5 个国产视频模型实测对比
人工智能·ai·prompt·音视频·agent·ai编程·agi
神奇霸王龙3 小时前
AI视频Prompt结构化实战指南:可灵/万相/豆包
人工智能·ai·prompt·aigc·音视频·ai编程
SEONIB_Explorer6 小时前
跨境电商新手零门槛教程:AI 一键生成 TikTok/Ins 投放短视频,5 步 60 秒出片
人工智能·音视频·跨境电商·视频制作·veonib
运营小白9 小时前
电商产品链接自动生成视频广告:从“信息搬运“到“链接进、视频出“的工程化思路
大数据·人工智能·aigc·音视频·veonib·电商视频广告·产品链接解析
AI创界者12 小时前
【AI视频生成】ComfyUI + LTX-Video 2.3 整合包:超低显存实现高帧率AI视频渲染与工作流详解
人工智能·aigc·音视频
开开心心就好15 小时前
Word双击预览图片插件弥补Word功能缺失
人工智能·python·智能手机·ocr·电脑·word·音视频
Yeauty17 小时前
你那条 ffmpeg 命令,一键翻成 Rust builder 代码
开发语言·rust·ffmpeg
tedcloud1231 天前
OmniRoute怎么部署?开源AI模型路由平台Linux部署教程
linux·服务器·人工智能·开源·音视频
潜创微科技1 天前
8K HDMI 2.1 多源切换芯片方案,一颗芯片搞定长线不黑屏
音视频
音视频牛哥1 天前
一款优秀的视频直播软件应具备哪些核心功能?结合 SmartMediaKit 谈实时音视频系统的能力边界
音视频·视频直播·低延迟rtsp播放器·gb28181接入·rtsp服务器·低延迟rtmp播放器·鸿蒙next rtsp