/**
* 最简单的基于FFmpeg的AVFilter例子(scale)
*
* 缪国凯(MK)
* [email protected]
*
* http://blog.csdn.net/dancing_night
*
* 本程序使用FFmpeg的AVfilter实现了视频的缩放功能。
*
*
*/
#include "stdafx.h"
#ifdef __cplusplus
extern "C"
{
#endif
#include
#include
#include
#include
#include
#include
#include
#include
#ifdef __cplusplus
};
#endif
#pragma comment(lib, "avcodec.lib")
#pragma comment(lib, "avformat.lib")
#pragma comment(lib, "avutil.lib")
#pragma comment(lib, "avdevice.lib")
#pragma comment(lib, "avfilter.lib")
//#pragma comment(lib, "avfilter.lib")
//#pragma comment(lib, "postproc.lib")
//#pragma comment(lib, "swresample.lib")
#pragma comment(lib, "swscale.lib")
static AVFormatContext *ifmt_ctx, *ofmt_ctx;
static AVCodecContext *pCodecCtx;
AVFilterContext *buffersink_ctx;
AVFilterContext *buffersrc_ctx;
AVFilterGraph *filter_graph;
static int video_stream_index = -1;
static int64_t last_pts = AV_NOPTS_VALUE;
static int open_input_file(const char *filename)
{
int ret;
AVCodec *dec;
if ((ret = avformat_open_input(&ifmt_ctx, filename, NULL, NULL)) < 0) {
printf( "Cannot open input file\n");
return ret;
}
if ((ret = avformat_find_stream_info(ifmt_ctx, NULL)) < 0) {
printf( "Cannot find stream information\n");
return ret;
}
/* select the video stream */
ret = av_find_best_stream(ifmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0);
if (ret < 0) {
printf( "Cannot find a video stream in the input file\n");
return ret;
}
video_stream_index = ret;
pCodecCtx = ifmt_ctx->streams[video_stream_index]->codec;
/* init the video decoder */
if ((ret = avcodec_open2(pCodecCtx, dec, NULL)) < 0)
{
printf( "Cannot open video decoder\n");
return ret;
}
return 0;
}
int openoutputfile(const char* filename, int width, int height)
{
AVStream *out_stream;
int ret = 0;
if ((ret = avformat_alloc_output_context2(&ofmt_ctx, NULL, NULL, filename)) < 0)
{
printf("can not alloc output context");
return ret;
}
for (int i = 0; i < ifmt_ctx->nb_streams; i++)
{
//if the stream is video stream then find the encoder default
//and set context and open the encoder
if (ifmt_ctx->streams[i]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
out_stream = NULL;
//new a stream
out_stream = avformat_new_stream(ofmt_ctx, NULL);
if (!out_stream)
{
printf("can not new stream for output");
return AVERROR_UNKNOWN;
}
//use default video encoder
out_stream->codec->codec = avcodec_find_encoder(ofmt_ctx->oformat->video_codec);
out_stream->codec->codec_type = AVMEDIA_TYPE_VIDEO;
out_stream->codec->pix_fmt = PIX_FMT_YUV420P;
out_stream->codec->width = width;
out_stream->codec->height = height;
out_stream->codec->time_base.num = 1;
out_stream->codec->time_base.den = 25;
out_stream->codec->bit_rate = 400000;
out_stream->codec->gop_size=250;
//H264
out_stream->codec->qmin = 10;
out_stream->codec->qmax = 40;
//Optional Param
out_stream->codec->max_b_frames=3;
AVDictionary *param = NULL;
if (out_stream->codec->codec->id == AV_CODEC_ID_H264)
{
av_dict_set(¶m, "preset", "slow", 0);
av_dict_set(¶m, "tune", "zerolatency", 0);
av_dict_set(¶m, "profile", "main", 0);
}
//open encoder
ret = avcodec_open2(out_stream->codec, out_stream->codec->codec, ¶m);
if (ret < 0)
{
printf("can not open encoder");
return ret;
}
if (ofmt_ctx->oformat->flags & AVFMT_GLOBALHEADER)
out_stream->codec->flags |= CODEC_FLAG_GLOBAL_HEADER;
break;
}
}
//dump output info
av_dump_format(ofmt_ctx, 0, filename, 1);
//open the output file handle
if (!(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
{
ret = avio_open(&ofmt_ctx->pb, filename, AVIO_FLAG_WRITE);
if (ret < 0)
{
printf("can not open the output file handle");
return ret;
}
}
//write output file header
if ((ret = avformat_write_header(ofmt_ctx, NULL)) < 0)
{
printf("can not write output file header");
return ret;
}
return 0;
}
static int init_filters(const char *filters_descr)
{
char args[512];
int ret;
AVFilter *buffersrc = avfilter_get_by_name("buffer");
AVFilter *buffersink = avfilter_get_by_name("ffbuffersink");
AVFilterInOut *outputs = avfilter_inout_alloc();
AVFilterInOut *inputs = avfilter_inout_alloc();
enum PixelFormat pix_fmts[] = { PIX_FMT_YUV420P, PIX_FMT_NONE };
AVBufferSinkParams *buffersink_params;
filter_graph = avfilter_graph_alloc();
/* buffer video source: the decoded frames from the decoder will be inserted here. */
_snprintf(args, sizeof(args),
"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d:pixel_aspect=%d/%d",
pCodecCtx->width, pCodecCtx->height, pCodecCtx->pix_fmt,
pCodecCtx->time_base.num, pCodecCtx->time_base.den,
pCodecCtx->sample_aspect_ratio.num, pCodecCtx->sample_aspect_ratio.den);
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in",
args, NULL, filter_graph);
if (ret < 0) {
printf("Cannot create buffer source\n");
return ret;
}
/* buffer video sink: to terminate the filter chain. */
buffersink_params = av_buffersink_params_alloc();
buffersink_params->pixel_fmts = pix_fmts;
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out",
NULL, buffersink_params, filter_graph);
av_free(buffersink_params);
if (ret < 0) {
printf("Cannot create buffer sink\n");
return ret;
}
/* Endpoints for the filter graph. */
outputs->name = av_strdup("in");
outputs->filter_ctx = buffersrc_ctx;
outputs->pad_idx = 0;
outputs->next = NULL;
inputs->name = av_strdup("out");
inputs->filter_ctx = buffersink_ctx;
inputs->pad_idx = 0;
inputs->next = NULL;
if ((ret = avfilter_graph_parse_ptr(filter_graph, filters_descr,
&inputs, &outputs, NULL)) < 0)
return ret;
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0)
return ret;
return 0;
}
static int encode_write_video_frame(AVFrame *filt_frame, int *got_frame)
{
int ret;
int got_frame_local;
AVPacket enc_pkt;
unsigned int stream_index = 0;
if (!got_frame)
got_frame = &got_frame_local;
av_log(NULL, AV_LOG_INFO, "Encoding frame\n");
/* encode filtered frame */
enc_pkt.data = NULL;
enc_pkt.size = 0;
av_init_packet(&enc_pkt);
ret = avcodec_encode_video2(ofmt_ctx->streams[stream_index]->codec, &enc_pkt,
filt_frame, got_frame);
av_frame_free(&filt_frame);
if (ret < 0)
return ret;
if (!(*got_frame))
return 0;
/* prepare packet for muxing */
enc_pkt.stream_index = stream_index;
enc_pkt.dts = av_rescale_q_rnd(enc_pkt.dts,
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
enc_pkt.pts = av_rescale_q_rnd(enc_pkt.pts,
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
enc_pkt.duration = av_rescale_q(enc_pkt.duration,
ofmt_ctx->streams[stream_index]->codec->time_base,
ofmt_ctx->streams[stream_index]->time_base);
av_log(NULL, AV_LOG_DEBUG, "Muxing frame\n");
/* mux encoded frame */
ret = av_interleaved_write_frame(ofmt_ctx, &enc_pkt);
return ret;
}
static int filter_encode_write_video_frame(AVFrame *frame)
{
int ret;
AVFrame *filt_frame;
av_log(NULL, AV_LOG_INFO, "Pushing decoded frame to filters\n");
/* push the decoded frame into the filtergraph */
ret = av_buffersrc_add_frame_flags(buffersrc_ctx,
frame, 0);
if (ret < 0) {
av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n");
return ret;
}
/* pull filtered frames from the filtergraph */
while (1) {
filt_frame = av_frame_alloc();
if (!filt_frame) {
ret = AVERROR(ENOMEM);
break;
}
av_log(NULL, AV_LOG_INFO, "Pulling filtered frame from filters\n");
ret = av_buffersink_get_frame(buffersink_ctx,
filt_frame);
if (ret < 0) {
/* if no more frames for output - returns AVERROR(EAGAIN)
* if flushed and no more frames for output - returns AVERROR_EOF
* rewrite retcode to 0 to show it as normal procedure completion
*/
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
ret = 0;
av_frame_free(&filt_frame);
break;
}
filt_frame->pict_type = AV_PICTURE_TYPE_NONE;
ret = encode_write_video_frame(filt_frame, NULL);
if (ret < 0)
break;
}
return ret;
}
int flush_encoder(AVFormatContext *fmt_ctx,unsigned int stream_index)
{
int ret;
int got_frame;
if (!(ofmt_ctx->streams[stream_index]->codec->codec->capabilities &
CODEC_CAP_DELAY))
return 0;
while (1) {
av_log(NULL, AV_LOG_INFO, "Flushing stream #%u encoder\n", stream_index);
ret = encode_write_video_frame(NULL, &got_frame);
if (ret < 0)
break;
if (!got_frame)
return 0;
}
return ret;
}
int _tmain(int argc, _TCHAR* argv[])
{
char filter_descr[100]/* = "movie=my_logo.png[wm];[in][wm]overlay=5:5[out]"*/;
AVPacket pkt_in, pkt_out;
unsigned int stream_index;
int ret;
AVPacket packet;
AVFrame *frame;
int got_frame;
int width, height;
width = 400;
height = 300;
sprintf(filter_descr, "[in]scale=%d:%d[out]", width, height);
avcodec_register_all();
av_register_all();
avfilter_register_all();
if ((ret = open_input_file("test.mp4")) < 0)
goto end;
if ((ret = init_filters(filter_descr)) < 0)
goto end;
if ((ret = openoutputfile("test_scale.mp4", width, height)) < 0)
goto end;
// to be add
while(1)
{
if (av_read_frame(ifmt_ctx, &pkt_in) < 0)
{
break;
}
pkt_out.data = NULL;
pkt_out.size = 0;
av_init_packet(&pkt_out);
stream_index = pkt_in.stream_index;
frame = av_frame_alloc();
int got_frame = -1;
int ret = -1;
//calculate the pts and dts
pkt_in.dts = av_rescale_q_rnd(pkt_in.dts,
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
pkt_in.pts = av_rescale_q_rnd(pkt_in.pts,
ifmt_ctx->streams[stream_index]->time_base,
ifmt_ctx->streams[stream_index]->codec->time_base,
(AVRounding)(AV_ROUND_NEAR_INF|AV_ROUND_PASS_MINMAX));
if (ifmt_ctx->streams[stream_index]->codec->codec_type == AVMEDIA_TYPE_VIDEO)
{
ret = avcodec_decode_video2(ifmt_ctx->streams[stream_index]->codec, frame, &got_frame, &pkt_in);
if (ret < 0)
{
av_frame_free(&frame);
printf("decoding video stream failed\n");
break;
}
if (got_frame)
{
frame->pts = av_frame_get_best_effort_timestamp(frame);
ret = filter_encode_write_video_frame(frame);
av_frame_free(&frame);
if (ret < 0)
goto end;
}
}
}
//Flush Encoder
ret = flush_encoder(ofmt_ctx,0);
if (ret < 0) {
printf("Flushing encoder failed\n");
return -1;
}
av_write_trailer(ofmt_ctx);
end:
avfilter_graph_free(&filter_graph);
if (pCodecCtx)
avcodec_close(pCodecCtx);
avformat_close_input(&ifmt_ctx);
if (ofmt_ctx && !(ofmt_ctx->oformat->flags & AVFMT_NOFILE))
{
for (int i = 0; i < ofmt_ctx->nb_streams; i++)
{
if (ofmt_ctx->streams[i]->codec)
{
avcodec_close(ofmt_ctx->streams[i]->codec);
}
}
avio_close(ofmt_ctx->pb);
}
avformat_free_context(ofmt_ctx);
if (ret < 0 && ret != AVERROR_EOF) {
char buf[1024];
av_strerror(ret, buf, sizeof(buf));
printf("Error occurred: %s\n", buf);
return -1;
}
return 0;
}