Initial checkin of mpeg2 transport stream parser for stagefright.
Change-Id: I328ce77404daf7127933b48c9d58ed504fb8fc6f
This commit is contained in:
@@ -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
|
||||
|
||||
|
||||
@@ -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");
|
||||
|
||||
@@ -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)
|
||||
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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;
|
||||
|
||||
@@ -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]);
|
||||
|
||||
54
media/libstagefright/include/MPEG2TSExtractor.h
Normal file
54
media/libstagefright/include/MPEG2TSExtractor.h
Normal 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_
|
||||
98
media/libstagefright/mpeg2ts/ABitReader.cpp
Normal file
98
media/libstagefright/mpeg2ts/ABitReader.cpp
Normal 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
|
||||
53
media/libstagefright/mpeg2ts/ABitReader.h
Normal file
53
media/libstagefright/mpeg2ts/ABitReader.h
Normal 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_
|
||||
941
media/libstagefright/mpeg2ts/ATSParser.cpp
Normal file
941
media/libstagefright/mpeg2ts/ATSParser.cpp
Normal 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
|
||||
68
media/libstagefright/mpeg2ts/ATSParser.h
Normal file
68
media/libstagefright/mpeg2ts/ATSParser.h
Normal 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_
|
||||
22
media/libstagefright/mpeg2ts/Android.mk
Normal file
22
media/libstagefright/mpeg2ts/Android.mk
Normal 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)
|
||||
112
media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
Normal file
112
media/libstagefright/mpeg2ts/AnotherPacketSource.cpp
Normal 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
|
||||
62
media/libstagefright/mpeg2ts/AnotherPacketSource.h
Normal file
62
media/libstagefright/mpeg2ts/AnotherPacketSource.h
Normal 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_
|
||||
189
media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
Normal file
189
media/libstagefright/mpeg2ts/MPEG2TSExtractor.cpp
Normal 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
|
||||
Reference in New Issue
Block a user