82ae175725fdf242ed5a220cadb21624b44e5167
[vlc.git] / modules / demux / mjpeg.c
1 /*****************************************************************************
2  * mjpeg.c : demuxes mjpeg webcam http streams
3  *****************************************************************************
4  * Copyright (C) 2004 VLC authors and VideoLAN
5  *
6  * Authors: Henry Jen (slowhog) <henryjen@ztune.net>
7  *          Derk-Jan Hartman (thedj)
8  *          Sigmund Augdal (Dnumgis)
9  *          Laurent Aimar <fenrir@via.ecp.fr>
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 /*****************************************************************************
27  * Preamble
28  *****************************************************************************/
29
30 #ifdef HAVE_CONFIG_H
31 # include "config.h"
32 #endif
33
34 #include <vlc_common.h>
35 #include <vlc_plugin.h>
36 #include <vlc_demux.h>
37 #include "mxpeg_helper.h"
38
39 /*****************************************************************************
40  * Module descriptor
41  *****************************************************************************/
42 static int  Open ( vlc_object_t * );
43
44 #define FPS_TEXT N_("Frames per Second")
45 #define FPS_LONGTEXT N_("This is the desired frame rate when " \
46     "playing MJPEG from a file. Use 0 (this is the default value) for a " \
47     "live stream (from a camera).")
48
49 vlc_module_begin ()
50     set_shortname( "MJPEG")
51     set_description( N_("M-JPEG camera demuxer") )
52     set_capability( "demux", 5 )
53     set_callback( Open )
54     set_category( CAT_INPUT )
55     set_subcategory( SUBCAT_INPUT_DEMUX )
56     add_float( "mjpeg-fps", 0.0, FPS_TEXT, FPS_LONGTEXT, false )
57 vlc_module_end ()
58
59 /*****************************************************************************
60  * Local prototypes
61  *****************************************************************************/
62 static int MimeDemux( demux_t * );
63 static int MjpgDemux( demux_t * );
64 static int Control( demux_t *, int i_query, va_list args );
65
66 typedef struct
67 {
68     es_format_t     fmt;
69     es_out_id_t     *p_es;
70
71     bool            b_still;
72     vlc_tick_t      i_still_end;
73     vlc_tick_t      i_time;
74     vlc_tick_t      i_frame_length;
75     char            *psz_separator;
76     int             i_frame_size_estimate;
77     const uint8_t   *p_peek;
78     int             i_data_peeked;
79     int             i_level;
80 } demux_sys_t;
81
82 /*****************************************************************************
83  * Peek: Helper function to peek data with incremental size.
84  * \return false if peek no more data, true otherwise.
85  *****************************************************************************/
86 static bool Peek( demux_t *p_demux, bool b_first )
87 {
88     int i_data;
89     demux_sys_t *p_sys = p_demux->p_sys;
90
91     if( b_first )
92     {
93         p_sys->i_data_peeked = 0;
94     }
95     else if( p_sys->i_data_peeked == p_sys->i_frame_size_estimate )
96     {
97         p_sys->i_frame_size_estimate += 5120;
98     }
99     i_data = vlc_stream_Peek( p_demux->s, &p_sys->p_peek,
100                           p_sys->i_frame_size_estimate );
101     if( i_data == p_sys->i_data_peeked )
102     {
103         msg_Warn( p_demux, "no more data" );
104         return false;
105     }
106     p_sys->i_data_peeked = i_data;
107     if( i_data <= 0 )
108     {
109         msg_Warn( p_demux, "cannot peek data" );
110         return false;
111     }
112     return true;
113 }
114
115 /*****************************************************************************
116  * GetLine: Internal function used to dup a line of string from the buffer
117  *****************************************************************************/
118 static char* GetLine( demux_t *p_demux, int *p_pos )
119 {
120     demux_sys_t *p_sys = p_demux->p_sys;
121     const uint8_t *p_buf;
122     int         i_size;
123     int         i;
124     char        *p_line;
125
126     while( *p_pos >= p_sys->i_data_peeked )
127     {
128         if( ! Peek( p_demux, false ) )
129         {
130             return NULL;
131         }
132     }
133     p_buf = p_sys->p_peek + *p_pos;
134     i_size = p_sys->i_data_peeked - *p_pos;
135     i = 0;
136     while( p_buf[i] != '\n' )
137     {
138         i++;
139         if( i == i_size )
140         {
141             if( ! Peek( p_demux, false ) )
142             {
143                 return NULL;
144             }
145             p_buf = p_sys->p_peek + *p_pos;
146             i_size = p_sys->i_data_peeked - *p_pos;
147         }
148     }
149     *p_pos += i + 1;
150     if( i > 0 && p_buf[i - 1] == '\r' )
151     {
152         i--;
153     }
154     p_line = vlc_obj_malloc( VLC_OBJECT(p_demux), i + 1 );
155     if( unlikely( p_line == NULL ) )
156         return NULL;
157     strncpy ( p_line, (char*)p_buf, i );
158     p_line[i] = '\0';
159     return p_line;
160 }
161
162 /*****************************************************************************
163  * CheckMimeHeader: Internal function used to verify and skip mime header
164  * \param p_header_size Return size of MIME header, 0 if no MIME header
165  * detected, minus value if error
166  * \return true if content type is image/jpeg, false otherwise
167  *****************************************************************************/
168 static bool CheckMimeHeader( demux_t *p_demux, int *p_header_size )
169 {
170     bool        b_jpeg = false;
171     int         i_pos = 0;
172     char        *psz_line;
173     char        *p_ch;
174     demux_sys_t *p_sys = p_demux->p_sys;
175
176     *p_header_size = -1;
177     if( !Peek( p_demux, true ) )
178     {
179         msg_Err( p_demux, "cannot peek" );
180         return false;
181     }
182     if( p_sys->i_data_peeked < 5)
183     {
184         msg_Err( p_demux, "data shortage" );
185         return false;
186     }
187     if( strncmp( (char *)p_sys->p_peek, "--", 2 ) != 0
188         && strncmp( (char *)p_sys->p_peek, "\r\n--", 4 ) != 0 )
189     {
190         *p_header_size = 0;
191         return false;
192     }
193     else
194     {
195         i_pos = *p_sys->p_peek == '-' ? 2 : 4;
196         psz_line = GetLine( p_demux, &i_pos );
197         if( NULL == psz_line )
198         {
199             msg_Err( p_demux, "no EOL" );
200             return false;
201         }
202
203         /* Read the separator and remember it if not yet stored */
204         if( p_sys->psz_separator == NULL )
205         {
206             p_sys->psz_separator = psz_line;
207             msg_Dbg( p_demux, "Multipart MIME detected, using separator: %s",
208                      p_sys->psz_separator );
209         }
210         else
211         {
212             if( strcmp( psz_line, p_sys->psz_separator ) )
213             {
214                 msg_Warn( p_demux, "separator %s does not match %s", psz_line,
215                           p_sys->psz_separator );
216             }
217             vlc_obj_free( VLC_OBJECT(p_demux), psz_line );
218         }
219     }
220
221     psz_line = GetLine( p_demux, &i_pos );
222     while( psz_line && *psz_line )
223     {
224         if( !strncasecmp( psz_line, "Content-Type:", 13 ) )
225         {
226             p_ch = psz_line + 13;
227             while( *p_ch != '\0' && ( *p_ch == ' ' || *p_ch == '\t' ) ) p_ch++;
228             if( strncasecmp( p_ch, "image/jpeg", 10 ) )
229             {
230                 msg_Warn( p_demux, "%s, image/jpeg is expected", psz_line );
231                 b_jpeg = false;
232             }
233             else
234             {
235                 b_jpeg = true;
236             }
237         }
238         else
239         {
240             msg_Dbg( p_demux, "discard MIME header: %s", psz_line );
241         }
242         vlc_obj_free( VLC_OBJECT(p_demux), psz_line );
243         psz_line = GetLine( p_demux, &i_pos );
244     }
245
246     if( NULL == psz_line )
247     {
248         msg_Err( p_demux, "no EOL" );
249         return false;
250     }
251
252     vlc_obj_free( VLC_OBJECT(p_demux), psz_line );
253
254     *p_header_size = i_pos;
255     return b_jpeg;
256 }
257
258 static int SendBlock( demux_t *p_demux, int i )
259 {
260     demux_sys_t *p_sys = p_demux->p_sys;
261     block_t     *p_block;
262
263     if( ( p_block = vlc_stream_Block( p_demux->s, i ) ) == NULL )
264     {
265         msg_Warn( p_demux, "cannot read data" );
266         return VLC_DEMUXER_EOF;
267     }
268
269     if( p_sys->i_frame_length != VLC_TICK_INVALID )
270     {
271         p_block->i_pts = p_sys->i_time;
272         p_sys->i_time += p_sys->i_frame_length;
273     }
274     else
275     {
276         p_block->i_pts = vlc_tick_now();
277     }
278     p_block->i_dts = p_block->i_pts;
279
280     es_out_SetPCR( p_demux->out, p_block->i_pts );
281     es_out_Send( p_demux->out, p_sys->p_es, p_block );
282
283     if( p_sys->b_still )
284         p_sys->i_still_end = vlc_tick_now() + p_sys->i_frame_length;
285
286     return VLC_DEMUXER_SUCCESS;
287 }
288
289 /*****************************************************************************
290  * Open: check file and initializes structures
291  *****************************************************************************/
292 static int Open( vlc_object_t * p_this )
293 {
294     demux_t     *p_demux = (demux_t*)p_this;
295     int         i_size;
296     bool        b_matched = false;
297
298     if( IsMxpeg( p_demux->s ) && !p_demux->obj.force )
299         // let avformat handle this case
300         return VLC_EGENERIC;
301
302     demux_sys_t *p_sys = vlc_obj_malloc( p_this, sizeof (*p_sys) );
303     if( unlikely(p_sys == NULL) )
304         return VLC_ENOMEM;
305
306     p_demux->p_sys      = p_sys;
307     p_sys->p_es         = NULL;
308     p_sys->i_time       = VLC_TICK_0;
309     p_sys->i_level      = 0;
310
311     p_sys->psz_separator = NULL;
312     p_sys->i_frame_size_estimate = 15 * 1024;
313
314     char *content_type = stream_ContentType( p_demux->s );
315     if ( content_type )
316     {
317         //FIXME: this is not fully match to RFC
318         char* boundary = strstr( content_type, "boundary=" );
319         if( boundary )
320         {
321             boundary += strlen( "boundary=" );
322             size_t len = strlen( boundary );
323             if( len > 2 && boundary[0] == '"'
324                 && boundary[len-1] == '"' )
325             {
326                 boundary[len-1] = '\0';
327                 boundary++;
328             }
329             p_sys->psz_separator = vlc_obj_strdup( p_this, boundary );
330             if( !p_sys->psz_separator )
331             {
332                 free( content_type );
333                 return VLC_ENOMEM;
334             }
335         }
336         free( content_type );
337     }
338
339     b_matched = CheckMimeHeader( p_demux, &i_size);
340     if( b_matched )
341     {
342         p_demux->pf_demux = MimeDemux;
343         if( vlc_stream_Read( p_demux->s, NULL, i_size ) < i_size )
344             return VLC_EGENERIC;
345     }
346     else if( i_size == 0 )
347     {
348         /* 0xffd8 identify a JPEG SOI */
349         if( p_sys->p_peek[0] == 0xFF && p_sys->p_peek[1] == 0xD8 )
350         {
351             msg_Dbg( p_demux, "JPEG SOI marker detected" );
352             p_demux->pf_demux = MjpgDemux;
353             p_sys->i_level++;
354         }
355         else
356         {
357             return VLC_EGENERIC;
358         }
359     }
360     else
361     {
362         return VLC_EGENERIC;
363     }
364
365     /* Frame rate */
366     float f_fps = var_InheritFloat( p_demux, "mjpeg-fps" );
367
368     p_sys->i_still_end = VLC_TICK_INVALID;
369     if( demux_IsPathExtension( p_demux, ".jpeg" ) ||
370         demux_IsPathExtension( p_demux, ".jpg" ) )
371     {
372         /* Plain JPEG file = single still picture */
373         p_sys->b_still = true;
374         if( f_fps == 0.f )
375             /* Defaults to 1fps */
376             f_fps = 1.f;
377     }
378     else
379         p_sys->b_still = false;
380     p_sys->i_frame_length = f_fps ? vlc_tick_rate_duration(f_fps) : VLC_TICK_INVALID;
381
382     es_format_Init( &p_sys->fmt, VIDEO_ES, VLC_CODEC_MJPG );
383
384     p_sys->fmt.i_id = 0;
385     p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt );
386     if( unlikely(p_sys->p_es == NULL) )
387         return VLC_ENOMEM;
388
389     p_demux->pf_control = Control;
390     return VLC_SUCCESS;
391 }
392
393 /*****************************************************************************
394  * Demux: read packet and send them to decoders
395  *****************************************************************************
396  * Returns -1 in case of error, 0 in case of EOF, 1 otherwise
397  *****************************************************************************/
398 static int MjpgDemux( demux_t *p_demux )
399 {
400     demux_sys_t *p_sys = p_demux->p_sys;
401     int i;
402
403     if( p_sys->b_still && p_sys->i_still_end != VLC_TICK_INVALID )
404     {
405         /* Still frame, wait until the pause delay is gone */
406         vlc_tick_wait( p_sys->i_still_end );
407         p_sys->i_still_end = VLC_TICK_INVALID;
408         return VLC_DEMUXER_SUCCESS;
409     }
410
411     if( !Peek( p_demux, true ) )
412     {
413         msg_Warn( p_demux, "cannot peek data" );
414         return VLC_DEMUXER_EOF;
415     }
416     if( p_sys->i_data_peeked < 4 )
417     {
418         msg_Warn( p_demux, "data shortage" );
419         return VLC_DEMUXER_EOF;
420     }
421     i = 3;
422 FIND_NEXT_EOI:
423     while( !( p_sys->p_peek[i-1] == 0xFF && p_sys->p_peek[i] == 0xD9 ) )
424     {
425         if( p_sys->p_peek[i-1] == 0xFF && p_sys->p_peek[i] == 0xD9  )
426         {
427             p_sys->i_level++;
428             msg_Dbg( p_demux, "we found another JPEG SOI at %d", i );
429         }
430         i++;
431         if( i >= p_sys->i_data_peeked )
432         {
433             msg_Dbg( p_demux, "did not find JPEG EOI in %d bytes",
434                      p_sys->i_data_peeked );
435             if( !Peek( p_demux, false ) )
436             {
437                 msg_Warn( p_demux, "no more data is available at the moment" );
438                 return VLC_DEMUXER_EOF;
439             }
440         }
441     }
442     i++;
443
444     msg_Dbg( p_demux, "JPEG EOI detected at %d", i );
445     p_sys->i_level--;
446
447     if( p_sys->i_level > 0 )
448         goto FIND_NEXT_EOI;
449     return SendBlock( p_demux, i );
450 }
451
452 static int MimeDemux( demux_t *p_demux )
453 {
454     demux_sys_t *p_sys = p_demux->p_sys;
455     int         i_size, i;
456
457     bool  b_match = CheckMimeHeader( p_demux, &i_size );
458
459     if( i_size > 0 )
460     {
461         if( vlc_stream_Read( p_demux->s, NULL, i_size ) != i_size )
462             return VLC_DEMUXER_EOF;
463     }
464     else if( i_size < 0 )
465     {
466         return VLC_DEMUXER_EOF;
467     }
468     else
469     {
470         // No MIME header, assume OK
471         b_match = true;
472     }
473
474     if( !Peek( p_demux, true ) )
475     {
476         msg_Warn( p_demux, "cannot peek data" );
477         return VLC_DEMUXER_EOF;
478     }
479
480     i = 0;
481     i_size = strlen( p_sys->psz_separator ) + 2;
482     if( p_sys->i_data_peeked < i_size )
483     {
484         msg_Warn( p_demux, "data shortage" );
485         return VLC_DEMUXER_EOF;
486     }
487
488     for( ;; )
489     {
490         while( !( p_sys->p_peek[i] == '-' && p_sys->p_peek[i+1] == '-' ) )
491         {
492             i++;
493             i_size++;
494             if( i_size >= p_sys->i_data_peeked )
495             {
496                 msg_Dbg( p_demux, "MIME boundary not found in %d bytes of "
497                          "data", p_sys->i_data_peeked );
498
499                 if( !Peek( p_demux, false ) )
500                 {
501                     msg_Warn( p_demux, "no more data is available at the "
502                               "moment" );
503                     return VLC_DEMUXER_EOF;
504                 }
505             }
506         }
507
508         /* Handle old and new style of separators */
509         if (!strncmp(p_sys->psz_separator, (char *)(p_sys->p_peek + i + 2),
510                      strlen( p_sys->psz_separator ))
511          || ((strlen(p_sys->psz_separator) > 4)
512           && !strncmp(p_sys->psz_separator, "--", 2)
513           && !strncmp(p_sys->psz_separator, (char *)(p_sys->p_peek + i),
514                       strlen( p_sys->psz_separator))))
515         {
516             break;
517         }
518
519         i++;
520         i_size++;
521     }
522
523     if( !b_match )
524     {
525         msg_Err( p_demux, "discard non-JPEG part" );
526         return VLC_DEMUXER_EOF;
527     }
528
529     return SendBlock( p_demux, i );
530 }
531
532 /*****************************************************************************
533  * Control:
534  *****************************************************************************/
535 static int Control( demux_t *p_demux, int i_query, va_list args )
536 {
537     return demux_vaControlHelper( p_demux->s, 0, 0, 0, 0, i_query, args );
538 }