原文:https://www.codeproject.com/Tips/5295527/Converting-Video-with-FFmpegCore
FFmpegCore是围绕FFMpeg/FFProbe的一个方便的包装器,可以方便地处理视频。在本文中,我们将了解如何利用它进行视频转换。
介绍
对于大多数开发人员来说,使用多媒体是一种莫名其妙的事情,因为在使用通常的业务应用程序时很少遇到这种情况。所以,当我被任务转换为我目前正在进行的项目的视频时,我期待着处理一些旧的维护不好的C++库。所以FFmpegCore是一个惊喜,因为它支持使用.NET核心,这是我的专长领域。
本文中的示例将用F#提供,我非常喜欢F#,但它们非常简单,因此将它们翻译成C#应该没有问题。
虽然文档显示这个核心是“一个.NET标准的FFMpeg/FFProbe包装器”,但这并没有说明FFMpeg/fmprobe应该安装在运行应用程序的机器上。
此外,我认为值得澄清的是,FFMpeg是一个跨平台的命令行工具,允许处理视频。
在Linux上,一旦你安装了apt,你就可以在Windows上运行了,这是一个有趣的问题。在Windows上安装FFmpeg只需下载二进制文件并将其放入文件夹即可,但一旦运行该工具,您可能会遇到以下错误:
system.componentmodel.win32exception: the system cannot find the file specified
在静态类FFMpegOptions的帮助下进行了修复:
let options = FFMpegOptions()
options.RootDirectory <- "path to your binaries"
FFMpegOptions.Configure(options)
查询视频信息
为了查询视频的相关信息,我们使用了同步和异步API的静态FFProbe进行视频分析。让我们继续使用异步版本并序列化输出,以检查FFProbe提供的丰富信息。
async {
let! videoInfo = FFProbe.AnalyseAsync fileName |> Async.AwaitTask
return JsonSerializer.Serialize videoInfo
}
输出可能如下所示:
{
"Path":"D:\\giphy.mp4",
"Extension":".mp4",
"Duration":{
"Ticks":17200000,
"Days":0,
"Hours":0,
"Milliseconds":720,
"Minutes":0,
"Seconds":1,
"TotalDays":1.990740740740741E-05,
"TotalHours":0.00047777777777777776,
"TotalMilliseconds":1720,
"TotalMinutes":0.028666666666666667,
"TotalSeconds":1.72
},
"Format":{
"Duration":{
"Ticks":17200000,
"Days":0,
"Hours":0,
"Milliseconds":720,
"Minutes":0,
"Seconds":1,
"TotalDays":1.990740740740741E-05,
"TotalHours":0.00047777777777777776,
"TotalMilliseconds":1720,
"TotalMinutes":0.028666666666666667,
"TotalSeconds":1.72
},
"FormatName":"mov,mp4,m4a,3gp,3g2,mj2",
"FormatLongName":"QuickTime / MOV",
"StreamCount":1,
"ProbeScore":100,
"BitRate":458339,
"Tags":{
"major_brand":"isom",
"minor_version":"512",
"compatible_brands":"isomiso2avc1mp41",
"encoder":"Lavf56.40.101"
}
},
"PrimaryAudioStream":null,
"PrimaryVideoStream":{
"AvgFrameRate":25,
"BitsPerRawSample":8,
"DisplayAspectRatio":{
},
"Profile":"Constrained Baseline",
"Width":480,
"Height":264,
"FrameRate":25,
"PixelFormat":"yuv420p",
"Rotation":0,
"Index":0,
"CodecName":"h264",
"CodecLongName":"H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"BitRate":453744,
"Duration":{
"Ticks":17200000,
"Days":0,
"Hours":0,
"Milliseconds":720,
"Minutes":0,
"Seconds":1,
"TotalDays":1.990740740740741E-05,
"TotalHours":0.00047777777777777776,
"TotalMilliseconds":1720,
"TotalMinutes":0.028666666666666667,
"TotalSeconds":1.72
},
"Language":"und",
"Tags":{
"language":"und",
"handler_name":"VideoHandler",
"vendor_id":"[0][0][0][0]"
}
},
"VideoStreams":[
{
"AvgFrameRate":25,
"BitsPerRawSample":8,
"DisplayAspectRatio":{
},
"Profile":"Constrained Baseline",
"Width":480,
"Height":264,
"FrameRate":25,
"PixelFormat":"yuv420p",
"Rotation":0,
"Index":0,
"CodecName":"h264",
"CodecLongName":"H.264 / AVC / MPEG-4 AVC / MPEG-4 part 10",
"BitRate":453744,
"Duration":{
"Ticks":17200000,
"Days":0,
"Hours":0,
"Milliseconds":720,
"Minutes":0,
"Seconds":1,
"TotalDays":1.990740740740741E-05,
"TotalHours":0.00047777777777777776,
"TotalMilliseconds":1720,
"TotalMinutes":0.028666666666666667,
"TotalSeconds":1.72
},
"Language":"und",
"Tags":{
"language":"und",
"handler_name":"VideoHandler",
"vendor_id":"[0][0][0][0]"
}
}
],
"AudioStreams":[
]
}
转换视频
async {
let! _ =
FFMpegArguments
.FromFileInput(fileName)
.OutputToFile(outputFileName,
true,
fun options -> options
.WithVideoCodec(VideoCodec.LibX264)
.WithAudioCodec(AudioCodec.Aac)
.WithVariableBitrate(4)
.Resize(newWidth, newHeight)
|> ignore)
.ProcessAsynchronously() |> Async.AwaitTask
()
}
在某些情况下,FFMpeg可能会返回错误。
"ffmpeg version 2021-01-24-git-1775688292-full_build-www.gyan.dev Copyright (c) 2000-2021
the FFmpeg developers\n built with gcc 10.2.0 (Rev6, Built by MSYS2 project)\n
configuration: --enable-gpl --enable-version3 --enable-static --disable-w32threads
--disable-autodetect --enable-fontconfig --enable-iconv --enable-gnutls --enable-libxml2
--enable-gmp --enable-lzma --enable-libsnappy --enable-zlib --enable-libsrt --enable-libssh
--enable-libzmq --enable-avisynth --enable-libbluray --enable-libcaca --enable-sdl2
--enable-libdav1d --enable-libzvbi --enable-librav1e --enable-libsvtav1 --enable-libwebp
--enable-libx264 --enable-libx265 --enable-libxvid --enable-libaom --enable-libopenjpeg
--enable-libvpx --enable-libass --enable-frei0r --enable-libfreetype --enable-libfribidi
--enable-libvidstab --enable-libvmaf --enable-libzimg --enable-amf --enable-cuda-llvm
--enable-cuvid --enable-ffnvcodec --enable-nvdec --enable-nvenc --enable-d3d11va
--enable-dxva2 --enable-libmfx --enable-libglslang --enable-vulkan --enable-opencl
--enable-libcdio --enable-libgme --enable-libmodplug --enable-libopenmpt
--enable-libopencore-amrwb --enable-libmp3lame --enable-libshine --enable-libtheora
--enable-libtwolame --enable-libvo-amrwbenc --enable-libilbc --enable-libgsm
--enable-libopencore-amrnb --enable-libopus --enable-libspeex --enable-libvorbis
--enable-ladspa --enable-libbs2b --enable-libflite --enable-libmysofa --enable-librubberband
--enable-libsoxr --enable-chromaprint\n libavutil
56. 63.101 / 56. 63.101\n libavcodec 58.117.101 / 58.117.101\n libavformat
58. 65.101 / 58. 65.101\n libavdevice 58. 11.103 / 58. 11.103\n libavfilter
7. 96.100 / 7. 96.100\n libswscale 5. 8.100 / 5. 8.100\n libswresample
3. 8.100 / 3. 8.100\n libpostproc 55. 8.100 / 55.
8.100\nInput #0, mov,mp4,m4a,3gp,3g2,mj2, from 'D:\\giphy.mp4':\n Metadata:\n
major_brand : isom\n minor_version : 512\n compatible_brands: isomiso2avc1mp41\n
encoder : Lavf56.40.101\n Duration: 00:00:01.72, start: 0.000000,
bitrate: 458 kb/s\n Stream #0:0(und): Video: h264 (Constrained Baseline)
(avc1 / 0x31637661), yuv420p, 480x264 [SAR 1:1 DAR 20:11], 453 kb/s, 25 fps, 25 tbr,
12800 tbn, 50 tbc (default)\n Metadata:\n handler_name : VideoHandler\n
vendor_id : [0][0][0][0]\nCodec AVOption vbr (Variable bit rate mode)
specified for output file #0 (D:\\kek.mp4) has not been used for any stream.
The most likely reason is either wrong type (e.g. a video option with no video streams)
or that it is a private option of some encoder which was not actually used for any stream.
\nStream mapping:\n Stream #0:0 -> #0:0 (h264 (native) -> h264 (libx264))\nPress [q] to stop,
[?] for help\n[libx264 @ 000001cd5ac43100] width not divisible by 2
(101x101)\nError initializing output stream 0:0 -- Error while opening encoder for
output stream #0:0 - maybe incorrect parameters such as bit_rate, rate, width or
height\nConversion failed!"
虽然stacktrace相当吓人,但“width not divisible by 2”表明FFMpeg具有奇数宽度和高度的特性。我用这个简单的黑客欺骗它,强迫它转换我的视频。
let newWidth =
if videoInfo.PrimaryVideoStream.Height % 2 = 0 then
videoInfo.PrimaryVideoStream.Height
else videoInfo.PrimaryVideoStream.Height - 1
这里的videoInfo是上面几段工作的结果。
FFmpegCore可以做更多的事情,比如捕捉屏幕截图,改变视频缩略图等等,但是我会把它留给好奇的读者去研究。