首先聲明測試平台為瑞芯微的rk3168,Android4.2.2,Android版本很重要,因為Android4.0和Android4.2.2的代碼有些地方就有差別,并不通用!
首先接到任務不知如何下手,因為我了解中Android啟動時沒有出現過播放視訊的,特别是在啟動動畫之前,Linux企鵝之後,而動畫也是一幅幅的圖檔,根本不是啥視訊如mp4,3gp等!
因為啟動動畫時可以播放聲音,而且上層應用的mediaplayer也可以播放聲音,結合surface可以播放視訊!這就是入口點~
動畫播放的重要檔案在:Z:\Backup\rk3168_v4.2\frameworks\base\cmds\bootanimation下!
1、了解Android顯示開機畫面的原理!
1. Linux 系統啟動,出現Linux小企鵝畫面(reboot)(Android 1.5及以上版本已經取消加載圖檔);
2. Android平台啟動初始化,出現"A N D R I O D"文字字樣畫面;
3. Android平台圖形系統啟動,出現含閃動的ANDROID字樣的動畫圖檔(start)。
4、原理參考資料:
http://blog.csdn.net/luoshengyang/article/details/7691321
http://blog.csdn.net/conowen/article/details/7884009
http://www.cnblogs.com/jqyp/archive/2012/03/07/2383973.html
http://blog.csdn.net/backgarden_straw/article/details/8571992
http://www.eoeandroid.com/thread-114742-1-1.html
1、播放音樂:
在瑞芯微提供的源碼中其實可以支援播放音樂了,隻是沒有提供音樂檔案!聲音移植
a、首先在BootAnimation.h添加方法的聲明和頭檔案的引用
1 2 | |
b、在 class BootAnimation : public Thread, public IBinder::DeathRecipient中
添加方法 voidplayMusic();
c、然後在BootAnimation.cpp中實作這個方法:
#define BOOTMUSIC_FILE "/system/media/audio/alarms/gx.mp4"
void BootAnimation::playMusic()
{
sp<MediaPlayer> mp = new MediaPlayer();
if ((0 == access(BOOTMUSIC_FILE, F_OK)) && mp != NULL) {
mp->setDataSource(BOOTMUSIC_FILE, NULL) //設定資源
mp->prepare(); //準備,同步
mp->start(); //播放
} //其實mediaplayer還有
很多方法,可以檢視mediaplayer類
}
d、調用并啟動聲音
bool BootAnimation::threadLoop()
{
bool r;
playMusic()
if (mAndroidAnimation) {
r = android();
} else {
r = movie();
}
.......
}
e、Android.mk的修改
因為播放聲音還需要引入庫
LOCAL_SHARED_LIBRARIES := \
libcutils \
libandroidfw \
libutils \
libbinder \
libui \
libskia \
libEGL \
libGLESv1_CM \
libmedia \
libgui
請注意,libmedia是新添加的;
f、聲音檔案的添加
源碼目錄在聲音在:
Z:\source\rk3168_v4.2\frameworks\base\data\sounds
視訊在:
Z:\source\rk3168_v4.2\frameworks\base\data\videos
至于編譯完成後放到什麼地方了那是AllAudio.mk檔案上配置的
$(LOCAL_PATH)/XXXX.mp3:system/etc/xxxx.mp3 \
g、将音頻放到此目錄編譯,就有開機聲音了!
2、視訊移植
a、其實看了前面的資料了解動畫的啟動過程和對上層應用播放視訊的方法其實很簡單了!
其實動畫的播放也是用surface來展示的,然後用OpenGL将圖檔繪制上去的!
聯系上層應用播放視訊的步驟(mediaplayer+surfaceview):
player=new MediaPlayer();
player.setAudioStreamType(AudioManager.STREAM_MUSIC);
player.setDisplay(surfaceHolder);
player.setDataSource("/sdcard/gx.mp4");
player.prepare();
player.start();
。。。。。。。
是以如果你做了聲音播放後是不是很自然想到,就差player.setDisplay(surfaceHolder);
既然如此就倒推這方法做了什麼:
因為setdisplay是mediaplayer的方法
base\media\java\android\media\mediaplayer.Java--->
public void setDisplay(SurfaceHolder sh) {
mSurfaceHolder = sh;
Surface surface;
if (sh != null) {
surface = sh.getSurface();
} else {
surface = null;
}
_setVideoSurface(surface);//重點設定一個surface
updateSurfaceScreenOn();
}
private native void _setVideoSurface(Surface surface); //本地方法
base\media\jni\android_media_MediaPlayer.cpp ----->
{"_setVideoSurface", "(Landroid/view/Surface;)V", (void *)android_media_MediaPlayer_setVideoSurface},
---->android_media_MediaPlayer_setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface)
{
setVideoSurface(env, thiz, jsurface, true /* mediaPlayerMustBeAlive */);
}
------------>
static void
setVideoSurface(JNIEnv *env, jobject thiz, jobject jsurface, jboolean mediaPlayerMustBeAlive)
{
sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
if (mp == NULL) {
if (mediaPlayerMustBeAlive) {
jniThrowException(env, "java/lang/IllegalStateException", NULL);
}
return;
}
decVideoSurfaceRef(env, thiz);
sp<ISurfaceTexture> new_st;
if (jsurface) {
sp<Surface> surface(android_view_Surface_getSurface(env, jsurface));
if (surface != NULL) {
new_st = surface->getSurfaceTexture();
if (new_st == NULL) {
jniThrowException(env, "java/lang/IllegalArgumentException",
"The surface does not have a binding SurfaceTexture!");
return;
}
new_st->incStrong(thiz);
} else {
jniThrowException(env, "java/lang/IllegalArgumentException",
"The surface has been released");
return;
}
}
env->SetIntField(thiz, fields.surface_texture, (int)new_st.get());
// This will fail if the media player has not been initialized yet. This
// can be the case if setDisplay() on MediaPlayer.java has been called
// before setDataSource(). The redundant call to setVideoSurfaceTexture()
// in prepare/prepareAsync covers for this case.
mp->setVideoSurfaceTexture(new_st); //重點
}
分析到這裡我們心裡基本有數了,就是要活的一個surface即可!
b、分析動畫中surface 的建構過程:
在base\cmds\bootanimation\BootAnimation.cpp的status_t BootAnimation::readyToRun()中有對surface的初始化,到了就完成了大半擷取這個surface在調用上面的方法就大功告成!
c、主要函數
#define ANIMATION_BF_SIZE 1024*1024*3
#define TEM_VIDEO_PATH "/data/local/open.mp4"
void BootAnimation::playMusic()
{
int length = 0;
int fd;
int ret = -1;
sp<MediaPlayer> mp = new MediaPlayer();
if (mp != NULL) {
char *bf = (char *)malloc(sizeof(char)*ANIMATION_BF_SIZE);
if(bf == NULL){
return;
}
fd = open(TEM_VIDEO_PATH,O_RDWR|O_CREAT,0777);
if(fd < 0){
ALOGD("hcm ++++++++++open++++++++++++failed++error = %d\n",errno);
return;
}
ret = write(fd,bf,ANIMATION_BF_SIZE);
if(ret < 0){
ALOGD("hcm ++++++++++write++++++++++++failed++error = %d\n",errno);
return;
}
mp->setDataSource(fd, 0,ANIMATION_BF_SIZE);
ALOGD("hcm ++++++++++playMusic++++++++++++\n");
#if 0
// set up the thread-pool
sp<ProcessState> proc(ProcessState::self());
ProcessState::self()->startThreadPool();
// create a client to surfaceflinger
sp<SurfaceComposerClient> client = new SurfaceComposerClient();
sp<SurfaceControl> surfaceControl = client->createSurface(
getpid(), 0, 160, 240, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
surfaceControl->setLayer(100000);
SurfaceComposerClient::closeGlobalTransaction();
// pretend it went cross-process
Parcel parcel;
SurfaceControl::writeSurfaceToParcel(surfaceControl, &parcel);
parcel.setDataPosition(0);
sp<Surface> surface = Surface::readFromParcel(parcel);
//surface.get
IPCThreadState::self()->joinThreadPool();
#else
//mAssets.addDefaultAssets();
sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
ISurfaceComposer::eDisplayIdMain));
DisplayInfo dinfo;
status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
Rect layerStackRect(dinfo.h,dinfo.w) ;
Rect displayRect(dinfo.h,dinfo.w) ;
//SurfaceComposerClient::setOrientation(dtoken, 1, 0);
SurfaceComposerClient::setDisplayProjection(dtoken,1,layerStackRect,displayRect);
SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
int curWidth = dinfo.w;
int curHeight = dinfo.h;
ALOGD("hcm +11+curWidth = %d+curHeight = %d\n",curWidth,curHeight);
ALOGD("hcm +11+curWidth = %d+curHeight = %d\n",curWidth,curHeight);
if (dinfo.orientation % 2 == 0) {
curWidth = dinfo.w;
curHeight = dinfo.h;
}else{
curWidth = dinfo.h;
curHeight = dinfo.w;
}
ALOGD("hcm +22+curWidth = %d+curHeight = %d+dinfo.orientation = %d\n",curWidth,curHeight,dinfo.orientation);
ALOGD("hcm +22+curWidth = %d+curHeight = %d\n",curWidth,curHeight);
curWidth = 1024;
curHeight = 600;
// create the native surface
sp<SurfaceControl> videoControl = session()->createSurface(String8("BootVideo"),
curWidth, curHeight, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
videoControl ->setLayer(0x40000000);
SurfaceComposerClient::closeGlobalTransaction();
sp<Surface> sf = videoControl->getSurface();
sp<ISurfaceTexture> new_st;
new_st = sf->getSurfaceTexture();
mp->setVideoSurfaceTexture(new_st);
#endif
mp->prepare();
mp->setVolume(1.0f,1.0f);
mp->start();
int msec = 0;
mp->getDuration(&msec);
ALOGD("mp->getDuration---hcm---msec = %ld\n",msec);
usleep((msec)*1000-128);
mp->stop();
sf.clear();
videoControl.clear();
close(fd);
free(bf);
ret = remove(TEM_VIDEO_PATH);
if(ret < 0){
ALOGD("hcm ++++++++++remove++++++++++++failed++error = %d\n",errno);
}
Rect cLayerStackRect(600,1024) ;
Rect cDisplayRect(600,1024) ;
SurfaceComposerClient::setDisplayProjection(dtoken,0,cLayerStackRect,cDisplayRect);
// create the native surface
sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
600, 1024, PIXEL_FORMAT_RGB_565);
SurfaceComposerClient::openGlobalTransaction();
control->setLayer(0x40000000);
SurfaceComposerClient::closeGlobalTransaction();
}
}
d、移植過程遇到的問題:
1、視訊方向和surface方向不一緻,簡單方法改變視訊的方向
2、視訊、音頻格式不比對出現卡頓現象,更換視訊的音頻視訊編碼格式
3、視訊一定的播放完畢或者一定的釋放SurfaceTexture,是以的注意結束時間,或者在結束bootanimation前告知播放器停止播放mp->stop();