public static function fixRed5WebcamFlv($flv_file_name, $new_file) { $flv_wrapper = new myFlvHandler($flv_file_name); $header = $flv_wrapper->getHeader(); // sort timestamps because of a bug in red5 webcam recording $sorted_tags = array(); $index = ""; $tag_count = 0; $last_pos = $flv_wrapper->pos; while ($tag = $flv_wrapper->getNextTag()) { // list($type, $size, $timestamp, $keyframe, $start_pos) = $tag; $index = sprintf("%010d%06d", $tag[self::TAG_FIELD_TIMESTAMP], ++$tag_count); $sorted_tags[$index] = $last_pos; $last_pos = $flv_wrapper->pos; } ksort($sorted_tags, SORT_NUMERIC); $fh = fopen($new_file, "wb"); fwrite($fh, $header); foreach ($sorted_tags as $timestamp => $pos) { $flv_wrapper->seek($pos); list($type, $size, $timestamp, $keyframe, $start_pos, $data) = $flv_wrapper->getNextTag(myFlvHandler::GET_NEXT_TAG_ALL); if ($size != self::TAG_WRAPPER_SIZE) { // dont write tag with no actual data fwrite($fh, $data); } } fclose($fh); }
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); }