127 lines
3.2 KiB
Go
127 lines
3.2 KiB
Go
package storage
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"fmt"
|
||
"io"
|
||
)
|
||
|
||
// Constants for detection video and audio types.
|
||
const (
|
||
PacketTypeAV1 = 1
|
||
PacketTypeH264 = 2
|
||
PacketTypeH265 = 3
|
||
PacketTypeMJPEG = 4
|
||
PacketTypeVP8 = 5
|
||
PacketTypeVP9 = 6
|
||
|
||
PacketTypeG711 = 21
|
||
PacketTypeAAC = 22
|
||
PacketTypeLPCM = 23
|
||
PacketTypeOPUS = 24
|
||
)
|
||
|
||
// InterleavedPacket is the structure of each inner packet.
|
||
type InterleavedPacket struct {
|
||
Type byte
|
||
Pts int64
|
||
|
||
H264AUs [][]byte
|
||
LPCMSamples []byte
|
||
}
|
||
|
||
// Segment is overall structure according to each period.
|
||
type Segment struct {
|
||
Date string
|
||
Duration string
|
||
Packet InterleavedPacket
|
||
Packets []InterleavedPacket
|
||
}
|
||
|
||
// writeString записывает строку: сначала int32 длина, затем данные.
|
||
func writeString(w io.Writer, s string) error {
|
||
if err := binary.Write(w, binary.LittleEndian, int32(len(s))); err != nil {
|
||
return err
|
||
}
|
||
_, err := w.Write([]byte(s))
|
||
return err
|
||
}
|
||
|
||
// WritePacket записывает один interleaved пакет.
|
||
func WritePacket(w io.Writer, pkt InterleavedPacket) error {
|
||
// Записываем тип пакета (1 байт)
|
||
if err := binary.Write(w, binary.LittleEndian, pkt.Type); err != nil {
|
||
return err
|
||
}
|
||
// Записываем pts (int64)
|
||
if err := binary.Write(w, binary.LittleEndian, pkt.Pts); err != nil {
|
||
return err
|
||
}
|
||
|
||
if pkt.Type == PacketTypeH264 {
|
||
// Для H264 – AU как [][]byte.
|
||
numAUs := int32(len(pkt.H264AUs))
|
||
if err := binary.Write(w, binary.LittleEndian, numAUs); err != nil {
|
||
return err
|
||
}
|
||
for _, au := range pkt.H264AUs {
|
||
if err := binary.Write(w, binary.LittleEndian, int32(len(au))); err != nil {
|
||
return err
|
||
}
|
||
if _, err := w.Write(au); err != nil {
|
||
return err
|
||
}
|
||
}
|
||
} else if pkt.Type == PacketTypeLPCM {
|
||
// Для LPCM – просто длина и данные.
|
||
if err := binary.Write(w, binary.LittleEndian, int32(len(pkt.LPCMSamples))); err != nil {
|
||
return err
|
||
}
|
||
if _, err := w.Write(pkt.LPCMSamples); err != nil {
|
||
return err
|
||
}
|
||
} else {
|
||
return fmt.Errorf("неизвестный тип пакета: %d", pkt.Type)
|
||
}
|
||
return nil
|
||
}
|
||
|
||
// WriteHeader записывает заголовок сегмента (Start и Duration).
|
||
func WriteHeader(w io.Writer, seg Segment) error {
|
||
var buf bytes.Buffer
|
||
if err := writeString(&buf, seg.Date); err != nil {
|
||
return err
|
||
}
|
||
if err := writeString(&buf, seg.Duration); err != nil {
|
||
return err
|
||
}
|
||
segData := buf.Bytes()
|
||
if err := binary.Write(w, binary.LittleEndian, int32(len(segData))); err != nil {
|
||
return err
|
||
}
|
||
_, err := w.Write(segData)
|
||
return err
|
||
}
|
||
|
||
// WriteInterleavedPacket записывает сегмент с пакетами.
|
||
// Теперь количество пакетов определяется динамически.
|
||
func WriteInterleavedPacket(w io.Writer, seg Segment) error {
|
||
var buf bytes.Buffer
|
||
|
||
if err := binary.Write(&buf, binary.LittleEndian, int32(1)); err != nil {
|
||
return err
|
||
}
|
||
// Записываем каждый пакет.
|
||
if err := WritePacket(&buf, seg.Packet); err != nil {
|
||
return err
|
||
}
|
||
|
||
segData := buf.Bytes()
|
||
if err := binary.Write(w, binary.LittleEndian, int32(len(segData))); err != nil {
|
||
return err
|
||
}
|
||
_, err := w.Write(segData)
|
||
return err
|
||
}
|