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