Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

时间:2022-03-12 08:38:56

1、

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

2、移植havlenapetr/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目录下。

绿色的是新加入的文件

Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2

需要注意的一点是:

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. #define TAG "SurfaceWrapper"
  22. using namespace android;
  23. static Surface*     sSurface;
  24. static SkBitmap     sBitmapClient;
  25. static SkBitmap     sBitmapSurface;
  26. static Surface* getNativeSurface(JNIEnv* env, jobject jsurface) {
  27. jclass clazz = env->FindClass("android/view/Surface");
  28. jfieldID field_surface = env->GetFieldID(clazz, "mNativeSurface", "I");
  29. if(field_surface == NULL) {
  30. return NULL;
  31. }
  32. return (Surface *) env->GetIntField(jsurface, field_surface);
  33. }
  34. static int initBitmap(SkBitmap *bitmap, int format, int width, int height, bool allocPixels) {
  35. switch (format) {
  36. case PIXEL_FORMAT_RGBA_8888:
  37. bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
  38. break;
  39. case PIXEL_FORMAT_RGBA_4444:
  40. bitmap->setConfig(SkBitmap::kARGB_4444_Config, width, height);
  41. break;
  42. case PIXEL_FORMAT_RGB_565:
  43. bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height);
  44. break;
  45. case PIXEL_FORMAT_A_8:
  46. bitmap->setConfig(SkBitmap::kA8_Config, width, height);
  47. break;
  48. default:
  49. bitmap->setConfig(SkBitmap::kNo_Config, width, height);
  50. break;
  51. }
  52. if(allocPixels) {
  53. bitmap->setIsOpaque(true);
  54. //-- alloc array of pixels
  55. if(!bitmap->allocPixels()) {
  56. return -1;
  57. }
  58. }
  59. return 0;
  60. }
  61. int AndroidSurface_register(JNIEnv* env, jobject jsurface) {
  62. __android_log_print(ANDROID_LOG_INFO, TAG, "registering video surface");
  63. sSurface = getNativeSurface(env, jsurface);
  64. if(sSurface == NULL) {
  65. return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
  66. }
  67. __android_log_print(ANDROID_LOG_INFO, TAG, "registered");
  68. return ANDROID_SURFACE_RESULT_SUCCESS;
  69. }
  70. int AndroidSurface_getPixels(int width, int height, void** pixels) {
  71. __android_log_print(ANDROID_LOG_INFO, TAG, "getting surface's pixels %ix%i", width, height);
  72. if(sSurface == NULL) {
  73. return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
  74. }
  75. if(initBitmap(&sBitmapClient, PIXEL_FORMAT_RGB_565, width, height, true) < 0) {
  76. return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_CLIENT;
  77. }
  78. *pixels = sBitmapClient.getPixels();
  79. __android_log_print(ANDROID_LOG_INFO, TAG, "getted");
  80. return ANDROID_SURFACE_RESULT_SUCCESS;
  81. }
  82. static void doUpdateSurface() {
  83. SkCanvas    canvas(sBitmapSurface);
  84. SkRect      surface_sBitmapClient;
  85. SkRect      surface_sBitmapSurface;
  86. SkMatrix    matrix;
  87. surface_sBitmapSurface.set(0, 0, sBitmapSurface.width(), sBitmapSurface.height());
  88. surface_sBitmapClient.set(0, 0, sBitmapClient.width(), sBitmapClient.height());
  89. matrix.setRectToRect(surface_sBitmapClient, surface_sBitmapSurface, SkMatrix::kFill_ScaleToFit);
  90. canvas.drawBitmapMatrix(sBitmapClient, matrix);
  91. }
  92. static int prepareSurfaceBitmap(Surface::SurfaceInfo* info) {
  93. if(initBitmap(&sBitmapSurface, info->format, info->w, info->h, false) < 0) {
  94. return -1;
  95. }
  96. sBitmapSurface.setPixels(info->bits);
  97. return 0;
  98. }
  99. int AndroidSurface_updateSurface() {
  100. static Surface::SurfaceInfo surfaceInfo;
  101. if(sSurface == NULL) {
  102. return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
  103. }
  104. if (!Surface::isValid (sSurface)){
  105. return ANDROID_SURFACE_RESULT_NOT_VALID;
  106. }
  107. if (sSurface->lock(&surfaceInfo) < 0) {
  108. return ANDROID_SURFACE_RESULT_COULDNT_LOCK;
  109. }
  110. if(prepareSurfaceBitmap(&surfaceInfo) < 0) {
  111. return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_SURFACE;
  112. }
  113. doUpdateSurface();
  114. if (sSurface->unlockAndPost() < 0) {
  115. return ANDROID_SURFACE_RESULT_COULDNT_UNLOCK_AND_POST;
  116. }
  117. return ANDROID_SURFACE_RESULT_SUCCESS;
  118. }
  119. int AndroidSurface_unregister() {
  120. __android_log_print(ANDROID_LOG_INFO, TAG, "unregistering video surface");
  121. __android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");
  122. return ANDROID_SURFACE_RESULT_SUCCESS;
  123. }

video/jni/Android.mk(注意目录结构)

  1. LOCAL_PATH:= $(call my-dir)
  2. include $(CLEAR_VARS)
  3. # our source files
  4. #
  5. LOCAL_SRC_FILES:= \
  6. surface.cpp
  7. LOCAL_SHARED_LIBRARIES := \
  8. libskia \
  9. libsurfaceflinger_client \
  10. libutils \
  11. liblog
  12. LOCAL_C_INCLUDES += \
  13. $(JNI_H_INCLUDE) \
  14. external/skia/src/core \
  15. external/skia/include/core \
  16. frameworks/base/include \
  17. frameworks/base/native/include
  18. # Optional tag would mean it doesn't get installed by default
  19. LOCAL_MODULE_TAGS := optional
  20. LOCAL_PRELINK_MODULE := false
  21. LOCAL_MODULE:= libjnivideo
  22. 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. #include <android/audiotrack.h>
  17. #include <utils/Log.h>
  18. #include <media/AudioTrack.h>
  19. #include <media/AudioSystem.h>
  20. #include <utils/Errors.h>
  21. #include <binder/MemoryHeapBase.h>
  22. #include <binder/MemoryBase.h>
  23. #define TAG "AudioTrackWrapper"
  24. using namespace android;
  25. //struct audiotrack_fields_t {
  26. static AudioTrack*                        track;
  27. //sp<MemoryHeapBase>                    memHeap;
  28. //sp<MemoryBase>                        memBase;
  29. //};
  30. //static struct audiotrack_fields_t audio;
  31. static AudioTrack* getNativeAudioTrack(JNIEnv* env, jobject jaudioTrack) {
  32. jclass clazz = env->FindClass("android/media/AudioTrack");
  33. jfieldID field_track = env->GetFieldID(clazz, "mNativeTrackInJavaObj", "I");
  34. if(field_track == NULL) {
  35. return NULL;
  36. }
  37. return (AudioTrack *) env->GetIntField(jaudioTrack, field_track);
  38. }
  39. /*
  40. static bool allocSharedMem(int sizeInBytes) {
  41. memHeap = new MemoryHeapBase(sizeInBytes);
  42. if (memHeap->getHeapID() < 0) {
  43. return false;
  44. }
  45. memBase = new MemoryBase(memHeap, 0, sizeInBytes);
  46. return true;
  47. }
  48. */
  49. int AndroidAudioTrack_register() {
  50. __android_log_print(ANDROID_LOG_INFO, TAG, "registering audio track");
  51. track = new AudioTrack();
  52. if(track == NULL) {
  53. return ANDROID_AUDIOTRACK_RESULT_JNI_EXCEPTION;
  54. }
  55. __android_log_print(ANDROID_LOG_INFO, TAG, "registered");
  56. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  57. }
  58. int AndroidAudioTrack_start() {
  59. //__android_log_print(ANDROID_LOG_INFO, TAG, "starting audio track");
  60. if(track == NULL) {
  61. return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
  62. }
  63. track->start();
  64. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  65. }
  66. int AndroidAudioTrack_set(int streamType,
  67. uint32_t sampleRate,
  68. int format,
  69. int channels) {
  70. if(track == NULL) {
  71. return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
  72. }
  73. __android_log_print(ANDROID_LOG_INFO, TAG, "setting audio track");
  74. status_t ret = track->set(streamType,
  75. sampleRate,
  76. format,
  77. channels,
  78. 0,
  79. 0,
  80. 0,
  81. 0,
  82. false);
  83. if (ret != NO_ERROR) {
  84. return ANDROID_AUDIOTRACK_RESULT_ERRNO;
  85. }
  86. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  87. }
  88. int AndroidAudioTrack_flush() {
  89. if(track == NULL) {
  90. return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
  91. }
  92. track->flush();
  93. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  94. }
  95. int AndroidAudioTrack_stop() {
  96. if(track == NULL) {
  97. return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
  98. }
  99. track->stop();
  100. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  101. }
  102. int AndroidAudioTrack_reload() {
  103. if(track == NULL) {
  104. return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
  105. }
  106. if(track->reload() != NO_ERROR) {
  107. return ANDROID_AUDIOTRACK_RESULT_ERRNO;
  108. }
  109. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  110. }
  111. int AndroidAudioTrack_unregister() {
  112. __android_log_print(ANDROID_LOG_INFO, TAG, "unregistering audio track");
  113. if(!track->stopped()) {
  114. track->stop();
  115. }
  116. //memBase.clear();
  117. //memHeap.clear();
  118. free(track);
  119. //track = NULL;
  120. __android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");
  121. return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
  122. }
  123. int AndroidAudioTrack_write(void *buffer, int buffer_size) {
  124. // give the data to the native AudioTrack object (the data starts at the offset)
  125. ssize_t written = 0;
  126. // regular write() or copy the data to the AudioTrack's shared memory?
  127. if (track->sharedBuffer() == 0) {
  128. written = track->write(buffer, buffer_size);
  129. } else {
  130. // writing to shared memory, check for capacity
  131. if ((size_t)buffer_size > track->sharedBuffer()->size()) {
  132. __android_log_print(ANDROID_LOG_INFO, TAG, "buffer size was too small");
  133. buffer_size = track->sharedBuffer()->size();
  134. }
  135. memcpy(track->sharedBuffer()->pointer(), buffer, buffer_size);
  136. written = buffer_size;
  137. }
  138. return written;
  139. }

/audio/jni/Android.mk(注意目录结构)

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