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