function processAndSendMPD($OriginalMPD, $PatchedMPD, $DASHContent, $offsetToNow) { $micro_date = microtime(); $date_array = explode(" ", $micro_date); $date_array[0] = round($date_array[0], 4); $date = date("Y-m-d H:i:s", $date_array[1]); //file_put_contents ( "timelog.txt" , "Tuned in:" . $date . $date_array[0] . " \r\n" , FILE_APPEND ); $AST_SEC = new DateTime('now', new DateTimeZone('UTC')); /* initializer for availability start time */ $AST_SEC->setTimestamp($date_array[1]); //Better use a single time than now above //$AST_SEC->add(new DateInterval('PT1S')); $AST_SEC_W3C = $AST_SEC->format(DATE_W3C); preg_match('/\\.\\d*/', $date_array[0], $dateFracPart); $extension_pos = strrpos($AST_SEC_W3C, '+'); // find position of the last + in W3C date to slip frac seconds $AST_W3C = substr($AST_SEC_W3C, 0, $extension_pos) . $dateFracPart[0] . "Z"; //substr($AST_SEC_W3C, $extension_pos); //file_put_contents ( "timelog.txt" , "Setting AST: " . $AST_W3C . " \r\n" , FILE_APPEND ); $ASTO_SEC = new DateTime('now', new DateTimeZone('UTC')); /* initializer for availability start time */ $ASTO_SEC->setTimestamp($date_array[1] - $offsetToNow); //Better use a single time than now above $ASTO_SEC_W3C = $ASTO_SEC->format(DATE_W3C); $extension_pos = strrpos($ASTO_SEC_W3C, '+'); // find position of the last + in W3C date to slip frac seconds $ASTO_W3C = substr($ASTO_SEC_W3C, 0, $extension_pos) . $dateFracPart[0] . "Z"; //substr($AST_SEC_W3C, $extension_pos); $MPD = simplexml_load_file($DASHContent . "/" . $OriginalMPD); if (!$MPD) { die("Failed loading XML file"); } $dom_sxe = dom_import_simplexml($MPD); if (!$dom_sxe) { echo 'Error while converting XML'; exit; } $dom = new DOMDocument('1.0'); $dom_sxe = $dom->importNode($dom_sxe, true); $dom_sxe = $dom->appendChild($dom_sxe); $periods = parseMPD($dom->documentElement); $cumulativeUpdatedDuration = 0; //Cumulation of period duration on updated MPD $tuneInPeriodStart = 0; $MPDNode =& $periods[0]['node']->parentNode; $MPD_AST = $MPDNode->getAttribute("availabilityStartTime"); $MPD_AST = $ASTO_W3C; //$MPDNode->getAttribute("availabilityStartTime"); preg_match('/\\.\\d*/', $MPD_AST, $matches); $fracAST = "0" . $matches[0]; $originalAST = new DateTime($MPD_AST); $deltaTimeASTTuneIn = $AST_SEC->getTimestamp() + round($date_array[0], 4) - ($originalAST->getTimestamp() + $fracAST); //Time elapsed between the original AST and Tune-in time //file_put_contents ( "timelog.txt" , "TimeOffset: " . $deltaTimeASTTuneIn . ", Original ts:" . ($originalAST->getTimestamp() + $fracAST) . "Tune-in TS: " . ($AST_SEC->getTimestamp() + round($date_array[0],4)) . "\r\n" , FILE_APPEND ); $MPDNode->setAttribute("availabilityStartTime", $AST_W3C); //Set AST to tune-in time $periodStart; //Start of this period in the iteration $duration; //Duration of current period in the iteration $lastPeriodStart; //Period start of the last period in the iteration $lastPeriodDuration; //Period duration of the last period in iteration $responseToSend[1] = count($periods) - 1; for ($periodIndex = 0; $periodIndex < count($periods); $periodIndex++) { $periodStart = $periods[$periodIndex]['node']->getAttribute("start"); $duration = somehowPleaseGetDurationInFractionalSecondsBecuasePHPHasABug($periods[$periodIndex]['node']->getAttribute("duration")); if ($periodStart === '') { $periodStart = $lastPeriodStart + $lastPeriodDuration; } else { $periodStart = somehowPleaseGetDurationInFractionalSecondsBecuasePHPHasABug($periodStart); } //Convert Duration string to number //updateTemplate(&$periods[$periodIndex]['node']); if ($deltaTimeASTTuneIn < $periodStart) { $periods[$periodIndex]['node']->setAttribute("start", "PT" . round($lastPeriodStart + $lastPeriodDuration, 4) . "S"); //Set already for the next iteration $lastPeriodStart = $lastPeriodStart + $lastPeriodDuration; $responseToSend[] = $lastPeriodStart; $lastPeriodDuration = $duration; continue; } //Set already for the next iteration $lastPeriodStart = $periodStart; $lastPeriodDuration = $duration; if ($deltaTimeASTTuneIn > $periodStart + $duration) { $dom->documentElement->removeChild($periods[$periodIndex]['node']); $responseToSend[1] = $responseToSend[1] - 1; continue; } //The only case here is the period in which we tune in $videoSegmentTemplate =& $periods[$periodIndex]['adaptationSet'][0]['representation'][0]['segmentTemplate']['node']; $audioSegmentTemplate =& $periods[$periodIndex]['adaptationSet'][1]['representation'][0]['segmentTemplate']['node']; $videoTimescale = $videoSegmentTemplate->getAttribute("timescale"); $videoSegmentDuration = $videoSegmentTemplate->getAttribute("duration"); $videoStartNum = $videoSegmentTemplate->getAttribute("startNumber"); $videoPTO = $videoSegmentTemplate->getAttribute("presentationTimeOffset"); $newVideoStartNumber = ceil(($deltaTimeASTTuneIn - $periodStart) * $videoTimescale / $videoSegmentDuration) + $videoStartNum; //file_put_contents ( "timelog.txt" , "new video offset: " . ($deltaTimeASTTuneIn - $periodStart)*$videoTimescale/$videoSegmentDuration . "\r\n" , FILE_APPEND ); $videoOffsetUpdate = ($newVideoStartNumber - $videoStartNum) * $videoSegmentDuration / $videoTimescale; $audioTimescale = $audioSegmentTemplate->getAttribute("timescale"); $audioSegmentDuration = $audioSegmentTemplate->getAttribute("duration"); $audioStartNum = $audioSegmentTemplate->getAttribute("startNumber"); $audioPTO = $audioSegmentTemplate->getAttribute("presentationTimeOffset"); $newAudioStartNumber = ceil(($deltaTimeASTTuneIn - $periodStart) * $audioTimescale / $audioSegmentDuration) + $audioStartNum; //file_put_contents ( "timelog.txt" , "new audio offset: " . ($deltaTimeASTTuneIn - $periodStart)*$audioTimescale/$audioSegmentDuration . "\r\n" , FILE_APPEND ); $audioOffsetUpdate = ($newAudioStartNumber - $audioStartNum) * $audioSegmentDuration / $audioTimescale; // Find the smaller update offset of audio and video, set the other to the smaller $offsetUpdate = min($videoOffsetUpdate, $audioOffsetUpdate); $newAudioPTO = round($offsetUpdate * $audioTimescale + $audioPTO); //Round, since PTO is int type $newVideoPTO = round($offsetUpdate * $videoTimescale + $videoPTO); //Round, since PTO is int type //The adjusted period start and duration governed by new audio/video offset above. $periods[$periodIndex]['node']->setAttribute("start", "PT" . round($offsetUpdate + $periodStart - $deltaTimeASTTuneIn, 4) . "S"); $remainingPeriodDuration = $duration - max($videoOffsetUpdate, $audioOffsetUpdate); $periods[$periodIndex]['node']->setAttribute("duration", "PT" . round($remainingPeriodDuration, 4) . "S"); //Update again the last saved values for the next iteration $lastPeriodStart = $offsetUpdate + $periodStart - $deltaTimeASTTuneIn; $lastPeriodDuration = $remainingPeriodDuration; $videoSegmentTemplate->setAttribute("presentationTimeOffset", $newVideoPTO); $videoSegmentTemplate->setAttribute("startNumber", $newVideoStartNumber); $audioSegmentTemplate->setAttribute("presentationTimeOffset", $newAudioPTO); $audioSegmentTemplate->setAttribute("startNumber", $newAudioStartNumber); //$periods[$periodIndex]['node']->removeChild ($periods[$periodIndex]['adaptationSet'][1]['node']); } //Set the updated MPD duration $MPDNode->setAttribute("mediaPresentationDuration", "PT" . round($lastPeriodStart + $lastPeriodDuration, 4) . "S"); /*if($AdSource == 1 && count($periods) >= 2) { $BaseURL = $dom->createElement( "BaseURL", "../Ad2/"); $TargetPeriod = $periods[count($periods) - 2]; $TargetAS = $TargetPeriod['adaptationSet'][0]['node']; $TargetPeriodNode = $TargetPeriod['node']; $TargetPeriodNode->insertBefore($BaseURL,$TargetAS); }*/ $BaseURL = $dom->createElement("BaseURL", $DASHContent); $TargetPeriodNode = $periods[0]['node']; $MPDNode->insertBefore($BaseURL, $TargetPeriodNode); $dom->save($PatchedMPD); corsHeader(); header("Content-Type: application/xml"); $toecho = $dom->saveXML(); echo $toecho; #file_put_contents ( "timelog.txt" , $latestFiles , FILE_APPEND ); $micro_date = microtime(); $date_array = explode(" ", $micro_date); $date = date("Y-m-d H:i:s", $date_array[1]); //file_put_contents ( "timelog.txt" , "Done:" . $date . $date_array[0] . " \r\n" , FILE_APPEND ); }
function generateFDTAndTimingSlS($videoSegmentDuration, $videoTimescale, $audioSegmentDuration, $audioTimescale, $mediaPresentationDuration) { global $mpdEFDTFile, $mpdTimingFile, $ASTUNIX, $mpdDoc, $mpdInstance, $mpdTOI, $slsFrequencyDuration, $slsObjectsSent; $mpdDoc = new DOMDocument('1.0'); $mpdDoc->formatOutput = true; $mpdInstance = $mpdDoc->createElement('EFDT-Instance'); $mpdInstance->setAttribute("TSI", "0"); $mpdInstance->setAttribute("IDRef", "file:///efdt_MPD.xml"); $mpdDoc->appendChild($mpdInstance); $fdtparametersMpd = $mpdDoc->createElement('FDT-Parameters'); $fdtparametersMpd->setAttribute("Expires", "32511974400"); $fdtparametersMpd->setAttribute("FEC-OTI-FEC-Encoding-ID", "0"); $fdtparametersMpd->setAttribute("FEC-OTI-Maximum-Source-Block-Length", "5000"); $fdtparametersMpd->setAttribute("FEC-OTI-Encoding-Symbol-Length", "1428"); $mpdInstance->appendChild($fdtparametersMpd); unlink($mpdTimingFile); $slsFrequencyDuration = 0.1; // How often to send the SLT segments in seconds // 0.1s means, for every 100ms we will send S-TSID, USBD (also, maybe the .mpd) segments. // In the SLT segments, we send .mpd related information as often as the smallest of audio or video segment. if ($videoSegmentDuration / $videoTimescale > $audioSegmentDuration / $audioTimescale) { $mpdFrequencyDuration = round($audioSegmentDuration / $audioTimescale, 1); // We have set to one degree of precision. } else { $mpdFrequencyDuration = round($videoSegmentDuration / $videoTimescale, 1); // We have set to one degree of precision. } $mpdFrequency = $mpdFrequencyDuration / $slsFrequencyDuration; //$mpdFrequency = 1; Set this to send .mpd all the time. // The mpd frequency in relative SLT frequency duration. // For example, mpdFrequency = 10 will imply that => for every 10 S-TSID and USBD segments we will send one MPD segment. //echo "The mpd frequency is ".$mpdFrequency.PHP_EOL; //echo "The SLT duration is" . $slsFrequencyDuration . PHP_EOL; //echo "MPD duration " . $mediaPresentationDuration . PHP_EOL; $duration = somehowPleaseGetDurationInFractionalSecondsBecuasePHPHasABug($mediaPresentationDuration); for ($mpdIndex = 1;; $mpdIndex++) { if ($mpdIndex * $slsFrequencyDuration >= $duration) { break; } // Keep sending the SLS segment until the end of the presentation duration file_put_contents($mpdTimingFile, $mpdTOI . " " . intval($ASTUNIX + $slsObjectsSent * $slsFrequencyDuration * 1000000) . "\n", FILE_APPEND); //$tmp_to_slt1 = $start + ($audioIndex - $audioStartNum - 1)*$slsFrequencyDuration; //$tmp_to_slt2 = ($audioIndex - $audioStartNum - 1); //$tmp_to_slt3 = $slsFrequencyDuration; //echo "SLT time = " . $start . "+" . $tmp_to_slt2 . "x" . $tmp_to_slt3. " = " . $tmp_to_slt1 . PHP_EOL; //$tmp_to_audio = ($start + ($audioIndex - $audioStartNum - 1)*$audioSegmentDuration/$audioTimescale + $deltaAudio); //$tmp_to_audio = ($audioIndex - $audioStartNum - 1)*$slsFrequencyDuration; //echo "Audio time = " . $tmp_to_audio . PHP_EOL; // In the same audio loop we will be writing out two files, // efdt_Audio.xml and efdt_MPD.xml //$file = $mpdDoc->createElement('File'); //$file->setAttribute("TOI",$mpdTOI);$mpdTOI++; //$file->setAttribute("Content-Location",'file:///usbd.xml'); //$file->setAttribute("Content-Length","USBDSizePlaceholder"); //$mpdInstance->appendChild($file); $file = $mpdDoc->createElement('File'); $file->setAttribute("TOI", $mpdTOI); $mpdTOI++; $file->setAttribute("Content-Location", 'file:///S-TSID.xml'); $file->setAttribute("Content-Length", "S_TSIDSizePlaceholder"); $mpdInstance->appendChild($file); $slsObjectsSent++; if ($slsObjectsSent % 3 == 0) { // Just send MPD after 3 S-TSID files sent. $file = $mpdDoc->createElement('File'); $file->setAttribute("TOI", $mpdTOI); $mpdTOI++; $file->setAttribute("Content-Location", 'file:///' . "MPDNamePlaceholder"); $file->setAttribute("Content-Length", "MPDSizePlaceholder"); $mpdInstance->appendChild($file); } } $mpdDoc->save($mpdEFDTFile); }
$periodStart; //Start of this period in the iteration $duration; //Duration of current period in the iteration $lastPeriodStart; //Period start of the last period in the iteration $lastPeriodDuration; //Period duration of the last period in iteration $responseToSend[1] = count($periods) - 1; for ($periodIndex = 0; $periodIndex < count($periods); $periodIndex++) { $periodStart = $periods[$periodIndex]['node']->getAttribute("start"); $duration = somehowPleaseGetDurationInFractionalSecondsBecuasePHPHasABug($periods[$periodIndex]['node']->getAttribute("duration")); if ($periodStart === '') { $periodStart = $lastPeriodStart + $lastPeriodDuration; } else { $periodStart = somehowPleaseGetDurationInFractionalSecondsBecuasePHPHasABug($periodStart); } //Convert Duration string to number if ($deltaTimeASTTuneIn < $periodStart) { $periods[$periodIndex]['node']->setAttribute("start", "PT" . round($lastPeriodStart + $lastPeriodDuration, 4) . "S"); //Set already for the next iteration $lastPeriodStart = $lastPeriodStart + $lastPeriodDuration; $responseToSend[] = $lastPeriodStart; $lastPeriodDuration = $duration; continue; } //Set already for the next iteration $lastPeriodStart = $periodStart; $lastPeriodDuration = $duration; if ($deltaTimeASTTuneIn > $periodStart + $duration) { $dom->documentElement->removeChild($periods[$periodIndex]['node']);
$profiles = $periods[0]['node']->parentNode->getAttribute("profiles"); $periods[0]['node']->parentNode->removeAttribute("profiles"); $periods[0]['node']->parentNode->setAttribute("availabilityStartTime", $AST_W3C); //Set AST to tune-in time $periods[0]['node']->parentNode->setAttribute("timeShiftBufferDepth", "PT60S"); $periods[0]['node']->parentNode->setAttribute("mediaPresentationDuration", $mediaPresentationDuration); $periods[0]['node']->parentNode->setAttribute("profiles", $profiles); $savedTotalDuration = 0; $restPeriodDuration = 0; $numVideoSegments = 0; $numAudioSegments = 0; $deltaAudio = 0; $deltaVideo = 0; for ($periodIndex = 0; $periodIndex < count($periods); $periodIndex++) { $durationInMPD = $periods[$periodIndex]['node']->getAttribute("duration"); $duration = somehowPleaseGetDurationInFractionalSecondsBecuasePHPHasABug($durationInMPD); if ($adInsertion) { $videoSegmentTemplate =& $periods[$periodIndex]['adaptationSet'][0]['representation'][0]['segmentTemplate']['node']; $audioSegmentTemplate =& $periods[$periodIndex]['adaptationSet'][1]['representation'][0]['segmentTemplate']['node']; $videoTimescale = $videoSegmentTemplate->getAttribute("timescale"); $videoSegmentDuration = $videoSegmentTemplate->getAttribute("duration"); $videoStartNum = $videoSegmentTemplate->getAttribute("startNumber"); $videoPTO = $videoSegmentTemplate->getAttribute("presentationTimeOffset"); $audioTimescale = $audioSegmentTemplate->getAttribute("timescale"); $audioSegmentDuration = $audioSegmentTemplate->getAttribute("duration"); $audioStartNum = $audioSegmentTemplate->getAttribute("startNumber"); $audioPTO = $audioSegmentTemplate->getAttribute("presentationTimeOffset"); if ($periodIndex == 0) { $adInsertionTime = getadInsertionTime($adInsertionTimeRequest, $videoSegmentDuration / $videoTimescale, 0, $audioSegmentDuration / $audioTimescale, 0); $numVideoSegments = round($adInsertionTime * $videoTimescale / $videoSegmentDuration); $numAudioSegments = round($adInsertionTime * $audioTimescale / $audioSegmentDuration);