diff --git a/dom/media/MediaRecorder.cpp b/dom/media/MediaRecorder.cpp index a1c747c4c6..a1878cc5c2 100644 --- a/dom/media/MediaRecorder.cpp +++ b/dom/media/MediaRecorder.cpp @@ -581,9 +581,17 @@ private: // Make sure the application has permission to assign AUDIO_3GPP if (mRecorder->mMimeType.EqualsLiteral(AUDIO_3GPP) && Check3gppPermission()) { - mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP), aTrackTypes); + mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(AUDIO_3GPP), + mRecorder->GetAudioBitrate(), + mRecorder->GetVideoBitrate(), + mRecorder->GetBitrate(), + aTrackTypes); } else { - mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), aTrackTypes); + mEncoder = MediaEncoder::CreateEncoder(NS_LITERAL_STRING(""), + mRecorder->GetAudioBitrate(), + mRecorder->GetVideoBitrate(), + mRecorder->GetBitrate(), + aTrackTypes); } if (!mEncoder) { @@ -984,6 +992,17 @@ MediaRecorder::SetOptions(const MediaRecorderOptions& aInitDict) aInitDict.mVideoBitsPerSecond.Value() : 0; mBitsPerSecond = aInitDict.mBitsPerSecond.WasPassed() ? aInitDict.mBitsPerSecond.Value() : 0; + // We're not handling dynamic changes yet. Eventually we'll handle + // setting audio, video and/or total -- and anything that isn't set, + // we'll derive. Calculated versions require querying bitrates after + // the encoder is Init()ed. This happens only after data is + // available and thus requires dynamic changes. + // + // Until dynamic changes are supported, we'll be safe and err + // slightly high. + if (aInitDict.mBitsPerSecond.WasPassed() && !aInitDict.mVideoBitsPerSecond.WasPassed()) { + mVideoBitsPerSecond = mBitsPerSecond; + } } nsresult diff --git a/dom/media/MediaRecorder.h b/dom/media/MediaRecorder.h index 49a1affeef..a4b74835f5 100644 --- a/dom/media/MediaRecorder.h +++ b/dom/media/MediaRecorder.h @@ -104,6 +104,9 @@ public: NS_DECL_NSIDOCUMENTACTIVITY + uint32_t GetAudioBitrate() { return mAudioBitsPerSecond; } + uint32_t GetVideoBitrate() { return mVideoBitsPerSecond; } + uint32_t GetBitrate() { return mBitsPerSecond; } protected: virtual ~MediaRecorder(); diff --git a/dom/media/encoder/MediaEncoder.cpp b/dom/media/encoder/MediaEncoder.cpp index ac0f2c7ade..23734c2f5e 100644 --- a/dom/media/encoder/MediaEncoder.cpp +++ b/dom/media/encoder/MediaEncoder.cpp @@ -77,7 +77,9 @@ MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph, /* static */ already_AddRefed -MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes) +MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate, + uint32_t aVideoBitrate, uint32_t aBitrate, + uint8_t aTrackTypes) { #ifdef PR_LOGGING if (!gMediaEncoderLog) { @@ -150,8 +152,15 @@ MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes) LOG(PR_LOG_DEBUG, ("Create encoder result:a[%d] v[%d] w[%d] mimeType = %s.", audioEncoder != nullptr, videoEncoder != nullptr, writer != nullptr, mimeType.get())); + if (videoEncoder && aVideoBitrate != 0) { + videoEncoder->SetBitrate(aVideoBitrate); + } + if (audioEncoder && aAudioBitrate != 0) { + audioEncoder->SetBitrate(aAudioBitrate); + } encoder = new MediaEncoder(writer.forget(), audioEncoder.forget(), - videoEncoder.forget(), mimeType); + videoEncoder.forget(), mimeType, aAudioBitrate, + aVideoBitrate, aBitrate); return encoder.forget(); } diff --git a/dom/media/encoder/MediaEncoder.h b/dom/media/encoder/MediaEncoder.h index e8b6ecd823..13faa838be 100644 --- a/dom/media/encoder/MediaEncoder.h +++ b/dom/media/encoder/MediaEncoder.h @@ -62,7 +62,10 @@ public : MediaEncoder(ContainerWriter* aWriter, AudioTrackEncoder* aAudioEncoder, VideoTrackEncoder* aVideoEncoder, - const nsAString& aMIMEType) + const nsAString& aMIMEType, + uint32_t aAudioBitrate, + uint32_t aVideoBitrate, + uint32_t aBitrate) : mWriter(aWriter) , mAudioEncoder(aAudioEncoder) , mVideoEncoder(aVideoEncoder) @@ -96,6 +99,8 @@ public : * Ogg+Opus if it is empty. */ static already_AddRefed CreateEncoder(const nsAString& aMIMEType, + uint32_t aAudioBitrate, uint32_t aVideoBitrate, + uint32_t aBitrate, uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK); /** * Encodes the raw track data and returns the final container data. Assuming diff --git a/dom/media/encoder/OpusTrackEncoder.cpp b/dom/media/encoder/OpusTrackEncoder.cpp index 9df18c0924..1974051629 100644 --- a/dom/media/encoder/OpusTrackEncoder.cpp +++ b/dom/media/encoder/OpusTrackEncoder.cpp @@ -189,6 +189,10 @@ OpusTrackEncoder::Init(int aChannels, int aSamplingRate) mInitialized = (error == OPUS_OK); + if (mAudioBitrate) { + opus_encoder_ctl(mEncoder, OPUS_SET_BITRATE(static_cast(mAudioBitrate))); + } + mReentrantMonitor.NotifyAll(); return error == OPUS_OK ? NS_OK : NS_ERROR_FAILURE; diff --git a/dom/media/encoder/TrackEncoder.h b/dom/media/encoder/TrackEncoder.h index fc186e7f4c..c7f8b7a975 100644 --- a/dom/media/encoder/TrackEncoder.h +++ b/dom/media/encoder/TrackEncoder.h @@ -84,6 +84,8 @@ public: mReentrantMonitor.NotifyAll(); } + virtual void SetBitrate(const uint32_t aBitrate) {} + protected: /** * Notifies track encoder that we have reached the end of source stream, and @@ -143,6 +145,7 @@ public: : TrackEncoder() , mChannels(0) , mSamplingRate(0) + , mAudioBitrate(0) {} virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, @@ -171,6 +174,10 @@ public: */ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + virtual void SetBitrate(const uint32_t aBitrate) override + { + mAudioBitrate = aBitrate; + } protected: /** * Number of samples per channel in a pcm buffer. This is also the value of @@ -219,6 +226,8 @@ protected: * A segment queue of audio track data, protected by mReentrantMonitor. */ AudioSegment mRawSegment; + + uint32_t mAudioBitrate; }; class VideoTrackEncoder : public TrackEncoder @@ -232,6 +241,7 @@ public: , mDisplayHeight(0) , mTrackRate(0) , mTotalFrameDuration(0) + , mVideoBitrate(0) {} /** @@ -247,6 +257,10 @@ public: */ size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const; + virtual void SetBitrate(const uint32_t aBitrate) override + { + mVideoBitrate = aBitrate; + } protected: /** * Initialized the video encoder. In order to collect the value of width and @@ -312,6 +326,8 @@ protected: * A segment queue of audio track data, protected by mReentrantMonitor. */ VideoSegment mRawSegment; + + uint32_t mVideoBitrate; }; } diff --git a/dom/media/encoder/VP8TrackEncoder.cpp b/dom/media/encoder/VP8TrackEncoder.cpp index a498ed4ee8..49923217de 100644 --- a/dom/media/encoder/VP8TrackEncoder.cpp +++ b/dom/media/encoder/VP8TrackEncoder.cpp @@ -24,7 +24,7 @@ PRLogModuleInfo* gVP8TrackEncoderLog; #define VP8LOG(msg, ...) #endif -#define DEFAULT_BITRATE 2500 // in kbit/s +#define DEFAULT_BITRATE_BPS 2500000 #define DEFAULT_ENCODE_FRAMERATE 30 using namespace mozilla::layers; @@ -93,7 +93,9 @@ VP8TrackEncoder::Init(int32_t aWidth, int32_t aHeight, int32_t aDisplayWidth, config.g_h = mFrameHeight; // TODO: Maybe we should have various aFrameRate bitrate pair for each devices? // or for different platform - config.rc_target_bitrate = DEFAULT_BITRATE; // in kbit/s + + // rc_target_bitrate needs kbit/s + config.rc_target_bitrate = (mVideoBitrate != 0 ? mVideoBitrate : DEFAULT_BITRATE_BPS)/1000; // Setting the time base of the codec config.g_timebase.num = 1; diff --git a/dom/media/encoder/VorbisTrackEncoder.cpp b/dom/media/encoder/VorbisTrackEncoder.cpp index 552ad6471b..b2ec8f522c 100644 --- a/dom/media/encoder/VorbisTrackEncoder.cpp +++ b/dom/media/encoder/VorbisTrackEncoder.cpp @@ -62,9 +62,12 @@ VorbisTrackEncoder::Init(int aChannels, int aSamplingRate) int ret = 0; vorbis_info_init(&mVorbisInfo); + double quality = mAudioBitrate ? (double)mAudioBitrate/aSamplingRate : + BASE_QUALITY; + printf("quality %f \n", quality); ret = vorbis_encode_init_vbr(&mVorbisInfo, mChannels, mSamplingRate, - BASE_QUALITY); + quality); mInitialized = (ret == 0); diff --git a/dom/webidl/MediaRecorder.webidl b/dom/webidl/MediaRecorder.webidl index d3caca1cc3..9202400a35 100644 --- a/dom/webidl/MediaRecorder.webidl +++ b/dom/webidl/MediaRecorder.webidl @@ -51,4 +51,7 @@ interface MediaRecorder : EventTarget { dictionary MediaRecorderOptions { DOMString mimeType = ""; // Default encoding mimeType. + unsigned long audioBitsPerSecond; + unsigned long videoBitsPerSecond; + unsigned long bitsPerSecond; };