if (strstr(url,"m3u8")) {
return kOurScore;
}
+
+ if ((len >= 4 && !strcasecmp(".sdp", &url[len - 4])) || strstr(url, ".sdp?")) {
+ return kOurScore;
+ }
}
if (!strncasecmp("rtsp://", url, 7)) {
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());
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);
}
#include "AnotherPacketSource.h"
#include "MyHandler.h"
+#include "SDPLoader.h"
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
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),
}
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() {
break;
}
+ case SDPLoader::kWhatSDPLoaded:
+ {
+ onSDPLoaded(msg);
+ break;
+ }
+
default:
TRESPASS();
}
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));
void NuPlayer::RTSPSource::finishDisconnectIfPossible() {
if (mState != DISCONNECTED) {
- mHandler->disconnect();
+ if (mHandler != NULL) {
+ mHandler->disconnect();
+ } else if (mSDPLoader != NULL) {
+ mSDPLoader->cancel();
+ }
return;
}
struct ALooper;
struct AnotherPacketSource;
struct MyHandler;
+struct SDPLoader;
struct NuPlayer::RTSPSource : public NuPlayer::Source {
RTSPSource(
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();
bool mUIDValid;
uid_t mUID;
uint32_t mFlags;
+ bool mIsSDP;
State mState;
status_t mFinalResult;
uint32_t mDisconnectReplyID;
sp<ALooper> mLooper;
sp<AHandlerReflector<RTSPSource> > mReflector;
sp<MyHandler> mHandler;
+ sp<SDPLoader> mSDPLoader;
Vector<TrackInfo> mTracks;
sp<AnotherPacketSource> mAudioTrack;
sp<AnotherPacketSource> getSource(bool audio);
void onConnected();
+ void onSDPLoaded(const sp<AMessage> &msg);
void onDisconnected(const sp<AMessage> &msg);
void finishDisconnectIfPossible();
--- /dev/null
+/*
+ * 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> ¬ify, 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_
ARTPWriter.cpp \
ARTSPConnection.cpp \
ASessionDescription.cpp \
+ SDPLoader.cpp \
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/media/libstagefright/include \
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();
}
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;
--- /dev/null
+/*
+ * 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> ¬ify, 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