chunker-player / chunker_player / chunker_player.c @ 157c26b4
History | View | Annotate | Download (18.8 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 "player_gui.h" |
18 |
#include <time.h> |
19 |
#include <getopt.h> |
20 |
|
21 |
#ifdef PSNR_PUBLICATION
|
22 |
#include <event2/event.h> |
23 |
#include <napa_log.h> |
24 |
#endif
|
25 |
|
26 |
#define MANDATORY_PARAMS 1 |
27 |
|
28 |
#ifdef _WIN32
|
29 |
#include <windows.h> |
30 |
#endif
|
31 |
|
32 |
static char *audio_codec = "mp2"; |
33 |
static char *video_codec = "mpeg4"; |
34 |
|
35 |
int ReadALine(FILE* fp, char* Output, int MaxOutputSize) |
36 |
{ |
37 |
int i=0; |
38 |
int c;
|
39 |
do
|
40 |
{ |
41 |
c=getc(fp); |
42 |
if(c=='\r' || c=='\n' || c==EOF) |
43 |
{ |
44 |
Output[i]=0;
|
45 |
return i;
|
46 |
} |
47 |
Output[i++]=c; |
48 |
} |
49 |
while(c!=EOF); |
50 |
|
51 |
return -1; |
52 |
} |
53 |
|
54 |
int CheckForRepoAddress(char* Param) |
55 |
{ |
56 |
int result = 0; |
57 |
int i, Found=0; |
58 |
|
59 |
if(strncasecmp(Param,"repo_address=",13)==0) |
60 |
{ |
61 |
result=1;
|
62 |
//find ',' in Param (if exist)
|
63 |
int len=strlen(Param);
|
64 |
char* tmp=(char*)calloc(1,len); |
65 |
if(tmp)
|
66 |
{ |
67 |
for(i=13;i<len;i++) |
68 |
{ |
69 |
if(Param[i]==',') |
70 |
{ |
71 |
Found=1;
|
72 |
break;
|
73 |
} |
74 |
tmp[i-13]=Param[i];
|
75 |
} |
76 |
|
77 |
if(i==len || Found) strncpy(RepoAddress,tmp,2048); |
78 |
free(tmp); |
79 |
} |
80 |
} |
81 |
|
82 |
return result;
|
83 |
} |
84 |
|
85 |
void sigproc()
|
86 |
{ |
87 |
printf("you have pressed ctrl-c, terminating...\n");
|
88 |
quit = 1;
|
89 |
} |
90 |
|
91 |
static void print_usage(int argc, char *argv[]) |
92 |
{ |
93 |
fprintf (stderr, |
94 |
"\nUsage:%s [options]\n"
|
95 |
"\n"
|
96 |
"Mandatory options:\n"
|
97 |
"\t[-c ChannelName]: channel name (from channels.conf)\n"
|
98 |
"Other options:\n"
|
99 |
"\t[-p port]: player http port\n\n"
|
100 |
"\t[-q q_thresh]: playout queue size\n"
|
101 |
"\t[-A audiocodec]\n"
|
102 |
"\t[-V videocodec]\n"
|
103 |
"\t[-t]: log traces (WARNING: old traces will be deleted).\n"
|
104 |
"\t[-s mode]: silent mode (no GUI) (mode=1 audio ON, mode=2 audio OFF, mode=3 audio OFF; P2P OFF).\n\n"
|
105 |
"=======================================================\n", argv[0] |
106 |
); |
107 |
} |
108 |
|
109 |
int main(int argc, char *argv[]) |
110 |
{ |
111 |
srand ( time(NULL) );
|
112 |
// some initializations
|
113 |
SilentMode = 0;
|
114 |
queue_filling_threshold = 50;
|
115 |
quit = 0;
|
116 |
QueueFillingMode=1;
|
117 |
LogTraces = 0;
|
118 |
|
119 |
#ifdef PSNR_PUBLICATION
|
120 |
repoclient=NULL;
|
121 |
LastTimeRepoPublish.tv_sec=0;
|
122 |
LastTimeRepoPublish.tv_usec=0;
|
123 |
eventbase = event_base_new(); |
124 |
napaInitLog(LOG_DEBUG, NULL, NULL); |
125 |
repInit("");
|
126 |
#endif
|
127 |
|
128 |
#ifndef __WIN32__
|
129 |
static pid_t fork_pid = -1; |
130 |
P2PProcessHandle=&fork_pid; |
131 |
#else
|
132 |
static PROCESS_INFORMATION ProcessInfo;
|
133 |
ZeroMemory( &ProcessInfo, sizeof(ProcessInfo) );
|
134 |
P2PProcessHandle=&ProcessInfo; |
135 |
#endif
|
136 |
|
137 |
NChannels = 0;
|
138 |
SelectedChannel = -1;
|
139 |
char firstChannelName[255]; |
140 |
int firstChannelIndex;
|
141 |
|
142 |
memset((void*)Channels, 0, (MAX_CHANNELS_NUM*sizeof(SChannel))); |
143 |
|
144 |
Port = 9876;
|
145 |
|
146 |
struct MHD_Daemon *daemon = NULL; |
147 |
SDL_Event event; |
148 |
OverlayMutex = SDL_CreateMutex(); |
149 |
int mandatories = 0; |
150 |
|
151 |
char c;
|
152 |
while ((c = getopt (argc, argv, "q:c:p:A:V:s:t")) != -1) |
153 |
{ |
154 |
switch (c) {
|
155 |
case 0: //for long options |
156 |
break;
|
157 |
case 'q': |
158 |
sscanf(optarg, "%d", &queue_filling_threshold);
|
159 |
break;
|
160 |
case 'c': |
161 |
sprintf(firstChannelName, "%s", optarg);
|
162 |
mandatories++; |
163 |
break;
|
164 |
case 'p': |
165 |
sscanf(optarg, "%d", &Port);
|
166 |
break;
|
167 |
case 'A': |
168 |
audio_codec = strdup(optarg); |
169 |
break;
|
170 |
case 'V': |
171 |
video_codec = strdup(optarg); |
172 |
break;
|
173 |
case 's': |
174 |
sscanf(optarg, "%d", &SilentMode);
|
175 |
break;
|
176 |
case 't': |
177 |
DELETE_DIR("traces");
|
178 |
CREATE_DIR("traces");
|
179 |
LogTraces = 1;
|
180 |
break;
|
181 |
default:
|
182 |
print_usage(argc, argv); |
183 |
return -1; |
184 |
} |
185 |
} |
186 |
if(mandatories < MANDATORY_PARAMS)
|
187 |
{ |
188 |
print_usage(argc, argv); |
189 |
return -1; |
190 |
} |
191 |
|
192 |
#ifdef EMULATE_CHUNK_LOSS
|
193 |
ScheduledChunkLosses = NULL;
|
194 |
cfg_opt_t scheduled_chunk_loss_opts[] = |
195 |
{ |
196 |
CFG_INT("Time", 0, CFGF_NONE), |
197 |
CFG_INT("Value", 0, CFGF_NONE), |
198 |
CFG_INT("MinValue", 0, CFGF_NONE), |
199 |
CFG_INT("MaxValue", 0, CFGF_NONE), |
200 |
CFG_INT("Burstiness", 0, CFGF_NONE), |
201 |
CFG_END() |
202 |
}; |
203 |
cfg_opt_t opts[] = |
204 |
{ |
205 |
CFG_SEC("ScheduledChunkLoss", scheduled_chunk_loss_opts, CFGF_MULTI),
|
206 |
CFG_END() |
207 |
}; |
208 |
cfg_t *cfg, *cfg_sched; |
209 |
cfg = cfg_init(opts, CFGF_NONE); |
210 |
if(!cfg_parse(cfg, "_chunklossrate.conf") == CFG_PARSE_ERROR) |
211 |
{ |
212 |
NScheduledChunkLosses = cfg_size(cfg, "ScheduledChunkLoss");
|
213 |
if(NScheduledChunkLosses > 0) |
214 |
ScheduledChunkLosses = (SChunkLoss*)malloc((NScheduledChunkLosses)*sizeof(SChunkLoss));
|
215 |
|
216 |
int j;
|
217 |
for(j = 0; j < cfg_size(cfg, "ScheduledChunkLoss"); j++) |
218 |
{ |
219 |
cfg_sched = cfg_getnsec(cfg, "ScheduledChunkLoss", j);
|
220 |
ScheduledChunkLosses[j].Time = cfg_getint(cfg_sched, "Time");
|
221 |
ScheduledChunkLosses[j].Value = cfg_getint(cfg_sched, "Value");
|
222 |
ScheduledChunkLosses[j].Burstiness = cfg_getint(cfg_sched, "Burstiness");
|
223 |
|
224 |
// -1 means random value between min and max
|
225 |
if(ScheduledChunkLosses[j].Value == -1) |
226 |
{ |
227 |
ScheduledChunkLosses[j].MinValue = cfg_getint(cfg_sched, "MinValue");
|
228 |
ScheduledChunkLosses[j].MaxValue = cfg_getint(cfg_sched, "MaxValue");
|
229 |
} |
230 |
} |
231 |
cfg_free(cfg); |
232 |
CurrChunkLossIndex = -1;
|
233 |
|
234 |
for(j=0; j < NScheduledChunkLosses; j++) |
235 |
{ |
236 |
printf("ScheduledChunkLosses[%d].Time = %ld\n", j, ScheduledChunkLosses[j].Time);
|
237 |
printf("ScheduledChunkLosses[%d].Value = %d\n", j, ScheduledChunkLosses[j].Value);
|
238 |
printf("ScheduledChunkLosses[%d].Burstiness = %d\n", j, ScheduledChunkLosses[j].Burstiness);
|
239 |
} |
240 |
} |
241 |
#endif
|
242 |
|
243 |
#ifdef HTTPIO
|
244 |
//this thread fetches chunks from the network by listening to the following path, port
|
245 |
daemon = (struct MHD_Daemon*)initChunkPuller(UL_DEFAULT_EXTERNALPLAYER_PATH, Port);
|
246 |
if(daemon == NULL) |
247 |
{ |
248 |
printf("CANNOT START MICROHTTPD SERVICE, EXITING...\n");
|
249 |
exit(2);
|
250 |
} |
251 |
#endif
|
252 |
#ifdef TCPIO
|
253 |
int fd = initChunkPuller(Port);
|
254 |
if(! (fd > 0)) |
255 |
{ |
256 |
printf("CANNOT START TCP PULLER...\n");
|
257 |
exit(2);
|
258 |
} |
259 |
#endif
|
260 |
|
261 |
if(SilentMode == 0) |
262 |
{ |
263 |
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
|
264 |
fprintf(stderr, "Could not initialize SDL audio/video or timer - %s\n", SDL_GetError());
|
265 |
return -1; |
266 |
} |
267 |
} |
268 |
else if(SilentMode == 1) |
269 |
{ |
270 |
if(SDL_Init(SDL_INIT_AUDIO | SDL_INIT_TIMER)) {
|
271 |
fprintf(stderr, "Could not initialize SDL audio or timer - %s\n", SDL_GetError());
|
272 |
return -1; |
273 |
} |
274 |
} |
275 |
else
|
276 |
{ |
277 |
if(SDL_Init(SDL_INIT_TIMER)) {
|
278 |
fprintf(stderr, "Could not initialize SDL timer - %s\n", SDL_GetError());
|
279 |
return -1; |
280 |
} |
281 |
} |
282 |
|
283 |
if(ParseConf())
|
284 |
{ |
285 |
printf("ERROR: Cannot parse configuration file, exit...\n");
|
286 |
exit(1);
|
287 |
} |
288 |
|
289 |
firstChannelIndex = -1;
|
290 |
int it;
|
291 |
for(it = 0; it < NChannels; it++) |
292 |
{ |
293 |
if(!strcmp(Channels[it].Title, firstChannelName))
|
294 |
{ |
295 |
firstChannelIndex = it; |
296 |
break;
|
297 |
} |
298 |
} |
299 |
|
300 |
if(firstChannelIndex < 0) |
301 |
{ |
302 |
printf("Cannot find the specified channel (%s) into the configuration file (channels.conf), exiting\n", firstChannelName);
|
303 |
exit(0);
|
304 |
} |
305 |
|
306 |
if(ChunkerPlayerGUI_Init())
|
307 |
{ |
308 |
printf("ERROR: Cannot init player gui, exit...\n");
|
309 |
exit(1);
|
310 |
} |
311 |
|
312 |
SelectedChannel = firstChannelIndex; |
313 |
|
314 |
SwitchChannel(&(Channels[SelectedChannel])); |
315 |
|
316 |
// Wait for user input
|
317 |
while(!quit) {
|
318 |
if(QueueFillingMode) {
|
319 |
SDL_WM_SetCaption("Filling buffer...", NULL); |
320 |
|
321 |
if(ChunkerPlayerCore_AudioEnded())
|
322 |
ChunkerPlayerCore_ResetAVQueues(); |
323 |
|
324 |
#ifdef DEBUG_QUEUE
|
325 |
//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);
|
326 |
#endif
|
327 |
} |
328 |
else
|
329 |
SDL_WM_SetCaption("NAPA-Wine Player", NULL); |
330 |
|
331 |
#ifdef PSNR_PUBLICATION
|
332 |
event_base_loop(eventbase, EVLOOP_ONCE); |
333 |
#endif
|
334 |
|
335 |
//listen for key and mouse
|
336 |
while(SDL_PollEvent(&event)) {
|
337 |
switch(event.type) {
|
338 |
case SDL_QUIT:
|
339 |
quit=1;
|
340 |
break;
|
341 |
case SDL_VIDEORESIZE:
|
342 |
if(SilentMode)
|
343 |
break;
|
344 |
// printf("\tSDL_VIDEORESIZE event received!! \n");
|
345 |
if(!FullscreenMode)
|
346 |
ChunkerPlayerGUI_HandleResize(event.resize.w, event.resize.h); |
347 |
else
|
348 |
ChunkerPlayerGUI_HandleResize(FullscreenWidth, FullscreenHeight); |
349 |
break;
|
350 |
case SDL_ACTIVEEVENT:
|
351 |
if(SilentMode)
|
352 |
break;
|
353 |
|
354 |
// if the window was iconified or restored
|
355 |
if(event.active.state & SDL_APPACTIVE)
|
356 |
{ |
357 |
//If the application is being reactivated
|
358 |
if( event.active.gain != 0 ) |
359 |
{ |
360 |
ChunkerPlayerGUI_HandleGetFocus(); |
361 |
} |
362 |
} |
363 |
|
364 |
//If something happened to the keyboard focus
|
365 |
else if( event.active.state & SDL_APPINPUTFOCUS ) |
366 |
{ |
367 |
//If the application gained keyboard focus
|
368 |
if( event.active.gain != 0 ) |
369 |
{ |
370 |
ChunkerPlayerGUI_HandleGetFocus(); |
371 |
} |
372 |
} |
373 |
//If something happened to the mouse focus
|
374 |
else if( event.active.state & SDL_APPMOUSEFOCUS ) |
375 |
{ |
376 |
//If the application gained mouse focus
|
377 |
if( event.active.gain != 0 ) |
378 |
{ |
379 |
ChunkerPlayerGUI_HandleGetFocus(); |
380 |
} |
381 |
} |
382 |
break;
|
383 |
case SDL_MOUSEMOTION:
|
384 |
if(SilentMode)
|
385 |
break;
|
386 |
|
387 |
ChunkerPlayerGUI_HandleMouseMotion(event.motion.x, event.motion.y); |
388 |
break;
|
389 |
case SDL_MOUSEBUTTONUP:
|
390 |
if(SilentMode)
|
391 |
break;
|
392 |
|
393 |
if( event.button.button != SDL_BUTTON_LEFT )
|
394 |
break;
|
395 |
|
396 |
ChunkerPlayerGUI_HandleLButton(event.motion.x, event.motion.y); |
397 |
break;
|
398 |
} |
399 |
ChunkerPlayerGUI_HandleKey(); |
400 |
} |
401 |
usleep(120000);
|
402 |
} |
403 |
|
404 |
KILL_PROCESS(P2PProcessHandle); |
405 |
|
406 |
//TERMINATE
|
407 |
ChunkerPlayerCore_Stop(); |
408 |
if(YUVOverlay != NULL) |
409 |
SDL_FreeYUVOverlay(YUVOverlay); |
410 |
|
411 |
ChunkerPlayerGUI_Close(); |
412 |
SDL_DestroyMutex(OverlayMutex); |
413 |
SDL_Quit(); |
414 |
|
415 |
#ifdef HTTPIO
|
416 |
finalizeChunkPuller(daemon); |
417 |
#endif
|
418 |
#ifdef TCPIO
|
419 |
finalizeChunkPuller(); |
420 |
#endif
|
421 |
|
422 |
#ifdef EMULATE_CHUNK_LOSS
|
423 |
if(ScheduledChunkLosses)
|
424 |
free(ScheduledChunkLosses); |
425 |
#endif
|
426 |
|
427 |
#ifdef PSNR_PUBLICATION
|
428 |
if(repoclient) repClose(repoclient);
|
429 |
event_base_free(eventbase); |
430 |
#endif
|
431 |
return 0; |
432 |
} |
433 |
|
434 |
int cb_validate_conffile(cfg_t *cfg)
|
435 |
{ |
436 |
char LaunchString[255]; |
437 |
cfg_t *cfg_greet; |
438 |
|
439 |
if(cfg_size(cfg, "Channel") == 0) |
440 |
{ |
441 |
cfg_error(cfg, "no \"Channel\" section found");
|
442 |
return -1; |
443 |
} |
444 |
|
445 |
printf("\t%d Channel setions found\n", cfg_size(cfg, "Channel")); |
446 |
|
447 |
int j;
|
448 |
for(j = 0; j < cfg_size(cfg, "Channel"); j++) |
449 |
{ |
450 |
cfg_greet = cfg_getnsec(cfg, "Channel", j);
|
451 |
sprintf(LaunchString, "%s", cfg_getstr(cfg_greet, "LaunchString")); |
452 |
if(!(strlen(LaunchString) > 0)) |
453 |
{ |
454 |
cfg_error(cfg, "invalid LaunchString for Channel[%d]", j);
|
455 |
return -1; |
456 |
} |
457 |
printf("\tChannel[%d].LaunchString = %s\n", j, LaunchString);
|
458 |
printf("\tChannel[%d].AudioChannels = %ld\n", j, cfg_getint(cfg_greet, "AudioChannels")); |
459 |
printf("\tChannel[%d].SampleRate = %ld\n", j, cfg_getint(cfg_greet, "SampleRate")); |
460 |
printf("\tChannel[%d].Width = %ld\n", j, cfg_getint(cfg_greet, "Width")); |
461 |
printf("\tChannel[%d].Height = %ld\n", j, cfg_getint(cfg_greet, "Height")); |
462 |
printf("\tChannel[%d].Bitrate = %ld\n", j, cfg_getint(cfg_greet, "Bitrate")); |
463 |
printf("\tChannel[%d].Ratio = %s\n", j, cfg_getstr(cfg_greet, "Ratio")); |
464 |
} |
465 |
return 0; |
466 |
} |
467 |
|
468 |
int ParseConf()
|
469 |
{ |
470 |
int j;
|
471 |
|
472 |
// PARSING CONF FILE
|
473 |
cfg_opt_t channel_opts[] = |
474 |
{ |
475 |
CFG_STR("Title", "", CFGF_NONE), |
476 |
CFG_STR("LaunchString", "", CFGF_NONE), |
477 |
CFG_INT("AudioChannels", 2, CFGF_NONE), |
478 |
CFG_INT("SampleRate", 48000, CFGF_NONE), |
479 |
CFG_INT("Width", 176, CFGF_NONE), |
480 |
CFG_INT("Height", 144, CFGF_NONE), |
481 |
CFG_INT("Bitrate", 0, CFGF_NONE), |
482 |
|
483 |
// for some reason libconfuse parsing for floating point does not work in windows
|
484 |
//~ CFG_FLOAT("Ratio", 1.22, CFGF_NONE),
|
485 |
CFG_STR("Ratio", "1.22", CFGF_NONE), |
486 |
CFG_END() |
487 |
}; |
488 |
cfg_opt_t opts[] = |
489 |
{ |
490 |
CFG_SEC("Channel", channel_opts, CFGF_TITLE | CFGF_MULTI),
|
491 |
CFG_END() |
492 |
}; |
493 |
cfg_t *cfg, *cfg_channel; |
494 |
cfg = cfg_init(opts, CFGF_NONE); |
495 |
if(cfg_parse(cfg, DEFAULT_CONF_FILENAME) == CFG_PARSE_ERROR)
|
496 |
{ |
497 |
printf("Error while parsing configuration file, exiting...\n");
|
498 |
cb_validate_conffile(cfg); |
499 |
return 1; |
500 |
} |
501 |
|
502 |
if(cfg_parse(cfg, DEFAULT_CONF_FILENAME) == CFG_FILE_ERROR)
|
503 |
{ |
504 |
printf("Error trying parsing configuration file. '%s' file couldn't be opened for reading\n", DEFAULT_CONF_FILENAME);
|
505 |
return 1; |
506 |
} |
507 |
|
508 |
FILE * tmp_file; |
509 |
if(tmp_file = fopen(DEFAULT_PEEREXECNAME_FILENAME, "r")) { |
510 |
if(fscanf(tmp_file, "%s", StreamerFilename) != 1) { |
511 |
printf("Wrong format of conf file %s containing peer application exec name. Assuming default: %s.\n\n", DEFAULT_PEEREXECNAME_FILENAME, DEFAULT_PEER_EXEC_NAME);
|
512 |
} |
513 |
fclose(tmp_file); |
514 |
} |
515 |
else {
|
516 |
printf("Could not find conf file %s containing peer application exec name. Exiting.\n\n", DEFAULT_PEEREXECNAME_FILENAME);
|
517 |
exit(1);
|
518 |
} |
519 |
if(tmp_file = fopen(StreamerFilename, "r")) |
520 |
{ |
521 |
fclose(tmp_file); |
522 |
} |
523 |
else
|
524 |
{ |
525 |
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);
|
526 |
exit(1);
|
527 |
} |
528 |
|
529 |
for(j = 0; j < cfg_size(cfg, "Channel"); j++) |
530 |
{ |
531 |
cfg_channel = cfg_getnsec(cfg, "Channel", j);
|
532 |
sprintf(Channels[j].Title, "%s", cfg_title(cfg_channel));
|
533 |
strcpy(Channels[j].LaunchString, cfg_getstr(cfg_channel, "LaunchString"));
|
534 |
Channels[j].Width = cfg_getint(cfg_channel, "Width");
|
535 |
Channels[j].Height = cfg_getint(cfg_channel, "Height");
|
536 |
Channels[j].AudioChannels = cfg_getint(cfg_channel, "AudioChannels");
|
537 |
Channels[j].SampleRate = cfg_getint(cfg_channel, "SampleRate");
|
538 |
Channels[j].Ratio = strtof(cfg_getstr(cfg_channel, "Ratio"), 0); |
539 |
Channels[j].Bitrate = cfg_getint(cfg_channel, "Bitrate");
|
540 |
|
541 |
Channels[j].Index = j+1;
|
542 |
NChannels++; |
543 |
} |
544 |
cfg_free(cfg); |
545 |
|
546 |
return 0; |
547 |
} |
548 |
|
549 |
int ReTune(SChannel* channel)
|
550 |
{ |
551 |
if(ChunkerPlayerCore_IsRunning())
|
552 |
ChunkerPlayerCore_Pause(); |
553 |
|
554 |
//reset quality info
|
555 |
channel->startTime = time(NULL);
|
556 |
|
557 |
ChunkerPlayerCore_Play(); |
558 |
|
559 |
return 0; |
560 |
} |
561 |
|
562 |
int SwitchChannel(SChannel* channel)
|
563 |
{ |
564 |
int i=0; |
565 |
#ifdef RESTORE_SCREEN_ON_ZAPPING
|
566 |
int was_fullscreen = FullscreenMode;
|
567 |
int old_width = window_width, old_height = window_height;
|
568 |
#endif
|
569 |
|
570 |
if(ChunkerPlayerCore_IsRunning())
|
571 |
ChunkerPlayerCore_Stop(); |
572 |
|
573 |
KILL_PROCESS(P2PProcessHandle); |
574 |
#ifdef PSNR_PUBLICATION
|
575 |
remove("NetworkID");
|
576 |
#endif
|
577 |
|
578 |
ratio = channel->Ratio; |
579 |
ChunkerPlayerGUI_SetChannelTitle(channel->Title); |
580 |
ChunkerPlayerGUI_ForceResize(channel->Width, channel->Height); |
581 |
|
582 |
int w=0, h=0; |
583 |
ChunkerPlayerGUI_AspectRatioResize((float)channel->Ratio, channel->Width, channel->Height, &w, &h);
|
584 |
ChunkerPlayerCore_SetupOverlay(w, h); |
585 |
//ChunkerPlayerGUI_SetupOverlayRect(channel);
|
586 |
|
587 |
if(ChunkerPlayerCore_InitCodecs(video_codec, channel->Width, channel->Height, audio_codec, channel->SampleRate, channel->AudioChannels) < 0) |
588 |
{ |
589 |
printf("ERROR, COULD NOT INITIALIZE CODECS\n");
|
590 |
exit(2);
|
591 |
} |
592 |
|
593 |
//reset quality info
|
594 |
channel->startTime = time(NULL);
|
595 |
channel->instant_score = 0.0; |
596 |
channel->average_score = 0.0; |
597 |
channel->history_index = 0;
|
598 |
for(i=0; i<CHANNEL_SCORE_HISTORY_SIZE; i++) |
599 |
channel->score_history[i] = -1;
|
600 |
sprintf(channel->quality, "EVALUATING...");
|
601 |
|
602 |
char argv0[255], parameters_string[511]; |
603 |
sprintf(argv0, "%s", StreamerFilename);
|
604 |
|
605 |
#ifdef HTTPIO
|
606 |
sprintf(parameters_string, "%s %s %s %d %s %s %d", "-C", channel->Title, "-P", (Port+channel->Index), channel->LaunchString, "-F", Port); |
607 |
#endif
|
608 |
|
609 |
#ifdef TCPIO
|
610 |
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); |
611 |
#endif
|
612 |
|
613 |
printf("OFFERSTREAMER LAUNCH STRING: %s %s\n", argv0, parameters_string);
|
614 |
|
615 |
char* parameters_vector[255]; |
616 |
parameters_vector[0] = argv0;
|
617 |
|
618 |
RepoAddress[0]='\0'; |
619 |
|
620 |
// split parameters and count them
|
621 |
int par_count=1; |
622 |
char* pch = strtok (parameters_string, " "); |
623 |
while (pch != NULL) |
624 |
{ |
625 |
if(par_count > 255) break; |
626 |
// printf ("\tpch=%s\n",pch);
|
627 |
parameters_vector[par_count] = (char*) malloc(sizeof(char)*(strlen(pch)+1)); |
628 |
strcpy(parameters_vector[par_count], pch); |
629 |
// Find repo_address
|
630 |
CheckForRepoAddress(parameters_vector[par_count]); |
631 |
pch = strtok (NULL, " "); |
632 |
par_count++; |
633 |
} |
634 |
parameters_vector[par_count] = NULL;
|
635 |
|
636 |
if(SilentMode != 3) //mode 3 is without P2P peer process |
637 |
{ |
638 |
|
639 |
#ifndef __WIN32__
|
640 |
int d;
|
641 |
int stdoutS, stderrS;
|
642 |
FILE* stream; |
643 |
stream = fopen("/dev/null", "a+"); |
644 |
d = fileno(stream); |
645 |
|
646 |
// create backup descriptors for the current stdout and stderr devices
|
647 |
stdoutS = dup(STDOUT_FILENO); |
648 |
stderrS = dup(STDERR_FILENO); |
649 |
|
650 |
// redirect child output to /dev/null
|
651 |
dup2(d, STDOUT_FILENO); |
652 |
dup2(d, STDERR_FILENO); |
653 |
|
654 |
int pid = fork();
|
655 |
if(pid == 0) |
656 |
{ |
657 |
execv(argv0, parameters_vector); |
658 |
printf("ERROR, COULD NOT LAUNCH OFFERSTREAMER\n");
|
659 |
exit(2);
|
660 |
} |
661 |
else
|
662 |
*((pid_t*)P2PProcessHandle) = pid; |
663 |
|
664 |
// restore backup descriptors in the parent process
|
665 |
dup2(stdoutS, STDOUT_FILENO); |
666 |
dup2(stderrS, STDERR_FILENO); |
667 |
|
668 |
fclose(stream); |
669 |
#else
|
670 |
STARTUPINFO sti; |
671 |
SECURITY_ATTRIBUTES sats = { 0 };
|
672 |
DWORD writ, excode, read, available; |
673 |
int ret = 0; |
674 |
|
675 |
//set SECURITY_ATTRIBUTES struct fields
|
676 |
sats.nLength = sizeof(sats);
|
677 |
sats.bInheritHandle = TRUE; |
678 |
sats.lpSecurityDescriptor = NULL;
|
679 |
|
680 |
ZeroMemory( &sti, sizeof(sti) );
|
681 |
sti.cb = sizeof(sti);
|
682 |
ZeroMemory( P2PProcessHandle, sizeof(PROCESS_INFORMATION) );
|
683 |
|
684 |
char buffer[512]; |
685 |
sprintf(buffer, "%s %s", argv0, parameters_string);
|
686 |
|
687 |
if(!CreateProcess(NULL, |
688 |
buffer, |
689 |
&sats, |
690 |
&sats, |
691 |
TRUE, |
692 |
0,
|
693 |
NULL,
|
694 |
NULL,
|
695 |
&sti, |
696 |
P2PProcessHandle)) |
697 |
{ |
698 |
printf("Unable to generate process \n");
|
699 |
return -1; |
700 |
} |
701 |
#endif
|
702 |
|
703 |
} |
704 |
|
705 |
#ifdef RESTORE_SCREEN_ON_ZAPPING
|
706 |
if(SilentMode == 0) { |
707 |
if(was_fullscreen)
|
708 |
ChunkerPlayerGUI_ToggleFullscreen(); |
709 |
else
|
710 |
{ |
711 |
ChunkerPlayerGUI_HandleResize(old_width, old_height); |
712 |
} |
713 |
} |
714 |
#endif
|
715 |
|
716 |
#ifdef PSNR_PUBLICATION
|
717 |
if(RepoAddress[0]!='\0') |
718 |
{ |
719 |
// Open Repository
|
720 |
if(repoclient)
|
721 |
repClose(repoclient); |
722 |
repoclient=NULL;
|
723 |
|
724 |
repoclient = repOpen(RepoAddress,0);
|
725 |
if (repoclient == NULL) |
726 |
printf("Unable to initialize PSNR publication in repoclient %s\n", RepoAddress);
|
727 |
} |
728 |
else {
|
729 |
printf("Repository address not present in streames launch string. Publication disabled\n");
|
730 |
} |
731 |
#endif
|
732 |
|
733 |
for(i=1; i<par_count; i++) |
734 |
free(parameters_vector[i]); |
735 |
|
736 |
#ifdef PSNR_PUBLICATION
|
737 |
// Read the Network ID
|
738 |
int Error=true; |
739 |
char Line1[255], Line2[255]; |
740 |
while(Error)
|
741 |
{ |
742 |
FILE* fp=fopen("NetworkID","r"); |
743 |
if(fp)
|
744 |
{ |
745 |
if(ReadALine(fp,Line1,255)!=-1) |
746 |
if(ReadALine(fp,Line2,255)!=-1) |
747 |
{ |
748 |
if(strcmp(Line2,"IDEnd")==0) |
749 |
{ |
750 |
strcpy(NetworkID,Line1); |
751 |
Error=false;
|
752 |
} |
753 |
} |
754 |
fclose(fp); |
755 |
} |
756 |
if(Error) usleep(100000); |
757 |
} |
758 |
|
759 |
printf("NetworkID = %s\n",NetworkID);
|
760 |
#endif
|
761 |
|
762 |
ChunkerPlayerCore_Play(); |
763 |
ChunkerPlayerGUI_ChannelSwitched(); |
764 |
return 0; |
765 |
} |
766 |
|
767 |
void ZapDown()
|
768 |
{ |
769 |
SelectedChannel = ((SelectedChannel+1) %NChannels);
|
770 |
SwitchChannel(&(Channels[SelectedChannel])); |
771 |
} |
772 |
|
773 |
void ZapUp()
|
774 |
{ |
775 |
SelectedChannel--; |
776 |
if(SelectedChannel < 0) |
777 |
SelectedChannel = NChannels-1;
|
778 |
|
779 |
SwitchChannel(&(Channels[SelectedChannel])); |
780 |
} |
781 |
|
782 |
int enqueueBlock(const uint8_t *block, const int block_size) |
783 |
{ |
784 |
return ChunkerPlayerCore_EnqueueBlocks(block, block_size);
|
785 |
} |