function cache_file_frames($vid_file_path, $fps, $start = 0, $duration = -1, $options = array(), $switches = array(), $ffmpeg = '')
{
    $result = FALSE;
    if ($vid_file_path && $fps && $duration && file_exists($vid_file_path)) {
        $media_dimensions = get_file_info('dimensions', $vid_file_path, $ffmpeg);
        // target frame size, if different from video
        if (empty($options['dimensions'])) {
            $options['dimensions'] = $media_dimensions;
        }
        // video duration, for convenience
        if (empty($options['duration'])) {
            $options['duration'] = get_file_info('duration', $vid_file_path, $ffmpeg);
        }
        $dimensions = scale_even($options['dimensions']);
        $size = explode('x', $dimensions);
        if (empty($options['dimensions_dir'])) {
            $options['dimensions_dir'] = $dimensions;
        }
        $build_dir = frame_file_path($vid_file_path, $options['dimensions_dir'], $fps);
        $media_duration = $options['duration'];
        if ($build_dir && $media_duration && $media_dimensions && safe_path($build_dir)) {
            $orig_size = explode('x', $media_dimensions);
            if ($duration == -1) {
                $duration = $media_duration;
                if ($duration && $start) {
                    $duration -= $start;
                }
            }
            $fps = floatval($fps);
            $start = floatval($start);
            $start_frame = ceil($fps * $start);
            $duration = floatval($duration);
            $media_duration = floatval($media_duration);
            $media_frames = floor($media_duration * $fps);
            $digits = strlen($media_frames);
            $frames = 2 + ceil($fps * $duration);
            $files = array();
            $need_files = FALSE;
            $stop_frame = $start_frame + $frames;
            //	print 'cacheVideoFileFrames: ' . $start_frame . ' to ' . $stop_frame . "\n";
            for ($i = $start_frame; $i <= $stop_frame; $i++) {
                $seq_file = $build_dir . str_pad(1 + $i, $digits, '0', STR_PAD_LEFT) . '.jpg';
                $files[] = $seq_file;
                if (!$need_files && !file_exists($seq_file)) {
                    $need_files = TRUE;
                }
            }
            $result = array();
            $result['files'] = $files;
            $err = FALSE;
            if ($need_files) {
                $cmd = '';
                $cmd .= $ffmpeg;
                if ($start) {
                    $cmd .= ' -ss ' . $start;
                }
                $cmd .= ' -s ' . $orig_size[0] . 'x' . $orig_size[1];
                $cmd .= ' -i ' . $vid_file_path;
                $cmd .= ' -s ' . join('x', scale_proud($orig_size, $size));
                if ($switches) {
                    $cmd .= ' ' . stringFromSwitches($switches);
                }
                if (empty($switches['b']) && !empty($options['bitrate'])) {
                    $cmd .= ' -b ' . $options['bitrate'] . 'K';
                }
                $cmd .= ' -an -r ' . $fps . ' -vframes ' . $frames . ' ' . $build_dir . 'build%d.jpg';
                $result['command'] = $cmd;
                //print $cmd;
                $response = shell_command($cmd);
                $result['result'] = $response;
                if ($response) {
                    $last_file = '';
                    for ($i = 0; $i <= $frames; $i++) {
                        $frame_file = $build_dir . 'build' . (1 + $i) . '.jpg';
                        if (!file_exists($frame_file)) {
                            // ffmpeg doesn't always write every last frame
                            if ($last_file) {
                                // so we copy the last one repeatedly
                                if (!@copy($last_file, $frame_file)) {
                                    $err = TRUE;
                                }
                            } else {
                                $err = TRUE;
                            }
                        }
                        if (!$err) {
                            $seq_file = $files[$i];
                            $last_file = $seq_file;
                            if (!@rename($frame_file, $seq_file)) {
                                $err = TRUE;
                            }
                        }
                        if ($err) {
                            break;
                        }
                    }
                }
            }
            /*
            if (! $err)
            {
            	$result['duration'] = $frames / $fps;
            	$result['orig_dimensions'] = join('x', $orig_size);
            	$result['orig_extension'] = file_extension($vid_file_path);
            	$result['dimensions'] = join('x', $size);
            	$result['type'] = 'video';
            	$result['zeropadding'] = $digits;
            	$result['fps'] = $fps;
            	$result['orig_fps'] = get_file_info('fps', $vid_file_path, $ffmpeg);
            	$result['pattern'] = '%.jpg';
            	$result['icon'] = str_pad(1 + $start_frame + floor($frames / 2), $digits, '0', STR_PAD_LEFT) . '.jpg';
            }
            */
            if ($err) {
                $result['files'] = '';
            }
        }
    }
    return $result;
}
 function __cacheMedia()
 {
     $fps = floatval($this->_options['DecoderFPS']);
     // cache frames for all video files used in flash
     $z = sizeof($this->__mashData['timespans']);
     for ($i = 0; $i < $z; $i++) {
         $span = $this->__mashData['timespans'][$i];
         $span_start = $span[0];
         $span_end = $span[0] + $span[1];
         $clips = MovieMasher_Coder_Decoder::videoBetween($this->__mashData['clips'], $span_start, $span_end);
         $this->_progressStep('CacheFiles', 1 + round(30 * ($i / $z)), 'Caching Frames');
         foreach ($clips as $clip) {
             if (empty($clip['encode'])) {
                 continue;
             }
             $clip_trim = $this->__clipTrim($clip, $span_start, $span_end, $fps);
             if (!$clip_trim) {
                 throw new UnexpectedValueException('Could not get clip info: ' . $clip['id']);
             }
             $media_tag = $this->__mashData['media'][$clip['id']];
             $url = (string) $media_tag['source'];
             $url = absolute_url(end_with_slash($this->_options['CoderBaseURL']), $url);
             $ext = file_extension($url);
             if (!$ext) {
                 throw new UnexpectedValueException('Media source attributes must have a file extension: ' . $url);
             }
             $vid_file_path = cache_url($url, $this->_options['DirCache']);
             if (!$vid_file_path) {
                 throw new UnexpectedValueException('Could not cache URL: ' . $url);
             }
             $options = array();
             $orig_dimensions = get_file_info('dimensions', $vid_file_path, $this->_options['PathFFmpeg']);
             if (!$orig_dimensions) {
                 throw new RuntimeException('Could not read dimensions: ' . $vid_file_path);
             }
             $target_dimensions = scale_proud($orig_dimensions, $this->_options['DecoderDimensions']);
             if (!$target_dimensions) {
                 $target_dimensions = $this->_options['DecoderDimensions'];
             }
             $options['dimensions'] = $target_dimensions;
             $options['bitrate'] = 8000;
             // max?
             $options['dimensions_dir'] = $this->_options['DecoderDimensions'];
             $options['duration'] = $clip['duration'];
             $switches = array();
             $fps_secs = floatval(1) / $this->__mashData['quantize'];
             // we need an extra frame in mash's fps
             if ($clip_trim['trimstart']) {
                 $clip_trim['trimstart'] -= $fps_secs;
                 $clip_trim['trimlength'] += $fps_secs;
             }
             $clip_trim['trimlength'] += $fps_secs;
             if ($clip['speed'] != 1 || !empty($this->_options['DecoderCacheAllVideoFrames'])) {
                 // TODO: don't cache all the frames when speed isn't normal
                 $clip_trim['trimstart'] = 0;
                 $clip_trim['trimlength'] = -1;
             }
             //$this->log('Caching ' . ((string) $media_tag['label']) . ' @ ' . $this->_options['DecoderFPS'] . ' ' . $clip_trim['trimstart'] . ' -> ' . $clip_trim['trimlength']);
             $cached_frames = cache_file_frames($vid_file_path, $this->_options['DecoderFPS'], $clip_trim['trimstart'], $clip_trim['trimlength'], $options, $switches, $this->_options['PathFFmpeg']);
             if (!$cached_frames) {
                 throw new RuntimeException('Could not prepare to cache image sequence: ' . $url);
             }
             if (!empty($this->_options['Verbose']) && !empty($cached_frames['command'])) {
                 $this->log($cached_frames['command'] . "\n" . $cached_frames['result']);
             }
             if (!$cached_frames['files']) {
                 throw new RuntimeException('Could not cache image sequence: ' . $url);
             }
             $media_tag['url'] = 'http://' . $this->getOption('HostLocal') . '/mm.' . $this->getVersion() . '/cache/' . frame_file_path(url_file_path($url), $this->_options['DecoderDimensions'], $this->_options['DecoderFPS']);
         }
     }
     // cache the non-flash video content
     $zero = floatval(0);
     $cur_time = $zero;
     $z = sizeof($this->__mashData['timespans']);
     for ($i = 0; $i < $z; $i++) {
         if (floatgtr($this->__mashData['timespans'][$i][0], $cur_time)) {
             // first timespan doesn't start where the last left off, so there is something between
             $this->__cacheVisuals($cur_time, $this->__mashData['timespans'][$i][0]);
         }
         $cur_time = $this->__mashData['timespans'][$i][1];
         $this->_progressStep('CacheFiles', 30 + (1 + round(30 * ($i / $z))), 'Caching Visuals');
     }
     if (!floatcmp($cur_time, $this->__mashData['duration'])) {
         // last timespan doesn't end the mash, so there is stuff after it
         $this->__cacheVisuals($cur_time, $this->__mashData['duration']);
     }
     // cache audio and video with audio files
     $i = 0;
     $z = sizeof(array_keys($this->__mashData['cache_urls']));
     foreach ($this->__mashData['cache_urls'] as $url => $true) {
         $this->_progressStep('CacheFiles', 60 + (1 + round(30 * ($i++ / $z))), 'Caching Audio');
         $result = cache_url($url, $this->_options['DirCache']);
         if (!$result) {
             throw new RuntimeException('Could not cache audio: ' . $url);
         }
     }
     $this->_progressStep('CacheFiles', 100, 'Cached Media');
 }
 function __buildSequence($file_path)
 {
     $this->_progressStep('EncodeVideo', 5, 'Encoding Video');
     if (isset($this->_options['EncoderExtension'])) {
         $options = array();
         $orig_dimensions = get_file_info('dimensions', $file_path);
         if (!$orig_dimensions) {
             throw new RuntimeException('Could not read dimensions: ' . $file_path);
         }
         $target_dimensions = scale_proud($orig_dimensions, $this->_options['EncoderDimensions']);
         if (!$target_dimensions) {
             $target_dimensions = $this->_options['EncoderDimensions'];
         }
         $target_size = explode('x', $target_dimensions);
         $options['bitrate'] = round($this->_options['EncoderImageQuality'] / 100 * ($target_size[0] * $target_size[1] * 3 * $this->_options['EncoderFPS'] / 1024));
         $options['dimensions'] = $target_dimensions;
         $options['dimensions_dir'] = $this->_options['EncoderDimensions'];
         $switches = switchesFromString($this->_options['EncoderSwitches']);
         $cached_frames = cache_file_frames($file_path, $this->_options['EncoderFPS'], 0, -1, $options, $switches, $this->_options['PathFFmpeg']);
         if (!$cached_frames) {
             throw new RuntimeException('Could not prepare to cache image sequence: ' . $file_path);
         }
         if (!empty($this->_options['Verbose']) && !empty($cached_frames['command'])) {
             $this->log($cached_frames['command'] . "\n" . $cached_frames['result']);
         }
         if (!$cached_frames['files']) {
             throw new RuntimeException('Could not cache image sequence: ' . $file_path);
         }
         $this->_progressStep('EncodeVideo', 100, 'Encoded Video');
     }
 }