blob: a0096c7d273d53a4c19cc9e02e58ec95b3c9ae14 [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>
24#include <vector>
25
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));
49 status = sampleReader->init();
50 if (status != AMEDIA_OK) {
51 LOG(ERROR) << "MediaSampleReaderNDK::init returned error: " << status;
52 return nullptr;
53 }
54
55 return sampleReader;
56}
57
58MediaSampleReaderNDK::MediaSampleReaderNDK(AMediaExtractor* extractor)
59 : mExtractor(extractor), mTrackCount(AMediaExtractor_getTrackCount(mExtractor)) {
60 if (mTrackCount > 0) {
61 mTrackCursors.resize(mTrackCount);
62 mTrackCursors.resize(mTrackCount);
63 }
64}
65
66media_status_t MediaSampleReaderNDK::init() {
67 for (size_t trackIndex = 0; trackIndex < mTrackCount; trackIndex++) {
68 media_status_t status = AMediaExtractor_selectTrack(mExtractor, trackIndex);
69 if (status != AMEDIA_OK) {
70 LOG(ERROR) << "AMediaExtractor_selectTrack returned error: " << status;
71 return status;
72 }
73 }
74
75 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
76 if (mExtractorTrackIndex >= 0) {
77 mTrackCursors[mExtractorTrackIndex].current.set(mExtractorSampleIndex,
78 AMediaExtractor_getSampleTime(mExtractor));
79 } else if (mTrackCount > 0) {
80 // The extractor track index is only allowed to be invalid if there are no tracks.
81 LOG(ERROR) << "Track index " << mExtractorTrackIndex << " is invalid for track count "
82 << mTrackCount;
83 return AMEDIA_ERROR_MALFORMED;
84 }
85
86 return AMEDIA_OK;
87}
88
89MediaSampleReaderNDK::~MediaSampleReaderNDK() {
90 if (mExtractor != nullptr) {
91 AMediaExtractor_delete(mExtractor);
92 }
93}
94
95bool MediaSampleReaderNDK::advanceExtractor_l() {
96 // Reset the "next" sample time whenever the extractor advances past a sample that is current,
97 // to ensure that "next" is appropriately updated when the extractor advances over the next
98 // sample of that track.
99 if (mTrackCursors[mExtractorTrackIndex].current.isSet &&
100 mTrackCursors[mExtractorTrackIndex].current.index == mExtractorSampleIndex) {
101 mTrackCursors[mExtractorTrackIndex].next.reset();
102 }
103
104 if (!AMediaExtractor_advance(mExtractor)) {
105 return false;
106 }
107
108 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
109 mExtractorSampleIndex++;
110
111 SampleCursor& cursor = mTrackCursors[mExtractorTrackIndex];
112 if (mExtractorSampleIndex > cursor.previous.index) {
113 if (!cursor.current.isSet) {
114 cursor.current.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor));
115 } else if (!cursor.next.isSet && mExtractorSampleIndex > cursor.current.index) {
116 cursor.next.set(mExtractorSampleIndex, AMediaExtractor_getSampleTime(mExtractor));
117 }
118 }
119 return true;
120}
121
122media_status_t MediaSampleReaderNDK::seekExtractorBackwards_l(int64_t targetTimeUs,
123 int targetTrackIndex,
124 uint64_t targetSampleIndex) {
125 if (targetSampleIndex > mExtractorSampleIndex) {
126 LOG(ERROR) << "Error: Forward seek is not supported";
127 return AMEDIA_ERROR_UNSUPPORTED;
128 }
129
130 // AMediaExtractor supports reading negative timestamps but does not support seeking to them.
131 const int64_t seekToTimeUs = std::max(targetTimeUs, (int64_t)0);
132 media_status_t status =
133 AMediaExtractor_seekTo(mExtractor, seekToTimeUs, AMEDIAEXTRACTOR_SEEK_PREVIOUS_SYNC);
134 if (status != AMEDIA_OK) {
135 LOG(ERROR) << "Unable to seek to " << seekToTimeUs << ", target " << targetTimeUs;
136 return status;
137 }
138 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
139 int64_t sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
140
141 while (sampleTimeUs != targetTimeUs || mExtractorTrackIndex != targetTrackIndex) {
142 if (!AMediaExtractor_advance(mExtractor)) {
143 return AMEDIA_ERROR_END_OF_STREAM;
144 }
145 mExtractorTrackIndex = AMediaExtractor_getSampleTrackIndex(mExtractor);
146 sampleTimeUs = AMediaExtractor_getSampleTime(mExtractor);
147 }
148 mExtractorSampleIndex = targetSampleIndex;
149 return AMEDIA_OK;
150}
151
152void MediaSampleReaderNDK::advanceTrack(int trackIndex) {
153 std::scoped_lock lock(mExtractorMutex);
154
155 if (trackIndex < 0 || trackIndex >= mTrackCount) {
156 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
157 return;
158 }
159
160 // Note: Positioning the extractor before advancing the track is needed for two reasons:
161 // 1. To enable multiple advances without explicitly letting the extractor catch up.
162 // 2. To prevent the extractor from being farther than "next".
163 (void)positionExtractorForTrack_l(trackIndex);
164
165 SampleCursor& cursor = mTrackCursors[trackIndex];
166 cursor.previous = cursor.current;
167 cursor.current = cursor.next;
168 cursor.next.reset();
169}
170
171media_status_t MediaSampleReaderNDK::positionExtractorForTrack_l(int trackIndex) {
172 media_status_t status = AMEDIA_OK;
173 const SampleCursor& cursor = mTrackCursors[trackIndex];
174
175 // Seek backwards if the extractor is ahead of the current time.
176 if (cursor.current.isSet && mExtractorSampleIndex > cursor.current.index) {
177 status = seekExtractorBackwards_l(cursor.current.timeStampUs, trackIndex,
178 cursor.current.index);
179 if (status != AMEDIA_OK) return status;
180 }
181
182 // Advance until extractor points to the current sample.
183 while (!(cursor.current.isSet && cursor.current.index == mExtractorSampleIndex)) {
184 if (!advanceExtractor_l()) {
185 return AMEDIA_ERROR_END_OF_STREAM;
186 }
187 }
188
189 return AMEDIA_OK;
190}
191
192media_status_t MediaSampleReaderNDK::getSampleInfoForTrack(int trackIndex, MediaSampleInfo* info) {
193 std::scoped_lock lock(mExtractorMutex);
194
195 if (trackIndex < 0 || trackIndex >= mTrackCount) {
196 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
197 return AMEDIA_ERROR_INVALID_PARAMETER;
198 } else if (info == nullptr) {
199 LOG(ERROR) << "MediaSampleInfo pointer is NULL.";
200 return AMEDIA_ERROR_INVALID_PARAMETER;
201 }
202
203 media_status_t status = positionExtractorForTrack_l(trackIndex);
204 if (status == AMEDIA_OK) {
205 info->presentationTimeUs = AMediaExtractor_getSampleTime(mExtractor);
206 info->flags = AMediaExtractor_getSampleFlags(mExtractor);
207 info->size = AMediaExtractor_getSampleSize(mExtractor);
208 } else if (status == AMEDIA_ERROR_END_OF_STREAM) {
209 info->presentationTimeUs = 0;
210 info->flags = SAMPLE_FLAG_END_OF_STREAM;
211 info->size = 0;
212 }
213
214 return status;
215}
216
217media_status_t MediaSampleReaderNDK::readSampleDataForTrack(int trackIndex, uint8_t* buffer,
218 size_t bufferSize) {
219 std::scoped_lock lock(mExtractorMutex);
220
221 if (trackIndex < 0 || trackIndex >= mTrackCount) {
222 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
223 return AMEDIA_ERROR_INVALID_PARAMETER;
224 } else if (buffer == nullptr) {
225 LOG(ERROR) << "buffer pointer is NULL";
226 return AMEDIA_ERROR_INVALID_PARAMETER;
227 }
228
229 media_status_t status = positionExtractorForTrack_l(trackIndex);
230 if (status != AMEDIA_OK) return status;
231
232 ssize_t sampleSize = AMediaExtractor_getSampleSize(mExtractor);
233 if (bufferSize < sampleSize) {
234 LOG(ERROR) << "Buffer is too small for sample, " << bufferSize << " vs " << sampleSize;
235 return AMEDIA_ERROR_INVALID_PARAMETER;
236 }
237
238 ssize_t bytesRead = AMediaExtractor_readSampleData(mExtractor, buffer, bufferSize);
239 if (bytesRead < sampleSize) {
240 LOG(ERROR) << "Unable to read full sample, " << bytesRead << " vs " << sampleSize;
241 return AMEDIA_ERROR_INVALID_PARAMETER;
242 }
243
244 return AMEDIA_OK;
245}
246
247AMediaFormat* MediaSampleReaderNDK::getFileFormat() {
248 return AMediaExtractor_getFileFormat(mExtractor);
249}
250
251size_t MediaSampleReaderNDK::getTrackCount() const {
252 return mTrackCount;
253}
254
255AMediaFormat* MediaSampleReaderNDK::getTrackFormat(int trackIndex) {
256 if (trackIndex < 0 || trackIndex >= mTrackCount) {
257 LOG(ERROR) << "Invalid trackIndex " << trackIndex << " for trackCount " << mTrackCount;
258 return AMediaFormat_new();
259 }
260
261 return AMediaExtractor_getTrackFormat(mExtractor, trackIndex);
262}
263
264} // namespace android