A/V info successfuly retrieves uploaded video information
[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         
162         while ( ($r = fgets($h, 512)) !== FALSE)
163         {
164                 // Match tags.
165                 if (preg_match('/^\[FORMAT\]/', $r))
166                 {
167                         $tag = 'FORMAT';        
168                         continue;
169                 }
170                 if (preg_match('/^\[STREAM\]/', $r))
171                 {
172                         $tag = 'STREAM';
173                         continue;
174                 }
175                 
176                 if ($tag == 'FORMAT')
177                 {
178                         // Duration
179                         if (preg_match('/^duration=/', $r))
180                                 $duration = format_duration(floatval(_parse_value($r)));
181                         
182                         // Size
183                         if (preg_match('/^size=/', $r))
184                                 $size = intval(_parse_value ($r));
185                 }
186                 
187                 if ($tag == 'STREAM')
188                 {
189                         // Width
190                         if (preg_match('/^width=/', $r))
191                                 $width = intval(_parse_value($r));
192                         
193                         // Height
194                         if (preg_match('/^height=/', $r))
195                                 $height = intval(_parse_value($r));
196                         
197                         // DAR
198                         if (preg_match('/^display_aspect_ratio=/', $r))
199                                 $dar = _parse_value($r);
200                 }
201         }
202         
203         if (pclose($h) > 0)
204                 return FALSE;
205         
206         return array('width'=>$width, 'height'=>$height, 'dar'=>$dar,
207                 'duration'=>$duration, 'size'=>$size);
208         
209 //      return array('width'=> 1440, 'height'=> 1080, 'dar'=> '16:9',
210 //                      'duration'=> '00:10', 'size'=> 5568748);
211 }
212
213 /**
214  * Return a dictionary with formats compliant for DB and CIS and computes
215  * resolutions such that an uploaded video will not be converted to a higher
216  * resolution.
217  * 
218  * @param type $formats formats as in content_ingestion config file
219  * @param type $av_info structure as returned by get_av_info function from this
220  * helper
221  * @param type $elim_dupl_res eliminate consecutive formats with the same
222  * resolution
223  * @return array a dictionary with DB format at key 'db_formats' and CIS format
224  * at key 'transcode_configs' 
225  */
226 function prepare_formats($formats, $av_info, $elim_dupl_res=FALSE)
227 {
228         $transcode_configs = array();
229         $db_formats = array();
230         
231         for ($i = 0; $i < count($formats); $i++)
232         {
233                 $transcode_configs[$i]['container'] = $formats[$i]['container'];
234                 $transcode_configs[$i]['extension'] = $formats[$i]['extension'];
235                 $db_formats[$i]['ext'] = $formats[$i]['extension'];
236                 $transcode_configs[$i]['a_codec'] = $formats[$i]['audio_codec'];
237                 $transcode_configs[$i]['a_bitrate'] = $formats[$i]['audio_bit_rate'];
238                 $transcode_configs[$i]['a_samplingrate'] = $formats[$i]['audio_sampling_rate'];
239                 $transcode_configs[$i]['a_channels'] = $formats[$i]['audio_channels'];
240                 $transcode_configs[$i]['v_codec'] = $formats[$i]['video_codec'];
241                 $transcode_configs[$i]['v_bitrate'] = $formats[$i]['video_bit_rate'];
242                 $transcode_configs[$i]['v_framerate'] = $formats[$i]['video_frame_rate'];
243                 $transcode_configs[$i]['v_dar'] = $av_info['dar'];
244                 $db_formats[$i]['dar'] = $av_info['dar'];
245
246                 $sar = $av_info['width'] / $av_info['height'];
247                 
248                 if ($av_info['height'] < $formats[$i]['video_height'])
249                 {
250                         $width = $av_info['width'];
251                         $height = $av_info['height'];
252                 }
253                 else
254                 {
255                         $height = $formats[$i]['video_height'];
256                         $width = round($sar * $height);
257                 }
258                 
259                 $transcode_configs[$i]['v_resolution'] = "${width}x${height}";
260                 $db_formats[$i]['res'] = "${width}x${height}";
261         }
262         
263         // Eliminate formats with duplicate resolutions.
264         if ($elim_dupl_res)
265         {
266                 for ($i = 1; $i < count($transcode_configs); $i++)
267                 {
268                         if ($transcode_configs[$i]['v_resolution']
269                                         === $transcode_configs[$i - 1]['v_resolution'])
270                         {
271                                 unset($transcode_configs[$i - 1]);
272                                 unset($db_formats[$i - 1]);
273                                 $i--;
274                         }
275                 }
276         }
277         
278         return array('transcode_configs'=>$transcode_configs,
279                 'db_formats'=>$db_formats);
280 }
281
282 /* End of file video_helper.php */
283 /* Location: ./application/helpers/video_helper.php */