mirror of
https://github.com/boykopovar/AnyPS5.git
synced 2026-11-03 12:57:27 +00:00
Merge pull request #13736 from 2Swagger/feat/libwebrtc-opus-encoder
feat(libwebrtc): implement the WebRtcOpus encoder functions
This commit is contained in:
7 files changed
+519
-97
No files matched your search
@@ -4,6 +4,8 @@ configure_windows_unwind(libwebrtc)
|
||||
|
||||
target_sources(libwebrtc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/ExpFilter.cpp)
|
||||
target_sources(libwebrtc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/RtcpCommonHeader.cpp)
|
||||
target_sources(libwebrtc PRIVATE ${CMAKE_CURRENT_SOURCE_DIR}/src/Opus.cpp)
|
||||
target_link_libraries(libwebrtc PRIVATE opus)
|
||||
|
||||
if(BUILD_TESTING)
|
||||
add_test_executable(guest_webrtc_fast_int_to_buffer_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/FastIntToBuffer.cpp)
|
||||
@@ -36,4 +38,14 @@ if(BUILD_TESTING)
|
||||
set_tests_properties(guest_webrtc_rtcp_common_header PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libwebrtc>")
|
||||
endif()
|
||||
add_test_executable(guest_webrtc_opus_tests ${CMAKE_CURRENT_SOURCE_DIR}/tests/Opus.cpp)
|
||||
target_include_directories(guest_webrtc_opus_tests PRIVATE ${LIBS_INCLUDE_DIR})
|
||||
target_link_libraries(guest_webrtc_opus_tests PRIVATE libwebrtc libc opus)
|
||||
configure_windows_unwind(guest_webrtc_opus_tests)
|
||||
add_test(NAME guest_webrtc_opus COMMAND guest_webrtc_opus_tests)
|
||||
set_tests_properties(guest_webrtc_opus PROPERTIES TIMEOUT 20)
|
||||
if(WIN32)
|
||||
set_tests_properties(guest_webrtc_opus PROPERTIES ENVIRONMENT_MODIFICATION
|
||||
"PATH=path_list_prepend:$<TARGET_FILE_DIR:libwebrtc>")
|
||||
endif()
|
||||
endif()
|
||||
@@ -1991,86 +1991,6 @@ int APS5_VABI libwebrtcUnknown56(void) {
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_EncoderFree_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_SetBitRate_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_EnableFec_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_DisableFec_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_SetMaxPlaybackRate_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_SetComplexity_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_EnableDtx_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_DisableDtx_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_EnableCbr_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_DisableCbr_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_Encode_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_EncoderCreate_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_SetPacketLossRate_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_GetBandwidth_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_SetBandwidth_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_SetForceChannels_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI _ZN6webrtc20AudioDecoderOpusImplC1ERKNS_15FieldTrialsViewEmi_nid_postfix(void) {
|
||||
NotImplemented_nid_no_patch(__func__);
|
||||
return 0;
|
||||
|
||||
@@ -3,6 +3,7 @@
|
||||
#include "SceTypes.hpp"
|
||||
#include "prx/libwebrtc/include/ExpFilter.hpp"
|
||||
#include "prx/libwebrtc/include/RtcpCommonHeader.hpp"
|
||||
#include "prx/libwebrtc/include/Opus.hpp"
|
||||
|
||||
extern "C" {
|
||||
|
||||
@@ -392,22 +393,22 @@ int APS5_VABI _ZN6webrtc29BuiltinAudioProcessingBuilder5BuildERKNS_11Environment
|
||||
int APS5_VABI _ZN6webrtc32CreateBuiltinAudioDecoderFactoryEv_nid_postfix(void);
|
||||
int APS5_VABI libwebrtcUnknown55(void);
|
||||
int APS5_VABI libwebrtcUnknown56(void);
|
||||
int APS5_VABI WebRtcOpus_EncoderFree_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_SetBitRate_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_EnableFec_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_DisableFec_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_SetMaxPlaybackRate_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_SetComplexity_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_EnableDtx_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_DisableDtx_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_EnableCbr_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_DisableCbr_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_Encode_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_EncoderCreate_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_SetPacketLossRate_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_GetBandwidth_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_SetBandwidth_nid_postfix(void);
|
||||
int APS5_VABI WebRtcOpus_SetForceChannels_nid_postfix(void);
|
||||
std::int16_t APS5_VABI WebRtcOpus_EncoderFree_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetBitRate_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t rate);
|
||||
std::int16_t APS5_VABI WebRtcOpus_EnableFec_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_DisableFec_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetMaxPlaybackRate_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t frequencyHz);
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetComplexity_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t complexity);
|
||||
std::int16_t APS5_VABI WebRtcOpus_EnableDtx_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_DisableDtx_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_EnableCbr_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_DisableCbr_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
int APS5_VABI WebRtcOpus_Encode_nid_postfix(WebRtcOpusEncInst* inst, const std::int16_t* audioIn, std::size_t samples, std::size_t lengthEncodedBuffer, std::uint8_t* encoded);
|
||||
std::int16_t APS5_VABI WebRtcOpus_EncoderCreate_nid_postfix(WebRtcOpusEncInst** inst, std::size_t channels, std::int32_t application, int sampleRateHz);
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetPacketLossRate_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t lossRate);
|
||||
std::int32_t APS5_VABI WebRtcOpus_GetBandwidth_nid_postfix(WebRtcOpusEncInst* inst);
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetBandwidth_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t bandwidth);
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetForceChannels_nid_postfix(WebRtcOpusEncInst* inst, std::size_t numChannels);
|
||||
int APS5_VABI _ZN6webrtc20AudioDecoderOpusImplC1ERKNS_15FieldTrialsViewEmi_nid_postfix(void);
|
||||
int APS5_VABI _ZN6webrtc20AudioDecoderOpusImplD1Ev_nid_postfix(void);
|
||||
int APS5_VABI _ZN6webrtc23AudioNetworkAdaptorImpl6ConfigC1Ev_nid_postfix(void);
|
||||
|
||||
@@ -0,0 +1,3 @@
|
||||
#pragma once
|
||||
|
||||
struct WebRtcOpusEncInst;
|
||||
@@ -0,0 +1,220 @@
|
||||
/*
|
||||
* Copyright (c) 2012 The WebRTC project authors. All Rights Reserved.
|
||||
*
|
||||
* Use of this source code is governed by a BSD-style license
|
||||
* that can be found in the LICENSE file in the root of the source
|
||||
* tree. An additional intellectual property rights grant can be found
|
||||
* in the file PATENTS. All contributing project authors may
|
||||
* be found in the AUTHORS file in the root of the source tree.
|
||||
*/
|
||||
|
||||
/*
|
||||
* Copyright (c) 2011, The WebRTC project authors. All rights reserved.
|
||||
*
|
||||
* Redistribution and use in source and binary forms, with or without
|
||||
* modification, are permitted provided that the following conditions are
|
||||
* met:
|
||||
*
|
||||
* * Redistributions of source code must retain the above copyright
|
||||
* notice, this list of conditions and the following disclaimer.
|
||||
*
|
||||
* * Redistributions in binary form must reproduce the above copyright
|
||||
* notice, this list of conditions and the following disclaimer in
|
||||
* the documentation and/or other materials provided with the
|
||||
* distribution.
|
||||
*
|
||||
* * Neither the name of Google nor the names of its contributors may
|
||||
* be used to endorse or promote products derived from this software
|
||||
* without specific prior written permission.
|
||||
*
|
||||
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS
|
||||
* "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT
|
||||
* LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR
|
||||
* A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT
|
||||
* HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL,
|
||||
* SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT
|
||||
* LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE,
|
||||
* DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY
|
||||
* THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT
|
||||
* (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE
|
||||
* OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
|
||||
*/
|
||||
|
||||
#include "prx/libwebrtc/libwebrtc.hpp"
|
||||
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <opus.h>
|
||||
#include <stdexcept>
|
||||
|
||||
struct WebRtcOpusEncInst {
|
||||
OpusEncoder* encoder;
|
||||
std::size_t channels;
|
||||
int inDtxMode;
|
||||
int sampleRateHz;
|
||||
};
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr std::size_t SamplesPerMillisecondAt48Khz = 48;
|
||||
constexpr std::size_t MaxFrameMillisecondsWithout120Ms = 60;
|
||||
constexpr std::size_t MaxFrameMillisecondsWith120Ms = 120;
|
||||
|
||||
}
|
||||
|
||||
extern "C" {
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_EncoderFree_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
if (inst->encoder)
|
||||
opus_encoder_destroy(inst->encoder);
|
||||
delete inst;
|
||||
return 0;
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_EncoderCreate_nid_postfix(WebRtcOpusEncInst** inst, std::size_t channels, std::int32_t application, int sampleRateHz) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
int opusApplication = 0;
|
||||
switch (application) {
|
||||
case 0:
|
||||
opusApplication = OPUS_APPLICATION_VOIP;
|
||||
break;
|
||||
case 1:
|
||||
opusApplication = OPUS_APPLICATION_AUDIO;
|
||||
break;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
auto* state = new WebRtcOpusEncInst {};
|
||||
int error = OPUS_OK;
|
||||
state->encoder = opus_encoder_create(sampleRateHz, static_cast<int>(channels), opusApplication, &error);
|
||||
if (error != OPUS_OK || !state->encoder) {
|
||||
WebRtcOpus_EncoderFree_nid_postfix(state);
|
||||
return -1;
|
||||
}
|
||||
state->inDtxMode = 0;
|
||||
state->channels = channels;
|
||||
state->sampleRateHz = sampleRateHz;
|
||||
*inst = state;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int APS5_VABI WebRtcOpus_Encode_nid_postfix(WebRtcOpusEncInst* inst, const std::int16_t* audioIn, std::size_t samples, std::size_t lengthEncodedBuffer, std::uint8_t* encoded) {
|
||||
if (!inst)
|
||||
throw std::invalid_argument("WebRtcOpus_Encode requires an encoder");
|
||||
if (samples > SamplesPerMillisecondAt48Khz * MaxFrameMillisecondsWith120Ms)
|
||||
return -1;
|
||||
if (samples > SamplesPerMillisecondAt48Khz * MaxFrameMillisecondsWithout120Ms)
|
||||
throw std::runtime_error("WebRtcOpus_Encode: frames over 60 ms depend on WEBRTC_OPUS_SUPPORT_120MS_PTIME, which is unknown for this module");
|
||||
if (!audioIn || !encoded)
|
||||
throw std::invalid_argument("WebRtcOpus_Encode requires input and output buffers");
|
||||
const int result = opus_encode(inst->encoder, audioIn, static_cast<int>(samples), encoded, static_cast<opus_int32>(lengthEncodedBuffer));
|
||||
if (result <= 0)
|
||||
return -1;
|
||||
if (result <= 2) {
|
||||
if (inst->inDtxMode)
|
||||
return 0;
|
||||
inst->inDtxMode = 1;
|
||||
return result;
|
||||
}
|
||||
inst->inDtxMode = 0;
|
||||
return result;
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetBitRate_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t rate) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_BITRATE(rate)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetPacketLossRate_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t lossRate) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_PACKET_LOSS_PERC(lossRate)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetMaxPlaybackRate_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t frequencyHz) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
opus_int32 bandwidth = OPUS_BANDWIDTH_FULLBAND;
|
||||
if (frequencyHz <= 8000)
|
||||
bandwidth = OPUS_BANDWIDTH_NARROWBAND;
|
||||
else if (frequencyHz <= 12000)
|
||||
bandwidth = OPUS_BANDWIDTH_MEDIUMBAND;
|
||||
else if (frequencyHz <= 16000)
|
||||
bandwidth = OPUS_BANDWIDTH_WIDEBAND;
|
||||
else if (frequencyHz <= 24000)
|
||||
bandwidth = OPUS_BANDWIDTH_SUPERWIDEBAND;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_MAX_BANDWIDTH(bandwidth)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_EnableFec_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_INBAND_FEC(1)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_DisableFec_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_INBAND_FEC(0)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_EnableDtx_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_DTX(1)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_DisableDtx_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_DTX(0)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_EnableCbr_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_VBR(0)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_DisableCbr_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_VBR(1)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetComplexity_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t complexity) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_COMPLEXITY(complexity)));
|
||||
}
|
||||
|
||||
std::int32_t APS5_VABI WebRtcOpus_GetBandwidth_nid_postfix(WebRtcOpusEncInst* inst) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
opus_int32 bandwidth = 0;
|
||||
if (opus_encoder_ctl(inst->encoder, OPUS_GET_BANDWIDTH(&bandwidth)) == OPUS_OK)
|
||||
return bandwidth;
|
||||
return -1;
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetBandwidth_nid_postfix(WebRtcOpusEncInst* inst, std::int32_t bandwidth) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_BANDWIDTH(bandwidth)));
|
||||
}
|
||||
|
||||
std::int16_t APS5_VABI WebRtcOpus_SetForceChannels_nid_postfix(WebRtcOpusEncInst* inst, std::size_t numChannels) {
|
||||
if (!inst)
|
||||
return -1;
|
||||
if (numChannels == 0)
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_FORCE_CHANNELS(OPUS_AUTO)));
|
||||
if (numChannels == 1 || numChannels == 2)
|
||||
return static_cast<std::int16_t>(opus_encoder_ctl(inst->encoder, OPUS_SET_FORCE_CHANNELS(static_cast<opus_int32>(numChannels))));
|
||||
return -1;
|
||||
}
|
||||
|
||||
}
|
||||
@@ -0,0 +1,265 @@
|
||||
#include "prx/libwebrtc/libwebrtc.hpp"
|
||||
|
||||
#include <algorithm>
|
||||
#include <array>
|
||||
#include <cmath>
|
||||
#include <cstddef>
|
||||
#include <cstdint>
|
||||
#include <cstdio>
|
||||
#include <cstdlib>
|
||||
#include <opus.h>
|
||||
#include <source_location>
|
||||
#include <vector>
|
||||
|
||||
namespace {
|
||||
|
||||
constexpr int SampleRate = 48000;
|
||||
constexpr std::size_t FrameSamples = 960;
|
||||
constexpr std::size_t PacketCapacity = 1500;
|
||||
constexpr std::int32_t Voip = 0;
|
||||
constexpr std::int32_t Audio = 1;
|
||||
|
||||
void Require(bool condition, std::source_location location = std::source_location::current()) {
|
||||
if (condition) return;
|
||||
std::fprintf(stderr, "libwebrtc Opus check failed at line %u\n", location.line());
|
||||
std::abort();
|
||||
}
|
||||
|
||||
WebRtcOpusEncInst* Create(std::size_t channels, std::int32_t application) {
|
||||
WebRtcOpusEncInst* inst = nullptr;
|
||||
Require(WebRtcOpus_EncoderCreate_nid_postfix(&inst, channels, application, SampleRate) == 0);
|
||||
Require(inst != nullptr);
|
||||
return inst;
|
||||
}
|
||||
|
||||
std::int16_t Tone(std::size_t index, double frequency, double amplitude) {
|
||||
return static_cast<std::int16_t>(std::lround(amplitude * std::sin(2.0 * 3.14159265358979323846 * frequency * static_cast<double>(index) / SampleRate)));
|
||||
}
|
||||
|
||||
struct Noise {
|
||||
std::uint32_t state = 12345;
|
||||
|
||||
std::int16_t Next(int amplitude) {
|
||||
state = state * 1664525u + 1013904223u;
|
||||
return static_cast<std::int16_t>(static_cast<int>(state >> 16) % (2 * amplitude + 1) - amplitude);
|
||||
}
|
||||
};
|
||||
|
||||
std::vector<std::int16_t> ToneFrame(std::size_t frame, double frequency) {
|
||||
std::vector<std::int16_t> pcm(FrameSamples);
|
||||
for (std::size_t i = 0; i < FrameSamples; ++i) pcm[i] = Tone(frame * FrameSamples + i, frequency, 8000.0);
|
||||
return pcm;
|
||||
}
|
||||
|
||||
std::vector<std::int16_t> NoiseFrame(Noise& noise, int amplitude) {
|
||||
std::vector<std::int16_t> pcm(FrameSamples);
|
||||
for (auto& sample : pcm) sample = noise.Next(amplitude);
|
||||
return pcm;
|
||||
}
|
||||
|
||||
int Encode(WebRtcOpusEncInst* inst, const std::vector<std::int16_t>& pcm, std::size_t samples, std::array<std::uint8_t, PacketCapacity>& packet) {
|
||||
return WebRtcOpus_Encode_nid_postfix(inst, pcm.data(), samples, packet.size(), packet.data());
|
||||
}
|
||||
|
||||
void TestRoundTrip() {
|
||||
auto* inst = Create(1, Voip);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, 32000) == 0);
|
||||
Require(WebRtcOpus_SetComplexity_nid_postfix(inst, 5) == 0);
|
||||
int error = OPUS_OK;
|
||||
OpusDecoder* decoder = opus_decoder_create(SampleRate, 1, &error);
|
||||
Require(error == OPUS_OK && decoder != nullptr);
|
||||
constexpr std::size_t Frames = 50;
|
||||
std::vector<std::int16_t> input;
|
||||
std::vector<std::int16_t> output;
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
for (std::size_t frame = 0; frame < Frames; ++frame) {
|
||||
const auto pcm = ToneFrame(frame, 440.0);
|
||||
input.insert(input.end(), pcm.begin(), pcm.end());
|
||||
const int size = Encode(inst, pcm, FrameSamples, packet);
|
||||
Require(size > 2 && size <= static_cast<int>(PacketCapacity));
|
||||
std::array<std::int16_t, FrameSamples> decoded {};
|
||||
Require(opus_decode(decoder, packet.data(), size, decoded.data(), static_cast<int>(FrameSamples), 0) == static_cast<int>(FrameSamples));
|
||||
output.insert(output.end(), decoded.begin(), decoded.end());
|
||||
}
|
||||
const std::size_t start = 10 * FrameSamples;
|
||||
const std::size_t length = 30 * FrameSamples;
|
||||
double best = 0.0;
|
||||
double inputEnergy = 0.0;
|
||||
double outputEnergy = 0.0;
|
||||
for (std::size_t lag = 0; lag < FrameSamples; ++lag) {
|
||||
double cross = 0.0;
|
||||
double in = 0.0;
|
||||
double out = 0.0;
|
||||
for (std::size_t i = start; i < start + length; ++i) {
|
||||
cross += static_cast<double>(input[i]) * output[i + lag];
|
||||
in += static_cast<double>(input[i]) * input[i];
|
||||
out += static_cast<double>(output[i + lag]) * output[i + lag];
|
||||
}
|
||||
const double correlation = cross / std::sqrt(in * out);
|
||||
if (correlation > best) {
|
||||
best = correlation;
|
||||
inputEnergy = in;
|
||||
outputEnergy = out;
|
||||
}
|
||||
}
|
||||
Require(best > 0.9);
|
||||
const double gain = std::sqrt(outputEnergy / inputEnergy);
|
||||
Require(gain > 0.7 && gain < 1.3);
|
||||
opus_decoder_destroy(decoder);
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
void TestForceChannels() {
|
||||
auto* inst = Create(2, Audio);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, 64000) == 0);
|
||||
std::vector<std::int16_t> pcm(FrameSamples * 2);
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
auto encodeStereo = [&](std::size_t frame) {
|
||||
for (std::size_t i = 0; i < FrameSamples; ++i) {
|
||||
pcm[i * 2] = Tone(frame * FrameSamples + i, 440.0, 8000.0);
|
||||
pcm[i * 2 + 1] = Tone(frame * FrameSamples + i, 660.0, 8000.0);
|
||||
}
|
||||
const int size = WebRtcOpus_Encode_nid_postfix(inst, pcm.data(), FrameSamples, packet.size(), packet.data());
|
||||
Require(size > 2);
|
||||
return opus_packet_get_nb_channels(packet.data());
|
||||
};
|
||||
Require(WebRtcOpus_SetForceChannels_nid_postfix(inst, 1) == 0);
|
||||
for (std::size_t frame = 0; frame < 10; ++frame) Require(encodeStereo(frame) == 1);
|
||||
Require(WebRtcOpus_SetForceChannels_nid_postfix(inst, 0) == 0);
|
||||
int channels = 0;
|
||||
for (std::size_t frame = 10; frame < 20; ++frame) channels = encodeStereo(frame);
|
||||
Require(channels == 2);
|
||||
Require(WebRtcOpus_SetForceChannels_nid_postfix(inst, 2) == 0);
|
||||
Require(WebRtcOpus_SetForceChannels_nid_postfix(inst, 3) == -1);
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
void TestConstantBitrate() {
|
||||
auto* inst = Create(1, Voip);
|
||||
Noise noise;
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
Require(WebRtcOpus_EnableCbr_nid_postfix(inst) == 0);
|
||||
for (const std::int32_t rate : {16000, 32000, 64000}) {
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, rate) == 0);
|
||||
const int expected = rate / 8 / 50;
|
||||
for (std::size_t frame = 0; frame < 10; ++frame) {
|
||||
const auto pcm = frame % 2 == 0 ? ToneFrame(frame, 300.0) : NoiseFrame(noise, 12000);
|
||||
Require(Encode(inst, pcm, FrameSamples, packet) == expected);
|
||||
}
|
||||
}
|
||||
Require(WebRtcOpus_DisableCbr_nid_postfix(inst) == 0);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, 32000) == 0);
|
||||
std::vector<int> sizes;
|
||||
for (std::size_t frame = 0; frame < 20; ++frame) {
|
||||
const auto pcm = frame % 2 == 0 ? ToneFrame(frame, 300.0) : NoiseFrame(noise, 12000);
|
||||
sizes.push_back(Encode(inst, pcm, FrameSamples, packet));
|
||||
}
|
||||
Require(std::all_of(sizes.begin(), sizes.end(), [](int size) { return size > 2; }));
|
||||
Require(*std::min_element(sizes.begin(), sizes.end()) != *std::max_element(sizes.begin(), sizes.end()));
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
void TestDtx() {
|
||||
auto* inst = Create(1, Voip);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, 32000) == 0);
|
||||
Require(WebRtcOpus_EnableDtx_nid_postfix(inst) == 0);
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
const std::vector<std::int16_t> silence(FrameSamples, 0);
|
||||
std::vector<int> results;
|
||||
for (std::size_t frame = 0; frame < 100; ++frame) results.push_back(Encode(inst, silence, FrameSamples, packet));
|
||||
Require(std::none_of(results.begin(), results.end(), [](int result) { return result < 0; }));
|
||||
const auto entered = std::find_if(results.begin(), results.end(), [](int result) { return result == 1 || result == 2; });
|
||||
Require(entered != results.end());
|
||||
Require(std::find(entered + 1, results.end(), 0) != results.end());
|
||||
Require(std::find(results.begin(), entered, 0) == entered);
|
||||
Noise noise;
|
||||
bool active = false;
|
||||
for (std::size_t frame = 0; frame < 5 && !active; ++frame) active = Encode(inst, NoiseFrame(noise, 12000), FrameSamples, packet) > 2;
|
||||
Require(active);
|
||||
Require(WebRtcOpus_DisableDtx_nid_postfix(inst) == 0);
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
void TestBandwidth() {
|
||||
auto* inst = Create(1, Voip);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, 32000) == 0);
|
||||
Noise noise;
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
Require(WebRtcOpus_SetMaxPlaybackRate_nid_postfix(inst, 8000) == 0);
|
||||
for (std::size_t frame = 0; frame < 10; ++frame) Require(Encode(inst, NoiseFrame(noise, 8000), FrameSamples, packet) > 2);
|
||||
Require(WebRtcOpus_GetBandwidth_nid_postfix(inst) == OPUS_BANDWIDTH_NARROWBAND);
|
||||
Require(WebRtcOpus_SetMaxPlaybackRate_nid_postfix(inst, 48000) == 0);
|
||||
Require(WebRtcOpus_SetBandwidth_nid_postfix(inst, OPUS_BANDWIDTH_WIDEBAND) == 0);
|
||||
for (std::size_t frame = 0; frame < 10; ++frame) Require(Encode(inst, NoiseFrame(noise, 8000), FrameSamples, packet) > 2);
|
||||
Require(WebRtcOpus_GetBandwidth_nid_postfix(inst) == OPUS_BANDWIDTH_WIDEBAND);
|
||||
Require(WebRtcOpus_SetBandwidth_nid_postfix(inst, 5) == OPUS_BAD_ARG);
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
void TestFec() {
|
||||
auto* inst = Create(1, Voip);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, 32000) == 0);
|
||||
Require(WebRtcOpus_EnableFec_nid_postfix(inst) == 0);
|
||||
Require(WebRtcOpus_SetPacketLossRate_nid_postfix(inst, 20) == 0);
|
||||
Noise noise;
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
int withLbrr = 0;
|
||||
for (std::size_t frame = 0; frame < 30; ++frame) {
|
||||
const int size = Encode(inst, NoiseFrame(noise, 8000), FrameSamples, packet);
|
||||
Require(size > 2);
|
||||
withLbrr += opus_packet_has_lbrr(packet.data(), size) == 1;
|
||||
}
|
||||
Require(withLbrr > 0);
|
||||
Require(WebRtcOpus_DisableFec_nid_postfix(inst) == 0);
|
||||
for (std::size_t frame = 0; frame < 5; ++frame) Require(Encode(inst, NoiseFrame(noise, 8000), FrameSamples, packet) > 2);
|
||||
for (std::size_t frame = 0; frame < 20; ++frame) {
|
||||
const int size = Encode(inst, NoiseFrame(noise, 8000), FrameSamples, packet);
|
||||
Require(size > 2 && opus_packet_has_lbrr(packet.data(), size) == 0);
|
||||
}
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
void TestErrors() {
|
||||
Require(WebRtcOpus_EncoderCreate_nid_postfix(nullptr, 1, Voip, SampleRate) == -1);
|
||||
auto* const untouched = reinterpret_cast<WebRtcOpusEncInst*>(static_cast<std::uintptr_t>(0x10));
|
||||
WebRtcOpusEncInst* inst = untouched;
|
||||
Require(WebRtcOpus_EncoderCreate_nid_postfix(&inst, 1, 2, SampleRate) == -1 && inst == untouched);
|
||||
Require(WebRtcOpus_EncoderCreate_nid_postfix(&inst, 1, Voip, 44100) == -1 && inst == untouched);
|
||||
Require(WebRtcOpus_EncoderCreate_nid_postfix(&inst, 3, Audio, SampleRate) == -1 && inst == untouched);
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(nullptr) == -1);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(nullptr, 32000) == -1);
|
||||
Require(WebRtcOpus_SetPacketLossRate_nid_postfix(nullptr, 10) == -1);
|
||||
Require(WebRtcOpus_SetMaxPlaybackRate_nid_postfix(nullptr, 8000) == -1);
|
||||
Require(WebRtcOpus_EnableFec_nid_postfix(nullptr) == -1 && WebRtcOpus_DisableFec_nid_postfix(nullptr) == -1);
|
||||
Require(WebRtcOpus_EnableDtx_nid_postfix(nullptr) == -1 && WebRtcOpus_DisableDtx_nid_postfix(nullptr) == -1);
|
||||
Require(WebRtcOpus_EnableCbr_nid_postfix(nullptr) == -1 && WebRtcOpus_DisableCbr_nid_postfix(nullptr) == -1);
|
||||
Require(WebRtcOpus_SetComplexity_nid_postfix(nullptr, 5) == -1);
|
||||
Require(WebRtcOpus_GetBandwidth_nid_postfix(nullptr) == -1);
|
||||
Require(WebRtcOpus_SetBandwidth_nid_postfix(nullptr, OPUS_BANDWIDTH_WIDEBAND) == -1);
|
||||
Require(WebRtcOpus_SetForceChannels_nid_postfix(nullptr, 1) == -1);
|
||||
|
||||
inst = Create(1, Audio);
|
||||
Require(WebRtcOpus_SetComplexity_nid_postfix(inst, 11) == OPUS_BAD_ARG);
|
||||
Require(WebRtcOpus_SetBitRate_nid_postfix(inst, -5) == OPUS_BAD_ARG);
|
||||
Require(WebRtcOpus_SetPacketLossRate_nid_postfix(inst, 101) == OPUS_BAD_ARG);
|
||||
std::vector<std::int16_t> pcm(48 * 120 + 1, 0);
|
||||
std::array<std::uint8_t, PacketCapacity> packet {};
|
||||
Require(Encode(inst, pcm, 48 * 120 + 1, packet) == -1);
|
||||
Require(Encode(inst, pcm, 100, packet) == -1);
|
||||
Require(WebRtcOpus_Encode_nid_postfix(inst, pcm.data(), FrameSamples, 0, packet.data()) == -1);
|
||||
Require(Encode(inst, pcm, 48 * 60, packet) > 0);
|
||||
Require(WebRtcOpus_EncoderFree_nid_postfix(inst) == 0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int main() {
|
||||
TestRoundTrip();
|
||||
TestForceChannels();
|
||||
TestConstantBitrate();
|
||||
TestDtx();
|
||||
TestBandwidth();
|
||||
TestFec();
|
||||
TestErrors();
|
||||
std::puts("libwebrtc Opus tests passed");
|
||||
}
|
||||
@@ -2,7 +2,7 @@
|
||||
|
||||
### Unknown function info
|
||||
|
||||
- [408 exports (60 without a known name, bound by NID)](../../../core/libs/prx/libwebrtc/Export.cpp) (libwebrtc) - every function export of `libwebrtc.sprx` from the firmware 13.60 export table that the next section does not implement, names from aerolib.csv only where `tools/nid_names.py` `compute_nid` reproduces the NID. Signatures, parameter layouts and behaviour are unknown; every export throws through `NotImplemented_nid_no_patch`
|
||||
- [392 exports (60 without a known name, bound by NID)](../../../core/libs/prx/libwebrtc/Export.cpp) (libwebrtc) - every function export of `libwebrtc.sprx` from the firmware 13.60 export table that the next section does not implement, names from aerolib.csv only where `tools/nid_names.py` `compute_nid` reproduces the NID. Signatures, parameter layouts and behaviour are unknown; every export throws through `NotImplemented_nid_no_patch`
|
||||
- [31 data exports](../../../core/libs/prx/libwebrtc/Export.cpp) (libwebrtc) - the module also exports 31 data objects (+qcl2IzxTzg, -ix-knPYqWA, 00mefEErs+Y, 5FM0YOa3NWc, 67nsSkXmJ7E, 7I2xDnqfDV4, 8Dzy4JayJSU, CVCDyAzGrhI, HpbtuVdvO-w, PGIpx6NnFLE, Q+xnDp4KI-s, QXDv9OpZNvM, aOOjLmup+u4, g5USRJNh3bU, hvpHGXjlpbk, iU6+qODNSjU, jpkYoOv34AM, lHLJlq3NEqE, lQJvoyxHov4, nUaORqH85DQ, o6LS+08kOUs, oh8srl3AcNE, oslHzys1vV0, sp5NqvqDdHw, t66Gw3OIpSo, tr8vseK8-ro, uSis0q+bv6k, uTz5pEKL+hM, uxkudCJIYJA, whHm70jlRXw, yW0sV3sCdPA); they are not defined, so imports of them stay unresolved until their layout is known
|
||||
|
||||
### Public source implementations
|
||||
@@ -11,3 +11,4 @@
|
||||
- [absl::numbers_internal::FastIntToBuffer(int, char*)](../../../core/libs/prx/libwebrtc/Export.cpp) (libwebrtc) - follows the abseil both branches pin, [65ef38d368](https://chromium.googlesource.com/chromium/src/third_party/+/65ef38d36813d1ad9d4cbf8a18b32ac3691f418b/abseil-cpp/absl/strings/numbers.cc) and [d212a71416](https://chromium.googlesource.com/chromium/src/third_party/+/d212a71416ad7c6ceca04d6d27dfdd84fd00c04c/abseil-cpp/absl/strings/numbers.cc): the decimal text and a NUL, returning a pointer to the NUL. For magnitudes from 10 to 99999999 its 8-byte store also zeroes the bytes after the digits up to 8 from the first digit. A null buffer, which abseil writes through, throws. Closes when: compared with the console.
|
||||
- [rtc::ExpFilter::Reset and Apply](../../../core/libs/prx/libwebrtc/src/ExpFilter.cpp) (libwebrtc) - adapted from [exp_filter.cc](https://webrtc.googlesource.com/src/+/8d78f5de6c27b2c793039989ea381f1428fb0100/rtc_base/numerics/exp_filter.cc), with its WebRTC copyright notice and BSD licence at the top of the file, on the 12-byte `alpha_`, `filtered_` and `max_` layout of `exp_filter.h`. -1 marks both an unset output and no maximum, so an output that lands on exactly -1 restarts the filter on the next sample, as upstream does. The last bit of a result can differ if the console build fuses the multiply-adds. A null filter throws. Closes when: compared with the console.
|
||||
- [webrtc::rtcp::CommonHeader::Parse](../../../core/libs/prx/libwebrtc/src/RtcpCommonHeader.cpp) (libwebrtc) - adapted from [common_header.cc](https://webrtc.googlesource.com/src/+/8d78f5de6c27b2c793039989ea381f1428fb0100/modules/rtp_rtcp/source/rtcp_packet/common_header.cc), with its WebRTC copyright notice and BSD licence at the top of the file, on the 16-byte layout of `common_header.h`: packet type, count or format and padding size at 0 to 2, payload size at 4 and payload pointer at 8. Its checks run in the same order, so a short buffer or a wrong version leaves the header untouched and the later failures leave the fields already parsed. The `RTC_LOG(LS_WARNING)` message each failure logs upstream is not emitted. A null header, or a null buffer of 4 bytes or more, which upstream reads, throws. Closes when: the warnings are logged through libwebrtc's logging and the parse is compared with the console.
|
||||
- [WebRtcOpus encoder functions](../../../core/libs/prx/libwebrtc/src/Opus.cpp) (libwebrtc) - the 16 `WebRtcOpus_*` encoder exports are adapted from [opus_interface.cc](https://webrtc.googlesource.com/src/+/8d78f5de6c27b2c793039989ea381f1428fb0100/modules/audio_coding/codecs/opus/opus_interface.cc), with its WebRTC copyright notice and BSD licence at the top of the file. They encode through the bundled libopus 1.6.1, not WebRTC's pinned copy, so packet bytes can differ. Signatures match at both revisions; M127 only adds a DTX change behind the `WebRTC-Audio-OpusAvoidNoisePumpingDuringDtx` field trial, which stays off because `InitFieldTrialsFromString` throws. Frames over 60 ms, up to 120 ms, throw: they depend on `WEBRTC_OPUS_SUPPORT_120MS_PTIME`. A null encoder or buffer in `Encode`, which upstream reads, throws. Closes when: the build flag is known and packets are compared with the console.
|
||||
Reference in new issue
Block a user