Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2
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的相关工具
- git clone https://github.com/havlenapetr/FFMpeg.git
3、利用NDK编译生成so库
下载后直接在havlenapetr-FFMpeg-7c27aa2的顶级目录下执行
- $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工程里面使用就会有以下错误信息。
- 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(注意目录结构)
- /*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
- #include <android/surface.h>
- #include <surfaceflinger/Surface.h>
- #include <utils/Log.h>
- #include <SkBitmap.h>
- #include <SkCanvas.h>
- #define TAG "SurfaceWrapper"
- using namespace android;
- static Surface* sSurface;
- static SkBitmap sBitmapClient;
- static SkBitmap sBitmapSurface;
- static Surface* getNativeSurface(JNIEnv* env, jobject jsurface) {
- jclass clazz = env->FindClass("android/view/Surface");
- jfieldID field_surface = env->GetFieldID(clazz, "mNativeSurface", "I");
- if(field_surface == NULL) {
- return NULL;
- }
- return (Surface *) env->GetIntField(jsurface, field_surface);
- }
- static int initBitmap(SkBitmap *bitmap, int format, int width, int height, bool allocPixels) {
- switch (format) {
- case PIXEL_FORMAT_RGBA_8888:
- bitmap->setConfig(SkBitmap::kARGB_8888_Config, width, height);
- break;
- case PIXEL_FORMAT_RGBA_4444:
- bitmap->setConfig(SkBitmap::kARGB_4444_Config, width, height);
- break;
- case PIXEL_FORMAT_RGB_565:
- bitmap->setConfig(SkBitmap::kRGB_565_Config, width, height);
- break;
- case PIXEL_FORMAT_A_8:
- bitmap->setConfig(SkBitmap::kA8_Config, width, height);
- break;
- default:
- bitmap->setConfig(SkBitmap::kNo_Config, width, height);
- break;
- }
- if(allocPixels) {
- bitmap->setIsOpaque(true);
- //-- alloc array of pixels
- if(!bitmap->allocPixels()) {
- return -1;
- }
- }
- return 0;
- }
- int AndroidSurface_register(JNIEnv* env, jobject jsurface) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "registering video surface");
- sSurface = getNativeSurface(env, jsurface);
- if(sSurface == NULL) {
- return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
- }
- __android_log_print(ANDROID_LOG_INFO, TAG, "registered");
- return ANDROID_SURFACE_RESULT_SUCCESS;
- }
- int AndroidSurface_getPixels(int width, int height, void** pixels) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "getting surface's pixels %ix%i", width, height);
- if(sSurface == NULL) {
- return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
- }
- if(initBitmap(&sBitmapClient, PIXEL_FORMAT_RGB_565, width, height, true) < 0) {
- return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_CLIENT;
- }
- *pixels = sBitmapClient.getPixels();
- __android_log_print(ANDROID_LOG_INFO, TAG, "getted");
- return ANDROID_SURFACE_RESULT_SUCCESS;
- }
- static void doUpdateSurface() {
- SkCanvas canvas(sBitmapSurface);
- SkRect surface_sBitmapClient;
- SkRect surface_sBitmapSurface;
- SkMatrix matrix;
- surface_sBitmapSurface.set(0, 0, sBitmapSurface.width(), sBitmapSurface.height());
- surface_sBitmapClient.set(0, 0, sBitmapClient.width(), sBitmapClient.height());
- matrix.setRectToRect(surface_sBitmapClient, surface_sBitmapSurface, SkMatrix::kFill_ScaleToFit);
- canvas.drawBitmapMatrix(sBitmapClient, matrix);
- }
- static int prepareSurfaceBitmap(Surface::SurfaceInfo* info) {
- if(initBitmap(&sBitmapSurface, info->format, info->w, info->h, false) < 0) {
- return -1;
- }
- sBitmapSurface.setPixels(info->bits);
- return 0;
- }
- int AndroidSurface_updateSurface() {
- static Surface::SurfaceInfo surfaceInfo;
- if(sSurface == NULL) {
- return ANDROID_SURFACE_RESULT_JNI_EXCEPTION;
- }
- if (!Surface::isValid (sSurface)){
- return ANDROID_SURFACE_RESULT_NOT_VALID;
- }
- if (sSurface->lock(&surfaceInfo) < 0) {
- return ANDROID_SURFACE_RESULT_COULDNT_LOCK;
- }
- if(prepareSurfaceBitmap(&surfaceInfo) < 0) {
- return ANDROID_SURFACE_RESULT_COULDNT_INIT_BITMAP_SURFACE;
- }
- doUpdateSurface();
- if (sSurface->unlockAndPost() < 0) {
- return ANDROID_SURFACE_RESULT_COULDNT_UNLOCK_AND_POST;
- }
- return ANDROID_SURFACE_RESULT_SUCCESS;
- }
- int AndroidSurface_unregister() {
- __android_log_print(ANDROID_LOG_INFO, TAG, "unregistering video surface");
- __android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");
- return ANDROID_SURFACE_RESULT_SUCCESS;
- }
video/jni/Android.mk(注意目录结构)
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- # our source files
- #
- LOCAL_SRC_FILES:= \
- surface.cpp
- LOCAL_SHARED_LIBRARIES := \
- libskia \
- libsurfaceflinger_client \
- libutils \
- liblog
- LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- external/skia/src/core \
- external/skia/include/core \
- frameworks/base/include \
- frameworks/base/native/include
- # Optional tag would mean it doesn't get installed by default
- LOCAL_MODULE_TAGS := optional
- LOCAL_PRELINK_MODULE := false
- LOCAL_MODULE:= libjnivideo
- include $(BUILD_SHARED_LIBRARY)
/audio/jni/audiotrack.cpp(注意目录结构)
- /*
- * Copyright (C) 2009 The Android Open Source Project
- *
- * 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.
- */
- #include <android/audiotrack.h>
- #include <utils/Log.h>
- #include <media/AudioTrack.h>
- #include <media/AudioSystem.h>
- #include <utils/Errors.h>
- #include <binder/MemoryHeapBase.h>
- #include <binder/MemoryBase.h>
- #define TAG "AudioTrackWrapper"
- using namespace android;
- //struct audiotrack_fields_t {
- static AudioTrack* track;
- //sp<MemoryHeapBase> memHeap;
- //sp<MemoryBase> memBase;
- //};
- //static struct audiotrack_fields_t audio;
- static AudioTrack* getNativeAudioTrack(JNIEnv* env, jobject jaudioTrack) {
- jclass clazz = env->FindClass("android/media/AudioTrack");
- jfieldID field_track = env->GetFieldID(clazz, "mNativeTrackInJavaObj", "I");
- if(field_track == NULL) {
- return NULL;
- }
- return (AudioTrack *) env->GetIntField(jaudioTrack, field_track);
- }
- /*
- static bool allocSharedMem(int sizeInBytes) {
- memHeap = new MemoryHeapBase(sizeInBytes);
- if (memHeap->getHeapID() < 0) {
- return false;
- }
- memBase = new MemoryBase(memHeap, 0, sizeInBytes);
- return true;
- }
- */
- int AndroidAudioTrack_register() {
- __android_log_print(ANDROID_LOG_INFO, TAG, "registering audio track");
- track = new AudioTrack();
- if(track == NULL) {
- return ANDROID_AUDIOTRACK_RESULT_JNI_EXCEPTION;
- }
- __android_log_print(ANDROID_LOG_INFO, TAG, "registered");
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_start() {
- //__android_log_print(ANDROID_LOG_INFO, TAG, "starting audio track");
- if(track == NULL) {
- return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
- }
- track->start();
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_set(int streamType,
- uint32_t sampleRate,
- int format,
- int channels) {
- if(track == NULL) {
- return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
- }
- __android_log_print(ANDROID_LOG_INFO, TAG, "setting audio track");
- status_t ret = track->set(streamType,
- sampleRate,
- format,
- channels,
- 0,
- 0,
- 0,
- 0,
- false);
- if (ret != NO_ERROR) {
- return ANDROID_AUDIOTRACK_RESULT_ERRNO;
- }
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_flush() {
- if(track == NULL) {
- return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
- }
- track->flush();
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_stop() {
- if(track == NULL) {
- return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
- }
- track->stop();
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_reload() {
- if(track == NULL) {
- return ANDROID_AUDIOTRACK_RESULT_ALLOCATION_FAILED;
- }
- if(track->reload() != NO_ERROR) {
- return ANDROID_AUDIOTRACK_RESULT_ERRNO;
- }
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_unregister() {
- __android_log_print(ANDROID_LOG_INFO, TAG, "unregistering audio track");
- if(!track->stopped()) {
- track->stop();
- }
- //memBase.clear();
- //memHeap.clear();
- free(track);
- //track = NULL;
- __android_log_print(ANDROID_LOG_INFO, TAG, "unregistered");
- return ANDROID_AUDIOTRACK_RESULT_SUCCESS;
- }
- int AndroidAudioTrack_write(void *buffer, int buffer_size) {
- // give the data to the native AudioTrack object (the data starts at the offset)
- ssize_t written = 0;
- // regular write() or copy the data to the AudioTrack's shared memory?
- if (track->sharedBuffer() == 0) {
- written = track->write(buffer, buffer_size);
- } else {
- // writing to shared memory, check for capacity
- if ((size_t)buffer_size > track->sharedBuffer()->size()) {
- __android_log_print(ANDROID_LOG_INFO, TAG, "buffer size was too small");
- buffer_size = track->sharedBuffer()->size();
- }
- memcpy(track->sharedBuffer()->pointer(), buffer, buffer_size);
- written = buffer_size;
- }
- return written;
- }
/audio/jni/Android.mk(注意目录结构)
- LOCAL_PATH:= $(call my-dir)
- include $(CLEAR_VARS)
- # our source files
- #
- LOCAL_SRC_FILES:= \
- audiotrack.cpp
- LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libmedia \
- libutils \
- liblog
- LOCAL_C_INCLUDES += \
- $(JNI_H_INCLUDE) \
- frameworks/base/include \
- frameworks/base/native/include
- # Optional tag would mean it doesn't get installed by default
- LOCAL_MODULE_TAGS := optional
- LOCAL_PRELINK_MODULE := false
- LOCAL_MODULE:= libjniaudio
- include $(BUILD_SHARED_LIBRARY)
Android多媒体开发(3)————使用Android NKD编译havlenapetr-FFMpeg-7c27aa2的更多相关文章
- 多媒体开发(7):编译Android与iOS平台的FFmpeg
编译FFmpeg,一个古老的话题,但小程还是介绍一遍,就当记录.之前介绍怎么给视频添加水印时,就已经提到FFmpeg的编译,并且在编译时指定了滤镜的功能. 但是,在手机盛行的时代,读者可能更需要的是能 ...
- Windows下搭建Android NDK开发环境及命令行编译
首先说明本文内的相关安装操作参考<Pro Android C++ with the NDK>一书. 安装 Windows搭建Android NDK开发环境需要安装如下部分(同时需要配置对应 ...
- 【Android 多媒体开发】 MediaPlayer 网络视频播放器
作者 : 万境绝尘 (octopus_truth@163.com) 转载请著名出处 : http://blog.csdn.net/shulianghan/article/details/3889514 ...
- 【Android 应用开发】对Android体系结构的理解--后续会补充
1.最底层_硬件 任何Android设备最底层的硬件包括 显示屏, wifi ,存储设备 等. Android最底层的硬件会根据需要进行裁剪,选择自己需要的硬件. 2.Linux内核层 该层主要对硬件 ...
- [Android Pro] 开发一流的 Android SDK:Fabric SDK 的创建经验
cp from : https://academy.realm.io/cn/posts/oredev-ty-smith-building-android-sdks-fabric/ Ty Smith T ...
- Android采访开发——2.通用Android基础笔试题
注意finddreams博客: http://blog.csdn.net/finddreams/article/details/44219231 正值跳槽的热季.整理一下Android面试中最常考的笔 ...
- IDEA搭建Android wear开发环境,Android wear,I'm comming!
随着google公布了android wear这个东西.然后又有了三星的gear,LG的G watch以及moto 360,苹果由公布了apple watch.未来可能在智能手表行业又有一场战争. 当 ...
- Android多媒体开发-stagefright及AwesomePlayer相关知识梳理
android的多媒体框架中, stagefright其实是AwesomePlayer的代理,就是个皮包公司. status_t StagefrightPlayer::setDataSource( c ...
- Android多媒体开发-- android中OpenMax的实现整体框架
1.android中用openmax来干啥? android中的AwesomePlayer就 是用openmax来做(code)编解码,其实在openmax接口设计中,他不光能用来当编解码.通过他的组 ...
随机推荐
- 洛谷 【P1252】马拉松接力赛
洛谷 [P1252]马拉松接力赛 题目描述 某城市冬季举办环城25km马拉松接力赛,每个代表队有5人参加比赛,比赛要求每个的每名参赛选手只能跑一次,一次至少跑1km.最多只能跑10km,而且每个选手所 ...
- RFID的工作流程
工作流程 1.阅读器通过发射天线发送一定频率的射频信号, 2.当射频卡进入发射天线工作区域时产生感应电流,射频卡获得能量被激活: 3.射频卡将自身编码等信息通过卡内置发送天线发送出去 4.系统接收天线 ...
- 代码生成器实现的Entity,Dao,Service,Controller,JSP神器(含代码附件)
package com.flong.codegenerator; import java.sql.Connection; import java.sql.DatabaseMetaData; impor ...
- js进阶 14-9 ajax事件有哪些
js进阶 14-9 ajax事件有哪些 一.总结 一句话总结:ajax开始时事件.发送时事件,请求完成时事件,请求成功时事件,请求结束时事件,请求错误时事件事件. 1.ajax事件的监听对象是谁? 都 ...
- css的三种表现形式
1.行内样式(内嵌样式):结构的内部,即写在标签内的样式:写在标签的开始部分内部,style属性当中:<标记 style="样式的属性名1:样式的属性值1:属性名2:属性值2:.... ...
- JS学习笔记 - fgm练习 - 限制输入框的字符类型 正则 和 || 或运算符的运用 i++和++i
<script> window.onload = function(){ var aInp = document.getElementsByTagName('input'); var oS ...
- [转]MySQLHelper类
本文转自:http://de.cel.blog.163.com/blog/static/5145123620110181003903/ 类似于SQLHelper,只是这里引用的是MySql.Data类 ...
- css页面滚动条出现时防止页面跳动的方法
大家写页面时应该都遇到过一个问题,尤其是写单页面应用的时候, 在有滚动条页面和没有滚动条页面之间相互跳转时, 你页面的主体内容会向左或者向右抖一下,让强迫症看了很不舒服. 现在就来解救一下强迫症: 方 ...
- LA 3989 - Ladies' Choice 稳定婚姻问题
https://icpcarchive.ecs.baylor.edu/index.php?option=com_onlinejudge&Itemid=8&page=show_probl ...
- (转)高强度密码管理软件KeePass使用详解
转自:http://www.ruancan.com/ 算下来,你接触电脑有多久了?从第一次上网,到今天,你一共申请了多少个网站或者软件的帐号?相信这是一个几乎无人能够回答的问题. 无数人面临着这两个问 ...