Thu, 22 Jan 2015 13:21:57 +0100
Incorporate requested changes from Mozilla in review:
https://bugzilla.mozilla.org/show_bug.cgi?id=1123480#c6
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 | |
michael@0 | 6 | #ifndef MediaEncoder_h_ |
michael@0 | 7 | #define MediaEncoder_h_ |
michael@0 | 8 | |
michael@0 | 9 | #include "mozilla/DebugOnly.h" |
michael@0 | 10 | #include "TrackEncoder.h" |
michael@0 | 11 | #include "ContainerWriter.h" |
michael@0 | 12 | #include "MediaStreamGraph.h" |
michael@0 | 13 | |
michael@0 | 14 | namespace mozilla { |
michael@0 | 15 | |
michael@0 | 16 | /** |
michael@0 | 17 | * MediaEncoder is the framework of encoding module, it controls and manages |
michael@0 | 18 | * procedures between ContainerWriter and TrackEncoder. ContainerWriter packs |
michael@0 | 19 | * the encoded track data with a specific container (e.g. ogg, mp4). |
michael@0 | 20 | * AudioTrackEncoder and VideoTrackEncoder are subclasses of TrackEncoder, and |
michael@0 | 21 | * are responsible for encoding raw data coming from MediaStreamGraph. |
michael@0 | 22 | * |
michael@0 | 23 | * Also, MediaEncoder is a type of MediaStreamListener, it starts to receive raw |
michael@0 | 24 | * segments after itself is added to the source stream. In the mean time, |
michael@0 | 25 | * encoded track data is pulled by its owner periodically on a worker thread. A |
michael@0 | 26 | * reentrant monitor is used to protect the push and pull of resource. |
michael@0 | 27 | * |
michael@0 | 28 | * MediaEncoder is designed to be a passive component, neither it owns nor in |
michael@0 | 29 | * charge of managing threads. However, a monitor is used in function |
michael@0 | 30 | * TrackEncoder::GetEncodedTrack() for the purpose of thread safety (e.g. |
michael@0 | 31 | * between callbacks of MediaStreamListener and others), a call to this function |
michael@0 | 32 | * might block. Therefore, MediaEncoder should not run on threads that forbid |
michael@0 | 33 | * blocking, such as main thread or I/O thread. |
michael@0 | 34 | * |
michael@0 | 35 | * For example, an usage from MediaRecorder of this component would be: |
michael@0 | 36 | * 1) Create an encoder with a valid MIME type. |
michael@0 | 37 | * => encoder = MediaEncoder::CreateEncoder(aMIMEType); |
michael@0 | 38 | * It then generate a ContainerWriter according to the MIME type, and an |
michael@0 | 39 | * AudioTrackEncoder (or a VideoTrackEncoder too) associated with the media |
michael@0 | 40 | * type. |
michael@0 | 41 | * |
michael@0 | 42 | * 2) Dispatch the task GetEncodedData() to a worker thread. |
michael@0 | 43 | * |
michael@0 | 44 | * 3) To start encoding, add this component to its source stream. |
michael@0 | 45 | * => sourceStream->AddListener(encoder); |
michael@0 | 46 | * |
michael@0 | 47 | * 4) To stop encoding, remove this component from its source stream. |
michael@0 | 48 | * => sourceStream->RemoveListener(encoder); |
michael@0 | 49 | */ |
michael@0 | 50 | class MediaEncoder : public MediaStreamListener |
michael@0 | 51 | { |
michael@0 | 52 | public : |
michael@0 | 53 | enum { |
michael@0 | 54 | ENCODE_METADDATA, |
michael@0 | 55 | ENCODE_TRACK, |
michael@0 | 56 | ENCODE_DONE, |
michael@0 | 57 | ENCODE_ERROR, |
michael@0 | 58 | }; |
michael@0 | 59 | |
michael@0 | 60 | MediaEncoder(ContainerWriter* aWriter, |
michael@0 | 61 | AudioTrackEncoder* aAudioEncoder, |
michael@0 | 62 | VideoTrackEncoder* aVideoEncoder, |
michael@0 | 63 | const nsAString& aMIMEType) |
michael@0 | 64 | : mWriter(aWriter) |
michael@0 | 65 | , mAudioEncoder(aAudioEncoder) |
michael@0 | 66 | , mVideoEncoder(aVideoEncoder) |
michael@0 | 67 | , mStartTime(TimeStamp::Now()) |
michael@0 | 68 | , mMIMEType(aMIMEType) |
michael@0 | 69 | , mState(MediaEncoder::ENCODE_METADDATA) |
michael@0 | 70 | , mShutdown(false) |
michael@0 | 71 | {} |
michael@0 | 72 | |
michael@0 | 73 | ~MediaEncoder() {}; |
michael@0 | 74 | |
michael@0 | 75 | /** |
michael@0 | 76 | * Notified by the control loop of MediaStreamGraph; aQueueMedia is the raw |
michael@0 | 77 | * track data in form of MediaSegment. |
michael@0 | 78 | */ |
michael@0 | 79 | virtual void NotifyQueuedTrackChanges(MediaStreamGraph* aGraph, TrackID aID, |
michael@0 | 80 | TrackRate aTrackRate, |
michael@0 | 81 | TrackTicks aTrackOffset, |
michael@0 | 82 | uint32_t aTrackEvents, |
michael@0 | 83 | const MediaSegment& aQueuedMedia); |
michael@0 | 84 | |
michael@0 | 85 | /** |
michael@0 | 86 | * Notified the stream is being removed. |
michael@0 | 87 | */ |
michael@0 | 88 | virtual void NotifyRemoved(MediaStreamGraph* aGraph); |
michael@0 | 89 | |
michael@0 | 90 | /** |
michael@0 | 91 | * Creates an encoder with a given MIME type. Returns null if we are unable |
michael@0 | 92 | * to create the encoder. For now, default aMIMEType to "audio/ogg" and use |
michael@0 | 93 | * Ogg+Opus if it is empty. |
michael@0 | 94 | */ |
michael@0 | 95 | static already_AddRefed<MediaEncoder> CreateEncoder(const nsAString& aMIMEType, |
michael@0 | 96 | uint8_t aTrackTypes = ContainerWriter::CREATE_AUDIO_TRACK); |
michael@0 | 97 | /** |
michael@0 | 98 | * Encodes the raw track data and returns the final container data. Assuming |
michael@0 | 99 | * it is called on a single worker thread. The buffer of container data is |
michael@0 | 100 | * allocated in ContainerWriter::GetContainerData(), and is appended to |
michael@0 | 101 | * aOutputBufs. aMIMEType is the valid mime-type of this returned container |
michael@0 | 102 | * data. |
michael@0 | 103 | */ |
michael@0 | 104 | void GetEncodedData(nsTArray<nsTArray<uint8_t> >* aOutputBufs, |
michael@0 | 105 | nsAString& aMIMEType); |
michael@0 | 106 | |
michael@0 | 107 | /** |
michael@0 | 108 | * Return true if MediaEncoder has been shutdown. Reasons are encoding |
michael@0 | 109 | * complete, encounter an error, or being canceled by its caller. |
michael@0 | 110 | */ |
michael@0 | 111 | bool IsShutdown() |
michael@0 | 112 | { |
michael@0 | 113 | return mShutdown; |
michael@0 | 114 | } |
michael@0 | 115 | |
michael@0 | 116 | /** |
michael@0 | 117 | * Cancel the encoding, and wakes up the lock of reentrant monitor in encoder. |
michael@0 | 118 | */ |
michael@0 | 119 | void Cancel() |
michael@0 | 120 | { |
michael@0 | 121 | if (mAudioEncoder) { |
michael@0 | 122 | mAudioEncoder->NotifyCancel(); |
michael@0 | 123 | } |
michael@0 | 124 | if (mVideoEncoder) { |
michael@0 | 125 | mVideoEncoder->NotifyCancel(); |
michael@0 | 126 | } |
michael@0 | 127 | } |
michael@0 | 128 | |
michael@0 | 129 | bool HasError() |
michael@0 | 130 | { |
michael@0 | 131 | return mState == ENCODE_ERROR; |
michael@0 | 132 | } |
michael@0 | 133 | |
michael@0 | 134 | #ifdef MOZ_WEBM_ENCODER |
michael@0 | 135 | static bool IsWebMEncoderEnabled(); |
michael@0 | 136 | #endif |
michael@0 | 137 | |
michael@0 | 138 | #ifdef MOZ_OMX_ENCODER |
michael@0 | 139 | static bool IsOMXEncoderEnabled(); |
michael@0 | 140 | #endif |
michael@0 | 141 | |
michael@0 | 142 | private: |
michael@0 | 143 | // Get encoded data from trackEncoder and write to muxer |
michael@0 | 144 | nsresult WriteEncodedDataToMuxer(TrackEncoder *aTrackEncoder); |
michael@0 | 145 | // Get metadata from trackEncoder and copy to muxer |
michael@0 | 146 | nsresult CopyMetadataToMuxer(TrackEncoder* aTrackEncoder); |
michael@0 | 147 | nsAutoPtr<ContainerWriter> mWriter; |
michael@0 | 148 | nsAutoPtr<AudioTrackEncoder> mAudioEncoder; |
michael@0 | 149 | nsAutoPtr<VideoTrackEncoder> mVideoEncoder; |
michael@0 | 150 | TimeStamp mStartTime; |
michael@0 | 151 | nsString mMIMEType; |
michael@0 | 152 | int mState; |
michael@0 | 153 | bool mShutdown; |
michael@0 | 154 | // Get duration from create encoder, for logging purpose |
michael@0 | 155 | double GetEncodeTimeStamp() |
michael@0 | 156 | { |
michael@0 | 157 | TimeDuration decodeTime; |
michael@0 | 158 | decodeTime = TimeStamp::Now() - mStartTime; |
michael@0 | 159 | return decodeTime.ToMilliseconds(); |
michael@0 | 160 | } |
michael@0 | 161 | }; |
michael@0 | 162 | |
michael@0 | 163 | } |
michael@0 | 164 | #endif |