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