视频切换SurfaceView 会闪一下

问题描述

最近在使用MediaCodec做视频播放,在小屏切换节目时,小屏就会出现闪一下的现象(黑屏->透明->视频画面。

看见这一现象后,想到了一个解决方案,就是参考tif框架中LiveTv中的遮黑处理,在停止播放时,再SurfaceView上覆盖一张黑色背景的View,再等视频画面出现时,把黑色背景的View隐藏,如此闪屏现象就消失了。

但是收到的出画消息好像不太精,准修改后出画速度好像没有以前快了,所以决定寻找一下根本原因。

原因分析:

在加了大量的debug log后,终于在SurfaceUtils.cpp找到了问题关键。
也就是在MediaCodec stop时,是调用到pushBlankBuffersToNativeWindow方法导致,SurfaceView的区域会显示黑色,又因为SurfaceView在绘画之前会是透明或者半透明,所以最终就会出现黑屏->透明->视频画面的闪一下的现象。

那怎样让MediaCodec stop不调用到pushBlankBuffersToNativeWindow呢?

在ACodec.cpp中,加log分析后,在不改动framework code 的前提下,好像并不能避免。
相关code路径:
android/frameworks/av/media/libstagefright/ACodec.cpp
android/frameworks/av/media/libstagefright/SurfaceUtils.cpp

原因如下:
MediaCodec 需要出画,MediaCodec的CodeName就需要是.secure结尾,所以mCodec->mFlags就会添加 kFlagPushBlankBuffersToNativeWindowOnShutdown flags。

bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
    
    
    ALOGV("onAllocateComponent");

    CHECK(mCodec->mOMXNode == NULL);
    mCodec->mFatalError = false;

    sp<AMessage> notify = new AMessage(kWhatOMXMessageList, mCodec);
    notify->setInt32("generation", mCodec->mNodeGeneration + 1);

    sp<RefBase> obj;
    CHECK(msg->findObject("codecInfo", &obj));
    sp<MediaCodecInfo> info = (MediaCodecInfo *)obj.get();
    if (info == nullptr) {
    
    
        ALOGE("Unexpected nullptr for codec information");
        mCodec->signalError(OMX_ErrorUndefined, UNKNOWN_ERROR);
        return false;
    }
    AString owner = (info->getOwnerName() == nullptr) ? "default" : info->getOwnerName();

    AString componentName;
    CHECK(msg->findString("componentName", &componentName));

    sp<CodecObserver> observer = new CodecObserver(notify);
    sp<IOMX> omx;
    sp<IOMXNode> omxNode;

    status_t err = NAME_NOT_FOUND;
    OMXClient client;
    if (client.connect(owner.c_str()) != OK) {
    
    
        mCodec->signalError(OMX_ErrorUndefined, NO_INIT);
        return false;
    }
    omx = client.interface();

    pid_t tid = gettid();
    int prevPriority = androidGetThreadPriority(tid);
    androidSetThreadPriority(tid, ANDROID_PRIORITY_FOREGROUND);
    err = omx->allocateNode(componentName.c_str(), observer, &omxNode);
    androidSetThreadPriority(tid, prevPriority);

    if (err != OK) {
    
    
        ALOGE("Unable to instantiate codec '%s' with err %#x.", componentName.c_str(), err);

        mCodec->signalError((OMX_ERRORTYPE)err, makeNoSideEffectStatus(err));
        return false;
    }

    mDeathNotifier = new DeathNotifier(new AMessage(kWhatOMXDied, mCodec));
    auto tOmxNode = omxNode->getHalInterface<IOmxNode>();
    if (tOmxNode && !tOmxNode->linkToDeath(mDeathNotifier, 0)) {
    
    
        mDeathNotifier.clear();
    }

    ++mCodec->mNodeGeneration;

    mCodec->mComponentName = componentName;
    mCodec->mRenderTracker.setComponentName(componentName);
    mCodec->mFlags = 0;
    //kFlagPushBlankBuffersToNativeWindowOnShutdown  设置了遮黑的flags   
    if (componentName.endsWith(".secure")) {
    
    
        mCodec->mFlags |= kFlagIsSecure;
        mCodec->mFlags |= kFlagIsGrallocUsageProtected;
        mCodec->mFlags |= kFlagPushBlankBuffersToNativeWindowOnShutdown;
    }

    mCodec->mOMX = omx;
    mCodec->mOMXNode = omxNode;
    mCodec->mCallback->onComponentAllocated(mCodec->mComponentName.c_str());
    mCodec->changeState(mCodec->mLoadedState);

    return true;
}

在MediaCodec stop时,又会判断mCodec->mFlags,导致call到了pushBlankBuffersToNativeWindow。

void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
    
    
    if (mComponentNowIdle && mCodec->allYourBuffersAreBelongToUs()) {
    
    
        status_t err = mCodec->mOMXNode->sendCommand(
                OMX_CommandStateSet, OMX_StateLoaded);
        if (err == OK) {
    
    
            err = mCodec->freeBuffersOnPort(kPortIndexInput);
            status_t err2 = mCodec->freeBuffersOnPort(kPortIndexOutput);
            if (err == OK) {
    
    
                err = err2;
            }
        }
		//判断使用遮黑的flags
        if ((mCodec->mFlags & kFlagPushBlankBuffersToNativeWindowOnShutdown)
                && mCodec->mNativeWindow != NULL) {
    
    
            // We push enough 1x1 blank buffers to ensure that one of
            // them has made it to the display.  This allows the OMX
            // component teardown to zero out any protected buffers
            // without the risk of scanning out one of those buffers.
            pushBlankBuffersToNativeWindow(mCodec->mNativeWindow.get());
        }

        if (err != OK) {
    
    
            mCodec->signalError(OMX_ErrorUndefined, FAILED_TRANSACTION);
            return;
        }

        mCodec->changeState(mCodec->mIdleToLoadedState);
    }
}

总结

就MediaCodec会闪一下的问题,希望有大神路过时指点一二。也希望遇见同样问题的朋友,可以一起交流心得。谢谢!

猜你喜欢

转载自blog.csdn.net/qq_35831940/article/details/126467784
今日推荐