static int Import_Dir( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t *)p_this; if( vlc_stream_Control( p_demux->s, STREAM_IS_DIRECTORY ) ) return VLC_EGENERIC; p_demux->pf_demux = Demux; p_demux->pf_control = Control; return VLC_SUCCESS; }
static int Control(access_t *access, int query, va_list args) { access_sys_t *sys = access->p_sys; stream_t *s = sys->s; if (!s) return VLC_EGENERIC; switch (query) { case STREAM_CAN_SEEK: { bool *b = va_arg(args, bool *); return vlc_stream_Control(s, STREAM_CAN_SEEK, b); } case STREAM_CAN_FASTSEEK: { bool *b = va_arg(args, bool *); return vlc_stream_Control(s, STREAM_CAN_FASTSEEK, b); } /* FIXME the following request should ask the underlying access object */ case STREAM_CAN_PAUSE: case STREAM_CAN_CONTROL_PACE: { bool *b = va_arg(args, bool *); *b = true; return VLC_SUCCESS; } case STREAM_GET_SIZE: *va_arg(args, uint64_t *) = sys->file->size; return VLC_SUCCESS; case STREAM_GET_PTS_DELAY: { int64_t *delay = va_arg(args, int64_t *); *delay = DEFAULT_PTS_DELAY; return VLC_SUCCESS; } case STREAM_SET_PAUSE_STATE: return VLC_SUCCESS; default: return VLC_EGENERIC; } }
int AVI_ChunkReadRoot( stream_t *s, avi_chunk_t *p_root ) { avi_chunk_list_t *p_list = (avi_chunk_list_t*)p_root; avi_chunk_t *p_chk; bool b_seekable; vlc_stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); p_list->i_chunk_pos = 0; p_list->i_chunk_size = stream_Size( s ); p_list->i_chunk_fourcc = AVIFOURCC_LIST; p_list->p_father = NULL; p_list->p_next = NULL; p_list->p_first = NULL; p_list->p_last = NULL; p_list->i_type = VLC_FOURCC( 'r', 'o', 'o', 't' ); for( ; ; ) { p_chk = xmalloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( !p_root->common.p_first ) { p_root->common.p_first = p_chk; } else { p_root->common.p_last->common.p_next = p_chk; } p_root->common.p_last = p_chk; if( AVI_ChunkRead( s, p_chk, p_root ) || ( vlc_stream_Tell( s ) >= (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found first RIFF-AVI */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_RIFF && p_chk->list.i_type == AVIFOURCC_AVI && !b_seekable ) { break; } } AVI_ChunkDumpDebug_level( (vlc_object_t*)s, p_root, 0 ); return VLC_SUCCESS; }
/* Allow to append indexes after starting playback */ int AVI_ChunkFetchIndexes( stream_t *s, avi_chunk_t *p_riff ) { avi_chunk_t *p_movi = AVI_ChunkFind( p_riff, AVIFOURCC_movi, 0 ); if ( !p_movi ) return VLC_EGENERIC; avi_chunk_t *p_chk; uint64_t i_indexpos = 8 + p_movi->common.i_chunk_pos + p_movi->common.i_chunk_size; bool b_seekable = false; int i_ret = VLC_SUCCESS; vlc_stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); if ( !b_seekable || vlc_stream_Seek( s, i_indexpos ) ) return VLC_EGENERIC; for( ; ; ) { p_chk = xmalloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if (unlikely( !p_riff->common.p_first )) p_riff->common.p_first = p_chk; else p_riff->common.p_last->common.p_next = p_chk; p_riff->common.p_last = p_chk; i_ret = AVI_ChunkRead( s, p_chk, p_riff ); if( i_ret ) break; if( p_chk->common.p_father->common.i_chunk_size > 0 && ( vlc_stream_Tell( s ) > (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found any index */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_indx || p_chk->common.i_chunk_fourcc == AVIFOURCC_idx1 ) { break; } } return i_ret; }
static int OpenCallback(struct archive *p_archive, void *p_object) { VLC_UNUSED(p_archive); callback_data_t *p_data = (callback_data_t *) p_object; access_sys_t *p_sys = p_data->p_access->p_sys; p_sys->p_stream = vlc_stream_NewURL( p_data->p_access, p_data->psz_uri ); if(!p_sys->p_stream) return ARCHIVE_FATAL; /* Seek callback must only be set if calls are guaranteed to succeed */ vlc_stream_Control(p_sys->p_stream, STREAM_CAN_SEEK, &p_sys->b_source_canseek); if(p_sys->b_source_canseek) archive_read_set_seek_callback(p_sys->p_archive, SeekCallback); return ARCHIVE_OK; }
static int Control (stream_t *stream, int query, va_list args) { stream_sys_t *p_sys = stream->p_sys; switch (query) { case STREAM_CAN_SEEK: case STREAM_CAN_FASTSEEK: *(va_arg (args, bool *)) = false; break; case STREAM_CAN_PAUSE: *(va_arg (args, bool *)) = p_sys->can_pause; break; case STREAM_CAN_CONTROL_PACE: *(va_arg (args, bool *)) = p_sys->can_pace; break; case STREAM_GET_SIZE: *(va_arg (args, uint64_t *)) = 0; break; case STREAM_GET_PTS_DELAY: *va_arg (args, int64_t *) = p_sys->pts_delay; break; case STREAM_SET_PAUSE_STATE: { bool paused = va_arg (args, unsigned); vlc_mutex_lock (&p_sys->lock); vlc_stream_Control(stream->p_source, STREAM_SET_PAUSE_STATE, paused); p_sys->paused = paused; vlc_cond_signal (&p_sys->wait); vlc_mutex_unlock (&p_sys->lock); break; } default: return VLC_EGENERIC; } return VLC_SUCCESS; }
static int AVI_NextChunk( stream_t *s, avi_chunk_t *p_chk ) { avi_chunk_t chk; if( !p_chk ) { if( AVI_ChunkReadCommon( s, &chk ) ) { return VLC_EGENERIC; } p_chk = &chk; } if( p_chk->common.p_father ) { if( p_chk->common.p_father->common.i_chunk_pos + __EVEN( p_chk->common.p_father->common.i_chunk_size ) + 8 < p_chk->common.i_chunk_pos + __EVEN( p_chk->common.i_chunk_size ) + 8 ) { return VLC_EGENERIC; } } bool b_seekable = false; uint64_t i_offset = p_chk->common.i_chunk_pos + __EVEN( p_chk->common.i_chunk_size ) + 8; if ( !vlc_stream_Control(s, STREAM_CAN_SEEK, &b_seekable) && b_seekable ) { return vlc_stream_Seek( s, i_offset ); } else { ssize_t i_read = i_offset - vlc_stream_Tell( s ); return (i_read >=0 && vlc_stream_Read( s, NULL, i_read ) == i_read) ? VLC_SUCCESS : VLC_EGENERIC; } }
/***************************************************************************** * Control: *****************************************************************************/ static int Control( demux_t *p_demux, int i_query, va_list args ) { demux_sys_t *p_sys = p_demux->p_sys; double f, *pf; int64_t i64; int i_ret; switch( i_query ) { case DEMUX_CAN_SEEK: *va_arg( args, bool * ) = p_sys->b_seekable; return VLC_SUCCESS; case DEMUX_GET_TITLE: *va_arg( args, int * ) = p_sys->current_title; return VLC_SUCCESS; case DEMUX_GET_SEEKPOINT: *va_arg( args, int * ) = p_sys->current_seekpoint; return VLC_SUCCESS; case DEMUX_GET_POSITION: pf = va_arg( args, double * ); i64 = stream_Size( p_demux->s ) - p_sys->i_start_byte; if( i64 > 0 ) { double current = vlc_stream_Tell( p_demux->s ) - p_sys->i_start_byte; *pf = current / (double)i64; } else { *pf = 0.0; } return VLC_SUCCESS; case DEMUX_SET_POSITION: f = va_arg( args, double ); i64 = stream_Size( p_demux->s ) - p_sys->i_start_byte; p_sys->i_current_pts = VLC_TICK_INVALID; p_sys->i_scr = VLC_TICK_INVALID; if( p_sys->format == CDXA_PS ) { i64 = (int64_t)(i64 * f); /* Align to sector payload */ i64 = p_sys->i_start_byte + i64 - (i64 % CDXA_SECTOR_SIZE) + CDXA_SECTOR_HEADER_SIZE; } else { i64 = p_sys->i_start_byte + (int64_t)(i64 * f); } i_ret = vlc_stream_Seek( p_demux->s, i64 ); if( i_ret == VLC_SUCCESS ) { NotifyDiscontinuity( p_sys->tk, p_demux->out ); return i_ret; } break; case DEMUX_GET_TIME: if( p_sys->i_time_track_index >= 0 && p_sys->i_current_pts != VLC_TICK_INVALID ) { *va_arg( args, vlc_tick_t * ) = p_sys->i_current_pts - p_sys->tk[p_sys->i_time_track_index].i_first_pts; return VLC_SUCCESS; } if( p_sys->i_first_scr != VLC_TICK_INVALID && p_sys->i_scr != VLC_TICK_INVALID ) { vlc_tick_t i_time = p_sys->i_scr - p_sys->i_first_scr; /* H.222 2.5.2.2 */ if( p_sys->i_mux_rate > 0 && p_sys->b_have_pack ) { uint64_t i_offset = vlc_stream_Tell( p_demux->s ) - p_sys->i_lastpack_byte; i_time += vlc_tick_from_samples(i_offset, p_sys->i_mux_rate * 50); } *va_arg( args, vlc_tick_t * ) = i_time; return VLC_SUCCESS; } *va_arg( args, vlc_tick_t * ) = 0; break; case DEMUX_GET_LENGTH: if( p_sys->i_length > VLC_TICK_0 ) { *va_arg( args, vlc_tick_t * ) = p_sys->i_length; return VLC_SUCCESS; } else if( p_sys->i_mux_rate > 0 ) { *va_arg( args, vlc_tick_t * ) = vlc_tick_from_samples( stream_Size( p_demux->s ) - p_sys->i_start_byte / 50, p_sys->i_mux_rate ); return VLC_SUCCESS; } *va_arg( args, vlc_tick_t * ) = 0; break; case DEMUX_SET_TIME: { if( p_sys->i_time_track_index >= 0 && p_sys->i_current_pts != VLC_TICK_INVALID && p_sys->i_length > VLC_TICK_0) { vlc_tick_t i_time = va_arg( args, vlc_tick_t ); i_time -= p_sys->tk[p_sys->i_time_track_index].i_first_pts; return demux_Control( p_demux, DEMUX_SET_POSITION, (double) i_time / p_sys->i_length ); } break; } case DEMUX_GET_TITLE_INFO: { struct input_title_t ***v = va_arg( args, struct input_title_t*** ); int *c = va_arg( args, int * ); *va_arg( args, int* ) = 0; /* Title offset */ *va_arg( args, int* ) = 0; /* Chapter offset */ return vlc_stream_Control( p_demux->s, STREAM_GET_TITLE_INFO, v, c ); } case DEMUX_SET_TITLE: return vlc_stream_vaControl( p_demux->s, STREAM_SET_TITLE, args ); case DEMUX_SET_SEEKPOINT: return vlc_stream_vaControl( p_demux->s, STREAM_SET_SEEKPOINT, args ); case DEMUX_TEST_AND_CLEAR_FLAGS: { unsigned *restrict flags = va_arg(args, unsigned *); *flags &= p_sys->updates; p_sys->updates &= ~*flags; return VLC_SUCCESS; } case DEMUX_GET_META: return vlc_stream_vaControl( p_demux->s, STREAM_GET_META, args ); case DEMUX_GET_FPS: break; case DEMUX_CAN_PAUSE: case DEMUX_SET_PAUSE_STATE: case DEMUX_CAN_CONTROL_PACE: case DEMUX_GET_PTS_DELAY: return demux_vaControlHelper( p_demux->s, 0, -1, 0, 1, i_query, args ); default: break; } return VLC_EGENERIC; }
/***************************************************************************** * Open *****************************************************************************/ static int OpenCommon( vlc_object_t *p_this, bool b_force ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; const uint8_t *p_peek; ssize_t i_peek = 0; ssize_t i_offset = 0; ssize_t i_skip = 0; unsigned i_max_packets = PS_PACKET_PROBE; int format = MPEG_PS; int i_mux_rate = 0; vlc_tick_t i_length = VLC_TICK_INVALID; i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 16 ); if( i_peek < 16 ) { msg_Dbg( p_demux, "cannot peek" ); return VLC_EGENERIC; } if( !memcmp( p_peek, "PSMF", 4 ) && (GetDWBE( &p_peek[4] ) & 0x30303030) == 0x30303030 ) { i_peek = vlc_stream_Peek( p_demux->s, &p_peek, 100 ); if( i_peek < 100 ) return VLC_EGENERIC; i_skip = i_offset = GetWBE( &p_peek[10] ); format = PSMF_PS; msg_Info( p_demux, "Detected PSMF-PS header"); i_mux_rate = GetDWBE( &p_peek[96] ); if( GetDWBE( &p_peek[86] ) > 0 ) i_length = vlc_tick_from_samples( GetDWBE( &p_peek[92] ), GetDWBE( &p_peek[86] )); } else if( !memcmp( p_peek, "RIFF", 4 ) && !memcmp( &p_peek[8], "CDXA", 4 ) ) { format = CDXA_PS; i_max_packets = 0; /* We can't probe here */ i_skip = CDXA_HEADER_SIZE; msg_Info( p_demux, "Detected CDXA-PS" ); /* FIXME: have a proper way to decap CD sectors or make an access stream filter */ } else if( b_force ) { msg_Warn( p_demux, "this does not look like an MPEG PS stream, " "continuing anyway" ); i_max_packets = 0; } for( unsigned i=0; i<i_max_packets; i++ ) { if( i_peek < i_offset + 16 ) { i_peek = vlc_stream_Peek( p_demux->s, &p_peek, i_offset + 16 ); if( i_peek < i_offset + 16 ) return VLC_EGENERIC; } const uint8_t startcode[3] = { 0x00, 0x00, 0x01 }; const uint8_t *p_header = &p_peek[i_offset]; if( memcmp( p_header, startcode, 3 ) || ( (p_header[3] & 0xB0) != 0xB0 && !(p_header[3] >= 0xC0 && p_header[3] <= 0xEF) && p_header[3] != PS_STREAM_ID_EXTENDED && p_header[3] != PS_STREAM_ID_DIRECTORY ) ) return VLC_EGENERIC; ssize_t i_pessize = ps_pkt_size( p_header, 16 ); if( i_pessize < 5 ) return VLC_EGENERIC; i_offset += i_pessize; } if( i_skip > 0 && !p_demux->b_preparsing && vlc_stream_Read( p_demux->s, NULL, i_skip ) != i_skip ) return VLC_EGENERIC; /* Fill p_demux field */ p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; p_demux->pf_demux = Demux; p_demux->pf_control = Control; /* Init p_sys */ p_sys->i_mux_rate = i_mux_rate; p_sys->i_pack_scr = VLC_TICK_INVALID; p_sys->i_first_scr = VLC_TICK_INVALID; p_sys->i_scr = VLC_TICK_INVALID; p_sys->i_scr_track_id = 0; p_sys->i_length = i_length; p_sys->i_current_pts = VLC_TICK_INVALID; p_sys->i_time_track_index = -1; p_sys->i_aob_mlp_count = 0; p_sys->i_start_byte = i_skip; p_sys->i_lastpack_byte = i_skip; p_sys->b_lost_sync = false; p_sys->b_have_pack = false; p_sys->b_bad_scr = false; p_sys->b_seekable = false; p_sys->format = format; p_sys->current_title = 0; p_sys->current_seekpoint = 0; p_sys->updates = 0; vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &p_sys->b_seekable ); ps_psm_init( &p_sys->psm ); ps_track_init( p_sys->tk ); /* TODO prescanning of ES */ return VLC_SUCCESS; }
static int ControlSetTime( demux_t *p_demux, int64_t i_time ) { demux_sys_t *p_sys = p_demux->p_sys; bool b_seekable; int i; /* */ vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_seekable ); if( !b_seekable ) return VLC_EGENERIC; const mtime_t i_length = ControlGetLength( p_demux ); if( i_length <= 0 ) return VLC_EGENERIC; const uint64_t i_stream_size = stream_Size( p_demux->s ); if( i_stream_size <= p_sys->i_data_pos ) return VLC_EGENERIC; const double i_bytemicrorate = (double) i_length / (i_stream_size - p_sys->i_data_pos); if( i_bytemicrorate == 0 ) return VLC_EGENERIC; uint64_t i_lower = p_sys->i_data_pos; uint64_t i_upper = i_stream_size; uint64_t i_start_pos; assert( p_sys->i_seekpoint > 0 ); /* ReadMeta ensure at least (0,0) */ if( p_sys->i_seekpoint > 1 ) { /* lookup base offset */ for( i = p_sys->i_seekpoint-1; i >= 0; i-- ) { if( p_sys->seekpoint[i]->i_time_offset <= i_time ) break; } i_lower = p_sys->seekpoint[0]->i_byte_offset + p_sys->i_data_pos; if( i+1 < p_sys->i_seekpoint ) i_upper = p_sys->seekpoint[i+1]->i_byte_offset + p_sys->i_data_pos; i_start_pos = i_lower; } else { i_start_pos = i_time / i_bytemicrorate; } if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) ) return VLC_EGENERIC; int i_ret = RefineSeek( p_demux, i_time, i_bytemicrorate, i_lower, i_upper ); if( i_ret == VLC_SUCCESS ) { p_sys->i_next_block_flags |= BLOCK_FLAG_DISCONTINUITY; Reset( p_sys ); es_out_Control( p_demux->out, ES_OUT_SET_NEXT_DISPLAY_TIME, i_time ); } return i_ret; }
static int RefineSeek( demux_t *p_demux, mtime_t i_time, double i_bytemicrorate, uint64_t i_lowpos, uint64_t i_highpos ) { demux_sys_t *p_sys = p_demux->p_sys; bool b_found = false; block_t *p_block_out; block_t *p_block_in; unsigned i_frame_size = FLAC_MIN_FRAME_SIZE; bool b_canfastseek = false; (int) vlc_stream_Control( p_demux->s, STREAM_CAN_FASTSEEK, &b_canfastseek ); uint64_t i_start_pos = vlc_stream_Tell( p_demux->s ); while( !b_found ) { FlushPacketizer( p_sys->p_packetizer ); p_block_out = NULL; p_block_in = NULL; while( !p_block_out ) { if( !p_block_in ) { if( !(p_block_in = vlc_stream_Block( p_demux->s, i_frame_size )) ) break; } p_block_out = p_sys->p_packetizer->pf_packetize( p_sys->p_packetizer, &p_block_in ); } if( !p_block_out ) { if( p_block_in ) block_Release( p_block_in ); break; } if( p_block_out->i_buffer > i_frame_size ) i_frame_size = p_block_out->i_buffer; /* If we are further than wanted block */ if( p_block_out->i_dts >= i_time ) { mtime_t i_diff = p_block_out->i_dts - i_time; /* Not in acceptable approximation range */ if( i_diff > CLOCK_FREQ / 10 && i_diff / i_bytemicrorate > i_frame_size ) { i_highpos = i_start_pos; i_start_pos -= ( i_diff / i_bytemicrorate ); i_start_pos = __MAX(i_start_pos, i_lowpos + i_frame_size); } else b_found = true; } else if( p_block_out->i_dts < i_time ) { mtime_t i_diff = i_time - p_block_out->i_dts; /* Not in acceptable NEXT_TIME demux range */ if( i_diff >= ((b_canfastseek) ? FLAC_MAX_PREROLL : FLAC_MAX_SLOW_PREROLL) && i_diff / i_bytemicrorate > i_frame_size ) { i_lowpos = i_start_pos; i_start_pos += ( i_diff / i_bytemicrorate ); i_start_pos = __MIN(i_start_pos, i_highpos - i_frame_size); } else b_found = true; } if( p_block_out ) block_Release( p_block_out ); if( p_block_in ) block_Release( p_block_in ); if( !b_found ) { if( i_highpos < i_lowpos || i_highpos - i_lowpos < i_frame_size ) break; if( VLC_SUCCESS != vlc_stream_Seek( p_demux->s, i_start_pos ) ) break; } } return b_found ? VLC_SUCCESS : VLC_EGENERIC; }
static int archive_init( private_sys_t* p_sys, stream_t* source ) { /* CREATE ARCHIVE HANDLE */ p_sys->p_archive = archive_read_new(); if( unlikely( !p_sys->p_archive ) ) { msg_Dbg( p_sys->p_obj, "unable to create libarchive handle" ); return VLC_EGENERIC; } /* SETUP SEEKING */ p_sys->b_seekable_archive = false; if( vlc_stream_Control( source, STREAM_CAN_SEEK, &p_sys->b_seekable_source ) ) { msg_Warn( p_sys->p_obj, "unable to query whether source stream can seek" ); p_sys->b_seekable_source = false; } if( p_sys->b_seekable_source ) { if( archive_read_set_seek_callback( p_sys->p_archive, libarchive_seek_cb ) ) { msg_Err( p_sys->p_obj, "archive_read_set_callback failed, aborting." ); return VLC_EGENERIC; } } /* ENABLE ALL FORMATS/FILTERS */ archive_read_support_filter_all( p_sys->p_archive ); archive_read_support_format_all( p_sys->p_archive ); /* REGISTER CALLBACK DATA */ if( archive_read_set_switch_callback( p_sys->p_archive, libarchive_jump_cb ) ) { msg_Err( p_sys->p_obj, "archive_read_set_switch_callback failed, aborting." ); return VLC_EGENERIC; } for( size_t i = 0; i < p_sys->i_callback_data; ++i ) { if( archive_read_append_callback_data( p_sys->p_archive, p_sys->pp_callback_data[i] ) ) { return VLC_EGENERIC; } } /* OPEN THE ARCHIVE */ if( archive_read_open2( p_sys->p_archive, p_sys->pp_callback_data[0], NULL, libarchive_read_cb, libarchive_skip_cb, libarchive_exit_cb ) ) { msg_Dbg( p_sys->p_obj, "libarchive: %s", archive_error_string( p_sys->p_archive ) ); return VLC_EGENERIC; } return VLC_SUCCESS; }
int avformat_OpenDemux( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; AVInputFormat *fmt = NULL; vlc_tick_t i_start_time = VLC_TICK_INVALID; bool b_can_seek; const char *psz_url; int error; if( p_demux->psz_filepath ) psz_url = p_demux->psz_filepath; else psz_url = p_demux->psz_url; if( avformat_ProbeDemux( p_this, &fmt, psz_url ) != VLC_SUCCESS ) return VLC_EGENERIC; vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ); /* Fill p_demux fields */ p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = malloc( sizeof( demux_sys_t ) ); if( !p_sys ) return VLC_ENOMEM; p_sys->ic = 0; p_sys->fmt = fmt; p_sys->tracks = NULL; p_sys->i_ssa_order = 0; TAB_INIT( p_sys->i_attachments, p_sys->attachments); p_sys->p_title = NULL; p_sys->i_seekpoint = 0; p_sys->i_update = 0; /* Create I/O wrapper */ unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE ); if( !p_io_buffer ) { avformat_CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic = avformat_alloc_context(); if( !p_sys->ic ) { av_free( p_io_buffer ); avformat_CloseDemux( p_this ); return VLC_ENOMEM; } AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer, AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek ); if( !pb ) { av_free( p_io_buffer ); avformat_CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0; error = avformat_open_input(&p_sys->ic, psz_url, p_sys->fmt, NULL); if( error < 0 ) { msg_Err( p_demux, "Could not open %s: %s", psz_url, vlc_strerror_c(AVUNERROR(error)) ); av_free( pb->buffer ); av_free( pb ); p_sys->ic = NULL; avformat_CloseDemux( p_this ); return VLC_EGENERIC; } char *psz_opts = var_InheritString( p_demux, "avformat-options" ); unsigned nb_streams = p_sys->ic->nb_streams; AVDictionary *options[nb_streams ? nb_streams : 1]; options[0] = NULL; for (unsigned i = 1; i < nb_streams; i++) options[i] = NULL; if (psz_opts) { vlc_av_get_options(psz_opts, &options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_copy(&options[i], options[0], 0); } free(psz_opts); } vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */ error = avformat_find_stream_info( p_sys->ic, options ); vlc_avcodec_unlock(); AVDictionaryEntry *t = NULL; while ((t = av_dict_get(options[0], "", t, AV_DICT_IGNORE_SUFFIX))) { msg_Err( p_demux, "Unknown option \"%s\"", t->key ); } av_dict_free(&options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_free(&options[i]); } nb_streams = p_sys->ic->nb_streams; /* it may have changed */ if( !nb_streams ) { msg_Err( p_demux, "No streams found"); avformat_CloseDemux( p_this ); return VLC_EGENERIC; } p_sys->tracks = calloc( nb_streams, sizeof(*p_sys->tracks) ); if( !p_sys->tracks ) { avformat_CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->i_tracks = nb_streams; if( error < 0 ) { msg_Warn( p_demux, "Could not find stream info: %s", vlc_strerror_c(AVUNERROR(error)) ); } for( unsigned i = 0; i < nb_streams; i++ ) { struct avformat_track_s *p_track = &p_sys->tracks[i]; AVStream *s = p_sys->ic->streams[i]; const AVCodecParameters *cp = s->codecpar; es_format_t es_fmt; const char *psz_type = "unknown"; /* Do not use the cover art as a stream */ if( s->disposition == AV_DISPOSITION_ATTACHED_PIC ) continue; vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id ); switch( cp->codec_type ) { case AVMEDIA_TYPE_AUDIO: es_format_Init( &es_fmt, AUDIO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.i_bitrate = cp->bit_rate; es_fmt.audio.i_channels = cp->channels; es_fmt.audio.i_rate = cp->sample_rate; es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample; es_fmt.audio.i_blockalign = cp->block_align; psz_type = "audio"; if(cp->codec_id == AV_CODEC_ID_AAC_LATM) { es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M'); es_fmt.b_packetized = false; } else if(cp->codec_id == AV_CODEC_ID_AAC && p_sys->fmt->long_name && strstr(p_sys->fmt->long_name, "raw ADTS AAC")) { es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S'); es_fmt.b_packetized = false; } break; case AVMEDIA_TYPE_VIDEO: es_format_Init( &es_fmt, VIDEO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.video.i_bits_per_pixel = cp->bits_per_coded_sample; /* Special case for raw video data */ if( cp->codec_id == AV_CODEC_ID_RAWVIDEO ) { msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format ); if( GetVlcChroma( &es_fmt.video, cp->format ) != VLC_SUCCESS) { msg_Err( p_demux, "was unable to find a FourCC match for raw video" ); } else es_fmt.i_codec = es_fmt.video.i_chroma; } /* We need this for the h264 packetizer */ else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") || p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) ) es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' ); es_fmt.video.i_width = cp->width; es_fmt.video.i_height = cp->height; es_fmt.video.i_visible_width = es_fmt.video.i_width; es_fmt.video.i_visible_height = es_fmt.video.i_height; get_rotation(&es_fmt, s); # warning FIXME: implement palette transmission psz_type = "video"; AVRational rate; #if (LIBAVUTIL_VERSION_MICRO < 100) /* libav */ # if (LIBAVFORMAT_VERSION_INT >= AV_VERSION_INT(55, 20, 0)) rate.num = s->time_base.num; rate.den = s->time_base.den; # else rate.num = s->codec->time_base.num; rate.den = s->codec->time_base.den; # endif rate.den *= __MAX( s->codec->ticks_per_frame, 1 ); #else /* ffmpeg */ rate = av_guess_frame_rate( p_sys->ic, s, NULL ); #endif if( rate.den && rate.num ) { es_fmt.video.i_frame_rate = rate.num; es_fmt.video.i_frame_rate_base = rate.den; } AVRational ar; #if (LIBAVUTIL_VERSION_MICRO < 100) /* libav */ ar.num = s->sample_aspect_ratio.num; ar.den = s->sample_aspect_ratio.den; #else ar = av_guess_sample_aspect_ratio( p_sys->ic, s, NULL ); #endif if( ar.num && ar.den ) { es_fmt.video.i_sar_den = ar.den; es_fmt.video.i_sar_num = ar.num; } break; case AVMEDIA_TYPE_SUBTITLE: es_format_Init( &es_fmt, SPU_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 && cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE && cp->extradata != NULL && cp->extradata_size > 0 ) { char *psz_start; char *psz_buf = malloc( cp->extradata_size + 1); if( psz_buf != NULL ) { memcpy( psz_buf, cp->extradata , cp->extradata_size ); psz_buf[cp->extradata_size] = '\0'; psz_start = strstr( psz_buf, "size:" ); if( psz_start && vobsub_size_parse( psz_start, &es_fmt.subs.spu.i_original_frame_width, &es_fmt.subs.spu.i_original_frame_height ) == VLC_SUCCESS ) { msg_Dbg( p_demux, "original frame size: %dx%d", es_fmt.subs.spu.i_original_frame_width, es_fmt.subs.spu.i_original_frame_height ); } else { msg_Warn( p_demux, "reading original frame size failed" ); } psz_start = strstr( psz_buf, "palette:" ); if( psz_start && vobsub_palette_parse( psz_start, &es_fmt.subs.spu.palette[1] ) == VLC_SUCCESS ) { es_fmt.subs.spu.palette[0] = SPU_PALETTE_DEFINED; msg_Dbg( p_demux, "vobsub palette read" ); } else { msg_Warn( p_demux, "reading original palette failed" ); } free( psz_buf ); } } else if( cp->codec_id == AV_CODEC_ID_DVB_SUBTITLE && cp->extradata_size > 3 ) { es_fmt.subs.dvb.i_id = GetWBE( cp->extradata ) | (GetWBE( cp->extradata + 2 ) << 16); } else if( cp->codec_id == AV_CODEC_ID_MOV_TEXT ) { if( cp->extradata_size && (es_fmt.p_extra = malloc(cp->extradata_size)) ) { memcpy( es_fmt.p_extra, cp->extradata, cp->extradata_size ); es_fmt.i_extra = cp->extradata_size; } } psz_type = "subtitle"; break; default: es_format_Init( &es_fmt, UNKNOWN_ES, 0 ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT ) { input_attachment_t *p_attachment; psz_type = "attachment"; if( cp->codec_id == AV_CODEC_ID_TTF ) { AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 ); if( filename && filename->value ) { p_attachment = vlc_input_attachment_New( filename->value, "application/x-truetype-font", NULL, cp->extradata, (int)cp->extradata_size ); if( p_attachment ) TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment ); } } else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id ); } else #endif { if( cp->codec_type == AVMEDIA_TYPE_DATA ) psz_type = "data"; msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id ); } break; } AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 ); if ( language && language->value ) es_fmt.psz_language = strdup( language->value ); if( s->disposition & AV_DISPOSITION_DEFAULT ) es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000; #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT ) #endif if( cp->codec_type != AVMEDIA_TYPE_DATA ) { const bool b_ogg = !strcmp( p_sys->fmt->name, "ogg" ); const uint8_t *p_extra = cp->extradata; unsigned i_extra = cp->extradata_size; if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg ) { unsigned pi_size[3]; const void *pp_data[3]; unsigned i_count; for( i_count = 0; i_count < 3; i_count++ ) { if( i_extra < 2 ) break; pi_size[i_count] = GetWBE( p_extra ); pp_data[i_count] = &p_extra[2]; if( i_extra < pi_size[i_count] + 2 ) break; p_extra += 2 + pi_size[i_count]; i_extra -= 2 + pi_size[i_count]; } if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, i_count ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg ) { const uint8_t p_dummy_comment[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_OPUS ) { const uint8_t p_dummy_comment[] = { 'O', 'p', 'u', 's', 'T', 'a', 'g', 's', 0, 0, 0, 0, /* Vendor String length */ /* Vendor String */ 0, 0, 0, 0, /* User Comment List Length */ }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->extradata_size > 0 && !es_fmt.i_extra ) { es_fmt.p_extra = malloc( i_extra ); if( es_fmt.p_extra ) { es_fmt.i_extra = i_extra; memcpy( es_fmt.p_extra, p_extra, i_extra ); } } p_track->p_es = es_out_Add( p_demux->out, &es_fmt ); if( p_track->p_es && (s->disposition & AV_DISPOSITION_DEFAULT) ) es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, p_track->p_es ); msg_Dbg( p_demux, "adding es: %s codec = %4.4s (%d)", psz_type, (char*)&fcc, cp->codec_id ); } es_format_Clean( &es_fmt ); } if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE ) i_start_time = FROM_AV_TS(p_sys->ic->start_time); msg_Dbg( p_demux, "AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT ); msg_Dbg( p_demux, " - format = %s (%s)", p_sys->fmt->name, p_sys->fmt->long_name ); msg_Dbg( p_demux, " - start time = %"PRId64, i_start_time ); msg_Dbg( p_demux, " - duration = %"PRId64, ( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ? FROM_AV_TS(p_sys->ic->duration) : -1 ); if( p_sys->ic->nb_chapters > 0 ) { p_sys->p_title = vlc_input_title_New(); p_sys->p_title->i_length = FROM_AV_TS(p_sys->ic->duration); } for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ ) { seekpoint_t *s = vlc_seekpoint_New(); AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0); if( title && title->value ) { s->psz_name = strdup( title->value ); EnsureUTF8( s->psz_name ); msg_Dbg( p_demux, " - chapter %d: %s", i, s->psz_name ); } s->i_time_offset = vlc_tick_from_samples( p_sys->ic->chapters[i]->start * p_sys->ic->chapters[i]->time_base.num, p_sys->ic->chapters[i]->time_base.den ) - (i_start_time != VLC_TICK_INVALID ? i_start_time : 0 ); TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); } ResetTime( p_demux, 0 ); return VLC_SUCCESS; }
int OpenDemux( vlc_object_t *p_this ) { demux_t *p_demux = (demux_t*)p_this; demux_sys_t *p_sys; AVProbeData pd = { }; AVInputFormat *fmt = NULL; int64_t i_start_time = -1; bool b_can_seek; char *psz_url; const uint8_t *peek; int error; /* Init Probe data */ pd.buf_size = vlc_stream_Peek( p_demux->s, &peek, 2048 + 213 ); if( pd.buf_size <= 0 ) { msg_Warn( p_demux, "cannot peek" ); return VLC_EGENERIC; } pd.buf = malloc( pd.buf_size + AVPROBE_PADDING_SIZE ); if( unlikely(pd.buf == NULL) ) return VLC_ENOMEM; memcpy( pd.buf, peek, pd.buf_size ); memset( pd.buf + pd.buf_size, 0, AVPROBE_PADDING_SIZE ); if( p_demux->psz_file ) psz_url = strdup( p_demux->psz_file ); else { if( asprintf( &psz_url, "%s://%s", p_demux->psz_access, p_demux->psz_location ) == -1) psz_url = NULL; } if( psz_url != NULL ) msg_Dbg( p_demux, "trying url: %s", psz_url ); pd.filename = psz_url; vlc_stream_Control( p_demux->s, STREAM_CAN_SEEK, &b_can_seek ); vlc_init_avformat(p_this); /* Guess format */ char *psz_format = var_InheritString( p_this, "avformat-format" ); if( psz_format ) { if( (fmt = av_find_input_format(psz_format)) ) msg_Dbg( p_demux, "forcing format: %s", fmt->name ); free( psz_format ); } if( fmt == NULL ) fmt = av_probe_input_format( &pd, 1 ); free( pd.buf ); if( fmt == NULL ) { msg_Dbg( p_demux, "couldn't guess format" ); free( psz_url ); return VLC_EGENERIC; } if( !p_demux->obj.force ) { static const char ppsz_blacklist[][16] = { /* Don't handle MPEG unless forced */ "mpeg", "vcd", "vob", "mpegts", /* libavformat's redirector won't work */ "redir", "sdp", /* Don't handle subtitles format */ "ass", "srt", "microdvd", /* No timestamps at all */ "hevc", "h264", "" }; for( int i = 0; *ppsz_blacklist[i]; i++ ) { if( !strcmp( fmt->name, ppsz_blacklist[i] ) ) { free( psz_url ); return VLC_EGENERIC; } } } /* Don't trigger false alarms on bin files */ if( !p_demux->obj.force && !strcmp( fmt->name, "psxstr" ) ) { int i_len; if( !p_demux->psz_file ) { free( psz_url ); return VLC_EGENERIC; } i_len = strlen( p_demux->psz_file ); if( i_len < 4 ) { free( psz_url ); return VLC_EGENERIC; } if( strcasecmp( &p_demux->psz_file[i_len - 4], ".str" ) && strcasecmp( &p_demux->psz_file[i_len - 4], ".xai" ) && strcasecmp( &p_demux->psz_file[i_len - 3], ".xa" ) ) { free( psz_url ); return VLC_EGENERIC; } } msg_Dbg( p_demux, "detected format: %s", fmt->name ); /* Fill p_demux fields */ p_demux->pf_demux = Demux; p_demux->pf_control = Control; p_demux->p_sys = p_sys = xmalloc( sizeof( demux_sys_t ) ); p_sys->ic = 0; p_sys->fmt = fmt; p_sys->i_tk = 0; p_sys->tk = NULL; p_sys->tk_pcr = NULL; p_sys->i_ssa_order = 0; TAB_INIT( p_sys->i_attachments, p_sys->attachments); p_sys->p_title = NULL; /* Create I/O wrapper */ unsigned char * p_io_buffer = av_malloc( AVFORMAT_IOBUFFER_SIZE ); if( !p_io_buffer ) { free( psz_url ); CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic = avformat_alloc_context(); if( !p_sys->ic ) { av_free( p_io_buffer ); free( psz_url ); CloseDemux( p_this ); return VLC_ENOMEM; } AVIOContext *pb = p_sys->ic->pb = avio_alloc_context( p_io_buffer, AVFORMAT_IOBUFFER_SIZE, 0, p_demux, IORead, NULL, IOSeek ); if( !pb ) { av_free( p_io_buffer ); free( psz_url ); CloseDemux( p_this ); return VLC_ENOMEM; } p_sys->ic->pb->seekable = b_can_seek ? AVIO_SEEKABLE_NORMAL : 0; error = avformat_open_input(&p_sys->ic, psz_url, p_sys->fmt, NULL); if( error < 0 ) { msg_Err( p_demux, "Could not open %s: %s", psz_url, vlc_strerror_c(AVUNERROR(error)) ); av_free( p_io_buffer ); av_free( pb ); p_sys->ic = NULL; free( psz_url ); CloseDemux( p_this ); return VLC_EGENERIC; } free( psz_url ); char *psz_opts = var_InheritString( p_demux, "avformat-options" ); AVDictionary *options[p_sys->ic->nb_streams ? p_sys->ic->nb_streams : 1]; options[0] = NULL; unsigned int nb_streams = p_sys->ic->nb_streams; for (unsigned i = 1; i < nb_streams; i++) options[i] = NULL; if (psz_opts) { vlc_av_get_options(psz_opts, &options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_copy(&options[i], options[0], 0); } free(psz_opts); } vlc_avcodec_lock(); /* avformat calls avcodec behind our back!!! */ error = avformat_find_stream_info( p_sys->ic, options ); /* FIXME: what if nb_streams change after that call? */ vlc_avcodec_unlock(); AVDictionaryEntry *t = NULL; while ((t = av_dict_get(options[0], "", t, AV_DICT_IGNORE_SUFFIX))) { msg_Err( p_demux, "Unknown option \"%s\"", t->key ); } av_dict_free(&options[0]); for (unsigned i = 1; i < nb_streams; i++) { av_dict_free(&options[i]); } if( error < 0 ) { msg_Warn( p_demux, "Could not find stream info: %s", vlc_strerror_c(AVUNERROR(error)) ); } for( unsigned i = 0; i < p_sys->ic->nb_streams; i++ ) { AVStream *s = p_sys->ic->streams[i]; const AVCodecParameters *cp = s->codecpar; es_out_id_t *es = NULL; es_format_t es_fmt; const char *psz_type = "unknown"; /* Do not use the cover art as a stream */ if( s->disposition == AV_DISPOSITION_ATTACHED_PIC ) { TAB_APPEND( p_sys->i_tk, p_sys->tk, NULL ); continue; } vlc_fourcc_t fcc = GetVlcFourcc( cp->codec_id ); switch( cp->codec_type ) { case AVMEDIA_TYPE_AUDIO: es_format_Init( &es_fmt, AUDIO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.i_bitrate = cp->bit_rate; es_fmt.audio.i_channels = cp->channels; es_fmt.audio.i_rate = cp->sample_rate; es_fmt.audio.i_bitspersample = cp->bits_per_coded_sample; es_fmt.audio.i_blockalign = cp->block_align; psz_type = "audio"; if(cp->codec_id == AV_CODEC_ID_AAC_LATM) { es_fmt.i_original_fourcc = VLC_FOURCC('L','A','T','M'); es_fmt.b_packetized = false; } else if(cp->codec_id == AV_CODEC_ID_AAC && strstr(p_sys->fmt->long_name, "raw ADTS AAC")) { es_fmt.i_original_fourcc = VLC_FOURCC('A','D','T','S'); es_fmt.b_packetized = false; } break; case AVMEDIA_TYPE_VIDEO: es_format_Init( &es_fmt, VIDEO_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); es_fmt.video.i_bits_per_pixel = cp->bits_per_coded_sample; /* Special case for raw video data */ if( cp->codec_id == AV_CODEC_ID_RAWVIDEO ) { msg_Dbg( p_demux, "raw video, pixel format: %i", cp->format ); if( GetVlcChroma( &es_fmt.video, cp->format ) != VLC_SUCCESS) { msg_Err( p_demux, "was unable to find a FourCC match for raw video" ); } else es_fmt.i_codec = es_fmt.video.i_chroma; } /* We need this for the h264 packetizer */ else if( cp->codec_id == AV_CODEC_ID_H264 && ( p_sys->fmt == av_find_input_format("flv") || p_sys->fmt == av_find_input_format("matroska") || p_sys->fmt == av_find_input_format("mp4") ) ) es_fmt.i_original_fourcc = VLC_FOURCC( 'a', 'v', 'c', '1' ); es_fmt.video.i_width = cp->width; es_fmt.video.i_height = cp->height; es_fmt.video.i_visible_width = es_fmt.video.i_width; es_fmt.video.i_visible_height = es_fmt.video.i_height; get_rotation(&es_fmt, s); # warning FIXME: implement palette transmission psz_type = "video"; es_fmt.video.i_frame_rate = s->codec->time_base.num; es_fmt.video.i_frame_rate_base = s->codec->time_base.den * __MAX( s->codec->ticks_per_frame, 1 ); es_fmt.video.i_sar_num = s->sample_aspect_ratio.num; if (s->sample_aspect_ratio.num > 0) es_fmt.video.i_sar_den = s->sample_aspect_ratio.den; else es_fmt.video.i_sar_den = 0; break; case AVMEDIA_TYPE_SUBTITLE: es_format_Init( &es_fmt, SPU_ES, fcc ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); if( strncmp( p_sys->ic->iformat->name, "matroska", 8 ) == 0 && cp->codec_id == AV_CODEC_ID_DVD_SUBTITLE && cp->extradata != NULL && cp->extradata_size > 0 ) { char *psz_start; char *psz_buf = malloc( cp->extradata_size + 1); if( psz_buf != NULL ) { memcpy( psz_buf, cp->extradata , cp->extradata_size ); psz_buf[cp->extradata_size] = '\0'; psz_start = strstr( psz_buf, "size:" ); if( psz_start && vobsub_size_parse( psz_start, &es_fmt.subs.spu.i_original_frame_width, &es_fmt.subs.spu.i_original_frame_height ) == VLC_SUCCESS ) { msg_Dbg( p_demux, "original frame size: %dx%d", es_fmt.subs.spu.i_original_frame_width, es_fmt.subs.spu.i_original_frame_height ); } else { msg_Warn( p_demux, "reading original frame size failed" ); } psz_start = strstr( psz_buf, "palette:" ); if( psz_start && vobsub_palette_parse( psz_start, &es_fmt.subs.spu.palette[1] ) == VLC_SUCCESS ) { es_fmt.subs.spu.palette[0] = SPU_PALETTE_DEFINED; msg_Dbg( p_demux, "vobsub palette read" ); } else { msg_Warn( p_demux, "reading original palette failed" ); } free( psz_buf ); } } psz_type = "subtitle"; break; default: es_format_Init( &es_fmt, UNKNOWN_ES, 0 ); es_fmt.i_original_fourcc = CodecTagToFourcc( cp->codec_tag ); #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type == AVMEDIA_TYPE_ATTACHMENT ) { input_attachment_t *p_attachment; psz_type = "attachment"; if( cp->codec_id == AV_CODEC_ID_TTF ) { AVDictionaryEntry *filename = av_dict_get( s->metadata, "filename", NULL, 0 ); if( filename && filename->value ) { p_attachment = vlc_input_attachment_New( filename->value, "application/x-truetype-font", NULL, cp->extradata, (int)cp->extradata_size ); if( p_attachment ) TAB_APPEND( p_sys->i_attachments, p_sys->attachments, p_attachment ); } } else msg_Warn( p_demux, "unsupported attachment type (%u) in avformat demux", cp->codec_id ); } else #endif { if( cp->codec_type == AVMEDIA_TYPE_DATA ) psz_type = "data"; msg_Warn( p_demux, "unsupported track type (%u:%u) in avformat demux", cp->codec_type, cp->codec_id ); } break; } AVDictionaryEntry *language = av_dict_get( s->metadata, "language", NULL, 0 ); if ( language && language->value ) es_fmt.psz_language = strdup( language->value ); if( s->disposition & AV_DISPOSITION_DEFAULT ) es_fmt.i_priority = ES_PRIORITY_SELECTABLE_MIN + 1000; #ifdef HAVE_AVUTIL_CODEC_ATTACHMENT if( cp->codec_type != AVMEDIA_TYPE_ATTACHMENT ) #endif if( cp->codec_type != AVMEDIA_TYPE_DATA ) { const bool b_ogg = !strcmp( p_sys->fmt->name, "ogg" ); const uint8_t *p_extra = cp->extradata; unsigned i_extra = cp->extradata_size; if( cp->codec_id == AV_CODEC_ID_THEORA && b_ogg ) { unsigned pi_size[3]; const void *pp_data[3]; unsigned i_count; for( i_count = 0; i_count < 3; i_count++ ) { if( i_extra < 2 ) break; pi_size[i_count] = GetWBE( p_extra ); pp_data[i_count] = &p_extra[2]; if( i_extra < pi_size[i_count] + 2 ) break; p_extra += 2 + pi_size[i_count]; i_extra -= 2 + pi_size[i_count]; } if( i_count > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, i_count ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_SPEEX && b_ogg ) { const uint8_t p_dummy_comment[] = { 0, 0, 0, 0, 0, 0, 0, 0, }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->codec_id == AV_CODEC_ID_OPUS ) { const uint8_t p_dummy_comment[] = { 'O', 'p', 'u', 's', 'T', 'a', 'g', 's', 0, 0, 0, 0, /* Vendor String length */ /* Vendor String */ 0, 0, 0, 0, /* User Comment List Length */ }; unsigned pi_size[2]; const void *pp_data[2]; pi_size[0] = i_extra; pp_data[0] = p_extra; pi_size[1] = sizeof(p_dummy_comment); pp_data[1] = p_dummy_comment; if( pi_size[0] > 0 && xiph_PackHeaders( &es_fmt.i_extra, &es_fmt.p_extra, pi_size, pp_data, 2 ) ) { es_fmt.i_extra = 0; es_fmt.p_extra = NULL; } } else if( cp->extradata_size > 0 ) { es_fmt.p_extra = malloc( i_extra ); if( es_fmt.p_extra ) { es_fmt.i_extra = i_extra; memcpy( es_fmt.p_extra, p_extra, i_extra ); } } es = es_out_Add( p_demux->out, &es_fmt ); if( s->disposition & AV_DISPOSITION_DEFAULT ) es_out_Control( p_demux->out, ES_OUT_SET_ES_DEFAULT, es ); es_format_Clean( &es_fmt ); msg_Dbg( p_demux, "adding es: %s codec = %4.4s (%d)", psz_type, (char*)&fcc, cp->codec_id ); } TAB_APPEND( p_sys->i_tk, p_sys->tk, es ); } p_sys->tk_pcr = xcalloc( p_sys->i_tk, sizeof(*p_sys->tk_pcr) ); if( p_sys->ic->start_time != (int64_t)AV_NOPTS_VALUE ) i_start_time = p_sys->ic->start_time * 1000000 / AV_TIME_BASE; msg_Dbg( p_demux, "AVFormat(%s %s) supported stream", AVPROVIDER(LIBAVFORMAT), LIBAVFORMAT_IDENT ); msg_Dbg( p_demux, " - format = %s (%s)", p_sys->fmt->name, p_sys->fmt->long_name ); msg_Dbg( p_demux, " - start time = %"PRId64, i_start_time ); msg_Dbg( p_demux, " - duration = %"PRId64, ( p_sys->ic->duration != (int64_t)AV_NOPTS_VALUE ) ? p_sys->ic->duration * 1000000 / AV_TIME_BASE : -1 ); if( p_sys->ic->nb_chapters > 0 ) { p_sys->p_title = vlc_input_title_New(); p_sys->p_title->i_length = p_sys->ic->duration * 1000000 / AV_TIME_BASE; } for( unsigned i = 0; i < p_sys->ic->nb_chapters; i++ ) { seekpoint_t *s = vlc_seekpoint_New(); AVDictionaryEntry *title = av_dict_get( p_sys->ic->metadata, "title", NULL, 0); if( title && title->value ) { s->psz_name = strdup( title->value ); EnsureUTF8( s->psz_name ); msg_Dbg( p_demux, " - chapter %d: %s", i, s->psz_name ); } s->i_time_offset = p_sys->ic->chapters[i]->start * 1000000 * p_sys->ic->chapters[i]->time_base.num / p_sys->ic->chapters[i]->time_base.den - (i_start_time != -1 ? i_start_time : 0 ); TAB_APPEND( p_sys->p_title->i_seekpoint, p_sys->p_title->seekpoint, s ); } ResetTime( p_demux, 0 ); return VLC_SUCCESS; }
/**************************************************************************** * * Functions to read chunks * ****************************************************************************/ static int AVI_ChunkRead_list( stream_t *s, avi_chunk_t *p_container ) { avi_chunk_t *p_chk; const uint8_t *p_peek; bool b_seekable; int i_ret = VLC_SUCCESS; if( p_container->common.i_chunk_size > 0 && p_container->common.i_chunk_size < 4 ) { /* empty box */ msg_Warn( (vlc_object_t*)s, "empty list chunk" ); return VLC_EGENERIC; } if( vlc_stream_Peek( s, &p_peek, 12 ) < 12 ) { msg_Warn( (vlc_object_t*)s, "cannot peek while reading list chunk" ); return VLC_EGENERIC; } vlc_stream_Control( s, STREAM_CAN_SEEK, &b_seekable ); p_container->list.i_type = GetFOURCC( p_peek + 8 ); /* XXX fixed for on2 hack */ if( p_container->common.i_chunk_fourcc == AVIFOURCC_ON2 && p_container->list.i_type == AVIFOURCC_ON2f ) { p_container->common.i_chunk_fourcc = AVIFOURCC_RIFF; p_container->list.i_type = AVIFOURCC_AVI; } if( p_container->common.i_chunk_fourcc == AVIFOURCC_LIST && p_container->list.i_type == AVIFOURCC_movi ) { if( !b_seekable ) return VLC_SUCCESS; msg_Dbg( (vlc_object_t*)s, "skipping movi chunk" ); return AVI_NextChunk( s, p_container ); /* points at begining of LIST-movi if not seekable */ } if( vlc_stream_Read( s, NULL, 12 ) != 12 ) { msg_Warn( (vlc_object_t*)s, "cannot enter chunk" ); return VLC_EGENERIC; } #ifdef AVI_DEBUG msg_Dbg( (vlc_object_t*)s, "found LIST chunk: \'%4.4s\'", (char*)&p_container->list.i_type ); #endif msg_Dbg( (vlc_object_t*)s, "<list \'%4.4s\'>", (char*)&p_container->list.i_type ); for( ; ; ) { p_chk = xmalloc( sizeof( avi_chunk_t ) ); memset( p_chk, 0, sizeof( avi_chunk_t ) ); if( !p_container->common.p_first ) { p_container->common.p_first = p_chk; } else { p_container->common.p_last->common.p_next = p_chk; } p_container->common.p_last = p_chk; i_ret = AVI_ChunkRead( s, p_chk, p_container ); if( i_ret ) { break; } if( p_chk->common.p_father->common.i_chunk_size > 0 && ( vlc_stream_Tell( s ) > (off_t)p_chk->common.p_father->common.i_chunk_pos + (off_t)__EVEN( p_chk->common.p_father->common.i_chunk_size ) ) ) { break; } /* If we can't seek then stop when we 've found LIST-movi */ if( p_chk->common.i_chunk_fourcc == AVIFOURCC_LIST && p_chk->list.i_type == AVIFOURCC_movi && ( !b_seekable || p_chk->common.i_chunk_size == 0 ) ) { break; } } msg_Dbg( (vlc_object_t*)s, "</list \'%4.4s\'>", (char*)&p_container->list.i_type ); if ( i_ret == AVI_ZERO_FOURCC ) return i_ret; return VLC_SUCCESS; }
/** * Pipe data through an external executable. * @param stream the stream filter object. * @param path path to the executable. */ static int Open (stream_t *stream, const char *path) { stream_sys_t *p_sys = stream->p_sys = malloc (sizeof (*p_sys)); if (p_sys == NULL) return VLC_ENOMEM; vlc_cond_init (&p_sys->wait); vlc_mutex_init (&p_sys->lock); p_sys->paused = false; p_sys->pid = -1; vlc_stream_Control(stream->p_source, STREAM_CAN_PAUSE, &p_sys->can_pause); vlc_stream_Control(stream->p_source, STREAM_CAN_CONTROL_PACE, &p_sys->can_pace); vlc_stream_Control(stream->p_source, STREAM_GET_PTS_DELAY, &p_sys->pts_delay); /* I am not a big fan of the pyramid style, but I cannot think of anything * better here. There are too many failure cases. */ int ret = VLC_EGENERIC; int comp[2]; /* We use two pipes rather than one stream socket pair, so that we can * use vmsplice() on Linux. */ if (vlc_pipe (comp) == 0) { p_sys->write_fd = comp[1]; int uncomp[2]; if (vlc_pipe (uncomp) == 0) { p_sys->read_fd = uncomp[0]; #if (_POSIX_SPAWN >= 0) posix_spawn_file_actions_t actions; if (posix_spawn_file_actions_init (&actions) == 0) { char *const argv[] = { (char *)path, NULL }; if (!posix_spawn_file_actions_adddup2 (&actions, comp[0], 0) && !posix_spawn_file_actions_adddup2 (&actions, uncomp[1], 1) && !posix_spawnp (&p_sys->pid, path, &actions, NULL, argv, environ)) { if (vlc_clone (&p_sys->thread, Thread, stream, VLC_THREAD_PRIORITY_INPUT) == 0) ret = VLC_SUCCESS; } else { msg_Err (stream, "cannot execute %s", path); p_sys->pid = -1; } posix_spawn_file_actions_destroy (&actions); } #else /* _POSIX_SPAWN */ switch (p_sys->pid = fork ()) { case -1: msg_Err (stream, "cannot fork: %s", vlc_strerror_c(errno)); break; case 0: dup2 (comp[0], 0); dup2 (uncomp[1], 1); execlp (path, path, (const char *)NULL); exit (1); /* if we get, execlp() failed! */ default: if (vlc_clone (&p_sys->thread, Thread, stream, VLC_THREAD_PRIORITY_INPUT) == 0) ret = VLC_SUCCESS; } #endif /* _POSIX_SPAWN < 0 */ vlc_close (uncomp[1]); if (ret != VLC_SUCCESS) vlc_close (uncomp[0]); } vlc_close (comp[0]); if (ret != VLC_SUCCESS) vlc_close (comp[1]); } if (ret != VLC_SUCCESS) { if (p_sys->pid != -1) while (waitpid (p_sys->pid, &(int){ 0 }, 0) == -1); vlc_mutex_destroy (&p_sys->lock); vlc_cond_destroy (&p_sys->wait); free (p_sys); return ret; }