Merge "Support for writing to MPEG2 transport stream files." into gingerbread
This commit is contained in:
committed by
Android (Google) Code Review
commit
312644cdbb
@@ -76,6 +76,9 @@ enum output_format {
|
||||
/* 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
|
||||
};
|
||||
|
||||
|
||||
@@ -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 @@ private:
|
||||
int64_t mNumTSPacketsWritten;
|
||||
int64_t mNumTSPacketsBeforeMeta;
|
||||
|
||||
void init();
|
||||
|
||||
void writeTS();
|
||||
void writeProgramAssociationTable();
|
||||
void writeProgramMap();
|
||||
|
||||
@@ -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;
|
||||
};
|
||||
|
||||
/**
|
||||
|
||||
@@ -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;
|
||||
}
|
||||
|
||||
@@ -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 @@ 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<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(
|
||||
|
||||
@@ -102,6 +102,7 @@ private:
|
||||
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);
|
||||
|
||||
@@ -63,6 +63,8 @@ private:
|
||||
sp<ALooper> mLooper;
|
||||
sp<AMessage> mNotify;
|
||||
|
||||
sp<ABuffer> mAACCodecSpecificData;
|
||||
|
||||
sp<ABuffer> mAACBuffer;
|
||||
|
||||
unsigned mStreamType;
|
||||
@@ -125,6 +127,8 @@ void MPEG2TSWriter::SourceInfo::start(const sp<AMessage> ¬ify) {
|
||||
void MPEG2TSWriter::SourceInfo::stop() {
|
||||
mLooper->unregisterHandler(id());
|
||||
mLooper->stop();
|
||||
|
||||
mSource->stop();
|
||||
}
|
||||
|
||||
void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
|
||||
@@ -133,18 +137,48 @@ void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
|
||||
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 @@ void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) {
|
||||
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 @@ void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &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<AMessage> &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();
|
||||
|
||||
|
||||
Reference in New Issue
Block a user