| File: | root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp |
| Warning: | line 401, column 5 Value stored to 'result' is never read |
Press '?' to see keyboard shortcuts
Keyboard shortcuts:
| 1 | /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ |
| 2 | /* This Source Code Form is subject to the terms of the Mozilla Public |
| 3 | * License, v. 2.0. If a copy of the MPL was not distributed with this file, |
| 4 | * You can obtain one at http://mozilla.org/MPL/2.0/. */ |
| 5 | #include "OpusTrackEncoder.h" |
| 6 | #include "nsString.h" |
| 7 | #include "mozilla/CheckedInt.h" |
| 8 | #include "mozilla/ProfilerLabels.h" |
| 9 | #include "VideoUtils.h" |
| 10 | |
| 11 | #include <opus/opus.h> |
| 12 | |
| 13 | #define LOG(args, ...) |
| 14 | |
| 15 | namespace mozilla { |
| 16 | |
| 17 | // The Opus format supports up to 8 channels, and supports multitrack audio up |
| 18 | // to 255 channels, but the current implementation supports only mono and |
| 19 | // stereo, and downmixes any more than that. |
| 20 | constexpr int MAX_SUPPORTED_AUDIO_CHANNELS = 8; |
| 21 | |
| 22 | // http://www.opus-codec.org/docs/html_api-1.0.2/group__opus__encoder.html |
| 23 | // In section "opus_encoder_init", channels must be 1 or 2 of input signal. |
| 24 | constexpr int MAX_CHANNELS = 2; |
| 25 | |
| 26 | // A maximum data bytes for Opus to encode. |
| 27 | constexpr int MAX_DATA_BYTES = 4096; |
| 28 | |
| 29 | // http://tools.ietf.org/html/draft-ietf-codec-oggopus-00#section-4 |
| 30 | // Second paragraph, " The granule position of an audio data page is in units |
| 31 | // of PCM audio samples at a fixed rate of 48 kHz." |
| 32 | constexpr int kOpusSamplingRate = 48000; |
| 33 | |
| 34 | // The duration of an Opus frame, and it must be 2.5, 5, 10, 20, 40 or 60 ms. |
| 35 | constexpr int kFrameDurationMs = 20; |
| 36 | |
| 37 | // The supported sampling rate of input signal (Hz), |
| 38 | // must be one of the following. Will resampled to 48kHz otherwise. |
| 39 | constexpr int kOpusSupportedInputSamplingRates[] = {8000, 12000, 16000, 24000, |
| 40 | 48000}; |
| 41 | |
| 42 | namespace { |
| 43 | |
| 44 | // An endian-neutral serialization of integers. Serializing T in little endian |
| 45 | // format to aOutput, where T is a 16 bits or 32 bits integer. |
| 46 | template <typename T> |
| 47 | static void SerializeToBuffer(T aValue, nsTArray<uint8_t>* aOutput) { |
| 48 | for (uint32_t i = 0; i < sizeof(T); i++) { |
| 49 | aOutput->AppendElement((uint8_t)(0x000000ff & (aValue >> (i * 8)))); |
| 50 | } |
| 51 | } |
| 52 | |
| 53 | static inline void SerializeToBuffer(const nsCString& aComment, |
| 54 | nsTArray<uint8_t>* aOutput) { |
| 55 | // Format of serializing a string to buffer is, the length of string (32 bits, |
| 56 | // little endian), and the string. |
| 57 | SerializeToBuffer((uint32_t)(aComment.Length()), aOutput); |
| 58 | aOutput->AppendElements(aComment.get(), aComment.Length()); |
| 59 | } |
| 60 | |
| 61 | static void SerializeOpusIdHeader(uint8_t aChannelCount, uint16_t aPreskip, |
| 62 | uint32_t aInputSampleRate, |
| 63 | nsTArray<uint8_t>* aOutput) { |
| 64 | // The magic signature, null terminator has to be stripped off from strings. |
| 65 | constexpr uint8_t magic[] = "OpusHead"; |
| 66 | aOutput->AppendElements(magic, sizeof(magic) - 1); |
| 67 | |
| 68 | // The version must always be 1 (8 bits, unsigned). |
| 69 | aOutput->AppendElement(1); |
| 70 | |
| 71 | // Number of output channels (8 bits, unsigned). |
| 72 | aOutput->AppendElement(aChannelCount); |
| 73 | |
| 74 | // Number of samples (at 48 kHz) to discard from the decoder output when |
| 75 | // starting playback (16 bits, unsigned, little endian). |
| 76 | SerializeToBuffer(aPreskip, aOutput); |
| 77 | |
| 78 | // The sampling rate of input source (32 bits, unsigned, little endian). |
| 79 | SerializeToBuffer(aInputSampleRate, aOutput); |
| 80 | |
| 81 | // Output gain, an encoder should set this field to zero (16 bits, signed, |
| 82 | // little endian). |
| 83 | SerializeToBuffer((int16_t)0, aOutput); |
| 84 | |
| 85 | // Channel mapping family. Family 0 allows only 1 or 2 channels (8 bits, |
| 86 | // unsigned). |
| 87 | aOutput->AppendElement(0); |
| 88 | } |
| 89 | |
| 90 | static void SerializeOpusCommentHeader(const nsCString& aVendor, |
| 91 | const nsTArray<nsCString>& aComments, |
| 92 | nsTArray<uint8_t>* aOutput) { |
| 93 | // The magic signature, null terminator has to be stripped off. |
| 94 | constexpr uint8_t magic[] = "OpusTags"; |
| 95 | aOutput->AppendElements(magic, sizeof(magic) - 1); |
| 96 | |
| 97 | // The vendor; Should append in the following order: |
| 98 | // vendor string length (32 bits, unsigned, little endian) |
| 99 | // vendor string. |
| 100 | SerializeToBuffer(aVendor, aOutput); |
| 101 | |
| 102 | // Add comments; Should append in the following order: |
| 103 | // comment list length (32 bits, unsigned, little endian) |
| 104 | // comment #0 string length (32 bits, unsigned, little endian) |
| 105 | // comment #0 string |
| 106 | // comment #1 string length (32 bits, unsigned, little endian) |
| 107 | // comment #1 string ... |
| 108 | SerializeToBuffer((uint32_t)aComments.Length(), aOutput); |
| 109 | for (uint32_t i = 0; i < aComments.Length(); ++i) { |
| 110 | SerializeToBuffer(aComments[i], aOutput); |
| 111 | } |
| 112 | } |
| 113 | |
| 114 | bool IsSampleRateSupported(TrackRate aSampleRate) { |
| 115 | // According to www.opus-codec.org, creating an opus encoder requires the |
| 116 | // sampling rate of source signal be one of 8000, 12000, 16000, 24000, or |
| 117 | // 48000. If this constraint is not satisfied, we resample the input to 48kHz. |
| 118 | AutoTArray<int, 5> supportedSamplingRates; |
| 119 | supportedSamplingRates.AppendElements( |
| 120 | kOpusSupportedInputSamplingRates, |
| 121 | std::size(kOpusSupportedInputSamplingRates)); |
| 122 | return supportedSamplingRates.Contains(aSampleRate); |
| 123 | } |
| 124 | |
| 125 | } // Anonymous namespace. |
| 126 | |
| 127 | OpusTrackEncoder::OpusTrackEncoder(TrackRate aTrackRate, |
| 128 | MediaQueue<EncodedFrame>& aEncodedDataQueue) |
| 129 | : AudioTrackEncoder(aTrackRate, aEncodedDataQueue), |
| 130 | mOutputSampleRate(IsSampleRateSupported(aTrackRate) ? aTrackRate |
| 131 | : kOpusSamplingRate), |
| 132 | mEncoder(nullptr), |
| 133 | mLookahead(0), |
| 134 | mLookaheadWritten(0), |
| 135 | mResampler(nullptr), |
| 136 | mNumOutputFrames(0) {} |
| 137 | |
| 138 | OpusTrackEncoder::~OpusTrackEncoder() { |
| 139 | if (mEncoder) { |
| 140 | opus_encoder_destroy(mEncoder); |
| 141 | } |
| 142 | if (mResampler) { |
| 143 | speex_resampler_destroymoz_speex_resampler_destroy(mResampler); |
| 144 | mResampler = nullptr; |
| 145 | } |
| 146 | } |
| 147 | |
| 148 | nsresult OpusTrackEncoder::Init(int aChannels) { |
| 149 | NS_ENSURE_TRUE((aChannels <= MAX_SUPPORTED_AUDIO_CHANNELS) && (aChannels > 0),do { if ((__builtin_expect(!!(!((aChannels <= MAX_SUPPORTED_AUDIO_CHANNELS ) && (aChannels > 0))), 0))) { NS_DebugBreak(NS_DEBUG_WARNING , "NS_ENSURE_TRUE(" "(aChannels <= MAX_SUPPORTED_AUDIO_CHANNELS) && (aChannels > 0)" ") failed", nullptr, "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 150); return NS_ERROR_FAILURE; } } while (false) |
| 150 | NS_ERROR_FAILURE)do { if ((__builtin_expect(!!(!((aChannels <= MAX_SUPPORTED_AUDIO_CHANNELS ) && (aChannels > 0))), 0))) { NS_DebugBreak(NS_DEBUG_WARNING , "NS_ENSURE_TRUE(" "(aChannels <= MAX_SUPPORTED_AUDIO_CHANNELS) && (aChannels > 0)" ") failed", nullptr, "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 150); return NS_ERROR_FAILURE; } } while (false); |
| 151 | |
| 152 | // This version of encoder API only support 1 or 2 channels, |
| 153 | // So set the mChannels less or equal 2 and |
| 154 | // let InterleaveTrackData downmix pcm data. |
| 155 | mChannels = aChannels > MAX_CHANNELS ? MAX_CHANNELS : aChannels; |
| 156 | |
| 157 | // Reject non-audio sample rates. |
| 158 | NS_ENSURE_TRUE(mTrackRate >= 8000, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(mTrackRate >= 8000)), 0))) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mTrackRate >= 8000" ") failed", nullptr, "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 158); return NS_ERROR_INVALID_ARG; } } while (false); |
| 159 | NS_ENSURE_TRUE(mTrackRate <= 192000, NS_ERROR_INVALID_ARG)do { if ((__builtin_expect(!!(!(mTrackRate <= 192000)), 0) )) { NS_DebugBreak(NS_DEBUG_WARNING, "NS_ENSURE_TRUE(" "mTrackRate <= 192000" ") failed", nullptr, "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 159); return NS_ERROR_INVALID_ARG; } } while (false); |
| 160 | |
| 161 | if (NeedsResampler()) { |
| 162 | int error; |
| 163 | mResampler = speex_resampler_initmoz_speex_resampler_init(mChannels, mTrackRate, kOpusSamplingRate, |
| 164 | SPEEX_RESAMPLER_QUALITY_DEFAULT4, &error); |
| 165 | |
| 166 | if (error != RESAMPLER_ERR_SUCCESS) { |
| 167 | return NS_ERROR_FAILURE; |
| 168 | } |
| 169 | } |
| 170 | |
| 171 | int error = 0; |
| 172 | mEncoder = opus_encoder_create(mOutputSampleRate, mChannels, |
| 173 | OPUS_APPLICATION_AUDIO2049, &error); |
| 174 | |
| 175 | if (error != OPUS_OK0) { |
| 176 | return NS_ERROR_FAILURE; |
| 177 | } |
| 178 | |
| 179 | if (mAudioBitrate) { |
| 180 | int bps = static_cast<int>( |
| 181 | std::min<uint32_t>(mAudioBitrate, std::numeric_limits<int>::max())); |
| 182 | error = opus_encoder_ctl(mEncoder, OPUS_SET_BITRATE(bps)4002, (((void)((bps) == (opus_int32)0)), (opus_int32)(bps))); |
| 183 | if (error != OPUS_OK0) { |
| 184 | return NS_ERROR_FAILURE; |
| 185 | } |
| 186 | } |
| 187 | |
| 188 | // In the case of Opus we need to calculate the codec delay based on the |
| 189 | // pre-skip. For more information see: |
| 190 | // https://tools.ietf.org/html/rfc7845#section-4.2 |
| 191 | error = opus_encoder_ctl(mEncoder, OPUS_GET_LOOKAHEAD(&mLookahead)4027, ((&mLookahead) + ((&mLookahead) - (opus_int32*) (&mLookahead)))); |
| 192 | if (error != OPUS_OK0) { |
| 193 | mLookahead = 0; |
| 194 | return NS_ERROR_FAILURE; |
| 195 | } |
| 196 | |
| 197 | SetInitialized(); |
| 198 | |
| 199 | return NS_OK; |
| 200 | } |
| 201 | |
| 202 | int OpusTrackEncoder::GetLookahead() const { |
| 203 | return mLookahead * kOpusSamplingRate / mOutputSampleRate; |
| 204 | } |
| 205 | |
| 206 | int OpusTrackEncoder::NumInputFramesPerPacket() const { |
| 207 | return mTrackRate * kFrameDurationMs / 1000; |
| 208 | } |
| 209 | |
| 210 | int OpusTrackEncoder::NumOutputFramesPerPacket() const { |
| 211 | return mOutputSampleRate * kFrameDurationMs / 1000; |
| 212 | } |
| 213 | |
| 214 | bool OpusTrackEncoder::NeedsResampler() const { |
| 215 | // A resampler is needed when mTrackRate is not supported by the opus encoder. |
| 216 | // This is equivalent to !IsSampleRateSupported(mTrackRate) but less cycles. |
| 217 | return mTrackRate != mOutputSampleRate && |
| 218 | mOutputSampleRate == kOpusSamplingRate; |
| 219 | } |
| 220 | |
| 221 | already_AddRefed<TrackMetadataBase> OpusTrackEncoder::GetMetadata() { |
| 222 | AUTO_PROFILER_LABEL("OpusTrackEncoder::GetMetadata", OTHER)mozilla::AutoProfilerLabel raiiObject222( "OpusTrackEncoder::GetMetadata" , nullptr, JS::ProfilingCategoryPair::OTHER); |
| 223 | |
| 224 | MOZ_ASSERT(mInitialized)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mInitialized)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mInitialized))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("mInitialized", "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 224); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mInitialized" ")"); do { MOZ_CrashSequence(__null, 224); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 225 | |
| 226 | if (!mInitialized) { |
| 227 | return nullptr; |
| 228 | } |
| 229 | |
| 230 | RefPtr<OpusMetadata> meta = new OpusMetadata(); |
| 231 | meta->mChannels = mChannels; |
| 232 | meta->mSamplingFrequency = mTrackRate; |
| 233 | |
| 234 | // Ogg and Webm timestamps are always sampled at 48k for Opus. |
| 235 | SerializeOpusIdHeader(mChannels, |
| 236 | mLookahead * (kOpusSamplingRate / mOutputSampleRate), |
| 237 | mTrackRate, &meta->mIdHeader); |
| 238 | |
| 239 | nsCString vendor; |
| 240 | vendor.AppendASCII(opus_get_version_string()); |
| 241 | |
| 242 | nsTArray<nsCString> comments; |
| 243 | comments.AppendElement( |
| 244 | nsLiteralCString("ENCODER=Mozilla" MOZ_APP_UA_VERSION"142.0a1")); |
| 245 | |
| 246 | SerializeOpusCommentHeader(vendor, comments, &meta->mCommentHeader); |
| 247 | |
| 248 | return meta.forget(); |
| 249 | } |
| 250 | |
| 251 | nsresult OpusTrackEncoder::Encode(AudioSegment* aSegment) { |
| 252 | AUTO_PROFILER_LABEL("OpusTrackEncoder::Encode", OTHER)mozilla::AutoProfilerLabel raiiObject252( "OpusTrackEncoder::Encode" , nullptr, JS::ProfilingCategoryPair::OTHER); |
| 253 | |
| 254 | MOZ_ASSERT(aSegment)do { static_assert( mozilla::detail::AssertionConditionType< decltype(aSegment)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(aSegment))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("aSegment", "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 254); AnnotateMozCrashReason("MOZ_ASSERT" "(" "aSegment" ")" ); do { MOZ_CrashSequence(__null, 254); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 255 | MOZ_ASSERT(mInitialized || mCanceled)do { static_assert( mozilla::detail::AssertionConditionType< decltype(mInitialized || mCanceled)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(mInitialized || mCanceled))) , 0))) { do { } while (false); MOZ_ReportAssertionFailure("mInitialized || mCanceled" , "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 255); AnnotateMozCrashReason("MOZ_ASSERT" "(" "mInitialized || mCanceled" ")"); do { MOZ_CrashSequence(__null, 255); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 256 | |
| 257 | if (mCanceled || IsEncodingComplete()) { |
| 258 | return NS_ERROR_FAILURE; |
| 259 | } |
| 260 | |
| 261 | if (!mInitialized) { |
| 262 | // calculation below depends on the truth that mInitialized is true. |
| 263 | return NS_ERROR_FAILURE; |
| 264 | } |
| 265 | |
| 266 | int result = 0; |
| 267 | // Loop until we run out of packets of input data |
| 268 | while (result >= 0 && !IsEncodingComplete()) { |
| 269 | // re-sampled frames left last time which didn't fit into an Opus packet |
| 270 | // duration. |
| 271 | const int framesLeft = mResampledLeftover.Length() / mChannels; |
| 272 | MOZ_ASSERT(NumOutputFramesPerPacket() >= framesLeft)do { static_assert( mozilla::detail::AssertionConditionType< decltype(NumOutputFramesPerPacket() >= framesLeft)>::isValid , "invalid assertion condition"); if ((__builtin_expect(!!(!( !!(NumOutputFramesPerPacket() >= framesLeft))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("NumOutputFramesPerPacket() >= framesLeft" , "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 272); AnnotateMozCrashReason("MOZ_ASSERT" "(" "NumOutputFramesPerPacket() >= framesLeft" ")"); do { MOZ_CrashSequence(__null, 272); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 273 | // Fetch input frames such that there will be n frames where (n + |
| 274 | // framesLeft) >= NumOutputFramesPerPacket() after re-sampling. |
| 275 | const int framesToFetch = NumInputFramesPerPacket() - |
| 276 | (framesLeft * mTrackRate / kOpusSamplingRate) + |
| 277 | (NeedsResampler() ? 1 : 0); |
| 278 | |
| 279 | if (!mEndOfStream && aSegment->GetDuration() < framesToFetch) { |
| 280 | // Not enough raw data |
| 281 | return NS_OK; |
| 282 | } |
| 283 | |
| 284 | // Start encoding data. |
| 285 | AutoTArray<AudioDataValue, 9600> pcm; |
| 286 | pcm.SetLength(NumOutputFramesPerPacket() * mChannels); |
| 287 | |
| 288 | int frameCopied = 0; |
| 289 | |
| 290 | for (AudioSegment::ChunkIterator iter(*aSegment); |
| 291 | !iter.IsEnded() && frameCopied < framesToFetch; iter.Next()) { |
| 292 | AudioChunk chunk = *iter; |
| 293 | |
| 294 | // Chunk to the required frame size. |
| 295 | TrackTime frameToCopy = |
| 296 | std::min(chunk.GetDuration(), |
| 297 | static_cast<TrackTime>(framesToFetch - frameCopied)); |
| 298 | |
| 299 | // Possible greatest value of framesToFetch = 3844: see |
| 300 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1349421#c8. frameToCopy |
| 301 | // should not be able to exceed this value. |
| 302 | MOZ_ASSERT(frameToCopy <= 3844, "frameToCopy exceeded expected range")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameToCopy <= 3844)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameToCopy <= 3844))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("frameToCopy <= 3844" " (" "frameToCopy exceeded expected range" ")", "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 302); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameToCopy <= 3844" ") (" "frameToCopy exceeded expected range" ")"); do { MOZ_CrashSequence (__null, 302); __attribute__((nomerge)) ::abort(); } while (false ); } } while (false); |
| 303 | |
| 304 | if (!chunk.IsNull()) { |
| 305 | // Append the interleaved data to the end of pcm buffer. |
| 306 | AudioTrackEncoder::InterleaveTrackData( |
| 307 | chunk, frameToCopy, mChannels, |
| 308 | pcm.Elements() + frameCopied * mChannels); |
| 309 | } else { |
| 310 | CheckedInt<int> memsetLength = |
| 311 | CheckedInt<int>(frameToCopy) * mChannels * sizeof(AudioDataValue); |
| 312 | if (!memsetLength.isValid()) { |
| 313 | // This should never happen, but we use a defensive check because |
| 314 | // we really don't want a bad memset |
| 315 | MOZ_ASSERT_UNREACHABLE("memsetLength invalid!")do { static_assert( mozilla::detail::AssertionConditionType< decltype(false)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(false))), 0))) { do { } while ( false); MOZ_ReportAssertionFailure("false" " (" "MOZ_ASSERT_UNREACHABLE: " "memsetLength invalid!" ")", "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 315); AnnotateMozCrashReason("MOZ_ASSERT" "(" "false" ") (" "MOZ_ASSERT_UNREACHABLE: " "memsetLength invalid!" ")"); do { MOZ_CrashSequence(__null, 315); __attribute__((nomerge)) ::abort (); } while (false); } } while (false); |
| 316 | return NS_ERROR_FAILURE; |
| 317 | } |
| 318 | memset(pcm.Elements() + frameCopied * mChannels, 0, |
| 319 | memsetLength.value()); |
| 320 | } |
| 321 | |
| 322 | frameCopied += frameToCopy; |
| 323 | } |
| 324 | |
| 325 | // Possible greatest value of framesToFetch = 3844: see |
| 326 | // https://bugzilla.mozilla.org/show_bug.cgi?id=1349421#c8. frameCopied |
| 327 | // should not be able to exceed this value. |
| 328 | MOZ_ASSERT(frameCopied <= 3844, "frameCopied exceeded expected range")do { static_assert( mozilla::detail::AssertionConditionType< decltype(frameCopied <= 3844)>::isValid, "invalid assertion condition" ); if ((__builtin_expect(!!(!(!!(frameCopied <= 3844))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("frameCopied <= 3844" " (" "frameCopied exceeded expected range" ")", "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 328); AnnotateMozCrashReason("MOZ_ASSERT" "(" "frameCopied <= 3844" ") (" "frameCopied exceeded expected range" ")"); do { MOZ_CrashSequence (__null, 328); __attribute__((nomerge)) ::abort(); } while (false ); } } while (false); |
| 329 | |
| 330 | int framesInPCM = frameCopied; |
| 331 | if (mResampler) { |
| 332 | AutoTArray<AudioDataValue, 9600> resamplingDest; |
| 333 | uint32_t inframes = frameCopied; |
| 334 | uint32_t outframes = inframes * kOpusSamplingRate / mTrackRate + 1; |
| 335 | |
| 336 | // We want to consume all the input data, so we slightly oversize the |
| 337 | // resampled data buffer so we can fit the output data in. We cannot |
| 338 | // really predict the output frame count at each call. |
| 339 | resamplingDest.SetLength(outframes * mChannels); |
| 340 | |
| 341 | float* in = reinterpret_cast<float*>(pcm.Elements()); |
| 342 | float* out = reinterpret_cast<float*>(resamplingDest.Elements()); |
| 343 | speex_resampler_process_interleaved_floatmoz_speex_resampler_process_interleaved_float(mResampler, in, &inframes, out, |
| 344 | &outframes); |
| 345 | |
| 346 | MOZ_ASSERT(pcm.Length() >= mResampledLeftover.Length())do { static_assert( mozilla::detail::AssertionConditionType< decltype(pcm.Length() >= mResampledLeftover.Length())>:: isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(pcm.Length() >= mResampledLeftover.Length()))), 0 ))) { do { } while (false); MOZ_ReportAssertionFailure("pcm.Length() >= mResampledLeftover.Length()" , "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 346); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pcm.Length() >= mResampledLeftover.Length()" ")"); do { MOZ_CrashSequence(__null, 346); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 347 | PodCopy(pcm.Elements(), mResampledLeftover.Elements(), |
| 348 | mResampledLeftover.Length()); |
| 349 | |
| 350 | uint32_t outframesToCopy = std::min( |
| 351 | outframes, |
| 352 | static_cast<uint32_t>(NumOutputFramesPerPacket() - framesLeft)); |
| 353 | |
| 354 | MOZ_ASSERT(pcm.Length() - mResampledLeftover.Length() >=do { static_assert( mozilla::detail::AssertionConditionType< decltype(pcm.Length() - mResampledLeftover.Length() >= outframesToCopy * mChannels)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(pcm.Length() - mResampledLeftover .Length() >= outframesToCopy * mChannels))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("pcm.Length() - mResampledLeftover.Length() >= outframesToCopy * mChannels" , "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pcm.Length() - mResampledLeftover.Length() >= outframesToCopy * mChannels" ")"); do { MOZ_CrashSequence(__null, 355); __attribute__((nomerge )) ::abort(); } while (false); } } while (false) |
| 355 | outframesToCopy * mChannels)do { static_assert( mozilla::detail::AssertionConditionType< decltype(pcm.Length() - mResampledLeftover.Length() >= outframesToCopy * mChannels)>::isValid, "invalid assertion condition"); if ((__builtin_expect(!!(!(!!(pcm.Length() - mResampledLeftover .Length() >= outframesToCopy * mChannels))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("pcm.Length() - mResampledLeftover.Length() >= outframesToCopy * mChannels" , "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 355); AnnotateMozCrashReason("MOZ_ASSERT" "(" "pcm.Length() - mResampledLeftover.Length() >= outframesToCopy * mChannels" ")"); do { MOZ_CrashSequence(__null, 355); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); |
| 356 | PodCopy(pcm.Elements() + mResampledLeftover.Length(), |
| 357 | resamplingDest.Elements(), outframesToCopy * mChannels); |
| 358 | int frameLeftover = outframes - outframesToCopy; |
| 359 | mResampledLeftover.SetLength(frameLeftover * mChannels); |
| 360 | PodCopy(mResampledLeftover.Elements(), |
| 361 | resamplingDest.Elements() + outframesToCopy * mChannels, |
| 362 | mResampledLeftover.Length()); |
| 363 | // This is always at 48000Hz. |
| 364 | framesInPCM = framesLeft + outframesToCopy; |
| 365 | } |
| 366 | |
| 367 | // Remove the raw data which has been pulled to pcm buffer. |
| 368 | // The value of frameCopied should be equal to (or smaller than, if eos) |
| 369 | // NumOutputFramesPerPacket(). |
| 370 | aSegment->RemoveLeading(frameCopied); |
| 371 | |
| 372 | // Has reached the end of input stream and all queued data has pulled for |
| 373 | // encoding. |
| 374 | bool isFinalPacket = false; |
| 375 | if (aSegment->GetDuration() == 0 && mEndOfStream && |
| 376 | framesInPCM < NumOutputFramesPerPacket()) { |
| 377 | // Pad |mLookahead| samples to the end of the track to prevent loss of |
| 378 | // original data. |
| 379 | const int toWrite = std::min(mLookahead - mLookaheadWritten, |
| 380 | NumOutputFramesPerPacket() - framesInPCM); |
| 381 | PodZero(pcm.Elements() + framesInPCM * mChannels, toWrite * mChannels); |
| 382 | mLookaheadWritten += toWrite; |
| 383 | framesInPCM += toWrite; |
| 384 | if (mLookaheadWritten == mLookahead) { |
| 385 | isFinalPacket = true; |
| 386 | } |
| 387 | } |
| 388 | |
| 389 | MOZ_ASSERT_IF(!isFinalPacket, framesInPCM == NumOutputFramesPerPacket())do { if (!isFinalPacket) { do { static_assert( mozilla::detail ::AssertionConditionType<decltype(framesInPCM == NumOutputFramesPerPacket ())>::isValid, "invalid assertion condition"); if ((__builtin_expect (!!(!(!!(framesInPCM == NumOutputFramesPerPacket()))), 0))) { do { } while (false); MOZ_ReportAssertionFailure("framesInPCM == NumOutputFramesPerPacket()" , "/root/firefox-clang/dom/media/encoder/OpusTrackEncoder.cpp" , 389); AnnotateMozCrashReason("MOZ_ASSERT" "(" "framesInPCM == NumOutputFramesPerPacket()" ")"); do { MOZ_CrashSequence(__null, 389); __attribute__((nomerge )) ::abort(); } while (false); } } while (false); } } while ( false); |
| 390 | |
| 391 | // Append null data to pcm buffer if the leftover data is not enough for |
| 392 | // opus encoder. |
| 393 | if (framesInPCM < NumOutputFramesPerPacket() && isFinalPacket) { |
| 394 | PodZero(pcm.Elements() + framesInPCM * mChannels, |
| 395 | (NumOutputFramesPerPacket() - framesInPCM) * mChannels); |
| 396 | } |
| 397 | auto frameData = MakeRefPtr<EncodedFrame::FrameData>(); |
| 398 | // Encode the data with Opus Encoder. |
| 399 | frameData->SetLength(MAX_DATA_BYTES); |
| 400 | // result is returned as opus error code if it is negative. |
| 401 | result = 0; |
Value stored to 'result' is never read | |
| 402 | const float* pcmBuf = static_cast<float*>(pcm.Elements()); |
| 403 | result = opus_encode_float(mEncoder, pcmBuf, NumOutputFramesPerPacket(), |
| 404 | frameData->Elements(), MAX_DATA_BYTES); |
| 405 | frameData->SetLength(result >= 0 ? result : 0); |
| 406 | |
| 407 | if (result < 0) { |
| 408 | LOG("[Opus] Fail to encode data! Result: %s.", opus_strerror(result)); |
| 409 | } |
| 410 | if (isFinalPacket) { |
| 411 | if (mResampler) { |
| 412 | speex_resampler_destroymoz_speex_resampler_destroy(mResampler); |
| 413 | mResampler = nullptr; |
| 414 | } |
| 415 | mResampledLeftover.SetLength(0); |
| 416 | } |
| 417 | |
| 418 | // timestamp should be the time of the first sample |
| 419 | mEncodedDataQueue.Push(MakeAndAddRef<EncodedFrame>( |
| 420 | media::TimeUnit(mNumOutputFrames + mLookahead, mOutputSampleRate), |
| 421 | static_cast<uint64_t>(framesInPCM) * kOpusSamplingRate / |
| 422 | mOutputSampleRate, |
| 423 | kOpusSamplingRate, EncodedFrame::OPUS_AUDIO_FRAME, |
| 424 | std::move(frameData))); |
| 425 | |
| 426 | mNumOutputFrames += NumOutputFramesPerPacket(); |
| 427 | LOG("[Opus] mOutputTimeStamp %.3f.", |
| 428 | media::TimeUnit(mNumOutputFrames, mOutputSampleRate).ToSeconds()); |
| 429 | |
| 430 | if (isFinalPacket) { |
| 431 | LOG("[Opus] Done encoding."); |
| 432 | mEncodedDataQueue.Finish(); |
| 433 | } |
| 434 | } |
| 435 | |
| 436 | return result >= 0 ? NS_OK : NS_ERROR_FAILURE; |
| 437 | } |
| 438 | |
| 439 | } // namespace mozilla |
| 440 | |
| 441 | #undef LOG |