2023-04-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。

答案2023-04-05:

使用github/moonfdd/ffmpeg-go库。

代码使用FFmpeg库打开一个音视频文件,提取其中的视频和音频流,并解码每一帧数据。它将解码后的视频和音频帧写入不同的输出文件中。代码中使用了libavformat、libavcodec和libavutil库提供的函数。

大体过程如下:

1.设置FFmpeg库的路径。

2.打开音视频文件并分配AVFormatContext结构体。

3.获取音频和视频流的信息,并选择合适的解码器进行解码。

4.对于视频流:
分配AVCodecContext结构体。
设置解码器参数并打开解码器。
读取视频帧并进行解码。
将解码后的视频帧写入输出文件。

5.对于音频流:
分配AVCodecContext结构体。
设置解码器参数并打开解码器。
读取音频帧并进行解码。
将解码后的音频帧写入输出文件。

6.关闭解码器和输入输出文件句柄,释放内存。

命令如下:

go run ./examples/internalexamples/demuxing_decoding/main.go ./resources/big_buck_bunny.mp4 ./out/big_buck_bunny.yuv ./out/big_buck_bunny.pcm

./lib/ffplay -f rawvideo -pix_fmt yuv420p -video_size 640x360 ./out/big_buck_bunny.yuv

./lib/ffplay -f f32le -ac 1 -ar 22050 .\out\big_buck_bunny.pcm

代码如下:

package main

import (
"fmt"
"os"
"unsafe" "github.com/moonfdd/ffmpeg-go/ffcommon"
"github.com/moonfdd/ffmpeg-go/libavcodec"
"github.com/moonfdd/ffmpeg-go/libavformat"
"github.com/moonfdd/ffmpeg-go/libavutil"
) func main0() (ret ffcommon.FInt) { if len(os.Args) != 4 {
fmt.Printf("usage: %s input_file video_output_file audio_output_file\nAPI example program to show how to read frames from an input file.\nThis program reads frames from a file, decodes them, and writes decoded\nvideo frames to a rawvideo file named video_output_file, and decoded\naudio frames to a rawaudio file named audio_output_file.\n",
os.Args[0])
os.Exit(1)
}
src_filename = os.Args[1]
video_dst_filename = os.Args[2]
audio_dst_filename = os.Args[3] /* open input file, and allocate format context */
if libavformat.AvformatOpenInput(&fmt_ctx, src_filename, nil, nil) < 0 {
fmt.Printf("Could not open source file %s\n", src_filename)
os.Exit(1)
} /* retrieve stream information */
if fmt_ctx.AvformatFindStreamInfo(nil) < 0 {
fmt.Printf("Could not find stream information\n")
os.Exit(1)
} for {
if open_codec_context(&video_stream_idx, &video_dec_ctx, fmt_ctx, libavutil.AVMEDIA_TYPE_VIDEO) >= 0 {
video_stream = fmt_ctx.GetStream(uint32(video_stream_idx)) video_dst_file, _ = os.Create(video_dst_filename)
if video_dst_file == nil {
fmt.Printf("Could not open destination file %s\n", video_dst_filename)
ret = 1
break
} /* allocate image where the decoded image will be put */
width = video_dec_ctx.Width
height = video_dec_ctx.Height
pix_fmt = video_dec_ctx.PixFmt
ret = libavutil.AvImageAlloc(&video_dst_data, &video_dst_linesize,
width, height, pix_fmt, 1)
if ret < 0 {
fmt.Printf("Could not allocate raw video buffer\n")
break
}
video_dst_bufsize = ret
} if open_codec_context(&audio_stream_idx, &audio_dec_ctx, fmt_ctx, libavutil.AVMEDIA_TYPE_AUDIO) >= 0 {
audio_stream = fmt_ctx.GetStream(uint32(audio_stream_idx))
audio_dst_file, _ = os.Create(audio_dst_filename)
if audio_dst_file == nil {
fmt.Printf("Could not open destination file %s\n", audio_dst_filename)
ret = 1
break
}
} /* dump input information to stderr */
fmt_ctx.AvDumpFormat(0, src_filename, 0) if audio_stream == nil && video_stream == nil {
fmt.Printf("Could not find audio or video stream in the input, aborting\n")
ret = 1
break
} frame = libavutil.AvFrameAlloc()
if frame == nil {
fmt.Printf("Could not allocate frame\n")
ret = -libavutil.ENOMEM
break
} pkt = libavcodec.AvPacketAlloc()
if pkt == nil {
fmt.Printf("Could not allocate packet\n")
ret = -libavutil.ENOMEM
break
} if video_stream != nil {
fmt.Printf("Demuxing video from file '%s' into '%s'\n", src_filename, video_dst_filename)
}
if audio_stream != nil {
fmt.Printf("Demuxing audio from file '%s' into '%s'\n", src_filename, audio_dst_filename)
} /* read frames from the file */
for fmt_ctx.AvReadFrame(pkt) >= 0 {
// check if the packet belongs to a stream we are interested in, otherwise
// skip it
if pkt.StreamIndex == uint32(video_stream_idx) {
ret = decode_packet(video_dec_ctx, pkt)
} else if pkt.StreamIndex == uint32(audio_stream_idx) {
ret = decode_packet(audio_dec_ctx, pkt)
}
pkt.AvPacketUnref()
if ret < 0 {
break
}
} /* flush the decoders */
if video_dec_ctx != nil {
decode_packet(video_dec_ctx, nil)
}
if audio_dec_ctx != nil {
decode_packet(audio_dec_ctx, nil)
} fmt.Printf("Demuxing succeeded.\n") if video_stream != nil {
fmt.Printf("Play the output video file with the command:\nffplay -f rawvideo -pix_fmt %s -video_size %dx%d %s\n",
libavutil.AvGetPixFmtName(pix_fmt), width, height,
video_dst_filename)
} if audio_stream != nil {
sfmt := audio_dec_ctx.SampleFmt
n_channels := audio_dec_ctx.Channels
var fmt0 string if libavutil.AvSampleFmtIsPlanar(sfmt) != 0 {
packed := libavutil.AvGetSampleFmtName(sfmt)
if packed == "" {
packed = "?"
}
fmt.Printf("Warning: the sample format the decoder produced is planar (%s). This example will output the first channel only.\n",
packed)
sfmt = libavutil.AvGetPackedSampleFmt(sfmt)
n_channels = 1
} ret = get_format_from_sample_fmt(&fmt0, sfmt)
if ret < 0 {
break
} fmt.Printf("Play the output audio file with the command:\nffplay -f %s -ac %d -ar %d %s\n",
fmt0, n_channels, audio_dec_ctx.SampleRate,
audio_dst_filename)
}
break
}
// end:
libavcodec.AvcodecFreeContext(&video_dec_ctx)
libavcodec.AvcodecFreeContext(&audio_dec_ctx)
libavformat.AvformatCloseInput(&fmt_ctx)
if video_dst_file != nil {
video_dst_file.Close()
}
if audio_dst_file != nil {
audio_dst_file.Close()
}
libavcodec.AvPacketFree(&pkt)
libavutil.AvFrameFree(&frame)
libavutil.AvFree(uintptr(unsafe.Pointer(video_dst_data[0]))) if ret < 0 {
return 1
} else {
return 0
}
} var fmt_ctx *libavformat.AVFormatContext
var video_dec_ctx, audio_dec_ctx *libavcodec.AVCodecContext
var width, height ffcommon.FInt
var pix_fmt libavutil.AVPixelFormat
var video_stream, audio_stream *libavformat.AVStream
var src_filename string
var video_dst_filename string
var audio_dst_filename string
var video_dst_file *os.File
var audio_dst_file *os.File
var video_dst_data [4]*ffcommon.FUint8T
var video_dst_linesize [4]ffcommon.FInt
var video_dst_bufsize ffcommon.FInt
var video_stream_idx, audio_stream_idx ffcommon.FInt = -1, -1
var frame *libavutil.AVFrame
var pkt *libavcodec.AVPacket
var video_frame_count ffcommon.FInt
var audio_frame_count ffcommon.FInt func output_video_frame(frame *libavutil.AVFrame) ffcommon.FInt {
if frame.Width != width || frame.Height != height ||
frame.Format != pix_fmt {
/* To handle this change, one could call av_image_alloc again and
* decode the following frames into another rawvideo file. */
fmt.Printf("Error: Width, height and pixel format have to be constant in a rawvideo file, but the width, height or pixel format of the input video changed:\nold: width = %d, height = %d, format = %s\nnew: width = %d, height = %d, format = %s\n",
width, height, libavutil.AvGetPixFmtName(pix_fmt),
frame.Width, frame.Height,
libavutil.AvGetPixFmtName(frame.Format))
return -1
} fmt.Printf("video_frame n:%d coded_n:%d\n",
video_frame_count, frame.CodedPictureNumber)
video_frame_count++ /* copy decoded frame to destination buffer:
* this is required since rawvideo expects non aligned data */
libavutil.AvImageCopy(&video_dst_data, &video_dst_linesize,
(*[4]*byte)(unsafe.Pointer(&frame.Data)), (*[4]int32)(unsafe.Pointer(&frame.Linesize)),
pix_fmt, width, height) /* write to rawvideo file */
video_dst_file.Write(ffcommon.ByteSliceFromByteP(video_dst_data[0], int(video_dst_bufsize)))
return 0
} func output_audio_frame(frame *libavutil.AVFrame) ffcommon.FInt {
unpadded_linesize := ffcommon.FSizeT(frame.NbSamples * libavutil.AvGetBytesPerSample(libavutil.AVSampleFormat(frame.Format)))
fmt.Printf("audio_frame n:%d nb_samples:%d pts:%s\n",
audio_frame_count, frame.NbSamples,
libavutil.AvTs2timestr(frame.Pts, &audio_dec_ctx.TimeBase))
audio_frame_count++
/* Write the raw audio data samples of the first plane. This works
* fine for packed formats (e.g. AV_SAMPLE_FMT_S16). However,
* most audio decoders output planar audio, which uses a separate
* plane of audio samples for each channel (e.g. AV_SAMPLE_FMT_S16P).
* In other words, this code will write only the first audio channel
* in these cases.
* You should use libswresample or libavfilter to convert the frame
* to packed data. */
audio_dst_file.Write(ffcommon.ByteSliceFromByteP(*frame.ExtendedData, int(unpadded_linesize))) return 0
} func decode_packet(dec *libavcodec.AVCodecContext, pkt *libavcodec.AVPacket) ffcommon.FInt {
ret := ffcommon.FInt(0) // submit the packet to the decoder
ret = dec.AvcodecSendPacket(pkt)
if ret < 0 {
fmt.Printf("Error submitting a packet for decoding (%s)\n", libavutil.AvErr2str(ret))
return ret
} // get all the available frames from the decoder
for ret >= 0 {
ret = dec.AvcodecReceiveFrame(frame)
if ret < 0 {
// those two return values are special and mean there is no output
// frame available, but there were no errors during decoding
if ret == libavutil.AVERROR_EOF || ret == -libavutil.EAGAIN {
return 0
} fmt.Printf("Error during decoding (%s)%d\n", libavutil.AvErr2str(ret), ret)
return ret
} // write the frame data to output file
if dec.Codec.Type == libavutil.AVMEDIA_TYPE_VIDEO {
ret = output_video_frame(frame)
} else {
ret = output_audio_frame(frame)
} frame.AvFrameUnref()
if ret < 0 {
return ret
}
} return 0
} func open_codec_context(stream_idx *ffcommon.FInt,
dec_ctx **libavcodec.AVCodecContext, fmt_ctx *libavformat.AVFormatContext, type0 libavutil.AVMediaType) ffcommon.FInt {
var ret, stream_index ffcommon.FInt
var st *libavformat.AVStream
var dec *libavcodec.AVCodec
var opts *libavutil.AVDictionary ret = fmt_ctx.AvFindBestStream(type0, -1, -1, nil, 0)
if ret < 0 {
fmt.Printf("Could not find %s stream in input file '%s'\n",
libavutil.AvGetMediaTypeString(type0), src_filename)
return ret
} else {
stream_index = ret
st = fmt_ctx.GetStream(uint32(stream_index)) /* find decoder for the stream */
dec = libavcodec.AvcodecFindDecoder(st.Codecpar.CodecId)
if dec == nil {
fmt.Printf("Failed to find %s codec\n",
libavutil.AvGetMediaTypeString(type0))
return -libavutil.EINVAL
} /* Allocate a codec context for the decoder */
*dec_ctx = dec.AvcodecAllocContext3()
if *dec_ctx == nil {
fmt.Printf("Failed to allocate the %s codec context\n",
libavutil.AvGetMediaTypeString(type0))
return -libavutil.ENOMEM
} /* Copy codec parameters from input stream to output codec context */
ret = (*dec_ctx).AvcodecParametersToContext(st.Codecpar)
if ret < 0 {
fmt.Printf("Failed to copy %s codec parameters to decoder context\n",
libavutil.AvGetMediaTypeString(type0))
return ret
} /* Init the decoders */
ret = (*dec_ctx).AvcodecOpen2(dec, &opts)
if ret < 0 {
fmt.Printf("Failed to open %s codec\n",
libavutil.AvGetMediaTypeString(type0))
return ret
}
*stream_idx = stream_index
} return 0
} func get_format_from_sample_fmt(fmt0 *string, sample_fmt libavutil.AVSampleFormat) (ret ffcommon.FInt) {
switch sample_fmt {
case libavutil.AV_SAMPLE_FMT_U8:
*fmt0 = "u8"
case libavutil.AV_SAMPLE_FMT_S16:
*fmt0 = "s16le"
case libavutil.AV_SAMPLE_FMT_S32:
*fmt0 = "s32le"
case libavutil.AV_SAMPLE_FMT_FLT:
*fmt0 = "f32le"
case libavutil.AV_SAMPLE_FMT_DBL:
*fmt0 = "f64le"
default:
fmt.Printf("sample format %s is not supported as output format\n",
libavutil.AvGetSampleFmtName(sample_fmt))
ret = -1
}
return
} 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-04:使用 Golang 和 ffmpeg-go 库实现 demuxing_decoding.c,轻松掌握音视频分离解码技巧。的更多相关文章

  1. 音视频编解码流程与如何使用 FFMPEG 命令进行音视频处理

    一.前言 FFMPEG 是特别强大的专门用于处理音视频的开源库.你既可以使用它的 API 对音视频进行处理,也可以使用它提供的工具,如 ffmpeg, ffplay, ffprobe,来编辑你的音视频 ...

  2. FFmpeg音视频编解码实践总结

    PS:由于目前开发RTSP服务器传输模块时用到了h264文件,所以攻了一段时间去实现h264的视频编解码,借用FFmpeg SDK实现了任意文件格式之间的转换,并实现了流媒体实时播放,目前音视频同步需 ...

  3. 【FFMPEG】各种音视频编解码学习详解 h264 ,mpeg4 ,aac 等所有音视频格式

    目录(?)[-] 编解码学习笔记二codec类型 编解码学习笔记三Mpeg系列Mpeg 1和Mpeg 2 编解码学习笔记四Mpeg系列Mpeg 4 编解码学习笔记五Mpeg系列AAC音频 编解码学习笔 ...

  4. 基于ffmpeg的简单音视频编解码的例子

    近日需要做一个视频转码服务器,对我这样一个在该领域的新手来说却是够我折腾一番,在别人的建议下开始研究开源ffmpeg项目,下面是在代码中看到的一 段例子代码,对我的学习非常有帮助.该例子代码包含音频的 ...

  5. Android 音视频深入 十一 FFmpeg和AudioTrack播放声音(附源码下载)

    项目地址,求starhttps://github.com/979451341/AudioVideoStudyCodeTwo/tree/master/FFmpeg%E6%92%AD%E6%94%BE%E ...

  6. FFmpeg命令行工具和批处理脚本进行简单的音视频文件编辑

    FFmpeg_Tutorial FFmpeg工具和sdk库的使用demo 一.使用FFmpeg命令行工具和批处理脚本进行简单的音视频文件编辑 1.基本介绍 对于每一个从事音视频技术开发的工程师,想必没 ...

  7. 视频编解码的理论和实践2:Ffmpeg视频编解码

    近几年,视频编解码技术在理论及应用方面都取得了重大的进展,越来越多的人想要了解编解码技术.因此,网易云信研发工程师为大家进行了归纳梳理,从理论及实践两个方面简单介绍视频编解码技术. 相关阅读推荐 &l ...

  8. vmware虚拟机下ubuntu 13.04使用zeranoe脚本交叉编译ffmpeg

    2013-07-01今天是建党节,习总书记指出,党的建设要以“照镜子.正衣冠.洗洗澡.治治病”为总要求.希望我们的党越来越纯洁,为人民谋福利.言归正传,每次项目中需要编译相应的ffmpeg,都很费时费 ...

  9. Ubuntu16.04下编译OpenCV2.4.13静态库(.a文件)

    Ubuntu16.04下编译OpenCV2.4.13静态库(.a文件) https://blog.csdn.net/woainishifu/article/details/79712110 我们在做项 ...

  10. Leetcode:面试题 04.04. 检查平衡性

    Leetcode:面试题 04.04. 检查平衡性 Leetcode:面试题 04.04. 检查平衡性 Talk is cheap . Show me the code . /** * Definit ...

随机推荐

  1. Linux下C语言程序的内存布局

    在<虚拟地址空间以及编译模式>一节中讲到,虚拟地址空间在32位环境下的大小为 4GB,在64位环境下的大小为 256TB,那么,一个C语言程序的内存在整个地址空间中是如何分布的呢?数据在哪 ...

  2. 1.java基本语法

    一.数据和数据类型 (一)标识符:给变量.常量.方法类.对象等命名的符号 (二)变量和常量 1.变量:值在运行时可以改变的量: 每个变量属于特定的数据类型,使用前要先声明,然后赋值,初始化或赋值后才能 ...

  3. STM32定时器(TIM1、TIM2、TIM3、TIM4、TIM5、TIM8)高级定时器+普通定时器,配置使用

    2.1    时钟来源 计数器时钟可以由下列时钟源提供: ·内部时钟(CK_INT) ·外部时钟模式1:外部输入脚(TIx) ·外部时钟模式2:外部触发输入(ETR) ·内部触发输入(ITRx):使用 ...

  4. React16下报错引发整个页面crash的解决方法

    如果报错没有没有被catch,将会引起整个React组件树的unmounting 解决方法:在生命周期中增加componentDidCatch https://reactjs.org/blog/201 ...

  5. MySQL根据经纬度和距离查询最近的数据

    [lat]:输入的纬度 [lon]:输入的经度 [distance]:查询距离内的数据,单位m SELECT * FROM ( SELECT id lon, lat, vin, ROUND( 6378 ...

  6. 网络基础-OSI七层模型

    什么是OSI模型 OSI模型(或 Open Systems Interconnection Model开放系统互连模型)是网络中使用的绝对基础模型.这个关键模型提供了一个框架,规定所有联网设备将如何发 ...

  7. 深入理解 Python 虚拟机:整型(int)的实现原理及源码剖析

    深入理解 Python 虚拟机:整型(int)的实现原理及源码剖析 在本篇文章当中主要给大家介绍在 cpython 内部是如何实现整型数据 int 的,主要是分析 int 类型的表示方式,分析 int ...

  8. Spring Cloud Alibaba实现服务的无损下线功能

    目录 1.背景 2.解决方案 2.1 找到通过负载均衡组件获取可用服务信息的地方 2.2 解决思路 3.部分实现代码 3.1 引入jar 3.2 编写服务下线方法 3.3 监听配置变更,清除服务缓存 ...

  9. Java(单元测试,反射)

    单元测试.反射 一.单元测试 1.1 单元测试快速入门 所谓单元测试,就是针对最小的功能单元,编写测试代码对其进行正确性测试. 我们想想,咱们之前是怎么进行测试的呢? 比如说我们写了一个学生管理系统, ...

  10. mysql5.7修改数据库密码&开通外界访问

    mysql修改数据库密码(版本5.7.33) 参考博客: 改密码:https://blog.csdn.net/m0_37482190/article/details/86635339 允许外界访问:h ...