277 lines
7.6 KiB
Go
277 lines
7.6 KiB
Go
package main
|
||
|
||
import (
|
||
"bytes"
|
||
"encoding/binary"
|
||
"fmt"
|
||
"io"
|
||
"log"
|
||
"os"
|
||
"reader/internal/processor"
|
||
|
||
"github.com/bluenviron/gortsplib/v4/pkg/format"
|
||
)
|
||
|
||
//port := 8080
|
||
//
|
||
//http.HandleFunc("GET /download", handlers.Download) // example request: {"date": "07-03-2025", "start_time": "16-43", "end_time": "16-44"}
|
||
//http.HandleFunc("GET /hls/", handlers.HLS)
|
||
//
|
||
//log.Println("Starting server on:")
|
||
//log.Printf("Serving on HTTP port: %d\n", port)
|
||
//
|
||
//log.Fatal(http.ListenAndServe(fmt.Sprintf(":%d", port), nil))
|
||
|
||
// Интерпретируем типы пакетов:
|
||
const (
|
||
PacketTypeH264 = 1
|
||
PacketTypeLPCM = 2
|
||
)
|
||
|
||
var (
|
||
h264 string
|
||
g711 string
|
||
)
|
||
|
||
// InterleavedPacket описывает пакет, который может быть либо H264, либо G711.
|
||
type InterleavedPacket struct {
|
||
Type byte
|
||
Pts int64
|
||
H264AUs [][]byte // для H264
|
||
LPCMSamples []byte // для G711
|
||
}
|
||
|
||
// Segment содержит строки Start и Duration, а также набор пакетов.
|
||
type Segment struct {
|
||
Start string
|
||
Duration string
|
||
Packets []InterleavedPacket
|
||
}
|
||
|
||
// readString читает строку: сначала длину (int32), затем данные строки.
|
||
func readString(r io.Reader) (string, error) {
|
||
var length int32
|
||
if err := binary.Read(r, binary.LittleEndian, &length); err != nil {
|
||
return "", err
|
||
}
|
||
buf := make([]byte, length)
|
||
if _, err := io.ReadFull(r, buf); err != nil {
|
||
return "", err
|
||
}
|
||
return string(buf), nil
|
||
}
|
||
|
||
// readHeaderSegment читает сегмент, записанный функцией WriteHeader (с Start и Duration).
|
||
func readHeaderSegment(r io.Reader) (Segment, error) {
|
||
var seg Segment
|
||
start, err := readString(r)
|
||
if err != nil {
|
||
return seg, err
|
||
}
|
||
seg.Start = start
|
||
|
||
duration, err := readString(r)
|
||
if err != nil {
|
||
return seg, err
|
||
}
|
||
seg.Duration = duration
|
||
|
||
return seg, nil
|
||
}
|
||
|
||
// readPacket читает один interleaved пакет.
|
||
func readPacket(r io.Reader) (InterleavedPacket, error) {
|
||
var pkt InterleavedPacket
|
||
|
||
// Читаем тип пакета (1 байт).
|
||
typeByte := make([]byte, 1)
|
||
if _, err := io.ReadFull(r, typeByte); err != nil {
|
||
return pkt, err
|
||
}
|
||
pkt.Type = typeByte[0]
|
||
|
||
// Читаем pts (int64).
|
||
if err := binary.Read(r, binary.LittleEndian, &pkt.Pts); err != nil {
|
||
return pkt, err
|
||
}
|
||
|
||
// В зависимости от типа, читаем данные.
|
||
if pkt.Type == PacketTypeH264 {
|
||
var numAUs int32
|
||
if err := binary.Read(r, binary.LittleEndian, &numAUs); err != nil {
|
||
return pkt, err
|
||
}
|
||
var auList [][]byte
|
||
for i := 0; i < int(numAUs); i++ {
|
||
var auLen int32
|
||
if err := binary.Read(r, binary.LittleEndian, &auLen); err != nil {
|
||
return pkt, err
|
||
}
|
||
auData := make([]byte, auLen)
|
||
if _, err := io.ReadFull(r, auData); err != nil {
|
||
return pkt, err
|
||
}
|
||
auList = append(auList, auData)
|
||
}
|
||
pkt.H264AUs = auList
|
||
} else if pkt.Type == PacketTypeLPCM {
|
||
var auLen int32
|
||
if err := binary.Read(r, binary.LittleEndian, &auLen); err != nil {
|
||
return pkt, err
|
||
}
|
||
auData := make([]byte, auLen)
|
||
if _, err := io.ReadFull(r, auData); err != nil {
|
||
return pkt, err
|
||
}
|
||
pkt.LPCMSamples = auData
|
||
} else {
|
||
return pkt, fmt.Errorf("неизвестный тип пакета: %d", pkt.Type)
|
||
}
|
||
|
||
return pkt, nil
|
||
}
|
||
|
||
// readPacketSegment читает сегмент, записанный функцией WriteInterleavedPacket:
|
||
// сначала число пакетов (int32), затем каждый пакет.
|
||
func readPacketSegment(r io.Reader) ([]InterleavedPacket, error) {
|
||
var numPackets int32
|
||
if err := binary.Read(r, binary.LittleEndian, &numPackets); err != nil {
|
||
return nil, err
|
||
}
|
||
|
||
var packets []InterleavedPacket
|
||
for i := 0; i < int(numPackets); i++ {
|
||
pkt, err := readPacket(r)
|
||
if err != nil {
|
||
return nil, err
|
||
}
|
||
packets = append(packets, pkt)
|
||
}
|
||
return packets, nil
|
||
}
|
||
|
||
func main() {
|
||
filename := "/home/psa/GoRepository/data/camera44-center-skver_temnika/12-10-00_21-03-2025.insit"
|
||
|
||
segment := Segment{}
|
||
|
||
// Открываем файл для чтения.
|
||
f, err := os.Open(filename)
|
||
if err != nil {
|
||
fmt.Println("Ошибка открытия файла:", err)
|
||
return
|
||
}
|
||
defer f.Close()
|
||
|
||
// Читаем streamID (первые 4 байта — длина, затем сами байты).
|
||
var streamIDLen int32
|
||
if err := binary.Read(f, binary.LittleEndian, &streamIDLen); err != nil {
|
||
fmt.Println("Ошибка чтения streamID длины:", err)
|
||
return
|
||
}
|
||
streamIDBytes := make([]byte, streamIDLen)
|
||
if _, err := io.ReadFull(f, streamIDBytes); err != nil {
|
||
fmt.Println("Ошибка чтения streamID:", err)
|
||
return
|
||
}
|
||
streamID := string(streamIDBytes)
|
||
fmt.Println("Stream ID:", streamID)
|
||
|
||
// Теперь в файле идут сегменты. Первый сегмент — заголовочный, далее — сегменты с пакетами.
|
||
// Читаем первый сегмент как заголовочный.
|
||
var segLen int32
|
||
if err := binary.Read(f, binary.LittleEndian, &segLen); err != nil {
|
||
fmt.Println("Ошибка чтения длины заголовочного сегмента:", err)
|
||
return
|
||
}
|
||
segData := make([]byte, segLen)
|
||
if _, err := io.ReadFull(f, segData); err != nil {
|
||
fmt.Println("Ошибка чтения заголовочного сегмента:", err)
|
||
return
|
||
}
|
||
headerReader := bytes.NewReader(segData)
|
||
headerSeg, err := readHeaderSegment(headerReader)
|
||
if err != nil {
|
||
fmt.Println("Ошибка чтения заголовочного сегмента:", err)
|
||
return
|
||
}
|
||
fmt.Println("Заголовочный сегмент:")
|
||
fmt.Println("\tStart:", headerSeg.Start)
|
||
fmt.Println("\tDuration:", headerSeg.Duration)
|
||
|
||
segment = headerSeg
|
||
|
||
// Setup MPEG-TS muxer.
|
||
var h264Format format.H264
|
||
var aacFormat *format.MPEG4Audio
|
||
|
||
h264Format.PayloadTyp = 96
|
||
h264Format.PacketizationMode = 1
|
||
|
||
currentMpegtsMuxer := processor.MpegtsMuxer{
|
||
FileName: segment.Start + "_videoFragment" + "_0" + ".ts",
|
||
H264Format: &h264Format,
|
||
Mpeg4AudioFormat: aacFormat,
|
||
}
|
||
|
||
err = currentMpegtsMuxer.Initialize()
|
||
if err != nil {
|
||
fmt.Printf("[%v-%v]: init muxer error: %w\n", h264, g711, err)
|
||
}
|
||
|
||
// Читаем последующие сегменты, содержащие пакеты.
|
||
segmentIndex := 1
|
||
for {
|
||
var segLen int32
|
||
err := binary.Read(f, binary.LittleEndian, &segLen)
|
||
if err != nil {
|
||
if err == io.EOF {
|
||
break // достигнут конец файла
|
||
}
|
||
fmt.Println("Ошибка чтения длины сегмента:", err)
|
||
return
|
||
}
|
||
segData = make([]byte, segLen)
|
||
if _, err := io.ReadFull(f, segData); err != nil {
|
||
fmt.Println("Ошибка чтения данных сегмента:", err)
|
||
return
|
||
}
|
||
packetReader := bytes.NewReader(segData)
|
||
packets, err := readPacketSegment(packetReader)
|
||
if err != nil {
|
||
fmt.Println("Ошибка чтения сегмента пакетов:", err)
|
||
return
|
||
}
|
||
|
||
segment.Packets = append(segment.Packets, packets...)
|
||
|
||
fmt.Printf("Сегмент пакетов #%d:\n", segmentIndex)
|
||
for _, pkt := range packets {
|
||
switch pkt.Type {
|
||
case PacketTypeH264:
|
||
h264 = "h264"
|
||
err = currentMpegtsMuxer.WriteH264(pkt.Pts, pkt.H264AUs)
|
||
if err != nil {
|
||
log.Printf("write h264 packet error: %v\n", err)
|
||
}
|
||
case PacketTypeLPCM:
|
||
g711 = "g711"
|
||
// Convert G711 to AAC.
|
||
au, err := processor.ConvertLPCMToAAC(pkt.LPCMSamples)
|
||
if err != nil {
|
||
log.Printf("converting to AAC frame error: %v\n", err)
|
||
}
|
||
|
||
// Encode the access unit into MPEG-TS.
|
||
err = currentMpegtsMuxer.WriteAAC([][]byte{au}, pkt.Pts)
|
||
if err != nil {
|
||
return
|
||
}
|
||
}
|
||
}
|
||
segmentIndex++
|
||
}
|
||
|
||
currentMpegtsMuxer.Close()
|
||
}
|