Esempio n. 1
0
 function WriteFlvTimestamp(&$frag, $fragPos, $packetTS)
 {
     WriteInt24($frag, $fragPos + 4, $packetTS & 0xffffff);
     WriteByte($frag, $fragPos + 7, ($packetTS & 0xff000000) >> 24);
 }
function WriteMetadata($f4f, $flv = false)
{
    if (isset($f4f->media) and $f4f->media['metadata']) {
        $metadataSize = strlen($f4f->media['metadata']);
        WriteByte($metadata, 0, SCRIPT_DATA);
        WriteInt24($metadata, 1, $metadataSize);
        WriteInt24($metadata, 4, 0);
        WriteInt32($metadata, 7, 0);
        $metadata = implode("", $metadata) . $f4f->media['metadata'];
        WriteByte($metadata, $f4f->tagHeaderLen + $metadataSize - 1, 0x9);
        WriteInt32($metadata, $f4f->tagHeaderLen + $metadataSize, $f4f->tagHeaderLen + $metadataSize);
        if (is_resource($flv)) {
            fwrite($flv, $metadata, $f4f->tagHeaderLen + $metadataSize + $f4f->prevTagSize);
            return true;
        } else {
            return $metadata;
        }
    }
    return false;
}
Esempio n. 3
0
 function DecodeFragment($frag, $fragNum, $opt = array())
 {
     $ad = null;
     $flvFile = null;
     $flvWrite = true;
     extract($opt, EXTR_IF_EXISTS);
     $debug = $this->debug;
     if ($this->decoderTest) {
         $debug = false;
     }
     $flvData = "";
     $flvTag = "";
     $fragPos = 0;
     $packetTS = 0;
     $fragLen = strlen($frag);
     if (!$this->VerifyFragment($frag)) {
         LogInfo("Skipping fragment number " . $fragNum);
         return false;
     }
     while ($fragPos < $fragLen) {
         ReadBoxHeader($frag, $fragPos, $boxType, $boxSize);
         if ($boxType == "mdat") {
             $fragLen = $fragPos + $boxSize;
             break;
         }
         $fragPos += $boxSize;
     }
     /**
      * Initialize Akamai decryptor
      * @var AkamaiDecryptor $ad
      */
     $ad->debug = $this->debug;
     $ad->decryptorTest = $this->decoderTest;
     $ad->InitDecryptor();
     LogDebug(sprintf("\nFragment %d:\n" . $this->format . "%-16s", $fragNum, "Type", "CurrentTS", "PreviousTS", "Size", "Position"), $debug);
     while ($fragPos < $fragLen) {
         $packetType = ReadByte($frag, $fragPos);
         $packetSize = ReadInt24($frag, $fragPos + 1);
         $packetTS = ReadInt24($frag, $fragPos + 4);
         $packetTS = $packetTS | ReadByte($frag, $fragPos + 7) << 24;
         if ($packetTS & 0x80000000) {
             $packetTS &= 0x7fffffff;
         }
         $totalTagLen = $this->tagHeaderLen + $packetSize + $this->prevTagSize;
         $tagHeader = substr($frag, $fragPos, $this->tagHeaderLen);
         $tagData = substr($frag, $fragPos + $this->tagHeaderLen, $packetSize);
         // Remove Akamai encryption
         if ($packetType == AKAMAI_ENC_AUDIO or $packetType == AKAMAI_ENC_VIDEO) {
             $opt['auth'] = $this->media['queryString'];
             $opt['baseUrl'] = $this->baseUrl;
             $tagData = $ad->Decrypt($tagData, 0, $opt);
             $packetType = $packetType == AKAMAI_ENC_AUDIO ? AUDIO : VIDEO;
             $packetSize = strlen($tagData);
             WriteByte($tagHeader, 0, $packetType);
             WriteInt24($tagHeader, 1, $packetSize);
             $this->sessionID = $ad->sessionID;
         }
         // Try to fix the odd timestamps and make them zero based
         $currentTS = $packetTS;
         $lastTS = $this->prevVideoTS >= $this->prevAudioTS ? $this->prevVideoTS : $this->prevAudioTS;
         $fixedTS = $lastTS + FRAMEFIX_STEP;
         if ($this->baseTS == INVALID_TIMESTAMP and ($packetType == AUDIO or $packetType == VIDEO)) {
             $this->baseTS = $packetTS;
         }
         if ($this->baseTS > 1000 and $packetTS >= $this->baseTS) {
             $packetTS -= $this->baseTS;
         }
         if ($lastTS != INVALID_TIMESTAMP) {
             $timeShift = $packetTS - $lastTS;
             if ($timeShift > $this->fixWindow) {
                 LogDebug("Timestamp gap detected: PacketTS=" . $packetTS . " LastTS=" . $lastTS . " Timeshift=" . $timeShift, $debug);
                 if ($this->baseTS < $packetTS) {
                     $this->baseTS += $timeShift - FRAMEFIX_STEP;
                 } else {
                     $this->baseTS = $timeShift - FRAMEFIX_STEP;
                 }
                 $packetTS = $fixedTS;
             } else {
                 $lastTS = $packetType == VIDEO ? $this->prevVideoTS : $this->prevAudioTS;
                 if ($packetTS < $lastTS - $this->fixWindow) {
                     if ($this->negTS != INVALID_TIMESTAMP and $packetTS + $this->negTS < $lastTS - $this->fixWindow) {
                         $this->negTS = INVALID_TIMESTAMP;
                     }
                     if ($this->negTS == INVALID_TIMESTAMP) {
                         $this->negTS = $fixedTS - $packetTS;
                         LogDebug("Negative timestamp detected: PacketTS=" . $packetTS . " LastTS=" . $lastTS . " NegativeTS=" . $this->negTS, $debug);
                         $packetTS = $fixedTS;
                     } else {
                         if ($packetTS + $this->negTS <= $lastTS + $this->fixWindow) {
                             $packetTS += $this->negTS;
                         } else {
                             $this->negTS = $fixedTS - $packetTS;
                             LogDebug("Negative timestamp override: PacketTS=" . $packetTS . " LastTS=" . $lastTS . " NegativeTS=" . $this->negTS, $debug);
                             $packetTS = $fixedTS;
                         }
                     }
                 }
             }
         }
         if ($packetTS != $currentTS) {
             WriteFlvTimestamp($tagHeader, 0, $packetTS);
         }
         switch ($packetType) {
             case AUDIO:
                 if ($packetTS > $this->prevAudioTS - $this->fixWindow) {
                     $FrameInfo = ReadByte($tagData, 0);
                     $CodecID = ($FrameInfo & 0xf0) >> 4;
                     if ($CodecID == CODEC_ID_AAC) {
                         $AAC_PacketType = ReadByte($tagData, 1);
                         if ($AAC_PacketType == AAC_SEQUENCE_HEADER) {
                             if ($this->AAC_HeaderWritten) {
                                 LogDebug(sprintf("%s\n" . $this->format, "Skipping AAC sequence header", "AUDIO", $packetTS, $this->prevAudioTS, $packetSize), $debug);
                                 break;
                             } else {
                                 LogDebug("Writing AAC sequence header", $debug);
                                 $this->AAC_HeaderWritten = true;
                             }
                         } else {
                             if (!$this->AAC_HeaderWritten) {
                                 LogDebug(sprintf("%s\n" . $this->format, "Discarding audio packet received before AAC sequence header", "AUDIO", $packetTS, $this->prevAudioTS, $packetSize), $debug);
                                 break;
                             }
                         }
                     }
                     if ($packetSize > 0) {
                         // Check for packets with non-monotonic audio timestamps and fix them
                         if (!($CodecID == CODEC_ID_AAC and ($AAC_PacketType == AAC_SEQUENCE_HEADER or $this->prevAAC_Header))) {
                             if ($this->prevAudioTS != INVALID_TIMESTAMP and $packetTS <= $this->prevAudioTS) {
                                 LogDebug(sprintf("%s\n" . $this->format, "Fixing audio timestamp", "AUDIO", $packetTS, $this->prevAudioTS, $packetSize), $debug);
                                 $packetTS += FRAMEFIX_STEP / 5 + ($this->prevAudioTS - $packetTS);
                                 WriteFlvTimestamp($tagHeader, 0, $packetTS);
                             }
                         }
                         $flvTag = $tagHeader . $tagData;
                         $flvTagLen = strlen($flvTag);
                         WriteInt32($flvTag, $flvTagLen, $flvTagLen);
                         $flvTagLen = strlen($flvTag);
                         if ($flvWrite and is_resource($flvFile)) {
                             $this->pAudioTagPos = ftell($flvFile);
                             $status = fwrite($flvFile, $flvTag, $flvTagLen);
                             if (!$status) {
                                 LogError("Failed to write flv data to file");
                             }
                             if ($debug) {
                                 LogDebug(sprintf($this->format . "%-16s", "AUDIO", $packetTS, $this->prevAudioTS, $packetSize, $this->pAudioTagPos));
                             }
                         } else {
                             $flvData .= $flvTag;
                             if ($debug) {
                                 LogDebug(sprintf($this->format, "AUDIO", $packetTS, $this->prevAudioTS, $packetSize));
                             }
                         }
                         if ($CodecID == CODEC_ID_AAC and $AAC_PacketType == AAC_SEQUENCE_HEADER) {
                             $this->prevAAC_Header = true;
                         } else {
                             $this->prevAAC_Header = false;
                         }
                         $this->prevAudioTS = $packetTS;
                         $this->pAudioTagLen = $flvTagLen;
                     } else {
                         LogDebug(sprintf("%s\n" . $this->format, "Skipping small sized audio packet", "AUDIO", $packetTS, $this->prevAudioTS, $packetSize), $debug);
                     }
                 } else {
                     LogDebug(sprintf("%s\n" . $this->format, "Skipping audio packet in fragment " . $fragNum, "AUDIO", $packetTS, $this->prevAudioTS, $packetSize), $debug);
                 }
                 if (!$this->audio) {
                     $this->audio = true;
                 }
                 break;
             case VIDEO:
                 if ($packetTS > $this->prevVideoTS - $this->fixWindow) {
                     $FrameInfo = ReadByte($tagData, 0);
                     $FrameType = ($FrameInfo & 0xf0) >> 4;
                     $CodecID = $FrameInfo & 0xf;
                     if ($FrameType == FRAME_TYPE_INFO) {
                         LogDebug(sprintf("%s\n" . $this->format, "Skipping video info frame", "VIDEO", $packetTS, $this->prevVideoTS, $packetSize), $debug);
                         break;
                     }
                     if ($CodecID == CODEC_ID_AVC) {
                         $AVC_PacketType = ReadByte($tagData, 1);
                         if ($AVC_PacketType == AVC_SEQUENCE_HEADER) {
                             if ($this->AVC_HeaderWritten) {
                                 LogDebug(sprintf("%s\n" . $this->format, "Skipping AVC sequence header", "VIDEO", $packetTS, $this->prevVideoTS, $packetSize), $debug);
                                 break;
                             } else {
                                 LogDebug("Writing AVC sequence header", $debug);
                                 $this->AVC_HeaderWritten = true;
                             }
                         } else {
                             if (!$this->AVC_HeaderWritten) {
                                 LogDebug(sprintf("%s\n" . $this->format, "Discarding video packet received before AVC sequence header", "VIDEO", $packetTS, $this->prevVideoTS, $packetSize), $debug);
                                 break;
                             }
                         }
                     }
                     if ($packetSize > 0) {
                         $pts = $packetTS;
                         if ($CodecID == CODEC_ID_AVC and $AVC_PacketType == AVC_NALU) {
                             $cts = ReadInt24($tagData, 2);
                             $cts = $cts + 0xff800000 ^ 0xff800000;
                             $pts = $packetTS + $cts;
                             if ($cts != 0) {
                                 LogDebug("DTS: {$packetTS} CTS: {$cts} PTS: {$pts}", $debug);
                             }
                         }
                         // Check for packets with non-monotonic video timestamps and fix them
                         if (!($CodecID == CODEC_ID_AVC and ($AVC_PacketType == AVC_SEQUENCE_HEADER or $AVC_PacketType == AVC_SEQUENCE_END or $this->prevAVC_Header))) {
                             if ($this->prevVideoTS != INVALID_TIMESTAMP and $packetTS <= $this->prevVideoTS) {
                                 LogDebug(sprintf("%s\n" . $this->format, "Fixing video timestamp", "VIDEO", $packetTS, $this->prevVideoTS, $packetSize), $debug);
                                 $packetTS += FRAMEFIX_STEP / 5 + ($this->prevVideoTS - $packetTS);
                                 WriteFlvTimestamp($tagHeader, 0, $packetTS);
                             }
                         }
                         $flvTag = $tagHeader . $tagData;
                         $flvTagLen = strlen($flvTag);
                         WriteInt32($flvTag, $flvTagLen, $flvTagLen);
                         $flvTagLen = strlen($flvTag);
                         if ($flvWrite and is_resource($flvFile)) {
                             $this->pVideoTagPos = ftell($flvFile);
                             $status = fwrite($flvFile, $flvTag, $flvTagLen);
                             if (!$status) {
                                 LogError("Failed to write flv data to file");
                             }
                             if ($debug) {
                                 LogDebug(sprintf($this->format . "%-16s", "VIDEO", $packetTS, $this->prevVideoTS, $packetSize, $this->pVideoTagPos));
                             }
                         } else {
                             $flvData .= $flvTag;
                             if ($debug) {
                                 LogDebug(sprintf($this->format, "VIDEO", $packetTS, $this->prevVideoTS, $packetSize));
                             }
                         }
                         if ($CodecID == CODEC_ID_AVC and $AVC_PacketType == AVC_SEQUENCE_HEADER) {
                             $this->prevAVC_Header = true;
                         } else {
                             $this->prevAVC_Header = false;
                         }
                         $this->prevVideoTS = $packetTS;
                         $this->pVideoTagLen = $flvTagLen;
                     } else {
                         LogDebug(sprintf("%s\n" . $this->format, "Skipping small sized video packet", "VIDEO", $packetTS, $this->prevVideoTS, $packetSize), $debug);
                     }
                 } else {
                     LogDebug(sprintf("%s\n" . $this->format, "Skipping video packet in fragment " . $fragNum, "VIDEO", $packetTS, $this->prevVideoTS, $packetSize), $debug);
                 }
                 if (!$this->video) {
                     $this->video = true;
                 }
                 break;
             case SCRIPT_DATA:
                 break;
             default:
                 if ($packetType == 40 or $packetType == 41) {
                     LogError("This stream is encrypted with FlashAccess DRM. Decryption of such streams isn't currently possible with this script.", 2);
                 } else {
                     LogInfo("Unknown packet type " . $packetType . " encountered! Unable to process fragment " . $fragNum);
                     break 2;
                 }
         }
         $fragPos += $totalTagLen;
     }
     $this->duration = round($packetTS / 1000, 0);
     if ($flvWrite and is_resource($flvFile)) {
         $this->filesize = ftell($flvFile) / (1024 * 1024);
         return true;
     } else {
         return $flvData;
     }
 }