blob: 00cbec2731605f12f899a0b41e4a50806b578a1c [file] [log] [blame]
Andreas Huberf9334412010-12-15 15:17:42 -08001/*
2 * Copyright (C) 2010 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17//#define LOG_NDEBUG 0
18#define LOG_TAG "NuPlayerRenderer"
19#include <utils/Log.h>
20
21#include "NuPlayerRenderer.h"
22
23#include <media/stagefright/foundation/ABuffer.h>
24#include <media/stagefright/foundation/ADebug.h>
Andreas Huber5bc087c2010-12-23 10:27:40 -080025#include <media/stagefright/foundation/AMessage.h>
Andreas Huberf9334412010-12-15 15:17:42 -080026
27namespace android {
28
29NuPlayer::Renderer::Renderer(
30 const sp<MediaPlayerBase::AudioSink> &sink,
31 const sp<AMessage> &notify)
32 : mAudioSink(sink),
33 mNotify(notify),
34 mNumFramesWritten(0),
35 mDrainAudioQueuePending(false),
36 mDrainVideoQueuePending(false),
37 mAudioQueueGeneration(0),
38 mVideoQueueGeneration(0),
39 mAnchorTimeMediaUs(-1),
40 mAnchorTimeRealUs(-1),
41 mFlushingAudio(false),
42 mFlushingVideo(false),
Andreas Huber3831a062010-12-21 10:22:33 -080043 mHasAudio(mAudioSink != NULL),
44 mHasVideo(true),
45 mSyncQueues(mHasAudio && mHasVideo) {
Andreas Huberf9334412010-12-15 15:17:42 -080046}
47
48NuPlayer::Renderer::~Renderer() {
49}
50
51void NuPlayer::Renderer::queueBuffer(
52 bool audio,
53 const sp<ABuffer> &buffer,
54 const sp<AMessage> &notifyConsumed) {
55 sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
56 msg->setInt32("audio", static_cast<int32_t>(audio));
57 msg->setObject("buffer", buffer);
58 msg->setMessage("notifyConsumed", notifyConsumed);
59 msg->post();
60}
61
62void NuPlayer::Renderer::queueEOS(bool audio, status_t finalResult) {
63 CHECK_NE(finalResult, (status_t)OK);
64
65 sp<AMessage> msg = new AMessage(kWhatQueueEOS, id());
66 msg->setInt32("audio", static_cast<int32_t>(audio));
67 msg->setInt32("finalResult", finalResult);
68 msg->post();
69}
70
71void NuPlayer::Renderer::flush(bool audio) {
72 {
73 Mutex::Autolock autoLock(mFlushLock);
74 if (audio) {
75 CHECK(!mFlushingAudio);
76 mFlushingAudio = true;
77 } else {
78 CHECK(!mFlushingVideo);
79 mFlushingVideo = true;
80 }
81 }
82
83 sp<AMessage> msg = new AMessage(kWhatFlush, id());
84 msg->setInt32("audio", static_cast<int32_t>(audio));
85 msg->post();
86}
87
88void NuPlayer::Renderer::signalTimeDiscontinuity() {
89 CHECK(mAudioQueue.empty());
90 CHECK(mVideoQueue.empty());
91 mAnchorTimeMediaUs = -1;
92 mAnchorTimeRealUs = -1;
Andreas Huber3831a062010-12-21 10:22:33 -080093 mSyncQueues = mHasAudio && mHasVideo;
Andreas Huberf9334412010-12-15 15:17:42 -080094}
95
96void NuPlayer::Renderer::onMessageReceived(const sp<AMessage> &msg) {
97 switch (msg->what()) {
98 case kWhatDrainAudioQueue:
99 {
100 int32_t generation;
101 CHECK(msg->findInt32("generation", &generation));
102 if (generation != mAudioQueueGeneration) {
103 break;
104 }
105
106 mDrainAudioQueuePending = false;
107
108 onDrainAudioQueue();
109
110 postDrainAudioQueue();
111 break;
112 }
113
114 case kWhatDrainVideoQueue:
115 {
116 int32_t generation;
117 CHECK(msg->findInt32("generation", &generation));
118 if (generation != mVideoQueueGeneration) {
119 break;
120 }
121
122 mDrainVideoQueuePending = false;
123
124 onDrainVideoQueue();
125
126 postDrainVideoQueue();
127 break;
128 }
129
130 case kWhatQueueBuffer:
131 {
132 onQueueBuffer(msg);
133 break;
134 }
135
136 case kWhatQueueEOS:
137 {
138 onQueueEOS(msg);
139 break;
140 }
141
142 case kWhatFlush:
143 {
144 onFlush(msg);
145 break;
146 }
147
Andreas Huber3831a062010-12-21 10:22:33 -0800148 case kWhatAudioSinkChanged:
149 {
150 onAudioSinkChanged();
151 break;
152 }
153
Andreas Huberf9334412010-12-15 15:17:42 -0800154 default:
155 TRESPASS();
156 break;
157 }
158}
159
160void NuPlayer::Renderer::postDrainAudioQueue() {
161 if (mDrainAudioQueuePending || mSyncQueues) {
162 return;
163 }
164
165 if (mAudioQueue.empty()) {
166 return;
167 }
168
169 mDrainAudioQueuePending = true;
170 sp<AMessage> msg = new AMessage(kWhatDrainAudioQueue, id());
171 msg->setInt32("generation", mAudioQueueGeneration);
172 msg->post(10000);
173}
174
Andreas Huber3831a062010-12-21 10:22:33 -0800175void NuPlayer::Renderer::signalAudioSinkChanged() {
176 (new AMessage(kWhatAudioSinkChanged, id()))->post();
177}
178
Andreas Huberf9334412010-12-15 15:17:42 -0800179void NuPlayer::Renderer::onDrainAudioQueue() {
180 uint32_t numFramesPlayed;
181 CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
182
183 ssize_t numFramesAvailableToWrite =
184 mAudioSink->frameCount() - (mNumFramesWritten - numFramesPlayed);
185
186 CHECK_GE(numFramesAvailableToWrite, 0);
187
188 size_t numBytesAvailableToWrite =
189 numFramesAvailableToWrite * mAudioSink->frameSize();
190
191 while (numBytesAvailableToWrite > 0) {
192 if (mAudioQueue.empty()) {
193 break;
194 }
195
196 QueueEntry *entry = &*mAudioQueue.begin();
197
198 if (entry->mBuffer == NULL) {
199 // EOS
200
201 notifyEOS(true /* audio */);
202
203 mAudioQueue.erase(mAudioQueue.begin());
204 entry = NULL;
205 return;
206 }
207
208 if (entry->mOffset == 0) {
209 int64_t mediaTimeUs;
210 CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
211
212 LOGV("rendering audio at media time %.2f secs", mediaTimeUs / 1E6);
213
214 mAnchorTimeMediaUs = mediaTimeUs;
215
216 uint32_t numFramesPlayed;
217 CHECK_EQ(mAudioSink->getPosition(&numFramesPlayed), (status_t)OK);
218
219 uint32_t numFramesPendingPlayout =
220 mNumFramesWritten - numFramesPlayed;
221
222 int64_t realTimeOffsetUs =
223 (mAudioSink->latency() / 2 /* XXX */
224 + numFramesPendingPlayout
225 * mAudioSink->msecsPerFrame()) * 1000ll;
226
227 // LOGI("realTimeOffsetUs = %lld us", realTimeOffsetUs);
228
229 mAnchorTimeRealUs =
230 ALooper::GetNowUs() + realTimeOffsetUs;
231 }
232
233 size_t copy = entry->mBuffer->size() - entry->mOffset;
234 if (copy > numBytesAvailableToWrite) {
235 copy = numBytesAvailableToWrite;
236 }
237
238 CHECK_EQ(mAudioSink->write(
239 entry->mBuffer->data() + entry->mOffset, copy),
240 (ssize_t)copy);
241
242 entry->mOffset += copy;
243 if (entry->mOffset == entry->mBuffer->size()) {
244 entry->mNotifyConsumed->post();
245 mAudioQueue.erase(mAudioQueue.begin());
246 entry = NULL;
247 }
248
249 numBytesAvailableToWrite -= copy;
250 mNumFramesWritten += copy / mAudioSink->frameSize();
251 }
252}
253
254void NuPlayer::Renderer::postDrainVideoQueue() {
255 if (mDrainVideoQueuePending || mSyncQueues) {
256 return;
257 }
258
259 if (mVideoQueue.empty()) {
260 return;
261 }
262
263 QueueEntry &entry = *mVideoQueue.begin();
264
265 sp<AMessage> msg = new AMessage(kWhatDrainVideoQueue, id());
266 msg->setInt32("generation", mVideoQueueGeneration);
267
268 int64_t delayUs;
269
270 if (entry.mBuffer == NULL) {
271 // EOS doesn't carry a timestamp.
272 delayUs = 0;
273 } else {
274 int64_t mediaTimeUs;
275 CHECK(entry.mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
276
277 if (mAnchorTimeMediaUs < 0) {
278 delayUs = 0;
279
Andreas Huber3831a062010-12-21 10:22:33 -0800280 if (!mHasAudio) {
Andreas Huberf9334412010-12-15 15:17:42 -0800281 mAnchorTimeMediaUs = mediaTimeUs;
282 mAnchorTimeRealUs = ALooper::GetNowUs();
283 }
284 } else {
285 int64_t realTimeUs =
286 (mediaTimeUs - mAnchorTimeMediaUs) + mAnchorTimeRealUs;
287
288 delayUs = realTimeUs - ALooper::GetNowUs();
289 }
290 }
291
292 msg->post(delayUs);
293
294 mDrainVideoQueuePending = true;
295}
296
297void NuPlayer::Renderer::onDrainVideoQueue() {
298 if (mVideoQueue.empty()) {
299 return;
300 }
301
302 QueueEntry *entry = &*mVideoQueue.begin();
303
304 if (entry->mBuffer == NULL) {
305 // EOS
306
307 notifyEOS(false /* audio */);
308
309 mVideoQueue.erase(mVideoQueue.begin());
310 entry = NULL;
311 return;
312 }
313
314#if 0
315 int64_t mediaTimeUs;
316 CHECK(entry->mBuffer->meta()->findInt64("timeUs", &mediaTimeUs));
317
318 LOGI("rendering video at media time %.2f secs", mediaTimeUs / 1E6);
319#endif
320
321 entry->mNotifyConsumed->setInt32("render", true);
322 entry->mNotifyConsumed->post();
323 mVideoQueue.erase(mVideoQueue.begin());
324 entry = NULL;
325}
326
327void NuPlayer::Renderer::notifyEOS(bool audio) {
328 sp<AMessage> notify = mNotify->dup();
329 notify->setInt32("what", kWhatEOS);
330 notify->setInt32("audio", static_cast<int32_t>(audio));
331 notify->post();
332}
333
334void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
335 int32_t audio;
336 CHECK(msg->findInt32("audio", &audio));
337
338 if (dropBufferWhileFlushing(audio, msg)) {
339 return;
340 }
341
342 sp<RefBase> obj;
343 CHECK(msg->findObject("buffer", &obj));
344 sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
345
346 sp<AMessage> notifyConsumed;
347 CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
348
349 QueueEntry entry;
350 entry.mBuffer = buffer;
351 entry.mNotifyConsumed = notifyConsumed;
352 entry.mOffset = 0;
353 entry.mFinalResult = OK;
354
355 if (audio) {
356 mAudioQueue.push_back(entry);
357 postDrainAudioQueue();
358 } else {
359 mVideoQueue.push_back(entry);
360 postDrainVideoQueue();
361 }
362
363 if (mSyncQueues && !mAudioQueue.empty() && !mVideoQueue.empty()) {
364 int64_t firstAudioTimeUs;
365 int64_t firstVideoTimeUs;
366 CHECK((*mAudioQueue.begin()).mBuffer->meta()
367 ->findInt64("timeUs", &firstAudioTimeUs));
368 CHECK((*mVideoQueue.begin()).mBuffer->meta()
369 ->findInt64("timeUs", &firstVideoTimeUs));
370
371 int64_t diff = firstVideoTimeUs - firstAudioTimeUs;
372
373 LOGV("queueDiff = %.2f secs", diff / 1E6);
374
375 if (diff > 100000ll) {
376 // Audio data starts More than 0.1 secs before video.
377 // Drop some audio.
378
379 (*mAudioQueue.begin()).mNotifyConsumed->post();
380 mAudioQueue.erase(mAudioQueue.begin());
381 return;
382 }
383
384 syncQueuesDone();
385 }
386}
387
388void NuPlayer::Renderer::syncQueuesDone() {
389 if (!mSyncQueues) {
390 return;
391 }
392
393 mSyncQueues = false;
394
395 if (!mAudioQueue.empty()) {
396 postDrainAudioQueue();
397 }
398
399 if (!mVideoQueue.empty()) {
400 postDrainVideoQueue();
401 }
402}
403
404void NuPlayer::Renderer::onQueueEOS(const sp<AMessage> &msg) {
405 int32_t audio;
406 CHECK(msg->findInt32("audio", &audio));
407
408 if (dropBufferWhileFlushing(audio, msg)) {
409 return;
410 }
411
412 int32_t finalResult;
413 CHECK(msg->findInt32("finalResult", &finalResult));
414
415 QueueEntry entry;
416 entry.mOffset = 0;
417 entry.mFinalResult = finalResult;
418
419 if (audio) {
420 mAudioQueue.push_back(entry);
421 postDrainAudioQueue();
422 } else {
423 mVideoQueue.push_back(entry);
424 postDrainVideoQueue();
425 }
426}
427
428void NuPlayer::Renderer::onFlush(const sp<AMessage> &msg) {
429 int32_t audio;
430 CHECK(msg->findInt32("audio", &audio));
431
432 // If we're currently syncing the queues, i.e. dropping audio while
433 // aligning the first audio/video buffer times and only one of the
434 // two queues has data, we may starve that queue by not requesting
435 // more buffers from the decoder. If the other source then encounters
436 // a discontinuity that leads to flushing, we'll never find the
437 // corresponding discontinuity on the other queue.
438 // Therefore we'll stop syncing the queues if at least one of them
439 // is flushed.
440 syncQueuesDone();
441
442 if (audio) {
443 flushQueue(&mAudioQueue);
444
445 Mutex::Autolock autoLock(mFlushLock);
446 mFlushingAudio = false;
447
448 mDrainAudioQueuePending = false;
449 ++mAudioQueueGeneration;
450 } else {
451 flushQueue(&mVideoQueue);
452
453 Mutex::Autolock autoLock(mFlushLock);
454 mFlushingVideo = false;
455
456 mDrainVideoQueuePending = false;
457 ++mVideoQueueGeneration;
458 }
459
460 notifyFlushComplete(audio);
461}
462
463void NuPlayer::Renderer::flushQueue(List<QueueEntry> *queue) {
464 while (!queue->empty()) {
465 QueueEntry *entry = &*queue->begin();
466
467 if (entry->mBuffer != NULL) {
468 entry->mNotifyConsumed->post();
469 }
470
471 queue->erase(queue->begin());
472 entry = NULL;
473 }
474}
475
476void NuPlayer::Renderer::notifyFlushComplete(bool audio) {
477 sp<AMessage> notify = mNotify->dup();
478 notify->setInt32("what", kWhatFlushComplete);
479 notify->setInt32("audio", static_cast<int32_t>(audio));
480 notify->post();
481}
482
483bool NuPlayer::Renderer::dropBufferWhileFlushing(
484 bool audio, const sp<AMessage> &msg) {
485 bool flushing = false;
486
487 {
488 Mutex::Autolock autoLock(mFlushLock);
489 if (audio) {
490 flushing = mFlushingAudio;
491 } else {
492 flushing = mFlushingVideo;
493 }
494 }
495
496 if (!flushing) {
497 return false;
498 }
499
500 sp<AMessage> notifyConsumed;
501 if (msg->findMessage("notifyConsumed", &notifyConsumed)) {
502 notifyConsumed->post();
503 }
504
505 return true;
506}
507
Andreas Huber3831a062010-12-21 10:22:33 -0800508void NuPlayer::Renderer::onAudioSinkChanged() {
509 CHECK(!mDrainAudioQueuePending);
510 mNumFramesWritten = 0;
511}
512
Andreas Huberf9334412010-12-15 15:17:42 -0800513} // namespace android
514