百度360必应搜狗淘宝本站头条
当前位置:网站首页 > 技术教程 > 正文

2023-03-21:音视频解混合(demuxer)为MP3和H264...

suiw9 2025-03-28 21:10 8 浏览 0 评论

2023-03-21:音视频解混合(demuxer)为MP3和H264,用go语言编写。


答案2023-03-21:


# 步骤1:安装
github.com/moonfdd/ffmpeg-go

go get -u github.com/moonfdd/ffmpeg-go

# 步骤2:导入所需的库


接下来,我们需要导入所需的库。这些库包括fmt、os、exec以及FFmpeg库中的libavcodec、libavdevice、libavformat和libavutil。在本教程中,我们还将使用moonfdd/ffmpeg-go库,该库提供了一些便捷的函数和类型定义,可帮助我们更轻松地使用FFmpeg库。



import (
"fmt"
"os"
"os/exec"


"github.com/moonfdd/ffmpeg-go/ffcommon"
"github.com/moonfdd/ffmpeg-go/libavcodec"
"github.com/moonfdd/ffmpeg-go/libavdevice"
"github.com/moonfdd/ffmpeg-go/libavformat"
"github.com/moonfdd/ffmpeg-go/libavutil"
)

# 步骤3:设置FFmpeg库路径


在使用FFmpeg库之前,我们需要设置FFmpeg库的路径。您可以通过设置环境变量来实现这一点,也可以直接调用FFmpeg库的SetXxxPath函数进行设置。

// 设置环境变量
os.Setenv("Path", os.Getenv("Path")+";./lib")


// 设置FFmpeg库路径
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")

# 步骤4:定义必要的变量


在使用FFmpeg库之前,我们需要定义一些必要的变量。这些变量包括输入文件名、输出音频文件名、输出视频文件名、输入格式上下文、输出音频格式上下文、输出视频格式上下文、AVPacket等。在本教程中,我们还定义了用于保存视频索引和音频索引的变量。

var ifmtCtx, ofmtCtxAudio, ofmtCtxVideo *libavformat.AVFormatContext
var packet libavcodec.AVPacket
var videoIndex ffcommon.FInt = -1
var audioIndex ffcommon.FInt = -1
var ret ffcommon.FInt = 0
inFileName := "./resources/big_buck_bunny.mp4"
outFilenameAudio := "./out/a22.aac"
outFilenameVideo := "./out/a22.h264"

# 步骤5:注册设备


在使用FFmpeg库之前,我们需要先注册设备。您可以使用
libavdevice.AvdeviceRegisterAll()函数来注册所有支持的设备。

libavdevice.AvdeviceRegisterAll()

# 步骤6:打开输入流


在从音视频文件中分离出音频和视频之前,我们需要打开音视频文件的输入流。您可以使用
libavformat.AvformatOpenInput函数来打开输入流,并使用ifmtCtx参数保存输入流的上下文。

if libavformat.AvformatOpenInput(&ifmtCtx, inFileName, nil, nil) < 0 {
fmt.Printf("Could not open input file '%s'\n", inFileName)
return
}
defer ifmtCtx.AvformatCloseInput()

# 步骤7:读取媒体信息


打开输入流后,我们需要读取音视频文件的媒体信息。您可以使用
libavformat.AvformatFindStreamInfo函数来读取媒体信息,并使用libavutil.AvDumpFormat函数将媒体信息输出到控制台。

if ifmtCtx.AvformatFindStreamInfo(nil) < 0 {
fmt.Println("Could not find stream information")
return
}
libavutil.AvDumpFormat(ifmtCtx, 0, inFileName, 0)

# 步骤8:查找音频和视频流


在读取媒体信息后,我们需要查找音频和视频流。您可以使用
libavformat.AvformatFindStreamInfo函数来查找音频和视频流,并使用videoIndex和audioIndex变量保存视频流和音频流的索引。

for i := 0; i < int(ifmtCtx.NbStreams()); i++ {
codecParams := ifmtCtx.Streams()[i].CodecParameters()
codecType := codecParams.AvCodecGetType()


switch codecType {
case libavutil.AVMEDIA_TYPE_VIDEO:
    if videoIndex == -1 {
        videoIndex = ffcommon.FInt(i)
    }
case libavutil.AVMEDIA_TYPE_AUDIO:
    if audioIndex == -1 {
        audioIndex = ffcommon.FInt(i)
    }
}
}
if videoIndex == -1 || audioIndex == -1 {
    fmt.Println("Could not find video or audio stream")
    return
}

# 步骤9:打开输出流


在查找音频和视频流后,我们需要打开输出流,以便将分离出的音频和视频写入文件。您可以使用
libavformat.AvformatAllocOutputContext2函数创建输出格式上下文,并使用ofmtCtxAudio和ofmtCtxVideo变量保存输出格式上下文。

// 打开输出音频流
if ofmtCtxAudio = libavformat.AvformatAllocOutputContext2(nil, nil, "", outFilenameAudio); ofmtCtxAudio == nil {
    fmt.Printf("could not create output context for '%s'\n", outFilenameAudio)
    return
}


// 打开输出视频流
if ofmtCtxVideo = libavformat.AvformatAllocOutputContext2(nil, nil, "h264", outFilenameVideo); ofmtCtxVideo == nil {
    fmt.Printf("could not create output context for '%s'\n", outFilenameVideo)
    return
}

# 步骤10:写入文件头


打开输出流后,我们需要写入文件头。您可以使用
libavformat.AvformatWriteHeader函数来写入文件头。

// 写入音频文件头
if (ofmtCtxAudio.Oformat().Flags() & libavformat.AVFMT_NOFILE) == 0 {
    if ret = ofmtCtxAudio.AvioOpen(nil, libavformat.AVIO_FLAG_WRITE); ret < 0 {
        fmt.Printf("could not open output file '%s'\n", outFilenameAudio)
        return
    }
    defer ofmtCtxAudio.AvioClose()
}
if ret = ofmtCtxAudio.AvformatWriteHeader(nil); ret < 0 {
    fmt.Println("Could not write output file header")
    return
}


// 写入视频文件头
if (ofmtCtxVideo.Oformat().Flags() & libavformat.AVFMT_NOFILE) == 0 {
    if ret = ofmtCtxVideo.AvioOpen(nil, libavformat.AVIO_FLAG_WRITE); ret < 0 {
        fmt.Printf("could not open output file '%s'\n", outFilenameVideo)
        return
    }
    defer ofmtCtxVideo.AvioClose()
}
if ret = ofmtCtxVideo.AvformatWriteHeader(nil); ret < 0 {
    fmt.Println("Could not write output file header")
    return
}

# 步骤11:分离音频和视频


写入文件头后,我们可以开始分离音频和视频了。您可以使用libavformat.AvReadFrame函数读取音视频帧,并根据音频或视频流的索引将音频帧写入音频文件,将视频帧写入视频文件。

for {
if ret = ifmtCtx.AvReadFrame(&packet); ret <0 {
break
}
defer packet.AvPacketUnref()


if packet.StreamIndex() == audioIndex {
    // 写入音频流
    if ret = ofmtCtxAudio.AvInterleavedWriteFrame(&packet); ret < 0 {
        fmt.Printf("error while writing audio frame: %v\n", ret)
        return
    }
} else if packet.StreamIndex() == videoIndex {
    // 写入视频流
    if ret = ofmtCtxVideo.AvInterleavedWriteFrame(&packet); ret < 0 {
        fmt.Printf("error while writing video frame: %v\n", ret)
        return
    }
}
}

# 步骤12:写入文件尾


完成音视频分离后,我们需要写入文件尾。您可以使用
libavformat.AvWriteTrailer函数来写入文件尾。

if ret = ofmtCtxAudio.AvWriteTrailer(); ret < 0 {
    fmt.Println("Could not write output file trailer")
    return
}


// 写入视频文件尾
if ret = ofmtCtxVideo.AvWriteTrailer(); ret < 0 {
    fmt.Println("Could not write output file trailer")
    return
}

# 完整代码

// https://feater.top/ffmpeg/ffmpeg-demuxer-video-to-mp3-and-h264
package main


import (
  "fmt"
  "os"
  "os/exec"


  "github.com/moonfdd/ffmpeg-go/ffcommon"
  "github.com/moonfdd/ffmpeg-go/libavcodec"
  "github.com/moonfdd/ffmpeg-go/libavdevice"
  "github.com/moonfdd/ffmpeg-go/libavformat"
  "github.com/moonfdd/ffmpeg-go/libavutil"
)


func open_codec_context(streamIndex *ffcommon.FInt, ofmtCtx **libavformat.AVFormatContext, ifmtCtx *libavformat.AVFormatContext, type0 libavutil.AVMediaType) ffcommon.FInt {
  var outStream, inStream *libavformat.AVStream
  // int ret = -1, index = -1;
  var ret ffcommon.FInt = -1
  var index ffcommon.FInt = -1


  index = ifmtCtx.AvFindBestStream(type0, -1, -1, nil, 0)
  if index < 0 {
    fmt.Printf("can't find %s stream in input file\n", libavutil.AvGetMediaTypeString(type0))
    return ret
  }


  inStream = ifmtCtx.GetStream(uint32(index))


  outStream = (*ofmtCtx).AvformatNewStream(nil)
  if outStream == nil {
    fmt.Printf("failed to allocate output stream\n")
    return ret
  }


  ret = libavcodec.AvcodecParametersCopy(outStream.Codecpar, inStream.Codecpar)
  if ret < 0 {
    fmt.Printf("failed to copy codec parametes\n")
    return ret
  }


  outStream.Codecpar.CodecTag = 0


  *streamIndex = index


  return 0
}


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
    }
  }


  inFileName := "./resources/big_buck_bunny.mp4"
  outFilenameAudio := "./out/a22.aac"
  outFilenameVideo := "./out/a22.h264"


  var ifmtCtx, ofmtCtxAudio, ofmtCtxVideo *libavformat.AVFormatContext
  var packet libavcodec.AVPacket


  var videoIndex ffcommon.FInt = -1
  var audioIndex ffcommon.FInt = -1
  var ret ffcommon.FInt = 0


  //注册设备
  libavdevice.AvdeviceRegisterAll()


  for {
    //打开输入流
    if libavformat.AvformatOpenInput(&ifmtCtx, inFileName, nil, nil) < 0 {
      fmt.Printf("Cannot open input file.\n")
      break
    }


    //获取流信息
    if ifmtCtx.AvformatFindStreamInfo(nil) < 0 {
      fmt.Printf("Cannot find stream info in input file.\n")
      break
    }


    //创建输出上下文:视频
    libavformat.AvformatAllocOutputContext2(&ofmtCtxVideo, nil, "", outFilenameVideo)
    if ofmtCtxVideo == nil {
      fmt.Printf("can't create video output context")
      break
    }


    //创建输出上下文:音频
    libavformat.AvformatAllocOutputContext2(&ofmtCtxAudio, nil, "", outFilenameAudio)
    if ofmtCtxAudio == nil {
      fmt.Printf("can't create audio output context")
      break
    }


    ret = open_codec_context(&videoIndex, &ofmtCtxVideo, ifmtCtx, libavutil.AVMEDIA_TYPE_VIDEO)
    if ret < 0 {
      fmt.Printf("can't decode video context\n")
      break
    }


    ret = open_codec_context(&audioIndex, &ofmtCtxAudio, ifmtCtx, libavutil.AVMEDIA_TYPE_AUDIO)
    if ret < 0 {
      fmt.Printf("can't decode video context\n")
      break
    }


    //Dump Format------------------
    fmt.Printf("\n==============Input Video=============\n")
    ifmtCtx.AvDumpFormat(0, inFileName, 0)
    fmt.Printf("\n==============Output Video============\n")
    ofmtCtxVideo.AvDumpFormat(0, outFilenameVideo, 1)
    fmt.Printf("\n==============Output Audio============\n")
    ofmtCtxAudio.AvDumpFormat(0, outFilenameAudio, 1)
    fmt.Printf("\n======================================\n")


    //打开输出文件:视频
    if ofmtCtxVideo.Oformat.Flags&libavformat.AVFMT_NOFILE == 0 {
      if libavformat.AvioOpen(&ofmtCtxVideo.Pb, outFilenameVideo, libavformat.AVIO_FLAG_WRITE) < 0 {
        fmt.Printf("can't open output file: %s\n", outFilenameVideo)
        break
      }
    }


    //打开输出文件:音频
    if ofmtCtxAudio.Oformat.Flags&libavformat.AVFMT_NOFILE == 0 {
      if libavformat.AvioOpen(&ofmtCtxAudio.Pb, outFilenameAudio, libavformat.AVIO_FLAG_WRITE) < 0 {
        fmt.Printf("can't open output file: %s\n", outFilenameVideo)
        break
      }
    }


    //写文件头
    if ofmtCtxVideo.AvformatWriteHeader(nil) < 0 {
      fmt.Printf("Error occurred when opening video output file\n")
      break
    }


    if ofmtCtxAudio.AvformatWriteHeader(nil) < 0 {
      fmt.Printf("Error occurred when opening audio output file\n")
      break
    }


    for {
      var ofmtCtx *libavformat.AVFormatContext
      var inStream, outStream *libavformat.AVStream


      if ifmtCtx.AvReadFrame(&packet) < 0 {
        break
      }


      inStream = ifmtCtx.GetStream(packet.StreamIndex)


      if packet.StreamIndex == uint32(videoIndex) {
        outStream = ofmtCtxVideo.GetStream(0)
        ofmtCtx = ofmtCtxVideo
      } else if packet.StreamIndex == uint32(audioIndex) {
        outStream = ofmtCtxAudio.GetStream(0)
        ofmtCtx = ofmtCtxAudio
      } else {
        continue
      }


      //convert PTS/DTS
      packet.Pts = libavutil.AvRescaleQRnd(packet.Pts, inStream.TimeBase, outStream.TimeBase,
        libavutil.AV_ROUND_NEAR_INF|libavutil.AV_ROUND_PASS_MINMAX)
      packet.Dts = libavutil.AvRescaleQRnd(packet.Dts, inStream.TimeBase, outStream.TimeBase,
        libavutil.AV_ROUND_NEAR_INF|libavutil.AV_ROUND_PASS_MINMAX)
      packet.Duration = libavutil.AvRescaleQ(packet.Duration, inStream.TimeBase, outStream.TimeBase)
      packet.Pos = -1
      packet.StreamIndex = 0


      //write
      if ofmtCtx.AvInterleavedWriteFrame(&packet) < 0 fmt.printferror muxing packet\n break packet.avpacketunref write file trailer ofmtctxvideo.avwritetrailer ofmtctxaudio.avwritetrailer break libavformat.avformatcloseinputifmtctx if ofmtctxvideo ofmtctxvideo.oformat.flagslibavformat.avfmt_nofile='= 0' ofmtctxvideo.pb.avioclose if ofmtctxaudio ofmtctxaudio.oformat.flagslibavformat.avfmt_nofile='= 0' ofmtctxaudio.pb.avioclose ofmtctxvideo.avformatfreecontext ofmtctxaudio.avformatfreecontext fmt.println----------------------------------------- go func _ err='exec.Command("./lib/ffplay.exe",' outfilenameaudio.output if err fmt.printlnplay err=', err)     }   }()   _, err = exec.Command(' .libffplay.exe outfilenamevideo.output if err fmt.printlnplay err=', err)   } }  

执行命令:

go run ./examples/a22.video_demuxer_mp42h264mp3/main.go


相关推荐

看完这一篇数据仓库干货,终于搞懂什么是hive了

一、Hive定义Hive最早来源于FaceBook,因为FaceBook网站每天产生海量的结构化日志数据,为了对这些数据进行管理,并且因为机器学习的需求,产生了Hive这们技术,并继续发展成为一个成...

真正让你明白Hive参数调优系列1:控制map个数与性能调优参数

本系列几章系统地介绍了开发中Hive常见的用户配置属性(有时称为参数,变量或选项),并说明了哪些版本引入了哪些属性,常见有哪些属性的使用,哪些属性可以进行Hive调优,以及如何使用的问题。以及日常Hi...

HIVE SQL基础语法(hive sql是什么)

引言与关系型数据库的SQL略有不同,但支持了绝大多数的语句如DDL、DML以及常见的聚合函数、连接查询、条件查询。HIVE不适合用于联机事务处理,也不提供实时查询功能。它最适合应用在基于大量不可变数据...

[干货]Hive与Spark sql整合并测试效率

在目前的大数据架构中hive是用来做离线数据分析的,而在Spark1.4版本中spark加入了sparksql,我们知道spark的优势是速度快,那么到底sparksql会比hive...

Hive 常用的函数(hive 数学函数)

一、Hive函数概述及分类标准概述Hive内建了不少函数,用于满足用户不同使用需求,提高SQL编写效率:...

数仓/数开面试题真题总结(二)(数仓面试时应该讲些什么)

二.Hive...

Tomcat处理HTTP请求流程解析(tomcat 处理请求过程)

1、一个简单的HTTP服务器在Web应用中,浏览器请求一个URL,服务器就把生成的HTML网页发送给浏览器,而浏览器和服务器之间的传输协议是HTTP,那么接下来我们看下如何用Java来实现一个简单...

Python 高级编程之网络编程 Socket(六)

一、概述Python网络编程是指使用Python语言编写的网络应用程序。这种编程涉及到网络通信、套接字编程、协议解析等多种方面的知识。...

[904]ScalersTalk成长会Python小组第20周学习笔记

Scalers点评:在2015年,ScalersTalk成长会Python小组完成了《Python核心编程》第1轮的学习。到2016年,我们开始第二轮的学习,并且将重点放在章节的习题上。Python小...

「web开发」几款http请求测试工具

curl命令CURL(CommandLineUniformResourceLocator),是一个利用URL语法,在命令行终端下使用的网络请求工具,支持HTTP、HTTPS、FTP等协议...

x-cmd pkg | hurl - 强力的 HTTP 请求测试工具,让 API 测试更加简洁高效

简介...

Mac 基于HTTP方式访问下载共享文件,配置共享服务器

方法一:使用Python的SimpleHTTPServer进行局域网文件共享Mac自带Python,所以不需要安装其他软件,一条命令即可...

Python 基础教程十五之 Python 使用requests库发送http请求

前言...

使用curl进行http高并发访问(php curl 大量并发获得结果)

本文主要介绍curl异步接口的使用方式,以及获取高性能的一些思路和实践。同时假设读者已经熟悉并且使用过同步接口。1.curl接口基本介绍curl一共有三种接口:EasyInterface...

Django 中的 HttpResponse理解和用法-基础篇1

思路是方向,代码是时间,知识需积累,经验需摸索。希望对大家有用,有错误还望指出。...

取消回复欢迎 发表评论: