• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
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 "MediaTranscoder"
19 
20 #include <android-base/logging.h>
21 #include <fcntl.h>
22 #include <media/MediaSampleReaderNDK.h>
23 #include <media/MediaSampleWriter.h>
24 #include <media/MediaTranscoder.h>
25 #include <media/NdkCommon.h>
26 #include <media/PassthroughTrackTranscoder.h>
27 #include <media/VideoTrackTranscoder.h>
28 #include <sys/prctl.h>
29 #include <unistd.h>
30 
31 namespace android {
32 
createVideoTrackFormat(AMediaFormat * srcFormat,AMediaFormat * options)33 static std::shared_ptr<AMediaFormat> createVideoTrackFormat(AMediaFormat* srcFormat,
34                                                             AMediaFormat* options) {
35     if (srcFormat == nullptr || options == nullptr) {
36         LOG(ERROR) << "Cannot merge null formats";
37         return nullptr;
38     }
39 
40     // ------- Define parameters to copy from the source track format -------
41     std::vector<AMediaFormatUtils::EntryCopier> srcParamsToCopy{
42             ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
43             ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
44             ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
45             ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
46             ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
47             ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
48             ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
49             ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
50     };
51 
52     // If the destination codec is the same as the source codec, we can preserve profile and level
53     // from the source track as default values. Otherwise leave them unspecified.
54     const char *srcMime, *dstMime;
55     AMediaFormat_getString(srcFormat, AMEDIAFORMAT_KEY_MIME, &srcMime);
56     if (!AMediaFormat_getString(options, AMEDIAFORMAT_KEY_MIME, &dstMime) ||
57         strcmp(srcMime, dstMime) == 0) {
58         srcParamsToCopy.push_back(ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, String));
59         srcParamsToCopy.push_back(ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, String));
60     }
61 
62     // ------- Define parameters to copy from the caller's options -------
63     static const std::vector<AMediaFormatUtils::EntryCopier> kSupportedOptions{
64             ENTRY_COPIER(AMEDIAFORMAT_KEY_MIME, String),
65             ENTRY_COPIER(AMEDIAFORMAT_KEY_DURATION, Int64),
66             ENTRY_COPIER(AMEDIAFORMAT_KEY_WIDTH, Int32),
67             ENTRY_COPIER(AMEDIAFORMAT_KEY_HEIGHT, Int32),
68             ENTRY_COPIER(AMEDIAFORMAT_KEY_BIT_RATE, Int32),
69             ENTRY_COPIER(AMEDIAFORMAT_KEY_PROFILE, Int32),
70             ENTRY_COPIER(AMEDIAFORMAT_KEY_LEVEL, Int32),
71             ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_RANGE, Int32),
72             ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_STANDARD, Int32),
73             ENTRY_COPIER(AMEDIAFORMAT_KEY_COLOR_TRANSFER, Int32),
74             ENTRY_COPIER(AMEDIAFORMAT_KEY_FRAME_RATE, Int32),
75             ENTRY_COPIER(AMEDIAFORMAT_KEY_I_FRAME_INTERVAL, Int32),
76             ENTRY_COPIER(AMEDIAFORMAT_KEY_PRIORITY, Int32),
77             ENTRY_COPIER2(AMEDIAFORMAT_KEY_OPERATING_RATE, Float, Int32),
78     };
79 
80     // ------- Copy parameters from source and options to the destination -------
81     auto trackFormat = std::shared_ptr<AMediaFormat>(AMediaFormat_new(), &AMediaFormat_delete);
82     AMediaFormatUtils::CopyFormatEntries(srcFormat, trackFormat.get(), srcParamsToCopy);
83     AMediaFormatUtils::CopyFormatEntries(options, trackFormat.get(), kSupportedOptions);
84     return trackFormat;
85 }
86 
onThreadFinished(const void * thread,media_status_t threadStatus,bool threadStopped)87 void MediaTranscoder::onThreadFinished(const void* thread, media_status_t threadStatus,
88                                        bool threadStopped) {
89     LOG(DEBUG) << "Thread " << thread << " finished with status " << threadStatus << " stopped "
90                << threadStopped;
91 
92     // Stop all threads if one reports an error.
93     if (threadStatus != AMEDIA_OK) {
94         requestStop(false /* stopOnSync */);
95     }
96 
97     std::scoped_lock lock{mThreadStateMutex};
98 
99     // Record the change.
100     mThreadStates[thread] = DONE;
101     if (threadStatus != AMEDIA_OK && mTranscoderStatus == AMEDIA_OK) {
102         mTranscoderStatus = threadStatus;
103     }
104 
105     mTranscoderStopped |= threadStopped;
106 
107     // Check if all threads are done. Note that if all transcoders have stopped but the sample
108     // writer has not yet started, it never will.
109     bool transcodersDone = true;
110     ThreadState sampleWriterState = PENDING;
111     for (const auto& it : mThreadStates) {
112         LOG(DEBUG) << "  Thread " << it.first << " state" << it.second;
113         if (it.first == static_cast<const void*>(mSampleWriter.get())) {
114             sampleWriterState = it.second;
115         } else {
116             transcodersDone &= (it.second == DONE);
117         }
118     }
119     if (!transcodersDone || sampleWriterState == RUNNING) {
120         return;
121     }
122 
123     // All done. Send callback asynchronously and wake up threads waiting in cancel/pause.
124     mThreadsDone = true;
125     if (!mCallbackSent) {
126         std::thread asyncNotificationThread{[this, self = shared_from_this(),
127                                              status = mTranscoderStatus,
128                                              stopped = mTranscoderStopped] {
129             prctl(PR_SET_NAME, (unsigned long)"TranscodCallbk", 0, 0, 0);
130 
131             // If the transcoder was stopped that means a caller is waiting in stop or pause
132             // in which case we don't send a callback.
133             if (status != AMEDIA_OK) {
134                 mCallbacks->onError(this, status);
135             } else if (!stopped) {
136                 mCallbacks->onFinished(this);
137             }
138             mThreadsDoneSignal.notify_all();
139         }};
140         asyncNotificationThread.detach();
141         mCallbackSent = true;
142     }
143 }
144 
onTrackFormatAvailable(const MediaTrackTranscoder * transcoder)145 void MediaTranscoder::onTrackFormatAvailable(const MediaTrackTranscoder* transcoder) {
146     LOG(DEBUG) << "TrackTranscoder " << transcoder << " format available.";
147 
148     std::scoped_lock lock{mTracksAddedMutex};
149     const void* sampleWriterPtr = static_cast<const void*>(mSampleWriter.get());
150 
151     // Ignore duplicate format change.
152     if (mTracksAdded.count(transcoder) > 0) {
153         return;
154     }
155 
156     // Add track to the writer.
157     auto consumer = mSampleWriter->addTrack(transcoder->getOutputFormat());
158     if (consumer == nullptr) {
159         LOG(ERROR) << "Unable to add track to sample writer.";
160         onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
161         return;
162     }
163 
164     // The sample writer is not yet started so notify the caller that progress is still made.
165     if (mHeartBeatIntervalUs > 0) {
166         mCallbacks->onHeartBeat(this);
167     }
168 
169     MediaTrackTranscoder* mutableTranscoder = const_cast<MediaTrackTranscoder*>(transcoder);
170     mutableTranscoder->setSampleConsumer(consumer);
171 
172     mTracksAdded.insert(transcoder);
173     bool errorStarting = false;
174     if (mTracksAdded.size() == mTrackTranscoders.size()) {
175         // Enable sequential access mode on the sample reader to achieve optimal read performance.
176         // This has to wait until all tracks have delivered their output formats and the sample
177         // writer is started. Otherwise the tracks will not get their output sample queues drained
178         // and the transcoder could hang due to one track running out of buffers and blocking the
179         // other tracks from reading source samples before they could output their formats.
180 
181         std::scoped_lock lock{mThreadStateMutex};
182         // Don't start the sample writer if a stop already has been requested.
183         if (!mSampleWriterStopped) {
184             if (!mCancelled) {
185                 mSampleReader->setEnforceSequentialAccess(true);
186             }
187             LOG(DEBUG) << "Starting sample writer.";
188             errorStarting = !mSampleWriter->start();
189             if (!errorStarting) {
190                 mThreadStates[sampleWriterPtr] = RUNNING;
191             }
192         }
193     }
194 
195     if (errorStarting) {
196         LOG(ERROR) << "Unable to start sample writer.";
197         onThreadFinished(sampleWriterPtr, AMEDIA_ERROR_UNKNOWN, false /* stopped */);
198     }
199 }
200 
onTrackFinished(const MediaTrackTranscoder * transcoder)201 void MediaTranscoder::onTrackFinished(const MediaTrackTranscoder* transcoder) {
202     LOG(DEBUG) << "TrackTranscoder " << transcoder << " finished";
203     onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, false /* stopped */);
204 }
205 
onTrackStopped(const MediaTrackTranscoder * transcoder)206 void MediaTranscoder::onTrackStopped(const MediaTrackTranscoder* transcoder) {
207     LOG(DEBUG) << "TrackTranscoder " << transcoder << " stopped";
208     onThreadFinished(static_cast<const void*>(transcoder), AMEDIA_OK, true /* stopped */);
209 }
210 
onTrackError(const MediaTrackTranscoder * transcoder,media_status_t status)211 void MediaTranscoder::onTrackError(const MediaTrackTranscoder* transcoder, media_status_t status) {
212     LOG(ERROR) << "TrackTranscoder " << transcoder << " returned error " << status;
213     onThreadFinished(static_cast<const void*>(transcoder), status, false /* stopped */);
214 }
215 
onFinished(const MediaSampleWriter * writer,media_status_t status)216 void MediaTranscoder::onFinished(const MediaSampleWriter* writer, media_status_t status) {
217     LOG(status == AMEDIA_OK ? DEBUG : ERROR) << "Sample writer finished with status " << status;
218     onThreadFinished(static_cast<const void*>(writer), status, false /* stopped */);
219 }
220 
onStopped(const MediaSampleWriter * writer)221 void MediaTranscoder::onStopped(const MediaSampleWriter* writer) {
222     LOG(DEBUG) << "Sample writer " << writer << " stopped";
223     onThreadFinished(static_cast<const void*>(writer), AMEDIA_OK, true /* stopped */);
224 }
225 
onProgressUpdate(const MediaSampleWriter * writer __unused,int32_t progress)226 void MediaTranscoder::onProgressUpdate(const MediaSampleWriter* writer __unused, int32_t progress) {
227     // Dispatch progress updated to the client.
228     mCallbacks->onProgressUpdate(this, progress);
229 }
230 
onHeartBeat(const MediaSampleWriter * writer __unused)231 void MediaTranscoder::onHeartBeat(const MediaSampleWriter* writer __unused) {
232     // Signal heart-beat to the client.
233     mCallbacks->onHeartBeat(this);
234 }
235 
MediaTranscoder(const std::shared_ptr<CallbackInterface> & callbacks,int64_t heartBeatIntervalUs,pid_t pid,uid_t uid)236 MediaTranscoder::MediaTranscoder(const std::shared_ptr<CallbackInterface>& callbacks,
237                                  int64_t heartBeatIntervalUs, pid_t pid, uid_t uid)
238       : mCallbacks(callbacks), mHeartBeatIntervalUs(heartBeatIntervalUs), mPid(pid), mUid(uid) {}
239 
create(const std::shared_ptr<CallbackInterface> & callbacks,int64_t heartBeatIntervalUs,pid_t pid,uid_t uid,const std::shared_ptr<ndk::ScopedAParcel> & pausedState)240 std::shared_ptr<MediaTranscoder> MediaTranscoder::create(
241         const std::shared_ptr<CallbackInterface>& callbacks, int64_t heartBeatIntervalUs, pid_t pid,
242         uid_t uid, const std::shared_ptr<ndk::ScopedAParcel>& pausedState) {
243     if (pausedState != nullptr) {
244         LOG(INFO) << "Initializing from paused state.";
245     }
246     if (callbacks == nullptr) {
247         LOG(ERROR) << "Callbacks cannot be null";
248         return nullptr;
249     }
250 
251     return std::shared_ptr<MediaTranscoder>(
252             new MediaTranscoder(callbacks, heartBeatIntervalUs, pid, uid));
253 }
254 
configureSource(int fd)255 media_status_t MediaTranscoder::configureSource(int fd) {
256     if (fd < 0) {
257         LOG(ERROR) << "Invalid source fd: " << fd;
258         return AMEDIA_ERROR_INVALID_PARAMETER;
259     }
260 
261     const size_t fileSize = lseek(fd, 0, SEEK_END);
262     lseek(fd, 0, SEEK_SET);
263 
264     mSampleReader = MediaSampleReaderNDK::createFromFd(fd, 0 /* offset */, fileSize);
265     if (mSampleReader == nullptr) {
266         LOG(ERROR) << "Unable to parse source fd: " << fd;
267         return AMEDIA_ERROR_UNSUPPORTED;
268     }
269 
270     const size_t trackCount = mSampleReader->getTrackCount();
271     for (size_t trackIndex = 0; trackIndex < trackCount; ++trackIndex) {
272         AMediaFormat* trackFormat = mSampleReader->getTrackFormat(static_cast<int>(trackIndex));
273         if (trackFormat == nullptr) {
274             LOG(ERROR) << "Track #" << trackIndex << " has no format";
275             return AMEDIA_ERROR_MALFORMED;
276         }
277 
278         mSourceTrackFormats.emplace_back(trackFormat, &AMediaFormat_delete);
279     }
280 
281     return AMEDIA_OK;
282 }
283 
getTrackFormats() const284 std::vector<std::shared_ptr<AMediaFormat>> MediaTranscoder::getTrackFormats() const {
285     // Return a deep copy of the formats to avoid the caller modifying our internal formats.
286     std::vector<std::shared_ptr<AMediaFormat>> trackFormats;
287     for (const std::shared_ptr<AMediaFormat>& sourceFormat : mSourceTrackFormats) {
288         AMediaFormat* copy = AMediaFormat_new();
289         AMediaFormat_copy(copy, sourceFormat.get());
290         trackFormats.emplace_back(copy, &AMediaFormat_delete);
291     }
292     return trackFormats;
293 }
294 
configureTrackFormat(size_t trackIndex,AMediaFormat * destinationOptions)295 media_status_t MediaTranscoder::configureTrackFormat(size_t trackIndex,
296                                                      AMediaFormat* destinationOptions) {
297     if (mSampleReader == nullptr) {
298         LOG(ERROR) << "Source must be configured before tracks";
299         return AMEDIA_ERROR_INVALID_OPERATION;
300     } else if (trackIndex >= mSourceTrackFormats.size()) {
301         LOG(ERROR) << "Track index " << trackIndex
302                    << " is out of bounds. Track count: " << mSourceTrackFormats.size();
303         return AMEDIA_ERROR_INVALID_PARAMETER;
304     }
305 
306     std::shared_ptr<MediaTrackTranscoder> transcoder;
307     std::shared_ptr<AMediaFormat> trackFormat;
308 
309     if (destinationOptions == nullptr) {
310         transcoder = std::make_shared<PassthroughTrackTranscoder>(shared_from_this());
311     } else {
312         AMediaFormat* srcTrackFormat = mSourceTrackFormats[trackIndex].get();
313 
314         const char* srcMime = nullptr;
315         if (!AMediaFormat_getString(srcTrackFormat, AMEDIAFORMAT_KEY_MIME, &srcMime)) {
316             LOG(ERROR) << "Source track #" << trackIndex << " has no mime type";
317             return AMEDIA_ERROR_MALFORMED;
318         }
319 
320         if (strncmp(srcMime, "video/", 6) != 0) {
321             LOG(ERROR) << "Only video tracks are supported for transcoding. Unable to configure "
322                           "track #"
323                        << trackIndex << " with mime " << srcMime;
324             return AMEDIA_ERROR_UNSUPPORTED;
325         }
326 
327         const char* dstMime = nullptr;
328         if (AMediaFormat_getString(destinationOptions, AMEDIAFORMAT_KEY_MIME, &dstMime)) {
329             if (strncmp(dstMime, "video/", 6) != 0) {
330                 LOG(ERROR) << "Unable to convert media types for track #" << trackIndex << ", from "
331                            << srcMime << " to " << dstMime;
332                 return AMEDIA_ERROR_UNSUPPORTED;
333             }
334         }
335 
336         transcoder = VideoTrackTranscoder::create(shared_from_this(), mPid, mUid);
337 
338         trackFormat = createVideoTrackFormat(srcTrackFormat, destinationOptions);
339         if (trackFormat == nullptr) {
340             LOG(ERROR) << "Unable to create video track format";
341             return AMEDIA_ERROR_UNKNOWN;
342         }
343     }
344 
345     media_status_t status = mSampleReader->selectTrack(trackIndex);
346     if (status != AMEDIA_OK) {
347         LOG(ERROR) << "Unable to select track " << trackIndex;
348         return status;
349     }
350 
351     status = transcoder->configure(mSampleReader, trackIndex, trackFormat);
352     if (status != AMEDIA_OK) {
353         LOG(ERROR) << "Configure track transcoder for track #" << trackIndex << " returned error "
354                    << status;
355         mSampleReader->unselectTrack(trackIndex);
356         return status;
357     }
358 
359     std::scoped_lock lock{mThreadStateMutex};
360     mThreadStates[static_cast<const void*>(transcoder.get())] = PENDING;
361 
362     mTrackTranscoders.emplace_back(std::move(transcoder));
363     return AMEDIA_OK;
364 }
365 
configureDestination(int fd)366 media_status_t MediaTranscoder::configureDestination(int fd) {
367     if (fd < 0) {
368         LOG(ERROR) << "Invalid destination fd: " << fd;
369         return AMEDIA_ERROR_INVALID_PARAMETER;
370     }
371 
372     if (mSampleWriter != nullptr) {
373         LOG(ERROR) << "Destination is already configured.";
374         return AMEDIA_ERROR_INVALID_OPERATION;
375     }
376 
377     mSampleWriter = MediaSampleWriter::Create();
378     const bool initOk = mSampleWriter->init(fd, shared_from_this(), mHeartBeatIntervalUs);
379 
380     if (!initOk) {
381         LOG(ERROR) << "Unable to initialize sample writer with destination fd: " << fd;
382         mSampleWriter.reset();
383         return AMEDIA_ERROR_UNKNOWN;
384     }
385 
386     std::scoped_lock lock{mThreadStateMutex};
387     mThreadStates[static_cast<const void*>(mSampleWriter.get())] = PENDING;
388     return AMEDIA_OK;
389 }
390 
start()391 media_status_t MediaTranscoder::start() {
392     if (mTrackTranscoders.size() < 1) {
393         LOG(ERROR) << "Unable to start, no tracks are configured.";
394         return AMEDIA_ERROR_INVALID_OPERATION;
395     } else if (mSampleWriter == nullptr) {
396         LOG(ERROR) << "Unable to start, destination is not configured";
397         return AMEDIA_ERROR_INVALID_OPERATION;
398     }
399 
400     // Start transcoders
401     bool started = true;
402     {
403         std::scoped_lock lock{mThreadStateMutex};
404         for (auto& transcoder : mTrackTranscoders) {
405             if (!(started = transcoder->start())) {
406                 break;
407             }
408             mThreadStates[static_cast<const void*>(transcoder.get())] = RUNNING;
409         }
410     }
411     if (!started) {
412         LOG(ERROR) << "Unable to start track transcoder.";
413         cancel();
414         return AMEDIA_ERROR_UNKNOWN;
415     }
416     return AMEDIA_OK;
417 }
418 
requestStop(bool stopOnSync)419 media_status_t MediaTranscoder::requestStop(bool stopOnSync) {
420     std::scoped_lock lock{mThreadStateMutex};
421     if (mCancelled) {
422         LOG(DEBUG) << "MediaTranscoder already cancelled";
423         return AMEDIA_ERROR_UNSUPPORTED;
424     }
425 
426     if (!stopOnSync) {
427         mSampleWriterStopped = true;
428         mSampleWriter->stop();
429     }
430 
431     mSampleReader->setEnforceSequentialAccess(false);
432     for (auto& transcoder : mTrackTranscoders) {
433         transcoder->stop(stopOnSync);
434     }
435 
436     mCancelled = true;
437     return AMEDIA_OK;
438 }
439 
waitForThreads()440 void MediaTranscoder::waitForThreads() NO_THREAD_SAFETY_ANALYSIS {
441     std::unique_lock lock{mThreadStateMutex};
442     while (!mThreadsDone) {
443         mThreadsDoneSignal.wait(lock);
444     }
445 }
446 
pause(std::shared_ptr<ndk::ScopedAParcel> * pausedState)447 media_status_t MediaTranscoder::pause(std::shared_ptr<ndk::ScopedAParcel>* pausedState) {
448     media_status_t status = requestStop(true /* stopOnSync */);
449     if (status != AMEDIA_OK) {
450         return status;
451     }
452 
453     waitForThreads();
454 
455     // TODO: write internal states to parcel.
456     *pausedState = std::shared_ptr<::ndk::ScopedAParcel>(new ::ndk::ScopedAParcel());
457     return AMEDIA_OK;
458 }
459 
cancel()460 media_status_t MediaTranscoder::cancel() {
461     media_status_t status = requestStop(false /* stopOnSync */);
462     if (status != AMEDIA_OK) {
463         return status;
464     }
465 
466     waitForThreads();
467 
468     // TODO: Release transcoders?
469     return AMEDIA_OK;
470 }
471 
resume()472 media_status_t MediaTranscoder::resume() {
473     // TODO: restore internal states from parcel.
474     return start();
475 }
476 
477 }  // namespace android
478