blob: 1a6e7edd9cd5a0ec7c8211922fe670edcdfa45ab [file] [log] [blame]
Linus Nilsson478df7e2020-01-29 15:34:24 -08001/*
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 Nilsson800793f2020-07-31 16:16:38 -070024#include <cmath>
Linus Nilsson478df7e2020-01-29 15:34:24 -080025
26namespace android {
27
Linus Nilsson0da327a2020-01-31 16:22:18 -080028// Check that the extractor sample flags have the expected NDK meaning.
29static_assert(SAMPLE_FLAG_SYNC_SAMPLE == AMEDIAEXTRACTOR_SAMPLE_FLAG_SYNC,
30 "Sample flag mismatch: SYNC_SAMPLE");
31
Linus Nilsson478df7e2020-01-29 15:34:24 -080032// static
33std::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 Nilsson478df7e2020-01-29 15:34:24 -080049 return sampleReader;
50}
51
52MediaSampleReaderNDK::MediaSampleReaderNDK(AMediaExtractor* extractor)
53 : mExtractor(extractor), mTrackCount(AMediaExtractor_getTrackCount(mExtractor)) {
54 if (mTrackCount > 0) {
55 mTrackCursors.resize(mTrackCount);
Linus Nilsson478df7e2020-01-29 15:34:24 -080056 }
57}
58
Linus Nilsson478df7e2020-01-29 15:34:24 -080059MediaSampleReaderNDK::~MediaSampleReaderNDK() {
60 if (mExtractor != nullptr) {
61 AMediaExtractor_delete(mExtractor);
62 }
63}
64
Linus Nilsson6233fed2020-08-13 15:15:14 -070065void 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 Nilsson478df7e2020-01-29 15:34:24 -080092bool 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
101 if (!AMediaExtractor_advance(mExtractor)) {
Linus Nilsson0c01f3d2020-12-01 09:29:50 -0800102 LOG(DEBUG) << " EOS in advanceExtractor_l";
Linus Nilsson6233fed2020-08-13 15:15:14 -0700103 mEosReached = true;
104 for (auto it = mTrackSignals.begin(); it != mTrackSignals.end(); ++it) {
105 it->second.notify_all();
106 }
Linus Nilsson478df7e2020-01-29 15:34:24 -0800107 return false;
108 }
109
110 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
111 mExtractorSampleIndex++;
112
113 SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex];
114 if (mExtractorSampleIndex > cursor.previous.index) {
115 if (!cursor.current.isSet) {
116 cursor.current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor));
117 } else if (!cursor.next.isSet && mExtractorSampleIndex > cursor.current.index) {
118 cursor.next.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor));
119 }
120 }
Linus Nilsson6233fed2020-08-13 15:15:14 -0700121
Linus Nilsson478df7e2020-01-29 15:34:24 -0800122 return true;
123}
124
125media_status_t MediaSampleReaderNDK::seekExtractorBackwards_l(int64_t targetTimeUs,
126 int targetTrackIndex,
127 uint64_t targetSampleIndex) {
128 if (targetSampleIndex > mExtractorSampleIndex) {
129 LOG(ERROR) << "Error: Forward seek is not supported";
130 return AMEDIA_ERROR_UNSUPPORTED;
131 }
132
133 // AMediaExtractor supports reading negative timestamps but does not support seeking to them.
134 const int64_t seekToTimeUs = std::max(targetTimeUs, (int64_t)0);
135 media_status_t status =
136 AMediaExtractor_seekTo(mExtractor, seekToTimeUs, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
137 if (status != AMEDIA_OK) {
138 LOG(ERROR) << "Unable to seek to " << seekToTimeUs << ", target " << targetTimeUs;
139 return status;
140 }
Linus Nilsson0c01f3d2020-12-01 09:29:50 -0800141
142 mEosReached = false;
Linus Nilsson478df7e2020-01-29 15:34:24 -0800143 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
144 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
145
146 while (sampleTimeUs != targetTimeUs || mExtractorTrackIndex != targetTrackIndex) {
147 if (!AMediaExtractor_advance(mExtractor)) {
148 return AMEDIA_ERROR_END_OF_STREAM;
149 }
150 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
151 sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
152 }
153 mExtractorSampleIndex = targetSampleIndex;
154 return AMEDIA_OK;
155}
156
Linus Nilsson6233fed2020-08-13 15:15:14 -0700157media_status_t MediaSampleReaderNDK::moveToSample_l(SamplePosition& pos, int trackIndex) {
158 // Seek backwards if the extractor is ahead of the sample.
159 if (pos.isSet && mExtractorSampleIndex > pos.index) {
160 media_status_t status = seekExtractorBackwards_l(pos.timeStampUs, trackIndex, pos.index);
Linus Nilsson478df7e2020-01-29 15:34:24 -0800161 if (status != AMEDIA_OK) return status;
162 }
163
Linus Nilsson6233fed2020-08-13 15:15:14 -0700164 // Advance until extractor points to the sample.
165 while (!(pos.isSet && pos.index == mExtractorSampleIndex)) {
Linus Nilsson478df7e2020-01-29 15:34:24 -0800166 if (!advanceExtractor_l()) {
167 return AMEDIA_ERROR_END_OF_STREAM;
168 }
169 }
170
171 return AMEDIA_OK;
172}
173
Linus Nilsson6233fed2020-08-13 15:15:14 -0700174media_status_t MediaSampleReaderNDK::moveToTrack_l(int trackIndex) {
175 return moveToSample_l(mTrackCursors[trackIndex].current, trackIndex);
176}
177
178media_status_t MediaSampleReaderNDK::waitForTrack_l(int trackIndex,
179 std::unique_lock<std::mutex>& lockHeld) {
180 while (trackIndex != mExtractorTrackIndex && !mEosReached && mEnforceSequentialAccess) {
181 mTrackSignals[trackIndex].wait(lockHeld);
182 }
183
184 if (mEosReached) {
185 return AMEDIA_ERROR_END_OF_STREAM;
186 }
Linus Nilssonfdb3e332020-09-18 17:11:41 -0700187
188 if (!mEnforceSequentialAccess) {
189 return moveToTrack_l(trackIndex);
190 }
191
Linus Nilsson6233fed2020-08-13 15:15:14 -0700192 return AMEDIA_OK;
193}
194
195media_status_t MediaSampleReaderNDK::primeExtractorForTrack_l(
196 int trackIndex, std::unique_lock<std::mutex>& lockHeld) {
197 if (mExtractorTrackIndex < 0) {
198 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
199 if (mExtractorTrackIndex < 0) {
200 return AMEDIA_ERROR_END_OF_STREAM;
201 }
202 mTrackCursors[mExtractorTrackIndex].current.set(mExtractorSampleIndex,
203 AMediaExtractor_getSampleTime(mExtractor));
204 }
205
206 if (mEnforceSequentialAccess) {
207 return waitForTrack_l(trackIndex, lockHeld);
208 } else {
209 return moveToTrack_l(trackIndex);
210 }
211}
212
213media_status_t MediaSampleReaderNDK::selectTrack(int trackIndex) {
Linus Nilsson800793f2020-07-31 16:16:38 -0700214 std::scoped_lock lock(mExtractorMutex);
Linus Nilsson800793f2020-07-31 16:16:38 -0700215
216 if (trackIndex < 0 || trackIndex >= mTrackCount) {
217 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
218 return AMEDIA_ERROR_INVALID_PARAMETER;
Linus Nilsson6233fed2020-08-13 15:15:14 -0700219 } else if (mTrackSignals.find(trackIndex) != mTrackSignals.end()) {
220 LOG(ERROR) << "TrackIndex " << trackIndex << " already selected";
221 return AMEDIA_ERROR_INVALID_PARAMETER;
222 } else if (mExtractorTrackIndex >= 0) {
223 LOG(ERROR) << "Tracks must be selected before sample reading begins.";
224 return AMEDIA_ERROR_UNSUPPORTED;
225 }
226
227 media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex);
228 if (status != AMEDIA_OK) {
229 LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status;
230 return status;
231 }
232
233 mTrackSignals.emplace(std::piecewise_construct, std::forward_as_tuple(trackIndex),
234 std::forward_as_tuple());
235 return AMEDIA_OK;
236}
237
Linus Nilssonaf4a3212020-12-15 08:18:25 -0800238media_status_t MediaSampleReaderNDK::unselectTrack(int trackIndex) {
239 std::scoped_lock lock(mExtractorMutex);
240
241 if (trackIndex < 0 || trackIndex >= mTrackCount) {
242 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
243 return AMEDIA_ERROR_INVALID_PARAMETER;
244 } else if (mExtractorTrackIndex >= 0) {
245 LOG(ERROR) << "unselectTrack must be called before sample reading begins.";
246 return AMEDIA_ERROR_UNSUPPORTED;
247 }
248
249 auto it = mTrackSignals.find(trackIndex);
250 if (it == mTrackSignals.end()) {
251 LOG(ERROR) << "TrackIndex " << trackIndex << " is not selected";
252 return AMEDIA_ERROR_INVALID_PARAMETER;
253 }
254 mTrackSignals.erase(it);
255
256 media_status_t status = AMediaExtractor_unselectTrack(mExtractor, trackIndex);
257 if (status != AMEDIA_OK) {
258 LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status;
259 return status;
260 }
261
262 return AMEDIA_OK;
263}
264
Linus Nilsson6233fed2020-08-13 15:15:14 -0700265media_status_t MediaSampleReaderNDK::setEnforceSequentialAccess(bool enforce) {
Linus Nilsson0c01f3d2020-12-01 09:29:50 -0800266 LOG(DEBUG) << "setEnforceSequentialAccess( " << enforce << " )";
267
Linus Nilsson6233fed2020-08-13 15:15:14 -0700268 std::scoped_lock lock(mExtractorMutex);
269
270 if (mEnforceSequentialAccess && !enforce) {
271 // If switching from enforcing to not enforcing sequential access there may be threads
272 // waiting that needs to be woken up.
273 for (auto it = mTrackSignals.begin(); it != mTrackSignals.end(); ++it) {
274 it->second.notify_all();
275 }
276 } else if (!mEnforceSequentialAccess && enforce && mExtractorTrackIndex >= 0) {
277 // If switching from not enforcing to enforcing sequential access the extractor needs to be
278 // positioned for the track farthest behind so that it won't get stuck waiting.
279 struct {
280 SamplePosition* pos = nullptr;
281 int trackIndex = -1;
282 } earliestSample;
283
284 for (int trackIndex = 0; trackIndex < mTrackCount; ++trackIndex) {
285 SamplePosition& lastKnownTrackPosition = mTrackCursors[trackIndex].current.isSet
286 ? mTrackCursors[trackIndex].current
287 : mTrackCursors[trackIndex].previous;
288
289 if (lastKnownTrackPosition.isSet) {
290 if (earliestSample.pos == nullptr ||
291 earliestSample.pos->index > lastKnownTrackPosition.index) {
292 earliestSample.pos = &lastKnownTrackPosition;
293 earliestSample.trackIndex = trackIndex;
294 }
295 }
296 }
297
298 if (earliestSample.pos == nullptr) {
299 LOG(ERROR) << "No known sample position found";
300 return AMEDIA_ERROR_UNKNOWN;
301 }
302
303 media_status_t status = moveToSample_l(*earliestSample.pos, earliestSample.trackIndex);
304 if (status != AMEDIA_OK) return status;
305
306 while (!(mTrackCursors[mExtractorTrackIndex].current.isSet &&
307 mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex)) {
308 if (!advanceExtractor_l()) {
309 return AMEDIA_ERROR_END_OF_STREAM;
310 }
311 }
312 }
313
314 mEnforceSequentialAccess = enforce;
315 return AMEDIA_OK;
316}
317
318media_status_t MediaSampleReaderNDK::getEstimatedBitrateForTrack(int trackIndex, int32_t* bitrate) {
319 std::scoped_lock lock(mExtractorMutex);
320 media_status_t status = AMEDIA_OK;
321
322 if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) {
323 LOG(ERROR) << "Track is not selected.";
324 return AMEDIA_ERROR_INVALID_PARAMETER;
Linus Nilsson800793f2020-07-31 16:16:38 -0700325 } else if (bitrate == nullptr) {
326 LOG(ERROR) << "bitrate pointer is NULL.";
327 return AMEDIA_ERROR_INVALID_PARAMETER;
Linus Nilsson6233fed2020-08-13 15:15:14 -0700328 } else if (mExtractorTrackIndex >= 0) {
329 LOG(ERROR) << "getEstimatedBitrateForTrack must be called before sample reading begins.";
330 return AMEDIA_ERROR_UNSUPPORTED;
Linus Nilsson800793f2020-07-31 16:16:38 -0700331 }
332
333 // Sample the track.
334 static constexpr int64_t kSamplingDurationUs = 10 * 1000 * 1000; // 10 seconds
335 size_t lastSampleSize = 0;
336 size_t totalSampleSize = 0;
337 int64_t firstSampleTimeUs = 0;
338 int64_t lastSampleTimeUs = 0;
339
340 do {
Linus Nilsson6233fed2020-08-13 15:15:14 -0700341 if (AMediaExtractor_getSampleTrackIndex(mExtractor) == trackIndex) {
Linus Nilsson800793f2020-07-31 16:16:38 -0700342 lastSampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
343 if (totalSampleSize == 0) {
344 firstSampleTimeUs = lastSampleTimeUs;
345 }
346
347 lastSampleSize = AMediaExtractor_getSampleSize(mExtractor);
348 totalSampleSize += lastSampleSize;
349 }
Linus Nilsson6233fed2020-08-13 15:15:14 -0700350 } while ((lastSampleTimeUs - firstSampleTimeUs) < kSamplingDurationUs &&
351 AMediaExtractor_advance(mExtractor));
352
353 // Reset the extractor to the beginning.
354 status = AMediaExtractor_seekTo(mExtractor, 0, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
355 if (status != AMEDIA_OK) {
356 LOG(ERROR) << "Unable to reset extractor: " << status;
357 return status;
358 }
Linus Nilsson800793f2020-07-31 16:16:38 -0700359
360 int64_t durationUs = 0;
361 const int64_t sampledDurationUs = lastSampleTimeUs - firstSampleTimeUs;
362
363 if (sampledDurationUs < kSamplingDurationUs) {
364 // Track is shorter than the sampling duration so use the full track duration to get better
365 // accuracy (i.e. don't skip the last sample).
366 AMediaFormat* trackFormat = getTrackFormat(trackIndex);
367 if (!AMediaFormat_getInt64(trackFormat, AMEDIAFORMAT_KEY_DURATION, &durationUs)) {
368 durationUs = 0;
369 }
370 AMediaFormat_delete(trackFormat);
371 }
372
373 if (durationUs == 0) {
374 // The sampled duration does not account for the last sample's duration so its size should
375 // not be included either.
376 totalSampleSize -= lastSampleSize;
377 durationUs = sampledDurationUs;
378 }
379
380 if (totalSampleSize == 0 || durationUs <= 0) {
381 LOG(ERROR) << "Unable to estimate track bitrate";
382 return AMEDIA_ERROR_MALFORMED;
383 }
384
385 *bitrate = roundf((float)totalSampleSize * 8 * 1000000 / durationUs);
386 return AMEDIA_OK;
387}
388
Linus Nilsson478df7e2020-01-29 15:34:24 -0800389media_status_t MediaSampleReaderNDK::getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) {
Linus Nilsson6233fed2020-08-13 15:15:14 -0700390 std::unique_lock<std::mutex> lock(mExtractorMutex);
Linus Nilsson478df7e2020-01-29 15:34:24 -0800391
Linus Nilsson6233fed2020-08-13 15:15:14 -0700392 if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) {
393 LOG(ERROR) << "Track not selected.";
Linus Nilsson478df7e2020-01-29 15:34:24 -0800394 return AMEDIA_ERROR_INVALID_PARAMETER;
395 } else if (info == nullptr) {
396 LOG(ERROR) << "MediaSampleInfo pointer is NULL.";
397 return AMEDIA_ERROR_INVALID_PARAMETER;
398 }
399
Linus Nilsson6233fed2020-08-13 15:15:14 -0700400 media_status_t status = primeExtractorForTrack_l(trackIndex, lock);
Linus Nilsson478df7e2020-01-29 15:34:24 -0800401 if (status == AMEDIA_OK) {
402 info->presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor);
403 info->flags = AMediaExtractor_getSampleFlags(mExtractor);
404 info->size = AMediaExtractor_getSampleSize(mExtractor);
405 } else if (status == AMEDIA_ERROR_END_OF_STREAM) {
406 info->presentationTimeUs = 0;
407 info->flags = SAMPLE_FLAG_END_OF_STREAM;
408 info->size = 0;
Linus Nilsson0c01f3d2020-12-01 09:29:50 -0800409 LOG(DEBUG) << " getSampleInfoForTrack #" << trackIndex << ": End Of Stream";
410 } else {
411 LOG(ERROR) << " getSampleInfoForTrack #" << trackIndex << ": Error " << status;
Linus Nilsson478df7e2020-01-29 15:34:24 -0800412 }
Linus Nilsson0c01f3d2020-12-01 09:29:50 -0800413
Linus Nilsson478df7e2020-01-29 15:34:24 -0800414 return status;
415}
416
417media_status_t MediaSampleReaderNDK::readSampleDataForTrack(int trackIndex, uint8_t* buffer,
418 size_t bufferSize) {
Linus Nilsson6233fed2020-08-13 15:15:14 -0700419 std::unique_lock<std::mutex> lock(mExtractorMutex);
Linus Nilsson478df7e2020-01-29 15:34:24 -0800420
Linus Nilsson6233fed2020-08-13 15:15:14 -0700421 if (mTrackSignals.find(trackIndex) == mTrackSignals.end()) {
422 LOG(ERROR) << "Track not selected.";
Linus Nilsson478df7e2020-01-29 15:34:24 -0800423 return AMEDIA_ERROR_INVALID_PARAMETER;
424 } else if (buffer == nullptr) {
425 LOG(ERROR) << "buffer pointer is NULL";
426 return AMEDIA_ERROR_INVALID_PARAMETER;
427 }
428
Linus Nilsson6233fed2020-08-13 15:15:14 -0700429 media_status_t status = primeExtractorForTrack_l(trackIndex, lock);
430 if (status != AMEDIA_OK) {
431 return status;
432 }
Linus Nilsson478df7e2020-01-29 15:34:24 -0800433
434 ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor);
435 if (bufferSize < sampleSize) {
436 LOG(ERROR) << "Buffer is too small for sample, " << bufferSize << " vs " << sampleSize;
437 return AMEDIA_ERROR_INVALID_PARAMETER;
438 }
439
440 ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize);
441 if (bytesRead < sampleSize) {
442 LOG(ERROR) << "Unable to read full sample, " << bytesRead << " vs " << sampleSize;
443 return AMEDIA_ERROR_INVALID_PARAMETER;
444 }
445
Linus Nilsson6233fed2020-08-13 15:15:14 -0700446 advanceTrack_l(trackIndex);
447
Linus Nilsson478df7e2020-01-29 15:34:24 -0800448 return AMEDIA_OK;
449}
450
Linus Nilsson6233fed2020-08-13 15:15:14 -0700451void MediaSampleReaderNDK::advanceTrack(int trackIndex) {
452 std::scoped_lock lock(mExtractorMutex);
453
454 if (mTrackSignals.find(trackIndex) != mTrackSignals.end()) {
455 advanceTrack_l(trackIndex);
456 } else {
457 LOG(ERROR) << "Trying to advance a track that is not selected (#" << trackIndex << ")";
458 }
459}
460
Linus Nilsson478df7e2020-01-29 15:34:24 -0800461AMediaFormat* MediaSampleReaderNDK::getFileFormat() {
462 return AMediaExtractor_getFileFormat(mExtractor);
463}
464
465size_t MediaSampleReaderNDK::getTrackCount() const {
466 return mTrackCount;
467}
468
469AMediaFormat* MediaSampleReaderNDK::getTrackFormat(int trackIndex) {
470 if (trackIndex < 0 || trackIndex >= mTrackCount) {
471 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
472 return AMediaFormat_new();
473 }
474
475 return AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
476}
477
478} // namespace android