Merge "Work to support switching transport streams mid-stream and signalling discontinuities to the decoder." into gingerbread

This commit is contained in:
Andreas Huber
2010-10-07 11:59:53 -07:00
committed by Android (Google) Code Review
16 changed files with 466 additions and 115 deletions

View File

@@ -39,6 +39,7 @@ enum {
// Not technically an error.
INFO_FORMAT_CHANGED = MEDIA_ERROR_BASE - 12,
INFO_DISCONTINUITY = MEDIA_ERROR_BASE - 13,
};
} // namespace android

View File

@@ -32,7 +32,8 @@ struct CodecProfileLevel;
struct OMXCodec : public MediaSource,
public MediaBufferObserver {
enum CreationFlags {
kPreferSoftwareCodecs = 1,
kPreferSoftwareCodecs = 1,
kIgnoreCodecSpecificData = 2
};
static sp<MediaSource> Create(
const sp<IOMX> &omx,
@@ -248,7 +249,7 @@ private:
void dumpPortStatus(OMX_U32 portIndex);
status_t configureCodec(const sp<MetaData> &meta);
status_t configureCodec(const sp<MetaData> &meta, uint32_t flags);
static uint32_t getComponentQuirks(
const char *componentName, bool isEncoder);

View File

@@ -561,6 +561,39 @@ void AwesomePlayer::onBufferingUpdate() {
postBufferingEvent_l();
}
void AwesomePlayer::partial_reset_l() {
// Only reset the video renderer and shut down the video decoder.
// Then instantiate a new video decoder and resume video playback.
mVideoRenderer.clear();
if (mLastVideoBuffer) {
mLastVideoBuffer->release();
mLastVideoBuffer = NULL;
}
if (mVideoBuffer) {
mVideoBuffer->release();
mVideoBuffer = NULL;
}
{
mVideoSource->stop();
// The following hack is necessary to ensure that the OMX
// component is completely released by the time we may try
// to instantiate it again.
wp<MediaSource> tmp = mVideoSource;
mVideoSource.clear();
while (tmp.promote() != NULL) {
usleep(1000);
}
IPCThreadState::self()->flushCommands();
}
CHECK_EQ(OK, initVideoDecoder(OMXCodec::kIgnoreCodecSpecificData));
}
void AwesomePlayer::onStreamDone() {
// Posted whenever any stream finishes playing.
@@ -570,7 +603,21 @@ void AwesomePlayer::onStreamDone() {
}
mStreamDoneEventPending = false;
if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
if (mStreamDoneStatus == INFO_DISCONTINUITY) {
// This special status is returned because an http live stream's
// video stream switched to a different bandwidth at this point
// and future data may have been encoded using different parameters.
// This requires us to shutdown the video decoder and reinstantiate
// a fresh one.
LOGV("INFO_DISCONTINUITY");
CHECK(mVideoSource != NULL);
partial_reset_l();
postVideoEvent_l();
return;
} else if (mStreamDoneStatus != ERROR_END_OF_STREAM) {
LOGV("MEDIA_ERROR %d", mStreamDoneStatus);
notifyListener_l(
@@ -939,8 +986,7 @@ void AwesomePlayer::setVideoSource(sp<MediaSource> source) {
mVideoTrack = source;
}
status_t AwesomePlayer::initVideoDecoder() {
uint32_t flags = 0;
status_t AwesomePlayer::initVideoDecoder(uint32_t flags) {
mVideoSource = OMXCodec::Create(
mClient.interface(), mVideoTrack->getFormat(),
false, // createEncoder

View File

@@ -504,7 +504,7 @@ sp<MediaSource> OMXCodec::Create(
observer->setCodec(codec);
err = codec->configureCodec(meta);
err = codec->configureCodec(meta, flags);
if (err == OK) {
return codec;
@@ -517,93 +517,95 @@ sp<MediaSource> OMXCodec::Create(
return NULL;
}
status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
uint32_t type;
const void *data;
size_t size;
if (meta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds((const char *)data, size);
CHECK_EQ(esds.InitCheck(), OK);
status_t OMXCodec::configureCodec(const sp<MetaData> &meta, uint32_t flags) {
if (!(flags & kIgnoreCodecSpecificData)) {
uint32_t type;
const void *data;
size_t size;
if (meta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds((const char *)data, size);
CHECK_EQ(esds.InitCheck(), OK);
const void *codec_specific_data;
size_t codec_specific_data_size;
esds.getCodecSpecificInfo(
&codec_specific_data, &codec_specific_data_size);
const void *codec_specific_data;
size_t codec_specific_data_size;
esds.getCodecSpecificInfo(
&codec_specific_data, &codec_specific_data_size);
addCodecSpecificData(
codec_specific_data, codec_specific_data_size);
} else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
addCodecSpecificData(
codec_specific_data, codec_specific_data_size);
} else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
const uint8_t *ptr = (const uint8_t *)data;
const uint8_t *ptr = (const uint8_t *)data;
CHECK(size >= 7);
CHECK_EQ(ptr[0], 1); // configurationVersion == 1
uint8_t profile = ptr[1];
uint8_t level = ptr[3];
CHECK(size >= 7);
CHECK_EQ(ptr[0], 1); // configurationVersion == 1
uint8_t profile = ptr[1];
uint8_t level = ptr[3];
// There is decodable content out there that fails the following
// assertion, let's be lenient for now...
// CHECK((ptr[4] >> 2) == 0x3f); // reserved
// There is decodable content out there that fails the following
// assertion, let's be lenient for now...
// CHECK((ptr[4] >> 2) == 0x3f); // reserved
size_t lengthSize = 1 + (ptr[4] & 3);
size_t lengthSize = 1 + (ptr[4] & 3);
// commented out check below as H264_QVGA_500_NO_AUDIO.3gp
// violates it...
// CHECK((ptr[5] >> 5) == 7); // reserved
// commented out check below as H264_QVGA_500_NO_AUDIO.3gp
// violates it...
// CHECK((ptr[5] >> 5) == 7); // reserved
size_t numSeqParameterSets = ptr[5] & 31;
size_t numSeqParameterSets = ptr[5] & 31;
ptr += 6;
size -= 6;
ptr += 6;
size -= 6;
for (size_t i = 0; i < numSeqParameterSets; ++i) {
CHECK(size >= 2);
size_t length = U16_AT(ptr);
for (size_t i = 0; i < numSeqParameterSets; ++i) {
CHECK(size >= 2);
size_t length = U16_AT(ptr);
ptr += 2;
size -= 2;
ptr += 2;
size -= 2;
CHECK(size >= length);
CHECK(size >= length);
addCodecSpecificData(ptr, length);
addCodecSpecificData(ptr, length);
ptr += length;
size -= length;
}
ptr += length;
size -= length;
}
CHECK(size >= 1);
size_t numPictureParameterSets = *ptr;
++ptr;
--size;
CHECK(size >= 1);
size_t numPictureParameterSets = *ptr;
++ptr;
--size;
for (size_t i = 0; i < numPictureParameterSets; ++i) {
CHECK(size >= 2);
size_t length = U16_AT(ptr);
for (size_t i = 0; i < numPictureParameterSets; ++i) {
CHECK(size >= 2);
size_t length = U16_AT(ptr);
ptr += 2;
size -= 2;
ptr += 2;
size -= 2;
CHECK(size >= length);
CHECK(size >= length);
addCodecSpecificData(ptr, length);
addCodecSpecificData(ptr, length);
ptr += length;
size -= length;
}
ptr += length;
size -= length;
}
CODEC_LOGV(
"AVC profile = %d (%s), level = %d",
(int)profile, AVCProfileToString(profile), level);
CODEC_LOGV(
"AVC profile = %d (%s), level = %d",
(int)profile, AVCProfileToString(profile), level);
if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
&& (profile != kAVCProfileBaseline || level > 30)) {
// This stream exceeds the decoder's capabilities. The decoder
// does not handle this gracefully and would clobber the heap
// and wreak havoc instead...
if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
&& (profile != kAVCProfileBaseline || level > 30)) {
// This stream exceeds the decoder's capabilities. The decoder
// does not handle this gracefully and would clobber the heap
// and wreak havoc instead...
LOGE("Profile and/or level exceed the decoder's capabilities.");
return ERROR_UNSUPPORTED;
LOGE("Profile and/or level exceed the decoder's capabilities.");
return ERROR_UNSUPPORTED;
}
}
}

View File

@@ -14,6 +14,7 @@
* limitations under the License.
*/
//#define LOG_NDEBUG 0
#define LOG_TAG "LiveSource"
#include <utils/Log.h>
@@ -22,18 +23,21 @@
#include "include/NuHTTPDataSource.h"
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaDebug.h>
namespace android {
LiveSource::LiveSource(const char *url)
: mURL(url),
: mMasterURL(url),
mInitCheck(NO_INIT),
mPlaylistIndex(0),
mLastFetchTimeUs(-1),
mSource(new NuHTTPDataSource),
mSourceSize(0),
mOffsetBias(0) {
mOffsetBias(0),
mSignalDiscontinuity(false),
mPrevBandwidthIndex(-1) {
if (switchToNext()) {
mInitCheck = OK;
}
@@ -46,21 +50,129 @@ status_t LiveSource::initCheck() const {
return mInitCheck;
}
bool LiveSource::loadPlaylist() {
// static
int LiveSource::SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b) {
if (a->mBandwidth < b->mBandwidth) {
return -1;
} else if (a->mBandwidth == b->mBandwidth) {
return 0;
}
return 1;
}
static double uniformRand() {
return (double)rand() / RAND_MAX;
}
bool LiveSource::loadPlaylist(bool fetchMaster) {
mSignalDiscontinuity = false;
mPlaylist.clear();
mPlaylistIndex = 0;
sp<ABuffer> buffer;
status_t err = fetchM3U(mURL.c_str(), &buffer);
if (fetchMaster) {
mPrevBandwidthIndex = -1;
if (err != OK) {
return false;
sp<ABuffer> buffer;
status_t err = fetchM3U(mMasterURL.c_str(), &buffer);
if (err != OK) {
return false;
}
mPlaylist = new M3UParser(
mMasterURL.c_str(), buffer->data(), buffer->size());
if (mPlaylist->initCheck() != OK) {
return false;
}
if (mPlaylist->isVariantPlaylist()) {
for (size_t i = 0; i < mPlaylist->size(); ++i) {
BandwidthItem item;
sp<AMessage> meta;
mPlaylist->itemAt(i, &item.mURI, &meta);
unsigned long bandwidth;
CHECK(meta->findInt32("bandwidth", (int32_t *)&item.mBandwidth));
mBandwidthItems.push(item);
}
mPlaylist.clear();
// fall through
if (mBandwidthItems.size() == 0) {
return false;
}
mBandwidthItems.sort(SortByBandwidth);
for (size_t i = 0; i < mBandwidthItems.size(); ++i) {
const BandwidthItem &item = mBandwidthItems.itemAt(i);
LOGV("item #%d: %s", i, item.mURI.c_str());
}
}
}
mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size());
if (mBandwidthItems.size() > 0) {
#if 0
// Change bandwidth at random()
size_t index = uniformRand() * mBandwidthItems.size();
#elif 0
// There's a 50% chance to stay on the current bandwidth and
// a 50% chance to switch to the next higher bandwidth (wrapping around
// to lowest)
size_t index;
if (uniformRand() < 0.5) {
index = mPrevBandwidthIndex < 0 ? 0 : (size_t)mPrevBandwidthIndex;
} else {
if (mPrevBandwidthIndex < 0) {
index = 0;
} else {
index = mPrevBandwidthIndex + 1;
if (index == mBandwidthItems.size()) {
index = 0;
}
}
}
#else
// Stay on the lowest bandwidth available.
size_t index = 0; // Lowest bandwidth stream
#endif
if (mPlaylist->initCheck() != OK) {
return false;
mURL = mBandwidthItems.editItemAt(index).mURI;
if (mPrevBandwidthIndex >= 0 && (size_t)mPrevBandwidthIndex != index) {
// If we switched streams because of bandwidth changes,
// we'll signal this discontinuity by inserting a
// special transport stream packet into the stream.
mSignalDiscontinuity = true;
}
mPrevBandwidthIndex = index;
} else {
mURL = mMasterURL;
}
if (mPlaylist == NULL) {
sp<ABuffer> buffer;
status_t err = fetchM3U(mURL.c_str(), &buffer);
if (err != OK) {
return false;
}
mPlaylist = new M3UParser(mURL.c_str(), buffer->data(), buffer->size());
if (mPlaylist->initCheck() != OK) {
return false;
}
if (mPlaylist->isVariantPlaylist()) {
return false;
}
}
if (!mPlaylist->meta()->findInt32(
@@ -79,6 +191,8 @@ static int64_t getNowUs() {
}
bool LiveSource::switchToNext() {
mSignalDiscontinuity = false;
mOffsetBias += mSourceSize;
mSourceSize = 0;
@@ -87,7 +201,7 @@ bool LiveSource::switchToNext() {
int32_t nextSequenceNumber =
mPlaylistIndex + mFirstItemSequenceNumber;
if (!loadPlaylist()) {
if (!loadPlaylist(mLastFetchTimeUs < 0)) {
LOGE("failed to reload playlist");
return false;
}
@@ -111,35 +225,62 @@ bool LiveSource::switchToNext() {
}
AString uri;
CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri));
LOGI("switching to %s", uri.c_str());
sp<AMessage> itemMeta;
CHECK(mPlaylist->itemAt(mPlaylistIndex, &uri, &itemMeta));
LOGV("switching to %s", uri.c_str());
if (mSource->connect(uri.c_str()) != OK
|| mSource->getSize(&mSourceSize) != OK) {
return false;
}
int32_t val;
if (itemMeta->findInt32("discontinuity", &val) && val != 0) {
mSignalDiscontinuity = true;
}
mPlaylistIndex++;
return true;
}
static const ssize_t kHeaderSize = 188;
ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) {
CHECK(offset >= mOffsetBias);
offset -= mOffsetBias;
if (offset >= mSourceSize) {
CHECK_EQ(offset, mSourceSize);
off_t delta = mSignalDiscontinuity ? kHeaderSize : 0;
offset -= mSourceSize;
if (offset >= mSourceSize + delta) {
CHECK_EQ(offset, mSourceSize + delta);
offset -= mSourceSize + delta;
if (!switchToNext()) {
return ERROR_END_OF_STREAM;
}
if (mSignalDiscontinuity) {
LOGV("switchToNext changed streams");
} else {
LOGV("switchToNext stayed within the same stream");
}
mOffsetBias += delta;
delta = mSignalDiscontinuity ? kHeaderSize : 0;
}
if (offset < delta) {
size_t avail = delta - offset;
memset(data, 0, avail);
return avail;
}
size_t numRead = 0;
while (numRead < size) {
ssize_t n = mSource->readAt(
offset + numRead, (uint8_t *)data + numRead, size - numRead);
offset + numRead - delta,
(uint8_t *)data + numRead, size - numRead);
if (n <= 0) {
break;
@@ -154,14 +295,24 @@ ssize_t LiveSource::readAt(off_t offset, void *data, size_t size) {
status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
*out = NULL;
status_t err = mSource->connect(url);
sp<DataSource> source;
if (err != OK) {
return err;
if (!strncasecmp(url, "file://", 7)) {
source = new FileSource(url + 7);
} else {
CHECK(!strncasecmp(url, "http://", 7));
status_t err = mSource->connect(url);
if (err != OK) {
return err;
}
source = mSource;
}
off_t size;
err = mSource->getSize(&size);
status_t err = source->getSize(&size);
if (err != OK) {
return err;
@@ -170,7 +321,7 @@ status_t LiveSource::fetchM3U(const char *url, sp<ABuffer> *out) {
sp<ABuffer> buffer = new ABuffer(size);
size_t offset = 0;
while (offset < (size_t)size) {
ssize_t n = mSource->readAt(
ssize_t n = source->readAt(
offset, buffer->data() + offset, size - offset);
if (n <= 0) {

View File

@@ -74,7 +74,8 @@ bool M3UParser::itemAt(size_t index, AString *uri, sp<AMessage> *meta) {
static bool MakeURL(const char *baseURL, const char *url, AString *out) {
out->clear();
if (strncasecmp("http://", baseURL, 7)) {
if (strncasecmp("http://", baseURL, 7)
&& strncasecmp("file://", baseURL, 7)) {
// Base URL must be absolute
return false;
}
@@ -128,7 +129,12 @@ status_t M3UParser::parse(const void *_data, size_t size) {
line.setTo(&data[offset], offsetLF - offset);
}
LOGI("#%s#", line.c_str());
// LOGI("#%s#", line.c_str());
if (line.empty()) {
offset = offsetLF + 1;
continue;
}
if (lineNo == 0 && line == "#EXTM3U") {
mIsExtM3U = true;
@@ -152,11 +158,20 @@ status_t M3UParser::parse(const void *_data, size_t size) {
return ERROR_MALFORMED;
}
err = parseMetaData(line, &itemMeta, "duration");
} else if (line.startsWith("#EXT-X-DISCONTINUITY")) {
if (mIsVariantPlaylist) {
return ERROR_MALFORMED;
}
if (itemMeta == NULL) {
itemMeta = new AMessage;
}
itemMeta->setInt32("discontinuity", true);
} else if (line.startsWith("#EXT-X-STREAM-INF")) {
if (mMeta != NULL) {
return ERROR_MALFORMED;
}
mIsVariantPlaylist = true;
err = parseStreamInf(line, &itemMeta);
}
if (err != OK) {
@@ -214,6 +229,61 @@ status_t M3UParser::parseMetaData(
return OK;
}
// static
status_t M3UParser::parseStreamInf(
const AString &line, sp<AMessage> *meta) {
ssize_t colonPos = line.find(":");
if (colonPos < 0) {
return ERROR_MALFORMED;
}
size_t offset = colonPos + 1;
while (offset < line.size()) {
ssize_t end = line.find(",", offset);
if (end < 0) {
end = line.size();
}
AString attr(line, offset, end - offset);
attr.trim();
offset = end + 1;
ssize_t equalPos = attr.find("=");
if (equalPos < 0) {
continue;
}
AString key(attr, 0, equalPos);
key.trim();
AString val(attr, equalPos + 1, attr.size() - equalPos - 1);
val.trim();
LOGV("key=%s value=%s", key.c_str(), val.c_str());
if (!strcasecmp("bandwidth", key.c_str())) {
const char *s = val.c_str();
char *end;
unsigned long x = strtoul(s, &end, 10);
if (end == s || *end != '\0') {
// malformed
continue;
}
if (meta->get() == NULL) {
*meta = new AMessage;
}
(*meta)->setInt32("bandwidth", x);
}
}
return OK;
}
// static
status_t M3UParser::ParseInt32(const char *s, int32_t *x) {
char *end;

View File

@@ -220,6 +220,7 @@ private:
status_t setDataSource_l(const sp<DataSource> &dataSource);
status_t setDataSource_l(const sp<MediaExtractor> &extractor);
void reset_l();
void partial_reset_l();
status_t seekTo_l(int64_t timeUs);
status_t pause_l(bool at_eos = false);
void initRenderer_l();
@@ -231,7 +232,7 @@ private:
status_t initAudioDecoder();
void setVideoSource(sp<MediaSource> source);
status_t initVideoDecoder();
status_t initVideoDecoder(uint32_t flags = 0);
void onStreamDone();

View File

@@ -44,6 +44,13 @@ protected:
virtual ~LiveSource();
private:
struct BandwidthItem {
AString mURI;
unsigned long mBandwidth;
};
Vector<BandwidthItem> mBandwidthItems;
AString mMasterURL;
AString mURL;
status_t mInitCheck;
@@ -56,10 +63,15 @@ private:
off_t mSourceSize;
off_t mOffsetBias;
bool mSignalDiscontinuity;
ssize_t mPrevBandwidthIndex;
status_t fetchM3U(const char *url, sp<ABuffer> *buffer);
static int SortByBandwidth(const BandwidthItem *a, const BandwidthItem *b);
bool switchToNext();
bool loadPlaylist();
bool loadPlaylist(bool fetchMaster);
DISALLOW_EVIL_CONSTRUCTORS(LiveSource);
};

View File

@@ -61,6 +61,9 @@ private:
static status_t parseMetaData(
const AString &line, sp<AMessage> *meta, const char *key);
static status_t parseStreamInf(
const AString &line, sp<AMessage> *meta);
static status_t ParseInt32(const char *s, int32_t *x);
DISALLOW_EVIL_CONSTRUCTORS(M3UParser);

View File

@@ -49,6 +49,8 @@ struct ATSParser::Program : public RefBase {
unsigned pid, unsigned payload_unit_start_indicator,
ABitReader *br);
void signalDiscontinuity();
sp<MediaSource> getSource(SourceType type);
private:
@@ -67,6 +69,8 @@ struct ATSParser::Stream : public RefBase {
unsigned payload_unit_start_indicator,
ABitReader *br);
void signalDiscontinuity();
sp<MediaSource> getSource(SourceType type);
protected:
@@ -124,6 +128,12 @@ bool ATSParser::Program::parsePID(
return true;
}
void ATSParser::Program::signalDiscontinuity() {
for (size_t i = 0; i < mStreams.size(); ++i) {
mStreams.editValueAt(i)->signalDiscontinuity();
}
}
void ATSParser::Program::parseProgramMap(ABitReader *br) {
unsigned table_id = br->getBits(8);
LOGV(" table_id = %u", table_id);
@@ -271,6 +281,19 @@ void ATSParser::Stream::parse(
mBuffer->setRange(0, mBuffer->size() + payloadSizeBits / 8);
}
void ATSParser::Stream::signalDiscontinuity() {
LOGV("Stream discontinuity");
mPayloadStarted = false;
mBuffer->setRange(0, 0);
mQueue.clear();
if (mStreamType == 0x1b && mSource != NULL) {
// Don't signal discontinuities on audio streams.
mSource->queueDiscontinuity();
}
}
void ATSParser::Stream::parsePES(ABitReader *br) {
unsigned packet_startcode_prefix = br->getBits(24);
@@ -459,7 +482,10 @@ void ATSParser::Stream::onPayloadData(
mSource = new AnotherPacketSource(meta);
mSource->queueAccessUnit(accessUnit);
}
} else {
} else if (mQueue.getFormat() != NULL) {
// After a discontinuity we invalidate the queue's format
// and won't enqueue any access units to the source until
// the queue has reestablished the new format.
mSource->queueAccessUnit(accessUnit);
}
}
@@ -489,6 +515,12 @@ void ATSParser::feedTSPacket(const void *data, size_t size) {
parseTS(&br);
}
void ATSParser::signalDiscontinuity() {
for (size_t i = 0; i < mPrograms.size(); ++i) {
mPrograms.editItemAt(i)->signalDiscontinuity();
}
}
void ATSParser::parseProgramAssociationTable(ABitReader *br) {
unsigned table_id = br->getBits(8);
LOGV(" table_id = %u", table_id);

View File

@@ -33,6 +33,7 @@ struct ATSParser : public RefBase {
ATSParser();
void feedTSPacket(const void *data, size_t size);
void signalDiscontinuity();
enum SourceType {
AVC_VIDEO,

View File

@@ -59,21 +59,26 @@ status_t AnotherPacketSource::read(
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;
int32_t discontinuity;
if (buffer->meta()->findInt32("discontinuity", &discontinuity)
&& discontinuity) {
return INFO_DISCONTINUITY;
} else {
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;
return OK;
}
}
return mEOSResult;
@@ -91,6 +96,15 @@ void AnotherPacketSource::queueAccessUnit(const sp<ABuffer> &buffer) {
mCondition.signal();
}
void AnotherPacketSource::queueDiscontinuity() {
sp<ABuffer> buffer = new ABuffer(0);
buffer->meta()->setInt32("discontinuity", true);
Mutex::Autolock autoLock(mLock);
mBuffers.push_back(buffer);
mCondition.signal();
}
void AnotherPacketSource::signalEOS(status_t result) {
CHECK(result != OK);

View File

@@ -40,6 +40,7 @@ struct AnotherPacketSource : public MediaSource {
bool hasBufferAvailable(status_t *finalResult);
void queueAccessUnit(const sp<ABuffer> &buffer);
void queueDiscontinuity();
void signalEOS(status_t result);
protected:

View File

@@ -115,6 +115,11 @@ static status_t getNextNALUnit(
return OK;
}
void ElementaryStreamQueue::clear() {
mBuffer->setRange(0, 0);
mFormat.clear();
}
status_t ElementaryStreamQueue::appendData(
const void *data, size_t size, int64_t timeUs) {
if (mBuffer == NULL || mBuffer->size() == 0) {
@@ -147,7 +152,7 @@ status_t ElementaryStreamQueue::appendData(
if (mBuffer == NULL || neededSize > mBuffer->capacity()) {
neededSize = (neededSize + 65535) & ~65535;
LOGI("resizing buffer to size %d", neededSize);
LOGV("resizing buffer to size %d", neededSize);
sp<ABuffer> buffer = new ABuffer(neededSize);
if (mBuffer != NULL) {
@@ -498,6 +503,8 @@ sp<MetaData> ElementaryStreamQueue::MakeAVCCodecSpecificData(
meta->setInt32(kKeyWidth, width);
meta->setInt32(kKeyHeight, height);
LOGI("found AVC codec config (%d x %d)", width, height);
return meta;
}

View File

@@ -35,6 +35,7 @@ struct ElementaryStreamQueue {
ElementaryStreamQueue(Mode mode);
status_t appendData(const void *data, size_t size, int64_t timeUs);
void clear();
sp<ABuffer> dequeueAccessUnit();

View File

@@ -165,18 +165,26 @@ void MPEG2TSExtractor::init() {
LOGI("haveAudio=%d, haveVideo=%d", haveAudio, haveVideo);
}
static bool isDiscontinuity(const uint8_t *data, ssize_t size) {
return size == 188 && data[0] == 0x00;
}
status_t MPEG2TSExtractor::feedMore() {
Mutex::Autolock autoLock(mLock);
uint8_t packet[kTSPacketSize];
ssize_t n = mDataSource->readAt(mOffset, packet, kTSPacketSize);
if (n < (ssize_t)kTSPacketSize) {
if (isDiscontinuity(packet, n)) {
LOGI("XXX discontinuity detected");
mParser->signalDiscontinuity();
} else if (n < (ssize_t)kTSPacketSize) {
return (n < 0) ? (status_t)n : ERROR_END_OF_STREAM;
} else {
mParser->feedTSPacket(packet, kTSPacketSize);
}
mOffset += kTSPacketSize;
mParser->feedTSPacket(packet, kTSPacketSize);
mOffset += n;
return OK;
}