Ver Mensaje Individual
  #1 (permalink)  
Antiguo 04/09/2009, 07:36
Avatar de Triby
Triby
Mod on free time
 
Fecha de Ingreso: agosto-2008
Ubicación: $MX->Gto['León'];
Mensajes: 10.106
Antigüedad: 13 años, 9 meses
Puntos: 2236
[Aporte] Convertir videos con ffmpeg en background

Despues de un tiempo de batallar con este tema, por fin logre solucionarlo y quiero compartirlo por si alguien lo necesita.

Configuracion del servidor (en mi caso: ubuntu):

Antes que nada, ya debe estar instalado en el servidor ffmpeg con sus respectivos codecs, FlvTool2 para insertar metaData en el video (FLV injection) y la libreria ffmpeg_movie para obtener algunos datos necesarios y/o generar imagen de vista previa.

Uno de los problemas que tuve es que al ejecutar el script desde el navegador se realizaba la conversion sin problemas, pero, al ejecutarlo en segundo plano (background) fallaba.

La causa de esto es por la configuracion de PHP que no tenia acceso a ffmpeg_movie y no lo notaba porque en /etc/php5/apache2/php.ini aparece extension=ffmpeg.so, entonces busque todos los php.ini (find / -name php.ini) y encontre /etc/php/cli/php.ini, donde no estaba la linea, simplemente la agregue y funciono correctamente.

Subir archivo y ejecutar script de conversion:

Despues de subir el archivo y validar la extension (Las que yo permito: mov, wmv, avi, mpg, mpeg y flv) iniciamos la conversion en segundo plano:

Nota: No coloco todas las validaciones necesarias, porque es demasiado codigo y vamos al grano con la conversion.

Código php:
Ver original
  1. <?php
  2. // Subimos el archivo como lo hacemos con cualquier otro:
  3. move_uploaded_file($_FILES['video']['tmp_name'], "/ruta/convertir/$video");
  4. // Para mas informacion de como subir archivos, en la seccion de aportes hay varios ejemplos
  5.  
  6. // En este punto, guardamos el registro del video en base de datos
  7. // Algunos de los campos que uso:
  8. // -- id = varchar(10)
  9. // -- archivo = Nombre de archivo, primero solo almaceno la extension del archivo original
  10. // -- estado:
  11. // 0 = No se ha subido archivo
  12. // 1 = Video subido, convertido y listo para verse en la web
  13. // 2 = Se subio archivo y esta listo para convertir a FLV
  14. // 3 = Esta en proceso de conversion
  15.  
  16.  
  17. // Notificamos al usuario que el archivo subio y va a iniciar la conversion
  18. ?>
  19. <html>
  20. <body>
  21.     <p>El archivo fue subido correctamente, ahora lo vamos a convertir a FLV, mientras puedes ver otras paginas en nuestro sitio.</p>
  22. </body>
  23. </html>
  24.  
  25. <?php
  26. // >/dev/null &  ---- Esto es lo que hace que se ejecute en segundo plano
  27. exec("php /convertir.php?id=$id> /dev/null &");

convertir.php -> Configuracion general

Necesitaremos algunas variables:
- Ruta de videos pendientes de convertir: $ruta_convertir = '/ruta/convertir';
- Ruta final de videos: $ruta_video = '/ruta/videos';
- Ruta de imagen de marca de agua (opcional): $marca_agua = '/ruta/marca_agua.gif';
- Dimensiones del video convertido ANCHOxALTO: $medidas = '640x480';
- Frecuencia de audio (sample rate), especificamente, las 3 unicas permitidas en videos flv $frecuencia = array(44100, 22050, 11025);

convertir.php -> Obtener info general del video

Código php:
Ver original
  1. // Tomamos la id del video desde GET (es necesario validar)
  2. $video = $_GET['id'];
  3.  
  4. // Leemos info de la base de datos
  5. $result = mysql_query("SELECT id, archivo, mas_campos FROM videos WHERE id = '$video'");
  6. list($id, $archivo, $mas_campos) = mysql_fetch_row($result);
  7.  
  8. // En el campo archivo solo tengo almacenada la extension
  9. $original = "$ruta_convertir/$id$archivo";
  10. $final = "$ruta_video/$id" . '.flv';
  11. $vista_previa = "$ruta_video/$id" . '.jpg';
  12.  
  13. // Creamos un objeto ffmpeg_movie para obtener datos necesarios para convertir
  14. $movie = new ffmpeg_movie($original, false);
  15. $duracion = $movie->getDuration();      // Duracion en segundos
  16. $audioRate = $movie->getAudioSampleRate();
  17. $audioBitr = $movie->getAudioBitRate();
  18.  
  19. if(!in_array($audioRate, $frecuencia))
  20.     $audioRate = 22050;
  21. if(!isset($aidioBitr) || !$aidioBitr || $aidioBitr < 1)
  22.     $audioBitr = $audioRate;
  23.  
  24. // Creamos el comando ffmpeg
  25. $ffmpeg_cmd = "ffmpeg -i $original -ar  $audioRate -ab $audioBitr -f flv -s $medidas $final";
  26. // La siguiente linea es necesaria solo si vas a agregar marca de agua
  27. $ffmpeg_cmd .= " -vhook '/usr/local/lib/vhook/watermark.so -f $marca_agua'";
  28. // Ejecutamos el comando y guardamos la salida en una matriz (util para depurar)
  29. exec("$ffmpeg_cmd 2>&1", $salida);
  30. // Si necesitas depurar, despues de cada exec() incluye:
  31. // file_put_contents('depurar.txt', implode("\n", $salida));
  32.  
  33. // Injectamos metaData:
  34. $ffmpeg_cmd = "flvtool2 -U $final";
  35. exec("$ffmpeg_cmd 2>&1", $salida);
  36.  
  37. // Generamos la vista previa (120 x 90 pixeles, puedes modificarlo a tu gusto)
  38. // Seleccionamos una posicion en medio del video
  39. $posicion = sec2hms(ceil($duracion / 2));
  40. $ffmpeg_cmd2 = "ffmpeg -i $final -ss $posicion -t 00:00:01 -s 120x90 -r 1 -f image2 $vista_previa";
  41. exec("$ffmpeg_cmd2 2>&1", $salida);
  42. // Si algo fallo, intentamos otro metodo
  43. if(!file_exists($vista_previa)) {
  44.     $ffmpeg_cmd2 = "ffmpeg  -itsoffset -4  -i $final -vcodec mjpeg -vframes 1 -an -f rawvideo -s 120x90 $vista_previa";
  45.     exec("$ffmpeg_cmd2 2>&1", $salida);
  46. }
  47. // Si volvio a fallar, intentamos con ffmpeg_movie
  48. // Nota: la libreria GD tambien debe estar habilitada en /etc/php5/cli/php.ini
  49. // extension=gd.so
  50. if(!file_exists($vista_previa)) {
  51.     // Creamos el objeto, obtenemos total de cuadros y calculamos la mitad
  52.     $movie = new ffmpeg_movie($final, false);
  53.     $total = $movie->getFrameCount();
  54.     $medio = intval($total / 2);
  55.  
  56.     // Leemos el cuadro elegido y redimensionamos
  57.     $frame = $movie->getFrame($medio);
  58.     $frame->resize(120, 90);
  59.  
  60.     // Convertimos a imagen de GD, guardamos como jpg y destruimos el recurso GD
  61.     $gd_image = $frame->toGDImage();
  62.     imagejpeg($gd_image, $vista_previa);
  63.     imagedestroy($gd_image);
  64. }
  65.  
  66. // Actualizamos la base de datos con la informacion necesaria
  67. // Como depende de los campos que se tengan solo pongo un ejemplo:
  68. // archivo = '$final', vista_previa = '$vista_previa', duracion = $duracion, estado = 1
  69.  
  70. // Eliminamos archivo original
  71. unlink($original)
  72.  
  73. // Listo, eso es todo... yeah!!!!
  74.  
  75. // Funcion basada en http://snipplr.com/view/1459/sec2hms/
  76. function sec2hms($sec, $useColon = false) {
  77.     $hms = '';
  78.     $hours = intval(intval($sec) / 3600);
  79.     if ($hours > 0) {
  80.         $hms .= ($useColon) ? str_pad($hours, 2, "0", STR_PAD_LEFT). ':' : $hours. ' tim ';
  81.     } elseif ($useColon) {
  82.         $hms .= '00:';
  83.     }
  84.     $minutes = intval(($sec / 60) &#37; 60);
  85.     if ($minutes > 0 || $hours > 0)
  86.         $hms .= ($useColon) ? str_pad($minutes, 2, "0", STR_PAD_LEFT). ':' : $minutes. ' min ';
  87.     $seconds = intval($sec % 60);
  88.     $hms .= ($useColon) ? str_pad($seconds, 2, "0", STR_PAD_LEFT) : $seconds. ' sek ';
  89.     return $hms;
  90. }

Es posible realizar la conversion del video e insertar metaData en un mismo comando exec('comando1 | comando2'); pero no lo recomiendo, en ocasiones no funciono y no hay diferencia en los recursos que consume el script al ejecutarse, estuve monitoreando con TOP desde SSH.

Enlaces de interes:
- API (clase?) de ffmpeg-php
- Convertir segundos (entero) en cadena (hh:mm:ss)
__________________
- León, Guanajuato
- GV-Foto

Última edición por Triby; 05/09/2009 a las 13:45 Razón: Agregar validacion de $audioBitr