input: add an input_item_t arg to input_CreateFilename()
[vlc.git] / modules / codec / oggspots.c
blob4f1a39f4e3d16b9f0d915a0b8055722279413d62
1 /*****************************************************************************
2 * oggspots.c: OggSpots decoder module.
3 *****************************************************************************
4 * Copyright (C) 2016 VLC authors and VideoLAN
5 * $Id$
7 * Authors: Michael Taenzer <neo@nhng.de>
9 * This program is free software; you can redistribute it and/or modify it
10 * under the terms of the GNU Lesser General Public License as published by
11 * the Free Software Foundation; either version 2.1 of the License, or
12 * (at your option) any later version.
14 * This program is distributed in the hope that it will be useful,
15 * but WITHOUT ANY WARRANTY; without even the implied warranty of
16 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
17 * GNU Lesser General Public License for more details.
19 * You should have received a copy of the GNU Lesser General Public License
20 * along with this program; if not, write to the Free Software Foundation,
21 * Inc., 51 Franklin Street, Fifth Floor, Boston MA 02110-1301, USA.
22 *****************************************************************************/
24 /*****************************************************************************
25 * Preamble
26 *****************************************************************************/
27 #ifdef HAVE_CONFIG_H
28 # include "config.h"
29 #endif
31 #include <vlc_common.h>
32 #include <vlc_plugin.h>
33 #include <vlc_codec.h>
34 #include <vlc_image.h>
36 #include <assert.h>
37 #include <limits.h>
39 /*****************************************************************************
40 * decoder_sys_t : oggspots decoder descriptor
41 *****************************************************************************/
42 typedef struct
44 /* Module mode */
45 bool b_packetizer;
48 * Input properties
50 bool b_has_headers;
53 * Image handler
55 image_handler_t* p_image;
58 * Common properties
60 vlc_tick_t i_pts;
61 } decoder_sys_t;
63 /*****************************************************************************
64 * Local prototypes
65 *****************************************************************************/
66 static int OpenDecoder (vlc_object_t*);
67 static int OpenPacketizer(vlc_object_t*);
68 static void CloseDecoder (vlc_object_t*);
70 static int DecodeVideo (decoder_t*, block_t*);
71 static block_t* Packetize (decoder_t*, block_t**);
72 static int ProcessHeader(decoder_t*);
73 static void* ProcessPacket(decoder_t*, block_t*);
74 static void Flush (decoder_t*);
75 static picture_t* DecodePacket (decoder_t*, block_t*);
78 /*****************************************************************************
79 * Module descriptor
80 *****************************************************************************/
82 vlc_module_begin ()
83 set_category(CAT_INPUT)
84 set_subcategory(SUBCAT_INPUT_VCODEC)
85 set_shortname("OggSpots")
86 set_description(N_("OggSpots video decoder"))
87 set_capability("video decoder", 10)
88 set_callbacks(OpenDecoder, CloseDecoder)
89 add_shortcut("oggspots")
91 add_submodule ()
92 set_description(N_("OggSpots video packetizer"))
93 set_capability("packetizer", 10)
94 set_callbacks(OpenPacketizer, CloseDecoder)
95 add_shortcut("oggspots")
96 vlc_module_end ()
98 static int OpenCommon(vlc_object_t* p_this, bool b_packetizer)
100 decoder_t* p_dec = (decoder_t*)p_this;
101 decoder_sys_t* p_sys;
103 if (p_dec->fmt_in.i_codec != VLC_CODEC_OGGSPOTS) {
104 return VLC_EGENERIC;
107 /* Allocate the memory needed to store the decoder's structure */
108 p_sys = malloc(sizeof(*p_sys));
109 if (p_sys == NULL) {
110 return VLC_ENOMEM;
112 p_dec->p_sys = p_sys;
113 p_sys->b_packetizer = b_packetizer;
114 p_sys->b_has_headers = false;
115 p_sys->i_pts = VLC_TICK_INVALID;
117 /* Initialize image handler */
118 p_sys->p_image = image_HandlerCreate(p_dec);
119 if (p_sys->p_image == NULL) {
120 free(p_sys);
121 return VLC_ENOMEM;
124 if( b_packetizer )
126 p_dec->fmt_out.i_codec = VLC_CODEC_OGGSPOTS;
127 p_dec->pf_packetize = Packetize;
129 else
131 p_dec->fmt_out.i_codec = VLC_CODEC_RGBA;
132 p_dec->pf_decode = DecodeVideo;
135 p_dec->pf_flush = Flush;
137 return VLC_SUCCESS;
140 /*****************************************************************************
141 * OpenDecoder: probe the decoder and return score
142 *****************************************************************************/
143 static int OpenDecoder(vlc_object_t* p_this)
145 return OpenCommon(p_this, false);
148 static int OpenPacketizer(vlc_object_t* p_this)
150 return OpenCommon(p_this, true);
153 /****************************************************************************
154 * DecodeBlock: the whole thing
155 ****************************************************************************
156 * This function must be fed with ogg packets.
157 ****************************************************************************/
158 static void* DecodeBlock(decoder_t* p_dec, block_t* p_block)
160 decoder_sys_t* p_sys = p_dec->p_sys;
162 /* Check for headers */
163 if (!p_sys->b_has_headers) {
164 if (ProcessHeader(p_dec)) {
165 block_Release(p_block);
166 return NULL;
168 p_sys->b_has_headers = true;
171 return ProcessPacket(p_dec, p_block);
174 static int DecodeVideo( decoder_t *p_dec, block_t *p_block )
176 if( p_block == NULL ) /* No Drain */
177 return VLCDEC_SUCCESS;
179 picture_t *p_pic = DecodeBlock( p_dec, p_block );
180 if( p_pic != NULL )
181 decoder_QueueVideo( p_dec, p_pic );
182 return VLCDEC_SUCCESS;
185 static block_t *Packetize( decoder_t *p_dec, block_t **pp_block )
187 if( pp_block == NULL ) /* No Drain */
188 return NULL;
189 block_t *p_block = *pp_block; *pp_block = NULL;
190 if( p_block == NULL )
191 return NULL;
192 return DecodeBlock( p_dec, p_block );
195 /*****************************************************************************
196 * ProcessHeader: process OggSpots header.
197 *****************************************************************************/
198 static int ProcessHeader(decoder_t* p_dec)
200 decoder_sys_t* p_sys = p_dec->p_sys;
201 const uint8_t* p_extra;
202 int i_major;
203 int i_minor;
204 uint64_t i_granulerate_numerator;
205 uint64_t i_granulerate_denominator;
207 /* The OggSpots header is always 52 bytes */
208 if (p_dec->fmt_in.i_extra != 52) {
209 return VLC_EGENERIC;
211 p_extra = p_dec->fmt_in.p_extra;
213 /* Identification string */
214 if ( memcmp(p_extra, "SPOTS\0\0", 8) ) {
215 return VLC_EGENERIC;
218 /* Version number */
219 i_major = GetWLE(&p_extra[ 8]); /* major version num */
220 i_minor = GetWLE(&p_extra[10]); /* minor version num */
221 if (i_major != 0 || i_minor != 1) {
222 return VLC_EGENERIC;
225 /* Granule rate */
226 i_granulerate_numerator = GetQWLE(&p_extra[12]);
227 i_granulerate_denominator = GetQWLE(&p_extra[20]);
228 if (i_granulerate_numerator == 0 || i_granulerate_denominator == 0) {
229 return VLC_EGENERIC;
232 /* The OggSpots spec contained an error and there are implementations out
233 * there that used the wrong value. So we detect that case and switch
234 * numerator and denominator in that case */
235 if (i_granulerate_numerator == 1 && i_granulerate_denominator == 30) {
236 i_granulerate_numerator = 30;
237 i_granulerate_denominator = 1;
240 /* Normalize granulerate */
241 vlc_ureduce(&p_dec->fmt_in.video.i_frame_rate,
242 &p_dec->fmt_in.video.i_frame_rate_base,
243 i_granulerate_numerator, i_granulerate_denominator, 0);
245 /* Image format */
246 if (!p_sys->b_packetizer) {
247 if ( memcmp(&p_extra[32], "PNG", 3) && memcmp(&p_extra[32], "JPEG", 4) ) {
248 char psz_image_type[8+1];
249 strncpy(psz_image_type, (char*)&p_extra[32], 8);
250 psz_image_type[sizeof(psz_image_type)-1] = '\0';
252 msg_Warn(p_dec, "Unsupported image format: %s", psz_image_type);
256 /* Dimensions */
257 p_dec->fmt_out.video.i_width = p_dec->fmt_out.video.i_visible_width =
258 GetWLE(&p_extra[40]);
259 p_dec->fmt_out.video.i_height = p_dec->fmt_out.video.i_visible_height =
260 GetWLE(&p_extra[42]);
262 /* We assume square pixels */
263 p_dec->fmt_out.video.i_sar_num = 1;
264 p_dec->fmt_out.video.i_sar_den = 1;
266 /* We don't implement background color, alignment and options at the
267 * moment because the former doesn't seem necessary right now and the
268 * latter are underspecified. */
270 if (p_sys->b_packetizer) {
271 void* p_extra = realloc(p_dec->fmt_out.p_extra,
272 p_dec->fmt_in.i_extra);
273 if (unlikely(p_extra == NULL)) {
274 return VLC_ENOMEM;
276 p_dec->fmt_out.p_extra = p_extra;
277 p_dec->fmt_out.i_extra = p_dec->fmt_in.i_extra;
278 memcpy(p_dec->fmt_out.p_extra,
279 p_dec->fmt_in.p_extra, p_dec->fmt_out.i_extra);
282 return VLC_SUCCESS;
285 /*****************************************************************************
286 * Flush:
287 *****************************************************************************/
288 static void Flush(decoder_t* p_dec)
290 decoder_sys_t* p_sys = p_dec->p_sys;
292 p_sys->i_pts = VLC_TICK_INVALID;
295 /*****************************************************************************
296 * ProcessPacket: processes an OggSpots packet.
297 *****************************************************************************/
298 static void* ProcessPacket(decoder_t* p_dec, block_t* p_block)
300 decoder_sys_t* p_sys = p_dec->p_sys;
301 void* p_buf;
303 if ( (p_block->i_flags & BLOCK_FLAG_DISCONTINUITY) != 0 ) {
304 p_sys->i_pts = p_block->i_pts;
307 if ( (p_block->i_flags & BLOCK_FLAG_CORRUPTED) != 0 ) {
308 block_Release(p_block);
309 return NULL;
312 /* Date management */
313 if (p_block->i_pts != VLC_TICK_INVALID && p_block->i_pts != p_sys->i_pts) {
314 p_sys->i_pts = p_block->i_pts;
317 if (p_sys->b_packetizer) {
318 /* Date management */
319 /* FIXME: This is copied from theora but it looks wrong.
320 * p_block->i_length will always be zero. */
321 p_block->i_dts = p_block->i_pts = p_sys->i_pts;
323 p_block->i_length = p_sys->i_pts - p_block->i_pts;
325 p_buf = p_block;
327 else {
328 p_buf = DecodePacket(p_dec, p_block);
331 return p_buf;
334 /*****************************************************************************
335 * DecodePacket: decodes an OggSpots packet.
336 *****************************************************************************/
337 static picture_t* DecodePacket(decoder_t* p_dec, block_t* p_block)
339 decoder_sys_t* p_sys = p_dec->p_sys;
340 uint32_t i_img_offset;
341 picture_t* p_pic;
343 if (p_block->i_buffer < 20) {
344 msg_Dbg(p_dec, "Packet too short");
345 goto error;
348 /* Byte offset */
349 i_img_offset = GetDWLE(p_block->p_buffer);
350 if (i_img_offset < 20) {
351 msg_Dbg(p_dec, "Invalid byte offset");
352 goto error;
355 /* Image format */
356 if ( !memcmp(&p_block->p_buffer[4], "PNG", 3) ) {
357 p_dec->fmt_in.video.i_chroma = VLC_CODEC_PNG;
359 else if ( !memcmp(&p_block->p_buffer[4], "JPEG", 4) ) {
360 p_dec->fmt_in.video.i_chroma = VLC_CODEC_JPEG;
362 else {
363 char psz_image_type[8+1];
364 strncpy(psz_image_type, (char*)&p_block->p_buffer[4], 8);
365 psz_image_type[sizeof(psz_image_type)-1] = '\0';
367 msg_Dbg(p_dec, "Unsupported image format: %s", psz_image_type);
368 goto error;
371 /* We currently ignore the rest of the header and let the image format
372 * handle the details */
374 p_block->i_buffer -= i_img_offset;
375 p_block->p_buffer += i_img_offset;
377 p_pic = image_Read(p_sys->p_image, p_block,
378 &p_dec->fmt_in.video,
379 &p_dec->fmt_out.video);
380 if (p_pic == NULL) {
381 return NULL;
384 p_pic->b_force = true;
385 p_dec->fmt_out.i_codec = p_dec->fmt_out.video.i_chroma;
386 decoder_UpdateVideoFormat(p_dec);
388 return p_pic;
390 error:
391 block_Release(p_block);
392 return NULL;
395 /*****************************************************************************
396 * CloseDecoder: OggSpots decoder destruction
397 *****************************************************************************/
398 static void CloseDecoder(vlc_object_t* p_this)
400 decoder_t* p_dec = (decoder_t*)p_this;
401 decoder_sys_t* p_sys = p_dec->p_sys;
403 image_HandlerDelete(p_sys->p_image);
404 free(p_sys);