You can't do this, due to a limitation of the Android app framework (as of Android 4.4 at least).
The SurfaceTexture that underlies the TextureView is a buffer consumer. The MediaPlayer is one example of a buffer producer, Canvas is another. Once you attach a producer, you have to detach it before you can attach a second producer.
The trouble is that there is no way to detach a software-based (Canvas) buffer producer. There could be, but isn't. So once you draw with Canvas, you're stuck. (There's a note to that effect here.)
You can detach a GLES producer. For example, in one of Grafika's video player classes you can find a clearSurface() method that clears the surface to black using GLES. Note the EGL context and window are created and explicitly released within the scope of the method. You could expand the method to show an image instead.
06-06 18:55:13.130 28137 25285 E BufferQueueProducer: [SurfaceTexture-0-28137-0](id:6de900000001,api:2,p:28137,c:28137) connect: already connected (cur=2 req=4) 06-06 18:55:13.130 1905 8873 E Camera3-OutputStream: configureConsumerQueueLocked: Unable to connect to native window for stream 0 06-06 18:55:13.130 1905 8873 E Camera3-Stream: finishConfiguration: Unable to configure stream 0 queue: Invalid argument (-22) 06-06 18:55:13.130 1905 8873 E Camera3-Device: Camera 0: configureStreamsLocked: Can't finish configuring output stream 0: Invalid argument ( -22) 06-06 18:55:13.130 1047 1365 E minksocket: MinkIPC_QRTR_Service: client with node 1 port 6838 went down 06-06 18:55:13.130 1905 8873 D CameraService: CameraPerf: setpriority success, tid is 8873, priority is 0 06-06 18:55:13.130 1905 8873 E CameraDeviceClient: endConfigure: Camera 0: Unsupported set of inputs/outputs provided
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r8:frameworks/av/services/camera/libcameraservice/device3/Camera3OutputStream.cpp status_tCamera3OutputStream::configureConsumerQueueLocked(bool allowPreviewRespace){ ... // Configure consumer-side ANativeWindow interface. The listener may be used // to notify buffer manager (if it is used) of the returned buffers. res = mConsumer->connect(NATIVE_WINDOW_API_CAMERA, /*reportBufferRemoval*/true, /*listener*/mBufferProducerListener); if (res != OK) { ALOGE("%s: Unable to connect to native window for stream %d", __FUNCTION__, mId); return res; } ... }
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r8:frameworks/native/libs/nativewindow/include/system/window.h /* parameter for NATIVE_WINDOW_[API_][DIS]CONNECT */ enum { /* Buffers will be queued by EGL via eglSwapBuffers after being filled using * OpenGL ES. */ NATIVE_WINDOW_API_EGL = 1,
/* Buffers will be queued after being filled using the CPU */ NATIVE_WINDOW_API_CPU = 2,
/* Buffers will be queued by Stagefright after being filled by a video * decoder. The video decoder can either be a software or hardware decoder. */ NATIVE_WINDOW_API_MEDIA = 3,
/* Buffers will be queued by the the camera HAL. */ NATIVE_WINDOW_API_CAMERA = 4, };
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r8:frameworks/native/libs/gui/Surface.cpp status_tSurface::lock( ANativeWindow_Buffer* outBuffer, ARect* inOutDirtyBounds) { ... if (!mConnectedToCpu) { int err = Surface::connect(NATIVE_WINDOW_API_CPU); if (err) { return err; } // we're intending to do software rendering from this point setUsage(GRALLOC_USAGE_SW_READ_OFTEN | GRALLOC_USAGE_SW_WRITE_OFTEN); } ... }
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r8:frameworks/native/libs/gui/Surface.cpp intSurface::connect( int api, const sp<IProducerListener>& listener, bool reportBufferRemoval){ int err = mGraphicBufferProducer->connect(listener, api, mProducerControlledByApp, &output); ... if (!err && api == NATIVE_WINDOW_API_CPU) { mConnectedToCpu = true; // Clear the dirty region in case we're switching from a non-CPU API mDirtyRegion.clear(); } ... }
// https://cs.android.com/android/platform/superproject/+/android-13.0.0_r8:frameworks/native/libs/gui/BufferQueueProducer.cpp status_tBufferQueueProducer::queueBuffer(int slot, const QueueBufferInput &input, QueueBufferOutput *output){ ... // Wait without lock held if (connectedApi == NATIVE_WINDOW_API_EGL) { // Waiting here allows for two full buffers to be queued but not a // third. In the event that frames take varying time, this makes a // small trade-off in favor of latency rather than throughput. lastQueuedFence->waitForever("Throttling EGL Production"); } ... }