Squashed commit of the following:

commit 8d179995654c19d9094e51989dc84ed0deba6e04
Author: Andreas Huber <andih@google.com>
Date:   Tue Jul 14 16:30:40 2009 -0700

    Protect stagefright code with BUILD_WITH_STAGEFRIGHT build-define.

commit 99b41acb58c34e8f024c30c833a09fd7b31c4e1f
Author: Andreas Huber <andih@google.com>
Date:   Tue Jul 14 14:06:00 2009 -0700

    OMX::list_nodes API, stagefright --list now lists all available OMX components.

commit 94a74c5b24aae6d7e446fff83d62ae3a57dc351b
Author: Andreas Huber <andih@google.com>
Date:   Mon Jul 13 16:04:29 2009 -0700

    Reenabled "record" commandline application to test encoding, support for h263 codec specific data when writing to .mp4.

commit d363dd74f611577567c66d25900d6264c306468c
Author: Andreas Huber <andih@google.com>
Date:   Mon Jul 13 10:59:58 2009 -0700

    Make non-socket communication in IOMX the default.

commit e89fc82c4fd138c88339887baf72f1d0e67f8660
Author: Andreas Huber <andih@google.com>
Date:   Mon Jul 13 10:32:33 2009 -0700

    The OMX interface now optionally supports communication through the binder only (no sockets involved)

commit 28611de11223fba65299787cdd33f7fdbd6a60a3
Author: Andreas Huber <andih@google.com>
Date:   Fri Jul 10 15:09:13 2009 -0700

    Split off the logic to render to a Surface (not ISurface!) into SurfaceRender.{cpp,h}

commit c0cfa885faaeb17d3fc5436f6447b61708bd6fe1
Author: Andreas Huber <andih@google.com>
Date:   Fri Jul 10 12:21:46 2009 -0700

    Fix display dimensions vs. decoded dimensions display issues, unfortunately breaks software rendering a bit...

commit ae209a2b5d09caed5dd383c910f238926aaa8fc6
Author: Andreas Huber <andih@google.com>
Date:   Fri Jul 10 09:33:29 2009 -0700

    Simple video decoder performance test in "stagefright" commandline tool.

commit 59fe7840111d6f46dd3c48f4950146aed962dbd2
Author: Andreas Huber <andih@google.com>
Date:   Thu Jul 9 16:05:12 2009 -0700

    StagefrightPlayer: Properly publish video dimensions by notifying the listener.

commit 51b6598f0e47be5e12d77f1d14e111e71c593638
Author: Andreas Huber <andih@google.com>
Date:   Thu Jul 9 11:38:15 2009 -0700

    MediaPlayerInterface::AudioSink now supports the specification of a callback for pull-based audio.

commit 31ce53375d748da3e7d30e993ce89dae97fd65ea
Author: Andreas Huber <andih@google.com>
Date:   Wed Jul 8 17:23:37 2009 -0700

    Proper support for pushing audio to a specified AudioSink interface. Still not bulletproof: video falls behind frequently.

commit a23e312b10631a5d47f5c9831205beac6b84ab36
Author: Andreas Huber <andih@google.com>
Date:   Wed Jul 8 15:10:13 2009 -0700

    IGenesisPlayer and friends are no more, java interface now uses local playback instead
    of playing inside the media server process.

commit 42ee5c06ab5ba1c16f50472b0b26a0526635c73c
Author: Andreas Huber <andih@google.com>
Date:   Wed Jul 8 13:17:03 2009 -0700

    MP3 extractor now supports seeking (only fixed bitrate files currently supported)

commit 96a790405763bfa738d1c6845c99a9fdf579b66f
Author: Andreas Huber <andih@google.com>
Date:   Tue Jul 7 10:53:54 2009 -0700

    Keeping up-to-date with new master-gl changes, removing CameraSource for now, removing unnecessary commandline utilities from the build.

commit 6c9dcb7c628c0347154dd7f37913a3a5f04deb99
Author: Andreas Huber <andih@google.com>
Date:   Mon Jul 6 16:40:02 2009 -0700

    Finally take advantage of the fact that the Qcom OMX decoders already place the output buffers into memory accessible by the hardware yuv converter and avoid extra allocations and memcpys in that case. Split of the render logic into subclasses of VideoRenderer.

commit e8d46e81cffc15ffb0bdf03f511e4a4497ca7621
Author: Andreas Huber <andih@google.com>
Date:   Mon Jul 6 12:18:29 2009 -0700

    Added a few more cases of valid MPEG4 files to the sniffer, fixed end of stream notifications in MPEG4 files.

commit 88bffc5ed4033a607080f8777a1c0ec9e3e079de
Author: Andreas Huber <andih@google.com>
Date:   Thu Jul 2 12:25:03 2009 -0700

    MediaPlayerImpl now properly pauses/resumes instead of tearing down the decoder chains and starting back up from the beginning.

commit 732aa0a87c7e8d8a10776b275712649954962414
Author: Andreas Huber <andih@google.com>
Date:   Wed Jul 1 14:58:56 2009 -0700

    Remove pthread_mutex_* and pthread_cond_* in favour of Mutex and Condition.

commit 669e5944b2fe0542a3b8dba0c441a94503e99fb0
Author: Andreas Huber <andih@google.com>
Date:   Wed Jul 1 14:43:02 2009 -0700

    Proper support for end-of-stream signalling of MediaSources.

commit afb038f2dc594c742e554b2d42b86ffc7434e3fc
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 30 16:48:03 2009 -0700

    Support for the "sniffing" of datasources to determine their probable mime type (along with confidence). Slightly refactored the MP3Extractor to share common code with the sniffer.

commit ed3644b8bd56807e1db4b32433a93a2467f1f3c5
Author: Andreas Huber <andih@google.com>
Date:   Fri Jun 26 13:27:52 2009 -0700

    I don't think prepare should automatically start the player.

commit 11eb9b49dc4a0819d366682817cba68613f4fe12
Author: Andreas Huber <andih@google.com>
Date:   Fri Jun 26 11:09:22 2009 -0700

    Somewhat better timing information implemented in the MP3Extractor.

commit cab023a1751735c0ed26c8493498a91d8644c6c9
Author: Andreas Huber <andih@google.com>
Date:   Thu Jun 25 15:41:56 2009 -0700

    Initial checkin for support of mp3 content, extraction and playback. Media extractors now abstracted in MediaExtractor baseclass.

commit 40f4146bb3c647e56690dd9b0fa3ffdea60030dc
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 24 10:41:12 2009 -0700

    Make sure we only allocate surface buffers if there is video to be played.

commit 7f85e7d92fe81740e102e09905b19354f4ac178e
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 24 10:19:32 2009 -0700

    Clean up a failed merge and implement stub for new API in MediaPlayerBase.

commit f0e9d494a4c03402ea7b54891848ca5a50c421b2
Author: Andreas Huber <andih@google.com>
Date:   Fri Jun 12 15:38:03 2009 -0700

    Support yuv420->rgb565 conversion if hardware color conversion is unavailable.

commit 87223248b3099cd5261b94169f84cfd1eba56a8f
Author: Andreas Huber <andih@google.com>
Date:   Fri Jun 12 09:35:56 2009 -0700

    Remove unimplemented and currently unnecessary blocking option from MediaSource::ReadOptions.

commit e07e651f0325892b797f3eb64dbeca0f317afdfd
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 10 16:26:42 2009 -0700

    Enable assertions in HTTPDataSource.

commit 0dc97adebea1721156880f4b3d5c70f8d50c53b2
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 10 12:34:39 2009 -0700

    delete foo.

commit 7f05845dda496e7defee284d32509d127e785784
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 10 12:34:08 2009 -0700

    added foo.

commit e432970c51eeec4e5d549bdb3e2d3e48ffb4f0e7
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 10 11:44:31 2009 -0700

    Make sure to initialize all fields in _all_ constructors... Use a Mutex instead of the raw pthread_mutex_t.

commit d6286a6bbca639952491448fa3e609b775a8a814
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 9 17:11:49 2009 -0700

    send_command is now implemented twice... ugly hack to get both shutdown working correctly and startup as well... playback now "works" on the emulator, too.

commit 8d657e004677a7c5dae4479e8519bcaa94c18aad
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 9 11:58:55 2009 -0700

    Instead of erasing codec specific data once it's been output to the omx node, retain the data and just rewind after a stop.

commit 279fede6ba35cbc37c35616c53406bf2c9720202
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 9 10:58:11 2009 -0700

    Various fixes related to play/pause. Decoder now properly frees OMXMediaBuffers, made MemoryDealer destructor protected instead of public to prevent accidental stack-allocation of MemoryDealer objects.

commit 0f717f15a6328efd3f34082929cd8ad2351095a9
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 9 08:44:12 2009 -0700

    Make sure to rewind codec specific data on OMXDecoder::stop and reset AudioPlayer's notion of time mapping.

commit 0cfb63e33270daf9b215214608ab383f93a31823
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 8 14:25:48 2009 -0700

    Massive rename.

commit 30ed8f476c4431a5275538958b3e281bd17d5eab
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 8 13:52:41 2009 -0700

    send_command and fill/empty buffer commands must be executed in sequence, send_command is now dispatched over the socket instead of through the binder interface to ensure this.

commit b832d04011b2dd8a9632eb7e575bfe1addd1270d
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 8 10:21:17 2009 -0700

    MediaBuffers can now be "clone()", they sit on top of the same data but have their own range and metadata. Once a clone's refcount drops to 0 it decrements the refcount on the original buffer.

commit 4e944672808bdf1ed40a69f3f602f93ebf7b4049
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 8 10:01:01 2009 -0700

    Split MediaBuffer.h into MediaBufferGroup.h and MediaBuffer.h, same with their respective source files.

commit ddb7529b4de4828db51432bd405fddd76cfa3b6a
Author: Andreas Huber <andih@google.com>
Date:   Fri Jun 5 16:30:25 2009 -0700

    More fudging with A/V sync...

commit 1e5a4d8df08237b58c06c6527a33152ae931addf
Author: Andreas Huber <andih@google.com>
Date:   Fri Jun 5 11:54:20 2009 -0700

    API cleanup of MediaSource. Got rid of MediaRing as it really doesn't serve a purpose if OMX decoders are used.

commit 0953c65232aa9753d17d2e25c5f5526eb810cf13
Author: Andreas Huber <andih@google.com>
Date:   Thu Jun 4 16:59:19 2009 -0700

    Experimental TimedEventQueue and an attempt at a cleaner player implementation using it, OMXDecoder is now a little less verbose.

commit 2085b8ea9484a21fa11f94194a27405d413aeecc
Author: Andreas Huber <andih@google.com>
Date:   Thu Jun 4 10:17:01 2009 -0700

    Properly bail out of the AV sync loop if the MediaPlayerImpl is stopped.

commit 471f143458a8a0d93cb1bf9568c63c3967279dc6
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 3 17:32:27 2009 -0700

    Now this is much better AV sync, defer seeking the audio source until we know where the video source synced to.

commit 4010d371dbe62ce4971e13437776cba32cb0bb3d
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 3 16:01:07 2009 -0700

    Tweaked some of the heuristics in AV sync, OMXDecoder is now a little less verbose, seek to a sync sample at or _after_ the requested position.

commit f3d4af1d5a4ac31fa02271b000f6c7ec593f0da9
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 3 15:33:58 2009 -0700

    Change the TimeSource interface's APIs, prefer microseconds over milliseconds now, changed the way MediaPlayerImpl does AV sync.

commit 08bb46fa61d12e852a424698ddb0c90700328f6e
Author: Andreas Huber <andih@google.com>
Date:   Wed Jun 3 11:57:24 2009 -0700

    Using framework's Mutex and Condition classes instead of pthread functions, much saner locking in OMXDecoder.

commit 4d77b4e09e59bbf8c1af009ae7dc62b10af52fd6
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 2 17:12:03 2009 -0700

    MediaSources now publish their format.

commit 7e429a26c57bf936e1a08035d2d11839f9f00310
Author: Andreas Huber <andih@google.com>
Date:   Tue Jun 2 16:38:29 2009 -0700

    MetaData is now implemented in terms of KeyedVector, MetaData is now reference counted.

commit 0fa957aa5c9bcb6ec122ed7b5b97430039be6797
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 1 14:39:55 2009 -0700

    Sync sample seek support.

commit 78a5f090d02e7e6e1a146b29bdb7771eecaa2f86
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 1 13:51:11 2009 -0700

    Distinguish shutdown from port-flush since one requires buffers to be freed and the other one doesn't. Fixes a problem with seek.

commit e57781d3b2c985a74cd8b60e7c8f2d576459d038
Author: Andreas Huber <andih@google.com>
Date:   Mon Jun 1 10:52:20 2009 -0700

    First shot at support for seeking a MediaSource. Sync frames are not properly handled yet.

commit 0551cd78fda78bf66877fb26c5a55e5ae23d8367
Author: Andreas Huber <andih@google.com>
Date:   Thu May 28 16:23:38 2009 -0700

    Preliminary support for camera input.

commit f8ebafa65d10d5a06944de6b2e2992a33121b857
Author: Andreas Huber <andih@google.com>
Date:   Thu May 28 12:13:47 2009 -0700

    Allow NativeMediaPlayer access to Surface's getISurface() method.

commit b1a9fb36eeccd9f49d28c1c64b31fccbd6506eeb
Author: Andreas Huber <andih@google.com>
Date:   Thu May 28 11:38:25 2009 -0700

    Header files are now in their own genesis2 hierarchy below frameworks/base/include.

commit f49b8b234b8f6ce83148787eef540dd920f04aa6
Author: Andreas Huber <andih@google.com>
Date:   Thu May 28 11:22:03 2009 -0700

    moved MPEG4Writer into libgenesis2.

commit 5224254919dac4728677335ea60429fa16f518b4
Author: Andreas Huber <andih@google.com>
Date:   Thu May 28 11:13:38 2009 -0700

    Everything now lives in the android namespace, integrated errors with android errors, renamed MediaPlayer to MediaPlayerImpl.

commit f9a9d84f7e0465ef5d1938a068655ba39527cf99
Author: Andreas Huber <andih@google.com>
Date:   Wed May 27 16:43:11 2009 -0700

    Support for getPosition() and getDuration().

commit 3206cc5140bcbc88be039f8d94b954af63e69752
Author: Andreas Huber <andih@google.com>
Date:   Wed May 27 15:37:45 2009 -0700

    Support for {MediaPlayer, GenesisPlayer, Genesis2Player}::isPlaying()

commit 9f41b248e0dfce71bbc50f883750cec52ebfd1fe
Author: Andreas Huber <andih@google.com>
Date:   Wed May 27 13:49:50 2009 -0700

    Proper disconnection from the OMX service.

commit cdcf47a2c31599109ee42277c7fa14af3c600091
Author: Andreas Huber <andih@google.com>
Date:   Tue May 26 17:27:06 2009 -0700

    New player type for the genesis player in libmediaplayerservice. Playing video through an ISurface now.

commit 0586af20ac9ae0403dd190bafd290b0f90549723
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 15:53:19 2009 -0700

    Fix a buffer overflow error by adding padding in case we need to de-frame AVC data.

commit eac62fff6ad2ffb7b75c0d4a1b9620d33e93fb9b
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 13:35:30 2009 -0700

    Support for http URIs in MediaPlayer, compensate for AudioTrack latency.

commit a7db456e3173d94d6b5e457547e2a6a5f7a8e1cc
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 11:33:25 2009 -0700

    Support for URIs in MediaPlayer class, support for shoutcast streams in MediaPlayer, force shutdown of OMX nodes if they don't shutdown cleanly within a second.

commit 1f0268f4f14ad5215668f34e06a420850174d665
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 08:33:16 2009 -0700

    and foo.txt is gone again.

commit 0feca4e0919f345704d78ec78894bac5027bd5ed
Author: Andreas Huber <andih@dhcp-172-19-27-148.mtv.corp.google.com>
Date:   Fri May 22 08:28:49 2009 -0700

    added foo.txt

commit 9c7d04b35cdc895a2de3602dc46522f608bd205f
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 08:28:04 2009 -0700

    delete foo again

commit fec3ca1720992cce51b9a92b86b945da88a6b16c
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 08:27:45 2009 -0700

    weird

commit 30a6571302333146e537e537a3a8986245cca87c
Author: Andreas Huber <andih@dhcp-172-19-27-148.mtv.corp.google.com>
Date:   Fri May 22 08:18:55 2009 -0700

    reverse test

commit dea3d99dd0fe9386e8a3c51954d9d0fe2be09d38
Author: Andreas Huber <andih@google.com>
Date:   Fri May 22 08:18:22 2009 -0700

    test

commit a9eeafec6e46072cac41e54ebdbd153cb7ae7518
Author: Andreas Huber <andih@google.com>
Date:   Thu May 21 16:37:50 2009 -0700

    Use the video ring again for now.

commit e05ff3d3c1c4f89ef499a2b172138a775fd694e3
Author: Andreas Huber <andih@google.com>
Date:   Thu May 21 15:42:19 2009 -0700

    Export to .mp4 now works for MPEG4 video content. Fixed a bug in the sample table wrt co64 chunks.

commit 5c44c540374a4e50c4e8aaab2b7a34aed6e1391e
Author: Andreas Huber <andih@google.com>
Date:   Thu May 21 09:56:11 2009 -0700

    Fix the build, all binder-related headers have moved and there's a new library libbinde
    r to link against now.

commit 23e0a24404d16c898c2c6b3c7495ad303844bc71
Author: Andreas Huber <andih@google.com>
Date:   Thu May 21 08:42:04 2009 -0700

    Initial checkin of MPEG4Writer, a class to export media sources into an mpeg4 file. MPEG4/AVC metadata (efss & friends) not currently working.

commit 9cd77adb1ac194197364ffad00a0d7215f458e1a
Author: Andreas Huber <andih@google.com>
Date:   Wed May 20 12:31:45 2009 -0700

    New shell command to test recording/encoding. Various changes to OMXDecoder to support encoding properly. Now each OMXDecoder needs a dedicated thread on the client side since onEvent responses could block which would cause deadlocks if a node depends on the output of another node for example.

commit 5c510fb55ffed7ec25305999ed9b6c1eec48d503
Author: Andreas Huber <andih@google.com>
Date:   Mon May 18 15:40:06 2009 -0700

    Make media decoding the default in the genesis2 commandline app instead of shoutcast playback.

commit f6c7a2040b8eb43326621f12c3a936e09b3a70e3
Author: Andreas Huber <andih@google.com>
Date:   Mon May 18 11:03:04 2009 -0700

    The king is dead, long live the kind... Replaced old OMX binder interface with new OMX binder interface/socket hybrid.

commit 0d4955eb5df9bfafb576d22f2ed572644d6d69ac
Author: Andreas Huber <andih@google.com>
Date:   Thu May 14 12:39:34 2009 -0700

    dynamic negotiation of NAL framing with the MPEG4Source, removed stale code from genesis2 commandline tool. Enable support form PV H264 software decoder.

commit 380369f54a8027cbc9349305184d6bf527a24efe
Author: Andreas Huber <andih@google.com>
Date:   Wed May 13 17:32:00 2009 -0700

    Explore the option of running everything inside the mediaserver process by encapsulating the player in an IGenesisPlayer binder interface.

commit b5cada56b9089a23e0c461352a97dc71e08b424d
Author: Andreas Huber <andih@google.com>
Date:   Wed May 13 12:11:06 2009 -0700

    Introduction of TimeSource class, clean shutdown of OMX Nodes.

commit 89f6e582e605dcb2149eb67f73fe9345d4b9be2e
Author: Andreas Huber <andih@google.com>
Date:   Tue May 12 17:11:56 2009 -0700

    Helper classes MediaRing and MediaPlayer.

commit 9aaed8739b4bb941473da367fb88f63224d2f1b7
Author: Andreas Huber <andih@google.com>
Date:   Tue May 12 14:19:18 2009 -0700

    Using mmap to map a file instead of going through FILE* improves performance, who knew?

commit ad097a78bbd002c59193ffdc39b64935928426c7
Author: Andreas Huber <andih@google.com>
Date:   Mon May 11 16:42:42 2009 -0700

    Added a performance test, refactored AudioDecoder a little.

commit bdd1b80ab3f3ba43e04f1f996e7fa84ed2e2728a
Author: Andreas Huber <andih@google.com>
Date:   Fri May 8 16:19:29 2009 -0700

    No more copying the output buffers coming out of the decoder, NAL separation also done in the extractor to avoid copying the data.

commit 858955b42c29b8033841a8caaa56c2a449810ef3
Author: Andreas Huber <andih@google.com>
Date:   Fri May 8 14:02:38 2009 -0700

    Change from a model in which the caller specifies the destination buffer to one where the MediaSource returns a buffer.

commit 95098b6d4f8bdc37dfb6a109ca58db4d9e414e2d
Author: Andreas Huber <andih@google.com>
Date:   Fri May 8 13:21:45 2009 -0700

    Changes to support multiple pieces of codec specific data to the decoder, de-frame nal units from the h.264 stream and insert start codes

commit 3941fcf87a48dda81683df727dfb1381f62d9184
Author: Andreas Huber <andih@google.com>
Date:   Thu May 7 16:37:45 2009 -0700

    some refactoring. Most streams play now.

commit d3152862840d4dd39068e5197bec8674f3d55dd5
Author: Andreas Huber <andih@google.com>
Date:   Thu May 7 15:37:32 2009 -0700

    Misc. changes to support H264 decoding using the qcom hardware decoder

commit d4ecdfe7af36e7468270d1c2cbbdc540efe0351e
Author: Andreas Huber <andih@google.com>
Date:   Thu May 7 09:28:18 2009 -0700

    Timestamps are now stored as a fraction, i.e. units and scale, also, PV appears to violate OMX specs by assuming timestamps are in milliseconds instead of microseconds.

commit fe6a50943f885e065d4061aa727a54417e9f36db
Author: Andreas Huber <andih@google.com>
Date:   Wed May 6 14:18:27 2009 -0700

    Refactored commandline app instantiation of the decoder.

commit da1ff2724d0e78e8b360d9a2c34c55dd0e2c0492
Author: Andreas Huber <andih@google.com>
Date:   Wed May 6 10:41:14 2009 -0700

    Proper audio/video synchronization.

commit 569ee3a23f600f4b6b87c6178c0223b06e97f201
Author: Andreas Huber <andih@google.com>
Date:   Wed May 6 09:17:20 2009 -0700

    Preliminary support for timestamps.

commit e4e302a89207550d8a7170d8c0afbd9c15047c84
Author: Andreas Huber <andih@google.com>
Date:   Tue May 5 15:13:12 2009 -0700

    Another quick hack job to make CachingDataSource thread-safe as well.

commit dfb376a58bd7c09aa04e89558186fcfe13fe696c
Author: Andreas Huber <andih@google.com>
Date:   Tue May 5 15:08:14 2009 -0700

    Quick rushjob to make FileSource and SampleTable thread safe.

commit 02b29e7ebdab1f8b959b6c177ceb3b89d998f375
Author: Andreas Huber <andih@google.com>
Date:   Mon May 4 17:22:04 2009 -0700

    Made video decoding work for mpeg4/h263 and mostly(?) with AVC content.

commit 3a57b8ff15e48f5a0af03bf0a99c32e240dbb7ef
Author: Andreas Huber <andih@google.com>
Date:   Mon May 4 09:26:42 2009 -0700

    Getting rid of old sample code to drive the OMXNode binder interface.

commit 5b046aed0cf524a57ea711bc1e43ea80e57b2cb3
Author: Andreas Huber <andih@google.com>
Date:   Fri May 1 16:21:11 2009 -0700

    Moved files from the commandline utility into the framework, renamed MP3Decoder to AudioDecoder

commit 7f3fbba21512a078b732cc52c7bd5ba5acb9a317
Author: Andreas Huber <andih@google.com>
Date:   Fri May 1 15:56:19 2009 -0700

    Fixed buffer ordering and an off-by-1 error in the SampleTable code. Plays all AACs now :)

commit 024d06baab8f2c540dde16f7e7a4b6dd57f638c7
Author: Andreas Huber <andih@google.com>
Date:   Fri May 1 11:52:48 2009 -0700

    Some AAC content in .3gp or .mp4 files plays correctly now, other does not. Implemented ESFS parser.

commit 1470427866b0d3e0ce6848b4edc01c13a09be289
Author: Andreas Huber <andih@google.com>
Date:   Thu Apr 30 15:19:31 2009 -0700

    Another checkpoint, amr decoding now functional.

commit e612d044439f56331b6a2bc4a88622e04d4d42e7
Author: Andreas Huber <andih@google.com>
Date:   Wed Apr 29 12:11:29 2009 -0700

    string wrapper around String8, various fixes, shoutcast now works again.

commit 2ea52dfb1a39bf3a223ffc87855e54919e2daff7
Author: Andreas Huber <andih@google.com>
Date:   Wed Apr 29 11:08:18 2009 -0700

    Initial check-in of OMXNode binder interface.

commit 7880d2957f74c0d7fba7679431ff5adb3780543b
Author: Andreas Huber <andih@google.com>
Date:   Wed Apr 29 10:05:01 2009 -0700

    Initial check-in of both libgenesis2 and the genesis2 commandline tool.
diff --git a/cmds/stagefright/Android.mk b/cmds/stagefright/Android.mk
new file mode 100644
index 0000000..fd681a2
--- /dev/null
+++ b/cmds/stagefright/Android.mk
@@ -0,0 +1,66 @@
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+	stagefright.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright
+
+LOCAL_C_INCLUDES:= \
+	frameworks/base/media/libstagefright \
+	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+        $(TOP)/external/opencore/android
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= stagefright
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=         \
+        record.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+	libstagefright
+
+LOCAL_C_INCLUDES:= \
+	frameworks/base/media/libstagefright \
+	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+        $(TOP)/external/opencore/android
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_MODULE:= record
+
+include $(BUILD_EXECUTABLE)
+
+################################################################################
+
+# include $(CLEAR_VARS)
+# 
+# LOCAL_SRC_FILES:=         \
+#         play.cpp
+# 
+# LOCAL_SHARED_LIBRARIES := \
+# 	libstagefright
+# 
+# LOCAL_C_INCLUDES:= \
+# 	frameworks/base/media/libstagefright \
+# 	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+#         $(TOP)/external/opencore/android
+# 
+# LOCAL_CFLAGS += -Wno-multichar
+# 
+# LOCAL_MODULE:= play
+# 
+# include $(BUILD_EXECUTABLE)
+
+endif
diff --git a/cmds/stagefright/WaveWriter.h b/cmds/stagefright/WaveWriter.h
new file mode 100644
index 0000000..a0eb66e
--- /dev/null
+++ b/cmds/stagefright/WaveWriter.h
@@ -0,0 +1,71 @@
+/*
+ * Copyright (C) 2009 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 ANDROID_WAVEWRITER_H_
+
+#define ANDROID_WAVEWRITER_H_
+
+namespace android {
+
+class WaveWriter {
+public:
+    WaveWriter(const char *filename,
+               uint16_t num_channels, uint32_t sampling_rate)
+        : mFile(fopen(filename, "wb")),
+          mTotalBytes(0) {
+        fwrite("RIFFxxxxWAVEfmt \x10\x00\x00\x00\x01\x00", 1, 22, mFile); 
+        write_u16(num_channels);
+        write_u32(sampling_rate);
+        write_u32(sampling_rate * num_channels * 2);
+        write_u16(num_channels * 2);
+        write_u16(16);
+        fwrite("dataxxxx", 1, 8, mFile);
+    }
+
+    ~WaveWriter() {
+        fseek(mFile, 40, SEEK_SET);
+        write_u32(mTotalBytes);
+
+        fseek(mFile, 4, SEEK_SET);
+        write_u32(36 + mTotalBytes);
+
+        fclose(mFile);
+        mFile = NULL;
+    }
+
+    void Append(const void *data, size_t size) {
+        fwrite(data, 1, size, mFile);
+        mTotalBytes += size;
+    }
+
+private:
+    void write_u16(uint16_t x) {
+        fputc(x & 0xff, mFile);
+        fputc(x >> 8, mFile);
+    }
+
+    void write_u32(uint32_t x) {
+        write_u16(x & 0xffff);
+        write_u16(x >> 16);
+    }
+
+    FILE *mFile;
+    size_t mTotalBytes;
+};
+
+}  // namespace android
+
+#endif  // ANDROID_WAVEWRITER_H_
diff --git a/cmds/stagefright/play.cpp b/cmds/stagefright/play.cpp
new file mode 100644
index 0000000..c6e778e
--- /dev/null
+++ b/cmds/stagefright/play.cpp
@@ -0,0 +1,295 @@
+/*
+ * Copyright (C) 2009 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 <binder/ProcessState.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/TimedEventQueue.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXDecoder.h>
+
+using namespace android;
+
+struct NewPlayer {
+    NewPlayer();
+    ~NewPlayer();
+
+    void setSource(const char *uri);
+    void start();
+    void pause();
+    void stop();
+
+private:
+    struct PlayerEvent : public TimedEventQueue::Event {
+        PlayerEvent(NewPlayer *player,
+                    void (NewPlayer::*method)(int64_t realtime_us))
+            : mPlayer(player),
+              mMethod(method) {
+        }
+
+        virtual void fire(TimedEventQueue *queue, int64_t realtime_us) {
+            (mPlayer->*mMethod)(realtime_us);
+        }
+
+    private:
+        NewPlayer *mPlayer;
+        void (NewPlayer::*mMethod)(int64_t realtime_us);
+
+        PlayerEvent(const PlayerEvent &);
+        PlayerEvent &operator=(const PlayerEvent &);
+    };
+
+    struct PlayVideoFrameEvent : public TimedEventQueue::Event {
+        PlayVideoFrameEvent(NewPlayer *player, MediaBuffer *buffer)
+            : mPlayer(player),
+              mBuffer(buffer) {
+        }
+
+        virtual ~PlayVideoFrameEvent() {
+            if (mBuffer != NULL) {
+                mBuffer->release();
+                mBuffer = NULL;
+            }
+        }
+
+        virtual void fire(TimedEventQueue *queue, int64_t realtime_us) {
+            mPlayer->onPlayVideoFrame(realtime_us, mBuffer);
+            mBuffer = NULL;
+        }
+
+    private:
+        NewPlayer *mPlayer;
+        MediaBuffer *mBuffer;
+
+        PlayVideoFrameEvent(const PlayVideoFrameEvent &);
+        PlayVideoFrameEvent &operator=(const PlayVideoFrameEvent &);
+    };
+
+    OMXClient mClient;
+
+    MPEG4Extractor *mExtractor;
+    MediaSource *mAudioSource;
+    OMXDecoder *mAudioDecoder;
+    MediaSource *mVideoSource;
+    OMXDecoder *mVideoDecoder;
+
+    int32_t mVideoWidth, mVideoHeight;
+
+    TimedEventQueue mQueue;
+    wp<TimedEventQueue::Event> mPlayVideoFrameEvent;
+
+    int64_t mMediaTimeUsStart;
+    int64_t mRealTimeUsStart;
+
+    void setAudioSource(MediaSource *source);
+    void setVideoSource(MediaSource *source);
+
+    int64_t approxRealTime(int64_t mediatime_us) const;
+
+    void onStart(int64_t realtime_us);
+    void onPause(int64_t realtime_us);
+    void onFetchVideoFrame(int64_t realtime_us);
+    void onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer);
+
+    static int64_t getMediaBufferTimeUs(MediaBuffer *buffer);
+
+    NewPlayer(const NewPlayer &);
+    NewPlayer &operator=(const NewPlayer &);
+};
+
+NewPlayer::NewPlayer()
+    : mExtractor(NULL),
+      mAudioSource(NULL),
+      mAudioDecoder(NULL),
+      mVideoSource(NULL),
+      mVideoDecoder(NULL),
+      mVideoWidth(0),
+      mVideoHeight(0) {
+    status_t err = mClient.connect();
+    assert(err == OK);
+}
+
+NewPlayer::~NewPlayer() {
+    stop();
+
+    mClient.disconnect();
+}
+
+void NewPlayer::setSource(const char *uri) {
+    stop();
+
+    mExtractor = new MPEG4Extractor(new MmapSource(uri));
+
+    int num_tracks;
+    status_t err = mExtractor->countTracks(&num_tracks);
+    assert(err == OK);
+
+    for (int i = 0; i < num_tracks; ++i) {
+        const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+        assert(meta != NULL);
+
+        const char *mime;
+        if (!meta->findCString(kKeyMIMEType, &mime)) {
+            continue;
+        }
+
+        bool is_audio = false;
+        bool is_acceptable = false;
+        if (!strncasecmp(mime, "audio/", 6)) {
+            is_audio = true;
+            is_acceptable = (mAudioSource == NULL);
+        } else if (!strncasecmp(mime, "video/", 6)) {
+            is_acceptable = (mVideoSource == NULL);
+        }
+
+        if (!is_acceptable) {
+            continue;
+        }
+
+        MediaSource *source;
+        if (mExtractor->getTrack(i, &source) != OK) {
+            continue;
+        }
+
+        if (is_audio) {
+            setAudioSource(source);
+        } else {
+            setVideoSource(source);
+        }
+    }
+}
+
+void NewPlayer::setAudioSource(MediaSource *source) {
+    mAudioSource = source;
+
+    sp<MetaData> meta = source->getFormat();
+
+    mAudioDecoder = OMXDecoder::Create(&mClient, meta);
+    mAudioDecoder->setSource(source);
+}
+
+void NewPlayer::setVideoSource(MediaSource *source) {
+    mVideoSource = source;
+
+    sp<MetaData> meta = source->getFormat();
+
+    bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
+    assert(success);
+
+    success = meta->findInt32(kKeyHeight, &mVideoHeight);
+    assert(success);
+
+    mVideoDecoder = OMXDecoder::Create(&mClient, meta);
+    mVideoDecoder->setSource(source);
+}
+
+void NewPlayer::start() {
+    mQueue.start();
+    mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onStart));
+}
+
+void NewPlayer::pause() {
+    mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onPause));
+}
+
+void NewPlayer::stop() {
+    mQueue.stop();
+
+    delete mVideoDecoder;
+    mVideoDecoder = NULL;
+    delete mVideoSource;
+    mVideoSource = NULL;
+    mVideoWidth = mVideoHeight = 0;
+
+    delete mAudioDecoder;
+    mAudioDecoder = NULL;
+    delete mAudioSource;
+    mAudioSource = NULL;
+
+    delete mExtractor;
+    mExtractor = NULL;
+}
+
+int64_t NewPlayer::approxRealTime(int64_t mediatime_us) const {
+    return mRealTimeUsStart + (mediatime_us - mMediaTimeUsStart);
+}
+
+void NewPlayer::onStart(int64_t realtime_us) {
+    mRealTimeUsStart = TimedEventQueue::getRealTimeUs();
+
+    if (mVideoDecoder != NULL) {
+        mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame));
+    }
+}
+
+void NewPlayer::onFetchVideoFrame(int64_t realtime_us) {
+    MediaBuffer *buffer;
+    status_t err = mVideoDecoder->read(&buffer);
+    assert(err == OK);
+
+    int64_t mediatime_us = getMediaBufferTimeUs(buffer);
+
+    sp<TimedEventQueue::Event> event = new PlayVideoFrameEvent(this, buffer);
+    mPlayVideoFrameEvent = event;
+
+    mQueue.postTimedEvent(event, approxRealTime(mediatime_us));
+}
+
+// static
+int64_t NewPlayer::getMediaBufferTimeUs(MediaBuffer *buffer) {
+    int32_t units, scale;
+    bool success =
+        buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+    assert(success);
+    success =
+        buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+    assert(success);
+
+    return (int64_t)units * 1000000 / scale;
+}
+
+void NewPlayer::onPlayVideoFrame(int64_t realtime_us, MediaBuffer *buffer) {
+    LOGI("playing video frame (mediatime: %.2f sec)\n",
+         getMediaBufferTimeUs(buffer) / 1E6);
+    fflush(stdout);
+
+    buffer->release();
+    buffer = NULL;
+
+    mQueue.postEvent(new PlayerEvent(this, &NewPlayer::onFetchVideoFrame));
+}
+
+void NewPlayer::onPause(int64_t realtime_us) {
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s filename\n", argv[0]);
+        return 1;
+    }
+
+    NewPlayer player;
+    player.setSource(argv[1]);
+    player.start();
+    sleep(10);
+    player.stop();
+
+    return 0;
+}
diff --git a/cmds/stagefright/record.cpp b/cmds/stagefright/record.cpp
new file mode 100644
index 0000000..12bdead
--- /dev/null
+++ b/cmds/stagefright/record.cpp
@@ -0,0 +1,181 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/ProcessState.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXDecoder.h>
+
+using namespace android;
+
+class DummySource : public MediaSource {
+public:
+    DummySource(int width, int height)
+        : mSize((width * height * 3) / 2) {
+        mGroup.add_buffer(new MediaBuffer(mSize));
+    }
+
+    virtual ::status_t getMaxSampleSize(size_t *max_size) {
+        *max_size = mSize;
+        return ::OK;
+    }
+
+    virtual ::status_t read(MediaBuffer **buffer) {
+        ::status_t err = mGroup.acquire_buffer(buffer);
+        if (err != ::OK) {
+            return err;
+        }
+
+        char x = (char)((double)rand() / RAND_MAX * 255);
+        memset((*buffer)->data(), x, mSize);
+        (*buffer)->set_range(0, mSize);
+
+        return ::OK;
+    }
+
+private:
+    MediaBufferGroup mGroup;
+    size_t mSize;
+
+    DummySource(const DummySource &);
+    DummySource &operator=(const DummySource &);
+};
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+#if 1
+    if (argc != 2) {
+        fprintf(stderr, "usage: %s filename\n", argv[0]);
+        return 1;
+    }
+
+    MPEG4Extractor extractor(new MmapSource(argv[1]));
+    int num_tracks;
+    assert(extractor.countTracks(&num_tracks) == ::OK);
+
+    MediaSource *source = NULL;
+    sp<MetaData> meta;
+    for (int i = 0; i < num_tracks; ++i) {
+        meta = extractor.getTrackMetaData(i);
+        assert(meta.get() != NULL);
+
+        const char *mime;
+        if (!meta->findCString(kKeyMIMEType, &mime)) {
+            continue;
+        }
+
+        if (strncasecmp(mime, "video/", 6)) {
+            continue;
+        }
+
+        if (extractor.getTrack(i, &source) != ::OK) {
+            source = NULL;
+            continue;
+        }
+        break;
+    }
+
+    if (source == NULL) {
+        fprintf(stderr, "Unable to find a suitable video track.\n");
+        return 1;
+    }
+
+    OMXClient client;
+    assert(client.connect() == android::OK);
+
+    OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
+    decoder->setSource(source);
+
+    int width, height;
+    bool success = meta->findInt32(kKeyWidth, &width);
+    success = success && meta->findInt32(kKeyHeight, &height);
+    assert(success);
+
+    sp<MetaData> enc_meta = new MetaData;
+    // enc_meta->setCString(kKeyMIMEType, "video/3gpp");
+    enc_meta->setCString(kKeyMIMEType, "video/mp4v-es");
+    enc_meta->setInt32(kKeyWidth, width);
+    enc_meta->setInt32(kKeyHeight, height);
+
+    OMXDecoder *encoder = OMXDecoder::CreateEncoder(&client, enc_meta);
+
+    encoder->setSource(decoder);
+    // encoder->setSource(meta, new DummySource(width, height));
+
+#if 1
+    MPEG4Writer writer("/sdcard/output.mp4");
+    writer.addSource(enc_meta, encoder);
+    writer.start();
+    sleep(120);
+    writer.stop();
+#else
+    encoder->start();
+
+    MediaBuffer *buffer;
+    while (encoder->read(&buffer) == ::OK) {
+        printf("got an output frame of size %d\n", buffer->range_length());
+
+        buffer->release();
+        buffer = NULL;
+    }
+
+    encoder->stop();
+#endif
+
+    delete encoder;
+    encoder = NULL;
+
+    delete decoder;
+    decoder = NULL;
+
+    client.disconnect();
+
+    delete source;
+    source = NULL;
+#endif
+
+#if 0
+    CameraSource *source = CameraSource::Create();
+    printf("source = %p\n", source);
+
+    for (int i = 0; i < 100; ++i) {
+        MediaBuffer *buffer;
+        status_t err = source->read(&buffer);
+        assert(err == OK);
+
+        printf("got a frame, data=%p, size=%d\n",
+               buffer->data(), buffer->range_length());
+
+        buffer->release();
+        buffer = NULL;
+    }
+
+    delete source;
+    source = NULL;
+#endif
+
+    return 0;
+}
+
diff --git a/cmds/stagefright/stagefright.cpp b/cmds/stagefright/stagefright.cpp
new file mode 100644
index 0000000..961942a
--- /dev/null
+++ b/cmds/stagefright/stagefright.cpp
@@ -0,0 +1,200 @@
+/*
+ * Copyright (C) 2009 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 <sys/time.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <binder/IServiceManager.h>
+#include <binder/ProcessState.h>
+#include <media/IMediaPlayerService.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/CachingDataSource.h>
+#include <media/stagefright/ESDS.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaPlayerImpl.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXDecoder.h>
+
+#include "WaveWriter.h"
+
+using namespace android;
+
+////////////////////////////////////////////////////////////////////////////////
+
+static bool convertToWav(
+        OMXClient *client, const sp<MetaData> &meta, MediaSource *source) {
+    printf("convertToWav\n");
+
+    OMXDecoder *decoder = OMXDecoder::Create(client, meta);
+
+    int32_t sampleRate;
+    bool success = meta->findInt32(kKeySampleRate, &sampleRate);
+    assert(success);
+
+    int32_t numChannels;
+    success = meta->findInt32(kKeyChannelCount, &numChannels);
+    assert(success);
+
+    const char *mime;
+    success = meta->findCString(kKeyMIMEType, &mime);
+    assert(success);
+
+    if (!strcasecmp("audio/3gpp", mime)) {
+        numChannels = 1;  // XXX
+    }
+
+    WaveWriter writer("/sdcard/Music/shoutcast.wav", numChannels, sampleRate);
+
+    decoder->setSource(source);
+    for (int i = 0; i < 100; ++i) {
+        MediaBuffer *buffer;
+
+        ::status_t err = decoder->read(&buffer);
+        if (err != ::OK) {
+            break;
+        }
+
+        writer.Append((const char *)buffer->data() + buffer->range_offset(),
+                      buffer->range_length());
+
+        buffer->release();
+        buffer = NULL;
+    }
+
+    delete decoder;
+    decoder = NULL;
+
+    return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+static int64_t getNowUs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    return (int64_t)tv.tv_usec + tv.tv_sec * 1000000;
+}
+
+int main(int argc, char **argv) {
+    android::ProcessState::self()->startThreadPool();
+
+    if (argc > 1 && !strcmp(argv[1], "--list")) {
+        sp<IServiceManager> sm = defaultServiceManager();
+        sp<IBinder> binder = sm->getService(String16("media.player"));
+        sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+        assert(service.get() != NULL);
+
+        sp<IOMX> omx = service->createOMX();
+        assert(omx.get() != NULL);
+
+        List<String8> list;
+        omx->list_nodes(&list);
+
+        for (List<String8>::iterator it = list.begin();
+             it != list.end(); ++it) {
+            printf("%s\n", (*it).string());
+        }
+
+        return 0;
+    }
+
+#if 0
+    MediaPlayerImpl player(argv[1]);
+    player.play();
+
+    sleep(10000);
+#else
+    DataSource::RegisterDefaultSniffers();
+
+    OMXClient client;
+    status_t err = client.connect();
+
+    MmapSource *dataSource = new MmapSource(argv[1]);
+    MediaExtractor *extractor = MediaExtractor::Create(dataSource);
+    dataSource = NULL;
+
+    int numTracks;
+    err = extractor->countTracks(&numTracks);
+
+    sp<MetaData> meta;
+    int i;
+    for (i = 0; i < numTracks; ++i) {
+        meta = extractor->getTrackMetaData(i);
+
+        const char *mime;
+        meta->findCString(kKeyMIMEType, &mime);
+
+        if (!strncasecmp(mime, "video/", 6)) {
+            break;
+        }
+    }
+
+    OMXDecoder *decoder = OMXDecoder::Create(&client, meta);
+
+    if (decoder != NULL) {
+        MediaSource *source;
+        err = extractor->getTrack(i, &source);
+
+        decoder->setSource(source);
+
+        decoder->start();
+
+        int64_t startTime = getNowUs();
+
+        int n = 0;
+        MediaBuffer *buffer;
+        while ((err = decoder->read(&buffer)) == OK) {
+            if ((++n % 16) == 0) {
+                printf(".");
+                fflush(stdout);
+            }
+
+            buffer->release();
+            buffer = NULL;
+        }
+        decoder->stop();
+        printf("\n");
+
+        int64_t delay = getNowUs() - startTime;
+        printf("avg. %.2f fps\n", n * 1E6 / delay);
+
+        delete decoder;
+        decoder = NULL;
+
+        delete source;
+        source = NULL;
+    }
+
+    delete extractor;
+    extractor = NULL;
+
+    client.disconnect();
+#endif
+
+    return 0;
+}
diff --git a/include/media/IMediaPlayerService.h b/include/media/IMediaPlayerService.h
index f6faf14..39b5e57 100644
--- a/include/media/IMediaPlayerService.h
+++ b/include/media/IMediaPlayerService.h
@@ -29,6 +29,7 @@
 namespace android {
 
 class IMediaRecorder;
+class IOMX;
 
 class IMediaPlayerService: public IInterface
 {
@@ -41,6 +42,7 @@
     virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length) = 0;
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat) = 0;
+    virtual sp<IOMX>            createOMX() = 0;
 };
 
 // ----------------------------------------------------------------------------
diff --git a/include/media/IOMX.h b/include/media/IOMX.h
new file mode 100644
index 0000000..5c61c50
--- /dev/null
+++ b/include/media/IOMX.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2009 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 ANDROID_IOMX_H_
+
+#define ANDROID_IOMX_H_
+
+#include <binder/IInterface.h>
+#include <utils/List.h>
+#include <utils/String8.h>
+
+#include <OMX_Core.h>
+
+#define IOMX_USES_SOCKETS       0
+
+namespace android {
+
+class IMemory;
+class IOMXObserver;
+
+class IOMX : public IInterface {
+public:
+    DECLARE_META_INTERFACE(OMX);
+
+    typedef void *buffer_id;
+    typedef void *node_id;
+
+#if IOMX_USES_SOCKETS
+    // If successful, returns a socket descriptor used for further
+    // communication. Caller assumes ownership of "*sd".
+    virtual status_t connect(int *sd) = 0;
+#endif
+
+    virtual status_t list_nodes(List<String8> *list) = 0;
+
+    virtual status_t allocate_node(const char *name, node_id *node) = 0;
+    virtual status_t free_node(node_id node) = 0;
+
+    virtual status_t send_command(
+            node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) = 0;
+
+    virtual status_t get_parameter(
+            node_id node, OMX_INDEXTYPE index,
+            void *params, size_t size) = 0;
+
+    virtual status_t set_parameter(
+            node_id node, OMX_INDEXTYPE index,
+            const void *params, size_t size) = 0;
+
+    virtual status_t use_buffer(
+            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+            buffer_id *buffer) = 0;
+
+    virtual status_t allocate_buffer(
+            node_id node, OMX_U32 port_index, size_t size,
+            buffer_id *buffer) = 0;
+
+    virtual status_t allocate_buffer_with_backup(
+            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+            buffer_id *buffer) = 0;
+
+    virtual status_t free_buffer(
+            node_id node, OMX_U32 port_index, buffer_id buffer) = 0;
+
+#if !IOMX_USES_SOCKETS
+    virtual status_t observe_node(
+            node_id node, const sp<IOMXObserver> &observer) = 0;
+
+    virtual void fill_buffer(node_id node, buffer_id buffer) = 0;
+
+    virtual void empty_buffer(
+            node_id node,
+            buffer_id buffer,
+            OMX_U32 range_offset, OMX_U32 range_length,
+            OMX_U32 flags, OMX_TICKS timestamp) = 0;
+#endif
+};
+
+struct omx_message {
+    enum {
+        EVENT,
+        EMPTY_BUFFER_DONE,
+        FILL_BUFFER_DONE,
+
+#if IOMX_USES_SOCKETS
+        EMPTY_BUFFER,
+        FILL_BUFFER,
+        SEND_COMMAND,
+        DISCONNECT,
+        DISCONNECTED,
+#endif
+
+        // reserved for OMXDecoder use.
+        START,
+        INITIAL_FILL_BUFFER,
+
+        // reserved for OMXObserver use.
+        QUIT_OBSERVER,
+    } type;
+
+    union {
+        // if type == EVENT
+        struct {
+            IOMX::node_id node;
+            OMX_EVENTTYPE event;
+            OMX_U32 data1;
+            OMX_U32 data2;
+        } event_data;
+
+        // if type == EMPTY_BUFFER_DONE || type == FILL_BUFFER
+        //    || type == INITIAL_FILL_BUFFER
+        struct {
+            IOMX::node_id node;
+            IOMX::buffer_id buffer;
+        } buffer_data;
+
+        // if type == EMPTY_BUFFER || type == FILL_BUFFER_DONE
+        struct {
+            IOMX::node_id node;
+            IOMX::buffer_id buffer;
+            OMX_U32 range_offset;
+            OMX_U32 range_length;
+            OMX_U32 flags;
+            OMX_TICKS timestamp;
+            OMX_PTR platform_private;  // ignored if type == EMPTY_BUFFER
+        } extended_buffer_data;
+
+        // if type == SEND_COMMAND
+        struct {
+            IOMX::node_id node;
+            OMX_COMMANDTYPE cmd;
+            OMX_S32 param;
+        } send_command_data;
+
+    } u;
+};
+
+class IOMXObserver : public IInterface {
+public:
+    DECLARE_META_INTERFACE(OMXObserver);
+
+    virtual void on_message(const omx_message &msg) = 0;
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BnOMX : public BnInterface<IOMX> {
+public:
+    virtual status_t onTransact(
+            uint32_t code, const Parcel &data, Parcel *reply,
+            uint32_t flags = 0);
+};
+
+class BnOMXObserver : public BnInterface<IOMXObserver> {
+public:
+    virtual status_t onTransact(
+            uint32_t code, const Parcel &data, Parcel *reply,
+            uint32_t flags = 0);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_IOMX_H_
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 21600b2..d1933f6 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -32,7 +32,8 @@
 enum player_type {
     PV_PLAYER = 1,
     SONIVOX_PLAYER = 2,
-    VORBIS_PLAYER = 3
+    VORBIS_PLAYER = 3,
+    STAGEFRIGHT_PLAYER = 4
 };
 
 
@@ -51,6 +52,9 @@
     // AudioSink: abstraction layer for audio output
     class AudioSink : public RefBase {
     public:
+        typedef void (*AudioCallback)(
+                AudioSink *audioSink, void *buffer, size_t size, void *cookie);
+
         virtual             ~AudioSink() {}
         virtual bool        ready() const = 0; // audio output is open and ready
         virtual bool        realtime() const = 0; // audio output is real-time output
@@ -60,7 +64,17 @@
         virtual ssize_t     frameSize() const = 0;
         virtual uint32_t    latency() const = 0;
         virtual float       msecsPerFrame() const = 0;
-        virtual status_t    open(uint32_t sampleRate, int channelCount, int format=AudioSystem::PCM_16_BIT, int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT) = 0;
+
+        // If no callback is specified, use the "write" API below to submit
+        // audio data. Otherwise return a full buffer of audio data on each
+        // callback.
+        virtual status_t    open(
+                uint32_t sampleRate, int channelCount,
+                int format=AudioSystem::PCM_16_BIT,
+                int bufferCount=DEFAULT_AUDIOSINK_BUFFERCOUNT,
+                AudioCallback cb = NULL,
+                void *cookie = NULL) = 0;
+
         virtual void        start() = 0;
         virtual ssize_t     write(const void* buffer, size_t size) = 0;
         virtual void        stop() = 0;
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
new file mode 100644
index 0000000..0f2e528
--- /dev/null
+++ b/include/media/stagefright/AudioPlayer.h
@@ -0,0 +1,98 @@
+/*
+ * Copyright (C) 2009 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 AUDIO_PLAYER_H_
+
+#define AUDIO_PLAYER_H_
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/TimeSource.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaSource;
+class AudioTrack;
+
+class AudioPlayer : public TimeSource {
+public:
+    AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink);
+    ~AudioPlayer();
+
+    // Caller retains ownership of "source".
+    void setSource(MediaSource *source);
+
+    // Return time in us.
+    virtual int64_t getRealTimeUs();
+
+    void start();
+
+    void pause();
+    void resume();
+
+    void stop();
+
+    // Returns the timestamp of the last buffer played (in us).
+    int64_t getMediaTimeUs();
+
+    // Returns true iff a mapping is established, i.e. the AudioPlayer
+    // has played at least one frame of audio.
+    bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
+
+    status_t seekTo(int64_t time_us);
+
+private:
+    MediaSource *mSource;
+    AudioTrack *mAudioTrack;
+
+    MediaBuffer *mInputBuffer;
+
+    int mSampleRate;
+    int64_t mLatencyUs;
+    size_t mFrameSize;
+
+    Mutex mLock;
+    int64_t mNumFramesPlayed;
+
+    int64_t mPositionTimeMediaUs;
+    int64_t mPositionTimeRealUs;
+
+    bool mSeeking;
+    int64_t mSeekTimeUs;
+
+    bool mStarted;
+
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+    static void AudioCallback(int event, void *user, void *info);
+    void AudioCallback(int event, void *info);
+
+    static void AudioSinkCallback(
+            MediaPlayerBase::AudioSink *audioSink,
+            void *data, size_t size, void *me);
+
+    void fillBuffer(void *data, size_t size);
+
+    int64_t getRealTimeUsLocked() const;
+
+    AudioPlayer(const AudioPlayer &);
+    AudioPlayer &operator=(const AudioPlayer &);
+};
+
+}  // namespace android
+
+#endif  // AUDIO_PLAYER_H_
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
new file mode 100644
index 0000000..e129958
--- /dev/null
+++ b/include/media/stagefright/AudioSource.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 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 AUDIO_SOURCE_H_
+
+#define AUDIO_SOURCE_H_
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class AudioRecord;
+
+class AudioSource {
+public:
+    AudioSource(int inputSource);
+    virtual ~AudioSource();
+
+    status_t initCheck() const;
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+    AudioRecord *mRecord;
+    status_t mInitCheck;
+
+    AudioSource(const AudioSource &);
+    AudioSource &operator=(const AudioSource &);
+};
+
+}  // namespace android
+
+#endif  // AUDIO_SOURCE_H_
diff --git a/include/media/stagefright/CachingDataSource.h b/include/media/stagefright/CachingDataSource.h
new file mode 100644
index 0000000..e275cb4
--- /dev/null
+++ b/include/media/stagefright/CachingDataSource.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 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 CACHING_DATASOURCE_H_
+
+#define CACHING_DATASOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class CachingDataSource : public DataSource {
+public:
+    // Assumes ownership of "source".
+    CachingDataSource(DataSource *source, size_t pageSize, int numPages);
+    virtual ~CachingDataSource();
+
+    status_t InitCheck() const;
+
+    virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+    struct Page {
+        Page *mPrev, *mNext;
+        off_t mOffset;
+        size_t mLength;
+        void *mData;
+    };
+
+    DataSource *mSource;
+    void *mData;
+    size_t mPageSize;
+    Page *mFirst, *mLast;
+
+    Page *allocate_page();
+
+    Mutex mLock;
+
+    CachingDataSource(const CachingDataSource &);
+    CachingDataSource &operator=(const CachingDataSource &);
+};
+
+}  // namespace android
+
+#endif  // CACHING_DATASOURCE_H_
diff --git a/include/media/stagefright/CameraSource.h b/include/media/stagefright/CameraSource.h
new file mode 100644
index 0000000..7042e1a
--- /dev/null
+++ b/include/media/stagefright/CameraSource.h
@@ -0,0 +1,72 @@
+/*
+ * Copyright (C) 2009 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 CAMERA_SOURCE_H_
+
+#define CAMERA_SOURCE_H_
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class ICamera;
+class ICameraClient;
+class IMemory;
+
+class CameraSource : public MediaSource,
+                     public MediaBufferObserver {
+public:
+    static CameraSource *Create();
+
+    virtual ~CameraSource();
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+    virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2);
+    virtual void dataCallback(int32_t msgType, const sp<IMemory>& data);
+
+    virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+    CameraSource(const sp<ICamera> &camera, const sp<ICameraClient> &client);
+
+    sp<ICamera> mCamera;
+    sp<ICameraClient> mCameraClient;
+
+    Mutex mLock;
+    Condition mFrameAvailableCondition;
+    List<sp<IMemory> > mFrames;
+
+    int mNumFrames;
+    bool mStarted;
+
+    CameraSource(const CameraSource &);
+    CameraSource &operator=(const CameraSource &);
+};
+
+}  // namespace android
+
+#endif  // CAMERA_SOURCE_H_
diff --git a/include/media/stagefright/DataSource.h b/include/media/stagefright/DataSource.h
new file mode 100644
index 0000000..31eea27
--- /dev/null
+++ b/include/media/stagefright/DataSource.h
@@ -0,0 +1,61 @@
+/*
+ * Copyright (C) 2009 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 DATA_SOURCE_H_
+
+#define DATA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <utils/Errors.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class String8;
+
+class DataSource {
+public:
+    DataSource() {}
+    virtual ~DataSource() {}
+
+    virtual ssize_t read_at(off_t offset, void *data, size_t size) = 0;
+
+    // May return ERROR_UNSUPPORTED.
+    virtual status_t getSize(off_t *size);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    bool sniff(String8 *mimeType, float *confidence);
+
+    typedef bool (*SnifferFunc)(
+            DataSource *source, String8 *mimeType, float *confidence);
+
+    static void RegisterSniffer(SnifferFunc func);
+    static void RegisterDefaultSniffers();
+
+private:
+    static Mutex gSnifferMutex;
+    static List<SnifferFunc> gSniffers;
+
+    DataSource(const DataSource &);
+    DataSource &operator=(const DataSource &);
+};
+
+}  // namespace android
+
+#endif  // DATA_SOURCE_H_
diff --git a/include/media/stagefright/ESDS.h b/include/media/stagefright/ESDS.h
new file mode 100644
index 0000000..01bcd18
--- /dev/null
+++ b/include/media/stagefright/ESDS.h
@@ -0,0 +1,64 @@
+/*
+ * Copyright (C) 2009 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 ESDS_H_
+
+#define ESDS_H_
+
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class ESDS {
+public:
+    ESDS(const void *data, size_t size);
+    ~ESDS();
+
+    status_t InitCheck() const;
+
+    status_t getCodecSpecificInfo(const void **data, size_t *size) const;
+
+private:
+    enum {
+        kTag_ESDescriptor            = 0x03,
+        kTag_DecoderConfigDescriptor = 0x04,
+        kTag_DecoderSpecificInfo     = 0x05
+    };
+
+    uint8_t *mData;
+    size_t mSize;
+
+    status_t mInitCheck;
+
+    size_t mDecoderSpecificOffset;
+    size_t mDecoderSpecificLength;
+
+    status_t skipDescriptorHeader(
+            size_t offset, size_t size,
+            uint8_t *tag, size_t *data_offset, size_t *data_size) const;
+
+    status_t parse();
+    status_t parseESDescriptor(size_t offset, size_t size);
+    status_t parseDecoderConfigDescriptor(size_t offset, size_t size);
+
+    ESDS(const ESDS &);
+    ESDS &operator=(const ESDS &);
+};
+
+}  // namespace android
+#endif  // ESDS_H_
diff --git a/include/media/stagefright/FileSource.h b/include/media/stagefright/FileSource.h
new file mode 100644
index 0000000..ccbe0ef
--- /dev/null
+++ b/include/media/stagefright/FileSource.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 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 FILE_SOURCE_H_
+
+#define FILE_SOURCE_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class FileSource : public DataSource {
+public:
+    FileSource(const char *filename);
+    virtual ~FileSource();
+
+    status_t InitCheck() const;
+
+    virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+    FILE *mFile;
+    Mutex mLock;
+
+    FileSource(const FileSource &);
+    FileSource &operator=(const FileSource &);
+};
+
+}  // namespace android
+
+#endif  // FILE_SOURCE_H_
+
diff --git a/include/media/stagefright/HTTPDataSource.h b/include/media/stagefright/HTTPDataSource.h
new file mode 100644
index 0000000..0587c7c
--- /dev/null
+++ b/include/media/stagefright/HTTPDataSource.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 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 HTTP_DATASOURCE_H_
+
+#define HTTP_DATASOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/HTTPStream.h>
+
+namespace android {
+
+class HTTPDataSource : public DataSource {
+public:
+    HTTPDataSource(const char *host, int port, const char *path);
+    HTTPDataSource(const char *uri);
+
+    virtual ~HTTPDataSource();
+
+    // XXXandih
+    status_t InitCheck() const { return OK; }
+
+    virtual ssize_t read_at(off_t offset, void *data, size_t size);
+
+private:
+    enum {
+        kBufferSize = 64 * 1024
+    };
+
+    HTTPStream mHttp;
+    char *mHost;
+    int mPort;
+    char *mPath;
+
+    void *mBuffer;
+    size_t mBufferLength;
+    off_t mBufferOffset;
+
+    HTTPDataSource(const HTTPDataSource &);
+    HTTPDataSource &operator=(const HTTPDataSource &);
+};
+
+}  // namespace android
+
+#endif  // HTTP_DATASOURCE_H_
+
diff --git a/include/media/stagefright/HTTPStream.h b/include/media/stagefright/HTTPStream.h
new file mode 100644
index 0000000..3d0d67a
--- /dev/null
+++ b/include/media/stagefright/HTTPStream.h
@@ -0,0 +1,74 @@
+/*
+ * Copyright (C) 2009 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 HTTP_STREAM_H_
+
+#define HTTP_STREAM_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/string.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+class HTTPStream {
+public:
+    HTTPStream();
+    ~HTTPStream();
+
+    status_t connect(const char *server, int port = 80);
+    status_t disconnect();
+
+    status_t send(const char *data, size_t size);
+
+    // Assumes data is a '\0' terminated string.
+    status_t send(const char *data);
+
+    // Receive up to "size" bytes of data.
+    ssize_t receive(void *data, size_t size);
+
+    status_t receive_header(int *http_status);
+
+    // The header key used to retrieve the status line.
+    static const char *kStatusKey;
+
+    bool find_header_value(
+            const string &key, string *value) const;
+
+private:
+    enum State {
+        READY,
+        CONNECTED
+    };
+
+    State mState;
+    int mSocket;
+
+    KeyedVector<string, string> mHeaders;
+
+    // Receive a line of data terminated by CRLF, line will be '\0' terminated
+    // _excluding_ the termianting CRLF.
+    status_t receive_line(char *line, size_t size);
+
+    HTTPStream(const HTTPStream &);
+    HTTPStream &operator=(const HTTPStream &);
+};
+
+}  // namespace android
+
+#endif  // HTTP_STREAM_H_
diff --git a/include/media/stagefright/MP3Extractor.h b/include/media/stagefright/MP3Extractor.h
new file mode 100644
index 0000000..09cfb70
--- /dev/null
+++ b/include/media/stagefright/MP3Extractor.h
@@ -0,0 +1,53 @@
+/*
+ * Copyright (C) 2009 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 MP3_EXTRACTOR_H_
+
+#define MP3_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class String8;
+
+class MP3Extractor : public MediaExtractor {
+public:
+    // Extractor assumes ownership of "source".
+    MP3Extractor(DataSource *source);
+
+    ~MP3Extractor();
+
+    status_t countTracks(int *num_tracks);
+    status_t getTrack(int index, MediaSource **source);
+    sp<MetaData> getTrackMetaData(int index);
+
+private:
+    DataSource *mDataSource;
+    off_t mFirstFramePos;
+    sp<MetaData> mMeta;
+    uint32_t mFixedHeader;
+
+    MP3Extractor(const MP3Extractor &);
+    MP3Extractor &operator=(const MP3Extractor &);
+};
+
+bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence);
+
+}  // namespace android
+
+#endif  // MP3_EXTRACTOR_H_
diff --git a/include/media/stagefright/MPEG4Extractor.h b/include/media/stagefright/MPEG4Extractor.h
new file mode 100644
index 0000000..51a7e82
--- /dev/null
+++ b/include/media/stagefright/MPEG4Extractor.h
@@ -0,0 +1,65 @@
+/*
+ * Copyright (C) 2009 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 MPEG4_EXTRACTOR_H_
+
+#define MPEG4_EXTRACTOR_H_
+
+#include <media/stagefright/MediaExtractor.h>
+
+namespace android {
+
+class DataSource;
+class SampleTable;
+class String8;
+
+class MPEG4Extractor : public MediaExtractor {
+public:
+    // Extractor assumes ownership of "source".
+    MPEG4Extractor(DataSource *source);
+    ~MPEG4Extractor();
+
+    status_t countTracks(int *num_tracks);
+    status_t getTrack(int index, MediaSource **source);
+    sp<MetaData> getTrackMetaData(int index);
+
+private:
+    struct Track {
+        Track *next;
+        sp<MetaData> meta;
+        uint32_t timescale;
+        SampleTable *sampleTable;
+    };
+
+    DataSource *mDataSource;
+    bool mHaveMetadata;
+
+    Track *mFirstTrack, *mLastTrack;
+
+    uint32_t mHandlerType;
+
+    status_t readMetaData();
+    status_t parseChunk(off_t *offset, int depth);
+
+    MPEG4Extractor(const MPEG4Extractor &);
+    MPEG4Extractor &operator=(const MPEG4Extractor &);
+};
+
+bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence);
+
+}  // namespace android
+
+#endif  // MPEG4_EXTRACTOR_H_
diff --git a/include/media/stagefright/MPEG4Writer.h b/include/media/stagefright/MPEG4Writer.h
new file mode 100644
index 0000000..40d6127
--- /dev/null
+++ b/include/media/stagefright/MPEG4Writer.h
@@ -0,0 +1,73 @@
+/*
+ * Copyright (C) 2009 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 MPEG4_WRITER_H_
+
+#define MPEG4_WRITER_H_
+
+#include <stdio.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MediaSource;
+class MetaData;
+
+class MPEG4Writer {
+public:
+    MPEG4Writer(const char *filename);
+    ~MPEG4Writer();
+
+    // Caller retains ownership of both meta and source.
+    void addSource(const sp<MetaData> &meta, MediaSource *source);
+    void start();
+    void stop();
+
+    void beginBox(const char *fourcc);
+    void writeInt8(int8_t x);
+    void writeInt16(int16_t x);
+    void writeInt32(int32_t x);
+    void writeInt64(int64_t x);
+    void writeCString(const char *s);
+    void writeFourcc(const char *fourcc);
+    void write(const void *data, size_t size);
+    void endBox();
+
+private:
+    class Track;
+
+    FILE *mFile;
+    off_t mOffset;
+    off_t mMdatOffset;
+    Mutex mLock;
+
+    List<Track *> mTracks;
+
+    List<off_t> mBoxes;
+
+    off_t addSample(MediaBuffer *buffer);
+
+    MPEG4Writer(const MPEG4Writer &);
+    MPEG4Writer &operator=(const MPEG4Writer &);
+};
+
+}  // namespace android
+
+#endif  // MPEG4_WRITER_H_
diff --git a/include/media/stagefright/MediaBuffer.h b/include/media/stagefright/MediaBuffer.h
new file mode 100644
index 0000000..c72ed66
--- /dev/null
+++ b/include/media/stagefright/MediaBuffer.h
@@ -0,0 +1,113 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_BUFFER_H_
+
+#define MEDIA_BUFFER_H_
+
+#include <pthread.h>
+
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MediaBufferObserver;
+class MetaData;
+
+class MediaBufferObserver {
+public:
+    MediaBufferObserver() {}
+    virtual ~MediaBufferObserver() {}
+
+    virtual void signalBufferReturned(MediaBuffer *buffer) = 0;
+
+private:
+    MediaBufferObserver(const MediaBufferObserver &);
+    MediaBufferObserver &operator=(const MediaBufferObserver &);
+};
+
+class MediaBuffer {
+public:
+    // The underlying data remains the responsibility of the caller!
+    MediaBuffer(void *data, size_t size);
+
+    MediaBuffer(size_t size);
+
+    // Decrements the reference count and returns the buffer to its
+    // associated MediaBufferGroup if the reference count drops to 0.
+    void release();
+
+    // Increments the reference count.
+    void add_ref();
+
+    void *data() const;
+    size_t size() const;
+
+    size_t range_offset() const;
+    size_t range_length() const;
+
+    void set_range(size_t offset, size_t length);
+
+    sp<MetaData> meta_data();
+
+    // Clears meta data and resets the range to the full extent.
+    void reset();
+
+    void setObserver(MediaBufferObserver *group);
+
+    // Returns a clone of this MediaBuffer increasing its reference count.
+    // The clone references the same data but has its own range and
+    // MetaData.
+    MediaBuffer *clone();
+
+protected:
+    virtual ~MediaBuffer();
+
+private:
+    friend class MediaBufferGroup;
+    friend class OMXDecoder;
+
+    // For use by OMXDecoder, reference count must be 1, drop reference
+    // count to 0 without signalling the observer.
+    void claim();
+
+    MediaBufferObserver *mObserver;
+    MediaBuffer *mNextBuffer;
+    int mRefCount;
+
+    void *mData;
+    size_t mSize, mRangeOffset, mRangeLength;
+
+    bool mOwnsData;
+
+    sp<MetaData> mMetaData;
+
+    MediaBuffer *mOriginal;
+
+    void setNextBuffer(MediaBuffer *buffer);
+    MediaBuffer *nextBuffer();
+
+    int refcount() const;
+
+    MediaBuffer(const MediaBuffer &);
+    MediaBuffer &operator=(const MediaBuffer &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_BUFFER_H_
diff --git a/include/media/stagefright/MediaBufferGroup.h b/include/media/stagefright/MediaBufferGroup.h
new file mode 100644
index 0000000..e95a9c2
--- /dev/null
+++ b/include/media/stagefright/MediaBufferGroup.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_BUFFER_GROUP_H_
+
+#define MEDIA_BUFFER_GROUP_H_
+
+#include <utils/Errors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+class MediaBufferGroup : public MediaBufferObserver {
+public:
+    MediaBufferGroup();
+    ~MediaBufferGroup();
+
+    void add_buffer(MediaBuffer *buffer);
+
+    // Blocks until a buffer is available and returns it to the caller,
+    // the returned buffer will have a reference count of 1.
+    status_t acquire_buffer(MediaBuffer **buffer);
+
+protected:
+    virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+    friend class MediaBuffer;
+
+    Mutex mLock;
+    Condition mCondition;
+
+    MediaBuffer *mFirstBuffer, *mLastBuffer;
+
+    MediaBufferGroup(const MediaBufferGroup &);
+    MediaBufferGroup &operator=(const MediaBufferGroup &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_BUFFER_GROUP_H_
diff --git a/include/media/stagefright/MediaErrors.h b/include/media/stagefright/MediaErrors.h
new file mode 100644
index 0000000..2bb0ed6
--- /dev/null
+++ b/include/media/stagefright/MediaErrors.h
@@ -0,0 +1,43 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_ERRORS_H_
+
+#define MEDIA_ERRORS_H_
+
+#include <utils/Errors.h>
+
+namespace android {
+
+enum {
+    MEDIA_ERROR_BASE        = -1000,
+
+    ERROR_ALREADY_CONNECTED = MEDIA_ERROR_BASE,
+    ERROR_NOT_CONNECTED     = MEDIA_ERROR_BASE - 1,
+    ERROR_UNKNOWN_HOST      = MEDIA_ERROR_BASE - 2,
+    ERROR_CANNOT_CONNECT    = MEDIA_ERROR_BASE - 3,
+    ERROR_IO                = MEDIA_ERROR_BASE - 4,
+    ERROR_CONNECTION_LOST   = MEDIA_ERROR_BASE - 5,
+    ERROR_MALFORMED         = MEDIA_ERROR_BASE - 7,
+    ERROR_OUT_OF_RANGE      = MEDIA_ERROR_BASE - 8,
+    ERROR_BUFFER_TOO_SMALL  = MEDIA_ERROR_BASE - 9,
+    ERROR_UNSUPPORTED       = MEDIA_ERROR_BASE - 10,
+    ERROR_END_OF_STREAM     = MEDIA_ERROR_BASE - 11,
+};
+
+}  // namespace android
+
+#endif  // MEDIA_ERRORS_H_
diff --git a/include/media/stagefright/MediaExtractor.h b/include/media/stagefright/MediaExtractor.h
new file mode 100644
index 0000000..38f8e5b
--- /dev/null
+++ b/include/media/stagefright/MediaExtractor.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_EXTRACTOR_H_
+
+#define MEDIA_EXTRACTOR_H_
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class DataSource;
+class MediaSource;
+class MetaData;
+
+class MediaExtractor {
+public:
+    static MediaExtractor *Create(DataSource *source, const char *mime = NULL);
+
+    virtual ~MediaExtractor() {}
+
+    virtual status_t countTracks(int *num_tracks) = 0;
+    virtual status_t getTrack(int index, MediaSource **source) = 0;
+    virtual sp<MetaData> getTrackMetaData(int index) = 0;
+
+protected:
+    MediaExtractor() {}
+
+private:
+    MediaExtractor(const MediaExtractor &);
+    MediaExtractor &operator=(const MediaExtractor &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_EXTRACTOR_H_
diff --git a/include/media/stagefright/MediaPlayerImpl.h b/include/media/stagefright/MediaPlayerImpl.h
new file mode 100644
index 0000000..c48400c
--- /dev/null
+++ b/include/media/stagefright/MediaPlayerImpl.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_PLAYER_IMPL_H_
+
+#define MEDIA_PLAYER_IMPL_H_
+
+#include <pthread.h>
+
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class AudioPlayer;
+class ISurface;
+class MediaExtractor;
+class MediaBuffer;
+class MediaSource;
+class MemoryHeapPmem;
+class MetaData;
+class OMXDecoder;
+class Surface;
+class TimeSource;
+class VideoRenderer;
+
+class MediaPlayerImpl {
+public:
+    MediaPlayerImpl(const char *uri);
+
+    status_t initCheck() const;
+
+    // Assumes ownership of "fd".
+    MediaPlayerImpl(int fd, int64_t offset, int64_t length);
+
+    ~MediaPlayerImpl();
+
+    void play();
+    void pause();
+    bool isPlaying() const;
+
+    void setSurface(const sp<Surface> &surface);
+    void setISurface(const sp<ISurface> &isurface);
+
+    void setAudioSink(const sp<MediaPlayerBase::AudioSink> &audioSink);
+
+    int32_t getWidth() const { return mVideoWidth; }
+    int32_t getHeight() const { return mVideoHeight; }
+
+    int64_t getDuration();
+    int64_t getPosition();
+    status_t seekTo(int64_t time);
+
+private:
+    status_t mInitCheck;
+
+    OMXClient mClient;
+
+    MediaExtractor *mExtractor;
+
+    TimeSource *mTimeSource;
+
+    MediaSource *mAudioSource;
+    OMXDecoder *mAudioDecoder;
+    AudioPlayer *mAudioPlayer;
+
+    MediaSource *mVideoSource;
+    MediaSource *mVideoDecoder;
+    int32_t mVideoWidth, mVideoHeight;
+    int64_t mVideoPosition;
+
+    int64_t mDuration;
+
+    bool mPlaying;
+    bool mPaused;
+
+    int64_t mTimeSourceDeltaUs;
+
+    sp<Surface> mSurface;
+    sp<ISurface> mISurface;
+    VideoRenderer *mRenderer;
+
+    sp<MediaPlayerBase::AudioSink> mAudioSink;
+
+    Mutex mLock;
+    pthread_t mVideoThread;
+
+    bool mSeeking;
+    int64_t mSeekTimeUs;
+
+    size_t mFrameSize;
+    bool mUseSoftwareColorConversion;
+
+    void init();
+
+    static void *VideoWrapper(void *me);
+    void videoEntry();
+
+    void setAudioSource(MediaSource *source);
+    void setVideoSource(MediaSource *source);
+
+    MediaSource *makeShoutcastSource(const char *path);
+
+    void displayOrDiscardFrame(MediaBuffer *buffer, int64_t pts_us);
+    void populateISurface();
+    void depopulateISurface();
+    void sendFrameToISurface(MediaBuffer *buffer);
+
+    void stop();
+
+    MediaPlayerImpl(const MediaPlayerImpl &);
+    MediaPlayerImpl &operator=(const MediaPlayerImpl &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_PLAYER_IMPL_H_
diff --git a/include/media/stagefright/MediaSource.h b/include/media/stagefright/MediaSource.h
new file mode 100644
index 0000000..eb07f68
--- /dev/null
+++ b/include/media/stagefright/MediaSource.h
@@ -0,0 +1,91 @@
+/*
+ * Copyright (C) 2009 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 MEDIA_SOURCE_H_
+
+#define MEDIA_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <utils/RefBase.h>
+
+namespace android {
+
+class MediaBuffer;
+class MetaData;
+
+struct MediaSource {
+    MediaSource();
+    virtual ~MediaSource();
+
+    // To be called before any other methods on this object, except
+    // getFormat().
+    virtual status_t start(MetaData *params = NULL) = 0;
+
+    // Any blocking read call returns immediately with a result of NO_INIT.
+    // It is an error to call any methods other than start after this call
+    // returns. Any buffers the object may be holding onto at the time of
+    // the stop() call are released.
+    // Also, it is imperative that any buffers output by this object and
+    // held onto by callers be released before a call to stop() !!!
+    virtual status_t stop() = 0;
+
+    // Returns the format of the data output by this media source.
+    virtual sp<MetaData> getFormat() = 0;
+
+    struct ReadOptions;
+
+    // Returns a new buffer of data. Call blocks until a
+    // buffer is available, an error is encountered of the end of the stream
+    // is reached.
+    // End of stream is signalled by a result of ERROR_END_OF_STREAM.
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL) = 0;
+
+    // Options that modify read() behaviour. The default is to
+    // a) not request a seek
+    // b) not be late, i.e. lateness_us = 0
+    struct ReadOptions {
+        ReadOptions();
+
+        // Reset everything back to defaults.
+        void reset();
+
+        void setSeekTo(int64_t time_us);
+        void clearSeekTo();
+        bool getSeekTo(int64_t *time_us) const;
+
+        void setLateBy(int64_t lateness_us);
+        int64_t getLateBy() const;
+
+    private:
+        enum Options {
+            kSeekTo_Option      = 1,
+        };
+
+        uint32_t mOptions;
+        int64_t mSeekTimeUs;
+        int64_t mLatenessUs;
+    };
+
+private:
+    MediaSource(const MediaSource &);
+    MediaSource &operator=(const MediaSource &);
+};
+
+}  // namespace android
+
+#endif  // MEDIA_SOURCE_H_
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
new file mode 100644
index 0000000..04805dab
--- /dev/null
+++ b/include/media/stagefright/MetaData.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 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 META_DATA_H_
+
+#define META_DATA_H_
+
+#include <sys/types.h>
+
+#include <stdint.h>
+
+#include <utils/RefBase.h>
+#include <utils/KeyedVector.h>
+
+namespace android {
+
+enum {
+    kKeyMIMEType         = 'mime',
+    kKeyWidth            = 'widt',
+    kKeyHeight           = 'heig',
+    kKeyChannelCount     = '#chn',
+    kKeySampleRate       = 'srte',
+    kKeyBitRate          = 'brte',
+    kKeyESDS             = 'esds',
+    kKeyAVCC             = 'avcc',
+    kKeyTimeUnits        = '#tim',
+    kKeyTimeScale        = 'scal',
+    kKeyNeedsNALFraming  = 'NALf',
+    kKeyIsSyncFrame      = 'sync',
+    kKeyDuration         = 'dura',
+    kKeyColorFormat      = 'colf',
+    kKeyPlatformPrivate  = 'priv',
+    kKeyDecoderComponent = 'decC',
+};
+
+enum {
+    kTypeESDS        = 'esds',
+    kTypeAVCC        = 'avcc',
+};
+
+class MetaData : public RefBase {
+public:
+    MetaData();
+    MetaData(const MetaData &from);
+
+    enum Type {
+        TYPE_NONE     = 'none',
+        TYPE_C_STRING = 'cstr',
+        TYPE_INT32    = 'in32',
+        TYPE_FLOAT    = 'floa',
+        TYPE_POINTER  = 'ptr ',
+    };
+
+    void clear();
+    bool remove(uint32_t key);
+
+    bool setCString(uint32_t key, const char *value);
+    bool setInt32(uint32_t key, int32_t value);
+    bool setFloat(uint32_t key, float value);
+    bool setPointer(uint32_t key, void *value);
+
+    bool findCString(uint32_t key, const char **value);
+    bool findInt32(uint32_t key, int32_t *value);
+    bool findFloat(uint32_t key, float *value);
+    bool findPointer(uint32_t key, void **value);
+
+    bool setData(uint32_t key, uint32_t type, const void *data, size_t size);
+
+    bool findData(uint32_t key, uint32_t *type,
+                  const void **data, size_t *size) const;
+
+protected:
+    virtual ~MetaData();
+
+private:
+    struct typed_data {
+        typed_data();
+        ~typed_data();
+
+        typed_data(const MetaData::typed_data &);
+        typed_data &operator=(const MetaData::typed_data &);
+
+        void clear();
+        void setData(uint32_t type, const void *data, size_t size);
+        void getData(uint32_t *type, const void **data, size_t *size) const;
+
+    private:
+        uint32_t mType;
+        size_t mSize;
+
+        union {
+            void *ext_data;
+            float reservoir;
+        } u;
+
+        bool usesReservoir() const {
+            return mSize <= sizeof(u.reservoir);
+        }
+
+        void allocateStorage(size_t size);
+        void freeStorage();
+
+        void *storage() {
+            return usesReservoir() ? &u.reservoir : u.ext_data;
+        }
+
+        const void *storage() const {
+            return usesReservoir() ? &u.reservoir : u.ext_data;
+        }
+    };
+
+    KeyedVector<uint32_t, typed_data> mItems;
+
+    // MetaData &operator=(const MetaData &);
+};
+
+}  // namespace android
+
+#endif  // META_DATA_H_
diff --git a/include/media/stagefright/MmapSource.h b/include/media/stagefright/MmapSource.h
new file mode 100644
index 0000000..a8bd57f
--- /dev/null
+++ b/include/media/stagefright/MmapSource.h
@@ -0,0 +1,52 @@
+/*
+ * Copyright (C) 2009 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 MMAP_SOURCE_H_
+
+#define MMAP_SOURCE_H_
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+class MmapSource : public DataSource {
+public:
+    MmapSource(const char *filename);
+
+    // Assumes ownership of "fd".
+    MmapSource(int fd, int64_t offset, int64_t length);
+
+    virtual ~MmapSource();
+
+    status_t InitCheck() const;
+
+    virtual ssize_t read_at(off_t offset, void *data, size_t size);
+    virtual status_t getSize(off_t *size);
+
+private:
+    int mFd;
+    void *mBase;
+    size_t mSize;
+
+    MmapSource(const MmapSource &);
+    MmapSource &operator=(const MmapSource &);
+};
+
+}  // namespace android
+
+#endif  // MMAP_SOURCE_H_
+
diff --git a/include/media/stagefright/OMXClient.h b/include/media/stagefright/OMXClient.h
new file mode 100644
index 0000000..454c38b
--- /dev/null
+++ b/include/media/stagefright/OMXClient.h
@@ -0,0 +1,124 @@
+/*
+ * Copyright (C) 2009 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 OMX_CLIENT_H_
+
+#define OMX_CLIENT_H_
+
+#include <media/IOMX.h>
+
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class OMXObserver {
+public:
+    OMXObserver();
+    virtual ~OMXObserver();
+
+    void postMessage(const omx_message &msg);
+
+protected:
+    virtual void onOMXMessage(const omx_message &msg) = 0;
+
+private:
+    friend class OMXClient;
+
+    pthread_t mThread;
+    Mutex mLock;
+    Condition mQueueNotEmpty;
+    List<omx_message> mQueue;
+
+    void start();
+    void stop();
+
+    static void *ThreadWrapper(void *me);
+    void threadEntry();
+
+    OMXObserver(const OMXObserver &);
+    OMXObserver &operator=(const OMXObserver &);
+};
+
+class OMXClient;
+
+class OMXClientReflector : public BnOMXObserver {
+public:
+    OMXClientReflector(OMXClient *client);
+
+    virtual void on_message(const omx_message &msg);
+    void reset();
+
+private:
+    OMXClient *mClient;
+
+    OMXClientReflector(const OMXClientReflector &);
+    OMXClientReflector &operator=(const OMXClientReflector &);
+};
+
+class OMXClient {
+public:
+    friend class OMXClientReflector;
+
+    OMXClient();
+    ~OMXClient();
+
+    status_t connect();
+    void disconnect();
+
+    sp<IOMX> interface() {
+        return mOMX;
+    }
+
+    status_t registerObserver(IOMX::node_id node, OMXObserver *observer);
+    void unregisterObserver(IOMX::node_id node);
+
+    status_t fillBuffer(IOMX::node_id node, IOMX::buffer_id buffer);
+
+    status_t emptyBuffer(
+            IOMX::node_id node, IOMX::buffer_id buffer,
+            OMX_U32 range_offset, OMX_U32 range_length,
+            OMX_U32 flags, OMX_TICKS timestamp);
+
+    status_t send_command(
+            IOMX::node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+private:
+    sp<IOMX> mOMX;
+
+    int mSock;
+    Mutex mLock;
+    pthread_t mThread;
+
+    KeyedVector<IOMX::node_id, OMXObserver *> mObservers;
+
+    sp<OMXClientReflector> mReflector;
+
+#if IOMX_USES_SOCKETS
+    static void *ThreadWrapper(void *me);
+    void threadEntry();
+#endif
+
+    bool onOMXMessage(const omx_message &msg);
+
+    OMXClient(const OMXClient &);
+    OMXClient &operator=(const OMXClient &);
+};
+
+}  // namespace android
+
+#endif  // OMX_CLIENT_H_
diff --git a/include/media/stagefright/OMXDecoder.h b/include/media/stagefright/OMXDecoder.h
new file mode 100644
index 0000000..0859457
--- /dev/null
+++ b/include/media/stagefright/OMXDecoder.h
@@ -0,0 +1,158 @@
+/*
+ * Copyright (C) 2009 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 OMX_DECODER_H_
+
+#define OMX_DECODER_H_
+
+#include <binder/MemoryDealer.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/OMXClient.h>
+#include <utils/KeyedVector.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class OMXMediaBuffer;
+
+class OMXDecoder : public MediaSource,
+                   public OMXObserver,
+                   public MediaBufferObserver {
+public:
+    static OMXDecoder *Create(
+            OMXClient *client, const sp<MetaData> &data);
+
+    static OMXDecoder *CreateEncoder(
+            OMXClient *client, const sp<MetaData> &data);
+
+    virtual ~OMXDecoder();
+
+    // Caller retains ownership of "source".
+    void setSource(MediaSource *source);
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+    void addCodecSpecificData(const void *data, size_t size);
+
+    // from OMXObserver
+    virtual void onOMXMessage(const omx_message &msg);
+
+    // from MediaBufferObserver
+    virtual void signalBufferReturned(MediaBuffer *buffer);
+
+private:
+    enum {
+        kPortIndexInput  = 0,
+        kPortIndexOutput = 1
+    };
+
+    enum PortStatus {
+        kPortStatusActive   = 0,
+        kPortStatusDisabled = 1,
+        kPortStatusShutdown = 2,
+        kPortStatusFlushing = 3
+    };
+
+    OMXClient *mClient;
+    sp<IOMX> mOMX;
+    IOMX::node_id mNode;
+    char *mComponentName;
+    bool mIsMP3;
+
+    MediaSource *mSource;
+    sp<MetaData> mOutputFormat;
+
+    Mutex mLock;
+    Condition mOutputBufferAvailable;
+
+    List<MediaBuffer *> mOutputBuffers;
+
+    struct CodecSpecificData {
+        void *data;
+        size_t size;
+    };
+
+    List<CodecSpecificData> mCodecSpecificData;
+    List<CodecSpecificData>::iterator mCodecSpecificDataIterator;
+
+    volatile OMX_STATETYPE mState;
+    OMX_U32 mPortStatusMask;
+    bool mShutdownInitiated;
+
+    typedef List<IOMX::buffer_id> BufferList;
+    Vector<BufferList> mBuffers;
+
+    KeyedVector<IOMX::buffer_id, sp<IMemory> > mBufferMap;
+    KeyedVector<IOMX::buffer_id, OMXMediaBuffer *> mMediaBufferMap;
+
+    sp<MemoryDealer> mDealer;
+
+    bool mSeeking;
+    int64_t mSeekTimeUs;
+
+    bool mStarted;
+    status_t mErrorCondition;
+    bool mReachedEndOfInput;
+
+    OMXDecoder(OMXClient *client, IOMX::node_id node,
+               const char *mime, const char *codec);
+
+    void setPortStatus(OMX_U32 port_index, PortStatus status);
+    PortStatus getPortStatus(OMX_U32 port_index) const;
+
+    void allocateBuffers(OMX_U32 port_index);
+
+    void setAMRFormat();
+    void setAACFormat();
+    void setVideoOutputFormat(OMX_U32 width, OMX_U32 height);
+    void setup();
+    void dumpPortDefinition(OMX_U32 port_index);
+
+    void onStart();
+    void onEvent(OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2);
+    void onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data);
+    void onEventPortSettingsChanged(OMX_U32 port_index);
+    void onStateChanged(OMX_STATETYPE to);
+    void onEmptyBufferDone(IOMX::buffer_id buffer);
+    void onFillBufferDone(const omx_message &msg);
+
+    void onRealEmptyBufferDone(IOMX::buffer_id buffer);
+    void onRealFillBufferDone(const omx_message &msg);
+
+    void initiateShutdown();
+
+    void freeInputBuffer(IOMX::buffer_id buffer);
+    void freeOutputBuffer(IOMX::buffer_id buffer);
+
+    void postStart();
+    void postEmptyBufferDone(IOMX::buffer_id buffer);
+    void postInitialFillBuffer(IOMX::buffer_id buffer);
+
+    OMXDecoder(const OMXDecoder &);
+    OMXDecoder &operator=(const OMXDecoder &);
+};
+
+}  // namespace android
+
+#endif  // OMX_DECODER_H_
diff --git a/include/media/stagefright/QComHardwareRenderer.h b/include/media/stagefright/QComHardwareRenderer.h
new file mode 100644
index 0000000..8292dd5
--- /dev/null
+++ b/include/media/stagefright/QComHardwareRenderer.h
@@ -0,0 +1,57 @@
+/*
+ * Copyright (C) 2009 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 QCOM_HARDWARE_RENDERER_H_
+
+#define QCOM_HARDWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapPmem;
+
+class QComHardwareRenderer : public VideoRenderer {
+public:
+    QComHardwareRenderer(
+            const sp<ISurface> &surface,
+            size_t displayWidth, size_t displayHeight,
+            size_t decodedWidth, size_t decodedHeight);
+
+    virtual ~QComHardwareRenderer();
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate);
+
+private:
+    sp<ISurface> mISurface;
+    size_t mDisplayWidth, mDisplayHeight;
+    size_t mDecodedWidth, mDecodedHeight;
+    size_t mFrameSize;
+    sp<MemoryHeapPmem> mMemoryHeap;
+
+    bool getOffset(void *platformPrivate, size_t *offset);
+    void publishBuffers(uint32_t pmem_fd);
+
+    QComHardwareRenderer(const QComHardwareRenderer &);
+    QComHardwareRenderer &operator=(const QComHardwareRenderer &);
+};
+
+}  // namespace android
+
+#endif  // QCOM_HARDWARE_RENDERER_H_
diff --git a/include/media/stagefright/SampleTable.h b/include/media/stagefright/SampleTable.h
new file mode 100644
index 0000000..712da10
--- /dev/null
+++ b/include/media/stagefright/SampleTable.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2009 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 SAMPLE_TABLE_H_
+
+#define SAMPLE_TABLE_H_
+
+#include <sys/types.h>
+#include <stdint.h>
+
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class DataSource;
+
+class SampleTable {
+public:
+    // Caller retains ownership of "source".
+    SampleTable(DataSource *source);
+    ~SampleTable();
+
+    // type can be 'stco' or 'co64'.
+    status_t setChunkOffsetParams(
+            uint32_t type, off_t data_offset, off_t data_size);
+
+    status_t setSampleToChunkParams(off_t data_offset, off_t data_size);
+
+    // type can be 'stsz' or 'stz2'.
+    status_t setSampleSizeParams(
+            uint32_t type, off_t data_offset, off_t data_size);
+
+    status_t setTimeToSampleParams(off_t data_offset, off_t data_size);
+
+    status_t setSyncSampleParams(off_t data_offset, off_t data_size);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    uint32_t countChunkOffsets() const;
+    status_t getChunkOffset(uint32_t chunk_index, off_t *offset);
+
+    status_t getChunkForSample(
+            uint32_t sample_index, uint32_t *chunk_index,
+            uint32_t *chunk_relative_sample_index, uint32_t *desc_index);
+
+    uint32_t countSamples() const;
+    status_t getSampleSize(uint32_t sample_index, size_t *sample_size);
+
+    status_t getSampleOffsetAndSize(
+            uint32_t sample_index, off_t *offset, size_t *size);
+
+    status_t getMaxSampleSize(size_t *size);
+
+    status_t getDecodingTime(uint32_t sample_index, uint32_t *time);
+
+    enum {
+        kSyncSample_Flag = 1
+    };
+    status_t findClosestSample(
+            uint32_t req_time, uint32_t *sample_index, uint32_t flags);
+
+    status_t findClosestSyncSample(
+            uint32_t start_sample_index, uint32_t *sample_index);
+
+private:
+    DataSource *mDataSource;
+    Mutex mLock;
+
+    off_t mChunkOffsetOffset;
+    uint32_t mChunkOffsetType;
+    uint32_t mNumChunkOffsets;
+
+    off_t mSampleToChunkOffset;
+    uint32_t mNumSampleToChunkOffsets;
+
+    off_t mSampleSizeOffset;
+    uint32_t mSampleSizeFieldSize;
+    uint32_t mDefaultSampleSize;
+    uint32_t mNumSampleSizes;
+
+    uint32_t mTimeToSampleCount;
+    uint32_t *mTimeToSample;
+
+    off_t mSyncSampleOffset;
+    uint32_t mNumSyncSamples;
+
+    SampleTable(const SampleTable &);
+    SampleTable &operator=(const SampleTable &);
+};
+
+}  // namespace android
+
+#endif  // SAMPLE_TABLE_H_
diff --git a/include/media/stagefright/ShoutcastSource.h b/include/media/stagefright/ShoutcastSource.h
new file mode 100644
index 0000000..352857a
--- /dev/null
+++ b/include/media/stagefright/ShoutcastSource.h
@@ -0,0 +1,59 @@
+/*
+ * Copyright (C) 2009 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 SHOUTCAST_SOURCE_H_
+
+#define SHOUTCAST_SOURCE_H_
+
+#include <sys/types.h>
+
+#include <media/stagefright/MediaSource.h>
+
+namespace android {
+
+class HTTPStream;
+class MediaBufferGroup;
+
+class ShoutcastSource : public MediaSource {
+public:
+    // Assumes ownership of "http".
+    ShoutcastSource(HTTPStream *http);
+    virtual ~ShoutcastSource();
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+    HTTPStream *mHttp;
+    size_t mMetaDataOffset;
+    size_t mBytesUntilMetaData;
+
+    MediaBufferGroup *mGroup;
+    bool mStarted;
+
+    ShoutcastSource(const ShoutcastSource &);
+    ShoutcastSource &operator= (const ShoutcastSource &);
+};
+
+}  // namespace android
+
+#endif  // SHOUTCAST_SOURCE_H_
+
diff --git a/include/media/stagefright/SoftwareRenderer.h b/include/media/stagefright/SoftwareRenderer.h
new file mode 100644
index 0000000..705b914
--- /dev/null
+++ b/include/media/stagefright/SoftwareRenderer.h
@@ -0,0 +1,55 @@
+/*
+ * Copyright (C) 2009 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 SOFTWARE_RENDERER_H_
+
+#define SOFTWARE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class ISurface;
+class MemoryHeapBase;
+
+class SoftwareRenderer : public VideoRenderer {
+public:
+    SoftwareRenderer(
+            const sp<ISurface> &surface,
+            size_t displayWidth, size_t displayHeight,
+            size_t decodedWidth, size_t decodedHeight);
+
+    virtual ~SoftwareRenderer();
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate);
+
+private:
+    sp<ISurface> mISurface;
+    size_t mDisplayWidth, mDisplayHeight;
+    size_t mDecodedWidth, mDecodedHeight;
+    size_t mFrameSize;
+    sp<MemoryHeapBase> mMemoryHeap;
+    int mIndex;
+
+    SoftwareRenderer(const SoftwareRenderer &);
+    SoftwareRenderer &operator=(const SoftwareRenderer &);
+};
+
+}  // namespace android
+
+#endif  // SOFTWARE_RENDERER_H_
diff --git a/include/media/stagefright/SurfaceRenderer.h b/include/media/stagefright/SurfaceRenderer.h
new file mode 100644
index 0000000..298ab50
--- /dev/null
+++ b/include/media/stagefright/SurfaceRenderer.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2009 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 SURFACE_RENDERER_H_
+
+#define SURFACE_RENDERER_H_
+
+#include <media/stagefright/VideoRenderer.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+class Surface;
+
+class SurfaceRenderer : public VideoRenderer {
+public:
+    SurfaceRenderer(
+            const sp<Surface> &surface,
+            size_t displayWidth, size_t displayHeight,
+            size_t decodedWidth, size_t decodedHeight);
+
+    virtual ~SurfaceRenderer();
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate);
+
+private:
+    sp<Surface> mSurface;
+    size_t mDisplayWidth, mDisplayHeight;
+    size_t mDecodedWidth, mDecodedHeight;
+
+    SurfaceRenderer(const SurfaceRenderer &);
+    SurfaceRenderer &operator=(const SurfaceRenderer &);
+};
+
+}  // namespace android
+
+#endif  // SURFACE_RENDERER_H_
diff --git a/include/media/stagefright/TimeSource.h b/include/media/stagefright/TimeSource.h
new file mode 100644
index 0000000..f57d8cf
--- /dev/null
+++ b/include/media/stagefright/TimeSource.h
@@ -0,0 +1,49 @@
+/*
+ * Copyright (C) 2009 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 TIME_SOURCE_H_
+
+#define TIME_SOURCE_H_
+
+namespace android {
+
+class TimeSource {
+public:
+    TimeSource() {}
+    virtual ~TimeSource() {}
+
+    virtual int64_t getRealTimeUs() = 0;
+
+private:
+    TimeSource(const TimeSource &);
+    TimeSource &operator=(const TimeSource &);
+};
+
+class SystemTimeSource : public TimeSource {
+public:
+    SystemTimeSource();
+
+    virtual int64_t getRealTimeUs();
+
+private:
+    static int64_t GetSystemTimeUs();
+
+    int64_t mStartTimeUs;
+};
+
+}  // namespace android
+
+#endif  // TIME_SOURCE_H_
diff --git a/include/media/stagefright/TimedEventQueue.h b/include/media/stagefright/TimedEventQueue.h
new file mode 100644
index 0000000..a264421
--- /dev/null
+++ b/include/media/stagefright/TimedEventQueue.h
@@ -0,0 +1,106 @@
+/*
+ * Copyright (C) 2009 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 TIMED_EVENT_QUEUE_H_
+
+#define TIMED_EVENT_QUEUE_H_
+
+#include <pthread.h>
+
+#include <utils/List.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+namespace android {
+
+struct TimedEventQueue {
+
+    struct Event : public RefBase {
+        Event() {}
+        virtual ~Event() {}
+
+    protected:
+        virtual void fire(TimedEventQueue *queue, int64_t now_us) = 0;
+
+    private:
+        friend class TimedEventQueue;
+
+        Event(const Event &);
+        Event &operator=(const Event &);
+    };
+
+    TimedEventQueue();
+    ~TimedEventQueue();
+
+    // Start executing the event loop.
+    void start();
+
+    // Stop executing the event loop, if flush is false, any pending
+    // events are discarded, otherwise the queue will stop (and this call
+    // return) once all pending events have been handled.
+    void stop(bool flush = false);
+
+    // Posts an event to the front of the queue (after all events that
+    // have previously been posted to the front but before timed events).
+    void postEvent(const sp<Event> &event);
+
+    void postEventToBack(const sp<Event> &event);
+
+    // It is an error to post an event with a negative delay.
+    void postEventWithDelay(const sp<Event> &event, int64_t delay_us);
+
+    // If the event is to be posted at a time that has already passed,
+    // it will fire as soon as possible.
+    void postTimedEvent(const sp<Event> &event, int64_t realtime_us);
+
+    // Returns true iff event is currently in the queue and has been
+    // successfully cancelled. In this case the event will have been
+    // removed from the queue and won't fire.
+    bool cancelEvent(const sp<Event> &event);
+
+    static int64_t getRealTimeUs();
+
+private:
+    struct QueueItem {
+        sp<Event> event;
+        int64_t realtime_us;
+    };
+
+    struct StopEvent : public TimedEventQueue::Event {
+        virtual void fire(TimedEventQueue *queue, int64_t now_us) {
+            queue->mStopped = true;
+        }
+    };
+
+    pthread_t mThread;
+    List<QueueItem> mQueue;
+    Mutex mLock;
+    Condition mQueueNotEmptyCondition;
+    Condition mQueueHeadChangedCondition;
+
+    bool mRunning;
+    bool mStopped;
+
+    static void *ThreadWrapper(void *me);
+    void threadEntry();
+
+    TimedEventQueue(const TimedEventQueue &);
+    TimedEventQueue &operator=(const TimedEventQueue &);
+};
+
+}  // namespace android
+
+#endif  // TIMED_EVENT_QUEUE_H_
diff --git a/include/media/stagefright/Utils.h b/include/media/stagefright/Utils.h
new file mode 100644
index 0000000..30c7f11
--- /dev/null
+++ b/include/media/stagefright/Utils.h
@@ -0,0 +1,37 @@
+/*
+ * Copyright (C) 2009 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 UTILS_H_
+
+#define UTILS_H_
+
+#include <stdint.h>
+
+namespace android {
+
+#define FOURCC(c1, c2, c3, c4) \
+    (c1 << 24 | c2 << 16 | c3 << 8 | c4)
+
+uint16_t U16_AT(const uint8_t *ptr);
+uint32_t U32_AT(const uint8_t *ptr);
+uint64_t U64_AT(const uint8_t *ptr);
+
+uint64_t ntoh64(uint64_t x);
+uint64_t hton64(uint64_t x);
+
+}  // namespace android
+
+#endif  // UTILS_H_
diff --git a/include/media/stagefright/VideoRenderer.h b/include/media/stagefright/VideoRenderer.h
new file mode 100644
index 0000000..f80b277
--- /dev/null
+++ b/include/media/stagefright/VideoRenderer.h
@@ -0,0 +1,41 @@
+/*
+ * Copyright (C) 2009 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 VIDEO_RENDERER_H_
+
+#define VIDEO_RENDERER_H_
+
+#include <sys/types.h>
+
+namespace android {
+
+class VideoRenderer {
+public:
+    virtual ~VideoRenderer() {}
+
+    virtual void render(
+            const void *data, size_t size, void *platformPrivate) = 0;
+
+protected:
+    VideoRenderer() {}
+
+    VideoRenderer(const VideoRenderer &);
+    VideoRenderer &operator=(const VideoRenderer &);
+};
+
+}  // namespace android
+
+#endif  // VIDEO_RENDERER_H_
diff --git a/include/media/stagefright/string.h b/include/media/stagefright/string.h
new file mode 100644
index 0000000..5dc7116
--- /dev/null
+++ b/include/media/stagefright/string.h
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 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 STRING_H_
+
+#define STRING_H_
+
+#include <utils/String8.h>
+
+namespace android {
+
+class string {
+public:
+    typedef size_t size_type;
+    static size_type npos;
+
+    string();
+    string(const char *s);
+    string(const char *s, size_t length);
+    string(const string &from, size_type start, size_type length = npos);
+
+    const char *c_str() const;
+    size_type size() const;
+
+    void clear();
+    void erase(size_type from, size_type length);
+
+    size_type find(char c) const;
+
+    bool operator<(const string &other) const;
+    bool operator==(const string &other) const;
+
+    string &operator+=(char c);
+
+private:
+    String8 mString;
+};
+
+}  // namespace android
+
+#endif  // STRING_H_
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index ebbf13f..cdaab04 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -18,7 +18,8 @@
 	IMediaMetadataRetriever.cpp \
 	mediametadataretriever.cpp \
 	ToneGenerator.cpp \
-	JetPlayer.cpp
+	JetPlayer.cpp \
+        IOMX.cpp
 
 LOCAL_SHARED_LIBRARIES := \
 	libui libcutils libutils libbinder libsonivox
@@ -34,6 +35,7 @@
 endif
 
 LOCAL_C_INCLUDES := \
-	$(call include-path-for, graphics corecg)
+	$(call include-path-for, graphics corecg) \
+        $(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
 
 include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libmedia/IMediaPlayerService.cpp b/media/libmedia/IMediaPlayerService.cpp
index 0f64259..8d2c360 100644
--- a/media/libmedia/IMediaPlayerService.cpp
+++ b/media/libmedia/IMediaPlayerService.cpp
@@ -17,12 +17,14 @@
 
 #include <stdint.h>
 #include <sys/types.h>
-#include <binder/Parcel.h>
 
+#include <binder/Parcel.h>
 #include <binder/IMemory.h>
-#include <utils/Errors.h>  // for status_t
 #include <media/IMediaPlayerService.h>
 #include <media/IMediaRecorder.h>
+#include <media/IOMX.h>
+
+#include <utils/Errors.h>  // for status_t
 
 namespace android {
 
@@ -33,6 +35,7 @@
     DECODE_FD,
     CREATE_MEDIA_RECORDER,
     CREATE_METADATA_RETRIEVER,
+    CREATE_OMX,
 };
 
 class BpMediaPlayerService: public BpInterface<IMediaPlayerService>
@@ -110,6 +113,13 @@
         *pFormat = reply.readInt32();
         return interface_cast<IMemory>(reply.readStrongBinder());
     }
+
+    virtual sp<IOMX> createOMX() {
+        Parcel data, reply;
+        data.writeInterfaceToken(IMediaPlayerService::getInterfaceDescriptor());
+        remote()->transact(CREATE_OMX, data, &reply);
+        return interface_cast<IOMX>(reply.readStrongBinder());
+    }
 };
 
 IMPLEMENT_META_INTERFACE(MediaPlayerService, "android.media.IMediaPlayerService");
@@ -182,6 +192,12 @@
             reply->writeStrongBinder(retriever->asBinder());
             return NO_ERROR;
         } break;
+        case CREATE_OMX: {
+            CHECK_INTERFACE(IMediaPlayerService, data, reply);
+            sp<IOMX> omx = createOMX();
+            reply->writeStrongBinder(omx->asBinder());
+            return NO_ERROR;
+        } break;
         default:
             return BBinder::onTransact(code, data, reply, flags);
     }
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
new file mode 100644
index 0000000..f2a657a
--- /dev/null
+++ b/media/libmedia/IOMX.cpp
@@ -0,0 +1,561 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "IOMX"
+#include <utils/Log.h>
+
+#include <binder/IMemory.h>
+#include <binder/Parcel.h>
+#include <media/IOMX.h>
+
+namespace android {
+
+enum {
+    CONNECT = IBinder::FIRST_CALL_TRANSACTION,
+    LIST_NODES,
+    ALLOCATE_NODE,
+    FREE_NODE,
+    SEND_COMMAND,
+    GET_PARAMETER,
+    SET_PARAMETER,
+    USE_BUFFER,
+    ALLOC_BUFFER,
+    ALLOC_BUFFER_WITH_BACKUP,
+    FREE_BUFFER,
+    OBSERVE_NODE,
+    FILL_BUFFER,
+    EMPTY_BUFFER,
+    OBSERVER_ON_MSG,
+};
+
+static void *readVoidStar(const Parcel *parcel) {
+    // FIX if sizeof(void *) != sizeof(int32)
+    return (void *)parcel->readInt32();
+}
+
+static void writeVoidStar(void *x, Parcel *parcel) {
+    // FIX if sizeof(void *) != sizeof(int32)
+    parcel->writeInt32((int32_t)x);
+}
+
+class BpOMX : public BpInterface<IOMX> {
+public:
+    BpOMX(const sp<IBinder> &impl)
+        : BpInterface<IOMX>(impl) {
+    }
+
+#if IOMX_USES_SOCKETS
+    virtual status_t connect(int *sd) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        remote()->transact(CONNECT, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err == OK) {
+            *sd = dup(reply.readFileDescriptor());
+        } else {
+            *sd = -1;
+        }
+
+        return reply.readInt32();
+    }
+#endif
+
+    virtual status_t list_nodes(List<String8> *list) {
+        list->clear();
+
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        remote()->transact(LIST_NODES, data, &reply);
+
+        int32_t n = reply.readInt32();
+        for (int32_t i = 0; i < n; ++i) {
+            String8 s = reply.readString8();
+
+            list->push_back(s);
+        }
+
+        return OK;
+    }
+
+    virtual status_t allocate_node(const char *name, node_id *node) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        data.writeCString(name);
+        remote()->transact(ALLOCATE_NODE, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err == OK) {
+            *node = readVoidStar(&reply);
+        } else {
+            *node = 0;
+        }
+
+        return err;
+    }
+
+    virtual status_t free_node(node_id node) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        remote()->transact(FREE_NODE, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t send_command(
+            node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(cmd);
+        data.writeInt32(param);
+        remote()->transact(SEND_COMMAND, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t get_parameter(
+            node_id node, OMX_INDEXTYPE index,
+            void *params, size_t size) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(index);
+        data.writeInt32(size);
+        data.write(params, size);
+        remote()->transact(GET_PARAMETER, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err != OK) {
+            return err;
+        }
+
+        reply.read(params, size);
+
+        return OK;
+    }
+
+    virtual status_t set_parameter(
+            node_id node, OMX_INDEXTYPE index,
+            const void *params, size_t size) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(index);
+        data.writeInt32(size);
+        data.write(params, size);
+        remote()->transact(SET_PARAMETER, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual status_t use_buffer(
+            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+            buffer_id *buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(port_index);
+        data.writeStrongBinder(params->asBinder());
+        remote()->transact(USE_BUFFER, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err != OK) {
+            *buffer = 0;
+
+            return err;
+        }
+
+        *buffer = readVoidStar(&reply);
+
+        return err;
+    }
+
+    virtual status_t allocate_buffer(
+            node_id node, OMX_U32 port_index, size_t size,
+            buffer_id *buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(port_index);
+        data.writeInt32(size);
+        remote()->transact(ALLOC_BUFFER, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err != OK) {
+            *buffer = 0;
+
+            return err;
+        }
+
+        *buffer = readVoidStar(&reply);
+
+        return err;
+    }
+
+    virtual status_t allocate_buffer_with_backup(
+            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+            buffer_id *buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(port_index);
+        data.writeStrongBinder(params->asBinder());
+        remote()->transact(ALLOC_BUFFER_WITH_BACKUP, data, &reply);
+
+        status_t err = reply.readInt32();
+        if (err != OK) {
+            *buffer = 0;
+
+            return err;
+        }
+
+        *buffer = readVoidStar(&reply);
+
+        return err;
+    }
+
+    virtual status_t free_buffer(
+            node_id node, OMX_U32 port_index, buffer_id buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeInt32(port_index);
+        writeVoidStar(buffer, &data);
+        remote()->transact(FREE_BUFFER, data, &reply);
+
+        return reply.readInt32();
+    }
+
+#if !IOMX_USES_SOCKETS
+    virtual status_t observe_node(
+            node_id node, const sp<IOMXObserver> &observer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        data.writeStrongBinder(observer->asBinder());
+        remote()->transact(OBSERVE_NODE, data, &reply);
+
+        return reply.readInt32();
+    }
+
+    virtual void fill_buffer(node_id node, buffer_id buffer) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        writeVoidStar(buffer, &data);
+        remote()->transact(FILL_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+
+    virtual void empty_buffer(
+            node_id node,
+            buffer_id buffer,
+            OMX_U32 range_offset, OMX_U32 range_length,
+            OMX_U32 flags, OMX_TICKS timestamp) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMX::getInterfaceDescriptor());
+        writeVoidStar(node, &data);
+        writeVoidStar(buffer, &data);
+        data.writeInt32(range_offset);
+        data.writeInt32(range_length);
+        data.writeInt32(flags);
+        data.writeInt64(timestamp);
+        remote()->transact(EMPTY_BUFFER, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+#endif
+};
+
+IMPLEMENT_META_INTERFACE(OMX, "android.hardware.IOMX");
+
+////////////////////////////////////////////////////////////////////////////////
+
+#define CHECK_INTERFACE(interface, data, reply) \
+        do { if (!data.enforceInterface(interface::getInterfaceDescriptor())) { \
+            LOGW("Call incorrectly routed to " #interface); \
+            return PERMISSION_DENIED; \
+        } } while (0)
+
+status_t BnOMX::onTransact(
+    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+    switch (code) {
+#if IOMX_USES_SOCKETS
+        case CONNECT:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            int s;
+            status_t err = connect(&s);
+            
+            reply->writeInt32(err);
+            if (err == OK) {
+                assert(s >= 0);
+                reply->writeDupFileDescriptor(s);
+                close(s);
+                s = -1;
+            } else {
+                assert(s == -1);
+            }
+
+            return NO_ERROR;
+        }
+#endif
+
+        case LIST_NODES:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            List<String8> list;
+            list_nodes(&list);
+
+            reply->writeInt32(list.size());
+            for (List<String8>::iterator it = list.begin();
+                 it != list.end(); ++it) {
+                reply->writeString8(*it);
+            }
+
+            return NO_ERROR;
+        }
+
+        case ALLOCATE_NODE:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node;
+            status_t err = allocate_node(data.readCString(), &node);
+            reply->writeInt32(err);
+            if (err == OK) {
+                writeVoidStar(node, reply);
+            }
+                
+            return NO_ERROR;
+        }
+
+        case FREE_NODE:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+
+            reply->writeInt32(free_node(node));
+                
+            return NO_ERROR;
+        }
+
+        case SEND_COMMAND:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+
+            OMX_COMMANDTYPE cmd =
+                static_cast<OMX_COMMANDTYPE>(data.readInt32());
+
+            OMX_S32 param = data.readInt32();
+            reply->writeInt32(send_command(node, cmd, param));
+
+            return NO_ERROR;
+        }
+
+        case GET_PARAMETER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+            size_t size = data.readInt32();
+
+            // XXX I am not happy with this but Parcel::readInplace didn't work.
+            void *params = malloc(size);
+            data.read(params, size);
+
+            status_t err = get_parameter(node, index, params, size);
+
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                reply->write(params, size);
+            }
+
+            free(params);
+            params = NULL;
+
+            return NO_ERROR;
+        }
+
+        case SET_PARAMETER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            OMX_INDEXTYPE index = static_cast<OMX_INDEXTYPE>(data.readInt32());
+
+            size_t size = data.readInt32();
+            void *params = const_cast<void *>(data.readInplace(size));
+
+            reply->writeInt32(set_parameter(node, index, params, size));
+
+            return NO_ERROR;
+        }
+
+        case USE_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            OMX_U32 port_index = data.readInt32();
+            sp<IMemory> params =
+                interface_cast<IMemory>(data.readStrongBinder());
+
+            buffer_id buffer;
+            status_t err = use_buffer(node, port_index, params, &buffer);
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                writeVoidStar(buffer, reply);
+            }
+
+            return NO_ERROR;
+        }
+
+        case ALLOC_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            OMX_U32 port_index = data.readInt32();
+            size_t size = data.readInt32();
+
+            buffer_id buffer;
+            status_t err = allocate_buffer(node, port_index, size, &buffer);
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                writeVoidStar(buffer, reply);
+            }
+
+            return NO_ERROR;
+        }
+
+        case ALLOC_BUFFER_WITH_BACKUP:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            OMX_U32 port_index = data.readInt32();
+            sp<IMemory> params =
+                interface_cast<IMemory>(data.readStrongBinder());
+
+            buffer_id buffer;
+            status_t err = allocate_buffer_with_backup(
+                    node, port_index, params, &buffer);
+
+            reply->writeInt32(err);
+
+            if (err == OK) {
+                writeVoidStar(buffer, reply);
+            }
+
+            return NO_ERROR;
+        }
+
+        case FREE_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            OMX_U32 port_index = data.readInt32();
+            buffer_id buffer = readVoidStar(&data);
+            reply->writeInt32(free_buffer(node, port_index, buffer));
+
+            return NO_ERROR;
+        }
+
+#if !IOMX_USES_SOCKETS
+        case OBSERVE_NODE:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            sp<IOMXObserver> observer =
+                interface_cast<IOMXObserver>(data.readStrongBinder());
+            reply->writeInt32(observe_node(node, observer));
+
+            return NO_ERROR;
+        }
+
+        case FILL_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            buffer_id buffer = readVoidStar(&data);
+            fill_buffer(node, buffer);
+
+            return NO_ERROR;
+        }
+
+        case EMPTY_BUFFER:
+        {
+            CHECK_INTERFACE(IOMX, data, reply);
+
+            node_id node = readVoidStar(&data);
+            buffer_id buffer = readVoidStar(&data);
+            OMX_U32 range_offset = data.readInt32();
+            OMX_U32 range_length = data.readInt32();
+            OMX_U32 flags = data.readInt32();
+            OMX_TICKS timestamp = data.readInt64();
+
+            empty_buffer(
+                    node, buffer, range_offset, range_length,
+                    flags, timestamp);
+
+            return NO_ERROR;
+        }
+#endif
+
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+class BpOMXObserver : public BpInterface<IOMXObserver> {
+public:
+    BpOMXObserver(const sp<IBinder> &impl)
+        : BpInterface<IOMXObserver>(impl) {
+    }
+
+    virtual void on_message(const omx_message &msg) {
+        Parcel data, reply;
+        data.writeInterfaceToken(IOMXObserver::getInterfaceDescriptor());
+        data.write(&msg, sizeof(msg));
+
+        remote()->transact(OBSERVER_ON_MSG, data, &reply, IBinder::FLAG_ONEWAY);
+    }
+};
+
+IMPLEMENT_META_INTERFACE(OMXObserver, "android.hardware.IOMXObserver");
+
+status_t BnOMXObserver::onTransact(
+    uint32_t code, const Parcel &data, Parcel *reply, uint32_t flags) {
+    switch (code) {
+        case OBSERVER_ON_MSG:
+        {
+            CHECK_INTERFACE(IOMXObserver, data, reply);
+
+            omx_message msg;
+            data.read(&msg, sizeof(msg));
+
+            // XXX Could use readInplace maybe?
+            on_message(msg);
+
+            return NO_ERROR;
+        }
+
+        default:
+            return BBinder::onTransact(code, data, reply, flags);
+    }
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index 0877142..6978b3d 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -29,9 +29,22 @@
     libandroid_runtime
 
 LOCAL_C_INCLUDES := external/tremor/Tremor \
-    $(call include-path-for, graphics corecg)
+	$(call include-path-for, graphics corecg) \
+	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include
 
 LOCAL_MODULE:= libmediaplayerservice
 
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+    LOCAL_SRC_FILES += StagefrightPlayer.cpp
+
+    LOCAL_SHARED_LIBRARIES += \
+	libstagefright        \
+	libstagefright_omx
+
+    LOCAL_C_INCLUDES += $(TOP)/frameworks/base/media/libstagefright/omx
+
+    LOCAL_CFLAGS += -DBUILD_WITH_STAGEFRIGHT -DUSE_STAGEFRIGHT
+endif
+
 include $(BUILD_SHARED_LIBRARY)
 
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 493dc13..c575f6c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -58,6 +58,15 @@
 #include "MidiFile.h"
 #include "VorbisPlayer.h"
 #include <media/PVPlayer.h>
+#if USE_STAGEFRIGHT
+#include "StagefrightPlayer.h"
+#endif
+
+#ifdef BUILD_WITH_STAGEFRIGHT
+#include <OMX.h>
+#else
+#include <media/IOMX.h>
+#endif
 
 /* desktop Linux needs a little help with gettid() */
 #if defined(HAVE_GETTID) && !defined(HAVE_ANDROID_OS)
@@ -186,6 +195,10 @@
     const player_type playertype;
 } extmap;
 extmap FILE_EXTS [] =  {
+#if USE_STAGEFRIGHT
+        {".mp4", STAGEFRIGHT_PLAYER},
+        {".3gp", STAGEFRIGHT_PLAYER},
+#endif
         {".mid", SONIVOX_PLAYER},
         {".midi", SONIVOX_PLAYER},
         {".smf", SONIVOX_PLAYER},
@@ -271,6 +284,14 @@
     return c;
 }
 
+sp<IOMX> MediaPlayerService::createOMX() {
+#ifdef BUILD_WITH_STAGEFRIGHT
+    return new OMX;
+#else
+    return NULL;
+#endif
+}
+
 status_t MediaPlayerService::AudioCache::dump(int fd, const Vector<String16>& args) const
 {
     const size_t SIZE = 256;
@@ -577,6 +598,7 @@
         p = mPlayer;
     }
     mClient.clear();
+
     mPlayer.clear();
 
     // clear the notification to prevent callbacks to dead client
@@ -624,13 +646,16 @@
         EAS_Shutdown(easdata);
     }
 
+#if USE_STAGEFRIGHT
+    return STAGEFRIGHT_PLAYER;
+#endif
+
     // Fall through to PV
     return PV_PLAYER;
 }
 
 static player_type getPlayerType(const char* url)
 {
-
     // use MidiFile for MIDI extensions
     int lenURL = strlen(url);
     for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -643,6 +668,10 @@
         }
     }
 
+#if USE_STAGEFRIGHT
+    return STAGEFRIGHT_PLAYER;
+#endif
+
     // Fall through to PV
     return PV_PLAYER;
 }
@@ -666,6 +695,17 @@
             LOGV(" create VorbisPlayer");
             p = new VorbisPlayer();
             break;
+#if USE_STAGEFRIGHT
+        case STAGEFRIGHT_PLAYER:
+            LOGV(" create StagefrightPlayer");
+            p = new StagefrightPlayer;
+            break;
+#else
+        case STAGEFRIGHT_PLAYER:
+            LOG_ALWAYS_FATAL(
+                    "Should not be here, stagefright player not enabled.");
+            break;
+#endif
     }
     if (p != NULL) {
         if (p->initCheck() == NO_ERROR) {
@@ -1136,7 +1176,8 @@
 #undef LOG_TAG
 #define LOG_TAG "AudioSink"
 MediaPlayerService::AudioOutput::AudioOutput()
-{
+    : mCallback(NULL),
+      mCallbackCookie(NULL) {
     mTrack = 0;
     mStreamType = AudioSystem::MUSIC;
     mLeftVolume = 1.0;
@@ -1206,8 +1247,13 @@
     return mMsecsPerFrame;
 }
 
-status_t MediaPlayerService::AudioOutput::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioOutput::open(
+        uint32_t sampleRate, int channelCount, int format, int bufferCount,
+        AudioCallback cb, void *cookie)
 {
+    mCallback = cb;
+    mCallbackCookie = cookie;
+
     // Check argument "bufferCount" against the mininum buffer count
     if (bufferCount < mMinBufferCount) {
         LOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
@@ -1228,7 +1274,17 @@
     }
 
     frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
-    AudioTrack *t = new AudioTrack(mStreamType, sampleRate, format, channelCount, frameCount);
+
+    AudioTrack *t;
+    if (mCallback != NULL) {
+        t = new AudioTrack(
+                mStreamType, sampleRate, format, channelCount, frameCount,
+                0 /* flags */, CallbackWrapper, this);
+    } else {
+        t = new AudioTrack(
+                mStreamType, sampleRate, format, channelCount, frameCount);
+    }
+
     if ((t == 0) || (t->initCheck() != NO_ERROR)) {
         LOGE("Unable to create audio track");
         delete t;
@@ -1254,6 +1310,8 @@
 
 ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
 {
+    LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
+
     //LOGV("write(%p, %u)", buffer, size);
     if (mTrack) return mTrack->write(buffer, size);
     return NO_INIT;
@@ -1294,6 +1352,20 @@
     }
 }
 
+// static
+void MediaPlayerService::AudioOutput::CallbackWrapper(
+        int event, void *cookie, void *info) {
+    if (event != AudioTrack::EVENT_MORE_DATA) {
+        return;
+    }
+
+    AudioOutput *me = (AudioOutput *)cookie;
+    AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+
+    (*me->mCallback)(
+            me, buffer->raw, buffer->size, me->mCallbackCookie);
+}
+
 #undef LOG_TAG
 #define LOG_TAG "AudioCache"
 MediaPlayerService::AudioCache::AudioCache(const char* name) :
@@ -1314,8 +1386,14 @@
     return mMsecsPerFrame;
 }
 
-status_t MediaPlayerService::AudioCache::open(uint32_t sampleRate, int channelCount, int format, int bufferCount)
+status_t MediaPlayerService::AudioCache::open(
+        uint32_t sampleRate, int channelCount, int format, int bufferCount,
+        AudioCallback cb, void *cookie)
 {
+    if (cb != NULL) {
+        return UNKNOWN_ERROR;  // TODO: implement this.
+    }
+
     LOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
     if (mHeap->getHeapID() < 0) return NO_INIT;
     mSampleRate = sampleRate;
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index db3d5d7c..94cb917 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -35,6 +35,7 @@
 
 class IMediaRecorder;
 class IMediaMetadataRetriever;
+class IOMX;
 
 #define CALLBACK_ANTAGONIZER 0
 #if CALLBACK_ANTAGONIZER
@@ -75,7 +76,12 @@
         virtual ssize_t         frameSize() const;
         virtual uint32_t        latency() const;
         virtual float           msecsPerFrame() const;
-        virtual status_t        open(uint32_t sampleRate, int channelCount, int format, int bufferCount=4);
+
+        virtual status_t        open(
+                uint32_t sampleRate, int channelCount,
+                int format, int bufferCount,
+                AudioCallback cb, void *cookie);
+
         virtual void            start();
         virtual ssize_t         write(const void* buffer, size_t size);
         virtual void            stop();
@@ -90,8 +96,12 @@
         static int              getMinBufferCount();
     private:
         static void             setMinBufferCount();
+        static void             CallbackWrapper(
+                int event, void *me, void *info);
 
         AudioTrack*             mTrack;
+        AudioCallback           mCallback;
+        void *                  mCallbackCookie;
         int                     mStreamType;
         float                   mLeftVolume;
         float                   mRightVolume;
@@ -119,7 +129,12 @@
         virtual ssize_t         frameSize() const { return ssize_t(mChannelCount * ((mFormat == AudioSystem::PCM_16_BIT)?sizeof(int16_t):sizeof(u_int8_t))); }
         virtual uint32_t        latency() const;
         virtual float           msecsPerFrame() const;
-        virtual status_t        open(uint32_t sampleRate, int channelCount, int format, int bufferCount=1);
+
+        virtual status_t        open(
+                uint32_t sampleRate, int channelCount, int format,
+                int bufferCount = 1,
+                AudioCallback cb = NULL, void *cookie = NULL);
+
         virtual void            start() {}
         virtual ssize_t         write(const void* buffer, size_t size);
         virtual void            stop() {}
@@ -166,6 +181,7 @@
     virtual sp<IMediaPlayer>    create(pid_t pid, const sp<IMediaPlayerClient>& client, int fd, int64_t offset, int64_t length);
     virtual sp<IMemory>         decode(const char* url, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
     virtual sp<IMemory>         decode(int fd, int64_t offset, int64_t length, uint32_t *pSampleRate, int* pNumChannels, int* pFormat);
+    virtual sp<IOMX>            createOMX();
 
     virtual status_t            dump(int fd, const Vector<String16>& args);
 
diff --git a/media/libmediaplayerservice/StagefrightPlayer.cpp b/media/libmediaplayerservice/StagefrightPlayer.cpp
new file mode 100644
index 0000000..ad1afbb
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightPlayer.cpp
@@ -0,0 +1,208 @@
+//#define LOG_NDEBUG 0
+#define LOG_TAG "StagefrightPlayer"
+#include <utils/Log.h>
+
+#include "StagefrightPlayer.h"
+#include <media/stagefright/MediaPlayerImpl.h>
+
+namespace android {
+
+StagefrightPlayer::StagefrightPlayer()
+    : mPlayer(NULL) {
+    LOGV("StagefrightPlayer");
+}
+
+StagefrightPlayer::~StagefrightPlayer() {
+    LOGV("~StagefrightPlayer");
+    reset();
+    LOGV("~StagefrightPlayer done.");
+}
+
+status_t StagefrightPlayer::initCheck() {
+    LOGV("initCheck");
+    return OK;
+}
+
+status_t StagefrightPlayer::setDataSource(const char *url) {
+    LOGV("setDataSource('%s')", url);
+
+    reset();
+    mPlayer = new MediaPlayerImpl(url);
+
+    status_t err = mPlayer->initCheck();
+    if (err != OK) {
+        delete mPlayer;
+        mPlayer = NULL;
+    } else {
+        mPlayer->setAudioSink(mAudioSink);
+    }
+
+    return err;
+}
+
+status_t StagefrightPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+    LOGV("setDataSource(%d, %lld, %lld)", fd, offset, length);
+
+    reset();
+    mPlayer = new MediaPlayerImpl(fd, offset, length);
+
+    status_t err = mPlayer->initCheck();
+    if (err != OK) {
+        delete mPlayer;
+        mPlayer = NULL;
+    } else {
+        mPlayer->setAudioSink(mAudioSink);
+    }
+
+    return err;
+}
+
+status_t StagefrightPlayer::setVideoSurface(const sp<ISurface> &surface) {
+    LOGV("setVideoSurface");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    mPlayer->setISurface(surface);
+
+    return OK;
+}
+
+status_t StagefrightPlayer::prepare() {
+    LOGV("prepare");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    sendEvent(
+            MEDIA_SET_VIDEO_SIZE,
+            mPlayer->getWidth(), mPlayer->getHeight());
+
+    return OK;
+}
+
+status_t StagefrightPlayer::prepareAsync() {
+    LOGV("prepareAsync");
+
+    status_t err = prepare();
+
+    if (err != OK) {
+        return err;
+    }
+
+    sendEvent(MEDIA_PREPARED);
+
+    return OK;
+}
+
+status_t StagefrightPlayer::start() {
+    LOGV("start");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    mPlayer->play();
+
+    return OK;
+}
+
+status_t StagefrightPlayer::stop() {
+    LOGV("stop");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    reset();
+
+    return OK;
+}
+
+status_t StagefrightPlayer::pause() {
+    LOGV("pause");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    mPlayer->pause();
+
+    return OK;
+}
+
+bool StagefrightPlayer::isPlaying() {
+    LOGV("isPlaying");
+    return mPlayer != NULL && mPlayer->isPlaying();
+}
+
+status_t StagefrightPlayer::seekTo(int msec) {
+    LOGV("seekTo");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+    
+    status_t err = mPlayer->seekTo((int64_t)msec * 1000);
+
+    sendEvent(MEDIA_SEEK_COMPLETE);
+
+    return err;
+}
+
+status_t StagefrightPlayer::getCurrentPosition(int *msec) {
+    LOGV("getCurrentPosition");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    *msec = mPlayer->getPosition() / 1000;
+    return OK;
+}
+
+status_t StagefrightPlayer::getDuration(int *msec) {
+    LOGV("getDuration");
+
+    if (mPlayer == NULL) {
+        return NO_INIT;
+    }
+
+    *msec = mPlayer->getDuration() / 1000;
+    return OK;
+}
+
+status_t StagefrightPlayer::reset() {
+    LOGV("reset");
+
+    delete mPlayer;
+    mPlayer = NULL;
+
+    return OK;
+}
+
+status_t StagefrightPlayer::setLooping(int loop) {
+    LOGV("setLooping");
+    return UNKNOWN_ERROR;
+}
+
+player_type StagefrightPlayer::playerType() {
+    LOGV("playerType");
+    return STAGEFRIGHT_PLAYER;
+}
+
+status_t StagefrightPlayer::invoke(const Parcel &request, Parcel *reply) {
+    return INVALID_OPERATION;
+}
+
+void StagefrightPlayer::setAudioSink(const sp<AudioSink> &audioSink) {
+    MediaPlayerInterface::setAudioSink(audioSink);
+
+    if (mPlayer != NULL) {
+        mPlayer->setAudioSink(audioSink);
+    }
+}
+
+}  // namespace android
diff --git a/media/libmediaplayerservice/StagefrightPlayer.h b/media/libmediaplayerservice/StagefrightPlayer.h
new file mode 100644
index 0000000..f214872c
--- /dev/null
+++ b/media/libmediaplayerservice/StagefrightPlayer.h
@@ -0,0 +1,60 @@
+/*
+**
+** Copyright 2009, 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 ANDROID_STAGEFRIGHTPLAYER_H
+#define ANDROID_STAGEFRIGHTPLAYER_H
+
+#include <media/MediaPlayerInterface.h>
+
+namespace android {
+
+class MediaPlayerImpl;
+
+class StagefrightPlayer : public MediaPlayerInterface {
+public:
+    StagefrightPlayer();
+    virtual ~StagefrightPlayer();
+
+    virtual status_t initCheck();
+    virtual status_t setDataSource(const char *url);
+    virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+    virtual status_t setVideoSurface(const sp<ISurface> &surface);
+    virtual status_t prepare();
+    virtual status_t prepareAsync();
+    virtual status_t start();
+    virtual status_t stop();
+    virtual status_t pause();
+    virtual bool isPlaying();
+    virtual status_t seekTo(int msec);
+    virtual status_t getCurrentPosition(int *msec);
+    virtual status_t getDuration(int *msec);
+    virtual status_t reset();
+    virtual status_t setLooping(int loop);
+    virtual player_type playerType();
+    virtual status_t invoke(const Parcel &request, Parcel *reply);
+    virtual void setAudioSink(const sp<AudioSink> &audioSink);
+
+private:
+    MediaPlayerImpl *mPlayer;
+
+    StagefrightPlayer(const StagefrightPlayer &);
+    StagefrightPlayer &operator=(const StagefrightPlayer &);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_STAGEFRIGHTPLAYER_H
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
new file mode 100644
index 0000000..6e7936c
--- /dev/null
+++ b/media/libstagefright/Android.mk
@@ -0,0 +1,56 @@
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:=                 \
+	CachingDataSource.cpp     \
+        DataSource.cpp            \
+	FileSource.cpp            \
+	HTTPDataSource.cpp        \
+	HTTPStream.cpp            \
+	MP3Extractor.cpp          \
+	MPEG4Extractor.cpp        \
+	MPEG4Writer.cpp           \
+	MediaBuffer.cpp           \
+        MediaBufferGroup.cpp      \
+        MediaExtractor.cpp        \
+        MediaPlayerImpl.cpp       \
+        MediaSource.cpp           \
+	MetaData.cpp              \
+        MmapSource.cpp            \
+        QComHardwareRenderer.cpp  \
+	SampleTable.cpp           \
+	ShoutcastSource.cpp       \
+        SoftwareRenderer.cpp      \
+        SurfaceRenderer.cpp       \
+        TimeSource.cpp            \
+        TimedEventQueue.cpp       \
+	Utils.cpp                 \
+        AudioPlayer.cpp           \
+        ESDS.cpp                  \
+        OMXClient.cpp             \
+        OMXDecoder.cpp            \
+        string.cpp
+
+LOCAL_C_INCLUDES:= \
+	$(TOP)/external/opencore/extern_libs_v2/khronos/openmax/include \
+        $(TOP)/external/opencore/android
+
+LOCAL_SHARED_LIBRARIES := \
+        libbinder         \
+        libmedia          \
+	libutils          \
+        libcutils         \
+        libui
+
+LOCAL_CFLAGS += -Wno-multichar
+
+LOCAL_PRELINK_MODULE:= false
+
+LOCAL_MODULE:= libstagefright
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(call all-makefiles-under,$(LOCAL_PATH))
+endif
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
new file mode 100644
index 0000000..17c72b9
--- /dev/null
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#define LOG_TAG "AudioPlayer"
+#include <utils/Log.h>
+
+#include <media/AudioTrack.h>
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+AudioPlayer::AudioPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink)
+    : mSource(NULL),
+      mAudioTrack(NULL),
+      mInputBuffer(NULL),
+      mSampleRate(0),
+      mLatencyUs(0),
+      mFrameSize(0),
+      mNumFramesPlayed(0),
+      mPositionTimeMediaUs(-1),
+      mPositionTimeRealUs(-1),
+      mSeeking(false),
+      mStarted(false),
+      mAudioSink(audioSink) {
+}
+
+AudioPlayer::~AudioPlayer() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+void AudioPlayer::setSource(MediaSource *source) {
+    assert(mSource == NULL);
+    mSource = source;
+}
+
+void AudioPlayer::start() {
+    assert(!mStarted);
+    assert(mSource != NULL);
+
+    status_t err = mSource->start();
+    assert(err == OK);
+
+    sp<MetaData> format = mSource->getFormat();
+    const char *mime;
+    bool success = format->findCString(kKeyMIMEType, &mime);
+    assert(success);
+    assert(!strcasecmp(mime, "audio/raw"));
+
+    success = format->findInt32(kKeySampleRate, &mSampleRate);
+    assert(success);
+
+    int32_t numChannels;
+    success = format->findInt32(kKeyChannelCount, &numChannels);
+    assert(success);
+
+    if (mAudioSink.get() != NULL) {
+        status_t err = mAudioSink->open(
+                mSampleRate, numChannels, AudioSystem::PCM_16_BIT,
+                DEFAULT_AUDIOSINK_BUFFERCOUNT,
+                &AudioPlayer::AudioSinkCallback, this);
+        assert(err == OK);
+
+        mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+        mFrameSize = mAudioSink->frameSize();
+
+        mAudioSink->start();
+    } else {
+        mAudioTrack = new AudioTrack(
+                AudioSystem::MUSIC, mSampleRate, AudioSystem::PCM_16_BIT,
+                numChannels, 8192, 0, &AudioCallback, this, 0);
+
+        assert(mAudioTrack->initCheck() == OK);
+
+        mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+        mFrameSize = mAudioTrack->frameSize();
+
+        mAudioTrack->start();
+    }
+
+    mStarted = true;
+}
+
+void AudioPlayer::pause() {
+    assert(mStarted);
+
+    if (mAudioSink.get() != NULL) {
+        mAudioSink->pause();
+    } else {
+        mAudioTrack->stop();
+    }
+}
+
+void AudioPlayer::resume() {
+    assert(mStarted);
+
+    if (mAudioSink.get() != NULL) {
+        mAudioSink->start();
+    } else {
+        mAudioTrack->start();
+    }
+}
+
+void AudioPlayer::stop() {
+    assert(mStarted);
+
+    if (mAudioSink.get() != NULL) {
+        mAudioSink->stop();
+    } else {
+        mAudioTrack->stop();
+
+        delete mAudioTrack;
+        mAudioTrack = NULL;
+    }
+    
+    // Make sure to release any buffer we hold onto so that the
+    // source is able to stop().
+    if (mInputBuffer != NULL) {
+        LOGI("AudioPlayer releasing input buffer.");
+
+        mInputBuffer->release();
+        mInputBuffer = NULL;
+    }
+
+    mSource->stop();
+    
+    mNumFramesPlayed = 0;
+    mPositionTimeMediaUs = -1;
+    mPositionTimeRealUs = -1;
+    mSeeking = false;
+    mStarted = false;
+}
+
+// static
+void AudioPlayer::AudioCallback(int event, void *user, void *info) {
+    static_cast<AudioPlayer *>(user)->AudioCallback(event, info);
+}
+
+// static
+void AudioPlayer::AudioSinkCallback(
+        MediaPlayerBase::AudioSink *audioSink,
+        void *buffer, size_t size, void *cookie) {
+    AudioPlayer *me = (AudioPlayer *)cookie;
+
+    me->fillBuffer(buffer, size);
+}
+
+void AudioPlayer::AudioCallback(int event, void *info) {
+    if (event != AudioTrack::EVENT_MORE_DATA) {
+        return;
+    }
+
+    AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+    fillBuffer(buffer->raw, buffer->size);
+}
+
+void AudioPlayer::fillBuffer(void *data, size_t size) {
+    if (mNumFramesPlayed == 0) {
+        LOGI("AudioCallback");
+    }
+
+    size_t size_done = 0;
+    size_t size_remaining = size;
+    while (size_remaining > 0) {
+        MediaSource::ReadOptions options;
+
+        {
+            Mutex::Autolock autoLock(mLock);
+
+            if (mSeeking) {
+                options.setSeekTo(mSeekTimeUs);
+
+                if (mInputBuffer != NULL) {
+                    mInputBuffer->release();
+                    mInputBuffer = NULL;
+                }
+                mSeeking = false;
+            }
+        }
+
+        if (mInputBuffer == NULL) {
+            status_t err = mSource->read(&mInputBuffer, &options);
+
+            assert((err == OK && mInputBuffer != NULL)
+                   || (err != OK && mInputBuffer == NULL));
+
+            if (err != OK) {
+                memset((char *)data + size_done, 0, size_remaining);
+                break;
+            }
+
+            int32_t units, scale;
+            bool success =
+                mInputBuffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+            success = success &&
+                mInputBuffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+            assert(success);
+
+            Mutex::Autolock autoLock(mLock);
+            mPositionTimeMediaUs = (int64_t)units * 1000000 / scale;
+            mPositionTimeRealUs =
+                ((mNumFramesPlayed + size_done / 4) * 1000000) / mSampleRate; // XXX
+        }
+
+        if (mInputBuffer->range_length() == 0) {
+            mInputBuffer->release();
+            mInputBuffer = NULL;
+
+            continue;
+        }
+
+        size_t copy = size_remaining;
+        if (copy > mInputBuffer->range_length()) {
+            copy = mInputBuffer->range_length();
+        }
+
+        memcpy((char *)data + size_done,
+               (const char *)mInputBuffer->data() + mInputBuffer->range_offset(),
+               copy);
+
+        mInputBuffer->set_range(mInputBuffer->range_offset() + copy,
+                                mInputBuffer->range_length() - copy);
+                    
+        size_done += copy;
+        size_remaining -= copy;
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    mNumFramesPlayed += size / mFrameSize;
+}
+
+int64_t AudioPlayer::getRealTimeUs() {
+    Mutex::Autolock autoLock(mLock);
+    return getRealTimeUsLocked();
+}
+
+int64_t AudioPlayer::getRealTimeUsLocked() const {
+    return -mLatencyUs + (mNumFramesPlayed * 1000000) / mSampleRate;
+}
+
+int64_t AudioPlayer::getMediaTimeUs() {
+    Mutex::Autolock autoLock(mLock);
+
+    return mPositionTimeMediaUs + (getRealTimeUsLocked() - mPositionTimeRealUs);
+}
+
+bool AudioPlayer::getMediaTimeMapping(
+        int64_t *realtime_us, int64_t *mediatime_us) {
+    Mutex::Autolock autoLock(mLock);
+
+    *realtime_us = mPositionTimeRealUs;
+    *mediatime_us = mPositionTimeMediaUs;
+
+    return mPositionTimeRealUs != -1 || mPositionTimeMediaUs != -1;
+}
+
+status_t AudioPlayer::seekTo(int64_t time_us) {
+    Mutex::Autolock autoLock(mLock);
+
+    mSeeking = true;
+    mSeekTimeUs = time_us;
+
+    return OK;
+}
+
+}
diff --git a/media/libstagefright/CachingDataSource.cpp b/media/libstagefright/CachingDataSource.cpp
new file mode 100644
index 0000000..0fd71d5
--- /dev/null
+++ b/media/libstagefright/CachingDataSource.cpp
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2009 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 <media/stagefright/CachingDataSource.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <stdlib.h>
+#include <string.h>
+
+namespace android {
+
+CachingDataSource::CachingDataSource(
+        DataSource *source, size_t pageSize, int numPages)
+    : mSource(source),
+      mData(malloc(pageSize * numPages)),
+      mPageSize(pageSize),
+      mFirst(NULL),
+      mLast(NULL) {
+    for (int i = 0; i < numPages; ++i) {
+        Page *page = new Page;
+        page->mPrev = mLast;
+        page->mNext = NULL;
+
+        if (mLast == NULL) {
+            mFirst = page;
+        } else {
+            mLast->mNext = page;
+        }
+
+        mLast = page;
+
+        page->mOffset = -1;
+        page->mLength = 0;
+        page->mData = (char *)mData + mPageSize * i;
+    }
+}
+
+CachingDataSource::~CachingDataSource() {
+    Page *page = mFirst;
+    while (page != NULL) {
+        Page *next = page->mNext;
+        delete page;
+        page = next;
+    }
+    mFirst = mLast = NULL;
+
+    free(mData);
+    mData = NULL;
+
+    delete mSource;
+    mSource = NULL;
+}
+
+status_t CachingDataSource::InitCheck() const {
+    return OK;
+}
+
+ssize_t CachingDataSource::read_at(off_t offset, void *data, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    size_t total = 0;
+    while (size > 0) {
+        Page *page = mFirst;
+        while (page != NULL) {
+            if (page->mOffset >= 0 && offset >= page->mOffset
+                && offset < page->mOffset + page->mLength) {
+                break;
+            }
+            page = page->mNext;
+        }
+
+        if (page == NULL) {
+            page = allocate_page();
+            page->mOffset = offset - offset % mPageSize;
+            ssize_t n = mSource->read_at(page->mOffset, page->mData, mPageSize);
+            if (n < 0) {
+                page->mLength = 0;
+            } else {
+                page->mLength = (size_t)n;
+            }
+            mFirst->mPrev = page;
+            page->mNext = mFirst;
+            page->mPrev = NULL;
+            mFirst = page;
+
+            if (n < 0) {
+                return n;
+            }
+
+            if (offset >= page->mOffset + page->mLength) {
+                break;
+            }
+        } else {
+            // Move "page" to the front in LRU order.
+            if (page->mNext != NULL) {
+                page->mNext->mPrev = page->mPrev;
+            } else {
+                mLast = page->mPrev;
+            }
+
+            if (page->mPrev != NULL) {
+                page->mPrev->mNext = page->mNext;
+            } else {
+                mFirst = page->mNext;
+            }
+
+            mFirst->mPrev = page;
+            page->mNext = mFirst;
+            page->mPrev = NULL;
+            mFirst = page;
+        }
+
+        size_t copy = page->mLength - (offset - page->mOffset);
+        if (copy > size) {
+            copy = size;
+        }
+        memcpy(data,(const char *)page->mData + (offset - page->mOffset),
+               copy);
+
+        total += copy;
+
+        if (page->mLength < mPageSize) {
+            // This was the final page. There is no more data beyond it.
+            break;
+        }
+
+        offset += copy;
+        size -= copy;
+        data = (char *)data + copy;
+    }
+
+    return total;
+}
+
+CachingDataSource::Page *CachingDataSource::allocate_page() {
+    // The last page is the least recently used, i.e. oldest.
+
+    Page *page = mLast;
+
+    page->mPrev->mNext = NULL;
+    mLast = page->mPrev;
+    page->mPrev = NULL;
+
+    return page;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
new file mode 100644
index 0000000..ee12873
--- /dev/null
+++ b/media/libstagefright/CameraSource.cpp
@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2009 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 <sys/time.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <OMX_Component.h>
+
+#include <binder/IServiceManager.h>
+#include <media/stagefright/CameraSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <ui/ICameraClient.h>
+#include <ui/ICameraService.h>
+#include <ui/Overlay.h>
+#include <utils/String16.h>
+
+namespace android {
+
+class CameraBuffer : public MediaBuffer {
+public:
+    CameraBuffer(const sp<IMemory> &frame)
+        : MediaBuffer(frame->pointer(), frame->size()),
+          mFrame(frame) {
+    }
+
+    sp<IMemory> releaseFrame() {
+        sp<IMemory> frame = mFrame;
+        mFrame.clear();
+        return frame;
+    }
+
+private:
+    sp<IMemory> mFrame;
+};
+
+class CameraSourceClient : public BnCameraClient {
+public:
+    CameraSourceClient()
+        : mSource(NULL) {
+    }
+
+    virtual void notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+        assert(mSource != NULL);
+        mSource->notifyCallback(msgType, ext1, ext2);
+    }
+
+    virtual void dataCallback(int32_t msgType, const sp<IMemory> &data) {
+        assert(mSource != NULL);
+        mSource->dataCallback(msgType, data);
+    }
+
+    void setCameraSource(CameraSource *source) {
+        mSource = source;
+    }
+
+private:
+    CameraSource *mSource;
+};
+
+class DummySurface : public BnSurface {
+public:
+    DummySurface() {}
+
+    virtual status_t registerBuffers(const BufferHeap &buffers) {
+        return OK;
+    }
+
+    virtual void postBuffer(ssize_t offset) {
+    }
+
+    virtual void unregisterBuffers() {
+    }
+    
+    virtual sp<OverlayRef> createOverlay(
+            uint32_t w, uint32_t h, int32_t format) {
+        return NULL;
+    }
+};
+
+// static
+CameraSource *CameraSource::Create() {
+    sp<IServiceManager> sm = defaultServiceManager();
+
+    sp<ICameraService> service =
+        interface_cast<ICameraService>(
+                sm->getService(String16("media.camera")));
+
+    sp<CameraSourceClient> client = new CameraSourceClient;
+    sp<ICamera> camera = service->connect(client);
+
+    CameraSource *source = new CameraSource(camera, client);
+    client->setCameraSource(source);
+
+    return source;
+}
+
+CameraSource::CameraSource(
+        const sp<ICamera> &camera, const sp<ICameraClient> &client)
+    : mCamera(camera),
+      mCameraClient(client),
+      mNumFrames(0),
+      mStarted(false) {
+    printf("params: \"%s\"\n", mCamera->getParameters().string());
+}
+
+CameraSource::~CameraSource() {
+    if (mStarted) {
+        stop();
+    }
+
+    mCamera->disconnect();
+}
+
+status_t CameraSource::start(MetaData *) {
+    assert(!mStarted);
+
+    status_t err = mCamera->lock();
+    assert(err == OK);
+
+    err = mCamera->setPreviewDisplay(new DummySurface);
+    assert(err == OK);
+    mCamera->setPreviewCallbackFlag(1);
+    mCamera->startPreview();
+    assert(err == OK);
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t CameraSource::stop() {
+    assert(mStarted);
+
+    mCamera->stopPreview();
+    mCamera->unlock();
+
+    mStarted = false;
+
+    return OK;
+}
+
+sp<MetaData> CameraSource::getFormat() {
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, "video/raw");
+    meta->setInt32(kKeyColorFormat, OMX_COLOR_FormatYUV420SemiPlanar);
+    meta->setInt32(kKeyWidth, 480);
+    meta->setInt32(kKeyHeight, 320);
+
+    return meta;
+}
+
+status_t CameraSource::read(
+        MediaBuffer **buffer, const ReadOptions *options) {
+    assert(mStarted);
+
+    *buffer = NULL;
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    sp<IMemory> frame;
+
+    {
+        Mutex::Autolock autoLock(mLock);
+        while (mFrames.empty()) {
+            mFrameAvailableCondition.wait(mLock);
+        }
+
+        frame = *mFrames.begin();
+        mFrames.erase(mFrames.begin());
+    }
+
+    int count = mNumFrames++;
+
+    *buffer = new CameraBuffer(frame);
+
+    (*buffer)->meta_data()->clear();
+    (*buffer)->meta_data()->setInt32(kKeyTimeScale, 15);
+    (*buffer)->meta_data()->setInt32(kKeyTimeUnits, count);
+
+    (*buffer)->add_ref();
+    (*buffer)->setObserver(this);
+
+    return OK;
+}
+
+void CameraSource::notifyCallback(int32_t msgType, int32_t ext1, int32_t ext2) {
+    printf("notifyCallback %d, %d, %d\n", msgType, ext1, ext2);
+}
+
+void CameraSource::dataCallback(int32_t msgType, const sp<IMemory> &data) {
+    Mutex::Autolock autoLock(mLock);
+
+    mFrames.push_back(data);
+    mFrameAvailableCondition.signal();
+}
+
+void CameraSource::signalBufferReturned(MediaBuffer *_buffer) {
+    CameraBuffer *buffer = static_cast<CameraBuffer *>(_buffer);
+
+    mCamera->releaseRecordingFrame(buffer->releaseFrame());
+
+    buffer->setObserver(NULL);
+    buffer->release();
+    buffer = NULL;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
new file mode 100644
index 0000000..6e6b43d
--- /dev/null
+++ b/media/libstagefright/DataSource.cpp
@@ -0,0 +1,76 @@
+/*
+ * Copyright (C) 2009 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 <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+status_t DataSource::getSize(off_t *size) {
+    *size = 0;
+
+    return ERROR_UNSUPPORTED;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+Mutex DataSource::gSnifferMutex;
+List<DataSource::SnifferFunc> DataSource::gSniffers;
+
+bool DataSource::sniff(String8 *mimeType, float *confidence) {
+    *mimeType = "";
+    *confidence = 0.0f;
+
+    Mutex::Autolock autoLock(gSnifferMutex);
+    for (List<SnifferFunc>::iterator it = gSniffers.begin();
+         it != gSniffers.end(); ++it) {
+        String8 newMimeType;
+        float newConfidence;
+        if ((*it)(this, &newMimeType, &newConfidence)) {
+            if (newConfidence > *confidence) {
+                *mimeType = newMimeType;
+                *confidence = newConfidence;
+            }
+        }
+    }
+
+    return *confidence > 0.0;
+}
+
+// static
+void DataSource::RegisterSniffer(SnifferFunc func) {
+    Mutex::Autolock autoLock(gSnifferMutex);
+
+    for (List<SnifferFunc>::iterator it = gSniffers.begin();
+         it != gSniffers.end(); ++it) {
+        if (*it == func) {
+            return;
+        }
+    }
+
+    gSniffers.push_back(func);
+}
+
+// static
+void DataSource::RegisterDefaultSniffers() {
+    RegisterSniffer(SniffMP3);
+    RegisterSniffer(SniffMPEG4);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/ESDS.cpp b/media/libstagefright/ESDS.cpp
new file mode 100644
index 0000000..53b92a0
--- /dev/null
+++ b/media/libstagefright/ESDS.cpp
@@ -0,0 +1,196 @@
+/*
+ * Copyright (C) 2009 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 <media/stagefright/ESDS.h>
+
+#include <string.h>
+
+namespace android {
+
+ESDS::ESDS(const void *data, size_t size)
+    : mData(new uint8_t[size]),
+      mSize(size),
+      mInitCheck(NO_INIT),
+      mDecoderSpecificOffset(0),
+      mDecoderSpecificLength(0) {
+    memcpy(mData, data, size);
+
+    mInitCheck = parse();
+}
+
+ESDS::~ESDS() {
+    delete[] mData;
+    mData = NULL;
+}
+
+status_t ESDS::InitCheck() const {
+    return mInitCheck;
+}
+
+status_t ESDS::getCodecSpecificInfo(const void **data, size_t *size) const {
+    if (mInitCheck != OK) {
+        return mInitCheck;
+    }
+
+    *data = &mData[mDecoderSpecificOffset];
+    *size = mDecoderSpecificLength;
+
+    return OK;
+}
+
+status_t ESDS::skipDescriptorHeader(
+        size_t offset, size_t size,
+        uint8_t *tag, size_t *data_offset, size_t *data_size) const {
+    if (size == 0) {
+        return ERROR_MALFORMED;
+    }
+
+    *tag = mData[offset++];
+    --size;
+
+    *data_size = 0;
+    bool more;
+    do {
+        if (size == 0) {
+            return ERROR_MALFORMED;
+        }
+
+        uint8_t x = mData[offset++];
+        --size;
+
+        *data_size = (*data_size << 7) | (x & 0x7f);
+        more = (x & 0x80) != 0;
+    }
+    while (more);
+
+    if (*data_size > size) {
+        return ERROR_MALFORMED;
+    }
+
+    *data_offset = offset;
+
+    return OK;
+}
+
+status_t ESDS::parse() {
+    uint8_t tag;
+    size_t data_offset;
+    size_t data_size;
+    status_t err =
+        skipDescriptorHeader(0, mSize, &tag, &data_offset, &data_size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (tag != kTag_ESDescriptor) {
+        return ERROR_MALFORMED;
+    }
+
+    return parseESDescriptor(data_offset, data_size);
+}
+
+status_t ESDS::parseESDescriptor(size_t offset, size_t size) {
+    if (size < 3) {
+        return ERROR_MALFORMED;
+    }
+
+    offset += 2;  // skip ES_ID
+    size -= 2;
+
+    unsigned streamDependenceFlag = mData[offset] & 0x80;
+    unsigned URL_Flag = mData[offset] & 0x40;
+    unsigned OCRstreamFlag = mData[offset] & 0x20;
+
+    ++offset;
+    --size;
+
+    if (streamDependenceFlag) {
+        offset += 2;
+        size -= 2;
+    }
+
+    if (URL_Flag) {
+        if (offset >= size) {
+            return ERROR_MALFORMED;
+        }
+        unsigned URLlength = mData[offset];
+        offset += URLlength + 1;
+        size -= URLlength + 1;
+    }
+
+    if (OCRstreamFlag) {
+        offset += 2;
+        size -= 2;
+    }
+    
+    if (offset >= size) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t tag;
+    size_t sub_offset, sub_size;
+    status_t err = skipDescriptorHeader(
+            offset, size, &tag, &sub_offset, &sub_size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (tag != kTag_DecoderConfigDescriptor) {
+        return ERROR_MALFORMED;
+    }
+
+    err = parseDecoderConfigDescriptor(sub_offset, sub_size);
+
+    return err;
+}
+
+status_t ESDS::parseDecoderConfigDescriptor(size_t offset, size_t size) {
+    if (size < 13) {
+        return ERROR_MALFORMED;
+    }
+
+    offset += 13;
+    size -= 13;
+
+    if (size == 0) {
+        mDecoderSpecificOffset = 0;
+        mDecoderSpecificLength = 0;
+        return OK;
+    }
+
+    uint8_t tag;
+    size_t sub_offset, sub_size;
+    status_t err = skipDescriptorHeader(
+            offset, size, &tag, &sub_offset, &sub_size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    if (tag != kTag_DecoderSpecificInfo) {
+        return ERROR_MALFORMED;
+    }
+
+    mDecoderSpecificOffset = sub_offset;
+    mDecoderSpecificLength = sub_size;
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/FileSource.cpp b/media/libstagefright/FileSource.cpp
new file mode 100644
index 0000000..c26d0a0
--- /dev/null
+++ b/media/libstagefright/FileSource.cpp
@@ -0,0 +1,50 @@
+/*
+ * Copyright (C) 2009 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 <media/stagefright/FileSource.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+namespace android {
+
+FileSource::FileSource(const char *filename)
+    : mFile(fopen(filename, "rb")) {
+}
+
+FileSource::~FileSource() {
+    if (mFile != NULL) {
+        fclose(mFile);
+        mFile = NULL;
+    }
+}
+
+status_t FileSource::InitCheck() const {
+    return mFile != NULL ? OK : NO_INIT;
+}
+
+ssize_t FileSource::read_at(off_t offset, void *data, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    int err = fseeko(mFile, offset, SEEK_SET);
+    assert(err != -1);
+
+    ssize_t result = fread(data, 1, size, mFile);
+
+    return result;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/HTTPDataSource.cpp b/media/libstagefright/HTTPDataSource.cpp
new file mode 100644
index 0000000..d1f8cd4
--- /dev/null
+++ b/media/libstagefright/HTTPDataSource.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <stdlib.h>
+
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/string.h>
+
+namespace android {
+
+HTTPDataSource::HTTPDataSource(const char *uri)
+    : mHost(NULL),
+      mPort(0),
+      mPath(NULL),
+      mBuffer(malloc(kBufferSize)),
+      mBufferLength(0),
+      mBufferOffset(0) {
+    assert(!strncasecmp("http://", uri, 7));
+
+    string host;
+    string path;
+    int port;
+
+    char *slash = strchr(uri + 7, '/');
+    if (slash == NULL) {
+        host = uri + 7;
+        path = "/";
+    } else {
+        host = string(uri + 7, slash - (uri + 7));
+        path = slash;
+    }
+
+    char *colon = strchr(host.c_str(), ':');
+    if (colon == NULL) {
+        port = 80;
+    } else {
+        char *end;
+        long tmp = strtol(colon + 1, &end, 10);
+        assert(end > colon + 1);
+        assert(tmp > 0 && tmp < 65536);
+        port = tmp;
+
+        host = string(host, 0, colon - host.c_str());
+    }
+
+    LOGI("Connecting to host '%s', port %d, path '%s'",
+         host.c_str(), port, path.c_str());
+
+    mHost = strdup(host.c_str());
+    mPort = port;
+    mPath = strdup(path.c_str());
+
+    status_t err = mHttp.connect(mHost, mPort);
+    assert(err == OK);
+}
+
+HTTPDataSource::HTTPDataSource(const char *host, int port, const char *path)
+    : mHost(strdup(host)),
+      mPort(port),
+      mPath(strdup(path)),
+      mBuffer(malloc(kBufferSize)),
+      mBufferLength(0),
+      mBufferOffset(0) {
+    status_t err = mHttp.connect(mHost, mPort);
+    assert(err == OK);
+}
+
+HTTPDataSource::~HTTPDataSource() {
+    mHttp.disconnect();
+
+    free(mBuffer);
+    mBuffer = NULL;
+
+    free(mPath);
+    mPath = NULL;
+}
+
+ssize_t HTTPDataSource::read_at(off_t offset, void *data, size_t size) {
+    if (offset >= mBufferOffset && offset < mBufferOffset + mBufferLength) {
+        size_t num_bytes_available = mBufferLength - (offset - mBufferOffset);
+
+        size_t copy = num_bytes_available;
+        if (copy > size) {
+            copy = size;
+        }
+
+        memcpy(data, (const char *)mBuffer + (offset - mBufferOffset), copy);
+
+        return copy;
+    }
+
+    mBufferOffset = offset;
+    mBufferLength = 0;
+
+    char host[128];
+    sprintf(host, "Host: %s\r\n", mHost);
+
+    char range[128];
+    sprintf(range, "Range: bytes=%ld-%ld\r\n\r\n",
+            mBufferOffset, mBufferOffset + kBufferSize - 1);
+
+    int http_status;
+
+    status_t err;
+    int attempt = 1;
+    for (;;) {
+        if ((err = mHttp.send("GET ")) != OK
+            || (err = mHttp.send(mPath)) != OK
+            || (err = mHttp.send(" HTTP/1.1\r\n")) != OK
+            || (err = mHttp.send(host)) != OK
+            || (err = mHttp.send(range)) != OK
+            || (err = mHttp.send("\r\n")) != OK
+            || (err = mHttp.receive_header(&http_status)) != OK) {
+
+            if (attempt == 3) {
+                return err;
+            }
+
+            mHttp.connect(mHost, mPort);
+            ++attempt;
+        } else {
+            break;
+        }
+    }
+
+    if ((http_status / 100) != 2) {
+        return UNKNOWN_ERROR;
+    }
+
+    string value;
+    if (!mHttp.find_header_value("Content-Length", &value)) {
+        return UNKNOWN_ERROR;
+    }
+
+    char *end;
+    unsigned long contentLength = strtoul(value.c_str(), &end, 10);
+
+    ssize_t num_bytes_received = mHttp.receive(mBuffer, contentLength);
+
+    if (num_bytes_received <= 0) {
+        return num_bytes_received;
+    }
+
+    mBufferLength = (size_t)num_bytes_received;
+
+    size_t copy = mBufferLength;
+    if (copy > size) {
+        copy = size;
+    }
+
+    memcpy(data, mBuffer, copy);
+
+    return copy;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/HTTPStream.cpp b/media/libstagefright/HTTPStream.cpp
new file mode 100644
index 0000000..29e6f72
--- /dev/null
+++ b/media/libstagefright/HTTPStream.cpp
@@ -0,0 +1,285 @@
+/*
+ * Copyright (C) 2009 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 <sys/socket.h>
+
+#include <arpa/inet.h>
+#include <assert.h>
+#include <ctype.h>
+#include <errno.h>
+#include <netdb.h>
+#include <stdio.h>
+#include <stdlib.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <media/stagefright/HTTPStream.h>
+
+namespace android {
+
+// static
+const char *HTTPStream::kStatusKey = ":status:";
+
+HTTPStream::HTTPStream()
+    : mState(READY),
+      mSocket(-1) {
+}
+
+HTTPStream::~HTTPStream() {
+    disconnect();
+}
+
+status_t HTTPStream::connect(const char *server, int port) {
+    status_t err = OK;
+
+    if (mState == CONNECTED) {
+        return ERROR_ALREADY_CONNECTED;
+    }
+
+    assert(mSocket == -1);
+    mSocket = socket(AF_INET, SOCK_STREAM, 0);
+    
+    if (mSocket < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    struct hostent *ent = gethostbyname(server);
+    if (ent == NULL) {
+        err = ERROR_UNKNOWN_HOST;
+        goto exit1;
+    }
+
+    struct sockaddr_in addr;
+    addr.sin_family = AF_INET;
+    addr.sin_port = htons(port);
+    addr.sin_addr.s_addr = *(in_addr_t *)ent->h_addr;
+    memset(addr.sin_zero, 0, sizeof(addr.sin_zero));
+
+    if (::connect(mSocket, (const struct sockaddr *)&addr, sizeof(addr)) < 0) {
+        err = ERROR_CANNOT_CONNECT;
+        goto exit1;
+    }
+
+    mState = CONNECTED;
+
+    return OK;
+
+exit1:
+    close(mSocket);
+    mSocket = -1;
+
+    return err;
+}
+
+status_t HTTPStream::disconnect() {
+    if (mState != CONNECTED) {
+        return ERROR_NOT_CONNECTED;
+    }
+
+    assert(mSocket >= 0);
+    close(mSocket);
+    mSocket = -1;
+
+    mState = READY;
+
+    return OK;
+}
+
+status_t HTTPStream::send(const char *data, size_t size) {
+    if (mState != CONNECTED) {
+        return ERROR_NOT_CONNECTED;
+    }
+
+    while (size > 0) {
+        ssize_t n = ::send(mSocket, data, size, 0);
+
+        if (n < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            disconnect();
+
+            return ERROR_IO;
+        } else if (n == 0) {
+            disconnect();
+
+            return ERROR_CONNECTION_LOST;
+        }
+
+        size -= (size_t)n;
+        data += (size_t)n;
+    }
+
+    return OK;
+}
+
+status_t HTTPStream::send(const char *data) {
+    return send(data, strlen(data));
+}
+
+status_t HTTPStream::receive_line(char *line, size_t size) {
+    if (mState != CONNECTED) {
+        return ERROR_NOT_CONNECTED;
+    }
+
+    bool saw_CR = false;
+    size_t length = 0;
+
+    for (;;) {
+        char c;
+        ssize_t n = recv(mSocket, &c, 1, 0);
+        if (n < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            disconnect();
+
+            return ERROR_IO;
+        } else if (n == 0) {
+            disconnect();
+
+            return ERROR_CONNECTION_LOST;
+        }
+
+        if (saw_CR && c == '\n') {
+            // We have a complete line.
+
+            line[length - 1] = '\0';
+            return OK;
+        }
+
+        saw_CR = (c == '\r');
+
+        assert(length + 1 < size);
+        line[length++] = c;
+    }
+}
+
+status_t HTTPStream::receive_header(int *http_status) {
+    *http_status = -1;
+    mHeaders.clear();
+
+    char line[256];
+    status_t err = receive_line(line, sizeof(line));
+    if (err != OK) {
+        return err;
+    }
+
+    mHeaders.add(string(kStatusKey), string(line));
+
+    char *spacePos = strchr(line, ' ');
+    if (spacePos == NULL) {
+        // Malformed response?
+        return UNKNOWN_ERROR;
+    }
+
+    char *status_start = spacePos + 1;
+    char *status_end = status_start;
+    while (isdigit(*status_end)) {
+        ++status_end;
+    }
+
+    if (status_end == status_start) {
+        // Malformed response, status missing?
+        return UNKNOWN_ERROR;
+    }
+
+    memmove(line, status_start, status_end - status_start);
+    line[status_end - status_start] = '\0';
+
+    long tmp = strtol(line, NULL, 10);
+    if (tmp < 0 || tmp > 999) {
+        return UNKNOWN_ERROR;
+    }
+
+    *http_status = (int)tmp;
+
+    for (;;) {
+        err = receive_line(line, sizeof(line));
+        if (err != OK) {
+            return err;
+        }
+
+        if (*line == '\0') {
+            // Empty line signals the end of the header.
+            break;
+        }
+
+        // puts(line);
+
+        char *colonPos = strchr(line, ':');
+        if (colonPos == NULL) {
+            mHeaders.add(string(line), string());
+        } else {
+            char *end_of_key = colonPos;
+            while (end_of_key > line && isspace(end_of_key[-1])) {
+                --end_of_key;
+            }
+
+            char *start_of_value = colonPos + 1;
+            while (isspace(*start_of_value)) {
+                ++start_of_value;
+            }
+
+            *end_of_key = '\0';
+
+            mHeaders.add(string(line), string(start_of_value));
+        }
+    }
+
+    return OK;
+}
+
+ssize_t HTTPStream::receive(void *data, size_t size) {
+    size_t total = 0;
+    while (total < size) {
+        ssize_t n = recv(mSocket, (char *)data + total, size - total, 0);
+
+        if (n < 0) {
+            if (errno == EINTR) {
+                continue;
+            }
+
+            disconnect();
+            return ERROR_IO;
+        } else if (n == 0) {
+            disconnect();
+
+            return ERROR_CONNECTION_LOST;
+        }
+
+        total += (size_t)n;
+    }
+
+    return (ssize_t)total;
+}
+
+bool HTTPStream::find_header_value(const string &key, string *value) const {
+    ssize_t index = mHeaders.indexOfKey(key);
+    if (index < 0) {
+        value->clear();
+        return false;
+    }
+
+    *value = mHeaders.valueAt(index);
+
+    return true;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MP3Extractor.cpp b/media/libstagefright/MP3Extractor.cpp
new file mode 100644
index 0000000..74f37b1
--- /dev/null
+++ b/media/libstagefright/MP3Extractor.cpp
@@ -0,0 +1,522 @@
+/*
+ * Copyright (C) 2009 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 "MP3Extractor"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+static bool get_mp3_frame_size(
+        uint32_t header, size_t *frame_size,
+        int *out_sampling_rate = NULL, int *out_channels = NULL,
+        int *out_bitrate = NULL) {
+    *frame_size = 0;
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = 0;
+    }
+
+    if (out_channels) {
+        *out_channels = 0;
+    }
+
+    if (out_bitrate) {
+        *out_bitrate = 0;
+    }
+
+    if ((header & 0xffe00000) != 0xffe00000) {
+        return false;
+    }
+
+    unsigned version = (header >> 19) & 3;
+
+    if (version == 0x01) {
+        return false;
+    }
+    
+    unsigned layer = (header >> 17) & 3;
+
+    if (layer == 0x00) {
+        return false;
+    }
+
+    unsigned protection = (header >> 16) & 1;
+
+    unsigned bitrate_index = (header >> 12) & 0x0f;
+
+    if (bitrate_index == 0 || bitrate_index == 0x0f) {
+        // Disallow "free" bitrate.
+        return false;
+    }
+
+    unsigned sampling_rate_index = (header >> 10) & 3;
+
+    if (sampling_rate_index == 3) {
+        return false;
+    }
+
+    static const int kSamplingRateV1[] = { 44100, 48000, 32000 };
+    int sampling_rate = kSamplingRateV1[sampling_rate_index];
+    if (version == 2 /* V2 */) {
+        sampling_rate /= 2;
+    } else if (version == 0 /* V2.5 */) {
+        sampling_rate /= 4;
+    }
+
+    unsigned padding = (header >> 9) & 1;
+
+    if (layer == 3) {
+        // layer I
+
+        static const int kBitrateV1[] = {
+            32, 64, 96, 128, 160, 192, 224, 256,
+            288, 320, 352, 384, 416, 448
+        };
+
+        static const int kBitrateV2[] = {
+            32, 48, 56, 64, 80, 96, 112, 128,
+            144, 160, 176, 192, 224, 256
+        };
+
+        int bitrate =
+            (version == 3 /* V1 */)
+                ? kBitrateV1[bitrate_index - 1]
+                : kBitrateV2[bitrate_index - 1];
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        *frame_size = (12000 * bitrate / sampling_rate + padding) * 4;
+    } else {
+        // layer II or III
+
+        static const int kBitrateV1L2[] = {
+            32, 48, 56, 64, 80, 96, 112, 128,
+            160, 192, 224, 256, 320, 384
+        };
+
+        static const int kBitrateV1L3[] = {
+            32, 40, 48, 56, 64, 80, 96, 112,
+            128, 160, 192, 224, 256, 320
+        };
+
+        static const int kBitrateV2[] = {
+            8, 16, 24, 32, 40, 48, 56, 64,
+            80, 96, 112, 128, 144, 160
+        };
+
+        int bitrate;
+        if (version == 3 /* V1 */) {
+            bitrate = (layer == 2 /* L2 */)
+                ? kBitrateV1L2[bitrate_index - 1]
+                : kBitrateV1L3[bitrate_index - 1];
+        } else {
+            // V2 (or 2.5)
+
+            bitrate = kBitrateV2[bitrate_index - 1];
+        }
+
+        if (out_bitrate) {
+            *out_bitrate = bitrate;
+        }
+
+        *frame_size = 144000 * bitrate / sampling_rate + padding;
+    }
+
+    if (out_sampling_rate) {
+        *out_sampling_rate = sampling_rate;
+    }
+
+    if (out_channels) {
+        int channel_mode = (header >> 6) & 3;
+
+        *out_channels = (channel_mode == 3) ? 1 : 2;
+    }
+
+    return true;
+}
+
+static bool Resync(
+        DataSource *source, uint32_t match_header,
+        off_t *inout_pos, uint32_t *out_header) {
+    // Everything must match except for
+    // protection, bitrate, padding, private bits and mode extension.
+    const uint32_t kMask = 0xfffe0ccf;
+
+    const size_t kMaxFrameSize = 4096;
+    uint8_t *buffer = new uint8_t[kMaxFrameSize];
+    
+    off_t pos = *inout_pos;
+    size_t buffer_offset = kMaxFrameSize;
+    size_t buffer_length = kMaxFrameSize;
+    bool valid = false;
+    do {
+        if (buffer_offset + 3 >= buffer_length) {
+            if (buffer_length < kMaxFrameSize) {
+                break;
+            }
+
+            pos += buffer_length;
+
+            if (pos >= *inout_pos + 128 * 1024) {
+                // Don't scan forever.
+                LOGV("giving up at offset %ld", pos);
+                break;
+            }
+
+            memmove(buffer, &buffer[buffer_offset], buffer_length - buffer_offset);
+            buffer_length = buffer_length - buffer_offset;
+            buffer_offset = 0;
+
+            ssize_t n = source->read_at(
+                    pos, &buffer[buffer_length], kMaxFrameSize - buffer_length);
+
+            if (n <= 0) {
+                break;
+            }
+
+            buffer_length += (size_t)n;
+
+            continue;
+        }
+
+        uint32_t header = U32_AT(&buffer[buffer_offset]);
+
+        if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
+            ++buffer_offset;
+            continue;
+        }
+
+        size_t frame_size;
+        int sample_rate, num_channels, bitrate;
+        if (get_mp3_frame_size(header, &frame_size,
+                               &sample_rate, &num_channels, &bitrate)) {
+            LOGV("found possible 1st frame at %ld", pos + buffer_offset);
+
+            // We found what looks like a valid frame,
+            // now find its successors.
+
+            off_t test_pos = pos + buffer_offset + frame_size;
+
+            valid = true;
+            for (int j = 0; j < 3; ++j) {
+                uint8_t tmp[4];
+                if (source->read_at(test_pos, tmp, 4) < 4) {
+                    valid = false;
+                    break;
+                }
+                
+                uint32_t test_header = U32_AT(tmp);
+
+                LOGV("subsequent header is %08x", test_header);
+
+                if ((test_header & kMask) != (header & kMask)) {
+                    valid = false;
+                    break;
+                }
+
+                size_t test_frame_size;
+                if (!get_mp3_frame_size(test_header, &test_frame_size)) {
+                    valid = false;
+                    break;
+                }
+
+                LOGV("found subsequent frame #%d at %ld", j + 2, test_pos);
+
+                test_pos += test_frame_size;
+            }
+        }
+
+        if (valid) {
+            *inout_pos = pos + buffer_offset;
+
+            if (out_header != NULL) {
+                *out_header = header;
+            }
+        } else {
+            LOGV("no dice, no valid sequence of frames found.");
+        }
+
+        ++buffer_offset;
+
+    } while (!valid);
+
+    delete[] buffer;
+    buffer = NULL;
+
+    return valid;
+}
+
+class MP3Source : public MediaSource {
+public:
+    MP3Source(
+            const sp<MetaData> &meta, DataSource *source,
+            off_t first_frame_pos, uint32_t fixed_header);
+
+    virtual ~MP3Source();
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+    sp<MetaData> mMeta;
+    DataSource *mDataSource;
+    off_t mFirstFramePos;
+    uint32_t mFixedHeader;
+    off_t mCurrentPos;
+    int64_t mCurrentTimeUs;
+    bool mStarted;
+
+    MediaBufferGroup *mGroup;
+
+    MP3Source(const MP3Source &);
+    MP3Source &operator=(const MP3Source &);
+};
+
+MP3Extractor::MP3Extractor(DataSource *source)
+    : mDataSource(source),
+      mFirstFramePos(-1),
+      mFixedHeader(0) {
+    off_t pos = 0;
+    uint32_t header;
+    bool success = Resync(mDataSource, 0, &pos, &header);
+    assert(success);
+
+    if (success) {
+        mFirstFramePos = pos;
+        mFixedHeader = header;
+
+        size_t frame_size;
+        int sample_rate;
+        int num_channels;
+        int bitrate;
+        get_mp3_frame_size(
+                header, &frame_size, &sample_rate, &num_channels, &bitrate);
+
+        mMeta = new MetaData;
+
+        mMeta->setCString(kKeyMIMEType, "audio/mpeg");
+        mMeta->setInt32(kKeySampleRate, sample_rate);
+        mMeta->setInt32(kKeyBitRate, bitrate);
+        mMeta->setInt32(kKeyChannelCount, num_channels);
+
+        off_t fileSize;
+        if (mDataSource->getSize(&fileSize) == OK) {
+            mMeta->setInt32(
+                    kKeyDuration,
+                    8 * (fileSize - mFirstFramePos) / bitrate);
+            mMeta->setInt32(kKeyTimeScale, 1000);
+        }
+    }
+}
+
+MP3Extractor::~MP3Extractor() {
+    delete mDataSource;
+    mDataSource = NULL;
+}
+
+status_t MP3Extractor::countTracks(int *num_tracks) {
+    *num_tracks = mFirstFramePos < 0 ? 0 : 1;
+
+    return OK;
+}
+
+status_t MP3Extractor::getTrack(int index, MediaSource **source) {
+    if (mFirstFramePos < 0 || index != 0) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    *source = new MP3Source(
+            mMeta, mDataSource, mFirstFramePos, mFixedHeader);
+
+    return OK;
+}
+
+sp<MetaData> MP3Extractor::getTrackMetaData(int index) {
+    if (mFirstFramePos < 0 || index != 0) {
+        return NULL;
+    }
+
+    return mMeta;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MP3Source::MP3Source(
+        const sp<MetaData> &meta, DataSource *source,
+        off_t first_frame_pos, uint32_t fixed_header)
+    : mMeta(meta),
+      mDataSource(source),
+      mFirstFramePos(first_frame_pos),
+      mFixedHeader(fixed_header),
+      mCurrentPos(0),
+      mCurrentTimeUs(0),
+      mStarted(false),
+      mGroup(NULL) {
+}
+
+MP3Source::~MP3Source() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t MP3Source::start(MetaData *) {
+    assert(!mStarted);
+
+    mGroup = new MediaBufferGroup;
+
+    const size_t kMaxFrameSize = 32768;
+    mGroup->add_buffer(new MediaBuffer(kMaxFrameSize));
+
+    mCurrentPos = mFirstFramePos;
+    mCurrentTimeUs = 0;
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t MP3Source::stop() {
+    assert(mStarted);
+
+    delete mGroup;
+    mGroup = NULL;
+
+    mStarted = false;
+
+    return OK;
+}
+
+sp<MetaData> MP3Source::getFormat() {
+    return mMeta;
+}
+
+status_t MP3Source::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    *out = NULL;
+
+    int64_t seekTimeUs;
+    if (options != NULL && options->getSeekTo(&seekTimeUs)) {
+        int32_t bitrate;
+        if (!mMeta->findInt32(kKeyBitRate, &bitrate)) {
+            // bitrate is in kbits/sec.
+            LOGI("no bitrate");
+
+            return ERROR_UNSUPPORTED;
+        }
+
+        mCurrentTimeUs = seekTimeUs;
+        mCurrentPos = mFirstFramePos + seekTimeUs * bitrate / 1000000 * 125;
+    }
+
+    MediaBuffer *buffer;
+    status_t err = mGroup->acquire_buffer(&buffer);
+    if (err != OK) {
+        return err;
+    }
+
+    size_t frame_size;
+    for (;;) {
+        ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), 4);
+        if (n < 4) {
+            buffer->release();
+            buffer = NULL;
+
+            return ERROR_END_OF_STREAM;
+        }
+
+        uint32_t header = U32_AT((const uint8_t *)buffer->data());
+        
+        if (get_mp3_frame_size(header, &frame_size)) {
+            break;
+        }
+
+        // Lost sync.
+        LOGW("lost sync!\n");
+
+        off_t pos = mCurrentPos;
+        if (!Resync(mDataSource, mFixedHeader, &pos, NULL)) {
+            LOGE("Unable to resync. Signalling end of stream.");
+
+            buffer->release();
+            buffer = NULL;
+
+            return ERROR_END_OF_STREAM;
+        }
+
+        mCurrentPos = pos;
+
+        // Try again with the new position.
+    }
+
+    assert(frame_size <= buffer->size());
+
+    ssize_t n = mDataSource->read_at(mCurrentPos, buffer->data(), frame_size);
+    if (n < (ssize_t)frame_size) {
+        buffer->release();
+        buffer = NULL;
+
+        return ERROR_END_OF_STREAM;
+    }
+
+    buffer->set_range(0, frame_size);
+
+    buffer->meta_data()->setInt32(kKeyTimeUnits, mCurrentTimeUs / 1000);
+    buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+
+    mCurrentPos += frame_size;
+    mCurrentTimeUs += 1152 * 1000000 / 44100;
+
+    *out = buffer;
+
+    return OK;
+}
+
+bool SniffMP3(DataSource *source, String8 *mimeType, float *confidence) {
+    off_t pos = 0;
+    uint32_t header;
+    if (!Resync(source, 0, &pos, &header)) {
+        return false;
+    }
+
+    *mimeType = "audio/mpeg";
+    *confidence = 0.3f;
+
+    return true;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
new file mode 100644
index 0000000..caaec06
--- /dev/null
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -0,0 +1,937 @@
+/*
+ * Copyright (C) 2009 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_TAG "MPEG4Extractor"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <ctype.h>
+#include <stdint.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/SampleTable.h>
+#include <media/stagefright/Utils.h>
+#include <utils/String8.h>
+
+namespace android {
+
+class MPEG4Source : public MediaSource {
+public:
+    // Caller retains ownership of both "dataSource" and "sampleTable".
+    MPEG4Source(const sp<MetaData> &format, DataSource *dataSource,
+                SampleTable *sampleTable);
+
+    virtual ~MPEG4Source();
+
+    virtual status_t start(MetaData *params = NULL);
+    virtual status_t stop();
+
+    virtual sp<MetaData> getFormat();
+
+    virtual status_t read(
+            MediaBuffer **buffer, const ReadOptions *options = NULL);
+
+private:
+    sp<MetaData> mFormat;
+    DataSource *mDataSource;
+    int32_t mTimescale;
+    SampleTable *mSampleTable;
+    uint32_t mCurrentSampleIndex;
+
+    bool mIsAVC;
+    bool mStarted;
+
+    MediaBufferGroup *mGroup;
+
+    MediaBuffer *mBuffer;
+    size_t mBufferOffset;
+    size_t mBufferSizeRemaining;
+
+    bool mNeedsNALFraming;
+
+    MPEG4Source(const MPEG4Source &);
+    MPEG4Source &operator=(const MPEG4Source &);
+};
+
+static void hexdump(const void *_data, size_t size) {
+    const uint8_t *data = (const uint8_t *)_data;
+    size_t offset = 0;
+    while (offset < size) {
+        printf("0x%04x  ", offset);
+
+        size_t n = size - offset;
+        if (n > 16) {
+            n = 16;
+        }
+
+        for (size_t i = 0; i < 16; ++i) {
+            if (i == 8) {
+                printf(" ");
+            }
+
+            if (offset + i < size) {
+                printf("%02x ", data[offset + i]);
+            } else {
+                printf("   ");
+            }
+        }
+
+        printf(" ");
+
+        for (size_t i = 0; i < n; ++i) {
+            if (isprint(data[offset + i])) {
+                printf("%c", data[offset + i]);
+            } else {
+                printf(".");
+            }
+        }
+
+        printf("\n");
+
+        offset += 16;
+    }
+}
+
+static const char *const FourCC2MIME(uint32_t fourcc) {
+    switch (fourcc) {
+        case FOURCC('m', 'p', '4', 'a'):
+            return "audio/mp4a-latm";
+
+        case FOURCC('s', 'a', 'm', 'r'):
+            return "audio/3gpp";
+
+        case FOURCC('m', 'p', '4', 'v'):
+            return "video/mp4v-es";
+
+        case FOURCC('s', '2', '6', '3'):
+            return "video/3gpp";
+
+        case FOURCC('a', 'v', 'c', '1'):
+            return "video/avc";
+
+        default:
+            assert(!"should not be here.");
+            return NULL;
+    }
+}
+
+MPEG4Extractor::MPEG4Extractor(DataSource *source)
+    : mDataSource(source),
+      mHaveMetadata(false),
+      mFirstTrack(NULL),
+      mLastTrack(NULL) {
+}
+
+MPEG4Extractor::~MPEG4Extractor() {
+    Track *track = mFirstTrack;
+    while (track) {
+        Track *next = track->next;
+
+        delete track->sampleTable;
+        track->sampleTable = NULL;
+
+        delete track;
+        track = next;
+    }
+    mFirstTrack = mLastTrack = NULL;
+
+    delete mDataSource;
+    mDataSource = NULL;
+}
+
+status_t MPEG4Extractor::countTracks(int *num_tracks) {
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        return err;
+    }
+
+    *num_tracks = 0;
+    Track *track = mFirstTrack;
+    while (track) {
+        ++*num_tracks;
+        track = track->next;
+    }
+
+    return OK;
+}
+
+sp<MetaData> MPEG4Extractor::getTrackMetaData(int index) {
+    if (index < 0) {
+        return NULL;
+    }
+
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        return NULL;
+    }
+
+    Track *track = mFirstTrack;
+    while (index > 0) {
+        if (track == NULL) {
+            return NULL;
+        }
+
+        track = track->next;
+        --index;
+    }
+
+    return track->meta;
+}
+
+status_t MPEG4Extractor::readMetaData() {
+    if (mHaveMetadata) {
+        return OK;
+    }
+
+    off_t offset = 0;
+    status_t err;
+    while ((err = parseChunk(&offset, 0)) == OK) {
+    }
+    
+    if (mHaveMetadata) {
+        return OK;
+    }
+
+    return err;
+}
+
+static void MakeFourCCString(uint32_t x, char *s) {
+    s[0] = x >> 24;
+    s[1] = (x >> 16) & 0xff;
+    s[2] = (x >> 8) & 0xff;
+    s[3] = x & 0xff;
+    s[4] = '\0';
+}
+
+status_t MPEG4Extractor::parseChunk(off_t *offset, int depth) {
+    uint32_t hdr[2];
+    if (mDataSource->read_at(*offset, hdr, 8) < 8) {
+        return ERROR_IO;
+    }
+    uint64_t chunk_size = ntohl(hdr[0]);
+    uint32_t chunk_type = ntohl(hdr[1]);
+    off_t data_offset = *offset + 8;
+
+    if (chunk_size == 1) {
+        if (mDataSource->read_at(*offset + 8, &chunk_size, 8) < 8) {
+            return ERROR_IO;
+        }
+        chunk_size = ntoh64(chunk_size);
+        data_offset += 8;
+    }
+
+    char chunk[5];
+    MakeFourCCString(chunk_type, chunk);
+
+#if 0
+    static const char kWhitespace[] = "                                        ";
+    const char *indent = &kWhitespace[sizeof(kWhitespace) - 1 - 2 * depth];
+    printf("%sfound chunk '%s' of size %lld\n", indent, chunk, chunk_size);
+
+    char buffer[256];
+    if (chunk_size <= sizeof(buffer)) {
+        if (mDataSource->read_at(*offset, buffer, chunk_size) < chunk_size) {
+            return ERROR_IO;
+        }
+
+        hexdump(buffer, chunk_size);
+    }
+#endif
+
+    off_t chunk_data_size = *offset + chunk_size - data_offset;
+
+    switch(chunk_type) {
+        case FOURCC('m', 'o', 'o', 'v'):
+        case FOURCC('t', 'r', 'a', 'k'):
+        case FOURCC('m', 'd', 'i', 'a'):
+        case FOURCC('m', 'i', 'n', 'f'):
+        case FOURCC('d', 'i', 'n', 'f'):
+        case FOURCC('s', 't', 'b', 'l'):
+        case FOURCC('m', 'v', 'e', 'x'):
+        case FOURCC('m', 'o', 'o', 'f'):
+        case FOURCC('t', 'r', 'a', 'f'):
+        case FOURCC('m', 'f', 'r', 'a'):
+        case FOURCC('s', 'k', 'i' ,'p'):
+        {
+            off_t stop_offset = *offset + chunk_size;
+            *offset = data_offset;
+            while (*offset < stop_offset) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+            assert(*offset == stop_offset);
+
+            if (chunk_type == FOURCC('m', 'o', 'o', 'v')) {
+                mHaveMetadata = true;
+
+                return UNKNOWN_ERROR;  // Return a dummy error.
+            }
+            break;
+        }
+
+        case FOURCC('t', 'k', 'h', 'd'):
+        {
+            assert(chunk_data_size >= 4);
+
+            uint8_t version;
+            if (mDataSource->read_at(data_offset, &version, 1) < 1) {
+                return ERROR_IO;
+            }
+
+            uint64_t ctime, mtime, duration;
+            int32_t id;
+            uint32_t width, height;
+
+            if (version == 1) {
+                if (chunk_data_size != 36 + 60) {
+                    return ERROR_MALFORMED;
+                }
+
+                uint8_t buffer[36 + 60];
+                if (mDataSource->read_at(
+                            data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+                    return ERROR_IO;
+                }
+
+                ctime = U64_AT(&buffer[4]);
+                mtime = U64_AT(&buffer[12]);
+                id = U32_AT(&buffer[20]);
+                duration = U64_AT(&buffer[28]);
+                width = U32_AT(&buffer[88]);
+                height = U32_AT(&buffer[92]);
+            } else if (version == 0) {
+                if (chunk_data_size != 24 + 60) {
+                    return ERROR_MALFORMED;
+                }
+
+                uint8_t buffer[24 + 60];
+                if (mDataSource->read_at(
+                            data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+                    return ERROR_IO;
+                }
+                ctime = U32_AT(&buffer[4]);
+                mtime = U32_AT(&buffer[8]);
+                id = U32_AT(&buffer[12]);
+                duration = U32_AT(&buffer[20]);
+                width = U32_AT(&buffer[76]);
+                height = U32_AT(&buffer[80]);
+            }
+
+            Track *track = new Track;
+            track->next = NULL;
+            if (mLastTrack) {
+                mLastTrack->next = track;
+            } else {
+                mFirstTrack = track;
+            }
+            mLastTrack = track;
+
+            track->meta = new MetaData;
+            track->timescale = 0;
+            track->sampleTable = new SampleTable(mDataSource);
+            track->meta->setCString(kKeyMIMEType, "application/octet-stream");
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('m', 'd', 'h', 'd'):
+        {
+            if (chunk_data_size < 4) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t version;
+            if (mDataSource->read_at(
+                        data_offset, &version, sizeof(version))
+                    < (ssize_t)sizeof(version)) {
+                return ERROR_IO;
+            }
+
+            off_t timescale_offset;
+
+            if (version == 1) {
+                timescale_offset = data_offset + 4 + 16;
+            } else if (version == 0) {
+                timescale_offset = data_offset + 4 + 8;
+            } else {
+                return ERROR_IO;
+            }
+
+            uint32_t timescale;
+            if (mDataSource->read_at(
+                        timescale_offset, &timescale, sizeof(timescale))
+                    < (ssize_t)sizeof(timescale)) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->timescale = ntohl(timescale);
+            mLastTrack->meta->setInt32(kKeyTimeScale, mLastTrack->timescale);
+
+            int64_t duration;
+            if (version == 1) {
+                if (mDataSource->read_at(
+                            timescale_offset + 4, &duration, sizeof(duration))
+                        < (ssize_t)sizeof(duration)) {
+                    return ERROR_IO;
+                }
+                duration = ntoh64(duration);
+            } else {
+                int32_t duration32;
+                if (mDataSource->read_at(
+                            timescale_offset + 4, &duration32, sizeof(duration32))
+                        < (ssize_t)sizeof(duration32)) {
+                    return ERROR_IO;
+                }
+                duration = ntohl(duration32);
+            }
+            mLastTrack->meta->setInt32(kKeyDuration, duration);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('h', 'd', 'l', 'r'):
+        {
+            if (chunk_data_size < 25) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[24];
+            if (mDataSource->read_at(data_offset, buffer, 24) < 24) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+                return ERROR_MALFORMED;
+            }
+
+            if (U32_AT(&buffer[4]) != 0) {
+                // pre_defined should be 0.
+                return ERROR_MALFORMED;
+            }
+
+            mHandlerType = U32_AT(&buffer[8]);
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 'd'):
+        {
+            if (chunk_data_size < 8) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[8];
+            assert(chunk_data_size >= (off_t)sizeof(buffer));
+            if (mDataSource->read_at(
+                        data_offset, buffer, 8) < 8) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+                return ERROR_MALFORMED;
+            }
+
+            uint32_t entry_count = U32_AT(&buffer[4]);
+
+            if (entry_count > 1) {
+                // For now we only support a single type of media per track.
+                return ERROR_UNSUPPORTED;
+            }
+
+            off_t stop_offset = *offset + chunk_size;
+            *offset = data_offset + 8;
+            for (uint32_t i = 0; i < entry_count; ++i) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+            assert(*offset == stop_offset);
+            break;
+        }
+
+        case FOURCC('m', 'p', '4', 'a'):
+        case FOURCC('s', 'a', 'm', 'r'):
+        {
+            if (mHandlerType != FOURCC('s', 'o', 'u', 'n')) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[8 + 20];
+            if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+                // Basic AudioSampleEntry size.
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->read_at(
+                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+                return ERROR_IO;
+            }
+
+            uint16_t data_ref_index = U16_AT(&buffer[6]);
+            uint16_t num_channels = U16_AT(&buffer[16]);
+
+            if (!strcasecmp("audio/3gpp", FourCC2MIME(chunk_type))) {
+                // AMR audio is always mono.
+                num_channels = 1;
+            }
+
+            uint16_t sample_size = U16_AT(&buffer[18]);
+            uint32_t sample_rate = U32_AT(&buffer[24]) >> 16;
+
+            printf("*** coding='%s' %d channels, size %d, rate %d\n",
+                   chunk, num_channels, sample_size, sample_rate);
+
+            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+            mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
+            mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
+
+            off_t stop_offset = *offset + chunk_size;
+            *offset = data_offset + sizeof(buffer);
+            while (*offset < stop_offset) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+            assert(*offset == stop_offset);
+            break;
+        }
+
+        case FOURCC('m', 'p', '4', 'v'):
+        case FOURCC('s', '2', '6', '3'):
+        case FOURCC('a', 'v', 'c', '1'):
+        {
+            if (mHandlerType != FOURCC('v', 'i', 'd', 'e')) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[78];
+            if (chunk_data_size < (ssize_t)sizeof(buffer)) {
+                // Basic VideoSampleEntry size.
+                return ERROR_MALFORMED;
+            }
+
+            if (mDataSource->read_at(
+                        data_offset, buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+                return ERROR_IO;
+            }
+
+            uint16_t data_ref_index = U16_AT(&buffer[6]);
+            uint16_t width = U16_AT(&buffer[6 + 18]);
+            uint16_t height = U16_AT(&buffer[6 + 20]);
+
+            printf("*** coding='%s' width=%d height=%d\n",
+                   chunk, width, height);
+
+            mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
+            mLastTrack->meta->setInt32(kKeyWidth, width);
+            mLastTrack->meta->setInt32(kKeyHeight, height);
+
+            off_t stop_offset = *offset + chunk_size;
+            *offset = data_offset + sizeof(buffer);
+            while (*offset < stop_offset) {
+                status_t err = parseChunk(offset, depth + 1);
+                if (err != OK) {
+                    return err;
+                }
+            }
+            assert(*offset == stop_offset);
+            break;
+        }
+
+        case FOURCC('s', 't', 'c', 'o'):
+        case FOURCC('c', 'o', '6', '4'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setChunkOffsetParams(
+                        chunk_type, data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 'c'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setSampleToChunkParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 'z'):
+        case FOURCC('s', 't', 'z', '2'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setSampleSizeParams(
+                        chunk_type, data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 't', 's'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setTimeToSampleParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('s', 't', 's', 's'):
+        {
+            status_t err =
+                mLastTrack->sampleTable->setSyncSampleParams(
+                        data_offset, chunk_data_size);
+
+            if (err != OK) {
+                return err;
+            }
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('e', 's', 'd', 's'):
+        {
+            if (chunk_data_size < 4) {
+                return ERROR_MALFORMED;
+            }
+
+            uint8_t buffer[256];
+            if (chunk_data_size > (off_t)sizeof(buffer)) {
+                return ERROR_BUFFER_TOO_SMALL;
+            }
+
+            if (mDataSource->read_at(
+                        data_offset, buffer, chunk_data_size) < chunk_data_size) {
+                return ERROR_IO;
+            }
+
+            if (U32_AT(buffer) != 0) {
+                // Should be version 0, flags 0.
+                return ERROR_MALFORMED;
+            }
+
+            mLastTrack->meta->setData(
+                    kKeyESDS, kTypeESDS, &buffer[4], chunk_data_size - 4);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        case FOURCC('a', 'v', 'c', 'C'):
+        {
+            char buffer[256];
+            if (chunk_data_size > (off_t)sizeof(buffer)) {
+                return ERROR_BUFFER_TOO_SMALL;
+            }
+
+            if (mDataSource->read_at(
+                        data_offset, buffer, chunk_data_size) < chunk_data_size) {
+                return ERROR_IO;
+            }
+
+            mLastTrack->meta->setData(
+                    kKeyAVCC, kTypeAVCC, buffer, chunk_data_size);
+
+            *offset += chunk_size;
+            break;
+        }
+
+        default:
+        {
+            *offset += chunk_size;
+            break;
+        }
+    }
+
+    return OK;
+}
+
+status_t MPEG4Extractor::getTrack(int index, MediaSource **source) {
+    *source = NULL;
+
+    if (index < 0) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    status_t err;
+    if ((err = readMetaData()) != OK) {
+        return err;
+    }
+
+    Track *track = mFirstTrack;
+    while (index > 0) {
+        if (track == NULL) {
+            return ERROR_OUT_OF_RANGE;
+        }
+
+        track = track->next;
+        --index;
+    }
+
+    *source = new MPEG4Source(
+            track->meta, mDataSource, track->sampleTable);
+
+    return OK;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Source::MPEG4Source(
+        const sp<MetaData> &format,
+        DataSource *dataSource, SampleTable *sampleTable)
+    : mFormat(format),
+      mDataSource(dataSource),
+      mTimescale(0),
+      mSampleTable(sampleTable),
+      mCurrentSampleIndex(0),
+      mIsAVC(false),
+      mStarted(false),
+      mGroup(NULL),
+      mBuffer(NULL),
+      mBufferOffset(0),
+      mBufferSizeRemaining(0),
+      mNeedsNALFraming(false) {
+    const char *mime;
+    bool success = mFormat->findCString(kKeyMIMEType, &mime);
+    assert(success);
+
+    success = mFormat->findInt32(kKeyTimeScale, &mTimescale);
+    assert(success);
+
+    mIsAVC = !strcasecmp(mime, "video/avc");
+}
+
+MPEG4Source::~MPEG4Source() {
+    if (mStarted) {
+        stop();
+    }
+}
+
+status_t MPEG4Source::start(MetaData *params) {
+    assert(!mStarted);
+
+    int32_t val;
+    if (mIsAVC && params && params->findInt32(kKeyNeedsNALFraming, &val)
+        && val != 0) {
+        mNeedsNALFraming = true;
+    } else {
+        mNeedsNALFraming = false;
+    }
+
+    mGroup = new MediaBufferGroup;
+
+    size_t max_size;
+    status_t err = mSampleTable->getMaxSampleSize(&max_size);
+    assert(err == OK);
+
+    // Add padding for de-framing of AVC content just in case.
+    mGroup->add_buffer(new MediaBuffer(max_size + 2));
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t MPEG4Source::stop() {
+    assert(mStarted);
+
+    if (mBuffer != NULL) {
+        mBuffer->release();
+        mBuffer = NULL;
+    }
+
+    delete mGroup;
+    mGroup = NULL;
+
+    mStarted = false;
+    mCurrentSampleIndex = 0;
+
+    return OK;
+}
+
+sp<MetaData> MPEG4Source::getFormat() {
+    return mFormat;
+}
+
+status_t MPEG4Source::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    assert(mStarted);
+
+    *out = NULL;
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        uint32_t sampleIndex;
+        status_t err = mSampleTable->findClosestSample(
+                seekTimeUs * mTimescale / 1000000,
+                &sampleIndex, SampleTable::kSyncSample_Flag);
+
+        if (err != OK) {
+            return err;
+        }
+
+        mCurrentSampleIndex = sampleIndex;
+        if (mBuffer != NULL) {
+            mBuffer->release();
+            mBuffer = NULL;
+        }
+
+        // fall through
+    }
+
+    if (mBuffer == NULL) {
+        off_t offset;
+        size_t size;
+        status_t err = mSampleTable->getSampleOffsetAndSize(
+                mCurrentSampleIndex, &offset, &size);
+
+        if (err != OK) {
+            return err;
+        }
+
+        uint32_t dts;
+        err = mSampleTable->getDecodingTime(mCurrentSampleIndex, &dts);
+
+        if (err != OK) {
+            return err;
+        }
+
+        err = mGroup->acquire_buffer(&mBuffer);
+        if (err != OK) {
+            assert(mBuffer == NULL);
+            return err;
+        }
+
+        assert(mBuffer->size() + 2 >= size);
+
+        ssize_t num_bytes_read =
+            mDataSource->read_at(offset, (uint8_t *)mBuffer->data() + 2, size);
+
+        if (num_bytes_read < (ssize_t)size) {
+            mBuffer->release();
+            mBuffer = NULL;
+
+            return err;
+        }
+
+        mBuffer->set_range(2, size);
+        mBuffer->meta_data()->clear();
+        mBuffer->meta_data()->setInt32(kKeyTimeUnits, dts);
+        mBuffer->meta_data()->setInt32(kKeyTimeScale, mTimescale);
+
+        ++mCurrentSampleIndex;
+
+        mBufferOffset = 2;
+        mBufferSizeRemaining = size;
+    }
+
+    if (!mIsAVC) {
+        *out = mBuffer;
+        mBuffer = NULL;
+
+        return OK;
+    }
+
+    uint8_t *data = (uint8_t *)mBuffer->data() + mBufferOffset;
+    assert(mBufferSizeRemaining >= 2);
+
+    size_t nal_length = (data[0] << 8) | data[1];
+    assert(mBufferSizeRemaining >= 2 + nal_length);
+
+    if (mNeedsNALFraming) {
+        // Insert marker.
+        data[-2] = data[-1] = data[0] = 0;
+        data[1] = 1;
+
+        mBuffer->set_range(mBufferOffset - 2, nal_length + 4);
+    } else {
+        mBuffer->set_range(mBufferOffset + 2, nal_length);
+    }
+
+    mBufferOffset += nal_length + 2;
+    mBufferSizeRemaining -= nal_length + 2;
+
+    if (mBufferSizeRemaining > 0) {
+        *out = mBuffer->clone();
+    } else {
+        *out = mBuffer;
+        mBuffer = NULL;
+    }
+
+    return OK;
+}
+
+bool SniffMPEG4(DataSource *source, String8 *mimeType, float *confidence) {
+    uint8_t header[8];
+
+    ssize_t n = source->read_at(4, header, sizeof(header));
+    if (n < (ssize_t)sizeof(header)) {
+        return false;
+    }
+
+    if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
+        || !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)) {
+        *mimeType = "video/mp4";
+        *confidence = 0.1;
+
+        return true;
+    }
+
+    return false;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
new file mode 100644
index 0000000..6bdf282
--- /dev/null
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -0,0 +1,641 @@
+/*
+ * Copyright (C) 2009 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 <arpa/inet.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <ctype.h>
+#include <pthread.h>
+
+#include <media/stagefright/MPEG4Writer.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+class MPEG4Writer::Track {
+public:
+    Track(MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source);
+    ~Track();
+
+    void start();
+    void stop();
+
+    int64_t getDuration() const;
+    void writeTrackHeader(int32_t trackID);
+
+private:
+    MPEG4Writer *mOwner;
+    sp<MetaData> mMeta;
+    MediaSource *mSource;
+    volatile bool mDone;
+
+    pthread_t mThread;
+
+    struct SampleInfo {
+        size_t size;
+        off_t offset;
+        int64_t timestamp;
+    };
+    List<SampleInfo> mSampleInfos;
+
+    void *mCodecSpecificData;
+    size_t mCodecSpecificDataSize;
+
+    static void *ThreadWrapper(void *me);
+    void threadEntry();
+
+    Track(const Track &);
+    Track &operator=(const Track &);
+};
+
+MPEG4Writer::MPEG4Writer(const char *filename)
+    : mFile(fopen(filename, "wb")),
+      mOffset(0),
+      mMdatOffset(0) {
+    assert(mFile != NULL);
+}
+
+MPEG4Writer::~MPEG4Writer() {
+    stop();
+
+    for (List<Track *>::iterator it = mTracks.begin();
+         it != mTracks.end(); ++it) {
+        delete *it;
+    }
+    mTracks.clear();
+}
+
+void MPEG4Writer::addSource(const sp<MetaData> &meta, MediaSource *source) {
+    Track *track = new Track(this, meta, source);
+    mTracks.push_back(track);
+}
+
+void MPEG4Writer::start() {
+    if (mFile == NULL) {
+        return;
+    }
+
+    beginBox("ftyp");
+      writeFourcc("isom");
+      writeInt32(0);
+      writeFourcc("isom");
+    endBox();
+
+    mMdatOffset = mOffset;
+    write("\x00\x00\x00\x01mdat????????", 16);
+
+    for (List<Track *>::iterator it = mTracks.begin();
+         it != mTracks.end(); ++it) {
+        (*it)->start();
+    }
+}
+
+void MPEG4Writer::stop() {
+    if (mFile == NULL) {
+        return;
+    }
+
+    int64_t max_duration = 0;
+    for (List<Track *>::iterator it = mTracks.begin();
+         it != mTracks.end(); ++it) {
+        (*it)->stop();
+
+        int64_t duration = (*it)->getDuration();
+        if (duration > max_duration) {
+            max_duration = duration;
+        }
+    }
+
+    // Fix up the size of the 'mdat' chunk.
+    fseek(mFile, mMdatOffset + 8, SEEK_SET);
+    int64_t size = mOffset - mMdatOffset;
+    size = hton64(size);
+    fwrite(&size, 1, 8, mFile);
+    fseek(mFile, mOffset, SEEK_SET);
+
+    time_t now = time(NULL);
+
+    beginBox("moov");
+
+      beginBox("mvhd");
+        writeInt32(0);             // version=0, flags=0
+        writeInt32(now);           // creation time
+        writeInt32(now);           // modification time
+        writeInt32(1000);          // timescale
+        writeInt32(max_duration);
+        writeInt32(0x10000);       // rate
+        writeInt16(0x100);         // volume
+        writeInt16(0);             // reserved
+        writeInt32(0);             // reserved
+        writeInt32(0);             // reserved
+        writeInt32(0x10000);       // matrix
+        writeInt32(0);
+        writeInt32(0);
+        writeInt32(0);
+        writeInt32(0x10000);
+        writeInt32(0);
+        writeInt32(0);
+        writeInt32(0);
+        writeInt32(0x40000000);
+        writeInt32(0);             // predefined
+        writeInt32(0);             // predefined
+        writeInt32(0);             // predefined
+        writeInt32(0);             // predefined
+        writeInt32(0);             // predefined
+        writeInt32(0);             // predefined
+        writeInt32(mTracks.size() + 1);  // nextTrackID
+      endBox();  // mvhd
+
+      int32_t id = 1;
+      for (List<Track *>::iterator it = mTracks.begin();
+           it != mTracks.end(); ++it, ++id) {
+          (*it)->writeTrackHeader(id);
+      }
+    endBox();  // moov
+
+    assert(mBoxes.empty());
+
+    fclose(mFile);
+    mFile = NULL;
+}
+
+off_t MPEG4Writer::addSample(MediaBuffer *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    off_t old_offset = mOffset;
+
+    fwrite((const uint8_t *)buffer->data() + buffer->range_offset(),
+           1, buffer->range_length(), mFile);
+
+    mOffset += buffer->range_length();
+
+    return old_offset;
+}
+
+void MPEG4Writer::beginBox(const char *fourcc) {
+    assert(strlen(fourcc) == 4);
+
+    mBoxes.push_back(mOffset);
+
+    writeInt32(0);
+    writeFourcc(fourcc);
+}
+
+void MPEG4Writer::endBox() {
+    assert(!mBoxes.empty());
+
+    off_t offset = *--mBoxes.end();
+    mBoxes.erase(--mBoxes.end());
+
+    fseek(mFile, offset, SEEK_SET);
+    writeInt32(mOffset - offset);
+    mOffset -= 4;
+    fseek(mFile, mOffset, SEEK_SET);
+}
+
+void MPEG4Writer::writeInt8(int8_t x) {
+    fwrite(&x, 1, 1, mFile);
+    ++mOffset;
+}
+
+void MPEG4Writer::writeInt16(int16_t x) {
+    x = htons(x);
+    fwrite(&x, 1, 2, mFile);
+    mOffset += 2;
+}
+
+void MPEG4Writer::writeInt32(int32_t x) {
+    x = htonl(x);
+    fwrite(&x, 1, 4, mFile);
+    mOffset += 4;
+}
+
+void MPEG4Writer::writeInt64(int64_t x) {
+    x = hton64(x);
+    fwrite(&x, 1, 8, mFile);
+    mOffset += 8;
+}
+
+void MPEG4Writer::writeCString(const char *s) {
+    size_t n = strlen(s);
+
+    fwrite(s, 1, n + 1, mFile);
+    mOffset += n + 1;
+}
+
+void MPEG4Writer::writeFourcc(const char *s) {
+    assert(strlen(s) == 4);
+    fwrite(s, 1, 4, mFile);
+    mOffset += 4;
+}
+
+void MPEG4Writer::write(const void *data, size_t size) {
+    fwrite(data, 1, size, mFile);
+    mOffset += size;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MPEG4Writer::Track::Track(
+        MPEG4Writer *owner, const sp<MetaData> &meta, MediaSource *source)
+    : mOwner(owner),
+      mMeta(meta),
+      mSource(source),
+      mDone(false),
+      mCodecSpecificData(NULL),
+      mCodecSpecificDataSize(0) {
+}
+
+MPEG4Writer::Track::~Track() {
+    stop();
+
+    if (mCodecSpecificData != NULL) {
+        free(mCodecSpecificData);
+        mCodecSpecificData = NULL;
+    }
+}
+
+void MPEG4Writer::Track::start() {
+    mSource->start();
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    mDone = false;
+
+    int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+    assert(err == 0);
+
+    pthread_attr_destroy(&attr);
+}
+
+void MPEG4Writer::Track::stop() {
+    if (mDone) {
+        return;
+    }
+
+    mDone = true;
+
+    void *dummy;
+    pthread_join(mThread, &dummy);
+
+    mSource->stop();
+}
+
+// static
+void *MPEG4Writer::Track::ThreadWrapper(void *me) {
+    Track *track = static_cast<Track *>(me);
+
+    track->threadEntry();
+
+    return NULL;
+}
+
+void MPEG4Writer::Track::threadEntry() {
+    bool is_mpeg4 = false;
+    sp<MetaData> meta = mSource->getFormat();
+    const char *mime;
+    meta->findCString(kKeyMIMEType, &mime);
+    is_mpeg4 = !strcasecmp(mime, "video/mp4v-es");
+
+    MediaBuffer *buffer;
+    while (!mDone && mSource->read(&buffer) == OK) {
+        if (buffer->range_length() == 0) {
+            buffer->release();
+            buffer = NULL;
+
+            continue;
+        }
+
+        if (mCodecSpecificData == NULL && is_mpeg4) {
+            const uint8_t *data =
+                (const uint8_t *)buffer->data() + buffer->range_offset();
+
+            const size_t size = buffer->range_length();
+
+            size_t offset = 0;
+            while (offset + 3 < size) {
+                if (data[offset] == 0x00 && data[offset + 1] == 0x00
+                    && data[offset + 2] == 0x01 && data[offset + 3] == 0xb6) {
+                    break;
+                }
+
+                ++offset;
+            }
+
+            assert(offset + 3 < size);
+
+            mCodecSpecificDataSize = offset;
+            mCodecSpecificData = malloc(offset);
+            memcpy(mCodecSpecificData, data, offset);
+
+            buffer->set_range(buffer->range_offset() + offset, size - offset);
+        }
+
+        off_t offset = mOwner->addSample(buffer);
+
+        SampleInfo info;
+        info.size = buffer->range_length();
+        info.offset = offset;
+
+        int32_t units, scale;
+        bool success =
+            buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+        assert(success);
+        success =
+            buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+        assert(success);
+
+        info.timestamp = (int64_t)units * 1000 / scale;
+
+        mSampleInfos.push_back(info);
+
+        buffer->release();
+        buffer = NULL;
+    }
+}
+
+int64_t MPEG4Writer::Track::getDuration() const {
+    return 10000;  // XXX
+}
+
+void MPEG4Writer::Track::writeTrackHeader(int32_t trackID) {
+    const char *mime;
+    bool success = mMeta->findCString(kKeyMIMEType, &mime);
+    assert(success);
+
+    bool is_audio = !strncasecmp(mime, "audio/", 6);
+
+    time_t now = time(NULL);
+
+    mOwner->beginBox("trak");
+
+      mOwner->beginBox("tkhd");
+        mOwner->writeInt32(0);             // version=0, flags=0
+        mOwner->writeInt32(now);           // creation time
+        mOwner->writeInt32(now);           // modification time
+        mOwner->writeInt32(trackID);
+        mOwner->writeInt32(0);             // reserved
+        mOwner->writeInt32(getDuration());
+        mOwner->writeInt32(0);             // reserved
+        mOwner->writeInt32(0);             // reserved
+        mOwner->writeInt16(0);             // layer
+        mOwner->writeInt16(0);             // alternate group
+        mOwner->writeInt16(is_audio ? 0x100 : 0);  // volume
+        mOwner->writeInt16(0);             // reserved
+
+        mOwner->writeInt32(0x10000);       // matrix
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0x10000);
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0);
+        mOwner->writeInt32(0x40000000);
+
+        if (is_audio) {
+            mOwner->writeInt32(0);
+            mOwner->writeInt32(0);
+        } else {
+            int32_t width, height;
+            bool success = mMeta->findInt32(kKeyWidth, &width);
+            success = success && mMeta->findInt32(kKeyHeight, &height);
+            assert(success);
+
+            mOwner->writeInt32(width);
+            mOwner->writeInt32(height);
+        }
+      mOwner->endBox();  // tkhd
+
+      mOwner->beginBox("mdia");
+
+        mOwner->beginBox("mdhd");
+          mOwner->writeInt32(0);             // version=0, flags=0
+          mOwner->writeInt32(now);           // creation time
+          mOwner->writeInt32(now);           // modification time
+          mOwner->writeInt32(1000);          // timescale
+          mOwner->writeInt32(getDuration());
+          mOwner->writeInt16(0);             // language code XXX
+          mOwner->writeInt16(0);             // predefined
+        mOwner->endBox();
+
+        mOwner->beginBox("hdlr");
+          mOwner->writeInt32(0);             // version=0, flags=0
+          mOwner->writeInt32(0);             // predefined
+          mOwner->writeFourcc(is_audio ? "soun" : "vide");
+          mOwner->writeInt32(0);             // reserved
+          mOwner->writeInt32(0);             // reserved
+          mOwner->writeInt32(0);             // reserved
+          mOwner->writeCString("");          // name
+        mOwner->endBox();
+
+        mOwner->beginBox("minf");
+
+          mOwner->beginBox("dinf");
+            mOwner->beginBox("dref");
+              mOwner->writeInt32(0);  // version=0, flags=0
+              mOwner->writeInt32(1);
+              mOwner->beginBox("url ");
+                mOwner->writeInt32(1);  // version=0, flags=1
+              mOwner->endBox();  // url
+            mOwner->endBox();  // dref
+          mOwner->endBox();  // dinf
+
+          if (is_audio) {
+              mOwner->beginBox("smhd");
+              mOwner->writeInt32(0);           // version=0, flags=0
+              mOwner->writeInt16(0);           // balance
+              mOwner->writeInt16(0);           // reserved
+              mOwner->endBox();
+          } else {
+              mOwner->beginBox("vmhd");
+              mOwner->writeInt32(0x00000001);  // version=0, flags=1
+              mOwner->writeInt16(0);           // graphics mode
+              mOwner->writeInt16(0);           // opcolor
+              mOwner->writeInt16(0);
+              mOwner->writeInt16(0);
+              mOwner->endBox();
+          }
+        mOwner->endBox();  // minf
+
+        mOwner->beginBox("stbl");
+
+          mOwner->beginBox("stsd");
+            mOwner->writeInt32(0);               // version=0, flags=0
+            mOwner->writeInt32(1);               // entry count
+            if (is_audio) {
+                mOwner->beginBox("xxxx");          // audio format XXX
+                  mOwner->writeInt32(0);           // reserved
+                  mOwner->writeInt16(0);           // reserved
+                  mOwner->writeInt16(0);           // data ref index
+                  mOwner->writeInt32(0);           // reserved
+                  mOwner->writeInt32(0);           // reserved
+                  mOwner->writeInt16(2);           // channel count
+                  mOwner->writeInt16(16);          // sample size
+                  mOwner->writeInt16(0);           // predefined
+                  mOwner->writeInt16(0);           // reserved
+
+                  int32_t samplerate;
+                  bool success = mMeta->findInt32(kKeySampleRate, &samplerate);
+                  assert(success);
+
+                  mOwner->writeInt32(samplerate << 16);
+                mOwner->endBox();
+            } else {
+                if (!strcasecmp("video/mp4v-es", mime)) {
+                    mOwner->beginBox("mp4v");
+                } else if (!strcasecmp("video/3gpp", mime)) {
+                    mOwner->beginBox("s263");
+                } else {
+                    assert(!"should not be here, unknown mime type.");
+                }
+
+                  mOwner->writeInt32(0);           // reserved
+                  mOwner->writeInt16(0);           // reserved
+                  mOwner->writeInt16(0);           // data ref index
+                  mOwner->writeInt16(0);           // predefined
+                  mOwner->writeInt16(0);           // reserved
+                  mOwner->writeInt32(0);           // predefined
+                  mOwner->writeInt32(0);           // predefined
+                  mOwner->writeInt32(0);           // predefined
+
+                  int32_t width, height;
+                  bool success = mMeta->findInt32(kKeyWidth, &width);
+                  success = success && mMeta->findInt32(kKeyHeight, &height);
+                  assert(success);
+
+                  mOwner->writeInt16(width);
+                  mOwner->writeInt16(height);
+                  mOwner->writeInt32(0x480000);    // horiz resolution
+                  mOwner->writeInt32(0x480000);    // vert resolution
+                  mOwner->writeInt32(0);           // reserved
+                  mOwner->writeInt16(1);           // frame count
+                  mOwner->write("                                ", 32);
+                  mOwner->writeInt16(0x18);        // depth
+                  mOwner->writeInt16(-1);          // predefined
+
+                  assert(23 + mCodecSpecificDataSize < 128);
+
+                  if (!strcasecmp("video/mp4v-es", mime)) {
+                      mOwner->beginBox("esds");
+
+                        mOwner->writeInt32(0);           // version=0, flags=0
+
+                        mOwner->writeInt8(0x03);  // ES_DescrTag
+                        mOwner->writeInt8(23 + mCodecSpecificDataSize);
+                        mOwner->writeInt16(0x0000);  // ES_ID
+                        mOwner->writeInt8(0x1f);
+
+                        mOwner->writeInt8(0x04);  // DecoderConfigDescrTag
+                        mOwner->writeInt8(15 + mCodecSpecificDataSize);
+                        mOwner->writeInt8(0x20);  // objectTypeIndication ISO/IEC 14492-2
+                        mOwner->writeInt8(0x11);  // streamType VisualStream
+
+                        static const uint8_t kData[] = {
+                            0x01, 0x77, 0x00,
+                            0x00, 0x03, 0xe8, 0x00,
+                            0x00, 0x03, 0xe8, 0x00
+                        };
+                        mOwner->write(kData, sizeof(kData));
+                        
+                        mOwner->writeInt8(0x05);  // DecoderSpecificInfoTag
+
+                        mOwner->writeInt8(mCodecSpecificDataSize);
+                        mOwner->write(mCodecSpecificData, mCodecSpecificDataSize);
+
+                        static const uint8_t kData2[] = {
+                            0x06,  // SLConfigDescriptorTag
+                            0x01,
+                            0x02
+                        };
+                        mOwner->write(kData2, sizeof(kData2));
+
+                      mOwner->endBox();  // esds
+                  } else if (!strcasecmp("video/3gpp", mime)) {
+                      mOwner->beginBox("d263");
+
+                          mOwner->writeInt32(0);  // vendor
+                          mOwner->writeInt8(0);   // decoder version
+                          mOwner->writeInt8(10);  // level: 10
+                          mOwner->writeInt8(0);   // profile: 0
+
+                      mOwner->endBox();  // d263
+                  }
+                mOwner->endBox();  // mp4v or s263
+            }
+          mOwner->endBox();  // stsd
+
+          mOwner->beginBox("stts");
+            mOwner->writeInt32(0);  // version=0, flags=0
+            mOwner->writeInt32(mSampleInfos.size() - 1);
+
+            List<SampleInfo>::iterator it = mSampleInfos.begin();
+            int64_t last = (*it).timestamp;
+            ++it;
+            while (it != mSampleInfos.end()) {
+                mOwner->writeInt32(1);
+                mOwner->writeInt32((*it).timestamp - last);
+
+                last = (*it).timestamp;
+
+                ++it;
+            }
+          mOwner->endBox();  // stts
+
+          mOwner->beginBox("stsz");
+            mOwner->writeInt32(0);  // version=0, flags=0
+            mOwner->writeInt32(0);  // default sample size
+            mOwner->writeInt32(mSampleInfos.size());
+            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+                 it != mSampleInfos.end(); ++it) {
+                mOwner->writeInt32((*it).size);
+            }
+          mOwner->endBox();  // stsz
+
+          mOwner->beginBox("stsc");
+            mOwner->writeInt32(0);  // version=0, flags=0
+            mOwner->writeInt32(mSampleInfos.size());
+            int32_t n = 1;
+            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+                 it != mSampleInfos.end(); ++it, ++n) {
+                mOwner->writeInt32(n);
+                mOwner->writeInt32(1);
+                mOwner->writeInt32(1);
+            }
+          mOwner->endBox();  // stsc
+
+          mOwner->beginBox("co64");
+            mOwner->writeInt32(0);  // version=0, flags=0
+            mOwner->writeInt32(mSampleInfos.size());
+            for (List<SampleInfo>::iterator it = mSampleInfos.begin();
+                 it != mSampleInfos.end(); ++it, ++n) {
+                mOwner->writeInt64((*it).offset);
+            }
+          mOwner->endBox();  // co64
+
+        mOwner->endBox();  // stbl
+      mOwner->endBox();  // mdia
+    mOwner->endBox();  // trak
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaBuffer.cpp b/media/libstagefright/MediaBuffer.cpp
new file mode 100644
index 0000000..cd78dbd
--- /dev/null
+++ b/media/libstagefright/MediaBuffer.cpp
@@ -0,0 +1,174 @@
+/*
+ * Copyright (C) 2009 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_TAG "MediaBuffer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <errno.h>
+#include <pthread.h>
+#include <stdlib.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+// XXX make this truly atomic.
+static int atomic_add(int *value, int delta) {
+    int prev_value = *value;
+    *value += delta;
+
+    return prev_value;
+}
+
+MediaBuffer::MediaBuffer(void *data, size_t size)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(data),
+      mSize(size),
+      mRangeOffset(0),
+      mRangeLength(size),
+      mOwnsData(false),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
+MediaBuffer::MediaBuffer(size_t size)
+    : mObserver(NULL),
+      mNextBuffer(NULL),
+      mRefCount(0),
+      mData(malloc(size)),
+      mSize(size),
+      mRangeOffset(0),
+      mRangeLength(size),
+      mOwnsData(true),
+      mMetaData(new MetaData),
+      mOriginal(NULL) {
+}
+
+void MediaBuffer::release() {
+    if (mObserver == NULL) {
+        assert(mRefCount == 0);
+        delete this;
+        return;
+    }
+
+    int prevCount = atomic_add(&mRefCount, -1);
+    if (prevCount == 1) {
+        if (mObserver == NULL) {
+            delete this;
+            return;
+        }
+
+        mObserver->signalBufferReturned(this);
+    }
+    assert(prevCount > 0);
+}
+
+void MediaBuffer::claim() {
+    assert(mObserver != NULL);
+    assert(mRefCount == 1);
+
+    mRefCount = 0;
+}
+
+void MediaBuffer::add_ref() {
+    atomic_add(&mRefCount, 1);
+}
+
+void *MediaBuffer::data() const {
+    return mData;
+}
+
+size_t MediaBuffer::size() const {
+    return mSize;
+}
+
+size_t MediaBuffer::range_offset() const {
+    return mRangeOffset;
+}
+
+size_t MediaBuffer::range_length() const {
+    return mRangeLength;
+}
+
+void MediaBuffer::set_range(size_t offset, size_t length) {
+    if (offset < 0 || offset + length > mSize) {
+        LOGE("offset = %d, length = %d, mSize = %d", offset, length, mSize);
+    }
+    assert(offset >= 0 && offset + length <= mSize);
+
+    mRangeOffset = offset;
+    mRangeLength = length;
+}
+
+sp<MetaData> MediaBuffer::meta_data() {
+    return mMetaData;
+}
+
+void MediaBuffer::reset() {
+    mMetaData->clear();
+    set_range(0, mSize);
+}
+
+MediaBuffer::~MediaBuffer() {
+    assert(mObserver == NULL);
+
+    if (mOwnsData && mData != NULL) {
+        free(mData);
+        mData = NULL;
+    }
+
+    if (mOriginal != NULL) {
+        mOriginal->release();
+        mOriginal = NULL;
+    }
+}
+
+void MediaBuffer::setObserver(MediaBufferObserver *observer) {
+    assert(observer == NULL || mObserver == NULL);
+    mObserver = observer;
+}
+
+void MediaBuffer::setNextBuffer(MediaBuffer *buffer) {
+    mNextBuffer = buffer;
+}
+
+MediaBuffer *MediaBuffer::nextBuffer() {
+    return mNextBuffer;
+}
+
+int MediaBuffer::refcount() const {
+    return mRefCount;
+}
+
+MediaBuffer *MediaBuffer::clone() {
+    MediaBuffer *buffer = new MediaBuffer(mData, mSize);
+    buffer->set_range(mRangeOffset, mRangeLength);
+    buffer->mMetaData = new MetaData(*mMetaData.get());
+
+    add_ref();
+    buffer->mOriginal = this;
+
+    return buffer;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MediaBufferGroup.cpp b/media/libstagefright/MediaBufferGroup.cpp
new file mode 100644
index 0000000..aec7722
--- /dev/null
+++ b/media/libstagefright/MediaBufferGroup.cpp
@@ -0,0 +1,88 @@
+/*
+ * Copyright (C) 2009 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_TAG "MediaBufferGroup"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+
+namespace android {
+
+MediaBufferGroup::MediaBufferGroup()
+    : mFirstBuffer(NULL),
+      mLastBuffer(NULL) {
+}
+
+MediaBufferGroup::~MediaBufferGroup() {
+    MediaBuffer *next;
+    for (MediaBuffer *buffer = mFirstBuffer; buffer != NULL;
+         buffer = next) {
+        next = buffer->nextBuffer();
+
+        assert(buffer->refcount() == 0);
+
+        buffer->setObserver(NULL);
+        buffer->release();
+    }
+}
+
+void MediaBufferGroup::add_buffer(MediaBuffer *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    buffer->setObserver(this);
+
+    if (mLastBuffer) {
+        mLastBuffer->setNextBuffer(buffer);
+    } else {
+        mFirstBuffer = buffer;
+    }
+
+    mLastBuffer = buffer;
+}
+
+status_t MediaBufferGroup::acquire_buffer(MediaBuffer **out) {
+    Mutex::Autolock autoLock(mLock);
+
+    for (;;) {
+        for (MediaBuffer *buffer = mFirstBuffer;
+             buffer != NULL; buffer = buffer->nextBuffer()) {
+            if (buffer->refcount() == 0) {
+                buffer->add_ref();
+                buffer->reset();
+
+                *out = buffer;
+                goto exit;
+            }
+        }
+
+        // All buffers are in use. Block until one of them is returned to us.
+        mCondition.wait(mLock);
+    }
+
+exit:
+    return OK;
+}
+
+void MediaBufferGroup::signalBufferReturned(MediaBuffer *) {
+    Mutex::Autolock autoLock(mLock);
+    mCondition.signal();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
new file mode 100644
index 0000000..bc66794
--- /dev/null
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -0,0 +1,54 @@
+/*
+ * Copyright (C) 2009 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 "MediaExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MP3Extractor.h>
+#include <media/stagefright/MPEG4Extractor.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <utils/String8.h>
+
+namespace android {
+
+// static
+MediaExtractor *MediaExtractor::Create(DataSource *source, const char *mime) {
+    String8 tmp;
+    if (mime == NULL) {
+        float confidence;
+        if (!source->sniff(&tmp, &confidence)) {
+            LOGE("FAILED to autodetect media content.");
+
+            return NULL;
+        }
+
+        mime = tmp.string();
+        LOGI("Autodetected media content as '%s' with confidence %.2f",
+             mime, confidence);
+    }
+
+    if (!strcasecmp(mime, "video/mp4") || !strcasecmp(mime, "audio/mp4")) {
+        return new MPEG4Extractor(source);
+    } else if (!strcasecmp(mime, "audio/mpeg")) {
+        return new MP3Extractor(source);
+    }
+
+    return NULL;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MediaPlayerImpl.cpp b/media/libstagefright/MediaPlayerImpl.cpp
new file mode 100644
index 0000000..78fcdee
--- /dev/null
+++ b/media/libstagefright/MediaPlayerImpl.cpp
@@ -0,0 +1,693 @@
+/*
+ * Copyright (C) 2009 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 "MediaPlayerImpl"
+#include "utils/Log.h"
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <OMX_Component.h>
+
+#include <unistd.h>
+
+#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/CachingDataSource.h>
+// #include <media/stagefright/CameraSource.h>
+#include <media/stagefright/HTTPDataSource.h>
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaPlayerImpl.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MmapSource.h>
+#include <media/stagefright/OMXDecoder.h>
+#include <media/stagefright/QComHardwareRenderer.h>
+#include <media/stagefright/ShoutcastSource.h>
+#include <media/stagefright/SoftwareRenderer.h>
+#include <media/stagefright/SurfaceRenderer.h>
+#include <media/stagefright/TimeSource.h>
+#include <ui/PixelFormat.h>
+#include <ui/Surface.h>
+
+namespace android {
+
+MediaPlayerImpl::MediaPlayerImpl(const char *uri)
+    : mInitCheck(NO_INIT),
+      mExtractor(NULL), 
+      mTimeSource(NULL),
+      mAudioSource(NULL),
+      mAudioDecoder(NULL),
+      mAudioPlayer(NULL),
+      mVideoSource(NULL),
+      mVideoDecoder(NULL),
+      mVideoWidth(0),
+      mVideoHeight(0),
+      mVideoPosition(0),
+      mDuration(0),
+      mPlaying(false),
+      mPaused(false),
+      mRenderer(NULL),
+      mSeeking(false),
+      mFrameSize(0),
+      mUseSoftwareColorConversion(false) {
+    LOGI("MediaPlayerImpl(%s)", uri);
+    DataSource::RegisterDefaultSniffers();
+
+    status_t err = mClient.connect();
+    if (err != OK) {
+        LOGE("Failed to connect to OMXClient.");
+        return;
+    }
+
+    if (!strncasecmp("shoutcast://", uri, 12)) {
+        setAudioSource(makeShoutcastSource(uri));
+#if 0
+    } else if (!strncasecmp("camera:", uri, 7)) {
+        mVideoWidth = 480;
+        mVideoHeight = 320;
+        mVideoDecoder = CameraSource::Create();
+#endif
+    } else {
+        DataSource *source = NULL;
+        if (!strncasecmp("file://", uri, 7)) {
+            source = new MmapSource(uri + 7);
+        } else if (!strncasecmp("http://", uri, 7)) {
+            source = new HTTPDataSource(uri);
+            source = new CachingDataSource(source, 64 * 1024, 10);
+        } else {
+            // Assume it's a filename.
+            source = new MmapSource(uri);
+        }
+
+        mExtractor = MediaExtractor::Create(source);
+
+        if (mExtractor == NULL) {
+            return;
+        }
+    }
+
+    init();
+
+    mInitCheck = OK;
+}
+
+MediaPlayerImpl::MediaPlayerImpl(int fd, int64_t offset, int64_t length)
+    : mInitCheck(NO_INIT),
+      mExtractor(NULL), 
+      mTimeSource(NULL),
+      mAudioSource(NULL),
+      mAudioDecoder(NULL),
+      mAudioPlayer(NULL),
+      mVideoSource(NULL),
+      mVideoDecoder(NULL),
+      mVideoWidth(0),
+      mVideoHeight(0),
+      mVideoPosition(0),
+      mDuration(0),
+      mPlaying(false),
+      mPaused(false),
+      mRenderer(NULL),
+      mSeeking(false),
+      mFrameSize(0),
+      mUseSoftwareColorConversion(false) {
+    LOGI("MediaPlayerImpl(%d, %lld, %lld)", fd, offset, length);
+    DataSource::RegisterDefaultSniffers();
+
+    status_t err = mClient.connect();
+    if (err != OK) {
+        LOGE("Failed to connect to OMXClient.");
+        return;
+    }
+
+    mExtractor = MediaExtractor::Create(
+            new MmapSource(fd, offset, length));
+
+    if (mExtractor == NULL) {
+        return;
+    }
+
+    init();
+
+    mInitCheck = OK;
+}
+
+status_t MediaPlayerImpl::initCheck() const {
+    return mInitCheck;
+}
+
+MediaPlayerImpl::~MediaPlayerImpl() {
+    stop();
+    setSurface(NULL);
+
+    LOGV("Shutting down audio.");
+    delete mAudioDecoder;
+    mAudioDecoder = NULL;
+
+    delete mAudioSource;
+    mAudioSource = NULL;
+
+    LOGV("Shutting down video.");
+    delete mVideoDecoder;
+    mVideoDecoder = NULL;
+
+    delete mVideoSource;
+    mVideoSource = NULL;
+
+    delete mExtractor;
+    mExtractor = NULL;
+
+    if (mInitCheck == OK) {
+        mClient.disconnect();
+    }
+
+    LOGV("~MediaPlayerImpl done.");
+}
+
+void MediaPlayerImpl::play() {
+    LOGI("play");
+
+    if (mPlaying) {
+        if (mPaused) {
+            if (mAudioSource != NULL) {
+                mAudioPlayer->resume();
+            }
+            mPaused = false;
+        }
+        return;
+    }
+
+    mPlaying = true;
+
+    if (mAudioSource != NULL) {
+        mAudioPlayer = new AudioPlayer(mAudioSink);
+        mAudioPlayer->setSource(mAudioDecoder);
+        mAudioPlayer->start();
+        mTimeSource = mAudioPlayer;
+    } else {
+        mTimeSource = new SystemTimeSource;
+    }
+
+    if (mVideoDecoder != NULL) {
+        pthread_attr_t attr;
+        pthread_attr_init(&attr);
+        pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+        pthread_create(&mVideoThread, &attr, VideoWrapper, this);
+
+        pthread_attr_destroy(&attr);
+    }
+}
+
+void MediaPlayerImpl::pause() {
+    if (!mPlaying || mPaused) {
+        return;
+    }
+
+    if (mAudioSource != NULL) {
+        mAudioPlayer->pause();
+    }
+
+    mPaused = true;
+}
+
+void MediaPlayerImpl::stop() {
+    if (!mPlaying) {
+        return;
+    }
+
+    mPlaying = false;
+
+    if (mVideoDecoder != NULL) {
+        void *dummy;
+        pthread_join(mVideoThread, &dummy);
+    }
+
+    if (mAudioSource != NULL) {
+        mAudioPlayer->stop();
+
+        delete mAudioPlayer;
+        mAudioPlayer = NULL;
+    } else {
+        delete mTimeSource;
+    }
+
+    mTimeSource = NULL;
+}
+
+// static
+void *MediaPlayerImpl::VideoWrapper(void *me) {
+    ((MediaPlayerImpl *)me)->videoEntry();
+
+    return NULL;
+}
+
+void MediaPlayerImpl::videoEntry() {
+    bool firstFrame = true;
+    bool eof = false;
+
+    status_t err = mVideoDecoder->start();
+    assert(err == OK);
+
+    while (mPlaying) {
+        MediaBuffer *buffer;
+
+        MediaSource::ReadOptions options;
+        bool seeking = false;
+
+        {
+            Mutex::Autolock autoLock(mLock);
+            if (mSeeking) {
+                LOGI("seek-options to %lld", mSeekTimeUs);
+                options.setSeekTo(mSeekTimeUs);
+
+                mSeeking = false;
+                seeking = true;
+                eof = false;
+            }
+        }
+
+        if (eof || mPaused) {
+            usleep(100000);
+            continue;
+        }
+
+        status_t err = mVideoDecoder->read(&buffer, &options);
+        assert((err == OK && buffer != NULL) || (err != OK && buffer == NULL));
+
+        if (err == ERROR_END_OF_STREAM || err != OK) {
+            eof = true;
+            continue;
+        }
+
+        if (buffer->range_length() == 0) {
+            // The final buffer is empty.
+            buffer->release();
+            continue;
+        }
+
+        int32_t units, scale;
+        bool success =
+            buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+        assert(success);
+        success =
+            buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+        assert(success);
+
+        int64_t pts_us = (int64_t)units * 1000000 / scale;
+        {
+            Mutex::Autolock autoLock(mLock);
+            mVideoPosition = pts_us;
+        }
+
+        if (seeking && mAudioPlayer != NULL) {
+            // Now that we know where exactly video seeked (taking sync-samples
+            // into account), we will seek the audio track to the same time.
+            mAudioPlayer->seekTo(pts_us);
+        }
+
+        if (firstFrame || seeking) {
+            mTimeSourceDeltaUs = mTimeSource->getRealTimeUs() - pts_us;
+            firstFrame = false;
+        }
+
+        displayOrDiscardFrame(buffer, pts_us);
+    }
+
+    mVideoDecoder->stop();
+}
+
+void MediaPlayerImpl::displayOrDiscardFrame(
+        MediaBuffer *buffer, int64_t pts_us) {
+    for (;;) {
+        if (!mPlaying || mPaused) {
+            buffer->release();
+            buffer = NULL;
+
+            return;
+        }
+
+        int64_t realtime_us, mediatime_us;
+        if (mAudioPlayer != NULL
+            && mAudioPlayer->getMediaTimeMapping(&realtime_us, &mediatime_us)) {
+            mTimeSourceDeltaUs = realtime_us - mediatime_us;
+        }
+
+        int64_t now_us = mTimeSource->getRealTimeUs();
+        now_us -= mTimeSourceDeltaUs;
+
+        int64_t delay_us = pts_us - now_us;
+
+        if (delay_us < -15000) {
+            // We're late.
+
+            LOGI("we're late by %lld ms, dropping a frame\n",
+                 -delay_us / 1000);
+
+            buffer->release();
+            buffer = NULL;
+            return;
+        } else if (delay_us > 100000) {
+            LOGI("we're much too early (by %lld ms)\n",
+                 delay_us / 1000);
+            usleep(100000);
+            continue;
+        } else if (delay_us > 0) {
+            usleep(delay_us);
+        }
+
+        break;
+    }
+
+    {
+        Mutex::Autolock autoLock(mLock);
+        if (mRenderer != NULL) {
+            sendFrameToISurface(buffer);
+        }
+    }
+
+    buffer->release();
+    buffer = NULL;
+}
+
+void MediaPlayerImpl::init() {
+    if (mExtractor != NULL) {
+        int num_tracks;
+        assert(mExtractor->countTracks(&num_tracks) == OK);
+
+        mDuration = 0;
+
+        for (int i = 0; i < num_tracks; ++i) {
+            const sp<MetaData> meta = mExtractor->getTrackMetaData(i);
+            assert(meta != NULL);
+
+            const char *mime;
+            if (!meta->findCString(kKeyMIMEType, &mime)) {
+                continue;
+            }
+
+            bool is_audio = false;
+            bool is_acceptable = false;
+            if (!strncasecmp(mime, "audio/", 6)) {
+                is_audio = true;
+                is_acceptable = (mAudioSource == NULL);
+            } else if (!strncasecmp(mime, "video/", 6)) {
+                is_acceptable = (mVideoSource == NULL);
+            }
+
+            if (!is_acceptable) {
+                continue;
+            }
+
+            MediaSource *source;
+            if (mExtractor->getTrack(i, &source) != OK) {
+                continue;
+            }
+
+            int32_t units, scale;
+            if (meta->findInt32(kKeyDuration, &units)
+                && meta->findInt32(kKeyTimeScale, &scale)) {
+                int64_t duration_us = (int64_t)units * 1000000 / scale;
+                if (duration_us > mDuration) {
+                    mDuration = duration_us;
+                }
+            }
+
+            if (is_audio) {
+                setAudioSource(source);
+            } else {
+                setVideoSource(source);
+            }
+        }
+    }
+}
+
+void MediaPlayerImpl::setAudioSource(MediaSource *source) {
+    mAudioSource = source;
+
+    sp<MetaData> meta = source->getFormat();
+
+    mAudioDecoder = OMXDecoder::Create(&mClient, meta);
+    mAudioDecoder->setSource(source);
+}
+
+void MediaPlayerImpl::setVideoSource(MediaSource *source) {
+    LOGI("setVideoSource");
+    mVideoSource = source;
+
+    sp<MetaData> meta = source->getFormat();
+
+    bool success = meta->findInt32(kKeyWidth, &mVideoWidth);
+    assert(success);
+
+    success = meta->findInt32(kKeyHeight, &mVideoHeight);
+    assert(success);
+
+    mVideoDecoder = OMXDecoder::Create(&mClient, meta);
+    ((OMXDecoder *)mVideoDecoder)->setSource(source);
+
+    if (mISurface.get() != NULL || mSurface.get() != NULL) {
+        depopulateISurface();
+        populateISurface();
+    }
+}
+
+void MediaPlayerImpl::setSurface(const sp<Surface> &surface) {
+    LOGI("setSurface %p", surface.get());
+    Mutex::Autolock autoLock(mLock);
+
+    depopulateISurface();
+
+    mSurface = surface;
+    mISurface = NULL;
+
+    if (mSurface.get() != NULL) {
+        populateISurface();
+    }
+}
+
+void MediaPlayerImpl::setISurface(const sp<ISurface> &isurface) {
+    LOGI("setISurface %p", isurface.get());
+    Mutex::Autolock autoLock(mLock);
+
+    depopulateISurface();
+
+    mSurface = NULL;
+    mISurface = isurface;
+
+    if (mISurface.get() != NULL) {
+        populateISurface();
+    }
+}
+
+MediaSource *MediaPlayerImpl::makeShoutcastSource(const char *uri) {
+    if (strncasecmp(uri, "shoutcast://", 12)) {
+        return NULL;
+    }
+
+    string host;
+    string path;
+    int port;
+
+    char *slash = strchr(uri + 12, '/');
+    if (slash == NULL) {
+        host = uri + 12;
+        path = "/";
+    } else {
+        host = string(uri + 12, slash - (uri + 12));
+        path = slash;
+    }
+
+    char *colon = strchr(host.c_str(), ':');
+    if (colon == NULL) {
+        port = 80;
+    } else {
+        char *end;
+        long tmp = strtol(colon + 1, &end, 10);
+        assert(end > colon + 1);
+        assert(tmp > 0 && tmp < 65536);
+        port = tmp;
+
+        host = string(host, 0, colon - host.c_str());
+    }
+
+    LOGI("Connecting to host '%s', port %d, path '%s'",
+         host.c_str(), port, path.c_str());
+
+    HTTPStream *http = new HTTPStream;
+    int http_status;
+
+    for (;;) {
+        status_t err = http->connect(host.c_str(), port);
+        assert(err == OK);
+
+        err = http->send("GET ");
+        err = http->send(path.c_str());
+        err = http->send(" HTTP/1.1\r\n");
+        err = http->send("Host: ");
+        err = http->send(host.c_str());
+        err = http->send("\r\n");
+        err = http->send("Icy-MetaData: 1\r\n\r\n");
+
+        assert(OK == http->receive_header(&http_status));
+
+        if (http_status == 301 || http_status == 302) {
+            string location;
+            assert(http->find_header_value("Location", &location));
+
+            assert(string(location, 0, 7) == "http://");
+            location.erase(0, 7);
+            string::size_type slashPos = location.find('/');
+            if (slashPos == string::npos) {
+                slashPos = location.size();
+                location += '/';
+            }
+
+            http->disconnect();
+
+            LOGI("Redirecting to %s\n", location.c_str());
+
+            host = string(location, 0, slashPos);
+
+            string::size_type colonPos = host.find(':');
+            if (colonPos != string::npos) {
+                const char *start = host.c_str() + colonPos + 1;
+                char *end;
+                long tmp = strtol(start, &end, 10);
+                assert(end > start && *end == '\0');
+
+                port = (tmp >= 0 && tmp < 65536) ? (int)tmp : 80;
+            } else {
+                port = 80;
+            }
+
+            path = string(location, slashPos);
+
+            continue;
+        }
+
+        break;
+    }
+
+    if (http_status != 200) {
+        LOGE("Connection failed: http_status = %d", http_status);
+        return NULL;
+    }
+
+    MediaSource *source = new ShoutcastSource(http);
+
+    return source;
+}
+
+bool MediaPlayerImpl::isPlaying() const {
+    return mPlaying && !mPaused;
+}
+
+int64_t MediaPlayerImpl::getDuration() {
+    return mDuration;
+}
+
+int64_t MediaPlayerImpl::getPosition() {
+    int64_t position = 0;
+    if (mVideoSource != NULL) {
+        Mutex::Autolock autoLock(mLock);
+        position = mVideoPosition;
+    } else if (mAudioPlayer != NULL) {
+        position = mAudioPlayer->getMediaTimeUs();
+    }
+
+    return position;
+}
+
+status_t MediaPlayerImpl::seekTo(int64_t time) {
+    LOGI("seekTo %lld", time);
+
+    if (mPaused) {
+        return UNKNOWN_ERROR;
+    }
+
+    if (mVideoSource == NULL && mAudioPlayer != NULL) {
+        mAudioPlayer->seekTo(time);
+    } else {
+        Mutex::Autolock autoLock(mLock);
+        mSeekTimeUs = time;
+        mSeeking = true;
+    }
+
+    return OK;
+}
+
+void MediaPlayerImpl::populateISurface() {
+    if (mVideoSource == NULL) {
+        return;
+    }
+
+    sp<MetaData> meta = mVideoDecoder->getFormat();
+
+    int32_t format;
+    const char *component;
+    int32_t decodedWidth, decodedHeight;
+    bool success = meta->findInt32(kKeyColorFormat, &format);
+    success = success && meta->findCString(kKeyDecoderComponent, &component);
+    success = success && meta->findInt32(kKeyWidth, &decodedWidth);
+    success = success && meta->findInt32(kKeyHeight, &decodedHeight);
+    assert(success);
+
+    if (mSurface.get() != NULL) {
+        mRenderer =
+            new SurfaceRenderer(
+                    mSurface, mVideoWidth, mVideoHeight,
+                    decodedWidth, decodedHeight);
+    } else if (format == OMX_COLOR_FormatYUV420Planar
+        && !strncasecmp(component, "OMX.qcom.video.decoder.", 23)) {
+        mRenderer =
+            new QComHardwareRenderer(
+                    mISurface, mVideoWidth, mVideoHeight,
+                    decodedWidth, decodedHeight);
+    } else {
+        LOGW("Using software renderer.");
+        mRenderer = new SoftwareRenderer(
+                mISurface, mVideoWidth, mVideoHeight,
+                decodedWidth, decodedHeight);
+    }
+}
+
+void MediaPlayerImpl::depopulateISurface() {
+    delete mRenderer;
+    mRenderer = NULL;
+}
+
+void MediaPlayerImpl::sendFrameToISurface(MediaBuffer *buffer) {
+    void *platformPrivate;
+    if (!buffer->meta_data()->findPointer(
+                kKeyPlatformPrivate, &platformPrivate)) {
+        platformPrivate = NULL;
+    }
+
+    mRenderer->render(
+        (const uint8_t *)buffer->data() + buffer->range_offset(),
+        buffer->range_length(),
+        platformPrivate);
+}
+
+void MediaPlayerImpl::setAudioSink(
+        const sp<MediaPlayerBase::AudioSink> &audioSink) {
+    LOGI("setAudioSink %p", audioSink.get());
+    mAudioSink = audioSink;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MediaSource.cpp b/media/libstagefright/MediaSource.cpp
new file mode 100644
index 0000000..ec89b74
--- /dev/null
+++ b/media/libstagefright/MediaSource.cpp
@@ -0,0 +1,60 @@
+/*
+ * Copyright (C) 2009 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 <media/stagefright/MediaSource.h>
+
+namespace android {
+
+MediaSource::MediaSource() {}
+
+MediaSource::~MediaSource() {}
+
+////////////////////////////////////////////////////////////////////////////////
+
+MediaSource::ReadOptions::ReadOptions() {
+    reset();
+}
+
+void MediaSource::ReadOptions::reset() {
+    mOptions = 0;
+    mSeekTimeUs = 0;
+    mLatenessUs = 0;
+}
+
+void MediaSource::ReadOptions::setSeekTo(int64_t time_us) {
+    mOptions |= kSeekTo_Option;
+    mSeekTimeUs = time_us;
+}
+
+void MediaSource::ReadOptions::clearSeekTo() {
+    mOptions &= ~kSeekTo_Option;
+    mSeekTimeUs = 0;
+}
+
+bool MediaSource::ReadOptions::getSeekTo(int64_t *time_us) const {
+    *time_us = mSeekTimeUs;
+    return (mOptions & kSeekTo_Option) != 0;
+}
+
+void MediaSource::ReadOptions::setLateBy(int64_t lateness_us) {
+    mLatenessUs = lateness_us;
+}
+
+int64_t MediaSource::ReadOptions::getLateBy() const {
+    return mLatenessUs;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/MetaData.cpp b/media/libstagefright/MetaData.cpp
new file mode 100644
index 0000000..5d23732b
--- /dev/null
+++ b/media/libstagefright/MetaData.cpp
@@ -0,0 +1,232 @@
+/*
+ * Copyright (C) 2009 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 <assert.h>
+#include <stdlib.h>
+#include <string.h>
+
+#include <media/stagefright/MetaData.h>
+
+namespace android {
+
+MetaData::MetaData() {
+}
+
+MetaData::MetaData(const MetaData &from)
+    : RefBase(),
+      mItems(from.mItems) {
+}
+
+MetaData::~MetaData() {
+    clear();
+}
+
+void MetaData::clear() {
+    mItems.clear();
+}
+
+bool MetaData::remove(uint32_t key) {
+    ssize_t i = mItems.indexOfKey(key);
+
+    if (i < 0) {
+        return false;
+    }
+
+    mItems.removeItemsAt(i);
+
+    return true;
+}
+
+bool MetaData::setCString(uint32_t key, const char *value) {
+    return setData(key, TYPE_C_STRING, value, strlen(value) + 1);
+}
+
+bool MetaData::setInt32(uint32_t key, int32_t value) {
+    return setData(key, TYPE_INT32, &value, sizeof(value));
+}
+
+bool MetaData::setFloat(uint32_t key, float value) {
+    return setData(key, TYPE_FLOAT, &value, sizeof(value));
+}
+
+bool MetaData::setPointer(uint32_t key, void *value) {
+    return setData(key, TYPE_POINTER, &value, sizeof(value));
+}
+
+bool MetaData::findCString(uint32_t key, const char **value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_C_STRING) {
+        return false;
+    }
+
+    *value = (const char *)data;
+
+    return true;
+}
+
+bool MetaData::findInt32(uint32_t key, int32_t *value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_INT32) {
+        return false;
+    }
+
+    assert(size == sizeof(*value));
+
+    *value = *(int32_t *)data;
+
+    return true;
+}
+
+bool MetaData::findFloat(uint32_t key, float *value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_FLOAT) {
+        return false;
+    }
+
+    assert(size == sizeof(*value));
+
+    *value = *(float *)data;
+
+    return true;
+}
+
+bool MetaData::findPointer(uint32_t key, void **value) {
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (!findData(key, &type, &data, &size) || type != TYPE_POINTER) {
+        return false;
+    }
+
+    assert(size == sizeof(*value));
+
+    *value = *(void **)data;
+
+    return true;
+}
+
+bool MetaData::setData(
+        uint32_t key, uint32_t type, const void *data, size_t size) {
+    bool overwrote_existing = true;
+
+    ssize_t i = mItems.indexOfKey(key);
+    if (i < 0) {
+        typed_data item;
+        i = mItems.add(key, item);
+
+        overwrote_existing = false;
+    }
+
+    typed_data &item = mItems.editValueAt(i);
+
+    item.setData(type, data, size);
+
+    return overwrote_existing;
+}
+
+bool MetaData::findData(uint32_t key, uint32_t *type,
+                        const void **data, size_t *size) const {
+    ssize_t i = mItems.indexOfKey(key);
+
+    if (i < 0) {
+        return false;
+    }
+
+    const typed_data &item = mItems.valueAt(i);
+
+    item.getData(type, data, size);
+
+    return true;
+}
+
+MetaData::typed_data::typed_data()
+    : mType(0),
+      mSize(0) {
+}
+
+MetaData::typed_data::~typed_data() {
+    clear();
+}
+
+MetaData::typed_data::typed_data(const typed_data &from)
+    : mType(from.mType),
+      mSize(0) {
+    allocateStorage(from.mSize);
+    memcpy(storage(), from.storage(), mSize);
+}
+
+MetaData::typed_data &MetaData::typed_data::operator=(
+        const MetaData::typed_data &from) {
+    if (this != &from) {
+        clear();
+        mType = from.mType;
+        allocateStorage(from.mSize);
+        memcpy(storage(), from.storage(), mSize);
+    }
+
+    return *this;
+}
+
+void MetaData::typed_data::clear() {
+    freeStorage();
+
+    mType = 0;
+}
+
+void MetaData::typed_data::setData(
+        uint32_t type, const void *data, size_t size) {
+    clear();
+
+    mType = type;
+    allocateStorage(size);
+    memcpy(storage(), data, size);
+}
+
+void MetaData::typed_data::getData(
+        uint32_t *type, const void **data, size_t *size) const {
+    *type = mType;
+    *size = mSize;
+    *data = storage();
+}
+
+void MetaData::typed_data::allocateStorage(size_t size) {
+    mSize = size;
+
+    if (usesReservoir()) {
+        return;
+    }
+
+    u.ext_data = malloc(mSize);
+}
+
+void MetaData::typed_data::freeStorage() {
+    if (!usesReservoir()) {
+        if (u.ext_data) {
+            free(u.ext_data);
+        }
+    }
+
+    mSize = 0;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/MmapSource.cpp b/media/libstagefright/MmapSource.cpp
new file mode 100644
index 0000000..7cb861c
--- /dev/null
+++ b/media/libstagefright/MmapSource.cpp
@@ -0,0 +1,112 @@
+/*
+ * Copyright (C) 2009 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 "MmapSource"
+#include <utils/Log.h>
+
+#include <sys/mman.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <fcntl.h>
+#include <string.h>
+#include <unistd.h>
+
+#include <media/stagefright/MmapSource.h>
+
+namespace android {
+
+MmapSource::MmapSource(const char *filename)
+    : mFd(open(filename, O_RDONLY)),
+      mBase(NULL),
+      mSize(0) {
+    LOGV("MmapSource '%s'", filename);
+    assert(mFd >= 0);
+
+    off_t size = lseek(mFd, 0, SEEK_END);
+    mSize = (size_t)size;
+
+    mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, 0);
+
+    if (mBase == (void *)-1) {
+        mBase = NULL;
+
+        close(mFd);
+        mFd = -1;
+    }
+}
+
+MmapSource::MmapSource(int fd, int64_t offset, int64_t length)
+    : mFd(fd),
+      mBase(NULL),
+      mSize(length) {
+    LOGV("MmapSource fd:%d offset:%lld length:%lld", fd, offset, length);
+    assert(fd >= 0);
+
+    mBase = mmap(0, mSize, PROT_READ, MAP_FILE | MAP_SHARED, mFd, offset);
+
+    if (mBase == (void *)-1) {
+        mBase = NULL;
+
+        close(mFd);
+        mFd = -1;
+    }
+
+}
+
+MmapSource::~MmapSource() {
+    if (mFd != -1) {
+        munmap(mBase, mSize);
+        mBase = NULL;
+        mSize = 0;
+
+        close(mFd);
+        mFd = -1;
+    }
+}
+
+status_t MmapSource::InitCheck() const {
+    return mFd == -1 ? NO_INIT : OK;
+}
+
+ssize_t MmapSource::read_at(off_t offset, void *data, size_t size) {
+    LOGV("read_at offset:%ld data:%p size:%d", offset, data, size);
+    assert(offset >= 0);
+
+    size_t avail = 0;
+    if (offset >= 0 && offset < (off_t)mSize) {
+        avail = mSize - offset;
+    }
+
+    if (size > avail) {
+        size = avail;
+    }
+
+    memcpy(data, (const uint8_t *)mBase + offset, size);
+
+    return (ssize_t)size;
+}
+
+status_t MmapSource::getSize(off_t *size) {
+    *size = mSize;
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
new file mode 100644
index 0000000..1bc8a44
--- /dev/null
+++ b/media/libstagefright/OMXClient.cpp
@@ -0,0 +1,369 @@
+/*
+ * Copyright (C) 2009 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 "OMXClient"
+#include <utils/Log.h>
+
+#include <sys/socket.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/IServiceManager.h>
+#include <media/IMediaPlayerService.h>
+#include <media/IOMX.h>
+#include <media/stagefright/OMXClient.h>
+
+namespace android {
+
+OMXClient::OMXClient()
+    : mSock(-1) {
+}
+
+OMXClient::~OMXClient() {
+    disconnect();
+}
+
+status_t OMXClient::connect() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mSock >= 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    sp<IServiceManager> sm = defaultServiceManager();
+    sp<IBinder> binder = sm->getService(String16("media.player"));
+    sp<IMediaPlayerService> service = interface_cast<IMediaPlayerService>(binder);
+
+    assert(service.get() != NULL);
+
+    mOMX = service->createOMX();
+    assert(mOMX.get() != NULL);
+
+#if IOMX_USES_SOCKETS
+    status_t result = mOMX->connect(&mSock);
+    if (result != OK) {
+        mSock = -1;
+
+        mOMX = NULL;
+        return result;
+    }
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+    assert(err == 0);
+
+    pthread_attr_destroy(&attr);
+#else
+    mReflector = new OMXClientReflector(this);
+#endif
+
+    return OK;
+}
+
+void OMXClient::disconnect() {
+    {
+        Mutex::Autolock autoLock(mLock);
+
+        if (mSock < 0) {
+            return;
+        }
+
+        assert(mObservers.isEmpty());
+    }
+
+#if IOMX_USES_SOCKETS
+    omx_message msg;
+    msg.type = omx_message::DISCONNECT;
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+
+    void *dummy;
+    pthread_join(mThread, &dummy);
+#else
+    mReflector->reset();
+    mReflector.clear();
+#endif
+}
+
+#if IOMX_USES_SOCKETS
+// static
+void *OMXClient::ThreadWrapper(void *me) {
+    ((OMXClient *)me)->threadEntry();
+
+    return NULL;
+}
+
+void OMXClient::threadEntry() {
+    bool done = false;
+    while (!done) {
+        omx_message msg;
+        ssize_t n = recv(mSock, &msg, sizeof(msg), 0);
+
+        if (n <= 0) {
+            break;
+        }
+
+        done = onOMXMessage(msg);
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    close(mSock);
+    mSock = -1;
+}
+#endif
+
+status_t OMXClient::fillBuffer(IOMX::node_id node, IOMX::buffer_id buffer) {
+#if !IOMX_USES_SOCKETS
+    mOMX->fill_buffer(node, buffer);
+#else
+    if (mSock < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    omx_message msg;
+    msg.type = omx_message::FILL_BUFFER;
+    msg.u.buffer_data.node = node;
+    msg.u.buffer_data.buffer = buffer;
+
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+    return OK;
+}
+
+status_t OMXClient::emptyBuffer(
+        IOMX::node_id node, IOMX::buffer_id buffer,
+        OMX_U32 range_offset, OMX_U32 range_length,
+        OMX_U32 flags, OMX_TICKS timestamp) {
+#if !IOMX_USES_SOCKETS
+    mOMX->empty_buffer(
+            node, buffer, range_offset, range_length, flags, timestamp);
+#else
+    if (mSock < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    // XXX I don't like all this copying...
+
+    omx_message msg;
+    msg.type = omx_message::EMPTY_BUFFER;
+    msg.u.extended_buffer_data.node = node;
+    msg.u.extended_buffer_data.buffer = buffer;
+    msg.u.extended_buffer_data.range_offset = range_offset;
+    msg.u.extended_buffer_data.range_length = range_length;
+    msg.u.extended_buffer_data.flags = flags;
+    msg.u.extended_buffer_data.timestamp = timestamp;
+
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+    return OK;
+}
+
+status_t OMXClient::send_command(
+        IOMX::node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+#if !IOMX_USES_SOCKETS
+    return mOMX->send_command(node, cmd, param);
+#else
+    if (mSock < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    omx_message msg;
+    msg.type = omx_message::SEND_COMMAND;
+    msg.u.send_command_data.node = node;
+    msg.u.send_command_data.cmd = cmd;
+    msg.u.send_command_data.param = param;
+
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+    return OK;
+}
+
+status_t OMXClient::registerObserver(
+        IOMX::node_id node, OMXObserver *observer) {
+    Mutex::Autolock autoLock(&mLock);
+
+    ssize_t index = mObservers.indexOfKey(node);
+    if (index >= 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    mObservers.add(node, observer);
+    observer->start();
+
+#if !IOMX_USES_SOCKETS
+    mOMX->observe_node(node, mReflector);
+#endif
+
+    return OK;
+}
+
+void OMXClient::unregisterObserver(IOMX::node_id node) {
+    Mutex::Autolock autoLock(mLock);
+
+    ssize_t index = mObservers.indexOfKey(node);
+    assert(index >= 0);
+
+    if (index < 0) {
+        return;
+    }
+
+    OMXObserver *observer = mObservers.valueAt(index);
+    observer->stop();
+    mObservers.removeItemsAt(index);
+}
+
+bool OMXClient::onOMXMessage(const omx_message &msg) {
+    bool done = false;
+
+    switch (msg.type) {
+        case omx_message::EVENT:
+        {
+            LOGV("OnEvent node:%p event:%d data1:%ld data2:%ld",
+                 msg.u.event_data.node,
+                 msg.u.event_data.event,
+                 msg.u.event_data.data1,
+                 msg.u.event_data.data2);
+
+            break;
+        }
+
+        case omx_message::FILL_BUFFER_DONE:
+        {
+            LOGV("FillBufferDone %p", msg.u.extended_buffer_data.buffer);
+            break;
+        }
+
+        case omx_message::EMPTY_BUFFER_DONE:
+        {
+            LOGV("EmptyBufferDone %p", msg.u.buffer_data.buffer);
+            break;
+        }
+
+#if IOMX_USES_SOCKETS
+        case omx_message::DISCONNECTED:
+        {
+            LOGV("Disconnected");
+            done = true;
+            break;
+        }
+#endif
+
+        default:
+            LOGE("received unknown omx_message type %d", msg.type);
+            break;
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    ssize_t index = mObservers.indexOfKey(msg.u.buffer_data.node);
+
+    if (index >= 0) {
+        mObservers.editValueAt(index)->postMessage(msg);
+    }
+
+    return done;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OMXObserver::OMXObserver() {
+}
+
+OMXObserver::~OMXObserver() {
+}
+
+void OMXObserver::start() {
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+    assert(err == 0);
+
+    pthread_attr_destroy(&attr);
+}
+
+void OMXObserver::stop() {
+    omx_message msg;
+    msg.type = omx_message::QUIT_OBSERVER;
+    postMessage(msg);
+
+    void *dummy;
+    pthread_join(mThread, &dummy);
+}
+
+void OMXObserver::postMessage(const omx_message &msg) {
+    Mutex::Autolock autoLock(mLock);
+    mQueue.push_back(msg);
+    mQueueNotEmpty.signal();
+}
+
+// static
+void *OMXObserver::ThreadWrapper(void *me) {
+    static_cast<OMXObserver *>(me)->threadEntry();
+
+    return NULL;
+}
+
+void OMXObserver::threadEntry() {
+    for (;;) {
+        omx_message msg;
+
+        {
+            Mutex::Autolock autoLock(mLock);
+            while (mQueue.empty()) {
+                mQueueNotEmpty.wait(mLock);
+            }
+
+            msg = *mQueue.begin();
+            mQueue.erase(mQueue.begin());
+        }
+
+        if (msg.type == omx_message::QUIT_OBSERVER) {
+            break;
+        }
+
+        onOMXMessage(msg);
+    }
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+OMXClientReflector::OMXClientReflector(OMXClient *client)
+    : mClient(client) {
+}
+
+void OMXClientReflector::on_message(const omx_message &msg) {
+    if (mClient != NULL) {
+        mClient->onOMXMessage(msg);
+    }
+}
+
+void OMXClientReflector::reset() {
+    mClient = NULL;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/OMXDecoder.cpp b/media/libstagefright/OMXDecoder.cpp
new file mode 100644
index 0000000..c059a9d
--- /dev/null
+++ b/media/libstagefright/OMXDecoder.cpp
@@ -0,0 +1,1329 @@
+/*
+ * Copyright (C) 2009 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 "OMXDecoder"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <OMX_Component.h>
+
+#include <media/stagefright/ESDS.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXDecoder.h>
+
+namespace android {
+
+class OMXMediaBuffer : public MediaBuffer {
+public:
+    OMXMediaBuffer(IOMX::buffer_id buffer_id, const sp<IMemory> &mem)
+        : MediaBuffer(mem->pointer(),
+                      mem->size()),
+          mBufferID(buffer_id),
+          mMem(mem) {
+    }
+
+    IOMX::buffer_id buffer_id() const { return mBufferID; }
+
+private:
+    IOMX::buffer_id mBufferID;
+    sp<IMemory> mMem;
+
+    OMXMediaBuffer(const OMXMediaBuffer &);
+    OMXMediaBuffer &operator=(const OMXMediaBuffer &);
+};
+
+struct CodecInfo {
+    const char *mime;
+    const char *codec;
+};
+
+static const CodecInfo kDecoderInfo[] = {
+    { "audio/mpeg", "OMX.PV.mp3dec" },
+    { "audio/3gpp", "OMX.PV.amrdec" },
+    { "audio/mp4a-latm", "OMX.PV.aacdec" },
+    { "video/mp4v-es", "OMX.qcom.video.decoder.mpeg4" },
+    { "video/mp4v-es", "OMX.PV.mpeg4dec" },
+    { "video/3gpp", "OMX.qcom.video.decoder.h263" },
+    { "video/3gpp", "OMX.PV.h263dec" },
+    { "video/avc", "OMX.qcom.video.decoder.avc" },
+    { "video/avc", "OMX.PV.avcdec" },
+};
+
+static const CodecInfo kEncoderInfo[] = {
+    { "audio/3gpp", "OMX.PV.amrencnb" },
+    { "audio/mp4a-latm", "OMX.PV.aacenc" },
+    { "video/mp4v-es", "OMX.qcom.video.encoder.mpeg4" },
+    { "video/mp4v-es", "OMX.PV.mpeg4enc" },
+    { "video/3gpp", "OMX.qcom.video.encoder.h263" },
+    { "video/3gpp", "OMX.PV.h263enc" },
+    { "video/avc", "OMX.PV.avcenc" },
+};
+
+static const char *GetCodec(const CodecInfo *info, size_t numInfos,
+                            const char *mime, int index) {
+    assert(index >= 0);
+    for(size_t i = 0; i < numInfos; ++i) {
+        if (!strcasecmp(mime, info[i].mime)) {
+            if (index == 0) {
+                return info[i].codec;
+            }
+
+            --index;
+        }
+    }
+
+    return NULL;
+}
+
+// static
+OMXDecoder *OMXDecoder::Create(OMXClient *client, const sp<MetaData> &meta) {
+    const char *mime;
+    bool success = meta->findCString(kKeyMIMEType, &mime);
+    assert(success);
+
+    sp<IOMX> omx = client->interface();
+
+    const char *codec = NULL;
+    IOMX::node_id node = 0;
+    for (int index = 0;; ++index) {
+        codec = GetCodec(
+                kDecoderInfo, sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
+                mime, index);
+
+        if (!codec) {
+            return NULL;
+        }
+
+        LOGI("Attempting to allocate OMX node '%s'", codec);
+
+        status_t err = omx->allocate_node(codec, &node);
+        if (err == OK) {
+            break;
+        }
+    }
+
+    OMXDecoder *decoder = new OMXDecoder(client, node, mime, codec);
+
+    uint32_t type;
+    const void *data;
+    size_t size;
+    if (meta->findData(kKeyESDS, &type, &data, &size)) {
+        ESDS esds((const char *)data, size);
+        assert(esds.InitCheck() == OK);
+        
+        const void *codec_specific_data;
+        size_t codec_specific_data_size;
+        esds.getCodecSpecificInfo(
+                &codec_specific_data, &codec_specific_data_size);
+
+        printf("found codec specific data of size %d\n",
+               codec_specific_data_size);
+
+        decoder->addCodecSpecificData(
+                codec_specific_data, codec_specific_data_size);
+    } else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+        printf("found avcc of size %d\n", size);
+
+        const uint8_t *ptr = (const uint8_t *)data + 6;
+        size -= 6;
+        while (size >= 2) {
+            size_t length = ptr[0] << 8 | ptr[1];
+
+            ptr += 2;
+            size -= 2;
+
+            // printf("length = %d, size = %d\n", length, size);
+
+            assert(size >= length);
+
+            decoder->addCodecSpecificData(ptr, length);
+
+            ptr += length;
+            size -= length;
+
+            if (size <= 1) {
+                break;
+            }
+
+            ptr++;  // XXX skip trailing 0x01 byte???
+            --size;
+        }
+    }
+
+    return decoder;
+}
+
+// static
+OMXDecoder *OMXDecoder::CreateEncoder(
+        OMXClient *client, const sp<MetaData> &meta) {
+    const char *mime;
+    bool success = meta->findCString(kKeyMIMEType, &mime);
+    assert(success);
+
+    sp<IOMX> omx = client->interface();
+
+    const char *codec = NULL;
+    IOMX::node_id node = 0;
+    for (int index = 0;; ++index) {
+        codec = GetCodec(
+                kEncoderInfo, sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
+                mime, index);
+
+        if (!codec) {
+            return NULL;
+        }
+
+        LOGI("Attempting to allocate OMX node '%s'", codec);
+
+        status_t err = omx->allocate_node(codec, &node);
+        if (err == OK) {
+            break;
+        }
+    }
+
+    OMXDecoder *encoder = new OMXDecoder(client, node, mime, codec);
+
+    return encoder;
+}
+
+OMXDecoder::OMXDecoder(OMXClient *client, IOMX::node_id node,
+                       const char *mime, const char *codec)
+    : mClient(client),
+      mOMX(mClient->interface()),
+      mNode(node),
+      mComponentName(strdup(codec)),
+      mIsMP3(!strcasecmp(mime, "audio/mpeg")),
+      mSource(NULL),
+      mCodecSpecificDataIterator(mCodecSpecificData.begin()),
+      mState(OMX_StateLoaded),
+      mPortStatusMask(kPortStatusActive << 2 | kPortStatusActive),
+      mShutdownInitiated(false),
+      mDealer(new MemoryDealer(3 * 1024 * 1024)),
+      mSeeking(false),
+      mStarted(false),
+      mErrorCondition(OK),
+      mReachedEndOfInput(false) {
+    mClient->registerObserver(mNode, this);
+
+    mBuffers.push();  // input buffers
+    mBuffers.push();  // output buffers
+}
+
+OMXDecoder::~OMXDecoder() {
+    if (mStarted) {
+        stop();
+    }
+
+    for (List<CodecSpecificData>::iterator it = mCodecSpecificData.begin();
+         it != mCodecSpecificData.end(); ++it) {
+        free((*it).data);
+    }
+    mCodecSpecificData.clear();
+
+    mClient->unregisterObserver(mNode);
+
+    status_t err = mOMX->free_node(mNode);
+    assert(err == OK);
+    mNode = 0;
+
+    free(mComponentName);
+    mComponentName = NULL;
+}
+
+void OMXDecoder::setSource(MediaSource *source) {
+    Mutex::Autolock autoLock(mLock);
+
+    assert(mSource == NULL);
+
+    mSource = source;
+    setup();
+}
+
+status_t OMXDecoder::start(MetaData *) {
+    assert(!mStarted);
+
+    // mDealer->dump("Decoder Dealer");
+
+    sp<MetaData> params = new MetaData;
+    if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+        params->setInt32(kKeyNeedsNALFraming, true);
+    }
+
+    status_t err = mSource->start(params.get());
+
+    if (err != OK) {
+        return err;
+    }
+
+    postStart();
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t OMXDecoder::stop() {
+    assert(mStarted);
+
+    LOGI("Initiating OMX Node shutdown, busy polling.");
+    initiateShutdown();
+
+    // Important: initiateShutdown must be called first, _then_ release
+    // buffers we're holding onto.
+    while (!mOutputBuffers.empty()) {
+        MediaBuffer *buffer = *mOutputBuffers.begin();
+        mOutputBuffers.erase(mOutputBuffers.begin());
+
+        LOGV("releasing buffer %p.", buffer->data());
+
+        buffer->release();
+        buffer = NULL;
+    }
+
+    int attempt = 1;
+    while (mState != OMX_StateLoaded && attempt < 10) {
+        usleep(100000);
+
+        ++attempt;
+    }
+
+    if (mState != OMX_StateLoaded) {
+        LOGE("!!! OMX Node '%s' did NOT shutdown cleanly !!!", mComponentName);
+    } else {
+        LOGI("OMX Node '%s' has shutdown cleanly.", mComponentName);
+    }
+
+    mSource->stop();
+
+    mCodecSpecificDataIterator = mCodecSpecificData.begin();
+    mShutdownInitiated = false;
+    mSeeking = false;
+    mStarted = false;
+    mErrorCondition = OK;
+    mReachedEndOfInput = false;
+
+    return OK;
+}
+
+sp<MetaData> OMXDecoder::getFormat() {
+    return mOutputFormat;
+}
+
+status_t OMXDecoder::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    assert(mStarted);
+
+    *out = NULL;
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (mErrorCondition != OK && mErrorCondition != ERROR_END_OF_STREAM) {
+        // Errors are sticky.
+        return mErrorCondition;
+    }
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        LOGI("[%s] seeking to %lld us", mComponentName, seekTimeUs);
+
+        mErrorCondition = OK;
+        mReachedEndOfInput = false;
+
+        setPortStatus(kPortIndexInput, kPortStatusFlushing);
+        setPortStatus(kPortIndexOutput, kPortStatusFlushing);
+
+        mSeeking = true;
+        mSeekTimeUs = seekTimeUs;
+
+        while (!mOutputBuffers.empty()) {
+            OMXMediaBuffer *buffer =
+                static_cast<OMXMediaBuffer *>(*mOutputBuffers.begin());
+
+            // We could have used buffer->release() instead, but we're
+            // holding the lock and signalBufferReturned attempts to acquire
+            // the lock.
+            buffer->claim();
+            mBuffers.editItemAt(
+                    kPortIndexOutput).push_back(buffer->buffer_id());
+            buffer = NULL;
+
+            mOutputBuffers.erase(mOutputBuffers.begin());
+        }
+
+        status_t err = mOMX->send_command(mNode, OMX_CommandFlush, -1);
+        assert(err == OK);
+
+        // Once flushing is completed buffers will again be scheduled to be
+        // filled/emptied.
+    }
+
+    while (mErrorCondition == OK && mOutputBuffers.empty()) {
+        mOutputBufferAvailable.wait(mLock);
+    }
+
+    if (!mOutputBuffers.empty()) {
+        MediaBuffer *buffer = *mOutputBuffers.begin();
+        mOutputBuffers.erase(mOutputBuffers.begin());
+
+        *out = buffer;
+
+        return OK;
+    } else {
+        assert(mErrorCondition != OK);
+        return mErrorCondition;
+    }
+}
+
+void OMXDecoder::addCodecSpecificData(const void *data, size_t size) {
+    CodecSpecificData specific;
+    specific.data = malloc(size);
+    memcpy(specific.data, data, size);
+    specific.size = size;
+
+    mCodecSpecificData.push_back(specific);
+    mCodecSpecificDataIterator = mCodecSpecificData.begin();
+}
+
+void OMXDecoder::onOMXMessage(const omx_message &msg) {
+    Mutex::Autolock autoLock(mLock);
+
+    switch (msg.type) {
+        case omx_message::START:
+        {
+            onStart();
+            break;
+        }
+
+        case omx_message::EVENT:
+        {
+            onEvent(msg.u.event_data.event, msg.u.event_data.data1,
+                    msg.u.event_data.data2);
+            break;
+        }
+
+        case omx_message::EMPTY_BUFFER_DONE:
+        {
+            onEmptyBufferDone(msg.u.buffer_data.buffer);
+            break;
+        }
+
+        case omx_message::FILL_BUFFER_DONE:
+        case omx_message::INITIAL_FILL_BUFFER:
+        {
+            onFillBufferDone(msg);
+            break;
+        }
+
+        default:
+            LOGE("received unknown omx_message type %d", msg.type);
+            break;
+    }
+}
+
+void OMXDecoder::setAMRFormat() {
+    OMX_AUDIO_PARAM_AMRTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = kPortIndexInput;
+
+    status_t err =
+        mOMX->get_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+
+    assert(err == NO_ERROR);
+
+    def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+    def.eAMRBandMode = OMX_AUDIO_AMRBandModeNB0;
+
+    err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+    assert(err == NO_ERROR);
+}
+
+void OMXDecoder::setAACFormat() {
+    OMX_AUDIO_PARAM_AACPROFILETYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = kPortIndexInput;
+
+    status_t err =
+        mOMX->get_parameter(mNode, OMX_IndexParamAudioAac, &def, sizeof(def));
+    assert(err == NO_ERROR);
+
+    def.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
+
+    err = mOMX->set_parameter(mNode, OMX_IndexParamAudioAac, &def, sizeof(def));
+    assert(err == NO_ERROR);
+}
+
+void OMXDecoder::setVideoOutputFormat(OMX_U32 width, OMX_U32 height) {
+    LOGI("setVideoOutputFormat width=%ld, height=%ld", width, height);
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+    bool is_encoder = strstr(mComponentName, ".encoder.") != NULL;  // XXX
+
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = is_encoder ? kPortIndexOutput : kPortIndexInput;
+
+    status_t err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+    assert(err == NO_ERROR);
+
+#if 1
+    // XXX Need a (much) better heuristic to compute input buffer sizes.
+    const size_t X = 64 * 1024;
+    if (def.nBufferSize < X) {
+        def.nBufferSize = X;
+    }
+#endif
+
+    assert(def.eDomain == OMX_PortDomainVideo);
+    
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+    // video_def.eCompressionFormat = OMX_VIDEO_CodingAVC;
+    video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+    err = mOMX->set_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == NO_ERROR);
+
+    ////////////////////////////////////////////////////////////////////////////
+
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = is_encoder ? kPortIndexInput : kPortIndexOutput;
+
+    err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == NO_ERROR);
+
+    assert(def.eDomain == OMX_PortDomainVideo);
+    
+    def.nBufferSize =
+        (((width + 15) & -16) * ((height + 15) & -16) * 3) / 2;  // YUV420
+
+    video_def->nFrameWidth = width;
+    video_def->nFrameHeight = height;
+    video_def->nStride = width;
+    // video_def->nSliceHeight = height;
+    video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+//    video_def->eColorFormat = OMX_COLOR_FormatYUV420Planar;
+
+    err = mOMX->set_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == NO_ERROR);
+}
+
+void OMXDecoder::setup() {
+    const sp<MetaData> &meta = mSource->getFormat();
+
+    const char *mime;
+    bool success = meta->findCString(kKeyMIMEType, &mime);
+    assert(success);
+
+    if (!strcasecmp(mime, "audio/3gpp")) {
+        setAMRFormat();
+    } else if (!strcasecmp(mime, "audio/mp4a-latm")) {
+        setAACFormat();
+    } else if (!strncasecmp(mime, "video/", 6)) {
+        int32_t width, height;
+        bool success = meta->findInt32(kKeyWidth, &width);
+        success = success && meta->findInt32(kKeyHeight, &height);
+        assert(success);
+
+        setVideoOutputFormat(width, height);
+    }
+
+    // dumpPortDefinition(0);
+    // dumpPortDefinition(1);
+
+    mOutputFormat = new MetaData;
+    mOutputFormat->setCString(kKeyDecoderComponent, mComponentName);
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = kPortIndexOutput;
+
+    status_t err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == NO_ERROR);
+
+    switch (def.eDomain) {
+        case OMX_PortDomainAudio:
+        {
+            OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
+
+            assert(audio_def->eEncoding == OMX_AUDIO_CodingPCM);
+
+            OMX_AUDIO_PARAM_PCMMODETYPE params;
+            params.nSize = sizeof(params);
+            params.nVersion.s.nVersionMajor = 1;
+            params.nVersion.s.nVersionMinor = 1;
+            params.nPortIndex = kPortIndexOutput;
+
+            err = mOMX->get_parameter(
+                    mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+            assert(err == OK);
+
+            assert(params.eNumData == OMX_NumericalDataSigned);
+            assert(params.nBitPerSample == 16);
+            assert(params.ePCMMode == OMX_AUDIO_PCMModeLinear);
+
+            int32_t numChannels, sampleRate;
+            meta->findInt32(kKeyChannelCount, &numChannels);
+            meta->findInt32(kKeySampleRate, &sampleRate);
+
+            mOutputFormat->setCString(kKeyMIMEType, "audio/raw");
+            mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+            mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+            break;
+        }
+
+        case OMX_PortDomainVideo:
+        {
+            OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+            if (video_def->eCompressionFormat == OMX_VIDEO_CodingUnused) {
+                mOutputFormat->setCString(kKeyMIMEType, "video/raw");
+            } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingMPEG4) {
+                mOutputFormat->setCString(kKeyMIMEType, "video/mp4v-es");
+            } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingH263) {
+                mOutputFormat->setCString(kKeyMIMEType, "video/3gpp");
+            } else if (video_def->eCompressionFormat == OMX_VIDEO_CodingAVC) {
+                mOutputFormat->setCString(kKeyMIMEType, "video/avc");
+            } else {
+                assert(!"Unknown compression format.");
+            }
+
+            if (!strcmp(mComponentName, "OMX.PV.avcdec")) {
+                // This component appears to be lying to me.
+                mOutputFormat->setInt32(
+                        kKeyWidth, (video_def->nFrameWidth + 15) & -16);
+                mOutputFormat->setInt32(
+                        kKeyHeight, (video_def->nFrameHeight + 15) & -16);
+            } else {
+                mOutputFormat->setInt32(kKeyWidth, video_def->nFrameWidth);
+                mOutputFormat->setInt32(kKeyHeight, video_def->nFrameHeight);
+            }
+
+            mOutputFormat->setInt32(kKeyColorFormat, video_def->eColorFormat);
+            break;
+        }
+
+        default:
+        {
+            assert(!"should not be here, neither audio nor video.");
+            break;
+        }
+    }
+}
+
+void OMXDecoder::onStart() {
+    bool needs_qcom_hack =
+        !strncmp(mComponentName, "OMX.qcom.video.", 15);
+
+    if (!needs_qcom_hack) {
+        status_t err =
+            mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+        assert(err == NO_ERROR);
+    }
+
+    allocateBuffers(kPortIndexInput);
+    allocateBuffers(kPortIndexOutput);
+
+    if (needs_qcom_hack) {
+        // XXX this should happen before AllocateBuffers, but qcom's
+        // h264 vdec disagrees.
+        status_t err =
+            mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+        assert(err == NO_ERROR);
+    }
+}
+
+void OMXDecoder::allocateBuffers(OMX_U32 port_index) {
+    assert(mBuffers[port_index].empty());
+
+    OMX_U32 num_buffers;
+    OMX_U32 buffer_size;
+
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nVersion.s.nRevision = 0;
+    def.nVersion.s.nStep = 0;
+    def.nPortIndex = port_index;
+
+    status_t err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == NO_ERROR);
+
+    num_buffers = def.nBufferCountActual;
+    buffer_size = def.nBufferSize;
+
+    LOGV("[%s] port %ld: allocating %ld buffers of size %ld each\n",
+           mComponentName, port_index, num_buffers, buffer_size);
+
+    for (OMX_U32 i = 0; i < num_buffers; ++i) {
+        sp<IMemory> mem = mDealer->allocate(buffer_size);
+        assert(mem.get() != NULL);
+
+        IOMX::buffer_id buffer;
+        status_t err;
+
+        if (port_index == kPortIndexInput
+            && !strncmp(mComponentName, "OMX.qcom.video.encoder.", 23)) {
+            // qcom's H.263 encoder appears to want to allocate its own input
+            // buffers.
+            err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
+            if (err != OK) {
+                LOGE("[%s] allocate_buffer_with_backup failed with error %d",
+                     mComponentName, err);
+            }
+        } else if (port_index == kPortIndexOutput
+            && !strncmp(mComponentName, "OMX.qcom.video.decoder.", 23)) {
+#if 1
+            err = mOMX->allocate_buffer_with_backup(mNode, port_index, mem, &buffer);
+#else
+            // XXX This is fine as long as we are either running the player
+            // inside the media server process or we are using the
+            // QComHardwareRenderer to output the frames.
+            err = mOMX->allocate_buffer(mNode, port_index, buffer_size, &buffer);
+#endif
+            if (err != OK) {
+                LOGE("[%s] allocate_buffer_with_backup failed with error %d",
+                     mComponentName, err);
+            }
+        } else {
+            err = mOMX->use_buffer(mNode, port_index, mem, &buffer);
+            if (err != OK) {
+                LOGE("[%s] use_buffer failed with error %d",
+                     mComponentName, err);
+            }
+        }
+        assert(err == OK);
+
+        LOGV("allocated %s buffer %p.",
+             port_index == kPortIndexInput ? "INPUT" : "OUTPUT",
+             buffer);
+
+        mBuffers.editItemAt(port_index).push_back(buffer);
+        mBufferMap.add(buffer, mem);
+
+        if (port_index == kPortIndexOutput) {
+            OMXMediaBuffer *media_buffer = new OMXMediaBuffer(buffer, mem);
+            media_buffer->setObserver(this);
+
+            mMediaBufferMap.add(buffer, media_buffer);
+        }
+    }
+
+    LOGV("allocate %s buffers done.",
+         port_index == kPortIndexInput ? "INPUT" : "OUTPUT");
+}
+
+void OMXDecoder::onEvent(
+        OMX_EVENTTYPE event, OMX_U32 data1, OMX_U32 data2) {
+    LOGV("[%s] onEvent event=%d, data1=%ld, data2=%ld",
+         mComponentName, event, data1, data2);
+
+    switch (event) {
+        case OMX_EventCmdComplete: {
+            onEventCmdComplete(
+                    static_cast<OMX_COMMANDTYPE>(data1), data2);
+
+            break;
+        }
+
+        case OMX_EventPortSettingsChanged: {
+            onEventPortSettingsChanged(data1);
+            break;
+        }
+
+        case OMX_EventBufferFlag: {
+            // initiateShutdown();
+            break;
+        }
+
+        default:
+            break;
+    }
+}
+
+void OMXDecoder::onEventCmdComplete(OMX_COMMANDTYPE type, OMX_U32 data) {
+    switch (type) {
+        case OMX_CommandStateSet: {
+            OMX_STATETYPE state = static_cast<OMX_STATETYPE>(data);
+            onStateChanged(state);
+            break;
+        }
+
+        case OMX_CommandPortDisable: {
+            OMX_U32 port_index = data;
+            assert(getPortStatus(port_index) == kPortStatusDisabled);
+
+            status_t err =
+                mOMX->send_command(mNode, OMX_CommandPortEnable, port_index);
+
+            allocateBuffers(port_index);
+
+            break;
+        }
+
+        case OMX_CommandPortEnable: {
+            OMX_U32 port_index = data;
+            assert(getPortStatus(port_index) ==kPortStatusDisabled);
+            setPortStatus(port_index, kPortStatusActive);
+
+            assert(port_index == kPortIndexOutput);
+
+            BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
+            while (!obuffers->empty()) {
+                IOMX::buffer_id buffer = *obuffers->begin();
+                obuffers->erase(obuffers->begin());
+
+                status_t err = mClient->fillBuffer(mNode, buffer);
+                assert(err == NO_ERROR);
+            }
+
+            break;
+        }
+
+        case OMX_CommandFlush: {
+            OMX_U32 port_index = data;
+            LOGV("Port %ld flush complete.", port_index);
+            assert(getPortStatus(port_index) == kPortStatusFlushing);
+
+            setPortStatus(port_index, kPortStatusActive);
+            BufferList *buffers = &mBuffers.editItemAt(port_index);
+            while (!buffers->empty()) {
+                IOMX::buffer_id buffer = *buffers->begin();
+                buffers->erase(buffers->begin());
+
+                if (port_index == kPortIndexInput) {
+                    postEmptyBufferDone(buffer);
+                } else {
+                    postInitialFillBuffer(buffer);
+                }
+            }
+            break;
+        }
+
+        default:
+            break;
+    }
+}
+
+void OMXDecoder::onEventPortSettingsChanged(OMX_U32 port_index) {
+    assert(getPortStatus(port_index) == kPortStatusActive);
+    setPortStatus(port_index, kPortStatusDisabled);
+
+    status_t err =
+        mOMX->send_command(mNode, OMX_CommandPortDisable, port_index);
+    assert(err == NO_ERROR);
+}
+
+void OMXDecoder::onStateChanged(OMX_STATETYPE to) {
+    if (mState == OMX_StateLoaded) {
+        assert(to == OMX_StateIdle);
+
+        mState = to;
+
+        status_t err =
+            mOMX->send_command(mNode, OMX_CommandStateSet, OMX_StateExecuting);
+        assert(err == NO_ERROR);
+    } else if (mState == OMX_StateIdle) {
+        if (to == OMX_StateExecuting) {
+            mState = to;
+
+            BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput);
+            while (!ibuffers->empty()) {
+                IOMX::buffer_id buffer = *ibuffers->begin();
+                ibuffers->erase(ibuffers->begin());
+
+                postEmptyBufferDone(buffer);
+            }
+
+            BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
+            while (!obuffers->empty()) {
+                IOMX::buffer_id buffer = *obuffers->begin();
+                obuffers->erase(obuffers->begin());
+
+                postInitialFillBuffer(buffer);
+            }
+        } else {
+            assert(to == OMX_StateLoaded);
+
+            mState = to;
+
+            setPortStatus(kPortIndexInput, kPortStatusActive);
+            setPortStatus(kPortIndexOutput, kPortStatusActive);
+        }
+    } else if (mState == OMX_StateExecuting) {
+        assert(to == OMX_StateIdle);
+
+        mState = to;
+
+        LOGV("Executing->Idle complete, initiating Idle->Loaded");
+        status_t err =
+            mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateLoaded);
+        assert(err == NO_ERROR);
+
+        BufferList *ibuffers = &mBuffers.editItemAt(kPortIndexInput);
+        for (BufferList::iterator it = ibuffers->begin();
+             it != ibuffers->end(); ++it) {
+            freeInputBuffer(*it);
+        }
+        ibuffers->clear();
+
+        BufferList *obuffers = &mBuffers.editItemAt(kPortIndexOutput);
+        for (BufferList::iterator it = obuffers->begin();
+             it != obuffers->end(); ++it) {
+            freeOutputBuffer(*it);
+        }
+        obuffers->clear();
+    }
+}
+
+void OMXDecoder::initiateShutdown() {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mShutdownInitiated) {
+        return;
+    }
+    
+    if (mState == OMX_StateLoaded) {
+        return;
+    }
+
+    assert(mState == OMX_StateExecuting);
+
+    mShutdownInitiated = true;
+
+    status_t err =
+        mClient->send_command(mNode, OMX_CommandStateSet, OMX_StateIdle);
+    assert(err == NO_ERROR);
+
+    setPortStatus(kPortIndexInput, kPortStatusShutdown);
+    setPortStatus(kPortIndexOutput, kPortStatusShutdown);
+}
+
+void OMXDecoder::setPortStatus(OMX_U32 port_index, PortStatus status) {
+    int shift = 2 * port_index;
+
+    mPortStatusMask &= ~(3 << shift);
+    mPortStatusMask |= status << shift;
+}
+
+OMXDecoder::PortStatus OMXDecoder::getPortStatus(
+        OMX_U32 port_index) const {
+    int shift = 2 * port_index;
+
+    return static_cast<PortStatus>((mPortStatusMask >> shift) & 3);
+}
+
+void OMXDecoder::onEmptyBufferDone(IOMX::buffer_id buffer) {
+    LOGV("[%s] onEmptyBufferDone (%p)", mComponentName, buffer);
+
+    status_t err;
+    switch (getPortStatus(kPortIndexInput)) {
+        case kPortStatusDisabled:
+            freeInputBuffer(buffer);
+            err = NO_ERROR;
+            break;
+
+        case kPortStatusShutdown:
+            LOGV("We're shutting down, enqueue INPUT buffer %p.", buffer);
+            mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
+            err = NO_ERROR;
+            break;
+
+        case kPortStatusFlushing:
+            LOGV("We're currently flushing, enqueue INPUT buffer %p.", buffer);
+            mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
+            err = NO_ERROR;
+            break;
+
+        default:
+            onRealEmptyBufferDone(buffer);
+            err = NO_ERROR;
+            break;
+    }
+    assert(err == NO_ERROR);
+}
+
+void OMXDecoder::onFillBufferDone(const omx_message &msg) {
+    IOMX::buffer_id buffer = msg.u.extended_buffer_data.buffer;
+
+    LOGV("[%s] onFillBufferDone (%p)", mComponentName, buffer);
+
+    status_t err;
+    switch (getPortStatus(kPortIndexOutput)) {
+        case kPortStatusDisabled:
+            freeOutputBuffer(buffer);
+            err = NO_ERROR;
+            break;
+        case kPortStatusShutdown:
+            LOGV("We're shutting down, enqueue OUTPUT buffer %p.", buffer);
+            mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
+            err = NO_ERROR;
+            break;
+
+        case kPortStatusFlushing:
+            LOGV("We're currently flushing, enqueue OUTPUT buffer %p.", buffer);
+            mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
+            err = NO_ERROR;
+            break;
+
+        default:
+        {
+            if (msg.type == omx_message::INITIAL_FILL_BUFFER) {
+                err = mClient->fillBuffer(mNode, buffer);
+            } else {
+                LOGV("[%s] Filled OUTPUT buffer %p, flags=0x%08lx.",
+                     mComponentName, buffer, msg.u.extended_buffer_data.flags);
+
+                onRealFillBufferDone(msg);
+                err = NO_ERROR;
+            }
+            break;
+        }
+    }
+    assert(err == NO_ERROR);
+}
+
+void OMXDecoder::onRealEmptyBufferDone(IOMX::buffer_id buffer) {
+    if (mReachedEndOfInput) {
+        // We already sent the EOS notification.
+
+        mBuffers.editItemAt(kPortIndexInput).push_back(buffer);
+        return;
+    }
+
+    const sp<IMemory> mem = mBufferMap.valueFor(buffer);
+    assert(mem.get() != NULL);
+
+    static const uint8_t kNALStartCode[4] = { 0x00, 0x00, 0x00, 0x01 };
+
+    if (mCodecSpecificDataIterator != mCodecSpecificData.end()) {
+        List<CodecSpecificData>::iterator it = mCodecSpecificDataIterator;
+
+        size_t range_length = 0;
+
+        if (!strcmp(mComponentName, "OMX.qcom.video.decoder.avc")) {
+            assert((*mCodecSpecificDataIterator).size + 4 <= mem->size());
+
+            memcpy(mem->pointer(), kNALStartCode, 4);
+
+            memcpy((uint8_t *)mem->pointer() + 4, (*it).data, (*it).size);
+            range_length = (*it).size + 4;
+        } else {
+            assert((*mCodecSpecificDataIterator).size <= mem->size());
+
+            memcpy((uint8_t *)mem->pointer(), (*it).data, (*it).size);
+            range_length = (*it).size;
+        }
+
+        ++mCodecSpecificDataIterator;
+
+        status_t err = mClient->emptyBuffer(
+                mNode, buffer, 0, range_length, 
+                OMX_BUFFERFLAG_ENDOFFRAME | OMX_BUFFERFLAG_CODECCONFIG,
+                0);
+
+        assert(err == NO_ERROR);
+
+        return;
+    }
+
+    LOGV("[%s] waiting for input data", mComponentName);
+
+    MediaBuffer *input_buffer;
+    for (;;) {
+        status_t err;
+
+        if (mSeeking) {
+            MediaSource::ReadOptions options;
+            options.setSeekTo(mSeekTimeUs);
+
+            mSeeking = false;
+
+            err = mSource->read(&input_buffer, &options);
+        } else {
+            err = mSource->read(&input_buffer);
+        }
+        assert((err == OK && input_buffer != NULL)
+               || (err != OK && input_buffer == NULL));
+
+        if (err == ERROR_END_OF_STREAM) {
+            LOGE("[%s] Reached end of stream.", mComponentName);
+            mReachedEndOfInput = true;
+        } else {
+            LOGV("[%s] got input data", mComponentName);
+        }
+
+        if (err != OK) {
+            status_t err2 = mClient->emptyBuffer(
+                    mNode, buffer, 0, 0, OMX_BUFFERFLAG_EOS, 0);
+
+            assert(err2 == NO_ERROR);
+            return;
+        }
+
+        if (mSeeking) {
+            input_buffer->release();
+            input_buffer = NULL;
+
+            continue;
+        }
+
+        break;
+    }
+
+    const uint8_t *src_data =
+        (const uint8_t *)input_buffer->data() + input_buffer->range_offset();
+
+    size_t src_length = input_buffer->range_length();
+    if (src_length == 195840) {
+        // When feeding the output of the AVC decoder into the H263 encoder,
+        // buffer sizes mismatch if width % 16 != 0 || height % 16 != 0.
+        src_length = 194400;  // XXX HACK
+    } else if (src_length == 115200) {
+        src_length = 114240;  // XXX HACK
+    }
+
+    if (src_length > mem->size()) {
+        LOGE("src_length=%d > mem->size() = %d\n",
+             src_length, mem->size());
+    }
+
+    assert(src_length <= mem->size());
+    memcpy(mem->pointer(), src_data, src_length);
+
+    OMX_U32 flags = 0;
+    if (!mIsMP3) {
+        // Only mp3 audio data may be streamed, all other data is assumed
+        // to be fed into the decoder at frame boundaries.
+        flags |= OMX_BUFFERFLAG_ENDOFFRAME;
+    }
+
+    int32_t units, scale;
+    bool success =
+        input_buffer->meta_data()->findInt32(kKeyTimeUnits, &units);
+
+    success = success &&
+        input_buffer->meta_data()->findInt32(kKeyTimeScale, &scale);
+
+    OMX_TICKS timestamp = 0;
+
+    if (success) {
+        // XXX units should be microseconds but PV treats them as milliseconds.
+        timestamp = ((OMX_S64)units * 1000) / scale;
+    }
+
+    input_buffer->release();
+    input_buffer = NULL;
+
+    LOGV("[%s] Calling EmptyBuffer on buffer %p",
+         mComponentName, buffer);
+
+    status_t err2 = mClient->emptyBuffer(
+            mNode, buffer, 0, src_length, flags, timestamp);
+    assert(err2 == OK);
+}
+
+void OMXDecoder::onRealFillBufferDone(const omx_message &msg) {
+    OMXMediaBuffer *media_buffer =
+        mMediaBufferMap.valueFor(msg.u.extended_buffer_data.buffer);
+
+    media_buffer->set_range(
+            msg.u.extended_buffer_data.range_offset,
+            msg.u.extended_buffer_data.range_length);
+
+    media_buffer->add_ref();
+
+    media_buffer->meta_data()->clear();
+
+    media_buffer->meta_data()->setInt32(
+            kKeyTimeUnits, msg.u.extended_buffer_data.timestamp);
+    media_buffer->meta_data()->setInt32(kKeyTimeScale, 1000);
+
+    if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_SYNCFRAME) {
+        media_buffer->meta_data()->setInt32(kKeyIsSyncFrame, true);
+    }
+
+    media_buffer->meta_data()->setPointer(
+            kKeyPlatformPrivate,
+            msg.u.extended_buffer_data.platform_private);
+
+    if (msg.u.extended_buffer_data.flags & OMX_BUFFERFLAG_EOS) {
+        mErrorCondition = ERROR_END_OF_STREAM;
+    }
+
+    mOutputBuffers.push_back(media_buffer);
+    mOutputBufferAvailable.signal();
+}
+
+void OMXDecoder::signalBufferReturned(MediaBuffer *_buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    OMXMediaBuffer *media_buffer = static_cast<OMXMediaBuffer *>(_buffer);
+
+    IOMX::buffer_id buffer = media_buffer->buffer_id();
+
+    PortStatus outputStatus = getPortStatus(kPortIndexOutput);
+    if (outputStatus == kPortStatusShutdown
+            || outputStatus == kPortStatusFlushing) {
+        mBuffers.editItemAt(kPortIndexOutput).push_back(buffer);
+    } else {
+        LOGV("[%s] Calling FillBuffer on buffer %p.", mComponentName, buffer);
+
+        status_t err = mClient->fillBuffer(mNode, buffer);
+        assert(err == NO_ERROR);
+    }
+}
+
+void OMXDecoder::freeInputBuffer(IOMX::buffer_id buffer) {
+    LOGV("freeInputBuffer %p", buffer);
+
+    status_t err = mOMX->free_buffer(mNode, kPortIndexInput, buffer);
+    assert(err == NO_ERROR);
+    mBufferMap.removeItem(buffer);
+
+    LOGV("freeInputBuffer %p done", buffer);
+}
+
+void OMXDecoder::freeOutputBuffer(IOMX::buffer_id buffer) {
+    LOGV("freeOutputBuffer %p", buffer);
+
+    status_t err = mOMX->free_buffer(mNode, kPortIndexOutput, buffer);
+    assert(err == NO_ERROR);
+    mBufferMap.removeItem(buffer);
+
+    ssize_t index = mMediaBufferMap.indexOfKey(buffer);
+    assert(index >= 0);
+    MediaBuffer *mbuffer = mMediaBufferMap.editValueAt(index);
+    mMediaBufferMap.removeItemsAt(index);
+    mbuffer->setObserver(NULL);
+    mbuffer->release();
+    mbuffer = NULL;
+
+    LOGV("freeOutputBuffer %p done", buffer);
+}
+
+void OMXDecoder::dumpPortDefinition(OMX_U32 port_index) {
+    OMX_PARAM_PORTDEFINITIONTYPE def;
+    def.nSize = sizeof(def);
+    def.nVersion.s.nVersionMajor = 1;
+    def.nVersion.s.nVersionMinor = 1;
+    def.nPortIndex = port_index;
+
+    status_t err = mOMX->get_parameter(
+            mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+    assert(err == NO_ERROR);
+
+    LOGI("DumpPortDefinition on port %ld", port_index);
+    LOGI("nBufferCountActual = %ld, nBufferCountMin = %ld, nBufferSize = %ld",
+         def.nBufferCountActual, def.nBufferCountMin, def.nBufferSize);
+    switch (def.eDomain) {
+        case OMX_PortDomainAudio:
+        {
+            LOGI("eDomain = AUDIO");
+
+            if (port_index == kPortIndexOutput) {
+                OMX_AUDIO_PORTDEFINITIONTYPE *audio_def = &def.format.audio;
+                assert(audio_def->eEncoding == OMX_AUDIO_CodingPCM);
+
+                OMX_AUDIO_PARAM_PCMMODETYPE params;
+                params.nSize = sizeof(params);
+                params.nVersion.s.nVersionMajor = 1;
+                params.nVersion.s.nVersionMinor = 1;
+                params.nPortIndex = port_index;
+
+                err = mOMX->get_parameter(
+                        mNode, OMX_IndexParamAudioPcm, &params, sizeof(params));
+                assert(err == OK);
+
+                assert(params.nChannels == 1 || params.bInterleaved);
+                assert(params.eNumData == OMX_NumericalDataSigned);
+                assert(params.nBitPerSample == 16);
+                assert(params.ePCMMode == OMX_AUDIO_PCMModeLinear);
+
+                LOGI("nChannels = %ld, nSamplingRate = %ld",
+                     params.nChannels, params.nSamplingRate);
+            }
+
+            break;
+        }
+
+        case OMX_PortDomainVideo:
+        {
+            LOGI("eDomain = VIDEO");
+
+            OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+            LOGI("nFrameWidth = %ld, nFrameHeight = %ld, nStride = %ld, "
+                 "nSliceHeight = %ld",
+                 video_def->nFrameWidth, video_def->nFrameHeight,
+                 video_def->nStride, video_def->nSliceHeight);
+            LOGI("nBitrate = %ld, xFrameRate = %.2f",
+                 video_def->nBitrate, video_def->xFramerate / 65536.0f);
+            LOGI("eCompressionFormat = %d, eColorFormat = %d",
+                 video_def->eCompressionFormat, video_def->eColorFormat);
+
+            break;
+        }
+
+        default:
+            LOGI("eDomain = UNKNOWN");
+            break;
+    }
+}
+
+void OMXDecoder::postStart() {
+    omx_message msg;
+    msg.type = omx_message::START;
+    postMessage(msg);
+}
+
+void OMXDecoder::postEmptyBufferDone(IOMX::buffer_id buffer) {
+    omx_message msg;
+    msg.type = omx_message::EMPTY_BUFFER_DONE;
+    msg.u.buffer_data.node = mNode;
+    msg.u.buffer_data.buffer = buffer;
+    postMessage(msg);
+}
+
+void OMXDecoder::postInitialFillBuffer(IOMX::buffer_id buffer) {
+    omx_message msg;
+    msg.type = omx_message::INITIAL_FILL_BUFFER;
+    msg.u.buffer_data.node = mNode;
+    msg.u.buffer_data.buffer = buffer;
+    postMessage(msg);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/QComHardwareRenderer.cpp b/media/libstagefright/QComHardwareRenderer.cpp
new file mode 100644
index 0000000..5a23792
--- /dev/null
+++ b/media/libstagefright/QComHardwareRenderer.cpp
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/MemoryHeapBase.h>
+#include <binder/MemoryHeapPmem.h>
+#include <media/stagefright/QComHardwareRenderer.h>
+#include <ui/ISurface.h>
+
+namespace android {
+
+////////////////////////////////////////////////////////////////////////////////
+
+typedef struct PLATFORM_PRIVATE_ENTRY
+{
+    /* Entry type */
+    uint32_t type;
+
+    /* Pointer to platform specific entry */
+    void *entry;
+
+} PLATFORM_PRIVATE_ENTRY;
+
+typedef struct PLATFORM_PRIVATE_LIST
+{
+    /* Number of entries */
+    uint32_t nEntries;
+
+    /* Pointer to array of platform specific entries *
+     * Contiguous block of PLATFORM_PRIVATE_ENTRY elements */
+    PLATFORM_PRIVATE_ENTRY *entryList;
+
+} PLATFORM_PRIVATE_LIST;
+
+// data structures for tunneling buffers
+typedef struct PLATFORM_PRIVATE_PMEM_INFO
+{
+    /* pmem file descriptor */
+    uint32_t pmem_fd;
+    uint32_t offset;
+
+} PLATFORM_PRIVATE_PMEM_INFO;
+
+#define PLATFORM_PRIVATE_PMEM   1
+
+QComHardwareRenderer::QComHardwareRenderer(
+        const sp<ISurface> &surface,
+        size_t displayWidth, size_t displayHeight,
+        size_t decodedWidth, size_t decodedHeight)
+    : mISurface(surface),
+      mDisplayWidth(displayWidth),
+      mDisplayHeight(displayHeight),
+      mDecodedWidth(decodedWidth),
+      mDecodedHeight(decodedHeight),
+      mFrameSize((mDecodedWidth * mDecodedHeight * 3) / 2) {
+    assert(mISurface.get() != NULL);
+    assert(mDecodedWidth > 0);
+    assert(mDecodedHeight > 0);
+}
+
+QComHardwareRenderer::~QComHardwareRenderer() {
+    mISurface->unregisterBuffers();
+}
+
+void QComHardwareRenderer::render(
+        const void *data, size_t size, void *platformPrivate) {
+    size_t offset;
+    if (!getOffset(platformPrivate, &offset)) {
+        return;
+    }
+
+    mISurface->postBuffer(offset);
+}
+
+bool QComHardwareRenderer::getOffset(void *platformPrivate, size_t *offset) {
+    *offset = 0;
+
+    PLATFORM_PRIVATE_LIST *list = (PLATFORM_PRIVATE_LIST *)platformPrivate;
+    for (uint32_t i = 0; i < list->nEntries; ++i) {
+        if (list->entryList[i].type != PLATFORM_PRIVATE_PMEM) {
+            continue;
+        }
+
+        PLATFORM_PRIVATE_PMEM_INFO *info =
+            (PLATFORM_PRIVATE_PMEM_INFO *)list->entryList[i].entry;
+
+        if (info != NULL) {
+            if (mMemoryHeap.get() == NULL) {
+                publishBuffers(info->pmem_fd);
+            }
+
+            if (mMemoryHeap.get() == NULL) {
+                return false;
+            }
+
+            *offset = info->offset;
+
+            return true;
+        }
+    }
+
+    return false;
+}
+
+void QComHardwareRenderer::publishBuffers(uint32_t pmem_fd) {
+    sp<MemoryHeapBase> master =
+        reinterpret_cast<MemoryHeapBase *>(pmem_fd);
+
+    master->setDevice("/dev/pmem");
+
+    mMemoryHeap = new MemoryHeapPmem(master, 0);
+    mMemoryHeap->slap();
+
+    ISurface::BufferHeap bufferHeap(
+            mDisplayWidth, mDisplayHeight,
+            mDecodedWidth, mDecodedHeight,
+            PIXEL_FORMAT_YCbCr_420_SP,
+            mMemoryHeap);
+
+    status_t err = mISurface->registerBuffers(bufferHeap);
+    assert(err == OK);
+}
+
+}  // namespace android
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
new file mode 100644
index 0000000..8f1fa67
--- /dev/null
+++ b/media/libstagefright/SampleTable.cpp
@@ -0,0 +1,598 @@
+/*
+ * Copyright (C) 2009 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_TAG "SampleTable"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <assert.h>
+
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/SampleTable.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+static const uint32_t kChunkOffsetType32 = FOURCC('s', 't', 'c', 'o');
+static const uint32_t kChunkOffsetType64 = FOURCC('c', 'o', '6', '4');
+static const uint32_t kSampleSizeType32 = FOURCC('s', 't', 's', 'z');
+static const uint32_t kSampleSizeTypeCompact = FOURCC('s', 't', 'z', '2');
+
+SampleTable::SampleTable(DataSource *source)
+    : mDataSource(source),
+      mChunkOffsetOffset(-1),
+      mChunkOffsetType(0),
+      mNumChunkOffsets(0),
+      mSampleToChunkOffset(-1),
+      mNumSampleToChunkOffsets(0),
+      mSampleSizeOffset(-1),
+      mSampleSizeFieldSize(0),
+      mDefaultSampleSize(0),
+      mNumSampleSizes(0),
+      mTimeToSampleCount(0),
+      mTimeToSample(NULL),
+      mSyncSampleOffset(-1),
+      mNumSyncSamples(0) {
+}
+
+SampleTable::~SampleTable() {
+    delete[] mTimeToSample;
+    mTimeToSample = NULL;
+}
+
+status_t SampleTable::setChunkOffsetParams(
+        uint32_t type, off_t data_offset, off_t data_size) {
+    if (mChunkOffsetOffset >= 0) {
+        return ERROR_MALFORMED;
+    }
+
+    assert(type == kChunkOffsetType32 || type == kChunkOffsetType64);
+
+    mChunkOffsetOffset = data_offset;
+    mChunkOffsetType = type;
+
+    if (data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->read_at(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mNumChunkOffsets = U32_AT(&header[4]);
+
+    if (mChunkOffsetType == kChunkOffsetType32) {
+        if (data_size < 8 + mNumChunkOffsets * 4) {
+            return ERROR_MALFORMED;
+        }
+    } else {
+        if (data_size < 8 + mNumChunkOffsets * 8) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setSampleToChunkParams(
+        off_t data_offset, off_t data_size) {
+    if (mSampleToChunkOffset >= 0) {
+        return ERROR_MALFORMED;
+    }
+
+    mSampleToChunkOffset = data_offset;
+
+    if (data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->read_at(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mNumSampleToChunkOffsets = U32_AT(&header[4]);
+
+    if (data_size < 8 + mNumSampleToChunkOffsets * 12) {
+        return ERROR_MALFORMED;
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setSampleSizeParams(
+        uint32_t type, off_t data_offset, off_t data_size) {
+    if (mSampleSizeOffset >= 0) {
+        return ERROR_MALFORMED;
+    }
+
+    assert(type == kSampleSizeType32 || type == kSampleSizeTypeCompact);
+
+    mSampleSizeOffset = data_offset;
+
+    if (data_size < 12) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[12];
+    if (mDataSource->read_at(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mDefaultSampleSize = U32_AT(&header[4]);
+    mNumSampleSizes = U32_AT(&header[8]);
+
+    if (type == kSampleSizeType32) {
+        mSampleSizeFieldSize = 32;
+
+        if (mDefaultSampleSize != 0) {
+            return OK;
+        }
+
+        if (data_size < 12 + mNumSampleSizes * 4) {
+            return ERROR_MALFORMED;
+        }
+    } else {
+        if ((mDefaultSampleSize & 0xffffff00) != 0) {
+            // The high 24 bits are reserved and must be 0.
+            return ERROR_MALFORMED;
+        }
+
+        mSampleSizeFieldSize = mDefaultSampleSize & 0xf;
+        mDefaultSampleSize = 0;
+
+        if (mSampleSizeFieldSize != 4 && mSampleSizeFieldSize != 8
+            && mSampleSizeFieldSize != 16) {
+            return ERROR_MALFORMED;
+        }
+
+        if (data_size < 12 + (mNumSampleSizes * mSampleSizeFieldSize + 4) / 8) {
+            return ERROR_MALFORMED;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setTimeToSampleParams(
+        off_t data_offset, off_t data_size) {
+    if (mTimeToSample != NULL || data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    uint8_t header[8];
+    if (mDataSource->read_at(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mTimeToSampleCount = U32_AT(&header[4]);
+    mTimeToSample = new uint32_t[mTimeToSampleCount * 2];
+
+    size_t size = sizeof(uint32_t) * mTimeToSampleCount * 2;
+    if (mDataSource->read_at(
+                data_offset + 8, mTimeToSample, size) < (ssize_t)size) {
+        return ERROR_IO;
+    }
+
+    for (uint32_t i = 0; i < mTimeToSampleCount * 2; ++i) {
+        mTimeToSample[i] = ntohl(mTimeToSample[i]);
+    }
+
+    return OK;
+}
+
+status_t SampleTable::setSyncSampleParams(off_t data_offset, off_t data_size) {
+    if (mSyncSampleOffset >= 0 || data_size < 8) {
+        return ERROR_MALFORMED;
+    }
+
+    mSyncSampleOffset = data_offset;
+
+    uint8_t header[8];
+    if (mDataSource->read_at(
+                data_offset, header, sizeof(header)) < (ssize_t)sizeof(header)) {
+        return ERROR_IO;
+    }
+
+    if (U32_AT(header) != 0) {
+        // Expected version = 0, flags = 0.
+        return ERROR_MALFORMED;
+    }
+
+    mNumSyncSamples = U32_AT(&header[4]);
+
+    if (mNumSyncSamples < 2) {
+        LOGW("Table of sync samples is empty or has only a single entry!");
+    }
+    return OK;
+}
+
+uint32_t SampleTable::countChunkOffsets() const {
+    return mNumChunkOffsets;
+}
+
+status_t SampleTable::getChunkOffset(uint32_t chunk_index, off_t *offset) {
+    *offset = 0;
+
+    if (mChunkOffsetOffset < 0) {
+        return ERROR_MALFORMED;
+    }
+
+    if (chunk_index >= mNumChunkOffsets) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    if (mChunkOffsetType == kChunkOffsetType32) {
+        uint32_t offset32;
+
+        if (mDataSource->read_at(
+                    mChunkOffsetOffset + 8 + 4 * chunk_index,
+                    &offset32,
+                    sizeof(offset32)) < (ssize_t)sizeof(offset32)) {
+            return ERROR_IO;
+        }
+
+        *offset = ntohl(offset32);
+    } else {
+        assert(mChunkOffsetOffset == kChunkOffsetType64);
+
+        uint64_t offset64;
+        if (mDataSource->read_at(
+                    mChunkOffsetOffset + 8 + 8 * chunk_index,
+                    &offset64,
+                    sizeof(offset64)) < (ssize_t)sizeof(offset64)) {
+            return ERROR_IO;
+        }
+
+        *offset = ntoh64(offset64);
+    }
+
+    return OK;
+}
+
+status_t SampleTable::getChunkForSample(
+        uint32_t sample_index,
+        uint32_t *chunk_index, 
+        uint32_t *chunk_relative_sample_index,
+        uint32_t *desc_index) {
+    *chunk_index = 0;
+    *chunk_relative_sample_index = 0;
+    *desc_index = 0;
+
+    if (mSampleToChunkOffset < 0) {
+        return ERROR_MALFORMED;
+    }
+
+    if (sample_index >= countSamples()) {
+        return ERROR_END_OF_STREAM;
+    }
+
+    uint32_t first_chunk = 0;
+    uint32_t samples_per_chunk = 0;
+    uint32_t chunk_desc_index = 0;
+
+    uint32_t index = 0;
+    while (index < mNumSampleToChunkOffsets) {
+        uint8_t buffer[12];
+        if (mDataSource->read_at(mSampleToChunkOffset + 8 + index * 12,
+                                 buffer, sizeof(buffer)) < (ssize_t)sizeof(buffer)) {
+            return ERROR_IO;
+        }
+
+        uint32_t stop_chunk = U32_AT(buffer);
+        if (sample_index < (stop_chunk - first_chunk) * samples_per_chunk) {
+            break;
+        }
+
+        sample_index -= (stop_chunk - first_chunk) * samples_per_chunk;
+        first_chunk = stop_chunk;
+        samples_per_chunk = U32_AT(&buffer[4]);
+        chunk_desc_index = U32_AT(&buffer[8]);
+
+        ++index;
+    }
+
+    *chunk_index = sample_index / samples_per_chunk + first_chunk - 1;
+    *chunk_relative_sample_index = sample_index % samples_per_chunk;
+    *desc_index = chunk_desc_index;
+
+    return OK;
+}
+
+uint32_t SampleTable::countSamples() const {
+    return mNumSampleSizes;
+}
+
+status_t SampleTable::getSampleSize(
+        uint32_t sample_index, size_t *sample_size) {
+    *sample_size = 0;
+
+    if (mSampleSizeOffset < 0) {
+        return ERROR_MALFORMED;
+    }
+
+    if (sample_index >= mNumSampleSizes) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    if (mDefaultSampleSize > 0) {
+        *sample_size = mDefaultSampleSize;
+        return OK;
+    }
+
+    switch (mSampleSizeFieldSize) {
+        case 32:
+        {
+            if (mDataSource->read_at(
+                        mSampleSizeOffset + 12 + 4 * sample_index,
+                        sample_size, sizeof(*sample_size)) < (ssize_t)sizeof(*sample_size)) {
+                return ERROR_IO;
+            }
+
+            *sample_size = ntohl(*sample_size);
+            break;
+        }
+
+        case 16:
+        {
+            uint16_t x;
+            if (mDataSource->read_at(
+                        mSampleSizeOffset + 12 + 2 * sample_index,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *sample_size = ntohs(x);
+            break;
+        }
+
+        case 8:
+        {
+            uint8_t x;
+            if (mDataSource->read_at(
+                        mSampleSizeOffset + 12 + sample_index,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *sample_size = x;
+            break;
+        }
+
+        default:
+        {
+            assert(mSampleSizeFieldSize == 4);
+
+            uint8_t x;
+            if (mDataSource->read_at(
+                        mSampleSizeOffset + 12 + sample_index / 2,
+                        &x, sizeof(x)) < (ssize_t)sizeof(x)) {
+                return ERROR_IO;
+            }
+
+            *sample_size = (sample_index & 1) ? x & 0x0f : x >> 4;
+            break;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleTable::getSampleOffsetAndSize(
+        uint32_t sample_index, off_t *offset, size_t *size) {
+    Mutex::Autolock autoLock(mLock);
+
+    *offset = 0;
+    *size = 0;
+
+    uint32_t chunk_index;
+    uint32_t chunk_relative_sample_index;
+    uint32_t desc_index;
+    status_t err = getChunkForSample(
+            sample_index, &chunk_index, &chunk_relative_sample_index,
+            &desc_index);
+
+    if (err != OK) {
+        return err;
+    }
+
+    err = getChunkOffset(chunk_index, offset);
+
+    if (err != OK) {
+        return err;
+    }
+
+    for (uint32_t j = 0; j < chunk_relative_sample_index; ++j) {
+        size_t sample_size;
+        err = getSampleSize(sample_index - j - 1, &sample_size);
+
+        if (err != OK) {
+            return err;
+        }
+
+        *offset += sample_size;
+    }
+
+    err = getSampleSize(sample_index, size);
+
+    if (err != OK) {
+        return err;
+    }
+
+    return OK;
+}
+
+status_t SampleTable::getMaxSampleSize(size_t *max_size) {
+    Mutex::Autolock autoLock(mLock);
+
+    *max_size = 0;
+
+    for (uint32_t i = 0; i < mNumSampleSizes; ++i) {
+        size_t sample_size;
+        status_t err = getSampleSize(i, &sample_size);
+        
+        if (err != OK) {
+            return err;
+        }
+
+        if (sample_size > *max_size) {
+            *max_size = sample_size;
+        }
+    }
+
+    return OK;
+}
+
+status_t SampleTable::getDecodingTime(uint32_t sample_index, uint32_t *time) {
+    // XXX FIXME idiotic (for the common use-case) O(n) algorithm below...
+
+    Mutex::Autolock autoLock(mLock);
+
+    if (sample_index >= mNumSampleSizes) {
+        return ERROR_OUT_OF_RANGE;
+    }
+
+    uint32_t cur_sample = 0;
+    *time = 0;
+    for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+        uint32_t n = mTimeToSample[2 * i];
+        uint32_t delta = mTimeToSample[2 * i + 1];
+
+        if (sample_index < cur_sample + n) {
+            *time += delta * (sample_index - cur_sample);
+
+            return OK;
+        }
+        
+        *time += delta * n;
+        cur_sample += n;
+    }
+
+    return ERROR_OUT_OF_RANGE;
+}
+
+status_t SampleTable::findClosestSample(
+        uint32_t req_time, uint32_t *sample_index, uint32_t flags) {
+    Mutex::Autolock autoLock(mLock);
+
+    uint32_t cur_sample = 0;
+    uint32_t time = 0;
+    for (uint32_t i = 0; i < mTimeToSampleCount; ++i) {
+        uint32_t n = mTimeToSample[2 * i];
+        uint32_t delta = mTimeToSample[2 * i + 1];
+
+        if (req_time < time + n * delta) {
+            int j = (req_time - time) / delta;
+
+            *sample_index = cur_sample + j;
+
+            if (flags & kSyncSample_Flag) {
+                return findClosestSyncSample(*sample_index, sample_index);
+            }
+
+            return OK;
+        }
+
+        time += delta * n;
+        cur_sample += n;
+    }
+
+    return ERROR_OUT_OF_RANGE;
+}
+
+status_t SampleTable::findClosestSyncSample(
+        uint32_t start_sample_index, uint32_t *sample_index) {
+    *sample_index = 0;
+
+    if (mSyncSampleOffset < 0) {
+        // All samples are sync-samples.
+        *sample_index = start_sample_index;
+        return OK;
+    }
+
+    uint32_t x;
+    uint32_t left = 0;
+    uint32_t right = mNumSyncSamples;
+    while (left < right) {
+        uint32_t mid = (left + right) / 2;
+        if (mDataSource->read_at(
+                    mSyncSampleOffset + 8 + (mid - 1) * 4, &x, 4) != 4) {
+            return ERROR_IO;
+        }
+
+        x = ntohl(x);
+
+        if (x < (start_sample_index + 1)) {
+            left = mid + 1;
+        } else if (x > (start_sample_index + 1)) {
+            right = mid;
+        } else {
+            break;
+        }
+    }
+
+#if 1
+    // Make sure we return a sample at or _after_ the requested one.
+    // Seeking to a particular time in a media source containing audio and
+    // video will most likely be able to sync fairly close to the requested
+    // time in the audio track but may only be able to seek a fair distance
+    // from the requested time in the video track.
+    // If we seek the video track to a time earlier than the audio track,
+    // we'll cause the video track to be late for quite a while, the decoder
+    // trying to catch up.
+    // If we seek the video track to a time later than the audio track,
+    // audio will start playing fine while no video will be output for a
+    // while, the video decoder will not stress the system.
+    if (mDataSource->read_at(
+                mSyncSampleOffset + 8 + (left - 1) * 4, &x, 4) != 4) {
+        return ERROR_IO;
+    }
+    x = ntohl(x);
+    assert((x - 1) >= start_sample_index);
+#endif
+
+    *sample_index = x - 1;
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/ShoutcastSource.cpp b/media/libstagefright/ShoutcastSource.cpp
new file mode 100644
index 0000000..17b626e
--- /dev/null
+++ b/media/libstagefright/ShoutcastSource.cpp
@@ -0,0 +1,154 @@
+/*
+ * Copyright (C) 2009 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 <stdlib.h>
+
+#include <media/stagefright/HTTPStream.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/ShoutcastSource.h>
+#include <media/stagefright/string.h>
+
+namespace android {
+
+ShoutcastSource::ShoutcastSource(HTTPStream *http)
+    : mHttp(http),
+      mMetaDataOffset(0),
+      mBytesUntilMetaData(0),
+      mGroup(NULL),
+      mStarted(false) {
+    string metaint;
+    if (mHttp->find_header_value("icy-metaint", &metaint)) {
+        char *end;
+        const char *start = metaint.c_str();
+        mMetaDataOffset = strtol(start, &end, 10);
+        assert(end > start && *end == '\0');
+        assert(mMetaDataOffset > 0);
+
+        mBytesUntilMetaData = mMetaDataOffset;
+    }
+}
+
+ShoutcastSource::~ShoutcastSource() {
+    if (mStarted) {
+        stop();
+    }
+
+    delete mHttp;
+    mHttp = NULL;
+}
+
+status_t ShoutcastSource::start(MetaData *) {
+    assert(!mStarted);
+
+    mGroup = new MediaBufferGroup;
+    mGroup->add_buffer(new MediaBuffer(4096));  // XXX
+
+    mStarted = true;
+
+    return OK;
+}
+
+status_t ShoutcastSource::stop() {
+    assert(mStarted);
+
+    delete mGroup;
+    mGroup = NULL;
+
+    mStarted = false;
+
+    return OK;
+}
+
+sp<MetaData> ShoutcastSource::getFormat() {
+    sp<MetaData> meta = new MetaData;
+    meta->setCString(kKeyMIMEType, "audio/mpeg");
+    meta->setInt32(kKeySampleRate, 44100);
+    meta->setInt32(kKeyChannelCount, 2);  // XXX
+
+    return meta;
+}
+
+status_t ShoutcastSource::read(
+        MediaBuffer **out, const ReadOptions *options) {
+    assert(mStarted);
+
+    *out = NULL;
+
+    int64_t seekTimeUs;
+    if (options && options->getSeekTo(&seekTimeUs)) {
+        return ERROR_UNSUPPORTED;
+    }
+
+    MediaBuffer *buffer;
+    status_t err = mGroup->acquire_buffer(&buffer);
+    if (err != OK) {
+        return err;
+    }
+
+    *out = buffer;
+
+    size_t num_bytes = buffer->size();
+    if (mMetaDataOffset > 0 && num_bytes > mBytesUntilMetaData) {
+        num_bytes = mBytesUntilMetaData;
+    }
+
+    ssize_t n = mHttp->receive(buffer->data(), num_bytes);
+
+    if (n <= 0) {
+        return (status_t)n;
+    }
+
+    buffer->set_range(0, n);
+
+    mBytesUntilMetaData -= (size_t)n;
+
+    if (mBytesUntilMetaData == 0) {
+        unsigned char num_16_byte_blocks = 0;
+        n = mHttp->receive((char *)&num_16_byte_blocks, 1);
+        assert(n == 1);
+
+        char meta[255 * 16];
+        size_t meta_size = num_16_byte_blocks * 16;
+        size_t meta_length = 0;
+        while (meta_length < meta_size) {
+            n = mHttp->receive(&meta[meta_length], meta_size - meta_length);
+            if (n <= 0) {
+                return (status_t)n;
+            }
+
+            meta_length += (size_t) n;
+        }
+
+        while (meta_length > 0 && meta[meta_length - 1] == '\0') {
+            --meta_length;
+        }
+
+        if (meta_length > 0) {
+            // Technically we should probably attach this meta data to the
+            // next buffer. XXX
+            buffer->meta_data()->setData('shou', 'shou', meta, meta_length);
+        }
+
+        mBytesUntilMetaData = mMetaDataOffset;
+    }
+
+    return OK;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/SoftwareRenderer.cpp b/media/libstagefright/SoftwareRenderer.cpp
new file mode 100644
index 0000000..66b6b07
--- /dev/null
+++ b/media/libstagefright/SoftwareRenderer.cpp
@@ -0,0 +1,173 @@
+/*
+ * Copyright (C) 2009 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_TAG "SoftwareRenderer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <binder/MemoryHeapBase.h>
+#include <media/stagefright/SoftwareRenderer.h>
+#include <ui/ISurface.h>
+
+namespace android {
+
+#define QCOM_YUV        0
+
+SoftwareRenderer::SoftwareRenderer(
+        const sp<ISurface> &surface,
+        size_t displayWidth, size_t displayHeight,
+        size_t decodedWidth, size_t decodedHeight)
+    : mISurface(surface),
+      mDisplayWidth(displayWidth),
+      mDisplayHeight(displayHeight),
+      mDecodedWidth(decodedWidth),
+      mDecodedHeight(decodedHeight),
+      mFrameSize(mDecodedWidth * mDecodedHeight * 2),  // RGB565
+      mMemoryHeap(new MemoryHeapBase(2 * mFrameSize)),
+      mIndex(0) {
+    assert(mISurface.get() != NULL);
+    assert(mDecodedWidth > 0);
+    assert(mDecodedHeight > 0);
+    assert(mMemoryHeap->heapID() >= 0);
+
+    ISurface::BufferHeap bufferHeap(
+            mDisplayWidth, mDisplayHeight,
+            mDecodedWidth, mDecodedHeight,
+            PIXEL_FORMAT_RGB_565,
+            mMemoryHeap);
+
+    status_t err = mISurface->registerBuffers(bufferHeap);
+    assert(err == OK);
+}
+
+SoftwareRenderer::~SoftwareRenderer() {
+    mISurface->unregisterBuffers();
+}
+
+void SoftwareRenderer::render(
+        const void *data, size_t size, void *platformPrivate) {
+    assert(size >= (mDecodedWidth * mDecodedHeight * 3) / 2);
+
+    static const signed kClipMin = -278;
+    static const signed kClipMax = 535;
+    static uint8_t kClip[kClipMax - kClipMin + 1];
+    static uint8_t *kAdjustedClip = &kClip[-kClipMin];
+
+    static bool clipInitialized = false;
+
+    if (!clipInitialized) {
+        for (signed i = kClipMin; i <= kClipMax; ++i) {
+            kClip[i - kClipMin] = (i < 0) ? 0 : (i > 255) ? 255 : (uint8_t)i;
+        }
+        clipInitialized = true;
+    }
+
+    size_t offset = mIndex * mFrameSize;
+
+    void *dst = (uint8_t *)mMemoryHeap->getBase() + offset;
+
+    uint32_t *dst_ptr = (uint32_t *)dst;
+
+    const uint8_t *src_y = (const uint8_t *)data;
+
+    const uint8_t *src_u =
+        (const uint8_t *)src_y + mDecodedWidth * mDecodedHeight;
+
+#if !QCOM_YUV
+    const uint8_t *src_v =
+        (const uint8_t *)src_u + (mDecodedWidth / 2) * (mDecodedHeight / 2);
+#endif
+
+    for (size_t y = 0; y < mDecodedHeight; ++y) {
+        for (size_t x = 0; x < mDecodedWidth; x += 2) {
+            // B = 1.164 * (Y - 16) + 2.018 * (U - 128)
+            // G = 1.164 * (Y - 16) - 0.813 * (V - 128) - 0.391 * (U - 128)
+            // R = 1.164 * (Y - 16) + 1.596 * (V - 128)
+
+            // B = 298/256 * (Y - 16) + 517/256 * (U - 128)
+            // G = .................. - 208/256 * (V - 128) - 100/256 * (U - 128)
+            // R = .................. + 409/256 * (V - 128)
+
+            // min_B = (298 * (- 16) + 517 * (- 128)) / 256 = -277
+            // min_G = (298 * (- 16) - 208 * (255 - 128) - 100 * (255 - 128)) / 256 = -172
+            // min_R = (298 * (- 16) + 409 * (- 128)) / 256 = -223
+
+            // max_B = (298 * (255 - 16) + 517 * (255 - 128)) / 256 = 534
+            // max_G = (298 * (255 - 16) - 208 * (- 128) - 100 * (- 128)) / 256 = 432
+            // max_R = (298 * (255 - 16) + 409 * (255 - 128)) / 256 = 481
+
+            // clip range -278 .. 535
+
+            signed y1 = (signed)src_y[x] - 16;
+            signed y2 = (signed)src_y[x + 1] - 16;
+
+#if QCOM_YUV
+            signed u = (signed)src_u[x & ~1] - 128;
+            signed v = (signed)src_u[(x & ~1) + 1] - 128;
+#else
+            signed u = (signed)src_u[x / 2] - 128;
+            signed v = (signed)src_v[x / 2] - 128;
+#endif
+
+            signed u_b = u * 517;
+            signed u_g = -u * 100;
+            signed v_g = -v * 208;
+            signed v_r = v * 409;
+
+            signed tmp1 = y1 * 298;
+            signed b1 = (tmp1 + u_b) / 256;
+            signed g1 = (tmp1 + v_g + u_g) / 256;
+            signed r1 = (tmp1 + v_r) / 256;
+
+            signed tmp2 = y2 * 298;
+            signed b2 = (tmp2 + u_b) / 256;
+            signed g2 = (tmp2 + v_g + u_g) / 256;
+            signed r2 = (tmp2 + v_r) / 256;
+
+            uint32_t rgb1 =
+                ((kAdjustedClip[r1] >> 3) << 11)
+                | ((kAdjustedClip[g1] >> 2) << 5)
+                | (kAdjustedClip[b1] >> 3);
+
+            uint32_t rgb2 =
+                ((kAdjustedClip[r2] >> 3) << 11)
+                | ((kAdjustedClip[g2] >> 2) << 5)
+                | (kAdjustedClip[b2] >> 3);
+
+            dst_ptr[x / 2] = (rgb2 << 16) | rgb1;
+        }
+
+        src_y += mDecodedWidth;
+
+        if (y & 1) {
+#if QCOM_YUV
+            src_u += mDecodedWidth;
+#else
+            src_u += mDecodedWidth / 2;
+            src_v += mDecodedWidth / 2;
+#endif
+        }
+
+        dst_ptr += mDecodedWidth / 2;
+    }
+
+    mISurface->postBuffer(offset);
+    mIndex = 1 - mIndex;
+}
+
+}  // namespace android
diff --git a/media/libstagefright/SurfaceRenderer.cpp b/media/libstagefright/SurfaceRenderer.cpp
new file mode 100644
index 0000000..e54288d
--- /dev/null
+++ b/media/libstagefright/SurfaceRenderer.cpp
@@ -0,0 +1,69 @@
+/*
+ * Copyright (C) 2009 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_TAG "SurfaceRenderer"
+#include <utils/Log.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/SurfaceRenderer.h>
+#include <ui/Surface.h>
+
+namespace android {
+
+SurfaceRenderer::SurfaceRenderer(
+        const sp<Surface> &surface,
+        size_t displayWidth, size_t displayHeight,
+        size_t decodedWidth, size_t decodedHeight)
+    : mSurface(surface),
+      mDisplayWidth(displayWidth),
+      mDisplayHeight(displayHeight),
+      mDecodedWidth(decodedWidth),
+      mDecodedHeight(decodedHeight) {
+}
+
+SurfaceRenderer::~SurfaceRenderer() {
+}
+
+void SurfaceRenderer::render(
+        const void *data, size_t size, void *platformPrivate) {
+    Surface::SurfaceInfo info;
+    status_t err = mSurface->lock(&info);
+    if (err != OK) {
+        return;
+    }
+
+    const uint8_t *src = (const uint8_t *)data;
+    uint8_t *dst = (uint8_t *)info.bits;
+
+    for (size_t i = 0; i < mDisplayHeight; ++i) {
+        memcpy(dst, src, mDisplayWidth);
+        src += mDecodedWidth;
+        dst += mDisplayWidth;
+    }
+    src += (mDecodedHeight - mDisplayHeight) * mDecodedWidth;
+    
+    for (size_t i = 0; i < (mDisplayHeight + 1) / 2; ++i) {
+        memcpy(dst, src, (mDisplayWidth + 1) & ~1);
+        src += (mDecodedWidth + 1) & ~1;
+        dst += (mDisplayWidth + 1) & ~1;
+    }
+
+    mSurface->unlockAndPost();
+}
+
+}  // namespace android
diff --git a/media/libstagefright/TimeSource.cpp b/media/libstagefright/TimeSource.cpp
new file mode 100644
index 0000000..7deb310
--- /dev/null
+++ b/media/libstagefright/TimeSource.cpp
@@ -0,0 +1,40 @@
+/*
+ * Copyright (C) 2009 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 <sys/time.h>
+
+#include <media/stagefright/TimeSource.h>
+
+namespace android {
+
+SystemTimeSource::SystemTimeSource()
+    : mStartTimeUs(GetSystemTimeUs()) {
+}
+
+int64_t SystemTimeSource::getRealTimeUs() {
+    return GetSystemTimeUs() - mStartTimeUs;
+}
+
+// static
+int64_t SystemTimeSource::GetSystemTimeUs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
new file mode 100644
index 0000000..10cf81a
--- /dev/null
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -0,0 +1,206 @@
+/*
+ * Copyright (C) 2009 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.
+ */
+
+#undef __STRICT_ANSI__
+#define __STDINT_LIMITS
+#include <stdint.h>
+
+#define LOG_TAG "TimedEventQueue"
+#include <utils/Log.h>
+
+#include <sys/time.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include <media/stagefright/TimedEventQueue.h>
+
+namespace android {
+
+TimedEventQueue::TimedEventQueue()
+    : mRunning(false),
+      mStopped(false) {
+}
+
+TimedEventQueue::~TimedEventQueue() {
+    stop();
+}
+
+void TimedEventQueue::start() {
+    if (mRunning) {
+        return;
+    }
+
+    mStopped = false;
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    pthread_create(&mThread, &attr, ThreadWrapper, this);
+
+    pthread_attr_destroy(&attr);
+
+    mRunning = true;
+}
+
+void TimedEventQueue::stop(bool flush) {
+    if (!mRunning) {
+        return;
+    }
+
+    if (flush) {
+        postEventToBack(new StopEvent);
+    } else {
+        postTimedEvent(new StopEvent, INT64_MIN);
+    }
+
+    void *dummy;
+    pthread_join(mThread, &dummy);
+
+    mQueue.clear();
+
+    mRunning = false;
+}
+
+void TimedEventQueue::postEvent(const sp<Event> &event) {
+    // Reserve an earlier timeslot an INT64_MIN to be able to post
+    // the StopEvent to the absolute head of the queue.
+    postTimedEvent(event, INT64_MIN + 1);
+}
+
+void TimedEventQueue::postEventToBack(const sp<Event> &event) {
+    postTimedEvent(event, INT64_MAX);
+}
+
+void TimedEventQueue::postEventWithDelay(
+        const sp<Event> &event, int64_t delay_us) {
+    assert(delay_us >= 0);
+    postTimedEvent(event, getRealTimeUs() + delay_us);
+}
+
+void TimedEventQueue::postTimedEvent(
+        const sp<Event> &event, int64_t realtime_us) {
+    Mutex::Autolock autoLock(mLock);
+
+    List<QueueItem>::iterator it = mQueue.begin();
+    while (it != mQueue.end() && realtime_us >= (*it).realtime_us) {
+        ++it;
+    }
+
+    QueueItem item;
+    item.event = event;
+    item.realtime_us = realtime_us;
+
+    if (it == mQueue.begin()) {
+        mQueueHeadChangedCondition.signal();
+    }
+
+    mQueue.insert(it, item);
+
+    mQueueNotEmptyCondition.signal();
+}
+
+bool TimedEventQueue::cancelEvent(const sp<Event> &event) {
+    Mutex::Autolock autoLock(mLock);
+
+    List<QueueItem>::iterator it = mQueue.begin();
+    while (it != mQueue.end() && (*it).event != event) {
+        ++it;
+    }
+
+    if (it == mQueue.end()) {
+        return false;
+    }
+
+    if (it == mQueue.begin()) {
+        mQueueHeadChangedCondition.signal();
+    }
+
+    mQueue.erase(it);
+
+    return true;
+}
+
+// static
+int64_t TimedEventQueue::getRealTimeUs() {
+    struct timeval tv;
+    gettimeofday(&tv, NULL);
+
+    return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
+
+// static
+void *TimedEventQueue::ThreadWrapper(void *me) {
+    static_cast<TimedEventQueue *>(me)->threadEntry();
+
+    return NULL;
+}
+
+void TimedEventQueue::threadEntry() {
+    for (;;) {
+        int64_t now_us;
+        sp<Event> event;
+
+        {
+            Mutex::Autolock autoLock(mLock);
+
+            if (mStopped) {
+                break;
+            }
+
+            while (mQueue.empty()) {
+                mQueueNotEmptyCondition.wait(mLock);
+            }
+
+            List<QueueItem>::iterator it;
+            for (;;) {
+                it = mQueue.begin();
+
+                now_us = getRealTimeUs();
+                int64_t when_us = (*it).realtime_us;
+
+                int64_t delay_us;
+                if (when_us < 0 || when_us == INT64_MAX) {
+                    delay_us = 0;
+                } else {
+                    delay_us = when_us - now_us;
+                }
+
+                if (delay_us <= 0) {
+                    break;
+                }
+
+                status_t err = mQueueHeadChangedCondition.waitRelative(
+                        mLock, delay_us * 1000);
+
+                if (err == -ETIMEDOUT) {
+                    now_us = getRealTimeUs();
+                    break;
+                }
+            }
+
+            event = (*it).event;
+            mQueue.erase(it);
+        }
+
+        // Fire event with the lock NOT held.
+        event->fire(this, now_us);
+    }
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
new file mode 100644
index 0000000..2720f93
--- /dev/null
+++ b/media/libstagefright/Utils.cpp
@@ -0,0 +1,45 @@
+/*
+ * Copyright (C) 2009 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 <arpa/inet.h>
+
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+uint16_t U16_AT(const uint8_t *ptr) {
+    return ptr[0] << 8 | ptr[1];
+}
+
+uint32_t U32_AT(const uint8_t *ptr) {
+    return ptr[0] << 24 | ptr[1] << 16 | ptr[2] << 8 | ptr[3];
+}
+
+uint64_t U64_AT(const uint8_t *ptr) {
+    return ((uint64_t)U32_AT(ptr)) << 32 | U32_AT(ptr + 4);
+}
+
+// XXX warning: these won't work on big-endian host.
+uint64_t ntoh64(uint64_t x) {
+    return ((uint64_t)ntohl(x & 0xffffffff) << 32) | ntohl(x >> 32);
+}
+
+uint64_t hton64(uint64_t x) {
+    return ((uint64_t)htonl(x & 0xffffffff) << 32) | htonl(x >> 32);
+}
+
+}  // namespace android
+
diff --git a/media/libstagefright/omx/Android.mk b/media/libstagefright/omx/Android.mk
new file mode 100644
index 0000000..9c6d475
--- /dev/null
+++ b/media/libstagefright/omx/Android.mk
@@ -0,0 +1,27 @@
+ifeq ($(BUILD_WITH_STAGEFRIGHT),true)
+
+LOCAL_PATH:= $(call my-dir)
+include $(CLEAR_VARS)
+
+# Set up the OpenCore variables.
+include external/opencore/Config.mk
+LOCAL_C_INCLUDES := $(PV_INCLUDES)
+LOCAL_CFLAGS := $(PV_CFLAGS_MINUS_VISIBILITY)
+
+LOCAL_SRC_FILES:=                 \
+	OMX.cpp
+
+LOCAL_SHARED_LIBRARIES := \
+        libbinder         \
+        libmedia          \
+	libutils          \
+        libui             \
+        libopencore_common
+
+LOCAL_PRELINK_MODULE:= false
+
+LOCAL_MODULE:= libstagefright_omx
+
+include $(BUILD_SHARED_LIBRARY)
+
+endif
diff --git a/media/libstagefright/omx/OMX.cpp b/media/libstagefright/omx/OMX.cpp
new file mode 100644
index 0000000..c18f5ce
--- /dev/null
+++ b/media/libstagefright/omx/OMX.cpp
@@ -0,0 +1,621 @@
+/*
+ * Copyright (C) 2009 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 "OMX"
+#include <utils/Log.h>
+
+#include <sys/socket.h>
+
+#undef NDEBUG
+#include <assert.h>
+
+#include "OMX.h"
+#include "pv_omxcore.h"
+
+#include <binder/IMemory.h>
+
+#include <OMX_Component.h>
+
+namespace android {
+
+class NodeMeta {
+public:
+    NodeMeta(OMX *owner)
+        : mOwner(owner),
+          mHandle(NULL) {
+    }
+
+    OMX *owner() const {
+        return mOwner;
+    }
+
+    void setHandle(OMX_HANDLETYPE handle) {
+        assert(mHandle == NULL);
+        mHandle = handle;
+    }
+
+    OMX_HANDLETYPE handle() const {
+        return mHandle;
+    }
+
+    void setObserver(const sp<IOMXObserver> &observer) {
+        mObserver = observer;
+    }
+
+    sp<IOMXObserver> observer() {
+        return mObserver;
+    }
+
+private:
+    OMX *mOwner;
+    OMX_HANDLETYPE mHandle;
+    sp<IOMXObserver> mObserver;
+
+    NodeMeta(const NodeMeta &);
+    NodeMeta &operator=(const NodeMeta &);
+};
+
+class BufferMeta {
+public:
+    BufferMeta(OMX *owner, const sp<IMemory> &mem, bool is_backup = false)
+        : mOwner(owner),
+          mMem(mem),
+          mIsBackup(is_backup) {
+    }
+
+    BufferMeta(OMX *owner, size_t size)
+        : mOwner(owner),
+          mSize(size),
+          mIsBackup(false) {
+    }
+
+    void CopyFromOMX(const OMX_BUFFERHEADERTYPE *header) {
+        if (!mIsBackup) {
+            return;
+        }
+
+        memcpy((OMX_U8 *)mMem->pointer() + header->nOffset,
+               header->pBuffer + header->nOffset,
+               header->nFilledLen);
+    }
+
+    void CopyToOMX(const OMX_BUFFERHEADERTYPE *header) {
+        if (!mIsBackup) {
+            return;
+        }
+
+        memcpy(header->pBuffer + header->nOffset,
+               (const OMX_U8 *)mMem->pointer() + header->nOffset,
+               header->nFilledLen);
+    }
+
+private:
+    OMX *mOwner;
+    sp<IMemory> mMem;
+    size_t mSize;
+    bool mIsBackup;
+
+    BufferMeta(const BufferMeta &);
+    BufferMeta &operator=(const BufferMeta &);
+};
+
+// static
+OMX_CALLBACKTYPE OMX::kCallbacks = {
+    &OnEvent, &OnEmptyBufferDone, &OnFillBufferDone
+};
+
+// static
+OMX_ERRORTYPE OMX::OnEvent(
+        OMX_IN OMX_HANDLETYPE hComponent,
+        OMX_IN OMX_PTR pAppData,
+        OMX_IN OMX_EVENTTYPE eEvent,
+        OMX_IN OMX_U32 nData1,
+        OMX_IN OMX_U32 nData2,
+        OMX_IN OMX_PTR pEventData) {
+    NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+    return meta->owner()->OnEvent(meta, eEvent, nData1, nData2, pEventData);
+}
+
+// static
+OMX_ERRORTYPE OMX::OnEmptyBufferDone(
+        OMX_IN OMX_HANDLETYPE hComponent,
+        OMX_IN OMX_PTR pAppData,
+        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+    NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+    return meta->owner()->OnEmptyBufferDone(meta, pBuffer);
+}
+
+// static
+OMX_ERRORTYPE OMX::OnFillBufferDone(
+        OMX_IN OMX_HANDLETYPE hComponent,
+        OMX_IN OMX_PTR pAppData,
+        OMX_IN OMX_BUFFERHEADERTYPE* pBuffer) {
+    NodeMeta *meta = static_cast<NodeMeta *>(pAppData);
+    return meta->owner()->OnFillBufferDone(meta, pBuffer);
+}
+
+OMX::OMX()
+#if IOMX_USES_SOCKETS
+    : mSock(-1)
+#endif
+{
+}
+
+OMX::~OMX() {
+#if IOMX_USES_SOCKETS
+    assert(mSock < 0);
+#endif
+}
+
+#if IOMX_USES_SOCKETS
+status_t OMX::connect(int *sd) {
+    Mutex::Autolock autoLock(mLock);
+
+    if (mSock >= 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    int sockets[2];
+    if (socketpair(AF_UNIX, SOCK_DGRAM, 0, sockets) < 0) {
+        return UNKNOWN_ERROR;
+    }
+
+    mSock = sockets[0];
+    *sd = sockets[1];
+
+    pthread_attr_t attr;
+    pthread_attr_init(&attr);
+    pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+    int err = pthread_create(&mThread, &attr, ThreadWrapper, this);
+    assert(err == 0);
+
+    pthread_attr_destroy(&attr);
+
+    return OK;
+}
+
+// static
+void *OMX::ThreadWrapper(void *me) {
+    ((OMX *)me)->threadEntry();
+
+    return NULL;
+}
+
+void OMX::threadEntry() {
+    bool done = false;
+    while (!done) {
+        omx_message msg;
+        ssize_t n = recv(mSock, &msg, sizeof(msg), 0);
+
+        if (n <= 0) {
+            break;
+        }
+
+        Mutex::Autolock autoLock(mLock);
+
+        switch (msg.type) {
+            case omx_message::FILL_BUFFER:
+            {
+                OMX_BUFFERHEADERTYPE *header =
+                    static_cast<OMX_BUFFERHEADERTYPE *>(
+                            msg.u.buffer_data.buffer);
+
+                header->nFilledLen = 0;
+                header->nOffset = 0;
+                header->nFlags = 0;
+
+                NodeMeta *node_meta = static_cast<NodeMeta *>(
+                        msg.u.buffer_data.node);
+                
+                LOGV("FillThisBuffer buffer=%p", header);
+
+                OMX_ERRORTYPE err =
+                    OMX_FillThisBuffer(node_meta->handle(), header);
+                assert(err == OMX_ErrorNone);
+                break;
+            }
+
+            case omx_message::EMPTY_BUFFER:
+            {
+                OMX_BUFFERHEADERTYPE *header =
+                    static_cast<OMX_BUFFERHEADERTYPE *>(
+                            msg.u.extended_buffer_data.buffer);
+
+                header->nFilledLen = msg.u.extended_buffer_data.range_length;
+                header->nOffset = msg.u.extended_buffer_data.range_offset;
+                header->nFlags = msg.u.extended_buffer_data.flags;
+                header->nTimeStamp = msg.u.extended_buffer_data.timestamp;
+
+                BufferMeta *buffer_meta =
+                    static_cast<BufferMeta *>(header->pAppPrivate);
+                buffer_meta->CopyToOMX(header);
+
+                NodeMeta *node_meta = static_cast<NodeMeta *>(
+                        msg.u.extended_buffer_data.node);
+
+                LOGV("EmptyThisBuffer buffer=%p", header);
+
+                OMX_ERRORTYPE err =
+                    OMX_EmptyThisBuffer(node_meta->handle(), header);
+                assert(err == OMX_ErrorNone);
+                break;
+            }
+
+            case omx_message::SEND_COMMAND:
+            {
+                NodeMeta *node_meta = static_cast<NodeMeta *>(
+                        msg.u.send_command_data.node);
+
+                OMX_ERRORTYPE err =
+                    OMX_SendCommand(
+                            node_meta->handle(), msg.u.send_command_data.cmd,
+                            msg.u.send_command_data.param, NULL);
+                assert(err == OMX_ErrorNone);
+                break;
+            }
+
+            case omx_message::DISCONNECT:
+            {
+                omx_message msg;
+                msg.type = omx_message::DISCONNECTED;
+                ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+                assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+                done = true;
+                break;
+            }
+
+            default:
+                LOGE("received unknown omx_message type %d", msg.type);
+                break;
+        }
+    }
+
+    Mutex::Autolock autoLock(mLock);
+    close(mSock);
+    mSock = -1;
+}
+#endif
+
+status_t OMX::list_nodes(List<String8> *list) {
+    OMX_MasterInit();  // XXX Put this somewhere else.
+
+    list->clear();
+
+    OMX_U32 index = 0;
+    char componentName[256];
+    while (OMX_MasterComponentNameEnum(componentName, sizeof(componentName), index)
+               == OMX_ErrorNone) {
+        list->push_back(String8(componentName));
+
+        ++index;
+    }
+
+    return OK;
+}
+
+status_t OMX::allocate_node(const char *name, node_id *node) {
+    Mutex::Autolock autoLock(mLock);
+
+    *node = 0;
+
+    OMX_MasterInit();  // XXX Put this somewhere else.
+
+    NodeMeta *meta = new NodeMeta(this);
+
+    OMX_HANDLETYPE handle;
+    OMX_ERRORTYPE err = OMX_MasterGetHandle(
+            &handle, const_cast<char *>(name), meta, &kCallbacks);
+
+    if (err != OMX_ErrorNone) {
+        LOGE("FAILED to allocate omx component '%s'", name);
+
+        delete meta;
+        meta = NULL;
+
+        return UNKNOWN_ERROR;
+    }
+
+    meta->setHandle(handle);
+
+    *node = meta;
+
+    return OK;
+}
+
+status_t OMX::free_node(node_id node) {
+    Mutex::Autolock autoLock(mLock);
+
+    NodeMeta *meta = static_cast<NodeMeta *>(node);
+
+    OMX_ERRORTYPE err = OMX_MasterFreeHandle(meta->handle());
+
+    delete meta;
+    meta = NULL;
+
+    return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::send_command(
+        node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param) {
+    Mutex::Autolock autoLock(mLock);
+
+#if IOMX_USES_SOCKETS
+    if (mSock < 0) {
+        return UNKNOWN_ERROR;
+    }
+#endif
+
+    NodeMeta *meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err = OMX_SendCommand(meta->handle(), cmd, param, NULL);
+
+    return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::get_parameter(
+        node_id node, OMX_INDEXTYPE index,
+        void *params, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    NodeMeta *meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err = OMX_GetParameter(meta->handle(), index, params);
+
+    return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::set_parameter(
+        node_id node, OMX_INDEXTYPE index,
+        const void *params, size_t size) {
+    Mutex::Autolock autoLock(mLock);
+
+    NodeMeta *meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err =
+        OMX_SetParameter(meta->handle(), index, const_cast<void *>(params));
+
+    return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+status_t OMX::use_buffer(
+        node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+        buffer_id *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    BufferMeta *buffer_meta = new BufferMeta(this, params);
+
+    OMX_BUFFERHEADERTYPE *header;
+
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err =
+        OMX_UseBuffer(node_meta->handle(), &header, port_index, buffer_meta,
+                      params->size(), static_cast<OMX_U8 *>(params->pointer()));
+
+    if (err != OMX_ErrorNone) {
+        LOGE("OMX_UseBuffer failed with error %d (0x%08x)", err, err);
+
+        delete buffer_meta;
+        buffer_meta = NULL;
+
+        *buffer = 0;
+        return UNKNOWN_ERROR;
+    }
+
+    *buffer = header;
+
+    return OK;
+}
+
+status_t OMX::allocate_buffer(
+        node_id node, OMX_U32 port_index, size_t size,
+        buffer_id *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    BufferMeta *buffer_meta = new BufferMeta(this, size);
+
+    OMX_BUFFERHEADERTYPE *header;
+
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err =
+        OMX_AllocateBuffer(node_meta->handle(), &header, port_index,
+                           buffer_meta, size);
+
+    if (err != OMX_ErrorNone) {
+        delete buffer_meta;
+        buffer_meta = NULL;
+
+        *buffer = 0;
+        return UNKNOWN_ERROR;
+    }
+
+    *buffer = header;
+
+    return OK;
+}
+
+status_t OMX::allocate_buffer_with_backup(
+        node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+        buffer_id *buffer) {
+    Mutex::Autolock autoLock(mLock);
+
+    BufferMeta *buffer_meta = new BufferMeta(this, params, true);
+
+    OMX_BUFFERHEADERTYPE *header;
+
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err =
+        OMX_AllocateBuffer(
+                node_meta->handle(), &header, port_index, buffer_meta,
+                params->size());
+
+    if (err != OMX_ErrorNone) {
+        delete buffer_meta;
+        buffer_meta = NULL;
+
+        *buffer = 0;
+        return UNKNOWN_ERROR;
+    }
+
+    *buffer = header;
+
+    return OK;
+}
+
+status_t OMX::free_buffer(node_id node, OMX_U32 port_index, buffer_id buffer) {
+    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+    BufferMeta *buffer_meta = static_cast<BufferMeta *>(header->pAppPrivate);
+
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+    OMX_ERRORTYPE err =
+        OMX_FreeBuffer(node_meta->handle(), port_index, header);
+
+    delete buffer_meta;
+    buffer_meta = NULL;
+
+    return (err != OMX_ErrorNone) ? UNKNOWN_ERROR : OK;
+}
+
+OMX_ERRORTYPE OMX::OnEvent(
+        NodeMeta *meta,
+        OMX_IN OMX_EVENTTYPE eEvent,
+        OMX_IN OMX_U32 nData1,
+        OMX_IN OMX_U32 nData2,
+        OMX_IN OMX_PTR pEventData) {
+    LOGV("OnEvent(%d, %ld, %ld)", eEvent, nData1, nData2);
+
+    omx_message msg;
+    msg.type = omx_message::EVENT;
+    msg.u.event_data.node = meta;
+    msg.u.event_data.event = eEvent;
+    msg.u.event_data.data1 = nData1;
+    msg.u.event_data.data2 = nData2;
+
+#if !IOMX_USES_SOCKETS
+    sp<IOMXObserver> observer = meta->observer();
+    if (observer.get() != NULL) {
+        observer->on_message(msg);
+    }
+#else
+    assert(mSock >= 0);
+
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+    return OMX_ErrorNone;
+}
+    
+OMX_ERRORTYPE OMX::OnEmptyBufferDone(
+        NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+    LOGV("OnEmptyBufferDone buffer=%p", pBuffer);
+
+    omx_message msg;
+    msg.type = omx_message::EMPTY_BUFFER_DONE;
+    msg.u.buffer_data.node = meta;
+    msg.u.buffer_data.buffer = pBuffer;
+
+#if !IOMX_USES_SOCKETS
+    sp<IOMXObserver> observer = meta->observer();
+    if (observer.get() != NULL) {
+        observer->on_message(msg);
+    }
+#else
+    assert(mSock >= 0);
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+    return OMX_ErrorNone;
+}
+
+OMX_ERRORTYPE OMX::OnFillBufferDone(
+        NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer) {
+    LOGV("OnFillBufferDone buffer=%p", pBuffer);
+    BufferMeta *buffer_meta = static_cast<BufferMeta *>(pBuffer->pAppPrivate);
+    buffer_meta->CopyFromOMX(pBuffer);
+
+    omx_message msg;
+    msg.type = omx_message::FILL_BUFFER_DONE;
+    msg.u.extended_buffer_data.node = meta;
+    msg.u.extended_buffer_data.buffer = pBuffer;
+    msg.u.extended_buffer_data.range_offset = pBuffer->nOffset;
+    msg.u.extended_buffer_data.range_length = pBuffer->nFilledLen;
+    msg.u.extended_buffer_data.flags = pBuffer->nFlags;
+    msg.u.extended_buffer_data.timestamp = pBuffer->nTimeStamp;
+    msg.u.extended_buffer_data.platform_private = pBuffer->pPlatformPrivate;
+
+#if !IOMX_USES_SOCKETS
+    sp<IOMXObserver> observer = meta->observer();
+    if (observer.get() != NULL) {
+        observer->on_message(msg);
+    }
+#else
+    assert(mSock >= 0);
+
+    ssize_t n = send(mSock, &msg, sizeof(msg), 0);
+    assert(n > 0 && static_cast<size_t>(n) == sizeof(msg));
+#endif
+
+    return OMX_ErrorNone;
+}
+
+#if !IOMX_USES_SOCKETS
+status_t OMX::observe_node(
+        node_id node, const sp<IOMXObserver> &observer) {
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+    node_meta->setObserver(observer);
+
+    return OK;
+}
+
+void OMX::fill_buffer(node_id node, buffer_id buffer) {
+    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+    header->nFilledLen = 0;
+    header->nOffset = 0;
+    header->nFlags = 0;
+
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+    OMX_ERRORTYPE err =
+        OMX_FillThisBuffer(node_meta->handle(), header);
+    assert(err == OMX_ErrorNone);
+}
+
+void OMX::empty_buffer(
+        node_id node,
+        buffer_id buffer,
+        OMX_U32 range_offset, OMX_U32 range_length,
+        OMX_U32 flags, OMX_TICKS timestamp) {
+    OMX_BUFFERHEADERTYPE *header = (OMX_BUFFERHEADERTYPE *)buffer;
+    header->nFilledLen = range_length;
+    header->nOffset = range_offset;
+    header->nFlags = flags;
+    header->nTimeStamp = timestamp;
+
+    BufferMeta *buffer_meta =
+        static_cast<BufferMeta *>(header->pAppPrivate);
+    buffer_meta->CopyToOMX(header);
+
+    NodeMeta *node_meta = static_cast<NodeMeta *>(node);
+
+    OMX_ERRORTYPE err =
+        OMX_EmptyThisBuffer(node_meta->handle(), header);
+    assert(err == OMX_ErrorNone);
+}
+#endif
+
+}  // namespace android
+
diff --git a/media/libstagefright/omx/OMX.h b/media/libstagefright/omx/OMX.h
new file mode 100644
index 0000000..ed4e5dd
--- /dev/null
+++ b/media/libstagefright/omx/OMX.h
@@ -0,0 +1,132 @@
+/*
+ * Copyright (C) 2009 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 ANDROID_OMX_H_
+#define ANDROID_OMX_H_
+
+#include <pthread.h>
+
+#include <media/IOMX.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class NodeMeta;
+
+class OMX : public BnOMX {
+public:
+    OMX();
+    virtual ~OMX();
+
+#if IOMX_USES_SOCKETS
+    virtual status_t connect(int *sd);
+#endif
+
+    virtual status_t list_nodes(List<String8> *list);
+
+    virtual status_t allocate_node(const char *name, node_id *node);
+    virtual status_t free_node(node_id node);
+
+    virtual status_t send_command(
+            node_id node, OMX_COMMANDTYPE cmd, OMX_S32 param);
+
+    virtual status_t get_parameter(
+            node_id node, OMX_INDEXTYPE index,
+            void *params, size_t size);
+
+    virtual status_t set_parameter(
+            node_id node, OMX_INDEXTYPE index,
+            const void *params, size_t size);
+
+    virtual status_t use_buffer(
+            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+            buffer_id *buffer);
+
+    virtual status_t allocate_buffer(
+            node_id node, OMX_U32 port_index, size_t size,
+            buffer_id *buffer);
+
+    virtual status_t allocate_buffer_with_backup(
+            node_id node, OMX_U32 port_index, const sp<IMemory> &params,
+            buffer_id *buffer);
+
+    virtual status_t free_buffer(
+            node_id node, OMX_U32 port_index, buffer_id buffer);
+
+#if !IOMX_USES_SOCKETS
+    virtual status_t observe_node(
+            node_id node, const sp<IOMXObserver> &observer);
+
+    virtual void fill_buffer(node_id node, buffer_id buffer);
+
+    virtual void empty_buffer(
+            node_id node,
+            buffer_id buffer,
+            OMX_U32 range_offset, OMX_U32 range_length,
+            OMX_U32 flags, OMX_TICKS timestamp);
+#endif
+
+private:
+    static OMX_CALLBACKTYPE kCallbacks;
+
+#if IOMX_USES_SOCKETS
+    int mSock;
+    pthread_t mThread;
+
+    static void *ThreadWrapper(void *me);
+    void threadEntry();
+#endif
+
+    Mutex mLock;
+
+    static OMX_ERRORTYPE OnEvent(
+            OMX_IN OMX_HANDLETYPE hComponent,
+            OMX_IN OMX_PTR pAppData,
+            OMX_IN OMX_EVENTTYPE eEvent,
+            OMX_IN OMX_U32 nData1,
+            OMX_IN OMX_U32 nData2,
+            OMX_IN OMX_PTR pEventData);
+
+    static OMX_ERRORTYPE OnEmptyBufferDone(
+            OMX_IN OMX_HANDLETYPE hComponent,
+            OMX_IN OMX_PTR pAppData,
+            OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+
+    static OMX_ERRORTYPE OnFillBufferDone(
+            OMX_IN OMX_HANDLETYPE hComponent,
+            OMX_IN OMX_PTR pAppData,
+            OMX_IN OMX_BUFFERHEADERTYPE* pBuffer);
+
+    OMX_ERRORTYPE OnEvent(
+            NodeMeta *meta,
+            OMX_IN OMX_EVENTTYPE eEvent,
+            OMX_IN OMX_U32 nData1,
+            OMX_IN OMX_U32 nData2,
+            OMX_IN OMX_PTR pEventData);
+        
+    OMX_ERRORTYPE OnEmptyBufferDone(
+            NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+    OMX_ERRORTYPE OnFillBufferDone(
+            NodeMeta *meta, OMX_IN OMX_BUFFERHEADERTYPE *pBuffer);
+
+    OMX(const OMX &);
+    OMX &operator=(const OMX &);
+};
+
+}  // namespace android
+
+#endif  // ANDROID_OMX_H_
diff --git a/media/libstagefright/string.cpp b/media/libstagefright/string.cpp
new file mode 100644
index 0000000..5b16784
--- /dev/null
+++ b/media/libstagefright/string.cpp
@@ -0,0 +1,83 @@
+/*
+ * Copyright (C) 2009 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 <media/stagefright/string.h>
+
+namespace android {
+
+// static
+string::size_type string::npos = (string::size_type)-1;
+
+string::string() {
+}
+
+string::string(const char *s, size_t length)
+    : mString(s, length) {
+}
+
+string::string(const string &from, size_type start, size_type length)
+    : mString(from.c_str() + start, length) {
+}
+
+string::string(const char *s)
+    : mString(s) {
+}
+
+const char *string::c_str() const {
+    return mString.string();
+}
+
+string::size_type string::size() const {
+    return mString.length();
+}
+
+void string::clear() {
+    mString = String8();
+}
+
+string::size_type string::find(char c) const {
+    char s[2];
+    s[0] = c;
+    s[1] = '\0';
+
+    ssize_t index = mString.find(s);
+
+    return index < 0 ? npos : (size_type)index;
+}
+
+bool string::operator<(const string &other) const {
+    return mString < other.mString;
+}
+
+bool string::operator==(const string &other) const {
+    return mString == other.mString;
+}
+
+string &string::operator+=(char c) {
+    mString.append(&c, 1);
+
+    return *this;
+}
+
+void string::erase(size_t from, size_t length) {
+    String8 s(mString.string(), from);
+    s.append(mString.string() + from + length);
+    
+    mString = s;
+}
+
+}  // namespace android
+