chromecast: refactor out encoder option functions
[vlc.git] / modules / stream_out / renderer_common.cpp
1 /*****************************************************************************
2  * renderer_common.cpp : renderer helper functions
3  *****************************************************************************
4  * Copyright © 2014-2018 VideoLAN
5  *
6  * Authors: Adrien Maglo <magsoft@videolan.org>
7  *          Jean-Baptiste Kempf <jb@videolan.org>
8  *          Steve Lhomme <robux4@videolabs.io>
9  *          Shaleen Jain <shaleen@jain.sh>
10  *
11  * This program is free software; you can redistribute it and/or modify it
12  * under the terms of the GNU Lesser General Public License as published by
13  * the Free Software Foundation; either version 2.1 of the License, or
14  * (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU Lesser General Public License for more details.
20  *
21  * You should have received a copy of the GNU Lesser General Public License
22  * along with this program; if not, write to the Free Software Foundation,
23  * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
24  *****************************************************************************/
25
26 #ifdef HAVE_CONFIG_H
27 # include "config.h"
28 #endif
29
30 #include <assert.h>
31 #include <map>
32 #include <sstream>
33
34 #include "renderer_common.hpp"
35
36 std::string
37 GetVencOption( sout_stream_t *p_stream, vlc_fourcc_t *p_codec_video,
38         const video_format_t *p_vid, int i_quality );
39
40 std::string GetVencVPXOption( sout_stream_t * /* p_stream */,
41                                       const video_format_t * /* p_vid */,
42                                       int /* i_quality */ );
43
44 std::string GetVencQSVH264Option( sout_stream_t * /* p_stream */,
45                                          const video_format_t * /* p_vid */,
46                                          int i_quality );
47
48 std::string GetVencX264Option( sout_stream_t * /* p_stream */,
49                                       const video_format_t *p_vid,
50                                       int i_quality );
51 #ifdef __APPLE__
52 std::string GetVencAvcodecVTOption( sout_stream_t * /* p_stream */,
53                                            const video_format_t * p_vid,
54                                            int i_quality );
55 #endif
56
57 struct venc_options
58 {
59     std::string (*get_opt)( sout_stream_t *, const video_format_t *, int);
60 };
61
62 std::map<vlc_fourcc_t, std::vector<venc_options>> opts = {
63     {
64         VLC_CODEC_H264,
65         {
66         #ifdef __APPLE__
67             { GetVencAvcodecVTOption },
68         #endif
69             { GetVencQSVH264Option },
70             { GetVencX264Option },
71             { NULL },
72         }
73     },
74     {
75         VLC_CODEC_VP8,
76         {
77             { GetVencVPXOption }
78         }
79     },
80 };
81
82 std::string
83 GetVencOption( sout_stream_t *p_stream, std::vector<vlc_fourcc_t> codecs,
84         const video_format_t *p_vid, int i_quality )
85 {
86     for (vlc_fourcc_t codec : codecs) {
87         auto opt = opts.find(codec);
88         if (opt == opts.end())
89             break;
90         for (const auto& venc_opt : opt->second)
91         {
92             std::stringstream ssout, ssvenc;
93             char fourcc[5];
94             ssvenc << "vcodec=";
95             vlc_fourcc_to_char( codec, fourcc );
96             fourcc[4] = '\0';
97             ssvenc << fourcc << ',';
98
99             if( venc_opt.get_opt != NULL )
100                 ssvenc << venc_opt.get_opt( p_stream, p_vid, i_quality ) << ',';
101
102             /* Test if a module can encode with the specified options / fmt_video. */
103             ssout << "transcode{" << ssvenc.str() << "}:dummy";
104
105             sout_stream_t *p_sout_test =
106                 sout_StreamChainNew( p_stream->p_sout, ssout.str().c_str(), NULL, NULL );
107
108             if( p_sout_test != NULL )
109             {
110                 p_sout_test->obj.flags |= OBJECT_FLAGS_QUIET|OBJECT_FLAGS_NOINTERACT;
111
112                 es_format_t fmt;
113                 es_format_InitFromVideo( &fmt, p_vid );
114                 fmt.i_codec = fmt.video.i_chroma = VLC_CODEC_I420;
115
116                 /* Test the maximum size/fps we will encode */
117                 fmt.video.i_visible_width = fmt.video.i_width = 1920;
118                 fmt.video.i_visible_height = fmt.video.i_height = 1080;
119                 fmt.video.i_frame_rate = 30;
120                 fmt.video.i_frame_rate_base = 1;
121
122                 void *id = sout_StreamIdAdd( p_sout_test, &fmt );
123
124                 es_format_Clean( &fmt );
125                 const bool success = id != NULL;
126
127                 if( id )
128                     sout_StreamIdDel( p_sout_test, id );
129                 sout_StreamChainDelete( p_sout_test, NULL );
130
131                 if( success )
132                 {
133                     msg_Dbg( p_stream, "Converting video to %.4s", (const char*)&codec );
134                     return ssvenc.str();
135                 }
136             }
137         }
138     }
139
140     throw std::domain_error("No configured encoder found");
141 }
142
143 std::string
144 vlc_sout_renderer_GetVcodecOption(sout_stream_t *p_stream,
145         std::vector<vlc_fourcc_t> codecs, const video_format_t *p_vid, int i_quality)
146 {
147     std::stringstream ssout;
148     static const char video_maxres_hd[] = "maxwidth=1920,maxheight=1080";
149     static const char video_maxres_720p[] = "maxwidth=1280,maxheight=720";
150
151     ssout << GetVencOption( p_stream, codecs, p_vid, i_quality );
152
153     switch ( i_quality )
154     {
155         case CONVERSION_QUALITY_HIGH:
156         case CONVERSION_QUALITY_MEDIUM:
157             ssout << ( ( p_vid->i_width > 1920 ) ? "width=1920," : "" ) << video_maxres_hd << ',';
158             break;
159         default:
160             ssout << ( ( p_vid->i_width > 1280 ) ? "width=1280," : "" ) << video_maxres_720p << ',';
161     }
162
163     if( p_vid->i_frame_rate == 0 || p_vid->i_frame_rate_base == 0
164      || ( p_vid->i_frame_rate / p_vid->i_frame_rate_base ) > 30 )
165     {
166         /* Even force 24fps if the frame rate is unknown */
167         msg_Warn( p_stream, "lowering frame rate to 24fps" );
168         ssout << "fps=24,";
169     }
170
171     return ssout.str();
172 }
173
174 std::string GetVencVPXOption( sout_stream_t * /* p_stream */,
175                                       const video_format_t * /* p_vid */,
176                                       int /* i_quality */ )
177 {
178     return "venc=vpx{quality-mode=1}";
179 }
180
181 std::string GetVencQSVH264Option( sout_stream_t * /* p_stream */,
182                                          const video_format_t * /* p_vid */,
183                                          int i_quality )
184 {
185     std::stringstream ssout;
186     static const char video_target_usage_quality[]  = "quality";
187     static const char video_target_usage_balanced[] = "balanced";
188     static const char video_target_usage_speed[]    = "speed";
189     static const char video_bitrate_high[] = "vb=8000000";
190     static const char video_bitrate_low[]  = "vb=3000000";
191     const char *psz_video_target_usage;
192     const char *psz_video_bitrate;
193
194     switch ( i_quality )
195     {
196         case CONVERSION_QUALITY_HIGH:
197             psz_video_target_usage = video_target_usage_quality;
198             psz_video_bitrate = video_bitrate_high;
199             break;
200         case CONVERSION_QUALITY_MEDIUM:
201             psz_video_target_usage = video_target_usage_balanced;
202             psz_video_bitrate = video_bitrate_high;
203             break;
204         case CONVERSION_QUALITY_LOW:
205             psz_video_target_usage = video_target_usage_balanced;
206             psz_video_bitrate = video_bitrate_low;
207             break;
208         default:
209         case CONVERSION_QUALITY_LOWCPU:
210             psz_video_target_usage = video_target_usage_speed;
211             psz_video_bitrate = video_bitrate_low;
212             break;
213     }
214
215     ssout << "venc=qsv{target-usage=" << psz_video_target_usage <<
216              "}," << psz_video_bitrate;
217     return ssout.str();
218 }
219
220 std::string GetVencX264Option( sout_stream_t * /* p_stream */,
221                                       const video_format_t *p_vid,
222                                       int i_quality )
223 {
224     std::stringstream ssout;
225     static const char video_x264_preset_veryfast[] = "veryfast";
226     static const char video_x264_preset_ultrafast[] = "ultrafast";
227     const char *psz_video_x264_preset;
228     unsigned i_video_x264_crf_hd, i_video_x264_crf_720p;
229
230     switch ( i_quality )
231     {
232         case CONVERSION_QUALITY_HIGH:
233             i_video_x264_crf_hd = i_video_x264_crf_720p = 21;
234             psz_video_x264_preset = video_x264_preset_veryfast;
235             break;
236         case CONVERSION_QUALITY_MEDIUM:
237             i_video_x264_crf_hd = 23;
238             i_video_x264_crf_720p = 21;
239             psz_video_x264_preset = video_x264_preset_veryfast;
240             break;
241         case CONVERSION_QUALITY_LOW:
242             i_video_x264_crf_hd = i_video_x264_crf_720p = 23;
243             psz_video_x264_preset = video_x264_preset_veryfast;
244             break;
245         default:
246         case CONVERSION_QUALITY_LOWCPU:
247             i_video_x264_crf_hd = i_video_x264_crf_720p = 23;
248             psz_video_x264_preset = video_x264_preset_ultrafast;
249             break;
250     }
251
252     const bool b_hdres = p_vid->i_height == 0 || p_vid->i_height >= 800;
253     unsigned i_video_x264_crf = b_hdres ? i_video_x264_crf_hd : i_video_x264_crf_720p;
254
255     ssout << "venc=x264{preset=" << psz_video_x264_preset
256           << ",crf=" << i_video_x264_crf << "}";
257     return ssout.str();
258 }
259
260 #ifdef __APPLE__
261 std::string GetVencAvcodecVTOption( sout_stream_t * /* p_stream */,
262                                            const video_format_t * p_vid,
263                                            int i_quality )
264 {
265     std::stringstream ssout;
266     ssout << "venc=avcodec{codec=h264_videotoolbox,options{realtime=1}}";
267     switch( i_quality )
268     {
269         /* Here, performances issues won't come from videotoolbox but from
270          * some old chromecast devices */
271
272         case CONVERSION_QUALITY_HIGH:
273             break;
274         case CONVERSION_QUALITY_MEDIUM:
275             ssout << ",vb=8000000";
276             break;
277         case CONVERSION_QUALITY_LOW:
278         case CONVERSION_QUALITY_LOWCPU:
279             ssout << ",vb=3000000";
280             break;
281     }
282
283     return ssout.str();
284 }
285 #endif