2023-04-01:当Go语言遇见FFmpeg视频解码器,使用Go语言改写decode_video.c文件,提升视频解码效率与开发体验。

答案2023-04-01:

步骤如下:

1.导入必要的依赖库,包括 fmt、os、unsafe 和其它 FFmpeg 库相关的 Go 库。

2.定义一个名为 main0() 的函数,该函数负责视频解码操作。在函数中定义了许多变量,例如文件名、编解码器、解析器、编解码器上下文、文件句柄、AVFrame 等等。

3.通过命令行参数获取输入文件名和输出文件名,并进行一些基本的参数检查。

4.通过调用 AvPacketAlloc() 函数创建一个 AVPacket 对象,用于存储解码后的帧数据。如果创建失败,则退出程序。

5.初始化输入缓冲区 inbuf 并设置结尾填充字节为 0。

6.调用 AvcodecFindDecoder() 函数查找 MPEG-1 视频解码器。如果找不到,则退出程序。

7.调用 AvParserInit() 函数初始化解析器。如果初始化失败,则退出程序。

8.调用 AvCodecAllocContext3() 函数分配一个新的编解码器上下文对象。如果分配失败,则退出程序。

9.调用 AvcodecOpen2() 函数打开编解码器。如果打开失败,则退出程序。

10.打开输入文件,并创建一个 AVFrame 对象。

11.进入循环,读取输入文件并将其分解成视频帧。如果读取失败或读取完毕,则跳出循环。

12.调用 AvParserParse2() 函数将输入缓冲区中的数据解析为视频帧,并存储在 AVPacket 对象中。如果解析失败,则退出程序。

13.如果成功解析到一个视频帧,则调用 decode() 函数对其进行解码并保存到输出文件中。

14.在循环结束后,调用 decode() 函数对剩余的数据进行解码并保存到输出文件中。

15.关闭输入文件句柄、解析器、编解码器上下文和 AVFrame 对象等资源,以避免内存泄漏。

16.定义一个名为 pgm_save() 的函数,该函数用于将视频帧写入 PGM 格式文件。

17.定义一个名为 decode() 的函数,该函数用于对视频帧进行解码并调用 pgm_save() 函数将其写入 PGM 格式文件。

18.定义 main() 函数,该函数将 FFmpeg 库的路径设置为当前目录下的 lib 子目录,并调用 main0() 函数进行视频解码操作。

注意:在 Windows 操作系统中,您可能需要将 FFmpeg 库的可执行文件添加到 PATH 环境变量中,或者使用 SetXXXPath() 函数设置它们的路径,才能够正常运行此代码。

代码见github/moonfdd/ffmpeg-go。

执行命令:

./lib/ffmpeg -i ./resources/big_buck_bunny.mp4 -c:v mpeg1video ./out/big_buck_bunny.mpg

go run ./examples/internalexamples/decode_video/main.go ./out/big_buck_bunny.mpg ./out/ppm/big_buck_bunny.yuv

./lib/ffplay  ./out/ppm/big_buck_bunny.yuv-113.ppm

golang代码如下:

package main

import (
"fmt"
"os"
"unsafe" "github.com/moonfdd/ffmpeg-go/ffcommon"
"github.com/moonfdd/ffmpeg-go/libavcodec"
"github.com/moonfdd/ffmpeg-go/libavutil"
) func main0() (ret ffcommon.FInt) {
var filename, outfilename string
var codec *libavcodec.AVCodec
var parser *libavcodec.AVCodecParserContext
var c *libavcodec.AVCodecContext
var f *os.File
var frame *libavutil.AVFrame
var inbuf [INBUF_SIZE + libavcodec.AV_INPUT_BUFFER_PADDING_SIZE]ffcommon.FUint8T
var data *ffcommon.FUint8T
var data_size ffcommon.FSizeT
var pkt *libavcodec.AVPacket if len(os.Args) <= 2 {
fmt.Printf("Usage: %s <input file> <output file>\nAnd check your input file is encoded by mpeg1video please.\n", os.Args[0])
os.Exit(0)
}
filename = os.Args[1]
outfilename = os.Args[2] pkt = libavcodec.AvPacketAlloc()
if pkt == nil {
os.Exit(1)
} /* set end of buffer to 0 (this ensures that no overreading happens for damaged MPEG streams) */
//memset(inbuf + INBUF_SIZE, 0, AV_INPUT_BUFFER_PADDING_SIZE); /* find the MPEG-1 video decoder */
codec = libavcodec.AvcodecFindDecoder(libavcodec.AV_CODEC_ID_MPEG1VIDEO)
if codec == nil {
fmt.Printf("Codec not found\n")
os.Exit(1)
} parser = libavcodec.AvParserInit(int32(codec.Id))
if parser == nil {
fmt.Printf("parser not found\n")
os.Exit(1)
} c = codec.AvcodecAllocContext3()
if c == nil {
fmt.Printf("Could not allocate video codec context\n")
os.Exit(1)
} /* For some codecs, such as msmpeg4 and mpeg4, width and height
MUST be initialized there because this information is not
available in the bitstream. */ /* open it */
if c.AvcodecOpen2(codec, nil) < 0 {
fmt.Printf("Could not open codec\n")
os.Exit(1)
} var err error
f, err = os.Open(filename)
if err != nil {
fmt.Printf("Could not open %s,err = %s\n", filename, err)
os.Exit(1)
} frame = libavutil.AvFrameAlloc()
if frame == nil {
fmt.Printf("Could not allocate video frame\n")
os.Exit(1)
} for {
/* read raw data from the input file */
var n int
n, err = f.Read(inbuf[:INBUF_SIZE])
if err != nil {
break
}
data_size = uint64(n)
if data_size == 0 {
break
} /* use the parser to split the data into frames */
data = (*byte)(unsafe.Pointer(&inbuf))
for data_size > 0 {
ret = parser.AvParserParse2(c, &pkt.Data, (*int32)(unsafe.Pointer(&pkt.Size)),
data, int32(data_size), libavutil.AV_NOPTS_VALUE, libavutil.AV_NOPTS_VALUE, 0)
if ret < 0 {
fmt.Printf("Error while parsing\n")
os.Exit(1)
}
data = (*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(data)) + uintptr(ret)))
data_size -= uint64(ret) if pkt.Size != 0 {
decode(c, frame, pkt, outfilename)
}
}
} /* flush the decoder */
decode(c, frame, nil, outfilename) f.Close() parser.AvParserClose()
libavcodec.AvcodecFreeContext(&c)
libavutil.AvFrameFree(&frame)
libavcodec.AvPacketFree(&pkt) return 0
} const INBUF_SIZE = 4096 func pgm_save(buf ffcommon.FBuf, wrap, xsize, ysize ffcommon.FInt, filename string) {
var f *os.File
var i ffcommon.FInt var err error
f, err = os.Create(filename)
if err != nil {
return
}
f.WriteString(fmt.Sprintf("P5\n%d %d\n%d\n", xsize, ysize, 255))
bytes := []byte{}
for i = 0; i < ysize; i++ {
for j := int32(0); j < xsize; j++ {
bytes = append(bytes, *(*byte)(unsafe.Pointer(uintptr(unsafe.Pointer(buf)) + uintptr(i*wrap+j))))
}
}
f.Write(bytes)
f.Close()
} func decode(dec_ctx *libavcodec.AVCodecContext, frame *libavutil.AVFrame, pkt *libavcodec.AVPacket, filename string) {
// var buf [1024]byte
var ret ffcommon.FInt ret = dec_ctx.AvcodecSendPacket(pkt)
if ret < 0 {
fmt.Printf("Error sending a packet for decoding\n")
os.Exit(1)
} for ret >= 0 {
ret = dec_ctx.AvcodecReceiveFrame(frame)
if ret == -libavutil.EAGAIN || ret == libavutil.AVERROR_EOF {
return
} else if ret < 0 {
fmt.Printf("Error during decoding %d\n", ret)
os.Exit(1)
} fmt.Printf("saving frame %3d\n", dec_ctx.FrameNumber)
//fflush(stdout) /* the picture is allocated by the decoder. no need to
free it */
pgm_save(frame.Data[0], frame.Linesize[0],
frame.Width, frame.Height, fmt.Sprintf("%s-%d.ppm", filename, dec_ctx.FrameNumber))
}
} func main() {
os.Setenv("Path", os.Getenv("Path")+";./lib")
ffcommon.SetAvutilPath("./lib/avutil-56.dll")
ffcommon.SetAvcodecPath("./lib/avcodec-58.dll")
ffcommon.SetAvdevicePath("./lib/avdevice-58.dll")
ffcommon.SetAvfilterPath("./lib/avfilter-56.dll")
ffcommon.SetAvformatPath("./lib/avformat-58.dll")
ffcommon.SetAvpostprocPath("./lib/postproc-55.dll")
ffcommon.SetAvswresamplePath("./lib/swresample-3.dll")
ffcommon.SetAvswscalePath("./lib/swscale-5.dll") genDir := "./out"
_, err := os.Stat(genDir)
if err != nil {
if os.IsNotExist(err) {
os.Mkdir(genDir, 0777) // Everyone can read write and execute
}
} main0()
}

2023-04-01:当Go语言遇见FFmpeg视频解码器,使用Go语言改写decode_video.c文件,提升视频解码效率与开发体验。的更多相关文章

  1. ok6410 u-boot-2012.04.01移植二修改源码支持单板

    继ok6410 u-boot-2012.04.01移植一后修改代码,对ok6410单板初始化,主要包括时钟.串口.NAND.DDR等初始化.这些工作在以前的裸板程序都写了,直接拿来用.我觉得先写裸板程 ...

  2. 移植u-boot.2012.04.01

    /*************************************************** *u-boot版本:u-boot2012.04.01 *gcc版本:arm-linux-gcc ...

  3. ok6410 u-boot-2012.04.01移植六完善MLC NAND支持

    继ok6410 u-boot-2012.04.01移植四.五后,开发板基本已支持MLC NAND,支持DM9000.但是通过NAND命令更新u-boot到NAND,还存在问题,需要根据u-boot的n ...

  4. ok6410 u-boot-2012.04.01移植七完善u-boot移植(u-boot移植结束)

    继ok6410 u-boot-2012.04.01移植六后,开发板已支持MLC NAND.DM9000等.但还需要完善比如环境变量.mtdpart分区.裁剪.制作补丁等.下面的工作就是完善移植的u-b ...

  5. ok6410 u-boot-2012.04.01移植五支持DM9000

    继ok6410 u-boot-2012.04.01移植四后,开发板基本已支持MLC NAND,但还有一些细节地方修改,这节增加DM9000支持,通过网卡tftp程序到内存,接着通过NAND命令写到NA ...

  6. 移植u-boot-2012.04.01到JZ2440

    开发环境:Ubuntu 12.04 开发板:JZ2440  256M NandFlash  64M SDRAM 交叉编译器:arm-linux-gcc-4.3.2 u-boot:u-boot-2012 ...

  7. uboot-2012.04.01移植编译前准备

    一:准备移植1.从下面的官网下载uboot-2012.04.012.建立sourceinsight工程 a.解压并在E:\colin weidongshan\transplant_u-boot-201 ...

  8. C# 使用ffmpeg视频截图

    <appSettings> <add key="ffmpeg" value="E:\ffmpeg\ffmpeg-20141012-git-20df026 ...

  9. 最简单的基于FFmpeg的移动端例子:IOS 视频解码器-保存

    ===================================================== 最简单的基于FFmpeg的移动端例子系列文章列表: 最简单的基于FFmpeg的移动端例子:A ...

  10. FFMPEG系列课程(一)打开视频解码器

    测试环境:windows10 开发工具:VS2013 从今天开始准备些FFmpeg的系列教程,今天是第一课我们研究下打开视频文件和视频解码器.演示环境在windows上,在Linux上代码也是一样. ...

随机推荐

  1. 多线程JUC练习

    package com.aliyun.test.learn; import java.util.concurrent.*; import java.util.concurrent.locks.Reen ...

  2. Jmeter--请求结果写入文件并生成报告

    一.数据写入文件 Jmeter中监听器控件中,都可以将"所有数据写入一个文件",且文件形式有:xml\jtl\csv 在需要写入的监听器下点击"浏览"按钮,选择 ...

  3. 关于npm audit fix无法修复问题的解决办法

    这两天新建项目 使用npm install的时候一直出现这个错误,使用npm audit fix 无法修复. 查询解决办法: 可以使用淘宝镜像源,会自动修复,然后下载相关依赖包 解决方法如下: 1.使 ...

  4. 修改/编辑jar包

    替换或者导入jar包时,jar包被自动压缩,springboot规定嵌套的jar包不能在被压缩的情况下存储. 解决(本文以升级ojdbc包为例): 使用jar命令解压jar包,在压缩包外重新替换jar ...

  5. WLAN的二层通信

    WLAN的二层通信中,无线接口收发的报文有4个地址: 发送地址(Transimission address),接收地址(Recevie address),源地址(Source address),目的地 ...

  6. Linux0.11源码学习(三)

    Linux0.11源码学习(三) linux0.11源码学习笔记 参考资料: https://github.com/sunym1993/flash-linux0.11-talk https://git ...

  7. Java面试——JVM知识

    一.什么情况下会发生栈内存溢出 [1]线程请求的栈深度大于虚拟机所允许的深度,将抛出 StackOverflowError 异常.递归的调用一个简单的方法,不断累积就会抛出 StackOverflow ...

  8. asp.net 应用程序中同步方法调用异步方法无响应解决方法

    微软发布 C# async/await 异步语法功能已经好久了,但是目前来看使用并不广泛.本人经过实践在开发过程中使用 async/await 一路到底确实很爽,而且也没有啥问题.但是在面对旧项目变更 ...

  9. pcm音频的录制、播放及转换

    操作系统 :Windows10_x64 pcm格式为原始音频数据,有时候会遇到需要录制.播放及转换的情况,这里记录下. 一.录制pcm音频 这里演示下使用Audacity进行pcm音频录音的过程. A ...

  10. vue指令系统之文本指令

    目录 什么是指令系统 文本指令 v-text指令 v-html指令 v-show v-if 什么是指令系统 指令系统是VUE提供的,语法为 v-xx 写在标签属性中的,系统都称之为指令 文本指令 文本 ...