• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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