~jstys-z/helioviewer.org/client5

« back to all changes in this revision

Viewing changes to api/src/Movie/FFMPEGEncoder.php

  • Committer: Keith Hughitt
  • Date: 2012-06-04 17:27:01 UTC
  • Revision ID: keith.hughitt@nasa.gov-20120604172701-eo2sj3ylxh4bik4l
Added support for reconnecting to MySQL if the connection fails when querying the db to determine which files are new

Show diffs side-by-side

added added

removed removed

Lines of Context:
 
1
<?php
 
2
/* vim: set expandtab tabstop=4 shiftwidth=4 softtabstop=4: */
 
3
/**
 
4
 * Movie_FFMPEGEncoder Class Definition
 
5
 * 
 
6
 * Detecting problems with FFmpeg encoding:
 
7
 *  When using exec to call FFmpeg from the command line no useful return code or output
 
8
 *  information is returned. In order to the detect problems then the simplest way is to
 
9
 *  check and make sure the filesize is reasonable.
 
10
 *
 
11
 *
 
12
 * PHP version 5
 
13
 *
 
14
 * @category Movie
 
15
 * @package  Helioviewer
 
16
 * @author   Jaclyn Beck <jaclyn.r.beck@gmail.com>
 
17
 * @author   Keith Hughitt <keith.hughitt@nasa.gov>
 
18
 * @license  http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
 
19
 * @link     http://launchpad.net/helioviewer.org
 
20
 */
 
21
/**
 
22
 * Calls FFMpeg commands
 
23
 *
 
24
 * @category Movie
 
25
 * @package  Helioviewer
 
26
 * @author   Jaclyn Beck <jaclyn.r.beck@gmail.com>
 
27
 * @author   Keith Hughitt <keith.hughitt@nasa.gov>
 
28
 * @license  http://www.mozilla.org/MPL/MPL-1.1.html Mozilla Public License 1.1
 
29
 * @link     http://launchpad.net/helioviewer.org
 
30
 */
 
31
class Movie_FFMPEGEncoder
 
32
{
 
33
    
 
34
    private $_directory;
 
35
    private $_filename;
 
36
    private $_format;
 
37
    private $_frameRate;
 
38
    private $_width;
 
39
    private $_height;
 
40
    private $_title;
 
41
    private $_description;
 
42
    private $_comment;    
 
43
    
 
44
    /**
 
45
     * Constructor
 
46
     * 
 
47
     * @param {int} $frameRate The number of frames per second in the movie
 
48
     * 
 
49
     * @return void
 
50
     */
 
51
    public function __construct($directory, $filename, $frameRate, $width, $height, $title, $description, $comment)
 
52
    {
 
53
        $info = pathinfo($filename);
 
54
        
 
55
        $this->_directory   = $directory;
 
56
        $this->_filename    = $filename;
 
57
        $this->_frameRate   = $frameRate;
 
58
        $this->_width       = $width;
 
59
        $this->_height      = $height;
 
60
        $this->_title       = $title;
 
61
        $this->_description = $description;
 
62
        $this->_comment     = $comment;
 
63
        $this->_format      = $info['extension'];
 
64
        
 
65
        $this->_log = fopen($directory . 'ffmpeg.log', 'a');
 
66
    }
 
67
    
 
68
    /**
 
69
     * Creates a medium quality video
 
70
     */
 
71
    public function createVideo()
 
72
    {
 
73
        $outputFile = $this->_directory . $this->_filename;
 
74
 
 
75
        if ($this->_format == "mp4") {
 
76
            $this->_createH264Video($outputFile);
 
77
        } else if ($this->_format == "webm") {
 
78
            $this->_createWebMVideo($outputFile);
 
79
        }
 
80
        
 
81
        // If FFmpeg segfaults, an empty movie container may still be produced,
 
82
        if (!file_exists($outputFile) || filesize($outputFile) < 1000)
 
83
            throw new Exception("FFmpeg error encountered.");
 
84
 
 
85
        return $outputFile;
 
86
    }
 
87
    
 
88
    /**
 
89
     * Creates a high quality video
 
90
     */
 
91
    public function createHQVideo()
 
92
    {
 
93
        $baseFilename = substr($this->_filename, 0, -strlen($this->_format) - 1);
 
94
        $outputFile = sprintf("%s%s-hq.%s", $this->_directory, $baseFilename, $this->_format);
 
95
        
 
96
        if ($this->_format == "mp4") {
 
97
            $this->_createH264Video($outputFile, HV_X264_HQ_PRESET, 15);
 
98
        } else if ($this->_format == "webm") {
 
99
            $this->_createWebMVideo($outputFile, 1);
 
100
        }
 
101
        
 
102
        // If FFmpeg segfaults, an empty movie container may still be produced
 
103
        if (!file_exists($outputFile) || filesize($outputFile) < 1000)
 
104
            throw new Exception("FFmpeg error encountered.");
 
105
 
 
106
        return $outputFile;
 
107
    }
 
108
    
 
109
    /**
 
110
     * Creates a WebM/VP8 video
 
111
     */
 
112
    private function _createWebMVideo($outputFile, $qmax=40)
 
113
    {
 
114
        //$cmd = HV_FFMPEG . " -r " . $this->_frameRate . " -i " . $this->_directory . "/frames/frame%d.bmp"
 
115
        //    . " -r " . $this->_frameRate . " -f webm -vcodec libvpx -qmax $qmax -threads " . HV_FFMPEG_MAX_THREADS 
 
116
        //    . " -s " . $this->_width . "x" . $this->_height . " -an -y $outputFile";
 
117
        $cmd = sprintf(
 
118
            "%s -r %f -i %sframes/frame%%d.bmp -r %f -f webm -vcodec libvpx -qmin 1 -qmax %d %s -threads %d -s %dx%d -an -y %s 2>/dev/null",
 
119
            HV_FFMPEG,
 
120
            $this->_frameRate,
 
121
            $this->_directory,
 
122
            $this->_frameRate,
 
123
            $qmax,
 
124
            $this->_getMetaDataString(),
 
125
            HV_FFMPEG_MAX_THREADS,
 
126
            $this->_width,
 
127
            $this->_height,
 
128
            $outputFile
 
129
        );
 
130
        
 
131
        $this->_logCommand($cmd);
 
132
 
 
133
        // Run FFmpeg        
 
134
        $output = shell_exec($cmd);
 
135
    }
 
136
    
 
137
    /**
 
138
     * Creates a video in whatever format is given in $filename
 
139
     * 
 
140
     * NOTE: Frame rate MUST be specified twice in the command, before and after the input file, or ffmpeg will start
 
141
     *       cutting off frames to adjust for what it thinks is the right frameRate. 
 
142
     *
 
143
     * @param int    $width       the width of the video
 
144
     * @param int    $height      the height of the video
 
145
     * 
 
146
     * @return String the filename of the video
 
147
     */
 
148
    private function _createH264Video($outputFile, $preset=HV_X264_PRESET, $crf=18)
 
149
    {
 
150
        // Include x264 FFpreset if specified
 
151
        if ($preset) {
 
152
            $ffpreset = "-vpre $preset ";
 
153
        } else {
 
154
            $ffpreset = "";
 
155
        }
 
156
        $cmd = HV_FFMPEG . " -r " . $this->_frameRate . " -i " . $this->_directory . "frames/frame%d.bmp"
 
157
            . " -r " . $this->_frameRate . " -vcodec libx264 " . $ffpreset . $this->_getMetaDataString() . "-threads " 
 
158
            . HV_FFMPEG_MAX_THREADS . " -crf $crf -s " . $this->_width . "x" . $this->_height . " -an -y $outputFile 2>/dev/null";
 
159
            
 
160
        $this->_logCommand($cmd);
 
161
            
 
162
        // Run FFmpeg
 
163
        $output = shell_exec($cmd);
 
164
    }
 
165
    
 
166
    /**
 
167
     * Creates a flash video by converting it from the high quality file
 
168
     *
 
169
     * @param string $directory  The directory where both files are stored
 
170
     * @param string $filename   Base filename of the video
 
171
     * @param string $origFormat The original container format
 
172
     * @param string $newFormat  The new container format
 
173
     * 
 
174
     * @return void
 
175
     */
 
176
    public function createFlashVideo()
 
177
    {
 
178
        $file = $this->_directory . substr($this->_filename, 0, -4);
 
179
        
 
180
        $cmd = sprintf("%s -i %s.mp4 -vcodec copy %s -threads %d %s.flv 2>/dev/null",
 
181
            HV_FFMPEG,
 
182
            $file,
 
183
            $this->_getMetaDataString(),
 
184
            HV_FFMPEG_MAX_THREADS,
 
185
            $file
 
186
        );
 
187
 
 
188
        $this->_logCommand($cmd);
 
189
        exec($cmd);
 
190
 
 
191
        // Check to ensure that movie size is valid
 
192
        if (filesize($file . ".flv") < 1000)
 
193
            throw new Exception("FFmpeg error encountered: Unable to create flv.");
 
194
    }
 
195
    
 
196
    
 
197
    /**
 
198
     * Creates an ipod-compatible mp4 video
 
199
 
 
200
     * @param int    $width      The width of the video
 
201
     * @param int    $height     The height of the video
 
202
     * 
 
203
     * @return String the filename of the ipod video
 
204
     */
 
205
    public function createIpodVideo($format, $width, $height) 
 
206
    {
 
207
        $ipodVideoName = $this->_directory . "/ipod-" . $this->_filename . "." . $format;
 
208
        
 
209
        $macFlags = "-flags +loop -cmp +chroma -vcodec libx264 -me_method 'hex' -me_range 16 "
 
210
                  . "-keyint_min 25 -sc_threshold 40 -i_qfactor 0.71 -b_strategy 1 -qcomp 0.6 -qmin 10 "
 
211
                  . "-qmax 51 -qdiff 4 -bf 3 -directpred 1 -trellis 1 -wpredp 2 -an -y";
 
212
        
 
213
        $cmd = HV_FFMPEG . " -i " . $this->_directory . "/frames/frame%d.bmp -r " . $this->_frameRate
 
214
            . " -f mp4 -b 800k -coder 0 -bt 200k -maxrate 96k -bufsize 96k -rc_eq 'blurCplx^(1-qComp)' -level 30 "
 
215
            . "-refs 1 -subq 5 -g 30 -s " . $width . "x" . $height . " $macFlags $ipodVideoName";
 
216
            
 
217
        exec($cmd);
 
218
 
 
219
        // Check to ensure that movie size is valid
 
220
        if (filesize($ipodVideoName) < 1000)
 
221
            throw new Exception("FFmpeg error encountered: Unable to create iPod video.");
 
222
 
 
223
        return $ipodVideoName;
 
224
    }
 
225
    
 
226
    /**
 
227
     * Adjusts the movie format that is used when encoding videos
 
228
     * 
 
229
     * @param {string} $format Extension for the format to switch to
 
230
     * 
 
231
     * @return void
 
232
     */
 
233
    public function setFormat($format) {
 
234
        $this->_format = $format;
 
235
        $this->_filename = str_replace(array("webm", "mp4"), $format, 
 
236
                            $this->_filename);
 
237
    }
 
238
 
 
239
    /**    
 
240
     * Log FFmpeg command
 
241
     */
 
242
    private function _logCommand($cmd)
 
243
    {
 
244
        $message = "========================================================\n" 
 
245
                 . "$cmd\n";
 
246
        fwrite($this->_log, $message);
 
247
    }
 
248
    
 
249
    /**
 
250
     * Creates the portion of the ffmpeg command relating to metadata properties
 
251
     */
 
252
    private function _getMetaDataString()
 
253
    {
 
254
        return sprintf(
 
255
            '-metadata title="%s" -metadata artist="Helioviewer.org" ' . 
 
256
            '-metadata description="%s" -metadata comment="%s" -timestamp "%s" ',
 
257
            $this->_title,
 
258
            $this->_description,
 
259
            str_replace(array("mp4", "webm"), $this->_format, $this->_comment),
 
260
            date("Y-m-d\TH:i:s\Z")
 
261
        );
 
262
    }
 
263
}
 
264
?>