1 /* 2 * Copyright (C) 2020 The Android Open Source Project 3 * 4 * Licensed under the Apache License, Version 2.0 (the "License"); 5 * you may not use this file except in compliance with the License. 6 * You may obtain a copy of the License at 7 * 8 * http://www.apache.org/licenses/LICENSE-2.0 9 * 10 * Unless required by applicable law or agreed to in writing, software 11 * distributed under the License is distributed on an "AS IS" BASIS, 12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. 13 * See the License for the specific language governing permissions and 14 * limitations under the License. 15 */ 16 17 #ifndef ANDROID_MEDIA_SAMPLE_WRITER_H 18 #define ANDROID_MEDIA_SAMPLE_WRITER_H 19 20 #include <media/MediaSample.h> 21 #include <media/NdkMediaCodec.h> 22 #include <media/NdkMediaError.h> 23 #include <media/NdkMediaFormat.h> 24 #include <utils/Mutex.h> 25 26 #include <condition_variable> 27 #include <functional> 28 #include <memory> 29 #include <mutex> 30 #include <queue> 31 #include <thread> 32 #include <unordered_map> 33 34 namespace android { 35 36 /** 37 * Muxer interface used by MediaSampleWriter. 38 * Methods in this interface are guaranteed to be called sequentially by MediaSampleWriter. 39 */ 40 class MediaSampleWriterMuxerInterface { 41 public: 42 /** 43 * Adds a new track to the muxer. 44 * @param trackFormat Format of the new track. 45 * @return A non-negative track index on success, or a negative number on failure. 46 */ 47 virtual ssize_t addTrack(AMediaFormat* trackFormat) = 0; 48 49 /** Starts the muxer. */ 50 virtual media_status_t start() = 0; 51 /** 52 * Writes sample data to a previously added track. 53 * @param trackIndex Index of the track the sample data belongs to. 54 * @param data The sample data. 55 * @param info The sample information. 56 * @return The number of bytes written. 57 */ 58 virtual media_status_t writeSampleData(size_t trackIndex, const uint8_t* data, 59 const AMediaCodecBufferInfo* info) = 0; 60 61 /** Stops the muxer. */ 62 virtual media_status_t stop() = 0; 63 virtual ~MediaSampleWriterMuxerInterface() = default; 64 }; 65 66 /** 67 * MediaSampleWriter is a wrapper around a muxer. The sample writer puts samples on a queue that 68 * is serviced by an internal thread to minimize blocking time for clients. MediaSampleWriter also 69 * provides progress reporting. The default muxer interface implementation is based 70 * directly on AMediaMuxer. 71 */ 72 class MediaSampleWriter : public std::enable_shared_from_this<MediaSampleWriter> { 73 public: 74 /** Function prototype for delivering media samples to the writer. */ 75 using MediaSampleConsumerFunction = 76 std::function<void(const std::shared_ptr<MediaSample>& sample)>; 77 78 /** Callback interface. */ 79 class CallbackInterface { 80 public: 81 /** 82 * Sample writer finished. The finished callback is only called after the sample writer has 83 * been successfully started. 84 */ 85 virtual void onFinished(const MediaSampleWriter* writer, media_status_t status) = 0; 86 87 /** Sample writer was stopped before it was finished. */ 88 virtual void onStopped(const MediaSampleWriter* writer) = 0; 89 90 /** Sample writer progress update in percent. */ 91 virtual void onProgressUpdate(const MediaSampleWriter* writer, int32_t progress) = 0; 92 93 /** Sample writer heart-beat signal. */ 94 virtual void onHeartBeat(const MediaSampleWriter* writer) = 0; 95 96 virtual ~CallbackInterface() = default; 97 }; 98 99 static std::shared_ptr<MediaSampleWriter> Create(); 100 101 /** 102 * Initializes the sample writer with its default muxer implementation. MediaSampleWriter needs 103 * to be initialized before tracks are added and can only be initialized once. 104 * @param fd An open file descriptor to write to. The caller is responsible for closing this 105 * file descriptor and it is safe to do so once this method returns. 106 * @param callbacks Client callback object that gets called by the sample writer. 107 * @param heartBeatIntervalUs Interval (in microsecond) at which the sample writer should send a 108 * heart-beat to onProgressUpdate() to indicate it's making progress. Value <=0 indicates 109 * that the heartbeat is not required. 110 * @return True if the writer was successfully initialized. 111 */ 112 bool init(int fd, const std::weak_ptr<CallbackInterface>& callbacks /* nonnull */, 113 int64_t heartBeatIntervalUs = -1); 114 115 /** 116 * Initializes the sample writer with a custom muxer interface implementation. 117 * @param muxer The custom muxer interface implementation. 118 * @param @param callbacks Client callback object that gets called by the sample writer. 119 * @param heartBeatIntervalUs Interval (in microsecond) at which the sample writer should send a 120 * heart-beat to onProgressUpdate() to indicate it's making progress. 121 * @return True if the writer was successfully initialized. 122 */ 123 bool init(const std::shared_ptr<MediaSampleWriterMuxerInterface>& muxer /* nonnull */, 124 const std::weak_ptr<CallbackInterface>& callbacks /* nonnull */, 125 int64_t heartBeatIntervalUs = -1); 126 127 /** 128 * Adds a new track to the sample writer. Tracks must be added after the sample writer has been 129 * initialized and before it is started. 130 * @param trackFormat The format of the track to add. 131 * @return A sample consumer to add samples to if the track was successfully added, or nullptr 132 * if the track could not be added. 133 */ 134 MediaSampleConsumerFunction addTrack( 135 const std::shared_ptr<AMediaFormat>& trackFormat /* nonnull */); 136 137 /** 138 * Starts the sample writer. The sample writer will start processing samples and writing them to 139 * its muxer on an internal thread. MediaSampleWriter can only be started once. 140 * @return True if the sample writer was successfully started. 141 */ 142 bool start(); 143 144 /** 145 * Stops the sample writer. If the sample writer is not yet finished, its operation will be 146 * aborted and the onStopped callback will fire. If the sample writer has already finished and 147 * the onFinished callback has fired the writer has already automatically stopped and there is 148 * no need to call stop manually. Once the sample writer has been stopped it cannot be 149 * restarted. This method is asynchronous and will not wait for the sample writer to stop before 150 * returning. 151 */ 152 void stop(); 153 154 /** Destructor. */ 155 ~MediaSampleWriter(); 156 157 private: 158 struct TrackRecord { TrackRecordTrackRecord159 TrackRecord(int64_t durationUs) 160 : mDurationUs(durationUs), 161 mFirstSampleTimeUs(0), 162 mPrevSampleTimeUs(INT64_MIN), 163 mFirstSampleTimeSet(false), 164 mReachedEos(false){}; 165 TrackRecordTrackRecord166 TrackRecord() : TrackRecord(0){}; 167 168 int64_t mDurationUs; 169 int64_t mFirstSampleTimeUs; 170 int64_t mPrevSampleTimeUs; 171 bool mFirstSampleTimeSet; 172 bool mReachedEos; 173 }; 174 175 // Track index and sample. 176 using SampleEntry = std::pair<size_t, std::shared_ptr<MediaSample>>; 177 178 struct SampleComparator { 179 // Return true if lhs should come after rhs in the sample queue. operatorSampleComparator180 bool operator()(const SampleEntry& lhs, const SampleEntry& rhs) { 181 const bool lhsEos = lhs.second->info.flags & SAMPLE_FLAG_END_OF_STREAM; 182 const bool rhsEos = rhs.second->info.flags & SAMPLE_FLAG_END_OF_STREAM; 183 184 if (lhsEos && !rhsEos) { 185 return true; 186 } else if (!lhsEos && rhsEos) { 187 return false; 188 } else if (lhsEos && rhsEos) { 189 return lhs.first > rhs.first; 190 } 191 192 return lhs.second->info.presentationTimeUs > rhs.second->info.presentationTimeUs; 193 } 194 }; 195 196 std::weak_ptr<CallbackInterface> mCallbacks; 197 std::shared_ptr<MediaSampleWriterMuxerInterface> mMuxer; 198 int64_t mHeartBeatIntervalUs; 199 200 std::mutex mMutex; // Protects sample queue and state. 201 std::condition_variable mSampleSignal; 202 std::unordered_map<size_t, TrackRecord> mTracks; 203 std::priority_queue<SampleEntry, std::vector<SampleEntry>, SampleComparator> mSampleQueue 204 GUARDED_BY(mMutex); 205 206 enum : int { 207 UNINITIALIZED, 208 INITIALIZED, 209 STARTED, 210 STOPPED, 211 } mState GUARDED_BY(mMutex); 212 MediaSampleWriter()213 MediaSampleWriter() : mState(UNINITIALIZED){}; 214 void addSampleToTrack(size_t trackIndex, const std::shared_ptr<MediaSample>& sample); 215 media_status_t writeSamples(bool* wasStopped); 216 media_status_t runWriterLoop(bool* wasStopped); 217 }; 218 219 } // namespace android 220 #endif // ANDROID_MEDIA_SAMPLE_WRITER_H 221