【移植havlenapetr】的【ffmpeg】-->【Android播放器】

重点文章(包括文章后的评论):

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

改为

LOCAL_LDLIBS := -llog

—————————————————————-
好了现在你可以编译了~~

步骤二:

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 环境中编译。
如何编译?哥终于搞出来了!
两篇极其重要参考文章:
1. 经典文章,尤其留意后面的一系列留言及留言人的博客http://blog.csdn.net/moruite/archive/2011/04/06/6305944.aspx
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

Android源码分析之MediaFramework

 如何移植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     // create new listener and give it to MediaPlayer
11     sp<JNIMediaPlayerListener> listener = new JNIMediaPlayerListener(env, thiz, weak_this);
12     mp->setListener(listener);
13  
14     // Stow our new C++ MediaPlayer in an opaque field in the Java object.
15     setMediaPlayer(env, thiz, mp);
16 }

 

 

 

从这里的这段代码我们可以看到,android在这里实例化了一个变量mp:MediaPlayer

并且为其设置了一个listenerJNIMediaPlayerListener

 

在后面我们会看到对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 // House keeping for media player clients
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更方便

 

 

查看评论
6楼  ALLENJIAO 6天前 15:59发表 [回复]
楼主能不能把你能正常编译的工程文件
上传资源里啊

或发我邮箱372518773@qq.com也行 
谢谢啦
5楼  wujxiaoz 2011-11-26 21:20发表 [回复]
请问下楼主,用这个如何播放m4v格式的视频?
4楼  mythouwl 2011-11-08 18:34发表 [回复]
楼主你好,我把工程编译出来,发现运行的时候有些只有声音没有画面,有些有画面,但是都是花屏的。请问这个是什么原因?
Re:  scut1135 2011-11-15 00:20发表 [回复]
回复mythouwl:这个原因我也出现过,可参考http://blog.csdn.net/moruite/article/details/6305944 这篇文章给出了一些分析 欢迎多交流讨论~
3楼  blueloading 2011-11-04 16:09发表 [回复]
楼主有没有用你的播放器播放.ts文件,我的在2.2下播放特别卡,可能是什么原因?
另外,能否提供2.3的audio和video so文件给我?谢谢了!
我的邮件blueloading@163.com
Re:  scut1135 2011-11-06 01:24发表 [回复]
回复blueloading:软解码,如果码率太高会有些卡
可以。
Re:  blueloading 2011-11-07 13:55发表 [回复]
回复scut1135:但是同样的文件用mobo播放没有任何问题。
请把那两个.so文件发到我的邮箱:blueloading@163.com
Re:  scut1135 2011-11-15 00:21发表 [回复]
回复blueloading:好,我待会切换到Ubuntu下发给你~
2楼  troy_synnex 2011-09-24 13:29发表 [回复]
楼主你好,能发一份编译后的so给我吗,不胜感激,
email: java-koma@163.com
Re:  scut1135 2011-10-31 19:44发表 [回复]
回复troy_synnex:不好意思 刚看到。你还需要吗?
1楼  kanaiji0808 2011-06-11 15:24发表 [回复] [引用] [举报]
Re:  scut1135 2011-06-11 16:32发表 [回复]
回复 kanaiji0808: 有问题一起讨论。。。
  • 0
    点赞
  • 4
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值