mirror of
https://gitlab.winehq.org/wine/wine-gecko.git
synced 2024-09-13 09:24:08 -07:00
Bug 959490 - [MediaEncoder] Support *.3gp with AMR-NB audio format on certificated application. r=roc
This commit is contained in:
parent
68a17dc990
commit
c9dd4c92fe
@ -16,7 +16,8 @@
|
||||
#include "EncodedBufferCache.h"
|
||||
#include "nsIDOMFile.h"
|
||||
#include "mozilla/dom/BlobEvent.h"
|
||||
|
||||
#include "nsIPrincipal.h"
|
||||
#include "nsMimeTypes.h"
|
||||
|
||||
#include "mozilla/dom/AudioStreamTrack.h"
|
||||
#include "mozilla/dom/VideoStreamTrack.h"
|
||||
@ -320,7 +321,19 @@ private:
|
||||
|
||||
// Allocate encoder and bind with union stream.
|
||||
// At this stage, the API doesn't allow UA to choose the output mimeType format.
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
|
||||
|
||||
nsCOMPtr<nsIDocument> doc = mRecorder->GetOwner()->GetExtantDoc();
|
||||
uint16_t appStatus = nsIPrincipal::APP_STATUS_NOT_INSTALLED;
|
||||
if (doc) {
|
||||
doc->NodePrincipal()->GetAppStatus(&appStatus);
|
||||
}
|
||||
// Only allow certificated application can assign AUDIO_3GPP
|
||||
if (appStatus == nsIPrincipal::APP_STATUS_CERTIFIED &&
|
||||
mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP)) {
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP), aTrackTypes);
|
||||
} else {
|
||||
mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes);
|
||||
}
|
||||
|
||||
if (!mEncoder) {
|
||||
DoSessionEndTask(NS_ERROR_ABORT);
|
||||
@ -579,7 +592,9 @@ MediaRecorder::WrapObject(JSContext* aCx, JS::Handle<JSObject*> aScope)
|
||||
|
||||
/* static */ already_AddRefed<MediaRecorder>
|
||||
MediaRecorder::Constructor(const GlobalObject& aGlobal,
|
||||
DOMMediaStream& aStream, ErrorResult& aRv)
|
||||
DOMMediaStream& aStream,
|
||||
const MediaRecorderOptions& aInitDict,
|
||||
ErrorResult& aRv)
|
||||
{
|
||||
nsCOMPtr<nsIScriptGlobalObject> sgo = do_QueryInterface(aGlobal.GetAsSupports());
|
||||
if (!sgo) {
|
||||
@ -594,6 +609,7 @@ MediaRecorder::Constructor(const GlobalObject& aGlobal,
|
||||
}
|
||||
|
||||
nsRefPtr<MediaRecorder> object = new MediaRecorder(aStream, ownerWindow);
|
||||
object->SetMimeType(aInitDict.mMimeType);
|
||||
return object.forget();
|
||||
}
|
||||
|
||||
|
@ -20,6 +20,7 @@ class EncodedBufferCache;
|
||||
class MediaEncoder;
|
||||
class ProcessedMediaStream;
|
||||
class MediaInputPort;
|
||||
struct MediaRecorderOptions;
|
||||
|
||||
namespace dom {
|
||||
|
||||
@ -75,7 +76,9 @@ public:
|
||||
|
||||
static already_AddRefed<MediaRecorder>
|
||||
Constructor(const GlobalObject& aGlobal,
|
||||
DOMMediaStream& aStream, ErrorResult& aRv);
|
||||
DOMMediaStream& aStream,
|
||||
const MediaRecorderOptions& aInitDict,
|
||||
ErrorResult& aRv);
|
||||
|
||||
// EventHandler
|
||||
IMPL_EVENT_HANDLER(dataavailable)
|
||||
|
@ -115,7 +115,7 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
|
||||
(aMIMEType.EqualsLiteral(VIDEO_MP4) ||
|
||||
(aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
|
||||
if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
|
||||
audioEncoder = new OmxAudioTrackEncoder();
|
||||
audioEncoder = new OmxAACAudioTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
}
|
||||
videoEncoder = new OmxVideoTrackEncoder();
|
||||
@ -123,6 +123,14 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
NS_ENSURE_TRUE(videoEncoder, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(VIDEO_MP4);
|
||||
} else if (MediaEncoder::IsOMXEncoderEnabled() &&
|
||||
(aMIMEType.EqualsLiteral(AUDIO_3GPP))) {
|
||||
audioEncoder = new OmxAMRAudioTrackEncoder();
|
||||
NS_ENSURE_TRUE(audioEncoder, nullptr);
|
||||
|
||||
writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
|
||||
NS_ENSURE_TRUE(writer, nullptr);
|
||||
mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
|
||||
}
|
||||
#endif // MOZ_OMX_ENCODER
|
||||
else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
|
||||
|
@ -155,49 +155,6 @@ OmxVideoTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
OmxAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
{
|
||||
mChannels = aChannels;
|
||||
mSamplingRate = aSamplingRate;
|
||||
|
||||
mEncoder = OMXCodecWrapper::CreateAACEncoder();
|
||||
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mInitialized = (rv == NS_OK);
|
||||
|
||||
mReentrantMonitor.NotifyAll();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<TrackMetadataBase>
|
||||
OmxAudioTrackEncoder::GetMetadata()
|
||||
{
|
||||
{
|
||||
// Wait if mEncoder is not initialized nor is being canceled.
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (!mCanceled && !mInitialized) {
|
||||
mReentrantMonitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (mCanceled || mEncodingComplete) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<AACTrackMetadata> meta = new AACTrackMetadata();
|
||||
meta->mChannels = mChannels;
|
||||
meta->mSampleRate = mSamplingRate;
|
||||
meta->mFrameSize = OMXCodecWrapper::kAACFrameSize;
|
||||
meta->mFrameDuration = OMXCodecWrapper::kAACFrameDuration;
|
||||
|
||||
return meta.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
|
||||
{
|
||||
@ -218,8 +175,15 @@ OmxAudioTrackEncoder::AppendEncodedFrames(EncodedFrameContainer& aContainer)
|
||||
}
|
||||
|
||||
nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
|
||||
audiodata->SetFrameType(isCSD ?
|
||||
EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME);
|
||||
if (mEncoder->GetCodecType() == OMXCodecWrapper::AAC_ENC) {
|
||||
audiodata->SetFrameType(isCSD ?
|
||||
EncodedFrame::AAC_CSD : EncodedFrame::AAC_AUDIO_FRAME);
|
||||
} else if (mEncoder->GetCodecType() == OMXCodecWrapper::AMR_NB_ENC){
|
||||
audiodata->SetFrameType(isCSD ?
|
||||
EncodedFrame::AMR_AUDIO_CSD : EncodedFrame::AMR_AUDIO_FRAME);
|
||||
} else {
|
||||
MOZ_ASSERT("audio codec not supported");
|
||||
}
|
||||
audiodata->SetTimeStamp(outTimeUs);
|
||||
rv = audiodata->SwapInFrameData(frameData);
|
||||
NS_ENSURE_SUCCESS(rv, rv);
|
||||
@ -277,4 +241,82 @@ OmxAudioTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
nsresult
|
||||
OmxAACAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
{
|
||||
mChannels = aChannels;
|
||||
mSamplingRate = aSamplingRate;
|
||||
|
||||
mEncoder = OMXCodecWrapper::CreateAACEncoder();
|
||||
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, mSamplingRate);
|
||||
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mInitialized = (rv == NS_OK);
|
||||
|
||||
mReentrantMonitor.NotifyAll();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<TrackMetadataBase>
|
||||
OmxAACAudioTrackEncoder::GetMetadata()
|
||||
{
|
||||
{
|
||||
// Wait if mEncoder is not initialized nor is being canceled.
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (!mCanceled && !mInitialized) {
|
||||
mReentrantMonitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (mCanceled || mEncodingComplete) {
|
||||
return nullptr;
|
||||
}
|
||||
nsRefPtr<AACTrackMetadata> meta = new AACTrackMetadata();
|
||||
meta->mChannels = mChannels;
|
||||
meta->mSampleRate = mSamplingRate;
|
||||
meta->mFrameSize = OMXCodecWrapper::kAACFrameSize;
|
||||
meta->mFrameDuration = OMXCodecWrapper::kAACFrameDuration;
|
||||
return meta.forget();
|
||||
}
|
||||
|
||||
nsresult
|
||||
OmxAMRAudioTrackEncoder::Init(int aChannels, int aSamplingRate)
|
||||
{
|
||||
mChannels = aChannels;
|
||||
mSamplingRate = aSamplingRate;
|
||||
|
||||
mEncoder = OMXCodecWrapper::CreateAMRNBEncoder();
|
||||
NS_ENSURE_TRUE(mEncoder, NS_ERROR_FAILURE);
|
||||
|
||||
nsresult rv = mEncoder->Configure(mChannels, mSamplingRate, AMR_NB_SAMPLERATE);
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
mInitialized = (rv == NS_OK);
|
||||
|
||||
mReentrantMonitor.NotifyAll();
|
||||
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
already_AddRefed<TrackMetadataBase>
|
||||
OmxAMRAudioTrackEncoder::GetMetadata()
|
||||
{
|
||||
{
|
||||
// Wait if mEncoder is not initialized nor is being canceled.
|
||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||
while (!mCanceled && !mInitialized) {
|
||||
mReentrantMonitor.Wait();
|
||||
}
|
||||
}
|
||||
|
||||
if (mCanceled || mEncodingComplete) {
|
||||
return nullptr;
|
||||
}
|
||||
|
||||
nsRefPtr<AMRTrackMetadata> meta = new AMRTrackMetadata();
|
||||
return meta.forget();
|
||||
}
|
||||
|
||||
}
|
||||
|
@ -43,26 +43,54 @@ private:
|
||||
nsAutoPtr<android::OMXVideoEncoder> mEncoder;
|
||||
};
|
||||
|
||||
class OmxAudioTrackEncoder MOZ_FINAL : public AudioTrackEncoder
|
||||
class OmxAudioTrackEncoder : public AudioTrackEncoder
|
||||
{
|
||||
public:
|
||||
OmxAudioTrackEncoder()
|
||||
: AudioTrackEncoder()
|
||||
{}
|
||||
|
||||
already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
|
||||
already_AddRefed<TrackMetadataBase> GetMetadata() = 0;
|
||||
|
||||
nsresult GetEncodedTrack(EncodedFrameContainer& aData) MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
|
||||
nsresult Init(int aChannels, int aSamplingRate) = 0;
|
||||
|
||||
private:
|
||||
// Append encoded frames to aContainer.
|
||||
nsresult AppendEncodedFrames(EncodedFrameContainer& aContainer);
|
||||
|
||||
nsAutoPtr<android::OMXAudioEncoder> mEncoder;
|
||||
};
|
||||
|
||||
class OmxAACAudioTrackEncoder MOZ_FINAL : public OmxAudioTrackEncoder
|
||||
{
|
||||
public:
|
||||
OmxAACAudioTrackEncoder()
|
||||
: OmxAudioTrackEncoder()
|
||||
{}
|
||||
|
||||
already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
class OmxAMRAudioTrackEncoder MOZ_FINAL : public OmxAudioTrackEncoder
|
||||
{
|
||||
public:
|
||||
OmxAMRAudioTrackEncoder()
|
||||
: OmxAudioTrackEncoder()
|
||||
{}
|
||||
|
||||
enum {
|
||||
AMR_NB_SAMPLERATE = 8000,
|
||||
};
|
||||
already_AddRefed<TrackMetadataBase> GetMetadata() MOZ_OVERRIDE;
|
||||
|
||||
protected:
|
||||
nsresult Init(int aChannels, int aSamplingRate) MOZ_OVERRIDE;
|
||||
};
|
||||
|
||||
}
|
||||
#endif
|
||||
|
@ -167,6 +167,7 @@ nsresult
|
||||
ISOControl::SetMetadata(TrackMetadataBase* aTrackMeta)
|
||||
{
|
||||
if (aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AMR ||
|
||||
aTrackMeta->GetKind() == TrackMetadataBase::METADATA_AVC) {
|
||||
mMetaArray.AppendElement(aTrackMeta);
|
||||
return NS_OK;
|
||||
@ -178,7 +179,8 @@ nsresult
|
||||
ISOControl::GetAudioMetadata(nsRefPtr<AudioTrackMetadata>& aAudMeta)
|
||||
{
|
||||
for (uint32_t i = 0; i < mMetaArray.Length() ; i++) {
|
||||
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC) {
|
||||
if (mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AAC ||
|
||||
mMetaArray[i]->GetKind() == TrackMetadataBase::METADATA_AMR) {
|
||||
aAudMeta = static_cast<AudioTrackMetadata*>(mMetaArray[i].get());
|
||||
return NS_OK;
|
||||
}
|
||||
|
@ -27,6 +27,8 @@ using namespace mozilla::layers;
|
||||
#define ENCODER_CONFIG_I_FRAME_INTERVAL 1
|
||||
// Wait up to 5ms for input buffers.
|
||||
#define INPUT_BUFFER_TIMEOUT_US (5 * 1000ll)
|
||||
// AMR NB kbps
|
||||
#define AMRNB_BITRATE 12200
|
||||
|
||||
#define CODEC_ERROR(args...) \
|
||||
do { \
|
||||
@ -45,6 +47,16 @@ OMXCodecWrapper::CreateAACEncoder()
|
||||
return aac.forget();
|
||||
}
|
||||
|
||||
OMXAudioEncoder*
|
||||
OMXCodecWrapper::CreateAMRNBEncoder()
|
||||
{
|
||||
nsAutoPtr<OMXAudioEncoder> amr(new OMXAudioEncoder(CodecType::AMR_NB_ENC));
|
||||
// Return the object only when media codec is valid.
|
||||
NS_ENSURE_TRUE(amr->IsValid(), nullptr);
|
||||
|
||||
return amr.forget();
|
||||
}
|
||||
|
||||
OMXVideoEncoder*
|
||||
OMXCodecWrapper::CreateAVCEncoder()
|
||||
{
|
||||
@ -56,7 +68,9 @@ OMXCodecWrapper::CreateAVCEncoder()
|
||||
}
|
||||
|
||||
OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
|
||||
: mStarted(false)
|
||||
: mCodecType(aCodecType)
|
||||
, mStarted(false)
|
||||
, mAMRCSDProvided(false)
|
||||
{
|
||||
ProcessState::self()->startThreadPool();
|
||||
|
||||
@ -65,6 +79,8 @@ OMXCodecWrapper::OMXCodecWrapper(CodecType aCodecType)
|
||||
|
||||
if (aCodecType == CodecType::AVC_ENC) {
|
||||
mCodec = MediaCodec::CreateByType(mLooper, MEDIA_MIMETYPE_VIDEO_AVC, true);
|
||||
} else if (aCodecType == CodecType::AMR_NB_ENC) {
|
||||
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 {
|
||||
@ -380,28 +396,53 @@ void OMXVideoEncoder::AppendFrame(nsTArray<uint8_t>* aOutputBuf,
|
||||
}
|
||||
|
||||
nsresult
|
||||
OMXAudioEncoder::Configure(int aChannels, int aSamplingRate)
|
||||
OMXAudioEncoder::Configure(int aChannels, int aInputSampleRate,
|
||||
int aEncodedSampleRate)
|
||||
{
|
||||
MOZ_ASSERT(!mStarted);
|
||||
|
||||
NS_ENSURE_TRUE(aChannels > 0 && aSamplingRate > 0, NS_ERROR_INVALID_ARG);
|
||||
NS_ENSURE_TRUE(aChannels > 0 && aInputSampleRate > 0 && aEncodedSampleRate >= 0,
|
||||
NS_ERROR_INVALID_ARG);
|
||||
|
||||
if (aInputSampleRate != aEncodedSampleRate) {
|
||||
int error;
|
||||
mResampler = speex_resampler_init(aChannels,
|
||||
aInputSampleRate,
|
||||
aEncodedSampleRate,
|
||||
SPEEX_RESAMPLER_QUALITY_DEFAULT,
|
||||
&error);
|
||||
|
||||
if (error != RESAMPLER_ERR_SUCCESS) {
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
speex_resampler_skip_zeros(mResampler);
|
||||
}
|
||||
// Set up configuration parameters for AAC encoder.
|
||||
sp<AMessage> format = new AMessage;
|
||||
// Fixed values.
|
||||
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
|
||||
format->setInt32("bitrate", kAACBitrate);
|
||||
format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
|
||||
if (mCodecType == AAC_ENC) {
|
||||
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AAC);
|
||||
format->setInt32("aac-profile", OMX_AUDIO_AACObjectLC);
|
||||
format->setInt32("bitrate", kAACBitrate);
|
||||
format->setInt32("sample-rate", aInputSampleRate);
|
||||
} else if (mCodecType == AMR_NB_ENC) {
|
||||
format->setString("mime", MEDIA_MIMETYPE_AUDIO_AMR_NB);
|
||||
format->setInt32("bitrate", AMRNB_BITRATE);
|
||||
format->setInt32("sample-rate", aEncodedSampleRate);
|
||||
} else {
|
||||
MOZ_ASSERT(false, "Can't support this codec type!!");
|
||||
}
|
||||
// Input values.
|
||||
format->setInt32("channel-count", aChannels);
|
||||
format->setInt32("sample-rate", aSamplingRate);
|
||||
|
||||
status_t result = mCodec->configure(format, nullptr, nullptr,
|
||||
MediaCodec::CONFIGURE_FLAG_ENCODE);
|
||||
NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
|
||||
|
||||
mChannels = aChannels;
|
||||
mSampleDuration = 1000000 / aSamplingRate;
|
||||
mSampleDuration = 1000000 / aInputSampleRate;
|
||||
mResamplingRatio = aEncodedSampleRate > 0 ? 1.0 *
|
||||
aEncodedSampleRate / aInputSampleRate : 1.0;
|
||||
result = Start();
|
||||
|
||||
return result == OK ? NS_OK : NS_ERROR_FAILURE;
|
||||
@ -474,6 +515,14 @@ private:
|
||||
size_t mOffset;
|
||||
};
|
||||
|
||||
OMXAudioEncoder::~OMXAudioEncoder()
|
||||
{
|
||||
if (mResampler) {
|
||||
speex_resampler_destroy(mResampler);
|
||||
mResampler = nullptr;
|
||||
}
|
||||
}
|
||||
|
||||
nsresult
|
||||
OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
|
||||
{
|
||||
@ -495,16 +544,16 @@ OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
|
||||
}
|
||||
NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
|
||||
|
||||
size_t samplesCopied = 0; // Number of copied samples.
|
||||
size_t sourceSamplesCopied = 0; // Number of copied samples.
|
||||
|
||||
if (numSamples > 0) {
|
||||
// Copy input PCM data to input buffer until queue is empty.
|
||||
AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(aSegment));
|
||||
while (!iter.IsEnded()) {
|
||||
AudioChunk chunk = *iter;
|
||||
size_t samplesToCopy = chunk.GetDuration(); // Number of samples to copy.
|
||||
size_t bytesToCopy = samplesToCopy * mChannels * sizeof(AudioDataValue);
|
||||
|
||||
size_t sourceSamplesToCopy = chunk.GetDuration(); // Number of samples to copy.
|
||||
size_t bytesToCopy = sourceSamplesToCopy * mChannels *
|
||||
sizeof(AudioDataValue) * mResamplingRatio;
|
||||
if (bytesToCopy > buffer.AvailableSize()) {
|
||||
// Not enough space left in input buffer. Send it to encoder and get a
|
||||
// new one.
|
||||
@ -515,32 +564,47 @@ OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
|
||||
if (result == -EAGAIN) {
|
||||
// All input buffers are full. Caller can try again later after
|
||||
// consuming some output buffers.
|
||||
aSegment.RemoveLeading(samplesCopied);
|
||||
aSegment.RemoveLeading(sourceSamplesCopied);
|
||||
return NS_OK;
|
||||
}
|
||||
|
||||
mTimestamp += samplesCopied * mSampleDuration;
|
||||
samplesCopied = 0;
|
||||
mTimestamp += sourceSamplesCopied * mSampleDuration;
|
||||
sourceSamplesCopied = 0;
|
||||
|
||||
NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
|
||||
}
|
||||
|
||||
AudioDataValue* dst = reinterpret_cast<AudioDataValue*>(buffer.GetPointer());
|
||||
uint32_t dstSamplesCopied = sourceSamplesToCopy;
|
||||
if (!chunk.IsNull()) {
|
||||
// Append the interleaved data to input buffer.
|
||||
AudioTrackEncoder::InterleaveTrackData(chunk, samplesToCopy, mChannels,
|
||||
dst);
|
||||
if (mResampler) {
|
||||
nsAutoTArray<AudioDataValue, 9600> pcm;
|
||||
pcm.SetLength(bytesToCopy);
|
||||
// Append the interleaved data to input buffer.
|
||||
AudioTrackEncoder::InterleaveTrackData(chunk, sourceSamplesToCopy,
|
||||
mChannels,
|
||||
pcm.Elements());
|
||||
uint32_t inframes = sourceSamplesToCopy;
|
||||
short* in = reinterpret_cast<short*>(pcm.Elements());
|
||||
speex_resampler_process_interleaved_int(mResampler, in, &inframes,
|
||||
dst, &dstSamplesCopied);
|
||||
} else {
|
||||
AudioTrackEncoder::InterleaveTrackData(chunk, sourceSamplesToCopy,
|
||||
mChannels,
|
||||
dst);
|
||||
dstSamplesCopied = sourceSamplesToCopy * mChannels;
|
||||
}
|
||||
} else {
|
||||
// Silence.
|
||||
memset(dst, 0, bytesToCopy);
|
||||
memset(dst, 0, mResamplingRatio * sourceSamplesToCopy * sizeof(AudioDataValue));
|
||||
}
|
||||
|
||||
samplesCopied += samplesToCopy;
|
||||
buffer.IncreaseOffset(bytesToCopy);
|
||||
sourceSamplesCopied += sourceSamplesToCopy;
|
||||
buffer.IncreaseOffset(dstSamplesCopied * sizeof(AudioDataValue));
|
||||
iter.Next();
|
||||
}
|
||||
if (samplesCopied > 0) {
|
||||
aSegment.RemoveLeading(samplesCopied);
|
||||
if (sourceSamplesCopied > 0) {
|
||||
aSegment.RemoveLeading(sourceSamplesCopied);
|
||||
}
|
||||
} else if (aInputFlags & BUFFER_EOS) {
|
||||
// No audio data left in segment but we still have to feed something to
|
||||
@ -548,10 +612,10 @@ OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
|
||||
size_t bytesToCopy = mChannels * sizeof(AudioDataValue);
|
||||
memset(buffer.GetPointer(), 0, bytesToCopy);
|
||||
buffer.IncreaseOffset(bytesToCopy);
|
||||
samplesCopied = 1;
|
||||
sourceSamplesCopied = 1;
|
||||
}
|
||||
|
||||
if (samplesCopied > 0) {
|
||||
if (sourceSamplesCopied > 0) {
|
||||
int flags = aInputFlags;
|
||||
if (aSegment.GetDuration() > 0) {
|
||||
// Don't signal EOS until source segment is empty.
|
||||
@ -560,7 +624,7 @@ OMXAudioEncoder::Encode(AudioSegment& aSegment, int aInputFlags)
|
||||
result = buffer.Enqueue(mTimestamp, flags);
|
||||
NS_ENSURE_TRUE(result == OK, NS_ERROR_FAILURE);
|
||||
|
||||
mTimestamp += samplesCopied * mSampleDuration;
|
||||
mTimestamp += sourceSamplesCopied * mSampleDuration;
|
||||
}
|
||||
|
||||
return NS_OK;
|
||||
@ -663,6 +727,20 @@ OMXCodecWrapper::GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
|
||||
mCodec->releaseOutputBuffer(index);
|
||||
return NS_ERROR_FAILURE;
|
||||
}
|
||||
} else if ((mCodecType == AMR_NB_ENC) && !mAMRCSDProvided){
|
||||
// OMX AMR codec won't provide csd data, need to generate a fake one.
|
||||
nsRefPtr<EncodedFrame> audiodata = new EncodedFrame();
|
||||
// Decoder config descriptor
|
||||
const uint8_t decConfig[] = {
|
||||
0x0, 0x0, 0x0, 0x0, // vendor: 4 bytes
|
||||
0x0, // decoder version
|
||||
0x83, 0xFF, // mode set: all enabled
|
||||
0x00, // mode change period
|
||||
0x01, // frames per sample
|
||||
};
|
||||
aOutputBuf->AppendElements(decConfig, sizeof(decConfig));
|
||||
outFlags |= MediaCodec::BUFFER_FLAG_CODECCONFIG;
|
||||
mAMRCSDProvided = true;
|
||||
} else {
|
||||
AppendFrame(aOutputBuf, omxBuf->data(), omxBuf->size());
|
||||
}
|
||||
|
@ -15,6 +15,8 @@
|
||||
#include "GonkNativeWindow.h"
|
||||
#include "GonkNativeWindowClient.h"
|
||||
|
||||
#include <speex/speex_resampler.h>
|
||||
|
||||
namespace android {
|
||||
|
||||
class OMXAudioEncoder;
|
||||
@ -53,6 +55,7 @@ public:
|
||||
// Codec types.
|
||||
enum CodecType {
|
||||
AAC_ENC, // AAC encoder.
|
||||
AMR_NB_ENC, // AMR_NB encoder.
|
||||
AVC_ENC, // AVC/H.264 encoder.
|
||||
TYPE_COUNT
|
||||
};
|
||||
@ -83,6 +86,9 @@ public:
|
||||
/** Create a AAC audio encoder. Returns nullptr when failed. */
|
||||
static OMXAudioEncoder* CreateAACEncoder();
|
||||
|
||||
/** Create a AMR audio encoder. Returns nullptr when failed. */
|
||||
static OMXAudioEncoder* CreateAMRNBEncoder();
|
||||
|
||||
/** Create a AVC/H.264 video encoder. Returns nullptr when failed. */
|
||||
static OMXVideoEncoder* CreateAVCEncoder();
|
||||
|
||||
@ -97,7 +103,10 @@ public:
|
||||
nsresult GetNextEncodedFrame(nsTArray<uint8_t>* aOutputBuf,
|
||||
int64_t* aOutputTimestamp, int* aOutputFlags,
|
||||
int64_t aTimeOut);
|
||||
|
||||
/*
|
||||
* Get the codec type
|
||||
*/
|
||||
int GetCodecType() { return mCodecType; }
|
||||
protected:
|
||||
/**
|
||||
* See whether the object has been initialized successfully and is ready to
|
||||
@ -160,7 +169,9 @@ private:
|
||||
Vector<sp<ABuffer> > mInputBufs; // MediaCodec buffers to hold input data.
|
||||
Vector<sp<ABuffer> > mOutputBufs; // MediaCodec buffers to hold output data.
|
||||
|
||||
int mCodecType;
|
||||
bool mStarted; // Has MediaCodec been started?
|
||||
bool mAMRCSDProvided;
|
||||
};
|
||||
|
||||
/**
|
||||
@ -172,8 +183,9 @@ public:
|
||||
/**
|
||||
* Configure audio codec parameters and start media codec. It must be called
|
||||
* before calling Encode() and GetNextEncodedFrame().
|
||||
* aReSamplingRate = 0 means no resampler required
|
||||
*/
|
||||
nsresult Configure(int aChannelCount, int aSampleRate);
|
||||
nsresult Configure(int aChannelCount, int aInputSampleRate, int aEncodedSampleRate);
|
||||
|
||||
/**
|
||||
* Encode 16-bit PCM audio samples stored in aSegment. To notify end of
|
||||
@ -183,10 +195,10 @@ public:
|
||||
*/
|
||||
nsresult Encode(mozilla::AudioSegment& aSegment, int aInputFlags = 0);
|
||||
|
||||
~OMXAudioEncoder();
|
||||
protected:
|
||||
virtual status_t AppendDecoderConfig(nsTArray<uint8_t>* aOutputBuf,
|
||||
ABuffer* aData) MOZ_OVERRIDE;
|
||||
|
||||
private:
|
||||
// Hide these. User should always use creator functions to get a media codec.
|
||||
OMXAudioEncoder() MOZ_DELETE;
|
||||
@ -199,15 +211,24 @@ private:
|
||||
*/
|
||||
OMXAudioEncoder(CodecType aCodecType)
|
||||
: OMXCodecWrapper(aCodecType)
|
||||
, mResampler(nullptr)
|
||||
, mChannels(0)
|
||||
, mTimestamp(0)
|
||||
, mSampleDuration(0) {}
|
||||
, mSampleDuration(0)
|
||||
, mResamplingRatio(0) {}
|
||||
|
||||
// For creator function to access hidden constructor.
|
||||
friend class OMXCodecWrapper;
|
||||
|
||||
/**
|
||||
* If the input sample rate does not divide 48kHz evenly, the input data are
|
||||
* resampled.
|
||||
*/
|
||||
SpeexResamplerState* mResampler;
|
||||
// Number of audio channels.
|
||||
size_t mChannels;
|
||||
|
||||
float mResamplingRatio;
|
||||
// The total duration of audio samples that have been encoded in microseconds.
|
||||
int64_t mTimestamp;
|
||||
// Time per audio sample in microseconds.
|
||||
|
@ -12,7 +12,7 @@
|
||||
|
||||
enum RecordingState { "inactive", "recording", "paused" };
|
||||
|
||||
[Constructor(MediaStream stream)]
|
||||
[Constructor(MediaStream stream, optional MediaRecorderOptions options)]
|
||||
interface MediaRecorder : EventTarget {
|
||||
|
||||
readonly attribute MediaStream stream;
|
||||
@ -45,3 +45,6 @@ interface MediaRecorder : EventTarget {
|
||||
void requestData();
|
||||
};
|
||||
|
||||
dictionary MediaRecorderOptions {
|
||||
DOMString mimeType = ""; // Default encoding mimeType.
|
||||
};
|
||||
|
@ -79,6 +79,7 @@
|
||||
#define AUDIO_MP3 "audio/mpeg"
|
||||
#define AUDIO_MP4 "audio/mp4"
|
||||
#define AUDIO_AMR "audio/amr"
|
||||
#define AUDIO_3GPP "audio/3gpp"
|
||||
#define AUDIO_MIDI "audio/x-midi"
|
||||
|
||||
#define BINARY_OCTET_STREAM "binary/octet-stream"
|
||||
|
Loading…
Reference in New Issue
Block a user