学习了雷神的文章,慕斯人分享精神,感其英年而逝,不胜唏嘘。他有分享一个转码程序《最简单的基于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;
}

  

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

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

    本文介绍一个简单的基于FFmpeg的转码器.它可以将一种视频格式(包括封转格式和编码格式)转换为另一种视频格式.转码器在视音频编解码处理的程序中,属于一个比较复杂的东西.因为它结合了视频的解码和编码. ...

  2. 最简单的基于FFMPEG的转码程序 —— 分析

    模块:  libavcodec    - 编码解码器         libavdevice   - 输入输出设备的支持         libavfilter   - 视音频滤镜支持         ...

  3. [开源]基于ffmpeg和libvlc的视频剪辑、播放器

    [开源]基于ffmpeg和libvlc的视频剪辑.播放器 以前研究的时候,写过一个简单的基于VLC的视频播放器.后来因为各种项目,有时为了方便测试,等各种原因,陆续加了一些功能,现在集成了视频播放.视 ...

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

    以前研究的时候,写过一个简单的基于VLC的视频播放器.后来因为各种项目,有时为了方便测试,等各种原因,陆续加了一些功能,现在集成了视频播放.视频加减速.视频剪切,视频合并(增加中)等功能在一起.有时候 ...

  5. 「小程序JAVA实战」小程序视频处理工具ffmpeg(47)

    转自:https://idig8.com/2018/09/16/xiaochengxujavashizhanxiaochengxushipinchuligongjuffmpeg46/ 前面已经把视频成 ...

  6. Java Web 中使用ffmpeg实现视频转码、视频截图

    Java Web 中使用ffmpeg实现视频转码.视频截图 转载自:[ http://www.cnblogs.com/dennisit/archive/2013/02/16/2913287.html  ...

  7. Flink源码分析 - 剖析一个简单的Flink程序

    本篇文章首发于头条号Flink程序是如何执行的?通过源码来剖析一个简单的Flink程序,欢迎关注头条号和微信公众号"大数据技术和人工智能"(微信搜索bigdata_ai_tech) ...

  8. FFmpeg 入门(1):截取视频帧

    本文转自:FFmpeg 入门(1):截取视频帧 | www.samirchen.com 背景 在 Mac OS 上如果要运行教程中的相关代码需要先安装 FFmpeg,建议使用 brew 来安装: // ...

  9. ffmpeg/ffplay源码剖析笔记<转>

    转载:http://www.cnblogs.com/azraelly/ http://www.cnblogs.com/azraelly/archive/2013/01/18/2865858.html ...

随机推荐

  1. SVN服务器安装与本地连接

    SVN服务器安装与本地连接 系统环境 Centos7 查看是否安装了低版本SVN [root@svn-server ~]# rpm -qa subversion 卸载旧版本SVN [root@svn- ...

  2. find 的一些用法

    find的一些用法 例1:find . -type f -exec chmod -R 644 {} \ ;   #{}代表签名的输出,\;代表结束命令操作结束 例2: find -print0 |xa ...

  3. where语句中不能直接使用聚合函数

    1.问题描述 select deptno ,avg(sal) from emp where count(*)>3 group by deptno; 在where 句中使用聚合函数count(*) ...

  4. OpenGL 混合功能

    一.概念:简言之,即在颜色缓存区和深度缓存区中,新旧颜色的覆盖和替换问题:已经存在于缓存区的为目标颜色,即将进入缓存区的为源颜色: 二.应用场景:在不透明的图形前绘制一个透明的图形: 三.主要代码实现 ...

  5. swoft orm中的坑(针对实体类的属性名称和数据库字段不相等)

    最近在用swoft的orm,发现了一些问题: 首先看下实体类的定义 它的属性名称和所映射的数据库字段名不一致,这个就会导致蛋疼的问题,首先,在我们使用orm的时候,应该使用哪个字段? 我直接说结论,在 ...

  6. JavaEE笔记(二)

    查询load()和get()的区别 # 以下查询都是根据id查询 // Load和Get都会在第一次查询的是创建一个一级缓存查询语句 // 下一次查询的时候从缓存中查询是否有缓存的语句 // 如果有只 ...

  7. 标准输入输出 sys.stdin与sys.stdin

    1.python中的标准输入输出 如果需要更好的控制输出,而print不能满足需求,input也不能 sys.stdout,sys.stdin,sys.stderr就是你需要的. 2.输入:sys.s ...

  8. [SHOI2008]仙人掌图

    [SHOI2008]仙人掌图 LG传送门 还不会仙人掌的同学可以看看我对仙人掌知识的一些梳理. 题意就是求仙人掌的直径,直径定义为图中最短路径最长的两点间的最短路径长度. 按照套路,先考虑求树的直径我 ...

  9. Core - Provide an easy way to store administrator and user model differences in a custom store (e.g., in a database)

    https://www.devexpress.com/Support/Center/Question/Details/S32444/core-provide-an-easy-way-to-store- ...

  10. 用Micro:bit控制遥控车

    很多遥控车是用Arduino来控制,同样也可以用Micro:bit来控制.这篇文章我们就来做个测试. 这次需要用到扩展板,管脚比较多,请参考下图 一.材料: •micro:bit 二片 •micro: ...