From ef27776a485c702094e8209f2c268835b99fdc30 Mon Sep 17 00:00:00 2001 From: f4exb Date: Mon, 18 Feb 2019 02:07:30 +0100 Subject: [PATCH] Opus basic encoder --- cmake/Modules/FindOpus.cmake | 52 +++++++++++++++++ sdrbase/CMakeLists.txt | 6 ++ sdrbase/audio/audioopus.cpp | 87 ++++++++++++++++++++++++++++ sdrbase/audio/audioopus.h | 43 ++++++++++++++ swagger/sdrangel/examples/rx_test.py | 2 +- 5 files changed, 189 insertions(+), 1 deletion(-) create mode 100644 cmake/Modules/FindOpus.cmake create mode 100644 sdrbase/audio/audioopus.cpp create mode 100644 sdrbase/audio/audioopus.h diff --git a/cmake/Modules/FindOpus.cmake b/cmake/Modules/FindOpus.cmake new file mode 100644 index 000000000..2ec4281dc --- /dev/null +++ b/cmake/Modules/FindOpus.cmake @@ -0,0 +1,52 @@ +############################################################################ +# FindOpus.txt +# Copyright (C) 2014 Belledonne Communications, Grenoble France +# +############################################################################ +# +# This program is free software; you can redistribute it and/or +# modify it under the terms of the GNU General Public License +# as published by the Free Software Foundation; either version 2 +# of the License, or (at your option) any later version. +# +# This program is distributed in the hope that it will be useful, +# but WITHOUT ANY WARRANTY; without even the implied warranty of +# MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the +# GNU General Public License for more details. +# +# You should have received a copy of the GNU General Public License +# along with this program; if not, write to the Free Software +# Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA. +# +############################################################################ +# +# - Find the opus include file and library +# +# OPUS_FOUND - system has opus +# OPUS_INCLUDE_DIRS - the opus include directory +# OPUS_LIBRARIES - The libraries needed to use opus + +find_path(OPUS_INCLUDE_DIRS + NAMES opus/opus.h + PATH_SUFFIXES include +) +if(OPUS_INCLUDE_DIRS) + set(HAVE_OPUS_OPUS_H 1) +endif() + +find_library(OPUS_LIBRARIES NAMES opus) + +if(OPUS_LIBRARIES) + find_library(LIBM NAMES m) + if(LIBM) + list(APPEND OPUS_LIBRARIES ${LIBM}) + endif() +endif() + +include(FindPackageHandleStandardArgs) +find_package_handle_standard_args(Opus + DEFAULT_MSG + OPUS_INCLUDE_DIRS OPUS_LIBRARIES HAVE_OPUS_OPUS_H +) + +mark_as_advanced(OPUS_INCLUDE_DIRS OPUS_LIBRARIES HAVE_OPUS_OPUS_H) \ No newline at end of file diff --git a/sdrbase/CMakeLists.txt b/sdrbase/CMakeLists.txt index 5f9b4d307..8d4d2bbb4 100644 --- a/sdrbase/CMakeLists.txt +++ b/sdrbase/CMakeLists.txt @@ -9,6 +9,7 @@ set(sdrbase_SOURCES audio/audiofifo.cpp audio/audiofilter.cpp audio/audiog722.cpp + audio/audioopus.cpp audio/audiooutput.cpp audio/audioinput.cpp audio/audionetsink.cpp @@ -104,6 +105,7 @@ set(sdrbase_HEADERS audio/audiofilter.h audio/audiog722.h audio/audiooutput.h + audio/audioopus.h audio/audioinput.h audio/audionetsink.h @@ -224,6 +226,8 @@ set(sdrbase_SOURCES ${sdrbase_HEADERS} ) +find_package(Opus REQUIRED) + if(FFTW3F_FOUND) set(sdrbase_SOURCES ${sdrbase_SOURCES} @@ -297,6 +301,7 @@ include_directories( ${CMAKE_SOURCE_DIR}/qrtplib ${CMAKE_SOURCE_DIR}/swagger/sdrangel/code/qt5/client ${Boost_INCLUDE_DIRS} + ${OPUS_INCLUDE_DIRS} ) target_link_libraries(sdrbase @@ -304,6 +309,7 @@ target_link_libraries(sdrbase httpserver qrtplib swagger + ${OPUS_LIBRARIES} ) if(FFTW3F_FOUND) diff --git a/sdrbase/audio/audioopus.cpp b/sdrbase/audio/audioopus.cpp new file mode 100644 index 000000000..2bccf1a55 --- /dev/null +++ b/sdrbase/audio/audioopus.cpp @@ -0,0 +1,87 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#include "opus/opus.h" +#include + +#include "audioopus.h" + +AudioOpus::AudioOpus() : + m_encoderState(0), + m_encoderOK(false) +{ + qDebug("AudioOpus::AudioOpus: libopus version %s", opus_get_version_string()); +} + +AudioOpus::~AudioOpus() +{ + if (m_encoderState) { + opus_encoder_destroy(m_encoderState); + } +} + +void AudioOpus::setEncoder(int32_t fs, int nChannels) +{ + int error; + bool newInstance = true; + + if (m_encoderState) + { + error = opus_encoder_init(m_encoderState, fs, nChannels, OPUS_APPLICATION_AUDIO); + newInstance = false; + } + else + { + m_encoderState = opus_encoder_create(fs, nChannels, OPUS_APPLICATION_AUDIO, &error); + } + + if (error != OPUS_OK) + { + qWarning("AudioOpus::setEncoder: %s error: %s", newInstance ? "create" : "init", opus_strerror(error)); + m_encoderOK = false; + return; + } + else + { + qDebug("AudioOpus::setEncoder: fs: %d, nChannels: %d", fs, nChannels); + m_encoderOK = true; + } + + error = opus_encoder_ctl(m_encoderState, OPUS_SET_BITRATE(m_bitrate)); + + if (error != OPUS_OK) + { + qWarning("AudioOpus::setEncoder: set bitrate error: %s", opus_strerror(error)); + m_encoderOK = false; + return; + } +} + +int AudioOpus::encode(int frameSize, int16_t *in, uint8_t *out) +{ + int nbBytes = opus_encode(m_encoderState, in, frameSize, out, m_maxPacketSize); + + if (nbBytes < 0) + { + qWarning("AudioOpus::encode failed: %s\n", opus_strerror(nbBytes)); + return 0; + } + else + { + return nbBytes; + } +} diff --git a/sdrbase/audio/audioopus.h b/sdrbase/audio/audioopus.h new file mode 100644 index 000000000..14fdee121 --- /dev/null +++ b/sdrbase/audio/audioopus.h @@ -0,0 +1,43 @@ +/////////////////////////////////////////////////////////////////////////////////// +// Copyright (C) 2019 F4EXB // +// written by Edouard Griffiths // +// // +// This program is free software; you can redistribute it and/or modify // +// it under the terms of the GNU General Public License as published by // +// the Free Software Foundation as version 3 of the License, or // +// // +// This program is distributed in the hope that it will be useful, // +// but WITHOUT ANY WARRANTY; without even the implied warranty of // +// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the // +// GNU General Public License V3 for more details. // +// // +// You should have received a copy of the GNU General Public License // +// along with this program. If not, see . // +/////////////////////////////////////////////////////////////////////////////////// + +#ifndef SDRBASE_AUDIO_AUDIOOPUS_H_ +#define SDRBASE_AUDIO_AUDIOOPUS_H_ + +#include +#include "export.h" + +class OpusEncoder; + +class SDRBASE_API AudioOpus +{ +public: + AudioOpus(); + ~AudioOpus(); + + void setEncoder(int32_t fs, int nChannels); + int encode(int frameSize, int16_t *in, uint8_t *out); + + static const int m_bitrate = 64000; //!< Fixed 64kb/s bitrate (8kB/s) + static const int m_maxPacketSize = 3*1276; + +private: + OpusEncoder *m_encoderState; + bool m_encoderOK; +}; + +#endif /* SDRBASE_AUDIO_AUDIOOPUS_H_ */ diff --git a/swagger/sdrangel/examples/rx_test.py b/swagger/sdrangel/examples/rx_test.py index 58cb2fd7d..b3dad9f4c 100755 --- a/swagger/sdrangel/examples/rx_test.py +++ b/swagger/sdrangel/examples/rx_test.py @@ -46,7 +46,7 @@ def getInputOptions(): parser.add_option("--audio-address", dest="audio_address", help="Audio: UDP destination address", metavar="IP_ADDRESS", type="string") parser.add_option("--audio-port", dest="audio_port", help="Audio: UDP destination port", metavar="IP_PORT", type="int") parser.add_option("--audio-channels", dest="audio_channels", help="Audio: UDP mode (0: L only 1: R only 2: L+R mono 3: LR stereo)", metavar="ENUM_INT", type="int") - parser.add_option("--audio-codec", dest="audio_codec", help="Audio: codec to use for UDP (0: L16, 1: L8, 2: PCMA, 3: PCMU)", metavar="ENUM_INT", type="int") + parser.add_option("--audio-codec", dest="audio_codec", help="Audio: codec to use for UDP (0: L16, 1: L8, 2: PCMA, 3: PCMU, 4: G722)", metavar="ENUM_INT", type="int") parser.add_option("--audio-decim", dest="audio_decim", help="Audio. decimation to apply for UDP (1 to 6)", metavar="INT", type="int") parser.add_option("--baud-rate", dest="baud_rate", help="DSD: baud rate in Baud", metavar="BAUD", type="int", default=4800) parser.add_option("--fm-dev", dest="fm_deviation", help="DSD: expected FM deviation", metavar="FREQ", type="int", default=5400)