mRunOnDraw;
- private final int mVertexShaderId;
- private final int mFragmentShaderId;
-
- private int mGLProgId;
- private int mGLPositionIndex;
- private int mGLInputImageTextureIndex;
- private int mGLTextureCoordinateIndex;
- private int mGLTextureTransformIndex;
-
- protected int mInputWidth;
- protected int mInputHeight;
- protected int mOutputWidth;
- protected int mOutputHeight;
- protected FloatBuffer mGLCubeBuffer;
- protected FloatBuffer mGLTextureBuffer;
-
- private int[] mGLCubeId;
- private int[] mGLTextureCoordinateId;
- private float[] mGLTextureTransformMatrix;
-
- private int[] mGLFboId;
- private int[] mGLFboTexId;
- private IntBuffer mGLFboBuffer;
-
- public GPUImageFilter() {
- this(MagicFilterType.NONE);
- }
-
- public GPUImageFilter(MagicFilterType type) {
- this(type, R.raw.vertex, R.raw.fragment);
- }
-
- public GPUImageFilter(MagicFilterType type, int fragmentShaderId) {
- this(type, R.raw.vertex, fragmentShaderId);
- }
-
- public GPUImageFilter(MagicFilterType type, int vertexShaderId, int fragmentShaderId) {
- mType = type;
- mRunOnDraw = new LinkedList<>();
- mVertexShaderId = vertexShaderId;
- mFragmentShaderId = fragmentShaderId;
-
- mGLCubeBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.CUBE.length * 4)
- .order(ByteOrder.nativeOrder())
- .asFloatBuffer();
- mGLCubeBuffer.put(TextureRotationUtil.CUBE).position(0);
-
- mGLTextureBuffer = ByteBuffer.allocateDirect(TextureRotationUtil.TEXTURE_NO_ROTATION.length * 4)
- .order(ByteOrder.nativeOrder())
- .asFloatBuffer();
- mGLTextureBuffer.put(TextureRotationUtil.getRotation(Rotation.NORMAL, false, true)).position(0);
- }
-
- public void init(Context context) {
- mContext = context;
- onInit();
- onInitialized();
- }
-
- protected void onInit() {
- initVbo();
- loadSamplerShader();
- }
-
- protected void onInitialized() {
- mIsInitialized = true;
- }
-
- public final void destroy() {
- mIsInitialized = false;
- destroyFboTexture();
- destoryVbo();
- GLES20.glDeleteProgram(mGLProgId);
- onDestroy();
- }
-
- protected void onDestroy() {
- }
-
- public void onInputSizeChanged(final int width, final int height) {
- mInputWidth = width;
- mInputHeight = height;
- initFboTexture(width, height);
- }
-
- public void onDisplaySizeChanged(final int width, final int height) {
- mOutputWidth = width;
- mOutputHeight = height;
- }
-
- private void loadSamplerShader() {
- mGLProgId = OpenGLUtils.loadProgram(OpenGLUtils.readShaderFromRawResource(getContext(), mVertexShaderId),
- OpenGLUtils.readShaderFromRawResource(getContext(), mFragmentShaderId));
- mGLPositionIndex = GLES20.glGetAttribLocation(mGLProgId, "position");
- mGLTextureCoordinateIndex = GLES20.glGetAttribLocation(mGLProgId,"inputTextureCoordinate");
- mGLTextureTransformIndex = GLES20.glGetUniformLocation(mGLProgId, "textureTransform");
- mGLInputImageTextureIndex = GLES20.glGetUniformLocation(mGLProgId, "inputImageTexture");
- }
-
- private void initVbo() {
- mGLCubeId = new int[1];
- mGLTextureCoordinateId = new int[1];
-
- GLES20.glGenBuffers(1, mGLCubeId, 0);
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLCubeId[0]);
- GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mGLCubeBuffer.capacity() * 4, mGLCubeBuffer, GLES20.GL_STATIC_DRAW);
-
- GLES20.glGenBuffers(1, mGLTextureCoordinateId, 0);
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLTextureCoordinateId[0]);
- GLES20.glBufferData(GLES20.GL_ARRAY_BUFFER, mGLTextureBuffer.capacity() * 4, mGLTextureBuffer, GLES20.GL_STATIC_DRAW);
- }
-
- private void destoryVbo() {
- if (mGLCubeId != null) {
- GLES20.glDeleteBuffers(1, mGLCubeId, 0);
- mGLCubeId = null;
- }
- if (mGLTextureCoordinateId != null) {
- GLES20.glDeleteBuffers(1, mGLTextureCoordinateId, 0);
- mGLTextureCoordinateId = null;
- }
- }
-
- private void initFboTexture(int width, int height) {
- if (mGLFboId != null && (mInputWidth != width || mInputHeight != height)) {
- destroyFboTexture();
- }
-
- mGLFboId = new int[1];
- mGLFboTexId = new int[1];
- mGLFboBuffer = IntBuffer.allocate(width * height);
-
- GLES20.glGenFramebuffers(1, mGLFboId, 0);
- GLES20.glGenTextures(1, mGLFboTexId, 0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, mGLFboTexId[0]);
- GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height, 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, null);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mGLFboId[0]);
- GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0, GLES20.GL_TEXTURE_2D, mGLFboTexId[0], 0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
- }
-
- private void destroyFboTexture() {
- if (mGLFboTexId != null) {
- GLES20.glDeleteTextures(1, mGLFboTexId, 0);
- mGLFboTexId = null;
- }
- if (mGLFboId != null) {
- GLES20.glDeleteFramebuffers(1, mGLFboId, 0);
- mGLFboId = null;
- }
- }
-
- public int onDrawFrame(final int textureId, final FloatBuffer cubeBuffer, final FloatBuffer textureBuffer) {
- if (!mIsInitialized) {
- return OpenGLUtils.NOT_INIT;
- }
-
- GLES20.glUseProgram(mGLProgId);
- runPendingOnDrawTasks();
-
- GLES20.glEnableVertexAttribArray(mGLPositionIndex);
- GLES20.glVertexAttribPointer(mGLPositionIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, cubeBuffer);
-
- GLES20.glEnableVertexAttribArray(mGLTextureCoordinateIndex);
- GLES20.glVertexAttribPointer(mGLTextureCoordinateIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, textureBuffer);
-
- if (textureId != OpenGLUtils.NO_TEXTURE) {
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureId);
- GLES20.glUniform1i(mGLInputImageTextureIndex, 0);
- }
-
- onDrawArraysPre();
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
- onDrawArraysAfter();
-
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, 0);
-
- GLES20.glDisableVertexAttribArray(mGLPositionIndex);
- GLES20.glDisableVertexAttribArray(mGLTextureCoordinateIndex);
-
- return OpenGLUtils.ON_DRAWN;
- }
-
- public int onDrawFrame(int cameraTextureId) {
- if (!mIsInitialized) {
- return OpenGLUtils.NOT_INIT;
- }
-
- if (mGLFboId == null) {
- return OpenGLUtils.NO_TEXTURE;
- }
-
- GLES20.glUseProgram(mGLProgId);
- runPendingOnDrawTasks();
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLCubeId[0]);
- GLES20.glEnableVertexAttribArray(mGLPositionIndex);
- GLES20.glVertexAttribPointer(mGLPositionIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, 0);
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, mGLTextureCoordinateId[0]);
- GLES20.glEnableVertexAttribArray(mGLTextureCoordinateIndex);
- GLES20.glVertexAttribPointer(mGLTextureCoordinateIndex, 2, GLES20.GL_FLOAT, false, 4 * 2, 0);
-
- GLES20.glUniformMatrix4fv(mGLTextureTransformIndex, 1, false, mGLTextureTransformMatrix, 0);
-
- GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
- GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, cameraTextureId);
- GLES20.glUniform1i(mGLInputImageTextureIndex, 0);
-
- onDrawArraysPre();
-
- GLES20.glViewport(0, 0, mInputWidth, mInputHeight);
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, mGLFboId[0]);
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
- GLES20.glReadPixels(0, 0, mInputWidth, mInputHeight, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, mGLFboBuffer);
- GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER, 0);
- GLES20.glViewport(0, 0, mOutputWidth, mOutputHeight);
-
- GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP, 0, 4);
-
- onDrawArraysAfter();
-
- GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, 0);
-
- GLES20.glDisableVertexAttribArray(mGLPositionIndex);
- GLES20.glDisableVertexAttribArray(mGLTextureCoordinateIndex);
-
- GLES20.glBindBuffer(GLES20.GL_ARRAY_BUFFER, 0);
-
- return mGLFboTexId[0];
- }
-
- protected void onDrawArraysPre() {}
-
- protected void onDrawArraysAfter() {}
-
- private void runPendingOnDrawTasks() {
- while (!mRunOnDraw.isEmpty()) {
- mRunOnDraw.removeFirst().run();
- }
- }
-
- public int getProgram() {
- return mGLProgId;
- }
-
- public IntBuffer getGLFboBuffer() {
- return mGLFboBuffer;
- }
-
- protected Context getContext() {
- return mContext;
- }
-
- protected MagicFilterType getFilterType() {
- return mType;
- }
-
- public void setTextureTransformMatrix(float[] mtx){
- mGLTextureTransformMatrix = mtx;
- }
-
- protected void setInteger(final int location, final int intValue) {
- runOnDraw(new Runnable() {
- @Override
- public void run() {
- GLES20.glUniform1i(location, intValue);
- }
- });
- }
-
- protected void setFloat(final int location, final float floatValue) {
- runOnDraw(new Runnable() {
- @Override
- public void run() {
- GLES20.glUniform1f(location, floatValue);
- }
- });
- }
-
- protected void setFloatVec2(final int location, final float[] arrayValue) {
- runOnDraw(new Runnable() {
- @Override
- public void run() {
- GLES20.glUniform2fv(location, 1, FloatBuffer.wrap(arrayValue));
- }
- });
- }
-
- protected void setFloatVec3(final int location, final float[] arrayValue) {
- runOnDraw(new Runnable() {
- @Override
- public void run() {
- GLES20.glUniform3fv(location, 1, FloatBuffer.wrap(arrayValue));
- }
- });
- }
-
- protected void setFloatVec4(final int location, final float[] arrayValue) {
- runOnDraw(new Runnable() {
- @Override
- public void run() {
- GLES20.glUniform4fv(location, 1, FloatBuffer.wrap(arrayValue));
- }
- });
- }
-
- protected void setFloatArray(final int location, final float[] arrayValue) {
- runOnDraw(new Runnable() {
- @Override
- public void run() {
- GLES20.glUniform1fv(location, arrayValue.length, FloatBuffer.wrap(arrayValue));
- }
- });
- }
-
- protected void setPoint(final int location, final PointF point) {
- runOnDraw(new Runnable() {
-
- @Override
- public void run() {
- float[] vec2 = new float[2];
- vec2[0] = point.x;
- vec2[1] = point.y;
- GLES20.glUniform2fv(location, 1, vec2, 0);
- }
- });
- }
-
- protected void setUniformMatrix3f(final int location, final float[] matrix) {
- runOnDraw(new Runnable() {
-
- @Override
- public void run() {
- GLES20.glUniformMatrix3fv(location, 1, false, matrix, 0);
- }
- });
- }
-
- protected void setUniformMatrix4f(final int location, final float[] matrix) {
- runOnDraw(new Runnable() {
-
- @Override
- public void run() {
- GLES20.glUniformMatrix4fv(location, 1, false, matrix, 0);
- }
- });
- }
-
- protected void runOnDraw(final Runnable runnable) {
- synchronized (mRunOnDraw) {
- mRunOnDraw.addLast(runnable);
- }
- }
-}
-
diff --git a/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageHueFilter.java b/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageHueFilter.java
deleted file mode 100755
index b2d9361..0000000
--- a/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageHueFilter.java
+++ /dev/null
@@ -1,56 +0,0 @@
-/*
- * Copyright (C) 2012 CyberAgent
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.seu.magicfilter.base.gpuimage;
-
-import android.opengl.GLES20;
-
-import com.seu.magicfilter.utils.MagicFilterType;
-
-import com.reactlibrary.R;
-
-public class GPUImageHueFilter extends GPUImageFilter {
-
- private float mHue;
- private int mHueLocation;
-
- public GPUImageHueFilter() {
- this(0.0f);
- }
-
- public GPUImageHueFilter(final float hue) {
- super(MagicFilterType.HUE, R.raw.hue);
- mHue = hue;
- }
-
- @Override
- public void onInit() {
- super.onInit();
- mHueLocation = GLES20.glGetUniformLocation(getProgram(), "hueAdjust");
- }
-
- @Override
- public void onInitialized() {
- super.onInitialized();
- setHue(mHue);
- }
-
- public void setHue(final float hue) {
- mHue = hue;
- float hueAdjust = (mHue % 360.0f) * (float) Math.PI / 180.0f;
- setFloat(mHueLocation, hueAdjust);
- }
-}
diff --git a/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageSaturationFilter.java b/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageSaturationFilter.java
deleted file mode 100755
index 63fe898..0000000
--- a/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageSaturationFilter.java
+++ /dev/null
@@ -1,58 +0,0 @@
-/*
- * Copyright (C) 2012 CyberAgent
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.seu.magicfilter.base.gpuimage;
-
-import android.opengl.GLES20;
-
-import com.seu.magicfilter.utils.MagicFilterType;
-
-import com.reactlibrary.R;
-
-/**
- * saturation: The degree of saturation or desaturation to apply to the image (0.0 - 2.0, with 1.0 as the default)
- */
-public class GPUImageSaturationFilter extends GPUImageFilter {
-
- private int mSaturationLocation;
- private float mSaturation;
-
- public GPUImageSaturationFilter() {
- this(1.0f);
- }
-
- public GPUImageSaturationFilter(final float saturation) {
- super(MagicFilterType.SATURATION, R.raw.saturation);
- mSaturation = saturation;
- }
-
- @Override
- public void onInit() {
- super.onInit();
- mSaturationLocation = GLES20.glGetUniformLocation(getProgram(), "saturation");
- }
-
- @Override
- public void onInitialized() {
- super.onInitialized();
- setSaturation(mSaturation);
- }
-
- public void setSaturation(final float saturation) {
- mSaturation = saturation;
- setFloat(mSaturationLocation, mSaturation);
- }
-}
diff --git a/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageSharpenFilter.java b/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageSharpenFilter.java
deleted file mode 100755
index 563faa9..0000000
--- a/android/src/main/java/com/seu/magicfilter/base/gpuimage/GPUImageSharpenFilter.java
+++ /dev/null
@@ -1,66 +0,0 @@
-/*
- * Copyright (C) 2012 CyberAgent
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.seu.magicfilter.base.gpuimage;
-
-import android.opengl.GLES20;
-
-import com.seu.magicfilter.utils.MagicFilterType;
-
-import com.reactlibrary.R;
-
-/**
- * Sharpens the picture.
- *
- * sharpness: from -4.0 to 4.0, with 0.0 as the normal level
- */
-public class GPUImageSharpenFilter extends GPUImageFilter {
-
- private int mSharpnessLocation;
- private float mSharpness;
- private int mImageWidthFactorLocation;
- private int mImageHeightFactorLocation;
-
- public GPUImageSharpenFilter() {
- this(0.0f);
- }
-
- public GPUImageSharpenFilter(final float sharpness) {
- super(MagicFilterType.SHARPEN, R.raw.vertex_sharpen, R.raw.sharpen);
- mSharpness = sharpness;
- }
-
- @Override
- public void onInit() {
- super.onInit();
- mSharpnessLocation = GLES20.glGetUniformLocation(getProgram(), "sharpness");
- mImageWidthFactorLocation = GLES20.glGetUniformLocation(getProgram(), "imageWidthFactor");
- mImageHeightFactorLocation = GLES20.glGetUniformLocation(getProgram(), "imageHeightFactor");
- setSharpness(mSharpness);
- }
-
- @Override
- public void onInputSizeChanged(final int width, final int height) {
- super.onInputSizeChanged(width, height);
- setFloat(mImageWidthFactorLocation, 1.0f / width);
- setFloat(mImageHeightFactorLocation, 1.0f / height);
- }
-
- public void setSharpness(final float sharpness) {
- mSharpness = sharpness;
- setFloat(mSharpnessLocation, mSharpness);
- }
-}
diff --git a/android/src/main/java/com/seu/magicfilter/utils/MagicFilterFactory.java b/android/src/main/java/com/seu/magicfilter/utils/MagicFilterFactory.java
deleted file mode 100755
index 8daffcc..0000000
--- a/android/src/main/java/com/seu/magicfilter/utils/MagicFilterFactory.java
+++ /dev/null
@@ -1,165 +0,0 @@
-package com.seu.magicfilter.utils;
-
-import android.content.Context;
-
-import com.seu.magicfilter.advanced.MagicAmaroFilter;
-import com.seu.magicfilter.advanced.MagicAntiqueFilter;
-import com.seu.magicfilter.advanced.MagicBeautyFilter;
-import com.seu.magicfilter.advanced.MagicBlackCatFilter;
-import com.seu.magicfilter.advanced.MagicBrannanFilter;
-import com.seu.magicfilter.advanced.MagicBrooklynFilter;
-import com.seu.magicfilter.advanced.MagicCalmFilter;
-import com.seu.magicfilter.advanced.MagicCoolFilter;
-import com.seu.magicfilter.advanced.MagicCrayonFilter;
-import com.seu.magicfilter.advanced.MagicEarlyBirdFilter;
-import com.seu.magicfilter.advanced.MagicEmeraldFilter;
-import com.seu.magicfilter.advanced.MagicEvergreenFilter;
-import com.seu.magicfilter.advanced.MagicFreudFilter;
-import com.seu.magicfilter.advanced.MagicHealthyFilter;
-import com.seu.magicfilter.advanced.MagicHefeFilter;
-import com.seu.magicfilter.advanced.MagicHudsonFilter;
-import com.seu.magicfilter.advanced.MagicImageAdjustFilter;
-import com.seu.magicfilter.advanced.MagicInkwellFilter;
-import com.seu.magicfilter.advanced.MagicKevinFilter;
-import com.seu.magicfilter.advanced.MagicLatteFilter;
-import com.seu.magicfilter.advanced.MagicLomoFilter;
-import com.seu.magicfilter.advanced.MagicN1977Filter;
-import com.seu.magicfilter.advanced.MagicNashvilleFilter;
-import com.seu.magicfilter.advanced.MagicNostalgiaFilter;
-import com.seu.magicfilter.advanced.MagicPixarFilter;
-import com.seu.magicfilter.advanced.MagicRiseFilter;
-import com.seu.magicfilter.advanced.MagicRomanceFilter;
-import com.seu.magicfilter.advanced.MagicSakuraFilter;
-import com.seu.magicfilter.advanced.MagicSierraFilter;
-import com.seu.magicfilter.advanced.MagicSketchFilter;
-import com.seu.magicfilter.advanced.MagicSkinWhitenFilter;
-import com.seu.magicfilter.advanced.MagicSunriseFilter;
-import com.seu.magicfilter.advanced.MagicSunsetFilter;
-import com.seu.magicfilter.advanced.MagicSutroFilter;
-import com.seu.magicfilter.advanced.MagicSweetsFilter;
-import com.seu.magicfilter.advanced.MagicTenderFilter;
-import com.seu.magicfilter.advanced.MagicToasterFilter;
-import com.seu.magicfilter.advanced.MagicValenciaFilter;
-import com.seu.magicfilter.advanced.MagicWaldenFilter;
-import com.seu.magicfilter.advanced.MagicWarmFilter;
-import com.seu.magicfilter.advanced.MagicWhiteCatFilter;
-import com.seu.magicfilter.advanced.MagicXproIIFilter;
-import com.seu.magicfilter.base.MagicLookupFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageBrightnessFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageContrastFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageExposureFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageHueFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageSaturationFilter;
-import com.seu.magicfilter.base.gpuimage.GPUImageSharpenFilter;
-
-public class MagicFilterFactory{
-
- public static GPUImageFilter initFilters(MagicFilterType type) {
- switch (type) {
- case NONE:
- return new GPUImageFilter();
- case WHITECAT:
- return new MagicWhiteCatFilter();
- case BLACKCAT:
- return new MagicBlackCatFilter();
- case SKINWHITEN:
- return new MagicSkinWhitenFilter();
- case BEAUTY:
- return new MagicBeautyFilter();
- case ROMANCE:
- return new MagicRomanceFilter();
- case SAKURA:
- return new MagicSakuraFilter();
- case AMARO:
- return new MagicAmaroFilter();
- case WALDEN:
- return new MagicWaldenFilter();
- case ANTIQUE:
- return new MagicAntiqueFilter();
- case CALM:
- return new MagicCalmFilter();
- case BRANNAN:
- return new MagicBrannanFilter();
- case BROOKLYN:
- return new MagicBrooklynFilter();
- case EARLYBIRD:
- return new MagicEarlyBirdFilter();
- case FREUD:
- return new MagicFreudFilter();
- case HEFE:
- return new MagicHefeFilter();
- case HUDSON:
- return new MagicHudsonFilter();
- case INKWELL:
- return new MagicInkwellFilter();
- case KEVIN:
- return new MagicKevinFilter();
- case LOCKUP:
- return new MagicLookupFilter("");
- case LOMO:
- return new MagicLomoFilter();
- case N1977:
- return new MagicN1977Filter();
- case NASHVILLE:
- return new MagicNashvilleFilter();
- case PIXAR:
- return new MagicPixarFilter();
- case RISE:
- return new MagicRiseFilter();
- case SIERRA:
- return new MagicSierraFilter();
- case SUTRO:
- return new MagicSutroFilter();
- case TOASTER2:
- return new MagicToasterFilter();
- case VALENCIA:
- return new MagicValenciaFilter();
- case XPROII:
- return new MagicXproIIFilter();
- case EVERGREEN:
- return new MagicEvergreenFilter();
- case HEALTHY:
- return new MagicHealthyFilter();
- case COOL:
- return new MagicCoolFilter();
- case EMERALD:
- return new MagicEmeraldFilter();
- case LATTE:
- return new MagicLatteFilter();
- case WARM:
- return new MagicWarmFilter();
- case TENDER:
- return new MagicTenderFilter();
- case SWEETS:
- return new MagicSweetsFilter();
- case NOSTALGIA:
- return new MagicNostalgiaFilter();
- case SUNRISE:
- return new MagicSunriseFilter();
- case SUNSET:
- return new MagicSunsetFilter();
- case CRAYON:
- return new MagicCrayonFilter();
- case SKETCH:
- return new MagicSketchFilter();
- //image adjust
- case BRIGHTNESS:
- return new GPUImageBrightnessFilter();
- case CONTRAST:
- return new GPUImageContrastFilter();
- case EXPOSURE:
- return new GPUImageExposureFilter();
- case HUE:
- return new GPUImageHueFilter();
- case SATURATION:
- return new GPUImageSaturationFilter();
- case SHARPEN:
- return new GPUImageSharpenFilter();
- case IMAGE_ADJUST:
- return new MagicImageAdjustFilter();
- default:
- return null;
- }
- }
-}
diff --git a/android/src/main/java/com/seu/magicfilter/utils/MagicFilterType.java b/android/src/main/java/com/seu/magicfilter/utils/MagicFilterType.java
deleted file mode 100755
index 419c716..0000000
--- a/android/src/main/java/com/seu/magicfilter/utils/MagicFilterType.java
+++ /dev/null
@@ -1,59 +0,0 @@
-package com.seu.magicfilter.utils;
-
-/**
- * Created by why8222 on 2016/2/25.
- */
-public enum MagicFilterType {
- NONE,
- FAIRYTALE,
- SUNRISE,
- SUNSET,
- WHITECAT,
- BLACKCAT,
- SKINWHITEN,
- BEAUTY,
- HEALTHY,
- SWEETS,
- ROMANCE,
- SAKURA,
- WARM,
- ANTIQUE,
- NOSTALGIA,
- CALM,
- LATTE,
- TENDER,
- COOL,
- EMERALD,
- EVERGREEN,
- CRAYON,
- SKETCH,
- AMARO,
- BRANNAN,
- BROOKLYN,
- EARLYBIRD,
- FREUD,
- HEFE,
- HUDSON,
- INKWELL,
- KEVIN,
- LOCKUP,
- LOMO,
- N1977,
- NASHVILLE,
- PIXAR,
- RISE,
- SIERRA,
- SUTRO,
- TOASTER2,
- VALENCIA,
- WALDEN,
- XPROII,
- //image adjust
- CONTRAST,
- BRIGHTNESS,
- EXPOSURE,
- HUE,
- SATURATION,
- SHARPEN,
- IMAGE_ADJUST
-}
diff --git a/android/src/main/java/com/seu/magicfilter/utils/OpenGLUtils.java b/android/src/main/java/com/seu/magicfilter/utils/OpenGLUtils.java
deleted file mode 100755
index b84e54b..0000000
--- a/android/src/main/java/com/seu/magicfilter/utils/OpenGLUtils.java
+++ /dev/null
@@ -1,230 +0,0 @@
-package com.seu.magicfilter.utils;
-
-import java.io.BufferedReader;
-import java.io.IOException;
-import java.io.InputStream;
-import java.io.InputStreamReader;
-import java.nio.Buffer;
-
-import javax.microedition.khronos.opengles.GL10;
-
-import android.content.Context;
-import android.content.res.AssetManager;
-import android.graphics.Bitmap;
-import android.graphics.BitmapFactory;
-import android.opengl.GLES11Ext;
-import android.opengl.GLES20;
-import android.opengl.GLUtils;
-import android.util.Log;
-
-public class OpenGLUtils {
- public static final int NO_TEXTURE = -1;
- public static final int NOT_INIT = -1;
- public static final int ON_DRAWN = 1;
-
- public static int loadTexture(Bitmap img, int usedTexId) {
- return loadTexture(img, usedTexId, false);
- }
-
- public static int loadTexture(Bitmap img, int usedTexId, boolean recyled) {
- if(img == null)
- return NO_TEXTURE;
- int textures[] = new int[1];
- if (usedTexId == NO_TEXTURE) {
- GLES20.glGenTextures(1, textures, 0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
-
- GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, img, 0);
- } else {
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, usedTexId);
- GLUtils.texSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, img);
- textures[0] = usedTexId;
- }
- if(recyled)
- img.recycle();
- return textures[0];
- }
-
- public static int loadTexture(Buffer data, int width, int height, int usedTexId) {
- if(data == null)
- return NO_TEXTURE;
- int textures[] = new int[1];
- if (usedTexId == NO_TEXTURE) {
- GLES20.glGenTextures(1, textures, 0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height,
- 0, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, data);
- } else {
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, usedTexId);
- GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
- height, GLES20.GL_RGBA, GLES20.GL_UNSIGNED_BYTE, data);
- textures[0] = usedTexId;
- }
- return textures[0];
- }
-
- public static int loadTexture(Buffer data, int width, int height, int usedTexId, int type) {
- if(data == null)
- return NO_TEXTURE;
- int textures[] = new int[1];
- if (usedTexId == NO_TEXTURE) {
- GLES20.glGenTextures(1, textures, 0);
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textures[0]);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameterf(GLES20.GL_TEXTURE_2D,
- GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexImage2D(GLES20.GL_TEXTURE_2D, 0, GLES20.GL_RGBA, width, height,
- 0, GLES20.GL_RGBA, type, data);
- } else {
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, usedTexId);
- GLES20.glTexSubImage2D(GLES20.GL_TEXTURE_2D, 0, 0, 0, width,
- height, GLES20.GL_RGBA, type, data);
- textures[0] = usedTexId;
- }
- return textures[0];
- }
-
- public static int loadTexture(final Context context, final String name){
- final int[] textureHandle = new int[1];
-
- GLES20.glGenTextures(1, textureHandle, 0);
-
- if (textureHandle[0] != 0){
-
- // Read in the resource
- final Bitmap bitmap = getImageFromAssetsFile(context,name);
-
- // Bind to the texture in OpenGL
- GLES20.glBindTexture(GLES20.GL_TEXTURE_2D, textureHandle[0]);
-
- // Set filtering
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MAG_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_MIN_FILTER, GLES20.GL_LINEAR);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_S, GLES20.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(GLES20.GL_TEXTURE_2D, GLES20.GL_TEXTURE_WRAP_T, GLES20.GL_CLAMP_TO_EDGE);
- // Load the bitmap into the bound texture.
- GLUtils.texImage2D(GLES20.GL_TEXTURE_2D, 0, bitmap, 0);
-
- // Recycle the bitmap, since its data has been loaded into OpenGL.
- bitmap.recycle();
- }
-
- if (textureHandle[0] == 0){
- throw new RuntimeException("Error loading texture.");
- }
-
- return textureHandle[0];
- }
-
- private static Bitmap getImageFromAssetsFile(Context context,String fileName){
- Bitmap image = null;
- AssetManager am = context.getResources().getAssets();
- try{
- InputStream is = am.open(fileName);
- image = BitmapFactory.decodeStream(is);
- is.close();
- }catch (IOException e){
- e.printStackTrace();
- }
- return image;
- }
-
- public static int loadProgram(String strVSource, String strFSource) {
- int iVShader;
- int iFShader;
- int iProgId;
- int[] link = new int[1];
- iVShader = loadShader(strVSource, GLES20.GL_VERTEX_SHADER);
- if (iVShader == 0) {
- Log.d("Load Program", "Vertex Shader Failed");
- return 0;
- }
- iFShader = loadShader(strFSource, GLES20.GL_FRAGMENT_SHADER);
- if (iFShader == 0) {
- Log.d("Load Program", "Fragment Shader Failed");
- return 0;
- }
-
- iProgId = GLES20.glCreateProgram();
- GLES20.glAttachShader(iProgId, iVShader);
- GLES20.glAttachShader(iProgId, iFShader);
- GLES20.glLinkProgram(iProgId);
- GLES20.glGetProgramiv(iProgId, GLES20.GL_LINK_STATUS, link, 0);
- if (link[0] <= 0) {
- Log.d("Load Program", "Linking Failed");
- return 0;
- }
- GLES20.glDeleteShader(iVShader);
- GLES20.glDeleteShader(iFShader);
- return iProgId;
- }
-
- private static int loadShader(String strSource, int iType) {
- int[] compiled = new int[1];
- int iShader = GLES20.glCreateShader(iType);
- GLES20.glShaderSource(iShader, strSource);
- GLES20.glCompileShader(iShader);
- GLES20.glGetShaderiv(iShader, GLES20.GL_COMPILE_STATUS, compiled, 0);
- if (compiled[0] == 0) {
- Log.e("Load Shader Failed", "Compilation\n" + GLES20.glGetShaderInfoLog(iShader));
- return 0;
- }
- return iShader;
- }
-
- public static int getExternalOESTextureID(){
- int[] texture = new int[1];
- GLES20.glGenTextures(1, texture, 0);
- GLES20.glBindTexture(GLES11Ext.GL_TEXTURE_EXTERNAL_OES, texture[0]);
- GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
- GL10.GL_TEXTURE_MIN_FILTER,GL10.GL_LINEAR);
- GLES20.glTexParameterf(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
- GL10.GL_TEXTURE_MAG_FILTER, GL10.GL_LINEAR);
- GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
- GL10.GL_TEXTURE_WRAP_S, GL10.GL_CLAMP_TO_EDGE);
- GLES20.glTexParameteri(GLES11Ext.GL_TEXTURE_EXTERNAL_OES,
- GL10.GL_TEXTURE_WRAP_T, GL10.GL_CLAMP_TO_EDGE);
- return texture[0];
- }
-
- public static String readShaderFromRawResource(Context context, int resourceId){
- final InputStream inputStream = context.getResources().openRawResource(resourceId);
- final InputStreamReader inputStreamReader = new InputStreamReader(inputStream);
- final BufferedReader bufferedReader = new BufferedReader(inputStreamReader);
-
- String nextLine;
- final StringBuilder body = new StringBuilder();
-
- try{
- while ((nextLine = bufferedReader.readLine()) != null){
- body.append(nextLine);
- body.append('\n');
- }
- }
- catch (IOException e){
- return null;
- }
- return body.toString();
- }
-}
diff --git a/android/src/main/java/com/seu/magicfilter/utils/Rotation.java b/android/src/main/java/com/seu/magicfilter/utils/Rotation.java
deleted file mode 100755
index 105cc78..0000000
--- a/android/src/main/java/com/seu/magicfilter/utils/Rotation.java
+++ /dev/null
@@ -1,54 +0,0 @@
-/*
- * Copyright (C) 2012 CyberAgent
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.seu.magicfilter.utils;
-
-public enum Rotation {
- NORMAL, ROTATION_90, ROTATION_180, ROTATION_270;
-
- /**
- * Retrieves the int representation of the Rotation.
- *
- * @return 0, 90, 180 or 270
- */
- public int asInt() {
- switch (this) {
- case NORMAL: return 0;
- case ROTATION_90: return 90;
- case ROTATION_180: return 180;
- case ROTATION_270: return 270;
- default: throw new IllegalStateException("Unknown Rotation!");
- }
- }
-
- /**
- * Create a Rotation from an integer. Needs to be either 0, 90, 180 or 270.
- *
- * @param rotation 0, 90, 180 or 270
- * @return Rotation object
- */
- public static Rotation fromInt(int rotation) {
- switch (rotation) {
- case 0: return NORMAL;
- case 90: return ROTATION_90;
- case 180: return ROTATION_180;
- case 270: return ROTATION_270;
- case 360: return NORMAL;
- default: throw new IllegalStateException(
- rotation + " is an unknown rotation. Needs to be either 0, 90, 180 or 270!");
- }
- }
-}
diff --git a/android/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java b/android/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java
deleted file mode 100755
index 4b28fea..0000000
--- a/android/src/main/java/com/seu/magicfilter/utils/TextureRotationUtil.java
+++ /dev/null
@@ -1,96 +0,0 @@
-/*
- * Copyright (C) 2012 CyberAgent
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-package com.seu.magicfilter.utils;
-
-public class TextureRotationUtil {
-
- public static final float TEXTURE_NO_ROTATION[] = {
- 0.0f, 1.0f,
- 1.0f, 1.0f,
- 0.0f, 0.0f,
- 1.0f, 0.0f,
- };
-
- public static final float TEXTURE_ROTATED_90[] = {
- 1.0f, 1.0f,
- 1.0f, 0.0f,
- 0.0f, 1.0f,
- 0.0f, 0.0f,
- };
- public static final float TEXTURE_ROTATED_180[] = {
- 1.0f, 0.0f,
- 0.0f, 0.0f,
- 1.0f, 1.0f,
- 0.0f, 1.0f,
- };
- public static final float TEXTURE_ROTATED_270[] = {
- 0.0f, 0.0f,
- 0.0f, 1.0f,
- 1.0f, 0.0f,
- 1.0f, 1.0f,
- };
-
- public static final float CUBE[] = {
- -1.0f, -1.0f,
- 1.0f, -1.0f,
- -1.0f, 1.0f,
- 1.0f, 1.0f,
- };
-
- private TextureRotationUtil() {}
-
- public static float[] getRotation(final Rotation rotation, final boolean flipHorizontal,
- final boolean flipVertical) {
- float[] rotatedTex;
- switch (rotation) {
- case ROTATION_90:
- rotatedTex = TEXTURE_ROTATED_90;
- break;
- case ROTATION_180:
- rotatedTex = TEXTURE_ROTATED_180;
- break;
- case ROTATION_270:
- rotatedTex = TEXTURE_ROTATED_270;
- break;
- case NORMAL:
- default:
- rotatedTex = TEXTURE_NO_ROTATION;
- break;
- }
- if (flipHorizontal) {
- rotatedTex = new float[]{
- flip(rotatedTex[0]), rotatedTex[1],
- flip(rotatedTex[2]), rotatedTex[3],
- flip(rotatedTex[4]), rotatedTex[5],
- flip(rotatedTex[6]), rotatedTex[7],
- };
- }
- if (flipVertical) {
- rotatedTex = new float[]{
- rotatedTex[0], flip(rotatedTex[1]),
- rotatedTex[2], flip(rotatedTex[3]),
- rotatedTex[4], flip(rotatedTex[5]),
- rotatedTex[6], flip(rotatedTex[7]),
- };
- }
- return rotatedTex;
- }
-
- private static float flip(final float i) {
- return i == 0.0f ? 1.0f : 0.0f;
- }
-}
diff --git a/android/src/main/java/net/ossrs/yasea/SrsAllocator.java b/android/src/main/java/net/ossrs/yasea/SrsAllocator.java
deleted file mode 100755
index d50dd57..0000000
--- a/android/src/main/java/net/ossrs/yasea/SrsAllocator.java
+++ /dev/null
@@ -1,116 +0,0 @@
-package net.ossrs.yasea;
-
-import java.util.Arrays;
-
-public final class SrsAllocator {
-
- public class Allocation {
-
- private byte[] data;
- private int size;
-
- public Allocation(int size) {
- this.data = new byte[size];
- this.size = 0;
- }
-
- public byte[] array() {
- return data;
- }
-
- public int size() {
- return size;
- }
-
- public void appendOffset(int offset) {
- size += offset;
- }
-
- public void clear() {
- size = 0;
- }
-
- public void put(byte b) {
- data[size++] = b;
- }
-
- public void put(byte b, int pos) {
- data[pos++] = b;
- size = pos > size ? pos : size;
- }
-
- public void put(short s) {
- put((byte) s);
- put((byte) (s >>> 8));
- }
-
- public void put(int i) {
- put((byte) i);
- put((byte) (i >>> 8));
- put((byte) (i >>> 16));
- put((byte) (i >>> 24));
- }
-
- public void put(byte[] bs) {
- System.arraycopy(bs, 0, data, size, bs.length);
- size += bs.length;
- }
- }
-
- private final int individualAllocationSize;
- private volatile int availableSentinel;
- private Allocation[] availableAllocations;
-
- /**
- * Constructs an instance without creating any {@link Allocation}s up front.
- *
- * @param individualAllocationSize The length of each individual {@link Allocation}.
- */
- public SrsAllocator(int individualAllocationSize) {
- this(individualAllocationSize, 0);
- }
-
- /**
- * Constructs an instance with some {@link Allocation}s created up front.
- *
- *
- * @param individualAllocationSize The length of each individual {@link Allocation}.
- * @param initialAllocationCount The number of allocations to create up front.
- */
- public SrsAllocator(int individualAllocationSize, int initialAllocationCount) {
- this.individualAllocationSize = individualAllocationSize;
- this.availableSentinel = initialAllocationCount + 10;
- this.availableAllocations = new Allocation[availableSentinel];
- for (int i = 0; i < availableSentinel; i++) {
- availableAllocations[i] = new Allocation(individualAllocationSize);
- }
- }
-
- public synchronized Allocation allocate(int size) {
- for (int i = 0; i < availableSentinel; i++) {
- if (availableAllocations[i].size() >= size) {
- Allocation ret = availableAllocations[i];
- availableAllocations[i] = null;
- return ret;
- }
- }
-
- return new Allocation(size > individualAllocationSize ? size : individualAllocationSize);
- }
-
- public synchronized void release(Allocation allocation) {
- allocation.clear();
-
- for (int i = 0; i < availableSentinel; i++) {
- if (availableAllocations[i].size() == 0) {
- availableAllocations[i] = allocation;
- return;
- }
- }
-
- if (availableSentinel + 1 > availableAllocations.length) {
- availableAllocations = Arrays.copyOf(availableAllocations, availableAllocations.length * 2);
- }
- availableAllocations[availableSentinel++] = allocation;
- }
-}
diff --git a/android/src/main/java/net/ossrs/yasea/SrsCameraView.java b/android/src/main/java/net/ossrs/yasea/SrsCameraView.java
deleted file mode 100755
index ca38afb..0000000
--- a/android/src/main/java/net/ossrs/yasea/SrsCameraView.java
+++ /dev/null
@@ -1,391 +0,0 @@
-package net.ossrs.yasea;
-
-import android.content.Context;
-import android.content.res.Configuration;
-import android.graphics.ImageFormat;
-import android.graphics.SurfaceTexture;
-import android.hardware.Camera;
-import android.opengl.GLES20;
-import android.opengl.GLSurfaceView;
-import android.opengl.Matrix;
-import android.os.Build;
-import android.util.AttributeSet;
-
-import com.seu.magicfilter.base.gpuimage.GPUImageFilter;
-import com.seu.magicfilter.utils.MagicFilterFactory;
-import com.seu.magicfilter.utils.MagicFilterType;
-import com.seu.magicfilter.utils.OpenGLUtils;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.IntBuffer;
-import java.util.List;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-import javax.microedition.khronos.egl.EGLConfig;
-import javax.microedition.khronos.opengles.GL10;
-
-/**
- * Created by Leo Ma on 2016/2/25.
- */
-public class SrsCameraView extends GLSurfaceView implements GLSurfaceView.Renderer {
-
- private GPUImageFilter magicFilter;
- private SurfaceTexture surfaceTexture;
- private int mOESTextureId = OpenGLUtils.NO_TEXTURE;
- private int mSurfaceWidth;
- private int mSurfaceHeight;
- private int mPreviewWidth;
- private int mPreviewHeight;
- private boolean mIsEncoding;
- private float mInputAspectRatio;
- private float mOutputAspectRatio;
- private float[] mProjectionMatrix = new float[16];
- private float[] mSurfaceMatrix = new float[16];
- private float[] mTransformMatrix = new float[16];
-
- private Camera mCamera;
- private ByteBuffer mGLPreviewBuffer;
- private int mCamId = -1;
- private int mPreviewRotation = 90;
- private int mPreviewOrientation = Configuration.ORIENTATION_PORTRAIT;
-
- private Thread worker;
- private final Object writeLock = new Object();
- private ConcurrentLinkedQueue mGLIntBufferCache = new ConcurrentLinkedQueue<>();
- private PreviewCallback mPrevCb;
-
- public SrsCameraView(Context context) {
- this(context, null);
- }
-
- public SrsCameraView(Context context, AttributeSet attrs) {
- super(context, attrs);
-
- setEGLContextClientVersion(2);
- setRenderer(this);
- setRenderMode(GLSurfaceView.RENDERMODE_WHEN_DIRTY);
- }
-
- @Override
- public void onSurfaceCreated(GL10 gl, EGLConfig config) {
- GLES20.glDisable(GL10.GL_DITHER);
- GLES20.glClearColor(0, 0, 0, 0);
-
- magicFilter = new GPUImageFilter(MagicFilterType.NONE);
- magicFilter.init(getContext().getApplicationContext());
- magicFilter.onInputSizeChanged(mPreviewWidth, mPreviewHeight);
-
- mOESTextureId = OpenGLUtils.getExternalOESTextureID();
- surfaceTexture = new SurfaceTexture(mOESTextureId);
- surfaceTexture.setOnFrameAvailableListener(new SurfaceTexture.OnFrameAvailableListener() {
- @Override
- public void onFrameAvailable(SurfaceTexture surfaceTexture) {
- requestRender();
- }
- });
-
- // For camera preview on activity creation
- if (mCamera != null) {
- try {
- mCamera.setPreviewTexture(surfaceTexture);
- } catch (IOException ioe) {
- ioe.printStackTrace();
- }
- }
- }
-
- @Override
- public void onSurfaceChanged(GL10 gl, int width, int height) {
- GLES20.glViewport(0, 0, width, height);
- mSurfaceWidth = width;
- mSurfaceHeight = height;
- magicFilter.onDisplaySizeChanged(width, height);
-
- mOutputAspectRatio = width > height ? (float) width / height : (float) height / width;
- float aspectRatio = mOutputAspectRatio / mInputAspectRatio;
- if (width > height) {
- Matrix.orthoM(mProjectionMatrix, 0, -1.0f, 1.0f, -aspectRatio, aspectRatio, -1.0f, 1.0f);
- } else {
- Matrix.orthoM(mProjectionMatrix, 0, -aspectRatio, aspectRatio, -1.0f, 1.0f, -1.0f, 1.0f);
- }
- }
-
- @Override
- public void onDrawFrame(GL10 gl) {
- GLES20.glClearColor(0.0f, 0.0f, 0.0f, 0.0f);
- GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
-
- surfaceTexture.updateTexImage();
-
- surfaceTexture.getTransformMatrix(mSurfaceMatrix);
- Matrix.multiplyMM(mTransformMatrix, 0, mSurfaceMatrix, 0, mProjectionMatrix, 0);
- magicFilter.setTextureTransformMatrix(mTransformMatrix);
- magicFilter.onDrawFrame(mOESTextureId);
-
- if (mIsEncoding) {
- mGLIntBufferCache.add(magicFilter.getGLFboBuffer());
- synchronized (writeLock) {
- writeLock.notifyAll();
- }
- }
- }
-
- public void setPreviewCallback(PreviewCallback cb) {
- mPrevCb = cb;
- }
-
- public int[] setPreviewResolution(int width, int height) {
- getHolder().setFixedSize(width, height);
-
- mCamera = openCamera();
- mPreviewWidth = width;
- mPreviewHeight = height;
- Camera.Size rs = adaptPreviewResolution(mCamera.new Size(width, height));
- if (rs != null) {
- mPreviewWidth = rs.width;
- mPreviewHeight = rs.height;
- }
- mCamera.getParameters().setPreviewSize(mPreviewWidth, mPreviewHeight);
-
- mGLPreviewBuffer = ByteBuffer.allocateDirect(mPreviewWidth * mPreviewHeight * 4);
- mInputAspectRatio = mPreviewWidth > mPreviewHeight ?
- (float) mPreviewWidth / mPreviewHeight : (float) mPreviewHeight / mPreviewWidth;
-
- return new int[] { mPreviewWidth, mPreviewHeight };
- }
-
- public boolean setFilter(final MagicFilterType type) {
- if (mCamera == null) {
- return false;
- }
-
- queueEvent(new Runnable() {
- @Override
- public void run() {
- if (magicFilter != null) {
- magicFilter.destroy();
- }
- magicFilter = MagicFilterFactory.initFilters(type);
- if (magicFilter != null) {
- magicFilter.init(getContext().getApplicationContext());
- magicFilter.onInputSizeChanged(mPreviewWidth, mPreviewHeight);
- magicFilter.onDisplaySizeChanged(mSurfaceWidth, mSurfaceHeight);
- }
- }
- });
- requestRender();
- return true;
- }
-
- private void deleteTextures() {
- if (mOESTextureId != OpenGLUtils.NO_TEXTURE) {
- queueEvent(new Runnable() {
- @Override
- public void run() {
- GLES20.glDeleteTextures(1, new int[]{ mOESTextureId }, 0);
- mOESTextureId = OpenGLUtils.NO_TEXTURE;
- }
- });
- }
- }
-
- public void setCameraId(int id) {
- mCamId = id;
- setPreviewOrientation(mPreviewOrientation);
- }
-
- public void setPreviewOrientation(int orientation) {
- mPreviewOrientation = orientation;
- Camera.CameraInfo info = new Camera.CameraInfo();
- Camera.getCameraInfo(mCamId, info);
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- mPreviewRotation = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? 270 : 90;
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mPreviewRotation = Build.VERSION.SDK_INT >= Build.VERSION_CODES.N ? 180 : 0;
- }
- } else if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- mPreviewRotation = 90;
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- mPreviewRotation = 0;
- }
- }
- }
-
- public int getCameraId() {
- return mCamId;
- }
-
- public void enableEncoding() {
- worker = new Thread(new Runnable() {
- @Override
- public void run() {
- while (!Thread.interrupted()) {
- while (!mGLIntBufferCache.isEmpty()) {
- IntBuffer picture = mGLIntBufferCache.poll();
- mGLPreviewBuffer.asIntBuffer().put(picture.array());
- mPrevCb.onGetRgbaFrame(mGLPreviewBuffer.array(), mPreviewWidth, mPreviewHeight);
- }
- // Waiting for next frame
- synchronized (writeLock) {
- try {
- // isEmpty() may take some time, so we set timeout to detect next frame
- writeLock.wait(500);
- } catch (InterruptedException ie) {
- worker.interrupt();
- }
- }
- }
- }
- });
- worker.start();
- mIsEncoding = true;
- }
-
- public void disableEncoding() {
- mIsEncoding = false;
- mGLIntBufferCache.clear();
-
- if (worker != null) {
- worker.interrupt();
- try {
- worker.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- worker.interrupt();
- }
- worker = null;
- }
- }
-
- public boolean startCamera() {
- if (mCamera == null) {
- mCamera = openCamera();
- if (mCamera == null) {
- return false;
- }
- }
-
- Camera.Parameters params = mCamera.getParameters();
- params.setPictureSize(mPreviewWidth, mPreviewHeight);
- params.setPreviewSize(mPreviewWidth, mPreviewHeight);
- int[] range = adaptFpsRange(SrsEncoder.VFPS, params.getSupportedPreviewFpsRange());
- params.setPreviewFpsRange(range[0], range[1]);
- params.setPreviewFormat(ImageFormat.NV21);
- params.setFlashMode(Camera.Parameters.FLASH_MODE_OFF);
- params.setWhiteBalance(Camera.Parameters.WHITE_BALANCE_AUTO);
- params.setSceneMode(Camera.Parameters.SCENE_MODE_AUTO);
-
- List supportedFocusModes = params.getSupportedFocusModes();
- if (supportedFocusModes != null && !supportedFocusModes.isEmpty()) {
- if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE)) {
- params.setFocusMode(Camera.Parameters.FOCUS_MODE_CONTINUOUS_PICTURE);
- } else if (supportedFocusModes.contains(Camera.Parameters.FOCUS_MODE_AUTO)) {
- params.setFocusMode(Camera.Parameters.FOCUS_MODE_AUTO);
- mCamera.autoFocus(null);
- } else {
- params.setFocusMode(supportedFocusModes.get(0));
- }
- }
-
- List supportedFlashModes = params.getSupportedFlashModes();
- if (supportedFlashModes != null && !supportedFlashModes.isEmpty()) {
- if (supportedFlashModes.contains(Camera.Parameters.FLASH_MODE_TORCH)) {
- params.setFlashMode(Camera.Parameters.FLASH_MODE_TORCH);
- } else {
- params.setFlashMode(supportedFlashModes.get(0));
- }
- }
-
- mCamera.setParameters(params);
-
- mCamera.setDisplayOrientation(mPreviewRotation);
-
- try {
- mCamera.setPreviewTexture(surfaceTexture);
- } catch (IOException e) {
- e.printStackTrace();
- }
- mCamera.startPreview();
-
- return true;
- }
-
- public void stopCamera() {
- disableEncoding();
-
- if (mCamera != null) {
- mCamera.stopPreview();
- mCamera.release();
- mCamera = null;
- }
- }
-
- private Camera openCamera() {
- Camera camera;
- if (mCamId < 0) {
- Camera.CameraInfo info = new Camera.CameraInfo();
- int numCameras = Camera.getNumberOfCameras();
- int frontCamId = -1;
- int backCamId = -1;
- for (int i = 0; i < numCameras; i++) {
- Camera.getCameraInfo(i, info);
- if (info.facing == Camera.CameraInfo.CAMERA_FACING_BACK) {
- backCamId = i;
- } else if (info.facing == Camera.CameraInfo.CAMERA_FACING_FRONT) {
- frontCamId = i;
- break;
- }
- }
- if (frontCamId != -1) {
- mCamId = frontCamId;
- } else if (backCamId != -1) {
- mCamId = backCamId;
- } else {
- mCamId = 0;
- }
- }
- camera = Camera.open(mCamId);
- return camera;
- }
-
- private Camera.Size adaptPreviewResolution(Camera.Size resolution) {
- float diff = 100f;
- float xdy = (float) resolution.width / (float) resolution.height;
- Camera.Size best = null;
- for (Camera.Size size : mCamera.getParameters().getSupportedPreviewSizes()) {
- if (size.equals(resolution)) {
- return size;
- }
- float tmp = Math.abs(((float) size.width / (float) size.height) - xdy);
- if (tmp < diff) {
- diff = tmp;
- best = size;
- }
- }
- return best;
- }
-
- private int[] adaptFpsRange(int expectedFps, List fpsRanges) {
- expectedFps *= 1000;
- int[] closestRange = fpsRanges.get(0);
- int measure = Math.abs(closestRange[0] - expectedFps) + Math.abs(closestRange[1] - expectedFps);
- for (int[] range : fpsRanges) {
- if (range[0] <= expectedFps && range[1] >= expectedFps) {
- int curMeasure = Math.abs(range[0] - expectedFps) + Math.abs(range[1] - expectedFps);
- if (curMeasure < measure) {
- closestRange = range;
- measure = curMeasure;
- }
- }
- }
- return closestRange;
- }
-
- public interface PreviewCallback {
-
- void onGetRgbaFrame(byte[] data, int width, int height);
- }
-}
diff --git a/android/src/main/java/net/ossrs/yasea/SrsEncodeHandler.java b/android/src/main/java/net/ossrs/yasea/SrsEncodeHandler.java
deleted file mode 100755
index e33a202..0000000
--- a/android/src/main/java/net/ossrs/yasea/SrsEncodeHandler.java
+++ /dev/null
@@ -1,68 +0,0 @@
-package net.ossrs.yasea;
-
-import android.os.Handler;
-import android.os.Message;
-
-import com.facebook.react.uimanager.ThemedReactContext;
-
-import java.lang.ref.WeakReference;
-
-/**
- * Created by leo.ma on 2016/11/4.
- */
-
-public class SrsEncodeHandler extends Handler {
-
- private static final int MSG_ENCODE_NETWORK_WEAK = 0;
- private static final int MSG_ENCODE_NETWORK_RESUME = 1;
- private static final int MSG_ENCODE_ILLEGAL_ARGUMENT_EXCEPTION = 2;
-
- private WeakReference mWeakListener;
-
- public SrsEncodeHandler(SrsEncodeListener listener) {
- mWeakListener = new WeakReference<>(listener);
- }
-
-
- public void notifyNetworkWeak() {
- sendEmptyMessage(MSG_ENCODE_NETWORK_WEAK);
- }
-
- public void notifyNetworkResume() {
- sendEmptyMessage(MSG_ENCODE_NETWORK_RESUME);
- }
-
- public void notifyEncodeIllegalArgumentException(IllegalArgumentException e) {
- obtainMessage(MSG_ENCODE_ILLEGAL_ARGUMENT_EXCEPTION, e).sendToTarget();
- }
-
- @Override // runs on UI thread
- public void handleMessage(Message msg) {
- SrsEncodeListener listener = mWeakListener.get();
- if (listener == null) {
- return;
- }
-
- switch (msg.what) {
- case MSG_ENCODE_NETWORK_WEAK:
- listener.onNetworkWeak();
- break;
- case MSG_ENCODE_NETWORK_RESUME:
- listener.onNetworkResume();
- break;
- case MSG_ENCODE_ILLEGAL_ARGUMENT_EXCEPTION:
- listener.onEncodeIllegalArgumentException((IllegalArgumentException) msg.obj);
- default:
- throw new RuntimeException("unknown msg " + msg.what);
- }
- }
-
- public interface SrsEncodeListener {
-
- void onNetworkWeak();
-
- void onNetworkResume();
-
- void onEncodeIllegalArgumentException(IllegalArgumentException e);
- }
-}
diff --git a/android/src/main/java/net/ossrs/yasea/SrsEncoder.java b/android/src/main/java/net/ossrs/yasea/SrsEncoder.java
deleted file mode 100755
index 71128b8..0000000
--- a/android/src/main/java/net/ossrs/yasea/SrsEncoder.java
+++ /dev/null
@@ -1,508 +0,0 @@
-package net.ossrs.yasea;
-
-import android.content.res.Configuration;
-import android.media.AudioFormat;
-import android.media.AudioRecord;
-import android.media.MediaCodec;
-import android.media.MediaCodecInfo;
-import android.media.MediaCodecList;
-import android.media.MediaFormat;
-import android.media.MediaRecorder;
-import android.util.Log;
-
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Created by Leo Ma on 4/1/2016.
- */
-public class SrsEncoder {
- private static final String TAG = "SrsEncoder";
-
- public static final String VCODEC = "video/avc";
- public static final String ACODEC = "audio/mp4a-latm";
- public static String x264Preset = "veryfast";
- public static int vPrevWidth = 640;
- public static int vPrevHeight = 360;
- public static int vPortraitWidth = 720;
- public static int vPortraitHeight = 1280;
- public static int vLandscapeWidth = 1280;
- public static int vLandscapeHeight = 720;
- public static int vOutWidth = 720; // Note: the stride of resolution must be set as 16x for hard encoding with some chip like MTK
- public static int vOutHeight = 1280; // Since Y component is quadruple size as U and V component, the stride must be set as 32x
- public static int vBitrate = 1200 * 1024; // 1200 kbps
- public static final int VFPS = 24;
- public static final int VGOP = 48;
- public static final int ASAMPLERATE = 44100;
- public static int aChannelConfig = AudioFormat.CHANNEL_IN_STEREO;
- public static final int ABITRATE = 128 * 1024; // 128 kbps
-
- private SrsEncodeHandler mHandler;
-
- private SrsFlvMuxer flvMuxer;
- private SrsMp4Muxer mp4Muxer;
-
- private MediaCodecInfo vmci;
- private MediaCodec vencoder;
- private MediaCodec aencoder;
- private MediaCodec.BufferInfo vebi = new MediaCodec.BufferInfo();
- private MediaCodec.BufferInfo aebi = new MediaCodec.BufferInfo();
-
- private boolean networkWeakTriggered = false;
- private boolean mCameraFaceFront = true;
- private boolean useSoftEncoder = false;
- private boolean canSoftEncode = false;
-
- private long mPresentTimeUs;
-
- private int mVideoColorFormat;
-
- private int videoFlvTrack;
- private int videoMp4Track;
- private int audioFlvTrack;
- private int audioMp4Track;
-
- // Y, U (Cb) and V (Cr)
- // yuv420 yuv yuv yuv yuv
- // yuv420p (planar) yyyy*2 uu vv
- // yuv420sp(semi-planner) yyyy*2 uv uv
- // I420 -> YUV420P yyyy*2 uu vv
- // YV12 -> YUV420P yyyy*2 vv uu
- // NV12 -> YUV420SP yyyy*2 uv uv
- // NV21 -> YUV420SP yyyy*2 vu vu
- // NV16 -> YUV422SP yyyy uv uv
- // YUY2 -> YUV422SP yuyv yuyv
-
- public SrsEncoder(SrsEncodeHandler handler) {
- mHandler = handler;
- mVideoColorFormat = chooseVideoEncoder();
- }
-
- public void setFlvMuxer(SrsFlvMuxer flvMuxer) {
- this.flvMuxer = flvMuxer;
- }
-
- public void setMp4Muxer(SrsMp4Muxer mp4Muxer) {
- this.mp4Muxer = mp4Muxer;
- }
-
- public boolean start() {
- if (flvMuxer == null || mp4Muxer == null) {
- return false;
- }
-
- // the referent PTS for video and audio encoder.
- mPresentTimeUs = System.nanoTime() / 1000;
-
- // Note: the stride of resolution must be set as 16x for hard encoding with some chip like MTK
- // Since Y component is quadruple size as U and V component, the stride must be set as 32x
- if (!useSoftEncoder && (vOutWidth % 32 != 0 || vOutHeight % 32 != 0)) {
- if (vmci.getName().contains("MTK")) {
- //throw new AssertionError("MTK encoding revolution stride must be 32x");
- }
- }
-
- setEncoderResolution(vOutWidth, vOutHeight);
- setEncoderFps(VFPS);
- setEncoderGop(VGOP);
- // Unfortunately for some android phone, the output fps is less than 10 limited by the
- // capacity of poor cheap chips even with x264. So for the sake of quick appearance of
- // the first picture on the player, a spare lower GOP value is suggested. But note that
- // lower GOP will produce more I frames and therefore more streaming data flow.
- // setEncoderGop(15);
- setEncoderBitrate(vBitrate);
- setEncoderPreset(x264Preset);
-
- if (useSoftEncoder) {
- canSoftEncode = openSoftEncoder();
- if (!canSoftEncode) {
- return false;
- }
- }
-
- // aencoder pcm to aac raw stream.
- // requires sdk level 16+, Android 4.1, 4.1.1, the JELLY_BEAN
- try {
- aencoder = MediaCodec.createEncoderByType(ACODEC);
- } catch (IOException e) {
- Log.e(TAG, "create aencoder failed.");
- e.printStackTrace();
- return false;
- }
-
- // setup the aencoder.
- // @see https://developer.android.com/reference/android/media/MediaCodec.html
- int ach = aChannelConfig == AudioFormat.CHANNEL_IN_STEREO ? 2 : 1;
- MediaFormat audioFormat = MediaFormat.createAudioFormat(ACODEC, ASAMPLERATE, ach);
- audioFormat.setInteger(MediaFormat.KEY_BIT_RATE, ABITRATE);
- audioFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
- aencoder.configure(audioFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- // add the audio tracker to muxer.
- audioFlvTrack = flvMuxer.addTrack(audioFormat);
- audioMp4Track = mp4Muxer.addTrack(audioFormat);
-
- // vencoder yuv to 264 es stream.
- // requires sdk level 16+, Android 4.1, 4.1.1, the JELLY_BEAN
- try {
- vencoder = MediaCodec.createByCodecName(vmci.getName());
- } catch (IOException e) {
- Log.e(TAG, "create vencoder failed.");
- e.printStackTrace();
- return false;
- }
-
- // setup the vencoder.
- // Note: landscape to portrait, 90 degree rotation, so we need to switch width and height in configuration
- MediaFormat videoFormat = MediaFormat.createVideoFormat(VCODEC, vOutWidth, vOutHeight);
- videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, mVideoColorFormat);
- videoFormat.setInteger(MediaFormat.KEY_MAX_INPUT_SIZE, 0);
- videoFormat.setInteger(MediaFormat.KEY_BIT_RATE, vBitrate);
- videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, VFPS);
- videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, VGOP / VFPS);
- vencoder.configure(videoFormat, null, null, MediaCodec.CONFIGURE_FLAG_ENCODE);
- // add the video tracker to muxer.
- videoFlvTrack = flvMuxer.addTrack(videoFormat);
- videoMp4Track = mp4Muxer.addTrack(videoFormat);
-
- // start device and encoder.
- vencoder.start();
- aencoder.start();
- return true;
- }
-
- public void stop() {
- if (useSoftEncoder) {
- closeSoftEncoder();
- canSoftEncode = false;
- }
-
- if (aencoder != null) {
- Log.i(TAG, "stop aencoder");
- aencoder.stop();
- aencoder.release();
- aencoder = null;
- }
-
- if (vencoder != null) {
- Log.i(TAG, "stop vencoder");
- vencoder.stop();
- vencoder.release();
- vencoder = null;
- }
- }
-
- public void setCameraFrontFace() {
- mCameraFaceFront = true;
- }
-
- public void setCameraBackFace() {
- mCameraFaceFront = false;
- }
-
- public void switchToSoftEncoder() {
- useSoftEncoder = true;
- }
-
- public void switchToHardEncoder() {
- useSoftEncoder = false;
- }
-
- public boolean isSoftEncoder() {
- return useSoftEncoder;
- }
-
- public boolean canHardEncode() {
- return vencoder != null;
- }
-
- public boolean canSoftEncode() {
- return canSoftEncode;
- }
-
- public boolean isEnabled() {
- return canHardEncode() || canSoftEncode();
- }
-
- public void setPreviewResolution(int width, int height) {
- vPrevWidth = width;
- vPrevHeight = height;
- }
-
- public void setPortraitResolution(int width, int height) {
- vOutWidth = width;
- vOutHeight = height;
- vPortraitWidth = width;
- vPortraitHeight = height;
- vLandscapeWidth = height;
- vLandscapeHeight = width;
- }
-
- public void setLandscapeResolution(int width, int height) {
- vOutWidth = width;
- vOutHeight = height;
- vLandscapeWidth = width;
- vLandscapeHeight = height;
- vPortraitWidth = height;
- vPortraitHeight = width;
- }
-
- public void setVideoHDMode() {
- vBitrate = 1200 * 1024; // 1200 kbps
- x264Preset = "veryfast";
- }
-
- public void setVideoSmoothMode() {
- vBitrate = 500 * 1024; // 500 kbps
- x264Preset = "superfast";
- }
-
- public int getPreviewWidth() {
- return vPrevWidth;
- }
-
- public int getPreviewHeight() {
- return vPrevHeight;
- }
-
- public int getOutputWidth() {
- return vOutWidth;
- }
-
- public int getOutputHeight() {
- return vOutHeight;
- }
-
- public void setScreenOrientation(int orientation) {
- if (orientation == Configuration.ORIENTATION_PORTRAIT) {
- vOutWidth = vPortraitWidth;
- vOutHeight = vPortraitHeight;
- } else if (orientation == Configuration.ORIENTATION_LANDSCAPE) {
- vOutWidth = vLandscapeWidth;
- vOutHeight = vLandscapeHeight;
- }
-
- // Note: the stride of resolution must be set as 16x for hard encoding with some chip like MTK
- // Since Y component is quadruple size as U and V component, the stride must be set as 32x
- if (!useSoftEncoder && (vOutWidth % 32 != 0 || vOutHeight % 32 != 0)) {
- if (vmci.getName().contains("MTK")) {
- //throw new AssertionError("MTK encoding revolution stride must be 32x");
- }
- }
-
- setEncoderResolution(vOutWidth, vOutHeight);
- }
-
- private void onProcessedYuvFrame(byte[] yuvFrame, long pts) {
- ByteBuffer[] inBuffers = vencoder.getInputBuffers();
- ByteBuffer[] outBuffers = vencoder.getOutputBuffers();
-
- int inBufferIndex = vencoder.dequeueInputBuffer(-1);
- if (inBufferIndex >= 0) {
- ByteBuffer bb = inBuffers[inBufferIndex];
- bb.clear();
- bb.put(yuvFrame, 0, yuvFrame.length);
- vencoder.queueInputBuffer(inBufferIndex, 0, yuvFrame.length, pts, 0);
- }
-
- for (; ; ) {
- int outBufferIndex = vencoder.dequeueOutputBuffer(vebi, 0);
- if (outBufferIndex >= 0) {
- ByteBuffer bb = outBuffers[outBufferIndex];
- onEncodedAnnexbFrame(bb, vebi);
- vencoder.releaseOutputBuffer(outBufferIndex, false);
- } else {
- break;
- }
- }
- }
-
- private void onSoftEncodedData(byte[] es, long pts, boolean isKeyFrame) {
- ByteBuffer bb = ByteBuffer.wrap(es);
- vebi.offset = 0;
- vebi.size = es.length;
- vebi.presentationTimeUs = pts;
- vebi.flags = isKeyFrame ? MediaCodec.BUFFER_FLAG_KEY_FRAME : 0;
- onEncodedAnnexbFrame(bb, vebi);
- }
-
- // when got encoded h264 es stream.
- private void onEncodedAnnexbFrame(ByteBuffer es, MediaCodec.BufferInfo bi) {
- mp4Muxer.writeSampleData(videoMp4Track, es.duplicate(), bi);
- flvMuxer.writeSampleData(videoFlvTrack, es, bi);
- }
-
- // when got encoded aac raw stream.
- private void onEncodedAacFrame(ByteBuffer es, MediaCodec.BufferInfo bi) {
- mp4Muxer.writeSampleData(audioMp4Track, es.duplicate(), bi);
- flvMuxer.writeSampleData(audioFlvTrack, es, bi);
- }
-
- public void onGetPcmFrame(byte[] data, int size) {
- ByteBuffer[] inBuffers = aencoder.getInputBuffers();
- ByteBuffer[] outBuffers = aencoder.getOutputBuffers();
-
- int inBufferIndex = aencoder.dequeueInputBuffer(-1);
- if (inBufferIndex >= 0) {
- ByteBuffer bb = inBuffers[inBufferIndex];
- bb.clear();
- bb.put(data, 0, size);
- long pts = System.nanoTime() / 1000 - mPresentTimeUs;
- aencoder.queueInputBuffer(inBufferIndex, 0, size, pts, 0);
- }
-
- for (; ; ) {
- int outBufferIndex = aencoder.dequeueOutputBuffer(aebi, 0);
- if (outBufferIndex >= 0) {
- ByteBuffer bb = outBuffers[outBufferIndex];
- onEncodedAacFrame(bb, aebi);
- aencoder.releaseOutputBuffer(outBufferIndex, false);
- } else {
- break;
- }
- }
- }
-
- public void onGetRgbaFrame(byte[] data, int width, int height) {
- // Check video frame cache number to judge the networking situation.
- // Just cache GOP / FPS seconds data according to latency.
- AtomicInteger videoFrameCacheNumber = flvMuxer.getVideoFrameCacheNumber();
- if (videoFrameCacheNumber != null && videoFrameCacheNumber.get() < VGOP) {
- long pts = System.nanoTime() / 1000 - mPresentTimeUs;
- if (useSoftEncoder) {
- swRgbaFrame(data, width, height, pts);
- } else {
- byte[] processedData = hwRgbaFrame(data, width, height);
- if (processedData != null) {
- onProcessedYuvFrame(processedData, pts);
- } else {
- mHandler.notifyEncodeIllegalArgumentException(new IllegalArgumentException("libyuv failure"));
- }
- }
-
- if (networkWeakTriggered) {
- networkWeakTriggered = false;
- mHandler.notifyNetworkResume();
- }
- } else {
- mHandler.notifyNetworkWeak();
- networkWeakTriggered = true;
- }
- }
-
- private byte[] hwRgbaFrame(byte[] data, int width, int height) {
- switch (mVideoColorFormat) {
- case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420Planar:
- return RGBAToI420(data, width, height, true, 180);
- case MediaCodecInfo.CodecCapabilities.COLOR_FormatYUV420SemiPlanar:
- return RGBAToNV12(data, width, height, true, 180);
- default:
- throw new IllegalStateException("Unsupported color format!");
- }
- }
-
- private void swRgbaFrame(byte[] data, int width, int height, long pts) {
- RGBASoftEncode(data, width, height, true, 180, pts);
- }
-
- public AudioRecord chooseAudioRecord() {
- AudioRecord mic = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, SrsEncoder.ASAMPLERATE,
- AudioFormat.CHANNEL_IN_STEREO, AudioFormat.ENCODING_PCM_16BIT, getPcmBufferSize() * 4);
- if (mic.getState() != AudioRecord.STATE_INITIALIZED) {
- mic = new AudioRecord(MediaRecorder.AudioSource.DEFAULT, SrsEncoder.ASAMPLERATE,
- AudioFormat.CHANNEL_IN_MONO, AudioFormat.ENCODING_PCM_16BIT, getPcmBufferSize() * 4);
- if (mic.getState() != AudioRecord.STATE_INITIALIZED) {
- mic = null;
- } else {
- SrsEncoder.aChannelConfig = AudioFormat.CHANNEL_IN_MONO;
- }
- } else {
- SrsEncoder.aChannelConfig = AudioFormat.CHANNEL_IN_STEREO;
- }
-
- return mic;
- }
-
- private int getPcmBufferSize() {
- int pcmBufSize = AudioRecord.getMinBufferSize(ASAMPLERATE, AudioFormat.CHANNEL_IN_STEREO,
- AudioFormat.ENCODING_PCM_16BIT) + 8191;
- return pcmBufSize - (pcmBufSize % 8192);
- }
-
- // choose the video encoder by name.
- private MediaCodecInfo chooseVideoEncoder(String name) {
- int nbCodecs = MediaCodecList.getCodecCount();
- for (int i = 0; i < nbCodecs; i++) {
- MediaCodecInfo mci = MediaCodecList.getCodecInfoAt(i);
- if (!mci.isEncoder()) {
- continue;
- }
-
- String[] types = mci.getSupportedTypes();
- for (int j = 0; j < types.length; j++) {
- if (types[j].equalsIgnoreCase(VCODEC)) {
- Log.i(TAG, String.format("vencoder %s types: %s", mci.getName(), types[j]));
- if (name == null) {
- return mci;
- }
-
- if (mci.getName().contains(name)) {
- return mci;
- }
- }
- }
- }
-
- return null;
- }
-
- // choose the right supported color format. @see below:
- private int chooseVideoEncoder() {
- // choose the encoder "video/avc":
- // 1. select default one when type matched.
- // 2. google avc is unusable.
- // 3. choose qcom avc.
- vmci = chooseVideoEncoder(null);
- //vmci = chooseVideoEncoder("google");
- //vmci = chooseVideoEncoder("qcom");
-
- int matchedColorFormat = 0;
- MediaCodecInfo.CodecCapabilities cc = vmci.getCapabilitiesForType(VCODEC);
- for (int i = 0; i < cc.colorFormats.length; i++) {
- int cf = cc.colorFormats[i];
- Log.i(TAG, String.format("vencoder %s supports color fomart 0x%x(%d)", vmci.getName(), cf, cf));
-
- // choose YUV for h.264, prefer the bigger one.
- // corresponding to the color space transform in onPreviewFrame
- if (cf >= cc.COLOR_FormatYUV420Planar && cf <= cc.COLOR_FormatYUV420SemiPlanar) {
- if (cf > matchedColorFormat) {
- matchedColorFormat = cf;
- }
- }
- }
-
- for (int i = 0; i < cc.profileLevels.length; i++) {
- MediaCodecInfo.CodecProfileLevel pl = cc.profileLevels[i];
- Log.i(TAG, String.format("vencoder %s support profile %d, level %d", vmci.getName(), pl.profile, pl.level));
- }
-
- Log.i(TAG, String.format("vencoder %s choose color format 0x%x(%d)", vmci.getName(), matchedColorFormat, matchedColorFormat));
- return matchedColorFormat;
- }
-
- private native void setEncoderResolution(int outWidth, int outHeight);
- private native void setEncoderFps(int fps);
- private native void setEncoderGop(int gop);
- private native void setEncoderBitrate(int bitrate);
- private native void setEncoderPreset(String preset);
- private native byte[] RGBAToI420(byte[] rgbaFrame, int width, int height, boolean flip, int rotate);
- private native byte[] RGBAToNV12(byte[] rgbaFrame, int width, int height, boolean flip, int rotate);
- private native int RGBASoftEncode(byte[] rgbaFrame, int width, int height, boolean flip, int rotate, long pts);
- private native boolean openSoftEncoder();
- private native void closeSoftEncoder();
-
- static {
- System.loadLibrary("yuv");
- System.loadLibrary("enc");
- }
-}
diff --git a/android/src/main/java/net/ossrs/yasea/SrsFlvMuxer.java b/android/src/main/java/net/ossrs/yasea/SrsFlvMuxer.java
deleted file mode 100755
index 1508b20..0000000
--- a/android/src/main/java/net/ossrs/yasea/SrsFlvMuxer.java
+++ /dev/null
@@ -1,986 +0,0 @@
-package net.ossrs.yasea;
-
-import android.media.MediaCodec;
-import android.media.MediaFormat;
-import android.util.Log;
-
-import com.github.faucamp.simplertmp.DefaultRtmpPublisher;
-import com.github.faucamp.simplertmp.RtmpHandler;
-
-import java.nio.ByteBuffer;
-import java.util.ArrayList;
-import java.util.concurrent.ConcurrentLinkedQueue;
-import java.util.concurrent.atomic.AtomicInteger;
-
-/**
- * Created by winlin on 5/2/15.
- * Updated by leoma on 4/1/16.
- * to POST the h.264/avc annexb frame over RTMP.
- * @see android.media.MediaMuxer https://developer.android.com/reference/android/media/MediaMuxer.html
- */
-public class SrsFlvMuxer {
-
- private static final int VIDEO_ALLOC_SIZE = 128 * 1024;
- private static final int AUDIO_ALLOC_SIZE = 4 * 1024;
-
- private volatile boolean connected = false;
- private DefaultRtmpPublisher publisher;
- private RtmpHandler mHandler;
-
- private Thread worker;
- private final Object txFrameLock = new Object();
-
- private SrsFlv flv = new SrsFlv();
- private boolean needToFindKeyFrame = true;
- private SrsFlvFrame mVideoSequenceHeader;
- private SrsFlvFrame mAudioSequenceHeader;
- private SrsAllocator mVideoAllocator = new SrsAllocator(VIDEO_ALLOC_SIZE);
- private SrsAllocator mAudioAllocator = new SrsAllocator(AUDIO_ALLOC_SIZE);
- private ConcurrentLinkedQueue mFlvTagCache = new ConcurrentLinkedQueue<>();
-
- private static final int VIDEO_TRACK = 100;
- private static final int AUDIO_TRACK = 101;
- private static final String TAG = "SrsFlvMuxer";
-
- /**
- * constructor.
- * @param handler the rtmp event handler.
- */
- public SrsFlvMuxer(RtmpHandler handler) {
- mHandler = handler;
- publisher = new DefaultRtmpPublisher(handler);
- }
-
- /**
- * get cached video frame number in publisher
- */
- public AtomicInteger getVideoFrameCacheNumber() {
- return publisher == null ? null : publisher.getVideoFrameCacheNumber();
- }
-
- /**
- * set video resolution for publisher
- * @param width width
- * @param height height
- */
- public void setVideoResolution(int width, int height) {
- if (publisher != null) {
- publisher.setVideoResolution(width, height);
- }
- }
-
- /**
- * Adds a track with the specified format.
- * @param format The media format for the track.
- * @return The track index for this newly added track.
- */
- public int addTrack(MediaFormat format) {
- if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
- flv.setVideoTrack(format);
- return VIDEO_TRACK;
- } else {
- flv.setAudioTrack(format);
- return AUDIO_TRACK;
- }
- }
-
- private void disconnect() {
- try {
- publisher.close();
- } catch (IllegalStateException e) {
- // Ignore illegal state.
- }
- connected = false;
- mVideoSequenceHeader = null;
- mAudioSequenceHeader = null;
- Log.i(TAG, "worker: disconnect ok.");
- }
-
- private boolean connect(String url) {
- if (!connected) {
- Log.i(TAG, String.format("worker: connecting to RTMP server by url=%s\n", url));
- if (publisher.connect(url)) {
- connected = publisher.publish("live");
- }
- mVideoSequenceHeader = null;
- mAudioSequenceHeader = null;
- }
- return connected;
- }
-
- private void sendFlvTag(SrsFlvFrame frame) {
- if (!connected || frame == null) {
- return;
- }
-
- if (frame.isVideo()) {
- if (frame.isKeyFrame()) {
- Log.i(TAG, String.format("worker: send frame type=%d, dts=%d, size=%dB",
- frame.type, frame.dts, frame.flvTag.array().length));
- }
- publisher.publishVideoData(frame.flvTag.array(), frame.flvTag.size(), frame.dts);
- mVideoAllocator.release(frame.flvTag);
- } else if (frame.isAudio()) {
- publisher.publishAudioData(frame.flvTag.array(), frame.flvTag.size(), frame.dts);
- mAudioAllocator.release(frame.flvTag);
- }
- }
-
- /**
- * start to the remote server for remux.
- */
- public void start(final String rtmpUrl) {
- worker = new Thread(new Runnable() {
- @Override
- public void run() {
- if (!connect(rtmpUrl)) {
- return;
- }
-
- while (!Thread.interrupted()) {
- while (!mFlvTagCache.isEmpty()) {
- SrsFlvFrame frame = mFlvTagCache.poll();
- if (frame.isSequenceHeader()) {
- if (frame.isVideo()) {
- mVideoSequenceHeader = frame;
- sendFlvTag(mVideoSequenceHeader);
- } else if (frame.isAudio()) {
- mAudioSequenceHeader = frame;
- sendFlvTag(mAudioSequenceHeader);
- }
- } else {
- if (frame.isVideo() && mVideoSequenceHeader != null) {
- sendFlvTag(frame);
- } else if (frame.isAudio() && mAudioSequenceHeader != null) {
- sendFlvTag(frame);
- }
- }
- }
- // Waiting for next frame
- synchronized (txFrameLock) {
- try {
- // isEmpty() may take some time, so we set timeout to detect next frame
- txFrameLock.wait(500);
- } catch (InterruptedException ie) {
- worker.interrupt();
- }
- }
- }
- }
- });
- worker.start();
- }
-
- /**
- * stop the muxer, disconnect RTMP connection.
- */
- public void stop() {
- mFlvTagCache.clear();
- if (worker != null) {
- worker.interrupt();
- try {
- worker.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- worker.interrupt();
- }
- worker = null;
- }
- flv.reset();
- needToFindKeyFrame = true;
- Log.i(TAG, "SrsFlvMuxer closed");
-
- new Thread(new Runnable() {
- @Override
- public void run() {
- disconnect();
- }
- }).start();
- }
-
- /**
- * send the annexb frame over RTMP.
- * @param trackIndex The track index for this sample.
- * @param byteBuf The encoded sample.
- * @param bufferInfo The buffer information related to this sample.
- */
- public void writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo) {
- if (bufferInfo.offset > 0) {
- Log.w(TAG, String.format("encoded frame %dB, offset=%d pts=%dms",
- bufferInfo.size, bufferInfo.offset, bufferInfo.presentationTimeUs / 1000
- ));
- }
-
- if (VIDEO_TRACK == trackIndex) {
- flv.writeVideoSample(byteBuf, bufferInfo);
- } else {
- flv.writeAudioSample(byteBuf, bufferInfo);
- }
- }
-
- // E.4.3.1 VIDEODATA
- // Frame Type UB [4]
- // Type of video frame. The following values are defined:
- // 1 = key frame (for AVC, a seekable frame)
- // 2 = inter frame (for AVC, a non-seekable frame)
- // 3 = disposable inter frame (H.263 only)
- // 4 = generated key frame (reserved for server use only)
- // 5 = video info/command frame
- private class SrsCodecVideoAVCFrame
- {
- // set to the zero to reserved, for array map.
- public final static int Reserved = 0;
- public final static int Reserved1 = 6;
-
- public final static int KeyFrame = 1;
- public final static int InterFrame = 2;
- public final static int DisposableInterFrame = 3;
- public final static int GeneratedKeyFrame = 4;
- public final static int VideoInfoFrame = 5;
- }
-
- // AVCPacketType IF CodecID == 7 UI8
- // The following values are defined:
- // 0 = AVC sequence header
- // 1 = AVC NALU
- // 2 = AVC end of sequence (lower level NALU sequence ender is
- // not required or supported)
- private class SrsCodecVideoAVCType
- {
- // set to the max value to reserved, for array map.
- public final static int Reserved = 3;
-
- public final static int SequenceHeader = 0;
- public final static int NALU = 1;
- public final static int SequenceHeaderEOF = 2;
- }
-
- /**
- * E.4.1 FLV Tag, page 75
- */
- private class SrsCodecFlvTag
- {
- // set to the zero to reserved, for array map.
- public final static int Reserved = 0;
-
- // 8 = audio
- public final static int Audio = 8;
- // 9 = video
- public final static int Video = 9;
- // 18 = script data
- public final static int Script = 18;
- };
-
- // E.4.3.1 VIDEODATA
- // CodecID UB [4]
- // Codec Identifier. The following values are defined:
- // 2 = Sorenson H.263
- // 3 = Screen video
- // 4 = On2 VP6
- // 5 = On2 VP6 with alpha channel
- // 6 = Screen video version 2
- // 7 = AVC
- private class SrsCodecVideo
- {
- // set to the zero to reserved, for array map.
- public final static int Reserved = 0;
- public final static int Reserved1 = 1;
- public final static int Reserved2 = 9;
-
- // for user to disable video, for example, use pure audio hls.
- public final static int Disabled = 8;
-
- public final static int SorensonH263 = 2;
- public final static int ScreenVideo = 3;
- public final static int On2VP6 = 4;
- public final static int On2VP6WithAlphaChannel = 5;
- public final static int ScreenVideoVersion2 = 6;
- public final static int AVC = 7;
- }
-
- /**
- * the aac object type, for RTMP sequence header
- * for AudioSpecificConfig, @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 33
- * for audioObjectType, @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23
- */
- private class SrsAacObjectType
- {
- public final static int Reserved = 0;
-
- // Table 1.1 – Audio Object Type definition
- // @see @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf, page 23
- public final static int AacMain = 1;
- public final static int AacLC = 2;
- public final static int AacSSR = 3;
-
- // AAC HE = LC+SBR
- public final static int AacHE = 5;
- // AAC HEv2 = LC+SBR+PS
- public final static int AacHEV2 = 29;
- }
-
- /**
- * the aac profile, for ADTS(HLS/TS)
- * @see https://github.com/simple-rtmp-server/srs/issues/310
- */
- private class SrsAacProfile
- {
- public final static int Reserved = 3;
-
- // @see 7.1 Profiles, aac-iso-13818-7.pdf, page 40
- public final static int Main = 0;
- public final static int LC = 1;
- public final static int SSR = 2;
- }
-
- /**
- * the FLV/RTMP supported audio sample rate.
- * Sampling rate. The following values are defined:
- * 0 = 5.5 kHz = 5512 Hz
- * 1 = 11 kHz = 11025 Hz
- * 2 = 22 kHz = 22050 Hz
- * 3 = 44 kHz = 44100 Hz
- */
- private class SrsCodecAudioSampleRate
- {
- // set to the max value to reserved, for array map.
- public final static int Reserved = 4;
-
- public final static int R5512 = 0;
- public final static int R11025 = 1;
- public final static int R22050 = 2;
- public final static int R44100 = 3;
- }
-
- /**
- * Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes
- * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
- */
- private class SrsAvcNaluType
- {
- // Unspecified
- public final static int Reserved = 0;
-
- // Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
- public final static int NonIDR = 1;
- // Coded slice data partition A slice_data_partition_a_layer_rbsp( )
- public final static int DataPartitionA = 2;
- // Coded slice data partition B slice_data_partition_b_layer_rbsp( )
- public final static int DataPartitionB = 3;
- // Coded slice data partition C slice_data_partition_c_layer_rbsp( )
- public final static int DataPartitionC = 4;
- // Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
- public final static int IDR = 5;
- // Supplemental enhancement information (SEI) sei_rbsp( )
- public final static int SEI = 6;
- // Sequence parameter set seq_parameter_set_rbsp( )
- public final static int SPS = 7;
- // Picture parameter set pic_parameter_set_rbsp( )
- public final static int PPS = 8;
- // Access unit delimiter access_unit_delimiter_rbsp( )
- public final static int AccessUnitDelimiter = 9;
- // End of sequence end_of_seq_rbsp( )
- public final static int EOSequence = 10;
- // End of stream end_of_stream_rbsp( )
- public final static int EOStream = 11;
- // Filler data filler_data_rbsp( )
- public final static int FilterData = 12;
- // Sequence parameter set extension seq_parameter_set_extension_rbsp( )
- public final static int SPSExt = 13;
- // Prefix NAL unit prefix_nal_unit_rbsp( )
- public final static int PrefixNALU = 14;
- // Subset sequence parameter set subset_seq_parameter_set_rbsp( )
- public final static int SubsetSPS = 15;
- // Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
- public final static int LayerWithoutPartition = 19;
- // Coded slice extension slice_layer_extension_rbsp( )
- public final static int CodedSliceExt = 20;
- }
-
- /**
- * the search result for annexb.
- */
- private class SrsAnnexbSearch {
- public int nb_start_code = 0;
- public boolean match = false;
- }
-
- /**
- * the demuxed tag frame.
- */
- private class SrsFlvFrameBytes {
- public ByteBuffer data;
- public int size;
- }
-
- /**
- * the muxed flv frame.
- */
- private class SrsFlvFrame {
- // the tag bytes.
- public SrsAllocator.Allocation flvTag;
- // the codec type for audio/aac and video/avc for instance.
- public int avc_aac_type;
- // the frame type, keyframe or not.
- public int frame_type;
- // the tag type, audio, video or data.
- public int type;
- // the dts in ms, tbn is 1000.
- public int dts;
-
- public boolean isKeyFrame() {
- return isVideo() && frame_type == SrsCodecVideoAVCFrame.KeyFrame;
- }
-
- public boolean isSequenceHeader() {
- return avc_aac_type == 0;
- }
-
- public boolean isVideo() {
- return type == SrsCodecFlvTag.Video;
- }
-
- public boolean isAudio() {
- return type == SrsCodecFlvTag.Audio;
- }
- }
-
- /**
- * the raw h.264 stream, in annexb.
- */
- private class SrsRawH264Stream {
- private final static String TAG = "SrsFlvMuxer";
-
- private SrsAnnexbSearch annexb = new SrsAnnexbSearch();
- private SrsFlvFrameBytes seq_hdr = new SrsFlvFrameBytes();
- private SrsFlvFrameBytes sps_hdr = new SrsFlvFrameBytes();
- private SrsFlvFrameBytes sps_bb = new SrsFlvFrameBytes();
- private SrsFlvFrameBytes pps_hdr = new SrsFlvFrameBytes();
- private SrsFlvFrameBytes pps_bb = new SrsFlvFrameBytes();
-
- public boolean isSps(SrsFlvFrameBytes frame) {
- return frame.size >= 1 && (frame.data.get(0) & 0x1f) == SrsAvcNaluType.SPS;
- }
-
- public boolean isPps(SrsFlvFrameBytes frame) {
- return frame.size >= 1 && (frame.data.get(0) & 0x1f) == SrsAvcNaluType.PPS;
- }
-
- public SrsFlvFrameBytes muxNaluHeader(SrsFlvFrameBytes frame) {
- SrsFlvFrameBytes nalu_hdr = new SrsFlvFrameBytes();
- nalu_hdr.data = ByteBuffer.allocateDirect(4);
- nalu_hdr.size = 4;
- // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
- // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size
- int NAL_unit_length = frame.size;
-
- // mux the avc NALU in "ISO Base Media File Format"
- // from H.264-AVC-ISO_IEC_14496-15.pdf, page 20
- // NALUnitLength
- nalu_hdr.data.putInt(NAL_unit_length);
-
- // reset the buffer.
- nalu_hdr.data.rewind();
- return nalu_hdr;
- }
-
- public void muxSequenceHeader(ByteBuffer sps, ByteBuffer pps, int dts, int pts,
- ArrayList frames) {
- // 5bytes sps/pps header:
- // configurationVersion, AVCProfileIndication, profile_compatibility,
- // AVCLevelIndication, lengthSizeMinusOne
- // 3bytes size of sps:
- // numOfSequenceParameterSets, sequenceParameterSetLength(2B)
- // Nbytes of sps.
- // sequenceParameterSetNALUnit
- // 3bytes size of pps:
- // numOfPictureParameterSets, pictureParameterSetLength
- // Nbytes of pps:
- // pictureParameterSetNALUnit
-
- // decode the SPS:
- // @see: 7.3.2.1.1, H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 62
- if (seq_hdr.data == null) {
- seq_hdr.data = ByteBuffer.allocate(5);
- seq_hdr.size = 5;
- }
- seq_hdr.data.rewind();
- // @see: Annex A Profiles and levels, H.264-AVC-ISO_IEC_14496-10.pdf, page 205
- // Baseline profile profile_idc is 66(0x42).
- // Main profile profile_idc is 77(0x4d).
- // Extended profile profile_idc is 88(0x58).
- byte profile_idc = sps.get(1);
- //u_int8_t constraint_set = frame[2];
- byte level_idc = sps.get(3);
-
- // generate the sps/pps header
- // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
- // configurationVersion
- seq_hdr.data.put((byte) 0x01);
- // AVCProfileIndication
- seq_hdr.data.put(profile_idc);
- // profile_compatibility
- seq_hdr.data.put((byte) 0x00);
- // AVCLevelIndication
- seq_hdr.data.put(level_idc);
- // lengthSizeMinusOne, or NAL_unit_length, always use 4bytes size,
- // so we always set it to 0x03.
- seq_hdr.data.put((byte) 0x03);
-
- // reset the buffer.
- seq_hdr.data.rewind();
- frames.add(seq_hdr);
-
- // sps
- if (sps_hdr.data == null) {
- sps_hdr.data = ByteBuffer.allocate(3);
- sps_hdr.size = 3;
- }
- sps_hdr.data.rewind();
- // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
- // numOfSequenceParameterSets, always 1
- sps_hdr.data.put((byte) 0x01);
- // sequenceParameterSetLength
- sps_hdr.data.putShort((short) sps.array().length);
-
- sps_hdr.data.rewind();
- frames.add(sps_hdr);
-
- // sequenceParameterSetNALUnit
- sps_bb.size = sps.array().length;
- sps_bb.data = sps.duplicate();
- frames.add(sps_bb);
-
- // pps
- if (pps_hdr.data == null) {
- pps_hdr.data = ByteBuffer.allocate(3);
- pps_hdr.size = 3;
- }
- pps_hdr.data.rewind();
- // 5.3.4.2.1 Syntax, H.264-AVC-ISO_IEC_14496-15.pdf, page 16
- // numOfPictureParameterSets, always 1
- pps_hdr.data.put((byte) 0x01);
- // pictureParameterSetLength
- pps_hdr.data.putShort((short) pps.array().length);
-
- pps_hdr.data.rewind();
- frames.add(pps_hdr);
-
- // pictureParameterSetNALUnit
- pps_bb.size = pps.array().length;
- pps_bb.data = pps.duplicate();
- frames.add(pps_bb);
- }
-
- public SrsAllocator.Allocation muxFlvTag(ArrayList frames, int frame_type,
- int avc_packet_type, int dts, int pts) {
- // for h264 in RTMP video payload, there is 5bytes header:
- // 1bytes, FrameType | CodecID
- // 1bytes, AVCPacketType
- // 3bytes, CompositionTime, the cts.
- // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
- int size = 5;
- for (int i = 0; i < frames.size(); i++) {
- size += frames.get(i).size;
- }
- SrsAllocator.Allocation allocation = mVideoAllocator.allocate(size);
-
- // @see: E.4.3 Video Tags, video_file_format_spec_v10_1.pdf, page 78
- // Frame Type, Type of video frame.
- // CodecID, Codec Identifier.
- // set the rtmp header
- allocation.put((byte) ((frame_type << 4) | SrsCodecVideo.AVC));
-
- // AVCPacketType
- allocation.put((byte)avc_packet_type);
-
- // CompositionTime
- // pts = dts + cts, or
- // cts = pts - dts.
- // where cts is the header in rtmp video packet payload header.
- int cts = pts - dts;
- allocation.put((byte)(cts >> 16));
- allocation.put((byte)(cts >> 8));
- allocation.put((byte)cts);
-
- // h.264 raw data.
- for (int i = 0; i < frames.size(); i++) {
- SrsFlvFrameBytes frame = frames.get(i);
- frame.data.get(allocation.array(), allocation.size(), frame.size);
- allocation.appendOffset(frame.size);
- }
-
- return allocation;
- }
-
- private SrsAnnexbSearch searchAnnexb(ByteBuffer bb, MediaCodec.BufferInfo bi) {
- annexb.match = false;
- annexb.nb_start_code = 0;
-
- for (int i = bb.position(); i < bi.size - 3; i++) {
- // not match.
- if (bb.get(i) != 0x00 || bb.get(i + 1) != 0x00) {
- break;
- }
-
- // match N[00] 00 00 01, where N>=0
- if (bb.get(i + 2) == 0x01) {
- annexb.match = true;
- annexb.nb_start_code = i + 3 - bb.position();
- break;
- }
- }
-
- return annexb;
- }
-
- public SrsFlvFrameBytes demuxAnnexb(ByteBuffer bb, MediaCodec.BufferInfo bi) {
- SrsFlvFrameBytes tbb = new SrsFlvFrameBytes();
-
- while (bb.position() < bi.size) {
- // each frame must prefixed by annexb format.
- // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
- SrsAnnexbSearch tbbsc = searchAnnexb(bb, bi);
- if (!tbbsc.match || tbbsc.nb_start_code < 3) {
- Log.e(TAG, "annexb not match.");
- mHandler.notifyRtmpIllegalArgumentException(new IllegalArgumentException(
- String.format("annexb not match for %dB, pos=%d", bi.size, bb.position())));
- }
-
- // the start codes.
- for (int i = 0; i < tbbsc.nb_start_code; i++) {
- bb.get();
- }
-
- // find out the frame size.
- tbb.data = bb.slice();
- int pos = bb.position();
- while (bb.position() < bi.size) {
- SrsAnnexbSearch bsc = searchAnnexb(bb, bi);
- if (bsc.match) {
- break;
- }
- bb.get();
- }
-
- tbb.size = bb.position() - pos;
- break;
- }
-
- return tbb;
- }
- }
-
- private class SrsRawAacStreamCodec {
- public byte protection_absent;
- // SrsAacObjectType
- public int aac_object;
- public byte sampling_frequency_index;
- public byte channel_configuration;
- public short frame_length;
-
- public byte sound_format;
- public byte sound_rate;
- public byte sound_size;
- public byte sound_type;
- // 0 for sh; 1 for raw data.
- public byte aac_packet_type;
-
- public byte[] frame;
- }
-
- /**
- * remux the annexb to flv tags.
- */
- private class SrsFlv {
- private MediaFormat videoTrack;
- private MediaFormat audioTrack;
- private int achannel;
- private int asample_rate;
- private SrsRawH264Stream avc = new SrsRawH264Stream();
- private ArrayList ipbs = new ArrayList<>();
- private SrsAllocator.Allocation audio_tag;
- private SrsAllocator.Allocation video_tag;
- private ByteBuffer h264_sps;
- private boolean h264_sps_changed;
- private ByteBuffer h264_pps;
- private boolean h264_pps_changed;
- private boolean h264_sps_pps_sent;
- private boolean aac_specific_config_got;
-
- public SrsFlv() {
- reset();
- }
-
- public void reset() {
- h264_sps_changed = false;
- h264_pps_changed = false;
- h264_sps_pps_sent = false;
- aac_specific_config_got = false;
- }
-
- public void setVideoTrack(MediaFormat format) {
- videoTrack = format;
- }
-
- public void setAudioTrack(MediaFormat format) {
- audioTrack = format;
- achannel = format.getInteger(MediaFormat.KEY_CHANNEL_COUNT);
- asample_rate = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
- }
-
- public void writeAudioSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
- int pts = (int)(bi.presentationTimeUs / 1000);
- int dts = pts;
-
- audio_tag = mAudioAllocator.allocate(bi.size + 2);
- byte aac_packet_type = 1; // 1 = AAC raw
- if (!aac_specific_config_got) {
- // @see aac-mp4a-format-ISO_IEC_14496-3+2001.pdf
- // AudioSpecificConfig (), page 33
- // 1.6.2.1 AudioSpecificConfig
- // audioObjectType; 5 bslbf
- byte ch = (byte)(bb.get(0) & 0xf8);
- // 3bits left.
-
- // samplingFrequencyIndex; 4 bslbf
- byte samplingFrequencyIndex = 0x04;
- if (asample_rate == SrsCodecAudioSampleRate.R22050) {
- samplingFrequencyIndex = 0x07;
- } else if (asample_rate == SrsCodecAudioSampleRate.R11025) {
- samplingFrequencyIndex = 0x0a;
- }
- ch |= (samplingFrequencyIndex >> 1) & 0x07;
- audio_tag.put(ch, 2);
-
- ch = (byte)((samplingFrequencyIndex << 7) & 0x80);
- // 7bits left.
-
- // channelConfiguration; 4 bslbf
- byte channelConfiguration = 1;
- if (achannel == 2) {
- channelConfiguration = 2;
- }
- ch |= (channelConfiguration << 3) & 0x78;
- // 3bits left.
-
- // GASpecificConfig(), page 451
- // 4.4.1 Decoder configuration (GASpecificConfig)
- // frameLengthFlag; 1 bslbf
- // dependsOnCoreCoder; 1 bslbf
- // extensionFlag; 1 bslbf
- audio_tag.put(ch, 3);
-
- aac_specific_config_got = true;
- aac_packet_type = 0; // 0 = AAC sequence header
-
- writeAdtsHeader(audio_tag.array(), 4);
- audio_tag.appendOffset(7);
- } else {
- bb.get(audio_tag.array(), 2, bi.size);
- audio_tag.appendOffset(bi.size + 2);
- }
-
- byte sound_format = 10; // AAC
- byte sound_type = 0; // 0 = Mono sound
- if (achannel == 2) {
- sound_type = 1; // 1 = Stereo sound
- }
- byte sound_size = 1; // 1 = 16-bit samples
- byte sound_rate = 3; // 44100, 22050, 11025
- if (asample_rate == 22050) {
- sound_rate = 2;
- } else if (asample_rate == 11025) {
- sound_rate = 1;
- }
-
- // for audio frame, there is 1 or 2 bytes header:
- // 1bytes, SoundFormat|SoundRate|SoundSize|SoundType
- // 1bytes, AACPacketType for SoundFormat == 10, 0 is sequence header.
- byte audio_header = (byte) (sound_type & 0x01);
- audio_header |= (sound_size << 1) & 0x02;
- audio_header |= (sound_rate << 2) & 0x0c;
- audio_header |= (sound_format << 4) & 0xf0;
-
- audio_tag.put(audio_header, 0);
- audio_tag.put(aac_packet_type, 1);
-
- writeRtmpPacket(SrsCodecFlvTag.Audio, dts, 0, aac_packet_type, audio_tag);
- }
-
- private void writeAdtsHeader(byte[] frame, int offset) {
- // adts sync word 0xfff (12-bit)
- frame[offset] = (byte) 0xff;
- frame[offset + 1] = (byte) 0xf0;
- // versioin 0 for MPEG-4, 1 for MPEG-2 (1-bit)
- frame[offset + 1] |= 0 << 3;
- // layer 0 (2-bit)
- frame[offset + 1] |= 0 << 1;
- // protection absent: 1 (1-bit)
- frame[offset + 1] |= 1;
- // profile: audio_object_type - 1 (2-bit)
- frame[offset + 2] = (SrsAacObjectType.AacLC - 1) << 6;
- // sampling frequency index: 4 (4-bit)
- frame[offset + 2] |= (4 & 0xf) << 2;
- // channel configuration (3-bit)
- frame[offset + 2] |= (2 & (byte) 0x4) >> 2;
- frame[offset + 3] = (byte) ((2 & (byte) 0x03) << 6);
- // original: 0 (1-bit)
- frame[offset + 3] |= 0 << 5;
- // home: 0 (1-bit)
- frame[offset + 3] |= 0 << 4;
- // copyright id bit: 0 (1-bit)
- frame[offset + 3] |= 0 << 3;
- // copyright id start: 0 (1-bit)
- frame[offset + 3] |= 0 << 2;
- // frame size (13-bit)
- frame[offset + 3] |= ((frame.length - 2) & 0x1800) >> 11;
- frame[offset + 4] = (byte) (((frame.length - 2) & 0x7f8) >> 3);
- frame[offset + 5] = (byte) (((frame.length - 2) & 0x7) << 5);
- // buffer fullness (0x7ff for variable bitrate)
- frame[offset + 5] |= (byte) 0x1f;
- frame[offset + 6] = (byte) 0xfc;
- // number of data block (nb - 1)
- frame[offset + 6] |= 0x0;
- }
-
- public void writeVideoSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
- int pts = (int) (bi.presentationTimeUs / 1000);
- int dts = pts;
-
- int type = SrsCodecVideoAVCFrame.InterFrame;
-
- // send each frame.
- while (bb.position() < bi.size) {
- SrsFlvFrameBytes frame = avc.demuxAnnexb(bb, bi);
-
- // 5bits, 7.3.1 NAL unit syntax,
- // H.264-AVC-ISO_IEC_14496-10.pdf, page 44.
- // 7: SPS, 8: PPS, 5: I Frame, 1: P Frame
- int nal_unit_type = (int)(frame.data.get(0) & 0x1f);
- if (nal_unit_type == SrsAvcNaluType.SPS || nal_unit_type == SrsAvcNaluType.PPS) {
- Log.i(TAG, String.format("annexb demux %dB, pts=%d, frame=%dB, nalu=%d",
- bi.size, pts, frame.size, nal_unit_type));
- }
-
- // for IDR frame, the frame is keyframe.
- if (nal_unit_type == SrsAvcNaluType.IDR) {
- type = SrsCodecVideoAVCFrame.KeyFrame;
- }
-
- // ignore the nalu type aud(9)
- if (nal_unit_type == SrsAvcNaluType.AccessUnitDelimiter) {
- continue;
- }
-
- // for sps
- if (avc.isSps(frame)) {
- if (!frame.data.equals(h264_sps)) {
- byte[] sps = new byte[frame.size];
- frame.data.get(sps);
- h264_sps_changed = true;
- h264_sps = ByteBuffer.wrap(sps);
- }
- continue;
- }
-
- // for pps
- if (avc.isPps(frame)) {
- if (!frame.data.equals(h264_pps)) {
- byte[] pps = new byte[frame.size];
- frame.data.get(pps);
- h264_pps_changed = true;
- h264_pps = ByteBuffer.wrap(pps);
- }
- continue;
- }
-
- // IPB frame.
- ipbs.add(avc.muxNaluHeader(frame));
- ipbs.add(frame);
- }
-
- writeH264SpsPps(dts, pts);
- writeH264IpbFrame(ipbs, type, dts, pts);
- ipbs.clear();
- }
-
- private void writeH264SpsPps(int dts, int pts) {
- // when sps or pps changed, update the sequence header,
- // for the pps maybe not changed while sps changed.
- // so, we must check when each video ts message frame parsed.
- if (h264_sps_pps_sent && !h264_sps_changed && !h264_pps_changed) {
- return;
- }
-
- // when not got sps/pps, wait.
- if (h264_pps == null || h264_sps == null) {
- return;
- }
-
- // h264 raw to h264 packet.
- ArrayList frames = new ArrayList<>();
- avc.muxSequenceHeader(h264_sps, h264_pps, dts, pts, frames);
-
- // h264 packet to flv packet.
- int frame_type = SrsCodecVideoAVCFrame.KeyFrame;
- int avc_packet_type = SrsCodecVideoAVCType.SequenceHeader;
- video_tag = avc.muxFlvTag(frames, frame_type, avc_packet_type, dts, pts);
-
- // the timestamp in rtmp message header is dts.
- writeRtmpPacket(SrsCodecFlvTag.Video, dts, frame_type, avc_packet_type, video_tag);
-
- // reset sps and pps.
- h264_sps_changed = false;
- h264_pps_changed = false;
- h264_sps_pps_sent = true;
- Log.i(TAG, String.format("flv: h264 sps/pps sent, sps=%dB, pps=%dB",
- h264_sps.array().length, h264_pps.array().length));
- }
-
- private void writeH264IpbFrame(ArrayList frames, int type, int dts, int pts) {
- // when sps or pps not sent, ignore the packet.
- // @see https://github.com/simple-rtmp-server/srs/issues/203
- if (!h264_sps_pps_sent) {
- return;
- }
-
- video_tag = avc.muxFlvTag(frames, type, SrsCodecVideoAVCType.NALU, dts, pts);
-
- // the timestamp in rtmp message header is dts.
- writeRtmpPacket(SrsCodecFlvTag.Video, dts, type, SrsCodecVideoAVCType.NALU, video_tag);
- }
-
- private void writeRtmpPacket(int type, int dts, int frame_type, int avc_aac_type, SrsAllocator.Allocation tag) {
- SrsFlvFrame frame = new SrsFlvFrame();
- frame.flvTag = tag;
- frame.type = type;
- frame.dts = dts;
- frame.frame_type = frame_type;
- frame.avc_aac_type = avc_aac_type;
-
- if (frame.isVideo()) {
- if (needToFindKeyFrame) {
- if (frame.isKeyFrame()) {
- needToFindKeyFrame = false;
- flvTagCacheAdd(frame);
- }
- } else {
- flvTagCacheAdd(frame);
- }
- } else if (frame.isAudio()) {
- flvTagCacheAdd(frame);
- }
- }
-
- private void flvTagCacheAdd(SrsFlvFrame frame) {
- mFlvTagCache.add(frame);
- if (frame.isVideo()) {
- getVideoFrameCacheNumber().incrementAndGet();
- }
- synchronized (txFrameLock) {
- txFrameLock.notifyAll();
- }
- }
- }
-}
diff --git a/android/src/main/java/net/ossrs/yasea/SrsMp4Muxer.java b/android/src/main/java/net/ossrs/yasea/SrsMp4Muxer.java
deleted file mode 100755
index ca4675d..0000000
--- a/android/src/main/java/net/ossrs/yasea/SrsMp4Muxer.java
+++ /dev/null
@@ -1,1086 +0,0 @@
-package net.ossrs.yasea;
-
-import android.media.MediaCodec;
-import android.media.MediaFormat;
-import android.util.Log;
-
-import com.coremedia.iso.BoxParser;
-import com.coremedia.iso.IsoFile;
-import com.coremedia.iso.IsoTypeWriter;
-import com.coremedia.iso.boxes.AbstractMediaHeaderBox;
-import com.coremedia.iso.boxes.Box;
-import com.coremedia.iso.boxes.ContainerBox;
-import com.coremedia.iso.boxes.DataEntryUrlBox;
-import com.coremedia.iso.boxes.DataInformationBox;
-import com.coremedia.iso.boxes.DataReferenceBox;
-import com.coremedia.iso.boxes.FileTypeBox;
-import com.coremedia.iso.boxes.HandlerBox;
-import com.coremedia.iso.boxes.MediaBox;
-import com.coremedia.iso.boxes.MediaHeaderBox;
-import com.coremedia.iso.boxes.MediaInformationBox;
-import com.coremedia.iso.boxes.MovieBox;
-import com.coremedia.iso.boxes.MovieHeaderBox;
-import com.coremedia.iso.boxes.SampleDescriptionBox;
-import com.coremedia.iso.boxes.SampleSizeBox;
-import com.coremedia.iso.boxes.SampleTableBox;
-import com.coremedia.iso.boxes.SampleToChunkBox;
-import com.coremedia.iso.boxes.SoundMediaHeaderBox;
-import com.coremedia.iso.boxes.StaticChunkOffsetBox;
-import com.coremedia.iso.boxes.SyncSampleBox;
-import com.coremedia.iso.boxes.TimeToSampleBox;
-import com.coremedia.iso.boxes.TrackBox;
-import com.coremedia.iso.boxes.TrackHeaderBox;
-import com.coremedia.iso.boxes.VideoMediaHeaderBox;
-import com.coremedia.iso.boxes.h264.AvcConfigurationBox;
-import com.coremedia.iso.boxes.sampleentry.AudioSampleEntry;
-import com.coremedia.iso.boxes.sampleentry.VisualSampleEntry;
-import com.googlecode.mp4parser.boxes.mp4.ESDescriptorBox;
-import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.AudioSpecificConfig;
-import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.DecoderConfigDescriptor;
-import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.ESDescriptor;
-import com.googlecode.mp4parser.boxes.mp4.objectdescriptors.SLConfigDescriptor;
-import com.googlecode.mp4parser.util.Math;
-import com.googlecode.mp4parser.util.Matrix;
-
-import java.io.File;
-import java.io.FileOutputStream;
-import java.io.IOException;
-import java.nio.ByteBuffer;
-import java.nio.channels.FileChannel;
-import java.nio.channels.ReadableByteChannel;
-import java.nio.channels.WritableByteChannel;
-import java.util.ArrayList;
-import java.util.Date;
-import java.util.HashMap;
-import java.util.LinkedList;
-import java.util.List;
-import java.util.Map;
-import java.util.concurrent.ConcurrentLinkedQueue;
-
-/**
- * Created by LeoMa on 2016/5/21.
- */
-public class SrsMp4Muxer {
-
- private static final String TAG = "SrsMp4Muxer";
- private static final int VIDEO_TRACK = 100;
- private static final int AUDIO_TRACK = 101;
-
- private File mRecFile;
- private SrsRecordHandler mHandler;
-
- private MediaFormat videoFormat = null;
- private MediaFormat audioFormat = null;
-
- private SrsRawH264Stream avc = new SrsRawH264Stream();
- private Mp4Movie mp4Movie = new Mp4Movie();
-
- private boolean aacSpecConfig = false;
- private ByteBuffer h264_sps = null;
- private ByteBuffer h264_pps = null;
- private ArrayList spsList = new ArrayList<>();
- private ArrayList ppsList = new ArrayList<>();
-
- private Thread worker;
- private volatile boolean bRecording = false;
- private volatile boolean bPaused = false;
- private volatile boolean needToFindKeyFrame = true;
- private final Object writeLock = new Object();
- private ConcurrentLinkedQueue frameCache = new ConcurrentLinkedQueue<>();
-
- private static Map samplingFrequencyIndexMap = new HashMap<>();
-
- static {
- samplingFrequencyIndexMap.put(96000, 0x0);
- samplingFrequencyIndexMap.put(88200, 0x1);
- samplingFrequencyIndexMap.put(64000, 0x2);
- samplingFrequencyIndexMap.put(48000, 0x3);
- samplingFrequencyIndexMap.put(44100, 0x4);
- samplingFrequencyIndexMap.put(32000, 0x5);
- samplingFrequencyIndexMap.put(24000, 0x6);
- samplingFrequencyIndexMap.put(22050, 0x7);
- samplingFrequencyIndexMap.put(16000, 0x8);
- samplingFrequencyIndexMap.put(12000, 0x9);
- samplingFrequencyIndexMap.put(11025, 0xa);
- samplingFrequencyIndexMap.put(8000, 0xb);
- }
-
- public SrsMp4Muxer(SrsRecordHandler handler) {
- mHandler = handler;
- }
-
- /**
- * start recording.
- */
- public boolean record(File outputFile) {
- if (videoFormat == null && audioFormat == null) {
- return false;
- }
-
- mRecFile = outputFile;
- createMovie(mRecFile);
- mHandler.notifyRecordStarted(mRecFile.getPath());
-
- if (!spsList.isEmpty() && !ppsList.isEmpty()) {
- mp4Movie.addTrack(videoFormat, false);
- }
- mp4Movie.addTrack(audioFormat, true);
-
- worker = new Thread(new Runnable() {
- @Override
- public void run() {
- bRecording = true;
- while (bRecording) {
- // Keep at least one audio and video frame in cache to ensure monotonically increasing.
- while (!frameCache.isEmpty()) {
- SrsEsFrame frame = frameCache.poll();
- writeSampleData(frame.bb, frame.bi, frame.is_audio());
- }
- // Waiting for next frame
- synchronized (writeLock) {
- try {
- // isEmpty() may take some time, so we set timeout to detect next frame
- writeLock.wait(500);
- } catch (InterruptedException ie) {
- worker.interrupt();
- }
- }
- }
- }
- });
- worker.start();
-
- return true;
- }
-
- /**
- * pause recording.
- */
- public void pause() {
- if (bRecording) {
- bPaused = true;
- mHandler.notifyRecordPause();
- }
- }
-
- /**
- * resume recording.
- */
- public void resume() {
- if (bRecording) {
- bPaused = false;
- needToFindKeyFrame = true;
- mHandler.notifyRecordResume();
- }
- }
-
- /**
- * finish recording.
- */
- public void stop() {
- bRecording = false;
- bPaused = false;
- needToFindKeyFrame = true;
- aacSpecConfig = false;
- frameCache.clear();
-
- if (worker != null) {
- try {
- worker.join();
- } catch (InterruptedException e) {
- e.printStackTrace();
- worker.interrupt();
- }
- worker = null;
-
- finishMovie();
- mHandler.notifyRecordFinished(mRecFile.getPath());
- }
- Log.i(TAG, "SrsMp4Muxer closed");
- }
-
- /**
- * Adds a track with the specified format.
- *
- * @param format The media format for the track.
- * @return The track index for this newly added track.
- */
- public int addTrack(MediaFormat format) {
- if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
- videoFormat = format;
- return VIDEO_TRACK;
- } else {
- audioFormat = format;
- return AUDIO_TRACK;
- }
- }
-
- /**
- * send the annexb frame to SRS over RTMP.
- *
- * @param trackIndex The track index for this sample.
- * @param byteBuf The encoded sample.
- * @param bufferInfo The buffer information related to this sample.
- */
- public void writeSampleData(int trackIndex, ByteBuffer byteBuf, MediaCodec.BufferInfo bufferInfo) {
- if (VIDEO_TRACK == trackIndex) {
- writeVideoSample(byteBuf, bufferInfo);
- } else {
- writeAudioSample(byteBuf, bufferInfo);
- }
- }
-
- /**
- * Table 7-1 – NAL unit type codes, syntax element categories, and NAL unit type classes
- * H.264-AVC-ISO_IEC_14496-10-2012.pdf, page 83.
- */
- private class SrsAvcNaluType
- {
- // Unspecified
- public final static int Reserved = 0;
-
- // Coded slice of a non-IDR picture slice_layer_without_partitioning_rbsp( )
- public final static int NonIDR = 1;
- // Coded slice data partition A slice_data_partition_a_layer_rbsp( )
- public final static int DataPartitionA = 2;
- // Coded slice data partition B slice_data_partition_b_layer_rbsp( )
- public final static int DataPartitionB = 3;
- // Coded slice data partition C slice_data_partition_c_layer_rbsp( )
- public final static int DataPartitionC = 4;
- // Coded slice of an IDR picture slice_layer_without_partitioning_rbsp( )
- public final static int IDR = 5;
- // Supplemental enhancement information (SEI) sei_rbsp( )
- public final static int SEI = 6;
- // Sequence parameter set seq_parameter_set_rbsp( )
- public final static int SPS = 7;
- // Picture parameter set pic_parameter_set_rbsp( )
- public final static int PPS = 8;
- // Access unit delimiter access_unit_delimiter_rbsp( )
- public final static int AccessUnitDelimiter = 9;
- // End of sequence end_of_seq_rbsp( )
- public final static int EOSequence = 10;
- // End of stream end_of_stream_rbsp( )
- public final static int EOStream = 11;
- // Filler data filler_data_rbsp( )
- public final static int FilterData = 12;
- // Sequence parameter set extension seq_parameter_set_extension_rbsp( )
- public final static int SPSExt = 13;
- // Prefix NAL unit prefix_nal_unit_rbsp( )
- public final static int PrefixNALU = 14;
- // Subset sequence parameter set subset_seq_parameter_set_rbsp( )
- public final static int SubsetSPS = 15;
- // Coded slice of an auxiliary coded picture without partitioning slice_layer_without_partitioning_rbsp( )
- public final static int LayerWithoutPartition = 19;
- // Coded slice extension slice_layer_extension_rbsp( )
- public final static int CodedSliceExt = 20;
- }
-
- private void writeVideoSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
- int nal_unit_type = bb.get(4) & 0x1f;
- if (nal_unit_type == SrsAvcNaluType.IDR || nal_unit_type == SrsAvcNaluType.NonIDR) {
- writeFrameByte(VIDEO_TRACK, bb, bi, nal_unit_type == SrsAvcNaluType.IDR);
- } else {
- while (bb.position() < bi.size) {
- SrsEsFrameBytes frame = avc.annexb_demux(bb, bi);
-
- if (avc.is_sps(frame)) {
- if (!frame.data.equals(h264_sps)) {
- byte[] sps = new byte[frame.size];
- frame.data.get(sps);
- h264_sps = ByteBuffer.wrap(sps);
- spsList.clear();
- spsList.add(sps);
- }
- continue;
- }
-
- if (avc.is_pps(frame)) {
- if (!frame.data.equals(h264_pps)) {
- byte[] pps = new byte[frame.size];
- frame.data.get(pps);
- h264_pps = ByteBuffer.wrap(pps);
- ppsList.clear();
- ppsList.add(pps);
- }
- continue;
- }
- }
- }
- }
-
- private void writeAudioSample(final ByteBuffer bb, MediaCodec.BufferInfo bi) {
- if (!aacSpecConfig) {
- aacSpecConfig = true;
- } else {
- writeFrameByte(AUDIO_TRACK, bb, bi, false);
- }
- }
-
- private void writeFrameByte(int track, ByteBuffer bb, MediaCodec.BufferInfo bi, boolean isKeyFrame) {
- SrsEsFrame frame = new SrsEsFrame();
- frame.bb = bb;
- frame.bi = bi;
- frame.isKeyFrame = isKeyFrame;
- frame.track = track;
-
- if (bRecording && !bPaused) {
- if (needToFindKeyFrame) {
- if (frame.isKeyFrame) {
- needToFindKeyFrame = false;
- frameCache.add(frame);
- synchronized (writeLock) {
- writeLock.notifyAll();
- }
- }
- } else {
- frameCache.add(frame);
- synchronized (writeLock) {
- writeLock.notifyAll();
- }
- }
- }
- }
-
- /**
- * the search result for annexb.
- */
- private class SrsAnnexbSearch {
- public int nb_start_code = 0;
- public boolean match = false;
- }
-
- /**
- * the demuxed tag frame.
- */
- private class SrsEsFrameBytes {
- public ByteBuffer data;
- public int size;
- }
-
- /**
- * the AV frame.
- */
- private class SrsEsFrame {
- public ByteBuffer bb;
- public MediaCodec.BufferInfo bi;
- public int track;
- public boolean isKeyFrame;
-
- public boolean is_video() {
- return track == VIDEO_TRACK;
- }
-
- public boolean is_audio() {
- return track == AUDIO_TRACK;
- }
- }
-
- /**
- * the raw h.264 stream, in annexb.
- */
- private class SrsRawH264Stream {
- public boolean is_sps(SrsEsFrameBytes frame) {
- if (frame.size < 1) {
- return false;
- }
-
- return (frame.data.get(0) & 0x1f) == SrsAvcNaluType.SPS;
- }
-
- public boolean is_pps(SrsEsFrameBytes frame) {
- if (frame.size < 1) {
- return false;
- }
- return (frame.data.get(0) & 0x1f) == SrsAvcNaluType.PPS;
- }
-
- public SrsAnnexbSearch srs_avc_startswith_annexb(ByteBuffer bb, MediaCodec.BufferInfo bi) {
- SrsAnnexbSearch as = new SrsAnnexbSearch();
- as.match = false;
-
- int pos = bb.position();
- while (pos < bi.size - 3) {
- // not match.
- if (bb.get(pos) != 0x00 || bb.get(pos + 1) != 0x00) {
- break;
- }
-
- // match N[00] 00 00 01, where N>=0
- if (bb.get(pos + 2) == 0x01) {
- as.match = true;
- as.nb_start_code = pos + 3 - bb.position();
- break;
- }
-
- pos++;
- }
-
- return as;
- }
-
- public SrsEsFrameBytes annexb_demux(ByteBuffer bb, MediaCodec.BufferInfo bi) {
- SrsEsFrameBytes tbb = new SrsEsFrameBytes();
-
- while (bb.position() < bi.size) {
- // each frame must prefixed by annexb format.
- // about annexb, @see H.264-AVC-ISO_IEC_14496-10.pdf, page 211.
- SrsAnnexbSearch tbbsc = srs_avc_startswith_annexb(bb, bi);
- if (!tbbsc.match || tbbsc.nb_start_code < 3) {
- Log.e(TAG, "annexb not match.");
- mHandler.notifyRecordIllegalArgumentException(new IllegalArgumentException(
- String.format("annexb not match for %dB, pos=%d", bi.size, bb.position())));
- }
-
- // the start codes.
- ByteBuffer tbbs = bb.slice();
- for (int i = 0; i < tbbsc.nb_start_code; i++) {
- bb.get();
- }
-
- // find out the frame size.
- tbb.data = bb.slice();
- int pos = bb.position();
- while (bb.position() < bi.size) {
- SrsAnnexbSearch bsc = srs_avc_startswith_annexb(bb, bi);
- if (bsc.match) {
- break;
- }
- bb.get();
- }
-
- tbb.size = bb.position() - pos;
- break;
- }
-
- return tbb;
- }
- }
-
- private class Sample {
- private long offset = 0;
- private long size = 0;
-
- public Sample(long offset, long size) {
- this.offset = offset;
- this.size = size;
- }
-
- public long getOffset() {
- return offset;
- }
-
- public long getSize() {
- return size;
- }
- }
-
- private class Track {
- private int trackId = 0;
- private ArrayList samples = new ArrayList<>();
- private long duration = 0;
- private String handler;
- private AbstractMediaHeaderBox headerBox = null;
- private SampleDescriptionBox sampleDescriptionBox = null;
- private LinkedList syncSamples = null;
- private int timeScale;
- private Date creationTime = new Date();
- private int height;
- private int width;
- private float volume = 0;
- private ArrayList sampleDurations = new ArrayList<>();
- private boolean isAudio = false;
- private long lastPresentationTimeUs = 0;
- private boolean first = true;
-
- public Track(int id, MediaFormat format, boolean audio) {
- trackId = id;
- isAudio = audio;
- if (!isAudio) {
- sampleDurations.add((long) 3015);
- duration = 3015;
- width = format.getInteger(MediaFormat.KEY_WIDTH);
- height = format.getInteger(MediaFormat.KEY_HEIGHT);
- timeScale = 90000;
- syncSamples = new LinkedList<>();
- handler = "vide";
- headerBox = new VideoMediaHeaderBox();
- sampleDescriptionBox = new SampleDescriptionBox();
- if (format.getString(MediaFormat.KEY_MIME).contentEquals(SrsEncoder.VCODEC)) {
- VisualSampleEntry visualSampleEntry = new VisualSampleEntry("avc1");
- visualSampleEntry.setDataReferenceIndex(1);
- visualSampleEntry.setDepth(24);
- visualSampleEntry.setFrameCount(1);
- visualSampleEntry.setHorizresolution(72);
- visualSampleEntry.setVertresolution(72);
- visualSampleEntry.setWidth(width);
- visualSampleEntry.setHeight(height);
- visualSampleEntry.setCompressorname("AVC Coding");
-
- AvcConfigurationBox avcConfigurationBox = new AvcConfigurationBox();
- avcConfigurationBox.setConfigurationVersion(1);
- avcConfigurationBox.setAvcProfileIndication((int) h264_sps.get(1));
- avcConfigurationBox.setProfileCompatibility(0);
- avcConfigurationBox.setAvcLevelIndication((int) h264_sps.get(3));
- avcConfigurationBox.setLengthSizeMinusOne(3);
- avcConfigurationBox.setSequenceParameterSets(spsList);
- avcConfigurationBox.setPictureParameterSets(ppsList);
- avcConfigurationBox.setBitDepthLumaMinus8(-1);
- avcConfigurationBox.setBitDepthChromaMinus8(-1);
- avcConfigurationBox.setChromaFormat(-1);
- avcConfigurationBox.setHasExts(false);
-
- visualSampleEntry.addBox(avcConfigurationBox);
- sampleDescriptionBox.addBox(visualSampleEntry);
- }
- } else {
- sampleDurations.add((long) 1024);
- duration = 1024;
- volume = 1;
- timeScale = format.getInteger(MediaFormat.KEY_SAMPLE_RATE);
- handler = "soun";
- headerBox = new SoundMediaHeaderBox();
- sampleDescriptionBox = new SampleDescriptionBox();
- AudioSampleEntry audioSampleEntry = new AudioSampleEntry("mp4a");
- audioSampleEntry.setChannelCount(format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
- audioSampleEntry.setSampleRate(format.getInteger(MediaFormat.KEY_SAMPLE_RATE));
- audioSampleEntry.setDataReferenceIndex(1);
- audioSampleEntry.setSampleSize(16);
-
- ESDescriptorBox esds = new ESDescriptorBox();
- ESDescriptor descriptor = new ESDescriptor();
- descriptor.setEsId(0);
-
- SLConfigDescriptor slConfigDescriptor = new SLConfigDescriptor();
- slConfigDescriptor.setPredefined(2);
- descriptor.setSlConfigDescriptor(slConfigDescriptor);
-
- DecoderConfigDescriptor decoderConfigDescriptor = new DecoderConfigDescriptor();
- decoderConfigDescriptor.setObjectTypeIndication(0x40);
- decoderConfigDescriptor.setStreamType(5);
- decoderConfigDescriptor.setBufferSizeDB(1536);
- decoderConfigDescriptor.setMaxBitRate(96000);
- decoderConfigDescriptor.setAvgBitRate(96000);
-
- AudioSpecificConfig audioSpecificConfig = new AudioSpecificConfig();
- audioSpecificConfig.setAudioObjectType(2);
- audioSpecificConfig.setSamplingFrequencyIndex(samplingFrequencyIndexMap.get((int) audioSampleEntry.getSampleRate()));
- audioSpecificConfig.setChannelConfiguration(audioSampleEntry.getChannelCount());
- decoderConfigDescriptor.setAudioSpecificInfo(audioSpecificConfig);
-
- descriptor.setDecoderConfigDescriptor(decoderConfigDescriptor);
-
- ByteBuffer data = descriptor.serialize();
- esds.setEsDescriptor(descriptor);
- esds.setData(data);
- audioSampleEntry.addBox(esds);
- sampleDescriptionBox.addBox(audioSampleEntry);
- }
- }
-
- public void addSample(long offset, MediaCodec.BufferInfo bi) {
- long delta = bi.presentationTimeUs - lastPresentationTimeUs;
- if (delta < 0) {
- return;
- }
- boolean isSyncFrame = !isAudio && (bi.flags & MediaCodec.BUFFER_FLAG_SYNC_FRAME) != 0;
- samples.add(new Sample(offset, bi.size));
- if (syncSamples != null && isSyncFrame) {
- syncSamples.add(samples.size());
- }
-
- delta = (delta * timeScale + 500000L) / 1000000L;
- lastPresentationTimeUs = bi.presentationTimeUs;
- if (!first) {
- sampleDurations.add(sampleDurations.size() - 1, delta);
- duration += delta;
- }
- first = false;
- }
-
- public void clearSample() {
- first = true;
- samples.clear();
- syncSamples.clear();
- sampleDurations.clear();
- }
-
- public ArrayList getSamples() {
- return samples;
- }
-
- public long getDuration() {
- return duration;
- }
-
- public String getHandler() {
- return handler;
- }
-
- public AbstractMediaHeaderBox getMediaHeaderBox() {
- return headerBox;
- }
-
- public SampleDescriptionBox getSampleDescriptionBox() {
- return sampleDescriptionBox;
- }
-
- public long[] getSyncSamples() {
- if (syncSamples == null || syncSamples.isEmpty()) {
- return null;
- }
- long[] returns = new long[syncSamples.size()];
- for (int i = 0; i < syncSamples.size(); i++) {
- returns[i] = syncSamples.get(i);
- }
- return returns;
- }
-
- public int getTimeScale() {
- return timeScale;
- }
-
- public Date getCreationTime() {
- return creationTime;
- }
-
- public int getWidth() {
- return width;
- }
-
- public int getHeight() {
- return height;
- }
-
- public float getVolume() {
- return volume;
- }
-
- public ArrayList getSampleDurations() {
- return sampleDurations;
- }
-
- public boolean isAudio() {
- return isAudio;
- }
-
- public int getTrackId() {
- return trackId;
- }
- }
-
- private class Mp4Movie {
- private Matrix matrix = Matrix.ROTATE_0;
- private HashMap tracks = new HashMap<>();
-
- public Matrix getMatrix() {
- return matrix;
- }
-
- public HashMap getTracks() {
- return tracks;
- }
-
- public void addSample(int trackIndex, long offset, MediaCodec.BufferInfo bi) {
- Track track = tracks.get(trackIndex);
- track.addSample(offset, bi);
- }
-
- public void addTrack(MediaFormat format, boolean isAudio) {
- if (format != null) {
- if (isAudio) {
- tracks.put(AUDIO_TRACK, new Track(tracks.size(), format, true));
- } else {
- tracks.put(VIDEO_TRACK, new Track(tracks.size(), format, false));
- }
- }
- }
-
- public void removeTrack(int trackIndex) {
- tracks.remove(trackIndex);
- }
- }
-
- private class InterleaveChunkMdat implements Box {
- private boolean first = true;
- private ContainerBox parent;
- private ByteBuffer header = ByteBuffer.allocateDirect(16);
- private long contentSize = 1024 * 1024 * 1024;
-
- public ContainerBox getParent() {
- return parent;
- }
-
- public void setParent(ContainerBox parent) {
- this.parent = parent;
- }
-
- public void setContentSize(long contentSize) {
- this.contentSize = contentSize;
- }
-
- public long getContentSize() {
- return contentSize;
- }
-
- public String getType() {
- return "mdat";
- }
-
- public long getSize() {
- return header.limit() + contentSize;
- }
-
- public int getHeaderSize() {
- return header.limit();
- }
-
- private boolean isSmallBox(long contentSize) {
- return (contentSize + header.limit()) < 4294967296L;
- }
-
- public void getBox(WritableByteChannel writableByteChannel) {
- header.rewind();
- long size = getSize();
- if (isSmallBox(size)) {
- IsoTypeWriter.writeUInt32(header, size);
- } else {
- IsoTypeWriter.writeUInt32(header, 1);
- }
- header.put(IsoFile.fourCCtoBytes("mdat"));
- if (isSmallBox(size)) {
- header.put(new byte[8]);
- } else {
- IsoTypeWriter.writeUInt64(header, size);
- }
- header.rewind();
-
- try {
- writableByteChannel.write(header);
- } catch (IOException e) {
- mHandler.notifyRecordIOException(e);
- }
- }
-
- @Override
- public void parse(ReadableByteChannel readableByteChannel, ByteBuffer header, long contentSize, BoxParser boxParser) throws IOException {
- }
- }
-
- private InterleaveChunkMdat mdat = null;
- private FileOutputStream fos = null;
- private FileChannel fc = null;
- private volatile long recFileSize = 0;
- private volatile long mdatOffset = 0;
- private volatile long flushBytes = 0;
- private HashMap