LCOV - code coverage report
Current view: top level - dom/media/encoder - MediaEncoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 207 0.0 %
Date: 2017-07-14 16:53:18 Functions: 0 14 0.0 %
Legend: Lines: hit not hit

          Line data    Source code
       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 "MediaEncoder.h"
       6             : #include "MediaDecoder.h"
       7             : #include "nsIPrincipal.h"
       8             : #include "nsMimeTypes.h"
       9             : #include "TimeUnits.h"
      10             : #include "mozilla/Logging.h"
      11             : #include "mozilla/Preferences.h"
      12             : #include "mozilla/StaticPtr.h"
      13             : #include "mozilla/gfx/Point.h" // IntSize
      14             : 
      15             : #include"GeckoProfiler.h"
      16             : #include "OggWriter.h"
      17             : #include "OpusTrackEncoder.h"
      18             : 
      19             : #ifdef MOZ_WEBM_ENCODER
      20             : #include "VP8TrackEncoder.h"
      21             : #include "WebMWriter.h"
      22             : #endif
      23             : 
      24             : #ifdef LOG
      25             : #undef LOG
      26             : #endif
      27             : 
      28             : mozilla::LazyLogModule gMediaEncoderLog("MediaEncoder");
      29             : #define LOG(type, msg) MOZ_LOG(gMediaEncoderLog, type, msg)
      30             : 
      31             : namespace mozilla {
      32             : 
      33             : void
      34           0 : MediaStreamVideoRecorderSink::SetCurrentFrames(const VideoSegment& aSegment)
      35             : {
      36           0 :   MOZ_ASSERT(mVideoEncoder);
      37             :   // If we're suspended (paused) we don't forward frames
      38           0 :   if (!mSuspended) {
      39           0 :     mVideoEncoder->SetCurrentFrames(aSegment);
      40             :   }
      41           0 : }
      42             : 
      43             : void
      44           0 : MediaEncoder::Suspend()
      45             : {
      46           0 :   MOZ_ASSERT(NS_IsMainThread());
      47           0 :   mLastPauseStartTime = TimeStamp::Now();
      48           0 :   mSuspended = true;
      49           0 :   mVideoSink->Suspend();
      50           0 : }
      51             : 
      52             : void
      53           0 : MediaEncoder::Resume()
      54             : {
      55           0 :   MOZ_ASSERT(NS_IsMainThread());
      56           0 :   if (!mSuspended) {
      57           0 :     return;
      58             :   }
      59             :   media::TimeUnit timeSpentPaused =
      60             :     media::TimeUnit::FromTimeDuration(
      61           0 :       TimeStamp::Now() - mLastPauseStartTime);
      62           0 :   MOZ_ASSERT(timeSpentPaused.ToMicroseconds() >= 0);
      63           0 :   MOZ_RELEASE_ASSERT(timeSpentPaused.IsValid());
      64           0 :   mMicrosecondsSpentPaused += timeSpentPaused.ToMicroseconds();;
      65           0 :   mSuspended = false;
      66           0 :   mVideoSink->Resume();
      67             : }
      68             : 
      69             : void
      70           0 : MediaEncoder::SetDirectConnect(bool aConnected)
      71             : {
      72           0 :   mDirectConnected = aConnected;
      73           0 : }
      74             : 
      75             : void
      76           0 : MediaEncoder::NotifyRealtimeData(MediaStreamGraph* aGraph,
      77             :                                  TrackID aID,
      78             :                                  StreamTime aTrackOffset,
      79             :                                  uint32_t aTrackEvents,
      80             :                                  const MediaSegment& aRealtimeMedia)
      81             : {
      82           0 :   if (mSuspended) {
      83           0 :     return;
      84             :   }
      85             :   // Process the incoming raw track data from MediaStreamGraph, called on the
      86             :   // thread of MediaStreamGraph.
      87           0 :   if (mAudioEncoder && aRealtimeMedia.GetType() == MediaSegment::AUDIO) {
      88           0 :     mAudioEncoder->NotifyQueuedTrackChanges(aGraph, aID,
      89             :                                             aTrackOffset, aTrackEvents,
      90           0 :                                             aRealtimeMedia);
      91           0 :   } else if (mVideoEncoder &&
      92           0 :               aRealtimeMedia.GetType() == MediaSegment::VIDEO &&
      93             :               aTrackEvents != TrackEventCommand::TRACK_EVENT_NONE) {
      94           0 :     mVideoEncoder->NotifyQueuedTrackChanges(aGraph, aID,
      95             :                                             aTrackOffset, aTrackEvents,
      96           0 :                                             aRealtimeMedia);
      97             :   }
      98             : }
      99             : 
     100             : void
     101           0 : MediaEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
     102             :                                        TrackID aID,
     103             :                                        StreamTime aTrackOffset,
     104             :                                        TrackEventCommand aTrackEvents,
     105             :                                        const MediaSegment& aQueuedMedia,
     106             :                                        MediaStream* aInputStream,
     107             :                                        TrackID aInputTrackID)
     108             : {
     109           0 :   if (!mDirectConnected) {
     110           0 :     NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, aQueuedMedia);
     111             :   } else {
     112           0 :     if (aTrackEvents != TrackEventCommand::TRACK_EVENT_NONE) {
     113             :       // forward events (TRACK_EVENT_ENDED) but not the media
     114           0 :       if (aQueuedMedia.GetType() == MediaSegment::VIDEO) {
     115           0 :         VideoSegment segment;
     116           0 :         NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
     117             :       } else {
     118           0 :         AudioSegment segment;
     119           0 :         NotifyRealtimeData(aGraph, aID, aTrackOffset, aTrackEvents, segment);
     120             :       }
     121             :     }
     122             :   }
     123           0 : }
     124             : 
     125             : void
     126           0 : MediaEncoder::NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
     127             :                                     StreamTime aTrackOffset,
     128             :                                     const AudioSegment& aQueuedMedia,
     129             :                                     MediaStream* aInputStream,
     130             :                                     TrackID aInputTrackID)
     131             : {
     132           0 :   if (!mDirectConnected) {
     133           0 :     NotifyRealtimeData(aGraph, aID, aTrackOffset, 0, aQueuedMedia);
     134             :   }
     135           0 : }
     136             : 
     137             : void
     138           0 : MediaEncoder::NotifyEvent(MediaStreamGraph* aGraph,
     139             :                           MediaStreamGraphEvent event)
     140             : {
     141             :   // In case that MediaEncoder does not receive a TRACK_EVENT_ENDED event.
     142           0 :   LOG(LogLevel::Debug, ("NotifyRemoved in [MediaEncoder]."));
     143           0 :   if (mAudioEncoder) {
     144           0 :     mAudioEncoder->NotifyEvent(aGraph, event);
     145             :   }
     146           0 :   if (mVideoEncoder) {
     147           0 :     mVideoEncoder->NotifyEvent(aGraph, event);
     148             :   }
     149           0 : }
     150             : 
     151             : /* static */
     152             : already_AddRefed<MediaEncoder>
     153           0 : MediaEncoder::CreateEncoder(const nsAString& aMIMEType, uint32_t aAudioBitrate,
     154             :                             uint32_t aVideoBitrate, uint32_t aBitrate,
     155             :                             uint8_t aTrackTypes,
     156             :                             TrackRate aTrackRate)
     157             : {
     158           0 :   AUTO_PROFILER_LABEL("MediaEncoder::CreateEncoder", OTHER);
     159             : 
     160           0 :   nsAutoPtr<ContainerWriter> writer;
     161           0 :   nsAutoPtr<AudioTrackEncoder> audioEncoder;
     162           0 :   nsAutoPtr<VideoTrackEncoder> videoEncoder;
     163           0 :   RefPtr<MediaEncoder> encoder;
     164           0 :   nsString mimeType;
     165           0 :   if (!aTrackTypes) {
     166           0 :     LOG(LogLevel::Error, ("NO TrackTypes!!!"));
     167           0 :     return nullptr;
     168             :   }
     169             : #ifdef MOZ_WEBM_ENCODER
     170           0 :   else if (MediaEncoder::IsWebMEncoderEnabled() &&
     171           0 :           (aMIMEType.EqualsLiteral(VIDEO_WEBM) ||
     172           0 :           (aTrackTypes & ContainerWriter::CREATE_VIDEO_TRACK))) {
     173           0 :     if (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK
     174           0 :         && MediaDecoder::IsOpusEnabled()) {
     175           0 :       audioEncoder = new OpusTrackEncoder();
     176           0 :       NS_ENSURE_TRUE(audioEncoder, nullptr);
     177             :     }
     178           0 :     videoEncoder = new VP8TrackEncoder(aTrackRate);
     179           0 :     writer = new WebMWriter(aTrackTypes);
     180           0 :     NS_ENSURE_TRUE(writer, nullptr);
     181           0 :     NS_ENSURE_TRUE(videoEncoder, nullptr);
     182           0 :     mimeType = NS_LITERAL_STRING(VIDEO_WEBM);
     183             :   }
     184             : #endif //MOZ_WEBM_ENCODER
     185           0 :   else if (MediaDecoder::IsOggEnabled() && MediaDecoder::IsOpusEnabled() &&
     186           0 :            (aMIMEType.EqualsLiteral(AUDIO_OGG) ||
     187           0 :            (aTrackTypes & ContainerWriter::CREATE_AUDIO_TRACK))) {
     188           0 :     writer = new OggWriter();
     189           0 :     audioEncoder = new OpusTrackEncoder();
     190           0 :     NS_ENSURE_TRUE(writer, nullptr);
     191           0 :     NS_ENSURE_TRUE(audioEncoder, nullptr);
     192           0 :     mimeType = NS_LITERAL_STRING(AUDIO_OGG);
     193             :   }
     194             :   else {
     195           0 :     LOG(LogLevel::Error, ("Can not find any encoder to record this media stream"));
     196           0 :     return nullptr;
     197             :   }
     198           0 :   LOG(LogLevel::Debug, ("Create encoder result:a[%d] v[%d] w[%d] mimeType = %s.",
     199             :                       audioEncoder != nullptr, videoEncoder != nullptr,
     200             :                       writer != nullptr, NS_ConvertUTF16toUTF8(mimeType).get()));
     201           0 :   if (videoEncoder && aVideoBitrate != 0) {
     202           0 :     videoEncoder->SetBitrate(aVideoBitrate);
     203             :   }
     204           0 :   if (audioEncoder && aAudioBitrate != 0) {
     205           0 :     audioEncoder->SetBitrate(aAudioBitrate);
     206             :   }
     207           0 :   encoder = new MediaEncoder(writer.forget(), audioEncoder.forget(),
     208           0 :                              videoEncoder.forget(), mimeType, aAudioBitrate,
     209           0 :                              aVideoBitrate, aBitrate);
     210           0 :   return encoder.forget();
     211             : }
     212             : 
     213             : /**
     214             :  * GetEncodedData() runs as a state machine, starting with mState set to
     215             :  * GET_METADDATA, the procedure should be as follow:
     216             :  *
     217             :  * While non-stop
     218             :  *   If mState is GET_METADDATA
     219             :  *     Get the meta data from audio/video encoder
     220             :  *     If a meta data is generated
     221             :  *       Get meta data from audio/video encoder
     222             :  *       Set mState to ENCODE_TRACK
     223             :  *       Return the final container data
     224             :  *
     225             :  *   If mState is ENCODE_TRACK
     226             :  *     Get encoded track data from audio/video encoder
     227             :  *     If a packet of track data is generated
     228             :  *       Insert encoded track data into the container stream of writer
     229             :  *       If the final container data is copied to aOutput
     230             :  *         Return the copy of final container data
     231             :  *       If this is the last packet of input stream
     232             :  *         Set mState to ENCODE_DONE
     233             :  *
     234             :  *   If mState is ENCODE_DONE or ENCODE_ERROR
     235             :  *     Stop the loop
     236             :  */
     237             : void
     238           0 : MediaEncoder::GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
     239             :                              nsAString& aMIMEType)
     240             : {
     241           0 :   MOZ_ASSERT(!NS_IsMainThread());
     242             : 
     243           0 :   aMIMEType = mMIMEType;
     244           0 :   AUTO_PROFILER_LABEL("MediaEncoder::GetEncodedData", OTHER);
     245             : 
     246           0 :   bool reloop = true;
     247           0 :   while (reloop) {
     248           0 :     switch (mState) {
     249             :     case ENCODE_METADDATA: {
     250           0 :       LOG(LogLevel::Debug, ("ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
     251           0 :       nsresult rv = CopyMetadataToMuxer(mAudioEncoder.get());
     252           0 :       if (NS_FAILED(rv)) {
     253           0 :         LOG(LogLevel::Error, ("Error! Fail to Set Audio Metadata"));
     254           0 :         break;
     255             :       }
     256           0 :       rv = CopyMetadataToMuxer(mVideoEncoder.get());
     257           0 :       if (NS_FAILED(rv)) {
     258           0 :         LOG(LogLevel::Error, ("Error! Fail to Set Video Metadata"));
     259           0 :         break;
     260             :       }
     261             : 
     262           0 :       rv = mWriter->GetContainerData(aOutputBufs,
     263           0 :                                      ContainerWriter::GET_HEADER);
     264           0 :       if (aOutputBufs != nullptr) {
     265           0 :         mSizeOfBuffer = aOutputBufs->ShallowSizeOfExcludingThis(MallocSizeOf);
     266             :       }
     267           0 :       if (NS_FAILED(rv)) {
     268           0 :        LOG(LogLevel::Error,("Error! writer fail to generate header!"));
     269           0 :        mState = ENCODE_ERROR;
     270           0 :        break;
     271             :       }
     272           0 :       LOG(LogLevel::Debug, ("Finish ENCODE_METADDATA TimeStamp = %f", GetEncodeTimeStamp()));
     273           0 :       mState = ENCODE_TRACK;
     274           0 :       break;
     275             :     }
     276             : 
     277             :     case ENCODE_TRACK: {
     278           0 :       LOG(LogLevel::Debug, ("ENCODE_TRACK TimeStamp = %f", GetEncodeTimeStamp()));
     279           0 :       EncodedFrameContainer encodedData;
     280           0 :       nsresult rv = NS_OK;
     281             :       // We're most likely to actually wait for a video frame, so do that first to minimize
     282             :       // capture offset/lipsync issues
     283           0 :       rv = WriteEncodedDataToMuxer(mVideoEncoder.get());
     284           0 :       if (NS_FAILED(rv)) {
     285           0 :         LOG(LogLevel::Error, ("Fail to write video encoder data to muxer"));
     286           0 :         break;
     287             :       }
     288           0 :       rv = WriteEncodedDataToMuxer(mAudioEncoder.get());
     289           0 :       if (NS_FAILED(rv)) {
     290           0 :         LOG(LogLevel::Error, ("Error! Fail to write audio encoder data to muxer"));
     291           0 :         break;
     292             :       }
     293           0 :       LOG(LogLevel::Debug, ("Audio encoded TimeStamp = %f", GetEncodeTimeStamp()));
     294           0 :       LOG(LogLevel::Debug, ("Video encoded TimeStamp = %f", GetEncodeTimeStamp()));
     295             :       // In audio only or video only case, let unavailable track's flag to be true.
     296           0 :       bool isAudioCompleted = (mAudioEncoder && mAudioEncoder->IsEncodingComplete()) || !mAudioEncoder;
     297           0 :       bool isVideoCompleted = (mVideoEncoder && mVideoEncoder->IsEncodingComplete()) || !mVideoEncoder;
     298           0 :       rv = mWriter->GetContainerData(aOutputBufs,
     299           0 :                                      isAudioCompleted && isVideoCompleted ?
     300           0 :                                      ContainerWriter::FLUSH_NEEDED : 0);
     301           0 :       if (aOutputBufs != nullptr) {
     302           0 :         mSizeOfBuffer = aOutputBufs->ShallowSizeOfExcludingThis(MallocSizeOf);
     303             :       }
     304           0 :       if (NS_SUCCEEDED(rv)) {
     305             :         // Successfully get the copy of final container data from writer.
     306           0 :         reloop = false;
     307             :       }
     308           0 :       mState = (mWriter->IsWritingComplete()) ? ENCODE_DONE : ENCODE_TRACK;
     309           0 :       LOG(LogLevel::Debug, ("END ENCODE_TRACK TimeStamp = %f "
     310             :           "mState = %d aComplete %d vComplete %d",
     311             :           GetEncodeTimeStamp(), mState, isAudioCompleted, isVideoCompleted));
     312           0 :       break;
     313             :     }
     314             : 
     315             :     case ENCODE_DONE:
     316             :     case ENCODE_ERROR:
     317           0 :       LOG(LogLevel::Debug, ("MediaEncoder has been shutdown."));
     318           0 :       mSizeOfBuffer = 0;
     319           0 :       mShutdown = true;
     320           0 :       reloop = false;
     321           0 :       break;
     322             :     default:
     323           0 :       MOZ_CRASH("Invalid encode state");
     324             :     }
     325             :   }
     326           0 : }
     327             : 
     328             : nsresult
     329           0 : MediaEncoder::WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder)
     330             : {
     331           0 :   if (aTrackEncoder == nullptr) {
     332           0 :     return NS_OK;
     333             :   }
     334           0 :   if (aTrackEncoder->IsEncodingComplete()) {
     335           0 :     return NS_OK;
     336             :   }
     337             : 
     338           0 :   AUTO_PROFILER_LABEL("MediaEncoder::WriteEncodedDataToMuxer", OTHER);
     339             : 
     340           0 :   EncodedFrameContainer encodedVideoData;
     341           0 :   nsresult rv = aTrackEncoder->GetEncodedTrack(encodedVideoData);
     342           0 :   if (NS_FAILED(rv)) {
     343             :     // Encoding might be canceled.
     344           0 :     LOG(LogLevel::Error, ("Error! Fail to get encoded data from video encoder."));
     345           0 :     mState = ENCODE_ERROR;
     346           0 :     return rv;
     347             :   }
     348             : 
     349             :   // Update timestamps to accommodate pauses
     350             :   const nsTArray<RefPtr<EncodedFrame> >& encodedFrames =
     351           0 :     encodedVideoData.GetEncodedFrames();
     352             :   // Take a copy of the atomic so we don't continually access it
     353           0 :   uint64_t microsecondsSpentPaused = mMicrosecondsSpentPaused;
     354           0 :   for (size_t i = 0; i < encodedFrames.Length(); ++i) {
     355           0 :     RefPtr<EncodedFrame> frame = encodedFrames[i];
     356           0 :     if (frame->GetTimeStamp() > microsecondsSpentPaused &&
     357           0 :         frame->GetTimeStamp() - microsecondsSpentPaused > mLastMuxedTimestamp) {
     358             :       // Use the adjusted timestamp if it's after the last timestamp
     359           0 :       frame->SetTimeStamp(frame->GetTimeStamp() - microsecondsSpentPaused);
     360             :     } else {
     361             :       // If not, we force the last time stamp. We do this so the frames are
     362             :       // still around and in order in case the codec needs to reference them.
     363             :       // Dropping them here may result in artifacts in playback.
     364           0 :       frame->SetTimeStamp(mLastMuxedTimestamp);
     365             :     }
     366           0 :     MOZ_ASSERT(mLastMuxedTimestamp <= frame->GetTimeStamp(),
     367             :       "Our frames should be ordered by this point!");
     368           0 :     mLastMuxedTimestamp = frame->GetTimeStamp();
     369             :   }
     370             : 
     371           0 :   rv = mWriter->WriteEncodedTrack(encodedVideoData,
     372           0 :                                   aTrackEncoder->IsEncodingComplete() ?
     373           0 :                                   ContainerWriter::END_OF_STREAM : 0);
     374           0 :   if (NS_FAILED(rv)) {
     375           0 :     LOG(LogLevel::Error, ("Error! Fail to write encoded video track to the media container."));
     376           0 :     mState = ENCODE_ERROR;
     377             :   }
     378           0 :   return rv;
     379             : }
     380             : 
     381             : nsresult
     382           0 : MediaEncoder::CopyMetadataToMuxer(TrackEncoder *aTrackEncoder)
     383             : {
     384           0 :   if (aTrackEncoder == nullptr) {
     385           0 :     return NS_OK;
     386             :   }
     387             : 
     388           0 :   AUTO_PROFILER_LABEL("MediaEncoder::CopyMetadataToMuxer", OTHER);
     389             : 
     390           0 :   RefPtr<TrackMetadataBase> meta = aTrackEncoder->GetMetadata();
     391           0 :   if (meta == nullptr) {
     392           0 :     LOG(LogLevel::Error, ("Error! metadata = null"));
     393           0 :     mState = ENCODE_ERROR;
     394           0 :     return NS_ERROR_ABORT;
     395             :   }
     396             : 
     397           0 :   nsresult rv = mWriter->SetMetadata(meta);
     398           0 :   if (NS_FAILED(rv)) {
     399           0 :    LOG(LogLevel::Error, ("Error! SetMetadata fail"));
     400           0 :    mState = ENCODE_ERROR;
     401             :   }
     402           0 :   return rv;
     403             : }
     404             : 
     405             : #ifdef MOZ_WEBM_ENCODER
     406             : bool
     407           0 : MediaEncoder::IsWebMEncoderEnabled()
     408             : {
     409           0 :   return Preferences::GetBool("media.encoder.webm.enabled");
     410             : }
     411             : #endif
     412             : 
     413             : /*
     414             :  * SizeOfExcludingThis measures memory being used by the Media Encoder.
     415             :  * Currently it measures the size of the Encoder buffer and memory occupied
     416             :  * by mAudioEncoder and mVideoEncoder.
     417             :  */
     418             : size_t
     419           0 : MediaEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     420             : {
     421           0 :   size_t amount = 0;
     422           0 :   if (mState == ENCODE_TRACK) {
     423           0 :     amount = mSizeOfBuffer +
     424           0 :              (mAudioEncoder != nullptr ? mAudioEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0) +
     425           0 :              (mVideoEncoder != nullptr ? mVideoEncoder->SizeOfExcludingThis(aMallocSizeOf) : 0);
     426             :   }
     427           0 :   return amount;
     428             : }
     429             : 
     430             : } // namespace mozilla

Generated by: LCOV version 1.13