1. <ul id="0c1fb"></ul>

      <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
      <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区

      RELATEED CONSULTING
      相關(guān)咨詢
      選擇下列產(chǎn)品馬上在線溝通
      服務(wù)時(shí)間:8:30-17:00
      你可能遇到了下面的問(wèn)題
      關(guān)閉右側(cè)工具欄

      新聞中心

      這里有您想知道的互聯(lián)網(wǎng)營(yíng)銷解決方案
      讓IjkPlayer支持插入自定義的GPU濾鏡方法

      最近因?yàn)楣ぷ鞯脑颍枰峁┮粋€(gè)將我們的AiyaEffectsSDK插入到IjkPlayer中的示例,就不得不好好看了下IjkPlayer的代碼。在IjkPlayer中并沒(méi)有提供設(shè)置自定義GPU濾鏡的接口,所以最后只能自己動(dòng)手,以求豐衣足食了。不得不說(shuō),Bilibili開源的這個(gè)IjkPlayer播放器的確非常強(qiáng)大,代碼設(shè)計(jì)的非常清晰,仔細(xì)看看,能學(xué)到不少東西。

      創(chuàng)新互聯(lián)建站-專業(yè)網(wǎng)站定制、快速模板網(wǎng)站建設(shè)、高性價(jià)比渭城網(wǎng)站開發(fā)、企業(yè)建站全套包干低至880元,成熟完善的模板庫(kù),直接使用。一站式渭城網(wǎng)站制作公司更省心,省錢,快速模板網(wǎng)站建設(shè)找我們,業(yè)務(wù)覆蓋渭城地區(qū)。費(fèi)用合理售后完善,十多年實(shí)體公司更值得信賴。

      IjkPlayer源碼獲取及編譯方法

      源碼地址,編譯參考readme即可:

      # 獲取ijk源碼
      git clone https://github.com/Bilibili/ijkplayer.git ijkplayer-android
      # 進(jìn)入源碼目錄
      cd ijkplayer-android
      # checkout 最新版本
      git checkout -B latest k0.8.0
      # 執(zhí)行腳本,此腳本會(huì)下載ijk依賴的源碼,比如ffmpeg
      ./init-android.sh
      # 編譯ffmpeg, all可以換成指定版本,如armv7a
      cd android/contrib
      ./compile-ffmpeg.sh clean
      ./compile-ffmpeg.sh all
      # 編譯ijkplayer,all可以換成指定版本,如armv7a
      cd ..
      ./compile-ijk.sh all

      IjkPlayer分析及修改

      Android 版的IjkPlayer示例工程中,播放視頻界面為tv.danmaku.ijk.media.example.activities.VideoActivity,在VideoActivity中使用的是IjkVideoView來(lái)播放視頻的,位于tv.danmaku.ijk.media.example.widget.media包下。

      IjkVideoView使用與Android的VideoView基本一致,在IjkVideoView中,設(shè)置視頻源調(diào)用setVideoURI方法,而此方法又會(huì)調(diào)用private屬性的openVideo方法。在openVideo方法中,會(huì)根據(jù)mSettings.getPlayer()的值創(chuàng)建一個(gè)IMediaPlayer:

      public IMediaPlayer createPlayer(int playerType) {
        IMediaPlayer mediaPlayer = null;
        switch (playerType) {
         case Settings.PV_PLAYER__IjkExoMediaPlayer: {
          IjkExoMediaPlayer IjkExoMediaPlayer = new IjkExoMediaPlayer(mAppContext);
          mediaPlayer = IjkExoMediaPlayer;
         }
         break;
         case Settings.PV_PLAYER__AndroidMediaPlayer: {
          AndroidMediaPlayer androidMediaPlayer = new AndroidMediaPlayer();
          mediaPlayer = androidMediaPlayer;
         }
         break;
         case Settings.PV_PLAYER__IjkMediaPlayer:
         default: {
          IjkMediaPlayer ijkMediaPlayer = null;
          if (mUri != null) {
           ijkMediaPlayer = new IjkMediaPlayer();
           ijkMediaPlayer.native_setLogLevel(IjkMediaPlayer.IJK_LOG_DEBUG);
           if (mSettings.getUsingMediaCodec()) {
            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 1);
            if (mSettings.getUsingMediaCodecAutoRotate()) {
             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 1);
            } else {
             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-auto-rotate", 0);
            }
            if (mSettings.getMediaCodecHandleResolutionChange()) {
             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 1);
            } else {
             ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec-handle-resolution-change", 0);
            }
           } else {
            ijkMediaPlayer.setOption(IjkMediaPlayer.OPT_CATEGORY_PLAYER, "mediacodec", 0);
           }
           //省略其他參數(shù)設(shè)置的的代碼
          }
          mediaPlayer = ijkMediaPlayer;
         }
         break;
        }
        if (mSettings.getEnableDetachedSurfaceTextureView()) {
         mediaPlayer = new TextureMediaPlayer(mediaPlayer);
        }
        return mediaPlayer;
       }

      從上面代碼中可以看到,在IjkVideoView中多處用到了mSettings,mSettings的值主要是用戶設(shè)置的,通過(guò)SharedPreferences保存的,包括音視頻解碼設(shè)置、是否使用OpenSLES、渲染View等等。可以參看SettingsActivity界面。

      根據(jù)playerType創(chuàng)建IjkMediaPlayer,前兩類分別為google的ExoPlayer和Android的MediaPlayer。除此之外才是真正的創(chuàng)建的IjkPlayer。

      mSettings中的其他參數(shù)最終會(huì)轉(zhuǎn)換后通過(guò)IjkMediaPlayer的setOption方法進(jìn)行設(shè)置,而IjkMediaPlayer.setOption又是直接調(diào)用native方法。進(jìn)入IjkMediaPlayer可以發(fā)現(xiàn),IjkMediaPlayer中的許多方法都是native方法,或者調(diào)用了native方法。

      增加setGLFilter接口

      在ijkmedia文件夾下全局搜索其中一個(gè)方法_setDataSource,得到內(nèi)容大致如下:

      F:\cres\C\ijkplayer-android\ijkmedia\ijkplayer\android\ijkplayer_jni.c:
      static void
       IjkMediaPlayer_setDataSourceAndHeaders(
       JNIEnv *env, jobject thiz, jstring path,
       jobjectArray keys, jobjectArray values)
      ...
      static void
      IjkMediaPlayer_setDataSourceFd(JNIEnv *env, jobject thiz, jint fd)
      {
       MPTRACE("%s\n", __func__);
      ...
      static void
      IjkMediaPlayer_setDataSourceCallback(JNIEnv *env, jobject thiz, jobject callback)
      {
       MPTRACE("%s\n", __func__);
      ...
      static JNINativeMethod g_methods[] = {
       {
         "_setDataSource",
        "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
         (void *) IjkMediaPlayer_setDataSourceAndHeaders
       },
        { "_setDataSourceFd",  "(I)V",  (void *) IjkMediaPlayer_setDataSourceFd },
        { "_setDataSource",   "(Ltv/danmaku/ijk/media/player/misc/IMediaDataSource;)V", (void *)IjkMediaPlayer_setDataSourceCallback },
       { "_setAndroidIOCallback", "(Ltv/danmaku/ijk/media/player/misc/IAndroidIO;)V", (void *)IjkMediaPlayer_setAndroidIOCallback },
      

      即知,在Android版本的ijkplayer,入口即為ijkmedia\ijkplayer\android\ijkplayer_jni.c

      在IjkMediaPlayer.java及ijkplayer_jni.c文件中增加setGLFilter方法:

      //增加setGLFilter方法
      static void IjkMediaPlayer_native_setGLFilter(JNIEnv *env, jclass clazz,jobject filter)
      {
      }
      
      // ----------------------------------------------------------------------------
      static JNINativeMethod g_methods[] = {
       {
        "_setDataSource",
        "(Ljava/lang/String;[Ljava/lang/String;[Ljava/lang/String;)V",
        (void *) IjkMediaPlayer_setDataSourceAndHeaders
       },
       { "_setDataSourceFd",  "(I)V",  (void *) IjkMediaPlayer_setDataSourceFd },
       { "_setDataSource",   "(Ltv/danmaku/ijk/media/player/misc/IMediaDataSource;)V", (void *)IjkMediaPlayer_setDataSourceCallback },
       //···省略其他方法
       { "_setGLFilter",   "(Ltv/danmaku/ijk/media/player/IjkFilter;)V", (void *) IjkMediaPlayer_native_setGLFilter },
      };
      

      渲染時(shí)回調(diào)用戶設(shè)置的GLFilter相關(guān)方法

      在ijkmedia/ijksdl/gles2/internal.h文件中,IJK_GLES2_Renderer結(jié)構(gòu)體內(nèi)增加:

      typedef struct IJK_GLES2_Renderer
      {
       //...
       GLuint frame_buffers[1];
       GLuint frame_textures[1];
       int hasFilter=0;
       void (* func_onCreate)(void);
       void (* func_onSizeChanged)(int width,int height);
       void (* func_onDrawFrame)(int textureId);
       //...
      } IJK_GLES2_Renderer;

      這三個(gè)方法即為IjkFlter的三個(gè)方法,將會(huì)在Jni里面,將Java中設(shè)置的IjkFilter對(duì)象的三個(gè)方法與之對(duì)應(yīng)起來(lái)。

      全局搜索glDraw搜索到只有一個(gè)在renderer.c的IJK_GLES2_Renderer_renderOverlay方法中,渲染工作也是此方法執(zhí)行的。當(dāng)然,IjkPlayer利用OpenGLES渲染時(shí),會(huì)根據(jù)從視頻中解碼出來(lái)的數(shù)據(jù)具體格式來(lái)進(jìn)行渲染,比如yuv420p、yuv420sp、rbg565等等諸多格式。具體在ijkmedia/ijksdl/gles2/下找到,renderer_rgb.c\renderer.yuv420p.c等等都是。

      當(dāng)用戶在Java層設(shè)置了GLFilter時(shí),GLFilter的三個(gè)方法應(yīng)該在合適的時(shí)候被C回調(diào),從名字可以看出來(lái),這三個(gè)方法,和GLSurfaceView.Renderer接口中定義的三個(gè)方法其實(shí)是一樣的。

      具體在IJK_GLES2_Renderer_renderOverlay方法中的修改如下:

      GLboolean IJK_GLES2_Renderer_renderOverlay(IJK_GLES2_Renderer *renderer, SDL_VoutOverlay *overlay)
      {
       /*用戶設(shè)置了fitler,而且沒(méi)有創(chuàng)建過(guò)framebuffer,創(chuàng)建framebuffer,依舊利用
       IjkPlayer里面原來(lái)的流程,就yuv或rgb的數(shù)據(jù),渲染到一個(gè)texture上面去,然后將
       這個(gè)texture作為原始數(shù)據(jù)傳遞給java層進(jìn)行處理。
       */
       if(renderer->hasFilter&&!renderer->frame_buffers[0]&&renderer->frame_width>0&&renderer->frame_height>0){
        //創(chuàng)建一個(gè)texture,用來(lái)接受將不同格式的視頻幀數(shù)據(jù),渲染成一個(gè)紋理
        glGenTextures(1,renderer->frame_textures);
        glBindTexture(GL_TEXTURE_2D,renderer->frame_textures[0]);
        glTexImage2D(GL_TEXTURE_2D,0,GL_RGBA,renderer->frame_width,renderer->frame_height,0,GL_RGBA,GL_UNSIGNED_BYTE,NULL); 
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR);
        glTexParameteri(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_CLAMP_TO_EDGE);
        glTexParameterf(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_CLAMP_TO_EDGE);
        glBindTexture(GL_TEXTURE_2D,0);
        //創(chuàng)建framebuffer來(lái)掛載紋理,以渲染視頻幀
        glGenFramebuffers(1,renderer->frame_buffers);
        glBindFramebuffer(GL_FRAMEBUFFER,renderer->frame_buffers[0]);
        glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,renderer->frame_textures[0],0);
        // int r;
        // if ((r = glCheckFramebufferStatus(GL_FRAMEBUFFER)) != GL_FRAMEBUFFER_COMPLETE)
        // {
        //  ALOGE("wuwang: Error in Framebuffer 0x%x", r);
        // }else{
        //  ALOGE("wuwang: glCheckFramebufferStatus 0x%x", r);
        // }
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        //調(diào)用Java傳遞進(jìn)來(lái)的Filter的onCreated方法及onSizeChanged方法
        renderer->func_onCreated();
        renderer->func_onSizeChanged(renderer->frame_width,renderer->frame_height);
        ALOGE("wuwang: create frame_buffers and textures %d,%d",renderer->frame_width,renderer->frame_height);
       }
       //用戶設(shè)置了Filter,就掛載frameBuffer,否則就按照原來(lái)的流程直接渲染到屏幕上
       if(renderer->hasFilter&&renderer->frame_buffers[0]){
        GLint bindFrame;
        glGetIntegerv(GL_FRAMEBUFFER_BINDING,&bindFrame);
        ALOGE("wuwang: default frame binding %d",bindFrame);
        glBindFramebuffer(GL_FRAMEBUFFER,renderer->frame_buffers[0]);
        // glFramebufferTexture2D(GL_FRAMEBUFFER,GL_COLOR_ATTACHMENT0,GL_TEXTURE_2D,renderer->frame_textures[0],0);
        /* 這句一定要加上,否則無(wú)法增加了Filter之后,啟用了其他GLProgram,無(wú)法渲染原始視頻到Texture上去了 */
        IJK_GLES2_Renderer_use(renderer);
       }
       if (!renderer || !renderer->func_uploadTexture)
        return GL_FALSE;
       glClear(GL_COLOR_BUFFER_BIT);    IJK_GLES2_checkError_TRACE("glClear");
       ALOGE("wuwang: frame buffer id:%d",renderer->frame_buffers[0]);
       GLsizei visible_width = renderer->frame_width;
       GLsizei visible_height = renderer->frame_height;
       if (overlay) {
        visible_width = overlay->w;
        visible_height = overlay->h;
        if (renderer->frame_width != visible_width ||
         renderer->frame_height != visible_height ||
         renderer->frame_sar_num != overlay->sar_num ||
         renderer->frame_sar_den != overlay->sar_den) {
         renderer->frame_width = visible_width;
         renderer->frame_height = visible_height;
         renderer->frame_sar_num = overlay->sar_num;
         renderer->frame_sar_den = overlay->sar_den;
         renderer->vertices_changed = 1;
        }
        renderer->last_buffer_width = renderer->func_getBufferWidth(renderer, overlay);
        if (!renderer->func_uploadTexture(renderer, overlay)){
         return GL_FALSE;
        }
       } else {
        // NULL overlay means force reload vertice
        renderer->vertices_changed = 1;
       }
       GLsizei buffer_width = renderer->last_buffer_width;
       if (renderer->vertices_changed ||
        (buffer_width > 0 &&
         buffer_width > visible_width &&
         buffer_width != renderer->buffer_width &&
         visible_width != renderer->visible_width)){
        if(renderer->hasFilter&&renderer->frame_buffers[0]){
         renderer->func_onSizeChanged(renderer->frame_width,renderer->frame_height);
        }
        renderer->vertices_changed = 0;
        IJK_GLES2_Renderer_Vertices_apply(renderer);
        IJK_GLES2_Renderer_Vertices_reset(renderer);
        IJK_GLES2_Renderer_Vertices_reloadVertex(renderer);
        renderer->buffer_width = buffer_width;
        renderer->visible_width = visible_width;
        GLsizei padding_pixels  = buffer_width - visible_width;
        GLfloat padding_normalized = ((GLfloat)padding_pixels) / buffer_width;
        IJK_GLES2_Renderer_TexCoords_reset(renderer);
        IJK_GLES2_Renderer_TexCoords_cropRight(renderer, padding_normalized);
        IJK_GLES2_Renderer_TexCoords_reloadVertex(renderer);
       }
       glDrawArrays(GL_TRIANGLE_STRIP, 0, 4);  IJK_GLES2_checkError_TRACE("glDrawArrays");
       //用戶設(shè)置了Filter,就取消掛載FrameBuffer,并調(diào)用Filter的onDrawFrame方法。
       if(renderer->hasFilter&&renderer->frame_buffers[0]){
        glBindFramebuffer(GL_FRAMEBUFFER,0);
        renderer->func_onDrawFrame(renderer->frame_textures[0]);
        // renderer->func_onDrawFrame(renderer->plane_textures[0]);
       }
       return GL_TRUE;
      }

      GLFilter從設(shè)置到調(diào)用實(shí)現(xiàn)分析

      上面已經(jīng)完成的接口的編寫,也做好執(zhí)行的編寫。現(xiàn)在需要將接口傳遞進(jìn)來(lái)的GLFilter的三個(gè)方法與執(zhí)行的三個(gè)方法對(duì)應(yīng)起來(lái),才能是用戶的Filter真正發(fā)揮作用。

      IJK_GLES2_Renderer的創(chuàng)建是根據(jù)SDL_VoutOverlay來(lái)的,在查找SDL_VoutOverlay從哪里來(lái)的,一路可以搜索到ijkmedia/ijksdl/android/ijksdl_vout_android_nativewindow.c中的func_create_overlay方法:

      static SDL_VoutOverlay *func_create_overlay(int width, int height, int frame_format, SDL_Vout *vout)
      {
       SDL_LockMutex(vout->mutex);
       SDL_VoutOverlay *overlay = func_create_overlay_l(width, height, frame_format, vout);
       SDL_UnlockMutex(vout->mutex);
       return overlay;
      }

      SDL_VoutOverlay的創(chuàng)建和SDL_Vout有關(guān),再查找SDL_Vout的來(lái)源,可以找到

      ijkmedia/ijksdl/android/ijksdl_vout_android_nativewindow.c中的SDL_VoutAndroid_CreateForANativeWindow:

      SDL_Vout *SDL_VoutAndroid_CreateForANativeWindow()
      {
       SDL_Vout *vout = SDL_Vout_CreateInternal(sizeof(SDL_Vout_Opaque));
       if (!vout)
        return NULL;
       SDL_Vout_Opaque *opaque = vout->opaque;
       opaque->native_window = NULL;
       if (ISDL_Array__init(&opaque->overlay_manager, 32))
        goto fail;
       if (ISDL_Array__init(&opaque->overlay_pool, 32))
        goto fail;
       opaque->egl = IJK_EGL_create();
       if (!opaque->egl)
        goto fail;
       vout->opaque_class = &g_nativewindow_class;
       vout->create_overlay = func_create_overlay;
       vout->free_l   = func_free_l;
       vout->display_overlay = func_display_overlay;
       return vout;
      fail:
       func_free_l(vout);
       return NULL;
      }

      它的初始化,再?zèng)]啥可以關(guān)聯(lián)了,那就只能找調(diào)用它的地方了。搜索后發(fā)現(xiàn)SDL_VoutAndroid_CreateForANativeWindow只在ijkmedia\ijksdl\android\ijksdl_vout_android_surface.c的SDL_VoutAndroid_CreateForAndroidSurface方法:

      SDL_Vout *SDL_VoutAndroid_CreateForAndroidSurface()
      {
       return SDL_VoutAndroid_CreateForANativeWindow();
      }

      看來(lái)是被包了一層皮,那就接著查找SDL_VoutAndroid_CreateForAndroidSurface,搜索到調(diào)用它的為ijkmedia\ijkplayer\android\ijkplayer_android.c的ijkmp_android_create方法:

      IjkMediaPlayer *ijkmp_android_create(int(*msg_loop)(void*))
      {
       IjkMediaPlayer *mp = ijkmp_create(msg_loop);
       if (!mp)
        goto fail;
       mp->ffplayer->vout = SDL_VoutAndroid_CreateForAndroidSurface();
       if (!mp->ffplayer->vout)
        goto fail;
       mp->ffplayer->pipeline = ffpipeline_create_from_android(mp->ffplayer);
       if (!mp->ffplayer->pipeline)
        goto fail;
       ffpipeline_set_vout(mp->ffplayer->pipeline, mp->ffplayer->vout);
       return mp;
      fail:
       ijkmp_dec_ref_p(&mp);
       return NULL;
      }

      可以看到ijkmp_android_create返回了一個(gè)IjkMediaPlayer,這個(gè)在Java中也有一個(gè)這樣的類,那么曙光應(yīng)該就不遠(yuǎn)了。

      再搜索ijkmp_android_create,結(jié)果調(diào)用它的只有ijkmedia\ijkplayer\android\ijkplayer_jni.c中的IjkMediaPlayer_native_setup方法,到了這里,就可以將IjkFilter傳遞下去了。

      在上面的過(guò)程中,可以看到從renderer.c到j(luò)ni,IJK_GLES2_Renderer的創(chuàng)建,依賴SDL_VoutOverlay。SDL_VoutOverlay的創(chuàng)建依賴SDL_Vout。SDL_Vout是FFPlayer的成員,而FFPlayer又是IjkMediaPlayer的成員變量。Java層的IjkMediaPlayer依賴于IjkMediaPlayer

      從GLFilter到IJK_GLES2_Renderer

      根據(jù)上面的分析,可以知道,在jni中增加的setGLFilter的方法中,我們可以將GLFilter的方法傳遞給IjkMediaPlayer->FFPlayer->SDLVout,然后再傳遞給SDL_VoutOverlay,再由SDL_VoutOverlay傳遞給IJK_GLES2_Renderer即可。這樣將增加Filter的功能增加進(jìn)去了,也不會(huì)影響IjkPlayer的流程,讓IOS同樣能夠快速的實(shí)現(xiàn)增加GPU濾鏡的功能。

      先在SDL_VoutOverlay和SDL_Vout中的結(jié)構(gòu)體定義中(ijkmedia/ijksdl/ijksdl_vout.h文件中)同樣加入在IJK_GLES2_Renderer中增加的成員:

      struct SDL_VoutOverlay {
       //...
       int hasFilter;
       void (* func_onCreated)(void);
       void (* func_onSizeChanged)(int width,int height);
       int (* func_onDrawFrame)(int textureId);
       //...
      };
      struct SDL_Vout {
       //...
       int hasFilter;
       void (* func_onCreated)(void);
       void (* func_onSizeChanged)(int width,int height);
       int (* func_onDrawFrame)(int textureId);
       //...
      };

      然后在上述幾個(gè)方法,分別完成這幾個(gè)成員數(shù)據(jù)從SDL_Vout到SDL_VoutOverlay,再到IJK_GLES2_Renderer的傳遞。

      分別為:

      ijkmedia\ijksdl\gles2\renderer.c文件中IJK_GLES2_Renderer_create方法

      IJK_GLES2_Renderer *IJK_GLES2_Renderer_create(SDL_VoutOverlay *overlay)
      {
       if (!overlay)
        return NULL;
       //中間省略...
       renderer->format = overlay->format;
       //增加的內(nèi)容
       renderer->hasFilter=overlay->hasFilter;
       renderer->func_onCreated=overlay->func_onCreated;
       renderer->func_onSizeChanged=overlay->func_onSizeChanged;
       renderer->func_onDrawFrame=overlay->func_onDrawFrame;
       return renderer;
      }

      ijksdl\android\ijksdl_vout_android_nativewindow.c文件中func_create_overlay方法

      static SDL_VoutOverlay *func_create_overlay(int width, int height, int frame_format, SDL_Vout *vout)
      {
       SDL_LockMutex(vout->mutex);
       SDL_VoutOverlay *overlay = func_create_overlay_l(width, height, frame_format, vout);
       //增加的內(nèi)容
       overlay->hasFilter=vout->hasFilter;
       overlay->func_onCreated=vout->func_onCreated;
       overlay->func_onSizeChanged=vout->func_onSizeChanged;
       overlay->func_onDrawFrame=vout->func_onDrawFrame;
       SDL_UnlockMutex(vout->mutex);
       return overlay;
      }
      

      最后還需要完成SDL_Vout中這幾個(gè)成員的賦值,并調(diào)用Java傳入的GLFilter對(duì)象的相關(guān)方法(ijkplayer_jni.c文件中):

      static JNIEnv * mEnv;
      static jobject mFilter;
      static jmethodID onCreatedMethod;
      static jmethodID onSizeChangedMethod;
      static jmethodID onDrawFrameMethod;
      void onCreated(){
       if(!mEnv){
        (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL);
        jclass filterClass=(*mEnv)->GetObjectClass(mEnv,mFilter);
        onCreatedMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onCreated","()V");
        onSizeChangedMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onSizeChanged","(II)V");
        onDrawFrameMethod=(*mEnv)->GetMethodID(mEnv,filterClass,"onDrawFrame","(I)I");
        (*g_jvm)->DetachCurrentThread(g_jvm);
       }
       if(onCreatedMethod){
        (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL);
        (*mEnv)->CallVoidMethod(mEnv,mFilter,onCreatedMethod);
        (*g_jvm)->DetachCurrentThread(g_jvm);
       }
      }
      void onSizeChanged(int width,int height){
       if(onSizeChangedMethod){
        (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL);
        (*mEnv)->CallVoidMethod(mEnv,mFilter,onSizeChangedMethod,width,height);
        (*g_jvm)->DetachCurrentThread(g_jvm);
       }
      }
      int onDrawFrame(int textureId){
       if(onDrawFrameMethod){
        (*g_jvm)->AttachCurrentThread(g_jvm,&mEnv,NULL);
        int ret=(*mEnv)->CallIntMethod(mEnv,mFilter,onDrawFrameMethod,textureId);
        (*g_jvm)->DetachCurrentThread(g_jvm);
        return ret;
       }
       return textureId;
      }
      /*注意不能直接保存env,filter然后在onDrawFrame等方法中使用,因?yàn)檫@三個(gè)方法的調(diào)用與setGLFilter不是在同一個(gè)線程中*/
      static void IjkMediaPlayer_native_setGLFilter(JNIEnv *env, jobject clazz, jobject filter)
      {
       if(mFilter){
        (*env)->DeleteGlobalRef(env,mFilter);
       }
       IjkMediaPlayer *mp = jni_get_media_player(env, clazz);
       if(filter!=NULL){
        mFilter=(*env)->NewGlobalRef(env,filter);
        mp->ffplayer->vout->hasFilter=1;
        mp->ffplayer->vout->func_onCreated=onCreated;
        mp->ffplayer->vout->func_onSizeChanged=onSizeChanged;
        mp->ffplayer->vout->func_onDrawFrame=onDrawFrame;
       }else{
        mp->ffplayer->vout->hasFilter=0;
       }
      }

      至此,Java一直到sdl中的renderer就算連通了。在Java中的處理就和GLSurfaceView設(shè)置Renderer基本一致了,不同的是,我們給IjkPlayer中增加的GLFilter,已經(jīng)提供了一個(gè)原始的視頻圖像作為onDrawFrame的參數(shù),在GLFilter中,只需要處理這個(gè)Texture并渲染出來(lái)就可以了。

      插入濾鏡示例

      將修改后的代碼重新編譯下,編譯后的庫(kù)會(huì)自動(dòng)更新到Ijkplayer的Android工程下,設(shè)置自定義的濾鏡后,不出意外就可以看到效果了。以下分別為原始視頻、黑白濾鏡處理后的視頻、增加了AiyaEffectsSDK并設(shè)置了特效的視頻效果,因?yàn)镃SDN對(duì)圖片大小限制的問(wèn)題,都是截取了一小段:

      讓IjkPlayer支持插入自定義的GPU濾鏡方法 讓IjkPlayer支持插入自定義的GPU濾鏡方法 讓IjkPlayer支持插入自定義的GPU濾鏡方法

      工程太大了,修改的地方也不算多,就不上傳代碼了。有需要的朋友根據(jù)以上流程下載ijkplayer源碼自行修改即可,同時(shí)也可以看看Ijkplayer的源碼。

      以上這篇讓IjkPlayer支持插入自定義的GPU濾鏡方法就是小編分享給大家的全部?jī)?nèi)容了,希望能給大家一個(gè)參考,也希望大家多多支持創(chuàng)新互聯(lián)。


      當(dāng)前題目:讓IjkPlayer支持插入自定義的GPU濾鏡方法
      當(dāng)前鏈接:http://www.ef60e0e.cn/article/gjceci.html
      99热在线精品一区二区三区_国产伦精品一区二区三区女破破_亚洲一区二区三区无码_精品国产欧美日韩另类一区
      1. <ul id="0c1fb"></ul>

        <noscript id="0c1fb"><video id="0c1fb"></video></noscript>
        <noscript id="0c1fb"><listing id="0c1fb"><thead id="0c1fb"></thead></listing></noscript>

        隆尧县| 西宁市| 裕民县| 洱源县| 灵武市| 德惠市| 时尚| 邛崃市| 望谟县| 阳原县| 海盐县| 夏邑县| 偃师市| 昆明市| 盐亭县| 玛沁县| 三都| 夏河县| 体育| 潼关县| 云林县| 东至县| 开平市| 浏阳市| 静安区| 连城县| 大姚县| 瑞安市| 获嘉县| 安丘市| 敦化市| 崇明县| 富蕴县| 兴化市| 滕州市| 潮安县| 城口县| 莱芜市| 丹巴县| 闽侯县| 蒲江县|