http://blog.csdn.net/moruite/archive/2011/04/06/6305944.aspx
解决Android平台移植ffmpeg的一揽子问题
”
havlenapetr 最新的ffmpeg可以直接编译通过,不过有个bug,就是播放完毕后,video_decoder线程不能退出,主要原因是阻塞在packet队列的Queue->get函数里,这里处理逻辑需要考虑两种情况:
1. AVPacket队列里没有音视频帧数据时,解码线程挂起进入等待状态,有新的AVPacket加入队列时激活解码线程
2. 如果文件读取到末尾,Queue->get直接返回-1,解码线程发现队列空时直接退出
“
havlenapetr/FFMpeg 下载地址
https://github.com/havlenapetr/FFMpeg
注意察看issues和文档更新说明!! 目前更新日期2011/5/25
++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
步骤一.
http://www.tlightsky.com/?p=55001
首先从git里面拖出debug分支的代码,时间是【2010年 11月 26日 星期五 22:48:41 CST】,注意目前的代码里面是
有bug的(在Vector里面),所以我们回滚两个周期
git reset –hard HEAD~2(就是在他加入Vector之前,这个可以看git log)
ok,现在我们尝试编译,1. 在 ( "repair bulding process for everyone - you must build in ndkr5")ndkr5根目录执行ndk-build。 具体指令为/usr/local/...ndk../ndk-bulid NDK_PROJECT_PATH=/.../havlenapetr-FFMpeg (下面的目录为jni !!!会在同一级内生成lib,obj文件夹) 注意:此时欲生成libffmpeg_jni.so 但需要如下的两个so文件:
报错,发现没有obj/local/armeabi/libjniaudio.so
git checkout libs/armeabi/*
cp libs/armeabi/* obj/local/armeabi/
拷贝havlenapetr同学为我们编译好的两个so文件(这个是区分版本的!下面会说明)。
完整指令(针对 Android NDK r5 &Android 2.2 ,整个编译时间大概10多分钟):
/usr/local/android-ndk-r5/ndk-build NDK_PROJECT_PATH=/.../havlenapetr-FFMpeg NDK_TOOLCHAIN=arm-eabi-4.4.0 NDK_PLATFORM =android-8
先别急着编译
现在,我们在再修改
jni/jni/Android.mk
ifeq ($(IN_NDK),true)
LOCAL_LDLIBS := -llog
else
LOCAL_PRELINK_MODULE := false
LOCAL_SHARED_LIBRARIES := liblog
endif
改为
—————————————————————-
好了现在你可以编译了~~
步骤二:
2.写一个(或者从其他项目拷贝)default.properties.( 里面其实没多少内容,只是定义默认版本貌似 :# Project target. target=android-9)
重启eclipse ,即可看到build path-自动添加了system library Android2.3。(之前不要手动添加android包,否则此时会弹出“Dx1 error; abortingConversion to Dalvik format failed with error 1错误”即有重复包)
步骤三:
接上,然后
3.自 动 会生成gen文件夹, 里面有R.java
补充1:android R.java 文件出错怎么办
1.先检查你的代码,代码有误,R.java文件是生成不了的
2. 把Project ----> Build Automatically那个选项勾上,让工程每次修改完后都会自动编译,这样R.java会是最新的(这时候程序有误,不会生成)
3. Project ---> Clean,点开后,选中你的工程,然后OK,会Reset你的工程设置,然后重新Build一下,这样三步下来,就能搞定你的问题了。
补充2: Android R.java问题汇总
1. The type R is already defined. (很多时候我们在导入其他人的程序的时候,会遇到这个错误)
通常在project里有两个R.java,一个在src,一个在gen,通常删除src里那个保留gen里的就OK
2.R.java无法修改
R.java是ATD自行产生的,ID号都是唯一的,如果不熟悉,最好是不要动这个里面的东西,这个类里面通常定义的都是project的一些resource信息,attr、drawable、id、raw、layout、string以及xml等
如果发现R.java没法修改,要去看你的配置文件,比如xml等是否有错误。例如一个图片资源文件,如果我没有放在res/drawtable下时,R.java是没法修改的,当你把这个资源拷到res/drawtable下后,R.java就可打开了。
2.R.java不能自动更新
1)是你的project配置有问题
XML格式错误等等
2) 设置的问题
i. 打开ECLIPSE的Poject-->Build Automaticaly ,把它勾起来,以后就会自动给你维护R.java
ii. 或者右击你的project,Android tools-->Fix project properties
ii. 再不行,同样打开eclipse的project-->clean,点下会reset你的project的配置的,然后再rebuild下你的project
以上是本人之前学习时遇到的,如有表达不好的,望斧正,后续继续更新
步骤四:
此时,仍会报错:
- The type new MediaController.MediaPlayerControl(){} must implement the inherited abstract method
MediaController.MediaPlayerControl.canSeekForward()
- The type new MediaController.MediaPlayerControl(){} must implement the inherited abstract method
MediaController.MediaPlayerControl.canSeekBackward()
- The type new MediaController.MediaPlayerControl(){} must implement the inherited abstract method MediaController.MediaPlayerControl.canPause()
fixed as: add unimplemented methods
4.实现相应的接口(内容无所谓)
添加如下代码:
public boolean canSeekForward() {
return false;
}
public boolean canSeekBackward() {
return false;
}
public boolean canPause() {
return false;
}
参考官网说明 公有方法: http://www.zuiniuwang.com/doc/reference/android/widget/MediaController.MediaPlayerControl.html
5. 重新编译使用的 libjnjvideo.so,libjniaudio.so的版本!!!(转向步骤1)
自带libjnjvideo.so,libjniaudio.so 是Froyo(2.2) 版的。如果是其他版本,需要自己编译Echair(2.1)or Gingerbread(2.3 对应api-9).
用eclipse build(即第一步)之前 ,要把libjnjvidoe.so libjniaudio.so copy 到
libs/armeabi/ 和 obj/local/armeabi/(两个目录下)
如何得到呢?如果是2.2 用自带的即可。这对2.3,
问题:libjniaudio.so、libjnivideo.so 怎么编译的?
回答: these two libs are
wrappers to android native AudioTrack and surface flinger so they couldn't be build in ndk. So I have build them in android build system and copy them into my ffmpeg project
通过nm命令,可以看到,libjniaudio.so主要提供了AudioTrack相关接口,libjnivideo.so主要提供surface相关接口。这些东西包含在android源码下,必须在android build 环境中编译。
如何编译?哥终于搞出来了!
两篇极其重要参考文章:
2. 上面某一留言人的博客,讲解如何编译两个so文件
其实,归根结底还是havlenapetr的网站上的issues 2,3等等
下面开始讲我的成功经历:
首先:
是要编译libjnivideo.so和libjniaudio.so 这两个so因为不同的版本或者修改过的framework是不一样的,所以需要在android源码中编译出自己的so。主要关注 framework/base目录 具体参考havlenapetr给出的Android2.2的目录结构
https://github.com/havlenapetr/android_frameworks_base
去上面网址base/native下的内容补充到android2.3的对应目录下。所做改变为增加了surface.cpp&h,audiotrack.cpp &h 及相应的mk文件。
(如果是想在2.1下 还要修改相应的头文件等(android的系统文件改变的原因)详见:https://github.com/havlenapetr/FFMpeg/issues/3)
添加 libjniaudio.so and libjnivideo.so to the end of myandroid/build/core/prelink-linux-arm.map
然后重新编译整个android系统...漫长的 一个小时20分钟(80分钟)。。。(以后开发调试可肿麽办)
then I get the libjniaudio.so and libjnivideo.so in out/target/product/generic/obj/lib for Gingerbread.
同时也要把刚才两个编译出来的so放到obj/local/armeabi目录下,之后就可以了编译出libffmpeg_jni.so.
最后也要把刚才两个so放到libs/armeabi目录下。要不然运行的时候找不到。
播放的时候太卡了,声音也没有同步。
/
此编译过程参考文献为:
1.
编译havlenapetr的ffmpeg工程播放视频
http://wingjang.blog.163.com/blog/static/47913442201112161310334/
2. Compile havlenapetr / FFMpeg for android
http://hi.baidu.com/eefolks/blog/item/e0329e4682859129cefca351.html
3. Compile android source code on Fedora 13
http://hi.baidu.com/eefolks/blog/item/9b195fc5f722dc149c163db1.html
+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
思考?
1. 只用sdk 如何编译: 不用eclipse的话
2.下面列出几个非常不错值得关注的网站!(跟本次内容不一定相关)
1. http://www.tlightsky.com/
eg.
如何移植Android源码里面的东西到NDK 十二月 23rd, 2010首先,有几点不一样的地方就是,在NDK中,
需要在LOCAL_C_INCLUDES中加入
core/include
system/include
这两个依赖的头文件。
首先,针对android.media.MediaPlayer进行分析。
里面有很多native代码,我们找到native_setup这个jni调用,就可以找到整个框架的入口。
我们查看
android_media_MediaPlayer_native_setup@framworks/base/media/jni/android_media_MediaPlayer.cpp
01
static
void
android_media_MediaPlayer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this)
02
{
03
LOGV(
"native_setup"
);
04
sp<MediaPlayer> mp =
new
MediaPlayer();
05
if
(mp == NULL) {
06
jniThrowException(env,
"java/lang/RuntimeException"
,
"Out of memory"
);
07
return
;
08
}
09
10
11
sp<JNIMediaPlayerListener> listener =
new
JNIMediaPlayerListener(env, thiz, weak_this);
12
mp->setListener(listener);
13
14
15
setMediaPlayer(env, thiz, mp);
16
}
从这里的这段代码我们可以看到,android在这里实例化了一个变量mp:MediaPlayer。
并且为其设置了一个listener:JNIMediaPlayerListener
在后面我们会看到对mp的调用,现在让我们先看看MediaPlayer是什么东东。
MediaPlayer@framworks/base/include/media/mediaplayer.h
MediaPlayer@framworks/base/media/libmedia/mediaplayer.cpp
在这里我们终于找到了MediaPlayer:BnMediaPlayerClient:IMediaPlayerClient
原来他也是对Bind Native的一个封装,而他本身提供了很多方法用于访问,包括start等。下面是start的cpp代码:
01
status_t MediaPlayer::start()
02
{
03
LOGV(
"start"
);
04
Mutex::Autolock _l(mLock);
05
if
(mCurrentState & MEDIA_PLAYER_STARTED)
06
return
NO_ERROR;
07
if
( (mPlayer != 0) && ( mCurrentState & ( MEDIA_PLAYER_PREPARED |
08
MEDIA_PLAYER_PLAYBACK_COMPLETE | MEDIA_PLAYER_PAUSED ) ) ) {
09
mPlayer->setLooping(mLoop);
10
mPlayer->setVolume(mLeftVolume, mRightVolume);
11
mCurrentState = MEDIA_PLAYER_STARTED;
12
status_t ret = mPlayer->start();
13
if
(ret != NO_ERROR) {
14
mCurrentState = MEDIA_PLAYER_STATE_ERROR;
15
}
else
{
16
if
(mCurrentState == MEDIA_PLAYER_PLAYBACK_COMPLETE) {
17
LOGV(
"playback completed immediately following start()"
);
18
}
19
}
20
return
ret;
21
}
22
LOGE(
"start called in state %d"
, mCurrentState);
23
return
INVALID_OPERATION;
24
}
原来这里又调用了mPlayer:sp<IMediaPlayer>
从这里我们发现最终的服务,还是由IMediaPlayer这个东西提供的,而IMediaPlayer@framworks/base/include/media/IMediaPlayer.h
实际上是如下定义的一个类,它继承了IInterface@framworks/base/include/binder/IInterface.h这个类(注意虽然名字是Interface,但是它确实是个类!:-))
01
class
IMediaPlayer:
public
IInterface
02
{
03
public
:
04
DECLARE_META_INTERFACE(MediaPlayer);
05
06
virtual
void
disconnect() = 0;
07
08
virtual
status_t setVideoSurface(
const
sp<ISurface>& surface) = 0;
09
virtual
status_t prepareAsync() = 0;
10
virtual
status_t start() = 0;
11
virtual
status_t stop() = 0;
12
virtual
status_t pause() = 0;
13
virtual
status_t isPlaying(
bool
* state) = 0;
14
virtual
status_t seekTo(
int
msec) = 0;
15
virtual
status_t getCurrentPosition(
int
* msec) = 0;
16
virtual
status_t getDuration(
int
* msec) = 0;
17
virtual
status_t reset() = 0;
18
virtual
status_t setAudioStreamType(
int
type) = 0;
19
virtual
status_t setLooping(
int
loop) = 0;
20
virtual
status_t setVolume(
float
leftVolume,
float
rightVolume) = 0;
21
virtual
status_t invoke(
const
Parcel& request, Parcel *reply) = 0;
22
virtual
status_t setMetadataFilter(
const
Parcel& filter) = 0;
23
virtual
status_t getMetadata(
bool
update_only,
24
bool
apply_filter,
25
Parcel *metadata) = 0;
26
};
为了弄清楚,在什么地方产生的mPlayer,我转而分析MediaPlayerService@framworks/base/media/libmediaplayerservice/MediaPlayerService.h
其中有如下代码
1
virtual
sp<IMediaRecorder> createMediaRecorder(pid_t pid);
2
void
removeMediaRecorderClient(wp<MediaRecorderClient> client);
3
virtual
sp<IMediaMetadataRetriever> createMetadataRetriever(pid_t pid);
4
5
6
virtual
sp<IMediaPlayer> create(pid_t pid,
const
sp<IMediaPlayerClient>& client,
const
char
* url);
7
virtual
sp<IMediaPlayer> create(pid_t pid,
const
sp<IMediaPlayerClient>& client,
int
fd, int64_t offset, int64_t length);
原来在这个地方会创建你sp<IMediaPlayer>对象。
2. http://blog.sina.com.cn/s/blog_639fb8350100mm5b.html
Android创建SD卡镜像文件:
进入SDK安装目录的tools目录下,执行以下命令:
$./mksdcard -l sdcard <size>M<path>/<filename>.img
以带SD卡的方式启动Android模拟器:
进入SDK安装目录的tools目录下,执行以下命令:
$./emulator -sdcard <sd image filename> -avd <avdname>
Android的adb工具的使用:
该工具主要用于与模拟器或真机交互。
位置:<android sdk path>/tools/adb !!!Grant: 现在sdk r5应该为<android sdk path>/platform-tools/adb
常用命令:
1、安装adk程序:
$./adb install <apk file name>
2、进入shell
$./adb shell
3、执行一条shell命令:
$./adb shell <command>
4、删除:
首先进入shell:
$./adb shell
再执行删除操作:
#rm [-r] <file name>
-r参数:用于删除文件夹。
5、拷贝文件
$./adb push <local file name><target path>
$./adb pull <target file name><local path>
6、取得ID及序列号:
$./adb get-product
$./adb get-serialno
7、提高执行权限:
$./adb remount
Android开发一些事项及Eclipse使用
Android添加source到Eclipse:
1、将sdk源代码目录文件夹 sources 拷贝到sdk的以下目录:
platforms/<sdk version name>/
2、在eclipse中添加一个用户库,并将android.jar加入到该库中。
3、将该自定义库的 source 指定到 sources 文件夹。
4、移除工程自带的Android库,加入自定义库。
5、刷新工程,这时可以查看skd的source了。
6、将引用的自定义库删除。
7、在项目属性的Android项中重新选择对应的 Target。
8、项目右键 -> Android Tools -> Fix Project Properties。
3. 如何使用Android SDK开发Android应用
http://hi.baidu.com/324280429/blog/item/4c33e9a4da1bd4fc9052eeda.html/cmtid/4f71a7065eed117503088164
从官方文档和实践可以总结出几点:
1、可以使用eclipse来编辑JAVA程序、检查错误(主要是类库包含和语法方面),但是不能在eclipse上编译运行android源码,还是得在shell中make(或mm或mmm)
2、android源码文件夹里提供有一些eclipse配置文件,
.claapath:eclipse工程的配置文件,方便我们直接把android源码相应的文件和JAVA包导入工程
android-formatting.xml和android.importorder:这个很重要,主要是用来规范我们的编码风格,更容易使我们的代码风格一致
3、把android源码作为一个工程导入eclipse时,必须注意两点
1)、新建的工程必须是java project,不能是android project,否则会破坏android源码(一般是多添加文件/文件夹)
2)、导入前最好检查.classpath里的文件在android源码中是否有相应的文件(文件夹),否则也会破坏android源码(一般是多添加文件/文件夹)
总的来说:
1、用eclipse来编辑代码、检查错误
2、不在eclipse上编译、运行android源码程序,只能在命令行通过make(或mm或mmm)编译android源码
3、可以在eclipse上调试android源码程序(原理:eclipse通过ddms服务器在emulator上进行调试),并可以单步调试、断点调试。
4、需要调试的程序把它从/system/app/移除,安装到data/app下,这样更方便
5、安装、卸载程序通过adb push 和adb shell rm更方便