NDK移植havlenapetr/FFMpeg |Android平台实现 【Mark一下,目前仅仅测试了一下可行性(可行,但有问题)|接下来就好好研究研究(站下巨人的肩膀么- -)】


/********************************************************************************************
 * author:conowen@大钟                                                                                                                          
 * E-mail:conowen@hotmail.com                                                                                                             
 * http://blog.csdn.net/conowen                                                                                                              
 * 注:本文为原创,仅作为学习交流使用,转载请标明作者及出处。     

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

原文链接:http://blog.csdn.net/conowen/article/details/7526398


1、

        使用NDK去编译官方的FFmpeg原版的话,还得自己实现JNI层与java层工程量比较大。所以移植FFmpeg到Android平台时,可以移植一些已经实现JNI与JAVA层的开源项目,毕竟软件行业从来都是站在巨人肩膀上发展的。


2、移植/FFMpeg  


havlenapetr的开源项目是比较出名的一个FFmpeg工程,很多Android多媒体项目都是在此基础上面修改的。


下载地址:https://github.com/havlenapetr/FFMpeg

可以直接ZIP包:https://github.com/havlenapetr/FFMpeg/zipball/debug

或者通过git方式下载,新建一个目录,然后在linux的终端下执行,当然了,你要事情安装git的相关工具

  1. git clone https://github.com/havlenapetr/FFMpeg.git  

3、利用NDK编译生成so库


下载后直接在havlenapetr-FFMpeg-7c27aa2的顶级目录下执行

  1. $ndk/ndk-build  

是可以编译通过的,不会提示任何error。

关于如何利用NDK编译,可以参考我之前的博文:http://blog.csdn.net/conowen/article/details/7518870


4、导入java工程,实现播放

       然后把在eclipse里面,把havlenapetr-FFMpeg-7c27aa2这个项目import进来,就可以播放视频了。


4.1、需要注意的是:这个版本的havlenapetr FFmpeg工程只能在Android 2.2上面运行,因为havlenapetr采用的是音视频直接在JNI层输入。可以注意到havlenapetr-FFMpeg-7c27aa2目录下有prebuilt这样一个目录,此目录下有Android 2.2版本的libjniaudio.so和libjnivideo.so两个库文件。


4.2、Android版本不同导致不能播放:

havlenapetr的FFmpeg项目音视频输出如下

音频:采用Android底层的audiotrack输出。

视频:在FFmpeg解码之后,得到YUV信号,然后转换成RGB信号,最终通过Android底层的surface输出。


提示:可以移植SDL开源库实现音视频输出,因为SDL的视频输出机制是通过OPenGL呈现画面,这样就可以兼容所有的Android平台。


       但是问题就来了,Android每个版本的framework都是不大一样的,所以要在底层使用Android的audiotrack和surface来输入音视频信号,就要在相应版本的Android源代码中,重新编译生成libjniaudio.so和libjnivideo.so两个库文件了。


5、编译havlenapetr FFmpeg工程Android 2.3版本的libjniaudio.so和libjnivideo.so

          首先要明白一点,Android的官方源代码编译之后,是不会生成libjniaudio.so和libjnivideo.so的。所以要自己添加audiotrack.cpp、surface.cpp和Android.mk文件到Android源代码里面编译生成。(每次编译libjniaudio.so和libjnivideo.so都要重新编译这个Android源代码,时间比较长。)


5.1下载audio与video文件夹

可以在https://github.com/havlenapetr/android_frameworks_base下载audiotrack.cpp、surface.cpp和Android.mk,注意要选择正确的branch(分支)

froyo---->Android 2.2

gingerbread---->Android 2.3

ICS---->Android 4.0


关于havlenapetr-FFMpeg在Android 4.0(ICS)的补充说明



5.2、编译Android系统源代码

     下载之后,然后找到里面的native文件夹,把里面的audio和video文件夹拖进Android源代码的frameworks/base/native目录下。

绿色的是新加入的文件


需要注意的一点是:

gingerbread下载之后,里面是没有audio和video文件夹的,但是可以用froyo版本的audio和video文件夹。(也就是下载gingerbread感觉也没啥用Orz~~~)

但是我们可以使用froyo的audio和video文件夹,编译Android源代码是可以成功通过的,ndk-build也可以通过,但是在Android的java工程里面使用就会有以下错误信息。

  1. java.lang.NoSuchFieldError: no field with name='mSurface' signature='I' in class Landroid/view/Surface;  

加载库时,找不到mSruface类
修改方法是:
将surface.cpp中mSurface改为 mNativeSurface ,然后重新编译即可。当然了,你也可以用ICS的surface.cpp文件,这个版本是没有问题的。


另外编译havlenapetr FFmpeg工程Android 4.0版本的libjniaudio.so和libjnivideo.so与上面步骤差不多。


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

附上我所使用的audio与video(来源havlenapetr的项目)

video/jni/surface.cpp(注意目录结构)


  1. /* 
  2.  * Copyright (C) 2009 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16. #include <android/surface.h>  
  17. #include <surfaceflinger/Surface.h>  
  18. #include <utils/Log.h>  
  19. #include <SkBitmap.h>  
  20. #include <SkCanvas.h>  
  21.   
  22. #define TAG "SurfaceWrapper"  
  23.   
  24. using namespace android;  
  25.   
  26. static Surface*     sSurface;  
  27. static SkBitmap     sBitmapClient;  
  28. static SkBitmap     sBitmapSurface;  
  29.   
  30. static Surface* getNativeSurface(JNIEnv* env, jobject jsurface) {  
  31.     jclass clazz = env->FindClass("android/view/Surface");  
  32.     jfieldID field_surface = env->GetFieldID(clazz, "mNativeSurface""I");  
  33.     if(field_surface == NULL) {  
  34.         return NULL;  
  35.     }  
  36.     return (Surface *) env->GetIntField(jsurface, field_surface);  
  37. }  
  38.   
  39. static int initBitmap(SkBitmap *bitmap, int format, int width, int height, bool allocPixels) {  
  40.     switch (format) {  
  41.         case PIXEL_FORMAT_RGBA_8888:  
  42.             bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);  
  43.             break;  
  44.               
  45.         case PIXEL_FORMAT_RGBA_4444:  
  46.             bitmap->setConfig(SkBitmap::kARGB_4444_Config, width, height);  
  47.             break;  
  48.               
  49.         case PIXEL_FORMAT_RGB_565:  
  50.             bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height);  
  51.             break;  
  52.               
  53.         case PIXEL_FORMAT_A_8:  
  54.             bitmap->setConfig(SkBitmap::kA8_Config, width, height);  
  55.             break;  
  56.               
  57.         default:  
  58.             bitmap->setConfig(SkBitmap::kNo_Config, width, height);  
  59.             break;  
  60.     }  
  61.       
  62.     if(allocPixels) {  
  63.         bitmap->setIsOpaque(true);  
  64.         //-- alloc array of pixels  
  65.         if(!bitmap->allocPixels()) {  
  66.             return -1;  
  67.         }  
  68.     }  
  69.     return 0;  
  70. }  
  71.   
  72. int AndroidSurface_register(JNIEnv* env, jobject jsurface) {  
  73.     __android_log_print(ANDROID_LOG_INFO, TAG, "registering video surface");  
  74.       
  75.     sSurface = getNativeSurface(env, jsurface);  
  76.     if(sSurface == NULL) {  
  77.          return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;  
  78.     }  
  79.       
  80.     __android_log_print(ANDROID_LOG_INFO, TAG, "registered");  
  81.       
  82.     return ANDROID_SURFACE_RESULT_SUCCESS;  
  83. }  
  84.   
  85. int AndroidSurface_getPixels(int width, int height, void** pixels) {  
  86.     __android_log_print(ANDROID_LOG_INFO, TAG, "getting surface's pixels %ix%i", width, height);  
  87.     if(sSurface == NULL) {  
  88.         return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;  
  89.     }  
  90.     if(initBitmap(&sBitmapClient, PIXEL_FORMAT_RGB_565, width, height, true) < 0) {  
  91.         return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_CLIENT;  
  92.     }  
  93.     *pixels = sBitmapClient.getPixels();  
  94.     __android_log_print(ANDROID_LOG_INFO, TAG, "getted");  
  95.     return ANDROID_SURFACE_RESULT_SUCCESS;  
  96. }  
  97.   
  98. static void doUpdateSurface() {  
  99.     SkCanvas    canvas(sBitmapSurface);  
  100.     SkRect      surface_sBitmapClient;  
  101.     SkRect      surface_sBitmapSurface;  
  102.     SkMatrix    matrix;  
  103.       
  104.     surface_sBitmapSurface.set(0, 0, sBitmapSurface.width(), sBitmapSurface.height());  
  105.     surface_sBitmapClient.set(0, 0, sBitmapClient.width(), sBitmapClient.height());  
  106.     matrix.setRectToRect(surface_sBitmapClient, surface_sBitmapSurface, SkMatrix::kFill_ScaleToFit);  
  107.       
  108.     canvas.drawBitmapMatrix(sBitmapClient, matrix);  
  109. }  
  110.   
  111. static int prepareSurfaceBitmap(Surface::SurfaceInfo* info) {  
  112.     if(initBitmap(&sBitmapSurface, info->format, info->w, info->h, false) < 0) {  
  113.         return -1;  
  114.     }  
  115.     sBitmapSurface.setPixels(info->bits);  
  116.     return 0;  
  117. }  
  118.   
  119. int AndroidSurface_updateSurface() {  
  120.     static Surface::SurfaceInfo surfaceInfo;  
  121.     if(sSurface == NULL) {  
  122.         return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;  
  123.     }  
  124.     if (!Surface::isValid (sSurface)){  
  125.         return ANDROID_SURFACE_RESULT_NOT_VALID;  
  126.     }  
  127.     if (sSurface->lock(&surfaceInfo) < 0) {  
  128.         return ANDROID_SURFACE_RESULT_COULDNT_LOCK;  
  129.     }  
  130.       
  131.     if(prepareSurfaceBitmap(&surfaceInfo) < 0) {  
  132.         return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_SURFACE;  
  133.     }  
  134.       
  135.     doUpdateSurface();  
  136.       
  137.     if (sSurface->unlockAndPost() < 0) {  
  138.         return ANDROID_SURFACE_RESULT_COULDNT_UNLOCK_AND_POST;  
  139.     }  
  140.     return ANDROID_SURFACE_RESULT_SUCCESS;  
  141. }  
  142.   
  143. int AndroidSurface_unregister() {  
  144.     __android_log_print(ANDROID_LOG_INFO, TAG, "unregistering video surface");  
  145.     __android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");  
  146.     return ANDROID_SURFACE_RESULT_SUCCESS;  
  147. }  


video/jni/Android.mk (注意目录结构)
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. # our source files  
  5. #  
  6. LOCAL_SRC_FILES:= \  
  7.     surface.cpp  
  8.   
  9. LOCAL_SHARED_LIBRARIES := \  
  10.     libskia \  
  11.         libsurfaceflinger_client \  
  12.         libutils \  
  13.     liblog  
  14.   
  15. LOCAL_C_INCLUDES += \  
  16.     $(JNI_H_INCLUDE) \  
  17.     external/skia/src/core \  
  18.     external/skia/include/core \  
  19.     frameworks/base/include \  
  20.     frameworks/base/native/include  
  21.   
  22. # Optional tag would mean it doesn't get installed by default  
  23. LOCAL_MODULE_TAGS := optional  
  24.   
  25. LOCAL_PRELINK_MODULE := false  
  26.   
  27. LOCAL_MODULE:= libjnivideo  
  28.   
  29. include $(BUILD_SHARED_LIBRARY)  

/audio/jni/audiotrack.cpp(注意目录结构)
  1. /* 
  2.  * Copyright (C) 2009 The Android Open Source Project 
  3.  * 
  4.  * Licensed under the Apache License, Version 2.0 (the "License"); 
  5.  * you may not use this file except in compliance with the License. 
  6.  * You may obtain a copy of the License at 
  7.  * 
  8.  *      http://www.apache.org/licenses/LICENSE-2.0 
  9.  * 
  10.  * Unless required by applicable law or agreed to in writing, software 
  11.  * distributed under the License is distributed on an "AS IS" BASIS, 
  12.  * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 
  13.  * See the License for the specific language governing permissions and 
  14.  * limitations under the License. 
  15.  */  
  16.   
  17. #include <android/audiotrack.h>  
  18. #include <utils/Log.h>  
  19. #include <media/AudioTrack.h>  
  20. #include <media/AudioSystem.h>  
  21. #include <utils/Errors.h>  
  22.   
  23. #include <binder/MemoryHeapBase.h>  
  24. #include <binder/MemoryBase.h>  
  25.   
  26. #define TAG "AudioTrackWrapper"  
  27.   
  28. using namespace android;  
  29.   
  30. //struct audiotrack_fields_t {  
  31. static AudioTrack*                        track;  
  32. //sp<MemoryHeapBase>                    memHeap;  
  33. //sp<MemoryBase>                        memBase;  
  34. //};  
  35. //static struct audiotrack_fields_t audio;  
  36.   
  37. static AudioTrack* getNativeAudioTrack(JNIEnv* env, jobject jaudioTrack) {  
  38.     jclass clazz = env->FindClass("android/media/AudioTrack");  
  39.     jfieldID field_track = env->GetFieldID(clazz, "mNativeTrackInJavaObj""I");  
  40.     if(field_track == NULL) {  
  41.         return NULL;  
  42.     }  
  43.     return (AudioTrack *) env->GetIntField(jaudioTrack, field_track);  
  44. }  
  45.   
  46. /* 
  47. static bool allocSharedMem(int sizeInBytes) { 
  48.     memHeap = new MemoryHeapBase(sizeInBytes); 
  49.     if (memHeap->getHeapID() < 0) { 
  50.         return false; 
  51.     } 
  52.     memBase = new MemoryBase(memHeap, 0, sizeInBytes); 
  53.     return true; 
  54. } 
  55. */  
  56.   
  57. int AndroidAudioTrack_register() {  
  58.     __android_log_print(ANDROID_LOG_INFO, TAG, "registering audio track");  
  59.     track = new AudioTrack();  
  60.     if(track == NULL) {  
  61.          return ANDROID_AUDIOTRACK_RESULT_JNI_EXCEPTION;  
  62.     }  
  63.     __android_log_print(ANDROID_LOG_INFO, TAG, "registered");  
  64.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  65. }  
  66.   
  67. int AndroidAudioTrack_start() {  
  68.     //__android_log_print(ANDROID_LOG_INFO, TAG, "starting audio track");  
  69.     if(track == NULL) {  
  70.         return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;  
  71.     }  
  72.     track->start();  
  73.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  74. }  
  75.   
  76. int AndroidAudioTrack_set(int streamType,  
  77.                           uint32_t sampleRate,  
  78.                           int format,  
  79.                           int channels) {  
  80.     if(track == NULL) {  
  81.         return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;  
  82.     }  
  83.       
  84.     __android_log_print(ANDROID_LOG_INFO, TAG, "setting audio track");  
  85.       
  86.     status_t ret = track->set(streamType,   
  87.                               sampleRate,   
  88.                               format,   
  89.                               channels,   
  90.                               0,   
  91.                               0,  
  92.                               0,   
  93.                               0,  
  94.                               false);  
  95.       
  96.     if (ret != NO_ERROR) {  
  97.         return ANDROID_AUDIOTRACK_RESULT_ERRNO;  
  98.     }  
  99.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  100. }  
  101.   
  102. int AndroidAudioTrack_flush() {  
  103.     if(track == NULL) {  
  104.         return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;  
  105.     }  
  106.     track->flush();  
  107.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  108. }  
  109.   
  110. int AndroidAudioTrack_stop() {  
  111.     if(track == NULL) {  
  112.         return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;  
  113.     }  
  114.     track->stop();  
  115.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  116. }  
  117.   
  118. int AndroidAudioTrack_reload() {  
  119.     if(track == NULL) {  
  120.         return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;  
  121.     }  
  122.     if(track->reload() != NO_ERROR) {  
  123.         return ANDROID_AUDIOTRACK_RESULT_ERRNO;  
  124.     }  
  125.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  126. }  
  127.   
  128. int AndroidAudioTrack_unregister() {  
  129.     __android_log_print(ANDROID_LOG_INFO, TAG, "unregistering audio track");  
  130.     if(!track->stopped()) {  
  131.         track->stop();  
  132.     }  
  133.     //memBase.clear();  
  134.     //memHeap.clear();  
  135.     free(track);  
  136.     //track = NULL;  
  137.     __android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");  
  138.     return ANDROID_AUDIOTRACK_RESULT_SUCCESS;  
  139. }  
  140.   
  141. int AndroidAudioTrack_write(void *buffer, int buffer_size) {  
  142.     // give the data to the native AudioTrack object (the data starts at the offset)  
  143.     ssize_t written = 0;  
  144.     // regular write() or copy the data to the AudioTrack's shared memory?  
  145.     if (track->sharedBuffer() == 0) {  
  146.         written = track->write(buffer, buffer_size);  
  147.     } else {  
  148.         // writing to shared memory, check for capacity  
  149.         if ((size_t)buffer_size > track->sharedBuffer()->size()) {  
  150.             __android_log_print(ANDROID_LOG_INFO, TAG, "buffer size was too small");  
  151.             buffer_size = track->sharedBuffer()->size();  
  152.         }  
  153.         memcpy(track->sharedBuffer()->pointer(), buffer, buffer_size);  
  154.         written = buffer_size;  
  155.     }  
  156.     return written;  
  157. }  

/audio/jni/Android.mk(注意目录结构)
  1. LOCAL_PATH:= $(call my-dir)  
  2. include $(CLEAR_VARS)  
  3.   
  4. # our source files  
  5. #  
  6. LOCAL_SRC_FILES:= \  
  7.     audiotrack.cpp  
  8.   
  9. LOCAL_SHARED_LIBRARIES := \  
  10.     libbinder \  
  11.         libmedia \  
  12.         libutils \  
  13.     liblog  
  14.   
  15. LOCAL_C_INCLUDES += \  
  16.     $(JNI_H_INCLUDE) \  
  17.     frameworks/base/include \  
  18.     frameworks/base/native/include  
  19.   
  20. # Optional tag would mean it doesn't get installed by default  
  21. LOCAL_MODULE_TAGS := optional  
  22.   
  23. LOCAL_PRELINK_MODULE := false  
  24.   
  25. LOCAL_MODULE:= libjniaudio  
  26.   
  27. include $(BUILD_SHARED_LIBRARY) 

=========================================================

备注:关于测试时,Android虚拟机与PC上的文件交互可以用Adb Shell 实现(例如Adb push/pull等等)。


  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 1
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值