-
Notifications
You must be signed in to change notification settings - Fork 1
/
Copy pathAgrippinaAudioESPlayer.cpp
392 lines (346 loc) · 13.2 KB
/
AgrippinaAudioESPlayer.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
193
194
195
196
197
198
199
200
201
202
203
204
205
206
207
208
209
210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
247
248
249
250
251
252
253
254
255
256
257
258
259
260
261
262
263
264
265
266
267
268
269
270
271
272
273
274
275
276
277
278
279
280
281
282
283
284
285
286
287
288
289
290
291
292
293
294
295
296
297
298
299
300
301
302
303
304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
329
330
331
332
333
334
335
336
337
338
339
340
341
342
343
344
345
346
347
348
349
350
351
352
353
354
355
356
357
358
359
360
361
362
363
364
365
366
367
368
369
370
371
372
373
374
375
376
377
378
379
380
381
382
383
384
385
386
387
388
389
390
391
392
/*
* (c) 1997-2016 Netflix, Inc. All content herein is protected by
* U.S. copyright and other applicable intellectual property laws and
* may not be copied without the express permission of Netflix, Inc.,
* which reserves all rights. Reuse of any of this content for any
* purpose without the permission of Netflix, Inc. is strictly
* prohibited.
*/
#include "AgrippinaAudioESPlayer.h"
#include "AgrippinaPlaybackGroup.h"
#include "AgrippinaESManager.h"
#include "ESPlayerConstants.h"
#include "DeviceThread.h"
#include "AgrippinaSampleWriter.h"
#include <nrd/AppThread.h>
#include <nrdbase/Log.h>
#include <nrdbase/Time.h>
#include <nrdbase/Thread.h>
#include <nrdbase/ScopedMutex.h>
#include <nrdbase/NFErr.h>
using namespace netflix::device::esplayer;
using namespace netflix;
Nero_audio_codec_t AgrippinaAudioESPlayer::Nrd2NeroAudioCodec_Remap(const char* nrd_audio_codec)
{
Nero_audio_codec_t NeroAudioCodec = NERO_CODEC_AUDIO_INVALID;
Log::error(TRACE_MEDIACONTROL, ">>>> codec =%s !!!\n",nrd_audio_codec );
if(strcmp (nrd_audio_codec, "ec-3") == 0)
{
NeroAudioCodec = NERO_CODEC_AUDIO_DD_PLUS;
}
else if(strcmp (nrd_audio_codec, "mp4a") == 0)
{
NeroAudioCodec = NERO_CODEC_AUDIO_AAC;
}
return(NeroAudioCodec);
}
AgrippinaAudioESPlayer::~AgrippinaAudioESPlayer()
{
Nero_error_t result = NERO_SUCCESS;
setCloseThreadsFlag();
result = m_NeroAudioDecoder->UnInit();
m_NeroAudioDecoder = NULL;
m_stc = NULL;
}
void AgrippinaAudioESPlayer::close()
{
Nero_error_t result = NERO_SUCCESS;
setCloseThreadsFlag();
AgrippinaESPlayer::close();
result = m_NeroAudioDecoder->Close();
}
AgrippinaAudioESPlayer::AgrippinaAudioESPlayer() :
mPlaybackPending(false),
mInputExhausted(false),
mIsFlushing(false)
{
}
AgrippinaAudioESPlayer::AgrippinaAudioESPlayer(NeroAudioDecoder* NeroAudioDecoder_ptr,NeroSTC* stc_ptr) :
mPlaybackPending(false),
mInputExhausted(false),
mIsFlushing(false)
{
m_NeroAudioDecoder = NeroAudioDecoder_ptr;
m_stc = stc_ptr;
}
NFErr AgrippinaAudioESPlayer::init(const struct StreamPlayerInitData& initData,
std::shared_ptr<IESPlayerCallback> callback,
AgrippinaPlaybackGroup* playbackGroup)
{
NFErr err;
Nero_error_t result = NERO_SUCCESS;
Nero_audio_codec_t NeroAudioAlgo = NERO_CODEC_AUDIO_INVALID;
AgrippinaESPlayer::init(initData, callback, playbackGroup);
uint32_t samplingRate = 48000;
NRDP_UNUSED(samplingRate);
if(initData.mInitialStreamAttributes->mAudioAttributes->mSamplesPerSecond != 0)
{
samplingRate = initData.mInitialStreamAttributes->
mAudioAttributes->mSamplesPerSecond;
}
NeroAudioAlgo = Nrd2NeroAudioCodec_Remap(initData.mInitialStreamAttributes->mAudioAttributes->mCodecParam.c_str());
NeroAudioFMT = NeroAudioAlgo;
result = m_NeroAudioDecoder->Init((size_t)NeroAudioAlgo);
if(NERO_SUCCESS != result)
{
Log::error(TRACE_MEDIACONTROL, ">>>> could not init Nero Audio decoder for =%s codec !!!\n",initData.mInitialStreamAttributes->mAudioAttributes->mCodecParam.c_str());
}
mSampleWriter.reset(new AgrippinaSampleWriter(NOT_3D, callback));
mAudioDecoderThread.reset(new DeviceThread(*this, &AgrippinaESPlayer::DecoderTask,
&THREAD_REFERENCE_DPI_AUDIO_DECODER));
mAudioEventThread.reset(new DeviceThread(*this, &AgrippinaESPlayer::EventTask,
&THREAD_REFERENCE_DPI_AUDIO_EVENT));
return err;
}
void AgrippinaAudioESPlayer::EventHandling(NeroEvents_t *event)
{
}
void AgrippinaAudioESPlayer::EventTask()
{
const Time EVENT_TASK_WAIT_TIME(100);
NeroEvents_t event;
while(closeThreadsFlagIsSet() == false)
{
if (m_NeroAudioDecoder->NeroEventWait(&event) == NERO_SUCCESS)
{
EventHandling(&event);
}
Thread::sleep(EVENT_TASK_WAIT_TIME);
}
}
void AgrippinaAudioESPlayer::DecoderTask()
{
static const Time WAIT_FOR_AUDIO_SAMPLE_BUFFER(30);
static const Time WAIT_FOR_AUDIO_DATA(100);
static const Time WAIT_WHILE_IDLING(100);
Status status;
const ISampleAttributes* pSampleAttr;
DecodedAudioBuffer decodedAudioBuffer;
bool errorReported = false;
NFErr err = NFErr_OK;
bool discontinuity =false;
Nero_error_t result = NERO_SUCCESS;
Nero_audio_codec_t NeroAudioAlgo = NERO_CODEC_AUDIO_INVALID;
while(closeThreadsFlagIsSet() == false)
{
if(mIsFlushing)
{
{
ScopedMutex cs(mDecoderTaskMutex);
mIsFlushing = true;
mEndOfStreamFlag = false;
}
Thread::sleep(ESPlayerConstants::WAIT_FOR_FLUSH_OFF);
continue;
}
if(errorReported)
{
Thread::sleep(WAIT_WHILE_IDLING);
continue;
}
// Playback may be pending if the player was enabled while the playback
// group's playback state was PLAY. This would be the case after an
// on-the-fly audio switch, for example. When playback is pending, the
// renderer continues to act as if the player is disabled (e.g. it
// doesn't render output and it doesn't report underflow). If we have
// enough audio frames buffered to begin playback, we should lower the
// pending flag and let playback begin.
if(playbackPending() && readyForPlaybackStart())
{
setPlaybackPending(false);
}
status = mCallback->getNextMediaSample(*mSampleWriter);
if(status == NO_AVAILABLE_SAMPLES) {
mInputExhausted = true;
Thread::sleep(WAIT_FOR_AUDIO_DATA);
continue;
} else if (status == NO_AVAILABLE_BUFFERS)
{
Thread::sleep(WAIT_FOR_AUDIO_DATA);
continue;
} else if (status == END_OF_STREAM)
{
// This is the only thread that sets mEndOfStream. We don't need
// the mutex to read it.
if(mEndOfStreamFlag == false)
{
NTRACE(TRACE_MEDIAPLAYBACK, "AgrippinaAudioESPlayer::decoderTask: "
"getNextMediaSample returns END_OF_STREAM");
}
{
// The renderer thread reads mEndOfStreamFlag. We need the mutex
// to set it.
ScopedMutex cs(mDecoderTaskMutex);
mEndOfStreamFlag = true;
}
mInputExhausted = true;
Thread::sleep(WAIT_FOR_AUDIO_DATA);
continue;
} else if (status == STREAM_ERROR) {
Log::error(TRACE_MEDIAPLAYBACK, "AgrippinaAudioESPlayer::decoderTask: "
"getNextMediaSample returns STREAM_ERROR. ");
errorReported = true;
continue;
} else if (status == ERROR) {
Log::error(TRACE_MEDIAPLAYBACK, "AgrippinaAudioESPlayer::decoderTask: "
"getNextMediaSample returns ERROR. ");
errorReported = true;
continue;
} else {
pSampleAttr = mSampleWriter->getSampleAttributes();
/*
* audio gap handling
*/
if ( pSampleAttr->getDiscontinuityGapInMs() > 1 ){
// this means that there is a discontinuity in fqront of this sample.
// send silent data corresponding to gap amount to decodedAudioBuffer
NTRACE(TRACE_MEDIAPLAYBACK, "AgrippinaAudioESPlayer::decoderTask: audioPtsGap %lld",
pSampleAttr->getDiscontinuityGapInMs());
discontinuity = true;
}
err = decrypt(pSampleAttr, *mSampleWriter);
if(!err.ok())
{
NTRACE(TRACE_MEDIAPLAYBACK, "AudioESPlayer received encrypted data. Audio stream should be clear.");
mCallback->reportError(err);
errorReported = true;
continue;
}
}
mInputExhausted = false;
if(pSampleAttr->getMediaAttributes() != 0)
{
// Update the audio decoder attributes after each discontinuity
// (right after open() or flush()). This is because the selected
// audio stream might be different after discontinuity, we must
// update the corresponding audio attributes to the decoder.
assert(pSampleAttr->getMediaAttributes()->mAudioAttributes);
NTRACE(TRACE_MEDIAPLAYBACK, "AudioESPlayer::decoderTask: updating audio decoder attributes"
" because sample media attributes are non-null.");
if(pSampleAttr->getMediaAttributes()->mAudioAttributes != NULL)
{
Log::error(TRACE_MEDIAPLAYBACK, "hammadiiiiiiiii");
// Reset audio device based on the audio sampling frequency of
// the selected audio stream.
const uint32_t samplesPerSecond = pSampleAttr->getMediaAttributes()->mAudioAttributes->mSamplesPerSecond;
NRDP_UNUSED(samplesPerSecond);
// Partner should check the current audio settings, if they have
// changed, then they need to reconfigure their decoder.
NeroAudioAlgo = Nrd2NeroAudioCodec_Remap(pSampleAttr->getMediaAttributes()->mAudioAttributes->mCodecParam.c_str());
if (NeroAudioAlgo != NeroAudioFMT){
result = m_NeroAudioDecoder->Reconfigure((size_t)NeroAudioAlgo);
NeroAudioFMT = NeroAudioAlgo;
Log::error(TRACE_MEDIAPLAYBACK, "NeroAudioAlgo %d",NeroAudioFMT);
}
}
}
if(true /* Feed the decoder true */ )
{
// push buffers down to audio renderer here.
// sampleWriter.getBuffer() pointer to the buffer
// sampleWriter.getSampleSize() size of the sample
NTRACE(TRACE_MEDIAPLAYBACK, "Feed Audio DATA");
m_NeroAudioDecoder->Feed(mSampleWriter->getBuffer(), mSampleWriter->getSampleSize(),(uint64_t)pSampleAttr->getPTS(),discontinuity);
}
else
{
Log::error(TRACE_MEDIAPLAYBACK, "Error AgrippinaAudioESPlayer::decoderTask: decode returns false.");
/* We don't call mCallback->reportError() here so that an issue
* decoding one audio frame doesn't force the UI to unload the
* movie. We continue decoding and try recover on the next
* sample.*/
if(mIsFlushing){
NTRACE(TRACE_MEDIAPLAYBACK, "[%d] AgrippinaAudioESPlayer::%s ignoring decode error while flushing"
, mPipelineId, __func__);
continue;
}
if(closeThreadsFlagIsSet() == true){
NTRACE(TRACE_MEDIAPLAYBACK, "[%d] AgrippinaAudioESPlayer::%s ignoring decode error while closing"
, mPipelineId, __func__);
return;
}
}
// if possible, partner should report dropped frames through this method
// reportDecodeOutcome(mAudioDecoder->getStatsCounter());
}
}
bool AgrippinaAudioESPlayer::readyForPlaybackStart()
{
{
// The decoder thread sets mEndOfStreamFlag. This method is called on
// the SDK's pumping loop thread. We need the mutex to read it.
ScopedMutex cs(mDecoderTaskMutex);
if(mEndOfStreamFlag)
{
return true;
}
}
// Partners should set this flag when the decoder has enough buffer
return true;
}
// If the AudioESPlayer is enabled and the playback state is play, we are
// probably coming out of an on-the-fly audio switch. In this case, set playback
// to pending so that we don't actually try to start rendering media until we
// are sure we've buffered enough decoded frames that we are unlikely to
// immediately underflow at the renderer. Each time through the decoder loop we
// will check whether we are ready to move out of the pending state and begin
// playing back.
void AgrippinaAudioESPlayer::enable()
{
AgrippinaESPlayer::enable();
}
void AgrippinaAudioESPlayer::flush()
{
if(mPlaybackGroup->getPlaybackState() == IPlaybackGroup::PLAY)
{
NTRACE(TRACE_MEDIACONTROL,
"On-the-fly audio track switch in progress, setPlaybackPending");
setPlaybackPending(true);
}
beginFlush();
mPlaybackGroup->audioFlushed();
endFlush();
}
void AgrippinaAudioESPlayer::beginFlush()
{
m_NeroAudioDecoder->Flush();
mIsFlushing = true;
}
void AgrippinaAudioESPlayer::endFlush()
{
mIsFlushing = false;
}
bool AgrippinaAudioESPlayer::inputsAreExhausted()
{
if(mInputExhausted)
{
return true;
}
return false;
}
MediaType AgrippinaAudioESPlayer::getMediaType()
{
return AUDIO;
}
void AgrippinaAudioESPlayer::underflowReporter()
{
if(!playbackPending())
{
AgrippinaESPlayer::underflowReporter();
}
}
bool AgrippinaAudioESPlayer::isDisabledOrPending()
{
ScopedMutex cs(mDecoderTaskMutex);
return mDisabled || mPlaybackPending;
}
bool AgrippinaAudioESPlayer::playbackPending()
{
ScopedMutex cs(mDecoderTaskMutex);
return mPlaybackPending;
}
void AgrippinaAudioESPlayer::setPlaybackPending(bool val)
{
ScopedMutex cs(mDecoderTaskMutex);
mPlaybackPending = val;
}