simplify client-record-format-mpeg4audio (#709)

This commit is contained in:
Alessandro Ros
2025-02-22 18:27:22 +01:00
committed by GitHub
parent 90cac184c9
commit 17ee46e238
14 changed files with 27 additions and 84 deletions

View File

@@ -20,7 +20,7 @@ jobs:
with: with:
go-version: ${{ matrix.go }} go-version: ${{ matrix.go }}
- run: sudo apt update && sudo apt install -y libavcodec-dev libswscale-dev libswresample-dev - run: sudo apt update && sudo apt install -y libavcodec-dev libswscale-dev
- run: make test-nodocker - run: make test-nodocker

View File

@@ -15,7 +15,7 @@ import (
// This example shows how to // This example shows how to
// 1. generate a dummy G711 audio stream // 1. generate a dummy G711 audio stream
// 2. connect to a RTSP server, find a back channel that supports G711 // 2. connect to a RTSP server, find a back channel that supports G711
// 3. route the packets from GStreamer to the channel // 3. route the G711 stream to the channel
func multiplyAndDivide(v, m, d int64) int64 { func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d secs := v / d

View File

@@ -20,7 +20,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a AV1 format // 2. check if there's a AV1 stream
// 3. decode the AV1 stream into RGBA frames // 3. decode the AV1 stream into RGBA frames
// 4. convert RGBA frames to JPEG images and save them on disk // 4. convert RGBA frames to JPEG images and save them on disk

View File

@@ -15,7 +15,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a AV1 format // 2. check if there's a AV1 stream
// 3. decode the AV1 stream into RGBA frames // 3. decode the AV1 stream into RGBA frames
// This example requires the FFmpeg libraries, that can be installed with this command: // This example requires the FFmpeg libraries, that can be installed with this command:

View File

@@ -12,7 +12,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a G711 format // 2. check if there's a G711 stream
// 3. decode the G711 stream into audio samples // 3. decode the G711 stream into audio samples
func main() { func main() {

View File

@@ -12,7 +12,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a H264 format and an MPEG-4 audio format // 2. check if there's a H264 stream and an MPEG-4 audio format
// 3. save the content of those formats in a file in MPEG-TS format // 3. save the content of those formats in a file in MPEG-TS format
func main() { func main() {

View File

@@ -12,7 +12,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a H264 format // 2. check if there's a H264 stream
// 3. save the content of the format in a file in MPEG-TS format // 3. save the content of the format in a file in MPEG-TS format
func main() { func main() {

View File

@@ -20,7 +20,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a H264 format // 2. check if there's a H264 stream
// 3. decode the H264 stream into RGBA frames // 3. decode the H264 stream into RGBA frames
// 4. convert RGBA frames to JPEG images and save them on disk // 4. convert RGBA frames to JPEG images and save them on disk

View File

@@ -12,7 +12,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a H265 format // 2. check if there's a H265 stream
// 3. save the content of the format in a file in MPEG-TS format // 3. save the content of the format in a file in MPEG-TS format
func main() { func main() {

View File

@@ -20,7 +20,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a H265 format // 2. check if there's a H265 stream
// 3. decode the H265 stream into RGBA frames // 3. decode the H265 stream into RGBA frames
// 4. convert RGBA frames to JPEG images and save them on disk // 4. convert RGBA frames to JPEG images and save them on disk

View File

@@ -14,7 +14,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a M-JPEG format // 2. check if there's a M-JPEG stream
// 3. get JPEG images of that format // 3. get JPEG images of that format
// 4. decode JPEG images into RGBA frames // 4. decode JPEG images into RGBA frames

View File

@@ -12,7 +12,7 @@ import (
// This example shows how to // This example shows how to
// 1. connect to a RTSP server // 1. connect to a RTSP server
// 2. check if there's a Opus format // 2. check if there's a Opus stream
// 3. save the content of the format in a file in MPEG-TS format // 3. save the content of the format in a file in MPEG-TS format
func main() { func main() {

View File

@@ -21,7 +21,7 @@ import (
// 4. write RTP packets to the server // 4. write RTP packets to the server
// This example requires the FFmpeg libraries, that can be installed with this command: // This example requires the FFmpeg libraries, that can be installed with this command:
// apt install -y libavcodec-dev libswresample-dev gcc pkg-config // apt install -y libavcodec-dev gcc pkg-config
func multiplyAndDivide(v, m, d int64) int64 { func multiplyAndDivide(v, m, d int64) int64 {
secs := v / d secs := v / d
@@ -45,7 +45,7 @@ func main() {
Config: &mpeg4audio.Config{ Config: &mpeg4audio.Config{
Type: mpeg4audio.ObjectTypeAACLC, Type: mpeg4audio.ObjectTypeAACLC,
SampleRate: 48000, SampleRate: 48000,
ChannelCount: 2, ChannelCount: 1,
}, },
SizeLength: 13, SizeLength: 13,
IndexLength: 3, IndexLength: 3,

View File

@@ -2,13 +2,12 @@ package main
import ( import (
"fmt" "fmt"
"runtime" "math"
"unsafe" "unsafe"
) )
// #cgo pkg-config: libavcodec libavutil libswresample // #cgo pkg-config: libavcodec libavutil
// #include <libavcodec/avcodec.h> // #include <libavcodec/avcodec.h>
// #include <libswresample/swresample.h>
// #include <libavutil/opt.h> // #include <libavutil/opt.h>
// #include <libavutil/channel_layout.h> // #include <libavutil/channel_layout.h>
import "C" import "C"
@@ -21,30 +20,19 @@ func frameLineSize(frame *C.AVFrame) *C.int {
return (*C.int)(unsafe.Pointer(&frame.linesize[0])) return (*C.int)(unsafe.Pointer(&frame.linesize[0]))
} }
func switchEndianness16(samples []byte) []byte { func bigEndianS16ToFloat32(samples []byte) ([]byte, error) {
ls := len(samples)
for i := 0; i < ls; i += 2 {
samples[i], samples[i+1] = samples[i+1], samples[i]
}
return samples
}
func littleEndianToFloat(swrCtx *C.struct_SwrContext, samples []byte) ([]byte, error) {
sampleCount := len(samples) / 2 sampleCount := len(samples) / 2
outSize := len(samples) * 2 outSize := len(samples) * 2
outSamples := make([]byte, outSize) outSamples := make([]byte, outSize)
var p runtime.Pinner for i := 0; i < sampleCount; i++ {
p.Pin(&outSamples[0]) s := (int16(samples[(i*2)]) << 8) | int16(samples[(i*2)+1])
p.Pin(&samples[0]) f := float32(s) / 32767
defer p.Unpin() b := math.Float32bits(f)
outSamples[(i * 4)] = byte(b)
outBufs := (*C.uint8_t)(&outSamples[0]) outSamples[(i*4)+1] = byte(b >> 8)
inBufs := (*C.uint8_t)(&samples[0]) outSamples[(i*4)+2] = byte(b >> 16)
outSamples[(i*4)+3] = byte(b >> 24)
res := C.swr_convert(swrCtx, &outBufs, (C.int)(sampleCount), &inBufs, (C.int)(sampleCount))
if res < 0 {
return nil, fmt.Errorf("swr_convert() failed")
} }
return outSamples, nil return outSamples, nil
@@ -58,7 +46,6 @@ type mp4aEncoder struct {
codecCtx *C.AVCodecContext codecCtx *C.AVCodecContext
frame *C.AVFrame frame *C.AVFrame
swrCtx *C.struct_SwrContext
pkt *C.AVPacket pkt *C.AVPacket
samplesBuffer []byte samplesBuffer []byte
samplesBufferPTS int64 samplesBufferPTS int64
@@ -105,48 +92,8 @@ func (d *mp4aEncoder) initialize() error {
return fmt.Errorf("av_frame_get_buffer() failed") return fmt.Errorf("av_frame_get_buffer() failed")
} }
d.swrCtx = C.swr_alloc()
if d.swrCtx == nil {
C.av_frame_free(&d.frame)
C.avcodec_close(d.codecCtx)
return fmt.Errorf("swr_alloc() failed")
}
cstr := C.CString("out_channel_layout")
defer C.free(unsafe.Pointer(cstr))
C.av_opt_set_channel_layout(unsafe.Pointer(d.swrCtx), cstr, (C.int64_t)(d.codecCtx.channel_layout), 0)
cstr = C.CString("out_sample_fmt")
defer C.free(unsafe.Pointer(cstr))
C.av_opt_set_int(unsafe.Pointer(d.swrCtx), cstr, C.AV_SAMPLE_FMT_FLTP, 0)
cstr = C.CString("out_sample_rate")
defer C.free(unsafe.Pointer(cstr))
C.av_opt_set_int(unsafe.Pointer(d.swrCtx), cstr, 48000, 0)
cstr = C.CString("in_channel_layout")
defer C.free(unsafe.Pointer(cstr))
C.av_opt_set_channel_layout(unsafe.Pointer(d.swrCtx), cstr, (C.int64_t)(d.codecCtx.channel_layout), 0)
cstr = C.CString("in_sample_fmt")
defer C.free(unsafe.Pointer(cstr))
C.av_opt_set_int(unsafe.Pointer(d.swrCtx), cstr, C.AV_SAMPLE_FMT_S16, 0)
cstr = C.CString("in_sample_rate")
defer C.free(unsafe.Pointer(cstr))
C.av_opt_set_int(unsafe.Pointer(d.swrCtx), cstr, 48000, 0)
res = C.swr_init(d.swrCtx)
if res < 0 {
C.swr_free(&d.swrCtx)
C.av_frame_free(&d.frame)
C.avcodec_close(d.codecCtx)
return fmt.Errorf("swr_init() failed")
}
d.pkt = C.av_packet_alloc() d.pkt = C.av_packet_alloc()
if d.pkt == nil { if d.pkt == nil {
C.swr_free(&d.swrCtx)
C.av_frame_free(&d.frame) C.av_frame_free(&d.frame)
C.avcodec_close(d.codecCtx) C.avcodec_close(d.codecCtx)
return fmt.Errorf("av_packet_alloc() failed") return fmt.Errorf("av_packet_alloc() failed")
@@ -158,18 +105,14 @@ func (d *mp4aEncoder) initialize() error {
// close closes the decoder. // close closes the decoder.
func (d *mp4aEncoder) close() { func (d *mp4aEncoder) close() {
C.av_packet_free(&d.pkt) C.av_packet_free(&d.pkt)
C.swr_free(&d.swrCtx)
C.av_frame_free(&d.frame) C.av_frame_free(&d.frame)
C.avcodec_close(d.codecCtx) C.avcodec_close(d.codecCtx)
} }
// encode encodes LPCM samples into Opus packets. // encode encodes LPCM samples into Opus packets.
func (d *mp4aEncoder) encode(samples []byte) ([][]byte, int64, error) { func (d *mp4aEncoder) encode(samples []byte) ([][]byte, int64, error) {
// convert from big-endian to little-endian // convert from big-endian signed 16-bit integer to float32
samples = switchEndianness16(samples) samples, err := bigEndianS16ToFloat32(samples)
// convert from little-endian to float
samples, err := littleEndianToFloat(d.swrCtx, samples)
if err != nil { if err != nil {
return nil, 0, err return nil, 0, err
} }