Statistics
| Branch: | Revision:

chunker-player / chunker_player / chunker_player.c @ e11386c0

History | View | Annotate | Download (15.8 KB)

1
#include <stdio.h>
2
#include <unistd.h>
3
#include <SDL.h>
4
#include <SDL_mutex.h>
5
#include "player_defines.h"
6
#include <confuse.h>
7
#include "http_default_urls.h"
8
#include "player_defines.h"
9
#include "chunker_player.h"
10
#include "player_gui.h"
11
#include <time.h>
12
#include <getopt.h>
13

    
14
#define MANDATORY_PARAMS 3
15
#define OPTIONAL_PARAMS 1
16

    
17
#ifdef WIN32
18
#include <windows.h>
19
#endif
20

    
21
void sigproc()
22
{
23
        printf("you have pressed ctrl-c, terminating...\n");
24
        quit = 1;
25
}
26

    
27
static void print_usage(int argc, char *argv[])
28
{
29
  fprintf (stderr,
30
    "\nUsage:%s [options]\n"
31
    "\n"
32
    "Mandatory options:\n"
33
    "\t[-q q_thresh]: playout queue size\n"
34
    "\t[-c ChannelName]: channel name (from channels.conf)\n"
35
    "\t[-p port]: player http port\n\n"
36
    "Other options:\n"
37
    "\t[-t]: log traces (WARNING: old traces will be deleted).\n"
38
    "\t[-s mode]: silent mode (mode=1 no user gui and no audio, mode=2 audio only, mode=3 only gui and no P2P).\n\n"
39
    "=======================================================\n", argv[0]
40
    );
41
}
42

    
43
int main(int argc, char *argv[])
44
{
45
        srand ( time(NULL) );
46
        // some initializations
47
        SilentMode = 0;
48
        queue_filling_threshold = 0;
49
        quit = 0;
50
        QueueFillingMode=1;
51
        LogTraces = 0;
52

    
53
#ifndef __WIN32__
54
        static pid_t fork_pid = -1;
55
        P2PProcessHandle=&fork_pid;
56
#else
57
        static PROCESS_INFORMATION ProcessInfo;
58
        ZeroMemory( &ProcessInfo, sizeof(ProcessInfo) );
59
        P2PProcessHandle=&ProcessInfo;
60
#endif
61

    
62
        NChannels = 0;
63
        SelectedChannel = -1;
64
        char firstChannelName[255];
65
        int firstChannelIndex;
66
        
67
        memset((void*)Channels, 0, (MAX_CHANNELS_NUM*sizeof(SChannel)));
68

    
69
#ifdef HTTPIO
70
        HttpPort = -1;
71
#endif
72
#ifdef TCPIO
73
        TcpPort = -1;
74
#endif
75
        struct MHD_Daemon *daemon = NULL;
76
        SDL_Event event;
77
        OverlayMutex = SDL_CreateMutex();
78
        int mandatories = 0;
79
        
80
        char c;
81
        while ((c = getopt (argc, argv, "q:c:p:s:t")) != -1)
82
        {
83
                switch (c) {
84
                        case 0: //for long options
85
                                break;
86
                        case 'q':
87
                                sscanf(optarg, "%d", &queue_filling_threshold);
88
                                mandatories++;
89
                                break;
90
                        case 'c':
91
                                sprintf(firstChannelName, "%s", optarg);
92
                                mandatories++;
93
                                break;
94
                        case 'p':
95
#ifdef HTTPIO
96
                                sscanf(optarg, "%d", &HttpPort);
97
#endif
98
#ifdef TCPIO
99
                                sscanf(optarg, "%d", &TcpPort);
100
#endif
101
                                mandatories++;
102
                                break;
103
                        case 's':
104
                                sscanf(optarg, "%d", &SilentMode);
105
                                break;
106
                        case 't':
107
                                DELETE_DIR("traces");
108
                                CREATE_DIR("traces");
109
                                LogTraces = 1;
110
                                break;
111
                        default:
112
                                print_usage(argc, argv);
113
                                return -1;
114
                }
115
        }
116
        if(mandatories < MANDATORY_PARAMS)
117
        {
118
                print_usage(argc, argv);
119
                return -1;
120
        }
121

    
122
#ifdef EMULATE_CHUNK_LOSS
123
        ScheduledChunkLosses = NULL;
124
        cfg_opt_t scheduled_chunk_loss_opts[] =
125
        {
126
                CFG_INT("Time", 0, CFGF_NONE),
127
                CFG_INT("Value", 0, CFGF_NONE),
128
                CFG_INT("MinValue", 0, CFGF_NONE),
129
                CFG_INT("MaxValue", 0, CFGF_NONE),
130
                CFG_INT("Burstiness", 0, CFGF_NONE),
131
                CFG_END()
132
        };
133
        cfg_opt_t opts[] =
134
        {
135
                CFG_SEC("ScheduledChunkLoss", scheduled_chunk_loss_opts, CFGF_MULTI),
136
                CFG_END()
137
        };
138
        cfg_t *cfg, *cfg_sched;
139
        cfg = cfg_init(opts, CFGF_NONE);
140
        if(!cfg_parse(cfg, "_chunklossrate.conf") == CFG_PARSE_ERROR)
141
        {
142
                NScheduledChunkLosses = cfg_size(cfg, "ScheduledChunkLoss");
143
                if(NScheduledChunkLosses > 0)
144
                        ScheduledChunkLosses = (SChunkLoss*)malloc((NScheduledChunkLosses)*sizeof(SChunkLoss));
145
                
146
                int j;
147
                for(j = 0; j < cfg_size(cfg, "ScheduledChunkLoss"); j++)
148
                {
149
                        cfg_sched = cfg_getnsec(cfg, "ScheduledChunkLoss", j);
150
                        ScheduledChunkLosses[j].Time = cfg_getint(cfg_sched, "Time");
151
                        ScheduledChunkLosses[j].Value = cfg_getint(cfg_sched, "Value");
152
                        ScheduledChunkLosses[j].Burstiness = cfg_getint(cfg_sched, "Burstiness");
153
                        
154
                        // -1 means random value between min and max
155
                        if(ScheduledChunkLosses[j].Value == -1)
156
                        {
157
                                ScheduledChunkLosses[j].MinValue = cfg_getint(cfg_sched, "MinValue");
158
                                ScheduledChunkLosses[j].MaxValue = cfg_getint(cfg_sched, "MaxValue");
159
                        }
160
                }
161
                cfg_free(cfg);
162
                CurrChunkLossIndex = -1;
163
                
164
                for(j=0; j < NScheduledChunkLosses; j++)
165
                {
166
                        printf("ScheduledChunkLosses[%d].Time = %ld\n", j, ScheduledChunkLosses[j].Time);
167
                        printf("ScheduledChunkLosses[%d].Value = %d\n", j, ScheduledChunkLosses[j].Value);
168
                        printf("ScheduledChunkLosses[%d].Burstiness = %d\n", j, ScheduledChunkLosses[j].Burstiness);
169
                }
170
        }
171
#endif
172

    
173
#ifdef HTTPIO
174
        //this thread fetches chunks from the network by listening to the following path, port
175
        daemon = (struct MHD_Daemon*)initChunkPuller(UL_DEFAULT_EXTERNALPLAYER_PATH, HttpPort);
176
        if(daemon == NULL)
177
        {
178
                printf("CANNOT START MICROHTTPD SERVICE, EXITING...\n");
179
                exit(2);
180
        }
181
#endif
182
#ifdef TCPIO
183
        int fd = initChunkPuller(TcpPort);
184
        if(! (fd > 0))
185
        {
186
                printf("CANNOT START TCP PULLER...\n");
187
                exit(2);
188
        }
189
#endif
190

    
191
        if(!SilentMode)
192
        {
193
                if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
194
                        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
195
                        return -1;
196
                }
197
        }
198
        else
199
        {
200
                if(SDL_Init(SDL_INIT_TIMER)) {
201
                        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
202
                        return -1;
203
                }
204
        }
205
        
206
        if(ParseConf())
207
        {
208
                printf("ERROR: Cannot parse configuration file, exit...\n");
209
                exit(1);
210
        }
211
        
212
        firstChannelIndex = -1;
213
        int it;
214
        for(it = 0; it < NChannels; it++)
215
        {
216
                if(!strcmp(Channels[it].Title, firstChannelName))
217
                {
218
                        firstChannelIndex = it;
219
                        break;
220
                }
221
        }
222
        
223
        if(firstChannelIndex < 0)
224
        {
225
                printf("Cannot find the specified channel (%s) into the configuration file (channels.conf), exiting\n", firstChannelName);
226
                exit(0);
227
        }
228
        
229
        if(ChunkerPlayerGUI_Init())
230
        {
231
                printf("ERROR: Cannot init player gui, exit...\n");
232
                exit(1);
233
        }
234
        
235
        SelectedChannel = firstChannelIndex;
236

    
237
        SwitchChannel(&(Channels[SelectedChannel]));
238

    
239
        // Wait for user input
240
        while(!quit) {
241
                if(QueueFillingMode) {
242
                        SDL_WM_SetCaption("Filling buffer...", NULL);
243

    
244
                        if(ChunkerPlayerCore_AudioEnded())
245
                                ChunkerPlayerCore_ResetAVQueues();
246

    
247
#ifdef DEBUG_QUEUE
248
                        //printf("QUEUE: MAIN audio:%d video:%d audiolastframe:%d videolastframe:%d\n", audioq.nb_packets, videoq.nb_packets, audioq.last_frame_extracted, videoq.last_frame_extracted);
249
#endif
250
                }
251
                else
252
                        SDL_WM_SetCaption("NAPA-Wine Player", NULL);
253

    
254
                //listen for key and mouse
255
                while(SDL_PollEvent(&event)) {
256
                        switch(event.type) {
257
                                case SDL_QUIT:
258
                                        quit=1;
259
                                break;
260
                                case SDL_VIDEORESIZE:
261
                                        if(SilentMode)
262
                                                break;
263
                                        // printf("\tSDL_VIDEORESIZE event received!! \n");
264
                                        if(!FullscreenMode)
265
                                                ChunkerPlayerGUI_HandleResize(event.resize.w, event.resize.h);
266
                                        else
267
                                                ChunkerPlayerGUI_HandleResize(FullscreenWidth, FullscreenHeight);
268
                                break;
269
                                case SDL_ACTIVEEVENT:
270
                                        if(SilentMode)
271
                                                break;
272
                                                
273
                                        // if the window was iconified or restored
274
                                        if(event.active.state & SDL_APPACTIVE)
275
                                        {
276
                                                //If the application is being reactivated
277
                                                if( event.active.gain != 0 )
278
                                                {
279
                                                        ChunkerPlayerGUI_HandleGetFocus();
280
                                                }
281
                                        }
282

    
283
                                        //If something happened to the keyboard focus
284
                                        else if( event.active.state & SDL_APPINPUTFOCUS )
285
                                        {
286
                                                //If the application gained keyboard focus
287
                                                if( event.active.gain != 0 )
288
                                                {
289
                                                        ChunkerPlayerGUI_HandleGetFocus();
290
                                                }
291
                                        }
292
                                        //If something happened to the mouse focus
293
                                        else if( event.active.state & SDL_APPMOUSEFOCUS )
294
                                        {
295
                                                //If the application gained mouse focus
296
                                                if( event.active.gain != 0 )
297
                                                {
298
                                                        ChunkerPlayerGUI_HandleGetFocus();
299
                                                }
300
                                        }
301
                                        break;
302
                                case SDL_MOUSEMOTION:
303
                                        if(SilentMode)
304
                                                break;
305
                                                
306
                                        ChunkerPlayerGUI_HandleMouseMotion(event.motion.x, event.motion.y);
307
                                break;
308
                                case SDL_MOUSEBUTTONUP:
309
                                        if(SilentMode)
310
                                                break;
311
                                                
312
                                        if( event.button.button != SDL_BUTTON_LEFT )
313
                                                break;
314

    
315
                                        ChunkerPlayerGUI_HandleLButton(event.motion.x, event.motion.y);
316
                                break;
317
                        }
318
                        ChunkerPlayerGUI_HandleKey();
319
                }
320
                usleep(120000);
321
        }
322

    
323
        KILL_PROCESS(P2PProcessHandle);
324

    
325
        //TERMINATE
326
        ChunkerPlayerCore_Stop();
327
        if(YUVOverlay != NULL)
328
                SDL_FreeYUVOverlay(YUVOverlay);
329
        
330
        ChunkerPlayerGUI_Close();
331
        SDL_DestroyMutex(OverlayMutex);
332
        SDL_Quit();
333
        
334
#ifdef HTTPIO
335
        finalizeChunkPuller(daemon);
336
#endif
337
#ifdef TCPIO
338
        finalizeChunkPuller();
339
#endif
340
        
341
#ifdef EMULATE_CHUNK_LOSS
342
        if(ScheduledChunkLosses)
343
                free(ScheduledChunkLosses);
344
#endif
345
        return 0;
346
}
347

    
348
int cb_validate_conffile(cfg_t *cfg)
349
{
350
        char LaunchString[255];
351
        cfg_t *cfg_greet;
352
        
353
        if(cfg_size(cfg, "Channel") == 0)
354
        {
355
                cfg_error(cfg, "no \"Channel\" section found");
356
                return -1;
357
        }
358
        
359
        printf("\t%d Channel setions found\n", cfg_size(cfg, "Channel"));
360
        
361
        int j;
362
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
363
        {
364
                cfg_greet = cfg_getnsec(cfg, "Channel", j);
365
                sprintf(LaunchString, "%s", cfg_getstr(cfg_greet, "LaunchString"));
366
                if(!(strlen(LaunchString) > 0))
367
                {
368
                        cfg_error(cfg, "invalid LaunchString for Channel[%d]", j);
369
                        return -1;
370
                }
371
                printf("\tChannel[%d].LaunchString = %s\n", j, LaunchString);
372
                printf("\tChannel[%d].AudioChannels = %ld\n", j, cfg_getint(cfg_greet, "AudioChannels"));
373
                printf("\tChannel[%d].SampleRate = %ld\n", j, cfg_getint(cfg_greet, "SampleRate"));
374
                printf("\tChannel[%d].Width = %ld\n", j, cfg_getint(cfg_greet, "Width"));
375
                printf("\tChannel[%d].Height = %ld\n", j, cfg_getint(cfg_greet, "Height"));
376
                printf("\tChannel[%d].Bitrate = %ld\n", j, cfg_getint(cfg_greet, "Bitrate"));
377
                printf("\tChannel[%d].Ratio = %s\n", j, cfg_getstr(cfg_greet, "Ratio"));
378
        }
379
    return 0;
380
}
381

    
382
int ParseConf()
383
{
384
        int j;
385
        
386
        // PARSING CONF FILE
387
        cfg_opt_t channel_opts[] =
388
        {
389
                CFG_STR("Title", "", CFGF_NONE),
390
                CFG_STR("LaunchString", "", CFGF_NONE),
391
                CFG_INT("AudioChannels", 2, CFGF_NONE),
392
                CFG_INT("SampleRate", 48000, CFGF_NONE),
393
                CFG_INT("Width", 176, CFGF_NONE),
394
                CFG_INT("Height", 144, CFGF_NONE),
395
                CFG_INT("Bitrate", 0, CFGF_NONE),
396
                
397
                // for some reason libconfuse parsing for floating point does not work in windows
398
                //~ CFG_FLOAT("Ratio", 1.22, CFGF_NONE),
399
                CFG_STR("Ratio", "1.22", CFGF_NONE),
400
                CFG_END()
401
        };
402
        cfg_opt_t opts[] =
403
        {
404
                CFG_SEC("Channel", channel_opts, CFGF_TITLE | CFGF_MULTI),
405
                CFG_END()
406
        };
407
        cfg_t *cfg, *cfg_channel;
408
        cfg = cfg_init(opts, CFGF_NONE);
409
        if(cfg_parse(cfg, DEFAULT_CONF_FILENAME) == CFG_PARSE_ERROR)
410
        {
411
                printf("Error while parsing configuration file, exiting...\n");
412
                cb_validate_conffile(cfg);
413
                return 1;
414
        }
415
        
416
        if(cfg_parse(cfg, DEFAULT_CONF_FILENAME) == CFG_FILE_ERROR)
417
        {
418
                printf("Error trying parsing configuration file. '%s' file couldn't be opened for reading\n", DEFAULT_CONF_FILENAME);
419
                return 1;
420
        }
421
        
422
        FILE * tmp_file;
423
        if(tmp_file = fopen(DEFAULT_PEEREXECNAME_FILENAME, "r")) {
424
                if(fscanf(tmp_file, "%s", StreamerFilename) != 1) {
425
                        printf("Wrong format of conf file %s containing peer application exec name. Assuming default: %s.\n\n", DEFAULT_PEEREXECNAME_FILENAME, DEFAULT_PEER_EXEC_NAME);
426
                }
427
                fclose(tmp_file);
428
        }
429
        else {
430
                printf("Could not find conf file %s containing peer application exec name. Exiting.\n\n", DEFAULT_PEEREXECNAME_FILENAME);
431
                exit(1);
432
        }
433
        if(tmp_file = fopen(StreamerFilename, "r"))
434
    {
435
        fclose(tmp_file);
436
    }
437
    else
438
        {
439
                printf("Could not find peer application (named '%s') into the current folder, please copy or link it into the player folder, then retry\n\n", StreamerFilename);
440
                exit(1);
441
        }
442
        
443
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
444
        {
445
                cfg_channel = cfg_getnsec(cfg, "Channel", j);
446
                sprintf(Channels[j].Title, "%s", cfg_title(cfg_channel));
447
                strcpy(Channels[j].LaunchString, cfg_getstr(cfg_channel, "LaunchString"));
448
                Channels[j].Width = cfg_getint(cfg_channel, "Width");
449
                Channels[j].Height = cfg_getint(cfg_channel, "Height");
450
                Channels[j].AudioChannels = cfg_getint(cfg_channel, "AudioChannels");
451
                Channels[j].SampleRate = cfg_getint(cfg_channel, "SampleRate");
452
                Channels[j].Ratio = strtof(cfg_getstr(cfg_channel, "Ratio"), 0);
453
                Channels[j].Bitrate = cfg_getint(cfg_channel, "Bitrate");
454
                
455
                Channels[j].Index = j+1;
456
                NChannels++;
457
        }
458
        cfg_free(cfg);
459

    
460
        return 0;
461
}
462

    
463
int ReTune(SChannel* channel)
464
{        
465
        if(ChunkerPlayerCore_IsRunning())
466
                ChunkerPlayerCore_Pause();
467
        
468
        //reset quality info
469
        channel->startTime = time(NULL);
470
        
471
        ChunkerPlayerCore_Play();
472
        
473
        return 0;
474
}
475

    
476
int SwitchChannel(SChannel* channel)
477
{
478
        int i=0;
479
#ifdef RESTORE_SCREEN_ON_ZAPPING
480
        int was_fullscreen = FullscreenMode;
481
        int old_width = window_width, old_height = window_height;
482
#endif
483
        
484
        if(ChunkerPlayerCore_IsRunning())
485
                ChunkerPlayerCore_Stop();
486

    
487
        KILL_PROCESS(P2PProcessHandle);
488
        
489
        ratio = channel->Ratio;
490
        ChunkerPlayerGUI_SetChannelTitle(channel->Title);
491
        ChunkerPlayerGUI_ForceResize(channel->Width, channel->Height);
492
        
493
        int w=0, h=0;
494
        ChunkerPlayerGUI_AspectRatioResize((float)channel->Ratio, channel->Width, channel->Height, &w, &h);
495
        ChunkerPlayerCore_SetupOverlay(w, h);
496
        //ChunkerPlayerGUI_SetupOverlayRect(channel);
497
        
498
        if(ChunkerPlayerCore_InitCodecs(channel->Width, channel->Height, channel->SampleRate, channel->AudioChannels) < 0)
499
        {
500
                printf("ERROR, COULD NOT INITIALIZE CODECS\n");
501
                exit(2);
502
        }
503
        
504
        //reset quality info
505
        channel->startTime = time(NULL);
506
        channel->instant_score = 0.0;
507
        channel->average_score = 0.0;
508
        channel->history_index = 0;
509
        for(i=0; i<CHANNEL_SCORE_HISTORY_SIZE; i++)
510
                channel->score_history[i] = -1;
511
        sprintf(channel->quality, "EVALUATING...");
512
        
513
        if(SilentMode != 3) //mode 3 is for GUI only, no P2P peer process
514
        {
515
                char argv0[255], parameters_string[511];
516
                sprintf(argv0, "%s", StreamerFilename);
517

    
518
#ifdef HTTPIO
519
                sprintf(parameters_string, "%s %s %s %d %s %s %d", "-C", channel->Title, "-P", (HttpPort+channel->Index), channel->LaunchString, "-F", HttpPort);
520
#endif
521

    
522
#ifdef TCPIO
523
                sprintf(parameters_string, "%s %s %s %d %s %s 127.0.0.1:%d", "-C", channel->Title, "-P", (TcpPort+channel->Index), channel->LaunchString, "-F", TcpPort);
524
#endif
525

    
526
                printf("OFFERSTREAMER LAUNCH STRING: %s %s\n", argv0, parameters_string);
527

    
528
#ifndef __WIN32__
529
                char* parameters_vector[255];
530
                parameters_vector[0] = argv0;
531
                
532
                // split parameters and count them
533
                int par_count=1;
534
                char* pch = strtok (parameters_string, " ");
535
                while (pch != NULL)
536
                {
537
                        if(par_count > 255) break;
538
                        // printf ("\tpch=%s\n",pch);
539
                        parameters_vector[par_count] = (char*) malloc(sizeof(char)*(strlen(pch)+1));
540
                        strcpy(parameters_vector[par_count], pch);
541
                        pch = strtok (NULL, " ");
542
                        par_count++;
543
                }
544
                parameters_vector[par_count] = NULL;
545

    
546
                int d;
547
                int stdoutS, stderrS;
548
                FILE* stream;
549
                stream = fopen("/dev/null", "a+");
550
                d = fileno(stream);
551

    
552
                // create backup descriptors for the current stdout and stderr devices
553
                stdoutS = dup(STDOUT_FILENO);
554
                stderrS = dup(STDERR_FILENO);
555
                
556
                // redirect child output to /dev/null
557
                dup2(d, STDOUT_FILENO);
558
                dup2(d, STDERR_FILENO);
559

    
560
                int pid = fork();
561
                if(pid == 0)
562
                {
563
                        execv(argv0, parameters_vector);
564
                        printf("ERROR, COULD NOT LAUNCH OFFERSTREAMER\n");
565
                        exit(2);
566
                }
567
                else
568
                        *((pid_t*)P2PProcessHandle) = pid;
569
                
570
                // restore backup descriptors in the parent process
571
                dup2(stdoutS, STDOUT_FILENO);
572
                dup2(stderrS, STDERR_FILENO);
573
                
574
                for(i=1; i<par_count; i++)
575
                        free(parameters_vector[i]);
576
                        
577
                fclose(stream);
578

    
579
#ifdef RESTORE_SCREEN_ON_ZAPPING
580
                if(was_fullscreen)
581
                        ChunkerPlayerGUI_ToggleFullscreen();
582
                else
583
                {
584
                        ChunkerPlayerGUI_HandleResize(old_width, old_height);
585
                }
586
#endif
587
        
588
#else
589

    
590
                STARTUPINFO sti;
591
                SECURITY_ATTRIBUTES sats = { 0 };
592
                DWORD writ, excode, read, available;
593
                int ret = 0;
594

    
595
                //set SECURITY_ATTRIBUTES struct fields
596
                sats.nLength = sizeof(sats);
597
                sats.bInheritHandle = TRUE;
598
                sats.lpSecurityDescriptor = NULL;
599

    
600
                ZeroMemory( &sti, sizeof(sti) );
601
                sti.cb = sizeof(sti);
602
                ZeroMemory( P2PProcessHandle, sizeof(PROCESS_INFORMATION) );
603

    
604
                char buffer[512];
605
                sprintf(buffer, "%s %s", argv0, parameters_string);
606

    
607
                if(!CreateProcess(NULL,
608
                  buffer,
609
                  &sats,
610
                  &sats,
611
                  TRUE,
612
                  0,
613
                  NULL,
614
                  NULL,
615
                  &sti,
616
                  P2PProcessHandle))
617
                {
618
                        printf("Unable to generate process \n");
619
                        return -1;
620
                }
621
#endif
622

    
623
        }
624
        ChunkerPlayerCore_Play();
625
        ChunkerPlayerGUI_ChannelSwitched();
626
        return 0;
627
}
628

    
629
void ZapDown()
630
{
631
        SelectedChannel = ((SelectedChannel+1) %NChannels);
632
        SwitchChannel(&(Channels[SelectedChannel]));
633
}
634

    
635
void ZapUp()
636
{
637
        SelectedChannel--;
638
        if(SelectedChannel < 0)
639
                SelectedChannel = NChannels-1;
640

    
641
        SwitchChannel(&(Channels[SelectedChannel]));
642
}
643

    
644
int enqueueBlock(const uint8_t *block, const int block_size)
645
{
646
        return ChunkerPlayerCore_EnqueueBlocks(block, block_size);
647
}