You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Android端用原生组件录制SurfaceView动画为视频文件的方法

Recording SurfaceView Animation with Android Native Tools (No FFmpeg)

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

  1. 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 your SurfaceView for 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;
    }
    
  2. Get the Recorder’s Surface
    Retrieve the surface that the MediaRecorder will use to capture frames:

    MediaRecorder recorder = setupMediaRecorder(outputFile, surfaceView.getWidth(), surfaceView.getHeight());
    Surface recorderSurface = recorder.getSurface();
    
  3. Draw Frames to Both Surfaces
    When replaying your animation (using the recorded touch coordinates), draw each frame to both your SurfaceView and the recorder’s surface. If you’re using Canvas for 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);
    
  4. Start/Stop Recording
    Call recorder.start() when you begin replaying the animation, and recorder.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

  1. Create an ImageReader
    This will capture frames from your SurfaceView (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
    );
    
  2. 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()));
    
  3. 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
    );
    
  4. Render to ImageReader’s Surface
    Instead of drawing directly to the SurfaceView, render your animation frames to the ImageReader’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);
    
  5. Encode Frames and Mux
    Convert the captured Image data into ByteBuffers, feed them to MediaCodec, then write the encoded output packets to MediaMuxer. 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 use MediaStore (Android 10+) to save the video file. If including audio, add RECORD_AUDIO permission.
  • 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.28 09:44:42