Created
February 27, 2019 09:10
-
-
Save wnpllrzodiac/5d583ba376e7a4f4d7ab6139837d9eee to your computer and use it in GitHub Desktop.
ffmpeg filter(with SDL) sample code
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (c) 2010 Nicolas George | |
* Copyright (c) 2011 Stefano Sabatini | |
* | |
* Permission is hereby granted, free of charge, to any person obtaining a copy | |
* of this software and associated documentation files (the "Software"), to deal | |
* in the Software without restriction, including without limitation the rights | |
* to use, copy, modify, merge, publish, distribute, sublicense, and/or sell | |
* copies of the Software, and to permit persons to whom the Software is | |
* furnished to do so, subject to the following conditions: | |
* | |
* The above copyright notice and this permission notice shall be included in | |
* all copies or substantial portions of the Software. | |
* | |
* THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR | |
* IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, | |
* FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL | |
* THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER | |
* LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, | |
* OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN | |
* THE SOFTWARE. | |
*/ | |
/** | |
* @file | |
* API example for decoding and filtering | |
* @example filtering_video.c | |
*/ | |
#include <stdio.h> | |
#include <stdlib.h> | |
#include <iostream> | |
#define snprintf _snprintf | |
#define _XOPEN_SOURCE 600 /* for usleep */ | |
#define WIN32_LEAN_AND_MEAN | |
#include <windows.h> | |
#ifdef __cplusplus | |
extern "C" | |
{ | |
#endif | |
#include "libavcodec/avcodec.h" | |
#include "libavformat/avformat.h" | |
#include "libavfilter/buffersink.h" | |
#include "libavfilter/buffersrc.h" | |
#include "libavutil/opt.h" | |
#include "SDL/SDL.h" | |
#ifdef __cplusplus | |
}; | |
#endif | |
//ANSI转UTF8 | |
static void ANSItoUTF8(char * strAnsi); | |
//Enable SDL? | |
#define ENABLE_SDL 1 | |
//const char *filter_descr = "drawbox=x=100:y=100:w=100:h=100:[email protected]"; | |
//中文字幕测试 | |
char filter_descr[256] = {0}; | |
char *filter_fmt = "drawtext=fontfile=msyh.ttf:fontsize=32.0:fontcolor=Red:x=50:y=50:text='%s'"; | |
static AVFormatContext *fmt_ctx; | |
static AVCodecContext *dec_ctx; | |
AVFilterContext *buffersink_ctx; | |
AVFilterContext *buffersrc_ctx; | |
AVFilterGraph *filter_graph; | |
static int video_stream_index = -1; | |
static int64_t last_pts = AV_NOPTS_VALUE; | |
//ANSI转UTF8 | |
char * ANSItoUTF8(const char * strAnsi) | |
{ | |
//获取转换为宽字节后需要的缓冲区大小,创建宽字节缓冲区,936为简体中文GB2312代码页 | |
UINT nLen = MultiByteToWideChar(936,NULL,strAnsi,-1,NULL,NULL); | |
WCHAR *wszBuffer = new WCHAR[nLen+1]; | |
nLen = MultiByteToWideChar(936,NULL,strAnsi,-1,wszBuffer,nLen); | |
wszBuffer[nLen] = 0; | |
//获取转为UTF8多字节后需要的缓冲区大小,创建多字节缓冲区 | |
nLen = WideCharToMultiByte(CP_UTF8,NULL,wszBuffer,-1,NULL,NULL,NULL,NULL); | |
CHAR *szBuffer = new CHAR[nLen+1]; | |
nLen = WideCharToMultiByte(CP_UTF8,NULL,wszBuffer,-1,szBuffer,nLen,NULL,NULL); | |
szBuffer[nLen] = 0; | |
return szBuffer; | |
//内存清理 | |
//delete []wszBuffer; | |
//delete []szBuffer; | |
} | |
static void ff_log_callback(void* avcl, int level, const char* fmt, va_list vl) | |
{ | |
AVClass* avc = avcl ? *(AVClass**)avcl : NULL; | |
const char * class_name = ((avc != NULL) ? avc->class_name : "N/A"); | |
static char msg[1024] = {0}; | |
vsnprintf(msg, sizeof(msg), fmt, vl); | |
static char log[4096] = {0}; | |
#ifdef _MSC_VER | |
_snprintf(log, 4096, "ffmpeg[%d][%s] %s", level, class_name, msg); | |
#else | |
snprintf(log, 4096, "ffmpeg[%d][%s] %s", level, class_name, msg); | |
#endif | |
switch(level) { | |
case AV_LOG_PANIC: | |
case AV_LOG_FATAL: | |
case AV_LOG_ERROR: | |
printf("%s\n", log); | |
break; | |
case AV_LOG_WARNING: | |
printf("%s\n", log); | |
break; | |
case AV_LOG_INFO: | |
printf("%s\n", log); | |
break; | |
case AV_LOG_DEBUG: | |
printf("%s\n", log); | |
break; | |
case AV_LOG_VERBOSE: | |
//printf("%s", log); | |
break; | |
case AV_LOG_MAX_OFFSET: | |
break; | |
default: | |
//printf("%s", log); | |
break; | |
} | |
} | |
static int open_input_file(const char *filename) | |
{ | |
int ret; | |
AVCodec *dec; | |
if ((ret = avformat_open_input(&fmt_ctx, filename, NULL, NULL)) < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot open input file\n"); | |
return ret; | |
} | |
if ((ret = avformat_find_stream_info(fmt_ctx, NULL)) < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot find stream information\n"); | |
return ret; | |
} | |
/* select the video stream */ | |
ret = av_find_best_stream(fmt_ctx, AVMEDIA_TYPE_VIDEO, -1, -1, &dec, 0); | |
if (ret < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot find a video stream in the input file\n"); | |
return ret; | |
} | |
video_stream_index = ret; | |
/* create decoding context */ | |
dec_ctx = avcodec_alloc_context3(dec); | |
if (!dec_ctx) | |
return AVERROR(ENOMEM); | |
avcodec_parameters_to_context(dec_ctx, fmt_ctx->streams[video_stream_index]->codecpar); | |
av_opt_set_int(dec_ctx, "refcounted_frames", 1, 0); | |
/* init the video decoder */ | |
if ((ret = avcodec_open2(dec_ctx, dec, NULL)) < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot open video decoder\n"); | |
return ret; | |
} | |
return 0; | |
} | |
static int init_filters(const char *filters_descr) | |
{ | |
char args[512]; | |
int ret = 0; | |
const AVFilter *buffersrc = avfilter_get_by_name("buffer"); | |
const AVFilter *buffersink = avfilter_get_by_name("buffersink"); | |
AVFilterInOut *outputs = avfilter_inout_alloc(); | |
AVFilterInOut *inputs = avfilter_inout_alloc(); | |
AVRational time_base = fmt_ctx->streams[video_stream_index]->time_base; | |
enum AVPixelFormat pix_fmts[] = { AV_PIX_FMT_YUV420P, AV_PIX_FMT_NONE }; | |
AVBufferSinkParams *buffersink_params; | |
filter_graph = avfilter_graph_alloc(); | |
if (!outputs || !inputs || !filter_graph) { | |
ret = AVERROR(ENOMEM); | |
goto end; | |
} | |
/* 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", | |
dec_ctx->width, dec_ctx->height, dec_ctx->pix_fmt, | |
time_base.num, time_base.den, | |
dec_ctx->sample_aspect_ratio.num, dec_ctx->sample_aspect_ratio.den); | |
ret = avfilter_graph_create_filter(&buffersrc_ctx, buffersrc, "in", | |
args, NULL, filter_graph); | |
if (ret < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer source\n"); | |
goto end; | |
} | |
/* buffer video sink: to terminate the filter chain. */ | |
ret = avfilter_graph_create_filter(&buffersink_ctx, buffersink, "out", | |
NULL, NULL, filter_graph); | |
if (ret < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot create buffer sink\n"); | |
goto end; | |
} | |
ret = av_opt_set_int_list(buffersink_ctx, "pix_fmts", pix_fmts, | |
AV_PIX_FMT_NONE, AV_OPT_SEARCH_CHILDREN); | |
if (ret < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Cannot set output pixel format\n"); | |
goto end; | |
} | |
/* | |
* Set the endpoints for the filter graph. The filter_graph will | |
* be linked to the graph described by filters_descr. | |
*/ | |
/* | |
* The buffer source output must be connected to the input pad of | |
* the first filter described by filters_descr; since the first | |
* filter input label is not specified, it is set to "in" by | |
* default. | |
*/ | |
outputs->name = av_strdup("in"); | |
outputs->filter_ctx = buffersrc_ctx; | |
outputs->pad_idx = 0; | |
outputs->next = NULL; | |
/* | |
* The buffer sink input must be connected to the output pad of | |
* the last filter described by filters_descr; since the last | |
* filter output label is not specified, it is set to "out" by | |
* default. | |
*/ | |
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) | |
goto end; | |
if ((ret = avfilter_graph_config(filter_graph, NULL)) < 0) | |
goto end; | |
#if OSD_TEST | |
for (int i = 0; i < filter_graph->nb_filters; i++) | |
{ | |
AVFilterContext* filter_ctxn = filter_graph->filters[i]; | |
std::cout << filter_ctxn->name << std::endl; | |
} | |
#endif | |
end: | |
/*avfilter_inout_free(&inputs); | |
avfilter_inout_free(&outputs);*/ | |
return ret; | |
} | |
int main(int argc, char* argv[]) | |
{ | |
av_register_all(); | |
avfilter_register_all(); | |
avformat_network_init(); | |
av_log_set_callback(ff_log_callback); | |
//av_log_set_level(AV_LOG_INFO); | |
int ret; | |
AVPacket packet; | |
AVFrame *frame = av_frame_alloc(); | |
AVFrame *filt_frame = av_frame_alloc(); | |
#if ENABLE_SDL | |
SDL_Surface *screen; | |
SDL_Overlay *bmp; | |
SDL_Rect rect; | |
int y_size; | |
#endif | |
if (!frame || !filt_frame) { | |
perror("Could not allocate frame"); | |
exit(1); | |
} | |
char *utf8_str = ANSItoUTF8("123abc中国rtr-/35AW"); | |
sprintf(filter_descr, filter_fmt, utf8_str); | |
delete utf8_str; | |
utf8_str = NULL; | |
if ((ret = open_input_file("cuc_ieschool.flv") )< 0) | |
goto end; | |
if ((ret = init_filters(filter_descr)) < 0) | |
goto end; | |
#if ENABLE_SDL | |
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) { | |
printf( "Could not initialize SDL - %s\n", SDL_GetError()); | |
return -1; | |
} | |
screen = SDL_SetVideoMode(dec_ctx->width, dec_ctx->height, 0, 0); | |
if(!screen) { | |
printf("SDL: could not set video mode - exiting\n"); | |
return -1; | |
} | |
bmp = SDL_CreateYUVOverlay(dec_ctx->width, dec_ctx->height,SDL_YV12_OVERLAY, screen); | |
SDL_WM_SetCaption("Simplest FFmpeg Video Filter",NULL); | |
#endif | |
/* read all packets */ | |
while (1) { | |
if ((ret = av_read_frame(fmt_ctx, &packet)) < 0) | |
break; | |
if (packet.stream_index == video_stream_index) { | |
ret = avcodec_send_packet(dec_ctx, &packet); | |
if (ret < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Error while sending a packet to the decoder\n"); | |
break; | |
} | |
while (ret >= 0) { | |
ret = avcodec_receive_frame(dec_ctx, frame); | |
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) { | |
break; | |
} else if (ret < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Error while receiving a frame from the decoder\n"); | |
goto end; | |
} | |
if (ret >= 0) { | |
frame->pts = frame->best_effort_timestamp; | |
/* push the decoded frame into the filtergraph */ | |
if (av_buffersrc_add_frame_flags(buffersrc_ctx, frame, AV_BUFFERSRC_FLAG_KEEP_REF) < 0) { | |
av_log(NULL, AV_LOG_ERROR, "Error while feeding the filtergraph\n"); | |
break; | |
} | |
/* pull filtered frames from the filtergraph */ | |
while (1) { | |
ret = av_buffersink_get_frame(buffersink_ctx, filt_frame); | |
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF) | |
break; | |
if (ret < 0) | |
goto end; | |
#if ENABLE_SDL | |
SDL_LockYUVOverlay(bmp); | |
y_size=filt_frame->width*filt_frame->height; | |
memcpy(bmp->pixels[0],filt_frame->data[0],y_size); //Y | |
memcpy(bmp->pixels[2],filt_frame->data[1],y_size/4); //U | |
memcpy(bmp->pixels[1],filt_frame->data[2],y_size/4); //V | |
bmp->pitches[0]=filt_frame->linesize[0]; | |
bmp->pitches[2]=filt_frame->linesize[1]; | |
bmp->pitches[1]=filt_frame->linesize[2]; | |
SDL_UnlockYUVOverlay(bmp); | |
rect.x = 0; | |
rect.y = 0; | |
rect.w = filt_frame->width; | |
rect.h = filt_frame->height; | |
SDL_DisplayYUVOverlay(bmp, &rect); | |
//Delay 40ms | |
SDL_Delay(40); | |
#endif | |
av_frame_unref(filt_frame); | |
} | |
av_frame_unref(frame); | |
} | |
} | |
} | |
av_packet_unref(&packet); | |
} | |
end: | |
avfilter_graph_free(&filter_graph); | |
avcodec_free_context(&dec_ctx); | |
avformat_close_input(&fmt_ctx); | |
av_frame_free(&frame); | |
av_frame_free(&filt_frame); | |
if (ret < 0 && ret != AVERROR_EOF) | |
{ | |
//fprintf(stderr, "Error occurred: %s\n", av_err2str(ret)); | |
return -1; | |
} | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment