1.1 --- /dev/null Thu Jan 01 00:00:00 1970 +0000 1.2 +++ b/content/media/encoder/TrackEncoder.cpp Wed Dec 31 06:09:35 2014 +0100 1.3 @@ -0,0 +1,281 @@ 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 "TrackEncoder.h" 1.9 +#include "AudioChannelFormat.h" 1.10 +#include "MediaStreamGraph.h" 1.11 +#include "prlog.h" 1.12 +#include "VideoUtils.h" 1.13 + 1.14 +#undef LOG 1.15 +#ifdef MOZ_WIDGET_GONK 1.16 +#include <android/log.h> 1.17 +#define LOG(args...) __android_log_print(ANDROID_LOG_INFO, "MediaEncoder", ## args); 1.18 +#else 1.19 +#define LOG(args, ...) 1.20 +#endif 1.21 + 1.22 +namespace mozilla { 1.23 + 1.24 +#ifdef PR_LOGGING 1.25 +PRLogModuleInfo* gTrackEncoderLog; 1.26 +#define TRACK_LOG(type, msg) PR_LOG(gTrackEncoderLog, type, msg) 1.27 +#else 1.28 +#define TRACK_LOG(type, msg) 1.29 +#endif 1.30 + 1.31 +static const int DEFAULT_CHANNELS = 1; 1.32 +static const int DEFAULT_SAMPLING_RATE = 16000; 1.33 +static const int DEFAULT_FRAME_WIDTH = 640; 1.34 +static const int DEFAULT_FRAME_HEIGHT = 480; 1.35 +static const int DEFAULT_TRACK_RATE = USECS_PER_S; 1.36 + 1.37 +TrackEncoder::TrackEncoder() 1.38 + : mReentrantMonitor("media.TrackEncoder") 1.39 + , mEncodingComplete(false) 1.40 + , mEosSetInEncoder(false) 1.41 + , mInitialized(false) 1.42 + , mEndOfStream(false) 1.43 + , mCanceled(false) 1.44 +#ifdef PR_LOGGING 1.45 + , mAudioInitCounter(0) 1.46 + , mVideoInitCounter(0) 1.47 +#endif 1.48 +{ 1.49 +#ifdef PR_LOGGING 1.50 + if (!gTrackEncoderLog) { 1.51 + gTrackEncoderLog = PR_NewLogModule("TrackEncoder"); 1.52 + } 1.53 +#endif 1.54 +} 1.55 + 1.56 +void 1.57 +AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, 1.58 + TrackID aID, 1.59 + TrackRate aTrackRate, 1.60 + TrackTicks aTrackOffset, 1.61 + uint32_t aTrackEvents, 1.62 + const MediaSegment& aQueuedMedia) 1.63 +{ 1.64 + if (mCanceled) { 1.65 + return; 1.66 + } 1.67 + 1.68 + const AudioSegment& audio = static_cast<const AudioSegment&>(aQueuedMedia); 1.69 + 1.70 + // Check and initialize parameters for codec encoder. 1.71 + if (!mInitialized) { 1.72 +#ifdef PR_LOGGING 1.73 + mAudioInitCounter++; 1.74 + TRACK_LOG(PR_LOG_DEBUG, ("Init the audio encoder %d times", mAudioInitCounter)); 1.75 +#endif 1.76 + AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(audio)); 1.77 + while (!iter.IsEnded()) { 1.78 + AudioChunk chunk = *iter; 1.79 + 1.80 + // The number of channels is determined by the first non-null chunk, and 1.81 + // thus the audio encoder is initialized at this time. 1.82 + if (!chunk.IsNull()) { 1.83 + nsresult rv = Init(chunk.mChannelData.Length(), aTrackRate); 1.84 + if (NS_FAILED(rv)) { 1.85 + LOG("[AudioTrackEncoder]: Fail to initialize the encoder!"); 1.86 + NotifyCancel(); 1.87 + } 1.88 + break; 1.89 + } 1.90 + 1.91 + iter.Next(); 1.92 + } 1.93 + } 1.94 + 1.95 + // Append and consume this raw segment. 1.96 + AppendAudioSegment(audio); 1.97 + 1.98 + 1.99 + // The stream has stopped and reached the end of track. 1.100 + if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) { 1.101 + LOG("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED ."); 1.102 + NotifyEndOfStream(); 1.103 + } 1.104 +} 1.105 + 1.106 +void 1.107 +AudioTrackEncoder::NotifyEndOfStream() 1.108 +{ 1.109 + // If source audio track is completely silent till the end of encoding, 1.110 + // initialize the encoder with default channel counts and sampling rate. 1.111 + if (!mCanceled && !mInitialized) { 1.112 + Init(DEFAULT_CHANNELS, DEFAULT_SAMPLING_RATE); 1.113 + } 1.114 + 1.115 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.116 + mEndOfStream = true; 1.117 + mReentrantMonitor.NotifyAll(); 1.118 +} 1.119 + 1.120 +nsresult 1.121 +AudioTrackEncoder::AppendAudioSegment(const AudioSegment& aSegment) 1.122 +{ 1.123 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.124 + 1.125 + AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(aSegment)); 1.126 + while (!iter.IsEnded()) { 1.127 + AudioChunk chunk = *iter; 1.128 + // Append and consume both non-null and null chunks. 1.129 + mRawSegment.AppendAndConsumeChunk(&chunk); 1.130 + iter.Next(); 1.131 + } 1.132 + 1.133 + if (mRawSegment.GetDuration() >= GetPacketDuration()) { 1.134 + mReentrantMonitor.NotifyAll(); 1.135 + } 1.136 + 1.137 + return NS_OK; 1.138 +} 1.139 + 1.140 +static const int AUDIO_PROCESSING_FRAMES = 640; /* > 10ms of 48KHz audio */ 1.141 +static const uint8_t gZeroChannel[MAX_AUDIO_SAMPLE_SIZE*AUDIO_PROCESSING_FRAMES] = {0}; 1.142 + 1.143 +/*static*/ 1.144 +void 1.145 +AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk, 1.146 + int32_t aDuration, 1.147 + uint32_t aOutputChannels, 1.148 + AudioDataValue* aOutput) 1.149 +{ 1.150 + if (aChunk.mChannelData.Length() < aOutputChannels) { 1.151 + // Up-mix. This might make the mChannelData have more than aChannels. 1.152 + AudioChannelsUpMix(&aChunk.mChannelData, aOutputChannels, gZeroChannel); 1.153 + } 1.154 + 1.155 + if (aChunk.mChannelData.Length() > aOutputChannels) { 1.156 + DownmixAndInterleave(aChunk.mChannelData, aChunk.mBufferFormat, aDuration, 1.157 + aChunk.mVolume, aOutputChannels, aOutput); 1.158 + } else { 1.159 + InterleaveAndConvertBuffer(aChunk.mChannelData.Elements(), 1.160 + aChunk.mBufferFormat, aDuration, aChunk.mVolume, 1.161 + aOutputChannels, aOutput); 1.162 + } 1.163 +} 1.164 + 1.165 +/*static*/ 1.166 +void 1.167 +AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput, 1.168 + int32_t aDuration, 1.169 + int32_t aChannels, 1.170 + AudioDataValue* aOutput) 1.171 +{ 1.172 + for (int32_t i = 0; i < aChannels; ++i) { 1.173 + for(int32_t j = 0; j < aDuration; ++j) { 1.174 + aOutput[i * aDuration + j] = aInput[i + j * aChannels]; 1.175 + } 1.176 + } 1.177 +} 1.178 + 1.179 +void 1.180 +VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, 1.181 + TrackID aID, 1.182 + TrackRate aTrackRate, 1.183 + TrackTicks aTrackOffset, 1.184 + uint32_t aTrackEvents, 1.185 + const MediaSegment& aQueuedMedia) 1.186 +{ 1.187 + if (mCanceled) { 1.188 + return; 1.189 + } 1.190 + 1.191 + const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia); 1.192 + 1.193 + // Check and initialize parameters for codec encoder. 1.194 + if (!mInitialized) { 1.195 +#ifdef PR_LOGGING 1.196 + mVideoInitCounter++; 1.197 + TRACK_LOG(PR_LOG_DEBUG, ("Init the video encoder %d times", mVideoInitCounter)); 1.198 +#endif 1.199 + VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(video)); 1.200 + while (!iter.IsEnded()) { 1.201 + VideoChunk chunk = *iter; 1.202 + if (!chunk.IsNull()) { 1.203 + gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize(); 1.204 + gfxIntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize(); 1.205 + nsresult rv = Init(imgsize.width, imgsize.height, 1.206 + intrinsicSize.width, intrinsicSize.height, 1.207 + aTrackRate); 1.208 + if (NS_FAILED(rv)) { 1.209 + LOG("[VideoTrackEncoder]: Fail to initialize the encoder!"); 1.210 + NotifyCancel(); 1.211 + } 1.212 + break; 1.213 + } 1.214 + 1.215 + iter.Next(); 1.216 + } 1.217 + } 1.218 + 1.219 + AppendVideoSegment(video); 1.220 + 1.221 + // The stream has stopped and reached the end of track. 1.222 + if (aTrackEvents == MediaStreamListener::TRACK_EVENT_ENDED) { 1.223 + LOG("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED ."); 1.224 + NotifyEndOfStream(); 1.225 + } 1.226 + 1.227 +} 1.228 + 1.229 +nsresult 1.230 +VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment) 1.231 +{ 1.232 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.233 + 1.234 + // Append all video segments from MediaStreamGraph, including null an 1.235 + // non-null frames. 1.236 + VideoSegment::ChunkIterator iter(const_cast<VideoSegment&>(aSegment)); 1.237 + while (!iter.IsEnded()) { 1.238 + VideoChunk chunk = *iter; 1.239 + nsRefPtr<layers::Image> image = chunk.mFrame.GetImage(); 1.240 + mRawSegment.AppendFrame(image.forget(), chunk.GetDuration(), 1.241 + chunk.mFrame.GetIntrinsicSize().ToIntSize()); 1.242 + iter.Next(); 1.243 + } 1.244 + 1.245 + if (mRawSegment.GetDuration() > 0) { 1.246 + mReentrantMonitor.NotifyAll(); 1.247 + } 1.248 + 1.249 + return NS_OK; 1.250 +} 1.251 + 1.252 +void 1.253 +VideoTrackEncoder::NotifyEndOfStream() 1.254 +{ 1.255 + // If source video track is muted till the end of encoding, initialize the 1.256 + // encoder with default frame width, frame height, and track rate. 1.257 + if (!mCanceled && !mInitialized) { 1.258 + Init(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, 1.259 + DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT, DEFAULT_TRACK_RATE); 1.260 + } 1.261 + 1.262 + ReentrantMonitorAutoEnter mon(mReentrantMonitor); 1.263 + mEndOfStream = true; 1.264 + mReentrantMonitor.NotifyAll(); 1.265 +} 1.266 + 1.267 +void 1.268 +VideoTrackEncoder::CreateMutedFrame(nsTArray<uint8_t>* aOutputBuffer) 1.269 +{ 1.270 + NS_ENSURE_TRUE_VOID(aOutputBuffer); 1.271 + 1.272 + // Supports YUV420 image format only. 1.273 + int yPlaneLen = mFrameWidth * mFrameHeight; 1.274 + int cbcrPlaneLen = yPlaneLen / 2; 1.275 + int frameLen = yPlaneLen + cbcrPlaneLen; 1.276 + 1.277 + aOutputBuffer->SetLength(frameLen); 1.278 + // Fill Y plane. 1.279 + memset(aOutputBuffer->Elements(), 0x10, yPlaneLen); 1.280 + // Fill Cb/Cr planes. 1.281 + memset(aOutputBuffer->Elements() + yPlaneLen, 0x80, cbcrPlaneLen); 1.282 +} 1.283 + 1.284 +}