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