Android FFmpeg

#include

#include

#include

#include

#include

#include

#include

#include

#include

#include"FFDemux.h"

#include"ChaoLog.h"

#include"ChaoDecode.h"

#include"FFDecode.h"

#include"ChaoVideoView.h"

#include"ChaoEGL.h"

#include"ChaoShader.h"

#include"GLVideoView.h"

#include"ChaoResample.h"

#include"FFResample.h"

#include"ChaoAudioPlay.h"

#include"SLAudioPlay.h"

#defineLOGW(...) __android_log_print(ANDROID_LOG_WARN,"ChaoPlayer", __VA_ARGS__)

#defineLOGE(...) __android_log_print(ANDROID_LOG_ERROR,"ChaoPlayer", __VA_ARGS__)

#defineLOGI(...) __android_log_print(ANDROID_LOG_INFO,"ChaoPlayer", __VA_ARGS__)

#defineLOGD(...) __android_log_print(ANDROID_LOG_DEBUG,"ChaoPlayer", __VA_ARGS__)

extern"C"{

#include

#include

#include

#include

#include

}

#include

usingnamespacestd;

//把分数转换成浮点型

staticdoubler2d(AVRational r) {

returnr.num==0|| r.den==0?0: (double)r.num/ (double)r.den;

}

//当前时间戳 clock

longlongGetNowMs() {

structtimevaltv;

gettimeofday(&tv,NULL);

intsec = tv.tv_sec%360000;

longlongt = sec *1000+ tv.tv_usec/1000;

returnt;

}

/*********************音频播放OpenSLES ****************************/

//1.创建引擎

staticSLObjectItf engineSL =NULL;

SLEngineItfCreateSL() {

    SLresult re;

    SLEngineItf en;

re =slCreateEngine(&engineSL,0,0,0,0,0);//创建引擎对象

if(re != SL_RESULT_SUCCESS)//失败

returnNULL;

re = (*engineSL)->Realize(engineSL, SL_BOOLEAN_FALSE);//成功  等待对象创建

if(re != SL_RESULT_SUCCESS)

returnNULL;

re = (*engineSL)->GetInterface(engineSL, SL_IID_ENGINE, &en);//获取接口

if(re != SL_RESULT_SUCCESS)

returnNULL;

returnen;

}

//播放Pcm音频文件

voidPcmCall(SLAndroidSimpleBufferQueueItf bf,void*context) {

LOGI("PcmCall");

staticFILE*fp =NULL;

staticchar*buf =NULL;

if(!buf) {

buf =newchar[1024*1024];

    }

if(!fp) {

fp =fopen("/sdcard/Music/test.pcm","rb");

    }

if(!fp)

return;

if(feof(fp) ==0) {//打开成功

intlen =fread(buf,1,1024, fp);

if(len >0)

(*bf)->Enqueue(bf, buf, len);//发送音频

    }

}

//顶点着色器glsl

#defineGET_STR(x) #x

staticconstchar*vertexShader = GET_STR(

attribute vec4 aPosition;//顶点坐标

attribute vec2 aTexCoord;//材质顶点坐标

varying vec2 vTexCoord;//输出的材质坐标

voidmain(){

vTexCoord =vec2(aTexCoord.x,1.0- aTexCoord.y);

gl_Position = aPosition;//显示顶点

        }

);

//片元着色器,软解码和部分x86硬解码

staticconstchar*fragYUV420P = GET_STR(

precision mediumpfloat;//精度

varying vec2 vTexCoord;//顶点着色器传递的坐标

uniform sampler2D yTexture;//输入的材质(不透明灰度,单像素)

        uniform sampler2D uTexture;

        uniform sampler2D vTexture;

voidmain(){

            vec3 yuv;

            vec3 rgb;

yuv.r=texture2D(yTexture,vTexCoord).r;

yuv.g=texture2D(uTexture,vTexCoord).r-0.5;

yuv.b=texture2D(vTexture,vTexCoord).r-0.5;

rgb =mat3(1.0,1.0,1.0,

0.0,-0.39465,2.03211,

1.13983,-0.58060,0.0)*yuv;

//输出像素颜色

gl_FragColor =vec4(rgb,1.0);

        }

);

GLintInitShader(constchar*code, GLint type) {

//创建shader

GLint sh =glCreateShader(type);

if(sh ==0) {

LOGD("glCreateShader %d failed!", type);

return0;

    }

//加载shader

glShaderSource(sh,

1,//shader数量

&code,//shader代码

0);//代码长度

//编译shader

glCompileShader(sh);

//获取编译情况

    GLint status;

glGetShaderiv(sh, GL_COMPILE_STATUS, &status);

if(status ==0) {

LOGD("glCompileShader failed!");

return0;

    }

LOGD("glCompileShader success!");

returnsh;

}

classTestObs:publicChaoObserver{

public:

voidUpdate(ChaoData d) {

//CHAOLOGI("TestObs Update data size is %d", d.size);

    }

};

ChaoVideoView *view =NULL;

extern"C"

JNIEXPORT

jintJNI_OnLoad(JavaVM *vm,void*res) {

FFDecode::InitHard(vm);

TestObs *tobs =newTestObs();

ChaoDemux *de =newFFDemux();

de->Open("/sdcard/Movies/1080.mp4");

ChaoDecode *vdecode =newFFDecode();

vdecode->Open(de->GetVPara(),true);

ChaoDecode *adecode =newFFDecode();

adecode->Open(de->GetAPara());

de->AddObs(vdecode);

de->AddObs(adecode);

view =newGLVideoView();

vdecode->AddObs(view);

ChaoResample *resample =newFFResample();

ChaoParameter outPara = de->GetAPara();

resample->Open(de->GetAPara(), outPara);

adecode->AddObs(resample);

ChaoAudioPlay *audioPlay =newSLAudioPlay();

audioPlay->StartPlay(outPara);

resample->AddObs(audioPlay);

de->Start();

vdecode->Start();

adecode->Start();

returnJNI_VERSION_1_4;

}

extern"C"

JNIEXPORT jstring JNICALL

Java_com_lichao_chaoplayer_MainActivity_stringFromJNI(JNIEnv *env, jobject/*this*/) {

std::string hello ="Hello from C++";

//1 创建引擎

SLEngineItf eng =CreateSL();

if(eng){

LOGD("CreateSL success!");

}else{

LOGD("CreateSL failed!");

    }

//2 创建混音器

SLObjectItf mix =NULL;

SLresult re =0;

re = (*eng)->CreateOutputMix(eng, &mix,0,0,0);//1 引擎  2 输出的混音器 345配置项音效

if(re != SL_RESULT_SUCCESS) {

LOGD("SL_RESULT_SUCCESS failed!");

    }

re = (*mix)->Realize(mix, SL_BOOLEAN_FALSE);//实例化对象  1 对象  2 等待创建完毕

if(re != SL_RESULT_SUCCESS) {

LOGD("(*mix)->Realize failed!");

    }

SLDataLocator_OutputMix outMix = {SL_DATALOCATOR_OUTPUTMIX, mix};//用来存放声音给音频

SLDataSink audioSink= {&outMix,0};

//3 配置音频信息

//缓冲队列

SLDataLocator_AndroidSimpleBufferQueue que = {SL_DATALOCATOR_ANDROIDSIMPLEBUFFERQUEUE,10};

//音频格式

    SLDataFormat_PCM pcm = {

            SL_DATAFORMAT_PCM,

2,//声道数

            SL_SAMPLINGRATE_44_1,

            SL_PCMSAMPLEFORMAT_FIXED_16,

            SL_PCMSAMPLEFORMAT_FIXED_16,

            SL_SPEAKER_FRONT_LEFT|SL_SPEAKER_FRONT_RIGHT,

SL_BYTEORDER_LITTLEENDIAN//字节序,小端

    };

    SLDataSource ds = {&que,&pcm};

//4 创建播放器

SLObjectItf player =NULL;

SLPlayItf iplayer =NULL;

SLAndroidSimpleBufferQueueItf pcmQue =NULL;//存放地址

constSLInterfaceID ids[] = {SL_IID_BUFFERQUEUE};

constSLboolean req[] = {SL_BOOLEAN_TRUE};

//创建播放器 1 引擎  2 播放器对象  3 数据源信息  4 混音器  5  6  7

re = (*eng)->CreateAudioPlayer(eng, &player, &ds, &audioSink,sizeof(ids) /sizeof(SLInterfaceID), ids, req);

if(re != SL_RESULT_SUCCESS) {

LOGD("CreateAudioPlayer failed!");

}else{

LOGD("CreateAudioPlayer success!");

    }

(*player)->Realize(player, SL_BOOLEAN_FALSE);//实例化

//获取player接口

re = (*player)->GetInterface(player, SL_IID_PLAY,&iplayer);

if(re != SL_RESULT_SUCCESS) {

LOGD("GetInterface SL_IID_PLAY failed!");

    }

re = (*player)->GetInterface(player, SL_IID_BUFFERQUEUE, &pcmQue);//获取播放用的接口

if(re != SL_RESULT_SUCCESS) {

LOGD("GetInterface SL_IID_BUFFERQUEUE failed!");

    }

//设置回调函数,播放队列空调用

(*pcmQue)->RegisterCallback(pcmQue, PcmCall,0);

//设置为播放状态

(*iplayer)->SetPlayState(iplayer, SL_PLAYSTATE_PLAYING);

//启动队列回调

(*pcmQue)->Enqueue(pcmQue,"",1);

returnenv->NewStringUTF(hello.c_str());

}

extern"C"

JNIEXPORTvoidJNICALL

Java_com_lichao_chaoplayer_ChaoPlay_Open(JNIEnv *env, jobject instance, jstring url_, jobject surface) {

constchar*path = env->GetStringUTFChars(url_,0);

//初始化解封装

av_register_all();

//初始化网络

avformat_network_init();

avcodec_register_all();

//打开文件

AVFormatContext *ic =NULL;

intre =avformat_open_input(&ic, path,0,0);

if(re !=0) {

//打开失败输出错误原因

LOGE("avformat_open_input failed!:%s",av_err2str(re));

return;

    }

LOGI("avformat_open_input %s success!", path);

//获取视频流信息 加这句能保证flv格式视频(没格式信息的视频)

re =avformat_find_stream_info(ic,0);

if(re !=0) {

LOGW("avformat_find_stream_info failed");

    }

//duration总时长微秒  nb_streams流的数量

LOGW("duration = %lld nb_streams = %d", ic->duration, ic->nb_streams);

//遍历流信息

intfps =0;//帧率

intvideoStream =0;//视频流

intaudioStream =1;//音频流

for(inti =0; i < ic->nb_streams; i++) {

AVStream *as = ic->streams[i];

if(as->codecpar->codec_type== AVMEDIA_TYPE_VIDEO) {

LOGW("视频数据");

            videoStream = i;

fps =r2d(as->avg_frame_rate);

LOGW("fps = %d, width=%d height=%d codecId=%d pixFormat=%d", fps,

as->codecpar->width,//宽度

as->codecpar->height,//高度

as->codecpar->codec_id,//编码器

as->codecpar->format//编码器格式

            );

}elseif(as->codecpar->codec_type== AVMEDIA_TYPE_AUDIO) {

LOGW("音频数据");

            audioStream = i;

LOGW("sample_rate=%d channels=%d sample_format=%d",

as->codecpar->sample_rate,//音频采样率

as->codecpar->channels,//通道数

as->codecpar->format//格式

            );

        }

    }

//获取音频流数据

audioStream =av_find_best_stream(ic, AVMEDIA_TYPE_AUDIO, -1, -1,NULL,0);

LOGW("av_find_best_stream audioStream = %d", audioStream);

/*********************************打开视频解码器****************************************/

//解码器--软解码

AVCodec *codec =avcodec_find_decoder(ic->streams[videoStream]->codecpar->codec_id);

//硬解码

codec =avcodec_find_decoder_by_name("h264_mediacodec");

if(!codec) {

LOGW("AVCodec find decoder failed");

return;

    }

//视频解码器初始化

AVCodecContext *vc =avcodec_alloc_context3(codec);

//复制视频

avcodec_parameters_to_context(vc, ic->streams[videoStream]->codecpar);

//线程数量

vc->thread_count=8;

//打开解码器

re =avcodec_open2(vc,0,0);

if(re !=0) {

LOGW("avcodec_open2 video failed");

return;

    }

/*********************************打开音频解码器****************************************/

//软解码器

AVCodec *acodec =avcodec_find_decoder(ic->streams[audioStream]->codecpar->codec_id);

if(!acodec) {

LOGW("AVCodec find decoder failed");

return;

    }

//视频解码器初始化

AVCodecContext *ac =avcodec_alloc_context3(acodec);

//复制视频

avcodec_parameters_to_context(ac, ic->streams[audioStream]->codecpar);

//线程数量

ac->thread_count=8;

//打开解码器

re =avcodec_open2(ac,0,0);

if(re !=0) {

LOGW("avcodec_open2 audio failed");

return;

    }

//读取帧数据

AVPacket *pkt =av_packet_alloc();

AVFrame *frame =av_frame_alloc();

longlongstart =GetNowMs();

intframeCount =0;

//初始化像素格式转换的上下文

SwsContext *vctx =NULL;

intoutWidth =1280;

intoutHeight =720;

char*rgb =newchar[1920*1080*4];

char*pcm =newchar[48000*4*2];

//音频重采样上下文初始化

SwrContext *actx =swr_alloc();

actx =swr_alloc_set_opts(actx,

av_get_default_channel_layout(2),//音频输出通道数

AV_SAMPLE_FMT_S16,//输出格式

ac->sample_rate,//输出样本率

av_get_default_channel_layout(ac->channels),//输入格式

ac->sample_fmt,//输入格式

ac->sample_rate,//输入样本采样率

0,0);

re =swr_init(actx);

if(re !=0) {

LOGW("swr_init failed!");

}else{

LOGW("swr_init success!");

    }

//显示窗口初始化

ANativeWindow *nwin =ANativeWindow_fromSurface(env, surface);

ANativeWindow_setBuffersGeometry(nwin,outWidth,outHeight, WINDOW_FORMAT_RGBA_8888);

    ANativeWindow_Buffer wbuf;

for(;;) {

//超过三秒

if(GetNowMs() - start >=3000) {

LOGW("now decode fps is %d", frameCount /3);

start =GetNowMs();

frameCount =0;

        }

intre =av_read_frame(ic, pkt);

if(re !=0) {

LOGW("读取到结尾处!");

//20秒

intpos =20*r2d(ic->streams[videoStream]->time_base);

//跳20秒 往后找和找关键帧

av_seek_frame(ic, videoStream, pos, AVSEEK_FLAG_BACKWARD|AVSEEK_FLAG_FRAME );

continue;

        }

        AVCodecContext *cc = vc;

if(pkt->stream_index== audioStream)

            cc = ac;

//LOGW("stream = %d size =%d pts=%lld flag=%d", pkt->stream_index, pkt->size, pkt->pts, pkt->flags);

//发送到线程中解码

re =avcodec_send_packet(cc, pkt);

//清理

intp = pkt->pts;

av_packet_unref(pkt);

if(re !=0) {

//失败

LOGW("avcodec_send_packet failed!");

continue;

        }

for(;;) {

re =avcodec_receive_frame(cc, frame);

if(re !=0) {

break;

            }

//LOGW("avcodec_receive_frame %lld", frame->pts);

//如果是视频帧

if(cc == vc) {

                frameCount++;

//像素格式初始化

vctx =sws_getCachedContext(vctx,

frame->width,

frame->height,

(AVPixelFormat)frame->format,

                                            outWidth,

                                            outHeight,

                                            AV_PIX_FMT_RGBA,

                                            SWS_FAST_BILINEAR,

0,0,0);

if(!vctx) {

LOGW("sws_getCachedContext failed!");

}else{

//显示大小转换

uint8_t*data[AV_NUM_DATA_POINTERS] = {0};

data[0] =(uint8_t*)rgb;

intlines[AV_NUM_DATA_POINTERS] = {0};//一行宽度的大小

lines[0] = outWidth *4;

inth =sws_scale(vctx,

(constuint8_t**)frame->data,

frame->linesize,0,

frame->height,

                                      data,lines);

LOGW("sws_scale = %d", h);

if(h >0) {

ANativeWindow_lock(nwin,&wbuf,0);

uint8_t*dst = (uint8_t*)wbuf.bits;

memcpy(dst,rgb,outWidth*outHeight *4);

ANativeWindow_unlockAndPost(nwin);

                    }

                }

}else{

//音频

uint8_t*out[2] = {0};

out[0] = (uint8_t*) pcm;

//音频重采样

intlen =swr_convert(actx,out,

frame->nb_samples,

(constuint8_t**)frame->data,

frame->nb_samples);

LOGW("swr_convert = %d", len);

            }

        }

    }

deletergb;

deletepcm;

//关闭上下文

avformat_close_input(&ic);

env->ReleaseStringUTFChars(url_, path);

}

extern"C"

JNIEXPORTvoidJNICALL

Java_com_lichao_chaoplayer_ChaoPlay_Yuv(JNIEnv *env, jobject instance, jstring url_, jobject surface) {

constchar*url = env->GetStringUTFChars(url_,0);

LOGD("open url is %s", url);

FILE*fp =fopen(url,"rb");

if(!fp) {

LOGD("open file %s failed!", url);

return;

    }

//获取原始窗口

ANativeWindow *nwin =ANativeWindow_fromSurface(env,surface);

//1 EGL display创建和初始化

EGLDisplay display =eglGetDisplay(EGL_DEFAULT_DISPLAY);//创建

if(display == EGL_NO_DISPLAY) {

LOGD("eglGetDisplay failed!");

return;

    }

if(EGL_TRUE !=eglInitialize(display,0,0)) {//初始化

LOGD("eglInitialize failed!");

return;

    }

//2 surface

//输出配置

    EGLConfig config;

    EGLint configNum;

//输入配置

    EGLint configSpec[] = {

EGL_RED_SIZE,8,

EGL_GREEN_SIZE,8,

EGL_BLUE_SIZE,8,

            EGL_SURFACE_TYPE, EGL_WINDOW_BIT, EGL_NONE

    };

//surface窗口配置

if(EGL_TRUE !=eglChooseConfig(display, configSpec, &config,1, &configNum)) {

LOGD("eglChooseConfig failed!");

return;

    }

//创建surface

EGLSurface winSurface =eglCreateWindowSurface(display, config, nwin,0);

if(winSurface == EGL_NO_SURFACE) {

LOGD("eglCreateWindowSurface failed!");

return;

    }

//3 context 创建关联的上下文

constEGLint ctxAttr[] = {

EGL_CONTEXT_CLIENT_VERSION,2, EGL_NONE

    };

EGLContext context =eglCreateContext(display, config, EGL_NO_CONTEXT, ctxAttr);

if(context == EGL_NO_CONTEXT) {

LOGD("eglCreateContext failed!");

return;

    }

if(EGL_TRUE !=eglMakeCurrent(display, winSurface, winSurface, context)) {

LOGD("eglMakeCurrent failed!");

return;

    }

LOGD("EGL Init Success!");

//顶点和片元shader初始化

//顶点shader初始化

GLint vsh =InitShader(vertexShader, GL_VERTEX_SHADER);

//片元yuv420 shader初始化

GLint fsh =InitShader(fragYUV420P, GL_FRAGMENT_SHADER);

//创建渲染程序

GLint program =glCreateProgram();

if(program ==0) {

LOGD("glCreateProgram failed!");

return;

    }

//渲染程序中加入着色器代码

glAttachShader(program, vsh);

glAttachShader(program, fsh);

//链接程序

glLinkProgram(program);

GLint status =0;

glGetProgramiv(program ,GL_LINK_STATUS, &status);

if(status != GL_TRUE) {

LOGD("glLinkProgram failed!");

return;

    }

glUseProgram(program);//激活渲染程序

LOGD("glLinkProgram success!");

/*****************************************************************/

//加入三维顶点数据 两个三角形组成正方形

staticfloatvers[] = {

1.0f,-1.0f,0.0f,

-1.0f,-1.0f,0.0f,

1.0f,1.0f,0.0f,

-1.0f,1.0f,0.0f,

    };

GLuint apos = (GLuint)glGetAttribLocation(program,"aPosition");

glEnableVertexAttribArray(apos);

//传递顶点

glVertexAttribPointer(apos,3, GL_FLOAT,GL_FALSE,12, vers);

//加入材质坐标数据

staticfloattxts[] = {

1.0f,0.0f,//右下

0.0f,0.0f,

1.0f,1.0f,

0.0,1.0

    };

GLuint atex = (GLuint)glGetAttribLocation(program,"aTexCoord");

glEnableVertexAttribArray(atex);

glVertexAttribPointer(atex,2, GL_FLOAT,GL_FALSE,8, txts);

intwidth =424;

intheight =240;

//材质纹理初始化

//设置纹理层

glUniform1i(glGetUniformLocation(program,"yTexture"),0);//对于纹理第1层

glUniform1i(glGetUniformLocation(program,"uTexture"),1);//对于纹理第2层

glUniform1i(glGetUniformLocation(program,"vTexture"),2);//对于纹理第3层

//创建opengl纹理

GLuint texts[3] = {0};

//创建三个纹理

glGenTextures(3, texts);

//设置纹理属性

glBindTexture(GL_TEXTURE_2D, texts[0]);

//缩小的过滤器

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//设置纹理的格式和大小

glTexImage2D(GL_TEXTURE_2D,

0,//细节基本 0默认

GL_LUMINANCE,//gpu内部格式 亮度,灰度图

width, height,//拉升到全屏

0,//边框

GL_LUMINANCE,//数据的像素格式 亮度,灰度图 要与上面一致

GL_UNSIGNED_BYTE,//像素的数据类型

NULL//纹理的数据

    );

//设置纹理属性

glBindTexture(GL_TEXTURE_2D, texts[1]);

//缩小的过滤器

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//设置纹理的格式和大小

glTexImage2D(GL_TEXTURE_2D,

0,//细节基本 0默认

GL_LUMINANCE,//gpu内部格式 亮度,灰度图

width/2, height/2,//拉升到全屏

0,//边框

GL_LUMINANCE,//数据的像素格式 亮度,灰度图 要与上面一致

GL_UNSIGNED_BYTE,//像素的数据类型

NULL//纹理的数据

    );

//设置纹理属性

glBindTexture(GL_TEXTURE_2D, texts[2]);

//缩小的过滤器

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);

glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);

//设置纹理的格式和大小

glTexImage2D(GL_TEXTURE_2D,

0,//细节基本 0默认

GL_LUMINANCE,//gpu内部格式 亮度,灰度图

width/2, height/2,//拉升到全屏

0,//边框

GL_LUMINANCE,//数据的像素格式 亮度,灰度图 要与上面一致

GL_UNSIGNED_BYTE,//像素的数据类型

NULL//纹理的数据

    );

/*****************************************************************/

//纹理的修改和显示

unsignedchar*buf[3] = {0};

buf[0] =newunsignedchar[width * height];

buf[1] =newunsignedchar[width * height /4];

buf[2] =newunsignedchar[width * height /4];

for(inti =0; i <10000; i++) {

//420p  yyyyyyyy uu vv

if(feof(fp) ==0) {

fread(buf[0],1, width*height, fp);

fread(buf[1],1, width*height /4, fp);

fread(buf[2],1, width*height /4, fp);

        }

//激活第1层纹理,绑定到创建的opengl纹理

glActiveTexture(GL_TEXTURE0);

glBindTexture(GL_TEXTURE_2D, texts[0]);

//替换纹理内容

glTexSubImage2D(GL_TEXTURE_2D,0,0,0, width, height, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[0]);

//激活第2层纹理,绑定到创建的opengl纹理

glActiveTexture(GL_TEXTURE0 +1);

glBindTexture(GL_TEXTURE_2D, texts[1]);

//替换纹理内容

glTexSubImage2D(GL_TEXTURE_2D,0,0,0, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[1]);

//激活第2层纹理,绑定到创建的opengl纹理

glActiveTexture(GL_TEXTURE0 +2);

glBindTexture(GL_TEXTURE_2D, texts[2]);

//替换纹理内容

glTexSubImage2D(GL_TEXTURE_2D,0,0,0, width/2, height/2, GL_LUMINANCE, GL_UNSIGNED_BYTE, buf[2]);

//三维绘制

glDrawArrays(GL_TRIANGLE_STRIP,0,4);

//窗口显示

eglSwapBuffers(display, winSurface);

    }

env->ReleaseStringUTFChars(url_, url);

}

extern"C"

JNIEXPORT jstring JNICALL

Java_com_lichao_chaoplayer_MainActivity_TestJNI(JNIEnv *env, jobject instance) {

std::string hello ="Hello from C++";

returnenv->NewStringUTF(hello.c_str());

}

extern"C"

JNIEXPORTvoidJNICALL

Java_com_lichao_chaoplayer_ChaoPlay_InitView(JNIEnv *env, jobject instance, jobject surface) {

ANativeWindow *win =ANativeWindow_fromSurface(env,surface);

view->SetRender(win);

//ChaoEGL::Get()->Init(win);

//ChaoShader shader;

//shader.Init();

}

推荐阅读更多精彩内容