public function Extract() { if (!file_exists($this->filename)) { throw new BaseException(_T('Validation:Video file could not be found'), $this->filename); } $tools = Video_Tools::Get(); // Check to see if file info has been cached if ($this->cache != null) { return $this->cache; } // Execute mplayer to get video file information $output = shell_exec($tools->mplayer . ' -identify ' . escapeshellarg($this->filename) . ' -ao null -vo null -frames 0 2>&1'); $data = array(); // Extract video file information if (preg_match_all('~^ID_([A-Z0-9_]+)=(.*)~m', $output, $matches, PREG_SET_ORDER)) { foreach ($matches as $match) { $data[strtolower($match[1])] = $match[2]; } if (!isset($data['video_format'])) { throw new BaseException(_T('Validation:Video invalid format'), $this->filename, $output); } $data['video_frames'] = ceil($data['video_fps'] * $data['length']); $data['has_audio'] = isset($data['audio_id']) && isset($data['audio_rate']); // Length likely incorrect if ($data['length'] <= 0.0) { $data['length'] = null; if (!empty($tools->ffmpeg)) { $output = shell_exec($tools->ffmpeg . ' -i ' . escapeshellarg($this->filename) . ' 2>&1'); if (preg_match('~Duration: (\\d+:\\d+:\\d+.\\d+)~', $output, $matches)) { $data['length'] = self::ToSeconds($matches[1]); } } // All we can do now is guess if (empty($data['length'])) { $data['length'] = 5; } } // Video FPS likely incorrect if ($data['video_fps'] > 100) { $data['video_fps'] = null; if (!empty($tools->ffmpeg)) { $output = shell_exec($tools->ffmpeg . ' -i ' . escapeshellarg($this->filename) . ' 2>&1'); if (preg_match('~([0-9.]+) tbr~', $output, $matches)) { $data['video_fps'] = $matches[1]; $data['video_frames'] = ceil($data['video_fps'] * $data['length']); } } // All we can do now is guess if (empty($data['video_fps'])) { $data['video_fps'] = '29.97'; } } } // Cache it $this->cache = $data; return $data; }
private static function ConvertToMp4($vi, $filename, $directory, $vbitrate, $abitrate, $scale, $callback) { $tools = Video_Tools::Get(); $vbitrate = ($vbitrate <= 40 ? 'crf=' : 'ratetol=1.0:bitrate=') . $vbitrate; $tmp_file = File::Temporary($directory, self::EXTENSION_AVI); $cmd = (!empty($tools->nice) ? $tools->nice . ' ' : '') . $tools->mencoder . ' ' . escapeshellarg($filename) . ' ' . '-o ' . escapeshellarg($tmp_file) . ' ' . '-sws 9 ' . '-noskip ' . '-ovc x264 ' . '-x264encopts ' . escapeshellarg($vbitrate . ':bframes=1:me=umh:partitions=all:trellis=1:qp_step=4:qcomp=0.7:direct_pred=auto:keyint=300:threads=' . self::THREADS) . ' ' . '-vf ' . escapeshellarg((!empty($scale) ? $scale . ',' : '') . 'harddup') . ' ' . ($vi->has_audio ? '-oac faac -faacopts ' . escapeshellarg('br=' . $abitrate . ':mpeg=4:object=2') . ' -channels ' . self::CHANNELS_AAC . ' -srate ' . self::SAMPLE_RATE_AAC . ' ' : ' -nosound ') . '-ofps ' . escapeshellarg($vi->video_fps); if (self::ExecuteCmdAsync($cmd, $callback)) { File::Delete($tmp_file); throw new BaseException('Video conversion was interrupted by user request'); } // Verify video file generated if (filesize($tmp_file) == 0) { File::Delete($tmp_file); throw new BaseException('Unable to convert video file to H.264/AAC MP4', $filename, $output); } // Get the filenames of the extracted raw streams $directory = Dir::StripTrailingSlash($directory); $basename = basename($tmp_file, '.' . self::EXTENSION_AVI); $videofile = $directory . '/' . $basename . '_video.h264'; $audiofile_raw = $directory . '/' . $basename . '_audio.raw'; $audiofile = $directory . '/' . $basename . '_audio.aac'; // Extract the raw streams $cmd = (!empty($tools->nice) ? $tools->nice . ' ' : '') . $tools->mp4box . ' -aviraw video ' . escapeshellarg($tmp_file) . ' 2>&1'; self::Log($cmd); $output = shell_exec($cmd); self::Log($output); if (!file_exists($videofile)) { File::Delete($tmp_file); throw new BaseException('Unable to extract video from file using MP4Box', $videofile, $output); } $output_file = File::Temporary($directory, self::EXTENSION_MP4); // Process video files that do have an audio stream if ($vi->has_audio) { $cmd = (!empty($tools->nice) ? $tools->nice . ' ' : '') . $tools->mp4box . ' -aviraw audio ' . escapeshellarg($tmp_file) . ' 2>&1'; self::Log($cmd); $output = shell_exec($cmd); self::Log($output); if (!file_exists($audiofile_raw)) { File::Delete($tmp_file); File::Delete($videofile); throw new BaseException('Unable to extract audio from file using MP4Box', $audiofile_raw, $output); } rename($audiofile_raw, $audiofile); $cmd = (!empty($tools->nice) ? $tools->nice . ' ' : '') . $tools->mp4box . ' -add ' . escapeshellarg($videofile) . ' -add ' . escapeshellarg($audiofile) . ' -fps ' . escapeshellarg($vi->video_fps) . ' -inter 500 ' . escapeshellarg($output_file) . ' 2>&1'; self::Log($cmd); $output = shell_exec($cmd); self::Log($output); File::Delete($audiofile); } else { $cmd = (!empty($tools->nice) ? $tools->nice . ' ' : '') . $tools->mp4box . ' -add ' . escapeshellarg($videofile) . ' -fps ' . escapeshellarg($vi->video_fps) . ' -inter 500 ' . escapeshellarg($output_file) . ' 2>&1'; self::Log($cmd); $output = shell_exec($cmd); self::Log($output); } // Remove temporary files File::Delete($tmp_file); File::Delete($videofile); if (!file_exists($output_file)) { throw new BaseException('Unable to generate MP4 file using MP4Box', $output_file, $output); } return $output_file; }
public static function Grab($filename, $directory, $num_frames = 10, $quality = 90, $dimensions = false, $vi = null) { if (!is_dir($directory) || !is_writeable($directory)) { throw new BaseException('Output directory is missing or not writeable', $directory); } if (!file_exists($filename)) { throw new BaseException('Input file is missing', $filename); } // Get video info if it was not provided if (!$vi instanceof Video_Info) { $vi = new Video_Info($filename); $vi->Extract(); } $output = null; $tools = Video_Tools::Get(); $filter = self::ScaleFilter($dimensions, $vi->video_width, $vi->video_height); // Extract frames from short videos (less than 1 minute) if ($vi->length < 60) { self::Log('Using short video frame extraction method'); $framestep = floor($vi->video_frames / $num_frames); $end = floor($vi->length); $cmd = $tools->mplayer . ' -nosound' . ' -vo ' . escapeshellarg('jpeg:quality=' . $quality . ':outdir=' . $directory) . ' -endpos ' . escapeshellarg($end) . ' -sws 9' . ' -speed 100' . ' -vf ' . escapeshellarg('framestep=' . $framestep . ',' . $filter) . ' ' . escapeshellarg($filename) . ' 2>&1'; self::Log($cmd); $output = shell_exec($cmd); self::Log($output); $frames = glob($directory . '/*.' . JPG_EXTENSION); $generated = count($frames); self::Log('Total frames generated: ' . $generated); } else { self::Log('Using long video frame extraction method'); $start = min(ceil($vi->length * 0.01), 15); $end = floor($vi->length - $start); $interval = floor(($end - $start) / ($num_frames - 1)); // Attempt to use the quick frame grab method $cmd = $tools->mplayer . ' -nosound' . ' -vo ' . escapeshellarg('jpeg:quality=' . $quality . ':outdir=' . $directory) . ' -frames ' . escapeshellarg($num_frames) . ' -ss ' . escapeshellarg($start) . ' -sstep ' . escapeshellarg($interval) . ' -endpos ' . escapeshellarg($end) . ' -sws 9 ' . ' -vf ' . escapeshellarg($filter) . ' ' . escapeshellarg($filename) . ' 2>&1'; self::Log($cmd); $output = shell_exec($cmd); self::Log($output); $frames = glob($directory . '/*.' . JPG_EXTENSION); $generated = count($frames); self::Log('Total frames generated: ' . $generated); // Fall back to the slow frame grab method if ($generated < 1 || $num_frames > 1 && $generated == 1 || stristr($output, 'first frame is no keyframe')) { self::Log('Falling back to long video SLOW frame extraction method'); // Reset values and directory contents $generated = 0; if (is_array($frames)) { foreach ($frames as $frame) { unlink($frame); } } // Grab each frame individually for ($i = 0; $i < $num_frames; $i++) { $cmd = $tools->mplayer . ' -nosound' . ' -vo ' . escapeshellarg('jpeg:quality=' . $quality . ':outdir=' . $directory) . ' -frames 1 ' . ' -sws 9 ' . ' -ss ' . ($start + $i * $interval) . ' -vf ' . escapeshellarg($filter) . ' ' . escapeshellarg($filename) . ' 2>&1'; self::Log($cmd); $this_output = shell_exec($cmd); $output .= $this_output; self::Log($this_output); if (file_exists("{$directory}/00000001.jpg")) { $generated++; rename("{$directory}/00000001.jpg", $directory . sprintf('/%s%08d.jpg', $generated == 1 ? 't' : '', $generated)); } } if (file_exists("{$directory}/t00000001.jpg")) { rename("{$directory}/t00000001.jpg", "{$directory}/00000001.jpg"); } $frames = glob($directory . '/*.' . JPG_EXTENSION); self::Log('Total frames generated: ' . $generated); } } if ($generated < 1) { throw new BaseException('Could not grab frames from this video file', $filename, $output); } if (Video_Thumbnail::CanResize()) { $frames = Video_Thumbnail::DiscardBlack($directory); self::Log('Total frames generated after black frame removal: ' . count($frames)); } return $frames; }