• Home
  • Line#
  • Scopes#
  • Navigate#
  • Raw
  • Download
1 /*
2  * Copyright (C) 2021 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 #include <android-base/macros.h>
18 #include <fcntl.h>
19 #include <getopt.h>
20 #include <media/MediaTranscoder.h>
21 #include <media/NdkCommon.h>
22 
23 using namespace android;
24 
25 #define ERR_MSG(fmt, ...) fprintf(stderr, "Error: " fmt "\n", ##__VA_ARGS__)
26 
27 class TranscoderCallbacks : public MediaTranscoder::CallbackInterface {
28 public:
waitForTranscodingFinished()29     media_status_t waitForTranscodingFinished() {
30         std::unique_lock<std::mutex> lock(mMutex);
31         while (!mFinished) {
32             mCondition.wait(lock);
33         }
34         return mStatus;
35     }
36 
37 private:
onFinished(const MediaTranscoder *)38     virtual void onFinished(const MediaTranscoder* /*transcoder*/) override {
39         notifyTranscoderFinished(AMEDIA_OK);
40     }
41 
onError(const MediaTranscoder *,media_status_t error)42     virtual void onError(const MediaTranscoder* /*transcoder*/, media_status_t error) override {
43         ERR_MSG("Transcoder failed with error %d", error);
44         notifyTranscoderFinished(error);
45     }
46 
onProgressUpdate(const MediaTranscoder *,int32_t)47     virtual void onProgressUpdate(const MediaTranscoder* /*transcoder*/,
48                                   int32_t /*progress*/) override {}
49 
onCodecResourceLost(const MediaTranscoder *,const std::shared_ptr<ndk::ScopedAParcel> &)50     virtual void onCodecResourceLost(
51             const MediaTranscoder* /*transcoder*/,
52             const std::shared_ptr<ndk::ScopedAParcel>& /*pausedState*/) override {
53         ERR_MSG("Transcoder lost codec resource while transcoding");
54         notifyTranscoderFinished(AMEDIACODEC_ERROR_INSUFFICIENT_RESOURCE);
55     }
56 
onHeartBeat(const MediaTranscoder *)57     virtual void onHeartBeat(const MediaTranscoder* /*transcoder*/) override {}
58 
notifyTranscoderFinished(media_status_t status)59     void notifyTranscoderFinished(media_status_t status) {
60         std::unique_lock<std::mutex> lock(mMutex);
61         mFinished = true;
62         mStatus = status;
63         mCondition.notify_all();
64     }
65 
66     std::mutex mMutex;
67     std::condition_variable mCondition;
68     bool mFinished = false;
69     media_status_t mStatus = AMEDIA_OK;
70 };
71 
72 struct TranscodeConfig {
73     std::string srcFile;
74     std::string dstFile;
75 
76     std::string dstCodec{AMEDIA_MIMETYPE_VIDEO_AVC};
77     int32_t bitrate = -1;
78 };
79 
transcode(const struct TranscodeConfig & config)80 static int transcode(const struct TranscodeConfig& config) {
81     auto callbacks = std::make_shared<TranscoderCallbacks>();
82     auto transcoder = MediaTranscoder::create(callbacks, -1 /*heartBeatIntervalUs*/);
83 
84     const int srcFd = open(config.srcFile.c_str(), O_RDONLY);
85     if (srcFd <= 0) {
86         ERR_MSG("Unable to open source file %s", config.srcFile.c_str());
87         return AMEDIA_ERROR_INVALID_PARAMETER;
88     }
89 
90     media_status_t status = transcoder->configureSource(srcFd);
91     close(srcFd);
92     if (status != AMEDIA_OK) {
93         ERR_MSG("configureSource returned error %d", status);
94         return status;
95     }
96 
97     std::vector<std::shared_ptr<AMediaFormat>> trackFormats = transcoder->getTrackFormats();
98     if (trackFormats.size() <= 0) {
99         ERR_MSG("No tracks found in source file");
100         return AMEDIA_ERROR_MALFORMED;
101     }
102 
103     for (int i = 0; i < trackFormats.size(); ++i) {
104         AMediaFormat* dstFormat = nullptr;
105 
106         const char* mime = nullptr;
107         AMediaFormat_getString(trackFormats[i].get(), AMEDIAFORMAT_KEY_MIME, &mime);
108 
109         if (strncmp(mime, "video/", 6) == 0) {
110             dstFormat = AMediaFormat_new();
111             AMediaFormat_setString(dstFormat, AMEDIAFORMAT_KEY_MIME, config.dstCodec.c_str());
112 
113             if (config.bitrate > 0) {
114                 AMediaFormat_setInt32(dstFormat, AMEDIAFORMAT_KEY_BIT_RATE, config.bitrate);
115             }
116         }
117 
118         status = transcoder->configureTrackFormat(i, dstFormat);
119 
120         if (dstFormat != nullptr) {
121             AMediaFormat_delete(dstFormat);
122         }
123 
124         if (status != AMEDIA_OK) {
125             ERR_MSG("configureTrack returned error %d", status);
126             return status;
127         }
128     }
129 
130     // Note: Overwrites existing file.
131     const int dstFd = open(config.dstFile.c_str(), O_WRONLY | O_CREAT, S_IRUSR | S_IWUSR);
132     if (dstFd <= 0) {
133         ERR_MSG("Unable to open destination file %s", config.dstFile.c_str());
134         return AMEDIA_ERROR_INVALID_PARAMETER;
135     }
136 
137     status = transcoder->configureDestination(dstFd);
138     close(dstFd);
139     if (status != AMEDIA_OK) {
140         ERR_MSG("configureDestination returned error %d", status);
141         return status;
142     }
143 
144     status = transcoder->start();
145     if (status != AMEDIA_OK) {
146         ERR_MSG("start returned error %d", status);
147         return status;
148     }
149 
150     return callbacks->waitForTranscodingFinished();
151 }
152 
153 // Options.
154 static const struct option kLongOpts[] = {{"help", no_argument, nullptr, 'h'},
155                                           {"codec", required_argument, nullptr, 'c'},
156                                           {"bitrate", required_argument, nullptr, 'b'},
157                                           {0, 0, 0, 0}};
158 static const char kShortOpts[] = "hc:b:";
159 
printUsageAndExit()160 static void printUsageAndExit() {
161     const char* usage =
162             "  -h / --help    : Print this usage message and exit.\n"
163             "  -c / --codec   : Specify output video codec type using MediaFormat codec mime "
164             "type.\n"
165             "                     Defaults to \"video/avc\".\n"
166             "  -b / --bitrate : Specify output video bitrate in bits per second.\n"
167             "                     Defaults to estimating and preserving the original bitrate.\n"
168             "";
169 
170     printf("Usage: %s [-h] [-c CODEC] <srcfile> <dstfile>\n%s", getprogname(), usage);
171     exit(-1);
172 }
173 
main(int argc,char ** argv)174 int main(int argc, char** argv) {
175     int c;
176     TranscodeConfig config;
177 
178     while ((c = getopt_long(argc, argv, kShortOpts, kLongOpts, nullptr)) >= 0) {
179         switch (c) {
180         case 'c':
181             config.dstCodec.assign(optarg);
182             break;
183 
184         case 'b':
185             config.bitrate = atoi(optarg);
186             if (config.bitrate <= 0) {
187                 ERR_MSG("Bitrate must an integer larger than zero.");
188                 printUsageAndExit();
189             }
190             break;
191 
192         case '?':
193             FALLTHROUGH_INTENDED;
194         case 'h':
195             FALLTHROUGH_INTENDED;
196         default:
197             printUsageAndExit();
198             break;
199         }
200     }
201 
202     if (optind > (argc - 2)) {
203         ERR_MSG("Source and destination file not specified");
204         printUsageAndExit();
205     }
206     config.srcFile.assign(argv[optind++]);
207     config.dstFile.assign(argv[optind]);
208 
209     return transcode(config);
210 }
211