+Index: tvtime-1.0.2/src/tvtimeosd.c
+===================================================================
+--- tvtime-1.0.2.orig/src/tvtimeosd.c 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/src/tvtimeosd.c 2014-06-11 09:20:25.620028483 +0000
+@@ -34,6 +34,8 @@
+ #include "tvtimeosd.h"
+ #include "pulldown.h"
+
++#define OSD_MAX 16
++
+ typedef struct string_object_s
+ {
+ osd_string_t *string;
+@@ -116,6 +118,9 @@
+ char hold_message[ 255 ];
+ char hold_message2[ 255 ];
+
++ char arguments[ OSD_MAX ][ 128 ];
++ int commands[ OSD_MAX ];
++
+ int film_mode;
+ int info_available;
+ int pulldown_mode;
+@@ -859,6 +864,24 @@
+ osd_list_set_text( osd->list, line, text );
+ }
+
++void tvtime_osd_list_set_enter_command( tvtime_osd_t *osd, int line, int command,
++ const char *argument )
++{
++ osd->commands[ line ] = command;
++ snprintf( osd->arguments[ line ], sizeof( osd->arguments[ 0 ] ),
++ "%s", argument );
++}
++
++int tvtime_osd_list_get_enter_command( tvtime_osd_t *osd, int line )
++{
++ return osd->commands[ line ];
++}
++
++const char *tvtime_osd_list_get_enter_argument( tvtime_osd_t *osd, int line )
++{
++ return osd->arguments[ line ];
++}
++
+ void tvtime_osd_list_set_lines( tvtime_osd_t *osd, int numlines )
+ {
+ osd_list_set_lines( osd->list, numlines );
+Index: tvtime-1.0.2/src/tvtimeosd.h
+===================================================================
+--- tvtime-1.0.2.orig/src/tvtimeosd.h 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/src/tvtimeosd.h 2014-06-11 09:20:25.620028483 +0000
+@@ -92,6 +92,10 @@
+ int tvtime_osd_list_get_line_pos( tvtime_osd_t *osd, int y );
+ void tvtime_osd_list_set_hilight( tvtime_osd_t *osd, int pos );
+ void tvtime_osd_list_set_text( tvtime_osd_t *osd, int line, const char *text );
++void tvtime_osd_list_set_enter_command( tvtime_osd_t *osd, int line, int command,
++ const char *argument );
++int tvtime_osd_list_get_enter_command( tvtime_osd_t *osd, int line );
++const char *tvtime_osd_list_get_enter_argument( tvtime_osd_t *osd, int line );
+ void tvtime_osd_list_set_lines( tvtime_osd_t *osd, int numlines );
+ void tvtime_osd_list_get_bounding_box( tvtime_osd_t *osd, int *x, int *y,
+ int *width, int *height );
+Index: tvtime-1.0.2/src/commands.c
+===================================================================
+--- tvtime-1.0.2.orig/src/commands.c 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/src/commands.c 2014-06-11 09:20:25.624028482 +0000
+@@ -42,6 +42,7 @@
+ #include "epg.h"
+
+ #define NUM_FAVORITES 9
++#define MAX_STATIONS ( ( num_stations > 15 ) ? 15 : num_stations )
+ #define MAX_USER_MENUS 64
+
+ /* Maximum number of steps to increment sleeptimer. */
+@@ -51,6 +52,7 @@
+ {
+ MENU_REDIRECT,
+ MENU_FAVORITES,
++ MENU_STATIONS,
+ MENU_USER
+ };
+
+@@ -65,6 +67,7 @@
+ { "picture", MENU_REDIRECT, "picture-tuner" },
+ { "input", MENU_REDIRECT, "input-ntsc" },
+ { "favorites", MENU_FAVORITES, 0 },
++ { "channel-list", MENU_STATIONS, 0 },
+ };
+
+ static int tvtime_num_builtin_menus( void )
+@@ -169,6 +172,9 @@
+ int numfavorites;
+ int favorites[ NUM_FAVORITES ];
+
++ int channel_list;
++ int channel_list_active;
++
+ int epg;
+ int epg_nowandnext; // We are showing page [epg_nowandnext] of the epg
+ int epg_channel;
+@@ -1145,12 +1151,16 @@
+ cmd->numfavorites = 0;
+ memset( cmd->favorites, 0, sizeof( cmd->favorites ) );
+
++ cmd->channel_list = 0;
++ cmd->channel_list_active = 0;
++
+ cmd->epg = 0;
+ cmd->epg_nowandnext = 0;
+ cmd->epg_channel = 0;
+
+ cmd->menuactive = 0;
+ cmd->curmenu = MENU_FAVORITES;
++ cmd->curmenu = MENU_STATIONS;
+ cmd->curmenupos = 0;
+ cmd->curmenusize = 0;
+ cmd->curusermenu = 0;
+@@ -1865,6 +1875,9 @@
+ tvtime_osd_list_hold( cmd->osd, 0 );
+ tvtime_osd_show_list( cmd->osd, 0, 0 );
+ cmd->menuactive = 0;
++ if( cmd->channel_list_active ) {
++ cmd->channel_list_active = 0;
++ }
+ }
+
+ static void menu_enter( commands_t *cmd )
+@@ -1879,6 +1892,15 @@
+ }
+ }
+ menu_off( cmd );
++ } else if( cmd->curmenu == MENU_STATIONS ) {
++ int command = tvtime_osd_list_get_enter_command( cmd->osd, cmd->curmenupos + 1 );
++ const char *argument = tvtime_osd_list_get_enter_argument( cmd->osd, cmd->curmenupos + 1 );
++ if( atoi( argument ) == station_get_current_pos( cmd->stationmgr ) + 1 ) return;
++
++ /* I check for MENU_ENTER just to avoid a malicious infinite loop. */
++ if( command != TVTIME_MENU_ENTER ) {
++ commands_handle( cmd, command, argument );
++ }
+ } else if( cmd->curmenu == MENU_USER ) {
+ int command = menu_get_enter_command( cmd->curusermenu, cmd->curmenupos + 1 );
+ const char *argument = menu_get_enter_argument( cmd->curusermenu, cmd->curmenupos + 1 );
+@@ -1892,7 +1914,7 @@
+
+ static void menu_back( commands_t *cmd )
+ {
+- if( cmd->curmenu == MENU_FAVORITES ) {
++ if( ( cmd->curmenu == MENU_FAVORITES ) || ( cmd->curmenu == MENU_STATIONS ) ) {
+ commands_handle( cmd, TVTIME_SHOW_MENU, "stations" );
+ } else if( cmd->curmenu == MENU_USER ) {
+ int command = menu_get_back_command( cmd->curusermenu );
+@@ -1915,7 +1937,8 @@
+ tvtime_osd_list_set_text( cmd->osd, 0, _("Favorites") );
+ for( i = 0; i < cmd->numfavorites; i++ ) {
+ char text[ 32 ];
+- snprintf( text, sizeof (text), "%d", cmd->favorites[ i ] );
++ snprintf( text, sizeof (text), "[%d] %s", cmd->favorites[ i ],
++ station_get_name( cmd->stationmgr, ( cmd->favorites[ i ] - 1 ) ) );
+ tvtime_osd_list_set_text( cmd->osd, i + 1, text );
+ }
+ snprintf( string, sizeof( string ), TVTIME_ICON_PLUSBUTTON " %s",
+@@ -1937,6 +1960,39 @@
+ tvtime_osd_list_hold( cmd->osd, 1 );
+ }
+
++static int display_channel_list( commands_t *cmd, tvtime_osd_t* osd, int channel )
++{
++ const int num_stations = station_get_num_stations( cmd->stationmgr );
++ int i;
++
++ tvtime_osd_list_set_lines( cmd->osd, MAX_STATIONS + 1 );
++ tvtime_osd_list_set_text( cmd->osd, 0, _("Channel list \0") );
++
++ for( i = 0; i < MAX_STATIONS; i++ ) {
++ const char *pos = station_get_channel( cmd->stationmgr, i + channel - 1 );
++ char string[ 128 ];
++
++ if ( channel > num_stations ) {
++ channel = 1;
++ } else if ( channel < 1 ) {
++ channel = num_stations;
++ }
++
++ snprintf( string, sizeof( string ), "[%s] %s",
++ station_get_channel( cmd->stationmgr, i + channel - 1 ),
++ station_get_name( cmd->stationmgr, i + channel - 1 ) );
++ tvtime_osd_list_set_text( cmd->osd, i + 1, string );
++ tvtime_osd_list_set_enter_command( cmd->osd, i + 1, TVTIME_SET_STATION, pos );
++ }
++ cmd->curmenusize = MAX_STATIONS;
++
++ tvtime_osd_list_set_hilight( cmd->osd, cmd->curmenupos + 1 );
++ tvtime_osd_show_list( cmd->osd, 1, 0 );
++ tvtime_osd_list_hold( cmd->osd, 1 );
++
++ return channel;
++}
++
+ menu_t *commands_get_menu( commands_t *cmd, const char *menuname )
+ {
+ int i;
+@@ -1966,6 +2022,7 @@
+
+ static int set_menu( commands_t *cmd, const char *menuname )
+ {
++ const int num_stations = station_get_num_stations( cmd->stationmgr );
+ int i;
+
+ if( !menuname || !*menuname ) {
+@@ -1981,7 +2038,15 @@
+ return set_menu( cmd, menu_table[ i ].dest );
+ } else {
+ cmd->curmenu = menu_table[ i ].menutype;
+- cmd->curmenupos = 0;
++ if( cmd->curmenu == MENU_STATIONS ) {
++ if( station_get_current_pos( cmd->stationmgr ) < MAX_STATIONS ) {
++ cmd->curmenupos = station_get_current_pos( cmd->stationmgr );
++ } else {
++ cmd->curmenupos = MAX_STATIONS - 1;
++ }
++ } else {
++ cmd->curmenupos = 0;
++ }
+ return 1;
+ }
+ }
+@@ -2104,20 +2169,46 @@
+ if( tvtime_cmd == TVTIME_NOCOMMAND ) return;
+
+ if( cmd->menuactive && tvtime_is_menu_command( tvtime_cmd ) ) {
++ const int num_stations = station_get_num_stations( cmd->stationmgr );
++ const char *argument = tvtime_osd_list_get_enter_argument( cmd->osd, cmd->curmenupos + 1 );
+ int x, y, line;
+ switch( tvtime_cmd ) {
+ case TVTIME_MENU_EXIT:
+ menu_off( cmd );
+ break;
+ case TVTIME_MENU_UP:
+- cmd->curmenupos = (cmd->curmenupos + cmd->curmenusize - 1) % (cmd->curmenusize);
+- if( cmd->curusermenu ) menu_set_cursor( cmd->curusermenu, cmd->curmenupos );
+- display_current_menu( cmd );
++ if( cmd->channel_list_active && ( cmd->curmenupos == 0 ) ) {
++ if( !strcasecmp( argument, station_get_channel( cmd->stationmgr, 0 ) ) ) {
++ cmd->curmenupos = MAX_STATIONS - 1;
++ do {
++ cmd->channel_list = display_channel_list( cmd, cmd->osd, cmd->channel_list - 1 );
++ argument = tvtime_osd_list_get_enter_argument( cmd->osd, cmd->curmenupos + 1 );
++ } while( strcasecmp( argument, station_get_channel( cmd->stationmgr, num_stations - 1 ) ) );
++ } else {
++ cmd->channel_list = display_channel_list( cmd, cmd->osd, cmd->channel_list - 1 );
++ }
++ } else {
++ cmd->curmenupos = (cmd->curmenupos + cmd->curmenusize - 1) % (cmd->curmenusize);
++ if( cmd->curusermenu ) menu_set_cursor( cmd->curusermenu, cmd->curmenupos );
++ display_current_menu( cmd );
++ }
+ break;
+ case TVTIME_MENU_DOWN:
+- cmd->curmenupos = (cmd->curmenupos + 1) % (cmd->curmenusize);
+- if( cmd->curusermenu ) menu_set_cursor( cmd->curusermenu, cmd->curmenupos );
+- display_current_menu( cmd );
++ if( cmd->channel_list_active && ( cmd->curmenupos == MAX_STATIONS - 1 ) ) {
++ if( !strcasecmp( argument, station_get_channel( cmd->stationmgr, num_stations - 1 ) ) ) {
++ cmd->curmenupos = 0;
++ do {
++ cmd->channel_list = display_channel_list( cmd, cmd->osd, cmd->channel_list + 1 );
++ argument = tvtime_osd_list_get_enter_argument( cmd->osd, cmd->curmenupos + 1 );
++ } while( strcasecmp( argument, station_get_channel( cmd->stationmgr, 0 ) ) );
++ } else {
++ cmd->channel_list = display_channel_list( cmd, cmd->osd, cmd->channel_list + 1 );
++ }
++ } else {
++ cmd->curmenupos = (cmd->curmenupos + 1) % (cmd->curmenusize);
++ if( cmd->curusermenu ) menu_set_cursor( cmd->curusermenu, cmd->curmenupos );
++ display_current_menu( cmd );
++ }
+ break;
+ case TVTIME_MENU_BACK: menu_back( cmd ); break;
+ case TVTIME_MENU_ENTER: menu_enter( cmd ); break;
+@@ -2128,6 +2219,9 @@
+ menu_off( cmd );
+ }
+ break;
++ case TVTIME_CHANNEL_LIST:
++ menu_off( cmd );
++ break;
+ case TVTIME_MOUSE_MOVE:
+ sscanf( arg, "%d %d", &x, &y );
+ if( cmd->halfsize ) y *= 2;
+@@ -2213,6 +2307,24 @@
+ }
+ break;
+
++ case TVTIME_CHANNEL_LIST:
++ if( cmd->vidin && videoinput_has_tuner( cmd->vidin ) ) {
++ if( cmd->osd ) {
++ if( set_menu( cmd, "channel-list" ) ) {
++ cmd->channel_list_active = 1;
++ const int num_stations = station_get_num_stations( cmd->stationmgr );
++ if( station_get_current_pos( cmd->stationmgr ) < MAX_STATIONS ) {
++ cmd->channel_list = display_channel_list( cmd, cmd->osd,
++ atoi( station_get_channel( cmd->stationmgr, 0 ) ) );
++ } else {
++ cmd->channel_list = display_channel_list( cmd, cmd->osd,
++ station_get_current_pos( cmd->stationmgr ) - MAX_STATIONS + 2 );
++ }
++ }
++ }
++ }
++ break;
++
+ case TVTIME_SHOW_EPG:
+ if( cmd->vidin && videoinput_has_tuner( cmd->vidin ) ) {
+ if( cmd->osd ) {
+@@ -2897,8 +3009,10 @@
+ /* Decode the input char from commands. */
+ if( cmd->digit_counter == 0 ) memset( cmd->next_chan_buffer, 0, 5 );
+ cmd->next_chan_buffer[ cmd->digit_counter ] = arg[ 0 ];
+- cmd->digit_counter++;
+- cmd->frame_counter = cmd->delay;
++ if( first_digit( atoi( cmd->next_chan_buffer ) ) != 0 ) {
++ cmd->digit_counter++;
++ cmd->frame_counter = cmd->delay;
++ }
+
+ /**
+ * Send an enter command if we type more
+@@ -3365,21 +3479,46 @@
+
+ case TVTIME_ENTER:
+ if( cmd->next_chan_buffer[ 0 ] ) {
+- if( cmd->renumbering ) {
+- station_remap( cmd->stationmgr, atoi( cmd->next_chan_buffer ) );
+- station_writeconfig( cmd->stationmgr );
+- cmd->renumbering = 0;
+- if( cmd->osd ) tvtime_osd_set_hold_message( cmd->osd, "", 0 );
+- }
+- if( station_set( cmd->stationmgr, atoi( cmd->next_chan_buffer ) ) ) {
+- cmd->change_channel = 1;
+- } else {
+- snprintf( cmd->next_chan_buffer, sizeof( cmd->next_chan_buffer ),
+- "%d", station_get_current_id( cmd->stationmgr ) );
+- if( cmd->osd ) {
+- tvtime_osd_set_channel_number( cmd->osd, cmd->next_chan_buffer );
++ if( cmd->channel_list_active ) {
++ const char *argument = tvtime_osd_list_get_enter_argument( cmd->osd, cmd->curmenupos + 1 );
++ if( atoi( cmd->next_chan_buffer ) <= station_get_num_stations( cmd->stationmgr ) ) {
++ do {
++ if( atoi( cmd->next_chan_buffer ) < atoi( station_get_channel( cmd->stationmgr, atoi( argument ) - 1 ) ) ) {
++ commands_handle( cmd, TVTIME_MENU_UP, "" );
++ } else {
++ commands_handle( cmd, TVTIME_MENU_DOWN, "" );
++ }
++ argument = tvtime_osd_list_get_enter_argument( cmd->osd, cmd->curmenupos + 1 );
++ } while( strcasecmp( argument, station_get_channel( cmd->stationmgr, atoi( cmd->next_chan_buffer ) - 1 ) ) );
++ if( cmd->osd ) {
++ tvtime_osd_set_channel_number( cmd->osd, cmd->next_chan_buffer );
++ tvtime_osd_show_info( cmd->osd );
++ }
++ } else if( cmd->osd ) {
++ tvtime_osd_set_channel_number( cmd->osd, argument );
+ tvtime_osd_show_info( cmd->osd );
+ }
++ } else {
++ if( cmd->renumbering ) {
++ if( atoi( cmd->next_chan_buffer ) <= station_get_num_stations( cmd->stationmgr ) ) {
++ if( first_digit( atoi( cmd->next_chan_buffer ) ) != 0 ) {
++ station_remap( cmd->stationmgr, atoi( cmd->next_chan_buffer ) );
++ station_writeconfig( cmd->stationmgr );
++ cmd->renumbering = 0;
++ }
++ }
++ if( cmd->osd ) tvtime_osd_set_hold_message( cmd->osd, "", 0 );
++ }
++ if( station_set( cmd->stationmgr, atoi( cmd->next_chan_buffer ) ) ) {
++ cmd->change_channel = 1;
++ } else {
++ snprintf( cmd->next_chan_buffer, sizeof( cmd->next_chan_buffer ),
++ "%d", station_get_current_id( cmd->stationmgr ) );
++ if( cmd->osd ) {
++ tvtime_osd_set_channel_number( cmd->osd, cmd->next_chan_buffer );
++ tvtime_osd_show_info( cmd->osd );
++ }
++ }
+ }
+ } else {
+ if( cmd->renumbering ) {
+@@ -3501,12 +3640,24 @@
+ if( cmd->osd ) tvtime_osd_set_hold_message( cmd->osd, "", 0 );
+ cmd->renumbering = 0;
+ }
++ if( cmd->channel_list_active ) {
++ snprintf( cmd->next_chan_buffer, sizeof( cmd->next_chan_buffer ),
++ "%d", station_get_current_id( cmd->stationmgr ) );
++ if( cmd->osd ) {
++ tvtime_osd_set_channel_number( cmd->osd, cmd->next_chan_buffer );
++ tvtime_osd_set_channel_name( cmd->osd, station_get_current_channel_name( cmd->stationmgr ) );
++ }
++ }
+ }
+
+ if( cmd->frame_counter > 0 && !(cmd->frame_counter % 5)) {
+ char input_text[6];
+
+- strcpy( input_text, cmd->next_chan_buffer );
++ if( first_digit( atoi( cmd->next_chan_buffer ) ) != 0 ) {
++ strcpy( input_text, cmd->next_chan_buffer );
++ } else {
++ strcpy( input_text, "" );
++ }
+ if( !(cmd->frame_counter % 10) ) {
+ strcat( input_text, "_" );
+ } else {
+@@ -3514,6 +3665,11 @@
+ }
+ if( cmd->osd ) {
+ tvtime_osd_set_channel_number( cmd->osd, input_text );
++ if( cmd->channel_list_active ) {
++ tvtime_osd_set_channel_name( cmd->osd, "" );
++ tvtime_osd_show_program_info( cmd->osd, 0, 0, 0 );
++ tvtime_osd_set_info_available( cmd->osd, 0 );
++ }
+ tvtime_osd_show_info( cmd->osd );
+ }
+ }
+Index: tvtime-1.0.2/docs/man/de/tvtime-command.1
+===================================================================
+--- tvtime-1.0.2.orig/docs/man/de/tvtime-command.1 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/docs/man/de/tvtime-command.1 2014-06-11 09:20:25.624028482 +0000
+@@ -86,6 +86,10 @@
+ auseinanderliegen.
+
+ .TP
++.B CHANNEL_LIST
++Dieser Befehl wird verwendet, um die Kanalliste.
++
++.TP
+ .B CHANNEL_RENUMBER
+ Verschiebt den aktuellen Kanal auf einen anderen Senderspeicher. Diese
+ Funktion tauscht den Speicherplatz des gerade aktiven Kanals mit dem, dessen
+Index: tvtime-1.0.2/docs/man/en/tvtime-command.1
+===================================================================
+--- tvtime-1.0.2.orig/docs/man/en/tvtime-command.1 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/docs/man/en/tvtime-command.1 2014-06-11 09:20:25.624028482 +0000
+@@ -82,6 +82,10 @@
+ forth between two programs on distant channels.
+
+ .TP
++.B CHANNEL_LIST
++This command is used to bring up channel list.
++
++.TP
+ .B CHANNEL_RENUMBER
+ Renumbers the current channel. This will swap the current channel with
+ the number you type in. Use this to configure your station list to suit
+Index: tvtime-1.0.2/docs/man/es/tvtime-command.1
+===================================================================
+--- tvtime-1.0.2.orig/docs/man/es/tvtime-command.1 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/docs/man/es/tvtime-command.1 2014-06-11 09:20:25.624028482 +0000
+@@ -88,6 +88,10 @@
+ para hacer zapping entre dos programas que están en canales distantes.
+
+ .TP
++.B CHANNEL_LIST
++Este comando se utiliza para mostrar la lista de canales.
++
++.TP
+ .B CHANNEL_RENUMBER
+ Renumera el canal actual. Intercambiará el canal actual con el número
+ que escribas. Úsalo para configurar tu lista de canales para acomodarla
+Index: tvtime-1.0.2/docs/man/de/tvtime.1
+===================================================================
+--- tvtime-1.0.2.orig/docs/man/de/tvtime.1 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/docs/man/de/tvtime.1 2014-06-11 09:20:25.624028482 +0000
+@@ -72,6 +72,9 @@
+ .I F1 oder Tab
+ Men anzeigen
+ .TP
++.I u
++Zeige Kanalliste
++.TP
+ .I hoch/runter
+ Sender whlen
+ .TP
+Index: tvtime-1.0.2/docs/man/en/tvtime.1
+===================================================================
+--- tvtime-1.0.2.orig/docs/man/en/tvtime.1 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/docs/man/en/tvtime.1 2014-06-11 09:20:25.624028482 +0000
+@@ -69,6 +69,9 @@
+ .I F1 or tab
+ Show menu
+ .TP
++.I u
++Show channel list
++.TP
+ .I up/down
+ Change channels
+ .TP
+Index: tvtime-1.0.2/docs/man/es/tvtime.1
+===================================================================
+--- tvtime-1.0.2.orig/docs/man/es/tvtime.1 2014-06-11 09:20:25.628028482 +0000
++++ tvtime-1.0.2/docs/man/es/tvtime.1 2014-06-11 09:20:25.624028482 +0000
+@@ -75,6 +75,9 @@
+ .I F1 o tabulador
+ Mostrar men
+ .TP
++.I u
++Mostrar lista de canales
++.TP
+ .I up/down
+ Cambiar canal
+ .TP
--- tvtime-1.0.2.orig/debian/patches/tvtime-1.0.2-closed_caption.patch
+++ tvtime-1.0.2/debian/patches/tvtime-1.0.2-closed_caption.patch
@@ -0,0 +1,22 @@
+## Description: Import patch from tvtime sourceforge tracker related to CC double-byte chars.
+## Origin: upstream, http://sourceforge.net/tracker/?func=detail&aid=1564644&group_id=64301&atid=506989
+## Bug-Ubuntu:
+## Bug-Debian:
+## Author: Nick Duffek
+## Forwarded: not-needed
+Index: tvtime-1.0.2/src/vbidata.c
+===================================================================
+--- tvtime-1.0.2.orig/src/vbidata.c 2014-04-28 06:28:51.728907156 +0000
++++ tvtime-1.0.2/src/vbidata.c 2014-04-28 06:28:51.724907156 +0000
+@@ -603,7 +603,10 @@
+ return 0;
+ }
+
+- if( vbi->vs && vbi->enabled && b1 >= 0x10 && b1 <= 0x1F && b2 >= 0x20 && b2 <= 0x7F ) {
++ if( vbi->vs && vbi->enabled && b1 >= 0x10 && b1 <= 0x1F && b2 >= 0x20 && b2 <= 0x7F
++ /* Double-byte characters are distinguishable from control codes by
++ having bit 6 clear and bit 4 set. */
++ && (b2 & 0x50) != 0x10 ) {
+ int code;
+ if( (b2 & 64) ) {
+ /* Preamble Code */
--- tvtime-1.0.2.orig/debian/patches/tvtime-1.0.2-command.patch
+++ tvtime-1.0.2/debian/patches/tvtime-1.0.2-command.patch
@@ -0,0 +1,23 @@
+## Description: Fix crash on "tvtime-command set_input_width 720"
+## Origin: upstream, http://tvtime.cvs.sourceforge.net/viewvc/tvtime/tvtime/src/commands.c?r1=1.290&r2=1.291&view=patch
+## Bug-Ubuntu: http://launchpad.net/bugs/947850
+## Bug-Debian:
+## Author: Mikael Magnusson
+## Forwarded: not-needed
+Index: tvtime-1.0.2/src/commands.c
+===================================================================
+--- tvtime-1.0.2.orig/src/commands.c 2012-03-06 10:06:33.705435710 +0000
++++ tvtime-1.0.2/src/commands.c 2012-03-06 10:32:57.229388640 +0000
+@@ -2402,12 +2402,10 @@
+ case TVTIME_SET_INPUT_WIDTH:
+ cmd->newinputwidth = atoi( arg );
+ if( cmd->osd ) {
+- const char *curname = menu_get_name( cmd->curusermenu );
+ menu_t *sharpmenu = commands_get_menu( cmd, "hres" );
+ char message[ 128 ];
+ int maxw = cmd->vidin? videoinput_get_maxwidth( cmd->vidin ) : 0;
+ reset_inputwidth_menu( sharpmenu, cmd->newinputwidth, maxw );
+- curname = menu_get_name( cmd->curusermenu );
+ commands_refresh_menu( cmd );
+ snprintf( message, sizeof( message ),
+ _("Horizontal resolution will be %d pixels on restart."),
--- tvtime-1.0.2.orig/debian/patches/tvtime-1.0.2-cpuid.patch
+++ tvtime-1.0.2/debian/patches/tvtime-1.0.2-cpuid.patch
@@ -0,0 +1,55 @@
+## Description: Update for cpuaccel.c from http://stapelspeicher.org/tvtime/patches/ (originally from libmpeg2).
+## Origin: upstream, http://stapelspeicher.org/tvtime/patches/tvtime-cpuid.patch
+## Bug-Ubuntu:
+## Bug-Debian:
+## Author: Mikael Magnusson
+## Forwarded: not-needed
+Index: tvtime-1.0.2/src/cpu_accel.c
+===================================================================
+--- tvtime-1.0.2.orig/src/cpu_accel.c 2012-03-06 12:25:30.479258191 +0000
++++ tvtime-1.0.2/src/cpu_accel.c 2012-03-06 12:29:10.303251655 +0000
+@@ -35,7 +35,7 @@
+ int AMD;
+ uint32_t caps;
+
+-#ifndef PIC
++#if defined(__x86_64__) || (!defined(PIC) && !defined(__PIC__))
+ #define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("cpuid" \
+ : "=a" (eax), \
+@@ -44,7 +44,7 @@
+ "=d" (edx) \
+ : "a" (op) \
+ : "cc")
+-#else /* PIC version : save ebx */
++#else /* PIC version : save ebx (not needed on x86_64) */
+ #define cpuid(op,eax,ebx,ecx,edx) \
+ __asm__ ("pushl %%ebx\n\t" \
+ "cpuid\n\t" \
+@@ -58,17 +58,17 @@
+ : "cc")
+ #endif
+
+-#ifdef ARCH_386
+- __asm__ ("pushfl\n\t"
+- "pushfl\n\t"
+- "popl %0\n\t"
++#ifndef __x86_64__ /* x86_64 supports the cpuid op */
++ __asm__ ("pushf\n\t"
++ "pushf\n\t"
++ "pop %0\n\t"
+ "movl %0,%1\n\t"
+ "xorl $0x200000,%0\n\t"
+- "pushl %0\n\t"
+- "popfl\n\t"
+- "pushfl\n\t"
+- "popl %0\n\t"
+- "popfl"
++ "push %0\n\t"
++ "popf\n\t"
++ "pushf\n\t"
++ "pop %0\n\t"
++ "popf"
+ : "=r" (eax),
+ "=r" (ebx)
+ :
--- tvtime-1.0.2.orig/debian/patches/tvtime-1.0.2-cursor.patch
+++ tvtime-1.0.2/debian/patches/tvtime-1.0.2-cursor.patch
@@ -0,0 +1,34 @@
+## Description: Fix nocursor using uninitialized pixmap data.
+## Origin: upstream, http://tvtime.cvs.sourceforge.net/viewvc/tvtime/tvtime/src/xcommon.c?r1=1.69&r2=1.70&view=patch
+## Bug-Ubuntu:
+## Bug-Debian:
+## Author: Mikael Magnusson
+## Forwarded: not-needed
+Index: tvtime-1.0.2/src/xcommon.c
+===================================================================
+--- tvtime-1.0.2.orig/src/xcommon.c 2012-03-06 10:36:58.325381470 +0000
++++ tvtime-1.0.2/src/xcommon.c 2012-03-06 10:47:05.901363409 +0000
+@@ -827,7 +827,7 @@
+ XEvent xev;
+ XSizeHints hint;
+ XClassHint classhint;
+- XColor curs_col;
++ XColor curs_col = { 0 };
+ XSetWindowAttributes xswa;
+ const char *hello = "tvtime";
+ unsigned long mask;
+@@ -1081,13 +1081,7 @@
+ XMaskEvent( display, StructureNotifyMask, &xev );
+
+ /* Create a 1 pixel cursor to use in full screen mode */
+- curs_pix = XCreatePixmap( display, output_window, 1, 1, 1 );
+- curs_col.pixel = 0;
+- curs_col.red = 0;
+- curs_col.green = 0;
+- curs_col.blue = 0;
+- curs_col.flags = 0;
+- curs_col.pad = 0;
++ curs_pix = XCreateBitmapFromData( display, output_window, (const char []){0}, 1, 1 );
+ nocursor = XCreatePixmapCursor( display, curs_pix, curs_pix, &curs_col, &curs_col, 1, 1 );
+ XDefineCursor( display, output_window, nocursor );
+ XSetIconName( display, wm_window, "tvtime" );
--- tvtime-1.0.2.orig/debian/patches/tvtime-1.0.2-device.patch
+++ tvtime-1.0.2/debian/patches/tvtime-1.0.2-device.patch
@@ -0,0 +1,1119 @@
+Description: Add feature to auto detect video and vbi device(s).
+Bug-Ubuntu: https://bugs.launchpad.net/ubuntu/+source/tvtime/+bug/153801
+Author: POJAR GEORGE
+===================================================================
+Index: tvtime-1.0.2/src/get_media_devices.c
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ tvtime-1.0.2/src/get_media_devices.c 2014-04-28 06:30:36.596904039 +0000
+@@ -0,0 +1,605 @@
++/**
++ * Copyright © 2011 by Mauro Carvalho Chehab
++ *
++ * The get_media_devices is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or ( at your option ) any later version.
++ *
++ * The libv4l2util Library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with the libv4l2util Library; if not, write to the Free
++ * Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA
++ * 02110-1335 USA.
++ */
++
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include
++#include "get_media_devices.h"
++
++#define ARRAY_SIZE( arr ) ( sizeof( arr ) / sizeof( ( arr )[0] ) )
++
++/**
++ * struct media_device_entry - Describes one device entry got via sysfs
++ *
++ * @device: sysfs name for a device.
++ * PCI devices are like: pci0000:00/0000:00:1b.0
++ * USB devices are like: pci0000:00/0000:00:1d.7/usb1/1-8
++ * @node: Device node, in sysfs or alsa hw identifier
++ * @device_type: Type of the device ( V4L_*, DVB_*, SND_* )
++ */
++struct media_device_entry {
++ char *device;
++ char *node;
++ char *parent;
++ enum device_type type;
++ enum bus_type bus;
++ unsigned major, minor; /* Device major/minor */
++};
++
++/**
++ * struct media_devices - Describes all devices found
++ *
++ * @device: sysfs name for a device.
++ * PCI devices are like: pci0000:00/0000:00:1b.0
++ * USB devices are like: pci0000:00/0000:00:1d.7/usb1/1-8
++ * @node: Device node, in sysfs or alsa hw identifier
++ * @device_type: Type of the device ( V4L_*, DVB_*, SND_* )
++ */
++struct media_devices {
++ struct media_device_entry *md_entry;
++ unsigned int md_size;
++};
++
++typedef int ( *fill_data_t )( struct media_device_entry *md );
++
++#define DEVICE_STR "devices"
++
++static void get_uevent_info( struct media_device_entry *md_ptr, char *dname )
++{
++ FILE *fd;
++ char file[PATH_MAX], *name, *p;
++ char s[1024];
++ char *s_ptr;
++
++ snprintf( file, PATH_MAX, "%s/%s/uevent", dname, md_ptr->node );
++ fd = fopen( file, "r" );
++ if( !fd )
++ return;
++ while( fgets( s, sizeof( s ), fd ) ) {
++ p = strtok_r( s, "=", &s_ptr );
++ if( !p )
++ continue;
++ name = p;
++ p = strtok_r( NULL, "\n", &s_ptr );
++ if( !p )
++ continue;
++ if( !strcmp( name, "MAJOR" ) )
++ md_ptr->major = atol( p );
++ else if( !strcmp( name, "MINOR" ) )
++ md_ptr->minor = atol( p );
++ }
++
++ fclose( fd );
++}
++
++static enum bus_type get_bus( char *device )
++{
++ char file[PATH_MAX];
++ char s[1024];
++ FILE *f;
++
++ if( !strcmp( device, "/sys/devices/virtual" ) )
++ return MEDIA_BUS_VIRTUAL;
++
++ snprintf( file, PATH_MAX, "%s/modalias", device );
++ f = fopen( file, "r" );
++ if( !f )
++ return MEDIA_BUS_UNKNOWN;
++ if( !fgets( s, sizeof( s ), f ) ) {
++ fclose( f );
++ return MEDIA_BUS_UNKNOWN;
++ }
++ fclose( f );
++
++ if( !strncmp( s, "pci", 3 ) )
++ return MEDIA_BUS_PCI;
++ if( !strncmp( s, "usb", 3 ) )
++ return MEDIA_BUS_USB;
++
++ return MEDIA_BUS_UNKNOWN;
++}
++
++static int get_class( char *class,
++ struct media_device_entry **md,
++ unsigned int *md_size,
++ fill_data_t fill )
++{
++ DIR *dir;
++ struct dirent entbuf, *entry;
++ char dname[PATH_MAX];
++ char fname[PATH_MAX];
++ char link[PATH_MAX];
++ char parent[PATH_MAX];
++ char virt_dev[60];
++ int err = -2;
++ struct media_device_entry *md_ptr = NULL;
++ char *p, *device;
++ enum bus_type bus;
++ static int virtual = 0;
++
++ snprintf( dname, PATH_MAX, "/sys/class/%s", class );
++ dir = opendir( dname );
++ if( !dir ) {
++ return 0;
++ }
++
++ while( !readdir_r( dir, &entbuf, &entry ) ) {
++ if( !entry )
++ break;
++ /* Skip . and .. */
++ if( entry->d_name[0] == '.' )
++ continue;
++ /* Canonicalize the device name */
++ snprintf( fname, PATH_MAX, "%s/%s", dname, entry->d_name );
++ if( realpath( fname, link ) ) {
++ device = link;
++
++ /* Remove the subsystem/class_name from the string */
++ p = strstr( device, class );
++ if( !p )
++ continue;
++ *( p - 1 ) = '\0';
++
++ snprintf( parent, PATH_MAX, "%s", device );
++ bus = get_bus( device );
++
++ /* remove the /sys/devices/ from the name */
++ device += 13;
++
++ switch ( bus ) {
++ case MEDIA_BUS_PCI:
++ /* Remove the device function nr */
++ p = strrchr( device, '.' );
++ if( !p )
++ continue;
++ *p = '\0';
++ break;
++ case MEDIA_BUS_USB:
++ /* Remove USB interface from the path */
++ p = strrchr( device, '/' );
++ if( !p )
++ continue;
++ /* In case we have a device where the driver
++ attaches directly to the usb device rather
++ then to an interface */
++ if( !strchr( p, ':' ) )
++ break;
++ *p = '\0';
++ break;
++ case MEDIA_BUS_VIRTUAL:
++ /* Don't group virtual devices */
++ sprintf( virt_dev, "virtual%d", virtual++ );
++ device = virt_dev;
++ break;
++ case MEDIA_BUS_UNKNOWN:
++ break;
++ }
++
++ /* Add one more element to the devices struct */
++ *md = realloc( *md, ( *md_size + 1 ) * sizeof( *md_ptr ) );
++ if( !*md )
++ goto error;
++ md_ptr = ( *md ) + *md_size;
++ ( *md_size )++;
++
++ /* Cleans previous data and fills it with device/node */
++ memset( md_ptr, 0, sizeof( *md_ptr ) );
++ md_ptr->type = UNKNOWN;
++ md_ptr->device = strdup( device );
++ md_ptr->node = strdup( entry->d_name );
++ md_ptr->parent = strdup( parent );
++
++ /* Retrieve major and minor information */
++ get_uevent_info( md_ptr, dname );
++
++ /* Used to identify the type of node */
++ fill( md_ptr );
++ }
++ }
++ err = 0;
++error:
++ closedir( dir );
++ return err;
++}
++
++static int add_v4l_class( struct media_device_entry *md )
++{
++ if( strstr( md->node, "video" ) )
++ md->type = MEDIA_V4L_VIDEO;
++ else if( strstr( md->node, "vbi" ) )
++ md->type = MEDIA_V4L_VBI;
++ else if( strstr( md->node, "radio" ) )
++ md->type = MEDIA_V4L_RADIO;
++ else if( strstr( md->node, "v4l-subdev" ) )
++ md->type = MEDIA_V4L_SUBDEV;
++
++ return 0;
++}
++
++static int add_snd_class( struct media_device_entry *md )
++{
++ unsigned c = 65535, d = 65535;
++ char node[64];
++
++ if( strstr( md->node, "timer" ) ) {
++ md->type = MEDIA_SND_TIMER;
++ return 0;
++ } else if( strstr( md->node, "seq" ) ) {
++ md->type = MEDIA_SND_SEQ;
++ return 0;
++ } if( strstr( md->node, "card" ) ) {
++ sscanf( md->node, "card%u", &c );
++ md->type = MEDIA_SND_CARD;
++ } else if( strstr( md->node, "hw" ) ) {
++ sscanf( md->node, "hwC%uD%u", &c, &d );
++ md->type = MEDIA_SND_HW;
++ } else if( strstr( md->node, "control" ) ) {
++ sscanf( md->node, "controlC%u", &c );
++ md->type = MEDIA_SND_CONTROL;
++ } else if( strstr( md->node, "pcm" ) ) {
++ sscanf( md->node, "pcmC%uD%u", &c, &d );
++ if( md->node[strlen( md->node ) - 1] == 'p' )
++ md->type = MEDIA_SND_OUT;
++ else if( md->node[strlen( md->node ) - 1] == 'c' )
++ md->type = MEDIA_SND_CAP;
++ }
++
++ if( c == 65535 )
++ return 0;
++
++ /* Reformat device to be useful for alsa userspace library */
++ if( d == 65535 )
++ snprintf( node, sizeof( node ), "hw:%u", c );
++ else
++ snprintf( node, sizeof( node ), "hw:%u,%u", c, d );
++
++ free( md->node );
++ md->node = strdup( node );
++
++ return 0;
++}
++
++static int add_dvb_class( struct media_device_entry *md )
++{
++ if( strstr( md->node, "video" ) )
++ md->type = MEDIA_DVB_VIDEO;
++ if( strstr( md->node, "audio" ) )
++ md->type = MEDIA_DVB_AUDIO;
++ if( strstr( md->node, "sec" ) )
++ md->type = MEDIA_DVB_SEC;
++ if( strstr( md->node, "frontend" ) )
++ md->type = MEDIA_DVB_FRONTEND;
++ else if( strstr( md->node, "demux" ) )
++ md->type = MEDIA_DVB_DEMUX;
++ else if( strstr( md->node, "dvr" ) )
++ md->type = MEDIA_DVB_DVR;
++ else if( strstr( md->node, "net" ) )
++ md->type = MEDIA_DVB_NET;
++ else if( strstr( md->node, "ca" ) )
++ md->type = MEDIA_DVB_CA;
++ else if( strstr( md->node, "osd" ) )
++ md->type = MEDIA_DVB_OSD;
++
++ return 0;
++}
++
++static int sort_media_device_entry( const void *a, const void *b )
++{
++ const struct media_device_entry *md_a = a;
++ const struct media_device_entry *md_b = b;
++ int cmp;
++
++ cmp = strcmp( md_a->device, md_b->device );
++ if( cmp )
++ return cmp;
++ cmp = ( int )md_a->type - ( int )md_b->type;
++ if( cmp )
++ return cmp;
++
++ return strcmp( md_a->node, md_b->node );
++}
++
++
++/* Public functions */
++
++void free_media_devices( void *opaque )
++{
++ struct media_devices *md = opaque;
++ struct media_device_entry *md_ptr = md->md_entry;
++ int i;
++ for( i = 0; i < md->md_size; i++ ) {
++ free( md_ptr->node );
++ free( md_ptr->device );
++ free( md_ptr->parent );
++ md_ptr++;
++ }
++ free( md->md_entry );
++ free( md );
++}
++
++void *discover_media_devices( void )
++{
++ struct media_devices *md = NULL;
++ struct media_device_entry *md_entry = NULL;
++
++ md = calloc( 1, sizeof( *md ) );
++ if( !md )
++ return NULL;
++
++ md->md_size = 0;
++ if( get_class( "video4linux", &md_entry, &md->md_size, add_v4l_class ) )
++ goto error;
++ if( get_class( "sound", &md_entry, &md->md_size, add_snd_class ) )
++ goto error;
++ if( get_class( "dvb", &md_entry, &md->md_size, add_dvb_class ) )
++ goto error;
++
++ /* There's no media device */
++ if( !md_entry )
++ goto error;
++
++ qsort( md_entry, md->md_size, sizeof( *md_entry ), sort_media_device_entry );
++
++ md->md_entry = md_entry;
++
++ return md;
++
++error:
++ free_media_devices( md );
++ return NULL;
++}
++
++const char *media_device_type( enum device_type type )
++{
++ switch( type ) {
++ /* V4L nodes */
++ case MEDIA_V4L_VIDEO:
++ return "video";
++ case MEDIA_V4L_VBI:
++ return "vbi";
++ case MEDIA_V4L_RADIO:
++ return "radio";
++ case MEDIA_V4L_SUBDEV:
++ return "v4l subdevice";
++
++ /* DVB nodes */
++ case MEDIA_DVB_VIDEO:
++ return "dvb video";
++ case MEDIA_DVB_AUDIO:
++ return "dvb audio";
++ case MEDIA_DVB_SEC:
++ return "dvb sec";
++ case MEDIA_DVB_FRONTEND:
++ return "dvb frontend";
++ case MEDIA_DVB_DEMUX:
++ return "dvb demux";
++ case MEDIA_DVB_DVR:
++ return "dvb dvr";
++ case MEDIA_DVB_NET:
++ return "dvb net";
++ case MEDIA_DVB_CA:
++ return "dvb conditional access";
++ case MEDIA_DVB_OSD:
++ return "dvb OSD";
++
++ /* Alsa nodes */
++ case MEDIA_SND_CARD:
++ return "sound card";
++ case MEDIA_SND_CAP:
++ return "pcm capture";
++ case MEDIA_SND_OUT:
++ return "pcm output";
++ case MEDIA_SND_CONTROL:
++ return "mixer";
++ case MEDIA_SND_HW:
++ return "sound hardware";
++ case MEDIA_SND_TIMER:
++ return "sound timer";
++ case MEDIA_SND_SEQ:
++ return "sound sequencer";
++
++ default:
++ return "unknown";
++ };
++}
++
++void display_media_devices( void *opaque )
++{
++ struct media_devices *md = opaque;
++ struct media_device_entry *md_ptr = md->md_entry;
++ int i;
++ char *prev = "";
++
++ for( i = 0; i < md->md_size; i++ ) {
++ if( strcmp( prev, md_ptr->device ) ) {
++ printf( "\nDevice %s:\n\t", md_ptr->device );
++ prev = md_ptr->device;
++ }
++ printf( "%s( %s, dev %i:%i ) ", md_ptr->node,
++ media_device_type( md_ptr->type ),
++ md_ptr->major, md_ptr->minor );
++ md_ptr++;
++ }
++ printf( "\n" );
++}
++
++const char *get_associated_device( void *opaque,
++ const char *last_seek,
++ const enum device_type desired_type,
++ const char *seek_device,
++ const enum device_type seek_type )
++{
++ struct media_devices *md = opaque;
++ struct media_device_entry *md_ptr = md->md_entry;
++ int i, found = 0;
++ char *prev, *p;
++
++ if( seek_type != NONE && seek_device[0] ) {
++ /* Get just the device name */
++ p = strrchr( seek_device, '/' );
++ if( p )
++ seek_device = p + 1;
++
++ /* Step 1: Find the seek node */
++ for( i = 0; i < md->md_size; i++, md_ptr++ ) {
++ if( last_seek && md_ptr->type == seek_type &&
++ !strcmp( md_ptr->node, last_seek ) ) {
++ found = 1;
++ continue;
++ }
++ if( last_seek && !found )
++ continue;
++ if( md_ptr->type == seek_type &&
++ !strcmp( seek_device, md_ptr->node ) )
++ break;
++ }
++ if( i == md->md_size )
++ return NULL;
++ i++;
++ prev = md_ptr->device;
++ md_ptr++;
++ /* Step 2: find the associated node */
++ for( ; i < md->md_size && !strcmp( prev, md_ptr->device ); i++, md_ptr++ ) {
++ if( last_seek && md_ptr->type == seek_type &&
++ !strcmp( md_ptr->node, last_seek ) ) {
++ found = 1;
++ continue;
++ }
++ if( last_seek && !found )
++ continue;
++ if( md_ptr->type == desired_type )
++ return md_ptr->node;
++ }
++ } else {
++ for( i = 0; i < md->md_size; i++, md_ptr++ ) {
++ if( last_seek && !strcmp( md_ptr->node, last_seek ) ) {
++ found = 1;
++ continue;
++ }
++ if( last_seek && !found )
++ continue;
++ if( md_ptr->type == desired_type )
++ return md_ptr->node;
++ }
++ }
++
++ return NULL;
++}
++
++const char *fget_associated_device( void *opaque,
++ const char *last_seek,
++ const enum device_type desired_type,
++ const int fd_seek_device,
++ const enum device_type seek_type )
++{
++ struct media_devices *md = opaque;
++ struct media_device_entry *md_ptr = md->md_entry;
++ struct stat f_status;
++ unsigned int dev_major, dev_minor;
++ int i, found = 0;
++ char *prev;
++
++ if( fstat( fd_seek_device, &f_status ) ) {
++ perror( "Can't get file status" );
++ return NULL;
++ }
++ if( !S_ISCHR( f_status.st_mode ) ) {
++ fprintf( stderr, "File descriptor is not a char device\n" );
++ return NULL;
++ }
++ dev_major = major( f_status.st_rdev );
++ dev_minor = minor( f_status.st_rdev );
++
++ /* Step 1: Find the seek node */
++ for( i = 0; i < md->md_size; i++, md_ptr++ ) {
++ if( last_seek && md_ptr->type == seek_type
++ && md_ptr->major == dev_major
++ && md_ptr->minor == dev_minor ) {
++ found = 1;
++ continue;
++ }
++ if( last_seek && !found )
++ continue;
++ if( md_ptr->type == seek_type
++ && md_ptr->major == dev_major
++ && md_ptr->minor == dev_minor )
++ break;
++ }
++ if( i == md->md_size )
++ return NULL;
++ i++;
++ prev = md_ptr->device;
++ md_ptr++;
++ /* Step 2: find the associated node */
++ for( ; i < md->md_size && !strcmp( prev, md_ptr->device ); i++, md_ptr++ ) {
++ if( last_seek && md_ptr->type == seek_type
++ && md_ptr->major == dev_major
++ && md_ptr->minor == dev_minor ) {
++ found = 1;
++ continue;
++ }
++ if( last_seek && !found )
++ continue;
++ if( md_ptr->type == desired_type )
++ return md_ptr->node;
++ }
++ return NULL;
++}
++
++const char *get_not_associated_device( void *opaque,
++ const char *last_seek,
++ const enum device_type desired_type,
++ const enum device_type not_desired_type )
++{
++ struct media_devices *md = opaque;
++ struct media_device_entry *md_ptr = md->md_entry;
++ int i, skip = 0, found = 0;
++ char *prev = "", *result = NULL;
++
++ /* Step 1: Find a device without seek_type node */
++ for( i = 0; i < md->md_size; i++, md_ptr++ ) {
++ if( last_seek && !strcmp( md_ptr->node, last_seek ) ) {
++ found = 1;
++ continue;
++ }
++ if( last_seek && !found )
++ continue;
++ if( strcmp( prev, md_ptr->device ) ) {
++ if( !skip && result )
++ break;
++ prev = md_ptr->device;
++ skip = 0;
++ result = NULL;
++ }
++ if( md_ptr->type == not_desired_type )
++ skip = 1;
++ else if( !skip && !result && md_ptr->type == desired_type )
++ result = md_ptr->node;
++ }
++ if( skip )
++ result = NULL;
++
++ return result;
++}
+Index: tvtime-1.0.2/src/get_media_devices.h
+===================================================================
+--- /dev/null 1970-01-01 00:00:00.000000000 +0000
++++ tvtime-1.0.2/src/get_media_devices.h 2014-04-28 06:30:36.596904039 +0000
+@@ -0,0 +1,168 @@
++/**
++ * Copyright © 2011 by Mauro Carvalho Chehab
++ *
++ * The get_media_devices is free software; you can redistribute it and/or
++ * modify it under the terms of the GNU Lesser General Public
++ * License as published by the Free Software Foundation; either
++ * version 2.1 of the License, or (at your option) any later version.
++ *
++ * The libv4l2util Library is distributed in the hope that it will be useful,
++ * but WITHOUT ANY WARRANTY; without even the implied warranty of
++ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
++ * Lesser General Public License for more details.
++ *
++ * You should have received a copy of the GNU Lesser General Public
++ * License along with the libv4l2util Library; if not, write to the Free
++ * Software Foundation, Inc., 51 Franklin Street, Suite 500, Boston, MA
++ * 02110-1335 USA.
++ */
++
++/*
++ * Version of the API
++ */
++#define GET_MEDIA_DEVICES_VERSION 0x0105
++
++/**
++ * enum device_type - Enumerates the type for each device
++ *
++ * The device_type is used to sort the media devices array.
++ * So, the order is relevant. The first device should be
++ * MEDIA_V4L_VIDEO.
++ */
++enum device_type {
++ UNKNOWN = 65535,
++ NONE = 65534,
++ MEDIA_V4L_VIDEO = 0,
++ MEDIA_V4L_VBI,
++ MEDIA_V4L_RADIO,
++ MEDIA_V4L_SUBDEV,
++
++ MEDIA_DVB_VIDEO = 100,
++ MEDIA_DVB_AUDIO,
++ MEDIA_DVB_SEC,
++ MEDIA_DVB_FRONTEND,
++ MEDIA_DVB_DEMUX,
++ MEDIA_DVB_DVR,
++ MEDIA_DVB_CA,
++ MEDIA_DVB_NET,
++ MEDIA_DVB_OSD,
++
++ MEDIA_SND_CARD = 200,
++ MEDIA_SND_CAP,
++ MEDIA_SND_OUT,
++ MEDIA_SND_CONTROL,
++ MEDIA_SND_HW,
++ MEDIA_SND_TIMER,
++ MEDIA_SND_SEQ,
++ /*
++ * FIXME: not all alsa devices were mapped. missing things like
++ * midi, midiC%iD%i and timer interfaces
++ */
++};
++
++enum bus_type {
++ MEDIA_BUS_UNKNOWN,
++ MEDIA_BUS_VIRTUAL,
++ MEDIA_BUS_PCI,
++ MEDIA_BUS_USB,
++};
++
++/**
++ * discover_media_devices() - Returns a list of the media devices
++ * @md_size: Returns the size of the media devices found
++ *
++ * This function reads the /sys/class nodes for V4L, DVB and sound,
++ * and returns an opaque desciptor that keeps a list of the devices.
++ * The fields on this list is opaque, as they can be changed on newer
++ * releases of this library. So, all access to it should be done via
++ * a function provided by the API. The devices are ordered by device,
++ * type and node. At return, md_size is updated.
++ */
++void *discover_media_devices( void );
++
++/**
++ * free_media_devices() - Frees the media devices array
++ *
++ * @opaque: media devices opaque descriptor
++ *
++ * As discover_media_devices() dynamically allocate space for the
++ * strings, feeing the list requires also to free those data. So,
++ * the safest and recommended way is to call this function.
++ */
++void free_media_devices( void *opaque );
++
++/**
++ * media_device_type() - returns a string with the name of a given type
++ *
++ * @type: media device type
++ */
++const char *media_device_type( const enum device_type type );
++
++/**
++ * display_media_devices() - prints a list of media devices
++ *
++ * @opaque: media devices opaque descriptor
++ */
++void display_media_devices( void *opaque );
++
++/**
++ * get_associated_device() - Return the next device associated with another one
++ *
++ * @opaque: media devices opaque descriptor
++ * @last_seek: last seek result. Use NULL to get the first result
++ * @desired_type: type of the desired device
++ * @seek_device: name of the device with you want to get an association.
++ *@ seek_type: type of the seek device. Using NONE produces the same
++ * result of using NULL for the seek_device.
++ *
++ * This function seeks inside the media_devices struct for the next device
++ * that it is associated with a seek parameter.
++ * It can be used to get an alsa device associated with a video device. If
++ * the seek_device is NULL or seek_type is NONE, it will just search for
++ * devices of the desired_type.
++ */
++const char *get_associated_device( void *opaque,
++ const char *last_seek,
++ const enum device_type desired_type,
++ const char *seek_device,
++ const enum device_type seek_type );
++
++/**
++ * fget_associated_device() - Return the next device associated with another one
++ *
++ * @opaque: media devices opaque descriptor
++ * @last_seek: last seek result. Use NULL to get the first result
++ * @desired_type: type of the desired device
++ * @fd_seek_device: file handler for the device where the association will
++ be made
++ *@ seek_type: type of the seek device. Using NONE produces the same
++ * result of using NULL for the seek_device.
++ *
++ * This function seeks inside the media_devices struct for the next device
++ * that it is associated with a seek parameter.
++ * It can be used to get an alsa device associated with an open file descriptor
++ */
++const char *fget_associated_device( void *opaque,
++ const char *last_seek,
++ const enum device_type desired_type,
++ const int fd_seek_device,
++ const enum device_type seek_type );
++
++/**
++ * get_not_associated_device() - Return the next device not associated with
++ * an specific device type.
++ *
++ * @opaque: media devices opaque descriptor
++ * @last_seek: last seek result. Use NULL to get the first result
++ * @desired_type: type of the desired device
++ * @not_desired_type: type of the seek device
++ *
++ * This function seeks inside the media_devices struct for the next physical
++ * device that doesn't support a non_desired type.
++ * This method is useful for example to return the audio devices that are
++ * provided by the motherboard.
++ */
++const char *get_not_associated_device( void *opaque,
++ const char *last_seek,
++ const enum device_type desired_type,
++ const enum device_type not_desired_type );
+Index: tvtime-1.0.2/src/Makefile.am
+===================================================================
+--- tvtime-1.0.2.orig/src/Makefile.am 2014-04-28 06:30:36.600904039 +0000
++++ tvtime-1.0.2/src/Makefile.am 2014-04-28 06:30:36.596904039 +0000
+@@ -40,7 +40,7 @@
+ utils.h utils.c pulldown.h pulldown.c hashtable.h hashtable.c \
+ cpuinfo.h cpuinfo.c videodev.h videodev2.h menu.c menu.h \
+ outputfilter.h outputfilter.c xmltv.h xmltv.c gettext.h tvtimeglyphs.h \
+- copyfunctions.h copyfunctions.c
++ copyfunctions.h copyfunctions.c get_media_devices.h get_media_devices.c
+
+ if ARCH_X86
+ DSCALER_SRCS = $(top_srcdir)/plugins/dscalerapi.h \
+@@ -89,7 +89,7 @@
+ tvtime_configure_LDFLAGS = $(ZLIB_LIBS) $(XML2_LIBS)
+ tvtime_scanner_SOURCES = utils.h utils.c videoinput.h videoinput.c \
+ tvtimeconf.h tvtimeconf.c station.h station.c tvtime-scanner.c \
+- mixer.h mixer.c
++ mixer.h mixer.c get_media_devices.h get_media_devices.c
+ tvtime_scanner_CFLAGS = $(OPT_CFLAGS) $(XML2_FLAG) $(AM_CFLAGS)
+ tvtime_scanner_LDFLAGS = $(ZLIB_LIBS) $(XML2_LIBS)
+
+Index: tvtime-1.0.2/src/videoinput.c
+===================================================================
+--- tvtime-1.0.2.orig/src/videoinput.c 2014-04-28 06:30:36.600904039 +0000
++++ tvtime-1.0.2/src/videoinput.c 2014-04-28 06:30:36.596904039 +0000
+@@ -32,12 +32,14 @@
+ #include
+ #include
+ #include
++#include
+ #ifdef HAVE_CONFIG_H
+ # include "config.h"
+ #endif
+ #include "videodev.h"
+ #include "videodev2.h"
+ #include "videoinput.h"
++#include "get_media_devices.h"
+ #include "mixer.h"
+
+ /**
+@@ -433,6 +435,9 @@
+ struct v4l2_capability caps_v4l2;
+ struct video_picture grab_pict;
+ struct video_window grab_win;
++ void *md;
++ char device[ PATH_MAX ];
++ const char *p = NULL;
+ int i;
+
+ if( capwidth & 1 ) {
+@@ -483,11 +488,28 @@
+ memset( vidin->drivername, 0, sizeof( vidin->drivername ) );
+ memset( vidin->shortdriver, 0, sizeof( vidin->shortdriver ) );
+
++ if( !strcasecmp( v4l_device, "auto" ) ) {
++ /* Discovers the media device(s). */
++ md = discover_media_devices();
++
++ while( 1 ) {
++ /* Get auto detected video device. */
++ p = get_associated_device( md, p, MEDIA_V4L_VIDEO, NULL, NONE );
++ if( !p ) {
++ break;
++ }
++ snprintf( device, PATH_MAX, "/dev/%s", p );
++ }
++ free_media_devices( md );
++ } else {
++ snprintf( device, PATH_MAX, "%s", v4l_device );
++ }
++
+ /* First, open the device. */
+- vidin->grab_fd = open( v4l_device, O_RDWR );
++ vidin->grab_fd = open( device, O_RDWR );
+ if( vidin->grab_fd < 0 ) {
+ fprintf( stderr, "videoinput: Cannot open capture device %s: %s\n",
+- v4l_device, strerror( errno ) );
++ device, strerror( errno ) );
+ sprintf( error_string, "%s", strerror( errno ) );
+ free( vidin );
+ return 0;
+@@ -501,7 +523,7 @@
+ /* Can't get V4L2 capabilities, maybe this is a V4L1 device? */
+ if( ioctl( vidin->grab_fd, VIDIOCGCAP, &caps_v4l1 ) < 0 ) {
+ fprintf( stderr, "videoinput: %s is not a video4linux device.\n",
+- v4l_device );
++ device );
+ sprintf( error_string, "Not a video4linux device" );
+ close( vidin->grab_fd );
+ free( vidin );
+@@ -548,7 +570,7 @@
+
+ if( !vidin->numinputs ) {
+ fprintf( stderr, "videoinput: No inputs available on "
+- "video4linux2 device '%s'.\n", v4l_device );
++ "video4linux2 device '%s'.\n", device );
+ sprintf( error_string, "No inputs available" );
+ close( vidin->grab_fd );
+ free( vidin );
+@@ -559,7 +581,7 @@
+ vidin->numinputs = caps_v4l1.channels;
+ if( vidin->numinputs == 0 ) {
+ fprintf( stderr, "videoinput: No inputs available on "
+- "video4linux device '%s'.\n", v4l_device );
++ "video4linux device '%s'.\n", device );
+ sprintf( error_string, "No inputs available" );
+ close( vidin->grab_fd );
+ free( vidin );
+@@ -580,7 +602,7 @@
+ if( !vidin->isbttv && norm > VIDEOINPUT_SECAM ) {
+ fprintf( stderr, "videoinput: Capture card '%s' does not seem to use the bttv driver.\n"
+ "videoinput: The norm %s is only supported in V4L1 for bttv-supported cards.\n",
+- v4l_device, videoinput_get_norm_name( norm ) );
++ device, videoinput_get_norm_name( norm ) );
+ sprintf( error_string, "%s unsupported by %s", videoinput_get_norm_name( norm ), vidin->shortdriver );
+ close( vidin->grab_fd );
+ free( vidin );
+@@ -707,7 +729,7 @@
+ if( ioctl( vidin->grab_fd, VIDIOCGCAP, &caps_v4l1 ) < 0 ) {
+ fprintf( stderr, "videoinput: video4linux device '%s' refuses "
+ "to provide set capability information, giving up.\n",
+- v4l_device );
++ device );
+ sprintf( error_string, "%s: %s", vidin->shortdriver, strerror( errno ) );
+ close( vidin->grab_fd );
+ free( vidin );
+Index: tvtime-1.0.2/src/tvtimeconf.c
+===================================================================
+--- tvtime-1.0.2.orig/src/tvtimeconf.c 2014-04-28 06:30:36.600904039 +0000
++++ tvtime-1.0.2/src/tvtimeconf.c 2014-04-28 06:30:36.596904039 +0000
+@@ -608,9 +608,9 @@
+ lfprintf( stderr, _("usage: %s [OPTION]...\n\n"), argv[ 0 ] );
+ lfputs( _(" -a, --widescreen 16:9 mode.\n"), stderr );
+ lfputs( _(" -A, --nowidescreen 4:3 mode.\n"), stderr );
+- lfputs( _(" -b, --vbidevice=DEVICE VBI device (defaults to /dev/vbi0).\n"), stderr );
++ lfputs( _(" -b, --vbidevice=DEVICE VBI device (defaults to auto).\n"), stderr );
+ lfputs( _(" -c, --channel=CHANNEL Tune to the specified channel on startup.\n"), stderr );
+- lfputs( _(" -d, --device=DEVICE video4linux device (defaults to /dev/video0).\n"), stderr );
++ lfputs( _(" -d, --device=DEVICE video4linux device (defaults to auto).\n"), stderr );
+ lfputs( _(" -f, --frequencies=NAME The frequency table to use for the tuner.\n"
+ " (defaults to us-cable).\n\n"
+ " Valid values are:\n"
+@@ -660,9 +660,9 @@
+ lfprintf( stderr, _("usage: %s [OPTION]...\n\n"), argv[ 0 ] );
+ lfputs( _(" -a, --widescreen 16:9 mode.\n"), stderr );
+ lfputs( _(" -A, --nowidescreen 4:3 mode.\n"), stderr );
+- lfputs( _(" -b, --vbidevice=DEVICE VBI device (defaults to /dev/vbi0).\n"), stderr );
++ lfputs( _(" -b, --vbidevice=DEVICE VBI device (defaults to auto).\n"), stderr );
+ lfputs( _(" -c, --channel=CHANNEL Tune to the specified channel on startup.\n"), stderr );
+- lfputs( _(" -d, --device=DEVICE video4linux device (defaults to /dev/video0).\n"), stderr );
++ lfputs( _(" -d, --device=DEVICE video4linux device (defaults to auto).\n"), stderr );
+ lfputs( _(" -f, --frequencies=NAME The frequency table to use for the tuner.\n"
+ " (defaults to us-cable).\n\n"
+ " Valid values are:\n"
+@@ -706,7 +706,7 @@
+ static void print_scanner_usage( char **argv )
+ {
+ lfprintf( stderr, _("usage: %s [OPTION]...\n\n"), argv[ 0 ]);
+- lfputs( _(" -d, --device=DEVICE video4linux device (defaults to /dev/video0).\n"), stderr );
++ lfputs( _(" -d, --device=DEVICE video4linux device (defaults to auto).\n"), stderr );
+ lfputs( _(" -F, --configfile=FILE Additional config file to load settings from.\n"), stderr );
+ lfputs( _(" -h, --help Show this help message.\n"), stderr );
+ lfputs( _(" -i, --input=INPUTNUM video4linux input number (defaults to 0).\n"), stderr );
+@@ -764,7 +764,7 @@
+
+ ct->inputwidth = 720;
+ ct->inputnum = 0;
+- ct->v4ldev = strdup( "/dev/video0" );
++ ct->v4ldev = strdup( "auto" );
+ ct->norm = strdup( "ntsc" );
+ ct->freq = strdup( "us-cable" );
+ temp_dirname = getenv( "HOME" );
+@@ -788,7 +788,7 @@
+ ct->check_freq_present = 1;
+
+ ct->use_xds = 0;
+- ct->vbidev = strdup( "/dev/vbi0" );
++ ct->vbidev = strdup( "auto" );
+
+ ct->start_channel = 1;
+ ct->prev_channel = 1;
+Index: tvtime-1.0.2/src/vbidata.c
+===================================================================
+--- tvtime-1.0.2.orig/src/vbidata.c 2014-04-28 06:30:36.600904039 +0000
++++ tvtime-1.0.2/src/vbidata.c 2014-04-28 06:30:36.596904039 +0000
+@@ -35,7 +35,9 @@
+ #include |