| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -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 "MediaSampleReader" | 
 | 19 |  | 
 | 20 | #include <android-base/logging.h> | 
 | 21 | #include <media/MediaSampleReaderNDK.h> | 
 | 22 |  | 
 | 23 | #include <algorithm> | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 24 | #include <cmath> | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 25 |  | 
 | 26 | namespace android { | 
 | 27 |  | 
| Linus Nilsson | 0da327a | 2020-01-31 16:22:18 -0800 | [diff] [blame] | 28 | // Check that the extractor sample flags have the expected NDK meaning. | 
 | 29 | static_assert(SAMPLE_FLAG_SYNC_SAMPLE == AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC, | 
 | 30 |               "Sample flag mismatch: SYNC_SAMPLE"); | 
 | 31 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 32 | // static | 
 | 33 | std::shared_ptr<MediaSampleReader> MediaSampleReaderNDK::createFromFd(int fd, size_t offset, | 
 | 34 |                                                                       size_t size) { | 
 | 35 |     AMediaExtractor* extractor = AMediaExtractor_new(); | 
 | 36 |     if (extractor == nullptr) { | 
 | 37 |         LOG(ERROR) << "Unable to allocate AMediaExtractor"; | 
 | 38 |         return nullptr; | 
 | 39 |     } | 
 | 40 |  | 
 | 41 |     media_status_t status = AMediaExtractor_setDataSourceFd(extractor, fd, offset, size); | 
 | 42 |     if (status != AMEDIA_OK) { | 
 | 43 |         LOG(ERROR) << "AMediaExtractor_setDataSourceFd returned error: " << status; | 
 | 44 |         AMediaExtractor_delete(extractor); | 
 | 45 |         return nullptr; | 
 | 46 |     } | 
 | 47 |  | 
 | 48 |     auto sampleReader = std::shared_ptr<MediaSampleReaderNDK>(new MediaSampleReaderNDK(extractor)); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 49 |     return sampleReader; | 
 | 50 | } | 
 | 51 |  | 
 | 52 | MediaSampleReaderNDK::MediaSampleReaderNDK(AMediaExtractor* extractor) | 
 | 53 |       : mExtractor(extractor), mTrackCount(AMediaExtractor_getTrackCount(mExtractor)) { | 
 | 54 |     if (mTrackCount > 0) { | 
 | 55 |         mTrackCursors.resize(mTrackCount); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 56 |     } | 
 | 57 | } | 
 | 58 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 59 | MediaSampleReaderNDK::~MediaSampleReaderNDK() { | 
 | 60 |     if (mExtractor != nullptr) { | 
 | 61 |         AMediaExtractor_delete(mExtractor); | 
 | 62 |     } | 
 | 63 | } | 
 | 64 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 65 | void MediaSampleReaderNDK::advanceTrack_l(int trackIndex) { | 
 | 66 |     if (!mEnforceSequentialAccess) { | 
 | 67 |         // Note: Positioning the extractor before advancing the track is needed for two reasons: | 
 | 68 |         // 1. To enable multiple advances without explicitly letting the extractor catch up. | 
 | 69 |         // 2. To prevent the extractor from being farther than "next". | 
 | 70 |         (void)moveToTrack_l(trackIndex); | 
 | 71 |     } | 
 | 72 |  | 
 | 73 |     SampleCursor& cursor = mTrackCursors[trackIndex]; | 
 | 74 |     cursor.previous = cursor.current; | 
 | 75 |     cursor.current = cursor.next; | 
 | 76 |     cursor.next.reset(); | 
 | 77 |  | 
 | 78 |     if (mEnforceSequentialAccess && trackIndex == mExtractorTrackIndex) { | 
 | 79 |         while (advanceExtractor_l()) { | 
 | 80 |             SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex]; | 
 | 81 |             if (cursor.current.isSet && cursor.current.index == mExtractorSampleIndex) { | 
 | 82 |                 if (mExtractorTrackIndex != trackIndex) { | 
 | 83 |                     mTrackSignals[mExtractorTrackIndex].notify_all(); | 
 | 84 |                 } | 
 | 85 |                 break; | 
 | 86 |             } | 
 | 87 |         } | 
 | 88 |     } | 
 | 89 |     return; | 
 | 90 | } | 
 | 91 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 92 | bool MediaSampleReaderNDK::advanceExtractor_l() { | 
 | 93 |     // Reset the "next" sample time whenever the extractor advances past a sample that is current, | 
 | 94 |     // to ensure that "next" is appropriately updated when the extractor advances over the next | 
 | 95 |     // sample of that track. | 
 | 96 |     if (mTrackCursors[mExtractorTrackIndex].current.isSet && | 
 | 97 |         mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex) { | 
 | 98 |         mTrackCursors[mExtractorTrackIndex].next.reset(); | 
 | 99 |     } | 
 | 100 |  | 
| Linus Nilsson | 6da1c54 | 2021-02-11 21:17:17 -0800 | [diff] [blame] | 101 |     // Update the extractor's sample index even if this track reaches EOS, so that the other tracks | 
 | 102 |     // are not given an incorrect extractor position. | 
 | 103 |     mExtractorSampleIndex++; | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 104 |     if (!AMediaExtractor_advance(mExtractor)) { | 
| Linus Nilsson | 0c01f3d | 2020-12-01 09:29:50 -0800 | [diff] [blame] | 105 |         LOG(DEBUG) << "  EOS in advanceExtractor_l"; | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 106 |         mEosReached = true; | 
 | 107 |         for (auto it = mTrackSignals.begin(); it != mTrackSignals.end(); ++it) { | 
 | 108 |             it->second.notify_all(); | 
 | 109 |         } | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 110 |         return false; | 
 | 111 |     } | 
 | 112 |  | 
 | 113 |     mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 114 |  | 
 | 115 |     SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex]; | 
 | 116 |     if (mExtractorSampleIndex > cursor.previous.index) { | 
 | 117 |         if (!cursor.current.isSet) { | 
 | 118 |             cursor.current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); | 
 | 119 |         } else if (!cursor.next.isSet && mExtractorSampleIndex > cursor.current.index) { | 
 | 120 |             cursor.next.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor)); | 
 | 121 |         } | 
 | 122 |     } | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 123 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 124 |     return true; | 
 | 125 | } | 
 | 126 |  | 
 | 127 | media_status_t MediaSampleReaderNDK::seekExtractorBackwards_l(int64_t targetTimeUs, | 
 | 128 |                                                               int targetTrackIndex, | 
 | 129 |                                                               uint64_t targetSampleIndex) { | 
 | 130 |     if (targetSampleIndex > mExtractorSampleIndex) { | 
 | 131 |         LOG(ERROR) << "Error: Forward seek is not supported"; | 
 | 132 |         return AMEDIA_ERROR_UNSUPPORTED; | 
 | 133 |     } | 
 | 134 |  | 
 | 135 |     // AMediaExtractor supports reading negative timestamps but does not support seeking to them. | 
 | 136 |     const int64_t seekToTimeUs = std::max(targetTimeUs, (int64_t)0); | 
 | 137 |     media_status_t status = | 
 | 138 |             AMediaExtractor_seekTo(mExtractor, seekToTimeUs, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC); | 
 | 139 |     if (status != AMEDIA_OK) { | 
 | 140 |         LOG(ERROR) << "Unable to seek to " << seekToTimeUs << ", target " << targetTimeUs; | 
 | 141 |         return status; | 
 | 142 |     } | 
| Linus Nilsson | 0c01f3d | 2020-12-01 09:29:50 -0800 | [diff] [blame] | 143 |  | 
 | 144 |     mEosReached = false; | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 145 |     mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); | 
 | 146 |     int64_t sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); | 
 | 147 |  | 
 | 148 |     while (sampleTimeUs != targetTimeUs || mExtractorTrackIndex != targetTrackIndex) { | 
 | 149 |         if (!AMediaExtractor_advance(mExtractor)) { | 
 | 150 |             return AMEDIA_ERROR_END_OF_STREAM; | 
 | 151 |         } | 
 | 152 |         mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); | 
 | 153 |         sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); | 
 | 154 |     } | 
 | 155 |     mExtractorSampleIndex = targetSampleIndex; | 
 | 156 |     return AMEDIA_OK; | 
 | 157 | } | 
 | 158 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 159 | media_status_t MediaSampleReaderNDK::moveToSample_l(SamplePosition& pos, int trackIndex) { | 
 | 160 |     // Seek backwards if the extractor is ahead of the sample. | 
 | 161 |     if (pos.isSet && mExtractorSampleIndex > pos.index) { | 
 | 162 |         media_status_t status = seekExtractorBackwards_l(pos.timeStampUs, trackIndex, pos.index); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 163 |         if (status != AMEDIA_OK) return status; | 
 | 164 |     } | 
 | 165 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 166 |     // Advance until extractor points to the sample. | 
 | 167 |     while (!(pos.isSet && pos.index == mExtractorSampleIndex)) { | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 168 |         if (!advanceExtractor_l()) { | 
 | 169 |             return AMEDIA_ERROR_END_OF_STREAM; | 
 | 170 |         } | 
 | 171 |     } | 
 | 172 |  | 
 | 173 |     return AMEDIA_OK; | 
 | 174 | } | 
 | 175 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 176 | media_status_t MediaSampleReaderNDK::moveToTrack_l(int trackIndex) { | 
 | 177 |     return moveToSample_l(mTrackCursors[trackIndex].current, trackIndex); | 
 | 178 | } | 
 | 179 |  | 
 | 180 | media_status_t MediaSampleReaderNDK::waitForTrack_l(int trackIndex, | 
 | 181 |                                                     std::unique_lock<std::mutex>& lockHeld) { | 
 | 182 |     while (trackIndex != mExtractorTrackIndex && !mEosReached && mEnforceSequentialAccess) { | 
 | 183 |         mTrackSignals[trackIndex].wait(lockHeld); | 
 | 184 |     } | 
 | 185 |  | 
 | 186 |     if (mEosReached) { | 
 | 187 |         return AMEDIA_ERROR_END_OF_STREAM; | 
 | 188 |     } | 
| Linus Nilsson | fdb3e33 | 2020-09-18 17:11:41 -0700 | [diff] [blame] | 189 |  | 
 | 190 |     if (!mEnforceSequentialAccess) { | 
 | 191 |         return moveToTrack_l(trackIndex); | 
 | 192 |     } | 
 | 193 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 194 |     return AMEDIA_OK; | 
 | 195 | } | 
 | 196 |  | 
 | 197 | media_status_t MediaSampleReaderNDK::primeExtractorForTrack_l( | 
 | 198 |         int trackIndex, std::unique_lock<std::mutex>& lockHeld) { | 
 | 199 |     if (mExtractorTrackIndex < 0) { | 
 | 200 |         mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor); | 
 | 201 |         if (mExtractorTrackIndex < 0) { | 
 | 202 |             return AMEDIA_ERROR_END_OF_STREAM; | 
 | 203 |         } | 
 | 204 |         mTrackCursors[mExtractorTrackIndex].current.set(mExtractorSampleIndex, | 
 | 205 |                                                         AMediaExtractor_getSampleTime(mExtractor)); | 
 | 206 |     } | 
 | 207 |  | 
 | 208 |     if (mEnforceSequentialAccess) { | 
 | 209 |         return waitForTrack_l(trackIndex, lockHeld); | 
 | 210 |     } else { | 
 | 211 |         return moveToTrack_l(trackIndex); | 
 | 212 |     } | 
 | 213 | } | 
 | 214 |  | 
 | 215 | media_status_t MediaSampleReaderNDK::selectTrack(int trackIndex) { | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 216 |     std::scoped_lock lock(mExtractorMutex); | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 217 |  | 
 | 218 |     if (trackIndex < 0 || trackIndex >= mTrackCount) { | 
 | 219 |         LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; | 
 | 220 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 221 |     } else if (mTrackSignals.find(trackIndex) != mTrackSignals.end()) { | 
 | 222 |         LOG(ERROR) << "TrackIndex " << trackIndex << " already selected"; | 
 | 223 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 224 |     } else if (mExtractorTrackIndex >= 0) { | 
 | 225 |         LOG(ERROR) << "Tracks must be selected before sample reading begins."; | 
 | 226 |         return AMEDIA_ERROR_UNSUPPORTED; | 
 | 227 |     } | 
 | 228 |  | 
 | 229 |     media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex); | 
 | 230 |     if (status != AMEDIA_OK) { | 
 | 231 |         LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status; | 
 | 232 |         return status; | 
 | 233 |     } | 
 | 234 |  | 
 | 235 |     mTrackSignals.emplace(std::piecewise_construct, std::forward_as_tuple(trackIndex), | 
 | 236 |                           std::forward_as_tuple()); | 
 | 237 |     return AMEDIA_OK; | 
 | 238 | } | 
 | 239 |  | 
| Linus Nilsson | af4a321 | 2020-12-15 08:18:25 -0800 | [diff] [blame] | 240 | media_status_t MediaSampleReaderNDK::unselectTrack(int trackIndex) { | 
 | 241 |     std::scoped_lock lock(mExtractorMutex); | 
 | 242 |  | 
 | 243 |     if (trackIndex < 0 || trackIndex >= mTrackCount) { | 
 | 244 |         LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; | 
 | 245 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 246 |     } else if (mExtractorTrackIndex >= 0) { | 
 | 247 |         LOG(ERROR) << "unselectTrack must be called before sample reading begins."; | 
 | 248 |         return AMEDIA_ERROR_UNSUPPORTED; | 
 | 249 |     } | 
 | 250 |  | 
 | 251 |     auto it = mTrackSignals.find(trackIndex); | 
 | 252 |     if (it == mTrackSignals.end()) { | 
 | 253 |         LOG(ERROR) << "TrackIndex " << trackIndex << " is not selected"; | 
 | 254 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 255 |     } | 
 | 256 |     mTrackSignals.erase(it); | 
 | 257 |  | 
 | 258 |     media_status_t status = AMediaExtractor_unselectTrack(mExtractor, trackIndex); | 
 | 259 |     if (status != AMEDIA_OK) { | 
 | 260 |         LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status; | 
 | 261 |         return status; | 
 | 262 |     } | 
 | 263 |  | 
 | 264 |     return AMEDIA_OK; | 
 | 265 | } | 
 | 266 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 267 | media_status_t MediaSampleReaderNDK::setEnforceSequentialAccess(bool enforce) { | 
| Linus Nilsson | 0c01f3d | 2020-12-01 09:29:50 -0800 | [diff] [blame] | 268 |     LOG(DEBUG) << "setEnforceSequentialAccess( " << enforce << " )"; | 
 | 269 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 270 |     std::scoped_lock lock(mExtractorMutex); | 
 | 271 |  | 
 | 272 |     if (mEnforceSequentialAccess && !enforce) { | 
 | 273 |         // If switching from enforcing to not enforcing sequential access there may be threads | 
 | 274 |         // waiting that needs to be woken up. | 
 | 275 |         for (auto it = mTrackSignals.begin(); it != mTrackSignals.end(); ++it) { | 
 | 276 |             it->second.notify_all(); | 
 | 277 |         } | 
 | 278 |     } else if (!mEnforceSequentialAccess && enforce && mExtractorTrackIndex >= 0) { | 
 | 279 |         // If switching from not enforcing to enforcing sequential access the extractor needs to be | 
 | 280 |         // positioned for the track farthest behind so that it won't get stuck waiting. | 
 | 281 |         struct { | 
 | 282 |             SamplePosition* pos = nullptr; | 
 | 283 |             int trackIndex = -1; | 
 | 284 |         } earliestSample; | 
 | 285 |  | 
 | 286 |         for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) { | 
 | 287 |             SamplePosition& lastKnownTrackPosition = mTrackCursors[trackIndex].current.isSet | 
 | 288 |                                                              ? mTrackCursors[trackIndex].current | 
 | 289 |                                                              : mTrackCursors[trackIndex].previous; | 
 | 290 |  | 
 | 291 |             if (lastKnownTrackPosition.isSet) { | 
 | 292 |                 if (earliestSample.pos == nullptr || | 
 | 293 |                     earliestSample.pos->index > lastKnownTrackPosition.index) { | 
 | 294 |                     earliestSample.pos = &lastKnownTrackPosition; | 
 | 295 |                     earliestSample.trackIndex = trackIndex; | 
 | 296 |                 } | 
 | 297 |             } | 
 | 298 |         } | 
 | 299 |  | 
 | 300 |         if (earliestSample.pos == nullptr) { | 
 | 301 |             LOG(ERROR) << "No known sample position found"; | 
 | 302 |             return AMEDIA_ERROR_UNKNOWN; | 
 | 303 |         } | 
 | 304 |  | 
 | 305 |         media_status_t status = moveToSample_l(*earliestSample.pos, earliestSample.trackIndex); | 
 | 306 |         if (status != AMEDIA_OK) return status; | 
 | 307 |  | 
 | 308 |         while (!(mTrackCursors[mExtractorTrackIndex].current.isSet && | 
 | 309 |                  mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex)) { | 
 | 310 |             if (!advanceExtractor_l()) { | 
 | 311 |                 return AMEDIA_ERROR_END_OF_STREAM; | 
 | 312 |             } | 
 | 313 |         } | 
 | 314 |     } | 
 | 315 |  | 
 | 316 |     mEnforceSequentialAccess = enforce; | 
 | 317 |     return AMEDIA_OK; | 
 | 318 | } | 
 | 319 |  | 
 | 320 | media_status_t MediaSampleReaderNDK::getEstimatedBitrateForTrack(int trackIndex, int32_t* bitrate) { | 
 | 321 |     std::scoped_lock lock(mExtractorMutex); | 
 | 322 |     media_status_t status = AMEDIA_OK; | 
 | 323 |  | 
 | 324 |     if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) { | 
 | 325 |         LOG(ERROR) << "Track is not selected."; | 
 | 326 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 327 |     } else if (bitrate == nullptr) { | 
 | 328 |         LOG(ERROR) << "bitrate pointer is NULL."; | 
 | 329 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 330 |     } else if (mExtractorTrackIndex >= 0) { | 
 | 331 |         LOG(ERROR) << "getEstimatedBitrateForTrack must be called before sample reading begins."; | 
 | 332 |         return AMEDIA_ERROR_UNSUPPORTED; | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 333 |     } | 
 | 334 |  | 
 | 335 |     // Sample the track. | 
 | 336 |     static constexpr int64_t kSamplingDurationUs = 10 * 1000 * 1000;  // 10 seconds | 
 | 337 |     size_t lastSampleSize = 0; | 
 | 338 |     size_t totalSampleSize = 0; | 
 | 339 |     int64_t firstSampleTimeUs = 0; | 
 | 340 |     int64_t lastSampleTimeUs = 0; | 
 | 341 |  | 
 | 342 |     do { | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 343 |         if (AMediaExtractor_getSampleTrackIndex(mExtractor) == trackIndex) { | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 344 |             lastSampleTimeUs = AMediaExtractor_getSampleTime(mExtractor); | 
 | 345 |             if (totalSampleSize == 0) { | 
 | 346 |                 firstSampleTimeUs = lastSampleTimeUs; | 
 | 347 |             } | 
 | 348 |  | 
 | 349 |             lastSampleSize = AMediaExtractor_getSampleSize(mExtractor); | 
 | 350 |             totalSampleSize += lastSampleSize; | 
 | 351 |         } | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 352 |     } while ((lastSampleTimeUs - firstSampleTimeUs) < kSamplingDurationUs && | 
 | 353 |              AMediaExtractor_advance(mExtractor)); | 
 | 354 |  | 
 | 355 |     // Reset the extractor to the beginning. | 
 | 356 |     status = AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC); | 
 | 357 |     if (status != AMEDIA_OK) { | 
 | 358 |         LOG(ERROR) << "Unable to reset extractor: " << status; | 
 | 359 |         return status; | 
 | 360 |     } | 
| Linus Nilsson | 800793f | 2020-07-31 16:16:38 -0700 | [diff] [blame] | 361 |  | 
 | 362 |     int64_t durationUs = 0; | 
 | 363 |     const int64_t sampledDurationUs = lastSampleTimeUs - firstSampleTimeUs; | 
 | 364 |  | 
 | 365 |     if (sampledDurationUs < kSamplingDurationUs) { | 
 | 366 |         // Track is shorter than the sampling duration so use the full track duration to get better | 
 | 367 |         // accuracy (i.e. don't skip the last sample). | 
 | 368 |         AMediaFormat* trackFormat = getTrackFormat(trackIndex); | 
 | 369 |         if (!AMediaFormat_getInt64(trackFormat, AMEDIAFORMAT_KEY_DURATION, &durationUs)) { | 
 | 370 |             durationUs = 0; | 
 | 371 |         } | 
 | 372 |         AMediaFormat_delete(trackFormat); | 
 | 373 |     } | 
 | 374 |  | 
 | 375 |     if (durationUs == 0) { | 
 | 376 |         // The sampled duration does not account for the last sample's duration so its size should | 
 | 377 |         // not be included either. | 
 | 378 |         totalSampleSize -= lastSampleSize; | 
 | 379 |         durationUs = sampledDurationUs; | 
 | 380 |     } | 
 | 381 |  | 
 | 382 |     if (totalSampleSize == 0 || durationUs <= 0) { | 
 | 383 |         LOG(ERROR) << "Unable to estimate track bitrate"; | 
 | 384 |         return AMEDIA_ERROR_MALFORMED; | 
 | 385 |     } | 
 | 386 |  | 
 | 387 |     *bitrate = roundf((float)totalSampleSize * 8 * 1000000 / durationUs); | 
 | 388 |     return AMEDIA_OK; | 
 | 389 | } | 
 | 390 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 391 | media_status_t MediaSampleReaderNDK::getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) { | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 392 |     std::unique_lock<std::mutex> lock(mExtractorMutex); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 393 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 394 |     if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) { | 
 | 395 |         LOG(ERROR) << "Track not selected."; | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 396 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 397 |     } else if (info == nullptr) { | 
 | 398 |         LOG(ERROR) << "MediaSampleInfo pointer is NULL."; | 
 | 399 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 400 |     } | 
 | 401 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 402 |     media_status_t status = primeExtractorForTrack_l(trackIndex, lock); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 403 |     if (status == AMEDIA_OK) { | 
 | 404 |         info->presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor); | 
 | 405 |         info->flags = AMediaExtractor_getSampleFlags(mExtractor); | 
 | 406 |         info->size = AMediaExtractor_getSampleSize(mExtractor); | 
 | 407 |     } else if (status == AMEDIA_ERROR_END_OF_STREAM) { | 
 | 408 |         info->presentationTimeUs = 0; | 
 | 409 |         info->flags = SAMPLE_FLAG_END_OF_STREAM; | 
 | 410 |         info->size = 0; | 
| Linus Nilsson | 0c01f3d | 2020-12-01 09:29:50 -0800 | [diff] [blame] | 411 |         LOG(DEBUG) << "  getSampleInfoForTrack #" << trackIndex << ": End Of Stream"; | 
 | 412 |     } else { | 
 | 413 |         LOG(ERROR) << "  getSampleInfoForTrack #" << trackIndex << ": Error " << status; | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 414 |     } | 
| Linus Nilsson | 0c01f3d | 2020-12-01 09:29:50 -0800 | [diff] [blame] | 415 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 416 |     return status; | 
 | 417 | } | 
 | 418 |  | 
 | 419 | media_status_t MediaSampleReaderNDK::readSampleDataForTrack(int trackIndex, uint8_t* buffer, | 
 | 420 |                                                             size_t bufferSize) { | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 421 |     std::unique_lock<std::mutex> lock(mExtractorMutex); | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 422 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 423 |     if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) { | 
 | 424 |         LOG(ERROR) << "Track not selected."; | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 425 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 426 |     } else if (buffer == nullptr) { | 
 | 427 |         LOG(ERROR) << "buffer pointer is NULL"; | 
 | 428 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 429 |     } | 
 | 430 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 431 |     media_status_t status = primeExtractorForTrack_l(trackIndex, lock); | 
 | 432 |     if (status != AMEDIA_OK) { | 
 | 433 |         return status; | 
 | 434 |     } | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 435 |  | 
 | 436 |     ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor); | 
 | 437 |     if (bufferSize < sampleSize) { | 
 | 438 |         LOG(ERROR) << "Buffer is too small for sample, " << bufferSize << " vs " << sampleSize; | 
 | 439 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 440 |     } | 
 | 441 |  | 
 | 442 |     ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize); | 
 | 443 |     if (bytesRead < sampleSize) { | 
 | 444 |         LOG(ERROR) << "Unable to read full sample, " << bytesRead << " vs " << sampleSize; | 
 | 445 |         return AMEDIA_ERROR_INVALID_PARAMETER; | 
 | 446 |     } | 
 | 447 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 448 |     advanceTrack_l(trackIndex); | 
 | 449 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 450 |     return AMEDIA_OK; | 
 | 451 | } | 
 | 452 |  | 
| Linus Nilsson | 6233fed | 2020-08-13 15:15:14 -0700 | [diff] [blame] | 453 | void MediaSampleReaderNDK::advanceTrack(int trackIndex) { | 
 | 454 |     std::scoped_lock lock(mExtractorMutex); | 
 | 455 |  | 
 | 456 |     if (mTrackSignals.find(trackIndex) != mTrackSignals.end()) { | 
 | 457 |         advanceTrack_l(trackIndex); | 
 | 458 |     } else { | 
 | 459 |         LOG(ERROR) << "Trying to advance a track that is not selected (#" << trackIndex << ")"; | 
 | 460 |     } | 
 | 461 | } | 
 | 462 |  | 
| Linus Nilsson | 478df7e | 2020-01-29 15:34:24 -0800 | [diff] [blame] | 463 | AMediaFormat* MediaSampleReaderNDK::getFileFormat() { | 
 | 464 |     return AMediaExtractor_getFileFormat(mExtractor); | 
 | 465 | } | 
 | 466 |  | 
 | 467 | size_t MediaSampleReaderNDK::getTrackCount() const { | 
 | 468 |     return mTrackCount; | 
 | 469 | } | 
 | 470 |  | 
 | 471 | AMediaFormat* MediaSampleReaderNDK::getTrackFormat(int trackIndex) { | 
 | 472 |     if (trackIndex < 0 || trackIndex >= mTrackCount) { | 
 | 473 |         LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount; | 
 | 474 |         return AMediaFormat_new(); | 
 | 475 |     } | 
 | 476 |  | 
 | 477 |     return AMediaExtractor_getTrackFormat(mExtractor, trackIndex); | 
 | 478 | } | 
 | 479 |  | 
 | 480 | }  // namespace android |