LCOV - code coverage report
Current view: top level - dom/media/encoder - TrackEncoder.cpp (source / functions) Hit Total Coverage
Test: output.info Lines: 0 198 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 "TrackEncoder.h"
       6             : #include "AudioChannelFormat.h"
       7             : #include "MediaStreamGraph.h"
       8             : #include "MediaStreamListener.h"
       9             : #include "mozilla/Logging.h"
      10             : #include "VideoUtils.h"
      11             : #include "mozilla/Logging.h"
      12             : 
      13             : namespace mozilla {
      14             : 
      15             : LazyLogModule gTrackEncoderLog("TrackEncoder");
      16             : #define TRACK_LOG(type, msg) MOZ_LOG(gTrackEncoderLog, type, msg)
      17             : 
      18             : static const int DEFAULT_CHANNELS = 1;
      19             : static const int DEFAULT_SAMPLING_RATE = 16000;
      20             : static const int DEFAULT_FRAME_WIDTH = 640;
      21             : static const int DEFAULT_FRAME_HEIGHT = 480;
      22             : static const int DEFAULT_TRACK_RATE = USECS_PER_S;
      23             : // 30 seconds threshold if the encoder still can't not be initialized.
      24             : static const int INIT_FAILED_DURATION = 30;
      25             : 
      26           0 : TrackEncoder::TrackEncoder()
      27             :   : mReentrantMonitor("media.TrackEncoder")
      28             :   , mEncodingComplete(false)
      29             :   , mEosSetInEncoder(false)
      30             :   , mInitialized(false)
      31             :   , mEndOfStream(false)
      32             :   , mCanceled(false)
      33             :   , mInitCounter(0)
      34           0 :   , mNotInitDuration(0)
      35             : {
      36           0 : }
      37             : 
      38           0 : void TrackEncoder::NotifyEvent(MediaStreamGraph* aGraph,
      39             :                  MediaStreamGraphEvent event)
      40             : {
      41           0 :   if (event == MediaStreamGraphEvent::EVENT_REMOVED) {
      42           0 :     NotifyEndOfStream();
      43             :   }
      44           0 : }
      45             : 
      46             : void
      47           0 : AudioTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
      48             :                                             TrackID aID,
      49             :                                             StreamTime aTrackOffset,
      50             :                                             uint32_t aTrackEvents,
      51             :                                             const MediaSegment& aQueuedMedia)
      52             : {
      53           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
      54             : 
      55           0 :   if (mCanceled) {
      56           0 :     return;
      57             :   }
      58             : 
      59           0 :   const AudioSegment& audio = static_cast<const AudioSegment&>(aQueuedMedia);
      60             : 
      61             :   // Check and initialize parameters for codec encoder.
      62           0 :   if (!mInitialized) {
      63           0 :     mInitCounter++;
      64           0 :     TRACK_LOG(LogLevel::Debug, ("Init the audio encoder %d times", mInitCounter));
      65           0 :     AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(audio));
      66           0 :     while (!iter.IsEnded()) {
      67           0 :       AudioChunk chunk = *iter;
      68             : 
      69             :       // The number of channels is determined by the first non-null chunk, and
      70             :       // thus the audio encoder is initialized at this time.
      71           0 :       if (!chunk.IsNull()) {
      72           0 :         nsresult rv = Init(chunk.mChannelData.Length(), aGraph->GraphRate());
      73           0 :         if (NS_FAILED(rv)) {
      74           0 :           TRACK_LOG(LogLevel::Error, ("[AudioTrackEncoder]: Fail to initialize the encoder!"));
      75           0 :           NotifyCancel();
      76             :         }
      77           0 :         break;
      78             :       }
      79             : 
      80           0 :       iter.Next();
      81             :     }
      82             : 
      83           0 :     mNotInitDuration += aQueuedMedia.GetDuration();
      84           0 :     if (!mInitialized &&
      85           0 :         (mNotInitDuration / aGraph->GraphRate() > INIT_FAILED_DURATION) &&
      86           0 :         mInitCounter > 1) {
      87           0 :       TRACK_LOG(LogLevel::Warning, ("[AudioTrackEncoder]: Initialize failed for 30s."));
      88           0 :       NotifyEndOfStream();
      89           0 :       return;
      90             :     }
      91             :   }
      92             : 
      93             :   // Append and consume this raw segment.
      94           0 :   AppendAudioSegment(audio);
      95             : 
      96             : 
      97             :   // The stream has stopped and reached the end of track.
      98           0 :   if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
      99           0 :     TRACK_LOG(LogLevel::Info, ("[AudioTrackEncoder]: Receive TRACK_EVENT_ENDED ."));
     100           0 :     NotifyEndOfStream();
     101             :   }
     102             : }
     103             : 
     104             : void
     105           0 : AudioTrackEncoder::NotifyEndOfStream()
     106             : {
     107           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     108             : 
     109             :   // If source audio track is completely silent till the end of encoding,
     110             :   // initialize the encoder with default channel counts and sampling rate.
     111           0 :   if (!mCanceled && !mInitialized) {
     112           0 :     Init(DEFAULT_CHANNELS, DEFAULT_SAMPLING_RATE);
     113             :   }
     114             : 
     115           0 :   mEndOfStream = true;
     116           0 :   mReentrantMonitor.NotifyAll();
     117           0 : }
     118             : 
     119             : nsresult
     120           0 : AudioTrackEncoder::AppendAudioSegment(const AudioSegment& aSegment)
     121             : {
     122           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     123             : 
     124           0 :   AudioSegment::ChunkIterator iter(const_cast<AudioSegment&>(aSegment));
     125           0 :   while (!iter.IsEnded()) {
     126           0 :     AudioChunk chunk = *iter;
     127             :     // Append and consume both non-null and null chunks.
     128           0 :     mRawSegment.AppendAndConsumeChunk(&chunk);
     129           0 :     iter.Next();
     130             :   }
     131             : 
     132           0 :   if (mRawSegment.GetDuration() >= GetPacketDuration()) {
     133           0 :     mReentrantMonitor.NotifyAll();
     134             :   }
     135             : 
     136           0 :   return NS_OK;
     137             : }
     138             : 
     139             : /*static*/
     140             : void
     141           0 : AudioTrackEncoder::InterleaveTrackData(AudioChunk& aChunk,
     142             :                                        int32_t aDuration,
     143             :                                        uint32_t aOutputChannels,
     144             :                                        AudioDataValue* aOutput)
     145             : {
     146             :   uint32_t numChannelsToCopy = std::min(aOutputChannels,
     147           0 :                                         static_cast<uint32_t>(aChunk.mChannelData.Length()));
     148           0 :   switch(aChunk.mBufferFormat) {
     149             :     case AUDIO_FORMAT_S16: {
     150           0 :       AutoTArray<const int16_t*, 2> array;
     151           0 :       array.SetLength(numChannelsToCopy);
     152           0 :       for (uint32_t i = 0; i < array.Length(); i++) {
     153           0 :         array[i] = static_cast<const int16_t*>(aChunk.mChannelData[i]);
     154             :       }
     155           0 :       InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
     156           0 :       break;
     157             :     }
     158             :     case AUDIO_FORMAT_FLOAT32: {
     159           0 :       AutoTArray<const float*, 2> array;
     160           0 :       array.SetLength(numChannelsToCopy);
     161           0 :       for (uint32_t i = 0; i < array.Length(); i++) {
     162           0 :         array[i] = static_cast<const float*>(aChunk.mChannelData[i]);
     163             :       }
     164           0 :       InterleaveTrackData(array, aDuration, aOutputChannels, aOutput, aChunk.mVolume);
     165           0 :       break;
     166             :    }
     167             :    case AUDIO_FORMAT_SILENCE: {
     168           0 :       MOZ_ASSERT(false, "To implement.");
     169             :     }
     170             :   };
     171           0 : }
     172             : 
     173             : /*static*/
     174             : void
     175           0 : AudioTrackEncoder::DeInterleaveTrackData(AudioDataValue* aInput,
     176             :                                          int32_t aDuration,
     177             :                                          int32_t aChannels,
     178             :                                          AudioDataValue* aOutput)
     179             : {
     180           0 :   for (int32_t i = 0; i < aChannels; ++i) {
     181           0 :     for(int32_t j = 0; j < aDuration; ++j) {
     182           0 :       aOutput[i * aDuration + j] = aInput[i + j * aChannels];
     183             :     }
     184             :   }
     185           0 : }
     186             : 
     187             : size_t
     188           0 : AudioTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     189             : {
     190           0 :   return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
     191             : }
     192             : 
     193             : void
     194           0 : VideoTrackEncoder::Init(const VideoSegment& aSegment)
     195             : {
     196           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     197             : 
     198           0 :   if (mInitialized) {
     199           0 :     return;
     200             :   }
     201             : 
     202           0 :   mInitCounter++;
     203           0 :   TRACK_LOG(LogLevel::Debug, ("Init the video encoder %d times", mInitCounter));
     204           0 :   VideoSegment::ConstChunkIterator iter(aSegment);
     205           0 :   while (!iter.IsEnded()) {
     206           0 :    VideoChunk chunk = *iter;
     207           0 :    if (!chunk.IsNull()) {
     208           0 :      gfx::IntSize imgsize = chunk.mFrame.GetImage()->GetSize();
     209           0 :      gfx::IntSize intrinsicSize = chunk.mFrame.GetIntrinsicSize();
     210           0 :      nsresult rv = Init(imgsize.width, imgsize.height,
     211           0 :                         intrinsicSize.width, intrinsicSize.height);
     212             : 
     213           0 :      if (NS_FAILED(rv)) {
     214           0 :        TRACK_LOG(LogLevel::Error, ("[VideoTrackEncoder]: Fail to initialize the encoder!"));
     215           0 :        NotifyCancel();
     216             :      }
     217           0 :      break;
     218             :    }
     219             : 
     220           0 :    iter.Next();
     221             :   }
     222             : 
     223           0 :   mNotInitDuration += aSegment.GetDuration();
     224           0 :   if ((mNotInitDuration / mTrackRate > INIT_FAILED_DURATION) &&
     225           0 :       mInitCounter > 1) {
     226           0 :     TRACK_LOG(LogLevel::Debug, ("[VideoTrackEncoder]: Initialize failed for %ds.", INIT_FAILED_DURATION));
     227           0 :     NotifyEndOfStream();
     228           0 :     return;
     229             :   }
     230             : }
     231             : 
     232             : void
     233           0 : VideoTrackEncoder::SetCurrentFrames(const VideoSegment& aSegment)
     234             : {
     235           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     236             : 
     237           0 :   if (mCanceled) {
     238           0 :     return;
     239             :   }
     240             : 
     241           0 :   Init(aSegment);
     242           0 :   AppendVideoSegment(aSegment);
     243             : }
     244             : 
     245             : void
     246           0 : VideoTrackEncoder::NotifyQueuedTrackChanges(MediaStreamGraph* aGraph,
     247             :                                             TrackID aID,
     248             :                                             StreamTime aTrackOffset,
     249             :                                             uint32_t aTrackEvents,
     250             :                                             const MediaSegment& aQueuedMedia)
     251             : {
     252           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     253             : 
     254           0 :   if (mCanceled) {
     255           0 :     return;
     256             :   }
     257             : 
     258           0 :   if (!(aTrackEvents == TRACK_EVENT_CREATED ||
     259             :        aTrackEvents == TRACK_EVENT_ENDED)) {
     260           0 :     return;
     261             :   }
     262             : 
     263           0 :   const VideoSegment& video = static_cast<const VideoSegment&>(aQueuedMedia);
     264             : 
     265             :    // Check and initialize parameters for codec encoder.
     266           0 :   Init(video);
     267             : 
     268           0 :   AppendVideoSegment(video);
     269             : 
     270             :   // The stream has stopped and reached the end of track.
     271           0 :   if (aTrackEvents == TrackEventCommand::TRACK_EVENT_ENDED) {
     272           0 :     TRACK_LOG(LogLevel::Info, ("[VideoTrackEncoder]: Receive TRACK_EVENT_ENDED ."));
     273           0 :     NotifyEndOfStream();
     274             :   }
     275             : 
     276             : }
     277             : 
     278             : nsresult
     279           0 : VideoTrackEncoder::AppendVideoSegment(const VideoSegment& aSegment)
     280             : {
     281           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     282             : 
     283           0 :   if (mEndOfStream) {
     284           0 :     MOZ_ASSERT(false);
     285             :     return NS_OK;
     286             :   }
     287             : 
     288             :   // Append all video segments from MediaStreamGraph, including null an
     289             :   // non-null frames.
     290           0 :   VideoSegment::ConstChunkIterator iter(aSegment);
     291           0 :   for (; !iter.IsEnded(); iter.Next()) {
     292           0 :     VideoChunk chunk = *iter;
     293             : 
     294           0 :     if (mLastChunk.mTimeStamp.IsNull()) {
     295           0 :       if (chunk.IsNull()) {
     296             :         // The start of this track is frameless. We need to track the time
     297             :         // it takes to get the first frame.
     298           0 :         mLastChunk.mDuration += chunk.mDuration;
     299           0 :         continue;
     300             :       }
     301             : 
     302             :       // This is the first real chunk in the track. Use its timestamp as the
     303             :       // starting point for this track.
     304           0 :       MOZ_ASSERT(!chunk.mTimeStamp.IsNull());
     305           0 :       const StreamTime nullDuration = mLastChunk.mDuration;
     306           0 :       mLastChunk = chunk;
     307           0 :       chunk.mDuration = 0;
     308             : 
     309           0 :       TRACK_LOG(LogLevel::Verbose,
     310             :                 ("[VideoTrackEncoder]: Got first video chunk after %" PRId64 " ticks.",
     311             :                  nullDuration));
     312             :       // Adapt to the time before the first frame. This extends the first frame
     313             :       // from [start, end] to [0, end], but it'll do for now.
     314           0 :       auto diff = FramesToTimeUnit(nullDuration, mTrackRate);
     315           0 :       if (!diff.IsValid()) {
     316           0 :         NS_ERROR("null duration overflow");
     317           0 :         return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     318             :       }
     319             : 
     320           0 :       mLastChunk.mTimeStamp -= diff.ToTimeDuration();
     321           0 :       mLastChunk.mDuration += nullDuration;
     322             :     }
     323             : 
     324           0 :     MOZ_ASSERT(!mLastChunk.IsNull());
     325           0 :     if (mLastChunk.CanCombineWithFollowing(chunk) || chunk.IsNull()) {
     326           0 :       TRACK_LOG(LogLevel::Verbose,
     327             :                 ("[VideoTrackEncoder]: Got dupe or null chunk."));
     328             :       // This is the same frame as before (or null). We extend the last chunk
     329             :       // with its duration.
     330           0 :       mLastChunk.mDuration += chunk.mDuration;
     331             : 
     332           0 :       if (mLastChunk.mDuration < mTrackRate) {
     333           0 :         TRACK_LOG(LogLevel::Verbose,
     334             :                   ("[VideoTrackEncoder]: Ignoring dupe/null chunk of duration %" PRId64,
     335             :                    chunk.mDuration));
     336           0 :         continue;
     337             :       }
     338             : 
     339           0 :       TRACK_LOG(LogLevel::Verbose,
     340             :                 ("[VideoTrackEncoder]: Chunk >1 second. duration=%" PRId64 ", "
     341             :                  "trackRate=%" PRId32, mLastChunk.mDuration, mTrackRate));
     342             : 
     343             :       // If we have gotten dupes for over a second, we force send one
     344             :       // to the encoder to make sure there is some output.
     345           0 :       chunk.mTimeStamp = mLastChunk.mTimeStamp + TimeDuration::FromSeconds(1);
     346             : 
     347             :       // chunk's duration has already been accounted for.
     348           0 :       chunk.mDuration = 0;
     349             : 
     350           0 :       if (chunk.IsNull()) {
     351             :         // Ensure that we don't pass null to the encoder by making mLastChunk
     352             :         // null later on.
     353           0 :         chunk.mFrame = mLastChunk.mFrame;
     354             :       }
     355             :     }
     356             : 
     357           0 :     if (mStartOffset.IsNull()) {
     358           0 :       mStartOffset = mLastChunk.mTimeStamp;
     359             :     }
     360             : 
     361           0 :     TimeDuration relativeTime = chunk.mTimeStamp - mStartOffset;
     362           0 :     RefPtr<layers::Image> lastImage = mLastChunk.mFrame.GetImage();
     363           0 :     TRACK_LOG(LogLevel::Verbose,
     364             :               ("[VideoTrackEncoder]: Appending video frame %p, at pos %.5fs",
     365             :                lastImage.get(), relativeTime.ToSeconds()));
     366             :     CheckedInt64 totalDuration =
     367           0 :       UsecsToFrames(relativeTime.ToMicroseconds(), mTrackRate);
     368           0 :     if (!totalDuration.isValid()) {
     369           0 :       NS_ERROR("Duration overflow");
     370           0 :       return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     371             :     }
     372             : 
     373           0 :     CheckedInt64 duration = totalDuration - mEncodedTicks;
     374           0 :     if (!duration.isValid()) {
     375           0 :       NS_ERROR("Duration overflow");
     376           0 :       return NS_ERROR_DOM_MEDIA_OVERFLOW_ERR;
     377             :     }
     378             : 
     379           0 :     if (duration.isValid()) {
     380           0 :       if (duration.value() <= 0) {
     381             :         // The timestamp for mLastChunk is newer than for chunk.
     382             :         // This means the durations reported from MediaStreamGraph for
     383             :         // mLastChunk were larger than the timestamp diff - and durations were
     384             :         // used to trigger the 1-second frame above. This could happen due to
     385             :         // drift or underruns in the graph.
     386           0 :         TRACK_LOG(LogLevel::Warning,
     387             :                   ("[VideoTrackEncoder]: Underrun detected. Diff=%" PRId64,
     388             :                    duration.value()));
     389           0 :         chunk.mTimeStamp = mLastChunk.mTimeStamp;
     390             :       } else {
     391           0 :         mEncodedTicks += duration.value();
     392           0 :         mRawSegment.AppendFrame(lastImage.forget(),
     393             :                                 duration.value(),
     394           0 :                                 mLastChunk.mFrame.GetIntrinsicSize(),
     395             :                                 PRINCIPAL_HANDLE_NONE,
     396           0 :                                 mLastChunk.mFrame.GetForceBlack(),
     397           0 :                                 mLastChunk.mTimeStamp);
     398             :       }
     399             :     }
     400             : 
     401           0 :     mLastChunk = chunk;
     402             :   }
     403             : 
     404           0 :   if (mRawSegment.GetDuration() > 0) {
     405           0 :     mReentrantMonitor.NotifyAll();
     406             :   }
     407             : 
     408           0 :   return NS_OK;
     409             : }
     410             : 
     411             : void
     412           0 : VideoTrackEncoder::NotifyEndOfStream()
     413             : {
     414           0 :   ReentrantMonitorAutoEnter mon(mReentrantMonitor);
     415             : 
     416             :   // If source video track is muted till the end of encoding, initialize the
     417             :   // encoder with default frame width, frame height, and track rate.
     418           0 :   if (!mCanceled && !mInitialized) {
     419             :     Init(DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT,
     420           0 :          DEFAULT_FRAME_WIDTH, DEFAULT_FRAME_HEIGHT);
     421             :   }
     422             : 
     423           0 :   if (mEndOfStream) {
     424             :     // We have already been notified.
     425           0 :     return;
     426             :   }
     427             : 
     428           0 :   mEndOfStream = true;
     429           0 :   TRACK_LOG(LogLevel::Info, ("[VideoTrackEncoder]: Reached end of stream"));
     430             : 
     431           0 :   if (!mLastChunk.IsNull() && mLastChunk.mDuration > 0) {
     432           0 :     RefPtr<layers::Image> lastImage = mLastChunk.mFrame.GetImage();
     433           0 :     TRACK_LOG(LogLevel::Debug,
     434             :               ("[VideoTrackEncoder]: Appending last video frame %p, "
     435             :                "duration=%.5f", lastImage.get(),
     436             :                FramesToTimeUnit(mLastChunk.mDuration, mTrackRate).ToSeconds()));
     437           0 :     mRawSegment.AppendFrame(lastImage.forget(),
     438             :                             mLastChunk.mDuration,
     439           0 :                             mLastChunk.mFrame.GetIntrinsicSize(),
     440             :                             PRINCIPAL_HANDLE_NONE,
     441           0 :                             mLastChunk.mFrame.GetForceBlack(),
     442           0 :                             mLastChunk.mTimeStamp);
     443             :   }
     444           0 :   mReentrantMonitor.NotifyAll();
     445             : }
     446             : 
     447             : size_t
     448           0 : VideoTrackEncoder::SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const
     449             : {
     450           0 :   return mRawSegment.SizeOfExcludingThis(aMallocSizeOf);
     451             : }
     452             : 
     453             : } // namespace mozilla

Generated by: LCOV version 1.13