Bug 1205927 - Part 1: [MediaEncoder] Support *.3g2 with EVRC audio format. r=ayang

This commit is contained in:
Munro Chiang 2015-10-27 15:12:26 +08:00
parent bff6406254
commit 42b02d58cd
18 changed files with 306 additions and 6 deletions

View File

@ -599,6 +599,12 @@ private:
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
aTrackTypes);
} else if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP2)) {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP2),
mRecorder->GetAudioBitrate(),
mRecorder->GetVideoBitrate(),
mRecorder->GetBitrate(),
aTrackTypes);
} else {
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""),
mRecorder->GetAudioBitrate(),

View File

@ -60,6 +60,8 @@ public:
AAC_CSD, // AAC codec specific data
AMR_AUDIO_CSD,
AMR_AUDIO_FRAME,
EVRC_AUDIO_CSD,
EVRC_AUDIO_FRAME,
UNKNOWN // FrameType not set
};
void SwapInFrameData(nsTArray<uint8_t>& aData)

View File

@ -130,6 +130,14 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
NS_ENSURE_TRUE(writer, nullptr);
mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
(aMIMEType.EqualsLiteral(AUDIO_3GPP2))) {
audioEncoder = new OmxEVRCAudioTrackEncoder();
NS_ENSURE_TRUE(audioEncoder, nullptr);
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3G2);
NS_ENSURE_TRUE(writer, nullptr);
mimeType = NS_LITERAL_STRING(AUDIO_3GPP2) ;
}
#endif // MOZ_OMX_ENCODER
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&

View File

@ -184,7 +184,7 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
3000); // wait up to 3ms
NS_ENSURE_SUCCESS(rv, rv);
if (!frameData.IsEmpty()) {
if (!frameData.IsEmpty() || outFlags & OMXCodecWrapper::BUFFER_EOS) { // Some hw codec may send out EOS with an empty frame
bool isCSD = false;
if (outFlags & OMXCodecWrapper::BUFFER_CODEC_CONFIG) { // codec specific data
isCSD = true;
@ -199,6 +199,9 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
audiodata->SetFrameType(isCSD ?
EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::EVRC_ENC){
audiodata->SetFrameType(isCSD ?
EncodedFrame::EVRC_AUDIO_CSD : EncodedFrame::EVRC_AUDIO_FRAME);
} else {
MOZ_ASSERT(false, "audio codec not supported");
}
@ -343,4 +346,44 @@ OmxAMRAudioTrackEncoder::GetMetadata()
return meta.forget();
}
nsresult
OmxEVRCAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
{
mChannels = aChannels;
mSamplingRate = aSamplingRate;
mEncoder = OMXCodecWrapper::CreateEVRCEncoder();
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, EVRC_SAMPLERATE);
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
mInitialized = (rv == NS_OK);
mReentrantMonitor.NotifyAll();
return NS_OK;
}
already_AddRefed<TrackMetadataBase>
OmxEVRCAudioTrackEncoder::GetMetadata()
{
PROFILER_LABEL("OmxEVRCAudioTrackEncoder", "GetMetadata",
js::ProfileEntry::Category::OTHER);
{
// Wait if mEncoder is not initialized nor is being canceled.
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
while (!mCanceled && !mInitialized) {
mReentrantMonitor.Wait();
}
}
if (mCanceled || mEncodingComplete) {
return nullptr;
}
RefPtr<EVRCTrackMetadata> meta = new EVRCTrackMetadata();
meta->mChannels = mChannels;
return meta.forget();
}
}

View File

@ -90,5 +90,21 @@ protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
class OmxEVRCAudioTrackEncoder final : public OmxAudioTrackEncoder
{
public:
OmxEVRCAudioTrackEncoder()
: OmxAudioTrackEncoder()
{}
enum {
EVRC_SAMPLERATE = 8000,
};
already_AddRefed<TrackMetadataBase> GetMetadata() override;
protected:
nsresult Init(int aChannels, int aSamplingRate) override;
};
}
#endif

View File

@ -22,6 +22,7 @@ public:
METADATA_AVC,
METADATA_AAC,
METADATA_AMR,
METADATA_EVRC,
METADATA_UNKNOWN // Metadata Kind not set
};
// Return the specific metadata kind

View File

@ -0,0 +1,84 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#include "ISOControl.h"
#include "ISOMediaBoxes.h"
#include "EVRCBox.h"
#include "ISOTrackMetadata.h"
namespace mozilla {
nsresult
EVRCSampleEntry::Generate(uint32_t* aBoxSize)
{
uint32_t box_size;
nsresult rv = evrc_special_box->Generate(&box_size);
NS_ENSURE_SUCCESS(rv, rv);
size += box_size;
*aBoxSize = size;
return NS_OK;
}
nsresult
EVRCSampleEntry::Write()
{
BoxSizeChecker checker(mControl, size);
nsresult rv;
rv = AudioSampleEntry::Write();
NS_ENSURE_SUCCESS(rv, rv);
rv = evrc_special_box->Write();
NS_ENSURE_SUCCESS(rv, rv);
return NS_OK;
}
EVRCSampleEntry::EVRCSampleEntry(ISOControl* aControl)
: AudioSampleEntry(NS_LITERAL_CSTRING("sevc"), aControl)
{
evrc_special_box = new EVRCSpecificBox(aControl);
MOZ_COUNT_CTOR(EVRCSampleEntry);
}
EVRCSampleEntry::~EVRCSampleEntry()
{
MOZ_COUNT_DTOR(EVRCSampleEntry);
}
nsresult
EVRCSpecificBox::Generate(uint32_t* aBoxSize)
{
nsresult rv;
FragmentBuffer* frag = mControl->GetFragment(Audio_Track);
rv = frag->GetCSD(evrcDecSpecInfo);
NS_ENSURE_SUCCESS(rv, rv);
size += evrcDecSpecInfo.Length();
*aBoxSize = size;
return NS_OK;
}
nsresult
EVRCSpecificBox::Write()
{
BoxSizeChecker checker(mControl, size);
Box::Write();
mControl->Write(evrcDecSpecInfo.Elements(), evrcDecSpecInfo.Length());
return NS_OK;
}
EVRCSpecificBox::EVRCSpecificBox(ISOControl* aControl)
: Box(NS_LITERAL_CSTRING("devc"), aControl)
{
MOZ_COUNT_CTOR(EVRCSpecificBox);
}
EVRCSpecificBox::~EVRCSpecificBox()
{
MOZ_COUNT_DTOR(EVRCSpecificBox);
}
}

View File

@ -0,0 +1,50 @@
/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
/* This Source Code Form is subject to the terms of the Mozilla Public
* License, v. 2.0. If a copy of the MPL was not distributed with this file,
* You can obtain one at http://mozilla.org/MPL/2.0/. */
#ifndef EVRCBOX_h_
#define EVRCBOX_h_
#include "nsTArray.h"
#include "MuxerOperation.h"
namespace mozilla {
class ISOControl;
// 3GPP TS 26.244 6.7 'EVRCSpecificBox field for EVRCSampleEntry box'
// Box type: 'devc'
class EVRCSpecificBox : public Box {
public:
// 3GPP members
nsTArray<uint8_t> evrcDecSpecInfo;
// MuxerOperation methods
nsresult Generate(uint32_t* aBoxSize) override;
nsresult Write() override;
// EVRCSpecificBox methods
EVRCSpecificBox(ISOControl* aControl);
~EVRCSpecificBox();
};
// 3GPP TS 26.244 6.5 'EVRCSampleEntry box'
// Box type: 'sevc'
class EVRCSampleEntry : public AudioSampleEntry {
public:
// 3GPP members
RefPtr<EVRCSpecificBox> evrc_special_box;
// MuxerOperation methods
nsresult Generate(uint32_t* aBoxSize) override;
nsresult Write() override;
// EVRCSampleEntry methods
EVRCSampleEntry(ISOControl* aControl);
~EVRCSampleEntry();
};
}
#endif // EVRCBOX_h_

View File

@ -62,7 +62,7 @@ FragmentBuffer::AddFrame(EncodedFrame* aFrame)
EncodedFrame::FrameType type = aFrame->GetFrameType();
if (type == EncodedFrame::AAC_CSD || type == EncodedFrame::AVC_CSD ||
type == EncodedFrame::AMR_AUDIO_CSD) {
type == EncodedFrame::AMR_AUDIO_CSD || type == EncodedFrame::EVRC_AUDIO_CSD) {
mCSDFrame = aFrame;
// Use CSD's timestamp as the start time. Encoder should send CSD frame first
// and then data frames.
@ -168,7 +168,8 @@ ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta)
{
if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC ||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR ||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) {
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC ||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
mMetaArray.AppendElement(aTrackMeta);
return NS_OK;
}
@ -180,7 +181,8 @@ ISOControl::GetAudioMetadata(RefPtr<AudioTrackMetadata>& aAudMeta)
{
for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) {
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR ||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_EVRC) {
aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
return NS_OK;
}

View File

@ -13,6 +13,7 @@
#include "MP4ESDS.h"
#include "AMRBox.h"
#include "AVCBox.h"
#include "EVRCBox.h"
#include "VideoUtils.h"
namespace mozilla {
@ -666,6 +667,8 @@ SampleDescriptionBox::CreateAudioSampleEntry(RefPtr<SampleEntryBox>& aSampleEntr
aSampleEntry = new AMRSampleEntry(mControl);
} else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_AAC) {
aSampleEntry = new MP4AudioSampleEntry(mControl);
} else if (mAudioMeta->GetKind() == TrackMetadataBase::METADATA_EVRC) {
aSampleEntry = new EVRCSampleEntry(mControl);
} else {
MOZ_ASSERT(0);
}
@ -1250,6 +1253,17 @@ FileTypeBox::Generate(uint32_t* aBoxSize)
compatible_brands.AppendElement("3gp7");
compatible_brands.AppendElement("3gp6");
compatible_brands.AppendElement("isom");
} else if (mControl->GetMuxingType() == ISOMediaWriter::TYPE_FRAG_3G2) {
major_brand = "3g2a";
// 3GPP2 Release 0 and A and 3GPP Release 6 allow movie fragmentation
compatible_brands.AppendElement("3gp9");
compatible_brands.AppendElement("3gp8");
compatible_brands.AppendElement("3gp7");
compatible_brands.AppendElement("3gp6");
compatible_brands.AppendElement("isom");
compatible_brands.AppendElement("3g2c");
compatible_brands.AppendElement("3g2b");
compatible_brands.AppendElement("3g2a");
} else {
MOZ_ASSERT(0);
}

View File

@ -104,7 +104,9 @@ ISOMediaWriter::WriteEncodedTrack(const EncodedFrameContainer& aData,
if (type == EncodedFrame::AAC_AUDIO_FRAME ||
type == EncodedFrame::AAC_CSD ||
type == EncodedFrame::AMR_AUDIO_FRAME ||
type == EncodedFrame::AMR_AUDIO_CSD) {
type == EncodedFrame::AMR_AUDIO_CSD ||
type == EncodedFrame::EVRC_AUDIO_FRAME ||
type == EncodedFrame::EVRC_AUDIO_CSD) {
frag = mAudioFragmentBuffer;
} else if (type == EncodedFrame::AVC_I_FRAME ||
type == EncodedFrame::AVC_P_FRAME ||
@ -212,7 +214,8 @@ ISOMediaWriter::SetMetadata(TrackMetadataBase* aMetadata)
PROFILER_LABEL("ISOMediaWriter", "SetMetadata",
js::ProfileEntry::Category::OTHER);
if (aMetadata->GetKind() == TrackMetadataBase::METADATA_AAC ||
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR) {
aMetadata->GetKind() == TrackMetadataBase::METADATA_AMR ||
aMetadata->GetKind() == TrackMetadataBase::METADATA_EVRC) {
mControl->SetMetadata(aMetadata);
mAudioFragmentBuffer = new FragmentBuffer(Audio_Track, FRAG_DURATION);
mControl->SetFragment(mAudioFragmentBuffer);

View File

@ -26,6 +26,10 @@ public:
// Brand names in 'ftyp' box are '3gp9' and 'isom'.
const static uint32_t TYPE_FRAG_3GP = 1 << 1;
// Generate an fragmented 3G2 stream, 3GPP2 C.S0050-B
// Brand names in 'ftyp' box are '3g2c' and 'isom'
const static uint32_t TYPE_FRAG_3G2 = 1 << 2;
// aType is the combination of CREATE_AUDIO_TRACK and CREATE_VIDEO_TRACK.
// It is a hint to muxer that the output streaming contains audio, video
// or both.

View File

@ -99,6 +99,33 @@ public:
~AMRTrackMetadata() { MOZ_COUNT_DTOR(AMRTrackMetadata); }
};
// EVRC sample rate is 8000 samples/s.
#define EVRC_SAMPLE_RATE 8000
class EVRCTrackMetadata : public AudioTrackMetadata {
public:
// AudioTrackMetadata members
//
// The number of sample sets generates by encoder is variant. So the
// frame duration and frame size are both 0.
uint32_t GetAudioFrameDuration() override { return 0; }
uint32_t GetAudioFrameSize() override { return 0; }
uint32_t GetAudioSampleRate() override { return EVRC_SAMPLE_RATE; }
uint32_t GetAudioChannels() override { return mChannels; }
// TrackMetadataBase member
MetadataKind GetKind() const override { return METADATA_EVRC; }
// EVRCTrackMetadata members
EVRCTrackMetadata()
: mChannels(0) {
MOZ_COUNT_CTOR(EVRCTrackMetadata);
}
~EVRCTrackMetadata() { MOZ_COUNT_DTOR(EVRCTrackMetadata); }
uint32_t mChannels; // Channel number, it should be 1 or 2.
};
}
#endif // ISOTrackMetadata_h_

View File

@ -12,6 +12,7 @@ EXPORTS += [
UNIFIED_SOURCES += [
'AMRBox.cpp',
'AVCBox.cpp',
'EVRCBox.cpp',
'ISOControl.cpp',
'ISOMediaBoxes.cpp',
'ISOMediaWriter.cpp',

View File

@ -30,6 +30,7 @@ using namespace mozilla::layers;
#define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
// AMR NB kbps
#define AMRNB_BITRATE 12200
#define EVRC_BITRATE 8755
#define CODEC_ERROR(args...) \
do { \
@ -38,6 +39,8 @@ using namespace mozilla::layers;
namespace android {
const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc";
enum BufferState
{
BUFFER_OK,
@ -85,6 +88,16 @@ OMXCodecWrapper::CreateAMRNBEncoder()
return amr.forget();
}
OMXAudioEncoder*
OMXCodecWrapper::CreateEVRCEncoder()
{
nsAutoPtr<OMXAudioEncoder> evrc(new OMXAudioEncoder(CodecType::EVRC_ENC));
// Return the object only when media codec is valid.
NS_ENSURE_TRUE(evrc->IsValid(), nullptr);
return evrc.forget();
}
OMXVideoEncoder*
OMXCodecWrapper::CreateAVCEncoder()
{
@ -99,6 +112,7 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
: mCodecType(aCodecType)
, mStarted(false)
, mAMRCSDProvided(false)
, mEVRCCSDProvided(false)
{
ProcessState::self()->startThreadPool();
@ -111,6 +125,8 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AMR_NB, true);
} else if (aCodecType == CodecType::AAC_ENC) {
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_AAC, true);
} else if (aCodecType == CodecType::EVRC_ENC) {
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_AUDIO_EVRC, true);
} else {
NS_ERROR("Unknown codec type.");
}
@ -633,6 +649,10 @@ OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
format->setInt32("bitrate", AMRNB_BITRATE);
format->setInt32("sample-rate", aEncodedSampleRate);
} else if (mCodecType == EVRC_ENC) {
format->setString("mime", MEDIA_MIMETYPE_AUDIO_EVRC);
format->setInt32("bitrate", EVRC_BITRATE);
format->setInt32("sample-rate", aEncodedSampleRate);
} else {
MOZ_ASSERT(false, "Can't support this codec type!!");
}
@ -1070,6 +1090,18 @@ OMXCodecWrapper::GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
mAMRCSDProvided = true;
} else if ((mCodecType == EVRC_ENC) && !mEVRCCSDProvided){
// OMX EVRC codec won't provide csd data, need to generate a fake one.
RefPtr<EncodedFrame> audiodata = new EncodedFrame();
// Decoder config descriptor
const uint8_t decConfig[] = {
0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
0x0, // decoder version
0x01, // frames per sample
};
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
mEVRCCSDProvided = true;
} else {
AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
}

View File

@ -88,6 +88,7 @@ public:
AAC_ENC, // AAC encoder.
AMR_NB_ENC, // AMR_NB encoder.
AVC_ENC, // AVC/H.264 encoder.
EVRC_ENC, // EVRC encoder
TYPE_COUNT
};
@ -120,6 +121,9 @@ public:
/** Create a AMR audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateAMRNBEncoder();
/** Create a EVRC audio encoder. Returns nullptr when failed. */
static OMXAudioEncoder* CreateEVRCEncoder();
/** Create a AVC/H.264 video encoder. Returns nullptr when failed. */
static OMXVideoEncoder* CreateAVCEncoder();
@ -204,6 +208,7 @@ private:
int mCodecType;
bool mStarted; // Has MediaCodec been started?
bool mAMRCSDProvided;
bool mEVRCCSDProvided;
};
/**

View File

@ -84,6 +84,7 @@
#define AUDIO_AMR "audio/amr"
#define AUDIO_FLAC "audio/flac"
#define AUDIO_3GPP "audio/3gpp"
#define AUDIO_3GPP2 "audio/3gpp2"
#define AUDIO_MIDI "audio/x-midi"
#define AUDIO_MATROSKA "audio/x-matroska"

View File

@ -618,6 +618,7 @@ static nsExtraMimeTypeEntry extraMimeEntries [] =
// app on Firefox OS depends on the "3gp" extension mapping to the
// "video/3gpp" MIME type.
{ AUDIO_3GPP, "3gpp,3gp", "3GPP Audio" },
{ AUDIO_3GPP2, "3g2", "3GPP2 Audio" },
#endif
{ AUDIO_MIDI, "mid", "Standard MIDI Audio" }
};