2 * video expand filter (alternative to pad syntax)
3 * copyright (c) 2008 Ryo Hirafuji <http://ledyba.ddo.jp/>
5 * This file is part of FFmpeg.
7 * FFmpeg is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU Lesser General Public
9 * License as published by the Free Software Foundation; either
10 * version 2.1 of the License, or (at your option) any later version.
12 * FFmpeg is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 * Lesser General Public License for more details.
17 * You should have received a copy of the GNU Lesser General Public
18 * License along with FFmpeg; if not, write to the Free Software
19 * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
31 int offset[INDEX_MAX];
34 int osd; ///< checked, but not used in this version.
38 int bpp; ///< bytes per pixel
42 static int init(AVFilterContext *ctx, const char *args, void *opaque)
44 ExpandContext *expand = ctx->priv;
47 /* default parameters */
48 for(i=0;i<INDEX_MAX;i++){
49 expand->offset[i] = -1;
54 expand->aspect = 0.0f;
61 int length = strlen(args);
62 char* osd_tmp = av_malloc(length);
63 char* aspect_tmp = av_malloc(length);
64 if(!osd_tmp || !aspect_tmp){
65 av_log(ctx, AV_LOG_ERROR, "Failed to malloc.\n");
68 sscanf(args,"%d:%d:%d:%d:%255[^:]:%255[^:]:%d",
69 &expand->size[INDEX_X],&expand->size[INDEX_Y],&expand->offset[INDEX_X],&expand->offset[INDEX_Y],
70 osd_tmp,aspect_tmp,&expand->round
73 if(osd_tmp && strlen(osd_tmp) > 0){ //checked, but not used in this version.
74 if(!strncmp(osd_tmp,"true",4)){
77 expand->osd = atoi(osd_tmp);
81 if(aspect_tmp && strlen(aspect_tmp) > 0){
82 char* cp = strchr(aspect_tmp, '/');
86 rat.num = strtol(aspect_tmp, &cpp, 10);
87 if(cpp != aspect_tmp || cpp == cp){
88 rat.den = strtol(cp+1, &cpp, 10);
92 if(rat.num && rat.den){
93 double eval = ((double)rat.num) / rat.den;
95 expand->aspect = eval;
99 double eval = strtod(aspect_tmp, 0);
101 expand->aspect = eval;
106 av_log(ctx, AV_LOG_INFO, "Expand: %dx%d , (%d,%d) , osd: %d, aspect: %lf, round: %d\n",
107 expand->size[INDEX_X], expand->size[INDEX_Y], expand->offset[INDEX_X], expand->offset[INDEX_Y], expand->osd, expand->aspect, expand->round);
116 static int query_formats(AVFilterContext *ctx){
117 avfilter_set_common_formats(ctx,avfilter_make_format_list(30, // out of 38
143 //PIX_FMT_YUYV422, // not supported.
144 //PIX_FMT_UYVY422, // not supported.
145 //PIX_FMT_UYYVYY411, // not supported.
151 //PIX_FMT_RGB4, //not supported
152 //PIX_FMT_BGR4, //not supported
153 //PIX_FMT_MONOWHITE, // not supported
154 //PIX_FMT_MONOBLACK, // not supported
155 //PIX_FMT_PAL8, // not supported
161 static int config_input(AVFilterLink *link)
163 ExpandContext *expand = link->dst->priv;
167 size[INDEX_X] = link->w;
168 size[INDEX_Y] = link->h;
170 for(i=0;i<INDEX_MAX;i++){
171 if (expand->size[i] == -1){
172 expand->size[i]=size[i];
173 } else if (expand->size[i] < -1){
174 expand->size[i]=size[i] - expand->size[i];
175 } else if (expand->size[INDEX_X] < size[i]){
176 expand->size[i]=size[i];
180 if (expand->aspect > 0.0f) {
181 if (expand->size[INDEX_Y] < (expand->size[INDEX_X] / expand->aspect)) {
182 expand->size[INDEX_Y] = (expand->size[INDEX_X] / expand->aspect) + 0.5;
184 expand->size[INDEX_X] = (expand->size[INDEX_Y] * expand->aspect) + 0.5;
188 for(i=0;i<INDEX_MAX;i++){
189 if (expand->round > 1) {
190 expand->size[i] = (1+(expand->size[i]-1)/expand->round)*expand->round;
192 if(expand->offset[i] < 0 || (expand->offset[i]+size[i]) > expand->size[i]){
193 expand->offset[i] = (expand->size[INDEX_X] - size[i])>>1;
197 avcodec_get_chroma_sub_sample(link->format, &expand->shift[INDEX_X], &expand->shift[INDEX_Y]);
198 for(i=0;i<INDEX_MAX;i++){
199 expand->offset[i] &= ~((1 << expand->shift[i]) - 1);
200 expand->size[i] &= ~((1 << expand->shift[i]) - 1);
203 switch(link->format) {
204 case PIX_FMT_YUV420P:
205 case PIX_FMT_YUV422P:
206 case PIX_FMT_YUV444P:
207 case PIX_FMT_YUV410P:
208 case PIX_FMT_YUV411P:
209 case PIX_FMT_YUV440P:
210 case PIX_FMT_YUVJ420P:
211 case PIX_FMT_YUVJ422P:
212 case PIX_FMT_YUVJ444P:
213 case PIX_FMT_YUVJ440P:
214 case PIX_FMT_YUVA420P:
220 case PIX_FMT_RGB4_BYTE:
221 case PIX_FMT_BGR4_BYTE:
231 case PIX_FMT_RGB32_1:
232 case PIX_FMT_BGR32_1:
235 case PIX_FMT_GRAY16BE:
236 case PIX_FMT_GRAY16LE:
244 //case PIX_FMT_YUYV422:
245 //case PIX_FMT_UYVY422:
246 //case PIX_FMT_UYYVYY411:
249 //case PIX_FMT_MONOWHITE:
250 //case PIX_FMT_MONOBLACK:
252 default: // invalid or not supported format
259 static int config_output(AVFilterLink *link)
261 ExpandContext *expand = link->src->priv;
263 link->w = expand->size[INDEX_X];
264 link->h = expand->size[INDEX_Y];
269 static void start_frame(AVFilterLink *link, AVFilterPicRef *picref)
271 AVFilterLink *out = link->dst->outputs[0];
272 out->outpic = avfilter_get_video_buffer(out, AV_PERM_WRITE);
273 out->outpic->pts = picref->pts;
274 avfilter_start_frame(out, avfilter_ref_pic(out->outpic, ~0));
277 static void draw_slice(AVFilterLink *link, int y, int h)
279 ExpandContext *expand = link->dst->priv;
280 AVFilterPicRef *outpic = link->dst->outputs[0]->outpic;
281 AVFilterPicRef *inpic = link->cur_pic;
283 int is_first = (y <= 0);
284 int is_end = (y+h >= inpic->h);
287 if(outpic->data[i]) {
289 char* out_buff = outpic->data[i];
290 const char* in_buff = inpic->data[i];
297 if(!expand->is_yuv || i == 3){ // not YUV, or alpha channel of YUVA
299 x_shift = y_shift = 0;
301 padcolor = (i == 0) ? 16 : 128;
302 x_shift = (i == 0) ? 0 : expand->shift[INDEX_X];
303 y_shift = (i == 0) ? 0 : expand->shift[INDEX_Y];
306 copy_length = (inpic->w >> x_shift) * expand->bpp;
310 int size = (expand->offset[INDEX_Y] >> y_shift) * outpic->linesize[i];
311 memset(out_buff,padcolor,size);
314 int y_skip = expand->offset[INDEX_Y] >> y_shift;
315 out_buff += outpic->linesize[i] * y_skip;
316 in_buff += inpic->linesize[i] * y_skip;
319 for(j=0;j<h;j+=y_add){
320 int size,total_size = 0;
321 size = (expand->offset[INDEX_X] >> x_shift) * expand->bpp;
322 memset(out_buff,padcolor,size);
326 memcpy(out_buff,in_buff,copy_length);
327 out_buff += copy_length;
328 total_size += copy_length;
330 size = outpic->linesize[i]-total_size;
331 memset(out_buff,padcolor,size);
334 in_buff += inpic->linesize[i];
338 memset(out_buff,padcolor,((outpic->h-expand->offset[INDEX_Y]-inpic->h) >> y_shift) * outpic->linesize[i]);
343 if(is_first && is_end){
344 avfilter_draw_slice(link->dst->outputs[0], 0, outpic->h);
346 avfilter_draw_slice(link->dst->outputs[0], 0, expand->offset[INDEX_Y] + h);
348 avfilter_draw_slice(link->dst->outputs[0], expand->offset[INDEX_Y] + y, outpic->h - expand->offset[INDEX_Y] - y);
350 avfilter_draw_slice(link->dst->outputs[0], expand->offset[INDEX_Y] + y, h);
354 AVFilter avfilter_vf_expand = {
356 .priv_size = sizeof(ExpandContext),
359 .query_formats = query_formats,
361 .inputs = (AVFilterPad[]) {{ .name = "default",
362 .type = CODEC_TYPE_VIDEO,
363 .start_frame = start_frame,
364 .draw_slice = draw_slice,
365 .config_props = config_input, },
367 .outputs = (AVFilterPad[]) {{ .name = "default",
368 .type = CODEC_TYPE_VIDEO,
369 .config_props = config_output, },