OSDN Git Service

Added HTTP support for SDP files.
authorOscar Rydhé <oscar.rydhe@sonyericsson.com>
Mon, 20 Feb 2012 09:15:48 +0000 (10:15 +0100)
committerAndreas Huber <andih@google.com>
Tue, 5 Feb 2013 18:27:34 +0000 (10:27 -0800)
Added support for playing SDP files from http links. Previously,
SDP files only worked when started from rtsp links
(rtsp://a.b.c/abc.sdp), but they are just as common in http links.

patch provided by "Oscar Rydhé <oscar.rydhe@sonyericsson.com>"

Change-Id: Ic73af3a9a002009dbe8b04c267a4621bf7fe2f46

media/libmediaplayerservice/MediaPlayerFactory.cpp
media/libmediaplayerservice/nuplayer/NuPlayer.cpp
media/libmediaplayerservice/nuplayer/RTSPSource.cpp
media/libmediaplayerservice/nuplayer/RTSPSource.h
media/libstagefright/include/SDPLoader.h [new file with mode: 0644]
media/libstagefright/rtsp/Android.mk
media/libstagefright/rtsp/MyHandler.h
media/libstagefright/rtsp/SDPLoader.cpp [new file with mode: 0644]

index 3f69c11..bb441cc 100644 (file)
@@ -215,6 +215,10 @@ class NuPlayerFactory : public MediaPlayerFactory::IFactory {
             if (strstr(url,"m3u8")) {
                 return kOurScore;
             }
+
+            if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {
+                return kOurScore;
+            }
         }
 
         if (!strncasecmp("rtsp://", url, 7)) {
index 9585aba..0736fbe 100644 (file)
@@ -177,6 +177,7 @@ static bool IsHTTPLiveURL(const char *url) {
 void NuPlayer::setDataSource(
         const char *url, const KeyedVector<String8, String8> *headers) {
     sp<AMessage> msg = new AMessage(kWhatSetDataSource, id());
+    size_t len = strlen(url);
 
     sp<AMessage> notify = new AMessage(kWhatSourceNotify, id());
 
@@ -185,6 +186,11 @@ void NuPlayer::setDataSource(
         source = new HTTPLiveSource(notify, url, headers, mUIDValid, mUID);
     } else if (!strncasecmp(url, "rtsp://", 7)) {
         source = new RTSPSource(notify, url, headers, mUIDValid, mUID);
+    } else if ((!strncasecmp(url, "http://", 7)
+                || !strncasecmp(url, "https://", 8))
+                    && ((len >= 4 && !strcasecmp(".sdp", &url[len - 4]))
+                    || strstr(url, ".sdp?"))) {
+        source = new RTSPSource(notify, url, headers, mUIDValid, mUID, true);
     } else {
         source = new GenericSource(notify, url, headers, mUIDValid, mUID);
     }
index e402115..3035589 100644 (file)
@@ -22,6 +22,7 @@
 
 #include "AnotherPacketSource.h"
 #include "MyHandler.h"
+#include "SDPLoader.h"
 
 #include <media/stagefright/MediaDefs.h>
 #include <media/stagefright/MetaData.h>
@@ -33,12 +34,14 @@ NuPlayer::RTSPSource::RTSPSource(
         const char *url,
         const KeyedVector<String8, String8> *headers,
         bool uidValid,
-        uid_t uid)
+        uid_t uid,
+        bool isSDP)
     : Source(notify),
       mURL(url),
       mUIDValid(uidValid),
       mUID(uid),
       mFlags(0),
+      mIsSDP(isSDP),
       mState(DISCONNECTED),
       mFinalResult(OK),
       mDisconnectReplyID(0),
@@ -73,16 +76,25 @@ void NuPlayer::RTSPSource::start() {
     }
 
     CHECK(mHandler == NULL);
+    CHECK(mSDPLoader == NULL);
 
     sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id());
 
-    mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID);
-    mLooper->registerHandler(mHandler);
-
     CHECK_EQ(mState, (int)DISCONNECTED);
     mState = CONNECTING;
 
-    mHandler->connect();
+    if (mIsSDP) {
+        mSDPLoader = new SDPLoader(notify,
+                (mFlags & kFlagIncognito) ? SDPLoader::kFlagIncognito : 0,
+                mUIDValid, mUID);
+
+        mSDPLoader->load(mURL.c_str(), mExtraHeaders.isEmpty() ? NULL : &mExtraHeaders);
+    } else {
+        mHandler = new MyHandler(mURL.c_str(), notify, mUIDValid, mUID);
+        mLooper->registerHandler(mHandler);
+
+        mHandler->connect();
+    }
 }
 
 void NuPlayer::RTSPSource::stop() {
@@ -408,6 +420,12 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
             break;
         }
 
+        case SDPLoader::kWhatSDPLoaded:
+        {
+            onSDPLoaded(msg);
+            break;
+        }
+
         default:
             TRESPASS();
     }
@@ -461,6 +479,46 @@ void NuPlayer::RTSPSource::onConnected() {
     mState = CONNECTED;
 }
 
+void NuPlayer::RTSPSource::onSDPLoaded(const sp<AMessage> &msg) {
+    status_t err;
+    CHECK(msg->findInt32("result", &err));
+
+    mSDPLoader.clear();
+
+    if (mDisconnectReplyID != 0) {
+        err = UNKNOWN_ERROR;
+    }
+
+    if (err == OK) {
+        sp<ASessionDescription> desc;
+        sp<RefBase> obj;
+        CHECK(msg->findObject("description", &obj));
+        desc = static_cast<ASessionDescription *>(obj.get());
+
+        AString rtspUri;
+        if (!desc->findAttribute(0, "a=control", &rtspUri)) {
+            ALOGE("Unable to find url in SDP");
+            err = UNKNOWN_ERROR;
+        } else {
+            sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id());
+
+            mHandler = new MyHandler(rtspUri.c_str(), notify, mUIDValid, mUID);
+            mLooper->registerHandler(mHandler);
+
+            mHandler->loadSDP(desc);
+        }
+    }
+
+    if (err != OK) {
+        mState = DISCONNECTED;
+        mFinalResult = err;
+
+        if (mDisconnectReplyID != 0) {
+            finishDisconnectIfPossible();
+        }
+    }
+}
+
 void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) {
     status_t err;
     CHECK(msg->findInt32("result", &err));
@@ -479,7 +537,11 @@ void NuPlayer::RTSPSource::onDisconnected(const sp<AMessage> &msg) {
 
 void NuPlayer::RTSPSource::finishDisconnectIfPossible() {
     if (mState != DISCONNECTED) {
-        mHandler->disconnect();
+        if (mHandler != NULL) {
+            mHandler->disconnect();
+        } else if (mSDPLoader != NULL) {
+            mSDPLoader->cancel();
+        }
         return;
     }
 
index 033b3e8..b2a7dae 100644 (file)
@@ -29,6 +29,7 @@ namespace android {
 struct ALooper;
 struct AnotherPacketSource;
 struct MyHandler;
+struct SDPLoader;
 
 struct NuPlayer::RTSPSource : public NuPlayer::Source {
     RTSPSource(
@@ -36,7 +37,8 @@ struct NuPlayer::RTSPSource : public NuPlayer::Source {
             const char *url,
             const KeyedVector<String8, String8> *headers,
             bool uidValid = false,
-            uid_t uid = 0);
+            uid_t uid = 0,
+            bool isSDP = false);
 
     virtual void start();
     virtual void stop();
@@ -90,6 +92,7 @@ private:
     bool mUIDValid;
     uid_t mUID;
     uint32_t mFlags;
+    bool mIsSDP;
     State mState;
     status_t mFinalResult;
     uint32_t mDisconnectReplyID;
@@ -98,6 +101,7 @@ private:
     sp<ALooper> mLooper;
     sp<AHandlerReflector<RTSPSource> > mReflector;
     sp<MyHandler> mHandler;
+    sp<SDPLoader> mSDPLoader;
 
     Vector<TrackInfo> mTracks;
     sp<AnotherPacketSource> mAudioTrack;
@@ -110,6 +114,7 @@ private:
     sp<AnotherPacketSource> getSource(bool audio);
 
     void onConnected();
+    void onSDPLoaded(const sp<AMessage> &msg);
     void onDisconnected(const sp<AMessage> &msg);
     void finishDisconnectIfPossible();
 
diff --git a/media/libstagefright/include/SDPLoader.h b/media/libstagefright/include/SDPLoader.h
new file mode 100644 (file)
index 0000000..ca59dc0
--- /dev/null
@@ -0,0 +1,70 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef SDP_LOADER_H_
+
+#define SDP_LOADER_H_
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/String8.h>
+
+namespace android {
+
+struct HTTPBase;
+
+struct SDPLoader : public AHandler {
+    enum Flags {
+        // Don't log any URLs.
+        kFlagIncognito = 1,
+    };
+    enum {
+        kWhatSDPLoaded = 'sdpl'
+    };
+    SDPLoader(const sp<AMessage> &notify, uint32_t flags = 0, bool uidValid = false, uid_t uid = 0);
+
+    void load(const char* url, const KeyedVector<String8, String8> *headers);
+
+    void cancel();
+
+protected:
+    virtual ~SDPLoader() {}
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatLoad = 'load',
+    };
+
+    void onLoad(const sp<AMessage> &msg);
+
+    sp<AMessage> mNotify;
+    const char* mUrl;
+    uint32_t mFlags;
+    bool mUIDValid;
+    uid_t mUID;
+    sp<ALooper> mNetLooper;
+    bool mCancelled;
+
+    sp<HTTPBase> mHTTPDataSource;
+
+    DISALLOW_EVIL_CONSTRUCTORS(SDPLoader);
+};
+
+}  // namespace android
+
+#endif  // SDP_LOADER_H_
index 49e2daf..9e2724d 100644 (file)
@@ -17,6 +17,7 @@ LOCAL_SRC_FILES:=       \
         ARTPWriter.cpp              \
         ARTSPConnection.cpp         \
         ASessionDescription.cpp     \
+        SDPLoader.cpp               \
 
 LOCAL_C_INCLUDES:= \
        $(TOP)/frameworks/av/media/libstagefright/include \
index 96c7683..b7183b1 100644 (file)
@@ -173,6 +173,18 @@ struct MyHandler : public AHandler {
         mConn->connect(mOriginalSessionURL.c_str(), reply);
     }
 
+    void loadSDP(const sp<ASessionDescription>& desc) {
+        looper()->registerHandler(mConn);
+        (1 ? mNetLooper : looper())->registerHandler(mRTPConn);
+
+        sp<AMessage> notify = new AMessage('biny', id());
+        mConn->observeBinaryData(notify);
+
+        sp<AMessage> reply = new AMessage('sdpl', id());
+        reply->setObject("description", desc);
+        mConn->connect(mOriginalSessionURL.c_str(), reply);
+    }
+
     void disconnect() {
         (new AMessage('abor', id()))->post();
     }
@@ -486,6 +498,47 @@ struct MyHandler : public AHandler {
                 break;
             }
 
+            case 'sdpl':
+            {
+                int32_t result;
+                CHECK(msg->findInt32("result", &result));
+
+                ALOGI("SDP connection request completed with result %d (%s)",
+                     result, strerror(-result));
+
+                if (result == OK) {
+                    sp<RefBase> obj;
+                    CHECK(msg->findObject("description", &obj));
+                    mSessionDesc =
+                        static_cast<ASessionDescription *>(obj.get());
+
+                    if (!mSessionDesc->isValid()) {
+                        ALOGE("Failed to parse session description.");
+                        result = ERROR_MALFORMED;
+                    } else {
+                        mBaseURL = mSessionURL;
+
+                        if (mSessionDesc->countTracks() < 2) {
+                            // There's no actual tracks in this session.
+                            // The first "track" is merely session meta
+                            // data.
+
+                            ALOGW("Session doesn't contain any playable "
+                                 "tracks. Aborting.");
+                            result = ERROR_UNSUPPORTED;
+                        } else {
+                            setupTrack(1);
+                        }
+                    }
+                }
+
+                if (result != OK) {
+                    sp<AMessage> reply = new AMessage('disc', id());
+                    mConn->disconnect(reply);
+                }
+                break;
+            }
+
             case 'setu':
             {
                 size_t index;
diff --git a/media/libstagefright/rtsp/SDPLoader.cpp b/media/libstagefright/rtsp/SDPLoader.cpp
new file mode 100644 (file)
index 0000000..ed3fa7e
--- /dev/null
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2012 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ *      http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "SDPLoader"
+#include <utils/Log.h>
+
+#include "SDPLoader.h"
+
+#include "ASessionDescription.h"
+#include "HTTPBase.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+
+#define DEFAULT_SDP_SIZE 100000
+
+namespace android {
+
+SDPLoader::SDPLoader(const sp<AMessage> &notify, uint32_t flags, bool uidValid, uid_t uid)
+    : mNotify(notify),
+      mFlags(flags),
+      mUIDValid(uidValid),
+      mUID(uid),
+      mNetLooper(new ALooper),
+      mCancelled(false),
+      mHTTPDataSource(
+              HTTPBase::Create(
+                  (mFlags & kFlagIncognito)
+                    ? HTTPBase::kFlagIncognito
+                    : 0)) {
+    if (mUIDValid) {
+        mHTTPDataSource->setUID(mUID);
+    }
+
+    mNetLooper->setName("sdp net");
+    mNetLooper->start(false /* runOnCallingThread */,
+                      false /* canCallJava */,
+                      PRIORITY_HIGHEST);
+}
+
+void SDPLoader::load(const char *url, const KeyedVector<String8, String8> *headers) {
+    mNetLooper->registerHandler(this);
+
+    sp<AMessage> msg = new AMessage(kWhatLoad, id());
+    msg->setString("url", url);
+
+    if (headers != NULL) {
+        msg->setPointer(
+                "headers",
+                new KeyedVector<String8, String8>(*headers));
+    }
+
+    msg->post();
+}
+
+void SDPLoader::cancel() {
+    mCancelled = true;
+    sp<HTTPBase> HTTPDataSource = mHTTPDataSource;
+    HTTPDataSource->disconnect();
+}
+
+void SDPLoader::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatLoad:
+            onLoad(msg);
+            break;
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void SDPLoader::onLoad(const sp<AMessage> &msg) {
+    status_t err = OK;
+    sp<ASessionDescription> desc = NULL;
+    AString url;
+    CHECK(msg->findString("url", &url));
+
+    KeyedVector<String8, String8> *headers = NULL;
+    msg->findPointer("headers", (void **)&headers);
+
+    if (!(mFlags & kFlagIncognito)) {
+        ALOGI("onLoad '%s'", url.c_str());
+    } else {
+        ALOGI("onLoad <URL suppressed>");
+    }
+
+    if (!mCancelled) {
+        err = mHTTPDataSource->connect(url.c_str(), headers);
+
+        if (err != OK) {
+            ALOGE("connect() returned %d", err);
+        }
+    }
+
+    if (headers != NULL) {
+        delete headers;
+        headers = NULL;
+    }
+
+    off64_t sdpSize;
+    if (err == OK && !mCancelled) {
+        err = mHTTPDataSource->getSize(&sdpSize);
+
+        if (err != OK) {
+            //We did not get the size of the sdp file, default to a large value
+            sdpSize = DEFAULT_SDP_SIZE;
+            err = OK;
+        }
+    }
+
+    sp<ABuffer> buffer = new ABuffer(sdpSize);
+
+    if (err == OK && !mCancelled) {
+        ssize_t readSize = mHTTPDataSource->readAt(0, buffer->data(), sdpSize);
+
+        if (readSize < 0) {
+            ALOGE("Failed to read SDP, error code = %ld", readSize);
+            err = UNKNOWN_ERROR;
+        } else {
+            desc = new ASessionDescription;
+
+            if (desc == NULL || !desc->setTo(buffer->data(), (size_t)readSize)) {
+                err = UNKNOWN_ERROR;
+                ALOGE("Failed to parse SDP");
+            }
+        }
+    }
+
+    mHTTPDataSource.clear();
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("what", kWhatSDPLoaded);
+    notify->setInt32("result", err);
+    notify->setObject("description", desc);
+    notify->post();
+}
+
+}  // namespace android