forked from jfchapman/VUPlayer
-
Notifications
You must be signed in to change notification settings - Fork 0
/
EncoderOpus.cpp
192 lines (175 loc) · 5.41 KB
/
EncoderOpus.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
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
#include "EncoderOpus.h"
#include "Handler.h"
#include "Utility.h"
#include <vector>
// Minimum/maximum/default bit rates.
static const int s_MinimumBitrate = 8;
static const int s_MaximumBitrate = 256;
static const int s_DefaultBitrate = 128;
EncoderOpus::EncoderOpus() :
Encoder(),
m_Channels( 0 ),
m_OpusEncoder( nullptr ),
m_Callbacks( {} )
{
}
EncoderOpus::~EncoderOpus()
{
}
int EncoderOpus::WriteCallback( void *user_data, const unsigned char *ptr, opus_int32 len )
{
FILE* f = reinterpret_cast<FILE*>( user_data );
if ( ( nullptr != f ) && ( nullptr != ptr ) && ( len > 0 ) ) {
fwrite( ptr, 1, len, f );
}
return 0;
}
int EncoderOpus::CloseCallback( void *user_data )
{
FILE* f = reinterpret_cast<FILE*>( user_data );
if ( nullptr != f ) {
fclose( f );
}
return 0;
}
bool EncoderOpus::Open( std::wstring& filename, const long sampleRate, const long channels, const std::optional<long> /*bitsPerSample*/, const long long /*totalSamples*/, const std::string& settings, const Tags& /*tags*/ )
{
m_Channels = channels;
OggOpusComments* opusComments = ope_comments_create();
if ( nullptr != opusComments ) {
filename += L".opus";
FILE* f = _wfsopen( filename.c_str(), L"wb", _SH_DENYRW );
if ( nullptr != f ) {
m_Callbacks.write = WriteCallback;
m_Callbacks.close = CloseCallback;
const int family = ( m_Channels > 8 ) ? 255 : ( ( m_Channels > 2 ) ? 1 : 0 );
m_OpusEncoder = ope_encoder_create_callbacks( &m_Callbacks, f /*userData*/, opusComments, sampleRate, channels, family, nullptr /*error*/ );
if ( nullptr != m_OpusEncoder ) {
const int bitrate = 1000 * GetBitrate( settings );
ope_encoder_ctl( m_OpusEncoder, OPUS_SET_BITRATE( bitrate ) );
} else {
fclose( f );
}
}
ope_comments_destroy( opusComments );
}
const bool success = ( nullptr != m_OpusEncoder );
return success;
}
bool EncoderOpus::Write( float* samples, const long sampleCount )
{
// For multi-channel streams, change from BASS to Opus channel ordering.
switch ( m_Channels ) {
case 3 : {
// (left, right, center) ->
// (left, center, right)
long offset = 0;
for ( long n = 0; n < sampleCount; n++, offset += m_Channels ) {
std::swap( samples[ offset + 1 ], samples[ offset + 2 ] );
}
break;
}
case 5 : {
// (front left, front right, front center, rear left, rear right) ->
// (front left, front center, front right, rear left, rear right)
long offset = 0;
for ( long n = 0; n < sampleCount; n++, offset += m_Channels ) {
std::swap( samples[ offset + 1 ], samples[ offset + 2 ] );
}
break;
}
case 6 : {
// (front left, front right, front center, LFE, rear left, rear right) ->
// (front left, front center, front right, rear left, rear right, LFE)
long offset = 0;
for ( long n = 0; n < sampleCount; n++, offset += m_Channels ) {
std::swap( samples[ offset + 1 ], samples[ offset + 2 ] );
const float lfe = samples[ offset + 3 ];
const float rearL = samples[ offset + 4 ];
const float rearR = samples[ offset + 5 ];
samples[ offset + 3 ] = rearL;
samples[ offset + 4 ] = rearR;
samples[ offset + 5 ] = lfe;
}
break;
}
case 7 : {
// (front left, front right, front center, LFE, rear center, side left, side right) ->
// (front left, front center, front right, side left, side right, rear center, LFE)
long offset = 0;
for ( long n = 0; n < sampleCount; n++, offset += m_Channels ) {
std::swap( samples[ offset + 1 ], samples[ offset + 2 ] );
const float lfe = samples[ offset + 3 ];
const float rearC = samples[ offset + 4 ];
const float sideL = samples[ offset + 5 ];
const float sideR = samples[ offset + 6 ];
samples[ offset + 3 ] = sideL;
samples[ offset + 4 ] = sideR;
samples[ offset + 5 ] = rearC;
samples[ offset + 6 ] = lfe;
}
break;
}
case 8 : {
// (front left, front right, front center, LFE, rear left, rear right, side left, side right) ->
// (front left, front center, front right, side left, side right, rear left, rear right, LFE)
long offset = 0;
for ( long n = 0; n < sampleCount; n++, offset += m_Channels ) {
std::swap( samples[ offset + 1 ], samples[ offset + 2 ] );
const float lfe = samples[ offset + 3 ];
const float rearL = samples[ offset + 4 ];
const float rearR = samples[ offset + 5 ];
const float sideL = samples[ offset + 6 ];
const float sideR = samples[ offset + 7 ];
samples[ offset + 3 ] = sideL;
samples[ offset + 4 ] = sideR;
samples[ offset + 5 ] = rearL;
samples[ offset + 6 ] = rearR;
samples[ offset + 7 ] = lfe;
}
break;
}
default: {
break;
}
}
const bool success = ( OPE_OK == ope_encoder_write_float( m_OpusEncoder, samples, sampleCount ) );
return success;
}
void EncoderOpus::Close()
{
if ( nullptr != m_OpusEncoder ) {
ope_encoder_drain( m_OpusEncoder );
ope_encoder_destroy( m_OpusEncoder );
}
}
int EncoderOpus::GetBitrate( const std::string& settings )
{
int bitrate = s_DefaultBitrate;
try {
bitrate = std::stoi( settings );
} catch ( ... ) {
}
LimitBitrate( bitrate );
return bitrate;
}
int EncoderOpus::GetDefaultBitrate()
{
return s_DefaultBitrate;
}
int EncoderOpus::GetMinimumBitrate()
{
return s_MinimumBitrate;
}
int EncoderOpus::GetMaximumBitrate()
{
return s_MaximumBitrate;
}
void EncoderOpus::LimitBitrate( int& bitrate )
{
if ( bitrate < s_MinimumBitrate ) {
bitrate = s_MinimumBitrate;
} else if ( bitrate > s_MaximumBitrate ) {
bitrate = s_MaximumBitrate;
}
}