上篇我们讲了流媒体RTSP部分的setdataSource方法,prepare没有实质的东西,我们直接讲start方法, 这个方法是它的核心方法,比较复杂,我们先来看下整个start方法的时序图吧,让大家有个大概的了解:
跟踪下代码,看看start里面有什么名堂?
NuPlayer.cpp
void NuPlayer::start() {
(new AMessage(kWhatStart, id()))->post();
}
void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatStart:
{
ALOGV("kWhatStart");
mVideoIsAVC = false;
mAudioEOS = false;
mVideoEOS = false;
mDecoderEOS = false;
mSkipRenderingAudioUntilMediaTimeUs = -1;
mSkipRenderingVideoUntilMediaTimeUs = -1;
mVideoLateByUs = 0;
mNumFramesTotal = 0;
mNumFramesDropped = 0;
(1) mSource->start();-------RTSPSource
(2) mRenderer = new Renderer(
mAudioSink,
new AMessage(kWhatRendererNotify, id()));
(3) postScanSources();
break;
}
}
从代码我们看到start分三步走:start(通过socket跟web服务器连接并通过HTTP协议从Web服务器获取所请求视频服务的演示描述等),创建Renderer(new Renderer),转载解码器并解码(posetScanSources).
首先我们来探讨下mSource->start(),mSource就是RTSPSource,
先看下总的流程图吧(画得不怎么好,将就看吧):
void NuPlayer::RTSPSource::start() {
if (mLooper == NULL) {
mLooper = new ALooper;
mLooper->setName("rtsp");
mLooper->start();
mReflector = new AHandlerReflector<RTSPSource>(this);
mLooper->registerHandler(mReflector);-------创建一个‘rtsp’的looper
}
CHECK(mHandler == NULL);
sp<AMessage> notify = new AMessage(kWhatNotify, mReflector->id());-----记住这个消息
mHandler = new MyHandler(mURL.c_str(),notify, mUIDValid, mUID);
mLooper->registerHandler(mHandler);-----MyHandler,‘rtsp’looper连接起来
CHECK_EQ(mState, (int)DISCONNECTED);
mState = CONNECTING;
mHandler->connect();-------调用myhandler的connect方法
}
我们来看这个Myhandler的构造函数:
MyHandler(
const char *url,
const sp<AMessage> ¬ify,
bool uidValid = false, uid_t uid = 0)
: mNotify(notify),
mUIDValid(uidValid),
mUID(uid),
mNetLooper(new ALooper),
mConn(new ARTSPConnection(mUIDValid, mUID)),-----创建ARTSPConnection,主要用来跟服务器连接
mRTPConn(new ARTPConnection),
………………………..
mKeepAliveGeneration(0) {
mNetLooper->setName("rtsp net");
mNetLooper->start(false /* runOnCallingThread */,
false /* canCallJava */,
PRIORITY_HIGHEST);-------自己创建一个looper。
……………
}
在MyHandler中我们创建了ARTSPConnection,这将在我们的connect方法中会用到:
void connect() {
looper()->registerHandler(mConn);
(1 ? mNetLooper : looper())->registerHandler(mRTPConn);
sp<AMessage> notify = new AMessage('biny', id());
mConn->observeBinaryData(notify);
sp<AMessage> reply = new AMessage('conn', id());----记住这AMessage,这个将会传给ARTSPConnection,并传回来
mConn->connect(mOriginalSessionURL.c_str(), reply);----mConn == ARTSPConnection
}
void ARTSPConnection::connect(const char *url, const sp<AMessage> &reply) {
sp<AMessage> msg = new AMessage(kWhatConnect, id());
msg->setString("url", url);
msg->setMessage("reply", reply);
msg->post();
}
void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatConnect:
onConnect(msg);
break;
………..
}
void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
++mConnectionID;
…………………
AString url;
CHECK(msg->findString("url", &url));
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));------reply == 'conn'
………………….
mSocket = socket(AF_INET, SOCK_STREAM, 0); ------ 建立一个socket
if (mUIDValid) {
HTTPBase::RegisterSocketUserTag(mSocket, mUID,
(uint32_t)*(uint32_t*) "RTSP");
}
MakeSocketBlocking(mSocket, false);------设置socket为非阻塞
struct sockaddr_in remote;
memset(remote.sin_zero, 0, sizeof(remote.sin_zero));
remote.sin_family = AF_INET;
remote.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
remote.sin_port = htons(port);
int err = ::connect(
mSocket, (const struct sockaddr *)&remote, sizeof(remote));----连接
reply->setInt32("server-ip", ntohl(remote.sin_addr.s_addr));
if (err < 0) {
if (errno == EINPROGRESS) {-----当非阻塞时,connect立刻返回-1,同时errno设置为EINPROGRESS。然后再检测socket是否可写,如果可写了,说明
socket已经建立的连接
sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id());
msg->setMessage("reply", reply);
msg->setInt32("connection-id", mConnectionID);
msg->post();
return;
}
……………………….
reply->post();
}
void ARTSPConnection::onCompleteConnection(const sp<AMessage> &msg) {
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
int32_t connectionID;
CHECK(msg->findInt32("connection-id", &connectionID));
if ((connectionID != mConnectionID) || mState != CONNECTING) {
// While we were attempting to connect, the attempt was
// cancelled.
reply->setInt32("result", -ECONNABORTED);
reply->post();
return;
}
struct timeval tv;
tv.tv_sec = 0;
tv.tv_usec = kSelectTimeoutUs;-----超时时间
fd_set ws;
FD_ZERO(&ws);
FD_SET(mSocket, &ws);
int res = select(mSocket + 1, NULL, &ws, NULL, &tv);
…………
int err;
socklen_t optionLen = sizeof(err);
CHECK_EQ(getsockopt(mSocket, SOL_SOCKET, SO_ERROR, &err, &optionLen), 0);
CHECK_EQ(optionLen, (socklen_t)sizeof(err));
if (err != 0) {
ALOGE("err = %d (%s)", err, strerror(err));
reply->setInt32("result", -err);
mState = DISCONNECTED;
if (mUIDValid) {
HTTPBase::UnRegisterSocketUserTag(mSocket);
}
close(mSocket);
mSocket = -1;
} else {
reply->setInt32("result", OK);
mState = CONNECTED;
mNextCSeq = 1;
postReceiveReponseEvent();------处理从服务器回来的reponse
}
reply->post();-----post给myhandler处理
}
又回到MyHandler.h,真够绕的啊!
virtual void onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case 'conn':
{
int32_t result;
CHECK(msg->findInt32("result", &result));
ALOGI("connection request completed with result %d (%s)",
result, strerror(-result));
if (result == OK) {
AString request;
request = "DESCRIBE ";----DESCRIBE获得媒体文件的类型的请求类型
request.append(mSessionURL);
request.append(" RTSP/1.0\r\n");
request.append("Accept: application/sdp\r\n");
request.append("\r\n"); -----建立连接后,发送获得媒体文件的类型的request
sp<AMessage> reply = new AMessage('desc', id());
mConn->sendRequest(request.c_str(), reply);
} else {
(new AMessage('disc', id()))->post();
}
break;
}
看到”DESRIBE”,我们可以回头看看流媒体的协议一张http://blog.csdn.net/tjy1985/article/details/7996121,在播放流媒体前,首先要从web服务器获取媒体文件的类型,要获取这些信息,就得往服务器发生“DESCRIBE”的请求,我们又得回到ARTSPConnection了:
void ARTSPConnection::sendRequest(
const char *request, const sp<AMessage> &reply) {
sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
msg->setString("request", request);
msg->setMessage("reply", reply);
msg->post();
}
void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatSendRequest:
onSendRequest(msg);
break;
}
void ARTSPConnection::onSendRequest(const sp<AMessage> &msg) {
sp<AMessage> reply;
CHECK(msg->findMessage("reply", &reply));
if (mState != CONNECTED) {
reply->setInt32("result", -ENOTCONN);
reply->post();
return;
}
…………………..
size_t numBytesSent = 0;
while (numBytesSent < request.size()) {
ssize_t n =
send(mSocket, request.c_str() + numBytesSent,
request.size() - numBytesSent, 0);-------通过send把request通过socket发送给服务器端
if (n < 0 && errno == EINTR) {
continue;
}
if (n <= 0) {
performDisconnect();
if (n == 0) {
// Server closed the connection.
ALOGE("Server unexpectedly closed the connection.");
reply->setInt32("result", ERROR_IO);
reply->post();
} else {
ALOGE("Error sending rtsp request. (%s)", strerror(errno));
reply->setInt32("result", -errno);
reply->post();
}
return;
}
numBytesSent += (size_t)n;
}
mPendingRequests.add(cseq, reply);
}
在等待服务器的response后,我们又回到MyHandler.h的onMessageReceived函数:
virtual void onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case 'desc':
{
int32_t result;
CHECK(msg->findInt32("result", &result));
ALOGI("DESCRIBE completed with result %d (%s)",
result, strerror(-result));
if (result == OK) {
sp<RefBase> obj;
CHECK(msg->findObject("response", &obj));
sp<ARTSPResponse> response =
static_cast<ARTSPResponse *>(obj.get());
…………………………….
if (response->mStatusCode != 200) {
result = UNKNOWN_ERROR;
} else {
mSessionDesc = new ASessionDescription; ---媒体流的演示描述,该文件提供的信息定位视频服务地址(包括视频服务器地址和端口号)及视频服务的编码方式等信息
mSessionDesc->setTo(
response->mContent->data(),
response->mContent->size());
…………..
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);--------此处到了我们RTSP中的所有的操作中SETUP步骤
}
}
}
}
if (result != OK) {
sp<AMessage> reply = new AMessage('disc', id());
mConn->disconnect(reply);
}
break;
}
}
bool ASessionDescription::setTo(const void *data, size_t size) {
mIsValid = parse(data, size);---解析该SessionDescription
if (!mIsValid) {
mTracks.clear();
mFormats.clear();
}
return mIsValid;
}
到此我们连接上web服务器,并从web服务器获取sessionDescription分析完了,具体还得大伙慢慢琢磨。下篇我们将要开始跟流媒体服务打交道了!