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 :
6 : #ifndef MediaEncoder_h_
7 : #define MediaEncoder_h_
8 :
9 : #include "mozilla/DebugOnly.h"
10 : #include "TrackEncoder.h"
11 : #include "ContainerWriter.h"
12 : #include "CubebUtils.h"
13 : #include "MediaStreamGraph.h"
14 : #include "MediaStreamListener.h"
15 : #include "nsAutoPtr.h"
16 : #include "MediaStreamVideoSink.h"
17 : #include "nsIMemoryReporter.h"
18 : #include "mozilla/MemoryReporting.h"
19 : #include "mozilla/Atomics.h"
20 :
21 : namespace mozilla {
22 :
23 : class MediaStreamVideoRecorderSink : public MediaStreamVideoSink
24 : {
25 : public:
26 0 : explicit MediaStreamVideoRecorderSink(VideoTrackEncoder* aEncoder)
27 0 : : mVideoEncoder(aEncoder)
28 0 : , mSuspended(false) {}
29 :
30 : // MediaStreamVideoSink methods
31 : virtual void SetCurrentFrames(const VideoSegment& aSegment) override;
32 0 : virtual void ClearFrames() override {}
33 :
34 0 : void Resume() { mSuspended = false; }
35 0 : void Suspend() { mSuspended = true; }
36 :
37 : private:
38 0 : virtual ~MediaStreamVideoRecorderSink() {}
39 : VideoTrackEncoder* mVideoEncoder;
40 : Atomic<bool> mSuspended;
41 : };
42 :
43 : /**
44 : * MediaEncoder is the framework of encoding module, it controls and manages
45 : * procedures between ContainerWriter and TrackEncoder. ContainerWriter packs
46 : * the encoded track data with a specific container (e.g. ogg, mp4).
47 : * AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and
48 : * are responsible for encoding raw data coming from MediaStreamGraph.
49 : *
50 : * Also, MediaEncoder is a type of MediaStreamListener, it starts to receive raw
51 : * segments after itself is added to the source stream. In the mean time,
52 : * encoded track data is pulled by its owner periodically on a worker thread. A
53 : * reentrant monitor is used to protect the push and pull of resource.
54 : *
55 : * MediaEncoder is designed to be a passive component, neither it owns nor in
56 : * charge of managing threads. However, a monitor is used in function
57 : * TrackEncoder::GetEncodedTrack() for the purpose of thread safety (e.g.
58 : * between callbacks of MediaStreamListener and others), a call to this function
59 : * might block. Therefore, MediaEncoder should not run on threads that forbid
60 : * blocking, such as main thread or I/O thread.
61 : *
62 : * For example, an usage from MediaRecorder of this component would be:
63 : * 1) Create an encoder with a valid MIME type.
64 : * => encoder = MediaEncoder::CreateEncoder(aMIMEType);
65 : * It then generate a ContainerWriter according to the MIME type, and an
66 : * AudioTrackEncoder (or a VideoTrackEncoder too) associated with the media
67 : * type.
68 : *
69 : * 2) Dispatch the task GetEncodedData() to a worker thread.
70 : *
71 : * 3) To start encoding, add this component to its source stream.
72 : * => sourceStream->AddListener(encoder);
73 : *
74 : * 4) To stop encoding, remove this component from its source stream.
75 : * => sourceStream->RemoveListener(encoder);
76 : */
77 : class MediaEncoder : public DirectMediaStreamListener
78 : {
79 : friend class MediaStreamVideoRecorderSink;
80 : public :
81 : enum {
82 : ENCODE_METADDATA,
83 : ENCODE_TRACK,
84 : ENCODE_DONE,
85 : ENCODE_ERROR,
86 : };
87 :
88 0 : MediaEncoder(ContainerWriter* aWriter,
89 : AudioTrackEncoder* aAudioEncoder,
90 : VideoTrackEncoder* aVideoEncoder,
91 : const nsAString& aMIMEType,
92 : uint32_t aAudioBitrate,
93 : uint32_t aVideoBitrate,
94 : uint32_t aBitrate)
95 0 : : mWriter(aWriter)
96 : , mAudioEncoder(aAudioEncoder)
97 : , mVideoEncoder(aVideoEncoder)
98 0 : , mVideoSink(new MediaStreamVideoRecorderSink(mVideoEncoder))
99 : , mStartTime(TimeStamp::Now())
100 : , mMIMEType(aMIMEType)
101 : , mSizeOfBuffer(0)
102 : , mState(MediaEncoder::ENCODE_METADDATA)
103 : , mShutdown(false)
104 : , mDirectConnected(false)
105 : , mSuspended(false)
106 : , mMicrosecondsSpentPaused(0)
107 0 : , mLastMuxedTimestamp(0)
108 0 : {}
109 :
110 0 : ~MediaEncoder() {};
111 :
112 : /* Note - called from control code, not on MSG threads. */
113 : void Suspend();
114 :
115 : /**
116 : * Note - called from control code, not on MSG threads.
117 : * Calculates time spent paused in order to offset frames. */
118 : void Resume();
119 :
120 : /**
121 : * Tells us which Notify to pay attention to for media
122 : */
123 : void SetDirectConnect(bool aConnected);
124 :
125 : /**
126 : * Notified by the AppendToTrack in MediaStreamGraph; aRealtimeMedia is the raw
127 : * track data in form of MediaSegment.
128 : */
129 : void NotifyRealtimeData(MediaStreamGraph* aGraph, TrackID aID,
130 : StreamTime aTrackOffset,
131 : uint32_t aTrackEvents,
132 : const MediaSegment& aRealtimeMedia) override;
133 :
134 : /**
135 : * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw
136 : * track data in form of MediaSegment.
137 : */
138 : void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID,
139 : StreamTime aTrackOffset,
140 : TrackEventCommand aTrackEvents,
141 : const MediaSegment& aQueuedMedia,
142 : MediaStream* aInputStream,
143 : TrackID aInputTrackID) override;
144 :
145 : /**
146 : * Notifed by the control loop of MediaStreamGraph; aQueueMedia is the audio
147 : * data in the form of an AudioSegment.
148 : */
149 : void NotifyQueuedAudioData(MediaStreamGraph* aGraph, TrackID aID,
150 : StreamTime aTrackOffset,
151 : const AudioSegment& aQueuedMedia,
152 : MediaStream* aInputStream,
153 : TrackID aInputTrackID) override;
154 :
155 : /**
156 : * * Notified the stream is being removed.
157 : */
158 : void NotifyEvent(MediaStreamGraph* aGraph,
159 : MediaStreamGraphEvent event) override;
160 :
161 : /**
162 : * Creates an encoder with a given MIME type. Returns null if we are unable
163 : * to create the encoder. For now, default aMIMEType to "audio/ogg" and use
164 : * Ogg+Opus if it is empty.
165 : */
166 : static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType,
167 : uint32_t aAudioBitrate, uint32_t aVideoBitrate,
168 : uint32_t aBitrate,
169 : uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK,
170 : TrackRate aTrackRate = CubebUtils::PreferredSampleRate());
171 : /**
172 : * Encodes the raw track data and returns the final container data. Assuming
173 : * it is called on a single worker thread. The buffer of container data is
174 : * allocated in ContainerWriter::GetContainerData(), and is appended to
175 : * aOutputBufs. aMIMEType is the valid mime-type of this returned container
176 : * data.
177 : */
178 : void GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs,
179 : nsAString& aMIMEType);
180 :
181 : /**
182 : * Return true if MediaEncoder has been shutdown. Reasons are encoding
183 : * complete, encounter an error, or being canceled by its caller.
184 : */
185 0 : bool IsShutdown()
186 : {
187 0 : return mShutdown;
188 : }
189 :
190 : /**
191 : * Cancel the encoding, and wakes up the lock of reentrant monitor in encoder.
192 : */
193 0 : void Cancel()
194 : {
195 0 : if (mAudioEncoder) {
196 0 : mAudioEncoder->NotifyCancel();
197 : }
198 0 : if (mVideoEncoder) {
199 0 : mVideoEncoder->NotifyCancel();
200 : }
201 0 : }
202 :
203 0 : bool HasError()
204 : {
205 0 : return mState == ENCODE_ERROR;
206 : }
207 :
208 : #ifdef MOZ_WEBM_ENCODER
209 : static bool IsWebMEncoderEnabled();
210 : #endif
211 :
212 0 : MOZ_DEFINE_MALLOC_SIZE_OF(MallocSizeOf)
213 : /*
214 : * Measure the size of the buffer, and memory occupied by mAudioEncoder
215 : * and mVideoEncoder
216 : */
217 : size_t SizeOfExcludingThis(mozilla::MallocSizeOf aMallocSizeOf) const;
218 :
219 0 : MediaStreamVideoRecorderSink* GetVideoSink() {
220 0 : return mVideoSink.get();
221 : }
222 :
223 : private:
224 : // Get encoded data from trackEncoder and write to muxer
225 : nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder);
226 : // Get metadata from trackEncoder and copy to muxer
227 : nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder);
228 : nsAutoPtr<ContainerWriter> mWriter;
229 : nsAutoPtr<AudioTrackEncoder> mAudioEncoder;
230 : nsAutoPtr<VideoTrackEncoder> mVideoEncoder;
231 : RefPtr<MediaStreamVideoRecorderSink> mVideoSink;
232 : TimeStamp mStartTime;
233 : nsString mMIMEType;
234 : int64_t mSizeOfBuffer;
235 : int mState;
236 : bool mShutdown;
237 : bool mDirectConnected;
238 : // Tracks if the encoder is suspended (paused). Used on the main thread and
239 : // MediaRecorder's read thread.
240 : Atomic<bool> mSuspended;
241 : // Timestamp of when the last pause happened. Should only be accessed on the
242 : // main thread.
243 : TimeStamp mLastPauseStartTime;
244 : // Exposes the time spend paused in microseconds. Read by the main thread
245 : // and MediaRecorder's read thread. Should only be written by main thread.
246 : Atomic<uint64_t> mMicrosecondsSpentPaused;
247 : // The timestamp of the last muxed sample. Should only be used on
248 : // MediaRecorder's read thread.
249 : uint64_t mLastMuxedTimestamp;
250 : // Get duration from create encoder, for logging purpose
251 0 : double GetEncodeTimeStamp()
252 : {
253 0 : TimeDuration decodeTime;
254 0 : decodeTime = TimeStamp::Now() - mStartTime;
255 0 : return decodeTime.ToMilliseconds();
256 : }
257 : };
258 :
259 : } // namespace mozilla
260 :
261 : #endif
|