Android端用原生组件录制SurfaceView动画为视频文件的方法
Hey there! Great question—let’s break down two solid approaches to record your drag-and-drop Bitmap animation directly using Android’s native APIs, no third-party libraries required. Both methods play nicely with your existing setup where you’re tracking touch coordinates to replay the animation.
Approach 1: MediaRecorder + Dual Surface Rendering
This is the simpler, more straightforward option if you want minimal code overhead. The core idea is to feed the same animation frames to both your SurfaceView and a MediaRecorder surface so every frame gets recorded as it’s displayed.
Step-by-Step Implementation
Set up the MediaRecorder
First, configure the recorder with your desired output settings (resolution, frame rate, codec, etc.). Make sure to match the resolution of yourSurfaceViewfor best results.private MediaRecorder setupMediaRecorder(File outputFile, int width, int height) throws IOException { MediaRecorder recorder = new MediaRecorder(); // Skip audio setup if you don't need sound in your video recorder.setAudioSource(MediaRecorder.AudioSource.MIC); recorder.setAudioEncoder(MediaRecorder.AudioEncoder.AAC); recorder.setAudioSamplingRate(44100); recorder.setVideoSource(MediaRecorder.VideoSource.SURFACE); recorder.setOutputFormat(MediaRecorder.OutputFormat.MPEG_4); recorder.setOutputFile(outputFile.getAbsolutePath()); recorder.setVideoSize(width, height); recorder.setVideoFrameRate(30); // Match your animation's playback frame rate recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H264); recorder.prepare(); return recorder; }Get the Recorder’s Surface
Retrieve the surface that theMediaRecorderwill use to capture frames:MediaRecorder recorder = setupMediaRecorder(outputFile, surfaceView.getWidth(), surfaceView.getHeight()); Surface recorderSurface = recorder.getSurface();Draw Frames to Both Surfaces
When replaying your animation (using the recorded touch coordinates), draw each frame to both yourSurfaceViewand the recorder’s surface. If you’re usingCanvasfor drawing, it’ll look something like this:// Draw to SurfaceView for user to see Canvas viewCanvas = surfaceHolder.lockCanvas(); drawAnimationFrame(viewCanvas, currentX, currentY); // Your existing drawing logic surfaceHolder.unlockCanvasAndPost(viewCanvas); // Draw the same frame to the recorder surface Canvas recorderCanvas = recorderSurface.lockCanvas(null); drawAnimationFrame(recorderCanvas, currentX, currentY); recorderSurface.unlockCanvasAndPost(recorderCanvas);Start/Stop Recording
Callrecorder.start()when you begin replaying the animation, andrecorder.stop()+recorder.release()once it’s finished.
Note: If you’re using OpenGL ES for rendering (instead of Canvas), you can render the same frame to two EGL surfaces (one for the
SurfaceView, one for the recorder) for better performance than double-drawing with Canvas.
Approach 2: ImageReader + MediaCodec + MediaMuxer
This method gives you more control over frame processing and encoding, which is useful if you need to modify frames (e.g., add filters) before recording, or if you want fine-tuned codec settings.
Step-by-Step Implementation
Create an ImageReader
This will capture frames from yourSurfaceView(or a dedicated rendering surface). Configure it with your target resolution and image format:ImageReader imageReader = ImageReader.newInstance( surfaceView.getWidth(), surfaceView.getHeight(), ImageFormat.YUV_420_888, 2 // Buffer size to avoid frame drops );Set Up Frame Listener
Listen for available frames and pass them to a video encoder:imageReader.setOnImageAvailableListener((reader) -> { try (Image image = reader.acquireNextImage()) { if (image != null) { encodeFrame(image); // Your encoding logic } } }, new Handler(Looper.getMainLooper()));Configure MediaCodec and MediaMuxer
Initialize the H.264 encoder and muxer to package encoded frames into a video file:// Initialize MediaCodec MediaCodec codec = MediaCodec.createEncoderByType("video/avc"); MediaFormat format = MediaFormat.createVideoFormat( "video/avc", surfaceView.getWidth(), surfaceView.getHeight() ); format.setInteger(MediaFormat.KEY_BIT_RATE, 5_000_000); // 5 Mbps format.setInteger(MediaFormat.KEY_FRAME_RATE, 30); format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, 5); // I-frame every 5 seconds codec.configure(format, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE); codec.start(); // Initialize MediaMuxer MediaMuxer muxer = new MediaMuxer( outputFile.getAbsolutePath(), MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4 );Render to ImageReader’s Surface
Instead of drawing directly to theSurfaceView, render your animation frames to theImageReader’s surface. For Canvas-based drawing:Canvas recordingCanvas = imageReader.getSurface().lockCanvas(null); drawAnimationFrame(recordingCanvas, currentX, currentY); imageReader.getSurface().unlockCanvasAndPost(recordingCanvas); // Optional: Draw to SurfaceView separately for preview Canvas viewCanvas = surfaceHolder.lockCanvas(); drawAnimationFrame(viewCanvas, currentX, currentY); surfaceHolder.unlockCanvasAndPost(viewCanvas);Encode Frames and Mux
Convert the capturedImagedata intoByteBuffers, feed them toMediaCodec, then write the encoded output packets toMediaMuxer. You’ll need to handle format changes (like when the codec outputs its first config packet) and track timestamps for smooth playback.
Key Considerations
- Permissions: Don’t forget to request
WRITE_EXTERNAL_STORAGE(for Android <10) or useMediaStore(Android 10+) to save the video file. If including audio, addRECORD_AUDIOpermission. - Frame Rate Sync: Ensure your animation replay matches the frame rate you set in the recorder/codec to avoid choppy video.
- Performance: Double-drawing with Canvas can cause lag on lower-end devices—use OpenGL ES for dual-surface rendering if you run into performance issues.
内容的提问来源于stack exchange,提问作者Alin

