public function write() { $amfSerializer = new FLV_Util_AMFSerialize(); $metadata_data = array(); // theses will be used as placeholders for dynamic data $metadata_data["canSeekToEnd"] = true; $metadata_data["duration"] = (int) ($this->duration / 1000); $metadata_data["timeOffset"] = 0; $metadata_data["bytesOffset"] = 0; // because of akamai seek limitation we want to limit metadata size to 16kb // 15K / 2 (times & positions arrays ) / 8 (bytes each) ~ 850 // we will skip keyframes in order to use at most 850 of them $kf_cnt = count($this->keyframeTimes); // if there are more than 1 keyframe per second and file size < 100MB its probably and edit flavor and we should maintain all KF // we serve the file from our server and not the cdn anyway because the cdn cant clip the file $lastKF = $lastKFPos = 0; if ($kf_cnt) { $lastKF = $this->keyframeTimes[$kf_cnt - 1] / 1000; $lastKFPos = $this->keyframeBytes[$kf_cnt - 1] / (1000 * 1000); } if ($kf_cnt > $lastKF && ($lastKFPos < 100 && !$this instanceof FlvMetadataAudio)) { $new_kf_step = 1; } else { $new_kf_step = max(1, $kf_cnt / 850); } $new_kf = array(); $new_fp = array(); $i = 0; $new_kf_pos = 0; while ($i < $kf_cnt) { $new_kf[] = $this->keyframeTimes[$i]; $new_fp[] = $this->keyframeBytes[$i]; $new_kf_pos += $new_kf_step; $i = floor($new_kf_pos); } $metadata_data["times"] = $new_kf; $metadata_data["filepositions"] = $new_fp; $data = $amfSerializer->serialize('onMetaData') . $amfSerializer->serialize($metadata_data); $data_len = strlen($data); $metatagSize = myFlvHandler::TAG_WRAPPER_SIZE + $data_len; for ($i = 0; $i < count($new_kf); ++$i) { $metadata_data["filepositions"][$i] += $metatagSize; } $res = $amfSerializer->serialize('onMetaData') . $amfSerializer->serialize($metadata_data); $meta_tag = myFlvHandler::createMetadataTag($data); kFile::safeFilePutContents($this->info_file_name, $meta_tag); // sync - OK $meta_tag = null; $amfSerializer = null; }
private static function createMetadataForStreamFlv($filePath, $assets, $streamInfo, $addPadding, $duration = null) { $metadata_content = self::getMetadataFromCache($filePath, $streamInfo, $addPadding); if ($metadata_content) { return $metadata_content; } list($sizeList, $timeList, $filePositionsList) = self::iterateAssets($assets, $streamInfo, $addPadding, false); $amfSerializer = new FLV_Util_AMFSerialize(); $metadata = array(); $metadata_data = array(); if ($duration) { $metadata_data["duration"] = (int) ($duration / 1000); } $metadata_data["bufferSizes"] = $sizeList; $metadata_data["times"] = $timeList; $metadata_data["filepositions"] = $filePositionsList; $res = $amfSerializer->serialize('onMetaData') . $amfSerializer->serialize($metadata_data); $data_len = strlen($res); // first create a metadata tag with it's real size - this will be the offset of all the rest of the tags // create a metadata tag $metadata_size = myFlvHandler::TAG_WRAPPER_SIZE + $data_len; $metatagEndOffset = myFlvHandler::getHeaderSize() + $metadata_size; // second - create the real metadata tag with the values of all the following tags with correct offsets for ($i = 0; $i < count($sizeList); ++$i) { $sizeList[$i] += $metatagEndOffset; } for ($i = 0; $i < count($filePositionsList); ++$i) { $filePositionsList[$i] += $metatagEndOffset; } $metadata_data["bufferSizes"] = $sizeList; $metadata_data["filepositions"] = $filePositionsList; $res = $amfSerializer->serialize('onMetaData') . $amfSerializer->serialize($metadata_data); $metadata_content = myFlvHandler::createMetadataTag($res); self::setMetadataInCache($filePath, $streamInfo, $addPadding, $metadata_content); return $metadata_content; }
/** * Returns the MetaData string * * @param array $newMetaData Array with new metadata * @param string $merge Merge original array with new one * * @return string New Metadata + next tag's previous size */ function createMetaData($newMetaData = false, $merge = true) { //if the metadata is pressent in the file merge it with the generated one $amf = new FLV_Util_AMFSerialize(); if (is_array($newMetaData)) { if ($merge && is_array($this->metadata)) { $newMetaData = array_merge($this->metadata, $newMetaData); } $metadata = $newMetaData; } else { $metadata = $this->metadata; } $metadata['metadatacreator'] = 'FLV Editor for PHP ' . FLV_VERSION . ' (Project: Flv4php)'; $serMeta = $amf->serialize('onMetaData') . $amf->serialize($metadata); $out = pack('N', 0); // PreviousTagSize $out .= pack('C', FLV_TAG_TYPE_DATA); // Type $out .= pack('Cn', "", strlen($serMeta)); // BodyLength assumes it's shorter than 64Kb $out .= pack('N', 0); // Time stamp (not used) $out .= pack('Cn', 0, 0); // Stream ID (not used) <---- WHERE IS THIS comming from $out .= $serMeta; // Metadata Body $out .= pack('N', strlen($serMeta) + 1 + 3 + 4 + 3); // PreviousTagSize return $out; }
function play($from = 0, $play_to = null) { fseek($this->fp, 0); // get original file header just in case it has any special flag echo fread($this->fp, $this->bodyOfs + 4); // output the metadata if available $meta = $this->getSegmentMetaData(); //$meta = $this->getMetaData(); if (!empty($meta)) { //serialize the metadata as an AMF stream include_once 'FLV/Util/AMFSerialize.php'; $amf = new FLV_Util_AMFSerialize(); $serMeta = $amf->serialize('onMetaData'); $serMeta .= $amf->serialize($meta); //Data tag mark $out = pack('C', FLV_Tag::TYPE_DATA); //Size of the data tag (BUG: limited to 64Kb) $out .= pack('Cn', 0, strlen($serMeta)); //Timestamp $out .= pack('N', 0); //StreamID $out .= pack('Cn', 0, 0); echo $out; echo $serMeta; // PrevTagSize for the metadata echo pack('N', strlen($serMeta) + strlen($out)); } $chunkSize = 4096; $skippedOrigMeta = empty($this->origMetaSize); //seek to offset point: fseek($this->fp, $from); while (!feof($this->fp)) { // if the original metadata is present and not yet skipped... if (!$skippedOrigMeta) { $pos = ftell($this->fp); // check if we are going to output it in this loop step if ($pos <= $this->origMetaOfs && $pos + $chunkSize > $this->origMetaOfs) { // output the bytes just before the original metadata tag if ($this->origMetaOfs - $pos > 0) { echo fread($this->fp, $this->origMetaOfs - $pos); } // position the file pointer just after the metadata tag fseek($this->fp, $this->origMetaOfs + $this->origMetaSize); $skippedOrigMeta = true; continue; } } if ($play_to != null) { if (ftell($this->fp) + $chunkSize > $play_to) { $read_amount = ftell($this->fp) + $chunkSize - $play_to; echo fread($this->fp, $read_amount); break; } } echo fread($this->fp, $chunkSize); } }
public function close() { if (!$this->creating) { return; } // save to disk $amfSerializer = new FLV_Util_AMFSerialize(); $metadata = array(); $metadata_data = array(); // theses will be used as placeholders for dynamic data $metadata_data["duration"] = (int) ($this->duration / 1000); $metadata_data["timeOffset"] = 0; $metadata_data["bytesOffset"] = 0; // TODO - remove !! //$metadata_data["bufferSizes"] = $this->sizeList; $metadata_data["times"] = $this->keyframeTimes; $metadata_data["filepositions"] = $this->keyframeBytes; $res = $amfSerializer->serialize('onMetaData') . $amfSerializer->serialize($metadata_data); // first create a metadata tag with it's real size - this will be the offset of all the rest of the tags // create a metadata tag $metatag = new FlvTag(); $metatag->setPrevTagSize(0); $metatag->setTagType(0x12); $metatag->setTimestamp(0); $metatag->setData($res); $metatagSize = $metatag->getSize(); /* // TODO - remove // second - create the real metadata tag with the values of all the following tags with correct offsets for ($i = 0 ; $i < count ( $this->sizeList ) ; ++$i ) { $this->sizeList[$i] += $metatagSize; } $metadata_data["bufferSizes"] = $this->sizeList; */ // fix the bytes' offsets for ($i = 0; $i < count($this->keyframeBytes); ++$i) { $metadata_data["filepositions"][$i] += $metatagSize; } $res = $amfSerializer->serialize('onMetaData') . $amfSerializer->serialize($metadata_data); $metatag->setData($res); $str = $metatag->dump(true); $amfSerializer = null; $metatag = null; file_put_contents($this->file_name, $str); }