golang原生解析与解复用MPEG传输流(.ts)插件库go-astits的使用

Golang原生解析与解复用MPEG传输流(.ts)插件库go-astits的使用

这是一个用于在Go中原生解复用和复用MPEG传输流(ts)的库。

警告:该库尚未准备好用于生产环境,请自行承担使用风险!

安装

要安装库,请使用以下命令:

go get -u github.com/asticode/go-astits/...

要安装可执行文件,请使用以下命令:

go install github.com/asticode/go-astits/cmd

使用说明

传输流由数据包组成。每个数据包都有一个头部、一个可选的适配字段和一个有效载荷。多个有效载荷可以附加并解析为数据。

                                           TRANSPORT STREAM
 +--------------------------------------------------------------------------------------------------+
 |                                                                                                  |

                       PACKET                                         PACKET
 +----------------------------------------------+----------------------------------------------+----
 |                                              |                                              |

 +--------+---------------------------+---------+--------+---------------------------+---------+
 | HEADER | OPTIONAL ADAPTATION FIELD | PAYLOAD | HEADER | OPTIONAL ADAPTATION FIELD | PAYLOAD | ...
 +--------+---------------------------+---------+--------+---------------------------+---------+

                                      |         |                                    |         |
                                      +---------+                                    +---------+
                                           |                                              |
                                           +----------------------------------------------+
                                                                DATA

代码示例

解复用(Demux)

// 创建一个可取消的上下文,以便随时停止读取数据包/数据
ctx, cancel := context.WithCancel(context.Background())

// 处理SIGTERM信号
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)
go func() {
    <-ch
    cancel()
}()

// 打开文件或初始化任何类型的io.Reader
// 建议使用bufio.Reader进行缓冲以提高性能
f, _ := os.Open("/path/to/file.ts")
defer f.Close()

// 创建解复用器
dmx := astits.NewDemuxer(ctx, f)
for {
    // 获取下一个数据
    d, _ := dmx.NextData()
    
    // 如果是PMT数据
    if d.PMT != nil {
        // 遍历基本流
        for _, es := range d.PMT.ElementaryStreams {
                fmt.Printf("Stream detected: %d\n", es.ElementaryPID)
        }
        return
    }
}

复用(Mux)

// 创建一个可取消的上下文,以便随时停止写入数据包/数据
ctx, cancel := context.WithCancel(context.Background())

// 处理SIGTERM信号
ch := make(chan os.Signal, 1)
signal.Notify(ch, syscall.SIGTERM)
go func() {
    <-ch
    cancel()
}()

// 创建文件或初始化任何类型的io.Writer
// 建议使用bufio.Writer进行缓冲以提高性能
f, _ := os.Create("/path/to/file.ts")
defer f.Close()

// 创建复用器
mx := astits.NewMuxer(ctx, f)

// 添加基本流
mx.AddElementaryStream(astits.PMTElementaryStream{
    ElementaryPID: 1,
    StreamType:    astits.StreamTypeMetadata,
})

// 写入表
// 使用该函数不是强制性的,WriteData会不时重新传输表
mx.WriteTables()

// 写入数据
mx.WriteData(&astits.MuxerData{
    PES: &astits.PESData{
        Data: []byte("test"),
    },
    PID: 1,
})

选项

要为解复用器或复用器传递选项,请查找前缀为"DemuxerOpt"或"MuxerOpt"的方法,并在调用"NewDemuxer"或"NewMuxer"时添加它们:

// 这是自定义的数据包解析器
p := func(ps []*astits.Packet) (ds []*astits.Data, skip bool, err error) {
        // 这里是你的逻辑
        skip = true
        return
}

// 现在你可以创建带有适当选项的解复用器
dmx := NewDemuxer(ctx, f, DemuxerOptPacketSize(192), DemuxerOptPacketsParser(p))

CLI

该库提供了2个CLI工具,它们会在执行"go get"时自动安装在"GOPATH/bin"中。

astits-probe

列出流

$ astits-probe -i <path to your file> -f <format: text|json (default: text)>

列出数据包

$ astits-probe packets -i <path to your file>

列出数据

$ astits-probe data -i <path to your file> -d <data type: eit|nit|... (repeatable argument | if empty, all data types are shown)>

astits-es-split

将流拆分为单独的.ts文件

$ astits-es-split <path to your file> -o <path to output dir>

特性和路线图

  • [x] 添加解复用器
  • [x] 添加复用器
  • [x] 解复用PES数据包
  • [x] 复用PES数据包
  • [x] 解复用PAT数据包
  • [x] 复用PAT数据包
  • [x] 解复用PMT数据包
  • [x] 复用PMT数据包
  • [x] 解复用EIT数据包
  • [ ] 复用EIT数据包
  • [x] 解复用NIT数据包
  • [ ] 复用NIT数据包
  • [x] 解复用SDT数据包
  • [ ] 复用SDT数据包
  • [x] 解复用TOT数据包
  • [ ] 复用TOT数据包
  • [ ] 解复用BAT数据包
  • [ ] 复用BAT数据包
  • [ ] 解复用DIT数据包
  • [ ] 复用DIT数据包
  • [ ] 解复用RST数据包
  • [ ] 复用RST数据包
  • [ ] 解复用SIT数据包
  • [ ] 复用SIT数据包
  • [ ] 复用ST数据包
  • [ ] 解复用TDT数据包
  • [ ] 复用TDT数据包
  • [ ] 解复用TSDT数据包
  • [ ] 复用TSDT数据包

更多关于golang原生解析与解复用MPEG传输流(.ts)插件库go-astits的使用的实战教程也可以访问 https://www.itying.com/category-94-b0.html

1 回复

更多关于golang原生解析与解复用MPEG传输流(.ts)插件库go-astits的使用的实战系列教程也可以访问 https://www.itying.com/category-94-b0.html


使用go-astits解析与解复用MPEG传输流(.ts)

go-astits是一个用于解析和解复用MPEG传输流(.ts)的Go语言库。它允许你从.ts文件中提取基本流(PES)和数据包(PES packets),支持解析MPEG-TS头部信息、节目关联表(PAT)、节目映射表(PMT)等。

基本用法

安装

go get github.com/asticode/go-astits

示例代码

package main

import (
	"fmt"
	"os"

	"github.com/asticode/go-astits"
)

func main() {
	// 打开.ts文件
	f, err := os.Open("example.ts")
	if err != nil {
		fmt.Printf("打开文件失败: %s\n", err)
		return
	}
	defer f.Close()

	// 创建demuxer
	dmx := astits.NewDemuxer(f)

	// 解析文件
	for {
		// 读取数据
		d, err := dmx.NextData()
		if err != nil {
			fmt.Printf("读取数据失败: %s\n", err)
			break
		}

		// 处理不同类型的数据
		switch {
		case d.PAT != nil:
			// 处理节目关联表
			fmt.Printf("PAT: %+v\n", d.PAT)
			for _, program := range d.PAT.Programs {
				fmt.Printf("  Program: %d -> PID: %d\n", program.ProgramNumber, program.ProgramMapPID)
			}

		case d.PMT != nil:
			// 处理节目映射表
			fmt.Printf("PMT: %+v\n", d.PMT)
			fmt.Printf("  PCR PID: %d\n", d.PMT.PCRPID)
			for _, es := range d.PMT.ElementaryStreams {
				fmt.Printf("  Elementary Stream: Type=%d, PID=%d\n", es.StreamType, es.ElementaryPID)
			}

		case d.PES != nil:
			// 处理PES包
			fmt.Printf("PES Packet: PID=%d, StreamID=%d, DataLength=%d\n",
				d.PID, d.PES.Header.StreamID, len(d.PES.Data))

			// 这里可以根据PID和StreamType处理特定的基本流
			// 例如提取视频或音频数据

		case d.TOT != nil:
			// 处理时间偏移表
			fmt.Printf("TOT: %+v\n", d.TOT)

		default:
			// 其他类型的包
			fmt.Printf("Unknown packet type on PID %d\n", d.PID)
		}
	}
}

高级功能

1. 提取特定PID的数据

// 假设视频流的PID是256
targetPID := uint16(256)

for {
	d, err := dmx.NextData()
	if err != nil {
		break
	}
	
	if d.PID == targetPID && d.PES != nil {
		// 处理视频数据
		processVideoData(d.PES.Data)
	}
}

2. 处理特定类型的流

var videoPID uint16

// 首先找到视频流的PID
for {
	d, err := dmx.NextData()
	if err != nil {
		break
	}
	
	if d.PMT != nil {
		for _, es := range d.PMT.ElementaryStreams {
			if es.StreamType == astits.StreamTypeH264Video {
				videoPID = es.ElementaryPID
				break
			}
		}
		break
	}
}

// 然后处理视频流
if videoPID != 0 {
	for {
		d, err := dmx.NextData()
		if err != nil {
			break
		}
		
		if d.PID == videoPID && d.PES != nil {
			processH264Data(d.PES.Data)
		}
	}
}

3. 解析PES头部信息

if d.PES != nil {
	pesHeader := d.PES.Header
	fmt.Printf("PES Header Info:\n")
	fmt.Printf("  Packet Length: %d\n", pesHeader.PacketLength)
	fmt.Printf("  PTS: %v\n", pesHeader.OptionalHeader.PTS)
	fmt.Printf("  DTS: %v\n", pesHeader.OptionalHeader.DTS)
	fmt.Printf("  Data Alignment Indicator: %v\n", pesHeader.OptionalHeader.DataAlignmentIndicator)
}

注意事项

  1. 性能考虑:处理大.ts文件时,考虑使用缓冲读取器提高性能:

    f, _ := os.Open("large.ts")
    buffered := bufio.NewReaderSize(f, 1024*1024) // 1MB缓冲区
    dmx := astits.NewDemuxer(buffered)
    
  2. 错误处理:TS流可能包含错误,应妥善处理错误情况。

  3. 内存管理:处理大文件时,注意内存使用,及时处理或丢弃不需要的数据。

  4. 时间戳处理:PES包可能包含PTS(显示时间戳)和DTS(解码时间戳),用于同步音视频。

go-astits提供了基础的TS流解析功能,但对于完整的媒体处理流程,通常需要结合其他库(如解码H.264的库)一起使用。

回到顶部