From 4b9a19a8f865970e0079f431c8c2c2a8e4333ae9 Mon Sep 17 00:00:00 2001 From: Leon Scroggins III Date: Tue, 3 Dec 2013 15:06:30 -0500 Subject: Call SkBitmap::config() instead of ::getConfig() getConfig() has been deprecated. Change-Id: I32066256ab82ac4760c752c80856d1b56d291fae --- cmds/bootanimation/BootAnimation.cpp | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 0f610e9..8c2931f 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -123,7 +123,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, glGenTextures(1, &texture->name); glBindTexture(GL_TEXTURE_2D, texture->name); - switch (bitmap.getConfig()) { + switch (bitmap.config()) { case SkBitmap::kA8_Config: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p); @@ -181,7 +181,7 @@ status_t BootAnimation::initTexture(void* buffer, size_t len) if (tw < w) tw <<= 1; if (th < h) th <<= 1; - switch (bitmap.getConfig()) { + switch (bitmap.config()) { case SkBitmap::kARGB_8888_Config: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, -- cgit v1.1 From 5a74e2df57acb93b61568b90b048016046ef4512 Mon Sep 17 00:00:00 2001 From: Narayan Kamath Date: Tue, 3 Dec 2013 13:16:03 +0000 Subject: Reimplement ZipFileRO in terms of libziparchive. This lets us share zip archive processing code with both the runtime (Art, dalvik) and critical java code (StrictJarFile). This change also moves several utility methods to ZipUtils and dedups code across several zip inflation methods. One of the side effects of this change is that several processing loops are now O(n) instead of O(n^2). bug: 10193060 (cherry picked from commit afd31e08299008fdc5c2813f21b2573f29dc53df) Change-Id: I86a2c528575d4a3ca86b94be068dcdc3c17c2ed6 --- cmds/bootanimation/BootAnimation.cpp | 98 ++++++++++++++++++++++-------------- cmds/bootanimation/BootAnimation.h | 3 +- 2 files changed, 60 insertions(+), 41 deletions(-) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 8c2931f..9085dc3 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -63,14 +63,19 @@ extern "C" int clock_nanosleep(clockid_t clock_id, int flags, namespace android { +static const int ANIM_ENTRY_NAME_MAX = 256; + // --------------------------------------------------------------------------- -BootAnimation::BootAnimation() : Thread(false) +BootAnimation::BootAnimation() : Thread(false), mZip(NULL) { mSession = new SurfaceComposerClient(); } BootAnimation::~BootAnimation() { + if (mZip != NULL) { + delete mZip; + } } void BootAnimation::onFirstRef() { @@ -86,7 +91,7 @@ sp BootAnimation::session() const { } -void BootAnimation::binderDied(const wp& who) +void BootAnimation::binderDied(const wp&) { // woah, surfaceflinger died! ALOGD("SurfaceFlinger died, exiting..."); @@ -268,8 +273,6 @@ status_t BootAnimation::readyToRun() { mFlingerSurfaceControl = control; mFlingerSurface = s; - 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]; @@ -277,16 +280,17 @@ status_t BootAnimation::readyToRun() { bool encryptedAnimation = atoi(decrypt) != 0 || !strcmp("trigger_restart_min_framework", decrypt); + ZipFileRO* zipFile = NULL; if ((encryptedAnimation && (access(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE, R_OK) == 0) && - (mZip.open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE) == NO_ERROR)) || + ((zipFile = ZipFileRO::open(SYSTEM_ENCRYPTED_BOOTANIMATION_FILE)) != NULL)) || ((access(USER_BOOTANIMATION_FILE, R_OK) == 0) && - (mZip.open(USER_BOOTANIMATION_FILE) == NO_ERROR)) || + ((zipFile = ZipFileRO::open(USER_BOOTANIMATION_FILE)) != NULL)) || ((access(SYSTEM_BOOTANIMATION_FILE, R_OK) == 0) && - (mZip.open(SYSTEM_BOOTANIMATION_FILE) == NO_ERROR))) { - mAndroidAnimation = false; + ((zipFile = ZipFileRO::open(SYSTEM_BOOTANIMATION_FILE)) != NULL))) { + mZip = zipFile; } return NO_ERROR; @@ -295,7 +299,9 @@ status_t BootAnimation::readyToRun() { bool BootAnimation::threadLoop() { bool r; - if (mAndroidAnimation) { + // We have no bootanimation file, so we use the stock android logo + // animation. + if (mZip == NULL) { r = android(); } else { r = movie(); @@ -392,11 +398,14 @@ void BootAnimation::checkExit() { bool BootAnimation::movie() { - ZipFileRO& zip(mZip); + ZipEntryRO desc = mZip->findEntryByName("desc.txt"); + ALOGE_IF(!desc, "couldn't find desc.txt"); + if (!desc) { + return false; + } - size_t numEntries = zip.getNumEntries(); - ZipEntryRO desc = zip.findEntryByName("desc.txt"); - FileMap* descMap = zip.createEntryFileMap(desc); + FileMap* descMap = mZip->createEntryFileMap(desc); + mZip->releaseEntry(desc); ALOGE_IF(!descMap, "descMap is null"); if (!descMap) { return false; @@ -415,7 +424,7 @@ bool BootAnimation::movie() String8 line(s, endl - s); const char* l = line.string(); int fps, width, height, count, pause; - char path[256]; + char path[ANIM_ENTRY_NAME_MAX]; char pathType; if (sscanf(l, "%d %d %d", &width, &height, &fps) == 3) { //LOGD("> w=%d, h=%d, fps=%d", width, height, fps); @@ -438,28 +447,37 @@ bool BootAnimation::movie() // read all the data structures const size_t pcount = animation.parts.size(); - for (size_t i=0 ; i 0) { - for (int j=0 ; jstartIteration(&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 ; jgetEntryInfo(entry, &method, NULL, NULL, NULL, NULL, NULL)) { + if (method == ZipFileRO::kCompressStored) { + FileMap* map = mZip->createEntryFileMap(entry); + if (map) { + Animation::Frame frame; + frame.name = leaf; + frame.map = map; + Animation::Part& part(animation.parts.editItemAt(j)); + part.frames.add(frame); } } } @@ -468,6 +486,8 @@ bool BootAnimation::movie() } } + mZip->endIteration(cookie); + // clear screen glShadeModel(GL_FLAT); glDisable(GL_DITHER); @@ -494,7 +514,7 @@ bool BootAnimation::movie() Region clearReg(Rect(mWidth, mHeight)); clearReg.subtractSelf(Rect(xc, yc, xc+animation.width, yc+animation.height)); - for (int i=0 ; i mFlingerSurfaceControl; sp mFlingerSurface; - bool mAndroidAnimation; - ZipFileRO mZip; + ZipFileRO *mZip; }; // --------------------------------------------------------------------------- -- cgit v1.1 From eece0dda56ae29fff6e9003df97594f6ac50b6e2 Mon Sep 17 00:00:00 2001 From: Derek Sollenberger Date: Thu, 27 Feb 2014 14:31:29 -0500 Subject: Fix includes so that they no longer rely on the global Skia includes directories. bug:13225538 Change-Id: Ia5d816dc665f81c7985f21036af4fd0a63c560cf --- cmds/bootanimation/Android.mk | 3 --- cmds/bootanimation/BootAnimation.cpp | 6 +++--- 2 files changed, 3 insertions(+), 6 deletions(-) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index d5ff84e..a4e2718 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -19,9 +19,6 @@ LOCAL_SHARED_LIBRARIES := \ libGLESv1_CM \ libgui -LOCAL_C_INCLUDES := \ - $(call include-path-for, corecg graphics) - LOCAL_MODULE:= bootanimation diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 125bea6..e12e961 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -42,9 +42,9 @@ #include #include -#include -#include -#include +#include +#include +#include #include #include -- cgit v1.1 From c749b44086722217526f220f3656ba06b2e8cea0 Mon Sep 17 00:00:00 2001 From: Andy McFadden Date: Wed, 5 Mar 2014 15:36:58 -0800 Subject: FramebufferNativeWindow.h is obsolete Change-Id: I3c645af7247a520e28d337e567f326375558519a --- cmds/bootanimation/BootAnimation.cpp | 1 - 1 file changed, 1 deletion(-) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index e12e961..41afa39 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -36,7 +36,6 @@ #include #include #include -#include #include #include -- cgit v1.1 From 28f0877073e0ebc59f1eeeb6e0d54b614b9d3fa5 Mon Sep 17 00:00:00 2001 From: Jeff Sharkey Date: Wed, 16 Apr 2014 09:41:58 -0700 Subject: Allow custom wallpaper and boot animation. Add new "ro.config.wallpaper" and "ro.config.wallpaper_component" properties which may be defined outside of the bundled framework resources. Falls back to bundled resources when properties are undefined. Also look for boot animation under OEM partition. Bug: 13340779 Change-Id: Ibdc9935dbdaae3319bf63b40573de0503d82ae67 --- cmds/bootanimation/BootAnimation.cpp | 4 ++++ 1 file changed, 4 insertions(+) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 41afa39..1780e03 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -51,6 +51,7 @@ #include "BootAnimation.h" +#define OEM_BOOTANIMATION_FILE "/oem/media/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" @@ -283,6 +284,9 @@ status_t BootAnimation::readyToRun() { (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; -- cgit v1.1 From 42a1d08df7d417fd4e67eabc91ff05ee77fd9995 Mon Sep 17 00:00:00 2001 From: Mike Reed Date: Mon, 7 Jul 2014 18:06:18 -0400 Subject: SkBitmap::Config is deprecated, use SkColorType Change-Id: Ida181d2aac760072cf2d01409edac37699dea216 --- cmds/bootanimation/BootAnimation.cpp | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 3dc024e..e659b2b 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -108,7 +108,7 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, return NO_INIT; SkBitmap bitmap; SkImageDecoder::DecodeMemory(asset->getBuffer(false), asset->getLength(), - &bitmap, SkBitmap::kNo_Config, SkImageDecoder::kDecodePixels_Mode); + &bitmap, kUnknown_SkColorType, SkImageDecoder::kDecodePixels_Mode); asset->close(); delete asset; @@ -127,20 +127,20 @@ status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, glGenTextures(1, &texture->name); glBindTexture(GL_TEXTURE_2D, texture->name); - switch (bitmap.config()) { - case SkBitmap::kA8_Config: + switch (bitmap.colorType()) { + case kAlpha_8_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_ALPHA, w, h, 0, GL_ALPHA, GL_UNSIGNED_BYTE, p); break; - case SkBitmap::kARGB_4444_Config: + case kARGB_4444_SkColorType: 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: + case kN32_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, w, h, 0, GL_RGBA, GL_UNSIGNED_BYTE, p); break; - case SkBitmap::kRGB_565_Config: + case kRGB_565_SkColorType: glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, w, h, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, p); break; @@ -166,7 +166,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) if (codec) { codec->setDitherImage(false); codec->decode(&stream, &bitmap, - SkBitmap::kARGB_8888_Config, + kN32_SkColorType, SkImageDecoder::kDecodePixels_Mode); delete codec; } @@ -190,8 +190,8 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) if (tw < w) tw <<= 1; if (th < h) th <<= 1; - switch (bitmap.config()) { - case SkBitmap::kARGB_8888_Config: + switch (bitmap.colorType()) { + case kN32_SkColorType: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, tw, th, 0, GL_RGBA, GL_UNSIGNED_BYTE, 0); @@ -203,7 +203,7 @@ status_t BootAnimation::initTexture(const Animation::Frame& frame) } break; - case SkBitmap::kRGB_565_Config: + case kRGB_565_SkColorType: if (tw != w || th != h) { glTexImage2D(GL_TEXTURE_2D, 0, GL_RGB, tw, th, 0, GL_RGB, GL_UNSIGNED_SHORT_5_6_5, 0); -- cgit v1.1 From 083b84cf8880bb7f0d2c2dc1b45c7d610656dbd1 Mon Sep 17 00:00:00 2001 From: Jesse Hall Date: Mon, 22 Sep 2014 10:51:09 -0700 Subject: bootanimation: allow animation to specify background color Some OEMs want to have a bootanimation (or parts of it) displayed on a color other than black. They currently just use full-screen frames for that, which wastes lots of memory and bandwidth. This change allows each part of the animation to specify a background color that will be applied outside of the frame images; if unspecified the background will be black as it is now. Bug: 16635599 Change-Id: Ibf008fc75c5aad891c86ba9e4ec0879b7a61b8bd --- cmds/bootanimation/BootAnimation.cpp | 45 +++++++++++++++++++++++++++++++++--- cmds/bootanimation/BootAnimation.h | 1 + 2 files changed, 43 insertions(+), 3 deletions(-) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index e659b2b..0892311 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -397,6 +397,31 @@ void BootAnimation::checkExit() { } } +// Parse a color represented as an HTML-style 'RRGGBB' string: each pair of +// characters in str is a hex number in [0, 255], which are converted to +// floating point values in the range [0.0, 1.0] and placed in the +// corresponding elements of color. +// +// If the input string isn't valid, parseColor returns false and color is +// left unchanged. +static bool parseColor(const char str[7], float color[3]) { + float tmpColor[3]; + for (int i = 0; i < 3; i++) { + int val = 0; + for (int j = 0; j < 2; j++) { + val *= 16; + char c = str[2*i + j]; + if (c >= '0' && c <= '9') val += c - '0'; + else if (c >= 'A' && c <= 'F') val += (c - 'A') + 10; + else if (c >= 'a' && c <= 'f') val += (c - 'a') + 10; + else return false; + } + tmpColor[i] = static_cast(val) / 255.0f; + } + memcpy(color, tmpColor, sizeof(tmpColor)); + return true; +} + bool BootAnimation::movie() { ZipEntryRO desc = mZip->findEntryByName("desc.txt"); @@ -427,20 +452,28 @@ bool BootAnimation::movie() 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) { - //LOGD("> w=%d, h=%d, fps=%d", width, height, fps); + // 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); + 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; + 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); } @@ -526,6 +559,12 @@ bool BootAnimation::movie() if(exitPending() && !part.playUntilComplete) break; + glClearColor( + part.backgroundColor[0], + part.backgroundColor[1], + part.backgroundColor[2], + 1.0f); + for (size_t j=0 ; j frames; bool playUntilComplete; + float backgroundColor[3]; }; int fps; int width; -- cgit v1.1 From ebf9a0d8a888042c16ec0cb6dd8419f18038663f Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Thu, 2 Oct 2014 16:08:47 -0700 Subject: Add support for playing audio during bootanimation The bootanimation daemon will play 16 bit WAV files contained in bootanimation.zip For this to work, the bootanimation.zip must contain an audio_conf.txt file, which contains parameters to be used for the tinyalsa pcm_open call as well as mixer parameters to set before attempting to play the sound. If the bootanimation finds an audio_conf.txt file, then it will look for a file named "audio.wav" in each of the part subdirectories. If audio.wav is found, it will play that WAV file starting at the beginning of that part. The code for this is based on the tinyplay utility in tinyalsa. The audio_conf.txt and must begin with the following header: card= device= period_size= period_count= This header is followed by zero or more mixer settings, each with the format: mixer "" = Since mixer names can contain spaces, the name must be enclosed in double quotes. The values in the value list can be integers, booleans (represented by 0 or 1) or strings for enum values. Finally I should mention that this change is not the right approach. Instead of going straight to ALSA we should be using the mediaserver instead. But mediaserver isn't ready in time due to interactions with the system server, and there isn't time to fix this for the current release. We need to fix that for the next one. Bug: 17674304 Change-Id: Ic391ade61c941d0a24f4d64fe005ac9375a23fa9 --- cmds/bootanimation/Android.mk | 6 +- cmds/bootanimation/AudioPlayer.cpp | 311 +++++++++++++++++++++++++++++++++++ cmds/bootanimation/AudioPlayer.h | 47 ++++++ cmds/bootanimation/BootAnimation.cpp | 69 ++++++-- cmds/bootanimation/BootAnimation.h | 6 +- 5 files changed, 421 insertions(+), 18 deletions(-) create mode 100644 cmds/bootanimation/AudioPlayer.cpp create mode 100644 cmds/bootanimation/AudioPlayer.h (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/Android.mk b/cmds/bootanimation/Android.mk index c4fe6cf..d6ecbe3 100644 --- a/cmds/bootanimation/Android.mk +++ b/cmds/bootanimation/Android.mk @@ -3,10 +3,13 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ bootanimation_main.cpp \ + AudioPlayer.cpp \ BootAnimation.cpp LOCAL_CFLAGS += -DGL_GLEXT_PROTOTYPES -DEGL_EGLEXT_PROTOTYPES +LOCAL_C_INCLUDES += external/tinyalsa/include + LOCAL_SHARED_LIBRARIES := \ libcutils \ liblog \ @@ -17,7 +20,8 @@ LOCAL_SHARED_LIBRARIES := \ libskia \ libEGL \ libGLESv1_CM \ - libgui + libgui \ + libtinyalsa LOCAL_MODULE:= bootanimation diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp new file mode 100644 index 0000000..a2ee7ea --- /dev/null +++ b/cmds/bootanimation/AudioPlayer.cpp @@ -0,0 +1,311 @@ +/* + * Copyright (C) 2014 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_NDEBUG 0 +#define LOG_TAG "BootAnim_AudioPlayer" + +#include "AudioPlayer.h" + +#include +#include +#include +#include + +#define ID_RIFF 0x46464952 +#define ID_WAVE 0x45564157 +#define ID_FMT 0x20746d66 +#define ID_DATA 0x61746164 + +// Maximum line length for audio_conf.txt +// We only accept lines less than this length to avoid overflows using sscanf() +#define MAX_LINE_LENGTH 1024 + +struct riff_wave_header { + uint32_t riff_id; + uint32_t riff_sz; + uint32_t wave_id; +}; + +struct chunk_header { + uint32_t id; + uint32_t sz; +}; + +struct chunk_fmt { + uint16_t audio_format; + uint16_t num_channels; + uint32_t sample_rate; + uint32_t byte_rate; + uint16_t block_align; + uint16_t bits_per_sample; +}; + + +namespace android { + +AudioPlayer::AudioPlayer() + : mCard(-1), + mDevice(-1), + mPeriodSize(0), + mPeriodCount(0), + mCurrentFile(NULL) +{ +} + +AudioPlayer::~AudioPlayer() { +} + +static bool setMixerValue(struct mixer* mixer, const char* name, const char* values) +{ + if (!mixer) { + ALOGE("no mixer in setMixerValue"); + return false; + } + struct mixer_ctl *ctl = mixer_get_ctl_by_name(mixer, name); + if (!ctl) { + ALOGE("mixer_get_ctl_by_name failed for %s", name); + return false; + } + + enum mixer_ctl_type type = mixer_ctl_get_type(ctl); + int numValues = mixer_ctl_get_num_values(ctl); + int intValue; + char stringValue[MAX_LINE_LENGTH]; + + for (int i = 0; i < numValues && values; i++) { + // strip leading space + while (*values == ' ') values++; + if (*values == 0) break; + + switch (type) { + case MIXER_CTL_TYPE_BOOL: + case MIXER_CTL_TYPE_INT: + if (sscanf(values, "%d", &intValue) == 1) { + if (mixer_ctl_set_value(ctl, i, intValue) != 0) { + ALOGE("mixer_ctl_set_value failed for %s %d", name, intValue); + } + } else { + ALOGE("Could not parse %s as int for %d", intValue, name); + } + break; + case MIXER_CTL_TYPE_ENUM: + if (sscanf(values, "%s", stringValue) == 1) { + if (mixer_ctl_set_enum_by_string(ctl, stringValue) != 0) { + ALOGE("mixer_ctl_set_enum_by_string failed for %s %%s", name, stringValue); + } + } else { + ALOGE("Could not parse %s as enum for %d", stringValue, name); + } + break; + default: + ALOGE("unsupported mixer type %d for %s", type, name); + break; + } + + values = strchr(values, ' '); + } + + return true; +} + + +/* + * Parse the audio configuration file. + * The file is named audio_conf.txt and must begin with the following header: + * + * card= + * device= + * period_size= + * period_count= + * + * This header is followed by zero or more mixer settings, each with the format: + * mixer "" = + * Since mixer names can contain spaces, the name must be enclosed in double quotes. + * The values in the value list can be integers, booleans (represented by 0 or 1) + * or strings for enum values. + */ +bool AudioPlayer::init(const char* config) +{ + int tempInt; + struct mixer* mixer = NULL; + char name[MAX_LINE_LENGTH]; + + for (;;) { + const char* endl = strstr(config, "\n"); + if (!endl) break; + String8 line(config, endl - config); + if (line.length() >= MAX_LINE_LENGTH) { + ALOGE("Line too long in audio_conf.txt"); + return false; + } + const char* l = line.string(); + + if (sscanf(l, "card=%d", &tempInt) == 1) { + ALOGD("card=%d", tempInt); + mCard = tempInt; + + mixer = mixer_open(mCard); + if (!mixer) { + ALOGE("could not open mixer for card %d", mCard); + return false; + } + } else if (sscanf(l, "device=%d", &tempInt) == 1) { + ALOGD("device=%d", tempInt); + mDevice = tempInt; + } else if (sscanf(l, "period_size=%d", &tempInt) == 1) { + ALOGD("period_size=%d", tempInt); + mPeriodSize = tempInt; + } else if (sscanf(l, "period_count=%d", &tempInt) == 1) { + ALOGD("period_count=%d", tempInt); + mPeriodCount = tempInt; + } else if (sscanf(l, "mixer \"%[0-9a-zA-Z _]s\"", name) == 1) { + const char* values = strchr(l, '='); + if (values) { + values++; // skip '=' + ALOGD("name: \"%s\" = %s", name, values); + setMixerValue(mixer, name, values); + } else { + ALOGE("values missing for name: \"%s\"", name); + } + } + config = ++endl; + } + + mixer_close(mixer); + + if (mCard >= 0 && mDevice >= 0) { + return true; + } + + return false; +} + +void AudioPlayer::playFile(struct FileMap* fileMap) { + // stop any currently playing sound + requestExitAndWait(); + + mCurrentFile = fileMap; + run("bootanim audio", PRIORITY_URGENT_AUDIO); +} + +bool AudioPlayer::threadLoop() +{ + struct pcm_config config; + struct pcm *pcm = NULL; + bool moreChunks = true; + const struct chunk_fmt* chunkFmt = NULL; + void* buffer = NULL; + int bufferSize; + const uint8_t* wavData; + size_t wavLength; + const struct riff_wave_header* wavHeader; + + if (mCurrentFile == NULL) { + ALOGE("mCurrentFile is NULL"); + return false; + } + + wavData = (const uint8_t *)mCurrentFile->getDataPtr(); + if (!wavData) { + ALOGE("Could not access WAV file data"); + goto exit; + } + wavLength = mCurrentFile->getDataLength(); + + wavHeader = (const struct riff_wave_header *)wavData; + if (wavLength < sizeof(*wavHeader) || (wavHeader->riff_id != ID_RIFF) || + (wavHeader->wave_id != ID_WAVE)) { + ALOGE("Error: audio file is not a riff/wave file\n"); + goto exit; + } + wavData += sizeof(*wavHeader); + wavLength -= sizeof(*wavHeader); + + do { + const struct chunk_header* chunkHeader = (const struct chunk_header*)wavData; + if (wavLength < sizeof(*chunkHeader)) { + ALOGE("EOF reading chunk headers"); + goto exit; + } + + wavData += sizeof(*chunkHeader); + wavLength -= sizeof(*chunkHeader); + + switch (chunkHeader->id) { + case ID_FMT: + chunkFmt = (const struct chunk_fmt *)wavData; + wavData += chunkHeader->sz; + wavLength -= chunkHeader->sz; + break; + case ID_DATA: + /* Stop looking for chunks */ + moreChunks = 0; + break; + default: + /* Unknown chunk, skip bytes */ + wavData += chunkHeader->sz; + wavLength -= chunkHeader->sz; + } + } while (moreChunks); + + if (!chunkFmt) { + ALOGE("format not found in WAV file"); + goto exit; + } + + + memset(&config, 0, sizeof(config)); + config.channels = chunkFmt->num_channels; + config.rate = chunkFmt->sample_rate; + config.period_size = mPeriodSize; + config.period_count = mPeriodCount; + if (chunkFmt->bits_per_sample != 16) { + ALOGE("only 16 bit WAV files are supported"); + goto exit; + } + config.format = PCM_FORMAT_S16_LE; + + pcm = pcm_open(mCard, mDevice, PCM_OUT, &config); + if (!pcm || !pcm_is_ready(pcm)) { + ALOGE("Unable to open PCM device (%s)\n", pcm_get_error(pcm)); + goto exit; + } + + bufferSize = pcm_frames_to_bytes(pcm, pcm_get_buffer_size(pcm)); + + while (wavLength > 0) { + if (exitPending()) goto exit; + size_t count = bufferSize; + if (count > wavLength) + count = wavLength; + + if (pcm_write(pcm, wavData, count)) { + ALOGE("pcm_write failed (%s)", pcm_get_error(pcm)); + goto exit; + } + wavData += count; + wavLength -= count; + } + +exit: + if (pcm) + pcm_close(pcm); + mCurrentFile->release(); + mCurrentFile = NULL; + return false; +} + +} // namespace android diff --git a/cmds/bootanimation/AudioPlayer.h b/cmds/bootanimation/AudioPlayer.h new file mode 100644 index 0000000..7e82a07 --- /dev/null +++ b/cmds/bootanimation/AudioPlayer.h @@ -0,0 +1,47 @@ +/* + * Copyright (C) 2014 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. + */ + +#ifndef _BOOTANIMATION_AUDIOPLAYER_H +#define _BOOTANIMATION_AUDIOPLAYER_H + +#include + +namespace android { + +class AudioPlayer : public Thread +{ +public: + AudioPlayer(); + virtual ~AudioPlayer(); + bool init(const char* config); + + void playFile(struct FileMap* fileMap); + +private: + virtual bool threadLoop(); + +private: + int mCard; // ALSA card to use + int mDevice; // ALSA device to use + int mPeriodSize; + int mPeriodCount; + + struct FileMap* mCurrentFile; +}; + +} // namespace android + +#endif // _BOOTANIMATION_AUDIOPLAYER_H diff --git a/cmds/bootanimation/BootAnimation.cpp b/cmds/bootanimation/BootAnimation.cpp index 0892311..b2474f2 100644 --- a/cmds/bootanimation/BootAnimation.cpp +++ b/cmds/bootanimation/BootAnimation.cpp @@ -14,6 +14,7 @@ * limitations under the License. */ +#define LOG_NDEBUG 0 #define LOG_TAG "BootAnimation" #include @@ -30,7 +31,6 @@ #include #include #include -#include #include #include @@ -50,6 +50,7 @@ #include #include "BootAnimation.h" +#include "AudioPlayer.h" #define OEM_BOOTANIMATION_FILE "/oem/media/bootanimation.zip" #define SYSTEM_BOOTANIMATION_FILE "/system/media/bootanimation.zip" @@ -99,6 +100,9 @@ void BootAnimation::binderDied(const wp&) // might be blocked on a condition variable that will never be updated. kill( getpid(), SIGKILL ); requestExit(); + if (mAudioPlayer != NULL) { + mAudioPlayer->requestExit(); + } } status_t BootAnimation::initTexture(Texture* texture, AssetManager& assets, @@ -394,6 +398,9 @@ void BootAnimation::checkExit() { int exitnow = atoi(value); if (exitnow) { requestExit(); + if (mAudioPlayer != NULL) { + mAudioPlayer->requestExit(); + } } } @@ -422,26 +429,45 @@ static bool parseColor(const char str[7], float color[3]) { return true; } -bool BootAnimation::movie() +bool BootAnimation::readFile(const char* name, String8& outString) { - ZipEntryRO desc = mZip->findEntryByName("desc.txt"); - ALOGE_IF(!desc, "couldn't find desc.txt"); - if (!desc) { + ZipEntryRO entry = mZip->findEntryByName(name); + ALOGE_IF(!entry, "couldn't find %s", name); + if (!entry) { return false; } - FileMap* descMap = mZip->createEntryFileMap(desc); - mZip->releaseEntry(desc); - ALOGE_IF(!descMap, "descMap is null"); - if (!descMap) { + FileMap* entryMap = mZip->createEntryFileMap(entry); + mZip->releaseEntry(entry); + ALOGE_IF(!entryMap, "entryMap is null"); + if (!entryMap) { return false; } - String8 desString((char const*)descMap->getDataPtr(), - descMap->getDataLength()); - descMap->release(); + outString.setTo((char const*)entryMap->getDataPtr(), entryMap->getDataLength()); + entryMap->release(); + return true; +} + +bool BootAnimation::movie() +{ + String8 desString; + + if (!readFile("desc.txt", desString)) { + 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 @@ -468,6 +494,7 @@ bool BootAnimation::movie() 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; @@ -508,11 +535,16 @@ bool BootAnimation::movie() if (method == ZipFileRO::kCompressStored) { FileMap* map = mZip->createEntryFileMap(entry); if (map) { - Animation::Frame frame; - frame.name = leaf; - frame.map = map; Animation::Part& part(animation.parts.editItemAt(j)); - part.frames.add(frame); + 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); + } } } } @@ -559,6 +591,11 @@ bool BootAnimation::movie() if(exitPending() && !part.playUntilComplete) break; + // only play audio file the first time we animate the part + if (r == 0 && mAudioPlayer != NULL && part.audioFile) { + mAudioPlayer->playFile(part.audioFile); + } + glClearColor( part.backgroundColor[0], part.backgroundColor[1], diff --git a/cmds/bootanimation/BootAnimation.h b/cmds/bootanimation/BootAnimation.h index 72cd62b..f968b25 100644 --- a/cmds/bootanimation/BootAnimation.h +++ b/cmds/bootanimation/BootAnimation.h @@ -21,7 +21,7 @@ #include #include -#include +#include #include #include @@ -30,6 +30,7 @@ class SkBitmap; namespace android { +class AudioPlayer; class Surface; class SurfaceComposerClient; class SurfaceControl; @@ -72,6 +73,7 @@ private: SortedVector frames; bool playUntilComplete; float backgroundColor[3]; + FileMap* audioFile; }; int fps; int width; @@ -82,11 +84,13 @@ private: status_t initTexture(Texture* texture, AssetManager& asset, const char* name); status_t initTexture(const Animation::Frame& frame); bool android(); + bool readFile(const char* name, String8& outString); bool movie(); void checkExit(); sp mSession; + sp mAudioPlayer; AssetManager mAssets; Texture mAndroid[2]; int mWidth; -- cgit v1.1 From 28138587ef4ec3da71c5275ac60ffae03ca69fce Mon Sep 17 00:00:00 2001 From: Mike Lockwood Date: Tue, 7 Oct 2014 14:47:26 -0700 Subject: bootanimation: Tweak parameters for pcm_open for playing boot sounds. Set start_threshold and avail_min to 1/4th period size and stop_threshold to INT_MAX Change-Id: I0a3aac18533709daa86a024cdd6a32086410723a --- cmds/bootanimation/AudioPlayer.cpp | 3 +++ 1 file changed, 3 insertions(+) (limited to 'cmds/bootanimation') diff --git a/cmds/bootanimation/AudioPlayer.cpp b/cmds/bootanimation/AudioPlayer.cpp index a2ee7ea..471b77f 100644 --- a/cmds/bootanimation/AudioPlayer.cpp +++ b/cmds/bootanimation/AudioPlayer.cpp @@ -272,6 +272,9 @@ bool AudioPlayer::threadLoop() config.rate = chunkFmt->sample_rate; config.period_size = mPeriodSize; config.period_count = mPeriodCount; + config.start_threshold = mPeriodSize / 4; + config.stop_threshold = INT_MAX; + config.avail_min = config.start_threshold; if (chunkFmt->bits_per_sample != 16) { ALOGE("only 16 bit WAV files are supported"); goto exit; -- cgit v1.1