Android Camera2 API实现在录制视频每帧添加时间戳与速度的技术问题
Hey there, let's tackle this Camera2 API + timestamp/speed overlay issue you're facing with MediaCodec and MediaMuxer. Force closes with JNI errors are never fun, so let's break down possible fixes and more reliable approaches:
First: Diagnose the JNI Crash Root Cause
JNI errors when using MediaCodec usually stem from mismanaged native resources or invalid buffer operations. Here are the top things to check:
- Always clean up Image resources: When using
ImageReaderwith Camera2, everyImageyou dequeue must be closed withimage.close()after processing. Failing to do this leaks native memory, which almost always leads to JNI crashes. Wrap your image processing in a try-finally block to enforce this:Image image = imageReader.acquireLatestImage(); if (image != null) { try { // Process the image (add overlays, send to codec) } finally { image.close(); } } - Validate MediaCodec buffer operations: Never access a buffer after you've queued it back to the codec, and never release a buffer twice. Stick strictly to this workflow:
- Dequeue input buffer with
dequeueInputBuffer(timeoutUs) - If the returned index is >=0, write your modified frame data to it
- Call
queueInputBuffer()with correct timestamp, flags, and buffer size - For output buffers, after processing, call
releaseOutputBuffer(index, render)appropriately
- Dequeue input buffer with
- Catch unhandled exceptions: MediaCodec often throws unchecked
RuntimeExceptions for invalid operations. Wrap all codec calls in try-catch blocks to prevent sudden app crashes, and log the exception details to pinpoint the issue:try { int inputBufferIndex = mediaCodec.dequeueInputBuffer(10000); // Handle buffer operations } catch (RuntimeException e) { Log.e("CodecError", "MediaCodec operation failed: " + e.getMessage()); }
Better Approach for Overlays: Use an Intermediate Drawing Surface
Modifying raw frame data from Camera2 directly with MediaCodec is error-prone. Instead, use a drawing layer to add your timestamp and speed before sending the frame to the encoder:
- Set up an ImageReader to receive frames from Camera2 (use format
ImageFormat.YUV_420_888for broad compatibility) - Convert the Image to a drawable surface: Use a
SurfaceTextureorBitmapto create aCanvaswhere you can draw overlays - Draw your elements: On the Canvas, use
Paintobjects to render the timestamp (format withSimpleDateFormatfor readability) and current speed value (from GPS/accelerometer) - Pass the modified frame to MediaCodec: Send the drawn surface to the codec's input surface for encoding
This method avoids low-level buffer manipulation, drastically reducing the chance of JNI errors.
Alternative: Use MediaRecorder for Simpler Encoding
If rolling your own MediaCodec/MediaMuxer stack is too unstable, switch to MediaRecorder—it handles most encoding logic natively, which is far more reliable:
- Create a
SurfaceVieworTextureViewfor your camera preview - Override the view's
onDraw()method to draw your timestamp and speed every frame - Configure
MediaRecorderto record directly from the view'sSurface - Start the Camera2 session to send frames to the view's surface, then initiate recording with MediaRecorder
This approach cuts down on custom codec code, so you'll avoid most JNI-related crashes entirely.
Sync Tips for Accurate Overlays
- Use Camera2's frame timestamp: Each
Imagefrom ImageReader has a hardware timestamp viaimage.getTimestamp(). Sync your overlay timestamp to this (convert it to system time if needed) instead of relying onSystem.currentTimeMillis()to avoid time drift. - Fetch speed data right before drawing: To ensure your speed value matches the current frame, get the latest reading from your location sensor (GPS) or accelerometer immediately before rendering the overlay.
备注:内容来源于stack exchange,提问作者CHINTAN PATEL

