Пример #1
0
 function DecodeFragment($frag, $fragNum, $opt = array())
 {
     $debug = $this->debug;
     $flv = false;
     extract($opt, EXTR_IF_EXISTS);
     $flvData = "";
     $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;
     }
     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;
         // 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($frag, $fragPos, $packetTS);
         }
         switch ($packetType) {
             case AUDIO:
                 if ($packetTS > $this->prevAudioTS - $this->fixWindow) {
                     $FrameInfo = ReadByte($frag, $fragPos + $this->tagHeaderLen);
                     $CodecID = ($FrameInfo & 0xf0) >> 4;
                     if ($CodecID == CODEC_ID_AAC) {
                         $AAC_PacketType = ReadByte($frag, $fragPos + $this->tagHeaderLen + 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($frag, $fragPos, $packetTS);
                             }
                         }
                         if (is_resource($flv)) {
                             $this->pAudioTagPos = ftell($flv);
                             $status = fwrite($flv, substr($frag, $fragPos, $totalTagLen), $totalTagLen);
                             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 .= substr($frag, $fragPos, $totalTagLen);
                             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 = $totalTagLen;
                     } 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($frag, $fragPos + $this->tagHeaderLen);
                     $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($frag, $fragPos + $this->tagHeaderLen + 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($frag, $fragPos + $this->tagHeaderLen + 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($frag, $fragPos, $packetTS);
                             }
                         }
                         if (is_resource($flv)) {
                             $this->pVideoTagPos = ftell($flv);
                             $status = fwrite($flv, substr($frag, $fragPos, $totalTagLen), $totalTagLen);
                             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 .= substr($frag, $fragPos, $totalTagLen);
                             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 = $totalTagLen;
                     } 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 == 10 or $packetType == 11) {
                     LogError("This stream is encrypted with Akamai DRM. Decryption of such streams isn't currently possible with this script.", 2);
                 } else {
                     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 (is_resource($flv)) {
         $this->filesize = ftell($flv) / (1024 * 1024);
         return true;
     } else {
         return $flvData;
     }
 }
Пример #2
0
             break;
         } else {
             LogDebug("Writing AVC sequence header");
             $AVC_HeaderWritten = true;
         }
     } else {
         if (!$AVC_HeaderWritten) {
             LogDebug(sprintf("%s\n" . $format, "Discarding video packet received before AVC sequence header", "VIDEO", $packetTS, $prevVideoTS, $packetSize));
             break;
         }
     }
 }
 if ($packetSize > 0) {
     $pts = $packetTS;
     if ($CodecID == CODEC_ID_AVC and $AVC_PacketType == AVC_NALU) {
         $cts = ReadInt24($flvTag, $tagPos + $tagHeaderLen + 2);
         $cts = $cts + 0xff800000 ^ 0xff800000;
         $pts = $packetTS + $cts;
         if ($cts != 0) {
             LogDebug("DTS: {$packetTS} CTS: {$cts} PTS: {$pts}");
         }
     }
     // 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 $prevAVC_Header))) {
         if ($prevVideoTS != INVALID_TIMESTAMP and $packetTS <= $prevVideoTS) {
             LogDebug(sprintf("%s\n" . $format, "Fixing video timestamp", "VIDEO", $packetTS, $prevVideoTS, $packetSize));
             $packetTS += FRAMEFIX_STEP / 5 + ($prevVideoTS - $packetTS);
             WriteFlvTimestamp($flvTag, $tagPos, $packetTS);
         }
     }
     $pVideoTagPos = ftell($flvOut);