Revision e11386c0 chunker_player/player_core.c

View differences:

chunker_player/player_core.c
2 2
#include "chunker_player.h"
3 3
#include "player_gui.h"
4 4
#include "player_core.h"
5
#include "player_stats.h"
5 6
#include <assert.h>
7
#include <time.h>
6 8

  
7 9
void SaveFrame(AVFrame *pFrame, int width, int height);
8 10
int VideoCallback(void *valthread);
11
int CollectStatisticsThread(void *params);
9 12
void AudioCallback(void *userdata, Uint8 *stream, int len);
10
void UpdateQueueStats(PacketQueue *q, int packet_index);
11
void UpdateLossTraces(int type, int first_lost, int n_lost);
12
int UpdateQualityEvaluation(double instant_lost_frames, double instant_skips, int do_update);
13
void PacketQueueCleanStats(PacketQueue *q);
13
void PacketQueueClearStats(PacketQueue *q);
14
void ChunkerPlayerCore_Pause();
15

  
16
//int lastCheckedVideoFrame = -1;
17
long int last_video_frame_extracted = -1;
14 18

  
15 19
void PacketQueueInit(PacketQueue *q, short int Type)
16 20
{
......
22 26
	QueueFillingMode=1;
23 27
	q->queueType=Type;
24 28
	q->last_frame_extracted = -1;
25
	q->total_lost_frames = 0;
26
	q->instant_lost_frames = 0;
27
	q->total_skips = 0;
28
	q->last_skips = 0;
29
	q->instant_skips = 0;
30 29
	q->first_pkt= NULL;
31 30
	//q->last_pkt = NULL;
32 31
	q->nb_packets = 0;
......
34 33
	q->density= 0.0;
35 34
	FirstTime = 1;
36 35
	FirstTimeAudio = 1;
37
	//init up statistcs
38
	PacketQueueCleanStats(q);
36
	//init up statistics
37
	
38
	q->PacketHistory.Mutex = SDL_CreateMutex();
39
	PacketQueueClearStats(q);
40
	
39 41
#ifdef DEBUG_QUEUE
40 42
	printf("QUEUE: INIT END: NPackets=%d Type=%s\n", q->nb_packets, (q->queueType==AUDIO) ? "AUDIO" : "VIDEO");
41 43
#endif
......
58 60
#ifdef DEBUG_QUEUE
59 61
		printf("F ");
60 62
#endif
61
		q->total_lost_frames++;
63
		q->PacketHistory.LostCount++;
62 64
	}
63 65
#ifdef DEBUG_QUEUE
64 66
	printf("\n");
......
66 68

  
67 69
	QueueFillingMode=1;
68 70
	q->last_frame_extracted = -1;
71
	
69 72
	// on queue reset do not reset loss count
70 73
	// (loss count reset is done on queue init, ie channel switch)
71
	// q->total_lost_frames = 0;
72 74
	q->density=0.0;
73 75
	q->first_pkt= NULL;
74 76
	//q->last_pkt = NULL;
......
76 78
	q->size = 0;
77 79
	FirstTime = 1;
78 80
	FirstTimeAudio = 1;
79
	//clean up statistcs
80
	PacketQueueCleanStats(q);
81
	//clean up statistics
82
	PacketQueueClearStats(q);
81 83
#ifdef DEBUG_QUEUE
82 84
	printf("QUEUE: RESET END: NPackets=%d Type=%s LastExtr=%d\n", q->nb_packets, (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->last_frame_extracted);
83 85
#endif
84 86
	SDL_UnlockMutex(q->mutex);
85 87
}
86 88

  
87
void PacketQueueCleanStats(PacketQueue *q) {
88
	int i=0;
89
	for(i=0; i<LOSS_HISTORY_MAX_SIZE; i++) {
90
		q->skip_history[i] = -1;
91
		q->loss_history[i] = -1;
92
	}
93
	q->loss_history_index = 0;
94
	q->skip_history_index = 0;
95
	q->instant_skips = 0.0;
96
	q->instant_lost_frames = 0.0;
97
	q->instant_window_size = 50; //averaging window size, self-correcting based on window_seconds
98
	q->instant_window_size_target = 0;
99
	q->instant_window_seconds = 1; //we want to compute number of events in a 1sec wide window
100
	q->last_window_size_update = 0;
101
	q->last_stats_display = 0;
89
void PacketQueueClearStats(PacketQueue *q)
90
{
102 91
	sprintf(q->stats_message, "%s", "\n");
92
	int i;
93
	memset((void*)q->PacketHistory.History, 0, sizeof(SHistoryElement)*QUEUE_HISTORY_SIZE);
94
	for(i=0; i<QUEUE_HISTORY_SIZE; i++)
95
	{
96
		q->PacketHistory.History[i].Statistics.LastIFrameDistance = -1;
97
		q->PacketHistory.History[i].Status = -1;
98
	}
99
	q->PacketHistory.Index = q->PacketHistory.LogIndex = 0;
100
	q->PacketHistory.Index = q->PacketHistory.QoEIndex = 0;
101
	q->PacketHistory.LostCount = q->PacketHistory.PlayedCount = q->PacketHistory.SkipCount = 0;
103 102
}
104 103

  
105 104
int ChunkerPlayerCore_PacketQueuePut(PacketQueue *q, AVPacket *pkt)
106 105
{
106
	//~ printf("\tSTREAM_INDEX=%d\n", pkt->stream_index);
107 107
	short int skip = 0;
108 108
	AVPacketList *pkt1, *tmp, *prevtmp;
109
	int res = 0;
109 110

  
110 111
	if(q->nb_packets > queue_filling_threshold*QUEUE_MAX_GROW_FACTOR) {
111 112
#ifdef DEBUG_QUEUE
112 113
		printf("QUEUE: PUT i have TOO MANY packets %d Type=%s, RESETTING\n", q->nb_packets, (q->queueType==AUDIO) ? "AUDIO" : "VIDEO");
113 114
#endif
114 115
		PacketQueueReset(q);
115
  }
116
	}
116 117

  
117 118
	//make a copy of the incoming packet
118 119
	if(av_dup_packet(pkt) < 0) {
......
129 130
	}
130 131
	pkt1->pkt = *pkt;
131 132
	pkt1->next = NULL;
133
	
134
	static time_t last_auto_switch = 0;
135

  
136
	// file streaming loop detected => re-tune channel and start grabbing statistics
137
	if(
138
		(pkt->stream_index < last_video_frame_extracted)
139
		&& (pkt->stream_index <= RESTART_FRAME_NUMBER_THRESHOLD)
140
		&& ((time(NULL) - last_auto_switch) > 10)
141
	)
142
	{
143
		last_auto_switch = time(NULL);
144
		SDL_LockMutex(q->mutex);
145
		ReTune(&(Channels[SelectedChannel]));
146
		SDL_UnlockMutex(q->mutex);
147
	}
132 148

  
133
	SDL_LockMutex(q->mutex);
149
	else
150
	{
151
		SDL_LockMutex(q->mutex);
134 152

  
135
	// INSERTION SORT ALGORITHM
136
	// before inserting pkt, check if pkt.stream_index is <= current_extracted_frame.
137
	if(pkt->stream_index > q->last_frame_extracted) {
138
		// either checking starting from the first_pkt or needed other struct like AVPacketList with next and prev....
139
		//if (!q->last_pkt)
140
		if(!q->first_pkt) {
141
			q->first_pkt = pkt1;
142
			q->last_pkt = pkt1;
143
		}
144
		else if(pkt->stream_index < q->first_pkt->pkt.stream_index) {
145
			//the packet that has arrived is earlier than the first we got some time ago!
146
			//we need to put it at the head of the queue
147
			pkt1->next = q->first_pkt;
148
			q->first_pkt = pkt1;
149
		}
150
		else {
151
			tmp = q->first_pkt;
152
			while(tmp->pkt.stream_index < pkt->stream_index) {
153
				prevtmp = tmp;
154
				tmp = tmp->next;
153
		// INSERTION SORT ALGORITHM
154
		// before inserting pkt, check if pkt.stream_index is <= current_extracted_frame.
155
		if(pkt->stream_index > q->last_frame_extracted)
156
		{
157
			// either checking starting from the first_pkt or needed other struct like AVPacketList with next and prev....
158
			//if (!q->last_pkt)
159
			if(!q->first_pkt) {
160
				q->first_pkt = pkt1;
161
				q->last_pkt = pkt1;
162
			}
163
			else if(pkt->stream_index < q->first_pkt->pkt.stream_index) {
164
				//the packet that has arrived is earlier than the first we got some time ago!
165
				//we need to put it at the head of the queue
166
				pkt1->next = q->first_pkt;
167
				q->first_pkt = pkt1;
168
			}
169
			else {
170
				tmp = q->first_pkt;
171
				while(tmp->pkt.stream_index < pkt->stream_index) {
172
					prevtmp = tmp;
173
					tmp = tmp->next;
155 174

  
156
				if(!tmp) {
157
					break;
175
					if(!tmp) {
176
						break;
177
					}
158 178
				}
159
			}
160
			if(tmp && tmp->pkt.stream_index == pkt->stream_index) {
161
				//we already have a frame with that index
162
				skip = 1;
179
				if(tmp && tmp->pkt.stream_index == pkt->stream_index) {
180
					//we already have a frame with that index
181
					skip = 1;
163 182
#ifdef DEBUG_QUEUE
164
				printf("QUEUE: PUT: we already have frame with index %d, skipping\n", pkt->stream_index);
183
					printf("%s QUEUE: PUT: we already have frame with index %d, skipping\n", ((q->queueType == AUDIO) ? "AUDIO" : "VIDEO"), pkt->stream_index);
165 184
#endif
185
				}
186
				else {
187
					prevtmp->next = pkt1;
188
					pkt1->next = tmp;
189
					if(pkt1->next == NULL)
190
						q->last_pkt = pkt1;
191
				}
192
				//q->last_pkt->next = pkt1; // It was uncommented when not insertion sort
166 193
			}
167
			else {
168
				prevtmp->next = pkt1;
169
				pkt1->next = tmp;
170
				if(pkt1->next == NULL)
171
					q->last_pkt = pkt1;
172
			}
173
			//q->last_pkt->next = pkt1; // It was uncommented when not insertion sort
174
		}
175
		if(skip == 0) {
176
			//q->last_pkt = pkt1;
177
			q->nb_packets++;
178
			q->size += pkt1->pkt.size;
179
			if(q->nb_packets>=queue_filling_threshold && QueueFillingMode) // && q->queueType==AUDIO)
180
			{
181
				QueueFillingMode=0;
194
			if(skip == 0) {
195
				//q->last_pkt = pkt1;
196
				q->nb_packets++;
197
				q->size += pkt1->pkt.size;
198
				if(q->nb_packets>=queue_filling_threshold && QueueFillingMode) // && q->queueType==AUDIO)
199
				{
200
					QueueFillingMode=0;
182 201
#ifdef DEBUG_QUEUE
183
				printf("QUEUE: PUT: FillingMode set to zero\n");
202
					printf("QUEUE: PUT: FillingMode set to zero\n");
184 203
#endif
204
				}
185 205
			}
186 206
		}
187
	}
188

  
189
	else {
190
		av_free_packet(&pkt1->pkt);
191
		av_free(pkt1);
207
		else {
208
			av_free_packet(&pkt1->pkt);
209
			av_free(pkt1);
192 210
#ifdef DEBUG_QUEUE
193
				printf("QUEUE: PUT: NOT inserting because index %d > last extracted %d\n", pkt->stream_index, q->last_frame_extracted);
211
			printf("QUEUE: PUT: NOT inserting because index %d <= last extracted %d\n", pkt->stream_index, q->last_frame_extracted);
194 212
#endif
213
			res = 1;
214
		}
215
		SDL_UnlockMutex(q->mutex);
195 216
	}
196 217

  
197
	// minus one means no lost frames estimation, useless during QueuePut operations
198
	UpdateQueueStats(q, -1);
199

  
200
	SDL_UnlockMutex(q->mutex);
201
	return 0;
218
	return res;
202 219
}
203 220

  
204 221
int ChunkerPlayerCore_InitCodecs(int width, int height, int sample_rate, short int audio_channels)
......
246 263
	printf("using audio Codecid: %d ",aCodecCtx->codec_id);
247 264
	printf("samplerate: %d ",aCodecCtx->sample_rate);
248 265
	printf("channels: %d\n",aCodecCtx->channels);
249
	wanted_spec.freq = aCodecCtx->sample_rate;
266
	CurrentAudioFreq = wanted_spec.freq = aCodecCtx->sample_rate;
250 267
	wanted_spec.format = AUDIO_S16SYS;
251 268
	wanted_spec.channels = aCodecCtx->channels;
252 269
	wanted_spec.silence = 0;
253
	wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
270
	CurrentAudioSamples = wanted_spec.samples = SDL_AUDIO_BUFFER_SIZE;
254 271
	wanted_spec.callback = AudioCallback;
255 272
	wanted_spec.userdata = aCodecCtx;
256 273
	if(SDL_OpenAudio(&wanted_spec,&AudioSpecification)<0)
257 274
	{
258
		fprintf(stderr,"SDL_OpenAudio: %s\n",SDL_GetError());
275
		fprintf(stderr,"SDL_OpenAudio: %s\n", SDL_GetError());
259 276
		return -1;
260 277
	}
261 278
	dimAudioQ = AudioSpecification.size;
......
305 322
	return 0;
306 323
}
307 324

  
308
int DecodeEnqueuedAudio(AVPacket *pkt, PacketQueue *q)
325
int DecodeEnqueuedAudio(AVPacket *pkt, PacketQueue *q, int* size)
309 326
{
310 327
	uint16_t *audio_bufQ = NULL;
311 328
	int16_t *dataQ = NULL;
......
334 351
				}
335 352
				//subtract them from queue size
336 353
				q->size -= pkt->size;
354
				*size = pkt->size;
337 355
				pkt->data = (int8_t *)dataQ;
338 356
				pkt->size = data_sizeQ;
339 357
				//add new size to queue size
......
371 389
	//adjust size here and not in the various cases of the dequeue
372 390
	q->size -= p->pkt.size;
373 391
	if(&p->pkt)
392
	{
374 393
		av_free_packet(&p->pkt);
394
	}
375 395
	if(p)
376 396
		av_free(p);
377 397
	return retpk;
378 398
}
379 399

  
380
AVPacketList *SeekAndDecodePacketStartingFrom(AVPacketList *p, PacketQueue *q)
400
AVPacketList *SeekAndDecodePacketStartingFrom(AVPacketList *p, PacketQueue *q, int* size)
381 401
{
382 402
	while(p) {
383 403
			//check if audio packet has been already decoded
384 404
			if(p->pkt.convergence_duration == 0) {
385 405
				//not decoded yet, try to decode it
386
				if( !DecodeEnqueuedAudio(&(p->pkt), q) ) {
406
				if( !DecodeEnqueuedAudio(&(p->pkt), q, size) ) {
387 407
					//it was not possible to decode this packet, return next one
388 408
					p = RemoveFromQueue(q, p);
389 409
				}
......
396 416
	return NULL;
397 417
}
398 418

  
399
void UpdateQueueStats(PacketQueue *q, int packet_index)
400
{
401
	//used as flag also (0 means dont update quality estimation average)
402
	int update_quality_avg = 0;
403
	int i;
404

  
405
	if(q == NULL)
406
		return;
407
	if(q->first_pkt == NULL)
408
		return;
409
	if(q->last_pkt == NULL)
410
		return;
411

  
412
	int now = time(NULL);
413
	if(!q->last_stats_display)
414
		q->last_stats_display = now;
415
	if(!q->last_window_size_update)
416
		q->last_window_size_update = now;
417

  
418
	//calculate the queue density both in case of QueuePut and QueueGet
419
	if(q->last_pkt->pkt.stream_index >= q->first_pkt->pkt.stream_index)
420
	{
421
		q->density = (double)q->nb_packets / (double)(q->last_pkt->pkt.stream_index - q->first_pkt->pkt.stream_index + 1) * 100.0; //plus 1 because if they are adjacent (difference 1) there really should be 2 packets in the queue
422
	}
423
#ifdef DEBUG_STATS
424
	if(q->queueType == AUDIO)
425
		printf("STATS: AUDIO QUEUE DENSITY percentage %f, last %d, first %d, pkts %d\n", q->density, q->last_pkt->pkt.stream_index, q->first_pkt->pkt.stream_index, q->nb_packets);
426
	if(q->queueType == VIDEO)
427
		printf("STATS: VIDEO QUEUE DENSITY percentage %f, last %d, first %d, pkts %d\n", q->density, q->last_pkt->pkt.stream_index, q->first_pkt->pkt.stream_index, q->nb_packets);
428
#endif
429

  
430
	//adjust the sliding window_size according to the elapsed time
431
	update_quality_avg = 0;
432
	//dynamically self-adjust the instant_window_size based on the time passing
433
	//to match it at our best, since we have no separate thread to count time
434
	if((now - q->last_window_size_update) >= q->instant_window_seconds) {
435
		int i = 0;
436
		//if window is enlarged, erase old samples
437
		for(i=q->instant_window_size; i<q->instant_window_size_target; i++) {
438
			q->skip_history[i] = -1;
439
			q->loss_history[i] = -1;
440
		}
441
#ifdef DEBUG_STATS
442
		printf("STATS: %s WINDOW_SIZE instant set from %d to %d\n", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->instant_window_size, q->instant_window_size_target);
443
#endif
444
		q->instant_window_size = q->instant_window_size_target;
445
		if(q->instant_window_size == 0) { //in case of anomaly reset to default
446
			q->instant_window_size = 50;
447
			printf("STATS: %s WINDOW_SIZE instant anomaly reset to default %d\n", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->instant_window_size);
448
		}
449
		if(q->instant_window_size > LOSS_HISTORY_MAX_SIZE) {
450
			printf("ERROR: %s instant_window size updated to %d, which is more than the max of %d. Exiting.\n", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->instant_window_size, LOSS_HISTORY_MAX_SIZE);
451
			exit(1);
452
		}
453
		q->instant_window_size_target = 0;
454
#ifdef DEBUG_STATS
455
		printf("STATS: %s WINDOW_SIZE instant moving time from %d to %d\n", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->last_window_size_update, now);
456
#endif
457
		//update time passing
458
		q->last_window_size_update = now;
459
		//signal to give the update values to the UpdateQuality evaluation
460
		//since we dont update long-term averaging every time, just do it at every window_seconds
461
		update_quality_avg = 1;
462
	}
463

  
464
	//calculate lost frames and skipped frames during playing only if we are called from a QueueGet
465
	//averaging them in a short-sized time window
466
	if(packet_index != -1)
467
	{
468
		int real_window_size = 0;
469
		int lost_frames = 0;
470
		double percentage = 0.0;	
471

  
472
		//self adjust window size
473
		q->instant_window_size_target++;
474

  
475
		//compute lost frame statistics
476
		if(q->last_frame_extracted > 0 && packet_index > q->last_frame_extracted)
477
		{
478
			lost_frames = packet_index - q->last_frame_extracted - 1;
479
			q->total_lost_frames += lost_frames;
480
			percentage = (double)q->total_lost_frames / (double)q->last_frame_extracted * 100.0;
481

  
482
			//save a trace of lost frames to file
483
			//we have lost "lost_frames" frames starting from the last extracted (excluded of course)
484
			UpdateLossTraces(q->queueType, q->last_frame_extracted+1, lost_frames);
485

  
486
			//compute the frame loss rate inside the short-sized sliding window
487
			q->loss_history[q->loss_history_index] = lost_frames;
488
			q->loss_history_index = (q->loss_history_index+1)%q->instant_window_size;
489
			q->instant_lost_frames = 0.0;
490
			real_window_size = 0;
491
#ifdef DEBUG_STATS_DEEP
492
			printf("STATS: QUALITY: %s UPDATE LOSS:", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO");
493
#endif
494
			for(i=0; i<q->instant_window_size; i++) {
495
				//-1 means not initialized value or erased value due to window shrinking
496
				if(q->loss_history[i] != -1) {
497
					real_window_size++;
498
					q->instant_lost_frames += (double)q->loss_history[i];
499
#ifdef DEBUG_STATS_DEEP
500
					printf(" %d", q->loss_history[i]);
501
#endif
502
				}
503
#ifdef DEBUG_STATS_DEEP
504
				else
505
					printf(" *");
506
#endif
507
			}
508
#ifdef DEBUG_STATS_DEEP
509
			printf("\n");
510
#endif
511
			q->instant_lost_frames /= (double)q->instant_window_seconds; //express them in events/sec
512
#ifdef DEBUG_STATS
513
			if(q->queueType == AUDIO)
514
				printf("STATS: AUDIO FRAMES LOST: instant %f, total %d, total percentage %f\n", q->instant_lost_frames, q->total_lost_frames, percentage);
515
			else if(q->queueType == VIDEO)
516
				printf("STATS: VIDEO FRAMES LOST: instant %f, total %d, total percentage %f\n", q->instant_lost_frames, q->total_lost_frames, percentage);
517

  
518
			printf("STATS: QUALITY: %s UPDATE LOSS instant window %d, real window %d\n", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->instant_window_size, real_window_size);
519
#endif
520
		}
521

  
522
		//compute the skip events inside the short-sized sliding window
523
		int skips = q->total_skips - q->last_skips;
524
		q->last_skips = q->total_skips;
525
		q->skip_history[q->skip_history_index] = skips;
526
		q->skip_history_index = (q->skip_history_index+1)%q->instant_window_size;
527
		q->instant_skips = 0.0;
528
		real_window_size = 0;
529
#ifdef DEBUG_STATS_DEEP
530
		printf("STATS: QUALITY: %s UPDATE SKIP:", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO");
531
#endif
532
		for(i=0; i<q->instant_window_size; i++) {
533
			//-1 means not initialized value or erased value due to window shrinking
534
			if(q->skip_history[i] != -1) {
535
				real_window_size++;
536
				q->instant_skips += (double)q->skip_history[i];
537
#ifdef DEBUG_STATS_DEEP
538
				printf(" %d", q->skip_history[i]);
539
#endif
540
			}
541
#ifdef DEBUG_STATS_DEEP
542
			else
543
				printf(" *");
544
#endif
545
		}
546
#ifdef DEBUG_STATS_DEEP
547
		printf("\n");
548
#endif
549
		q->instant_skips /= (double)q->instant_window_seconds; //express them in events/sec
550
#ifdef DEBUG_STATS
551
		if(q->queueType == AUDIO)
552
			printf("STATS: AUDIO SKIPS: instant %f, total %d\n", q->instant_skips, q->total_skips);
553
		else if(q->queueType == VIDEO)
554
			printf("STATS: VIDEO SKIPS: instant %f, total %d\n", q->instant_skips, q->total_skips);
555

  
556
		printf("STATS: QUALITY: %s UPDATE SKIP instant window %d, real window %d\n", (q->queueType==AUDIO) ? "AUDIO" : "VIDEO", q->instant_window_size, real_window_size);
557
#endif
558
	}
559

  
560
	//continuous estimate of the channel quality
561
	//but print it only if it has changed since last time
562
	if(UpdateQualityEvaluation(q->instant_lost_frames, q->instant_skips, update_quality_avg)) {
563
		//display channel quality
564
		char stats[255];
565
		sprintf(stats, "%s - %s", Channels[SelectedChannel].Title, Channels[SelectedChannel].quality);
566
		ChunkerPlayerGUI_SetChannelTitle(stats);
567
	}
568

  
569
	//if statistics estimate printout has changed since last time, display it
570
	{
571
		char stats[255];
572
		if(q->queueType == AUDIO)
573
		{
574
			sprintf(stats, "[AUDIO] %d\%% qdensity - %d lost_frames/sec - %d lost_frames - %d skips/sec - %d skips", (int)q->density, (int)q->instant_lost_frames, q->total_lost_frames, (int)q->instant_skips, q->total_skips);
575
		}
576
		else if(q->queueType == VIDEO)
577
		{
578
			sprintf(stats, "[VIDEO] %d\%% qdensity - %d lost_frames/sec - %d lost_frames - %d skips/sec - %d skips", (int)q->density, (int)q->instant_lost_frames, q->total_lost_frames, (int)q->instant_skips, q->total_skips);
579
		}
580
		if((strcmp(q->stats_message, stats) ) && ((now-q->last_stats_display) >= 1))
581
		{
582
			//statistics estimate have changed
583
			sprintf(q->stats_message, "%s", stats);
584
			if(q->queueType == AUDIO)
585
				ChunkerPlayerGUI_SetStatsText(stats, NULL);
586
			else if(q->queueType == VIDEO)
587
				ChunkerPlayerGUI_SetStatsText(NULL, stats);
588

  
589
			q->last_stats_display = now;
590
		}
591
	}
592
}
593

  
594
//returns 1 if quality value has changed since last time
595
int UpdateQualityEvaluation(double instant_lost_frames, double instant_skips, int do_update)
596
{
597
	//as of now, we enter new samples in the long-term averaging once every sec,
598
	//thus 120 samples worth of window size equals 2 minutes averaging
599
	static int avg_window_size = 10; //averaging window size, self-correcting based on window_seconds
600
	static int avg_window_size_target = 0;
601
	static int avg_window_seconds = 10; //we want to compute number of events in a 2min wide window
602

  
603
	static int last_window_size_update = 0;
604

  
605
	char base_quality[255];
606
	char quality[255];
607
	int i;
608
	int now = time(NULL);
609
	if(!last_window_size_update)
610
		last_window_size_update = now;
611
	int runningTime = now - Channels[SelectedChannel].startTime;
612

  
613
	if(runningTime <= 0) {
614
		runningTime = 1;
615
#ifdef DEBUG_STATS
616
		printf("STATS: QUALITY warning channel runningTime %d. Set to one!\n", runningTime);
617
#endif
618
	}
619

  
620
	//update continuously the quality score
621
	Channels[SelectedChannel].instant_score = instant_skips + instant_lost_frames;
622

  
623
	if(do_update) {
624
#ifdef DEBUG_STATS
625
		printf("STATS: QUALITY: UPDATE SCORE lost frames %f, skips %f\n", instant_lost_frames, instant_skips);
626
#endif
627
		//every once in a while also enter samples in the long-term averaging window
628
		Channels[SelectedChannel].score_history[Channels[SelectedChannel].history_index] = Channels[SelectedChannel].instant_score;
629
		Channels[SelectedChannel].history_index = (Channels[SelectedChannel].history_index+1)%avg_window_size;
630

  
631
		Channels[SelectedChannel].average_score = 0.0;
632
		int real_window_size = 0;
633
		for(i=0; i<avg_window_size; i++) {
634
			//-1 means not initialized value or erased value due to window shrinking
635
			if(Channels[SelectedChannel].score_history[i] != -1) {
636
				real_window_size++;
637
				Channels[SelectedChannel].average_score += Channels[SelectedChannel].score_history[i];
638
			}
639
		}
640
		Channels[SelectedChannel].average_score /= (double)real_window_size; //average in the window
641
		Channels[SelectedChannel].average_score /= (double)avg_window_seconds; //express it in events/sec
642
#ifdef DEBUG_STATS
643
		printf("STATS: QUALITY: UPDATE SCORE avg window %d, real window %d\n", avg_window_size, real_window_size);
644
#endif
645
		//whenever we enter a sample, enlarge the self-adjusting window target (it will be checked later on)
646
		avg_window_size_target++;
647
	}
648
#ifdef DEBUG_STATS
649
	printf("STATS: QUALITY: instant skips %f, instant loss %f\n", instant_skips, instant_lost_frames);
650
	printf("STATS: QUALITY: instant score %f, avg score %f\n", Channels[SelectedChannel].instant_score, Channels[SelectedChannel].average_score);
651
#endif
652

  
653
	if(Channels[SelectedChannel].average_score > 0.02) {
654
		sprintf(base_quality, "POOR");
655
		if(Channels[SelectedChannel].instant_score < Channels[SelectedChannel].average_score) {
656
			sprintf(quality, "%s, GETTING BETTER", base_quality);
657
		}
658
		else if(Channels[SelectedChannel].instant_score == Channels[SelectedChannel].average_score) {
659
			sprintf(quality, "%s, STABLE", base_quality);
660
		}
661
		else {
662
			sprintf(quality, "%s, GETTING WORSE", base_quality);
663
		}
664
	}
665
	else {
666
		sprintf(base_quality, "GOOD");
667
		if(Channels[SelectedChannel].instant_score < Channels[SelectedChannel].average_score) {
668
			sprintf(quality, "%s, GETTING BETTER", base_quality);
669
		}
670
		else if(Channels[SelectedChannel].instant_score == Channels[SelectedChannel].average_score) {
671
			sprintf(quality, "%s, STABLE", base_quality);
672
		}
673
		else {
674
			sprintf(quality, "%s, GETTING WORSE", base_quality);
675
		}
676
	}
677

  
678
#ifdef DEBUG_STATS
679
	printf("STATS: QUALITY %s\n", quality);
680
#endif
681

  
682
	//dynamically self-adjust the instant_window_size based on the time passing
683
	//to match it at our best, since we have no separate thread to count time
684
	if((now - last_window_size_update) >= avg_window_seconds) {
685
		int i = 0;
686
		//if window is enlarged, erase old samples
687
		for(i=avg_window_size; i<avg_window_size_target; i++) {
688
			Channels[SelectedChannel].score_history[i] = -1;
689
		}
690
		avg_window_size = avg_window_size_target;
691
#ifdef DEBUG_STATS
692
		printf("STATS: WINDOW_SIZE avg set to %d\n", avg_window_size);
693
#endif
694
		if(avg_window_size > CHANNEL_SCORE_HISTORY_SIZE) {
695
			printf("ERROR: avg_window size updated to %d, which is more than the max of %d. Exiting.\n", avg_window_size, CHANNEL_SCORE_HISTORY_SIZE);
696
			exit(1);
697
		}
698
		avg_window_size_target = 0;
699
		//update time passing
700
		last_window_size_update = now;
701
	}
702

  
703
	if( strcmp(Channels[SelectedChannel].quality, quality) ) {
704
		//quality estimate has changed
705
		sprintf(Channels[SelectedChannel].quality, "%s", quality);
706
		return 1;
707
	}
708
	else {
709
		return 0;
710
	}
711
}
712

  
713
void UpdateLossTraces(int type, int first_lost, int n_lost)
419
int PacketQueueGet(PacketQueue *q, AVPacket *pkt, short int av, int* size)
714 420
{
715
	FILE *lossFile;
716
	int i;
717

  
718
	// Open loss traces file
719
	char filename[255];
720
	if(type == AUDIO)
721
		sprintf(filename, "audio_%s", LossTracesFilename);
722
	else
723
		sprintf(filename, "video_%s", LossTracesFilename);
724

  
725
	lossFile=fopen(filename, "a");
726
	if(lossFile==NULL) {
727
		printf("STATS: UNABLE TO OPEN Loss FILE: %s\n", filename);
728
		return;
729
	}
730

  
731
	for(i=0; i<n_lost; i++) {
732
		fprintf(lossFile, "%d\n", first_lost+i);
733
	}
734

  
735
	fclose(lossFile);
736
}
737

  
738
int PacketQueueGet(PacketQueue *q, AVPacket *pkt, short int av) {
739 421
	//AVPacket tmp;
740 422
	AVPacketList *pkt1 = NULL;
741 423
	int ret=-1;
742 424
	int SizeToCopy=0;
425
	struct timeval now_tv;
743 426

  
744 427
	SDL_LockMutex(q->mutex);
745 428

  
......
755 438

  
756 439
	if(av==1) { //somebody requested an audio packet, q is the audio queue
757 440
		//try to dequeue the first packet of the audio queue
758
		pkt1 = SeekAndDecodePacketStartingFrom(q->first_pkt, q);
441
		pkt1 = SeekAndDecodePacketStartingFrom(q->first_pkt, q, size);
759 442
		if(pkt1) { //yes we have them!
760 443
			if(pkt1->pkt.size-AudioQueueOffset > dimAudioQ) {
761
				//one packet if enough to give us the requested number of bytes by the audio_callback
444
				//one packet is enough to give us the requested number of bytes by the audio_callback
762 445
#ifdef DEBUG_QUEUE_DEEP
763 446
				printf("  AV=1 and Extract from the same packet\n");
764 447
#endif
......
781 464
#ifdef DEBUG_QUEUE_DEEP
782 465
				printf("   deltaAudioQError = %f\n",deltaAudioQError);
783 466
#endif
784
				//update overall state of queue
785
				//size is diminished because we played some audio samples
786
				//but packet is not removed since a portion has still to be played
787
				//HINT ERRATA we had a size mismatch since size grows with the
788
				//number of compressed bytes, and diminishes here with the number
789
				//of raw uncompressed bytes, hence we update size during the
790
				//real removes and not here anymore
791
				//q->size -= dimAudioQ;
792
				UpdateQueueStats(q, pkt->stream_index);
467

  
468
				ChunkerPlayerStats_UpdateAudioLossHistory(&(q->PacketHistory), pkt->stream_index, q->last_frame_extracted);
469
				
793 470
				//update index of last frame extracted
794 471
				q->last_frame_extracted = pkt->stream_index;
795 472
#ifdef DEBUG_AUDIO_BUFFER
......
804 481
#endif
805 482
				//check for a valid next packet since we will finish the current packet
806 483
				//and also take some bytes from the next one
807
				pkt1->next = SeekAndDecodePacketStartingFrom(pkt1->next, q);
484
				pkt1->next = SeekAndDecodePacketStartingFrom(pkt1->next, q, size);
808 485
				if(pkt1->next) {
809 486
#ifdef DEBUG_QUEUE_DEEP
810 487
					printf("   we have a next...\n");
......
850 527
					AudioQueueOffset = dimAudioQ - SizeToCopy;
851 528
					//SEE BEFORE HINT q->size -= AudioQueueOffset;
852 529
					ret = 1;
853
					UpdateQueueStats(q, pkt->stream_index);
530
					
531
					ChunkerPlayerStats_UpdateAudioLossHistory(&(q->PacketHistory), pkt->stream_index, q->last_frame_extracted);
854 532
				}
855 533
				else {
856 534
					AudioQueueOffset=0;
......
880 558
			
881 559
			if((pkt->data != NULL) && (pkt1->pkt.data != NULL))
882 560
				memcpy(pkt->data, pkt1->pkt.data, pkt1->pkt.size);
883

  
561
				
884 562
			//HINT SEE BEFORE q->size -= pkt1->pkt.size;
885 563
			q->first_pkt = RemoveFromQueue(q, pkt1);
886 564

  
887 565
			ret = 1;
888
			UpdateQueueStats(q, pkt->stream_index);
566
			
567
			ChunkerPlayerStats_UpdateVideoLossHistory(&(q->PacketHistory), pkt->stream_index, q->last_frame_extracted);
568
			
889 569
			//update index of last frame extracted
890 570
			q->last_frame_extracted = pkt->stream_index;
571
			last_video_frame_extracted = q->last_frame_extracted;
891 572
		}
892 573
#ifdef DEBUG_QUEUE
893 574
		else {
......
913 594
int AudioDecodeFrame(uint8_t *audio_buf, int buf_size) {
914 595
	//struct timeval now;
915 596
	int audio_pkt_size = 0;
597
	int compressed_size = 0;
916 598
	long long Now;
917 599
	short int DecodeAudio=0, SkipAudio=0;
918 600
	//int len1, data_size;
......
920 602
	//gettimeofday(&now,NULL);
921 603
	//Now = (now.tv_sec)*1000+now.tv_usec/1000;
922 604
	Now=(long long)SDL_GetTicks();
605
	struct timeval now_tv;
923 606

  
924 607
	if(QueueFillingMode || QueueStopped)
925 608
	{
......
955 638
		printf("AUDIO: audio_decode_frame - Empty queue\n");
956 639
#endif
957 640

  
958

  
959
	if(audioq.nb_packets>0) {
960
		if((long long)audioq.first_pkt->pkt.pts+DeltaTime<Now-(long long)MAX_TOLLERANCE) {
641
	gettimeofday(&now_tv, NULL);
642
	if(audioq.nb_packets>0)
643
	{
644
		if((long long)audioq.first_pkt->pkt.pts+DeltaTime<Now-(long long)MAX_TOLLERANCE)
645
		{
961 646
			SkipAudio = 1;
962 647
			DecodeAudio = 0;
963 648
		}
......
967 652
				DecodeAudio = 1;
968 653
		}
969 654
	}
970
		
971
	while(SkipAudio==1 && audioq.size>0) {
972
		audioq.total_skips++;
655
	
656
	while(SkipAudio==1 && audioq.size>0)
657
	{
973 658
		SkipAudio = 0;
974 659
#ifdef DEBUG_AUDIO
975 660
 		printf("AUDIO: skipaudio: queue size=%d\n",audioq.size);
976 661
#endif
977
		if(PacketQueueGet(&audioq,&AudioPkt,1) < 0) {
662
		if(PacketQueueGet(&audioq,&AudioPkt,1, &compressed_size) < 0) {
978 663
			return -1;
979 664
		}
980 665
		if(audioq.first_pkt)
981 666
		{
982
			if((long long)audioq.first_pkt->pkt.pts+DeltaTime<Now-(long long)MAX_TOLLERANCE) {
667
			ChunkerPlayerStats_UpdateAudioSkipHistory(&(audioq.PacketHistory), AudioPkt.stream_index, compressed_size);
668
			
669
			if((long long)audioq.first_pkt->pkt.pts+DeltaTime<Now-(long long)MAX_TOLLERANCE)
670
			{
983 671
				SkipAudio = 1;
984 672
				DecodeAudio = 0;
985 673
			}
......
991 679
		}
992 680
	}
993 681
	if(DecodeAudio==1) {
994
		if(PacketQueueGet(&audioq,&AudioPkt,1) < 0) {
682
		if(PacketQueueGet(&audioq,&AudioPkt,1, &compressed_size) < 0) {
995 683
			return -1;
996 684
		}
997 685
		memcpy(audio_buf,AudioPkt.data,AudioPkt.size);
......
999 687
#ifdef DEBUG_AUDIO
1000 688
 		printf("AUDIO: Decode audio\n");
1001 689
#endif
690

  
691
		ChunkerPlayerStats_UpdateAudioPlayedHistory(&(audioq.PacketHistory), AudioPkt.stream_index, compressed_size);
1002 692
	}
1003 693

  
1004 694
	return audio_pkt_size;
......
1014 704
	AVPicture pict;
1015 705
	long long Now;
1016 706
	short int SkipVideo, DecodeVideo;
707
	
708
#ifdef SAVE_YUV
709
	static AVFrame* lastSavedFrameBuffer = NULL;
710
	
711
	if(!lastSavedFrameBuffer)
712
		lastSavedFrameBuffer = (AVFrame*) malloc(sizeof(AVFrame));
713
#endif
1017 714

  
1018 715
	//double frame_rate = 0.0,time_between_frames=0.0;
1019 716
	//struct timeval now;
......
1030 727

  
1031 728
	pCodecCtx=avcodec_alloc_context();
1032 729
	pCodecCtx->codec_type = CODEC_TYPE_VIDEO;
730
	//pCodecCtx->debug = FF_DEBUG_DCT_COEFF;
1033 731
#ifdef H264_VIDEO_ENCODER
1034 732
	pCodecCtx->codec_id  = CODEC_ID_H264;
1035 733
	pCodecCtx->me_range = 16;
......
1044 742
	// resolution must be a multiple of two
1045 743
	pCodecCtx->width = tval->width;//176;//352;
1046 744
	pCodecCtx->height = tval->height;//144;//288;
1047
	
745

  
1048 746
	// frames per second
1049 747
	//pCodecCtx->time_base = (AVRational){1,25};
1050 748
	//pCodecCtx->gop_size = 10; // emit one intra frame every ten frames
......
1069 767
#ifdef DEBUG_VIDEO
1070 768
 	printf("VIDEO: video_callback entering main cycle\n");
1071 769
#endif
770

  
771
	struct timeval now_tv;
1072 772
	while(AVPlaying && !quit) {
1073 773
		if(QueueFillingMode || QueueStopped)
1074 774
		{
......
1108 808
#endif
1109 809

  
1110 810
		if(videoq.nb_packets>0) {
1111
			if(((long long)videoq.first_pkt->pkt.pts+DeltaTime)<Now-(long long)MAX_TOLLERANCE) {
811
			if(((long long)videoq.first_pkt->pkt.pts+DeltaTime)<Now-(long long)MAX_TOLLERANCE)
812
			{
1112 813
				SkipVideo = 1;
1113 814
				DecodeVideo = 0;
1114 815
			}
......
1118 819
					SkipVideo = 0;
1119 820
					DecodeVideo = 1;
1120 821
				}
822
				
823
				// else (i.e. videoq.first_pkt->pkt.pts+DeltaTime>Now+MAX_TOLLERANCE)
824
				// do nothing and continue
1121 825
		}
1122 826
#ifdef DEBUG_VIDEO
1123 827
		printf("VIDEO: skipvideo:%d decodevideo:%d\n",SkipVideo,DecodeVideo);
1124 828
#endif
1125

  
1126
		while(SkipVideo==1 && videoq.size>0) {
1127
			videoq.total_skips++;
829
		gettimeofday(&now_tv, NULL);
830
		
831
		while(SkipVideo==1 && videoq.size>0)
832
		{
1128 833
			SkipVideo = 0;
1129 834
#ifdef DEBUG_VIDEO 
1130 835
 			printf("VIDEO: Skip Video\n");
1131 836
#endif
1132
			if(PacketQueueGet(&videoq,&VideoPkt,0) < 0) {
837
			if(PacketQueueGet(&videoq,&VideoPkt,0, NULL) < 0) {
1133 838
				break;
1134 839
			}
840

  
1135 841
			avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &VideoPkt);
842
			
843
			// sometimes assertion fails, maybe the decoder change the frame type
844
			//~ if(LastSourceIFrameDistance == 0)
845
				//~ assert(pFrame->pict_type == 1);
846

  
1136 847
			if(videoq.first_pkt)
1137 848
			{
1138
				if((long long)videoq.first_pkt->pkt.pts+DeltaTime<Now-(long long)MAX_TOLLERANCE) {
849
				if((long long)videoq.first_pkt->pkt.pts+DeltaTime<Now-(long long)MAX_TOLLERANCE)
850
				{
1139 851
					SkipVideo = 1;
1140 852
					DecodeVideo = 0;
1141 853
				}
......
1145 857
					DecodeVideo = 1;
1146 858
				}
1147 859
			}
860
			
861
			ChunkerPlayerStats_UpdateVideoSkipHistory(&(videoq.PacketHistory), VideoPkt.stream_index, pFrame->pict_type, VideoPkt.size, pFrame);
862
			
863
			/*if(pFrame->pict_type == 1)
864
			{
865
				int i1;
866
				// every 23 items (23 is the qstride field in the AVFrame struct) there is 1 zero.
867
				// 396/23 = 17 => 396 macroblocks + 17 zeros = 413 items
868
				for(i1=0; i1< 413; i1++)
869
					fprintf(qscaletable_file, "%d\t", (int)pFrame->qscale_table[i1]);
870
				fprintf(qscaletable_file, "\n");
871
			}*/
872
			
873
			//ChunkerPlayerStats_UpdateVideoPlayedHistory(&(videoq.PacketHistory), VideoPkt.stream_index, pFrame->pict_type, VideoPkt.size);
1148 874
		}
1149
		
1150
		if(DecodeVideo==1) {
1151
			if(PacketQueueGet(&videoq,&VideoPkt,0) > 0) {
1152

  
1153
#ifdef DEBUG_VIDEO
1154
				printf("VIDEO: Decode video FrameTime=%lld Now=%lld\n",(long long)VideoPkt.pts+DeltaTime,Now);
1155
#endif
1156 875

  
876
		if(DecodeVideo==1) {
877
			if(PacketQueueGet(&videoq,&VideoPkt,0, NULL) > 0) {
1157 878
				avcodec_decode_video2(pCodecCtx, pFrame, &frameFinished, &VideoPkt);
1158 879

  
1159
				if(frameFinished) { // it must be true all the time else error
880
				if(frameFinished)
881
				{ // it must be true all the time else error
1160 882
#ifdef DEBUG_VIDEO
1161 883
					printf("VIDEO: FrameFinished\n");
1162 884
#endif
1163
					if(SaveYUV)
885
					decoded_vframes++;
886
					
887
					// sometimes assertion fails, maybe the decoder change the frame type
888
					//~ if(LastSourceIFrameDistance == 0)
889
						//~ assert(pFrame->pict_type == 1);
890
#ifdef SAVE_YUV
891
					if(LastSavedVFrame == -1)
892
					{
893
						memcpy(lastSavedFrameBuffer, pFrame, sizeof(AVFrame));
1164 894
						SaveFrame(pFrame, pCodecCtx->width, pCodecCtx->height);
1165
					//fwrite(pktvideo.data, 1, pktvideo.size, frecon);
895
						LastSavedVFrame = VideoPkt.stream_index;
896
					}
897
					else if(LastSavedVFrame == (VideoPkt.stream_index-1))
898
					{
899
						memcpy(lastSavedFrameBuffer, pFrame, sizeof(AVFrame));
900
						SaveFrame(pFrame, pCodecCtx->width, pCodecCtx->height);
901
						LastSavedVFrame = VideoPkt.stream_index;
902
					}
903
					else if(LastSavedVFrame >= 0)
904
					{
905
						while(LastSavedVFrame < (VideoPkt.stream_index-1))
906
						{
907
							SaveFrame(lastSavedFrameBuffer, pCodecCtx->width, pCodecCtx->height);
908
						}
909

  
910
						memcpy(lastSavedFrameBuffer, pFrame, sizeof(AVFrame));
911
						SaveFrame(pFrame, pCodecCtx->width, pCodecCtx->height);
912
						LastSavedVFrame = VideoPkt.stream_index;
913
					}
914
#endif
915
					ChunkerPlayerStats_UpdateVideoPlayedHistory(&(videoq.PacketHistory), VideoPkt.stream_index, pFrame->pict_type, VideoPkt.size, pFrame);
1166 916

  
1167 917
					if(SilentMode)
1168 918
						continue;
......
1196 946
							exit(1);
1197 947
						}
1198 948
					}
949
					
950
#ifdef VIDEO_DEINTERLACE
951
					avpicture_deinterlace(
952
						(AVPicture*) pFrame,
953
						(const AVPicture*) pFrame,
954
						pCodecCtx->pix_fmt,
955
						tval->width, tval->height);
956
#endif
957
					
1199 958
					// let's draw the data (*yuv[3]) on a SDL screen (*screen)
1200 959
					sws_scale(img_convert_ctx, pFrame->data, pFrame->linesize, 0, tval->height, pict.data, pict.linesize);
1201 960
					SDL_UnlockYUVOverlay(YUVOverlay);
......
1213 972
						SDL_UnlockSurface(MainScreen);
1214 973
					}
1215 974
				} //if FrameFinished
975
				else
976
				{
977
					ChunkerPlayerStats_UpdateVideoLossHistory(&(videoq.PacketHistory), VideoPkt.stream_index+1, videoq.last_frame_extracted-1);
978
				}
1216 979
			} // if packet_queue_get
1217 980
		} //if DecodeVideo=1
1218 981

  
1219 982
		usleep(5000);
1220 983
	}
1221
	
984
	avcodec_close(pCodecCtx);
1222 985
	av_free(pCodecCtx);
1223 986
	av_free(pFrame);
1224 987
	//fclose(frecon);
1225 988
#ifdef DEBUG_VIDEO
1226 989
 	printf("VIDEO: video callback end\n");
1227 990
#endif
991

  
992
#ifdef SAVE_YUV
993
	if(!lastSavedFrameBuffer)
994
		free(lastSavedFrameBuffer);
995
	
996
	lastSavedFrameBuffer = NULL;
997
#endif
998

  
1228 999
	return 0;
1229 1000
}
1230 1001

  
......
1237 1008

  
1238 1009
	audio_size = AudioDecodeFrame(audio_buf, sizeof(audio_buf));
1239 1010
	
1240
	if(!SilentMode)
1011
	if(SilentMode != 1)
1241 1012
		if(audio_size != len) {
1242 1013
			memset(stream, 0, len);
1243 1014
		} else {
......
1280 1051
void ChunkerPlayerCore_Play()
1281 1052
{
1282 1053
	if(AVPlaying) return;
1283
	
1284 1054
	AVPlaying = 1;
1055
	
1285 1056
	SDL_PauseAudio(0);
1286 1057
	video_thread = SDL_CreateThread(VideoCallback, &VideoCallbackThreadParams);
1058
	ChunkerPlayerStats_Init();
1059
	stats_thread = SDL_CreateThread(CollectStatisticsThread, NULL);
1060
	
1061
	decoded_vframes = 0;
1062
	LastSavedVFrame = -1;
1287 1063
}
1288 1064

  
1289 1065
void ChunkerPlayerCore_Stop()
......
1294 1070
	
1295 1071
	// Stop audio&video playback
1296 1072
	SDL_WaitThread(video_thread, NULL);
1297
	SDL_PauseAudio(1);
1073
	SDL_WaitThread(stats_thread, NULL);
1074
	SDL_PauseAudio(1);	
1298 1075
	SDL_CloseAudio();
1299 1076
	
1300 1077
	if(YUVOverlay != NULL)
......
1306 1083
	PacketQueueReset(&audioq);
1307 1084
	PacketQueueReset(&videoq);
1308 1085
	
1086
	avcodec_close(aCodecCtx);
1309 1087
	av_free(aCodecCtx);
1310 1088
	free(AudioPkt.data);
1311 1089
	free(VideoPkt.data);
1312 1090
	free(outbuf_audio);
1313 1091
	free(InitRect);
1092
	
1093
	/*
1094
	* Sleep two buffers' worth of audio before closing, in order
1095
	*  to allow the playback to finish. This isn't always enough;
1096
	*   perhaps SDL needs a way to explicitly wait for device drain?
1097
	*/
1098
	int delay = 2 * 1000 * CurrentAudioSamples / CurrentAudioFreq;
1099
	// printf("SDL_Delay(%d)\n", delay*10);
1100
	SDL_Delay(delay*10);
1101
}
1102

  
1103
void ChunkerPlayerCore_Pause()
1104
{
1105
	if(!AVPlaying) return;
1106
	
1107
	AVPlaying = 0;
1108
	
1109
	// Stop audio&video playback
1110
	SDL_WaitThread(video_thread, NULL);
1111
	SDL_PauseAudio(1);
1112
	
1113
	PacketQueueReset(&audioq);
1114
	PacketQueueReset(&videoq);
1314 1115
}
1315 1116

  
1316 1117
int ChunkerPlayerCore_AudioEnded()
......
1329 1130

  
1330 1131
int ChunkerPlayerCore_EnqueueBlocks(const uint8_t *block, const int block_size)
1331 1132
{
1133
#ifdef EMULATE_CHUNK_LOSS
1134
	static time_t loss_cycle_start_time = 0, now = 0;
1135
	static int early_losses = 0;
1136
	static int clp_frames = 0;
1137
	
1138
	if(ScheduledChunkLosses)
1139
	{
1140
		static unsigned int random_threshold;
1141
		now=time(NULL);
1142
		if(!loss_cycle_start_time)
1143
			loss_cycle_start_time = now;
1144
			
1145
		if(((now-loss_cycle_start_time) >= ScheduledChunkLosses[((CurrChunkLossIndex+1)%NScheduledChunkLosses)].Time) && (NScheduledChunkLosses>1 || CurrChunkLossIndex==-1))
1146
		{
1147
			CurrChunkLossIndex = ((CurrChunkLossIndex+1)%NScheduledChunkLosses);
1148
			if(CurrChunkLossIndex == (NScheduledChunkLosses-1))
1149
				loss_cycle_start_time = now;
1150
			
1151
			if(ScheduledChunkLosses[CurrChunkLossIndex].Value == -1)
1152
				random_threshold = ScheduledChunkLosses[CurrChunkLossIndex].MinValue + (rand() % (ScheduledChunkLosses[CurrChunkLossIndex].MaxValue-ScheduledChunkLosses[CurrChunkLossIndex].MinValue));
1153
			else
1154
				random_threshold = ScheduledChunkLosses[CurrChunkLossIndex].Value;
1155
			
1156
			printf("new ScheduledChunkLoss, time: %d, value: %d\n", (int)ScheduledChunkLosses[CurrChunkLossIndex].Time, random_threshold);
1157
		}
1158
	
1159
		if(clp_frames > 0)
1160
		{
1161
			clp_frames--;
1162
			return PLAYER_FAIL_RETURN;
1163
		}
1164
		if((rand() % 100) < random_threshold)
1165
		{
1166
			if(early_losses > 0)
1167
                early_losses--;
1168
            else
1169
            {
1170
                clp_frames=early_losses=(ScheduledChunkLosses[CurrChunkLossIndex].Burstiness-1);
1171
                return PLAYER_FAIL_RETURN;
1172
            }
1173
		}
1174
	}
1175
#endif
1176

  
1332 1177
	Chunk *gchunk = NULL;
1333 1178
	int decoded_size = -1;
1334 1179
	uint8_t *tempdata, *buffer;
......
1510 1355
	
1511 1356
	SDL_UnlockMutex(OverlayMutex);
1512 1357
}
1358

  
1359
int CollectStatisticsThread(void *params)
1360
{
1361
	struct timeval last_stats_evaluation, now, last_trace, last_qoe_evaluation;
1362
	gettimeofday(&last_stats_evaluation, NULL);
1363
	last_trace = last_stats_evaluation;
1364
	last_qoe_evaluation = last_stats_evaluation;
1365
	
1366
	double video_qdensity;
1367
	double audio_qdensity;
1368
	char audio_stats_text[255];
1369
	char video_stats_text[255];
1370
	int loss_changed = 0;
1371
	int density_changed = 0;
1372
	SStats audio_statistics, video_statistics;
1373
	double qoe = 0;
1374
	int sleep_time = STATS_THREAD_GRANULARITY*1000;
1375
	
1376
	while(AVPlaying && !quit)
1377
	{
1378
		usleep(sleep_time);
1379
		
1380
		gettimeofday(&now, NULL);
1381
		
1382
		if((((now.tv_sec*1000)+(now.tv_usec/1000)) - ((last_stats_evaluation.tv_sec*1000)+(last_stats_evaluation.tv_usec/1000))) > GUI_PRINTSTATS_INTERVAL)
1383
		{
1384
			// estimate audio queue stats
1385
			int audio_stats_changed = ChunkerPlayerStats_GetStats(&(audioq.PacketHistory), &audio_statistics);
1386
			
1387
			// estimate video queue stats
1388
			int video_stats_changed = ChunkerPlayerStats_GetStats(&(videoq.PacketHistory), &video_statistics);
1389

  
1390
#ifdef DEBUG_STATS
1391
			printf("VIDEO: %d Kbit/sec; ", video_statistics.Bitrate);
1392
			printf("AUDIO: %d Kbit/sec\n", audio_statistics.Bitrate);
1393
#endif
1394

  
1395
			// QUEUE DENSITY EVALUATION
1396
			if((audioq.last_pkt != NULL) && (audioq.first_pkt != NULL))
1397
				if(audioq.last_pkt->pkt.stream_index >= audioq.first_pkt->pkt.stream_index)
1398
				{
1399
					//plus 1 because if they are adjacent (difference 1) there really should be 2 packets in the queue
1400
					audio_qdensity = (double)audioq.nb_packets / (double)(audioq.last_pkt->pkt.stream_index - audioq.first_pkt->pkt.stream_index + 1) * 100.0;
1401
				}
1402
			
1403
			if((videoq.last_pkt != NULL) && (videoq.first_pkt != NULL))
1404
				if(videoq.last_pkt->pkt.stream_index >= videoq.first_pkt->pkt.stream_index)
1405
				{
1406
					// plus 1 because if they are adjacent (difference 1) there really should be 2 packets in the queue
1407
					video_qdensity = (double)videoq.nb_packets / (double)(videoq.last_pkt->pkt.stream_index - videoq.first_pkt->pkt.stream_index + 1) * 100.0;
1408
				}
1409
			
1410
			if(LogTraces)
1411
			{
1412
				ChunkerPlayerStats_PrintHistoryTrace(&(audioq.PacketHistory), AudioTraceFilename);
1413
				ChunkerPlayerStats_PrintHistoryTrace(&(videoq.PacketHistory), VideoTraceFilename);
1414
				
1415
				if(SilentMode != 1 && SilentMode != 2)
1416
					ChunkerPlayerStats_PrintContextFile();
1417
			}
1418

  
1419
			// PRINT STATISTICS ON GUI
1420
			if(!Audio_ON)
1421
				sprintf(audio_stats_text, "AUDIO MUTED");
1422
			else if(audio_stats_changed)
1423
				sprintf(audio_stats_text, "[AUDIO] qdensity: %d\%% - losses: %d/sec (%ld tot) - skips: %d/sec (%ld tot)", (int)audio_qdensity, (int)audio_statistics.Lossrate, audioq.PacketHistory.LostCount, audio_statistics.Skiprate, audioq.PacketHistory.SkipCount);
1424
			else
1425
				sprintf(audio_stats_text, "waiting for incoming audio packets...");
1426

  
1427
			if(video_stats_changed)
1428
			{
1429
				char est_psnr_string[255];
1430
				sprintf(est_psnr_string, "");
1431
				if(qoe)
1432
					sprintf(est_psnr_string, " - Est. Mean PSNR: %.1f db", (float)qoe);
1433

  
1434
				sprintf(video_stats_text, "[VIDEO] qdensity: %d\%% - losses: %d/sec (%ld tot) - skips: %d/sec (%ld tot)%s", (int)video_qdensity, video_statistics.Lossrate, videoq.PacketHistory.LostCount, video_statistics.Skiprate, videoq.PacketHistory.SkipCount, est_psnr_string);
1435
			}
1436
			else
1437
				sprintf(video_stats_text, "waiting for incoming video packets...");
1438

  
1439
			ChunkerPlayerGUI_SetStatsText(audio_stats_text, video_stats_text);
1440
			last_stats_evaluation = now;
1441
		}
1442
		
1443
		if((((now.tv_sec*1000)+(now.tv_usec/1000)) - ((last_qoe_evaluation.tv_sec*1000)+(last_qoe_evaluation.tv_usec/1000))) > EVAL_QOE_INTERVAL)
1444
		{
1445
			// ESTIMATE QoE
1446
			ChunkerPlayerStats_GetMeanVideoQuality(&(videoq.PacketHistory), &qoe);
1447
			
1448
#ifdef DEBUG_STATS
1449
			printf("QoE index: %f\n", (float) qoe);
1450
#endif
1451
			last_qoe_evaluation = now;
1452
		}
1453
	}
1454
}

Also available in: Unified diff