content/media/encoder/MediaEncoder.cpp

Wed, 31 Dec 2014 06:09:35 +0100

author
Michael Schloh von Bennewitz <michael@schloh.com>
date
Wed, 31 Dec 2014 06:09:35 +0100
changeset 0
6474c204b198
permissions
-rw-r--r--

Cloned upstream origin tor-browser at tor-browser-31.3.0esr-4.5-1-build1
revision ID fc1c9ff7c1b2defdbc039f12214767608f46423f for hacking purpose.

michael@0 1 /* -*- Mode: C++; tab-width: 2; indent-tabs-mode: nil; c-basic-offset: 2 -*-*/
michael@0 2 /* This Source Code Form is subject to the terms of the Mozilla Public
michael@0 3 * License, v. 2.0. If a copy of the MPL was not distributed with this file,
michael@0 4 * You can obtain one at http://mozilla.org/MPL/2.0/. */
michael@0 5 #include "MediaEncoder.h"
michael@0 6 #include "MediaDecoder.h"
michael@0 7 #include "nsIPrincipal.h"
michael@0 8 #include "nsMimeTypes.h"
michael@0 9 #include "prlog.h"
michael@0 10 #include "mozilla/Preferences.h"
michael@0 11
michael@0 12 #include "OggWriter.h"
michael@0 13 #ifdef MOZ_OPUS
michael@0 14 #include "OpusTrackEncoder.h"
michael@0 15
michael@0 16 #endif
michael@0 17
michael@0 18 #ifdef MOZ_VORBIS
michael@0 19 #include "VorbisTrackEncoder.h"
michael@0 20 #endif
michael@0 21 #ifdef MOZ_WEBM_ENCODER
michael@0 22 #include "VorbisTrackEncoder.h"
michael@0 23 #include "VP8TrackEncoder.h"
michael@0 24 #include "WebMWriter.h"
michael@0 25 #endif
michael@0 26 #ifdef MOZ_OMX_ENCODER
michael@0 27 #include "OmxTrackEncoder.h"
michael@0 28 #include "ISOMediaWriter.h"
michael@0 29 #endif
michael@0 30
michael@0 31 #ifdef LOG
michael@0 32 #undef LOG
michael@0 33 #endif
michael@0 34
michael@0 35 #ifdef PR_LOGGING
michael@0 36 PRLogModuleInfo* gMediaEncoderLog;
michael@0 37 #define LOG(type, msg) PR_LOG(gMediaEncoderLog, type, msg)
michael@0 38 #else
michael@0 39 #define LOG(type, msg)
michael@0 40 #endif
michael@0 41
michael@0 42 namespace mozilla {
michael@0 43
michael@0 44 void
michael@0 45 MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
michael@0 46 TrackID aID,
michael@0 47 TrackRate aTrackRate,
michael@0 48 TrackTicks aTrackOffset,
michael@0 49 uint32_t aTrackEvents,
michael@0 50 const MediaSegment& aQueuedMedia)
michael@0 51 {
michael@0 52 // Process the incoming raw track data from MediaStreamGraph, called on the
michael@0 53 // thread of MediaStreamGraph.
michael@0 54 if (mAudioEncoder && aQueuedMedia.GetType() == MediaSegment::AUDIO) {
michael@0 55 mAudioEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
michael@0 56 aTrackOffset, aTrackEvents,
michael@0 57 aQueuedMedia);
michael@0 58
michael@0 59 } else if (mVideoEncoder && aQueuedMedia.GetType() == MediaSegment::VIDEO) {
michael@0 60 mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID, aTrackRate,
michael@0 61 aTrackOffset, aTrackEvents,
michael@0 62 aQueuedMedia);
michael@0 63 }
michael@0 64 }
michael@0 65
michael@0 66 void
michael@0 67 MediaEncoder::NotifyRemoved(MediaStreamGraph* aGraph)
michael@0 68 {
michael@0 69 // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
michael@0 70 LOG(PR_LOG_DEBUG, ("NotifyRemoved in [MediaEncoder]."));
michael@0 71 if (mAudioEncoder) {
michael@0 72 mAudioEncoder->NotifyRemoved(aGraph);
michael@0 73 }
michael@0 74 if (mVideoEncoder) {
michael@0 75 mVideoEncoder->NotifyRemoved(aGraph);
michael@0 76 }
michael@0 77
michael@0 78 }
michael@0 79
michael@0 80 /* static */
michael@0 81 already_AddRefed<MediaEncoder>
michael@0 82 MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint8_t aTrackTypes)
michael@0 83 {
michael@0 84 #ifdef PR_LOGGING
michael@0 85 if (!gMediaEncoderLog) {
michael@0 86 gMediaEncoderLog = PR_NewLogModule("MediaEncoder");
michael@0 87 }
michael@0 88 #endif
michael@0 89 nsAutoPtr<ContainerWriter> writer;
michael@0 90 nsAutoPtr<AudioTrackEncoder> audioEncoder;
michael@0 91 nsAutoPtr<VideoTrackEncoder> videoEncoder;
michael@0 92 nsRefPtr<MediaEncoder> encoder;
michael@0 93 nsString mimeType;
michael@0 94 if (!aTrackTypes) {
michael@0 95 LOG(PR_LOG_ERROR, ("NO TrackTypes!!!"));
michael@0 96 return nullptr;
michael@0 97 }
michael@0 98 #ifdef MOZ_WEBM_ENCODER
michael@0 99 else if (MediaEncoder::IsWebMEncoderEnabled() &&
michael@0 100 (aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
michael@0 101 (aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
michael@0 102 if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
michael@0 103 audioEncoder = new VorbisTrackEncoder();
michael@0 104 NS_ENSURE_TRUE(audioEncoder, nullptr);
michael@0 105 }
michael@0 106 videoEncoder = new VP8TrackEncoder();
michael@0 107 writer = new WebMWriter(aTrackTypes);
michael@0 108 NS_ENSURE_TRUE(writer, nullptr);
michael@0 109 NS_ENSURE_TRUE(videoEncoder, nullptr);
michael@0 110 mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
michael@0 111 }
michael@0 112 #endif //MOZ_WEBM_ENCODER
michael@0 113 #ifdef MOZ_OMX_ENCODER
michael@0 114 else if (MediaEncoder::IsOMXEncoderEnabled() &&
michael@0 115 (aMIMEType.EqualsLiteral(VIDEO_MP4) ||
michael@0 116 (aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
michael@0 117 if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK) {
michael@0 118 audioEncoder = new OmxAACAudioTrackEncoder();
michael@0 119 NS_ENSURE_TRUE(audioEncoder, nullptr);
michael@0 120 }
michael@0 121 videoEncoder = new OmxVideoTrackEncoder();
michael@0 122 writer = new ISOMediaWriter(aTrackTypes);
michael@0 123 NS_ENSURE_TRUE(writer, nullptr);
michael@0 124 NS_ENSURE_TRUE(videoEncoder, nullptr);
michael@0 125 mimeType = NS_LITERAL_STRING(VIDEO_MP4);
michael@0 126 } else if (MediaEncoder::IsOMXEncoderEnabled() &&
michael@0 127 (aMIMEType.EqualsLiteral(AUDIO_3GPP))) {
michael@0 128 audioEncoder = new OmxAMRAudioTrackEncoder();
michael@0 129 NS_ENSURE_TRUE(audioEncoder, nullptr);
michael@0 130
michael@0 131 writer = new ISOMediaWriter(aTrackTypes, ISOMediaWriter::TYPE_FRAG_3GP);
michael@0 132 NS_ENSURE_TRUE(writer, nullptr);
michael@0 133 mimeType = NS_LITERAL_STRING(AUDIO_3GPP);
michael@0 134 }
michael@0 135 #endif // MOZ_OMX_ENCODER
michael@0 136 else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
michael@0 137 (aMIMEType.EqualsLiteral(AUDIO_OGG) ||
michael@0 138 (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
michael@0 139 writer = new OggWriter();
michael@0 140 audioEncoder = new OpusTrackEncoder();
michael@0 141 NS_ENSURE_TRUE(writer, nullptr);
michael@0 142 NS_ENSURE_TRUE(audioEncoder, nullptr);
michael@0 143 mimeType = NS_LITERAL_STRING(AUDIO_OGG);
michael@0 144 }
michael@0 145 else {
michael@0 146 LOG(PR_LOG_ERROR, ("Can not find any encoder to record this media stream"));
michael@0 147 return nullptr;
michael@0 148 }
michael@0 149 LOG(PR_LOG_DEBUG, ("Create encoder result:a[%d] v[%d] w[%d] mimeType = %s.",
michael@0 150 audioEncoder != nullptr, videoEncoder != nullptr,
michael@0 151 writer != nullptr, mimeType.get()));
michael@0 152 encoder = new MediaEncoder(writer.forget(), audioEncoder.forget(),
michael@0 153 videoEncoder.forget(), mimeType);
michael@0 154 return encoder.forget();
michael@0 155 }
michael@0 156
michael@0 157 /**
michael@0 158 * GetEncodedData() runs as a state machine, starting with mState set to
michael@0 159 * GET_METADDATA, the procedure should be as follow:
michael@0 160 *
michael@0 161 * While non-stop
michael@0 162 * If mState is GET_METADDATA
michael@0 163 * Get the meta data from audio/video encoder
michael@0 164 * If a meta data is generated
michael@0 165 * Get meta data from audio/video encoder
michael@0 166 * Set mState to ENCODE_TRACK
michael@0 167 * Return the final container data
michael@0 168 *
michael@0 169 * If mState is ENCODE_TRACK
michael@0 170 * Get encoded track data from audio/video encoder
michael@0 171 * If a packet of track data is generated
michael@0 172 * Insert encoded track data into the container stream of writer
michael@0 173 * If the final container data is copied to aOutput
michael@0 174 * Return the copy of final container data
michael@0 175 * If this is the last packet of input stream
michael@0 176 * Set mState to ENCODE_DONE
michael@0 177 *
michael@0 178 * If mState is ENCODE_DONE or ENCODE_ERROR
michael@0 179 * Stop the loop
michael@0 180 */
michael@0 181 void
michael@0 182 MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
michael@0 183 nsAString& aMIMEType)
michael@0 184 {
michael@0 185 MOZ_ASSERT(!NS_IsMainThread());
michael@0 186
michael@0 187 aMIMEType = mMIMEType;
michael@0 188
michael@0 189 bool reloop = true;
michael@0 190 while (reloop) {
michael@0 191 switch (mState) {
michael@0 192 case ENCODE_METADDATA: {
michael@0 193 LOG(PR_LOG_DEBUG, ("ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
michael@0 194 nsresult rv = CopyMetadataToMuxer(mAudioEncoder.get());
michael@0 195 if (NS_FAILED(rv)) {
michael@0 196 LOG(PR_LOG_ERROR, ("Error! Fail to Set Audio Metadata"));
michael@0 197 break;
michael@0 198 }
michael@0 199 rv = CopyMetadataToMuxer(mVideoEncoder.get());
michael@0 200 if (NS_FAILED(rv)) {
michael@0 201 LOG(PR_LOG_ERROR, ("Error! Fail to Set Video Metadata"));
michael@0 202 break;
michael@0 203 }
michael@0 204
michael@0 205 rv = mWriter->GetContainerData(aOutputBufs,
michael@0 206 ContainerWriter::GET_HEADER);
michael@0 207 if (NS_FAILED(rv)) {
michael@0 208 LOG(PR_LOG_ERROR,("Error! writer fail to generate header!"));
michael@0 209 mState = ENCODE_ERROR;
michael@0 210 break;
michael@0 211 }
michael@0 212 LOG(PR_LOG_DEBUG, ("Finish ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
michael@0 213 mState = ENCODE_TRACK;
michael@0 214 break;
michael@0 215 }
michael@0 216
michael@0 217 case ENCODE_TRACK: {
michael@0 218 LOG(PR_LOG_DEBUG, ("ENCODE_TRACK TimeStamp = %f", GetEncodeTimeStamp()));
michael@0 219 EncodedFrameContainer encodedData;
michael@0 220 nsresult rv = NS_OK;
michael@0 221 rv = WriteEncodedDataToMuxer(mAudioEncoder.get());
michael@0 222 if (NS_FAILED(rv)) {
michael@0 223 LOG(PR_LOG_ERROR, ("Error! Fail to write audio encoder data to muxer"));
michael@0 224 break;
michael@0 225 }
michael@0 226 LOG(PR_LOG_DEBUG, ("Audio encoded TimeStamp = %f", GetEncodeTimeStamp()));
michael@0 227 rv = WriteEncodedDataToMuxer(mVideoEncoder.get());
michael@0 228 if (NS_FAILED(rv)) {
michael@0 229 LOG(PR_LOG_ERROR, ("Fail to write video encoder data to muxer"));
michael@0 230 break;
michael@0 231 }
michael@0 232 LOG(PR_LOG_DEBUG, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
michael@0 233 // In audio only or video only case, let unavailable track's flag to be true.
michael@0 234 bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
michael@0 235 bool isVideoCompleted = (mVideoEncoder && mVideoEncoder->IsEncodingComplete()) || !mVideoEncoder;
michael@0 236 rv = mWriter->GetContainerData(aOutputBufs,
michael@0 237 isAudioCompleted && isVideoCompleted ?
michael@0 238 ContainerWriter::FLUSH_NEEDED : 0);
michael@0 239 if (NS_SUCCEEDED(rv)) {
michael@0 240 // Successfully get the copy of final container data from writer.
michael@0 241 reloop = false;
michael@0 242 }
michael@0 243 mState = (mWriter->IsWritingComplete()) ? ENCODE_DONE : ENCODE_TRACK;
michael@0 244 LOG(PR_LOG_DEBUG, ("END ENCODE_TRACK TimeStamp = %f "
michael@0 245 "mState = %d aComplete %d vComplete %d",
michael@0 246 GetEncodeTimeStamp(), mState, isAudioCompleted, isVideoCompleted));
michael@0 247 break;
michael@0 248 }
michael@0 249
michael@0 250 case ENCODE_DONE:
michael@0 251 case ENCODE_ERROR:
michael@0 252 LOG(PR_LOG_DEBUG, ("MediaEncoder has been shutdown."));
michael@0 253 mShutdown = true;
michael@0 254 reloop = false;
michael@0 255 break;
michael@0 256 default:
michael@0 257 MOZ_CRASH("Invalid encode state");
michael@0 258 }
michael@0 259 }
michael@0 260 }
michael@0 261
michael@0 262 nsresult
michael@0 263 MediaEncoder::WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder)
michael@0 264 {
michael@0 265 if (aTrackEncoder == nullptr) {
michael@0 266 return NS_OK;
michael@0 267 }
michael@0 268 if (aTrackEncoder->IsEncodingComplete()) {
michael@0 269 return NS_OK;
michael@0 270 }
michael@0 271 EncodedFrameContainer encodedVideoData;
michael@0 272 nsresult rv = aTrackEncoder->GetEncodedTrack(encodedVideoData);
michael@0 273 if (NS_FAILED(rv)) {
michael@0 274 // Encoding might be canceled.
michael@0 275 LOG(PR_LOG_ERROR, ("Error! Fail to get encoded data from video encoder."));
michael@0 276 mState = ENCODE_ERROR;
michael@0 277 return rv;
michael@0 278 }
michael@0 279 rv = mWriter->WriteEncodedTrack(encodedVideoData,
michael@0 280 aTrackEncoder->IsEncodingComplete() ?
michael@0 281 ContainerWriter::END_OF_STREAM : 0);
michael@0 282 if (NS_FAILED(rv)) {
michael@0 283 LOG(PR_LOG_ERROR, ("Error! Fail to write encoded video track to the media container."));
michael@0 284 mState = ENCODE_ERROR;
michael@0 285 }
michael@0 286 return rv;
michael@0 287 }
michael@0 288
michael@0 289 nsresult
michael@0 290 MediaEncoder::CopyMetadataToMuxer(TrackEncoder *aTrackEncoder)
michael@0 291 {
michael@0 292 if (aTrackEncoder == nullptr) {
michael@0 293 return NS_OK;
michael@0 294 }
michael@0 295 nsRefPtr<TrackMetadataBase> meta = aTrackEncoder->GetMetadata();
michael@0 296 if (meta == nullptr) {
michael@0 297 LOG(PR_LOG_ERROR, ("Error! metadata = null"));
michael@0 298 mState = ENCODE_ERROR;
michael@0 299 return NS_ERROR_ABORT;
michael@0 300 }
michael@0 301
michael@0 302 nsresult rv = mWriter->SetMetadata(meta);
michael@0 303 if (NS_FAILED(rv)) {
michael@0 304 LOG(PR_LOG_ERROR, ("Error! SetMetadata fail"));
michael@0 305 mState = ENCODE_ERROR;
michael@0 306 }
michael@0 307 return rv;
michael@0 308 }
michael@0 309
michael@0 310 #ifdef MOZ_WEBM_ENCODER
michael@0 311 bool
michael@0 312 MediaEncoder::IsWebMEncoderEnabled()
michael@0 313 {
michael@0 314 return Preferences::GetBool("media.encoder.webm.enabled");
michael@0 315 }
michael@0 316 #endif
michael@0 317
michael@0 318 #ifdef MOZ_OMX_ENCODER
michael@0 319 bool
michael@0 320 MediaEncoder::IsOMXEncoderEnabled()
michael@0 321 {
michael@0 322 return Preferences::GetBool("media.encoder.omx.enabled");
michael@0 323 }
michael@0 324 #endif
michael@0 325
michael@0 326 }

mercurial