================================================
FILE: .idea/vcs.xml
================================================
================================================
FILE: LICENSE.txt
================================================
Apache License
Version 2.0, January 2004
http://www.apache.org/licenses/
TERMS AND CONDITIONS FOR USE, REPRODUCTION, AND DISTRIBUTION
1. Definitions.
"License" shall mean the terms and conditions for use, reproduction,
and distribution as defined by Sections 1 through 9 of this document.
"Licensor" shall mean the copyright owner or entity authorized by
the copyright owner that is granting the License.
"Legal Entity" shall mean the union of the acting entity and all
other entities that control, are controlled by, or are under common
control with that entity. For the purposes of this definition,
"control" means (i) the power, direct or indirect, to cause the
direction or management of such entity, whether by contract or
otherwise, or (ii) ownership of fifty percent (50%) or more of the
outstanding shares, or (iii) beneficial ownership of such entity.
"You" (or "Your") shall mean an individual or Legal Entity
exercising permissions granted by this License.
"Source" form shall mean the preferred form for making modifications,
including but not limited to software source code, documentation
source, and configuration files.
"Object" form shall mean any form resulting from mechanical
transformation or translation of a Source form, including but
not limited to compiled object code, generated documentation,
and conversions to other media types.
"Work" shall mean the work of authorship, whether in Source or
Object form, made available under the License, as indicated by a
copyright notice that is included in or attached to the work
(an example is provided in the Appendix below).
"Derivative Works" shall mean any work, whether in Source or Object
form, that is based on (or derived from) the Work and for which the
editorial revisions, annotations, elaborations, or other modifications
represent, as a whole, an original work of authorship. For the purposes
of this License, Derivative Works shall not include works that remain
separable from, or merely link (or bind by name) to the interfaces of,
the Work and Derivative Works thereof.
"Contribution" shall mean any work of authorship, including
the original version of the Work and any modifications or additions
to that Work or Derivative Works thereof, that is intentionally
submitted to Licensor for inclusion in the Work by the copyright owner
or by an individual or Legal Entity authorized to submit on behalf of
the copyright owner. For the purposes of this definition, "submitted"
means any form of electronic, verbal, or written communication sent
to the Licensor or its representatives, including but not limited to
communication on electronic mailing lists, source code control systems,
and issue tracking systems that are managed by, or on behalf of, the
Licensor for the purpose of discussing and improving the Work, but
excluding communication that is conspicuously marked or otherwise
designated in writing by the copyright owner as "Not a Contribution."
"Contributor" shall mean Licensor and any individual or Legal Entity
on behalf of whom a Contribution has been received by Licensor and
subsequently incorporated within the Work.
2. Grant of Copyright License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
copyright license to reproduce, prepare Derivative Works of,
publicly display, publicly perform, sublicense, and distribute the
Work and such Derivative Works in Source or Object form.
3. Grant of Patent License. Subject to the terms and conditions of
this License, each Contributor hereby grants to You a perpetual,
worldwide, non-exclusive, no-charge, royalty-free, irrevocable
(except as stated in this section) patent license to make, have made,
use, offer to sell, sell, import, and otherwise transfer the Work,
where such license applies only to those patent claims licensable
by such Contributor that are necessarily infringed by their
Contribution(s) alone or by combination of their Contribution(s)
with the Work to which such Contribution(s) was submitted. If You
institute patent litigation against any entity (including a
cross-claim or counterclaim in a lawsuit) alleging that the Work
or a Contribution incorporated within the Work constitutes direct
or contributory patent infringement, then any patent licenses
granted to You under this License for that Work shall terminate
as of the date such litigation is filed.
4. Redistribution. You may reproduce and distribute copies of the
Work or Derivative Works thereof in any medium, with or without
modifications, and in Source or Object form, provided that You
meet the following conditions:
(a) You must give any other recipients of the Work or
Derivative Works a copy of this License; and
(b) You must cause any modified files to carry prominent notices
stating that You changed the files; and
(c) You must retain, in the Source form of any Derivative Works
that You distribute, all copyright, patent, trademark, and
attribution notices from the Source form of the Work,
excluding those notices that do not pertain to any part of
the Derivative Works; and
(d) If the Work includes a "NOTICE" text file as part of its
distribution, then any Derivative Works that You distribute must
include a readable copy of the attribution notices contained
within such NOTICE file, excluding those notices that do not
pertain to any part of the Derivative Works, in at least one
of the following places: within a NOTICE text file distributed
as part of the Derivative Works; within the Source form or
documentation, if provided along with the Derivative Works; or,
within a display generated by the Derivative Works, if and
wherever such third-party notices normally appear. The contents
of the NOTICE file are for informational purposes only and
do not modify the License. You may add Your own attribution
notices within Derivative Works that You distribute, alongside
or as an addendum to the NOTICE text from the Work, provided
that such additional attribution notices cannot be construed
as modifying the License.
You may add Your own copyright statement to Your modifications and
may provide additional or different license terms and conditions
for use, reproduction, or distribution of Your modifications, or
for any such Derivative Works as a whole, provided Your use,
reproduction, and distribution of the Work otherwise complies with
the conditions stated in this License.
5. Submission of Contributions. Unless You explicitly state otherwise,
any Contribution intentionally submitted for inclusion in the Work
by You to the Licensor shall be under the terms and conditions of
this License, without any additional terms or conditions.
Notwithstanding the above, nothing herein shall supersede or modify
the terms of any separate license agreement you may have executed
with Licensor regarding such Contributions.
6. Trademarks. This License does not grant permission to use the trade
names, trademarks, service marks, or product names of the Licensor,
except as required for reasonable and customary use in describing the
origin of the Work and reproducing the content of the NOTICE file.
7. Disclaimer of Warranty. Unless required by applicable law or
agreed to in writing, Licensor provides the Work (and each
Contributor provides its Contributions) on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or
implied, including, without limitation, any warranties or conditions
of TITLE, NON-INFRINGEMENT, MERCHANTABILITY, or FITNESS FOR A
PARTICULAR PURPOSE. You are solely responsible for determining the
appropriateness of using or redistributing the Work and assume any
risks associated with Your exercise of permissions under this License.
8. Limitation of Liability. In no event and under no legal theory,
whether in tort (including negligence), contract, or otherwise,
unless required by applicable law (such as deliberate and grossly
negligent acts) or agreed to in writing, shall any Contributor be
liable to You for damages, including any direct, indirect, special,
incidental, or consequential damages of any character arising as a
result of this License or out of the use or inability to use the
Work (including but not limited to damages for loss of goodwill,
work stoppage, computer failure or malfunction, or any and all
other commercial damages or losses), even if such Contributor
has been advised of the possibility of such damages.
9. Accepting Warranty or Additional Liability. While redistributing
the Work or Derivative Works thereof, You may choose to offer,
and charge a fee for, acceptance of support, warranty, indemnity,
or other liability obligations and/or rights consistent with this
License. However, in accepting such obligations, You may act only
on Your own behalf and on Your sole responsibility, not on behalf
of any other Contributor, and only if You agree to indemnify,
defend, and hold each Contributor harmless for any liability
incurred by, or claims asserted against, such Contributor by reason
of your accepting any such warranty or additional liability.
END OF TERMS AND CONDITIONS
APPENDIX: How to apply the Apache License to your work.
To apply the Apache License to your work, attach the following
boilerplate notice, with the fields enclosed by brackets "{}"
replaced with your own identifying information. (Don't include
the brackets!) The text should be enclosed in the appropriate
comment syntax for the file format. We also recommend that a
file or class name and description of purpose be included on the
same "printed page" as the copyright notice for easier
identification within third-party archives.
Copyright {yyyy} {name of copyright owner}
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.
================================================
FILE: aavt/.gitignore
================================================
/build
================================================
FILE: aavt/bintrayUpload.gradle
================================================
apply plugin: 'com.github.dcendents.android-maven'
apply plugin: 'com.jfrog.bintray'
// 配置版本
version = rootProject.ext.vName
// 定义相关网站
def siteUrl = 'https://github.com/doggycoder/AAVT' // 项目的主页
def gitUrl = 'https://github.com/doggycoder/AAVT.git'
group = "com.wuwang.aavt"
install {
repositories.mavenInstaller {
// This generates POM.xml with proper parameters
pom {
project {
packaging 'aar'
// Add your description here
name 'Aavt'
url siteUrl
// Set your license
licenses {
license {
name 'The Apache Software License, Version 2.0'
url 'http://www.apache.org/licenses/LICENSE-2.0.txt'
}
}
developers {
developer {
id 'doggycoder' //填写的一些基本信息
name 'doggycoder'
email '158183202@qq.com' // 填写邮箱
}
}
scm {
connection gitUrl
developerConnection gitUrl
url siteUrl
}
}
}
}
}
// 打包 javadocjar 和 sourcejar
task sourcesJar(type: Jar) {
from android.sourceSets.main.java.srcDirs
classifier = 'sources'
}
task javadoc(type: Javadoc) {
source = android.sourceSets.main.java.srcDirs
classpath += project.files(android.getBootClasspath().join(File.pathSeparator))
}
task javadocJar(type: Jar, dependsOn: javadoc) {
classifier = 'javadoc'
from javadoc.destinationDir
}
artifacts {
archives javadocJar
archives sourcesJar
}
def propertiesFile = project.rootProject.file('local.properties')
if(propertiesFile.exists()){
Properties properties = new Properties()
properties.load(propertiesFile.newDataInputStream())
bintray {
user = properties.getProperty("bintray.user")
key = properties.getProperty("bintray.apikey")
configurations = ['archives']
pkg {
repo = "maven"
name = "Aavt" //发布到JCenter上的项目名字
websiteUrl = siteUrl
vcsUrl = gitUrl
licenses = ["Apache-2.0"]
publish = true
}
}
}
javadoc { //jav doc采用utf-8编码否则会报“GBK的不可映射字符”错误
options{
encoding "UTF-8"
charSet 'UTF-8'
links "http://docs.oracle.com/javase/7/docs/api"
}
}
================================================
FILE: aavt/build.gradle
================================================
apply plugin: 'com.android.library'
apply plugin: 'com.github.dcendents.android-maven'
group='com.wuwang.aavt'
android {
compileSdkVersion 25
defaultConfig {
minSdkVersion 14
targetSdkVersion 25
versionCode rootProject.ext.vCode
versionName rootProject.ext.vName
testInstrumentationRunner "android.support.test.runner.AndroidJUnitRunner"
}
buildTypes {
release {
minifyEnabled false
proguardFiles getDefaultProguardFile('proguard-android.txt'), 'proguard-rules.pro'
}
}
}
task buildMyJar(type: Jar, dependsOn: ['build']) {
//导出的jar文件名称
archiveName = 'Aavt.jar'
//从哪个目录打包jar
from('build/intermediates/classes/release/')
destinationDir = file('output')
//去掉不要的类
exclude('**/BuildConfig.class')
exclude('**/BuildConfig\$*.class')
exclude('**/R.class')
exclude('**/R\$*.class')
include('com/wuwang/aavt/**')
// doLast{
// copy {
// from('libs/','build/intermediates/cmake/release/obj/')
// into('output/')
// }
// }
}
dependencies {
implementation fileTree(dir: 'libs', include: ['*.jar'])
androidTestImplementation('com.android.support.test.espresso:espresso-core:2.2.2', {
exclude group: 'com.android.support', module: 'support-annotations'
})
implementation 'com.android.support:appcompat-v7:25.3.1'
testImplementation 'junit:junit:4.12'
}
apply from: "bintrayUpload.gradle"
================================================
FILE: aavt/proguard-rules.pro
================================================
# Add project specific ProGuard rules here.
# By default, the flags in this file are appended to flags specified
# in D:\Android\android-sdk/tools/proguard/proguard-android.txt
# You can edit the include path and order by changing the proguardFiles
# directive in build.gradle.
#
# For more details, see
# http://developer.android.com/guide/developing/tools/proguard.html
# Add any project specific keep options here:
# If your project uses WebView with JS, uncomment the following
# and specify the fully qualified class name to the JavaScript interface
# class:
#-keepclassmembers class fqcn.of.javascript.interface.for.webview {
# public *;
#}
# Uncomment this to preserve the line number information for
# debugging stack traces.
#-keepattributes SourceFile,LineNumberTable
# If you keep the line number information, uncomment this to
# hide the original source file name.
#-renamesourcefileattribute SourceFile
================================================
FILE: aavt/src/androidTest/java/com/wuwang/aavt/ExampleInstrumentedTest.java
================================================
package com.wuwang.aavt;
import android.content.Context;
import android.support.test.InstrumentationRegistry;
import android.support.test.runner.AndroidJUnit4;
import org.junit.Test;
import org.junit.runner.RunWith;
import static org.junit.Assert.*;
/**
* Instrumentation test, which will execute on an Android device.
*
* @see Testing documentation
*/
@RunWith(AndroidJUnit4.class)
public class ExampleInstrumentedTest {
@Test
public void useAppContext() throws Exception {
// Context of the app under test.
Context appContext = InstrumentationRegistry.getTargetContext();
assertEquals("com.wuwang.aavt.test", appContext.getPackageName());
}
}
================================================
FILE: aavt/src/main/AndroidManifest.xml
================================================
================================================
FILE: aavt/src/main/assets/shader/base.frag
================================================
precision mediump float;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
void main() {
gl_FragColor = texture2D( uTexture, vTextureCo);
}
================================================
FILE: aavt/src/main/assets/shader/base.vert
================================================
precision highp float;
precision highp int;
attribute vec4 aVertexCo;
attribute vec2 aTextureCo;
uniform mat4 uVertexMatrix;
uniform mat4 uTextureMatrix;
varying vec2 vTextureCo;
void main(){
gl_Position = uVertexMatrix*aVertexCo;
vTextureCo = (uTextureMatrix*vec4(aTextureCo,0,1)).xy;
}
================================================
FILE: aavt/src/main/assets/shader/beauty/beauty.frag
================================================
precision highp float;
precision highp int;
uniform sampler2D uTexture;
uniform int uIternum;
uniform float uACoef; //参数
uniform float uMixCoef; //混合系数
varying highp vec2 vTextureCo;
varying highp vec2 vBlurCoord1s[14];
const float distanceNormalizationFactor = 4.0;
const mat3 saturateMatrix = mat3(1.1102,-0.0598,-0.061,-0.0774,1.0826,-0.1186,-0.0228,-0.0228,1.1772);
void main() {
vec3 centralColor;
float central;
float gaussianWeightTotal;
float sum;
float sampleColor;
float distanceFromCentralColor;
float gaussianWeight;
central = texture2D( uTexture, vTextureCo ).g;
gaussianWeightTotal = 0.2;
sum = central * 0.2;
for (int i = 0; i < 6; i++) {
sampleColor = texture2D( uTexture, vBlurCoord1s[i] ).g;
distanceFromCentralColor = min( abs( central - sampleColor ) * distanceNormalizationFactor, 1.0 );
gaussianWeight = 0.05 * (1.0 - distanceFromCentralColor);
gaussianWeightTotal += gaussianWeight;
sum += sampleColor * gaussianWeight;
}
for (int i = 6; i < 14; i++) {
sampleColor = texture2D( uTexture, vBlurCoord1s[i] ).g;
distanceFromCentralColor = min( abs( central - sampleColor ) * distanceNormalizationFactor, 1.0 );
gaussianWeight = 0.1 * (1.0 - distanceFromCentralColor);
gaussianWeightTotal += gaussianWeight;
sum += sampleColor * gaussianWeight;
}
sum = sum / gaussianWeightTotal;
centralColor = texture2D( uTexture, vTextureCo ).rgb;
sampleColor = centralColor.g - sum + 0.5;
for (int i = 0; i < 100; i++) {
if(i>=uIternum){
break;
}
if (sampleColor <= 0.5) {
sampleColor = sampleColor * sampleColor * 2.0;
}
else {
sampleColor = 1.0 - ((1.0 - sampleColor)*(1.0 - sampleColor) * 2.0);
}
}
float aa = 1.0 + pow( centralColor.g, 0.3 )*uACoef;
vec3 smoothColor = centralColor*aa - vec3( sampleColor )*(aa - 1.0);
smoothColor = clamp( smoothColor, vec3( 0.0 ), vec3( 1.0 ) );
smoothColor = mix( centralColor, smoothColor, pow( centralColor.g, 0.33 ) );
smoothColor = mix( centralColor, smoothColor, pow( centralColor.g, uMixCoef ) );
gl_FragColor = vec4( pow( smoothColor, vec3( 0.96 ) ), 1.0 );
vec3 satcolor = gl_FragColor.rgb * saturateMatrix;
gl_FragColor.rgb = mix( gl_FragColor.rgb, satcolor, 0.23 );
}
================================================
FILE: aavt/src/main/assets/shader/beauty/beauty.vert
================================================
attribute vec4 aVertexCo;
attribute vec2 aTextureCo;
varying vec2 vTextureCo;
varying vec2 vBlurCoord1s[14];
uniform float uWidth;
uniform float uHeight;
uniform mat4 uVertexMatrix;
uniform mat4 uTextureMatrix;
void main()
{
gl_Position = uVertexMatrix*aVertexCo;
vTextureCo = (uTextureMatrix*vec4(aTextureCo,0,1)).xy;
highp float mul_x = 2.0 / uWidth;
highp float mul_y = 2.0 / uHeight;
vBlurCoord1s[0] = vTextureCo + vec2( 0.0 * mul_x, -10.0 * mul_y );
vBlurCoord1s[1] = vTextureCo + vec2( 8.0 * mul_x, -5.0 * mul_y );
vBlurCoord1s[2] = vTextureCo + vec2( 8.0 * mul_x, 5.0 * mul_y );
vBlurCoord1s[3] = aTextureCo + vec2( 0.0 * mul_x, 10.0 * mul_y );
vBlurCoord1s[4] = aTextureCo + vec2( -8.0 * mul_x, 5.0 * mul_y );
vBlurCoord1s[5] = aTextureCo + vec2( -8.0 * mul_x, -5.0 * mul_y );
mul_x = 1.2 / uWidth;
mul_y = 1.2 / uHeight;
vBlurCoord1s[6] = aTextureCo + vec2( 0.0 * mul_x, -6.0 * mul_y );
vBlurCoord1s[7] = aTextureCo + vec2( -4.0 * mul_x, -4.0 * mul_y );
vBlurCoord1s[8] = aTextureCo + vec2( -6.0 * mul_x, 0.0 * mul_y );
vBlurCoord1s[9] = aTextureCo + vec2( -4.0 * mul_x, 4.0 * mul_y );
vBlurCoord1s[10] = aTextureCo + vec2( 0.0 * mul_x, 6.0 * mul_y );
vBlurCoord1s[11] = aTextureCo + vec2( 4.0 * mul_x, 4.0 * mul_y );
vBlurCoord1s[12] = aTextureCo + vec2( 6.0 * mul_x, 0.0 * mul_y );
vBlurCoord1s[13] = aTextureCo + vec2( 4.0 * mul_x, -4.0 * mul_y );
}
================================================
FILE: aavt/src/main/assets/shader/color/gray.frag
================================================
precision mediump float;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
const highp vec3 CO = vec3(0.2125, 0.7154, 0.0721);
void main() {
gl_FragColor=vec4(vec3(dot(texture2D( uTexture, vTextureCo).rgb,CO)),1.0);
}
================================================
FILE: aavt/src/main/assets/shader/convert/eo_yuv420p.frag
================================================
precision highp float;
precision highp int;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
//为了简化计算,宽高都必须为8的倍数
uniform float uWidth; // 纹理宽
uniform float uHeight; // 纹理高
//转换公式
//Y’= 0.299*R’ + 0.587*G’ + 0.114*B’
//U’= -0.147*R’ - 0.289*G’ + 0.436*B’ = 0.492*(B’- Y’)
//V’= 0.615*R’ - 0.515*G’ - 0.100*B’ = 0.877*(R’- Y’)
//导出原理:采样坐标只作为确定输出位置使用,通过输出纹理计算实际采样位置,进行采样和并转换,
//然后将转换的结果填充到输出位置
float cY(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return c.r*0.2990+c.g*0.5870+c.b*0.1140;
}
float cU(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return -0.1471*c.r - 0.2889*c.g + 0.4360*c.b+0.5000;
}
float cV(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return 0.6150*c.r - 0.5150*c.g - 0.1000*c.b+0.5000;
}
vec2 cPos(float t,float shiftx,float gy){
vec2 pos=vec2(floor(uWidth*vTextureCo.x),floor(uHeight*gy));
return vec2(mod(pos.x*shiftx,uWidth),(pos.y*shiftx+floor(pos.x*shiftx/uWidth))*t);
}
//Y分量的计算
vec4 calculateY(){
vec2 pos=cPos(1.,4.,vTextureCo.y);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]=cY(pos.x/uWidth,textureYPos);
oColor[1]=cY((pos.x+1.)/uWidth,textureYPos);
oColor[2]=cY((pos.x+2.)/uWidth,textureYPos);
oColor[3]=cY((pos.x+3.)/uWidth,textureYPos);
return oColor;
}
//U分量的计算
vec4 calculateU(){
vec2 pos=cPos(2.,8.,vTextureCo.y-0.2500);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]= cU(pos.x/uWidth,textureYPos);
oColor[1]= cU((pos.x+2.)/uWidth,textureYPos);
oColor[2]= cU((pos.x+4.)/uWidth,textureYPos);
oColor[3]= cU((pos.x+6.)/uWidth,textureYPos);
return oColor;
}
//V分量计算
vec4 calculateV(){
vec2 pos=cPos(2.,8.,vTextureCo.y-0.3125);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]=cV(pos.x/uWidth,textureYPos);
oColor[1]=cV((pos.x+2.)/uWidth,textureYPos);
oColor[2]=cV((pos.x+4.)/uWidth,textureYPos);
oColor[3]=cV((pos.x+6.)/uWidth,textureYPos);
return oColor;
}
//UV的计算,YUV420SP用,test
vec4 calculateUV(){
vec2 pos=cPos(2.,4.,vTextureCo.y-0.2500);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]= cU(pos.x/uWidth,textureYPos);
oColor[1]= cV(pos.x/uWidth,textureYPos);
oColor[2]= cU((pos.x+2.)/uWidth,textureYPos);
oColor[3]= cV((pos.x+2.)/uWidth,textureYPos);
return oColor;
}
void main() {
if(vTextureCo.y<0.2500){
gl_FragColor=calculateY();
}else if(vTextureCo.y<0.3125){
gl_FragColor=calculateU();
}else if(vTextureCo.y<0.3750){
gl_FragColor=calculateV();
}else{
gl_FragColor=vec4(0,0,0,0);
}
//gl_FragColor=vec4(rPosX/uWidth,rPosY/uHeight,rPosX/uWidth,rPosY/uHeight);
}
================================================
FILE: aavt/src/main/assets/shader/convert/export_yuv.frag
================================================
precision highp float;
precision highp int;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
uniform float uWidth;
uniform float uHeight;
float cY(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return c.r*0.2126+c.g*0.7152+c.b*0.0722;
}
vec4 cC(float x,float y,float dx,float dy){
vec4 c0=texture2D(uTexture,vec2(x,y));
vec4 c1=texture2D(uTexture,vec2(x+dx,y));
vec4 c2=texture2D(uTexture,vec2(x,y+dy));
vec4 c3=texture2D(uTexture,vec2(x+dx,y+dy));
return (c0+c1+c2+c3)/4.;
}
float cU(float x,float y,float dx,float dy){
vec4 c=cC(x,y,dx,dy);
return -0.09991*c.r - 0.33609*c.g + 0.43600*c.b+0.5000;
}
float cV(float x,float y,float dx,float dy){
vec4 c=cC(x,y,dx,dy);
return 0.61500*c.r - 0.55861*c.g - 0.05639*c.b+0.5000;
}
vec2 cPos(float t,float shiftx,float gy){
vec2 pos=vec2(floor(uWidth*vTextureCo.x),floor(uHeight*gy));
return vec2(mod(pos.x*shiftx,uWidth),(pos.y*shiftx+floor(pos.x*shiftx/uWidth))*t);
}
vec4 calculateY(){
vec2 pos=cPos(1.,4.,vTextureCo.y);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]=cY(pos.x/uWidth,textureYPos);
oColor[1]=cY((pos.x+1.)/uWidth,textureYPos);
oColor[2]=cY((pos.x+2.)/uWidth,textureYPos);
oColor[3]=cY((pos.x+3.)/uWidth,textureYPos);
return oColor;
}
vec4 calculateU(float gy,float dx,float dy){
vec2 pos=cPos(2.,8.,vTextureCo.y-gy);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]= cU(pos.x/uWidth,textureYPos,dx,dy);
oColor[1]= cU((pos.x+2.)/uWidth,textureYPos,dx,dy);
oColor[2]= cU((pos.x+4.)/uWidth,textureYPos,dx,dy);
oColor[3]= cU((pos.x+6.)/uWidth,textureYPos,dx,dy);
return oColor;
}
vec4 calculateV(float gy,float dx,float dy){
vec2 pos=cPos(2.,8.,vTextureCo.y-gy);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]=cV(pos.x/uWidth,textureYPos,dx,dy);
oColor[1]=cV((pos.x+2.)/uWidth,textureYPos,dx,dy);
oColor[2]=cV((pos.x+4.)/uWidth,textureYPos,dx,dy);
oColor[3]=cV((pos.x+6.)/uWidth,textureYPos,dx,dy);
return oColor;
}
vec4 calculateUV(float dx,float dy){
vec2 pos=cPos(2.,4.,vTextureCo.y-0.2500);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]= cU(pos.x/uWidth,textureYPos,dx,dy);
oColor[1]= cV(pos.x/uWidth,textureYPos,dx,dy);
oColor[2]= cU((pos.x+2.)/uWidth,textureYPos,dx,dy);
oColor[3]= cV((pos.x+2.)/uWidth,textureYPos,dx,dy);
return oColor;
}
vec4 calculateVU(float dx,float dy){
vec2 pos=cPos(2.,4.,vTextureCo.y-0.2500);
vec4 oColor=vec4(0);
float textureYPos=pos.y/uHeight;
oColor[0]= cV(pos.x/uWidth,textureYPos,dx,dy);
oColor[1]= cU(pos.x/uWidth,textureYPos,dx,dy);
oColor[2]= cV((pos.x+2.)/uWidth,textureYPos,dx,dy);
oColor[3]= cU((pos.x+2.)/uWidth,textureYPos,dx,dy);
return oColor;
}
================================================
FILE: aavt/src/main/assets/shader/convert/export_yuv420p.frag
================================================
precision highp float;
precision highp int;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
//为了简化计算,宽高都必须为8的倍数
uniform float uWidth; // 纹理宽
uniform float uHeight; // 纹理高
//转换公式
//Y’= 0.299*R’ + 0.587*G’ + 0.114*B’
//U’= -0.147*R’ - 0.289*G’ + 0.436*B’ = 0.492*(B’- Y’)
//V’= 0.615*R’ - 0.515*G’ - 0.100*B’ = 0.877*(R’- Y’)
//导出原理:采样坐标只作为确定输出位置使用,通过输出纹理计算实际采样位置,进行采样和并转换,
//然后将转换的结果填充到输出位置
float cY(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return c.r*0.2990+c.g*0.5870+c.b*0.1140;
}
float cU(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return -0.1471*c.r - 0.2889*c.g + 0.4360*c.b+0.5000;
}
float cV(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return 0.6150*c.r - 0.5150*c.g - 0.1000*c.b+0.5000;
}
vec2 cPos(float t,float shiftx,float shifty){
vec2 pos=vec2(uWidth*vTextureCo.x,uHeight*(vTextureCo-shifty));
return vec2(mod(pos.x*shiftx,uWidth),(pos.y*shiftx+floor(pos.x*shiftx/uWidth))*t);
}
//Y分量的计算
vec4 calculateY(){
//填充点对应图片的位置
float posX=floor(uWidth*vTextureCo.x);
float posY=floor(uHeight*vTextureCo.y);
//实际采样起始点对应图片的位置
float rPosX=mod(posX*4.,uWidth);
float rPosY=posY*4.+floor(posX*4./uWidth);
vec4 oColor=vec4(0);
float textureYPos=rPosY/uHeight;
oColor[0]=cY(rPosX/uWidth,textureYPos);
oColor[1]=cY((rPosX+1.)/uWidth,textureYPos);
oColor[2]=cY((rPosX+2.)/uWidth,textureYPos);
oColor[3]=cY((rPosX+3.)/uWidth,textureYPos);
return oColor;
}
//U分量的计算
vec4 calculateU(){
//U的采样,宽度是1:8,高度是1:2,U的位置高度偏移了1/4,一个点是4个U,采样区域是宽高位8*2
float posX=floor(uWidth*vTextureCo.x);
float posY=floor(uHeight*(vTextureCo.y-0.2500));
//实际采样起始点对应图片的位置
float rPosX=mod(posX*8.,uWidth);
float rPosY=posY*16.+floor(posX*8./uWidth)*2.;
vec4 oColor=vec4(0);
oColor[0]= cU(rPosX/uWidth,rPosY/uHeight);
oColor[1]= cU((rPosX+2.)/uWidth,rPosY/uHeight);
oColor[2]= cU((rPosX+4.)/uWidth,rPosY/uHeight);
oColor[3]= cU((rPosX+6.)/uWidth,rPosY/uHeight);
return oColor;
}
//V分量计算
vec4 calculateV(){
//V的采样,宽度是1:8,高度是1:2,U的位置高度偏移了1/4,一个点是4个V,采样区域是宽高位8*2
float posX=floor(uWidth*vTextureCo.x);
float posY=floor(uHeight*(vTextureCo.y-0.3125));
//实际采样起始点对应图片的位置
float rPosX=mod(posX*8.,uWidth);
float rPosY=posY*16.+floor(posX*8./uWidth)*2.;
vec4 oColor=vec4(0);
oColor[0]=cV(rPosX/uWidth,rPosY/uHeight);
oColor[1]=cV((rPosX+2.)/uWidth,rPosY/uHeight);
oColor[2]=cV((rPosX+4.)/uWidth,rPosY/uHeight);
oColor[3]=cV((rPosX+6.)/uWidth,rPosY/uHeight);
return oColor;
}
//UV的计算,YUV420SP用,test
vec4 calculateUV(){
float posX=floor(uWidth*vTextureCo.x);
float posY=floor(uHeight*(vTextureCo.y-0.2500));
//实际采样起始点对应图片的位置
float rPosX=mod(posX*4.,uWidth);
float rPosY=posY*8.+floor(posX*4./uWidth)*2.;
vec4 oColor=vec4(0);
oColor[0]= cU((rPosX+1.)/uWidth,(rPosY+1.)/uHeight);
oColor[1]= cV((rPosX+1.)/uWidth,(rPosY+1.)/uHeight);
oColor[2]= cU((rPosX+3.)/uWidth,(rPosY+1.)/uHeight);
oColor[3]= cV((rPosX+3.)/uWidth,(rPosY+1.)/uHeight);
return oColor;
}
void main() {
if(vTextureCo.y<0.2500){
gl_FragColor=calculateY();
}else if(vTextureCo.y<0.3125){
gl_FragColor=calculateU();
}else if(vTextureCo.y<0.3750){
gl_FragColor=calculateV();
}else{
gl_FragColor=vec4(0,0,0,0);
}
//gl_FragColor=vec4(rPosX/uWidth,rPosY/uHeight,rPosX/uWidth,rPosY/uHeight);
}
================================================
FILE: aavt/src/main/assets/shader/convert/export_yuv420sp.frag
================================================
precision highp float;
precision highp int;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
//为了简化计算,宽高都必须为8的倍数
uniform float uWidth; // 纹理宽
uniform float uHeight; // 纹理高
//转换公式
//Y’= 0.299*R’ + 0.587*G’ + 0.114*B’
//U’= -0.147*R’ - 0.289*G’ + 0.436*B’ = 0.492*(B’- Y’)
//V’= 0.615*R’ - 0.515*G’ - 0.100*B’ = 0.877*(R’- Y’)
//导出原理:采样坐标只作为确定输出位置使用,通过输出纹理计算实际采样位置,进行采样和并转换,
//然后将转换的结果填充到输出位置
float cY(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return c.r*0.2990+c.g*0.5870+c.b*0.1140;
}
float cU(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return -0.1471*c.r - 0.2889*c.g + 0.4360*c.b+0.5000;
}
float cV(float x,float y){
vec4 c=texture2D(uTexture,vec2(x,y));
return 0.6150*c.r - 0.5150*c.g - 0.1000*c.b+0.5000;
}
vec2 cPos(float t,float shiftx,float shifty){
vec2 pos=vec2(uWidth*vTextureCo.x,uHeight*(vTextureCo-shifty));
return vec2(mod(pos.x*shiftx,uWidth),(pos.y*shiftx+floor(pos.x*shiftx/uWidth))*t);
}
//Y分量的计算
vec4 calculateY(){
//填充点对应图片的位置
float posX=floor(uWidth*vTextureCo.x);
float posY=floor(uHeight*vTextureCo.y);
//实际采样起始点对应图片的位置
float rPosX=mod(posX*4.,uWidth);
float rPosY=posY*4.+floor(posX*4./uWidth);
vec4 oColor=vec4(0);
float textureYPos=rPosY/uHeight;
oColor[0]=cY(rPosX/uWidth,textureYPos);
oColor[1]=cY((rPosX+1.)/uWidth,textureYPos);
oColor[2]=cY((rPosX+2.)/uWidth,textureYPos);
oColor[3]=cY((rPosX+3.)/uWidth,textureYPos);
return oColor;
}
gi git
void main() {
if(vTextureCo.y<0.2500){
gl_FragColor=calculateY();
}else if(vTextureCo.y<0.3750){
gl_FragColor=calculateUV();
}else{
gl_FragColor=vec4(0,0,0,0);
}
}
================================================
FILE: aavt/src/main/assets/shader/effect/fluorescence.frag
================================================
precision highp float;
uniform sampler2D uTexture;
uniform sampler2D uTexture2;
uniform float uWidth;
uniform float uHeight;
varying vec2 vTextureCo;
uniform vec4 uBorderColor;
uniform float uStep;
void main(){
vec4 baseColor=texture2D(uTexture,vTextureCo);
float sobelColor=texture2D(uTexture2,vTextureCo).r;
gl_FragColor=(1.-sobelColor*uStep)*baseColor+sobelColor*uBorderColor*uStep;
}
================================================
FILE: aavt/src/main/assets/shader/effect/water_color.frag
================================================
precision mediump float;
uniform sampler2D uTexture;
uniform sampler2D uNoiseTexture;
uniform float uWidth;
uniform float uHeight;
varying vec2 vTextureCo;
vec4 valueAdd(vec2 pos,float shiftX,float shiftY,float p){
vec2 newPos=vec2((pos.x+shiftX)/uWidth,(pos.y+shiftY)/uHeight);
return texture2D(uTexture,newPos)/p;
}
void main(){
float step=floor(uWidth/128.);
vec2 xy = vec2(vTextureCo.x * uWidth, vTextureCo.y * uHeight);
vec4 color=valueAdd(xy,0.,0.,4.);
color+=valueAdd(xy,-step,-step,16.);
color+=valueAdd(xy,-step,step,8.);
color+=valueAdd(xy,-step,step,16.);
color+=valueAdd(xy,step,-step,8.);
color+=valueAdd(xy,step,step,8.);
color+=valueAdd(xy,step,-step,16.);
color+=valueAdd(xy,step,step,8.);
color+=valueAdd(xy,step,step,16.);
gl_FragColor = color;
}
================================================
FILE: aavt/src/main/assets/shader/effect/water_color_step1.frag
================================================
precision mediump float;
uniform sampler2D uTexture;
uniform sampler2D uNoiseTexture;
uniform float uWidth;
uniform float uHeight;
varying vec2 vTextureCo;
vec4 quant(vec4 cl, float n) {
cl.x = floor(cl.x * 255./n)*n/255.;
cl.y = floor(cl.y * 255./n)*n/255.;
cl.z = floor(cl.z * 255./n)*n/255.;
return cl;
}
void main(void){
vec4 noiseColor = texture2D(uNoiseTexture, vTextureCo);
vec2 newUV = vec2(vTextureCo.x + noiseColor.x / uWidth, vTextureCo.y + noiseColor.y / uHeight);
vec4 fColor = texture2D(uTexture, newUV);
vec4 color = quant(fColor, 255./pow(2., 4.));
//vec4 color = vec4(1., 1., .5, 1.);
gl_FragColor = color;
}
================================================
FILE: aavt/src/main/assets/shader/func/candy.frag
================================================
precision highp float;
uniform sampler2D uTexture;
uniform float uWidth;
uniform float uHeight;
varying vec2 vTextureCo;
const float step=1.;
const mat3 GX=mat3(-1.,0., +1., -2., 0., +2., -1., 0., +1.);
const mat3 GY=mat3(-1., -2., -1., 0., 0., 0., +1., +2., +1.);
//sobel 算子有两个滤波矩阵Gx和Gy,注意:
//边缘检测时Gx为检测纵向边缘,Gy为检测横向边缘
//计算法线时Gx为计算法线的横向偏移,Gy为计算法线的纵向偏移
//Gx为[-1 0 +1 -2 0 +2 -1 0 +1] 3*3矩阵
//Gy为[-1 -2 -1 0 0 0 +1 +2 +1] 3*3矩阵
//candy 最主要的是在sobel的基础上做非最大值抑制
//非最大值抑制前,先要更具Gx和Gy得到强度梯度
float colorR(vec2 center,float shiftX,float shiftY){
return texture2D(uTexture,vec2(vTextureCo.x+shiftX/uWidth,vTextureCo.y+shiftY/uHeight)).r;
}
void main(){
vec2 center=vec2(vTextureCo.x*uWidth,vTextureCo.y*uHeight);
float leftTop=colorR(center,-step,-step);
float centerTop=colorR(center,0.,-step);
float rightTop=colorR(center,step,-step);
float leftCenter=colorR(center,-step,0.);
float rightCenter=colorR(center,step,0.);
float leftBottom=colorR(center,-step,step);
float centerBottom=colorR(center,0.,step);
float rightBottom=colorR(center,step,step);
mat3 d=mat3(colorR(center,-step,-step),colorR(center,0.,-step),colorR(center,step,-step),
colorR(center,-step,0.),colorR(center,0.,0.),colorR(center,step,0.),
colorR(center,-step,step),colorR(center,0.,step),colorR(center,step,step));
//计算Gx Gy
float x = d[0][0]*GX[0][0]+d[1][0]*GX[1][0]+d[2][0]*GX[2][0]+
d[0][1]*GX[0][1]+d[1][1]*GX[1][1]+d[2][1]*GX[2][1]+
d[0][2]*GX[0][2]+d[1][2]*GX[1][2]+d[2][2]*GX[2][2];
float y = d[0][0]*GY[0][0]+d[1][0]*GY[1][0]+d[2][0]*GY[2][0]+
d[0][1]*GY[0][1]+d[1][1]*GY[1][1]+d[2][1]*GY[2][1]+
d[0][2]*GY[0][2]+d[1][2]*GY[1][2]+d[2][2]*GY[2][2];
//计算强度梯度
float G=length(vec2(x,y));
float thita=atan(y,x);
gl_FragColor=vec4(vec3(G),1.);
}
================================================
FILE: aavt/src/main/assets/shader/func/faltung3x3.frag
================================================
precision mediump float;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
uniform mat3 uFaltung;
uniform float uWidth;
uniform float uHeight;
vec4 getColor(float x,float y,float p){
return p*texture2D(uTexture,vec2(x/uWidth,y/uHeight)+vTextureCo);
}
void main() {
vec4 color;
color+=getColor(-1.,-1.,uFaltung[0][0]);
color+=getColor(0.,-1.,uFaltung[1][0]);
color+=getColor(1.,-1.,uFaltung[2][0]);
color+=getColor(-1.,0.,uFaltung[0][1]);
color+=getColor(0.,0.,uFaltung[1][1]);
color+=getColor(1.,0.,uFaltung[2][1]);
color+=getColor(-1.,1.,uFaltung[0][2]);
color+=getColor(0.,1.,uFaltung[1][2]);
color+=getColor(1.,1.,uFaltung[2][2]);
gl_FragColor = color;
}
================================================
FILE: aavt/src/main/assets/shader/func/gauss.frag
================================================
precision mediump float;
varying vec2 vTextureCo;
uniform sampler2D uTexture;
uniform float uWidth;
uniform float uHeight;
vec4 getColor(float x,float y,float p){
return p*texture2D(uTexture,vec2(x/uWidth,y/uHeight)+vTextureCo);
}
void main() {
vec4 color;
color+=getColor(-2.,-2.,2.);
color+=getColor(-1.,-2.,4.);
color+=getColor(0.,-2.,5.);
color+=getColor(1.,-2.,4.);
color+=getColor(2.,-2.,2.);
color+=getColor(-2.,-1.,4.);
color+=getColor(-1.,-1.,9.);
color+=getColor(0.,-1.,12.);
color+=getColor(1.,-1.,9.);
color+=getColor(2.,-1.,4.);
color+=getColor(-2.,1.,4.);
color+=getColor(-1.,1.,9.);
color+=getColor(0.,1.,12.);
color+=getColor(1.,1.,9.);
color+=getColor(2.,1.,4.);
color+=getColor(-2.,0.,9.);
color+=getColor(-1.,0.,12.);
color+=getColor(0.,0.,15.);
color+=getColor(1.,0.,12.);
color+=getColor(2.,0.,9.);
color+=getColor(-2.,2.,2.);
color+=getColor(-1.,2.,4.);
color+=getColor(0.,2.,5.);
color+=getColor(1.,2.,4.);
color+=getColor(2.,2.,2.);
gl_FragColor = color/159.;
}
================================================
FILE: aavt/src/main/assets/shader/func/sobel.frag
================================================
precision highp float;
uniform sampler2D uTexture;
uniform float uWidth;
uniform float uHeight;
varying vec2 vTextureCo;
const float step=1.;
const mat3 GX=mat3(-1.,0., +1., -2., 0., +2., -1., 0., +1.);
const mat3 GY=mat3(-1., -2., -1., 0., 0., 0., +1., +2., +1.);
//sobel 算子有两个滤波矩阵Gx和Gy,注意:
//边缘检测时Gx为检测纵向边缘,Gy为检测横向边缘
//计算法线时Gx为计算法线的横向偏移,Gy为计算法线的纵向偏移
//Gx为[-1 0 +1 -2 0 +2 -1 0 +1] 3*3矩阵
//Gy为[-1 -2 -1 0 0 0 +1 +2 +1] 3*3矩阵
float colorR(vec2 center,float shiftX,float shiftY){
return texture2D(uTexture,vec2(vTextureCo.x+shiftX/uWidth,vTextureCo.y+shiftY/uHeight)).r;
}
void main(){
vec2 center=vec2(vTextureCo.x*uWidth,vTextureCo.y*uHeight);
float leftTop=colorR(center,-step,-step);
float centerTop=colorR(center,0.,-step);
float rightTop=colorR(center,step,-step);
float leftCenter=colorR(center,-step,0.);
float rightCenter=colorR(center,step,0.);
float leftBottom=colorR(center,-step,step);
float centerBottom=colorR(center,0.,step);
float rightBottom=colorR(center,step,step);
mat3 d=mat3(colorR(center,-step,-step),colorR(center,0.,-step),colorR(center,step,-step),
colorR(center,-step,0.),colorR(center,0.,0.),colorR(center,step,0.),
colorR(center,-step,step),colorR(center,0.,step),colorR(center,step,step));
float x = d[0][0]*GX[0][0]+d[1][0]*GX[1][0]+d[2][0]*GX[2][0]+
d[0][1]*GX[0][1]+d[1][1]*GX[1][1]+d[2][1]*GX[2][1]+
d[0][2]*GX[0][2]+d[1][2]*GX[1][2]+d[2][2]*GX[2][2];
float y = d[0][0]*GY[0][0]+d[1][0]*GY[1][0]+d[2][0]*GY[2][0]+
d[0][1]*GY[0][1]+d[1][1]*GY[1][1]+d[2][1]*GY[2][1]+
d[0][2]*GY[0][2]+d[1][2]*GY[1][2]+d[2][2]*GY[2][2];
gl_FragColor=vec4(vec3(length(vec2(x,y))),1.);
}
================================================
FILE: aavt/src/main/assets/shader/func/sobel2.frag
================================================
precision highp float;
uniform sampler2D uTexture;
uniform float uWidth;
uniform float uHeight;
varying vec2 vTextureCo;
const float step=1.;
const mat3 GX=mat3(-1.,0., +1., -1., 0., +1., -1., 0., +1.);
const mat3 GY=mat3(-1., -1., -1., 0., 0., 0., +1., +1., +1.);
//sobel 算子有两个滤波矩阵Gx和Gy,注意:
//边缘检测时Gx为检测纵向边缘,Gy为检测横向边缘
//计算法线时Gx为计算法线的横向偏移,Gy为计算法线的纵向偏移
//Gx为[-1 0 +1 -2 0 +2 -1 0 +1] 3*3矩阵
//Gy为[-1 -2 -1 0 0 0 +1 +2 +1] 3*3矩阵
float colorR(vec2 center,float shiftX,float shiftY){
return texture2D(uTexture,vec2(vTextureCo.x+shiftX/uWidth,vTextureCo.y+shiftY/uHeight)).r;
}
void main(){
vec2 center=vec2(vTextureCo.x*uWidth,vTextureCo.y*uHeight);
float leftTop=colorR(center,-step,-step);
float centerTop=colorR(center,0.,-step);
float rightTop=colorR(center,step,-step);
float leftCenter=colorR(center,-step,0.);
float rightCenter=colorR(center,step,0.);
float leftBottom=colorR(center,-step,step);
float centerBottom=colorR(center,0.,step);
float rightBottom=colorR(center,step,step);
mat3 d=mat3(colorR(center,-step,-step),colorR(center,0.,-step),colorR(center,step,-step),
colorR(center,-step,0.),colorR(center,0.,0.),colorR(center,step,0.),
colorR(center,-step,step),colorR(center,0.,step),colorR(center,step,step));
float x = d[0][0]*GX[0][0]+d[1][0]*GX[1][0]+d[2][0]*GX[2][0]+
d[0][1]*GX[0][1]+d[1][1]*GX[1][1]+d[2][1]*GX[2][1]+
d[0][2]*GX[0][2]+d[1][2]*GX[1][2]+d[2][2]*GX[2][2];
float y = d[0][0]*GY[0][0]+d[1][0]*GY[1][0]+d[2][0]*GY[2][0]+
d[0][1]*GY[0][1]+d[1][1]*GY[1][1]+d[2][1]*GY[2][1]+
d[0][2]*GY[0][2]+d[1][2]*GY[1][2]+d[2][2]*GY[2][2];
gl_FragColor=vec4(vec3(1.)-vec3(length(vec2(x,y))),1.);
}
================================================
FILE: aavt/src/main/assets/shader/oes.frag
================================================
#extension GL_OES_EGL_image_external : require
precision mediump float;
varying vec2 vTextureCo;
uniform samplerExternalOES uTexture;
void main() {
gl_FragColor = texture2D( uTexture, vTextureCo);
}
================================================
FILE: aavt/src/main/assets/shader/oes.vert
================================================
attribute vec4 aVertexCo;
attribute vec2 aTextureCo;
uniform mat4 uVertexMatrix;
uniform mat4 uTextureMatrix;
varying vec2 vTextureCo;
void main(){
gl_Position = uVertexMatrix*aVertexCo;
vTextureCo = (uTextureMatrix*vec4(aTextureCo,0,1)).xy;
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/av/CameraRecorder2.java
================================================
/*
* 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.wuwang.aavt.av;
import android.content.Context;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.media.video.Camera2Provider;
import com.wuwang.aavt.media.video.ITextureProvider;
import com.wuwang.aavt.media.SurfaceEncoder;
import com.wuwang.aavt.media.SoundRecorder;
import com.wuwang.aavt.media.SurfaceShower;
import com.wuwang.aavt.media.VideoSurfaceProcessor;
import com.wuwang.aavt.media.av.AvException;
import com.wuwang.aavt.media.hard.IHardStore;
import com.wuwang.aavt.media.hard.StrengthenMp4MuxStore;
/**
* CameraRecorder2 相机预览及录制工具类
*
* @author wuwang
* @version v1.0 2017:10:26 18:35
*/
public class CameraRecorder2 {
private VideoSurfaceProcessor mTextureProcessor;
private ITextureProvider mCameraProvider;
private SurfaceShower mShower;
private SurfaceEncoder mSurfaceStore;
private IHardStore mMuxer;
private SoundRecorder mSoundRecord;
public CameraRecorder2(Context context){
//用于视频混流和存储
mMuxer=new StrengthenMp4MuxStore(true);
//用于预览图像
mShower=new SurfaceShower();
mShower.setOutputSize(720,1280);
//用于编码图像
mSurfaceStore=new SurfaceEncoder();
mSurfaceStore.setStore(mMuxer);
//用于音频
mSoundRecord=new SoundRecorder(mMuxer);
//用于处理视频图像
mTextureProcessor=new VideoSurfaceProcessor();
mTextureProcessor.setTextureProvider(mCameraProvider=new Camera2Provider(context));
mTextureProcessor.addObserver(mShower);
mTextureProcessor.addObserver(mSurfaceStore);
}
public void setRenderer(Renderer renderer){
mTextureProcessor.setRenderer(renderer);
}
/**
* 设置预览对象,必须是{@link android.view.Surface}、{@link android.graphics.SurfaceTexture}或者
* {@link android.view.TextureView}
* @param surface 预览对象
*/
public void setSurface(Object surface){
mShower.setSurface(surface);
}
/**
* 设置录制的输出路径
* @param path 输出路径
*/
public void setOutputPath(String path){
mMuxer.setOutputPath(path);
}
/**
* 设置预览大小
* @param width 预览区域宽度
* @param height 预览区域高度
*/
public void setPreviewSize(int width,int height){
mShower.setOutputSize(width,height);
}
/**
* 打开数据源
*/
public void open(){
mTextureProcessor.start();
}
/**
* 关闭数据源
*/
public void close(){
mTextureProcessor.stop();
stopRecord();
}
/**
* 打开预览
*/
public void startPreview(){
mShower.open();
}
/**
* 关闭预览
*/
public void stopPreview(){
mShower.close();
}
/**
* 开始录制
*/
public void startRecord(){
mSurfaceStore.open();
mSoundRecord.start();
}
/**
* 关闭录制
*/
public void stopRecord(){
mSoundRecord.stop();
mSurfaceStore.close();
mMuxer.close();
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/av/Mp4Processor.java
================================================
package com.wuwang.aavt.av;
import android.annotation.TargetApi;
import android.graphics.SurfaceTexture;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.media.MediaMetadataRetriever;
import android.media.MediaMuxer;
import android.os.Build;
import android.os.Bundle;
import android.view.Surface;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.egl.EGLConfigAttrs;
import com.wuwang.aavt.egl.EGLContextAttrs;
import com.wuwang.aavt.egl.EglHelper;
import com.wuwang.aavt.log.AvLog;
import com.wuwang.aavt.media.WrapRenderer;
import com.wuwang.aavt.utils.GpuUtils;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Semaphore;
/**
* MP4处理工具,暂时只用于处理图像。
* 4.4的手机不支持video/mp4v-es格式的视频流,MediaMuxer混合无法stop,5.0以上可以
*
*/
@Deprecated
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class Mp4Processor {
private final int TIME_OUT=1000;
private String mInputPath; //输入路径
private String mOutputPath; //输出路径
private MediaCodec mVideoDecoder; //视频解码器
private MediaCodec mVideoEncoder; //视频编码器
//private MediaCodec mAudioDecoder; //音频解码器
//private MediaCodec mAudioEncoder; //音频编码器
private MediaExtractor mExtractor; //音视频分离器
private MediaMuxer mMuxer; //音视频混合器
private EglHelper mEGLHelper; //GL环境创建的帮助类
private MediaCodec.BufferInfo mVideoDecoderBufferInfo; //用于存储当前帧的视频解码信息
//private MediaCodec.BufferInfo mAudioDecoderBufferInfo; //用于存储当前帧的音频解码信息
private MediaCodec.BufferInfo mVideoEncoderBufferInfo; //用于存储当前帧的视频编码信息
private MediaCodec.BufferInfo mAudioEncoderBufferInfo; //用于纯粹当前帧的音频编码信息
private int mAudioEncoderTrack=-1; //解码音轨
private int mVideoEncoderTrack=-1; //解码视轨
private int mAudioDecoderTrack=-1; //编码音轨
private int mVideoDecoderTrack=-1; //编码视轨
//private String mAudioMime;
//private String mVideoMime;
private int mInputVideoWidth=0; //输入视频的宽度
private int mInputVideoHeight=0; //输入视频的高度
private int mOutputVideoWidth=0; //输出视频的宽度
private int mOutputVideoHeight=0; //输出视频的高度
private int mVideoTextureId; //原始视频图像的纹理
private SurfaceTexture mVideoSurfaceTexture; //用于接收原始视频的解码的图像流
private boolean isRenderToWindowSurface; //是否渲染到用户设置的WindowBuffer上,用于测试
private Surface mOutputSurface; //视频输出的Surface
private Thread mDecodeThread;
private Thread mGLThread;
private boolean mCodecFlag=false;
private boolean isVideoExtractorEnd=false;
private boolean isAudioExtractorEnd=false;
private boolean isStarted=false;
private WrapRenderer mRenderer;
private boolean mGLThreadFlag=false;
private Semaphore mSem;
private Semaphore mDecodeSem;
private final Object Extractor_LOCK=new Object();
private final Object MUX_LOCK=new Object();
private final Object PROCESS_LOCK=new Object();
private OnProgressListener mProgressListener;
private boolean isUserWantToStop=false;
private long mVideoStopTimeStamp=0; //视频停止时的时间戳,用于外部主动停止处理时,音频截取
private long mTotalVideoTime=0; //视频的总时长
public Mp4Processor(){
mEGLHelper=new EglHelper();
mVideoDecoderBufferInfo=new MediaCodec.BufferInfo();
//mAudioDecoderBufferInfo=new MediaCodec.BufferInfo();
mVideoEncoderBufferInfo=new MediaCodec.BufferInfo();
mAudioEncoderBufferInfo=new MediaCodec.BufferInfo();
}
/**
* 设置用于处理的MP4文件
* @param path 文件路径
*/
public void setInputPath(String path){
this.mInputPath=path;
}
/**
* 设置处理后的mp4存储的位置
* @param path 文件路径
*/
public void setOutputPath(String path){
this.mOutputPath=path;
}
/**
* 设置直接渲染到指定的Surface上,测试用
* @param surface 渲染的位置
*/
public void setOutputSurface(Surface surface){
this.mOutputSurface=surface;
this.isRenderToWindowSurface=surface!=null;
}
/**
* 设置用户处理接口
* @param renderer 处理接口
*/
public void setRenderer(Renderer renderer){
mRenderer=new WrapRenderer(renderer);
}
public int getVideoSurfaceTextureId(){
return mVideoTextureId;
}
public SurfaceTexture getVideoSurfaceTexture(){
return mVideoSurfaceTexture;
}
/**
* 设置输出Mp4的图像大小,默认为输出大小
* @param width 视频图像宽度
* @param height 视频图像高度
*/
public void setOutputSize(int width,int height){
this.mOutputVideoWidth=width;
this.mOutputVideoHeight=height;
}
public void setOnCompleteListener(OnProgressListener listener){
this.mProgressListener=listener;
}
private boolean prepare() throws IOException {
//todo 获取视频旋转信息,并做出相应处理
synchronized (PROCESS_LOCK){
int videoRotation=0;
MediaMetadataRetriever mMetRet=new MediaMetadataRetriever();
mMetRet.setDataSource(mInputPath);
mExtractor=new MediaExtractor();
mExtractor.setDataSource(mInputPath);
int count=mExtractor.getTrackCount();
//解析Mp4
for (int i=0;i"+mExtractor.getTrackFormat(i));
if(mime.startsWith("audio")){
mAudioDecoderTrack=i;
//todo 暂时不对音频处理,后续需要对音频处理时再修改这个
/*mAudioDecoder=MediaCodec.createDecoderByType(mime);
mAudioDecoder.configure(format,null,null,0);
if(!isRenderToWindowSurface){
Log.e("wuwang", format.toString());
MediaFormat audioFormat=MediaFormat.createAudioFormat(mime,
format.getInteger(MediaFormat.KEY_SAMPLE_RATE),
format.getInteger(MediaFormat.KEY_CHANNEL_COUNT));
audioFormat.setInteger(MediaFormat.KEY_AAC_PROFILE,
format.getInteger(MediaFormat.KEY_AAC_PROFILE));
audioFormat.setInteger(MediaFormat.KEY_BIT_RATE,
Integer.valueOf(mMetRet.extractMetadata(MediaMetadataRetriever.METADATA_KEY_BITRATE)));
mAudioEncoder=MediaCodec.createEncoderByType(mime);
mAudioEncoder.configure(audioFormat,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
}*/
}else if(mime.startsWith("video")){
//5.0以下,不能解析mp4v-es //todo 5.0以上也可能存在问题,目前还不知道原因
// if (Build.VERSION.SDK_INT < Build.VERSION_CODES.LOLLIPOP&&mime.equals(MediaFormat.MIMETYPE_VIDEO_MPEG4)) {
// return false;
// }
mVideoDecoderTrack=i;
MediaFormat originFormat=mExtractor.getTrackFormat(mVideoDecoderTrack);
int frameRate=originFormat.getInteger(MediaFormat.KEY_FRAME_RATE);
frameRate=frameRate==0?24:frameRate;
mTotalVideoTime=Long.valueOf(mMetRet.extractMetadata(MediaMetadataRetriever.METADATA_KEY_DURATION));
String rotation=mMetRet.extractMetadata(MediaMetadataRetriever.METADATA_KEY_VIDEO_ROTATION);
if(rotation!=null){
videoRotation=Integer.valueOf(rotation);
}
if(videoRotation==90||videoRotation==270){
mInputVideoHeight=format.getInteger(MediaFormat.KEY_WIDTH);
mInputVideoWidth=format.getInteger(MediaFormat.KEY_HEIGHT);
}else{
mInputVideoWidth=format.getInteger(MediaFormat.KEY_WIDTH);
mInputVideoHeight=format.getInteger(MediaFormat.KEY_HEIGHT);
}
AvLog.d("createDecoder");
mVideoDecoder= MediaCodec.createDecoderByType(mime);
AvLog.d("createDecoder end");
mVideoTextureId= GpuUtils.createTextureID(true);
mVideoSurfaceTexture=new SurfaceTexture(mVideoTextureId);
mVideoDecoder.configure(format,new Surface(mVideoSurfaceTexture),null,0);
if(!isRenderToWindowSurface){
if(mOutputVideoWidth==0||mOutputVideoHeight==0){
mOutputVideoWidth=mInputVideoWidth;
mOutputVideoHeight=mInputVideoHeight;
}
MediaFormat videoFormat= MediaFormat.createVideoFormat(/*mime*/"video/avc",mOutputVideoWidth,mOutputVideoHeight);
videoFormat.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
videoFormat.setInteger(MediaFormat.KEY_BIT_RATE,mOutputVideoHeight*mOutputVideoWidth*5);
videoFormat.setInteger(MediaFormat.KEY_FRAME_RATE, frameRate);
videoFormat.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL, frameRate*10);
mVideoEncoder= MediaCodec.createEncoderByType(/*mime*/"video/avc");
mVideoEncoder.configure(videoFormat,null,null, MediaCodec.CONFIGURE_FLAG_ENCODE);
mOutputSurface=mVideoEncoder.createInputSurface();
Bundle bundle=new Bundle();
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.KITKAT) {
bundle.putInt(MediaCodec.PARAMETER_KEY_VIDEO_BITRATE,mOutputVideoHeight*mOutputVideoWidth*5);
mVideoEncoder.setParameters(bundle);
}
}
}
}
if(!isRenderToWindowSurface){
//如果用户没有设置渲染到指定Surface,就需要导出视频,暂时不对音频做处理
mMuxer=new MediaMuxer(mOutputPath, MediaMuxer.OutputFormat.MUXER_OUTPUT_MPEG_4);
// mMuxer.setOrientationHint(videoRotation);
AvLog.d("video rotation:"+videoRotation);
//如果mp4中有音轨
if(mAudioDecoderTrack>=0){
MediaFormat format=mExtractor.getTrackFormat(mAudioDecoderTrack);
AvLog.d("audio track-->"+format.toString());
mAudioEncoderTrack=mMuxer.addTrack(format);
}
}
}
return true;
}
public boolean start() throws IOException {
synchronized (PROCESS_LOCK){
if(!isStarted){
if(!prepare()){
AvLog.d("prepare failed");
return false;
}
isUserWantToStop=false;
isVideoExtractorEnd=false;
isVideoExtractorEnd=false;
mGLThreadFlag=true;
mVideoDecoder.start();
//mAudioDecoder.start();
if(!isRenderToWindowSurface){
//mAudioEncoder.start();
mVideoEncoder.start();
}
mGLThread=new Thread(new Runnable() {
@Override
public void run() {
glRunnable();
}
});
mGLThread.start();
mCodecFlag=true;
mDecodeThread=new Thread(new Runnable() {
@Override
public void run() {
//视频处理
if(mVideoDecoderTrack>=0){
AvLog.d("videoDecodeStep start");
codecNum=0;
while (mCodecFlag&&!videoDecodeStep()){};
AvLog.d("videoDecodeStep end--FrameNum="+codecNum);
mGLThreadFlag=false;
try {
mSem.release();
mGLThread.join();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
//将原视频中的音频复制到新视频中
if(mAudioDecoderTrack>=0&&mVideoEncoderTrack>=0){
ByteBuffer buffer= ByteBuffer.allocate(1024*32);
while (mCodecFlag&&!audioDecodeStep(buffer)){};
buffer.clear();
}
AvLog.d("codec thread_finish");
mCodecFlag=false;
avStop();
//todo 判断是用户取消了的情况
if(mProgressListener!=null){
mProgressListener.onComplete(mOutputPath);
}
}
});
mDecodeThread.start();
isStarted=true;
}
}
return true;
}
/**
* 等待解码线程执行完毕,异步线程同步等待
*/
public void waitProcessFinish() throws InterruptedException {
if(mDecodeThread!=null&&mDecodeThread.isAlive()){
mDecodeThread.join();
}
}
private boolean audioDecodeStep(ByteBuffer buffer){
boolean isTimeEnd=false;
buffer.clear();
synchronized (Extractor_LOCK){
mExtractor.selectTrack(mAudioDecoderTrack);
int length=mExtractor.readSampleData(buffer,0);
if(length!=-1){
int flags=mExtractor.getSampleFlags();
mAudioEncoderBufferInfo.size=length;
mAudioEncoderBufferInfo.flags=flags;
mAudioEncoderBufferInfo.presentationTimeUs=mExtractor.getSampleTime();
mAudioEncoderBufferInfo.offset=0;
AvLog.d("audio sampleTime="+mAudioEncoderBufferInfo.presentationTimeUs);
isTimeEnd=mExtractor.getSampleTime()>=mVideoStopTimeStamp;
mMuxer.writeSampleData(mAudioEncoderTrack,buffer,mAudioEncoderBufferInfo);
}
isAudioExtractorEnd=!mExtractor.advance();
}
return isAudioExtractorEnd||isTimeEnd;
}
//视频解码到SurfaceTexture上,以供后续处理。返回值为是否是最后一帧视频
private int codecNum=0;
private boolean videoDecodeStep(){
int mInputIndex=mVideoDecoder.dequeueInputBuffer(TIME_OUT);
if(mInputIndex>=0){
ByteBuffer buffer=getInputBuffer(mVideoDecoder,mInputIndex);
buffer.clear();
synchronized (Extractor_LOCK) {
mExtractor.selectTrack(mVideoDecoderTrack);
int ret = mExtractor.readSampleData(buffer, 0);
if (ret != -1) {
mVideoStopTimeStamp=mExtractor.getSampleTime();
AvLog.d("mVideoStopTimeStamp:"+mVideoStopTimeStamp);
mVideoDecoder.queueInputBuffer(mInputIndex, 0, ret, mVideoStopTimeStamp, mExtractor.getSampleFlags());
}
isVideoExtractorEnd = !mExtractor.advance();
}
}
while (true){
int mOutputIndex=mVideoDecoder.dequeueOutputBuffer(mVideoDecoderBufferInfo,TIME_OUT);
if(mOutputIndex>=0){
try {
AvLog.d(" mDecodeSem.acquire ");
if(!isUserWantToStop){
mDecodeSem.acquire();
}
AvLog.d(" mDecodeSem.acquire end ");
} catch (InterruptedException e) {
e.printStackTrace();
}
codecNum++;
mVideoDecoder.releaseOutputBuffer(mOutputIndex,true);
mSem.release();
}else if(mOutputIndex== MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
//MediaFormat format=mVideoDecoder.getOutputFormat();
}else if(mOutputIndex== MediaCodec.INFO_TRY_AGAIN_LATER){
break;
}
}
return isVideoExtractorEnd||isUserWantToStop;
}
private boolean videoEncodeStep(boolean isEnd){
if(isEnd){
mVideoEncoder.signalEndOfInputStream();
}
while (true){
int mOutputIndex=mVideoEncoder.dequeueOutputBuffer(mVideoEncoderBufferInfo,TIME_OUT);
AvLog.d("videoEncodeStep-------------------mOutputIndex="+mOutputIndex+"/"+mVideoEncoderBufferInfo.presentationTimeUs);
if(mOutputIndex>=0){
ByteBuffer buffer=getOutputBuffer(mVideoEncoder,mOutputIndex);
if(mVideoEncoderBufferInfo.size>0){
mMuxer.writeSampleData(mVideoEncoderTrack,buffer,mVideoEncoderBufferInfo);
}
mVideoEncoder.releaseOutputBuffer(mOutputIndex,false);
}else if(mOutputIndex== MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
MediaFormat format=mVideoEncoder.getOutputFormat();
AvLog.d("video format -->"+format.toString());
mVideoEncoderTrack=mMuxer.addTrack(format);
mMuxer.start();
synchronized (MUX_LOCK){
MUX_LOCK.notifyAll();
}
}else if(mOutputIndex== MediaCodec.INFO_TRY_AGAIN_LATER){
break;
}
}
return false;
}
private void glRunnable(){
mSem=new Semaphore(0);
mDecodeSem=new Semaphore(1);
boolean ret=mEGLHelper.createGLESWithSurface(new EGLConfigAttrs(),new EGLContextAttrs(),mOutputSurface);
if(!ret){
return;
}
if(mRenderer==null){
mRenderer=new WrapRenderer(null);
}
mRenderer.create();
mRenderer.sizeChanged(mOutputVideoWidth,mOutputVideoHeight);
int frameNum=0;
while (mGLThreadFlag){
try {
AvLog.d(" mSem.acquire ");
mSem.acquire();
AvLog.d(" mSem.acquire end");
} catch (InterruptedException e) {
e.printStackTrace();
}
if(mGLThreadFlag){
mVideoSurfaceTexture.updateTexImage();
//todo 带有rotation的视频,还需要处理
mVideoSurfaceTexture.getTransformMatrix(mRenderer.getTextureMatrix());
mRenderer.draw(mVideoTextureId);
mEGLHelper.setPresentationTime(mEGLHelper.getDefaultSurface(),mVideoDecoderBufferInfo.presentationTimeUs*1000);
if(!isRenderToWindowSurface){
frameNum++;
videoEncodeStep(false);
}
mEGLHelper.swapBuffers(mEGLHelper.getDefaultSurface());
}
if(mProgressListener!=null){
mProgressListener.onProgress(getTotalVideoTime()*1000L,mVideoDecoderBufferInfo.presentationTimeUs);
}
mDecodeSem.release();
}
AvLog.d("Encode Frame num-----:"+frameNum);
if(!isRenderToWindowSurface){
videoEncodeStep(true);
}
mRenderer.destroy();
mEGLHelper.destroyGLES(mEGLHelper.getDefaultSurface(),mEGLHelper.getDefaultContext());
}
public long getPresentationTime(){
return mVideoDecoderBufferInfo.presentationTimeUs*1000;
}
public long getTotalVideoTime(){
return mTotalVideoTime;
}
private void avStop(){
if(isStarted){
if(mVideoDecoder!=null){
mVideoDecoder.stop();
mVideoDecoder.release();
mVideoDecoder=null;
}
if(!isRenderToWindowSurface&&mVideoEncoder!=null){
mVideoEncoder.stop();
mVideoEncoder.release();
mVideoEncoder=null;
}
if(!isRenderToWindowSurface){
if(mMuxer!=null&&mVideoEncoderTrack>=0){
try {
mMuxer.stop();
}catch (IllegalStateException e){
e.printStackTrace();
}
}
if(mMuxer!=null){
try {
mMuxer.release();
}catch (IllegalStateException e){
e.printStackTrace();
}
mMuxer=null;
}
}
if(mExtractor!=null){
mExtractor.release();
}
isStarted=false;
mVideoEncoderTrack=-1;
mVideoDecoderTrack=-1;
mAudioEncoderTrack=-1;
mAudioDecoderTrack=-1;
}
}
public boolean stop() throws InterruptedException {
synchronized (PROCESS_LOCK){
if(isStarted){
if(mCodecFlag){
mDecodeSem.release();
isUserWantToStop=true;
if(mDecodeThread!=null&&mDecodeThread.isAlive()){
AvLog.d("try to stop decode thread");
mDecodeThread.join();
AvLog.d("decode thread stoped");
}
isUserWantToStop=false;
}
}
}
return true;
}
public boolean release() throws InterruptedException {
synchronized (PROCESS_LOCK){
if(mCodecFlag){
stop();
}
}
return true;
}
private ByteBuffer getInputBuffer(MediaCodec codec, int index){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return codec.getInputBuffer(index);
}else{
return codec.getInputBuffers()[index];
}
}
private ByteBuffer getOutputBuffer(MediaCodec codec, int index){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return codec.getOutputBuffer(index);
}else{
return codec.getOutputBuffers()[index];
}
}
public interface OnProgressListener{
void onProgress(long max, long current);
void onComplete(String path);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/av/Mp4Processor2.java
================================================
/*
* 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.wuwang.aavt.av;
import com.wuwang.aavt.media.video.Mp4Provider;
import com.wuwang.aavt.media.SurfaceEncoder;
import com.wuwang.aavt.media.SurfaceShower;
import com.wuwang.aavt.media.VideoSurfaceProcessor;
import com.wuwang.aavt.media.hard.IHardStore;
import com.wuwang.aavt.media.hard.StrengthenMp4MuxStore;
/**
* Mp4Processor2 用于处理Mp4文件
*
* @author wuwang
* @version v1.0 2017:10:26 18:48
*/
public class Mp4Processor2 {
private VideoSurfaceProcessor mTextureProcessor;
private Mp4Provider mMp4Provider;
private SurfaceShower mShower;
private SurfaceEncoder mSurfaceStore;
private IHardStore mMuxer;
public Mp4Processor2(){
//用于视频混流和存储
mMuxer=new StrengthenMp4MuxStore(true);
//用于预览图像
mShower=new SurfaceShower();
mShower.setOutputSize(720,1280);
//用于编码图像
mSurfaceStore=new SurfaceEncoder();
mSurfaceStore.setStore(mMuxer);
//用于音频
// mSoundRecord=new SoundRecorder(mMuxer);
mMp4Provider=new Mp4Provider();
mMp4Provider.setStore(mMuxer);
//用于处理视频图像
mTextureProcessor=new VideoSurfaceProcessor();
mTextureProcessor.setTextureProvider(mMp4Provider);
mTextureProcessor.addObserver(mShower);
mTextureProcessor.addObserver(mSurfaceStore);
}
public void setSurface(Object surface){
mShower.setSurface(surface);
}
public void setInputPath(String path){
mMp4Provider.setInputPath(path);
}
public void setOutputPath(String path){
mMuxer.setOutputPath(path);
}
public void setPreviewSize(int width,int height){
mShower.setOutputSize(width,height);
}
public void open(){
mTextureProcessor.start();
}
public void close(){
mTextureProcessor.stop();
}
public void startPreview(){
mShower.open();
}
public void stopPreview(){
mShower.close();
}
public void startRecord(){
mSurfaceStore.open();
// mSoundRecord.start();
}
public void stopRecord(){
mSurfaceStore.close();
// mSoundRecord.stop();
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/av/SurfaceRecorder.java
================================================
package com.wuwang.aavt.av;
import android.content.Context;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.media.video.Camera2Provider;
import com.wuwang.aavt.media.video.ITextureProvider;
import com.wuwang.aavt.media.SoundRecorder;
import com.wuwang.aavt.media.SurfaceEncoder;
import com.wuwang.aavt.media.SurfaceShower;
import com.wuwang.aavt.media.VideoSurfaceProcessor;
import com.wuwang.aavt.media.av.AvException;
import com.wuwang.aavt.media.hard.IHardStore;
import com.wuwang.aavt.media.hard.StrengthenMp4MuxStore;
/**
* @author wuwang
* @version 1.00 , 2018/11/14
*/
public class SurfaceRecorder {
private Context context;
private VideoSurfaceProcessor mTextureProcessor;
private SurfaceShower mShower;
private SurfaceEncoder mSurfaceStore;
private IHardStore mMuxer;
private ITextureProvider mProvider;
private SoundRecorder mSoundRecord;
private int mPreviewWidth = 720;
private int mPreviewHeight = 1280;
private int mRecordWidth = 720;
private int mRecordHeight = 1280;
private String path;
private Renderer mRender;
private Object mShowSurface;
public SurfaceRecorder(Context context){
this.context = context;
}
public void setTextureProvider(ITextureProvider provider){
this.mProvider = provider;
}
public void setPreviewSize(int width,int height){
this.mPreviewWidth = width;
this.mPreviewHeight = height;
}
public void setRecordSize(int width,int height){
this.mRecordWidth = width;
this.mRecordHeight = height;
}
public void setSurface(Object surface){
this.mShowSurface = surface;
}
/**
* 设置录制的输出路径
* @param path 输出路径
*/
public void setOutputPath(String path){
this.path = path;
}
public void setRenderer(Renderer renderer){
this.mRender = renderer;
}
public void open(){
if(mMuxer == null){
mMuxer=new StrengthenMp4MuxStore(true);
mMuxer.setOutputPath(path);
}
if(mProvider == null){
mProvider=new Camera2Provider(context);
}
if(mShower == null){
//用于预览图像
mShower=new SurfaceShower();
mShower.setOutputSize(mPreviewWidth,mPreviewHeight);
mShower.setSurface(mShowSurface);
}
if(mSurfaceStore == null){
//用于编码图像
mSurfaceStore=new SurfaceEncoder();
mSurfaceStore.setOutputSize(mRecordWidth,mRecordHeight);
mSurfaceStore.setStore(mMuxer);
}
if(mSoundRecord == null){
//用于音频
mSoundRecord=new SoundRecorder(mMuxer);
}
if(mTextureProcessor == null){
//用于处理视频图像
mTextureProcessor=new VideoSurfaceProcessor();
mTextureProcessor.setTextureProvider(mProvider);
mTextureProcessor.addObserver(mShower);
mTextureProcessor.addObserver(mSurfaceStore);
mTextureProcessor.setRenderer(mRender);
}
mTextureProcessor.start();
}
public void close(){
if(mTextureProcessor != null){
mTextureProcessor.stop();
}
}
public void startPreview(){
if(mShower != null){
mShower.open();
}
}
public void stopPreview(){
if(mShower != null){
mShower.close();
}
}
/**
* 开始录制
*/
public void startRecord(){
if(mSurfaceStore != null && mSoundRecord !=null){
mSurfaceStore.open();
mSoundRecord.start();
}
}
/**
* 关闭录制
*/
public void stopRecord(){
if(mSoundRecord != null && mSurfaceStore != null && mMuxer != null){
mSoundRecord.stop();
mSurfaceStore.close();
mMuxer.close();
}
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/av/VideoCapture.java
================================================
package com.wuwang.aavt.av;
import android.annotation.SuppressLint;
import android.content.Context;
import android.util.SparseArray;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.media.video.CameraProvider;
import com.wuwang.aavt.media.video.ITextureProvider;
import com.wuwang.aavt.media.video.Mp4Provider;
import com.wuwang.aavt.media.SurfaceEncoder;
import com.wuwang.aavt.media.SurfaceShower;
import com.wuwang.aavt.media.VideoSurfaceProcessor;
import com.wuwang.aavt.media.hard.IHardStore;
import com.wuwang.aavt.media.hard.StrengthenMp4MuxStore;
/**
* @author wuwang
* @version 1.00 , 2019/03/13
*/
public class VideoCapture {
public static final int KEY_CAMERA_MIN_WIDTH = 1;
public static final int KEY_CAMERA_RATE = 2;
public static final int KEY_OUTPUT_WIDTH = 3;
public static final int KEY_OUTPUT_HEIGHT = 4;
public static final int KEY_OUTPUT_PATH = 5;
public static final int KEY_PREVIEW_WIDTH = 6;
public static final int KEY_PREVIEW_HEIGHT = 7;
private Context context;
private ITextureProvider provider;
private SurfaceShower shower;
private SurfaceEncoder encoder;
private VideoSurfaceProcessor processor;
private IHardStore muxer;
private SparseArray propFloat;
private SparseArray propStr;
private Renderer renderer;
private Object showSurface;
@SuppressLint("SdCardPath")
private static final String DEFAULT_OUTPUT_PATH = "/mnt/sdcard/test.mp4";
public VideoCapture(Context context){
this.context = context;
propFloat = new SparseArray<>();
propStr = new SparseArray<>();
}
public void setRenderer(Renderer renderer){
this.renderer = renderer;
if(processor != null){
processor.setRenderer(renderer);
}
}
public void setProperty(int key,float value){
propFloat.put(key,value);
}
public void setProperty(int key,String value){
propStr.put(key,value);
}
@SuppressLint("SdCardPath")
public void open(int id){
provider = new CameraProvider();
((CameraProvider) provider).setDefaultCamera(id);
((CameraProvider) provider).setCameraSize((int)(float)(propFloat.get(KEY_CAMERA_MIN_WIDTH,720f)),propFloat.get(KEY_CAMERA_RATE,1.7f));
shower = new SurfaceShower();
shower.setOutputSize((int)(float)(propFloat.get(KEY_PREVIEW_WIDTH,720.0f)),
(int)(float)(propFloat.get(KEY_PREVIEW_HEIGHT,1280.0f)));
muxer = new StrengthenMp4MuxStore(false);
muxer.setOutputPath(propStr.get(KEY_OUTPUT_PATH,DEFAULT_OUTPUT_PATH));
encoder = new SurfaceEncoder();
encoder.setStore(muxer);
encoder.setOutputSize((int)(float)(propFloat.get(KEY_OUTPUT_WIDTH,368.0f)),
(int)(float)(propFloat.get(KEY_OUTPUT_HEIGHT,640.f)));
processor = new VideoSurfaceProcessor();
processor.setTextureProvider(provider);
processor.addObserver(shower);
processor.addObserver(encoder);
setRenderer(renderer);
processor.start();
}
public void open(String path){
muxer = new StrengthenMp4MuxStore(false);
muxer.setOutputPath(propStr.get(KEY_OUTPUT_PATH,DEFAULT_OUTPUT_PATH));
provider = new Mp4Provider();
((Mp4Provider) provider).setInputPath(path);
((Mp4Provider) provider).setStore(muxer);
shower = new SurfaceShower();
shower.setOutputSize((int)(float)(propFloat.get(KEY_OUTPUT_WIDTH,720.0f)),
(int)(float)(propFloat.get(KEY_OUTPUT_HEIGHT,1280.0f)));
encoder = new SurfaceEncoder();
encoder.setStore(muxer);
encoder.setOutputSize((int)(float)(propFloat.get(KEY_OUTPUT_WIDTH,368.0f)),
(int)(float)(propFloat.get(KEY_OUTPUT_HEIGHT,640.f)));
processor = new VideoSurfaceProcessor();
processor.setTextureProvider(provider);
processor.addObserver(shower);
processor.addObserver(encoder);
setRenderer(renderer);
processor.start();
}
public void setPreviewSurface(Object surface){
this.showSurface = surface;
if(shower != null){
shower.setSurface(surface);
}
}
public void startPreview(){
setPreviewSurface(showSurface);
shower.open();
}
public void stopPreview(){
shower.close();
}
public void startRecord(){
encoder.open();
}
public void stopRecord(){
encoder.close();
muxer.close();
}
public void close(){
processor.stop();
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/core/IObservable.java
================================================
package com.wuwang.aavt.core;
/**
* Created by wuwang on 2017/10/20.
*/
public interface IObservable {
void addObserver(IObserver observer);
void notify(Type type);
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/core/IObserver.java
================================================
package com.wuwang.aavt.core;
/**
* Created by wuwang on 2017/10/20.
*/
public interface IObserver {
void onCall(Type type);
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/core/Observable.java
================================================
package com.wuwang.aavt.core;
import java.util.ArrayList;
import java.util.Observer;
/*
* Created by Wuwang on 2017/10/23
*/
public class Observable implements IObservable {
private ArrayList> temp;
@Override
public void addObserver(IObserver observer) {
if(temp==null){
temp=new ArrayList<>();
}
temp.add(observer);
}
public void clear(){
if(temp!=null){
temp.clear();
temp=null;
}
}
@Override
public void notify(Type type) {
for (IObserver t:temp){
t.onCall(type);
}
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/core/Renderer.java
================================================
/*
* 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.wuwang.aavt.core;
/**
* Renderer 渲染接口,渲染的四个接口应该在同一个GL线程中调用
*
* @author wuwang
* @version v1.0 2017:10:31 11:40
*/
public interface Renderer {
/**
* 创建
*/
void create();
/**
* 大小改变
* @param width 宽度
* @param height 高度
*/
void sizeChanged(int width, int height);
/**
* 渲染
* @param texture 输入纹理
*/
void draw(int texture);
/**
* 销毁
*/
void destroy();
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/egl/EGLConfigAttrs.java
================================================
package com.wuwang.aavt.egl;
import android.opengl.EGL14;
import java.util.Arrays;
import javax.microedition.khronos.egl.EGL10;
/*
* Created by Wuwang on 2017/10/18
*/
public class EGLConfigAttrs {
private int red=8;
private int green=8;
private int blue=8;
private int alpha=8;
private int depth=8;
private int renderType=4;
private int surfaceType= EGL10.EGL_WINDOW_BIT;
private boolean makeDefault=false;
public EGLConfigAttrs red(int red){
this.red=red;
return this;
}
public EGLConfigAttrs green(int green){
this.green=green;
return this;
}
public EGLConfigAttrs blue(int blue){
this.blue=blue;
return this;
}
public EGLConfigAttrs alpha(int alpha){
this.alpha=alpha;
return this;
}
public EGLConfigAttrs depth(int depth){
this.depth=depth;
return this;
}
public EGLConfigAttrs renderType(int type){
this.renderType=type;
return this;
}
public EGLConfigAttrs surfaceType(int type){
this.surfaceType=type;
return this;
}
public EGLConfigAttrs makeDefault(boolean def){
this.makeDefault=def;
return this;
}
public boolean isDefault(){
return makeDefault;
}
int[] build(){
return new int[] {
EGL10.EGL_SURFACE_TYPE, surfaceType, //渲染类型
EGL10.EGL_RED_SIZE, red, //指定RGB中的R大小(bits)
EGL10.EGL_GREEN_SIZE, green, //指定G大小
EGL10.EGL_BLUE_SIZE, blue, //指定B大小
EGL10.EGL_ALPHA_SIZE, alpha, //指定Alpha大小,以上四项实际上指定了像素格式
EGL10.EGL_DEPTH_SIZE, depth, //指定深度缓存(Z Buffer)大小
EGL10.EGL_RENDERABLE_TYPE, renderType, //指定渲染api类别, 如上一小节描述,这里或者是硬编码的4(EGL14.EGL_OPENGL_ES2_BIT)
EGL10.EGL_NONE }; //总是以EGL14.EGL_NONE结尾
}
@Override
public String toString() {
return Arrays.toString(build());
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/egl/EGLContextAttrs.java
================================================
package com.wuwang.aavt.egl;
import javax.microedition.khronos.egl.EGL10;
/*
* Created by Wuwang on 2017/10/18
*/
public class EGLContextAttrs {
private int version=2;
private boolean isDefault;
public EGLContextAttrs version(int v){
this.version=v;
return this;
}
public EGLContextAttrs makeDefault(boolean def){
this.isDefault=def;
return this;
}
public boolean isDefault(){
return isDefault;
}
int[] build(){
return new int[]{0x3098,version, EGL10.EGL_NONE};
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/egl/EglHelper.java
================================================
/*
* 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.wuwang.aavt.egl;
import android.annotation.TargetApi;
import android.opengl.EGL14;
import android.opengl.EGLConfig;
import android.opengl.EGLContext;
import android.opengl.EGLDisplay;
import android.opengl.EGLExt;
import android.opengl.EGLSurface;
import android.opengl.GLES11Ext;
import android.opengl.GLES20;
import android.os.Build;
import android.util.Log;
import javax.microedition.khronos.opengles.GL10;
/**
* EGLHelper
*
* @author wuwang
* @version v1.0 2017:11:01 11:41
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class EglHelper {
private boolean isDebug=true;
private EGLDisplay mEGLDisplay;
private EGLConfig mEGLConfig;
private EGLContext mEGLContext;
private EGLSurface mEGLSurface;
public EglHelper(int display){
changeDisplay(display);
}
public EglHelper(){
this(EGL14.EGL_DEFAULT_DISPLAY);
}
public void changeDisplay(int key){
mEGLDisplay=EGL14.eglGetDisplay(key);
//获取版本号,[0]为版本号,[1]为子版本号
int[] versions=new int[2];
EGL14.eglInitialize(mEGLDisplay,versions,0,versions,1);
log(EGL14.eglQueryString(mEGLDisplay, EGL14.EGL_VENDOR));
log(EGL14.eglQueryString(mEGLDisplay, EGL14.EGL_VERSION));
log(EGL14.eglQueryString(mEGLDisplay, EGL14.EGL_EXTENSIONS));
}
public EGLConfig getConfig(EGLConfigAttrs attrs){
EGLConfig[] configs = new EGLConfig[1];
int[] configNum = new int[1];
EGL14.eglChooseConfig(mEGLDisplay,attrs.build(),0,configs,0,1,configNum,0);
//选择的过程可能出现多个,也可能一个都没有,这里只用一个
if(configNum[0]>0){
if(attrs.isDefault()){
mEGLConfig=configs[0];
}
return configs[0];
}
return null;
}
public EGLConfig getDefaultConfig(){
return mEGLConfig;
}
public EGLSurface getDefaultSurface(){
return mEGLSurface;
}
public EGLContext getDefaultContext(){
return mEGLContext;
}
public EGLContext createContext(EGLConfig config,EGLContext share,EGLContextAttrs attrs){
EGLContext context= EGL14.eglCreateContext(mEGLDisplay,config,share,attrs.build(),0);
if(attrs.isDefault()){
mEGLContext=context;
}
return context;
}
public EGLSurface createWindowSurface(EGLConfig config,Object surface){
return EGL14.eglCreateWindowSurface(mEGLDisplay,config,surface,new int[]{EGL14.EGL_NONE},0);
}
public EGLSurface createWindowSurface(Object surface){
mEGLSurface=EGL14.eglCreateWindowSurface(mEGLDisplay,mEGLConfig,surface,new int[]{EGL14.EGL_NONE},0);
return mEGLSurface;
}
public EGLSurface createPBufferSurface(EGLConfig config,int width,int height){
return EGL14.eglCreatePbufferSurface(mEGLDisplay,config,new int[]{EGL14.EGL_WIDTH,width,EGL14.EGL_HEIGHT,height,EGL14.EGL_NONE},0);
}
public boolean createGLESWithSurface(EGLConfigAttrs attrs,EGLContextAttrs ctxAttrs,Object surface){
EGLConfig config=getConfig(attrs.surfaceType(EGL14.EGL_WINDOW_BIT).makeDefault(true));
if(config==null){
log("getConfig failed : "+EGL14.eglGetError());
return false;
}
mEGLContext=createContext(config,EGL14.EGL_NO_CONTEXT,ctxAttrs.makeDefault(true));
if(mEGLContext==EGL14.EGL_NO_CONTEXT){
log("createContext failed : "+EGL14.eglGetError());
return false;
}
mEGLSurface=createWindowSurface(surface);
if(mEGLSurface==EGL14.EGL_NO_SURFACE){
log("createWindowSurface failed : "+EGL14.eglGetError());
return false;
}
if(!EGL14.eglMakeCurrent(mEGLDisplay,mEGLSurface,mEGLSurface,mEGLContext)){
log("eglMakeCurrent failed : "+EGL14.eglGetError());
return false;
}
return true;
}
public boolean makeCurrent(EGLSurface draw,EGLSurface read,EGLContext context){
if(!EGL14.eglMakeCurrent(mEGLDisplay,draw,read,context)){
log("eglMakeCurrent failed : "+EGL14.eglGetError());
}
return true;
}
public boolean makeCurrent(EGLSurface surface,EGLContext context){
return makeCurrent(surface,surface,context);
}
public boolean makeCurrent(EGLSurface surface){
return makeCurrent(surface,mEGLContext);
}
public boolean makeCurrent(){
return makeCurrent(mEGLSurface,mEGLContext);
}
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public void setPresentationTime(EGLSurface surface, long time){
EGLExt.eglPresentationTimeANDROID(mEGLDisplay,surface,time);
}
public EGLSurface createGLESWithPBuffer(EGLConfigAttrs attrs,EGLContextAttrs ctxAttrs,int width,int height){
EGLConfig config=getConfig(attrs.surfaceType(EGL14.EGL_PBUFFER_BIT));
if(config==null){
log("getConfig failed : "+EGL14.eglGetError());
return null;
}
EGLContext eglContext=createContext(config,EGL14.EGL_NO_CONTEXT,ctxAttrs);
if(eglContext==EGL14.EGL_NO_CONTEXT){
log("createContext failed : "+EGL14.eglGetError());
return null;
}
EGLSurface eglSurface=createPBufferSurface(config,width,height);
if(eglSurface==EGL14.EGL_NO_SURFACE){
log("createWindowSurface failed : "+EGL14.eglGetError());
return null;
}
if(!EGL14.eglMakeCurrent(mEGLDisplay,eglSurface,eglSurface,eglContext)){
log("eglMakeCurrent failed : "+EGL14.eglGetError());
return null;
}
return eglSurface;
}
public void swapBuffers(EGLSurface surface){
EGL14.eglSwapBuffers(mEGLDisplay,surface);
}
public boolean destroyGLES(EGLSurface surface,EGLContext context){
EGL14.eglMakeCurrent(mEGLDisplay, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
if(surface!=null){
EGL14.eglDestroySurface(mEGLDisplay,surface);
}
if(context!=null){
EGL14.eglDestroyContext(mEGLDisplay,context);
}
EGL14.eglTerminate(mEGLDisplay);
log("gl destroy gles");
return true;
}
public void destroySurface(EGLSurface surface){
EGL14.eglDestroySurface(mEGLDisplay,surface);
}
public EGLDisplay getDisplay(){
return mEGLDisplay;
}
//创建视频数据流的OES TEXTURE
private void log(String log){
if(isDebug){
Log.e("EGLHelper",log);
}
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/expend/SluggardFilterTool.java
================================================
/*
* 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.wuwang.aavt.expend;
import com.wuwang.aavt.gl.BaseFilter;
import java.util.HashMap;
import java.util.Map;
/**
* SluggardFilterTool 懒汉Filter工具,该工具用于快速使用{@link BaseFilter}及其子类来处理纹理,而无需关注框架中
* 的滤镜处理流程。通常情况下,不推荐使用此工具类,推荐尽可能熟悉框架中的滤镜处理流程。然后自行根据业务逻辑,
* 参照此类进行封装。
*
* @author wuwang
* @version v1.0 2017:11:11 15:41
*/
public class SluggardFilterTool {
private long threadId=-1;
private HashMap filters=new HashMap<>();
/**
* 处理一个纹理,并输出处理后的纹理
* @param texture 输入纹理
* @param width 输出纹理宽度
* @param height 输出纹理高度
* @param clazz 滤镜类型
* @return 输出纹理
*/
public int processTexture(int texture, int width, int height, Class extends BaseFilter> clazz){
long nowThreadId=Thread.currentThread().getId();
if(nowThreadId!=threadId){
filters.clear();
threadId=nowThreadId;
}
BaseFilter filter=filters.get(clazz);
if(filter==null){
try {
filter=clazz.newInstance();
filter.create();
} catch (InstantiationException e) {
e.printStackTrace();
} catch (IllegalAccessException e) {
e.printStackTrace();
}
filters.put(clazz,filter);
}
if(filter!=null){
filter.sizeChanged(width, height);
return filter.drawToTexture(texture);
}
return -1;
}
/**
* 处理一个纹理,并输出处理后的纹理
* @param texture 输入纹理
* @param width 输出纹理宽度
* @param height 输出纹理高度
* @param filter 滤镜实体
* @return 输出纹理
*/
public int processTexture(int texture,int width,int height,BaseFilter filter){
long nowThreadId=Thread.currentThread().getId();
if(nowThreadId!=threadId){
filters.clear();
threadId=nowThreadId;
}
if(filter!=null){
Class clazz=filter.getClass();
if(filters.get(clazz)==null){
filters.put(clazz,filter);
filter.create();
}
filter.sizeChanged(width, height);
return filter.drawToTexture(texture);
}
return -1;
}
public void onGlDestroy(){
for (Map.Entry classBaseFilterEntry : filters.entrySet()) {
BaseFilter filter = classBaseFilterEntry.getValue();
filter.destroy();
}
filters.clear();
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/BaseFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
import android.opengl.GLES20;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.utils.GpuUtils;
import com.wuwang.aavt.utils.MatrixUtils;
import java.nio.ByteBuffer;
import java.nio.ByteOrder;
import java.nio.FloatBuffer;
import java.util.LinkedList;
/**
* BaseFilter 滤镜的基类。对于滤镜而言,要求使用者外部调用的时候必须调用的方法为
* {@link #create()}、{@link #sizeChanged(int, int)}以及{@link #draw(int)}或者
* {@link #drawToTexture(int)}。
* 在实现Filter子类时,通常需要自行编写shader,shader中的变量应同assets下base.frag及
* base.vert中变量一致,可以增加。增加的变量需要重写{@link #onCreate()}方法,在其中
* 获取变量用于传参。
* @author wuwang
* @version v1.0 2017:10:31 10:48
*/
public abstract class BaseFilter implements Renderer {
public static final String BASE_VERT="attribute vec4 aVertexCo;\n" +
"attribute vec2 aTextureCo;\n" +
"\n" +
"uniform mat4 uVertexMatrix;\n" +
"uniform mat4 uTextureMatrix;\n" +
"\n" +
"varying vec2 vTextureCo;\n" +
"\n" +
"void main(){\n" +
" gl_Position = uVertexMatrix*aVertexCo;\n" +
" vTextureCo = (uTextureMatrix*vec4(aTextureCo,0,1)).xy;\n" +
"}";
private float[] mVertexMatrix= MatrixUtils.getOriginalMatrix();
private float[] mTextureMatrix=MatrixUtils.getOriginalMatrix();
protected FloatBuffer mVertexBuffer;
protected FloatBuffer mTextureBuffer;
protected int mWidth;
protected int mHeight;
protected Resources mRes;
private String mVertex;
private String mFragment;
protected int mGLProgram;
protected int mGLVertexCo;
protected int mGLTextureCo;
protected int mGLVertexMatrix;
protected int mGLTextureMatrix;
protected int mGLTexture;
private int mGLWidth;
private int mGLHeight;
private boolean isUseSize=false;
private FrameBuffer mFrameTemp;
private final LinkedList mTasks=new LinkedList<>();
private final Object Lock = new Object();
protected BaseFilter(Resources resource,String vertex,String fragment){
this.mRes=resource;
this.mVertex=vertex;
this.mFragment=fragment;
mFrameTemp=new FrameBuffer();
initBuffer();
}
protected void initBuffer(){
ByteBuffer vertex=ByteBuffer.allocateDirect(32);
vertex.order(ByteOrder.nativeOrder());
mVertexBuffer=vertex.asFloatBuffer();
mVertexBuffer.put(MatrixUtils.getOriginalVertexCo());
mVertexBuffer.position(0);
ByteBuffer texture=ByteBuffer.allocateDirect(32);
texture.order(ByteOrder.nativeOrder());
mTextureBuffer=texture.asFloatBuffer();
mTextureBuffer.put(MatrixUtils.getOriginalTextureCo());
mTextureBuffer.position(0);
}
public void setVertexCo(float[] vertexCo){
mVertexBuffer.clear();
mVertexBuffer.put(vertexCo);
mVertexBuffer.position(0);
}
public void setTextureCo(float[] textureCo){
mTextureBuffer.clear();
mTextureBuffer.put(textureCo);
mTextureBuffer.position(0);
}
public void setVertexBuffer(FloatBuffer vertexBuffer){
this.mVertexBuffer=vertexBuffer;
}
public void setTextureBuffer(FloatBuffer textureBuffer){
this.mTextureBuffer=textureBuffer;
}
public void setVertexMatrix(float[] matrix){
this.mVertexMatrix=matrix;
}
public void setTextureMatrix(float[] matrix){
this.mTextureMatrix=matrix;
}
public float[] getVertexMatrix(){
return mVertexMatrix;
}
public float[] getTextureMatrix(){
return mTextureMatrix;
}
protected void shaderNeedTextureSize(boolean need){
this.isUseSize=need;
}
protected void onCreate(){
if(mRes!=null){
mGLProgram= GpuUtils.createGLProgramByAssetsFile(mRes,mVertex,mFragment);
}else{
mGLProgram= GpuUtils.createGLProgram(mVertex,mFragment);
}
mGLVertexCo= GLES20.glGetAttribLocation(mGLProgram,"aVertexCo");
mGLTextureCo=GLES20.glGetAttribLocation(mGLProgram,"aTextureCo");
mGLVertexMatrix=GLES20.glGetUniformLocation(mGLProgram,"uVertexMatrix");
mGLTextureMatrix=GLES20.glGetUniformLocation(mGLProgram,"uTextureMatrix");
mGLTexture=GLES20.glGetUniformLocation(mGLProgram,"uTexture");
if(isUseSize){
mGLWidth=GLES20.glGetUniformLocation(mGLProgram,"uWidth");
mGLHeight=GLES20.glGetUniformLocation(mGLProgram,"uHeight");
}
}
protected void onSizeChanged(int width,int height){
}
@Override
public final void create() {
if(mVertex!=null&&mFragment!=null){
onCreate();
}
}
@Override
public void sizeChanged(int width, int height) {
this.mWidth=width;
this.mHeight=height;
onSizeChanged(width, height);
mFrameTemp.destroyFrameBuffer();
}
@Override
public void draw(int texture) {
onClear();
onUseProgram();
onSetExpandData();
onBindTexture(texture);
onDraw();
}
/**
* 绘制内容到纹理上
* @param texture 输入纹理ID
* @return 输出纹理ID
*/
public int drawToTexture(int texture){
mFrameTemp.bindFrameBuffer(mWidth,mHeight);
draw(texture);
mFrameTemp.unBindFrameBuffer();
return mFrameTemp.getCacheTextureId();
}
@Override
public void destroy() {
mFrameTemp.destroyFrameBuffer();
GLES20.glDeleteProgram(mGLProgram);
}
protected void onTaskExec(){
synchronized (Lock) {
while (!mTasks.isEmpty()){
mTasks.removeFirst().run();
}
}
}
protected void onUseProgram(){
GLES20.glUseProgram(mGLProgram);
onTaskExec();
}
protected void onDraw(){
GLES20.glEnableVertexAttribArray(mGLVertexCo);
GLES20.glVertexAttribPointer(mGLVertexCo,2, GLES20.GL_FLOAT, false, 0,mVertexBuffer);
GLES20.glEnableVertexAttribArray(mGLTextureCo);
GLES20.glVertexAttribPointer(mGLTextureCo, 2, GLES20.GL_FLOAT, false, 0, mTextureBuffer);
GLES20.glDrawArrays(GLES20.GL_TRIANGLE_STRIP,0,4);
GLES20.glDisableVertexAttribArray(mGLVertexCo);
GLES20.glDisableVertexAttribArray(mGLTextureCo);
}
protected void onClear(){
GLES20.glClearColor(1.0f, 1.0f, 1.0f, 1.0f);
GLES20.glClear(GLES20.GL_COLOR_BUFFER_BIT | GLES20.GL_DEPTH_BUFFER_BIT);
}
public void runOnGLThread(Runnable runnable){
synchronized (Lock) {
mTasks.addLast(runnable);
}
}
/**
* 设置其他扩展数据
*/
protected void onSetExpandData(){
GLES20.glUniformMatrix4fv(mGLVertexMatrix,1,false,mVertexMatrix,0);
GLES20.glUniformMatrix4fv(mGLTextureMatrix,1,false,mTextureMatrix,0);
if(isUseSize){
GLES20.glUniform1f(mGLWidth,mWidth);
GLES20.glUniform1f(mGLHeight,mHeight);
}
}
/**
* 绑定默认纹理
*/
protected void onBindTexture(int textureId){
GLES20.glActiveTexture(GLES20.GL_TEXTURE0);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,textureId);
GLES20.glUniform1i(mGLTexture,0);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/BaseFuncFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
/**
* BaseFuncFilter 基础功能滤镜
*
* @author wuwang
* @version v1.0 2017:10:31 11:47
*/
class BaseFuncFilter extends BaseFilter {
static final String FILTER_SOBEL="shader/func/sobel.frag";
static final String FILTER_SOBEL_REVERSE="shader/func/sobel2.frag";
static final String FILTER_GAUSS="shader/func/gauss.frag";
BaseFuncFilter(Resources resource, String fragment) {
super(resource, "shader/base.vert", fragment);
shaderNeedTextureSize(true);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/BeautyFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
import android.opengl.GLES20;
/**
* BeautyFilter 美颜滤镜
*
* @author wuwang
* @version v1.0 2017:10:31 11:46
*/
public class BeautyFilter extends BaseFilter {
private int mGLaaCoef;
private int mGLmixCoef;
private int mGLiternum;
private float aaCoef;
private float mixCoef;
private int iternum;
public BeautyFilter(Resources resource) {
super(resource,"shader/beauty/beauty.vert", "shader/beauty/beauty.frag");
shaderNeedTextureSize(true);
setBeautyLevel(0);
}
@Override
protected void onCreate() {
super.onCreate();
mGLaaCoef= GLES20.glGetUniformLocation(mGLProgram,"uACoef");
mGLmixCoef=GLES20.glGetUniformLocation(mGLProgram,"uMixCoef");
mGLiternum=GLES20.glGetUniformLocation(mGLProgram,"uIternum");
}
public BaseFilter setBeautyLevel(int level){
switch (level){
case 1:
a(1,0.19f,0.54f);
break;
case 2:
a(2,0.29f,0.54f);
break;
case 3:
a(3,0.17f,0.39f);
break;
case 4:
a(3,0.25f,0.54f);
break;
case 5:
a(4,0.13f,0.54f);
break;
case 6:
a(4,0.19f,0.69f);
break;
default:
a(0,0f,0f);
break;
}
return this;
}
private void a(final int a, final float b, final float c){
runOnGLThread(new Runnable() {
@Override
public void run() {
GLES20.glUniform1f(mGLaaCoef,b);
GLES20.glUniform1f(mGLmixCoef,c);
GLES20.glUniform1i(mGLiternum,a);
}
});
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/BlackMagicFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
/**
* BlackMagicFilter 黑魔法滤镜,sobel算法实现
*
* @author wuwang
* @version v1.0 2017:10:31 11:47
*/
public class BlackMagicFilter extends GroupFilter {
public BlackMagicFilter(Resources resources){
super(resources);
}
@Override
protected void initBuffer() {
super.initBuffer();
addFilter(new GrayFilter(mRes));
addFilter(new BaseFuncFilter(mRes,BaseFuncFilter.FILTER_SOBEL));
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/CandyFilter.java
================================================
package com.wuwang.aavt.gl;
import android.content.res.Resources;
/**
* Created by 15581 on 2017/9/30.
*/
public class CandyFilter extends GroupFilter {
public CandyFilter(Resources resource) {
super(resource);
}
@Override
protected void initBuffer() {
super.initBuffer();
addFilter(new GrayFilter(mRes));
addFilter(new BaseFuncFilter(mRes,BaseFuncFilter.FILTER_GAUSS));
addFilter(new BaseFuncFilter(mRes,BaseFuncFilter.FILTER_SOBEL));
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/Faltung3x3Filter.java
================================================
package com.wuwang.aavt.gl;
import android.content.res.Resources;
import android.opengl.GLES20;
/**
* Created by wuwang on 2017/10/1.
*/
public class Faltung3x3Filter extends BaseFilter {
private float[] mFaltung;
private int mGLFaltung;
public static final float[] FILTER_SHARPEN=new float[]{0,-1,0,-1,5,-1,0,-1,0};
public static final float[] FILTER_BORDER=new float[]{0,1,0,1,-4,1,0,1,0};
public static final float[] FILTER_CAMEO=new float[]{2,0,2,0,0,0,3,0,-6};
public Faltung3x3Filter(Resources res,float[] faltung){
super(res,"shader/base.vert","shader/func/faltung3x3.frag");
shaderNeedTextureSize(true);
this.mFaltung=faltung;
}
@Override
protected void onCreate() {
super.onCreate();
mGLFaltung= GLES20.glGetUniformLocation(mGLProgram,"uFaltung");
}
@Override
protected void onSetExpandData() {
super.onSetExpandData();
GLES20.glUniformMatrix3fv(mGLFaltung,1,false,mFaltung,0);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/FluorescenceFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
import android.opengl.GLES20;
/**
* FluorescenceFilter 荧光滤镜
*
* @author wuwang
* @version v1.0 2017:10:31 11:50
*/
public class FluorescenceFilter extends BaseFilter {
private int mGLTexture2;
private int mGLBorderColor;
private int mGLStep;
private BlackMagicFilter mBlackFilter;
private int mTempTexture;
private float[] mBorderColor=new float[]{0f,1f,1f,1};
private float mStep=1.0f;
private boolean isAdd=true;
public FluorescenceFilter(Resources resource) {
super(resource, "shader/base.vert", "shader/effect/fluorescence.frag");
shaderNeedTextureSize(true);
mBlackFilter=new BlackMagicFilter(resource);
}
@Override
protected void onCreate() {
mBlackFilter.create();
super.onCreate();
mGLTexture2= GLES20.glGetUniformLocation(mGLProgram,"uTexture2");
mGLBorderColor= GLES20.glGetUniformLocation(mGLProgram,"uBorderColor");
mGLStep=GLES20.glGetUniformLocation(mGLProgram,"uStep");
}
@Override
protected void onSizeChanged(int width, int height) {
super.onSizeChanged(width, height);
mBlackFilter.sizeChanged(width, height);
}
@Override
public void draw(int texture) {
mTempTexture=mBlackFilter.drawToTexture(texture);
super.draw(texture);
}
@Override
protected void onBindTexture(int textureId) {
super.onBindTexture(textureId);
GLES20.glActiveTexture(GLES20.GL_TEXTURE1);
GLES20.glBindTexture(GLES20.GL_TEXTURE_2D,mTempTexture);
GLES20.glUniform1i(mGLTexture2,1);
}
@Override
protected void onSetExpandData() {
//todo 根据时间修改
if(isAdd){
mStep+=0.08f;
}else{
mStep-=0.08f;
}
if(mStep>=1.0f){
isAdd=false;
mStep=1.0f;
}else if(mStep<=0.0f){
isAdd=true;
mStep=0.0f;
}
super.onSetExpandData();
GLES20.glUniform4fv(mGLBorderColor,1,mBorderColor,0);
GLES20.glUniform1f(mGLStep,mStep);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/FrameBuffer.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.opengl.GLES20;
/**
* FrameBuffer 工具类
*
* @author wuwang
* @version v1.0 2017:10:31 10:31
*/
public class FrameBuffer {
private int[] mFrameTemp;
private int lastWidth=0,lastHeight=0;
/**
* 绑定到FrameBuffer,不使用RenderBuffer。{@link #bindFrameBuffer(int, int, boolean)}
* @param width 宽度
* @param height 高度
* @return 绑定结果,0表示成功,其他值为GL错误
*/
public int bindFrameBuffer(int width,int height){
return bindFrameBuffer(width, height,false);
}
/**
* 绑定到FrameBuffer
* @param width 宽度
* @param height 高度
* @param hasRenderBuffer 是否使用renderBuffer
* @return 绑定结果,0表示成功,其他值为GL错误
*/
public int bindFrameBuffer(int width,int height,boolean hasRenderBuffer){
if(lastWidth!=width||lastHeight!=height){
destroyFrameBuffer();
this.lastWidth=width;
this.lastHeight=height;
}
if(mFrameTemp==null){
return createFrameBuffer(hasRenderBuffer,width,height, GLES20.GL_TEXTURE_2D,GLES20.GL_RGBA,
GLES20.GL_LINEAR,GLES20.GL_LINEAR,GLES20.GL_CLAMP_TO_EDGE,GLES20.GL_CLAMP_TO_EDGE);
}else{
return bindFrameBuffer();
}
}
/**
* 创建FrameBuffer
* @param hasRenderBuffer 是否启用RenderBuffer
* @param width 宽度
* @param height 高度
* @param texType 类型,一般为{@link GLES20#GL_TEXTURE_2D}
* @param texFormat 纹理格式,一般为{@link GLES20#GL_RGBA}、{@link GLES20#GL_RGB}等
* @param minParams 纹理的缩小过滤参数
* @param maxParams 纹理的放大过滤参数
* @param wrapS 纹理的S环绕参数
* @param wrapT 纹理的W环绕参数
* @return 创建结果,0表示成功,其他值为GL错误
*/
public int createFrameBuffer(boolean hasRenderBuffer,int width,int height,int texType,int texFormat,
int minParams,int maxParams,int wrapS,int wrapT){
mFrameTemp=new int[4];
GLES20.glGenFramebuffers(1,mFrameTemp,0);
GLES20.glGenTextures(1,mFrameTemp,1);
GLES20.glBindTexture(texType,mFrameTemp[1]);
GLES20.glTexImage2D(texType, 0,texFormat, width, height,
0, texFormat, GLES20.GL_UNSIGNED_BYTE, null);
//设置缩小过滤为使用纹理中坐标最接近的一个像素的颜色作为需要绘制的像素颜色
GLES20.glTexParameteri(texType, GLES20.GL_TEXTURE_MIN_FILTER,minParams);
//设置放大过滤为使用纹理中坐标最接近的若干个颜色,通过加权平均算法得到需要绘制的像素颜色
GLES20.glTexParameteri(texType, GLES20.GL_TEXTURE_MAG_FILTER,maxParams);
//设置环绕方向S,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameteri(texType, GLES20.GL_TEXTURE_WRAP_S,wrapS);
//设置环绕方向T,截取纹理坐标到[1/2n,1-1/2n]。将导致永远不会与border融合
GLES20.glTexParameteri(texType, GLES20.GL_TEXTURE_WRAP_T,wrapT);
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING,mFrameTemp,3);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,mFrameTemp[0]);
GLES20.glFramebufferTexture2D(GLES20.GL_FRAMEBUFFER, GLES20.GL_COLOR_ATTACHMENT0,
texType, mFrameTemp[1], 0);
if(hasRenderBuffer){
GLES20.glGenRenderbuffers(1,mFrameTemp,2);
GLES20.glBindRenderbuffer(GLES20.GL_RENDERBUFFER,mFrameTemp[2]);
GLES20.glRenderbufferStorage(GLES20.GL_RENDERBUFFER,GLES20.GL_DEPTH_COMPONENT16,width,height);
GLES20.glFramebufferRenderbuffer(GLES20.GL_FRAMEBUFFER,GLES20.GL_DEPTH_ATTACHMENT,GLES20.GL_RENDERBUFFER,mFrameTemp[2]);
}
return GLES20.glGetError();
}
/**
* 绑定FrameBuffer,只有之前创建过FrameBuffer,才能调用此方法进行绑定
* @return 绑定结果
*/
public int bindFrameBuffer(){
if(mFrameTemp==null){
return -1;
}
GLES20.glGetIntegerv(GLES20.GL_FRAMEBUFFER_BINDING,mFrameTemp,3);
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,mFrameTemp[0]);
return GLES20.glGetError();
}
/**
* 取消FrameBuffer绑定
*/
public void unBindFrameBuffer(){
if(mFrameTemp!=null){
GLES20.glBindFramebuffer(GLES20.GL_FRAMEBUFFER,mFrameTemp[3]);
}
}
/**
* 获取绘制再FrameBuffer中的内容
* @return FrameBuffer绘制内容的纹理ID
*/
public int getCacheTextureId(){
return mFrameTemp!=null?mFrameTemp[1]:-1;
}
/**
* 销毁FrameBuffer
*/
public void destroyFrameBuffer(){
if(mFrameTemp!=null){
GLES20.glDeleteFramebuffers(1,mFrameTemp,0);
GLES20.glDeleteTextures(1,mFrameTemp,1);
if(mFrameTemp[2]>0){
GLES20.glDeleteRenderbuffers(1,mFrameTemp,2);
}
mFrameTemp=null;
}
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/GrayFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
/**
* GrayFilter 灰度滤镜
*
* @author wuwang
* @version v1.0 2017:10:31 11:52
*/
public class GrayFilter extends BaseFilter {
public GrayFilter(Resources resource) {
super(resource, "shader/base.vert", "shader/color/gray.frag");
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/gl/GroupFilter.java
================================================
/*
* 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.wuwang.aavt.gl;
import android.content.res.Resources;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;
/**
* GroupFilter 滤镜组,将多个滤镜串联起来,合并成一个滤镜
*
* @author wuwang
* @version v1.0 2017:10:31 11:53
*/
public class GroupFilter extends LazyFilter {
private ArrayList mGroup;
public GroupFilter(Resources resource) {
super(resource);
}
public GroupFilter(){
super();
}
@Override
protected void initBuffer() {
super.initBuffer();
mGroup=new ArrayList<>();
}
public void addFilter(final BaseFilter filter){
runOnGLThread(new Runnable() {
@Override
public void run() {
filter.create();
filter.sizeChanged(mWidth,mHeight);
mGroup.add(filter);
}
});
}
public void addFilter(final int index,final BaseFilter filter){
runOnGLThread(new Runnable() {
@Override
public void run() {
filter.create();
filter.sizeChanged(mWidth,mHeight);
mGroup.add(index, filter);
}
});
}
public BaseFilter removeFilter(final int index){
BaseFilter filter=mGroup.get(index);
runOnGLThread(new Runnable() {
@Override
public void run() {
BaseFilter filter=mGroup.remove(index);
if(filter!=null){
filter.destroy();
}
}
});
return filter;
}
public void removeFilter(final BaseFilter filter){
runOnGLThread(new Runnable() {
@Override
public void run() {
mGroup.remove(filter);
}
});
}
public BaseFilter get(int index){
return mGroup.get(index);
}
public Iterator iterator(){
return mGroup.iterator();
}
public boolean isEmpty(){
return mGroup.isEmpty();
}
@Override
public void draw(int texture) {
int tempTextureId=texture;
for (int i=0;i=(mXRollTime+mYRollTime)){
mFrameCount=0;
}
if(mFrameCount= Build.VERSION_CODES.LOLLIPOP) {
return codec.getInputBuffer(index);
}else{
return codec.getInputBuffers()[index];
}
}
public static ByteBuffer getOutputBuffer(MediaCodec codec, int index){
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.LOLLIPOP) {
return codec.getOutputBuffer(index);
}else{
return codec.getOutputBuffers()[index];
}
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/MediaConfig.java
================================================
/*
* 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.wuwang.aavt.media;
/**
* MediaConfig 音视频编码信息设置
*
* @author wuwang
* @version v1.0 2017:10:26 18:28
*/
public class MediaConfig {
public Video mVideo=new Video();
public Audio mAudio=new Audio();
public class Video{
public String mime="video/avc";
public int width=368;
public int height=640;
public int frameRate=24;
public int iframe=1;
public int bitrate=1177600;
public int colorFormat;
}
public class Audio{
public String mime="audio/mp4a-latm";
public int sampleRate=48000;
public int channelCount=2;
public int bitrate=128000;
public int profile;
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/RenderBean.java
================================================
/*
* 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.wuwang.aavt.media;
import com.wuwang.aavt.egl.EglHelper;
/**
* RenderBean
*
* @author wuwang
* @version v1.0 2017:10:27 15:02
*/
public class RenderBean {
public EglHelper egl;
public int sourceWidth;
public int sourceHeight;
public int textureId;
public boolean endFlag;
public long timeStamp;
public long textureTime;
public long threadId;
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/SoundRecorder.java
================================================
package com.wuwang.aavt.media;
import android.annotation.TargetApi;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.media.MediaRecorder;
import android.os.Build;
import android.os.SystemClock;
import android.util.Log;
import com.wuwang.aavt.log.AvLog;
import com.wuwang.aavt.media.hard.HardMediaData;
import com.wuwang.aavt.media.hard.IHardStore;
import java.io.IOException;
import java.nio.ByteBuffer;
import java.util.concurrent.Executors;
import java.util.concurrent.ThreadPoolExecutor;
/*
* Created by Wuwang on 2017/10/26
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class SoundRecorder {
private AudioRecord mRecord;
private int mRecordBufferSize=0;
private int mRecordSampleRate=48000; //音频采样率
private int mRecordChannelConfig= AudioFormat.CHANNEL_IN_STEREO; //音频录制通道,默认为立体声
private int mRecordAudioFormat=AudioFormat.ENCODING_PCM_16BIT; //音频录制格式,默认为PCM16Bit
private MediaCodec mAudioEncoder;
private MediaConfig mConfig=new MediaConfig();
private boolean isStarted=false;
private IHardStore mStore;
private static final int TIME_OUT=1000;
private int mAudioTrack=-1;
private long startTime=0;
private boolean stopFlag=false;
private Executors mExec;
public SoundRecorder(IHardStore store){
this.mStore=store;
}
public void configure(){
}
public void start(){
if(!isStarted){
stopFlag=false;
mRecordBufferSize = AudioRecord.getMinBufferSize(mRecordSampleRate,
mRecordChannelConfig, mRecordAudioFormat)*2;
mRecord=new AudioRecord(MediaRecorder.AudioSource.MIC,mRecordSampleRate,mRecordChannelConfig,
mRecordAudioFormat,mRecordBufferSize);
mRecord.startRecording();
try {
MediaFormat format=convertAudioConfigToFormat(mConfig.mAudio);
mAudioEncoder=MediaCodec.createEncoderByType(format.getString(MediaFormat.KEY_MIME));
mAudioEncoder.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
mAudioEncoder.start();
} catch (IOException e) {
e.printStackTrace();
}
Thread thread=new Thread(new Runnable() {
@Override
public void run() {
while (!stopFlag&&!audioEncodeStep(false)){};
audioEncodeStep(true);
Log.e("wuwang","audio stop");
if(isStarted){
mRecord.stop();
mRecord.release();
mRecord=null;
}
if(mAudioEncoder!=null){
mAudioEncoder.stop();
mAudioEncoder.release();
mAudioEncoder=null;
}
isStarted=false;
}
});
thread.start();
startTime=SystemClock.elapsedRealtimeNanos();
isStarted=true;
}
}
private synchronized boolean audioEncodeStep(boolean isEnd){
if(isStarted){
AvLog.d("audioEncodeStep");
int inputIndex=mAudioEncoder.dequeueInputBuffer(TIME_OUT);
if(inputIndex>=0){
ByteBuffer buffer= CodecUtil.getInputBuffer(mAudioEncoder,inputIndex);
buffer.clear();
long time= (SystemClock.elapsedRealtimeNanos()-startTime)/1000;
int length=mRecord.read(buffer,mRecordBufferSize);
if(length>=0){
mAudioEncoder.queueInputBuffer(inputIndex,0,length,time,
isEnd?MediaCodec.BUFFER_FLAG_END_OF_STREAM:0);
}
}
MediaCodec.BufferInfo info=new MediaCodec.BufferInfo();
while (true){
int outputIndex=mAudioEncoder.dequeueOutputBuffer(info,TIME_OUT);
if(outputIndex>=0){
if(mStore!=null){
mStore.addData(mAudioTrack,new HardMediaData(CodecUtil.getOutputBuffer(mAudioEncoder,outputIndex),info));
}
mAudioEncoder.releaseOutputBuffer(outputIndex,false);
if(info.flags==MediaCodec.BUFFER_FLAG_END_OF_STREAM){
AvLog.d("CameraRecorder get audio encode end of stream");
stop();
return true;
}
}else if(outputIndex==MediaCodec.INFO_TRY_AGAIN_LATER){
break;
}else if(outputIndex==MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
AvLog.d("get audio output format changed ->"+mAudioEncoder.getOutputFormat().toString());
mAudioTrack=mStore.addTrack(mAudioEncoder.getOutputFormat());
}
}
}
return false;
}
public void stop(){
stopFlag=true;
}
public void setConfig(MediaConfig config){
this.mConfig=config;
}
protected MediaFormat convertAudioConfigToFormat(MediaConfig.Audio config){
MediaFormat format=MediaFormat.createAudioFormat(config.mime,config.sampleRate,config.channelCount);
format.setInteger(MediaFormat.KEY_BIT_RATE,config.bitrate);
format.setInteger(MediaFormat.KEY_AAC_PROFILE, MediaCodecInfo.CodecProfileLevel.AACObjectLC);
return format;
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/SurfaceEncoder.java
================================================
/*
* 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.wuwang.aavt.media;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaCodecInfo;
import android.media.MediaFormat;
import android.opengl.EGLSurface;
import android.os.Build;
import com.wuwang.aavt.log.AvLog;
import com.wuwang.aavt.media.hard.HardMediaData;
import com.wuwang.aavt.media.hard.IHardStore;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* SurfaceEncoder 从surface上进行硬编码,通过{@link #setStore(IHardStore)}来设置存储器进行存储
*
* @author wuwang
* @version v1.0 2017:10:27 08:29
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR2)
public class SurfaceEncoder extends SurfaceShower{
private final String TAG="SurfaceEncoder";
private MediaConfig mConfig=new MediaConfig();
private MediaCodec mVideoEncoder;
private boolean isEncodeStarted=false;
private static final int TIME_OUT=1000;
private IHardStore mStore;
private int mVideoTrack=-1;
private OnDrawEndListener mListener;
private long startTime=-1;
public SurfaceEncoder(){
super.setOnDrawEndListener(new OnDrawEndListener() {
@Override
public void onDrawEnd(EGLSurface surface, RenderBean bean) {
AvLog.d(TAG,"onDrawEnd start-->");
if(bean.timeStamp!=-1){
bean.egl.setPresentationTime(surface,bean.timeStamp*1000);
}else{
if(startTime==-1){
startTime=bean.textureTime;
}
bean.egl.setPresentationTime(surface,bean.textureTime-startTime);
}
videoEncodeStep(false);
AvLog.e(TAG,"onDrawEnd end-->");
if(mListener!=null){
mListener.onDrawEnd(surface,bean);
}
}
});
}
@Override
public void onCall(RenderBean rb) {
if (rb.endFlag){
videoEncodeStep(true);
}
super.onCall(rb);
}
public void setConfig(MediaConfig config){
this.mConfig=config;
}
public void setStore(IHardStore store){
this.mStore=store;
}
@Override
public void setOutputSize(int width, int height) {
super.setOutputSize(width, height);
mConfig.mVideo.width=width;
mConfig.mVideo.height=height;
}
protected MediaFormat convertVideoConfigToFormat(MediaConfig.Video config){
MediaFormat format=MediaFormat.createVideoFormat(config.mime,config.width,config.height);
format.setInteger(MediaFormat.KEY_BIT_RATE,config.bitrate);
format.setInteger(MediaFormat.KEY_FRAME_RATE,config.frameRate);
format.setInteger(MediaFormat.KEY_I_FRAME_INTERVAL,config.iframe);
format.setInteger(MediaFormat.KEY_COLOR_FORMAT, MediaCodecInfo.CodecCapabilities.COLOR_FormatSurface);
return format;
}
private void openVideoEncoder(){
AvLog.d(TAG,"openVideoEncoder startTime-->");
if(mVideoEncoder==null){
try {
MediaFormat format=convertVideoConfigToFormat(mConfig.mVideo);
mVideoEncoder= MediaCodec.createEncoderByType(mConfig.mVideo.mime);
mVideoEncoder.configure(format,null,null,MediaCodec.CONFIGURE_FLAG_ENCODE);
super.setSurface(mVideoEncoder.createInputSurface());
super.setOutputSize(mConfig.mVideo.width,mConfig.mVideo.height);
mVideoEncoder.start();
isEncodeStarted=true;
} catch (IOException e) {
e.printStackTrace();
}
}
AvLog.d(TAG,"openVideoEncoder endTime-->");
}
private void closeVideoEncoder(){
AvLog.d(TAG,"closeEncoder");
if(mVideoEncoder!=null){
mVideoEncoder.stop();
mVideoEncoder.release();
mVideoEncoder=null;
}
}
private synchronized boolean videoEncodeStep(boolean isEnd){
AvLog.d(TAG,"videoEncodeStep:"+isEncodeStarted+"/"+isEnd);
if(isEncodeStarted){
if(isEnd){
mVideoEncoder.signalEndOfInputStream();
}
MediaCodec.BufferInfo info=new MediaCodec.BufferInfo();
while (true){
int mOutputIndex=mVideoEncoder.dequeueOutputBuffer(info,TIME_OUT);
AvLog.i(TAG,"videoEncodeStep:mOutputIndex="+mOutputIndex);
if(mOutputIndex>=0){
if((info.flags&MediaCodec.BUFFER_FLAG_CODEC_CONFIG)!=0){
info.size=0;
}
ByteBuffer buffer= CodecUtil.getOutputBuffer(mVideoEncoder,mOutputIndex);
if(mStore!=null){
mStore.addData(mVideoTrack,new HardMediaData(buffer,info));
}
mVideoEncoder.releaseOutputBuffer(mOutputIndex,false);
if((info.flags&MediaCodec.BUFFER_FLAG_END_OF_STREAM)!=0){
closeVideoEncoder();
isEncodeStarted=false;
AvLog.i(TAG,"videoEncodeStep: MediaCodec.BUFFER_FLAG_END_OF_STREAM ");
break;
}
}else if(mOutputIndex== MediaCodec.INFO_OUTPUT_FORMAT_CHANGED){
MediaFormat format=mVideoEncoder.getOutputFormat();
if(mStore!=null){
mVideoTrack=mStore.addTrack(format);
}
}else if(mOutputIndex== MediaCodec.INFO_TRY_AGAIN_LATER&&!isEnd){
break;
}
}
}
return false;
}
@Override
public void open() {
openVideoEncoder();
super.open();
}
@Override
public void close() {
super.close();
videoEncodeStep(true);
startTime=-1;
}
@Override
public void setOnDrawEndListener(OnDrawEndListener listener) {
this.mListener=listener;
}
@Override
public void setSurface(Object surface) {}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/SurfaceShower.java
================================================
/*
* 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.wuwang.aavt.media;
import android.opengl.EGLSurface;
import android.opengl.GLES20;
import com.wuwang.aavt.core.IObserver;
import com.wuwang.aavt.gl.BaseFilter;
import com.wuwang.aavt.gl.LazyFilter;
import com.wuwang.aavt.utils.MatrixUtils;
/**
* SurfaceShower 用于将RenderBean展示到指定的Surface上
*
* @author wuwang
* @version v1.0 2017:10:27 08:53
*/
public class SurfaceShower implements IObserver {
private EGLSurface mShowSurface;
private boolean isShow=false;
private BaseFilter mFilter;
private Object mSurface;
private int mWidth;
private int mHeight;
private int mMatrixType= MatrixUtils.TYPE_CENTERCROP;
private OnDrawEndListener mListener;
private RenderBean mBean;
public void setOutputSize(int width,int height){
this.mWidth=width;
this.mHeight=height;
}
private void clearSurface() {
if(mShowSurface!=null){
mBean.egl.destroySurface(mShowSurface);
mShowSurface = null;
}
}
/**
* 设置输出的Surface
* @param surface {@link android.view.Surface}、{@link android.graphics.SurfaceTexture}或{@link android.view.TextureView}
*/
public void setSurface(Object surface){
this.mSurface=surface;
clearSurface();
}
/**
* 设置矩阵变换类型
* @param type 变换类型,{@link MatrixUtils#TYPE_FITXY},{@link MatrixUtils#TYPE_FITSTART},{@link MatrixUtils#TYPE_CENTERCROP},{@link MatrixUtils#TYPE_CENTERINSIDE}或{@link MatrixUtils#TYPE_FITEND}
*/
public void setMatrixType(int type){
this.mMatrixType=type;
}
public void open(){
isShow=true;
}
public void close(){
isShow=false;
}
@Override
public void onCall(RenderBean rb) {
if(rb.endFlag){
clearSurface();
}else if(isShow&&mSurface!=null){
if(mShowSurface==null){
mBean = rb;
mShowSurface=rb.egl.createWindowSurface(mSurface);
mFilter=new LazyFilter();
mFilter.create();
mFilter.sizeChanged(rb.sourceWidth, rb.sourceHeight);
MatrixUtils.getMatrix(mFilter.getVertexMatrix(),mMatrixType,rb.sourceWidth,rb.sourceHeight,
mWidth,mHeight);
MatrixUtils.flip(mFilter.getVertexMatrix(),false,true);
}
rb.egl.makeCurrent(mShowSurface);
GLES20.glViewport(0,0,mWidth,mHeight);
mFilter.draw(rb.textureId);
if(mListener!=null){
mListener.onDrawEnd(mShowSurface,rb);
}
rb.egl.swapBuffers(mShowSurface);
}
}
/**
* 设置单帧渲染完成监听器
* @param listener 监听器
*/
public void setOnDrawEndListener(OnDrawEndListener listener){
this.mListener=listener;
}
public interface OnDrawEndListener{
/**
* 渲染完成通知
* @param surface 渲染的目标EGLSurface
* @param bean 渲染用的资源
*/
void onDrawEnd(EGLSurface surface,RenderBean bean);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/VideoSurfaceProcessor.java
================================================
/*
* 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.wuwang.aavt.media;
import android.annotation.TargetApi;
import android.graphics.Point;
import android.graphics.SurfaceTexture;
import android.opengl.EGL14;
import android.opengl.GLES20;
import android.os.Build;
import com.wuwang.aavt.core.IObserver;
import com.wuwang.aavt.core.Observable;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.egl.EGLConfigAttrs;
import com.wuwang.aavt.egl.EGLContextAttrs;
import com.wuwang.aavt.egl.EglHelper;
import com.wuwang.aavt.gl.FrameBuffer;
import com.wuwang.aavt.log.AvLog;
import com.wuwang.aavt.media.video.ITextureProvider;
import com.wuwang.aavt.utils.GpuUtils;
/**
* VideoSurfaceProcessor 视频流图像处理类,以{@link ITextureProvider}作为视频流图像输入。通过设置{@link IObserver}
* 来接收处理完毕的{@link RenderBean},并做相应处理,诸如展示、编码等。
*
* @author wuwang
* @version v1.0 2017:10:27 08:37
*/
@TargetApi(Build.VERSION_CODES.JELLY_BEAN_MR1)
public class VideoSurfaceProcessor{
private String TAG=getClass().getSimpleName();
private boolean mGLThreadFlag=false;
private Thread mGLThread;
private Renderer renderer;
private boolean isRendererChanged = false;
private Observable observable;
private final Object LOCK=new Object();
private ITextureProvider mProvider;
public VideoSurfaceProcessor(){
observable=new Observable<>();
}
public void setTextureProvider(ITextureProvider provider){
this.mProvider=provider;
}
public void start(){
synchronized (LOCK){
if(!mGLThreadFlag){
if(mProvider==null){
return;
}
mGLThreadFlag=true;
mGLThread=new Thread(new Runnable() {
@Override
public void run() {
glRun();
}
});
mGLThread.start();
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
public void stop(){
synchronized (LOCK){
if(mGLThreadFlag){
mGLThreadFlag=false;
mProvider.close();
try {
LOCK.wait();
} catch (InterruptedException e) {
e.printStackTrace();
}
}
}
}
private WrapRenderer checkWrapRenderer(WrapRenderer renderer,int width,int height){
if(isRendererChanged){
isRendererChanged = false;
renderer.destroy();
renderer = new WrapRenderer(this.renderer);
renderer.create();
renderer.sizeChanged(width, height);
renderer.setFlag(mProvider.isLandscape()?WrapRenderer.TYPE_CAMERA:WrapRenderer.TYPE_MOVE);
}
return renderer;
}
public void setRenderer(Renderer renderer){
this.renderer = renderer;
this.isRendererChanged = true;
}
private void glRun(){
EglHelper egl=new EglHelper();
boolean ret=egl.createGLESWithSurface(new EGLConfigAttrs(),new EGLContextAttrs(),new SurfaceTexture(1));
if(!ret){
//todo 错误处理
return;
}
int mInputSurfaceTextureId = GpuUtils.createTextureID(true);
SurfaceTexture mInputSurfaceTexture = new SurfaceTexture(mInputSurfaceTextureId);
Point size=mProvider.open(mInputSurfaceTexture);
AvLog.d(TAG,"Provider Opened . data size (x,y)="+size.x+"/"+size.y);
if(size.x<=0||size.y<=0){
//todo 错误处理
destroyGL(egl);
synchronized (LOCK){
LOCK.notifyAll();
}
return;
}
int mSourceWidth = size.x;
int mSourceHeight = size.y;
synchronized (LOCK){
LOCK.notifyAll();
}
//要求数据源提供者必须同步返回数据大小
if(mSourceWidth <=0|| mSourceHeight <=0){
error(1,"video source return inaccurate size to SurfaceTextureActuator");
return;
}
WrapRenderer wrapRenderer=new WrapRenderer(null);
wrapRenderer.create();
wrapRenderer.sizeChanged(mSourceWidth,mSourceHeight);
wrapRenderer.setFlag(mProvider.isLandscape()?WrapRenderer.TYPE_CAMERA:WrapRenderer.TYPE_MOVE);
FrameBuffer sourceFrame=new FrameBuffer();
//用于其他的回调
RenderBean rb=new RenderBean();
rb.egl=egl;
rb.sourceWidth= mSourceWidth;
rb.sourceHeight= mSourceHeight;
rb.endFlag=false;
rb.threadId=Thread.currentThread().getId();
AvLog.d(TAG,"Processor While Loop Entry");
//要求数据源必须同步填充SurfaceTexture,填充完成前等待
while (!mProvider.frame()&&mGLThreadFlag){
wrapRenderer = checkWrapRenderer(wrapRenderer,mSourceWidth,mSourceHeight);
mInputSurfaceTexture.updateTexImage();
mInputSurfaceTexture.getTransformMatrix(wrapRenderer.getTextureMatrix());
AvLog.d(TAG,"timestamp:"+ mInputSurfaceTexture.getTimestamp());
sourceFrame.bindFrameBuffer(mSourceWidth, mSourceHeight);
GLES20.glViewport(0,0, mSourceWidth, mSourceHeight);
wrapRenderer.draw(mInputSurfaceTextureId);
sourceFrame.unBindFrameBuffer();
rb.textureId=sourceFrame.getCacheTextureId();
//接收数据源传入的时间戳
rb.timeStamp=mProvider.getTimeStamp();
rb.textureTime= mInputSurfaceTexture.getTimestamp();
observable.notify(rb);
}
AvLog.d(TAG,"out of gl thread loop");
synchronized (LOCK){
rb.endFlag=true;
observable.notify(rb);
wrapRenderer.destroy();
destroyGL(egl);
LOCK.notifyAll();
AvLog.d(TAG,"gl thread exit");
}
}
private void destroyGL(EglHelper egl){
mGLThreadFlag=false;
EGL14.eglMakeCurrent(egl.getDisplay(), EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_SURFACE, EGL14.EGL_NO_CONTEXT);
EGL14.eglDestroyContext(egl.getDisplay(),egl.getDefaultContext());
EGL14.eglTerminate(egl.getDisplay());
}
public void addObserver(IObserver observer) {
observable.addObserver(observer);
}
protected void error(int id,String msg) {
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/WrapRenderer.java
================================================
/*
* 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.wuwang.aavt.media;
import com.wuwang.aavt.core.Renderer;
import com.wuwang.aavt.gl.OesFilter;
import com.wuwang.aavt.utils.MatrixUtils;
/**
* WrapRenderer 用于包装其他Filter渲染OES纹理
*
* @author wuwang
* @version v1.0 2017:10:27 08:53
*/
public class WrapRenderer implements Renderer {
private Renderer mRenderer;
private OesFilter mFilter;
public static final int TYPE_MOVE=0;
public static final int TYPE_CAMERA=1;
public WrapRenderer(Renderer renderer){
this.mRenderer=renderer;
mFilter=new OesFilter();
setFlag(TYPE_MOVE);
}
public void setFlag(int flag){
if(flag==TYPE_MOVE){
mFilter.setVertexCo(new float[]{
-1.0f,1.0f,
-1.0f,-1.0f,
1.0f,1.0f,
1.0f,-1.0f,
});
}else if(flag==TYPE_CAMERA){
mFilter.setVertexCo(new float[]{
-1.0f, -1.0f,
1.0f, -1.0f,
-1.0f, 1.0f,
1.0f, 1.0f,
});
}
}
public float[] getTextureMatrix(){
return mFilter.getTextureMatrix();
}
@Override
public void create() {
mFilter.create();
if(mRenderer!=null){
mRenderer.create();
}
}
@Override
public void sizeChanged(int width, int height) {
mFilter.sizeChanged(width, height);
if(mRenderer!=null){
mRenderer.sizeChanged(width, height);
}
}
@Override
public void draw(int texture) {
if(mRenderer!=null){
mRenderer.draw(mFilter.drawToTexture(texture));
}else{
mFilter.draw(texture);
}
}
@Override
public void destroy() {
if(mRenderer!=null){
mRenderer.destroy();
}
mFilter.destroy();
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/audio/FileAudioProvider.java
================================================
package com.wuwang.aavt.media.audio;
import android.annotation.TargetApi;
import android.media.MediaCodec;
import android.media.MediaExtractor;
import android.media.MediaFormat;
import android.os.Build;
import android.support.annotation.NonNull;
import android.support.annotation.RequiresApi;
import com.wuwang.aavt.media.CodecUtil;
import java.io.IOException;
import java.nio.ByteBuffer;
/**
* @author wuwang
* @version 1.00 , 2019/03/13
*/
@RequiresApi(api = Build.VERSION_CODES.LOLLIPOP)
public class FileAudioProvider extends MediaCodec.Callback implements ISoundProvider {
private MediaExtractor extractor;
private MediaCodec codec;
private int audioTrack = -1;
private MediaFormat format;
private final long TIME_US = 1000;
private FileAudioProvider(){}
@Override
public void onInputBufferAvailable(@NonNull MediaCodec codec, int index) {
ByteBuffer buffer = CodecUtil.getInputBuffer(codec,index);
buffer.clear();
int size = extractor.readSampleData(buffer,0);
codec.queueInputBuffer(index,0,size ,extractor.getSampleTime(),extractor.getSampleFlags());
}
@Override
public void onOutputBufferAvailable(@NonNull MediaCodec codec, int index, @NonNull MediaCodec.BufferInfo info) {
ByteBuffer buffer = CodecUtil.getOutputBuffer(codec,index);
codec.releaseOutputBuffer(index,false);
}
@Override
public void onError(@NonNull MediaCodec codec, @NonNull MediaCodec.CodecException e) {
}
@Override
public void onOutputFormatChanged(@NonNull MediaCodec codec, @NonNull MediaFormat format) {
}
private boolean setPath(String path){
try {
extractor = new MediaExtractor();
extractor.setDataSource(path);
int count = extractor.getTrackCount();
if(count <= 0){
return false;
}
for (int i=0;i=0){
ByteBuffer buffer = CodecUtil.getInputBuffer(codec,inputId);
buffer.clear();
}
}
@Override
public void close() {
codec.stop();
codec.release();
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/audio/ISoundProvider.java
================================================
package com.wuwang.aavt.media.audio;
import com.wuwang.aavt.media.av.ICloseable;
/**
* @author wuwang
* @version 1.00 , 2019/03/13
*/
public interface ISoundProvider extends ICloseable {
void open();
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/audio/MicAudioProvider.java
================================================
package com.wuwang.aavt.media.audio;
import android.media.AudioFormat;
import android.media.AudioRecord;
import android.media.MediaRecorder;
import com.wuwang.aavt.media.audio.ISoundProvider;
import com.wuwang.aavt.media.av.AvException;
/**
* @author wuwang
* @version 1.00 , 2019/03/13
*/
public class MicAudioProvider implements ISoundProvider {
private AudioRecord record;
private int recordBufferSize=0;
private int sampleRateInHz=48000; //音频采样率
private int channelConfig= AudioFormat.CHANNEL_IN_STEREO; //音频录制通道,默认为立体声
private int audioFormat=AudioFormat.ENCODING_PCM_16BIT; //音频录制格式,默认为PCM16Bit
public MicAudioProvider(){
setProperty(sampleRateInHz,channelConfig,audioFormat);
}
public MicAudioProvider(int sampleRateInHz, int channelConfig, int audioFormat){
setProperty(sampleRateInHz, channelConfig, audioFormat);
}
public void setProperty(int sampleRateInHz, int channelConfig, int audioFormat){
this.sampleRateInHz = sampleRateInHz;
this.channelConfig = channelConfig;
this.audioFormat = audioFormat;
this.recordBufferSize = AudioRecord.getMinBufferSize(sampleRateInHz, channelConfig, audioFormat) * 2;
}
@Override
public void open() {
close();
record = new AudioRecord(MediaRecorder.AudioSource.MIC,sampleRateInHz,channelConfig,audioFormat,recordBufferSize);
record.startRecording();
}
@Override
public void close() {
if(record != null){
record.stop();
record.release();
record = null;
}
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/av/AvException.java
================================================
/*
* 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.wuwang.aavt.media.av;
/**
* AVException
*
* @author wuwang
* @version v1.0 2017:10:28 17:27
*/
public class AvException extends Exception {
public AvException(){
super();
}
public AvException(String msg){
super("AVException:"+msg);
}
public AvException(String message, Throwable cause) {
super("AVException:"+message, cause);
}
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/av/ICloseable.java
================================================
/*
* 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.wuwang.aavt.media.av;
/**
* ICloseable
*
* @author wuwang
* @version v1.0 2017:10:28 17:25
*/
public interface ICloseable {
void close();
}
================================================
FILE: aavt/src/main/java/com/wuwang/aavt/media/av/IStore.java
================================================
/*
* 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.wuwang.aavt.media.av;
/**
* IStore 文件存储接口
*
* @author wuwang
* @version v1.0 2017:10:28 16:39
*/
public interface IStore