2013-05-16 2 views
0

Мне пришлось извлечь первый кадр видеофайлов (mp4, wmv, mov), который хранится на лазурной памяти в виде блочных блоков. Я должен был сделать это внутри httphandler, а затем сохранить его в качестве байтового буфера в таблицу в нашей базе данных SQL Azure.Как получить первый кадр видеофайла, хранящегося в лазурном хранилище

Любая помощь была бы принята с благодарностью. Спасибо

ответ

1

Возможно, вы сможете сделать это с помощью Windows Azure Media Services. Это похоже на то, что они могут вам пригодиться, но у меня нет особого опыта с ними.

В противном случае вам придется делать это самостоятельно. Это будет выглядеть примерно так:

  1. Получить блоб из хранилища, чтобы где-то вы можете работать с ним
  2. захватить кадр из.
  3. Сохраните рамку в SQL Azure.

Я предполагаю, что вы можете найти № 1 & # 3 самостоятельно. Для # 2 я бы рекомендовал посмотреть на ffmpeg. Это какая-то боль в прикладе, но она схватит раму из почти всего. Фактически, я использовал его для этого уже пару лет, и он работает достаточно хорошо. Аргументы, которые я использую для framegrabbing являются:

ffmpeg -i <inputFileName> -frames:v 1 -ss <video duration in seconds/2> -f image2 <output file name>

Но есть, конечно, больше способов сделать это.

Пример: Это (несколько упрощенная) версия того, как я это делаю. Обратите внимание: моя версия ffmpeg немного устарела, поэтому аргументы командной строки могут быть изменены. Но основная идея должна работать.

/// <summary> 
/// Extract a thumbnail from the middle (by duration) of a video file 
/// </summary> 
/// <param name="inputFileName">Path to the video file on the local filesystem</param> 
/// <param name="duration">Duration of the video</param> 
/// <returns></returns> 
public Image ExtractThumbnail(string inputFileName) 
{ 
    if (string.IsNullOrEmpty(inputFileName)) 
    { 
     throw new ArgumentNullException("inputFileName", "Input file is null"); 
    } 


    TimeSpan duration = GetVideoDuration(inputFileName); 

    const string framegrabTemplate = @"-i ""{0}"" -frames:v 1 -ss {2:##0.0##} -f image2 {1}"; 

    string framegrabArgs = string.Format(framegrabTemplate, inputFileName, OutputFileName, duration.TotalSeconds/2); 
    WindowsProcessResult result = null; 

    try 
    { 
     result = WindowsProcessUtil.RunProcess(ExePath, framegrabArgs); 
    } 
    catch (Exception ex) 
    { 
     log.Error("Framegrab process failed with exception {0}.", ex); 
     return null; 
    } 

    if (result.ExitCode != 0) 
    { 
     log.Error("Framegrab process failed with exitCode {0}. Process output:\r\n{1}\r\nProcess Error: {2}", result.ExitCode, result.StandardOutput, result.StandardError); 
     return null; 
    } 

    var img = Image.FromFile(OutputFileName); 

    //Certain video sources (primarily iOS v5 and below) will give you videos rotated to one side with embedded metadata telling you what that rotation is 
    //If you need to deal with that, you can set rotationAngle from that metadata, but that's a whole other issue 
    //Leaving it here for now as an FYI 
    //int rotationAngle = 0; 
    //if (rotationAngle != 0) 
    //{ 
    // if (rotationAngle == 90) 
    // { 
    //  img.RotateFlip(RotateFlipType.Rotate90FlipNone); 
    // } 
    // else if (rotationAngle == 180) 
    // { 
    //  img.RotateFlip(RotateFlipType.Rotate180FlipNone); 
    // } 
    // else if (rotationAngle == 270) 
    // { 
    //  img.RotateFlip(RotateFlipType.Rotate270FlipNone); 
    // } 
    //} 

    return img; 
} 

private static readonly Regex durationRegex = new Regex(@"Duration\: (?<duration>[\d\:\.]+)", RegexOptions.Compiled | RegexOptions.IgnoreCase | RegexOptions.ExplicitCapture); 
private TimeSpan GetVideoDuration(string inputFileName) 
{ 
    Process getDurationProcess = null; 
    try 
    { 
     const string fileInfoTemplate = @"-i ""{0}"""; 
     ProcessStartInfo psi = new ProcessStartInfo(ExePath, string.Format(fileInfoTemplate, inputFileName)); 
     psi.UseShellExecute = false; 
     psi.RedirectStandardOutput = true; 
     psi.RedirectStandardError = true; 
     psi.CreateNoWindow = true; 
     //psi.WorkingDirectory = Path.GetDirectoryName(ExePath); 
     //psi.EnvironmentVariables["Path"] = psi.EnvironmentVariables["Path"] + ";" + Path.GetDirectoryName(ExePath); 

     getDurationProcess = new Process(); 
     getDurationProcess.StartInfo = psi; 
     getDurationProcess.EnableRaisingEvents = true; 

     StringBuilder processOutput = new StringBuilder(); 
     StringBuilder processError = new StringBuilder(); 
     getDurationProcess.OutputDataReceived += (o, args) => 
     { 
      processOutput.AppendLine(args.Data); 
     }; 
     getDurationProcess.ErrorDataReceived += (o, args) => 
     { 
      processError.AppendLine(args.Data); 
     }; 

     getDurationProcess.Start(); 
     getDurationProcess.BeginOutputReadLine(); 
     getDurationProcess.BeginErrorReadLine(); 

     getDurationProcess.WaitForExit(); 

     //Don't do this - ffmpeg errors out because we didn't give it an output file 
     //if (getDurationProcess.ExitCode != 0) 
     //{ 
     // log.Error("Get video duration process failed with exitCode {0}. Process output:\r\n{1}\r\nProcess Error: {2}", getDurationProcess.ExitCode, processOutput, processError); 
     //} 

     //Now we need to parse output 
     #region Sample output 
     /* 
      ffmpeg version git-N-29946-g27614b1, Copyright (c) 2000-2011 the FFmpeg developers 
      built on May 15 2011 15:07:09 with gcc 4.5.3 
      configuration: --enable-gpl --enable-version3 --enable-memalign-hack --enable- 
     runtime-cpudetect --enable-avisynth --enable-bzlib --enable-frei0r --enable-libo 
     pencore-amrnb --enable-libopencore-amrwb --enable-libfreetype --enable-libgsm -- 
     enable-libmp3lame --enable-libopenjpeg --enable-librtmp --enable-libschroedinger 
      --enable-libspeex --enable-libtheora --enable-libvorbis --enable-libvpx --enabl 
     e-libx264 --enable-libxavs --enable-libxvid --enable-zlib --pkg-config=pkg-confi 
     g 
      libavutil 51. 2. 1/51. 2. 1 
      libavcodec 53. 5. 0/53. 5. 0 
      libavformat 53. 0. 3/53. 0. 3 
      libavdevice 53. 0. 0/53. 0. 0 
      libavfilter 2. 5. 0/2. 5. 0 
      libswscale 0. 14. 0/0. 14. 0 
      libpostproc 51. 2. 0/51. 2. 0 

     Seems stream 0 codec frame rate differs from container frame rate: 180000.00 (18 
     0000/1) -> 30.00 (30/1) 
     Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '..\fromQuicktime.mp4': 
      Metadata: 
      major_brand  : mp42 
      minor_version : 0 
      compatible_brands: mp42isomavc1 
      creation_time : 2011-04-22 16:36:45 
      encoder   : HandBrake 0.9.5 2011010300 
      Duration: 00:00:22.13, start: 0.000000, bitrate: 712 kb/s 
      Stream #0.0(und): Video: h264 (High), yuv420p, 480x272, 578 kb/s, 30 fps, 30 
      tbr, 90k tbn, 180k tbc 
      Metadata: 
       creation_time : 2011-04-22 16:36:45 
      Stream #0.1(und): Audio: aac, 44100 Hz, mono, s16, 127 kb/s 
      Metadata: 
       creation_time : 2011-04-22 16:36:45 
     At least one output file must be specified 
      * */ 
     #endregion 

     processOutput.Append(processError.ToString()); 

     Match m = durationRegex.Match(processOutput.ToString()); 
     Group durationGroup = m.Groups["duration"]; 

     TimeSpan duration; 
     if (!TimeSpan.TryParse(durationGroup.Value, out duration)) 
     { 
      log.Error("Failed to parse duration from FFMpeg output:\r\n{0}", processOutput); 
      return TimeSpan.Zero; 
     } 
     else 
     { 
      return duration; 
     } 
    } 
    finally 
    { 
     if (getDurationProcess != null) 
     { 
      getDurationProcess.Dispose(); 
      getDurationProcess = null; 
     } 
    } 
} 

Это опирается на следующих вспомогательных классах:

public static class WindowsProcessUtil 
{ 
    /// <summary> 
    /// Spawn a Windows process, capture StandardOut and StandardError, and wait for it to complete 
    /// </summary> 
    public static WindowsProcessResult RunProcess(string exePath, string cmdLineArgs, TimeSpan? timeout = null) 
    { 
     Process p = null; 
     StringBuilder processOutput = new StringBuilder(); 
     StringBuilder processError = new StringBuilder(); 
     int exitCode = 0; 

     try 
     { 
      ProcessStartInfo psi = new ProcessStartInfo(exePath, cmdLineArgs); 
      psi.UseShellExecute = false; 
      psi.RedirectStandardOutput = true; 
      psi.RedirectStandardError = true; 
      psi.CreateNoWindow = true; 
      psi.WorkingDirectory = Path.GetDirectoryName(exePath); 
      psi.EnvironmentVariables["Path"] = psi.EnvironmentVariables["Path"] + ";" + Path.GetDirectoryName(exePath); 
      psi.LoadUserProfile = true; 

      p = new Process(); 
      p.StartInfo = psi; 
      p.EnableRaisingEvents = true; 

      p.OutputDataReceived += (o, args) => 
      { 
       processOutput.AppendLine(args.Data); 
      }; 
      p.ErrorDataReceived += (o, args) => 
      { 
       processError.AppendLine(args.Data); 
      }; 

      p.Start(); 
      p.BeginOutputReadLine(); 
      p.BeginErrorReadLine(); 

      if (timeout.HasValue) 
      { 
       bool processExited = p.WaitForExit((int)timeout.Value.TotalMilliseconds); 
       if (!processExited) 
       { 
        p.Kill(); 
        throw new TimeoutException("Process did not complete after " + timeout.Value.TotalMilliseconds + " msec"); 
       } 
      } 
      else 
      { 
       p.WaitForExit(); 
      } 

      exitCode = p.ExitCode; 
     } 
     finally 
     { 
      if (p != null) 
      { 
       p.Dispose(); 
       p = null; 
      } 
     } 

     return new WindowsProcessResult() 
     { 
      ExitCode = exitCode, 
      StandardError = processError.ToString(), 
      StandardOutput = processOutput.ToString() 
     }; 
    } 
} 

public class WindowsProcessResult 
{ 
    public int ExitCode { get; set; } 
    public string StandardOutput { get; set; } 
    public string StandardError { get; set; } 
} 
+0

Спасибо за ответ, я думал о создании виртуального жесткого диска, где я могу временно размещать свои видео с лазурного хранения и их удалить их после того, как , Я считаю, что это довольно дорого. Я собираюсь проверить эту службу Windows Azure Media Services, которую вы пытались мне рассказать .. Спасибо! –

+0

ahmm еще один вопрос, чувак, можете ли вы дать мне пример того, как запустить его с помощью кода .net? –

+0

Вам не нужно использовать постоянное хранилище во время работы над видео - если VM умирает, вы можете просто перезапустить весь процесс. Таким образом, вы можете использовать диск локального хранилища (который является бесплатным) вместо постоянного виртуального диска. Я отредактирую свой ответ с кодом, чтобы запустить ffmpeg через несколько минут. –

Смежные вопросы