| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 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 | // #define LOG_NDEBUG 0 | 
 | 18 | #define LOG_TAG "MediaSampleWriter" | 
 | 19 |  | 
 | 20 | #include <android-base/logging.h> | 
 | 21 | #include <media/MediaSampleWriter.h> | 
 | 22 | #include <media/NdkMediaMuxer.h> | 
 | 23 |  | 
 | 24 | namespace android { | 
 | 25 |  | 
 | 26 | class DefaultMuxer : public MediaSampleWriterMuxerInterface { | 
 | 27 | public: | 
 | 28 |     // MediaSampleWriterMuxerInterface | 
| Chong Zhang | d6e4aec | 2020-06-22 14:13:07 -0700 | [diff] [blame] | 29 |     ssize_t addTrack(AMediaFormat* trackFormat) override { | 
 | 30 |         // If the track format has rotation, need to call AMediaMuxer_setOrientationHint | 
 | 31 |         // to set the rotation. Muxer doesn't take rotation specified on the track. | 
 | 32 |         const char* mime; | 
 | 33 |         if (AMediaFormat_getString(trackFormat, AMEDIAFORMAT_KEY_MIME, &mime) && | 
 | 34 |             strncmp(mime, "video/", 6) == 0) { | 
 | 35 |             int32_t rotation; | 
 | 36 |             if (AMediaFormat_getInt32(trackFormat, AMEDIAFORMAT_KEY_ROTATION, &rotation) && | 
 | 37 |                 (rotation != 0)) { | 
 | 38 |                 AMediaMuxer_setOrientationHint(mMuxer, rotation); | 
 | 39 |             } | 
 | 40 |         } | 
 | 41 |  | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 42 |         return AMediaMuxer_addTrack(mMuxer, trackFormat); | 
 | 43 |     } | 
 | 44 |     media_status_t start() override { return AMediaMuxer_start(mMuxer); } | 
 | 45 |     media_status_t writeSampleData(size_t trackIndex, const uint8_t* data, | 
 | 46 |                                    const AMediaCodecBufferInfo* info) override { | 
 | 47 |         return AMediaMuxer_writeSampleData(mMuxer, trackIndex, data, info); | 
 | 48 |     } | 
 | 49 |     media_status_t stop() override { return AMediaMuxer_stop(mMuxer); } | 
 | 50 |     // ~MediaSampleWriterMuxerInterface | 
 | 51 |  | 
 | 52 |     static std::shared_ptr<DefaultMuxer> create(int fd) { | 
 | 53 |         AMediaMuxer* ndkMuxer = AMediaMuxer_new(fd, AMEDIAMUXER_OUTPUT_FORMAT_MPEG_4); | 
 | 54 |         if (ndkMuxer == nullptr) { | 
 | 55 |             LOG(ERROR) << "Unable to create AMediaMuxer"; | 
 | 56 |             return nullptr; | 
 | 57 |         } | 
 | 58 |  | 
 | 59 |         return std::make_shared<DefaultMuxer>(ndkMuxer); | 
 | 60 |     } | 
 | 61 |  | 
 | 62 |     ~DefaultMuxer() { | 
 | 63 |         if (mMuxer != nullptr) { | 
 | 64 |             AMediaMuxer_delete(mMuxer); | 
 | 65 |         } | 
 | 66 |     } | 
 | 67 |  | 
 | 68 |     DefaultMuxer(AMediaMuxer* muxer) : mMuxer(muxer){}; | 
 | 69 |     DefaultMuxer() = delete; | 
 | 70 |  | 
 | 71 | private: | 
 | 72 |     AMediaMuxer* mMuxer; | 
 | 73 | }; | 
 | 74 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 75 | // static | 
 | 76 | std::shared_ptr<MediaSampleWriter> MediaSampleWriter::Create() { | 
 | 77 |     return std::shared_ptr<MediaSampleWriter>(new MediaSampleWriter()); | 
 | 78 | } | 
 | 79 |  | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 80 | MediaSampleWriter::~MediaSampleWriter() { | 
 | 81 |     if (mState == STARTED) { | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 82 |         stop(); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 83 |     } | 
 | 84 | } | 
 | 85 |  | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 86 | bool MediaSampleWriter::init(int fd, const std::weak_ptr<CallbackInterface>& callbacks, | 
 | 87 |                              int64_t heartBeatIntervalUs) { | 
 | 88 |     return init(DefaultMuxer::create(fd), callbacks, heartBeatIntervalUs); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 89 | } | 
 | 90 |  | 
 | 91 | bool MediaSampleWriter::init(const std::shared_ptr<MediaSampleWriterMuxerInterface>& muxer, | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 92 |                              const std::weak_ptr<CallbackInterface>& callbacks, | 
 | 93 |                              int64_t heartBeatIntervalUs) { | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 94 |     if (callbacks.lock() == nullptr) { | 
 | 95 |         LOG(ERROR) << "Callback object cannot be null"; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 96 |         return false; | 
 | 97 |     } else if (muxer == nullptr) { | 
 | 98 |         LOG(ERROR) << "Muxer cannot be null"; | 
 | 99 |         return false; | 
 | 100 |     } | 
 | 101 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 102 |     std::scoped_lock lock(mMutex); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 103 |     if (mState != UNINITIALIZED) { | 
 | 104 |         LOG(ERROR) << "Sample writer is already initialized"; | 
 | 105 |         return false; | 
 | 106 |     } | 
 | 107 |  | 
 | 108 |     mState = INITIALIZED; | 
 | 109 |     mMuxer = muxer; | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 110 |     mCallbacks = callbacks; | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 111 |     mHeartBeatIntervalUs = heartBeatIntervalUs; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 112 |     return true; | 
 | 113 | } | 
 | 114 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 115 | MediaSampleWriter::MediaSampleConsumerFunction MediaSampleWriter::addTrack( | 
 | 116 |         const std::shared_ptr<AMediaFormat>& trackFormat) { | 
 | 117 |     if (trackFormat == nullptr) { | 
 | 118 |         LOG(ERROR) << "Track format must be non-null"; | 
 | 119 |         return nullptr; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 120 |     } | 
 | 121 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 122 |     std::scoped_lock lock(mMutex); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 123 |     if (mState != INITIALIZED) { | 
 | 124 |         LOG(ERROR) << "Muxer needs to be initialized when adding tracks."; | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 125 |         return nullptr; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 126 |     } | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 127 |     ssize_t trackIndexOrError = mMuxer->addTrack(trackFormat.get()); | 
 | 128 |     if (trackIndexOrError < 0) { | 
 | 129 |         LOG(ERROR) << "Failed to add media track to muxer: " << trackIndexOrError; | 
 | 130 |         return nullptr; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 131 |     } | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 132 |     const size_t trackIndex = static_cast<size_t>(trackIndexOrError); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 133 |  | 
| Linus Nilsson | 42a971b | 2020-07-01 16:41:11 -0700 | [diff] [blame] | 134 |     int64_t durationUs; | 
 | 135 |     if (!AMediaFormat_getInt64(trackFormat.get(), AMEDIAFORMAT_KEY_DURATION, &durationUs)) { | 
 | 136 |         durationUs = 0; | 
 | 137 |     } | 
 | 138 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 139 |     mTracks.emplace(trackIndex, durationUs); | 
 | 140 |     std::shared_ptr<MediaSampleWriter> thisWriter = shared_from_this(); | 
 | 141 |  | 
 | 142 |     return [self = shared_from_this(), trackIndex](const std::shared_ptr<MediaSample>& sample) { | 
 | 143 |         self->addSampleToTrack(trackIndex, sample); | 
 | 144 |     }; | 
 | 145 | } | 
 | 146 |  | 
 | 147 | void MediaSampleWriter::addSampleToTrack(size_t trackIndex, | 
 | 148 |                                          const std::shared_ptr<MediaSample>& sample) { | 
 | 149 |     if (sample == nullptr) return; | 
 | 150 |  | 
 | 151 |     bool wasEmpty; | 
 | 152 |     { | 
 | 153 |         std::scoped_lock lock(mMutex); | 
 | 154 |         wasEmpty = mSampleQueue.empty(); | 
 | 155 |         mSampleQueue.push(std::make_pair(trackIndex, sample)); | 
 | 156 |     } | 
 | 157 |  | 
 | 158 |     if (wasEmpty) { | 
 | 159 |         mSampleSignal.notify_one(); | 
 | 160 |     } | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 161 | } | 
 | 162 |  | 
 | 163 | bool MediaSampleWriter::start() { | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 164 |     std::scoped_lock lock(mMutex); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 165 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 166 |     if (mTracks.size() == 0) { | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 167 |         LOG(ERROR) << "No tracks to write."; | 
 | 168 |         return false; | 
 | 169 |     } else if (mState != INITIALIZED) { | 
 | 170 |         LOG(ERROR) << "Sample writer is not initialized"; | 
 | 171 |         return false; | 
 | 172 |     } | 
 | 173 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 174 |     mState = STARTED; | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 175 |     std::thread([this] { | 
 | 176 |         bool wasStopped = false; | 
 | 177 |         media_status_t status = writeSamples(&wasStopped); | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 178 |         if (auto callbacks = mCallbacks.lock()) { | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 179 |             if (wasStopped && status == AMEDIA_OK) { | 
 | 180 |                 callbacks->onStopped(this); | 
 | 181 |             } else { | 
 | 182 |                 callbacks->onFinished(this, status); | 
 | 183 |             } | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 184 |         } | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 185 |     }).detach(); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 186 |     return true; | 
 | 187 | } | 
 | 188 |  | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 189 | void MediaSampleWriter::stop() { | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 190 |     { | 
 | 191 |         std::scoped_lock lock(mMutex); | 
 | 192 |         if (mState != STARTED) { | 
 | 193 |             LOG(ERROR) << "Sample writer is not started."; | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 194 |             return; | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 195 |         } | 
 | 196 |         mState = STOPPED; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 197 |     } | 
 | 198 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 199 |     mSampleSignal.notify_all(); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 200 | } | 
 | 201 |  | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 202 | media_status_t MediaSampleWriter::writeSamples(bool* wasStopped) { | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 203 |     media_status_t muxerStatus = mMuxer->start(); | 
 | 204 |     if (muxerStatus != AMEDIA_OK) { | 
 | 205 |         LOG(ERROR) << "Error starting muxer: " << muxerStatus; | 
 | 206 |         return muxerStatus; | 
 | 207 |     } | 
 | 208 |  | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 209 |     media_status_t writeStatus = runWriterLoop(wasStopped); | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 210 |     if (writeStatus != AMEDIA_OK) { | 
 | 211 |         LOG(ERROR) << "Error writing samples: " << writeStatus; | 
 | 212 |     } | 
 | 213 |  | 
 | 214 |     muxerStatus = mMuxer->stop(); | 
 | 215 |     if (muxerStatus != AMEDIA_OK) { | 
 | 216 |         LOG(ERROR) << "Error stopping muxer: " << muxerStatus; | 
 | 217 |     } | 
 | 218 |  | 
 | 219 |     return writeStatus != AMEDIA_OK ? writeStatus : muxerStatus; | 
 | 220 | } | 
 | 221 |  | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 222 | media_status_t MediaSampleWriter::runWriterLoop(bool* wasStopped) NO_THREAD_SAFETY_ANALYSIS { | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 223 |     AMediaCodecBufferInfo bufferInfo; | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 224 |     int32_t lastProgressUpdate = 0; | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 225 |     bool progressSinceLastReport = false; | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 226 |     int trackEosCount = 0; | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 227 |  | 
 | 228 |     // Set the "primary" track that will be used to determine progress to the track with longest | 
 | 229 |     // duration. | 
 | 230 |     int primaryTrackIndex = -1; | 
 | 231 |     int64_t longestDurationUs = 0; | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 232 |     for (auto it = mTracks.begin(); it != mTracks.end(); ++it) { | 
 | 233 |         if (it->second.mDurationUs > longestDurationUs) { | 
 | 234 |             primaryTrackIndex = it->first; | 
 | 235 |             longestDurationUs = it->second.mDurationUs; | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 236 |         } | 
 | 237 |     } | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 238 |  | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 239 |     std::chrono::microseconds updateInterval(mHeartBeatIntervalUs); | 
 | 240 |     std::chrono::system_clock::time_point nextUpdateTime = | 
 | 241 |             std::chrono::system_clock::now() + updateInterval; | 
 | 242 |  | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 243 |     while (true) { | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 244 |         if (trackEosCount >= mTracks.size()) { | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 245 |             break; | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 246 |         } | 
 | 247 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 248 |         size_t trackIndex; | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 249 |         std::shared_ptr<MediaSample> sample; | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 250 |         { | 
 | 251 |             std::unique_lock lock(mMutex); | 
 | 252 |             while (mSampleQueue.empty() && mState == STARTED) { | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 253 |                 if (mHeartBeatIntervalUs <= 0) { | 
 | 254 |                     mSampleSignal.wait(lock); | 
 | 255 |                     continue; | 
 | 256 |                 } | 
 | 257 |  | 
 | 258 |                 if (mSampleSignal.wait_until(lock, nextUpdateTime) == std::cv_status::timeout) { | 
 | 259 |                     // Send heart-beat if there is any progress since last update time. | 
 | 260 |                     if (progressSinceLastReport) { | 
 | 261 |                         if (auto callbacks = mCallbacks.lock()) { | 
 | 262 |                             callbacks->onHeartBeat(this); | 
 | 263 |                         } | 
 | 264 |                         progressSinceLastReport = false; | 
 | 265 |                     } | 
 | 266 |                     nextUpdateTime += updateInterval; | 
 | 267 |                 } | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 268 |             } | 
 | 269 |  | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 270 |             if (mState == STOPPED) { | 
 | 271 |                 *wasStopped = true; | 
 | 272 |                 return AMEDIA_OK; | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 273 |             } | 
 | 274 |  | 
 | 275 |             auto& topEntry = mSampleQueue.top(); | 
 | 276 |             trackIndex = topEntry.first; | 
 | 277 |             sample = topEntry.second; | 
 | 278 |             mSampleQueue.pop(); | 
 | 279 |         } | 
 | 280 |  | 
 | 281 |         TrackRecord& track = mTracks[trackIndex]; | 
 | 282 |  | 
 | 283 |         if (sample->info.flags & SAMPLE_FLAG_END_OF_STREAM) { | 
 | 284 |             if (track.mReachedEos) { | 
 | 285 |                 continue; | 
 | 286 |             } | 
 | 287 |  | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 288 |             // Track reached end of stream. | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 289 |             track.mReachedEos = true; | 
 | 290 |             trackEosCount++; | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 291 |  | 
 | 292 |             // Preserve source track duration by setting the appropriate timestamp on the | 
 | 293 |             // empty End-Of-Stream sample. | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 294 |             if (track.mDurationUs > 0 && track.mFirstSampleTimeSet) { | 
 | 295 |                 sample->info.presentationTimeUs = track.mDurationUs + track.mFirstSampleTimeUs; | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 296 |             } | 
 | 297 |         } | 
 | 298 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 299 |         track.mPrevSampleTimeUs = sample->info.presentationTimeUs; | 
 | 300 |         if (!track.mFirstSampleTimeSet) { | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 301 |             // Record the first sample's timestamp in order to translate duration to EOS | 
 | 302 |             // time for tracks that does not start at 0. | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 303 |             track.mFirstSampleTimeUs = sample->info.presentationTimeUs; | 
 | 304 |             track.mFirstSampleTimeSet = true; | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 305 |         } | 
 | 306 |  | 
 | 307 |         bufferInfo.offset = sample->dataOffset; | 
 | 308 |         bufferInfo.size = sample->info.size; | 
 | 309 |         bufferInfo.flags = sample->info.flags; | 
 | 310 |         bufferInfo.presentationTimeUs = sample->info.presentationTimeUs; | 
 | 311 |  | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 312 |         media_status_t status = mMuxer->writeSampleData(trackIndex, sample->buffer, &bufferInfo); | 
| Linus Nilsson | b09aac2 | 2020-07-29 11:56:53 -0700 | [diff] [blame] | 313 |         if (status != AMEDIA_OK) { | 
 | 314 |             LOG(ERROR) << "writeSampleData returned " << status; | 
 | 315 |             return status; | 
 | 316 |         } | 
 | 317 |         sample.reset(); | 
 | 318 |  | 
 | 319 |         // TODO(lnilsson): Add option to toggle progress reporting on/off. | 
| Linus Nilsson | c31d249 | 2020-09-23 12:30:00 -0700 | [diff] [blame] | 320 |         if (trackIndex == primaryTrackIndex) { | 
 | 321 |             const int64_t elapsed = track.mPrevSampleTimeUs - track.mFirstSampleTimeUs; | 
 | 322 |             int32_t progress = (elapsed * 100) / track.mDurationUs; | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 323 |             progress = std::clamp(progress, 0, 100); | 
 | 324 |  | 
 | 325 |             if (progress > lastProgressUpdate) { | 
 | 326 |                 if (auto callbacks = mCallbacks.lock()) { | 
 | 327 |                     callbacks->onProgressUpdate(this, progress); | 
 | 328 |                 } | 
 | 329 |                 lastProgressUpdate = progress; | 
 | 330 |             } | 
| Chong Zhang | 457c689 | 2021-02-01 15:34:20 -0800 | [diff] [blame] | 331 |             progressSinceLastReport = true; | 
| Linus Nilsson | e2cdd1f | 2020-07-07 17:29:26 -0700 | [diff] [blame] | 332 |         } | 
| Linus Nilsson | a85df7f | 2020-02-20 16:32:04 -0800 | [diff] [blame] | 333 |     } | 
 | 334 |  | 
 | 335 |     return AMEDIA_OK; | 
 | 336 | } | 
| Chong Zhang | d6e4aec | 2020-06-22 14:13:07 -0700 | [diff] [blame] | 337 | }  // namespace android |