MHEG5  18.9.0
MHEG5 Documentation
mh5streamer.c
Go to the documentation of this file.
1 /*******************************************************************************
2  * Copyright © 2014 The DTVKit Open Software Foundation Ltd (www.dtvkit.org)
3  * Copyright © 2010 Ocean Blue Software Ltd
4  *
5  * This file is part of a DTVKit Software Component
6  * You are permitted to copy, modify or distribute this file subject to the terms
7  * of the DTVKit 1.0 Licence which can be found in licence.txt or at www.dtvkit.org
8  *
9  * THIS CODE AND INFORMATION ARE PROVIDED "AS IS" WITHOUT WARRANTY OF ANY KIND,
10  * EITHER EXPRESSED OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE IMPLIED WARRANTIES
11  * OF MERCHANTABILITY AND/OR FITNESS FOR A PARTICULAR PURPOSE.
12  *
13  * If you or your organisation is not a member of DTVKit then you have access
14  * to this source code outside of the terms of the licence agreement
15  * and you are expected to delete this and any associated files immediately.
16  * Further information on DTVKit, membership and terms can be found at www.dtvkit.org
17  *******************************************************************************/
25 /*---includes for this file--------------------------------------------------*/
26 #include <string.h>
27 
28 #include "stb_os.h"
29 #include "dvb_audio.h"
30 #include "dvb_video.h"
31 #include "httptype.h"
32 #include "mherrors.h"
33 #include "dvb_ics.h"
34 
35 #ifdef INCLUDE_ICS
36 
37 #include "mh5streamer.h"
38 #include "mh5profile.h"
39 #include "mh5memory.h"
40 #include "mh5gate.h"
41 #include "mh5object.h"
42 #include "mh5audio.h"
43 #include "mh5video.h"
44 #include "glue_queue.h"
45 #include "glue_timers.h"
46 #include "mh5application.h"
47 #include "mh5queue.h"
48 #include "mh5prgs.h"
49 #include "mh5control.h"
50 #include "mh5access.h"
51 #include "mh5tls.h"
52 #include "mh5fileorm.h"
53 #include "mh5parse.h"
54 #include "stmr_common.h"
55 #include "stmr_queue.h"
56 #include "stmr_dl.h"
57 #include "stmr_task.h"
58 #include "stmr_msp.h"
59 #include "glue_main.h"
60 #include "fs_types.h"
61 
62 /*---constant definitions for this file--------------------------------------*/
63 
64 #define STREAMER_STACK_SIZE (4096)
65 #define MIN_BUFFERED_BYTES (524288)
66 /*#define MIN_BUFFERED_BYTES (3000000)*/
67 #define INVALID_MEASUREMENT (-1)
68 #define MAX_HISTORY_SIZE (262144)
69 #define MAX_REDIRECTS (5)
70 
71 #define DEBUG_DETAIL(x)
72 #define DBG(x)
73 #define PDBG(x) TRACE(TICS, x)
74 
75 /* verbose debug */
76 #define VDBG(x)
77 
78 #define REQUEST_STREAM_STATE_STR(r) \
79  ((r)->streamState == STATE_STREAM_STOPPED ? "STATE_STREAM_STOPPED" : \
80  (r)->streamState == STATE_STREAM_PREPARED ? "STATE_STREAM_PREPARED" : \
81  (r)->streamState == STATE_STREAM_RUNNING ? "STATE_STREAM_RUNNING" : \
82  "UNKNOWN")
83 
84 #define REQUEST_DOWNLOAD_STATE_STR(r) \
85  ((r)->downloadState == STATE_DOWNLOAD_STOPPED ? "STATE_DOWNLOAD_STOPPED" : \
86  (r)->downloadState == STATE_DOWNLOAD_QUEUED ? "STATE_DOWNLOAD_QUEUED" : \
87  (r)->downloadState == STATE_DOWNLOAD_CHECK_ACCESS ? "STATE_DOWNLOAD_CHECK_ACCESS" : \
88  (r)->downloadState == STATE_DOWNLOAD_WAIT_ACCESS ? "STATE_DOWNLOAD_WAIT_ACCESS" : \
89  (r)->downloadState == STATE_DOWNLOAD_HAS_ACCESS ? "STATE_DOWNLOAD_HAS_ACCESS" : \
90  (r)->downloadState == STATE_DOWNLOAD_WAIT_CERT ? "STATE_DOWNLOAD_WAIT_CERT" : \
91  (r)->downloadState == STATE_DOWNLOAD_HAS_CERT ? "STATE_DOWNLOAD_HAS_CERT" : \
92  (r)->downloadState == STATE_DOWNLOAD_STARTED ? "STATE_DOWNLOAD_STARTED" : \
93  (r)->downloadState == STATE_DOWNLOAD_RUNNING ? "STATE_DOWNLOAD_RUNNING" : \
94  (r)->downloadState == STATE_DOWNLOAD_DONE ? "STATE_DOWNLOAD_DONE" : \
95  (r)->downloadState == STATE_DOWNLOAD_FAILED ? "STATE_DOWNLOAD_FAILED" : \
96  "UNKNOWN")
97 
98 #define REQUEST_OUTPUT_STATE_STR(r) \
99  ((r)->outputState == STATE_OUTPUT_STOPPED ? "STATE_OUTPUT_STOPPED" : \
100  (r)->outputState == STATE_OUTPUT_PENDING ? "STATE_OUTPUT_PENDING" : \
101  (r)->outputState == STATE_OUTPUT_SETUP ? "STATE_OUTPUT_SETUP" : \
102  (r)->outputState == STATE_OUTPUT_READY ? "STATE_OUTPUT_READY" : \
103  (r)->outputState == STATE_OUTPUT_RUNNING ? "STATE_OUTPUT_RUNNING" : \
104  (r)->outputState == STATE_OUTPUT_STARVED ? "STATE_OUTPUT_STARVED" : \
105  (r)->outputState == STATE_OUTPUT_DONE ? "STATE_OUTPUT_DONE" : \
106  "UNKNOWN")
107 
108 #define REQUEST_FLOW_STATE_STR(r) \
109  ((r)->flowState == STATE_FLOW_STOPPED ? "STATE_FLOW_STOPPED" : \
110  (r)->flowState == STATE_FLOW_STARTED ? "STATE_FLOW_STARTED" : \
111  (r)->flowState == STATE_FLOW_RUNNING ? "STATE_FLOW_RUNNING" : \
112  (r)->flowState == STATE_FLOW_UNDERFLOW ? "STATE_FLOW_UNDERFLOW" : \
113  "UNKNOWN")
114 
115 #define REQUEST_KEY_STATE_STR(r) \
116  ((r)->keyState == STATE_KEY_UNKNOWN ? "STATE_KEY_UNKNOWN" : \
117  (r)->keyState == STATE_KEY_VALID ? "STATE_KEY_VALID" : \
118  (r)->keyState == STATE_KEY_PENDING ? "STATE_KEY_PENDING" : \
119  (r)->keyState == STATE_KEY_ERROR ? "STATE_KEY_ERROR" : \
120  "UNKNOWN")
121 
122 
123 /*---local typedef structs for this file-------------------------------------*/
124 
125 typedef enum
126 {
127  ST_PES_PKT = 0x06,
128  ST_AAC_ADTS = 0x0f,
129  ST_HEAAC = 0x11,
130  ST_H264 = 0x1b,
131  ST_DOLBY_BRAY = 0x84,
132  ST_DOLBY_ATSC = 0x87,
133 } E_STREAM_TYPE;
134 
135 typedef enum {
136  ISO_LANG_DTAG = 0x0a,
137  STREAMID_DTAG = 0x52,
138  SUBTITLE_DTAG = 0x59,
139 } E_DTAGS;
140 
141 typedef enum
142 {
143  STATE_STREAM_STOPPED,
144  STATE_STREAM_PREPARED,
145  STATE_STREAM_RUNNING
146 } StreamState;
147 
148 typedef enum
149 {
150  STATE_DOWNLOAD_QUEUED,
151  STATE_DOWNLOAD_STOPPED,
152  STATE_DOWNLOAD_CHECK_ACCESS,
153  STATE_DOWNLOAD_WAIT_ACCESS,
154  STATE_DOWNLOAD_HAS_ACCESS,
155  STATE_DOWNLOAD_WAIT_CERT,
156  STATE_DOWNLOAD_HAS_CERT,
157  STATE_DOWNLOAD_STARTED,
158  STATE_DOWNLOAD_RUNNING,
159  STATE_DOWNLOAD_DONE,
160  STATE_DOWNLOAD_FAILED
161 } DownloadState;
162 
163 typedef enum
164 {
165  STATE_OUTPUT_STOPPED,
166  STATE_OUTPUT_PENDING,
167  STATE_OUTPUT_SETUP,
168  STATE_OUTPUT_READY,
169  STATE_OUTPUT_RUNNING,
170  STATE_OUTPUT_STARVED,
171  STATE_OUTPUT_DONE
172 } OutputState;
173 
174 typedef enum
175 {
176  STATE_FLOW_STOPPED,
177  STATE_FLOW_STARTED,
178  STATE_FLOW_RUNNING,
179  STATE_FLOW_UNDERFLOW
180 } FlowState;
181 
182 typedef enum
183 {
184  STATE_KEY_UNKNOWN,
185  STATE_KEY_VALID,
186  STATE_KEY_PENDING,
187  STATE_KEY_ERROR
188 } KeyState;
189 
190 /* A/V component selection for IP streaming */
191 typedef struct
192 {
193  BOOLEAN audio_active;
194  BOOLEAN video_active;
195  S32BIT audio_component_tag;
196  S32BIT video_component_tag;
197  E_VideoTermination video_termination;
198 } S_ICSComponents;
199 
200 typedef struct sCounterTrigger
201 {
202  S32BIT id;
203  S32BIT position;
204  struct sCounterTrigger *next;
205 } CounterTrigger;
206 
207 typedef struct sStreamerRequest
208 {
209  U32BIT requestId;
210  U32BIT downloadId;
211 
212  MHEG5Root *root;
213 
214  BOOLEAN isMeasureType;
215  BOOLEAN contentAvailableNotified;
216  BOOLEAN contentAvailableGenerated;
217  BOOLEAN streamPlayingGenerated;
218  BOOLEAN paused;
219  BOOLEAN restart;
220  BOOLEAN isPartial;
221  BOOLEAN pmtRetrieved;
222  BOOLEAN terminateStreamSetup;
223 
224  S32BIT pauseRequestCount;
225  S32BIT counterEndPosition;
226  S32BIT counterMaxPosition;
227  S32BIT looping;
228  S32BIT currentLoop;
229 
230  U64BIT contentLength;
231  U64BIT bytePosition;
232  U64BIT currentBytePosition;
233  U64BIT expectedBytes;
234  U64BIT downloadedBytes;
235  U64BIT presentedBytes;
236 
237  StreamState streamState;
238  DownloadState downloadState;
239  OutputState outputState;
240  FlowState flowState;
241 
242  MHEG5String url;
243  MHEG5String redirectUrl;
244 
245  U32BIT certCount;
246  U32BIT redirectCount;
247 
248  U32BIT bitrate;
249  U32BIT prepareNumber;
250  U32BIT maxBytes;
251 
252  S_ICSKeys keys;
253  MHEG5String keyLocation;
254  KeyState keyState;
255  U32BIT keyDownloadId;
256 
258 
259  CounterTrigger *triggers;
260 
261  struct sStreamerRequest *next;
262 } StreamerRequest;
263 
264 /*---local function definitions----------------------------------------------*/
265 
266 /*---global function definitions---------------------------------------------*/
267 
268 static StreamerRequest* CreateRequest(BOOLEAN isMeasure);
269 static void PrepareStreamingRequest(StreamerRequest *request,
270  MHEG5Stream *stream);
271 static void SetCounterPosition(StreamerRequest *request, MHEG5Stream *stream);
272 static void AddRequestToList(StreamerRequest *request);
273 static void RemoveRequestFromList(StreamerRequest *request);
274 static void DestroyRequest(StreamerRequest *request);
275 static void CreateDownloadRequest(StreamerRequest *request);
276 static void ProgressDownloadRequest(StreamerRequest *request);
277 static void CheckTlsCertificates(StreamerRequest *request);
278 static void DestroyDownloadRequest(StreamerRequest *request);
279 static void DestroyMeasurementRequest(StreamerRequest *request);
280 static StreamerRequest* FindRequestById(U32BIT requestId);
281 static StreamerRequest* FindRequestByObj(MHEG5Root *root);
282 static StreamerRequest* FindRequestByDownloadId(U32BIT downloadId);
283 static void ProcessHttpHeaders(U32BIT downloadId,S32BIT code);
284 static void CalculateExpectedBytes(StreamerRequest *request);
285 static void HandleStreamKeys(StreamerRequest *request);
286 static void HandleDownloadFinished(U32BIT downloadId,E_HttpStatus status,S32BIT code);
287 static void HandleDownloadRedirection(StreamerRequest *request,
288  BOOLEAN permanent);
289 static void HandleDownloadSuccess(StreamerRequest *request);
290 static void HandleDownloadPartial(StreamerRequest *request,U32BIT downloadId);
291 static void HandleMeasurementFinished(U32BIT downloadId);
292 static void HandleMeasurementRedirection(StreamerRequest *request);
293 static void HandleStreamPlaying(U32BIT requestId);
294 static void HandleStreamStopped(U32BIT requestId);
295 static void HandleCounterTrigger(U32BIT requestId, S32BIT triggerId);
296 static void HandleContentAvailable(U32BIT requestId);
297 static void GenerateStreamRefError(StreamerRequest *request);
298 #ifdef INCLUDE_FREESAT
299 static void GenerateIPStreamAccessError(StreamerRequest *request);
300 #endif /* INCLUDE_FREESAT */
301 static void GenerateStreamUnderflow(void);
302 static void GenerateStreamUnderflowResume(void);
303 static void GenerateContentAvailable(StreamerRequest *request);
304 static void GenerateStreamPlaying(StreamerRequest *request);
305 static void GenerateStreamStopped(StreamerRequest *request);
306 static void GenerateCounterTrigger(StreamerRequest *request, S32BIT triggerId);
307 static void GenerateKeyFileError(void);
308 static void NotifyContentAvailable(U32BIT requestId);
309 static void NotifyStreamPlaying(U32BIT requestId);
310 static void NotifyStreamStopped(U32BIT requestId);
311 static void ItemInsertCallback(U32BIT downloadId, U64BIT base,
312  U64BIT position, U32BIT len, BOOLEAN last);
313 static void ItemReleaseCallback(U32BIT requestId, U64BIT base,
314  U64BIT position, U32BIT len, BOOLEAN last);
315 static void CheckCounterTriggers(StreamerRequest *request, U64BIT base,
316  U64BIT position, U32BIT len);
317 static void CheckRunCondition(StreamerRequest *request);
318 static BOOLEAN IsEnoughDataBuffered(StreamerRequest *request);
319 static U32BIT ConvertToUnits(StreamerRequest *request, U64BIT bytes);
320 static U64BIT ConvertToBytes(StreamerRequest *request, U32BIT units);
321 static BOOLEAN IsStreamRunning(void);
322 static void CheckDownloadCondition(void);
323 static void StopContentOutput(StreamerRequest *request, BOOLEAN flush);
324 static void ResumePlayback(StreamerRequest *request);
325 static BOOLEAN IsValidUrl(MHEG5String *url);
326 static void UnderflowCallback(U32BIT requestId,U32BIT playoutTime);
327 static void StreamKeyLoadedCallback(void *userData, S_CONTENT *content);
328 static void StreamKeyLoadFailCallback(void *userData);
329 static void ServerAccessCallback(void);
330 static void TlsCertCallback(void);
331 static BOOLEAN MHEG5StreamerProcess(void);
332 
333 
334 /*---local (static) variable declarations for this file----------------------*/
335 static StreamerRequest *requestList = NULL;
336 
337 /* Start from 1, to detect invalid requests */
338 static U32BIT globalRequestId = 1;
339 
340 /* Start from 1, to detect invalid values */
341 static U32BIT globalPrepareNumber = 1;
342 
343 static void *streamerBuffer = NULL;
344 static U32BIT streamerBufferSize = 0;
345 
346 static void *requestMutex = NULL;
347 static BOOLEAN isIcsSetup = FALSE;
348 static BOOLEAN processRequests = FALSE;
349 
357 E_MhegErr MHEG5StreamerOpen(void *buffer, U32BIT size, U32BIT taskPriority)
358 {
359  E_MhegErr result;
360  streamerBuffer = NULL;
361  streamerBufferSize = 0;
362 
363  if (buffer == NULL)
364  {
365  #ifdef ERROR_ON_SMALL_ICS_BUFFER
366  TRACE(TERROR, ("Streaming buffer must not be NULL"));
367  result = MHERR_BAD_ICS_BUFFER;
368  #else
369  DPL2(("Streaming buffer NULL - Streaming disabled"));
370  result = MHERR_OK;
371  #endif
372  }
373  else if (size < ICS_MINIMUM_BUFFER_SIZE)
374  {
375  #ifdef ERROR_ON_SMALL_ICS_BUFFER
376  TRACE(TERROR, ("Minimum buffer size is %d, given %d", ICS_MINIMUM_BUFFER_SIZE, size));
377  result = MHERR_BAD_ICS_BUFFER_SIZE;
378  #else
379  DPL2(("Streaming buffer too small - Streaming disabled"));
380  result = MHERR_OK;
381  #endif
382  }
383  else
384  {
385  streamerBuffer = buffer;
386  streamerBufferSize = size;
387 
388  /* Initialise queue manager */
389  result = MHEG5QueueInit(streamerBuffer, streamerBufferSize);
390  if (result == MHERR_OK)
391  {
392  MHEG5QueueRegisterInsertCallback(ItemInsertCallback);
393  MHEG5QueueRegisterReleaseCallback(ItemReleaseCallback);
394 
395  /* Initialise streamer task */
396  MHEG5StreamerRegisterUnderflowCallback(UnderflowCallback);
397 
398  requestList = NULL;
399  ASSERT(requestMutex == NULL)
400 
401  requestMutex = STB_OSCreateMutex();
402  if (requestMutex == NULL)
403  {
404  TRACE(TERROR, ("Streaming requerst mutex failure"));
406  }
407  else
408  {
409  MHEG5AddServerAccessCallback(ServerAccessCallback);
410  MHEG5AddTlsCertificateCallback(TlsCertCallback);
411 
412  result = MHEG5StreamerStartTask(STREAMER_STACK_SIZE, (U8BIT)taskPriority);
413  }
414  }
415  }
416  return result;
417 }
418 
423 void MHEG5StreamerReset(void)
424 {
425  StreamerRequest *request;
426  StreamerRequest *list;
427  if (streamerBuffer != NULL)
428  {
430 
431  STB_OSMutexLock(requestMutex);
432  list = requestList;
433  requestList = NULL;
434  STB_OSMutexUnlock(requestMutex);
435 
436  /* The request list should be empty, but clean up if not */
437  while (list != NULL)
438  {
439  request = list;
440  list = request->next;
441  DestroyRequest(request);
442  }
443  }
444 }
445 
451 void MHEG5StreamerPrepare(MHEG5Stream *stream)
452 {
453  StreamerRequest *request;
454 
455  assert(((MHEG5Root *)stream)->clazz == MHEG5STREAM);
456  request = FindRequestByObj(&stream->ingredient.root);
457  if (request == NULL)
458  {
459  /* Create and initialise new request */
460  request = CreateRequest(FALSE);
461  if (request != NULL)
462  {
463  STB_OSMutexLock(requestMutex);
464  AddRequestToList(request);
465  STB_OSMutexUnlock(requestMutex);
466  }
467  }
468  else
469  {
470  /* This could be SetData action - previous counter max posn is no longer valid */
471  request->counterMaxPosition = -1;
472  }
473 
474  if (request != NULL)
475  {
476  /* ContentAvailable will only be generated when preparing
477  * the stream (also after SetData and Preload, if necessary). But
478  * if the stream is stopped and then run again, ContentAvailable
479  * will not be generated.
480  */
481  if (request->streamState != STATE_STREAM_RUNNING)
482  {
483  request->contentAvailableNotified = FALSE;
484  request->contentAvailableGenerated = FALSE;
485  }
486 
487  /* Update "prepare number" to push the request to the end
488  * of the queue. If the request is primary, it doesn't matter.
489  */
490  request->prepareNumber = globalPrepareNumber;
491  globalPrepareNumber = (globalPrepareNumber % 1073741824) + 1;
492 
493  PrepareStreamingRequest(request, stream);
494  }
495 }
496 
503 {
504  StreamerRequest *request;
505  S32BIT counterPosition;
506 
507  counterPosition = 0;
508 
509  request = FindRequestByObj(&stream->ingredient.root);
510  if (request != NULL)
511  {
512  STB_OSMutexLock(requestMutex);
513  counterPosition = ConvertToUnits(request, request->currentBytePosition);
514  STB_OSMutexUnlock(requestMutex);
515  }
516  else
517  {
518  /* Ignore quietly */
519  TRACE(TERROR, ("Cannot find request"));
520  counterPosition = stream->counterPosition;
521  }
522 
523  return counterPosition;
524 }
525 
533 {
534  StreamerRequest *request;
535  S32BIT endPosition;
536 
537  request = FindRequestByObj(&stream->ingredient.root);
538  if (request != NULL)
539  {
540  /* Sanity check */
541  if (stream->counterPosition >= 0)
542  {
543  if (((request->counterMaxPosition == -1) ||
544  (request->counterMaxPosition > stream->counterPosition)) &&
545  ((request->counterEndPosition == -1) ||
546  (request->counterEndPosition > stream->counterPosition)))
547  {
548  SetCounterPosition(request, stream);
549  }
550  else
551  {
552  if (request->streamState == STATE_STREAM_RUNNING)
553  {
555  }
556 
557  /* Stop current download */
558  DestroyDownloadRequest(request);
559 
560  /* Generate StreamStopped if required */
561  if (request->outputState != STATE_OUTPUT_DONE)
562  {
563  GenerateStreamStopped(request);
564  }
565 
566  /* Disable streaming and remove queue items from the queue */
567  StopContentOutput(request, TRUE);
568 
569  /* Update counter position */
570  endPosition = stream->counterPosition;
571  if (stream->counterEndPosition >= 0 &&
572  stream->counterEndPosition < endPosition)
573  {
574  endPosition = stream->counterEndPosition;
575  }
576  if (stream->counterMaxPosition >= 0 &&
577  stream->counterMaxPosition < endPosition)
578  {
579  endPosition = stream->counterMaxPosition;
580  }
581  request->bytePosition = ConvertToBytes(request, endPosition);
582  request->currentBytePosition = request->bytePosition;
583  request->currentLoop = 0;
584  request->restart = TRUE;
585 
586  if (request->streamState == STATE_STREAM_RUNNING && isIcsSetup)
587  {
588  isIcsSetup = FALSE;
590  }
591  }
592  }
593  }
594 }
595 
603 {
604  StreamerRequest *request;
605  //S32BIT contentLength = -1;
606 
607  request = FindRequestByObj(&stream->ingredient.root);
608  if (request != NULL)
609  {
610  request->counterEndPosition = stream->counterEndPosition;
611  //contentLength = 0;
612  //if (request->counterMaxPosition != -1)
613  //{
614  // contentLength = request->counterMaxPosition + 1;
615  //}
616  CalculateExpectedBytes(request);
617  }
618 }
619 
626 {
627  StreamerRequest *request;
628  S32BIT counterMaxPosition;
629 
630  request = FindRequestByObj(&stream->ingredient.root);
631  if (request != NULL)
632  {
633  STB_OSMutexLock(requestMutex);
634  counterMaxPosition = request->counterMaxPosition;
635  STB_OSMutexUnlock(requestMutex);
636  }
637  else
638  {
639  /* Ignore quietly */
640  TRACE(TERROR, ("Cannot find request"));
641  counterMaxPosition = stream->counterMaxPosition;
642  }
643 
644  return counterMaxPosition;
645 }
646 
657 void MHEG5StreamerSetCounterTrigger(MHEG5Stream *stream, S32BIT triggerId,
658  S32BIT newValue)
659 {
660  StreamerRequest *request;
661  CounterTrigger **pTrigger, *trigger;
662  BOOLEAN found;
663 
664  request = FindRequestByObj(&stream->ingredient.root);
665  if (request != NULL)
666  {
667  STB_OSMutexLock(requestMutex);
668 
669  found = FALSE;
670  pTrigger = &request->triggers;
671  while (*pTrigger != NULL)
672  {
673  trigger = *pTrigger;
674  if (trigger->id == triggerId)
675  {
676  if (newValue >= 0)
677  {
678  /* Replace value */
679  trigger->position = newValue;
680  }
681  else
682  {
683  /* Remove trigger */
684  *pTrigger = trigger->next;
685  MHEG5freeMem(trigger);
686  }
687  found = TRUE;
688  break;
689  }
690  pTrigger = &trigger->next;
691  }
692 
693  if (!found && newValue >= 0)
694  {
695  /* Add new trigger */
696  trigger = MHEG5getMem(sizeof *trigger);
697  if (trigger != NULL)
698  {
699  trigger->id = triggerId;
700  trigger->position = newValue;
701  trigger->next = NULL;
702  *pTrigger = trigger;
703  }
704  }
705  STB_OSMutexUnlock(requestMutex);
706  }
707  else
708  {
709  /* Ignore quietly */
710  TRACE(TERROR, ("Cannot find request"));
711  }
712 }
713 
719 void MHEG5StreamerRun(MHEG5Stream *stream)
720 {
721  StreamerRequest *request;
722 
723  request = FindRequestByObj(&stream->ingredient.root);
724  if (request != NULL)
725  {
726  if (request->streamState == STATE_STREAM_RUNNING)
727  {
728  /* Shouldn't be here - already running */
729  TRACE(TICS,("already running"));
730  return;
731  }
732 
733  if (IsStreamRunning())
734  {
735  /* Another stream is already running, do nothing */
736  TRACE(TICS,("another running"));
737  return;
738  }
739 
740  /* If the stream is at the end (including looping), it should
741  * play as if it were activated for the first time.
742  */
743  if (request->restart)
744  {
745  /* Restart stream */
746  request->currentLoop = 0;
747  ULL_Set32(request->bytePosition, 0);
748  ULL_Set32(request->currentBytePosition, 0);
749  }
750 
751  #ifdef TO_REMOVE
752  /* The download may be stopped in the following scenario:
753  * prepare -> run -> stop -> run
754  * In that case the stream is already "prepared", but the
755  * download is stopped. In this case we need to start
756  * the download again (everything else is ready to run).
757  */
758  if (request->downloadState == STATE_DOWNLOAD_STOPPED)
759  {
760  /* Already stopped, need to re-prepare it */
761  CreateDownloadRequest(request);
762  }
763  #endif
764 
765  STB_OSMutexLock(requestMutex);
766 
767  /* StreamPlaying will be generated when the stream starts
768  * playing
769  */
770  request->streamPlayingGenerated = FALSE;
771  request->streamState = STATE_STREAM_RUNNING;
772 
773  CheckDownloadCondition();
774 
775  assert(request->outputState == STATE_OUTPUT_STOPPED);
776  CheckRunCondition(request);
777 
778  STB_OSMutexUnlock(requestMutex);
779  }
780  else
781  {
782  /* Ignore quietly */
783  TRACE(TERROR, ("Cannot find request"));
784  }
785 }
786 
792 void MHEG5StreamerStop(MHEG5Stream *stream)
793 {
794  StreamerRequest *request;
795 
796  request = FindRequestByObj(&stream->ingredient.root);
797  if (request != NULL)
798  {
799  if (request->streamState == STATE_STREAM_STOPPED)
800  {
801  /* Nothing to do. This can happen if the stream was :Run, but
802  * another stream was already running. In that case the streamer
803  * state is incosistent with the object state, so :Stop can
804  * still be used on the Stream object, but the request is
805  * actually stopped.
806  */
807  return;
808  }
809 
810  if (request->streamState == STATE_STREAM_RUNNING)
811  {
813  }
814 
815  /* Stop and destroy the HTTP request before clearing the queue,
816  * otherwise there's a race condition.
817  */
818  DestroyDownloadRequest(request);
819 
820  /* Generate StreamStopped if required */
821  if (request->outputState != STATE_OUTPUT_DONE)
822  {
823  GenerateStreamStopped(request);
824  }
825 
826  /* Disable streaming and remove queue items from the queue */
827  StopContentOutput(request, TRUE);
828 
829  /* Update counter position */
830  stream->counterPosition = ConvertToUnits(request, request->currentBytePosition);
831  request->bytePosition = request->currentBytePosition;
832  request->pauseRequestCount = 0;
833 
834  MHEG5stringDestruct(&request->redirectUrl);
835  request->redirectCount = 0;
836 
837  if (request->streamState == STATE_STREAM_RUNNING && isIcsSetup)
838  {
839  isIcsSetup = FALSE;
841  }
842 
843  request->streamState = STATE_STREAM_STOPPED;
844  }
845  else
846  {
847  /* Ignore quietly */
848  TRACE(TERROR, ("Cannot find request"));
849  }
850 }
851 
857 void MHEG5StreamerPause(MHEG5Stream *stream)
858 {
859  StreamerRequest *request;
860 
861  request = FindRequestByObj(&stream->ingredient.root);
862  if (request != NULL)
863  {
864  if ((request->streamState == STATE_STREAM_RUNNING) &&
865  (!request->paused))
866  {
867  switch (request->outputState)
868  {
869  case STATE_OUTPUT_READY:
870  case STATE_OUTPUT_RUNNING:
871  case STATE_OUTPUT_STARVED:
872  ++request->pauseRequestCount;
873  if (request->pauseRequestCount == 1)
874  {
877  }
878  break;
879 
880  default:
881  /* Do nothing - no output */
882  ;
883  }
884 
885  /* Update counter position */
886  stream->counterPosition = ConvertToUnits(request, request->currentBytePosition);
887  }
888  request->paused = TRUE;
889  }
890  else
891  {
892  /* Ignore quietly */
893  TRACE(TERROR, ("Cannot find request"));
894  }
895 }
896 
902 void MHEG5StreamerResume(MHEG5Stream *stream)
903 {
904  StreamerRequest *request;
905 
906  request = FindRequestByObj(&stream->ingredient.root);
907  if (request != NULL)
908  {
909  if ((request->streamState == STATE_STREAM_RUNNING) &&
910  (request->paused))
911  {
912  switch (request->outputState)
913  {
914  case STATE_OUTPUT_READY:
915  case STATE_OUTPUT_RUNNING:
916  case STATE_OUTPUT_STARVED:
917  case STATE_OUTPUT_STOPPED:
918  ResumePlayback(request);
919  break;
920  default:
921  ;
922  }
923  }
924  request->paused = FALSE;
925  }
926  else
927  {
928  /* Ignore quietly */
929  TRACE(TERROR, ("Cannot find request"));
930  }
931 }
932 
940 {
941  return (FindRequestByObj(&stream->ingredient.root))? TRUE : FALSE;
942 }
943 
949 void MHEG5StreamerRemove(MHEG5Stream *stream)
950 {
951  StreamerRequest *request;
952 
953  request = FindRequestByObj(&stream->ingredient.root);
954  if (request != NULL)
955  {
956  if (request->streamState == STATE_STREAM_RUNNING)
957  {
959  }
960 
961  /* Stop and destroy the HTTP request before clearing the queue,
962  * otherwise there's a race condition.
963  */
964  DestroyDownloadRequest(request);
965 
966  /* Disable streaming and remove queue items from the queue */
967  StopContentOutput(request, TRUE);
968 
969  STB_OSMutexLock(requestMutex);
970  RemoveRequestFromList(request);
971  STB_OSMutexUnlock(requestMutex);
972 
973  DestroyRequest(request);
974  }
975 }
976 
983 void MHEG5StreamerHandle(MHEG5StreamerEventParams_t *params)
984 {
985  switch (params->eventType)
986  {
988  PDBG(("MHEG5_STREAMER_EVENT_MSP_DOWNLOAD_DONE"));
989  HandleMeasurementFinished(params->requestId);
990  break;
992  PDBG(("MHEG5_STREAMER_HTTP_HEADERS_DONE"));
993  ProcessHttpHeaders(params->requestId,params->code);
994  break;
996  PDBG(("MHEG5_STREAMER_EVENT_HTTP_DOWNLOAD_DONE"));
997  HandleDownloadFinished(params->requestId,params->status,params->code);
998  break;
1000  PDBG(("MHEG5_STREAMER_EVENT_TASK_STREAM_PLAYING"));
1001  HandleStreamPlaying(params->requestId);
1002  break;
1004  PDBG(("MHEG5_STREAMER_EVENT_TASK_STREAM_STOPPED"));
1005  HandleStreamStopped(params->requestId);
1006  break;
1008  PDBG(("MHEG5_STREAMER_EVENT_COUNTER_TRIGGER"));
1009  HandleCounterTrigger(params->requestId, params->triggerId);
1010  break;
1012  DBG(("MHEG5_STREAMER_EVENT_CONTENT_AVAILABLE"));
1013  HandleContentAvailable(params->requestId);
1014  break;
1015  default:
1016  PDBG(("Unhandled event %d!", params->eventType));
1017  break;
1018  }
1019  MH5GlueAddPostProcessFunc( MHEG5StreamerProcess );
1020 }
1021 
1029 {
1030  StreamerRequest *request;
1031 
1032  assert(((MHEG5Root *)stream)->clazz == MHEG5STREAM);
1033  request = FindRequestByObj(&stream->ingredient.root);
1034  if (request != NULL)
1035  {
1036  switch (request->flowState)
1037  {
1038  case STATE_FLOW_STARTED:
1039  request->flowState = STATE_FLOW_RUNNING;
1040  GenerateStreamPlaying(request);
1041  break;
1042  case STATE_FLOW_UNDERFLOW:
1043  request->flowState = STATE_FLOW_RUNNING;
1044  GenerateStreamUnderflowResume();
1045  break;
1046  case STATE_FLOW_RUNNING:
1047  /* Multiple components, playback resumed or confused decoders */
1048  DBG(("Ignoring StreamStarted in STATE_FLOW_RUNNING"));
1049  break;
1050  case STATE_FLOW_STOPPED:
1051  PDBG(("%d ~ WARNING: Stream started when flow is stopped",
1052  request->requestId));
1053  break;
1054  default:
1055  TRACE(TERROR, ("Unhandled flow state"));
1056  assert(0);
1057  break;
1058  }
1059  }
1060 }
1061 
1069 {
1070  StreamerRequest *request;
1071 
1072  assert(((MHEG5Root *)stream)->clazz == MHEG5STREAM);
1073  request = FindRequestByObj(&stream->ingredient.root);
1074  if (request != NULL)
1075  {
1076  PDBG(("%d ~ request->flowState = %s", request->requestId,
1077  REQUEST_FLOW_STATE_STR(request)));
1078  switch (request->flowState)
1079  {
1080  case STATE_FLOW_RUNNING:
1081  PDBG(("%d ~ request->downloadState = %s", request->requestId,
1082  REQUEST_DOWNLOAD_STATE_STR(request)));
1083  if (request->downloadState == STATE_DOWNLOAD_RUNNING)
1084  {
1085  request->flowState = STATE_FLOW_UNDERFLOW;
1086  GenerateStreamUnderflow();
1087  }
1088  else if (request->downloadState == STATE_DOWNLOAD_DONE)
1089  {
1090  request->flowState = STATE_FLOW_STOPPED;
1091  GenerateStreamStopped(request);
1092  }
1093  else
1094  {
1095  TRACE(TERROR, ("Unhandled download state"));
1096  assert(0);
1097  }
1098  break;
1099  case STATE_FLOW_STOPPED:
1100  /* Expected underflow, ignore */
1101  DBG(("Expected underflow in %s", REQUEST_FLOW_STATE_STR(request)));
1102  break;
1103  case STATE_FLOW_UNDERFLOW:
1104  /* Spurious notification */
1105  break;
1106  case STATE_FLOW_STARTED:
1107  /* This can happen if the flow is starting for a new stream
1108  * (or a new position in the same stream), without notifying
1109  * that the old stream is stopped.
1110  * TODO: Need to think if this is valid
1111  */
1112  break;
1113  default:
1114  TRACE(TERROR, ("Unhandled flow state"));
1115  assert(0);
1116  }
1117  }
1118 }
1119 
1124 static BOOLEAN MHEG5StreamerProcess(void)
1125 {
1126  StreamerRequest *request;
1127 
1128  if (processRequests)
1129  {
1130  /* Set flag to FALSE, but while processing this flag may be
1131  * turned on again.
1132  */
1133  processRequests = FALSE;
1134 
1135  request = requestList;
1136  while (request != NULL)
1137  {
1138  switch (request->downloadState)
1139  {
1140  case STATE_DOWNLOAD_CHECK_ACCESS:
1141  ProgressDownloadRequest(request);
1142  break;
1143 
1144  default:
1145  ;
1146  }
1147 
1148  request = request->next;
1149  }
1150  }
1151  return processRequests;
1152 }
1153 
1163  MHEG5Program *program)
1164 {
1165  StreamerRequest *request;
1166 
1167  request = CreateRequest(TRUE);
1168  if (request != NULL)
1169  {
1170  /* Convert using name mapping rules */
1171  MHEG5stringDestruct(&request->url);
1172  request->url = MHEG5convertGID(url);
1173  if (IsValidUrl(&request->url))
1174  {
1175  DBG(("Measurement URL: %s", request->url.data));
1176 
1177  request->root = &program->ingredient.root;
1178  request->maxBytes = maxBytes;
1179 
1180  STB_OSMutexLock(requestMutex);
1181  AddRequestToList(request);
1182  STB_OSMutexUnlock(requestMutex);
1183 
1184  request->downloadState = STATE_DOWNLOAD_QUEUED;
1185  ProgressDownloadRequest(request);
1186  }
1187  else
1188  {
1189  /* Invalid source, cannot download */
1190  DBG(("MHEG5StreamerMeasurePerformance: Invalid URL"));
1191  request->downloadState = STATE_DOWNLOAD_FAILED;
1192  }
1193  }
1194 }
1195 
1202 {
1203  StreamerRequest *request;
1204 
1205  assert(((MHEG5Root *)program)->clazz == MHEG5RESIDENTPROGRAM);
1206  request = FindRequestByObj(&program->ingredient.root);
1207  if (request != NULL)
1208  {
1209  MHEG5StreamerNotifyStreamingPerformance(request->requestId, -1);
1210  }
1211  else
1212  {
1213  /* Ignore quietly */
1214  TRACE(TERROR, ("Cannot find request"));
1215  }
1216 }
1217 
1224 void MHEG5StreamerSetActiveState(BOOLEAN activeState)
1225 {
1226  StreamerRequest *request;
1227 
1228  /* Find the running request (if any) */
1229  request = requestList;
1230  while (request != NULL)
1231  {
1232  if (request->streamState == STATE_STREAM_RUNNING)
1233  {
1234  break;
1235  }
1236  request = request->next;
1237  }
1238 
1239  /* This only affects streaming output */
1240  if (request != NULL)
1241  {
1242  if (activeState && isIcsSetup)
1243  {
1244  PDBG(("rid=%d",request->requestId));
1245  MHEG5QueueEnableStreaming(request->requestId);
1246  if (request->isPartial)
1247  {
1248  request->downloadState = STATE_DOWNLOAD_QUEUED;
1249  ProgressDownloadRequest(request);
1250  }
1251  }
1252  else
1253  {
1255  }
1256  }
1257 }
1258 
1263 void MHEG5StreamerClose(void)
1264 {
1265  if (streamerBuffer != NULL)
1266  {
1267  /* Stop and delete every request */
1269 
1270  /* Stop the task */
1272 
1273  if (requestMutex != NULL)
1274  {
1275  STB_OSDeleteMutex( requestMutex );
1276  requestMutex = NULL;
1277  }
1278 
1279  /* Terminate queue */
1280  MHEG5QueueTerm();
1281  }
1282 }
1283 
1288 void MHEG5StreamerRefresh(void)
1289 {
1290  StreamerRequest *request;
1291  if (streamerBuffer != NULL)
1292  {
1293  STB_OSMutexLock(requestMutex);
1294  request = requestList;
1295  while (request != NULL)
1296  {
1297  if (request->streamState == STATE_STREAM_RUNNING)
1298  {
1300  request->flowState = STATE_FLOW_STOPPED;
1301  request->outputState = STATE_OUTPUT_STOPPED;
1302  CheckRunCondition(request);
1303  }
1304  request = request->next;
1305  }
1306  STB_OSMutexUnlock(requestMutex);
1307  }
1308 }
1309 
1310 /*****************************************************************************/
1311 /* NOTIFICATIONS FROM MSP MODULE */
1312 /*****************************************************************************/
1313 
1321 {
1322  StreamerRequest *request;
1323 
1324  request = FindRequestById(requestId);
1325  if (request != NULL)
1326  {
1327  /* Stop and destroy the measurement request */
1328  MHEG5StopMeasureRequest(requestId);
1329  MHEG5DestroyMeasureRequest(requestId);
1330 
1331  /* Report measurement to resident program */
1332  MHEG5notifyStreamPerformanceMeasurement(request->root, speed);
1333 
1334  /* Remove request from list and destroy it */
1335  STB_OSMutexLock(requestMutex);
1336  RemoveRequestFromList(request);
1337  STB_OSMutexUnlock(requestMutex);
1338 
1339  DestroyRequest(request);
1340  }
1341 }
1342 
1343 /*****************************************************************************/
1344 /* */
1345 /*****************************************************************************/
1346 
1352 static StreamerRequest* CreateRequest(BOOLEAN isMeasure)
1353 {
1354  StreamerRequest *request;
1355 
1356 #ifndef ERROR_ON_SMALL_ICS_BUFFER
1357  if (streamerBuffer == NULL)
1358  {
1359  TRACE(TERROR, ("Streaming buffer must not be NULL"));
1360  request = NULL;
1361  }
1362  else
1363 #endif
1364  {
1365  request = MHEG5getMem(sizeof *request);
1366  if (request != NULL)
1367  {
1368  /* Request ID is a unique ID */
1369  request->requestId = globalRequestId;
1370  globalRequestId = (globalRequestId % 1073741824) + 1;
1371 
1372  /* Download ID is unknown */
1373  request->downloadId = 0;
1374 
1375  /* Set type */
1376  request->isMeasureType = isMeasure;
1377 
1378  /* Initialise request */
1379  request->paused = TRUE;
1380  request->isPartial = FALSE;
1381  request->pauseRequestCount = 0;
1382  request->contentAvailableNotified = FALSE;
1383  request->contentAvailableGenerated = FALSE;
1384  request->streamPlayingGenerated = FALSE;
1385  request->counterEndPosition = -1;
1386  request->counterMaxPosition = -1;
1387  request->certCount = 0;
1388  request->streamState = STATE_STREAM_STOPPED;
1389  request->downloadState = STATE_DOWNLOAD_STOPPED;
1390  request->outputState = STATE_OUTPUT_STOPPED;
1391  request->flowState = STATE_FLOW_STOPPED;
1392 
1393  ULL_SetInvalid(request->contentLength);
1394  ULL_Set32(request->bytePosition, 0);
1395  ULL_Set32(request->currentBytePosition, 0);
1396  ULL_SetInvalid(request->expectedBytes);
1397  ULL_Set32(request->downloadedBytes, 0);
1398  ULL_Set32(request->presentedBytes, 0);
1399 
1400  request->url.len = 0;
1401  request->url.data = NULL;
1402 
1403  request->redirectUrl.len = 0;
1404  request->redirectUrl.data = NULL;
1405  request->redirectCount = 0;
1406 
1407  /* 0 = unknown, X-BytesPerSecond must be positive */
1408  request->bitrate = 0;
1409 
1410  request->keys.num_pids = 0;
1411  request->keys.pid_info = NULL;
1412  request->keyLocation.len = 0;
1413  request->keyLocation.data = NULL;
1414  request->keyState = STATE_KEY_UNKNOWN;
1415  request->keyDownloadId = 0;
1416 
1417  request->pmtRetrieved = FALSE;
1418  request->triggers = NULL;
1419  request->prepareNumber = globalPrepareNumber;
1420  globalPrepareNumber = (globalPrepareNumber % 1073741824) + 1;
1421  }
1422  }
1423  return request;
1424 }
1425 
1432 static void PrepareStreamingRequest(StreamerRequest *request,
1433  MHEG5Stream *stream)
1434 {
1435  int len;
1436  MHEG5String *ref;
1437  MHEG5String newUrl;
1438  MHEG5Bool sameUrl;
1439 
1440  ref = &stream->ingredient.content.ref.referenced.reference;
1441 
1442  /* Store stream object reference */
1443  request->root = &stream->ingredient.root;
1444 
1445  /* Convert using name mapping rules */
1446  newUrl = MHEG5convertGID(ref);
1447  sameUrl = MHEG5stringEqual(&newUrl, &request->url);
1448  MHEG5stringDestruct( &request->url );
1449  request->url = newUrl;
1450 
1451  /* Clear any previous redirections */
1452  MHEG5stringDestruct(&request->redirectUrl);
1453  request->redirectCount = 0;
1454 
1455  len = request->url.len;
1456  if (len == 0)
1457  {
1458  PDBG(("%d ~ Cannot convert GID", request->requestId));
1459  request->downloadState = STATE_DOWNLOAD_FAILED;
1460  return;
1461  }
1462 
1463  if (IsValidUrl(&request->url))
1464  {
1465  PDBG(("Stream URL: %s, counterPosition %d", request->url.data, stream->counterPosition));
1466 
1467  if (request->streamState == STATE_STREAM_RUNNING)
1468  {
1470  }
1471 
1472  /* Stop current download */
1473  DestroyDownloadRequest(request);
1474 
1475  /* Disable streaming and remove queue items from the queue */
1476  StopContentOutput(request, TRUE);
1477 
1478  /* Counter position is taken from the stream object */
1479  request->bytePosition = ConvertToBytes(request, stream->counterPosition);
1480  request->currentBytePosition = request->bytePosition;
1481  request->counterEndPosition = stream->counterEndPosition;
1482 
1483  request->looping = stream->looping;
1484  request->currentLoop = 0;
1485  request->restart = FALSE;
1486 
1487  if (stream->speed[0] == 0)
1488  {
1489  request->paused = TRUE;
1490  }
1491  else
1492  {
1493  request->paused = FALSE;
1494  }
1495 
1496  switch (request->streamState)
1497  {
1498  case STATE_STREAM_RUNNING:
1499  request->flowState = STATE_FLOW_STOPPED;
1500  request->outputState = STATE_OUTPUT_STOPPED;
1501  break;
1502  case STATE_STREAM_STOPPED:
1503  request->streamState = STATE_STREAM_PREPARED;
1504  /*fall thro*/
1505  case STATE_STREAM_PREPARED:
1506  request->contentAvailableNotified = FALSE;
1507  request->contentAvailableGenerated = FALSE;
1508  break;
1509  }
1510 
1511  /* If it's the same URL, we assume that the PMT and stream
1512  * keys are the same. We also expect another ContentAvailable event
1513  */
1514  if (!sameUrl)
1515  {
1516  request->contentAvailableNotified = FALSE;
1517  request->contentAvailableGenerated = FALSE;
1518 
1519  request->pmtRetrieved = FALSE;
1520  if (request->keys.num_pids > 0)
1521  {
1522  MHEG5freeMem(request->keys.pid_info);
1523  request->keys.num_pids = 0;
1524  request->keys.pid_info = NULL;
1525  }
1526  MHEG5stringDestruct(&request->keyLocation);
1527  request->keyState = STATE_KEY_UNKNOWN;
1528  request->keyDownloadId = 0;
1529  }
1530 
1531  CheckDownloadCondition();
1532  }
1533  else
1534  {
1535  /* Invalid source, cannot download */
1536  DBG(("InitStreamRequest: Invalid source"));
1537  request->downloadState = STATE_DOWNLOAD_FAILED;
1538  }
1539 }
1540 
1547 static void SetCounterPosition(StreamerRequest *request, MHEG5Stream *stream)
1548 {
1549  U64BIT oldpos;
1550  U64BIT newpos;
1551  U64BIT downpos;
1552 
1553  oldpos = request->currentBytePosition;
1554  downpos = request->bytePosition;
1555  ULL_Add(downpos,request->downloadedBytes);
1556 
1557  /* Counter position is taken from the stream object */
1558  newpos = ConvertToBytes(request, stream->counterPosition);
1559  TRACE(TICS,("requestId=%d newBytePos=%d oldBytePos=%d",request->requestId,
1560  ULL_Get32(newpos), ULL_Get32(oldpos) ));
1561 
1562  if (ULL_Compare(newpos,oldpos) > 0 &&
1563  ULL_Compare(newpos,downpos) <= 0)
1564  {
1565  /* flush downloaded and queued data that is no longer required */
1566  TRACE(TICS,("flushing data that is no longer required"));
1567  if (request->streamState == STATE_STREAM_RUNNING)
1568  {
1570  request->flowState = STATE_FLOW_STOPPED;
1571  request->outputState = STATE_OUTPUT_STOPPED;
1572  }
1573  MHEG5QueueReleaseItems(request->requestId,newpos);
1574  request->currentBytePosition = newpos;
1575 
1576  STB_OSMutexLock(requestMutex);
1577  CheckRunCondition(request);
1578  STB_OSMutexUnlock(requestMutex);
1579  }
1580  else if (stream->ingredient.root.runningStatus)
1581  {
1582  TRACE(TICS,("SCP outside of buffered data, restarting download SCOut"));
1583  /* New counter position maps to place outside of the currently buffered data */
1584  if (request->streamState == STATE_STREAM_RUNNING)
1585  {
1587  }
1588  /* remove queue items from the queue */
1589  MHEG5QueueReleaseRequestItems(request->requestId);
1590 
1591  /* Stop current download */
1592  DestroyDownloadRequest(request);
1593 
1594  /* new download request will be at new counter position */
1595  request->bytePosition = newpos;
1596  request->currentBytePosition = newpos;
1597 
1598  request->currentLoop = 0;
1599  request->restart = FALSE;
1600 
1601  PDBG(("streamState %u", request->streamState));
1602  switch (request->streamState)
1603  {
1604  case STATE_STREAM_RUNNING:
1605  request->flowState = STATE_FLOW_STOPPED;
1606  request->outputState = STATE_OUTPUT_STOPPED;
1607  break;
1608 
1609  case STATE_STREAM_STOPPED:
1610  request->streamState = STATE_STREAM_PREPARED;
1611  /*fall thro*/
1612  case STATE_STREAM_PREPARED:
1613  request->contentAvailableNotified = FALSE;
1614  request->contentAvailableGenerated = FALSE;
1615  break;
1616  }
1617 
1618  CheckDownloadCondition();
1619  }
1620 }
1621 
1627 static void AddRequestToList(StreamerRequest *request)
1628 {
1629  StreamerRequest **ppRequest;
1630 
1631  /* Always append, as the order is important for buffering model */
1632  ppRequest = &requestList;
1633  while (*ppRequest != NULL)
1634  {
1635  ppRequest = &(*ppRequest)->next;
1636  }
1637 
1638  *ppRequest = request;
1639  request->next = NULL;
1640 }
1641 
1647 static void RemoveRequestFromList(StreamerRequest *request)
1648 {
1649  StreamerRequest **ppRequest;
1650 
1651  ppRequest = &requestList;
1652  while (*ppRequest != NULL && *ppRequest != request)
1653  {
1654  ppRequest = &(*ppRequest)->next;
1655  }
1656 
1657  if (*ppRequest != NULL)
1658  {
1659  /* Found the request in the list, remove it */
1660  *ppRequest = (*ppRequest)->next;
1661  }
1662 }
1663 
1669 static void CreateDownloadRequest(StreamerRequest *request)
1670 {
1671  assert(request->downloadId == 0);
1672  assert(request->downloadState != STATE_DOWNLOAD_STARTED &&
1673  request->downloadState != STATE_DOWNLOAD_RUNNING);
1674 
1675  request->downloadState = STATE_DOWNLOAD_QUEUED;
1676  ProgressDownloadRequest(request);
1677 }
1678 
1690 static void ProgressDownloadRequest(StreamerRequest *request)
1691 {
1692  MHEG5ServerAccess_t access;
1693  BOOLEAN lastRequest;
1694  char *url;
1695  U64BIT rangeFrom, rangeTo;
1696 
1697  if (request->downloadState == STATE_DOWNLOAD_QUEUED)
1698  {
1699  /* Start from scratch */
1700  PDBG(("%d ~ Progress: STATE_DOWNLOAD_QUEUED", request->requestId));
1701  ULL_Set32(request->downloadedBytes, 0);
1702  ULL_Set32(request->presentedBytes, 0);
1703  request->downloadState = STATE_DOWNLOAD_CHECK_ACCESS;
1704  }
1705 
1706  if (request->downloadState == STATE_DOWNLOAD_CHECK_ACCESS)
1707  {
1708  /* Check server access */
1709  PDBG(("%d ~ Progress: STATE_DOWNLOAD_CHECK_ACCESS", request->requestId));
1710  if (request->redirectCount == 0)
1711  {
1712  access = MHEG5CheckServerPermitted(request->url.data);
1713  if (access == SERVER_ACCESS_BLOCKED)
1714  {
1715  request->downloadState = STATE_DOWNLOAD_FAILED;
1716  GenerateStreamRefError(request);
1717  PDBG(("%d ~ Progress: STATE_DOWNLOAD_FAILED", request->requestId));
1718  }
1719  else if (access == SERVER_ACCESS_PENDING)
1720  {
1721  request->downloadState = STATE_DOWNLOAD_WAIT_ACCESS;
1722  PDBG(("%d ~ Progress: STATE_DOWNLOAD_WAIT_ACCESS", request->requestId));
1723  }
1724  else
1725  {
1726  request->downloadState = STATE_DOWNLOAD_HAS_ACCESS;
1727  }
1728  }
1729  else
1730  {
1731  /* URL obtained by redirection does not need to be checked */
1732  request->downloadState = STATE_DOWNLOAD_HAS_ACCESS;
1733  }
1734  }
1735 
1736  if (request->downloadState == STATE_DOWNLOAD_HAS_ACCESS)
1737  {
1738  /* Check TLS certificates */
1739  PDBG(("%d ~ Progress: STATE_DOWNLOAD_HAS_ACCESS", request->requestId));
1740  CheckTlsCertificates(request);
1741  }
1742 
1743  if (request->downloadState == STATE_DOWNLOAD_HAS_CERT)
1744  {
1745  /* Check redirection */
1746  PDBG(("%d ~ Progress: STATE_DOWNLOAD_HAS_CERT", request->requestId));
1747  if (request->redirectCount > 0)
1748  {
1749  url = (char *)request->redirectUrl.data;
1750  }
1751  else
1752  {
1753  url = (char *)request->url.data;
1754  }
1755 
1756  if (request->isPartial)
1757  {
1758  assert(request->downloadId != 0);
1759  MHEG5RenewDownloadRequest(request->downloadId,url);
1760  request->isPartial = FALSE;
1761  }
1762  else
1763  {
1764  /* Check last request -- depends on looping attribute */
1765  if (request->currentLoop == request->looping - 1)
1766  {
1767  lastRequest = TRUE;
1768  }
1769  else
1770  {
1771  lastRequest = FALSE;
1772  }
1773 
1774  rangeFrom = request->bytePosition;
1775  ULL_SetInvalid(rangeTo);
1776  if (!request->isMeasureType)
1777  {
1778  request->downloadId = MHEG5CreateDownloadRequest(
1779  request->requestId, url, rangeFrom, rangeTo, lastRequest);
1780  }
1781  else
1782  {
1783  request->downloadId = MHEG5CreateMeasureRequest(
1784  request->requestId, url, request->maxBytes);
1785  }
1786  }
1787 
1788  if (request->downloadId != 0)
1789  {
1790  request->downloadState = STATE_DOWNLOAD_STARTED;
1791  if (!request->isMeasureType)
1792  {
1793  MHEG5StartDownloadRequest(request->downloadId);
1794  }
1795  else
1796  {
1797  MHEG5StartMeasureRequest(request->downloadId);
1798  }
1799  }
1800  else
1801  {
1802  request->downloadState = STATE_DOWNLOAD_FAILED;
1803  }
1804  }
1805 }
1806 
1817 static void CheckTlsCertificates(StreamerRequest *request)
1818 {
1819  U32BIT certCount;
1821  U8BIT *url;
1822 
1823  if (request->redirectCount == 0)
1824  {
1825  url = request->url.data;
1826  }
1827  else
1828  {
1829  url = request->redirectUrl.data;
1830  }
1831 
1832  if (MHEG5strncmp(url, "https://", 8) == 0)
1833  {
1834  /* Check TLS certificates */
1835  request->downloadState = STATE_DOWNLOAD_WAIT_CERT;
1836 
1837  certCount = MHEG5GetTlsCertStoreCount();
1838  if (certCount == 0 || request->certCount == certCount)
1839  {
1840  /* Either no certificates, or no new certificates - request
1841  * next certificate
1842  */
1843  status = MHEG5GetNextTlsCertificate();
1844  if (status == MHEG5_TLS_CERT_REQUEST_SUCCESS)
1845  {
1846  /* New certificates obtained (synchronously) */
1847  request->certCount = MHEG5GetTlsCertStoreCount();
1848  request->downloadState = STATE_DOWNLOAD_HAS_CERT;
1849  }
1850  else if (status == MHEG5_TLS_CERT_REQUEST_FAILURE)
1851  {
1852  request->downloadState = STATE_DOWNLOAD_FAILED;
1853  GenerateStreamRefError(request);
1854  }
1855  }
1856  else if (request->certCount < certCount)
1857  {
1858  /* No need to request new certificates */
1859  request->certCount = certCount;
1860  request->downloadState = STATE_DOWNLOAD_HAS_CERT;
1861  }
1862  }
1863  else
1864  {
1865  /* HTTP requests don't need to check certificates */
1866  request->downloadState = STATE_DOWNLOAD_HAS_CERT;
1867  }
1868 }
1869 
1875 static void DestroyDownloadRequest(StreamerRequest *request)
1876 {
1877  MHEG5StopDownloadRequest(request->downloadId);
1878  MHEG5DestroyDownloadRequest(request->downloadId);
1879 
1880  request->downloadState = STATE_DOWNLOAD_STOPPED;
1881  request->downloadId = 0;
1882  request->certCount = 0;
1883 }
1884 
1890 static void DestroyMeasurementRequest(StreamerRequest *request)
1891 {
1892  MHEG5StopMeasureRequest(request->downloadId);
1893  MHEG5DestroyMeasureRequest(request->downloadId);
1894 
1895  request->downloadState = STATE_DOWNLOAD_STOPPED;
1896  request->downloadId = 0;
1897  request->certCount = 0;
1898 }
1899 
1905 static void DestroyRequest(StreamerRequest *request)
1906 {
1907  CounterTrigger *trigger, *next;
1908 
1909  DestroyDownloadRequest(request);
1910  MHEG5stringDestruct(&request->url);
1911  MHEG5stringDestruct(&request->redirectUrl);
1912 
1913  trigger = request->triggers;
1914  while (trigger != NULL)
1915  {
1916  next = trigger->next;
1917  MHEG5freeMem(trigger);
1918  trigger = next;
1919  }
1920  request->triggers = NULL;
1921 
1922  if (request->keys.pid_info != NULL)
1923  {
1924  MHEG5freeMem(request->keys.pid_info);
1925  }
1926  MHEG5stringDestruct(&request->keyLocation);
1927 
1928  MHEG5freeMem(request);
1929 }
1930 
1938 static StreamerRequest* FindRequestById(U32BIT requestId)
1939 {
1940  StreamerRequest *request;
1941 
1942  request = requestList;
1943 
1944  while (request != NULL && request->requestId != requestId)
1945  {
1946  request = request->next;
1947  }
1948 
1949  return request;
1950 }
1951 
1957 static StreamerRequest* FindRequestByObj(MHEG5Root *root)
1958 {
1959  StreamerRequest *request;
1960 
1961  request = requestList;
1962  while ((request != NULL) && (request->root != root))
1963  {
1964  request = request->next;
1965  }
1966 
1967  return request;
1968 }
1969 
1975 static StreamerRequest* FindRequestByDownloadId(U32BIT downloadId)
1976 {
1977  StreamerRequest *request;
1978 
1979  request = requestList;
1980 
1981  while (request != NULL && request->downloadId != downloadId)
1982  {
1983  request = request->next;
1984  }
1985 
1986  return request;
1987 }
1988 
2003 static void ProcessHttpHeaders(U32BIT downloadId,S32BIT code)
2004 {
2005  StreamerRequest *request;
2006  U32BIT units;
2007 
2008  STB_OSMutexLock(requestMutex);
2009 
2010  request = FindRequestByDownloadId(downloadId);
2011  if (request != NULL)
2012  {
2013  PDBG(("%d ~ response code: %d", request->requestId, code));
2014 
2015  if (code == 200 || code == 206)
2016  {
2017  if (code == 200)
2018  {
2019  /* Range header is not supported, data is returned from the
2020  * start of the stream. This means that CounterPosition is 0.
2021  */
2022  ULL_Set32(request->currentBytePosition, 0);
2023  }
2024 
2025  DBG(("request %d download state %s", request->requestId,
2026  REQUEST_DOWNLOAD_STATE_STR(request)));
2027 
2028  /*assert(request->downloadState == STATE_DOWNLOAD_STARTED);*/
2029  if (request->downloadState != STATE_DOWNLOAD_STARTED)
2030  {
2031  PDBG(("%d ~ WARNING: Incorrect download state, overridden",
2032  request->requestId));
2033  }
2034  request->downloadState = STATE_DOWNLOAD_RUNNING;
2035 
2036  /* Copy bitrate and index file (if any) */
2037  MHEG5CopyDownloadBitrate(request->downloadId, &request->bitrate);
2038 
2039  /* Calculate expected number of units (if possible) */
2040  request->contentLength =
2041  MHEG5GetDownloadContentLength(request->downloadId,
2042  request->contentLength);
2043  PDBG(("%d ~ contentLength:", request->requestId));
2044  ULL_Print(request->contentLength);
2045  if (ULL_IsValid(request->contentLength))
2046  {
2047  /* Convert bytes to units */
2048  units = ConvertToUnits(request, request->contentLength);
2049  request->counterMaxPosition = units;
2050  PDBG(("%d ~ counterMaxPosition = units = %d",
2051  request->requestId, units));
2052  CalculateExpectedBytes(request);
2053  }
2054 
2055  /* Copy stream keys and locations (if any) */
2056  MHEG5CopyDownloadStreamKeyInfo(request->downloadId, &request->keys,
2057  &request->keyLocation);
2058  HandleStreamKeys(request);
2059 
2060  /* It may be possible to start streaming, if enough data
2061  * has already been buffered.
2062  */
2063  CheckRunCondition(request);
2064  }
2065  else
2066  {
2067  /* Anything else is handled when the download request is done */
2068  }
2069  }
2070 
2071  STB_OSMutexUnlock(requestMutex);
2072 }
2073 
2080 static void CalculateExpectedBytes(StreamerRequest *request)
2081 {
2082  U64BIT endPosition, counterEndPosition;
2083 
2084  ULL_SetInvalid(request->expectedBytes);
2085 
2086  /* Check end position of stream, if known */
2087  ULL_SetInvalid(endPosition);
2088  if (ULL_IsValid(request->contentLength))
2089  {
2090  /* Initialise to content length */
2091  PDBG(("%d ~ contentLength is valid", request->requestId));
2092  endPosition = request->contentLength;
2093  if (request->counterEndPosition > 0)
2094  {
2095  PDBG(("%d ~ counterEndPosition > 0", request->requestId));
2096  counterEndPosition = ConvertToBytes(request, request->counterEndPosition);
2097  if (ULL_Compare(counterEndPosition, endPosition) < 0)
2098  {
2099  /* CounterEndPosition is before the end of the stream */
2100  PDBG(("%d ~ counterEndPosition < counterMaxPosition",
2101  request->requestId));
2102  endPosition = counterEndPosition;
2103  }
2104  }
2105  }
2106 
2107  PDBG(("%d ~ endPosition is valid: %s\n", request->requestId,
2108  ULL_IsValid(endPosition) ? "TRUE" : "FALSE"));
2109  if (ULL_IsValid(endPosition))
2110  {
2111  PDBG(("endPosition(%u) - bytePosition(%u)", ULL_Get32(endPosition), ULL_Get32(request->bytePosition)));
2112  request->expectedBytes = endPosition;
2113  ULL_Sub(request->expectedBytes, request->bytePosition);
2114  PDBG(("%d ~ request->expectedBytes = ", request->requestId));
2115  ULL_Print(request->expectedBytes);
2116  }
2117 }
2118 
2124 static void HandleStreamKeys(StreamerRequest *request)
2125 {
2126  /* Only load keys file if they are not available directly from the
2127  * headers.
2128  */
2129  if ((request->keyState == STATE_KEY_UNKNOWN) &&
2130  (request->keys.num_pids == 0) &&
2131  (request->keyLocation.len > 0))
2132  {
2133  request->keyState = STATE_KEY_PENDING;
2134 
2135  /* donwloadId is used to make sure the file is for the right download.
2136  * A key is associated with a specific download (but should remain
2137  * valid for future downloads unless the stream is prepared again).
2138  */
2139  request->keyDownloadId = request->downloadId;
2140  (void)MHEG5FileOrmGet(request->keyLocation,
2142  (void *)request->root,
2143  StreamKeyLoadedCallback,
2144  StreamKeyLoadFailCallback);
2145  }
2146  else
2147  {
2148  request->keyState = STATE_KEY_VALID;
2149  }
2150 }
2151 
2157 static void HandleDownloadFinished(U32BIT downloadId,E_HttpStatus status,S32BIT code)
2158 {
2159  StreamerRequest *request;
2160 
2161  request = FindRequestByDownloadId(downloadId);
2162  if (request == NULL)
2163  {
2164  PDBG(("Cannot find request from download id %d - already gone",downloadId));
2165  }
2166  else
2167  {
2168  if (status == HTTP_STATUS_OK)
2169  {
2170  if (code == 301)
2171  {
2172  /* Permanent redirection */
2173  HandleDownloadRedirection(request, TRUE);
2174  }
2175  else if ((code > 301) && (code <= 307) && (code != 306))
2176  {
2177  /* Temporary redirection */
2178  HandleDownloadRedirection(request, FALSE);
2179  }
2180  else if ((code == 200) || (code == 206))
2181  {
2182  /* Success */
2183  HandleDownloadSuccess(request);
2184  }
2185  else
2186  {
2187  PDBG(("%d ~ HTTP_STATUS_OK code=%d", request->requestId,code));
2188  #ifdef INCLUDE_FREESAT
2189  if ((code == 401) || (code == 403) || (code == 407))
2190  {
2191  GenerateIPStreamAccessError(request);
2192  }
2193  else
2194  {
2195  GenerateStreamRefError(request);
2196  }
2197  #else /* INCLUDE_FREESAT */
2198  GenerateStreamRefError(request);
2199  #endif /* INCLUDE_FREESAT */
2200  }
2201  }
2202  else if (status == HTTP_STATUS_PARTIAL)
2203  {
2204  PDBG(("%d ~ HTTP_STATUS_PARTIAL", request->requestId));
2205  HandleDownloadPartial(request,downloadId);
2206  }
2207  else if (status == HTTP_STATUS_SSL_ERROR)
2208  {
2209  PDBG(("%d ~ HTTP_STATUS_SSL_ERROR", request->requestId));
2210  request->downloadState = STATE_DOWNLOAD_QUEUED;
2211  ProgressDownloadRequest(request);
2212  }
2213  else
2214  {
2215  PDBG(("%d ~ Network error, timeout or other", request->requestId));
2216  /* Network error, timeout or other - failure */
2217  /*request->state = STATE_FAILED;*/
2218  request->downloadState = STATE_DOWNLOAD_FAILED;
2219  GenerateStreamRefError(request);
2220  }
2221  }
2222 
2223  CheckDownloadCondition();
2224 }
2225 
2232 static void HandleDownloadRedirection(StreamerRequest *request,
2233  BOOLEAN permanent)
2234 {
2235  MHEG5String redirectUrl;
2236 
2237  if (request->redirectCount < MAX_REDIRECTS)
2238  {
2239  redirectUrl.data = MHEG5GetDownloadRedirect(request->downloadId);
2240  if (redirectUrl.data != NULL)
2241  {
2242  MHEG5CopyDownloadBitrate(request->downloadId, &request->bitrate);
2243  MHEG5CopyDownloadStreamKeyInfo(request->downloadId, &request->keys,
2244  &request->keyLocation);
2245 
2246  MHEG5stringDestruct(&request->redirectUrl);
2247  redirectUrl.len = strlen((char *)redirectUrl.data);
2248  if (permanent)
2249  {
2250  MHEG5stringDestruct(&request->url);
2251  request->url = MHEG5convertGID(&redirectUrl);
2252  }
2253 
2254  request->redirectUrl = MHEG5convertGID(&redirectUrl);
2255  ++request->redirectCount;
2256 
2257  DestroyDownloadRequest(request);
2258 
2259  request->downloadState = STATE_DOWNLOAD_HAS_ACCESS;
2260  ProgressDownloadRequest(request);
2261  }
2262  else
2263  {
2264  PDBG(("%d ~ Cannot find redirection URL", request->requestId));
2265  request->downloadState = STATE_DOWNLOAD_FAILED;
2266  GenerateStreamRefError(request);
2267  }
2268  }
2269  else
2270  {
2271  PDBG(("%d ~ Too many redirections!", request->requestId));
2272  request->downloadState = STATE_DOWNLOAD_FAILED;
2273  GenerateStreamRefError(request);
2274  }
2275 }
2276 
2282 static void HandleDownloadSuccess(StreamerRequest *request)
2283 {
2284  DestroyDownloadRequest(request);
2285 
2286  if (request->looping == 0 || request->currentLoop < request->looping - 1)
2287  {
2288  PDBG(("%d ~ Looping: current loop %d out of %d",
2289  request->requestId, request->currentLoop, request->looping));
2290  ++request->currentLoop;
2291  assert(request->downloadState == STATE_DOWNLOAD_STOPPED);
2292  CreateDownloadRequest(request);
2293  }
2294  else
2295  {
2296  request->downloadState = STATE_DOWNLOAD_DONE;
2297  }
2298 
2299  /* If the request is waiting for a "DOWNLOAD_DONE", it needs to
2300  * be released from this state
2301  */
2302  STB_OSMutexLock(requestMutex);
2303  CheckRunCondition(request);
2304  STB_OSMutexUnlock(requestMutex);
2305 }
2306 
2313 static void HandleDownloadPartial(StreamerRequest *request,U32BIT downloadId)
2314 {
2315  U64BIT position;
2316  BOOLEAN needsRestart;
2317  needsRestart = MHEG5DownloadPositionPartial(downloadId,&position);
2318  if (needsRestart)
2319  {
2320  PDBG(("%d ~ restart download",request->requestId));
2321 
2322  MHEG5ClearDownloadRequest(downloadId);
2323  request->bytePosition = position;
2324  request->isPartial = TRUE;
2325  request->certCount = 0;
2327  {
2328  request->downloadState = STATE_DOWNLOAD_QUEUED;
2329  ProgressDownloadRequest(request);
2330  }
2331  }
2332  else
2333  {
2334  DestroyDownloadRequest(request);
2335  request->downloadState = STATE_DOWNLOAD_DONE;
2336  }
2337  STB_OSMutexLock(requestMutex);
2338  CheckRunCondition(request);
2339  STB_OSMutexUnlock(requestMutex);
2340 }
2341 
2347 static void HandleMeasurementFinished(U32BIT downloadId)
2348 {
2349  StreamerRequest *request;
2350  S32BIT status;
2351  S32BIT code;
2352 
2353  request = FindRequestByDownloadId(downloadId);
2354  if (request != NULL)
2355  {
2356  status = MHEG5GetMeasureStatus(downloadId);
2357  if (status == HTTP_STATUS_OK)
2358  {
2359  code = MHEG5GetMeasureResponseCode(downloadId);
2360  if ((code >= 301) && (code <= 307) && (code != 306))
2361  {
2362  /* Redirection */
2363  HandleMeasurementRedirection(request);
2364  }
2365  else
2366  {
2367  /* Success */
2368  MHEG5ProcessMeasureRequest(downloadId);
2369  }
2370  }
2371  else if (status == HTTP_STATUS_SSL_ERROR)
2372  {
2373  PDBG(("%d ~ HTTP_STATUS_SSL_ERROR", request->requestId));
2374  request->downloadState = STATE_DOWNLOAD_QUEUED;
2375  ProgressDownloadRequest(request);
2376  }
2377  else
2378  {
2379  PDBG(("%d ~ Network error, timeout or other", request->requestId));
2380  /* Network error, timeout or other - failure */
2381  request->downloadState = STATE_DOWNLOAD_FAILED;
2382  MHEG5ProcessMeasureRequest(downloadId);
2383  }
2384  }
2385  else
2386  {
2387  /* Cannot find request, measurement request must already be gone */
2388  }
2389 }
2390 
2396 static void HandleMeasurementRedirection(StreamerRequest *request)
2397 {
2398  MHEG5String redirectUrl;
2399 
2400  if (request->redirectCount < MAX_REDIRECTS)
2401  {
2402  redirectUrl.data = MHEG5GetMeasureRedirect(request->downloadId);
2403  if (redirectUrl.data != NULL)
2404  {
2405  MHEG5stringDestruct(&request->redirectUrl);
2406  redirectUrl.len = strlen((char *)redirectUrl.data);
2407  request->redirectUrl = MHEG5convertGID(&redirectUrl);
2408  ++request->redirectCount;
2409 
2410  DestroyMeasurementRequest(request);
2411 
2412  request->downloadState = STATE_DOWNLOAD_HAS_ACCESS;
2413  ProgressDownloadRequest(request);
2414  }
2415  else
2416  {
2417  PDBG(("%d ~ Cannot find redirection URL", request->requestId));
2418  request->downloadState = STATE_DOWNLOAD_FAILED;
2419  MHEG5ProcessMeasureRequest(request->downloadId);
2420  }
2421  }
2422  else
2423  {
2424  PDBG(("%d ~ Too many redirections!", request->requestId));
2425  request->downloadState = STATE_DOWNLOAD_FAILED;
2426  MHEG5ProcessMeasureRequest(request->downloadId);
2427  }
2428 }
2429 
2436 {
2437  StreamerRequest *request;
2438 
2439  STB_OSMutexLock(requestMutex);
2440 
2441  request = requestList;
2442  while (request != NULL)
2443  {
2444  if (request->outputState == STATE_OUTPUT_PENDING)
2445  {
2446  /* This must be done before output state is modified,
2447  * otherwise the run condition may be checked by other
2448  * tasks before the components are setup.
2449  */
2450  //SetupComponents(request);
2451 
2452  request->outputState = STATE_OUTPUT_SETUP;
2453  CheckRunCondition(request);
2454  break;
2455  }
2456 
2457  request = request->next;
2458  }
2459 
2460  STB_OSMutexUnlock(requestMutex);
2461 }
2462 
2468 static void HandleStreamPlaying(U32BIT requestId)
2469 {
2470  StreamerRequest *request;
2471 
2472  request = FindRequestById(requestId);
2473  if (request != NULL)
2474  {
2475  switch (request->flowState)
2476  {
2477  case STATE_FLOW_STARTED:
2478  request->flowState = STATE_FLOW_RUNNING;
2479  GenerateStreamPlaying(request);
2480  break;
2481  case STATE_FLOW_UNDERFLOW:
2482  request->flowState = STATE_FLOW_RUNNING;
2483  GenerateStreamUnderflowResume();
2484  break;
2485  case STATE_FLOW_RUNNING:
2486  /* Multiple components, playback resumed or confused decoders */
2487  DBG(("Ignoring StreamStarted in STATE_FLOW_RUNNING"));
2488  break;
2489  case STATE_FLOW_STOPPED:
2490  PDBG(("%d ~ WARNING: Stream started when flow is stopped",
2491  request->requestId));
2492  break;
2493  default:
2494  TRACE(TERROR, ("Unhandled flow state"));
2495  assert(0);
2496  break;
2497  }
2498  }
2499 }
2500 
2506 static void HandleStreamStopped(U32BIT requestId)
2507 {
2508  StreamerRequest *request;
2509 
2510  request = FindRequestById(requestId);
2511  if (request != NULL)
2512  {
2513  PDBG(("%d ~ request->flowState = %s", request->requestId,
2514  REQUEST_FLOW_STATE_STR(request)));
2515  switch (request->flowState)
2516  {
2517  case STATE_FLOW_RUNNING:
2518  PDBG(("%d ~ request->downloadState = %s", request->requestId,
2519  REQUEST_DOWNLOAD_STATE_STR(request)));
2520  if (request->downloadState == STATE_DOWNLOAD_RUNNING)
2521  {
2522  request->flowState = STATE_FLOW_UNDERFLOW;
2523  GenerateStreamUnderflow();
2524  }
2525  else if (request->downloadState == STATE_DOWNLOAD_DONE)
2526  {
2527  request->flowState = STATE_FLOW_STOPPED;
2528  GenerateStreamStopped(request);
2529  }
2530  else if (request->downloadState == STATE_DOWNLOAD_STARTED)
2531  {
2532  /* Ignore, it's not the same stream */
2533  }
2534  else
2535  {
2536  TRACE(TERROR, ("Unhandled download state"));
2537  assert(0);
2538  }
2539  break;
2540  case STATE_FLOW_STOPPED:
2541  /* Expected underflow, ignore */
2542  DBG(("Expected underflow in %s", REQUEST_FLOW_STATE_STR(request)));
2543  break;
2544  case STATE_FLOW_UNDERFLOW:
2545  /* Spurious notification */
2546  break;
2547  case STATE_FLOW_STARTED:
2548  /* This can happen if the flow is starting for a new stream
2549  * (or a new position in the same stream), without notifying
2550  * that the old stream is stopped.
2551  * TODO: Need to think if this is valid
2552  */
2553  break;
2554  default:
2555  TRACE(TERROR, ("Unhandled flow state"));
2556  assert(0);
2557  }
2558  }
2559 }
2560 
2567 static void HandleCounterTrigger(U32BIT requestId, S32BIT triggerId)
2568 {
2569  StreamerRequest *request;
2570 
2571  request = FindRequestById(requestId);
2572  if (request != NULL)
2573  {
2574  GenerateCounterTrigger(request, triggerId);
2575  }
2576 }
2577 
2583 static void HandleContentAvailable(U32BIT requestId)
2584 {
2585  StreamerRequest *request;
2586 
2587  request = FindRequestById(requestId);
2588  if (request != NULL)
2589  {
2590  GenerateContentAvailable(request);
2591  }
2592 }
2593 
2599 static void GenerateStreamRefError(StreamerRequest *request)
2600 {
2601  MHEG5Root *app;
2602 
2603  if (request->root->runningStatus)
2604  {
2606  if (app != NULL)
2607  {
2608  PDBG(("%d ~ Generating StreamRefError EngineEvent",
2609  request->requestId));
2610  MHEG5sendEvent(app, MHEG5ENGINEEVENT, EE_IC_STREAM_REF_ERROR);
2611  }
2612  }
2613 }
2614 
2615 #ifdef INCLUDE_FREESAT
2616 
2621 static void GenerateIPStreamAccessError(StreamerRequest *request)
2622 {
2623  MHEG5Root *app;
2624 
2625  if (request->root->runningStatus)
2626  {
2628  if (app != NULL)
2629  {
2630  PDBG(("%d ~ Generating IPStreamAccessError EngineEvent",
2631  request->requestId));
2632  MHEG5sendEvent(app, MHEG5ENGINEEVENT, EE_IP_STREAM_ACCESS_ERROR);
2633  }
2634  }
2635 }
2636 
2637 #endif /* INCLUDE_FREESAT */
2638 
2639 
2644 static void GenerateStreamUnderflow(void)
2645 {
2646  MHEG5Root *app;
2647 
2649  if (app != NULL)
2650  {
2651  PDBG(("Generating StreamUnderflow EngineEvent"));
2652  MHEG5sendEvent(app, MHEG5ENGINEEVENT, EE_IC_STREAM_UNDERFLOW);
2653  }
2654 }
2655 
2660 static void GenerateStreamUnderflowResume(void)
2661 {
2662  MHEG5Root *app;
2663 
2665  if (app != NULL)
2666  {
2667  PDBG(("Generating StreamUnderflowResume EngineEvent"));
2668  MHEG5sendEvent(app, MHEG5ENGINEEVENT, EE_IC_STREAM_UNDERFLOW_RESUME);
2669  }
2670 }
2671 
2676 static void GenerateContentAvailable(StreamerRequest *request)
2677 {
2678  if (!request->contentAvailableGenerated)
2679  {
2680  /* Generate event */
2681  PDBG(("%d ~ Generating ContentAvailable for (%.*s %ld) [%d]",
2682  request->requestId,
2683  (int)request->root->grp->groupName.len,
2684  request->root->grp->groupName.data, request->root->id,
2685  request->requestId));
2686  request->contentAvailableGenerated = TRUE;
2687  MHEG5sendEvent(request->root, MHEG5CONTENTAVAILABLE, 0);
2688  }
2689 }
2690 
2695 static void GenerateStreamPlaying(StreamerRequest *request)
2696 {
2697  if (!request->streamPlayingGenerated)
2698  {
2699  /* Generate event */
2700  PDBG(("%d ~ Generating StreamPlaying for (%.*s %ld)",
2701  request->requestId,
2702  (int)request->root->grp->groupName.len,
2703  request->root->grp->groupName.data, request->root->id));
2704  request->streamPlayingGenerated = TRUE;
2705  MHEG5sendEvent(request->root, MHEG5STREAMPLAYING, 0);
2706  }
2707 }
2708 
2714 static void GenerateStreamStopped(StreamerRequest *request)
2715 {
2716  if (request->streamPlayingGenerated)
2717  {
2718  /* Generate event */
2719  PDBG(("%d ~ Generating StreamStopped for (%.*s %ld)",
2720  request->requestId, (int)request->root->grp->groupName.len,
2721  request->root->grp->groupName.data, request->root->id));
2722  if (request->terminateStreamSetup && isIcsSetup)
2723  {
2725  isIcsSetup = FALSE;
2726  }
2727  request->streamPlayingGenerated = FALSE;
2728  MHEG5sendEvent(request->root, MHEG5STREAMSTOPPED, 0);
2729  }
2730 }
2731 
2738 static void GenerateCounterTrigger(StreamerRequest *request, S32BIT triggerId)
2739 {
2740  /* Generate event */
2741  PDBG(("%d ~ Generating CounterTrigger for (%.*s %ld), triggerId = %d",
2742  request->requestId, (int)request->root->grp->groupName.len,
2743  request->root->grp->groupName.data, request->root->id, triggerId));
2744  MHEG5sendEvent(request->root, MHEG5COUNTERTRIGGER, triggerId);
2745 }
2746 
2751 static void GenerateKeyFileError(void)
2752 {
2753  MHEG5Root *app;
2754 
2756  if (app != NULL)
2757  {
2758  PDBG(("Generating KeyFileError EngineEvent"));
2759  MHEG5sendEvent(app, MHEG5ENGINEEVENT, EE_IC_KEY_FILE_ERROR);
2760  }
2761 }
2762 
2771 static void NotifyContentAvailable(U32BIT requestId)
2772 {
2773  MHEG5eventMessage_t event;
2774  E_MhegErr cqu_err;
2775 
2777  event.data_type = DT_VALUE;
2778  event.data.streamer.requestId = requestId;
2779  event.data.streamer.eventType = MHEG5_STREAMER_EVENT_CONTENT_AVAILABLE;
2780 
2781  PDBG(("%d ~ Sending MHEG5_STREAMER_EVENT_CONTENT_AVAILABLE", requestId));
2782  cqu_err = VQ_PutMsg(&event, PRTY_NORMAL);
2783  if (cqu_err != MHERR_OK)
2784  {
2785  TRACE(TERROR, ("Cannot generate event %d", cqu_err));
2786  }
2787 }
2788 
2795 static void NotifyStreamPlaying(U32BIT requestId)
2796 {
2797  MHEG5eventMessage_t event;
2798  E_MhegErr cqu_err;
2799 
2801  event.data_type = DT_VALUE;
2802  event.data.streamer.requestId = requestId;
2803  event.data.streamer.eventType = MHEG5_STREAMER_EVENT_TASK_STREAM_PLAYING;
2804 
2805  PDBG(("%d ~ Sending MHEG5_STREAMER_EVENT_TASK_STREAM_PLAYING", requestId));
2806  cqu_err = VQ_PutMsg(&event, PRTY_NORMAL);
2807  if (cqu_err != MHERR_OK)
2808  {
2809  TRACE(TERROR, ("Cannot generate event %d", cqu_err));
2810  }
2811 }
2812 
2819 static void NotifyStreamStopped(U32BIT requestId)
2820 {
2821  MHEG5eventMessage_t event;
2822  E_MhegErr cqu_err;
2823 
2825  event.data_type = DT_VALUE;
2826  event.data.streamer.requestId = requestId;
2827  event.data.streamer.eventType = MHEG5_STREAMER_EVENT_TASK_STREAM_STOPPED;
2828 
2829  PDBG(("%d ~ Sending MHEG5_STREAMER_EVENT_TASK_STREAM_STOPPED", requestId));
2830  cqu_err = VQ_PutMsg(&event, PRTY_NORMAL);
2831  if (cqu_err != MHERR_OK)
2832  {
2833  TRACE(TERROR, ("Cannot generate event %d", cqu_err));
2834  }
2835 }
2836 
2846 static void ItemInsertCallback(U32BIT downloadId, U64BIT base,
2847  U64BIT position, U32BIT len, BOOLEAN last)
2848 {
2849  StreamerRequest *request;
2850 
2851  STB_OSMutexLock(requestMutex);
2852 
2853  request = FindRequestByDownloadId(downloadId);
2854  if (request != NULL)
2855  {
2856  assert(ULL_IsEqual(request->bytePosition, base));
2857 
2858  request->downloadedBytes = position;
2859  ULL_Add32(request->downloadedBytes, len);
2860 
2861  if (!request->contentAvailableNotified)
2862  {
2863  /* Item buffered, ContentAvailable not generated yet */
2864  request->contentAvailableNotified = TRUE;
2865  NotifyContentAvailable(request->requestId);
2866  }
2867 
2868  /* Check if the PMT is available */
2869  if (!request->pmtRetrieved)
2870  {
2871  request->pmtRetrieved = MHEG5GetDownloadPmt(request->downloadId,
2872  request->pmt);
2873  }
2874 
2875  /* Check termination (max/end position) */
2876  if ((ULL_IsValid(request->expectedBytes)) &&
2877  (ULL_Compare(request->downloadedBytes,
2878  request->expectedBytes) >= 0))
2879  {
2880  MHEG5StopDownloadRequestAsync(request->downloadId);
2881  }
2882 
2883  /* It may be possible to start streaming, if enough data
2884  * has already been buffered.
2885  */
2886  CheckRunCondition(request);
2887  }
2888 
2889  STB_OSMutexUnlock(requestMutex);
2890 }
2891 
2901 static void ItemReleaseCallback(U32BIT requestId, U64BIT base,
2902  U64BIT position, U32BIT len, BOOLEAN last)
2903 {
2904  StreamerRequest *request;
2905 
2906  STB_OSMutexLock(requestMutex);
2907 
2908  request = FindRequestById(requestId);
2909  if (request != NULL)
2910  {
2911  DBG(("%d ~ request->outputState = %s", request->requestId,
2912  REQUEST_OUTPUT_STATE_STR(request)));
2913  if (request->outputState == STATE_OUTPUT_READY)
2914  {
2915  /* Stream started playing */
2916  request->outputState = STATE_OUTPUT_RUNNING;
2917  NotifyStreamPlaying(request->requestId);
2918  }
2919  else if (request->outputState == STATE_OUTPUT_STARVED)
2920  {
2921  /* Stream started playing */
2922  request->outputState = STATE_OUTPUT_RUNNING;
2923  NotifyStreamPlaying(request->requestId);
2924  }
2925  if (request->outputState == STATE_OUTPUT_RUNNING)
2926  {
2927  request->currentBytePosition = base;
2928  ULL_Add(request->currentBytePosition, position);
2929  ULL_Add32(request->currentBytePosition, len);
2930 
2931  request->presentedBytes = position;
2932  ULL_Add32(request->presentedBytes, len);
2933 
2934  /* Check triggers in for any position covered by this block */
2935  CheckCounterTriggers(request, base, position, len);
2936  }
2937  }
2938  STB_OSMutexUnlock(requestMutex);
2939 }
2940 
2949 static void CheckCounterTriggers(StreamerRequest *request, U64BIT base,
2950  U64BIT position, U32BIT len)
2951 {
2952  CounterTrigger *trigger;
2953  MHEG5eventMessage_t event;
2954  E_MhegErr cqu_err;
2955  U64BIT itemPosition;
2956  U64BIT itemLimit;
2957  U64BIT triggerPosition;
2958 
2959  itemPosition = base;
2960  ULL_Add(itemPosition, position);
2961 
2962  itemLimit = itemPosition;
2963  ULL_Add32(itemLimit, len);
2964 
2965  trigger = request->triggers;
2966  while (trigger != NULL)
2967  {
2968  triggerPosition = ConvertToBytes(request, trigger->position);
2969  if ((ULL_Compare(triggerPosition, itemPosition) >= 0) &&
2970  (ULL_Compare(triggerPosition, itemLimit) < 0))
2971  {
2972  /* Generate trigger event */
2973 
2974  event.proc_msg_func = (F_MSG_PROCESS)MHEG5StreamerHandle;
2975  event.data_type = DT_VALUE;
2976  event.data.streamer.requestId = request->requestId;
2977  event.data.streamer.eventType = MHEG5_STREAMER_EVENT_COUNTER_TRIGGER;
2978  event.data.streamer.triggerId = trigger->id;
2979 
2980  PDBG(("%d ~ Sending MHEG5_STREAMER_EVENT_COUNTER_TRIGGER",
2981  request->requestId));
2982  cqu_err = VQ_PutMsg(&event, PRTY_NORMAL);
2983  if (cqu_err != MHERR_OK)
2984  {
2985  TRACE(TERROR, ("Cannot generate event %d", cqu_err));
2986  }
2987  }
2988  trigger = trigger->next;
2989  }
2990 }
2991 
2998 static void SetupComponents(StreamerRequest *request,S_ICSComponents *components)
2999 {
3000  MHEG5Stream *stream;
3001  MHEG5Ingredient *item;
3002  MHEG5Video *video;
3003  MHEG5Audio *audio;
3004  MHEG5Int index;
3005 
3006  components->audio_active = FALSE;
3007  components->audio_component_tag = -1;
3008  components->video_active = FALSE;
3009  components->video_component_tag = -1;
3010  components->video_termination = MHEG5_VIDEO_TERMINATION_DISAPPEAR;
3011 
3012  stream = (MHEG5Stream *)request->root;
3013  for (index = 0; index != MAX_MLTPLX; index++)
3014  {
3015  item = stream->multiplex[index];
3016  if (item == NULL)
3017  {
3018  break;
3019  }
3020  if (!item->root.runningStatus)
3021  {
3022  continue;
3023  }
3024 
3025  if (item->root.clazz == MHEG5AUDIO && !components->audio_active)
3026  {
3027  audio = (MHEG5Audio *)item;
3028  components->audio_active = TRUE;
3029  components->audio_component_tag = audio->componentTag;
3030  DBGTRACE(TSTRM|TICS,"Found active audio component: tag = %d",
3031  components->audio_component_tag);
3032  continue;
3033  }
3034  if (item->root.clazz == MHEG5VIDEO && !components->video_active)
3035  {
3036  video = (MHEG5Video *)item;
3037  components->video_active = TRUE;
3038  components->video_component_tag = video->componentTag;
3039  if (video->terminationFreeze)
3040  {
3041  components->video_termination = MHEG5_VIDEO_TERMINATION_FREEZE;
3042  }
3043  DBGTRACE(TSTRM|TICS,"Found active video component: tag = %d, freeze = %s",
3044  components->video_component_tag,
3045  components->video_termination == MHEG5_VIDEO_TERMINATION_FREEZE ? "TRUE" : "FALSE");
3046  continue;
3047  }
3048  }
3049  request->terminateStreamSetup = (components->video_termination == MHEG5_VIDEO_TERMINATION_DISAPPEAR)? TRUE : FALSE;
3050 }
3051 
3052 static BOOLEAN ParseAudio( U8BIT *dptr, U8BIT *dend, S_ICSLangInfo *info_ptr )
3053 {
3054  U8BIT dlen,dtag;
3055  BOOLEAN result = FALSE;
3056  while ( dptr < dend )
3057  {
3058  dtag = *dptr++;
3059  dlen = *dptr++;
3060  DBGTRACE(TSTRM,"dtag=%d dlen=%d",dtag,dlen)
3061  switch ( dtag )
3062  {
3063  default:
3064  break;
3065  case ISO_LANG_DTAG:
3066  DBGTRACE(TSTRM,"lang=%c%c%c atyp=%x",dptr[0],dptr[1],dptr[2],dptr[3])
3067  info_ptr->lang_code = dptr[0] << 16 | dptr[1] << 8 | dptr[2] | 0x202020; // lower case
3068  info_ptr->a_type = dptr[3];
3069  result = TRUE;
3070  break;
3071  case STREAMID_DTAG:
3072  DBGTRACE(TSTRM,"stag=%x",dptr[0])
3073  break;
3074  case SUBTITLE_DTAG:
3075  DBGTRACE(TERROR,"lang=%c%c%c styp=%x",dptr[0],dptr[1],dptr[2],dptr[3])
3076  break;
3077  }
3078  dptr += dlen;
3079  }
3080  return result;
3081 }
3082 
3083 static BOOLEAN ParseSubts( U8BIT *dptr, U8BIT *dend, S_ICSLangInfo *info_ptr )
3084 {
3085  U8BIT dlen,dtag;
3086  BOOLEAN result = FALSE;
3087  while ( dptr < dend )
3088  {
3089  dtag = *dptr++;
3090  dlen = *dptr++;
3091  DBGTRACE(TSTRM,"dtag=%d dlen=%d",dtag,dlen)
3092  switch ( dtag )
3093  {
3094  default:
3095  break;
3096  case ISO_LANG_DTAG:
3097  DBGTRACE(TERROR,"lang=%c%c%c atyp=%x",dptr[0],dptr[1],dptr[2],dptr[3])
3098  break;
3099  case STREAMID_DTAG:
3100  DBGTRACE(TSTRM,"stag=%x",dptr[0])
3101  break;
3102  case SUBTITLE_DTAG:
3103  DBGTRACE(TSTRM,"lang=%c%c%c styp=%x",dptr[0],dptr[1],dptr[2],dptr[3])
3104  info_ptr->lang_code = dptr[0] << 16 | dptr[1] << 8 | dptr[2] | 0x202020; // lower case
3105  info_ptr->s_type = dptr[3];
3106  info_ptr->u.page.composition = (dptr[4] << 8) | dptr[5];
3107  info_ptr->u.page.ancillary = (dptr[6] << 8) | dptr[7];
3108  result = TRUE;
3109  break;
3110  }
3111  dptr += dlen;
3112  }
3113  return result;
3114 }
3115 
3121 static BOOLEAN HasStreamId( U8BIT *dptr, U8BIT *dend, U8BIT id )
3122 {
3123  BOOLEAN found = FALSE;
3124  U8BIT dlen,dtag;
3125  while ( dptr < dend )
3126  {
3127  dtag = *dptr++;
3128  dlen = *dptr++;
3129  if (dtag == STREAMID_DTAG && *dptr == id)
3130  {
3131  found = TRUE;
3132  break;
3133  }
3134  dptr += dlen;
3135  }
3136  return found;
3137 }
3138 
3139 static S_ICSPidKeys* SetupKeys(S_ICSPidInfo *pid_info, U16BIT num_pids, U16BIT pid, S_ICSPidKeys* pkeys)
3140 {
3141  U16BIT i;
3142  //DBGTRACE((TICS|TSTRM),"num_pids=%u pid=%x",num_pids,pid)
3143  for (i = 0; i != num_pids; i++)
3144  {
3145  //DBGTRACE((TICS|TSTRM),"[%d]PID=%x",i,pid_info[i].PID)
3146  if (pid_info[i].PID == pid)
3147  {
3148  memcpy(pkeys->even, pid_info[i].even_key, 16);
3149  memcpy(pkeys->odd, pid_info[i].odd_key, 16);
3150  memcpy(pkeys->even + 16, pid_info[i].iv, 16);
3151  memcpy(pkeys->odd + 16, pid_info[i].iv, 16);
3152  break;
3153  }
3154  }
3155  return (i != num_pids)? pkeys : NULL;
3156 }
3157 
3163 static void StartStreaming(StreamerRequest *request)
3164 {
3165  U32BIT pref_lang;
3166  U32BIT subt_lang;
3167  U16BIT dlen, pid;
3168  U8BIT type;
3169  S_StreamPids pids;
3170  S_ICSLangInfo *info_ptr;
3171  U8BIT *sptr;
3172  U8BIT *eptr;
3173  U8BIT *data = request->pmt;
3174  S_ICSComponents components;
3175  S_ICSPidKeys video_keys;
3176  S_ICSPidKeys audio_keys;
3177  E_ICSAudioCodec audcodec = ICS_AC_NONE;
3178  E_ICSVideoCodec vidcodec = ICS_VC_NONE;
3179 
3180  SetupComponents(request,&components);
3181  memset(&pids,0,sizeof(S_StreamPids));
3182  DVB_MhegPrefAudioLangs(&pref_lang,1);
3183  DBGTRACE((TICS|TSTRM),"PREF lang=%x",pref_lang)
3184  if (!DVB_MhegPrefSubtitleLangs(&subt_lang,1))
3185  {
3186  subt_lang = UND_LANGUAGE_CODE;
3187  }
3188 
3189  dlen = (data[1] << 8 | data[2]) & 0xfff;
3190  eptr = data + dlen - 4; // -4 for crc
3191  pids.pcr_pid = (data[8] << 8 | data[9]) & 0x1fff;
3192  dlen = (data[10] << 8 | data[11]) & 0xfff;
3193  data += 12 + dlen;
3194  sptr = data;
3195  while (sptr < eptr)
3196  {
3197  type = *sptr;
3198  dlen = ((sptr[3] & 0x0f) << 8) | sptr[4];
3199  switch( *sptr )
3200  {
3201  default:
3202  break;
3203  case ST_PES_PKT:
3204  if (components.video_active)
3205  {
3206  pids.info_num++;
3207  }
3208  break;
3209  case ST_HEAAC:
3210  case ST_AAC_ADTS:
3211  case ST_DOLBY_BRAY:
3212  case ST_DOLBY_ATSC:
3213  if (components.audio_active)
3214  {
3215  pids.info_num++;
3216  }
3217  break;
3218  }
3219  sptr += 5 + dlen;
3220  }
3221  if (pids.info_num)
3222  {
3223  pids.info_ptr = (S_ICSLangInfo*)STB_MemAlloc(sizeof(S_ICSLangInfo)*pids.info_num);
3224  if (pids.info_ptr == NULL)
3225  {
3226  pids.info_num = 0;
3227  }
3228  else
3229  {
3230  memset(pids.info_ptr,0,sizeof(S_ICSLangInfo)*pids.info_num);
3231  }
3232  }
3233  info_ptr = pids.info_ptr;
3234  while (data < eptr)
3235  {
3236  type = *data;
3237  pid = ((data[1] & 0x1f) << 8) | data[2];
3238  dlen = ((data[3] & 0x0f) << 8) | data[4];
3239  data += 5;
3240  switch( type )
3241  {
3242  default:
3243  DBGTRACE(TERROR,"Unsupported stream type=0x%x",type)
3244  break;
3245  case ST_PES_PKT:
3246  DBGTRACE(TSTRM,"PES type pid=%d len=%d",pid,dlen)
3247  if (components.video_active && info_ptr)
3248  {
3249  if (ParseSubts(data,data+dlen,info_ptr))
3250  {
3251  info_ptr->pid = pid;
3252  if (pids.subtitle_pid == 0 || info_ptr->lang_code == subt_lang)
3253  {
3254  pids.subtitle_pid = pid;
3255  }
3256  info_ptr++;
3257  }
3258  else
3259  {
3260  pids.info_num--;
3261  }
3262  }
3263  else
3264  {
3265  DBGTRACE(TERROR,"cactive=%u iptr=%p",components.video_active,info_ptr)
3266  }
3267  break;
3268  case ST_H264:
3269  DBGTRACE(TSTRM,"H264 type pid=%d len=%d",pid,dlen)
3270  if (components.video_active)
3271  {
3272  if (components.video_component_tag == -1)
3273  {
3274  vidcodec = ICS_VC_H264;
3275  pids.video_pid = pid;
3276  }
3277  else if (HasStreamId(data,data+dlen,(U8BIT)components.video_component_tag))
3278  {
3279  vidcodec = ICS_VC_H264;
3280  pids.video_pid = pid;
3281  }
3282  }
3283  else
3284  {
3285  DBGTRACE(TERROR,"cactive=%u iptr=%p",components.video_active,info_ptr)
3286  }
3287  break;
3288  case ST_AAC_ADTS:
3289  case ST_HEAAC:
3290  case ST_DOLBY_BRAY:
3291  case ST_DOLBY_ATSC:
3292  DBGTRACE(TSTRM,"type=%d pid=%d len=%d",type,pid,dlen)
3293  if (components.audio_active && info_ptr)
3294  {
3295  info_ptr->pid = pid;
3296  switch( type )
3297  {
3298  case ST_AAC_ADTS:
3299  info_ptr->u.codec = ICS_AC_AAC_ADTS;
3300  break;
3301  case ST_HEAAC:
3302  info_ptr->u.codec = ICS_AC_HE_AAC_LATM;
3303  break;
3304  case ST_DOLBY_BRAY:
3305  case ST_DOLBY_ATSC:
3306  info_ptr->u.codec = ICS_AC_EAC3;
3307  break;
3308  }
3309  if (components.audio_component_tag == -1)
3310  {
3311  if ((ParseAudio(data,data+dlen,info_ptr) && info_ptr->lang_code == pref_lang) || pids.audio_pid == 0)
3312  {
3313  DBGTRACE((TICS|TSTRM),"INFO lang=%x Aud-PID=%x",info_ptr->lang_code,pids.audio_pid)
3314  audcodec = info_ptr->u.codec;
3315  pids.audio_pid = pid;
3316  }
3317  }
3318  else if (HasStreamId(data,data+dlen,(U8BIT)components.audio_component_tag))
3319  {
3320  audcodec = info_ptr->u.codec;
3321  pids.audio_pid = pid;
3322  }
3323  info_ptr++;
3324  }
3325  else
3326  {
3327  DBGTRACE(TERROR,"cactive=%u iptr=%p",components.audio_active,info_ptr)
3328  }
3329  break;
3330  }
3331  data += dlen;
3332  }
3333 
3334  DVB_MhegICStreamSetup( pids,
3335  SetupKeys(request->keys.pid_info, request->keys.num_pids, pids.audio_pid, &audio_keys),
3336  SetupKeys(request->keys.pid_info, request->keys.num_pids, pids.video_pid, &video_keys),
3337  components.video_termination, audcodec, vidcodec );
3338 }
3339 
3346 static void CheckRunCondition(StreamerRequest *request)
3347 {
3348  DBG(("%d ~ CheckRunCondition: streamState = %s", request->requestId,
3349  REQUEST_STREAM_STATE_STR(request)));
3350  if (request->streamState == STATE_STREAM_RUNNING)
3351  {
3352  DBG(("%d ~ request->outputState = %s", request->requestId,
3353  REQUEST_OUTPUT_STATE_STR(request)));
3354  DBG(("%d ~ request->downloadState = %s", request->requestId,
3355  REQUEST_DOWNLOAD_STATE_STR(request)));
3356  DBG(("%d ~ request->keyState = %s", request->requestId,
3357  REQUEST_KEY_STATE_STR(request)));
3358  DBG(("%d ~ request->pmtRetrieved = %s", request->requestId,
3359  request->pmtRetrieved ? "TRUE" : "FALSE"));
3360 
3361  /* If output is stopped but we can setup ICS, go ahead */
3362  if ((request->outputState == STATE_OUTPUT_STOPPED) &&
3363  (request->downloadState != STATE_DOWNLOAD_STARTED) &&
3364  (request->keyState == STATE_KEY_VALID) &&
3365  (request->pmtRetrieved))
3366  {
3367  PDBG(("%d ~ Output stopped, ICS ready", request->requestId));
3368  if (isIcsSetup)
3369  {
3371  }
3372  isIcsSetup = TRUE;
3373  StartStreaming(request);
3374  request->outputState = STATE_OUTPUT_SETUP;
3375  }
3376 
3377  if (request->outputState == STATE_OUTPUT_SETUP)
3378  {
3379  /* Check if there's enough data to start streaming */
3380  PDBG(("%d ~ Output is setup, check buffer / download",
3381  request->requestId));
3382  if ((IsEnoughDataBuffered(request)) ||
3383  (request->downloadState == STATE_DOWNLOAD_DONE))
3384  {
3385  PDBG(("%d ~ Start streaming", request->requestId));
3386  request->outputState = STATE_OUTPUT_READY;
3387  request->flowState = STATE_FLOW_STARTED;
3388 
3389  if (!request->paused)
3390  {
3391  MHEG5QueueEnableStreaming(request->requestId);
3393  if (request->isPartial)
3394  {
3395  request->downloadState = STATE_DOWNLOAD_QUEUED;
3396  ProgressDownloadRequest(request);
3397  }
3398  }
3399  }
3400  }
3401  else if (request->outputState == STATE_OUTPUT_STARVED)
3402  {
3403  /* Check if there's any data to continue streaming */
3404  PDBG(("%d ~ Output is starved, check buffer / download",
3405  request->requestId));
3406  if ((IsEnoughDataBuffered(request)) ||
3407  (request->downloadState == STATE_DOWNLOAD_DONE))
3408  {
3409  /*request->outputState = STATE_OUTPUT_RUNNING;*/
3410  PDBG(("%d ~ Starved output resumed", request->requestId));
3411  ResumePlayback(request);
3413  }
3414  }
3415  }
3416 }
3417 
3423 static BOOLEAN IsEnoughDataBuffered(StreamerRequest *request)
3424 {
3425 #if 0
3426  U64BIT expectedBytes;
3427  char buffer[24];
3428 #endif
3429  U32BIT bufferedBytes;
3430  BOOLEAN enough = FALSE;
3431 
3432  bufferedBytes = MHEG5QueueGetBufferedBytes();
3433 #if 0
3434  if (ULL_IsValid(request->expectedBytes))
3435  {
3436  ULL_Itoa(request->expectedBytes, buffer);
3437  PDBG(("%d ~ request->expectedBytes = %s", request->requestId, buffer));
3438  expectedBytes = request->expectedBytes;
3439  ULL_Sub(expectedBytes, request->downloadedBytes);
3440  ULL_Itoa(expectedBytes, buffer);
3441  PDBG(("%d ~ expectedBytes = %s", request->requestId, buffer));
3442  }
3443  else
3444  {
3445  /* Unknown - always allow */
3446  PDBG(("%d ~ Unknown expected: allow"));
3447  ULL_Set32(expectedBytes, 0);
3448  }
3449 
3450  PDBG(("%d ~ bufferedBytes = %d, expectedBytes %s bufferedBytes",
3451  request->requestId, bufferedBytes,
3452  bufferedBytes >= MIN_BUFFERED_BYTES ? "?" :
3453  ULL_Compare32(expectedBytes, bufferedBytes) <= 0 ? "<=" : ">"));
3454  if ((bufferedBytes >= MIN_BUFFERED_BYTES) ||
3455  (ULL_Compare32(expectedBytes, bufferedBytes) <= 0))
3456  {
3457  enough = TRUE;
3458  }
3459 #else
3460  if (bufferedBytes >= MIN_BUFFERED_BYTES)
3461  {
3462  enough = TRUE;
3463  }
3464 #endif
3465 
3466  return enough;
3467 }
3468 
3476 static U32BIT ConvertToUnits(StreamerRequest *request, U64BIT bytes)
3477 {
3478  U64BIT tmp;
3479  U32BIT units;
3480 
3481  if (request->bitrate > 0)
3482  {
3483  /* Known bitrate: t_current = b_current * 1000 / bytesPerSecond */
3484  tmp = bytes;
3485  ULL_Mul32(tmp, 1000);
3486  ULL_Div32(tmp, request->bitrate);
3487 
3488  units = ULL_Get32(tmp);
3489  }
3490  else /* TODO: Index file needs to be supported */
3491  {
3492  /* Unknown bitrate */
3493  tmp = bytes;
3495 
3496  units = ULL_Get32(tmp);
3497  }
3498 
3499  return units;
3500 }
3501 
3509 static U64BIT ConvertToBytes(StreamerRequest *request, U32BIT units)
3510 {
3511  U64BIT bytes;
3512  U32BIT mod;
3513 
3514  if (request->bitrate > 0)
3515  {
3516  /* Known bitrate: b_required = t_required * bytesPerSecond / 1000 */
3517  ULL_Set32(bytes, units);
3518  ULL_Mul32(bytes, request->bitrate);
3519  ULL_Div32(bytes, 1000);
3520 
3521  /* Round to nearest multiple of TS packet size */
3522  mod = ULL_Mod32(bytes, STMR_TS_PACKET_SIZE);
3523  if (mod < 94)
3524  {
3525  ULL_Sub32(bytes, mod);
3526  }
3527  else
3528  {
3529  ULL_Add32(bytes, STMR_TS_PACKET_SIZE - mod);
3530  }
3531  }
3532  else /* TODO: Index file needs to be supported */
3533  {
3534  /* Unknown bitrate */
3535  ULL_Set32(bytes, units);
3537  }
3538  return bytes;
3539 }
3540 
3545 static BOOLEAN IsStreamRunning(void)
3546 {
3547  StreamerRequest *request;
3548  BOOLEAN running;
3549 
3550  running = FALSE;
3551  request = requestList;
3552  while (request != NULL)
3553  {
3554  if (request->streamState == STATE_STREAM_RUNNING)
3555  {
3556  /* Found a running stream */
3557  running = TRUE;
3558  break;
3559  }
3560  request = request->next;
3561  }
3562 
3563  return running;
3564 }
3565 
3571 static void CheckDownloadCondition(void)
3572 {
3573  StreamerRequest *request;
3574  StreamerRequest *primaryRequest = NULL;
3575  BOOLEAN secondaryDownloading = FALSE;
3576  U32BIT minPrepareNumber = 0;
3577  StreamerRequest *nextPrepare = NULL;
3578 
3579  /* Gather some information about primary and secondary streams */
3580  request = requestList;
3581  while (request != NULL)
3582  {
3583  if (request->streamState == STATE_STREAM_RUNNING)
3584  {
3585  /* Running stream is always primary */
3586  primaryRequest = request;
3587  }
3588  else
3589  {
3590  if ((request->downloadState != STATE_DOWNLOAD_STOPPED) &&
3591  (request->downloadState != STATE_DOWNLOAD_DONE))
3592  {
3593  /* If any secondary stream is downloading, others should
3594  * not be downloaded at the same time.
3595  */
3596  secondaryDownloading = TRUE;
3597  }
3598 
3599  if ((minPrepareNumber == 0) ||
3600  (request->prepareNumber < minPrepareNumber))
3601  {
3602  if ((request->streamState == STATE_STREAM_PREPARED) &&
3603  (request->downloadState == STATE_DOWNLOAD_STOPPED))
3604  {
3605  minPrepareNumber = request->prepareNumber;
3606  nextPrepare = request;
3607  }
3608  }
3609  }
3610  request = request->next;
3611  }
3612 
3613  PDBG(("primaryRequest = %p, nextPrepare = %p", primaryRequest,
3614  nextPrepare));
3615 
3616  if ((primaryRequest != NULL) &&
3617  (primaryRequest->downloadState == STATE_DOWNLOAD_STOPPED))
3618  {
3619  /* Primary request should be downloading, any other download should
3620  * stop.
3621  */
3622  PDBG(("Downloading primary, stopping any secondary"));
3623  request = requestList;
3624  while (request != NULL)
3625  {
3626  if (request != primaryRequest)
3627  {
3628  PDBG(("Destroying download request for %d (secondary)",
3629  request->requestId));
3630  DestroyDownloadRequest(request);
3631  MHEG5QueueReleaseRequestItems(request->requestId);
3632  }
3633  request = request->next;
3634  }
3635 
3636  PDBG(("Creating download request for %d (primary)",
3637  primaryRequest->requestId));
3638 
3639  primaryRequest->currentLoop = 0;
3640  CreateDownloadRequest(primaryRequest);
3641  }
3642  else if ((primaryRequest == NULL) ||
3643  (primaryRequest->downloadState == STATE_DOWNLOAD_DONE) ||
3644  (primaryRequest->downloadState == STATE_DOWNLOAD_FAILED))
3645  {
3646  /* Primary request download is done. Next prepared stream can
3647  * start downloading, if other secondary requests are not
3648  * currently download (only one download at a time is permitted).
3649  */
3650  if (nextPrepare != NULL && !secondaryDownloading)
3651  {
3652  PDBG(("Creating download request for %d (secondary)",
3653  nextPrepare->requestId));
3654  nextPrepare->currentLoop = 0;
3655  CreateDownloadRequest(nextPrepare);
3656  }
3657  }
3658 }
3659 
3667 static void StopContentOutput(StreamerRequest *request, BOOLEAN flush)
3668 {
3669  if (flush)
3670  {
3671  MHEG5QueueReleaseRequestItems(request->requestId);
3672  }
3673 
3674  /* If the stream is running */
3675  if (request->streamState == STATE_STREAM_RUNNING)
3676  {
3677  request->flowState = STATE_FLOW_STOPPED;
3678  request->outputState = STATE_OUTPUT_STOPPED;
3679  }
3680 }
3681 
3689 static void ResumePlayback(StreamerRequest *request)
3690 {
3691  /* This function may be called more than once when underflow is
3692  * resumed.
3693  */
3694  if (request->pauseRequestCount > 0)
3695  {
3696  --request->pauseRequestCount;
3697  if (request->pauseRequestCount == 0)
3698  {
3700  }
3701  }
3702 
3703  if (isIcsSetup)
3704  {
3705  MHEG5QueueEnableStreaming(request->requestId);
3706  if (request->isPartial)
3707  {
3708  request->downloadState = STATE_DOWNLOAD_QUEUED;
3709  ProgressDownloadRequest(request);
3710  }
3711  }
3712 }
3713 
3719 static BOOLEAN IsValidUrl(MHEG5String *url)
3720 {
3721  if (((url->len >= 7) && (MHEG5strncmp(url->data, "http://", 7) == 0)) ||
3722  ((url->len >= 8) && (MHEG5strncmp(url->data, "https://", 8) == 0)))
3723  {
3724  return TRUE;
3725  }
3726  return FALSE;
3727 }
3728 
3729 static void TimerCallback(BOOLEAN triggered, void *callerRef, H_Timer timerHandle)
3730 {
3731  U_PARAM request;
3732  request.ptr = callerRef;
3733  PDBG(("%d",STB_OSGetClockMilliseconds()));
3734  NotifyStreamStopped(request.u32);
3735 }
3736 
3744 static void UnderflowCallback(U32BIT requestId,U32BIT playoutTime)
3745 {
3746  StreamerRequest *request;
3747  H_Timer timerHandle;
3748 
3749  STB_OSMutexLock(requestMutex);
3750  request = FindRequestById(requestId);
3751  if (request != NULL)
3752  {
3753  PDBG(("UnderflowCallback: request->outputState = %s",
3754  REQUEST_OUTPUT_STATE_STR(request)));
3755  if (request->outputState == STATE_OUTPUT_RUNNING)
3756  {
3757  if ((request->downloadState == STATE_DOWNLOAD_DONE) &&
3758  (ULL_IsValid(request->expectedBytes)) &&
3759  (ULL_Compare(request->presentedBytes,request->expectedBytes) >= 0) &&
3760  (request->currentLoop == request->looping - 1))
3761  {
3762  U_PARAM rqstId;
3763  rqstId.u32 = requestId;
3764  PDBG(("%d ~ STATE_OUTPUT_DONE", request->requestId));
3765  request->outputState = STATE_OUTPUT_DONE;
3766  request->restart = TRUE;
3767 
3768  if (playoutTime == 0 ||
3769  VT_TimerCreate(playoutTime,(F_TimerCallback)TimerCallback,rqstId.ptr,&timerHandle) != MHERR_OK)
3770  {
3771  NotifyStreamStopped(requestId);
3772  }
3773  }
3774  else
3775  {
3776  request->outputState = STATE_OUTPUT_STARVED;
3777  if (request->flowState != STATE_FLOW_UNDERFLOW)
3778  {
3779  request->pauseRequestCount++;
3780  if (request->pauseRequestCount == 1)
3781  {
3784  }
3785 
3787  NotifyStreamStopped(request->requestId);
3788  }
3789  }
3790  }
3791  else
3792  {
3793  /* Not running, cannot be starved */
3794  PDBG(("%d ~ UnderflowCallback: output state = %s",
3795  request->requestId, REQUEST_OUTPUT_STATE_STR(request)));
3796  }
3797  }
3798  STB_OSMutexUnlock(requestMutex);
3799 }
3800 
3807 static void StreamKeyLoadedCallback(void *userData, S_CONTENT *content)
3808 {
3809  StreamerRequest *request;
3810  U8BIT *data;
3811  U32BIT size;
3812 
3813  PDBG((" "));
3814 
3815  STB_OSMutexLock(requestMutex);
3816  request = FindRequestByObj(userData);
3817  if ((request != NULL) &&
3818  ((request->downloadId == request->keyDownloadId) ||
3819  (request->downloadState == STATE_DOWNLOAD_DONE)))
3820  {
3821  assert(request->keys.pid_info == NULL);
3822 
3823  /* HACK! This is a hack to pass ICS AES tests, but actually the
3824  * file format is wrong and this needs to be removed.
3825  */
3826  data = content->data;
3827  size = content->size;
3828  if (content->size > 8 && memcmp(content->data, "X-Keys: ", 8) == 0)
3829  {
3830  data = content->data + 8;
3831  content->size -= 8;
3832  }
3833 
3834  MHEG5parseStreamKeys(data, (U16BIT)size, &request->keys);
3835  if (request->keys.num_pids == 0)
3836  {
3837  GenerateKeyFileError();
3838  request->keyState = STATE_KEY_ERROR;
3839  }
3840  else
3841  {
3842  request->keyState = STATE_KEY_VALID;
3843  CheckRunCondition(request);
3844  }
3845  }
3846  STB_OSMutexUnlock(requestMutex);
3847 }
3848 
3854 static void StreamKeyLoadFailCallback(void *userData)
3855 {
3856  StreamerRequest *request;
3857 
3858  PDBG((" "));
3859 
3860  request = FindRequestByObj(userData);
3861  if ((request != NULL) &&
3862  ((request->downloadId == request->keyDownloadId) ||
3863  (request->downloadState == STATE_DOWNLOAD_DONE)))
3864  {
3865  GenerateKeyFileError();
3866  request->keyState = STATE_KEY_ERROR;
3867  }
3868 }
3869 
3874 static void ServerAccessCallback(void)
3875 {
3876  StreamerRequest *request;
3877 
3878  PDBG((" "));
3879 
3880  request = requestList;
3881  while (request != NULL)
3882  {
3883  if (request->downloadState == STATE_DOWNLOAD_WAIT_ACCESS)
3884  {
3885  request->downloadState = STATE_DOWNLOAD_CHECK_ACCESS;
3886  processRequests = TRUE;
3887  MH5GlueAddPostProcessFunc( MHEG5StreamerProcess );
3888  }
3889  request = request->next;
3890  }
3891 }
3892 
3898 static void TlsCertCallback(void)
3899 {
3900  StreamerRequest *request;
3901 
3902  PDBG((" "));
3903 
3904  request = requestList;
3905  while (request != NULL)
3906  {
3907  if (request->downloadState == STATE_DOWNLOAD_WAIT_CERT)
3908  {
3909  request->downloadState = STATE_DOWNLOAD_QUEUED;
3910  processRequests = TRUE;
3911  MH5GlueAddPostProcessFunc( MHEG5StreamerProcess );
3912  }
3913  request = request->next;
3914  }
3915 }
3916 
3917 #endif /*INCLUDE_ICS*/
U8BIT DVB_MhegPrefSubtitleLangs(U32BIT *langs, U8BIT max)
Get list of preferred subtitle languages. If there is no preference then zero is returned. This only writes into array up to &#39;max&#39; items This MUST be a non-blocking function, returning results immediately.
U32BIT STB_OSGetClockMilliseconds(void)
Get Current Computer Clock Time.
Task functions for IC Streamer.
E_VideoTermination
Definition: dvb_ics.h:39
U64BIT MHEG5GetDownloadContentLength(U32BIT downloadId, U64BIT contentLength)
Return content length (in bytes) for download request. The content length may be: ...
Definition: stmr_dl.c:394
Implement MHEG5 engine control functions (i.e. start/stop etc)
void MHEG5StreamerResume(MHEG5Stream *stream)
Resume IP content streaming after it was paused by MHEG5StreamerPause.
E_MhegErr VQ_PutMsg(S_MhegMessage *pMsg, E_PRIORITY priority)
Post event or section message on queue. Copies data into queue.
Definition: glue_queue.c:248
void MHEG5AddTlsCertificateCallback(void(*loadCallback)(void))
Add a callback function to be called when pending requests are resolved.
Definition: mh5tls.c:189
void MHEG5StopMeasureRequest(U32BIT downloadId)
Stop HTTP streaming performance measurement request.
Definition: stmr_msp.c:310
#define ULL_IsValid(variable)
Definition: glue_ulong.h:90
#define ULL_Mod32(variable, value)
Definition: glue_ulong.h:92
MHEG5Int counterMaxPosition
Definition: mh5stream.h:78
F_MSG_PROCESS proc_msg_func
Definition: glue_queue.h:198
void MHEG5StreamerSetCounterPosition(MHEG5Stream *stream)
Set the counter position of a stream in the streamer. This function uses the counter position as set ...
E_MhegErr DVB_MhegICStreamSetup(S_StreamPids pids, S_ICSPidKeys *audio_keys, S_ICSPidKeys *video_keys, E_VideoTermination video_termination, E_ICSAudioCodec audio_codec, E_ICSVideoCodec video_codec)
This function tells the external application that an IC delivered stream is to be played using DVB_Mh...
#define MAX_MLTPLX
Definition: mh5stream.h:41
U32BIT size
Definition: fs_types.h:54
E_MhegErr DVB_MhegICStreamRelease(void)
This function tells the external application that the IC delivered stream is stopped. Presentation of all stream components (audio, video and/or subtitles) should cease. This function may be called while DVB_MhegHandleStreamData is in progress. In this case DVB_MhegHandleStreamData should return indicating that the entire block has been processed.
void MHEG5StreamerNotifyStreamStopped(MHEG5Stream *stream)
Notify that a stream has stopped. This notification comes from the audio/video deocder.
MHEG5TlsCertRequestStatus
Definition: mh5tls.h:37
MHEG5Ingredient ingredient
Definition: mh5stream.h:65
E_MhegErr VT_TimerCreate(U32BIT millisecs, F_TimerCallback callback, void *callerRef, H_Timer *pTimerHandle)
Set a timer for a certain number of millseconds. When the timer expires a specified callback will be ...
Definition: glue_timers.c:223
E_HttpStatus
Definition: httptype.h:36
#define DBGTRACE(...)
Definition: glue_debug.h:126
void * ptr
Definition: glue_type.h:38
void MHEG5StreamerPrepare(MHEG5Stream *stream)
Prepare for retrieval of a stream from the IP connection.
MHEG5Bool MHEG5stringEqual(MHEG5String *s1, MHEG5String *s2)
Compare two Strings (case sensitive!)
Definition: mh5base.c:710
const char * data
Definition: mh5gate.c:56
MHEG5ServerAccess_t
Definition: mh5access.h:36
#define ULL_Sub(variable, value)
Definition: glue_ulong.h:82
void MHEG5ClearDownloadRequest(U32BIT downloadId)
Clear HTTP download request.
Definition: stmr_dl.c:667
void * H_Timer
Definition: glue_timers.h:58
Common header internal to IC streamer.
void MHEG5CopyDownloadStreamKeyInfo(U32BIT downloadId, S_ICSKeys *keys, MHEG5String *keyLocation)
Copy any keys or key location that were obtained from the headers. If such keys or location exist...
Definition: stmr_dl.c:444
void MHEG5StreamerSetCounterEndPosition(MHEG5Stream *stream)
Set the counter end position of a stream in the streamer. This function uses the counter end position...
void ULL_Print(U64BIT size)
Print a stream size value (for debug purposes).
Definition: stmr_util.c:54
E_MhegErr MHEG5StreamerStartTask(U32BIT stack, U8BIT priority)
Start streamer task. The task passes stream data to the external application and generates stream and...
Definition: stmr_task.c:92
BOOLEAN MHEG5GetDownloadPmt(U32BIT downloadId, U8BIT *pmt)
Return program map table (PMT) for download (if available). The table is provided as a complete secti...
Definition: stmr_dl.c:501
Implementation of the Video class Description Defines the attributes and behaviour of an elementary v...
#define ULL_IsEqual(variable, value)
Definition: glue_ulong.h:91
void STB_OSDeleteMutex(void *mutex)
Delete a mutex.
MHEG5 queue.
E_ICSVideoCodec
Definition: dvb_ics.h:76
#define ULL_Sub32(variable, value)
Definition: glue_ulong.h:83
MHEG5Int looping
Definition: mh5stream.h:71
#define MHEG5getMem
Definition: glue_memory.h:93
MHEG5Final clazz
Definition: mh5root.h:55
#define PDBG(x)
Definition: stmr_dl.c:70
void MHEG5StreamerAbortMeasurement(MHEG5Program *program)
Abort streaming performance measurement.
void MHEG5ClearDownloadThrottling(void)
Clear information about throttling, causing the download to proceed at full speed (until throttling k...
Definition: stmr_dl.c:549
U16BIT info_num
Definition: dvb_ics.h:116
S32BIT MHEG5GetMeasureStatus(U32BIT downloadId)
Return HTTP status of performance measurement request.
Definition: stmr_msp.c:226
U16BIT pcr_pid
Definition: dvb_ics.h:112
void * MHEG5FileOrmGet(MHEG5String name, U16BIT priority, void *userData, F_CB_Good cbGood, F_CB_Fail cbFail)
Get a file. The file will be loaded and one of the callback functions called when request is resolved...
Definition: mh5fileorm.c:1179
E_ICSAudioCodec
Definition: dvb_ics.h:67
MHEG5ContentBody content
Definition: mh5ingredient.h:75
MHEG5Int counterEndPosition
Definition: mh5stream.h:76
E_MhegErr DVB_MhegICStreamResume(void)
Resume presentation of audio and/or video components of the currently playing IC delivered stream...
MHEG5Ingredient ingredient
Definition: mh5program.h:47
void MHEG5QueueRegisterReleaseCallback(void(*callback)(U32BIT requestId, U64BIT base, U64BIT position, U32BIT len, BOOLEAN last))
Register notification callback for item release events. If a callback is already registered for the e...
Definition: stmr_queue.c:786
E_MhegErr
Definition: mherrors.h:28
#define FRP_CACHE_DEFAULT
Definition: mh5fileorm.h:38
U8BIT even[32]
Definition: dvb_ics.h:88
MHEG5TlsCertRequestStatus MHEG5GetNextTlsCertificate(void)
Issues a request to load the next TLS certificate from the DSM-CC object carousel.
Definition: mh5tls.c:126
void MHEG5StartMeasureRequest(U32BIT downloadId)
Start HTTP streaming performance measurement request.
Definition: stmr_msp.c:185
void MHEG5StreamerSetActiveState(BOOLEAN activeState)
Set the active state of the streamer. When it is not active, it is allowed to buffer data but not to ...
void MHEG5stringDestruct(MHEG5String *item)
Destruct a MHEG5String.
Definition: mh5base.c:686
MHEG5 engine interface error codes.
U32BIT MHEG5QueueGetBufferedBytes(void)
Return number of buffered bytes in the queue (regardless of request)
Definition: stmr_queue.c:570
void(* F_MSG_PROCESS)(void *data)
Function to Process voyager message.
Definition: glue_queue.h:70
S32BIT MHEG5StreamerGetCounterPosition(MHEG5Stream *stream)
Return the current counter position (in units of 188 bytes).
uint8_t U8BIT
Definition: techtype.h:82
U8BIT * MHEG5GetMeasureRedirect(U32BIT downloadId)
Return the redirection URL for a request that was redirected (HTTP status 3xx).
Definition: stmr_msp.c:248
long MHEG5Int
Definition: mh5base.h:73
#define MAX_REDIRECTS
Definition: mh5fileorm.c:87
MHEG5Int speed[2]
Definition: mh5stream.h:74
void MHEG5StreamerRegisterUnderflowCallback(void(*callback)(U32BIT requestId, U32BIT playoutTime))
Register notification callback for underflow events. If a callback is already registered for the even...
Definition: stmr_task.c:136
BOOLEAN MHEG5DownloadPositionPartial(U32BIT downloadId, U64BIT *position)
Return what is current position of download request.
Definition: stmr_dl.c:359
void MHEG5StreamerMeasurePerformance(MHEG5String *url, MHEG5Int maxBytes, MHEG5Program *program)
Measure streaming performance (bytes per second). The result is returned to the resident program...
void(* F_TimerCallback)(BOOLEAN triggered, void *callerRef, H_Timer timerHandle)
Definition: glue_timers.h:77
File System types.
DVB Video functions are required by MHEG5 engine. All required functions should be non-blocking...
string parsing utility functions for MHEG5
#define ICS_MINIMUM_BUFFER_SIZE
Definition: mheg5_control.h:76
void MHEG5QueueTerm(void)
Terminate queue manager.
Definition: stmr_queue.c:799
#define ULL_Itoa(variable, str)
Definition: glue_ulong.h:97
void MHEG5RenewDownloadRequest(U32BIT downloadId, char *url)
Renew HTTP download request.
Definition: stmr_dl.c:697
U16BIT audio_pid
Definition: dvb_ics.h:113
void MHEG5StopDownloadRequestAsync(U32BIT downloadId)
Stop HTTP download request asynchronously. This function should be used when the caller wants to stop...
Definition: stmr_dl.c:609
void STB_OSMutexUnlock(void *mutex)
Unlock a mutex (a.k.a. &#39;leave&#39;, &#39;signal&#39; or &#39;release&#39;)
#define MHEG5freeMem
Definition: glue_memory.h:94
#define ASSERT(x)
Definition: glue_assert.h:50
void * STB_MemAlloc(U32BIT memSize)
Allocates the specified number of bytes.
API for IC streamer.
IC Streamer queue manager.
This file defines the profile for the MHEG engine.
void MHEG5StopDownloadRequest(U32BIT downloadId)
Stop HTTP download request.
Definition: stmr_dl.c:584
void MHEG5AddServerAccessCallback(void(*LoadNotifyCallback)(void))
Add callback function to be called when the server access permission file is loaded.
Definition: mh5access.c:344
MHEG5Int componentTag
Definition: mh5video.h:63
Implementation of the resident programs which are defined by the current profile. ...
Implement Functions to support Service Gateways. Functions for standarizing several GroupIDs like +DS...
void MHEG5StreamerStop(MHEG5Stream *stream)
Stop streaming IP content.
#define ULL_Mul32(variable, value)
Definition: glue_ulong.h:84
short MHEG5Bool
Definition: mh5base.h:71
void MHEG5streamerNotifyStreamingReady(void)
Handle notification that the external application is ready to handle IP stream data.
S32BIT MHEG5GetMeasureResponseCode(U32BIT downloadId)
Return response code for performance measurement request.
Definition: stmr_msp.c:205
S_ICSLangInfo * info_ptr
Definition: dvb_ics.h:117
U32BIT u32
Definition: glue_type.h:39
void * STB_OSCreateMutex(void)
Create a mutex.
#define ULL_SetInvalid(variable)
Definition: glue_ulong.h:89
void MHEG5DestroyMeasureRequest(U32BIT downloadId)
Destroy HTTP streaming performance measurement request.
Definition: stmr_msp.c:329
void MHEG5QueueReleaseItems(U32BIT requestId, U64BIT marker)
Release queue items that are related to a given request up to a marker.
Definition: stmr_queue.c:680
U32BIT MHEG5CreateDownloadRequest(U32BIT requestId, char *url, U64BIT rangeFrom, U64BIT rangeTo, BOOLEAN lastRequest)
Create HTTP download request.
Definition: stmr_dl.c:212
MHEG5ServerAccess_t MHEG5CheckServerPermitted(U8BIT *url)
Definition: mh5access.c:111
void MHEG5StreamerSendSignalToTask(void)
Send signal to streamer task to wake it up (if it&#39;s asleep)
Definition: stmr_task.c:122
U8BIT even_key[16]
Definition: stmr_header.h:46
Event handling. Implementation of a combined queue for events and actions. This is the eventsystem wh...
S32BIT MHEG5StreamerGetCounterMaxPosition(MHEG5Stream *stream)
Return the counter maximum position (content length in units of 188 bytes).
Functions relating to HTTPS Server Access.
MHEG5Byte * data
Definition: mh5base.h:85
void MHEG5StartDownloadRequest(U32BIT downloadId)
Start HTTP download request.
Definition: stmr_dl.c:317
int len
Definition: mh5gate.c:57
BOOLEAN MHEG5StreamerIsRunable(MHEG5Stream *stream)
Check that stream is runable (i.e. MHEG5StreamerPrepare has been called for it)
U16BIT video_pid
Definition: dvb_ics.h:114
#define UND_LANGUAGE_CODE
Definition: mh5control.h:39
#define DPL2(x)
Definition: glue_debug.h:185
int32_t S32BIT
Definition: techtype.h:87
void MHEG5StreamerNotifyStreamStarted(MHEG5Stream *stream)
Notify that a stream has started. This notification comes from the audio/video deocder.
#define MHEG5strncmp(a, b, n)
Definition: mh5base.h:52
U16BIT subtitle_pid
Definition: dvb_ics.h:115
uint16_t U16BIT
Definition: techtype.h:84
Implement functions to retrieve MHEG5objects by GroupID and ID.
MHEG5Ingredient * multiplex[MAX_MLTPLX]
Definition: mh5stream.h:68
Implementation of the MHEG5 Application Class Defines a set of Ingredient objects, which are shared within an application scope. Base class: Group Subclasses: None Status: Concrete class.
void MHEG5parseStreamKeys(U8BIT *data, U16BIT len, S_ICSKeys *keys)
Parse IC stream keys. These keys are delivered in the HTTP headers or in a separate file...
Definition: stmr_header.c:119
#define ULL_Add(variable, value)
Definition: glue_ulong.h:80
MHEG5Bool runningStatus
Definition: mh5root.h:51
U32BIT MHEG5CreateMeasureRequest(U32BIT requestId, char *url, S32BIT maxBytes)
Create HTTP streaming performance measurement request.
Definition: stmr_msp.c:119
MHEG5Int counterPosition
Definition: mh5stream.h:75
void MHEG5QueueEnableStreaming(U32BIT requestId)
Allow streaming data from the queue.
Definition: stmr_queue.c:584
E_MhegErr DVB_MhegICStreamPause(void)
Pause presentation of audio and/or video components of the currently playing IC delivered stream...
File interface functions to DSMCC component.
void MHEG5StreamerRefresh(void)
Refresh any running IC streams - called when user preferences have changed.
U8BIT DVB_MhegPrefAudioLangs(U32BIT *langs, U8BIT max)
Get list of preferred audio languages. If there is no preference then zero is returned. This only writes into array up to &#39;max&#39; items This MUST be a non-blocking function, returning results immediately.
DVB Audio functions are required by MHEG5 engine. All required functions should be non-blocking...
void MHEG5StreamerClose(void)
Close the IC streaming module.
redirection include
U32BIT MHEG5GetTlsCertStoreCount(void)
Return number of TLS certificates in the TLS certificate store.
Definition: mh5tls.c:112
void MHEG5QueueRegisterInsertCallback(void(*callback)(U32BIT downloadId, U64BIT base, U64BIT position, U32BIT len, BOOLEAN last))
Register notification callback for item insertion event. If a callback is already registered for the ...
Definition: stmr_queue.c:771
BOOLEAN MHEG5QueueIsStreamingEnabled(void)
Tell whether streaming is enabled.
Definition: stmr_queue.c:626
Definition: mg_png.c:52
void MHEG5sendEvent(MHEG5Root *source, MHEG5EventType event, MHEG5Int data)
Store an event in the asynchronous event queue.
Definition: mh5queue.c:1540
#define FALSE
Definition: techtype.h:68
#define ULL_Compare(first, second)
Definition: glue_ulong.h:93
void STB_OSMutexLock(void *mutex)
Lock a mutex (a.k.a. &#39;enter&#39;, &#39;wait&#39; or &#39;get&#39;).
Functions relating to TLS certificate store.
The timer module allows the use of timers within the MHEG5 component. These timers can be set by othe...
void MHEG5CopyDownloadBitrate(U32BIT downloadId, U32BIT *bytesPerSecond)
Copy download bitrate that was obtained from the headers. If the X-BytesPerSecond header were not rec...
Definition: stmr_dl.c:416
#define ULL_Compare32(first, second)
Definition: glue_ulong.h:94
E_MhegErr MHEG5QueueInit(U8BIT *buffer, U32BIT bufferSize)
Initialise queue manager.
Definition: stmr_queue.c:128
IC Streamer performance measurement.
void MHEG5QueueReleaseRequestItems(U32BIT requestId)
Release all queue items that are related to a given request.
Definition: stmr_queue.c:636
union sMHEG5ContentBody::@2 ref
#define FRP_HASH
Definition: mh5fileorm.h:45
MHEG5Int len
Definition: mh5base.h:84
void MHEG5StreamerHandle(MHEG5StreamerEventParams_t *params)
Process an MHEG5StreamerEvent message The event is passed onto the streamer module for processing...
void MHEG5StreamerNotifyStreamingPerformance(U32BIT requestId, U32BIT speed)
Notify measured streaming performance (speed).
#define ULL_Set32(variable, value)
Definition: glue_ulong.h:88
void MHEG5StreamerRemove(MHEG5Stream *stream)
Remove streaming IP request.
#define DBG(x)
Definition: http_header.c:39
#define STMR_TS_PACKET_SIZE
Definition: stmr_common.h:35
Interaction Channel Streaming functions required by MHEG5 engine References: [1] UK1 Profile - Digita...
U8BIT * data
Definition: fs_types.h:55
U8BIT BOOLEAN
Definition: techtype.h:99
void MHEG5StreamerRun(MHEG5Stream *stream)
Start streaming IP content for a prepared request.
#define ULL_Div32(variable, value)
Definition: glue_ulong.h:85
#define TRUE
Definition: techtype.h:69
#define STMR_MAX_PSI_SECTION
Definition: stmr_common.h:36
MHEG5String MHEG5convertGID(MHEG5String *inRef)
Convert a group ID from a relative reference to an absolute reference. See UK1.05 section 8...
Definition: mh5gate.c:269
void MHEG5QueueReleaseAllItems(void)
Release all queue items - clear the queue completely.
Definition: stmr_queue.c:749
HTTP types.
void MHEG5DestroyDownloadRequest(U32BIT downloadId)
Destroy HTTP download request.
Definition: stmr_dl.c:627
void MHEG5StreamerSetCounterTrigger(MHEG5Stream *stream, S32BIT triggerId, S32BIT newValue)
Add, remove or update a counter trigger for the stream. If the trigger does not exist, it will be added. If it already exists it will be updated if the new value is non-negative, otherwise it will be removed.
U8BIT odd_key[16]
Definition: stmr_header.h:45
U8BIT odd[32]
Definition: dvb_ics.h:87
Implement the MHEG5 Audio Class Audio Class Defines the attributes and behaviour of an elementary aud...
MHEG5Bool referenced
Definition: mh5ingredient.h:49
void MHEG5StreamerPause(MHEG5Stream *stream)
Pause IP content streaming.
MHEG5Bool terminationFreeze
Definition: mh5video.h:62
void MHEG5StreamerStopTask(void)
Stop streamer task. This function blocks until the task is stopped.
Definition: stmr_task.c:145
void MHEG5QueueDisableStreaming(void)
Do not allow streaming data from the queue.
Definition: stmr_queue.c:609
void MHEG5StreamerReset(void)
Reset the IC streaming module.
#define ULL_Get32(variable)
Definition: glue_ulong.h:79
U8BIT iv[16]
Definition: stmr_header.h:47
uint32_t U32BIT
Definition: techtype.h:86
void MHEG5ProcessMeasureRequest(U32BIT downloadId)
Process HTTP streaming performance measurement request.
Definition: stmr_msp.c:269
void MH5GlueAddPostProcessFunc(F_PostProcess func)
Definition: glue_main.c:275
MHEG5Int componentTag
Definition: mh5audio.h:52
IC Streamer download manager.
MHEG5Application * MHEG5getCurrentApplication(void)
<Function description>="">
E_MhegErr MHEG5StreamerOpen(void *buffer, U32BIT size, U32BIT taskPriority)
Open the IC streaming module.
#define ULL_Add32(variable, value)
Definition: glue_ulong.h:81
#define TRACE(t, x)
Definition: glue_debug.h:118
U8BIT * MHEG5GetDownloadRedirect(U32BIT downloadId)
Return the redirection URL for a request that was redirected (HTTP status 3xx).
Definition: stmr_dl.c:337
Header file - Function prototypes for operating system.