android5.1 自定义开机音乐动画

开发平台:君正M200S

安卓系统:Android5.1

一、前言

        这几天做了一个自定义开机音乐动画的任务,再此记录一下,方便日后复习。若是哪里有误,敬请指教。

二、代码分析

        开机动画的源码在ingenic-android/frameworks/base/cmds/bootanimation这个目录下,这个目录有这几个文件:

    Android.mk,AudioPlayer.cpp,AudioPlayer.h,BootAnimation.cpp,BootAnimation.h,bootanimation_main.cpp

首先看下bootanimation_main.cpp,这个文件就一个mian函数,

int main(int argc, char** argv)
{
#if defined(HAVE_PTHREADS)
    setpriority(PRIO_PROCESS, 0, ANDROID_PRIORITY_DISPLAY);
#endif

    char value[PROPERTY_VALUE_MAX];
    property_get("debug.sf.nobootanimation", value, "0");
    int noBootAnimation = atoi(value);
    ALOGI_IF(noBootAnimation,  "boot animation disabled");
    if (!noBootAnimation) {
        sp<ProcessState> proc(ProcessState::self());
        ProcessState::self()->startThreadPool();
        // create the boot animation object
        sp<BootAnimation> boot = new BootAnimation();
        IPCThreadState::self()->joinThreadPool();
    }
    return 0;
}

这里创建了一个bootanimation的对象,然后进入BootAnimation.cpp

BootAnimation::BootAnimation() : Thread(false), mZip(NULL)
{
    mSession = new SurfaceComposerClient();
}

在这里构造函数里创建了SurfaceComposerClient这个类,bootanimation通过SurfaceComposerClient类来与SurfaceFlinger服务建立连接。

class BootAnimation : public Thread, public IBinder::DeathRecipient
{
public:
                BootAnimation();
    virtual     ~BootAnimation();

    sp<SurfaceComposerClient> session() const;

private:
    virtual bool        threadLoop();
    virtual status_t    readyToRun();
    virtual void        onFirstRef();
    virtual void        binderDied(const wp<IBinder>& who);

接着从BootAnimation.h里看到BootAnimation有个Thread的父类,会运行readyToRun()这个方法

status_t BootAnimation::readyToRun() {
    mAssets.addDefaultAssets();

    sp<IBinder> dtoken(SurfaceComposerClient::getBuiltInDisplay(
            ISurfaceComposer::eDisplayIdMain));
    DisplayInfo dinfo;
    status_t status = SurfaceComposerClient::getDisplayInfo(dtoken, &dinfo);
    if (status)
        return -1;

    // create the native surface
    sp<SurfaceControl> control = session()->createSurface(String8("BootAnimation"),
            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;

    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;

    // 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);

    ZipFileRO* zipFile = NULL;
    if ((encryptedAnimation &&
            (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) ||

            ((access(OEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(OEM_BOOTANIMATION_FILE)) != NULL)) ||

            ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) &&
            ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) {
        mZip = zipFile;
    }

    return NO_ERROR;
}

这个方法主要就是检测bootanimation.zip是否存在,然后运行threadLoop()

bool BootAnimation::threadLoop()
{
    bool r;
    // We have no bootanimation file, so we use the stock android logo
    // animation.
    if (mZip == NULL) {
        r = android();
    } else {
        r = movie();
    }

    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;
}

在这里可以看到如果没有bootanimation.zip,则运行android()启动安卓自带的开机画面,如果存在就运行movie();

bool BootAnimation::movie()
{
    String8 desString;

    if (!readFile("desc.txt", desString)) {
        ALOGE("readFile desc.txt failed");
        return false;
    }
    char const* s = desString.string();

    // Create and initialize an AudioPlayer if we have an audio_conf.txt file
    String8 audioConf;
    if (readFile("audio_conf.txt", audioConf)) {
        mAudioPlayer = new AudioPlayer;
        if (!mAudioPlayer->init(audioConf.string())) {
            ALOGE("mAudioPlayer.init failed");
            mAudioPlayer = NULL;
        }
    }

    Animation animation;

    // 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[ANIM_ENTRY_NAME_MAX];
        char color[7] = "000000"; // default to black if unspecified

        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 #%6s", &pathType, &count, &pause, path, color) >= 4) {
            // ALOGD("> type=%c, count=%d, pause=%d, path=%s, color=%s", pathType, count, pause, path, color);
            Animation::Part part;
            part.playUntilComplete = pathType == 'c';
            part.count = count;
            part.pause = pause;
            part.path = path;
            part.audioFile = NULL;
            if (!parseColor(color, part.backgroundColor)) {
                ALOGE("> invalid color '#%s'", color);
                part.backgroundColor[0] = 0.0f;
                part.backgroundColor[1] = 0.0f;
                part.backgroundColor[2] = 0.0f;
            }
            animation.parts.add(part);
        }

        s = ++endl;
    }

    // read all the data structures
    const size_t pcount = animation.parts.size();
    void *cookie = NULL;
    if (!mZip->startIteration(&cookie)) {
        return false;
    }

    ZipEntryRO entry;
    char name[ANIM_ENTRY_NAME_MAX];
    while ((entry = mZip->nextEntry(cookie)) != NULL) {
        const int foundEntryName = mZip->getEntryFileName(entry, name, ANIM_ENTRY_NAME_MAX);
        if (foundEntryName > ANIM_ENTRY_NAME_MAX || foundEntryName == -1) {
            ALOGE("Error fetching entry file name");
            continue;
        }

        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 (mZip->getEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) {
                        if (method == ZipFileRO::kCompressStored) {
                            FileMap* map = mZip->createEntryFileMap(entry);
                            if (map) {
                                Animation::Part& part(animation.parts.editItemAt(j));
                                if (leaf == "audio.wav") {
                                    // a part may have at most one audio file
                                    part.audioFile = map;
                                } else {
                                    Animation::Frame frame;
                                    frame.name = leaf;
                                    frame.map = map;
                                    part.frames.add(frame);
                                }
                            }
                        }
                    }
                }
            }
        }
    }

    mZip->endIteration(cookie);

    // 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));

    for (size_t i=0 ; i<pcount ; i++) {
        const Animation::Part& part(animation.parts[i]);
        const size_t fcount = part.frames.size();
        glBindTexture(GL_TEXTURE_2D, 0);

        for (int r=0 ; !part.count || r<part.count ; r++) {
            // Exit any non playuntil complete parts immediately
            if(exitPending() && !part.playUntilComplete)
                break;

            // only play audio file the first time we animate the part
            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
                ALOGE("part.audioFile play srart");
                mAudioPlayer->playFile(part.audioFile);
            }

            glClearColor(
                    part.backgroundColor[0],
                    part.backgroundColor[1],
                    part.backgroundColor[2],
                    1.0f);

            for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                const Animation::Frame& frame(part.frames[j]);
                nsecs_t lastFrame = systemTime();

                if (r > 0) {
                    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);
                }

                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);

                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);
                }

                checkExit();
            }

            usleep(part.pause * ns2us(frameDuration));

            // For infinite parts, we've now played them at least once, so perhaps exit
            if(exitPending() && !part.count)
                break;
        }

        // free the textures for this part
        if (part.count != 1) {
            for (size_t j=0 ; j<fcount ; j++) {
                const Animation::Frame& frame(part.frames[j]);
                glDeleteTextures(1, &frame.tid);
            }
        }
    }

    return false;
}

然后解析zip文件中的内容,先是处理desc.txt和audio_conf.txt。

接下来是读取音乐和图片信息

if (leaf == "audio.wav") {
// a part may have at most one audio file
   part.audioFile = map;
} else {
   Animation::Frame frame;
   frame.name = leaf;
   frame.map = map;
   part.frames.add(frame);
}

leaf是part0里的读到的文件,如果读到的是"audio.wav",则记录在part.audioFile这个变量中,所以音乐文件的名字和格式必须是audio.wav,此外我还放了3张图片,else里记录的就是这几张图片的信息。注意这些文件一定要在part0中。

            if (r == 0 && mAudioPlayer != NULL && part.audioFile) {
                ALOGE("part.audioFile play srart");
                mAudioPlayer->playFile(part.audioFile);
            }

这里是播放音乐

for (size_t j=0 ; j<fcount && (!exitPending() || part.playUntilComplete) ; j++) {
                const Animation::Frame& frame(part.frames[j]);
                nsecs_t lastFrame = systemTime();

                if (r > 0) {
                    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);
                }

                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);

                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);
                }

                checkExit();
            }

这里就是处理图片的代码。

 

三、BootAnimation.zip制作

 

BootAnimation.zip里面有这么几个文件,desc.txt、audio_conf.txt和part0文件夹,part0文件夹里有需要播放的audio.wav音频文件和1.png、2.png、3.png这4个图片文件。

desc.txt里内容为

400 400 1
p 0 0 part0

第一位和第二位表示400X400的分辨率,第三位为帧率,意思是1秒播放一张图片;

p可能是固定格式吧,第二位代表循环播放,如果只播放1次可以给1,第三位是播放间隔,第四位是文件夹名称。

audio_conf.txt是音频的参数配置

card=0
device=0
period_size=1024
period_count=4

mixer "Digital Playback Volume"=31 31
mixer "Headphone Volume"=15 15
mixer "Digital Capture Volume"=15 15
mixer "Mic1 Volume"=5
mixer "Mic2 Volume"=0
mixer "Bypass1 Volume"=0
mixer "Bypass2 Volume"=0
mixer "ADC High Pass Filter Switch"=On
mixer "ADC Wind Noise Filter Switch"=Inactive
mixer "Mic1 Diff Switch"=On
mixer "Mic Stereo Switch"=Off
mixer "ADC Stereo Switch"=On
mixer "Bypass1 Diff Switch"=Off
mixer "Mic1 Bias Switch"=Off
mixer "Mic2 Bias Switch"=Off
mixer "AILatt Mux"=AIPN1
mixer "AIL Mux"=AIPN1
mixer "AOLO Vmux"=LO-ON
mixer "AOHP Vmux"=HP-ON
mixer "AOLO Mux"=DACL/R
mixer "AOHP Mux"=DACL/R
mixer "ADC Mux"=AIL/L

不同芯片配置名称和参数可能不一样,可以在adb中输入tinymix查询ctl。

准备好这些文件后,打开ubuntu终端,制作bootanimation.zip

#cd  bootanimation/

#zip -r -X -Z store ../bootanimation ./

接着打开安卓adb终端,将bootanimation.zip放入安卓系统

#adb remount    //开发系统修改权限

#adb push ../bootanimation.zip /system/media/   //push文件

然后重启验证效果。

PS:desc.txt该文件内容要在linux环境下编写,windows下编写解析失败,大概是win下的换行为/n/r,而linux换行为/n的缘故吧。

四、参考文献

最后感谢各位前辈的分享

https://blog.csdn.net/briblue/article/details/51104230    //Android Framework中的线程Thread及它的threadLoop方法

https://blog.csdn.net/kris_fei/article/details/78130095    //[RK3288][Android6.0] 调试笔记 --- RT5640的ctl name列表

https://blog.csdn.net/ni357103403/article/details/50970303   //android 开机动画desc.txt格式介绍

https://blog.csdn.net/luoshengyang/article/details/7857163   //Android应用程序与SurfaceFlinger服务的连接过程分析

版权声明:本文为博主原创文章,未经博主允许不得转载。

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
### 回答1: 要优化rk3128 android 5.1开机速度,可以采取以下几个措施: 1. 关闭启动时不必要的自启动应用程序:在设置中的应用程序管理中,禁用那些不需要在开机时自动启动的应用程序。这样可以减少开机时的负担,提升开机速度。 2. 清理系统缓存:打开设置,找到存储选项,进入缓存数据,并点击清除缓存按钮。这将清除系统中的临时文件和无用数据,从而提高启动速度。 3. 禁用不必要的动画效果:在开发者选项中,找到动画选项,并将所有的动画效果设置为关闭。这可以减少开机时显示的动画,加快开机速度。 4. 更新系统和应用程序:通过检查系统和应用程序是否有更新,并及时安装最新版本,可以修复一些bug和优化性能,从而提高开机速度。 5. 减少开机自启动应用程序:进入设置-应用程序管理-自启动管理,关闭那些不必要的应用程序自启动。这样可以减少开机时需要加载的应用程序数量,提高开机速度。 6. 运行内存清理:在设置-内存中,找到清理内存选项,点击清理内存按钮。这将释放占用了内存的信息和进程,优化系统性能,加快开机速度。 通过以上优化措施,可以提高rk3128 android 5.1开机速度,让设备更加流畅高效。 ### 回答2: 要优化RK3128 Android 5.1开机速度,可以采取以下措施: 1. 清理启动项:在系统设置中禁用不必要的应用程序的自启动选项。这样可以减少开机时需要加载的应用程序数量,提升开机速度。 2. 禁用冷启动:冷启动是指应用程序第一次运行时需要加载的相关数据和资源,导致启动时间较长。可以通过在开发者选项中禁用冷启动来减少开机时应用程序的加载时间。 3. 清理缓存:开机时,系统会加载缓存文件,这些文件可能会导致开机速度变慢。因此,可以定期清理系统缓存,以提高开机速度。 4. 禁用动画效果:在系统设置中,禁用开机动画效果可以减少开机时间。可以通过进入“开发者选项”并将“窗口动画缩放”、“过渡动画缩放”和“动画持续时间缩放”调整为较低的数值或关闭来实现。 5. 更新系统:如果RK3128 Android 5.1的系统版本较旧,建议升级到最新的系统版本。新版本通常会修复一些开机速度方面的问题,并提供更好的性能和稳定性。 通过以上方法优化RK3128 Android 5.1开机速度,可以更快地启动设备,并提升使用体验。 ### 回答3: 为了优化rk3128 android5.1开机速度,可以采取以下几个方法: 1. 清理系统垃圾:首先,可以通过清理系统垃圾文件来释放存储空间,从而提升开机速度。可以使用一些系统清理工具,如CCleaner等来清理系统中的无效文件和缓存。 2. 禁用开机自启动程序:在系统设置中,可以禁用一些不必要的开机自启动程序。这些自启动程序会消耗系统资源,导致开机速度变慢。通过禁用不必要的自启动程序,可以减少开机时的负荷,从而提升开机速度。 3. 更新系统和应用程序:及时更新系统和应用程序可以优化开机速度。新版本的系统和应用程序通常会修复一些bug,并且优化系统性能。可以定期检查系统更新和应用程序更新,并及时进行更新。 4. 减少开机项:在系统设置中,可以选择性地关闭一些不必要的开机项。这些开机项会在系统启动时加载,影响开机速度。可以根据自己的需求,选择性地关闭不需要的开机项。 5. 使用高速存储卡:如果设备支持外置存储卡,可以使用高速的存储卡来提升开机速度。高速存储卡读写速度更快,可以加快系统的启动时间。 通过以上几个方法的结合使用,可以有效地优化rk3128 android5.1开机速度,提升用户的使用体验。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值