1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/encoder/VorbisTrackEncoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,238 @@ 1.4 +/* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/ 1.5 +/* This Source Code Form is subject to the terms of the Mozilla Public 1.6 + * License, v. 2.0. If a copy of the MPL was not distributed with this file, 1.7 + * You can obtain one at http://mozilla.org/MPL/2.0/. */ 1.8 +#include "VorbisTrackEncoder.h" 1.9 +#include <ogg/ogg.h> 1.10 +#include <vorbis/vorbisenc.h> 1.11 +#include "WebMWriter.h" 1.12 + 1.13 +// One actually used: Encoding using a VBR quality mode. The usable range is -.1 1.14 +// (lowest quality, smallest file) to 1. (highest quality, largest file). 1.15 +// Example quality mode .4: 44kHz stereo coupled, roughly 128kbps VBR 1.16 +// ret = vorbis_encode_init_vbr(&vi,2,44100,.4); 1.17 +static const float BASE_QUALITY = 0.4f; 1.18 + 1.19 +namespace mozilla { 1.20 + 1.21 +#undef LOG 1.22 +#ifdef PR_LOGGING 1.23 +PRLogModuleInfo* gVorbisTrackEncoderLog; 1.24 +#define VORBISLOG(msg, ...) PR_LOG(gVorbisTrackEncoderLog, PR_LOG_DEBUG, \ 1.25 + (msg, ##__VA_ARGS__)) 1.26 +#else 1.27 +#define VORBISLOG(msg, ...) 1.28 +#endif 1.29 + 1.30 +VorbisTrackEncoder::VorbisTrackEncoder() 1.31 + : AudioTrackEncoder() 1.32 +{ 1.33 + MOZ_COUNT_CTOR(VorbisTrackEncoder); 1.34 +#ifdef PR_LOGGING 1.35 + if (!gVorbisTrackEncoderLog) { 1.36 + gVorbisTrackEncoderLog = PR_NewLogModule("VorbisTrackEncoder"); 1.37 + } 1.38 +#endif 1.39 +} 1.40 + 1.41 +VorbisTrackEncoder::~VorbisTrackEncoder() 1.42 +{ 1.43 + MOZ_COUNT_DTOR(VorbisTrackEncoder); 1.44 + if (mInitialized) { 1.45 + vorbis_block_clear(&mVorbisBlock); 1.46 + vorbis_dsp_clear(&mVorbisDsp); 1.47 + vorbis_info_clear(&mVorbisInfo); 1.48 + } 1.49 +} 1.50 + 1.51 +nsresult 1.52 +VorbisTrackEncoder::Init(int aChannels, int aSamplingRate) 1.53 +{ 1.54 + if (aChannels <= 0 || aChannels > 8) { 1.55 + VORBISLOG("aChannels <= 0 || aChannels > 8"); 1.56 + return NS_ERROR_INVALID_ARG; 1.57 + } 1.58 + 1.59 + // This monitor is used to wake up other methods that are waiting for encoder 1.60 + // to be completely initialized. 1.61 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.62 + mChannels = aChannels; 1.63 + mSamplingRate = aSamplingRate; 1.64 + 1.65 + int ret = 0; 1.66 + vorbis_info_init(&mVorbisInfo); 1.67 + 1.68 + ret = vorbis_encode_init_vbr(&mVorbisInfo, mChannels, mSamplingRate, 1.69 + BASE_QUALITY); 1.70 + 1.71 + mInitialized = (ret == 0); 1.72 + 1.73 + if (mInitialized) { 1.74 + // Set up the analysis state and auxiliary encoding storage 1.75 + vorbis_analysis_init(&mVorbisDsp, &mVorbisInfo); 1.76 + vorbis_block_init(&mVorbisDsp, &mVorbisBlock); 1.77 + } 1.78 + 1.79 + mon.NotifyAll(); 1.80 + 1.81 + return ret == 0 ? NS_OK : NS_ERROR_FAILURE; 1.82 +} 1.83 + 1.84 +void VorbisTrackEncoder::WriteLacing(nsTArray<uint8_t> *aOutput, int32_t aLacing) 1.85 +{ 1.86 + while (aLacing >= 255) { 1.87 + aLacing -= 255; 1.88 + aOutput->AppendElement(255); 1.89 + } 1.90 + aOutput->AppendElement((uint8_t)aLacing); 1.91 +} 1.92 + 1.93 +already_AddRefed<TrackMetadataBase> 1.94 +VorbisTrackEncoder::GetMetadata() 1.95 +{ 1.96 + { 1.97 + // Wait if encoder is not initialized. 1.98 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.99 + while (!mCanceled && !mInitialized) { 1.100 + mon.Wait(); 1.101 + } 1.102 + } 1.103 + 1.104 + if (mCanceled || mEncodingComplete) { 1.105 + return nullptr; 1.106 + } 1.107 + 1.108 + // Vorbis codec specific data 1.109 + // http://matroska.org/technical/specs/codecid/index.html 1.110 + nsRefPtr<VorbisMetadata> meta = new VorbisMetadata(); 1.111 + meta->mBitDepth = 32; // float for desktop 1.112 + meta->mChannels = mChannels; 1.113 + meta->mSamplingFrequency = mSamplingRate; 1.114 + ogg_packet header; 1.115 + ogg_packet header_comm; 1.116 + ogg_packet header_code; 1.117 + // Add comment 1.118 + vorbis_comment vorbisComment; 1.119 + vorbis_comment_init(&vorbisComment); 1.120 + vorbis_comment_add_tag(&vorbisComment, "ENCODER", 1.121 + NS_LITERAL_CSTRING("Mozilla VorbisTrackEncoder " MOZ_APP_UA_VERSION).get()); 1.122 + vorbis_analysis_headerout(&mVorbisDsp, &vorbisComment, 1.123 + &header,&header_comm, &header_code); 1.124 + vorbis_comment_clear(&vorbisComment); 1.125 + // number of distinct packets - 1 1.126 + meta->mData.AppendElement(2); 1.127 + // Xiph-style lacing header.bytes, header_comm.bytes 1.128 + WriteLacing(&(meta->mData), header.bytes); 1.129 + WriteLacing(&(meta->mData), header_comm.bytes); 1.130 + 1.131 + // Append the three packets 1.132 + meta->mData.AppendElements(header.packet, header.bytes); 1.133 + meta->mData.AppendElements(header_comm.packet, header_comm.bytes); 1.134 + meta->mData.AppendElements(header_code.packet, header_code.bytes); 1.135 + 1.136 + return meta.forget(); 1.137 +} 1.138 + 1.139 +void 1.140 +VorbisTrackEncoder::GetEncodedFrames(EncodedFrameContainer& aData) 1.141 +{ 1.142 + // vorbis does some data preanalysis, then divvies up blocks for 1.143 + // more involved (potentially parallel) processing. Get a single 1.144 + // block for encoding now. 1.145 + while (vorbis_analysis_blockout(&mVorbisDsp, &mVorbisBlock) == 1) { 1.146 + ogg_packet oggPacket; 1.147 + if (vorbis_analysis(&mVorbisBlock, &oggPacket) == 0) { 1.148 + VORBISLOG("vorbis_analysis_blockout block size %d", oggPacket.bytes); 1.149 + EncodedFrame* audiodata = new EncodedFrame(); 1.150 + audiodata->SetFrameType(EncodedFrame::VORBIS_AUDIO_FRAME); 1.151 + nsTArray<uint8_t> frameData; 1.152 + frameData.AppendElements(oggPacket.packet, oggPacket.bytes); 1.153 + audiodata->SwapInFrameData(frameData); 1.154 + aData.AppendEncodedFrame(audiodata); 1.155 + } 1.156 + } 1.157 +} 1.158 + 1.159 +nsresult 1.160 +VorbisTrackEncoder::GetEncodedTrack(EncodedFrameContainer& aData) 1.161 +{ 1.162 + if (mEosSetInEncoder) { 1.163 + return NS_OK; 1.164 + } 1.165 + 1.166 + nsAutoPtr<AudioSegment> sourceSegment; 1.167 + sourceSegment = new AudioSegment(); 1.168 + { 1.169 + // Move all the samples from mRawSegment to sourceSegment. We only hold 1.170 + // the monitor in this block. 1.171 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.172 + 1.173 + // Wait if mEncoder is not initialized, or when not enough raw data, but is 1.174 + // not the end of stream nor is being canceled. 1.175 + while (!mCanceled && mRawSegment.GetDuration() < GetPacketDuration() && 1.176 + !mEndOfStream) { 1.177 + mon.Wait(); 1.178 + } 1.179 + VORBISLOG("GetEncodedTrack passes wait, duration is %lld\n", 1.180 + mRawSegment.GetDuration()); 1.181 + if (mCanceled || mEncodingComplete) { 1.182 + return NS_ERROR_FAILURE; 1.183 + } 1.184 + 1.185 + sourceSegment->AppendFrom(&mRawSegment); 1.186 + } 1.187 + 1.188 + if (mEndOfStream && (sourceSegment->GetDuration() == 0) 1.189 + && !mEosSetInEncoder) { 1.190 + mEncodingComplete = true; 1.191 + mEosSetInEncoder = true; 1.192 + VORBISLOG("[Vorbis] Done encoding."); 1.193 + vorbis_analysis_wrote(&mVorbisDsp, 0); 1.194 + GetEncodedFrames(aData); 1.195 + 1.196 + return NS_OK; 1.197 + } 1.198 + 1.199 + // Start encoding data. 1.200 + AudioSegment::ChunkIterator iter(*sourceSegment); 1.201 + 1.202 + AudioDataValue **vorbisBuffer = 1.203 + vorbis_analysis_buffer(&mVorbisDsp, (int)sourceSegment->GetDuration()); 1.204 + 1.205 + int framesCopied = 0; 1.206 + nsAutoTArray<AudioDataValue, 9600> interleavedPcm; 1.207 + nsAutoTArray<AudioDataValue, 9600> nonInterleavedPcm; 1.208 + interleavedPcm.SetLength(sourceSegment->GetDuration() * mChannels); 1.209 + nonInterleavedPcm.SetLength(sourceSegment->GetDuration() * mChannels); 1.210 + while (!iter.IsEnded()) { 1.211 + AudioChunk chunk = *iter; 1.212 + int frameToCopy = chunk.GetDuration(); 1.213 + if (!chunk.IsNull()) { 1.214 + InterleaveTrackData(chunk, frameToCopy, mChannels, 1.215 + interleavedPcm.Elements() + framesCopied * mChannels); 1.216 + } else { // empty data 1.217 + memset(interleavedPcm.Elements() + framesCopied * mChannels, 0, 1.218 + frameToCopy * mChannels * sizeof(AudioDataValue)); 1.219 + } 1.220 + framesCopied += frameToCopy; 1.221 + iter.Next(); 1.222 + } 1.223 + // De-interleave the interleavedPcm. 1.224 + DeInterleaveTrackData(interleavedPcm.Elements(), framesCopied, mChannels, 1.225 + nonInterleavedPcm.Elements()); 1.226 + // Copy the nonInterleavedPcm to vorbis buffer. 1.227 + for(uint8_t i = 0; i < mChannels; ++i) { 1.228 + memcpy(vorbisBuffer[i], nonInterleavedPcm.Elements() + framesCopied * i, 1.229 + framesCopied * sizeof(AudioDataValue)); 1.230 + } 1.231 + 1.232 + // Now the vorbisBuffer contain the all data in non-interleaved. 1.233 + // Tell the library how much we actually submitted. 1.234 + vorbis_analysis_wrote(&mVorbisDsp, framesCopied); 1.235 + VORBISLOG("vorbis_analysis_wrote framesCopied %d\n", framesCopied); 1.236 + GetEncodedFrames(aData); 1.237 + 1.238 + return NS_OK; 1.239 +} 1.240 + 1.241 +} // namespace mozilla