diff options
Diffstat (limited to 'media/libmediaplayerservice/nuplayer/NuPlayer.cpp')
-rw-r--r-- | media/libmediaplayerservice/nuplayer/NuPlayer.cpp | 1194 |
1 files changed, 901 insertions, 293 deletions
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp index 25d55a3..df3e992 100644 --- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp +++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp @@ -22,24 +22,22 @@ #include "HTTPLiveSource.h" #include "NuPlayerDecoder.h" +#include "NuPlayerDecoderPassThrough.h" #include "NuPlayerDriver.h" #include "NuPlayerRenderer.h" #include "NuPlayerSource.h" #include "RTSPSource.h" #include "StreamingSource.h" #include "GenericSource.h" -#include "mp4/MP4Source.h" +#include "TextDescriptions.h" #include "ATSParser.h" -#include "SoftwareRenderer.h" - -#include <cutils/properties.h> // for property_get #include <media/stagefright/foundation/hexdump.h> #include <media/stagefright/foundation/ABuffer.h> #include <media/stagefright/foundation/ADebug.h> #include <media/stagefright/foundation/AMessage.h> -#include <media/stagefright/ACodec.h> +#include <media/stagefright/MediaBuffer.h> #include <media/stagefright/MediaDefs.h> #include <media/stagefright/MediaErrors.h> #include <media/stagefright/MetaData.h> @@ -147,13 +145,18 @@ private: NuPlayer::NuPlayer() : mUIDValid(false), mSourceFlags(0), + mCurrentPositionUs(0), mVideoIsAVC(false), - mNeedsSwRenderer(false), + mOffloadAudio(false), + mCurrentOffloadInfo(AUDIO_INFO_INITIALIZER), + mAudioDecoderGeneration(0), + mVideoDecoderGeneration(0), mAudioEOS(false), mVideoEOS(false), mScanSourcesPending(false), mScanSourcesGeneration(0), mPollDurationGeneration(0), + mTimedTextGeneration(0), mTimeDiscontinuityPending(false), mFlushingAudio(NONE), mFlushingVideo(NONE), @@ -183,14 +186,7 @@ void NuPlayer::setDataSourceAsync(const sp<IStreamSource> &source) { sp<AMessage> notify = new AMessage(kWhatSourceNotify, id()); - char prop[PROPERTY_VALUE_MAX]; - if (property_get("media.stagefright.use-mp4source", prop, NULL) - && (!strcmp(prop, "1") || !strcasecmp(prop, "true"))) { - msg->setObject("source", new MP4Source(notify, source)); - } else { - msg->setObject("source", new StreamingSource(notify, source)); - } - + msg->setObject("source", new StreamingSource(notify, source)); msg->post(); } @@ -212,7 +208,10 @@ static bool IsHTTPLiveURL(const char *url) { } void NuPlayer::setDataSourceAsync( - const char *url, const KeyedVector<String8, String8> *headers) { + const sp<IMediaHTTPService> &httpService, + const char *url, + const KeyedVector<String8, String8> *headers) { + sp<AMessage> msg = new AMessage(kWhatSetDataSource, id()); size_t len = strlen(url); @@ -220,18 +219,31 @@ void NuPlayer::setDataSourceAsync( sp<Source> source; if (IsHTTPLiveURL(url)) { - source = new HTTPLiveSource(notify, url, headers, mUIDValid, mUID); + source = new HTTPLiveSource(notify, httpService, url, headers); } else if (!strncasecmp(url, "rtsp://", 7)) { - source = new RTSPSource(notify, url, headers, mUIDValid, mUID); + source = new RTSPSource( + notify, httpService, url, headers, mUIDValid, mUID); } else if ((!strncasecmp(url, "http://", 7) || !strncasecmp(url, "https://", 8)) && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?"))) { - source = new RTSPSource(notify, url, headers, mUIDValid, mUID, true); + source = new RTSPSource( + notify, httpService, url, headers, mUIDValid, mUID, true); } else { - source = new GenericSource(notify, url, headers, mUIDValid, mUID); + sp<GenericSource> genericSource = + new GenericSource(notify, mUIDValid, mUID); + // Don't set FLAG_SECURE on mSourceFlags here for widevine. + // The correct flags will be updated in Source::kWhatFlagsChanged + // handler when GenericSource is prepared. + + status_t err = genericSource->setDataSource(httpService, url, headers); + + if (err == OK) { + source = genericSource; + } else { + ALOGE("Failed to set data source!"); + } } - msg->setObject("source", source); msg->post(); } @@ -241,7 +253,16 @@ void NuPlayer::setDataSourceAsync(int fd, int64_t offset, int64_t length) { sp<AMessage> notify = new AMessage(kWhatSourceNotify, id()); - sp<Source> source = new GenericSource(notify, fd, offset, length); + sp<GenericSource> source = + new GenericSource(notify, mUIDValid, mUID); + + status_t err = source->setDataSource(fd, offset, length); + + if (err != OK) { + ALOGE("Failed to set data source!"); + source = NULL; + } + msg->setObject("source", source); msg->post(); } @@ -260,7 +281,7 @@ void NuPlayer::setVideoSurfaceTextureAsync( msg->setObject( "native-window", new NativeWindowWrapper( - new Surface(bufferProducer))); + new Surface(bufferProducer, true /* controlledByApp */))); } msg->post(); @@ -314,6 +335,34 @@ bool NuPlayer::IsFlushingState(FlushStatus state, bool *needShutdown) { } } +void NuPlayer::writeTrackInfo( + Parcel* reply, const sp<AMessage> format) const { + int32_t trackType; + CHECK(format->findInt32("type", &trackType)); + + AString lang; + CHECK(format->findString("language", &lang)); + + reply->writeInt32(2); // write something non-zero + reply->writeInt32(trackType); + reply->writeString16(String16(lang.c_str())); + + if (trackType == MEDIA_TRACK_TYPE_SUBTITLE) { + AString mime; + CHECK(format->findString("mime", &mime)); + + int32_t isAuto, isDefault, isForced; + CHECK(format->findInt32("auto", &isAuto)); + CHECK(format->findInt32("default", &isDefault)); + CHECK(format->findInt32("forced", &isForced)); + + reply->writeString16(String16(mime.c_str())); + reply->writeInt32(isAuto); + reply->writeInt32(isDefault); + reply->writeInt32(isForced); + } +} + void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { switch (msg->what()) { case kWhatSetDataSource: @@ -322,17 +371,19 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { CHECK(mSource == NULL); + status_t err = OK; sp<RefBase> obj; CHECK(msg->findObject("source", &obj)); - - mSource = static_cast<Source *>(obj.get()); - - looper()->registerHandler(mSource); + if (obj != NULL) { + mSource = static_cast<Source *>(obj.get()); + } else { + err = UNKNOWN_ERROR; + } CHECK(mDriver != NULL); sp<NuPlayerDriver> driver = mDriver.promote(); if (driver != NULL) { - driver->notifySetDataSourceCompleted(OK); + driver->notifySetDataSourceCompleted(err); } break; } @@ -348,16 +399,58 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + Parcel* reply; + CHECK(msg->findPointer("reply", (void**)&reply)); + + size_t inbandTracks = 0; + if (mSource != NULL) { + inbandTracks = mSource->getTrackCount(); + } + + size_t ccTracks = 0; + if (mCCDecoder != NULL) { + ccTracks = mCCDecoder->getTrackCount(); + } + + // total track count + reply->writeInt32(inbandTracks + ccTracks); + + // write inband tracks + for (size_t i = 0; i < inbandTracks; ++i) { + writeTrackInfo(reply, mSource->getTrackInfo(i)); + } + + // write CC track + for (size_t i = 0; i < ccTracks; ++i) { + writeTrackInfo(reply, mCCDecoder->getTrackInfo(i)); + } + + sp<AMessage> response = new AMessage; + response->postReply(replyID); + break; + } + + case kWhatGetSelectedTrack: + { status_t err = INVALID_OPERATION; if (mSource != NULL) { + err = OK; + + int32_t type32; + CHECK(msg->findInt32("type", (int32_t*)&type32)); + media_track_type type = (media_track_type)type32; + ssize_t selectedTrack = mSource->getSelectedTrack(type); + Parcel* reply; CHECK(msg->findPointer("reply", (void**)&reply)); - err = mSource->getTrackInfo(reply); + reply->writeInt32(selectedTrack); } sp<AMessage> response = new AMessage; response->setInt32("err", err); + uint32_t replyID; + CHECK(msg->senderAwaitsResponse(&replyID)); response->postReply(replyID); break; } @@ -367,13 +460,40 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { uint32_t replyID; CHECK(msg->senderAwaitsResponse(&replyID)); + size_t trackIndex; + int32_t select; + CHECK(msg->findSize("trackIndex", &trackIndex)); + CHECK(msg->findInt32("select", &select)); + status_t err = INVALID_OPERATION; + + size_t inbandTracks = 0; if (mSource != NULL) { - size_t trackIndex; - int32_t select; - CHECK(msg->findSize("trackIndex", &trackIndex)); - CHECK(msg->findInt32("select", &select)); + inbandTracks = mSource->getTrackCount(); + } + size_t ccTracks = 0; + if (mCCDecoder != NULL) { + ccTracks = mCCDecoder->getTrackCount(); + } + + if (trackIndex < inbandTracks) { err = mSource->selectTrack(trackIndex, select); + + if (!select && err == OK) { + int32_t type; + sp<AMessage> info = mSource->getTrackInfo(trackIndex); + if (info != NULL + && info->findInt32("type", &type) + && type == MEDIA_TRACK_TYPE_TIMEDTEXT) { + ++mTimedTextGeneration; + } + } + } else { + trackIndex -= inbandTracks; + + if (trackIndex < ccTracks) { + err = mCCDecoder->selectTrack(trackIndex, select); + } } sp<AMessage> response = new AMessage; @@ -421,6 +541,14 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { static_cast<NativeWindowWrapper *>(obj.get()))); if (obj != NULL) { + if (mStarted && mVideoDecoder != NULL) { + // Issue a seek to refresh the video screen only if started otherwise + // the extractor may not yet be started and will assert. + // If the video decoder is not set (perhaps audio only in this case) + // do not perform a seek as it is not needed. + mDeferredActions.push_back(new SeekAction(mCurrentPositionUs)); + } + // If there is a new surface texture, instantiate decoders // again if possible. mDeferredActions.push_back( @@ -447,7 +575,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("kWhatStart"); mVideoIsAVC = false; - mNeedsSwRenderer = false; + mOffloadAudio = false; mAudioEOS = false; mVideoEOS = false; mSkipRenderingAudioUntilMediaTimeUs = -1; @@ -457,6 +585,17 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { mNumFramesDropped = 0; mStarted = true; + /* instantiate decoders now for secure playback */ + if (mSourceFlags & Source::FLAG_SECURE) { + if (mNativeWindow != NULL) { + instantiateDecoder(false, &mVideoDecoder); + } + + if (mAudioSink != NULL) { + instantiateDecoder(true, &mAudioDecoder); + } + } + mSource->start(); uint32_t flags = 0; @@ -465,12 +604,30 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { flags |= Renderer::FLAG_REAL_TIME; } + sp<MetaData> audioMeta = mSource->getFormatMeta(true /* audio */); + audio_stream_type_t streamType = AUDIO_STREAM_MUSIC; + if (mAudioSink != NULL) { + streamType = mAudioSink->getAudioStreamType(); + } + + sp<AMessage> videoFormat = mSource->getFormat(false /* audio */); + + mOffloadAudio = + canOffloadStream(audioMeta, (videoFormat != NULL), + true /* is_streaming */, streamType); + if (mOffloadAudio) { + flags |= Renderer::FLAG_OFFLOAD_AUDIO; + } + mRenderer = new Renderer( mAudioSink, new AMessage(kWhatRendererNotify, id()), flags); - looper()->registerHandler(mRenderer); + mRendererLooper = new ALooper; + mRendererLooper->setName("NuPlayerRenderer"); + mRendererLooper->start(false, false, ANDROID_PRIORITY_AUDIO); + mRendererLooper->registerHandler(mRenderer); postScanSources(); break; @@ -493,11 +650,18 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { bool mHadAnySourcesBefore = (mAudioDecoder != NULL) || (mVideoDecoder != NULL); + // initialize video before audio because successful initialization of + // video may change deep buffer mode of audio. if (mNativeWindow != NULL) { instantiateDecoder(false, &mVideoDecoder); } if (mAudioSink != NULL) { + if (mOffloadAudio) { + // open audio sink early under offload mode. + sp<AMessage> format = mSource->getFormat(true /*audio*/); + openAudioSink(format, true /*offloadOnly*/); + } instantiateDecoder(true, &mAudioDecoder); } @@ -538,24 +702,40 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { { bool audio = msg->what() == kWhatAudioNotify; - sp<AMessage> codecRequest; - CHECK(msg->findMessage("codec-request", &codecRequest)); + int32_t currentDecoderGeneration = + (audio? mAudioDecoderGeneration : mVideoDecoderGeneration); + int32_t requesterGeneration = currentDecoderGeneration - 1; + CHECK(msg->findInt32("generation", &requesterGeneration)); + + if (requesterGeneration != currentDecoderGeneration) { + ALOGV("got message from old %s decoder, generation(%d:%d)", + audio ? "audio" : "video", requesterGeneration, + currentDecoderGeneration); + sp<AMessage> reply; + if (!(msg->findMessage("reply", &reply))) { + return; + } + + reply->setInt32("err", INFO_DISCONTINUITY); + reply->post(); + return; + } int32_t what; - CHECK(codecRequest->findInt32("what", &what)); + CHECK(msg->findInt32("what", &what)); - if (what == ACodec::kWhatFillThisBuffer) { + if (what == Decoder::kWhatFillThisBuffer) { status_t err = feedDecoderInputData( - audio, codecRequest); + audio, msg); if (err == -EWOULDBLOCK) { if (mSource->feedMoreTSData() == OK) { msg->post(10000ll); } } - } else if (what == ACodec::kWhatEOS) { + } else if (what == Decoder::kWhatEOS) { int32_t err; - CHECK(codecRequest->findInt32("err", &err)); + CHECK(msg->findInt32("err", &err)); if (err == ERROR_END_OF_STREAM) { ALOGV("got %s decoder EOS", audio ? "audio" : "video"); @@ -566,7 +746,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } mRenderer->queueEOS(audio, err); - } else if (what == ACodec::kWhatFlushCompleted) { + } else if (what == Decoder::kWhatFlushCompleted) { bool needShutdown; if (audio) { @@ -585,7 +765,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { ALOGV("initiating %s decoder shutdown", audio ? "audio" : "video"); - (audio ? mAudioDecoder : mVideoDecoder)->initiateShutdown(); + getDecoder(audio)->initiateShutdown(); if (audio) { mFlushingAudio = SHUTTING_DOWN_DECODER; @@ -595,111 +775,20 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } finishFlushIfPossible(); - } else if (what == ACodec::kWhatOutputFormatChanged) { - if (audio) { - int32_t numChannels; - CHECK(codecRequest->findInt32( - "channel-count", &numChannels)); - - int32_t sampleRate; - CHECK(codecRequest->findInt32("sample-rate", &sampleRate)); - - ALOGV("Audio output format changed to %d Hz, %d channels", - sampleRate, numChannels); - - mAudioSink->close(); - - audio_output_flags_t flags; - int64_t durationUs; - // FIXME: we should handle the case where the video decoder - // is created after we receive the format change indication. - // Current code will just make that we select deep buffer - // with video which should not be a problem as it should - // not prevent from keeping A/V sync. - if (mVideoDecoder == NULL && - mSource->getDuration(&durationUs) == OK && - durationUs - > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { - flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; - } else { - flags = AUDIO_OUTPUT_FLAG_NONE; - } - - int32_t channelMask; - if (!codecRequest->findInt32("channel-mask", &channelMask)) { - channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; - } + } else if (what == Decoder::kWhatOutputFormatChanged) { + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); - CHECK_EQ(mAudioSink->open( - sampleRate, - numChannels, - (audio_channel_mask_t)channelMask, - AUDIO_FORMAT_PCM_16_BIT, - 8 /* bufferCount */, - NULL, - NULL, - flags), - (status_t)OK); - mAudioSink->start(); - - mRenderer->signalAudioSinkChanged(); + if (audio) { + openAudioSink(format, false /*offloadOnly*/); } else { // video + sp<AMessage> inputFormat = + mSource->getFormat(false /* audio */); - int32_t width, height; - CHECK(codecRequest->findInt32("width", &width)); - CHECK(codecRequest->findInt32("height", &height)); - - int32_t cropLeft, cropTop, cropRight, cropBottom; - CHECK(codecRequest->findRect( - "crop", - &cropLeft, &cropTop, &cropRight, &cropBottom)); - - int32_t displayWidth = cropRight - cropLeft + 1; - int32_t displayHeight = cropBottom - cropTop + 1; - - ALOGV("Video output format changed to %d x %d " - "(crop: %d x %d @ (%d, %d))", - width, height, - displayWidth, - displayHeight, - cropLeft, cropTop); - - sp<AMessage> videoInputFormat = - mSource->getFormat(false /* audio */); - - // Take into account sample aspect ratio if necessary: - int32_t sarWidth, sarHeight; - if (videoInputFormat->findInt32("sar-width", &sarWidth) - && videoInputFormat->findInt32( - "sar-height", &sarHeight)) { - ALOGV("Sample aspect ratio %d : %d", - sarWidth, sarHeight); - - displayWidth = (displayWidth * sarWidth) / sarHeight; - - ALOGV("display dimensions %d x %d", - displayWidth, displayHeight); - } - - notifyListener( - MEDIA_SET_VIDEO_SIZE, displayWidth, displayHeight); - - if (mNeedsSwRenderer && mNativeWindow != NULL) { - int32_t colorFormat; - CHECK(codecRequest->findInt32("color-format", &colorFormat)); - - sp<MetaData> meta = new MetaData; - meta->setInt32(kKeyWidth, width); - meta->setInt32(kKeyHeight, height); - meta->setRect(kKeyCropRect, cropLeft, cropTop, cropRight, cropBottom); - meta->setInt32(kKeyColorFormat, colorFormat); - - mRenderer->setSoftRenderer( - new SoftwareRenderer(mNativeWindow->getNativeWindow(), meta)); - } + updateVideoSize(inputFormat, format); } - } else if (what == ACodec::kWhatShutdownCompleted) { + } else if (what == Decoder::kWhatShutdownCompleted) { ALOGV("%s shutdown completed", audio ? "audio" : "video"); if (audio) { mAudioDecoder.clear(); @@ -714,22 +803,27 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } finishFlushIfPossible(); - } else if (what == ACodec::kWhatError) { + } else if (what == Decoder::kWhatError) { ALOGE("Received error from %s decoder, aborting playback.", audio ? "audio" : "video"); - mRenderer->queueEOS(audio, UNKNOWN_ERROR); - } else if (what == ACodec::kWhatDrainThisBuffer) { - renderBuffer(audio, codecRequest); - } else if (what == ACodec::kWhatComponentAllocated) { - if (!audio) { - AString name; - CHECK(codecRequest->findString("componentName", &name)); - mNeedsSwRenderer = name.startsWith("OMX.google."); + status_t err; + if (!msg->findInt32("err", &err)) { + err = UNKNOWN_ERROR; + } + mRenderer->queueEOS(audio, err); + if (audio && mFlushingAudio != NONE) { + mAudioDecoder.clear(); + mFlushingAudio = SHUT_DOWN; + } else if (!audio && mFlushingVideo != NONE){ + mVideoDecoder.clear(); + mFlushingVideo = SHUT_DOWN; } - } else if (what != ACodec::kWhatComponentConfigured - && what != ACodec::kWhatBuffersAllocated) { - ALOGV("Unhandled codec notification %d '%c%c%c%c'.", + finishFlushIfPossible(); + } else if (what == Decoder::kWhatDrainThisBuffer) { + renderBuffer(audio, msg); + } else { + ALOGV("Unhandled decoder notification %d '%c%c%c%c'.", what, what >> 24, (what >> 16) & 0xff, @@ -775,6 +869,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } else if (what == Renderer::kWhatPosition) { int64_t positionUs; CHECK(msg->findInt64("positionUs", &positionUs)); + mCurrentPositionUs = positionUs; CHECK(msg->findInt64("videoLateByUs", &mVideoLateByUs)); @@ -797,6 +892,21 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } else if (what == Renderer::kWhatMediaRenderingStart) { ALOGV("media rendering started"); notifyListener(MEDIA_STARTED, 0, 0); + } else if (what == Renderer::kWhatAudioOffloadTearDown) { + ALOGV("Tear down audio offload, fall back to s/w path"); + int64_t positionUs; + CHECK(msg->findInt64("positionUs", &positionUs)); + closeAudioSink(); + mAudioDecoder.clear(); + mRenderer->flush(true /* audio */); + if (mVideoDecoder != NULL) { + mRenderer->flush(false /* audio */); + } + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; + + performSeek(positionUs); + instantiateDecoder(true /* audio */, &mAudioDecoder); } break; } @@ -859,6 +969,12 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { break; } + case kWhatClosedCaptionNotify: + { + onClosedCaptionNotify(msg); + break; + } + default: TRESPASS(); break; @@ -866,26 +982,30 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) { } void NuPlayer::finishFlushIfPossible() { - if (mFlushingAudio != FLUSHED && mFlushingAudio != SHUT_DOWN) { + if (mFlushingAudio != NONE && mFlushingAudio != FLUSHED + && mFlushingAudio != SHUT_DOWN) { return; } - if (mFlushingVideo != FLUSHED && mFlushingVideo != SHUT_DOWN) { + if (mFlushingVideo != NONE && mFlushingVideo != FLUSHED + && mFlushingVideo != SHUT_DOWN) { return; } ALOGV("both audio and video are flushed now."); + mPendingAudioAccessUnit.clear(); + if (mTimeDiscontinuityPending) { mRenderer->signalTimeDiscontinuity(); mTimeDiscontinuityPending = false; } - if (mAudioDecoder != NULL) { + if (mAudioDecoder != NULL && mFlushingAudio == FLUSHED) { mAudioDecoder->signalResume(); } - if (mVideoDecoder != NULL) { + if (mVideoDecoder != NULL && mFlushingVideo == FLUSHED) { mVideoDecoder->signalResume(); } @@ -907,6 +1027,149 @@ void NuPlayer::postScanSources() { mScanSourcesPending = true; } +void NuPlayer::openAudioSink(const sp<AMessage> &format, bool offloadOnly) { + ALOGV("openAudioSink: offloadOnly(%d) mOffloadAudio(%d)", + offloadOnly, mOffloadAudio); + bool audioSinkChanged = false; + + int32_t numChannels; + CHECK(format->findInt32("channel-count", &numChannels)); + + int32_t channelMask; + if (!format->findInt32("channel-mask", &channelMask)) { + // signal to the AudioSink to derive the mask from count. + channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER; + } + + int32_t sampleRate; + CHECK(format->findInt32("sample-rate", &sampleRate)); + + uint32_t flags; + int64_t durationUs; + // FIXME: we should handle the case where the video decoder + // is created after we receive the format change indication. + // Current code will just make that we select deep buffer + // with video which should not be a problem as it should + // not prevent from keeping A/V sync. + if (mVideoDecoder == NULL && + mSource->getDuration(&durationUs) == OK && + durationUs + > AUDIO_SINK_MIN_DEEP_BUFFER_DURATION_US) { + flags = AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + } else { + flags = AUDIO_OUTPUT_FLAG_NONE; + } + + if (mOffloadAudio) { + audio_format_t audioFormat = AUDIO_FORMAT_PCM_16_BIT; + AString mime; + CHECK(format->findString("mime", &mime)); + status_t err = mapMimeToAudioFormat(audioFormat, mime.c_str()); + + if (err != OK) { + ALOGE("Couldn't map mime \"%s\" to a valid " + "audio_format", mime.c_str()); + mOffloadAudio = false; + } else { + ALOGV("Mime \"%s\" mapped to audio_format 0x%x", + mime.c_str(), audioFormat); + + int avgBitRate = -1; + format->findInt32("bit-rate", &avgBitRate); + + int32_t aacProfile = -1; + if (audioFormat == AUDIO_FORMAT_AAC + && format->findInt32("aac-profile", &aacProfile)) { + // Redefine AAC format as per aac profile + mapAACProfileToAudioFormat( + audioFormat, + aacProfile); + } + + audio_offload_info_t offloadInfo = AUDIO_INFO_INITIALIZER; + offloadInfo.duration_us = -1; + format->findInt64( + "durationUs", &offloadInfo.duration_us); + offloadInfo.sample_rate = sampleRate; + offloadInfo.channel_mask = channelMask; + offloadInfo.format = audioFormat; + offloadInfo.stream_type = AUDIO_STREAM_MUSIC; + offloadInfo.bit_rate = avgBitRate; + offloadInfo.has_video = (mVideoDecoder != NULL); + offloadInfo.is_streaming = true; + + if (memcmp(&mCurrentOffloadInfo, &offloadInfo, sizeof(offloadInfo)) == 0) { + ALOGV("openAudioSink: no change in offload mode"); + return; // no change from previous configuration, everything ok. + } + ALOGV("openAudioSink: try to open AudioSink in offload mode"); + flags |= AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + flags &= ~AUDIO_OUTPUT_FLAG_DEEP_BUFFER; + audioSinkChanged = true; + mAudioSink->close(); + err = mAudioSink->open( + sampleRate, + numChannels, + (audio_channel_mask_t)channelMask, + audioFormat, + 8 /* bufferCount */, + &NuPlayer::Renderer::AudioSinkCallback, + mRenderer.get(), + (audio_output_flags_t)flags, + &offloadInfo); + + if (err == OK) { + // If the playback is offloaded to h/w, we pass + // the HAL some metadata information. + // We don't want to do this for PCM because it + // will be going through the AudioFlinger mixer + // before reaching the hardware. + sp<MetaData> audioMeta = + mSource->getFormatMeta(true /* audio */); + sendMetaDataToHal(mAudioSink, audioMeta); + mCurrentOffloadInfo = offloadInfo; + err = mAudioSink->start(); + ALOGV_IF(err == OK, "openAudioSink: offload succeeded"); + } + if (err != OK) { + // Clean up, fall back to non offload mode. + mAudioSink->close(); + mRenderer->signalDisableOffloadAudio(); + mOffloadAudio = false; + mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + ALOGV("openAudioSink: offload failed"); + } + } + } + if (!offloadOnly && !mOffloadAudio) { + flags &= ~AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD; + ALOGV("openAudioSink: open AudioSink in NON-offload mode"); + + audioSinkChanged = true; + mAudioSink->close(); + mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; + CHECK_EQ(mAudioSink->open( + sampleRate, + numChannels, + (audio_channel_mask_t)channelMask, + AUDIO_FORMAT_PCM_16_BIT, + 8 /* bufferCount */, + NULL, + NULL, + (audio_output_flags_t)flags), + (status_t)OK); + mAudioSink->start(); + } + if (audioSinkChanged) { + mRenderer->signalAudioSinkChanged(); + } +} + +void NuPlayer::closeAudioSink() { + mAudioSink->close(); + mCurrentOffloadInfo = AUDIO_INFO_INITIALIZER; +} + status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { if (*decoder != NULL) { return OK; @@ -922,18 +1185,57 @@ status_t NuPlayer::instantiateDecoder(bool audio, sp<Decoder> *decoder) { AString mime; CHECK(format->findString("mime", &mime)); mVideoIsAVC = !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str()); - } - sp<AMessage> notify = - new AMessage(audio ? kWhatAudioNotify : kWhatVideoNotify, - id()); + sp<AMessage> ccNotify = new AMessage(kWhatClosedCaptionNotify, id()); + mCCDecoder = new CCDecoder(ccNotify); - *decoder = audio ? new Decoder(notify) : - new Decoder(notify, mNativeWindow); - looper()->registerHandler(*decoder); + if (mSourceFlags & Source::FLAG_SECURE) { + format->setInt32("secure", true); + } + } + + if (audio) { + sp<AMessage> notify = new AMessage(kWhatAudioNotify, id()); + ++mAudioDecoderGeneration; + notify->setInt32("generation", mAudioDecoderGeneration); + + if (mOffloadAudio) { + *decoder = new DecoderPassThrough(notify); + } else { + *decoder = new Decoder(notify); + } + } else { + sp<AMessage> notify = new AMessage(kWhatVideoNotify, id()); + ++mVideoDecoderGeneration; + notify->setInt32("generation", mVideoDecoderGeneration); + *decoder = new Decoder(notify, mNativeWindow); + } + (*decoder)->init(); (*decoder)->configure(format); + // allocate buffers to decrypt widevine source buffers + if (!audio && (mSourceFlags & Source::FLAG_SECURE)) { + Vector<sp<ABuffer> > inputBufs; + CHECK_EQ((*decoder)->getInputBuffers(&inputBufs), (status_t)OK); + + Vector<MediaBuffer *> mediaBufs; + for (size_t i = 0; i < inputBufs.size(); i++) { + const sp<ABuffer> &buffer = inputBufs[i]; + MediaBuffer *mbuf = new MediaBuffer(buffer->data(), buffer->size()); + mediaBufs.push(mbuf); + } + + status_t err = mSource->setBuffers(audio, mediaBufs); + if (err != OK) { + for (size_t i = 0; i < mediaBufs.size(); ++i) { + mediaBufs[i]->release(); + } + mediaBufs.clear(); + ALOGE("Secure source didn't support secure mediaBufs."); + return err; + } + } return OK; } @@ -941,8 +1243,9 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { sp<AMessage> reply; CHECK(msg->findMessage("reply", &reply)); - if ((audio && IsFlushingState(mFlushingAudio)) - || (!audio && IsFlushingState(mFlushingVideo))) { + if ((audio && mFlushingAudio != NONE) + || (!audio && mFlushingVideo != NONE) + || mSource == NULL) { reply->setInt32("err", INFO_DISCONTINUITY); reply->post(); return OK; @@ -950,14 +1253,47 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { sp<ABuffer> accessUnit; + // Aggregate smaller buffers into a larger buffer. + // The goal is to reduce power consumption. + // Unfortunately this does not work with the software AAC decoder. + // TODO optimize buffer size for power consumption + // The offload read buffer size is 32 KB but 24 KB uses less power. + const int kAudioBigBufferSizeBytes = 24 * 1024; + bool doBufferAggregation = (audio && mOffloadAudio); + sp<ABuffer> biggerBuffer; + bool needMoreData = false; + int numSmallBuffers = 0; + bool gotTime = false; + bool dropAccessUnit; do { - status_t err = mSource->dequeueAccessUnit(audio, &accessUnit); + status_t err; + // Did we save an accessUnit earlier because of a discontinuity? + if (audio && (mPendingAudioAccessUnit != NULL)) { + accessUnit = mPendingAudioAccessUnit; + mPendingAudioAccessUnit.clear(); + err = mPendingAudioErr; + ALOGV("feedDecoderInputData() use mPendingAudioAccessUnit"); + } else { + err = mSource->dequeueAccessUnit(audio, &accessUnit); + } if (err == -EWOULDBLOCK) { - return err; + if (biggerBuffer == NULL) { + return err; + } else { + break; // Reply with data that we already have. + } } else if (err != OK) { if (err == INFO_DISCONTINUITY) { + if (biggerBuffer != NULL) { + // We already have some data so save this for later. + mPendingAudioErr = err; + mPendingAudioAccessUnit = accessUnit; + accessUnit.clear(); + ALOGD("feedDecoderInputData() save discontinuity for later"); + break; + } int32_t type; CHECK(accessUnit->meta()->findInt32("discontinuity", &type)); @@ -1002,34 +1338,44 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mTimeDiscontinuityPending = mTimeDiscontinuityPending || timeChange; - if (formatChange || timeChange) { - if (mFlushingAudio == NONE && mFlushingVideo == NONE) { - // And we'll resume scanning sources once we're done - // flushing. - mDeferredActions.push_front( - new SimpleAction( - &NuPlayer::performScanSources)); - } + bool seamlessFormatChange = false; + sp<AMessage> newFormat = mSource->getFormat(audio); + if (formatChange) { + seamlessFormatChange = + getDecoder(audio)->supportsSeamlessFormatChange(newFormat); + // treat seamless format change separately + formatChange = !seamlessFormatChange; + } + bool shutdownOrFlush = formatChange || timeChange; + + // We want to queue up scan-sources only once per discontinuity. + // We control this by doing it only if neither audio nor video are + // flushing or shutting down. (After handling 1st discontinuity, one + // of the flushing states will not be NONE.) + // No need to scan sources if this discontinuity does not result + // in a flush or shutdown, as the flushing state will stay NONE. + if (mFlushingAudio == NONE && mFlushingVideo == NONE && + shutdownOrFlush) { + // And we'll resume scanning sources once we're done + // flushing. + mDeferredActions.push_front( + new SimpleAction( + &NuPlayer::performScanSources)); + } - sp<AMessage> newFormat = mSource->getFormat(audio); - sp<Decoder> &decoder = audio ? mAudioDecoder : mVideoDecoder; - if (formatChange && !decoder->supportsSeamlessFormatChange(newFormat)) { - flushDecoder(audio, /* needShutdown = */ true); - } else { - flushDecoder(audio, /* needShutdown = */ false); - err = OK; - } + if (formatChange /* not seamless */) { + // must change decoder + flushDecoder(audio, /* needShutdown = */ true); + } else if (timeChange) { + // need to flush + flushDecoder(audio, /* needShutdown = */ false, newFormat); + err = OK; + } else if (seamlessFormatChange) { + // reuse existing decoder and don't flush + updateDecoderFormatWithoutFlush(audio, newFormat); + err = OK; } else { // This stream is unaffected by the discontinuity - - if (audio) { - mFlushingAudio = FLUSHED; - } else { - mFlushingVideo = FLUSHED; - } - - finishFlushIfPossible(); - return -EWOULDBLOCK; } } @@ -1045,13 +1391,59 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { dropAccessUnit = false; if (!audio + && !(mSourceFlags & Source::FLAG_SECURE) && mVideoLateByUs > 100000ll && mVideoIsAVC && !IsAVCReferenceFrame(accessUnit)) { dropAccessUnit = true; ++mNumFramesDropped; } - } while (dropAccessUnit); + + size_t smallSize = accessUnit->size(); + needMoreData = false; + if (doBufferAggregation && (biggerBuffer == NULL) + // Don't bother if only room for a few small buffers. + && (smallSize < (kAudioBigBufferSizeBytes / 3))) { + // Create a larger buffer for combining smaller buffers from the extractor. + biggerBuffer = new ABuffer(kAudioBigBufferSizeBytes); + biggerBuffer->setRange(0, 0); // start empty + } + + if (biggerBuffer != NULL) { + int64_t timeUs; + bool smallTimestampValid = accessUnit->meta()->findInt64("timeUs", &timeUs); + // Will the smaller buffer fit? + size_t bigSize = biggerBuffer->size(); + size_t roomLeft = biggerBuffer->capacity() - bigSize; + // Should we save this small buffer for the next big buffer? + // If the first small buffer did not have a timestamp then save + // any buffer that does have a timestamp until the next big buffer. + if ((smallSize > roomLeft) + || (!gotTime && (numSmallBuffers > 0) && smallTimestampValid)) { + mPendingAudioErr = err; + mPendingAudioAccessUnit = accessUnit; + accessUnit.clear(); + } else { + // Append small buffer to the bigger buffer. + memcpy(biggerBuffer->base() + bigSize, accessUnit->data(), smallSize); + bigSize += smallSize; + biggerBuffer->setRange(0, bigSize); + + // Keep looping until we run out of room in the biggerBuffer. + needMoreData = true; + + // Grab time from first small buffer if available. + if ((numSmallBuffers == 0) && smallTimestampValid) { + biggerBuffer->meta()->setInt64("timeUs", timeUs); + gotTime = true; + } + + ALOGV("feedDecoderInputData() #%d, smallSize = %zu, bigSize = %zu, capacity = %zu", + numSmallBuffers, smallSize, bigSize, biggerBuffer->capacity()); + numSmallBuffers++; + } + } + } while (dropAccessUnit || needMoreData); // ALOGV("returned a valid buffer of %s data", audio ? "audio" : "video"); @@ -1063,7 +1455,17 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) { mediaTimeUs / 1E6); #endif - reply->setBuffer("buffer", accessUnit); + if (!audio) { + mCCDecoder->decode(accessUnit); + } + + if (biggerBuffer != NULL) { + ALOGV("feedDecoderInputData() reply with aggregated buffer, %d", numSmallBuffers); + reply->setBuffer("buffer", biggerBuffer); + } else { + reply->setBuffer("buffer", accessUnit); + } + reply->post(); return OK; @@ -1075,7 +1477,8 @@ void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) { sp<AMessage> reply; CHECK(msg->findMessage("reply", &reply)); - if (IsFlushingState(audio ? mFlushingAudio : mFlushingVideo)) { + if ((audio && mFlushingAudio != NONE) + || (!audio && mFlushingVideo != NONE)) { // We're currently attempting to flush the decoder, in order // to complete this, the decoder wants all its buffers back, // so we don't want any output buffers it sent us (from before @@ -1091,14 +1494,15 @@ void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) { sp<ABuffer> buffer; CHECK(msg->findBuffer("buffer", &buffer)); + int64_t mediaTimeUs; + CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs)); + int64_t &skipUntilMediaTimeUs = audio ? mSkipRenderingAudioUntilMediaTimeUs : mSkipRenderingVideoUntilMediaTimeUs; if (skipUntilMediaTimeUs >= 0) { - int64_t mediaTimeUs; - CHECK(buffer->meta()->findInt64("timeUs", &mediaTimeUs)); if (mediaTimeUs < skipUntilMediaTimeUs) { ALOGV("dropping %s buffer at time %lld as requested.", @@ -1112,9 +1516,79 @@ void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) { skipUntilMediaTimeUs = -1; } + if (!audio && mCCDecoder->isSelected()) { + mCCDecoder->display(mediaTimeUs); + } + mRenderer->queueBuffer(audio, buffer, reply); } +void NuPlayer::updateVideoSize( + const sp<AMessage> &inputFormat, + const sp<AMessage> &outputFormat) { + if (inputFormat == NULL) { + ALOGW("Unknown video size, reporting 0x0!"); + notifyListener(MEDIA_SET_VIDEO_SIZE, 0, 0); + return; + } + + int32_t displayWidth, displayHeight; + int32_t cropLeft, cropTop, cropRight, cropBottom; + + if (outputFormat != NULL) { + int32_t width, height; + CHECK(outputFormat->findInt32("width", &width)); + CHECK(outputFormat->findInt32("height", &height)); + + int32_t cropLeft, cropTop, cropRight, cropBottom; + CHECK(outputFormat->findRect( + "crop", + &cropLeft, &cropTop, &cropRight, &cropBottom)); + + displayWidth = cropRight - cropLeft + 1; + displayHeight = cropBottom - cropTop + 1; + + ALOGV("Video output format changed to %d x %d " + "(crop: %d x %d @ (%d, %d))", + width, height, + displayWidth, + displayHeight, + cropLeft, cropTop); + } else { + CHECK(inputFormat->findInt32("width", &displayWidth)); + CHECK(inputFormat->findInt32("height", &displayHeight)); + + ALOGV("Video input format %d x %d", displayWidth, displayHeight); + } + + // Take into account sample aspect ratio if necessary: + int32_t sarWidth, sarHeight; + if (inputFormat->findInt32("sar-width", &sarWidth) + && inputFormat->findInt32("sar-height", &sarHeight)) { + ALOGV("Sample aspect ratio %d : %d", sarWidth, sarHeight); + + displayWidth = (displayWidth * sarWidth) / sarHeight; + + ALOGV("display dimensions %d x %d", displayWidth, displayHeight); + } + + int32_t rotationDegrees; + if (!inputFormat->findInt32("rotation-degrees", &rotationDegrees)) { + rotationDegrees = 0; + } + + if (rotationDegrees == 90 || rotationDegrees == 270) { + int32_t tmp = displayWidth; + displayWidth = displayHeight; + displayHeight = tmp; + } + + notifyListener( + MEDIA_SET_VIDEO_SIZE, + displayWidth, + displayHeight); +} + void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) { if (mDriver == NULL) { return; @@ -1129,63 +1603,70 @@ void NuPlayer::notifyListener(int msg, int ext1, int ext2, const Parcel *in) { driver->notifyListener(msg, ext1, ext2, in); } -void NuPlayer::flushDecoder(bool audio, bool needShutdown) { +void NuPlayer::flushDecoder( + bool audio, bool needShutdown, const sp<AMessage> &newFormat) { ALOGV("[%s] flushDecoder needShutdown=%d", audio ? "audio" : "video", needShutdown); - if ((audio && mAudioDecoder == NULL) || (!audio && mVideoDecoder == NULL)) { + const sp<Decoder> &decoder = getDecoder(audio); + if (decoder == NULL) { ALOGI("flushDecoder %s without decoder present", audio ? "audio" : "video"); + return; } // Make sure we don't continue to scan sources until we finish flushing. ++mScanSourcesGeneration; mScanSourcesPending = false; - (audio ? mAudioDecoder : mVideoDecoder)->signalFlush(); + decoder->signalFlush(newFormat); mRenderer->flush(audio); FlushStatus newStatus = needShutdown ? FLUSHING_DECODER_SHUTDOWN : FLUSHING_DECODER; if (audio) { - CHECK(mFlushingAudio == NONE - || mFlushingAudio == AWAITING_DISCONTINUITY); - + ALOGE_IF(mFlushingAudio != NONE, + "audio flushDecoder() is called in state %d", mFlushingAudio); mFlushingAudio = newStatus; - - if (mFlushingVideo == NONE) { - mFlushingVideo = (mVideoDecoder != NULL) - ? AWAITING_DISCONTINUITY - : FLUSHED; - } } else { - CHECK(mFlushingVideo == NONE - || mFlushingVideo == AWAITING_DISCONTINUITY); - + ALOGE_IF(mFlushingVideo != NONE, + "video flushDecoder() is called in state %d", mFlushingVideo); mFlushingVideo = newStatus; - if (mFlushingAudio == NONE) { - mFlushingAudio = (mAudioDecoder != NULL) - ? AWAITING_DISCONTINUITY - : FLUSHED; + if (mCCDecoder != NULL) { + mCCDecoder->flush(); } } } -sp<AMessage> NuPlayer::Source::getFormat(bool audio) { - sp<MetaData> meta = getFormatMeta(audio); +void NuPlayer::updateDecoderFormatWithoutFlush( + bool audio, const sp<AMessage> &format) { + ALOGV("[%s] updateDecoderFormatWithoutFlush", audio ? "audio" : "video"); - if (meta == NULL) { - return NULL; + const sp<Decoder> &decoder = getDecoder(audio); + if (decoder == NULL) { + ALOGI("updateDecoderFormatWithoutFlush %s without decoder present", + audio ? "audio" : "video"); + return; } - sp<AMessage> msg = new AMessage; + decoder->signalUpdateFormat(format); +} - if(convertMetaDataToMessage(meta, &msg) == OK) { - return msg; - } - return NULL; +void NuPlayer::queueDecoderShutdown( + bool audio, bool video, const sp<AMessage> &reply) { + ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video); + + mDeferredActions.push_back( + new ShutdownDecoderAction(audio, video)); + + mDeferredActions.push_back( + new SimpleAction(&NuPlayer::performScanSources)); + + mDeferredActions.push_back(new PostMessageAction(reply)); + + processDeferredActions(); } status_t NuPlayer::setVideoScalingMode(int32_t mode) { @@ -1211,6 +1692,19 @@ status_t NuPlayer::getTrackInfo(Parcel* reply) const { return err; } +status_t NuPlayer::getSelectedTrack(int32_t type, Parcel* reply) const { + sp<AMessage> msg = new AMessage(kWhatGetSelectedTrack, id()); + msg->setPointer("reply", reply); + msg->setInt32("type", type); + + sp<AMessage> response; + status_t err = msg->postAndAwaitResponse(&response); + if (err == OK && response != NULL) { + CHECK(response->findInt32("err", &err)); + } + return err; +} + status_t NuPlayer::selectTrack(size_t trackIndex, bool select) { sp<AMessage> msg = new AMessage(kWhatSelectTrack, id()); msg->setSize("trackIndex", trackIndex); @@ -1219,6 +1713,14 @@ status_t NuPlayer::selectTrack(size_t trackIndex, bool select) { sp<AMessage> response; status_t err = msg->postAndAwaitResponse(&response); + if (err != OK) { + return err; + } + + if (!response->findInt32("err", &err)) { + err = OK; + } + return err; } @@ -1238,18 +1740,6 @@ void NuPlayer::processDeferredActions() { // an intermediate state, i.e. one more more decoders are currently // flushing or shutting down. - if (mRenderer != NULL) { - // There's an edge case where the renderer owns all output - // buffers and is paused, therefore the decoder will not read - // more input data and will never encounter the matching - // discontinuity. To avoid this, we resume the renderer. - - if (mFlushingAudio == AWAITING_DISCONTINUITY - || mFlushingVideo == AWAITING_DISCONTINUITY) { - mRenderer->resume(); - } - } - if (mFlushingAudio != NONE || mFlushingVideo != NONE) { // We're currently flushing, postpone the reset until that's // completed. @@ -1272,7 +1762,16 @@ void NuPlayer::performSeek(int64_t seekTimeUs) { seekTimeUs, seekTimeUs / 1E6); + if (mSource == NULL) { + // This happens when reset occurs right before the loop mode + // asynchronously seeks to the start of the stream. + LOG_ALWAYS_FATAL_IF(mAudioDecoder != NULL || mVideoDecoder != NULL, + "mSource is NULL and decoders not NULL audio(%p) video(%p)", + mAudioDecoder.get(), mVideoDecoder.get()); + return; + } mSource->seekTo(seekTimeUs); + ++mTimedTextGeneration; if (mDriver != NULL) { sp<NuPlayerDriver> driver = mDriver.promote(); @@ -1313,14 +1812,6 @@ void NuPlayer::performDecoderShutdown(bool audio, bool video) { mTimeDiscontinuityPending = true; - if (mFlushingAudio == NONE && (!audio || mAudioDecoder == NULL)) { - mFlushingAudio = FLUSHED; - } - - if (mFlushingVideo == NONE && (!video || mVideoDecoder == NULL)) { - mFlushingVideo = FLUSHED; - } - if (audio && mAudioDecoder != NULL) { flushDecoder(true /* audio */, true /* needShutdown */); } @@ -1341,13 +1832,21 @@ void NuPlayer::performReset() { ++mScanSourcesGeneration; mScanSourcesPending = false; + ++mAudioDecoderGeneration; + ++mVideoDecoderGeneration; + + if (mRendererLooper != NULL) { + if (mRenderer != NULL) { + mRendererLooper->unregisterHandler(mRenderer->id()); + } + mRendererLooper->stop(); + mRendererLooper.clear(); + } mRenderer.clear(); if (mSource != NULL) { mSource->stop(); - looper()->unregisterHandler(mSource->id()); - mSource.clear(); } @@ -1408,16 +1907,15 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { sp<NuPlayerDriver> driver = mDriver.promote(); if (driver != NULL) { - driver->notifyPrepareCompleted(err); - } - - int64_t durationUs; - if (mDriver != NULL && mSource->getDuration(&durationUs) == OK) { - sp<NuPlayerDriver> driver = mDriver.promote(); - if (driver != NULL) { + // notify duration first, so that it's definitely set when + // the app received the "prepare complete" callback. + int64_t durationUs; + if (mSource->getDuration(&durationUs) == OK) { driver->notifyDuration(durationUs); } + driver->notifyPrepareCompleted(err); } + break; } @@ -1446,11 +1944,19 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { case Source::kWhatVideoSizeChanged: { - int32_t width, height; - CHECK(msg->findInt32("width", &width)); - CHECK(msg->findInt32("height", &height)); + sp<AMessage> format; + CHECK(msg->findMessage("format", &format)); - notifyListener(MEDIA_SET_VIDEO_SIZE, width, height); + updateVideoSize(format); + break; + } + + case Source::kWhatBufferingUpdate: + { + int32_t percentage; + CHECK(msg->findInt32("percentage", &percentage)); + + notifyListener(MEDIA_BUFFERING_UPDATE, percentage, 0); break; } @@ -1471,21 +1977,40 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { sp<ABuffer> buffer; CHECK(msg->findBuffer("buffer", &buffer)); - int32_t trackIndex; - int64_t timeUs, durationUs; - CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex)); - CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); - CHECK(buffer->meta()->findInt64("durationUs", &durationUs)); + sendSubtitleData(buffer, 0 /* baseIndex */); + break; + } - Parcel in; - in.writeInt32(trackIndex); - in.writeInt64(timeUs); - in.writeInt64(durationUs); - in.writeInt32(buffer->size()); - in.writeInt32(buffer->size()); - in.write(buffer->data(), buffer->size()); + case Source::kWhatTimedTextData: + { + int32_t generation; + if (msg->findInt32("generation", &generation) + && generation != mTimedTextGeneration) { + break; + } - notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in); + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + sp<NuPlayerDriver> driver = mDriver.promote(); + if (driver == NULL) { + break; + } + + int posMs; + int64_t timeUs, posUs; + driver->getCurrentPosition(&posMs); + posUs = posMs * 1000; + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + + if (posUs < timeUs) { + if (!msg->findInt32("generation", &generation)) { + msg->setInt32("generation", mTimedTextGeneration); + } + msg->post(timeUs - posUs); + } else { + sendTimedTextData(buffer); + } break; } @@ -1502,13 +2027,112 @@ void NuPlayer::onSourceNotify(const sp<AMessage> &msg) { break; } + case Source::kWhatDrmNoLicense: + { + notifyListener(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, ERROR_DRM_NO_LICENSE); + break; + } + default: TRESPASS(); } } +void NuPlayer::onClosedCaptionNotify(const sp<AMessage> &msg) { + int32_t what; + CHECK(msg->findInt32("what", &what)); + + switch (what) { + case NuPlayer::CCDecoder::kWhatClosedCaptionData: + { + sp<ABuffer> buffer; + CHECK(msg->findBuffer("buffer", &buffer)); + + size_t inbandTracks = 0; + if (mSource != NULL) { + inbandTracks = mSource->getTrackCount(); + } + + sendSubtitleData(buffer, inbandTracks); + break; + } + + case NuPlayer::CCDecoder::kWhatTrackAdded: + { + notifyListener(MEDIA_INFO, MEDIA_INFO_METADATA_UPDATE, 0); + + break; + } + + default: + TRESPASS(); + } + + +} + +void NuPlayer::sendSubtitleData(const sp<ABuffer> &buffer, int32_t baseIndex) { + int32_t trackIndex; + int64_t timeUs, durationUs; + CHECK(buffer->meta()->findInt32("trackIndex", &trackIndex)); + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + CHECK(buffer->meta()->findInt64("durationUs", &durationUs)); + + Parcel in; + in.writeInt32(trackIndex + baseIndex); + in.writeInt64(timeUs); + in.writeInt64(durationUs); + in.writeInt32(buffer->size()); + in.writeInt32(buffer->size()); + in.write(buffer->data(), buffer->size()); + + notifyListener(MEDIA_SUBTITLE_DATA, 0, 0, &in); +} + +void NuPlayer::sendTimedTextData(const sp<ABuffer> &buffer) { + const void *data; + size_t size = 0; + int64_t timeUs; + int32_t flag = TextDescriptions::LOCAL_DESCRIPTIONS; + + AString mime; + CHECK(buffer->meta()->findString("mime", &mime)); + CHECK(strcasecmp(mime.c_str(), MEDIA_MIMETYPE_TEXT_3GPP) == 0); + + data = buffer->data(); + size = buffer->size(); + + Parcel parcel; + if (size > 0) { + CHECK(buffer->meta()->findInt64("timeUs", &timeUs)); + flag |= TextDescriptions::IN_BAND_TEXT_3GPP; + TextDescriptions::getParcelOfDescriptions( + (const uint8_t *)data, size, flag, timeUs / 1000, &parcel); + } + + if ((parcel.dataSize() > 0)) { + notifyListener(MEDIA_TIMED_TEXT, 0, 0, &parcel); + } else { // send an empty timed text + notifyListener(MEDIA_TIMED_TEXT, 0, 0); + } +} //////////////////////////////////////////////////////////////////////////////// +sp<AMessage> NuPlayer::Source::getFormat(bool audio) { + sp<MetaData> meta = getFormatMeta(audio); + + if (meta == NULL) { + return NULL; + } + + sp<AMessage> msg = new AMessage; + + if(convertMetaDataToMessage(meta, &msg) == OK) { + return msg; + } + return NULL; +} + void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) { sp<AMessage> notify = dupNotify(); notify->setInt32("what", kWhatFlagsChanged); @@ -1516,11 +2140,10 @@ void NuPlayer::Source::notifyFlagsChanged(uint32_t flags) { notify->post(); } -void NuPlayer::Source::notifyVideoSizeChanged(int32_t width, int32_t height) { +void NuPlayer::Source::notifyVideoSizeChanged(const sp<AMessage> &format) { sp<AMessage> notify = dupNotify(); notify->setInt32("what", kWhatVideoSizeChanged); - notify->setInt32("width", width); - notify->setInt32("height", height); + notify->setMessage("format", format); notify->post(); } @@ -1531,23 +2154,8 @@ void NuPlayer::Source::notifyPrepared(status_t err) { notify->post(); } -void NuPlayer::Source::onMessageReceived(const sp<AMessage> &msg) { +void NuPlayer::Source::onMessageReceived(const sp<AMessage> & /* msg */) { TRESPASS(); } -void NuPlayer::queueDecoderShutdown( - bool audio, bool video, const sp<AMessage> &reply) { - ALOGI("queueDecoderShutdown audio=%d, video=%d", audio, video); - - mDeferredActions.push_back( - new ShutdownDecoderAction(audio, video)); - - mDeferredActions.push_back( - new SimpleAction(&NuPlayer::performScanSources)); - - mDeferredActions.push_back(new PostMessageAction(reply)); - - processDeferredActions(); -} - } // namespace android |