Golang解析MP4视频的方法与实践

Golang解析MP4视频的方法与实践 在我的应用中,我想解析MP4视频并从中获取编解码器、时长等信息。目前我使用ffprobe并解析其输出,但我想知道是否有库可以实现这一功能,这样我就不必依赖其二进制文件了。

当然,我见过一些用于解析MP4的库,但我不知道如何使用它们。特别是如何使用这些库来解析编解码器。应该使用哪个box(容器)呢?

2 回复

您可能需要(部分)规范来了解所有细节。MPEG-4 - 维基百科。如果您理解“盒子”(boxes)的概念,可以跳过第10部分,直接查看第12和第14部分。 以下是片段,以便您判断这些部分是否包含您所需的内容。

来自第12部分:

盒子类型:‘stsd’ 容器:样本表盒子(‘stbl’) 必需:是 数量:恰好一个 样本描述表提供了所用编码类型的详细信息,以及该编码所需的任何初始化信息。

来自第14部分:

盒子类型:‘mp4v’, ‘mp4a’, ‘mp4s’ 容器:样本表盒子(‘stbl’) 必需:是 数量:恰好一个 对于视频流,使用 VisualSampleEntry;对于音频流,使用 AudioSampleEntry。对于所有其他 MPEG-4 流,使用 MpegSampleEntry。提示轨道(Hint tracks)使用特定于其协议的条目格式,并具有适当的名称。

当我处理 MP4 流时,我曾试图避免购买规范,但我找不到足够的免费信息。拥有规范极大地节省了我的时间,几乎立刻就物有所值了。

更多关于Golang解析MP4视频的方法与实践的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


对于解析MP4视频,Go语言有几个优秀的库可以直接处理MP4容器格式,无需依赖外部二进制文件。以下是使用github.com/abema/go-mp4库的实践示例:

package main

import (
    "fmt"
    "io"
    "os"
    "github.com/abema/go-mp4"
)

func main() {
    file, err := os.Open("video.mp4")
    if err != nil {
        panic(err)
    }
    defer file.Close()

    // 解析MP4文件结构
    info, err := mp4.ProbeReader(file)
    if err != nil {
        panic(err)
    }

    // 获取时长(以秒为单位)
    duration := info.Duration.Seconds()
    fmt.Printf("视频时长: %.2f秒\n", duration)

    // 遍历所有track获取编解码器信息
    for i, track := range info.Tracks {
        fmt.Printf("\nTrack %d:\n", i+1)
        fmt.Printf("  类型: %s\n", track.Type)
        fmt.Printf("  编解码器: %s\n", track.Codec)
        
        // 获取具体编解码器参数
        if track.Audio != nil {
            fmt.Printf("  音频采样率: %d Hz\n", track.Audio.SampleRate)
            fmt.Printf("  音频声道数: %d\n", track.Audio.ChannelCount)
        }
        if track.Video != nil {
            fmt.Printf("  视频宽度: %d px\n", track.Video.Width)
            fmt.Printf("  视频高度: %d px\n", track.Video.Height)
            fmt.Printf("  帧率: %.2f fps\n", track.Video.FrameRate)
        }
    }
}

对于更底层的MP4 box解析,可以使用github.com/alfg/mp4库:

package main

import (
    "fmt"
    "github.com/alfg/mp4"
)

func main() {
    mp4, err := mp4.Open("video.mp4")
    if err != nil {
        panic(err)
    }
    defer mp4.Close()

    // 获取MOOV box中的关键信息
    fmt.Printf("MP4品牌: %s\n", mp4.Ftyp.Name)
    fmt.Printf("时长: %d 毫秒\n", mp4.Moov.Mvhd.Duration)

    // 遍历所有track
    for i, track := range mp4.Moov.Traks {
        fmt.Printf("\nTrack %d:\n", i+1)
        
        // 从STSD box获取编解码器信息
        stsd := track.Mdia.Minf.Stbl.Stsd
        if stsd.Avc1 != nil {
            fmt.Printf("  视频编解码器: AVC/H.264\n")
            fmt.Printf("  宽度: %d\n", stsd.Avc1.Width)
            fmt.Printf("  高度: %d\n", stsd.Avc1.Height)
        }
        if stsd.Mp4a != nil {
            fmt.Printf("  音频编解码器: MPEG-4 AAC\n")
            fmt.Printf("  声道数: %d\n", stsd.Mp4a.ChannelCount)
            fmt.Printf("  采样率: %d Hz\n", stsd.Mp4a.SampleRate)
        }
        if stsd.Hvc1 != nil {
            fmt.Printf("  视频编解码器: HEVC/H.265\n")
        }
    }
}

如果需要处理特定的MP4 box,这里是如何手动解析moovtrak box的示例:

package main

import (
    "encoding/binary"
    "fmt"
    "io"
    "os"
)

func parseMP4Boxes(r io.Reader) error {
    for {
        var size uint32
        var boxType [4]byte
        
        if err := binary.Read(r, binary.BigEndian, &size); err != nil {
            if err == io.EOF {
                break
            }
            return err
        }
        
        if _, err := io.ReadFull(r, boxType[:]); err != nil {
            return err
        }
        
        boxName := string(boxType[:])
        fmt.Printf("发现Box: %s, 大小: %d字节\n", boxName, size)
        
        // 跳过box内容
        if size > 8 {
            if _, err := io.CopyN(io.Discard, r, int64(size-8)); err != nil {
                return err
            }
        }
        
        // 处理关键box
        switch boxName {
        case "moov":
            fmt.Println("找到moov box - 包含视频元数据")
        case "trak":
            fmt.Println("找到trak box - 包含音视频轨道信息")
        case "mdhd":
            fmt.Println("找到mdhd box - 包含媒体时长信息")
        case "stsd":
            fmt.Println("找到stsd box - 包含编解码器信息")
        }
    }
    return nil
}

func main() {
    file, err := os.Open("video.mp4")
    if err != nil {
        panic(err)
    }
    defer file.Close()
    
    parseMP4Boxes(file)
}

这些库直接解析MP4文件格式,通过读取moov容器中的trakmdhd(媒体头)和stsd(样本描述)box来获取编解码器、时长等元数据信息。stsd box特别重要,它包含了具体的编解码器参数。

回到顶部