您的位置:首页 > 移动开发 > Android开发

android多媒体框架之流媒体具体流程篇2----base on jellybean(十二) .

2014-01-09 12:26 561 查看
上篇我们讲了流媒体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”,我们可以回头看看流媒体的协议一张/article/8044644.html,在播放流媒体前,首先要从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分析完了,具体还得大伙慢慢琢磨。下篇我们将要开始跟流媒体服务打交道了!
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: 
相关文章推荐