1 /*****************************************************************************
2  * video_output.c
3  *****************************************************************************
4  * Copyright (C) 2013-2015 L-SMASH Works project
5  *
6  * Authors: Yusuke Nakamura <muken.the.vfrmaniac@gmail.com>
7  *
8  * Permission to use, copy, modify, and/or distribute this software for any
9  * purpose with or without fee is hereby granted, provided that the above
10  * copyright notice and this permission notice appear in all copies.
11  *
12  * THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
13  * WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
14  * MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
15  * ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
16  * WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
17  * ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
18  * OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
19  *****************************************************************************/
20 
21 /* This file is available under an ISC license. */
22 
23 #include <string.h>
24 
25 /* Libav */
26 #include <libavcodec/avcodec.h>         /* Decoder */
27 #include <libswscale/swscale.h>         /* Colorspace converter */
28 #include <libavutil/imgutils.h>
29 #include <libavutil/mem.h>
30 
31 #include "lsmashsource.h"
32 #include "video_output.h"
33 
34 typedef struct
35 {
36     uint8_t *data    [4];
37     int      linesize[4];
38 } vs_picture_t;
39 
make_black_background_planar_yuv8(VSFrameRef * vs_frame,const VSAPI * vsapi)40 static void make_black_background_planar_yuv8
41 (
42     VSFrameRef  *vs_frame,
43     const VSAPI *vsapi
44 )
45 {
46     for( int i = 0; i < 3; i++ )
47         memset( vsapi->getWritePtr( vs_frame, i ), i ? 0x80 : 0x00, vsapi->getStride( vs_frame, i ) * vsapi->getFrameHeight( vs_frame, i ) );
48 }
49 
make_black_background_planar_yuv16(VSFrameRef * vs_frame,const VSAPI * vsapi)50 static void make_black_background_planar_yuv16
51 (
52     VSFrameRef  *vs_frame,
53     const VSAPI *vsapi
54 )
55 {
56     int shift = vsapi->getFrameFormat( vs_frame )->bitsPerSample - 8;
57     for( int i = 0; i < 3; i++ )
58     {
59         int v = i ? 0x00000080 << shift : 0x00000000;
60         uint8_t *data = vsapi->getWritePtr( vs_frame, i );
61         uint8_t *end  = data + vsapi->getStride( vs_frame, i ) * vsapi->getFrameHeight( vs_frame, i );
62         while( data < end )
63         {
64             /* Assume little endianess. */
65             data[0] = v;
66             data[1] = v >> 8;
67             data += 2;
68         }
69     }
70 }
71 
make_black_background_planar_rgb(VSFrameRef * vs_frame,const VSAPI * vsapi)72 static void make_black_background_planar_rgb
73 (
74     VSFrameRef  *vs_frame,
75     const VSAPI *vsapi
76 )
77 {
78     for( int i = 0; i < 3; i++ )
79         memset( vsapi->getWritePtr( vs_frame, i ), 0x00, vsapi->getStride( vs_frame, i ) * vsapi->getFrameHeight( vs_frame, i ) );
80 }
81 
make_frame_planar_yuv(lw_video_scaler_handler_t * vshp,AVFrame * av_picture,const component_reorder_t * component_reorder,VSFrameRef * vs_frame,VSFrameContext * frame_ctx,const VSAPI * vsapi)82 static void make_frame_planar_yuv
83 (
84     lw_video_scaler_handler_t *vshp,
85     AVFrame                   *av_picture,
86     const component_reorder_t *component_reorder,
87     VSFrameRef                *vs_frame,
88     VSFrameContext            *frame_ctx,
89     const VSAPI               *vsapi
90 )
91 {
92     vs_picture_t vs_picture =
93     {
94         /* data */
95         {
96             vsapi->getWritePtr( vs_frame, 0 ),
97             vsapi->getWritePtr( vs_frame, 1 ),
98             vsapi->getWritePtr( vs_frame, 2 ),
99             NULL
100         },
101         /* linesize */
102         {
103             vsapi->getStride( vs_frame, 0 ),
104             vsapi->getStride( vs_frame, 1 ),
105             vsapi->getStride( vs_frame, 2 ),
106             0
107         }
108     };
109     sws_scale( vshp->sws_ctx, (const uint8_t* const*)av_picture->data, av_picture->linesize, 0, av_picture->height, vs_picture.data, vs_picture.linesize );
110 }
111 
make_frame_planar_rgb(lw_video_scaler_handler_t * vshp,AVFrame * av_picture,const component_reorder_t * component_reorder,VSFrameRef * vs_frame,VSFrameContext * frame_ctx,const VSAPI * vsapi)112 static void make_frame_planar_rgb
113 (
114     lw_video_scaler_handler_t *vshp,
115     AVFrame                   *av_picture,
116     const component_reorder_t *component_reorder,
117     VSFrameRef                *vs_frame,
118     VSFrameContext            *frame_ctx,
119     const VSAPI               *vsapi
120 )
121 {
122     vs_picture_t vs_picture =
123     {
124         /* data */
125         {
126             vsapi->getWritePtr( vs_frame, component_reorder[0] ),
127             vsapi->getWritePtr( vs_frame, component_reorder[1] ),
128             vsapi->getWritePtr( vs_frame, component_reorder[2] ),
129             NULL
130         },
131         /* linesize */
132         {
133             vsapi->getStride( vs_frame, component_reorder[0] ),
134             vsapi->getStride( vs_frame, component_reorder[1] ),
135             vsapi->getStride( vs_frame, component_reorder[2] ),
136             0
137         }
138 
139     };
140     sws_scale( vshp->sws_ctx, (const uint8_t* const*)av_picture->data, av_picture->linesize, 0, av_picture->height, vs_picture.data, vs_picture.linesize );
141 }
142 
make_frame_planar_rgb8(lw_video_scaler_handler_t * vshp,AVFrame * av_picture,const component_reorder_t * component_reorder,VSFrameRef * vs_frame,VSFrameContext * frame_ctx,const VSAPI * vsapi)143 static void make_frame_planar_rgb8
144 (
145     lw_video_scaler_handler_t *vshp,
146     AVFrame                   *av_picture,
147     const component_reorder_t *component_reorder,
148     VSFrameRef                *vs_frame,
149     VSFrameContext            *frame_ctx,
150     const VSAPI               *vsapi
151 )
152 {
153     uint8_t *vs_frame_data[3] =
154         {
155             vsapi->getWritePtr( vs_frame, 0 ),
156             vsapi->getWritePtr( vs_frame, 1 ),
157             vsapi->getWritePtr( vs_frame, 2 )
158         };
159     const VSFormat *vs_format = vsapi->getFrameFormat( vs_frame );
160     int av_num_components = vs_format->numPlanes + (component_reorder[3] == -1 ? 0 : 1);
161     int vs_frame_linesize = vsapi->getStride( vs_frame, 0 );
162     int vs_pixel_offset   = 0;
163     int av_pixel_offset   = 0;
164     for( int i = 0; i < av_picture->height; i++ )
165     {
166         uint8_t *av_pixel   = av_picture->data[0] + av_pixel_offset;
167         uint8_t *av_pixel_r = av_pixel + component_reorder[0];
168         uint8_t *av_pixel_g = av_pixel + component_reorder[1];
169         uint8_t *av_pixel_b = av_pixel + component_reorder[2];
170         uint8_t *vs_pixel_r = vs_frame_data[0] + vs_pixel_offset;
171         uint8_t *vs_pixel_g = vs_frame_data[1] + vs_pixel_offset;
172         uint8_t *vs_pixel_b = vs_frame_data[2] + vs_pixel_offset;
173         for( int j = 0; j < av_picture->width; j++ )
174         {
175             *(vs_pixel_r++) = *av_pixel_r;
176             *(vs_pixel_g++) = *av_pixel_g;
177             *(vs_pixel_b++) = *av_pixel_b;
178             av_pixel_r += av_num_components;
179             av_pixel_g += av_num_components;
180             av_pixel_b += av_num_components;
181         }
182         av_pixel_offset += av_picture->linesize[0];
183         vs_pixel_offset += vs_frame_linesize;
184     }
185 }
186 
make_frame_planar_rgb16(lw_video_scaler_handler_t * vshp,AVFrame * av_picture,const component_reorder_t * component_reorder,VSFrameRef * vs_frame,VSFrameContext * frame_ctx,const VSAPI * vsapi)187 static void make_frame_planar_rgb16
188 (
189     lw_video_scaler_handler_t *vshp,
190     AVFrame                   *av_picture,
191     const component_reorder_t *component_reorder,
192     VSFrameRef                *vs_frame,
193     VSFrameContext            *frame_ctx,
194     const VSAPI               *vsapi
195 )
196 {
197     uint8_t *vs_frame_data[3] =
198         {
199             vsapi->getWritePtr( vs_frame, 0 ),
200             vsapi->getWritePtr( vs_frame, 1 ),
201             vsapi->getWritePtr( vs_frame, 2 )
202         };
203     const VSFormat *vs_format = vsapi->getFrameFormat( vs_frame );
204     int av_num_components = vs_format->numPlanes + (component_reorder[3] == -1 ? 0 : 1);
205     int vs_frame_linesize = vsapi->getStride( vs_frame, 0 );
206     int vs_pixel_offset   = 0;
207     int av_pixel_offset   = 0;
208     for( int i = 0; i < av_picture->height; i++ )
209     {
210         uint16_t *av_pixel   = (uint16_t *)(av_picture->data[0] + av_pixel_offset);
211         uint16_t *av_pixel_r = av_pixel + component_reorder[0];
212         uint16_t *av_pixel_g = av_pixel + component_reorder[1];
213         uint16_t *av_pixel_b = av_pixel + component_reorder[2];
214         uint16_t *vs_pixel_r = (uint16_t *)(vs_frame_data[0] + vs_pixel_offset);
215         uint16_t *vs_pixel_g = (uint16_t *)(vs_frame_data[1] + vs_pixel_offset);
216         uint16_t *vs_pixel_b = (uint16_t *)(vs_frame_data[2] + vs_pixel_offset);
217         for( int j = 0; j < av_picture->width; j++ )
218         {
219             *(vs_pixel_r++) = *av_pixel_r;
220             *(vs_pixel_g++) = *av_pixel_g;
221             *(vs_pixel_b++) = *av_pixel_b;
222             av_pixel_r += av_num_components;
223             av_pixel_g += av_num_components;
224             av_pixel_b += av_num_components;
225         }
226         av_pixel_offset += av_picture->linesize[0];
227         vs_pixel_offset += vs_frame_linesize;
228     }
229 }
230 
get_vs_output_pixel_format(const char * format_name)231 VSPresetFormat get_vs_output_pixel_format( const char *format_name )
232 {
233     if( !format_name )
234         return pfNone;
235     static const struct
236     {
237         const char     *format_name;
238         VSPresetFormat  vs_output_pixel_format;
239     } format_table[] =
240         {
241             { "YUV420P8",  pfYUV420P8  },
242             { "YUV422P8",  pfYUV422P8  },
243             { "YUV444P8",  pfYUV444P8  },
244             { "YUV410P8",  pfYUV410P8  },
245             { "YUV411P8",  pfYUV411P8  },
246             { "YUV440P8",  pfYUV440P8  },
247             { "YUV420P9",  pfYUV420P9  },
248             { "YUV422P9",  pfYUV422P9  },
249             { "YUV444P9",  pfYUV444P9  },
250             { "YUV420P10", pfYUV420P10 },
251             { "YUV422P10", pfYUV422P10 },
252             { "YUV444P10", pfYUV444P10 },
253             { "YUV420P16", pfYUV420P16 },
254             { "YUV422P16", pfYUV422P16 },
255             { "YUV444P16", pfYUV444P16 },
256             { "RGB24",     pfRGB24     },
257             { "RGB27",     pfRGB27     },
258             { "RGB30",     pfRGB30     },
259             { "RGB48",     pfRGB48     },
260             { NULL,        pfNone      }
261         };
262     for( int i = 0; format_table[i].format_name; i++ )
263         if( strcasecmp( format_name, format_table[i].format_name ) == 0 )
264             return format_table[i].vs_output_pixel_format;
265     return pfNone;
266 }
267 
vs_to_av_output_pixel_format(VSPresetFormat vs_output_pixel_format)268 static enum AVPixelFormat vs_to_av_output_pixel_format( VSPresetFormat vs_output_pixel_format )
269 {
270     static const struct
271     {
272         VSPresetFormat     vs_output_pixel_format;
273         enum AVPixelFormat av_output_pixel_format;
274     } format_table[] =
275         {
276             { pfYUV420P8,  AV_PIX_FMT_YUV420P     },
277             { pfYUV422P8,  AV_PIX_FMT_YUV422P     },
278             { pfYUV444P8,  AV_PIX_FMT_YUV444P     },
279             { pfYUV410P8,  AV_PIX_FMT_YUV410P     },
280             { pfYUV411P8,  AV_PIX_FMT_YUV411P     },
281             { pfYUV440P8,  AV_PIX_FMT_YUV440P     },
282             { pfYUV420P9,  AV_PIX_FMT_YUV420P9LE  },
283             { pfYUV422P9,  AV_PIX_FMT_YUV422P9LE  },
284             { pfYUV444P9,  AV_PIX_FMT_YUV444P9LE  },
285             { pfYUV420P10, AV_PIX_FMT_YUV420P10LE },
286             { pfYUV422P10, AV_PIX_FMT_YUV422P10LE },
287             { pfYUV444P10, AV_PIX_FMT_YUV444P10LE },
288             { pfYUV420P16, AV_PIX_FMT_YUV420P16LE },
289             { pfYUV422P16, AV_PIX_FMT_YUV422P16LE },
290             { pfYUV444P16, AV_PIX_FMT_YUV444P16LE },
291             { pfRGB24,     AV_PIX_FMT_GBRP        },
292             { pfRGB27,     AV_PIX_FMT_GBRP9LE     },
293             { pfRGB30,     AV_PIX_FMT_GBRP10LE    },
294             { pfRGB48,     AV_PIX_FMT_GBRP16LE    },
295             { pfNone,      AV_PIX_FMT_NONE        }
296         };
297     for( int i = 0; format_table[i].vs_output_pixel_format != pfNone; i++ )
298         if( vs_output_pixel_format == format_table[i].vs_output_pixel_format )
299             return format_table[i].av_output_pixel_format;
300     return AV_PIX_FMT_NONE;
301 }
302 
get_component_reorder(enum AVPixelFormat av_output_pixel_format)303 static const component_reorder_t *get_component_reorder( enum AVPixelFormat av_output_pixel_format )
304 {
305     static const struct
306     {
307         enum AVPixelFormat  av_output_pixel_format;
308         component_reorder_t component_reorder[4];
309     } reorder_table[] =
310         {
311             /* YUV */
312             { AV_PIX_FMT_YUV420P,     {  0,  1,  2, -1 } },
313             { AV_PIX_FMT_YUV422P,     {  0,  1,  2, -1 } },
314             { AV_PIX_FMT_YUV444P,     {  0,  1,  2, -1 } },
315             { AV_PIX_FMT_YUV410P,     {  0,  1,  2, -1 } },
316             { AV_PIX_FMT_YUV411P,     {  0,  1,  2, -1 } },
317             { AV_PIX_FMT_YUV440P,     {  0,  1,  2, -1 } },
318             { AV_PIX_FMT_YUV420P9LE,  {  0,  1,  2, -1 } },
319             { AV_PIX_FMT_YUV422P9LE,  {  0,  1,  2, -1 } },
320             { AV_PIX_FMT_YUV444P9LE,  {  0,  1,  2, -1 } },
321             { AV_PIX_FMT_YUV420P10LE, {  0,  1,  2, -1 } },
322             { AV_PIX_FMT_YUV422P10LE, {  0,  1,  2, -1 } },
323             { AV_PIX_FMT_YUV444P10LE, {  0,  1,  2, -1 } },
324             { AV_PIX_FMT_YUV420P16LE, {  0,  1,  2, -1 } },
325             { AV_PIX_FMT_YUV422P16LE, {  0,  1,  2, -1 } },
326             { AV_PIX_FMT_YUV444P16LE, {  0,  1,  2, -1 } },
327             /* RGB */
328             { AV_PIX_FMT_GBRP,        {  1,  2,  0, -1 } },
329             { AV_PIX_FMT_GBRP9LE,     {  1,  2,  0, -1 } },
330             { AV_PIX_FMT_GBRP10LE,    {  1,  2,  0, -1 } },
331             { AV_PIX_FMT_GBRP16LE,    {  1,  2,  0, -1 } },
332             { AV_PIX_FMT_RGB24,       {  0,  1,  2, -1 } },
333             { AV_PIX_FMT_ARGB,        {  1,  2,  3,  0 } },
334             { AV_PIX_FMT_RGBA,        {  0,  1,  2,  3 } },
335             { AV_PIX_FMT_ABGR,        {  3,  2,  1,  0 } },
336             { AV_PIX_FMT_BGRA,        {  2,  1,  0,  3 } },
337             { AV_PIX_FMT_BGR48LE,     {  2,  1,  0, -1 } },
338             { AV_PIX_FMT_NONE,        {  0,  1,  2,  3 } }
339         };
340     int i = 0;
341     while( reorder_table[i].av_output_pixel_format != AV_PIX_FMT_NONE )
342     {
343         if( av_output_pixel_format == reorder_table[i].av_output_pixel_format )
344             break;
345         ++i;
346     }
347     return reorder_table[i].component_reorder;
348 }
349 
set_frame_maker(vs_video_output_handler_t * vs_vohp,int av_output_is_planar_rgb)350 static inline int set_frame_maker
351 (
352     vs_video_output_handler_t *vs_vohp,
353     int                        av_output_is_planar_rgb
354 )
355 {
356     static const struct
357     {
358         VSPresetFormat              vs_output_pixel_format;
359         int                         av_output_is_planar_rgb;
360         func_make_black_background *func_make_black_background;
361         func_make_frame            *func_make_frame;
362     } frame_maker_table[] =
363         {
364             { pfYUV420P8,  0, make_black_background_planar_yuv8,  make_frame_planar_yuv   },
365             { pfYUV422P8,  0, make_black_background_planar_yuv8,  make_frame_planar_yuv   },
366             { pfYUV444P8,  0, make_black_background_planar_yuv8,  make_frame_planar_yuv   },
367             { pfYUV410P8,  0, make_black_background_planar_yuv8,  make_frame_planar_yuv   },
368             { pfYUV411P8,  0, make_black_background_planar_yuv8,  make_frame_planar_yuv   },
369             { pfYUV440P8,  0, make_black_background_planar_yuv8,  make_frame_planar_yuv   },
370             { pfYUV420P9,  0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
371             { pfYUV422P9,  0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
372             { pfYUV444P9,  0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
373             { pfYUV420P10, 0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
374             { pfYUV422P10, 0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
375             { pfYUV444P10, 0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
376             { pfYUV420P16, 0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
377             { pfYUV422P16, 0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
378             { pfYUV444P16, 0, make_black_background_planar_yuv16, make_frame_planar_yuv   },
379             { pfRGB24,     1, make_black_background_planar_rgb,   make_frame_planar_rgb   },
380             { pfRGB27,     1, make_black_background_planar_rgb,   make_frame_planar_rgb   },
381             { pfRGB30,     1, make_black_background_planar_rgb,   make_frame_planar_rgb   },
382             { pfRGB48,     1, make_black_background_planar_rgb,   make_frame_planar_rgb   },
383             { pfRGB24,     0, make_black_background_planar_rgb,   make_frame_planar_rgb8  },
384             { pfRGB48,     0, make_black_background_planar_rgb,   make_frame_planar_rgb16 },
385             { pfNone,      0, NULL,                               NULL                    }
386         };
387     for( int i = 0; frame_maker_table[i].vs_output_pixel_format != pfNone; i++ )
388         if( vs_vohp->vs_output_pixel_format == frame_maker_table[i].vs_output_pixel_format
389          && av_output_is_planar_rgb         == frame_maker_table[i].av_output_is_planar_rgb )
390         {
391             vs_vohp->make_black_background = frame_maker_table[i].func_make_black_background;
392             vs_vohp->make_frame            = frame_maker_table[i].func_make_frame;
393             return 0;
394         }
395     vs_vohp->make_black_background = NULL;
396     vs_vohp->make_frame            = NULL;
397     return -1;
398 }
399 
determine_colorspace_conversion(vs_video_output_handler_t * vs_vohp,enum AVPixelFormat input_pixel_format,enum AVPixelFormat * output_pixel_format)400 static int determine_colorspace_conversion
401 (
402     vs_video_output_handler_t *vs_vohp,
403     enum AVPixelFormat         input_pixel_format,
404     enum AVPixelFormat        *output_pixel_format
405 )
406 {
407     int fmt_conv_required = 1;
408     avoid_yuv_scale_conversion( &input_pixel_format );
409     static const struct
410     {
411         enum AVPixelFormat  av_input_pixel_format;
412         VSPresetFormat      vs_output_pixel_format;
413         int                 fmt_conv_required;
414     } conversion_table[] =
415         {
416             { AV_PIX_FMT_YUV420P,     pfYUV420P8,  0 },
417             { AV_PIX_FMT_NV12,        pfYUV420P8,  1 },
418             { AV_PIX_FMT_NV21,        pfYUV420P8,  1 },
419             { AV_PIX_FMT_YUV422P,     pfYUV422P8,  0 },
420             { AV_PIX_FMT_UYVY422,     pfYUV422P8,  1 },
421             { AV_PIX_FMT_YUYV422,     pfYUV422P8,  1 },
422             { AV_PIX_FMT_YUV444P,     pfYUV444P8,  0 },
423             { AV_PIX_FMT_YUV410P,     pfYUV410P8,  0 },
424             { AV_PIX_FMT_YUV411P,     pfYUV411P8,  0 },
425             { AV_PIX_FMT_UYYVYY411,   pfYUV411P8,  1 },
426             { AV_PIX_FMT_YUV440P,     pfYUV440P8,  0 },
427             { AV_PIX_FMT_YUV420P9LE,  pfYUV420P9,  0 },
428             { AV_PIX_FMT_YUV420P9BE,  pfYUV420P9,  1 },
429             { AV_PIX_FMT_YUV422P9LE,  pfYUV422P9,  0 },
430             { AV_PIX_FMT_YUV422P9BE,  pfYUV422P9,  1 },
431             { AV_PIX_FMT_YUV444P9LE,  pfYUV444P9,  0 },
432             { AV_PIX_FMT_YUV444P9BE,  pfYUV444P9,  1 },
433             { AV_PIX_FMT_YUV420P10LE, pfYUV420P10, 0 },
434             { AV_PIX_FMT_YUV420P10BE, pfYUV420P10, 1 },
435             { AV_PIX_FMT_YUV422P10LE, pfYUV422P10, 0 },
436             { AV_PIX_FMT_YUV422P10BE, pfYUV422P10, 1 },
437             { AV_PIX_FMT_YUV444P10LE, pfYUV444P10, 0 },
438             { AV_PIX_FMT_YUV444P10BE, pfYUV444P10, 1 },
439             { AV_PIX_FMT_YUV420P16LE, pfYUV420P16, 0 },
440             { AV_PIX_FMT_YUV420P16BE, pfYUV420P16, 1 },
441             { AV_PIX_FMT_YUV422P16LE, pfYUV422P16, 0 },
442             { AV_PIX_FMT_YUV422P16BE, pfYUV422P16, 1 },
443             { AV_PIX_FMT_YUV444P16LE, pfYUV444P16, 0 },
444             { AV_PIX_FMT_YUV444P16BE, pfYUV444P16, 1 },
445             { AV_PIX_FMT_GBRP,        pfRGB24,     0 },
446             { AV_PIX_FMT_GBRP9LE,     pfRGB48,     0 },
447             { AV_PIX_FMT_GBRP9BE,     pfRGB48,     1 },
448             { AV_PIX_FMT_GBRP10LE,    pfRGB48,     0 },
449             { AV_PIX_FMT_GBRP10BE,    pfRGB48,     1 },
450             { AV_PIX_FMT_GBRP16LE,    pfRGB48,     0 },
451             { AV_PIX_FMT_GBRP16BE,    pfRGB48,     1 },
452             { AV_PIX_FMT_BGR24,       pfRGB24,     0 },
453             { AV_PIX_FMT_RGB24,       pfRGB24,     0 },
454             { AV_PIX_FMT_ARGB,        pfRGB24,     0 },
455             { AV_PIX_FMT_RGBA,        pfRGB24,     0 },
456             { AV_PIX_FMT_ABGR,        pfRGB24,     0 },
457             { AV_PIX_FMT_BGRA,        pfRGB24,     0 },
458             { AV_PIX_FMT_BGR48LE,     pfRGB48,     0 },
459             { AV_PIX_FMT_BGR48BE,     pfRGB48,     1 },
460             { AV_PIX_FMT_NONE,        pfNone,      1 }
461         };
462     if( vs_vohp->variable_info || vs_vohp->vs_output_pixel_format == pfNone )
463     {
464         /* Determine by input pixel format. */
465         for( int i = 0; conversion_table[i].vs_output_pixel_format != pfNone; i++ )
466             if( input_pixel_format == conversion_table[i].av_input_pixel_format )
467             {
468                 vs_vohp->vs_output_pixel_format = conversion_table[i].vs_output_pixel_format;
469                 fmt_conv_required               = conversion_table[i].fmt_conv_required;
470                 break;
471             }
472     }
473     else
474     {
475         /* Determine by both input pixel format and output pixel format. */
476         for( int i = 0; conversion_table[i].vs_output_pixel_format != pfNone; i++ )
477         {
478             if( input_pixel_format              == conversion_table[i].av_input_pixel_format
479              && vs_vohp->vs_output_pixel_format == conversion_table[i].vs_output_pixel_format )
480             {
481                 fmt_conv_required = conversion_table[i].fmt_conv_required;
482                 break;
483             }
484         }
485     }
486     *output_pixel_format = fmt_conv_required
487                          ? vs_to_av_output_pixel_format( vs_vohp->vs_output_pixel_format )
488                          : input_pixel_format;
489     vs_vohp->component_reorder = get_component_reorder( *output_pixel_format );
490     int av_output_flags = av_pix_fmt_desc_get( *output_pixel_format )->flags;
491     return set_frame_maker( vs_vohp, (av_output_flags & AV_PIX_FMT_FLAG_PLANAR) && (av_output_flags & AV_PIX_FMT_FLAG_RGB) );
492 }
493 
494 typedef struct
495 {
496     VSFrameRef  *vs_frame_buffer;
497     const VSAPI *vsapi;
498 } vs_video_buffer_handler_t;
499 
new_output_video_frame(vs_video_output_handler_t * vs_vohp,const AVFrame * av_frame,enum AVPixelFormat * output_pixel_format,int input_pix_fmt_change,VSFrameContext * frame_ctx,VSCore * core,const VSAPI * vsapi)500 static VSFrameRef *new_output_video_frame
501 (
502     vs_video_output_handler_t *vs_vohp,
503     const AVFrame             *av_frame,
504     enum AVPixelFormat        *output_pixel_format,
505     int                        input_pix_fmt_change,
506     VSFrameContext            *frame_ctx,
507     VSCore                    *core,
508     const VSAPI               *vsapi
509 )
510 {
511     if( vs_vohp->variable_info )
512     {
513         if( !av_frame->opaque
514          && determine_colorspace_conversion( vs_vohp, av_frame->format, output_pixel_format ) < 0 )
515             goto fail;
516         const VSFormat *vs_format = vsapi->getFormatPreset( vs_vohp->vs_output_pixel_format, core );
517         return vsapi->newVideoFrame( vs_format, av_frame->width, av_frame->height, NULL, core );
518     }
519     else
520     {
521         if( !av_frame->opaque
522          && input_pix_fmt_change
523          && determine_colorspace_conversion( vs_vohp, av_frame->format, output_pixel_format ) < 0 )
524             goto fail;
525         return vsapi->copyFrame( vs_vohp->background_frame, core );
526     }
527 fail:
528     if( frame_ctx )
529         vsapi->setFilterError( "lsmas: failed to determine colorspace conversion.", frame_ctx );
530     return NULL;
531 }
532 
make_frame(lw_video_output_handler_t * vohp,AVFrame * av_frame)533 VSFrameRef *make_frame
534 (
535     lw_video_output_handler_t *vohp,
536     AVFrame                   *av_frame
537 )
538 {
539     vs_video_output_handler_t *vs_vohp = (vs_video_output_handler_t *)vohp->private_handler;
540     lw_video_scaler_handler_t *vshp    = &vohp->scaler;
541     VSFrameContext *frame_ctx = vs_vohp->frame_ctx;
542     VSCore         *core      = vs_vohp->core;
543     const VSAPI    *vsapi     = vs_vohp->vsapi;
544     if( av_frame->opaque )
545     {
546         /* Render from the decoder directly. */
547         vs_video_buffer_handler_t *vs_vbhp = (vs_video_buffer_handler_t *)av_frame->opaque;
548         return vs_vbhp ? (VSFrameRef *)vs_vbhp->vsapi->cloneFrameRef( vs_vbhp->vs_frame_buffer ) : NULL;
549     }
550     if( !vs_vohp->make_frame )
551         return NULL;
552     /* Make video frame.
553      * Convert pixel format if needed. We don't change the presentation resolution. */
554     VSFrameRef *vs_frame = new_output_video_frame( vs_vohp, av_frame,
555                                                   &vshp->output_pixel_format,
556                                                   !!(vshp->frame_prop_change_flags & LW_FRAME_PROP_CHANGE_FLAG_PIXEL_FORMAT),
557                                                   frame_ctx, core, vsapi );
558     if( vs_frame )
559         vs_vohp->make_frame( vshp, av_frame, vs_vohp->component_reorder, vs_frame, frame_ctx, vsapi );
560     else if( frame_ctx )
561         vsapi->setFilterError( "lsmas: failed to allocate a output video frame.", frame_ctx );
562     return vs_frame;
563 }
564 
vs_check_dr_available(AVCodecContext * ctx,enum AVPixelFormat pixel_format)565 static int vs_check_dr_available
566 (
567     AVCodecContext    *ctx,
568     enum AVPixelFormat pixel_format
569 )
570 {
571     if( !(ctx->codec->capabilities & AV_CODEC_CAP_DR1) )
572         return 0;
573     static enum AVPixelFormat dr_support_pix_fmt[] =
574         {
575             AV_PIX_FMT_YUV420P,
576             AV_PIX_FMT_YUV422P,
577             AV_PIX_FMT_YUV444P,
578             AV_PIX_FMT_YUV410P,
579             AV_PIX_FMT_YUV411P,
580             AV_PIX_FMT_YUV440P,
581             AV_PIX_FMT_YUV420P9LE,
582             AV_PIX_FMT_YUV422P9LE,
583             AV_PIX_FMT_YUV444P9LE,
584             AV_PIX_FMT_YUV420P10LE,
585             AV_PIX_FMT_YUV422P10LE,
586             AV_PIX_FMT_YUV444P10LE,
587             AV_PIX_FMT_YUV420P16LE,
588             AV_PIX_FMT_YUV422P16LE,
589             AV_PIX_FMT_YUV444P16LE,
590             AV_PIX_FMT_GBRP,
591             AV_PIX_FMT_GBRP9LE,
592             AV_PIX_FMT_GBRP10LE,
593             AV_PIX_FMT_GBRP16LE,
594             AV_PIX_FMT_NONE
595         };
596     for( int i = 0; dr_support_pix_fmt[i] != AV_PIX_FMT_NONE; i++ )
597         if( dr_support_pix_fmt[i] == pixel_format )
598             return 1;
599     return 0;
600 }
601 
vs_video_release_buffer_handler(void * opaque,uint8_t * data)602 static void vs_video_release_buffer_handler
603 (
604     void    *opaque,
605     uint8_t *data
606 )
607 {
608     vs_video_buffer_handler_t *vs_vbhp = (vs_video_buffer_handler_t *)opaque;
609     if( !vs_vbhp )
610         return;
611     if( vs_vbhp->vsapi && vs_vbhp->vsapi->freeFrame )
612         vs_vbhp->vsapi->freeFrame( vs_vbhp->vs_frame_buffer );
613     free( vs_vbhp );
614 }
615 
vs_video_unref_buffer_handler(void * opaque,uint8_t * data)616 static void vs_video_unref_buffer_handler
617 (
618     void    *opaque,
619     uint8_t *data
620 )
621 {
622     /* Decrement the reference-counter to the video buffer handler by 1.
623      * Delete it by vs_video_release_buffer_handler() if there are no reference to it i.e. the reference-counter equals zero. */
624     AVBufferRef *vs_buffer_ref = (AVBufferRef *)opaque;
625     av_buffer_unref( &vs_buffer_ref );
626 }
627 
vs_create_plane_buffer(vs_video_buffer_handler_t * vs_vbhp,AVBufferRef * vs_buffer_handler,AVFrame * av_frame,int av_plane,int vs_plane)628 static inline int vs_create_plane_buffer
629 (
630     vs_video_buffer_handler_t *vs_vbhp,
631     AVBufferRef               *vs_buffer_handler,
632     AVFrame                   *av_frame,
633     int                        av_plane,
634     int                        vs_plane
635 )
636 {
637     AVBufferRef *vs_buffer_ref = av_buffer_ref( vs_buffer_handler );
638     if( !vs_buffer_ref )
639     {
640         av_buffer_unref( &vs_buffer_handler );
641         return -1;
642     }
643     av_frame->linesize[av_plane] = vs_vbhp->vsapi->getStride( vs_vbhp->vs_frame_buffer, vs_plane );
644     int vs_plane_size = vs_vbhp->vsapi->getFrameHeight( vs_vbhp->vs_frame_buffer, vs_plane )
645                       * av_frame->linesize[av_plane];
646     av_frame->buf[av_plane] = av_buffer_create( vs_vbhp->vsapi->getWritePtr( vs_vbhp->vs_frame_buffer, vs_plane ),
647                                                 vs_plane_size,
648                                                 vs_video_unref_buffer_handler,
649                                                 vs_buffer_ref,
650                                                 0 );
651     if( !av_frame->buf[av_plane] )
652         return -1;
653     av_frame->data[av_plane] = av_frame->buf[av_plane]->data;
654     return 0;
655 }
656 
vs_video_get_buffer(AVCodecContext * ctx,AVFrame * av_frame,int flags)657 static int vs_video_get_buffer
658 (
659     AVCodecContext *ctx,
660     AVFrame        *av_frame,
661     int             flags
662 )
663 {
664     av_frame->opaque = NULL;
665     lw_video_output_handler_t *lw_vohp = (lw_video_output_handler_t *)ctx->opaque;
666     vs_video_output_handler_t *vs_vohp = (vs_video_output_handler_t *)lw_vohp->private_handler;
667     enum AVPixelFormat pix_fmt = av_frame->format;
668     avoid_yuv_scale_conversion( &pix_fmt );
669     av_frame->format = pix_fmt; /* Don't use AV_PIX_FMT_YUVJ*. */
670     if( (!vs_vohp->variable_info && lw_vohp->scaler.output_pixel_format != pix_fmt)
671      || !vs_check_dr_available( ctx, pix_fmt ) )
672         return avcodec_default_get_buffer2( ctx, av_frame, flags );
673     /* New VapourSynth video frame buffer. */
674     vs_video_buffer_handler_t *vs_vbhp = malloc( sizeof(vs_video_buffer_handler_t) );
675     if( !vs_vbhp )
676     {
677         av_frame_unref( av_frame );
678         return AVERROR( ENOMEM );
679     }
680     av_frame->opaque = vs_vbhp;
681     avcodec_align_dimensions2( ctx, &av_frame->width, &av_frame->height, av_frame->linesize );
682     VSFrameRef *vs_frame_buffer = new_output_video_frame( vs_vohp, av_frame, NULL, 0,
683                                                           vs_vohp->frame_ctx, vs_vohp->core, vs_vohp->vsapi );
684     if( !vs_frame_buffer )
685     {
686         free( vs_vbhp );
687         av_frame_unref( av_frame );
688         return AVERROR( ENOMEM );
689     }
690     vs_vbhp->vs_frame_buffer = vs_frame_buffer;
691     vs_vbhp->vsapi           = vs_vohp->vsapi;
692     /* Create frame buffers for the decoder.
693      * The callback vs_video_release_buffer_handler() shall be called when no reference to the video buffer handler is present.
694      * The callback vs_video_unref_buffer_handler() decrements the reference-counter by 1. */
695     memset( av_frame->buf,      0, sizeof(av_frame->buf) );
696     memset( av_frame->data,     0, sizeof(av_frame->data) );
697     memset( av_frame->linesize, 0, sizeof(av_frame->linesize) );
698     AVBufferRef *vs_buffer_handler = av_buffer_create( NULL, 0, vs_video_release_buffer_handler, vs_vbhp, 0 );
699     if( !vs_buffer_handler )
700     {
701         vs_video_release_buffer_handler( vs_vbhp, NULL );
702         av_frame_unref( av_frame );
703         return AVERROR( ENOMEM );
704     }
705     vs_vohp->component_reorder = get_component_reorder( pix_fmt );
706     for( int i = 0; i < 3; i++ )
707         if( vs_create_plane_buffer( vs_vbhp, vs_buffer_handler, av_frame, i, vs_vohp->component_reorder[i] ) < 0 )
708             goto fail;
709     /* Here, a variable 'vs_buffer_handler' itself is not referenced by any pointer. */
710     av_buffer_unref( &vs_buffer_handler );
711     av_frame->nb_extended_buf = 0;
712     av_frame->extended_data   = av_frame->data;
713     return 0;
714 fail:
715     av_frame_unref( av_frame );
716     av_buffer_unref( &vs_buffer_handler );
717     return AVERROR( ENOMEM );
718 }
719 
vs_setup_video_rendering(lw_video_output_handler_t * lw_vohp,AVCodecContext * ctx,VSVideoInfo * vi,VSMap * out,int width,int height)720 int vs_setup_video_rendering
721 (
722     lw_video_output_handler_t *lw_vohp,
723     AVCodecContext            *ctx,
724     VSVideoInfo               *vi,
725     VSMap                     *out,
726     int                        width,
727     int                        height
728 )
729 {
730     vs_video_output_handler_t *vs_vohp = (vs_video_output_handler_t *)lw_vohp->private_handler;
731     const VSAPI *vsapi = vs_vohp->vsapi;
732     enum AVPixelFormat output_pixel_format;
733     if( determine_colorspace_conversion( vs_vohp, ctx->pix_fmt, &output_pixel_format ) )
734     {
735         set_error_on_init( out, vsapi, "lsmas: %s is not supported", av_get_pix_fmt_name( ctx->pix_fmt ) );
736         return -1;
737     }
738     vs_vohp->direct_rendering &= vs_check_dr_available( ctx, ctx->pix_fmt );
739     int (*dr_get_buffer)( struct AVCodecContext *, AVFrame *, int ) = vs_vohp->direct_rendering ? vs_video_get_buffer : NULL;
740     setup_video_rendering( lw_vohp, SWS_FAST_BILINEAR,
741                            width, height, output_pixel_format,
742                            ctx, dr_get_buffer );
743     if( vs_vohp->variable_info )
744     {
745         vi->format = NULL;
746         vi->width  = 0;
747         vi->height = 0;
748         /* Unused */
749         //lw_vohp->output_width  = 0;
750         //lw_vohp->output_height = 0;
751     }
752     else
753     {
754         vi->format = vsapi->getFormatPreset( vs_vohp->vs_output_pixel_format, vs_vohp->core );
755         vi->width  = lw_vohp->output_width;
756         vi->height = lw_vohp->output_height;
757         vs_vohp->background_frame = vsapi->newVideoFrame( vi->format, vi->width, vi->height, NULL, vs_vohp->core );
758         if( !vs_vohp->background_frame )
759         {
760             set_error_on_init( out, vsapi, "lsmas: failed to allocate memory for the background black frame data." );
761             return -1;
762         }
763         vs_vohp->make_black_background( vs_vohp->background_frame, vsapi );
764     }
765     return 0;
766 }
767 
vs_free_video_output_handler(void * private_handler)768 static void vs_free_video_output_handler
769 (
770     void *private_handler
771 )
772 {
773     vs_video_output_handler_t *vs_vohp = (vs_video_output_handler_t *)private_handler;
774     if( !vs_vohp )
775         return;
776     if( vs_vohp->vsapi && vs_vohp->vsapi->freeFrame && vs_vohp->background_frame )
777         vs_vohp->vsapi->freeFrame( vs_vohp->background_frame );
778     lw_free( vs_vohp );
779 }
780 
vs_allocate_video_output_handler(lw_video_output_handler_t * vohp)781 vs_video_output_handler_t *vs_allocate_video_output_handler
782 (
783     lw_video_output_handler_t *vohp
784 )
785 {
786     vs_video_output_handler_t *vs_vohp = lw_malloc_zero( sizeof(vs_video_output_handler_t) );
787     if( !vs_vohp )
788         return NULL;
789     vohp->private_handler      = vs_vohp;
790     vohp->free_private_handler = vs_free_video_output_handler;
791     return vs_vohp;
792 }
793 
vs_set_frame_properties(AVFrame * av_frame,int64_t duration_num,int64_t duration_den,VSFrameRef * vs_frame,const VSAPI * vsapi)794 void vs_set_frame_properties
795 (
796     AVFrame        *av_frame,
797     int64_t         duration_num,
798     int64_t         duration_den,
799     VSFrameRef     *vs_frame,
800     const VSAPI    *vsapi
801 )
802 {
803     VSMap *props = vsapi->getFramePropsRW( vs_frame );
804     /* Sample aspect ratio */
805     vsapi->propSetInt( props, "_SARNum", av_frame->sample_aspect_ratio.num, paReplace );
806     vsapi->propSetInt( props, "_SARDen", av_frame->sample_aspect_ratio.den, paReplace );
807     /* Sample duration */
808     vsapi->propSetInt( props, "_DurationNum", duration_num, paReplace );
809     vsapi->propSetInt( props, "_DurationDen", duration_den, paReplace );
810     /* Color format
811      * The decoded color format may not match with the output. Set proper properties when
812      * no YUV->RGB conversion is there. */
813     const VSFormat *vs_format = vsapi->getFrameFormat( vs_frame );
814     if( vs_format->colorFamily != cmRGB )
815     {
816         if( av_frame->color_range != AVCOL_RANGE_UNSPECIFIED )
817             vsapi->propSetInt( props, "_ColorRange", av_frame->color_range == AVCOL_RANGE_MPEG, paReplace );
818         vsapi->propSetInt( props, "_Primaries", av_frame->color_primaries, paReplace );
819         vsapi->propSetInt( props, "_Transfer",  av_frame->color_trc,       paReplace );
820         vsapi->propSetInt( props, "_Matrix",    av_frame->colorspace,      paReplace );
821         if( av_frame->chroma_location > 0 )
822             vsapi->propSetInt( props, "_ChromaLocation", av_frame->chroma_location - 1, paReplace );
823     }
824     /* Picture type */
825     char pict_type = av_get_picture_type_char( av_frame->pict_type );
826     vsapi->propSetData( props, "_PictType", &pict_type, 1, paReplace );
827     /* BFF or TFF */
828     int field_based = 0;
829     if( av_frame->interlaced_frame )
830         field_based = av_frame->top_field_first ? 2 : 1;
831     vsapi->propSetInt( props, "_FieldBased", field_based, paReplace );
832 }
833