+1

Tìm hiểu về convert video trong Android 4.3

Convert video đã không còn xa lạ trên máy tính nhưng trên điện thoại để làm được việc đó không phải vấn đề đơn giản về tốc độ cũng như performance không bị ảnh hưởng đến các ứng dụng khác đang chạy

Để làm được việc đó android ra đời công cụ cho phép chỉnh sửa video thông qua: MediaExtractor, MediaFormat, MediaMuxer, MediaCodec, và OpenGL ES (phiên bản 20 - GLES20)

**1. Cấu trúc **

  • Sử dụng MediaExtractor extract video input thành 2 MediaCodec: VideoFormat và AudioFormat
  • Từ VideoFormat ta sẽ render vào GLSurfaceview tại đây ta có thể điều chỉnh kích cỡ về chất lượng video: 480x320, 720x480,...
  • Sử dụng MediaMuxer để add các MediaFormat + Surfaceview ta được video output với chất lượng tương ứng

2. Code demo

Khởi tạo Frame trên SurfaceTexture để làm nên cho việc render video sau khi decoder

class OutputSurface implements SurfaceTexture.OnFrameAvailableListener {
.....

Decode video đầu vào thành MediaCodec và render lên surfaceview

private MediaCodec createVideoDecoder(MediaFormat inputFormat, Surface surface) {
        try {
            MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
            decoder.configure(inputFormat, surface, null, 0);
            decoder.start();
            return decoder;
        } catch (Exception e) {
            return null;
        }
    }

Thông qua phương thức createVideoDecoder ta decode video đầu vào dạng 1 MediaFormat thành MediaCodec và render trên SurfaceView

Từ SurfaceTexture ta có thể sử dụng GLES20 để draw data (list các bitmap sau khi decoder từ video) lên trên frame của SurfaceTexture

Từ Surfaceture ta có thể khai báo các thuộc tính về frame, màu sắc, ... và có thể add thêm các đối tượng khác như: text, image, hay bất kỳ thuộc tính nào đó thông qua GLES

Cấu trúc thực hiện draw Object của GLES thông qua GLSurfaceview

Khởi tạo surface với 1 số thuộc tính config: Khung nhìn, background

onSurfaceCreated

Tạo shader cho GL với type và shaderCode

loadShader(int type, String shaderCode) {
	GLES20.glShaderSource(shader, shaderCode);
	GLES20.glCompileShader(shader);
}

Draw lại hoặc update 1 Objects khi sureface thay đổi như thay đổi khung nhìn khi onritation thay đổi trong hàm

@Override
    public void onSurfaceChanged(GL20 unused, int width, int height) {
    }

Draw objects trên surface

@Override
    public void onDrawFrame(GL20 unused) {
    // Clear background color
GLES20.glClearColor(0.0f, 1.0f, 0.0f, 1.0f);
        GLES20.glClear(GLES20.GL_DEPTH_BUFFER_BIT | GLES20.GL_COLOR_BUFFER_BIT);
        // Create port view
        GLES20.glUseProgram(mProgram);
        checkGlError("glUseProgram");
        GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
        GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, mTextureID);
        // Move to position
        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_POS_OFFSET);
        GLES20.glVertexAttribPointer(maPositionHandle, 3, GLES20.GL_FLOAT, false,
                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        checkGlError("glVertexAttribPointer maPosition");
        GLES20.glEnableVertexAttribArray(maPositionHandle);
        checkGlError("glEnableVertexAttribArray maPositionHandle");
        mTriangleVertices.position(TRIANGLE_VERTICES_DATA_UV_OFFSET);
        GLES20.glVertexAttribPointer(maTextureHandle, 2, GLES20.GL_FLOAT, false,
                TRIANGLE_VERTICES_DATA_STRIDE_BYTES, mTriangleVertices);
        checkGlError("glVertexAttribPointer maTextureHandle");
        GLES20.glEnableVertexAttribArray(maTextureHandle);
        checkGlError("glEnableVertexAttribArray maTextureHandle");
        Matrix.setIdentityM(mMVPMatrix, 0);

        // display data

        GLES20.glUniformMatrix4fv(muMVPMatrixHandle, 1, false, mMVPMatrix, 0);
        GLES20.glUniformMatrix4fv(muSTMatrixHandle, 1, false, mSTMatrix, 0);

        // save data

        GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
        checkGlError("glDrawArrays");
        /**
         * You can draw anything in here
         * Text, Images
         */

		// Finish draw
        GLES20.glFinish();
        }

Link tham khảo: http://developer.android.com/training/graphics/opengl/environment.html

Tương tự với thuộc tính trên ta sẽ render video thành AudioFormat

private MediaCodec createAudioDecoder(MediaFormat inputFormat) {
        try {
            MediaCodec decoder = MediaCodec.createDecoderByType(getMimeTypeFor(inputFormat));
            decoder.configure(inputFormat, null, null, 0);
            decoder.start();
            return decoder;
        } catch (Exception e) {
            return null;
        }
    }

Sau khi chỉnh sửa với video trên frame ta thực hiện export video thông qua MediaMuxer với một số thuộc tính của video + audio cho phù hợp với đầu ra định dạng mong muốn của người dùng

Ví dụ trong code sample

OUTPUT_VIDEO_MIME_TYPE = "video/avc"; // H.264 Advanced Video Coding
OUTPUT_VIDEO_BIT_RATE = 2000000; // 2Mbps
OUTPUT_VIDEO_FRAME_RATE = 15; // 15fps
OUTPUT_VIDEO_IFRAME_INTERVAL = 10; // 10 seconds between I-frames
OUTPUT_VIDEO_COLOR_FORMAT
    // parameters for the audio encoder
OUTPUT_AUDIO_MIME_TYPE = "audio/mp4a-latm"; // Advanced Audio Coding
OUTPUT_AUDIO_CHANNEL_COUNT = 2; // Must match the input stream.
OUTPUT_AUDIO_BIT_RATE = 128 * 1024;
OUTPUT_AUDIO_AAC_PROFILE =
            MediaCodecInfo.CodecProfileLevel.AACObjectHE;
OUTPUT_AUDIO_SAMPLE_RATE_HZ = 44100; // Must match the input stream.

Export to video

Thông qua thời lượng video và IFrame ta có được số lượng frame trên tổng video

Ví dụ video đầu vào có thời lượng 30' với thông số trên ta có được 450 FRAME

Từ MediaFormat + Audio Format ta sẽ write nó thông qua ByteBuffer sẽ được video đầu ra tương ứng với các thuộc tính đã config

**3. Screenshot demo ** device-2015-11-27-182409.png

device-2015-11-27-182416.png

device-2015-11-27-182427.png

**4. Code ** https://www.dropbox.com/s/he4a3aa31hoz64y/DecodeVideoSample.zip?dl=0


All Rights Reserved

Viblo
Let's register a Viblo Account to get more interesting posts.