1 /******************************************************************************
2 QtAV: Multimedia framework based on Qt and FFmpeg
3 Copyright (C) 2012-2018 Wang Bin <wbsecg1@gmail.com>
4
5 * This file is part of QtAV (from 2015)
6
7 This library is free software; you can redistribute it and/or
8 modify it under the terms of the GNU Lesser General Public
9 License as published by the Free Software Foundation; either
10 version 2.1 of the License, or (at your option) any later version.
11
12 This library is distributed in the hope that it will be useful,
13 but WITHOUT ANY WARRANTY; without even the implied warranty of
14 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
15 Lesser General Public License for more details.
16
17 You should have received a copy of the GNU Lesser General Public
18 License along with this library; if not, write to the Free Software
19 Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
20 ******************************************************************************/
21
22 #include "QtAV/MediaIO.h"
23 #include "QtAV/private/MediaIO_p.h"
24 #include "QtAV/private/mkid.h"
25 #include "QtAV/private/factory.h"
26 #include <QtCore/QFile>
27 #include <QtGui/QGuiApplication>
28 #include <QtAndroidExtras>
29 #include "utils/Logger.h"
30 #include "jmi/jmi.h"
31
32 // TODO: how to get filename and find subtitles?
33 //http://stackoverflow.com/questions/5657411/android-getting-a-file-uri-from-a-content-uri
34 //http://stackoverflow.com/questions/19834842/android-gallery-on-kitkat-returns-different-uri-for-intent-action-get-content/20559418#20559418
35 //http://stackoverflow.com/questions/22029815/how-to-use-the-qt-jni-class-qandroidjniobject
36
37 namespace QtAV {
38 static const MediaIOId MediaIOId_Android = mkid::id32base36_6<'A','D','r','o','i', 'd'>::value;
39 static const char kName[] = "Android";
40 class AndroidIOPrivate;
41 class AndroidIO : public MediaIO
42 {
43 DPTR_DECLARE_PRIVATE(AndroidIO)
44 public:
45 AndroidIO();
name() const46 QString name() const Q_DECL_OVERRIDE { return QLatin1String(kName);}
protocols() const47 const QStringList& protocols() const Q_DECL_OVERRIDE
48 {
49 static QStringList p = QStringList() << QStringLiteral("content") << QStringLiteral("android.resource"); // "file:" is supported too but we use QFile
50 return p;
51 }
52 virtual bool isSeekable() const Q_DECL_OVERRIDE;
53 virtual qint64 read(char *data, qint64 maxSize) Q_DECL_OVERRIDE;
54 virtual bool seek(qint64 offset, int from) Q_DECL_OVERRIDE;
55 virtual qint64 position() const Q_DECL_OVERRIDE;
56 /*!
57 * \brief size
58 * \return <=0 if not support
59 */
size() const60 virtual qint64 size() const Q_DECL_OVERRIDE { return qt_file.size();}
61 protected:
62 AndroidIO(AndroidIOPrivate &d);
63 void onUrlChanged() Q_DECL_OVERRIDE;
64
65 private:
66 QFile qt_file;
67 // if use Java.io.InputStream, record pos
68 };
69
70 typedef AndroidIO MediaIOAndroid;
FACTORY_REGISTER(MediaIO,Android,kName)71 FACTORY_REGISTER(MediaIO, Android, kName)
72
73 AndroidIO::AndroidIO()
74 : MediaIO()
75 {
76 jmi::javaVM(QAndroidJniEnvironment::javaVM()); // nativeResourceForIntegration("javaVM")
77 }
78
isSeekable() const79 bool AndroidIO::isSeekable() const
80 {
81 return !qt_file.isSequential();
82 }
83
read(char * data,qint64 maxSize)84 qint64 AndroidIO::read(char *data, qint64 maxSize)
85 {
86 return qt_file.read(data, maxSize);
87 }
88
seek(qint64 offset,int from)89 bool AndroidIO::seek(qint64 offset, int from)
90 {
91 if (from == SEEK_END)
92 offset = qt_file.size() - offset;
93 else if (from == SEEK_CUR)
94 offset = qt_file.pos() + offset;
95 return qt_file.seek(offset);
96 }
97
position() const98 qint64 AndroidIO::position() const
99 {
100 return qt_file.pos();
101 }
102
onUrlChanged()103 void AndroidIO::onUrlChanged()
104 {
105 qt_file.close();
106 if (url().isEmpty())
107 return;
108 struct Application final: jmi::ClassTag { static std::string name() {return "android/app/Application";}};
109 jmi::JObject<Application> app_ctx(jmi::android::application());
110
111 struct ContentResolver final: jmi::ClassTag { static std::string name() { return "android/content/ContentResolver";}};
112 struct GetContentResolver final: jmi::MethodTag { static const char* name() {return "getContentResolver";}};
113 jmi::JObject<ContentResolver> cr = app_ctx.call<jmi::JObject<ContentResolver>, GetContentResolver>();
114 if (!cr.error().empty()) {
115 qWarning("getContentResolver error: %s", cr.error().data());
116 return;
117 }
118 struct Uri final: jmi::ClassTag { static std::string name() { return "android/net/Uri";}};
119 struct Parse final: jmi::MethodTag { static const char* name() {return "parse";}};
120 jmi::JObject<Uri> uri = jmi::JObject<Uri>::callStatic<jmi::JObject<Uri>, Parse>(url().toUtf8().constData()); // move?
121 // openInputStream?
122 struct ParcelFileDescriptor final: jmi::ClassTag { static std::string name() { return "android/os/ParcelFileDescriptor";}};
123 // AssetFileDescriptor supported schemes: content, android.resource, file
124 // ParcelFileDescriptor supported schemes: content, file
125 #if 1
126 struct AssetFileDescriptor final: jmi::ClassTag { static std::string name() { return "android/content/res/AssetFileDescriptor";}};
127 struct OpenAssetFileDescriptor final: jmi::MethodTag { static const char* name() {return "openAssetFileDescriptor";}};
128 jmi::JObject<AssetFileDescriptor> afd = cr.call<jmi::JObject<AssetFileDescriptor>, OpenAssetFileDescriptor>(std::move(uri), "r"); // TODO: rw
129 if (!afd.error().empty()) {
130 qWarning("openAssetFileDescriptor error: %s", afd.error().data());
131 return;
132 }
133 struct GetParcelFileDescriptor final: jmi::MethodTag { static const char* name() {return "getParcelFileDescriptor";}};
134 jmi::JObject<ParcelFileDescriptor> pfd = afd.call<jmi::JObject<ParcelFileDescriptor>, GetParcelFileDescriptor>();
135 #else
136 struct OpenFileDescriptor final: jmi::MethodTag { static const char* name() {return "openFileDescriptor";}};
137 jmi::JObject<ParcelFileDescriptor> pfd = cr.call<jmi::JObject<ParcelFileDescriptor>, OpenFileDescriptor>(std::move(uri), "r");
138 #endif
139 if (!pfd.error().empty()) {
140 qWarning("get ParcelFileDescriptor error: %s", pfd.error().data());
141 return;
142 }
143 struct DetachFd final: jmi::MethodTag { static const char* name() {return "detachFd";}};
144 int fd = pfd.call<int,DetachFd>();
145 qt_file.open(fd, QIODevice::ReadOnly);
146 }
147 } //namespace QtAV
148