FFmpeg简单转码程序--视频剪辑

学习了雷神的文章,慕斯人分享精神,感其英年而逝,不胜唏嘘。他有分享一个转码程序《最简单的基于FFMPEG的转码程序》其中使用了filter(参考了ffmpeg.c中的流程),他曾说想再编写一个不需要filter的版本,可惜未有机会。恰好工作中有相关ffmpeg处理内容,故狗尾续貂,撰写本文。

相关流程:

1.打开输入文件

2.打开输出文件

3.设置解码环境

4.设置输出流信息

5.设置编码环境

6.打开输入流循环读取,解码再编码写入

7.fflush解码和编码ctx

8.关闭文件

本文的代码,为了支持视频精确剪辑,因为GOP关键帧问题,需要使用解码再编码,在编码中对时间做校验

使用方式:

./mycut input output start end

  如,截取1到10秒的视频:

代码如下:

// mycut.cpp
extern "C"
{
#include <libavutil/time.h>
#include <libavutil/timestamp.h>
#include <libavformat/avformat.h>
#include <libavformat/avio.h>
#include <libavfilter/avfiltergraph.h>
#include <libavfilter/buffersink.h>
#include <libavfilter/buffersrc.h>
#include <libavutil/opt.h>
#include <libavutil/imgutils.h>

#include <sys/types.h>
#include <unistd.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <strings.h>
#include <stdio.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <errno.h>
#include <sys/file.h>
#include <signal.h>
#include <sys/wait.h>
#include <time.h> // time_t, tm, time, localtime, strftime
#include <sys/time.h> // time_t, tm, time, localtime, strftime
}

#define TIME_DEN 1000

// Returns the local date/time formatted as 2014-03-19 11:11:52
char* getFormattedTime(void);

// Remove path from filename
#define __SHORT_FILE__ (strrchr(__FILE__, ‘/‘) ? strrchr(__FILE__, ‘/‘) + 1 : __FILE__)

// Main log macro
//#define __LOG__(format, loglevel, ...) printf("%s %-5s [%s] [%s:%d] " format "\n", getFormattedTime(), loglevel, __func__, __SHORT_FILE__, __LINE__, ## __VA_ARGS__)
#define __LOG__(format, loglevel, ...) printf("%ld %-5s [%s] [%s:%d] " format "\n", current_timestamp(), loglevel, __func__, __SHORT_FILE__, __LINE__, ## __VA_ARGS__)

// Specific log macros with
#define LOGDEBUG(format, ...) __LOG__(format, "DEBUG", ## __VA_ARGS__)
#define LOGWARN(format, ...) __LOG__(format, "WARN", ## __VA_ARGS__)
#define LOGERROR(format, ...) __LOG__(format, "ERROR", ## __VA_ARGS__)
#define LOGINFO(format, ...) __LOG__(format, "INFO", ## __VA_ARGS__)

// Returns the local date/time formatted as 2014-03-19 11:11:52
char* getFormattedTime(void) {

    time_t rawtime;
    struct tm* timeinfo;

    time(&rawtime);
    timeinfo = localtime(&rawtime);

    // Must be static, otherwise won‘t work
    static char _retval[26];
    strftime(_retval, sizeof(_retval), "%Y-%m-%d %H:%M:%S", timeinfo);

    return _retval;
}

long long current_timestamp() {
    struct timeval te;
    gettimeofday(&te, NULL); // get current time
    long long milliseconds = te.tv_sec*1000LL + te.tv_usec/1000; // calculate milliseconds
    // printf("milliseconds: %lld\n", milliseconds);
    return milliseconds;
}

static int encode_and_save_pkt(AVCodecContext *enc_ctx, AVFormatContext *ofmt_ctx, AVStream *out_stream)
{
    AVPacket enc_pkt;
    av_init_packet(&enc_pkt);
    enc_pkt.data = NULL;
    enc_pkt.size = 0;

    int ret = 0;
    while (ret >= 0)
    {
        ret = avcodec_receive_packet(enc_ctx, &enc_pkt);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            ret = 0;
            break;
        }

        else if (ret < 0)
        {
            printf("[avcodec_receive_packet]Error during encoding, ret:%d\n", ret);
            break;
        }
        LOGDEBUG("encode type:%d pts:%d dts:%d\n", enc_ctx->codec_type, enc_pkt.pts, enc_pkt.dts);

        /* rescale output packet timestamp values from codec to stream timebase */
        av_packet_rescale_ts(&enc_pkt, enc_ctx->time_base, out_stream->time_base);
        enc_pkt.stream_index = out_stream->index;

        ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
        if (ret < 0)
        {
            printf("write frame error, ret:%d\n", ret);
            break;
        }

        av_packet_unref(&enc_pkt);
    }
    return ret;
}

static int decode_and_send_frame(AVCodecContext *dec_ctx, AVCodecContext *enc_ctx, int start, int end)
{
    AVFrame *frame = av_frame_alloc();
    int ret = 0;

    while (ret >= 0)
    {
        ret = avcodec_receive_frame(dec_ctx, frame);
        if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
        {
            ret = 0;
            break;
        }
        else if (ret < 0)
        {
            printf("Error while receiving a frame from the decoder");
            break;
        }
        int pts = frame->pts;
        LOGDEBUG("decode type:%d pts:%d\n", dec_ctx->codec_type, pts);
        if ((pts < start*TIME_DEN) || (pts > end*TIME_DEN)) {  // 数据裁剪
            continue;
        }
        frame->pict_type = AV_PICTURE_TYPE_NONE;
        // 修改pts
        frame->pts  = pts - start*TIME_DEN;
        //printf("pts:%d\n", frame->pts);

        ret = avcodec_send_frame(enc_ctx, frame);
        if (ret < 0)
        {
            printf("Error sending a frame for encoding\n");
            break;
        }
    }
    av_frame_free(&frame);
    return ret;
}

static int open_decodec_context(int *stream_idx,
                                AVCodecContext **dec_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{
    int ret, stream_index;
    AVStream *st;
    AVCodec *dec = NULL;
    AVDictionary *opts = NULL;

    ret = av_find_best_stream(fmt_ctx, type, -1, -1, &dec, 0);
    if (ret < 0)
    {
        fprintf(stderr, "Could not find %s stream in input file \n",
                av_get_media_type_string(type));
        return ret;
    }
    else
    {
        stream_index = ret;
        st = fmt_ctx->streams[stream_index];

        /* Allocate a codec context for the decoder */
        *dec_ctx = avcodec_alloc_context3(dec);
        if (!*dec_ctx)
        {
            fprintf(stderr, "Failed to allocate the %s codec context\n",
                    av_get_media_type_string(type));
            return AVERROR(ENOMEM);
        }

        /* Copy codec parameters from input stream to output codec context */
        if ((ret = avcodec_parameters_to_context(*dec_ctx, st->codecpar)) < 0)
        {
            fprintf(stderr, "Failed to copy %s codec parameters to decoder context\n",
                    av_get_media_type_string(type));
            return ret;
        }

        if ((*dec_ctx)->codec_type == AVMEDIA_TYPE_VIDEO)
            (*dec_ctx)->framerate = av_guess_frame_rate(fmt_ctx, st, NULL);

        /* Init the decoders, with or without reference counting */
        av_dict_set_int(&opts, "refcounted_frames", 1, 0);
        if ((ret = avcodec_open2(*dec_ctx, dec, &opts)) < 0)
        {
            fprintf(stderr, "Failed to open %s codec\n",
                    av_get_media_type_string(type));
            return ret;
        }
        *stream_idx = stream_index;
    }

    return 0;
}

static int set_encode_option(AVCodecContext *dec_ctx, AVDictionary **opt)
{
    const char *profile = avcodec_profile_name(dec_ctx->codec_id, dec_ctx->profile);
    if (profile)
    {
        if (!strcasecmp(profile, "high"))
        {
            av_dict_set(opt, "profile", "high", 0);
        }
    }
    else
    {
        av_dict_set(opt, "profile", "main", 0);
    }

    av_dict_set(opt, "threads", "16", 0);

    av_dict_set(opt, "preset", "slow", 0);
    av_dict_set(opt, "level", "4.0", 0);

    return 0;
}

static int open_encodec_context(int stream_index, AVCodecContext **oenc_ctx, AVFormatContext *fmt_ctx, enum AVMediaType type)
{
    int ret;
    AVStream *st;
    AVCodec *encoder = NULL;
    AVDictionary *opts = NULL;
    AVCodecContext *enc_ctx;

    st = fmt_ctx->streams[stream_index];

    /* find encoder for the stream */
    encoder = avcodec_find_encoder(st->codecpar->codec_id);
    if (!encoder)
    {
        fprintf(stderr, "Failed to find %s codec\n",
                av_get_media_type_string(type));
        return AVERROR(EINVAL);
    }

    enc_ctx = avcodec_alloc_context3(encoder);
    if (!enc_ctx)
    {
        printf("Failed to allocate the encoder context\n");
        return AVERROR(ENOMEM);
    }

    AVCodecContext *dec_ctx = st->codec;
    if (type == AVMEDIA_TYPE_VIDEO)
    {
        enc_ctx->height = dec_ctx->height;
        enc_ctx->width = dec_ctx->width;
        enc_ctx->sample_aspect_ratio = dec_ctx->sample_aspect_ratio;

        enc_ctx->bit_rate = dec_ctx->bit_rate;
        enc_ctx->rc_max_rate = dec_ctx->bit_rate;
        enc_ctx->rc_buffer_size = dec_ctx->bit_rate;
        enc_ctx->bit_rate_tolerance = 0;
        // use yuv420P
        enc_ctx->pix_fmt = AV_PIX_FMT_YUV420P;
        // set frame rate
        enc_ctx->time_base.num = 1;
        enc_ctx->time_base.den = TIME_DEN;

        enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
        enc_ctx->has_b_frames = false;
        enc_ctx->max_b_frames = 0;
        enc_ctx->gop_size = 120;

        set_encode_option(dec_ctx, &opts);
    }
    else if (type == AVMEDIA_TYPE_AUDIO)
    {
        enc_ctx->sample_rate = dec_ctx->sample_rate;
        enc_ctx->channel_layout = dec_ctx->channel_layout;
        enc_ctx->channels = av_get_channel_layout_nb_channels(enc_ctx->channel_layout);
        /* take first format from list of supported formats */
        enc_ctx->sample_fmt = encoder->sample_fmts[0];
        enc_ctx->time_base = (AVRational){1, TIME_DEN};
        enc_ctx->bit_rate = dec_ctx->bit_rate;
        enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    }
    else
    {
        ret = avcodec_copy_context(enc_ctx, st->codec);
        if (ret < 0)
        {
            fprintf(stderr, "Failed to copy context from input to output stream codec context\n");
            return ret;
        }
    }

    if ((ret = avcodec_open2(enc_ctx, encoder, &opts)) < 0)
    {
        fprintf(stderr, "Failed to open %s codec\n",
                av_get_media_type_string(type));
        return ret;
    }

    *oenc_ctx = enc_ctx;
    return 0;
}
int test_cut(char *input_file, char *output_file, int start, int end)
{
    int ret = 0;

    av_register_all();

    // 输入流
    AVFormatContext *ifmt_ctx = NULL;
    AVCodecContext *video_dec_ctx = NULL;
    AVCodecContext *audio_dec_ctx = NULL;
    char *flv_name = input_file;
    int video_stream_idx = 0;
    int audio_stream_idx = 1;

    // 输出流
    AVFormatContext *ofmt_ctx = NULL;
    AVCodecContext *audio_enc_ctx = NULL;
    AVCodecContext *video_enc_ctx = NULL;

    if ((ret = avformat_open_input(&ifmt_ctx, flv_name, 0, 0)) < 0)
    {
        printf("Could not open input file ‘%s‘ ret:%d\n", flv_name, ret);
        goto end;
    }

    if ((ret = avformat_find_stream_info(ifmt_ctx, 0)) < 0)
    {
        printf("Failed to retrieve input stream information");
        goto end;
    }

    if (open_decodec_context(&video_stream_idx, &video_dec_ctx, ifmt_ctx, AVMEDIA_TYPE_VIDEO) < 0)
    {
        printf("fail to open vedio decode context, ret:%d\n", ret);
        goto end;
    }
    if (open_decodec_context(&audio_stream_idx, &audio_dec_ctx, ifmt_ctx, AVMEDIA_TYPE_AUDIO) < 0)
    {
        printf("fail to open audio decode context, ret:%d\n", ret);
        goto end;
    }
    av_dump_format(ifmt_ctx, 0, input_file, 0);

    // 设置输出
    avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, output_file);
    if (!ofmt_ctx)
    {
        printf("can not open ouout context");
        goto end;
    }

    // video stream
    AVStream *out_stream;
    out_stream = avformat_new_stream(ofmt_ctx, NULL);
    if (!out_stream)
    {
        printf("Failed allocating output stream\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }
    if ((ret = open_encodec_context(video_stream_idx, &video_enc_ctx, ifmt_ctx, AVMEDIA_TYPE_VIDEO)) < 0)
    {
        printf("video enc ctx init err\n");
        goto end;
    }
    ret = avcodec_parameters_from_context(out_stream->codecpar, video_enc_ctx);
    if (ret < 0)
    {
        printf("Failed to copy codec parameters\n");
        goto end;
    }
    video_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    out_stream->time_base = video_enc_ctx->time_base;
    out_stream->codecpar->codec_tag = 0;

    // audio stream
    out_stream = avformat_new_stream(ofmt_ctx, NULL);
    if (!out_stream)
    {
        printf("Failed allocating output stream\n");
        ret = AVERROR_UNKNOWN;
        goto end;
    }

    if ((ret = open_encodec_context(audio_stream_idx, &audio_enc_ctx, ifmt_ctx, AVMEDIA_TYPE_AUDIO)) < 0)
    {
        printf("audio enc ctx init err\n");
        goto end;
    }
    ret = avcodec_parameters_from_context(out_stream->codecpar, audio_enc_ctx);
    if (ret < 0)
    {
        printf("Failed to copy codec parameters\n");
        goto end;
    }
    audio_enc_ctx->flags |= AV_CODEC_FLAG_GLOBAL_HEADER;
    out_stream->time_base = audio_enc_ctx->time_base;
    out_stream->codecpar->codec_tag = 0;

    av_dump_format(ofmt_ctx, 0, output_file, 1);

    // 打开文件
    if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
    {
        ret = avio_open(&ofmt_ctx->pb, output_file, AVIO_FLAG_WRITE);
        if (ret < 0)
        {
            printf("Could not open output file ‘%s‘\n", output_file);
            goto end;
        }
    }

    ret = avformat_write_header(ofmt_ctx, NULL);
    if (ret < 0)
    {
        printf("Error occurred when opening output file\n");
        goto end;
    }

    AVPacket flv_pkt;
    int stream_index;
    while (1)
    {
        AVPacket *pkt = &flv_pkt;
        ret = av_read_frame(ifmt_ctx, pkt);
        if (ret < 0)
        {
            break;
        }

        if (pkt->stream_index == video_stream_idx && (pkt->flags & AV_PKT_FLAG_KEY))
        {
            printf("pkt.dts = %ld pkt.pts = %ld pkt.stream_index = %d is key frame\n", pkt->dts, pkt->pts, pkt->stream_index);
        }
        stream_index = pkt->stream_index;

        if (pkt->stream_index == video_stream_idx)
        {
            ret = avcodec_send_packet(video_dec_ctx, pkt);
            LOGDEBUG("read type:%d pts:%d dts:%d\n", video_dec_ctx->codec_type, pkt->pts, pkt->dts);

            ret = decode_and_send_frame(video_dec_ctx, video_enc_ctx, start, end);
            ret = encode_and_save_pkt(video_enc_ctx, ofmt_ctx, ofmt_ctx->streams[0]);
            if (ret < 0)
            {
                printf("re encode video error, ret:%d\n", ret);
            }
        }
        else if (pkt->stream_index == audio_stream_idx)
        {
            ret = avcodec_send_packet(audio_dec_ctx, pkt);
            LOGDEBUG("read type:%d pts:%d dts:%d\n", audio_dec_ctx->codec_type, pkt->pts, pkt->dts);

            ret = decode_and_send_frame(audio_dec_ctx, audio_enc_ctx, start, end);
            ret = encode_and_save_pkt(audio_enc_ctx, ofmt_ctx, ofmt_ctx->streams[1]);
            if (ret < 0)
            {
                printf("re encode audio error, ret:%d\n", ret);
            }
        }
        av_packet_unref(pkt);
    }
    // fflush
    // fflush encode
    avcodec_send_packet(video_dec_ctx, NULL);
    decode_and_send_frame(video_dec_ctx, video_enc_ctx, start, end);
    avcodec_send_packet(audio_dec_ctx, NULL);
    decode_and_send_frame(audio_dec_ctx, audio_enc_ctx, start, end);
    // fflush decode
    avcodec_send_frame(video_enc_ctx, NULL);
    encode_and_save_pkt(video_enc_ctx, ofmt_ctx, ofmt_ctx->streams[0]);
    avcodec_send_frame(audio_enc_ctx, NULL);
    encode_and_save_pkt(audio_enc_ctx, ofmt_ctx, ofmt_ctx->streams[1]);
    LOGDEBUG("stream end\n");
    av_write_trailer(ofmt_ctx);

end:
    avformat_close_input(&ifmt_ctx);
    if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
        avio_closep(&ofmt_ctx->pb);
    avformat_free_context(ofmt_ctx);

    avcodec_free_context(&video_dec_ctx);
    avcodec_free_context(&audio_dec_ctx);

    return ret;
}

int main(int argc, char **argv)
{
    int ret = 0;
    if (argc < 5)
    {
        printf("Usage: %s input output start end \n", argv[0]);
        return 1;
    }
    char *input_file = argv[1];
    char *output_file = argv[2];
    int start = atoi(argv[3]);
    int end = atoi(argv[4]);
    ret = test_cut(input_file, output_file, start, end);
    return ret;
}

  

原文地址:https://www.cnblogs.com/hustlijian/p/9753787.html

时间: 2024-09-28 05:38:37

FFmpeg简单转码程序--视频剪辑的相关文章

最简单的基于FFMPEG的转码程序

本文介绍一个简单的基于FFmpeg的转码器.转码器在视音频编解码处理的程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码.一个视频播放器,一般只包含解码功能:一个视频编码工具,一般只包含编码功能:而一个视频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合.下图例举了一个视频的转码流程.输入视频的封装格式是FLV,视频编码标准是H.264,音频编码标准是AAC:输出视频的封装格式是AVI,视频编码标准是MPEG2,音频编码标准是MP3.从流程中可以看出

最简单的基于FFMPEG的转码程序 [转]

本文介绍一个简单的基于FFmpeg的转码器.它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式.转码器在视音频编解码处理的 程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码.一个视频播放器,一般只包含解码功能:一个视频编码工具,一般只包含编码功能:而一个视 频转码器,则需要先对视频进行解码,然后再对视频进行编码,因而相当于解码器和编码器的结合.下图例举了一个视频的转码流程.输入视频的封装格式是 FLV,视频编码标准是H.264,音频编码标准是AAC:输出视频的封装格式

最简单的基于FFMPEG的转码程序分析 +ffmpga代码简析(转 +总结)

模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         libavformat   - 视音频等格式的解析         libavutil     - 工具库         libpostproc   - 后期效果处理         libswscale    - 图像颜色.尺寸转换 1. ffmpga代码简析 1.1 av_log() av_

最简单的基于FFMPEG的转码程序 [转] —— 分析

模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         libavformat   - 视音频等格式的解析         libavutil     - 工具库         libpostproc   - 后期效果处理         libswscale    - 图像颜色.尺寸转换 1. 主函数分析: int_tmain(int argc, 

基于ffmpeg和libvlc的视频剪辑、播放器

以前研究的时候,写过一个简单的基于VLC的视频播放器.后来因为各种项目,有时为了方便测试,等各种原因,陆续加了一些功能,现在集成了视频播放.视频加减速.视频剪切,视频合并(增加中)等功能在一起.有时候看点网上下载的视频,可以一边看,一边能处理视频前后的广告,感觉也还可以用,就想把它开源出去,一方面希望有需要的朋友可以参考.借鉴,另一方面也希望可以促进它进一步的丰富功能,最终能实现一款简单又够用的视频剪辑软件. 程序框架 先上一张程序截图 基本上讲,它的播放功能是基于VLC,剪辑功能是基于FFmp

超简单MVC应用程序播放WMV视频

本篇博文,介绍给大家的是,在MVC应用程序中,播放Windows media video(.wmv) 视频文件. Insus.NET的实现方法,把media player组件,嵌入MVC的控制器的ContentResult的Action中即可.在本次的演示中,Insus.NET只是传入三个参数,视频的长与宽以及视频文件路径. 如果还想设置参其它参数,可以照此雷同即可. 参数可以直接在Action中设置,也可以是获取来自数据库,前提是你参数设置的程序,存储于数据库中. 第一步,建立控制器: 第二步

ffmpeg/ffplay源码剖析笔记&lt;转&gt;

转载:http://www.cnblogs.com/azraelly/ http://www.cnblogs.com/azraelly/archive/2013/01/18/2865858.html 内容摘自<ffmpeg/ffplay源码剖析> 1.播放器一般原理 可以直观的看到播放这个媒体文件的基本模块(filter),七个模块按广度顺序:读文件模块(source filter),解复用模块(Demux filter),视/音频解码模块(Decode filter),颜色空间转换模块(Co

视频剪辑什么鬼?Python 带你高效创作短视频

近两年,抖音.快手将短视频推到风口浪尖上,要生产出高质量的视频,离不开视频剪辑这一环节:在全民剪片浪潮中,大众使用最多的剪辑软件如:Pr.FCPX.剪印.Vue 等.视频剪辑过程中,Python 一些比较实用的技能,帮助我们更快地进行短视频的创作. ! 1.提取背景音乐和修改音量先从抖音上下载视频文件,然后利用 moviepy 依赖库提取到背景音乐. def get_audio_from_video(video_path):    """    从视频中提取音频    :pa

如何利用CEF3创建一个简单的应用程序 (Windows Platform)

1. 说明 这篇文章主要讲述如何利用CEF3来创建一个简单的应用程序,引用的是1535及以上版本中包含的 Cefsimple 项目例子.如果想知道关于CEF3更多的使用方法,可以去访问 GeneralUsage. 2. 开始 首先,根据自身所使用的开发平台,可以去 这里 下载对应的发布版本.针对这个教程,我们需要下载1750或者更新的版本.当前支持的平台有Windows, Linux和Mac OS X.每一个版本都包含了当在特定平台上编译特定版本CEF3时所需要的所有文件和资源.您可以通过包含在