Statistics
| Branch: | Revision:

chunker-player / chunker_player / chunker_player.c @ add73b22

History | View | Annotate | Download (12.6 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

    
12
#ifdef WIN32
13
#include <windows.h> 
14
#endif
15

    
16
int main(int argc, char *argv[])
17
{
18
        // some initializations
19
        SilentMode = 0;
20
        queue_filling_threshold = 0;
21
        SaveYUV = 0;
22
        quit = 0;
23
        QueueFillingMode=1;
24

    
25
#ifndef __WIN32__
26
        static pid_t fork_pid = -1;
27
        P2PProcessHandle=&fork_pid;
28
#else
29
        static PROCESS_INFORMATION ProcessInfo;
30
        ZeroMemory( &ProcessInfo, sizeof(ProcessInfo) );
31
        P2PProcessHandle=&ProcessInfo;
32
#endif
33
        NChannels = 0;
34
        SelectedChannel = -1;
35
        char firstChannelName[255];
36
        int firstChannelIndex;
37
        
38
        memset((void*)Channels, 0, (MAX_CHANNELS_NUM*sizeof(SChannel)));
39

    
40
        HttpPort = -1;
41
        struct MHD_Daemon *daemon = NULL;
42
        SDL_Event event;
43
        OverlayMutex = SDL_CreateMutex();
44

    
45
        FILE *fp;
46
                
47
        if(argc<7) {
48
                printf("\nUSAGE:\n\tchunker_player queue_thresh player_ip player_port silentMode LossTracesFilenameSuffix ChannelName <YUVFilename>\n\n");
49
                exit(1);
50
        }
51
        sscanf(argv[1],"%d",&queue_filling_threshold);
52
        sscanf(argv[2],"%s",PlayerIP);
53
        sscanf(argv[3],"%d",&HttpPort);
54
        sscanf(argv[4],"%d",&SilentMode);
55
        sscanf(argv[5],"%s",LossTracesFilename);
56
        sscanf(argv[6],"%s",firstChannelName);
57
        
58
        if(argc==8)
59
        {
60
                sscanf(argv[7],"%s",YUVFileName);
61
                printf("YUVFile: %s\n",YUVFileName);
62
                fp=fopen(YUVFileName, "wb");
63
                if(fp)
64
                {
65
                        SaveYUV=1;
66
                        fclose(fp);
67
                }
68
                else
69
                        printf("ERROR: Unable to create YUVFile\n");
70
        }
71
        
72
        char filename[255];
73
        sprintf(filename, "audio_%s", LossTracesFilename);
74
        fp=fopen(filename, "wb");
75
        if(fp)
76
        {
77
                fclose(fp);
78
                sprintf(filename, "video_%s", LossTracesFilename);
79
                fp=fopen(filename, "wb");
80
                if(fp)
81
                        fclose(fp);
82
                else
83
                {
84
                        printf("ERROR: Unable to create loss trace files\n");
85
                        exit(1);
86
                }
87
        }
88
        else
89
        {
90
                printf("ERROR: Unable to create loss trace files\n");
91
                exit(1);
92
        }
93

    
94
        //this thread fetches chunks from the network by listening to the following path, port
95
        daemon = (struct MHD_Daemon*)initChunkPuller(UL_DEFAULT_EXTERNALPLAYER_PATH, HttpPort);
96
        if(daemon == NULL)
97
        {
98
                printf("CANNOT START MICROHTTPD SERVICE, EXITING...\n");
99
//                KILLALL("offerstreamer");
100
                exit(2);
101
        }
102

    
103
        if(!SilentMode)
104
        {
105
                if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
106
                        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
107
                        return -1;
108
                }
109
        }
110
        else
111
        {
112
                if(SDL_Init(SDL_INIT_TIMER)) {
113
                        fprintf(stderr, "Could not initialize SDL - %s\n", SDL_GetError());
114
                        return -1;
115
                }
116
        }
117
        
118
        if(ParseConf())
119
        {
120
                printf("ERROR: Cannot parse configuration file, exit...\n");
121
                exit(1);
122
        }
123
        
124
        firstChannelIndex = -1;
125
        int it;
126
        for(it = 0; it < NChannels; it++)
127
        {
128
                if(!strcmp(Channels[it].Title, firstChannelName))
129
                {
130
                        firstChannelIndex = it;
131
                        break;
132
                }
133
        }
134
        
135
        if(firstChannelIndex < 0)
136
        {
137
                printf("Cannot find the specified channel (%s) into the configuration file (channels.conf), exiting\n");
138
                exit(0);
139
        }
140
        
141
        if(ChunkerPlayerGUI_Init())
142
        {
143
                printf("ERROR: Cannot init player gui, exit...\n");
144
                exit(1);
145
        }
146
        
147
        SelectedChannel = firstChannelIndex;
148

    
149
        SwitchChannel(&(Channels[SelectedChannel]));
150

    
151
        // Wait for user input
152
        while(!quit) {
153
                if(QueueFillingMode) {
154
                        SDL_WM_SetCaption("Filling buffer...", NULL);
155

    
156
                        if(ChunkerPlayerCore_AudioEnded())
157
                                ChunkerPlayerCore_ResetAVQueues();
158

    
159
#ifdef DEBUG_QUEUE
160
                        //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);
161
#endif
162
                }
163
                else
164
                        SDL_WM_SetCaption("NAPA-Wine Player", NULL);
165

    
166
                //listen for key and mouse
167
                while(SDL_PollEvent(&event)) {
168
                        switch(event.type) {
169
                                case SDL_QUIT:
170
                                        quit=1;
171
                                break;
172
                                case SDL_VIDEORESIZE:
173
                                        if(SilentMode)
174
                                                break;
175
                                        // printf("\tSDL_VIDEORESIZE event received!! \n");
176
                                        if(!FullscreenMode)
177
                                                ChunkerPlayerGUI_HandleResize(event.resize.w, event.resize.h);
178
                                        else
179
                                                ChunkerPlayerGUI_HandleResize(FullscreenWidth, FullscreenHeight);
180
                                break;
181
                                case SDL_ACTIVEEVENT:
182
                                        if(SilentMode)
183
                                                break;
184
                                                
185
                                        // if the window was iconified or restored
186
                                        if(event.active.state & SDL_APPACTIVE)
187
                                        {
188
                                                //If the application is being reactivated
189
                                                if( event.active.gain != 0 )
190
                                                {
191
                                                        ChunkerPlayerGUI_HandleGetFocus();
192
                                                }
193
                                        }
194

    
195
                                        //If something happened to the keyboard focus
196
                                        else if( event.active.state & SDL_APPINPUTFOCUS )
197
                                        {
198
                                                //If the application gained keyboard focus
199
                                                if( event.active.gain != 0 )
200
                                                {
201
                                                        ChunkerPlayerGUI_HandleGetFocus();
202
                                                }
203
                                        }
204
                                        //If something happened to the mouse focus
205
                                        else if( event.active.state & SDL_APPMOUSEFOCUS )
206
                                        {
207
                                                //If the application gained mouse focus
208
                                                if( event.active.gain != 0 )
209
                                                {
210
                                                        ChunkerPlayerGUI_HandleGetFocus();
211
                                                }
212
                                        }
213
                                        break;
214
                                case SDL_MOUSEMOTION:
215
                                        if(SilentMode)
216
                                                break;
217
                                                
218
                                        ChunkerPlayerGUI_HandleMouseMotion(event.motion.x, event.motion.y);
219
                                break;
220
                                case SDL_MOUSEBUTTONUP:
221
                                        if(SilentMode)
222
                                                break;
223
                                                
224
                                        if( event.button.button != SDL_BUTTON_LEFT )
225
                                                break;
226

    
227
                                        ChunkerPlayerGUI_HandleLButton(event.motion.x, event.motion.y);
228
                                break;
229
                        }
230
                        ChunkerPlayerGUI_HandleKey();
231
                }
232
                usleep(120000);
233
        }
234

    
235
        KILL_PROCESS(P2PProcessHandle);
236

    
237
        //TERMINATE
238
        ChunkerPlayerCore_Stop();
239
        if(YUVOverlay != NULL)
240
                SDL_FreeYUVOverlay(YUVOverlay);
241
        
242
        ChunkerPlayerGUI_Close();
243
        SDL_DestroyMutex(OverlayMutex);
244
        SDL_Quit();
245
        finalizeChunkPuller(daemon);
246
        return 0;
247
}
248

    
249
int cb_validate_conffile(cfg_t *cfg)
250
{
251
        char PeerExecName[255];
252
        char LaunchString[255];
253
        int AudioChannels;
254
        int SampleRate;
255
        int Width;
256
        int Height;
257
        float Ratio;
258
        cfg_t *cfg_greet;
259
        
260
        sprintf(PeerExecName, "%s", cfg_getstr(cfg, "PeerExecName"));
261
        if(!(strlen(PeerExecName) > 0))
262
        {
263
                cfg_error(cfg, "invalid PeerExecName");
264
                return -1;
265
        }
266
        
267
        printf("\tPeerExecName ok\n");
268
        
269
        if(cfg_size(cfg, "Channel") == 0)
270
        {
271
                cfg_error(cfg, "no \"Channel\" section found");
272
                return -1;
273
        }
274
        
275
        printf("\t%d Channel setions found\n", cfg_size(cfg, "Channel"));
276
        
277
        int j;
278
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
279
        {
280
                cfg_greet = cfg_getnsec(cfg, "Channel", j);
281
                sprintf(LaunchString, "%s", cfg_getstr(cfg_greet, "LaunchString"));
282
                if(!(strlen(LaunchString) > 0))
283
                {
284
                        cfg_error(cfg, "invalid LaunchString for Channel[%d]", j);
285
                        return -1;
286
                }
287
                printf("\tChannel[%d].LaunchString = %s\n", j, LaunchString);
288
                printf("\tChannel[%d].AudioChannels = %d\n", j, cfg_getint(cfg_greet, "AudioChannels"));
289
                printf("\tChannel[%d].SampleRate = %d\n", j, cfg_getint(cfg_greet, "SampleRate"));
290
                printf("\tChannel[%d].Width = %d\n", j, cfg_getint(cfg_greet, "Width"));
291
                printf("\tChannel[%d].Height = %d\n", j, cfg_getint(cfg_greet, "Height"));
292
                printf("\tChannel[%d].Ratio = %s\n", j, cfg_getstr(cfg_greet, "Ratio"));
293
        }
294
    return 0;
295
}
296

    
297
int ParseConf()
298
{
299
        int j;
300
        
301
        // PARSING CONF FILE
302
        cfg_opt_t channel_opts[] =
303
        {
304
                CFG_STR("Title", "", CFGF_NONE),
305
                CFG_STR("LaunchString", "", CFGF_NONE),
306
                CFG_INT("AudioChannels", 2, CFGF_NONE),
307
                CFG_INT("SampleRate", 48000, CFGF_NONE),
308
                CFG_INT("Width", 176, CFGF_NONE),
309
                CFG_INT("Height", 144, CFGF_NONE),
310
                
311
                // for some reason libconfuse parsing for floating point does not work in windows
312
                //~ CFG_FLOAT("Ratio", 1.22, CFGF_NONE),
313
                CFG_STR("Ratio", "1.22", CFGF_NONE),
314
                CFG_END()
315
        };
316
        cfg_opt_t opts[] =
317
        {
318
                CFG_STR("PeerExecName", DEFAULT_CHANNEL_EXEC_NAME, CFGF_NONE),
319
                CFG_SEC("Channel", channel_opts, CFGF_TITLE | CFGF_MULTI),
320
                CFG_END()
321
        };
322
        cfg_t *cfg, *cfg_channel;
323
        cfg = cfg_init(opts, CFGF_NONE);
324
        if(cfg_parse(cfg, DEFAULT_CONF_FILENAME) == CFG_PARSE_ERROR)
325
        {
326
                printf("Error while parsing configuration file, exiting...\n");
327
                cb_validate_conffile(cfg);
328
                return 1;
329
        }
330
        
331
        if(cfg_parse(cfg, DEFAULT_CONF_FILENAME) == CFG_FILE_ERROR)
332
        {
333
                printf("Error trying parsing configuration file. '%s' file couldn't be opened for reading\n", DEFAULT_CONF_FILENAME);
334
                return 1;
335
        }
336
        
337
        sprintf(OfferStreamerFilename, "%s", cfg_getstr(cfg, "PeerExecName"));
338
        
339
        FILE * tmp_file;
340
        if(tmp_file = fopen(OfferStreamerFilename, "r"))
341
    {
342
        fclose(tmp_file);
343
    }
344
    else
345
        {
346
                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", OfferStreamerFilename);
347
                exit(1);
348
        }
349
        
350
        for(j = 0; j < cfg_size(cfg, "Channel"); j++)
351
        {
352
                cfg_channel = cfg_getnsec(cfg, "Channel", j);
353
                sprintf(Channels[j].Title, "%s", cfg_title(cfg_channel));
354
                strcpy(Channels[j].LaunchString, cfg_getstr(cfg_channel, "LaunchString"));
355
                Channels[j].Width = cfg_getint(cfg_channel, "Width");
356
                Channels[j].Height = cfg_getint(cfg_channel, "Height");
357
                Channels[j].AudioChannels = cfg_getint(cfg_channel, "AudioChannels");
358
                Channels[j].SampleRate = cfg_getint(cfg_channel, "SampleRate");
359
                Channels[j].Ratio = strtof(cfg_getstr(cfg_channel, "Ratio"), 0);
360
                
361
                Channels[j].Index = j+1;
362
                NChannels++;
363
        }
364
        cfg_free(cfg);
365

    
366
        return 0;
367
}
368

    
369
int SwitchChannel(SChannel* channel)
370
{
371
        int i=0;
372
#ifdef RESTORE_SCREEN_ON_ZAPPING
373
        int was_fullscreen = FullscreenMode;
374
        int old_width = window_width, old_height = window_height;
375
#endif
376
        
377
        if(ChunkerPlayerCore_IsRunning())
378
                ChunkerPlayerCore_Stop();
379

    
380
        KILL_PROCESS(P2PProcessHandle);
381
        
382
        ratio = channel->Ratio;
383
        ChunkerPlayerGUI_SetChannelTitle(channel->Title);
384
        ChunkerPlayerGUI_ForceResize(channel->Width, channel->Height);
385
        
386
        int w=0, h=0;
387
        ChunkerPlayerGUI_AspectRatioResize((float)channel->Ratio, channel->Width, channel->Height, &w, &h);
388
        ChunkerPlayerCore_SetupOverlay(w, h);
389
        //ChunkerPlayerGUI_SetupOverlayRect(channel);
390
        
391
        if(ChunkerPlayerCore_InitCodecs(channel->Width, channel->Height, channel->SampleRate, channel->AudioChannels) < 0)
392
        {
393
                printf("ERROR, COULD NOT INITIALIZE CODECS\n");
394
                exit(2);
395
        }
396
        
397
        //reset quality info
398
        channel->startTime = time(NULL);
399
        channel->instant_score = 0.0;
400
        channel->average_score = 0.0;
401
        channel->history_index = 0;
402
        for(i=0; i<CHANNEL_SCORE_HISTORY_SIZE; i++)
403
                channel->score_history[i] = -1;
404
        sprintf(channel->quality, "EVALUATING...");
405
                
406
        char argv0[255], parameters_string[511];
407
        sprintf(argv0, "%s", OfferStreamerFilename);
408
        
409
        sprintf(parameters_string, "%s %s %s %s %d %s %s:%d", channel->LaunchString, "-C", channel->Title, "-P", (HttpPort+channel->Index), "-F", PlayerIP, HttpPort);
410
        
411
        printf("OFFERSTREAMER LAUNCH STRING: %s %s\n", argv0, parameters_string);
412

    
413
#ifdef __LINUX__
414
        char* parameters_vector[255];
415
        parameters_vector[0] = argv0;
416
        
417
        // split parameters and count them
418
        int par_count=1;
419
        char* pch = strtok (parameters_string, " ");
420
        while (pch != NULL)
421
        {
422
                if(par_count > 255) break;
423
                // printf ("\tpch=%s\n",pch);
424
                parameters_vector[par_count] = (char*) malloc(sizeof(char)*(strlen(pch)+1));
425
                strcpy(parameters_vector[par_count], pch);
426
                pch = strtok (NULL, " ");
427
                par_count++;
428
        }
429
        parameters_vector[par_count] = NULL;
430

    
431
        int d;
432
        int stdoutS, stderrS;
433
        FILE* stream;
434
        stream = fopen("/dev/null", "a+");
435
        d = fileno(stream);
436

    
437
        // create backup descriptors for the current stdout and stderr devices
438
        stdoutS = dup(STDOUT_FILENO);
439
        stderrS = dup(STDERR_FILENO);
440
        
441
        // redirect child output to /dev/null
442
        dup2(d, STDOUT_FILENO);
443
        dup2(d, STDERR_FILENO);
444

    
445
        int pid = fork();
446
        if(pid == 0)
447
        {
448
                execv(argv0, parameters_vector);
449
                printf("ERROR, COULD NOT LAUNCH OFFERSTREAMER\n");
450
                exit(2);
451
        }
452
        else
453
                *((pid_t*)P2PProcessHandle) = pid;
454
        
455
        // restore backup descriptors in the parent process
456
        dup2(stdoutS, STDOUT_FILENO);
457
        dup2(stderrS, STDERR_FILENO);
458
        
459
        for(i=1; i<par_count; i++)
460
                free(parameters_vector[i]);
461
                
462
        fclose(stream);
463

    
464
#ifdef RESTORE_SCREEN_ON_ZAPPING
465
        if(was_fullscreen)
466
                ChunkerPlayerGUI_ToggleFullscreen();
467
        else
468
        {
469
                ChunkerPlayerGUI_HandleResize(old_width, old_height);
470
        }
471
#endif
472
        
473
        ChunkerPlayerCore_Play();
474
        
475
        return 0;
476

    
477
#else
478
#ifdef __WIN32__
479
        STARTUPINFO sti;
480
        SECURITY_ATTRIBUTES sats = { 0 }; 
481
        DWORD writ, excode, read, available; 
482
        int ret = 0; 
483

    
484
        //set SECURITY_ATTRIBUTES struct fields 
485
        sats.nLength = sizeof(sats); 
486
        sats.bInheritHandle = TRUE; 
487
        sats.lpSecurityDescriptor = NULL;
488
        
489
        ZeroMemory( &sti, sizeof(sti) );
490
    sti.cb = sizeof(sti);
491
    ZeroMemory( P2PProcessHandle, sizeof(PROCESS_INFORMATION) );
492
    
493
        if(!CreateProcess(argv0, 
494
          parameters_string,
495
          &sats, 
496
          &sats, 
497
          TRUE, 
498
          0, 
499
          NULL, 
500
          NULL, 
501
          &sti, 
502
          P2PProcessHandle))
503
        { 
504
                printf("Unable to generate process \n"); 
505
                return -1; 
506
        }
507
        
508
        ChunkerPlayerCore_Play();
509
        return 0;
510
#endif
511
#endif
512

    
513
        return 1;
514
}
515

    
516
void ZapDown()
517
{
518
        SelectedChannel = ((SelectedChannel+1) %NChannels);
519
        SwitchChannel(&(Channels[SelectedChannel]));
520
}
521

    
522
void ZapUp()
523
{
524
        SelectedChannel--;
525
        if(SelectedChannel < 0)
526
                SelectedChannel = NChannels-1;
527

    
528
        SwitchChannel(&(Channels[SelectedChannel]));
529
}
530

    
531
int enqueueBlock(const uint8_t *block, const int block_size)
532
{
533
        return ChunkerPlayerCore_EnqueueBlocks(block, block_size);
534
}