/** * Destroy playlist. * This is not thread-safe. Any reference to the playlist is assumed gone. * (In particular, all interface and services threads must have been joined). * * \param p_playlist the playlist object */ void playlist_Destroy( playlist_t *p_playlist ) { playlist_private_t *p_sys = pl_priv(p_playlist); /* Remove all services discovery */ playlist_ServicesDiscoveryKillAll( p_playlist ); msg_Dbg( p_playlist, "destroying" ); playlist_Deactivate( p_playlist ); if( p_sys->p_preparser ) playlist_preparser_Delete( p_sys->p_preparser ); /* Release input resources */ assert( p_sys->p_input == NULL ); input_resource_Release( p_sys->p_input_resource ); if( p_playlist->p_media_library != NULL ) playlist_MLDump( p_playlist ); PL_LOCK; /* Release the current node */ set_current_status_node( p_playlist, NULL ); /* Release the current item */ set_current_status_item( p_playlist, NULL ); PL_UNLOCK; vlc_cond_destroy( &p_sys->signal ); vlc_mutex_destroy( &p_sys->lock ); /* Remove all remaining items */ FOREACH_ARRAY( playlist_item_t *p_del, p_playlist->all_items ) free( p_del->pp_children ); vlc_gc_decref( p_del->p_input ); free( p_del ); FOREACH_END(); ARRAY_RESET( p_playlist->all_items ); FOREACH_ARRAY( playlist_item_t *p_del, p_sys->items_to_delete ) free( p_del->pp_children ); vlc_gc_decref( p_del->p_input ); free( p_del ); FOREACH_END(); ARRAY_RESET( p_sys->items_to_delete ); ARRAY_RESET( p_playlist->items ); ARRAY_RESET( p_playlist->current ); vlc_http_cookie_jar_t *cookies = var_GetAddress( p_playlist, "http-cookies" ); if ( cookies ) { var_Destroy( p_playlist, "http-cookies" ); vlc_http_cookies_destroy( cookies ); } vlc_object_release( p_playlist ); }
static int Remove( addons_storage_t *p_storage, addon_entry_t *p_entry ) { vlc_mutex_lock( &p_entry->lock ); FOREACH_ARRAY( addon_file_t *p_file, p_entry->files ) switch( p_file->e_filetype ) { case ADDON_EXTENSION: case ADDON_PLAYLIST_PARSER: case ADDON_SERVICE_DISCOVERY: case ADDON_INTERFACE: case ADDON_META: case ADDON_SKIN2: { char *psz_dest; char *psz_translated_filename = strdup( p_file->psz_filename ); if ( !psz_translated_filename ) return VLC_ENOMEM; char *tmp = psz_translated_filename; while (*tmp++) if ( *tmp == '/' ) *tmp = DIR_SEP_CHAR; char *psz_dir = getAddonInstallDir( p_file->e_filetype ); if ( !psz_dir || asprintf( &psz_dest, "%s"DIR_SEP"%s", psz_dir, psz_translated_filename ) < 1 ) { free( psz_dir ); free( psz_translated_filename ); return VLC_EGENERIC; } free( psz_dir ); free( psz_translated_filename ); vlc_unlink( psz_dest ); msg_Dbg( p_storage, "removing %s", psz_dest ); free( psz_dest ); break; } /* Ignore all other unhandled files */ case ADDON_UNKNOWN: case ADDON_PLUGIN: case ADDON_OTHER: default: break; } FOREACH_END() /* Remove file info on success */ FOREACH_ARRAY( addon_file_t *p_file, p_entry->files ) free( p_file->psz_filename ); free( p_file->psz_download_uri ); free( p_file ); FOREACH_END() ARRAY_RESET( p_entry->files ); vlc_mutex_unlock( &p_entry->lock ); return VLC_SUCCESS; }
sms_stream_t * sms_get_stream_by_cat( stream_sys_t *p_sys, int i_cat ) { assert( p_sys->sms_selected.i_size >= 0 && p_sys->sms_selected.i_size <= 3 ); FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected ); if( sms->type == i_cat ) return sms; FOREACH_END(); return NULL; }
bool no_more_chunks( stream_sys_t *p_sys ) { FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected ); if ( sms->p_playback ) { return false; } FOREACH_END(); return true; }
void ql_Free( quality_level_t *qlevel ) { free( qlevel->CodecPrivateData ); FOREACH_ARRAY( custom_attrs_t *p_attrs, qlevel->custom_attrs ) free( p_attrs->psz_key ); free( p_attrs->psz_value ); FOREACH_END() ARRAY_RESET(qlevel->custom_attrs); free( qlevel ); qlevel = NULL; }
static sms_stream_t *next_playback_stream( stream_sys_t *p_sys ) { sms_stream_t *p_candidate = NULL; FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected ); if ( !sms->p_playback ) continue; if ( p_candidate == NULL || sms->p_playback->start_time < p_candidate->p_playback->start_time ) p_candidate = sms; FOREACH_END(); return p_candidate; }
static void SysCleanup( stream_sys_t *p_sys ) { if ( p_sys->sms.i_size ) { FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms ); sms_Free( sms ); FOREACH_END(); ARRAY_RESET( p_sys->sms ); } ARRAY_RESET( p_sys->sms_selected ); if ( p_sys->playback.init.p_datachunk ) chunk_Free( p_sys->playback.init.p_datachunk ); free( p_sys->download.base_url ); }
void resetChunksState( stream_sys_t *p_sys ) { FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected ); vlc_mutex_lock( &sms->chunks_lock ); chunk_t *p_chunk = sms->p_playback; while( p_chunk ) { FREENULL( p_chunk->data ); p_chunk->offset = CHUNK_OFFSET_UNSET; p_chunk->size = 0; p_chunk->read_pos = 0; if ( p_chunk == sms->p_nextdownload ) break; p_chunk = p_chunk->p_next; } sms->p_playback = NULL; sms->p_nextdownload = NULL; vlc_mutex_unlock( &sms->chunks_lock ); FOREACH_END(); }
void sms_Free( sms_stream_t *sms ) { if ( !sms ) return; FOREACH_ARRAY( quality_level_t *qlevel, sms->qlevels ); if( qlevel ) ql_Free( qlevel ); FOREACH_END(); ARRAY_RESET( sms->qlevels ); vlc_mutex_lock( &sms->chunks_lock ); while( sms->p_chunks ) { chunk_t *p_chunk = sms->p_chunks; sms->p_chunks = sms->p_chunks->p_next; chunk_Free( p_chunk ); } vlc_mutex_unlock( &sms->chunks_lock ); vlc_mutex_destroy( &sms->chunks_lock ); free( sms->name ); free( sms->url_template ); free( sms ); }
static int WriteCatalog( addons_storage_t *p_storage, addon_entry_t **pp_entries, int i_entries ) { addon_entry_t *p_entry; char *psz_file; char *psz_file_tmp; char *psz_tempstring; char *psz_userdir = config_GetUserDir( VLC_DATA_DIR ); if ( !psz_userdir ) return VLC_ENOMEM; if ( asprintf( &psz_file, "%s%s", psz_userdir, ADDONS_CATALOG ) < 1 ) { free( psz_userdir ); return VLC_ENOMEM; } free( psz_userdir ); if ( asprintf( &psz_file_tmp, "%s.tmp%"PRIu32, psz_file, (uint32_t)getpid() ) < 1 ) { free( psz_file ); return VLC_ENOMEM; } char *psz_path = strdup( psz_file ); if ( !psz_path ) { free( psz_file ); free( psz_file_tmp ); return VLC_ENOMEM; } char *psz_buf = strrchr( psz_path, DIR_SEP_CHAR ); if( psz_buf ) { *++psz_buf = '\0'; /* ensure directory exists */ if( !EMPTY_STR( psz_path ) ) recursive_mkdir( VLC_OBJECT(p_storage), psz_path ); } free( psz_path ); FILE *p_catalog = vlc_fopen( psz_file_tmp, "wt" ); if ( !p_catalog ) { free( psz_file ); free( psz_file_tmp ); return VLC_EGENERIC; } /* write XML header */ fprintf( p_catalog, "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\n" ); fprintf( p_catalog, "<videolan xmlns=\"http://videolan.org/ns/vlc/addons/1.0\">\n" ); fprintf( p_catalog, "\t<addons>\n" ); for ( int i=0; i<i_entries; i++ ) { p_entry = pp_entries[i]; vlc_mutex_lock( &p_entry->lock ); psz_tempstring = NULL; if ( ( p_entry->e_state != ADDON_INSTALLED ) || !( p_entry->e_flags & ADDON_MANAGEABLE ) ) { vlc_mutex_unlock( &p_entry->lock ); continue; } if ( p_entry->psz_source_module ) psz_tempstring = vlc_xml_encode( p_entry->psz_source_module ); char *psz_uuid = addons_uuid_to_psz( ( const addon_uuid_t * ) & p_entry->uuid ); fprintf( p_catalog, "\t\t<addon source=\"%s\" type=\"%s\" id=\"%s\" " "downloads=\"%ld\" score=\"%d\"", ( psz_tempstring ) ? psz_tempstring : "", getTypePsz( p_entry->e_type ), psz_uuid, p_entry->i_downloads, p_entry->i_score ); free( psz_uuid ); free( psz_tempstring ); WRITE_WITH_ENTITIES( " version=\"%s\"", p_entry->psz_version ) fprintf( p_catalog, ">\n" ); WRITE_WITH_ENTITIES( "\t\t\t<name>%s</name>\n", p_entry->psz_name ) WRITE_WITH_ENTITIES( "\t\t\t<summary>%s</summary>\n", p_entry->psz_summary ) if ( p_entry->psz_description ) { psz_tempstring = p_entry->psz_description; /* FIXME: do real escaping */ while( ( psz_tempstring = strstr( psz_tempstring, "]]>" ) ) ) *psz_tempstring = ' '; fprintf( p_catalog, "\t\t\t<description><![CDATA[%s]]></description>\n", p_entry->psz_description ); } WRITE_WITH_ENTITIES( "\t\t\t<image>%s</image>\n", p_entry->psz_image_data ) WRITE_WITH_ENTITIES( "\t\t\t<archive>%s</archive>\n", p_entry->psz_archive_uri ) fprintf( p_catalog, "\t\t\t<authorship>\n" ); WRITE_WITH_ENTITIES( "\t\t\t\t<creator>%s</creator>\n", p_entry->psz_author ) WRITE_WITH_ENTITIES( "\t\t\t\t<sourceurl>%s</sourceurl>\n", p_entry->psz_source_uri ) fprintf( p_catalog, "\t\t\t</authorship>\n" ); FOREACH_ARRAY( addon_file_t *p_file, p_entry->files ) psz_tempstring = vlc_xml_encode( p_file->psz_filename ); fprintf( p_catalog, "\t\t\t<resource type=\"%s\">%s</resource>\n", getTypePsz( p_file->e_filetype ), psz_tempstring ); free( psz_tempstring ); FOREACH_END(); fprintf( p_catalog, "\t\t</addon>\n" ); vlc_mutex_unlock( &p_entry->lock ); } fprintf( p_catalog, "\t</addons>\n" ); fprintf( p_catalog, "</videolan>\n" ); fclose( p_catalog ); int i_ret = vlc_rename( psz_file_tmp, psz_file ); free( psz_file ); free( psz_file_tmp ); if( i_ret == -1 ) { msg_Err( p_storage, "could not rename temp catalog: %s", vlc_strerror_c(errno) ); return VLC_EGENERIC; } return VLC_SUCCESS; }
/* Normaly a stream_filter is not able to provide *time* seeking, since a * stream_filter operates on a byte stream. Thus, in order to circumvent this * limitation, I treat a STREAM_SET_POSITION request which value "pos" is less * than FAKE_STREAM_SIZE as a *time* seek request, and more precisely a request * to jump at time position: pos / FAKE_STREAM_SIZE * total_video_duration. * For exemple, it pos == 500, it would be interpreted as a request to jump at * the middle of the video. * If pos > 1000, it would be treated as a normal byte seek request. That means * the demux is not able to request a byte seek with 0 <= pos <= 1000 * (unless it is in the current chunk), but that doesn't matter in practice. * Of course this a bit hack-ish, but if Smooth Streaming doesn't die, its * implementation will be moved to a access_demux module, and this hack won't * be needed anymore (among others). */ static int chunk_Seek( stream_t *s, const uint64_t pos ) { stream_sys_t *p_sys = s->p_sys; if( pos == p_sys->playback.boffset ) return VLC_SUCCESS; chunk_t *chunk = get_chunk( s, false, NULL ); if( chunk == NULL ) return VLC_EGENERIC; assert( chunk->offset != CHUNK_OFFSET_UNSET ); uint64_t i_chunkspos = CHUNK_OFFSET_0 + pos; bool inside_chunk = i_chunkspos >= chunk->offset && i_chunkspos < (chunk->offset + chunk->size); if( inside_chunk ) { chunk->read_pos = i_chunkspos - chunk->offset; p_sys->playback.boffset = pos; return VLC_SUCCESS; } else { if( p_sys->b_live ) { msg_Err( s, "Cannot seek to %"PRIu64" outside the current chunk for a live stream at %"PRIu64, pos, p_sys->playback.boffset ); return VLC_EGENERIC; } msg_Info( s, "Seeking outside the current chunk (%"PRIu64"->%"PRIu64") to %"PRIu64, chunk->offset, chunk->offset+chunk->size, pos ); assert( pos <= FAKE_STREAM_SIZE ); vlc_mutex_lock( &p_sys->lock ); resetChunksState( p_sys ); p_sys->playback.next_chunk_offset = 0; p_sys->playback.boffset = 0; p_sys->time_pos = p_sys->vod_duration * pos / FAKE_STREAM_SIZE; /* set queues heads */ FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms_selected ); vlc_mutex_lock( &sms->chunks_lock ); chunk_t *p_chunk = sms->p_chunks; while ( p_chunk ) { if ( p_chunk->start_time > p_sys->time_pos ) break; if ( p_chunk->start_time <= p_sys->time_pos && p_chunk->start_time + p_chunk->duration >= p_sys->time_pos ) { sms->p_playback = p_chunk; sms->p_nextdownload = p_chunk; break; } p_chunk = p_chunk->p_next; } vlc_mutex_unlock( &sms->chunks_lock ); FOREACH_END(); /* !set queues heads */ vlc_cond_signal( &p_sys->download.wait); vlc_mutex_unlock( &p_sys->lock ); return VLC_SUCCESS; } }
static int Open( vlc_object_t *p_this ) { stream_t *s = (stream_t*)p_this; stream_sys_t *p_sys; if( !isSmoothStreaming( s ) || s->psz_url == NULL ) return VLC_EGENERIC; msg_Info( p_this, "Smooth Streaming (%s)", s->psz_url ); s->p_sys = p_sys = calloc( 1, sizeof(*p_sys ) ); if( unlikely( p_sys == NULL ) ) return VLC_ENOMEM; char *uri = strdup( s->psz_url ); if( unlikely( uri == NULL ) ) { free( p_sys ); return VLC_ENOMEM; } /* remove the last part of the url */ char *pos = strrchr( uri, '/'); *pos = '\0'; p_sys->download.base_url = uri; ARRAY_INIT( p_sys->sms ); ARRAY_INIT( p_sys->sms_selected ); /* Parse SMS ismc content. */ if( parse_Manifest( s ) != VLC_SUCCESS ) { SysCleanup( p_sys ); free( p_sys ); return VLC_EGENERIC; } if( !p_sys->vod_duration ) p_sys->b_live = true; /* Choose first video / audio / subtitle stream available */ sms_stream_t *selected = NULL; FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms ); selected = SMS_GET_SELECTED_ST( sms->type ); if( !selected ) ARRAY_APPEND( p_sys->sms_selected, sms ); FOREACH_END(); /* Choose lowest quality for the first chunks */ FOREACH_ARRAY( sms_stream_t *sms, p_sys->sms ); quality_level_t *wanted = NULL; if ( sms->qlevels.i_size ) { wanted = sms->qlevels.p_elems[0]; for( int i=1; i < sms->qlevels.i_size; i++ ) { if( sms->qlevels.p_elems[i]->Bitrate < wanted->Bitrate ) wanted = sms->qlevels.p_elems[i]; } sms->current_qlvl = wanted; } FOREACH_END(); /* Init our playback queue */ p_sys->p_current_stream = next_playback_stream( p_sys ); if ( !p_sys->p_current_stream ) { SysCleanup( p_sys ); free( p_sys ); return VLC_EGENERIC; } p_sys->playback.next_chunk_offset = CHUNK_OFFSET_UNSET; p_sys->i_probe_length = SMS_PROBE_LENGTH; vlc_mutex_init( &p_sys->lock ); vlc_cond_init( &p_sys->download.wait ); vlc_cond_init( &p_sys->playback.wait ); vlc_mutex_init( &p_sys->playback.lock ); /* */ s->pf_read = Read; s->pf_control = Control; if( vlc_clone( &p_sys->download.thread, sms_Thread, s, VLC_THREAD_PRIORITY_INPUT ) ) { SysCleanup( p_sys ); vlc_mutex_destroy( &p_sys->lock ); vlc_cond_destroy( &p_sys->download.wait ); vlc_mutex_destroy( &p_sys->playback.lock ); vlc_cond_destroy( &p_sys->playback.wait ); free( p_sys ); return VLC_EGENERIC; } return VLC_SUCCESS; }