mirror of
https://github.com/moonlight-stream/moonlight-chrome.git
synced 2025-08-17 08:36:42 +00:00
Add audio decoding support
This commit is contained in:
parent
8d02bd8dc0
commit
c9e65715cb
1
Makefile
1
Makefile
@ -28,6 +28,7 @@ SOURCES = \
|
|||||||
gamepad.cpp \
|
gamepad.cpp \
|
||||||
connectionlistener.cpp \
|
connectionlistener.cpp \
|
||||||
viddec.cpp \
|
viddec.cpp \
|
||||||
|
auddec.cpp \
|
||||||
|
|
||||||
# Build rules generated by macros from common.mk:
|
# Build rules generated by macros from common.mk:
|
||||||
|
|
||||||
|
143
auddec.cpp
Normal file
143
auddec.cpp
Normal file
@ -0,0 +1,143 @@
|
|||||||
|
#include "moonlight.hpp"
|
||||||
|
|
||||||
|
#define MAX_CHANNEL_COUNT 6
|
||||||
|
#define FRAME_SIZE 240
|
||||||
|
|
||||||
|
typedef struct decoded_sample_entry {
|
||||||
|
struct decoded_sample_entry *next;
|
||||||
|
int sampleLength;
|
||||||
|
short sampleBuffer[1];
|
||||||
|
} decoded_sample_entry_t;
|
||||||
|
|
||||||
|
#define MAX_QUEUE_LENGTH 14
|
||||||
|
#define QUEUE_PRUNING_LENGTH 7
|
||||||
|
|
||||||
|
static int s_OpusChannelCount;
|
||||||
|
static decoded_sample_entry_t* s_SampleQueueHead;
|
||||||
|
static decoded_sample_entry_t* s_SampleQueueTail;
|
||||||
|
static int s_SampleQueueLength;
|
||||||
|
static pthread_mutex_t s_SampleQueueLock;
|
||||||
|
|
||||||
|
static void ReapSampleQueue() {
|
||||||
|
decoded_sample_entry_t *entry;
|
||||||
|
|
||||||
|
while (s_SampleQueueHead) {
|
||||||
|
entry = s_SampleQueueHead->next;
|
||||||
|
free(s_SampleQueueHead);
|
||||||
|
s_SampleQueueHead = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_SampleQueueTail = NULL;
|
||||||
|
|
||||||
|
s_SampleQueueLength = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
static void AudioPlayerSampleCallback(void* samples, uint32_t buffer_size, void* data) {
|
||||||
|
unsigned char* buffer = (unsigned char *)samples;
|
||||||
|
int offset = 0;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_SampleQueueLock);
|
||||||
|
|
||||||
|
while (s_SampleQueueHead && s_SampleQueueHead->sampleLength <= buffer_size - offset) {
|
||||||
|
decoded_sample_entry_t* lastEnt;
|
||||||
|
|
||||||
|
memcpy(&buffer[offset], s_SampleQueueHead->sampleBuffer, s_SampleQueueHead->sampleLength);
|
||||||
|
offset += s_SampleQueueHead->sampleLength;
|
||||||
|
|
||||||
|
lastEnt = s_SampleQueueHead;
|
||||||
|
s_SampleQueueHead = s_SampleQueueHead->next;
|
||||||
|
free(lastEnt);
|
||||||
|
s_SampleQueueLength--;
|
||||||
|
|
||||||
|
// Remove another sample if we're in pruning mode
|
||||||
|
if (s_SampleQueueLength > QUEUE_PRUNING_LENGTH) {
|
||||||
|
lastEnt = s_SampleQueueHead;
|
||||||
|
s_SampleQueueHead = s_SampleQueueHead->next;
|
||||||
|
free(lastEnt);
|
||||||
|
s_SampleQueueLength--;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s_SampleQueueHead) {
|
||||||
|
s_SampleQueueTail = NULL;
|
||||||
|
}
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&s_SampleQueueLock);
|
||||||
|
|
||||||
|
// Zero the remaining portion of the sample buffer to reduce noise when underflowing
|
||||||
|
if (buffer_size != offset) {
|
||||||
|
memset(&buffer[offset], 0, buffer_size - offset);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoonlightInstance::AudDecInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig) {
|
||||||
|
int rc;
|
||||||
|
|
||||||
|
pthread_mutex_init(&s_SampleQueueLock, NULL);
|
||||||
|
|
||||||
|
s_OpusChannelCount = opusConfig->channelCount;
|
||||||
|
g_Instance->m_OpusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
|
||||||
|
opusConfig->channelCount,
|
||||||
|
opusConfig->streams,
|
||||||
|
opusConfig->coupledStreams,
|
||||||
|
opusConfig->mapping,
|
||||||
|
&rc);
|
||||||
|
|
||||||
|
pp::AudioConfig audioConfig = pp::AudioConfig(g_Instance, PP_AUDIOSAMPLERATE_48000, FRAME_SIZE * 3);
|
||||||
|
|
||||||
|
g_Instance->m_AudioPlayer = pp::Audio(g_Instance, audioConfig, AudioPlayerSampleCallback, NULL);
|
||||||
|
|
||||||
|
// Start playback now
|
||||||
|
g_Instance->m_AudioPlayer.StartPlayback();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoonlightInstance::AudDecCleanup(void) {
|
||||||
|
pthread_mutex_destroy(&s_SampleQueueLock);
|
||||||
|
|
||||||
|
if (g_Instance->m_OpusDecoder) {
|
||||||
|
opus_multistream_decoder_destroy(g_Instance->m_OpusDecoder);
|
||||||
|
}
|
||||||
|
|
||||||
|
ReapSampleQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
void MoonlightInstance::AudDecDecodeAndPlaySample(char* sampleData, int sampleLength) {
|
||||||
|
decoded_sample_entry_t* entry = (decoded_sample_entry_t*)malloc(sizeof(decoded_sample_entry_t) +
|
||||||
|
(s_OpusChannelCount * FRAME_SIZE * sizeof(short)));
|
||||||
|
if (entry) {
|
||||||
|
int decodeLen = opus_multistream_decode(g_Instance->m_OpusDecoder, (unsigned char *)sampleData, sampleLength,
|
||||||
|
entry->sampleBuffer, FRAME_SIZE, 0);
|
||||||
|
if (decodeLen > 0) {
|
||||||
|
entry->sampleLength = decodeLen * s_OpusChannelCount * sizeof(short);
|
||||||
|
entry->next = NULL;
|
||||||
|
|
||||||
|
pthread_mutex_lock(&s_SampleQueueLock);
|
||||||
|
|
||||||
|
if (s_SampleQueueLength == MAX_QUEUE_LENGTH) {
|
||||||
|
printf("Reaped sample queue\n");
|
||||||
|
ReapSampleQueue();
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!s_SampleQueueTail) {
|
||||||
|
s_SampleQueueHead = s_SampleQueueTail = entry;
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
s_SampleQueueTail->next = entry;
|
||||||
|
s_SampleQueueTail = entry;
|
||||||
|
}
|
||||||
|
|
||||||
|
s_SampleQueueLength++;
|
||||||
|
|
||||||
|
pthread_mutex_unlock(&s_SampleQueueLock);
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
free(entry);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
AUDIO_RENDERER_CALLBACKS MoonlightInstance::s_ArCallbacks = {
|
||||||
|
MoonlightInstance::AudDecInit,
|
||||||
|
MoonlightInstance::AudDecCleanup,
|
||||||
|
MoonlightInstance::AudDecDecodeAndPlaySample
|
||||||
|
};
|
2
main.cpp
2
main.cpp
@ -53,7 +53,7 @@ void* MoonlightInstance::ConnectionThreadFunc(void* context) {
|
|||||||
&s_StreamConfig,
|
&s_StreamConfig,
|
||||||
&MoonlightInstance::s_ClCallbacks,
|
&MoonlightInstance::s_ClCallbacks,
|
||||||
&MoonlightInstance::s_DrCallbacks,
|
&MoonlightInstance::s_DrCallbacks,
|
||||||
NULL,
|
&MoonlightInstance::s_ArCallbacks,
|
||||||
NULL, 0, 4);
|
NULL, 0, 4);
|
||||||
if (err != 0) {
|
if (err != 0) {
|
||||||
pp::Var response("Starting connection failed");
|
pp::Var response("Starting connection failed");
|
||||||
|
@ -4,6 +4,7 @@
|
|||||||
#include "ppapi/cpp/mouse_lock.h"
|
#include "ppapi/cpp/mouse_lock.h"
|
||||||
#include "ppapi/cpp/graphics_3d.h"
|
#include "ppapi/cpp/graphics_3d.h"
|
||||||
#include "ppapi/cpp/video_decoder.h"
|
#include "ppapi/cpp/video_decoder.h"
|
||||||
|
#include "ppapi/cpp/audio.h"
|
||||||
|
|
||||||
#include "ppapi/c/ppb_gamepad.h"
|
#include "ppapi/c/ppb_gamepad.h"
|
||||||
#include "ppapi/c/pp_input_event.h"
|
#include "ppapi/c/pp_input_event.h"
|
||||||
@ -22,6 +23,8 @@
|
|||||||
|
|
||||||
#include <Limelight.h>
|
#include <Limelight.h>
|
||||||
|
|
||||||
|
#include <opus_multistream.h>
|
||||||
|
|
||||||
struct Shader {
|
struct Shader {
|
||||||
Shader() : program(0), texcoord_scale_location(0) {}
|
Shader() : program(0), texcoord_scale_location(0) {}
|
||||||
~Shader() {}
|
~Shader() {}
|
||||||
@ -36,6 +39,7 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
|
|||||||
pp::Instance(instance),
|
pp::Instance(instance),
|
||||||
pp::MouseLock(this),
|
pp::MouseLock(this),
|
||||||
m_IsPainting(false),
|
m_IsPainting(false),
|
||||||
|
m_OpusDecoder(NULL),
|
||||||
m_CallbackFactory(this),
|
m_CallbackFactory(this),
|
||||||
m_MouseLocked(false),
|
m_MouseLocked(false),
|
||||||
m_KeyModifiers(0) {
|
m_KeyModifiers(0) {
|
||||||
@ -90,9 +94,14 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
|
|||||||
static void VidDecCleanup(void);
|
static void VidDecCleanup(void);
|
||||||
static int VidDecSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
static int VidDecSubmitDecodeUnit(PDECODE_UNIT decodeUnit);
|
||||||
|
|
||||||
|
static void AudDecInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig);
|
||||||
|
static void AudDecCleanup(void);
|
||||||
|
static void AudDecDecodeAndPlaySample(char* sampleData, int sampleLength);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
static CONNECTION_LISTENER_CALLBACKS s_ClCallbacks;
|
static CONNECTION_LISTENER_CALLBACKS s_ClCallbacks;
|
||||||
static DECODER_RENDERER_CALLBACKS s_DrCallbacks;
|
static DECODER_RENDERER_CALLBACKS s_DrCallbacks;
|
||||||
|
static AUDIO_RENDERER_CALLBACKS s_ArCallbacks;
|
||||||
|
|
||||||
pp::Graphics3D m_Graphics3D;
|
pp::Graphics3D m_Graphics3D;
|
||||||
pp::VideoDecoder* m_VideoDecoder;
|
pp::VideoDecoder* m_VideoDecoder;
|
||||||
@ -103,6 +112,9 @@ class MoonlightInstance : public pp::Instance, public pp::MouseLock {
|
|||||||
std::queue<PP_VideoPicture> m_PendingPictureQueue;
|
std::queue<PP_VideoPicture> m_PendingPictureQueue;
|
||||||
bool m_IsPainting;
|
bool m_IsPainting;
|
||||||
|
|
||||||
|
OpusMSDecoder* m_OpusDecoder;
|
||||||
|
pp::Audio m_AudioPlayer;
|
||||||
|
|
||||||
double m_LastPadTimestamps[4];
|
double m_LastPadTimestamps[4];
|
||||||
const PPB_Gamepad* m_GamepadApi;
|
const PPB_Gamepad* m_GamepadApi;
|
||||||
pp::CompletionCallbackFactory<MoonlightInstance> m_CallbackFactory;
|
pp::CompletionCallbackFactory<MoonlightInstance> m_CallbackFactory;
|
||||||
|
Loading…
x
Reference in New Issue
Block a user