|
47 | 47 | #if defined(ENABLE_OPUS) |
48 | 48 | #include "avstream/OpusFileStreamer.h" |
49 | 49 | #include "codec/OpusDecoder.h" |
| 50 | +#include "codec/OpusEncoder.h" |
50 | 51 | #endif |
51 | 52 |
|
52 | 53 | #if defined(ENABLE_FFMPEG) |
@@ -3641,6 +3642,88 @@ TEST_CASE("OPUSFileSeek") |
3641 | 3642 | REQUIRE(opusdecfile.GetDurationMSec() == mfi.uDurationMSec); |
3642 | 3643 | } |
3643 | 3644 |
|
| 3645 | +TEST_CASE("OpusFECRecovery") |
| 3646 | +{ |
| 3647 | + const int SAMPLERATE = 48000; |
| 3648 | + const int CHANNELS = 1; |
| 3649 | + const int FRAMESIZE = SAMPLERATE * 20 / 1000; |
| 3650 | + const int N_FRAMES = 12; |
| 3651 | + const int LOST_FRAME = 4; |
| 3652 | + |
| 3653 | + OpusEncode enc; |
| 3654 | + REQUIRE(enc.Open(SAMPLERATE, CHANNELS, OPUS_APPLICATION_VOIP)); |
| 3655 | + REQUIRE(enc.SetBitrate(16000)); |
| 3656 | + REQUIRE(enc.SetComplexity(10)); |
| 3657 | + REQUIRE(enc.SetFEC(true)); |
| 3658 | + REQUIRE(enc.SetPacketLossPerc(30)); |
| 3659 | + REQUIRE(enc.SetSignalVoice(true)); |
| 3660 | + REQUIRE(enc.SetLSBDepth(16)); |
| 3661 | + REQUIRE(enc.SetDTX(false)); |
| 3662 | + REQUIRE(enc.SetVBR(false)); |
| 3663 | + |
| 3664 | + std::vector<short> input(FRAMESIZE * N_FRAMES); |
| 3665 | + for (int f = 0; f < N_FRAMES; ++f) |
| 3666 | + { |
| 3667 | + int period = 60 + f * 8; |
| 3668 | + int half = period / 2; |
| 3669 | + for (int s = 0; s < FRAMESIZE; ++s) |
| 3670 | + { |
| 3671 | + int phase = s % period; |
| 3672 | + int tri = (phase < half) ? phase : (period - phase); |
| 3673 | + input[f * FRAMESIZE + s] = static_cast<short>((tri - half / 2) * 400); |
| 3674 | + } |
| 3675 | + } |
| 3676 | + |
| 3677 | + std::vector<std::vector<char>> packets(N_FRAMES); |
| 3678 | + for (int i = 0; i < N_FRAMES; ++i) |
| 3679 | + { |
| 3680 | + std::vector<char> buf(4000); |
| 3681 | + int ret = enc.Encode(&input[i * FRAMESIZE], FRAMESIZE, buf.data(), int(buf.size())); |
| 3682 | + REQUIRE(ret > 0); |
| 3683 | + buf.resize(ret); |
| 3684 | + packets[i] = std::move(buf); |
| 3685 | + } |
| 3686 | + |
| 3687 | + OpusDecode decPLC, decFEC; |
| 3688 | + REQUIRE(decPLC.Open(SAMPLERATE, CHANNELS)); |
| 3689 | + REQUIRE(decFEC.Open(SAMPLERATE, CHANNELS)); |
| 3690 | + |
| 3691 | + std::vector<short> outPLC(FRAMESIZE), outFEC(FRAMESIZE), warm(FRAMESIZE); |
| 3692 | + |
| 3693 | + for (int i = 0; i < LOST_FRAME; ++i) |
| 3694 | + { |
| 3695 | + REQUIRE(decPLC.Decode(packets[i].data(), int(packets[i].size()), warm.data(), FRAMESIZE) == FRAMESIZE); |
| 3696 | + REQUIRE(decFEC.Decode(packets[i].data(), int(packets[i].size()), warm.data(), FRAMESIZE) == FRAMESIZE); |
| 3697 | + } |
| 3698 | + |
| 3699 | + int retPLC = decPLC.Decode(nullptr, 0, outPLC.data(), FRAMESIZE, false); |
| 3700 | + int retFEC = decFEC.Decode(packets[LOST_FRAME + 1].data(), |
| 3701 | + int(packets[LOST_FRAME + 1].size()), |
| 3702 | + outFEC.data(), FRAMESIZE, true); |
| 3703 | + REQUIRE(retPLC == FRAMESIZE); |
| 3704 | + REQUIRE(retFEC == FRAMESIZE); |
| 3705 | + |
| 3706 | + long long fecEnergy = 0; |
| 3707 | + for (short s : outFEC) fecEnergy += (s < 0 ? -int(s) : int(s)); |
| 3708 | + REQUIRE(fecEnergy > 0); |
| 3709 | + |
| 3710 | + bool differs = false; |
| 3711 | + for (int i = 0; i < FRAMESIZE; ++i) |
| 3712 | + if (outPLC[i] != outFEC[i]) { differs = true; break; } |
| 3713 | + REQUIRE(differs); |
| 3714 | + |
| 3715 | + const short* truth = &input[LOST_FRAME * FRAMESIZE]; |
| 3716 | + long long ssePLC = 0, sseFEC = 0; |
| 3717 | + for (int i = 0; i < FRAMESIZE; ++i) |
| 3718 | + { |
| 3719 | + long long dP = outPLC[i] - truth[i]; |
| 3720 | + long long dF = outFEC[i] - truth[i]; |
| 3721 | + ssePLC += dP * dP; |
| 3722 | + sseFEC += dF * dF; |
| 3723 | + } |
| 3724 | + REQUIRE(sseFEC < ssePLC); |
| 3725 | +} |
| 3726 | + |
3644 | 3727 | TEST_CASE("OPUSStreamer") |
3645 | 3728 | { |
3646 | 3729 | const auto IN_SAMPLERATE = 12000; |
|
0 commit comments