Support for writing to MPEG2 transport stream files.

Change-Id: If3b7a807bc224a4b1cb2236537c3ebdc5aee0d97
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index 291b18a..5ab1640 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -76,6 +76,9 @@
     /* Stream over a socket, limited to a single stream */
     OUTPUT_FORMAT_RTP_AVP = 7,
 
+    /* H.264/AAC data encapsulated in MPEG2/TS */
+    OUTPUT_FORMAT_MPEG2TS = 8,
+
     OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
 };
 
diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h
index 551ca01..f2c6505 100644
--- a/include/media/stagefright/MPEG2TSWriter.h
+++ b/include/media/stagefright/MPEG2TSWriter.h
@@ -25,7 +25,10 @@
 
 namespace android {
 
+struct ABuffer;
+
 struct MPEG2TSWriter : public MediaWriter {
+    MPEG2TSWriter(int fd);
     MPEG2TSWriter(const char *filename);
 
     virtual status_t addSource(const sp<MediaSource> &source);
@@ -59,6 +62,8 @@
     int64_t mNumTSPacketsWritten;
     int64_t mNumTSPacketsBeforeMeta;
 
+    void init();
+
     void writeTS();
     void writeProgramAssociationTable();
     void writeProgramMap();
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 34a86ec..b38124e 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -191,6 +191,9 @@
 
         /** @hide Stream over a socket, limited to a single stream */
         public static final int OUTPUT_FORMAT_RTP_AVP = 7;
+
+        /** @hide H.264/AAC data encapsulated in MPEG2/TS */
+        public static final int OUTPUT_FORMAT_MPEG2TS = 8;
     };
 
     /**
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 9d53c25..e20e3ba 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -181,7 +181,7 @@
         LOGE("setOutputFormat called in an invalid state: %d", mCurrentState);
         return INVALID_OPERATION;
     }
-    if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_AUDIO_ONLY_START && of != OUTPUT_FORMAT_RTP_AVP) { //first non-video output format
+    if (mIsVideoSourceSet && of >= OUTPUT_FORMAT_AUDIO_ONLY_START && of != OUTPUT_FORMAT_RTP_AVP && of != OUTPUT_FORMAT_MPEG2TS) { //first non-video output format
         LOGE("output format (%d) is meant for audio recording only and incompatible with video recording", of);
         return INVALID_OPERATION;
     }
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index cf01ff6..d37d83d 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -24,6 +24,7 @@
 #include <media/stagefright/AudioSource.h>
 #include <media/stagefright/AMRWriter.h>
 #include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MPEG2TSWriter.h>
 #include <media/stagefright/MPEG4Writer.h>
 #include <media/stagefright/MediaDebug.h>
 #include <media/stagefright/MediaDefs.h>
@@ -632,6 +633,9 @@
         case OUTPUT_FORMAT_RTP_AVP:
             return startRTPRecording();
 
+        case OUTPUT_FORMAT_MPEG2TS:
+            return startMPEG2TSRecording();
+
         default:
             LOGE("Unsupported output file format: %d", mOutputFormat);
             return UNKNOWN_ERROR;
@@ -799,6 +803,52 @@
     return mWriter->start();
 }
 
+status_t StagefrightRecorder::startMPEG2TSRecording() {
+    CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS);
+
+    sp<MediaWriter> writer = new MPEG2TSWriter(dup(mOutputFd));
+
+    if (mAudioSource != AUDIO_SOURCE_LIST_END) {
+        if (mAudioEncoder != AUDIO_ENCODER_AAC) {
+            return ERROR_UNSUPPORTED;
+        }
+
+        status_t err = setupAudioEncoder(writer);
+
+        if (err != OK) {
+            return err;
+        }
+    }
+
+    if (mVideoSource == VIDEO_SOURCE_DEFAULT
+            || mVideoSource == VIDEO_SOURCE_CAMERA) {
+        if (mVideoEncoder != VIDEO_ENCODER_H264) {
+            return ERROR_UNSUPPORTED;
+        }
+
+        sp<MediaSource> encoder;
+        status_t err = setupVideoEncoder(&encoder);
+
+        if (err != OK) {
+            return err;
+        }
+
+        writer->addSource(encoder);
+    }
+
+    if (mMaxFileDurationUs != 0) {
+        writer->setMaxFileDuration(mMaxFileDurationUs);
+    }
+
+    if (mMaxFileSizeBytes != 0) {
+        writer->setMaxFileSize(mMaxFileSizeBytes);
+    }
+
+    mWriter = writer;
+
+    return mWriter->start();
+}
+
 void StagefrightRecorder::clipVideoFrameRate() {
     LOGV("clipVideoFrameRate: encoder %d", mVideoEncoder);
     int minFrameRate = mEncoderProfiles->getVideoEncoderParamByName(
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index 216f6bc..ad0dfa0 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -102,6 +102,7 @@
     status_t startAMRRecording();
     status_t startAACRecording();
     status_t startRTPRecording();
+    status_t startMPEG2TSRecording();
     sp<MediaSource> createAudioSource();
     status_t setupCameraSource();
     status_t setupAudioEncoder(const sp<MediaWriter>& writer);
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index ee74b88..b3daf67 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -63,6 +63,8 @@
     sp<ALooper> mLooper;
     sp<AMessage> mNotify;
 
+    sp<ABuffer> mAACCodecSpecificData;
+
     sp<ABuffer> mAACBuffer;
 
     unsigned mStreamType;
@@ -125,6 +127,8 @@
 void MPEG2TSWriter::SourceInfo::stop() {
     mLooper->unregisterHandler(id());
     mLooper->stop();
+
+    mSource->stop();
 }
 
 void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
@@ -133,18 +137,48 @@
     const char *mime;
     CHECK(meta->findCString(kKeyMIMEType, &mime));
 
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
+        uint32_t type;
+        const void *data;
+        size_t size;
+        if (!meta->findData(kKeyESDS, &type, &data, &size)) {
+            // Codec specific data better be in the first data buffer.
+            return;
+        }
+
+        ESDS esds((const char *)data, size);
+        CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+        const uint8_t *codec_specific_data;
+        size_t codec_specific_data_size;
+        esds.getCodecSpecificInfo(
+                (const void **)&codec_specific_data, &codec_specific_data_size);
+
+        CHECK_GE(codec_specific_data_size, 2u);
+
+        mAACCodecSpecificData = new ABuffer(codec_specific_data_size);
+
+        memcpy(mAACCodecSpecificData->data(), codec_specific_data,
+               codec_specific_data_size);
+
+        return;
+    }
+
     if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
         return;
     }
 
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!meta->findData(kKeyAVCC, &type, &data, &size)) {
+        // Codec specific data better be part of the data stream then.
+        return;
+    }
+
     sp<ABuffer> out = new ABuffer(1024);
     out->setRange(0, 0);
 
-    uint32_t type;
-    const void *data;
-    size_t size;
-    CHECK(meta->findData(kKeyAVCC, &type, &data, &size));
-
     const uint8_t *ptr = (const uint8_t *)data;
 
     size_t numSeqParameterSets = ptr[5] & 31;
@@ -250,21 +284,7 @@
         mAACBuffer->setRange(0, 0);
     }
 
-    sp<MetaData> meta = mSource->getFormat();
-    uint32_t type;
-    const void *data;
-    size_t size;
-    CHECK(meta->findData(kKeyESDS, &type, &data, &size));
-
-    ESDS esds((const char *)data, size);
-    CHECK_EQ(esds.InitCheck(), (status_t)OK);
-
-    const uint8_t *codec_specific_data;
-    size_t codec_specific_data_size;
-    esds.getCodecSpecificInfo(
-            (const void **)&codec_specific_data, &codec_specific_data_size);
-
-    CHECK_GE(codec_specific_data_size, 2u);
+    const uint8_t *codec_specific_data = mAACCodecSpecificData->data();
 
     unsigned profile = (codec_specific_data[0] >> 3) - 1;
 
@@ -355,7 +375,18 @@
             }
 
             if (err == OK) {
-                if (buffer->range_length() > 0) {
+                if (mStreamType == 0x0f && mAACCodecSpecificData == NULL) {
+                    // The first buffer contains codec specific data.
+
+                    CHECK_GE(buffer->range_length(), 2u);
+
+                    mAACCodecSpecificData = new ABuffer(buffer->range_length());
+
+                    memcpy(mAACCodecSpecificData->data(),
+                           (const uint8_t *)buffer->data()
+                            + buffer->range_offset(),
+                           buffer->range_length());
+                } else if (buffer->range_length() > 0) {
                     if (mStreamType == 0x0f) {
                         appendAACFrames(buffer);
                     } else {
@@ -378,12 +409,25 @@
 
 ////////////////////////////////////////////////////////////////////////////////
 
+MPEG2TSWriter::MPEG2TSWriter(int fd)
+    : mFile(fdopen(fd, "wb")),
+      mStarted(false),
+      mNumSourcesDone(0),
+      mNumTSPacketsWritten(0),
+      mNumTSPacketsBeforeMeta(0) {
+    init();
+}
+
 MPEG2TSWriter::MPEG2TSWriter(const char *filename)
     : mFile(fopen(filename, "wb")),
       mStarted(false),
       mNumSourcesDone(0),
       mNumTSPacketsWritten(0),
       mNumTSPacketsBeforeMeta(0) {
+    init();
+}
+
+void MPEG2TSWriter::init() {
     CHECK(mFile != NULL);
 
     mLooper = new ALooper;
@@ -396,6 +440,10 @@
 }
 
 MPEG2TSWriter::~MPEG2TSWriter() {
+    if (mStarted) {
+        stop();
+    }
+
     mLooper->unregisterHandler(mReflector->id());
     mLooper->stop();