diff options
Diffstat (limited to 'cmds')
| -rw-r--r-- | cmds/am/Android.mk | 17 | ||||
| -rw-r--r-- | cmds/am/src/com/android/commands/am/Am.java | 61 | ||||
| -rw-r--r-- | cmds/app_process/app_main.cpp | 2 | ||||
| -rw-r--r-- | cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java | 21 | ||||
| -rw-r--r-- | cmds/dumpstate/dumpstate.c | 63 | ||||
| -rw-r--r-- | cmds/dumpstate/dumpstate.h | 3 | ||||
| -rw-r--r-- | cmds/dumpstate/utils.c | 4 | ||||
| -rwxr-xr-x | cmds/input/src/com/android/commands/input/Input.java | 2 | ||||
| -rw-r--r-- | cmds/installd/Android.mk | 17 | ||||
| -rw-r--r-- | cmds/installd/commands.c | 50 | ||||
| -rw-r--r-- | cmds/pm/src/com/android/commands/pm/Pm.java | 5 | ||||
| -rw-r--r-- | cmds/rawbu/backup.cpp | 1 | ||||
| -rw-r--r-- | cmds/screencap/Android.mk | 8 | ||||
| -rw-r--r-- | cmds/screencap/screencap.cpp | 92 | ||||
| -rw-r--r-- | cmds/screenshot/Android.mk | 16 | ||||
| -rw-r--r-- | cmds/screenshot/screenshot.c | 171 | ||||
| -rw-r--r-- | cmds/servicemanager/service_manager.c | 4 | ||||
| -rw-r--r-- | cmds/stagefright/Android.mk | 56 | ||||
| -rw-r--r-- | cmds/stagefright/recordvideo.cpp | 303 | ||||
| -rw-r--r-- | cmds/stagefright/stagefright.cpp | 124 | ||||
| -rw-r--r-- | cmds/stagefright/stream.cpp | 169 |
21 files changed, 1086 insertions, 103 deletions
diff --git a/cmds/am/Android.mk b/cmds/am/Android.mk index 170849d..8b321b3 100644 --- a/cmds/am/Android.mk +++ b/cmds/am/Android.mk @@ -12,3 +12,20 @@ ALL_PREBUILT += $(TARGET_OUT)/bin/am $(TARGET_OUT)/bin/am : $(LOCAL_PATH)/am | $(ACP) $(transform-prebuilt-to-target) +NOTICE_FILE := NOTICE +files_noticed := bin/am + +# Generate rules for a single file. The argument is the file path relative to +# the installation root +define make-notice-file + +$(TARGET_OUT_NOTICE_FILES)/src/$(1).txt: $(LOCAL_PATH)/$(NOTICE_FILE) + @echo Notice file: $$< -- $$@ + @mkdir -p $$(dir $$@) + @cat $$< >> $$@ + +$(TARGET_OUT_NOTICE_FILES)/hash-timestamp: $(TARGET_OUT_NOTICE_FILES)/src/$(1).txt + +endef + +$(foreach file,$(files_noticed),$(eval $(call make-notice-file,$(file)))) diff --git a/cmds/am/src/com/android/commands/am/Am.java b/cmds/am/src/com/android/commands/am/Am.java index f2aa91f..b073004 100644 --- a/cmds/am/src/com/android/commands/am/Am.java +++ b/cmds/am/src/com/android/commands/am/Am.java @@ -102,6 +102,8 @@ public class Am { sendBroadcast(); } else if (op.equals("profile")) { runProfile(); + } else if (op.equals("dumpheap")) { + runDumpHeap(); } else if (op.equals("monitor")) { runMonitor(); } else { @@ -146,6 +148,31 @@ public class Am { String value = nextArgRequired(); intent.putExtra(key, Integer.valueOf(value)); hasIntentInfo = true; + } else if (opt.equals("--eia")) { + String key = nextArgRequired(); + String value = nextArgRequired(); + String[] strings = value.split(","); + int[] list = new int[strings.length]; + for (int i = 0; i < strings.length; i++) { + list[i] = Integer.valueOf(strings[i]); + } + intent.putExtra(key, list); + hasIntentInfo = true; + } else if (opt.equals("--el")) { + String key = nextArgRequired(); + String value = nextArgRequired(); + intent.putExtra(key, Long.valueOf(value)); + hasIntentInfo = true; + } else if (opt.equals("--ela")) { + String key = nextArgRequired(); + String value = nextArgRequired(); + String[] strings = value.split(","); + long[] list = new long[strings.length]; + for (int i = 0; i < strings.length; i++) { + list[i] = Long.valueOf(strings[i]); + } + intent.putExtra(key, list); + hasIntentInfo = true; } else if (opt.equals("--ez")) { String key = nextArgRequired(); String value = nextArgRequired(); @@ -430,6 +457,28 @@ public class Am { } } + private void runDumpHeap() throws Exception { + boolean managed = !"-n".equals(nextOption()); + String process = nextArgRequired(); + String heapFile = nextArgRequired(); + ParcelFileDescriptor fd = null; + + try { + fd = ParcelFileDescriptor.open( + new File(heapFile), + ParcelFileDescriptor.MODE_CREATE | + ParcelFileDescriptor.MODE_TRUNCATE | + ParcelFileDescriptor.MODE_READ_WRITE); + } catch (FileNotFoundException e) { + System.err.println("Error: Unable to open file: " + heapFile); + return; + } + + if (!mAm.dumpHeap(process, managed, heapFile, fd)) { + throw new AndroidException("HEAP DUMP FAILED on process " + process); + } + } + class MyActivityController extends IActivityController.Stub { final String mGdbPort; @@ -894,8 +943,15 @@ public class Am { " -p <FILE>: write profiling data to <FILE>\n" + " -w: wait for instrumentation to finish before returning\n" + "\n" + + " run a test package against an application: am instrument [flags] <TEST_PACKAGE>/<RUNNER_CLASS>\n" + + " -e <testrunner_flag> <testrunner_value> [,<testrunner_value>]\n" + + " -w wait for the test to finish (required)\n" + + " -r use with -e perf true to generate raw output for performance measurements\n" + + "\n" + " start profiling: am profile <PROCESS> start <FILE>\n" + " stop profiling: am profile <PROCESS> stop\n" + + " dump heap: am dumpheap [flags] <PROCESS> <FILE>\n" + + " -n: dump native heap instead of managed heap\n" + "\n" + " start monitoring: am monitor [--gdb <port>]\n" + " --gdb: start gdbserv on the given port at crash/ANR\n" + @@ -906,7 +962,10 @@ public class Am { " [-e|--es <EXTRA_KEY> <EXTRA_STRING_VALUE> ...]\n" + " [--esn <EXTRA_KEY> ...]\n" + " [--ez <EXTRA_KEY> <EXTRA_BOOLEAN_VALUE> ...]\n" + - " [-e|--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + + " [--ei <EXTRA_KEY> <EXTRA_INT_VALUE> ...]\n" + + " [--el <EXTRA_KEY> <EXTRA_LONG_VALUE> ...]\n" + + " [--eia <EXTRA_KEY> <EXTRA_INT_VALUE>[,<EXTRA_INT_VALUE...]]\n" + + " [--ela <EXTRA_KEY> <EXTRA_LONG_VALUE>[,<EXTRA_LONG_VALUE...]]\n" + " [-n <COMPONENT>] [-f <FLAGS>]\n" + " [--grant-read-uri-permission] [--grant-write-uri-permission]\n" + " [--debug-log-resolution]\n" + diff --git a/cmds/app_process/app_main.cpp b/cmds/app_process/app_main.cpp index 7decf9a..0159edd 100644 --- a/cmds/app_process/app_main.cpp +++ b/cmds/app_process/app_main.cpp @@ -171,9 +171,9 @@ int main(int argc, const char* const argv[]) runtime.start(); } } else { - LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); fprintf(stderr, "Error: no class name or --zygote supplied.\n"); app_usage(); + LOG_ALWAYS_FATAL("app_process: no class name or --zygote supplied."); return 10; } diff --git a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java index 37c8ad0..ac0e410 100644 --- a/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java +++ b/cmds/bmgr/src/com/android/commands/bmgr/Bmgr.java @@ -217,8 +217,7 @@ public final class Bmgr { // The rest of the 'list' options work with a restore session on the current transport try { - String curTransport = mBmgr.getCurrentTransport(); - mRestore = mBmgr.beginRestoreSession(curTransport); + mRestore = mBmgr.beginRestoreSession(null, null); if (mRestore == null) { System.err.println(BMGR_NOT_RUNNING_ERR); return; @@ -349,8 +348,7 @@ public final class Bmgr { private void doRestorePackage(String pkg) { try { - String curTransport = mBmgr.getCurrentTransport(); - mRestore = mBmgr.beginRestoreSession(curTransport); + mRestore = mBmgr.beginRestoreSession(pkg, null); if (mRestore == null) { System.err.println(BMGR_NOT_RUNNING_ERR); return; @@ -378,8 +376,7 @@ public final class Bmgr { try { boolean didRestore = false; - String curTransport = mBmgr.getCurrentTransport(); - mRestore = mBmgr.beginRestoreSession(curTransport); + mRestore = mBmgr.beginRestoreSession(null, null); if (mRestore == null) { System.err.println(BMGR_NOT_RUNNING_ERR); return; @@ -389,11 +386,13 @@ public final class Bmgr { if (err == 0) { observer.waitForCompletion(); sets = observer.sets; - for (RestoreSet s : sets) { - if (s.token == token) { - System.out.println("Scheduling restore: " + s.name); - didRestore = (mRestore.restoreAll(token, observer) == 0); - break; + if (sets != null) { + for (RestoreSet s : sets) { + if (s.token == token) { + System.out.println("Scheduling restore: " + s.name); + didRestore = (mRestore.restoreAll(token, observer) == 0); + break; + } } } } diff --git a/cmds/dumpstate/dumpstate.c b/cmds/dumpstate/dumpstate.c index 0723f67..b1b7715 100644 --- a/cmds/dumpstate/dumpstate.c +++ b/cmds/dumpstate/dumpstate.c @@ -39,6 +39,8 @@ static char cmdline_buf[16384] = "(unknown)"; static const char *dump_traces_path = NULL; +static char screenshot_path[PATH_MAX] = ""; + /* dumps the current system state to stdout */ static void dumpstate() { time_t now = time(NULL); @@ -76,7 +78,13 @@ static void dumpstate() { dump_file("SLAB INFO", "/proc/slabinfo"); dump_file("ZONEINFO", "/proc/zoneinfo"); - run_command("SYSTEM LOG", 20, "logcat", "-v", "time", "-d", "*:v", NULL); + if (screenshot_path[0]) { + LOGI("taking screenshot\n"); + run_command(NULL, 5, "su", "root", "screenshot", screenshot_path, NULL); + LOGI("wrote screenshot: %s\n", screenshot_path); + } + + run_command("SYSTEM LOG", 20, "logcat", "-v", "threadtime", "-d", "*:v", NULL); /* show the traces we collected in main(), if that was done */ if (dump_traces_path != NULL) { @@ -96,16 +104,23 @@ static void dumpstate() { } // dump_file("EVENT LOG TAGS", "/etc/event-log-tags"); - run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "time", "-d", "*:v", NULL); - run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "time", "-d", "*:v", NULL); + run_command("EVENT LOG", 20, "logcat", "-b", "events", "-v", "threadtime", "-d", "*:v", NULL); + run_command("RADIO LOG", 20, "logcat", "-b", "radio", "-v", "threadtime", "-d", "*:v", NULL); - run_command("NETWORK INTERFACES", 10, "netcfg", NULL); + run_command("NETWORK INTERFACES", 10, "su", "root", "netcfg", NULL); dump_file("NETWORK ROUTES", "/proc/net/route"); dump_file("ARP CACHE", "/proc/net/arp"); + run_command("WIFI NETWORKS", 20, + "su", "root", "wpa_cli", "list_networks", NULL); + #ifdef FWDUMP_bcm4329 + run_command("DUMP WIFI STATUS", 20, + "su", "root", "dhdutil", "-i", "eth0", "dump", NULL); run_command("DUMP WIFI FIRMWARE LOG", 60, "su", "root", "dhdutil", "-i", "eth0", "upload", "/data/local/tmp/wlan_crash.dump", NULL); + run_command("DUMP WIFI INTERNAL COUNTERS", 20, + "su", "root", "wlutil", "counters", NULL); #endif print_properties(); @@ -161,21 +176,36 @@ static void dumpstate() { to increase its timeout. we really need to do the timeouts in dumpsys itself... */ run_command("DUMPSYS", 60, "dumpsys", NULL); + + printf("========================================================\n"); + printf("== Application Services\n"); + printf("========================================================\n"); + + /* Instead of a 60s timeout, we should give each service a 5 second timeout */ + run_command("APP SERVICES", 60, "dumpsys", "activity", "service", NULL); + } static void usage() { - fprintf(stderr, "usage: dumpstate [-d] [-o file] [-s] [-z]\n" - " -d: append date to filename (requires -o)\n" + fprintf(stderr, "usage: dumpstate [-b soundfile] [-e soundfile] [-o file [-d] [-p] [-z]] [-s]\n" " -o: write to file (instead of stdout)\n" + " -d: append date to filename (requires -o)\n" + " -z: gzip output (requires -o)\n" + " -p: capture screenshot to filename.png (requires -o)\n" " -s: write output to control socket (for init)\n" - " -z: gzip output (requires -o)\n"); + " -b: play sound file instead of vibrate, at beginning of job\n" + " -e: play sound file instead of vibrate, at end of job\n" + ); } int main(int argc, char *argv[]) { int do_add_date = 0; int do_compress = 0; char* use_outfile = 0; + char* begin_sound = 0; + char* end_sound = 0; int use_socket = 0; + int do_fb = 0; LOGI("begin\n"); @@ -191,13 +221,16 @@ int main(int argc, char *argv[]) { dump_traces_path = dump_vm_traces(); int c; - while ((c = getopt(argc, argv, "dho:svz")) != -1) { + while ((c = getopt(argc, argv, "b:de:ho:svzp")) != -1) { switch (c) { + case 'b': begin_sound = optarg; break; case 'd': do_add_date = 1; break; + case 'e': end_sound = optarg; break; case 'o': use_outfile = optarg; break; case 's': use_socket = 1; break; case 'v': break; // compatibility no-op case 'z': do_compress = 6; break; + case 'p': do_fb = 1; break; case '?': printf("\n"); case 'h': usage(); @@ -246,6 +279,10 @@ int main(int argc, char *argv[]) { strftime(date, sizeof(date), "-%Y-%m-%d-%H-%M-%S", localtime(&now)); strlcat(path, date, sizeof(path)); } + if (do_fb) { + strlcpy(screenshot_path, path, sizeof(screenshot_path)); + strlcat(screenshot_path, ".png", sizeof(screenshot_path)); + } strlcat(path, ".txt", sizeof(path)); if (do_compress) strlcat(path, ".gz", sizeof(path)); strlcpy(tmp_path, path, sizeof(tmp_path)); @@ -253,16 +290,18 @@ int main(int argc, char *argv[]) { gzip_pid = redirect_to_file(stdout, tmp_path, do_compress); } - /* bzzzzzz */ - if (vibrator) { + if (begin_sound) { + play_sound(begin_sound); + } else if (vibrator) { fputs("150", vibrator); fflush(vibrator); } dumpstate(); - /* bzzz bzzz bzzz */ - if (vibrator) { + if (end_sound) { + play_sound(end_sound); + } else if (vibrator) { int i; for (i = 0; i < 3; i++) { fputs("75\n", vibrator); diff --git a/cmds/dumpstate/dumpstate.h b/cmds/dumpstate/dumpstate.h index 682eafd..83b1d11 100644 --- a/cmds/dumpstate/dumpstate.h +++ b/cmds/dumpstate/dumpstate.h @@ -44,4 +44,7 @@ void for_each_pid(void (*func)(int, const char *), const char *header); /* Displays a blocked processes in-kernel wait channel */ void show_wchan(int pid, const char *name); +/* Play a sound via Stagefright */ +void play_sound(const char* path); + #endif /* _DUMPSTATE_H_ */ diff --git a/cmds/dumpstate/utils.c b/cmds/dumpstate/utils.c index c7a78cc..f92acbb 100644 --- a/cmds/dumpstate/utils.c +++ b/cmds/dumpstate/utils.c @@ -429,3 +429,7 @@ const char *dump_vm_traces() { rename(anr_traces_path, traces_path); return dump_traces_path; } + +void play_sound(const char* path) { + run_command(NULL, 5, "/system/bin/stagefright", "-o", "-a", path, NULL); +} diff --git a/cmds/input/src/com/android/commands/input/Input.java b/cmds/input/src/com/android/commands/input/Input.java index 3a1accd..df1d0bf 100755 --- a/cmds/input/src/com/android/commands/input/Input.java +++ b/cmds/input/src/com/android/commands/input/Input.java @@ -91,7 +91,7 @@ public class Input { char[] chars = buff.toString().toCharArray(); KeyCharacterMap mKeyCharacterMap = KeyCharacterMap. - load(KeyCharacterMap.BUILT_IN_KEYBOARD); + load(KeyCharacterMap.VIRTUAL_KEYBOARD); KeyEvent[] events = mKeyCharacterMap.getEvents(chars); diff --git a/cmds/installd/Android.mk b/cmds/installd/Android.mk index 6673862..8641c30 100644 --- a/cmds/installd/Android.mk +++ b/cmds/installd/Android.mk @@ -1,21 +1,24 @@ ifneq ($(TARGET_SIMULATOR),true) -LOCAL_PATH:= $(call my-dir) +LOCAL_PATH := $(call my-dir) include $(CLEAR_VARS) -LOCAL_SRC_FILES:= \ +LOCAL_SRC_FILES := \ installd.c commands.c utils.c -LOCAL_C_INCLUDES := \ - $(call include-path-for, system-core)/cutils +#LOCAL_C_INCLUDES := \ +# $(call include-path-for, system-core)/cutils LOCAL_SHARED_LIBRARIES := \ libcutils -LOCAL_STATIC_LIBRARIES := +LOCAL_STATIC_LIBRARIES := \ + libdiskusage -LOCAL_MODULE:= installd +LOCAL_MODULE := installd + +LOCAL_MODULE_TAGS := optional include $(BUILD_EXECUTABLE) -endif # !simulator)) +endif # !simulator diff --git a/cmds/installd/commands.c b/cmds/installd/commands.c index 2f03c7a..cde1573 100644 --- a/cmds/installd/commands.c +++ b/cmds/installd/commands.c @@ -15,6 +15,7 @@ */ #include "installd.h" +#include <diskusage/dirsize.h> int install(const char *pkgname, int encrypted_fs_flag, uid_t uid, gid_t gid) { @@ -327,55 +328,6 @@ int protect(char *pkgname, gid_t gid) return 0; } -static int64_t stat_size(struct stat *s) -{ - int64_t blksize = s->st_blksize; - int64_t size = s->st_size; - - if (blksize) { - /* round up to filesystem block size */ - size = (size + blksize - 1) & (~(blksize - 1)); - } - - return size; -} - -static int64_t calculate_dir_size(int dfd) -{ - int64_t size = 0; - struct stat s; - DIR *d; - struct dirent *de; - - d = fdopendir(dfd); - if (d == NULL) { - close(dfd); - return 0; - } - - while ((de = readdir(d))) { - const char *name = de->d_name; - if (de->d_type == DT_DIR) { - int subfd; - /* always skip "." and ".." */ - if (name[0] == '.') { - if (name[1] == 0) continue; - if ((name[1] == '.') && (name[2] == 0)) continue; - } - subfd = openat(dfd, name, O_RDONLY | O_DIRECTORY); - if (subfd >= 0) { - size += calculate_dir_size(subfd); - } - } else { - if (fstatat(dfd, name, &s, AT_SYMLINK_NOFOLLOW) == 0) { - size += stat_size(&s); - } - } - } - closedir(d); - return size; -} - int get_size(const char *pkgname, const char *apkpath, const char *fwdlock_apkpath, int64_t *_codesize, int64_t *_datasize, int64_t *_cachesize, int encrypted_fs_flag) diff --git a/cmds/pm/src/com/android/commands/pm/Pm.java b/cmds/pm/src/com/android/commands/pm/Pm.java index 41ab0f0..a236a3c 100644 --- a/cmds/pm/src/com/android/commands/pm/Pm.java +++ b/cmds/pm/src/com/android/commands/pm/Pm.java @@ -252,7 +252,7 @@ public final class Pm { for (int i=0; i<rawList.length; i++) { list.add(rawList[i]); } - + // Sort by name Collections.sort(list, new Comparator<FeatureInfo>() { @@ -971,6 +971,9 @@ public final class Pm { System.err.println("The list instrumentation command prints all instrumentations,"); System.err.println("or only those that target a specified package. Options:"); System.err.println(" -f: see their associated file."); + System.err.println("(Use this command to list all test packages, or use <TARGET-PACKAGE> "); + System.err.println(" to list the test packages for a particular application. The -f "); + System.err.println(" option lists the .apk file for the test package.)"); System.err.println(""); System.err.println("The list features command prints all features of the system."); System.err.println(""); diff --git a/cmds/rawbu/backup.cpp b/cmds/rawbu/backup.cpp index c4fa765..9ea046d 100644 --- a/cmds/rawbu/backup.cpp +++ b/cmds/rawbu/backup.cpp @@ -14,6 +14,7 @@ #include <utime.h> #include <sys/stat.h> #include <sys/types.h> +#include <stdint.h> #include <cutils/properties.h> diff --git a/cmds/screencap/Android.mk b/cmds/screencap/Android.mk index 1a6e23e..400a36b 100644 --- a/cmds/screencap/Android.mk +++ b/cmds/screencap/Android.mk @@ -8,6 +8,7 @@ LOCAL_SHARED_LIBRARIES := \ libcutils \ libutils \ libbinder \ + libskia \ libui \ libsurfaceflinger_client @@ -15,4 +16,11 @@ LOCAL_MODULE:= screencap LOCAL_MODULE_TAGS := optional +LOCAL_C_INCLUDES += \ + external/skia/include/core \ + external/skia/include/effects \ + external/skia/include/images \ + external/skia/src/ports \ + external/skia/include/utils + include $(BUILD_EXECUTABLE) diff --git a/cmds/screencap/screencap.cpp b/cmds/screencap/screencap.cpp index bc5e10d..dcea968 100644 --- a/cmds/screencap/screencap.cpp +++ b/cmds/screencap/screencap.cpp @@ -14,29 +14,109 @@ * limitations under the License. */ +#include <errno.h> #include <unistd.h> +#include <stdio.h> #include <fcntl.h> #include <binder/IMemory.h> #include <surfaceflinger/SurfaceComposerClient.h> +#include <SkImageEncoder.h> +#include <SkBitmap.h> +#include <SkStream.h> + using namespace android; +static void usage() +{ + fprintf(stderr, + "usage: screenshot [-hp] [FILENAME]\n" + " -h: this message\n" + " -p: save the file as a png.\n" + "If FILENAME ends with .png it will be saved as a png.\n" + "If FILENAME is not given, the results will be printed to stdout.\n" + ); +} + +static SkBitmap::Config flinger2skia(PixelFormat f) +{ + switch (f) { + case PIXEL_FORMAT_A_8: + case PIXEL_FORMAT_L_8: + return SkBitmap::kA8_Config; + case PIXEL_FORMAT_RGB_565: + return SkBitmap::kRGB_565_Config; + case PIXEL_FORMAT_RGBA_4444: + return SkBitmap::kARGB_4444_Config; + default: + return SkBitmap::kARGB_8888_Config; + } +} + int main(int argc, char** argv) { + bool png = false; + int c; + while ((c = getopt(argc, argv, "ph")) != -1) { + switch (c) { + case 'p': + png = true; + break; + case '?': + case 'h': + usage(); + return 1; + } + } + argc -= optind; + argv += optind; + + int fd = -1; + if (argc == 0) { + fd = dup(STDOUT_FILENO); + } else if (argc == 1) { + const char* fn = argv[0]; + fd = open(fn, O_WRONLY | O_CREAT | O_TRUNC, 0664); + if (fd == -1) { + fprintf(stderr, "Error opening file: (%d) %s\n", errno, strerror(errno)); + return 1; + } + const int len = strlen(fn); + if (len >= 4 && 0 == strcmp(fn+len-4, ".png")) { + png = true; + } + } + + if (fd == -1) { + usage(); + return 1; + } + ScreenshotClient screenshot; - if (screenshot.update() != NO_ERROR) + if (screenshot.update() != NO_ERROR) { return 0; + } void const* base = screenshot.getPixels(); uint32_t w = screenshot.getWidth(); uint32_t h = screenshot.getHeight(); uint32_t f = screenshot.getFormat(); - int fd = dup(STDOUT_FILENO); - write(fd, &w, 4); - write(fd, &h, 4); - write(fd, &f, 4); - write(fd, base, w*h*4); + + if (png) { + SkBitmap b; + b.setConfig(flinger2skia(f), w, h); + b.setPixels((void*)base); + SkDynamicMemoryWStream stream; + SkImageEncoder::EncodeStream(&stream, b, + SkImageEncoder::kPNG_Type, SkImageEncoder::kDefaultQuality); + write(fd, stream.getStream(), stream.getOffset()); + } else { + write(fd, &w, 4); + write(fd, &h, 4); + write(fd, &f, 4); + write(fd, base, w*h*4); + } close(fd); return 0; } diff --git a/cmds/screenshot/Android.mk b/cmds/screenshot/Android.mk new file mode 100644 index 0000000..99c7aeb --- /dev/null +++ b/cmds/screenshot/Android.mk @@ -0,0 +1,16 @@ +ifneq ($(TARGET_SIMULATOR),true) + +LOCAL_PATH:= $(call my-dir) +include $(CLEAR_VARS) + +LOCAL_SRC_FILES := screenshot.c + +LOCAL_MODULE := screenshot + +LOCAL_SHARED_LIBRARIES := libcutils libz +LOCAL_STATIC_LIBRARIES := libpng +LOCAL_C_INCLUDES += external/zlib + +include $(BUILD_EXECUTABLE) + +endif diff --git a/cmds/screenshot/screenshot.c b/cmds/screenshot/screenshot.c new file mode 100644 index 0000000..048636c --- /dev/null +++ b/cmds/screenshot/screenshot.c @@ -0,0 +1,171 @@ +#include <stdlib.h> +#include <stdio.h> +#include <unistd.h> +#include <string.h> +#include <fcntl.h> +#include <errno.h> + +#include <linux/fb.h> + +#include <zlib.h> +#include <libpng/png.h> + +#include "private/android_filesystem_config.h" + +#define LOG_TAG "screenshot" +#include <utils/Log.h> + +void take_screenshot(FILE *fb_in, FILE *fb_out) { + int fb; + char imgbuf[0x10000]; + struct fb_var_screeninfo vinfo; + png_structp png; + png_infop info; + unsigned int r,c,rowlen; + unsigned int bytespp,offset; + + fb = fileno(fb_in); + if(fb < 0) { + LOGE("failed to open framebuffer\n"); + return; + } + fb_in = fdopen(fb, "r"); + + if(ioctl(fb, FBIOGET_VSCREENINFO, &vinfo) < 0) { + LOGE("failed to get framebuffer info\n"); + return; + } + fcntl(fb, F_SETFD, FD_CLOEXEC); + + png = png_create_write_struct(PNG_LIBPNG_VER_STRING, NULL, NULL, NULL); + if (png == NULL) { + LOGE("failed png_create_write_struct\n"); + fclose(fb_in); + return; + } + + png_init_io(png, fb_out); + info = png_create_info_struct(png); + if (info == NULL) { + LOGE("failed png_create_info_struct\n"); + png_destroy_write_struct(&png, NULL); + fclose(fb_in); + return; + } + if (setjmp(png_jmpbuf(png))) { + LOGE("failed png setjmp\n"); + png_destroy_write_struct(&png, NULL); + fclose(fb_in); + return; + } + + bytespp = vinfo.bits_per_pixel / 8; + png_set_IHDR(png, info, + vinfo.xres, vinfo.yres, vinfo.bits_per_pixel / 4, + PNG_COLOR_TYPE_RGB_ALPHA, PNG_INTERLACE_NONE, + PNG_COMPRESSION_TYPE_BASE, PNG_FILTER_TYPE_BASE); + png_write_info(png, info); + + rowlen=vinfo.xres * bytespp; + if (rowlen > sizeof(imgbuf)) { + LOGE("crazy rowlen: %d\n", rowlen); + png_destroy_write_struct(&png, NULL); + fclose(fb_in); + return; + } + + offset = vinfo.xoffset * bytespp + vinfo.xres * vinfo.yoffset * bytespp; + fseek(fb_in, offset, SEEK_SET); + + for(r=0; r<vinfo.yres; r++) { + int len = fread(imgbuf, 1, rowlen, fb_in); + if (len <= 0) break; + png_write_row(png, (png_bytep)imgbuf); + } + + png_write_end(png, info); + fclose(fb_in); + png_destroy_write_struct(&png, NULL); +} + +void fork_sound(const char* path) { + pid_t pid = fork(); + if (pid == 0) { + execl("/system/bin/stagefright", "stagefright", "-o", "-a", path, NULL); + } +} + +void usage() { + fprintf(stderr, + "usage: screenshot [-s soundfile] filename.png\n" + " -s: play a sound effect to signal success\n" + " -i: autoincrement to avoid overwriting filename.png\n" + ); +} + +int main(int argc, char**argv) { + FILE *png = NULL; + FILE *fb_in = NULL; + char outfile[PATH_MAX] = ""; + + char * soundfile = NULL; + int do_increment = 0; + + int c; + while ((c = getopt(argc, argv, "s:i")) != -1) { + switch (c) { + case 's': soundfile = optarg; break; + case 'i': do_increment = 1; break; + case '?': + case 'h': + usage(); exit(1); + } + } + argc -= optind; + argv += optind; + + if (argc < 1) { + usage(); exit(1); + } + + strlcpy(outfile, argv[0], PATH_MAX); + if (do_increment) { + struct stat st; + char base[PATH_MAX] = ""; + int i = 0; + while (stat(outfile, &st) == 0) { + if (!base[0]) { + char *p = strrchr(outfile, '.'); + if (p) *p = '\0'; + strcpy(base, outfile); + } + snprintf(outfile, PATH_MAX, "%s-%d.png", base, ++i); + } + } + + fb_in = fopen("/dev/graphics/fb0", "r"); + if (!fb_in) { + fprintf(stderr, "error: could not read framebuffer\n"); + exit(1); + } + + /* switch to non-root user and group */ + gid_t groups[] = { AID_LOG, AID_SDCARD_RW }; + setgroups(sizeof(groups)/sizeof(groups[0]), groups); + setuid(AID_SHELL); + + png = fopen(outfile, "w"); + if (!png) { + fprintf(stderr, "error: writing file %s: %s\n", + outfile, strerror(errno)); + exit(1); + } + + take_screenshot(fb_in, png); + + if (soundfile) { + fork_sound(soundfile); + } + + exit(0); +} diff --git a/cmds/servicemanager/service_manager.c b/cmds/servicemanager/service_manager.c index 14536bd..ba7f807 100644 --- a/cmds/servicemanager/service_manager.c +++ b/cmds/servicemanager/service_manager.c @@ -161,9 +161,9 @@ int do_add_service(struct binder_state *bs, si = find_svc(s, len); if (si) { if (si->ptr) { - LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED\n", + LOGE("add_service('%s',%p) uid=%d - ALREADY REGISTERED, OVERRIDE\n", str8(s), ptr, uid); - return -1; + svcinfo_death(bs, si); } si->ptr = ptr; } else { diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk index 5b74007..f8650eb 100644 --- a/cmds/stagefright/Android.mk +++ b/cmds/stagefright/Android.mk @@ -7,13 +7,16 @@ LOCAL_SRC_FILES:= \ SineSource.cpp LOCAL_SHARED_LIBRARIES := \ - libstagefright libmedia libutils libbinder libstagefright_foundation + libstagefright libmedia libutils libbinder libstagefright_foundation \ + libskia LOCAL_C_INCLUDES:= \ $(JNI_H_INCLUDE) \ frameworks/base/media/libstagefright \ frameworks/base/media/libstagefright/include \ - $(TOP)/frameworks/base/include/media/stagefright/openmax + $(TOP)/frameworks/base/include/media/stagefright/openmax \ + external/skia/include/core \ + external/skia/include/images \ LOCAL_CFLAGS += -Wno-multichar @@ -53,6 +56,31 @@ include $(CLEAR_VARS) LOCAL_SRC_FILES:= \ SineSource.cpp \ + recordvideo.cpp + +LOCAL_SHARED_LIBRARIES := \ + libstagefright liblog libutils libbinder + +LOCAL_C_INCLUDES:= \ + $(JNI_H_INCLUDE) \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax + +LOCAL_CFLAGS += -Wno-multichar + +LOCAL_MODULE_TAGS := debug + +LOCAL_MODULE:= recordvideo + +include $(BUILD_EXECUTABLE) + + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + SineSource.cpp \ audioloop.cpp LOCAL_SHARED_LIBRARIES := \ @@ -70,3 +98,27 @@ LOCAL_MODULE_TAGS := debug LOCAL_MODULE:= audioloop include $(BUILD_EXECUTABLE) + +################################################################################ + +include $(CLEAR_VARS) + +LOCAL_SRC_FILES:= \ + stream.cpp \ + +LOCAL_SHARED_LIBRARIES := \ + libstagefright liblog libutils libbinder libsurfaceflinger_client \ + libstagefright_foundation libmedia + +LOCAL_C_INCLUDES:= \ + $(JNI_H_INCLUDE) \ + frameworks/base/media/libstagefright \ + $(TOP)/frameworks/base/include/media/stagefright/openmax + +LOCAL_CFLAGS += -Wno-multichar + +LOCAL_MODULE_TAGS := debug + +LOCAL_MODULE:= stream + +include $(BUILD_EXECUTABLE) diff --git a/cmds/stagefright/recordvideo.cpp b/cmds/stagefright/recordvideo.cpp new file mode 100644 index 0000000..1264215 --- /dev/null +++ b/cmds/stagefright/recordvideo.cpp @@ -0,0 +1,303 @@ +/* + * Copyright (C) 2010 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. + */ + +#include "SineSource.h" + +#include <binder/ProcessState.h> +#include <media/stagefright/AudioPlayer.h> +#include <media/stagefright/MediaBufferGroup.h> +#include <media/stagefright/MediaDebug.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/MPEG4Writer.h> +#include <media/stagefright/OMXClient.h> +#include <media/stagefright/OMXCodec.h> +#include <media/MediaPlayerInterface.h> + +using namespace android; + +// Print usage showing how to use this utility to record videos +static void usage(const char *me) { + fprintf(stderr, "usage: %s\n", me); + fprintf(stderr, " -h(elp)\n"); + fprintf(stderr, " -b bit rate in bits per second (default: 300000)\n"); + fprintf(stderr, " -c YUV420 color format: [0] semi planar or [1] planar (default: 1)\n"); + fprintf(stderr, " -f frame rate in frames per second (default: 30)\n"); + fprintf(stderr, " -i I frame interval in seconds (default: 1)\n"); + fprintf(stderr, " -n number of frames to be recorded (default: 300)\n"); + fprintf(stderr, " -w width in pixels (default: 176)\n"); + fprintf(stderr, " -t height in pixels (default: 144)\n"); + fprintf(stderr, " -l encoder level. see omx il header (default: encoder specific)\n"); + fprintf(stderr, " -p encoder profile. see omx il header (default: encoder specific)\n"); + fprintf(stderr, " -v video codec: [0] AVC [1] M4V [2] H263 (default: 0)\n"); + fprintf(stderr, "The output file is /sdcard/output.mp4\n"); + exit(1); +} + +class DummySource : public MediaSource { + +public: + DummySource(int width, int height, int nFrames, int fps, int colorFormat) + : mWidth(width), + mHeight(height), + mMaxNumFrames(nFrames), + mFrameRate(fps), + mColorFormat(colorFormat), + mSize((width * height * 3) / 2) { + + mGroup.add_buffer(new MediaBuffer(mSize)); + + // Check the color format to make sure + // that the buffer size mSize it set correctly above. + CHECK(colorFormat == OMX_COLOR_FormatYUV420SemiPlanar || + colorFormat == OMX_COLOR_FormatYUV420Planar); + } + + virtual sp<MetaData> getFormat() { + sp<MetaData> meta = new MetaData; + meta->setInt32(kKeyWidth, mWidth); + meta->setInt32(kKeyHeight, mHeight); + meta->setInt32(kKeyColorFormat, mColorFormat); + meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_RAW); + + return meta; + } + + virtual status_t start(MetaData *params) { + mNumFramesOutput = 0; + return OK; + } + + virtual status_t stop() { + return OK; + } + + virtual status_t read( + MediaBuffer **buffer, const MediaSource::ReadOptions *options) { + + if (mNumFramesOutput % 10 == 0) { + fprintf(stderr, "."); + } + if (mNumFramesOutput == mMaxNumFrames) { + return ERROR_END_OF_STREAM; + } + + status_t err = mGroup.acquire_buffer(buffer); + if (err != OK) { + return err; + } + + // We don't care about the contents. we just test video encoder + // Also, by skipping the content generation, we can return from + // read() much faster. + //char x = (char)((double)rand() / RAND_MAX * 255); + //memset((*buffer)->data(), x, mSize); + (*buffer)->set_range(0, mSize); + (*buffer)->meta_data()->clear(); + (*buffer)->meta_data()->setInt64( + kKeyTime, (mNumFramesOutput * 1000000) / mFrameRate); + ++mNumFramesOutput; + + return OK; + } + +protected: + virtual ~DummySource() {} + +private: + MediaBufferGroup mGroup; + int mWidth, mHeight; + int mMaxNumFrames; + int mFrameRate; + int mColorFormat; + size_t mSize; + int64_t mNumFramesOutput;; + + DummySource(const DummySource &); + DummySource &operator=(const DummySource &); +}; + +enum { + kYUV420SP = 0, + kYUV420P = 1, +}; + +// returns -1 if mapping of the given color is unsuccessful +// returns an omx color enum value otherwise +static int translateColorToOmxEnumValue(int color) { + switch (color) { + case kYUV420SP: + return OMX_COLOR_FormatYUV420SemiPlanar; + case kYUV420P: + return OMX_COLOR_FormatYUV420Planar; + default: + fprintf(stderr, "Unsupported color: %d\n", color); + return -1; + } +} + +int main(int argc, char **argv) { + + // Default values for the program if not overwritten + int frameRateFps = 30; + int width = 176; + int height = 144; + int bitRateBps = 300000; + int iFramesIntervalSeconds = 1; + int colorFormat = OMX_COLOR_FormatYUV420Planar; + int nFrames = 300; + int level = -1; // Encoder specific default + int profile = -1; // Encoder specific default + int codec = 0; + const char *fileName = "/sdcard/output.mp4"; + + android::ProcessState::self()->startThreadPool(); + int res; + while ((res = getopt(argc, argv, "b:c:f:i:n:w:t:l:p:v:h")) >= 0) { + switch (res) { + case 'b': + { + bitRateBps = atoi(optarg); + break; + } + + case 'c': + { + colorFormat = translateColorToOmxEnumValue(atoi(optarg)); + if (colorFormat == -1) { + usage(argv[0]); + } + break; + } + + case 'f': + { + frameRateFps = atoi(optarg); + break; + } + + case 'i': + { + iFramesIntervalSeconds = atoi(optarg); + break; + } + + case 'n': + { + nFrames = atoi(optarg); + break; + } + + case 'w': + { + width = atoi(optarg); + break; + } + + case 't': + { + height = atoi(optarg); + break; + } + + case 'l': + { + level = atoi(optarg); + break; + } + + case 'p': + { + profile = atoi(optarg); + break; + } + + case 'v': + { + codec = atoi(optarg); + if (codec < 0 || codec > 2) { + usage(argv[0]); + } + break; + } + + case 'h': + default: + { + usage(argv[0]); + break; + } + } + } + + OMXClient client; + CHECK_EQ(client.connect(), OK); + + status_t err = OK; + sp<MediaSource> source = + new DummySource(width, height, nFrames, frameRateFps, colorFormat); + + sp<MetaData> enc_meta = new MetaData; + switch (codec) { + case 1: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4); + break; + case 2: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263); + break; + default: + enc_meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC); + break; + } + enc_meta->setInt32(kKeyWidth, width); + enc_meta->setInt32(kKeyHeight, height); + enc_meta->setInt32(kKeyFrameRate, frameRateFps); + enc_meta->setInt32(kKeyBitRate, bitRateBps); + enc_meta->setInt32(kKeyStride, width); + enc_meta->setInt32(kKeySliceHeight, height); + enc_meta->setInt32(kKeyIFramesInterval, iFramesIntervalSeconds); + enc_meta->setInt32(kKeyColorFormat, colorFormat); + if (level != -1) { + enc_meta->setInt32(kKeyVideoLevel, level); + } + if (profile != -1) { + enc_meta->setInt32(kKeyVideoProfile, profile); + } + + sp<MediaSource> encoder = + OMXCodec::Create( + client.interface(), enc_meta, true /* createEncoder */, source); + + sp<MPEG4Writer> writer = new MPEG4Writer(fileName); + writer->addSource(encoder); + int64_t start = systemTime(); + CHECK_EQ(OK, writer->start()); + while (!writer->reachedEOS()) { + } + err = writer->stop(); + int64_t end = systemTime(); + + fprintf(stderr, "$\n"); + client.disconnect(); + + if (err != OK && err != ERROR_END_OF_STREAM) { + fprintf(stderr, "record failed: %d\n", err); + return 1; + } + fprintf(stderr, "encoding %d frames in %lld us\n", nFrames, (end-start)/1000); + fprintf(stderr, "encoding speed is: %.2f fps\n", (nFrames * 1E9) / (end-start)); + return 0; +} diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index f55b746..7ba5291 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -31,7 +31,7 @@ #include <media/IMediaPlayerService.h> #include <media/stagefright/foundation/ALooper.h> #include "include/ARTSPController.h" -#include "include/LiveSource.h" +#include "include/LiveSession.h" #include "include/NuCachedSource2.h" #include <media/stagefright/AudioPlayer.h> #include <media/stagefright/DataSource.h> @@ -49,6 +49,10 @@ #include <media/stagefright/MPEG2TSWriter.h> #include <media/stagefright/MPEG4Writer.h> +#include <private/media/VideoFrame.h> +#include <SkBitmap.h> +#include <SkImageEncoder.h> + #include <fcntl.h> using namespace android; @@ -59,6 +63,7 @@ static long gReproduceBug; // if not -1. static bool gPreferSoftwareCodec; static bool gPlaybackAudio; static bool gWriteMP4; +static bool gDisplayHistogram; static String8 gWriteMP4Filename; static int64_t getNowUs() { @@ -68,6 +73,58 @@ static int64_t getNowUs() { return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll; } +static int CompareIncreasing(const int64_t *a, const int64_t *b) { + return (*a) < (*b) ? -1 : (*a) > (*b) ? 1 : 0; +} + +static void displayDecodeHistogram(Vector<int64_t> *decodeTimesUs) { + printf("decode times:\n"); + + decodeTimesUs->sort(CompareIncreasing); + + size_t n = decodeTimesUs->size(); + int64_t minUs = decodeTimesUs->itemAt(0); + int64_t maxUs = decodeTimesUs->itemAt(n - 1); + + printf("min decode time %lld us (%.2f secs)\n", minUs, minUs / 1E6); + printf("max decode time %lld us (%.2f secs)\n", maxUs, maxUs / 1E6); + + size_t counts[100]; + for (size_t i = 0; i < 100; ++i) { + counts[i] = 0; + } + + for (size_t i = 0; i < n; ++i) { + int64_t x = decodeTimesUs->itemAt(i); + + size_t slot = ((x - minUs) * 100) / (maxUs - minUs); + if (slot == 100) { slot = 99; } + + ++counts[slot]; + } + + for (size_t i = 0; i < 100; ++i) { + int64_t slotUs = minUs + (i * (maxUs - minUs) / 100); + + double fps = 1E6 / slotUs; + printf("[%.2f fps]: %d\n", fps, counts[i]); + } +} + +static void displayAVCProfileLevelIfPossible(const sp<MetaData>& meta) { + uint32_t type; + const void *data; + size_t size; + if (meta->findData(kKeyAVCC, &type, &data, &size)) { + const uint8_t *ptr = (const uint8_t *)data; + CHECK(size >= 7); + CHECK(ptr[0] == 1); // configurationVersion == 1 + uint8_t profile = ptr[1]; + uint8_t level = ptr[3]; + fprintf(stderr, "AVC video profile %d and level %d\n", profile, level); + } +} + static void playSource(OMXClient *client, sp<MediaSource> &source) { sp<MetaData> meta = source->getFormat(); @@ -87,6 +144,7 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { fprintf(stderr, "Failed to instantiate decoder for '%s'.\n", mime); return; } + displayAVCProfileLevelIfPossible(meta); } source.clear(); @@ -201,6 +259,8 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { int64_t sumDecodeUs = 0; int64_t totalBytes = 0; + Vector<int64_t> decodeTimesUs; + while (numIterationsLeft-- > 0) { long numFrames = 0; @@ -224,9 +284,17 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { break; } - if (buffer->range_length() > 0 && (n++ % 16) == 0) { - printf("."); - fflush(stdout); + if (buffer->range_length() > 0) { + if (gDisplayHistogram && n > 0) { + // Ignore the first time since it includes some setup + // cost. + decodeTimesUs.push(delayDecodeUs); + } + + if ((n++ % 16) == 0) { + printf("."); + fflush(stdout); + } } sumDecodeUs += delayDecodeUs; @@ -266,6 +334,10 @@ static void playSource(OMXClient *client, sp<MediaSource> &source) { (double)sumDecodeUs / n); printf("decoded a total of %d frame(s).\n", n); + + if (gDisplayHistogram) { + displayDecodeHistogram(&decodeTimesUs); + } } else if (!strncasecmp("audio/", mime, 6)) { // Frame count makes less sense for audio, as the output buffer // sizes may be different across decoders. @@ -466,6 +538,8 @@ static void usage(const char *me) { fprintf(stderr, " -o playback audio\n"); fprintf(stderr, " -w(rite) filename (write to .mp4 file)\n"); fprintf(stderr, " -k seek test\n"); + fprintf(stderr, " -x display a histogram of decoding times/fps " + "(video only)\n"); } int main(int argc, char **argv) { @@ -482,12 +556,14 @@ int main(int argc, char **argv) { gPreferSoftwareCodec = false; gPlaybackAudio = false; gWriteMP4 = false; + gDisplayHistogram = false; sp<ALooper> looper; sp<ARTSPController> rtspController; + sp<LiveSession> liveSession; int res; - while ((res = getopt(argc, argv, "han:lm:b:ptsow:k")) >= 0) { + while ((res = getopt(argc, argv, "han:lm:b:ptsow:kx")) >= 0) { switch (res) { case 'a': { @@ -560,6 +636,12 @@ int main(int argc, char **argv) { break; } + case 'x': + { + gDisplayHistogram = true; + break; + } + case '?': case 'h': default: @@ -600,17 +682,32 @@ int main(int argc, char **argv) { METADATA_MODE_FRAME_CAPTURE_AND_METADATA_RETRIEVAL), (status_t)OK); - sp<IMemory> mem = retriever->captureFrame(); + sp<IMemory> mem = + retriever->getFrameAtTime(-1, + MediaSource::ReadOptions::SEEK_PREVIOUS_SYNC); if (mem != NULL) { - printf("captureFrame(%s) => OK\n", filename); + printf("getFrameAtTime(%s) => OK\n", filename); + + VideoFrame *frame = (VideoFrame *)mem->pointer(); + + SkBitmap bitmap; + bitmap.setConfig( + SkBitmap::kRGB_565_Config, frame->mWidth, frame->mHeight); + + bitmap.setPixels((uint8_t *)frame + sizeof(VideoFrame)); + + CHECK(SkImageEncoder::EncodeFile( + "/sdcard/out.jpg", bitmap, + SkImageEncoder::kJPEG_Type, + SkImageEncoder::kDefaultQuality)); } else { mem = retriever->extractAlbumArt(); if (mem != NULL) { printf("extractAlbumArt(%s) => OK\n", filename); } else { - printf("both captureFrame and extractAlbumArt " + printf("both getFrameAtTime and extractAlbumArt " "failed on file '%s'.\n", filename); } } @@ -756,8 +853,15 @@ int main(int argc, char **argv) { String8 uri("http://"); uri.append(filename + 11); - dataSource = new LiveSource(uri.string()); - dataSource = new NuCachedSource2(dataSource); + if (looper == NULL) { + looper = new ALooper; + looper->start(); + } + liveSession = new LiveSession; + looper->registerHandler(liveSession); + + liveSession->connect(uri.string()); + dataSource = liveSession->getDataSource(); extractor = MediaExtractor::Create( diff --git a/cmds/stagefright/stream.cpp b/cmds/stagefright/stream.cpp new file mode 100644 index 0000000..a3d7a6e --- /dev/null +++ b/cmds/stagefright/stream.cpp @@ -0,0 +1,169 @@ +#include <binder/ProcessState.h> + +#include <media/IStreamSource.h> +#include <media/mediaplayer.h> +#include <media/stagefright/foundation/ADebug.h> +#include <media/stagefright/foundation/AMessage.h> + +#include <binder/IServiceManager.h> +#include <media/IMediaPlayerService.h> +#include <surfaceflinger/ISurfaceComposer.h> +#include <surfaceflinger/SurfaceComposerClient.h> + +#include <fcntl.h> + +using namespace android; + +struct MyStreamSource : public BnStreamSource { + // Caller retains ownership of fd. + MyStreamSource(int fd); + + virtual void setListener(const sp<IStreamListener> &listener); + virtual void setBuffers(const Vector<sp<IMemory> > &buffers); + + virtual void onBufferAvailable(size_t index); + +protected: + virtual ~MyStreamSource(); + +private: + int mFd; + + sp<IStreamListener> mListener; + Vector<sp<IMemory> > mBuffers; + + DISALLOW_EVIL_CONSTRUCTORS(MyStreamSource); +}; + +MyStreamSource::MyStreamSource(int fd) + : mFd(fd) { + CHECK_GE(fd, 0); +} + +MyStreamSource::~MyStreamSource() { +} + +void MyStreamSource::setListener(const sp<IStreamListener> &listener) { + mListener = listener; +} + +void MyStreamSource::setBuffers(const Vector<sp<IMemory> > &buffers) { + mBuffers = buffers; +} + +void MyStreamSource::onBufferAvailable(size_t index) { + CHECK_LT(index, mBuffers.size()); + sp<IMemory> mem = mBuffers.itemAt(index); + + ssize_t n = read(mFd, mem->pointer(), mem->size()); + if (n <= 0) { + mListener->issueCommand(IStreamListener::EOS, false /* synchronous */); + } else { + mListener->queueBuffer(index, n); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +struct MyClient : public BnMediaPlayerClient { + MyClient() + : mEOS(false) { + } + + virtual void notify(int msg, int ext1, int ext2) { + Mutex::Autolock autoLock(mLock); + + if (msg == MEDIA_ERROR || msg == MEDIA_PLAYBACK_COMPLETE) { + mEOS = true; + mCondition.signal(); + } + } + + void waitForEOS() { + Mutex::Autolock autoLock(mLock); + while (!mEOS) { + mCondition.wait(mLock); + } + } + +protected: + virtual ~MyClient() { + } + +private: + Mutex mLock; + Condition mCondition; + + bool mEOS; + + DISALLOW_EVIL_CONSTRUCTORS(MyClient); +}; + +int main(int argc, char **argv) { + android::ProcessState::self()->startThreadPool(); + + if (argc != 2) { + fprintf(stderr, "Usage: %s filename\n", argv[0]); + return 1; + } + + sp<SurfaceComposerClient> composerClient = new SurfaceComposerClient; + CHECK_EQ(composerClient->initCheck(), (status_t)OK); + + sp<SurfaceControl> control = + composerClient->createSurface( + getpid(), + String8("A Surface"), + 0, + 1280, + 800, + PIXEL_FORMAT_RGB_565, + 0); + + CHECK(control != NULL); + CHECK(control->isValid()); + + CHECK_EQ(composerClient->openTransaction(), (status_t)OK); + CHECK_EQ(control->setLayer(30000), (status_t)OK); + CHECK_EQ(control->show(), (status_t)OK); + CHECK_EQ(composerClient->closeTransaction(), (status_t)OK); + + sp<Surface> surface = control->getSurface(); + CHECK(surface != NULL); + + sp<IServiceManager> sm = defaultServiceManager(); + sp<IBinder> binder = sm->getService(String16("media.player")); + sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder); + + CHECK(service.get() != NULL); + + int fd = open(argv[1], O_RDONLY); + + if (fd < 0) { + fprintf(stderr, "Failed to open file '%s'.", argv[1]); + return 1; + } + + sp<MyClient> client = new MyClient; + + sp<IMediaPlayer> player = + service->create(getpid(), client, new MyStreamSource(fd), 0); + + if (player != NULL) { + player->setVideoSurface(surface); + player->start(); + + client->waitForEOS(); + + player->stop(); + } else { + fprintf(stderr, "failed to instantiate player.\n"); + } + + close(fd); + fd = -1; + + composerClient->dispose(); + + return 0; +} |
