/** * Convert a stored video file to flashvideo * * @param array $pParamHash * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function mime_video_converter(&$pParamHash, $pOnlyGetParameters = FALSE) { global $gBitSystem; // video conversion can take a while ini_set("max_execution_time", "1800"); $ret = FALSE; if (@BitBase::verifyId($pParamHash['attachment_id'])) { // we might have some attachment preferences set if this is an update LibertyMime::expungeAttachmentPreferences($pParamHash['attachment_id']); // these are set in the liberty plugin admin screen $ffmpeg = trim($gBitSystem->getConfig('ffmpeg_path', shell_exec('which ffmpeg'))); $width = trim($gBitSystem->getConfig('mime_video_width', 320)); $begin = date('U'); $log = $actionLog = array(); if (!is_executable($ffmpeg)) { $log['time'] = date('Y-M-d - H:i:s O'); $log['duration'] = 0; $log['message'] = 'ERROR: ffmpeg does not seem to be available on your system at: ' . $ffmpeg . ' Please set the path to ffmpeg in the liberty plugin administration screen.'; $actionLog['log_message'] = "ERROR: ffmpeg does not seem to be available on your system at: '{$ffmpeg}' Please set the path to ffmpeg in the liberty plugin administration screen."; } else { // this is the codec we'll use - currently this might be: flv, h264, h264-2pass $codec = $gBitSystem->getConfig("mime_video_video_codec", "flv"); $source = STORAGE_PKG_PATH . $pParamHash['upload']['dest_branch'] . $pParamHash['upload']['name']; $destPath = dirname($source); // set some default values if ffpeg-php isn't available or fails $default['aspect'] = 4 / 3; $default['video_width'] = $width; $default['video_height'] = round($width / 4 * 3); $default['size'] = "{$default['video_width']}x{$default['video_height']}"; $default['offset'] = '00:00:10'; if (extension_loaded('ffmpeg')) { // we silence these calls since they might spew errors $movie = @new ffmpeg_movie($source); $info = array('vcodec' => @$movie->getVideoCodec(), 'duration' => round(@$movie->getDuration()), 'width' => @$movie->getFrameWidth(), 'height' => @$movie->getFrameHeight(), 'video_bitrate' => @$movie->getVideoBitRate(), 'acodec' => @$movie->getAudioCodec(), 'audio_bitrate' => @$movie->getAudioBitRate(), 'audio_samplerate' => @$movie->getAudioSampleRate()); // make sure audio sample rate is valid if (!empty($info['audio_samplerate']) && !in_array($info['audio_samplerate'], array(11025, 22050, 44100))) { unset($info['audio_samplerate']); } } else { // alternative method using ffmpeg to fetch source dimensions $command = "{$ffmpeg} -i " . escapeshellarg($source) . ' 2>&1'; exec($command, $output, $status); if (!preg_match('/Stream #(?:[0-9\\.]+)(?:.*)\\: Video: (?P<videocodec>.*) (?P<width>[0-9]*)x(?P<height>[0-9]*)/', implode('\\n', $output), $matches)) { preg_match('/Could not find codec parameters \\(Video: (?P<videocodec>.*) (?P<width>[0-9]*)x(?P<height>[0-9]*)\\)/', implode('\\n', $output), $matches); } if (!empty($matches['width']) && !empty($matches['height'])) { $info['width'] = $matches['width']; $info['height'] = $matches['height']; } } // our player supports flv and h264 so we might as well use the default if (!$gBitSystem->isFeatureActive('mime_video_force_encode') && !empty($info) && ($info['vcodec'] == 'h264' && (empty($info['acodec']) || $info['acodec'] == 'mpeg4aac' || $info['acodec'] == 'aac') || $info['vcodec'] == 'flv' && (empty($info['acodec']) || $info['acodec'] == 'mp3'))) { // work out what the target filename is $extension = $info['vcodec'] == "flv" ? "flv" : "mp4"; $dest_file = $destPath . "/flick.{$extension}"; // if the video can be processed by ffmpeg-php, width and height are greater than 1 if (!empty($info['width']) && $info['width'] > 1) { $info['aspect'] = $info['width'] / $info['height']; $info['offset'] = strftime("%T", round($info['duration'] / 5 - 60 * 60)); } else { $info = $default; } // store prefs and create thumbnails LibertyMime::expungeMetaData($pParamHash['attachment_id']); LibertyMime::storeMetaData($pParamHash['attachment_id'], 'Video', $info); mime_video_create_thumbnail($source, $info['offset']); if (!is_file($dest_file) && !link($source, $dest_file)) { copy($source, $dest_file); } mime_video_fix_streaming($dest_file); $log['message'] = 'SUCCESS: Converted to flash video'; $actionLog['log_message'] = "Video file was successfully uploaded and thumbnails extracted."; $ret = TRUE; } else { // work out what the target filename is $extension = $codec == "flv" ? "flv" : "mp4"; $dest_file = $destPath . "/flick.{$extension}"; // if the video can be processed by ffmpeg-php, width and height are greater than 1 if (!empty($info['width']) && $info['width'] > 1) { // reset some values to reduce video size if ($info['width'] < $width) { $width = $info['width']; } // here we calculate the size and aspect ratio of the output video $size_ratio = $width / $info['width']; $info['aspect'] = $info['width'] / $info['height']; $info['video_width'] = $width; $info['video_height'] = round($size_ratio * $info['height']); // height of video needs to be an even number if ($info['video_height'] % 2) { $info['video_height']++; } $info['size'] = "{$info['video_width']}x{$info['video_height']}"; } else { $info = $default; } // transfer settings to vars for easy manipulation for various APIs of ffmpeg $audio_bitrate = $gBitSystem->getConfig('mime_video_audio_bitrate', 32000) / 1000 . 'kb'; $audio_samplerate = $gBitSystem->getConfig('mime_video_audio_samplerate', 22050); $video_bitrate = $gBitSystem->getConfig('mime_video_video_bitrate', 160000) / 1000 . 'kb'; $acodec_mp3 = $gBitSystem->getConfig('ffmpeg_mp3_lib', 'libmp3lame'); $me_param = $gBitSystem->getConfig('ffmpeg_me_method', 'me'); if ($codec == "h264") { $parameters = " -i '{$source}'" . " -acodec libfaac" . " -ab {$audio_bitrate}" . " -ar {$audio_samplerate}" . " -vcodec libx264" . " -b {$video_bitrate}" . " -bt {$video_bitrate}" . " -s " . $info['size'] . " -aspect " . $info['aspect'] . " -flags +loop -cmp +chroma -refs 1 -coder 0 -me_range 16 -g 300 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -maxrate 10M -bufsize 10M -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30" . " -partitions +parti4x4+partp8x8+partb8x8 -{$me_param} epzs -subq 5 -trellis 1" . " -y '{$dest_file}'"; } elseif ($codec == "h264-2pass") { // it is not possible to pass in the path for the x264 log file and it is always generated in the working dir. $cwd = getcwd(); chdir(dirname($dest_file)); $passlogfile = dirname($dest_file) . "/ffmpeg2pass"; // pass 1 $parameters = " -i '{$source}'" . " -an" . " -pass 1" . " -passlogfile {$passlogfile}" . " -vcodec libx264" . " -b {$video_bitrate}" . " -bt {$video_bitrate}" . " -s " . $info['size'] . " -aspect " . $info['aspect'] . " -flags +loop -cmp +chroma -refs 1 -coder 0 -me_range 16 -g 300 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -bf 16 -maxrate 10M -bufsize 10M -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4 -level 30" . " -partitions 0 -{$me_param} epzs -subq 1 -trellis 0" . " -y '{$dest_file}'"; // pass 2 $parameters2 = " -i '{$source}'" . " -acodec libfaac" . " -ab {$audio_bitrate}" . " -ar {$audio_samplerate}" . " -pass 2" . " -passlogfile {$passlogfile}" . " -vcodec libx264" . " -b {$video_bitrate}" . " -bt {$video_bitrate}" . " -s " . $info['size'] . " -aspect " . $info['aspect'] . " -flags +loop -cmp +chroma -me_range 16 -g 250 -keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -rc_eq 'blurCplx^(1-qComp)' -qcomp 0.6 -qmin 10 -qmax 51 -qdiff 4" . " -partitions +parti8x8+parti4x4+partp8x8+partp4x4+partb8x8 -flags2 +brdo+dct8x8+wpred+bpyramid+mixed_refs -{$me_param} epzs -subq 7 -trellis 1 -refs 6 -bf 16 -directpred 3 -b_strategy 1 -bidir_refine 1 -coder 1" . " -y '{$dest_file}'"; } else { $parameters = " -i '{$source}'" . " -acodec {$acodec_mp3}" . " -ab {$audio_bitrate}" . " -ar {$audio_samplerate}" . " -f flv" . " -b {$video_bitrate}" . " -bt {$video_bitrate}" . " -s " . $info['size'] . " -aspect " . $info['aspect'] . " -y '{$dest_file}'"; } if ($pOnlyGetParameters) { return $parameters; } else { // we keep the output of this that we can store it to the error file if we need to do so $debug = shell_exec("{$ffmpeg} {$parameters} 2>&1"); if (!empty($parameters2)) { $debug .= shell_exec("{$ffmpeg} {$parameters2} 2>&1"); // change back to whence we came chdir($cwd); } } // make sure the conversion was successfull if (is_file($dest_file) && filesize($dest_file) > 48) { mime_video_fix_streaming($dest_file); // try to work out a reasonable timepoint where to extract a screenshot if (preg_match('!Duration: ([\\d:\\.]*)!', $debug, $time)) { list($h, $m, $s) = explode(':', $time[1]); $seconds = round(60 * 60 * (int) $h + 60 * (int) $m + (double) $s); // we need to subract one hour from our time for strftime to return the correct value $info['offset'] = strftime("%T", round($seconds / 5 - 60 * 60)); } else { $info['offset'] = "00:00:10"; } // store some video specific settings LibertyMime::expungeMetaData($pParamHash['attachment_id']); LibertyMime::storeMetaData($pParamHash['attachment_id'], 'Video', $info); // since the flv conversion worked, we will create a preview screenshots to show. mime_video_create_thumbnail($dest_file, $info['offset']); $log['message'] = 'SUCCESS: Converted to flash video'; $actionLog['log_message'] = "Converted to flashvideo in " . (date('U') - $begin) . " seconds"; $ret = TRUE; } else { // remove unsuccessfully converted file @unlink($dest_file); $log['message'] = "ERROR: The video you uploaded could not be converted by ffmpeg.\nDEBUG OUTPUT:\n\n" . $debug; $actionLog['log_message'] = "Video could not be converted to flashvideo. An error dump was saved to: " . $destPath . '/error'; // write error message to error file $h = fopen($destPath . "/error", 'w'); fwrite($h, "{$ffmpeg} {$parameters}\n\n{$debug}"); fclose($h); } @unlink($destPath . '/processing'); } } $log['time'] = date('d/M/Y:H:i:s O'); $log['duration'] = date('U') - $begin; // we'll insert some info into the database for reference $actionLog['content_id'] = $pParamHash['content_id']; $actionLog['title'] = "Uploaded file: {$pParamHash['upload']['name']} [Attchment ID: {$pParamHash['attachment_id']}]"; // if this all goes t**s up, we'll know why $pParamHash['log'] = $log; // we'll add an entry in the action logs LibertyContent::storeActionLogFromHash(array('action_log' => $actionLog)); // return the log $pParamHash['log'] = $log; } return $ret; }
/** * mime_audio_converter * * @param array $pParamHash * @access public * @return TRUE on success, FALSE on failure - mErrors will contain reason for failure */ function mime_audio_converter(&$pParamHash) { global $gBitSystem; // audio conversion can take a while ini_set("max_execution_time", "1800"); $ret = FALSE; $log = array(); $source = STORAGE_PKG_PATH . $pParamHash['upload']['dest_branch'] . $pParamHash['upload']['name']; $destPath = dirname($source); if (@BitBase::verifyId($pParamHash['attachment_id'])) { $pattern = "#.*\\.(mp3|m4a)\$#i"; if (!$gBitSystem->isFeatureActive('mime_audio_force_encode') && preg_match($pattern, $pParamHash['upload']['name'])) { // make a copy of the original maintaining the original extension $dest_file = $destPath . '/bitverted.' . preg_replace($pattern, "\$1", strtolower($pParamHash['upload']['name'])); if (!is_file($dest_file) && !link($source, $dest_file)) { copy($source, $dest_file); } $ret = TRUE; } else { // TODO: have a better mechanism of converting audio to mp3. ffmpeg works well as long as the source is 'perfect' // there are many audiofiles that can't be read by ffmpeg but by other tools like flac, faac, oggenc // mplayer is very good, but has a lot of dependencies and not many servers have it installed // also, using mplayer is a 2 step process: decoding and encoding // if we convert audio, we always make an mp3 $dest_file = $destPath . '/bitverted.mp3'; if (!($ret = mime_audio_converter_ffmpeg($pParamHash, $source, $dest_file))) { // fall back to using slower mplayer / lame combo $ret = mime_audio_converter_mplayer_lame($pParamHash, $source, $dest_file); } } // if the conversion was successful, we'll copy the tags to the new mp3 file and import data to meta tables if ($ret == TRUE) { $log['success'] = 'Successfully converted to mp3 audio'; // now that we have a new mp3 file, we might as well copy the tags accross in case someone downloads it require_once UTIL_PKG_PATH . 'getid3/getid3/getid3.php'; $getID3 = new getID3(); // we silence this since this will spew lots of ugly errors when using UTF-8 and some odd character in the file ID $meta = @$getID3->analyze($source); getid3_lib::CopyTagsToComments($meta); // write tags to new mp3 file if ($errors = mime_audio_update_tags($dest_file, $meta['comments'])) { $log['tagging'] = $errors; } // getID3 returns everything in subarrays - we want to store everything in [0] foreach ($meta['comments'] as $key => $comment) { $store[$key] = $comment[0]; } $store['playtimeseconds'] = $meta['playtime_seconds']; $store['playtimestring'] = $meta['playtime_string']; // make sure we remove previous entries first LibertyMime::expungeMetaData($pParamHash['attachment_id']); if (!LibertyMime::storeMetaData($pParamHash['attachment_id'], 'ID3', $store)) { $log['store_meta'] = "There was a problem storing the meta data in the database"; } // if we have an image in the id3v2 tag, we might as well do something with it // we'll simply use the first image we can find in the file if (!empty($meta['id3v2']['APIC'][0]['data'])) { $image = $meta['id3v2']['APIC'][0]; } elseif (!empty($meta['id3v2']['PIC'][0]['data'])) { $image = $meta['id3v2']['PIC'][0]; } if (!empty($image)) { // write the image to temp file for us to process $tmpfile = str_replace("//", "/", tempnam(TEMP_PKG_PATH, LIBERTY_PKG_NAME)); if ($fp = fopen($tmpfile, 'w')) { fwrite($fp, $image['data']); fclose($fp); $fileHash['type'] = $image['mime']; $fileHash['source_file'] = $tmpfile; $fileHash['dest_branch'] = $pParamHash['upload']['dest_branch']; liberty_generate_thumbnails($fileHash); // remove temp file if (!empty($tmpfile) && is_file($tmpfile)) { unlink($tmpfile); } } } // TODO: when tags package is enabled add an option to add tags // recommended tags might be artist and album // TODO: fetch album cover from amazon.com or musicbrainz.org // fetch lyrics from lyricwiki.org //$item->mLogs['audio_converter'] = "Audio file was successfully converted to MP3."; } } // update log $pParamHash['log'] = array_merge($pParamHash['log'], $log); return $ret; }