Upload bugs solved: elim_dupl_res feature causes generation of a malformed content...
[living-lab-site.git] / application / helpers / video_helper.php
1 <?php  if ( ! defined('BASEPATH')) exit('No direct script access allowed');
2
3 /**
4  * Tests if parameter is a resolution string with format [width]x[height].
5  * @param string $res
6  * @return boolean
7  */
8 function is_res($res)
9 {
10         if (strpos($res, 'x') === FALSE)
11         return FALSE;
12
13         return TRUE;
14 }
15
16 /**
17  * Return the width from a resolution string with format [width]x[height].
18  * @param string $res
19  */
20 function res_to_width($res)
21 {
22         if (! is_res($res))
23         return FALSE;
24
25         return intval( substr($res, 0, strpos($res, 'x')) );
26 }
27
28 /**
29 * Return the height from a resolution string with format [width]x[height].
30 * @param string $res
31 */
32 function res_to_height($res)
33 {
34         if (! is_res($res))
35         return FALSE;
36
37         return intval( substr($res, strpos($res, 'x') + 1) );
38 }
39
40 /**
41  * Compares two resolution strings $a and $b with format [width]x[height] based
42  * on theirs megapixels number by return -1, 0 or 1 like any compare function.
43  * @param string $a
44  * @param string $b
45  * @param function $access_function     filters input parameters by doing something
46  * like $a = $access_function($a). Leave it NULL for no filtering.
47  */
48 function megapixels_cmp($a, $b, $access_function = NULL)
49 {
50         if ($access_function !== NULL)
51         {
52                 $a = $access_function($a);
53                 $b = $access_function($b);
54         }
55
56         $a_w = intval( substr($a, 0, strpos($a, 'x')) );
57         $a_h = intval( substr($a, strpos($a, 'x') + 1) );
58         if ($a_w === FALSE || $a_h === FALSE)
59         return 0;
60         $a_Mp = $a_w * $a_h;
61         $b_w = intval( substr($b, 0, strpos($b, 'x')) );
62         $b_h = intval( substr($b, strpos($b, 'x') + 1) );
63         if ($b_w === FALSE || $b_h === FALSE)
64         return 0;
65         $b_Mp = $b_w * $b_h;
66
67         if ($a_Mp == $b_Mp)
68         return 0;
69
70         return $a_Mp > $b_Mp ? 1 : -1;
71 }
72
73 /**
74  * Return the index of the $haystack element which has the closest resolution
75  * to $needle resolution string.
76  * @param array $haystack
77  * @param string $needle
78  * @param function $access_function     filters input parameters by doing something
79  * like $a = $access_function($a). Leave it NULL for no filtering.
80  */
81 function get_closest_res($haystack, $needle, $access_function = NULL)
82 {
83         $d_min = INF;
84         $i_min = FALSE;
85
86         foreach($haystack as $i => $elem)
87         {
88                 if ($access_function !== NULL)
89                 $elem = $access_function($elem);
90
91                 $d = abs(res_to_width($elem) * res_to_height($elem)
92                 - res_to_width($needle) * res_to_height($needle));
93                 if ($d < $d_min)
94                 {
95                         $d_min = $d;
96                         $i_min = $i;
97                 }
98         }
99
100         return $i_min;
101 }
102
103 /**
104  * "Private" function used by get_av_info which returns the value from a
105  * "key=value" formatted string.
106  * 
107  * @param string $str_key_value a string formatted as key=value
108  * @return string
109  */
110 function _parse_value($str_key_value)
111 {
112         return trim(substr(
113                         $str_key_value, 
114                         strpos($str_key_value, '=') + 1,
115                         strlen($str_key_value)
116                         ));
117 }
118
119 /**
120  * Formats the floating number of seconds to a string with format [HH:]mm:ss .
121  * 
122  * @param float $secs
123  * @return string 
124  */
125 function format_duration($secs)
126 {
127         $secs = intval(round($secs));
128         
129         $h = intval(floor($secs / 3600));
130         $m = intval(floor(($secs % 3600) / 60));
131         $s = $secs % 3600 % 60;
132         
133         $duration = sprintf('%02d', $m) . ':' . sprintf('%02d', $s);
134         
135         if ($h > 0)
136                 return sprintf('%02d', $h) . ':' . $duration;
137         
138         return $duration;
139 }
140
141 /**
142  * Returns information about an Audio/Video file.
143  * 
144  * @param string $file_name Audio/Video file
145  * @return dictionary FALSE on error or a dictionary of audio/video properties
146  * with the following keys otherwise:
147  * <ul>
148  *   <li>width</li>
149  *   <li>height</li>
150  *   <li>dar (display aspect ratio)</li>
151  *   <li>duration (formated as [HH:]mm:ss)</li>
152  *   <li>size (in bytes)</li>
153  * </ul>
154  */
155 function get_av_info($file_name)
156 {
157         $h = popen('ffprobe -show_streams -show_format "'
158                         . $file_name . '" 2> /dev/null', 'r');
159
160         $tag = NULL;
161         $codec_type = NULL;
162         
163         while ( ($r = fgets($h, 512)) !== FALSE)
164         {
165                 // Match tags.
166                 if (preg_match('/^\[FORMAT\]/', $r))
167                 {
168                         $tag = 'FORMAT';        
169                         continue;
170                 }
171                 if (preg_match('/^\[STREAM\]/', $r))
172                 {
173                         $tag = 'STREAM';
174                         continue;
175                 }
176                 
177                 if ($tag == 'FORMAT')
178                 {
179                         // Size
180                         if (preg_match('/^size=/', $r))
181                                 $size = intval(_parse_value ($r));
182                 }
183                 
184                 if ($tag == 'STREAM')
185                 {
186                         // Width
187                         if (preg_match('/^width=/', $r))
188                                 $width = intval(_parse_value($r));
189                         
190                         // Height
191                         if (preg_match('/^height=/', $r))
192                                 $height = intval(_parse_value($r));
193                         
194                         // DAR
195                         if (preg_match('/^display_aspect_ratio=/', $r))
196                                 $dar = _parse_value($r);
197                         
198                         // Codec Type
199                         if (preg_match('/^codec_type=/', $r))
200                                 $codec_type = _parse_value($r);
201                         
202                         // Duration
203                         if (preg_match('/^duration=/', $r)
204                                         && strcmp($codec_type, 'video') == 0)
205                                 $duration = format_duration(floatval(_parse_value($r)));
206                 }
207         }
208         
209         if (pclose($h) > 0)
210                 return FALSE;
211         
212         return array('width'=>$width, 'height'=>$height, 'dar'=>$dar,
213                 'duration'=>$duration, 'size'=>$size);
214         
215 //      return array('width'=> 1440, 'height'=> 1080, 'dar'=> '16:9',
216 //                      'duration'=> '00:10', 'size'=> 5568748);
217 }
218
219 /**
220  * Return a dictionary with formats compliant for DB and CIS and computes
221  * resolutions such that an uploaded video will not be converted to a higher
222  * resolution.
223  * 
224  * @param type $formats formats as in content_ingestion config file
225  * @param type $av_info structure as returned by get_av_info function from this
226  * helper
227  * @param type $elim_dupl_res eliminate consecutive formats with the same
228  * resolution
229  * @return array a dictionary with DB format at key 'db_formats' and CIS format
230  * at key 'transcode_configs' 
231  */
232 function prepare_formats($formats, $av_info, $elim_dupl_res=FALSE)
233 {
234         $transcode_configs = array();
235         $db_formats = array();
236         
237         for ($i = 0; $i < count($formats); $i++)
238         {
239                 $transcode_configs[$i]['container'] = $formats[$i]['container'];
240                 $transcode_configs[$i]['extension'] = $formats[$i]['extension'];
241                 $db_formats[$i]['ext'] = $formats[$i]['extension'];
242                 $transcode_configs[$i]['a_codec'] = $formats[$i]['audio_codec'];
243                 $transcode_configs[$i]['a_bitrate'] = $formats[$i]['audio_bit_rate'];
244                 $transcode_configs[$i]['a_samplingrate'] = $formats[$i]['audio_sampling_rate'];
245                 $transcode_configs[$i]['a_channels'] = $formats[$i]['audio_channels'];
246                 $transcode_configs[$i]['v_codec'] = $formats[$i]['video_codec'];
247                 $transcode_configs[$i]['v_bitrate'] = $formats[$i]['video_bit_rate'];
248                 $transcode_configs[$i]['v_framerate'] = $formats[$i]['video_frame_rate'];
249                 $transcode_configs[$i]['v_dar'] = $av_info['dar'];
250                 $db_formats[$i]['dar'] = $av_info['dar'];
251
252                 $sar = $av_info['width'] / $av_info['height'];
253                 
254                 if ($av_info['height'] < $formats[$i]['video_height'])
255                 {
256                         $width = $av_info['width'];
257                         $height = $av_info['height'];
258                 }
259                 else
260                 {
261                         $height = $formats[$i]['video_height'];
262                         $width = round($sar * $height);
263                 }
264                 
265                 $transcode_configs[$i]['v_resolution'] = "${width}x${height}";
266                 $db_formats[$i]['res'] = "${width}x${height}";
267         }
268         
269         // Eliminate formats with duplicate resolutions.
270 //      if ($elim_dupl_res)
271 //      {
272 //              for ($i = 1; $i < count($transcode_configs); $i++)
273 //              {
274 //                      if ($transcode_configs[$i]['v_resolution']
275 //                                      === $transcode_configs[$i - 1]['v_resolution'])
276 //                      {
277 //                              unset($transcode_configs[$i - 1]);
278 //                              unset($db_formats[$i - 1]);
279 //                              $i--;
280 //                      }
281 //              }
282 //      }
283         
284         return array('transcode_configs'=>$transcode_configs,
285                 'db_formats'=>$db_formats);
286 }
287
288 /* End of file video_helper.php */
289 /* Location: ./application/helpers/video_helper.php */