forked from moonlight-stream/moonlight-chrome
-
Notifications
You must be signed in to change notification settings - Fork 0
/
auddec.cpp
92 lines (72 loc) · 3.89 KB
/
auddec.cpp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#include "moonlight.hpp"
#define MAX_CHANNEL_COUNT 2
#define FRAME_SIZE 240
#define CIRCULAR_BUFFER_SIZE 32
// This code uses volatiles for synchronization between the producer and consumer side. This is
// only safe because this code executes under very specific conditions, namely that the framework
// ensures AudioPlayerSampleCallback and AudDecDecodeAndPlaySample are each only active on one thread
// at a time.
static short s_CircularBuffer[CIRCULAR_BUFFER_SIZE][FRAME_SIZE * MAX_CHANNEL_COUNT];
static int s_ReadIndex = 0;
static int s_WriteIndex = 0;
static void AudioPlayerSampleCallback(void* samples, uint32_t buffer_size, void* data) {
// It should only ask us for complete buffers
assert(buffer_size == FRAME_SIZE * MAX_CHANNEL_COUNT * sizeof(short));
// If the indexes aren't equal, we have a sample
if (s_WriteIndex != s_ReadIndex) {
memcpy(samples, s_CircularBuffer[s_ReadIndex], buffer_size);
// Use a full memory barrier to ensure the circular buffer is read before incrementing the index
__sync_synchronize();
// This can race with the reader in the AudDecDecodeAndPlaySample function. This is
// not a problem because at worst, it just won't see that we've consumed this sample yet.
s_ReadIndex = (s_ReadIndex + 1) % CIRCULAR_BUFFER_SIZE;
}
else {
memset(samples, 0, buffer_size);
}
}
int MoonlightInstance::AudDecInit(int audioConfiguration, POPUS_MULTISTREAM_CONFIGURATION opusConfig, void* context, int flags) {
int rc;
g_Instance->m_OpusDecoder = opus_multistream_decoder_create(opusConfig->sampleRate,
opusConfig->channelCount,
opusConfig->streams,
opusConfig->coupledStreams,
opusConfig->mapping,
&rc);
g_Instance->m_AudioPlayer = pp::Audio(g_Instance, pp::AudioConfig(g_Instance, PP_AUDIOSAMPLERATE_48000, FRAME_SIZE),
AudioPlayerSampleCallback, NULL);
// Start playback now
g_Instance->m_AudioPlayer.StartPlayback();
return 0;
}
void MoonlightInstance::AudDecCleanup(void) {
// Stop playback
g_Instance->m_AudioPlayer.StopPlayback();
if (g_Instance->m_OpusDecoder) {
opus_multistream_decoder_destroy(g_Instance->m_OpusDecoder);
}
}
void MoonlightInstance::AudDecDecodeAndPlaySample(char* sampleData, int sampleLength) {
int decodeLen;
// Check if there is space for this sample in the buffer. Again, this can race
// but in the worst case, we'll not see the sample callback having consumed a sample.
if (((s_WriteIndex + 1) % CIRCULAR_BUFFER_SIZE) == s_ReadIndex) {
return;
}
decodeLen = opus_multistream_decode(g_Instance->m_OpusDecoder, (unsigned char *)sampleData, sampleLength,
s_CircularBuffer[s_WriteIndex], FRAME_SIZE, 0);
if (decodeLen > 0) {
// Use a full memory barrier to ensure the circular buffer is written before incrementing the index
__sync_synchronize();
// This can race with the reader in the sample callback, however this is a benign
// race since we'll either read the original value of s_WriteIndex (which is safe,
// we just won't consider this sample) or the new value of s_WriteIndex
s_WriteIndex = (s_WriteIndex + 1) % CIRCULAR_BUFFER_SIZE;
}
}
AUDIO_RENDERER_CALLBACKS MoonlightInstance::s_ArCallbacks = {
.init = MoonlightInstance::AudDecInit,
.cleanup = MoonlightInstance::AudDecCleanup,
.decodeAndPlaySample = MoonlightInstance::AudDecDecodeAndPlaySample,
.capabilities = CAPABILITY_DIRECT_SUBMIT
};