VideoFileRenderer實現(xiàn)了VideoRenderer的Callbacks曹铃,可以回調(diào)到產(chǎn)生的Frame盒延。
它的主要功能是將產(chǎn)生的視頻流以文件的形式存儲在本地嘴拢。
//定義一個新的工作的線程
private final HandlerThread renderThread;
//定義一個線程鎖
private final Object handlerLock = new Object();
//定義一個Handler
private final Handler renderThreadHandler;
//文件輸出的流
private final FileOutputStream videoOutFile;
//流的屬性
private final int outputFileWidth;
private final int outputFileHeight;
//幀的數(shù)量
private final int outputFrameSize;
//一個輸出的Buffer
private final ByteBuffer outputFrameBuffer;
//全局的上下文
private EglBase eglBase;
//信號的轉(zhuǎn)化
private YuvConverter yuvConverter;
//構(gòu)造方法,四個參數(shù)分別是目標文件眯勾,文件的寬,文件的高度婆誓,上下文
public VideoFileRenderer(String outputFile, int outputFileWidth, int outputFileHeight,
final EglBase.Context sharedContext) throws IOException {
//向外寫文件寬高都應(yīng)該是偶數(shù)
if ((outputFileWidth % 2) == 1 || (outputFileHeight % 2) == 1) {
throw new IllegalArgumentException("Does not support uneven width or height");
}
this.outputFileWidth = outputFileWidth;
this.outputFileHeight = outputFileHeight;
//計算frameSize
outputFrameSize = outputFileWidth * outputFileHeight * 3 / 2;
outputFrameBuffer = ByteBuffer.allocateDirect(outputFrameSize);
//寫文件
videoOutFile = new FileOutputStream(outputFile);
videoOutFile.write(
("YUV4MPEG2 C420 W" + outputFileWidth + " H" + outputFileHeight + " Ip F30:1 A1:1\n")
.getBytes());
//開啟一個新線程
renderThread = new HandlerThread(TAG);
renderThread.start();
renderThreadHandler = new Handler(renderThread.getLooper());
ThreadUtils.invokeAtFrontUninterruptibly(renderThreadHandler, new Runnable() {
@Override
public void run() {
//在新的線程中初始化全局吃环,并且進行編碼轉(zhuǎn)化
eglBase = EglBase.create(sharedContext, EglBase.CONFIG_PIXEL_BUFFER);
eglBase.createDummyPbufferSurface();
eglBase.makeCurrent();
yuvConverter = new YuvConverter();
}
});
}
@Override
public void renderFrame(final VideoRenderer.I420Frame frame) {
renderThreadHandler.post(new Runnable() {
@Override
public void run() {
//當有流產(chǎn)生的時候要進行回調(diào)4
renderFrameOnRenderThread(frame);
}
});
}
private void renderFrameOnRenderThread(VideoRenderer.I420Frame frame) {
//呈現(xiàn)畫面在新的線程中
//畫面呈現(xiàn)的比例
final float frameAspectRatio = (float) frame.rotatedWidth() / (float) frame.rotatedHeight();
//旋轉(zhuǎn)抽樣矩陣
final float[] rotatedSamplingMatrix =
RendererCommon.rotateTextureMatrix(frame.samplingMatrix, frame.rotationDegree);
final float[] layoutMatrix = RendererCommon.getLayoutMatrix(
false, frameAspectRatio, (float) outputFileWidth / outputFileHeight);
final float[] texMatrix = RendererCommon.multiplyMatrices(rotatedSamplingMatrix, layoutMatrix);
//==========以下是圖像處理的代碼,真的看不太懂洋幻,日后鉆研明白再來解析====================
try {
videoOutFile.write("FRAME\n".getBytes());
if (!frame.yuvFrame) {
yuvConverter.convert(outputFrameBuffer, outputFileWidth, outputFileHeight, outputFileWidth,
frame.textureId, texMatrix);
int stride = outputFileWidth;
byte[] data = outputFrameBuffer.array();
int offset = outputFrameBuffer.arrayOffset();
// Write Y
videoOutFile.write(data, offset, outputFileWidth * outputFileHeight);
// Write U
for (int r = outputFileHeight; r < outputFileHeight * 3 / 2; ++r) {
videoOutFile.write(data, offset + r * stride, stride / 2);
}
// Write V
for (int r = outputFileHeight; r < outputFileHeight * 3 / 2; ++r) {
videoOutFile.write(data, offset + r * stride + stride / 2, stride / 2);
}
} else {
nativeI420Scale(frame.yuvPlanes[0], frame.yuvStrides[0], frame.yuvPlanes[1],
frame.yuvStrides[1], frame.yuvPlanes[2], frame.yuvStrides[2], frame.width, frame.height,
outputFrameBuffer, outputFileWidth, outputFileHeight);
videoOutFile.write(
outputFrameBuffer.array(), outputFrameBuffer.arrayOffset(), outputFrameSize);
}
} catch (IOException e) {
Logging.e(TAG, "Failed to write to file for video out");
throw new RuntimeException(e);
} finally {
VideoRenderer.renderFrameDone(frame);
}
}
//釋放資源郁轻,關(guān)閉文件等處理
public void release() {
final CountDownLatch cleanupBarrier = new CountDownLatch(1);
renderThreadHandler.post(new Runnable() {
@Override
public void run() {
try {
videoOutFile.close();
} catch (IOException e) {
Logging.d(TAG, "Error closing output video file");
}
yuvConverter.release();
eglBase.release();
renderThread.quit();
cleanupBarrier.countDown();
}
});
ThreadUtils.awaitUninterruptibly(cleanupBarrier);
}