From 9adf466021d37a5062d7d3361e14bfd9e7ffeba6 Mon Sep 17 00:00:00 2001 From: Andreas Huber Date: Tue, 12 Oct 2010 14:17:45 -0700 Subject: Support for writing to MPEG2 transport stream files. Change-Id: If3b7a807bc224a4b1cb2236537c3ebdc5aee0d97 --- media/java/android/media/MediaRecorder.java | 3 + media/libmedia/mediarecorder.cpp | 2 +- .../libmediaplayerservice/StagefrightRecorder.cpp | 50 ++++++++++++ media/libmediaplayerservice/StagefrightRecorder.h | 1 + media/libstagefright/MPEG2TSWriter.cpp | 88 +++++++++++++++++----- 5 files changed, 123 insertions(+), 21 deletions(-) (limited to 'media') 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 @@ public class MediaRecorder /** @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 @@ status_t MediaRecorder::setOutputFormat(int of) 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 #include #include +#include #include #include #include @@ -632,6 +633,9 @@ status_t StagefrightRecorder::start() { 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 @@ status_t StagefrightRecorder::startRTPRecording() { return mWriter->start(); } +status_t StagefrightRecorder::startMPEG2TSRecording() { + CHECK_EQ(mOutputFormat, OUTPUT_FORMAT_MPEG2TS); + + sp 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 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 @@ private: status_t startAMRRecording(); status_t startAACRecording(); status_t startRTPRecording(); + status_t startMPEG2TSRecording(); sp createAudioSource(); status_t setupCameraSource(); status_t setupAudioEncoder(const sp& 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 @@ private: sp mLooper; sp mNotify; + sp mAACCodecSpecificData; + sp mAACBuffer; unsigned mStreamType; @@ -125,6 +127,8 @@ void MPEG2TSWriter::SourceInfo::start(const sp ¬ify) { void MPEG2TSWriter::SourceInfo::stop() { mLooper->unregisterHandler(id()); mLooper->stop(); + + mSource->stop(); } void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() { @@ -133,17 +137,47 @@ void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() { const char *mime; CHECK(meta->findCString(kKeyMIMEType, &mime)); - if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + 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; } - sp out = new ABuffer(1024); - out->setRange(0, 0); + if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + return; + } uint32_t type; const void *data; size_t size; - CHECK(meta->findData(kKeyAVCC, &type, &data, &size)); + if (!meta->findData(kKeyAVCC, &type, &data, &size)) { + // Codec specific data better be part of the data stream then. + return; + } + + sp out = new ABuffer(1024); + out->setRange(0, 0); const uint8_t *ptr = (const uint8_t *)data; @@ -250,21 +284,7 @@ void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) { mAACBuffer->setRange(0, 0); } - sp 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 @@ void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp &msg) { } 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 @@ void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp &msg) { //////////////////////////////////////////////////////////////////////////////// +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(const char *filename) } MPEG2TSWriter::~MPEG2TSWriter() { + if (mStarted) { + stop(); + } + mLooper->unregisterHandler(mReflector->id()); mLooper->stop(); -- cgit v1.1