00001
00002
00003
00004
00005
00006
00007
00008
00009
00010
00011
00012
00013
00014
00015
00016
00017
00018
00019
00020
00021
00022
00023
00024
00025
00026
00027
00028
00029
00030 #include <stdlib.h>
00031
00032 #include <vlc/vlc.h>
00033 #include <vlc/input.h>
00034
00035 #include <codecs.h>
00036
00037
00038
00039
00040 static int Open ( vlc_object_t * );
00041 static void Close( vlc_object_t * );
00042
00043 #define FPS_TEXT N_("Frames per Second")
00044 #define FPS_LONGTEXT N_("Allows you to set the desired frame rate when " \
00045 "playing from files, use 0 for live.")
00046
00047 vlc_module_begin();
00048 set_shortname( "MJPEG");
00049 set_description( _("JPEG camera demuxer") );
00050 set_capability( "demux2", 5 );
00051 set_callbacks( Open, Close );
00052 set_category( CAT_INPUT );
00053 set_subcategory( SUBCAT_INPUT_DEMUX );
00054 add_float( "mjpeg-fps", 0.0, NULL, FPS_TEXT, FPS_LONGTEXT, VLC_FALSE );
00055 vlc_module_end();
00056
00057
00058
00059
00060 static int MimeDemux( demux_t * );
00061 static int MjpgDemux( demux_t * );
00062 static int Control( demux_t *, int i_query, va_list args );
00063
00064 struct demux_sys_t
00065 {
00066 es_format_t fmt;
00067 es_out_id_t *p_es;
00068
00069 vlc_bool_t b_still;
00070 mtime_t i_still_end;
00071 mtime_t i_still_length;
00072
00073 mtime_t i_time;
00074 mtime_t i_frame_length;
00075 char *psz_separator;
00076 int i_frame_size_estimate;
00077 uint8_t *p_peek;
00078 int i_data_peeked;
00079 };
00080
00081
00082
00083
00084
00085 static vlc_bool_t Peek( demux_t *p_demux, vlc_bool_t b_first )
00086 {
00087 int i_data;
00088 demux_sys_t *p_sys = p_demux->p_sys;
00089
00090 if( b_first )
00091 {
00092 p_sys->i_data_peeked = 0;
00093 }
00094 else if( p_sys->i_data_peeked == p_sys->i_frame_size_estimate )
00095 {
00096 p_sys->i_frame_size_estimate += 5120;
00097 }
00098 i_data = stream_Peek( p_demux->s, &p_sys->p_peek,
00099 p_sys->i_frame_size_estimate );
00100 if( i_data == p_sys->i_data_peeked )
00101 {
00102 msg_Warn( p_demux, "no more data" );
00103 return VLC_FALSE;
00104 }
00105 p_sys->i_data_peeked = i_data;
00106 if( i_data <= 0 )
00107 {
00108 msg_Warn( p_demux, "cannot peek data" );
00109 return VLC_FALSE;
00110 }
00111 return VLC_TRUE;
00112 }
00113
00114
00115
00116
00117 static char* GetLine( demux_t *p_demux, int *p_pos )
00118 {
00119 demux_sys_t *p_sys = p_demux->p_sys;
00120 uint8_t *p_buf;
00121 int i_size;
00122 int i;
00123 char *p_line;
00124
00125 while( *p_pos > p_sys->i_data_peeked )
00126 {
00127 if( ! Peek( p_demux, VLC_FALSE ) )
00128 {
00129 return NULL;
00130 }
00131 }
00132 p_buf = p_sys->p_peek + *p_pos;
00133 i_size = p_sys->i_data_peeked - *p_pos;
00134 i = 0;
00135 while( p_buf[i] != '\n' )
00136 {
00137 i++;
00138 if( i == i_size )
00139 {
00140 if( ! Peek( p_demux, VLC_FALSE ) )
00141 {
00142 return NULL;
00143 }
00144 }
00145 p_buf = p_sys->p_peek + *p_pos;
00146 i_size = p_sys->i_data_peeked - *p_pos;
00147 }
00148 *p_pos += ( i + 1 );
00149 if( i > 0 && '\r' == p_buf[i - 1] )
00150 {
00151 i--;
00152 }
00153 p_line = malloc( i + 1 );
00154 if( NULL == p_line )
00155 {
00156 msg_Err( p_demux, "out of memory" );
00157 return NULL;
00158 }
00159 strncpy ( p_line, p_buf, i );
00160 p_line[i] = '\0';
00161
00162 return p_line;
00163 }
00164
00165
00166
00167
00168
00169
00170
00171 static vlc_bool_t CheckMimeHeader( demux_t *p_demux, int *p_header_size )
00172 {
00173 vlc_bool_t b_jpeg = VLC_FALSE;
00174 int i_pos;
00175 char *psz_line;
00176 char *p_ch;
00177 demux_sys_t *p_sys = p_demux->p_sys;
00178
00179 if( !Peek( p_demux, VLC_TRUE ) )
00180 {
00181 msg_Err( p_demux, "cannot peek" );
00182 *p_header_size = -1;
00183 return VLC_FALSE;
00184 }
00185 if( p_sys->i_data_peeked < 3)
00186 {
00187 msg_Err( p_demux, "data shortage" );
00188 *p_header_size = -2;
00189 return VLC_FALSE;
00190 }
00191 if( strncmp( (char *)p_sys->p_peek, "--", 2 ) )
00192 {
00193 *p_header_size = 0;
00194 return VLC_FALSE;
00195 }
00196 i_pos = 2;
00197 psz_line = GetLine( p_demux, &i_pos );
00198 if( NULL == psz_line )
00199 {
00200 msg_Err( p_demux, "no EOL" );
00201 *p_header_size = -3;
00202 return VLC_FALSE;
00203 }
00204 if( NULL == p_sys->psz_separator )
00205 {
00206 p_sys->psz_separator = psz_line;
00207 msg_Dbg( p_demux, "Multipart MIME detected, using separator: %s",
00208 p_sys->psz_separator );
00209 }
00210 else
00211 {
00212 if( strcmp( psz_line, p_sys->psz_separator ) )
00213 {
00214 msg_Warn( p_demux, "separator %s does not match %s", psz_line,
00215 p_sys->psz_separator );
00216 }
00217 free( psz_line );
00218 }
00219 psz_line = GetLine( p_demux, &i_pos );
00220 while( psz_line && *psz_line )
00221 {
00222 if( !strncasecmp( psz_line, "Content-Type:", 13 ) )
00223 {
00224 p_ch = psz_line + 13;
00225 while( *p_ch != '\0' && ( *p_ch == ' ' || *p_ch == '\t' ) ) p_ch++;
00226 if( strncasecmp( p_ch, "image/jpeg", 10 ) )
00227 {
00228 msg_Warn( p_demux, "%s, image/jpeg is expected", psz_line );
00229 b_jpeg = VLC_FALSE;
00230 }
00231 else
00232 {
00233 b_jpeg = VLC_TRUE;
00234 }
00235 }
00236 else
00237 {
00238 msg_Dbg( p_demux, "Discard MIME header: %s", psz_line );
00239 }
00240 free( psz_line );
00241 psz_line = GetLine( p_demux, &i_pos );
00242 }
00243
00244 if( NULL == psz_line )
00245 {
00246 msg_Err( p_demux, "no EOL" );
00247 *p_header_size = -3;
00248 return VLC_FALSE;
00249 }
00250
00251 free( psz_line );
00252
00253 *p_header_size = i_pos;
00254 return b_jpeg;
00255 }
00256
00257 static int SendBlock( demux_t *p_demux, int i )
00258 {
00259 demux_sys_t *p_sys = p_demux->p_sys;
00260 block_t *p_block;
00261
00262 if( ( p_block = stream_Block( p_demux->s, i ) ) == NULL )
00263 {
00264 msg_Warn( p_demux, "cannot read data" );
00265 return 0;
00266 }
00267
00268 if( !p_sys->i_frame_length || !p_sys->i_time )
00269 {
00270 p_sys->i_time = p_block->i_dts = p_block->i_pts = mdate();
00271 }
00272 else
00273 {
00274 p_block->i_dts = p_block->i_pts = p_sys->i_time;
00275 p_sys->i_time += p_sys->i_frame_length;
00276 }
00277
00278
00279 es_out_Control( p_demux->out, ES_OUT_SET_PCR, p_block->i_pts );
00280 es_out_Send( p_demux->out, p_sys->p_es, p_block );
00281
00282 if( p_sys->b_still )
00283 {
00284 p_sys->i_still_end = mdate() + p_sys->i_still_length;
00285 }
00286
00287 return 1;
00288 }
00289
00290
00291
00292
00293 static int Open( vlc_object_t * p_this )
00294 {
00295 demux_t *p_demux = (demux_t*)p_this;
00296 demux_sys_t *p_sys;
00297 int i_size;
00298 int b_matched = VLC_FALSE;
00299 vlc_value_t val;
00300 char *psz_ext;
00301
00302 p_demux->pf_control = Control;
00303 p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) );
00304 p_sys->p_es = NULL;
00305 p_sys->i_time = 0;
00306
00307 p_sys->psz_separator = NULL;
00308 p_sys->i_frame_size_estimate = 15 * 1024;
00309
00310 b_matched = CheckMimeHeader( p_demux, &i_size);
00311 if( b_matched )
00312 {
00313 p_demux->pf_demux = MimeDemux;
00314 stream_Read( p_demux->s, NULL, i_size );
00315 }
00316 else if( 0 == i_size )
00317 {
00318
00319 if( p_sys->p_peek[0] == 0xFF && p_sys->p_peek[1] == 0xD8 )
00320 {
00321 msg_Dbg( p_demux, "JPEG SOI marker detected" );
00322 p_demux->pf_demux = MjpgDemux;
00323 }
00324 else
00325 {
00326 goto error;
00327 }
00328 }
00329 else
00330 {
00331 goto error;
00332 }
00333
00334
00335 var_Create( p_demux, "mjpeg-fps", VLC_VAR_FLOAT | VLC_VAR_DOINHERIT );
00336 var_Get( p_demux, "mjpeg-fps", &val );
00337 p_sys->i_frame_length = 0;
00338
00339
00340 p_sys->b_still = VLC_FALSE;
00341 p_sys->i_still_end = 0;
00342 psz_ext = strrchr( p_demux->psz_path, '.' );
00343 if( psz_ext && ( !strcasecmp( psz_ext, ".jpeg" ) ||
00344 !strcasecmp( psz_ext, ".jpg" ) ) )
00345 {
00346 p_sys->b_still = VLC_TRUE;
00347 if( val.f_float)
00348 {
00349 p_sys->i_still_length =1000000.0 / val.f_float;
00350 }
00351 else
00352 {
00353
00354 p_sys->i_still_length = 1000000;
00355 }
00356 }
00357 else if ( val.f_float )
00358 {
00359 p_sys->i_frame_length = 1000000.0 / val.f_float;
00360 }
00361
00362 es_format_Init( &p_sys->fmt, VIDEO_ES, 0 );
00363 p_sys->fmt.i_codec = VLC_FOURCC('m','j','p','g');
00364
00365 p_sys->p_es = es_out_Add( p_demux->out, &p_sys->fmt );
00366 return VLC_SUCCESS;
00367
00368 error:
00369 free( p_sys );
00370 return VLC_EGENERIC;
00371 }
00372
00373
00374
00375
00376
00377
00378 static int MjpgDemux( demux_t *p_demux )
00379 {
00380 demux_sys_t *p_sys = p_demux->p_sys;
00381 int i;
00382
00383 if( p_sys->b_still && p_sys->i_still_end && p_sys->i_still_end < mdate() )
00384 {
00385
00386 p_sys->i_still_end = 0;
00387 }
00388 else if( p_sys->b_still && p_sys->i_still_end )
00389 {
00390 msleep( 400 );
00391 return 1;
00392 }
00393
00394 if( !Peek( p_demux, VLC_TRUE ) )
00395 {
00396 msg_Warn( p_demux, "cannot peek data" );
00397 return 0;
00398 }
00399 if( p_sys->i_data_peeked < 4 )
00400 {
00401 msg_Warn( p_demux, "data shortage" );
00402 return 0;
00403 }
00404 i = 3;
00405 while( !( 0xFF == p_sys->p_peek[i-1] && 0xD9 == p_sys->p_peek[i] ) )
00406 {
00407 i++;
00408 if( i >= p_sys->i_data_peeked )
00409 {
00410 msg_Dbg( p_demux, "Did not find JPEG EOI in %d bytes",
00411 p_sys->i_data_peeked );
00412 if( !Peek( p_demux, VLC_FALSE ) )
00413 {
00414 msg_Warn( p_demux, "No more data is available at the moment" );
00415 return 0;
00416 }
00417 }
00418 }
00419 i++;
00420
00421 msg_Dbg( p_demux, "JPEG EOI detected at %d", i );
00422 return SendBlock( p_demux, i );
00423 }
00424
00425 static int MimeDemux( demux_t *p_demux )
00426 {
00427 demux_sys_t *p_sys = p_demux->p_sys;
00428 int i_size, i;
00429 vlc_bool_t b_match;
00430 vlc_bool_t b_done;
00431
00432 b_match = CheckMimeHeader( p_demux, &i_size );
00433 if( i_size > 0 )
00434 {
00435 stream_Read( p_demux->s, NULL, i_size );
00436 }
00437 else if( i_size < 0 )
00438 {
00439 return 0;
00440 }
00441 else
00442 {
00443
00444 b_match = VLC_TRUE;
00445 }
00446
00447 if( !Peek( p_demux, VLC_TRUE ) )
00448 {
00449 msg_Warn( p_demux, "cannot peek data" );
00450 return 0;
00451 }
00452 i = 0;
00453 i_size = strlen( p_sys->psz_separator ) + 2;
00454 if( p_sys->i_data_peeked < i_size )
00455 {
00456 msg_Warn( p_demux, "data shortage" );
00457 return 0;
00458 }
00459 b_done = VLC_FALSE;
00460 while( !b_done )
00461 {
00462 while( !( p_sys->p_peek[i] == '-' && p_sys->p_peek[i+1] == '-' ) )
00463 {
00464 i++;
00465 i_size++;
00466 if( i_size >= p_sys->i_data_peeked )
00467 {
00468 msg_Dbg( p_demux, "MIME boundary not found in %d bytes of "
00469 "data", p_sys->i_data_peeked );
00470
00471 if( !Peek( p_demux, VLC_FALSE ) )
00472 {
00473 msg_Warn( p_demux, "No more data is available at the "
00474 "moment" );
00475 return 0;
00476 }
00477 }
00478 }
00479 if( !strncmp( p_sys->psz_separator, (char *)(p_sys->p_peek + i + 2),
00480 strlen( p_sys->psz_separator ) ) )
00481 {
00482 b_done = VLC_TRUE;
00483 }
00484 else
00485 {
00486 i++;
00487 i_size++;
00488 }
00489 }
00490
00491 if( !b_match )
00492 {
00493 msg_Err( p_demux, "Discard non-JPEG part" );
00494 stream_Read( p_demux->s, NULL, i );
00495 return 0;
00496 }
00497
00498 return SendBlock( p_demux, i );
00499 }
00500
00501
00502
00503
00504 static void Close ( vlc_object_t * p_this )
00505 {
00506 demux_t *p_demux = (demux_t*)p_this;
00507 demux_sys_t *p_sys = p_demux->p_sys;
00508
00509 if( p_sys->psz_separator )
00510 {
00511 free( p_sys->psz_separator );
00512 }
00513 free( p_sys );
00514 }
00515
00516
00517
00518
00519 static int Control( demux_t *p_demux, int i_query, va_list args )
00520 {
00521 return demux2_vaControlHelper( p_demux->s, 0, 0, 0, 0, i_query, args );
00522 }