Statistics
| Branch: | Revision:

chunker-player / chunker_player / chunker_player.c @ 25d95dfe

History | View | Annotate | Download (22 KB)

1
/*
2
 *  Copyright (c) 2009-2011 Carmelo Daniele, Dario Marchese, Diego Reforgiato, Giuseppe Tropea
3
 *  developed for the Napa-Wine EU project. See www.napa-wine.eu
4
 *
5
 *  This is free software; see lgpl-2.1.txt
6
 */
7

    
8
#include <stdio.h>
9
#include <unistd.h>
10
#include <SDL.h>
11
#include <SDL_mutex.h>
12
#include "player_defines.h"
13
#include <confuse.h>
14
#include "http_default_urls.h"
15
#include "player_defines.h"
16
#include "chunker_player.h"
17
#include "chunk_puller.h"
18
#include "player_gui.h"
19
#include <time.h>
20
#include <getopt.h>
21

    
22
#ifdef PSNR_PUBLICATION
23
#include <event2/event.h>
24
#include <napa_log.h>
25
#endif
26

    
27
#ifdef CHANNELS_DOWNLOAD
28
#include "http.h"
29
#endif
30

    
31
#ifdef _WIN32
32
#include <windows.h>
33
#endif
34

    
35
#ifdef __linux__
36
#include <X11/Xlib.h>
37
#endif
38

    
39
int NChannels;
40
char StreamerFilename[255];
41
char *ConfFilename = NULL;
42
int Port;
43

    
44
int ParseConf(char *file, char *uri);
45
int SwitchChannel(SChannel* channel);
46

    
47
int ReadALine(FILE* fp, char* Output, int MaxOutputSize)
48
{
49
    int i=0;
50
    int c;
51
    do
52
    {
53
        c=getc(fp);
54
        if(c=='\r' || c=='\n' || c==EOF)
55
        {
56
            Output[i]=0;
57
            return i;
58
        }
59
        Output[i++]=c;
60
    }
61
    while(c!=EOF);
62
    
63
    return -1;
64
}
65

    
66
int CheckForRepoAddress(char* Param)
67
{
68
        int result = 0;
69
    int i, Found=0;
70

    
71
    if(strncasecmp(Param,"repo_address=",13)==0)
72
    {
73
        result=1;
74
        //find ',' in Param (if exist)
75
        int len=strlen(Param);
76
        char* tmp=(char*)calloc(1,len);
77
        if(tmp)
78
        {
79
            for(i=13;i<len;i++)
80
            {
81
                if(Param[i]==',')
82
                {
83
                    Found=1;
84
                    break;
85
                }
86
                tmp[i-13]=Param[i];
87
            }
88

    
89
            if(i==len || Found) strncpy(RepoAddress,tmp,2048);
90
            free(tmp);
91
        }
92
    }
93

    
94
        return result;
95
}
96

    
97
void sigproc()
98
{
99
        printf("you have pressed ctrl-c, terminating...\n");
100
        quit = 1;
101
}
102

    
103
static void print_usage(int argc, char *argv[])
104
{
105
  fprintf (stderr,
106
    "\nUsage:%s [options]\n"
107
    "\n"
108
    "\t[-c ChannelName]: channel name (from channels.conf)\n"
109
    "\t[-C file]: channel list file name (default: channels.conf)\n"
110
    "\t[-p port]: player http port\n"
111
    "\t[-q q_thresh]: playout queue size\n"
112
    "\t[-A audiocodec]\n"
113
    "\t[-V videocodec]\n"
114
    "\t[-t]: log traces (WARNING: old traces will be deleted).\n"
115
    "\t[-s mode]: silent mode (no GUI) (mode=1 audio ON, mode=2 audio OFF, mode=3 audio OFF; P2P OFF).\n\n"
116
    "=======================================================\n", argv[0]
117
    );
118
}
119

    
120
// initialize a receiver instance for receiving chunks from a Streamer process
121
// returns: <0 on error
122
int initIPCReceiver(Port)
123
{
124
#ifdef HTTPIO
125
        struct MHD_Daemon *daemon = NULL;
126
        //this thread fetches chunks from the network by listening to the following path, port
127
        daemon = (struct MHD_Daemon*)initChunkPuller(UL_DEFAULT_EXTERNALPLAYER_PATH, Port);
128
        if(daemon == NULL)
129
        {
130
                printf("CANNOT START MICROHTTPD SERVICE, EXITING...\n");
131
                return -1;
132
        }
133
#endif
134
#ifdef TCPIO
135
        int fd = initChunkPuller(Port);
136
        if(! (fd > 0))
137
        {
138
                printf("CANNOT START TCP PULLER...\n");
139
                return -1;
140
        }
141
#endif
142

    
143
        return 1;
144
}
145

    
146
int main(int argc, char *argv[])
147
{
148
        srand ( time(NULL) );
149
#ifdef __linux__
150
        XInitThreads();
151
#endif
152
        printf("[DEBUG] Player Video Started!\n");
153
        // some initializations
154
        SilentMode = 0;
155
        queue_filling_threshold = 5;
156
        quit = 0;
157
        QueueFillingMode=1;
158
        LogTraces = 0;
159
        qoe_led = 0;
160
        scale_with_sdl = SCALE_WITH_SDL_DEFAULT;
161

    
162
        NChannels = 0;
163
        SelectedChannel = -1;
164
        char firstChannelName[255];
165
        int firstChannelIndex;
166
        
167
        firstChannelName[0] = 0;
168
        memset((void*)Channels, 0, (MAX_CHANNELS_NUM*sizeof(SChannel)));
169

    
170
        Port = 6100;
171

    
172
        SDL_Event event;
173
        OverlayMutex = SDL_CreateMutex();
174
        
175
        char c;
176
        while ((c = getopt (argc, argv, "q:c:C:p:s:t")) != -1)
177
        {
178
                switch (c) {
179
                        case 0: //for long options
180
                                break;
181
                        case 'q':
182
                                sscanf(optarg, "%d", &queue_filling_threshold);
183
                                break;
184
                        case 'c':
185
                                sprintf(firstChannelName, "%s", optarg);
186
                                break;
187
                        case 'C':
188
                                ConfFilename = strdup(optarg);
189
                                break;
190
                        case 'p':
191
                                sscanf(optarg, "%d", &Port);
192
                                break;
193
                        case 's':
194
                                sscanf(optarg, "%d", &SilentMode);
195
                                break;
196
                        case 't':
197
                                DELETE_DIR("traces");
198
                                CREATE_DIR("traces");
199
                                LogTraces = 1;
200
                                break;
201
                        default:
202
                                print_usage(argc, argv);
203
                                return -1;
204
                }
205
        }
206

    
207
#ifdef EMULATE_CHUNK_LOSS
208
        printf("[DEBUG] Emulated chunk loss activated!\n");
209
        ScheduledChunkLosses = NULL;
210
        cfg_opt_t scheduled_chunk_loss_opts[] =
211
        {
212
                CFG_INT("Time", 0, CFGF_NONE),
213
                CFG_INT("Value", 0, CFGF_NONE),
214
                CFG_INT("MinValue", 0, CFGF_NONE),
215
                CFG_INT("MaxValue", 0, CFGF_NONE),
216
                CFG_INT("Burstiness", 0, CFGF_NONE),
217
                CFG_END()
218
        };
219
        cfg_opt_t opts[] =
220
        {
221
                CFG_SEC("ScheduledChunkLoss", scheduled_chunk_loss_opts, CFGF_MULTI),
222
                CFG_END()
223
        };
224
        cfg_t *cfg, *cfg_sched;
225
        cfg = cfg_init(opts, CFGF_NONE);
226
        if(!cfg_parse(cfg, "_chunklossrate.conf") == CFG_PARSE_ERROR)
227
        {
228
                NScheduledChunkLosses = cfg_size(cfg, "ScheduledChunkLoss");
229
                if(NScheduledChunkLosses > 0)
230
                        ScheduledChunkLosses = (SChunkLoss*)malloc((NScheduledChunkLosses)*sizeof(SChunkLoss));
231
                
232
                int j;
233
                for(j = 0; j < cfg_size(cfg, "ScheduledChunkLoss"); j++)
234
                {
235
                        cfg_sched = cfg_getnsec(cfg, "ScheduledChunkLoss", j);
236
                        ScheduledChunkLosses[j].Time = cfg_getint(cfg_sched, "Time");
237
                        ScheduledChunkLosses[j].Value = cfg_getint(cfg_sched, "Value");
238
                        ScheduledChunkLosses[j].Burstiness = cfg_getint(cfg_sched, "Burstiness");
239
                        
240
                        // -1 means random value between min and max
241
                        if(ScheduledChunkLosses[j].Value == -1)
242
                        {
243
                                ScheduledChunkLosses[j].MinValue = cfg_getint(cfg_sched, "MinValue");
244
                                ScheduledChunkLosses[j].MaxValue = cfg_getint(cfg_sched, "MaxValue");
245
                        }
246
                }
247
                cfg_free(cfg);
248
                CurrChunkLossIndex = -1;
249
                
250
                for(j=0; j < NScheduledChunkLosses; j++)
251
                {
252
                        printf("ScheduledChunkLosses[%d].Time = %ld\n", j, ScheduledChunkLosses[j].Time);
253
                        printf("ScheduledChunkLosses[%d].Value = %d\n", j, ScheduledChunkLosses[j].Value);
254
                        printf("ScheduledChunkLosses[%d].Burstiness = %d\n", j, ScheduledChunkLosses[j].Burstiness);
255
                }
256
        }
257
#endif
258

    
259
        if (initIPCReceiver(Port) < 0) {
260
        printf("[DEBUG] Failed to init IPC receiver...!\n");
261
                exit(2);
262
        }
263

    
264
#ifdef PSNR_PUBLICATION
265
        printf("[DEBUG] PSNR pubblication activated!\n");
266
        repoclient=NULL;
267
        LastTimeRepoPublish.tv_sec=0;
268
        LastTimeRepoPublish.tv_usec=0;
269
        eventbase = event_base_new();
270
        napaInitLog(LOG_DEBUG, NULL, NULL);
271
        repInit("");
272
#endif
273

    
274
        if(SilentMode == 0)
275
        {
276
                printf("[DEBUG] complete mode enabled\n");
277
                if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
278
                        fprintf(stderr, "Could not initialize SDL audio/video or timer - %s\n", SDL_GetError());
279
                        return -1;
280
                }
281
        }
282
        else if(SilentMode == 1)
283
        {
284
                printf("[DEBUG] Videoless mode enabled\n");
285
                if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
286
                        fprintf(stderr, "Could not initialize SDL audio or timer - %s\n", SDL_GetError());
287
                        return -1;
288
                }
289
        }
290
        else
291
        {
292
                printf("[DEBUG] Videoless and audioless mode enabled\n");
293
                if(SDL_Init(SDL_INIT_TIMER)) {
294
                        fprintf(stderr, "Could not initialize SDL timer - %s\n", SDL_GetError());
295
                        return -1;
296
                }
297
        }
298

    
299
        if (ConfFilename) {
300
                if(ParseConf(ConfFilename, NULL))
301
                {
302
                        printf("ERROR: Cannot parse configuration file %s, exit...\n", ConfFilename);
303
                        exit(1);
304
                }
305
        } else {
306
                if(ParseConf(DEFAULT_CONF_FILENAME, DEFAULT_CONF_URI))
307
                {
308
                        printf("ERROR: Cannot parse configuration file, exit...\n");
309
                        exit(1);
310
                }
311
        }
312

    
313
        printf("[DEBUG] Selecting channel...\n");
314
        firstChannelIndex = -1;
315
        int it;
316
        for(it = 0; it < NChannels; it++)
317
        {
318
                if(firstChannelName[0] == 0) {
319
                        firstChannelIndex = 0;
320
                        break;
321
                } else if (!strcmp(Channels[it].Title, firstChannelName))
322
                {
323
                        firstChannelIndex = it;
324
                        break;
325
                }
326
        }
327
        
328
        if(firstChannelIndex < 0)
329
        {
330
                printf("Cannot find the specified channel (%s) in the configuration file (channels.conf), exiting\n", firstChannelName);
331
                exit(0);
332
        }
333
        
334
        if(ChunkerPlayerGUI_Init())
335
        {
336
                printf("ERROR: Cannot init player gui, exit...\n");
337
                exit(1);
338
        }
339
        
340
        SelectedChannel = firstChannelIndex;
341

    
342
        printf("[DEBUG] Switching to channel...\n");
343
        SwitchChannel(&(Channels[SelectedChannel]));
344

    
345
        // Wait for user input
346
        while(!quit) {
347
                if(QueueFillingMode) {
348
                        if(ChunkerPlayerCore_AudioEnded())
349
                        {
350
                                printf("[DEBUG] Chunker player core audio ended...\n");
351
                                ChunkerPlayerCore_ResetAVQueues();
352
                        }
353

    
354
#ifdef DEBUG_QUEUE
355
                        //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);
356
#endif
357
                }
358
                
359
#ifdef PSNR_PUBLICATION
360
                event_base_loop(eventbase, EVLOOP_NONBLOCK);
361
#endif
362

    
363
                //listen for key and mouse
364
                while(SDL_PollEvent(&event)) {
365
                        switch(event.type) {
366
                                case SDL_QUIT:
367
                                        quit=1;
368
                                break;
369
                                case SDL_VIDEORESIZE:
370
                                        if(SilentMode)
371
                                                break;
372
                                        // printf("\tSDL_VIDEORESIZE event received!! \n");
373
                                        if(!FullscreenMode)
374
                                                ChunkerPlayerGUI_HandleResize(event.resize.w, event.resize.h);
375
                                        else
376
                                                ChunkerPlayerGUI_HandleResize(FullscreenWidth, FullscreenHeight);
377
                                break;
378
                                case SDL_ACTIVEEVENT:
379
                                        if(SilentMode)
380
                                                break;
381
                                                
382
                                        // if the window was iconified or restored
383
                                        if(event.active.state & SDL_APPACTIVE)
384
                                        {
385
                                                //If the application is being reactivated
386
                                                if( event.active.gain != 0 )
387
                                                {
388
                                                        ChunkerPlayerGUI_HandleGetFocus();
389
                                                }
390
                                        }
391

    
392
                                        //If something happened to the keyboard focus
393
                                        else if( event.active.state & SDL_APPINPUTFOCUS )
394
                                        {
395
                                                //If the application gained keyboard focus
396
                                                if( event.active.gain != 0 )
397
                                                {
398
                                                        ChunkerPlayerGUI_HandleGetFocus();
399
                                                }
400
                                        }
401
                                        //If something happened to the mouse focus
402
                                        else if( event.active.state & SDL_APPMOUSEFOCUS )
403
                                        {
404
                                                //If the application gained mouse focus
405
                                                if( event.active.gain != 0 )
406
                                                {
407
                                                        ChunkerPlayerGUI_HandleGetFocus();
408
                                                }
409
                                        }
410
                                        break;
411
                                case SDL_MOUSEMOTION:
412
                                        if(SilentMode)
413
                                                break;
414
                                                
415
                                        ChunkerPlayerGUI_HandleMouseMotion(event.motion.x, event.motion.y);
416
                                break;
417
                                case SDL_MOUSEBUTTONUP:
418
                                        if(SilentMode)
419
                                                break;
420
                                                
421
                                        if( event.button.button != SDL_BUTTON_LEFT )
422
                                                break;
423

    
424
                                        ChunkerPlayerGUI_HandleLButton(event.motion.x, event.motion.y);
425
                                break;
426
                                case SDL_KEYDOWN:  /* Handle a KEYDOWN event */
427
                                        switch( event.key.keysym.sym ){
428
                                                case SDLK_ESCAPE:
429
                                                        if(FullscreenMode) {
430
                                                                ChunkerPlayerGUI_ToggleFullscreen();
431
                                                        }
432
                                                        break;
433
                                                case SDLK_r:
434
                                                        ChunkerPlayerGUI_ChangeRatio();
435
                                                        break;
436
                                                case SDLK_q:
437
                                                        qoe_led = !qoe_led;
438
                                                        break;
439
                                                case SDLK_x:
440
                                                        scale_with_sdl = !scale_with_sdl;
441
                                                        break;
442
                                                case SDLK_f:
443
                                                        ChunkerPlayerGUI_ToggleFullscreen();
444
                                                        break;
445
                                                case SDLK_UP:
446
                                                        ZapUp();
447
                                                        break;
448
                                                case SDLK_DOWN:
449
                                                        ZapDown();
450
                                                        break;
451
                                                case SDLK_LEFT:
452
                                                        ChunkerPlayerCore_ChangeDelay(100);
453
                                                        break;
454
                                                case SDLK_RIGHT:
455
                                                        ChunkerPlayerCore_ChangeDelay(-100);
456
                                                        break;
457
                                                default:
458
                                                        break;
459
                                        }
460
                                break;
461
                        }
462
                        ChunkerPlayerGUI_HandleKey();
463
                }
464
                usleep(120000);
465
        }
466

    
467
        printf("[DEBUG] Exiting...\n");
468
        KILL_PROCESS(&(Channels[SelectedChannel].StreamerProcess));
469

    
470
        //TERMINATE
471
        ChunkerPlayerCore_Stop();
472
        ChunkerPlayerCore_Finalize();
473
        ChunkerPlayerGUI_Close();
474
        SDL_DestroyMutex(OverlayMutex);
475
        SDL_Quit();
476
        
477
#ifdef HTTPIO
478
        finalizeChunkPuller(daemon);
479
#endif
480
#ifdef TCPIO
481
        finalizeChunkPuller();
482
#endif
483
        
484
#ifdef EMULATE_CHUNK_LOSS
485
        if(ScheduledChunkLosses)
486
                free(ScheduledChunkLosses);
487
#endif
488

    
489
#ifdef PSNR_PUBLICATION
490
        if(repoclient) repClose(repoclient);        event_base_free(eventbase);
491
#endif
492
        return 0;
493
}
494

    
495
int cb_validate_conffile(cfg_t *cfg)
496
{
497
        char LaunchString[255];
498
        cfg_t *cfg_greet;
499
        
500
        if(cfg_size(cfg, "Channel") == 0)
501
        {
502
                cfg_error(cfg, "no \"Channel\" section found");
503
                return -1;
504
        }
505
        
506
        printf("\t%d Channel setions found\n", cfg_size(cfg, "Channel"));
507
        
508
        int j;
509
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
510
        {
511
                cfg_greet = cfg_getnsec(cfg, "Channel", j);
512
                sprintf(LaunchString, "%s", cfg_getstr(cfg_greet, "LaunchString"));
513
                if(!(strlen(LaunchString) > 0))
514
                {
515
                        cfg_error(cfg, "invalid LaunchString for Channel[%d]", j);
516
                        return -1;
517
                }
518
                printf("\tChannel[%d].LaunchString = %s\n", j, LaunchString);
519
                printf("\tChannel[%d].AudioChannels = %ld\n", j, cfg_getint(cfg_greet, "AudioChannels"));
520
                printf("\tChannel[%d].SampleRate = %ld\n", j, cfg_getint(cfg_greet, "SampleRate"));
521
                printf("\tChannel[%d].Width = %ld\n", j, cfg_getint(cfg_greet, "Width"));
522
                printf("\tChannel[%d].Height = %ld\n", j, cfg_getint(cfg_greet, "Height"));
523
                printf("\tChannel[%d].Bitrate = %ld\n", j, cfg_getint(cfg_greet, "Bitrate"));
524
                printf("\tChannel[%d].Ratio = %s\n", j, cfg_getstr(cfg_greet, "Ratio"));
525
        }
526
    return 0;
527
}
528

    
529
// helper funcion to trim the end of the channel name
530
int strtrim(char *h, const char *n)
531
{
532
        int hlen = strlen(h);
533
        int nlen = strlen(n);
534

    
535
        if (hlen >= nlen && strstr(h+hlen-nlen, n)) {
536
                h[hlen-nlen] = 0;
537
                return 1;
538
        }
539

    
540
        return 0;
541
}
542

    
543
int ParseConf(char *file, char *uri)
544
{
545
        int j,r;
546
        char * conf = NULL;
547

    
548
        // PARSING CONF FILE
549
        cfg_opt_t channel_opts[] =
550
        {
551
                CFG_STR("Title", "", CFGF_NONE),
552
                CFG_STR("ChannelGroup", "", CFGF_NONE),
553
                CFG_STR("LaunchString", "", CFGF_NONE),
554
                CFG_INT("AudioChannels", 2, CFGF_NONE),
555
                CFG_INT("SampleRate", 48000, CFGF_NONE),
556
                CFG_INT("Width", 176, CFGF_NONE),
557
                CFG_INT("Height", 144, CFGF_NONE),
558
                CFG_INT("Bitrate", 0, CFGF_NONE),
559
                CFG_STR("VideoCodec", "mpeg4", CFGF_NONE),
560
                CFG_STR("AudioCodec", "mp3", CFGF_NONE),
561
                
562
                // for some reason libconfuse parsing for floating point does not work in windows
563
                //~ CFG_FLOAT("Ratio", 1.22, CFGF_NONE),
564
                CFG_STR("Ratio", "1.22", CFGF_NONE),
565
                CFG_END()
566
        };
567
        cfg_opt_t opts[] =
568
        {
569
                CFG_SEC("Channel", channel_opts, CFGF_TITLE | CFGF_MULTI),
570
                CFG_END()
571
        };
572
        cfg_t *cfg, *cfg_channel;
573
        cfg = cfg_init(opts, CFGF_NONE);
574

    
575
#ifdef CHANNELS_DOWNLOAD
576
        if (uri) {
577
                http_get2file(uri, file);
578
        }
579
#endif
580

    
581
        r = cfg_parse(cfg, file);
582
        if (r == CFG_PARSE_ERROR) {
583
                printf("Error while parsing configuration file, exiting...\n");
584
                cb_validate_conffile(cfg);
585
                return 1;
586
        } else if (r == CFG_FILE_ERROR) {
587
                printf("Error trying parsing configuration file. '%s' couldn't be opened for reading\n", file);
588
                return 1;
589
        }
590

    
591
        FILE * tmp_file;
592
        if( (tmp_file = fopen(DEFAULT_PEEREXECNAME_FILENAME, "r")) ) {
593
                if(fscanf(tmp_file, "%s", StreamerFilename) != 1) {
594
                        printf("Wrong format of conf file %s containing peer application exec name. Assuming default: %s.\n\n", DEFAULT_PEEREXECNAME_FILENAME, DEFAULT_PEER_EXEC_NAME);
595
                        strncpy(StreamerFilename, DEFAULT_PEER_EXEC_NAME, 255);
596
                }
597
                fclose(tmp_file);
598
        }
599
        else {
600
                printf("Could not find conf file %s containing peer application exec name. Exiting.\n\n", DEFAULT_PEEREXECNAME_FILENAME);
601
                exit(1);
602
        }
603
        if( (tmp_file = fopen(StreamerFilename, "r")) )
604
    {
605
        fclose(tmp_file);
606
    }
607
    else
608
        {
609
                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);
610
                exit(1);
611
        }
612
        
613
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
614
        {
615
                cfg_channel = cfg_getnsec(cfg, "Channel", j);
616
                sprintf(Channels[j].Title, "%s", cfg_title(cfg_channel));
617
                strcpy(Channels[j].ChannelGroup, cfg_getstr(cfg_channel, "ChannelGroup"));
618
                if (strlen(Channels[j].ChannelGroup) == 0) {
619
                        strcpy(Channels[j].ChannelGroup,Channels[j].Title);
620
                        strtrim(Channels[j].ChannelGroup, "-HQ") || strtrim(Channels[j].ChannelGroup, "-SQ") || strtrim(Channels[j].ChannelGroup, "-LQ") || strtrim(Channels[j].ChannelGroup, "-orig");
621
                }
622
                strcpy(Channels[j].LaunchString, cfg_getstr(cfg_channel, "LaunchString"));
623
                strcpy(Channels[j].VideoCodec, cfg_getstr(cfg_channel, "VideoCodec"));
624
                strcpy(Channels[j].AudioCodec, cfg_getstr(cfg_channel, "AudioCodec"));
625
                Channels[j].Width = cfg_getint(cfg_channel, "Width");
626
                Channels[j].Height = cfg_getint(cfg_channel, "Height");
627
                Channels[j].AudioChannels = cfg_getint(cfg_channel, "AudioChannels");
628
                Channels[j].SampleRate = cfg_getint(cfg_channel, "SampleRate");
629
                Channels[j].Ratio = strtof(cfg_getstr(cfg_channel, "Ratio"), 0);
630
                Channels[j].Bitrate = cfg_getint(cfg_channel, "Bitrate");
631
                
632
                Channels[j].Index = j+1;
633
                NChannels++;
634
        }
635
        cfg_free(cfg);
636

    
637
        return 0;
638
}
639

    
640
int ReTune(SChannel* channel)
641
{        
642
        if(ChunkerPlayerCore_IsRunning())
643
                ChunkerPlayerCore_Pause();
644
        
645
        //reset quality info
646
        channel->startTime = time(NULL);
647
        
648
        ChunkerPlayerCore_Play();
649
        
650
        return 0;
651
}
652

    
653
int StartStreamer(SChannel* channel)
654
{
655
        char argv0[255], parameters_string[511];
656
        sprintf(argv0, "%s", StreamerFilename);
657

    
658
#ifdef HTTPIO
659
        sprintf(parameters_string, "%s %s %s %d %s %s %d", "-C", channel->ChannelGroup, "-P", (Port+channel->Index), channel->LaunchString, "-F", Port);
660
#endif
661

    
662
#ifdef TCPIO
663
        sprintf(parameters_string, "%s %s %s %d %s %s tcp://127.0.0.1:%d", "-C", channel->ChannelGroup, "-P", (Port+channel->Index), channel->LaunchString, "-F", Port);
664
#endif
665

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

    
668
        if(SilentMode != 3) //mode 3 is without P2P peer process
669
        {
670

    
671
#ifndef __WIN32__
672
                int i;
673
                char* parameters_vector[255];
674
                parameters_vector[0] = argv0;
675

    
676
                RepoAddress[0]='\0';
677

    
678
                // split parameters and count them
679
                int par_count=1;
680
                char* pch = strtok (parameters_string, " ");
681
                while (pch != NULL)
682
                {
683
                        if(par_count > 255) break;
684
                        //printf ("\tpch=%s\n",pch);
685
                        parameters_vector[par_count] = strdup(pch);
686
                        // Find repo_address
687
                        CheckForRepoAddress(parameters_vector[par_count]);
688
                        pch = strtok (NULL, " ");
689
                        par_count++;
690
                }
691
                parameters_vector[par_count] = NULL;
692

    
693
                int d;
694
                int stdoutS, stderrS;
695
                FILE* stream;
696
                stream = fopen("/dev/null", "a+");
697
                d = fileno(stream);
698

    
699
                // create backup descriptors for the current stdout and stderr devices
700
                stdoutS = dup(STDOUT_FILENO);
701
                stderrS = dup(STDERR_FILENO);
702
                
703
                // redirect child output to /dev/null
704
                dup2(d, STDOUT_FILENO);
705
                //dup2(d, STDERR_FILENO);
706

    
707
                int pid = fork();
708
                if(pid == 0)
709
                {
710
                        execv(argv0, parameters_vector);
711
                        printf("ERROR, COULD NOT LAUNCH OFFERSTREAMER\n");
712
                        exit(2);
713
                }
714
                else {
715
                        channel->StreamerProcess = pid;
716
                }
717

    
718
                // restore backup descriptors in the parent process
719
                dup2(stdoutS, STDOUT_FILENO);
720
                dup2(stderrS, STDERR_FILENO);
721

    
722
                fclose(stream);
723
                for(i=1; i<par_count; i++)
724
                        free(parameters_vector[i]);
725

    
726
#else
727
                STARTUPINFO sti;
728
                SECURITY_ATTRIBUTES sats = { 0 };
729
                int ret = 0;
730

    
731
                //set SECURITY_ATTRIBUTES struct fields
732
                sats.nLength = sizeof(sats);
733
                sats.bInheritHandle = TRUE;
734
                sats.lpSecurityDescriptor = NULL;
735

    
736
                ZeroMemory( &sti, sizeof(sti) );
737
                sti.cb = sizeof(sti);
738
                ZeroMemory( &channel->StreamerProcess, sizeof(PROCESS_INFORMATION) );
739

    
740
                char buffer[512];
741
                sprintf(buffer, "%s %s", argv0, parameters_string);
742

    
743
                if(!CreateProcess(NULL,
744
                  buffer,
745
                  &sats,
746
                  &sats,
747
                  TRUE,
748
                  0,
749
                  NULL,
750
                  NULL,
751
                  &sti,
752
                &channel->StreamerProcess))
753
                {
754
                        printf("Unable to generate process \n");
755
                        return -1;
756
                }
757
#endif
758
        }
759

    
760
        return 1;
761
}
762

    
763
int SwitchChannel(SChannel* channel)
764
{
765
        int i=0;
766
#ifdef RESTORE_SCREEN_ON_ZAPPING
767
        int was_fullscreen = FullscreenMode;
768
        int old_width = window_width, old_height = window_height;
769
#endif
770
        
771
        if(ChunkerPlayerCore_IsRunning())
772
                ChunkerPlayerCore_Stop();
773

    
774
#ifdef PSNR_PUBLICATION
775
        remove("NetworkID");
776
#endif
777
        
778
        ChunkerPlayerGUI_SetChannelRatio(channel->Ratio);
779
        ChunkerPlayerGUI_SetChannelTitle(channel->Title);
780
        ChunkerPlayerGUI_ForceResize(channel->Width, channel->Height);
781

    
782
        if (!scale_with_sdl) {        //TODO: maybe move to GUI
783
                int w=0, h=0;
784
                ChunkerPlayerGUI_AspectRatioResize((float)channel->Ratio, channel->Width, channel->Height, &w, &h);
785
                ChunkerPlayerCore_SetupOverlay(w, h);
786
        } else {
787
                ChunkerPlayerCore_SetupOverlay(channel->Width, channel->Height);
788
        }
789
        
790
        if(ChunkerPlayerCore_InitCodecs(channel->VideoCodec, channel->Width, channel->Height, channel->AudioCodec, channel->SampleRate, channel->AudioChannels) < 0)
791
        {
792
                printf("ERROR, COULD NOT INITIALIZE CODECS\n");
793
//                exit(2);
794
        }
795
        
796
        //reset quality info
797
        channel->startTime = time(NULL);
798
        channel->instant_score = 0.0;
799
        channel->average_score = 0.0;
800
        channel->history_index = 0;
801
        for(i=0; i<CHANNEL_SCORE_HISTORY_SIZE; i++)
802
                channel->score_history[i] = -1;
803
        sprintf(channel->quality, "EVALUATING...");
804

    
805
        StartStreamer(channel);
806

    
807
#ifdef RESTORE_SCREEN_ON_ZAPPING
808
        if(SilentMode == 0) {
809
                if(was_fullscreen)
810
                        ChunkerPlayerGUI_ToggleFullscreen();
811
                else
812
                {
813
                        ChunkerPlayerGUI_HandleResize(old_width, old_height);
814
                }
815
        }
816
#endif
817

    
818
#ifdef PSNR_PUBLICATION
819
        if(RepoAddress[0]!='\0')        // TODO: search for RepoAddress currently disabled on linux
820
        {
821
            // Open Repository
822
            if(repoclient)
823
                    repClose(repoclient);
824
            repoclient=NULL;
825

    
826
            repoclient = repOpen(RepoAddress,0);
827
            if (repoclient == NULL)
828
                    printf("Unable to initialize PSNR publication in repoclient %s\n", RepoAddress);
829
    }
830
    else {
831
            printf("Repository address not present in streames launch string. Publication disabled\n");
832
    }
833
#endif
834

    
835

    
836
#ifdef PSNR_PUBLICATION
837
        // Read the Network ID
838
        int Error=true;
839
        char Line1[255], Line2[255];
840
        while(Error)
841
        {
842
            FILE* fp=fopen("NetworkID","r");        //TODO: better error handling needed, this could block the player if there are no write permissions
843
            if(fp)
844
            {
845
                if(ReadALine(fp,Line1,255)!=-1)
846
                    if(ReadALine(fp,Line2,255)!=-1)
847
                    {
848
                        if(strcmp(Line2,"IDEnd")==0)
849
                        {
850
                            strcpy(NetworkID,Line1);
851
                            Error=false;
852
                        }
853
                    }
854
                fclose(fp);
855
            }
856
            if(Error) usleep(100000);
857
        }
858
        
859
        printf("NetworkID = %s\n",NetworkID);
860
#endif
861
        
862
        ChunkerPlayerCore_Play();
863
        ChunkerPlayerGUI_ChannelSwitched();
864
        return 0;
865
}
866

    
867
void selectedChannelVerifier()
868
{
869
        if(SelectedChannel < 0) SelectedChannel = NChannels - 1;
870
        if(SelectedChannel >= NChannels) SelectedChannel = 0;
871
}
872

    
873
void ZapDown()
874
{
875
        KILL_PROCESS(&Channels[SelectedChannel].StreamerProcess);
876
        SelectedChannel = ((SelectedChannel+1) %NChannels);
877
        selectedChannelVerifier();
878
        SwitchChannel(&(Channels[SelectedChannel]));
879
}
880

    
881
void ZapUp()
882
{
883
        KILL_PROCESS(&Channels[SelectedChannel].StreamerProcess);
884
        SelectedChannel = ((SelectedChannel+NChannels-1) %NChannels);
885
//        SelectedChannel--;
886
//        if(SelectedChannel < 0)
887
//                SelectedChannel = NChannels-1;
888
        selectedChannelVerifier();
889
        SwitchChannel(&(Channels[SelectedChannel]));
890
}
891

    
892
int enqueueBlock(const uint8_t *block, const int block_size)
893
{
894
        return ChunkerPlayerCore_EnqueueBlocks(block, block_size);
895
}