BootAnimation+SE+开机MP4动画播放

先看效果

编译

m libbootanimation -j8

手动播放 验证编码器可用

bash 复制代码
PS C:\Users\HiMaq> adb push C:\Users\HiMaq\Videos\bootanimation.mp4 /system/media/bootanimation.mp4
C:\Users\HiMaq\Videos\bootanimation.mp4: 1 file pushed, 0 skipped. 8.9 MB/s (37097541 bytes in 3.994s)

adb push C:\Users\HiMaq\Videos\bootanimation.mp4 /sdcard/video.mp4
adb shell am start -a android.intent.action.VIEW -d file:///sdcard/video.mp4 -t video/mp4
Starting: Intent { act=android.intent.action.VIEW dat=file:///sdcard/video.mp4 typ=video/mp4 }

强行启动开机动画

修改思路,修改属性为1

bash 复制代码
  void BootAnimation::checkExit() {                                                                                                                                                                          
          char value[PROPERTY_VALUE_MAX];                                                                                                                                                                        
          property_get("service.bootanim.exit", value, "0");  // 读系统属性                                                                                                                                      
          int exitnow = atoi(value);                                                                                                                                                                             
          if (exitnow) {                                                                                                                                                                                         
              requestExit();    // 设置 Thread::mExitPending = true                                                                                                                                              
          }                                                                                                                                                                                                      
      }                                                                                                                                                                                                                        

手动启动bootanmation 可执行文件

bash 复制代码
PS C:\Users\HiMaq\Desktop> adb shell setprop service.bootanim.exit 0
PS C:\Users\HiMaq\Desktop> adb shell getprop service.bootanim.exit
0
PS C:\Users\HiMaq\Desktop> adb logcat -b main -s "BootAnimation"  
PS C:\Users\HiMaq\Desktop> adb shell system/bin/bootanimation

或者

bash 复制代码
emulator64_x86_64:/ #  setprop ctl.start bootanim
emulator64_x86_64:/ #  setprop ctl.stop bootanim

手动直接启动日志

./system/bin/bootanimation

bash 复制代码
行 17207: 06-09 12:45:50.302  2195  2199 D BootAnimation: Found MP4 boot animation: /system/media/bootanimation.mp4
	行 17208: 06-09 12:45:50.302  2195  2199 D BootAnimation: Playing MP4 boot animation: /system/media/bootanimation.mp4
	行 17209: 06-09 12:45:50.302  2195  2199 D BootAnimation: Emulator detected, skipping MP4 boot animation
	行 17210: 06-09 12:45:50.302  2195  2199 D BootAnimation: media.player service available after 0ms
	行 17211: 06-09 12:45:50.805   514  1993 D NuPlayerDriver: NuPlayerDriver(0x707b9355cfb0) created, clientPid(2195)
	行 17215: 06-09 12:45:50.808   514  2201 D NuPlayer: onSetVideoSurface(0x707c53563640, no video decoder)
	行 17218: 06-09 12:45:50.815  2195  2199 D BootAnimation: MP4 boot animation started (attempt 1)
	行 17484: 06-09 12:45:50.978   514  1993 D NuPlayerDriver: stop(0x707b9355cfb0)
	行 17485: 06-09 12:45:50.979   514  1993 D NuPlayerDriver: reset(0x707b9355cfb0) at state 8
	行 17504: 06-09 12:45:51.027   514  2201 D NuPlayerDriver: notifyResetComplete(0x707b9355cfb0)
	行 17505: 06-09 12:45:51.027   514  1993 D NuPlayerDriver: reset(0x707b9355cfb0) at state 0
	行 17509: 06-09 12:45:51.028  2195  2199 D BootAnimation: MP4 boot animation finished
	行 17510: 06-09 12:45:51.043  2195  2199 D BootAnimation: BootAnimationStopTiming start time: 103220ms
	行 17521: 06-09 12:45:52.578  2217  2217 D BootAnimation: BootAnimationStartTiming start time: 104755ms
	行 17522: 06-09 12:45:52.578  2217  2217 D BootAnimation: BootAnimationPreloadTiming start time: 104755ms
	行 17523: 06-09 12:45:52.578  2217  2217 D BootAnimation: BootAnimationPreloadStopTiming start time: 104755ms

获取属性

bash 复制代码
130|emulator64_x86_64:/ $ su
emulator64_x86_64:/ # getprop | grep exit
[service.bootanim.exit]: [1]

杀进程

bash 复制代码
maqi@QiMa:~$ ps -A |grep java
 124655 pts/0    00:01:24 java
maqi@QiMa:~$ kill -9 124655

检查生成时间

bash 复制代码
maqi@QiMa:~/android13-r44$ ls -l out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so 
-rwxr-xr-x 1 maqi maqi 112040  6月  9 20:38 out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so
bash 复制代码
maqi@QiMa:~/android13-r44$ ls -l out/target/product/emulator_car_x86_64/system/bin/bootanimation 
-rwxr-xr-x 1 maqi maqi 17104  6月  9 17:35 out/target/product/emulator_car_x86_64/system/bin/bootanimation

md5sum检查

bash 复制代码
maqi@QiMa:~/android13-r44$ md5sum out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so 
050059780cb5b71aba61b362496c954c  out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so

推包

bash 复制代码
maqi@QiMa:~/android13-r44$ adb root
restarting adbd as root
maqi@QiMa:~/android13-r44$ adb remount
remount succeeded
maqi@QiMa:~/android13-r44$ adb push out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so system/lib64/
out/target/product/emulator_car_x86_64/system/lib64/libbootanimation.so: 1 file pushed, 0 skipped. 147.6 MB/s (112040 bytes in 0.001s)

重启模拟器

这里我使用的是谷歌官方下载的模拟因为,我自己编译的模拟器不支持解码器

emulator -avd QmX86Api33 -writable-system

因为根本没有mediaserver NuPlayerDriver 报错无法找到解码器

bash 复制代码
# 1. 检查 mediaserver 进程是否存在                                                                                                                                                                         
ps -A | grep mediaserver                                                                                                                                                                                   
# 2. 检查 "media.player" 是否已注册                                                                                                                                                                        
service list | grep media.player                                                                                                                                                                           
# 3. 查看 mediaserver 启动日志                                                                                                                                                                             
adb logcat -b system -s "mediaserver" -t 100                                                                                                                                                                   
# 4. 看 BootAnimation 的等待输出                                                                                                                                                                           
adb logcat -b main -s "BootAnimation" -t 200     
# se 权限检查
adb logcat -b all -d | grep "avc: denied" | grep -E "bootanim|mediaserver"    

权限问题 拒绝访问 media.player

bash 复制代码
06-10 03:33:03.057   482   486 D BootAnimation: media.player service available after 27000ms
06-10 03:33:03.560   482   482 I BootAnimation: type=1400 audit(0.0:17): avc: denied { call } for scontext=u:r:bootanim:s0 tcontext=u:r:mediaserver:s0 tclass=binder permissive=1
06-10 03:33:03.560   482   482 I BootAnimation: type=1400 audit(0.0:18): avc: denied { transfer } for scontext=u:r:bootanim:s0 tcontext=u:r:mediaserver:s0 tclass=binder permissive=1
06-10 03:33:03.568   482   482 I BootAnimation: type=1400 audit(0.0:20): avc: denied { use } for path="/system/media/bootanimation.mp4" dev="overlay" ino=50 scontext=u:r:mediaserver:s0 tcontext=u:r:bootanim:s0 tclass=fd permissive=1
06-10 03:33:03.606   482   486 D BootAnimation: MP4 boot animation started (attempt 1)
06-10 03:33:06.858   482   486 D BootAnimation: MP4 boot animation finished
06-10 03:33:06.866   482   486 D BootAnimation: BootAnimationStopTiming start time: 40025ms

***手动验证 SE ***

启动途中 强行关闭se

bash 复制代码
PS C:\Users\HiMaq\Desktop> adb root
restarting adbd as root
PS C:\Users\HiMaq\Desktop> adb shell setenforce 0

验证

bash 复制代码
adb logcat | grep "avc: denied"

编译和推送SE

bash 复制代码
#!/bin/bash
set -e

cd /home/maqi/android13-r44
source build/envsetup.sh
lunch emulator_car_x86_64

# 编译
m selinux_policy

setools 权限检查工具

bash 复制代码
sudo apt-get install setools

查找precompiled_sepolicy

java 复制代码
maqi@QiMa:~/android13-r44$ tree -L 1  out/target/product/emulator_car_x86_64/vendor/etc/selinux/
out/target/product/emulator_car_x86_64/vendor/etc/selinux/
├── plat_pub_versioned.cil
├── plat_sepolicy_vers.txt
├── precompiled_sepolicy
├── precompiled_sepolicy.plat_sepolicy_and_mapping.sha256
├── precompiled_sepolicy.product_sepolicy_and_mapping.sha256
├── selinux_denial_metadata
├── vendor_file_contexts
├── vendor_hwservice_contexts
├── vendor_mac_permissions.xml
├── vendor_property_contexts
├── vendor_seapp_contexts
├── vendor_sepolicy.cil
├── vendor_service_contexts
└── vndservice_contexts

0 directories, 14 files

sesearch 产物权限检查

bash 复制代码
# 设置路径变量
POLICY_PATH="out/target/product/emulator_car_x86_64/vendor/etc/selinux/precompiled_sepolicy"

# 1. 查询 service_manager 权限
sesearch --allow -s bootanim -t mediaserver_service -c service_manager $POLICY_PATH

# 2. 查询 binder 权限
sesearch --allow -s bootanim -t mediaserver -c binder $POLICY_PATH

# 3. 查询 file 权限
sesearch --allow -s bootanim -t mnt_product_file -c file $POLICY_PATH

SE检查结果

bash 复制代码
maqi@QiMa:~/android13-r44$ # 设置路径变量
maqi@QiMa:~/android13-r44$ POLICY_PATH="out/target/product/emulator_car_x86_64/vendor/etc/selinux/precompiled_sepolicy"

maqi@QiMa:~/android13-r44$ # 1. 查询 service_manager 权限
maqi@QiMa:~/android13-r44$ sesearch --allow -s bootanim -t mediaserver_service -c service_manager $POLICY_PATH
allow bootanim mediaserver_service:service_manager find;

maqi@QiMa:~/android13-r44$ # 2. 查询 binder 权限
maqi@QiMa:~/android13-r44$ sesearch --allow -s bootanim -t mediaserver -c binder $POLICY_PATH
allow bootanim mediaserver:binder { call transfer };

maqi@QiMa:~/android13-r44$ # 3. 查询 file 权限
maqi@QiMa:~/android13-r44$ sesearch --allow -s bootanim -t mnt_product_file -c file $POLICY_PATH
allow bootanim mnt_product_file:file { getattr ioctl lock map open read watch watch_reads };

好的! SE检查完毕

推送precompiled_sepolicy到设备

bash 复制代码
adb root
adb remount

adb push out/target/product/emulator_car_x86_64/vendor/etc/selinux/. /vendor/etc/selinux/
adb push out/target/product/emulator_car_x86_64/system/etc/selinux/. /system/etc/selinux/
adb push out/target/product/emulator_car_x86_64/product/etc/selinux/. /product/etc/selinux/
adb reboot

附件代码diff

附件SE配置 说明

MP4 开机动画 SELinux 权限清单

Android 13 (AOSP) 主线自带,共 3 条规则 ,分布在 2 套策略副本


涉及的策略文件(2 套副本)

策略副本 文件路径
当前策略 system/sepolicy/public/bootanim.te
system/sepolicy/private/bootanim.te
API 33 冻结 system/sepolicy/prebuilts/api/33.0/public/bootanim.te
system/sepolicy/prebuilts/api/33.0/private/bootanim.te

每份副本的内容完全一致(同一笔 commit 同步写入)。修改时必须同步更新所有 2 套副本,否则 m selinux_policy 编译时会因 API 冻结校验失败。


规则 1:文件系统权限

文件 行号 规则
private/bootanim.te 22-23 r_dir_file(bootanim, mnt_product_file)
prebuilts/api/33.0/private/bootanim.te 22-23 同上

含义: 允许 bootanim 读取 /product/media/ 目录及下面的 bootanimation.mp4

说明:

  • r_dir_file 是 SELinux 宏,展开等价于:
    • allow bootanim mnt_product_file:dir r_dir_perms; --- 目录搜索/读
    • allow bootanim mnt_product_file:file r_file_perms; --- 文件读取
  • MP4 文件路径固定为 /product/media/bootanimation.mp4/oem/media/bootanimation.mp4/system/media/bootanimation.mp4,当前策略只覆盖了 /product/media/(第一优先级路径)。

规则 2:Service Manager 权限

文件 行号 规则
public/bootanim.te 49 allow bootanim mediaserver_service:service_manager find;
prebuilts/api/33.0/public/bootanim.te 49 同上

含义: 允许 bootanim 在 servicemanager 中查找 media.player 服务。

说明:

  • 代码中通过 defaultServiceManager()->checkService(String16("media.player")) 调用。
  • 这是 waitForMediaCodecs() 中轮询检测的服务名。

规则 3:Binder IPC 权限

文件 行号 规则
public/bootanim.te 48 binder_call(bootanim, mediaserver)
prebuilts/api/33.0/public/bootanim.te 48 同上

含义: 允许 bootanim 通过 Binder 向 mediaserver 进程发起 IPC 调用。

说明:

  • binder_call 是宏,展开为:
    • allow bootanim mediaserver:fd use;
    • allow bootanim mediaserver:binder { call transfer };
  • 这是 MediaPlayer 所有 API(setDataSourcepreparestartstop 等)的底层通信基础。

规则 4:mediaserver → bootanim 反向权限

Android 13 主线不包含以下规则,需要手动添加。

system/sepolicy/public/mediaserver.te 及对应的 API 冻结副本中添加:

文件 规则 含义
system/sepolicy/public/mediaserver.te allow mediaserver bootanim:fd use; 允许 mediaserver 使用 bootanim 传过来的文件描述符
allow mediaserver bootanim:binder call; 允许 mediaserver 调用 bootanim 的 Binder 接口

说明:

  • 这两条是规则3的反向补充 。完整通信需要双向权限:
    • 规则3:binder_call(bootanim, mediaserver) → bootanim 可调 mediaserver
    • 规则4:allow mediaserver bootanim:binder call → mediaserver 可调 bootanim
  • fd use 允许 mediaserver 接收 bootanim 通过 Binder 传递的 ParcelFileDescriptor
  • 可以添加到 mediaserver.te 文件末尾的 neverallow 规则之前,与其他 fd use 规则(hal_graphics_allocator:fd usesystem_server:fd usevold:fd use 等)放在一起。

涉及的策略文件(2 套副本):

策略副本 文件路径
当前策略 system/sepolicy/public/mediaserver.te
API 33 冻结 system/sepolicy/prebuilts/api/33.0/public/mediaserver.te

如果 API 28-32 的冻结副本也需要支持 MP4 boot animation,应同步添加。


汇总

复制代码
-----------------------------------------------------------------------------------
 策略副本                       文件           权限                    用途
-----------------------------------------------------------------------------------
 system/sepolicy/               public/        binder_call(bootanim,   Binder IPC
                                 bootanim.te    mediaserver)
 
                                public/        allow bootanim          查找 media.player
                                 bootanim.te    mediaserver_service:    服务
                                                service_manager find;

                                private/       r_dir_file(bootanim,    读 /product/media/
                                 bootanim.te    mnt_product_file)       *.mp4

 prebuilts/api/33.0/            public/        同上 (行号相同)
                                 bootanim.te
                                private/        同上 (行号相同)
                                 bootanim.te
-----------------------------------------------------------------------------------

⚠️ 注意:这 3 条规则已经是 不需要手动添加。 如果模拟器上 waitForMediaCodecs 等 90 秒才超时,是SELinux 权限问题, 或模拟器根本没有注册 media.player 这个服务名。

附件源码

Android.bp

加入libmedia

bash 复制代码
cc_library_shared {
    name: "libbootanimation",
    defaults: ["bootanimation_defaults"],

    srcs: ["BootAnimation.cpp"],

    shared_libs: [
        "libui",
        "libjnigraphics",
        "libEGL",
        "libGLESv2",
        "libgui",
        "libmedia",
    ],
}

Animation.h

bash 复制代码
    // MP4 boot animation support
    static const char* findBootAnimationMp4();
    bool playMp4(const char* filePath);

Animation.cpp

c 复制代码
// MP4 boot animation paths (checked before the traditional zip paths)
    static const char *BOOTANIMATION_MP4_FILES[] = {
            "/product/media/bootanimation.mp4",
            "/oem/media/bootanimation.mp4",
            "/system/media/bootanimation.mp4",
            nullptr,
    };

// Simple listener to track MediaPlayer playback completion and errors.
    class Mp4PlaybackListener : public MediaPlayerListener {
    public:
        bool completed = false;
        bool errored = false;
        int errorCode = 0;

        virtual void notify(int msg, int /*ext1*/, int /*ext2*/, const Parcel * /*obj*/) {
            if (msg == MEDIA_PLAYBACK_COMPLETE) {
                completed = true;
            } else if (msg == MEDIA_ERROR) {
                errored = true;
                errorCode = -100;
            }
        }
    };

    const char *BootAnimation::findBootAnimationMp4() {
        for (int i = 0; BOOTANIMATION_MP4_FILES[i] != nullptr; i++) {
            if (access(BOOTANIMATION_MP4_FILES[i], R_OK) == 0) {
                ALOGD("Found MP4 boot animation: %s", BOOTANIMATION_MP4_FILES[i]);
                return BOOTANIMATION_MP4_FILES[i];
            }
        }
        return nullptr;
    }
    static bool isEmulator() {
        char value[PROPERTY_VALUE_MAX];
        property_get("ro.kernel.qemu", value, "0");
        return atoi(value) == 1;
    }

    static bool waitForMediaCodecs(int maxWaitMs) {
        // 模拟器没有硬件 AVC decoder,直接跳过 MP4
        if (isEmulator()) {
            ALOGD("Emulator detected, skipping MP4 boot animation");
//            return false;
        }
        sp<IServiceManager> sm = defaultServiceManager();
        for (int waited = 0; waited < maxWaitMs; waited += 200) {
            sp<IBinder> binder = sm->checkService(String16("media.player"));
            if (binder != nullptr) {
                ALOGD("media.player service available after %dms", waited);
                usleep(500000);
                return true;
            }
            ALOGD("Waiting for media.player service... %dms", waited);
            usleep(200000);
        }
        SLOGE("media.player service not available after %dms", maxWaitMs);
        return false;
    }

    // ============================================================
    // playMp4: 使用 MediaPlayer 播放 MP4 开机动画
    // 参数: filePath - MP4 文件的绝对路径
    // 返回值: false = 播放完成或失败(需要回退到 ZIP 动画)
    // ============================================================
    bool BootAnimation::playMp4(const char* filePath) {
        ALOGD("Playing MP4 boot animation: %s", filePath);  // 日志:开始播放 MP4

        // 1. 销毁旧的 EGL 上下文/表面(之前的 ZIP 动画使用 GLES 渲染)
        eglMakeCurrent(mDisplay, EGL_NO_SURFACE, EGL_NO_SURFACE, EGL_NO_CONTEXT); // 解除当前 EGL 绑定
        eglDestroySurface(mDisplay, mSurface);      // 销毁 EGL 窗口表面
        eglDestroyContext(mDisplay, mContext);       // 销毁 EGL 上下文

        // 2. 等待 mediaserver 的 media.player 服务就绪(最多等 180 秒)
        //    注意:media.player 可能启动较慢(如模拟器上需 90 秒),
        //    这期间 service.bootanim.exit 可能已被 WMS 置为 "1",
        //    但我们必须在播放开始后再响应退出,否则 MP4 永远放不出来。
        if (!waitForMediaCodecs(10000*18)) {
            SLOGE("Media not available, falling back to ZIP");  // 超时,回退到 ZIP
            goto fallback_to_zip;                               // 跳转到 GLES 环境恢复逻辑
        }

        // 3. 最多重试 5 次(prepare/解码可能暂时失败)
        for (int attempt = 0; attempt < 5; attempt++) {
            sp<MediaPlayer> mp = new MediaPlayer();             // 创建 MediaPlayer 实例
            sp<Mp4PlaybackListener> listener = new Mp4PlaybackListener(); // 创建播放状态监听器
            mp->setListener(listener);                          // 绑定监听器

            // 4. 打开 MP4 文件并设置为 MediaPlayer 的数据源
            int fd = open(filePath, O_RDONLY);                  // 以只读方式打开文件
            if (fd < 0) {
                SLOGE("Failed to open MP4: %s", filePath);      // 文件不存在或无法读取
                goto fallback_to_zip;
            }
            struct stat sb;                                     // 获取文件大小
            fstat(fd, &sb);
            status_t err = mp->setDataSource(fd, 0, sb.st_size); // 设置数据源(文件描述符 + 偏移 + 大小)
            close(fd);                                          // 关闭 fd(MediaPlayer 内部已复制)

            if (err != NO_ERROR) {
                SLOGE("setDataSource failed: %d", err);         // MediaPlayer 不接受该文件
                goto fallback_to_zip;
            }

            // 5. 关联 SurfaceFlinger 的 BufferQueue,让视频渲染到启动动画图层
            err = mp->setVideoSurfaceTexture(
                    mFlingerSurface->getIGraphicBufferProducer());
            if (err != NO_ERROR) {
                SLOGE("setVideoSurfaceTexture failed: %d", err); // Surface 绑定失败
                goto fallback_to_zip;
            }

            // 6. 准备播放(setDataSource -> prepare 是异步的准备流程)
            mp->setLooping(true);                               // 设置循环播放
            err = mp->prepare();                                // 同步准备(解析文件头、初始化解码器)
            if (err != NO_ERROR) {
                ALOGW("prepare() failed (attempt %d/5): %d", attempt + 1, err); // 准备失败,可能解码器未就绪
                mp->reset();                                    // 重置 MediaPlayer 状态
                usleep(1000000);                                // 等待 1 秒后重试
                continue;
            }

            // 7. 开始播放
            mCallbacks->init({});                               // 通知回调初始化(清除之前的动画状态)
            mp->start();                                        // 启动 MP4 播放
            ALOGD("MP4 boot animation started (attempt %d)", attempt + 1); // 播放成功

            // 8. 短等 100ms 后检查解码器是否立刻报错(异步初始化失败检测)
            usleep(100000);
            if (listener->errored) {
                ALOGW("Async decoder failed immediately (attempt %d/5), retrying in 1s",
                      attempt + 1);                             // 解码器瞬间报错,重试
                mp->stop();
                mp->reset();
                usleep(1000000);                                // 等 1 秒
                continue;
            }

            // 9. 主循环:播放 MP4,同时轮询退出信号
            //    设计要点:
            //    - media.player 可能启动很慢(如 90 秒),导致进入此循环时
            //      service.bootanim.exit 早已被置 "1"。
            //    - 如果不给最低播放时间,checkExit() 会在第一个 50ms 周期
            //      就触发 requestExit(),MP4 几乎无机会渲染。
            //    - 因此加入 kMinPlayMs 保障:至少播放 N 毫秒后才响应退出。
            //    - 改为直接用 true 做 while 条件,手动控制 break 逻辑。
            {
                const int kMinPlayMs = 3000;  // 最低保障播放 3 秒
                int elapsedMs = 0;
                while (true) {
                    if (listener->errored) break;               // 播放出错则跳出
                    usleep(50000);                              // 每 50ms 检查一次
                    elapsedMs += 50;
                    // 超过最低保障时间后才开始响应退出请求
                    if (elapsedMs >= kMinPlayMs) {
                        checkExit();
                        if (exitPending()) break;
                    }
                }
            }

            // 10. 停止播放
            mp->stop();                                         // 停止播放
            mp->reset();                                        // 重置(释放解码器资源)

            // 11. 如果是正常退出(动画播放完毕),清理并结束进程
            if (exitPending()) {
                ALOGD("MP4 boot animation finished");           // 日志:播放结束
                mCallbacks->shutdown();                         // 通知回调关闭
                mFlingerSurface.clear();                        // 释放 Surface
                mFlingerSurfaceControl.clear();                 // 释放 SurfaceControl
                eglTerminate(mDisplay);                         // 终止 EGL 显示连接
                eglReleaseThread();                             // 释放线程 EGL 资源
                IPCThreadState::self()->stopProcess();          // 停止本进程(bootanim 退出)
                return false;
            }

            // 12. 如果不是退出(可能是出错循环重试),等 1 秒后进入下一次尝试
            usleep(1000000);
        }

        // 13. 5 次重试全部失败,记录错误并回退到 ZIP
        SLOGE("MP4 playback failed after all attempts, falling back to ZIP");

        fallback_to_zip:
        {
            // 14. 恢复 GLES 渲染环境,用于回退到 ZIP 动画播放
            //     必须指定 GLES2,否则 glCreateShader 等函数指针为 null 导致 SIGSEGV
            EGLConfig config = getEglConfig(mDisplay);                     // 获取 EGL 配置
            EGLint contextAttributes[] = {EGL_CONTEXT_CLIENT_VERSION, 2, EGL_NONE}; // GLES 2.0
            mContext = eglCreateContext(mDisplay, config, nullptr, contextAttributes); // 重建 EGL 上下文
            mSurface = eglCreateWindowSurface(mDisplay, config,            // 重建 EGL 窗口表面
                                              mFlingerSurface.get(), nullptr);
            eglMakeCurrent(mDisplay, mSurface, mSurface, mContext);        // 绑定到当前线程
        }
        return false;   // 返回 false,告知调用方需要回退到 ZIP 动画
    }
相关推荐
加农炮手Jinx2 小时前
Flutter for OpenHarmony:pub_updater 命令行工具自动更新专家(DevOps 运维必备) 深度解析与鸿蒙适配指南
android·运维·网络·flutter·华为·harmonyos·devops
2601_957418802 小时前
告别OTG碎片化!Android MTP协议深度解析与高性能通信方案
android
故渊at2 小时前
第二板块:Android 四大组件标准化学理 | 第七篇:Activity 页面载体与任务栈算法
android·算法·生命周期·activity·任务栈
QING6183 小时前
Kotlin 协程新手指南 —— 协程上下文与调度器
android·kotlin·android jetpack
潘潘潘4 小时前
Android JAVA Socket 知识梳理
android
00后程序员张4 小时前
Jenkins 自动上传 IPA 到 App Store 把发布步骤融入 CI/CD
android·ios·小程序·https·uni-app·iphone·webview
Gary Studio4 小时前
复杂 SoC(RK3568)PCB 布局的五步
android·linux·硬件
plainGeekDev5 小时前
HttpURLConnection → OkHttp + Kotlin
android·java·kotlin