2023-04-18:ffmpeg中的hw_decode.c的功能是通过使用显卡硬件加速器(如 NVIDIA CUDA、Intel Quick Sync Video 等)对视频进行解码,从而提高解码效率和性能。在进行硬件加速解码时,相较于 CPU 的软件解码方式,GPU 可以利用其并行处理能力和更高的带宽进行更高效的解码操作。请用go语言改写hw_decode.c文件。

答案2023-04-18:

hw_decode.c 功能和执行过程

ffmpeg 中的 hw_decode.c 代码,其功能是通过使用显卡硬件加速器对视频进行解码,从而提高解码效率和性能。下面将分步骤描述该代码的功能和执行过程。

  1. 引入头文件

    代码开头引入了必要的头文件,包括 libavcodec/avcodec.h、libavformat/avformat.h、libavutil/pixdesc.h 等,这些头文件定义了解码和编码相关的结构体和函数。

  2. 初始化变量和数据

    接下来的一段代码初始化了一些变量和数据,例如 hw_device_ctx 是显卡设备上下文的引用,hw_pix_fmt 是像素格式等。它们都将在后面的代码中使用到。

  3. 硬件加速器初始化

    在 hw_decoder_init 函数中,调用 av_hwdevice_ctx_create 创建指定类型的硬件加速器,并将它保存到 ctx->hw_device_ctx 所指向的 AVBufferRef 缓冲区中。

  4. 获取硬件支持的像素格式

    在 get_hw_format 函数中,遍历 pix_fmts 数组,查找是否有与 hw_pix_fmt 相等的像素格式,如果找到则返回该像素格式,否则返回 AV_PIX_FMT_NONE。

  5. 解码和输出

    decode_write 函数是该代码的核心部分,实现了解码和输出功能。首先调用 avcodec_send_packet 将输入的 packet 数据发送给解码器,然后进入一个无限循环,直到所有数据都被解码并输出。在循环中,先调用 av_frame_alloc 分配 AVFrame 帧空间,然后调用 avcodec_receive_frame 从解码器中接收已解码的帧数据。如果返回的是 EAGAIN 或 EOF,则退出循环;如果出现错误则跳转到 fail 标签处处理。如果解码得到的帧格式与硬件支持的像素格式相同,则将该帧数据从 GPU 拷贝到 CPU 上,再调用 av_image_copy_to_buffer 将帧数据复制到内存缓冲区中,并通过 fwrite 函数将数据写入文件中。最后通过 av_frame_free 和 av_freep 函数释放内存空间。

  6. 主函数

    main 函数首先解析命令行参数,包括设备类型、输入文件名和输出文件名。然后通过 avformat_open_input 打开输入文件,通过 av_find_best_stream 查找视频流,并获取硬件支持的像素格式。接下来创建 AVCodexContext 上下文,设置 get_format 回调函数和硬件加速器上下文。通过 avcodec_open2 打开解码器,并打开输出文件。最后通过 av_read_frame 读取文件数据,调用 decode_write 函数进行解码和输出,直到读取完毕。

综上所述,该代码实现了使用显卡硬件加速器对视频进行解码的功能,并通过调用相关的结构体和函数实现了硬件加速器的初始化、解码和输出等操作。其主要思路是将显卡的并行处理能力和更高的带宽用于视频解码,从而提高解码效率和性能。

go代码如下:

github/moonfdd/ffmpeg-go库,把hw_decode.c改写成了go代码。如下:

  1. package main
  2. import (
  3. "fmt"
  4. "os"
  5. "unsafe"
  6. "github.com/moonfdd/ffmpeg-go/ffcommon"
  7. "github.com/moonfdd/ffmpeg-go/libavcodec"
  8. "github.com/moonfdd/ffmpeg-go/libavformat"
  9. "github.com/moonfdd/ffmpeg-go/libavutil"
  10. )
  11. func main0() (ret ffcommon.FInt) {
  12. var input_ctx *libavformat.AVFormatContext
  13. var video_stream ffcommon.FInt
  14. var video *libavformat.AVStream
  15. var decoder_ctx *libavcodec.AVCodecContext
  16. var decoder *libavcodec.AVCodec
  17. var packet libavformat.AVPacket
  18. var type0 libavutil.AVHWDeviceType
  19. var i ffcommon.FInt
  20. if len(os.Args) < 4 {
  21. fmt.Printf("Usage: %s <device type> <input file> <output file>\n", os.Args[0])
  22. return -1
  23. }
  24. type0 = libavutil.AvHwdeviceFindTypeByName(os.Args[1])
  25. if type0 == libavutil.AV_HWDEVICE_TYPE_NONE {
  26. fmt.Printf("Device type %s is not supported.\n", os.Args[1])
  27. fmt.Printf("Available device types:")
  28. type0 = libavutil.AvHwdeviceIterateTypes(type0)
  29. for type0 != libavutil.AV_HWDEVICE_TYPE_NONE {
  30. fmt.Printf(" %s", libavutil.AvHwdeviceGetTypeName(type0))
  31. type0 = libavutil.AvHwdeviceIterateTypes(type0)
  32. }
  33. fmt.Printf("\n")
  34. return -1
  35. }
  36. /* open the input file */
  37. if libavformat.AvformatOpenInput(&input_ctx, os.Args[2], nil, nil) != 0 {
  38. fmt.Printf("Cannot open input file '%s'\n", os.Args[2])
  39. return -1
  40. }
  41. if input_ctx.AvformatFindStreamInfo(nil) < 0 {
  42. fmt.Printf("Cannot find input stream information.\n")
  43. return -1
  44. }
  45. /* find the video stream information */
  46. ret = input_ctx.AvFindBestStream(libavutil.AVMEDIA_TYPE_VIDEO, -1, -1, &decoder, 0)
  47. if ret < 0 {
  48. fmt.Printf("Cannot find a video stream in the input file\n")
  49. return -1
  50. }
  51. video_stream = ret
  52. for i = 0; ; i++ {
  53. config := decoder.AvcodecGetHwConfig(i)
  54. if config == nil {
  55. fmt.Printf("Decoder %s does not support device type %s.\n",
  56. ffcommon.StringFromPtr(decoder.Name), libavutil.AvHwdeviceGetTypeName(type0))
  57. return -1
  58. }
  59. if config.Methods&libavcodec.AV_CODEC_HW_CONFIG_METHOD_HW_DEVICE_CTX != 0 && config.DeviceType == type0 {
  60. hw_pix_fmt = config.PixFmt
  61. break
  62. }
  63. }
  64. decoder_ctx = decoder.AvcodecAllocContext3()
  65. if decoder_ctx == nil {
  66. return -libavutil.ENOMEM
  67. }
  68. video = input_ctx.GetStream(uint32(video_stream))
  69. if decoder_ctx.AvcodecParametersToContext(video.Codecpar) < 0 {
  70. return -1
  71. }
  72. decoder_ctx.GetFormat = ffcommon.NewCallback(get_hw_format)
  73. if hw_decoder_init(decoder_ctx, type0) < 0 {
  74. return -1
  75. }
  76. ret = decoder_ctx.AvcodecOpen2(decoder, nil)
  77. if ret < 0 {
  78. fmt.Printf("Failed to open codec for stream #%d\n", video_stream)
  79. return -1
  80. }
  81. /* open the file to dump raw data */
  82. output_file, _ = os.Create(os.Args[3])
  83. /* actual decoding and dump the raw data */
  84. for ret >= 0 {
  85. ret = input_ctx.AvReadFrame(&packet)
  86. if ret < 0 {
  87. break
  88. }
  89. if uint32(video_stream) == packet.StreamIndex {
  90. ret = decode_write(decoder_ctx, &packet)
  91. }
  92. packet.AvPacketUnref()
  93. }
  94. /* flush the decoder */
  95. packet.Data = nil
  96. packet.Size = 0
  97. ret = decode_write(decoder_ctx, &packet)
  98. packet.AvPacketUnref()
  99. if output_file != nil {
  100. output_file.Close()
  101. }
  102. libavcodec.AvcodecFreeContext(&decoder_ctx)
  103. libavformat.AvformatCloseInput(&input_ctx)
  104. libavutil.AvBufferUnref(&hw_device_ctx)
  105. return 0
  106. }
  107. var hw_device_ctx *libavutil.AVBufferRef
  108. var hw_pix_fmt libavutil.AVPixelFormat
  109. var output_file *os.File
  110. func hw_decoder_init(ctx *libavcodec.AVCodecContext, type0 libavutil.AVHWDeviceType) ffcommon.FInt {
  111. var err ffcommon.FInt = 0
  112. err = libavutil.AvHwdeviceCtxCreate(&hw_device_ctx, type0, "", nil, 0)
  113. if err < 0 {
  114. fmt.Printf("Failed to create specified HW device.\n")
  115. return err
  116. }
  117. ctx.HwDeviceCtx = hw_device_ctx.AvBufferRef()
  118. return err
  119. }
  120. func get_hw_format(ctx *libavcodec.AVCodecContext, pix_fmts *libavutil.AVPixelFormat) uintptr {
  121. var p *libavutil.AVPixelFormat
  122. for p = pix_fmts; *p != -1; p = (*libavutil.AVPixelFormat)(unsafe.Pointer(uintptr(unsafe.Pointer(p)) + uintptr(4))) {
  123. if *p == hw_pix_fmt {
  124. return uintptr(*p)
  125. }
  126. }
  127. fmt.Printf("Failed to get HW surface format.\n")
  128. r := libavutil.AVPixelFormat(libavutil.AV_PIX_FMT_NONE)
  129. return uintptr(r)
  130. }
  131. func decode_write(avctx *libavcodec.AVCodecContext, packet *libavcodec.AVPacket) ffcommon.FInt {
  132. var frame, sw_frame *libavutil.AVFrame
  133. var tmp_frame *libavutil.AVFrame
  134. var buffer *ffcommon.FUint8T
  135. var size ffcommon.FInt
  136. var ret ffcommon.FInt = 0
  137. var e error
  138. ret = avctx.AvcodecSendPacket(packet)
  139. if ret < 0 {
  140. fmt.Printf("Error during decoding\n")
  141. return ret
  142. }
  143. for {
  144. frame = libavutil.AvFrameAlloc()
  145. sw_frame = libavutil.AvFrameAlloc()
  146. if frame == nil || sw_frame == nil {
  147. fmt.Printf("Can not alloc frame\n")
  148. ret = -libavutil.ENOMEM
  149. goto fail
  150. }
  151. ret = avctx.AvcodecReceiveFrame(frame)
  152. if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF {
  153. libavutil.AvFrameFree(&frame)
  154. libavutil.AvFrameFree(&sw_frame)
  155. return 0
  156. } else if ret < 0 {
  157. fmt.Printf("Error while decoding\n")
  158. goto fail
  159. }
  160. if frame.Format == hw_pix_fmt {
  161. /* retrieve data from GPU to CPU */
  162. ret = libavutil.AvHwframeTransferData(sw_frame, frame, 0)
  163. if ret < 0 {
  164. fmt.Printf("Error transferring the data to system memory\n")
  165. goto fail
  166. }
  167. tmp_frame = sw_frame
  168. } else {
  169. tmp_frame = frame
  170. }
  171. size = libavutil.AvImageGetBufferSize(tmp_frame.Format, tmp_frame.Width,
  172. tmp_frame.Height, 1)
  173. buffer = (*byte)(unsafe.Pointer(libavutil.AvMalloc(uint64(size))))
  174. if buffer == nil {
  175. fmt.Printf("Can not alloc buffer\n")
  176. ret = -libavutil.ENOMEM
  177. goto fail
  178. }
  179. ret = libavutil.AvImageCopyToBuffer(buffer, size,
  180. (*[4]*byte)(unsafe.Pointer(&tmp_frame.Data)),
  181. (*[4]int32)(unsafe.Pointer(&tmp_frame.Linesize)), tmp_frame.Format,
  182. tmp_frame.Width, tmp_frame.Height, 1)
  183. if ret < 0 {
  184. fmt.Printf("Can not copy image to buffer\n")
  185. goto fail
  186. }
  187. _, e = output_file.Write(ffcommon.ByteSliceFromByteP(buffer, int(size)))
  188. if e != nil {
  189. fmt.Printf("Failed to dump raw data.\n")
  190. goto fail
  191. }
  192. fail:
  193. libavutil.AvFrameFree(&frame)
  194. libavutil.AvFrameFree(&sw_frame)
  195. libavutil.AvFreep(uintptr(unsafe.Pointer(&buffer)))
  196. if ret < 0 {
  197. return ret
  198. }
  199. }
  200. }
  201. func main() {
  202. // go run ./examples/internalexamples/hw_decode/main.go cuda ./resources/big_buck_bunny.mp4 ./out/hw.yuv
  203. // ./lib/ffplay -pixel_format yuv420p -video_size 640x360 ./out/hw.yuv
  204. os.Setenv("Path", os.Getenv("Path")+";./lib")
  205. ffcommon.SetAvutilPath("./lib/avutil-56.dll")
  206. ffcommon.SetAvcodecPath("./lib/avcodec-58.dll")
  207. ffcommon.SetAvdevicePath("./lib/avdevice-58.dll")
  208. ffcommon.SetAvfilterPath("./lib/avfilter-56.dll")
  209. ffcommon.SetAvformatPath("./lib/avformat-58.dll")
  210. ffcommon.SetAvpostprocPath("./lib/postproc-55.dll")
  211. ffcommon.SetAvswresamplePath("./lib/swresample-3.dll")
  212. ffcommon.SetAvswscalePath("./lib/swscale-5.dll")
  213. genDir := "./out"
  214. _, err := os.Stat(genDir)
  215. if err != nil {
  216. if os.IsNotExist(err) {
  217. os.Mkdir(genDir, 0777) // Everyone can read write and execute
  218. }
  219. }
  220. main0()
  221. }

执行命令如下:

  1. go run ./examples/internalexamples/hw_decode/main.go cuda ./resources/big_buck_bunny.mp4 ./out/hw.yuv
  2. ./lib/ffplay -pixel_format yuv420p -video_size 640x360 ./out/hw.yuv



解码出来的视频,看起来有点失真的。

代码分析

首先,我们需要导入所需的库文件。在主函数中,我们首先检查输入参数数量是否正确,如果不正确则输出使用说明并返回错误。

接下来,我们通过设备类型名称获取设备类型,如果不支持该设备类型,则输出可用设备类型列表并返回错误。

在打开输入文件之后,我们使用AvFindBestStream函数查找最佳视频流,并使用其参数初始化解码器并打开解码器。

我们得到每帧数据之后,解码函数AvcodecSendPacket和AvcodecReceiveFrame会被循环调用,以将解码后的帧数据写入输出文件。

最后,我们关闭所有打开的资源,包括输入、输出文件和解码器等。

结语

本文介绍了如何使用Golang实现FFmpeg硬解码程序。通过对FFmpeg官方的HW Decode示例进行适当修改,我们成功地完成了设备类型检查、输入文件打开、解码器配置和输出文件处理等功能。此外,我们也介绍了如何在实际应用中使用FFmpeg库,并提供了一些代码片段供读者参考。

2023-04-18:ffmpeg中的hw_decode.c的功能是通过使用显卡硬件加速器(如 NVIDIA CUDA、Intel Quick Sync Video 等)对视频进行解码,从而提高解码效的更多相关文章

  1. Wed Jul 04 18:01:38 CST 2018 WARN: Establishing SSL connection without server's identity verification is not recommended

    Wed Jul 04 18:01:38 CST 2018 WARN: Establishing SSL connection without server's identity verificatio ...

  2. (转)ffmpeg 中 av_read_frame_internal分析

    作者: chenwei1983    时间: 2012-3-5 04:21 PM标题: ffmpeg 中 av_read_frame_internal分析                       ...

  3. ffmpeg中关于EAGAIN的理解及非阻塞IO

    ffmpeg为在linux下开发的开源音视频框架,所以经常会碰到很多错误(设置errno),其中EAGAIN是其中比较常见的一个错误(比如用在非阻塞操作中).  try again,从字面上来看,是提 ...

  4. 【并行计算与CUDA开发】英伟达硬件加速解码器在 FFMPEG 中的使用

    目录(?)[-] 私有驱动 编译 FFMPEG 使用 nvenc 这篇文档介绍如何在 ffmpeg 中使用 nvenc 硬件编码器. 私有驱动 nvenc 本身是依赖于 nvidia 底层的私有驱动的 ...

  5. FFmpeg 中AVPacket的使用

    AVPacket保存的是解码前的数据,也就是压缩后的数据.该结构本身不直接包含数据,其有一个指向数据域的指针,FFmpeg中很多的数据结构都使用这种方法来管理数据. AVPacket的使用通常离不开下 ...

  6. 【DDD/CQRS/微服务架构案例】在Ubuntu 14.04.4 LTS中运行WeText项目的服务端

    在<WeText项目:一个基于.NET实现的DDD.CQRS与微服务架构的演示案例>文章中,我介绍了自己用Visual Studio 2015(C# 6.0 with .NET Frame ...

  7. FFmpeg中HLS文件解析源码

    不少人都在找FFmpeg中是否有hls(m3u8)解析的源码,其实是有的.就是ffmpeg/libavformat/hlsproto.c,它依赖的文件也在那个目录中. 如果要是单纯想解析HLS的话,建 ...

  8. ffmpeg中的sws_scale算法性能测试

    经常用到ffmpeg中的sws_scale来进行图像缩放和格式转换,该函数可以使用各种不同算法来对图像进行处理.以前一直很懒,懒得测试和甄 别应该使用哪种算法,最近的工作时间,很多时候需要等待别人.忙 ...

  9. ffmpeg 中添加264支持

    转自:http://blog.sina.com.cn/s/blog_513f4e8401011yuq.html ffmpeg 中带有264的解码,没有编码,需要添加x264: 参考百度上的“windo ...

  10. 零基础学习视频解码之FFMpeg中比较重要的函数以及数据结构

    http://www.cnblogs.com/tanlon/p/3879081.html 在正式开始解码练习前先了解下关于FFmpeg中比较重要的函数以及数据结构. 1. 数据结构:  (1) AVF ...

随机推荐

  1. OO多项式求导作业总结

    一.程序分析 1.1第一次作业 第一次作业是简单的多项式求导,甚至没有括号嵌套.但是,就是这个在如今看来如此简单的作业,由于俺寒假过于起飞,pre没做,正则表达式也不会(属实拉跨),一度想用c语言字符 ...

  2. Unity图片转存及读取

    [code]csharpcode: /// <summary> /// 加载图片 /// </summary> private Sprite LoadTexture(strin ...

  3. https加固,https://ip暴露后端IP。

    增加server配置server { listen 443 default_server; server_name _ ; ssl on; ssl_certificate test.crt 随便设置一 ...

  4. git clone的时候出现 fatal: unable to access 'https://github.com/...':OpenSSL SSL_read: Connection was reset, errno 10054解决方法

    git clone的时候出现fatal: unable to access 'https://github.com/...':OpenSSL SSL_read: Connection was rese ...

  5. Maven学习笔记1:Maven基本介绍和安装配置

    一.认识Maven 官网 http://maven.apache.org/ 上面有最权威的说明,其中包括下载.安装.运行示例,但是是英文版的. Maven是什么 Maven是一个项目管理工具. 它有何 ...

  6. fortify Unsafe JNI

    Unsafe JNI 主要解决问题: 1.system.currentTimeMillis(); 使用SystemClock.now()替换. 2.isAssignableFrom(); 使用新定义的 ...

  7. 使用 DeepSpeed 和 Hugging Face 🤗 Transformer 微调 FLAN-T5 XL/XXL

    Scaling Instruction-Finetuned Language Models 论文发布了 FLAN-T5 模型,它是 T5 模型的增强版.FLAN-T5 由很多各种各样的任务微调而得,因 ...

  8. 一次spark任务提交参数的优化

    起因 新接触一个spark集群,明明集群资源(core,内存)还有剩余,但是提交的任务却申请不到资源. 分析 环境 spark 2.2.0 基于yarn集群 参数 spark任务提交参数中最重要的几个 ...

  9. 网络----OSI七层

    OSI 订制的是一个用于计算机或通信系统间互联的标准体质(一般称为OSI参考模型或七层模型) OSI 模型把网络通信的工作分为7层分别是: 常用内容:物理层 数据链层 网络层 注解 OSI 7层 1. ...

  10. jQ的事件

    <!DOCTYPE html> <html lang="en"> <head> <meta charset="UTF-8&quo ...