Initial checkin of mpeg2 transport stream parser for stagefright.

Change-Id: I328ce77404daf7127933b48c9d58ed504fb8fc6f
This commit is contained in:
Andreas Huber
2010-06-07 13:05:37 -07:00
parent 455d41002e
commit a557b24ac4
16 changed files with 1614 additions and 3 deletions

View File

@@ -40,6 +40,7 @@ extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG4;
extern const char *MEDIA_MIMETYPE_CONTAINER_WAV;
extern const char *MEDIA_MIMETYPE_CONTAINER_OGG;
extern const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA;
extern const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS;
} // namespace android

View File

@@ -65,8 +65,9 @@ public class MediaFile {
public static final int FILE_TYPE_WMV = 25;
public static final int FILE_TYPE_ASF = 26;
public static final int FILE_TYPE_MKV = 27;
public static final int FILE_TYPE_MP2TS = 28;
private static final int FIRST_VIDEO_FILE_TYPE = FILE_TYPE_MP4;
private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MKV;
private static final int LAST_VIDEO_FILE_TYPE = FILE_TYPE_MP2TS;
// Image file types
public static final int FILE_TYPE_JPEG = 31;
@@ -156,6 +157,8 @@ public class MediaFile {
addFileType("3GPP2", FILE_TYPE_3GPP2, "video/3gpp2");
addFileType("MKV", FILE_TYPE_MKV, "video/x-matroska");
addFileType("WEBM", FILE_TYPE_MKV, "video/x-matroska");
addFileType("TS", FILE_TYPE_MP2TS, "video/mp2ts");
if (isWMVEnabled()) {
addFileType("WMV", FILE_TYPE_WMV, "video/x-ms-wmv");
addFileType("ASF", FILE_TYPE_ASF, "video/x-ms-asf");

View File

@@ -80,11 +80,13 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_matroska \
libstagefright_vpxdec \
libvpx \
libstagefright_mpeg2ts \
LOCAL_SHARED_LIBRARIES += \
libstagefright_amrnb_common \
libstagefright_enc_common \
libstagefright_avc_common
libstagefright_avc_common \
libstagefright_foundation \
ifeq ($(BUILD_WITH_FULL_STAGEFRIGHT),true)

View File

@@ -19,6 +19,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/MPEG2TSExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -97,6 +98,7 @@ void DataSource::RegisterDefaultSniffers() {
RegisterSniffer(SniffWAV);
RegisterSniffer(SniffOgg);
RegisterSniffer(SniffMatroska);
RegisterSniffer(SniffMPEG2TS);
}
// static

View File

@@ -38,5 +38,6 @@ const char *MEDIA_MIMETYPE_CONTAINER_MPEG4 = "video/mpeg4";
const char *MEDIA_MIMETYPE_CONTAINER_WAV = "audio/wav";
const char *MEDIA_MIMETYPE_CONTAINER_OGG = "application/ogg";
const char *MEDIA_MIMETYPE_CONTAINER_MATROSKA = "video/x-matroska";
const char *MEDIA_MIMETYPE_CONTAINER_MPEG2TS = "video/mp2ts";
} // namespace android

View File

@@ -23,6 +23,7 @@
#include "include/MPEG4Extractor.h"
#include "include/WAVExtractor.h"
#include "include/OggExtractor.h"
#include "include/MPEG2TSExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -73,6 +74,8 @@ sp<MediaExtractor> MediaExtractor::Create(
return new OggExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MATROSKA)) {
return new MatroskaExtractor(source);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_CONTAINER_MPEG2TS)) {
return new MPEG2TSExtractor(source);
}
return NULL;

View File

@@ -39,7 +39,7 @@ static bool FileHasAcceptableExtension(const char *extension) {
".mp3", ".mp4", ".m4a", ".3gp", ".3gpp", ".3g2", ".3gpp2",
".mpeg", ".ogg", ".mid", ".smf", ".imy", ".wma", ".aac",
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
".mkv", ".mka", ".webm"
".mkv", ".mka", ".webm", ".ts"
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);

View File

@@ -0,0 +1,54 @@
#ifndef MPEG2_TS_EXTRACTOR_H_
#define MPEG2_TS_EXTRACTOR_H_
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaExtractor.h>
#include <utils/threads.h>
#include <utils/Vector.h>
namespace android {
struct AnotherPacketSource;
struct ATSParser;
struct DataSource;
struct MPEG2TSSource;
struct String8;
struct MPEG2TSExtractor : public MediaExtractor {
MPEG2TSExtractor(const sp<DataSource> &source);
virtual size_t countTracks();
virtual sp<MediaSource> getTrack(size_t index);
virtual sp<MetaData> getTrackMetaData(size_t index, uint32_t flags);
virtual sp<MetaData> getMetaData();
virtual uint32_t flags() const {
return CAN_PAUSE;
}
private:
friend struct MPEG2TSSource;
Mutex mLock;
sp<DataSource> mDataSource;
sp<ATSParser> mParser;
Vector<sp<AnotherPacketSource> > mSourceImpls;
off_t mOffset;
void init();
status_t feedMore();
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSExtractor);
};
bool SniffMPEG2TS(
const sp<DataSource> &source, String8 *mimeType, float *confidence);
} // namespace android
#endif // MPEG2_TS_EXTRACTOR_H_

View File

@@ -0,0 +1,98 @@
/*
* 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.
*/
#include "ABitReader.h"
#include <media/stagefright/foundation/ADebug.h>
namespace android {
ABitReader::ABitReader(const uint8_t *data, size_t size)
: mData(data),
mSize(size),
mReservoir(0),
mNumBitsLeft(0) {
}
void ABitReader::fillReservoir() {
CHECK_GT(mSize, 0u);
mReservoir = 0;
size_t i;
for (i = 0; mSize > 0 && i < 4; ++i) {
mReservoir = (mReservoir << 8) | *mData;
++mData;
--mSize;
}
mNumBitsLeft = 8 * i;
mReservoir <<= 32 - mNumBitsLeft;
}
uint32_t ABitReader::getBits(size_t n) {
CHECK_LE(n, 32u);
uint32_t result = 0;
while (n > 0) {
if (mNumBitsLeft == 0) {
fillReservoir();
}
size_t m = n;
if (m > mNumBitsLeft) {
m = mNumBitsLeft;
}
result = (result << m) | (mReservoir >> (32 - m));
mReservoir <<= m;
mNumBitsLeft -= m;
n -= m;
}
return result;
}
void ABitReader::skipBits(size_t n) {
while (n > 32) {
getBits(32);
n -= 32;
}
if (n > 0) {
getBits(n);
}
}
void ABitReader::putBits(uint32_t x, size_t n) {
CHECK_LE(mNumBitsLeft + n, 32u);
mReservoir = (mReservoir >> n) | (x << (32 - n));
mNumBitsLeft += n;
}
size_t ABitReader::numBitsLeft() const {
return mSize * 8 + mNumBitsLeft;
}
const uint8_t *ABitReader::data() const {
CHECK_EQ(mNumBitsLeft % 8, 0u);
return mData - mNumBitsLeft / 8;
}
} // namespace android

View File

@@ -0,0 +1,53 @@
/*
* 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 A_BIT_READER_H_
#define A_BIT_READER_H_
#include <media/stagefright/foundation/ABase.h>
#include <sys/types.h>
#include <stdint.h>
namespace android {
struct ABitReader {
ABitReader(const uint8_t *data, size_t size);
uint32_t getBits(size_t n);
void skipBits(size_t n);
size_t numBitsLeft() const;
const uint8_t *data() const;
private:
const uint8_t *mData;
size_t mSize;
uint32_t mReservoir; // left-aligned bits
size_t mNumBitsLeft;
void fillReservoir();
void putBits(uint32_t x, size_t n);
DISALLOW_EVIL_CONSTRUCTORS(ABitReader);
};
} // namespace android
#endif // A_BIT_READER_H_

View File

@@ -0,0 +1,941 @@
/*
* 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.
*/
#include "ATSParser.h"
#include "ABitReader.h"
#include "AnotherPacketSource.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
#include <utils/KeyedVector.h>
namespace android {
static const size_t kTSPacketSize = 188;
struct ATSParser::Program : public RefBase {
Program(unsigned programMapPID);
bool parsePID(
unsigned pid, unsigned payload_unit_start_indicator,
ABitReader *br);
sp<MediaSource> getSource(SourceType type);
private:
unsigned mProgramMapPID;
KeyedVector<unsigned, sp<Stream> > mStreams;
void parseProgramMap(ABitReader *br);
DISALLOW_EVIL_CONSTRUCTORS(Program);
};
struct ATSParser::Stream : public RefBase {
Stream(unsigned elementaryPID, unsigned streamType);
void parse(
unsigned payload_unit_start_indicator,
ABitReader *br);
sp<MediaSource> getSource(SourceType type);
protected:
virtual ~Stream();
private:
unsigned mElementaryPID;
unsigned mStreamType;
sp<ABuffer> mBuffer;
sp<AnotherPacketSource> mSource;
bool mPayloadStarted;
void flush();
void parsePES(ABitReader *br);
void onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
const uint8_t *data, size_t size);
DISALLOW_EVIL_CONSTRUCTORS(Stream);
};
////////////////////////////////////////////////////////////////////////////////
ATSParser::Program::Program(unsigned programMapPID)
: mProgramMapPID(programMapPID) {
}
bool ATSParser::Program::parsePID(
unsigned pid, unsigned payload_unit_start_indicator,
ABitReader *br) {
if (pid == mProgramMapPID) {
if (payload_unit_start_indicator) {
unsigned skip = br->getBits(8);
br->skipBits(skip * 8);
}
parseProgramMap(br);
return true;
}
ssize_t index = mStreams.indexOfKey(pid);
if (index < 0) {
return false;
}
mStreams.editValueAt(index)->parse(
payload_unit_start_indicator, br);
return true;
}
void ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned table_id = br->getBits(8);
LOG(VERBOSE) << " table_id = " << table_id;
CHECK_EQ(table_id, 0x02u);
unsigned section_syntax_indictor = br->getBits(1);
LOG(VERBOSE) << " section_syntax_indictor = " << section_syntax_indictor;
CHECK_EQ(section_syntax_indictor, 1u);
CHECK_EQ(br->getBits(1), 0u);
LOG(VERBOSE) << " reserved = " << br->getBits(2);
unsigned section_length = br->getBits(12);
LOG(VERBOSE) << " section_length = " << section_length;
CHECK((section_length & 0xc00) == 0);
CHECK_LE(section_length, 1021u);
LOG(VERBOSE) << " program_number = " << br->getBits(16);
LOG(VERBOSE) << " reserved = " << br->getBits(2);
LOG(VERBOSE) << " version_number = " << br->getBits(5);
LOG(VERBOSE) << " current_next_indicator = " << br->getBits(1);
LOG(VERBOSE) << " section_number = " << br->getBits(8);
LOG(VERBOSE) << " last_section_number = " << br->getBits(8);
LOG(VERBOSE) << " reserved = " << br->getBits(3);
LOG(VERBOSE) << " PCR_PID = "
<< StringPrintf("0x%04x", br->getBits(13));
LOG(VERBOSE) << " reserved = " << br->getBits(4);
unsigned program_info_length = br->getBits(12);
LOG(VERBOSE) << " program_info_length = " << program_info_length;
CHECK((program_info_length & 0xc00) == 0);
br->skipBits(program_info_length * 8); // skip descriptors
// infoBytesRemaining is the number of bytes that make up the
// variable length section of ES_infos. It does not include the
// final CRC.
size_t infoBytesRemaining = section_length - 9 - program_info_length - 4;
while (infoBytesRemaining > 0) {
CHECK_GE(infoBytesRemaining, 5u);
unsigned streamType = br->getBits(8);
LOG(VERBOSE) << " stream_type = "
<< StringPrintf("0x%02x", streamType);
LOG(VERBOSE) << " reserved = " << br->getBits(3);
unsigned elementaryPID = br->getBits(13);
LOG(VERBOSE) << " elementary_PID = "
<< StringPrintf("0x%04x", elementaryPID);
LOG(VERBOSE) << " reserved = " << br->getBits(4);
unsigned ES_info_length = br->getBits(12);
LOG(VERBOSE) << " ES_info_length = " << ES_info_length;
CHECK((ES_info_length & 0xc00) == 0);
CHECK_GE(infoBytesRemaining - 5, ES_info_length);
#if 0
br->skipBits(ES_info_length * 8); // skip descriptors
#else
unsigned info_bytes_remaining = ES_info_length;
while (info_bytes_remaining >= 2) {
LOG(VERBOSE) << " tag = " << StringPrintf("0x%02x", br->getBits(8));
unsigned descLength = br->getBits(8);
LOG(VERBOSE) << " len = " << descLength;
CHECK_GE(info_bytes_remaining, 2 + descLength);
br->skipBits(descLength * 8);
info_bytes_remaining -= descLength + 2;
}
CHECK_EQ(info_bytes_remaining, 0u);
#endif
ssize_t index = mStreams.indexOfKey(elementaryPID);
#if 0 // XXX revisit
CHECK_LT(index, 0);
mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
#else
if (index < 0) {
mStreams.add(elementaryPID, new Stream(elementaryPID, streamType));
}
#endif
infoBytesRemaining -= 5 + ES_info_length;
}
CHECK_EQ(infoBytesRemaining, 0u);
LOG(VERBOSE) << " CRC = " << StringPrintf("0x%08x", br->getBits(32));
}
sp<MediaSource> ATSParser::Program::getSource(SourceType type) {
for (size_t i = 0; i < mStreams.size(); ++i) {
sp<MediaSource> source = mStreams.editValueAt(i)->getSource(type);
if (source != NULL) {
return source;
}
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
ATSParser::Stream::Stream(unsigned elementaryPID, unsigned streamType)
: mElementaryPID(elementaryPID),
mStreamType(streamType),
mBuffer(new ABuffer(65536)),
mPayloadStarted(false) {
mBuffer->setRange(0, 0);
}
ATSParser::Stream::~Stream() {
}
void ATSParser::Stream::parse(
unsigned payload_unit_start_indicator, ABitReader *br) {
if (payload_unit_start_indicator) {
if (mPayloadStarted) {
// Otherwise we run the danger of receiving the trailing bytes
// of a PES packet that we never saw the start of and assuming
// we have a a complete PES packet.
flush();
}
mPayloadStarted = true;
}
if (!mPayloadStarted) {
return;
}
size_t payloadSizeBits = br->numBitsLeft();
CHECK_EQ(payloadSizeBits % 8, 0u);
CHECK_LE(mBuffer->size() + payloadSizeBits / 8, mBuffer->capacity());
memcpy(mBuffer->data() + mBuffer->size(), br->data(), payloadSizeBits / 8);
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
}
void ATSParser::Stream::parsePES(ABitReader *br) {
unsigned packet_startcode_prefix = br->getBits(24);
LOG(VERBOSE) << "packet_startcode_prefix = "
<< StringPrintf("0x%08x", packet_startcode_prefix);
CHECK_EQ(packet_startcode_prefix, 0x000001u);
unsigned stream_id = br->getBits(8);
LOG(VERBOSE) << "stream_id = " << StringPrintf("0x%02x", stream_id);
unsigned PES_packet_length = br->getBits(16);
LOG(VERBOSE) << "PES_packet_length = " << PES_packet_length;
if (stream_id != 0xbc // program_stream_map
&& stream_id != 0xbe // padding_stream
&& stream_id != 0xbf // private_stream_2
&& stream_id != 0xf0 // ECM
&& stream_id != 0xf1 // EMM
&& stream_id != 0xff // program_stream_directory
&& stream_id != 0xf2 // DSMCC
&& stream_id != 0xf8) { // H.222.1 type E
CHECK_EQ(br->getBits(2), 2u);
LOG(VERBOSE) << "PES_scrambling_control = " << br->getBits(2);
LOG(VERBOSE) << "PES_priority = " << br->getBits(1);
LOG(VERBOSE) << "data_alignment_indicator = " << br->getBits(1);
LOG(VERBOSE) << "copyright = " << br->getBits(1);
LOG(VERBOSE) << "original_or_copy = " << br->getBits(1);
unsigned PTS_DTS_flags = br->getBits(2);
LOG(VERBOSE) << "PTS_DTS_flags = " << PTS_DTS_flags;
unsigned ESCR_flag = br->getBits(1);
LOG(VERBOSE) << "ESCR_flag = " << ESCR_flag;
unsigned ES_rate_flag = br->getBits(1);
LOG(VERBOSE) << "ES_rate_flag = " << ES_rate_flag;
unsigned DSM_trick_mode_flag = br->getBits(1);
LOG(VERBOSE) << "DSM_trick_mode_flag = " << DSM_trick_mode_flag;
unsigned additional_copy_info_flag = br->getBits(1);
LOG(VERBOSE) << "additional_copy_info_flag = "
<< additional_copy_info_flag;
LOG(VERBOSE) << "PES_CRC_flag = " << br->getBits(1);
LOG(VERBOSE) << "PES_extension_flag = " << br->getBits(1);
unsigned PES_header_data_length = br->getBits(8);
LOG(VERBOSE) << "PES_header_data_length = " << PES_header_data_length;
unsigned optional_bytes_remaining = PES_header_data_length;
uint64_t PTS = 0, DTS = 0;
if (PTS_DTS_flags == 2 || PTS_DTS_flags == 3) {
CHECK_GE(optional_bytes_remaining, 5u);
CHECK_EQ(br->getBits(4), PTS_DTS_flags);
PTS = ((uint64_t)br->getBits(3)) << 30;
CHECK_EQ(br->getBits(1), 1u);
PTS |= ((uint64_t)br->getBits(15)) << 15;
CHECK_EQ(br->getBits(1), 1u);
PTS |= br->getBits(15);
CHECK_EQ(br->getBits(1), 1u);
LOG(VERBOSE) << "PTS = " << PTS;
// LOG(INFO) << "PTS = " << PTS / 90000.0f << " secs";
optional_bytes_remaining -= 5;
if (PTS_DTS_flags == 3) {
CHECK_GE(optional_bytes_remaining, 5u);
CHECK_EQ(br->getBits(4), 1u);
DTS = ((uint64_t)br->getBits(3)) << 30;
CHECK_EQ(br->getBits(1), 1u);
DTS |= ((uint64_t)br->getBits(15)) << 15;
CHECK_EQ(br->getBits(1), 1u);
DTS |= br->getBits(15);
CHECK_EQ(br->getBits(1), 1u);
LOG(VERBOSE) << "DTS = " << DTS;
optional_bytes_remaining -= 5;
}
}
if (ESCR_flag) {
CHECK_GE(optional_bytes_remaining, 6u);
br->getBits(2);
uint64_t ESCR = ((uint64_t)br->getBits(3)) << 30;
CHECK_EQ(br->getBits(1), 1u);
ESCR |= ((uint64_t)br->getBits(15)) << 15;
CHECK_EQ(br->getBits(1), 1u);
ESCR |= br->getBits(15);
CHECK_EQ(br->getBits(1), 1u);
LOG(VERBOSE) << "ESCR = " << ESCR;
LOG(VERBOSE) << "ESCR_extension = " << br->getBits(9);
CHECK_EQ(br->getBits(1), 1u);
optional_bytes_remaining -= 6;
}
if (ES_rate_flag) {
CHECK_GE(optional_bytes_remaining, 3u);
CHECK_EQ(br->getBits(1), 1u);
LOG(VERBOSE) << "ES_rate = " << br->getBits(22);
CHECK_EQ(br->getBits(1), 1u);
optional_bytes_remaining -= 3;
}
br->skipBits(optional_bytes_remaining * 8);
// 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);
br->skipBits(dataLength * 8);
} else {
size_t payloadSizeBits = br->numBitsLeft();
CHECK((payloadSizeBits % 8) == 0);
LOG(VERBOSE) << "There's " << (payloadSizeBits / 8)
<< " bytes of payload.";
}
} else if (stream_id == 0xbe) { // padding_stream
CHECK_NE(PES_packet_length, 0u);
br->skipBits(PES_packet_length * 8);
} else {
CHECK_NE(PES_packet_length, 0u);
br->skipBits(PES_packet_length * 8);
}
}
void ATSParser::Stream::flush() {
if (mBuffer->size() == 0) {
return;
}
LOG(VERBOSE) << "flushing stream "
<< StringPrintf("0x%04x", mElementaryPID)
<< " size = " << mBuffer->size();
ABitReader br(mBuffer->data(), mBuffer->size());
parsePES(&br);
mBuffer->setRange(0, 0);
}
static sp<ABuffer> FindNAL(
const uint8_t *data, size_t size, unsigned nalType,
size_t *stopOffset) {
bool foundStart = false;
size_t startOffset = 0;
size_t offset = 0;
for (;;) {
while (offset + 3 < size
&& memcmp("\x00\x00\x00\x01", &data[offset], 4)) {
++offset;
}
if (foundStart) {
size_t nalSize;
if (offset + 3 >= size) {
nalSize = size - startOffset;
} else {
nalSize = offset - startOffset;
}
sp<ABuffer> nal = new ABuffer(nalSize);
memcpy(nal->data(), &data[startOffset], nalSize);
if (stopOffset != NULL) {
*stopOffset = startOffset + nalSize;
}
return nal;
}
if (offset + 4 >= size) {
return NULL;
}
if ((data[offset + 4] & 0x1f) == nalType) {
foundStart = true;
startOffset = offset + 4;
}
offset += 4;
}
}
static unsigned parseUE(ABitReader *br) {
unsigned numZeroes = 0;
while (br->getBits(1) == 0) {
++numZeroes;
}
unsigned x = br->getBits(numZeroes);
return x + (1u << numZeroes) - 1;
}
// Determine video dimensions from the sequence parameterset.
static void FindDimensions(
const sp<ABuffer> seqParamSet, int32_t *width, int32_t *height) {
ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
unsigned profile_idc = br.getBits(8);
br.skipBits(16);
parseUE(&br); // seq_parameter_set_id
if (profile_idc == 100 || profile_idc == 110
|| profile_idc == 122 || profile_idc == 144) {
TRESPASS();
}
parseUE(&br); // log2_max_frame_num_minus4
unsigned pic_order_cnt_type = parseUE(&br);
if (pic_order_cnt_type == 0) {
parseUE(&br); // log2_max_pic_order_cnt_lsb_minus4
} else if (pic_order_cnt_type == 1) {
br.getBits(1); // delta_pic_order_always_zero_flag
parseUE(&br); // offset_for_non_ref_pic
parseUE(&br); // offset_for_top_to_bottom_field
unsigned num_ref_frames_in_pic_order_cnt_cycle = parseUE(&br);
for (unsigned i = 0; i < num_ref_frames_in_pic_order_cnt_cycle; ++i) {
parseUE(&br); // offset_for_ref_frame
}
}
parseUE(&br); // num_ref_frames
br.getBits(1); // gaps_in_frame_num_value_allowed_flag
unsigned pic_width_in_mbs_minus1 = parseUE(&br);
unsigned pic_height_in_map_units_minus1 = parseUE(&br);
unsigned frame_mbs_only_flag = br.getBits(1);
*width = pic_width_in_mbs_minus1 * 16 + 16;
*height = (2 - frame_mbs_only_flag)
* (pic_height_in_map_units_minus1 * 16 + 16);
}
static sp<ABuffer> MakeAVCCodecSpecificData(
const sp<ABuffer> &buffer, int32_t *width, int32_t *height) {
const uint8_t *data = buffer->data();
size_t size = buffer->size();
sp<ABuffer> seqParamSet = FindNAL(data, size, 7, NULL);
if (seqParamSet == NULL) {
return NULL;
}
FindDimensions(seqParamSet, width, height);
size_t stopOffset;
sp<ABuffer> picParamSet = FindNAL(data, size, 8, &stopOffset);
CHECK(picParamSet != NULL);
buffer->setRange(stopOffset, size - stopOffset);
LOG(INFO) << "buffer has " << buffer->size() << " bytes left.";
size_t csdSize =
1 + 3 + 1 + 1
+ 2 * 1 + seqParamSet->size()
+ 1 + 2 * 1 + picParamSet->size();
sp<ABuffer> csd = new ABuffer(csdSize);
uint8_t *out = csd->data();
*out++ = 0x01; // configurationVersion
memcpy(out, seqParamSet->data() + 1, 3); // profile/level...
out += 3;
*out++ = (0x3f << 2) | 1; // lengthSize == 2 bytes
*out++ = 0xe0 | 1;
*out++ = seqParamSet->size() >> 8;
*out++ = seqParamSet->size() & 0xff;
memcpy(out, seqParamSet->data(), seqParamSet->size());
out += seqParamSet->size();
*out++ = 1;
*out++ = picParamSet->size() >> 8;
*out++ = picParamSet->size() & 0xff;
memcpy(out, picParamSet->data(), picParamSet->size());
return csd;
}
static bool getNextNALUnit(
const uint8_t **_data, size_t *_size,
const uint8_t **nalStart, size_t *nalSize) {
const uint8_t *data = *_data;
size_t size = *_size;
*nalStart = NULL;
*nalSize = 0;
if (size == 0) {
return false;
}
size_t offset = 0;
for (;;) {
CHECK_LT(offset + 2, size);
if (!memcmp("\x00\x00\x01", &data[offset], 3)) {
break;
}
CHECK_EQ((unsigned)data[offset], 0x00u);
++offset;
}
offset += 3;
size_t startOffset = offset;
while (offset + 2 < size
&& memcmp("\x00\x00\x00", &data[offset], 3)
&& memcmp("\x00\x00\x01", &data[offset], 3)) {
++offset;
}
if (offset + 2 >= size) {
*nalStart = &data[startOffset];
*nalSize = size - startOffset;
*_data = NULL;
*_size = 0;
return true;
}
size_t endOffset = offset;
while (offset + 2 < size && memcmp("\x00\x00\x01", &data[offset], 3)) {
CHECK_EQ((unsigned)data[offset], 0x00u);
++offset;
}
CHECK_LT(offset + 2, size);
*nalStart = &data[startOffset];
*nalSize = endOffset - startOffset;
*_data = &data[offset];
*_size = size - offset;
return true;
}
sp<ABuffer> MakeCleanAVCData(const uint8_t *data, size_t size) {
const uint8_t *tmpData = data;
size_t tmpSize = size;
size_t totalSize = 0;
const uint8_t *nalStart;
size_t nalSize;
while (getNextNALUnit(&tmpData, &tmpSize, &nalStart, &nalSize)) {
totalSize += 4 + nalSize;
}
sp<ABuffer> buffer = new ABuffer(totalSize);
size_t offset = 0;
while (getNextNALUnit(&data, &size, &nalStart, &nalSize)) {
memcpy(buffer->data() + offset, "\x00\x00\x00\x01", 4);
memcpy(buffer->data() + offset + 4, nalStart, nalSize);
offset += 4 + nalSize;
}
return buffer;
}
static sp<ABuffer> FindMPEG2ADTSConfig(
const sp<ABuffer> &buffer, int32_t *sampleRate, int32_t *channelCount) {
ABitReader br(buffer->data(), buffer->size());
CHECK_EQ(br.getBits(12), 0xfffu);
CHECK_EQ(br.getBits(1), 0u);
CHECK_EQ(br.getBits(2), 0u);
br.getBits(1); // protection_absent
unsigned profile = br.getBits(2);
LOG(INFO) << "profile = " << 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);
LOG(INFO) << "sampling_freq_index = " << sampling_freq_index;
LOG(INFO) << "channel_configuration = " << channel_configuration;
CHECK_LE(sampling_freq_index, 11u);
static const int32_t kSamplingFreq[] = {
96000, 88200, 64000, 48000, 44100, 32000, 24000, 22050,
16000, 12000, 11025, 8000
};
*sampleRate = kSamplingFreq[sampling_freq_index];
*channelCount = channel_configuration;
static const uint8_t kStaticESDS[] = {
0x03, 22,
0x00, 0x00, // ES_ID
0x00, // streamDependenceFlag, URL_Flag, OCRstreamFlag
0x04, 17,
0x40, // Audio ISO/IEC 14496-3
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x00, 0x00, 0x00, 0x00,
0x05, 2,
// AudioSpecificInfo follows
// oooo offf fccc c000
// o - audioObjectType
// f - samplingFreqIndex
// c - channelConfig
};
sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
csd->data()[sizeof(kStaticESDS)] =
((profile + 1) << 3) | (sampling_freq_index >> 1);
csd->data()[sizeof(kStaticESDS) + 1] =
((sampling_freq_index << 7) & 0x80) | (channel_configuration << 3);
hexdump(csd->data(), csd->size());
return csd;
}
void ATSParser::Stream::onPayloadData(
unsigned PTS_DTS_flags, uint64_t PTS, uint64_t DTS,
const uint8_t *data, size_t size) {
LOG(VERBOSE) << "onPayloadData mStreamType="
<< StringPrintf("0x%02x", mStreamType);
sp<ABuffer> buffer;
if (mStreamType == 0x1b) {
buffer = MakeCleanAVCData(data, size);
} else {
// hexdump(data, size);
buffer = new ABuffer(size);
memcpy(buffer->data(), data, size);
}
if (mSource == NULL) {
sp<MetaData> meta = new MetaData;
if (mStreamType == 0x1b) {
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
int32_t width, height;
sp<ABuffer> csd = MakeAVCCodecSpecificData(buffer, &width, &height);
if (csd == NULL) {
return;
}
meta->setData(kKeyAVCC, 0, csd->data(), csd->size());
meta->setInt32(kKeyWidth, width);
meta->setInt32(kKeyHeight, height);
} else {
CHECK_EQ(mStreamType, 0x0fu);
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
int32_t sampleRate, channelCount;
sp<ABuffer> csd =
FindMPEG2ADTSConfig(buffer, &sampleRate, &channelCount);
LOG(INFO) << "sampleRate = " << sampleRate;
LOG(INFO) << "channelCount = " << channelCount;
meta->setInt32(kKeySampleRate, sampleRate);
meta->setInt32(kKeyChannelCount, channelCount);
meta->setData(kKeyESDS, 0, csd->data(), csd->size());
}
LOG(INFO) << "created source!";
mSource = new AnotherPacketSource(meta);
// fall through
}
CHECK(PTS_DTS_flags == 2 || PTS_DTS_flags == 3);
buffer->meta()->setInt64("time", (PTS * 100) / 9);
if (mStreamType == 0x0f) {
// WHY???
buffer->setRange(7, buffer->size() - 7);
}
mSource->queueAccessUnit(buffer);
}
sp<MediaSource> ATSParser::Stream::getSource(SourceType type) {
if ((type == AVC_VIDEO && mStreamType == 0x1b)
|| (type == MPEG2ADTS_AUDIO && mStreamType == 0x0f)) {
return mSource;
}
return NULL;
}
////////////////////////////////////////////////////////////////////////////////
ATSParser::ATSParser() {
}
ATSParser::~ATSParser() {
}
void ATSParser::feedTSPacket(const void *data, size_t size) {
CHECK_EQ(size, kTSPacketSize);
ABitReader br((const uint8_t *)data, kTSPacketSize);
parseTS(&br);
}
void ATSParser::parseProgramAssociationTable(ABitReader *br) {
unsigned table_id = br->getBits(8);
LOG(VERBOSE) << " table_id = " << table_id;
CHECK_EQ(table_id, 0x00u);
unsigned section_syntax_indictor = br->getBits(1);
LOG(VERBOSE) << " section_syntax_indictor = " << section_syntax_indictor;
CHECK_EQ(section_syntax_indictor, 1u);
CHECK_EQ(br->getBits(1), 0u);
LOG(VERBOSE) << " reserved = " << br->getBits(2);
unsigned section_length = br->getBits(12);
LOG(VERBOSE) << " section_length = " << section_length;
CHECK((section_length & 0xc00) == 0);
LOG(VERBOSE) << " transport_stream_id = " << br->getBits(16);
LOG(VERBOSE) << " reserved = " << br->getBits(2);
LOG(VERBOSE) << " version_number = " << br->getBits(5);
LOG(VERBOSE) << " current_next_indicator = " << br->getBits(1);
LOG(VERBOSE) << " section_number = " << br->getBits(8);
LOG(VERBOSE) << " last_section_number = " << br->getBits(8);
size_t numProgramBytes = (section_length - 5 /* header */ - 4 /* crc */);
CHECK_EQ((numProgramBytes % 4), 0u);
for (size_t i = 0; i < numProgramBytes / 4; ++i) {
unsigned program_number = br->getBits(16);
LOG(VERBOSE) << " program_number = " << program_number;
LOG(VERBOSE) << " reserved = " << br->getBits(3);
if (program_number == 0) {
LOG(VERBOSE) << " network_PID = "
<< StringPrintf("0x%04x", br->getBits(13));
} else {
unsigned programMapPID = br->getBits(13);
LOG(VERBOSE) << " program_map_PID = "
<< StringPrintf("0x%04x", programMapPID);
mPrograms.push(new Program(programMapPID));
}
}
LOG(VERBOSE) << " CRC = " << StringPrintf("0x%08x", br->getBits(32));
}
void ATSParser::parsePID(
ABitReader *br, unsigned PID,
unsigned payload_unit_start_indicator) {
if (PID == 0) {
if (payload_unit_start_indicator) {
unsigned skip = br->getBits(8);
br->skipBits(skip * 8);
}
parseProgramAssociationTable(br);
return;
}
bool handled = false;
for (size_t i = 0; i < mPrograms.size(); ++i) {
if (mPrograms.editItemAt(i)->parsePID(
PID, payload_unit_start_indicator, br)) {
handled = true;
break;
}
}
if (!handled) {
LOG(WARNING) << "PID " << StringPrintf("0x%04x", PID)
<< " not handled.";
}
}
void ATSParser::parseAdaptationField(ABitReader *br) {
unsigned adaptation_field_length = br->getBits(8);
if (adaptation_field_length > 0) {
br->skipBits(adaptation_field_length * 8); // XXX
}
}
void ATSParser::parseTS(ABitReader *br) {
LOG(VERBOSE) << "---";
unsigned sync_byte = br->getBits(8);
CHECK_EQ(sync_byte, 0x47u);
LOG(VERBOSE) << "transport_error_indicator = " << br->getBits(1);
unsigned payload_unit_start_indicator = br->getBits(1);
LOG(VERBOSE) << "payload_unit_start_indicator = "
<< payload_unit_start_indicator;
LOG(VERBOSE) << "transport_priority = " << br->getBits(1);
unsigned PID = br->getBits(13);
LOG(VERBOSE) << "PID = " << StringPrintf("0x%04x", PID);
LOG(VERBOSE) << "transport_scrambling_control = " << br->getBits(2);
unsigned adaptation_field_control = br->getBits(2);
LOG(VERBOSE) << "adaptation_field_control = " << adaptation_field_control;
LOG(VERBOSE) << "continuity_counter = " << br->getBits(4);
if (adaptation_field_control == 2 || adaptation_field_control == 3) {
parseAdaptationField(br);
}
if (adaptation_field_control == 1 || adaptation_field_control == 3) {
parsePID(br, PID, payload_unit_start_indicator);
}
}
sp<MediaSource> ATSParser::getSource(SourceType type) {
for (size_t i = 0; i < mPrograms.size(); ++i) {
sp<MediaSource> source = mPrograms.editItemAt(i)->getSource(type);
if (source != NULL) {
return source;
}
}
return NULL;
}
} // namespace android

View File

@@ -0,0 +1,68 @@
/*
* 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 A_TS_PARSER_H_
#define A_TS_PARSER_H_
#include <sys/types.h>
#include <media/stagefright/foundation/ABase.h>
#include <utils/Vector.h>
#include <utils/RefBase.h>
namespace android {
struct ABitReader;
struct MediaSource;
struct ATSParser : public RefBase {
ATSParser();
void feedTSPacket(const void *data, size_t size);
enum SourceType {
AVC_VIDEO,
MPEG2ADTS_AUDIO
};
sp<MediaSource> getSource(SourceType type);
protected:
virtual ~ATSParser();
private:
struct Program;
struct Stream;
Vector<sp<Program> > mPrograms;
void parseProgramAssociationTable(ABitReader *br);
void parseProgramMap(ABitReader *br);
void parsePES(ABitReader *br);
void parsePID(
ABitReader *br, unsigned PID,
unsigned payload_unit_start_indicator);
void parseAdaptationField(ABitReader *br);
void parseTS(ABitReader *br);
DISALLOW_EVIL_CONSTRUCTORS(ATSParser);
};
} // namespace android
#endif // A_TS_PARSER_H_

View File

@@ -0,0 +1,22 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
ABitReader.cpp \
AnotherPacketSource.cpp \
ATSParser.cpp \
MPEG2TSExtractor.cpp \
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
$(TOP)/frameworks/base/media/libstagefright
LOCAL_MODULE:= libstagefright_mpeg2ts
ifeq ($(TARGET_ARCH),arm)
LOCAL_CFLAGS += -Wno-psabi
endif
include $(BUILD_STATIC_LIBRARY)

View File

@@ -0,0 +1,112 @@
/*
* 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.
*/
#include "AnotherPacketSource.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/foundation/AString.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <utils/Vector.h>
namespace android {
AnotherPacketSource::AnotherPacketSource(const sp<MetaData> &meta)
: mFormat(meta),
mEOSResult(OK) {
}
AnotherPacketSource::~AnotherPacketSource() {
}
status_t AnotherPacketSource::start(MetaData *params) {
return OK;
}
status_t AnotherPacketSource::stop() {
return OK;
}
sp<MetaData> AnotherPacketSource::getFormat() {
return mFormat;
}
status_t AnotherPacketSource::read(
MediaBuffer **out, const ReadOptions *) {
*out = NULL;
Mutex::Autolock autoLock(mLock);
while (mEOSResult == OK && mBuffers.empty()) {
mCondition.wait(mLock);
}
if (!mBuffers.empty()) {
const sp<ABuffer> buffer = *mBuffers.begin();
uint64_t timeUs;
CHECK(buffer->meta()->findInt64(
"time", (int64_t *)&timeUs));
MediaBuffer *mediaBuffer = new MediaBuffer(buffer->size());
mediaBuffer->meta_data()->setInt64(kKeyTime, timeUs);
// hexdump(buffer->data(), buffer->size());
memcpy(mediaBuffer->data(), buffer->data(), buffer->size());
*out = mediaBuffer;
mBuffers.erase(mBuffers.begin());
return OK;
}
return mEOSResult;
}
void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
int32_t damaged;
if (buffer->meta()->findInt32("damaged", &damaged) && damaged) {
// LOG(VERBOSE) << "discarding damaged AU";
return;
}
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
}
void AnotherPacketSource::signalEOS(status_t result) {
CHECK(result != OK);
Mutex::Autolock autoLock(mLock);
mEOSResult = result;
mCondition.signal();
}
bool AnotherPacketSource::hasBufferAvailable(status_t *finalResult) {
Mutex::Autolock autoLock(mLock);
if (!mBuffers.empty()) {
return true;
}
*finalResult = mEOSResult;
return false;
}
} // namespace android

View File

@@ -0,0 +1,62 @@
/*
* 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 ANOTHER_PACKET_SOURCE_H_
#define ANOTHER_PACKET_SOURCE_H_
#include <media/stagefright/foundation/ABase.h>
#include <media/stagefright/MediaSource.h>
#include <utils/threads.h>
#include <utils/List.h>
namespace android {
struct ABuffer;
struct AnotherPacketSource : public MediaSource {
AnotherPacketSource(const sp<MetaData> &meta);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
bool hasBufferAvailable(status_t *finalResult);
void queueAccessUnit(const sp<ABuffer> &buffer);
void signalEOS(status_t result);
protected:
virtual ~AnotherPacketSource();
private:
Mutex mLock;
Condition mCondition;
sp<MetaData> mFormat;
List<sp<ABuffer> > mBuffers;
status_t mEOSResult;
DISALLOW_EVIL_CONSTRUCTORS(AnotherPacketSource);
};
} // namespace android
#endif // ANOTHER_PACKET_SOURCE_H_

View File

@@ -0,0 +1,189 @@
/*
* 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 "MPEG2TSExtractor"
#include <utils/Log.h>
#include "include/MPEG2TSExtractor.h"
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
#include "AnotherPacketSource.h"
#include "ATSParser.h"
namespace android {
struct MPEG2TSSource : public MediaSource {
MPEG2TSSource(
const sp<MPEG2TSExtractor> &extractor,
const sp<AnotherPacketSource> &impl);
virtual status_t start(MetaData *params = NULL);
virtual status_t stop();
virtual sp<MetaData> getFormat();
virtual status_t read(
MediaBuffer **buffer, const ReadOptions *options = NULL);
private:
sp<MPEG2TSExtractor> mExtractor;
sp<AnotherPacketSource> mImpl;
DISALLOW_EVIL_CONSTRUCTORS(MPEG2TSSource);
};
MPEG2TSSource::MPEG2TSSource(
const sp<MPEG2TSExtractor> &extractor,
const sp<AnotherPacketSource> &impl)
: mExtractor(extractor),
mImpl(impl) {
}
status_t MPEG2TSSource::start(MetaData *params) {
return mImpl->start(params);
}
status_t MPEG2TSSource::stop() {
return mImpl->stop();
}
sp<MetaData> MPEG2TSSource::getFormat() {
return mImpl->getFormat();
}
status_t MPEG2TSSource::read(
MediaBuffer **out, const ReadOptions *options) {
*out = NULL;
status_t finalResult;
while (!mImpl->hasBufferAvailable(&finalResult)) {
if (finalResult != OK) {
return ERROR_END_OF_STREAM;
}
status_t err = mExtractor->feedMore();
if (err != OK) {
mImpl->signalEOS(err);
}
}
return mImpl->read(out, options);
}
////////////////////////////////////////////////////////////////////////////////
MPEG2TSExtractor::MPEG2TSExtractor(const sp<DataSource> &source)
: mDataSource(source),
mParser(new ATSParser),
mOffset(0) {
init();
}
size_t MPEG2TSExtractor::countTracks() {
return mSourceImpls.size();
}
sp<MediaSource> MPEG2TSExtractor::getTrack(size_t index) {
if (index >= mSourceImpls.size()) {
return NULL;
}
return new MPEG2TSSource(this, mSourceImpls.editItemAt(index));
}
sp<MetaData> MPEG2TSExtractor::getTrackMetaData(
size_t index, uint32_t flags) {
return index < mSourceImpls.size()
? mSourceImpls.editItemAt(index)->getFormat() : NULL;
}
sp<MetaData> MPEG2TSExtractor::getMetaData() {
sp<MetaData> meta = new MetaData;
meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
return meta;
}
void MPEG2TSExtractor::init() {
bool haveAudio = false;
bool haveVideo = false;
while (feedMore() == OK) {
ATSParser::SourceType type;
if (haveAudio && haveVideo) {
break;
}
if (haveVideo) {
type = ATSParser::MPEG2ADTS_AUDIO;
} else {
type = ATSParser::AVC_VIDEO;
}
sp<AnotherPacketSource> impl =
(AnotherPacketSource *)mParser->getSource(type).get();
if (impl != NULL) {
if (type == ATSParser::MPEG2ADTS_AUDIO) {
haveAudio = true;
} else {
haveVideo = true;
}
mSourceImpls.push(impl);
}
}
LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
}
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);
if (n < (ssize_t)kTSPacketSize) {
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
}
mOffset += kTSPacketSize;
mParser->feedTSPacket(packet, kTSPacketSize);
return OK;
}
////////////////////////////////////////////////////////////////////////////////
bool SniffMPEG2TS(
const sp<DataSource> &source, String8 *mimeType, float *confidence) {
char header;
if (source->readAt(0, &header, 1) != 1 || header != 0x47) {
return false;
}
*confidence = 0.05f;
mimeType->setTo(MEDIA_MIMETYPE_CONTAINER_MPEG2TS);
return true;
}
} // namespace android