/** * Create a playlist node * * \param p_playlist the playlist * \param psz_name the name of the node * \param p_parent the parent node to attach to or NULL if no attach * \param i_pos position of the node in the parent, PLAYLIST_END to append to end. * \param p_flags miscellaneous flags * \param p_input the input_item to attach to or NULL if it has to be created * \return the new node */ playlist_item_t * playlist_NodeCreate( playlist_t *p_playlist, const char *psz_name, playlist_item_t *p_parent, int i_pos, int i_flags, input_item_t *p_input ) { input_item_t *p_new_input = NULL; playlist_item_t *p_item; PL_ASSERT_LOCKED; if( !psz_name ) psz_name = _("Undefined"); if( !p_input ) p_new_input = input_item_NewWithType( NULL, psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE ); p_item = playlist_ItemNewFromInput( p_playlist, p_input ? p_input : p_new_input ); if( p_new_input ) vlc_gc_decref( p_new_input ); if( p_item == NULL ) return NULL; p_item->i_children = 0; ARRAY_APPEND(p_playlist->all_items, p_item); if( p_parent != NULL ) playlist_NodeInsert( p_playlist, p_item, p_parent, i_pos == PLAYLIST_END ? -1 : i_pos ); playlist_SendAddNotify( p_playlist, p_item->i_id, p_parent ? p_parent->i_id : -1, !( i_flags & PLAYLIST_NO_REBUILD )); p_item->i_flags |= i_flags; return p_item; }
/** * Probes and initializes. */ static int Open (vlc_object_t *obj) { services_discovery_t *sd = (services_discovery_t *)obj; LONG drives = GetLogicalDrives (); char mrl[12] = "file:///A:/", name[3] = "A:"; TCHAR path[4] = "A:\\"; for (char d = 0; d < 26; d++) { input_item_t *item; char letter = 'A' + d; /* Does this drive actually exist? */ if (!(drives & (1 << d))) continue; /* Is it a disc drive? */ path[0] = letter; if (GetDriveType (path) != DRIVE_CDROM) continue; mrl[8] = name[0] = letter; item = input_item_NewWithType (VLC_OBJECT (sd), mrl, name, 0, NULL, 0, -1, ITEM_TYPE_DISC); msg_Dbg (sd, "adding %s (%s)", mrl, name); if (item == NULL) break; services_discovery_AddItem (sd, item, _("Local drives")); } return VLC_SUCCESS; }
input_item_t *__input_item_NewExt( vlc_object_t *p_obj, const char *psz_uri, const char *psz_name, int i_options, const char *const *ppsz_options, unsigned i_option_flags, mtime_t i_duration ) { return input_item_NewWithType( p_obj, psz_uri, psz_name, i_options, ppsz_options, i_option_flags, i_duration, ITEM_TYPE_UNKNOWN ); }
static int vlclua_node_add_node( lua_State *L ) { services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L ); input_item_t **pp_node = (input_item_t **)luaL_checkudata( L, 1, "node" ); if( *pp_node ) { if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "title" ); if( lua_isstring( L, -1 ) ) { const char *psz_name = lua_tostring( L, -1 ); input_item_node_t *p_input_node = input_item_node_Create( *pp_node ); input_item_t *p_input = input_item_NewWithType( VLC_OBJECT( p_sd ), "vlc://nop", psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE ); lua_pop( L, 1 ); if( p_input ) { lua_getfield( L, -1, "arturl" ); if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) ) { char *psz_value = strdup( lua_tostring( L, -1 ) ); EnsureUTF8( psz_value ); msg_Dbg( p_sd, "ArtURL: %s", psz_value ); input_item_SetArtURL( p_input, psz_value ); free( psz_value ); } input_item_node_AppendItem( p_input_node, p_input ); input_item_node_PostAndDelete( p_input_node ); input_item_t **udata = (input_item_t **) lua_newuserdata( L, sizeof( input_item_t * ) ); *udata = p_input; if( luaL_newmetatable( L, "node" ) ) { lua_newtable( L ); luaL_register( L, NULL, vlclua_node_reg ); lua_setfield( L, -2, "__index" ); } lua_setmetatable( L, -2 ); } } else msg_Err( p_sd, "node:add_node: the \"title\" parameter can't be empty" ); } else msg_Err( p_sd, "Error parsing add_node arguments" ); } return 1; }
static int vlclua_sd_add_node( lua_State *L ) { services_discovery_t *p_sd = (services_discovery_t *)vlclua_get_this( L ); if( lua_istable( L, -1 ) ) { lua_getfield( L, -1, "title" ); if( lua_isstring( L, -1 ) ) { const char *psz_name = lua_tostring( L, -1 ); input_item_t *p_input = input_item_NewWithType( "vlc://nop", psz_name, 0, NULL, 0, -1, ITEM_TYPE_NODE ); lua_pop( L, 1 ); if( p_input ) { lua_getfield( L, -1, "arturl" ); if( lua_isstring( L, -1 ) && strcmp( lua_tostring( L, -1 ), "" ) ) { char *psz_value = strdup( lua_tostring( L, -1 ) ); EnsureUTF8( psz_value ); msg_Dbg( p_sd, "ArtURL: %s", psz_value ); /** @todo Ask for art download if not local file */ input_item_SetArtURL( p_input, psz_value ); free( psz_value ); } lua_pop( L, 1 ); lua_getfield( L, -1, "category" ); if( lua_isstring( L, -1 ) ) services_discovery_AddItem( p_sd, p_input, luaL_checkstring( L, -1 ) ); else services_discovery_AddItem( p_sd, p_input, NULL ); input_item_t **udata = (input_item_t **) lua_newuserdata( L, sizeof( input_item_t * ) ); *udata = p_input; if( luaL_newmetatable( L, "node" ) ) { lua_newtable( L ); luaL_register( L, NULL, vlclua_node_reg ); lua_setfield( L, -2, "__index" ); } lua_setmetatable( L, -2 ); } } else msg_Err( p_sd, "vlc.sd.add_node: the \"title\" parameter can't be empty" ); } else msg_Err( p_sd, "Error parsing add_node arguments" ); return 1; }
int SdOpen (vlc_object_t *p_this) { services_discovery_t *p_sd = (services_discovery_t *)p_this; services_discovery_sys_t *p_sys = malloc (sizeof (*p_sys)); if( p_sys == NULL ) return VLC_ENOMEM; p_sd->p_sys = p_sys; /* Let's create a NETBIOS name service object */ p_sys->ns = netbios_ns_new(); if( p_sys->ns == NULL ) goto error; if( !netbios_ns_discover( p_sys->ns ) ) goto error; for( ssize_t i = 0; i < netbios_ns_entry_count( p_sys->ns ); i++ ) { netbios_ns_entry *p_entry = netbios_ns_entry_at( p_sys->ns, i ); if( p_entry->type == 0x20 ) { input_item_t *p_item; char *psz_mrl; if( asprintf(&psz_mrl, "smb://%s", p_entry->name) < 0 ) goto error; p_item = input_item_NewWithType( psz_mrl, p_entry->name, 0, NULL, 0, -1, ITEM_TYPE_NODE ); msg_Dbg( p_sd, "Adding item %s", psz_mrl ); services_discovery_AddItem( p_sd, p_item, NULL ); free( psz_mrl ); } } return VLC_SUCCESS; error: if( p_sys->ns != NULL ) netbios_ns_destroy( p_sys->ns ); free( p_sys ); p_sd->p_sys = NULL; return VLC_EGENERIC; }
/** * Adds a udev device. */ static int AddDevice (services_discovery_t *sd, struct udev_device *dev) { services_discovery_sys_t *p_sys = sd->p_sys; char *mrl = p_sys->subsys->get_mrl (dev); if (mrl == NULL) return 0; /* don't know if it was an error... */ char *name = p_sys->subsys->get_name (dev); input_item_t *item = input_item_NewWithType (VLC_OBJECT (sd), mrl, name ? name : mrl, 0, NULL, 0, -1, p_sys->subsys->item_type); msg_Dbg (sd, "adding %s (%s)", mrl, name); free (name); free (mrl); if (item == NULL) return -1; struct device *d = malloc (sizeof (*d)); if (d == NULL) { vlc_gc_decref (item); return -1; } d->devnum = udev_device_get_devnum (dev); d->item = item; d->sd = NULL; struct device **dp = tsearch (d, &p_sys->root, cmpdev); if (dp == NULL) /* Out-of-memory */ { DestroyDevice (d); return -1; } if (*dp != d) /* Overwrite existing device */ { DestroyDevice (*dp); *dp = d; } name = p_sys->subsys->get_cat (dev); services_discovery_AddItem (sd, item, name ? name : "Generic"); d->sd = sd; free (name); return 0; }
/** * Adds a source. */ static int AddSource (services_discovery_t *sd, const pa_source_info *info) { services_discovery_sys_t *sys = sd->p_sys; msg_Dbg (sd, "adding %s (%s)", info->name, info->description); char *mrl; if (unlikely(asprintf (&mrl, "pulse://%s", info->name) == -1)) return -1; input_item_t *item = input_item_NewWithType (mrl, info->description, 0, NULL, 0, -1, ITEM_TYPE_CARD); free (mrl); if (unlikely(item == NULL)) return -1; struct device *d = malloc (sizeof (*d)); if (unlikely(d == NULL)) { vlc_gc_decref (item); return -1; } d->index = info->index; d->item = item; d->sd = NULL; struct device **dp = tsearch (d, &sys->root, cmpsrc); if (dp == NULL) /* Out-of-memory */ { DestroySource (d); return -1; } if (*dp != d) /* Replace existing source */ { DestroySource (*dp); *dp = d; } const char *card = pa_proplist_gets(info->proplist, "device.product.name"); services_discovery_AddItem (sd, item, (card != NULL) ? card : N_("Generic")); d->sd = sd; return 0; }
static int GetTracks( access_t *p_access, input_item_t *p_current ) { access_sys_t *p_sys = p_access->p_sys; const int i_titles = ioctl_GetTracksMap( VLC_OBJECT(p_access), p_sys->vcddev, &p_sys->p_sectors ); if( i_titles <= 0 ) { if( i_titles < 0 ) msg_Err( p_access, "unable to count tracks" ); else if( i_titles <= 0 ) msg_Err( p_access, "no audio tracks found" ); return VLC_EGENERIC;; } /* */ input_item_SetName( p_current, "Audio CD" ); const char *psz_album = NULL; const char *psz_year = NULL; const char *psz_genre = NULL; const char *psz_artist = NULL; const char *psz_description = NULL; /* Return true if the given string is not NULL and not empty */ #define NONEMPTY( psz ) ( (psz) && *(psz) ) /* If the given string is NULL or empty, fill it by the return value of 'code' */ #define ON_EMPTY( psz, code ) do { if( !NONEMPTY( psz) ) { (psz) = code; } } while(0) /* Retreive CDDB information */ #ifdef HAVE_LIBCDDB char psz_year_buffer[4+1]; msg_Dbg( p_access, "fetching infos with CDDB" ); cddb_disc_t *p_disc = GetCDDBInfo( p_access, i_titles, p_sys->p_sectors ); if( p_disc ) { psz_album = cddb_disc_get_title( p_disc ); psz_genre = cddb_disc_get_genre( p_disc ); /* */ const unsigned i_year = cddb_disc_get_year( p_disc ); if( i_year > 0 ) { psz_year = psz_year_buffer; snprintf( psz_year_buffer, sizeof(psz_year_buffer), "%u", i_year ); } /* Set artist only if unique */ for( int i = 0; i < i_titles; i++ ) { cddb_track_t *t = cddb_disc_get_track( p_disc, i ); if( !t ) continue; const char *psz_track_artist = cddb_track_get_artist( t ); if( psz_artist && psz_track_artist && strcmp( psz_artist, psz_track_artist ) ) { psz_artist = NULL; break; } psz_artist = psz_track_artist; } } #endif /* CD-Text */ vlc_meta_t **pp_cd_text; int i_cd_text; if( ioctl_GetCdText( VLC_OBJECT(p_access), p_sys->vcddev, &pp_cd_text, &i_cd_text ) ) { msg_Dbg( p_access, "CD-TEXT information missing" ); i_cd_text = 0; pp_cd_text = NULL; } /* Retrieve CD-TEXT information but prefer CDDB */ if( i_cd_text > 0 && pp_cd_text[0] ) { const vlc_meta_t *p_disc = pp_cd_text[0]; ON_EMPTY( psz_album, vlc_meta_Get( p_disc, vlc_meta_Album ) ); ON_EMPTY( psz_genre, vlc_meta_Get( p_disc, vlc_meta_Genre ) ); ON_EMPTY( psz_artist, vlc_meta_Get( p_disc, vlc_meta_Artist ) ); ON_EMPTY( psz_description, vlc_meta_Get( p_disc, vlc_meta_Description ) ); } if( NONEMPTY( psz_album ) ) { input_item_SetName( p_current, psz_album ); input_item_SetAlbum( p_current, psz_album ); } if( NONEMPTY( psz_genre ) ) input_item_SetGenre( p_current, psz_genre ); if( NONEMPTY( psz_artist ) ) input_item_SetArtist( p_current, psz_artist ); if( NONEMPTY( psz_year ) ) input_item_SetDate( p_current, psz_year ); if( NONEMPTY( psz_description ) ) input_item_SetDescription( p_current, psz_description ); const mtime_t i_duration = (int64_t)( p_sys->p_sectors[i_titles] - p_sys->p_sectors[0] ) * CDDA_DATA_SIZE * 1000000 / 44100 / 2 / 2; input_item_SetDuration( p_current, i_duration ); input_item_node_t *p_root = input_item_node_Create( p_current ); /* Build title table */ for( int i = 0; i < i_titles; i++ ) { input_item_t *p_input_item; char *psz_uri, *psz_opt, *psz_first, *psz_last; char *psz_name; msg_Dbg( p_access, "track[%d] start=%d", i, p_sys->p_sectors[i] ); /* */ if( asprintf( &psz_uri, "cdda://%s", p_access->psz_location ) == -1 ) psz_uri = NULL; if( asprintf( &psz_opt, "cdda-track=%i", i+1 ) == -1 ) psz_opt = NULL; if( asprintf( &psz_first, "cdda-first-sector=%i",p_sys->p_sectors[i] ) == -1 ) psz_first = NULL; if( asprintf( &psz_last, "cdda-last-sector=%i", p_sys->p_sectors[i+1] ) == -1 ) psz_last = NULL; /* Define a "default name" */ if( asprintf( &psz_name, _("Audio CD - Track %02i"), (i+1) ) == -1 ) psz_name = NULL; /* Create playlist items */ const mtime_t i_duration = (int64_t)( p_sys->p_sectors[i+1] - p_sys->p_sectors[i] ) * CDDA_DATA_SIZE * 1000000 / 44100 / 2 / 2; p_input_item = input_item_NewWithType( psz_uri, psz_name, 0, NULL, 0, i_duration, ITEM_TYPE_DISC ); input_item_CopyOptions( p_current, p_input_item ); input_item_AddOption( p_input_item, psz_first, VLC_INPUT_OPTION_TRUSTED ); input_item_AddOption( p_input_item, psz_last, VLC_INPUT_OPTION_TRUSTED ); input_item_AddOption( p_input_item, psz_opt, VLC_INPUT_OPTION_TRUSTED ); const char *psz_track_title = NULL; const char *psz_track_artist = NULL; const char *psz_track_genre = NULL; const char *psz_track_description = NULL; #ifdef HAVE_LIBCDDB /* Retreive CDDB information */ if( p_disc ) { cddb_track_t *t = cddb_disc_get_track( p_disc, i ); if( t != NULL ) { psz_track_title = cddb_track_get_title( t ); psz_track_artist = cddb_track_get_artist( t ); } } #endif /* Retreive CD-TEXT information but prefer CDDB */ if( i+1 < i_cd_text && pp_cd_text[i+1] ) { const vlc_meta_t *t = pp_cd_text[i+1]; ON_EMPTY( psz_track_title, vlc_meta_Get( t, vlc_meta_Title ) ); ON_EMPTY( psz_track_artist, vlc_meta_Get( t, vlc_meta_Artist ) ); ON_EMPTY( psz_track_genre, vlc_meta_Get( t, vlc_meta_Genre ) ); ON_EMPTY( psz_track_description, vlc_meta_Get( t, vlc_meta_Description ) ); } /* */ ON_EMPTY( psz_track_artist, psz_artist ); ON_EMPTY( psz_track_genre, psz_genre ); ON_EMPTY( psz_track_description, psz_description ); /* */ if( NONEMPTY( psz_track_title ) ) { input_item_SetName( p_input_item, psz_track_title ); input_item_SetTitle( p_input_item, psz_track_title ); } if( NONEMPTY( psz_track_artist ) ) input_item_SetArtist( p_input_item, psz_track_artist ); if( NONEMPTY( psz_track_genre ) ) input_item_SetGenre( p_input_item, psz_track_genre ); if( NONEMPTY( psz_track_description ) ) input_item_SetDescription( p_input_item, psz_track_description ); if( NONEMPTY( psz_album ) ) input_item_SetAlbum( p_input_item, psz_album ); if( NONEMPTY( psz_year ) ) input_item_SetDate( p_input_item, psz_year ); char psz_num[3+1]; snprintf( psz_num, sizeof(psz_num), "%d", 1+i ); input_item_SetTrackNum( p_input_item, psz_num ); input_item_node_AppendItem( p_root, p_input_item ); vlc_gc_decref( p_input_item ); free( psz_uri ); free( psz_opt ); free( psz_name ); free( psz_first ); free( psz_last ); } #undef ON_EMPTY #undef NONEMPTY input_item_node_PostAndDelete( p_root ); /* */ for( int i = 0; i < i_cd_text; i++ ) { vlc_meta_t *p_meta = pp_cd_text[i]; if( !p_meta ) continue; vlc_meta_Delete( p_meta ); } free( pp_cd_text ); #ifdef HAVE_LIBCDDB if( p_disc ) cddb_disc_destroy( p_disc ); #endif return VLC_SUCCESS; }
/***************************************************************************** * Demux: reads and demuxes data packets ***************************************************************************** * Returns -1 in case of error, 0 in case of EOF, 1 otherwise *****************************************************************************/ static int Demux ( demux_t *p_demux ) { demux_sys_t *p_sys = p_demux->p_sys; input_item_t *p_child = NULL; char *psz_line; input_item_t *p_current_input = GetCurrentItem(p_demux); while( ( psz_line = stream_ReadLine( p_demux->s ) ) ) { ParseLine( p_demux, psz_line ); free( psz_line ); } if( p_sys->psz_mcast_ip ) { /* Definetly schedules multicast session */ /* We don't care if it's live or not */ free( p_sys->psz_uri ); if( asprintf( &p_sys->psz_uri, "udp://@" "%s:%i", p_sys->psz_mcast_ip, p_sys->i_mcast_port ) == -1 ) { p_sys->psz_uri = NULL; return -1; } } if( p_sys->psz_uri == NULL ) { if( p_sys->psz_server && p_sys->psz_location ) { if( asprintf( &p_sys->psz_uri, "rtsp://" "%s:%i%s", p_sys->psz_server, p_sys->i_port > 0 ? p_sys->i_port : 554, p_sys->psz_location ) == -1 ) { p_sys->psz_uri = NULL; return -1; } } } if( p_sys->b_concert ) { /* It's definetly a simulcasted scheduled stream */ /* We don't care if it's live or not */ if( p_sys->psz_uri == NULL ) { msg_Err( p_demux, "no URI was found" ); return -1; } char *uri; if( asprintf( &uri, "%s%%3FMeDiAbAsEshowingId=%d%%26MeDiAbAsEconcert" "%%3FMeDiAbAsE", p_sys->psz_uri, p_sys->i_sid ) == -1 ) return -1; free( p_sys->psz_uri ); p_sys->psz_uri = uri; } p_child = input_item_NewWithType( p_sys->psz_uri, p_sys->psz_name ? p_sys->psz_name : p_sys->psz_uri, 0, NULL, 0, p_sys->i_duration, ITEM_TYPE_NET ); if( !p_child ) { msg_Err( p_demux, "A valid playlistitem could not be created" ); return -1; } input_item_CopyOptions( p_current_input, p_child ); if( p_sys->i_packet_size && p_sys->psz_mcast_ip ) { char *psz_option; p_sys->i_packet_size += 1000; if( asprintf( &psz_option, "mtu=%i", p_sys->i_packet_size ) != -1 ) { input_item_AddOption( p_child, psz_option, VLC_INPUT_OPTION_TRUSTED ); free( psz_option ); } } if( !p_sys->psz_mcast_ip ) input_item_AddOption( p_child, "rtsp-caching=5000", VLC_INPUT_OPTION_TRUSTED ); if( !p_sys->psz_mcast_ip && p_sys->b_rtsp_kasenna ) input_item_AddOption( p_child, "rtsp-kasenna", VLC_INPUT_OPTION_TRUSTED ); input_item_PostSubItem( p_current_input, p_child ); vlc_gc_decref( p_child ); vlc_gc_decref(p_current_input); return 0; /* Needed for correct operation of go back */ }
/* http://www.linuxtv.org/vdrwiki/index.php/Syntax_of_channels.conf or not... * Read the dvb-apps source code for reference. */ static input_item_t *ParseLine(char *line) { char *str, *end; line += strspn(line, " \t\r"); /* skip leading white spaces */ if (*line == '#') return NULL; /* skip comments */ /* Extract channel cute name */ char *name = strsep(&line, ":"); assert(name != NULL); EnsureUTF8(name); /* Extract central frequency */ str = strsep(&line, ":"); if (str == NULL) return NULL; unsigned long freq = strtoul(str, &end, 10); if (*end) return NULL; /* Extract tuning parameters */ str = strsep(&line, ":"); if (str == NULL) return NULL; char *mrl; if (!strcmp(str, "h") || !strcmp(str, "v")) { /* DVB-S */ char polarization = toupper(*str); /* TODO: sat no. */ str = strsep(&line, ":"); if (str == NULL) return NULL; /* baud rate */ str = strsep(&line, ":"); if (str == NULL) return NULL; unsigned long rate = strtoul(str, &end, 10); if (*end || rate > (ULONG_MAX / 1000u)) return NULL; rate *= 1000; if (asprintf(&mrl, "dvb-s://frequency=%"PRIu64":polarization=%c:srate=%lu", freq * UINT64_C(1000000), polarization, rate) == -1) mrl = NULL; } else if (!strncmp(str, "INVERSION_", 10)) { /* DVB-C or DVB-T */ int inversion; str += 10; if (strcmp(str, "AUTO")) inversion = -1; else if (strcmp(str, "OFF")) inversion = 0; else if (strcmp(str, "ON")) inversion = 1; else return NULL; str = strsep(&line, ":"); if (str == NULL) return NULL; if (strncmp(str, "BANDWIDTH_", 10)) { /* DVB-C */ unsigned long rate = strtoul(str, &end, 10); if (*end) return NULL; str = strsep(&line, ":"); const char *fec = ParseFEC(str); str = strsep(&line, ":"); const char *mod = ParseModulation(str); if (fec == NULL || mod == NULL) return NULL; if (asprintf(&mrl, "dvb-c://frequency=%lu:inversion:%d:srate=%lu:" "fec=%s:modulation=%s", freq, inversion, rate, fec, mod) == -1) mrl = NULL; } else { /* DVB-T */ unsigned bandwidth = atoi(str + 10); str = strsep(&line, ":"); const char *hp = ParseFEC(str); str = strsep(&line, ":"); const char *lp = ParseFEC(str); str = strsep(&line, ":"); const char *mod = ParseModulation(str); if (hp == NULL || lp == NULL || mod == NULL) return NULL; str = strsep(&line, ":"); if (str == NULL || strncmp(str, "TRANSMISSION_MODE_", 18)) return NULL; int xmit = atoi(str); if (xmit == 0) xmit = -1; /* AUTO */ str = strsep(&line, ":"); const char *guard = ParseGuard(str); if (guard == NULL) return NULL; str = strsep(&line, ":"); if (str == NULL || strncmp(str, "HIERARCHY_", 10)) return NULL; str += 10; int hierarchy = atoi(str); if (!strcmp(str, "AUTO")) hierarchy = -1; if (asprintf(&mrl, "dvb-t://frequency=%lu:inversion=%d:" "bandwidth=%u:code-rate-hp=%s:code-rate-lp=%s:" "modulation=%s:transmission=%d:guard=%s:" "hierarchy=%d", freq, inversion, bandwidth, hp, lp, mod, xmit, guard, hierarchy) == -1) mrl = NULL; } } else { /* ATSC */ const char *mod = ParseModulation(str); if (mod == NULL) return NULL; if (asprintf(&mrl, "atsc://frequency=%lu:modulation=%s", freq, mod) == -1) mrl = NULL; } if (unlikely(mrl == NULL)) return NULL; /* Video PID (TODO? set video track) */ strsep(&line, ":"); /* Audio PID (TODO? set audio track) */ strsep(&line, ":"); /* Extract SID */ str = strsep(&line, ":"); if (str == NULL) { free(mrl); return NULL; } unsigned long sid = strtoul(str, &end, 10); if (*end || sid > 65535) { free(mrl); return NULL; } char sid_opt[sizeof("program=65535")]; snprintf(sid_opt, sizeof(sid_opt), "program=%lu", sid); const char *opts[] = { sid_opt }; input_item_t *item = input_item_NewWithType(mrl, name, 1, opts, 0, -1, ITEM_TYPE_CARD); free(mrl); return item; }