public function dump($chunk_size, $from_byte, $to_byte, $only_audio = false, $dump_from_byte = 0, $range_from = 0, $range_to = 2147483647, $cuepoint_time = 0, $cuepoint_pos = 0) { $this->validateMetadata($only_audio); // find out if there are any audio tags when creating the header for a video dump // otherwise flash may act in a wierd way when playing the video. // for an audio dump assume there are audio tags (otherwise the dump is empty) if ($only_audio) { $has_vtags = false; $has_atags = true; } else { $vinfo = $this->validateHelper(false); $ainfo = $this->validateHelper(true); $has_vtags = $vinfo->hasTags(); $has_atags = $ainfo->hasTags(); } $ofs = 0; // we always dump the header even if the request is for a partial flv (for scrubbing) $header = self::createFlvHeader($has_vtags, $has_atags); if ($this->echo_range($header, $ofs, $range_from, $range_to)) { return; } $metadata = $this->getMetadata($only_audio); // we either dump the whole metadata or nothing at all - no need to dump partial metadata $dumped_offset = self::FLV_HEADER_SIZE + strlen($metadata); if ($dump_from_byte < $dumped_offset) { if ($this->echo_range($metadata, $ofs, $range_from, $range_to)) { return; } } if ($only_audio) { // dont support scrubbing for audio for now $this->seek($from_byte); while ($this->pos < $to_byte && ($data = $this->getNextTag(myFlvHandler::GET_NEXT_TAG_DATA))) { $type = unpack("C", $data); if ($type[1] == myFlvHandler::TAG_TYPE_AUDIO) { if ($this->echo_range($data, $ofs, $range_from, $range_to)) { return; } } } } else { // start serving from the requested position (after substracting header and metadata size) $from_byte += max(0, $dump_from_byte - $dumped_offset); $file_seek_pos = $from_byte; $file_need_seek = true; $dump_ranges = array(); // in case of a cuepoint dump three parts - before cuepoint, the cuepoint itself, after cuepoint if ($cuepoint_pos) { $dump_ranges[] = array("type" => "data", "from" => $from_byte, "to" => $cuepoint_pos); $dump_ranges[] = array("type" => "cuepoint"); $dump_ranges[] = array("type" => "data", "from" => $cuepoint_pos, "to" => $to_byte); } else { $dump_ranges[] = array("type" => "data", "from" => $from_byte, "to" => $to_byte); } foreach ($dump_ranges as $range) { if ($range["type"] == "cuepoint") { $amfSerializer = new FLV_Util_AMFSerialize(); $cuepoint = $amfSerializer->serialize('onCuePoint') . $amfSerializer->serialize(array()); $cuepoint_tag = myFlvHandler::createMetadataTag($cuepoint); $cuepoint_tag = myFlvHandler::dumpTag($cuepoint_tag, $cuepoint_time); if ($this->echo_range($cuepoint_tag, $ofs, $range_from, $range_to)) { return; } } else { $dump_from = $range["from"]; $dump_to = $range["to"]; $dump_chunk_size = $chunk_size; while ($dump_from < $dump_to) { if ($dump_to - $dump_from < $dump_chunk_size) { // this is for the very last chunk - make it as big of the what's left $dump_chunk_size = $dump_to - $dump_from; } // check if we need to dump anything otherwise dont read file if ($this->check_range($dump_chunk_size, $ofs, $range_from, $range_to)) { if ($file_need_seek) { fseek($this->fh, $file_seek_pos); } $content = fread($this->fh, $dump_chunk_size); $file_need_seek = false; } else { $content = ""; $file_need_seek = true; } $file_seek_pos += $dump_chunk_size; if ($this->echo_range($content, $ofs, $range_from, $range_to, $dump_chunk_size)) { return; } $dump_from += $dump_chunk_size; } } } } }
private static function iterateAssets($assets, $streamInfo, $addPadding, $echo) { if ($assets == null) { return null; } $total_bytes = 0; list($filePath, $timeline, $streamNum, $fileTimestamp) = $streamInfo; $lastTimeStamp = 0; $sizeList = array(); $timeList = array(); $filePositionsList = array(); $sizeListTime = 1000; $dump_type = $echo ? myFlvHandler::GET_NEXT_TAG_ALL : myFlvHandler::GET_NEXT_TAG_META; if ($addPadding) { $silence_tag_data = '080000d1000000000000002efffb50c40003c00001a400000020000034800000' . '044c414d45332e39382e32555555555555555555555555555555555555555555' . '5555555555555555555555555555555555555555554c414d45332e39382e3255' . '5555555555555555555555555555555555555555555555555555555555555555' . '5555555555555555555555555555555555555555555555555555555555555555' . '5555555555555555555555555555555555555555555555555555555555555555' . '55555555555555555555555555555555555555555555555555555555000000dc'; $silence_tag = pack("H*", $silence_tag_data); } foreach ($assets as $asset) { // in the future this will always be true,for backward compatibility - make sure will work OK if // there is no edit flavor $use_multi_flavor = $asset['file_name_edit'] && $asset['file_name_data'] && $timeline == "video"; $number_of_iterations = $use_multi_flavor ? 2 : 1; for ($i = 0; $i < $number_of_iterations; $i++) { if ($use_multi_flavor) { if ($i == 0) { $file_name = $asset['file_name_edit']; // edit flavor with original attributes $start_byte = $asset['start_byte']; $end_byte = $asset['end_byte']; } else { $file_name = $asset['file_name_data']; // original file name with '_play' attributes $start_byte = $asset['start_byte_play']; $end_byte = $asset['end_byte_play']; } KalturaLog::log("myFlvStreamer:: ({$i}) using {$file_name} ({$start_byte} - {$end_byte})"); } else { $file_name = $asset['file_name_data']; // original file name with original attributes $start_byte = $asset['start_byte']; $end_byte = $asset['end_byte']; } if ($start_byte >= $end_byte) { // edit flavor not used (play clip was used from keyframe) continue; } $first_frame = true; KalturaLog::log("playing file [{$file_name}] from [{$start_byte}] to [{$end_byte}]"); // if should echo - don't optimize (we need the actual data // if should not echo - use optimization $flv_wrapper = new myFlvHandler($file_name); $flv_wrapper->seek($start_byte); $flv_tag_prev = $flv_wrapper->getNextTag($dump_type); if ($flv_tag_prev == NULL) { continue; } KalturaLog::log("file [{$file_name}]: flv_tag_prev is not null"); $videoTimeline = $timeline == "video"; while ($flv_tag_prev[myFlvHandler::TAG_FIELD_POS] < $end_byte) { $flv_current_tag = $flv_wrapper->getNextTag($dump_type); // dont write the last tag as we dont know its duration and we wont be able to give the next chunk // a percise timestamp if ($flv_current_tag == NULL) { break; } $prev_tag_type = $flv_tag_prev[myFlvHandler::TAG_FIELD_TYPE]; if ($prev_tag_type != myFlvHandler::TAG_TYPE_METADATA) { $prev_tag_timestamp = $flv_tag_prev[myFlvHandler::TAG_FIELD_TIMESTAMP]; if ($first_frame) { $first_frame = false; $lastTimeStamp -= $prev_tag_timestamp; } // if the timeline is video dump both audio and video data chunks // otherwise (timeline is audio / voice) dump only audio data chunks if ($videoTimeline || $prev_tag_type == myFlvHandler::TAG_TYPE_AUDIO) { $currentTimeStamp = $prev_tag_timestamp + $lastTimeStamp; if ($echo) { echo myFlvHandler::dumpTag($flv_tag_prev[myFlvHandler::TAG_FIELD_DATA], $lastTimeStamp); } else { $total_bytes += $flv_tag_prev[myFlvHandler::TAG_FIELD_SIZE]; // we accumulate 3 types of metadata // filepositions and times - VIDEOS - each keyframe. AUDIO - each second (used for scrubbing validation) // bufferTimes - once per second (used for buffering calculations) if ($videoTimeline) { if ($flv_tag_prev[myFlvHandler::TAG_FIELD_KEYFRAME]) { $filePositionsList[] = $total_bytes; $timeList[] = $currentTimeStamp; } } if ($sizeListTime < $currentTimeStamp) { $sizeList[] = $total_bytes; if (!$videoTimeline) { $filePositionsList[] = $total_bytes; $timeList[] = $sizeListTime; } $sizeListTime += 1000; } } } } $flv_tag_prev = $flv_current_tag; } $lastTimeStamp += $flv_tag_prev[myFlvHandler::TAG_FIELD_TIMESTAMP]; } // add silence padding between clips in order to give the flash player enough time to // execute some logic in between. if ($addPadding) { $paddingTags = self::PADDING_TAGS; while ($paddingTags--) { if ($echo) { echo myFlvHandler::dumpTag($silence_tag, $lastTimeStamp); } $lastTimeStamp += self::PADDING_TAG_TIME; $total_bytes += self::PADDING_TAG_SIZE; } } } return array($sizeList, $timeList, $filePositionsList); }