/src/vlc/modules/codec/vpx.c
Line | Count | Source |
1 | | /***************************************************************************** |
2 | | * vpx.c: libvpx decoder (VP8/VP9) module |
3 | | ***************************************************************************** |
4 | | * Copyright (C) 2013 Rafaël Carré |
5 | | * |
6 | | * Authors: Rafaël Carré <funman@videolanorg> |
7 | | * |
8 | | * This program is free software; you can redistribute it and/or modify it |
9 | | * under the terms of the GNU Lesser General Public License as published by |
10 | | * the Free Software Foundation; either version 2.1 of the License, or |
11 | | * (at your option) any later version. |
12 | | * |
13 | | * This program is distributed in the hope that it will be useful, |
14 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
15 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
16 | | * GNU Lesser General Public License for more details. |
17 | | * |
18 | | * You should have received a copy of the GNU Lesser General Public License |
19 | | * along with this program; if not, write to the Free Software Foundation, |
20 | | * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA. |
21 | | *****************************************************************************/ |
22 | | |
23 | | /***************************************************************************** |
24 | | * Preamble |
25 | | *****************************************************************************/ |
26 | | #ifdef HAVE_CONFIG_H |
27 | | # include "config.h" |
28 | | #endif |
29 | | |
30 | | #include <vlc_common.h> |
31 | | #include <vlc_configuration.h> |
32 | | #include <vlc_plugin.h> |
33 | | #include <vlc_codec.h> |
34 | | |
35 | | #include <vpx/vpx_decoder.h> |
36 | | #include <vpx/vp8dx.h> |
37 | | #include <vpx/vpx_image.h> |
38 | | |
39 | | #ifdef ENABLE_SOUT |
40 | | # include <vpx/vpx_encoder.h> |
41 | | # include <vpx/vp8cx.h> |
42 | | #endif |
43 | | |
44 | | /**************************************************************************** |
45 | | * Local prototypes |
46 | | ****************************************************************************/ |
47 | | static int OpenDecoder(vlc_object_t *); |
48 | | static void CloseDecoder(vlc_object_t *); |
49 | | #ifdef ENABLE_SOUT |
50 | | static const char *const ppsz_sout_options[] = { "quality-mode", NULL }; |
51 | | static int OpenEncoder(vlc_object_t *); |
52 | | static void CloseEncoder(encoder_t *); |
53 | | static block_t *Encode(encoder_t *p_enc, picture_t *p_pict); |
54 | | |
55 | | #define QUALITY_MODE_TEXT N_("Quality mode") |
56 | | #define QUALITY_MODE_LONGTEXT N_("Quality setting which will determine max encoding time.") |
57 | | |
58 | | static const int quality_values[] = { |
59 | | VPX_DL_GOOD_QUALITY, VPX_DL_REALTIME, VPX_DL_BEST_QUALITY |
60 | | }; |
61 | | static const char* const quality_desc[] = { |
62 | | N_("Good"), N_("Realtime"), N_("Best"), |
63 | | }; |
64 | | #endif |
65 | | #define THREADS_TEXT N_( "Threads" ) |
66 | | #define THREADS_LONGTEXT N_( "Number of threads used for decoding, 0 meaning auto" ) |
67 | | |
68 | | /***************************************************************************** |
69 | | * Module descriptor |
70 | | *****************************************************************************/ |
71 | | |
72 | 104 | vlc_module_begin () |
73 | 52 | set_shortname("vpx") |
74 | 52 | set_description(N_("WebM video decoder")) |
75 | 52 | set_capability("video decoder", 60) |
76 | 104 | set_callbacks(OpenDecoder, CloseDecoder) |
77 | 52 | add_integer( "vpx-threads", 0, THREADS_TEXT, THREADS_LONGTEXT ); |
78 | 52 | set_subcategory(SUBCAT_INPUT_VCODEC) |
79 | 52 | #ifdef ENABLE_SOUT |
80 | 52 | add_submodule() |
81 | 52 | set_shortname("vpx") |
82 | 52 | set_capability("video encoder", 60) |
83 | 52 | set_description(N_("WebM video encoder")) |
84 | 52 | set_callback(OpenEncoder) |
85 | | |
86 | 52 | add_submodule() |
87 | 52 | set_shortname("vpx") |
88 | 52 | set_capability("image encoder", 60) |
89 | 52 | set_description(N_("WebP image encoder")) |
90 | 52 | set_callback(OpenEncoder) |
91 | | |
92 | 52 | # define ENC_CFG_PREFIX "sout-vpx-" |
93 | 52 | add_integer( ENC_CFG_PREFIX "quality-mode", VPX_DL_BEST_QUALITY, QUALITY_MODE_TEXT, |
94 | 52 | QUALITY_MODE_LONGTEXT ) |
95 | 52 | change_integer_list( quality_values, quality_desc ) |
96 | 52 | #endif |
97 | 52 | vlc_module_end () |
98 | | |
99 | | static void vpx_err_msg(vlc_object_t *this, struct vpx_codec_ctx *ctx, |
100 | | const char *msg) |
101 | 13 | { |
102 | 13 | const char *error = vpx_codec_error(ctx); |
103 | 13 | const char *detail = vpx_codec_error_detail(ctx); |
104 | 13 | if (!detail) |
105 | 13 | detail = "no specific information"; |
106 | 13 | msg_Err(this, msg, error, detail); |
107 | 13 | } |
108 | | |
109 | 13 | #define VPX_ERR(this, ctx, msg) vpx_err_msg(VLC_OBJECT(this), ctx, msg ": %s (%s)") |
110 | | |
111 | | /***************************************************************************** |
112 | | * decoder_sys_t: libvpx decoder descriptor |
113 | | *****************************************************************************/ |
114 | | typedef struct |
115 | | { |
116 | | struct vpx_codec_ctx ctx; |
117 | | } decoder_sys_t; |
118 | | |
119 | | static const struct |
120 | | { |
121 | | vlc_fourcc_t i_chroma; |
122 | | enum vpx_img_fmt i_chroma_id; |
123 | | uint8_t i_bitdepth; |
124 | | enum vpx_color_space cs; |
125 | | } chroma_table[] = |
126 | | { |
127 | | /* Transfer characteristic-dependent mappings must come first */ |
128 | | { VLC_CODEC_GBR_PLANAR, VPX_IMG_FMT_I444, 8, VPX_CS_SRGB }, |
129 | | { VLC_CODEC_GBR_PLANAR_10L, VPX_IMG_FMT_I44416, 10, VPX_CS_SRGB }, |
130 | | |
131 | | { VLC_CODEC_I420, VPX_IMG_FMT_I420, 8, VPX_CS_UNKNOWN }, |
132 | | { VLC_CODEC_I422, VPX_IMG_FMT_I422, 8, VPX_CS_UNKNOWN }, |
133 | | { VLC_CODEC_I444, VPX_IMG_FMT_I444, 8, VPX_CS_UNKNOWN }, |
134 | | { VLC_CODEC_I440, VPX_IMG_FMT_I440, 8, VPX_CS_UNKNOWN }, |
135 | | |
136 | | { VLC_CODEC_YV12, VPX_IMG_FMT_YV12, 8, VPX_CS_UNKNOWN }, |
137 | | |
138 | | { VLC_CODEC_I420_10L, VPX_IMG_FMT_I42016, 10, VPX_CS_UNKNOWN }, |
139 | | { VLC_CODEC_I422_10L, VPX_IMG_FMT_I42216, 10, VPX_CS_UNKNOWN }, |
140 | | { VLC_CODEC_I444_10L, VPX_IMG_FMT_I44416, 10, VPX_CS_UNKNOWN }, |
141 | | |
142 | | { VLC_CODEC_I420_12L, VPX_IMG_FMT_I42016, 12, VPX_CS_UNKNOWN }, |
143 | | { VLC_CODEC_I422_12L, VPX_IMG_FMT_I42216, 12, VPX_CS_UNKNOWN }, |
144 | | { VLC_CODEC_I444_12L, VPX_IMG_FMT_I44416, 12, VPX_CS_UNKNOWN }, |
145 | | |
146 | | { VLC_CODEC_I444_16L, VPX_IMG_FMT_I44416, 16, VPX_CS_UNKNOWN }, |
147 | | }; |
148 | | |
149 | | struct video_color |
150 | | { |
151 | | video_color_primaries_t primaries; |
152 | | video_transfer_func_t transfer; |
153 | | video_color_space_t space; |
154 | | }; |
155 | | |
156 | | const struct video_color vpx_color_mapping_table[] = |
157 | | { |
158 | | [VPX_CS_UNKNOWN] = { COLOR_PRIMARIES_UNDEF, |
159 | | TRANSFER_FUNC_UNDEF, |
160 | | COLOR_SPACE_UNDEF }, |
161 | | [VPX_CS_BT_601] = { COLOR_PRIMARIES_BT601_525, |
162 | | TRANSFER_FUNC_BT709, |
163 | | COLOR_SPACE_BT601 }, |
164 | | [VPX_CS_BT_709] = { COLOR_PRIMARIES_BT709, |
165 | | TRANSFER_FUNC_BT709, |
166 | | COLOR_SPACE_BT709 }, |
167 | | [VPX_CS_SMPTE_170] = { COLOR_PRIMARIES_SMTPE_170, |
168 | | TRANSFER_FUNC_BT709, |
169 | | COLOR_SPACE_BT601 }, |
170 | | [VPX_CS_SMPTE_240] = { COLOR_PRIMARIES_SMTPE_240, |
171 | | TRANSFER_FUNC_SMPTE_240, |
172 | | COLOR_SPACE_UNDEF }, |
173 | | [VPX_CS_BT_2020] = { COLOR_PRIMARIES_BT2020, |
174 | | TRANSFER_FUNC_BT2020, |
175 | | COLOR_SPACE_BT2020 }, |
176 | | [VPX_CS_RESERVED] = { COLOR_PRIMARIES_UNDEF, |
177 | | TRANSFER_FUNC_UNDEF, |
178 | | COLOR_SPACE_UNDEF }, |
179 | | [VPX_CS_SRGB] = { COLOR_PRIMARIES_SRGB, |
180 | | TRANSFER_FUNC_SRGB, |
181 | | COLOR_SPACE_UNDEF }, |
182 | | }; |
183 | | |
184 | | static vlc_fourcc_t FindVlcChroma( struct vpx_image *img ) |
185 | 0 | { |
186 | 0 | for( unsigned int i = 0; i < ARRAY_SIZE(chroma_table); i++ ) |
187 | 0 | if( chroma_table[i].i_chroma_id == img->fmt && |
188 | 0 | chroma_table[i].i_bitdepth == img->bit_depth && |
189 | 0 | ( chroma_table[i].cs == VPX_CS_UNKNOWN || |
190 | 0 | chroma_table[i].cs == img->cs ) ) |
191 | 0 | return chroma_table[i].i_chroma; |
192 | | |
193 | 0 | return 0; |
194 | 0 | } |
195 | | |
196 | | /**************************************************************************** |
197 | | * Decode: the whole thing |
198 | | ****************************************************************************/ |
199 | | static int Decode(decoder_t *dec, block_t *block) |
200 | 35 | { |
201 | 35 | decoder_sys_t *p_sys = dec->p_sys; |
202 | 35 | struct vpx_codec_ctx *ctx = &p_sys->ctx; |
203 | | |
204 | 35 | if (block == NULL) /* No Drain */ |
205 | 22 | return VLCDEC_SUCCESS; |
206 | | |
207 | 13 | if (block->i_flags & (BLOCK_FLAG_CORRUPTED)) { |
208 | 0 | block_Release(block); |
209 | 0 | return VLCDEC_SUCCESS; |
210 | 0 | } |
211 | | |
212 | | /* Associate packet PTS with decoded frame */ |
213 | 13 | vlc_tick_t *pkt_pts = malloc(sizeof(*pkt_pts)); |
214 | 13 | if (!pkt_pts) { |
215 | 0 | block_Release(block); |
216 | 0 | return VLCDEC_SUCCESS; |
217 | 0 | } |
218 | | |
219 | 13 | *pkt_pts = (block->i_pts != VLC_TICK_INVALID) ? block->i_pts : block->i_dts; |
220 | | |
221 | 13 | vpx_codec_err_t err; |
222 | 13 | err = vpx_codec_decode(ctx, block->p_buffer, block->i_buffer, pkt_pts, 0); |
223 | | |
224 | 13 | block_Release(block); |
225 | | |
226 | 13 | if (err != VPX_CODEC_OK) { |
227 | 13 | free(pkt_pts); |
228 | 13 | VPX_ERR(dec, ctx, "Failed to decode frame"); |
229 | 13 | if (err == VPX_CODEC_UNSUP_BITSTREAM) |
230 | 4 | return VLCDEC_ECRITICAL; |
231 | 9 | else |
232 | 9 | return VLCDEC_SUCCESS; |
233 | 13 | } |
234 | | |
235 | 0 | const void *iter = NULL; |
236 | 0 | struct vpx_image *img = vpx_codec_get_frame(ctx, &iter); |
237 | 0 | if (!img) { |
238 | 0 | free(pkt_pts); |
239 | 0 | return VLCDEC_SUCCESS; |
240 | 0 | } |
241 | | |
242 | | /* fetches back the PTS */ |
243 | 0 | pkt_pts = img->user_priv; |
244 | 0 | vlc_tick_t pts = *pkt_pts; |
245 | 0 | free(pkt_pts); |
246 | |
|
247 | 0 | dec->fmt_out.i_codec = FindVlcChroma(img); |
248 | |
|
249 | 0 | if( dec->fmt_out.i_codec == 0 ) { |
250 | 0 | msg_Err(dec, "Unsupported output colorspace %d", img->fmt); |
251 | 0 | return VLCDEC_SUCCESS; |
252 | 0 | } |
253 | | |
254 | 0 | video_format_t *v = &dec->fmt_out.video; |
255 | |
|
256 | 0 | if (img->d_w != v->i_visible_width || img->d_h != v->i_visible_height) { |
257 | 0 | v->i_visible_width = dec->fmt_out.video.i_width = img->d_w; |
258 | 0 | v->i_visible_height = dec->fmt_out.video.i_height = img->d_h; |
259 | 0 | } |
260 | |
|
261 | 0 | if( !dec->fmt_out.video.i_sar_num || !dec->fmt_out.video.i_sar_den ) |
262 | 0 | { |
263 | 0 | dec->fmt_out.video.i_sar_num = 1; |
264 | 0 | dec->fmt_out.video.i_sar_den = 1; |
265 | 0 | } |
266 | |
|
267 | 0 | if(dec->fmt_in->video.primaries == COLOR_PRIMARIES_UNDEF && |
268 | 0 | img->cs >= 0 && img->cs < ARRAY_SIZE(vpx_color_mapping_table)) |
269 | 0 | { |
270 | 0 | v->primaries = vpx_color_mapping_table[img->cs].primaries; |
271 | 0 | v->transfer = vpx_color_mapping_table[img->cs].transfer; |
272 | 0 | v->space = vpx_color_mapping_table[img->cs].space; |
273 | 0 | v->color_range = img->range == VPX_CR_FULL_RANGE ? COLOR_RANGE_FULL : COLOR_RANGE_LIMITED; |
274 | 0 | } |
275 | |
|
276 | 0 | dec->fmt_out.video.projection_mode = dec->fmt_in->video.projection_mode; |
277 | 0 | dec->fmt_out.video.multiview_mode = dec->fmt_in->video.multiview_mode; |
278 | 0 | dec->fmt_out.video.pose = dec->fmt_in->video.pose; |
279 | |
|
280 | 0 | if (decoder_UpdateVideoFormat(dec)) |
281 | 0 | return VLCDEC_SUCCESS; |
282 | 0 | picture_t *pic = decoder_NewPicture(dec); |
283 | 0 | if (!pic) |
284 | 0 | return VLCDEC_SUCCESS; |
285 | | |
286 | 0 | for (int plane = 0; plane < pic->i_planes; plane++ ) { |
287 | 0 | plane_t src_plane = pic->p[plane]; |
288 | 0 | src_plane.p_pixels = img->planes[plane]; |
289 | 0 | src_plane.i_pitch = img->stride[plane]; |
290 | 0 | plane_CopyPixels(&pic->p[plane], &src_plane); |
291 | 0 | } |
292 | |
|
293 | 0 | pic->b_progressive = true; /* codec does not support interlacing */ |
294 | 0 | pic->date = pts; |
295 | |
|
296 | 0 | decoder_QueueVideo(dec, pic); |
297 | 0 | return VLCDEC_SUCCESS; |
298 | 0 | } |
299 | | |
300 | | /***************************************************************************** |
301 | | * OpenDecoder: probe the decoder |
302 | | *****************************************************************************/ |
303 | | static int OpenDecoder(vlc_object_t *p_this) |
304 | 12.5k | { |
305 | 12.5k | decoder_t *dec = (decoder_t *)p_this; |
306 | 12.5k | const struct vpx_codec_iface *iface; |
307 | 12.5k | int vp_version; |
308 | | |
309 | 12.5k | switch (dec->fmt_in->i_codec) |
310 | 12.5k | { |
311 | 0 | #ifdef ENABLE_VP8_DECODER |
312 | 41 | case VLC_CODEC_VP8: |
313 | 41 | if (es_format_HasVpxAlpha(dec->fmt_in)) // contains alpha extradata |
314 | 41 | return VLC_ENOTSUP; |
315 | | // fallthrough |
316 | 26 | case VLC_CODEC_WEBP: |
317 | 26 | case VLC_CODEC_VP8ALPHA_ES: |
318 | 26 | iface = &vpx_codec_vp8_dx_algo; |
319 | 26 | vp_version = 8; |
320 | 26 | break; |
321 | 0 | #endif |
322 | 0 | #ifdef ENABLE_VP9_DECODER |
323 | 1 | case VLC_CODEC_VP9: |
324 | 1 | if (es_format_HasVpxAlpha(dec->fmt_in)) // contains alpha extradata |
325 | 1 | return VLC_ENOTSUP; |
326 | | // fallthrough |
327 | 0 | case VLC_CODEC_VP9ALPHA_ES: |
328 | 0 | iface = &vpx_codec_vp9_dx_algo; |
329 | 0 | vp_version = 9; |
330 | 0 | break; |
331 | 0 | #endif |
332 | 12.4k | default: |
333 | 12.4k | return VLC_EGENERIC; |
334 | 12.5k | } |
335 | | |
336 | 26 | decoder_sys_t *sys = malloc(sizeof(*sys)); |
337 | 26 | if (!sys) |
338 | 0 | return VLC_ENOMEM; |
339 | 26 | dec->p_sys = sys; |
340 | | |
341 | 26 | int i_thread_count = var_InheritInteger(p_this, "vpx-threads"); |
342 | 26 | if (i_thread_count <= 0) |
343 | 26 | i_thread_count = vlc_GetCPUCount(); |
344 | 26 | struct vpx_codec_dec_cfg deccfg = { |
345 | 26 | .threads = __MIN(i_thread_count, 16) |
346 | 26 | }; |
347 | | |
348 | 26 | msg_Dbg(p_this, "VP%d: using libvpx version %s (build options %s)", |
349 | 26 | vp_version, vpx_codec_version_str(), vpx_codec_build_config()); |
350 | | |
351 | 26 | if (vpx_codec_dec_init(&sys->ctx, iface, &deccfg, 0) != VPX_CODEC_OK) { |
352 | 0 | VPX_ERR(p_this, &sys->ctx, "Failed to initialize decoder"); |
353 | 0 | free(sys); |
354 | 0 | return VLC_EGENERIC; |
355 | 0 | } |
356 | | |
357 | 26 | dec->pf_decode = Decode; |
358 | | |
359 | 26 | dec->fmt_out.video.i_width = dec->fmt_in->video.i_width; |
360 | 26 | dec->fmt_out.video.i_height = dec->fmt_in->video.i_height; |
361 | | |
362 | 26 | if (dec->fmt_in->video.i_sar_num > 0 && dec->fmt_in->video.i_sar_den > 0) { |
363 | 0 | dec->fmt_out.video.i_sar_num = dec->fmt_in->video.i_sar_num; |
364 | 0 | dec->fmt_out.video.i_sar_den = dec->fmt_in->video.i_sar_den; |
365 | 0 | } |
366 | | |
367 | 26 | return VLC_SUCCESS; |
368 | 26 | } |
369 | | |
370 | | /***************************************************************************** |
371 | | * CloseDecoder: decoder destruction |
372 | | *****************************************************************************/ |
373 | | static void CloseDecoder(vlc_object_t *p_this) |
374 | 26 | { |
375 | 26 | decoder_t *dec = (decoder_t *)p_this; |
376 | 26 | decoder_sys_t *sys = dec->p_sys; |
377 | | |
378 | | /* Free our PTS */ |
379 | 26 | const void *iter = NULL; |
380 | 26 | for (;;) { |
381 | 26 | struct vpx_image *img = vpx_codec_get_frame(&sys->ctx, &iter); |
382 | 26 | if (!img) |
383 | 26 | break; |
384 | 0 | free(img->user_priv); |
385 | 0 | } |
386 | | |
387 | 26 | vpx_codec_destroy(&sys->ctx); |
388 | | |
389 | 26 | free(sys); |
390 | 26 | } |
391 | | |
392 | | #ifdef ENABLE_SOUT |
393 | | |
394 | | /***************************************************************************** |
395 | | * encoder_sys_t: libvpx encoder descriptor |
396 | | *****************************************************************************/ |
397 | | typedef struct |
398 | | { |
399 | | struct vpx_codec_ctx ctx; |
400 | | unsigned long quality; |
401 | | } encoder_sys_t; |
402 | | |
403 | | /***************************************************************************** |
404 | | * OpenEncoder: probe the encoder |
405 | | *****************************************************************************/ |
406 | | static int OpenEncoder(vlc_object_t *p_this) |
407 | 0 | { |
408 | 0 | encoder_t *p_enc = (encoder_t *)p_this; |
409 | 0 | encoder_sys_t *p_sys; |
410 | |
|
411 | 0 | const struct vpx_codec_iface *iface; |
412 | 0 | int vp_version; |
413 | |
|
414 | 0 | switch (p_enc->fmt_out.i_codec) |
415 | 0 | { |
416 | 0 | #ifdef ENABLE_VP8_ENCODER |
417 | 0 | case VLC_CODEC_VP8: |
418 | 0 | if (es_format_HasVpxAlpha(&p_enc->fmt_out)) // contains alpha extradata |
419 | 0 | return VLC_ENOTSUP; |
420 | | // fallthrough |
421 | 0 | case VLC_CODEC_WEBP: |
422 | 0 | iface = &vpx_codec_vp8_cx_algo; |
423 | 0 | vp_version = 8; |
424 | 0 | break; |
425 | 0 | #endif |
426 | 0 | #ifdef ENABLE_VP9_ENCODER |
427 | 0 | case VLC_CODEC_VP9: |
428 | 0 | if (es_format_HasVpxAlpha(&p_enc->fmt_out)) // contains alpha extradata |
429 | 0 | return VLC_ENOTSUP; |
430 | 0 | iface = &vpx_codec_vp9_cx_algo; |
431 | 0 | vp_version = 9; |
432 | 0 | break; |
433 | 0 | #endif |
434 | 0 | default: |
435 | 0 | return VLC_EGENERIC; |
436 | 0 | } |
437 | | |
438 | | /* Allocate the memory needed to store the encoder's structure */ |
439 | 0 | p_sys = malloc(sizeof(*p_sys)); |
440 | 0 | if (p_sys == NULL) |
441 | 0 | return VLC_ENOMEM; |
442 | | |
443 | 0 | struct vpx_codec_enc_cfg enccfg = {0}; |
444 | 0 | vpx_codec_enc_config_default(iface, &enccfg, 0); |
445 | 0 | enccfg.g_threads = __MIN(vlc_GetCPUCount(), 4); |
446 | 0 | enccfg.g_w = p_enc->fmt_in.video.i_visible_width; |
447 | 0 | enccfg.g_h = p_enc->fmt_in.video.i_visible_height; |
448 | |
|
449 | 0 | msg_Dbg(p_this, "VP%d: using libvpx version %s (build options %s)", |
450 | 0 | vp_version, vpx_codec_version_str(), vpx_codec_build_config()); |
451 | |
|
452 | 0 | struct vpx_codec_ctx *ctx = &p_sys->ctx; |
453 | 0 | if (vpx_codec_enc_init(ctx, iface, &enccfg, 0) != VPX_CODEC_OK) { |
454 | 0 | VPX_ERR(p_this, ctx, "Failed to initialize encoder"); |
455 | 0 | goto error; |
456 | 0 | } |
457 | | |
458 | 0 | p_enc->fmt_in.i_codec = p_enc->fmt_in.video.i_chroma = VLC_CODEC_I420; |
459 | 0 | config_ChainParse(p_enc, ENC_CFG_PREFIX, ppsz_sout_options, p_enc->p_cfg); |
460 | | |
461 | | /* Deadline (in ms) to spend in encoder */ |
462 | 0 | const unsigned long quality = var_GetInteger(p_enc, ENC_CFG_PREFIX "quality-mode"); |
463 | 0 | switch (quality) { |
464 | 0 | case VPX_DL_REALTIME: |
465 | 0 | case VPX_DL_BEST_QUALITY: |
466 | 0 | case VPX_DL_GOOD_QUALITY: |
467 | 0 | p_sys->quality = quality; |
468 | 0 | break; |
469 | 0 | default: |
470 | 0 | msg_Warn(p_this, "Unexpected quality %lu, forcing %lu", quality, (unsigned long)VPX_DL_BEST_QUALITY); |
471 | 0 | p_sys->quality = VPX_DL_BEST_QUALITY; |
472 | 0 | break; |
473 | 0 | } |
474 | | |
475 | 0 | static const struct vlc_encoder_operations ops = |
476 | 0 | { |
477 | 0 | .close = CloseEncoder, |
478 | 0 | .encode_video = Encode, |
479 | 0 | }; |
480 | 0 | p_enc->ops = &ops; |
481 | 0 | p_enc->p_sys = p_sys; |
482 | |
|
483 | 0 | return VLC_SUCCESS; |
484 | 0 | error: |
485 | 0 | free(p_sys); |
486 | 0 | return VLC_EGENERIC; |
487 | 0 | } |
488 | | |
489 | | static const uint32_t webp_simple_lossy_header[5] = { |
490 | | VLC_FOURCC('R', 'I', 'F', 'F'), |
491 | | 0, /* TBD: total size of VP8 data plus 12 bytes for WEBP fourcc + VP8 ChunkHeader */ |
492 | | VLC_FOURCC('W', 'E', 'B', 'P'), |
493 | | VLC_FOURCC('V', 'P', '8', ' '), |
494 | | 0, /* TBD: total size of VP8 data */ |
495 | | }; |
496 | | |
497 | | static void webp_write_header(uint8_t *p_header, uint32_t i_size, size_t i_header_size) |
498 | 0 | { |
499 | 0 | assert(i_header_size == sizeof(webp_simple_lossy_header)); |
500 | |
|
501 | 0 | memcpy(p_header, webp_simple_lossy_header, i_header_size); |
502 | 0 | SetDWLE(p_header + 1*sizeof(uint32_t), i_size + 4 + 8); |
503 | 0 | SetDWLE(p_header + 4*sizeof(uint32_t), i_size); |
504 | 0 | } |
505 | | |
506 | | /**************************************************************************** |
507 | | * Encode: the whole thing |
508 | | ****************************************************************************/ |
509 | | static block_t *Encode(encoder_t *p_enc, picture_t *p_pict) |
510 | 0 | { |
511 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
512 | 0 | struct vpx_codec_ctx *ctx = &p_sys->ctx; |
513 | |
|
514 | 0 | if (!p_pict) return NULL; |
515 | | |
516 | 0 | vpx_image_t img = {0}; |
517 | 0 | unsigned i_w = p_enc->fmt_in.video.i_visible_width; |
518 | 0 | unsigned i_h = p_enc->fmt_in.video.i_visible_height; |
519 | | |
520 | | /* Create and initialize the vpx_image (use 1 and correct later to avoid getting |
521 | | rejected for non-power of 2 pitch) */ |
522 | 0 | if (!vpx_img_wrap(&img, VPX_IMG_FMT_I420, i_w, i_h, 1, p_pict->p[0].p_pixels)) { |
523 | 0 | VPX_ERR(p_enc, ctx, "Failed to wrap image"); |
524 | 0 | return NULL; |
525 | 0 | } |
526 | | |
527 | | /* Fill in real plane/stride values. */ |
528 | 0 | for (int plane = 0; plane < p_pict->i_planes; plane++) { |
529 | 0 | img.planes[plane] = p_pict->p[plane].p_pixels; |
530 | 0 | img.stride[plane] = p_pict->p[plane].i_pitch; |
531 | 0 | } |
532 | |
|
533 | 0 | int flags = 0; |
534 | |
|
535 | 0 | vpx_codec_err_t res = vpx_codec_encode(ctx, &img, p_pict->date, 1, |
536 | 0 | flags, p_sys->quality); |
537 | 0 | if (res != VPX_CODEC_OK) { |
538 | 0 | VPX_ERR(p_enc, ctx, "Failed to encode frame"); |
539 | 0 | vpx_img_free(&img); |
540 | 0 | return NULL; |
541 | 0 | } |
542 | | |
543 | 0 | const vpx_codec_cx_pkt_t *pkt = NULL; |
544 | 0 | vpx_codec_iter_t iter = NULL; |
545 | 0 | block_t *p_out = NULL; |
546 | | |
547 | | /* WebP container specific context */ |
548 | 0 | uint32_t i_vp8_data_size = 0; |
549 | 0 | uint8_t *p_header = NULL; |
550 | 0 | const bool b_is_webp = p_enc->fmt_out.i_codec == VLC_CODEC_WEBP; |
551 | 0 | static const size_t i_webp_header_size = sizeof(webp_simple_lossy_header); |
552 | |
|
553 | 0 | while ((pkt = vpx_codec_get_cx_data(ctx, &iter)) != NULL) |
554 | 0 | { |
555 | 0 | if (pkt->kind == VPX_CODEC_CX_FRAME_PKT) |
556 | 0 | { |
557 | 0 | size_t i_block_sz = pkt->data.frame.sz; |
558 | 0 | const bool b_needs_padding_byte = b_is_webp && (pkt->data.frame.sz & 1); |
559 | 0 | int keyframe = pkt->data.frame.flags & VPX_FRAME_IS_KEY; |
560 | |
|
561 | 0 | if (b_is_webp && p_header == NULL) { |
562 | 0 | i_block_sz += i_webp_header_size; |
563 | 0 | i_block_sz += b_needs_padding_byte; |
564 | 0 | } |
565 | |
|
566 | 0 | block_t *p_block = block_Alloc(i_block_sz); |
567 | 0 | if (unlikely(p_block == NULL)) |
568 | 0 | { |
569 | 0 | block_ChainRelease(p_out); |
570 | 0 | p_out = NULL; |
571 | 0 | break; |
572 | 0 | } |
573 | | |
574 | 0 | uint8_t *p_buffer = p_block->p_buffer; |
575 | | |
576 | | /* Leave room at the beginning for the WebP header data. */ |
577 | 0 | if (b_is_webp && p_header == NULL) { |
578 | 0 | p_header = p_buffer; |
579 | 0 | p_buffer += i_webp_header_size; |
580 | 0 | i_vp8_data_size += pkt->data.frame.sz; |
581 | 0 | } |
582 | |
|
583 | 0 | memcpy(p_buffer, pkt->data.frame.buf, pkt->data.frame.sz); |
584 | 0 | p_block->i_dts = p_block->i_pts = pkt->data.frame.pts; |
585 | 0 | if (keyframe) |
586 | 0 | p_block->i_flags |= BLOCK_FLAG_TYPE_I; |
587 | | |
588 | | /* If Chunk Size is odd, a single padding byte -- that MUST be 0 to |
589 | | conform with RIFF -- is added. */ |
590 | 0 | if (b_needs_padding_byte) |
591 | 0 | p_block->p_buffer[i_block_sz - 1] = 0; |
592 | |
|
593 | 0 | block_ChainAppend(&p_out, p_block); |
594 | 0 | } |
595 | 0 | } |
596 | | |
597 | | /* For WebP, now that we have the total size, write the RIFF header. */ |
598 | 0 | if (b_is_webp && p_header) |
599 | 0 | webp_write_header(p_header, i_vp8_data_size, i_webp_header_size); |
600 | |
|
601 | 0 | vpx_img_free(&img); |
602 | 0 | return p_out; |
603 | 0 | } |
604 | | |
605 | | /***************************************************************************** |
606 | | * CloseEncoder: encoder destruction |
607 | | *****************************************************************************/ |
608 | | static void CloseEncoder(encoder_t *p_enc) |
609 | 0 | { |
610 | 0 | encoder_sys_t *p_sys = p_enc->p_sys; |
611 | 0 | if (vpx_codec_destroy(&p_sys->ctx)) |
612 | 0 | VPX_ERR(&p_enc->obj, &p_sys->ctx, "Failed to destroy codec"); |
613 | 0 | free(p_sys); |
614 | 0 | } |
615 | | |
616 | | #endif /* ENABLE_SOUT */ |