mirror of
https://github.com/flavioribeiro/donut.git
synced 2025-10-05 15:06:51 +08:00
moving entities and controllers to the right place
This commit is contained in:
114
h264/h264.go
114
h264/h264.go
@@ -1,114 +0,0 @@
|
|||||||
package h264
|
|
||||||
|
|
||||||
import (
|
|
||||||
"bytes"
|
|
||||||
"fmt"
|
|
||||||
)
|
|
||||||
|
|
||||||
func ParseNALUs(data []byte) (NALUs, error) {
|
|
||||||
var nalus NALUs
|
|
||||||
|
|
||||||
rawNALUs := bytes.Split(data, []byte{0x00, 0x00, 0x01})
|
|
||||||
|
|
||||||
for _, rawNALU := range rawNALUs[1:] {
|
|
||||||
nal, err := ParseNAL(rawNALU)
|
|
||||||
if err != nil {
|
|
||||||
return NALUs{}, err
|
|
||||||
|
|
||||||
}
|
|
||||||
nalus.Units = append(nalus.Units, nal)
|
|
||||||
}
|
|
||||||
|
|
||||||
return nalus, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func ParseNAL(data []byte) (NAL, error) {
|
|
||||||
index := 0
|
|
||||||
n := NAL{}
|
|
||||||
if data[index]>>7&0x01 != 0 {
|
|
||||||
return NAL{}, fmt.Errorf("forbidden_zero_bit is not 0")
|
|
||||||
}
|
|
||||||
n.RefIDC = (data[index] >> 5) & 0x03
|
|
||||||
n.UnitType = NALUnitType(data[index] & 0x1f)
|
|
||||||
numBytesInRBSP := 0
|
|
||||||
nalUnitHeaderBytes := 1
|
|
||||||
n.HeaderBytes = data[:nalUnitHeaderBytes]
|
|
||||||
|
|
||||||
index += nalUnitHeaderBytes
|
|
||||||
|
|
||||||
n.RBSPByte = make([]byte, 0, 16)
|
|
||||||
i := 0
|
|
||||||
for i = index; i < len(data); i++ {
|
|
||||||
if (i+2) < len(data) && (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x03) {
|
|
||||||
n.RBSPByte = append(n.RBSPByte, data[i], data[i+1])
|
|
||||||
i += 2
|
|
||||||
numBytesInRBSP += 2
|
|
||||||
// 0x03
|
|
||||||
} else {
|
|
||||||
n.RBSPByte = append(n.RBSPByte, data[i])
|
|
||||||
numBytesInRBSP++
|
|
||||||
}
|
|
||||||
}
|
|
||||||
index += numBytesInRBSP
|
|
||||||
|
|
||||||
n.ParseRBSP()
|
|
||||||
|
|
||||||
return n, nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NAL) ParseRBSP() error {
|
|
||||||
switch n.UnitType {
|
|
||||||
case SupplementalEnhancementInformation:
|
|
||||||
err := n.parseSEI()
|
|
||||||
if err != nil {
|
|
||||||
return err
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
||||||
|
|
||||||
func (n *NAL) parseSEI() error {
|
|
||||||
numBits := 0
|
|
||||||
byteOffset := 0
|
|
||||||
n.SEI.PayloadType = 0
|
|
||||||
n.SEI.PayloadSize = 0
|
|
||||||
nextBits := n.RBSPByte[byteOffset]
|
|
||||||
|
|
||||||
for {
|
|
||||||
if nextBits == 0xff {
|
|
||||||
n.PayloadType += 255
|
|
||||||
numBits += 8
|
|
||||||
byteOffset += numBits / 8
|
|
||||||
numBits = numBits % 8
|
|
||||||
nextBits = n.RBSPByte[byteOffset]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
n.PayloadType += int(nextBits)
|
|
||||||
numBits += 8
|
|
||||||
byteOffset += numBits / 8
|
|
||||||
numBits = numBits % 8
|
|
||||||
nextBits = n.RBSPByte[byteOffset]
|
|
||||||
|
|
||||||
// read size
|
|
||||||
for {
|
|
||||||
if nextBits == 0xff {
|
|
||||||
n.PayloadSize += 255
|
|
||||||
numBits += 8
|
|
||||||
byteOffset += numBits / 8
|
|
||||||
numBits = numBits % 8
|
|
||||||
nextBits = n.RBSPByte[byteOffset]
|
|
||||||
continue
|
|
||||||
}
|
|
||||||
break
|
|
||||||
}
|
|
||||||
|
|
||||||
n.PayloadSize += int(nextBits)
|
|
||||||
numBits += 8
|
|
||||||
byteOffset += numBits / 8
|
|
||||||
|
|
||||||
return nil
|
|
||||||
}
|
|
@@ -1,11 +1,10 @@
|
|||||||
package eia608
|
package controllers
|
||||||
|
|
||||||
import (
|
import (
|
||||||
"encoding/json"
|
"encoding/json"
|
||||||
|
|
||||||
"github.com/flavioribeiro/donut/h264"
|
|
||||||
|
|
||||||
"github.com/asticode/go-astits"
|
"github.com/asticode/go-astits"
|
||||||
|
"github.com/flavioribeiro/donut/internal/entities"
|
||||||
gocaption "github.com/szatmary/gocaption"
|
gocaption "github.com/szatmary/gocaption"
|
||||||
)
|
)
|
||||||
|
|
||||||
@@ -13,25 +12,19 @@ type EIA608Reader struct {
|
|||||||
frame gocaption.EIA608Frame
|
frame gocaption.EIA608Frame
|
||||||
}
|
}
|
||||||
|
|
||||||
type Cue struct {
|
|
||||||
Type string
|
|
||||||
StartTime int64
|
|
||||||
Text string
|
|
||||||
}
|
|
||||||
|
|
||||||
func NewEIA608Reader() (r *EIA608Reader) {
|
func NewEIA608Reader() (r *EIA608Reader) {
|
||||||
return &EIA608Reader{}
|
return &EIA608Reader{}
|
||||||
}
|
}
|
||||||
|
|
||||||
func (r *EIA608Reader) Parse(PES *astits.PESData) (string, error) {
|
func (r *EIA608Reader) Parse(PES *astits.PESData) (string, error) {
|
||||||
nalus, err := h264.ParseNALUs(PES.Data)
|
nalus, err := ParseNALUs(PES.Data)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
return "", err
|
return "", err
|
||||||
}
|
}
|
||||||
for _, nal := range nalus.Units {
|
for _, nal := range nalus.Units {
|
||||||
// ANSI/SCTE 128-1 2020
|
// ANSI/SCTE 128-1 2020
|
||||||
// Note that SEI payload is a SEI payloadType of 4 which contains the itu_t_t35_payload_byte for the terminal provider
|
// Note that SEI payload is a SEI payloadType of 4 which contains the itu_t_t35_payload_byte for the terminal provider
|
||||||
if nal.UnitType == h264.SupplementalEnhancementInformation && nal.SEI.PayloadType == 4 {
|
if nal.UnitType == entities.SupplementalEnhancementInformation && nal.SEI.PayloadType == 4 {
|
||||||
// ANSI/SCTE 128-1 2020
|
// ANSI/SCTE 128-1 2020
|
||||||
// Caption, AFD and bar data shall be carried in the SEI raw byte sequence payload (RBSP)
|
// Caption, AFD and bar data shall be carried in the SEI raw byte sequence payload (RBSP)
|
||||||
// syntax of the video Elementary Stream.
|
// syntax of the video Elementary Stream.
|
||||||
@@ -55,7 +48,7 @@ func (r *EIA608Reader) Parse(PES *astits.PESData) (string, error) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func BuildCaptionsMessage(pts *astits.ClockReference, captions string) (string, error) {
|
func BuildCaptionsMessage(pts *astits.ClockReference, captions string) (string, error) {
|
||||||
cue := Cue{
|
cue := entities.Cue{
|
||||||
StartTime: pts.Base,
|
StartTime: pts.Base,
|
||||||
Text: captions,
|
Text: captions,
|
||||||
Type: "captions",
|
Type: "captions",
|
59
internal/controllers/h264_controller.go
Normal file
59
internal/controllers/h264_controller.go
Normal file
@@ -0,0 +1,59 @@
|
|||||||
|
package controllers
|
||||||
|
|
||||||
|
import (
|
||||||
|
"bytes"
|
||||||
|
"fmt"
|
||||||
|
|
||||||
|
"github.com/flavioribeiro/donut/internal/entities"
|
||||||
|
)
|
||||||
|
|
||||||
|
func ParseNALUs(data []byte) (entities.NALUs, error) {
|
||||||
|
var nalus entities.NALUs
|
||||||
|
|
||||||
|
rawNALUs := bytes.Split(data, []byte{0x00, 0x00, 0x01})
|
||||||
|
|
||||||
|
for _, rawNALU := range rawNALUs[1:] {
|
||||||
|
nal, err := ParseNAL(rawNALU)
|
||||||
|
if err != nil {
|
||||||
|
return entities.NALUs{}, err
|
||||||
|
|
||||||
|
}
|
||||||
|
nalus.Units = append(nalus.Units, nal)
|
||||||
|
}
|
||||||
|
|
||||||
|
return nalus, nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func ParseNAL(data []byte) (entities.NAL, error) {
|
||||||
|
index := 0
|
||||||
|
n := entities.NAL{}
|
||||||
|
if data[index]>>7&0x01 != 0 {
|
||||||
|
return entities.NAL{}, fmt.Errorf("forbidden_zero_bit is not 0")
|
||||||
|
}
|
||||||
|
n.RefIDC = (data[index] >> 5) & 0x03
|
||||||
|
n.UnitType = entities.NALUnitType(data[index] & 0x1f)
|
||||||
|
numBytesInRBSP := 0
|
||||||
|
nalUnitHeaderBytes := 1
|
||||||
|
n.HeaderBytes = data[:nalUnitHeaderBytes]
|
||||||
|
|
||||||
|
index += nalUnitHeaderBytes
|
||||||
|
|
||||||
|
n.RBSPByte = make([]byte, 0, 16)
|
||||||
|
i := 0
|
||||||
|
for i = index; i < len(data); i++ {
|
||||||
|
if (i+2) < len(data) && (data[i] == 0x00 && data[i+1] == 0x00 && data[i+2] == 0x03) {
|
||||||
|
n.RBSPByte = append(n.RBSPByte, data[i], data[i+1])
|
||||||
|
i += 2
|
||||||
|
numBytesInRBSP += 2
|
||||||
|
// 0x03
|
||||||
|
} else {
|
||||||
|
n.RBSPByte = append(n.RBSPByte, data[i])
|
||||||
|
numBytesInRBSP++
|
||||||
|
}
|
||||||
|
}
|
||||||
|
index += numBytesInRBSP
|
||||||
|
|
||||||
|
n.ParseRBSP()
|
||||||
|
|
||||||
|
return n, nil
|
||||||
|
}
|
@@ -8,7 +8,6 @@ import (
|
|||||||
|
|
||||||
astisrt "github.com/asticode/go-astisrt/pkg"
|
astisrt "github.com/asticode/go-astisrt/pkg"
|
||||||
"github.com/asticode/go-astits"
|
"github.com/asticode/go-astits"
|
||||||
"github.com/flavioribeiro/donut/eia608"
|
|
||||||
"github.com/flavioribeiro/donut/internal/entities"
|
"github.com/flavioribeiro/donut/internal/entities"
|
||||||
"github.com/pion/webrtc/v3"
|
"github.com/pion/webrtc/v3"
|
||||||
"github.com/pion/webrtc/v3/pkg/media"
|
"github.com/pion/webrtc/v3/pkg/media"
|
||||||
@@ -43,7 +42,7 @@ func (c *StreamingController) Stream(sp entities.StreamParameters) {
|
|||||||
|
|
||||||
// reading from reader pipe into mpeg-ts demuxer
|
// reading from reader pipe into mpeg-ts demuxer
|
||||||
mpegTSDemuxer := astits.NewDemuxer(sp.Ctx, r)
|
mpegTSDemuxer := astits.NewDemuxer(sp.Ctx, r)
|
||||||
eia608Reader := eia608.NewEIA608Reader()
|
eia608Reader := NewEIA608Reader()
|
||||||
h264PID := uint16(0)
|
h264PID := uint16(0)
|
||||||
|
|
||||||
for {
|
for {
|
||||||
@@ -83,7 +82,7 @@ func (c *StreamingController) Stream(sp entities.StreamParameters) {
|
|||||||
return
|
return
|
||||||
}
|
}
|
||||||
if captions != "" {
|
if captions != "" {
|
||||||
captionsMsg, err := eia608.BuildCaptionsMessage(mpegTSDemuxData.PES.Header.OptionalHeader.PTS, captions)
|
captionsMsg, err := BuildCaptionsMessage(mpegTSDemuxData.PES.Header.OptionalHeader.PTS, captions)
|
||||||
if err != nil {
|
if err != nil {
|
||||||
c.l.Sugar().Errorw("failed to build captions message",
|
c.l.Sugar().Errorw("failed to build captions message",
|
||||||
"error", err,
|
"error", err,
|
||||||
|
@@ -12,8 +12,6 @@ const (
|
|||||||
MetadataChannelID string = "metadata"
|
MetadataChannelID string = "metadata"
|
||||||
)
|
)
|
||||||
|
|
||||||
type Media struct{}
|
|
||||||
|
|
||||||
type RequestParams struct {
|
type RequestParams struct {
|
||||||
SRTHost string
|
SRTHost string
|
||||||
SRTPort uint16 `json:",string"`
|
SRTPort uint16 `json:",string"`
|
||||||
@@ -69,6 +67,12 @@ type Track struct {
|
|||||||
Type TrackType
|
Type TrackType
|
||||||
}
|
}
|
||||||
|
|
||||||
|
type Cue struct {
|
||||||
|
Type string
|
||||||
|
StartTime int64
|
||||||
|
Text string
|
||||||
|
}
|
||||||
|
|
||||||
type StreamParameters struct {
|
type StreamParameters struct {
|
||||||
WebRTCConn *webrtc.PeerConnection
|
WebRTCConn *webrtc.PeerConnection
|
||||||
Cancel context.CancelFunc
|
Cancel context.CancelFunc
|
||||||
|
@@ -1,4 +1,4 @@
|
|||||||
package h264
|
package entities
|
||||||
|
|
||||||
type NALUs struct {
|
type NALUs struct {
|
||||||
Units []NAL
|
Units []NAL
|
||||||
@@ -56,3 +56,60 @@ const (
|
|||||||
Unspecified31 = NALUnitType(31) // Unspecified
|
Unspecified31 = NALUnitType(31) // Unspecified
|
||||||
|
|
||||||
)
|
)
|
||||||
|
|
||||||
|
func (n *NAL) ParseRBSP() error {
|
||||||
|
switch n.UnitType {
|
||||||
|
case SupplementalEnhancementInformation:
|
||||||
|
err := n.parseSEI()
|
||||||
|
if err != nil {
|
||||||
|
return err
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func (n *NAL) parseSEI() error {
|
||||||
|
numBits := 0
|
||||||
|
byteOffset := 0
|
||||||
|
n.SEI.PayloadType = 0
|
||||||
|
n.SEI.PayloadSize = 0
|
||||||
|
nextBits := n.RBSPByte[byteOffset]
|
||||||
|
|
||||||
|
for {
|
||||||
|
if nextBits == 0xff {
|
||||||
|
n.PayloadType += 255
|
||||||
|
numBits += 8
|
||||||
|
byteOffset += numBits / 8
|
||||||
|
numBits = numBits % 8
|
||||||
|
nextBits = n.RBSPByte[byteOffset]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
n.PayloadType += int(nextBits)
|
||||||
|
numBits += 8
|
||||||
|
byteOffset += numBits / 8
|
||||||
|
numBits = numBits % 8
|
||||||
|
nextBits = n.RBSPByte[byteOffset]
|
||||||
|
|
||||||
|
// read size
|
||||||
|
for {
|
||||||
|
if nextBits == 0xff {
|
||||||
|
n.PayloadSize += 255
|
||||||
|
numBits += 8
|
||||||
|
byteOffset += numBits / 8
|
||||||
|
numBits = numBits % 8
|
||||||
|
nextBits = n.RBSPByte[byteOffset]
|
||||||
|
continue
|
||||||
|
}
|
||||||
|
break
|
||||||
|
}
|
||||||
|
|
||||||
|
n.PayloadSize += int(nextBits)
|
||||||
|
numBits += 8
|
||||||
|
byteOffset += numBits / 8
|
||||||
|
|
||||||
|
return nil
|
||||||
|
}
|
Reference in New Issue
Block a user