Created
June 28, 2013 15:50
-
-
Save ma34s/5885706 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
* Copyright (C) 2007 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. | |
*/ | |
#define LOG_TAG "BootAnimation" | |
#include <stdint.h> | |
#include <sys/types.h> | |
#include <math.h> | |
#include <fcntl.h> | |
#include <utils/misc.h> | |
#include <signal.h> | |
#include <cutils/properties.h> | |
#include <androidfw/AssetManager.h> | |
#include <binder/IPCThreadState.h> | |
#include <utils/Atomic.h> | |
#include <utils/Errors.h> | |
#include <utils/Log.h> | |
#include <utils/threads.h> | |
#include <ui/PixelFormat.h> | |
#include <ui/Rect.h> | |
#include <ui/Region.h> | |
#include <ui/DisplayInfo.h> | |
#include <ui/FramebufferNativeWindow.h> | |
#include <gui/Surface.h> | |
#include <gui/SurfaceComposerClient.h> | |
#include <core/SkBitmap.h> | |
#include <core/SkStream.h> | |
#include <images/SkImageDecoder.h> | |
#include <GLES/gl.h> | |
#include <GLES/glext.h> | |
#include <EGL/eglext.h> | |
#include <media/AudioSystem.h> | |
#include <media/mediaplayer.h> | |
#include "BootAnimation.h" | |
#define USER_BOOTANIMATION_FILE "/data/local/bootanimation.zip" | |
#define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" | |
#define SYSTEM_ENCRYPTED_BOOTANIMATION_FILE "/system/media/bootanimation-encrypted.zip" | |
#define EXIT_PROP_NAME "service.bootanim.exit" | |
extern "C" int clock_nanosleep(clockid_t clock_id, int flags, | |
const struct timespec *request, | |
struct timespec *remain); | |
namespace android { | |
// --------------------------------------------------------------------------- | |
BootAnimation::BootAnimation( | |
bool noBootAnimationWait , | |
const char* animationFile , | |
const char* audioFile , | |
const char* movieFile , | |
float audioVolume ) : | |
Thread(false), | |
mAudioVolume(audioVolume), | |
mNoBootAnimationWait(noBootAnimationWait) | |
{ | |
if (animationFile) { | |
strcpy(mAnimationFile, animationFile); | |
} else { | |
mAnimationFile[0] = '\0'; | |
} | |
if (audioFile) { | |
strcpy(mAudioFile, audioFile); | |
} else { | |
mAudioFile[0] = '\0'; | |
} | |
if (movieFile) { | |
strcpy(mMovieFile, movieFile); | |
} else { | |
mMovieFile[0] = '\0'; | |
} | |
mSession = new SurfaceComposerClient(); | |
} | |
BootAnimation::~BootAnimation() { | |
} | |
void BootAnimation::onFirstRef() { | |
status_t err = mSession->linkToComposerDeath(this); | |
ALOGE_IF(err, "linkToComposerDeath failed (%s) ", strerror(-err)); | |
if (err == NO_ERROR) { | |
run("BootAnimation", PRIORITY_DISPLAY); | |
} | |
} | |
sp<SurfaceComposerClient> BootAnimation::session() const { | |
return mSession; | |
} | |
void BootAnimation::binderDied(const wp<IBinder>& who) | |
{ | |
// woah, surfaceflinger died! | |
ALOGD("SurfaceFlinger died, exiting..."); | |
// calling requestExit() is not enough here because the Surface code | |
// might be blocked on a condition variable that will never be updated. | |
kill( getpid(), SIGKILL ); | |
requestExit(); | |
} | |
status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, | |
const char* name) { | |
Asset* asset = assets.open(name, Asset::ACCESS_BUFFER); | |
if (!asset) | |
return NO_INIT; | |
SkBitmap bitmap; | |
SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), | |
&bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); | |
asset->close(); | |
delete asset; | |
// ensure we can call getPixels(). No need to call unlock, since the | |
// bitmap will go out of scope when we return from this method. | |
bitmap.lockPixels(); | |
const int w = bitmap.width(); | |
const int h = bitmap.height(); | |
const void* p = bitmap.getPixels(); | |
GLint crop[4] = { 0, h, w, -h }; | |
texture->w = w; | |
texture->h = h; | |
glGenTextures(1, &texture->name); | |
glBindTexture(GL_TEXTURE_2D, texture->name); | |
switch (bitmap.getConfig()) { | |
case SkBitmap::kA8_Config: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, | |
GL_UNSIGNED_BYTE, p); | |
break; | |
case SkBitmap::kARGB_4444_Config: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, | |
GL_UNSIGNED_SHORT_4_4_4_4, p); | |
break; | |
case SkBitmap::kARGB_8888_Config: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, | |
GL_UNSIGNED_BYTE, p); | |
break; | |
case SkBitmap::kRGB_565_Config: | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, | |
GL_UNSIGNED_SHORT_5_6_5, p); | |
break; | |
default: | |
break; | |
} | |
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_NEAREST); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_NEAREST); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
return NO_ERROR; | |
} | |
status_t BootAnimation::initTexture(void* buffer, size_t len) | |
{ | |
//StopWatch watch("blah"); | |
SkBitmap bitmap; | |
SkMemoryStream stream(buffer, len); | |
SkImageDecoder* codec = SkImageDecoder::Factory(&stream); | |
codec->setDitherImage(false); | |
if (codec) { | |
codec->decode(&stream, &bitmap, | |
#ifdef USE_565 | |
SkBitmap::kRGB_565_Config, | |
#else | |
SkBitmap::kARGB_8888_Config, | |
#endif | |
SkImageDecoder::kDecodePixels_Mode); | |
delete codec; | |
} | |
// ensure we can call getPixels(). No need to call unlock, since the | |
// bitmap will go out of scope when we return from this method. | |
bitmap.lockPixels(); | |
const int w = bitmap.width(); | |
const int h = bitmap.height(); | |
const void* p = bitmap.getPixels(); | |
GLint crop[4] = { 0, h, w, -h }; | |
int tw = 1 << (31 - __builtin_clz(w)); | |
int th = 1 << (31 - __builtin_clz(h)); | |
if (tw < w) tw <<= 1; | |
if (th < h) th <<= 1; | |
switch (bitmap.getConfig()) { | |
case SkBitmap::kARGB_8888_Config: | |
if (tw != w || th != h) { | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, | |
GL_UNSIGNED_BYTE, 0); | |
glTexSubImage2D(GL_TEXTURE_2D, 0, | |
0, 0, w, h, GL_RGBA, GL_UNSIGNED_BYTE, p); | |
} else { | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, | |
GL_UNSIGNED_BYTE, p); | |
} | |
break; | |
case SkBitmap::kRGB_565_Config: | |
if (tw != w || th != h) { | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, | |
GL_UNSIGNED_SHORT_5_6_5, 0); | |
glTexSubImage2D(GL_TEXTURE_2D, 0, | |
0, 0, w, h, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); | |
} else { | |
glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, | |
GL_UNSIGNED_SHORT_5_6_5, p); | |
} | |
break; | |
default: | |
break; | |
} | |
glTexParameteriv(GL_TEXTURE_2D, GL_TEXTURE_CROP_RECT_OES, crop); | |
return NO_ERROR; | |
} | |
status_t BootAnimation::readyToRun() { | |
mAssets.addDefaultAssets(); | |
DisplayInfo dinfo; | |
status_t status = session()->getDisplayInfo(0, &dinfo); | |
if (status) | |
return -1; | |
// create the native surface | |
sp<SurfaceControl> control = session()->createSurface( | |
0, dinfo.w, dinfo.h, PIXEL_FORMAT_RGB_565); | |
SurfaceComposerClient::openGlobalTransaction(); | |
control->setLayer(0x40000000); | |
SurfaceComposerClient::closeGlobalTransaction(); | |
sp<Surface> s = control->getSurface(); | |
// initialize opengl and egl | |
const EGLint attribs[] = { | |
EGL_RED_SIZE, 8, | |
EGL_GREEN_SIZE, 8, | |
EGL_BLUE_SIZE, 8, | |
EGL_DEPTH_SIZE, 0, | |
EGL_NONE | |
}; | |
EGLint w, h, dummy; | |
EGLint numConfigs; | |
EGLConfig config; | |
EGLSurface surface; | |
EGLContext context; | |
//----------------------------------------------------------------- | |
// at first | |
// judge play contents. | |
mMoviePlay = false; | |
if(access(mMovieFile, R_OK) == 0) | |
{ | |
mMoviePlay = true; | |
} | |
else | |
{ | |
mAndroidAnimation = true; | |
// If the device has encryption turned on or is in process | |
// of being encrypted we show the encrypted boot animation. | |
char decrypt[PROPERTY_VALUE_MAX]; | |
property_get("vold.decrypt", decrypt, ""); | |
bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); | |
mAndroidAnimation = false; | |
#ifdef APP_BUILD | |
seteuid(0); | |
#endif //#ifdef APP_BUILD | |
#ifdef PRELOAD_BOOTANIMATION | |
// Preload the bootanimation zip on memory, so we don't stutter | |
// when showing the animation | |
FILE* fd; | |
#endif | |
if((access(mAnimationFile, R_OK) == 0) && | |
(mZip.open(mAnimationFile) == NO_ERROR)) | |
{ | |
ALOGI("mAnimationFile = %s",mAnimationFile); | |
//mAndroidAnimation = false; | |
#ifdef PRELOAD_BOOTANIMATION | |
fd = fopen(mAnimationFile, "r"); | |
#endif //#ifdef PRELOAD_BOOTANIMATION | |
} | |
else if(encryptedAnimation && | |
(access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && | |
(mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) | |
{ | |
//mAndroidAnimation = false; | |
#ifdef PRELOAD_BOOTANIMATION | |
fd = fopen(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, "r"); | |
#endif //#ifdef PRELOAD_BOOTANIMATION | |
} | |
else if((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && | |
(mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) | |
{ | |
//mAndroidAnimation = false; | |
#ifdef PRELOAD_BOOTANIMATION | |
fd = fopen(USER_BOOTANIMATION_FILE, "r"); | |
#endif //#ifdef PRELOAD_BOOTANIMATION | |
} | |
else if((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && | |
(mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR)) | |
{ | |
//mAndroidAnimation = false; | |
#ifdef PRELOAD_BOOTANIMATION | |
fd = fopen(SYSTEM_BOOTANIMATION_FILE, "r"); | |
#endif //#ifdef PRELOAD_BOOTANIMATION | |
} | |
else | |
{ | |
mAndroidAnimation = true; | |
} | |
#ifdef APP_BUILD | |
seteuid(1003); | |
#endif //#ifdef APP_BUILD | |
//----------------------------------------------------------------- | |
//if movie play not need ogl setting... | |
EGLDisplay display = eglGetDisplay(EGL_DEFAULT_DISPLAY); | |
eglInitialize(display, 0, 0); | |
eglChooseConfig(display, attribs, &config, 1, &numConfigs); | |
surface = eglCreateWindowSurface(display, config, s.get(), NULL); | |
context = eglCreateContext(display, config, NULL, NULL); | |
eglQuerySurface(display, surface, EGL_WIDTH, &w); | |
eglQuerySurface(display, surface, EGL_HEIGHT, &h); | |
if (eglMakeCurrent(display, surface, surface, context) == EGL_FALSE) | |
return NO_INIT; | |
mDisplay = display; | |
mContext = context; | |
mSurface = surface; | |
} | |
mWidth = w; | |
mHeight = h; | |
mFlingerSurfaceControl = control; | |
mFlingerSurface = s; | |
#ifdef PRELOAD_BOOTANIMATION | |
if (fd != NULL) { | |
// We could use readahead.. | |
// ... if bionic supported it :( | |
//readahead(fd, 0, INT_MAX); | |
void *crappyBuffer = malloc(2*1024*1024); | |
if (crappyBuffer != NULL) { | |
// Read all the zip | |
while (!feof(fd)) | |
fread(crappyBuffer, 1024, 2*1024, fd); | |
free(crappyBuffer); | |
} else { | |
ALOGW("Unable to allocate memory to preload the animation"); | |
} | |
fclose(fd); | |
} | |
#endif | |
return NO_ERROR; | |
} | |
void BootAnimation::setProperty(char* prop,char* val) | |
{ | |
ALOGI("setProp;%s = %s",prop,val); | |
seteuid(0); | |
property_set(prop, val); | |
seteuid(1003); | |
} | |
bool BootAnimation::threadLoop() | |
{ | |
bool r; | |
setProperty("sys.bootanim_wait", "1"); | |
//prority is movie > animation ( when no animation, android) | |
if(mMoviePlay) | |
{ | |
r = movie(); | |
} | |
else if (mAndroidAnimation) { | |
r = android(); | |
} else { | |
r = animation(); | |
} | |
//set complete to system | |
setProperty("sys.bootanim_completed", "1"); | |
eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); | |
eglDestroyContext(mDisplay, mContext); | |
eglDestroySurface(mDisplay, mSurface); | |
mFlingerSurface.clear(); | |
mFlingerSurfaceControl.clear(); | |
eglTerminate(mDisplay); | |
IPCThreadState::self()->stopProcess(); | |
return r; | |
} | |
bool BootAnimation::android() | |
{ | |
initTexture(&mAndroid[0], mAssets, "images/android-logo-mask.png"); | |
initTexture(&mAndroid[1], mAssets, "images/android-logo-shine.png"); | |
// clear screen | |
glShadeModel(GL_FLAT); | |
glDisable(GL_DITHER); | |
glDisable(GL_SCISSOR_TEST); | |
glClearColor(0,0,0,1); | |
glClear(GL_COLOR_BUFFER_BIT); | |
eglSwapBuffers(mDisplay, mSurface); | |
glEnable(GL_TEXTURE_2D); | |
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); | |
const GLint xc = (mWidth - mAndroid[0].w) / 2; | |
const GLint yc = (mHeight - mAndroid[0].h) / 2; | |
const Rect updateRect(xc, yc, xc + mAndroid[0].w, yc + mAndroid[0].h); | |
glScissor(updateRect.left, mHeight - updateRect.bottom, updateRect.width(), | |
updateRect.height()); | |
// Blend state | |
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA); | |
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); | |
const nsecs_t startTime = systemTime(); | |
do { | |
nsecs_t now = systemTime(); | |
double time = now - startTime; | |
float t = 4.0f * float(time / us2ns(16667)) / mAndroid[1].w; | |
GLint offset = (1 - (t - floorf(t))) * mAndroid[1].w; | |
GLint x = xc - offset; | |
glDisable(GL_SCISSOR_TEST); | |
glClear(GL_COLOR_BUFFER_BIT); | |
glEnable(GL_SCISSOR_TEST); | |
glDisable(GL_BLEND); | |
glBindTexture(GL_TEXTURE_2D, mAndroid[1].name); | |
glDrawTexiOES(x, yc, 0, mAndroid[1].w, mAndroid[1].h); | |
glDrawTexiOES(x + mAndroid[1].w, yc, 0, mAndroid[1].w, mAndroid[1].h); | |
glEnable(GL_BLEND); | |
glBindTexture(GL_TEXTURE_2D, mAndroid[0].name); | |
glDrawTexiOES(xc, yc, 0, mAndroid[0].w, mAndroid[0].h); | |
EGLBoolean res = eglSwapBuffers(mDisplay, mSurface); | |
if (res == EGL_FALSE) | |
break; | |
// 12fps: don't animate too fast to preserve CPU | |
const nsecs_t sleepTime = 83333 - ns2us(systemTime() - now); | |
if (sleepTime > 0) | |
usleep(sleepTime); | |
checkExit(); | |
} while (!exitPending()); | |
glDeleteTextures(1, &mAndroid[0].name); | |
glDeleteTextures(1, &mAndroid[1].name); | |
return false; | |
} | |
void BootAnimation::checkExit() { | |
// Allow surface flinger to gracefully request shutdown | |
char value[PROPERTY_VALUE_MAX]; | |
property_get(EXIT_PROP_NAME, value, "0"); | |
int exitnow = atoi(value); | |
if (exitnow) { | |
ALOGD("%s detect. exitting", EXIT_PROP_NAME); | |
setProperty("sys.bootanim_completed", "1"); | |
requestExit(); | |
} | |
} | |
bool BootAnimation::animation() | |
{ | |
ZipFileRO& zip(mZip); | |
size_t numEntries = zip.getNumEntries(); | |
ZipEntryRO desc = zip.findEntryByName("desc.txt"); | |
FileMap* descMap = zip.createEntryFileMap(desc); | |
ALOGE_IF(!descMap, "descMap is null"); | |
if (!descMap) { | |
return false; | |
} | |
String8 desString((char const*)descMap->getDataPtr(), | |
descMap->getDataLength()); | |
char const* s = desString.string(); | |
Animation animation; | |
#ifdef APP_BUILD | |
seteuid(0); | |
#endif //#ifdef APP_BUILD | |
// Parse the description file | |
for (;;) { | |
const char* endl = strstr(s, "\n"); | |
if (!endl) break; | |
String8 line(s, endl - s); | |
const char* l = line.string(); | |
int fps, width, height, count, pause; | |
char path[256]; | |
char pathType; | |
if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { | |
ALOGD("> w=%d, h=%d, fps=%d", width, height, fps); | |
animation.width = width; | |
animation.height = height; | |
animation.fps = fps; | |
} | |
else if (sscanf(l, " %c %d %d %s", &pathType, &count, &pause, path) == 4) { | |
//LOGD("> type=%c, count=%d, pause=%d, path=%s", pathType, count, pause, path); | |
Animation::Part part; | |
part.playUntilComplete = pathType == 'c'; | |
part.count = count; | |
part.pause = pause; | |
part.path = path; | |
animation.parts.add(part); | |
} | |
s = ++endl; | |
} | |
if (animation.width == 0) animation.width = mWidth; | |
if (animation.height == 0) animation.height = mHeight; | |
if (animation.fps == 0) animation.fps = 30; | |
// read all the data structures | |
const size_t pcount = animation.parts.size(); | |
for (size_t i=0 ; i<numEntries ; i++) { | |
char name[256]; | |
ZipEntryRO entry = zip.findEntryByIndex(i); | |
if (zip.getEntryFileName(entry, name, 256) == 0) { | |
const String8 entryName(name); | |
const String8 path(entryName.getPathDir()); | |
const String8 leaf(entryName.getPathLeaf()); | |
if (leaf.size() > 0) { | |
for (size_t j=0 ; j<pcount ; j++) { | |
if (path == animation.parts[j].path) { | |
int method; | |
// supports only stored png files | |
if (zip.getEntryInfo(entry, &method, 0, 0, 0, 0, 0)) { | |
if (method == ZipFileRO::kCompressStored) { | |
FileMap* map = zip.createEntryFileMap(entry); | |
if (map) { | |
Animation::Frame frame; | |
frame.name = leaf; | |
frame.map = map; | |
Animation::Part& part(animation.parts.editItemAt(j)); | |
part.frames.add(frame); | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
} | |
#ifdef APP_BUILD | |
seteuid(1003); | |
#endif //#ifdef APP_BUILD | |
// clear screen | |
glShadeModel(GL_FLAT); | |
glDisable(GL_DITHER); | |
glDisable(GL_SCISSOR_TEST); | |
glDisable(GL_BLEND); | |
glClearColor(0,0,0,1); | |
glClear(GL_COLOR_BUFFER_BIT); | |
eglSwapBuffers(mDisplay, mSurface); | |
glBindTexture(GL_TEXTURE_2D, 0); | |
glEnable(GL_TEXTURE_2D); | |
glTexEnvx(GL_TEXTURE_ENV, GL_TEXTURE_ENV_MODE, GL_REPLACE); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_S, GL_REPEAT); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_WRAP_T, GL_REPEAT); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
const int xc = (mWidth - animation.width) / 2; | |
const int yc = ((mHeight - animation.height) / 2); | |
nsecs_t lastFrame = systemTime(); | |
nsecs_t frameDuration = s2ns(1) / animation.fps; | |
Region clearReg(Rect(mWidth, mHeight)); | |
clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); | |
char propValue[PROPERTY_VALUE_MAX]; | |
bool isBootCompleted = false; | |
MediaPlayer* mp = NULL; | |
if (mAudioFile[0] != '\0') { | |
mp = new MediaPlayer(); | |
if (mp->setDataSource(mAudioFile, NULL) == NO_ERROR) { | |
//mp->setAudioStreamType(AUDIO_STREAM_SYSTEM); | |
mp->setVolume(mAudioVolume, mAudioVolume); | |
mp->prepare(); | |
mp->seekTo(0); | |
mp->start(); | |
// status_t getVideoWidth(int *w); | |
// status_t getVideoHeight(int *h); | |
} else { | |
ALOGE("Failed to load audio file: %s", mAudioFile); | |
mp->disconnect(); | |
delete mp; | |
mp = NULL; | |
} | |
} | |
for (size_t i=0 ; i<pcount && !exitPending() ; i++) { | |
const Animation::Part& part(animation.parts[i]); | |
const size_t fcount = part.frames.size(); | |
#ifdef NO_TEXTURE_CACHE | |
const int noTextureCache = NO_TEXTURE_CACHE; | |
#else | |
const int noTextureCache = ((animation.width * animation.height * fcount) > | |
48 * 1024 * 1024) ? 1 : 0; | |
#endif | |
glBindTexture(GL_TEXTURE_2D, 0); | |
for (int r=0 ; (!part.count || r<part.count) && !isBootCompleted; r++) { | |
// Exit any non playuntil complete parts immediately | |
if( (exitPending() && !part.playUntilComplete) || | |
mNoBootAnimationWait || | |
(r > part.count && !isBootCompleted) ){ | |
if (propValue[0] == '1') { | |
ALOGI("detect sys.boot_completed=1"); | |
isBootCompleted = true; | |
break; | |
} | |
} | |
for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) && !isBootCompleted ; j++) { | |
if (mNoBootAnimationWait && !isBootCompleted) { | |
property_get("sys.boot_completed", propValue, "0"); | |
if (propValue[0] == '1') { | |
ALOGI("detect sys.boot_completed=1"); | |
isBootCompleted = true; | |
break; | |
} | |
} | |
const Animation::Frame& frame(part.frames[j]); | |
#ifndef NO_MA34_TIMER | |
nsecs_t lastFrame = systemTime(); | |
#endif | |
if (r > 0 && !noTextureCache) { | |
glBindTexture(GL_TEXTURE_2D, frame.tid); | |
} else { | |
if (part.count != 1) { | |
glGenTextures(1, &frame.tid); | |
glBindTexture(GL_TEXTURE_2D, frame.tid); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MIN_FILTER, GL_LINEAR); | |
glTexParameterx(GL_TEXTURE_2D, GL_TEXTURE_MAG_FILTER, GL_LINEAR); | |
} | |
initTexture( | |
frame.map->getDataPtr(), | |
frame.map->getDataLength()); | |
} | |
if (!clearReg.isEmpty()) { | |
Region::const_iterator head(clearReg.begin()); | |
Region::const_iterator tail(clearReg.end()); | |
glEnable(GL_SCISSOR_TEST); | |
while (head != tail) { | |
const Rect& r(*head++); | |
glScissor(r.left, mHeight - r.bottom, | |
r.width(), r.height()); | |
glClear(GL_COLOR_BUFFER_BIT); | |
} | |
glDisable(GL_SCISSOR_TEST); | |
} | |
glDrawTexiOES(xc, yc, 0, animation.width, animation.height); | |
eglSwapBuffers(mDisplay, mSurface); | |
#ifndef NO_MA34_TIMER | |
nsecs_t now = systemTime(); | |
nsecs_t delay = frameDuration - (now - lastFrame); | |
//ALOGD("%lld, %lld", ns2ms(now - lastFrame), ns2ms(delay)); | |
lastFrame = now; | |
if (delay > 0) { | |
struct timespec spec; | |
spec.tv_sec = (now + delay) / 1000000000; | |
spec.tv_nsec = (now + delay) % 1000000000; | |
int err; | |
do { | |
err = clock_nanosleep(CLOCK_MONOTONIC, TIMER_ABSTIME, &spec, NULL); | |
} while (err<0 && errno == EINTR); | |
} | |
#else | |
//-- framerate priority by ma34 | |
nsecs_t now; | |
while(1) | |
{ | |
now = systemTime(); | |
nsecs_t elapsed = (now - lastFrame); | |
if(frameDuration <= elapsed ) | |
{ | |
break; | |
} | |
usleep(250); | |
} | |
lastFrame = now; | |
#endif | |
checkExit(); | |
if (noTextureCache) | |
glDeleteTextures(1, &frame.tid); | |
} | |
usleep(part.pause * ns2us(frameDuration)); | |
// For infinite parts, we've now played them at least once, so perhaps exit | |
if(exitPending() && !part.count) | |
{ | |
property_get("sys.boot_completed", propValue, "0"); | |
if (propValue[0] == '1') { | |
isBootCompleted = true; | |
break; | |
} | |
} | |
} | |
// free the textures for this part | |
if (part.count != 1 && !noTextureCache) { | |
for (size_t j=0 ; j<fcount ; j++) { | |
const Animation::Frame& frame(part.frames[j]); | |
glDeleteTextures(1, &frame.tid); | |
} | |
} | |
} | |
if (mp) { | |
mp->stop(); | |
mp->disconnect(); | |
delete mp; | |
} | |
return false; | |
} | |
bool BootAnimation::movie() | |
{ | |
char propValue[PROPERTY_VALUE_MAX]; | |
bool isBootCompleted = false; | |
//SurfaceTextureClient mSTC; | |
MediaPlayer* mp = NULL; | |
if (mMovieFile[0] != '\0') { | |
mp = new MediaPlayer(); | |
if (mp->setDataSource(mMovieFile, NULL) == NO_ERROR) { | |
//mp->setAudioStreamType(AUDIO_STREAM_SYSTEM); | |
mp->setVolume(mAudioVolume, mAudioVolume); | |
//mSurface->getSurfaceTexture() | |
mp->setVideoSurfaceTexture(mFlingerSurface->getSurfaceTexture()); | |
mp->prepare(); | |
mp->seekTo(0); | |
mp->start(); | |
} else { | |
ALOGE("Failed to load movie file: %s", mMovieFile); | |
mp->disconnect(); | |
delete mp; | |
mp = NULL; | |
} | |
} | |
if (mp) { | |
ALOGI("wait play : %s", mMovieFile); | |
while(!exitPending()&& !isBootCompleted) | |
{ | |
if (mNoBootAnimationWait && !isBootCompleted) | |
{ | |
property_get("sys.boot_completed", propValue, "0"); | |
if (propValue[0] == '1') { | |
isBootCompleted = true; | |
break; | |
} | |
} | |
if(!mp->isPlaying()) | |
{ | |
break; | |
} | |
usleep(5000);//sleep | |
} | |
mp->stop(); | |
mp->disconnect(); | |
delete mp; | |
} | |
return false; | |
} | |
// --------------------------------------------------------------------------- | |
} | |
; // namespace android |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment