1 /*****************************************************************************
2  * video_output.c / video_output.cpp
3  *****************************************************************************
4  * Copyright (C) 2012-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 "cpp_compat.h"
24 
25 #ifdef __cplusplus
26 extern "C"
27 {
28 #endif  /* __cplusplus */
29 #include <libavutil/opt.h>
30 #include <libavcodec/avcodec.h>
31 #include <libswscale/swscale.h>
32 #ifdef __cplusplus
33 }
34 #endif  /* __cplusplus */
35 
36 #include "utils.h"
37 #include "video_output.h"
38 
39 /* If YUV is treated as full range, return 1.
40  * Otherwise, return 0. */
avoid_yuv_scale_conversion(enum AVPixelFormat * pixel_format)41 int avoid_yuv_scale_conversion( enum AVPixelFormat *pixel_format )
42 {
43     static const struct
44     {
45         enum AVPixelFormat full;
46         enum AVPixelFormat limited;
47     } range_hack_table[]
48         = {
49             { AV_PIX_FMT_YUVJ420P, AV_PIX_FMT_YUV420P },
50             { AV_PIX_FMT_YUVJ422P, AV_PIX_FMT_YUV422P },
51             { AV_PIX_FMT_YUVJ444P, AV_PIX_FMT_YUV444P },
52             { AV_PIX_FMT_YUVJ440P, AV_PIX_FMT_YUV440P },
53             { AV_PIX_FMT_NONE,     AV_PIX_FMT_NONE    }
54           };
55     for( int i = 0; range_hack_table[i].full != AV_PIX_FMT_NONE; i++ )
56         if( *pixel_format == range_hack_table[i].full )
57         {
58             *pixel_format = range_hack_table[i].limited;
59             return 1;
60         }
61     return 0;
62 }
63 
initialize_scaler_handler(lw_video_scaler_handler_t * vshp,int flags,enum AVPixelFormat output_pixel_format)64 static void initialize_scaler_handler
65 (
66     lw_video_scaler_handler_t *vshp,
67     int                        flags,
68     enum AVPixelFormat         output_pixel_format
69 )
70 {
71     if( flags != SWS_FAST_BILINEAR )
72         flags |= SWS_FULL_CHR_H_INT | SWS_FULL_CHR_H_INP | SWS_ACCURATE_RND | SWS_BITEXACT;
73     vshp->scaler_flags        = flags;
74     vshp->input_width         = 0;
75     vshp->input_height        = 0;
76     vshp->input_pixel_format  = AV_PIX_FMT_NONE;
77     vshp->output_pixel_format = output_pixel_format;
78     vshp->input_colorspace    = AVCOL_SPC_UNSPECIFIED;
79     vshp->input_yuv_range     = AVCOL_RANGE_UNSPECIFIED;
80 }
81 
setup_video_rendering(lw_video_output_handler_t * vohp,int scaler_flags,int width,int height,enum AVPixelFormat output_pixel_format,struct AVCodecContext * ctx,int (* dr_get_buffer)(struct AVCodecContext *,AVFrame *,int))82 void setup_video_rendering
83 (
84     lw_video_output_handler_t *vohp,
85     int                        scaler_flags,
86     int                        width,
87     int                        height,
88     enum AVPixelFormat         output_pixel_format,
89     struct AVCodecContext     *ctx,
90     int (*dr_get_buffer)( struct AVCodecContext *, AVFrame *, int )
91 )
92 {
93     lw_video_scaler_handler_t *vshp = &vohp->scaler;
94     initialize_scaler_handler( vshp, scaler_flags, output_pixel_format );
95     /* Set up direct rendering if available. */
96     if( ctx && dr_get_buffer )
97     {
98         /* Align output width and height for direct rendering. */
99         int linesize_align[AV_NUM_DATA_POINTERS];
100         enum AVPixelFormat input_pixel_format = ctx->pix_fmt;
101         ctx->pix_fmt = output_pixel_format;
102         avcodec_align_dimensions2( ctx, &width, &height, linesize_align );
103         ctx->pix_fmt = input_pixel_format;
104         /* Set up custom get_buffer() for direct rendering if available. */
105         ctx->get_buffer2 = dr_get_buffer;
106         ctx->opaque      = vohp;
107     }
108     vohp->output_width  = width;
109     vohp->output_height = height;
110 }
111 
update_scaler_configuration(struct SwsContext * sws_ctx,int flags,int width,int height,enum AVPixelFormat input_pixel_format,enum AVPixelFormat output_pixel_format,enum AVColorSpace colorspace,int yuv_range)112 static struct SwsContext *update_scaler_configuration
113 (
114     struct SwsContext *sws_ctx,
115     int                flags,
116     int                width,
117     int                height,
118     enum AVPixelFormat input_pixel_format,
119     enum AVPixelFormat output_pixel_format,
120     enum AVColorSpace  colorspace,
121     int                yuv_range
122 )
123 {
124     if( sws_ctx )
125         sws_freeContext( sws_ctx );
126     sws_ctx = sws_alloc_context();
127     if( !sws_ctx )
128         return NULL;
129     av_opt_set_int( sws_ctx, "sws_flags",  flags,               0 );
130     av_opt_set_int( sws_ctx, "srcw",       width,               0 );
131     av_opt_set_int( sws_ctx, "srch",       height,              0 );
132     av_opt_set_int( sws_ctx, "dstw",       width,               0 );
133     av_opt_set_int( sws_ctx, "dsth",       height,              0 );
134     av_opt_set_int( sws_ctx, "src_format", input_pixel_format,  0 );
135     av_opt_set_int( sws_ctx, "dst_format", output_pixel_format, 0 );
136     const int *yuv2rgb_coeffs = sws_getCoefficients( colorspace );
137     sws_setColorspaceDetails( sws_ctx,
138                               yuv2rgb_coeffs, yuv_range,
139                               yuv2rgb_coeffs, yuv_range,
140                               0, 1 << 16, 1 << 16 );
141     if( sws_init_context( sws_ctx, NULL, NULL ) < 0 )
142     {
143         sws_freeContext( sws_ctx );
144         return NULL;
145     }
146     return sws_ctx;
147 }
148 
update_scaler_configuration_if_needed(lw_video_scaler_handler_t * vshp,lw_log_handler_t * lhp,const AVFrame * av_frame)149 int update_scaler_configuration_if_needed
150 (
151     lw_video_scaler_handler_t *vshp,
152     lw_log_handler_t          *lhp,
153     const AVFrame             *av_frame
154 )
155 {
156     enum AVPixelFormat *input_pixel_format = (enum AVPixelFormat *)&av_frame->format;
157     int yuv_range = avoid_yuv_scale_conversion( input_pixel_format );
158     if( av_frame->color_range == AVCOL_RANGE_MPEG
159      || av_frame->color_range == AVCOL_RANGE_JPEG )
160         yuv_range = (av_frame->color_range == AVCOL_RANGE_JPEG);
161     vshp->frame_prop_change_flags
162         = (vshp->input_width        != av_frame->width      ? LW_FRAME_PROP_CHANGE_FLAG_WIDTH        : 0)
163         | (vshp->input_height       != av_frame->height     ? LW_FRAME_PROP_CHANGE_FLAG_HEIGHT       : 0)
164         | (vshp->input_pixel_format != *input_pixel_format  ? LW_FRAME_PROP_CHANGE_FLAG_PIXEL_FORMAT : 0)
165         | (vshp->input_colorspace   != av_frame->colorspace ? LW_FRAME_PROP_CHANGE_FLAG_COLORSPACE   : 0)
166         | (vshp->input_yuv_range    != yuv_range            ? LW_FRAME_PROP_CHANGE_FLAG_YUV_RANGE    : 0);
167     if( !vshp->sws_ctx || vshp->frame_prop_change_flags )
168     {
169         /* Update scaler. */
170         vshp->sws_ctx = update_scaler_configuration( vshp->sws_ctx, vshp->scaler_flags,
171                                                      av_frame->width, av_frame->height,
172                                                      *input_pixel_format, vshp->output_pixel_format,
173                                                      av_frame->colorspace, yuv_range );
174         if( !vshp->sws_ctx )
175         {
176             lw_log_show( lhp, LW_LOG_WARNING, "Failed to update video scaler configuration." );
177             return -1;
178         }
179         vshp->input_width        = av_frame->width;
180         vshp->input_height       = av_frame->height;
181         vshp->input_pixel_format = *input_pixel_format;
182         vshp->input_colorspace   = av_frame->colorspace;
183         vshp->input_yuv_range    = yuv_range;
184         return 1;
185     }
186     return 0;
187 }
188 
lw_cleanup_video_output_handler(lw_video_output_handler_t * vohp)189 void lw_cleanup_video_output_handler
190 (
191     lw_video_output_handler_t *vohp
192 )
193 {
194     if( vohp->free_private_handler )
195         vohp->free_private_handler( vohp->private_handler );
196     vohp->private_handler = NULL;
197     lw_freep( &vohp->frame_order_list );
198     for( int i = 0; i < REPEAT_CONTROL_CACHE_NUM; i++ )
199         av_frame_free( &vohp->frame_cache_buffers[i] );
200     if( vohp->scaler.sws_ctx )
201     {
202         sws_freeContext( vohp->scaler.sws_ctx );
203         vohp->scaler.sws_ctx = NULL;
204     }
205 }
206