Merge "AudioTrack: fix write retries for compressed audio" into klp-dev
diff --git a/cmds/screenrecord/screenrecord.cpp b/cmds/screenrecord/screenrecord.cpp
index 3f8567c..d027ba9 100644
--- a/cmds/screenrecord/screenrecord.cpp
+++ b/cmds/screenrecord/screenrecord.cpp
@@ -21,6 +21,7 @@
 #include <binder/IPCThreadState.h>
 #include <utils/Errors.h>
 #include <utils/Thread.h>
+#include <utils/Timers.h>
 
 #include <gui/Surface.h>
 #include <gui/SurfaceComposerClient.h>
@@ -35,6 +36,8 @@
 #include <media/stagefright/MediaMuxer.h>
 #include <media/ICrypto.h>
 
+#include <stdlib.h>
+#include <string.h>
 #include <stdio.h>
 #include <fcntl.h>
 #include <signal.h>
@@ -42,6 +45,12 @@
 
 using namespace android;
 
+static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
+static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
+static const uint32_t kMaxTimeLimitSec = 180;       // 3 minutes
+static const uint32_t kFallbackWidth = 1280;        // 720p
+static const uint32_t kFallbackHeight = 720;
+
 // Command-line parameters.
 static bool gVerbose = false;               // chatty on stdout
 static bool gRotate = false;                // rotate 90 degrees
@@ -49,6 +58,7 @@
 static uint32_t gVideoWidth = 0;            // default width+height
 static uint32_t gVideoHeight = 0;
 static uint32_t gBitRate = 4000000;         // 4Mbps
+static uint32_t gTimeLimitSec = kMaxTimeLimitSec;
 
 // Set by signal handler to stop recording.
 static bool gStopRequested;
@@ -57,8 +67,6 @@
 static struct sigaction gOrigSigactionINT;
 static struct sigaction gOrigSigactionHUP;
 
-static const uint32_t kMinBitRate = 100000;         // 0.1Mbps
-static const uint32_t kMaxBitRate = 100 * 1000000;  // 100Mbps
 
 /*
  * Catch keyboard interrupt signals.  On receipt, the "stop requested"
@@ -70,9 +78,8 @@
     gStopRequested = true;
     switch (signum) {
     case SIGINT:
-        sigaction(SIGINT, &gOrigSigactionINT, NULL);
-        break;
     case SIGHUP:
+        sigaction(SIGINT, &gOrigSigactionINT, NULL);
         sigaction(SIGHUP, &gOrigSigactionHUP, NULL);
         break;
     default:
@@ -138,7 +145,6 @@
     format->setFloat("frame-rate", displayFps);
     format->setInt32("i-frame-interval", 10);
 
-    /// MediaCodec
     sp<ALooper> looper = new ALooper;
     looper->setName("screenrecord_looper");
     looper->start();
@@ -279,7 +285,8 @@
     status_t err;
     ssize_t trackIdx = -1;
     uint32_t debugNumFrames = 0;
-    time_t debugStartWhen = time(NULL);
+    int64_t startWhenNsec = systemTime(CLOCK_MONOTONIC);
+    int64_t endWhenNsec = startWhenNsec + seconds_to_nanoseconds(gTimeLimitSec);
 
     Vector<sp<ABuffer> > buffers;
     err = encoder->getOutputBuffers(&buffers);
@@ -296,6 +303,14 @@
         size_t bufIndex, offset, size;
         int64_t ptsUsec;
         uint32_t flags;
+
+        if (systemTime(CLOCK_MONOTONIC) > endWhenNsec) {
+            if (gVerbose) {
+                printf("Time limit reached\n");
+            }
+            break;
+        }
+
         ALOGV("Calling dequeueOutputBuffer");
         err = encoder->dequeueOutputBuffer(&bufIndex, &offset, &size, &ptsUsec,
                 &flags, kTimeout);
@@ -345,7 +360,6 @@
             }
             break;
         case -EAGAIN:                       // INFO_TRY_AGAIN_LATER
-            // not expected with infinite timeout
             ALOGV("Got -EAGAIN, looping");
             break;
         case INFO_FORMAT_CHANGED:           // INFO_OUTPUT_FORMAT_CHANGED
@@ -370,18 +384,24 @@
             if (err != NO_ERROR) {
                 fprintf(stderr,
                         "Unable to get new output buffers (err=%d)\n", err);
+                return err;
             }
             break;
+        case INVALID_OPERATION:
+            fprintf(stderr, "Request for encoder buffer failed\n");
+            return err;
         default:
-            ALOGW("Got weird result %d from dequeueOutputBuffer", err);
+            fprintf(stderr,
+                    "Got weird result %d from dequeueOutputBuffer\n", err);
             return err;
         }
     }
 
     ALOGV("Encoder stopping (req=%d)", gStopRequested);
     if (gVerbose) {
-        printf("Encoder stopping; recorded %u frames in %ld seconds\n",
-                debugNumFrames, time(NULL) - debugStartWhen);
+        printf("Encoder stopping; recorded %u frames in %lld seconds\n",
+                debugNumFrames,
+                nanoseconds_to_seconds(systemTime(CLOCK_MONOTONIC) - startWhenNsec));
     }
     return NO_ERROR;
 }
@@ -432,12 +452,12 @@
     sp<IGraphicBufferProducer> bufferProducer;
     err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
     if (err != NO_ERROR && !gSizeSpecified) {
-        ALOGV("Retrying with 720p");
-        if (gVideoWidth != 1280 && gVideoHeight != 720) {
+        if (gVideoWidth != kFallbackWidth && gVideoHeight != kFallbackHeight) {
+            ALOGV("Retrying with 720p");
             fprintf(stderr, "WARNING: failed at %dx%d, retrying at 720p\n",
                     gVideoWidth, gVideoHeight);
-            gVideoWidth = 1280;
-            gVideoHeight = 720;
+            gVideoWidth = kFallbackWidth;
+            gVideoHeight = kFallbackHeight;
             err = prepareEncoder(mainDpyInfo.fps, &encoder, &bufferProducer);
         }
     }
@@ -477,6 +497,29 @@
 }
 
 /*
+ * Sends a broadcast to the media scanner to tell it about the new video.
+ */
+static status_t notifyMediaScanner(const char* fileName) {
+    String8 command("am broadcast -a android.intent.action.MEDIA_SCANNER_SCAN_FILE -d file://");
+    command.append(fileName);
+    if (gVerbose) {
+        printf("Shell: %s\n", command.string());
+    }
+
+    // TODO: for non-verbose mode we should suppress stdout
+    int status = system(command.string());
+    if (status < 0) {
+        fprintf(stderr, "Unable to fork shell for media scanner broadcast\n");
+        return UNKNOWN_ERROR;
+    } else if (status != 0) {
+        fprintf(stderr, "am command failed (status=%d): '%s'\n",
+                status, command.string());
+        return UNKNOWN_ERROR;
+    }
+    return NO_ERROR;
+}
+
+/*
  * Parses a string of the form "1280x720".
  *
  * Returns true on success.
@@ -514,10 +557,13 @@
         "\n"
         "Options:\n"
         "--size WIDTHxHEIGHT\n"
-        "    Set the video size, e.g. \"1280x720\".  For best results, use\n"
-        "    a size supported by the AVC encoder.\n"
+        "    Set the video size, e.g. \"1280x720\".  Default is the device's main\n"
+        "    display resolution (if supported), 1280x720 if not.  For best results,\n"
+        "    use a size supported by the AVC encoder.\n"
         "--bit-rate RATE\n"
-        "    Set the video bit rate, in megabits per second.  Default 4Mbps.\n"
+        "    Set the video bit rate, in megabits per second.  Default %dMbps.\n"
+        "--time-limit TIME\n"
+        "    Set the maximum recording time, in seconds.  Default / maximum is %d.\n"
         "--rotate\n"
         "    Rotate the output 90 degrees.\n"
         "--verbose\n"
@@ -525,8 +571,9 @@
         "--help\n"
         "    Show this message.\n"
         "\n"
-        "Recording continues until Ctrl-C is hit.\n"
-        "\n"
+        "Recording continues until Ctrl-C is hit or the time limit is reached.\n"
+        "\n",
+        gBitRate / 1000000, gTimeLimitSec
         );
 }
 
@@ -539,6 +586,7 @@
         { "verbose",    no_argument,        NULL, 'v' },
         { "size",       required_argument,  NULL, 's' },
         { "bit-rate",   required_argument,  NULL, 'b' },
+        { "time-limit", required_argument,  NULL, 't' },
         { "rotate",     no_argument,        NULL, 'r' },
         { NULL,         0,                  NULL, 0 }
     };
@@ -580,6 +628,15 @@
                 return 2;
             }
             break;
+        case 't':
+            gTimeLimitSec = atoi(optarg);
+            if (gTimeLimitSec == 0 || gTimeLimitSec > kMaxTimeLimitSec) {
+                fprintf(stderr,
+                        "Time limit %ds outside acceptable range [1,%d]\n",
+                        gTimeLimitSec, kMaxTimeLimitSec);
+                return 2;
+            }
+            break;
         case 'r':
             gRotate = true;
             break;
@@ -609,6 +666,10 @@
     close(fd);
 
     status_t err = recordScreen(fileName);
+    if (err == NO_ERROR) {
+        // Try to notify the media scanner.  Not fatal if this fails.
+        notifyMediaScanner(fileName);
+    }
     ALOGD(err == NO_ERROR ? "success" : "failed");
     return (int) err;
 }