Fri, 16 Jan 2015 04:50:19 +0100
Replace accessor implementation with direct member state manipulation, by
request https://trac.torproject.org/projects/tor/ticket/9701#comment:32
1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
2 /* vim:set ts=2 sw=2 sts=2 et cindent: */
3 /*
4 * Copyright (c) 2014 The Linux Foundation. All rights reserved.
5 * Copyright (C) 2009 The Android Open Source Project
6 *
7 * Licensed under the Apache License, Version 2.0 (the "License");
8 * you may not use this file except in compliance with the License.
9 * You may obtain a copy of the License at
10 *
11 * http://www.apache.org/licenses/LICENSE-2.0
12 *
13 * Unless required by applicable law or agreed to in writing, software
14 * distributed under the License is distributed on an "AS IS" BASIS,
15 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
16 * See the License for the specific language governing permissions and
17 * limitations under the License.
18 */
20 #include "AudioOffloadPlayer.h"
21 #include "nsComponentManagerUtils.h"
22 #include "nsITimer.h"
23 #include "mozilla/dom/HTMLMediaElement.h"
25 #include <binder/IPCThreadState.h>
26 #include <stagefright/foundation/ADebug.h>
27 #include <stagefright/foundation/ALooper.h>
28 #include <stagefright/MediaDefs.h>
29 #include <stagefright/MediaErrors.h>
30 #include <stagefright/MediaSource.h>
31 #include <stagefright/MetaData.h>
32 #include <stagefright/Utils.h>
33 #include <AudioTrack.h>
34 #include <AudioSystem.h>
35 #include <AudioParameter.h>
36 #include <hardware/audio.h>
38 using namespace android;
40 namespace mozilla {
42 #ifdef PR_LOGGING
43 PRLogModuleInfo* gAudioOffloadPlayerLog;
44 #define AUDIO_OFFLOAD_LOG(type, msg) \
45 PR_LOG(gAudioOffloadPlayerLog, type, msg)
46 #else
47 #define AUDIO_OFFLOAD_LOG(type, msg)
48 #endif
50 // maximum time in paused state when offloading audio decompression.
51 // When elapsed, the AudioSink is destroyed to allow the audio DSP to power down.
52 static const uint64_t OFFLOAD_PAUSE_MAX_MSECS = 60000ll;
54 AudioOffloadPlayer::AudioOffloadPlayer(MediaOmxDecoder* aObserver) :
55 mObserver(aObserver),
56 mInputBuffer(nullptr),
57 mSampleRate(0),
58 mSeeking(false),
59 mSeekDuringPause(false),
60 mReachedEOS(false),
61 mSeekTimeUs(0),
62 mStartPosUs(0),
63 mPositionTimeMediaUs(-1),
64 mStarted(false),
65 mPlaying(false),
66 mIsElementVisible(true)
67 {
68 MOZ_ASSERT(NS_IsMainThread());
70 #ifdef PR_LOGGING
71 if (!gAudioOffloadPlayerLog) {
72 gAudioOffloadPlayerLog = PR_NewLogModule("AudioOffloadPlayer");
73 }
74 #endif
76 CHECK(aObserver);
77 mSessionId = AudioSystem::newAudioSessionId();
78 AudioSystem::acquireAudioSessionId(mSessionId);
79 mAudioSink = new AudioOutput(mSessionId,
80 IPCThreadState::self()->getCallingUid());
81 }
83 AudioOffloadPlayer::~AudioOffloadPlayer()
84 {
85 Reset();
86 AudioSystem::releaseAudioSessionId(mSessionId);
87 }
89 void AudioOffloadPlayer::SetSource(const sp<MediaSource> &aSource)
90 {
91 MOZ_ASSERT(NS_IsMainThread());
92 CHECK(!mSource.get());
94 mSource = aSource;
95 }
97 status_t AudioOffloadPlayer::Start(bool aSourceAlreadyStarted)
98 {
99 MOZ_ASSERT(NS_IsMainThread());
100 CHECK(!mStarted);
101 CHECK(mSource.get());
103 status_t err;
104 CHECK(mAudioSink.get());
106 if (!aSourceAlreadyStarted) {
107 err = mSource->start();
109 if (err != OK) {
110 return err;
111 }
112 }
114 sp<MetaData> format = mSource->getFormat();
115 const char* mime;
116 int avgBitRate = -1;
117 int32_t channelMask;
118 int32_t numChannels;
119 int64_t durationUs = -1;
120 audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT;
121 uint32_t flags = AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD;
122 audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER;
124 CHECK(format->findCString(kKeyMIMEType, &mime));
125 CHECK(format->findInt32(kKeySampleRate, &mSampleRate));
126 CHECK(format->findInt32(kKeyChannelCount, &numChannels));
127 format->findInt32(kKeyBitRate, &avgBitRate);
128 format->findInt64(kKeyDuration, &durationUs);
130 if(!format->findInt32(kKeyChannelMask, &channelMask)) {
131 channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
132 }
134 if (mapMimeToAudioFormat(audioFormat, mime) != OK) {
135 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Couldn't map mime type \"%s\" to a valid "
136 "AudioSystem::audio_format", mime));
137 audioFormat = AUDIO_FORMAT_INVALID;
138 }
140 offloadInfo.duration_us = durationUs;
141 offloadInfo.sample_rate = mSampleRate;
142 offloadInfo.channel_mask = channelMask;
143 offloadInfo.format = audioFormat;
144 offloadInfo.stream_type = AUDIO_STREAM_MUSIC;
145 offloadInfo.bit_rate = avgBitRate;
146 offloadInfo.has_video = false;
147 offloadInfo.is_streaming = false;
149 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("isOffloadSupported: SR=%u, CM=0x%x, "
150 "Format=0x%x, StreamType=%d, BitRate=%u, duration=%lld us, has_video=%d",
151 offloadInfo.sample_rate, offloadInfo.channel_mask, offloadInfo.format,
152 offloadInfo.stream_type, offloadInfo.bit_rate, offloadInfo.duration_us,
153 offloadInfo.has_video));
155 err = mAudioSink->Open(mSampleRate,
156 numChannels,
157 channelMask,
158 audioFormat,
159 &AudioOffloadPlayer::AudioSinkCallback,
160 this,
161 (audio_output_flags_t) flags,
162 &offloadInfo);
163 if (err == OK) {
164 // If the playback is offloaded to h/w we pass the
165 // HAL some metadata information
166 // We don't want to do this for PCM because it will be going
167 // through the AudioFlinger mixer before reaching the hardware
168 SendMetaDataToHal(mAudioSink, format);
169 }
170 mStarted = true;
171 mPlaying = false;
173 return err;
174 }
176 status_t AudioOffloadPlayer::ChangeState(MediaDecoder::PlayState aState)
177 {
178 MOZ_ASSERT(NS_IsMainThread());
179 mPlayState = aState;
181 switch (mPlayState) {
182 case MediaDecoder::PLAY_STATE_PLAYING: {
183 status_t err = Play();
184 if (err != OK) {
185 return err;
186 }
187 StartTimeUpdate();
188 } break;
190 case MediaDecoder::PLAY_STATE_SEEKING: {
191 int64_t seekTimeUs
192 = mObserver->GetSeekTime();
193 SeekTo(seekTimeUs, true);
194 mObserver->ResetSeekTime();
195 } break;
197 case MediaDecoder::PLAY_STATE_PAUSED:
198 case MediaDecoder::PLAY_STATE_SHUTDOWN:
199 // Just pause here during play state shutdown as well to stop playing
200 // offload track immediately. Resources will be freed by MediaOmxDecoder
201 Pause();
202 break;
204 case MediaDecoder::PLAY_STATE_ENDED:
205 Pause(true);
206 break;
208 default:
209 break;
210 }
211 return OK;
212 }
214 static void ResetCallback(nsITimer* aTimer, void* aClosure)
215 {
216 AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure);
217 if (player) {
218 player->Reset();
219 }
220 }
222 void AudioOffloadPlayer::Pause(bool aPlayPendingSamples)
223 {
224 MOZ_ASSERT(NS_IsMainThread());
226 if (mStarted) {
227 CHECK(mAudioSink.get());
228 if (aPlayPendingSamples) {
229 mAudioSink->Stop();
230 } else {
231 mAudioSink->Pause();
232 }
233 mPlaying = false;
234 }
236 if (mResetTimer) {
237 return;
238 }
239 mResetTimer = do_CreateInstance("@mozilla.org/timer;1");
240 mResetTimer->InitWithFuncCallback(ResetCallback,
241 this,
242 OFFLOAD_PAUSE_MAX_MSECS,
243 nsITimer::TYPE_ONE_SHOT);
244 }
246 status_t AudioOffloadPlayer::Play()
247 {
248 MOZ_ASSERT(NS_IsMainThread());
250 if (mResetTimer) {
251 mResetTimer->Cancel();
252 mResetTimer = nullptr;
253 }
255 status_t err = OK;
257 if (!mStarted) {
258 // Last pause timed out and offloaded audio sink was reset. Start it again
259 err = Start(false);
260 if (err != OK) {
261 return err;
262 }
263 // Seek to last play position only when there was no seek during last pause
264 if (!mSeeking) {
265 SeekTo(mPositionTimeMediaUs);
266 }
267 }
269 if (!mPlaying) {
270 CHECK(mAudioSink.get());
271 err = mAudioSink->Start();
272 if (err == OK) {
273 mPlaying = true;
274 }
275 }
277 return err;
278 }
280 void AudioOffloadPlayer::Reset()
281 {
282 if (!mStarted) {
283 return;
284 }
286 CHECK(mAudioSink.get());
288 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("reset: mPlaying=%d mReachedEOS=%d",
289 mPlaying, mReachedEOS));
291 mAudioSink->Stop();
292 // If we're closing and have reached EOS, we don't want to flush
293 // the track because if it is offloaded there could be a small
294 // amount of residual data in the hardware buffer which we must
295 // play to give gapless playback.
296 // But if we're resetting when paused or before we've reached EOS
297 // we can't be doing a gapless playback and there could be a large
298 // amount of data queued in the hardware if the track is offloaded,
299 // so we must flush to prevent a track switch being delayed playing
300 // the buffered data that we don't want now
301 if (!mPlaying || !mReachedEOS) {
302 mAudioSink->Flush();
303 }
305 mAudioSink->Close();
306 // Make sure to release any buffer we hold onto so that the
307 // source is able to stop().
309 if (mInputBuffer) {
310 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Releasing input buffer"));
312 mInputBuffer->release();
313 mInputBuffer = nullptr;
314 }
315 mSource->stop();
317 IPCThreadState::self()->flushCommands();
318 StopTimeUpdate();
320 mReachedEOS = false;
321 mStarted = false;
322 mPlaying = false;
323 mStartPosUs = 0;
324 }
326 status_t AudioOffloadPlayer::SeekTo(int64_t aTimeUs, bool aDispatchSeekEvents)
327 {
328 MOZ_ASSERT(NS_IsMainThread());
329 CHECK(mAudioSink.get());
331 android::Mutex::Autolock autoLock(mLock);
333 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SeekTo ( %lld )", aTimeUs));
335 mSeeking = true;
336 mReachedEOS = false;
337 mPositionTimeMediaUs = -1;
338 mSeekTimeUs = aTimeUs;
339 mStartPosUs = aTimeUs;
340 mDispatchSeekEvents = aDispatchSeekEvents;
342 if (mDispatchSeekEvents) {
343 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
344 &MediaDecoder::SeekingStarted);
345 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
346 }
348 if (mPlaying) {
349 mAudioSink->Pause();
350 mAudioSink->Flush();
351 mAudioSink->Start();
353 } else {
354 mSeekDuringPause = true;
356 if (mStarted) {
357 mAudioSink->Flush();
358 }
360 if (mDispatchSeekEvents) {
361 mDispatchSeekEvents = false;
362 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Fake seek complete during pause"));
363 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
364 &MediaDecoder::SeekingStopped);
365 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
366 }
367 }
369 return OK;
370 }
372 double AudioOffloadPlayer::GetMediaTimeSecs()
373 {
374 MOZ_ASSERT(NS_IsMainThread());
375 return (static_cast<double>(GetMediaTimeUs()) /
376 static_cast<double>(USECS_PER_S));
377 }
379 int64_t AudioOffloadPlayer::GetMediaTimeUs()
380 {
381 android::Mutex::Autolock autoLock(mLock);
383 int64_t playPosition = 0;
384 if (mSeeking) {
385 return mSeekTimeUs;
386 }
387 if (!mStarted) {
388 return mPositionTimeMediaUs;
389 }
391 playPosition = GetOutputPlayPositionUs_l();
392 if (!mReachedEOS) {
393 mPositionTimeMediaUs = playPosition;
394 }
396 return mPositionTimeMediaUs;
397 }
399 int64_t AudioOffloadPlayer::GetOutputPlayPositionUs_l() const
400 {
401 CHECK(mAudioSink.get());
402 uint32_t playedSamples = 0;
404 mAudioSink->GetPosition(&playedSamples);
406 const int64_t playedUs = (static_cast<int64_t>(playedSamples) * 1000000 ) /
407 mSampleRate;
409 // HAL position is relative to the first buffer we sent at mStartPosUs
410 const int64_t renderedDuration = mStartPosUs + playedUs;
411 return renderedDuration;
412 }
414 void AudioOffloadPlayer::NotifyAudioEOS()
415 {
416 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
417 &MediaDecoder::PlaybackEnded);
418 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
419 }
421 void AudioOffloadPlayer::NotifyPositionChanged()
422 {
423 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
424 &MediaOmxDecoder::PlaybackPositionChanged);
425 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
426 }
428 void AudioOffloadPlayer::NotifyAudioTearDown()
429 {
430 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
431 &MediaOmxDecoder::AudioOffloadTearDown);
432 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
433 }
435 // static
436 size_t AudioOffloadPlayer::AudioSinkCallback(AudioSink* aAudioSink,
437 void* aBuffer,
438 size_t aSize,
439 void* aCookie,
440 AudioSink::cb_event_t aEvent)
441 {
442 AudioOffloadPlayer* me = (AudioOffloadPlayer*) aCookie;
444 switch (aEvent) {
446 case AudioSink::CB_EVENT_FILL_BUFFER:
447 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio position changed"));
448 me->NotifyPositionChanged();
449 return me->FillBuffer(aBuffer, aSize);
451 case AudioSink::CB_EVENT_STREAM_END:
452 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Audio EOS"));
453 me->mReachedEOS = true;
454 me->NotifyAudioEOS();
455 break;
457 case AudioSink::CB_EVENT_TEAR_DOWN:
458 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Notify Tear down event"));
459 me->NotifyAudioTearDown();
460 break;
462 default:
463 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Unknown event %d from audio sink",
464 aEvent));
465 break;
466 }
467 return 0;
468 }
470 size_t AudioOffloadPlayer::FillBuffer(void* aData, size_t aSize)
471 {
472 CHECK(mAudioSink.get());
474 if (mReachedEOS) {
475 return 0;
476 }
478 size_t sizeDone = 0;
479 size_t sizeRemaining = aSize;
480 while (sizeRemaining > 0) {
481 MediaSource::ReadOptions options;
482 bool refreshSeekTime = false;
484 {
485 android::Mutex::Autolock autoLock(mLock);
487 if (mSeeking) {
488 options.setSeekTo(mSeekTimeUs);
489 refreshSeekTime = true;
491 if (mInputBuffer) {
492 mInputBuffer->release();
493 mInputBuffer = nullptr;
494 }
495 mSeeking = false;
496 }
497 }
499 if (!mInputBuffer) {
501 status_t err;
502 err = mSource->read(&mInputBuffer, &options);
504 CHECK((!err && mInputBuffer) || (err && !mInputBuffer));
506 android::Mutex::Autolock autoLock(mLock);
508 if (err != OK) {
509 AUDIO_OFFLOAD_LOG(PR_LOG_ERROR, ("Error while reading media source %d "
510 "Ok to receive EOS error at end", err));
511 if (!mReachedEOS) {
512 // After seek there is a possible race condition if
513 // OffloadThread is observing state_stopping_1 before
514 // framesReady() > 0. Ensure sink stop is called
515 // after last buffer is released. This ensures the
516 // partial buffer is written to the driver before
517 // stopping one is observed.The drawback is that
518 // there will be an unnecessary call to the parser
519 // after parser signalled EOS.
520 if (sizeDone > 0) {
521 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("send Partial buffer down"));
522 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("skip calling stop till next"
523 " fillBuffer"));
524 break;
525 }
526 // no more buffers to push - stop() and wait for STREAM_END
527 // don't set mReachedEOS until stream end received
528 mAudioSink->Stop();
529 }
530 break;
531 }
533 if(mInputBuffer->range_length() != 0) {
534 CHECK(mInputBuffer->meta_data()->findInt64(
535 kKeyTime, &mPositionTimeMediaUs));
536 }
538 if (refreshSeekTime) {
540 if (mDispatchSeekEvents && !mSeekDuringPause) {
541 mDispatchSeekEvents = false;
542 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("FillBuffer posting SEEK_COMPLETE"));
543 nsCOMPtr<nsIRunnable> nsEvent = NS_NewRunnableMethod(mObserver,
544 &MediaDecoder::SeekingStopped);
545 NS_DispatchToMainThread(nsEvent, NS_DISPATCH_NORMAL);
547 } else if (mSeekDuringPause) {
548 // Callback is already called for seek during pause. Just reset the
549 // flag
550 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Not posting seek complete as its"
551 " already faked"));
552 mSeekDuringPause = false;
553 }
555 NotifyPositionChanged();
557 // need to adjust the mStartPosUs for offload decoding since parser
558 // might not be able to get the exact seek time requested.
559 mStartPosUs = mPositionTimeMediaUs;
560 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Adjust seek time to: %.2f",
561 mStartPosUs / 1E6));
563 // clear seek time with mLock locked and once we have valid
564 // mPositionTimeMediaUs
565 // before clearing mSeekTimeUs check if a new seek request has been
566 // received while we were reading from the source with mLock released.
567 if (!mSeeking) {
568 mSeekTimeUs = 0;
569 }
570 }
571 }
573 if (mInputBuffer->range_length() == 0) {
574 mInputBuffer->release();
575 mInputBuffer = nullptr;
576 continue;
577 }
579 size_t copy = sizeRemaining;
580 if (copy > mInputBuffer->range_length()) {
581 copy = mInputBuffer->range_length();
582 }
584 memcpy((char *)aData + sizeDone,
585 (const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
586 copy);
588 mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
589 mInputBuffer->range_length() - copy);
591 sizeDone += copy;
592 sizeRemaining -= copy;
593 }
594 return sizeDone;
595 }
597 void AudioOffloadPlayer::SetElementVisibility(bool aIsVisible)
598 {
599 MOZ_ASSERT(NS_IsMainThread());
600 mIsElementVisible = aIsVisible;
601 if (mIsElementVisible) {
602 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("Element is visible. Start time update"));
603 StartTimeUpdate();
604 }
605 }
607 static void TimeUpdateCallback(nsITimer* aTimer, void* aClosure)
608 {
609 AudioOffloadPlayer* player = static_cast<AudioOffloadPlayer*>(aClosure);
610 player->TimeUpdate();
611 }
613 void AudioOffloadPlayer::TimeUpdate()
614 {
615 MOZ_ASSERT(NS_IsMainThread());
616 TimeStamp now = TimeStamp::Now();
618 // If TIMEUPDATE_MS has passed since the last fire update event fired, fire
619 // another timeupdate event.
620 if ((mLastFireUpdateTime.IsNull() ||
621 now - mLastFireUpdateTime >=
622 TimeDuration::FromMilliseconds(TIMEUPDATE_MS))) {
623 mLastFireUpdateTime = now;
624 NotifyPositionChanged();
625 }
627 if (mPlayState != MediaDecoder::PLAY_STATE_PLAYING || !mIsElementVisible) {
628 StopTimeUpdate();
629 }
630 }
632 nsresult AudioOffloadPlayer::StartTimeUpdate()
633 {
634 MOZ_ASSERT(NS_IsMainThread());
635 if (mTimeUpdateTimer) {
636 return NS_OK;
637 }
639 mTimeUpdateTimer = do_CreateInstance("@mozilla.org/timer;1");
640 return mTimeUpdateTimer->InitWithFuncCallback(TimeUpdateCallback,
641 this,
642 TIMEUPDATE_MS,
643 nsITimer::TYPE_REPEATING_SLACK);
644 }
646 nsresult AudioOffloadPlayer::StopTimeUpdate()
647 {
648 MOZ_ASSERT(NS_IsMainThread());
649 if (!mTimeUpdateTimer) {
650 return NS_OK;
651 }
653 nsresult rv = mTimeUpdateTimer->Cancel();
654 mTimeUpdateTimer = nullptr;
655 return rv;
656 }
658 MediaDecoderOwner::NextFrameStatus AudioOffloadPlayer::GetNextFrameStatus()
659 {
660 MOZ_ASSERT(NS_IsMainThread());
661 if (mPlayState == MediaDecoder::PLAY_STATE_SEEKING) {
662 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE_BUFFERING;
663 } else if (mPlayState == MediaDecoder::PLAY_STATE_ENDED) {
664 return MediaDecoderOwner::NEXT_FRAME_UNAVAILABLE;
665 } else {
666 return MediaDecoderOwner::NEXT_FRAME_AVAILABLE;
667 }
668 }
670 void AudioOffloadPlayer::SendMetaDataToHal(sp<AudioSink>& aSink,
671 const sp<MetaData>& aMeta)
672 {
673 int32_t sampleRate = 0;
674 int32_t bitRate = 0;
675 int32_t channelMask = 0;
676 int32_t delaySamples = 0;
677 int32_t paddingSamples = 0;
678 CHECK(aSink.get());
680 AudioParameter param = AudioParameter();
682 if (aMeta->findInt32(kKeySampleRate, &sampleRate)) {
683 param.addInt(String8(AUDIO_OFFLOAD_CODEC_SAMPLE_RATE), sampleRate);
684 }
685 if (aMeta->findInt32(kKeyChannelMask, &channelMask)) {
686 param.addInt(String8(AUDIO_OFFLOAD_CODEC_NUM_CHANNEL), channelMask);
687 }
688 if (aMeta->findInt32(kKeyBitRate, &bitRate)) {
689 param.addInt(String8(AUDIO_OFFLOAD_CODEC_AVG_BIT_RATE), bitRate);
690 }
691 if (aMeta->findInt32(kKeyEncoderDelay, &delaySamples)) {
692 param.addInt(String8(AUDIO_OFFLOAD_CODEC_DELAY_SAMPLES), delaySamples);
693 }
694 if (aMeta->findInt32(kKeyEncoderPadding, &paddingSamples)) {
695 param.addInt(String8(AUDIO_OFFLOAD_CODEC_PADDING_SAMPLES), paddingSamples);
696 }
698 AUDIO_OFFLOAD_LOG(PR_LOG_DEBUG, ("SendMetaDataToHal: bitRate %d,"
699 " sampleRate %d, chanMask %d, delaySample %d, paddingSample %d", bitRate,
700 sampleRate, channelMask, delaySamples, paddingSamples));
702 aSink->SetParameters(param.toString());
703 return;
704 }
706 void AudioOffloadPlayer::SetVolume(double aVolume)
707 {
708 MOZ_ASSERT(NS_IsMainThread());
709 CHECK(mAudioSink.get());
710 mAudioSink->SetVolume((float) aVolume);
711 }
713 } // namespace mozilla