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