diff --git a/.github/workflows/test.yml b/.github/workflows/test.yml index c5fd7328..e2e0ef55 100644 --- a/.github/workflows/test.yml +++ b/.github/workflows/test.yml @@ -20,7 +20,7 @@ jobs: with: 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 diff --git a/examples/client-play-backchannel/main.go b/examples/client-play-backchannel/main.go index cce92927..e511cd34 100644 --- a/examples/client-play-backchannel/main.go +++ b/examples/client-play-backchannel/main.go @@ -15,7 +15,7 @@ import ( // This example shows how to // 1. generate a dummy G711 audio stream // 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 { secs := v / d diff --git a/examples/client-play-format-av1-to-jpeg/main.go b/examples/client-play-format-av1-to-jpeg/main.go index 06efce6f..fa385763 100644 --- a/examples/client-play-format-av1-to-jpeg/main.go +++ b/examples/client-play-format-av1-to-jpeg/main.go @@ -20,7 +20,7 @@ import ( // This example shows how to // 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 // 4. convert RGBA frames to JPEG images and save them on disk diff --git a/examples/client-play-format-av1/main.go b/examples/client-play-format-av1/main.go index 86f80f4d..978a1a7d 100644 --- a/examples/client-play-format-av1/main.go +++ b/examples/client-play-format-av1/main.go @@ -15,7 +15,7 @@ import ( // This example shows how to // 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 // This example requires the FFmpeg libraries, that can be installed with this command: diff --git a/examples/client-play-format-g711/main.go b/examples/client-play-format-g711/main.go index b1100e7b..dda7f8c8 100644 --- a/examples/client-play-format-g711/main.go +++ b/examples/client-play-format-g711/main.go @@ -12,7 +12,7 @@ import ( // This example shows how to // 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 func main() { diff --git a/examples/client-play-format-h264-mpeg4audio-to-disk/main.go b/examples/client-play-format-h264-mpeg4audio-to-disk/main.go index ce69f8a0..8b5f98ed 100644 --- a/examples/client-play-format-h264-mpeg4audio-to-disk/main.go +++ b/examples/client-play-format-h264-mpeg4audio-to-disk/main.go @@ -12,7 +12,7 @@ import ( // This example shows how to // 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 func main() { diff --git a/examples/client-play-format-h264-to-disk/main.go b/examples/client-play-format-h264-to-disk/main.go index 584cb3f3..75b472d6 100644 --- a/examples/client-play-format-h264-to-disk/main.go +++ b/examples/client-play-format-h264-to-disk/main.go @@ -12,7 +12,7 @@ import ( // This example shows how to // 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 func main() { diff --git a/examples/client-play-format-h264-to-jpeg/main.go b/examples/client-play-format-h264-to-jpeg/main.go index e08c19e9..829e1fa8 100644 --- a/examples/client-play-format-h264-to-jpeg/main.go +++ b/examples/client-play-format-h264-to-jpeg/main.go @@ -20,7 +20,7 @@ import ( // This example shows how to // 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 // 4. convert RGBA frames to JPEG images and save them on disk diff --git a/examples/client-play-format-h265-to-disk/main.go b/examples/client-play-format-h265-to-disk/main.go index 12a4464c..0fa40f35 100644 --- a/examples/client-play-format-h265-to-disk/main.go +++ b/examples/client-play-format-h265-to-disk/main.go @@ -12,7 +12,7 @@ import ( // This example shows how to // 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 func main() { diff --git a/examples/client-play-format-h265-to-jpeg/main.go b/examples/client-play-format-h265-to-jpeg/main.go index 0c054c5e..30a6caf2 100644 --- a/examples/client-play-format-h265-to-jpeg/main.go +++ b/examples/client-play-format-h265-to-jpeg/main.go @@ -20,7 +20,7 @@ import ( // This example shows how to // 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 // 4. convert RGBA frames to JPEG images and save them on disk diff --git a/examples/client-play-format-mjpeg/main.go b/examples/client-play-format-mjpeg/main.go index 0e903f6a..455f74d4 100644 --- a/examples/client-play-format-mjpeg/main.go +++ b/examples/client-play-format-mjpeg/main.go @@ -14,7 +14,7 @@ import ( // This example shows how to // 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 // 4. decode JPEG images into RGBA frames diff --git a/examples/client-play-format-opus-to-disk/main.go b/examples/client-play-format-opus-to-disk/main.go index c25af675..8bdfecb3 100644 --- a/examples/client-play-format-opus-to-disk/main.go +++ b/examples/client-play-format-opus-to-disk/main.go @@ -12,7 +12,7 @@ import ( // This example shows how to // 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 func main() { diff --git a/examples/client-record-format-mpeg4audio/main.go b/examples/client-record-format-mpeg4audio/main.go index b7404060..f2d4af9a 100644 --- a/examples/client-record-format-mpeg4audio/main.go +++ b/examples/client-record-format-mpeg4audio/main.go @@ -21,7 +21,7 @@ import ( // 4. write RTP packets to the server // 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 { secs := v / d @@ -45,7 +45,7 @@ func main() { Config: &mpeg4audio.Config{ Type: mpeg4audio.ObjectTypeAACLC, SampleRate: 48000, - ChannelCount: 2, + ChannelCount: 1, }, SizeLength: 13, IndexLength: 3, diff --git a/examples/client-record-format-mpeg4audio/mp4a_encoder.go b/examples/client-record-format-mpeg4audio/mp4a_encoder.go index 48aa7fea..80c20580 100644 --- a/examples/client-record-format-mpeg4audio/mp4a_encoder.go +++ b/examples/client-record-format-mpeg4audio/mp4a_encoder.go @@ -2,13 +2,12 @@ package main import ( "fmt" - "runtime" + "math" "unsafe" ) -// #cgo pkg-config: libavcodec libavutil libswresample +// #cgo pkg-config: libavcodec libavutil // #include -// #include // #include // #include import "C" @@ -21,30 +20,19 @@ func frameLineSize(frame *C.AVFrame) *C.int { return (*C.int)(unsafe.Pointer(&frame.linesize[0])) } -func switchEndianness16(samples []byte) []byte { - 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) { +func bigEndianS16ToFloat32(samples []byte) ([]byte, error) { sampleCount := len(samples) / 2 outSize := len(samples) * 2 outSamples := make([]byte, outSize) - var p runtime.Pinner - p.Pin(&outSamples[0]) - p.Pin(&samples[0]) - defer p.Unpin() - - outBufs := (*C.uint8_t)(&outSamples[0]) - inBufs := (*C.uint8_t)(&samples[0]) - - res := C.swr_convert(swrCtx, &outBufs, (C.int)(sampleCount), &inBufs, (C.int)(sampleCount)) - if res < 0 { - return nil, fmt.Errorf("swr_convert() failed") + for i := 0; i < sampleCount; i++ { + s := (int16(samples[(i*2)]) << 8) | int16(samples[(i*2)+1]) + f := float32(s) / 32767 + b := math.Float32bits(f) + outSamples[(i * 4)] = byte(b) + outSamples[(i*4)+1] = byte(b >> 8) + outSamples[(i*4)+2] = byte(b >> 16) + outSamples[(i*4)+3] = byte(b >> 24) } return outSamples, nil @@ -58,7 +46,6 @@ type mp4aEncoder struct { codecCtx *C.AVCodecContext frame *C.AVFrame - swrCtx *C.struct_SwrContext pkt *C.AVPacket samplesBuffer []byte samplesBufferPTS int64 @@ -105,48 +92,8 @@ func (d *mp4aEncoder) initialize() error { 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() if d.pkt == nil { - C.swr_free(&d.swrCtx) C.av_frame_free(&d.frame) C.avcodec_close(d.codecCtx) return fmt.Errorf("av_packet_alloc() failed") @@ -158,18 +105,14 @@ func (d *mp4aEncoder) initialize() error { // close closes the decoder. func (d *mp4aEncoder) close() { C.av_packet_free(&d.pkt) - C.swr_free(&d.swrCtx) C.av_frame_free(&d.frame) C.avcodec_close(d.codecCtx) } // encode encodes LPCM samples into Opus packets. func (d *mp4aEncoder) encode(samples []byte) ([][]byte, int64, error) { - // convert from big-endian to little-endian - samples = switchEndianness16(samples) - - // convert from little-endian to float - samples, err := littleEndianToFloat(d.swrCtx, samples) + // convert from big-endian signed 16-bit integer to float32 + samples, err := bigEndianS16ToFloat32(samples) if err != nil { return nil, 0, err }