Statistics
| Branch: | Revision:

chunker-player / chunker_player / chunker_player.c @ 408d8a7a

History | View | Annotate | Download (20.5 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
        // some initializations
153
        SilentMode = 0;
154
        queue_filling_threshold = 5;
155
        quit = 0;
156
        QueueFillingMode=1;
157
        LogTraces = 0;
158
        qoe_led = 1;
159
        scale_with_sdl = SCALE_WITH_SDL_DEFAULT;
160

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

    
169
        Port = 6100;
170

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

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

    
257
        if (initIPCReceiver(Port) < 0) {
258
                exit(2);
259
        }
260

    
261
#ifdef PSNR_PUBLICATION
262
        repoclient=NULL;
263
        LastTimeRepoPublish.tv_sec=0;
264
        LastTimeRepoPublish.tv_usec=0;
265
        eventbase = event_base_new();
266
        napaInitLog(LOG_DEBUG, NULL, NULL);
267
        repInit("");
268
#endif
269

    
270
        if(SilentMode == 0)
271
        {
272
                if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
273
                        fprintf(stderr, "Could not initialize SDL audio/video or timer - %s\n", SDL_GetError());
274
                        return -1;
275
                }
276
        }
277
        else if(SilentMode == 1)
278
        {
279
                if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
280
                        fprintf(stderr, "Could not initialize SDL audio or timer - %s\n", SDL_GetError());
281
                        return -1;
282
                }
283
        }
284
        else
285
        {
286
                if(SDL_Init(SDL_INIT_TIMER)) {
287
                        fprintf(stderr, "Could not initialize SDL timer - %s\n", SDL_GetError());
288
                        return -1;
289
                }
290
        }
291

    
292
        if (ConfFilename) {
293
                if(ParseConf(ConfFilename, NULL))
294
                {
295
                        printf("ERROR: Cannot parse configuration file %s, exit...\n", ConfFilename);
296
                        exit(1);
297
                }
298
        } else {
299
                if(ParseConf(DEFAULT_CONF_FILENAME, DEFAULT_CONF_URI))
300
                {
301
                        printf("ERROR: Cannot parse configuration file, exit...\n");
302
                        exit(1);
303
                }
304
        }
305

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

    
334
        SwitchChannel(&(Channels[SelectedChannel]));
335

    
336
        // Wait for user input
337
        while(!quit) {
338
                if(QueueFillingMode) {
339
                        if(ChunkerPlayerCore_AudioEnded())
340
                                ChunkerPlayerCore_ResetAVQueues();
341

    
342
#ifdef DEBUG_QUEUE
343
                        //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);
344
#endif
345
                }
346
                
347
#ifdef PSNR_PUBLICATION
348
                event_base_loop(eventbase, EVLOOP_NONBLOCK);
349
#endif
350

    
351
                //listen for key and mouse
352
                while(SDL_PollEvent(&event)) {
353
                        switch(event.type) {
354
                                case SDL_QUIT:
355
                                        quit=1;
356
                                break;
357
                                case SDL_VIDEORESIZE:
358
                                        if(SilentMode)
359
                                                break;
360
                                        // printf("\tSDL_VIDEORESIZE event received!! \n");
361
                                        if(!FullscreenMode)
362
                                                ChunkerPlayerGUI_HandleResize(event.resize.w, event.resize.h);
363
                                        else
364
                                                ChunkerPlayerGUI_HandleResize(FullscreenWidth, FullscreenHeight);
365
                                break;
366
                                case SDL_ACTIVEEVENT:
367
                                        if(SilentMode)
368
                                                break;
369
                                                
370
                                        // if the window was iconified or restored
371
                                        if(event.active.state & SDL_APPACTIVE)
372
                                        {
373
                                                //If the application is being reactivated
374
                                                if( event.active.gain != 0 )
375
                                                {
376
                                                        ChunkerPlayerGUI_HandleGetFocus();
377
                                                }
378
                                        }
379

    
380
                                        //If something happened to the keyboard focus
381
                                        else if( event.active.state & SDL_APPINPUTFOCUS )
382
                                        {
383
                                                //If the application gained keyboard focus
384
                                                if( event.active.gain != 0 )
385
                                                {
386
                                                        ChunkerPlayerGUI_HandleGetFocus();
387
                                                }
388
                                        }
389
                                        //If something happened to the mouse focus
390
                                        else if( event.active.state & SDL_APPMOUSEFOCUS )
391
                                        {
392
                                                //If the application gained mouse focus
393
                                                if( event.active.gain != 0 )
394
                                                {
395
                                                        ChunkerPlayerGUI_HandleGetFocus();
396
                                                }
397
                                        }
398
                                        break;
399
                                case SDL_MOUSEMOTION:
400
                                        if(SilentMode)
401
                                                break;
402
                                                
403
                                        ChunkerPlayerGUI_HandleMouseMotion(event.motion.x, event.motion.y);
404
                                break;
405
                                case SDL_MOUSEBUTTONUP:
406
                                        if(SilentMode)
407
                                                break;
408
                                                
409
                                        if( event.button.button != SDL_BUTTON_LEFT )
410
                                                break;
411

    
412
                                        ChunkerPlayerGUI_HandleLButton(event.motion.x, event.motion.y);
413
                                break;
414
                                case SDL_KEYDOWN:  /* Handle a KEYDOWN event */
415
                                        switch( event.key.keysym.sym ){
416
                                                case SDLK_ESCAPE:
417
                                                        if(FullscreenMode) {
418
                                                                ChunkerPlayerGUI_ToggleFullscreen();
419
                                                        }
420
                                                        break;
421
                                                case SDLK_r:
422
                                                        ChunkerPlayerGUI_ChangeRatio();
423
                                                        break;
424
                                                case SDLK_q:
425
                                                        qoe_led = !qoe_led;
426
                                                        break;
427
                                                case SDLK_x:
428
                                                        scale_with_sdl = !scale_with_sdl;
429
                                                        break;
430
                                                case SDLK_f:
431
                                                        ChunkerPlayerGUI_ToggleFullscreen();
432
                                                        break;
433
                                                case SDLK_UP:
434
                                                        ZapUp();
435
                                                        break;
436
                                                case SDLK_DOWN:
437
                                                        ZapDown();
438
                                                        break;
439
                                                case SDLK_LEFT:
440
                                                        ChunkerPlayerCore_ChangeDelay(100);
441
                                                        break;
442
                                                case SDLK_RIGHT:
443
                                                        ChunkerPlayerCore_ChangeDelay(-100);
444
                                                        break;
445
                                                default:
446
                                                        break;
447
                                        }
448
                                break;
449
                        }
450
                        ChunkerPlayerGUI_HandleKey();
451
                }
452
                usleep(120000);
453
        }
454

    
455
        KILL_PROCESS(&(Channels[SelectedChannel].StreamerProcess));
456

    
457
        //TERMINATE
458
        ChunkerPlayerCore_Stop();
459
        ChunkerPlayerCore_Finalize();
460
        ChunkerPlayerGUI_Close();
461
        SDL_DestroyMutex(OverlayMutex);
462
        SDL_Quit();
463
        
464
#ifdef HTTPIO
465
        finalizeChunkPuller(daemon);
466
#endif
467
#ifdef TCPIO
468
        finalizeChunkPuller();
469
#endif
470
        
471
#ifdef EMULATE_CHUNK_LOSS
472
        if(ScheduledChunkLosses)
473
                free(ScheduledChunkLosses);
474
#endif
475

    
476
#ifdef PSNR_PUBLICATION
477
        if(repoclient) repClose(repoclient);        event_base_free(eventbase);
478
#endif
479
        return 0;
480
}
481

    
482
int cb_validate_conffile(cfg_t *cfg)
483
{
484
        char LaunchString[255];
485
        cfg_t *cfg_greet;
486
        
487
        if(cfg_size(cfg, "Channel") == 0)
488
        {
489
                cfg_error(cfg, "no \"Channel\" section found");
490
                return -1;
491
        }
492
        
493
        printf("\t%d Channel setions found\n", cfg_size(cfg, "Channel"));
494
        
495
        int j;
496
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
497
        {
498
                cfg_greet = cfg_getnsec(cfg, "Channel", j);
499
                sprintf(LaunchString, "%s", cfg_getstr(cfg_greet, "LaunchString"));
500
                if(!(strlen(LaunchString) > 0))
501
                {
502
                        cfg_error(cfg, "invalid LaunchString for Channel[%d]", j);
503
                        return -1;
504
                }
505
                printf("\tChannel[%d].LaunchString = %s\n", j, LaunchString);
506
                printf("\tChannel[%d].AudioChannels = %ld\n", j, cfg_getint(cfg_greet, "AudioChannels"));
507
                printf("\tChannel[%d].SampleRate = %ld\n", j, cfg_getint(cfg_greet, "SampleRate"));
508
                printf("\tChannel[%d].Width = %ld\n", j, cfg_getint(cfg_greet, "Width"));
509
                printf("\tChannel[%d].Height = %ld\n", j, cfg_getint(cfg_greet, "Height"));
510
                printf("\tChannel[%d].Bitrate = %ld\n", j, cfg_getint(cfg_greet, "Bitrate"));
511
                printf("\tChannel[%d].Ratio = %s\n", j, cfg_getstr(cfg_greet, "Ratio"));
512
        }
513
    return 0;
514
}
515

    
516
int ParseConf(char *file, char *uri)
517
{
518
        int j,r;
519
        char * conf = NULL;
520

    
521
        // PARSING CONF FILE
522
        cfg_opt_t channel_opts[] =
523
        {
524
                CFG_STR("Title", "", CFGF_NONE),
525
                CFG_STR("LaunchString", "", CFGF_NONE),
526
                CFG_INT("AudioChannels", 2, CFGF_NONE),
527
                CFG_INT("SampleRate", 48000, CFGF_NONE),
528
                CFG_INT("Width", 176, CFGF_NONE),
529
                CFG_INT("Height", 144, CFGF_NONE),
530
                CFG_INT("Bitrate", 0, CFGF_NONE),
531
                CFG_STR("VideoCodec", "mpeg4", CFGF_NONE),
532
                CFG_STR("AudioCodec", "mp3", CFGF_NONE),
533
                
534
                // for some reason libconfuse parsing for floating point does not work in windows
535
                //~ CFG_FLOAT("Ratio", 1.22, CFGF_NONE),
536
                CFG_STR("Ratio", "1.22", CFGF_NONE),
537
                CFG_END()
538
        };
539
        cfg_opt_t opts[] =
540
        {
541
                CFG_SEC("Channel", channel_opts, CFGF_TITLE | CFGF_MULTI),
542
                CFG_END()
543
        };
544
        cfg_t *cfg, *cfg_channel;
545
        cfg = cfg_init(opts, CFGF_NONE);
546

    
547
#ifdef CHANNELS_DOWNLOAD
548
        if (uri) {
549
                http_get2file(uri, file);
550
        }
551
#endif
552

    
553
        r = cfg_parse(cfg, file);
554
        if (r == CFG_PARSE_ERROR) {
555
                printf("Error while parsing configuration file, exiting...\n");
556
                cb_validate_conffile(cfg);
557
                return 1;
558
        } else if (r == CFG_FILE_ERROR) {
559
                printf("Error trying parsing configuration file. '%s' couldn't be opened for reading\n", file);
560
                return 1;
561
        }
562

    
563
        FILE * tmp_file;
564
        if( (tmp_file = fopen(DEFAULT_PEEREXECNAME_FILENAME, "r")) ) {
565
                if(fscanf(tmp_file, "%s", StreamerFilename) != 1) {
566
                        printf("Wrong format of conf file %s containing peer application exec name. Assuming default: %s.\n\n", DEFAULT_PEEREXECNAME_FILENAME, DEFAULT_PEER_EXEC_NAME);
567
                        strncpy(StreamerFilename, DEFAULT_PEER_EXEC_NAME, 255);
568
                }
569
                fclose(tmp_file);
570
        }
571
        else {
572
                printf("Could not find conf file %s containing peer application exec name. Exiting.\n\n", DEFAULT_PEEREXECNAME_FILENAME);
573
                exit(1);
574
        }
575
        if( (tmp_file = fopen(StreamerFilename, "r")) )
576
    {
577
        fclose(tmp_file);
578
    }
579
    else
580
        {
581
                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);
582
                exit(1);
583
        }
584
        
585
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
586
        {
587
                cfg_channel = cfg_getnsec(cfg, "Channel", j);
588
                sprintf(Channels[j].Title, "%s", cfg_title(cfg_channel));
589
                strcpy(Channels[j].LaunchString, cfg_getstr(cfg_channel, "LaunchString"));
590
                strcpy(Channels[j].VideoCodec, cfg_getstr(cfg_channel, "VideoCodec"));
591
                strcpy(Channels[j].AudioCodec, cfg_getstr(cfg_channel, "AudioCodec"));
592
                Channels[j].Width = cfg_getint(cfg_channel, "Width");
593
                Channels[j].Height = cfg_getint(cfg_channel, "Height");
594
                Channels[j].AudioChannels = cfg_getint(cfg_channel, "AudioChannels");
595
                Channels[j].SampleRate = cfg_getint(cfg_channel, "SampleRate");
596
                Channels[j].Ratio = strtof(cfg_getstr(cfg_channel, "Ratio"), 0);
597
                Channels[j].Bitrate = cfg_getint(cfg_channel, "Bitrate");
598
                
599
                Channels[j].Index = j+1;
600
                NChannels++;
601
        }
602
        cfg_free(cfg);
603

    
604
        return 0;
605
}
606

    
607
int ReTune(SChannel* channel)
608
{        
609
        if(ChunkerPlayerCore_IsRunning())
610
                ChunkerPlayerCore_Pause();
611
        
612
        //reset quality info
613
        channel->startTime = time(NULL);
614
        
615
        ChunkerPlayerCore_Play();
616
        
617
        return 0;
618
}
619

    
620
int StartStreamer(SChannel* channel)
621
{
622
        char argv0[255], parameters_string[511];
623
        sprintf(argv0, "%s", StreamerFilename);
624

    
625
#ifdef HTTPIO
626
        sprintf(parameters_string, "%s %s %s %d %s %s %d", "-C", channel->Title, "-P", (Port+channel->Index), channel->LaunchString, "-F", Port);
627
#endif
628

    
629
#ifdef TCPIO
630
        sprintf(parameters_string, "%s %s %s %d %s %s tcp://127.0.0.1:%d", "-C", channel->Title, "-P", (Port+channel->Index), channel->LaunchString, "-F", Port);
631
#endif
632

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

    
635
        if(SilentMode != 3) //mode 3 is without P2P peer process
636
        {
637

    
638
#ifndef __WIN32__
639
                int i;
640
                char* parameters_vector[255];
641
                parameters_vector[0] = argv0;
642

    
643
                RepoAddress[0]='\0';
644

    
645
                // split parameters and count them
646
                int par_count=1;
647
                char* pch = strtok (parameters_string, " ");
648
                while (pch != NULL)
649
                {
650
                        if(par_count > 255) break;
651
                        //printf ("\tpch=%s\n",pch);
652
                        parameters_vector[par_count] = strdup(pch);
653
                        // Find repo_address
654
                        CheckForRepoAddress(parameters_vector[par_count]);
655
                        pch = strtok (NULL, " ");
656
                        par_count++;
657
                }
658
                parameters_vector[par_count] = NULL;
659

    
660
                int d;
661
                int stdoutS, stderrS;
662
                FILE* stream;
663
                stream = fopen("/dev/null", "a+");
664
                d = fileno(stream);
665

    
666
                // create backup descriptors for the current stdout and stderr devices
667
                stdoutS = dup(STDOUT_FILENO);
668
                stderrS = dup(STDERR_FILENO);
669
                
670
                // redirect child output to /dev/null
671
                dup2(d, STDOUT_FILENO);
672
                //dup2(d, STDERR_FILENO);
673

    
674
                int pid = fork();
675
                if(pid == 0)
676
                {
677
                        execv(argv0, parameters_vector);
678
                        printf("ERROR, COULD NOT LAUNCH OFFERSTREAMER\n");
679
                        exit(2);
680
                }
681
                else {
682
                        channel->StreamerProcess = pid;
683
                }
684

    
685
                // restore backup descriptors in the parent process
686
                dup2(stdoutS, STDOUT_FILENO);
687
                dup2(stderrS, STDERR_FILENO);
688

    
689
                fclose(stream);
690
                for(i=1; i<par_count; i++)
691
                        free(parameters_vector[i]);
692

    
693
#else
694
                STARTUPINFO sti;
695
                SECURITY_ATTRIBUTES sats = { 0 };
696
                int ret = 0;
697

    
698
                //set SECURITY_ATTRIBUTES struct fields
699
                sats.nLength = sizeof(sats);
700
                sats.bInheritHandle = TRUE;
701
                sats.lpSecurityDescriptor = NULL;
702

    
703
                ZeroMemory( &sti, sizeof(sti) );
704
                sti.cb = sizeof(sti);
705
                ZeroMemory( &channel->StreamerProcess, sizeof(PROCESS_INFORMATION) );
706

    
707
                char buffer[512];
708
                sprintf(buffer, "%s %s", argv0, parameters_string);
709

    
710
                if(!CreateProcess(NULL,
711
                  buffer,
712
                  &sats,
713
                  &sats,
714
                  TRUE,
715
                  0,
716
                  NULL,
717
                  NULL,
718
                  &sti,
719
                &channel->StreamerProcess))
720
                {
721
                        printf("Unable to generate process \n");
722
                        return -1;
723
                }
724
#endif
725
        }
726

    
727
        return 1;
728
}
729

    
730
int SwitchChannel(SChannel* channel)
731
{
732
        int i=0;
733
#ifdef RESTORE_SCREEN_ON_ZAPPING
734
        int was_fullscreen = FullscreenMode;
735
        int old_width = window_width, old_height = window_height;
736
#endif
737
        
738
        if(ChunkerPlayerCore_IsRunning())
739
                ChunkerPlayerCore_Stop();
740

    
741
#ifdef PSNR_PUBLICATION
742
        remove("NetworkID");
743
#endif
744
        
745
        ChunkerPlayerGUI_SetChannelRatio(channel->Ratio);
746
        ChunkerPlayerGUI_SetChannelTitle(channel->Title);
747
        ChunkerPlayerGUI_ForceResize(channel->Width, channel->Height);
748

    
749
        if (!scale_with_sdl) {        //TODO: maybe move to GUI
750
                int w=0, h=0;
751
                ChunkerPlayerGUI_AspectRatioResize((float)channel->Ratio, channel->Width, channel->Height, &w, &h);
752
                ChunkerPlayerCore_SetupOverlay(w, h);
753
        } else {
754
                ChunkerPlayerCore_SetupOverlay(channel->Width, channel->Height);
755
        }
756
        
757
        if(ChunkerPlayerCore_InitCodecs(channel->VideoCodec, channel->Width, channel->Height, channel->AudioCodec, channel->SampleRate, channel->AudioChannels) < 0)
758
        {
759
                printf("ERROR, COULD NOT INITIALIZE CODECS\n");
760
                exit(2);
761
        }
762
        
763
        //reset quality info
764
        channel->startTime = time(NULL);
765
        channel->instant_score = 0.0;
766
        channel->average_score = 0.0;
767
        channel->history_index = 0;
768
        for(i=0; i<CHANNEL_SCORE_HISTORY_SIZE; i++)
769
                channel->score_history[i] = -1;
770
        sprintf(channel->quality, "EVALUATING...");
771

    
772
        StartStreamer(channel);
773

    
774
#ifdef RESTORE_SCREEN_ON_ZAPPING
775
        if(SilentMode == 0) {
776
                if(was_fullscreen)
777
                        ChunkerPlayerGUI_ToggleFullscreen();
778
                else
779
                {
780
                        ChunkerPlayerGUI_HandleResize(old_width, old_height);
781
                }
782
        }
783
#endif
784

    
785
#ifdef PSNR_PUBLICATION
786
        if(RepoAddress[0]!='\0')        // TODO: search for RepoAddress currently disabled on linux
787
        {
788
            // Open Repository
789
            if(repoclient)
790
                    repClose(repoclient);
791
            repoclient=NULL;
792

    
793
            repoclient = repOpen(RepoAddress,0);
794
            if (repoclient == NULL)
795
                    printf("Unable to initialize PSNR publication in repoclient %s\n", RepoAddress);
796
    }
797
    else {
798
            printf("Repository address not present in streames launch string. Publication disabled\n");
799
    }
800
#endif
801

    
802

    
803
#ifdef PSNR_PUBLICATION
804
        // Read the Network ID
805
        int Error=true;
806
        char Line1[255], Line2[255];
807
        while(Error)
808
        {
809
            FILE* fp=fopen("NetworkID","r");        //TODO: better error handling needed, this could block the player if there are no write permissions
810
            if(fp)
811
            {
812
                if(ReadALine(fp,Line1,255)!=-1)
813
                    if(ReadALine(fp,Line2,255)!=-1)
814
                    {
815
                        if(strcmp(Line2,"IDEnd")==0)
816
                        {
817
                            strcpy(NetworkID,Line1);
818
                            Error=false;
819
                        }
820
                    }
821
                fclose(fp);
822
            }
823
            if(Error) usleep(100000);
824
        }
825
        
826
        printf("NetworkID = %s\n",NetworkID);
827
#endif
828
        
829
        ChunkerPlayerCore_Play();
830
        ChunkerPlayerGUI_ChannelSwitched();
831
        return 0;
832
}
833

    
834
void ZapDown()
835
{
836
        KILL_PROCESS(&Channels[SelectedChannel].StreamerProcess);
837
        SelectedChannel = ((SelectedChannel+1) %NChannels);
838
        SwitchChannel(&(Channels[SelectedChannel]));
839
}
840

    
841
void ZapUp()
842
{
843
        KILL_PROCESS(&Channels[SelectedChannel].StreamerProcess);
844
        SelectedChannel--;
845
        if(SelectedChannel < 0)
846
                SelectedChannel = NChannels-1;
847

    
848
        SwitchChannel(&(Channels[SelectedChannel]));
849
}
850

    
851
int enqueueBlock(const uint8_t *block, const int block_size)
852
{
853
        return ChunkerPlayerCore_EnqueueBlocks(block, block_size);
854
}