API 21以下版本MediaCodec解码时TextureView无法正常工作
Hey, this is a super common compatibility issue with MediaCodec pre-API 21—those older Android versions are way pickier about configuration and Surface handling compared to Lollipop and above. Let's walk through the key fixes and checks to get your TextureView showing video:
1. Fix the Invalid csd-0 Data
First off, your current csd-0 setup is a problem! csd-0 holds the H.264 SPS (Sequence Parameter Set) and PPS (Picture Parameter Set) data—this is mandatory for the decoder to understand how to decode the video. Just allocating an empty ByteBuffer like ByteBuffer.allocate(100) works for API 21+ (they have better fallback logic), but pre-21 decoders will just refuse to work without valid codec-specific data.
Here's how to do it right:
- Extract the actual SPS and PPS bytes from your video stream (these usually come at the start of the stream, or in the container metadata)
- Package them into a single ByteBuffer following the H.264 standard: start with the SPS (prefixed with
0x00 0x00 0x00 0x01), then the PPS (also prefixed with0x00 0x00 0x00 0x01) - Don't forget to
flip()the ByteBuffer so the decoder reads from the start:
// Example: assuming you have spsBytes and ppsBytes as your real SPS/PPS data ByteBuffer csd0 = ByteBuffer.allocate(spsBytes.length + ppsBytes.length); csd0.put(spsBytes); csd0.put(ppsBytes); csd0.flip(); // Critical! Resets the buffer position to 0 format.setByteBuffer("csd-0", csd0);
2. Fix Surface Initialization for Pre-API 21
Before API 21, you can't just create a Surface from a TextureView's SurfaceTexture directly and pass it to MediaCodec.configure(). You need to wait until the TextureView's Surface is fully available, and use the Surface provided by the TextureView itself.
Do this with a SurfaceTextureListener:
mTextureView.setSurfaceTextureListener(new TextureView.SurfaceTextureListener() { @Override public void onSurfaceTextureAvailable(SurfaceTexture surface, int width, int height) { // Initialize your codec ONLY after the TextureView is ready setupMediaCodec(); } @Override public void onSurfaceTextureSizeChanged(SurfaceTexture surface, int width, int height) { // Handle size changes if needed } @Override public boolean onSurfaceTextureDestroyed(SurfaceTexture surface) { // Clean up codec resources here if (m_codec != null) { m_codec.stop(); m_codec.release(); } return true; } @Override public void onSurfaceTextureUpdated(SurfaceTexture surface) { // Optional: handle texture updates } }); private void setupMediaCodec() { MediaFormat format = MediaFormat.createVideoFormat(MediaFormat.MIMETYPE_VIDEO_AVC, 1920, 1080); // Set your corrected csd-0 here... format.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 100000); try { m_codec = MediaCodec.createDecoderByType(MediaFormat.MIMETYPE_VIDEO_AVC); // Use the TextureView's built-in Surface directly m_codec.configure(format, mTextureView.getSurface(), null, 0); m_codec.start(); // Now start feeding frames to the decoder... } catch (IOException e) { e.printStackTrace(); } }
3. Enforce Strict Buffer Handling for Older Decoders
Pre-API 21 decoders are less forgiving with buffer timing and handling. Make sure you use timeouts when dequeuing buffers, and properly manage input/output buffers:
// Feed input frames to the decoder private void feedFrameToDecoder(byte[] frameData, long timestampUs) { long timeoutUs = 10000; // 10ms timeout to avoid hanging int inputBufferId = m_codec.dequeueInputBuffer(timeoutUs); if (inputBufferId >= 0) { ByteBuffer inputBuffer = m_codec.getInputBuffer(inputBufferId); inputBuffer.clear(); inputBuffer.put(frameData); // Submit the buffer with a valid timestamp (use monotonic time for real-time streams) m_codec.queueInputBuffer(inputBufferId, 0, frameData.length, timestampUs, 0); } } // Process output frames to render to TextureView private void processOutputFrames() { MediaCodec.BufferInfo bufferInfo = new MediaCodec.BufferInfo(); long timeoutUs = 10000; int outputBufferId = m_codec.dequeueOutputBuffer(bufferInfo, timeoutUs); while (outputBufferId >= 0) { // Release the buffer to render it to the Surface (true = auto-release after rendering) m_codec.releaseOutputBuffer(outputBufferId, true); outputBufferId = m_codec.dequeueOutputBuffer(bufferInfo, timeoutUs); } }
4. Extra Checks for Pre-API 21
- Ensure your video frames are valid H.264 NALU units (either prefixed with
0x00 0x00 0x00 0x01or using length prefixes—older decoders won't handle malformed NALUs) - Don't run codec operations on the main thread—use a dedicated background thread to avoid ANRs and timing issues
- Make sure timestamps are monotonic (increasing) — older decoders can glitch or stop if they get out-of-order timestamps
Start with fixing the csd-0 data first—that's the most likely culprit here. Once that's sorted, verify your Surface initialization flow, and you should see video rendering on pre-API 21 devices.
内容的提问来源于stack exchange,提问作者Bikash Sahani

