You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1478 lines
42 KiB
1478 lines
42 KiB
/*
|
|
* Copyright 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 "WifiDisplaySink"
|
|
#include <utils/Log.h>
|
|
|
|
#include "include/WifiDisplaySink.h"
|
|
#include "include/RTPSink.h"
|
|
#include "include/ParsedMessage.h"
|
|
#include <binder/IServiceManager.h>
|
|
#include <media/IMediaPlayerService.h>
|
|
|
|
#ifdef WFD_HDCP_SUPPORT
|
|
#include <media/IHDCP.h>
|
|
#endif
|
|
|
|
#include <media/stagefright/foundation/ABuffer.h>
|
|
#include <media/stagefright/foundation/ADebug.h>
|
|
#include <media/stagefright/foundation/AMessage.h>
|
|
#include <media/stagefright/MediaErrors.h>
|
|
|
|
#include <stdio.h>
|
|
#include <stdlib.h>
|
|
#include <errno.h>
|
|
#include <string.h>
|
|
#include <sys/types.h>
|
|
#include <sys/socket.h>
|
|
#include <sys/un.h>
|
|
#include <sys/poll.h>
|
|
#include <sys/stat.h>
|
|
#include <gui/SurfaceComposerClient.h> // add by lance 2013.07.31
|
|
#include <gui/ISurfaceComposer.h> // add by lance 2013.07.31
|
|
#include <netdb.h>
|
|
#include <arpa/inet.h>
|
|
#include <cutils/properties.h>
|
|
#include <inttypes.h>
|
|
#if (PLATFORM_VERSION == 11)
|
|
#include <ui/DisplayConfig.h>
|
|
#elif (PLATFORM_VERSION == 12)
|
|
#include <ui/DisplayMode.h>
|
|
#else
|
|
#include <ui/DisplayInfo.h>
|
|
#endif
|
|
|
|
#define SOCK_PATH "/data/data/wfd_socket"
|
|
|
|
#ifdef __cplusplus
|
|
extern "C" int hdmiedid_init(uint8_t *buf, uint32_t *buflen);
|
|
#endif
|
|
|
|
#define UNUSED(x) (void)(x)
|
|
|
|
namespace android {
|
|
|
|
WifiDisplaySink::WifiDisplaySink(
|
|
const sp<ANetworkSession> &netSession)
|
|
: mUsingHDCP(false),
|
|
mHDCPInitializationComplete(false),
|
|
mState(UNDEFINED),
|
|
mNetSession(netSession),
|
|
connectionCount(0),
|
|
mSessionID(0),
|
|
mNextCSeq(1) {
|
|
mUrl = NULL;
|
|
end_flag = 0;
|
|
pthread_create(&mThread, NULL, rec_data, this);
|
|
}
|
|
|
|
WifiDisplaySink::~WifiDisplaySink() {
|
|
void* retval1;
|
|
end_flag = 1;
|
|
connectionCount = 0;
|
|
ALOGD("~WifiDisplaySink::~~WifiDisplaySink start mSocket_Rec_Client mState %d",mState);
|
|
pthread_join(mThread, &retval1);
|
|
if(mUrl != NULL)
|
|
{
|
|
delete mUrl;
|
|
mUrl = NULL;
|
|
}
|
|
ALOGD("~WifiDisplaySink::~~WifiDisplaySink end");
|
|
if(mRTPSink!=NULL)
|
|
{
|
|
if (mRTPSink->get_mRTCPSessionID() != 0) {
|
|
ALOGD("RTPSink::~RTPSink mRTCPSessionID %d",mRTPSink->get_mRTCPSessionID());
|
|
mRTPSink->get_mNetSession()->destroySession(mRTPSink->get_mRTCPSessionID());
|
|
}
|
|
if (mRTPSink->get_mRTPSessionID() != 0) {
|
|
ALOGD("RTPSink::~RTPSink mRTPSessionID %d",mRTPSink->get_mRTPSessionID());
|
|
mRTPSink->get_mNetSession()->destroySession(mRTPSink->get_mRTPSessionID());
|
|
}
|
|
mRTPSink->clear_mNetSession(); // for resolve the sp problem modify by lance 2013.06.01
|
|
}
|
|
else
|
|
ALOGD("WifiDisplaySink::~WifiDisplaySink quit unnormally ,maybe something wrong with the connection");
|
|
}
|
|
|
|
void WifiDisplaySink::start(const char *sourceHost, int32_t sourcePort) {
|
|
sp<AMessage> msg = new AMessage(kWhatStart, this);
|
|
ALOGI("start---------(shost,sport)");
|
|
msg->setString("sourceHost", sourceHost);
|
|
msg->setInt32("sourcePort", sourcePort);
|
|
msg->post();
|
|
}
|
|
|
|
void WifiDisplaySink::start(const char *uri) {
|
|
sp<AMessage> msg = new AMessage(kWhatStart, this);
|
|
ALOGI("start---------(uri)");
|
|
msg->setString("setupURI", uri);
|
|
msg->post();
|
|
}
|
|
void* WifiDisplaySink::rec_data(void* me)
|
|
{
|
|
WifiDisplaySink* wifidisplaysink = static_cast<WifiDisplaySink *>(me);
|
|
ALOGD("wifidisplaysink event thread start");
|
|
wifidisplaysink->ThreadWrapper(NULL);
|
|
return NULL;
|
|
}
|
|
void *WifiDisplaySink::ThreadWrapper(void *)
|
|
{
|
|
int s, client_sock, len;
|
|
socklen_t t;
|
|
struct sockaddr_un local, remote;
|
|
struct pollfd fds[2];
|
|
if ((s = socket(AF_UNIX, SOCK_STREAM, 0)) == -1) {
|
|
ALOGE("open socket error");
|
|
goto wfd_sink_thread_end;
|
|
}
|
|
fds[0].fd= s;
|
|
fds[0].events = POLLIN;
|
|
fds[0].revents = 0;
|
|
|
|
fds[1].fd= -1;
|
|
fds[1].events = POLLIN;
|
|
fds[1].revents = 0;
|
|
|
|
local.sun_family = AF_UNIX;
|
|
strcpy(local.sun_path, SOCK_PATH);
|
|
unlink(local.sun_path);
|
|
len = strlen(local.sun_path) + sizeof(local.sun_family);
|
|
if (bind(s, (struct sockaddr *)&local, len) == -1) {
|
|
ALOGE("bin socket error");
|
|
shutdown(fds[0].fd, SHUT_RDWR);
|
|
close(fds[0].fd);
|
|
fds[0].fd = -1;
|
|
goto wfd_sink_thread_end;
|
|
}
|
|
chmod(SOCK_PATH,0x777);
|
|
|
|
ALOGD(" before listen s %d" ,s);
|
|
if (listen(s, 5) == -1) {
|
|
ALOGE("listen socket error");
|
|
shutdown(fds[0].fd, SHUT_RDWR);
|
|
close(fds[0].fd);
|
|
fds[0].fd = -1;
|
|
goto wfd_sink_thread_end;
|
|
}
|
|
while(!end_flag)
|
|
{
|
|
int ret;
|
|
if ((ret = poll(fds, 2, 50)) > 0)//poll(fds, 2, -1) > 0) -1 ==inftim
|
|
{
|
|
|
|
if (fds[0].revents & POLLIN)
|
|
{
|
|
if ((client_sock = accept(s, (struct sockaddr *)&remote, &t)) == -1) {
|
|
ALOGE("accept socket error");
|
|
shutdown(fds[0].fd, SHUT_RDWR);
|
|
close(fds[0].fd);
|
|
fds[0].fd = -1;
|
|
goto wfd_sink_thread_end;
|
|
}
|
|
if (client_sock >= 0) {
|
|
ALOGE("receiver: accept client %d success\n", client_sock);
|
|
fds[1].fd = client_sock ;
|
|
} else {
|
|
ALOGE("receiver: accept client %d failed\n", client_sock);
|
|
break;
|
|
}
|
|
}
|
|
if (fds[0].revents & POLLHUP)
|
|
{
|
|
ALOGD("sender shutdown fds[0] %d fds[1] %d",fds[0].revents,fds[1].revents);
|
|
shutdown(fds[0].fd, SHUT_RDWR);
|
|
close(fds[0].fd);
|
|
fds[0].fd = -1;
|
|
break;
|
|
|
|
}
|
|
if (fds[1].fd > 0)
|
|
{
|
|
|
|
if (fds[1].revents & POLLIN)
|
|
{
|
|
long temp;
|
|
int n = recv(fds[1].fd, &temp, 4, 0);
|
|
if(n > 0 && temp == 0x1234)
|
|
sendIDR(mSessionID,mUrl->c_str());
|
|
ALOGD("sendIDR mSessionID %d %s",mSessionID,mUrl->c_str());
|
|
}
|
|
if (fds[1].revents & POLLHUP)
|
|
{
|
|
|
|
shutdown(fds[1].fd, SHUT_RDWR);
|
|
close(fds[1].fd);
|
|
fds[1].fd = -1;
|
|
break;
|
|
}
|
|
if(fds[1].revents & (POLLERR | POLLNVAL))
|
|
{
|
|
ALOGD("fds 2 error %d errno %d",fds[1].revents,errno);
|
|
shutdown(fds[1].fd, SHUT_RDWR);
|
|
close(fds[1].fd);
|
|
fds[1].fd = -1;
|
|
break;
|
|
}
|
|
}
|
|
|
|
}
|
|
}
|
|
wfd_sink_thread_end:
|
|
ALOGD("end of threadloop end_flag %d errno %d",end_flag,errno);
|
|
if(fds[1].fd!=-1)
|
|
{
|
|
shutdown(fds[1].fd, SHUT_RDWR);
|
|
close(fds[1].fd);
|
|
fds[1].fd = -1;
|
|
}
|
|
if(fds[0].fd!=-1)
|
|
{
|
|
shutdown(fds[0].fd, SHUT_RDWR);
|
|
close(fds[0].fd);
|
|
fds[0].fd = -1;
|
|
}
|
|
return NULL;
|
|
}
|
|
|
|
|
|
// static
|
|
bool WifiDisplaySink::ParseURL(
|
|
const char *url, AString *host, int32_t *port, AString *path,
|
|
AString *user, AString *pass) {
|
|
host->clear();
|
|
*port = 0;
|
|
path->clear();
|
|
user->clear();
|
|
pass->clear();
|
|
|
|
if (strncasecmp("rtsp://", url, 7)) {
|
|
return false;
|
|
}
|
|
|
|
const char *slashPos = strchr(&url[7], '/');
|
|
|
|
if (slashPos == NULL) {
|
|
host->setTo(&url[7]);
|
|
path->setTo("/");
|
|
} else {
|
|
host->setTo(&url[7], slashPos - &url[7]);
|
|
path->setTo(slashPos);
|
|
}
|
|
|
|
ssize_t atPos = host->find("@");
|
|
|
|
if (atPos >= 0) {
|
|
// Split of user:pass@ from hostname.
|
|
|
|
AString userPass(*host, 0, atPos);
|
|
host->erase(0, atPos + 1);
|
|
|
|
ssize_t colonPos = userPass.find(":");
|
|
|
|
if (colonPos < 0) {
|
|
*user = userPass;
|
|
} else {
|
|
user->setTo(userPass, 0, colonPos);
|
|
pass->setTo(userPass, colonPos + 1, userPass.size() - colonPos - 1);
|
|
}
|
|
}
|
|
|
|
const char *colonPos = strchr(host->c_str(), ':');
|
|
|
|
if (colonPos != NULL) {
|
|
char *end;
|
|
unsigned long x = strtoul(colonPos + 1, &end, 10);
|
|
|
|
if (end == colonPos + 1 || *end != '\0' || x >= 65536) {
|
|
return false;
|
|
}
|
|
|
|
*port = x;
|
|
|
|
size_t colonOffset = colonPos - host->c_str();
|
|
size_t trailing = host->size() - colonOffset;
|
|
host->erase(colonOffset, trailing);
|
|
} else {
|
|
*port = 554;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
void WifiDisplaySink::onMessageReceived(const sp<AMessage> &msg) {
|
|
switch (msg->what()) {
|
|
case kWhatStart:
|
|
{
|
|
ALOGI("start sink");
|
|
if (msg->findString("setupURI", &mSetupURI)) {
|
|
AString path, user, pass;
|
|
CHECK(ParseURL(
|
|
mSetupURI.c_str(),
|
|
&mRTSPHost, &mRTSPPort, &path, &user, &pass)
|
|
&& user.empty() && pass.empty());
|
|
} else {
|
|
CHECK(msg->findString("sourceHost", &mRTSPHost));
|
|
CHECK(msg->findInt32("sourcePort", &mRTSPPort));
|
|
}
|
|
|
|
sp<AMessage> notify = new AMessage(kWhatRTSPNotify, this);
|
|
status_t err = mNetSession->createRTSPClient(
|
|
mRTSPHost.c_str(), mRTSPPort, notify, &mSessionID);
|
|
CHECK_EQ(err, (status_t)OK);
|
|
mState = CONNECTING;
|
|
break;
|
|
}
|
|
|
|
case kWhatRTSPNotify:
|
|
{
|
|
int32_t reason;
|
|
CHECK(msg->findInt32("reason", &reason));
|
|
|
|
switch (reason) {
|
|
case ANetworkSession::kWhatError:
|
|
{
|
|
int32_t sessionID;
|
|
CHECK(msg->findInt32("sessionID", &sessionID));
|
|
|
|
int32_t err;
|
|
CHECK(msg->findInt32("err", &err));
|
|
|
|
AString detail;
|
|
CHECK(msg->findString("detail", &detail));
|
|
|
|
// ---------------
|
|
// add by lance 2014.05.20
|
|
if ((connectionCount < 25) && (mState == CONNECTING) && (err == -111)){
|
|
++connectionCount;
|
|
ALOGD("RTSP server may not yet run, try reconnect");
|
|
usleep(200*1000);
|
|
start(mRTSPHost.c_str(),mRTSPPort);
|
|
break;
|
|
}
|
|
// ---------------
|
|
|
|
ALOGE("An error occurred in session %d (%d, '%s/%s').",
|
|
sessionID,
|
|
err,
|
|
detail.c_str(),
|
|
strerror(-err));
|
|
|
|
if (sessionID == mSessionID) {
|
|
ALOGI("Lost control connection.");
|
|
// The control connection is dead now.
|
|
ALOGD("WifiDisplaySink::onMessageReceivedSessionID %d",mSessionID);
|
|
mNetSession->destroySession(mSessionID);
|
|
mSessionID = 0;
|
|
looper()->stop();
|
|
finishStop();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ANetworkSession::kWhatConnected:
|
|
{
|
|
ALOGI("We're now connected.");
|
|
mState = CONNECTED;
|
|
|
|
if (!mSetupURI.empty()) {
|
|
status_t err =
|
|
sendDescribe(mSessionID, mSetupURI.c_str());
|
|
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
break;
|
|
}
|
|
|
|
case ANetworkSession::kWhatData:
|
|
{
|
|
ALOGD("onReceiveClientData sessionID %d",mSessionID);
|
|
onReceiveClientData(msg);
|
|
break;
|
|
}
|
|
|
|
case ANetworkSession::kWhatBinaryData:
|
|
{
|
|
CHECK(sUseTCPInterleaving);
|
|
|
|
int32_t channel;
|
|
CHECK(msg->findInt32("channel", &channel));
|
|
|
|
sp<ABuffer> data;
|
|
CHECK(msg->findBuffer("data", &data));
|
|
|
|
mRTPSink->injectPacket(channel == 0 /* isRTP */, data);
|
|
break;
|
|
}
|
|
|
|
default:
|
|
TRESPASS();
|
|
}
|
|
break;
|
|
}
|
|
|
|
case kWhatStop:
|
|
{
|
|
ALOGD("kWhatStop");
|
|
mNetSession->destroySession(mSessionID);
|
|
mSessionID = 0;
|
|
looper()->stop();
|
|
finishStop();
|
|
break;
|
|
}
|
|
|
|
default:
|
|
TRESPASS();
|
|
}
|
|
}
|
|
|
|
void WifiDisplaySink::registerResponseHandler(
|
|
int32_t sessionID, int32_t cseq, HandleRTSPResponseFunc func) {
|
|
ResponseID id;
|
|
id.mSessionID = sessionID;
|
|
id.mCSeq = cseq;
|
|
mResponseHandlers.add(id, func);
|
|
}
|
|
|
|
status_t WifiDisplaySink::sendM2(int32_t sessionID) {
|
|
AString request = "OPTIONS * RTSP/1.0\r\n";
|
|
AppendCommonResponse(&request, mNextCSeq);
|
|
|
|
request.append(
|
|
"Require: org.wfa.wfd1.0\r\n"
|
|
"\r\n");
|
|
|
|
ALOGI("*******************************");
|
|
ALOGI("%s\n",request.c_str());
|
|
ALOGI("*******************************");
|
|
status_t err =
|
|
mNetSession->sendRequest(sessionID, request.c_str(), request.size());
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
registerResponseHandler(
|
|
sessionID, mNextCSeq, &WifiDisplaySink::onReceiveM2Response);
|
|
|
|
++mNextCSeq;
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t WifiDisplaySink::onReceiveM2Response(
|
|
int32_t sessionID, const sp<ParsedMessage> &msg) {
|
|
UNUSED(sessionID);
|
|
int32_t statusCode;
|
|
if (!msg->getStatusCode(&statusCode)) {
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (statusCode != 200) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t WifiDisplaySink::onReceiveDescribeResponse(
|
|
int32_t sessionID, const sp<ParsedMessage> &msg) {
|
|
int32_t statusCode;
|
|
if (!msg->getStatusCode(&statusCode)) {
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (statusCode != 200) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
return sendSetup(sessionID, mSetupURI.c_str());
|
|
}
|
|
|
|
status_t WifiDisplaySink::onReceiveSetupResponse(
|
|
int32_t sessionID, const sp<ParsedMessage> &msg) {
|
|
int32_t statusCode;
|
|
ALOGI("onReceiveSetupResponse********************");
|
|
if (!msg->getStatusCode(&statusCode)) {
|
|
ALOGI("status error******************");
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (statusCode != 200) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
if (!msg->findString("session", &mPlaybackSessionID)) {
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (!ParsedMessage::GetInt32Attribute(
|
|
mPlaybackSessionID.c_str(),
|
|
"timeout",
|
|
&mPlaybackSessionTimeoutSecs)) {
|
|
mPlaybackSessionTimeoutSecs = -1;
|
|
}
|
|
|
|
ssize_t colonPos = mPlaybackSessionID.find(";");
|
|
if (colonPos >= 0) {
|
|
// Strip any options from the returned session id.
|
|
mPlaybackSessionID.erase(
|
|
colonPos, mPlaybackSessionID.size() - colonPos);
|
|
}
|
|
|
|
status_t err = 0;//configureTransport(msg);
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
mState = PAUSED;
|
|
|
|
return sendPlay(
|
|
sessionID,
|
|
!mSetupURI.empty()
|
|
? mSetupURI.c_str() : mUrl->c_str());
|
|
}
|
|
|
|
status_t WifiDisplaySink::configureTransport(const sp<ParsedMessage> &msg) {
|
|
if (sUseTCPInterleaving) {
|
|
return OK;
|
|
}
|
|
|
|
AString transport;
|
|
if (!msg->findString("transport", &transport)) {
|
|
ALOGE("Missing 'transport' field in SETUP response.");
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
AString sourceHost;
|
|
if (!ParsedMessage::GetAttribute(
|
|
transport.c_str(), "source", &sourceHost)) {
|
|
sourceHost = mRTSPHost;
|
|
}
|
|
|
|
AString serverPortStr;
|
|
if (!ParsedMessage::GetAttribute(
|
|
transport.c_str(), "server_port", &serverPortStr)) {
|
|
ALOGE("Missing 'server_port' in Transport field.");
|
|
return OK;
|
|
//return ERROR_MALFORMED;
|
|
} else {
|
|
|
|
int rtpPort, rtcpPort;
|
|
ALOGI("serverPortStr = %s\n",serverPortStr.c_str());
|
|
if(serverPortStr.find("-") >= 0)
|
|
{
|
|
if (sscanf(serverPortStr.c_str(), "%d-%d", &rtpPort, &rtcpPort) != 2
|
|
|| rtpPort <= 0 || rtpPort > 65535
|
|
|| rtcpPort <=0 || rtcpPort > 65535
|
|
/*|| rtcpPort != rtpPort + 1*/) {
|
|
ALOGE("%d",rtcpPort);
|
|
ALOGE("Invalid server_port description '%s'.",
|
|
serverPortStr.c_str());
|
|
|
|
return ERROR_MALFORMED;
|
|
}
|
|
}
|
|
else
|
|
{
|
|
if(sscanf(serverPortStr.c_str(), "%d", &rtpPort) == 1)
|
|
{
|
|
rtcpPort = rtpPort+1;
|
|
} else {
|
|
ALOGE("Invalid rtp port description");
|
|
return ERROR_MALFORMED;
|
|
}
|
|
}
|
|
|
|
if (rtpPort & 1) {
|
|
ALOGW("Server picked an odd numbered RTP port.");
|
|
}
|
|
|
|
return mRTPSink->connect(sourceHost.c_str(), rtpPort, rtcpPort);
|
|
}
|
|
}
|
|
|
|
status_t WifiDisplaySink::onReceiveTearDownResponse(
|
|
int32_t sessionID, const sp<ParsedMessage> &msg){
|
|
UNUSED(sessionID);
|
|
int32_t statusCode;
|
|
ALOGI("I have received source teardown response");
|
|
if (!msg->getStatusCode(&statusCode)) {
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (statusCode != 200) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
mState = TEARDOWN;
|
|
|
|
sp<AMessage> msg_stop = new AMessage(kWhatStop, this);
|
|
msg_stop->post();
|
|
return OK;
|
|
|
|
}
|
|
status_t WifiDisplaySink::onReceivePlayResponse(
|
|
int32_t sessionID, const sp<ParsedMessage> &msg) {
|
|
UNUSED(sessionID);
|
|
int32_t statusCode;
|
|
if (!msg->getStatusCode(&statusCode)) {
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (statusCode != 200) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
|
|
mState = PLAYING;
|
|
|
|
return OK;
|
|
}
|
|
|
|
void WifiDisplaySink::onReceiveClientData(const sp<AMessage> &msg) {
|
|
int32_t sessionID;
|
|
CHECK(msg->findInt32("sessionID", &sessionID));
|
|
|
|
sp<RefBase> obj;
|
|
CHECK(msg->findObject("data", &obj));
|
|
|
|
sp<ParsedMessage> data =
|
|
static_cast<ParsedMessage *>(obj.get());
|
|
|
|
//ALOGD("session %d received %s",sessionID, data->debugString().c_str());
|
|
ALOGI("****************************************\n");
|
|
ALOGI("%s\n",data->debugString().c_str());
|
|
ALOGI("****************************************\n");
|
|
|
|
AString method;
|
|
AString uri;
|
|
data->getRequestField(0, &method);
|
|
|
|
int32_t cseq;
|
|
if (!data->findInt32("cseq", &cseq)) {
|
|
sendErrorResponse(sessionID, "400 Bad Request", -1 /* cseq */);
|
|
return;
|
|
}
|
|
|
|
if (method.startsWith("RTSP/")) {
|
|
// This is a response.
|
|
|
|
ResponseID id;
|
|
id.mSessionID = sessionID;
|
|
id.mCSeq = cseq;
|
|
|
|
ssize_t index = mResponseHandlers.indexOfKey(id);
|
|
|
|
if (index < 0) {
|
|
ALOGW("Received unsolicited server response, cseq %d", cseq);
|
|
return;
|
|
}
|
|
|
|
HandleRTSPResponseFunc func = mResponseHandlers.valueAt(index);
|
|
mResponseHandlers.removeItemsAt(index);
|
|
|
|
status_t err = (this->*func)(sessionID, data);
|
|
CHECK_EQ(err, (status_t)OK);
|
|
} else {
|
|
AString version;
|
|
data->getRequestField(2, &version);
|
|
if (!(version == AString("RTSP/1.0"))) {
|
|
sendErrorResponse(sessionID, "505 RTSP Version not supported", cseq);
|
|
return;
|
|
}
|
|
|
|
if (method == "OPTIONS") {
|
|
onOptionsRequest(sessionID, cseq, data);
|
|
} else if (method == "GET_PARAMETER") {
|
|
onGetParameterRequest(sessionID, cseq, data);
|
|
} else if (method == "SET_PARAMETER") {
|
|
onSetParameterRequest(sessionID, cseq, data);
|
|
} else {
|
|
sendErrorResponse(sessionID, "405 Method Not Allowed", cseq);
|
|
ALOGD("onReceiveClientData::----- error 405");
|
|
}
|
|
}
|
|
}
|
|
|
|
void WifiDisplaySink::onOptionsRequest(
|
|
int32_t sessionID,
|
|
int32_t cseq,
|
|
const sp<ParsedMessage> &data) {
|
|
UNUSED(data);
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append("Public: org.wfa.wfd1.0, GET_PARAMETER, SET_PARAMETER\r\n");
|
|
response.append("\r\n");
|
|
|
|
status_t err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
|
|
err = sendM2(sessionID);
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
|
|
uint8_t WifiDisplaySink::vauleParsedByResolution(int32_t displayWidth, int32_t displayHeight, float displayFps)
|
|
{
|
|
uint8_t parsedValue = 0x00;
|
|
switch(displayWidth)
|
|
{
|
|
case 640:
|
|
if(displayHeight == 480)
|
|
{
|
|
parsedValue = 0x00;
|
|
}
|
|
else if(displayHeight == 360)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x32;
|
|
else
|
|
parsedValue = 0x3A;
|
|
}
|
|
break;
|
|
case 720:
|
|
if(displayHeight == 480 && 1) // 1 stand for progressive frame modify by lance 2013.07.31
|
|
parsedValue = 0x08;
|
|
else if(displayHeight == 480 && 0) // 0 stand for interlaced frame modify by lance 2013.07.31
|
|
parsedValue =0x10;
|
|
else if(displayHeight == 576 && 1)
|
|
parsedValue = 0x18;
|
|
else if(displayHeight == 576 && 0)
|
|
parsedValue = 0x20;
|
|
break;
|
|
case 800:
|
|
if(displayHeight == 600)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60))
|
|
parsedValue = 0x01;
|
|
else
|
|
parsedValue = 0x09;
|
|
}
|
|
else if(displayHeight == 480)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x02;
|
|
else
|
|
parsedValue = 0x0A;
|
|
}
|
|
break;
|
|
case 848:
|
|
if(displayHeight == 480)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x52;
|
|
else
|
|
parsedValue = 0x5A;
|
|
}
|
|
break;
|
|
case 854:
|
|
if(displayHeight == 480)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x12;
|
|
else
|
|
parsedValue = 0x1A;
|
|
}
|
|
break;
|
|
case 864:
|
|
if(displayHeight == 480)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x22;
|
|
else
|
|
parsedValue = 0x2A;
|
|
}
|
|
break;
|
|
case 960:
|
|
if(displayHeight == 540)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x42;
|
|
else
|
|
parsedValue = 0x4A;
|
|
}
|
|
break;
|
|
case 1024:
|
|
if(displayHeight == 768)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60))
|
|
parsedValue = 0x11;
|
|
else
|
|
parsedValue = 0x19;
|
|
}
|
|
break;
|
|
case 1152:
|
|
if(displayHeight == 864)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60))
|
|
parsedValue = 0x21;
|
|
else
|
|
parsedValue = 0x29;
|
|
}
|
|
break;
|
|
case 1280:
|
|
if(displayHeight == 720)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x28;
|
|
else
|
|
parsedValue = 0x30;
|
|
}
|
|
else if(displayHeight == 768)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x31;
|
|
else
|
|
parsedValue = 0x39;
|
|
}
|
|
else if(displayHeight == 800)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x41;
|
|
else
|
|
parsedValue = 0x49;
|
|
}
|
|
else if(displayHeight == 1024)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x71;
|
|
else
|
|
parsedValue = 0x79;
|
|
}
|
|
break;
|
|
case 1360:
|
|
if(displayHeight == 768)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x51;
|
|
else
|
|
parsedValue = 0x59;
|
|
}
|
|
break;
|
|
case 1366:
|
|
if(displayHeight == 768)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x61;
|
|
else
|
|
parsedValue = 0x69;
|
|
}
|
|
break;
|
|
case 1400:
|
|
if(displayHeight == 1050)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x81;
|
|
else
|
|
parsedValue = 0x89;
|
|
}
|
|
break;
|
|
case 1440:
|
|
if(displayHeight == 900)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0x91;
|
|
else
|
|
parsedValue = 0x99;
|
|
}
|
|
break;
|
|
case 1600:
|
|
if(displayHeight == 900)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0xA1;
|
|
else
|
|
parsedValue = 0xA9;
|
|
}
|
|
else if(displayHeight == 1200)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0xB1;
|
|
else
|
|
parsedValue = 0xB9;
|
|
}
|
|
break;
|
|
case 1680:
|
|
if(displayHeight == 1024)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0xC1;
|
|
else
|
|
parsedValue = 0xC9;
|
|
}
|
|
else if(displayHeight == 1050)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0xD1;
|
|
else
|
|
parsedValue = 0xD9;
|
|
}
|
|
break;
|
|
case 1920:
|
|
if(displayHeight == 1080 && 1) // 1 stand for the progressive frame modify by lance 2013.07.31 // here should decide the fps 25/50/30 modify by lance 2013.07.31
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60))
|
|
parsedValue = 0x38;
|
|
else
|
|
parsedValue = 0x40;
|
|
}
|
|
else if(displayHeight == 1080 && 0) // 0 stand for the interlaced frame modify by lance 2013.07.31
|
|
parsedValue = 0x48;
|
|
else if(displayHeight == 1200)
|
|
{
|
|
if(abs((int)displayFps - 30) < abs((int)displayFps - 60)) // here should step to decide 24\25\30\50\60 fps modify by lance 2013.07.31
|
|
parsedValue = 0xE1;
|
|
else
|
|
parsedValue = 0xE9;
|
|
}
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
return parsedValue;
|
|
}
|
|
void WifiDisplaySink::getParameterString(char *parameterByte, uint8_t parsedValue)
|
|
{
|
|
sprintf(parameterByte, "%02x", parsedValue);
|
|
}
|
|
void WifiDisplaySink::resolutionToParameterByte(char *parameterByte, int32_t displayWidth, int32_t displayHeight, float displayFps)
|
|
{
|
|
uint8_t parsedValue = vauleParsedByResolution(displayWidth, displayHeight, displayFps);
|
|
getParameterString(parameterByte, parsedValue);
|
|
}
|
|
void WifiDisplaySink::onGetParameterRequest(
|
|
int32_t sessionID,
|
|
int32_t cseq,
|
|
const sp<ParsedMessage> &data) {
|
|
AString msession;
|
|
bool find = data->findString("Session",&msession);
|
|
if(!find)
|
|
{
|
|
#if PLATFORM_SDK_VERSION >= 29
|
|
sp<IBinder> display(SurfaceComposerClient::getInternalDisplayToken());
|
|
#else
|
|
sp<IBinder> display(SurfaceComposerClient::getBuiltInDisplay(
|
|
ISurfaceComposer::eDisplayIdMain));
|
|
#endif
|
|
|
|
#if (PLATFORM_VERSION == 11)
|
|
DisplayConfig config;
|
|
CHECK_EQ(SurfaceComposerClient::getActiveDisplayConfig(display, &config), NO_ERROR);
|
|
const ui::Size& resolution = config.resolution;
|
|
const ssize_t displayWidth = resolution.getWidth();
|
|
const ssize_t displayHeight = resolution.getHeight();
|
|
float fps = config.refreshRate;
|
|
#elif (PLATFORM_VERSION == 12)
|
|
CHECK(display != nullptr);
|
|
ui::DisplayMode mode;
|
|
CHECK_EQ(SurfaceComposerClient::getActiveDisplayMode(display, &mode), NO_ERROR);
|
|
const ui::Size& resolution = mode.resolution;
|
|
const ssize_t displayWidth = resolution.getWidth();
|
|
const ssize_t displayHeight = resolution.getHeight();
|
|
float fps = mode.refreshRate;
|
|
#else
|
|
DisplayInfo info;
|
|
SurfaceComposerClient::getDisplayInfo(display, &info);
|
|
int32_t displayWidth ;
|
|
int32_t displayHeight ;
|
|
float fps = info.fps;
|
|
|
|
if(info.w > info.h)
|
|
{
|
|
displayWidth = (int32_t)info.w;
|
|
displayHeight = (int32_t)info.h;
|
|
}
|
|
else
|
|
{
|
|
displayWidth = (int32_t)info.h;
|
|
displayHeight = (int32_t)info.w;
|
|
}
|
|
#endif
|
|
char parameterByte[3]; // for save the transfer byte modify by lance 2013.07.31
|
|
memset(parameterByte, 0, 3);
|
|
resolutionToParameterByte(parameterByte, displayWidth, displayHeight, fps);
|
|
AString wfdVideoFormatsString(parameterByte);
|
|
wfdVideoFormatsString.append(" 00 02 08 0001DEFF 157C7FFF 00000FFF 00 0000 0000 00 none none,01 08 0001DEFF 157C7FFF 00000FFF 00 0000 0000 00 none none");
|
|
|
|
const char *request_param = data->getContent();
|
|
|
|
AString body = AStringPrintf(
|
|
"wfd_video_formats: %s\r\n"
|
|
"wfd_audio_codecs: LPCM 00000003 00, AAC 00000007 00\r\n", wfdVideoFormatsString.c_str());
|
|
/* answers capabilities that the WFD source are only interested in */
|
|
if (strstr(request_param, "wfd_3d_video_formats"))
|
|
body.append("wfd_3d_video_formats: none\r\n");
|
|
|
|
if (strstr(request_param, "wfd_uibc_capability"))
|
|
body.append("wfd_uibc_capability: none\r\n");
|
|
|
|
if (strstr(request_param, "wfd_content_protection")) {
|
|
#ifdef WFD_HDCP_SUPPORT
|
|
mUsingHDCP = true;
|
|
#endif
|
|
if (mUsingHDCP) {
|
|
body.append(AStringPrintf("wfd_content_protection: HDCP2.1 port=%d\r\n",kHDCPDefaultPort));
|
|
} else {
|
|
body.append("wfd_content_protection: none\r\n");
|
|
}
|
|
}
|
|
|
|
if (strstr(request_param, "wfd_display_edid")) {
|
|
bool noedid = true;
|
|
uint32_t edid_count = 0;
|
|
char edid_hex[256*256] = {0};
|
|
#ifdef LINUX_DRM_SUPPORT
|
|
uint32_t edid_len = 0;
|
|
uint8_t edid_buf[128*256] = {0};
|
|
if (hdmiedid_init(&edid_buf[0], &edid_len) == 0) {
|
|
if (edid_len > 0) {
|
|
noedid = false;
|
|
edid_count = edid_len/128;
|
|
char *cp = edid_hex;
|
|
for (int i = 0; i < edid_len; i++){
|
|
uint8_t data = edid_buf[i];
|
|
cp += sprintf(cp, "%02x", data);
|
|
}
|
|
}
|
|
}
|
|
#endif
|
|
if (noedid) {
|
|
body.append("wfd_display_edid: none\r\n");
|
|
} else {
|
|
body.append(AStringPrintf("wfd_display_edid: %04x %s\r\n",edid_count,edid_hex));
|
|
}
|
|
}
|
|
|
|
if (strstr(request_param, "wfd_coupled_sink"))
|
|
body.append("wfd_coupled_sink: none\r\n");
|
|
if (strstr(request_param, "wfd_connector_type"))
|
|
body.append("wfd_connector_type: 05\r\n"); //05:HDMI
|
|
body.append("wfd_client_rtp_ports: RTP/AVP/UDP;unicast 15550 0 mode=play\r\n");
|
|
|
|
#ifdef WFD_HDCP_SUPPORT
|
|
if (mUsingHDCP && mHDCP==NULL) {
|
|
ALOGD("makeHDCP");
|
|
status_t errHDCP = makeHDCP();
|
|
if (errHDCP != OK) {
|
|
ALOGE("Unable to instantiate HDCP component.");
|
|
mUsingHDCP = false;
|
|
}
|
|
/* important! sleep 100ms to ensure hdcp listen thread is running up */
|
|
usleep(100*1000);
|
|
}
|
|
#endif
|
|
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append("Content-Type: text/parameters\r\n");
|
|
response.append(AStringPrintf("Content-Length: %d\r\n", body.size()));
|
|
response.append("\r\n");
|
|
response.append(body);
|
|
ALOGD("send m3 respose\n");
|
|
ALOGD("%s\n",response.c_str());
|
|
status_t err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
else
|
|
{
|
|
static int64_t last_time;
|
|
int64_t cur_time = systemTime(SYSTEM_TIME_MONOTONIC) / 1000;
|
|
ALOGD("onReceiveClientData:: and send----- m16 time %" PRId64 " %" PRId64 " cseq" " %" PRId32,cur_time,
|
|
cur_time- last_time, cseq);
|
|
last_time = systemTime(SYSTEM_TIME_MONOTONIC) / 1000;
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
|
|
response.append("\r\n");
|
|
status_t err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
ALOGI("%s\n",response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
}
|
|
|
|
status_t WifiDisplaySink::sendDescribe(int32_t sessionID, const char *uri) {
|
|
uri = "rtsp://xwgntvx.is.livestream-api.com/livestreamiphone/wgntv";
|
|
uri = "rtsp://v2.cache6.c.youtube.com/video.3gp?cid=e101d4bf280055f9&fmt=18";
|
|
|
|
AString request = AStringPrintf("DESCRIBE %s RTSP/1.0\r\n", uri);
|
|
AppendCommonResponse(&request, mNextCSeq);
|
|
|
|
request.append("Accept: application/sdp\r\n");
|
|
request.append("\r\n");
|
|
|
|
status_t err = mNetSession->sendRequest(
|
|
sessionID, request.c_str(), request.size());
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
registerResponseHandler(
|
|
sessionID, mNextCSeq, &WifiDisplaySink::onReceiveDescribeResponse);
|
|
|
|
++mNextCSeq;
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t WifiDisplaySink::sendSetup(int32_t sessionID, const char *uri) {
|
|
mRTPSink = new RTPSink(mNetSession);
|
|
looper()->registerHandler(mRTPSink);
|
|
|
|
status_t err = mRTPSink->init(sUseTCPInterleaving);
|
|
|
|
if (err != OK) {
|
|
looper()->unregisterHandler(mRTPSink->id());
|
|
mRTPSink.clear();
|
|
return err;
|
|
}
|
|
|
|
AString request = AStringPrintf("SETUP %s RTSP/1.0\r\n", uri);
|
|
|
|
AppendCommonResponse(&request, mNextCSeq);
|
|
|
|
if (sUseTCPInterleaving) {
|
|
request.append("Transport: RTP/AVP/TCP;interleaved=0-1\r\n");
|
|
} else {
|
|
int32_t rtpPort = mRTPSink->getRTPPort();
|
|
|
|
request.append(
|
|
AStringPrintf(
|
|
"Transport: RTP/AVP/UDP;unicast;client_port=%d-%d\r\n",
|
|
rtpPort, rtpPort + 1));
|
|
}
|
|
|
|
request.append("\r\n");
|
|
|
|
ALOGV("request = '%s'", request.c_str());
|
|
|
|
err = mNetSession->sendRequest(sessionID, request.c_str(), request.size());
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
registerResponseHandler(
|
|
sessionID, mNextCSeq, &WifiDisplaySink::onReceiveSetupResponse);
|
|
|
|
++mNextCSeq;
|
|
|
|
return OK;
|
|
}
|
|
|
|
status_t WifiDisplaySink::sendPlay(int32_t sessionID, const char *uri) {
|
|
AString request = AStringPrintf("PLAY %s RTSP/1.0\r\n", uri);
|
|
|
|
AppendCommonResponse(&request, mNextCSeq);
|
|
|
|
request.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
|
|
request.append("\r\n");
|
|
|
|
status_t err =
|
|
mNetSession->sendRequest(sessionID, request.c_str(), request.size());
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
registerResponseHandler(
|
|
sessionID, mNextCSeq, &WifiDisplaySink::onReceivePlayResponse);
|
|
|
|
++mNextCSeq;
|
|
return OK;
|
|
}
|
|
|
|
status_t WifiDisplaySink::sendTearDown(int32_t sessionID, const char *uri) {
|
|
AString request = AStringPrintf("TEARDOWN %s RTSP/1.0\r\n", uri);
|
|
|
|
AppendCommonResponse(&request, mNextCSeq);
|
|
|
|
request.append(AStringPrintf("Session: %s\r\n", mPlaybackSessionID.c_str()));
|
|
request.append("\r\n");
|
|
|
|
status_t err =
|
|
mNetSession->sendRequest(sessionID, request.c_str(), request.size());
|
|
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
|
|
registerResponseHandler(
|
|
sessionID, mNextCSeq, &WifiDisplaySink::onReceiveTearDownResponse);
|
|
|
|
++mNextCSeq;
|
|
|
|
return OK;
|
|
}
|
|
|
|
void WifiDisplaySink::sendIDR(int32_t sessionID, const char *uri)
|
|
{
|
|
AString request = AStringPrintf("SET_PARAMETER %s RTSP/1.0\r\n", uri);
|
|
|
|
AppendCommonResponse(&request, mNextCSeq);
|
|
request.append("Content-Type: text/parameters\r\n");
|
|
request.append("Content-Length: 17\r\n");
|
|
request.append("\r\n");
|
|
request.append("wfd_idr_request\r\n");
|
|
|
|
status_t err =
|
|
mNetSession->sendRequest(sessionID, request.c_str(), request.size());
|
|
|
|
ALOGI("%s\n",request.c_str());
|
|
|
|
registerResponseHandler(
|
|
sessionID, mNextCSeq, &WifiDisplaySink::onReceiveIdrResponse);
|
|
|
|
if (err != OK) {
|
|
return;
|
|
}
|
|
|
|
++mNextCSeq;
|
|
|
|
}
|
|
|
|
|
|
status_t WifiDisplaySink::onReceiveIdrResponse(
|
|
int32_t sessionID, const sp<ParsedMessage> &msg) {
|
|
UNUSED(sessionID);
|
|
int32_t statusCode;
|
|
ALOGD("WifiDisplaySink::onReceiveIdrResponse");
|
|
if (!msg->getStatusCode(&statusCode)) {
|
|
return ERROR_MALFORMED;
|
|
}
|
|
|
|
if (statusCode != 200) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
return OK;
|
|
}
|
|
|
|
void WifiDisplaySink::onSetParameterRequest(
|
|
int32_t sessionID,
|
|
int32_t cseq,
|
|
const sp<ParsedMessage> &data) {
|
|
if (mUsingHDCP && !mHDCPInitializationComplete) {
|
|
ALOGI("HDCP initialization uncompletes.");
|
|
}
|
|
const char *content = data->getContent();
|
|
if (strstr(content, "wfd_trigger_method: SETUP\r\n") != NULL) {
|
|
status_t err =
|
|
sendSetup(
|
|
sessionID,
|
|
mUrl->c_str());//rtsp://x.x.x.x:x/wfd1.0/streamid=0
|
|
|
|
CHECK_EQ(err, (status_t)OK);
|
|
|
|
#if 1
|
|
ALOGD("onSetParmeterRequest content %s",content);
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append("\r\n");
|
|
|
|
err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
#endif
|
|
}
|
|
else if (strstr(content, "wfd_trigger_method: TEARDOWN\r\n") != NULL) {
|
|
ALOGI("receive source trigger teardown request, do nothing");
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append("\r\n");
|
|
status_t err1 = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err1, (status_t)OK);
|
|
|
|
status_t err =
|
|
sendTearDown(
|
|
sessionID,
|
|
mUrl->c_str());//rtsp://x.x.x.x:x/wfd1.0/streamid=0
|
|
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
else if (strstr(content, "wfd_presentation_URL:")!= NULL)
|
|
{
|
|
AString temp(strstr(content, "wfd_presentation_URL:"));
|
|
int quo = temp.find("rtsp");
|
|
|
|
if(quo >= 0)
|
|
{
|
|
int space = temp.find(" ",quo+1);
|
|
if(space >= 0)
|
|
mUrl = new AString(temp,quo,space-quo);
|
|
ALOGD("SET_Parameter: parse url = %s\n",mUrl->c_str());
|
|
}
|
|
|
|
if(strstr(content,"AAC") != NULL){
|
|
//AAC codec
|
|
}else if (strstr(content,"LPCM") != NULL){
|
|
//LPCM codec
|
|
}else{
|
|
//LPCM
|
|
}
|
|
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append("\r\n");
|
|
ALOGI("send m4 response\n");
|
|
ALOGI("%s\n",response.c_str());
|
|
status_t err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
else
|
|
{
|
|
#if 1
|
|
ALOGD("onSetParmeterRequest content %s",content);
|
|
AString response = "RTSP/1.0 200 OK\r\n";
|
|
AppendCommonResponse(&response, cseq);
|
|
response.append("\r\n");
|
|
|
|
status_t err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
#endif
|
|
}
|
|
}
|
|
|
|
void WifiDisplaySink::sendErrorResponse(
|
|
int32_t sessionID,
|
|
const char *errorDetail,
|
|
int32_t cseq) {
|
|
AString response;
|
|
response.append("RTSP/1.0 ");
|
|
response.append(errorDetail);
|
|
response.append("\r\n");
|
|
|
|
AppendCommonResponse(&response, cseq);
|
|
|
|
response.append("\r\n");
|
|
|
|
status_t err = mNetSession->sendRequest(sessionID, response.c_str());
|
|
CHECK_EQ(err, (status_t)OK);
|
|
}
|
|
|
|
void WifiDisplaySink::finishStop()
|
|
{
|
|
#ifdef WFD_HDCP_SUPPORT
|
|
if (mHDCP != NULL)
|
|
{
|
|
ALOGI("Initiating HDCP shutdown.");
|
|
mHDCP->shutdownAsync();
|
|
mHDCP->setObserver(NULL);
|
|
mHDCPObserver.clear();
|
|
mHDCP.clear();
|
|
return;
|
|
}
|
|
#endif
|
|
}
|
|
|
|
char* WifiDisplaySink::getFirstLocalAddress(){
|
|
char local[255] = {0};
|
|
gethostname(local, sizeof(local));
|
|
hostent* ph = gethostbyname(local);
|
|
if(ph == NULL)
|
|
return NULL;
|
|
char *addrChar = NULL;
|
|
for(int i=0; ph->h_addr_list[i]; ++i){
|
|
in_addr addr;
|
|
memcpy(&addr, ph->h_addr_list[i], sizeof(in_addr));
|
|
addrChar = (inet_ntoa(addr));
|
|
AString addrStr(addrChar);
|
|
if(addrStr=="127.0.0.1"){
|
|
addrChar = NULL;
|
|
continue;
|
|
}
|
|
}
|
|
if(addrChar==NULL){
|
|
char* anyAddr = (char*)"0.0.0.0";
|
|
return anyAddr;
|
|
}
|
|
return addrChar;
|
|
}
|
|
|
|
#ifdef WFD_HDCP_SUPPORT
|
|
struct WifiDisplaySink::HDCPObserver : public BnHDCPObserver {
|
|
HDCPObserver(const sp<AMessage> ¬ify);
|
|
|
|
virtual void notify(
|
|
int msg, int ext1, int ext2, const Parcel *obj);
|
|
|
|
private:
|
|
sp<AMessage> mNotify;
|
|
|
|
DISALLOW_EVIL_CONSTRUCTORS(HDCPObserver);
|
|
};
|
|
|
|
WifiDisplaySink::HDCPObserver::HDCPObserver(
|
|
const sp<AMessage> ¬ify)
|
|
: mNotify(notify) {
|
|
}
|
|
|
|
void WifiDisplaySink::HDCPObserver::notify(
|
|
int msg, int ext1, int ext2, const Parcel *obj) {
|
|
UNUSED(obj);
|
|
sp<AMessage> notify = mNotify->dup();
|
|
notify->setInt32("msg", msg);
|
|
notify->setInt32("ext1", ext1);
|
|
notify->setInt32("ext2", ext2);
|
|
notify->post();
|
|
}
|
|
|
|
status_t WifiDisplaySink::makeHDCP() {
|
|
sp<IServiceManager> sm = defaultServiceManager();
|
|
sp<IBinder> binder = sm->getService(String16("media.player"));
|
|
sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
|
|
CHECK(service != NULL);
|
|
mHDCP = service->makeHDCP(false);
|
|
if (mHDCP == NULL) {
|
|
return ERROR_UNSUPPORTED;
|
|
}
|
|
sp<AMessage> notify = new AMessage(kWhatHDCPNotify, this);
|
|
mHDCPObserver = new HDCPObserver(notify);
|
|
status_t err = mHDCP->setObserver(mHDCPObserver);
|
|
if (err != OK) {
|
|
ALOGE("Failed to set HDCP observer.");
|
|
mHDCPObserver.clear();
|
|
mHDCP.clear();
|
|
return err;
|
|
}
|
|
|
|
char* localIP = getFirstLocalAddress();
|
|
err = mHDCP->initAsync(localIP, kHDCPDefaultPort);
|
|
if (err != OK) {
|
|
return err;
|
|
}
|
|
return OK;
|
|
}
|
|
#endif
|
|
|
|
// static
|
|
void WifiDisplaySink::AppendCommonResponse(AString *response, int32_t cseq) {
|
|
time_t now = time(NULL);
|
|
struct tm *now2 = gmtime(&now);
|
|
char buf[128];
|
|
strftime(buf, sizeof(buf), "%a, %d %b %Y %H:%M:%S %z", now2);
|
|
|
|
response->append("Date: ");
|
|
response->append(buf);
|
|
response->append("\r\n");
|
|
|
|
response->append("User-Agent: stagefright/1.1 (Linux;Android 4.1):rockchip\r\n");
|
|
|
|
if (cseq >= 0) {
|
|
response->append(AStringPrintf("CSeq: %d\r\n", cseq));
|
|
}
|
|
}
|
|
|
|
} // namespace android
|