Tìm hiểu về convert video trong Android 4.3
Bài đăng này đã không được cập nhật trong 3 năm
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 **
**4. Code ** https://www.dropbox.com/s/he4a3aa31hoz64y/DecodeVideoSample.zip?dl=0
All rights reserved