diff options
-rw-r--r-- | cmds/stagefright/stagefright.cpp | 6 | ||||
-rw-r--r-- | include/media/stagefright/MPEG2TSWriter.h | 72 | ||||
-rw-r--r-- | media/libstagefright/Android.mk | 1 | ||||
-rw-r--r-- | media/libstagefright/MPEG2TSWriter.cpp | 758 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/ATSParser.cpp | 48 | ||||
-rw-r--r-- | media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp | 55 |
6 files changed, 898 insertions, 42 deletions
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp index 8b54871..f55b746 100644 --- a/cmds/stagefright/stagefright.cpp +++ b/cmds/stagefright/stagefright.cpp @@ -46,6 +46,7 @@ #include <media/mediametadataretriever.h> #include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/MPEG2TSWriter.h> #include <media/stagefright/MPEG4Writer.h> #include <fcntl.h> @@ -366,8 +367,13 @@ status_t DetectSyncSource::read( static void writeSourcesToMP4( Vector<sp<MediaSource> > &sources, bool syncInfoPresent) { +#if 0 sp<MPEG4Writer> writer = new MPEG4Writer(gWriteMP4Filename.string()); +#else + sp<MPEG2TSWriter> writer = + new MPEG2TSWriter(gWriteMP4Filename.string()); +#endif // at most one minute. writer->setMaxFileDuration(60000000ll); diff --git a/include/media/stagefright/MPEG2TSWriter.h b/include/media/stagefright/MPEG2TSWriter.h new file mode 100644 index 0000000..551ca01 --- /dev/null +++ b/include/media/stagefright/MPEG2TSWriter.h @@ -0,0 +1,72 @@ +/* + * 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. + */ + +#ifndef MPEG2TS_WRITER_H_ + +#define MPEG2TS_WRITER_H_ + +#include <media/stagefright/foundation/ABase.h> +#include <media/stagefright/foundation/AHandlerReflector.h> +#include <media/stagefright/foundation/ALooper.h> +#include <media/stagefright/MediaWriter.h> + +namespace android { + +struct MPEG2TSWriter : public MediaWriter { + MPEG2TSWriter(const char *filename); + + virtual status_t addSource(const sp<MediaSource> &source); + virtual status_t start(MetaData *param = NULL); + virtual status_t stop(); + virtual status_t pause(); + virtual bool reachedEOS(); + virtual status_t dump(int fd, const Vector<String16>& args); + + void onMessageReceived(const sp<AMessage> &msg); + +protected: + virtual ~MPEG2TSWriter(); + +private: + enum { + kWhatSourceNotify = 'noti' + }; + + struct SourceInfo; + + FILE *mFile; + sp<ALooper> mLooper; + sp<AHandlerReflector<MPEG2TSWriter> > mReflector; + + bool mStarted; + + Vector<sp<SourceInfo> > mSources; + size_t mNumSourcesDone; + + int64_t mNumTSPacketsWritten; + int64_t mNumTSPacketsBeforeMeta; + + void writeTS(); + void writeProgramAssociationTable(); + void writeProgramMap(); + void writeAccessUnit(int32_t sourceIndex, const sp<ABuffer> &buffer); + + DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSWriter); +}; + +} // namespace android + +#endif // MPEG2TS_WRITER_H_ diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk index e0321a5..3e17a7e 100644 --- a/media/libstagefright/Android.mk +++ b/media/libstagefright/Android.mk @@ -16,6 +16,7 @@ LOCAL_SRC_FILES:= \ HTTPStream.cpp \ JPEGSource.cpp \ MP3Extractor.cpp \ + MPEG2TSWriter.cpp \ MPEG4Extractor.cpp \ MPEG4Writer.cpp \ MediaBuffer.cpp \ diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp new file mode 100644 index 0000000..ee74b88 --- /dev/null +++ b/media/libstagefright/MPEG2TSWriter.cpp @@ -0,0 +1,758 @@ +/* + * 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. + */ + +//#define LOG_NDEBUG 0 +#define LOG_TAG "MPEG2TSWriter" +#include <media/stagefright/foundation/ADebug.h> + +#include <media/stagefright/foundation/hexdump.h> +#include <media/stagefright/foundation/ABuffer.h> +#include <media/stagefright/foundation/AMessage.h> +#include <media/stagefright/MPEG2TSWriter.h> +#include <media/stagefright/MediaBuffer.h> +#include <media/stagefright/MediaDefs.h> +#include <media/stagefright/MediaErrors.h> +#include <media/stagefright/MediaSource.h> +#include <media/stagefright/MetaData.h> +#include <media/stagefright/Utils.h> + +#include "include/ESDS.h" + +namespace android { + +struct MPEG2TSWriter::SourceInfo : public AHandler { + SourceInfo(const sp<MediaSource> &source); + + void start(const sp<AMessage> ¬ify); + void stop(); + + unsigned streamType() const; + unsigned incrementContinuityCounter(); + + enum { + kNotifyStartFailed, + kNotifyBuffer, + kNotifyReachedEOS, + }; + +protected: + virtual void onMessageReceived(const sp<AMessage> &msg); + + virtual ~SourceInfo(); + +private: + enum { + kWhatStart = 'strt', + kWhatRead = 'read', + }; + + sp<MediaSource> mSource; + sp<ALooper> mLooper; + sp<AMessage> mNotify; + + sp<ABuffer> mAACBuffer; + + unsigned mStreamType; + unsigned mContinuityCounter; + + void extractCodecSpecificData(); + + void appendAACFrames(MediaBuffer *buffer); + void flushAACFrames(); + + void postAVCFrame(MediaBuffer *buffer); + + DISALLOW_EVIL_CONSTRUCTORS(SourceInfo); +}; + +MPEG2TSWriter::SourceInfo::SourceInfo(const sp<MediaSource> &source) + : mSource(source), + mLooper(new ALooper), + mStreamType(0), + mContinuityCounter(0) { + mLooper->setName("MPEG2TSWriter source"); + + sp<MetaData> meta = mSource->getFormat(); + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + mStreamType = 0x0f; + } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + mStreamType = 0x1b; + } else { + TRESPASS(); + } +} + +MPEG2TSWriter::SourceInfo::~SourceInfo() { +} + +unsigned MPEG2TSWriter::SourceInfo::streamType() const { + return mStreamType; +} + +unsigned MPEG2TSWriter::SourceInfo::incrementContinuityCounter() { + if (++mContinuityCounter == 16) { + mContinuityCounter = 0; + } + + return mContinuityCounter; +} + +void MPEG2TSWriter::SourceInfo::start(const sp<AMessage> ¬ify) { + mLooper->registerHandler(this); + mLooper->start(); + + mNotify = notify; + + (new AMessage(kWhatStart, id()))->post(); +} + +void MPEG2TSWriter::SourceInfo::stop() { + mLooper->unregisterHandler(id()); + mLooper->stop(); +} + +void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() { + sp<MetaData> meta = mSource->getFormat(); + + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) { + 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; + + ptr += 6; + size -= 6; + + for (size_t i = 0; i < numSeqParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + CHECK_LE(out->size() + 4 + length, out->capacity()); + memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4); + memcpy(out->data() + out->size() + 4, ptr, length); + out->setRange(0, out->size() + length + 4); + + ptr += length; + size -= length; + } + + CHECK(size >= 1); + size_t numPictureParameterSets = *ptr; + ++ptr; + --size; + + for (size_t i = 0; i < numPictureParameterSets; ++i) { + CHECK(size >= 2); + size_t length = U16_AT(ptr); + + ptr += 2; + size -= 2; + + CHECK(size >= length); + + CHECK_LE(out->size() + 4 + length, out->capacity()); + memcpy(out->data() + out->size(), "\x00\x00\x00\x01", 4); + memcpy(out->data() + out->size() + 4, ptr, length); + out->setRange(0, out->size() + length + 4); + + ptr += length; + size -= length; + } + + out->meta()->setInt64("timeUs", 0ll); + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyBuffer); + notify->setObject("buffer", out); + notify->post(); +} + +void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyBuffer); + + sp<ABuffer> copy = + new ABuffer(buffer->range_length()); + memcpy(copy->data(), + (const uint8_t *)buffer->data() + + buffer->range_offset(), + buffer->range_length()); + + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + copy->meta()->setInt64("timeUs", timeUs); + + int32_t isSync; + if (buffer->meta_data()->findInt32(kKeyIsSyncFrame, &isSync) + && isSync != 0) { + copy->meta()->setInt32("isSync", true); + } + + notify->setObject("buffer", copy); + notify->post(); +} + +void MPEG2TSWriter::SourceInfo::appendAACFrames(MediaBuffer *buffer) { + if (mAACBuffer != NULL + && mAACBuffer->size() + 7 + buffer->range_length() + > mAACBuffer->capacity()) { + flushAACFrames(); + } + + if (mAACBuffer == NULL) { + size_t alloc = 4096; + if (buffer->range_length() + 7 > alloc) { + alloc = 7 + buffer->range_length(); + } + + mAACBuffer = new ABuffer(alloc); + + int64_t timeUs; + CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs)); + + mAACBuffer->meta()->setInt64("timeUs", timeUs); + mAACBuffer->meta()->setInt32("isSync", true); + + 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); + + unsigned profile = (codec_specific_data[0] >> 3) - 1; + + unsigned sampling_freq_index = + ((codec_specific_data[0] & 7) << 1) + | (codec_specific_data[1] >> 7); + + unsigned channel_configuration = + (codec_specific_data[1] >> 3) & 0x0f; + + uint8_t *ptr = mAACBuffer->data() + mAACBuffer->size(); + + const uint32_t aac_frame_length = buffer->range_length() + 7; + + *ptr++ = 0xff; + *ptr++ = 0xf1; // b11110001, ID=0, layer=0, protection_absent=1 + + *ptr++ = + profile << 6 + | sampling_freq_index << 2 + | ((channel_configuration >> 2) & 1); // private_bit=0 + + // original_copy=0, home=0, copyright_id_bit=0, copyright_id_start=0 + *ptr++ = + (channel_configuration & 3) << 6 + | aac_frame_length >> 11; + *ptr++ = (aac_frame_length >> 3) & 0xff; + *ptr++ = (aac_frame_length & 7) << 5; + + // adts_buffer_fullness=0, number_of_raw_data_blocks_in_frame=0 + *ptr++ = 0; + + memcpy(ptr, + (const uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length()); + + ptr += buffer->range_length(); + + mAACBuffer->setRange(0, ptr - mAACBuffer->data()); +} + +void MPEG2TSWriter::SourceInfo::flushAACFrames() { + if (mAACBuffer == NULL) { + return; + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyBuffer); + notify->setObject("buffer", mAACBuffer); + notify->post(); + + mAACBuffer.clear(); +} + +void MPEG2TSWriter::SourceInfo::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatStart: + { + status_t err = mSource->start(); + if (err != OK) { + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyStartFailed); + notify->post(); + break; + } + + extractCodecSpecificData(); + + (new AMessage(kWhatRead, id()))->post(); + break; + } + + case kWhatRead: + { + MediaBuffer *buffer; + status_t err = mSource->read(&buffer); + + if (err != OK && err != INFO_FORMAT_CHANGED) { + if (mStreamType == 0x0f) { + flushAACFrames(); + } + + sp<AMessage> notify = mNotify->dup(); + notify->setInt32("what", kNotifyReachedEOS); + notify->setInt32("status", err); + notify->post(); + break; + } + + if (err == OK) { + if (buffer->range_length() > 0) { + if (mStreamType == 0x0f) { + appendAACFrames(buffer); + } else { + postAVCFrame(buffer); + } + } + + buffer->release(); + buffer = NULL; + } + + msg->post(); + break; + } + + default: + TRESPASS(); + } +} + +//////////////////////////////////////////////////////////////////////////////// + +MPEG2TSWriter::MPEG2TSWriter(const char *filename) + : mFile(fopen(filename, "wb")), + mStarted(false), + mNumSourcesDone(0), + mNumTSPacketsWritten(0), + mNumTSPacketsBeforeMeta(0) { + CHECK(mFile != NULL); + + mLooper = new ALooper; + mLooper->setName("MPEG2TSWriter"); + + mReflector = new AHandlerReflector<MPEG2TSWriter>(this); + + mLooper->registerHandler(mReflector); + mLooper->start(); +} + +MPEG2TSWriter::~MPEG2TSWriter() { + mLooper->unregisterHandler(mReflector->id()); + mLooper->stop(); + + fclose(mFile); + mFile = NULL; +} + +status_t MPEG2TSWriter::addSource(const sp<MediaSource> &source) { + CHECK(!mStarted); + + sp<MetaData> meta = source->getFormat(); + const char *mime; + CHECK(meta->findCString(kKeyMIMEType, &mime)); + + if (strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC) + && strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) { + return ERROR_UNSUPPORTED; + } + + sp<SourceInfo> info = new SourceInfo(source); + + mSources.push(info); + + return OK; +} + +status_t MPEG2TSWriter::start(MetaData *param) { + CHECK(!mStarted); + + mStarted = true; + mNumSourcesDone = 0; + mNumTSPacketsWritten = 0; + mNumTSPacketsBeforeMeta = 0; + + for (size_t i = 0; i < mSources.size(); ++i) { + sp<AMessage> notify = + new AMessage(kWhatSourceNotify, mReflector->id()); + + notify->setInt32("source-index", i); + + mSources.editItemAt(i)->start(notify); + } + + return OK; +} + +status_t MPEG2TSWriter::stop() { + CHECK(mStarted); + + for (size_t i = 0; i < mSources.size(); ++i) { + mSources.editItemAt(i)->stop(); + } + mStarted = false; + + return OK; +} + +status_t MPEG2TSWriter::pause() { + CHECK(mStarted); + + return OK; +} + +bool MPEG2TSWriter::reachedEOS() { + return !mStarted || (mNumSourcesDone == mSources.size() ? true : false); +} + +status_t MPEG2TSWriter::dump(int fd, const Vector<String16> &args) { + return OK; +} + +void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) { + switch (msg->what()) { + case kWhatSourceNotify: + { + int32_t sourceIndex; + CHECK(msg->findInt32("source-index", &sourceIndex)); + + int32_t what; + CHECK(msg->findInt32("what", &what)); + + if (what == SourceInfo::kNotifyReachedEOS + || what == SourceInfo::kNotifyStartFailed) { + ++mNumSourcesDone; + } else if (what == SourceInfo::kNotifyBuffer) { + sp<RefBase> obj; + CHECK(msg->findObject("buffer", &obj)); + + writeTS(); + + sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get()); + writeAccessUnit(sourceIndex, buffer); + } + break; + } + + default: + TRESPASS(); + } +} + +void MPEG2TSWriter::writeProgramAssociationTable() { + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b1 + // transport_priority = b0 + // PID = b0000000000000 (13 bits) + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // skip = 0x00 + // --- payload follows + // table_id = 0x00 + // section_syntax_indicator = b1 + // must_be_zero = b0 + // reserved = b11 + // section_length = 0x00d + // transport_stream_id = 0x0000 + // reserved = b11 + // version_number = b00001 + // current_next_indicator = b1 + // section_number = 0x00 + // last_section_number = 0x00 + // one program follows: + // program_number = 0x0001 + // reserved = b111 + // program_map_PID = 0x01e0 (13 bits!) + // CRC = 0x???????? + + static const uint8_t kData[] = { + 0x47, + 0x40, 0x00, 0x10, 0x00, // b0100 0000 0000 0000 0001 ???? 0000 0000 + 0x00, 0xb0, 0x0d, 0x00, // b0000 0000 1011 0000 0000 1101 0000 0000 + 0x00, 0xc3, 0x00, 0x00, // b0000 0000 1100 0011 0000 0000 0000 0000 + 0x00, 0x01, 0xe1, 0xe0, // b0000 0000 0000 0001 1110 0001 1110 0000 + 0x00, 0x00, 0x00, 0x00 // b???? ???? ???? ???? ???? ???? ???? ???? + }; + + sp<ABuffer> buffer = new ABuffer(188); + memset(buffer->data(), 0, buffer->size()); + memcpy(buffer->data(), kData, sizeof(kData)); + + static const unsigned kContinuityCounter = 5; + buffer->data()[3] |= kContinuityCounter; + + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); +} + +void MPEG2TSWriter::writeProgramMap() { + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b1 + // transport_priority = b0 + // PID = b0 0001 1110 0000 (13 bits) [0x1e0] + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // skip = 0x00 + // -- payload follows + // table_id = 0x02 + // section_syntax_indicator = b1 + // must_be_zero = b0 + // reserved = b11 + // section_length = 0x??? + // program_number = 0x0001 + // reserved = b11 + // version_number = b00001 + // current_next_indicator = b1 + // section_number = 0x00 + // last_section_number = 0x00 + // reserved = b111 + // PCR_PID = b? ???? ???? ???? (13 bits) + // reserved = b1111 + // program_info_length = 0x000 + // one or more elementary stream descriptions follow: + // stream_type = 0x?? + // reserved = b111 + // elementary_PID = b? ???? ???? ???? (13 bits) + // reserved = b1111 + // ES_info_length = 0x000 + // CRC = 0x???????? + + static const uint8_t kData[] = { + 0x47, + 0x41, 0xe0, 0x10, 0x00, // b0100 0001 1110 0000 0001 ???? 0000 0000 + 0x02, 0xb0, 0x00, 0x00, // b0000 0010 1011 ???? ???? ???? 0000 0000 + 0x01, 0xc3, 0x00, 0x00, // b0000 0001 1100 0011 0000 0000 0000 0000 + 0xe0, 0x00, 0xf0, 0x00 // b111? ???? ???? ???? 1111 0000 0000 0000 + }; + + sp<ABuffer> buffer = new ABuffer(188); + memset(buffer->data(), 0, buffer->size()); + memcpy(buffer->data(), kData, sizeof(kData)); + + static const unsigned kContinuityCounter = 5; + buffer->data()[3] |= kContinuityCounter; + + size_t section_length = 5 * mSources.size() + 4 + 9; + buffer->data()[6] |= section_length >> 8; + buffer->data()[7] = section_length & 0xff; + + static const unsigned kPCR_PID = 0x1e1; + buffer->data()[13] |= (kPCR_PID >> 8) & 0x1f; + buffer->data()[14] = kPCR_PID & 0xff; + + uint8_t *ptr = &buffer->data()[sizeof(kData)]; + for (size_t i = 0; i < mSources.size(); ++i) { + *ptr++ = mSources.editItemAt(i)->streamType(); + + const unsigned ES_PID = 0x1e0 + i + 1; + *ptr++ = 0xe0 | (ES_PID >> 8); + *ptr++ = ES_PID & 0xff; + *ptr++ = 0xf0; + *ptr++ = 0x00; + } + + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x00; + + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); +} + +void MPEG2TSWriter::writeAccessUnit( + int32_t sourceIndex, const sp<ABuffer> &accessUnit) { + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b1 + // transport_priority = b0 + // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex] + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // -- payload follows + // packet_startcode_prefix = 0x000001 + // stream_id = 0x?? (0xe0 for avc video, 0xc0 for aac audio) + // PES_packet_length = 0x???? + // reserved = b10 + // PES_scrambling_control = b00 + // PES_priority = b0 + // data_alignment_indicator = b1 + // copyright = b0 + // original_or_copy = b0 + // PTS_DTS_flags = b10 (PTS only) + // ESCR_flag = b0 + // ES_rate_flag = b0 + // DSM_trick_mode_flag = b0 + // additional_copy_info_flag = b0 + // PES_CRC_flag = b0 + // PES_extension_flag = b0 + // PES_header_data_length = 0x05 + // reserved = b0010 (PTS) + // PTS[32..30] = b??? + // reserved = b1 + // PTS[29..15] = b??? ???? ???? ???? (15 bits) + // reserved = b1 + // PTS[14..0] = b??? ???? ???? ???? (15 bits) + // reserved = b1 + // the first fragment of "buffer" follows + + sp<ABuffer> buffer = new ABuffer(188); + memset(buffer->data(), 0, buffer->size()); + + const unsigned PID = 0x1e0 + sourceIndex + 1; + + const unsigned continuity_counter = + mSources.editItemAt(sourceIndex)->incrementContinuityCounter(); + + // XXX if there are multiple streams of a kind (more than 1 audio or + // more than 1 video) they need distinct stream_ids. + const unsigned stream_id = + mSources.editItemAt(sourceIndex)->streamType() == 0x0f ? 0xc0 : 0xe0; + + int64_t timeUs; + CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs)); + + uint32_t PTS = (timeUs * 9ll) / 100ll; + + size_t PES_packet_length = accessUnit->size() + 8; + + uint8_t *ptr = buffer->data(); + *ptr++ = 0x47; + *ptr++ = 0x40 | (PID >> 8); + *ptr++ = PID & 0xff; + *ptr++ = 0x10 | continuity_counter; + *ptr++ = 0x00; + *ptr++ = 0x00; + *ptr++ = 0x01; + *ptr++ = stream_id; + *ptr++ = PES_packet_length >> 8; + *ptr++ = PES_packet_length & 0xff; + *ptr++ = 0x84; + *ptr++ = 0x80; + *ptr++ = 0x05; + *ptr++ = 0x20 | (((PTS >> 30) & 7) << 1) | 1; + *ptr++ = (PTS >> 22) & 0xff; + *ptr++ = (((PTS >> 15) & 0x7f) << 1) | 1; + *ptr++ = (PTS >> 7) & 0xff; + *ptr++ = ((PTS & 0x7f) << 1) | 1; + + size_t sizeLeft = buffer->data() + buffer->size() - ptr; + size_t copy = accessUnit->size(); + if (copy > sizeLeft) { + copy = sizeLeft; + } + + memcpy(ptr, accessUnit->data(), copy); + + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), buffer->size()); + + size_t offset = copy; + while (offset < accessUnit->size()) { + // for subsequent fragments of "buffer": + // 0x47 + // transport_error_indicator = b0 + // payload_unit_start_indicator = b0 + // transport_priority = b0 + // PID = b0 0001 1110 ???? (13 bits) [0x1e0 + 1 + sourceIndex] + // transport_scrambling_control = b00 + // adaptation_field_control = b01 (no adaptation field, payload only) + // continuity_counter = b???? + // the fragment of "buffer" follows. + + memset(buffer->data(), 0, buffer->size()); + + const unsigned continuity_counter = + mSources.editItemAt(sourceIndex)->incrementContinuityCounter(); + + ptr = buffer->data(); + *ptr++ = 0x47; + *ptr++ = 0x00 | (PID >> 8); + *ptr++ = PID & 0xff; + *ptr++ = 0x10 | continuity_counter; + + size_t sizeLeft = buffer->data() + buffer->size() - ptr; + size_t copy = accessUnit->size() - offset; + if (copy > sizeLeft) { + copy = sizeLeft; + } + + memcpy(ptr, accessUnit->data() + offset, copy); + CHECK_EQ(fwrite(buffer->data(), 1, buffer->size(), mFile), + buffer->size()); + + offset += copy; + } +} + +void MPEG2TSWriter::writeTS() { + if (mNumTSPacketsWritten >= mNumTSPacketsBeforeMeta) { + writeProgramAssociationTable(); + writeProgramMap(); + + mNumTSPacketsBeforeMeta = mNumTSPacketsWritten + 2500; + } +} + +} // namespace android + diff --git a/media/libstagefright/mpeg2ts/ATSParser.cpp b/media/libstagefright/mpeg2ts/ATSParser.cpp index 9952783..47cca80 100644 --- a/media/libstagefright/mpeg2ts/ATSParser.cpp +++ b/media/libstagefright/mpeg2ts/ATSParser.cpp @@ -389,20 +389,23 @@ void ATSParser::Stream::parsePES(ABitReader *br) { // ES data follows. - onPayloadData( - PTS_DTS_flags, PTS, DTS, - br->data(), br->numBitsLeft() / 8); - if (PES_packet_length != 0) { CHECK_GE(PES_packet_length, PES_header_data_length + 3); unsigned dataLength = PES_packet_length - 3 - PES_header_data_length; - CHECK_EQ(br->numBitsLeft(), dataLength * 8); + CHECK_GE(br->numBitsLeft(), dataLength * 8); + + onPayloadData( + PTS_DTS_flags, PTS, DTS, br->data(), dataLength); br->skipBits(dataLength * 8); } else { + onPayloadData( + PTS_DTS_flags, PTS, DTS, + br->data(), br->numBitsLeft() / 8); + size_t payloadSizeBits = br->numBitsLeft(); CHECK((payloadSizeBits % 8) == 0); @@ -491,7 +494,7 @@ static sp<ABuffer> MakeAVCCodecSpecificData( CHECK(picParamSet != NULL); buffer->setRange(stopOffset, size - stopOffset); - LOGI("buffer has %d bytes left.", buffer->size()); + LOGV("buffer has %d bytes left.", buffer->size()); size_t csdSize = 1 + 3 + 1 + 1 @@ -527,6 +530,8 @@ static bool getNextNALUnit( const uint8_t *data = *_data; size_t size = *_size; + // hexdump(data, size); + *nalStart = NULL; *nalSize = 0; @@ -572,18 +577,23 @@ static bool getNextNALUnit( ++offset; } - CHECK_LT(offset + 2, size); - *nalStart = &data[startOffset]; *nalSize = endOffset - startOffset; - *_data = &data[offset]; - *_size = size - offset; + if (offset + 2 < size) { + *_data = &data[offset]; + *_size = size - offset; + } else { + *_data = NULL; + *_size = 0; + } return true; } sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) { + // hexdump(data, size); + const uint8_t *tmpData = data; size_t tmpSize = size; @@ -591,6 +601,7 @@ sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) { const uint8_t *nalStart; size_t nalSize; while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) { + // hexdump(nalStart, nalSize); totalSize += 4 + nalSize; } @@ -615,15 +626,15 @@ static sp<ABuffer> FindMPEG2ADTSConfig( CHECK_EQ(br.getBits(2), 0u); br.getBits(1); // protection_absent unsigned profile = br.getBits(2); - LOGI("profile = %u", profile); + LOGV("profile = %u", profile); CHECK_NE(profile, 3u); unsigned sampling_freq_index = br.getBits(4); br.getBits(1); // private_bit unsigned channel_configuration = br.getBits(3); CHECK_NE(channel_configuration, 0u); - LOGI("sampling_freq_index = %u", sampling_freq_index); - LOGI("channel_configuration = %u", channel_configuration); + LOGV("sampling_freq_index = %u", sampling_freq_index); + LOGV("channel_configuration = %u", channel_configuration); CHECK_LE(sampling_freq_index, 11u); static const int32_t kSamplingFreq[] = { @@ -707,8 +718,8 @@ void ATSParser::Stream::onPayloadData( sp<ABuffer> csd = FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount); - LOGI("sampleRate = %d", sampleRate); - LOGI("channelCount = %d", channelCount); + LOGV("sampleRate = %d", sampleRate); + LOGV("channelCount = %d", channelCount); meta->setInt32(kKeySampleRate, sampleRate); meta->setInt32(kKeyChannelCount, channelCount); @@ -716,7 +727,7 @@ void ATSParser::Stream::onPayloadData( meta->setData(kKeyESDS, 0, csd->data(), csd->size()); } - LOGI("created source!"); + LOGV("created source!"); mSource = new AnotherPacketSource(meta); // fall through @@ -915,7 +926,10 @@ void ATSParser::parseTS(ABitReader *br) { unsigned adaptation_field_control = br->getBits(2); LOGV("adaptation_field_control = %u", adaptation_field_control); - MY_LOGV("continuity_counter = %u", br->getBits(4)); + unsigned continuity_counter = br->getBits(4); + LOGV("continuity_counter = %u", continuity_counter); + + // LOGI("PID = 0x%04x, continuity_counter = %u", PID, continuity_counter); if (adaptation_field_control == 2 || adaptation_field_control == 3) { parseAdaptationField(br); diff --git a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp index 56ca375..2417305 100644 --- a/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp +++ b/media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp @@ -32,6 +32,8 @@ namespace android { +static const size_t kTSPacketSize = 188; + struct MPEG2TSSource : public MediaSource { MPEG2TSSource( const sp<MPEG2TSExtractor> &extractor, @@ -126,27 +128,37 @@ sp<MetaData> MPEG2TSExtractor::getMetaData() { void MPEG2TSExtractor::init() { bool haveAudio = false; bool haveVideo = false; + int numPacketsParsed = 0; while (feedMore() == OK) { ATSParser::SourceType type; if (haveAudio && haveVideo) { break; } - if (haveVideo) { - type = ATSParser::MPEG2ADTS_AUDIO; - } else { - type = ATSParser::AVC_VIDEO; + if (!haveVideo) { + sp<AnotherPacketSource> impl = + (AnotherPacketSource *)mParser->getSource( + ATSParser::AVC_VIDEO).get(); + + if (impl != NULL) { + haveVideo = true; + mSourceImpls.push(impl); + } } - sp<AnotherPacketSource> impl = - (AnotherPacketSource *)mParser->getSource(type).get(); - if (impl != NULL) { - if (type == ATSParser::MPEG2ADTS_AUDIO) { + if (!haveAudio) { + sp<AnotherPacketSource> impl = + (AnotherPacketSource *)mParser->getSource( + ATSParser::MPEG2ADTS_AUDIO).get(); + + if (impl != NULL) { haveAudio = true; - } else { - haveVideo = true; + mSourceImpls.push(impl); } - mSourceImpls.push(impl); + } + + if (++numPacketsParsed > 1500) { + break; } } @@ -156,8 +168,6 @@ void MPEG2TSExtractor::init() { status_t MPEG2TSExtractor::feedMore() { Mutex::Autolock autoLock(mLock); - static const size_t kTSPacketSize = 188; - uint8_t packet[kTSPacketSize]; ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize); @@ -176,23 +186,18 @@ status_t MPEG2TSExtractor::feedMore() { bool SniffMPEG2TS( const sp<DataSource> &source, String8 *mimeType, float *confidence, sp<AMessage> *) { -#if 0 - char header; - if (source->readAt(0, &header, 1) != 1 || header != 0x47) { - return false; + for (int i = 0; i < 5; ++i) { + char header; + if (source->readAt(kTSPacketSize * i, &header, 1) != 1 + || header != 0x47) { + return false; + } } - *confidence = 0.05f; + *confidence = 0.1f; mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS); return true; -#else - // For now we're going to never identify this type of stream, since we'd - // just base our decision on a single byte... - // Instead you can instantiate an MPEG2TSExtractor by explicitly stating - // its proper mime type in the call to MediaExtractor::Create(...). - return false; -#endif } } // namespace android |