summaryrefslogtreecommitdiffstats
path: root/media/libstagefright/wifi-display/rtptest.cpp
diff options
context:
space:
mode:
Diffstat (limited to 'media/libstagefright/wifi-display/rtptest.cpp')
-rw-r--r--media/libstagefright/wifi-display/rtptest.cpp565
1 files changed, 565 insertions, 0 deletions
diff --git a/media/libstagefright/wifi-display/rtptest.cpp b/media/libstagefright/wifi-display/rtptest.cpp
new file mode 100644
index 0000000..764a38b
--- /dev/null
+++ b/media/libstagefright/wifi-display/rtptest.cpp
@@ -0,0 +1,565 @@
+/*
+ * 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 "TimeSyncer.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/foundation/hexdump.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/NuMediaExtractor.h>
+#include <media/stagefright/Utils.h>
+
+#define MEDIA_FILENAME "/sdcard/Frame Counter HD 30FPS_1080p.mp4"
+
+namespace android {
+
+struct PacketSource : public RefBase {
+ PacketSource() {}
+
+ virtual sp<ABuffer> getNextAccessUnit() = 0;
+
+protected:
+ virtual ~PacketSource() {}
+
+private:
+ DISALLOW_EVIL_CONSTRUCTORS(PacketSource);
+};
+
+struct MediaPacketSource : public PacketSource {
+ MediaPacketSource()
+ : mMaxSampleSize(1024 * 1024) {
+ mExtractor = new NuMediaExtractor;
+ CHECK_EQ((status_t)OK,
+ mExtractor->setDataSource(MEDIA_FILENAME));
+
+ 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);
+ }
+
+ virtual sp<ABuffer> getNextAccessUnit() {
+ int64_t timeUs;
+ status_t err = mExtractor->getSampleTime(&timeUs);
+
+ if (err != OK) {
+ return NULL;
+ }
+
+ 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());
+
+ return accessUnit;
+ }
+
+protected:
+ virtual ~MediaPacketSource() {
+ }
+
+private:
+ sp<NuMediaExtractor> mExtractor;
+ size_t mMaxSampleSize;
+
+ DISALLOW_EVIL_CONSTRUCTORS(MediaPacketSource);
+};
+
+struct SimplePacketSource : public PacketSource {
+ SimplePacketSource()
+ : mCounter(0) {
+ }
+
+ virtual sp<ABuffer> getNextAccessUnit() {
+ sp<ABuffer> buffer = new ABuffer(4);
+ uint8_t *dst = buffer->data();
+ dst[0] = mCounter >> 24;
+ dst[1] = (mCounter >> 16) & 0xff;
+ dst[2] = (mCounter >> 8) & 0xff;
+ dst[3] = mCounter & 0xff;
+
+ buffer->meta()->setInt64("timeUs", mCounter * 1000000ll / kFrameRate);
+
+ ++mCounter;
+
+ return buffer;
+ }
+
+protected:
+ virtual ~SimplePacketSource() {
+ }
+
+private:
+ enum {
+ kFrameRate = 30
+ };
+
+ uint32_t mCounter;
+
+ DISALLOW_EVIL_CONSTRUCTORS(SimplePacketSource);
+};
+
+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,
+ kWhatTimeSyncerNotify,
+ };
+
+#if 1
+ static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_UDP;
+ static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_UDP;
+#else
+ static const RTPBase::TransportMode kRTPMode = RTPBase::TRANSPORT_TCP;
+ static const RTPBase::TransportMode kRTCPMode = RTPBase::TRANSPORT_NONE;
+#endif
+
+#if 1
+ static const RTPBase::PacketizationMode kPacketizationMode
+ = RTPBase::PACKETIZATION_H264;
+#else
+ static const RTPBase::PacketizationMode kPacketizationMode
+ = RTPBase::PACKETIZATION_NONE;
+#endif
+
+ sp<ANetworkSession> mNetSession;
+ sp<PacketSource> mSource;
+ sp<RTPSender> mSender;
+ sp<RTPReceiver> mReceiver;
+
+ sp<TimeSyncer> mTimeSyncer;
+ bool mTimeSyncerStarted;
+
+ int64_t mFirstTimeRealUs;
+ int64_t mFirstTimeMediaUs;
+
+ int64_t mTimeOffsetUs;
+ bool mTimeOffsetValid;
+
+ status_t readMore();
+
+ DISALLOW_EVIL_CONSTRUCTORS(TestHandler);
+};
+
+TestHandler::TestHandler(const sp<ANetworkSession> &netSession)
+ : mNetSession(netSession),
+ mTimeSyncerStarted(false),
+ mFirstTimeRealUs(-1ll),
+ mFirstTimeMediaUs(-1ll),
+ mTimeOffsetUs(-1ll),
+ mTimeOffsetValid(false) {
+}
+
+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();
+}
+
+static void dumpDelay(int64_t delayMs) {
+ static const int64_t kMinDelayMs = 0;
+ static const int64_t kMaxDelayMs = 300;
+
+ const char *kPattern = "########################################";
+ size_t kPatternSize = strlen(kPattern);
+
+ int n = (kPatternSize * (delayMs - kMinDelayMs))
+ / (kMaxDelayMs - kMinDelayMs);
+
+ if (n < 0) {
+ n = 0;
+ } else if ((size_t)n > kPatternSize) {
+ n = kPatternSize;
+ }
+
+ ALOGI("(%4lld ms) %s\n",
+ delayMs,
+ kPattern + kPatternSize - n);
+}
+
+void TestHandler::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatListen:
+ {
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+
+ notify = new AMessage(kWhatReceiverNotify, id());
+ mReceiver = new RTPReceiver(
+ mNetSession, notify, RTPReceiver::FLAG_AUTO_CONNECT);
+ looper()->registerHandler(mReceiver);
+
+ CHECK_EQ((status_t)OK,
+ mReceiver->registerPacketType(33, kPacketizationMode));
+
+ int32_t receiverRTPPort;
+ CHECK_EQ((status_t)OK,
+ mReceiver->initAsync(
+ kRTPMode,
+ kRTCPMode,
+ &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));
+
+ sp<AMessage> notify = new AMessage(kWhatTimeSyncerNotify, id());
+ mTimeSyncer = new TimeSyncer(mNetSession, notify);
+ looper()->registerHandler(mTimeSyncer);
+ mTimeSyncer->startServer(8123);
+
+ int32_t receiverRTPPort;
+ CHECK(msg->findInt32("port", &receiverRTPPort));
+
+#if 1
+ mSource = new MediaPacketSource;
+#else
+ mSource = new SimplePacketSource;
+#endif
+
+ notify = new AMessage(kWhatSenderNotify, id());
+ mSender = new RTPSender(mNetSession, notify);
+
+ looper()->registerHandler(mSender);
+
+ int32_t senderRTPPort;
+ CHECK_EQ((status_t)OK,
+ mSender->initAsync(
+ host.c_str(),
+ receiverRTPPort,
+ kRTPMode,
+ kRTCPMode == RTPBase::TRANSPORT_NONE
+ ? -1 : receiverRTPPort + 1,
+ kRTCPMode,
+ &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:
+ {
+ ALOGV("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 RTPReceiver::kWhatError:
+ break;
+
+ case RTPReceiver::kWhatAccessUnit:
+ {
+#if 0
+ if (!mTimeSyncerStarted) {
+ mTimeSyncer->startClient("172.18.41.216", 8123);
+ mTimeSyncerStarted = true;
+ }
+
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ if (mTimeOffsetValid) {
+ timeUs -= mTimeOffsetUs;
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t delayMs = (nowUs - timeUs) / 1000ll;
+
+ dumpDelay(delayMs);
+ }
+#endif
+ break;
+ }
+
+ case RTPReceiver::kWhatPacketLost:
+ ALOGV("kWhatPacketLost");
+ break;
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatSendMore:
+ {
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("accessUnit", &accessUnit));
+
+ CHECK_EQ((status_t)OK,
+ mSender->queueBuffer(
+ accessUnit,
+ 33,
+ kPacketizationMode));
+
+ 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();
+ }
+
+ mSource.clear();
+
+ looper()->stop();
+ break;
+ }
+
+ case kWhatTimeSyncerNotify:
+ {
+ CHECK(msg->findInt64("offset", &mTimeOffsetUs));
+ mTimeOffsetValid = true;
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+status_t TestHandler::readMore() {
+ sp<ABuffer> accessUnit = mSource->getNextAccessUnit();
+
+ if (accessUnit == NULL) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ int64_t timeUs;
+ CHECK(accessUnit->meta()->findInt64("timeUs", &timeUs));
+
+ int64_t nowUs = ALooper::GetNowUs();
+ int64_t whenUs;
+
+ if (mFirstTimeRealUs < 0ll) {
+ mFirstTimeRealUs = whenUs = nowUs;
+ mFirstTimeMediaUs = timeUs;
+ } else {
+ whenUs = mFirstTimeRealUs + timeUs - mFirstTimeMediaUs;
+ }
+
+ accessUnit->meta()->setInt64("timeUs", whenUs);
+
+ 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;
+}
+