使用 FFmpegCore 转换视频





5.00/5 (3投票s)
一个简单的代码片段,展示了如何在 .NET Core 中转换视频
引言
对于大多数开发者来说,处理多媒体是一片未知的领域,因为在常规业务应用中很少会遇到这种情况。因此,当我被要求为我目前正在进行的项目转换视频时,我预计会处理某种古老且维护不良的 C++ 库。所以 FFmpegCore 让我感到惊喜,因为它能够与 .NET Core 协同工作,而 .NET Core 正是我擅长的领域。
本文中的示例将使用 F# 提供 我非常喜欢的一种语言,但它们非常简单明了,因此翻译成 C# 应该不会有任何问题。
安装 FFmpeg
虽然文档中说明这个核心是“一个 .NET Standard FFMpeg/FFProbe 包装器”,但这并没有说明 Ffmpeg
/fmprobe
应该安装在运行应用程序的机器上。
此外,我认为值得澄清的是,FFMpeg 是一个跨平台命令行工具,允许处理视频。
在 Linux 上,一旦你执行 apt install
就可以使用了,但在 Windows 上,有一个需要注意的地方。在 Windows 上安装 FFmpeg
涉及下载二进制文件并将其放在一个文件夹中,但一旦你运行该工具,你可能会遇到错误
system.componentmodel.win32exception: the system cannot find the file specified
这个错误可以通过 static
类 FFMpegOptions
来解决
let options = FFMpegOptions()
options.RootDirectory <- "path to your binaries"
FFMpegOptions.Configure(options)
查询视频信息
为了查询视频信息,我们使用静态的 FFProbe
,它具有同步和异步 API 用于视频分析。让我们坚持使用 async
版本并将输出序列化以检查 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":[
]
}
转换视频
为了转换视频,可以使用 static
FFMpegArguments
类,它实现了一种静态构建器模式。同样,它也同时提供同步和异步 API,我们将坚持使用后者。
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
看起来很吓人,但“宽度不能被 2 整除”表明 FFMpeg
对奇数的 width
和 height
有要求。我使用这个简单的技巧来欺骗它并强制它转换我的视频。
let newWidth =
if videoInfo.PrimaryVideoStream.Height % 2 = 0 then
videoInfo.PrimaryVideoStream.Height
else videoInfo.PrimaryVideoStream.Height - 1
videoInfo
这里是几段上面的 FFProbe
的工作结果。
FFmpegCore
能够做更多的事情,例如,截取屏幕截图、更改视频缩略图等,但我将把研究留给好奇的读者。
历史
- 2021 年 2 月 23 日 - 提交初始版本