1 /*
2 * Copyright (C) 2017 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 "NativeMediaEnc-Source"
19 #include <log/log.h>
20
21 #include "native_media_source.h"
22
23 using namespace Utils;
24
25 class DecoderSource : public Thread, public Source {
26 public:
27 DecoderSource(
28 int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping, bool regulate);
29 ~DecoderSource();
30 DecoderSource(const DecoderSource& ) = delete;
31
32 Status setDataSourceFd(int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize);
33 Status prepare(std::shared_ptr<Listener> l, std::shared_ptr<ANativeWindow> n) override;
34 Status start() override;
35 Status stop() override;
36
37 protected:
38 void run() override;
39
40 private:
41 // seek the extractor back to beginning
42 void rewindExtractor();
43
44 // When setting dynamic params, if the source is faster than the encoder,
45 // there is a possibility of param set via setParameters() will get delayed.
46 // Simulate a real-time source by slowing down the feeding rate (up to configured fps)
47 bool mRegulateFramerate;
48
49 std::shared_ptr<AMediaExtractor> mEx;
50 std::shared_ptr<AMediaCodec> mDec;
51 std::shared_ptr<AMediaFormat> mFormat;
52 std::string mMime;
53 int mVideoTrackIndex;
54 int mFrameCount;
55 bool mStopRequest;
56 bool mStarted;
57 };
58
createDecoderSource(int32_t w,int32_t h,int32_t colorFormat,float fps,bool looping,bool regulateFeedingRate,int sourceFileFd,off64_t sourceFileOffset,off64_t sourceFileSize)59 std::shared_ptr<Source> createDecoderSource(
60 int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping,
61 bool regulateFeedingRate, /* WA for dynamic settings */
62 int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize) {
63 DecoderSource *d = new DecoderSource(w, h, colorFormat, fps, looping, regulateFeedingRate);
64 d->setDataSourceFd(sourceFileFd, sourceFileOffset, sourceFileSize);
65 std::shared_ptr<Source> src(d);
66 return src;
67 }
68
DecoderSource(int32_t w,int32_t h,int32_t colorFormat,float fps,bool looping,bool regulate)69 DecoderSource::DecoderSource(
70 int32_t w, int32_t h, int32_t colorFormat, float fps, bool looping, bool regulate)
71 : Source(w, h, colorFormat, fps, looping),
72 mRegulateFramerate(regulate),
73 mEx(nullptr),
74 mDec(nullptr),
75 mFormat(nullptr),
76 mMime(""),
77 mVideoTrackIndex(-1),
78 mFrameCount(0),
79 mStopRequest(false),
80 mStarted(false) {
81 }
82
setDataSourceFd(int sourceFileFd,off64_t sourceFileOffset,off64_t sourceFileSize)83 Status DecoderSource::setDataSourceFd(
84 int sourceFileFd, off64_t sourceFileOffset, off64_t sourceFileSize) {
85
86 mEx = std::shared_ptr<AMediaExtractor>(AMediaExtractor_new(), deleter_AMediExtractor);
87 int err = AMediaExtractor_setDataSourceFd(mEx.get(), sourceFileFd, sourceFileOffset, sourceFileSize);
88 if (err != 0) {
89 ALOGE("setDataSource error: %d", err);
90 return FAIL;
91 }
92
93 const char *mime;
94 mVideoTrackIndex = -1;
95 int numtracks = AMediaExtractor_getTrackCount(mEx.get());
96 for (int i = 0; i < numtracks; i++) {
97 AMediaFormat *format = AMediaExtractor_getTrackFormat(mEx.get(), i);
98 const char *s = AMediaFormat_toString(format);
99 ALOGV("track %d format: %s", i, s);
100 if (!AMediaFormat_getString(format, AMEDIAFORMAT_KEY_MIME, &mime)) {
101 ALOGE("no mime type");
102 mEx = nullptr;
103 AMediaFormat_delete(format);
104 return FAIL;
105 } else if (!strncmp(mime, "video/", 6)) {
106 mVideoTrackIndex = i;
107 mFormat = std::shared_ptr<AMediaFormat>(format, deleter_AMediaFormat);
108 mMime = mime;
109 break;
110 } else {
111 ALOGE("expected video mime type, got %s", mime);
112 mEx = nullptr;
113 }
114 AMediaFormat_delete(format);
115 }
116 return mVideoTrackIndex == -1 ? FAIL : OK;
117 }
118
~DecoderSource()119 DecoderSource::~DecoderSource() {
120 mDec = nullptr;
121 mEx = nullptr;
122 mFormat = nullptr;
123 }
124
run()125 void DecoderSource::run() {
126 while(!mStopRequest) {
127 int t = AMediaExtractor_getSampleTrackIndex(mEx.get());
128 if (t < 0) {
129 if (mLooping) {
130 rewindExtractor();
131 continue;
132 } else {
133 ALOGV("no more samples");
134 break;
135 }
136 } else if (t != mVideoTrackIndex) {
137 continue;
138 }
139
140 ssize_t bufidx = AMediaCodec_dequeueInputBuffer(mDec.get(), 5000);
141 if (bufidx >= 0) {
142 size_t bufsize;
143 uint8_t *buf = AMediaCodec_getInputBuffer(mDec.get(), bufidx, &bufsize);
144 int sampleSize = AMediaExtractor_readSampleData(mEx.get(), buf, bufsize);
145 int32_t flags = 0;
146 if (sampleSize < 0) {
147 if (mLooping) {
148 rewindExtractor();
149 continue;
150 } else {
151 flags = AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM;
152 }
153 }
154 // synthesize timestamps based on required fps
155 int64_t timeStampUs = 1e6 / mFps * mFrameCount;
156 AMediaCodec_queueInputBuffer(mDec.get(), bufidx, 0, sampleSize, timeStampUs, flags);
157
158 AMediaExtractor_advance(mEx.get());
159 ++mFrameCount;
160 }
161
162 AMediaCodecBufferInfo info;
163 int status = AMediaCodec_dequeueOutputBuffer(mDec.get(), &info, 1000);
164 if (status >= 0) {
165 ALOGV("got decoded buffer of size=%d @%lld us",
166 info.size, (long long)info.presentationTimeUs);
167 bool render = info.size > 0;
168 if (mBufListener != nullptr) {
169 //TBD
170 //mBufListener->onBufferAvailable(..);
171 }
172
173 // WA: if decoder runs free, dynamic settings applied by
174 // MediaCodec.setParameters() are off
175 if (mRegulateFramerate) {
176 usleep(1e6/mFps);
177 }
178
179 AMediaCodec_releaseOutputBuffer(mDec.get(), status, render);
180
181 if (info.flags & AMEDIACODEC_BUFFER_FLAG_END_OF_STREAM) {
182 ALOGV("saw EOS");
183 break;
184 }
185
186 } else if (status == AMEDIACODEC_INFO_OUTPUT_BUFFERS_CHANGED) {
187 } else if (status == AMEDIACODEC_INFO_OUTPUT_FORMAT_CHANGED) {
188 mFormat = std::shared_ptr<AMediaFormat>(
189 AMediaCodec_getOutputFormat(mDec.get()), deleter_AMediaFormat);
190 ALOGV("format changed: %s", AMediaFormat_toString(mFormat.get()));
191 } else if (status == AMEDIACODEC_INFO_TRY_AGAIN_LATER) {
192 } else {
193 ALOGV("Invalid status : %d", status);
194 }
195 }
196 }
197
prepare(std::shared_ptr<Listener> l,std::shared_ptr<ANativeWindow> n)198 Status DecoderSource::prepare(
199 std::shared_ptr<Listener> l, std::shared_ptr<ANativeWindow> n) {
200
201 mBufListener = l;
202 mSurface = n;
203
204 if (mVideoTrackIndex < 0) {
205 ALOGE("Video track not found !");
206 return FAIL;
207 }
208
209 assert(mEx.get() != nullptr);
210 AMediaExtractor_selectTrack(mEx.get(), mVideoTrackIndex);
211
212 AMediaCodec *dec = AMediaCodec_createDecoderByType(mMime.c_str());
213 mDec = std::shared_ptr<AMediaCodec>(dec, deleter_AMediaCodec);
214
215 ALOGI("configure decoder. surface = %p", mSurface.get());
216 media_status_t status = AMediaCodec_configure(
217 mDec.get(), mFormat.get(), mSurface.get(), NULL /* crypto */, 0);
218 if (status != AMEDIA_OK) {
219 ALOGE("failed to configure decoder");
220 return FAIL;
221 }
222 return OK;
223 }
224
start()225 Status DecoderSource::start() {
226 ALOGV("start");
227 media_status_t status = AMediaCodec_start(mDec.get());
228 if (status != AMEDIA_OK) {
229 ALOGE("failed to start decoder");
230 return FAIL;
231 }
232 if (startThread() != OK) {
233 return FAIL;
234 }
235 mStarted = true;
236 return OK;
237 }
238
stop()239 Status DecoderSource::stop() {
240 if (!mStarted) {
241 return FAIL;
242 }
243
244 ALOGV("Stopping decoder source..");
245 mStopRequest = true;
246 joinThread();
247
248 media_status_t status = AMediaCodec_stop(mDec.get());
249 if (status != AMEDIA_OK) {
250 ALOGE("failed to stop decoder");
251 }
252
253 mDec = nullptr;
254 mEx = nullptr;
255 mFormat = nullptr;
256 return OK;
257 }
258
rewindExtractor()259 void DecoderSource::rewindExtractor() {
260 assert(mEx.get() != nullptr);
261 media_status_t status = AMediaExtractor_seekTo(mEx.get(), 0, AMEDIAEXTRACTOR_SEEK_CLOSEST_SYNC);
262 if (status != AMEDIA_OK) {
263 ALOGE("failed to seek Extractor to 0");
264 }
265 }
266
267