From ae614d22fd87feb1857e8041978ab2c38cfaa16e Mon Sep 17 00:00:00 2001 From: Lubin Yin Date: Wed, 29 Jul 2015 12:31:09 +0800 Subject: libstagefright: MPEG4Writer: Add support for HEVC muxing Changes done to enable HEVC muxing - writing HVCC atom - configure HEVC encoder Fix HEVC flag initialization Check for HEVC for single track usecase Change-Id: I1757d0c442e7cc3ef251431f220395131a1eb4ec --- media/libavextensions/stagefright/AVExtensions.h | 38 +++++++++++++++++ media/libavextensions/stagefright/AVUtils.cpp | 34 +++++++++++++++ media/libmedia/MediaProfiles.cpp | 3 +- .../libmediaplayerservice/StagefrightRecorder.cpp | 8 ++++ media/libmediaplayerservice/StagefrightRecorder.h | 5 ++- media/libstagefright/MPEG4Writer.cpp | 48 ++++++++++++++++++---- media/libstagefright/Utils.cpp | 2 + 7 files changed, 128 insertions(+), 10 deletions(-) (limited to 'media') diff --git a/media/libavextensions/stagefright/AVExtensions.h b/media/libavextensions/stagefright/AVExtensions.h index ab0f180..3e51463 100644 --- a/media/libavextensions/stagefright/AVExtensions.h +++ b/media/libavextensions/stagefright/AVExtensions.h @@ -38,6 +38,8 @@ namespace android { class AudioParameter; class MetaData; class MediaExtractor; +class MPEG4Writer; +struct ABuffer; struct ACodec; struct ALooper; struct IMediaHTTPConnection; @@ -106,6 +108,42 @@ struct AVUtils { List &/*decodeTimeQueue*/) {} virtual bool useQCHWEncoder(const sp &, AString &) { return false; } + + struct HEVCMuxer { + + virtual bool reassembleHEVCCSD(const AString &mime, sp csd0, sp &meta); + + virtual void writeHEVCFtypBox(MPEG4Writer *writer); + + virtual status_t makeHEVCCodecSpecificData(const uint8_t *data, + size_t size, void** codecSpecificData, + size_t *codecSpecificDataSize); + + virtual const char *getFourCCForMime(const char *mime); + + virtual void writeHvccBox(MPEG4Writer *writer, + void* codecSpecificData, size_t codecSpecificDataSize, + bool useNalLengthFour); + + virtual bool isVideoHEVC(const char* mime); + + virtual void getHEVCCodecSpecificDataFromInputFormatIfPossible( + sp meta, void **codecSpecificData, + size_t *codecSpecificDataSize, bool *gotAllCodecSpecificData); + + protected: + HEVCMuxer() {}; + virtual ~HEVCMuxer() {}; + friend struct AVUtils; + }; + + virtual inline HEVCMuxer& HEVCMuxerUtils() { + return mHEVCMuxer; + } + +private: + HEVCMuxer mHEVCMuxer; + // ----- NO TRESSPASSING BEYOND THIS LINE ------ DECLARE_LOADABLE_SINGLETON(AVUtils); }; diff --git a/media/libavextensions/stagefright/AVUtils.cpp b/media/libavextensions/stagefright/AVUtils.cpp index a683f48..8e7c39d 100644 --- a/media/libavextensions/stagefright/AVUtils.cpp +++ b/media/libavextensions/stagefright/AVUtils.cpp @@ -109,6 +109,40 @@ bool AVUtils::isEnhancedExtension(const char *) { return false; } +bool AVUtils::HEVCMuxer::reassembleHEVCCSD(const AString &/*mime*/, sp /*csd0*/, sp &/*meta*/) { + return false; +} + +void AVUtils::HEVCMuxer::writeHEVCFtypBox(MPEG4Writer * /*writer*/) { + return; +} + +status_t AVUtils::HEVCMuxer::makeHEVCCodecSpecificData(const uint8_t * /*data*/, + size_t /*size*/, void ** /*codecSpecificData*/, + size_t * /*codecSpecificDataSize*/) { + return UNKNOWN_ERROR; +} + +const char *AVUtils::HEVCMuxer::getFourCCForMime(const char * /*mime*/) { + return NULL; +} + +void AVUtils::HEVCMuxer::writeHvccBox(MPEG4Writer * /*writer*/, + void * /*codecSpecificData*/, size_t /*codecSpecificDataSize*/, + bool /*useNalLengthFour*/) { + return; +} + +bool AVUtils::HEVCMuxer::isVideoHEVC(const char * /*mime*/) { + return false; +} + +void AVUtils::HEVCMuxer::getHEVCCodecSpecificDataFromInputFormatIfPossible( + sp /*meta*/, void ** /*codecSpecificData*/, + size_t * /*codecSpecificDataSize*/, bool * /*gotAllCodecSpecificData*/) { + return; +} + // ----- NO TRESSPASSING BEYOND THIS LINE ------ AVUtils::AVUtils() {} diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp index f993950..8af0ce8 100644 --- a/media/libmedia/MediaProfiles.cpp +++ b/media/libmedia/MediaProfiles.cpp @@ -37,7 +37,8 @@ MediaProfiles *MediaProfiles::sInstance = NULL; const MediaProfiles::NameToTagMap MediaProfiles::sVideoEncoderNameMap[] = { {"h263", VIDEO_ENCODER_H263}, {"h264", VIDEO_ENCODER_H264}, - {"m4v", VIDEO_ENCODER_MPEG_4_SP} + {"m4v", VIDEO_ENCODER_MPEG_4_SP}, + {"h265", VIDEO_ENCODER_H265} }; const MediaProfiles::NameToTagMap MediaProfiles::sAudioEncoderNameMap[] = { diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp index f261d44..3b4d0eb 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.cpp +++ b/media/libmediaplayerservice/StagefrightRecorder.cpp @@ -1501,6 +1501,11 @@ status_t StagefrightRecorder::setupCameraSource( return OK; } +bool StagefrightRecorder::setCustomVideoEncoderMime(const video_encoder /*videoEncoder*/, + sp /*format*/) { + return false; +} + status_t StagefrightRecorder::setupVideoEncoder( sp cameraSource, sp *source) { @@ -1526,6 +1531,9 @@ status_t StagefrightRecorder::setupVideoEncoder( break; default: + if (setCustomVideoEncoderMime(mVideoEncoder, format)) { + break; + } CHECK(!"Should not be here, unsupported video encoding."); break; } diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h index e7b9e55..bd09d3a 100644 --- a/media/libmediaplayerservice/StagefrightRecorder.h +++ b/media/libmediaplayerservice/StagefrightRecorder.h @@ -141,7 +141,7 @@ protected: status_t setupRTPRecording(); status_t setupMPEG2TSRecording(); sp createAudioSource(); - status_t checkVideoEncoderCapabilities(); + virtual status_t checkVideoEncoderCapabilities(); status_t checkAudioEncoderCapabilities(); // Generic MediaSource set-up. Returns the appropriate // source (CameraSource or SurfaceMediaSource) @@ -152,6 +152,7 @@ protected: status_t setupVideoEncoder(sp cameraSource, sp *source); virtual void setupCustomVideoEncoderParams(sp /*cameraSource*/, sp &/*format*/) {} + virtual bool setCustomVideoEncoderMime(const video_encoder videoEncoder, sp format); // Encoding parameter handling utilities status_t setParameter(const String8 &key, const String8 &value); @@ -184,7 +185,7 @@ protected: void clipAudioSampleRate(); void clipNumberOfAudioChannels(); void setDefaultProfileIfNecessary(); - void setDefaultVideoEncoderIfNecessary(); + virtual void setDefaultVideoEncoderIfNecessary(); StagefrightRecorder(const StagefrightRecorder &); diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp index 8af2615..86ca2a1 100644 --- a/media/libstagefright/MPEG4Writer.cpp +++ b/media/libstagefright/MPEG4Writer.cpp @@ -38,6 +38,7 @@ #include #include #include +#include #include #include "include/ESDS.h" @@ -91,6 +92,7 @@ public: bool isAvc() const { return mIsAvc; } bool isAudio() const { return mIsAudio; } bool isMPEG4() const { return mIsMPEG4; } + bool isHEVC() const { return mIsHEVC; } void addChunkOffset(off64_t offset); int32_t getTrackId() const { return mTrackId; } status_t dump(int fd, const Vector& args) const; @@ -236,6 +238,7 @@ private: bool mIsAvc; bool mIsAudio; bool mIsMPEG4; + bool mIsHEVC; int32_t mTrackId; int64_t mTrackDurationUs; int64_t mMaxChunkDurationUs; @@ -386,6 +389,7 @@ MPEG4Writer::MPEG4Writer(int fd) mAreGeoTagsAvailable(false), mStartTimeOffsetMs(-1), mMetaKeys(new AMessage()), + mIsVideoHEVC(false), mIsAudioAMR(false) { addDeviceMeta(); @@ -464,6 +468,8 @@ const char *MPEG4Writer::Track::getFourCCForMime(const char *mime) { return "s263"; } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { return "avc1"; + } else { + return AVUtils::get()->HEVCMuxerUtils().getFourCCForMime(mime); } } else { ALOGE("Track (%s) other than video or audio is not supported", mime); @@ -489,6 +495,7 @@ status_t MPEG4Writer::addSource(const sp &source) { const char *mime; source->getFormat()->findCString(kKeyMIMEType, &mime); bool isAudio = !strncasecmp(mime, "audio/", 6); + bool isVideo = !strncasecmp(mime, "video/", 6); if (Track::getFourCCForMime(mime) == NULL) { ALOGE("Unsupported mime '%s'", mime); return ERROR_UNSUPPORTED; @@ -497,6 +504,10 @@ status_t MPEG4Writer::addSource(const sp &source) { !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, mime)); + if (isVideo) { + mIsVideoHEVC = AVUtils::get()->HEVCMuxerUtils().isVideoHEVC(mime); + } + // At this point, we know the track to be added is either // video or audio. Thus, we only need to check whether it // is an audio track or not (if it is not, then it must be @@ -1051,7 +1062,9 @@ void MPEG4Writer::writeFtypBox(MetaData *param) { beginBox("ftyp"); int32_t fileType; - if (mIsAudioAMR || (param && param->findInt32(kKeyFileType, &fileType) && + if (mIsVideoHEVC) { + AVUtils::get()->HEVCMuxerUtils().writeHEVCFtypBox(this); + } else if (mIsAudioAMR || (param && param->findInt32(kKeyFileType, &fileType) && fileType != OUTPUT_FORMAT_MPEG_4)) { writeFourcc("3gp4"); writeInt32(0); @@ -1468,6 +1481,12 @@ MPEG4Writer::Track::Track( mIsAudio = !strncasecmp(mime, "audio/", 6); mIsMPEG4 = !strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_MPEG4) || !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC); + mIsHEVC = AVUtils::get()->HEVCMuxerUtils().isVideoHEVC(mime); + + if (mIsHEVC) { + AVUtils::get()->HEVCMuxerUtils().getHEVCCodecSpecificDataFromInputFormatIfPossible( + mMeta, &mCodecSpecificData, &mCodecSpecificDataSize, &mGotAllCodecSpecificData); + } setTimeScale(); } @@ -1663,7 +1682,7 @@ void MPEG4Writer::writeChunkToFile(Chunk* chunk) { while (!chunk->mSamples.empty()) { List::iterator it = chunk->mSamples.begin(); - off64_t offset = chunk->mTrack->isAvc() + off64_t offset = (chunk->mTrack->isAvc() || chunk->mTrack->isHEVC()) ? addLengthPrefixedSample_l(*it) : addSample_l(*it); @@ -2244,6 +2263,13 @@ status_t MPEG4Writer::Track::threadEntry() { (const uint8_t *)buffer->data() + buffer->range_offset(), buffer->range_length()); + } else if (mIsHEVC) { + status_t err = AVUtils::get()->HEVCMuxerUtils().makeHEVCCodecSpecificData( + (const uint8_t *)buffer->data() + buffer->range_offset(), + buffer->range_length(), &mCodecSpecificData, &mCodecSpecificDataSize); + if ((status_t)OK != err) { + return err; + } } buffer->release(); @@ -2263,10 +2289,10 @@ status_t MPEG4Writer::Track::threadEntry() { buffer->release(); buffer = NULL; - if (mIsAvc) StripStartcode(copy); + if (mIsAvc || mIsHEVC) StripStartcode(copy); size_t sampleSize = copy->range_length(); - if (mIsAvc) { + if (mIsAvc || mIsHEVC) { if (mOwner->useNalLengthFour()) { sampleSize += 4; } else { @@ -2466,7 +2492,7 @@ status_t MPEG4Writer::Track::threadEntry() { trackProgressStatus(timestampUs); } if (!hasMultipleTracks) { - off64_t offset = mIsAvc? mOwner->addLengthPrefixedSample_l(copy) + off64_t offset = (mIsAvc || mIsHEVC)? mOwner->addLengthPrefixedSample_l(copy) : mOwner->addSample_l(copy); uint32_t count = (mOwner->use32BitFileOffset() @@ -2723,7 +2749,8 @@ status_t MPEG4Writer::Track::checkCodecSpecificData() const { CHECK(mMeta->findCString(kKeyMIMEType, &mime)); if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_AAC, mime) || !strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime) || - !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { + !strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime) || + mIsHEVC) { if (!mCodecSpecificData || mCodecSpecificDataSize <= 0) { ALOGE("Missing codec specific data"); @@ -2832,9 +2859,16 @@ void MPEG4Writer::Track::writeVideoFourCCBox() { writeD263Box(); } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) { writeAvccBox(); + } else if (mIsHEVC) { + AVUtils::get()->HEVCMuxerUtils().writeHvccBox(mOwner, mCodecSpecificData, + mCodecSpecificDataSize, + mOwner->useNalLengthFour()); + } + + if (!mIsHEVC) { + writePaspBox(); } - writePaspBox(); mOwner->endBox(); // mp4v, s263 or avc1 } diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp index 5339c82..935fbf7 100644 --- a/media/libstagefright/Utils.cpp +++ b/media/libstagefright/Utils.cpp @@ -668,6 +668,8 @@ void convertMessageToMetaData(const sp &msg, sp &meta) { // for transporting the CSD to muxers. reassembleESDS(csd0, esds); meta->setData(kKeyESDS, kKeyESDS, esds, sizeof(esds)); + } else { + AVUtils::get()->HEVCMuxerUtils().reassembleHEVCCSD(mime, csd0, meta); } } -- cgit v1.1