blob: 607d9d20198c13a6648072cb48f1374a8b484629 [file] [log] [blame]
/*
* Copyright 2013, 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_NEBUG 0
#define LOG_TAG "rtptest"
#include <utils/Log.h>
#include "ANetworkSession.h"
#include "rtp/RTPSender.h"
#include "rtp/RTPReceiver.h"
#include <binder/ProcessState.h>
#include <media/stagefright/foundation/ABuffer.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/foundation/ALooper.h>
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/NuMediaExtractor.h>
namespace android {
struct TestHandler : public AHandler {
TestHandler(const sp<ANetworkSession> &netSession);
void listen();
void connect(const char *host, int32_t port);
protected:
virtual ~TestHandler();
virtual void onMessageReceived(const sp<AMessage> &msg);
private:
enum {
kWhatListen,
kWhatConnect,
kWhatReceiverNotify,
kWhatSenderNotify,
kWhatSendMore,
kWhatStop,
};
sp<ANetworkSession> mNetSession;
sp<NuMediaExtractor> mExtractor;
sp<RTPSender> mSender;
sp<RTPReceiver> mReceiver;
size_t mMaxSampleSize;
int64_t mFirstTimeRealUs;
int64_t mFirstTimeMediaUs;
status_t readMore();
DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
};
TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
: mNetSession(netSession),
mMaxSampleSize(1024 * 1024),
mFirstTimeRealUs(-1ll),
mFirstTimeMediaUs(-1ll) {
}
TestHandler::~TestHandler() {
}
void TestHandler::listen() {
sp<AMessage> msg = new AMessage(kWhatListen, id());
msg->post();
}
void TestHandler::connect(const char *host, int32_t port) {
sp<AMessage> msg = new AMessage(kWhatConnect, id());
msg->setString("host", host);
msg->setInt32("port", port);
msg->post();
}
void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatListen:
{
sp<AMessage> notify = new AMessage(kWhatReceiverNotify, id());
mReceiver = new RTPReceiver(mNetSession, notify);
looper()->registerHandler(mReceiver);
CHECK_EQ((status_t)OK,
mReceiver->registerPacketType(
33, RTPReceiver::PACKETIZATION_H264));
int32_t receiverRTPPort;
CHECK_EQ((status_t)OK,
mReceiver->initAsync(
RTPReceiver::TRANSPORT_UDP, &receiverRTPPort));
printf("picked receiverRTPPort %d\n", receiverRTPPort);
#if 0
CHECK_EQ((status_t)OK,
mReceiver->connect(
"127.0.0.1", senderRTPPort, senderRTPPort + 1));
#endif
break;
}
case kWhatConnect:
{
AString host;
CHECK(msg->findString("host", &host));
int32_t receiverRTPPort;
CHECK(msg->findInt32("port", &receiverRTPPort));
mExtractor = new NuMediaExtractor;
CHECK_EQ((status_t)OK,
mExtractor->setDataSource(
"/sdcard/Frame Counter HD 30FPS_1080p.mp4"));
bool haveVideo = false;
for (size_t i = 0; i < mExtractor->countTracks(); ++i) {
sp<AMessage> format;
CHECK_EQ((status_t)OK, mExtractor->getTrackFormat(i, &format));
AString mime;
CHECK(format->findString("mime", &mime));
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime.c_str())) {
mExtractor->selectTrack(i);
haveVideo = true;
break;
}
}
CHECK(haveVideo);
sp<AMessage> notify = new AMessage(kWhatSenderNotify, id());
mSender = new RTPSender(mNetSession, notify);
looper()->registerHandler(mSender);
int32_t senderRTPPort;
CHECK_EQ((status_t)OK,
mSender->initAsync(
RTPSender::TRANSPORT_UDP,
host.c_str(),
receiverRTPPort,
receiverRTPPort + 1,
&senderRTPPort));
printf("picked senderRTPPort %d\n", senderRTPPort);
break;
}
case kWhatSenderNotify:
{
ALOGI("kWhatSenderNotify");
int32_t what;
CHECK(msg->findInt32("what", &what));
switch (what) {
case RTPSender::kWhatInitDone:
{
int32_t err;
CHECK(msg->findInt32("err", &err));
ALOGI("RTPSender::initAsync completed w/ err %d", err);
if (err == OK) {
err = readMore();
if (err != OK) {
(new AMessage(kWhatStop, id()))->post();
}
}
break;
}
case RTPSender::kWhatError:
break;
}
break;
}
case kWhatReceiverNotify:
{
ALOGI("kWhatReceiverNotify");
int32_t what;
CHECK(msg->findInt32("what", &what));
switch (what) {
case RTPReceiver::kWhatInitDone:
{
int32_t err;
CHECK(msg->findInt32("err", &err));
ALOGI("RTPReceiver::initAsync completed w/ err %d", err);
break;
}
case RTPSender::kWhatError:
break;
}
break;
}
case kWhatSendMore:
{
sp<ABuffer> accessUnit;
CHECK(msg->findBuffer("accessUnit", &accessUnit));
CHECK_EQ((status_t)OK,
mSender->queueBuffer(
accessUnit,
33,
RTPSender::PACKETIZATION_H264));
status_t err = readMore();
if (err != OK) {
(new AMessage(kWhatStop, id()))->post();
}
break;
}
case kWhatStop:
{
if (mReceiver != NULL) {
looper()->unregisterHandler(mReceiver->id());
mReceiver.clear();
}
if (mSender != NULL) {
looper()->unregisterHandler(mSender->id());
mSender.clear();
}
mExtractor.clear();
looper()->stop();
break;
}
default:
TRESPASS();
}
}
status_t TestHandler::readMore() {
int64_t timeUs;
status_t err = mExtractor->getSampleTime(&timeUs);
if (err != OK) {
return err;
}
sp<ABuffer> accessUnit = new ABuffer(mMaxSampleSize);
CHECK_EQ((status_t)OK, mExtractor->readSampleData(accessUnit));
accessUnit->meta()->setInt64("timeUs", timeUs);
CHECK_EQ((status_t)OK, mExtractor->advance());
int64_t nowUs = ALooper::GetNowUs();
int64_t whenUs;
if (mFirstTimeRealUs < 0ll) {
mFirstTimeRealUs = whenUs = nowUs;
mFirstTimeMediaUs = timeUs;
} else {
whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
}
sp<AMessage> msg = new AMessage(kWhatSendMore, id());
msg->setBuffer("accessUnit", accessUnit);
msg->post(whenUs - nowUs);
return OK;
}
} // namespace android
static void usage(const char *me) {
fprintf(stderr,
"usage: %s -c host:port\tconnect to remote host\n"
" -l \tlisten\n",
me);
}
int main(int argc, char **argv) {
using namespace android;
// srand(time(NULL));
ProcessState::self()->startThreadPool();
DataSource::RegisterDefaultSniffers();
bool listen = false;
int32_t connectToPort = -1;
AString connectToHost;
int res;
while ((res = getopt(argc, argv, "hc:l")) >= 0) {
switch (res) {
case 'c':
{
const char *colonPos = strrchr(optarg, ':');
if (colonPos == NULL) {
usage(argv[0]);
exit(1);
}
connectToHost.setTo(optarg, colonPos - optarg);
char *end;
connectToPort = strtol(colonPos + 1, &end, 10);
if (*end != '\0' || end == colonPos + 1
|| connectToPort < 1 || connectToPort > 65535) {
fprintf(stderr, "Illegal port specified.\n");
exit(1);
}
break;
}
case 'l':
{
listen = true;
break;
}
case '?':
case 'h':
usage(argv[0]);
exit(1);
}
}
if (!listen && connectToPort < 0) {
fprintf(stderr,
"You need to select either client or server mode.\n");
exit(1);
}
sp<ANetworkSession> netSession = new ANetworkSession;
netSession->start();
sp<ALooper> looper = new ALooper;
sp<TestHandler> handler = new TestHandler(netSession);
looper->registerHandler(handler);
if (listen) {
handler->listen();
}
if (connectToPort >= 0) {
handler->connect(connectToHost.c_str(), connectToPort);
}
looper->start(true /* runOnCallingThread */);
return 0;
}