Bug 831645 - Part1, Add Rtsp protocol and streaming protocol interface. r=jduell.mcbugs, sworkman
authorBenjamin Chen <bechen@mozilla.com>
Mon, 23 Sep 2013 18:12:11 +0800
changeset 150764 13746e3db7caf27555f126fe5fc7dd7eccb442b0
parent 150763 0df04ffc22467ac7c328a6a8219b27aedf891c6c
child 150765 59fb4a13900786755c36b9c4175f4e2704ebccc8
push id25466
push userkwierso@gmail.com
push dateWed, 16 Oct 2013 03:09:49 +0000
treeherdermozilla-central@71909c62bc14 [default view] [failures only]
perfherder[talos] [build metrics] [platform microbench] (compared to previous push)
reviewersjduell
bugs831645
milestone27.0a1
first release with
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
last release without
nightly linux32
nightly linux64
nightly mac
nightly win32
nightly win64
Bug 831645 - Part1, Add Rtsp protocol and streaming protocol interface. r=jduell.mcbugs, sworkman
b2g/installer/package-manifest.in
content/media/moz.build
content/media/omx/moz.build
layout/build/nsLayoutModule.cpp
netwerk/base/public/moz.build
netwerk/base/public/nsIStreamingProtocolController.idl
netwerk/base/public/nsIStreamingProtocolService.idl
netwerk/base/src/Makefile.in
netwerk/base/src/StreamingProtocolService.cpp
netwerk/base/src/StreamingProtocolService.h
netwerk/base/src/moz.build
netwerk/build/Makefile.in
netwerk/build/nsNetCID.h
netwerk/build/nsNetModule.cpp
netwerk/ipc/NeckoChild.cpp
netwerk/ipc/NeckoChild.h
netwerk/ipc/NeckoParent.cpp
netwerk/ipc/NeckoParent.h
netwerk/ipc/PNecko.ipdl
netwerk/ipc/PRtspController.ipdl
netwerk/ipc/moz.build
netwerk/protocol/moz.build
netwerk/protocol/rtsp/Makefile.in
netwerk/protocol/rtsp/RtspChannel.cpp
netwerk/protocol/rtsp/RtspChannel.h
netwerk/protocol/rtsp/RtspHandler.cpp
netwerk/protocol/rtsp/RtspHandler.h
netwerk/protocol/rtsp/controller/RtspController.cpp
netwerk/protocol/rtsp/controller/RtspController.h
netwerk/protocol/rtsp/controller/RtspControllerChild.cpp
netwerk/protocol/rtsp/controller/RtspControllerChild.h
netwerk/protocol/rtsp/controller/RtspControllerParent.cpp
netwerk/protocol/rtsp/controller/RtspControllerParent.h
netwerk/protocol/rtsp/controller/RtspMetaData.cpp
netwerk/protocol/rtsp/controller/RtspMetaData.h
netwerk/protocol/rtsp/moz.build
netwerk/protocol/rtsp/rtsp/AAMRAssembler.cpp
netwerk/protocol/rtsp/rtsp/AAMRAssembler.h
netwerk/protocol/rtsp/rtsp/AAVCAssembler.cpp
netwerk/protocol/rtsp/rtsp/AAVCAssembler.h
netwerk/protocol/rtsp/rtsp/AH263Assembler.cpp
netwerk/protocol/rtsp/rtsp/AH263Assembler.h
netwerk/protocol/rtsp/rtsp/AMPEG4AudioAssembler.cpp
netwerk/protocol/rtsp/rtsp/AMPEG4AudioAssembler.h
netwerk/protocol/rtsp/rtsp/AMPEG4ElementaryAssembler.cpp
netwerk/protocol/rtsp/rtsp/AMPEG4ElementaryAssembler.h
netwerk/protocol/rtsp/rtsp/APacketSource.cpp
netwerk/protocol/rtsp/rtsp/APacketSource.h
netwerk/protocol/rtsp/rtsp/ARTPAssembler.cpp
netwerk/protocol/rtsp/rtsp/ARTPAssembler.h
netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp
netwerk/protocol/rtsp/rtsp/ARTPConnection.h
netwerk/protocol/rtsp/rtsp/ARTPSession.cpp
netwerk/protocol/rtsp/rtsp/ARTPSession.h
netwerk/protocol/rtsp/rtsp/ARTPSource.cpp
netwerk/protocol/rtsp/rtsp/ARTPSource.h
netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp
netwerk/protocol/rtsp/rtsp/ARTPWriter.h
netwerk/protocol/rtsp/rtsp/ARTSPConnection.cpp
netwerk/protocol/rtsp/rtsp/ARTSPConnection.h
netwerk/protocol/rtsp/rtsp/ARawAudioAssembler.cpp
netwerk/protocol/rtsp/rtsp/ARawAudioAssembler.h
netwerk/protocol/rtsp/rtsp/ASessionDescription.cpp
netwerk/protocol/rtsp/rtsp/ASessionDescription.h
netwerk/protocol/rtsp/rtsp/RTSPConnectionHandler.h
netwerk/protocol/rtsp/rtsp/RTSPSource.cpp
netwerk/protocol/rtsp/rtsp/RTSPSource.h
netwerk/protocol/rtsp/rtsp/RTSPTransmitter.h
netwerk/protocol/rtsp/rtsp/UDPPusher.cpp
netwerk/protocol/rtsp/rtsp/UDPPusher.h
netwerk/protocol/rtsp/rtsp/VideoSource.h
netwerk/protocol/rtsp/rtsp/rtp_test.cpp
toolkit/library/Makefile.in
--- a/b2g/installer/package-manifest.in
+++ b/b2g/installer/package-manifest.in
@@ -263,16 +263,19 @@
 @BINPATH@/components/necko_http.xpt
 @BINPATH@/components/necko_res.xpt
 @BINPATH@/components/necko_socket.xpt
 @BINPATH@/components/necko_strconv.xpt
 @BINPATH@/components/necko_viewsource.xpt
 @BINPATH@/components/necko_websocket.xpt
 @BINPATH@/components/necko_wifi.xpt
 @BINPATH@/components/necko_wyciwyg.xpt
+#ifdef MOZ_RTSP
+@BINPATH@/components/necko_rtsp.xpt
+#endif
 @BINPATH@/components/necko.xpt
 @BINPATH@/components/loginmgr.xpt
 @BINPATH@/components/parentalcontrols.xpt
 #ifdef MOZ_WEBRTC
 @BINPATH@/components/peerconnection.xpt
 #endif
 @BINPATH@/components/places.xpt
 @BINPATH@/components/plugin.xpt
--- a/content/media/moz.build
+++ b/content/media/moz.build
@@ -77,16 +77,17 @@ EXPORTS += [
     'MediaDecoderOwner.h',
     'MediaDecoderReader.h',
     'MediaDecoderStateMachine.h',
     'MediaMetadataManager.h',
     'MediaRecorder.h',
     'MediaResource.h',
     'MediaSegment.h',
     'MediaStreamGraph.h',
+    'RtspMediaResource.h',
     'SharedBuffer.h',
     'StreamBuffer.h',
     'TimeVarying.h',
     'TrackUnionStream.h',
     'VideoFrameContainer.h',
     'VideoSegment.h',
     'VideoUtils.h',
     'VorbisUtils.h',
@@ -124,16 +125,17 @@ CPP_SOURCES += [
     'MediaCache.cpp',
     'MediaDecoder.cpp',
     'MediaDecoderReader.cpp',
     'MediaDecoderStateMachine.cpp',
     'MediaRecorder.cpp',
     'MediaResource.cpp',
     'MediaStreamGraph.cpp',
     'MediaStreamTrack.cpp',
+    'RtspMediaResource.cpp',
     'StreamBuffer.cpp',
     'TextTrack.cpp',
     'TextTrackCue.cpp',
     'TextTrackCueList.cpp',
     'TextTrackList.cpp',
     'TextTrackRegion.cpp',
     'TextTrackRegionList.cpp',
     'VideoFrameContainer.cpp',
--- a/content/media/omx/moz.build
+++ b/content/media/omx/moz.build
@@ -13,12 +13,22 @@ EXPORTS += [
 
 CPP_SOURCES += [
     'MediaOmxDecoder.cpp',
     'MediaOmxReader.cpp',
     'OmxDecoder.cpp',
     'OMXCodecProxy.cpp',
 ]
 
+if CONFIG['MOZ_RTSP']:
+    EXPORTS += [
+        'RtspOmxDecoder.h',
+        'RtspOmxReader.h',
+    ]
+    CPP_SOURCES += [
+        'RtspOmxDecoder.cpp',
+        'RtspOmxReader.cpp',
+    ]
+
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'gkconomx_s'
 
--- a/layout/build/nsLayoutModule.cpp
+++ b/layout/build/nsLayoutModule.cpp
@@ -225,16 +225,17 @@ static void Shutdown();
 #include "nsIMediaManager.h"
 #include "nsMixedContentBlocker.h"
 
 #include "AudioChannelService.h"
 
 #include "mozilla/dom/power/PowerManagerService.h"
 #include "mozilla/dom/alarm/AlarmHalService.h"
 #include "mozilla/dom/time/TimeService.h"
+#include "StreamingProtocolService.h"
 
 #include "mozilla/dom/telephony/TelephonyFactory.h"
 #include "nsITelephonyProvider.h"
 
 #ifdef MOZ_WIDGET_GONK
 #include "GonkGPSGeolocationProvider.h"
 #endif
 #include "MediaManager.h"
@@ -246,16 +247,17 @@ using namespace mozilla::dom::telephony;
 using mozilla::dom::alarm::AlarmHalService;
 using mozilla::dom::indexedDB::IndexedDatabaseManager;
 using mozilla::dom::power::PowerManagerService;
 using mozilla::dom::quota::QuotaManager;
 using mozilla::dom::TCPSocketChild;
 using mozilla::dom::TCPSocketParent;
 using mozilla::dom::TCPServerSocketChild;
 using mozilla::dom::time::TimeService;
+using mozilla::net::StreamingProtocolControllerService;
 
 // Transformiix
 /* 5d5d92cd-6bf8-11d9-bf4a-000a95dc234c */
 #define TRANSFORMIIX_NODESET_CID \
 { 0x5d5d92cd, 0x6bf8, 0x11d9, { 0xbf, 0x4a, 0x0, 0x0a, 0x95, 0xdc, 0x23, 0x4c } }
 
 #define TRANSFORMIIX_NODESET_CONTRACTID \
 "@mozilla.org/transformiix-nodeset;1"
@@ -323,16 +325,19 @@ NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIMobileMessageDatabaseService,
                                          SmsServicesFactory::CreateMobileMessageDatabaseService)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIPowerManagerService,
                                          PowerManagerService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIAlarmHalService,
                                          AlarmHalService::GetInstance)
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsITimeService,
                                          TimeService::GetInstance)
+NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIStreamingProtocolControllerService,
+                                         StreamingProtocolControllerService::GetInstance)
+
 #ifdef MOZ_GAMEPAD
 using mozilla::dom::GamepadServiceTest;
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(GamepadServiceTest,
                                          GamepadServiceTest::CreateService)
 #endif
 
 #ifdef MOZ_WIDGET_GONK
 NS_GENERIC_FACTORY_SINGLETON_CONSTRUCTOR(nsIGeolocationProvider,
@@ -799,16 +804,17 @@ NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_SERVI
 NS_DEFINE_NAMED_CID(MOBILE_MESSAGE_DATABASE_SERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_POWERMANAGERSERVICE_CID);
 NS_DEFINE_NAMED_CID(OSFILECONSTANTSSERVICE_CID);
 NS_DEFINE_NAMED_CID(NS_ALARMHALSERVICE_CID);
 NS_DEFINE_NAMED_CID(TCPSOCKETCHILD_CID);
 NS_DEFINE_NAMED_CID(TCPSOCKETPARENT_CID);
 NS_DEFINE_NAMED_CID(TCPSERVERSOCKETCHILD_CID);
 NS_DEFINE_NAMED_CID(NS_TIMESERVICE_CID);
+NS_DEFINE_NAMED_CID(NS_MEDIASTREAMCONTROLLERSERVICE_CID);
 #ifdef MOZ_WIDGET_GONK
 NS_DEFINE_NAMED_CID(GONK_GPS_GEOLOCATION_PROVIDER_CID);
 #endif
 NS_DEFINE_NAMED_CID(NS_MEDIAMANAGERSERVICE_CID);
 #ifdef MOZ_GAMEPAD
 NS_DEFINE_NAMED_CID(NS_GAMEPAD_TEST_CID);
 #endif
 #ifdef MOZ_WEBSPEECH
@@ -1088,16 +1094,17 @@ static const mozilla::Module::CIDEntry k
   { &kMOBILE_MESSAGE_DATABASE_SERVICE_CID, false, nullptr, nsIMobileMessageDatabaseServiceConstructor },
   { &kNS_POWERMANAGERSERVICE_CID, false, nullptr, nsIPowerManagerServiceConstructor },
   { &kOSFILECONSTANTSSERVICE_CID, true, nullptr, OSFileConstantsServiceConstructor },
   { &kNS_ALARMHALSERVICE_CID, false, nullptr, nsIAlarmHalServiceConstructor },
   { &kTCPSOCKETCHILD_CID, false, nullptr, TCPSocketChildConstructor },
   { &kTCPSOCKETPARENT_CID, false, nullptr, TCPSocketParentConstructor },
   { &kTCPSERVERSOCKETCHILD_CID, false, nullptr, TCPServerSocketChildConstructor },
   { &kNS_TIMESERVICE_CID, false, nullptr, nsITimeServiceConstructor },
+  { &kNS_MEDIASTREAMCONTROLLERSERVICE_CID, false, NULL, nsIStreamingProtocolControllerServiceConstructor },
 #ifdef MOZ_WIDGET_GONK
   { &kGONK_GPS_GEOLOCATION_PROVIDER_CID, false, nullptr, nsIGeolocationProviderConstructor },
 #endif
   { &kNS_MEDIAMANAGERSERVICE_CID, false, nullptr, nsIMediaManagerServiceConstructor },
 #ifdef MOZ_GAMEPAD
   { &kNS_GAMEPAD_TEST_CID, false, nullptr, GamepadServiceTestConstructor },
 #endif
 #ifdef ACCESSIBILITY
@@ -1244,16 +1251,17 @@ static const mozilla::Module::ContractID
   { MOBILE_MESSAGE_DATABASE_SERVICE_CONTRACTID, &kMOBILE_MESSAGE_DATABASE_SERVICE_CID },
   { POWERMANAGERSERVICE_CONTRACTID, &kNS_POWERMANAGERSERVICE_CID },
   { OSFILECONSTANTSSERVICE_CONTRACTID, &kOSFILECONSTANTSSERVICE_CID },
   { ALARMHALSERVICE_CONTRACTID, &kNS_ALARMHALSERVICE_CID },
   { "@mozilla.org/tcp-socket-child;1", &kTCPSOCKETCHILD_CID },
   { "@mozilla.org/tcp-socket-parent;1", &kTCPSOCKETPARENT_CID },
   { "@mozilla.org/tcp-server-socket-child;1", &kTCPSERVERSOCKETCHILD_CID },
   { TIMESERVICE_CONTRACTID, &kNS_TIMESERVICE_CID },
+  { MEDIASTREAMCONTROLLERSERVICE_CONTRACTID, &kNS_MEDIASTREAMCONTROLLERSERVICE_CID },
 #ifdef MOZ_WIDGET_GONK
   { GONK_GPS_GEOLOCATION_PROVIDER_CONTRACTID, &kGONK_GPS_GEOLOCATION_PROVIDER_CID },
 #endif
 #ifdef MOZ_GAMEPAD
   { NS_GAMEPAD_TEST_CONTRACTID, &kNS_GAMEPAD_TEST_CID },
 #endif
   { MEDIAMANAGERSERVICE_CONTRACTID, &kNS_MEDIAMANAGERSERVICE_CID },
 #ifdef ACCESSIBILITY
--- a/netwerk/base/public/moz.build
+++ b/netwerk/base/public/moz.build
@@ -90,16 +90,18 @@ XPIDL_SOURCES += [
     'nsISocketTransport.idl',
     'nsISocketTransportService.idl',
     'nsISpeculativeConnect.idl',
     'nsIStandardURL.idl',
     'nsIStreamListener.idl',
     'nsIStreamListenerTee.idl',
     'nsIStreamLoader.idl',
     'nsIStreamTransportService.idl',
+    'nsIStreamingProtocolController.idl',
+    'nsIStreamingProtocolService.idl',
     'nsISyncStreamListener.idl',
     'nsISystemProxySettings.idl',
     'nsIThreadRetargetableRequest.idl',
     'nsIThreadRetargetableStreamListener.idl',
     'nsITimedChannel.idl',
     'nsITraceableChannel.idl',
     'nsITransport.idl',
     'nsIUDPServerSocket.idl',
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIStreamingProtocolController.idl
@@ -0,0 +1,179 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=4 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+interface nsIURI;
+
+#include "nsISupports.idl"
+
+%{C++
+#define MEDIASTREAM_FRAMETYPE_NORMAL          0x00000001
+#define MEDIASTREAM_FRAMETYPE_DISCONTINUITY   0x00000002
+%}
+
+/**
+ * Metadata of the media stream.
+ */
+[uuid(294adb30-856c-11e2-9e96-0800200c9a66)]
+interface nsIStreamingProtocolMetaData : nsISupports
+{
+    /**
+     * Frame type.
+     */
+    attribute uint32_t frameType;
+
+    /**
+     * The total tracks for the given media stream session.
+     */
+    attribute uint32_t totalTracks;
+
+    /**
+     * The mime type of the track.
+     */
+    attribute ACString mimeType;
+
+    /**
+     * The width of the resolution.
+     */
+    attribute unsigned long width;
+
+    /**
+     * The height of the resolution.
+     */
+    attribute unsigned long height;
+
+    /**
+     * The duration of the media stream.
+     */
+    attribute unsigned long long duration;
+
+    /**
+     * The sample rate of the media stream.
+     */
+    attribute unsigned long sampleRate;
+
+    /**
+     * The timestamp indicates the stream absolute position
+     * relative to the beginning of the presentation.
+     */
+    attribute unsigned long long timeStamp;
+
+    /**
+     * The total number of audio channels in the media stream.
+     */
+    attribute unsigned long channelCount;
+
+    /**
+     * The AAC audio codec specific data.
+     */
+    attribute ACString esdsData;
+
+    /**
+     * The AVCC format extradata of H.264 stream.
+     */
+    attribute ACString avccData;
+};
+
+/**
+ * nsIStreamingProtocolListener
+ */
+[scriptable, uuid(c4f6b660-892e-11e2-9e96-0800200c9a66)]
+interface nsIStreamingProtocolListener : nsISupports
+{
+    /**
+     * Called when the data may be read without blocking the calling thread.
+     * @param index The track number of the media stream.
+     * @param data Raw data of the media stream on given track number.
+     * @param length The length of the raw data.
+     * @param offset The offset in the data stream from the start of the media
+     *               presentation in bytes.
+     * @param meta The meta data of the frame.
+     */
+    void onMediaDataAvailable(in uint8_t index,
+                              in ACString data,
+                              in uint32_t length,
+                              in uint32_t offset,
+                              in nsIStreamingProtocolMetaData meta);
+
+    /**
+     * Called when the meta data for a given session is available.
+     * @param index The track number of the media stream.
+     * @param meta The meta data of the media stream.
+     */
+    void onConnected(in uint8_t index, in nsIStreamingProtocolMetaData meta);
+
+    /**
+     * Called when the Rtsp session is closed.
+     * @param index Track number of the media stream.
+     * @param reason The reason of disconnection.
+     */
+    void onDisconnected(in uint8_t index, in uint32_t reason);
+};
+
+/**
+ * Media stream controller API: control and retrieve meta data from media stream.
+ */
+[uuid(a9bdd4b0-8559-11e2-9e96-0800200c9a66)]
+interface nsIStreamingProtocolController : nsISupports
+{
+    /**
+     * Preprare the URI before we can start the connection.
+     * @param aUri The URI of the media stream.
+     */
+    void init(in nsIURI aUri);
+
+    /**
+     * Asynchronously open this controller.  Data is fed to the specified
+     * media stream listener as it becomes available. If asyncOpen returns
+     * successfully, the controller is responsible for keeping itself alive
+     * until it has called onStopRequest on aListener.
+     *
+     * @param aListener The nsIStreamingProtocolListener implementation
+     */
+    void asyncOpen(in nsIStreamingProtocolListener aListener);
+
+    /*
+     * Get the metadata of a track.
+     * @param index Index of a track.
+     * @return A nsIStreamingProtocolMetaData.
+     */
+    nsIStreamingProtocolMetaData getTrackMetaData(in octet index);
+
+    /*
+     * Tell the streaming server to start sending media data.
+     */
+    void play();
+
+    /*
+     * Tell the streaming server to pause sending media data.
+     */
+    void pause();
+
+    /*
+     * Tell the streaming server to resume the suspended media stream.
+     */
+    void resume();
+
+    /*
+     * Tell the streaming server to suspend the media stream.
+     */
+    void suspend();
+
+    /*
+     * Tell the streaming server to send media data in specific time.
+     * @param seekTimeUs Start time of the media stream in microseconds.
+     */
+    void seek(in unsigned long long seekTimeUs);
+
+    /*
+     * Tell the streaming server to stop the
+     * media stream and close the connection.
+     */
+    void stop();
+
+    /**
+     * Total number of audio/video tracks.
+     */
+    readonly attribute octet totalTracks;
+};
new file mode 100644
--- /dev/null
+++ b/netwerk/base/public/nsIStreamingProtocolService.idl
@@ -0,0 +1,35 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 4 -*- */
+/* vim: set sw=4 ts=4 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+interface nsIStreamingProtocolController;
+interface nsIChannel;
+
+#include "nsISupports.idl"
+
+%{C++
+#define NS_MEDIASTREAMCONTROLLERSERVICE_CID                                   \
+  { 0x94838530, 0x8627, 0x11e2,                                               \
+    {                                                                         \
+      0x9e, 0x96, 0x08, 0x00,                                                 \
+      0x20, 0x0c, 0x9a, 0x66                                                  \
+    }                                                                         \
+  }
+#define MEDIASTREAMCONTROLLERSERVICE_CONTRACTID                               \
+  "@mozilla.org/mediastream/mediastreamcontrollerservice;1"
+%}
+
+/**
+ * Media stream controller Service API.
+ */
+[uuid(94838530-8627-11e2-9e96-0800200c9a66)]
+interface nsIStreamingProtocolControllerService : nsISupports
+{
+    /*
+     * Create a new media stream controller from the given channel.
+     * @param channel nsIChannel for the given URI.
+     */
+    nsIStreamingProtocolController create(in nsIChannel channel);
+};
--- a/netwerk/base/src/Makefile.in
+++ b/netwerk/base/src/Makefile.in
@@ -1,16 +1,21 @@
 # vim:set ts=8 sw=8 sts=8 noet:
 #
 # This Source Code Form is subject to the terms of the Mozilla Public
 # License, v. 2.0. If a copy of the MPL was not distributed with this
 # file, You can obtain one at http://mozilla.org/MPL/2.0/.
 
 LOCAL_INCLUDES	+= -I$(topsrcdir)/dom/base
 
+ifdef MOZ_RTSP
+LOCAL_INCLUDES  += -I$(topsrcdir)/netwerk/protocol/rtsp/controller
+LOCAL_INCLUDES  += -I$(topsrcdir)/netwerk/protocol/rtsp/rtsp
+endif
+
 ifdef MOZ_ENABLE_QTNETWORK
 	LOCAL_INCLUDES += -I$(srcdir)/../../system/qt
 	OS_INCLUDES += $(MOZ_QT_CFLAGS)
 endif
 
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
 
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/StreamingProtocolService.cpp
@@ -0,0 +1,71 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "base/basictypes.h"
+#include "mozilla/ClearOnShutdown.h"
+#include "StreamingProtocolService.h"
+#include "mozilla/net/NeckoChild.h"
+#include "nsIURI.h"
+
+#ifdef MOZ_RTSP
+#include "RtspControllerChild.h"
+#include "RtspController.h"
+#endif
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS1(StreamingProtocolControllerService,
+                   nsIStreamingProtocolControllerService)
+
+/* static */
+StaticRefPtr<StreamingProtocolControllerService> sSingleton;
+
+/* static */
+already_AddRefed<StreamingProtocolControllerService>
+StreamingProtocolControllerService::GetInstance()
+{
+  if (!sSingleton) {
+    sSingleton = new StreamingProtocolControllerService();
+    ClearOnShutdown(&sSingleton);
+  }
+  nsRefPtr<StreamingProtocolControllerService> service = sSingleton.get();
+  return service.forget();
+}
+
+NS_IMETHODIMP
+StreamingProtocolControllerService::Create(nsIChannel *aChannel, nsIStreamingProtocolController **aResult)
+{
+  nsRefPtr<nsIStreamingProtocolController> mediacontroller;
+  nsCOMPtr<nsIURI> uri;
+  nsAutoCString scheme;
+
+  NS_ENSURE_ARG_POINTER(aChannel);
+  aChannel->GetURI(getter_AddRefs(uri));
+
+  nsresult rv = uri->GetScheme(scheme);
+  if (NS_FAILED(rv)) return rv;
+
+#ifdef MOZ_RTSP
+  if (scheme.EqualsLiteral("rtsp")) {
+    if (IsNeckoChild()) {
+      mediacontroller = new RtspControllerChild(aChannel);
+    } else {
+      mediacontroller = new RtspController(aChannel);
+    }
+  }
+#endif
+
+  if (!mediacontroller) {
+    return NS_ERROR_NO_INTERFACE;
+  }
+
+  mediacontroller->Init(uri);
+
+  mediacontroller.forget(aResult);
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/base/src/StreamingProtocolService.h
@@ -0,0 +1,33 @@
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef mozilla_net_StreamingProtocolControllerService_h
+#define mozilla_net_StreamingProtocolControllerService_h
+
+#include "mozilla/StaticPtr.h"
+#include "nsIStreamingProtocolService.h"
+#include "nsIStreamingProtocolController.h"
+#include "nsCOMPtr.h"
+#include "nsIChannel.h"
+
+namespace mozilla {
+namespace net {
+
+/**
+ * This class implements a service to help to create streaming protocol controller.
+ */
+class StreamingProtocolControllerService : public nsIStreamingProtocolControllerService
+{
+public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSISTREAMINGPROTOCOLCONTROLLERSERVICE
+
+  StreamingProtocolControllerService() {};
+  virtual ~StreamingProtocolControllerService() {};
+  static already_AddRefed<StreamingProtocolControllerService> GetInstance();
+};
+} // namespace dom
+} // namespace mozilla
+
+#endif //mozilla_net_StreamingProtocolControllerService_h
--- a/netwerk/base/src/moz.build
+++ b/netwerk/base/src/moz.build
@@ -69,16 +69,17 @@ CPP_SOURCES += [
     'nsSyncStreamListener.cpp',
     'nsTemporaryFileInputStream.cpp',
     'nsTransportUtils.cpp',
     'nsUDPServerSocket.cpp',
     'nsURIChecker.cpp',
     'nsURLHelper.cpp',
     'nsURLParsers.cpp',
     'nsUnicharStreamLoader.cpp',
+    'StreamingProtocolService.cpp',
 ]
 
 if CONFIG['MOZ_WIDGET_TOOLKIT'] == 'os2':
     CPP_SOURCES += [
         'nsURLHelperOS2.cpp',
     ]
 elif CONFIG['MOZ_WIDGET_TOOLKIT'] == 'windows':
     CPP_SOURCES += [
--- a/netwerk/build/Makefile.in
+++ b/netwerk/build/Makefile.in
@@ -13,16 +13,20 @@ SHARED_LIBRARY_LIBS = \
   ../cache/$(LIB_PREFIX)nkcache_s.$(LIB_SUFFIX) \
   ../cache2/$(LIB_PREFIX)nkcache2_s.$(LIB_SUFFIX) \
   ../protocol/about/$(LIB_PREFIX)nkabout_s.$(LIB_SUFFIX) \
   $(foreach d,$(filter-out about,$(NECKO_PROTOCOLS)), \
     ../protocol/$(d)/$(LIB_PREFIX)nk$(d)_s.$(LIB_SUFFIX)) \
   ../ipc/$(LIB_PREFIX)neckoipc_s.$(LIB_SUFFIX) \
   $(NULL)
 
+ifdef MOZ_RTSP
+SHARED_LIBRARY_LIBS += ../protocol/rtsp/$(LIB_PREFIX)nkrtsp_s.$(LIB_SUFFIX)
+endif
+
 ifdef MOZ_SRTP
 SHARED_LIBRARY_LIBS += \
   ../srtp/src/$(LIB_PREFIX)nksrtp_s.$(LIB_SUFFIX) \
   $(NULL)
 endif
 
 ifdef MOZ_SCTP
 SHARED_LIBRARY_LIBS += \
@@ -96,10 +100,14 @@ endif
 ifdef NECKO_WIFI
 SHARED_LIBRARY_LIBS += \
   ../wifi/$(LIB_PREFIX)neckowifi_s.$(LIB_SUFFIX) \
   $(NULL)
 LOCAL_INCLUDES += -I$(srcdir)/../wifi
 
 endif
 
+ifdef MOZ_RTSP
+    LOCAL_INCLUDES += -I$(srcdir)/../protocol/rtsp
+endif
+
 include $(topsrcdir)/config/rules.mk
 include $(topsrcdir)/ipc/chromium/chromium-config.mk
--- a/netwerk/build/nsNetCID.h
+++ b/netwerk/build/nsNetCID.h
@@ -666,16 +666,28 @@
 { /* {dc01dbbb-a5bb-4cbb-82bb-085cce06c0bb} */       \
   0xdc01dbbb,                                        \
   0xa5bb,                                            \
   0x4cbb,                                            \
   {0x82, 0xbb, 0x08, 0x5c, 0xce, 0x06, 0xc0, 0xbb}   \
 }
 
 /******************************************************************************
+ * netwerk/protocol/rtsp / classes
+ */
+
+#define NS_RTSPPROTOCOLHANDLER_CID                   \
+{ /* {5bb4b980-7b10-11e2-b92a-0800200c9a66} */       \
+  0x5bb4b980,                                        \
+  0x7b10,                                            \
+  0x11e2,                                            \
+  {0xb9, 0x2a, 0x08, 0x00, 0x20, 0x0c, 0x9a, 0x66}   \
+}
+
+/******************************************************************************
  * netwerk/protocol/about/ classes
  */
 
 #define NS_ABOUTPROTOCOLHANDLER_CID                  \
 { /* 9e3b6c90-2f75-11d3-8cd0-0060b0fc14a3 */         \
     0x9e3b6c90,                                      \
     0x2f75,                                          \
     0x11d3,                                          \
--- a/netwerk/build/nsNetModule.cpp
+++ b/netwerk/build/nsNetModule.cpp
@@ -312,16 +312,25 @@ type##Constructor(nsISupports *aOuter, R
 
 WEB_SOCKET_HANDLER_CONSTRUCTOR(WebSocketChannel, false)
 WEB_SOCKET_HANDLER_CONSTRUCTOR(WebSocketSSLChannel, true)
 #undef WEB_SOCKET_HANDLER_CONSTRUCTOR
 } // namespace mozilla::net
 } // namespace mozilla
 #endif
 
+#ifdef MOZ_RTSP
+#include "RtspHandler.h"
+namespace mozilla {
+namespace net {
+NS_GENERIC_FACTORY_CONSTRUCTOR(RtspHandler)
+} // namespace mozilla::net
+} // namespace mozilla
+#endif
+
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "nsURIChecker.h"
 NS_GENERIC_FACTORY_CONSTRUCTOR(nsURIChecker)
 
 ///////////////////////////////////////////////////////////////////////////////
 
 #include "nsURLParsers.h"
@@ -781,16 +790,19 @@ NS_DEFINE_NAMED_CID(NS_VIEWSOURCEHANDLER
 #endif
 #ifdef NECKO_PROTOCOL_wyciwyg
 NS_DEFINE_NAMED_CID(NS_WYCIWYGPROTOCOLHANDLER_CID);
 #endif
 #ifdef NECKO_PROTOCOL_websocket
 NS_DEFINE_NAMED_CID(NS_WEBSOCKETPROTOCOLHANDLER_CID);
 NS_DEFINE_NAMED_CID(NS_WEBSOCKETSSLPROTOCOLHANDLER_CID);
 #endif
+#ifdef MOZ_RTSP
+NS_DEFINE_NAMED_CID(NS_RTSPPROTOCOLHANDLER_CID);
+#endif
 #if defined(XP_WIN)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_WIDGET_COCOA)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_ENABLE_QTNETWORK)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
 #elif defined(MOZ_WIDGET_ANDROID)
 NS_DEFINE_NAMED_CID(NS_NETWORK_LINK_SERVICE_CID);
@@ -918,16 +930,19 @@ static const mozilla::Module::CIDEntry k
     { &kNS_WYCIWYGPROTOCOLHANDLER_CID, false, nullptr, nsWyciwygProtocolHandlerConstructor },
 #endif
 #ifdef NECKO_PROTOCOL_websocket
     { &kNS_WEBSOCKETPROTOCOLHANDLER_CID, false, nullptr,
       mozilla::net::WebSocketChannelConstructor },
     { &kNS_WEBSOCKETSSLPROTOCOLHANDLER_CID, false, nullptr,
       mozilla::net::WebSocketSSLChannelConstructor },
 #endif
+#ifdef MOZ_RTSP
+    { &kNS_RTSPPROTOCOLHANDLER_CID, false, NULL, mozilla::net::RtspHandlerConstructor },
+#endif
 #if defined(XP_WIN)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsNotifyAddrListenerConstructor },
 #elif defined(MOZ_WIDGET_COCOA)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsNetworkLinkServiceConstructor },
 #elif defined(MOZ_ENABLE_QTNETWORK)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsQtNetworkLinkServiceConstructor },
 #elif defined(MOZ_WIDGET_ANDROID)
     { &kNS_NETWORK_LINK_SERVICE_CID, false, nullptr, nsAndroidNetworkLinkServiceConstructor },
@@ -1058,16 +1073,19 @@ static const mozilla::Module::ContractID
 #endif
 #ifdef NECKO_PROTOCOL_wyciwyg
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "wyciwyg", &kNS_WYCIWYGPROTOCOLHANDLER_CID },
 #endif
 #ifdef NECKO_PROTOCOL_websocket
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "ws", &kNS_WEBSOCKETPROTOCOLHANDLER_CID },
     { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "wss", &kNS_WEBSOCKETSSLPROTOCOLHANDLER_CID },
 #endif
+#ifdef MOZ_RTSP
+    { NS_NETWORK_PROTOCOL_CONTRACTID_PREFIX "rtsp", &kNS_RTSPPROTOCOLHANDLER_CID },
+#endif
 #if defined(XP_WIN)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #elif defined(MOZ_WIDGET_COCOA)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #elif defined(MOZ_ENABLE_QTNETWORK)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
 #elif defined(MOZ_WIDGET_ANDROID)
     { NS_NETWORK_LINK_SERVICE_CONTRACTID, &kNS_NETWORK_LINK_SERVICE_CID },
--- a/netwerk/ipc/NeckoChild.cpp
+++ b/netwerk/ipc/NeckoChild.cpp
@@ -11,16 +11,19 @@
 #include "mozilla/net/HttpChannelChild.h"
 #include "mozilla/net/CookieServiceChild.h"
 #include "mozilla/net/WyciwygChannelChild.h"
 #include "mozilla/net/FTPChannelChild.h"
 #include "mozilla/net/WebSocketChannelChild.h"
 #include "mozilla/net/RemoteOpenFileChild.h"
 #include "mozilla/dom/network/TCPSocketChild.h"
 #include "mozilla/dom/network/TCPServerSocketChild.h"
+#ifdef MOZ_RTSP
+#include "mozilla/net/RtspControllerChild.h"
+#endif
 
 using mozilla::dom::TCPSocketChild;
 using mozilla::dom::TCPServerSocketChild;
 
 namespace mozilla {
 namespace net {
 
 PNeckoChild *gNeckoChild = nullptr;
@@ -150,16 +153,33 @@ NeckoChild::AllocPWebSocketChild(PBrowse
 bool
 NeckoChild::DeallocPWebSocketChild(PWebSocketChild* child)
 {
   WebSocketChannelChild* p = static_cast<WebSocketChannelChild*>(child);
   p->ReleaseIPDLReference();
   return true;
 }
 
+PRtspControllerChild*
+NeckoChild::AllocPRtspControllerChild()
+{
+  NS_NOTREACHED("AllocPRtspController should not be called");
+  return nullptr;
+}
+
+bool
+NeckoChild::DeallocPRtspControllerChild(PRtspControllerChild* child)
+{
+#ifdef MOZ_RTSP
+  RtspControllerChild* p = static_cast<RtspControllerChild*>(child);
+  p->ReleaseIPDLReference();
+#endif
+  return true;
+}
+
 PTCPSocketChild*
 NeckoChild::AllocPTCPSocketChild()
 {
   TCPSocketChild* p = new TCPSocketChild();
   p->AddIPDLReference();
   return p;
 }
 
--- a/netwerk/ipc/NeckoChild.h
+++ b/netwerk/ipc/NeckoChild.h
@@ -45,16 +45,18 @@ protected:
   virtual bool DeallocPTCPSocketChild(PTCPSocketChild*);
   virtual PTCPServerSocketChild* AllocPTCPServerSocketChild(const uint16_t& aLocalPort,
                                                        const uint16_t& aBacklog,
                                                        const nsString& aBinaryType);
   virtual bool DeallocPTCPServerSocketChild(PTCPServerSocketChild*);
   virtual PRemoteOpenFileChild* AllocPRemoteOpenFileChild(const URIParams&,
                                                           const OptionalURIParams&);
   virtual bool DeallocPRemoteOpenFileChild(PRemoteOpenFileChild*);
+  virtual PRtspControllerChild* AllocPRtspControllerChild();
+  virtual bool DeallocPRtspControllerChild(PRtspControllerChild*);
 };
 
 /**
  * Reference to the PNecko Child protocol.
  * Null if this is not a content process.
  */
 extern PNeckoChild *gNeckoChild;
 
--- a/netwerk/ipc/NeckoParent.cpp
+++ b/netwerk/ipc/NeckoParent.cpp
@@ -7,16 +7,19 @@
 
 #include "nsHttp.h"
 #include "mozilla/net/NeckoParent.h"
 #include "mozilla/net/HttpChannelParent.h"
 #include "mozilla/net/CookieServiceParent.h"
 #include "mozilla/net/WyciwygChannelParent.h"
 #include "mozilla/net/FTPChannelParent.h"
 #include "mozilla/net/WebSocketChannelParent.h"
+#ifdef MOZ_RTSP
+#include "mozilla/net/RtspControllerParent.h"
+#endif
 #include "mozilla/net/RemoteOpenFileParent.h"
 #include "mozilla/dom/ContentParent.h"
 #include "mozilla/dom/TabParent.h"
 #include "mozilla/dom/network/TCPSocketParent.h"
 #include "mozilla/dom/network/TCPServerSocketParent.h"
 #include "mozilla/ipc/URIUtils.h"
 #include "mozilla/LoadContext.h"
 #include "mozilla/AppProcessChecker.h"
@@ -298,16 +301,38 @@ NeckoParent::AllocPWebSocketParent(PBrow
 bool
 NeckoParent::DeallocPWebSocketParent(PWebSocketParent* actor)
 {
   WebSocketChannelParent* p = static_cast<WebSocketChannelParent*>(actor);
   p->Release();
   return true;
 }
 
+PRtspControllerParent*
+NeckoParent::AllocPRtspControllerParent()
+{
+#ifdef MOZ_RTSP
+  RtspControllerParent* p = new RtspControllerParent();
+  p->AddRef();
+  return p;
+#else
+  return nullptr;
+#endif
+}
+
+bool
+NeckoParent::DeallocPRtspControllerParent(PRtspControllerParent* actor)
+{
+#ifdef MOZ_RTSP
+  RtspControllerParent* p = static_cast<RtspControllerParent*>(actor);
+  p->Release();
+#endif
+  return true;
+}
+
 PTCPSocketParent*
 NeckoParent::AllocPTCPSocketParent()
 {
   TCPSocketParent* p = new TCPSocketParent();
   p->AddIPDLReference();
   return p;
 }
 
--- a/netwerk/ipc/NeckoParent.h
+++ b/netwerk/ipc/NeckoParent.h
@@ -120,16 +120,18 @@ protected:
                                    const uint16_t& flags);
   virtual bool RecvCancelHTMLDNSPrefetch(const nsString& hostname,
                                          const uint16_t& flags,
                                          const nsresult& reason);
 
   virtual mozilla::ipc::IProtocol*
   CloneProtocol(Channel* aChannel,
                 mozilla::ipc::ProtocolCloneContext* aCtx) MOZ_OVERRIDE;
+  virtual PRtspControllerParent* AllocPRtspControllerParent();
+  virtual bool DeallocPRtspControllerParent(PRtspControllerParent*);
 
 private:
   nsCString mCoreAppsBasePath;
   nsCString mWebAppsBasePath;
 };
 
 } // namespace net
 } // namespace mozilla
--- a/netwerk/ipc/PNecko.ipdl
+++ b/netwerk/ipc/PNecko.ipdl
@@ -12,16 +12,17 @@ include protocol PBrowser;
 include protocol PWyciwygChannel;
 include protocol PFTPChannel;
 include protocol PWebSocket;
 include protocol PTCPSocket;
 include protocol PTCPServerSocket;
 include protocol PRemoteOpenFile;
 include protocol PBlob; //FIXME: bug #792908
 
+include protocol PRtspController;
 include URIParams;
 include InputStreamParams;
 include NeckoChannelParams;
 
 include "SerializedLoadContext.h";
 
 using IPC::SerializedLoadContext;
 
@@ -35,16 +36,17 @@ sync protocol PNecko
   manages PHttpChannel;
   manages PCookieService;
   manages PWyciwygChannel;
   manages PFTPChannel;
   manages PWebSocket;
   manages PTCPSocket;
   manages PTCPServerSocket;
   manages PRemoteOpenFile;
+  manages PRtspController;
 
 parent:
   __delete__();
 
   PCookieService();
   PHttpChannel(nullable PBrowser browser,
                SerializedLoadContext loadContext,
                HttpChannelCreationArgs args);
@@ -53,16 +55,17 @@ parent:
               FTPChannelCreationArgs args);
 
   PWebSocket(PBrowser browser, SerializedLoadContext loadContext);
   PTCPServerSocket(uint16_t localPort, uint16_t backlog, nsString binaryType);
   PRemoteOpenFile(URIParams fileuri, OptionalURIParams appuri);
 
   HTMLDNSPrefetch(nsString hostname, uint16_t flags);
   CancelHTMLDNSPrefetch(nsString hostname, uint16_t flags, nsresult reason);
+  PRtspController();
 
 both:
   PTCPSocket();
 };
 
 
 } // namespace net
 } // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/ipc/PRtspController.ipdl
@@ -0,0 +1,63 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 ft=cpp : */
+
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+include protocol PNecko;
+include URIParams;
+
+namespace mozilla {
+namespace net {
+
+/**
+ * Defined meta data format.
+ */
+union RtspMetaValue
+{
+  bool;
+  uint8_t;
+  uint32_t;
+  uint64_t;
+  nsCString;
+};
+
+/**
+ * Key-value pair.
+ */
+struct RtspMetadataParam
+{
+  nsCString name;
+  RtspMetaValue value;
+};
+
+async protocol PRtspController
+{
+  manager PNecko;
+
+parent:
+  AsyncOpen(URIParams aURI);
+  Play();
+  Pause();
+  Resume();
+  Suspend();
+  Seek(uint64_t offset);
+  Stop();
+  __delete__();
+
+child:
+  OnMediaDataAvailable(uint8_t index,
+                       nsCString data,
+                       uint32_t length,
+                       uint32_t offset,
+                       RtspMetadataParam[] meta);
+  OnConnected(uint8_t index,
+              RtspMetadataParam[] meta);
+  OnDisconnected(uint8_t index,
+                 uint32_t reason);
+  AsyncOpenFailed(uint8_t reason);
+};
+
+} //namespace net
+} //namespace mozilla
--- a/netwerk/ipc/moz.build
+++ b/netwerk/ipc/moz.build
@@ -30,16 +30,17 @@ CPP_SOURCES += [
     'RemoteOpenFileChild.cpp',
     'RemoteOpenFileParent.cpp',
 ]
 
 IPDL_SOURCES = [
     'NeckoChannelParams.ipdlh',
     'PNecko.ipdl',
     'PRemoteOpenFile.ipdl',
+    'PRtspController.ipdl',
 ]
 
 FAIL_ON_WARNINGS = True
 
 LIBXUL_LIBRARY = True
 
 LIBRARY_NAME = 'neckoipc_s'
 
--- a/netwerk/protocol/moz.build
+++ b/netwerk/protocol/moz.build
@@ -12,8 +12,14 @@ PARALLEL_DIRS += [
     'file',
     'ftp',
     'http',
     'res',
     'viewsource',
     'websocket',
     'wyciwyg',
 ]
+
+if CONFIG['MOZ_RTSP']:
+    PARALLEL_DIRS += [
+        'rtsp',
+    ]
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/Makefile.in
@@ -0,0 +1,25 @@
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+DEPTH     = @DEPTH@
+topsrcdir = @top_srcdir@
+srcdir    = @srcdir@
+
+include $(DEPTH)/config/autoconf.mk
+
+LOCAL_INCLUDES = \
+  -I$(srcdir)/../../base/src \
+  -I$(topsrcdir)/content/base/src \
+  -I$(topsrcdir)/content/events/src \
+  -I$(topsrcdir)/xpcom/ds \
+  -I$(srcdir) \
+  -I$(srcdir)/rtsp \
+  -I$(srcdir)/controller \
+  -I$(ANDROID_SOURCE)/frameworks/base/media/libstagefright/mpeg2ts \
+  $(NULL)
+
+include $(topsrcdir)/config/rules.mk
+include $(topsrcdir)/ipc/chromium/chromium-config.mk
+
+DEFINES += -DIMPL_NS_NET -Wno-multichar -DFORCE_PR_LOG
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/RtspChannel.cpp
@@ -0,0 +1,62 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspChannel.h"
+#include "nsIURI.h"
+#include "nsAutoPtr.h"
+#include "nsStandardURL.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS_INHERITED1(RtspChannel,
+                             nsBaseChannel,
+                             nsIChannel)
+
+//-----------------------------------------------------------------------------
+// RtspChannel::nsIChannel
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+RtspChannel::AsyncOpen(nsIStreamListener *aListener, nsISupports *aContext)
+{
+  MOZ_ASSERT(aListener);
+
+  nsCOMPtr<nsIURI> uri = nsBaseChannel::URI();
+  NS_ENSURE_TRUE(uri, NS_ERROR_ILLEGAL_VALUE);
+
+  nsAutoCString uriSpec;
+  uri->GetSpec(uriSpec);
+
+  mListener = aListener;
+  mListenerContext = aContext;
+
+  // Call OnStartRequest directly. mListener is expected to create/load an
+  // RtspMediaResource which will create an RtspMediaController. This controller
+  // manages the control and data streams to and from the network.
+  mListener->OnStartRequest(this, aContext);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspChannel::GetContentType(nsACString& aContentType)
+{
+  aContentType.AssignLiteral("RTSP");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspChannel::Init(nsIURI* aUri)
+{
+  MOZ_ASSERT(aUri);
+
+  nsBaseChannel::Init();
+  nsBaseChannel::SetURI(aUri);
+  return NS_OK;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/RtspChannel.h
@@ -0,0 +1,76 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RtspChannel_h
+#define RtspChannel_h
+
+#include "nsBaseChannel.h"
+
+namespace mozilla {
+namespace net {
+
+//-----------------------------------------------------------------------------
+// RtspChannel is a dummy channel used to aid MediaResource creation in
+// HTMLMediaElement. Actual network control and data flows are managed by an
+// RtspController object created and owned by RtspMediaResource.
+// Therefore, when RtspChannel::AsyncOpen is called, mListener->OnStartRequest
+// will be called immediately. It is expected that an RtspMediaResource object
+// will be created in that calling context or after; the RtspController object
+// will be created internally by RtspMediaResource."
+
+class RtspChannel : public nsBaseChannel
+                  , public nsIStreamListener
+{
+public:
+  NS_DECL_ISUPPORTS
+
+  RtspChannel() { }
+
+  ~RtspChannel() { }
+
+  // Overrides nsBaseChannel::AsyncOpen and call listener's OnStartRequest immediately.
+  NS_IMETHOD AsyncOpen(nsIStreamListener *listener,
+                       nsISupports *aContext) MOZ_OVERRIDE MOZ_FINAL;
+  // Set Rtsp URL.
+  NS_IMETHOD Init(nsIURI* uri);
+  // Overrides nsBaseChannel::GetContentType, return streaming protocol type "RTSP".
+  NS_IMETHOD GetContentType(nsACString & aContentType) MOZ_OVERRIDE MOZ_FINAL;
+
+  NS_IMETHOD OpenContentStream(bool aAsync,
+                               nsIInputStream **aStream,
+                               nsIChannel **aChannel) MOZ_OVERRIDE MOZ_FINAL
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // nsIRequestObserver
+  NS_IMETHOD OnStartRequest(nsIRequest *aRequest,
+                            nsISupports *aContext) MOZ_OVERRIDE MOZ_FINAL
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  NS_IMETHOD OnStopRequest(nsIRequest *aRequest,
+                           nsISupports *aContext,
+                           nsresult aStatusCode) MOZ_OVERRIDE MOZ_FINAL
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+
+  // nsIStreamListener
+  NS_IMETHOD OnDataAvailable(nsIRequest *aRequest,
+                             nsISupports *aContext,
+                             nsIInputStream *aInputStream,
+                             uint64_t aOffset,
+                             uint32_t aCount) MOZ_OVERRIDE MOZ_FINAL
+  {
+    return NS_ERROR_NOT_IMPLEMENTED;
+  }
+};
+
+}
+} // namespace mozilla::net
+#endif // RtspChannel_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/RtspHandler.cpp
@@ -0,0 +1,95 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspChannel.h"
+#include "RtspHandler.h"
+#include "nsILoadGroup.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsIURI.h"
+#include "nsAutoPtr.h"
+#include "nsStandardURL.h"
+#include "mozilla/net/NeckoChild.h"
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS1(RtspHandler, nsIProtocolHandler)
+
+//-----------------------------------------------------------------------------
+// RtspHandler::nsIProtocolHandler
+//-----------------------------------------------------------------------------
+
+NS_IMETHODIMP
+RtspHandler::GetScheme(nsACString &aScheme)
+{
+  aScheme.AssignLiteral("rtsp");
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspHandler::GetDefaultPort(int32_t *aDefaultPort)
+{
+  *aDefaultPort = kDefaultRtspPort;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspHandler::GetProtocolFlags(uint32_t *aProtocolFlags)
+{
+  *aProtocolFlags = URI_NORELATIVE | URI_NOAUTH | URI_INHERITS_SECURITY_CONTEXT |
+    URI_LOADABLE_BY_ANYONE | URI_NON_PERSISTABLE | URI_SYNC_LOAD_IS_OK;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspHandler::NewURI(const nsACString & aSpec,
+                    const char *aOriginCharset,
+                    nsIURI *aBaseURI, nsIURI **aResult)
+{
+  int32_t port;
+
+  nsresult rv = GetDefaultPort(&port);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  nsRefPtr<nsStandardURL> url = new nsStandardURL();
+  rv = url->Init(nsIStandardURL::URLTYPE_AUTHORITY, port, aSpec,
+                 aOriginCharset, aBaseURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  url.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspHandler::NewChannel(nsIURI *aURI, nsIChannel **aResult)
+{
+  bool isRtsp = false;
+  nsRefPtr<RtspChannel> rtspChannel;
+
+  nsresult rv = aURI->SchemeIs("rtsp", &isRtsp);
+  NS_ENSURE_SUCCESS(rv, rv);
+  NS_ENSURE_TRUE(isRtsp, NS_ERROR_UNEXPECTED);
+
+  rtspChannel = new RtspChannel();
+
+  rv = rtspChannel->Init(aURI);
+  NS_ENSURE_SUCCESS(rv, rv);
+
+  rtspChannel.forget(aResult);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspHandler::AllowPort(int32_t port, const char *scheme, bool *aResult)
+{
+  // Do not override any blacklisted ports.
+  *aResult = false;
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/RtspHandler.h
@@ -0,0 +1,32 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RtspHandler_h
+#define RtspHandler_h
+
+#include "nsIProtocolHandler.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+
+class RtspHandler : public nsIProtocolHandler
+{
+ public:
+  NS_DECL_ISUPPORTS
+  NS_DECL_NSIPROTOCOLHANDLER
+
+  RtspHandler() { }
+  ~RtspHandler() { }
+  const static int32_t kDefaultRtspPort = 554;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // RtspHandler_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspController.cpp
@@ -0,0 +1,371 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspController.h"
+#include "RtspMetaData.h"
+#include "nsIURI.h"
+#include "nsICryptoHash.h"
+#include "nsIRunnable.h"
+#include "nsIPrefBranch.h"
+#include "nsIPrefService.h"
+#include "nsICancelable.h"
+#include "nsIStreamConverterService.h"
+#include "nsIIOService2.h"
+#include "nsIProtocolProxyService.h"
+#include "nsIProxyInfo.h"
+#include "nsIProxiedChannel.h"
+
+#include "nsAutoPtr.h"
+#include "nsStandardURL.h"
+#include "nsNetCID.h"
+#include "nsServiceManagerUtils.h"
+#include "nsXPIDLString.h"
+#include "nsCRT.h"
+#include "nsThreadUtils.h"
+#include "nsError.h"
+#include "nsStringStream.h"
+#include "nsAlgorithm.h"
+#include "nsProxyRelease.h"
+#include "nsNetUtil.h"
+#include "mozilla/Attributes.h"
+#include "TimeStamp.h"
+#include "mozilla/Telemetry.h"
+#include "prlog.h"
+
+#include "plbase64.h"
+#include "prmem.h"
+#include "prnetdb.h"
+#include "prbit.h"
+#include "zlib.h"
+#include <algorithm>
+#include "nsDebug.h"
+
+extern PRLogModuleInfo* gRtspLog;
+#undef LOG
+#define LOG(args) PR_LOG(gRtspLog, PR_LOG_DEBUG, args)
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS1(RtspController,
+                   nsIStreamingProtocolController)
+
+RtspController::RtspController(nsIChannel *channel)
+  : mState(INIT)
+{
+  LOG(("RtspController::RtspController()"));
+}
+
+RtspController::~RtspController()
+{
+  LOG(("RtspController::~RtspController()"));
+  if (mRtspSource.get()) {
+    mRtspSource.clear();
+  }
+}
+
+NS_IMETHODIMP
+RtspController::GetTrackMetaData(uint8_t index,
+                                 nsIStreamingProtocolMetaData * *_retval)
+{
+  LOG(("RtspController::GetTrackMetaData()"));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::Play(void)
+{
+  LOG(("RtspController::Play()"));
+  if (!mRtspSource.get()) {
+    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (mState != CONNECTED) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mRtspSource->play();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::Pause(void)
+{
+  LOG(("RtspController::Pause()"));
+  if (!mRtspSource.get()) {
+    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (mState != CONNECTED) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mRtspSource->pause();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::Resume(void)
+{
+  LOG(("RtspController::Resume()"));
+  if (!mRtspSource.get()) {
+    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (mState != CONNECTED) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mRtspSource->play();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::Suspend(void)
+{
+  LOG(("RtspController::Suspend()"));
+  if (!mRtspSource.get()) {
+    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (mState != CONNECTED) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mRtspSource->pause();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::Seek(uint64_t seekTimeUs)
+{
+  LOG(("RtspController::Seek() %llu", seekTimeUs));
+  if (!mRtspSource.get()) {
+    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  if (mState != CONNECTED) {
+    return NS_ERROR_UNEXPECTED;
+  }
+
+  mRtspSource->seek(seekTimeUs);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::Stop()
+{
+  LOG(("RtspController::Stop()"));
+  mState = INIT;
+  if (!mRtspSource.get()) {
+    MOZ_ASSERT(mRtspSource.get(), "mRtspSource should not be null!");
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  mRtspSource->stop();
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspController::GetTotalTracks(uint8_t *aTracks)
+{
+  LOG(("RtspController::GetTotalTracks()"));
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RtspController::AsyncOpen(nsIStreamingProtocolListener *aListener)
+{
+  if (!aListener) {
+    LOG(("RtspController::AsyncOpen() illegal listener"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  mListener = aListener;
+
+  if (!mURI) {
+    LOG(("RtspController::AsyncOpen() illegal URI"));
+    return NS_ERROR_ILLEGAL_VALUE;
+  }
+
+  nsAutoCString uriSpec;
+  mURI->GetSpec(uriSpec);
+  LOG(("RtspController AsyncOpen uri=%s", uriSpec.get()));
+
+  if (!mRtspSource.get()) {
+    mRtspSource = new android::RTSPSource(this, uriSpec.get(), false, 0);
+  }
+  // Connect to Rtsp Server.
+  mRtspSource->start();
+
+  return NS_OK;
+}
+
+class SendMediaDataTask : public nsRunnable
+{
+public:
+  SendMediaDataTask(nsIStreamingProtocolListener *listener,
+                    uint8_t index,
+                    const nsACString & data,
+                    uint32_t length,
+                    uint32_t offset,
+                    nsIStreamingProtocolMetaData *meta)
+    : mIndex(index)
+    , mLength(length)
+    , mOffset(offset)
+    , mMetaData(meta)
+    , mListener(listener)
+  {
+    mData.Assign(data);
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mListener->OnMediaDataAvailable(mIndex, mData, mLength,
+                                    mOffset, mMetaData);
+    return NS_OK;
+  }
+
+private:
+  uint8_t mIndex;
+  nsCString mData;
+  uint32_t mLength;
+  uint32_t mOffset;
+  nsRefPtr<nsIStreamingProtocolMetaData> mMetaData;
+  nsCOMPtr<nsIStreamingProtocolListener> mListener;
+};
+
+NS_IMETHODIMP
+RtspController::OnMediaDataAvailable(uint8_t index,
+                                     const nsACString & data,
+                                     uint32_t length,
+                                     uint32_t offset,
+                                     nsIStreamingProtocolMetaData *meta)
+{
+  if (mListener && mState == CONNECTED) {
+    nsRefPtr<SendMediaDataTask> task =
+      new SendMediaDataTask(mListener, index, data, length, offset, meta);
+    return NS_DispatchToMainThread(task);
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+class SendOnConnectedTask : public nsRunnable
+{
+public:
+  SendOnConnectedTask(nsIStreamingProtocolListener *listener,
+                      uint8_t index,
+                      nsIStreamingProtocolMetaData *meta)
+    : mListener(listener)
+    , mIndex(index)
+    , mMetaData(meta)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mListener->OnConnected(mIndex, mMetaData);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIStreamingProtocolListener> mListener;
+  uint8_t mIndex;
+  nsRefPtr<nsIStreamingProtocolMetaData> mMetaData;
+};
+
+
+NS_IMETHODIMP
+RtspController::OnConnected(uint8_t index,
+                            nsIStreamingProtocolMetaData *meta)
+{
+  LOG(("RtspController::OnConnected()"));
+  mState = CONNECTED;
+  if (mListener) {
+    nsRefPtr<SendOnConnectedTask> task =
+      new SendOnConnectedTask(mListener, index, meta);
+    return NS_DispatchToMainThread(task);
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+class SendOnDisconnectedTask : public nsRunnable
+{
+public:
+  SendOnDisconnectedTask(nsIStreamingProtocolListener *listener,
+                         uint8_t index,
+                         uint32_t reason)
+    : mListener(listener)
+    , mIndex(index)
+    , mReason(reason)
+  { }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+    mListener->OnDisconnected(mIndex, mReason);
+    return NS_OK;
+  }
+
+private:
+  nsCOMPtr<nsIStreamingProtocolListener> mListener;
+  uint8_t mIndex;
+  uint32_t mReason;
+};
+
+NS_IMETHODIMP
+RtspController::OnDisconnected(uint8_t index,
+                               uint32_t reason)
+{
+  LOG(("RtspController::OnDisconnected()"));
+  mState = DISCONNECTED;
+  if (mListener) {
+    nsRefPtr<SendOnDisconnectedTask> task =
+      new SendOnDisconnectedTask(mListener, index, reason);
+    return NS_DispatchToMainThread(task);
+  }
+  return NS_ERROR_NOT_AVAILABLE;
+}
+
+NS_IMETHODIMP
+RtspController::Init(nsIURI *aURI)
+{
+  nsresult rv;
+
+  if (!aURI) {
+    LOG(("RtspController::Init() - invalid URI"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  nsAutoCString host;
+  int32_t port = -1;
+
+  rv = aURI->GetAsciiHost(host);
+  if (NS_FAILED(rv)) return rv;
+
+  // Reject the URL if it doesn't specify a host
+  if (host.IsEmpty())
+    return NS_ERROR_MALFORMED_URI;
+
+  rv = aURI->GetPort(&port);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = aURI->GetAsciiSpec(mSpec);
+  if (NS_FAILED(rv)) return rv;
+
+  mURI = aURI;
+
+  return NS_OK;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspController.h
@@ -0,0 +1,52 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RtspController_h
+#define RtspController_h
+
+#include "nsIStreamingProtocolController.h"
+#include "nsIChannel.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "RTSPSource.h"
+
+namespace mozilla {
+namespace net {
+
+class RtspController : public nsIStreamingProtocolController
+                     , public nsIStreamingProtocolListener
+{
+public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSISTREAMINGPROTOCOLCONTROLLER
+  NS_DECL_NSISTREAMINGPROTOCOLLISTENER
+
+  RtspController(nsIChannel *channel);
+  ~RtspController();
+
+private:
+  enum State {
+    INIT,
+    CONNECTED,
+    DISCONNECTED
+  };
+
+  // RTSP URL refer to a stream or an aggregate of streams.
+  nsCOMPtr<nsIURI> mURI;
+  // The nsIStreamingProtocolListener implementation.
+  nsCOMPtr<nsIStreamingProtocolListener> mListener;
+  // ASCII encoded URL spec.
+  nsCString mSpec;
+  // Indicate the connection state between the
+  // media streaming server and the Rtsp client.
+  State mState;
+  // Rtsp Streaming source.
+  android::sp<android::RTSPSource> mRtspSource;
+};
+
+}
+} // namespace mozilla::net
+#endif
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspControllerChild.cpp
@@ -0,0 +1,466 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspControllerChild.h"
+#include "RtspMetaData.h"
+#include "mozilla/dom/TabChild.h"
+#include "mozilla/net/NeckoChild.h"
+#include "nsITabChild.h"
+#include "nsILoadContext.h"
+#include "nsNetUtil.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "nsStringStream.h"
+#include "prlog.h"
+
+PRLogModuleInfo* gRtspChildLog = nullptr;
+#undef LOG
+#define LOG(args) PR_LOG(gRtspChildLog, PR_LOG_DEBUG, args)
+
+const uint32_t kRtspTotalTracks = 2;
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ADDREF(RtspControllerChild)
+
+NS_IMETHODIMP_(nsrefcnt) RtspControllerChild::Release()
+{
+  NS_PRECONDITION(0 != mRefCnt, "dup release");
+  // Enable this to find non-threadsafe destructors:
+  // NS_ASSERT_OWNINGTHREAD(RtspControllerChild);
+  --mRefCnt;
+  NS_LOG_RELEASE(this, mRefCnt, "RtspControllerChild");
+
+  if (mRefCnt == 1 && mIPCOpen) {
+    Send__delete__(this);
+    return mRefCnt;
+  }
+
+  if (mRefCnt == 0) {
+    mRefCnt = 1; /* stabilize */
+    delete this;
+    return 0;
+  }
+  return mRefCnt;
+}
+
+NS_INTERFACE_MAP_BEGIN(RtspControllerChild)
+  NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolController)
+  NS_INTERFACE_MAP_ENTRY(nsIStreamingProtocolListener)
+  NS_INTERFACE_MAP_ENTRY_AMBIGUOUS(nsISupports, nsIStreamingProtocolController)
+NS_INTERFACE_MAP_END
+
+RtspControllerChild::RtspControllerChild(nsIChannel *channel)
+  : mIPCOpen(false)
+  , mChannel(channel)
+  , mTotalTracks(0)
+  , mSuspendCount(0)
+{
+#if defined(PR_LOGGING)
+  if (!gRtspChildLog)
+    gRtspChildLog = PR_NewLogModule("nsRtspChild");
+#endif
+  AddIPDLReference();
+  gNeckoChild->SendPRtspControllerConstructor(this);
+}
+
+RtspControllerChild::~RtspControllerChild()
+{
+  LOG(("RtspControllerChild::~RtspControllerChild()"));
+}
+
+bool
+RtspControllerChild::RecvOnMediaDataAvailable(
+                       const uint8_t& index,
+                       const nsCString& data,
+                       const uint32_t& length,
+                       const uint32_t& offset,
+                       const InfallibleTArray<RtspMetadataParam>& metaArray)
+{
+  nsRefPtr<RtspMetaData> meta = new RtspMetaData();
+  nsresult rv = meta->DeserializeRtspMetaData(metaArray);
+  NS_ENSURE_SUCCESS(rv, false);
+
+  if (mListener) {
+    mListener->OnMediaDataAvailable(index, data, length, offset, meta.get());
+  }
+  return true;
+}
+
+void
+RtspControllerChild::AddMetaData(
+                       already_AddRefed<nsIStreamingProtocolMetaData> meta)
+{
+  mMetaArray.AppendElement(meta);
+}
+
+int
+RtspControllerChild::GetMetaDataLength()
+{
+  return mMetaArray.Length();
+}
+
+bool
+RtspControllerChild::RecvOnConnected(
+                       const uint8_t& index,
+                       const InfallibleTArray<RtspMetadataParam>& metaArray)
+{
+  uint32_t tracks;
+
+  // Deserialize meta data.
+  nsRefPtr<RtspMetaData> meta = new RtspMetaData();
+  nsresult rv = meta->DeserializeRtspMetaData(metaArray);
+  NS_ENSURE_SUCCESS(rv, false);
+  meta->GetTotalTracks(&tracks);
+  if (tracks <= 0) {
+    LOG(("RtspControllerChild::RecvOnConnected invalid tracks %d", tracks));
+    // Set the default value.
+    tracks = kRtspTotalTracks;
+  }
+  mTotalTracks = tracks;
+  AddMetaData(meta.forget());
+
+  // Notify the listener when meta data of tracks are available.
+  if ((index + 1) == tracks) {
+    // The controller provide |GetTrackMetaData| method for his client.
+    if (mListener) {
+      mListener->OnConnected(index, nullptr);
+    }
+  }
+  return true;
+}
+
+bool
+RtspControllerChild::RecvOnDisconnected(
+                       const uint8_t& index,
+                       const uint32_t& reason)
+{
+  LOG(("RtspControllerChild::RecvOnDisconnected %d %d", index, reason));
+  if (mListener) {
+    mListener->OnDisconnected(index, reason);
+  }
+  return true;
+}
+
+bool
+RtspControllerChild::RecvAsyncOpenFailed(const uint8_t& reason)
+{
+  LOG(("RtspControllerChild::RecvAsyncOpenFailed %d", reason));
+  if (mListener) {
+    mListener->OnDisconnected(0, NS_ERROR_CONNECTION_REFUSED);
+  }
+  return true;
+}
+
+void
+RtspControllerChild::AddIPDLReference()
+{
+  NS_ABORT_IF_FALSE(!mIPCOpen,
+                    "Attempt to retain more than one IPDL reference");
+  mIPCOpen = true;
+  AddRef();
+}
+
+void
+RtspControllerChild::ReleaseIPDLReference()
+{
+  NS_ABORT_IF_FALSE(mIPCOpen, "Attempt to release nonexistent IPDL reference");
+  mIPCOpen = false;
+  Release();
+}
+
+NS_IMETHODIMP
+RtspControllerChild::GetTrackMetaData(
+                       uint8_t index,
+                       nsIStreamingProtocolMetaData **result)
+{
+  if (GetMetaDataLength() <= 0 || index >= GetMetaDataLength()) {
+    LOG(("RtspControllerChild:: meta data is not available"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  LOG(("RtspControllerChild::GetTrackMetaData() %d", index));
+  NS_IF_ADDREF(*result = mMetaArray[index]);
+  return NS_OK;
+}
+
+enum IPCEvent
+{
+  SendNoneEvent = 0,
+  SendPlayEvent,
+  SendPauseEvent,
+  SendSeekEvent,
+  SendResumeEvent,
+  SendSuspendEvent,
+  SendStopEvent
+};
+
+class SendIPCEvent : public nsRunnable
+{
+public:
+  SendIPCEvent(RtspControllerChild *aController, IPCEvent aEvent)
+    : mController(aController)
+    , mEvent(aEvent)
+    , mSeekTime(0)
+  {
+  }
+
+  SendIPCEvent(RtspControllerChild *aController,
+               IPCEvent aEvent,
+               uint64_t aSeekTime)
+    : mController(aController)
+    , mEvent(aEvent)
+    , mSeekTime(aSeekTime)
+  {
+  }
+
+  NS_IMETHOD Run()
+  {
+    MOZ_ASSERT(NS_IsMainThread());
+
+    if (mEvent == SendPlayEvent) {
+      mController->SendPlay();
+    } else if (mEvent == SendPauseEvent) {
+      mController->SendPause();
+    } else if (mEvent == SendSeekEvent) {
+      mController->SendSeek(mSeekTime);
+    } else if (mEvent == SendResumeEvent) {
+      mController->SendResume();
+    } else if (mEvent == SendSuspendEvent) {
+      mController->SendSuspend();
+    } else if (mEvent == SendStopEvent) {
+      mController->SendStop();
+    } else {
+      LOG(("RtspControllerChild::SendIPCEvent"));
+    }
+    return NS_OK;
+  }
+private:
+  nsRefPtr<RtspControllerChild> mController;
+  IPCEvent mEvent;
+  uint64_t mSeekTime;
+};
+
+NS_IMETHODIMP
+RtspControllerChild::Play(void)
+{
+  LOG(("RtspControllerChild::Play()"));
+  NS_ENSURE_SUCCESS(mIPCOpen, NS_ERROR_FAILURE);
+
+  if (NS_IsMainThread()) {
+    if (!SendPlay())
+      return NS_ERROR_FAILURE;
+  } else {
+    nsresult rv = NS_DispatchToMainThread(
+                    new SendIPCEvent(this, SendPlayEvent));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::Pause(void)
+{
+  LOG(("RtspControllerChild::Pause()"));
+  NS_ENSURE_SUCCESS(mIPCOpen, NS_ERROR_FAILURE);
+
+  if (NS_IsMainThread()) {
+    if (!SendPause())
+      return NS_ERROR_FAILURE;
+  } else {
+    nsresult rv = NS_DispatchToMainThread(
+                    new SendIPCEvent(this, SendPauseEvent));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::Resume(void)
+{
+  LOG(("RtspControllerChild::Resume()"));
+  NS_ENSURE_SUCCESS(mIPCOpen, NS_ERROR_FAILURE);
+  NS_ENSURE_TRUE(mSuspendCount > 0, NS_ERROR_UNEXPECTED);
+
+  if (!--mSuspendCount) {
+    if (NS_IsMainThread()) {
+      if (!SendResume())
+        return NS_ERROR_FAILURE;
+    } else {
+      nsresult rv = NS_DispatchToMainThread(
+                      new SendIPCEvent(this, SendResumeEvent));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::Suspend(void)
+{
+  LOG(("RtspControllerChild::Suspend()"));
+  NS_ENSURE_SUCCESS(mIPCOpen, NS_ERROR_FAILURE);
+
+  if (!mSuspendCount++) {
+    if (NS_IsMainThread()) {
+      if (!SendSuspend())
+        return NS_ERROR_FAILURE;
+    } else {
+      nsresult rv = NS_DispatchToMainThread(
+                      new SendIPCEvent(this, SendSuspendEvent));
+      NS_ENSURE_SUCCESS(rv, rv);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::Seek(uint64_t seekTimeUs)
+{
+  LOG(("RtspControllerChild::Seek() %llu", seekTimeUs));
+  NS_ENSURE_SUCCESS(mIPCOpen, NS_ERROR_FAILURE);
+
+  if (NS_IsMainThread()) {
+    if (!SendSeek(seekTimeUs))
+      return NS_ERROR_FAILURE;
+  } else {
+    nsresult rv = NS_DispatchToMainThread(
+                    new SendIPCEvent(this, SendSeekEvent, seekTimeUs));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::Stop()
+{
+  LOG(("RtspControllerChild::Stop()"));
+  NS_ENSURE_SUCCESS(mIPCOpen, NS_ERROR_FAILURE);
+
+  if (NS_IsMainThread()) {
+    if (!SendStop())
+      return NS_ERROR_FAILURE;
+  } else {
+    nsresult rv = NS_DispatchToMainThread(
+                    new SendIPCEvent(this, SendStopEvent));
+    NS_ENSURE_SUCCESS(rv, rv);
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::GetTotalTracks(uint8_t *aTracks)
+{
+  NS_ENSURE_ARG_POINTER(aTracks);
+  *aTracks = kRtspTotalTracks;
+  if (mTotalTracks) {
+    *aTracks = mTotalTracks;
+  }
+  LOG(("RtspControllerChild::GetTracks() %d", *aTracks));
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::OnMediaDataAvailable(uint8_t index,
+                                          const nsACString & data,
+                                          uint32_t length,
+                                          uint32_t offset,
+                                          nsIStreamingProtocolMetaData *meta)
+{
+  LOG(("RtspControllerChild::OnMediaDataAvailable()"));
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::OnConnected(uint8_t index,
+                                 nsIStreamingProtocolMetaData *meta)
+
+{
+  LOG(("RtspControllerChild::OnConnected()"));
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::OnDisconnected(uint8_t index,
+                                    uint32_t reason)
+{
+  LOG(("RtspControllerChild::OnDisconnected()"));
+  return NS_ERROR_NOT_IMPLEMENTED;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::Init(nsIURI *aURI)
+{
+  nsresult rv;
+
+  if (!aURI) {
+    LOG(("RtspControllerChild::Init() - invalid URI"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  nsAutoCString host;
+  int32_t port = -1;
+
+  rv = aURI->GetAsciiHost(host);
+  if (NS_FAILED(rv)) return rv;
+
+  // Reject the URL if it doesn't specify a host
+  if (host.IsEmpty())
+    return NS_ERROR_MALFORMED_URI;
+
+  rv = aURI->GetPort(&port);
+  if (NS_FAILED(rv)) return rv;
+
+  rv = aURI->GetAsciiSpec(mSpec);
+  if (NS_FAILED(rv)) return rv;
+
+  if (!strncmp(mSpec.get(), "rtsp:", 5) == 0)
+    return NS_ERROR_UNEXPECTED;
+
+  mURI = aURI;
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerChild::AsyncOpen(nsIStreamingProtocolListener *aListener)
+{
+  LOG(("RtspControllerChild::AsyncOpen()"));
+  if (!aListener) {
+    LOG(("RtspControllerChild::AsyncOpen() - invalid listener"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  mListener = aListener;
+
+  if (!mChannel) {
+    LOG(("RtspControllerChild::AsyncOpen() - invalid URI"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+
+  nsCOMPtr<nsIURI> uri;
+  URIParams uriParams;
+  mChannel->GetURI(getter_AddRefs(uri));
+  if (!uri) {
+    LOG(("RtspControllerChild::AsyncOpen() - invalid URI"));
+    return NS_ERROR_NOT_INITIALIZED;
+  }
+  SerializeURI(uri, uriParams);
+
+  if (!mIPCOpen || !SendAsyncOpen(uriParams)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspControllerChild.h
@@ -0,0 +1,71 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RtspControllerChild_h
+#define RtspControllerChild_h
+
+#include "mozilla/net/PRtspControllerChild.h"
+#include "nsIStreamingProtocolController.h"
+#include "nsIChannel.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsTArray.h"
+
+namespace mozilla {
+namespace net {
+
+class RtspControllerChild : public nsIStreamingProtocolController
+                          , public nsIStreamingProtocolListener
+                          , public PRtspControllerChild
+{
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSISTREAMINGPROTOCOLCONTROLLER
+  NS_DECL_NSISTREAMINGPROTOCOLLISTENER
+
+  RtspControllerChild(nsIChannel *channel);
+  ~RtspControllerChild();
+
+  bool RecvOnConnected(const uint8_t& index,
+                       const InfallibleTArray<RtspMetadataParam>& meta);
+
+  bool RecvOnMediaDataAvailable(
+         const uint8_t& index,
+         const nsCString& data,
+         const uint32_t& length,
+         const uint32_t& offset,
+         const InfallibleTArray<RtspMetadataParam>& meta);
+
+  bool RecvOnDisconnected(const uint8_t& index,
+                          const uint32_t& reason);
+
+  bool RecvAsyncOpenFailed(const uint8_t& reason);
+  void AddIPDLReference();
+  void ReleaseIPDLReference();
+  void AddMetaData(already_AddRefed<nsIStreamingProtocolMetaData> meta);
+  int  GetMetaDataLength();
+
+ private:
+  bool mIPCOpen;
+  // Dummy channel used to aid MediaResource creation in HTMLMediaElement.
+  nsCOMPtr<nsIChannel> mChannel;
+  // The nsIStreamingProtocolListener implementation.
+  nsCOMPtr<nsIStreamingProtocolListener> mListener;
+  // RTSP URL refer to a stream or an aggregate of streams.
+  nsCOMPtr<nsIURI> mURI;
+  // Array refer to metadata of the media stream.
+  nsTArray<nsCOMPtr<nsIStreamingProtocolMetaData>> mMetaArray;
+  // ASCII encoded URL spec
+  nsCString mSpec;
+  // The total tracks for the given media stream session.
+  uint32_t mTotalTracks;
+  // Current suspension depth for this channel object
+  uint32_t mSuspendCount;
+};
+} // namespace net
+} // namespace mozilla
+
+#endif // RtspControllerChild_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspControllerParent.cpp
@@ -0,0 +1,264 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspControllerParent.h"
+#include "RtspController.h"
+#include "nsIAuthPromptProvider.h"
+#include "mozilla/ipc/InputStreamUtils.h"
+#include "mozilla/ipc/URIUtils.h"
+#include "nsNetUtil.h"
+#include "prlog.h"
+
+#include <sys/types.h>
+#include <sys/socket.h>
+
+PRLogModuleInfo* gRtspLog;
+#undef LOG
+#define LOG(args) PR_LOG(gRtspLog, PR_LOG_DEBUG, args)
+
+using namespace mozilla::ipc;
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS2(RtspControllerParent,
+                   nsIInterfaceRequestor,
+                   nsIStreamingProtocolListener)
+
+RtspControllerParent::RtspControllerParent()
+  : mIPCOpen(true)
+{
+#if defined(PR_LOGGING)
+  if (!gRtspLog)
+    gRtspLog = PR_NewLogModule("nsRtsp");
+#endif
+}
+
+RtspControllerParent::~RtspControllerParent()
+{
+}
+
+void
+RtspControllerParent::ActorDestroy(ActorDestroyReason why)
+{
+  LOG(("RtspControllerParent::ActorDestroy()"));
+  mIPCOpen = false;
+
+  NS_ENSURE_TRUE_VOID(mController);
+  mController->Stop();
+  mController = nullptr;
+}
+
+bool
+RtspControllerParent::RecvAsyncOpen(const URIParams& aURI)
+{
+  LOG(("RtspControllerParent::RecvAsyncOpen()"));
+
+  mURI = DeserializeURI(aURI);
+
+  mController = new RtspController(nullptr);
+  mController->Init(mURI);
+  nsresult rv = mController->AsyncOpen(this);
+  if (NS_SUCCEEDED(rv)) return true;
+
+  mController = nullptr;
+  return SendAsyncOpenFailed(rv);
+}
+
+bool
+RtspControllerParent::RecvPlay()
+{
+  LOG(("RtspControllerParent::RecvPlay()"));
+  NS_ENSURE_TRUE(mController, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mController->Play();
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+bool
+RtspControllerParent::RecvPause()
+{
+  LOG(("RtspControllerParent::RecvPause()"));
+  NS_ENSURE_TRUE(mController, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mController->Pause();
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+bool
+RtspControllerParent::RecvResume()
+{
+  LOG(("RtspControllerParent::RecvResume()"));
+  NS_ENSURE_TRUE(mController, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mController->Resume();
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+bool
+RtspControllerParent::RecvSuspend()
+{
+  LOG(("RtspControllerParent::RecvSuspend()"));
+  NS_ENSURE_TRUE(mController, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mController->Suspend();
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+bool
+RtspControllerParent::RecvSeek(const uint64_t& offset)
+{
+  LOG(("RtspControllerParent::RecvSeek()"));
+  NS_ENSURE_TRUE(mController, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mController->Seek(offset);
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+bool
+RtspControllerParent::RecvStop()
+{
+  LOG(("RtspControllerParent::RecvStop()"));
+  NS_ENSURE_TRUE(mController, NS_ERROR_NOT_INITIALIZED);
+
+  nsresult rv = mController->Stop();
+  NS_ENSURE_SUCCESS(rv, false);
+  return true;
+}
+
+NS_IMETHODIMP
+RtspControllerParent::OnMediaDataAvailable(uint8_t index,
+                                           const nsACString & data,
+                                           uint32_t length,
+                                           uint32_t offset,
+                                           nsIStreamingProtocolMetaData *meta)
+{
+  NS_ENSURE_ARG_POINTER(meta);
+  uint32_t int32Value;
+  uint64_t int64Value;
+
+  LOG(("RtspControllerParent:: OnMediaDataAvailable %d:%d time %lld",
+       index, length, int64Value));
+
+  // Serialize meta data.
+  nsCString name;
+  name.AssignLiteral("TIMESTAMP");
+  nsresult rv = meta->GetTimeStamp(&int64Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  InfallibleTArray<RtspMetadataParam> metaData;
+  metaData.AppendElement(RtspMetadataParam(name, int64Value));
+
+  name.AssignLiteral("FRAMETYPE");
+  rv = meta->GetFrameType(&int32Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int32Value));
+
+  nsCString stream;
+  stream.Assign(data);
+  if (!mIPCOpen ||
+      !SendOnMediaDataAvailable(index, stream, length, offset, metaData)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerParent::OnConnected(uint8_t index,
+                                  nsIStreamingProtocolMetaData *meta)
+{
+  NS_ENSURE_ARG_POINTER(meta);
+  uint32_t int32Value;
+  uint64_t int64Value;
+
+  LOG(("RtspControllerParent:: OnConnected"));
+  // Serialize meta data.
+  InfallibleTArray<RtspMetadataParam> metaData;
+  nsCString name;
+  name.AssignLiteral("TRACKS");
+  nsresult rv = meta->GetTotalTracks(&int32Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int32Value));
+
+  name.AssignLiteral("MIMETYPE");
+  nsCString mimeType;
+  rv = meta->GetMimeType(mimeType);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, mimeType));
+
+  name.AssignLiteral("WIDTH");
+  rv = meta->GetWidth(&int32Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int32Value));
+
+  name.AssignLiteral("HEIGHT");
+  rv = meta->GetHeight(&int32Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int32Value));
+
+  name.AssignLiteral("DURATION");
+  rv = meta->GetDuration(&int64Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int64Value));
+
+  name.AssignLiteral("SAMPLERATE");
+  rv = meta->GetSampleRate(&int32Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int32Value));
+
+  name.AssignLiteral("TIMESTAMP");
+  rv = meta->GetTimeStamp(&int64Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int64Value));
+
+  name.AssignLiteral("CHANNELCOUNT");
+  rv = meta->GetChannelCount(&int32Value);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  metaData.AppendElement(RtspMetadataParam(name, int32Value));
+
+  nsCString esds;
+  rv = meta->GetEsdsData(esds);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  name.AssignLiteral("ESDS");
+  metaData.AppendElement(RtspMetadataParam(name, esds));
+
+  nsCString avcc;
+  rv = meta->GetAvccData(avcc);
+  NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+  name.AssignLiteral("AVCC");
+  metaData.AppendElement(RtspMetadataParam(name, avcc));
+
+  if (!mIPCOpen || !SendOnConnected(index, metaData)) {
+    return NS_ERROR_FAILURE;
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerParent::OnDisconnected(uint8_t index,
+                                     uint32_t reason)
+{
+  LOG(("RtspControllerParent::OnDisconnected()"));
+  if (!mIPCOpen || !SendOnDisconnected(index, reason)) {
+    return NS_ERROR_FAILURE;
+  }
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspControllerParent::GetInterface(const nsIID & iid, void **result)
+{
+  LOG(("RtspControllerParent::GetInterface()"));
+  return QueryInterface(iid, result);
+}
+
+} // namespace net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspControllerParent.h
@@ -0,0 +1,55 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RtspControllerParent_h
+#define RtspControllerParent_h
+
+#include "mozilla/net/PRtspControllerParent.h"
+#include "mozilla/net/NeckoParent.h"
+#include "nsIStreamingProtocolController.h"
+#include "nsIInterfaceRequestor.h"
+#include "nsILoadContext.h"
+#include "nsIURI.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+class nsIAuthPromptProvider;
+
+namespace mozilla {
+namespace net {
+
+class RtspControllerParent : public PRtspControllerParent
+                           , public nsIInterfaceRequestor
+                           , public nsIStreamingProtocolListener
+{
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSIINTERFACEREQUESTOR
+  NS_DECL_NSISTREAMINGPROTOCOLLISTENER
+
+  RtspControllerParent();
+  ~RtspControllerParent();
+
+  bool RecvAsyncOpen(const URIParams& aURI);
+  bool RecvPlay();
+  bool RecvPause();
+  bool RecvResume();
+  bool RecvSuspend();
+  bool RecvSeek(const uint64_t& offset);
+  bool RecvStop();
+
+ private:
+  bool mIPCOpen;
+  void ActorDestroy(ActorDestroyReason why);
+  // RTSP URL refer to a stream or an aggregate of streams.
+  nsCOMPtr<nsIURI> mURI;
+  // The nsIStreamingProtocolController implementation.
+  nsCOMPtr<nsIStreamingProtocolController> mController;
+};
+
+} // namespace net
+} // namespace mozilla
+#endif // RtspControllerParent_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspMetaData.cpp
@@ -0,0 +1,249 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#include "RtspMetaData.h"
+#include "prlog.h"
+
+using namespace mozilla;
+
+namespace mozilla {
+namespace net {
+
+NS_IMPL_ISUPPORTS1(RtspMetaData, nsIStreamingProtocolMetaData)
+
+RtspMetaData::RtspMetaData()
+ : mIndex(0)
+ , mWidth(0)
+ , mHeight(0)
+ , mDuration(0)
+ , mSampleRate(0)
+ , mCurrentTimeStamp(0)
+ , mChannelCount(0)
+{
+  mMimeType.AssignLiteral("NONE");
+}
+
+RtspMetaData::~RtspMetaData()
+{
+
+}
+
+nsresult
+RtspMetaData::DeserializeRtspMetaData(const InfallibleTArray<RtspMetadataParam>& metaArray)
+{
+  nsresult rv;
+
+  // Deserialize meta data.
+  for (uint32_t i = 0; i < metaArray.Length(); i++) {
+    const RtspMetaValue& value = metaArray[i].value();
+    const nsCString& name = metaArray[i].name();
+
+    if (name.EqualsLiteral("FRAMETYPE")) {
+      rv = SetFrameType(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("TIMESTAMP")) {
+      rv = SetTimeStamp(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("TRACKS")) {
+      rv = SetTotalTracks(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if(name.EqualsLiteral("MIMETYPE")) {
+      rv = SetMimeType(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("WIDTH")) {
+      rv = SetWidth(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("HEIGHT")) {
+      rv = SetHeight(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("SAMPLERATE")) {
+      rv = SetSampleRate(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if(name.EqualsLiteral("DURATION")) {
+      rv = SetDuration(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("CHANNELCOUNT")) {
+      rv = SetChannelCount(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("ESDS")) {
+      rv = SetEsdsData(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    } else if (name.EqualsLiteral("AVCC")) {
+      rv = SetAvccData(value);
+      NS_ENSURE_SUCCESS(rv, NS_ERROR_FAILURE);
+    }
+  }
+
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetFrameType(uint32_t *aFrameType)
+{
+  NS_ENSURE_ARG_POINTER(aFrameType);
+  *aFrameType = mFrameType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetFrameType(uint32_t aFrameType)
+{
+  mFrameType = aFrameType;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetTotalTracks(uint32_t *aTracks)
+{
+  NS_ENSURE_ARG_POINTER(aTracks);
+  *aTracks = mTotalTracks;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetTotalTracks(uint32_t aTracks)
+{
+  mTotalTracks = aTracks;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetMimeType(nsACString & aMimeType)
+{
+  aMimeType.Assign(mMimeType);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetMimeType(const nsACString & aMimeType)
+{
+  mMimeType.Assign(aMimeType);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetWidth(uint32_t *aWidth)
+{
+  NS_ENSURE_ARG_POINTER(aWidth);
+  *aWidth = mWidth;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetWidth(uint32_t aWidth)
+{
+  mWidth = aWidth;
+  return NS_OK;
+}
+
+/* attribute unsigned long height; */
+NS_IMETHODIMP
+RtspMetaData::GetHeight(uint32_t *aHeight)
+{
+  NS_ENSURE_ARG_POINTER(aHeight);
+  *aHeight = mHeight;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetHeight(uint32_t aHeight)
+{
+  mHeight = aHeight;
+  return NS_OK;
+}
+
+/* attribute unsigned long long duration; */
+NS_IMETHODIMP
+RtspMetaData::GetDuration(uint64_t *aDuration)
+{
+  NS_ENSURE_ARG_POINTER(aDuration);
+  *aDuration = mDuration;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetDuration(uint64_t aDuration)
+{
+  mDuration = aDuration;
+  return NS_OK;
+}
+
+/* attribute unsigned long sampleRate; */
+NS_IMETHODIMP
+RtspMetaData::GetSampleRate(uint32_t *aSampleRate)
+{
+  NS_ENSURE_ARG_POINTER(aSampleRate);
+  *aSampleRate = mSampleRate;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetSampleRate(uint32_t aSampleRate)
+{
+  mSampleRate = aSampleRate;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetTimeStamp(uint64_t *aTimeStamp)
+{
+  NS_ENSURE_ARG_POINTER(aTimeStamp);
+  *aTimeStamp = mCurrentTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetTimeStamp(uint64_t aTimeStamp)
+{
+  mCurrentTimeStamp = aTimeStamp;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetChannelCount(uint32_t *aChannelCount)
+{
+  NS_ENSURE_ARG_POINTER(aChannelCount);
+  *aChannelCount = mChannelCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetChannelCount(uint32_t aChannelCount)
+{
+  mChannelCount = aChannelCount;
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetEsdsData(nsACString & aESDS)
+{
+  aESDS.Assign(mESDS);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetEsdsData(const nsACString & aESDS)
+{
+  mESDS.Assign(aESDS);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::GetAvccData(nsACString & aAVCC)
+{
+  aAVCC.Assign(mAVCC);
+  return NS_OK;
+}
+
+NS_IMETHODIMP
+RtspMetaData::SetAvccData(const nsACString & aAVCC)
+{
+  mAVCC.Assign(aAVCC);
+  return NS_OK;
+}
+
+} // namespace mozilla::net
+} // namespace mozilla
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/controller/RtspMetaData.h
@@ -0,0 +1,47 @@
+/* -*- Mode: C++; tab-width: 8; indent-tabs-mode: nil; c-basic-offset: 2 -*- */
+/* vim: set sw=2 ts=8 et tw=80 : */
+/* This Source Code Form is subject to the terms of the Mozilla Public
+ * License, v. 2.0. If a copy of the MPL was not distributed with this
+ * file, You can obtain one at http://mozilla.org/MPL/2.0/. */
+
+#ifndef RtspMetaData_h
+#define RtspMetaData_h
+
+#include "mozilla/net/PRtspController.h"
+#include "nsIStreamingProtocolController.h"
+#include "nsCOMPtr.h"
+#include "nsString.h"
+
+namespace mozilla {
+namespace net {
+
+class RtspMetaData : public nsIStreamingProtocolMetaData
+{
+ public:
+  NS_DECL_THREADSAFE_ISUPPORTS
+  NS_DECL_NSISTREAMINGPROTOCOLMETADATA
+
+  RtspMetaData();
+  ~RtspMetaData();
+
+  nsresult DeserializeRtspMetaData(const InfallibleTArray<RtspMetadataParam>& metaArray);
+
+ private:
+  uint32_t  mFrameType;
+  uint32_t  mIndex;
+  uint32_t  mTotalTracks;
+  uint32_t  mWidth;
+  uint32_t  mHeight;
+  uint64_t  mDuration;
+  uint32_t  mSampleRate;
+  uint64_t  mCurrentTimeStamp;
+  uint32_t  mChannelCount;
+  nsCString mMimeType;
+  nsCString mESDS;
+  nsCString mAVCC;
+};
+
+} // namespace net
+} // namespace mozilla
+
+#endif // RtspMetaData_h
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/moz.build
@@ -0,0 +1,49 @@
+# vim: set filetype=python:
+# This Source Code Form is subject to the terms of the Mozilla Public
+# License, v. 2.0. If a copy of the MPL was not distributed with this
+# file, You can obtain one at http://mozilla.org/MPL/2.0/.
+
+XPIDL_MODULE = 'necko_rtsp'
+
+MODULE = 'necko'
+
+EXPORTS.mozilla.net += [
+    'RtspChannel.h',
+    'RtspHandler.h',
+    'controller/RtspController.h',
+    'controller/RtspControllerChild.h',
+    'controller/RtspControllerParent.h',
+    'controller/RtspMetaData.h',
+    'rtsp/RTSPSource.h',
+]
+
+CPP_SOURCES += [
+    'RtspChannel.cpp',
+    'RtspHandler.cpp',
+    'controller/RtspController.cpp',
+    'controller/RtspControllerChild.cpp',
+    'controller/RtspControllerParent.cpp',
+    'controller/RtspMetaData.cpp',
+    'rtsp/AAMRAssembler.cpp',
+    'rtsp/AAVCAssembler.cpp',
+    'rtsp/AH263Assembler.cpp',
+    'rtsp/AMPEG4AudioAssembler.cpp',
+    'rtsp/AMPEG4ElementaryAssembler.cpp',
+    'rtsp/APacketSource.cpp',
+    'rtsp/ARTPAssembler.cpp',
+    'rtsp/ARTPConnection.cpp',
+    'rtsp/ARTPSource.cpp',
+    'rtsp/ARTPWriter.cpp',
+    'rtsp/ARTSPConnection.cpp',
+    'rtsp/ARawAudioAssembler.cpp',
+    'rtsp/ASessionDescription.cpp',
+    'rtsp/RTSPSource.cpp',
+]
+
+FAIL_ON_WARNINGS = True
+
+LIBXUL_LIBRARY = True
+
+MSVC_ENABLE_PGO = True
+
+LIBRARY_NAME = 'nkrtsp_s'
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AAMRAssembler.cpp
@@ -0,0 +1,234 @@
+/*
+ * Copyright (C) 2010 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 "AAMRAssembler"
+#include <utils/Log.h>
+
+#include "AAMRAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+        if (len == keyLen && !strncmp(s, key, keyLen)) {
+            value->setTo("1");
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+AAMRAssembler::AAMRAssembler(
+        const sp<AMessage> &notify, bool isWide, const AString &params)
+    : mIsWide(isWide),
+      mNotifyMsg(notify),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0) {
+    AString value;
+    CHECK(GetAttribute(params.c_str(), "octet-align", &value) && value == "1");
+    CHECK(!GetAttribute(params.c_str(), "crc", &value) || value == "0");
+    CHECK(!GetAttribute(params.c_str(), "interleaving", &value));
+}
+
+AAMRAssembler::~AAMRAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AAMRAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    return addPacket(source);
+}
+
+static size_t getFrameSize(bool isWide, unsigned FT) {
+    static const size_t kFrameSizeNB[9] = {
+        95, 103, 118, 134, 148, 159, 204, 244, 39
+    };
+    static const size_t kFrameSizeWB[10] = {
+        132, 177, 253, 285, 317, 365, 397, 461, 477, 40
+    };
+
+    if (FT == 15) {
+        return 1;
+    }
+
+    size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
+
+    // Round up bits to bytes and add 1 for the header byte.
+    frameSize = (frameSize + 7) / 8 + 1;
+
+    return frameSize;
+}
+
+ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+        LOGV("Not the sequence number I expected");
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    // hexdump(buffer->data(), buffer->size());
+
+    if (buffer->size() < 1) {
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        LOGV("AMR packet too short.");
+
+        return MALFORMED_PACKET;
+    }
+
+    unsigned payloadHeader = buffer->data()[0];
+    unsigned CMR = payloadHeader >> 4;
+    CHECK_EQ(payloadHeader & 0x0f, 0u);  // RR
+
+    Vector<uint8_t> tableOfContents;
+
+    size_t offset = 1;
+    size_t totalSize = 0;
+    for (;;) {
+        if (offset >= buffer->size()) {
+            queue->erase(queue->begin());
+            ++mNextExpectedSeqNo;
+
+            LOGV("Unable to parse TOC.");
+
+            return MALFORMED_PACKET;
+        }
+
+        uint8_t toc = buffer->data()[offset++];
+
+        unsigned FT = (toc >> 3) & 0x0f;
+        if ((toc & 3) != 0
+                || (mIsWide && FT > 9 && FT != 15)
+                || (!mIsWide && FT > 8 && FT != 15)) {
+            queue->erase(queue->begin());
+            ++mNextExpectedSeqNo;
+
+            LOGV("Illegal TOC entry.");
+
+            return MALFORMED_PACKET;
+        }
+
+        totalSize += getFrameSize(mIsWide, (toc >> 3) & 0x0f);
+
+        tableOfContents.push(toc);
+
+        if (0 == (toc & 0x80)) {
+            break;
+        }
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    CopyTimes(accessUnit, buffer);
+
+    size_t dstOffset = 0;
+    for (size_t i = 0; i < tableOfContents.size(); ++i) {
+        uint8_t toc = tableOfContents[i];
+
+        size_t frameSize = getFrameSize(mIsWide, (toc >> 3) & 0x0f);
+
+        if (offset + frameSize - 1 > buffer->size()) {
+            queue->erase(queue->begin());
+            ++mNextExpectedSeqNo;
+
+            LOGV("AMR packet too short.");
+
+            return MALFORMED_PACKET;
+        }
+
+        accessUnit->data()[dstOffset++] = toc;
+        memcpy(accessUnit->data() + dstOffset,
+               buffer->data() + offset, frameSize - 1);
+
+        offset += frameSize - 1;
+        dstOffset += frameSize - 1;
+    }
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AAMRAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    ++mNextExpectedSeqNo;
+}
+
+void AAMRAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AAMRAssembler.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_AMR_ASSEMBLER_H_
+
+#define A_AMR_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+
+#include <stdint.h>
+
+namespace android {
+
+struct AMessage;
+struct AString;
+
+struct AAMRAssembler : public ARTPAssembler {
+    AAMRAssembler(
+            const sp<AMessage> &notify, bool isWide,
+            const AString &params);
+
+protected:
+    virtual ~AAMRAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    bool mIsWide;
+
+    sp<AMessage> mNotifyMsg;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAMRAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_AMR_ASSEMBLER_H_
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AAVCAssembler.cpp
@@ -0,0 +1,376 @@
+/*
+ * Copyright (C) 2010 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 "AAVCAssembler"
+#include <utils/Log.h>
+
+#include "AAVCAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <stdint.h>
+
+namespace android {
+
+// static
+AAVCAssembler::AAVCAssembler(const sp<AMessage> &notify)
+    : mNotifyMsg(notify),
+      mAccessUnitRTPTime(0),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0),
+      mAccessUnitDamaged(false) {
+}
+
+AAVCAssembler::~AAVCAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AAVCAssembler::addNALUnit(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+        LOGV("Not the sequence number I expected");
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    if (size < 1 || (data[0] & 0x80)) {
+        // Corrupt.
+
+        LOGV("Ignoring corrupt buffer.");
+        queue->erase(queue->begin());
+
+        ++mNextExpectedSeqNo;
+        return MALFORMED_PACKET;
+    }
+
+    unsigned nalType = data[0] & 0x1f;
+    if (nalType >= 1 && nalType <= 23) {
+        addSingleNALUnit(buffer);
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+        return OK;
+    } else if (nalType == 28) {
+        // FU-A
+        return addFragmentedNALUnit(queue);
+    } else if (nalType == 24) {
+        // STAP-A
+        bool success = addSingleTimeAggregationPacket(buffer);
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        return success ? OK : MALFORMED_PACKET;
+    } else {
+        LOGV("Ignoring unsupported buffer (nalType=%d)", nalType);
+
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        return MALFORMED_PACKET;
+    }
+}
+
+void AAVCAssembler::addSingleNALUnit(const sp<ABuffer> &buffer) {
+    LOGV("addSingleNALUnit of size %d", buffer->size());
+#if !LOG_NDEBUG
+    hexdump(buffer->data(), buffer->size());
+#endif
+
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    if (!mNALUnits.empty() && rtpTime != mAccessUnitRTPTime) {
+        submitAccessUnit();
+    }
+    mAccessUnitRTPTime = rtpTime;
+
+    mNALUnits.push_back(buffer);
+}
+
+bool AAVCAssembler::addSingleTimeAggregationPacket(const sp<ABuffer> &buffer) {
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    if (size < 3) {
+        LOGV("Discarding too small STAP-A packet.");
+        return false;
+    }
+
+    ++data;
+    --size;
+    while (size >= 2) {
+        size_t nalSize = (data[0] << 8) | data[1];
+
+        if (size < nalSize + 2) {
+            LOGV("Discarding malformed STAP-A packet.");
+            return false;
+        }
+
+        sp<ABuffer> unit = new ABuffer(nalSize);
+        memcpy(unit->data(), &data[2], nalSize);
+
+        CopyTimes(unit, buffer);
+
+        addSingleNALUnit(unit);
+
+        data += 2 + nalSize;
+        size -= 2 + nalSize;
+    }
+
+    if (size != 0) {
+        LOGV("Unexpected padding at end of STAP-A packet.");
+    }
+
+    return true;
+}
+
+ARTPAssembler::AssemblyStatus AAVCAssembler::addFragmentedNALUnit(
+        List<sp<ABuffer> > *queue) {
+    CHECK(!queue->empty());
+
+    sp<ABuffer> buffer = *queue->begin();
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    CHECK(size > 0);
+    unsigned indicator = data[0];
+
+    CHECK((indicator & 0x1f) == 28);
+
+    if (size < 2) {
+        LOGV("Ignoring malformed FU buffer (size = %d)", size);
+
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+        return MALFORMED_PACKET;
+    }
+
+    if (!(data[1] & 0x80)) {
+        // Start bit not set on the first buffer.
+
+        LOGV("Start bit not set on first buffer");
+
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+        return MALFORMED_PACKET;
+    }
+
+    uint32_t nalType = data[1] & 0x1f;
+    uint32_t nri = (data[0] >> 5) & 3;
+
+    uint32_t expectedSeqNo = (uint32_t)buffer->int32Data() + 1;
+    size_t totalSize = size - 2;
+    size_t totalCount = 1;
+    bool complete = false;
+
+    if (data[1] & 0x40) {
+        // Huh? End bit also set on the first buffer.
+
+        LOGV("Grrr. This isn't fragmented at all.");
+
+        complete = true;
+    } else {
+        List<sp<ABuffer> >::iterator it = ++queue->begin();
+        while (it != queue->end()) {
+            LOGV("sequence length %d", totalCount);
+
+            const sp<ABuffer> &buffer = *it;
+
+            const uint8_t *data = buffer->data();
+            size_t size = buffer->size();
+
+            if ((uint32_t)buffer->int32Data() != expectedSeqNo) {
+                LOGV("sequence not complete, expected seqNo %d, got %d",
+                     expectedSeqNo, (uint32_t)buffer->int32Data());
+
+                return WRONG_SEQUENCE_NUMBER;
+            }
+
+            if (size < 2
+                    || data[0] != indicator
+                    || (data[1] & 0x1f) != nalType
+                    || (data[1] & 0x80)) {
+                LOGV("Ignoring malformed FU buffer.");
+
+                // Delete the whole start of the FU.
+
+                it = queue->begin();
+                for (size_t i = 0; i <= totalCount; ++i) {
+                    it = queue->erase(it);
+                }
+
+                mNextExpectedSeqNo = expectedSeqNo + 1;
+
+                return MALFORMED_PACKET;
+            }
+
+            totalSize += size - 2;
+            ++totalCount;
+
+            expectedSeqNo = expectedSeqNo + 1;
+
+            if (data[1] & 0x40) {
+                // This is the last fragment.
+                complete = true;
+                break;
+            }
+
+            ++it;
+        }
+    }
+
+    if (!complete) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    mNextExpectedSeqNo = expectedSeqNo;
+
+    // We found all the fragments that make up the complete NAL unit.
+
+    // Leave room for the header. So far totalSize did not include the
+    // header byte.
+    ++totalSize;
+
+    sp<ABuffer> unit = new ABuffer(totalSize);
+    CopyTimes(unit, *queue->begin());
+
+    unit->data()[0] = (nri << 5) | nalType;
+
+    size_t offset = 1;
+    List<sp<ABuffer> >::iterator it = queue->begin();
+    for (size_t i = 0; i < totalCount; ++i) {
+        const sp<ABuffer> &buffer = *it;
+
+        LOGV("piece #%d/%d", i + 1, totalCount);
+#if !LOG_NDEBUG
+        hexdump(buffer->data(), buffer->size());
+#endif
+
+        memcpy(unit->data() + offset, buffer->data() + 2, buffer->size() - 2);
+        offset += buffer->size() - 2;
+
+        it = queue->erase(it);
+    }
+
+    unit->setRange(0, totalSize);
+
+    addSingleNALUnit(unit);
+
+    LOGV("successfully assembled a NAL unit from fragments.");
+
+    return OK;
+}
+
+void AAVCAssembler::submitAccessUnit() {
+    CHECK(!mNALUnits.empty());
+
+    LOGV("Access unit complete (%d nal units)", mNALUnits.size());
+
+    size_t totalSize = 0;
+    for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
+         it != mNALUnits.end(); ++it) {
+        totalSize += 4 + (*it)->size();
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    for (List<sp<ABuffer> >::iterator it = mNALUnits.begin();
+         it != mNALUnits.end(); ++it) {
+        memcpy(accessUnit->data() + offset, "\x00\x00\x00\x01", 4);
+        offset += 4;
+
+        sp<ABuffer> nal = *it;
+        memcpy(accessUnit->data() + offset, nal->data(), nal->size());
+        offset += nal->size();
+    }
+
+    CopyTimes(accessUnit, *mNALUnits.begin());
+
+#if 0
+    printf(mAccessUnitDamaged ? "X" : ".");
+    fflush(stdout);
+#endif
+
+    if (mAccessUnitDamaged) {
+        accessUnit->meta()->setInt32("damaged", true);
+    }
+
+    mNALUnits.clear();
+    mAccessUnitDamaged = false;
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+}
+
+ARTPAssembler::AssemblyStatus AAVCAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    AssemblyStatus status = addNALUnit(source);
+    if (status == MALFORMED_PACKET) {
+        mAccessUnitDamaged = true;
+    }
+    return status;
+}
+
+void AAVCAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    LOGV("packetLost (expected %d)", mNextExpectedSeqNo);
+
+    ++mNextExpectedSeqNo;
+
+    mAccessUnitDamaged = true;
+}
+
+void AAVCAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AAVCAssembler.h
@@ -0,0 +1,62 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_AVC_ASSEMBLER_H_
+
+#define A_AVC_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct AAVCAssembler : public ARTPAssembler {
+    AAVCAssembler(const sp<AMessage> &notify);
+
+protected:
+    virtual ~AAVCAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+
+    uint32_t mAccessUnitRTPTime;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+    bool mAccessUnitDamaged;
+    List<sp<ABuffer> > mNALUnits;
+
+    AssemblyStatus addNALUnit(const sp<ARTPSource> &source);
+    void addSingleNALUnit(const sp<ABuffer> &buffer);
+    AssemblyStatus addFragmentedNALUnit(List<sp<ABuffer> > *queue);
+    bool addSingleTimeAggregationPacket(const sp<ABuffer> &buffer);
+
+    void submitAccessUnit();
+
+    DISALLOW_EVIL_CONSTRUCTORS(AAVCAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_AVC_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AH263Assembler.cpp
@@ -0,0 +1,187 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "AH263Assembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+AH263Assembler::AH263Assembler(const sp<AMessage> &notify)
+    : mNotifyMsg(notify),
+      mAccessUnitRTPTime(0),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0),
+      mAccessUnitDamaged(false) {
+}
+
+AH263Assembler::~AH263Assembler() {
+}
+
+ARTPAssembler::AssemblyStatus AH263Assembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    AssemblyStatus status = addPacket(source);
+    if (status == MALFORMED_PACKET) {
+        mAccessUnitDamaged = true;
+    }
+    return status;
+}
+
+ARTPAssembler::AssemblyStatus AH263Assembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+#if VERBOSE
+        LOG(VERBOSE) << "Not the sequence number I expected";
+#endif
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+        submitAccessUnit();
+    }
+    mAccessUnitRTPTime = rtpTime;
+
+    // hexdump(buffer->data(), buffer->size());
+
+    if (buffer->size() < 2) {
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        return MALFORMED_PACKET;
+    }
+
+    unsigned payloadHeader = U16_AT(buffer->data());
+    CHECK_EQ(payloadHeader >> 11, 0u);  // RR=0
+    unsigned P = (payloadHeader >> 10) & 1;
+    CHECK_EQ((payloadHeader >> 9) & 1, 0u);  // V=0
+    CHECK_EQ((payloadHeader >> 3) & 0x3f, 0u);  // PLEN=0
+    CHECK_EQ(payloadHeader & 7, 0u);  // PEBIT=0
+
+    if (P) {
+        buffer->data()[0] = 0x00;
+        buffer->data()[1] = 0x00;
+    } else {
+        buffer->setRange(buffer->offset() + 2, buffer->size() - 2);
+    }
+
+    mPackets.push_back(buffer);
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AH263Assembler::submitAccessUnit() {
+    CHECK(!mPackets.empty());
+
+#if VERBOSE
+    LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
+#endif
+
+    size_t totalSize = 0;
+    List<sp<ABuffer> >::iterator it = mPackets.begin();
+    while (it != mPackets.end()) {
+        const sp<ABuffer> &unit = *it;
+
+        totalSize += unit->size();
+        ++it;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    it = mPackets.begin();
+    while (it != mPackets.end()) {
+        const sp<ABuffer> &unit = *it;
+
+        memcpy((uint8_t *)accessUnit->data() + offset,
+               unit->data(), unit->size());
+
+        offset += unit->size();
+
+        ++it;
+    }
+
+    CopyTimes(accessUnit, *mPackets.begin());
+
+#if 0
+    printf(mAccessUnitDamaged ? "X" : ".");
+    fflush(stdout);
+#endif
+
+    if (mAccessUnitDamaged) {
+        accessUnit->meta()->setInt32("damaged", true);
+    }
+
+    mPackets.clear();
+    mAccessUnitDamaged = false;
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+}
+
+void AH263Assembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    ++mNextExpectedSeqNo;
+
+    mAccessUnitDamaged = true;
+}
+
+void AH263Assembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AH263Assembler.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_H263_ASSEMBLER_H_
+
+#define A_H263_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+
+#include <stdint.h>
+
+namespace android {
+
+struct AMessage;
+
+struct AH263Assembler : public ARTPAssembler {
+    AH263Assembler(const sp<AMessage> &notify);
+
+protected:
+    virtual ~AH263Assembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+    uint32_t mAccessUnitRTPTime;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+    bool mAccessUnitDamaged;
+    List<sp<ABuffer> > mPackets;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+    void submitAccessUnit();
+
+    DISALLOW_EVIL_CONSTRUCTORS(AH263Assembler);
+};
+
+}  // namespace android
+
+#endif  // A_H263_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AMPEG4AudioAssembler.cpp
@@ -0,0 +1,591 @@
+/*
+ * Copyright (C) 2010 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 "AMPEG4AudioAssembler"
+
+#include "AMPEG4AudioAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <ctype.h>
+
+namespace android {
+
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+static sp<ABuffer> decodeHex(const AString &s) {
+    if ((s.size() % 2) != 0) {
+        return NULL;
+    }
+
+    size_t outLen = s.size() / 2;
+    sp<ABuffer> buffer = new ABuffer(outLen);
+    uint8_t *out = buffer->data();
+
+    uint8_t accum = 0;
+    for (size_t i = 0; i < s.size(); ++i) {
+        char c = s.c_str()[i];
+        unsigned value;
+        if (c >= '0' && c <= '9') {
+            value = c - '0';
+        } else if (c >= 'a' && c <= 'f') {
+            value = c - 'a' + 10;
+        } else if (c >= 'A' && c <= 'F') {
+            value = c - 'A' + 10;
+        } else {
+            return NULL;
+        }
+
+        accum = (accum << 4) | value;
+
+        if (i & 1) {
+            *out++ = accum;
+
+            accum = 0;
+        }
+    }
+
+    return buffer;
+}
+
+static status_t parseAudioObjectType(
+        ABitReader *bits, unsigned *audioObjectType) {
+    *audioObjectType = bits->getBits(5);
+    if ((*audioObjectType) == 31) {
+        *audioObjectType = 32 + bits->getBits(6);
+    }
+
+    return OK;
+}
+
+static status_t parseGASpecificConfig(
+        ABitReader *bits,
+        unsigned audioObjectType, unsigned channelConfiguration) {
+    unsigned frameLengthFlag = bits->getBits(1);
+    unsigned dependsOnCoreCoder = bits->getBits(1);
+    if (dependsOnCoreCoder) {
+        /* unsigned coreCoderDelay = */bits->getBits(1);
+    }
+    unsigned extensionFlag = bits->getBits(1);
+
+    if (!channelConfiguration) {
+        // program_config_element
+        return ERROR_UNSUPPORTED;  // XXX to be implemented
+    }
+
+    if (audioObjectType == 6 || audioObjectType == 20) {
+        /* unsigned layerNr = */bits->getBits(3);
+    }
+
+    if (extensionFlag) {
+        if (audioObjectType == 22) {
+            /* unsigned numOfSubFrame = */bits->getBits(5);
+            /* unsigned layerLength = */bits->getBits(11);
+        } else if (audioObjectType == 17 || audioObjectType == 19
+                || audioObjectType == 20 || audioObjectType == 23) {
+            /* unsigned aacSectionDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacScalefactorDataResilienceFlag = */bits->getBits(1);
+            /* unsigned aacSpectralDataResilienceFlag = */bits->getBits(1);
+        }
+
+        unsigned extensionFlag3 = bits->getBits(1);
+        CHECK_EQ(extensionFlag3, 0u);  // TBD in version 3
+    }
+
+    return OK;
+}
+
+static status_t parseAudioSpecificConfig(ABitReader *bits, sp<ABuffer> *asc) {
+    const uint8_t *dataStart = bits->data();
+    size_t totalNumBits = bits->numBitsLeft();
+
+    unsigned audioObjectType;
+    CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+
+    unsigned samplingFreqIndex = bits->getBits(4);
+    if (samplingFreqIndex == 0x0f) {
+        /* unsigned samplingFrequency = */bits->getBits(24);
+    }
+
+    unsigned channelConfiguration = bits->getBits(4);
+
+    unsigned extensionAudioObjectType = 0;
+    unsigned sbrPresent = 0;
+
+    if (audioObjectType == 5) {
+        extensionAudioObjectType = audioObjectType;
+        sbrPresent = 1;
+        unsigned extensionSamplingFreqIndex = bits->getBits(4);
+        if (extensionSamplingFreqIndex == 0x0f) {
+            /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+        }
+        CHECK_EQ(parseAudioObjectType(bits, &audioObjectType), (status_t)OK);
+    }
+
+    CHECK((audioObjectType >= 1 && audioObjectType <= 4)
+        || (audioObjectType >= 6 && audioObjectType <= 7)
+        || audioObjectType == 17
+        || (audioObjectType >= 19 && audioObjectType <= 23));
+
+    CHECK_EQ(parseGASpecificConfig(
+                bits, audioObjectType, channelConfiguration), (status_t)OK);
+
+    if (audioObjectType == 17
+            || (audioObjectType >= 19 && audioObjectType <= 27)) {
+        unsigned epConfig = bits->getBits(2);
+        if (epConfig == 2 || epConfig == 3) {
+            // ErrorProtectionSpecificConfig
+            return ERROR_UNSUPPORTED;  // XXX to be implemented
+
+            if (epConfig == 3) {
+                unsigned directMapping = bits->getBits(1);
+                CHECK_EQ(directMapping, 1u);
+            }
+        }
+    }
+
+    if (extensionAudioObjectType != 5 && bits->numBitsLeft() >= 16) {
+        size_t numBitsLeftAtStart = bits->numBitsLeft();
+
+        unsigned syncExtensionType = bits->getBits(11);
+        if (syncExtensionType == 0x2b7) {
+            LOGI("found syncExtension");
+
+            CHECK_EQ(parseAudioObjectType(bits, &extensionAudioObjectType),
+                     (status_t)OK);
+
+            sbrPresent = bits->getBits(1);
+
+            if (sbrPresent == 1) {
+                unsigned extensionSamplingFreqIndex = bits->getBits(4);
+                if (extensionSamplingFreqIndex == 0x0f) {
+                    /* unsigned extensionSamplingFrequency = */bits->getBits(24);
+                }
+            }
+
+            size_t numBitsInExtension =
+                numBitsLeftAtStart - bits->numBitsLeft();
+
+            if (numBitsInExtension & 7) {
+                // Apparently an extension is always considered an even
+                // multiple of 8 bits long.
+
+                LOGI("Skipping %d bits after sync extension",
+                     8 - (numBitsInExtension & 7));
+
+                bits->skipBits(8 - (numBitsInExtension & 7));
+            }
+        } else {
+            bits->putBits(syncExtensionType, 11);
+        }
+    }
+
+    if (asc != NULL) {
+        size_t bitpos = totalNumBits & 7;
+
+        ABitReader bs(dataStart, (totalNumBits + 7) / 8);
+
+        totalNumBits -= bits->numBitsLeft();
+
+        size_t numBytes = (totalNumBits + 7) / 8;
+
+        *asc = new ABuffer(numBytes);
+
+        if (bitpos & 7) {
+            bs.skipBits(8 - (bitpos & 7));
+        }
+
+        uint8_t *dstPtr = (*asc)->data();
+        while (numBytes > 0) {
+            *dstPtr++ = bs.getBits(8);
+            --numBytes;
+        }
+    }
+
+    return OK;
+}
+
+static status_t parseStreamMuxConfig(
+        ABitReader *bits,
+        unsigned *numSubFrames,
+        unsigned *frameLengthType,
+        ssize_t *fixedFrameLength,
+        bool *otherDataPresent,
+        unsigned *otherDataLenBits) {
+    unsigned audioMuxVersion = bits->getBits(1);
+
+    unsigned audioMuxVersionA = 0;
+    if (audioMuxVersion == 1) {
+        audioMuxVersionA = bits->getBits(1);
+    }
+
+    CHECK_EQ(audioMuxVersionA, 0u);  // otherwise future spec
+
+    if (audioMuxVersion != 0) {
+        return ERROR_UNSUPPORTED;  // XXX to be implemented;
+    }
+    CHECK_EQ(audioMuxVersion, 0u);  // XXX to be implemented
+
+    unsigned allStreamsSameTimeFraming = bits->getBits(1);
+    CHECK_EQ(allStreamsSameTimeFraming, 1u);  // There's only one stream.
+
+    *numSubFrames = bits->getBits(6);
+    unsigned numProgram = bits->getBits(4);
+    CHECK_EQ(numProgram, 0u);  // disabled in RTP LATM
+
+    unsigned numLayer = bits->getBits(3);
+    CHECK_EQ(numLayer, 0u);  // disabled in RTP LATM
+
+    if (audioMuxVersion == 0) {
+        // AudioSpecificConfig
+        CHECK_EQ(parseAudioSpecificConfig(bits, NULL /* asc */), (status_t)OK);
+    } else {
+        TRESPASS();  // XXX to be implemented
+    }
+
+    *frameLengthType = bits->getBits(3);
+    *fixedFrameLength = -1;
+
+    switch (*frameLengthType) {
+        case 0:
+        {
+            /* unsigned bufferFullness = */bits->getBits(8);
+
+            // The "coreFrameOffset" does not apply since there's only
+            // a single layer.
+            break;
+        }
+
+        case 1:
+        {
+            *fixedFrameLength = bits->getBits(9);
+            break;
+        }
+
+        case 2:
+        {
+            // reserved
+            TRESPASS();
+            break;
+        }
+
+        case 3:
+        case 4:
+        case 5:
+        {
+            /* unsigned CELPframeLengthTableIndex = */bits->getBits(6);
+            break;
+        }
+
+        case 6:
+        case 7:
+        {
+            /* unsigned HVXCframeLengthTableIndex = */bits->getBits(1);
+            break;
+        }
+
+        default:
+            break;
+    }
+
+    *otherDataPresent = bits->getBits(1);
+    *otherDataLenBits = 0;
+    if (*otherDataPresent) {
+        if (audioMuxVersion == 1) {
+            TRESPASS();  // XXX to be implemented
+        } else {
+            *otherDataLenBits = 0;
+
+            unsigned otherDataLenEsc;
+            do {
+                (*otherDataLenBits) <<= 8;
+                otherDataLenEsc = bits->getBits(1);
+                unsigned otherDataLenTmp = bits->getBits(8);
+                (*otherDataLenBits) += otherDataLenTmp;
+            } while (otherDataLenEsc);
+        }
+    }
+
+    unsigned crcCheckPresent = bits->getBits(1);
+    if (crcCheckPresent) {
+        /* unsigned crcCheckSum = */bits->getBits(8);
+    }
+
+    return OK;
+}
+
+sp<ABuffer> AMPEG4AudioAssembler::removeLATMFraming(const sp<ABuffer> &buffer) {
+    CHECK(!mMuxConfigPresent);  // XXX to be implemented
+
+    sp<ABuffer> out = new ABuffer(buffer->size());
+    out->setRange(0, 0);
+
+    size_t offset = 0;
+    uint8_t *ptr = buffer->data();
+
+    for (size_t i = 0; i <= mNumSubFrames; ++i) {
+        // parse PayloadLengthInfo
+
+        unsigned payloadLength = 0;
+
+        switch (mFrameLengthType) {
+            case 0:
+            {
+                unsigned muxSlotLengthBytes = 0;
+                unsigned tmp;
+                do {
+                    CHECK_LT(offset, buffer->size());
+                    tmp = ptr[offset++];
+                    muxSlotLengthBytes += tmp;
+                } while (tmp == 0xff);
+
+                payloadLength = muxSlotLengthBytes;
+                break;
+            }
+
+            case 2:
+            {
+                // reserved
+
+                TRESPASS();
+                break;
+            }
+
+            default:
+            {
+                CHECK_GE(mFixedFrameLength, 0);
+
+                payloadLength = mFixedFrameLength;
+                break;
+            }
+        }
+
+        CHECK_LE(offset + payloadLength, buffer->size());
+
+        memcpy(out->data() + out->size(), &ptr[offset], payloadLength);
+        out->setRange(0, out->size() + payloadLength);
+
+        offset += payloadLength;
+
+        if (mOtherDataPresent) {
+            // We want to stay byte-aligned.
+
+            CHECK((mOtherDataLenBits % 8) == 0);
+            CHECK_LE(offset + (mOtherDataLenBits / 8), buffer->size());
+            offset += mOtherDataLenBits / 8;
+        }
+    }
+
+    if (offset < buffer->size()) {
+        LOGI("ignoring %d bytes of trailing data", buffer->size() - offset);
+    }
+    CHECK_LE(offset, buffer->size());
+
+    return out;
+}
+
+AMPEG4AudioAssembler::AMPEG4AudioAssembler(
+        const sp<AMessage> &notify, const AString &params)
+    : mNotifyMsg(notify),
+      mMuxConfigPresent(false),
+      mAccessUnitRTPTime(0),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0),
+      mAccessUnitDamaged(false) {
+    AString val;
+    if (!GetAttribute(params.c_str(), "cpresent", &val)) {
+        mMuxConfigPresent = true;
+    } else if (val == "0") {
+        mMuxConfigPresent = false;
+    } else {
+        CHECK(val == "1");
+        mMuxConfigPresent = true;
+    }
+
+    CHECK(GetAttribute(params.c_str(), "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    ABitReader bits(config->data(), config->size());
+    status_t err = parseStreamMuxConfig(
+            &bits, &mNumSubFrames, &mFrameLengthType,
+            &mFixedFrameLength,
+            &mOtherDataPresent, &mOtherDataLenBits);
+
+    CHECK_EQ(err, (status_t)NO_ERROR);
+}
+
+AMPEG4AudioAssembler::~AMPEG4AudioAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4AudioAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    AssemblyStatus status = addPacket(source);
+    if (status == MALFORMED_PACKET) {
+        mAccessUnitDamaged = true;
+    }
+    return status;
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4AudioAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+#if VERBOSE
+        LOG(VERBOSE) << "Not the sequence number I expected";
+#endif
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+        submitAccessUnit();
+    }
+    mAccessUnitRTPTime = rtpTime;
+
+    mPackets.push_back(buffer);
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AMPEG4AudioAssembler::submitAccessUnit() {
+    CHECK(!mPackets.empty());
+
+#if VERBOSE
+    LOG(VERBOSE) << "Access unit complete (" << mPackets.size() << " packets)";
+#endif
+
+    size_t totalSize = 0;
+    List<sp<ABuffer> >::iterator it = mPackets.begin();
+    while (it != mPackets.end()) {
+        const sp<ABuffer> &unit = *it;
+
+        totalSize += unit->size();
+        ++it;
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    it = mPackets.begin();
+    while (it != mPackets.end()) {
+        const sp<ABuffer> &unit = *it;
+
+        memcpy((uint8_t *)accessUnit->data() + offset,
+               unit->data(), unit->size());
+
+        ++it;
+    }
+
+    accessUnit = removeLATMFraming(accessUnit);
+    CopyTimes(accessUnit, *mPackets.begin());
+
+#if 0
+    printf(mAccessUnitDamaged ? "X" : ".");
+    fflush(stdout);
+#endif
+
+    if (mAccessUnitDamaged) {
+        accessUnit->meta()->setInt32("damaged", true);
+    }
+
+    mPackets.clear();
+    mAccessUnitDamaged = false;
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+}
+
+void AMPEG4AudioAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    ++mNextExpectedSeqNo;
+
+    mAccessUnitDamaged = true;
+}
+
+void AMPEG4AudioAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AMPEG4AudioAssembler.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_MPEG4_AUDIO_ASSEMBLER_H_
+
+#define A_MPEG4_AUDIO_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <utils/List.h>
+
+#include <stdint.h>
+
+namespace android {
+
+struct AMessage;
+struct AString;
+
+struct AMPEG4AudioAssembler : public ARTPAssembler {
+    AMPEG4AudioAssembler(
+            const sp<AMessage> &notify, const AString &params);
+
+protected:
+    virtual ~AMPEG4AudioAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+
+    bool mMuxConfigPresent;
+    unsigned mNumSubFrames;
+    unsigned mFrameLengthType;
+    ssize_t mFixedFrameLength;
+    bool mOtherDataPresent;
+    unsigned mOtherDataLenBits;
+
+    uint32_t mAccessUnitRTPTime;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+    bool mAccessUnitDamaged;
+    List<sp<ABuffer> > mPackets;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+    void submitAccessUnit();
+
+    sp<ABuffer> removeLATMFraming(const sp<ABuffer> &buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(AMPEG4AudioAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_MPEG4_AUDIO_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -0,0 +1,399 @@
+/*
+ * Copyright (C) 2010 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 "AMPEG4ElementaryAssembler"
+#include <utils/Log.h>
+
+#include "AMPEG4ElementaryAssembler.h"
+
+#include "ARTPSource.h"
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/Utils.h>
+
+#include <ctype.h>
+#include <stdint.h>
+
+namespace android {
+
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '='
+                && !strncasecmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+static bool GetIntegerAttribute(
+        const char *s, const char *key, unsigned *x) {
+    *x = 0;
+
+    AString val;
+    if (!GetAttribute(s, key, &val)) {
+        return false;
+    }
+
+    s = val.c_str();
+    char *end;
+    unsigned y = strtoul(s, &end, 10);
+
+    if (end == s || *end != '\0') {
+        return false;
+    }
+
+    *x = y;
+
+    return true;
+}
+
+// static
+AMPEG4ElementaryAssembler::AMPEG4ElementaryAssembler(
+        const sp<AMessage> &notify, const AString &desc, const AString &params)
+    : mNotifyMsg(notify),
+      mIsGeneric(false),
+      mParams(params),
+      mSizeLength(0),
+      mIndexLength(0),
+      mIndexDeltaLength(0),
+      mCTSDeltaLength(0),
+      mDTSDeltaLength(0),
+      mRandomAccessIndication(false),
+      mStreamStateIndication(0),
+      mAuxiliaryDataSizeLength(0),
+      mHasAUHeader(false),
+      mAccessUnitRTPTime(0),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0),
+      mAccessUnitDamaged(false) {
+    mIsGeneric = !strncasecmp(desc.c_str(),"mpeg4-generic/", 14);
+
+    if (mIsGeneric) {
+        AString value;
+        CHECK(GetAttribute(params.c_str(), "mode", &value));
+
+        if (!GetIntegerAttribute(params.c_str(), "sizeLength", &mSizeLength)) {
+            mSizeLength = 0;
+        }
+
+        if (!GetIntegerAttribute(
+                    params.c_str(), "indexLength", &mIndexLength)) {
+            mIndexLength = 0;
+        }
+
+        if (!GetIntegerAttribute(
+                    params.c_str(), "indexDeltaLength", &mIndexDeltaLength)) {
+            mIndexDeltaLength = 0;
+        }
+
+        if (!GetIntegerAttribute(
+                    params.c_str(), "CTSDeltaLength", &mCTSDeltaLength)) {
+            mCTSDeltaLength = 0;
+        }
+
+        if (!GetIntegerAttribute(
+                    params.c_str(), "DTSDeltaLength", &mDTSDeltaLength)) {
+            mDTSDeltaLength = 0;
+        }
+
+        unsigned x;
+        if (!GetIntegerAttribute(
+                    params.c_str(), "randomAccessIndication", &x)) {
+            mRandomAccessIndication = false;
+        } else {
+            CHECK(x == 0 || x == 1);
+            mRandomAccessIndication = (x != 0);
+        }
+
+        if (!GetIntegerAttribute(
+                    params.c_str(), "streamStateIndication",
+                    &mStreamStateIndication)) {
+            mStreamStateIndication = 0;
+        }
+
+        if (!GetIntegerAttribute(
+                    params.c_str(), "auxiliaryDataSizeLength",
+                    &mAuxiliaryDataSizeLength)) {
+            mAuxiliaryDataSizeLength = 0;
+        }
+
+        mHasAUHeader =
+            mSizeLength > 0
+            || mIndexLength > 0
+            || mIndexDeltaLength > 0
+            || mCTSDeltaLength > 0
+            || mDTSDeltaLength > 0
+            || mRandomAccessIndication
+            || mStreamStateIndication > 0;
+    }
+}
+
+AMPEG4ElementaryAssembler::~AMPEG4ElementaryAssembler() {
+}
+
+struct AUHeader {
+    unsigned mSize;
+    unsigned mSerial;
+};
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+        LOGV("Not the sequence number I expected");
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    uint32_t rtpTime;
+    CHECK(buffer->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    if (mPackets.size() > 0 && rtpTime != mAccessUnitRTPTime) {
+        submitAccessUnit();
+    }
+    mAccessUnitRTPTime = rtpTime;
+
+    if (!mIsGeneric) {
+        mPackets.push_back(buffer);
+    } else {
+        // hexdump(buffer->data(), buffer->size());
+
+        CHECK_GE(buffer->size(), 2u);
+        unsigned AU_headers_length = U16_AT(buffer->data());  // in bits
+
+        CHECK_GE(buffer->size(), 2 + (AU_headers_length + 7) / 8);
+
+        List<AUHeader> headers;
+
+        ABitReader bits(buffer->data() + 2, buffer->size() - 2);
+        unsigned numBitsLeft = AU_headers_length;
+
+        unsigned AU_serial = 0;
+        for (;;) {
+            if (numBitsLeft < mSizeLength) { break; }
+
+            unsigned AU_size = bits.getBits(mSizeLength);
+            numBitsLeft -= mSizeLength;
+
+            size_t n = headers.empty() ? mIndexLength : mIndexDeltaLength;
+            if (numBitsLeft < n) { break; }
+
+            unsigned AU_index = bits.getBits(n);
+            numBitsLeft -= n;
+
+            if (headers.empty()) {
+                AU_serial = AU_index;
+            } else {
+                AU_serial += 1 + AU_index;
+            }
+
+            if (mCTSDeltaLength > 0) {
+                if (numBitsLeft < 1) {
+                    break;
+                }
+                --numBitsLeft;
+                if (bits.getBits(1)) {
+                    if (numBitsLeft < mCTSDeltaLength) {
+                        break;
+                    }
+                    bits.skipBits(mCTSDeltaLength);
+                    numBitsLeft -= mCTSDeltaLength;
+                }
+            }
+
+            if (mDTSDeltaLength > 0) {
+                if (numBitsLeft < 1) {
+                    break;
+                }
+                --numBitsLeft;
+                if (bits.getBits(1)) {
+                    if (numBitsLeft < mDTSDeltaLength) {
+                        break;
+                    }
+                    bits.skipBits(mDTSDeltaLength);
+                    numBitsLeft -= mDTSDeltaLength;
+                }
+            }
+
+            if (mRandomAccessIndication) {
+                if (numBitsLeft < 1) {
+                    break;
+                }
+                bits.skipBits(1);
+                --numBitsLeft;
+            }
+
+            if (mStreamStateIndication > 0) {
+                if (numBitsLeft < mStreamStateIndication) {
+                    break;
+                }
+                bits.skipBits(mStreamStateIndication);
+            }
+
+            AUHeader header;
+            header.mSize = AU_size;
+            header.mSerial = AU_serial;
+            headers.push_back(header);
+        }
+
+        size_t offset = 2 + (AU_headers_length + 7) / 8;
+
+        if (mAuxiliaryDataSizeLength > 0) {
+            ABitReader bits(buffer->data() + offset, buffer->size() - offset);
+
+            unsigned auxSize = bits.getBits(mAuxiliaryDataSizeLength);
+
+            offset += (mAuxiliaryDataSizeLength + auxSize + 7) / 8;
+        }
+
+        for (List<AUHeader>::iterator it = headers.begin();
+             it != headers.end(); ++it) {
+            const AUHeader &header = *it;
+
+            CHECK_LE(offset + header.mSize, buffer->size());
+
+            sp<ABuffer> accessUnit = new ABuffer(header.mSize);
+            memcpy(accessUnit->data(), buffer->data() + offset, header.mSize);
+
+            offset += header.mSize;
+
+            CopyTimes(accessUnit, buffer);
+            mPackets.push_back(accessUnit);
+        }
+
+        CHECK_EQ(offset, buffer->size());
+    }
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void AMPEG4ElementaryAssembler::submitAccessUnit() {
+    CHECK(!mPackets.empty());
+
+    LOGV("Access unit complete (%d nal units)", mPackets.size());
+
+    size_t totalSize = 0;
+    for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+         it != mPackets.end(); ++it) {
+        totalSize += (*it)->size();
+    }
+
+    sp<ABuffer> accessUnit = new ABuffer(totalSize);
+    size_t offset = 0;
+    for (List<sp<ABuffer> >::iterator it = mPackets.begin();
+         it != mPackets.end(); ++it) {
+        sp<ABuffer> nal = *it;
+        memcpy(accessUnit->data() + offset, nal->data(), nal->size());
+        offset += nal->size();
+    }
+
+    CopyTimes(accessUnit, *mPackets.begin());
+
+#if 0
+    printf(mAccessUnitDamaged ? "X" : ".");
+    fflush(stdout);
+#endif
+
+    if (mAccessUnitDamaged) {
+        accessUnit->meta()->setInt32("damaged", true);
+    }
+
+    mPackets.clear();
+    mAccessUnitDamaged = false;
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", accessUnit);
+    msg->post();
+}
+
+ARTPAssembler::AssemblyStatus AMPEG4ElementaryAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    AssemblyStatus status = addPacket(source);
+    if (status == MALFORMED_PACKET) {
+        mAccessUnitDamaged = true;
+    }
+    return status;
+}
+
+void AMPEG4ElementaryAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    LOGV("packetLost (expected %d)", mNextExpectedSeqNo);
+
+    ++mNextExpectedSeqNo;
+
+    mAccessUnitDamaged = true;
+}
+
+void AMPEG4ElementaryAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/AMPEG4ElementaryAssembler.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_MPEG4_ELEM_ASSEMBLER_H_
+
+#define A_MPEG4_ELEM_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+#include <media/stagefright/foundation/AString.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+
+struct AMPEG4ElementaryAssembler : public ARTPAssembler {
+    AMPEG4ElementaryAssembler(
+            const sp<AMessage> &notify, const AString &desc,
+            const AString &params);
+
+protected:
+    virtual ~AMPEG4ElementaryAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    sp<AMessage> mNotifyMsg;
+    bool mIsGeneric;
+    AString mParams;
+
+    unsigned mSizeLength;
+    unsigned mIndexLength;
+    unsigned mIndexDeltaLength;
+    unsigned mCTSDeltaLength;
+    unsigned mDTSDeltaLength;
+    bool mRandomAccessIndication;
+    unsigned mStreamStateIndication;
+    unsigned mAuxiliaryDataSizeLength;
+    bool mHasAUHeader;
+
+    uint32_t mAccessUnitRTPTime;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+    bool mAccessUnitDamaged;
+    List<sp<ABuffer> > mPackets;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+    void submitAccessUnit();
+
+    DISALLOW_EVIL_CONSTRUCTORS(AMPEG4ElementaryAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_MPEG4_ELEM_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/APacketSource.cpp
@@ -0,0 +1,580 @@
+/*
+ * Copyright (C) 2010 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 "APacketSource"
+#include <utils/Log.h>
+
+#include "APacketSource.h"
+
+#include "ARawAudioAssembler.h"
+#include "ASessionDescription.h"
+
+#include "avc_utils.h"
+
+#include <ctype.h>
+
+#include <media/stagefright/foundation/ABitReader.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/Vector.h>
+
+namespace android {
+
+static bool GetAttribute(const char *s, const char *key, AString *value) {
+    value->clear();
+
+    size_t keyLen = strlen(key);
+
+    for (;;) {
+        while (isspace(*s)) {
+            ++s;
+        }
+
+        const char *colonPos = strchr(s, ';');
+
+        size_t len =
+            (colonPos == NULL) ? strlen(s) : colonPos - s;
+
+        if (len >= keyLen + 1 && s[keyLen] == '=' && !strncmp(s, key, keyLen)) {
+            value->setTo(&s[keyLen + 1], len - keyLen - 1);
+            return true;
+        }
+
+        if (colonPos == NULL) {
+            return false;
+        }
+
+        s = colonPos + 1;
+    }
+}
+
+static sp<ABuffer> decodeHex(const AString &s) {
+    if ((s.size() % 2) != 0) {
+        return NULL;
+    }
+
+    size_t outLen = s.size() / 2;
+    sp<ABuffer> buffer = new ABuffer(outLen);
+    uint8_t *out = buffer->data();
+
+    uint8_t accum = 0;
+    for (size_t i = 0; i < s.size(); ++i) {
+        char c = s.c_str()[i];
+        unsigned value;
+        if (c >= '0' && c <= '9') {
+            value = c - '0';
+        } else if (c >= 'a' && c <= 'f') {
+            value = c - 'a' + 10;
+        } else if (c >= 'A' && c <= 'F') {
+            value = c - 'A' + 10;
+        } else {
+            return NULL;
+        }
+
+        accum = (accum << 4) | value;
+
+        if (i & 1) {
+            *out++ = accum;
+
+            accum = 0;
+        }
+    }
+
+    return buffer;
+}
+
+static sp<ABuffer> MakeAVCCodecSpecificData(
+        const char *params, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    AString val;
+    if (!GetAttribute(params, "profile-level-id", &val)) {
+        return NULL;
+    }
+
+    sp<ABuffer> profileLevelID = decodeHex(val);
+    CHECK(profileLevelID != NULL);
+    CHECK_EQ(profileLevelID->size(), 3u);
+
+    Vector<sp<ABuffer> > paramSets;
+
+    size_t numSeqParameterSets = 0;
+    size_t totalSeqParameterSetSize = 0;
+    size_t numPicParameterSets = 0;
+    size_t totalPicParameterSetSize = 0;
+
+    if (!GetAttribute(params, "sprop-parameter-sets", &val)) {
+        return NULL;
+    }
+
+    size_t start = 0;
+    for (;;) {
+        ssize_t commaPos = val.find(",", start);
+        size_t end = (commaPos < 0) ? val.size() : commaPos;
+
+        AString nalString(val, start, end - start);
+        sp<ABuffer> nal = decodeBase64(nalString);
+        CHECK(nal != NULL);
+        CHECK_GT(nal->size(), 0u);
+        CHECK_LE(nal->size(), 65535u);
+
+        uint8_t nalType = nal->data()[0] & 0x1f;
+        if (numSeqParameterSets == 0) {
+            CHECK_EQ((unsigned)nalType, 7u);
+        } else if (numPicParameterSets > 0) {
+            CHECK_EQ((unsigned)nalType, 8u);
+        }
+        if (nalType == 7) {
+            ++numSeqParameterSets;
+            totalSeqParameterSetSize += nal->size();
+        } else  {
+            CHECK_EQ((unsigned)nalType, 8u);
+            ++numPicParameterSets;
+            totalPicParameterSetSize += nal->size();
+        }
+
+        paramSets.push(nal);
+
+        if (commaPos < 0) {
+            break;
+        }
+
+        start = commaPos + 1;
+    }
+
+    CHECK_LT(numSeqParameterSets, 32u);
+    CHECK_LE(numPicParameterSets, 255u);
+
+    size_t csdSize =
+        1 + 3 + 1 + 1
+        + 2 * numSeqParameterSets + totalSeqParameterSetSize
+        + 1 + 2 * numPicParameterSets + totalPicParameterSetSize;
+
+    sp<ABuffer> csd = new ABuffer(csdSize);
+    uint8_t *out = csd->data();
+
+    *out++ = 0x01;  // configurationVersion
+    memcpy(out, profileLevelID->data(), 3);
+    out += 3;
+    *out++ = (0x3f << 2) | 1;  // lengthSize == 2 bytes
+    *out++ = 0xe0 | numSeqParameterSets;
+
+    for (size_t i = 0; i < numSeqParameterSets; ++i) {
+        sp<ABuffer> nal = paramSets.editItemAt(i);
+
+        *out++ = nal->size() >> 8;
+        *out++ = nal->size() & 0xff;
+
+        memcpy(out, nal->data(), nal->size());
+
+        out += nal->size();
+
+        if (i == 0) {
+            FindAVCDimensions(nal, width, height);
+            LOGI("dimensions %dx%d", *width, *height);
+        }
+    }
+
+    *out++ = numPicParameterSets;
+
+    for (size_t i = 0; i < numPicParameterSets; ++i) {
+        sp<ABuffer> nal = paramSets.editItemAt(i + numSeqParameterSets);
+
+        *out++ = nal->size() >> 8;
+        *out++ = nal->size() & 0xff;
+
+        memcpy(out, nal->data(), nal->size());
+
+        out += nal->size();
+    }
+
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+sp<ABuffer> MakeAACCodecSpecificData(const char *params) {
+    AString val;
+    CHECK(GetAttribute(params, "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+    CHECK_GE(config->size(), 4u);
+
+    const uint8_t *data = config->data();
+    uint32_t x = data[0] << 24 | data[1] << 16 | data[2] << 8 | data[3];
+    x = (x >> 1) & 0xffff;
+
+    static const uint8_t kStaticESDS[] = {
+        0x03, 22,
+        0x00, 0x00,     // ES_ID
+        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+        0x04, 17,
+        0x40,                       // Audio ISO/IEC 14496-3
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+
+        0x05, 2,
+        // AudioSpecificInfo follows
+    };
+
+    sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + 2);
+    memcpy(csd->data(), kStaticESDS, sizeof(kStaticESDS));
+    csd->data()[sizeof(kStaticESDS)] = (x >> 8) & 0xff;
+    csd->data()[sizeof(kStaticESDS) + 1] = x & 0xff;
+
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+// From mpeg4-generic configuration data.
+sp<ABuffer> MakeAACCodecSpecificData2(const char *params) {
+    AString val;
+    unsigned long objectType;
+    if (GetAttribute(params, "objectType", &val)) {
+        const char *s = val.c_str();
+        char *end;
+        objectType = strtoul(s, &end, 10);
+        CHECK(end > s && *end == '\0');
+    } else {
+        objectType = 0x40;  // Audio ISO/IEC 14496-3
+    }
+
+    CHECK(GetAttribute(params, "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    // Make sure size fits into a single byte and doesn't have to
+    // be encoded.
+    CHECK_LT(20 + config->size(), 128u);
+
+    const uint8_t *data = config->data();
+
+    static const uint8_t kStaticESDS[] = {
+        0x03, 22,
+        0x00, 0x00,     // ES_ID
+        0x00,           // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+        0x04, 17,
+        0x40,                       // Audio ISO/IEC 14496-3
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+        0x00, 0x00, 0x00, 0x00,
+
+        0x05, 2,
+        // AudioSpecificInfo follows
+    };
+
+    sp<ABuffer> csd = new ABuffer(sizeof(kStaticESDS) + config->size());
+    uint8_t *dst = csd->data();
+    *dst++ = 0x03;
+    *dst++ = 20 + config->size();
+    *dst++ = 0x00;  // ES_ID
+    *dst++ = 0x00;
+    *dst++ = 0x00;  // streamDependenceFlag, URL_Flag, OCRstreamFlag
+    *dst++ = 0x04;
+    *dst++ = 15 + config->size();
+    *dst++ = objectType;
+    for (int i = 0; i < 12; ++i) { *dst++ = 0x00; }
+    *dst++ = 0x05;
+    *dst++ = config->size();
+    memcpy(dst, config->data(), config->size());
+
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+static size_t GetSizeWidth(size_t x) {
+    size_t n = 1;
+    while (x > 127) {
+        ++n;
+        x >>= 7;
+    }
+    return n;
+}
+
+static uint8_t *EncodeSize(uint8_t *dst, size_t x) {
+    while (x > 127) {
+        *dst++ = (x & 0x7f) | 0x80;
+        x >>= 7;
+    }
+    *dst++ = x;
+    return dst;
+}
+
+static bool ExtractDimensionsMPEG4Config(
+        const sp<ABuffer> &config, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    const uint8_t *ptr = config->data();
+    size_t offset = 0;
+    bool foundVOL = false;
+    while (offset + 3 < config->size()) {
+        if (memcmp("\x00\x00\x01", &ptr[offset], 3)
+                || (ptr[offset + 3] & 0xf0) != 0x20) {
+            ++offset;
+            continue;
+        }
+
+        foundVOL = true;
+        break;
+    }
+
+    if (!foundVOL) {
+        return false;
+    }
+
+    return ExtractDimensionsFromVOLHeader(
+            &ptr[offset], config->size() - offset, width, height);
+}
+
+static sp<ABuffer> MakeMPEG4VideoCodecSpecificData(
+        const char *params, int32_t *width, int32_t *height) {
+    *width = 0;
+    *height = 0;
+
+    AString val;
+    CHECK(GetAttribute(params, "config", &val));
+
+    sp<ABuffer> config = decodeHex(val);
+    CHECK(config != NULL);
+
+    if (!ExtractDimensionsMPEG4Config(config, width, height)) {
+        return NULL;
+    }
+
+    LOGI("VOL dimensions = %dx%d", *width, *height);
+
+    size_t len1 = config->size() + GetSizeWidth(config->size()) + 1;
+    size_t len2 = len1 + GetSizeWidth(len1) + 1 + 13;
+    size_t len3 = len2 + GetSizeWidth(len2) + 1 + 3;
+
+    sp<ABuffer> csd = new ABuffer(len3);
+    uint8_t *dst = csd->data();
+    *dst++ = 0x03;
+    dst = EncodeSize(dst, len2 + 3);
+    *dst++ = 0x00;  // ES_ID
+    *dst++ = 0x00;
+    *dst++ = 0x00;  // streamDependenceFlag, URL_Flag, OCRstreamFlag
+
+    *dst++ = 0x04;
+    dst = EncodeSize(dst, len1 + 13);
+    *dst++ = 0x01;  // Video ISO/IEC 14496-2 Simple Profile
+    for (size_t i = 0; i < 12; ++i) {
+        *dst++ = 0x00;
+    }
+
+    *dst++ = 0x05;
+    dst = EncodeSize(dst, config->size());
+    memcpy(dst, config->data(), config->size());
+    dst += config->size();
+
+    // hexdump(csd->data(), csd->size());
+
+    return csd;
+}
+
+APacketSource::APacketSource(
+        const sp<ASessionDescription> &sessionDesc, size_t index)
+    : mInitCheck(NO_INIT),
+      mFormat(new MetaData) {
+    unsigned long PT;
+    AString desc;
+    AString params;
+    sessionDesc->getFormatType(index, &PT, &desc, &params);
+
+    int64_t durationUs;
+    if (sessionDesc->getDurationUs(&durationUs)) {
+        mFormat->setInt64(kKeyDuration, durationUs);
+    } else {
+        // Set its value to zero(long long) to indicate that this is a live stream.
+        mFormat->setInt64(kKeyDuration, 0ll);
+    }
+
+    mInitCheck = OK;
+    if (!strncmp(desc.c_str(), "H264/", 5)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_AVC);
+
+        int32_t width, height;
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            width = -1;
+            height = -1;
+        }
+
+        int32_t encWidth, encHeight;
+        sp<ABuffer> codecSpecificData =
+            MakeAVCCodecSpecificData(params.c_str(), &encWidth, &encHeight);
+
+        if (codecSpecificData != NULL) {
+            if (width < 0) {
+                // If no explicit width/height given in the sdp, use the dimensions
+                // extracted from the first sequence parameter set.
+                width = encWidth;
+                height = encHeight;
+            }
+
+            mFormat->setData(
+                    kKeyAVCC, 0,
+                    codecSpecificData->data(), codecSpecificData->size());
+        } else if (width < 0) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
+    } else if (!strncmp(desc.c_str(), "H263-2000/", 10)
+            || !strncmp(desc.c_str(), "H263-1998/", 10)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_H263);
+
+        int32_t width, height;
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
+    } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+        int32_t sampleRate, numChannels;
+        ASessionDescription::ParseFormatDesc(
+                desc.c_str(), &sampleRate, &numChannels);
+
+        mFormat->setInt32(kKeySampleRate, sampleRate);
+        mFormat->setInt32(kKeyChannelCount, numChannels);
+
+        sp<ABuffer> codecSpecificData =
+            MakeAACCodecSpecificData(params.c_str());
+
+        mFormat->setData(
+                kKeyESDS, 0,
+                codecSpecificData->data(), codecSpecificData->size());
+    } else if (!strncmp(desc.c_str(), "AMR/", 4)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_NB);
+
+        int32_t sampleRate, numChannels;
+        ASessionDescription::ParseFormatDesc(
+                desc.c_str(), &sampleRate, &numChannels);
+
+        mFormat->setInt32(kKeySampleRate, sampleRate);
+        mFormat->setInt32(kKeyChannelCount, numChannels);
+
+        if (sampleRate != 8000 || numChannels != 1) {
+            mInitCheck = ERROR_UNSUPPORTED;
+        }
+    } else if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AMR_WB);
+
+        int32_t sampleRate, numChannels;
+        ASessionDescription::ParseFormatDesc(
+                desc.c_str(), &sampleRate, &numChannels);
+
+        mFormat->setInt32(kKeySampleRate, sampleRate);
+        mFormat->setInt32(kKeyChannelCount, numChannels);
+
+        if (sampleRate != 16000 || numChannels != 1) {
+            mInitCheck = ERROR_UNSUPPORTED;
+        }
+    } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)) {
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_VIDEO_MPEG4);
+
+        int32_t width, height;
+        if (!sessionDesc->getDimensions(index, PT, &width, &height)) {
+            width = -1;
+            height = -1;
+        }
+
+        int32_t encWidth, encHeight;
+        sp<ABuffer> codecSpecificData =
+            MakeMPEG4VideoCodecSpecificData(
+                    params.c_str(), &encWidth, &encHeight);
+
+        if (codecSpecificData != NULL) {
+            mFormat->setData(
+                    kKeyESDS, 0,
+                    codecSpecificData->data(), codecSpecificData->size());
+
+            if (width < 0) {
+                width = encWidth;
+                height = encHeight;
+            }
+        } else if (width < 0) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+
+        mFormat->setInt32(kKeyWidth, width);
+        mFormat->setInt32(kKeyHeight, height);
+    } else if (!strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
+        AString val;
+        if (!GetAttribute(params.c_str(), "mode", &val)
+                || (strcasecmp(val.c_str(), "AAC-lbr")
+                    && strcasecmp(val.c_str(), "AAC-hbr"))) {
+            mInitCheck = ERROR_UNSUPPORTED;
+            return;
+        }
+
+        mFormat->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+
+        int32_t sampleRate, numChannels;
+        ASessionDescription::ParseFormatDesc(
+                desc.c_str(), &sampleRate, &numChannels);
+
+        mFormat->setInt32(kKeySampleRate, sampleRate);
+        mFormat->setInt32(kKeyChannelCount, numChannels);
+
+        sp<ABuffer> codecSpecificData =
+            MakeAACCodecSpecificData2(params.c_str());
+
+        mFormat->setData(
+                kKeyESDS, 0,
+                codecSpecificData->data(), codecSpecificData->size());
+    } else if (ARawAudioAssembler::Supports(desc.c_str())) {
+        ARawAudioAssembler::MakeFormat(desc.c_str(), mFormat);
+    } else {
+        mInitCheck = ERROR_UNSUPPORTED;
+    }
+}
+
+APacketSource::~APacketSource() {
+}
+
+status_t APacketSource::initCheck() const {
+    return mInitCheck;
+}
+
+sp<MetaData> APacketSource::getFormat() {
+    return mFormat;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/APacketSource.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_PACKET_SOURCE_H_
+
+#define A_PACKET_SOURCE_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ASessionDescription;
+
+struct APacketSource : public RefBase {
+    APacketSource(const sp<ASessionDescription> &sessionDesc, size_t index);
+
+    status_t initCheck() const;
+
+    virtual sp<MetaData> getFormat();
+
+protected:
+    virtual ~APacketSource();
+
+private:
+    status_t mInitCheck;
+
+    sp<MetaData> mFormat;
+
+    DISALLOW_EVIL_CONSTRUCTORS(APacketSource);
+};
+
+
+}  // namespace android
+
+#endif  // A_PACKET_SOURCE_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPAssembler.cpp
@@ -0,0 +1,77 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#include "ARTPAssembler.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+#include <stdint.h>
+
+namespace android {
+
+static int64_t getNowUs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000ll;
+}
+
+ARTPAssembler::ARTPAssembler()
+    : mFirstFailureTimeUs(-1) {
+}
+
+void ARTPAssembler::onPacketReceived(const sp<ARTPSource> &source) {
+    AssemblyStatus status;
+    for (;;) {
+        status = assembleMore(source);
+
+        if (status == WRONG_SEQUENCE_NUMBER) {
+            if (mFirstFailureTimeUs >= 0) {
+                if (getNowUs() - mFirstFailureTimeUs > 10000ll) {
+                    mFirstFailureTimeUs = -1;
+
+                    // LOG(VERBOSE) << "waited too long for packet.";
+                    packetLost();
+                    continue;
+                }
+            } else {
+                mFirstFailureTimeUs = getNowUs();
+            }
+            break;
+        } else {
+            mFirstFailureTimeUs = -1;
+
+            if (status == NOT_ENOUGH_DATA) {
+                break;
+            }
+        }
+    }
+}
+
+// static
+void ARTPAssembler::CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from) {
+    uint32_t rtpTime;
+    CHECK(from->meta()->findInt32("rtp-time", (int32_t *)&rtpTime));
+
+    to->meta()->setInt32("rtp-time", rtpTime);
+
+    // Copy the seq number.
+    to->setInt32Data(from->int32Data());
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPAssembler.h
@@ -0,0 +1,56 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_RTP_ASSEMBLER_H_
+
+#define A_RTP_ASSEMBLER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct ARTPSource;
+
+struct ARTPAssembler : public RefBase {
+    enum AssemblyStatus {
+        MALFORMED_PACKET,
+        WRONG_SEQUENCE_NUMBER,
+        NOT_ENOUGH_DATA,
+        OK
+    };
+
+    ARTPAssembler();
+
+    void onPacketReceived(const sp<ARTPSource> &source);
+    virtual void onByeReceived() = 0;
+
+protected:
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source) = 0;
+    virtual void packetLost() = 0;
+
+    static void CopyTimes(const sp<ABuffer> &to, const sp<ABuffer> &from);
+
+private:
+    int64_t mFirstFailureTimeUs;
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARTPAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_RTP_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPConnection.cpp
@@ -0,0 +1,676 @@
+/*
+ * Copyright (C) 2010 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 "ARTPConnection"
+#include <utils/Log.h>
+
+#include "ARTPConnection.h"
+
+#include "ARTPSource.h"
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+namespace android {
+
+static const size_t kMaxUDPSize = 1500;
+
+static uint16_t u16at(const uint8_t *data) {
+    return data[0] << 8 | data[1];
+}
+
+static uint32_t u32at(const uint8_t *data) {
+    return u16at(data) << 16 | u16at(&data[2]);
+}
+
+static uint64_t u64at(const uint8_t *data) {
+    return (uint64_t)(u32at(data)) << 32 | u32at(&data[4]);
+}
+
+// static
+const int64_t ARTPConnection::kSelectTimeoutUs = 1000ll;
+
+struct ARTPConnection::StreamInfo {
+    int mRTPSocket;
+    int mRTCPSocket;
+    sp<ASessionDescription> mSessionDesc;
+    size_t mIndex;
+    sp<AMessage> mNotifyMsg;
+    KeyedVector<uint32_t, sp<ARTPSource> > mSources;
+
+    int64_t mNumRTCPPacketsReceived;
+    int64_t mNumRTPPacketsReceived;
+    struct sockaddr_in mRemoteRTCPAddr;
+
+    bool mIsInjected;
+};
+
+ARTPConnection::ARTPConnection(uint32_t flags)
+    : mFlags(flags),
+      mPollEventPending(false),
+      mLastReceiverReportTimeUs(-1) {
+}
+
+ARTPConnection::~ARTPConnection() {
+}
+
+void ARTPConnection::addStream(
+        int rtpSocket, int rtcpSocket,
+        const sp<ASessionDescription> &sessionDesc,
+        size_t index,
+        const sp<AMessage> &notify,
+        bool injected) {
+    sp<AMessage> msg = new AMessage(kWhatAddStream, id());
+    msg->setInt32("rtp-socket", rtpSocket);
+    msg->setInt32("rtcp-socket", rtcpSocket);
+    msg->setObject("session-desc", sessionDesc);
+    msg->setSize("index", index);
+    msg->setMessage("notify", notify);
+    msg->setInt32("injected", injected);
+    msg->post();
+}
+
+void ARTPConnection::removeStream(int rtpSocket, int rtcpSocket) {
+    sp<AMessage> msg = new AMessage(kWhatRemoveStream, id());
+    msg->setInt32("rtp-socket", rtpSocket);
+    msg->setInt32("rtcp-socket", rtcpSocket);
+    msg->post();
+}
+
+static void bumpSocketBufferSize(int s) {
+    int size = 256 * 1024;
+    CHECK_EQ(setsockopt(s, SOL_SOCKET, SO_RCVBUF, &size, sizeof(size)), 0);
+}
+
+// static
+void ARTPConnection::MakePortPair(
+        int *rtpSocket, int *rtcpSocket, unsigned *rtpPort) {
+    *rtpSocket = socket(AF_INET, SOCK_DGRAM, 0);
+    CHECK_GE(*rtpSocket, 0);
+
+    bumpSocketBufferSize(*rtpSocket);
+
+    *rtcpSocket = socket(AF_INET, SOCK_DGRAM, 0);
+    CHECK_GE(*rtcpSocket, 0);
+
+    bumpSocketBufferSize(*rtcpSocket);
+
+    unsigned start = (rand() * 1000)/ RAND_MAX + 15550;
+    start &= ~1;
+
+    for (unsigned port = start; port < 65536; port += 2) {
+        struct sockaddr_in addr;
+        memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+        addr.sin_family = AF_INET;
+        addr.sin_addr.s_addr = htonl(INADDR_ANY);
+        addr.sin_port = htons(port);
+
+        if (bind(*rtpSocket,
+                 (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
+            continue;
+        }
+
+        addr.sin_port = htons(port + 1);
+
+        if (bind(*rtcpSocket,
+                 (const struct sockaddr *)&addr, sizeof(addr)) == 0) {
+            *rtpPort = port;
+            return;
+        }
+    }
+
+    TRESPASS();
+}
+
+void ARTPConnection::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatAddStream:
+        {
+            onAddStream(msg);
+            break;
+        }
+
+        case kWhatRemoveStream:
+        {
+            onRemoveStream(msg);
+            break;
+        }
+
+        case kWhatPollStreams:
+        {
+            onPollStreams();
+            break;
+        }
+
+        case kWhatInjectPacket:
+        {
+            onInjectPacket(msg);
+            break;
+        }
+
+        default:
+        {
+            TRESPASS();
+            break;
+        }
+    }
+}
+
+void ARTPConnection::onAddStream(const sp<AMessage> &msg) {
+    mStreams.push_back(StreamInfo());
+    StreamInfo *info = &*--mStreams.end();
+
+    int32_t s;
+    CHECK(msg->findInt32("rtp-socket", &s));
+    info->mRTPSocket = s;
+    CHECK(msg->findInt32("rtcp-socket", &s));
+    info->mRTCPSocket = s;
+
+    int32_t injected;
+    CHECK(msg->findInt32("injected", &injected));
+
+    info->mIsInjected = injected;
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("session-desc", &obj));
+    info->mSessionDesc = static_cast<ASessionDescription *>(obj.get());
+
+    CHECK(msg->findSize("index", &info->mIndex));
+    CHECK(msg->findMessage("notify", &info->mNotifyMsg));
+
+    info->mNumRTCPPacketsReceived = 0;
+    info->mNumRTPPacketsReceived = 0;
+    memset(&info->mRemoteRTCPAddr, 0, sizeof(info->mRemoteRTCPAddr));
+
+    if (!injected) {
+        postPollEvent();
+    }
+}
+
+void ARTPConnection::onRemoveStream(const sp<AMessage> &msg) {
+    int32_t rtpSocket, rtcpSocket;
+    CHECK(msg->findInt32("rtp-socket", &rtpSocket));
+    CHECK(msg->findInt32("rtcp-socket", &rtcpSocket));
+
+    List<StreamInfo>::iterator it = mStreams.begin();
+    while (it != mStreams.end()
+           && (it->mRTPSocket != rtpSocket || it->mRTCPSocket != rtcpSocket)) {
+        ++it;
+    }
+
+    if (it == mStreams.end()) {
+        return;
+    }
+
+    mStreams.erase(it);
+}
+
+void ARTPConnection::postPollEvent() {
+    if (mPollEventPending) {
+        return;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatPollStreams, id());
+    msg->post();
+
+    mPollEventPending = true;
+}
+
+void ARTPConnection::onPollStreams() {
+    mPollEventPending = false;
+
+    if (mStreams.empty()) {
+        return;
+    }
+
+    struct timeval tv;
+    tv.tv_sec = 0;
+    tv.tv_usec = kSelectTimeoutUs;
+
+    fd_set rs;
+    FD_ZERO(&rs);
+
+    int maxSocket = -1;
+    for (List<StreamInfo>::iterator it = mStreams.begin();
+         it != mStreams.end(); ++it) {
+        if ((*it).mIsInjected) {
+            continue;
+        }
+
+        FD_SET(it->mRTPSocket, &rs);
+        FD_SET(it->mRTCPSocket, &rs);
+
+        if (it->mRTPSocket > maxSocket) {
+            maxSocket = it->mRTPSocket;
+        }
+        if (it->mRTCPSocket > maxSocket) {
+            maxSocket = it->mRTCPSocket;
+        }
+    }
+
+    if (maxSocket == -1) {
+        return;
+    }
+
+    int res = select(maxSocket + 1, &rs, NULL, NULL, &tv);
+
+    if (res > 0) {
+        List<StreamInfo>::iterator it = mStreams.begin();
+        while (it != mStreams.end()) {
+            if ((*it).mIsInjected) {
+                ++it;
+                continue;
+            }
+
+            status_t err = OK;
+            if (FD_ISSET(it->mRTPSocket, &rs)) {
+                err = receive(&*it, true);
+            }
+            if (err == OK && FD_ISSET(it->mRTCPSocket, &rs)) {
+                err = receive(&*it, false);
+            }
+
+            if (err == -ECONNRESET) {
+                // socket failure, this stream is dead, Jim.
+
+                LOGW("failed to receive RTP/RTCP datagram.");
+                it = mStreams.erase(it);
+                continue;
+            }
+
+            ++it;
+        }
+    }
+
+    int64_t nowUs = ALooper::GetNowUs();
+    if (mLastReceiverReportTimeUs <= 0
+            || mLastReceiverReportTimeUs + 5000000ll <= nowUs) {
+        sp<ABuffer> buffer = new ABuffer(kMaxUDPSize);
+        List<StreamInfo>::iterator it = mStreams.begin();
+        while (it != mStreams.end()) {
+            StreamInfo *s = &*it;
+
+            if (s->mIsInjected) {
+                ++it;
+                continue;
+            }
+
+            if (s->mNumRTCPPacketsReceived == 0) {
+                // We have never received any RTCP packets on this stream,
+                // we don't even know where to send a report.
+                ++it;
+                continue;
+            }
+
+            buffer->setRange(0, 0);
+
+            for (size_t i = 0; i < s->mSources.size(); ++i) {
+                sp<ARTPSource> source = s->mSources.valueAt(i);
+
+                source->addReceiverReport(buffer);
+
+                if (mFlags & kRegularlyRequestFIR) {
+                    source->addFIR(buffer);
+                }
+            }
+
+            if (buffer->size() > 0) {
+                LOGV("Sending RR...");
+
+                ssize_t n;
+                do {
+                    n = sendto(
+                        s->mRTCPSocket, buffer->data(), buffer->size(), 0,
+                        (const struct sockaddr *)&s->mRemoteRTCPAddr,
+                        sizeof(s->mRemoteRTCPAddr));
+                } while (n < 0 && errno == EINTR);
+
+                if (n <= 0) {
+                    LOGW("failed to send RTCP receiver report (%s).",
+                         n == 0 ? "connection gone" : strerror(errno));
+
+                    it = mStreams.erase(it);
+                    continue;
+                }
+
+                CHECK_EQ(n, (ssize_t)buffer->size());
+
+                mLastReceiverReportTimeUs = nowUs;
+            }
+
+            ++it;
+        }
+    }
+
+    if (!mStreams.empty()) {
+        postPollEvent();
+    }
+}
+
+status_t ARTPConnection::receive(StreamInfo *s, bool receiveRTP) {
+    LOGV("receiving %s", receiveRTP ? "RTP" : "RTCP");
+
+    CHECK(!s->mIsInjected);
+
+    sp<ABuffer> buffer = new ABuffer(65536);
+
+    socklen_t remoteAddrLen =
+        (!receiveRTP && s->mNumRTCPPacketsReceived == 0)
+            ? sizeof(s->mRemoteRTCPAddr) : 0;
+
+    ssize_t nbytes;
+    do {
+        nbytes = recvfrom(
+            receiveRTP ? s->mRTPSocket : s->mRTCPSocket,
+            buffer->data(),
+            buffer->capacity(),
+            0,
+            remoteAddrLen > 0 ? (struct sockaddr *)&s->mRemoteRTCPAddr : NULL,
+            remoteAddrLen > 0 ? &remoteAddrLen : NULL);
+    } while (nbytes < 0 && errno == EINTR);
+
+    if (nbytes <= 0) {
+        return -ECONNRESET;
+    }
+
+    buffer->setRange(0, nbytes);
+
+    // LOGI("received %d bytes.", buffer->size());
+
+    status_t err;
+    if (receiveRTP) {
+        err = parseRTP(s, buffer);
+    } else {
+        err = parseRTCP(s, buffer);
+    }
+
+    return err;
+}
+
+status_t ARTPConnection::parseRTP(StreamInfo *s, const sp<ABuffer> &buffer) {
+    if (s->mNumRTPPacketsReceived++ == 0) {
+        sp<AMessage> notify = s->mNotifyMsg->dup();
+        notify->setInt32("first-rtp", true);
+        notify->post();
+    }
+
+    size_t size = buffer->size();
+
+    if (size < 12) {
+        // Too short to be a valid RTP header.
+        return -1;
+    }
+
+    const uint8_t *data = buffer->data();
+
+    if ((data[0] >> 6) != 2) {
+        // Unsupported version.
+        return -1;
+    }
+
+    if (data[0] & 0x20) {
+        // Padding present.
+
+        size_t paddingLength = data[size - 1];
+
+        if (paddingLength + 12 > size) {
+            // If we removed this much padding we'd end up with something
+            // that's too short to be a valid RTP header.
+            return -1;
+        }
+
+        size -= paddingLength;
+    }
+
+    int numCSRCs = data[0] & 0x0f;
+
+    size_t payloadOffset = 12 + 4 * numCSRCs;
+
+    if (size < payloadOffset) {
+        // Not enough data to fit the basic header and all the CSRC entries.
+        return -1;
+    }
+
+    if (data[0] & 0x10) {
+        // Header eXtension present.
+
+        if (size < payloadOffset + 4) {
+            // Not enough data to fit the basic header, all CSRC entries
+            // and the first 4 bytes of the extension header.
+
+            return -1;
+        }
+
+        const uint8_t *extensionData = &data[payloadOffset];
+
+        size_t extensionLength =
+            4 * (extensionData[2] << 8 | extensionData[3]);
+
+        if (size < payloadOffset + 4 + extensionLength) {
+            return -1;
+        }
+
+        payloadOffset += 4 + extensionLength;
+    }
+
+    uint32_t srcId = u32at(&data[8]);
+
+    sp<ARTPSource> source = findSource(s, srcId);
+
+    uint32_t rtpTime = u32at(&data[4]);
+
+    sp<AMessage> meta = buffer->meta();
+    meta->setInt32("ssrc", srcId);
+    meta->setInt32("rtp-time", rtpTime);
+    meta->setInt32("PT", data[1] & 0x7f);
+    meta->setInt32("M", data[1] >> 7);
+
+    buffer->setInt32Data(u16at(&data[2]));
+    buffer->setRange(payloadOffset, size - payloadOffset);
+
+    source->processRTPPacket(buffer);
+
+    return OK;
+}
+
+status_t ARTPConnection::parseRTCP(StreamInfo *s, const sp<ABuffer> &buffer) {
+    if (s->mNumRTCPPacketsReceived++ == 0) {
+        sp<AMessage> notify = s->mNotifyMsg->dup();
+        notify->setInt32("first-rtcp", true);
+        notify->post();
+    }
+
+    const uint8_t *data = buffer->data();
+    size_t size = buffer->size();
+
+    while (size > 0) {
+        if (size < 8) {
+            // Too short to be a valid RTCP header
+            return -1;
+        }
+
+        if ((data[0] >> 6) != 2) {
+            // Unsupported version.
+            return -1;
+        }
+
+        if (data[0] & 0x20) {
+            // Padding present.
+
+            size_t paddingLength = data[size - 1];
+
+            if (paddingLength + 12 > size) {
+                // If we removed this much padding we'd end up with something
+                // that's too short to be a valid RTP header.
+                return -1;
+            }
+
+            size -= paddingLength;
+        }
+
+        size_t headerLength = 4 * (data[2] << 8 | data[3]) + 4;
+
+        if (size < headerLength) {
+            // Only received a partial packet?
+            return -1;
+        }
+
+        switch (data[1]) {
+            case 200:
+            {
+                parseSR(s, data, headerLength);
+                break;
+            }
+
+            case 201:  // RR
+            case 202:  // SDES
+            case 204:  // APP
+                break;
+
+            case 205:  // TSFB (transport layer specific feedback)
+            case 206:  // PSFB (payload specific feedback)
+                // hexdump(data, headerLength);
+                break;
+
+            case 203:
+            {
+                parseBYE(s, data, headerLength);
+                break;
+            }
+
+            default:
+            {
+                LOGW("Unknown RTCP packet type %u of size %d",
+                     (unsigned)data[1], headerLength);
+                break;
+            }
+        }
+
+        data += headerLength;
+        size -= headerLength;
+    }
+
+    return OK;
+}
+
+status_t ARTPConnection::parseBYE(
+        StreamInfo *s, const uint8_t *data, size_t size) {
+    size_t SC = data[0] & 0x3f;
+
+    if (SC == 0 || size < (4 + SC * 4)) {
+        // Packet too short for the minimal BYE header.
+        return -1;
+    }
+
+    uint32_t id = u32at(&data[4]);
+
+    sp<ARTPSource> source = findSource(s, id);
+
+    source->byeReceived();
+
+    return OK;
+}
+
+status_t ARTPConnection::parseSR(
+        StreamInfo *s, const uint8_t *data, size_t size) {
+    size_t RC = data[0] & 0x1f;
+
+    if (size < (7 + RC * 6) * 4) {
+        // Packet too short for the minimal SR header.
+        return -1;
+    }
+
+    uint32_t id = u32at(&data[4]);
+    uint64_t ntpTime = u64at(&data[8]);
+    uint32_t rtpTime = u32at(&data[16]);
+
+#if 0
+    LOGI("XXX timeUpdate: ssrc=0x%08x, rtpTime %u == ntpTime %.3f",
+         id,
+         rtpTime,
+         (ntpTime >> 32) + (double)(ntpTime & 0xffffffff) / (1ll << 32));
+#endif
+
+    sp<ARTPSource> source = findSource(s, id);
+
+    source->timeUpdate(rtpTime, ntpTime);
+
+    return 0;
+}
+
+sp<ARTPSource> ARTPConnection::findSource(StreamInfo *info, uint32_t srcId) {
+    sp<ARTPSource> source;
+    ssize_t index = info->mSources.indexOfKey(srcId);
+    if (index < 0) {
+        index = info->mSources.size();
+
+        source = new ARTPSource(
+                srcId, info->mSessionDesc, info->mIndex, info->mNotifyMsg);
+
+        info->mSources.add(srcId, source);
+    } else {
+        source = info->mSources.valueAt(index);
+    }
+
+    return source;
+}
+
+void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) {
+    sp<AMessage> msg = new AMessage(kWhatInjectPacket, id());
+    msg->setInt32("index", index);
+    msg->setObject("buffer", buffer);
+    msg->post();
+}
+
+void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) {
+    int32_t index;
+    CHECK(msg->findInt32("index", &index));
+
+    sp<RefBase> obj;
+    CHECK(msg->findObject("buffer", &obj));
+
+    sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+
+    List<StreamInfo>::iterator it = mStreams.begin();
+    while (it != mStreams.end()
+           && it->mRTPSocket != index && it->mRTCPSocket != index) {
+        ++it;
+    }
+
+    if (it == mStreams.end()) {
+        TRESPASS();
+    }
+
+    StreamInfo *s = &*it;
+
+    status_t err;
+    if (it->mRTPSocket == index) {
+        err = parseRTP(s, buffer);
+    } else {
+        err = parseRTCP(s, buffer);
+    }
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPConnection.h
@@ -0,0 +1,97 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_RTP_CONNECTION_H_
+
+#define A_RTP_CONNECTION_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <utils/List.h>
+
+namespace android {
+
+struct ABuffer;
+struct ARTPSource;
+struct ASessionDescription;
+
+struct ARTPConnection : public AHandler {
+    enum Flags {
+        kRegularlyRequestFIR = 2,
+    };
+
+    ARTPConnection(uint32_t flags = 0);
+
+    void addStream(
+            int rtpSocket, int rtcpSocket,
+            const sp<ASessionDescription> &sessionDesc, size_t index,
+            const sp<AMessage> &notify,
+            bool injected);
+
+    void removeStream(int rtpSocket, int rtcpSocket);
+
+    void injectPacket(int index, const sp<ABuffer> &buffer);
+
+    // Creates a pair of UDP datagram sockets bound to adjacent ports
+    // (the rtpSocket is bound to an even port, the rtcpSocket to the
+    // next higher port).
+    static void MakePortPair(
+            int *rtpSocket, int *rtcpSocket, unsigned *rtpPort);
+
+protected:
+    virtual ~ARTPConnection();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum {
+        kWhatAddStream,
+        kWhatRemoveStream,
+        kWhatPollStreams,
+        kWhatInjectPacket,
+    };
+
+    static const int64_t kSelectTimeoutUs;
+
+    uint32_t mFlags;
+
+    struct StreamInfo;
+    List<StreamInfo> mStreams;
+
+    bool mPollEventPending;
+    int64_t mLastReceiverReportTimeUs;
+
+    void onAddStream(const sp<AMessage> &msg);
+    void onRemoveStream(const sp<AMessage> &msg);
+    void onPollStreams();
+    void onInjectPacket(const sp<AMessage> &msg);
+    void onSendReceiverReports();
+
+    status_t receive(StreamInfo *info, bool receiveRTP);
+
+    status_t parseRTP(StreamInfo *info, const sp<ABuffer> &buffer);
+    status_t parseRTCP(StreamInfo *info, const sp<ABuffer> &buffer);
+    status_t parseSR(StreamInfo *info, const uint8_t *data, size_t size);
+    status_t parseBYE(StreamInfo *info, const uint8_t *data, size_t size);
+
+    sp<ARTPSource> findSource(StreamInfo *info, uint32_t id);
+
+    void postPollEvent();
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARTPConnection);
+};
+
+}  // namespace android
+
+#endif  // A_RTP_CONNECTION_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPSession.cpp
@@ -0,0 +1,244 @@
+/*
+ * Copyright (C) 2010 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 "ARTPSession"
+#include <utils/Log.h>
+
+#include "ARTPSession.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+
+#include <ctype.h>
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#include "APacketSource.h"
+#include "ARTPConnection.h"
+#include "ASessionDescription.h"
+
+namespace android {
+
+ARTPSession::ARTPSession()
+    : mInitCheck(NO_INIT) {
+}
+
+status_t ARTPSession::setup(const sp<ASessionDescription> &desc) {
+    CHECK_EQ(mInitCheck, (status_t)NO_INIT);
+
+    mDesc = desc;
+
+    mRTPConn = new ARTPConnection(ARTPConnection::kRegularlyRequestFIR);
+
+    looper()->registerHandler(mRTPConn);
+
+    for (size_t i = 1; i < mDesc->countTracks(); ++i) {
+        AString connection;
+        if (!mDesc->findAttribute(i, "c=", &connection)) {
+            // No per-stream connection information, try global fallback.
+            if (!mDesc->findAttribute(0, "c=", &connection)) {
+                LOGE("Unable to find connection attribute.");
+                return mInitCheck;
+            }
+        }
+        if (!(connection == "IN IP4 127.0.0.1")) {
+            LOGE("We only support localhost connections for now.");
+            return mInitCheck;
+        }
+
+        unsigned port;
+        if (!validateMediaFormat(i, &port) || (port & 1) != 0) {
+            LOGE("Invalid media format.");
+            return mInitCheck;
+        }
+
+        sp<APacketSource> source = new APacketSource(mDesc, i);
+        if (source->initCheck() != OK) {
+            LOGE("Unsupported format.");
+            return mInitCheck;
+        }
+
+        int rtpSocket = MakeUDPSocket(port);
+        int rtcpSocket = MakeUDPSocket(port + 1);
+
+        mTracks.push(TrackInfo());
+        TrackInfo *info = &mTracks.editItemAt(mTracks.size() - 1);
+        info->mRTPSocket = rtpSocket;
+        info->mRTCPSocket = rtcpSocket;
+
+        sp<AMessage> notify = new AMessage(kWhatAccessUnitComplete, id());
+        notify->setSize("track-index", mTracks.size() - 1);
+
+        mRTPConn->addStream(
+                rtpSocket, rtcpSocket, mDesc, i, notify, false /* injected */);
+
+        info->mPacketSource = source;
+    }
+
+    mInitCheck = OK;
+
+    return OK;
+}
+
+// static
+int ARTPSession::MakeUDPSocket(unsigned port) {
+    int s = socket(AF_INET, SOCK_DGRAM, 0);
+    CHECK_GE(s, 0);
+
+    struct sockaddr_in addr;
+    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+    addr.sin_family = AF_INET;
+    addr.sin_addr.s_addr = INADDR_ANY;
+    addr.sin_port = htons(port);
+
+    CHECK_EQ(0, bind(s, (const struct sockaddr *)&addr, sizeof(addr)));
+
+    return s;
+}
+
+ARTPSession::~ARTPSession() {
+    for (size_t i = 0; i < mTracks.size(); ++i) {
+        TrackInfo *info = &mTracks.editItemAt(i);
+
+        info->mPacketSource->signalEOS(UNKNOWN_ERROR);
+
+        close(info->mRTPSocket);
+        close(info->mRTCPSocket);
+    }
+}
+
+void ARTPSession::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatAccessUnitComplete:
+        {
+            int32_t firstRTCP;
+            if (msg->findInt32("first-rtcp", &firstRTCP)) {
+                // There won't be an access unit here, it's just a notification
+                // that the data communication worked since we got the first
+                // rtcp packet.
+                break;
+            }
+
+            size_t trackIndex;
+            CHECK(msg->findSize("track-index", &trackIndex));
+
+            int32_t eos;
+            if (msg->findInt32("eos", &eos) && eos) {
+                TrackInfo *info = &mTracks.editItemAt(trackIndex);
+                info->mPacketSource->signalEOS(ERROR_END_OF_STREAM);
+                break;
+            }
+
+            sp<RefBase> obj;
+            CHECK(msg->findObject("access-unit", &obj));
+
+            sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+
+            uint64_t ntpTime;
+            CHECK(accessUnit->meta()->findInt64(
+                        "ntp-time", (int64_t *)&ntpTime));
+
+#if 0
+#if 0
+            printf("access unit complete size=%d\tntp-time=0x%016llx\n",
+                   accessUnit->size(), ntpTime);
+#else
+            LOGI("access unit complete, size=%d, ntp-time=%llu",
+                 accessUnit->size(), ntpTime);
+            hexdump(accessUnit->data(), accessUnit->size());
+#endif
+#endif
+
+#if 0
+            CHECK_GE(accessUnit->size(), 5u);
+            CHECK(!memcmp("\x00\x00\x00\x01", accessUnit->data(), 4));
+            unsigned x = accessUnit->data()[4];
+
+            LOGI("access unit complete: nalType=0x%02x, nalRefIdc=0x%02x",
+                 x & 0x1f, (x & 0x60) >> 5);
+#endif
+
+            accessUnit->meta()->setInt64("ntp-time", ntpTime);
+            accessUnit->meta()->setInt64("timeUs", 0);
+
+#if 0
+            int32_t damaged;
+            if (accessUnit->meta()->findInt32("damaged", &damaged)
+                    && damaged != 0) {
+                LOGI("ignoring damaged AU");
+            } else
+#endif
+            {
+                TrackInfo *info = &mTracks.editItemAt(trackIndex);
+                info->mPacketSource->queueAccessUnit(accessUnit);
+            }
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+bool ARTPSession::validateMediaFormat(size_t index, unsigned *port) const {
+    AString format;
+    mDesc->getFormat(index, &format);
+
+    ssize_t i = format.find(" ");
+    if (i < 0) {
+        return false;
+    }
+
+    ++i;
+    size_t j = i;
+    while (isdigit(format.c_str()[j])) {
+        ++j;
+    }
+    if (format.c_str()[j] != ' ') {
+        return false;
+    }
+
+    AString portString(format, i, j - i);
+
+    char *end;
+    unsigned long x = strtoul(portString.c_str(), &end, 10);
+    if (end == portString.c_str() || *end != '\0') {
+        return false;
+    }
+
+    if (x == 0 || x > 65535) {
+        return false;
+    }
+
+    *port = x;
+
+    return true;
+}
+
+size_t ARTPSession::countTracks() {
+    return mTracks.size();
+}
+
+sp<MediaSource> ARTPSession::trackAt(size_t index) {
+    CHECK_LT(index, mTracks.size());
+    return mTracks.editItemAt(index).mPacketSource;
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPSession.h
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_RTP_SESSION_H_
+
+#define A_RTP_SESSION_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+
+namespace android {
+
+struct APacketSource;
+struct ARTPConnection;
+struct ASessionDescription;
+struct MediaSource;
+
+struct ARTPSession : public AHandler {
+    ARTPSession();
+
+    status_t setup(const sp<ASessionDescription> &desc);
+
+    size_t countTracks();
+    sp<MediaSource> trackAt(size_t index);
+
+protected:
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+    virtual ~ARTPSession();
+
+private:
+    enum {
+        kWhatAccessUnitComplete = 'accu'
+    };
+
+    struct TrackInfo {
+        int mRTPSocket;
+        int mRTCPSocket;
+
+        sp<APacketSource> mPacketSource;
+    };
+
+    status_t mInitCheck;
+    sp<ASessionDescription> mDesc;
+    sp<ARTPConnection> mRTPConn;
+
+    Vector<TrackInfo> mTracks;
+
+    bool validateMediaFormat(size_t index, unsigned *port) const;
+    static int MakeUDPSocket(unsigned port);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARTPSession);
+};
+
+}  // namespace android
+
+#endif  // A_RTP_SESSION_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPSource.cpp
@@ -0,0 +1,275 @@
+/*
+ * Copyright (C) 2010 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 "ARTPSource"
+#include <utils/Log.h>
+
+#include "ARTPSource.h"
+
+#include "AAMRAssembler.h"
+#include "AAVCAssembler.h"
+#include "AH263Assembler.h"
+#include "AMPEG4AudioAssembler.h"
+#include "AMPEG4ElementaryAssembler.h"
+#include "ARawAudioAssembler.h"
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+
+namespace android {
+
+static const uint32_t kSourceID = 0xdeadbeef;
+
+ARTPSource::ARTPSource(
+        uint32_t id,
+        const sp<ASessionDescription> &sessionDesc, size_t index,
+        const sp<AMessage> &notify)
+    : mID(id),
+      mHighestSeqNumber(0),
+      mNumBuffersReceived(0),
+      mLastNTPTime(0),
+      mLastNTPTimeUpdateUs(0),
+      mIssueFIRRequests(false),
+      mLastFIRRequestUs(-1),
+      mNextFIRSeqNo((rand() * 256.0) / RAND_MAX),
+      mNotify(notify) {
+    unsigned long PT;
+    AString desc;
+    AString params;
+    sessionDesc->getFormatType(index, &PT, &desc, &params);
+
+    if (!strncmp(desc.c_str(), "H264/", 5)) {
+        mAssembler = new AAVCAssembler(notify);
+        mIssueFIRRequests = true;
+    } else if (!strncmp(desc.c_str(), "MP4A-LATM/", 10)) {
+        mAssembler = new AMPEG4AudioAssembler(notify, params);
+    } else if (!strncmp(desc.c_str(), "H263-1998/", 10)
+            || !strncmp(desc.c_str(), "H263-2000/", 10)) {
+        mAssembler = new AH263Assembler(notify);
+        mIssueFIRRequests = true;
+    } else if (!strncmp(desc.c_str(), "AMR/", 4)) {
+        mAssembler = new AAMRAssembler(notify, false /* isWide */, params);
+    } else  if (!strncmp(desc.c_str(), "AMR-WB/", 7)) {
+        mAssembler = new AAMRAssembler(notify, true /* isWide */, params);
+    } else if (!strncmp(desc.c_str(), "MP4V-ES/", 8)
+            || !strncasecmp(desc.c_str(), "mpeg4-generic/", 14)) {
+        mAssembler = new AMPEG4ElementaryAssembler(notify, desc, params);
+        mIssueFIRRequests = true;
+    } else if (ARawAudioAssembler::Supports(desc.c_str())) {
+        mAssembler = new ARawAudioAssembler(notify, desc.c_str(), params);
+    } else {
+        TRESPASS();
+    }
+}
+
+static uint32_t AbsDiff(uint32_t seq1, uint32_t seq2) {
+    return seq1 > seq2 ? seq1 - seq2 : seq2 - seq1;
+}
+
+void ARTPSource::processRTPPacket(const sp<ABuffer> &buffer) {
+    if (queuePacket(buffer) && mAssembler != NULL) {
+        mAssembler->onPacketReceived(this);
+    }
+}
+
+void ARTPSource::timeUpdate(uint32_t rtpTime, uint64_t ntpTime) {
+    mLastNTPTime = ntpTime;
+    mLastNTPTimeUpdateUs = ALooper::GetNowUs();
+
+    sp<AMessage> notify = mNotify->dup();
+    notify->setInt32("time-update", true);
+    notify->setInt32("rtp-time", rtpTime);
+    notify->setInt64("ntp-time", ntpTime);
+    notify->post();
+}
+
+bool ARTPSource::queuePacket(const sp<ABuffer> &buffer) {
+    uint32_t seqNum = (uint32_t)buffer->int32Data();
+
+    if (mNumBuffersReceived++ == 0) {
+        mHighestSeqNumber = seqNum;
+        mQueue.push_back(buffer);
+        return true;
+    }
+
+    // Only the lower 16-bit of the sequence numbers are transmitted,
+    // derive the high-order bits by choosing the candidate closest
+    // to the highest sequence number (extended to 32 bits) received so far.
+
+    uint32_t seq1 = seqNum | (mHighestSeqNumber & 0xffff0000);
+    uint32_t seq2 = seqNum | ((mHighestSeqNumber & 0xffff0000) + 0x10000);
+    uint32_t seq3 = seqNum | ((mHighestSeqNumber & 0xffff0000) - 0x10000);
+    uint32_t diff1 = AbsDiff(seq1, mHighestSeqNumber);
+    uint32_t diff2 = AbsDiff(seq2, mHighestSeqNumber);
+    uint32_t diff3 = AbsDiff(seq3, mHighestSeqNumber);
+
+    if (diff1 < diff2) {
+        if (diff1 < diff3) {
+            // diff1 < diff2 ^ diff1 < diff3
+            seqNum = seq1;
+        } else {
+            // diff3 <= diff1 < diff2
+            seqNum = seq3;
+        }
+    } else if (diff2 < diff3) {
+        // diff2 <= diff1 ^ diff2 < diff3
+        seqNum = seq2;
+    } else {
+        // diff3 <= diff2 <= diff1
+        seqNum = seq3;
+    }
+
+    if (seqNum > mHighestSeqNumber) {
+        mHighestSeqNumber = seqNum;
+    }
+
+    buffer->setInt32Data(seqNum);
+
+    List<sp<ABuffer> >::iterator it = mQueue.begin();
+    while (it != mQueue.end() && (uint32_t)(*it)->int32Data() < seqNum) {
+        ++it;
+    }
+
+    if (it != mQueue.end() && (uint32_t)(*it)->int32Data() == seqNum) {
+        LOGW("Discarding duplicate buffer");
+        return false;
+    }
+
+    mQueue.insert(it, buffer);
+
+    return true;
+}
+
+void ARTPSource::byeReceived() {
+    mAssembler->onByeReceived();
+}
+
+void ARTPSource::addFIR(const sp<ABuffer> &buffer) {
+    if (!mIssueFIRRequests) {
+        return;
+    }
+
+    int64_t nowUs = ALooper::GetNowUs();
+    if (mLastFIRRequestUs >= 0 && mLastFIRRequestUs + 5000000ll > nowUs) {
+        // Send FIR requests at most every 5 secs.
+        return;
+    }
+
+    mLastFIRRequestUs = nowUs;
+
+    if (buffer->size() + 20 > buffer->capacity()) {
+        LOGW("RTCP buffer too small to accomodate FIR.");
+        return;
+    }
+
+    uint8_t *data = buffer->data() + buffer->size();
+
+    data[0] = 0x80 | 4;
+    data[1] = 206;  // PSFB
+    data[2] = 0;
+    data[3] = 4;
+    data[4] = kSourceID >> 24;
+    data[5] = (kSourceID >> 16) & 0xff;
+    data[6] = (kSourceID >> 8) & 0xff;
+    data[7] = kSourceID & 0xff;
+
+    data[8] = 0x00;  // SSRC of media source (unused)
+    data[9] = 0x00;
+    data[10] = 0x00;
+    data[11] = 0x00;
+
+    data[12] = mID >> 24;
+    data[13] = (mID >> 16) & 0xff;
+    data[14] = (mID >> 8) & 0xff;
+    data[15] = mID & 0xff;
+
+    data[16] = mNextFIRSeqNo++;  // Seq Nr.
+
+    data[17] = 0x00;  // Reserved
+    data[18] = 0x00;
+    data[19] = 0x00;
+
+    buffer->setRange(buffer->offset(), buffer->size() + 20);
+
+    LOGV("Added FIR request.");
+}
+
+void ARTPSource::addReceiverReport(const sp<ABuffer> &buffer) {
+    if (buffer->size() + 32 > buffer->capacity()) {
+        LOGW("RTCP buffer too small to accomodate RR.");
+        return;
+    }
+
+    uint8_t *data = buffer->data() + buffer->size();
+
+    data[0] = 0x80 | 1;
+    data[1] = 201;  // RR
+    data[2] = 0;
+    data[3] = 7;
+    data[4] = kSourceID >> 24;
+    data[5] = (kSourceID >> 16) & 0xff;
+    data[6] = (kSourceID >> 8) & 0xff;
+    data[7] = kSourceID & 0xff;
+
+    data[8] = mID >> 24;
+    data[9] = (mID >> 16) & 0xff;
+    data[10] = (mID >> 8) & 0xff;
+    data[11] = mID & 0xff;
+
+    data[12] = 0x00;  // fraction lost
+
+    data[13] = 0x00;  // cumulative lost
+    data[14] = 0x00;
+    data[15] = 0x00;
+
+    data[16] = mHighestSeqNumber >> 24;
+    data[17] = (mHighestSeqNumber >> 16) & 0xff;
+    data[18] = (mHighestSeqNumber >> 8) & 0xff;
+    data[19] = mHighestSeqNumber & 0xff;
+
+    data[20] = 0x00;  // Interarrival jitter
+    data[21] = 0x00;
+    data[22] = 0x00;
+    data[23] = 0x00;
+
+    uint32_t LSR = 0;
+    uint32_t DLSR = 0;
+    if (mLastNTPTime != 0) {
+        LSR = (mLastNTPTime >> 16) & 0xffffffff;
+
+        DLSR = (uint32_t)
+            ((ALooper::GetNowUs() - mLastNTPTimeUpdateUs) * 65536.0 / 1E6);
+    }
+
+    data[24] = LSR >> 24;
+    data[25] = (LSR >> 16) & 0xff;
+    data[26] = (LSR >> 8) & 0xff;
+    data[27] = LSR & 0xff;
+
+    data[28] = DLSR >> 24;
+    data[29] = (DLSR >> 16) & 0xff;
+    data[30] = (DLSR >> 8) & 0xff;
+    data[31] = DLSR & 0xff;
+
+    buffer->setRange(buffer->offset(), buffer->size() + 32);
+}
+
+}  // namespace android
+
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPSource.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_RTP_SOURCE_H_
+
+#define A_RTP_SOURCE_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ABuffer;
+struct AMessage;
+struct ARTPAssembler;
+struct ASessionDescription;
+
+struct ARTPSource : public RefBase {
+    ARTPSource(
+            uint32_t id,
+            const sp<ASessionDescription> &sessionDesc, size_t index,
+            const sp<AMessage> &notify);
+
+    void processRTPPacket(const sp<ABuffer> &buffer);
+    void timeUpdate(uint32_t rtpTime, uint64_t ntpTime);
+    void byeReceived();
+
+    List<sp<ABuffer> > *queue() { return &mQueue; }
+
+    void addReceiverReport(const sp<ABuffer> &buffer);
+    void addFIR(const sp<ABuffer> &buffer);
+
+private:
+    uint32_t mID;
+    uint32_t mHighestSeqNumber;
+    int32_t mNumBuffersReceived;
+
+    List<sp<ABuffer> > mQueue;
+    sp<ARTPAssembler> mAssembler;
+
+    uint64_t mLastNTPTime;
+    int64_t mLastNTPTimeUpdateUs;
+
+    bool mIssueFIRRequests;
+    int64_t mLastFIRRequestUs;
+    uint8_t mNextFIRSeqNo;
+
+    sp<AMessage> mNotify;
+
+    bool queuePacket(const sp<ABuffer> &buffer);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARTPSource);
+};
+
+}  // namespace android
+
+#endif  // A_RTP_SOURCE_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPWriter.cpp
@@ -0,0 +1,836 @@
+/*
+ * Copyright (C) 2010 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 "ARTPWriter"
+#include <utils/Log.h>
+
+#include "ARTPWriter.h"
+
+#include <fcntl.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/ByteOrder.h>
+
+#define PT      97
+#define PT_STR  "97"
+
+namespace android {
+
+// static const size_t kMaxPacketSize = 65507;  // maximum payload in UDP over IP
+static const size_t kMaxPacketSize = 1500;
+
+static int UniformRand(int limit) {
+    return ((double)rand() * limit) / RAND_MAX;
+}
+
+ARTPWriter::ARTPWriter(int fd)
+    : mFlags(0),
+      mFd(dup(fd)),
+      mLooper(new ALooper),
+      mReflector(new AHandlerReflector<ARTPWriter>(this)) {
+    CHECK_GE(fd, 0);
+
+    mLooper->setName("rtp writer");
+    mLooper->registerHandler(mReflector);
+    mLooper->start();
+
+    mSocket = socket(AF_INET, SOCK_DGRAM, 0);
+    CHECK_GE(mSocket, 0);
+
+    memset(mRTPAddr.sin_zero, 0, sizeof(mRTPAddr.sin_zero));
+    mRTPAddr.sin_family = AF_INET;
+
+#if 1
+    mRTPAddr.sin_addr.s_addr = INADDR_ANY;
+#else
+    mRTPAddr.sin_addr.s_addr = inet_addr("172.19.18.246");
+#endif
+
+    mRTPAddr.sin_port = htons(5634);
+    CHECK_EQ(0, ntohs(mRTPAddr.sin_port) & 1);
+
+    mRTCPAddr = mRTPAddr;
+    mRTCPAddr.sin_port = htons(ntohs(mRTPAddr.sin_port) | 1);
+
+#if LOG_TO_FILES
+    mRTPFd = open(
+            "/data/misc/rtpout.bin",
+            O_WRONLY | O_CREAT | O_TRUNC,
+            0644);
+    CHECK_GE(mRTPFd, 0);
+
+    mRTCPFd = open(
+            "/data/misc/rtcpout.bin",
+            O_WRONLY | O_CREAT | O_TRUNC,
+            0644);
+    CHECK_GE(mRTCPFd, 0);
+#endif
+}
+
+ARTPWriter::~ARTPWriter() {
+#if LOG_TO_FILES
+    close(mRTCPFd);
+    mRTCPFd = -1;
+
+    close(mRTPFd);
+    mRTPFd = -1;
+#endif
+
+    close(mSocket);
+    mSocket = -1;
+
+    close(mFd);
+    mFd = -1;
+}
+
+status_t ARTPWriter::addSource(const sp<MediaSource> &source) {
+    mSource = source;
+    return OK;
+}
+
+bool ARTPWriter::reachedEOS() {
+    Mutex::Autolock autoLock(mLock);
+    return (mFlags & kFlagEOS) != 0;
+}
+
+status_t ARTPWriter::start(MetaData *params) {
+    Mutex::Autolock autoLock(mLock);
+    if (mFlags & kFlagStarted) {
+        return INVALID_OPERATION;
+    }
+
+    mFlags &= ~kFlagEOS;
+    mSourceID = rand();
+    mSeqNo = UniformRand(65536);
+    mRTPTimeBase = rand();
+    mNumRTPSent = 0;
+    mNumRTPOctetsSent = 0;
+    mLastRTPTime = 0;
+    mLastNTPTime = 0;
+    mNumSRsSent = 0;
+
+    const char *mime;
+    CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+
+    mMode = INVALID;
+    if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_AVC)) {
+        mMode = H264;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_VIDEO_H263)) {
+        mMode = H263;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
+        mMode = AMR_NB;
+    } else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
+        mMode = AMR_WB;
+    } else {
+        TRESPASS();
+    }
+
+    (new AMessage(kWhatStart, mReflector->id()))->post();
+
+    while (!(mFlags & kFlagStarted)) {
+        mCondition.wait(mLock);
+    }
+
+    return OK;
+}
+
+status_t ARTPWriter::stop() {
+    Mutex::Autolock autoLock(mLock);
+    if (!(mFlags & kFlagStarted)) {
+        return OK;
+    }
+
+    (new AMessage(kWhatStop, mReflector->id()))->post();
+
+    while (mFlags & kFlagStarted) {
+        mCondition.wait(mLock);
+    }
+    return OK;
+}
+
+status_t ARTPWriter::pause() {
+    return OK;
+}
+
+static void StripStartcode(MediaBuffer *buffer) {
+    if (buffer->range_length() < 4) {
+        return;
+    }
+
+    const uint8_t *ptr =
+        (const uint8_t *)buffer->data() + buffer->range_offset();
+
+    if (!memcmp(ptr, "\x00\x00\x00\x01", 4)) {
+        buffer->set_range(
+                buffer->range_offset() + 4, buffer->range_length() - 4);
+    }
+}
+
+void ARTPWriter::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatStart:
+        {
+            CHECK_EQ(mSource->start(), (status_t)OK);
+
+#if 0
+            if (mMode == H264) {
+                MediaBuffer *buffer;
+                CHECK_EQ(mSource->read(&buffer), (status_t)OK);
+
+                StripStartcode(buffer);
+                makeH264SPropParamSets(buffer);
+                buffer->release();
+                buffer = NULL;
+            }
+
+            dumpSessionDesc();
+#endif
+
+            {
+                Mutex::Autolock autoLock(mLock);
+                mFlags |= kFlagStarted;
+                mCondition.signal();
+            }
+
+            (new AMessage(kWhatRead, mReflector->id()))->post();
+            (new AMessage(kWhatSendSR, mReflector->id()))->post();
+            break;
+        }
+
+        case kWhatStop:
+        {
+            CHECK_EQ(mSource->stop(), (status_t)OK);
+
+            sendBye();
+
+            {
+                Mutex::Autolock autoLock(mLock);
+                mFlags &= ~kFlagStarted;
+                mCondition.signal();
+            }
+            break;
+        }
+
+        case kWhatRead:
+        {
+            {
+                Mutex::Autolock autoLock(mLock);
+                if (!(mFlags & kFlagStarted)) {
+                    break;
+                }
+            }
+
+            onRead(msg);
+            break;
+        }
+
+        case kWhatSendSR:
+        {
+            {
+                Mutex::Autolock autoLock(mLock);
+                if (!(mFlags & kFlagStarted)) {
+                    break;
+                }
+            }
+
+            onSendSR(msg);
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+void ARTPWriter::onRead(const sp<AMessage> &msg) {
+    MediaBuffer *mediaBuf;
+    status_t err = mSource->read(&mediaBuf);
+
+    if (err != OK) {
+        LOGI("reached EOS.");
+
+        Mutex::Autolock autoLock(mLock);
+        mFlags |= kFlagEOS;
+        return;
+    }
+
+    if (mediaBuf->range_length() > 0) {
+        LOGV("read buffer of size %d", mediaBuf->range_length());
+
+        if (mMode == H264) {
+            StripStartcode(mediaBuf);
+            sendAVCData(mediaBuf);
+        } else if (mMode == H263) {
+            sendH263Data(mediaBuf);
+        } else if (mMode == AMR_NB || mMode == AMR_WB) {
+            sendAMRData(mediaBuf);
+        }
+    }
+
+    mediaBuf->release();
+    mediaBuf = NULL;
+
+    msg->post();
+}
+
+void ARTPWriter::onSendSR(const sp<AMessage> &msg) {
+    sp<ABuffer> buffer = new ABuffer(65536);
+    buffer->setRange(0, 0);
+
+    addSR(buffer);
+    addSDES(buffer);
+
+    send(buffer, true /* isRTCP */);
+
+    ++mNumSRsSent;
+    msg->post(3000000);
+}
+
+void ARTPWriter::send(const sp<ABuffer> &buffer, bool isRTCP) {
+    ssize_t n = sendto(
+            mSocket, buffer->data(), buffer->size(), 0,
+            (const struct sockaddr *)(isRTCP ? &mRTCPAddr : &mRTPAddr),
+            sizeof(mRTCPAddr));
+
+    CHECK_EQ(n, (ssize_t)buffer->size());
+
+#if LOG_TO_FILES
+    int fd = isRTCP ? mRTCPFd : mRTPFd;
+
+    uint32_t ms = tolel(ALooper::GetNowUs() / 1000ll);
+    uint32_t length = tolel(buffer->size());
+    write(fd, &ms, sizeof(ms));
+    write(fd, &length, sizeof(length));
+    write(fd, buffer->data(), buffer->size());
+#endif
+}
+
+void ARTPWriter::addSR(const sp<ABuffer> &buffer) {
+    uint8_t *data = buffer->data() + buffer->size();
+
+    data[0] = 0x80 | 0;
+    data[1] = 200;  // SR
+    data[2] = 0;
+    data[3] = 6;
+    data[4] = mSourceID >> 24;
+    data[5] = (mSourceID >> 16) & 0xff;
+    data[6] = (mSourceID >> 8) & 0xff;
+    data[7] = mSourceID & 0xff;
+
+    data[8] = mLastNTPTime >> (64 - 8);
+    data[9] = (mLastNTPTime >> (64 - 16)) & 0xff;
+    data[10] = (mLastNTPTime >> (64 - 24)) & 0xff;
+    data[11] = (mLastNTPTime >> 32) & 0xff;
+    data[12] = (mLastNTPTime >> 24) & 0xff;
+    data[13] = (mLastNTPTime >> 16) & 0xff;
+    data[14] = (mLastNTPTime >> 8) & 0xff;
+    data[15] = mLastNTPTime & 0xff;
+
+    data[16] = (mLastRTPTime >> 24) & 0xff;
+    data[17] = (mLastRTPTime >> 16) & 0xff;
+    data[18] = (mLastRTPTime >> 8) & 0xff;
+    data[19] = mLastRTPTime & 0xff;
+
+    data[20] = mNumRTPSent >> 24;
+    data[21] = (mNumRTPSent >> 16) & 0xff;
+    data[22] = (mNumRTPSent >> 8) & 0xff;
+    data[23] = mNumRTPSent & 0xff;
+
+    data[24] = mNumRTPOctetsSent >> 24;
+    data[25] = (mNumRTPOctetsSent >> 16) & 0xff;
+    data[26] = (mNumRTPOctetsSent >> 8) & 0xff;
+    data[27] = mNumRTPOctetsSent & 0xff;
+
+    buffer->setRange(buffer->offset(), buffer->size() + 28);
+}
+
+void ARTPWriter::addSDES(const sp<ABuffer> &buffer) {
+    uint8_t *data = buffer->data() + buffer->size();
+    data[0] = 0x80 | 1;
+    data[1] = 202;  // SDES
+    data[4] = mSourceID >> 24;
+    data[5] = (mSourceID >> 16) & 0xff;
+    data[6] = (mSourceID >> 8) & 0xff;
+    data[7] = mSourceID & 0xff;
+
+    size_t offset = 8;
+
+    data[offset++] = 1;  // CNAME
+
+    static const char *kCNAME = "someone@somewhere";
+    data[offset++] = strlen(kCNAME);
+
+    memcpy(&data[offset], kCNAME, strlen(kCNAME));
+    offset += strlen(kCNAME);
+
+    data[offset++] = 7;  // NOTE
+
+    static const char *kNOTE = "Hell's frozen over.";
+    data[offset++] = strlen(kNOTE);
+
+    memcpy(&data[offset], kNOTE, strlen(kNOTE));
+    offset += strlen(kNOTE);
+
+    data[offset++] = 0;
+
+    if ((offset % 4) > 0) {
+        size_t count = 4 - (offset % 4);
+        switch (count) {
+            case 3:
+                data[offset++] = 0;
+            case 2:
+                data[offset++] = 0;
+            case 1:
+                data[offset++] = 0;
+        }
+    }
+
+    size_t numWords = (offset / 4) - 1;
+    data[2] = numWords >> 8;
+    data[3] = numWords & 0xff;
+
+    buffer->setRange(buffer->offset(), buffer->size() + offset);
+}
+
+// static
+uint64_t ARTPWriter::GetNowNTP() {
+    uint64_t nowUs = ALooper::GetNowUs();
+
+    nowUs += ((70ll * 365 + 17) * 24) * 60 * 60 * 1000000ll;
+
+    uint64_t hi = nowUs / 1000000ll;
+    uint64_t lo = ((1ll << 32) * (nowUs % 1000000ll)) / 1000000ll;
+
+    return (hi << 32) | lo;
+}
+
+void ARTPWriter::dumpSessionDesc() {
+    AString sdp;
+    sdp = "v=0\r\n";
+
+    sdp.append("o=- ");
+
+    uint64_t ntp = GetNowNTP();
+    sdp.append(ntp);
+    sdp.append(" ");
+    sdp.append(ntp);
+    sdp.append(" IN IP4 127.0.0.0\r\n");
+
+    sdp.append(
+          "s=Sample\r\n"
+          "i=Playing around\r\n"
+          "c=IN IP4 ");
+
+    struct in_addr addr;
+    addr.s_addr = ntohl(INADDR_LOOPBACK);
+
+    sdp.append(inet_ntoa(addr));
+
+    sdp.append(
+          "\r\n"
+          "t=0 0\r\n"
+          "a=range:npt=now-\r\n");
+
+    sp<MetaData> meta = mSource->getFormat();
+
+    if (mMode == H264 || mMode == H263) {
+        sdp.append("m=video ");
+    } else {
+        sdp.append("m=audio ");
+    }
+
+    sdp.append(StringPrintf("%d", ntohs(mRTPAddr.sin_port)));
+    sdp.append(
+          " RTP/AVP " PT_STR "\r\n"
+          "b=AS 320000\r\n"
+          "a=rtpmap:" PT_STR " ");
+
+    if (mMode == H264) {
+        sdp.append("H264/90000");
+    } else if (mMode == H263) {
+        sdp.append("H263-1998/90000");
+    } else if (mMode == AMR_NB || mMode == AMR_WB) {
+        int32_t sampleRate, numChannels;
+        CHECK(mSource->getFormat()->findInt32(kKeySampleRate, &sampleRate));
+        CHECK(mSource->getFormat()->findInt32(kKeyChannelCount, &numChannels));
+
+        CHECK_EQ(numChannels, 1);
+        CHECK_EQ(sampleRate, (mMode == AMR_NB) ? 8000 : 16000);
+
+        sdp.append(mMode == AMR_NB ? "AMR" : "AMR-WB");
+        sdp.append(StringPrintf("/%d/%d", sampleRate, numChannels));
+    } else {
+        TRESPASS();
+    }
+
+    sdp.append("\r\n");
+
+    if (mMode == H264 || mMode == H263) {
+        int32_t width, height;
+        CHECK(meta->findInt32(kKeyWidth, &width));
+        CHECK(meta->findInt32(kKeyHeight, &height));
+
+        sdp.append("a=cliprect 0,0,");
+        sdp.append(height);
+        sdp.append(",");
+        sdp.append(width);
+        sdp.append("\r\n");
+
+        sdp.append(
+              "a=framesize:" PT_STR " ");
+        sdp.append(width);
+        sdp.append("-");
+        sdp.append(height);
+        sdp.append("\r\n");
+    }
+
+    if (mMode == H264) {
+        sdp.append(
+              "a=fmtp:" PT_STR " profile-level-id=");
+        sdp.append(mProfileLevel);
+        sdp.append(";sprop-parameter-sets=");
+
+        sdp.append(mSeqParamSet);
+        sdp.append(",");
+        sdp.append(mPicParamSet);
+        sdp.append(";packetization-mode=1\r\n");
+    } else if (mMode == AMR_NB || mMode == AMR_WB) {
+        sdp.append("a=fmtp:" PT_STR " octed-align\r\n");
+    }
+
+    LOGI("%s", sdp.c_str());
+}
+
+void ARTPWriter::makeH264SPropParamSets(MediaBuffer *buffer) {
+    static const char kStartCode[] = "\x00\x00\x00\x01";
+
+    const uint8_t *data =
+        (const uint8_t *)buffer->data() + buffer->range_offset();
+    size_t size = buffer->range_length();
+
+    CHECK_GE(size, 0u);
+
+    size_t startCodePos = 0;
+    while (startCodePos + 3 < size
+            && memcmp(kStartCode, &data[startCodePos], 4)) {
+        ++startCodePos;
+    }
+
+    CHECK_LT(startCodePos + 3, size);
+
+    CHECK_EQ((unsigned)data[0], 0x67u);
+
+    mProfileLevel =
+        StringPrintf("%02X%02X%02X", data[1], data[2], data[3]);
+
+    encodeBase64(data, startCodePos, &mSeqParamSet);
+
+    encodeBase64(&data[startCodePos + 4], size - startCodePos - 4,
+                 &mPicParamSet);
+}
+
+void ARTPWriter::sendBye() {
+    sp<ABuffer> buffer = new ABuffer(8);
+    uint8_t *data = buffer->data();
+    *data++ = (2 << 6) | 1;
+    *data++ = 203;
+    *data++ = 0;
+    *data++ = 1;
+    *data++ = mSourceID >> 24;
+    *data++ = (mSourceID >> 16) & 0xff;
+    *data++ = (mSourceID >> 8) & 0xff;
+    *data++ = mSourceID & 0xff;
+    buffer->setRange(0, 8);
+
+    send(buffer, true /* isRTCP */);
+}
+
+void ARTPWriter::sendAVCData(MediaBuffer *mediaBuf) {
+    // 12 bytes RTP header + 2 bytes for the FU-indicator and FU-header.
+    CHECK_GE(kMaxPacketSize, 12u + 2u);
+
+    int64_t timeUs;
+    CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+
+    uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
+
+    const uint8_t *mediaData =
+        (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+    sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
+    if (mediaBuf->range_length() + 12 <= buffer->capacity()) {
+        // The data fits into a single packet
+        uint8_t *data = buffer->data();
+        data[0] = 0x80;
+        data[1] = (1 << 7) | PT;  // M-bit
+        data[2] = (mSeqNo >> 8) & 0xff;
+        data[3] = mSeqNo & 0xff;
+        data[4] = rtpTime >> 24;
+        data[5] = (rtpTime >> 16) & 0xff;
+        data[6] = (rtpTime >> 8) & 0xff;
+        data[7] = rtpTime & 0xff;
+        data[8] = mSourceID >> 24;
+        data[9] = (mSourceID >> 16) & 0xff;
+        data[10] = (mSourceID >> 8) & 0xff;
+        data[11] = mSourceID & 0xff;
+
+        memcpy(&data[12],
+               mediaData, mediaBuf->range_length());
+
+        buffer->setRange(0, mediaBuf->range_length() + 12);
+
+        send(buffer, false /* isRTCP */);
+
+        ++mSeqNo;
+        ++mNumRTPSent;
+        mNumRTPOctetsSent += buffer->size() - 12;
+    } else {
+        // FU-A
+
+        unsigned nalType = mediaData[0];
+        size_t offset = 1;
+
+        bool firstPacket = true;
+        while (offset < mediaBuf->range_length()) {
+            size_t size = mediaBuf->range_length() - offset;
+            bool lastPacket = true;
+            if (size + 12 + 2 > buffer->capacity()) {
+                lastPacket = false;
+                size = buffer->capacity() - 12 - 2;
+            }
+
+            uint8_t *data = buffer->data();
+            data[0] = 0x80;
+            data[1] = (lastPacket ? (1 << 7) : 0x00) | PT;  // M-bit
+            data[2] = (mSeqNo >> 8) & 0xff;
+            data[3] = mSeqNo & 0xff;
+            data[4] = rtpTime >> 24;
+            data[5] = (rtpTime >> 16) & 0xff;
+            data[6] = (rtpTime >> 8) & 0xff;
+            data[7] = rtpTime & 0xff;
+            data[8] = mSourceID >> 24;
+            data[9] = (mSourceID >> 16) & 0xff;
+            data[10] = (mSourceID >> 8) & 0xff;
+            data[11] = mSourceID & 0xff;
+
+            data[12] = 28 | (nalType & 0xe0);
+
+            CHECK(!firstPacket || !lastPacket);
+
+            data[13] =
+                (firstPacket ? 0x80 : 0x00)
+                | (lastPacket ? 0x40 : 0x00)
+                | (nalType & 0x1f);
+
+            memcpy(&data[14], &mediaData[offset], size);
+
+            buffer->setRange(0, 14 + size);
+
+            send(buffer, false /* isRTCP */);
+
+            ++mSeqNo;
+            ++mNumRTPSent;
+            mNumRTPOctetsSent += buffer->size() - 12;
+
+            firstPacket = false;
+            offset += size;
+        }
+    }
+
+    mLastRTPTime = rtpTime;
+    mLastNTPTime = GetNowNTP();
+}
+
+void ARTPWriter::sendH263Data(MediaBuffer *mediaBuf) {
+    CHECK_GE(kMaxPacketSize, 12u + 2u);
+
+    int64_t timeUs;
+    CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+
+    uint32_t rtpTime = mRTPTimeBase + (timeUs * 9 / 100ll);
+
+    const uint8_t *mediaData =
+        (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+    // hexdump(mediaData, mediaBuf->range_length());
+
+    CHECK_EQ((unsigned)mediaData[0], 0u);
+    CHECK_EQ((unsigned)mediaData[1], 0u);
+
+    size_t offset = 2;
+    size_t size = mediaBuf->range_length();
+
+    while (offset < size) {
+        sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
+        // CHECK_LE(mediaBuf->range_length() -2 + 14, buffer->capacity());
+
+        size_t remaining = size - offset;
+        bool lastPacket = (remaining + 14 <= buffer->capacity());
+        if (!lastPacket) {
+            remaining = buffer->capacity() - 14;
+        }
+
+        uint8_t *data = buffer->data();
+        data[0] = 0x80;
+        data[1] = (lastPacket ? 0x80 : 0x00) | PT;  // M-bit
+        data[2] = (mSeqNo >> 8) & 0xff;
+        data[3] = mSeqNo & 0xff;
+        data[4] = rtpTime >> 24;
+        data[5] = (rtpTime >> 16) & 0xff;
+        data[6] = (rtpTime >> 8) & 0xff;
+        data[7] = rtpTime & 0xff;
+        data[8] = mSourceID >> 24;
+        data[9] = (mSourceID >> 16) & 0xff;
+        data[10] = (mSourceID >> 8) & 0xff;
+        data[11] = mSourceID & 0xff;
+
+        data[12] = (offset == 2) ? 0x04 : 0x00;  // P=?, V=0
+        data[13] = 0x00;  // PLEN = PEBIT = 0
+
+        memcpy(&data[14], &mediaData[offset], remaining);
+        offset += remaining;
+
+        buffer->setRange(0, remaining + 14);
+
+        send(buffer, false /* isRTCP */);
+
+        ++mSeqNo;
+        ++mNumRTPSent;
+        mNumRTPOctetsSent += buffer->size() - 12;
+    }
+
+    mLastRTPTime = rtpTime;
+    mLastNTPTime = GetNowNTP();
+}
+
+static size_t getFrameSize(bool isWide, unsigned FT) {
+    static const size_t kFrameSizeNB[8] = {
+        95, 103, 118, 134, 148, 159, 204, 244
+    };
+    static const size_t kFrameSizeWB[9] = {
+        132, 177, 253, 285, 317, 365, 397, 461, 477
+    };
+
+    size_t frameSize = isWide ? kFrameSizeWB[FT] : kFrameSizeNB[FT];
+
+    // Round up bits to bytes and add 1 for the header byte.
+    frameSize = (frameSize + 7) / 8 + 1;
+
+    return frameSize;
+}
+
+void ARTPWriter::sendAMRData(MediaBuffer *mediaBuf) {
+    const uint8_t *mediaData =
+        (const uint8_t *)mediaBuf->data() + mediaBuf->range_offset();
+
+    size_t mediaLength = mediaBuf->range_length();
+
+    CHECK_GE(kMaxPacketSize, 12u + 1u + mediaLength);
+
+    const bool isWide = (mMode == AMR_WB);
+
+    int64_t timeUs;
+    CHECK(mediaBuf->meta_data()->findInt64(kKeyTime, &timeUs));
+    uint32_t rtpTime = mRTPTimeBase + (timeUs / (isWide ? 250 : 125));
+
+    // hexdump(mediaData, mediaLength);
+
+    Vector<uint8_t> tableOfContents;
+    size_t srcOffset = 0;
+    while (srcOffset < mediaLength) {
+        uint8_t toc = mediaData[srcOffset];
+
+        unsigned FT = (toc >> 3) & 0x0f;
+        CHECK((isWide && FT <= 8) || (!isWide && FT <= 7));
+
+        tableOfContents.push(toc);
+        srcOffset += getFrameSize(isWide, FT);
+    }
+    CHECK_EQ(srcOffset, mediaLength);
+
+    sp<ABuffer> buffer = new ABuffer(kMaxPacketSize);
+    CHECK_LE(mediaLength + 12 + 1, buffer->capacity());
+
+    // The data fits into a single packet
+    uint8_t *data = buffer->data();
+    data[0] = 0x80;
+    data[1] = PT;
+    if (mNumRTPSent == 0) {
+        // Signal start of talk-spurt.
+        data[1] |= 0x80;  // M-bit
+    }
+    data[2] = (mSeqNo >> 8) & 0xff;
+    data[3] = mSeqNo & 0xff;
+    data[4] = rtpTime >> 24;
+    data[5] = (rtpTime >> 16) & 0xff;
+    data[6] = (rtpTime >> 8) & 0xff;
+    data[7] = rtpTime & 0xff;
+    data[8] = mSourceID >> 24;
+    data[9] = (mSourceID >> 16) & 0xff;
+    data[10] = (mSourceID >> 8) & 0xff;
+    data[11] = mSourceID & 0xff;
+
+    data[12] = 0xf0;  // CMR=15, RR=0
+
+    size_t dstOffset = 13;
+
+    for (size_t i = 0; i < tableOfContents.size(); ++i) {
+        uint8_t toc = tableOfContents[i];
+
+        if (i + 1 < tableOfContents.size()) {
+            toc |= 0x80;
+        } else {
+            toc &= ~0x80;
+        }
+
+        data[dstOffset++] = toc;
+    }
+
+    srcOffset = 0;
+    for (size_t i = 0; i < tableOfContents.size(); ++i) {
+        uint8_t toc = tableOfContents[i];
+        unsigned FT = (toc >> 3) & 0x0f;
+        size_t frameSize = getFrameSize(isWide, FT);
+
+        ++srcOffset;  // skip toc
+        memcpy(&data[dstOffset], &mediaData[srcOffset], frameSize - 1);
+        srcOffset += frameSize - 1;
+        dstOffset += frameSize - 1;
+    }
+
+    buffer->setRange(0, dstOffset);
+
+    send(buffer, false /* isRTCP */);
+
+    ++mSeqNo;
+    ++mNumRTPSent;
+    mNumRTPOctetsSent += buffer->size() - 12;
+
+    mLastRTPTime = rtpTime;
+    mLastNTPTime = GetNowNTP();
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTPWriter.h
@@ -0,0 +1,128 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_RTP_WRITER_H_
+
+#define A_RTP_WRITER_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/MediaWriter.h>
+
+#include <arpa/inet.h>
+#include <sys/socket.h>
+
+#define LOG_TO_FILES    0
+
+namespace android {
+
+struct ABuffer;
+struct MediaBuffer;
+
+struct ARTPWriter : public MediaWriter {
+    ARTPWriter(int fd);
+
+    virtual status_t addSource(const sp<MediaSource> &source);
+    virtual bool reachedEOS();
+    virtual status_t start(MetaData *params);
+    virtual status_t stop();
+    virtual status_t pause();
+
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+protected:
+    virtual ~ARTPWriter();
+
+private:
+    enum {
+        kWhatStart  = 'strt',
+        kWhatStop   = 'stop',
+        kWhatRead   = 'read',
+        kWhatSendSR = 'sr  ',
+    };
+
+    enum {
+        kFlagStarted  = 1,
+        kFlagEOS      = 2,
+    };
+
+    Mutex mLock;
+    Condition mCondition;
+    uint32_t mFlags;
+
+    int mFd;
+
+#if LOG_TO_FILES
+    int mRTPFd;
+    int mRTCPFd;
+#endif
+
+    sp<MediaSource> mSource;
+    sp<ALooper> mLooper;
+    sp<AHandlerReflector<ARTPWriter> > mReflector;
+
+    int mSocket;
+    struct sockaddr_in mRTPAddr;
+    struct sockaddr_in mRTCPAddr;
+
+    AString mProfileLevel;
+    AString mSeqParamSet;
+    AString mPicParamSet;
+
+    uint32_t mSourceID;
+    uint32_t mSeqNo;
+    uint32_t mRTPTimeBase;
+    uint32_t mNumRTPSent;
+    uint32_t mNumRTPOctetsSent;
+    uint32_t mLastRTPTime;
+    uint64_t mLastNTPTime;
+
+    int32_t mNumSRsSent;
+
+    enum {
+        INVALID,
+        H264,
+        H263,
+        AMR_NB,
+        AMR_WB,
+    } mMode;
+
+    static uint64_t GetNowNTP();
+
+    void onRead(const sp<AMessage> &msg);
+    void onSendSR(const sp<AMessage> &msg);
+
+    void addSR(const sp<ABuffer> &buffer);
+    void addSDES(const sp<ABuffer> &buffer);
+
+    void makeH264SPropParamSets(MediaBuffer *buffer);
+    void dumpSessionDesc();
+
+    void sendBye();
+    void sendAVCData(MediaBuffer *mediaBuf);
+    void sendH263Data(MediaBuffer *mediaBuf);
+    void sendAMRData(MediaBuffer *mediaBuf);
+
+    void send(const sp<ABuffer> &buffer, bool isRTCP);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARTPWriter);
+};
+
+}  // namespace android
+
+#endif  // A_RTP_WRITER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTSPConnection.cpp
@@ -0,0 +1,1082 @@
+/*
+ * Copyright (C) 2010 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 "ARTSPConnection"
+#include <utils/Log.h>
+
+#include "ARTSPConnection.h"
+
+#include <cutils/properties.h>
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/base64.h>
+#include <media/stagefright/MediaErrors.h>
+
+#include <arpa/inet.h>
+#include <fcntl.h>
+#include <netdb.h>
+#include <sys/socket.h>
+
+#include "HTTPBase.h"
+
+#include "nsCOMPtr.h"
+#include "nsString.h"
+#include "nsNetCID.h"
+#include "nsIServiceManager.h"
+#include "nsICryptoHash.h"
+
+namespace android {
+
+// static
+const int64_t ARTSPConnection::kSelectTimeoutUs = 1000ll;
+
+ARTSPConnection::ARTSPConnection(bool uidValid, uid_t uid)
+    : mUIDValid(uidValid),
+      mUID(uid),
+      mState(DISCONNECTED),
+      mAuthType(NONE),
+      mSocket(-1),
+      mConnectionID(0),
+      mNextCSeq(0),
+      mReceiveResponseEventPending(false) {
+    MakeUserAgent(&mUserAgent);
+}
+
+ARTSPConnection::~ARTSPConnection() {
+    if (mSocket >= 0) {
+        LOGE("Connection is still open, closing the socket.");
+        if (mUIDValid) {
+            HTTPBase::UnRegisterSocketUserTag(mSocket);
+        }
+        close(mSocket);
+        mSocket = -1;
+    }
+}
+
+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::disconnect(const sp<AMessage> &reply) {
+    sp<AMessage> msg = new AMessage(kWhatDisconnect, id());
+    msg->setMessage("reply", reply);
+    msg->post();
+}
+
+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::observeBinaryData(const sp<AMessage> &reply) {
+    sp<AMessage> msg = new AMessage(kWhatObserveBinaryData, id());
+    msg->setMessage("reply", reply);
+    msg->post();
+}
+
+void ARTSPConnection::onMessageReceived(const sp<AMessage> &msg) {
+    switch (msg->what()) {
+        case kWhatConnect:
+            onConnect(msg);
+            break;
+
+        case kWhatDisconnect:
+            onDisconnect(msg);
+            break;
+
+        case kWhatCompleteConnection:
+            onCompleteConnection(msg);
+            break;
+
+        case kWhatSendRequest:
+            onSendRequest(msg);
+            break;
+
+        case kWhatReceiveResponse:
+            onReceiveResponse();
+            break;
+
+        case kWhatObserveBinaryData:
+        {
+            CHECK(msg->findMessage("reply", &mObserveBinaryMessage));
+            break;
+        }
+
+        default:
+            TRESPASS();
+            break;
+    }
+}
+
+// static
+bool ARTSPConnection::ParseURL(
+        const char *url, AString *host, unsigned *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) {
+        unsigned long x;
+        if (!ParseSingleUnsignedLong(colonPos + 1, &x) || 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;
+}
+
+static status_t MakeSocketBlocking(int s, bool blocking) {
+    // Make socket non-blocking.
+    int flags = fcntl(s, F_GETFL, 0);
+
+    if (flags == -1) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (blocking) {
+        flags &= ~O_NONBLOCK;
+    } else {
+        flags |= O_NONBLOCK;
+    }
+
+    flags = fcntl(s, F_SETFL, flags);
+
+    return flags == -1 ? UNKNOWN_ERROR : OK;
+}
+
+void ARTSPConnection::onConnect(const sp<AMessage> &msg) {
+    ++mConnectionID;
+
+    if (mState != DISCONNECTED) {
+        if (mUIDValid) {
+            HTTPBase::UnRegisterSocketUserTag(mSocket);
+        }
+        close(mSocket);
+        mSocket = -1;
+
+        flushPendingRequests();
+    }
+
+    mState = CONNECTING;
+
+    AString url;
+    CHECK(msg->findString("url", &url));
+
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+    AString host, path;
+    unsigned port;
+    if (!ParseURL(url.c_str(), &host, &port, &path, &mUser, &mPass)
+            || (mUser.size() > 0 && mPass.size() == 0)) {
+        // If we have a user name but no password we have to give up
+        // right here, since we currently have no way of asking the user
+        // for this information.
+
+        LOGE("Malformed rtsp url %s", url.c_str());
+
+        reply->setInt32("result", ERROR_MALFORMED);
+        reply->post();
+
+        mState = DISCONNECTED;
+        return;
+    }
+
+    if (mUser.size() > 0) {
+        LOGV("user = '%s', pass = '%s'", mUser.c_str(), mPass.c_str());
+    }
+
+    struct hostent *ent = gethostbyname(host.c_str());
+    if (ent == NULL) {
+        LOGE("Unknown host %s", host.c_str());
+
+        reply->setInt32("result", -ENOENT);
+        reply->post();
+
+        mState = DISCONNECTED;
+        return;
+    }
+
+    mSocket = socket(AF_INET, SOCK_STREAM, 0);
+
+    if (mUIDValid) {
+        HTTPBase::RegisterSocketUserTag(mSocket, mUID,
+                                        (uint32_t)*(uint32_t*) "RTSP");
+    }
+
+    MakeSocketBlocking(mSocket, false);
+
+    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) {
+            sp<AMessage> msg = new AMessage(kWhatCompleteConnection, id());
+            msg->setMessage("reply", reply);
+            msg->setInt32("connection-id", mConnectionID);
+            msg->post();
+            return;
+        }
+
+        reply->setInt32("result", -errno);
+        mState = DISCONNECTED;
+
+        if (mUIDValid) {
+            HTTPBase::UnRegisterSocketUserTag(mSocket);
+        }
+        close(mSocket);
+        mSocket = -1;
+    } else {
+        reply->setInt32("result", OK);
+        mState = CONNECTED;
+        mNextCSeq = 1;
+
+        postReceiveReponseEvent();
+    }
+
+    reply->post();
+}
+
+void ARTSPConnection::performDisconnect() {
+    if (mUIDValid) {
+        HTTPBase::UnRegisterSocketUserTag(mSocket);
+    }
+    close(mSocket);
+    mSocket = -1;
+
+    flushPendingRequests();
+
+    mUser.clear();
+    mPass.clear();
+    mAuthType = NONE;
+    mNonce.clear();
+
+    mState = DISCONNECTED;
+}
+
+void ARTSPConnection::onDisconnect(const sp<AMessage> &msg) {
+    if (mState == CONNECTED || mState == CONNECTING) {
+        performDisconnect();
+    }
+
+    sp<AMessage> reply;
+    CHECK(msg->findMessage("reply", &reply));
+
+    reply->setInt32("result", OK);
+
+    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);
+    CHECK_GE(res, 0);
+
+    if (res == 0) {
+        // Timed out. Not yet connected.
+
+        msg->post();
+        return;
+    }
+
+    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) {
+        LOGE("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();
+    }
+
+    reply->post();
+}
+
+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;
+    }
+
+    AString request;
+    CHECK(msg->findString("request", &request));
+
+    // Just in case we need to re-issue the request with proper authentication
+    // later, stash it away.
+    reply->setString("original-request", request.c_str(), request.size());
+
+    addAuthentication(&request);
+    addUserAgent(&request);
+
+    // Find the boundary between headers and the body.
+    ssize_t i = request.find("\r\n\r\n");
+    CHECK_GE(i, 0);
+
+    int32_t cseq = mNextCSeq++;
+
+    AString cseqHeader = "CSeq: ";
+    cseqHeader.append(cseq);
+    cseqHeader.append("\r\n");
+
+    request.insert(cseqHeader, i + 2);
+
+    LOGV("request: '%s'", request.c_str());
+
+    size_t numBytesSent = 0;
+    while (numBytesSent < request.size()) {
+        ssize_t n =
+            send(mSocket, request.c_str() + numBytesSent,
+                 request.size() - numBytesSent, 0);
+
+        if (n < 0 && errno == EINTR) {
+            continue;
+        }
+
+        if (n <= 0) {
+            performDisconnect();
+
+            if (n == 0) {
+                // Server closed the connection.
+                LOGE("Server unexpectedly closed the connection.");
+
+                reply->setInt32("result", ERROR_IO);
+                reply->post();
+            } else {
+                LOGE("Error sending rtsp request. (%s)", strerror(errno));
+                reply->setInt32("result", -errno);
+                reply->post();
+            }
+
+            return;
+        }
+
+        numBytesSent += (size_t)n;
+    }
+
+    mPendingRequests.add(cseq, reply);
+}
+
+void ARTSPConnection::onReceiveResponse() {
+    mReceiveResponseEventPending = false;
+
+    if (mState != CONNECTED) {
+        return;
+    }
+
+    struct timeval tv;
+    tv.tv_sec = 0;
+    tv.tv_usec = kSelectTimeoutUs;
+
+    fd_set rs;
+    FD_ZERO(&rs);
+    FD_SET(mSocket, &rs);
+
+    int res = select(mSocket + 1, &rs, NULL, NULL, &tv);
+    CHECK_GE(res, 0);
+
+    if (res == 1) {
+        MakeSocketBlocking(mSocket, true);
+
+        bool success = receiveRTSPReponse();
+
+        MakeSocketBlocking(mSocket, false);
+
+        if (!success) {
+            // Something horrible, irreparable has happened.
+            flushPendingRequests();
+            return;
+        }
+    }
+
+    postReceiveReponseEvent();
+}
+
+void ARTSPConnection::flushPendingRequests() {
+    for (size_t i = 0; i < mPendingRequests.size(); ++i) {
+        sp<AMessage> reply = mPendingRequests.valueAt(i);
+
+        reply->setInt32("result", -ECONNABORTED);
+        reply->post();
+    }
+
+    mPendingRequests.clear();
+}
+
+void ARTSPConnection::postReceiveReponseEvent() {
+    if (mReceiveResponseEventPending) {
+        return;
+    }
+
+    sp<AMessage> msg = new AMessage(kWhatReceiveResponse, id());
+    msg->post();
+
+    mReceiveResponseEventPending = true;
+}
+
+status_t ARTSPConnection::receive(void *data, size_t size) {
+    size_t offset = 0;
+    while (offset < size) {
+        ssize_t n = recv(mSocket, (uint8_t *)data + offset, size - offset, 0);
+
+        if (n < 0 && errno == EINTR) {
+            continue;
+        }
+
+        if (n <= 0) {
+            performDisconnect();
+
+            if (n == 0) {
+                // Server closed the connection.
+                LOGE("Server unexpectedly closed the connection.");
+                return ERROR_IO;
+            } else {
+                LOGE("Error reading rtsp response. (%s)", strerror(errno));
+                return -errno;
+            }
+        }
+
+        offset += (size_t)n;
+    }
+
+    return OK;
+}
+
+bool ARTSPConnection::receiveLine(AString *line) {
+    line->clear();
+
+    bool sawCR = false;
+    for (;;) {
+        char c;
+        if (receive(&c, 1) != OK) {
+            return false;
+        }
+
+        if (sawCR && c == '\n') {
+            line->erase(line->size() - 1, 1);
+            return true;
+        }
+
+        line->append(&c, 1);
+
+        if (c == '$' && line->size() == 1) {
+            // Special-case for interleaved binary data.
+            return true;
+        }
+
+        sawCR = (c == '\r');
+    }
+}
+
+sp<ABuffer> ARTSPConnection::receiveBinaryData() {
+    uint8_t x[3];
+    if (receive(x, 3) != OK) {
+        return NULL;
+    }
+
+    sp<ABuffer> buffer = new ABuffer((x[1] << 8) | x[2]);
+    if (receive(buffer->data(), buffer->size()) != OK) {
+        return NULL;
+    }
+
+    buffer->meta()->setInt32("index", (int32_t)x[0]);
+
+    return buffer;
+}
+
+static bool IsRTSPVersion(const AString &s) {
+    return s == "RTSP/1.0";
+}
+
+bool ARTSPConnection::receiveRTSPReponse() {
+    AString statusLine;
+
+    if (!receiveLine(&statusLine)) {
+        return false;
+    }
+
+    if (statusLine == "$") {
+        sp<ABuffer> buffer = receiveBinaryData();
+
+        if (buffer == NULL) {
+            return false;
+        }
+
+        if (mObserveBinaryMessage != NULL) {
+            sp<AMessage> notify = mObserveBinaryMessage->dup();
+            notify->setObject("buffer", buffer);
+            notify->post();
+        } else {
+            LOGW("received binary data, but no one cares.");
+        }
+
+        return true;
+    }
+
+    sp<ARTSPResponse> response = new ARTSPResponse;
+    response->mStatusLine = statusLine;
+
+    LOGI("status: %s", response->mStatusLine.c_str());
+
+    ssize_t space1 = response->mStatusLine.find(" ");
+    if (space1 < 0) {
+        return false;
+    }
+    ssize_t space2 = response->mStatusLine.find(" ", space1 + 1);
+    if (space2 < 0) {
+        return false;
+    }
+
+    bool isRequest = false;
+
+    if (!IsRTSPVersion(AString(response->mStatusLine, 0, space1))) {
+        CHECK(IsRTSPVersion(
+                    AString(
+                        response->mStatusLine,
+                        space2 + 1,
+                        response->mStatusLine.size() - space2 - 1)));
+
+        isRequest = true;
+
+        response->mStatusCode = 0;
+    } else {
+        AString statusCodeStr(
+                response->mStatusLine, space1 + 1, space2 - space1 - 1);
+
+        if (!ParseSingleUnsignedLong(
+                    statusCodeStr.c_str(), &response->mStatusCode)
+                || response->mStatusCode < 100 || response->mStatusCode > 999) {
+            return false;
+        }
+    }
+
+    AString line;
+    ssize_t lastDictIndex = -1;
+    for (;;) {
+        if (!receiveLine(&line)) {
+            break;
+        }
+
+        if (line.empty()) {
+            break;
+        }
+
+        LOGV("line: '%s'", line.c_str());
+
+        if (line.c_str()[0] == ' ' || line.c_str()[0] == '\t') {
+            // Support for folded header values.
+
+            if (lastDictIndex < 0) {
+                // First line cannot be a continuation of the previous one.
+                return false;
+            }
+
+            AString &value = response->mHeaders.editValueAt(lastDictIndex);
+            value.append(line);
+
+            continue;
+        }
+
+        ssize_t colonPos = line.find(":");
+        if (colonPos < 0) {
+            // Malformed header line.
+            return false;
+        }
+
+        AString key(line, 0, colonPos);
+        key.trim();
+        key.tolower();
+
+        line.erase(0, colonPos + 1);
+
+        lastDictIndex = response->mHeaders.add(key, line);
+    }
+
+    for (size_t i = 0; i < response->mHeaders.size(); ++i) {
+        response->mHeaders.editValueAt(i).trim();
+    }
+
+    unsigned long contentLength = 0;
+
+    ssize_t i = response->mHeaders.indexOfKey("content-length");
+
+    if (i >= 0) {
+        AString value = response->mHeaders.valueAt(i);
+        if (!ParseSingleUnsignedLong(value.c_str(), &contentLength)) {
+            return false;
+        }
+    }
+
+    if (contentLength > 0) {
+        response->mContent = new ABuffer(contentLength);
+
+        if (receive(response->mContent->data(), contentLength) != OK) {
+            return false;
+        }
+    }
+
+    if (response->mStatusCode == 401) {
+        if (mAuthType == NONE && mUser.size() > 0
+                && parseAuthMethod(response)) {
+            ssize_t i;
+            CHECK_EQ((status_t)OK, findPendingRequest(response, &i));
+            CHECK_GE(i, 0);
+
+            sp<AMessage> reply = mPendingRequests.valueAt(i);
+            mPendingRequests.removeItemsAt(i);
+
+            AString request;
+            CHECK(reply->findString("original-request", &request));
+
+            sp<AMessage> msg = new AMessage(kWhatSendRequest, id());
+            msg->setMessage("reply", reply);
+            msg->setString("request", request.c_str(), request.size());
+
+            LOGI("re-sending request with authentication headers...");
+            onSendRequest(msg);
+
+            return true;
+        }
+    }
+
+    return isRequest
+        ? handleServerRequest(response)
+        : notifyResponseListener(response);
+}
+
+bool ARTSPConnection::handleServerRequest(const sp<ARTSPResponse> &request) {
+    // Implementation of server->client requests is optional for all methods
+    // but we do need to respond, even if it's just to say that we don't
+    // support the method.
+
+    ssize_t space1 = request->mStatusLine.find(" ");
+    CHECK_GE(space1, 0);
+
+    AString response;
+    response.append("RTSP/1.0 501 Not Implemented\r\n");
+
+    ssize_t i = request->mHeaders.indexOfKey("cseq");
+
+    if (i >= 0) {
+        AString value = request->mHeaders.valueAt(i);
+
+        unsigned long cseq;
+        if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
+            return false;
+        }
+
+        response.append("CSeq: ");
+        response.append(cseq);
+        response.append("\r\n");
+    }
+
+    response.append("\r\n");
+
+    size_t numBytesSent = 0;
+    while (numBytesSent < response.size()) {
+        ssize_t n =
+            send(mSocket, response.c_str() + numBytesSent,
+                 response.size() - numBytesSent, 0);
+
+        if (n < 0 && errno == EINTR) {
+            continue;
+        }
+
+        if (n <= 0) {
+            if (n == 0) {
+                // Server closed the connection.
+                LOGE("Server unexpectedly closed the connection.");
+            } else {
+                LOGE("Error sending rtsp response (%s).", strerror(errno));
+            }
+
+            performDisconnect();
+
+            return false;
+        }
+
+        numBytesSent += (size_t)n;
+    }
+
+    return true;
+}
+
+// static
+bool ARTSPConnection::ParseSingleUnsignedLong(
+        const char *from, unsigned long *x) {
+    char *end;
+    *x = strtoul(from, &end, 10);
+
+    if (end == from || *end != '\0') {
+        return false;
+    }
+
+    return true;
+}
+
+status_t ARTSPConnection::findPendingRequest(
+        const sp<ARTSPResponse> &response, ssize_t *index) const {
+    *index = 0;
+
+    ssize_t i = response->mHeaders.indexOfKey("cseq");
+
+    if (i < 0) {
+        // This is an unsolicited server->client message.
+        return OK;
+    }
+
+    AString value = response->mHeaders.valueAt(i);
+
+    unsigned long cseq;
+    if (!ParseSingleUnsignedLong(value.c_str(), &cseq)) {
+        return ERROR_MALFORMED;
+    }
+
+    i = mPendingRequests.indexOfKey(cseq);
+
+    if (i < 0) {
+        return -ENOENT;
+    }
+
+    *index = i;
+
+    return OK;
+}
+
+bool ARTSPConnection::notifyResponseListener(
+        const sp<ARTSPResponse> &response) {
+    ssize_t i;
+    status_t err = findPendingRequest(response, &i);
+
+    if (err == OK && i < 0) {
+        // An unsolicited server response is not a problem.
+        return true;
+    }
+
+    if (err != OK) {
+        return false;
+    }
+
+    sp<AMessage> reply = mPendingRequests.valueAt(i);
+    mPendingRequests.removeItemsAt(i);
+
+    reply->setInt32("result", OK);
+    reply->setObject("response", response);
+    reply->post();
+
+    return true;
+}
+
+bool ARTSPConnection::parseAuthMethod(const sp<ARTSPResponse> &response) {
+    ssize_t i = response->mHeaders.indexOfKey("www-authenticate");
+
+    if (i < 0) {
+        return false;
+    }
+
+    AString value = response->mHeaders.valueAt(i);
+
+    if (!strncmp(value.c_str(), "Basic", 5)) {
+        mAuthType = BASIC;
+    } else {
+
+        CHECK(!strncmp(value.c_str(), "Digest", 6));
+        mAuthType = DIGEST;
+
+        i = value.find("nonce=");
+        CHECK_GE(i, 0);
+        CHECK_EQ(value.c_str()[i + 6], '\"');
+        ssize_t j = value.find("\"", i + 7);
+        CHECK_GE(j, 0);
+
+        mNonce.setTo(value, i + 7, j - i - 7);
+    }
+
+    return true;
+}
+
+static void H(const AString &s, AString *out) {
+    nsresult rv;
+    nsCOMPtr<nsICryptoHash> cryptoHash;
+
+    out->clear();
+
+    cryptoHash = do_CreateInstance(NS_CRYPTO_HASH_CONTRACTID, &rv);
+    if (NS_FAILED(rv)) {
+        LOGE(("Failed to create crypto hash."));
+        return;
+    }
+    rv = cryptoHash->Init(nsICryptoHash::MD5);
+    if (NS_FAILED(rv)) {
+        LOGE(("Failed to init md5 hash."));
+        return;
+    }
+
+    rv = cryptoHash->Update((unsigned char*)s.c_str(), s.size());
+    if (NS_FAILED(rv)) {
+        LOGE(("Failed to update md5 hash."));
+        return;
+    }
+
+    nsAutoCString hashString;
+    rv = cryptoHash->Finish(false, hashString);
+    if (NS_FAILED(rv)) {
+        LOGE(("Failed to finish md5 hash."));
+        return;
+    }
+
+    const uint8_t kHashKeyLength = 16;
+    if (hashString.Length() != kHashKeyLength) {
+      LOGE(("Invalid hash key length."));
+      return;
+    }
+
+    const char *key;
+    hashString.GetData(&key);
+
+    for (size_t i = 0; i < 16; ++i) {
+        char nibble = key[i] >> 4;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+
+        nibble = key[i] & 0x0f;
+        if (nibble <= 9) {
+            nibble += '0';
+        } else {
+            nibble += 'a' - 10;
+        }
+        out->append(&nibble, 1);
+    }
+}
+
+static void GetMethodAndURL(
+        const AString &request, AString *method, AString *url) {
+    ssize_t space1 = request.find(" ");
+    CHECK_GE(space1, 0);
+
+    ssize_t space2 = request.find(" ", space1 + 1);
+    CHECK_GE(space2, 0);
+
+    method->setTo(request, 0, space1);
+    url->setTo(request, space1 + 1, space2 - space1);
+}
+
+void ARTSPConnection::addAuthentication(AString *request) {
+    if (mAuthType == NONE) {
+        return;
+    }
+
+    // Find the boundary between headers and the body.
+    ssize_t i = request->find("\r\n\r\n");
+    CHECK_GE(i, 0);
+
+    if (mAuthType == BASIC) {
+        AString tmp;
+        tmp.append(mUser);
+        tmp.append(":");
+        tmp.append(mPass);
+
+        AString out;
+        encodeBase64(tmp.c_str(), tmp.size(), &out);
+
+        AString fragment;
+        fragment.append("Authorization: Basic ");
+        fragment.append(out);
+        fragment.append("\r\n");
+
+        request->insert(fragment, i + 2);
+
+        return;
+    }
+
+    CHECK_EQ((int)mAuthType, (int)DIGEST);
+
+    AString method, url;
+    GetMethodAndURL(*request, &method, &url);
+
+    AString A1;
+    A1.append(mUser);
+    A1.append(":");
+    A1.append("Streaming Server");
+    A1.append(":");
+    A1.append(mPass);
+
+    AString A2;
+    A2.append(method);
+    A2.append(":");
+    A2.append(url);
+
+    AString HA1, HA2;
+    H(A1, &HA1);
+    H(A2, &HA2);
+
+    AString tmp;
+    tmp.append(HA1);
+    tmp.append(":");
+    tmp.append(mNonce);
+    tmp.append(":");
+    tmp.append(HA2);
+
+    AString digest;
+    H(tmp, &digest);
+
+    AString fragment;
+    fragment.append("Authorization: Digest ");
+    fragment.append("nonce=\"");
+    fragment.append(mNonce);
+    fragment.append("\", ");
+    fragment.append("username=\"");
+    fragment.append(mUser);
+    fragment.append("\", ");
+    fragment.append("uri=\"");
+    fragment.append(url);
+    fragment.append("\", ");
+    fragment.append("response=\"");
+    fragment.append(digest);
+    fragment.append("\"");
+    fragment.append("\r\n");
+
+    request->insert(fragment, i + 2);
+}
+
+// static
+void ARTSPConnection::MakeUserAgent(AString *userAgent) {
+    userAgent->clear();
+    userAgent->setTo("User-Agent: stagefright/1.1 (Linux;Android ");
+
+#if (PROPERTY_VALUE_MAX < 8)
+#error "PROPERTY_VALUE_MAX must be at least 8"
+#endif
+
+    char value[PROPERTY_VALUE_MAX];
+    property_get("ro.build.version.release", value, "Unknown");
+    userAgent->append(value);
+    userAgent->append(")\r\n");
+}
+
+void ARTSPConnection::addUserAgent(AString *request) const {
+    // Find the boundary between headers and the body.
+    ssize_t i = request->find("\r\n\r\n");
+    CHECK_GE(i, 0);
+
+    request->insert(mUserAgent, i + 2);
+}
+
+}  // namespace android
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARTSPConnection.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2010 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.
+ */
+
+#ifndef A_RTSP_CONNECTION_H_
+
+#define A_RTSP_CONNECTION_H_
+
+#include <media/stagefright/foundation/AHandler.h>
+#include <media/stagefright/foundation/AString.h>
+
+namespace android {
+
+struct ABuffer;
+
+struct ARTSPResponse : public RefBase {
+    unsigned long mStatusCode;
+    AString mStatusLine;
+    KeyedVector<AString,AString> mHeaders;
+    sp<ABuffer> mContent;
+};
+
+struct ARTSPConnection : public AHandler {
+    ARTSPConnection(bool uidValid = false, uid_t uid = 0);
+
+    void connect(const char *url, const sp<AMessage> &reply);
+    void disconnect(const sp<AMessage> &reply);
+
+    void sendRequest(const char *request, const sp<AMessage> &reply);
+
+    void observeBinaryData(const sp<AMessage> &reply);
+
+    static bool ParseURL(
+            const char *url, AString *host, unsigned *port, AString *path,
+            AString *user, AString *pass);
+
+protected:
+    virtual ~ARTSPConnection();
+    virtual void onMessageReceived(const sp<AMessage> &msg);
+
+private:
+    enum State {
+        DISCONNECTED,
+        CONNECTING,
+        CONNECTED,
+    };
+
+    enum {
+        kWhatConnect            = 'conn',
+        kWhatDisconnect         = 'disc',
+        kWhatCompleteConnection = 'comc',
+        kWhatSendRequest        = 'sreq',
+        kWhatReceiveResponse    = 'rres',
+        kWhatObserveBinaryData  = 'obin',
+    };
+
+    enum AuthType {
+        NONE,
+        BASIC,
+        DIGEST
+    };
+
+    static const int64_t kSelectTimeoutUs;
+
+    bool mUIDValid;
+    uid_t mUID;
+    State mState;
+    AString mUser, mPass;
+    AuthType mAuthType;
+    AString mNonce;
+    int mSocket;
+    int32_t mConnectionID;
+    int32_t mNextCSeq;
+    bool mReceiveResponseEventPending;
+
+    KeyedVector<int32_t, sp<AMessage> > mPendingRequests;
+
+    sp<AMessage> mObserveBinaryMessage;
+
+    AString mUserAgent;
+
+    void performDisconnect();
+
+    void onConnect(const sp<AMessage> &msg);
+    void onDisconnect(const sp<AMessage> &msg);
+    void onCompleteConnection(const sp<AMessage> &msg);
+    void onSendRequest(const sp<AMessage> &msg);
+    void onReceiveResponse();
+
+    void flushPendingRequests();
+    void postReceiveReponseEvent();
+
+    // Return false iff something went unrecoverably wrong.
+    bool receiveRTSPReponse();
+    status_t receive(void *data, size_t size);
+    bool receiveLine(AString *line);
+    sp<ABuffer> receiveBinaryData();
+    bool notifyResponseListener(const sp<ARTSPResponse> &response);
+
+    bool parseAuthMethod(const sp<ARTSPResponse> &response);
+    void addAuthentication(AString *request);
+
+    void addUserAgent(AString *request) const;
+
+    status_t findPendingRequest(
+            const sp<ARTSPResponse> &response, ssize_t *index) const;
+
+    bool handleServerRequest(const sp<ARTSPResponse> &request);
+
+    static bool ParseSingleUnsignedLong(
+            const char *from, unsigned long *x);
+
+    static void MakeUserAgent(AString *userAgent);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARTSPConnection);
+};
+
+}  // namespace android
+
+#endif  // A_RTSP_CONNECTION_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARawAudioAssembler.cpp
@@ -0,0 +1,143 @@
+/*
+ * Copyright (C) 2011 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 "ARawAudioAssembler"
+#include <utils/Log.h>
+
+#include "ARawAudioAssembler.h"
+
+#include "ARTPSource.h"
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/hexdump.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+ARawAudioAssembler::ARawAudioAssembler(
+        const sp<AMessage> &notify, const char *desc, const AString &params)
+    : mNotifyMsg(notify),
+      mNextExpectedSeqNoValid(false),
+      mNextExpectedSeqNo(0) {
+}
+
+ARawAudioAssembler::~ARawAudioAssembler() {
+}
+
+ARTPAssembler::AssemblyStatus ARawAudioAssembler::assembleMore(
+        const sp<ARTPSource> &source) {
+    return addPacket(source);
+}
+
+ARTPAssembler::AssemblyStatus ARawAudioAssembler::addPacket(
+        const sp<ARTPSource> &source) {
+    List<sp<ABuffer> > *queue = source->queue();
+
+    if (queue->empty()) {
+        return NOT_ENOUGH_DATA;
+    }
+
+    if (mNextExpectedSeqNoValid) {
+        List<sp<ABuffer> >::iterator it = queue->begin();
+        while (it != queue->end()) {
+            if ((uint32_t)(*it)->int32Data() >= mNextExpectedSeqNo) {
+                break;
+            }
+
+            it = queue->erase(it);
+        }
+
+        if (queue->empty()) {
+            return NOT_ENOUGH_DATA;
+        }
+    }
+
+    sp<ABuffer> buffer = *queue->begin();
+
+    if (!mNextExpectedSeqNoValid) {
+        mNextExpectedSeqNoValid = true;
+        mNextExpectedSeqNo = (uint32_t)buffer->int32Data();
+    } else if ((uint32_t)buffer->int32Data() != mNextExpectedSeqNo) {
+        LOGV("Not the sequence number I expected");
+
+        return WRONG_SEQUENCE_NUMBER;
+    }
+
+    // hexdump(buffer->data(), buffer->size());
+
+    if (buffer->size() < 1) {
+        queue->erase(queue->begin());
+        ++mNextExpectedSeqNo;
+
+        LOGV("raw audio packet too short.");
+
+        return MALFORMED_PACKET;
+    }
+
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setObject("access-unit", buffer);
+    msg->post();
+
+    queue->erase(queue->begin());
+    ++mNextExpectedSeqNo;
+
+    return OK;
+}
+
+void ARawAudioAssembler::packetLost() {
+    CHECK(mNextExpectedSeqNoValid);
+    ++mNextExpectedSeqNo;
+}
+
+void ARawAudioAssembler::onByeReceived() {
+    sp<AMessage> msg = mNotifyMsg->dup();
+    msg->setInt32("eos", true);
+    msg->post();
+}
+
+// static
+bool ARawAudioAssembler::Supports(const char *desc) {
+    return !strncmp(desc, "PCMU/", 5)
+        || !strncmp(desc, "PCMA/", 5);
+}
+
+// static
+void ARawAudioAssembler::MakeFormat(
+        const char *desc, const sp<MetaData> &format) {
+    if (!strncmp(desc, "PCMU/", 5)) {
+        format->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_MLAW);
+    } else if (!strncmp(desc, "PCMA/", 5)) {
+        format->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_G711_ALAW);
+    } else {
+        TRESPASS();
+    }
+
+    int32_t sampleRate, numChannels;
+    ASessionDescription::ParseFormatDesc(
+            desc, &sampleRate, &numChannels);
+
+    format->setInt32(kKeySampleRate, sampleRate);
+    format->setInt32(kKeyChannelCount, numChannels);
+}
+
+}  // namespace android
+
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ARawAudioAssembler.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2011 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.
+ */
+
+#ifndef A_RAW_AUDIO_ASSEMBLER_H_
+
+#define A_RAW_AUDIO_ASSEMBLER_H_
+
+#include "ARTPAssembler.h"
+
+namespace android {
+
+struct AMessage;
+struct AString;
+struct MetaData;
+
+struct ARawAudioAssembler : public ARTPAssembler {
+    ARawAudioAssembler(
+            const sp<AMessage> &notify,
+            const char *desc, const AString &params);
+
+    static bool Supports(const char *desc);
+
+    static void MakeFormat(
+            const char *desc, const sp<MetaData> &format);
+
+protected:
+    virtual ~ARawAudioAssembler();
+
+    virtual AssemblyStatus assembleMore(const sp<ARTPSource> &source);
+    virtual void onByeReceived();
+    virtual void packetLost();
+
+private:
+    bool mIsWide;
+
+    sp<AMessage> mNotifyMsg;
+    bool mNextExpectedSeqNoValid;
+    uint32_t mNextExpectedSeqNo;
+
+    AssemblyStatus addPacket(const sp<ARTPSource> &source);
+
+    DISALLOW_EVIL_CONSTRUCTORS(ARawAudioAssembler);
+};
+
+}  // namespace android
+
+#endif  // A_RAW_AUDIO_ASSEMBLER_H_
new file mode 100644
--- /dev/null
+++ b/netwerk/protocol/rtsp/rtsp/ASessionDescription.cpp
@@ -0,0 +1,336 @@
+/*
+ * Copyright (C) 2010 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 "ASessionDescription"
+#include <utils/Log.h>
+
+#include "ASessionDescription.h"
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AString.h>
+
+#include <stdlib.h>
+
+namespace android {
+
+ASessionDescription::ASessionDescription()
+    : mIsValid(false) {
+}
+
+ASessionDescription::~ASessionDescription() {
+}
+
+bool ASessionDescription::setTo(const void *data, size_t size) {
+    mIsValid = parse(data, size);
+
+    if (!mIsValid) {
+        mTracks.clear();
+        mFormats.clear();
+    }
+
+    return mIsValid;
+}
+
+bool ASessionDescription::parse(const void *data, size_t size) {
+    mTracks.clear();
+    mFormats.clear();
+
+    mTracks.push(Attribs());
+    mFormats.push(AString("[root]"));
+
+    AString desc((const char *)data, size);
+
+    size_t i = 0;
+    for (;;) {
+        ssize_t eolPos = desc.find("\n", i);
+
+        if (eolPos < 0) {
+            break;
+        }
+
+        AString line;
+        if ((size_t)eolPos > i && desc.c_str()[eolPos - 1] == '\r') {
+            // We accept both '\n' and '\r\n' line endings, if it's
+            // the latter, strip the '\r' as well.
+            line.setTo(desc, i, eolPos - i - 1);
+        } else {
+            line.setTo(desc, i, eolPos - i);
+        }
+
+        if (line.empty()) {
+            i = eolPos + 1;
+            continue;
+        }
+
+        if (line.size() < 2 || line.c_str()[1] != '=') {
+            return false;
+        }
+
+        LOGI("%s", line.c_str());
+
+        switch (line.c_str()[0]) {
+            case 'v':
+            {
+                if (strcmp(line.c_str(), "v=0")) {
+                    return false;
+                }
+                break;
+            }
+
+            case 'a':
+            case 'b':
+            {
+                AString key, value;
+
+                ssize_t colonPos = line.find(":", 2);
+                if (colonPos < 0) {
+                    key = line;
+                } else {
+                    key.setTo(line, 0, colonPos);
+
+                    if (key == "a=fmtp" || key == "a=rtpmap"
+                            || key == "a=framesize") {
+                        ssize_t spacePos = line.find(" ", colonPos + 1);
+                        if (spacePos < 0) {
+                            return false;
+                        }
+
+                        key.setTo(line, 0, spacePos);
+
+                        colonPos = spacePos;
+                    }
+
+                    value.setTo(line, colonPos + 1, line.size() - colonPos - 1);
+                }
+
+                key.trim();
+                value.trim();
+
+                LOGV("adding '%s' => '%s'", key.c_str(), value.c_str());
+
+                mTracks.editItemAt(mTracks.size() - 1).add(key, value);
+                break;
+            }
+
+            case 'm':
+            {
+                LOGV("new section '%s'",
+                     AString(line, 2, line.size() - 2).c_str());
+
+                mTracks.push(Attribs());
+                mFormats.push(AString(line, 2, line.size() - 2));
+                break;
+            }
+
+            def