зеркало из https://github.com/mozilla/gecko-dev.git
Backed out changeset 09d9b633e335 (bug 1294753)
This commit is contained in:
Родитель
a1568bf7f1
Коммит
612d7d1f4b
|
@ -308,19 +308,17 @@ MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
|
||||||
LOG(LogLevel::Debug, ("ENCODE_TRACK TimeStamp = %f", GetEncodeTimeStamp()));
|
LOG(LogLevel::Debug, ("ENCODE_TRACK TimeStamp = %f", GetEncodeTimeStamp()));
|
||||||
EncodedFrameContainer encodedData;
|
EncodedFrameContainer encodedData;
|
||||||
nsresult rv = NS_OK;
|
nsresult rv = NS_OK;
|
||||||
// We're most likely to actually wait for a video frame, so do that first to minimize
|
|
||||||
// capture offset/lipsync issues
|
|
||||||
rv = WriteEncodedDataToMuxer(mVideoEncoder.get());
|
|
||||||
if (NS_FAILED(rv)) {
|
|
||||||
LOG(LogLevel::Error, ("Fail to write video encoder data to muxer"));
|
|
||||||
break;
|
|
||||||
}
|
|
||||||
rv = WriteEncodedDataToMuxer(mAudioEncoder.get());
|
rv = WriteEncodedDataToMuxer(mAudioEncoder.get());
|
||||||
if (NS_FAILED(rv)) {
|
if (NS_FAILED(rv)) {
|
||||||
LOG(LogLevel::Error, ("Error! Fail to write audio encoder data to muxer"));
|
LOG(LogLevel::Error, ("Error! Fail to write audio encoder data to muxer"));
|
||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
LOG(LogLevel::Debug, ("Audio encoded TimeStamp = %f", GetEncodeTimeStamp()));
|
LOG(LogLevel::Debug, ("Audio encoded TimeStamp = %f", GetEncodeTimeStamp()));
|
||||||
|
rv = WriteEncodedDataToMuxer(mVideoEncoder.get());
|
||||||
|
if (NS_FAILED(rv)) {
|
||||||
|
LOG(LogLevel::Error, ("Fail to write video encoder data to muxer"));
|
||||||
|
break;
|
||||||
|
}
|
||||||
LOG(LogLevel::Debug, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
|
LOG(LogLevel::Debug, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
|
||||||
// In audio only or video only case, let unavailable track's flag to be true.
|
// In audio only or video only case, let unavailable track's flag to be true.
|
||||||
bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
|
bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
|
||||||
|
|
|
@ -275,187 +275,175 @@ OpusTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData)
|
||||||
// calculation below depends on the truth that mInitialized is true.
|
// calculation below depends on the truth that mInitialized is true.
|
||||||
MOZ_ASSERT(mInitialized);
|
MOZ_ASSERT(mInitialized);
|
||||||
|
|
||||||
bool wait = true;
|
// re-sampled frames left last time which didn't fit into an Opus packet duration.
|
||||||
int result = 0;
|
const int framesLeft = mResampledLeftover.Length() / mChannels;
|
||||||
// Only wait once, then loop until we run out of packets of input data
|
// When framesLeft is 0, (GetPacketDuration() - framesLeft) is a multiple
|
||||||
while (result >= 0) {
|
// of kOpusSamplingRate. There is not precision loss in the integer division
|
||||||
// re-sampled frames left last time which didn't fit into an Opus packet duration.
|
// in computing framesToFetch. If frameLeft > 0, we need to add 1 to
|
||||||
const int framesLeft = mResampledLeftover.Length() / mChannels;
|
// framesToFetch to ensure there will be at least n frames after re-sampling.
|
||||||
// When framesLeft is 0, (GetPacketDuration() - framesLeft) is a multiple
|
const int frameRoundUp = framesLeft ? 1 : 0;
|
||||||
// of kOpusSamplingRate. There is not precision loss in the integer division
|
|
||||||
// in computing framesToFetch. If frameLeft > 0, we need to add 1 to
|
|
||||||
// framesToFetch to ensure there will be at least n frames after re-sampling.
|
|
||||||
const int frameRoundUp = framesLeft ? 1 : 0;
|
|
||||||
|
|
||||||
MOZ_ASSERT(GetPacketDuration() >= framesLeft);
|
MOZ_ASSERT(GetPacketDuration() >= framesLeft);
|
||||||
// Try to fetch m frames such that there will be n frames
|
// Try to fetch m frames such that there will be n frames
|
||||||
// where (n + frameLeft) >= GetPacketDuration() after re-sampling.
|
// where (n + frameLeft) >= GetPacketDuration() after re-sampling.
|
||||||
const int framesToFetch = !mResampler ? GetPacketDuration()
|
const int framesToFetch = !mResampler ? GetPacketDuration()
|
||||||
: (GetPacketDuration() - framesLeft) * mSamplingRate / kOpusSamplingRate
|
: (GetPacketDuration() - framesLeft) * mSamplingRate / kOpusSamplingRate
|
||||||
+ frameRoundUp;
|
+ frameRoundUp;
|
||||||
{
|
{
|
||||||
// Move all the samples from mRawSegment to mSourceSegment. We only hold
|
// Move all the samples from mRawSegment to mSourceSegment. We only hold
|
||||||
// the monitor in this block.
|
// the monitor in this block.
|
||||||
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
ReentrantMonitorAutoEnter mon(mReentrantMonitor);
|
||||||
|
|
||||||
// Wait until enough raw data, end of stream or cancelled.
|
// Wait until enough raw data, end of stream or cancelled.
|
||||||
while (!mCanceled && mRawSegment.GetDuration() +
|
while (!mCanceled && mRawSegment.GetDuration() +
|
||||||
mSourceSegment.GetDuration() < framesToFetch &&
|
mSourceSegment.GetDuration() < framesToFetch &&
|
||||||
!mEndOfStream) {
|
!mEndOfStream) {
|
||||||
if (wait) {
|
mReentrantMonitor.Wait();
|
||||||
mReentrantMonitor.Wait();
|
|
||||||
wait = false;
|
|
||||||
} else {
|
|
||||||
goto done; // nested while's...
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if (mCanceled || mEncodingComplete) {
|
|
||||||
return NS_ERROR_FAILURE;
|
|
||||||
}
|
|
||||||
|
|
||||||
mSourceSegment.AppendFrom(&mRawSegment);
|
|
||||||
|
|
||||||
// Pad |mLookahead| samples to the end of source stream to prevent lost of
|
|
||||||
// original data, the pcm duration will be calculated at rate 48K later.
|
|
||||||
if (mEndOfStream && !mEosSetInEncoder) {
|
|
||||||
mEosSetInEncoder = true;
|
|
||||||
mSourceSegment.AppendNullData(mLookahead);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
// Start encoding data.
|
if (mCanceled || mEncodingComplete) {
|
||||||
AutoTArray<AudioDataValue, 9600> pcm;
|
return NS_ERROR_FAILURE;
|
||||||
pcm.SetLength(GetPacketDuration() * mChannels);
|
|
||||||
AudioSegment::ChunkIterator iter(mSourceSegment);
|
|
||||||
int frameCopied = 0;
|
|
||||||
|
|
||||||
while (!iter.IsEnded() && frameCopied < framesToFetch) {
|
|
||||||
AudioChunk chunk = *iter;
|
|
||||||
|
|
||||||
// Chunk to the required frame size.
|
|
||||||
int frameToCopy = chunk.GetDuration();
|
|
||||||
if (frameCopied + frameToCopy > framesToFetch) {
|
|
||||||
frameToCopy = framesToFetch - frameCopied;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!chunk.IsNull()) {
|
|
||||||
// Append the interleaved data to the end of pcm buffer.
|
|
||||||
AudioTrackEncoder::InterleaveTrackData(chunk, frameToCopy, mChannels,
|
|
||||||
pcm.Elements() + frameCopied * mChannels);
|
|
||||||
} else {
|
|
||||||
memset(pcm.Elements() + frameCopied * mChannels, 0,
|
|
||||||
frameToCopy * mChannels * sizeof(AudioDataValue));
|
|
||||||
}
|
|
||||||
|
|
||||||
frameCopied += frameToCopy;
|
|
||||||
iter.Next();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
RefPtr<EncodedFrame> audiodata = new EncodedFrame();
|
mSourceSegment.AppendFrom(&mRawSegment);
|
||||||
audiodata->SetFrameType(EncodedFrame::OPUS_AUDIO_FRAME);
|
|
||||||
int framesInPCM = frameCopied;
|
|
||||||
if (mResampler) {
|
|
||||||
AutoTArray<AudioDataValue, 9600> resamplingDest;
|
|
||||||
// We want to consume all the input data, so we slightly oversize the
|
|
||||||
// resampled data buffer so we can fit the output data in. We cannot really
|
|
||||||
// predict the output frame count at each call.
|
|
||||||
uint32_t outframes = frameCopied * kOpusSamplingRate / mSamplingRate + 1;
|
|
||||||
uint32_t inframes = frameCopied;
|
|
||||||
|
|
||||||
resamplingDest.SetLength(outframes * mChannels);
|
// Pad |mLookahead| samples to the end of source stream to prevent lost of
|
||||||
|
// original data, the pcm duration will be calculated at rate 48K later.
|
||||||
|
if (mEndOfStream && !mEosSetInEncoder) {
|
||||||
|
mEosSetInEncoder = true;
|
||||||
|
mSourceSegment.AppendNullData(mLookahead);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// Start encoding data.
|
||||||
|
AutoTArray<AudioDataValue, 9600> pcm;
|
||||||
|
pcm.SetLength(GetPacketDuration() * mChannels);
|
||||||
|
AudioSegment::ChunkIterator iter(mSourceSegment);
|
||||||
|
int frameCopied = 0;
|
||||||
|
|
||||||
|
while (!iter.IsEnded() && frameCopied < framesToFetch) {
|
||||||
|
AudioChunk chunk = *iter;
|
||||||
|
|
||||||
|
// Chunk to the required frame size.
|
||||||
|
int frameToCopy = chunk.GetDuration();
|
||||||
|
if (frameCopied + frameToCopy > framesToFetch) {
|
||||||
|
frameToCopy = framesToFetch - frameCopied;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (!chunk.IsNull()) {
|
||||||
|
// Append the interleaved data to the end of pcm buffer.
|
||||||
|
AudioTrackEncoder::InterleaveTrackData(chunk, frameToCopy, mChannels,
|
||||||
|
pcm.Elements() + frameCopied * mChannels);
|
||||||
|
} else {
|
||||||
|
memset(pcm.Elements() + frameCopied * mChannels, 0,
|
||||||
|
frameToCopy * mChannels * sizeof(AudioDataValue));
|
||||||
|
}
|
||||||
|
|
||||||
|
frameCopied += frameToCopy;
|
||||||
|
iter.Next();
|
||||||
|
}
|
||||||
|
|
||||||
|
RefPtr<EncodedFrame> audiodata = new EncodedFrame();
|
||||||
|
audiodata->SetFrameType(EncodedFrame::OPUS_AUDIO_FRAME);
|
||||||
|
int framesInPCM = frameCopied;
|
||||||
|
if (mResampler) {
|
||||||
|
AutoTArray<AudioDataValue, 9600> resamplingDest;
|
||||||
|
// We want to consume all the input data, so we slightly oversize the
|
||||||
|
// resampled data buffer so we can fit the output data in. We cannot really
|
||||||
|
// predict the output frame count at each call.
|
||||||
|
uint32_t outframes = frameCopied * kOpusSamplingRate / mSamplingRate + 1;
|
||||||
|
uint32_t inframes = frameCopied;
|
||||||
|
|
||||||
|
resamplingDest.SetLength(outframes * mChannels);
|
||||||
|
|
||||||
#if MOZ_SAMPLE_TYPE_S16
|
#if MOZ_SAMPLE_TYPE_S16
|
||||||
short* in = reinterpret_cast<short*>(pcm.Elements());
|
short* in = reinterpret_cast<short*>(pcm.Elements());
|
||||||
short* out = reinterpret_cast<short*>(resamplingDest.Elements());
|
short* out = reinterpret_cast<short*>(resamplingDest.Elements());
|
||||||
speex_resampler_process_interleaved_int(mResampler, in, &inframes,
|
speex_resampler_process_interleaved_int(mResampler, in, &inframes,
|
||||||
out, &outframes);
|
out, &outframes);
|
||||||
#else
|
#else
|
||||||
float* in = reinterpret_cast<float*>(pcm.Elements());
|
float* in = reinterpret_cast<float*>(pcm.Elements());
|
||||||
float* out = reinterpret_cast<float*>(resamplingDest.Elements());
|
float* out = reinterpret_cast<float*>(resamplingDest.Elements());
|
||||||
speex_resampler_process_interleaved_float(mResampler, in, &inframes,
|
speex_resampler_process_interleaved_float(mResampler, in, &inframes,
|
||||||
out, &outframes);
|
out, &outframes);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
MOZ_ASSERT(pcm.Length() >= mResampledLeftover.Length());
|
MOZ_ASSERT(pcm.Length() >= mResampledLeftover.Length());
|
||||||
PodCopy(pcm.Elements(), mResampledLeftover.Elements(),
|
PodCopy(pcm.Elements(), mResampledLeftover.Elements(),
|
||||||
mResampledLeftover.Length());
|
mResampledLeftover.Length());
|
||||||
|
|
||||||
uint32_t outframesToCopy = std::min(outframes,
|
uint32_t outframesToCopy = std::min(outframes,
|
||||||
static_cast<uint32_t>(GetPacketDuration() - framesLeft));
|
static_cast<uint32_t>(GetPacketDuration() - framesLeft));
|
||||||
|
|
||||||
MOZ_ASSERT(pcm.Length() - mResampledLeftover.Length() >=
|
MOZ_ASSERT(pcm.Length() - mResampledLeftover.Length() >=
|
||||||
outframesToCopy * mChannels);
|
outframesToCopy * mChannels);
|
||||||
PodCopy(pcm.Elements() + mResampledLeftover.Length(),
|
PodCopy(pcm.Elements() + mResampledLeftover.Length(),
|
||||||
resamplingDest.Elements(), outframesToCopy * mChannels);
|
resamplingDest.Elements(), outframesToCopy * mChannels);
|
||||||
int frameLeftover = outframes - outframesToCopy;
|
int frameLeftover = outframes - outframesToCopy;
|
||||||
mResampledLeftover.SetLength(frameLeftover * mChannels);
|
mResampledLeftover.SetLength(frameLeftover * mChannels);
|
||||||
PodCopy(mResampledLeftover.Elements(),
|
PodCopy(mResampledLeftover.Elements(),
|
||||||
resamplingDest.Elements() + outframesToCopy * mChannels,
|
resamplingDest.Elements() + outframesToCopy * mChannels,
|
||||||
mResampledLeftover.Length());
|
mResampledLeftover.Length());
|
||||||
// This is always at 48000Hz.
|
// This is always at 48000Hz.
|
||||||
framesInPCM = framesLeft + outframesToCopy;
|
framesInPCM = framesLeft + outframesToCopy;
|
||||||
audiodata->SetDuration(framesInPCM);
|
audiodata->SetDuration(framesInPCM);
|
||||||
} else {
|
} else {
|
||||||
// The ogg time stamping and pre-skip is always timed at 48000.
|
// The ogg time stamping and pre-skip is always timed at 48000.
|
||||||
audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate));
|
audiodata->SetDuration(frameCopied * (kOpusSamplingRate / mSamplingRate));
|
||||||
}
|
|
||||||
|
|
||||||
// Remove the raw data which has been pulled to pcm buffer.
|
|
||||||
// The value of frameCopied should equal to (or smaller than, if eos)
|
|
||||||
// GetPacketDuration().
|
|
||||||
mSourceSegment.RemoveLeading(frameCopied);
|
|
||||||
|
|
||||||
// Has reached the end of input stream and all queued data has pulled for
|
|
||||||
// encoding.
|
|
||||||
if (mSourceSegment.GetDuration() == 0 && mEndOfStream) {
|
|
||||||
mEncodingComplete = true;
|
|
||||||
LOG("[Opus] Done encoding.");
|
|
||||||
}
|
|
||||||
|
|
||||||
MOZ_ASSERT(mEndOfStream || framesInPCM == GetPacketDuration());
|
|
||||||
|
|
||||||
// Append null data to pcm buffer if the leftover data is not enough for
|
|
||||||
// opus encoder.
|
|
||||||
if (framesInPCM < GetPacketDuration() && mEndOfStream) {
|
|
||||||
PodZero(pcm.Elements() + framesInPCM * mChannels,
|
|
||||||
(GetPacketDuration() - framesInPCM) * mChannels);
|
|
||||||
}
|
|
||||||
nsTArray<uint8_t> frameData;
|
|
||||||
// Encode the data with Opus Encoder.
|
|
||||||
frameData.SetLength(MAX_DATA_BYTES);
|
|
||||||
// result is returned as opus error code if it is negative.
|
|
||||||
result = 0;
|
|
||||||
#ifdef MOZ_SAMPLE_TYPE_S16
|
|
||||||
const opus_int16* pcmBuf = static_cast<opus_int16*>(pcm.Elements());
|
|
||||||
result = opus_encode(mEncoder, pcmBuf, GetPacketDuration(),
|
|
||||||
frameData.Elements(), MAX_DATA_BYTES);
|
|
||||||
#else
|
|
||||||
const float* pcmBuf = static_cast<float*>(pcm.Elements());
|
|
||||||
result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(),
|
|
||||||
frameData.Elements(), MAX_DATA_BYTES);
|
|
||||||
#endif
|
|
||||||
frameData.SetLength(result >= 0 ? result : 0);
|
|
||||||
|
|
||||||
if (result < 0) {
|
|
||||||
LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
|
|
||||||
}
|
|
||||||
if (mEncodingComplete) {
|
|
||||||
if (mResampler) {
|
|
||||||
speex_resampler_destroy(mResampler);
|
|
||||||
mResampler = nullptr;
|
|
||||||
}
|
|
||||||
mResampledLeftover.SetLength(0);
|
|
||||||
}
|
|
||||||
|
|
||||||
audiodata->SwapInFrameData(frameData);
|
|
||||||
// timestamp should be the time of the first sample
|
|
||||||
audiodata->SetTimeStamp(mOutputTimeStamp);
|
|
||||||
mOutputTimeStamp += FramesToUsecs(GetPacketDuration(), kOpusSamplingRate).value();
|
|
||||||
LOG("[Opus] mOutputTimeStamp %lld.",mOutputTimeStamp);
|
|
||||||
aData.AppendEncodedFrame(audiodata);
|
|
||||||
}
|
}
|
||||||
done:
|
|
||||||
|
// Remove the raw data which has been pulled to pcm buffer.
|
||||||
|
// The value of frameCopied should equal to (or smaller than, if eos)
|
||||||
|
// GetPacketDuration().
|
||||||
|
mSourceSegment.RemoveLeading(frameCopied);
|
||||||
|
|
||||||
|
// Has reached the end of input stream and all queued data has pulled for
|
||||||
|
// encoding.
|
||||||
|
if (mSourceSegment.GetDuration() == 0 && mEndOfStream) {
|
||||||
|
mEncodingComplete = true;
|
||||||
|
LOG("[Opus] Done encoding.");
|
||||||
|
}
|
||||||
|
|
||||||
|
MOZ_ASSERT(mEndOfStream || framesInPCM == GetPacketDuration());
|
||||||
|
|
||||||
|
// Append null data to pcm buffer if the leftover data is not enough for
|
||||||
|
// opus encoder.
|
||||||
|
if (framesInPCM < GetPacketDuration() && mEndOfStream) {
|
||||||
|
PodZero(pcm.Elements() + framesInPCM * mChannels,
|
||||||
|
(GetPacketDuration() - framesInPCM) * mChannels);
|
||||||
|
}
|
||||||
|
nsTArray<uint8_t> frameData;
|
||||||
|
// Encode the data with Opus Encoder.
|
||||||
|
frameData.SetLength(MAX_DATA_BYTES);
|
||||||
|
// result is returned as opus error code if it is negative.
|
||||||
|
int result = 0;
|
||||||
|
#ifdef MOZ_SAMPLE_TYPE_S16
|
||||||
|
const opus_int16* pcmBuf = static_cast<opus_int16*>(pcm.Elements());
|
||||||
|
result = opus_encode(mEncoder, pcmBuf, GetPacketDuration(),
|
||||||
|
frameData.Elements(), MAX_DATA_BYTES);
|
||||||
|
#else
|
||||||
|
const float* pcmBuf = static_cast<float*>(pcm.Elements());
|
||||||
|
result = opus_encode_float(mEncoder, pcmBuf, GetPacketDuration(),
|
||||||
|
frameData.Elements(), MAX_DATA_BYTES);
|
||||||
|
#endif
|
||||||
|
frameData.SetLength(result >= 0 ? result : 0);
|
||||||
|
|
||||||
|
if (result < 0) {
|
||||||
|
LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result));
|
||||||
|
}
|
||||||
|
if (mEncodingComplete) {
|
||||||
|
if (mResampler) {
|
||||||
|
speex_resampler_destroy(mResampler);
|
||||||
|
mResampler = nullptr;
|
||||||
|
}
|
||||||
|
mResampledLeftover.SetLength(0);
|
||||||
|
}
|
||||||
|
|
||||||
|
audiodata->SwapInFrameData(frameData);
|
||||||
|
mOutputTimeStamp += FramesToUsecs(GetPacketDuration(), kOpusSamplingRate).value();
|
||||||
|
audiodata->SetTimeStamp(mOutputTimeStamp);
|
||||||
|
LOG("[Opus] mOutputTimeStamp %lld.",mOutputTimeStamp);
|
||||||
|
aData.AppendEncodedFrame(audiodata);
|
||||||
return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
|
return result >= 0 ? NS_OK : NS_ERROR_FAILURE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Загрузка…
Ссылка в новой задаче