frameworks/av: Squashed commit of media features from CAF
* Completely synced with kk_2.7_rb1.9 branch, as of:
libmedia: Add QC enhancements to MediaProfiles.
* Following patch reverted for now, because it doesn't compile:
frameworks/av: Enable subsystem re-start for LPA/Tunnel
---
MediaProfiles: Add support for camcorder video resolutions.
Added video resolutions to support camcorder recording.
(cherry picked from commit I2291de43ae2be7338c673f2f0417ec5c1581391e)
Change-Id: I309fa41cfa36ee0551a8a7c53072089858a2c66a
(cherry picked from commit 805cb98f40acfb242206b7db71c50bc56ea77d2a)
(cherry picked from commit 68c1f41295f4be54ed13af20ebd1041890d2f3ca)
(cherry picked from commit 5a74f7e0e0fbec707333a1f7481d85961aff3908)
frameworks/av: Add support for FM feature
Add FM handle key and volume key to AudioParamater
class which is used to initimate Audio HAL to
that FM device is connected and apply correct
volume to FM stream
Change-Id: If5fa14faf7946ab27eb42b89546dc2274b24f754
(cherry picked from commit 165f20759433d0137a292b457aa783f801f13277)
(cherry picked from commit c98b484de0877e159f00b3b01d622b6539f0b2bc)
(cherry picked from commit f752d489bb85a94b81d38c72333409b4384d860a)
libstagefright:add enhancements for extended format support
Extended-extractor to support extended container format.
- Introduce an Extended Extractor to facilitate plugging-in
of parsers extensions.
Extended-codec to configure codec specific configurations.
- configures omx component with extensions to augment
container/codec format-suppor
Extended-utility to add utility methods and shell extensions.
- Utility methods for supporting following features
- High Frame Rate (HFR) recording
- Support for B frame encoding
Change-Id: I77a40af701916f1803fa9c6af3c43a312a8d2c98
(cherry picked from commit ab6a474cd9acc7bc54c54a7c235f6eab032349ca)
(cherry picked from commit fe07c231dbecd255aedbf7aac597260831eb98cf)
(cherry picked from commit df7b013ceeeb4697f448a2d150ae75b86c325013)
libstagefright:enable extended codec/format support
Configure omx component with extended extensions and
register exended-extractor to augment container/codec
support.
Change-Id: I0bfa4bd6c4dfed93a96e1af431018de54d6fb57b
(cherry picked from commit 318c38f40b53e1987473b29a359e586615bf3c02)
(cherry picked from commit 4a0e6d65c200604a9618e2fc90326031153e6f08)
(cherry picked from commit f9b65059fabc1c24ae5107b54f96a017e8fc7376)
libstagefright: For interlace, set decoder in arbitrary mode
In arbitrary mode, frame parsers sends individual fields
to core. Once both the field is decoded, output done (FBD)
is sent back to client.
Default frame by frame mode will send both the fields in
a single buffer to core, which might decode only one field
or will throw up error.
CRs-Fixed: 531034
Change-Id: I31844093b6a93d3a62577fc8d799353d30573c17
(cherry picked from commit cb45f5a702992242538fab6d9e2c36b7b8e55c7f)
(cherry picked from commit 214938ef4f9997233a33a24e9e9e9751a1fa1882)
(cherry picked from commit 646e3bdce1c8241fb46802e7eba3c4f8a528517d)
audio: add support for MSM audio formats
- Add support for EVRC, QCELP, AMRWB+, and WMA formats
- Refernece change If110058284c8b3e18c2f242f560fc7748f62081c form jb-mr2
Change-Id: Ie20f2d61f79d2ba41d552718187b7098141e7b8c
(cherry picked from commit bca561e38853351c0bae15c5d201dbca7f5a027e)
(cherry picked from commit a11972f505000be397ee3a377ec42bf9e5fef420)
(cherry picked from commit 7465e6129b291c66dc7741325a90e3725e40e941)
libstagefright: fix metadata key sizes
- some wma metadata keys should be treated as 32 bit keys instead
of 64 bits.
- fixes wma lossless from failing
Change-Id: Ic6af8392aea6a1bd8810dac59e1d1c27c21c4059
(cherry picked from commit 1fae0bcb8ef9244e1f28d21c36e388ab365e3c0f)
(cherry picked from commit 97be73e3004a838227060f026f38c7231dd7e2f7)
(cherry picked from commit 4d24c91e49c89eadb7b48e47e38a9b7b6df8f1cf)
frameworks/av: Add support for LPA and Tunnel Playback
-Add DirectTrack and DirectTrackClient
-DirectTrack enables client to create a direct
output
-DirectTrackClient allows notifications to be sent to the
client from DirectTrack.
-DirectTrack is being used for Low Power Audio
playback and Tunnel Audio playback
Change-Id: I265ae1fa32c6fdd8be0382cc2b28605bc756a2f3
(cherry picked from commit b255c1963bb3af6f547b68d646737a7c92739555)
(cherry picked from commit 7cb21f77ae28f58da1cb8ba8f684b2968dcd4122)
(cherry picked from commit d4fa9e74c1ee1c2f7f7262c245da0b94dc0ac05b)
frameworks/av: port fixes in LPA and Tunnel mode playback
No History was maintained during porting. Updating the Description
and CR's of all the fixes.
Reference point: AU_LINUX_ANDROID_JB_MR1.04.02.02.049.366
LPA Mode:
LPAPlayer synchronization fixes.
Fixes for seek, pause/resume, EOS handling
Fixes for synchronization b/w the decoder thread, TimedEventQueue and player thread.
Convert mono to stereo for LPA clips - CRs-Fixed: 421639
Add support to change clip duration to enable LPA
Set correct device while creating effects - CRs-Fixed: 496866
Fix the LPA-AudioEffects dead lock issue - CRs-Fixed: 477511
fix volume not updated in pause state of direct track - CRs-Fixed: 490592
Tunnel Mode:
Use tunnel player only for music stream
Exceptions in using Tunnel mode decode - CRs-Fixed: 432080
Enable Tunnel Decode for select formats - CRs-Fixed:437651
Use software decoder for ADTS content. - CRs-fixed: 431096
Skip tunnel mode for playback through AudioCache - CRs-Fixed: 437539
Fix stability crash on tunnel playback. - CRs-Fixed: 498386
support multiple tunnel instances - CRs-Fixed: 483603
Fix ANR repeated next with tunnel clips on BT/AOA - CRs-Fixed: 482966
Update condition to ignore a seek request - CRs-Fixed: 461908
Return seek position until seek has been processed - CRs-Fixed: 454825
TunnelPlayer performance tweaks for audio video playback - CRs-Fixed: 444041
Stability fixes for Tunnel Player (part 2) - CRs-Fixed: 449122
Adjustment for TunnelPlayer buffer size - CRs-Fixed: 447274, 442365
Remove unnecessary code from TunnelPlayer - CRs-Fixed: 442365
update condition to send SEEK_COMPLETE - CRs-Fixed: 441411
Stop extractor source after start in TunnelPlayer - CRs-Fixed: 440239
Handling of EOS, and triggering EOS was wrong in TunnelPlayer.
EOS should not be posted till seek is complete
Disable tunnel mode playback for streaming use cases to avoid jittery playback
CRs-Fixed: 433346 432233 429868
APIs for returning correct timestamps were implemented
Check whether sink is open before flushing or closing it.
Check for mIsAudioRouted is good to know if we are closing it
Seekbar freezes after seek and pause
Check pause status before writing
Change-Id: Iea150f8d75aa9aa6d14da078695e920a3742dd81
(cherry picked from commit f7c9bafa439c5d52d945b966ba08cb16dfc34818)
(cherry picked from commit 68dee8112bbac7e0a70003175ed71b4e6a0376f4)
(cherry picked from commit 19c7fd3e330be551d477e8388b536294b1166a22)
frameworks/av: port fixes in LPA and Tunnel mode playback draft -2
-LPAPlayer: Update condition to ignore seek CRs-Fixed: 453067
-libstagefright: TunnelPlayer: update condition to send SEEK_COMPLETE
CRs-Fixed: 441411
-libstagefright: Fix ANR with rigorous rewind of 3gp LPA clips
CRs-Fixed: 491077 490992
-frameworks/av: Add metadata mode changes to LPAPlayer
CRs-Fixed: 458904 491077 490992
Change-Id: Ia89d7d6ac63b1267422a9138df9f98ef59b0ccb7
(cherry picked from commit 72f3bd4cddd3d93812692bffbfc19e2534477938)
(cherry picked from commit db6330d01d91d466d18c7714bd024a10fb009f57)
(cherry picked from commit 416e9ef0eed0ffa7b6e2c9d5a107d1ed2a94a624)
frameworks/av: port fixes in LPA effects
-audio: fix for deadlock issue during LPA playback.
-frameworks/av: Fix the LPA-AudioEffects dead lock issue CRs-Fixed: 477511
-audioflinger: Fix pops in LPA while effects are applied CRs-Fixed: 526799
-audioflinger: Fix delay in enabling effect for LPA CRs-Fixed: 525510
Change-Id: I92118b5cba17f5337608007052e7dc891d2d6b5b
(cherry picked from commit b24a38903f3fc2fd2bf7cc8fb71d50e1ff38162d)
(cherry picked from commit e8a1d6f80e64822d83f04f4f0704890644048189)
(cherry picked from commit 571f36890abe9f6b8bfe7341776cb5b3975bf952)
frameworks/av: Use the correct macro for AV enhancement
- Use correct macro for AV enhancement
- Add HW aac encoder entry in codec list
Change-Id: I2933ee92ff15875144acec17d3dd8ef61d6b37f9
(cherry picked from commit 5bb728d3bb84f2f24ddfbaa99fa133c87ff66298)
(cherry picked from commit dbc1e5bde1564e80052086a7ee301173515c8d64)
(cherry picked from commit 941f87a69f87dbeab0068f3e9a98c97981ff2700)
libstagefright: Add support for H/W AAC decoder
- Currently, only software AAC decoding is supported.
- Add support for H/W AAC decoding by including it in the
list of available decoders and use it for decoding only
if the property 'media.aaccodectype' is set to 0.
Change-Id: I35caba6fb694d17b51a0a9983a5fc5f28cb375ec
CRs-Fixed: 449145
(cherry picked from commit a87c3159a34da86e62c029a06e8e39bef66ae2db)
(cherry picked from commit 6a47c5cb0fe741f31b36bee7e74bd953a3ad3bcc)
(cherry picked from commit 8f6bacb4427d3c84af00b06a8ceb1bb324550acc)
frameworks/av: Compile AudioParameter as shared library
- AudioParameter as shared lib is needed by BT,
support in WFD source, SRS TruMedia
Change-Id: I30ee584ba7f45f5cafbdf07c45aa7429a84b1b49
(cherry picked from commit 30b89b3bfdafa2f3b877644400efbaefd6572f1e)
(cherry picked from commit 84c3b42f25a28d14426dba29276aae8c5ab795ce)
(cherry picked from commit 53c42631eeb1e13b786d9f4f616f38fe59690e4e)
frameworks/av: Define MSM Audio parameters
- Define MSM audio paramters for FM, VOIP,
fluence, SSR, and etc.
Change-Id: Iec6f146a0dde3095e3380da439a883b1bbb7d462
(cherry picked from commit 8e943f5bd88bda87f1aa9230487a69372ad49cf7)
(cherry picked from commit 85f4743d12c13aab39ac712a9005632f427c1c43)
(cherry picked from commit 333ba54561c59049f695b07479afe1d5ed734d31)
frameworsk/av: SSR bring-up
- Initial bring up for Surround Sound recording
- 5.1 channel AAC recording and LPCM 5.1 channel
recording supported
Change-Id: I0d237dc91b74ce6e6550243c9e48cf358734634b
(cherry picked from commit 9f5c9b8eb2f34a0431621b0b54b1b0ea4914224a)
(cherry picked from commit d551917f9e65a0468bd838d4ffbf21006426b4c5)
(cherry picked from commit 236c0861d24eab95f2f6e8941394b38cdd2e15c8)
libmediaplayerservice: Add support for DashPlayer
Add support for DashPlayer in MedaPlayerFactor
Change-Id: I3e1fd871340117cb11975f6819d4bf0e6630607a
(cherry picked from commit f0dcae2a6bf22c8c5846b235f3ae900b8b824f79)
(cherry picked from commit d73fd11ce138f661770e809b959a17dac975b284)
(cherry picked from commit 8d3b0b273ddd2ff08707d33b647d8ee6db3c7598)
frameworks/av: Enable subsystem re-start for LPA/Tunnel
-Audio Daemon in Audio HAL intimates AudioFlinger
of ADSP status (online/offline) using set_paramaters.
-AudioFlinger send set_parameters to its clients
which is DirectTrack used for LPA/Tunnel playback.
-In case of ADSP crash during LPA/Tunnel playback,
clean up of DirectTrack is initiated.
Change-Id: I87f720f305765e00e12032eed3e018aeff952390
(cherry picked from commit 02861e0f092c46640576bb8c700418ed82c266c3)
(cherry picked from commit 5abe97aa9e103420813b87e7abb2b676ffe8d097)
(cherry picked from commit 9a27f1e6c449585095e518f0d2f44e621ef18f52)
frameworks/av: Add support for Quality Of Experience (QOE) events in MediaPlayer
- Add MEDIA_QOE event type
- Allow MEDIA_QOE event to be passed to Application after reset
is completed, similar to MEDIA_ERROR
Change-Id: I71e3d9e1f5f934c5844d80c65c653b2702a22ca1
(cherry picked from commit a3320b6e617d1143bf2135abfaaff28365513de4)
(cherry picked from commit 3ca9bdff130c204678def4578fc7a06fd8b3a03a)
(cherry picked from commit d13bd4be1a5418cb806029ba65854c13608cbb0c)
frameworks/av: fix for testAACEncoder CTS testcase
- testAACEncoders CTS testcase is failing for
Hardware aac encoder use case
- hardware encoder has bitrate limitation
due to which CTS testcase is failing
- Removed hardware AAC encoder component from MediaCodec list
- Updated the macro for av enhancements in OMXCodec
Change-Id: I3140898f466a600a4488306701aa16e194df8cc6
(cherry picked from commit a084948269982c868916833546c4b56acdeb1de9)
(cherry picked from commit b5cff08f65b0811efec6b739c98b3aa42d1dd3f7)
(cherry picked from commit 96b425ed8125a7f535572276c2efd09ec81a6a59)
audio : init rc to avoid effect's fault state
Effect command doesn't necessarily fill cmd return code into &cmdStatus,
so we should initialize cmdStatus to avoid uninitialized value propagates
to upper stack caller.
Change-Id: I2a2985e3010f0d7b8aedf9244a6faabdf05740ac
CRs-Fixed: 517766
Conflicts:
services/audioflinger/Effects.cpp
(cherry picked from commit 708dd6e07fc363c76ee9d26a4bc6bb6d7cd3815e)
(cherry picked from commit 0f864db4f4c30c0f31a6998301dd4ec0098a5b24)
(cherry picked from commit 70047618259f68ce74349ed33edffd0ab5a467cc)
audio: Add pause support for hardware omx component
-ADSP doesn't enter sleep state after wma playback is paused
and power suspended.
-No support for NT session pause in case of hardware component.
NT session need to be paused to put ADSP into power collapse.
-Add support of pause in stagefright to ensure device enters
suspend mode. Also add intermediate states to avoid concurrency
issues between read and pause.
Change-Id: Ia0d77a3a0a344b8caad4f4d395641fd6a82f80b9
CRs-Fixed: 496256
(cherry picked from commit 369536f28349e7d1e3033c9d6205c692e32d0751)
(cherry picked from commit 1dcd1d7eba1d82c14961b05dab4e854ed2a979b3)
(cherry picked from commit 0a1b5cbced55d0718d96eccab9e2fe06aa68b199)
audio: Fix for race conditions in direct audio track
-Issue: Mediaserver crash is observed in direct track functions.
-Rootcause: Between write and flush functions, write and destructor
effectpool buffers are accessed without any synchronization that
might lead to operations on null buffers and cause crash.
-Fix: Use mEffectLock to synchronize access of effectpool buffers.
Change-Id: I5982e31fd6d292a9fe28dfee4cf75ebfe0ade5e6
CRs-Fixed: 515107
(cherry picked from commit b14c28f0d3b51ee989640aa46f3addcfabccb6be)
(cherry picked from commit 782f8512023facb1b655fb636bdcbd19af21b493)
(cherry picked from commit 1601287d6a06e11d782a821791bf1f5a9dcbd188)
audio: Correct the condition that checks for wma version
-Few wma clips fail to play when tried to play from music app.
-In overrideComponentName(), a wrong condition is added due to which
component name is always returned null. Due to this all wma clips use
wma component irrespective of the version. This causes playback
failure for other version clips.
-Correct this condition to ensure that all wma clips play fine.
Change-Id: I3a724755fb254bedfc0d3a6dba46c019bc3535bb
CRs-Fixed: 548188
(cherry picked from commit a716cf363b4c7e89fba11547732bbdd9ceff185b)
(cherry picked from commit b7e09e104afb3eda1f2ce95f5ac25fca6483fa28)
(cherry picked from commit bb37a77adbfc6eabbb2c15ea9c7fcae113b6fa9d)
audio: Fix to prevent deadlock in AudioEffects
-Write is blocked waiting for effect chain lock and this causes
decoder thread to wait indefintely.
-Sometimes it is observed that effectschain is locked before
mLPAEffectChain is initialized and but unlocking is skipped if
mLPAEffectChain is initialized in between.Due to this LPA
silence and framework reboot issues are observed as
applyEffectsOn() cannot acquire lock to progress further.
-Use flag to check if all effects have been locked and unlock
accordingly to prevent the deadlock scenario.
CRs-Fixed: 524759
Change-Id: I8ea0b1fc97de157b8254688b5471fd21bc9b565d
(cherry picked from commit 64dda9be5640f02c923cb7d5a08036ebb49ce86f)
(cherry picked from commit 9056fa50895243fbb1672eb117d60004757e7deb)
(cherry picked from commit b43e3b9859ddd90f512c80b637186f2a6e54a835)
audio: check for divide by zero possibilities
When output stream is not available to audioflinger
due to any reason, sampleRate and frameCount have
zero values when trying to create new Audiotrack.
This might result in divide by 0 situation.
Change-Id: If7fbcb6eb4cfcdba6d6bb3b4f758e46e4a57e37b
(cherry picked from commit d1576c1648a78354535fc9cd7130412f300864e5)
(cherry picked from commit 15b951bfcb2180f728669ab23ef0d6791fd790c2)
(cherry picked from commit c980dbea39f984a3c28e2694174e87b311e77308)
audio: Fix crash in flinger during monkey runs
The trackrefptr in DirectTrack descriptor can be invalid and
this can result in a crash is stability runs. Add check for
validity of the trackrefptr before accessing it
Change-Id: I4c7fbff022f381f9d2cdbf0fc04ade8defbb2195
(cherry picked from commit 66917496a28241bf38f117cb05b5810021a185a0)
(cherry picked from commit e1c916bb324f4f6f7923b8e4b2a9c436a65534ff)
(cherry picked from commit dc983d3efd2a5ec3435246d4d2a2e1b4265b5f22)
audio: Use High Quality resampler
Use very high quality resampler to upsample to 48KHz sample
rate.
CRs-fixed: 416730
Change-Id: I30420cbaa8f6d34e2bce408494cc08cb00b175a3
(cherry picked from commit 2904e01dd4a3921b7bc0bec71f63f4757204408b)
(cherry picked from commit 0302cfba74b948e5fb3b2adbcb9330b5dcc36595)
(cherry picked from commit 955a6569ab9d28fbfd6e1e234f06016fcbe5fb6b)
libstagefright: Add interlaced video support
- Adds call to set output buffer size on the native window
Change-Id: I4754e1f1d4d0bba375f719240499ef58c9e87ca2
(cherry picked from commit 63452051ea03bc48af85bd58767fd2a9b0ae13b6)
(cherry picked from commit 856b08a0c452a5d1e0b02487bcbebc44e55fd3e7)
(cherry picked from commit 8f445b519a7c3d107f4180ead01c9722b12b0adf)
ExtendedCodec: Enable timestamp reordering for MPEG4 codec type clips
MPEG4 codec type video clips can contain B frames. This change will
enable timestamp reordering for all MPEG4 codec type clips
CRs-Fixed: 539036
Change-Id: I7682a3781e6aa1b8e73418e4e73ee266c6338770
(cherry picked from commit 8d52ec137be60676c310bda168e8ce18d22dc534)
(cherry picked from commit 3b47eeb2cb5d595f2e755dcaa9e564eac8cbf33d)
(cherry picked from commit 9b47486f507fe834e4c5ab51524be8aa91df875b)
libstagefright: Use proper ctts offset
Calculate ctts offset as difference between composition
time and decoding time, and avoid adding kMaxCttsOffsetTimeUs
as it increases the sample timestamp by the same amount
which leads to av sync loss.
CRs-Fixed: 524739
Change-Id: I75a1bb594536623e3a445af2ee1ceb0cf0926a6e
(cherry picked from commit 863cb2bae6015b32c9ad12f074394c9a54c7bc47)
(cherry picked from commit 8e0e1a4330babf3c38152d48db1f2edf4db7a659)
(cherry picked from commit 87b09615f9473e369883142009d4035e37a2a806)
libvideoeditor: use vWidth and vHeight for buffer allocation
Change-Id: I2ab01b8ef79a3ad9b1eb737b2e9f1211a6e668c2
- video editor detects crop information from decoder, crop
width and height will override metadata width and height.
- decoder is capable of sending crop information where
crop width and height are smaller than actual resolution.
- use actual metadata width and height for calculating
buffer size.
CRs-Fixed: 452511
(cherry picked from commit 93432b8b1c29b2dd62fad923ffa622b002311fa8)
(cherry picked from commit de34cceeb13eead49daf4a47f28bf49286917101)
(cherry picked from commit 865f7b2e0f034b0aea09ef5b13f401c776585bf2)
libstagefright: post video events dynamically - calculate amount of time to delay video event after renderusing the formula: delay = (us between frames)-(us taken by event)-(lateness)
Change-Id: I136ee19520200b26058099f88c8979d1e4e34e23
(cherry picked from commit 61ef39e403f708b014ca434eec35056815558deb)
(cherry picked from commit eb7422fb58a6f95f385574c4d845754db8da6b1f)
(cherry picked from commit 427260792cfa7a53fb27808ac24600a3ebfa7ca2)
libstagefright: Reset the decoder while performing seek
-Observed noise when seeking to beginning of file for the recorded
AMR clip.
-While performing seek, the AMR decoder states are not being reset.
In AMR decoder, the current frame output is predicted using the states
from previous frame. If these states are not cleared, it might cause
noise when seek is performed.
-Fixed by resetting the decoder when seek is issued and port flush
completion is notified to the decoder.
CRs-Fixed: 500851
Change-Id: Ia8480b88521abc09c8b01d6052df4730a0359518
(cherry picked from commit df2b77618d69ac10f1c41ffd5eedb92dd43e161d)
(cherry picked from commit 654f3ad1192bd210b27dbfe29f3cf75a4a3774d8)
(cherry picked from commit 6374b1a4bbf6e853d7214eee6c7177251d246c58)
libstagefright: notify updated stride to NativeWindow for DRC -Notify updated stride after resolution switch to NativeWindow to re-interpret the buffer layout -CRs-fixed: 534910
Change-Id: I4c1427a4826c8d29f93064c34eb3e2eaeef434d7
(cherry picked from commit f93a1ed7561a3a29b73466054e8c43dcbb4c9a1e)
(cherry picked from commit fc948d949de674c4d978b9ca87dea039f4aa86bd)
(cherry picked from commit e5954f19fc67414620ce582a361dd09f5444a01c)
libstagefright: Fix crash in media server mpeg2 playback. -There is a check in dequeueAccessUnitMPEGAudio function inESQueue that time stamp returned by fetchTimeStamp is alwaysgreater than 0 -In the current scenario, valid time stamp set on the firstframe is getting replaced in the sub sequent code by -1. -Fix is to reset the flag after first frame is identified CRs-Fixed: 512218
Change-Id: I1be136fb8c8c3a88f46b88f42a1f200582fd1086
(cherry picked from commit 275b8f8d4047346fea7450b97a004c4e43e9a72b)
(cherry picked from commit 2a2bc3622413ac1d0304cffaec9603de77d5228d)
(cherry picked from commit 7836a4a64b3afb56689bc7384cb3dcdaa5a454c0)
frameworks/av: Fix for failure in CTS MediaRecorderTest cases
-Issue: MediaRecorder CTS test cases are failing.
-Root Cause: Running the CTS test "testRecorderRandomAction" at beginning
sets the huge file size and stargeFrightRecorder set the 64 bit file
offset.
In MPEG4 writer estimateMoovBoxSize depends on a static const
variable which uses this file offset info.
As the variable to calculate estimateMoovBoxSize is static const it
remains same for next media recording session even if file size is small.
-Fix: Don't use static const variable which is used to calculate
estimateMoovBoxSize as its value may change depending on file offsets.
Change-Id: I6bd859fcd7951a4e78abc50bc99dab38e8284877
(cherry picked from commit 0542e6f3af9e17559542764856f430ceaa255cac)
(cherry picked from commit 48fda261ceba7ed1c38c09e3282e9f9147bc03a0)
(cherry picked from commit 70af301150f7a5829f490b44da5caa596da76fa6)
audioflinger: Fix monkey crash in audioflinger set parameter.
-Track ref ptr is initialized to direct track pointer only in create
Direct Track although the descriptor is created in open Output
-If there is a set parameter coming in during this time it could
result in a crash as track ref ptr is not initialized.
-Initialize track ref ptr to NULL when descriptor is created.
Change-Id: Iee887baee45a9e7b381f6e2ea2ab523a23689f6b
(cherry picked from commit 10d4ae79b727f7191484f3adda13f9bba9d62c09)
(cherry picked from commit 7d130a825ad58e44b04cce4a4e3f4e4919337a7b)
(cherry picked from commit 0b72bac19b8f8988e385b06ede234ca80e2e2e07)
audio: Adding support for SRS effects
-Add support for SRS Trumedia post processing effects
Change-Id: I41b8983e1a59f5a84e10a21418d7731e6d8ec430
(cherry picked from commit 5b7c47861ce5e4afee2a3ce8e956e0cfec10cf29)
(cherry picked from commit e0ff892f774310e921f9926748ed9a3fe9784af3)
(cherry picked from commit 6d92096966d3dd16f5d08556e3b1273041d1cf58)
audio: Do not apply effect on multichannel content
-Do not apply effect on multichannel content.
-Effects are applied only for mono or stereo.
CRs-Fixed: 484078
Change-Id: I623d40f926d5dd80e74f9aa2f98448158518047e
(cherry picked from commit b11bb5ec8184b0880a37b13033b22e255dbfccac)
(cherry picked from commit af7c6323538ad5b0e5a9f3efd2458cd44744c397)
(cherry picked from commit b984891aaade017a7fc9aad0c0d9f9398653cf71)
audioflinger:Set correct device while creating effects
-Issue: Effectmodule is created with stale output device
-Rootcause: While creating effects the output device set is
output device of mixer thread, which is stale and
not updated, so effects use the wrong/old device as
the output device and leads to issue such as difference
in volume level after setting correct device etc.
-Fix: Cache the latest device value for direct track
in a flinger member variable when setparameter of
flinger is called with new device and
audioflinger::openoutput is called.
Use this cached value while creating effects
CRs-Fixed: 496866
Change-Id: I66f08b968c75e8a3b812aad7d91b20fd4557ba24
(cherry picked from commit 20d090b0a481722eede1ca01d38fddcd0977c633)
(cherry picked from commit b39d113f2bac7ca1df8ef144d81bdb28c32975d0)
(cherry picked from commit baf45ef7b6c12a1916d56ce37965de3d366b9f84)
frameworks/av: Initial bringup of voip and vocoders
-Add support for voip and vocoders.
Change-Id: Ibe6dfa100e564e057deebe1613cafd282ee2344e
(cherry picked from commit 96043bb0b5e93099bbbdd2a64c2b6039911c0ac3)
(cherry picked from commit 1682b8eb630c22b7ef0aa7a29746c6ce06118d95)
(cherry picked from commit bf72f4c502777df4703c345d1269057c57d11615)
stagefright: Enable HFR feature during recording video.
- Enable HFR related calls to set HFR mode in stagefright and
libmediaplayerservice.
CRs-fixed: 557371
Change-Id: I8cf0856b608c5a76a4ac0ad128f05eab43ca4d49
(cherry picked from commit 94a175141e61b8fa20b687ea054a05d15b7746b5)
(cherry picked from commit c57bfc012bd9004ea1ab79d617d55418412bbaaf)
(cherry picked from commit b19127db20c99a23f26adafe105928bca38c4749)
libstagefright: In thumbnail mode set EOS on first frame
- Hardware decoder will only expect I frames, OMXCodec will
set EOS on first ETB to stop more frames from being pulled
- Skip EOS check on FTB so that the first frame will
behandled
- Required for thumbnail generation for some interlaced
videos
Change-Id: I896dec4c58f688a76c29a5d72fb8b61cb04bbfcc
(cherry picked from commit a124c3b722b251f148141b503ba9614cf33af43a)
(cherry picked from commit 44ee9348a2743bf39c2e517612b7e75d7be797ad)
(cherry picked from commit cb113596724882fcd2ce2ee52d1e447698d79865)
audio: Fix a/v sync issue for playback over BT
-Audio and video are out of sync when a video is played/streamed
with a BT headset connected to the target.
-AudioTrack does not consider the BT latency.
-Latency of the BT stream added to the existing latency.
-query for tunnel mode latency
-During tunnel playback, we need to offset latency for audio plaback
on BT devices by querying BT HAL.
Change-Id: I01a132687aec09cd2897039622daa35cff7ec409
(cherry picked from commit 9096501e28ecb56dc3a5a4d36d474b99804315cd)
(cherry picked from commit 29cb9299834d73a916b7a68617a32fb615c95d95)
(cherry picked from commit ca1f9dd47ca4299797e68029f3c260a5f291d3bd)
audio: AudioTrack constructor initialization
- Initialized few member variables to null
Change-Id: I36fe9fd2f9e4c2b1967b8d9562ffcad635c6f686
(cherry picked from commit 673fd8919e310f3b2ecea082cfb9c2b08d00ad44)
(cherry picked from commit 75a64de910334e8cb12cfa0079910269e3f4e0e3)
audio: enable tunnel mode based on flags
- Enable tunnel mode playback based on flags defined
in target board config file instead of checking
target name.
Change-Id: I78c39b2653e7bae6a6eae8de36fbcacbeb1e6686
Conflicts:
media/libstagefright/Android.mk
(cherry picked from commit 55a727f33978d374ffbdf612dd4a8560c3bffe3f)
(cherry picked from commit bb7234c9bb0505f3d6df998f84b9a062b206e097)
libstagefright: adjust AV sync late margin based on property
- Current AV late margin is 40 ms
- Read system property media.sf.set.late.margin and if
new AV sync late margin is set then use the same
- Max AV sync late margin allowed is 250 ms
- Min AV sync late margin is default 40 ms
Change-Id: Ie99a4b85264d4a07c983a3f116c18bee3b0acfc7
(cherry picked from commit 0931561dafecce2a0b9148822e77c5f526f31934)
(cherry picked from commit a7fbe5b8ff05c52c902f4f550b812169c8ed456b)
Camcoder: Add missing timelapse recording resolutions to map variable.
VGA and WVGA resolutions were missing in a map variable.
Because of this parsing media profiles xml for these
profiles was crashing.
Change-Id: I5494436f8a3cde9c8a5f0d26b066ca5b37dbd585
CRs-fixed: 564722
(cherry picked from commit 4ac681161652d6dcf6b3313853769b19905010f2)
(cherry picked from commit 668759a8ba976b18932773ad002b7964b9cc071c)
libstagefright: For interlace, set decoder in arbitrary mode
In arbitrary mode, frame parsers sends individual fields
to core. Once both the field is decoded, output done (FBD)
is sent back to client.
Default frame by frame mode will send both the fields in
a single buffer to core, which might decode only one field
or will throw up error.
Change-Id: Ia35d754e685f28f0df9f2c6ef2180b8a3dae1f28
CRs-Fixed: 531034
(cherry picked from commit 0428493caa128cfcb7b3df9082427cb6872a029e)
(cherry picked from commit f0ca79d0509d278a4a322121dc113e173c33534c)
framework\av: libstagefright: Update NOTICE file.
- Update NOTICE file with the correct information.
Change-Id: I04384d4cc12d05db768a41c4580f39050e99441f
CRs-Fixed: 534648
(cherry picked from commit 7edaaf1230953115325b4b2701160d0edd8d63f9)
frameworks/av: rendering statistics for Stagefright.
- this adds extra fps statisticis
- report via dumpsys
- prints statistics before reset at the end of playback ontologcat
- add resume latency, from play_l to render
- add seek latency, from seek_l to render
- enabled using persist.debug.sf.statistics 1
- print all statistics when seek or resume is issued.
Change-Id: I90e215bf3139c38731b9db2b07f6d6f5a916b11e
(cherry picked from commit ecc5a30e06abcf87c941068b44023227e0eb8266)
libmedia: Add QC enhancements to MediaProfiles.
Add entries for:
- 4K resolution support for camcorder
- Timelapse for some resolution
Change-Id: I2bbeec06ac457eb9a6fb92e3aa8fc5ead3372809
(cherry picked from commit 90b4f7c00008b7d1afe4bcc503680e807a935db9)
Revert "frameworks/av: Enable subsystem re-start for LPA/Tunnel"
This reverts commit 9fd40491ae10c99b35c5592e96cc9246b2a8c4ae.
Change-Id: Ia7faba22db4134c36875fec90f98cf23e019288b
diff --git a/NOTICE b/NOTICE
index 152be20..6f652a2 100644
--- a/NOTICE
+++ b/NOTICE
@@ -322,3 +322,32 @@
shall not be used in advertising or otherwise to promote the sale, use
or other dealings in these Data Files or Software without prior written
authorization of the copyright holder.
+
+
+Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
diff --git a/include/media/AudioParameter.h b/include/media/AudioParameter.h
index 891bc4b..100d2e1 100644
--- a/include/media/AudioParameter.h
+++ b/include/media/AudioParameter.h
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2008-2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -48,6 +51,16 @@
static const char * const keyFrameCount;
static const char * const keyInputSource;
static const char * const keyScreenState;
+#ifdef QCOM_FM_ENABLED
+ static const char * const keyHandleFm;
+ static const char * const keyVoipCheck;
+ static const char * const keyFluenceType;
+ static const char * const keySSR;
+ static const char * const keyHandleA2dpDevice;
+ static const char * const keyADSPStatus;
+ static const char * const keyCanOpenProxy;
+ static const char * const keyFmVolume;
+#endif
String8 toString();
diff --git a/include/media/AudioRecord.h b/include/media/AudioRecord.h
index 052064d..fe1b35b 100644
--- a/include/media/AudioRecord.h
+++ b/include/media/AudioRecord.h
@@ -210,8 +210,8 @@
audio_format_t format() const { return mFormat; }
uint32_t channelCount() const { return mChannelCount; }
size_t frameCount() const { return mFrameCount; }
- size_t frameSize() const { return mFrameSize; }
- audio_source_t inputSource() const { return mInputSource; }
+ size_t frameSize() const;
+ audio_source_t inputSource() const;
/* After it's created the track is not active. Call start() to
* make it active. If set, the callback will start being called.
diff --git a/include/media/AudioSystem.h b/include/media/AudioSystem.h
index 225ef76..78fad3c 100644
--- a/include/media/AudioSystem.h
+++ b/include/media/AudioSystem.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2008 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -23,6 +25,7 @@
#include <system/audio_policy.h>
#include <utils/Errors.h>
#include <utils/Mutex.h>
+#include <media/AudioParameter.h>
namespace android {
@@ -147,6 +150,7 @@
INPUT_CLOSED,
INPUT_CONFIG_CHANGED,
STREAM_CONFIG_CHANGED,
+ EFFECT_CONFIG_CHANGED,
NUM_CONFIG_EVENTS
};
diff --git a/include/media/AudioTrack.h b/include/media/AudioTrack.h
index f2f9c22..f74825c 100644
--- a/include/media/AudioTrack.h
+++ b/include/media/AudioTrack.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -22,7 +24,8 @@
#include <media/AudioTimestamp.h>
#include <media/IAudioTrack.h>
#include <utils/threads.h>
-
+#include <media/IDirectTrack.h>
+#include <media/IDirectTrackClient.h>
namespace android {
// ----------------------------------------------------------------------------
@@ -33,7 +36,8 @@
// ----------------------------------------------------------------------------
-class AudioTrack : public RefBase
+class AudioTrack : public BnDirectTrackClient,
+ virtual public RefBase
{
public:
enum channel_index {
@@ -66,6 +70,7 @@
EVENT_NEW_TIMESTAMP = 8, // Delivered periodically and when there's a significant change
// in the mapping from frame position to presentation time.
// See AudioTimestamp for the information included with event.
+ EVENT_HW_FAIL = 9, // ADSP failure.
};
/* Client should declare Buffer on the stack and pass address to obtainBuffer()
@@ -216,9 +221,8 @@
/* Terminates the AudioTrack and unregisters it from AudioFlinger.
* Also destroys all resources associated with the AudioTrack.
*/
-protected:
+
virtual ~AudioTrack();
-public:
/* Initialize an AudioTrack that was created using the AudioTrack() constructor.
* Don't call set() more than once, or after the AudioTrack() constructors that take parameters.
@@ -261,7 +265,7 @@
* This includes the latency due to AudioTrack buffer size, AudioMixer (if any)
* and audio hardware driver.
*/
- uint32_t latency() const { return mLatency; }
+ uint32_t latency() const;
/* getters, see constructors and set() */
@@ -579,7 +583,9 @@
* consider implementing that at application level, based on the low resolution timestamps.
* Returns NO_ERROR if timestamp is valid.
*/
- status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual status_t getTimestamp(AudioTimestamp& timestamp);
+ virtual void notify(int msg);
+ virtual status_t getTimeStamp(uint64_t *tstamp);
protected:
/* copying audio tracks is not allowed */
@@ -650,6 +656,7 @@
bool isOffloaded() const
{ return (mFlags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) != 0; }
+ sp<IDirectTrack> mDirectTrack;
// Next 3 fields may be changed if IAudioTrack is re-created, but always != 0
sp<IAudioTrack> mAudioTrack;
sp<IMemory> mCblkMemory;
@@ -717,11 +724,13 @@
uint32_t mUpdatePeriod; // in frames, zero means no EVENT_NEW_POS
audio_output_flags_t mFlags;
+ sp<IAudioFlinger> mAudioFlinger;
+ audio_io_handle_t mAudioDirectOutput;
int mSessionId;
int mAuxEffectId;
mutable Mutex mLock;
-
+ void* mObserver;
bool mIsTimed;
int mPreviousPriority; // before start()
SchedPolicy mPreviousSchedulingGroup;
diff --git a/include/media/IAudioFlinger.h b/include/media/IAudioFlinger.h
index eaf7780..abd7754 100644
--- a/include/media/IAudioFlinger.h
+++ b/include/media/IAudioFlinger.h
@@ -1,5 +1,7 @@
/*
- * Copyright (C) 2007 The Android Open Source Project
+ * Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ * Not a contribution.
+ * Copyright (C) 2007 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.
@@ -24,6 +26,8 @@
#include <utils/RefBase.h>
#include <utils/Errors.h>
#include <binder/IInterface.h>
+#include <media/IDirectTrack.h>
+#include <media/IDirectTrackClient.h>
#include <media/IAudioTrack.h>
#include <media/IAudioRecord.h>
#include <media/IAudioFlingerClient.h>
@@ -50,6 +54,7 @@
TRACK_TIMED = 1, // client requests a TimedAudioTrack
TRACK_FAST = 2, // client requests a fast AudioTrack or AudioRecord
TRACK_OFFLOAD = 4, // client requests offload to hw codec
+ TRACK_VOICE_COMMUNICATION = 8, //client uses voice communication
};
typedef uint32_t track_flags_t;
@@ -76,6 +81,19 @@
String8& name,
status_t *status) = 0;
+ /* create a direct audio track and registers it with AudioFlinger.
+ * return null if the track cannot be created.
+ */
+ virtual sp<IDirectTrack> createDirectTrack(
+ pid_t pid,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_io_handle_t output,
+ int *sessionId,
+ IDirectTrackClient* client,
+ audio_stream_type_t streamType,
+ status_t *status) = 0;
+
virtual sp<IAudioRecord> openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
diff --git a/include/media/IDirectTrack.h b/include/media/IDirectTrack.h
new file mode 100644
index 0000000..bfb29ec
--- /dev/null
+++ b/include/media/IDirectTrack.h
@@ -0,0 +1,92 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2007 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_IDIRECTTRACK_H
+#define ANDROID_IDIRECTTRACK_H
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <utils/RefBase.h>
+#include <utils/Errors.h>
+#include <binder/IInterface.h>
+#include <binder/IMemory.h>
+
+
+namespace android {
+
+// ----------------------------------------------------------------------------
+
+class IDirectTrack : public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(DirectTrack);
+
+ /* After it's created the track is not active. Call start() to
+ * make it active. If set, the callback will start being called.
+ */
+ virtual status_t start() = 0;
+
+ /* Stop a track. If set, the callback will cease being called and
+ * obtainBuffer will return an error. Buffers that are already released
+ * will be processed, unless flush() is called.
+ */
+ virtual void stop() = 0;
+
+ /* flush a stopped track. All pending buffers are discarded.
+ * This function has no effect if the track is not stoped.
+ */
+ virtual void flush() = 0;
+
+ /* mute or unmutes this track.
+ * While mutted, the callback, if set, is still called.
+ */
+ virtual void mute(bool) = 0;
+
+ /* Pause a track. If set, the callback will cease being called and
+ * obtainBuffer will return an error. Buffers that are already released
+ * will be processed, unless flush() is called.
+ */
+ virtual void pause() = 0;
+
+ /* set volume for both left and right channels.
+ */
+ virtual void setVolume(float l, float r) = 0;
+
+ virtual ssize_t write(const void*, size_t) = 0;
+
+ virtual int64_t getTimeStamp() = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDirectTrack : public BnInterface<IDirectTrack>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+// ----------------------------------------------------------------------------
+
+}; // namespace android
+
+#endif // ANDROID_IAUDIOTRACK_H
diff --git a/include/media/IDirectTrackClient.h b/include/media/IDirectTrackClient.h
new file mode 100644
index 0000000..703df35
--- /dev/null
+++ b/include/media/IDirectTrackClient.h
@@ -0,0 +1,50 @@
+/*
+ * Copyright (c) 2012, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
+ * Copyright (C) 2007 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_IDIRECTTRACKCLIENT_H
+#define ANDROID_IDIRECTTRACKCLIENT_H
+
+#include <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+namespace android {
+
+class IDirectTrackClient: public IInterface
+{
+public:
+ DECLARE_META_INTERFACE(DirectTrackClient);
+
+ virtual void notify(int msg) = 0;
+};
+
+// ----------------------------------------------------------------------------
+
+class BnDirectTrackClient: public BnInterface<IDirectTrackClient>
+{
+public:
+ virtual status_t onTransact( uint32_t code,
+ const Parcel& data,
+ Parcel* reply,
+ uint32_t flags = 0);
+};
+
+}; // namespace android
+
+#endif // ANDROID_IDIRECTTRACKCLIENT_H
diff --git a/include/media/MediaPlayerInterface.h b/include/media/MediaPlayerInterface.h
index 412448a..edf198a 100644
--- a/include/media/MediaPlayerInterface.h
+++ b/include/media/MediaPlayerInterface.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2007 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -79,8 +81,10 @@
CB_EVENT_FILL_BUFFER, // Request to write more data to buffer.
CB_EVENT_STREAM_END, // Sent after all the buffers queued in AF and HW are played
// back (after stop is called)
- CB_EVENT_TEAR_DOWN // The AudioTrack was invalidated due to use case change:
+ CB_EVENT_TEAR_DOWN, // The AudioTrack was invalidated due to use case change:
// Need to re-evaluate offloading options
+ CB_EVENT_UNDERRUN,
+ CB_EVENT_HW_FAIL
};
// Callback returns the number of bytes actually written to the buffer.
@@ -96,6 +100,7 @@
virtual ssize_t channelCount() const = 0;
virtual ssize_t frameSize() const = 0;
virtual uint32_t latency() const = 0;
+ virtual audio_stream_type_t streamType() const {return AUDIO_STREAM_DEFAULT;}
virtual float msecsPerFrame() const = 0;
virtual status_t getPosition(uint32_t *position) const = 0;
virtual status_t getFramesWritten(uint32_t *frameswritten) const = 0;
@@ -121,9 +126,11 @@
virtual status_t setPlaybackRatePermille(int32_t rate) { return INVALID_OPERATION; }
virtual bool needsTrailingPadding() { return true; }
-
virtual status_t setParameters(const String8& keyValuePairs) { return NO_ERROR; };
virtual String8 getParameters(const String8& keys) { return String8::empty(); };
+
+ virtual ssize_t sampleRate() const {return 0;};
+ virtual status_t getTimeStamp(uint64_t *tstamp) {return 0;};
};
MediaPlayerBase() : mCookie(0), mNotify(0) {}
diff --git a/include/media/MediaProfiles.h b/include/media/MediaProfiles.h
index 60d6bef..74fc247 100644
--- a/include/media/MediaProfiles.h
+++ b/include/media/MediaProfiles.h
@@ -32,13 +32,14 @@
CAMCORDER_QUALITY_480P = 4,
CAMCORDER_QUALITY_720P = 5,
CAMCORDER_QUALITY_1080P = 6,
- CAMCORDER_QUALITY_QVGA = 11,
- CAMCORDER_QUALITY_FWVGA = 7,
- CAMCORDER_QUALITY_WVGA = 8,
- CAMCORDER_QUALITY_VGA = 9,
- CAMCORDER_QUALITY_WQVGA = 10,
-
- CAMCORDER_QUALITY_LIST_END = 11,
+ CAMCORDER_QUALITY_QVGA = 7,
+ CAMCORDER_QUALITY_FWVGA = 8,
+ CAMCORDER_QUALITY_WVGA = 9,
+ CAMCORDER_QUALITY_VGA = 10,
+ CAMCORDER_QUALITY_WQVGA = 11,
+ CAMCORDER_QUALITY_4kUHD = 12,
+ CAMCORDER_QUALITY_4kDCI = 13,
+ CAMCORDER_QUALITY_LIST_END = 13,
CAMCORDER_QUALITY_TIME_LAPSE_LIST_START = 1000,
CAMCORDER_QUALITY_TIME_LAPSE_LOW = 1000,
@@ -49,7 +50,11 @@
CAMCORDER_QUALITY_TIME_LAPSE_720P = 1005,
CAMCORDER_QUALITY_TIME_LAPSE_1080P = 1006,
CAMCORDER_QUALITY_TIME_LAPSE_QVGA = 1007,
- CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1007,
+ CAMCORDER_QUALITY_TIME_LAPSE_FWVGA = 1008,
+ CAMCORDER_QUALITY_TIME_LAPSE_WVGA = 1009,
+ CAMCORDER_QUALITY_TIME_LAPSE_VGA = 1010,
+ CAMCORDER_QUALITY_TIME_LAPSE_WQVGA = 1011,
+ CAMCORDER_QUALITY_TIME_LAPSE_LIST_END = 1011,
};
/**
@@ -461,6 +466,8 @@
static VideoEncoderCap* createDefaultH263VideoEncoderCap();
static VideoEncoderCap* createDefaultM4vVideoEncoderCap();
static AudioEncoderCap* createDefaultAmrNBEncoderCap();
+ static AudioEncoderCap* createDefaultAacEncoderCap();
+ static AudioEncoderCap* createDefaultLpcmEncoderCap();
static int findTagForName(const NameToTagMap *map, size_t nMappings, const char *name);
diff --git a/include/media/mediaplayer.h b/include/media/mediaplayer.h
index 4c05fc3..1fe34e5 100644
--- a/include/media/mediaplayer.h
+++ b/include/media/mediaplayer.h
@@ -50,6 +50,7 @@
MEDIA_ERROR = 100,
MEDIA_INFO = 200,
MEDIA_SUBTITLE_DATA = 201,
+ MEDIA_QOE = 300,
};
// Generic error codes for the media player framework. Errors are fatal, the
diff --git a/include/media/mediarecorder.h b/include/media/mediarecorder.h
index d61e990..b3109ed 100644
--- a/include/media/mediarecorder.h
+++ b/include/media/mediarecorder.h
@@ -67,6 +67,9 @@
/* H.264/AAC data encapsulated in MPEG2/TS */
OUTPUT_FORMAT_MPEG2TS = 8,
+ OUTPUT_FORMAT_QCP = 9, // QCP file format
+ OUTPUT_FORMAT_THREE_GPP2 = 10, /*3GPP2*/
+ OUTPUT_FORMAT_WAVE = 11, /*WAVE*/
OUTPUT_FORMAT_LIST_END // must be last - used to validate format type
};
@@ -77,7 +80,9 @@
AUDIO_ENCODER_AAC = 3,
AUDIO_ENCODER_HE_AAC = 4,
AUDIO_ENCODER_AAC_ELD = 5,
-
+ AUDIO_ENCODER_EVRC = 6,
+ AUDIO_ENCODER_QCELP = 7,
+ AUDIO_ENCODER_LPCM = 8,
AUDIO_ENCODER_LIST_END // must be the last - used to validate the audio encoder type
};
diff --git a/include/media/stagefright/ACodec.h b/include/media/stagefright/ACodec.h
index 2882cb5..a2d5cb1 100644
--- a/include/media/stagefright/ACodec.h
+++ b/include/media/stagefright/ACodec.h
@@ -196,6 +196,7 @@
bool mChannelMaskPresent;
int32_t mChannelMask;
+ bool mInSmoothStreamingMode;
unsigned mDequeueCounter;
bool mStoreMetaDataInOutputBuffers;
int32_t mMetaDataBuffersToSubmit;
diff --git a/include/media/stagefright/AudioPlayer.h b/include/media/stagefright/AudioPlayer.h
index 912a43c..422148f 100644
--- a/include/media/stagefright/AudioPlayer.h
+++ b/include/media/stagefright/AudioPlayer.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -51,27 +53,27 @@
virtual ~AudioPlayer();
// Caller retains ownership of "source".
- void setSource(const sp<MediaSource> &source);
+ virtual void setSource(const sp<MediaSource> &source);
// Return time in us.
virtual int64_t getRealTimeUs();
- status_t start(bool sourceAlreadyStarted = false);
+ virtual status_t start(bool sourceAlreadyStarted = false);
- void pause(bool playPendingSamples = false);
- status_t resume();
+ virtual void pause(bool playPendingSamples = false);
+ virtual status_t resume();
// Returns the timestamp of the last buffer played (in us).
- int64_t getMediaTimeUs();
+ virtual 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);
+ virtual bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
- status_t seekTo(int64_t time_us);
+ virtual status_t seekTo(int64_t time_us);
- bool isSeeking();
- bool reachedEOS(status_t *finalStatus);
+ virtual bool isSeeking();
+ virtual bool reachedEOS(status_t *finalStatus);
status_t setPlaybackRatePermille(int32_t ratePermille);
@@ -101,6 +103,7 @@
int64_t mSeekTimeUs;
bool mStarted;
+ bool mSourcePaused;
bool mIsFirstBuffer;
status_t mFirstBufferResult;
diff --git a/include/media/stagefright/AudioSource.h b/include/media/stagefright/AudioSource.h
index 5e5137b..161b64ff 100644
--- a/include/media/stagefright/AudioSource.h
+++ b/include/media/stagefright/AudioSource.h
@@ -23,7 +23,7 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MediaBuffer.h>
#include <utils/List.h>
-
+#include <utils/String8.h>
#include <system/audio.h>
namespace android {
@@ -104,6 +104,14 @@
AudioSource(const AudioSource &);
AudioSource &operator=(const AudioSource &);
+public:
+ AudioSource(
+ audio_source_t inputSource, const sp<MetaData>& meta );
+private:
+ audio_format_t mFormat;
+ String8 mMime;
+ int32_t mMaxBufferSize;
+ int64_t bufferDurationUs( ssize_t n );
};
} // namespace android
diff --git a/include/media/stagefright/ExtendedCodec.h b/include/media/stagefright/ExtendedCodec.h
new file mode 100644
index 0000000..eeb31d6
--- /dev/null
+++ b/include/media/stagefright/ExtendedCodec.h
@@ -0,0 +1,177 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef EXTENDED_CODEC_H_
+#define EXTENDED_CODEC_H_
+
+#include <android/native_window.h>
+#include <media/IOMX.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/AString.h>
+#include <utils/threads.h>
+
+#include <OMX_Audio.h>
+
+namespace android {
+
+struct MediaCodecList;
+struct OMXCodec;
+
+enum{
+ kRequiresWMAProComponent = 0x40000000,
+};
+
+
+struct ExtendedCodec {
+
+ enum {
+ kPortIndexInput = 0,
+ kPortIndexOutput = 1
+ };
+ static status_t convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format);
+
+ static uint32_t getComponentQuirks (
+ const MediaCodecList *list, size_t index);
+
+ static status_t setAudioFormat(
+ const sp<MetaData> &meta, const char* mime,
+ sp<IOMX> OMXhandle,IOMX::node_id nodeID,
+ bool isEncoder);
+
+ static status_t setAudioFormat(
+ const sp<AMessage> &msg, const char* mime,
+ sp<IOMX> OMXhandle,IOMX::node_id nodeID,
+ bool isEncoder);
+
+ static status_t setVideoInputFormat(
+ const char *mime,
+ OMX_VIDEO_CODINGTYPE *compressionFormat);
+
+ static status_t setVideoOutputFormat(
+ const char *mime,
+ OMX_VIDEO_CODINGTYPE *compressionFormat);
+
+ static status_t getSupportedAudioFormatInfo(
+ const AString* mime,
+ sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID,
+ int portIndex,
+ int* channelCount);
+
+ static status_t handleSupportedAudioFormats(
+ int format, AString* mime);
+
+ static const char* overrideComponentName(
+ uint32_t quirks, const sp<MetaData> &meta);
+
+ static void overrideComponentName(
+ uint32_t quirks, const sp<AMessage> &msg,
+ AString* componentName);
+
+ static void getRawCodecSpecificData(
+ const sp<MetaData> &meta,
+ const void* &data,
+ size_t& size);
+
+ static sp<ABuffer> getRawCodecSpecificData(
+ const sp<AMessage> &msg);
+
+ static void getAacCodecSpecificData(
+ const sp<MetaData> &meta,
+ const void* &data,
+ size_t& size);
+
+ static sp<ABuffer> getAacCodecSpecificData(
+ const sp<AMessage> &msg);
+
+ static status_t setSupportedRole(
+ const sp<IOMX> &omx, IOMX::node_id node,
+ bool isEncoder, const char *mime);
+
+ static void configureFramePackingFormat(
+ const sp<AMessage> &msg, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, const char* componentName);
+
+ static void configureFramePackingFormat(
+ const sp<MetaData> &meta, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, const char* componentName);
+
+ static void configureVideoDecoder(
+ const sp<MetaData> &meta, const char* mime,
+ sp<IOMX> OMXhandle, const uint32_t flags,
+ IOMX::node_id nodeID, const char* componentName);
+
+ static void configureVideoDecoder(
+ const sp<AMessage> &msg, const char* mime,
+ sp<IOMX> OMXhandle, const uint32_t flags,
+ IOMX::node_id nodeID, const char* componentName);
+
+ static void enableSmoothStreaming(
+ const sp<IOMX> &omx, IOMX::node_id nodeID, bool* isEnabled,
+ const char* componentName);
+
+ static bool useHWAACDecoder(const char *mime);
+
+private:
+ static const char* getMsgKey(int key );
+
+ static status_t setWMAFormat(
+ const sp<MetaData> &meta, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder );
+
+ static status_t setWMAFormat(
+ const sp<AMessage> &msg, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder );
+
+ static void setEVRCFormat(
+ int32_t numChannels, int32_t sampleRate,
+ sp<IOMX> OMXhandle, IOMX::node_id nodeID,
+ bool isEncoder );
+
+ static void setQCELPFormat(
+ int32_t numChannels, int32_t sampleRate,
+ sp<IOMX> OMXhandle, IOMX::node_id nodeID,
+ bool isEncoder );
+
+ static void setAC3Format(
+ int32_t numChannels, int32_t sampleRate,
+ sp<IOMX> OMXhandle, IOMX::node_id nodeID);
+
+ static status_t setDIVXFormat(
+ const sp<AMessage> &msg, const char* mime,
+ sp<IOMX> OMXhandle,IOMX::node_id nodeID, int port_index);
+
+};
+
+}
+#endif /*EXTENDED_CODEC_H_ */
+
diff --git a/include/media/stagefright/ExtendedWriter.h b/include/media/stagefright/ExtendedWriter.h
new file mode 100644
index 0000000..c56239f
--- /dev/null
+++ b/include/media/stagefright/ExtendedWriter.h
@@ -0,0 +1,142 @@
+/*
+ * Copyright (c) 2012 - 2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution
+ *
+ * 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 EXTENDED_WRITER_H_
+
+#define EXTENDED_WRITER_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/MediaWriter.h>
+#include <utils/threads.h>
+#include <cutils/log.h>
+
+#define LITERAL_TO_STRING_INTERNAL(x) #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+
+#define CHECK_EQ(x,y) \
+ LOG_ALWAYS_FATAL_IF( \
+ (x) != (y), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x " != " #y)
+
+#define CHECK(x) \
+ LOG_ALWAYS_FATAL_IF( \
+ !(x), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x)
+
+namespace android {
+
+struct MediaSource;
+struct MetaData;
+
+struct ExtendedWriter : public MediaWriter {
+ ExtendedWriter(const char *filename);
+ ExtendedWriter(int fd);
+
+ status_t initCheck() const;
+
+ virtual status_t addSource(const sp<MediaSource> &source);
+ virtual bool reachedEOS();
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
+
+protected:
+ virtual ~ExtendedWriter();
+
+private:
+ FILE *mFile;
+ status_t mInitCheck;
+ sp<MediaSource> mSource;
+ bool mStarted;
+ volatile bool mPaused;
+ volatile bool mResumed;
+ volatile bool mDone;
+ volatile bool mReachedEOS;
+ pthread_t mThread;
+ int64_t mEstimatedSizeBytes;
+ int64_t mEstimatedDurationUs;
+
+ int32_t mFormat;
+
+ //QCP/EVRC header
+ struct QCPEVRCHeader
+ {
+ /* RIFF Section */
+ char riff[4];
+ unsigned int s_riff;
+ char qlcm[4];
+
+ /* Format chunk */
+ char fmt[4];
+ unsigned int s_fmt;
+ char mjr;
+ char mnr;
+ unsigned int data1;
+
+ /* UNIQUE ID of the codec */
+ unsigned short data2;
+ unsigned short data3;
+ char data4[8];
+ unsigned short ver;
+
+ /* Codec Info */
+ char name[80];
+ unsigned short abps;
+
+ /* average bits per sec of the codec */
+ unsigned short bytes_per_pkt;
+ unsigned short samp_per_block;
+ unsigned short samp_per_sec;
+ unsigned short bits_per_samp;
+ unsigned char vr_num_of_rates;
+
+ /* Rate Header fmt info */
+ unsigned char rvd1[3];
+ unsigned short vr_bytes_per_pkt[8];
+ unsigned int rvd2[5];
+
+ /* Vrat chunk */
+ unsigned char vrat[4];
+ unsigned int s_vrat;
+ unsigned int v_rate;
+ unsigned int size_in_pkts;
+
+ /* Data chunk */
+ unsigned char data[4];
+ unsigned int s_data;
+ } __attribute__ ((packed));
+
+ struct QCPEVRCHeader mHeader;
+ off_t mOffset; //note off_t
+
+ static void *ThreadWrapper(void *);
+ status_t threadFunc();
+ bool exceedsFileSizeLimit();
+ bool exceedsFileDurationLimit();
+
+ ExtendedWriter(const ExtendedWriter &);
+ ExtendedWriter &operator=(const ExtendedWriter &);
+
+ status_t writeQCPHeader( );
+ status_t writeEVRCHeader( );
+};
+
+} // namespace android
+
+#endif // AMR_WRITER_H_
diff --git a/include/media/stagefright/LPAPlayer.h b/include/media/stagefright/LPAPlayer.h
new file mode 100644
index 0000000..a2ae966
--- /dev/null
+++ b/include/media/stagefright/LPAPlayer.h
@@ -0,0 +1,198 @@
+/*
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ * 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 LPA_PLAYER_H_
+
+#define LPA_PLAYER_H_
+
+#include "AudioPlayer.h"
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <include/TimedEventQueue.h>
+
+// Pause timeout = 3sec
+#define LPA_PAUSE_TIMEOUT_USEC 3000000
+
+namespace android {
+
+class LPAPlayer : public AudioPlayer {
+public:
+ enum {
+ REACHED_EOS,
+ SEEK_COMPLETE
+ };
+
+ enum {
+ TRACK_DIRECT,
+ TRACK_REGULAR,
+ TRACK_NONE
+ };
+
+ LPAPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck,
+ AwesomePlayer *audioObserver = NULL);
+
+ virtual ~LPAPlayer();
+
+ // Caller retains ownership of "source".
+ virtual void setSource(const sp<MediaSource> &source);
+
+ // Return time in us.
+ virtual int64_t getRealTimeUs();
+
+ virtual status_t start(bool sourceAlreadyStarted = false);
+
+ virtual void pause(bool playPendingSamples = false);
+ virtual status_t resume();
+
+ // Returns the timestamp of the last buffer played (in us).
+ virtual int64_t getMediaTimeUs();
+
+ // Returns true iff a mapping is established, i.e. the LPAPlayer
+ // has played at least one frame of audio.
+ virtual bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
+
+ virtual status_t seekTo(int64_t time_us);
+
+ virtual bool isSeeking();
+ virtual bool reachedEOS(status_t *finalStatus);
+
+ static int mObjectsAlive;
+private:
+ int64_t mPositionTimeMediaUs;
+ int64_t mPositionTimeRealUs;
+ bool mInternalSeeking;
+ bool mIsAudioRouted;
+ bool mStarted;
+ bool mPaused;
+ int32_t mChannelMask;
+ int32_t mNumOutputChannels;
+ int32_t mNumInputChannels;
+ int32_t mSampleRate;
+ int64_t mLatencyUs;
+ size_t mFrameSize;
+ int64_t mTimeStarted;
+ int64_t mTimePlayed;
+ int64_t mNumFramesPlayed;
+ int64_t mNumFramesPlayedSysTimeUs;
+
+ pthread_t mDecoderThread;
+
+ //Kill Thread boolean
+ bool mKillDecoderThread;
+
+ //Thread alive boolean
+ bool mDecoderThreadAlive;
+
+ //Declare the condition Variables and Mutex
+
+ Mutex mDecoderMutex;
+
+ Condition mDecoderCv;
+
+ // make sure Decoder thread has exited
+ void requestAndWaitForDecoderThreadExit_l();
+
+ static void *decoderThreadWrapper(void *me);
+ void decoderThreadEntry();
+
+ void createThreads();
+
+ void onPauseTimeOut();
+
+ sp<MediaSource> mSource;
+
+ MediaBuffer *mInputBuffer;
+
+ Mutex mLock;
+
+ bool mSeeking;
+ bool mReachedEOS;
+ bool mReachedOutputEOS;
+ status_t mFinalStatus;
+ int64_t mSeekTimeUs;
+ int64_t mPauseTime;
+
+
+ bool mIsFirstBuffer;
+ status_t mFirstBufferResult;
+ MediaBuffer *mFirstBuffer;
+ TimedEventQueue mQueue;
+ bool mQueueStarted;
+ sp<TimedEventQueue::Event> mPauseEvent;
+ bool mPauseEventPending;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ AwesomePlayer *mObserver;
+ int mTrackType;
+
+ static size_t AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *data, size_t size, void *me,
+ MediaPlayerBase::AudioSink::cb_event_t event);
+
+ int64_t getTimeStamp();
+
+ size_t fillBuffer(void *data, size_t size);
+
+ int64_t getRealTimeUsLocked();
+
+ void reset();
+
+ status_t setupAudioSink();
+ static size_t AudioCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *buffer, size_t size, void *cookie);
+ size_t AudioCallback(void *cookie, void *data, size_t size);
+ int64_t getMediaTimeUs_l();
+ bool seekTooClose(int64_t);
+
+ void convertMonoToStereo(int16_t *data, size_t size);
+
+ LPAPlayer(const LPAPlayer &);
+ LPAPlayer &operator=(const LPAPlayer &);
+};
+
+struct TimedEvent : public TimedEventQueue::Event {
+ TimedEvent(LPAPlayer *player,
+ void (LPAPlayer::*method)())
+ : mPlayer(player),
+ mMethod(method) {
+ }
+
+protected:
+ virtual ~TimedEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ (mPlayer->*mMethod)();
+ }
+
+private:
+ LPAPlayer *mPlayer;
+ void (LPAPlayer::*mMethod)();
+
+ TimedEvent(const TimedEvent &);
+ TimedEvent &operator=(const TimedEvent &);
+};
+
+} // namespace android
+
+#endif // LPA_PLAYER_H_
+
diff --git a/include/media/stagefright/MediaCodecList.h b/include/media/stagefright/MediaCodecList.h
index 590623b..947f123 100644
--- a/include/media/stagefright/MediaCodecList.h
+++ b/include/media/stagefright/MediaCodecList.h
@@ -100,6 +100,8 @@
status_t addTypeFromAttributes(const char **attrs);
void addType(const char *name);
+ friend class ExtendedUtils;
+
DISALLOW_EVIL_CONSTRUCTORS(MediaCodecList);
};
diff --git a/include/media/stagefright/MediaDefs.h b/include/media/stagefright/MediaDefs.h
index 85693d4..11e6988 100644
--- a/include/media/stagefright/MediaDefs.h
+++ b/include/media/stagefright/MediaDefs.h
@@ -32,6 +32,7 @@
extern const char *MEDIA_MIMETYPE_AUDIO_AMR_NB;
extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB;
+extern const char *MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS;
extern const char *MEDIA_MIMETYPE_AUDIO_MPEG; // layer III
extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_I;
extern const char *MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II;
diff --git a/include/media/stagefright/MetaData.h b/include/media/stagefright/MetaData.h
index de3fc36..a92ba61 100644
--- a/include/media/stagefright/MetaData.h
+++ b/include/media/stagefright/MetaData.h
@@ -1,6 +1,9 @@
/*
* Copyright (C) 2009 The Android Open Source Project
*
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution
+ *
* 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
@@ -161,6 +164,7 @@
kKeyCryptoDefaultIVSize = 'cryS', // int32_t
kKeyPssh = 'pssh', // raw data
+ kKeyTunnelException = 'Ntnl', // not tunnel
};
enum {
diff --git a/include/media/stagefright/OMXCodec.h b/include/media/stagefright/OMXCodec.h
index 5c169e7..6e3d5a4 100644
--- a/include/media/stagefright/OMXCodec.h
+++ b/include/media/stagefright/OMXCodec.h
@@ -103,6 +103,8 @@
#if defined(OMAP_ENHANCEMENT)
kAvoidMemcopyInputRecordingFrames = 0x20000000,
#endif
+ kRequiresGlobalFlush = 0x20000000, // 2^29
+ kRequiresWMAProComponent = 0x40000000, //2^30
};
struct CodecNameAndQuirks {
@@ -142,10 +144,14 @@
EXECUTING_TO_IDLE,
IDLE_TO_LOADED,
RECONFIGURING,
+ PAUSING,
+ FLUSHING,
+ PAUSED,
ERROR
};
enum {
+ kPortIndexBoth = -1,
kPortIndexInput = 0,
kPortIndexOutput = 1
};
@@ -367,6 +373,9 @@
OMXCodec(const OMXCodec &);
OMXCodec &operator=(const OMXCodec &);
+
+ int32_t mNumBFrames;
+ bool mInSmoothStreamingMode;
};
struct CodecCapabilities {
diff --git a/include/media/stagefright/TunnelPlayer.h b/include/media/stagefright/TunnelPlayer.h
new file mode 100644
index 0000000..bec2485
--- /dev/null
+++ b/include/media/stagefright/TunnelPlayer.h
@@ -0,0 +1,180 @@
+/*
+ * Copyright (c) 2009-2012, The Linux Foundation. All rights reserved.
+ * Not a Contribution
+ * 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 TUNNEL_PLAYER_H_
+
+#define TUNNEL_PLAYER_H_
+
+#include "AudioPlayer.h"
+#include <utils/threads.h>
+#include <utils/List.h>
+#include <utils/Vector.h>
+#include <fcntl.h>
+#include <pthread.h>
+#include <include/TimedEventQueue.h>
+
+// Pause timeout = 3sec
+#define TUNNEL_PAUSE_TIMEOUT_USEC 3000000
+namespace android {
+
+class TunnelPlayer : public AudioPlayer {
+public:
+ enum {
+ REACHED_EOS,
+ SEEK_COMPLETE
+ };
+
+ TunnelPlayer(const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck,
+ AwesomePlayer *audioObserver = NULL, bool hasVideo = false);
+
+ virtual ~TunnelPlayer();
+
+ // Caller retains ownership of "source".
+ virtual void setSource(const sp<MediaSource> &source);
+
+ // Return time in us.
+ virtual int64_t getRealTimeUs();
+
+ virtual status_t start(bool sourceAlreadyStarted = false);
+
+ virtual void pause(bool playPendingSamples = false);
+ virtual status_t resume();
+
+ // Returns the timestamp of the last buffer played (in us).
+ virtual int64_t getMediaTimeUs();
+
+ // Returns true iff a mapping is established, i.e. the TunnelPlayer
+ // has played at least one frame of audio.
+ virtual bool getMediaTimeMapping(int64_t *realtime_us, int64_t *mediatime_us);
+
+ virtual status_t seekTo(int64_t time_us);
+
+ virtual bool isSeeking();
+ virtual bool reachedEOS(status_t *finalStatus);
+
+
+ static int mTunnelObjectsAlive;
+ static const int getTunnelObjectsAliveMax();
+private:
+ int64_t mPositionTimeMediaUs;
+ int64_t mPositionTimeRealUs;
+ bool mInternalSeeking;
+ bool mIsAudioRouted;
+ bool mStarted;
+ bool mPaused;
+ int32_t mChannelMask;
+ int32_t numChannels;
+ int32_t mSampleRate;
+ int64_t mLatencyUs;
+ size_t mFrameSize;
+ int64_t mNumFramesPlayed;
+ int64_t mNumFramesPlayedSysTimeUs;
+ audio_format_t mFormat;
+ bool mHasVideo;
+
+ pthread_t mExtractorThread;
+
+ //Kill Thread boolean
+ bool mKillExtractorThread;
+
+ //Thread alive boolean
+ bool mExtractorThreadAlive;
+
+ //Declare the condition Variables and Mutex
+ Mutex mExtractorMutex;
+ Condition mExtractorCv;
+
+
+ // make sure Decoder thread has exited
+ void requestAndWaitForExtractorThreadExit_l();
+
+
+ static void *extractorThreadWrapper(void *me);
+ void extractorThreadEntry();
+
+ void createThreads();
+
+ void onPauseTimeOut();
+
+ sp<MediaSource> mSource;
+
+ MediaBuffer *mInputBuffer;
+
+ Mutex mLock;
+
+ bool mSeeking;
+ bool mReachedEOS;
+ bool mReachedOutputEOS;
+ status_t mFinalStatus;
+ int64_t mSeekTimeUs;
+ int64_t mPauseTime;
+
+
+ bool mIsFirstBuffer;
+ status_t mFirstBufferResult;
+ MediaBuffer *mFirstBuffer;
+ TimedEventQueue mQueue;
+ bool mQueueStarted;
+ sp<TimedEventQueue::Event> mPauseEvent;
+ bool mPauseEventPending;
+
+ sp<MediaPlayerBase::AudioSink> mAudioSink;
+ AwesomePlayer *mObserver;
+
+ static size_t AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *data, size_t size, void *me,
+ MediaPlayerBase::AudioSink::cb_event_t event);
+
+ void getPlayedTimeFromDSP_l(int64_t *timeStamp);
+ void getOffsetRealTime_l(int64_t *offsetTime);
+
+ size_t fillBuffer(void *data, size_t size);
+
+ void reset();
+ bool seekTooClose(int64_t time_us);
+
+ TunnelPlayer(const TunnelPlayer &);
+ TunnelPlayer &operator=(const TunnelPlayer &);
+};
+
+struct TunnelEvent : public TimedEventQueue::Event {
+ TunnelEvent(TunnelPlayer *player,
+ void (TunnelPlayer::*method)())
+ : mPlayer(player),
+ mMethod(method) {
+ }
+
+protected:
+ virtual ~TunnelEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ (mPlayer->*mMethod)();
+ }
+
+private:
+ TunnelPlayer *mPlayer;
+ void (TunnelPlayer::*mMethod)();
+
+ TunnelEvent(const TunnelEvent &);
+ TunnelEvent &operator=(const TunnelEvent &);
+};
+
+} // namespace android
+
+#endif // LPA_PLAYER_H_
diff --git a/include/media/stagefright/WAVEWriter.h b/include/media/stagefright/WAVEWriter.h
new file mode 100644
index 0000000..a314008
--- /dev/null
+++ b/include/media/stagefright/WAVEWriter.h
@@ -0,0 +1,108 @@
+/*
+ * Copyright (c) 2012 - 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef WAVE_WRITER_H_
+
+#define WAVE_WRITER_H_
+
+#include <stdio.h>
+
+#include <media/stagefright/MediaWriter.h>
+#include <utils/threads.h>
+
+namespace android {
+
+
+#define ID_RIFF 0x46464952
+#define ID_WAVE 0x45564157
+#define ID_FMT 0x20746d66
+#define ID_DATA 0x61746164
+#define FORMAT_PCM 1
+
+
+struct MediaSource;
+struct MetaData;
+
+struct wav_header {
+ uint32_t riff_id;
+ uint32_t riff_sz;
+ uint32_t riff_fmt;
+ uint32_t fmt_id;
+ uint32_t fmt_sz;
+ uint16_t audio_format;
+ uint16_t num_channels;
+ uint32_t sample_rate;
+ uint32_t byte_rate; /* sample_rate * num_channels * bps / 8 */
+ uint16_t block_align; /* num_channels * bps / 8 */
+ uint16_t bits_per_sample;
+ uint32_t data_id;
+ uint32_t data_sz;
+};
+
+
+struct WAVEWriter : public MediaWriter {
+ WAVEWriter(const char *filename);
+ WAVEWriter(int fd);
+
+ status_t initCheck() const;
+
+ virtual status_t addSource(const sp<MediaSource> &source);
+ virtual bool reachedEOS();
+ virtual status_t start(MetaData *params = NULL);
+ virtual status_t stop();
+ virtual status_t pause();
+
+protected:
+ virtual ~WAVEWriter();
+
+private:
+ int mFd;
+ status_t mInitCheck;
+ sp<MediaSource> mSource;
+ bool mStarted;
+ volatile bool mPaused;
+ volatile bool mResumed;
+ volatile bool mDone;
+ volatile bool mReachedEOS;
+ pthread_t mThread;
+ int64_t mEstimatedSizeBytes;
+ int64_t mEstimatedDurationUs;
+
+ static void *ThreadWrapper(void *);
+ status_t threadFunc();
+ bool exceedsFileSizeLimit();
+ bool exceedsFileDurationLimit();
+
+ WAVEWriter(const WAVEWriter &);
+ WAVEWriter &operator=(const WAVEWriter &);
+};
+
+} // namespace android
+
+#endif // WAVE_WRITER_H_
diff --git a/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp b/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp
index 8d18d5c..5386cdd 100755
--- a/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp
+++ b/libvideoeditor/vss/stagefrightshells/src/VideoEditorVideoDecoder.cpp
@@ -827,17 +827,17 @@
pDecShellContext->mCropRect.left = cropLeft;
pDecShellContext->mCropRect.right = cropRight;
-#ifdef QCOM_HARDWARE
pDecShellContext->mCropRect.top = cropTop;
pDecShellContext->mCropRect.bottom = cropBottom;
+
+#ifdef QCOM_HARDWARE
+ width = vWidth;
+ height = vHeight;
#else
width = cropRight - cropLeft + 1;
height = cropBottom - cropTop + 1;
#endif
- width = vWidth;
- height = vHeight;
-
ALOGV("VideoDecoder_configureFromMetadata : W=%d H=%d", width, height);
VIDEOEDITOR_CHECK((0 != width) && (0 != height), M4ERR_PARAMETER);
diff --git a/media/libmedia/Android.mk b/media/libmedia/Android.mk
index 2bd8475..fb12e08 100644
--- a/media/libmedia/Android.mk
+++ b/media/libmedia/Android.mk
@@ -11,11 +11,22 @@
include $(CLEAR_VARS)
+LOCAL_SRC_FILES:= AudioParameter.cpp
+LOCAL_MODULE:= libaudioparameter
+LOCAL_MODULE_TAGS := optional
+LOCAL_SHARED_LIBRARIES := libutils libcutils
+
+include $(BUILD_SHARED_LIBRARY)
+
+include $(CLEAR_VARS)
+
LOCAL_SRC_FILES:= \
AudioTrack.cpp \
AudioTrackShared.cpp \
IAudioFlinger.cpp \
IAudioFlingerClient.cpp \
+ IDirectTrack.cpp \
+ IDirectTrackClient.cpp \
IAudioTrack.cpp \
IAudioRecord.cpp \
ICrypto.cpp \
diff --git a/media/libmedia/AudioParameter.cpp b/media/libmedia/AudioParameter.cpp
index e3fea77..cf4d8c3 100644
--- a/media/libmedia/AudioParameter.cpp
+++ b/media/libmedia/AudioParameter.cpp
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2006-2011 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -32,6 +35,16 @@
const char * const AudioParameter::keyFrameCount = AUDIO_PARAMETER_STREAM_FRAME_COUNT;
const char * const AudioParameter::keyInputSource = AUDIO_PARAMETER_STREAM_INPUT_SOURCE;
const char * const AudioParameter::keyScreenState = AUDIO_PARAMETER_KEY_SCREEN_STATE;
+#ifdef QCOM_FM_ENABLED
+const char * const AudioParameter::keyHandleFm = AUDIO_PARAMETER_KEY_HANDLE_FM;
+const char * const AudioParameter::keyVoipCheck = AUDIO_PARAMETER_KEY_VOIP_CHECK;
+const char * const AudioParameter::keyFluenceType = AUDIO_PARAMETER_KEY_FLUENCE_TYPE;
+const char * const AudioParameter::keySSR = AUDIO_PARAMETER_KEY_SSR;
+const char * const AudioParameter::keyHandleA2dpDevice = AUDIO_PARAMETER_KEY_HANDLE_A2DP_DEVICE;
+const char * const AudioParameter::keyADSPStatus = AUDIO_PARAMETER_KEY_ADSP_STATUS;
+const char * const AudioParameter::keyCanOpenProxy = AUDIO_CAN_OPEN_PROXY;
+const char * const AudioParameter::keyFmVolume = AUDIO_PARAMETER_KEY_FM_VOLUME;
+#endif
AudioParameter::AudioParameter(const String8& keyValuePairs)
{
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index 666fafa..0e33805 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -60,9 +60,10 @@
// We double the size of input buffer for ping pong use of record buffer.
size <<= 1;
- // Assumes audio_is_linear_pcm(format)
+ if (audio_is_linear_pcm(format) || format == AUDIO_FORMAT_AMR_WB) {
uint32_t channelCount = popcount(channelMask);
size /= channelCount * audio_bytes_per_sample(format);
+ }
*frameCount = size;
return NO_ERROR;
@@ -194,11 +195,6 @@
ALOGE("Invalid format %d", format);
return BAD_VALUE;
}
- // Temporary restriction: AudioFlinger currently supports 16-bit PCM only
- if (format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("Format %d is not supported", format);
- return BAD_VALUE;
- }
mFormat = format;
if (!audio_is_input_channel(channelMask)) {
@@ -206,20 +202,27 @@
return BAD_VALUE;
}
mChannelMask = channelMask;
- uint32_t channelCount = popcount(channelMask);
+ uint32_t channelCount = popcount(channelMask
+ &(AUDIO_CHANNEL_IN_STEREO|AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_5POINT1));
mChannelCount = channelCount;
- // Assumes audio_is_linear_pcm(format), else sizeof(uint8_t)
- mFrameSize = channelCount * audio_bytes_per_sample(format);
+ mFrameSize = frameSize();
- // validate framecount
- size_t minFrameCount = 0;
- status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
- sampleRate, format, channelMask);
+ size_t inputBuffSizeInBytes = -1;
+ status_t status = AudioSystem::getInputBufferSize(sampleRate, format, channelMask, &inputBuffSizeInBytes);
if (status != NO_ERROR) {
- ALOGE("getMinFrameCount() failed; status %d", status);
- return status;
+ ALOGE("AudioSystem could not query the input buffer size; status %d", status);
+ return NO_INIT;
}
+
+ if (inputBuffSizeInBytes == 0) {
+ ALOGE("Unsupported configuration: sampleRate %u, format %d, channelMask %#x",
+ sampleRate, format, channelMask);
+ return BAD_VALUE;
+ }
+
+ int minFrameCount = (inputBuffSizeInBytes * 2)/mFrameSize;
+
ALOGV("AudioRecord::set() minFrameCount = %d", minFrameCount);
if (frameCount == 0) {
@@ -276,6 +279,11 @@
return NO_ERROR;
}
+audio_source_t AudioRecord::inputSource() const
+{
+ return mInputSource;
+}
+
// -------------------------------------------------------------------------
status_t AudioRecord::start(AudioSystem::sync_event_t event, int triggerSession)
@@ -463,6 +471,10 @@
}
int originalSessionId = mSessionId;
+ if (inputSource() == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+ ALOGV("Notify use of Voice Communication");
+ trackFlags |= IAudioFlinger::TRACK_VOICE_COMMUNICATION;
+ }
sp<IAudioRecord> record = audioFlinger->openRecord(input,
mSampleRate, mFormat,
mChannelMask,
@@ -952,6 +964,36 @@
return result;
}
+size_t AudioRecord::frameSize() const
+{
+ if (inputSource() == AUDIO_SOURCE_VOICE_COMMUNICATION) {
+ if (audio_is_linear_pcm(mFormat)) {
+ return channelCount()*audio_bytes_per_sample(mFormat);
+ } else {
+ return channelCount()*sizeof(int16_t);
+ }
+ } else {
+ if (format() ==AUDIO_FORMAT_AMR_NB) {
+ return channelCount() * AMR_FRAMESIZE; // Full rate framesize
+ } else if (format() == AUDIO_FORMAT_EVRC) {
+ return channelCount() * EVRC_FRAMESIZE; // Full rate framesize
+ } else if (format() == AUDIO_FORMAT_QCELP) {
+ return channelCount() * QCELP_FRAMESIZE; // Full rate framesize
+ } else if (format() == AUDIO_FORMAT_AAC) {
+ // Not actual framsize but for variable frame rate AAC encoding,
+ // buffer size is treated as a frame size
+ return AAC_FRAMESIZE;
+ } else if(format() == AUDIO_FORMAT_AMR_WB) {
+ return channelCount() * AMR_WB_FRAMESIZE;
+ }
+ if (audio_is_linear_pcm(mFormat)) {
+ return channelCount()*audio_bytes_per_sample(mFormat);
+ } else {
+ return sizeof(uint8_t);
+ }
+ }
+}
+
// =========================================================================
void AudioRecord::DeathNotifier::binderDied(const wp<IBinder>& who)
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index fffd4a5..234ec71 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -316,19 +316,9 @@
audio_stream_type_t streamType,
uint32_t* latency)
{
- OutputDescriptor *outputDesc;
-
- gLock.lock();
- outputDesc = AudioSystem::gOutputs.valueFor(output);
- if (outputDesc == NULL) {
- gLock.unlock();
- const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
- if (af == 0) return PERMISSION_DENIED;
- *latency = af->latency(output);
- } else {
- *latency = outputDesc->latency;
- gLock.unlock();
- }
+ const sp<IAudioFlinger>& af = AudioSystem::get_audio_flinger();
+ if (af == 0) return PERMISSION_DENIED;
+ *latency = af->latency(output);
ALOGV("getLatency() streamType %d, output %d, latency %d", streamType, output, *latency);
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index fe5cd9e..f626f6f 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -53,19 +54,22 @@
// audio_format_t format
// audio_channel_mask_t channelMask
// audio_output_flags_t flags
- uint32_t afSampleRate;
+ uint32_t afSampleRate = 0;
if (AudioSystem::getOutputSamplingRate(&afSampleRate, streamType) != NO_ERROR) {
return NO_INIT;
}
- size_t afFrameCount;
+ size_t afFrameCount = 0;
if (AudioSystem::getOutputFrameCount(&afFrameCount, streamType) != NO_ERROR) {
return NO_INIT;
}
- uint32_t afLatency;
+ uint32_t afLatency = 0;
if (AudioSystem::getOutputLatency(&afLatency, streamType) != NO_ERROR) {
return NO_INIT;
}
-
+ if(!afSampleRate || !afFrameCount) {
+ ALOGW("samplerate or framecount 0");
+ return NO_INIT;
+ }
// Ensure that buffer depth covers at least audio hardware latency
uint32_t minBufCount = afLatency / ((1000 * afFrameCount) / afSampleRate);
if (minBufCount < 2) {
@@ -85,7 +89,10 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mAudioFlinger(NULL),
+ mObserver(NULL),
+ mCblk(NULL)
{
}
@@ -105,7 +112,10 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mAudioFlinger(NULL),
+ mObserver(NULL),
+ mCblk(NULL)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
@@ -128,7 +138,11 @@
: mStatus(NO_INIT),
mIsTimed(false),
mPreviousPriority(ANDROID_PRIORITY_NORMAL),
- mPreviousSchedulingGroup(SP_DEFAULT)
+ mPreviousSchedulingGroup(SP_DEFAULT),
+ mProxy(NULL),
+ mAudioFlinger(NULL),
+ mObserver(NULL),
+ mCblk(NULL)
{
mStatus = set(streamType, sampleRate, format, channelMask,
0 /*frameCount*/, flags, cbf, user, notificationFrames,
@@ -148,10 +162,16 @@
mAudioTrackThread->requestExitAndWait();
mAudioTrackThread.clear();
}
- mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
- mAudioTrack.clear();
+ if (mAudioTrack != 0) {
+ mAudioTrack->asBinder()->unlinkToDeath(mDeathNotifier, this);
+ mAudioTrack.clear();
+ AudioSystem::releaseAudioSessionId(mSessionId);
+ }
+
+ if (mDirectTrack != 0) {
+ mDirectTrack.clear();
+ }
IPCThreadState::self()->flushCommands();
- AudioSystem::releaseAudioSessionId(mSessionId);
}
}
@@ -286,12 +306,30 @@
uint32_t channelCount = popcount(channelMask);
mChannelCount = channelCount;
- if (audio_is_linear_pcm(format)) {
- mFrameSize = channelCount * audio_bytes_per_sample(format);
- mFrameSizeAF = channelCount * sizeof(int16_t);
+ if ((streamType == AUDIO_STREAM_VOICE_CALL)
+ && (channelCount == 1)
+ && ((sampleRate == 8000 || sampleRate == 16000)))
+ {
+ ALOGD("Turn on Direct Output for VOIP RX");
+ flags = (audio_output_flags_t)(flags | AUDIO_OUTPUT_FLAG_VOIP_RX|AUDIO_OUTPUT_FLAG_DIRECT);
+ }
+
+ if ((audio_stream_type_t)streamType == AUDIO_STREAM_VOICE_CALL) {
+ if (audio_is_linear_pcm(format)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ mFrameSizeAF = channelCount * sizeof(int16_t);
+ } else {
+ mFrameSize = sizeof(uint16_t);
+ mFrameSizeAF = sizeof(uint16_t);
+ }
} else {
- mFrameSize = sizeof(uint8_t);
- mFrameSizeAF = sizeof(uint8_t);
+ if (audio_is_linear_pcm(format)) {
+ mFrameSize = channelCount * audio_bytes_per_sample(format);
+ mFrameSizeAF = channelCount * sizeof(int16_t);
+ } else {
+ mFrameSize = sizeof(uint8_t);
+ mFrameSizeAF = sizeof(uint8_t);
+ }
}
audio_io_handle_t output = AudioSystem::getOutput(
@@ -316,39 +354,70 @@
mAuxEffectId = 0;
mFlags = flags;
mCbf = cbf;
+ mSampleRate = sampleRate;
- if (cbf != NULL) {
- mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
- mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
- }
-
- // create the IAudioTrack
- status_t status = createTrack_l(streamType,
- sampleRate,
- format,
- frameCount,
- flags,
- sharedBuffer,
- output,
- 0 /*epoch*/);
-
- if (status != NO_ERROR) {
- if (mAudioTrackThread != 0) {
- mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
- mAudioTrackThread->requestExitAndWait();
- mAudioTrackThread.clear();
+ if (flags & AUDIO_OUTPUT_FLAG_LPA || flags & AUDIO_OUTPUT_FLAG_TUNNEL) {
+ ALOGV("Creating Direct Track");
+ const sp<IAudioFlinger>& audioFlinger = AudioSystem::get_audio_flinger();
+ if (audioFlinger == 0) {
+ ALOGE("Could not get audioflinger");
+ return NO_INIT;
}
- //Use of direct and offloaded output streams is ref counted by audio policy manager.
- // As getOutput was called above and resulted in an output stream to be opened,
- // we need to release it.
- AudioSystem::releaseOutput(output);
- return status;
+ mAudioFlinger = audioFlinger;
+ status_t status = NO_ERROR;
+ mAudioDirectOutput = output;
+ mDirectTrack = audioFlinger->createDirectTrack( getpid(),
+ sampleRate,
+ channelMask,
+ mAudioDirectOutput,
+ &mSessionId,
+ this,
+ streamType,
+ &status);
+ if(status != NO_ERROR) {
+ ALOGE("createDirectTrack returned with status %d", status);
+ return status;
+ }
+ mAudioTrack = NULL;
+ mSharedBuffer = NULL;
}
+ else {
+ if (cbf != NULL) {
+ mAudioTrackThread = new AudioTrackThread(*this, threadCanCallJava);
+ mAudioTrackThread->run("AudioTrack", ANDROID_PRIORITY_AUDIO, 0 /*stack*/);
+ }
+ // create the IAudioTrack
+ status_t status = createTrack_l(streamType,
+ sampleRate,
+ format,
+ frameCount,
+ flags,
+ sharedBuffer,
+ output,
+ 0 /*epoch*/);
+
+ if (status != NO_ERROR) {
+ if (mAudioTrackThread != 0) {
+ mAudioTrackThread->requestExit(); // see comment in AudioTrack.h
+ mAudioTrackThread->requestExitAndWait();
+ mAudioTrackThread.clear();
+ }
+ //Use of direct and offloaded output streams is ref counted by audio policy manager.
+ // As getOutput was called above and resulted in an output stream to be opened,
+ // we need to release it.
+ AudioSystem::releaseOutput(output);
+ return status;
+ }
+ AudioSystem::acquireAudioSessionId(mSessionId);
+ mAudioDirectOutput = -1;
+ mDirectTrack = NULL;
+ mSharedBuffer = sharedBuffer;
+ }
+ mUserData = user;
mStatus = NO_ERROR;
mStreamType = streamType;
mFormat = format;
- mSharedBuffer = sharedBuffer;
mState = STATE_STOPPED;
mUserData = user;
mLoopPeriod = 0;
@@ -356,7 +425,6 @@
mMarkerReached = false;
mNewPosition = 0;
mUpdatePeriod = 0;
- AudioSystem::acquireAudioSessionId(mSessionId);
mSequence = 1;
mObservedSequence = mSequence;
mInUnderrun = false;
@@ -367,10 +435,27 @@
// -------------------------------------------------------------------------
+uint32_t AudioTrack::latency() const
+{
+ if (mAudioDirectOutput != -1) {
+ return mAudioFlinger->latency(mAudioDirectOutput);
+ } else if (mOutput != 0) {
+ uint32_t afLatency = 0;
+ uint32_t newLatency = 0;
+ AudioSystem::getLatency(mOutput, mStreamType, &afLatency);
+ if (0 != mSampleRate){
+ newLatency = afLatency + (1000*mCblk->frameCount_) / mSampleRate;
+ }
+ ALOGD("latency() mLatency = %d, newLatency = %d", mLatency, newLatency);
+ return newLatency;
+ }
+ return mLatency;
+}
+
status_t AudioTrack::start()
{
+ status_t status = NO_ERROR;
AutoMutex lock(mLock);
-
if (mState == STATE_ACTIVE) {
return INVALID_OPERATION;
}
@@ -383,6 +468,10 @@
} else {
mState = STATE_ACTIVE;
}
+ if (mDirectTrack != NULL) {
+ mDirectTrack->start();
+ return status;
+ }
if (previousState == STATE_STOPPED || previousState == STATE_FLUSHED) {
// reset current position as seen by client to 0
mProxy->setEpoch(mProxy->getEpoch() - mProxy->getPosition());
@@ -406,7 +495,6 @@
androidSetThreadPriority(0, ANDROID_PRIORITY_AUDIO);
}
- status_t status = NO_ERROR;
if (!(flags & CBLK_INVALID)) {
status = mAudioTrack->start();
if (status == DEAD_OBJECT) {
@@ -447,28 +535,32 @@
mState = STATE_STOPPED;
}
- mProxy->interrupt();
- mAudioTrack->stop();
- // the playback head position will reset to 0, so if a marker is set, we need
- // to activate it again
- mMarkerReached = false;
+ if(mDirectTrack != NULL) {
+ mDirectTrack->stop();
+ } else if (mAudioTrack != NULL) {
+ mProxy->interrupt();
+ mAudioTrack->stop();
+ // the playback head position will reset to 0, so if a marker is set, we need
+ // to activate it again
+ mMarkerReached = false;
#if 0
- // Force flush if a shared buffer is used otherwise audioflinger
- // will not stop before end of buffer is reached.
- // It may be needed to make sure that we stop playback, likely in case looping is on.
- if (mSharedBuffer != 0) {
- flush_l();
- }
+ // Force flush if a shared buffer is used otherwise audioflinger
+ // will not stop before end of buffer is reached.
+ // It may be needed to make sure that we stop playback, likely in case looping is on.
+ if (mSharedBuffer != 0) {
+ flush_l();
+ }
#endif
- sp<AudioTrackThread> t = mAudioTrackThread;
- if (t != 0) {
- if (!isOffloaded()) {
- t->pause();
+ sp<AudioTrackThread> t = mAudioTrackThread;
+ if (t != 0) {
+ if (!isOffloaded()) {
+ t->pause();
+ }
+ } else {
+ setpriority(PRIO_PROCESS, 0, mPreviousPriority);
+ set_sched_policy(0, mPreviousSchedulingGroup);
}
- } else {
- setpriority(PRIO_PROCESS, 0, mPreviousPriority);
- set_sched_policy(0, mPreviousSchedulingGroup);
}
}
@@ -480,10 +572,11 @@
void AudioTrack::flush()
{
- if (mSharedBuffer != 0) {
- return;
+ if(mDirectTrack != NULL) {
+ ALOGE("mdirect track flush");
+ mDirectTrack->flush();
+ return ;
}
- AutoMutex lock(mLock);
if (mState == STATE_ACTIVE || mState == STATE_FLUSHED) {
return;
}
@@ -518,8 +611,13 @@
} else {
return;
}
- mProxy->interrupt();
- mAudioTrack->pause();
+ if(mDirectTrack != NULL) {
+ ALOGV("mDirectTrack pause");
+ mDirectTrack->pause();
+ } else {
+ mProxy->interrupt();
+ mAudioTrack->pause();
+ }
}
status_t AudioTrack::setVolume(float left, float right)
@@ -531,8 +629,12 @@
AutoMutex lock(mLock);
mVolume[LEFT] = left;
mVolume[RIGHT] = right;
-
- mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
+ if(mDirectTrack != NULL) {
+ ALOGV("mDirectTrack->setVolume(left = %f , right = %f)", left,right);
+ mDirectTrack->setVolume(left, right);
+ } else {
+ mProxy->setVolumeLR((uint32_t(uint16_t(right * 0x1000)) << 16) | uint16_t(left * 0x1000));
+ }
if (isOffloaded()) {
mAudioTrack->signal();
@@ -547,6 +649,9 @@
status_t AudioTrack::setAuxEffectSendLevel(float level)
{
+ if (mDirectTrack != NULL) {
+ return NO_ERROR;
+ }
if (level < 0.0f || level > 1.0f) {
return BAD_VALUE;
}
@@ -594,6 +699,9 @@
}
AutoMutex lock(mLock);
+ if(mAudioDirectOutput != -1) {
+ return mAudioFlinger->sampleRate(mAudioDirectOutput);
+ }
return mSampleRate;
}
@@ -820,21 +928,21 @@
// Not all of these values are needed under all conditions, but it is easier to get them all
- uint32_t afLatency;
+ uint32_t afLatency = 0;
status = AudioSystem::getLatency(output, streamType, &afLatency);
if (status != NO_ERROR) {
ALOGE("getLatency(%d) failed status %d", output, status);
return NO_INIT;
}
- size_t afFrameCount;
+ size_t afFrameCount = 0;
status = AudioSystem::getFrameCount(output, streamType, &afFrameCount);
if (status != NO_ERROR) {
ALOGE("getFrameCount(output=%d, streamType=%d) status %d", output, streamType, status);
return NO_INIT;
}
- uint32_t afSampleRate;
+ uint32_t afSampleRate = 0;
status = AudioSystem::getSamplingRate(output, streamType, &afSampleRate);
if (status != NO_ERROR) {
ALOGE("getSamplingRate(output=%d, streamType=%d) status %d", output, streamType, status);
@@ -903,6 +1011,10 @@
// FIXME move these calculations and associated checks to server
// Ensure that buffer depth covers at least audio hardware latency
+ if(!afSampleRate && !afFrameCount) {
+ ALOGW("samplerate or framecount zero");
+ return NO_INIT;
+ }
uint32_t minBufCount = afLatency / ((1000 * afFrameCount)/afSampleRate);
ALOGV("afFrameCount=%d, minBufCount=%d, afSampleRate=%u, afLatency=%d",
afFrameCount, minBufCount, afSampleRate, afLatency);
@@ -1039,7 +1151,11 @@
mAudioTrack->attachAuxEffect(mAuxEffectId);
// FIXME don't believe this lie
- mLatency = afLatency + (1000*frameCount) / sampleRate;
+ if(sampleRate){
+ mLatency = afLatency + (1000*frameCount) / sampleRate;
+ }else{
+ mLatency = afLatency;
+ }
mFrameCount = frameCount;
// If IAudioTrack is re-created, don't let the requested frameCount
// decrease. This can confuse clients that cache frameCount().
@@ -1192,7 +1308,8 @@
// restart track if it was disabled by audioflinger due to previous underrun
if (mState == STATE_ACTIVE) {
audio_track_cblk_t* cblk = mCblk;
- if (android_atomic_and(~CBLK_DISABLED, &cblk->mFlags) & CBLK_DISABLED) {
+ if (cblk->mFlags & CBLK_DISABLED) {
+ android_atomic_and(~CBLK_DISABLED, &cblk->mFlags);
ALOGW("releaseBuffer() track %p name=%s disabled due to previous underrun, restarting",
this, mName.string());
// FIXME ignoring status
@@ -1205,6 +1322,11 @@
ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{
+ if (mDirectTrack != NULL) {
+ ssize_t written = 0;
+ written = mDirectTrack->write(buffer,userSize);
+ return written;
+ }
if (mTransfer != TRANSFER_SYNC || mIsTimed) {
return INVALID_OPERATION;
}
@@ -1761,7 +1883,9 @@
result.append(buffer);
snprintf(buffer, 255, " sample rate(%u), status(%d)\n", mSampleRate, mStatus);
result.append(buffer);
- snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, mLatency);
+ uint32_t afLatency = 0;
+ AudioSystem::getLatency(mOutput, mStreamType, &afLatency);
+ snprintf(buffer, 255, " state(%d), latency (%d)\n", mState, afLatency + (1000*mCblk->frameCount_) / mSampleRate);
result.append(buffer);
::write(fd, result.string(), result.size());
return NO_ERROR;
@@ -1784,6 +1908,25 @@
}
}
+void AudioTrack::notify(int msg) {
+ if (msg == EVENT_UNDERRUN) {
+ ALOGV("Posting event underrun to Audio Sink.");
+ mCbf(EVENT_UNDERRUN, mUserData, 0);
+ }
+ if (msg == EVENT_HW_FAIL) {
+ ALOGV("Posting event HW fail to Audio Sink.");
+ mCbf(EVENT_HW_FAIL, mUserData, 0);
+ }
+}
+
+status_t AudioTrack::getTimeStamp(uint64_t *tstamp) {
+ if (mDirectTrack != NULL) {
+ *tstamp = mDirectTrack->getTimeStamp();
+ ALOGE("Timestamp %lld ", *tstamp);
+ }
+ return NO_ERROR;
+}
+
// =========================================================================
AudioTrack::AudioTrackThread::AudioTrackThread(AudioTrack& receiver, bool bCanCallJava)
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 448a82e..d248d19 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -74,6 +75,7 @@
GET_PRIMARY_OUTPUT_SAMPLING_RATE,
GET_PRIMARY_OUTPUT_FRAME_COUNT,
SET_LOW_RAM_DEVICE,
+ CREATE_DIRECT_TRACK,
};
class BpAudioFlinger : public BpInterface<IAudioFlinger>
@@ -143,6 +145,47 @@
return track;
}
+ virtual sp<IDirectTrack> createDirectTrack(
+ pid_t pid,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_io_handle_t output,
+ int *sessionId,
+ IDirectTrackClient* client,
+ audio_stream_type_t streamType,
+ status_t *status)
+ {
+ Parcel data, reply;
+ sp<IDirectTrack> track;
+ data.writeInterfaceToken(IAudioFlinger::getInterfaceDescriptor());
+ data.writeInt32(pid);
+ data.writeInt32(sampleRate);
+ data.writeInt32(channelMask);
+ data.writeInt32((int32_t)output);
+ int lSessionId = 0;
+ if (sessionId != NULL) {
+ lSessionId = *sessionId;
+ }
+ data.writeInt32(lSessionId);
+ data.write(client, sizeof(IDirectTrackClient));
+ data.writeInt32((int32_t) streamType);
+ status_t lStatus = remote()->transact(CREATE_DIRECT_TRACK, data, &reply);
+ if (lStatus != NO_ERROR) {
+ ALOGE("createDirectTrack error: %s", strerror(-lStatus));
+ } else {
+ lSessionId = reply.readInt32();
+ if (sessionId != NULL) {
+ *sessionId = lSessionId;
+ }
+ lStatus = reply.readInt32();
+ track = interface_cast<IDirectTrack>(reply.readStrongBinder());
+ }
+ if (status) {
+ *status = lStatus;
+ }
+ return track;
+ }
+
virtual sp<IAudioRecord> openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
@@ -782,6 +825,24 @@
reply->writeStrongBinder(track->asBinder());
return NO_ERROR;
} break;
+ case CREATE_DIRECT_TRACK: {
+ CHECK_INTERFACE(IAudioFlinger, data, reply);
+ pid_t pid = data.readInt32();
+ uint32_t sampleRate = data.readInt32();
+ audio_channel_mask_t channelMask = data.readInt32();
+ audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
+ int sessionId = data.readInt32();
+ IDirectTrackClient* client;
+ data.read(client,sizeof(IDirectTrackClient));
+ int streamType = data.readInt32();
+ status_t status;
+ sp<IDirectTrack> track = createDirectTrack(pid,
+ sampleRate, channelMask, output, &sessionId, client,(audio_stream_type_t) streamType, &status);
+ reply->writeInt32(sessionId);
+ reply->writeInt32(status);
+ reply->writeStrongBinder(track->asBinder());
+ return NO_ERROR;
+ } break;
case OPEN_RECORD: {
CHECK_INTERFACE(IAudioFlinger, data, reply);
audio_io_handle_t input = (audio_io_handle_t) data.readInt32();
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index 3c0d4cf..5e4da5d 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -1,4 +1,7 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ *
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -49,9 +52,9 @@
uint32_t stream = *(const uint32_t *)param2;
ALOGV("ioConfigChanged stream %d", stream);
data.writeInt32(stream);
- } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED) {
- const AudioSystem::OutputDescriptor *desc =
- (const AudioSystem::OutputDescriptor *)param2;
+ } else if (event != AudioSystem::OUTPUT_CLOSED && event != AudioSystem::INPUT_CLOSED &&
+ event != AudioSystem::EFFECT_CONFIG_CHANGED) {
+ const AudioSystem::OutputDescriptor *desc = (const AudioSystem::OutputDescriptor *)param2;
data.writeInt32(desc->samplingRate);
data.writeInt32(desc->format);
data.writeInt32(desc->channelMask);
diff --git a/media/libmedia/IDirectTrack.cpp b/media/libmedia/IDirectTrack.cpp
new file mode 100644
index 0000000..e12cd74
--- /dev/null
+++ b/media/libmedia/IDirectTrack.cpp
@@ -0,0 +1,177 @@
+/*
+** Copyright (c) 2012, The Linux Foundation. All rights reserved.
+** Not a Contribution
+**
+** Copyright 2007, 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 "IDirectTrack"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <stdint.h>
+#include <sys/types.h>
+
+#include <binder/Parcel.h>
+
+#include <media/IDirectTrack.h>
+
+namespace android {
+
+enum {
+ START = IBinder::FIRST_CALL_TRANSACTION,
+ STOP,
+ FLUSH,
+ MUTE,
+ PAUSE,
+ SET_VOLUME,
+ WRITE,
+ GET_TIMESTAMP
+};
+
+class BpDirectTrack : public BpInterface<IDirectTrack>
+{
+public:
+ BpDirectTrack(const sp<IBinder>& impl)
+ : BpInterface<IDirectTrack>(impl)
+ {
+ }
+
+ virtual status_t start()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ status_t status = remote()->transact(START, data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ } else {
+ ALOGW("start() error: %s", strerror(-status));
+ }
+ return status;
+ }
+
+ virtual void stop()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ remote()->transact(STOP, data, &reply);
+ }
+
+ virtual void flush()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ remote()->transact(FLUSH, data, &reply);
+ }
+
+ virtual void mute(bool e)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ data.writeInt32(e);
+ remote()->transact(MUTE, data, &reply);
+ }
+
+ virtual void pause()
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ remote()->transact(PAUSE, data, &reply);
+ }
+
+ virtual void setVolume(float left, float right)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ remote()->transact(SET_VOLUME, data, &reply);
+ }
+
+ virtual ssize_t write(const void* buffer, size_t bytes)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ ssize_t bytesWritten = remote()->transact(WRITE, data, &reply);
+ return bytesWritten;
+ }
+
+ virtual int64_t getTimeStamp() {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrack::getInterfaceDescriptor());
+ int64_t tstamp = remote()->transact(GET_TIMESTAMP, data, &reply);
+ return tstamp;
+ }
+};
+
+IMPLEMENT_META_INTERFACE(DirectTrack, "android.media.IDirectTrack");
+
+// ----------------------------------------------------------------------
+
+status_t BnDirectTrack::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch(code) {
+ case START: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ reply->writeInt32(start());
+ return NO_ERROR;
+ } break;
+ case STOP: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ stop();
+ return NO_ERROR;
+ } break;
+ case FLUSH: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ flush();
+ return NO_ERROR;
+ } break;
+ case MUTE: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ mute( data.readInt32() );
+ return NO_ERROR;
+ } break;
+ case PAUSE: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ pause();
+ return NO_ERROR;
+ }
+ case SET_VOLUME: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ float left = 1.0;
+ float right = 1.0;
+ setVolume(left, right);
+ return NO_ERROR;
+ }
+ case WRITE: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ const void *buffer = (void *)data.readInt32();
+ size_t bytes = data.readInt32();
+ ssize_t bytesWritten = write(buffer, bytes);
+ reply->writeInt32(bytesWritten);
+ return NO_ERROR;
+ }
+ case GET_TIMESTAMP: {
+ CHECK_INTERFACE(IDirectTrack, data, reply);
+ int64_t time = getTimeStamp();
+ reply->writeInt32(time);
+ return NO_ERROR;
+ }
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
+
diff --git a/media/libmedia/IDirectTrackClient.cpp b/media/libmedia/IDirectTrackClient.cpp
new file mode 100644
index 0000000..f837a14
--- /dev/null
+++ b/media/libmedia/IDirectTrackClient.cpp
@@ -0,0 +1,68 @@
+/*
+** Copyright (c) 2012, The Linux Foundation. All rights reserved.
+** Not a Contribution.
+**
+** Copyright 2007, 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 <utils/RefBase.h>
+#include <binder/IInterface.h>
+#include <binder/Parcel.h>
+
+#include <media/IDirectTrackClient.h>
+
+namespace android {
+
+enum {
+ NOTIFY = IBinder::FIRST_CALL_TRANSACTION,
+};
+
+class BpDirectTrackClient: public BpInterface<IDirectTrackClient>
+{
+public:
+ BpDirectTrackClient(const sp<IBinder>& impl)
+ : BpInterface<IDirectTrackClient>(impl)
+ {
+ }
+
+ virtual void notify(int msg)
+ {
+ Parcel data, reply;
+ data.writeInterfaceToken(IDirectTrackClient::getInterfaceDescriptor());
+ data.writeInt32(msg);
+ remote()->transact(NOTIFY, data, &reply, IBinder::FLAG_ONEWAY);
+ }
+};
+
+IMPLEMENT_META_INTERFACE(DirectTrackClient, "android.media.IDirectTrackClient");
+
+// ----------------------------------------------------------------------
+
+status_t BnDirectTrackClient::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ switch (code) {
+ case NOTIFY: {
+ CHECK_INTERFACE(IDirectTrackClient, data, reply);
+ int msg = data.readInt32();
+ notify(msg);
+ return NO_ERROR;
+ } break;
+ default:
+ return BBinder::onTransact(code, data, reply, flags);
+ }
+}
+
+}; // namespace android
diff --git a/media/libmedia/MediaProfiles.cpp b/media/libmedia/MediaProfiles.cpp
index f69dbea..50ff5d0 100644
--- a/media/libmedia/MediaProfiles.cpp
+++ b/media/libmedia/MediaProfiles.cpp
@@ -45,7 +45,8 @@
{"amrwb", AUDIO_ENCODER_AMR_WB},
{"aac", AUDIO_ENCODER_AAC},
{"heaac", AUDIO_ENCODER_HE_AAC},
- {"aaceld", AUDIO_ENCODER_AAC_ELD}
+ {"aaceld", AUDIO_ENCODER_AAC_ELD},
+ {"lpcm", AUDIO_ENCODER_LPCM}
};
const MediaProfiles::NameToTagMap MediaProfiles::sFileFormatMap[] = {
@@ -74,6 +75,8 @@
{"wvga", CAMCORDER_QUALITY_WVGA},
{"vga", CAMCORDER_QUALITY_VGA},
{"wqvga", CAMCORDER_QUALITY_WQVGA},
+ {"4kuhd", CAMCORDER_QUALITY_4kUHD},
+ {"4kdci", CAMCORDER_QUALITY_4kDCI},
{"timelapselow", CAMCORDER_QUALITY_TIME_LAPSE_LOW},
{"timelapsehigh", CAMCORDER_QUALITY_TIME_LAPSE_HIGH},
@@ -83,6 +86,10 @@
{"timelapse720p", CAMCORDER_QUALITY_TIME_LAPSE_720P},
{"timelapse1080p", CAMCORDER_QUALITY_TIME_LAPSE_1080P},
{"timelapseqvga", CAMCORDER_QUALITY_TIME_LAPSE_QVGA},
+ {"timelapsevga", CAMCORDER_QUALITY_TIME_LAPSE_VGA},
+ {"timelapsewvga", CAMCORDER_QUALITY_TIME_LAPSE_WVGA},
+ {"timelapsefwvga", CAMCORDER_QUALITY_TIME_LAPSE_FWVGA},
+ {"timelapsewqvga", CAMCORDER_QUALITY_TIME_LAPSE_WQVGA},
};
/*static*/ void
@@ -804,6 +811,8 @@
MediaProfiles::createDefaultAudioEncoders(MediaProfiles *profiles)
{
profiles->mAudioEncoders.add(createDefaultAmrNBEncoderCap());
+ profiles->mAudioEncoders.add(createDefaultAacEncoderCap());
+ profiles->mAudioEncoders.add(createDefaultLpcmEncoderCap());
}
/*static*/ void
@@ -837,6 +846,18 @@
return new MediaProfiles::AudioEncoderCap(
AUDIO_ENCODER_AMR_NB, 5525, 12200, 8000, 8000, 1, 1);
}
+/*static*/ MediaProfiles::AudioEncoderCap*
+MediaProfiles::createDefaultAacEncoderCap()
+{
+ return new MediaProfiles::AudioEncoderCap(
+ AUDIO_ENCODER_AAC, 64000, 156000, 8000, 48000, 1, 2);
+}
+/*static*/ MediaProfiles::AudioEncoderCap*
+MediaProfiles::createDefaultLpcmEncoderCap()
+{
+ return new MediaProfiles::AudioEncoderCap(
+ AUDIO_ENCODER_LPCM, 768000, 4608000, 48000, 48000, 1, 6);
+}
/*static*/ void
MediaProfiles::createDefaultImageEncodingQualityLevels(MediaProfiles *profiles)
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 7e0881c..6297061 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -683,7 +683,7 @@
}
// Allows calls from JNI in idle state to notify errors
- if (!(msg == MEDIA_ERROR && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
+ if (!((msg == MEDIA_ERROR || msg == MEDIA_QOE) && mCurrentState == MEDIA_PLAYER_IDLE) && mPlayer == 0) {
ALOGV("notify(%d, %d, %d) callback on disconnected mediaplayer", msg, ext1, ext2);
if (locked) mLock.unlock(); // release the lock when done.
return;
@@ -758,6 +758,8 @@
break;
case MEDIA_SUBTITLE_DATA:
ALOGV("Received subtitle data message");
+ case MEDIA_QOE:
+ ALOGV("Received QOE Message for event : %d",ext2);
break;
default:
ALOGV("unrecognized message: (%d, %d, %d)", msg, ext1, ext2);
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index b47a785..badb84c 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -65,6 +65,18 @@
LOCAL_MODULE:= libmediaplayerservice
+ifeq ($(TARGET_ENABLE_QC_AV_ENHANCEMENTS),true)
+ LOCAL_CFLAGS += -DENABLE_AV_ENHANCEMENTS
+ LOCAL_C_INCLUDES += $(TOP)/frameworks/av/include/media
+ ifneq ($(TARGET_QCOM_MEDIA_VARIANT),)
+ LOCAL_C_INCLUDES += \
+ $(TOP)/hardware/qcom/media-$(TARGET_QCOM_MEDIA_VARIANT)/mm-core/inc
+ else
+ LOCAL_C_INCLUDES += \
+ $(TOP)/hardware/qcom/media/mm-core/inc
+ endif
+endif #TARGET_ENABLE_QC_AV_ENHANCEMENTS
+
include $(BUILD_SHARED_LIBRARY)
include $(call all-makefiles-under,$(LOCAL_PATH))
diff --git a/media/libmediaplayerservice/MediaPlayerFactory.cpp b/media/libmediaplayerservice/MediaPlayerFactory.cpp
index b1685b9..29e1edd 100644
--- a/media/libmediaplayerservice/MediaPlayerFactory.cpp
+++ b/media/libmediaplayerservice/MediaPlayerFactory.cpp
@@ -346,21 +346,25 @@
MediaPlayerFactory::IFactory* pFactory = NULL;
void* pFactoryLib = NULL;
typedef MediaPlayerFactory::IFactory* (*CreateDASHDriverFn)();
-
+ ALOGE("calling dlopen on FACTORY_LIB");
pFactoryLib = ::dlopen(FACTORY_LIB, RTLD_LAZY);
- if (pFactoryLib != NULL) {
- CreateDASHDriverFn pCreateFnPtr;
- pCreateFnPtr = (CreateDASHDriverFn) dlsym(pFactoryLib, FACTORY_CREATE_FN);
- if (pCreateFnPtr == NULL) {
- ALOGE("Could not locate pCreateFnPtr");
+ if (pFactoryLib == NULL) {
+ ALOGE("Failed to open FACTORY_LIB Error : %s ",::dlerror());
+ } else {
+ CreateDASHDriverFn pCreateFnPtr;
+ ALOGE("calling dlsym on pFactoryLib for FACTORY_CREATE_FN ");
+ pCreateFnPtr = (CreateDASHDriverFn) dlsym(pFactoryLib, FACTORY_CREATE_FN);
+ if (pCreateFnPtr == NULL) {
+ ALOGE("Could not locate pCreateFnPtr");
+ } else {
+ pFactory = pCreateFnPtr();
+ if(pFactory == NULL) {
+ ALOGE("Failed to invoke CreateDASHDriverFn...");
} else {
- pFactory = pCreateFnPtr();
- if(pFactory == NULL) {
- ALOGE("Failed to invoke CreateDASHDriverFn...");
- } else {
- registerFactory_l(pFactory,DASH_PLAYER);
- }
+ ALOGE("registering DASH Player factory...");
+ registerFactory_l(pFactory,DASH_PLAYER);
}
+ }
}
sInitComplete = true;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 9553458..951ca97 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -1,5 +1,8 @@
/*
**
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
+**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -78,6 +81,7 @@
#include "HDCP.h"
#include "HTTPBase.h"
#include "RemoteDisplay.h"
+#define DEFAULT_SAMPLE_RATE 44100
namespace {
using android::media::Metadata;
@@ -1372,6 +1376,11 @@
return mTrack->latency();
}
+audio_stream_type_t MediaPlayerService::AudioOutput::streamType () const
+{
+ return mStreamType;
+}
+
float MediaPlayerService::AudioOutput::msecsPerFrame() const
{
return mMsecsPerFrame;
@@ -1383,6 +1392,20 @@
return mTrack->getPosition(position);
}
+ssize_t MediaPlayerService::AudioOutput::sampleRate() const
+{
+ if (mTrack == 0) return NO_INIT;
+ return DEFAULT_SAMPLE_RATE;
+}
+
+status_t MediaPlayerService::AudioOutput::getTimeStamp(uint64_t *tstamp)
+{
+ if (tstamp == 0) return BAD_VALUE;
+ if (mTrack == 0) return NO_INIT;
+ mTrack->getTimeStamp(tstamp);
+ return NO_ERROR;
+}
+
status_t MediaPlayerService::AudioOutput::getFramesWritten(uint32_t *frameswritten) const
{
if (mTrack == 0) return NO_INIT;
@@ -1438,7 +1461,48 @@
{
mCallback = cb;
mCallbackCookie = cookie;
+ if (flags & AUDIO_OUTPUT_FLAG_LPA || flags & AUDIO_OUTPUT_FLAG_TUNNEL) {
+ ALOGV("AudioOutput open: with flags %x",flags);
+ channelMask = audio_channel_out_mask_from_count(channelCount);
+ if (0 == channelMask) {
+ ALOGE("open() error, can't derive mask for %d audio channels", channelCount);
+ return NO_INIT;
+ }
+ AudioTrack *audioTrack = NULL;
+ CallbackData *newcbd = NULL;
+ if (mCallback != NULL) {
+ newcbd = new CallbackData(this);
+ audioTrack = new AudioTrack(
+ mStreamType,
+ sampleRate,
+ format,
+ channelMask,
+ 0,
+ flags,
+ CallbackWrapper,
+ newcbd,
+ 0,
+ mSessionId);
+ if ((audioTrack == 0) || (audioTrack->initCheck() != NO_ERROR)) {
+ ALOGE("Unable to create audio track");
+ delete audioTrack;
+ delete newcbd;
+ return NO_INIT;
+ }
+ } else {
+ ALOGE("no callback supplied");
+ return NO_INIT;
+ }
+ deleteRecycledTrack();
+ ALOGV("setVolume");
+ mCallbackData = newcbd;
+ audioTrack->setVolume(mLeftVolume, mRightVolume);
+ mSampleRateHz = sampleRate;
+ mFlags = flags;
+ mTrack = audioTrack;
+ return NO_ERROR;
+ }
// Check argument "bufferCount" against the mininum buffer count
if (bufferCount < mMinBufferCount) {
ALOGD("bufferCount (%d) is too small and increased to %d", bufferCount, mMinBufferCount);
@@ -1667,7 +1731,6 @@
ssize_t MediaPlayerService::AudioOutput::write(const void* buffer, size_t size)
{
- LOG_FATAL_IF(mCallback != NULL, "Don't call write if supplying a callback.");
//ALOGV("write(%p, %u)", buffer, size);
if (mTrack != 0) {
@@ -1699,7 +1762,7 @@
void MediaPlayerService::AudioOutput::close()
{
ALOGV("close");
- mTrack.clear();
+ if (mTrack != 0) mTrack.clear();
}
void MediaPlayerService::AudioOutput::setVolume(float left, float right)
@@ -1752,55 +1815,97 @@
void MediaPlayerService::AudioOutput::CallbackWrapper(
int event, void *cookie, void *info) {
//ALOGV("callbackwrapper");
- CallbackData *data = (CallbackData*)cookie;
- data->lock();
- AudioOutput *me = data->getOutput();
- AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
- if (me == NULL) {
- // no output set, likely because the track was scheduled to be reused
- // by another player, but the format turned out to be incompatible.
+ if (event == AudioTrack::EVENT_UNDERRUN) {
+ ALOGW("Event underrun");
+ CallbackData *data = (CallbackData*)cookie;
+ data->lock();
+ AudioOutput *me = data->getOutput();
+ if (me == NULL) {
+ // no output set, likely because the track was scheduled to be reused
+ // by another player, but the format turned out to be incompatible.
+ data->unlock();
+ return;
+ }
+ ALOGD("Callback!!!");
+ (*me->mCallback)(
+ me, NULL, (size_t)AudioTrack::EVENT_UNDERRUN, me->mCallbackCookie, CB_EVENT_UNDERRUN);
data->unlock();
- if (buffer != NULL) {
- buffer->size = 0;
+ return;
+ }
+ if (event == AudioTrack::EVENT_HW_FAIL) {
+ ALOGW("Event hardware failure");
+ CallbackData *data = (CallbackData*)cookie;
+ if (data != NULL) {
+ data->lock();
+ AudioOutput *me = data->getOutput();
+ if (me == NULL) {
+ // no output set, likely because the track was
+ // scheduled to be reused
+ // by another player, but the format turned out
+ // to be incompatible.
+ data->unlock();
+ return;
+ }
+ ALOGV("Callback!!!");
+ (*me->mCallback)(me, NULL, (size_t)AudioTrack::EVENT_HW_FAIL,
+ me->mCallbackCookie, CB_EVENT_HW_FAIL);
+ data->unlock();
}
return;
}
-
- switch(event) {
- case AudioTrack::EVENT_MORE_DATA: {
- size_t actualSize = (*me->mCallback)(
- me, buffer->raw, buffer->size, me->mCallbackCookie,
- CB_EVENT_FILL_BUFFER);
-
- if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) {
- // We've reached EOS but the audio track is not stopped yet,
- // keep playing silence.
-
- memset(buffer->raw, 0, buffer->size);
- actualSize = buffer->size;
+ if (event == AudioTrack::EVENT_MORE_DATA) {
+ CallbackData *data = (CallbackData*)cookie;
+ data->lock();
+ AudioOutput *me = data->getOutput();
+ AudioTrack::Buffer *buffer = (AudioTrack::Buffer *)info;
+ if (me == NULL) {
+ // no output set, likely because the track was scheduled to be reused
+ // by another player, but the format turned out to be incompatible.
+ data->unlock();
+ if (buffer != NULL) {
+ buffer->size = 0;
+ }
+ return;
}
- buffer->size = actualSize;
- } break;
+ switch(event) {
+ case AudioTrack::EVENT_MORE_DATA: {
+ size_t actualSize = (*me->mCallback)(
+ me, buffer->raw, buffer->size, me->mCallbackCookie,
+ CB_EVENT_FILL_BUFFER);
+
+ if (actualSize == 0 && buffer->size > 0 && me->mNextOutput == NULL) {
+ // We've reached EOS but the audio track is not stopped yet,
+ // keep playing silence.
+
+ memset(buffer->raw, 0, buffer->size);
+ actualSize = buffer->size;
+ }
+
+ buffer->size = actualSize;
+ } break;
- case AudioTrack::EVENT_STREAM_END:
- ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_STREAM_END);
- break;
+ case AudioTrack::EVENT_STREAM_END:
+ ALOGV("callbackwrapper: deliver EVENT_STREAM_END");
+ (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_STREAM_END);
+ break;
- case AudioTrack::EVENT_NEW_IAUDIOTRACK :
- ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
- (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
- me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
- break;
+ case AudioTrack::EVENT_NEW_IAUDIOTRACK :
+ ALOGV("callbackwrapper: deliver EVENT_TEAR_DOWN");
+ (*me->mCallback)(me, NULL /* buffer */, 0 /* size */,
+ me->mCallbackCookie, CB_EVENT_TEAR_DOWN);
+ break;
- default:
- ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
+ default:
+ ALOGE("received unknown event type: %d inside CallbackWrapper !", event);
+ }
+
+ data->unlock();
}
- data->unlock();
+ return;
}
int MediaPlayerService::AudioOutput::getSessionId() const
@@ -1833,6 +1938,11 @@
return NO_ERROR;
}
+ssize_t MediaPlayerService::AudioCache::sampleRate() const
+{
+ return mSampleRate;
+}
+
status_t MediaPlayerService::AudioCache::getFramesWritten(uint32_t *written) const
{
if (written == 0) return BAD_VALUE;
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 21f4117..1df311c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -1,5 +1,8 @@
/*
**
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
+**
** Copyright 2008, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -82,6 +85,7 @@
virtual ssize_t channelCount() const;
virtual ssize_t frameSize() const;
virtual uint32_t latency() const;
+ virtual audio_stream_type_t streamType() const;
virtual float msecsPerFrame() const;
virtual status_t getPosition(uint32_t *position) const;
virtual status_t getFramesWritten(uint32_t *frameswritten) const;
@@ -112,9 +116,13 @@
void setNextOutput(const sp<AudioOutput>& nextOutput);
void switchToNextOutput();
virtual bool needsTrailingPadding() { return mNextOutput == NULL; }
+
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys);
+ virtual ssize_t sampleRate() const;
+ virtual status_t getTimeStamp(uint64_t *tstamp);
+
private:
static void setMinBufferCount();
static void CallbackWrapper(
@@ -208,7 +216,6 @@
void setAudioStreamType(audio_stream_type_t streamType) {}
void setVolume(float left, float right) {}
virtual status_t setPlaybackRatePermille(int32_t ratePermille) { return INVALID_OPERATION; }
- uint32_t sampleRate() const { return mSampleRate; }
audio_format_t format() const { return mFormat; }
size_t size() const { return mSize; }
status_t wait();
@@ -218,6 +225,7 @@
static void notify(void* cookie, int msg,
int ext1, int ext2, const Parcel *obj);
virtual status_t dump(int fd, const Vector<String16>& args) const;
+ virtual ssize_t sampleRate() const;
private:
AudioCache();
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index 4fc38da..c1c6e4a 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -17,7 +17,7 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "StagefrightRecorder"
#include <utils/Log.h>
-
+#include <media/AudioParameter.h>
#include "StagefrightRecorder.h"
#include <binder/IPCThreadState.h>
@@ -29,6 +29,8 @@
#include <media/stagefright/AudioSource.h>
#include <media/stagefright/AMRWriter.h>
#include <media/stagefright/AACWriter.h>
+#include <media/stagefright/ExtendedWriter.h>
+#include <media/stagefright/WAVEWriter.h>
#include <media/stagefright/CameraSource.h>
#include <media/stagefright/CameraSourceTimeLapse.h>
#include <media/stagefright/MPEG2TSWriter.h>
@@ -42,13 +44,18 @@
#include <camera/ICamera.h>
#include <camera/CameraParameters.h>
#include <gui/Surface.h>
+#include <utils/String8.h>
#include <utils/Errors.h>
#include <sys/types.h>
#include <ctype.h>
#include <unistd.h>
+#include "ExtendedUtils.h"
#include <system/audio.h>
+#ifdef ENABLE_AV_ENHANCEMENTS
+#include <QCMediaDefs.h>
+#endif
#include "ARTPWriter.h"
#include <cutils/properties.h>
@@ -103,6 +110,9 @@
ALOGE("Invalid audio source: %d", as);
return BAD_VALUE;
}
+ if (ExtendedUtils::ShellProp::isAudioDisabled()) {
+ return OK;
+ }
if (as == AUDIO_SOURCE_DEFAULT) {
mAudioSource = AUDIO_SOURCE_MIC;
@@ -155,12 +165,32 @@
return BAD_VALUE;
}
+ if (ExtendedUtils::ShellProp::isAudioDisabled()) {
+ return OK;
+ }
if (ae == AUDIO_ENCODER_DEFAULT) {
mAudioEncoder = AUDIO_ENCODER_AMR_NB;
} else {
mAudioEncoder = ae;
}
+ if(mAudioEncoder == AUDIO_ENCODER_AAC) {
+ mSampleRate = mSampleRate ? mSampleRate : 48000;
+ mAudioChannels = mAudioChannels ? mAudioChannels : 2;
+ mAudioBitRate = mAudioBitRate ? mAudioBitRate : 156000;
+ } else if(mAudioEncoder == AUDIO_ENCODER_LPCM) {
+ mSampleRate = mSampleRate ? mSampleRate : 48000;
+ mAudioChannels = mAudioChannels ? mAudioChannels : 2;
+ mAudioBitRate = mAudioBitRate ? mAudioBitRate : 4608000;
+ } else if(mAudioEncoder == AUDIO_ENCODER_AMR_WB) {
+ mSampleRate = 16000;
+ mAudioChannels = 1;
+ mAudioBitRate = mAudioBitRate ? mAudioBitRate : 23850;
+ } else {
+ mSampleRate = mSampleRate ? mSampleRate : 8000;
+ mAudioChannels = mAudioChannels ? mAudioChannels : 1;
+ mAudioBitRate = mAudioBitRate ? mAudioBitRate : 12200;
+ }
return OK;
}
@@ -329,7 +359,7 @@
status_t StagefrightRecorder::setParamAudioNumberOfChannels(int32_t channels) {
ALOGV("setParamAudioNumberOfChannels: %d", channels);
- if (channels <= 0 || channels >= 3) {
+ if (channels != 1 && channels != 2 && channels != 6) {
ALOGE("Invalid number of audio channels: %d", channels);
return BAD_VALUE;
}
@@ -795,6 +825,14 @@
status = startMPEG2TSRecording();
break;
+#ifdef ENABLE_AV_ENHANCEMENTS
+ case OUTPUT_FORMAT_QCP:
+ status = startExtendedRecording( );
+ break;
+#endif
+ case OUTPUT_FORMAT_WAVE:
+ status = startWAVERecording( );
+ break;
default:
ALOGE("Unsupported output file format: %d", mOutputFormat);
status = UNKNOWN_ERROR;
@@ -819,6 +857,52 @@
}
sp<MediaSource> StagefrightRecorder::createAudioSource() {
+ bool tunneledSource = false;
+ const char *tunnelMime;
+ {
+ AudioParameter param;
+ String8 key("tunneled-input-formats");
+ param.add( key, String8("get") );
+ String8 valueStr = AudioSystem::getParameters( 0, param.toString());
+ AudioParameter result(valueStr);
+ int value;
+ if ( mAudioEncoder == AUDIO_ENCODER_AMR_NB &&
+ result.getInt(String8("AMR"),value) == NO_ERROR ) {
+ tunneledSource = true;
+ tunnelMime = MEDIA_MIMETYPE_AUDIO_AMR_NB;
+ }
+#ifdef ENABLE_AV_ENHANCEMENTS
+ else if ( mAudioEncoder == AUDIO_ENCODER_QCELP &&
+ result.getInt(String8("QCELP"),value) == NO_ERROR ) {
+ tunneledSource = true;
+ tunnelMime = MEDIA_MIMETYPE_AUDIO_QCELP;
+ }
+ else if ( mAudioEncoder == AUDIO_ENCODER_EVRC &&
+ result.getInt(String8("EVRC"),value) == NO_ERROR ) {
+ tunneledSource = true;
+ tunnelMime = MEDIA_MIMETYPE_AUDIO_EVRC;
+ }
+#endif
+ else if ( mAudioEncoder == AUDIO_ENCODER_AMR_WB &&
+ result.getInt(String8("AWB"),value) == NO_ERROR ) {
+ tunneledSource = true;
+ tunnelMime = MEDIA_MIMETYPE_AUDIO_AMR_WB;
+ }
+ }
+ if ( tunneledSource ) {
+ ALOGD("tunnel recording");
+ sp<AudioSource> audioSource = NULL;
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyChannelCount, mAudioChannels);
+ meta->setInt32(kKeySampleRate, mSampleRate);
+ meta->setInt32(kKeyBitRate, mAudioBitRate);
+ if (mAudioTimeScale > 0) {
+ meta->setInt32(kKeyTimeScale, mAudioTimeScale);
+ }
+ meta->setCString( kKeyMIMEType, tunnelMime );
+ audioSource = new AudioSource( mAudioSource, meta);
+ return audioSource->initCheck( ) == OK ? audioSource : NULL;
+ }
sp<AudioSource> audioSource =
new AudioSource(
mAudioSource,
@@ -835,6 +919,9 @@
sp<MetaData> encMeta = new MetaData;
const char *mime;
switch (mAudioEncoder) {
+ case AUDIO_ENCODER_LPCM:
+ mime = MEDIA_MIMETYPE_AUDIO_RAW;
+ break;
case AUDIO_ENCODER_AMR_NB:
case AUDIO_ENCODER_DEFAULT:
mime = MEDIA_MIMETYPE_AUDIO_AMR_NB;
@@ -854,6 +941,14 @@
mime = MEDIA_MIMETYPE_AUDIO_AAC;
encMeta->setInt32(kKeyAACProfile, OMX_AUDIO_AACObjectELD);
break;
+#ifdef ENABLE_AV_ENHANCEMENTS
+ case AUDIO_ENCODER_EVRC:
+ mime = MEDIA_MIMETYPE_AUDIO_EVRC;
+ break;
+ case AUDIO_ENCODER_QCELP:
+ mime = MEDIA_MIMETYPE_AUDIO_QCELP;
+ break;
+#endif
default:
ALOGE("Unknown audio encoder: %d", mAudioEncoder);
@@ -875,9 +970,28 @@
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
+#ifdef ENABLE_AV_ENHANCEMENTS
+ sp<MediaSource> audioEncoder;
+ if (ExtendedUtils::UseQCHWAACEncoder(mAudioEncoder,mAudioChannels,mAudioBitRate,mSampleRate)) {
+ ALOGV("use QCOM HW AAC encoder");
+ audioEncoder = OMXCodec::Create(client.interface(), encMeta,
+ true /* createEncoder */, audioSource,"OMX.qcom.audio.encoder.aac",OMXCodec::kHardwareCodecsOnly );
+ } else {
+ audioEncoder = OMXCodec::Create(client.interface(), encMeta,
+ true /* createEncoder */, audioSource);
+ }
+#else
sp<MediaSource> audioEncoder =
OMXCodec::Create(client.interface(), encMeta,
true /* createEncoder */, audioSource);
+#endif
+ if (audioEncoder == NULL) {
+ ALOGD("No encoder is needed, use the AudioSource directly as the MediaSource for LPCM format");
+ audioEncoder = audioSource;
+ }
+ if (mAudioSourceNode != NULL) {
+ mAudioSourceNode.clear();
+ }
mAudioSourceNode = audioSource;
return audioEncoder;
@@ -914,14 +1028,29 @@
mAudioEncoder);
return BAD_VALUE;
}
+ if (mSampleRate != 8000) {
+ ALOGE("Invalid sampling rate %d used for AMRNB recording",
+ mSampleRate);
+ return BAD_VALUE;
+ }
} else { // mOutputFormat must be OUTPUT_FORMAT_AMR_WB
if (mAudioEncoder != AUDIO_ENCODER_AMR_WB) {
ALOGE("Invlaid encoder %d used for AMRWB recording",
mAudioEncoder);
return BAD_VALUE;
}
+ if (mSampleRate != 16000) {
+ ALOGE("Invalid sample rate %d used for AMRWB recording",
+ mSampleRate);
+ return BAD_VALUE;
+ }
}
+ if (mAudioChannels != 1) {
+ ALOGE("Invalid number of audio channels %d used for amr recording",
+ mAudioChannels);
+ return BAD_VALUE;
+ }
mWriter = new AMRWriter(mOutputFd);
status_t status = startRawAudioRecording();
if (status != OK) {
@@ -930,6 +1059,18 @@
}
return status;
}
+status_t StagefrightRecorder::startWAVERecording() {
+ CHECK(mOutputFormat == OUTPUT_FORMAT_WAVE);
+ CHECK(mAudioEncoder == AUDIO_ENCODER_LPCM);
+ CHECK(mAudioSource != AUDIO_SOURCE_CNT);
+ mWriter = new WAVEWriter(mOutputFd);
+ status_t status = startRawAudioRecording();
+ if (status != OK) {
+ mWriter.clear();
+ mWriter = NULL;
+ }
+ return status;
+}
status_t StagefrightRecorder::startRawAudioRecording() {
if (mAudioSource >= AUDIO_SOURCE_CNT) {
@@ -1433,6 +1574,15 @@
if (mVideoTimeScale > 0) {
enc_meta->setInt32(kKeyTimeScale, mVideoTimeScale);
}
+
+ status_t retVal = ExtendedUtils::HFR::reCalculateFileDuration(
+ meta, enc_meta, mMaxFileDurationUs, mFrameRate, mVideoEncoder);
+ if(retVal != OK) {
+ return retVal;
+ }
+
+ ExtendedUtils::ShellProp::setEncoderProfile(mVideoEncoder, mVideoEncoderProfile);
+
if (mVideoEncoderProfile != -1) {
enc_meta->setInt32(kKeyVideoProfile, mVideoEncoderProfile);
}
@@ -1485,6 +1635,7 @@
case AUDIO_ENCODER_AAC:
case AUDIO_ENCODER_HE_AAC:
case AUDIO_ENCODER_AAC_ELD:
+ case AUDIO_ENCODER_LPCM:
break;
default:
@@ -1646,6 +1797,10 @@
::close(mOutputFd);
mOutputFd = -1;
}
+ if (mAudioSourceNode != NULL) {
+ mAudioSourceNode.clear();
+ mAudioSourceNode = NULL;
+ }
if (mStarted) {
mStarted = false;
@@ -1688,9 +1843,9 @@
mVideoHeight = 144;
mFrameRate = -1;
mVideoBitRate = 192000;
- mSampleRate = 8000;
- mAudioChannels = 1;
- mAudioBitRate = 12200;
+ mSampleRate = 0;
+ mAudioChannels = 0;
+ mAudioBitRate = 0;
mInterleaveDurationUs = 0;
mIFramesIntervalSec = 1;
mAudioSourceNode = 0;
@@ -1802,4 +1957,39 @@
::write(fd, result.string(), result.size());
return OK;
}
+#ifdef ENABLE_AV_ENHANCEMENTS
+status_t StagefrightRecorder::startExtendedRecording() {
+ CHECK(mOutputFormat == OUTPUT_FORMAT_QCP);
+ if (mSampleRate != 8000) {
+ ALOGE("Invalid sampling rate %d used for recording",
+ mSampleRate);
+ return BAD_VALUE;
+ }
+ if (mAudioChannels != 1) {
+ ALOGE("Invalid number of audio channels %d used for recording",
+ mAudioChannels);
+ return BAD_VALUE;
+ }
+ if (mAudioSource >= AUDIO_SOURCE_CNT) {
+ ALOGE("Invalid audio source: %d", mAudioSource);
+ return BAD_VALUE;
+ }
+ sp<MediaSource> audioEncoder = createAudioSource();
+ if (audioEncoder == NULL) {
+ ALOGE("AudioEncoder NULL");
+ return UNKNOWN_ERROR;
+ }
+ mWriter = new ExtendedWriter(dup(mOutputFd));
+ mWriter->addSource(audioEncoder);
+ if (mMaxFileDurationUs != 0) {
+ mWriter->setMaxFileDuration(mMaxFileDurationUs);
+ }
+ if (mMaxFileSizeBytes != 0) {
+ mWriter->setMaxFileSize(mMaxFileSizeBytes);
+ }
+ mWriter->setListener(mListener);
+ mWriter->start();
+ return OK;
+}
+#endif
} // namespace android
diff --git a/media/libmediaplayerservice/StagefrightRecorder.h b/media/libmediaplayerservice/StagefrightRecorder.h
index c864207..04724fe 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.h
+++ b/media/libmediaplayerservice/StagefrightRecorder.h
@@ -135,6 +135,7 @@
status_t startMPEG4Recording();
status_t startAMRRecording();
status_t startAACRecording();
+ status_t startWAVERecording();
status_t startRawAudioRecording();
status_t startRTPRecording();
status_t startMPEG2TSRecording();
@@ -190,6 +191,9 @@
StagefrightRecorder(const StagefrightRecorder &);
StagefrightRecorder &operator=(const StagefrightRecorder &);
+#ifdef ENABLE_AV_ENHANCEMENTS
+ status_t startExtendedRecording();
+#endif
};
} // namespace android
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 22f699e..6b27e52 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -25,6 +25,7 @@
#include <media/stagefright/foundation/AMessage.h>
#include <media/stagefright/ACodec.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/ExtendedCodec.h>
namespace android {
@@ -57,6 +58,18 @@
mCSD.push(csd);
}
+ sp<ABuffer> extendedCSD = ExtendedCodec::getRawCodecSpecificData(format);
+ if (extendedCSD != NULL) {
+ ALOGV("pushing extended CSD of size %d", extendedCSD->size());
+ mCSD.push(extendedCSD);
+ }
+
+ sp<ABuffer> aacCSD = ExtendedCodec::getAacCodecSpecificData(format);
+ if (aacCSD != NULL) {
+ ALOGV("pushing AAC CSD of size %d", aacCSD->size());
+ mCSD.push(aacCSD);
+ }
+
if (mNativeWindow != NULL) {
format->setObject("native-window", mNativeWindow);
}
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
old mode 100644
new mode 100755
index f315d04..1732620
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -32,6 +32,7 @@
#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/ExtendedCodec.h>
#include <media/hardware/HardwareAPI.h>
@@ -46,6 +47,7 @@
#endif
#include "include/avc_utils.h"
+#include "include/ExtendedUtils.h"
namespace android {
@@ -378,7 +380,8 @@
mDequeueCounter(0),
mStoreMetaDataInOutputBuffers(false),
mMetaDataBuffersToSubmit(0),
- mRepeatFrameDelayUs(-1ll) {
+ mRepeatFrameDelayUs(-1ll),
+ mInSmoothStreamingMode(false) {
mUninitializedState = new UninitializedState(this);
mLoadedState = new LoadedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
@@ -979,6 +982,10 @@
"audio_decoder.amrnb", "audio_encoder.amrnb" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB,
"audio_decoder.amrwb", "audio_encoder.amrwb" },
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS,
+ "audio_decoder.amrwbplus", "audio_encoder.amrwbplus" },
+#endif
{ MEDIA_MIMETYPE_AUDIO_AAC,
"audio_decoder.aac", "audio_encoder.aac" },
{ MEDIA_MIMETYPE_AUDIO_VORBIS,
@@ -1016,7 +1023,7 @@
}
if (i == kNumMimeToRole) {
- return ERROR_UNSUPPORTED;
+ return ExtendedCodec::setSupportedRole(mOMX, mNode, isEncoder, mime);
}
const char *role =
@@ -1212,6 +1219,10 @@
err = setupVideoDecoder(mime, width, height);
}
}
+ if (err == OK) {
+ const char* componentName = mComponentName.c_str();
+ ExtendedCodec::configureVideoDecoder(msg, mime, mOMX, 0, mNode, componentName);
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
int32_t numChannels, sampleRate;
if (!msg->findInt32("channel-count", &numChannels)
@@ -1294,6 +1305,19 @@
} else {
err = setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
}
+ } else {
+ if (encoder) {
+ int32_t numChannels, sampleRate;
+ if (msg->findInt32("channel-count", &numChannels)
+ && msg->findInt32("sample-rate", &sampleRate)) {
+ setupRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ }
+ }
+ err = ExtendedCodec::setAudioFormat(
+ msg, mime, mOMX, mNode, mIsEncoder);
+ if(err != OK) {
+ return err;
+ }
}
if (err != OK) {
@@ -1772,7 +1796,9 @@
status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
if (err != OK) {
- return err;
+ err = ExtendedCodec::setVideoOutputFormat(mime, &compressionFormat);
+ if (err != OK)
+ return err;
}
err = setVideoPortFormatType(
@@ -1802,6 +1828,9 @@
return err;
}
+ ExtendedCodec::enableSmoothStreaming(
+ mOMX, mNode, &mInSmoothStreamingMode, mComponentName.c_str());
+
return OK;
}
@@ -1895,7 +1924,11 @@
err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
if (err != OK) {
- return err;
+ err = ExtendedCodec::setVideoInputFormat(mime, &compressionFormat);
+ if (err != OK) {
+ ALOGE("Not a supported video mime type: %s", mime);
+ return err;
+ }
}
err = setVideoPortFormatType(
@@ -2559,6 +2592,9 @@
rect.nTop,
rect.nLeft + rect.nWidth,
rect.nTop + rect.nHeight);
+ reply->setInt32(
+ "color-format",
+ (int)(videoDef->eColorFormat));
}
}
break;
@@ -2675,7 +2711,24 @@
}
default:
+ {
+ AString mimeType;
+ status_t err = ExtendedCodec::handleSupportedAudioFormats(
+ audioDef->eEncoding, &mimeType);
+ if (err == OK) {
+ int channelCount;
+ err = ExtendedCodec::getSupportedAudioFormatInfo(
+ &mimeType,
+ mOMX,
+ mNode,
+ kPortIndexOutput,
+ &channelCount);
+ notify->setString("mime", mimeType.c_str());
+ notify->setInt32("channel-count", channelCount);
+ break;
+ }
TRESPASS();
+ }
}
break;
}
@@ -3425,6 +3478,13 @@
android_native_rect_t crop;
if (msg->findRect("crop",
&crop.left, &crop.top, &crop.right, &crop.bottom)) {
+ if (mCodec->mInSmoothStreamingMode) {
+ OMX_COLOR_FORMATTYPE eColorFormat = OMX_COLOR_FormatUnused;
+ CHECK(msg->findInt32("color-format", (int32_t*)&eColorFormat));
+ ExtendedUtils::updateNativeWindowBufferGeometry(
+ mCodec->mNativeWindow.get(), crop.right,
+ crop.bottom, eColorFormat);
+ }
CHECK_EQ(0, native_window_set_crop(
mCodec->mNativeWindow.get(), &crop));
}
@@ -3637,6 +3697,7 @@
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).mName.string();
quirks = matchingCodecs.itemAt(matchIndex).mQuirks;
+ ExtendedCodec::overrideComponentName(quirks, msg, &componentName);
pid_t tid = androidGetTid();
int prevPriority = androidGetThreadPriority(tid);
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 3d5e06d..9373e33 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -1,6 +1,15 @@
LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
+ifeq ($(BOARD_USES_ALSA_AUDIO),true)
+ ifeq ($(USE_TUNNEL_MODE),true)
+ LOCAL_CFLAGS += -DUSE_TUNNEL_MODE
+ endif
+ ifeq ($(NO_TUNNEL_MODE_FOR_MULTICHANNEL),true)
+ LOCAL_CFLAGS += -DNO_TUNNEL_MODE_FOR_MULTICHANNEL
+ endif
+endif
+
include frameworks/av/media/libstagefright/codecs/common/Config.mk
ifeq ($(BOARD_HTC_3D_SUPPORT),true)
@@ -25,6 +34,7 @@
FLACExtractor.cpp \
HTTPBase.cpp \
JPEGSource.cpp \
+ LPAPlayerALSA.cpp \
MP3Extractor.cpp \
MPEG2TSWriter.cpp \
MPEG4Extractor.cpp \
@@ -43,6 +53,7 @@
NuMediaExtractor.cpp \
OMXClient.cpp \
OMXCodec.cpp \
+ ExtendedCodec.cpp \
OggExtractor.cpp \
SampleIterator.cpp \
SampleTable.cpp \
@@ -53,14 +64,18 @@
ThrottledSource.cpp \
TimeSource.cpp \
TimedEventQueue.cpp \
+ TunnelPlayer.cpp \
Utils.cpp \
VBRISeeker.cpp \
WAVExtractor.cpp \
+ WAVEWriter.cpp \
WVMExtractor.cpp \
XINGSeeker.cpp \
avc_utils.cpp \
mp4/FragmentedMP4Parser.cpp \
mp4/TrackFragment.cpp \
+ ExtendedExtractor.cpp \
+ ExtendedUtils.cpp \
LOCAL_C_INCLUDES:= \
$(TOP)/frameworks/av/include/media/stagefright/timedtext \
@@ -111,6 +126,7 @@
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
+ libstagefright_mp3dec \
libstagefright_aacenc \
libstagefright_matroska \
libstagefright_timedtext \
@@ -121,6 +137,13 @@
libFLAC \
libmedia_helper
+ifeq ($(TARGET_ENABLE_QC_AV_ENHANCEMENTS),true)
+ LOCAL_CFLAGS += -DENABLE_AV_ENHANCEMENTS
+ LOCAL_C_INCLUDES += $(TOP)/hardware/qcom/media/mm-core/inc
+ LOCAL_SRC_FILES += ExtendedMediaDefs.cpp
+ LOCAL_SRC_FILES += ExtendedWriter.cpp
+endif #TARGET_ENABLE_AV_ENHANCEMENTS
+
LOCAL_SRC_FILES += \
chromium_http_stub.cpp
LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
@@ -150,6 +173,12 @@
LOCAL_CFLAGS += -DUSE_TI_DUCATI_H264_PROFILE
endif
+ifdef DOLBY_UDC
+ LOCAL_CFLAGS += -DDOLBY_UDC
+endif #DOLBY_UDC
+ifdef DOLBY_UDC_MULTICHANNEL
+ LOCAL_CFLAGS += -DDOLBY_UDC_MULTICHANNEL
+endif #DOLBY_UDC_MULTICHANNEL
LOCAL_MODULE:= libstagefright
LOCAL_MODULE_TAGS := optional
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index 18bebd8..ffa3475 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -36,8 +38,7 @@
AudioPlayer::AudioPlayer(
const sp<MediaPlayerBase::AudioSink> &audioSink,
- uint32_t flags,
- AwesomePlayer *observer)
+ uint32_t flags, AwesomePlayer *observer)
: mInputBuffer(NULL),
mSampleRate(0),
mLatencyUs(0),
@@ -51,6 +52,7 @@
mFinalStatus(OK),
mSeekTimeUs(0),
mStarted(false),
+ mSourcePaused(false),
mIsFirstBuffer(false),
mFirstBufferResult(OK),
mFirstBuffer(NULL),
@@ -79,6 +81,7 @@
status_t err;
if (!sourceAlreadyStarted) {
+ mSourcePaused = false;
err = mSource->start();
if (err != OK) {
@@ -253,7 +256,6 @@
void AudioPlayer::pause(bool playPendingSamples) {
CHECK(mStarted);
-
if (playPendingSamples) {
if (mAudioSink.get() != NULL) {
mAudioSink->stop();
@@ -274,10 +276,19 @@
}
mPlaying = false;
+ CHECK(mSource != NULL);
+ if (mSource->pause() == OK) {
+ mSourcePaused = true;
+ }
}
status_t AudioPlayer::resume() {
CHECK(mStarted);
+ CHECK(mSource != NULL);
+ if (mSourcePaused == true) {
+ mSourcePaused = false;
+ mSource->start();
+ }
status_t err;
if (mAudioSink.get() != NULL) {
@@ -339,7 +350,7 @@
mInputBuffer->release();
mInputBuffer = NULL;
}
-
+ mSourcePaused = false;
mSource->stop();
// The following hack is necessary to ensure that the OMX
@@ -415,6 +426,11 @@
MediaPlayerBase::AudioSink::cb_event_t event) {
AudioPlayer *me = (AudioPlayer *)cookie;
+ if (buffer == NULL) {
+ //Not applicable for AudioPlayer
+ ALOGE("This indicates the event underrun case for LPA/Tunnel");
+ return 0;
+ }
switch(event) {
case MediaPlayerBase::AudioSink::CB_EVENT_FILL_BUFFER:
return me->fillBuffer(buffer, size);
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index 5396c14..4e9699e 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -27,10 +27,14 @@
#include <media/stagefright/foundation/ALooper.h>
#include <cutils/properties.h>
#include <stdlib.h>
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+#include <QCMediaDefs.h>
+#endif
+#include <system/audio.h>
+#define DEFAULT_TUNNEL_BUFFER_COUNT 4
namespace android {
-// Treat time out as an error if we have not received any output
-// buffers after 1 seconds
+
const static int64_t WaitLockEventTimeOutNs = 1000000000LL;
static void AudioRecordCallbackFunction(int event, void *user, void *info) {
AudioSource *source = (AudioSource *) user;
@@ -51,23 +55,31 @@
AudioSource::AudioSource(
audio_source_t inputSource, uint32_t sampleRate, uint32_t channelCount)
- : mStarted(false),
+ : mRecord(NULL),
+ mStarted(false),
mSampleRate(sampleRate),
mPrevSampleTimeUs(0),
mNumFramesReceived(0),
- mNumClientOwnedBuffers(0) {
+ mNumClientOwnedBuffers(0),
+ mFormat(AUDIO_FORMAT_PCM_16_BIT),
+ mMime(MEDIA_MIMETYPE_AUDIO_RAW) {
ALOGV("sampleRate: %d, channelCount: %d", sampleRate, channelCount);
- CHECK(channelCount == 1 || channelCount == 2);
+ CHECK(channelCount == 1 || channelCount == 2 || channelCount == 6);
size_t minFrameCount;
status_t status = AudioRecord::getMinFrameCount(&minFrameCount,
sampleRate,
AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount));
+ if ( NO_ERROR != AudioSystem::getInputBufferSize(
+ sampleRate, mFormat, channelCount, (size_t*)&mMaxBufferSize) ) {
+ mMaxBufferSize = kMaxBufferSize;
+ ALOGV("mMaxBufferSize = %d", mMaxBufferSize);
+ }
if (status == OK) {
// make sure that the AudioRecord callback never returns more than the maximum
// buffer size
- int frameCount = kMaxBufferSize / sizeof(int16_t) / channelCount;
+ int frameCount = mMaxBufferSize / sizeof(int16_t) / channelCount;
// make sure that the AudioRecord total buffer size is large enough
int bufCount = 2;
@@ -78,13 +90,11 @@
mRecord = new AudioRecord(
inputSource, sampleRate, AUDIO_FORMAT_PCM_16_BIT,
audio_channel_in_mask_from_count(channelCount),
- bufCount * frameCount,
+ DEFAULT_TUNNEL_BUFFER_COUNT * mMaxBufferSize / sizeof(int16_t), /* Enable ping-pong buffers */
AudioRecordCallbackFunction,
this,
frameCount);
mInitCheck = mRecord->initCheck();
-
- //configure the auto ramp start duration
mAutoRampStartUs = kAutoRampStartUs;
uint32_t playbackLatencyMs = 0;
if (AudioSystem::getOutputLatency(&playbackLatencyMs,
@@ -99,6 +109,59 @@
}
}
+AudioSource::AudioSource( audio_source_t inputSource, const sp<MetaData>& meta )
+ : mStarted(false),
+ mPrevSampleTimeUs(0),
+ mNumFramesReceived(0),
+ mNumClientOwnedBuffers(0),
+ mFormat(AUDIO_FORMAT_PCM_16_BIT),
+ mMime(MEDIA_MIMETYPE_AUDIO_RAW) {
+ const char * mime;
+ ALOGE("SK: in AudioSource : inputSource: %d", inputSource);
+ CHECK( meta->findCString( kKeyMIMEType, &mime ) );
+ mMime = mime;
+ int32_t sampleRate = 0; //these are the only supported values
+ int32_t channels = 0; //for the below tunnel formats
+ CHECK( meta->findInt32( kKeyChannelCount, &channels ) );
+ CHECK( meta->findInt32( kKeySampleRate, &sampleRate ) );
+ int32_t frameSize = -1;
+ mSampleRate = sampleRate;
+ if ( !strcasecmp( mime, MEDIA_MIMETYPE_AUDIO_AMR_NB ) ) {
+ mFormat = AUDIO_FORMAT_AMR_NB;
+ frameSize = AMR_FRAMESIZE;
+ mMaxBufferSize = AMR_FRAMESIZE*10;
+ }
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+ else if ( !strcasecmp( mime, MEDIA_MIMETYPE_AUDIO_QCELP ) ) {
+ mFormat = AUDIO_FORMAT_QCELP;
+ frameSize = QCELP_FRAMESIZE;
+ mMaxBufferSize = QCELP_FRAMESIZE*10;
+ }
+ else if ( !strcasecmp( mime, MEDIA_MIMETYPE_AUDIO_EVRC ) ) {
+ mFormat = AUDIO_FORMAT_EVRC;
+ frameSize = EVRC_FRAMESIZE;
+ mMaxBufferSize = EVRC_FRAMESIZE*10;
+ }
+#endif
+ else if ( !strcasecmp( mime, MEDIA_MIMETYPE_AUDIO_AMR_WB ) ) {
+ mFormat = AUDIO_FORMAT_AMR_WB;
+ frameSize = AMR_WB_FRAMESIZE;
+ mMaxBufferSize = AMR_WB_FRAMESIZE*10;
+ }
+ else {
+ CHECK(0);
+ }
+ mAutoRampStartUs = 0;
+ CHECK(channels == 1 || channels == 2);
+ mRecord = new AudioRecord(
+ inputSource, sampleRate, mFormat,
+ channels > 1? AUDIO_CHANNEL_IN_STEREO:
+ AUDIO_CHANNEL_IN_MONO,
+ 4*mMaxBufferSize/channels/frameSize,
+ AudioRecordCallbackFunction,
+ this);
+ mInitCheck = mRecord->initCheck();
+}
AudioSource::~AudioSource() {
if (mStarted) {
reset();
@@ -180,10 +243,10 @@
}
sp<MetaData> meta = new MetaData;
- meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
- meta->setInt32(kKeySampleRate, mSampleRate);
+ meta->setCString(kKeyMIMEType, mMime);
+ meta->setInt32(kKeySampleRate, mRecord->getSampleRate());
meta->setInt32(kKeyChannelCount, mRecord->channelCount());
- meta->setInt32(kKeyMaxInputSize, kMaxBufferSize);
+ meta->setInt32(kKeyMaxInputSize, mMaxBufferSize);
return meta;
}
@@ -247,22 +310,24 @@
int64_t timeUs;
CHECK(buffer->meta_data()->findInt64(kKeyTime, &timeUs));
int64_t elapsedTimeUs = timeUs - mStartTimeUs;
- if (elapsedTimeUs < mAutoRampStartUs) {
+ if ( mFormat == AUDIO_FORMAT_PCM_16_BIT ) {
+ if (elapsedTimeUs < mAutoRampStartUs) {
memset((uint8_t *) buffer->data(), 0, buffer->range_length());
- } else if (elapsedTimeUs < mAutoRampStartUs + kAutoRampDurationUs) {
+ } else if (elapsedTimeUs < mAutoRampStartUs + kAutoRampDurationUs) {
int32_t autoRampDurationFrames =
(kAutoRampDurationUs * mSampleRate + 500000LL) / 1000000LL;
int32_t autoRampStartFrames =
- (mAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL;
+ (mAutoRampStartUs * mSampleRate + 500000LL) / 1000000LL;
int32_t nFrames = mNumFramesReceived - autoRampStartFrames;
rampVolume(nFrames, autoRampDurationFrames,
(uint8_t *) buffer->data(), buffer->range_length());
+ }
}
// Track the max recording signal amplitude.
- if (mTrackMaxAmplitude) {
+ if (mTrackMaxAmplitude && ( mFormat == AUDIO_FORMAT_PCM_16_BIT)) {
trackMaxAmplitude(
(int16_t *) buffer->data(), buffer->range_length() >> 1);
}
@@ -319,6 +384,7 @@
}
CHECK_EQ(numLostBytes & 1, 0u);
+ if ( mFormat == AUDIO_FORMAT_PCM_16_BIT )
CHECK_EQ(audioBuffer.size & 1, 0u);
if (numLostBytes > 0) {
// Loss of audio frames should happen rarely; thus the LOGW should
@@ -357,19 +423,30 @@
void AudioSource::queueInputBuffer_l(MediaBuffer *buffer, int64_t timeUs) {
const size_t bufferSize = buffer->range_length();
const size_t frameSize = mRecord->frameSize();
- const int64_t timestampUs =
- mPrevSampleTimeUs +
- ((1000000LL * (bufferSize / frameSize)) +
+ int64_t timestampUs = mPrevSampleTimeUs;
+ int64_t recordDurationUs = 0;
+ if ( mFormat == AUDIO_FORMAT_PCM_16_BIT && mSampleRate){
+ recordDurationUs = ((1000000LL * (bufferSize / (2 * mRecord->channelCount()))) +
(mSampleRate >> 1)) / mSampleRate;
+ } else {
+ recordDurationUs = bufferDurationUs(bufferSize);
+ }
+ timestampUs += recordDurationUs;
if (mNumFramesReceived == 0) {
buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
}
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
+ if (mFormat == AUDIO_FORMAT_PCM_16_BIT) {
buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
+ } else {
+ int64_t wallClockTimeUs = timeUs - mInitialReadTimeUs;
+ int64_t mediaTimeUs = mStartTimeUs + mPrevSampleTimeUs;
+ buffer->meta_data()->setInt64(kKeyDriftTime, mediaTimeUs - wallClockTimeUs);
+ }
mPrevSampleTimeUs = timestampUs;
- mNumFramesReceived += bufferSize / frameSize;
+ mNumFramesReceived += buffer->range_length() / sizeof(int16_t);
mBuffersReceived.push_back(buffer);
mFrameAvailableCondition.signal();
}
@@ -397,4 +474,22 @@
return value;
}
+int64_t AudioSource::bufferDurationUs( ssize_t n ) {
+ int64_t dataDurationMs = 0;
+ if (mFormat == AUDIO_FORMAT_AMR_NB) {
+ dataDurationMs = (n/AMR_FRAMESIZE) * 20; //ms
+ }
+ else if (mFormat == AUDIO_FORMAT_EVRC) {
+ dataDurationMs = (n/EVRC_FRAMESIZE) * 20; //ms
+ }
+ else if (mFormat == AUDIO_FORMAT_QCELP) {
+ dataDurationMs = (n/QCELP_FRAMESIZE) * 20; //ms
+ }
+ else if (mFormat == AUDIO_FORMAT_AMR_WB) {
+ dataDurationMs = (n/AMR_WB_FRAMESIZE) * 20; //ms
+ }
+ else
+ CHECK(0);
+ return dataDurationMs*1000LL;
+}
} // namespace android
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index b817a35..1ea742a 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2009 The Android Open Source Project
* Copyright (c) 2012, The Linux Foundation. All rights reserved.
*
@@ -40,6 +42,10 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/timedtext/TimedTextDriver.h>
#include <media/stagefright/AudioPlayer.h>
+#include <media/stagefright/LPAPlayer.h>
+#ifdef USE_TUNNEL_MODE
+#include <media/stagefright/TunnelPlayer.h>
+#endif
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
#include <media/stagefright/MediaBuffer.h>
@@ -48,6 +54,7 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
+#include "include/ExtendedUtils.h"
#include <media/stagefright/Utils.h>
#include <gui/IGraphicBufferProducer.h>
@@ -59,6 +66,8 @@
#define USE_SURFACE_ALLOC 1
#define FRAME_DROP_FREQ 0
+#define LPA_MIN_DURATION_USEC_ALLOWED 30000000
+#define LPA_MIN_DURATION_USEC_DEFAULT 60000000
namespace android {
@@ -66,6 +75,10 @@
static int64_t kHighWaterMarkUs = 5000000ll; // 5secs
static const size_t kLowWaterMarkBytes = 40000;
static const size_t kHighWaterMarkBytes = 200000;
+static const int64_t kInitFrameDurationUs = 16000;
+static const int64_t kScheduleLagGapUs = 1000;
+static const int64_t kDefaultEventDelayUs = 10000;
+int AwesomePlayer::mTunnelAliveAP = 0;
// maximum time in paused state when offloading audio decompression. When elapsed, the AudioPlayer
// is destroyed to allow the audio DSP to power down.
@@ -203,6 +216,7 @@
mVideoBuffer(NULL),
mDecryptHandle(NULL),
mLastVideoTimeUs(-1),
+ mFrameDurationUs(kInitFrameDurationUs),
mTextDriver(NULL),
mOffloadAudio(false),
mAudioTearDown(false) {
@@ -229,6 +243,8 @@
mAudioTearDownEventPending = false;
reset();
+ mIsTunnelAudio = false;
+ mLateAVSyncMargin = ExtendedUtils::ShellProp::getMaxAVSyncLateMargin();
}
AwesomePlayer::~AwesomePlayer() {
@@ -238,9 +254,57 @@
reset();
+ // Disable Tunnel Mode Audio
+ if (mIsTunnelAudio) {
+ if (mTunnelAliveAP > 0) {
+ mTunnelAliveAP--;
+ ALOGV("mTunnelAliveAP = %d", mTunnelAliveAP);
+ }
+ }
+ mIsTunnelAudio = false;
mClient.disconnect();
}
+void AwesomePlayer::printStats() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("persist.debug.sf.statistics", value, "0");
+ if (atoi(value) && mVideoSource != NULL) {
+ ALOGE("===========================\n"
+ " videoDimensions(%d x %d)\n"
+ " Total Video Frames Decoded(%lld)\n"
+ " Total Video Frames Rendered(%lld)\n"
+ " Total Playback Duration(%lld ms)\n"
+ " numVideoFramesDropped(%lld)\n"
+ " Average Frames Per Second(%.4f)\n"
+ " Last Seek To Time(%lld ms)\n"
+ " Last Paused Time(%lld ms)\n"
+ " First Frame Latency (%lld ms)\n"
+ " Number of times AV Sync Lost(%u)\n"
+ " Max Video Ahead Time Delta(%u)\n"
+ " Max Video Behind Time Delta(%u)\n"
+ " Max Time Sync Loss(%u)\n"
+ " EOS(%d)\n"
+ " PLAYING(%d)\n"
+ "===========================\n\n",
+ mStats.mVideoWidth,
+ mStats.mVideoHeight,
+ mStats.mNumVideoFramesDecoded,
+ mStats.mTotalFrames,
+ mStats.mTotalTimeUs/1000,
+ mStats.mNumVideoFramesDropped,
+ mStats.mTotalTimeUs > 0 ? ((double)(mStats.mTotalFrames)*1E6)/((double)mStats.mTotalTimeUs) : 0,
+ mStats.mLastSeekToTimeMs,
+ mStats.mLastPausedTimeMs,
+ mStats.mFirstFrameLatencyUs/1000,
+ mStats.mNumTimesSyncLoss,
+ -mStats.mMaxEarlyDelta/1000,
+ mStats.mMaxLateDelta/1000,
+ mStats.mMaxTimeSyncLoss/1000,
+ (mFlags & VIDEO_AT_EOS) > 0,
+ (mFlags & PLAYING) > 0);
+ }
+}
+
void AwesomePlayer::cancelPlayerEvents(bool keepNotifications) {
mQueue.cancelEvent(mVideoEvent->eventID());
mVideoEventPending = false;
@@ -317,12 +381,28 @@
return OK;
}
+void AwesomePlayer::printFileName(int fd) {
+
+ char symName[40] = {0};
+ char fileName[256] = {0};
+
+ snprintf(symName, sizeof(symName), "/proc/%d/fd/%d", getpid(), fd);
+
+ if (readlink( symName, fileName, (sizeof(fileName) - 1)) != -1 ) {
+ ALOGD("printFileName fd(%d) -> %s", fd, fileName);
+ }
+}
+
status_t AwesomePlayer::setDataSource(
int fd, int64_t offset, int64_t length) {
Mutex::Autolock autoLock(mLock);
+ ALOGD("Before reset_l");
reset_l();
+ if(fd)
+ printFileName(fd);
+
sp<DataSource> dataSource = new FileSource(fd, offset, length);
status_t err = dataSource->initCheck();
@@ -568,6 +648,8 @@
mVideoRenderer.clear();
+ modifyFlags(PLAYING, CLEAR);
+ printStats();
if (mVideoSource != NULL) {
shutdownVideoDecoder_l();
}
@@ -589,7 +671,7 @@
mBitrate = -1;
mLastVideoTimeUs = -1;
-
+ mFrameDurationUs = kInitFrameDurationUs;
{
Mutex::Autolock autoLock(mStatsLock);
mStats.mFd = -1;
@@ -603,6 +685,21 @@
mStats.mVideoHeight = -1;
mStats.mFlags = 0;
mStats.mTracks.clear();
+ mStats.mConsecutiveFramesDropped = 0;
+ mStats.mCatchupTimeStart = 0;
+ mStats.mNumTimesSyncLoss = 0;
+ mStats.mMaxEarlyDelta = 0;
+ mStats.mMaxLateDelta = 0;
+ mStats.mMaxTimeSyncLoss = 0;
+ mStats.mTotalFrames = 0;
+ mStats.mLastFrameUs = 0;
+ mStats.mTotalTimeUs = 0;
+ mStats.mVeryFirstFrame = true;
+ mStats.mFirstFrameLatencyUs = 0;
+ mStats.mLastPausedTimeMs = 0;
+ mStats.mLastSeekToTimeMs = 0;
+ mStats.mResumeDelayStartUs = -1;
+ mStats.mSeekDelayStartUs = -1;
}
mWatchForAudioSeekComplete = false;
@@ -925,9 +1022,12 @@
// We don't want to post an error notification at this point,
// the error returned from MediaPlayer::start() will suffice.
-
- status_t err = startAudioPlayer_l(
- false /* sendErrorNotification */);
+ bool sendErrorNotification = false;
+ if(mIsTunnelAudio) {
+ // For tunnel Audio error has to be posted to the client
+ sendErrorNotification = true;
+ }
+ status_t err = startAudioPlayer_l(sendErrorNotification);
if ((err != OK) && mOffloadAudio) {
ALOGI("play_l() cannot create offload output, fallback to sw decode");
@@ -979,6 +1079,15 @@
mTimeSource = &mSystemTimeSource;
}
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ if (mStats.mVeryFirstFrame) {
+ mStats.mFirstFrameLatencyStartUs = getTimeOfDayUs();
+ } else {
+ mStats.mResumeDelayStartUs = getTimeOfDayUs();
+ }
+ }
+
if (mVideoSource != NULL) {
// Kick off video playback
postVideoEvent_l();
@@ -1016,7 +1125,11 @@
uint32_t flags = 0;
int64_t cachedDurationUs;
bool eos;
-
+ sp<MetaData> format = mAudioTrack->getFormat();
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ int tunnelObjectsAlive = 0;
+ CHECK(success);
if (mOffloadAudio) {
flags |= AudioPlayer::USE_OFFLOAD;
} else if (mVideoSource == NULL
@@ -1031,8 +1144,85 @@
if (mVideoSource != NULL) {
flags |= AudioPlayer::HAS_VIDEO;
}
+#ifdef USE_TUNNEL_MODE
+ // Create tunnel player if tunnel mode is enabled
+ ALOGW("Trying to create tunnel player mIsTunnelAudio %d, \
+ LPAPlayer::mObjectsAlive %d, \
+ TunnelPlayer::mTunnelObjectsAlive = %d,\
+ (mAudioPlayer == NULL) %d",
+ mIsTunnelAudio, TunnelPlayer::mTunnelObjectsAlive,
+ LPAPlayer::mObjectsAlive,(mAudioPlayer == NULL));
- mAudioPlayer = new AudioPlayer(mAudioSink, flags, this);
+ if(mIsTunnelAudio && (mAudioPlayer == NULL) &&
+ (LPAPlayer::mObjectsAlive == 0) &&
+ (TunnelPlayer::mTunnelObjectsAlive < TunnelPlayer::getTunnelObjectsAliveMax())) {
+ ALOGD("Tunnel player created for mime %s duration %lld\n",\
+ mime, mDurationUs);
+ bool initCheck = false;
+ if(mVideoSource != NULL) {
+ // The parameter true is to inform tunnel player that
+ // clip is audio video
+ ALOGD("Tunnel for video");
+ mAudioPlayer = new TunnelPlayer(mAudioSink, initCheck,
+ this, true);
+ }
+ else {
+ ALOGD("Tunnel for audio");
+ mAudioPlayer = new TunnelPlayer(mAudioSink, initCheck,
+ this);
+ }
+ if(!initCheck) {
+ ALOGE("deleting Tunnel Player - initCheck failed");
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+ }
+ }
+ tunnelObjectsAlive = (TunnelPlayer::mTunnelObjectsAlive);
+#endif
+ int32_t nchannels = 0;
+ if(mAudioTrack != NULL) {
+ sp<MetaData> format = mAudioTrack->getFormat();
+ if(format != NULL) {
+ format->findInt32( kKeyChannelCount, &nchannels );
+ ALOGV("nchannels %d;LPA will be skipped if nchannels is > 2 or nchannels == 0",nchannels);
+ }
+ }
+ char lpaDecode[PROPERTY_VALUE_MAX];
+ uint32_t minDurationForLPA = LPA_MIN_DURATION_USEC_DEFAULT;
+ char minUserDefDuration[PROPERTY_VALUE_MAX];
+ property_get("lpa.decode",lpaDecode,"0");
+ property_get("lpa.min_duration",minUserDefDuration,"LPA_MIN_DURATION_USEC_DEFAULT");
+ minDurationForLPA = atoi(minUserDefDuration);
+ if(minDurationForLPA < LPA_MIN_DURATION_USEC_ALLOWED) {
+ if(mAudioPlayer == NULL) {
+ ALOGE("LPAPlayer::Clip duration setting of less than 30sec not supported, defaulting to 60sec");
+ minDurationForLPA = LPA_MIN_DURATION_USEC_DEFAULT;
+ }
+ }
+ if((strcmp("true",lpaDecode) == 0) && (mAudioPlayer == NULL) &&
+#ifdef USE_TUNNEL_MODE
+ (tunnelObjectsAlive < TunnelPlayer::getTunnelObjectsAliveMax()) &&
+#endif
+ (nchannels && (nchannels <= 2)))
+ {
+ ALOGV("LPAPlayer::getObjectsAlive() %d",LPAPlayer::mObjectsAlive);
+ if ( mDurationUs > minDurationForLPA
+ && (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG) || !strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC))
+ && LPAPlayer::mObjectsAlive == 0 && mVideoSource == NULL) {
+ ALOGD("LPAPlayer created, LPA MODE detected mime %s duration %lld", mime, mDurationUs);
+ bool initCheck = false;
+ mAudioPlayer = new LPAPlayer(mAudioSink, initCheck, this);
+ if(!initCheck) {
+ ALOGE("deleting Tunnel Player - initCheck failed");
+ delete mAudioPlayer;
+ mAudioPlayer = NULL;
+ }
+ }
+ }
+ if(mAudioPlayer == NULL) {
+ ALOGV("AudioPlayer created, Non-LPA mode mime %s duration %lld\n", mime, mDurationUs);
+ mAudioPlayer = new AudioPlayer(mAudioSink, flags, this);
+ }
mAudioPlayer->setSource(mAudioSource);
mTimeSource = mAudioPlayer;
@@ -1073,6 +1263,7 @@
true /* sourceAlreadyStarted */);
if (err != OK) {
+ ALOGE("AudioPlayer start error");
if (sendErrorNotification) {
notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
}
@@ -1264,6 +1455,12 @@
Playback::PAUSE, 0);
}
+ if(!(mFlags & VIDEO_AT_EOS)){
+ Mutex::Autolock autoLock(mStatsLock);
+ mStats.mLastPausedTimeMs = mVideoTimeUs/1000;
+ printStats();
+ }
+
uint32_t params = IMediaPlayerService::kBatteryDataTrackDecoder;
if ((mAudioSource != NULL) && (mAudioSource != mAudioTrack)) {
params |= IMediaPlayerService::kBatteryDataTrackAudio;
@@ -1423,6 +1620,11 @@
}
mSeeking = SEEK;
+
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ mStats.mSeekDelayStartUs = getTimeOfDayUs();
+ }
mSeekNotificationSent = false;
mSeekTimeUs = timeUs;
modifyFlags((AT_EOS | AUDIO_AT_EOS | VIDEO_AT_EOS), CLEAR);
@@ -1499,22 +1701,121 @@
// stream to hardware that doesn't have the necessary codec
mOffloadAudio = canOffloadStream(meta, (mVideoSource != NULL), isStreamingHTTP());
- if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW)) {
- ALOGV("createAudioPlayer: bypass OMX (raw)");
+ int32_t nchannels = 0;
+ int32_t isADTS = 0;
+ meta->findInt32( kKeyChannelCount, &nchannels );
+ meta->findInt32(kKeyIsADTS, &isADTS);
+ if (isADTS == 1) {
+ ALOGV("Widevine content\n");
+ }
+
+ ALOGV("nchannels %d;LPA will be skipped if nchannels is > 2 or nchannels == 0",
+ nchannels);
+
+#ifdef USE_TUNNEL_MODE
+ char tunnelDecode[PROPERTY_VALUE_MAX];
+ property_get("tunnel.decode",tunnelDecode,"0");
+ // Enable tunnel mode for mp3 and aac and if the clip is not aac adif
+ // and if no other tunnel mode instances aare running.
+ ALOGD("Tunnel Mime Type: %s, object alive = %d, mTunnelAliveAP = %d",\
+ mime, (TunnelPlayer::mTunnelObjectsAlive), mTunnelAliveAP);
+
+ bool sys_prop_enabled = !strcmp("true",tunnelDecode) || atoi(tunnelDecode);
+ ALOGD("maxPossible tunnels = %d", TunnelPlayer::getTunnelObjectsAliveMax());
+ //widevine will fallback to software decoder
+ if (sys_prop_enabled && (TunnelPlayer::mTunnelObjectsAlive < TunnelPlayer::getTunnelObjectsAliveMax()) &&
+ (mTunnelAliveAP < TunnelPlayer::getTunnelObjectsAliveMax()) && (isADTS == 0) &&
+ mAudioSink->realtime() &&
+ inSupportedTunnelFormats(mime)) {
+
+ if (mVideoSource != NULL) {
+ char tunnelAVDecode[PROPERTY_VALUE_MAX];
+ property_get("tunnel.audiovideo.decode",tunnelAVDecode,"0");
+ sys_prop_enabled = !strncmp("true", tunnelAVDecode, 4) || atoi(tunnelAVDecode);
+ if (sys_prop_enabled) {
+ ALOGD("Enable Tunnel Mode for A-V playback");
+ mIsTunnelAudio = true;
+ }
+ }
+ else {
+ ALOGI("Tunnel Mode Audio Enabled");
+ mIsTunnelAudio = true;
+ }
+#ifdef NO_TUNNEL_MODE_FOR_MULTICHANNEL
+ if (nchannels > 2 || nchannels <= 0) {
+ ALOGD("Use tunnel mode only for mono and stereo channels");
+ mIsTunnelAudio = false;
+ }
+#endif
+ }
+ else
+ ALOGD("Normal Audio Playback");
+#endif
+
+ checkTunnelExceptions();
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW) ||
+ (mIsTunnelAudio
+#ifdef USE_TUNNEL_MODE
+ && (mTunnelAliveAP < TunnelPlayer::getTunnelObjectsAliveMax())
+#endif
+ )) {
+ ALOGD("Set Audio Track as Audio Source");
+ if(mIsTunnelAudio) {
+ mTunnelAliveAP++;
+ }
mAudioSource = mAudioTrack;
} else {
- // If offloading we still create a OMX decoder as a fall-back
- // but we don't start it
- mOmxSource = OMXCodec::Create(
+ // For LPA Playback use the decoder without OMX layer
+ char *matchComponentName = NULL;
+ int64_t durationUs;
+ uint32_t flags = 0;
+ char lpaDecode[128];
+ uint32_t minDurationForLPA = LPA_MIN_DURATION_USEC_DEFAULT;
+ char minUserDefDuration[PROPERTY_VALUE_MAX];
+ property_get("lpa.decode",lpaDecode,"0");
+ property_get("lpa.min_duration",minUserDefDuration,"LPA_MIN_DURATION_USEC_DEFAULT");
+ minDurationForLPA = atoi(minUserDefDuration);
+ if(minDurationForLPA < LPA_MIN_DURATION_USEC_ALLOWED) {
+ ALOGE("LPAPlayer::Clip duration setting of less than 30sec not supported, defaulting to 60sec");
+ minDurationForLPA = LPA_MIN_DURATION_USEC_DEFAULT;
+ }
+ if (mAudioTrack->getFormat()->findInt64(kKeyDuration, &durationUs)) {
+ Mutex::Autolock autoLock(mMiscStateLock);
+ if (mDurationUs < 0 || durationUs > mDurationUs) {
+ mDurationUs = durationUs;
+ }
+ }
+ if ( mDurationUs > minDurationForLPA
+ && (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG) || !strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC))
+ && LPAPlayer::mObjectsAlive == 0 && mVideoSource == NULL && (strcmp("true",lpaDecode) == 0)
+ && (nchannels && (nchannels <= 2)) ) {
+ char nonOMXDecoder[128];
+ if(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ ALOGD("matchComponentName is set to MP3Decoder %lld, mime %s",mDurationUs,mime);
+ property_get("use.non-omx.mp3.decoder",nonOMXDecoder,"0");
+ if((strcmp("true",nonOMXDecoder) == 0)) {
+ matchComponentName = (char *) "MP3Decoder";
+ }
+ } else if((!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC))) {
+ ALOGD("matchComponentName is set to AACDecoder %lld, mime %s",mDurationUs,mime);
+ property_get("use.non-omx.aac.decoder",nonOMXDecoder,"0");
+ if((strcmp("true",nonOMXDecoder) == 0)) {
+ matchComponentName = (char *) "AACDecoder";
+ } else {
+ matchComponentName = (char *) "OMX.google.aac.decoder";
+ }
+ }
+ flags |= OMXCodec::kSoftwareCodecsOnly;
+ }
+ mAudioSource = OMXCodec::Create(
mClient.interface(), mAudioTrack->getFormat(),
false, // createEncoder
- mAudioTrack);
+ mAudioTrack, matchComponentName, flags,NULL);
if (mOffloadAudio) {
ALOGV("createAudioPlayer: bypass OMX (offload)");
mAudioSource = mAudioTrack;
- } else {
- mAudioSource = mOmxSource;
}
}
@@ -1710,10 +2011,18 @@
mDrmManagerClient->setPlaybackStatus(mDecryptHandle,
Playback::START, videoTimeUs / 1000);
}
+
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ mStats.mLastSeekToTimeMs = mSeekTimeUs/1000;
+ printStats();
+ }
}
void AwesomePlayer::onVideoEvent() {
ATRACE_CALL();
+ int64_t eventStartTimeUs = mSystemTimeSource.getRealTimeUs();
+ int64_t earlyGapUs = kScheduleLagGapUs; // gap for in case of event scheduling lag
Mutex::Autolock autoLock(mLock);
if (!mVideoEventPending) {
// The event has been cancelled in reset_l() but had already
@@ -1722,6 +2031,14 @@
}
mVideoEventPending = false;
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ if(!mStats.mVeryFirstFrame && mSeeking == NO_SEEK){
+ mStats.mTotalTimeUs += getTimeOfDayUs() - mStats.mLastFrameUs;
+ }
+ mStats.mLastFrameUs = getTimeOfDayUs();
+ }
+
if (mSeeking != NO_SEEK) {
if (mVideoBuffer) {
mVideoBuffer->release();
@@ -1815,7 +2132,11 @@
int64_t timeUs;
CHECK(mVideoBuffer->meta_data()->findInt64(kKeyTime, &timeUs));
-
+ if ((mLastVideoTimeUs != timeUs)
+ && (mLastVideoTimeUs > 0)
+ && (mSeeking == NO_SEEK)) {
+ mFrameDurationUs = timeUs - mLastVideoTimeUs;
+ }
mLastVideoTimeUs = timeUs;
if (mSeeking == SEEK_VIDEO_ONLY) {
@@ -1855,18 +2176,27 @@
modifyFlags(FIRST_FRAME, CLEAR);
mSinceLastDropped = 0;
mTimeSourceDeltaUs = ts->getRealTimeUs() - timeUs;
+
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ if(mStats.mVeryFirstFrame){
+ logFirstFrame();
+ printStats();
+ mStats.mLastFrameUs = getTimeOfDayUs();
+ }
+ }
}
- int64_t realTimeUs, mediaTimeUs;
+ int64_t realTimeUs, mediaTimeUs, nowUs = 0, latenessUs = 0;
if (!(mFlags & AUDIO_AT_EOS) && mAudioPlayer != NULL
&& mAudioPlayer->getMediaTimeMapping(&realTimeUs, &mediaTimeUs)) {
mTimeSourceDeltaUs = realTimeUs - mediaTimeUs;
}
if (wasSeeking == SEEK_VIDEO_ONLY) {
- int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
+ nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
- int64_t latenessUs = nowUs - timeUs;
+ latenessUs = nowUs - timeUs;
ATRACE_INT("Video Lateness (ms)", latenessUs / 1E3);
@@ -1878,9 +2208,9 @@
if (wasSeeking == NO_SEEK) {
// Let's display the first frame after seeking right away.
- int64_t nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
+ nowUs = ts->getRealTimeUs() - mTimeSourceDeltaUs;
- int64_t latenessUs = nowUs - timeUs;
+ latenessUs = nowUs - timeUs;
ATRACE_INT("Video Lateness (ms)", latenessUs / 1E3);
@@ -1908,7 +2238,7 @@
}
}
- if (latenessUs > 40000) {
+ if (latenessUs > mLateAVSyncMargin) {
// We're more than 40ms late.
ALOGV("we're late by %lld us (%.2f secs)",
latenessUs, latenessUs / 1E6);
@@ -1927,15 +2257,28 @@
{
Mutex::Autolock autoLock(mStatsLock);
++mStats.mNumVideoFramesDropped;
+ mStats.mConsecutiveFramesDropped++;
+ if (mStats.mConsecutiveFramesDropped == 1){
+ mStats.mCatchupTimeStart = mTimeSource->getRealTimeUs();
+ }
+ if(!(mFlags & AT_EOS)) logLate(timeUs,nowUs,latenessUs);
}
- postVideoEvent_l();
+ int64_t eventDurationUs = mSystemTimeSource.getRealTimeUs() - eventStartTimeUs;
+ int64_t delayUs = mFrameDurationUs - eventDurationUs - latenessUs - earlyGapUs;
+ delayUs = delayUs > kDefaultEventDelayUs ? kDefaultEventDelayUs : delayUs;
+ postVideoEvent_l(delayUs > 0 ? delayUs : 0);
return;
}
}
if (latenessUs < -10000) {
// We're more than 10ms early.
+ logOnTime(timeUs,nowUs,latenessUs);
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ mStats.mConsecutiveFramesDropped = 0;
+ }
postVideoEvent_l(10000);
return;
}
@@ -1959,6 +2302,26 @@
if (mFlags & PLAYING) {
notifyIfMediaStarted_l();
}
+ {
+ Mutex::Autolock autoLock(mStatsLock);
+ logOnTime(timeUs,nowUs,latenessUs);
+ mStats.mTotalFrames++;
+ mStats.mConsecutiveFramesDropped = 0;
+ char value[PROPERTY_VALUE_MAX];
+ property_get("persist.debug.sf.statistics", value, "0");
+ if (atoi(value) && mVideoSource != NULL) {
+ if (mStats.mResumeDelayStartUs > 0) {
+ printStats();
+ ALOGE("Resume Latency = %lld us", getTimeOfDayUs() - mStats.mResumeDelayStartUs);
+ mStats.mResumeDelayStartUs = -1;
+ }
+ if (mStats.mSeekDelayStartUs > 0) {
+ printStats();
+ ALOGE("Seek Latency = %lld us", getTimeOfDayUs() - mStats.mSeekDelayStartUs);
+ mStats.mSeekDelayStartUs = -1;
+ }
+ }
+ }
}
mVideoBuffer->release();
@@ -1968,8 +2331,10 @@
modifyFlags(SEEK_PREVIEW, CLEAR);
return;
}
-
- postVideoEvent_l();
+ int64_t eventDurationUs = mSystemTimeSource.getRealTimeUs() - eventStartTimeUs;
+ int64_t delayUs = mFrameDurationUs - eventDurationUs - latenessUs - earlyGapUs;
+ delayUs = delayUs > kDefaultEventDelayUs ? kDefaultEventDelayUs : delayUs;
+ postVideoEvent_l(delayUs > 0 ? delayUs : 0);
}
void AwesomePlayer::postVideoEvent_l(int64_t delayUs) {
@@ -2803,13 +3168,33 @@
if ((ssize_t)i == mStats.mVideoTrackIndex) {
fprintf(out,
- " videoDimensions(%d x %d), "
- "numVideoFramesDecoded(%lld), "
- "numVideoFramesDropped(%lld)\n",
+ " videoDimensions(%d x %d)\n"
+ " Total Video Frames Decoded(%lld)\n"
+ " Total Video Frames Rendered(%lld)\n"
+ " Total Playback Duration(%lld ms)\n"
+ " numVideoFramesDropped(%lld)\n"
+ " Average Frames Per Second(%.4f)\n"
+ " First Frame Latency (%lld ms)\n"
+ " Number of times AV Sync Lost(%u)\n"
+ " Max Video Ahead Time Delta(%u)\n"
+ " Max Video Behind Time Delta(%u)\n"
+ " Max Time Sync Loss(%u)\n"
+ " EOS(%d)\n"
+ " PLAYING(%d)\n",
mStats.mVideoWidth,
mStats.mVideoHeight,
mStats.mNumVideoFramesDecoded,
- mStats.mNumVideoFramesDropped);
+ mStats.mTotalFrames,
+ mStats.mTotalTimeUs/1000,
+ mStats.mNumVideoFramesDropped,
+ ((double)(mStats.mTotalFrames)*1E6)/((double)mStats.mTotalTimeUs),
+ mStats.mFirstFrameLatencyUs/1000,
+ mStats.mNumTimesSyncLoss,
+ -mStats.mMaxEarlyDelta/1000,
+ mStats.mMaxLateDelta/1000,
+ mStats.mMaxTimeSyncLoss/1000,
+ (mFlags & AT_EOS) > 0,
+ (mFlags & PLAYING) > 0);
}
}
@@ -2891,4 +3276,137 @@
beginPrepareAsync_l();
}
+bool AwesomePlayer::inSupportedTunnelFormats(const char * mime) {
+ const char * tunnelFormats [ ] = {
+ MEDIA_MIMETYPE_AUDIO_MPEG,
+ MEDIA_MIMETYPE_AUDIO_AAC,
+#ifdef TUNNEL_MODE_SUPPORTS_AMRWB
+ MEDIA_MIMETYPE_AUDIO_AMR_WB,
+ #ifdef ENABLE_QC_AV_ENHANCEMENTS
+ MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS
+ #endif
+#endif
+ };
+
+ if (!mime) {
+ return false;
+ }
+
+ size_t len = sizeof(tunnelFormats)/sizeof(const char *);
+ for (size_t i = 0; i < len; i++) {
+ const char * tf = tunnelFormats[i];
+ if (!strncasecmp(mime, tf, strlen(tf))) {
+ if (strlen(mime) == strlen(tf)) { //to prevent a substring match
+ ALOGD("Tunnel playback supported for %s", tf);
+ return true;
+ }
+ }
+ }
+
+ ALOGW("Tunnel playback unsupported for %s", mime);
+ return false;
+}
+
+void AwesomePlayer::checkTunnelExceptions()
+{
+ /* exception 1: No streaming */
+ if (isStreamingHTTP()) {
+ ALOGV("Streaming, force disable tunnel mode playback");
+ mIsTunnelAudio = false;
+ return;
+ }
+
+ CHECK(mAudioTrack != NULL);
+
+ /*
+ * exception 2: No AAC-LD content,
+ * hint given by setting kKeyTunnelException in the track meta
+ */
+ int32_t exception = 0;
+ if (mAudioTrack->getFormat()->findInt32(kKeyTunnelException, &exception) &&
+ exception) {
+ ALOGV("kKeyTunnelException set, disable tunnel mode");
+ mIsTunnelAudio = false;
+ return;
+ }
+
+ /* exception 3: use tunnel player only for AUDIO_STREAM_MUSIC */
+ if (mAudioSink->streamType() != AUDIO_STREAM_MUSIC ) {
+ ALOGV("Use tunnel player only for AUDIO_STREAM_MUSIC");
+ mIsTunnelAudio = false;
+ return;
+ }
+
+ /* below exceptions are only for av content */
+ if (mVideoTrack == NULL) return;
+
+ /* exception 3: No avi having video + mp3 */
+ if (mExtractor == NULL) return;
+
+ sp<MetaData> metaData = mExtractor->getMetaData();
+ const char * container;
+
+ /*only proceed for avi content.*/
+ if (!metaData->findCString(kKeyMIMEType, &container) ||
+ strcmp(container, MEDIA_MIMETYPE_CONTAINER_AVI)) {
+ return;
+ }
+
+ const char * mime;
+ metaData = mAudioTrack->getFormat();
+ /*disable for av content having mp3*/
+ if (metaData->findCString(kKeyMIMEType, &mime) &&
+ !strcmp(mime, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ ALOGV("Clip has AVI extractor and mp3 content, disable tunnel mode");
+ mIsTunnelAudio = false;
+ return;
+ }
+
+ return;
+}
+
+inline void AwesomePlayer::logFirstFrame() {
+ mStats.mFirstFrameLatencyUs = getTimeOfDayUs()-mStats.mFirstFrameLatencyStartUs;
+ mStats.mVeryFirstFrame = false;
+}
+
+inline void AwesomePlayer::logCatchUp(int64_t ts, int64_t clock, int64_t delta)
+{
+ if (mStats.mConsecutiveFramesDropped > 0) {
+ mStats.mNumTimesSyncLoss++;
+ if (mStats.mMaxTimeSyncLoss < (clock - mStats.mCatchupTimeStart) && clock > 0 && ts > 0) {
+ mStats.mMaxTimeSyncLoss = clock - mStats.mCatchupTimeStart;
+ }
+ }
+}
+
+inline void AwesomePlayer::logLate(int64_t ts, int64_t clock, int64_t delta)
+{
+ if (mStats.mMaxLateDelta < delta && clock > 0 && ts > 0) {
+ mStats.mMaxLateDelta = delta;
+ }
+}
+
+inline void AwesomePlayer::logOnTime(int64_t ts, int64_t clock, int64_t delta)
+{
+ bool needLogLate = false;
+ logCatchUp(ts, clock, delta);
+ if (delta <= 0) {
+ if ((-delta) > (-mStats.mMaxEarlyDelta) && clock > 0 && ts > 0) {
+ mStats.mMaxEarlyDelta = delta;
+ }
+ }
+ else {
+ needLogLate = true;
+ }
+
+ if(needLogLate) logLate(ts, clock, delta);
+}
+
+inline int64_t AwesomePlayer::getTimeOfDayUs() {
+ struct timeval tv;
+ gettimeofday(&tv, NULL);
+
+ return (int64_t)tv.tv_sec * 1000000 + tv.tv_usec;
+}
} // namespace android
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index f139637..2067420 100644
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -30,6 +30,7 @@
#include <gui/Surface.h>
#include <utils/String8.h>
#include <cutils/properties.h>
+#include "include/ExtendedUtils.h"
#ifdef USE_TI_CUSTOM_DOMX
#include <OMX_TI_IVCommon.h>
@@ -580,6 +581,9 @@
mMeta->setInt32(kKeyStride, mVideoSize.width);
mMeta->setInt32(kKeySliceHeight, mVideoSize.height);
mMeta->setInt32(kKeyFrameRate, mVideoFrameRate);
+
+ ExtendedUtils::HFR::setHFRIfEnabled(params, mMeta);
+
return OK;
}
diff --git a/media/libstagefright/DataSource.cpp b/media/libstagefright/DataSource.cpp
old mode 100644
new mode 100755
index fc6fd9c..ed5b67e
--- a/media/libstagefright/DataSource.cpp
+++ b/media/libstagefright/DataSource.cpp
@@ -32,6 +32,7 @@
#include "include/OggExtractor.h"
#include "include/WAVExtractor.h"
#include "include/WVMExtractor.h"
+#include "include/ExtendedExtractor.h"
#include "matroska/MatroskaExtractor.h"
@@ -159,6 +160,7 @@
RegisterSniffer(SniffAAC);
RegisterSniffer(SniffMPEG2PS);
RegisterSniffer(SniffWVM);
+ RegisterSniffer(ExtendedExtractor::Sniff);
char value[PROPERTY_VALUE_MAX];
if (property_get("drm.service.enabled", value, NULL)
diff --git a/media/libstagefright/ExtendedCodec.cpp b/media/libstagefright/ExtendedCodec.cpp
new file mode 100644
index 0000000..ab2d839
--- /dev/null
+++ b/media/libstagefright/ExtendedCodec.cpp
@@ -0,0 +1,1078 @@
+/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ExtendedCodec"
+#include <utils/Log.h>
+#include <cutils/properties.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaCodecList.h>
+
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/ExtendedCodec.h>
+#include <media/stagefright/OMXCodec.h>
+
+#ifdef ENABLE_AV_ENHANCEMENTS
+
+#include <QCMetaData.h>
+#include <QCMediaDefs.h>
+#include <OMX_QCOMExtns.h>
+#include <OMX_Component.h>
+#include <QOMX_AudioExtensions.h>
+#include "include/ExtendedUtils.h"
+
+namespace android {
+enum MetaKeyType{
+ INT32, INT64, STRING, DATA, CSD
+};
+
+struct MetaKeyEntry{
+ int MetaKey;
+ const char* MsgKey;
+ MetaKeyType KeyType;
+};
+
+static const MetaKeyEntry MetaKeyTable[] {
+ {kKeyBitRate , "bitrate" , INT32},
+ {kKeyAacCodecSpecificData , "aac-codec-specific-data", CSD},
+ {kKeyRawCodecSpecificData , "raw-codec-specific-data", CSD},
+ {kKeyDivXVersion , "divx-version" , INT32}, // int32_t
+ {kKeyDivXDrm , "divx-drm" , DATA}, // void *
+ {kKeyWMAEncodeOpt , "wma-encode-opt" , INT32}, // int32_t
+ {kKeyWMABlockAlign , "wma-block-align" , INT32}, // int32_t
+ {kKeyWMAVersion , "wma-version" , INT32}, // int32_t
+ {kKeyWMAAdvEncOpt1 , "wma-adv-enc-opt1" , INT32}, // int16_t
+ {kKeyWMAAdvEncOpt2 , "wma-adv-enc-opt2" , INT32}, // int32_t
+ {kKeyWMAFormatTag , "wma-format-tag" , INT32}, // int32_t
+ {kKeyWMABitspersample , "wma-bits-per-sample" , INT32}, // int32_t
+ {kKeyWMAVirPktSize , "wma-vir-pkt-size" , INT32}, // int32_t
+ {kKeyWMAChannelMask , "wma-channel-mask" , INT32}, // int32_t
+
+ {kKeyFileFormat , "file-format" , STRING}, // cstring
+
+ {kkeyAacFormatAdif , "aac-format-adif" , INT32}, // bool (int32_t)
+ {kkeyAacFormatLtp , "aac-format-ltp" , INT32},
+
+ //DTS subtype
+ {kKeyDTSSubtype , "dts-subtype" , INT32}, //int32_t
+
+ //Extractor sets this
+ {kKeyUseArbitraryMode , "use-arbitrary-mode" , INT32}, //bool (int32_t)
+ {kKeySmoothStreaming , "smooth-streaming" , INT32}, //bool (int32_t)
+ {kKeyHFR , "hfr" , INT32}, // int32_t
+
+ {kKeySampleRate , "sample-rate" , INT32},
+ {kKeyChannelCount , "channel-count" , INT32},
+};
+
+const char* ExtendedCodec::getMsgKey(int key) {
+ static const size_t numMetaKeys =
+ sizeof(MetaKeyTable) / sizeof(MetaKeyTable[0]);
+ size_t i;
+ for (i = 0; i < numMetaKeys; ++i) {
+ if (key == MetaKeyTable[i].MetaKey) {
+ return MetaKeyTable[i].MsgKey;
+ }
+ }
+ return "unknown";
+}
+
+status_t ExtendedCodec::convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format) {
+ const char * str_val;
+ int32_t int32_val;
+ int64_t int64_val;
+ uint32_t data_type;
+ const void * data;
+ size_t size;
+ static const size_t numMetaKeys =
+ sizeof(MetaKeyTable) / sizeof(MetaKeyTable[0]);
+ size_t i;
+ for (i = 0; i < numMetaKeys; ++i) {
+ if (MetaKeyTable[i].KeyType == INT32 &&
+ meta->findInt32(MetaKeyTable[i].MetaKey, &int32_val)) {
+ ALOGV("found metakey %s of type int32", MetaKeyTable[i].MsgKey);
+ format->get()->setInt32(MetaKeyTable[i].MsgKey, int32_val);
+ }
+ else if (MetaKeyTable[i].KeyType == INT64 &&
+ meta->findInt64(MetaKeyTable[i].MetaKey, &int64_val)) {
+ ALOGV("found metakey %s of type int64", MetaKeyTable[i].MsgKey);
+ format->get()->setInt64(MetaKeyTable[i].MsgKey, int64_val);
+ }
+ else if (MetaKeyTable[i].KeyType == STRING &&
+ meta->findCString(MetaKeyTable[i].MetaKey, &str_val)) {
+ ALOGV("found metakey %s of type string", MetaKeyTable[i].MsgKey);
+ format->get()->setString(MetaKeyTable[i].MsgKey, str_val);
+ }
+ else if ( (MetaKeyTable[i].KeyType == DATA ||
+ MetaKeyTable[i].KeyType == CSD) &&
+ meta->findData(MetaKeyTable[i].MetaKey, &data_type, &data, &size)) {
+ ALOGV("found metakey %s of type data", MetaKeyTable[i].MsgKey);
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+ if (MetaKeyTable[i].KeyType == CSD) {
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ }
+ format->get()->setBuffer(MetaKeyTable[i].MsgKey, buffer);
+ }
+ }
+ return OK;
+}
+
+uint32_t ExtendedCodec::getComponentQuirks(
+ const MediaCodecList *list, size_t index) {
+ uint32_t quirks = 0;
+
+ if (list->codecHasQuirk(
+ index, "requires-wma-pro-component")) {
+ quirks |= kRequiresWMAProComponent;
+ }
+ return quirks;
+}
+
+const char* ExtendedCodec::overrideComponentName(
+ uint32_t quirks, const sp<MetaData> &meta) {
+ const char* componentName = NULL;
+ if(quirks & kRequiresWMAProComponent)
+ {
+ int32_t version = 0;
+ if (meta->findInt32(kKeyWMAVersion, &version)) {
+ if(version==kTypeWMA) {
+ componentName = "OMX.qcom.audio.decoder.wma";
+ } else if(version==kTypeWMAPro) {
+ componentName = "OMX.qcom.audio.decoder.wma10Pro";
+ } else if(version==kTypeWMALossLess) {
+ componentName = "OMX.qcom.audio.decoder.wmaLossLess";
+ }
+ }
+ }
+ return componentName;
+}
+
+void ExtendedCodec::overrideComponentName(
+ uint32_t quirks, const sp<AMessage> &msg, AString* componentName) {
+ if(quirks & kRequiresWMAProComponent)
+ {
+ int32_t version = 0;
+ if (msg->findInt32(getMsgKey(kKeyWMAVersion), &version)) {
+ if(version==kTypeWMA) {
+ componentName->setTo("OMX.qcom.audio.decoder.wma");
+ } else if(version==kTypeWMAPro) {
+ componentName->setTo("OMX.qcom.audio.decoder.wma10Pro");
+ } else if(version==kTypeWMALossLess) {
+ componentName->setTo("OMX.qcom.audio.decoder.wmaLossLess");
+ }
+ }
+ }
+}
+
+template<class T>
+static void InitOMXParams(T *params) {
+ params->nSize = sizeof(T);
+ params->nVersion.s.nVersionMajor = 1;
+ params->nVersion.s.nVersionMinor = 0;
+ params->nVersion.s.nRevision = 0;
+ params->nVersion.s.nStep = 0;
+}
+
+status_t ExtendedCodec::setDIVXFormat(
+ const sp<AMessage> &msg, const char* mime, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, int port_index) {
+ status_t err = OK;
+
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX4, mime) ||
+ !strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX311, mime)) {
+ ALOGV("Setting the QOMX_VIDEO_PARAM_DIVXTYPE params ");
+ QOMX_VIDEO_PARAM_DIVXTYPE paramDivX;
+ InitOMXParams(¶mDivX);
+ paramDivX.nPortIndex = port_index;
+ int32_t DivxVersion = 0;
+ CHECK(msg->findInt32(getMsgKey(kKeyDivXVersion),&DivxVersion));
+ ALOGV("Divx Version Type %d\n",DivxVersion);
+
+ if(DivxVersion == kTypeDivXVer_4) {
+ paramDivX.eFormat = QOMX_VIDEO_DIVXFormat4;
+ } else if(DivxVersion == kTypeDivXVer_5) {
+ paramDivX.eFormat = QOMX_VIDEO_DIVXFormat5;
+ } else if(DivxVersion == kTypeDivXVer_6) {
+ paramDivX.eFormat = QOMX_VIDEO_DIVXFormat6;
+ } else if(DivxVersion == kTypeDivXVer_3_11 ) {
+ paramDivX.eFormat = QOMX_VIDEO_DIVXFormat311;
+ } else {
+ paramDivX.eFormat = QOMX_VIDEO_DIVXFormatUnused;
+ }
+ paramDivX.eProfile = (QOMX_VIDEO_DIVXPROFILETYPE)0; //Not used for now.
+
+ err = OMXhandle->setParameter(nodeID,
+ (OMX_INDEXTYPE)OMX_QcomIndexParamVideoDivx,
+ ¶mDivX, sizeof(paramDivX));
+ }
+
+ return err;
+}
+
+void ExtendedCodec::getRawCodecSpecificData(
+ const sp<MetaData> &meta, const void* &data, size_t &size) {
+ uint32_t type = 0;
+ size = 0;
+ if (meta->findData(kKeyRawCodecSpecificData, &type, &data, &size)) {
+ ALOGV("OMXCodec::configureCodec found kKeyRawCodecSpecificData of size %d\n", size);
+ }
+}
+
+sp<ABuffer> ExtendedCodec::getRawCodecSpecificData(
+ const sp<AMessage> &msg) {
+ sp<ABuffer> buffer;
+ if (msg->findBuffer(getMsgKey(kKeyRawCodecSpecificData), &buffer)) {
+ ALOGV("ACodec found kKeyRawCodecSpecificData of size %d\n", buffer->size());
+ return buffer;
+ }
+ return NULL;
+}
+
+void ExtendedCodec::getAacCodecSpecificData(
+ const sp<MetaData> &meta, const void* &data, size_t &size) {
+ uint32_t type = 0;
+ size = 0;
+ if (meta->findData(kKeyAacCodecSpecificData, &type, &data, &size)) {
+ ALOGV("OMXCodec::configureCodec found kKeyAacCodecSpecificData of size %d\n", size);
+ }
+}
+
+sp<ABuffer> ExtendedCodec::getAacCodecSpecificData(
+ const sp<AMessage> &msg) {
+ sp<ABuffer> buffer;
+ if (msg->findBuffer(getMsgKey(kKeyAacCodecSpecificData), &buffer)) {
+ ALOGV("ACodec found kKeyAacCodecSpecificData of size %d\n", buffer->size());
+ return buffer;
+ }
+ return NULL;
+}
+
+status_t ExtendedCodec::setAudioFormat(
+ const sp<MetaData> &meta, const char* mime, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder ) {
+ sp<AMessage> msg = new AMessage();
+ msg->clear();
+ convertMetaDataToMessage(meta, &msg);
+ return setAudioFormat(msg, mime, OMXhandle, nodeID, isEncoder);
+}
+
+status_t ExtendedCodec::setAudioFormat(
+ const sp<AMessage> &msg, const char* mime, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder ) {
+ ALOGV("setAudioFormat called");
+ status_t err = OK;
+
+ if ((!strcasecmp(MEDIA_MIMETYPE_AUDIO_AC3, mime)) ||
+ (!strcasecmp(MEDIA_MIMETYPE_AUDIO_EAC3, mime))){
+ int32_t numChannels, sampleRate;
+ /* Commenting follwoing call as AC3 soft decoder does not
+ need it and it causes issue with playback*/
+ //setAC3Format(numChannels, sampleRate, OMXhandle, nodeID);
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+ setAC3Format(numChannels, sampleRate, OMXhandle, nodeID);
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_EVRC, mime)) {
+ int32_t numChannels, sampleRate;
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+ setEVRCFormat(numChannels, sampleRate, OMXhandle, nodeID, isEncoder );
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_QCELP, mime)) {
+ int32_t numChannels, sampleRate;
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+ setQCELPFormat(numChannels, sampleRate, OMXhandle, nodeID, isEncoder);
+ } else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_WMA, mime)) {
+ err = setWMAFormat(msg, OMXhandle, nodeID, isEncoder);
+ }
+ return err;
+}
+
+status_t ExtendedCodec::setVideoInputFormat(
+ const char *mime, OMX_VIDEO_CODINGTYPE *compressionFormat) {
+ status_t retVal = OK;
+ if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime)){
+ *compressionFormat= (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX4, mime)){
+ *compressionFormat= (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX311, mime)){
+ *compressionFormat= (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_WMV, mime)){
+ *compressionFormat = OMX_VIDEO_CodingWMV;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_CONTAINER_MPEG2, mime)){
+ *compressionFormat = OMX_VIDEO_CodingMPEG2;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)){
+ *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingHevc;
+ } else {
+ retVal = BAD_VALUE;
+ }
+
+ return retVal;
+}
+
+status_t ExtendedCodec::setVideoOutputFormat(
+ const char *mime, OMX_VIDEO_CODINGTYPE *compressionFormat) {
+ status_t retVal = OK;
+ if(!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX, mime)) {
+ *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
+ } else if(!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX311, mime)) {
+ *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
+ } else if(!strcasecmp(MEDIA_MIMETYPE_VIDEO_DIVX4, mime)) {
+ *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingDivx;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_WMV, mime)){
+ *compressionFormat = OMX_VIDEO_CodingWMV;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_HEVC, mime)){
+ *compressionFormat = (OMX_VIDEO_CODINGTYPE)QOMX_VIDEO_CodingHevc;
+ } else {
+ retVal = BAD_VALUE;
+ }
+ return retVal;
+}
+
+status_t ExtendedCodec::setSupportedRole(
+ const sp<IOMX> &omx, IOMX::node_id node,
+ bool isEncoder, const char *mime){
+ ALOGV("setSupportedRole Called %s", mime);
+ struct MimeToRole {
+ const char *mime;
+ const char *decoderRole;
+ const char *encoderRole;
+ };
+
+ static const MimeToRole kQCMimeToRole[] = {
+ { MEDIA_MIMETYPE_AUDIO_EVRC,
+ "audio_decoder.evrchw", "audio_encoder.evrc" },
+ { MEDIA_MIMETYPE_AUDIO_QCELP,
+ "audio_decoder,qcelp13Hw", "audio_encoder.qcelp13" },
+ { MEDIA_MIMETYPE_VIDEO_DIVX,
+ "video_decoder.divx", NULL },
+ { MEDIA_MIMETYPE_VIDEO_DIVX4,
+ "video_decoder.divx", NULL },
+ { MEDIA_MIMETYPE_VIDEO_DIVX311,
+ "video_decoder.divx", NULL },
+ { MEDIA_MIMETYPE_VIDEO_WMV,
+ "video_decoder.vc1", NULL },
+ { MEDIA_MIMETYPE_AUDIO_AC3,
+ "audio_decoder.ac3", NULL },
+ { MEDIA_MIMETYPE_AUDIO_WMA,
+ "audio_decoder.wma" , NULL },
+ };
+
+ static const size_t kNumMimeToRole =
+ sizeof(kQCMimeToRole) / sizeof(kQCMimeToRole[0]);
+
+ size_t i;
+ for (i = 0; i < kNumMimeToRole; ++i) {
+ if (!strcasecmp(mime, kQCMimeToRole[i].mime)) {
+ break;
+ }
+ }
+
+ if (i == kNumMimeToRole) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ const char *role =
+ isEncoder ? kQCMimeToRole[i].encoderRole
+ : kQCMimeToRole[i].decoderRole;
+
+ if (role != NULL) {
+ OMX_PARAM_COMPONENTROLETYPE roleParams;
+ InitOMXParams(&roleParams);
+
+ strncpy((char *)roleParams.cRole,
+ role, OMX_MAX_STRINGNAME_SIZE - 1);
+
+ roleParams.cRole[OMX_MAX_STRINGNAME_SIZE - 1] = '\0';
+
+ status_t err = omx->setParameter(
+ node, OMX_IndexParamStandardComponentRole,
+ &roleParams, sizeof(roleParams));
+
+ if (err != OK) {
+ ALOGW("Failed to set standard component role '%s'.", role);
+ return err;
+ }
+ }
+ return OK;
+}
+
+status_t ExtendedCodec::getSupportedAudioFormatInfo(
+ const AString* mime,
+ sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID,
+ int portIndex,
+ int* channelCount) {
+ status_t retVal = OK;
+ if (!strcmp(mime->c_str(),MEDIA_MIMETYPE_AUDIO_QCELP)) {
+ OMX_AUDIO_PARAM_QCELP13TYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = portIndex;
+ CHECK_EQ(OMXhandle->getParameter(
+ nodeID, OMX_IndexParamAudioQcelp13, ¶ms, sizeof(params)), (status_t)OK);
+ *channelCount = params.nChannels;
+ } else if(!strcmp(mime->c_str(), MEDIA_MIMETYPE_AUDIO_EVRC)) {
+ OMX_AUDIO_PARAM_EVRCTYPE params;
+ InitOMXParams(¶ms);
+ params.nPortIndex = portIndex;
+ CHECK_EQ(OMXhandle->getParameter(
+ nodeID, OMX_IndexParamAudioEvrc, ¶ms, sizeof(params)), (status_t)OK);
+ *channelCount = params.nChannels;
+ } else {
+ retVal = BAD_VALUE;
+ }
+ return retVal;
+}
+
+status_t ExtendedCodec::handleSupportedAudioFormats(int format, AString* mime) {
+ ALOGV("checkQCFormats called");
+ status_t retVal = OK;
+ if (format == OMX_AUDIO_CodingQCELP13 ) {
+ *mime = MEDIA_MIMETYPE_AUDIO_QCELP;
+ } else if(format == OMX_AUDIO_CodingEVRC ) {
+ *mime = MEDIA_MIMETYPE_AUDIO_EVRC;
+ } else {
+ retVal = BAD_VALUE;
+ }
+ return retVal;
+}
+
+void ExtendedCodec::configureFramePackingFormat(
+ const sp<AMessage> &msg, sp<IOMX> OMXhandle, IOMX::node_id nodeID,
+ const char* componentName) {
+ //ignore non QC components
+ if (strncmp(componentName, "OMX.qcom.", 9)) {
+ return;
+ }
+
+ int32_t arbitraryMode = 0;
+ bool success = msg->findInt32(getMsgKey(kKeyUseArbitraryMode), &arbitraryMode);
+ bool useFrameByFrameMode = true; //default option
+ if (success && arbitraryMode) {
+ useFrameByFrameMode = false;
+ }
+
+ if (useFrameByFrameMode) {
+ ALOGI("Enable frame by frame mode");
+ OMX_QCOM_PARAM_PORTDEFINITIONTYPE portFmt;
+ portFmt.nPortIndex = kPortIndexInput;
+ portFmt.nFramePackingFormat = OMX_QCOM_FramePacking_OnlyOneCompleteFrame;
+ status_t err = OMXhandle->setParameter(
+ nodeID, (OMX_INDEXTYPE)OMX_QcomIndexPortDefn, (void *)&portFmt, sizeof(portFmt));
+ if(err != OK) {
+ ALOGW("Failed to set frame packing format on component");
+ }
+ } else {
+ ALOGI("Decoder should be in arbitrary mode");
+ }
+}
+
+void ExtendedCodec::configureFramePackingFormat(
+ const sp<MetaData> &meta, sp<IOMX> OMXhandle, IOMX::node_id nodeID,
+ const char* componentName) {
+ sp<AMessage> msg = new AMessage();
+ msg->clear();
+ convertMetaDataToMessage(meta, &msg);
+ configureFramePackingFormat(msg, OMXhandle, nodeID, componentName);
+}
+
+void ExtendedCodec::configureVideoDecoder(
+ const sp<AMessage> &msg, const char* mime, sp<IOMX> OMXhandle,
+ const uint32_t flags, IOMX::node_id nodeID, const char* componentName ) {
+ if (strncmp(componentName, "OMX.qcom.", 9)) {
+ //do nothing for non QC component
+ return;
+ }
+
+ setDIVXFormat(msg, mime, OMXhandle, nodeID, kPortIndexOutput);
+ AString fileFormat;
+ const char *fileFormatCStr = NULL;
+ bool success = msg->findString(getMsgKey(kKeyFileFormat), &fileFormat);
+ if (success) {
+ fileFormatCStr = fileFormat.c_str();
+ }
+
+ // Enable timestamp reordering for AVI file type, mpeg4 and vc1 codec types
+ if (!strcmp(componentName, "OMX.qcom.video.decoder.vc1") ||
+ !strcmp(componentName, "OMX.qcom.video.decoder.mpeg4") ||
+ (fileFormatCStr!= NULL && !strncmp(fileFormatCStr, "video/avi", 9))) {
+ ALOGI("Enabling timestamp reordering");
+ QOMX_INDEXTIMESTAMPREORDER reorder;
+ InitOMXParams(&reorder);
+ reorder.nPortIndex = kPortIndexOutput;
+ reorder.bEnable = OMX_TRUE;
+ status_t err = OMXhandle->setParameter(nodeID,
+ (OMX_INDEXTYPE)OMX_QcomIndexParamEnableTimeStampReorder,
+ (void *)&reorder, sizeof(reorder));
+
+ if(err != OK) {
+ ALOGW("Failed to enable timestamp reordering");
+ }
+ }
+
+ // Enable Sync-frame decode mode for thumbnails
+ if (flags & OMXCodec::kClientNeedsFramebuffer) {
+ ALOGV("Enabling thumbnail mode.");
+ QOMX_ENABLETYPE enableType;
+ OMX_INDEXTYPE indexType;
+
+ status_t err = OMXhandle->getExtensionIndex(
+ nodeID, OMX_QCOM_INDEX_PARAM_VIDEO_SYNCFRAMEDECODINGMODE,
+ &indexType);
+ if(err != OK) {
+ ALOGW("Failed to get extension for SYNCFRAMEDECODINGMODE");
+ return;
+ }
+
+ enableType.bEnable = OMX_TRUE;
+ err = OMXhandle->setParameter(nodeID,indexType,
+ (void *)&enableType, sizeof(enableType));
+ if(err != OK) {
+ ALOGW("Failed to get extension for SYNCFRAMEDECODINGMODE");
+ return;
+ }
+ ALOGI("Thumbnail mode enabled.");
+ }
+
+}
+
+void ExtendedCodec::configureVideoDecoder(
+ const sp<MetaData> &meta, const char* mime, sp<IOMX> OMXhandle,
+ const uint32_t flags, IOMX::node_id nodeID, const char* componentName ) {
+ sp<AMessage> msg = new AMessage();
+ msg->clear();
+ convertMetaDataToMessage(meta, &msg);
+ configureVideoDecoder(msg, mime, OMXhandle, flags, nodeID, componentName);
+}
+
+void ExtendedCodec::enableSmoothStreaming(
+ const sp<IOMX> &omx, IOMX::node_id nodeID, bool* isEnabled,
+ const char* componentName) {
+ *isEnabled = false;
+
+ if (!ExtendedUtils::ShellProp::isSmoothStreamingEnabled()) {
+ return;
+ }
+
+ //ignore non QC components
+ if (strncmp(componentName, "OMX.qcom.", 9)) {
+ return;
+ }
+ status_t err = omx->setParameter(
+ nodeID,
+ (OMX_INDEXTYPE)OMX_QcomIndexParamEnableSmoothStreaming,
+ &err, sizeof(status_t));
+ if (err != OK) {
+ ALOGE("Failed to enable Smoothstreaming!");
+ return;
+ }
+ *isEnabled = true;
+ ALOGI("Smoothstreaming Enabled");
+ return;
+}
+
+//private methods
+void ExtendedCodec::setEVRCFormat(
+ int32_t numChannels, int32_t sampleRate, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder ) {
+ ALOGV("setEVRCFormat called");
+
+ if (isEncoder) {
+ CHECK(numChannels == 1);
+ //////////////// input port ////////////////////
+ //handle->setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ //////////////// output port ////////////////////
+ // format
+ OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ format.nPortIndex = kPortIndexOutput;
+ format.nIndex = 0;
+ status_t err = OMX_ErrorNone;
+ while (OMX_ErrorNone == err) {
+ CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format)), (status_t)OK);
+ if (format.eEncoding == OMX_AUDIO_CodingEVRC) {
+ break;
+ }
+ format.nIndex++;
+ }
+ CHECK_EQ((status_t)OK, err);
+ CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format)), (status_t)OK);
+
+ // port definition
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+ def.format.audio.cMIMEType = NULL;
+ CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), (status_t)OK);
+ def.format.audio.bFlagErrorConcealment = OMX_TRUE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingEVRC;
+ CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), (status_t)OK);
+
+ // profile
+ OMX_AUDIO_PARAM_EVRCTYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexOutput;
+ CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioEvrc,
+ &profile, sizeof(profile)), (status_t)OK);
+ profile.nChannels = numChannels;
+ CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioEvrc,
+ &profile, sizeof(profile)), (status_t)OK);
+ }
+ else{
+ ALOGI("EVRC decoder \n");
+ }
+}
+
+void ExtendedCodec::setQCELPFormat(
+ int32_t numChannels, int32_t sampleRate, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder ) {
+
+ if (isEncoder) {
+ CHECK(numChannels == 1);
+ //////////////// input port ////////////////////
+ //handle->setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ //////////////// output port ////////////////////
+ // format
+ OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ format.nPortIndex = kPortIndexOutput;
+ format.nIndex = 0;
+ status_t err = OMX_ErrorNone;
+ while (OMX_ErrorNone == err) {
+ CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format)), (status_t)OK);
+ if (format.eEncoding == OMX_AUDIO_CodingQCELP13) {
+ break;
+ }
+ format.nIndex++;
+ }
+ CHECK_EQ((status_t)OK, err);
+ CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format)), (status_t)OK);
+
+ // port definition
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+ def.format.audio.cMIMEType = NULL;
+ CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), (status_t)OK);
+ def.format.audio.bFlagErrorConcealment = OMX_TRUE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingQCELP13;
+ CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamPortDefinition,
+ &def, sizeof(def)), (status_t)OK);
+
+ // profile
+ OMX_AUDIO_PARAM_QCELP13TYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexOutput;
+ CHECK_EQ(OMXhandle->getParameter(nodeID, OMX_IndexParamAudioQcelp13,
+ &profile, sizeof(profile)), (status_t)OK);
+ profile.nChannels = numChannels;
+ CHECK_EQ(OMXhandle->setParameter(nodeID, OMX_IndexParamAudioQcelp13,
+ &profile, sizeof(profile)), (status_t)OK);
+ }
+ else {
+ ALOGI("QCELP decoder \n");
+ }
+}
+
+status_t ExtendedCodec::setWMAFormat(
+ const sp<MetaData> &meta, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder ) {
+ sp<AMessage> msg = new AMessage();
+ msg->clear();
+ convertMetaDataToMessage(meta, &msg);
+ return setWMAFormat(msg, OMXhandle, nodeID, isEncoder);
+}
+
+status_t ExtendedCodec::setWMAFormat(
+ const sp<AMessage> &msg, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder ) {
+ ALOGV("setWMAFormat Called");
+
+ if (isEncoder) {
+ ALOGE("WMA encoding not supported");
+ return OK;
+ } else {
+ int32_t version;
+ OMX_AUDIO_PARAM_WMATYPE paramWMA;
+ QOMX_AUDIO_PARAM_WMA10PROTYPE paramWMA10;
+ CHECK(msg->findInt32(getMsgKey(kKeyWMAVersion), &version));
+ int32_t numChannels;
+ int32_t bitRate;
+ int32_t sampleRate;
+ int32_t encodeOptions;
+ int32_t blockAlign;
+ int32_t bitspersample;
+ int32_t formattag;
+ int32_t advencopt1;
+ int32_t advencopt2;
+ int32_t VirtualPktSize;
+ if(version==kTypeWMAPro || version==kTypeWMALossLess) {
+ CHECK(msg->findInt32(getMsgKey(kKeyWMABitspersample), &bitspersample));
+ CHECK(msg->findInt32(getMsgKey(kKeyWMAFormatTag), &formattag));
+ CHECK(msg->findInt32(getMsgKey(kKeyWMAAdvEncOpt1), &advencopt1));
+ CHECK(msg->findInt32(getMsgKey(kKeyWMAAdvEncOpt2), &advencopt2));
+ CHECK(msg->findInt32(getMsgKey(kKeyWMAVirPktSize), &VirtualPktSize));
+ }
+ if(version==kTypeWMA) {
+ InitOMXParams(¶mWMA);
+ paramWMA.nPortIndex = kPortIndexInput;
+ } else if(version==kTypeWMAPro || version==kTypeWMALossLess) {
+ InitOMXParams(¶mWMA10);
+ paramWMA10.nPortIndex = kPortIndexInput;
+ }
+ CHECK(msg->findInt32("channel-count", &numChannels));
+ CHECK(msg->findInt32("sample-rate", &sampleRate));
+ CHECK(msg->findInt32(getMsgKey(kKeyBitRate), &bitRate));
+ CHECK(msg->findInt32(getMsgKey(kKeyWMAEncodeOpt), &encodeOptions));
+ CHECK(msg->findInt32(getMsgKey(kKeyWMABlockAlign), &blockAlign));
+ ALOGV("Channels: %d, SampleRate: %d, BitRate; %d"
+ "EncodeOptions: %d, blockAlign: %d", numChannels,
+ sampleRate, bitRate, encodeOptions, blockAlign);
+ if(sampleRate>48000 || numChannels>2)
+ {
+ ALOGE("Unsupported samplerate/channels");
+ return ERROR_UNSUPPORTED;
+ }
+ if(version==kTypeWMAPro || version==kTypeWMALossLess)
+ {
+ ALOGV("Bitspersample: %d, wmaformattag: %d,"
+ "advencopt1: %d, advencopt2: %d VirtualPktSize %d", bitspersample,
+ formattag, advencopt1, advencopt2, VirtualPktSize);
+ }
+ status_t err = OK;
+ OMX_INDEXTYPE index;
+ if(version==kTypeWMA) {
+ err = OMXhandle->getParameter(
+ nodeID, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA));
+ } else if(version==kTypeWMAPro || version==kTypeWMALossLess) {
+ OMXhandle->getExtensionIndex(nodeID,"OMX.Qualcomm.index.audio.wma10Pro",&index);
+ err = OMXhandle->getParameter(
+ nodeID, index, ¶mWMA10, sizeof(paramWMA10));
+ }
+ CHECK_EQ(err, (status_t)OK);
+ if(version==kTypeWMA) {
+ paramWMA.nChannels = numChannels;
+ paramWMA.nSamplingRate = sampleRate;
+ paramWMA.nEncodeOptions = encodeOptions;
+ paramWMA.nBitRate = bitRate;
+ paramWMA.nBlockAlign = blockAlign;
+ } else if(version==kTypeWMAPro || version==kTypeWMALossLess) {
+ paramWMA10.nChannels = numChannels;
+ paramWMA10.nSamplingRate = sampleRate;
+ paramWMA10.nEncodeOptions = encodeOptions;
+ paramWMA10.nBitRate = bitRate;
+ paramWMA10.nBlockAlign = blockAlign;
+ }
+ if(version==kTypeWMAPro || version==kTypeWMALossLess) {
+ paramWMA10.advancedEncodeOpt = advencopt1;
+ paramWMA10.advancedEncodeOpt2 = advencopt2;
+ paramWMA10.formatTag = formattag;
+ paramWMA10.validBitsPerSample = bitspersample;
+ paramWMA10.nVirtualPktSize = VirtualPktSize;
+ }
+ if(version==kTypeWMA) {
+ err = OMXhandle->setParameter(
+ nodeID, OMX_IndexParamAudioWma, ¶mWMA, sizeof(paramWMA));
+ } else if(version==kTypeWMAPro || version==kTypeWMALossLess) {
+ err = OMXhandle->setParameter(
+ nodeID, index, ¶mWMA10, sizeof(paramWMA10));
+ }
+ return err;
+ }
+ return OK;
+}
+
+void ExtendedCodec::setAC3Format(
+ int32_t numChannels, int32_t sampleRate, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID) {
+ QOMX_AUDIO_PARAM_AC3TYPE profileAC3;
+ QOMX_AUDIO_PARAM_AC3PP profileAC3PP;
+ OMX_INDEXTYPE indexTypeAC3;
+ OMX_INDEXTYPE indexTypeAC3PP;
+ OMX_PARAM_PORTDEFINITIONTYPE portParam;
+
+ //configure input port
+ ALOGV("setAC3Format samplerate %d, numChannels %d", sampleRate, numChannels);
+ InitOMXParams(&portParam);
+ portParam.nPortIndex = 0;
+ status_t err = OMXhandle->getParameter(
+ nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam));
+ CHECK_EQ(err, (status_t)OK);
+ err = OMXhandle->setParameter(
+ nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam));
+ CHECK_EQ(err, (status_t)OK);
+
+ //configure output port
+ portParam.nPortIndex = 1;
+ err = OMXhandle->getParameter(
+ nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam));
+ CHECK_EQ(err, (status_t)OK);
+ err = OMXhandle->setParameter(
+ nodeID, OMX_IndexParamPortDefinition, &portParam, sizeof(portParam));
+ CHECK_EQ(err, (status_t)OK);
+
+ err = OMXhandle->getExtensionIndex(nodeID, OMX_QCOM_INDEX_PARAM_AC3TYPE, &indexTypeAC3);
+
+ InitOMXParams(&profileAC3);
+ profileAC3.nPortIndex = kPortIndexInput;
+ err = OMXhandle->getParameter(nodeID, indexTypeAC3, &profileAC3, sizeof(profileAC3));
+ CHECK_EQ(err,(status_t)OK);
+
+ profileAC3.nSamplingRate = sampleRate;
+ profileAC3.nChannels = 2;
+ profileAC3.eChannelConfig = OMX_AUDIO_AC3_CHANNEL_CONFIG_2_0;
+
+ ALOGV("numChannels = %d, profileAC3.nChannels = %d", numChannels, profileAC3.nChannels);
+
+ err = OMXhandle->setParameter(nodeID, indexTypeAC3, &profileAC3, sizeof(profileAC3));
+ CHECK_EQ(err,(status_t)OK);
+
+ //for output port
+ OMX_AUDIO_PARAM_PCMMODETYPE profilePcm;
+ InitOMXParams(&profilePcm);
+ profilePcm.nPortIndex = kPortIndexOutput;
+ err = OMXhandle->getParameter(
+ nodeID, OMX_IndexParamAudioPcm, &profilePcm, sizeof(profilePcm));
+ CHECK_EQ(err, (status_t)OK);
+
+ profilePcm.nSamplingRate = sampleRate;
+ err = OMXhandle->setParameter(
+ nodeID, OMX_IndexParamAudioPcm, &profilePcm, sizeof(profilePcm));
+ CHECK_EQ(err, (status_t)OK);
+ OMXhandle->getExtensionIndex(nodeID, OMX_QCOM_INDEX_PARAM_AC3PP, &indexTypeAC3PP);
+
+ InitOMXParams(&profileAC3PP);
+ profileAC3PP.nPortIndex = kPortIndexInput;
+ err = OMXhandle->getParameter(
+ nodeID, indexTypeAC3PP, &profileAC3PP, sizeof(profileAC3PP));
+ CHECK_EQ(err, (status_t)OK);
+
+ int i;
+ int channel_routing[6] = {0};
+
+ for (i=0; i<6; i++) {
+ channel_routing[i] = -1;
+ }
+ for (i=0; i<6; i++) {
+ profileAC3PP.eChannelRouting[i] = (OMX_AUDIO_AC3_CHANNEL_ROUTING)channel_routing[i];
+ }
+
+ profileAC3PP.eChannelRouting[0] = OMX_AUDIO_AC3_CHANNEL_LEFT;
+ profileAC3PP.eChannelRouting[1] = OMX_AUDIO_AC3_CHANNEL_RIGHT;
+ err = OMXhandle->setParameter(nodeID, indexTypeAC3PP, &profileAC3PP, sizeof(profileAC3PP));
+ CHECK_EQ(err, (status_t)OK);
+}
+
+bool ExtendedCodec::useHWAACDecoder(const char *mime) {
+ char value[PROPERTY_VALUE_MAX] = {0};
+ int aaccodectype = 0;
+ aaccodectype = property_get("media.aaccodectype", value, NULL);
+ if (aaccodectype && !strncmp("0", value, 1) &&
+ !strncmp(mime, MEDIA_MIMETYPE_AUDIO_AAC,sizeof(MEDIA_MIMETYPE_AUDIO_AAC))) {
+ ALOGI("Using Hardware AAC Decoder");
+ return true;
+ }
+ return false;
+}
+
+} //namespace android
+
+#else //ENABLE_AV_ENHANCEMENTS
+
+namespace android {
+ status_t ExtendedCodec::convertMetaDataToMessage(
+ const sp<MetaData> &meta, sp<AMessage> *format) {
+ return OK;
+ }
+
+ uint32_t ExtendedCodec::getComponentQuirks (
+ const MediaCodecList *list, size_t index) {
+ return 0;
+ }
+
+ status_t ExtendedCodec::setDIVXFormat(
+ const sp<AMessage> &msg, const char* mime,
+ sp<IOMX> OMXhandle,IOMX::node_id nodeID, int port_index) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::setAudioFormat(
+ const sp<MetaData> &meta, const char* mime,
+ sp<IOMX> OMXhandle,IOMX::node_id nodeID,
+ bool isEncoder) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::setAudioFormat(
+ const sp<AMessage> &msg, const char* mime,
+ sp<IOMX> OMXhandle,IOMX::node_id nodeID,
+ bool isEncoder) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::setVideoInputFormat(
+ const char *mime,
+ OMX_VIDEO_CODINGTYPE *compressionFormat) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::setVideoOutputFormat(
+ const char *mime,
+ OMX_VIDEO_CODINGTYPE *compressionFormat) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::getSupportedAudioFormatInfo(
+ const AString* mime,
+ sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID,
+ int portIndex,
+ int* channelCount) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::handleSupportedAudioFormats(
+ int format, AString* meta) {
+ return UNKNOWN_ERROR;
+ }
+
+ const char* ExtendedCodec::overrideComponentName (
+ uint32_t quirks, const sp<MetaData> &meta) {
+ return NULL;
+ }
+
+ void ExtendedCodec::overrideComponentName(
+ uint32_t quirks, const sp<AMessage> &msg,
+ AString* componentName){
+ }
+
+ void ExtendedCodec::getRawCodecSpecificData(
+ const sp<MetaData> &meta, const void* &data, size_t &size) {
+ size = 0;
+ }
+
+ sp<ABuffer> ExtendedCodec::getRawCodecSpecificData(
+ const sp<AMessage> &msg){
+ return NULL;
+ }
+
+ void ExtendedCodec::getAacCodecSpecificData(
+ const sp<MetaData> &meta,
+ const void* &data,
+ size_t& size){
+ return;
+ }
+
+ sp<ABuffer> ExtendedCodec::getAacCodecSpecificData(
+ const sp<AMessage> &msg){
+ return NULL;
+ }
+
+
+ status_t ExtendedCodec::setSupportedRole(
+ const sp<IOMX> &omx, IOMX::node_id node,
+ bool isEncoder, const char *mime) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::setWMAFormat(
+ const sp<MetaData> &meta, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder) {
+ return OK;
+ }
+
+ status_t ExtendedCodec::setWMAFormat(
+ const sp<AMessage> &msg, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, bool isEncoder) {
+ return OK;
+ }
+
+ void ExtendedCodec::setEVRCFormat(
+ int32_t numChannels, int32_t sampleRate,
+ sp<IOMX> OMXhandle, IOMX::node_id nodeID,
+ bool isEncoder) {
+ }
+
+ void ExtendedCodec::setQCELPFormat(
+ int32_t numChannels, int32_t sampleRate,
+ sp<IOMX> OMXhandle, IOMX::node_id nodeID,
+ bool isEncoder) {
+ }
+
+ void ExtendedCodec::setAC3Format(
+ int32_t numChannels, int32_t sampleRate,
+ sp<IOMX> OMXhandle, IOMX::node_id nodeID) {
+ }
+
+ void ExtendedCodec::configureFramePackingFormat(
+ const sp<AMessage> &msg, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, const char* componentName){
+ }
+
+ void ExtendedCodec::configureFramePackingFormat(
+ const sp<MetaData> &meta, sp<IOMX> OMXhandle,
+ IOMX::node_id nodeID, const char* componentName) {
+ }
+
+ void ExtendedCodec::configureVideoDecoder(
+ const sp<MetaData> &meta, const char* mime, sp<IOMX> OMXhandle,
+ const uint32_t flags, IOMX::node_id nodeID, const char* componentName) {
+ }
+
+ void ExtendedCodec::configureVideoDecoder(
+ const sp<AMessage> &msg, const char* mime, sp<IOMX> OMXhandle,
+ const uint32_t flags, IOMX::node_id nodeID, const char* componentName) {
+ }
+
+ bool ExtendedCodec::useHWAACDecoder(const char *mime) {
+ return false;
+ }
+
+ void ExtendedCodec::enableSmoothStreaming(
+ const sp<IOMX> &omx, IOMX::node_id nodeID, bool* isEnabled,
+ const char* componentName) {
+ *isEnabled = false;
+ return;
+ }
+
+} //namespace android
+
+#endif //ENABLE_AV_ENHANCEMENTS
diff --git a/media/libstagefright/ExtendedExtractor.cpp b/media/libstagefright/ExtendedExtractor.cpp
new file mode 100644
index 0000000..952f542
--- /dev/null
+++ b/media/libstagefright/ExtendedExtractor.cpp
@@ -0,0 +1,143 @@
+/*Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ExtendedExtractor"
+#include <utils/Log.h>
+#include <dlfcn.h> // for dlopen/dlclose
+#include "include/ExtendedExtractor.h"
+
+#ifdef ENABLE_AV_ENHANCEMENTS
+
+namespace android {
+
+static const char* EXTENDED_EXTRACTOR_LIB = "libExtendedExtractor.so";
+static const char* EXTENDED_EXTRACTOR_CREATE = "CreateExtractor";
+static const char* EXTENDED_EXTRACTOR_SNIFF = "SniffExtendedExtractor";
+
+typedef MediaExtractor* (*ExtendedExtractorCreate)
+ (const sp<DataSource> &source, const char* mime);
+
+typedef bool (*ExtendedExtractorSniff)
+ (const sp<DataSource> &source, String8 *mimeType,
+ float *confidence,sp<AMessage> *meta);
+
+static void* loadExtendedExtractorLib() {
+ static void* extendedExtractorLib = NULL;
+ static bool alreadyTriedToLoadLib = false;
+
+ if(!alreadyTriedToLoadLib) {
+ alreadyTriedToLoadLib = true;
+
+ extendedExtractorLib = ::dlopen(EXTENDED_EXTRACTOR_LIB, RTLD_LAZY);
+
+ if(extendedExtractorLib == NULL) {
+ ALOGV("Failed to load %s, dlerror = %s \n",
+ EXTENDED_EXTRACTOR_LIB, dlerror());
+ }
+ }
+
+ return extendedExtractorLib;
+}
+
+MediaExtractor* ExtendedExtractor::Create (
+ const sp<DataSource> &source, const char *mime) {
+ static ExtendedExtractorCreate create = NULL;
+ static bool alreadyTriedToFindCreateFunction = false;
+ MediaExtractor* extractor = NULL;
+
+ if (!alreadyTriedToFindCreateFunction) {
+ void *extendedExtractorLib = loadExtendedExtractorLib();
+
+ if (extendedExtractorLib != NULL) {
+ create = (ExtendedExtractorCreate) dlsym (
+ extendedExtractorLib, EXTENDED_EXTRACTOR_CREATE);
+ alreadyTriedToFindCreateFunction = true;
+ }
+ }
+
+ if (create == NULL) {
+ ALOGE ("Failed to find symbol : %s, dlerror = %s",
+ EXTENDED_EXTRACTOR_CREATE, dlerror());
+ return NULL;
+ }
+
+ extractor = create (source, mime);
+ if (extractor == NULL) {
+ ALOGE("Failed to instantiate extractor \n");
+ }
+
+ return extractor;
+}
+
+bool ExtendedExtractor::Sniff (
+ const sp<DataSource> &source, String8 *mimeType,
+ float *confidence,sp<AMessage> *meta) {
+ void *extendedExtractorLib = loadExtendedExtractorLib();
+ bool retVal = false;
+
+ if (extendedExtractorLib != NULL) {
+ ExtendedExtractorSniff sniff = (ExtendedExtractorSniff) dlsym (
+ extendedExtractorLib, EXTENDED_EXTRACTOR_SNIFF);
+
+ if (sniff == NULL) {
+ ALOGE ("Failed to find symbol : %s, dlerror = %s",
+ EXTENDED_EXTRACTOR_SNIFF, dlerror());
+ return retVal;
+ }
+
+ retVal = sniff (source, mimeType, confidence, meta);
+
+ if(!retVal) {
+ ALOGV("Sniff Failed");
+ }
+ }
+ return retVal;
+}
+
+} // namespace android
+
+#else //ENABLE_AV_ENHANCEMENTS
+
+namespace android {
+
+MediaExtractor* ExtendedExtractor::Create (
+ const sp<DataSource> &source, const char *mime) {
+ return NULL;
+}
+bool ExtendedExtractor::Sniff (
+ const sp<DataSource> &source, String8 *mimeType,
+ float *confidence,sp<AMessage> *meta) {
+ *confidence = 0.0;
+ return false;
+}
+
+} // namespace android
+
+#endif //ENABLE_AV_ENHANCEMENTS
+
diff --git a/media/libstagefright/ExtendedMediaDefs.cpp b/media/libstagefright/ExtendedMediaDefs.cpp
new file mode 100644
index 0000000..209db2b
--- /dev/null
+++ b/media/libstagefright/ExtendedMediaDefs.cpp
@@ -0,0 +1,66 @@
+/*Copyright (c) 2012 - 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#include <QCMediaDefs.h>
+
+namespace android {
+
+const char *MEDIA_MIMETYPE_AUDIO_EVRC = "audio/evrc";
+
+const char *MEDIA_MIMETYPE_VIDEO_WMV = "video/x-ms-wmv";
+const char *MEDIA_MIMETYPE_AUDIO_WMA = "audio/x-ms-wma";
+const char *MEDIA_MIMETYPE_CONTAINER_ASF = "video/x-ms-asf";
+const char *MEDIA_MIMETYPE_VIDEO_DIVX = "video/divx";
+const char *MEDIA_MIMETYPE_AUDIO_AC3 = "audio/ac3";
+const char *MEDIA_MIMETYPE_CONTAINER_AAC = "audio/aac";
+const char *MEDIA_MIMETYPE_CONTAINER_QCP = "audio/vnd.qcelp";
+const char *MEDIA_MIMETYPE_VIDEO_DIVX311 = "video/divx311";
+const char *MEDIA_MIMETYPE_VIDEO_DIVX4 = "video/divx4";
+
+const char *MEDIA_MIMETYPE_CONTAINER_MPEG2 = "video/mp2";
+
+const char *MEDIA_MIMETYPE_CONTAINER_3G2 = "video/3g2";
+const char *MEDIA_MIMETYPE_AUDIO_DTS = "audio/dts";
+
+const char *MEDIA_MIMETYPE_AUDIO_DTS_LBR = "audio/dts-lbr";
+const char *MEDIA_MIMETYPE_AUDIO_EAC3 = "audio/eac3";
+const char *MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS = "audio/amr-wb-plus";
+const char *MEDIA_MIMETYPE_VIDEO_HEVC = "video/hevc";
+const char *MEDIA_MIMETYPE_CONTAINER_QCAMR_NB = "audio/qc-amr";
+const char *MEDIA_MIMETYPE_CONTAINER_QCAMR_WB = "audio/qc-amr-wb";
+const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG = "audio/qc-mpeg";
+const char *MEDIA_MIMETYPE_CONTAINER_QCWAV = "audio/qc-wav";
+const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG2TS = "video/qc-mp2ts";
+const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG2PS = "video/qc-mp2ps";
+const char *MEDIA_MIMETYPE_CONTAINER_QCMPEG4 = "video/qc-mp4";
+const char *MEDIA_MIMETYPE_CONTAINER_QCMATROSKA = "video/qc-matroska";
+const char *MEDIA_MIMETYPE_CONTAINER_QCOGG = "video/qc-ogg";
+const char *MEDIA_MIMETYPE_CONTAINER_QCFLV = "video/qc-flv";
+const char *MEDIA_MIMETYPE_VIDEO_VPX = "video/x-vnd.on2.vp8"; //backward compatibility
+
+} // namespace android
diff --git a/media/libstagefright/ExtendedUtils.cpp b/media/libstagefright/ExtendedUtils.cpp
new file mode 100644
index 0000000..1da6c9d
--- /dev/null
+++ b/media/libstagefright/ExtendedUtils.cpp
@@ -0,0 +1,649 @@
+/*Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "ExtendedUtils"
+#include <utils/Log.h>
+
+#include <utils/Errors.h>
+#include <sys/types.h>
+#include <ctype.h>
+#include <unistd.h>
+
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/OMXCodec.h>
+#include <cutils/properties.h>
+#include <media/stagefright/MediaExtractor.h>
+
+#include "include/ExtendedUtils.h"
+
+static const int64_t kDefaultAVSyncLateMargin = 40000;
+static const int64_t kMaxAVSyncLateMargin = 250000;
+
+#ifdef ENABLE_AV_ENHANCEMENTS
+
+#include <QCMetaData.h>
+#include <QCMediaDefs.h>
+
+#include "include/ExtendedExtractor.h"
+#include "include/avc_utils.h"
+
+namespace android {
+
+void ExtendedUtils::HFR::setHFRIfEnabled(
+ const CameraParameters& params,
+ sp<MetaData> &meta) {
+ const char *hfr_str = params.get("video-hfr");
+ int32_t hfr = -1;
+ if ( hfr_str != NULL ) {
+ hfr = atoi(hfr_str);
+ }
+ if (hfr < 0) {
+ ALOGW("Invalid hfr value(%d) set from app. Disabling HFR.", hfr);
+ hfr = 0;
+ }
+
+ meta->setInt32(kKeyHFR, hfr);
+}
+
+status_t ExtendedUtils::HFR::reCalculateFileDuration(
+ sp<MetaData> &meta, sp<MetaData> &enc_meta,
+ int64_t &maxFileDurationUs, int32_t frameRate,
+ video_encoder videoEncoder) {
+ status_t retVal = OK;
+ int32_t hfr = 0;
+
+ if (!meta->findInt32(kKeyHFR, &hfr)) {
+ ALOGW("hfr not found, default to 0");
+ }
+
+ if (hfr && frameRate) {
+ maxFileDurationUs = maxFileDurationUs * (hfr/frameRate);
+ }
+
+ enc_meta->setInt32(kKeyHFR, hfr);
+ int32_t width = 0, height = 0;
+
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ char mDeviceName[100];
+ property_get("ro.board.platform",mDeviceName,"0");
+ if (!strncmp(mDeviceName, "msm7627a", 8)) {
+ if (hfr && (width * height > 432*240)) {
+ ALOGE("HFR mode is supported only upto WQVGA resolution");
+ return INVALID_OPERATION;
+ }
+ } else if (!strncmp(mDeviceName, "msm8974", 7) ||
+ !strncmp(mDeviceName, "msm8610", 7)) {
+ if (hfr && (width * height > 1920*1088)) {
+ ALOGE("HFR mode is supported only upto 1080p resolution");
+ return INVALID_OPERATION;
+ }
+ } else {
+ if (hfr && ((videoEncoder != VIDEO_ENCODER_H264) || (width * height > 800*480))) {
+ ALOGE("HFR mode is supported only upto WVGA and H264 codec.");
+ return INVALID_OPERATION;
+ }
+ }
+ return retVal;
+}
+
+void ExtendedUtils::HFR::reCalculateTimeStamp(
+ sp<MetaData> &meta, int64_t ×tampUs) {
+ int32_t frameRate = 0, hfr = 0;
+ if (!(meta->findInt32(kKeyFrameRate, &frameRate))) {
+ return;
+ }
+
+ if (!(meta->findInt32(kKeyHFR, &hfr))) {
+ return;
+ }
+
+ if (hfr && frameRate) {
+ timestampUs = (hfr * timestampUs) / frameRate;
+ }
+}
+
+void ExtendedUtils::HFR::reCalculateHFRParams(
+ const sp<MetaData> &meta, int32_t &frameRate,
+ int32_t &bitRate) {
+ int32_t hfr = 0;
+ if (!(meta->findInt32(kKeyHFR, &hfr))) {
+ return;
+ }
+
+ if (hfr && frameRate) {
+ bitRate = (hfr * bitRate) / frameRate;
+ frameRate = hfr;
+ }
+}
+
+void ExtendedUtils::HFR::copyHFRParams(
+ const sp<MetaData> &inputFormat,
+ sp<MetaData> &outputFormat) {
+ int32_t frameRate = 0, hfr = 0;
+ inputFormat->findInt32(kKeyHFR, &hfr);
+ inputFormat->findInt32(kKeyFrameRate, &frameRate);
+ outputFormat->setInt32(kKeyHFR, hfr);
+ outputFormat->setInt32(kKeyFrameRate, frameRate);
+}
+
+bool ExtendedUtils::ShellProp::isAudioDisabled() {
+ bool retVal = false;
+ char disableAudio[PROPERTY_VALUE_MAX];
+ property_get("persist.debug.sf.noaudio", disableAudio, "0");
+ if (atoi(disableAudio) == 1) {
+ retVal = true;
+ }
+ return retVal;
+}
+
+void ExtendedUtils::ShellProp::setEncoderProfile(
+ video_encoder &videoEncoder, int32_t &videoEncoderProfile) {
+ char value[PROPERTY_VALUE_MAX];
+ bool customProfile = false;
+ if (!property_get("encoder.video.profile", value, NULL) > 0) {
+ return;
+ }
+
+ switch (videoEncoder) {
+ case VIDEO_ENCODER_H264:
+ if (strncmp("base", value, 4) == 0) {
+ videoEncoderProfile = OMX_VIDEO_AVCProfileBaseline;
+ ALOGI("H264 Baseline Profile");
+ } else if (strncmp("main", value, 4) == 0) {
+ videoEncoderProfile = OMX_VIDEO_AVCProfileMain;
+ ALOGI("H264 Main Profile");
+ } else if (strncmp("high", value, 4) == 0) {
+ videoEncoderProfile = OMX_VIDEO_AVCProfileHigh;
+ ALOGI("H264 High Profile");
+ } else {
+ ALOGW("Unsupported H264 Profile");
+ }
+ break;
+ case VIDEO_ENCODER_MPEG_4_SP:
+ if (strncmp("simple", value, 5) == 0 ) {
+ videoEncoderProfile = OMX_VIDEO_MPEG4ProfileSimple;
+ ALOGI("MPEG4 Simple profile");
+ } else if (strncmp("asp", value, 3) == 0 ) {
+ videoEncoderProfile = OMX_VIDEO_MPEG4ProfileAdvancedSimple;
+ ALOGI("MPEG4 Advanced Simple Profile");
+ } else {
+ ALOGW("Unsupported MPEG4 Profile");
+ }
+ break;
+ default:
+ ALOGW("No custom profile support for other codecs");
+ break;
+ }
+}
+
+int64_t ExtendedUtils::ShellProp::getMaxAVSyncLateMargin() {
+ char lateMarginMs[PROPERTY_VALUE_MAX] = {0};
+ property_get("media.sf.set.late.margin", lateMarginMs, "0");
+ int64_t newLateMarginUs = atoi(lateMarginMs)*1000;
+ int64_t maxLateMarginUs = newLateMarginUs;
+
+ if (newLateMarginUs > kDefaultAVSyncLateMargin
+ || newLateMarginUs < kDefaultAVSyncLateMargin) {
+ maxLateMarginUs = kDefaultAVSyncLateMargin;
+ }
+
+ ALOGI("AV Sync late margin : Intended=%lldms Using=%lldms",
+ maxLateMarginUs/1000, newLateMarginUs/1000);
+ return maxLateMarginUs;
+}
+
+bool ExtendedUtils::ShellProp::isSmoothStreamingEnabled() {
+ char prop[PROPERTY_VALUE_MAX] = {0};
+ property_get("mm.enable.smoothstreaming", prop, "0");
+ if (!strncmp(prop, "true", 4) || atoi(prop)) {
+ return true;
+ }
+ return false;
+}
+
+void ExtendedUtils::setBFrames(
+ OMX_VIDEO_PARAM_MPEG4TYPE &mpeg4type, int32_t &numBFrames,
+ const char* componentName) {
+ //ignore non QC components
+ if (strncmp(componentName, "OMX.qcom.", 9)) {
+ return;
+ }
+ if (mpeg4type.eProfile > OMX_VIDEO_MPEG4ProfileSimple) {
+ mpeg4type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
+ mpeg4type.nBFrames = 1;
+ mpeg4type.nPFrames /= (mpeg4type.nBFrames + 1);
+ numBFrames = mpeg4type.nBFrames;
+ }
+ return;
+}
+
+void ExtendedUtils::setBFrames(
+ OMX_VIDEO_PARAM_AVCTYPE &h264type, int32_t &numBFrames,
+ int32_t iFramesInterval, int32_t frameRate, const char* componentName) {
+ //ignore non QC components
+ if (strncmp(componentName, "OMX.qcom.", 9)) {
+ return;
+ }
+ OMX_U32 val = 0;
+ if (iFramesInterval < 0) {
+ val = 0xFFFFFFFF;
+ } else if (iFramesInterval == 0) {
+ val = 0;
+ } else {
+ val = frameRate * iFramesInterval - 1;
+ CHECK(val > 1);
+ }
+
+ h264type.nPFrames = val;
+
+ if (h264type.nPFrames == 0) {
+ h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+
+ if (h264type.eProfile > OMX_VIDEO_AVCProfileBaseline) {
+ h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
+ h264type.nBFrames = 1;
+ h264type.nPFrames /= (h264type.nBFrames + 1);
+ //enable CABAC as default entropy mode for Hihg/Main profiles
+ h264type.bEntropyCodingCABAC = OMX_TRUE;
+ h264type.nCabacInitIdc = 0;
+ numBFrames = h264type.nBFrames;
+ }
+ return;
+}
+
+/*
+QCOM HW AAC encoder allowed bitrates
+------------------------------------------------------------------------------------------------------------------
+Bitrate limit |AAC-LC(Mono) | AAC-LC(Stereo) |AAC+(Mono) | AAC+(Stereo) | eAAC+ |
+Minimum |Min(24000,0.5 * f_s) |Min(24000,f_s) | 24000 |24000 | 24000 |
+Maximum |Min(192000,6 * f_s) |Min(192000,12 * f_s) | Min(192000,6 * f_s) | Min(192000,12 * f_s) | Min(192000,12 * f_s) |
+------------------------------------------------------------------------------------------------------------------
+*/
+bool ExtendedUtils::UseQCHWAACEncoder(audio_encoder Encoder,int32_t Channel,int32_t BitRate,int32_t SampleRate)
+{
+ bool ret = false;
+ int minBiteRate = -1;
+ int maxBiteRate = -1;
+ char propValue[PROPERTY_VALUE_MAX] = {0};
+
+ property_get("qcom.hw.aac.encoder",propValue,NULL);
+ if (!strncmp(propValue,"true",sizeof("true"))) {
+ //check for QCOM's HW AAC encoder only when qcom.aac.encoder = true;
+ ALOGV("qcom.aac.encoder enabled, check AAC encoder(%d) allowed bitrates",Encoder);
+ switch (Encoder) {
+ case AUDIO_ENCODER_AAC:// for AAC-LC format
+ if (Channel == 1) {//mono
+ minBiteRate = MIN_BITERATE_AAC<(SampleRate/2)?MIN_BITERATE_AAC:(SampleRate/2);
+ maxBiteRate = MAX_BITERATE_AAC<(SampleRate*6)?MAX_BITERATE_AAC:(SampleRate*6);
+ } else if (Channel == 2) {//stereo
+ minBiteRate = MIN_BITERATE_AAC<SampleRate?MIN_BITERATE_AAC:SampleRate;
+ maxBiteRate = MAX_BITERATE_AAC<(SampleRate*12)?MAX_BITERATE_AAC:(SampleRate*12);
+ }
+ break;
+ case AUDIO_ENCODER_HE_AAC:// for AAC+ format
+ if (Channel == 1) {//mono
+ minBiteRate = MIN_BITERATE_AAC;
+ maxBiteRate = MAX_BITERATE_AAC<(SampleRate*6)?MAX_BITERATE_AAC:(SampleRate*6);
+ } else if (Channel == 2) {//stereo
+ minBiteRate = MIN_BITERATE_AAC;
+ maxBiteRate = MAX_BITERATE_AAC<(SampleRate*12)?MAX_BITERATE_AAC:(SampleRate*12);
+ }
+ break;
+ default:
+ ALOGV("encoder:%d not supported by QCOM HW AAC encoder",Encoder);
+
+ }
+
+ //return true only when 1. minBiteRate and maxBiteRate are updated(not -1) 2. minBiteRate <= SampleRate <= maxBiteRate
+ if (SampleRate >= minBiteRate && SampleRate <= maxBiteRate) {
+ ret = true;
+ }
+ }
+
+ return ret;
+}
+
+
+//- returns NULL if we dont really need a new extractor (or cannot),
+// valid extractor is returned otherwise
+//- caller needs to check for NULL
+// ----------------------------------------
+// defaultExt - the existing extractor
+// source - file source
+// mime - container mime
+// ----------------------------------------
+// Note: defaultExt will be deleted in this function if the new parser is selected
+
+sp<MediaExtractor> ExtendedUtils::MediaExtractor_CreateIfNeeded(sp<MediaExtractor> defaultExt,
+ const sp<DataSource> &source,
+ const char *mime) {
+ bool bCheckExtendedExtractor = false;
+ bool videoTrackFound = false;
+ bool audioTrackFound = false;
+ bool amrwbAudio = false;
+ int numOfTrack = 0;
+
+ if (defaultExt != NULL) {
+ for (size_t trackItt = 0; trackItt < defaultExt->countTracks(); ++trackItt) {
+ ++numOfTrack;
+ sp<MetaData> meta = defaultExt->getTrackMetaData(trackItt);
+ const char *_mime;
+ CHECK(meta->findCString(kKeyMIMEType, &_mime));
+
+ String8 mime = String8(_mime);
+
+ if (!strncasecmp(mime.string(), "audio/", 6)) {
+ audioTrackFound = true;
+
+ amrwbAudio = !strncasecmp(mime.string(),
+ MEDIA_MIMETYPE_AUDIO_AMR_WB,
+ strlen(MEDIA_MIMETYPE_AUDIO_AMR_WB));
+ if (amrwbAudio) {
+ break;
+ }
+ }else if(!strncasecmp(mime.string(), "video/", 6)) {
+ videoTrackFound = true;
+ }
+ }
+
+ if(amrwbAudio) {
+ bCheckExtendedExtractor = true;
+ }else if (numOfTrack == 0) {
+ bCheckExtendedExtractor = true;
+ } else if(numOfTrack == 1) {
+ if((videoTrackFound) ||
+ (!videoTrackFound && !audioTrackFound)){
+ bCheckExtendedExtractor = true;
+ }
+ } else if (numOfTrack >= 2){
+ if(videoTrackFound && audioTrackFound) {
+ if(amrwbAudio) {
+ bCheckExtendedExtractor = true;
+ }
+ } else {
+ bCheckExtendedExtractor = true;
+ }
+ }
+ } else {
+ bCheckExtendedExtractor = true;
+ }
+
+ if (!bCheckExtendedExtractor) {
+ ALOGD("extended extractor not needed, return default");
+ return defaultExt;
+ }
+
+ //Create Extended Extractor only if default extractor is not selected
+ ALOGD("Try creating ExtendedExtractor");
+ sp<MediaExtractor> retExtExtractor = ExtendedExtractor::Create(source, mime);
+
+ if (retExtExtractor == NULL) {
+ ALOGD("Couldn't create the extended extractor, return default one");
+ return defaultExt;
+ }
+
+ if (defaultExt == NULL) {
+ ALOGD("default extractor is NULL, return extended extractor");
+ return retExtExtractor;
+ }
+
+ //bCheckExtendedExtractor is true which means default extractor was found
+ //but we want to give preference to extended extractor based on certain
+ //conditions.
+
+ //needed to prevent a leak in case both extractors are valid
+ //but we still dont want to use the extended one. we need
+ //to delete the new one
+ bool bUseDefaultExtractor = true;
+
+ for (size_t trackItt = 0; (trackItt < retExtExtractor->countTracks()); ++trackItt) {
+ sp<MetaData> meta = retExtExtractor->getTrackMetaData(trackItt);
+ const char *mime;
+ bool success = meta->findCString(kKeyMIMEType, &mime);
+ if ((success == true) &&
+ (!strncasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS,
+ strlen(MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS)) ||
+ !strncasecmp(mime, MEDIA_MIMETYPE_VIDEO_HEVC,
+ strlen(MEDIA_MIMETYPE_VIDEO_HEVC)) )) {
+
+ ALOGD("Discarding default extractor and using the extended one");
+ bUseDefaultExtractor = false;
+ break;
+ }
+ }
+
+ if (bUseDefaultExtractor) {
+ ALOGD("using default extractor inspite of having a new extractor");
+ retExtExtractor.clear();
+ return defaultExt;
+ } else {
+ defaultExt.clear();
+ return retExtExtractor;
+ }
+
+}
+
+
+void ExtendedUtils::helper_addMediaCodec(Vector<MediaCodecList::CodecInfo> &mCodecInfos,
+ KeyedVector<AString, size_t> &mTypes,
+ bool encoder, const char *name,
+ const char *type, uint32_t quirks) {
+ mCodecInfos.push();
+ MediaCodecList::CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mName = name;
+ info->mIsEncoder = encoder;
+ ssize_t index = mTypes.indexOfKey(type);
+ uint32_t bit = mTypes.valueAt(index);
+ info->mTypes |= 1ul << bit;
+ info->mQuirks = quirks;
+}
+
+uint32_t ExtendedUtils::helper_getCodecSpecificQuirks(KeyedVector<AString, size_t> &mCodecQuirks,
+ Vector<AString> quirks) {
+ size_t i = 0, numQuirks = quirks.size();
+ uint32_t bit = 0, value = 0;
+ for (i = 0; i < numQuirks; i++)
+ {
+ ssize_t index = mCodecQuirks.indexOfKey(quirks.itemAt(i));
+ bit = mCodecQuirks.valueAt(index);
+ value |= 1ul << bit;
+ }
+ return value;
+}
+
+bool ExtendedUtils::isAVCProfileSupported(int32_t profile){
+ if(profile == OMX_VIDEO_AVCProfileMain || profile == OMX_VIDEO_AVCProfileHigh || profile == OMX_VIDEO_AVCProfileBaseline){
+ return true;
+ } else {
+ return false;
+ }
+}
+
+void ExtendedUtils::updateNativeWindowBufferGeometry(ANativeWindow* anw,
+ OMX_U32 width, OMX_U32 height, OMX_COLOR_FORMATTYPE colorFormat) {
+#if UPDATE_BUFFER_GEOMETRY_AVAILABLE
+ if (anw != NULL) {
+ ALOGI("Calling native window update buffer geometry [%lu x %lu]",
+ width, height);
+ status_t err = anw->perform(
+ anw, NATIVE_WINDOW_UPDATE_BUFFERS_GEOMETRY,
+ width, height, colorFormat);
+ if (err != OK) {
+ ALOGE("UPDATE_BUFFER_GEOMETRY failed %d", err);
+ }
+ }
+#endif
+}
+
+bool ExtendedUtils::checkIsThumbNailMode(const uint32_t flags, char* componentName) {
+ bool isInThumbnailMode = false;
+ if ((flags & OMXCodec::kClientNeedsFramebuffer) && !strncmp(componentName, "OMX.qcom.", 9)) {
+ isInThumbnailMode = true;
+ }
+ return isInThumbnailMode;
+}
+
+void ExtendedUtils::setArbitraryModeIfInterlaced(
+ const uint8_t *ptr, const sp<MetaData> &meta) {
+
+ if (ptr == NULL) {
+ return;
+ }
+ uint16_t spsSize = (((uint16_t)ptr[6]) << 8) + (uint16_t)(ptr[7]);
+ int32_t width = 0, height = 0, isInterlaced = 0;
+ const uint8_t *spsStart = &ptr[8];
+
+ sp<ABuffer> seqParamSet = new ABuffer(spsSize);
+ memcpy(seqParamSet->data(), spsStart, spsSize);
+ FindAVCDimensions(seqParamSet, &width, &height, NULL, NULL, &isInterlaced);
+
+ ALOGV("height is %d, width is %d, isInterlaced is %d\n", height, width, isInterlaced);
+ if (isInterlaced) {
+ meta->setInt32(kKeyUseArbitraryMode, 1);
+ meta->setInt32(kKeyInterlace, 1);
+ }
+ return;
+}
+
+int32_t ExtendedUtils::checkIsInterlace(sp<MetaData> &meta) {
+ int32_t isInterlaceFormat = 0;
+
+ if(meta->findInt32(kKeyInterlace, &isInterlaceFormat)) {
+ ALOGI("interlace format detected");
+ }
+
+ return isInterlaceFormat;
+}
+
+}
+#else //ENABLE_AV_ENHANCEMENTS
+
+namespace android {
+
+void ExtendedUtils::HFR::setHFRIfEnabled(
+ const CameraParameters& params, sp<MetaData> &meta) {
+}
+
+status_t ExtendedUtils::HFR::reCalculateFileDuration(
+ sp<MetaData> &meta, sp<MetaData> &enc_meta,
+ int64_t &maxFileDurationUs, int32_t frameRate,
+ video_encoder videoEncoder) {
+ return OK;
+}
+
+void ExtendedUtils::HFR::reCalculateTimeStamp(
+ sp<MetaData> &meta, int64_t ×tampUs) {
+}
+
+void ExtendedUtils::HFR::reCalculateHFRParams(
+ const sp<MetaData> &meta, int32_t &frameRate,
+ int32_t &bitrate) {
+}
+
+void ExtendedUtils::HFR::copyHFRParams(
+ const sp<MetaData> &inputFormat,
+ sp<MetaData> &outputFormat) {
+}
+
+bool ExtendedUtils::ShellProp::isAudioDisabled() {
+ return false;
+}
+
+void ExtendedUtils::ShellProp::setEncoderProfile(
+ video_encoder &videoEncoder, int32_t &videoEncoderProfile) {
+}
+
+int64_t ExtendedUtils::ShellProp::getMaxAVSyncLateMargin() {
+ return kDefaultAVSyncLateMargin;
+}
+
+bool ExtendedUtils::ShellProp::isSmoothStreamingEnabled() {
+ return false;
+}
+
+void ExtendedUtils::setBFrames(
+ OMX_VIDEO_PARAM_MPEG4TYPE &mpeg4type, int32_t &numBFrames,
+ const char* componentName) {
+}
+
+void ExtendedUtils::setBFrames(
+ OMX_VIDEO_PARAM_AVCTYPE &h264type, int32_t &numBFrames,
+ int32_t iFramesInterval, int32_t frameRate,
+ const char* componentName) {
+}
+
+bool ExtendedUtils::UseQCHWAACEncoder(audio_encoder Encoder,int32_t Channel,
+ int32_t BitRate,int32_t SampleRate) {
+ return false;
+}
+
+sp<MediaExtractor> ExtendedUtils::MediaExtractor_CreateIfNeeded(sp<MediaExtractor> defaultExt,
+ const sp<DataSource> &source,
+ const char *mime) {
+ return defaultExt;
+}
+
+void QCUtils::helper_addMediaCodec(Vector<MediaCodecList::CodecInfo> &mCodecInfos,
+ KeyedVector<AString, size_t> &mTypes,
+ bool encoder, const char *name,
+ const char *type, uint32_t quirks) {
+}
+
+uint32_t QCUtils::helper_getCodecSpecificQuirks(KeyedVector<AString, size_t> &mCodecQuirks,
+ Vector<AString> quirks) {
+ return 0;
+}
+
+bool ExtendedUtils::isAVCProfileSupported(int32_t profile){
+ return false;
+}
+
+void ExtendedUtils::updateNativeWindowBufferGeometry(ANativeWindow* anw,
+ OMX_U32 width, OMX_U32 height, OMX_COLOR_FORMATTYPE colorFormat) {
+}
+
+bool ExtendedUtils::checkIsThumbNailMode(const uint32_t flags, char* componentName) {
+ return false;
+}
+
+void ExtendedUtils::setArbitraryModeIfInterlaced(
+ const uint8_t *ptr, const sp<MetaData> &meta) {
+}
+
+int32_t ExtendedUtils::checkIsInterlace(sp<MetaData> &meta) {
+ return false;
+}
+
+}
+#endif //ENABLE_AV_ENHANCEMENTS
diff --git a/media/libstagefright/ExtendedWriter.cpp b/media/libstagefright/ExtendedWriter.cpp
new file mode 100644
index 0000000..db5bb4a
--- /dev/null
+++ b/media/libstagefright/ExtendedWriter.cpp
@@ -0,0 +1,390 @@
+/*
+ * Copyright (c) 2012 - 2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution.
+ *
+ * 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/ExtendedWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/mediarecorder.h>
+#include <system/audio.h>
+
+#include <sys/prctl.h>
+#include <sys/resource.h>
+
+#include <arpa/inet.h>
+#include <QCMediaDefs.h>
+
+#undef LOG_TAG
+#define LOG_TAG "ExtendedWriter"
+
+namespace android {
+
+ExtendedWriter::ExtendedWriter(const char *filename)
+ : mFile(fopen(filename, "wb")),
+ mInitCheck(mFile != NULL ? OK : NO_INIT),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false),
+ mOffset(0) {
+}
+
+ExtendedWriter::ExtendedWriter(int fd)
+ : mFile(fdopen(fd, "wb")),
+ mInitCheck(mFile != NULL ? OK : NO_INIT),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false),
+ mOffset(0) {
+}
+
+ExtendedWriter::~ExtendedWriter() {
+ if (mStarted) {
+ stop();
+ }
+
+ if (mFile != NULL) {
+ fclose(mFile);
+ mFile = NULL;
+ }
+}
+
+status_t ExtendedWriter::initCheck() const {
+ return mInitCheck;
+}
+
+status_t ExtendedWriter::addSource(const sp<MediaSource> &source) {
+ if (mInitCheck != OK) {
+ ALOGE("Init Check not OK, return");
+ return mInitCheck;
+ }
+
+ if (mSource != NULL) {
+ ALOGE("A source already exists, return");
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if ( !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_QCELP)) {
+ mFormat = AUDIO_FORMAT_QCELP;
+ } else if ( !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_EVRC)) {
+ mFormat = AUDIO_FORMAT_EVRC;
+ }
+ else {
+ return UNKNOWN_ERROR;
+ }
+
+ int32_t channelCount;
+ int32_t sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
+ CHECK_EQ(channelCount, 1);
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+ CHECK_EQ(sampleRate, 8000);
+
+ mSource = source;
+
+ return OK;
+}
+
+status_t ExtendedWriter::start(MetaData *params) {
+ if (mInitCheck != OK) {
+ ALOGE("Init Check not OK, return");
+ return mInitCheck;
+ }
+
+ if (mSource == NULL) {
+ ALOGE("NULL Source");
+ return UNKNOWN_ERROR;
+ }
+
+ if (mStarted && mPaused) {
+ mPaused = false;
+ mResumed = true;
+ return OK;
+ } else if (mStarted) {
+ ALOGE("Already startd, return");
+ return OK;
+ }
+
+ //space for header;
+ size_t headerSize = sizeof( struct QCPEVRCHeader );
+ uint8_t * header = (uint8_t *)malloc(headerSize);
+ memset( header, '?', headerSize);
+ fwrite( header, 1, headerSize, mFile );
+ mOffset += headerSize;
+ delete header;
+
+ status_t err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mReachedEOS = false;
+ mDone = false;
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+ pthread_attr_destroy(&attr);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t ExtendedWriter::pause() {
+ if (!mStarted) {
+ return OK;
+ }
+ mPaused = true;
+ return OK;
+}
+
+status_t ExtendedWriter::stop() {
+ if (!mStarted) {
+ return OK;
+ }
+
+ mDone = true;
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ status_t err = (status_t) dummy;
+ {
+ status_t status = mSource->stop();
+ if (err == OK &&
+ (status != OK && status != ERROR_END_OF_STREAM)) {
+ err = status;
+ }
+ }
+
+ mStarted = false;
+ return err;
+}
+
+bool ExtendedWriter::exceedsFileSizeLimit() {
+ if (mMaxFileSizeLimitBytes == 0) {
+ return false;
+ }
+ return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
+}
+
+bool ExtendedWriter::exceedsFileDurationLimit() {
+ if (mMaxFileDurationLimitUs == 0) {
+ return false;
+ }
+ return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
+}
+
+// static
+void *ExtendedWriter::ThreadWrapper(void *me) {
+ return (void *) static_cast<ExtendedWriter *>(me)->threadFunc();
+}
+
+status_t ExtendedWriter::threadFunc() {
+ mEstimatedDurationUs = 0;
+ mEstimatedSizeBytes = 0;
+ bool stoppedPrematurely = true;
+ int64_t previousPausedDurationUs = 0;
+ int64_t maxTimestampUs = 0;
+ status_t err = OK;
+
+ pid_t tid = gettid();
+ androidSetThreadPriority(tid, ANDROID_PRIORITY_AUDIO);
+ prctl(PR_SET_NAME, (unsigned long)"ExtendedWriter", 0, 0, 0);
+ while (!mDone) {
+ MediaBuffer *buffer;
+ err = mSource->read(&buffer);
+
+ if (err != OK) {
+ break;
+ }
+
+ if (mPaused) {
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+
+ mEstimatedSizeBytes += buffer->range_length();
+ if (exceedsFileSizeLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+ break;
+ }
+
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
+ if (timestampUs > mEstimatedDurationUs) {
+ mEstimatedDurationUs = timestampUs;
+ }
+ if (mResumed) {
+ previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000);
+ mResumed = false;
+ }
+ timestampUs -= previousPausedDurationUs;
+ ALOGV("time stamp: %lld, previous paused duration: %lld",
+ timestampUs, previousPausedDurationUs);
+ if (timestampUs > maxTimestampUs) {
+ maxTimestampUs = timestampUs;
+ }
+
+ if (exceedsFileDurationLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+ break;
+ }
+ ssize_t n = fwrite(
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ 1,
+ buffer->range_length(),
+ mFile);
+ mOffset += n;
+
+ if (n < (ssize_t)buffer->range_length()) {
+ buffer->release();
+ buffer = NULL;
+
+ break;
+ }
+
+ // XXX: How to tell it is stopped prematurely?
+ if (stoppedPrematurely) {
+ stoppedPrematurely = false;
+ }
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ if (stoppedPrematurely) {
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
+ }
+
+ if ( mFormat == AUDIO_FORMAT_QCELP ) {
+ writeQCPHeader( );
+ }
+ else if ( mFormat == AUDIO_FORMAT_EVRC ) {
+ writeEVRCHeader( );
+ }
+
+ fflush(mFile);
+ fclose(mFile);
+ mFile = NULL;
+ mReachedEOS = true;
+ if (err == ERROR_END_OF_STREAM || (err == -ETIMEDOUT)) {
+ return OK;
+ }
+ return err;
+}
+
+bool ExtendedWriter::reachedEOS() {
+ return mReachedEOS;
+}
+
+status_t ExtendedWriter::writeQCPHeader() {
+ /* Common part */
+ struct QCPEVRCHeader header = {
+ {'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, /* Riff */
+ {'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, /* Fmt */
+ {'v','r','a','t'}, 0, 0, 0, /* Vrat */
+ {'d','a','t','a'},0 /* Data */
+ };
+
+ fseeko(mFile, 0, SEEK_SET);
+ header.s_riff = (mOffset - 8);
+ header.data1 = (0x5E7F6D41);
+ header.data2 = (0xB115);
+ header.data3 = (0x11D0);
+ header.data4[0] = 0xBA;
+ header.data4[1] = 0x91;
+ header.data4[2] = 0x00;
+ header.data4[3] = 0x80;
+ header.data4[4] = 0x5F;
+ header.data4[5] = 0xB4;
+ header.data4[6] = 0xB9;
+ header.data4[7] = 0x7E;
+ header.ver = (0x0002);
+ memcpy(header.name, "Qcelp 13K", 9);
+ header.abps = (13000);
+ header.bytes_per_pkt = (35);
+ header.vr_num_of_rates = 5;
+ header.vr_bytes_per_pkt[0] = (0x0422);
+ header.vr_bytes_per_pkt[1] = (0x0310);
+ header.vr_bytes_per_pkt[2] = (0x0207);
+ header.vr_bytes_per_pkt[3] = (0x0103);
+ header.s_vrat = (0x00000008);
+ header.v_rate = (0x00000001);
+ header.size_in_pkts = (mOffset - sizeof( struct QCPEVRCHeader ))/ header.bytes_per_pkt;
+ header.s_data = mOffset - sizeof( struct QCPEVRCHeader );
+ fwrite( &header, 1, sizeof( struct QCPEVRCHeader ), mFile );
+ return OK;
+}
+
+status_t ExtendedWriter::writeEVRCHeader() {
+ /* Common part */
+ struct QCPEVRCHeader header = {
+ {'R', 'I', 'F', 'F'}, 0, {'Q', 'L', 'C', 'M'}, /* Riff */
+ {'f', 'm', 't', ' '}, 150, 1, 0, 0, 0, 0,{0}, 0, {0},0,0,160,8000,16,0,{0},{0},{0}, /* Fmt */
+ {'v','r','a','t'}, 0, 0, 0, /* Vrat */
+ {'d','a','t','a'},0 /* Data */
+ };
+
+ fseeko(mFile, 0, SEEK_SET);
+ header.s_riff = (mOffset - 8);
+ header.data1 = (0xe689d48d);
+ header.data2 = (0x9076);
+ header.data3 = (0x46b5);
+ header.data4[0] = 0x91;
+ header.data4[1] = 0xef;
+ header.data4[2] = 0x73;
+ header.data4[3] = 0x6a;
+ header.data4[4] = 0x51;
+ header.data4[5] = 0x00;
+ header.data4[6] = 0xce;
+ header.data4[7] = 0xb4;
+ header.ver = (0x0001);
+ memcpy(header.name, "TIA IS-127 Enhanced Variable Rate Codec, Speech Service Option 3", 64);
+ header.abps = (9600);
+ header.bytes_per_pkt = (23);
+ header.vr_num_of_rates = 4;
+ header.vr_bytes_per_pkt[0] = (0x0416);
+ header.vr_bytes_per_pkt[1] = (0x030a);
+ header.vr_bytes_per_pkt[2] = (0x0200);
+ header.vr_bytes_per_pkt[3] = (0x0102);
+ header.s_vrat = (0x00000008);
+ header.v_rate = (0x00000001);
+ header.size_in_pkts = (mOffset - sizeof( struct QCPEVRCHeader )) / header.bytes_per_pkt;
+ header.s_data = mOffset - sizeof( struct QCPEVRCHeader );
+ fwrite( &header, 1, sizeof( struct QCPEVRCHeader ), mFile );
+ return OK;
+}
+
+
+} // namespace android
diff --git a/media/libstagefright/LPAPlayerALSA.cpp b/media/libstagefright/LPAPlayerALSA.cpp
new file mode 100644
index 0000000..c7b490f
--- /dev/null
+++ b/media/libstagefright/LPAPlayerALSA.cpp
@@ -0,0 +1,750 @@
+/*
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ * 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_NDDEBUG 0
+//#define LOG_NDEBUG 0
+#define LOG_TAG "LPAPlayerALSA"
+
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <signal.h>
+#include <sys/prctl.h>
+#include <binder/IPCThreadState.h>
+#include <media/AudioTrack.h>
+
+#include <media/stagefright/LPAPlayer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include "include/AwesomePlayer.h"
+
+static const char mName[] = "LPAPlayer";
+
+#define MEM_METADATA_SIZE 64
+#define MEM_BUFFER_SIZE ((256*1024) - MEM_METADATA_SIZE)
+#define MEM_BUFFER_COUNT 4
+#define LPA_BUFFER_TIME 1500000
+
+namespace android {
+int LPAPlayer::mObjectsAlive = 0;
+
+LPAPlayer::LPAPlayer(
+ const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck,
+ AwesomePlayer *observer)
+:AudioPlayer(audioSink, 0, observer),
+mPositionTimeMediaUs(-1),
+mPositionTimeRealUs(-1),
+mInternalSeeking(false),
+mStarted(false),
+mSampleRate(0),
+mLatencyUs(0),
+mFrameSize(0),
+mNumFramesPlayed(0),
+mNumFramesPlayedSysTimeUs(0),
+mInputBuffer(NULL),
+mSeeking(false),
+mReachedEOS(false),
+mReachedOutputEOS(false),
+mFinalStatus(OK),
+mSeekTimeUs(0),
+mPauseTime(0),
+mIsFirstBuffer(false),
+mFirstBufferResult(OK),
+mFirstBuffer(NULL),
+mAudioSink(audioSink),
+mObserver(observer) {
+ ALOGV("LPAPlayer::LPAPlayer() ctor");
+ mObjectsAlive++;
+ mNumOutputChannels =0;
+ mNumInputChannels = 0;
+ mPaused = false;
+ /* Initialize Suspend/Resume related variables */
+ mQueue.start();
+ mQueueStarted = true;
+ mPauseEvent = new TimedEvent(this, &LPAPlayer::onPauseTimeOut);
+ mPauseEventPending = false;
+ ALOGV("Registering client with AudioFlinger");
+
+ mIsAudioRouted = false;
+
+ initCheck = true;
+
+}
+
+LPAPlayer::~LPAPlayer() {
+ ALOGV("LPAPlayer::~LPAPlayer()");
+ if (mQueueStarted) {
+ mQueue.stop();
+ }
+
+ reset();
+
+ mObjectsAlive--;
+}
+
+void LPAPlayer::setSource(const sp<MediaSource> &source) {
+ CHECK(mSource == NULL);
+ ALOGV("Setting source from LPA Player");
+ mSource = source;
+}
+
+status_t LPAPlayer::start(bool sourceAlreadyStarted) {
+ CHECK(!mStarted);
+ CHECK(mSource != NULL);
+
+ ALOGV("start: sourceAlreadyStarted %d", sourceAlreadyStarted);
+ //Check if the source is started, start it
+ status_t err;
+ if (!sourceAlreadyStarted) {
+ err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ //Create decoder thread and initialize all the
+ //mutexes and coditional variables
+ createThreads();
+ ALOGV("All Threads Created.");
+
+ // We allow an optional INFO_FORMAT_CHANGED at the very beginning
+ // of playback, if there is one, getFormat below will retrieve the
+ // updated format, if there isn't, we'll stash away the valid buffer
+ // of data to be used on the first audio callback.
+
+ CHECK(mFirstBuffer == NULL);
+
+ MediaSource::ReadOptions options;
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs);
+ mSeeking = false;
+ }
+
+ mFirstBufferResult = mSource->read(&mFirstBuffer, &options);
+ if (mFirstBufferResult == INFO_FORMAT_CHANGED) {
+ ALOGV("INFO_FORMAT_CHANGED!!!");
+ CHECK(mFirstBuffer == NULL);
+ mFirstBufferResult = OK;
+ mIsFirstBuffer = false;
+ } else {
+ mIsFirstBuffer = true;
+ }
+
+ sp<MetaData> format = mSource->getFormat();
+ const char *mime;
+
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ CHECK(success);
+ CHECK(!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_RAW));
+
+ success = format->findInt32(kKeySampleRate, &mSampleRate);
+ CHECK(success);
+
+ success = format->findInt32(kKeyChannelCount, &mNumInputChannels);
+ CHECK(success);
+
+ // Always produce stereo output
+ mNumOutputChannels = 2;
+
+ if(!format->findInt32(kKeyChannelMask, &mChannelMask)) {
+ // log only when there's a risk of ambiguity of channel mask selection
+ ALOGI_IF(mNumInputChannels > 2,
+ "source format didn't specify channel mask, using (%d) channel order", mNumInputChannels);
+ mChannelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
+ }
+ audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_LPA |
+ AUDIO_OUTPUT_FLAG_DIRECT);
+ ALOGV("mAudiosink->open() mSampleRate %d, numOutputChannels %d, mChannelMask %d, flags %d",mSampleRate,
+ mNumOutputChannels, mChannelMask, flags);
+ err = mAudioSink->open(
+ mSampleRate, mNumOutputChannels, mChannelMask, AUDIO_FORMAT_PCM_16_BIT,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &LPAPlayer::AudioSinkCallback,
+ this,
+ flags,
+ NULL);
+
+ if (err != OK) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (!sourceAlreadyStarted) {
+ mSource->stop();
+ }
+
+ ALOGE("Opening a routing session failed");
+ return err;
+ }
+
+ mIsAudioRouted = true;
+ mStarted = true;
+ mAudioSink->start();
+ ALOGV("Waking up decoder thread");
+ mLock.lock();
+ mDecoderCv.signal();
+ mLock.unlock();
+
+ return OK;
+}
+
+status_t LPAPlayer::seekTo(int64_t time_us) {
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("seekTo: time_us %lld", time_us);
+
+ if (seekTooClose(time_us)) {
+ mLock.unlock();
+ mObserver->postAudioSeekComplete();
+ mLock.lock();
+ return OK;
+ }
+
+ mSeeking = true;
+ mSeekTimeUs = time_us;
+ mPauseTime = mSeekTimeUs;
+ ALOGV("In seekTo(), mSeekTimeUs %lld",mSeekTimeUs);
+
+ if (mIsAudioRouted) {
+ mAudioSink->flush();
+ }
+
+ if (mReachedEOS) {
+ mReachedEOS = false;
+ mReachedOutputEOS = false;
+ if(mPaused == false) {
+ ALOGV("Going to signal decoder thread since playback is already going on ");
+ mDecoderCv.signal();
+ ALOGV("Signalled extractor thread.");
+ }
+ }
+ ALOGV("seek done.");
+ return OK;
+}
+
+void LPAPlayer::pause(bool playPendingSamples) {
+ CHECK(mStarted);
+ if (mPaused) {
+ return;
+ }
+ ALOGV("pause: playPendingSamples %d", playPendingSamples);
+ mPaused = true;
+ if (!mPauseEventPending) {
+ ALOGV("Posting an event for Pause timeout");
+ mQueue.postEventWithDelay(mPauseEvent, LPA_PAUSE_TIMEOUT_USEC);
+ mPauseEventPending = true;
+ }
+ mPauseTime = mSeekTimeUs + getTimeStamp();
+
+ if (mAudioSink.get() != NULL) {
+ ALOGV("AudioSink pause");
+ mAudioSink->pause();
+ }
+}
+
+status_t LPAPlayer::resume() {
+ ALOGV("resume: mPaused %d",mPaused);
+ status_t err = NO_ERROR;
+ Mutex::Autolock autoLock(mLock);
+ if ( mPaused) {
+ CHECK(mStarted);
+ if(mPauseEventPending) {
+ ALOGV("Resume(): Cancelling the puaseTimeout event");
+ mPauseEventPending = false;
+ mQueue.cancelEvent(mPauseEvent->eventID());
+ }
+
+ if (!mIsAudioRouted) {
+ audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_LPA |
+ AUDIO_OUTPUT_FLAG_DIRECT);
+ err = mAudioSink->open(
+ mSampleRate, mNumOutputChannels, mChannelMask, AUDIO_FORMAT_PCM_16_BIT,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &LPAPlayer::AudioSinkCallback,
+ this,
+ flags,
+ NULL);
+ if (err != NO_ERROR) {
+ ALOGE("Audio sink open failed.");
+ }
+ mIsAudioRouted = true;
+ }
+ mPaused = false;
+ mAudioSink->start();
+ mDecoderCv.signal();
+
+ }
+ return err;
+}
+
+//static
+size_t LPAPlayer::AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *buffer, size_t size, void *cookie,
+ MediaPlayerBase::AudioSink::cb_event_t event) {
+ if (buffer == NULL && size == AudioTrack::EVENT_UNDERRUN) {
+ LPAPlayer *me = (LPAPlayer *)cookie;
+ if(me->mReachedEOS == true) {
+ //in the case of seek all these flags will be reset
+ me->mReachedOutputEOS = true;
+ ALOGV("postAudioEOS mSeeking %d", me->mSeeking);
+ me->mObserver->postAudioEOS(0);
+ }else {
+ ALOGV("postAudioEOS ignored since %d", me->mSeeking);
+ }
+ }
+ return 1;
+}
+
+void LPAPlayer::reset() {
+ ALOGD("Reset");
+
+ Mutex::Autolock _l(mLock); //to sync w/ onpausetimeout
+
+ //cancel any pending onpause timeout events
+ //doesnt matter if the event is really present or not
+ mPauseEventPending = false;
+ mQueue.cancelEvent(mPauseEvent->eventID());
+
+ // Close the audiosink after all the threads exited to make sure
+ mReachedEOS = true;
+
+ // make sure Decoder thread has exited
+ ALOGD("Closing all the threads");
+ requestAndWaitForDecoderThreadExit_l();
+
+ ALOGD("Close the Sink");
+ if (mIsAudioRouted) {
+ mAudioSink->stop();
+ mAudioSink->close();
+ mAudioSink.clear();
+ mIsAudioRouted = false;
+ }
+ // Make sure to release any buffer we hold onto so that the
+ // source is able to stop().
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (mInputBuffer != NULL) {
+ ALOGV("AudioPlayer releasing input buffer.");
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ mSource->stop();
+
+ // The following hack is necessary to ensure that the OMX
+ // component is completely released by the time we may try
+ // to instantiate it again.
+ wp<MediaSource> tmp = mSource;
+ mSource.clear();
+ while (tmp.promote() != NULL) {
+ usleep(1000);
+ }
+
+ mPositionTimeMediaUs = -1;
+ mPositionTimeRealUs = -1;
+ mSeeking = false;
+ mReachedEOS = false;
+ mReachedOutputEOS = false;
+ mFinalStatus = OK;
+ mStarted = false;
+}
+
+
+bool LPAPlayer::isSeeking() {
+ Mutex::Autolock autoLock(mLock);
+ return mSeeking;
+}
+
+bool LPAPlayer::reachedEOS(status_t *finalStatus) {
+ *finalStatus = OK;
+ Mutex::Autolock autoLock(mLock);
+ *finalStatus = mFinalStatus;
+ return mReachedOutputEOS;
+}
+
+
+void *LPAPlayer::decoderThreadWrapper(void *me) {
+ static_cast<LPAPlayer *>(me)->decoderThreadEntry();
+ return NULL;
+}
+
+
+void LPAPlayer::decoderThreadEntry() {
+
+ mLock.lock();
+ pid_t tid = gettid();
+ androidSetThreadPriority(tid, ANDROID_PRIORITY_AUDIO);
+ prctl(PR_SET_NAME, (unsigned long)"LPA DecodeThread", 0, 0, 0);
+
+ ALOGV("mDecoderThreadEntry wait for signal \n");
+ if (!mStarted) {
+ mDecoderCv.wait(mLock);
+ }
+ ALOGV("decoderThreadEntry ready to work \n");
+ mLock.unlock();
+ if (mKillDecoderThread) {
+ return;
+ }
+ void* local_buf = malloc(MEM_BUFFER_SIZE);
+ if(local_buf == (void*) NULL) {
+ mKillDecoderThread = true;
+ ALOGE("Malloc failed");
+ return;
+ }
+ int *lptr = ((int*)local_buf);
+ int bytesWritten = 0;
+
+ if (!local_buf) {
+ ALOGE("Failed to allocate temporary buffer for mDecoderThread");
+ return;
+ }
+
+ bool lSeeking = false;
+ bool lPaused = false;
+
+ while (!mKillDecoderThread) {
+
+ if (mReachedEOS || mPaused || !mIsAudioRouted) {
+ ALOGV("Going to sleep before write since "
+ "mReachedEOS %d, mPaused %d, mIsAudioRouted %d",
+ mReachedEOS, mPaused, mIsAudioRouted);
+ mDecoderMutex.lock();
+ mDecoderCv.wait(mDecoderMutex);
+ mDecoderMutex.unlock();
+ ALOGV("Woke up from sleep before write since "
+ "mReachedEOS %d, mPaused %d, mIsAudioRouted %d",
+ mReachedEOS, mPaused, mIsAudioRouted);
+ continue;
+ }
+
+ ALOGV("FillBuffer: MemBuffer size %d", MEM_BUFFER_SIZE);
+ ALOGV("Fillbuffer started");
+ if (mNumInputChannels == 1) {
+ bytesWritten = fillBuffer(local_buf, MEM_BUFFER_SIZE/2);
+ CHECK(bytesWritten <= MEM_BUFFER_SIZE/2);
+
+ convertMonoToStereo((int16_t*)local_buf, bytesWritten);
+ bytesWritten *= 2;
+ } else {
+ bytesWritten = fillBuffer(local_buf, MEM_BUFFER_SIZE);
+ CHECK(bytesWritten <= MEM_BUFFER_SIZE);
+ }
+
+ ALOGV("FillBuffer completed bytesToWrite %d", bytesWritten);
+ if(!mKillDecoderThread) {
+ mLock.lock();
+ lPaused = mPaused;
+ mLock.unlock();
+
+ if(lPaused == true) {
+ //write only if player is not in paused state. Sleep on lock
+ // resume is called
+ ALOGV("Going to sleep in decodethreadiwrite since sink is paused");
+ mDecoderMutex.lock();
+ mDecoderCv.wait(mDecoderMutex);
+ ALOGV("Going to unlock n decodethreadwrite since sink "
+ "resumed mPaused %d, mIsAudioRouted %d, mReachedEOS %d",
+ mPaused, mIsAudioRouted, mReachedEOS);
+ mDecoderMutex.unlock();
+ }
+ mLock.lock();
+ lSeeking = mSeeking||mInternalSeeking;
+ mLock.unlock();
+
+ if(lSeeking == false && (mKillDecoderThread == false)){
+ //if we are seeking, ignore write, otherwise write
+ ALOGV("Fillbuffer before seeling flag %d", mSeeking);
+ int lWrittenBytes = mAudioSink->write(local_buf, bytesWritten);
+ ALOGV("Fillbuffer after write, written bytes %d and seek flag %d", lWrittenBytes, mSeeking);
+ } else {
+ ALOGV("Fillbuffer ignored since we seeked after fillBuffer was set %d", mSeeking);
+ }
+ }
+ }
+
+ free(local_buf);
+
+ //TODO: Call fillbuffer with different size and write to mAudioSink()
+}
+
+void LPAPlayer::createThreads() {
+
+ // Create 4 threads Effect, decoder, event and A2dp
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mKillDecoderThread = false;
+
+ mDecoderThreadAlive = true;
+
+ ALOGV("Creating decoder Thread");
+ pthread_create(&mDecoderThread, &attr, decoderThreadWrapper, this);
+
+ pthread_attr_destroy(&attr);
+}
+
+size_t LPAPlayer::fillBuffer(void *data, size_t size) {
+
+ if (mReachedEOS) {
+ return 0;
+ }
+
+ if ((data == (void*) NULL) || size > MEM_BUFFER_SIZE) {
+ ALOGE("fillBuffer given wrong buffer");
+ return 0;
+ }
+
+ bool postSeekComplete = false;
+
+ size_t size_done = 0;
+ size_t size_remaining = size;
+ ALOGV("fillBuffer: Clearing seek flag in fill buffer");
+
+ while (size_remaining > 0) {
+ MediaSource::ReadOptions options;
+
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if(mSeeking) {
+ mInternalSeeking = false;
+ }
+
+ if (mSeeking || mInternalSeeking) {
+ if (mIsFirstBuffer) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+ mIsFirstBuffer = false;
+ }
+
+ options.setSeekTo(mSeekTimeUs);
+
+ if (mInputBuffer != NULL) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ // This is to ignore the data already filled in the output buffer
+ size_done = 0;
+ size_remaining = size;
+
+ mSeeking = false;
+ if (mObserver && !mInternalSeeking) {
+ ALOGV("fillBuffer: Posting audio seek complete event");
+ postSeekComplete = true;
+ }
+ mInternalSeeking = false;
+ ALOGV("fillBuffer: Setting seek flag in fill buffer");
+ }
+ }
+
+ if (mInputBuffer == NULL) {
+ status_t err;
+
+ if (mIsFirstBuffer) {
+ mInputBuffer = mFirstBuffer;
+ mFirstBuffer = NULL;
+ err = mFirstBufferResult;
+
+ mIsFirstBuffer = false;
+ } else {
+ err = mSource->read(&mInputBuffer, &options);
+ }
+
+ CHECK((err == OK && mInputBuffer != NULL)
+ || (err != OK && mInputBuffer == NULL));
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (err != OK) {
+ ALOGD("fill buffer - reached eos true");
+ mReachedEOS = true;
+ mFinalStatus = err;
+ break;
+ }
+ }
+
+ CHECK(mInputBuffer->meta_data()->findInt64(
+ kKeyTime, &mPositionTimeMediaUs));
+ mFrameSize = mAudioSink->frameSize();
+ mPositionTimeRealUs =
+ ((mNumFramesPlayed + size_done / mFrameSize) * 1000000)
+ / mSampleRate;
+ }
+ if (mInputBuffer->range_length() == 0) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+
+ continue;
+ }
+
+ size_t copy = size_remaining;
+ if (copy > mInputBuffer->range_length()) {
+ copy = mInputBuffer->range_length();
+ } //is size_remaining < range_length impossible?
+
+ 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;
+ }
+
+ if (postSeekComplete) {
+ mObserver->postAudioSeekComplete();
+ }
+
+ return size_done;
+}
+
+int64_t LPAPlayer::getRealTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+ return getRealTimeUsLocked();
+}
+
+
+int64_t LPAPlayer::getRealTimeUsLocked(){
+ //Used for AV sync: irrelevant API for LPA.
+ return 0;
+}
+
+int64_t LPAPlayer::getTimeStamp() {
+ uint64_t timestamp = 0;
+ mAudioSink->getTimeStamp(×tamp);
+ ALOGV("timestamp %lld ", timestamp);
+ return timestamp;
+}
+
+int64_t LPAPlayer::getMediaTimeUs_l() {
+ if (mPaused) {
+ return mPauseTime;
+ } else {
+ return (mSeekTimeUs + getTimeStamp());
+ }
+}
+
+int64_t LPAPlayer::getMediaTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("getMediaTimeUs() mPaused %d mSeekTimeUs %lld mPauseTime %lld", mPaused, mSeekTimeUs, mPauseTime);
+ return getMediaTimeUs_l();
+}
+
+bool LPAPlayer::getMediaTimeMapping(
+ int64_t *realtime_us, int64_t *mediatime_us) {
+ ALOGE("getMediaTimeMapping is invalid for LPA Player");
+ *realtime_us = -1;
+ *mediatime_us = -1;
+ return false;
+}
+
+bool LPAPlayer::seekTooClose(int64_t time_us) {
+ int64_t t1 = getMediaTimeUs_l();
+ /*
+ * empirical
+ * -----------
+ * This constant signifies how much data (in Us) has been rendered by the
+ * DSP in the interval between the moment flush is issued on AudioSink to
+ * after ioctl(PAUSE) returns in Audio HAL. (flush triggers an implicit
+ * pause in audio HAL)
+ *
+ */
+ const int64_t kDeltaUs = 60000LL; /* 60-70ms on msm8974, must be measured for other targets */
+ t1 += kDeltaUs;
+ return (time_us > t1) && ((time_us - t1) <= LPA_BUFFER_TIME);
+}
+
+//lock taken in reset()
+void LPAPlayer::requestAndWaitForDecoderThreadExit_l() {
+
+ if (!mDecoderThreadAlive)
+ return;
+ mKillDecoderThread = true;
+
+ /* Flush the audio sink to unblock the decoder thread
+ if any write to audio HAL is blocked */
+ if (!mReachedOutputEOS && mIsAudioRouted)
+ mAudioSink->flush();
+
+ mDecoderCv.signal();
+ mLock.unlock();
+ pthread_join(mDecoderThread,NULL);
+ mLock.lock();
+ ALOGD("decoder thread killed");
+
+}
+
+void LPAPlayer::onPauseTimeOut() {
+ ALOGV("onPauseTimeOut");
+ Mutex::Autolock autoLock(mLock);
+ if (!mPauseEventPending) {
+ return;
+ }
+ mPauseEventPending = false;
+ // 1.) Set seek flags
+ mReachedEOS = false;
+ mReachedOutputEOS = false;
+ if(mSeeking == false) {
+ mSeekTimeUs += getTimeStamp();
+ mInternalSeeking = true;
+ } else {
+ //do not update seek time if user has already seeked
+ // to a new position
+ // also seek has to be posted back to player,
+ // so do not set mInternalSeeking flag
+ ALOGV("do not update seek time %lld ", mSeekTimeUs);
+ }
+ ALOGV("newseek time = %lld ", mSeekTimeUs);
+ // 2.) Close routing Session
+ mAudioSink->flush();
+ mAudioSink->stop();
+ mAudioSink->close();
+ mIsAudioRouted = false;
+}
+
+//dup each mono frame
+void LPAPlayer::convertMonoToStereo(int16_t *data, size_t size)
+{
+ int i =0;
+ int16_t *start_pointer = data;
+ int monoFrameCount = (size) / (sizeof(int16_t));
+
+ for (i = monoFrameCount; i > 0 ; i--) {
+ int16_t temp_sample = *(start_pointer + i - 1);
+ *(start_pointer + (i*2) - 1) = temp_sample;
+ *(start_pointer + (i*2) - 2) = temp_sample;
+ }
+}
+
+} //namespace android
diff --git a/media/libstagefright/MPEG4Extractor.cpp b/media/libstagefright/MPEG4Extractor.cpp
index 8cd8830..fcb47d1 100644
--- a/media/libstagefright/MPEG4Extractor.cpp
+++ b/media/libstagefright/MPEG4Extractor.cpp
@@ -37,6 +37,10 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+#include <QCMediaDefs.h>
+#include "include/ExtendedUtils.h"
+#endif
namespace android {
@@ -186,6 +190,11 @@
mCachedOffset(0),
mCachedSize(0),
mCache(NULL) {
+ #ifdef DOLBY_UDC
+ #if defined (DEBUG_LOG_DDP_DECODER_EXTRA)
+ ALOGE("@DDP MPEG4DataSource::MPEG4DataSource");
+ #endif
+ #endif //DOLBY_UDC
}
MPEG4DataSource::~MPEG4DataSource() {
@@ -293,9 +302,16 @@
}
static const char *FourCC2MIME(uint32_t fourcc) {
+ #ifdef DOLBY_UDC
+ #if defined (DEBUG_LOG_DDP_DECODER_EXTRA)
+ ALOGE("@DDP FourCC2MIME");
+ #endif
+ #endif //DOLBY_UDC
switch (fourcc) {
case FOURCC('m', 'p', '4', 'a'):
return MEDIA_MIMETYPE_AUDIO_AAC;
+ case FOURCC('.', 'm', 'p', '3'):
+ return MEDIA_MIMETYPE_AUDIO_MPEG;
case FOURCC('s', 'a', 'm', 'r'):
return MEDIA_MIMETYPE_AUDIO_AMR_NB;
@@ -314,8 +330,29 @@
case FOURCC('a', 'v', 'c', '1'):
return MEDIA_MIMETYPE_VIDEO_AVC;
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+ case FOURCC('s', 'q', 'c', 'p'):
+ return MEDIA_MIMETYPE_AUDIO_QCELP;
+ case FOURCC('s', 'e', 'v', 'c'):
+ return MEDIA_MIMETYPE_AUDIO_EVRC;
+ case FOURCC('d', 't', 's', 'c'):
+ case FOURCC('d', 't', 's', 'h'):
+ case FOURCC('d', 't', 's', 'l'):
+ return MEDIA_MIMETYPE_AUDIO_DTS;
+ case FOURCC('d', 't', 's', 'e'):
+ return MEDIA_MIMETYPE_AUDIO_DTS_LBR;
+ case FOURCC('a', 'c', '-', '3'):
+ return MEDIA_MIMETYPE_AUDIO_AC3;
+ case FOURCC('e', 'c', '-', '3'):
+ return MEDIA_MIMETYPE_AUDIO_EAC3;
+#endif
default:
CHECK(!"should not be here.");
+ #ifdef DOLBY_UDC
+ #if defined (DEBUG_LOG_DDP_DECODER_EXTRA)
+ ALOGE("@DDP FourCC2Mime default (not found)");
+ #endif
+ #endif //DOLBY_UDC
return NULL;
}
}
@@ -347,6 +384,11 @@
mFileMetaData(new MetaData),
mFirstSINF(NULL),
mIsDrm(false) {
+ #ifdef DOLBY_UDC
+ #if defined (DEBUG_LOG_DDP_DECODER_EXTRA)
+ ALOGE("@DDP MPEG4Extractor::MPEG4Extractor");
+ #endif
+ #endif //DOLBY_UDC
}
MPEG4Extractor::~MPEG4Extractor() {
@@ -1220,8 +1262,17 @@
case FOURCC('m', 'p', '4', 'a'):
case FOURCC('e', 'n', 'c', 'a'):
+ case FOURCC('.', 'm', 'p', '3'):
case FOURCC('s', 'a', 'm', 'r'):
case FOURCC('s', 'a', 'w', 'b'):
+ case FOURCC('s', 'e', 'v', 'c'):
+ case FOURCC('s', 'q', 'c', 'p'):
+ case FOURCC('d', 't', 's', 'c'):
+ case FOURCC('d', 't', 's', 'h'):
+ case FOURCC('d', 't', 's', 'l'):
+ case FOURCC('d', 't', 's', 'e'):
+ case FOURCC('a', 'c', '-', '3'):
+ case FOURCC('e', 'c', '-', '3'):
{
uint8_t buffer[8 + 20];
if (chunk_data_size < (ssize_t)sizeof(buffer)) {
@@ -1245,13 +1296,23 @@
mLastTrack->meta->setCString(kKeyMIMEType, FourCC2MIME(chunk_type));
AdjustChannelsAndRate(chunk_type, &num_channels, &sample_rate);
}
+ #ifdef DOLBY_UDC
+ #if defined (DEBUG_LOG_DDP_DECODER_EXTRA)
+ ALOGE("@DDP FourCC:'%s'", FourCC2MIME(chunk_type));
+ #endif
+ #endif //DOLBY_UDC
ALOGV("*** coding='%s' %d channels, size %d, rate %d\n",
chunk, num_channels, sample_size, sample_rate);
mLastTrack->meta->setInt32(kKeyChannelCount, num_channels);
mLastTrack->meta->setInt32(kKeySampleRate, sample_rate);
off64_t stop_offset = *offset + chunk_size;
+ if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, FourCC2MIME(chunk_type)) ||
+ !strcasecmp(MEDIA_MIMETYPE_AUDIO_AMR_WB, FourCC2MIME(chunk_type))) {
+ *offset = stop_offset;
+ } else {
*offset = data_offset + sizeof(buffer);
+ }
while (*offset < stop_offset) {
status_t err = parseChunk(offset, depth + 1);
if (err != OK) {
@@ -1525,6 +1586,14 @@
break;
}
+ case FOURCC('d', 'd', 't', 's'):
+ case FOURCC('d', 'a', 'c', '3'):
+ case FOURCC('d', 'e', 'c', '3'):
+ {
+ ALOGV("ddts/dac3/dec3 pass from mpeg4 extractor");
+ *offset += chunk_size;
+ break;
+ }
case FOURCC('a', 'v', 'c', 'C'):
{
sp<ABuffer> buffer = new ABuffer(chunk_data_size);
@@ -2233,18 +2302,24 @@
return ERROR_MALFORMED;
}
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+ if (objectTypeIndication == 0xA0) {
+ mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_EVRC);
+ return OK;
+ }
if (objectTypeIndication == 0xe1) {
// This isn't MPEG4 audio at all, it's QCELP 14k...
mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_QCELP);
return OK;
}
+#endif
- if (objectTypeIndication == 0x6b) {
- // The media subtype is MP3 audio
+ if (objectTypeIndication == 0x6b
+ || objectTypeIndication == 0x69) {
// Our software MP3 audio decoder may not be able to handle
- // packetized MP3 audio; for now, lets just return ERROR_UNSUPPORTED
- ALOGE("MP3 track in MP4/3GPP file is not supported");
- return ERROR_UNSUPPORTED;
+ mLastTrack->meta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ ALOGD("objectTypeIndication:0x%x, set mimetype to mpeg ",objectTypeIndication);
+ return OK;
}
const uint8_t *csd;
@@ -2372,6 +2447,11 @@
mBuffer(NULL),
mWantsNALFragments(false),
mSrcBuffer(NULL) {
+ #ifdef DOLBY_UDC
+ #if defined (DEBUG_LOG_DDP_DECODER_EXTRA)
+ ALOGE("@DDP MPEG4Source::MPEG4Source");
+ #endif
+ #endif //DOLBY_UDC
mFormat->findInt32(kKeyCryptoMode, &mCryptoMode);
mDefaultIVSize = 0;
@@ -3150,7 +3230,9 @@
return ERROR_IO;
}
-
+#ifdef ENABLE_QC_AV_ENHANCEMENTS
+ ExtendedUtils::helper_mpeg4extractor_checkAC3EAC3(mBuffer, mFormat, size);
+#endif
CHECK(mBuffer != NULL);
mBuffer->set_range(0, size);
mBuffer->meta_data()->clear();
@@ -3582,7 +3664,8 @@
return false;
}
- if (!memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
+ if (!memcmp(header, "ftyp3g2a", 8) || !memcmp(header, "ftyp3g2b", 8) || !memcmp(header, "ftyp3g2c", 8)
+ || !memcmp(header, "ftyp3gp", 7) || !memcmp(header, "ftypmp42", 8)
|| !memcmp(header, "ftyp3gr6", 8) || !memcmp(header, "ftyp3gs6", 8)
|| !memcmp(header, "ftyp3ge6", 8) || !memcmp(header, "ftyp3gg6", 8)
|| !memcmp(header, "ftypisom", 8) || !memcmp(header, "ftypM4V ", 8)
diff --git a/media/libstagefright/MPEG4Writer.cpp b/media/libstagefright/MPEG4Writer.cpp
index 9569fc1..2b1227c 100644
--- a/media/libstagefright/MPEG4Writer.cpp
+++ b/media/libstagefright/MPEG4Writer.cpp
@@ -40,6 +40,8 @@
#include "include/ESDS.h"
+#include "include/ExtendedUtils.h"
+
namespace android {
static const int64_t kMinStreamableFileSizeInBytes = 5 * 1024 * 1024;
@@ -510,10 +512,10 @@
// If the estimation is wrong, we will pay the price of wasting
// some reserved space. This should not happen so often statistically.
+ int32_t factor = mUse32BitOffset? 1: 2;
static const int64_t MIN_MOOV_BOX_SIZE = 3 * 1024; // 3 KB
static const int64_t MAX_MOOV_BOX_SIZE = (180 * 3000000 * 6LL / 8000);
int64_t size = MIN_MOOV_BOX_SIZE;
- int32_t factor = mUse32BitOffset? 1: 2;
// Max file size limit is set
if (mMaxFileSizeLimitBytes != 0 && mIsFileSizeLimitExplicitlyRequested) {
@@ -2264,6 +2266,10 @@
meta_data->findInt32(kKeyIsSyncFrame, &isSync);
CHECK(meta_data->findInt64(kKeyTime, ×tampUs));
+ if (!mIsAudio) {
+ ExtendedUtils::HFR::reCalculateTimeStamp(mMeta, timestampUs);
+ }
+
////////////////////////////////////////////////////////////////////////////////
if (mStszTableEntries->count() == 0) {
mFirstSampleTimeRealUs = systemTime() / 1000;
@@ -2293,10 +2299,12 @@
*/
int64_t decodingTimeUs;
CHECK(meta_data->findInt64(kKeyDecodingTime, &decodingTimeUs));
+ ExtendedUtils::HFR::reCalculateTimeStamp(mMeta, decodingTimeUs);
+
decodingTimeUs -= previousPausedDurationUs;
cttsOffsetTimeUs =
- timestampUs + kMaxCttsOffsetTimeUs - decodingTimeUs;
- CHECK_GE(cttsOffsetTimeUs, 0ll);
+ timestampUs - decodingTimeUs;
+ CHECK_GE(kMaxCttsOffsetTimeUs, decodingTimeUs - timestampUs);
timestampUs = decodingTimeUs;
ALOGV("decoding time: %lld and ctts offset time: %lld",
timestampUs, cttsOffsetTimeUs);
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
old mode 100644
new mode 100755
index e0686be..5a1d341
--- a/media/libstagefright/MediaCodec.cpp
+++ b/media/libstagefright/MediaCodec.cpp
@@ -35,6 +35,7 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
+#include <media/stagefright/ExtendedCodec.h>
#include <media/stagefright/NativeWindowWrapper.h>
#include "include/avc_utils.h"
@@ -1415,6 +1416,19 @@
++i;
}
+ sp<ABuffer> extendedCSD = ExtendedCodec::getRawCodecSpecificData(format);
+ if (extendedCSD != NULL) {
+ ALOGV("pushing extended CSD of size %d", extendedCSD->size());
+ mCSD.push_back(extendedCSD);
+ }
+
+ sp<ABuffer> aacCSD = ExtendedCodec::getAacCodecSpecificData(format);
+ if (aacCSD != NULL) {
+ ALOGV("pushing AAC CSD of size %d", aacCSD->size());
+ mCSD.push_back(aacCSD);
+ }
+
+
ALOGV("Found %u pieces of codec specific data.", mCSD.size());
}
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
index 6248e90..b8499f9 100644
--- a/media/libstagefright/MediaCodecList.cpp
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -27,6 +27,7 @@
#include <utils/threads.h>
#include <libexpat/expat.h>
+#include "include/ExtendedUtils.h"
namespace android {
@@ -62,8 +63,15 @@
addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
+
addMediaCodec(
- false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
+ false /* encoder */, "OMX.google.raw.decoder", "audio/raw");
+
+ Vector<AString> QcomAACQuirks;
+ QcomAACQuirks.push(AString("requires-allocate-on-input-ports"));
+ QcomAACQuirks.push(AString("requires-allocate-on-output-ports"));
+ ExtendedUtils::helper_addMediaCodec(mCodecInfos, mTypes, false, "OMX.qcom.audio.decoder.multiaac",
+ "audio/mp4a-latm", ExtendedUtils::helper_getCodecSpecificQuirks(mCodecQuirks, QcomAACQuirks));
}
#if 0
diff --git a/media/libstagefright/MediaExtractor.cpp b/media/libstagefright/MediaExtractor.cpp
old mode 100644
new mode 100755
index 9ab6611..7c66429
--- a/media/libstagefright/MediaExtractor.cpp
+++ b/media/libstagefright/MediaExtractor.cpp
@@ -39,6 +39,8 @@
#include <media/stagefright/MetaData.h>
#include <utils/String8.h>
+#include "include/ExtendedUtils.h"
+
namespace android {
sp<MetaData> MediaExtractor::getMetaData() {
@@ -126,7 +128,7 @@
}
}
- return ret;
+ return ExtendedUtils::MediaExtractor_CreateIfNeeded(ret, source, mime);
}
} // namespace android
diff --git a/media/libstagefright/NOTICE b/media/libstagefright/NOTICE
index c5b1efa..1c3f344 100644
--- a/media/libstagefright/NOTICE
+++ b/media/libstagefright/NOTICE
@@ -188,3 +188,31 @@
END OF TERMS AND CONDITIONS
+Copyright (c) 2013, The Linux Foundation. All rights reserved.
+
+Redistribution and use in source and binary forms, with or without
+modification, are permitted provided that the following conditions are
+met:
+ * Redistributions of source code must retain the above copyright
+ notice, this list of conditions and the following disclaimer.
+ * Redistributions in binary form must reproduce the above
+ copyright notice, this list of conditions and the following
+ disclaimer in the documentation and/or other materials provided
+ with the distribution.
+ * Neither the name of The Linux Foundation nor the names of its
+ contributors may be used to endorse or promote products derived
+ from this software without specific prior written permission.
+
+THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+
+
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 9aa77fc..fd20b6b 100644
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -1,5 +1,8 @@
/*
* Copyright (C) 2009 The Android Open Source Project
+ * Copyright (c) 2010 - 2013, The Linux Foundation. All rights reserved.
+ *
+ * Not a Contribution
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
@@ -19,6 +22,7 @@
#include <utils/Log.h>
#include "include/AACEncoder.h"
+#include "include/MP3Decoder.h"
#include "include/ESDS.h"
@@ -41,9 +45,17 @@
#include <OMX_Audio.h>
#include <OMX_Component.h>
-
+#include <media/stagefright/ExtendedCodec.h>
+#include "include/ExtendedUtils.h"
#include "include/avc_utils.h"
+#ifdef ENABLE_AV_ENHANCEMENTS
+#include <QCMediaDefs.h>
+#include <QCMetaData.h>
+#include <QOMX_AudioExtensions.h>
+#endif
+#include "include/ExtendedUtils.h"
+
#ifdef USE_SAMSUNG_COLORFORMAT
#include <sec_format.h>
#endif
@@ -87,6 +99,11 @@
// component in question is buggy or not.
const static uint32_t kMaxColorFormatSupported = 1000;
+#define FACTORY_CREATE(name) \
+static sp<MediaSource> Make##name(const sp<MediaSource> &source) { \
+ return new name(source); \
+}
+
#define FACTORY_CREATE_ENCODER(name) \
static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \
return new name(source, meta); \
@@ -94,6 +111,7 @@
#define FACTORY_REF(name) { #name, Make##name },
+FACTORY_CREATE(MP3Decoder)
FACTORY_CREATE_ENCODER(AACEncoder)
static sp<MediaSource> InstantiateSoftwareEncoder(
@@ -117,8 +135,26 @@
return NULL;
}
+static sp<MediaSource> InstantiateSoftwareDecoder(
+ const char *name, const sp<MediaSource> &source) {
+ struct FactoryInfo {
+ const char *name;
+ sp<MediaSource> (*CreateFunc)(const sp<MediaSource> &);
+ };
+ static const FactoryInfo kFactoryInfo[] = {
+ FACTORY_REF(MP3Decoder)
+ };
+ for (size_t i = 0;
+ i < sizeof(kFactoryInfo) / sizeof(kFactoryInfo[0]); ++i) {
+ if (!strcmp(name, kFactoryInfo[i].name)) {
+ return (*kFactoryInfo[i].CreateFunc)(source);
+ }
+ }
+ return NULL;
+}
#undef FACTORY_CREATE_ENCODER
#undef FACTORY_REF
+#undef FACTORY_CREATE
#define CODEC_LOGI(x, ...) ALOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
#define CODEC_LOGV(x, ...) ALOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
@@ -163,7 +199,13 @@
}
static bool IsSoftwareCodec(const char *componentName) {
- if (!strncmp("OMX.google.", componentName, 11)) {
+#ifdef DOLBY_UDC
+ if (!strncmp("OMX.dolby.", componentName, 10)) {
+ return true;
+ }
+#endif // DOLBY_UDC
+ if (!strncmp("OMX.google.", componentName, 11)
+ || !strncmp("OMX.PV.", componentName, 7)) {
return true;
}
@@ -223,19 +265,15 @@
size_t index = 0;
-#ifdef ENABLE_QC_AV_ENHANCEMENTS
- //Check if application specially reuqested for aac hardware encoder
- //This is not a part of mediacodec list
+#ifdef ENABLE_AV_ENHANCEMENTS
if (matchComponentName && !strcmp("OMX.qcom.audio.encoder.aac", matchComponentName)) {
matchingCodecs->add();
-
CodecNameAndQuirks *entry = &matchingCodecs->editItemAt(index);
entry->mName = String8("OMX.qcom.audio.encoder.aac");
entry->mQuirks = 0;
return;
}
#endif
-
for (;;) {
ssize_t matchIndex =
list->findCodecByType(mime, createEncoder, index);
@@ -249,6 +287,7 @@
const char *componentName = list->getCodecName(matchIndex);
// If a specific codec is requested, skip the non-matching ones.
+ ALOGV("matchComponentName %s ",matchComponentName);
if (matchComponentName && strcmp(componentName, matchComponentName)) {
continue;
}
@@ -313,6 +352,28 @@
index, "output-buffers-are-unreadable")) {
quirks |= kOutputBuffersAreUnreadable;
}
+ if (list->codecHasQuirk(
+ index, "requies-loaded-to-idle-after-allocation")) {
+ quirks |= kRequiresLoadedToIdleAfterAllocation;
+ }
+ if (list->codecHasQuirk(
+ index, "requires-global-flush")) {
+ quirks |= kRequiresGlobalFlush;
+ }
+ if (list->codecHasQuirk(
+ index, "defers-output-buffer-allocation")) {
+ quirks |= kDefersOutputBufferAllocation;
+ }
+#ifdef DOLBY_UDC
+ if (list->codecHasQuirk(
+ index, "needs-flush-before-disable")) {
+ quirks |= kNeedsFlushBeforeDisable;
+ }
+ if (list->codecHasQuirk(
+ index, "requires-flush-complete-emulation")) {
+ quirks |= kRequiresFlushCompleteEmulation;
+ }
+#endif // DOLBY_UDC
#ifdef OMAP_ENHANCEMENT
if (list->codecHasQuirk(
index, "avoid-memcopy-input-recording-frames")) {
@@ -324,6 +385,8 @@
}
#endif
+ quirks |= ExtendedCodec::getComponentQuirks(list,index);
+
return quirks;
}
@@ -334,9 +397,7 @@
if (list == NULL) {
return false;
}
-#ifdef ENABLE_QC_AV_ENHANCEMENTS
- //Check for aac hardware encoder
- //This is not a part of mediacodec list
+#ifdef ENABLE_AV_ENHANCEMENTS
if (componentName && !strcmp("OMX.qcom.audio.encoder.aac", componentName)) {
*quirks = 0;
return true;
@@ -376,8 +437,14 @@
CHECK(success);
Vector<CodecNameAndQuirks> matchingCodecs;
- findMatchingCodecs(
+
+ if (ExtendedCodec::useHWAACDecoder(mime)) {
+ findMatchingCodecs(mime, createEncoder,
+ "OMX.qcom.audio.decoder.multiaac", flags, &matchingCodecs);
+ } else {
+ findMatchingCodecs(
mime, createEncoder, matchComponentName, flags, &matchingCodecs);
+ }
if (matchingCodecs.isEmpty()) {
ALOGV("No matching codecs! (mime: %s, createEncoder: %s, "
@@ -402,15 +469,21 @@
componentName = tmp.c_str();
}
+ sp<MediaSource> softwareCodec;
if (createEncoder) {
- sp<MediaSource> softwareCodec =
- InstantiateSoftwareEncoder(componentName, source, meta);
-
+ softwareCodec = InstantiateSoftwareEncoder(componentName, source, meta);
+ } else {
+ softwareCodec = InstantiateSoftwareDecoder(componentName, source);
+ }
if (softwareCodec != NULL) {
ALOGV("Successfully allocated software codec '%s'", componentName);
return softwareCodec;
}
+
+ const char* ext_componentName = ExtendedCodec::overrideComponentName(quirks, meta);
+ if(ext_componentName != NULL) {
+ componentName = ext_componentName;
}
ALOGV("Attempting to allocate OMX node '%s'", componentName);
@@ -554,8 +627,14 @@
esds.getCodecSpecificInfo(
&codec_specific_data, &codec_specific_data_size);
+ const char * mime_type;
+ meta->findCString(kKeyMIMEType, &mime_type);
+ if (strncmp(mime_type,
+ MEDIA_MIMETYPE_AUDIO_MPEG,
+ strlen(MEDIA_MIMETYPE_AUDIO_MPEG))) {
addCodecSpecificData(
codec_specific_data, codec_specific_data_size);
+ }
} else if (meta->findData(kKeyAVCC, &type, &data, &size)) {
// Parse the AVCDecoderConfigurationRecord
@@ -567,6 +646,8 @@
return err;
}
+ ExtendedUtils::setArbitraryModeIfInterlaced((const uint8_t *)data, meta);
+
CODEC_LOGI(
"AVC profile = %u (%s), level = %u",
profile, AVCProfileToString(profile), level);
@@ -575,6 +656,16 @@
CHECK(meta->findData(kKeyVorbisBooks, &type, &data, &size));
addCodecSpecificData(data, size);
+#ifdef ENABLE_AV_ENHANCEMENTS
+ } else if (meta->findData(kKeyRawCodecSpecificData, &type, &data, &size)) {
+ ALOGV("OMXCodec::configureCodec found kKeyRawCodecSpecificData of size %d\n", size);
+ addCodecSpecificData(data, size);
+#endif
+ } else {
+ ExtendedCodec::getRawCodecSpecificData(meta, data, size);
+ if (size) {
+ addCodecSpecificData(data, size);
+ }
}
}
@@ -605,6 +696,15 @@
CODEC_LOGE("setAACFormat() failed (err = %d)", err);
return err;
}
+#ifdef ENABLE_AV_ENHANCEMENTS
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAacCodecSpecificData, &type, &data, &size)) {
+ ALOGV("OMXCodec:: configureCodec found kKeyAacCodecSpecificData of size %d\n", size);
+ addCodecSpecificData(data, size);
+ }
+#endif
} else if (!strcasecmp(MEDIA_MIMETYPE_AUDIO_MPEG, mMIME)) {
int32_t numChannels, sampleRate;
if (meta->findInt32(kKeyChannelCount, &numChannels)
@@ -633,6 +733,18 @@
CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ } else {
+ if (mIsEncoder && !mIsVideo) {
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+ setRawAudioFormat(kPortIndexInput, sampleRate, numChannels);
+ }
+ status_t err = ExtendedCodec::setAudioFormat(
+ meta, mMIME, mOMX, mNode, mIsEncoder);
+ if(OK != err) {
+ return err;
+ }
}
if (!strncasecmp(mMIME, "video/", 6)) {
@@ -649,6 +761,13 @@
if (err != OK) {
return err;
}
+
+ ExtendedCodec::configureVideoDecoder(
+ meta, mMIME, mOMX, mFlags, mNode, mComponentName);
+ ExtendedCodec::configureFramePackingFormat(
+ meta, mOMX, mNode, mComponentName);
+ ExtendedCodec::enableSmoothStreaming(
+ mOMX, mNode, &mInSmoothStreamingMode, mComponentName);
}
}
@@ -915,6 +1034,8 @@
CHECK(success);
CHECK(stride != 0);
+ ExtendedUtils::HFR::reCalculateHFRParams(meta, frameRate, bitRate);
+
OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
compressionFormat = OMX_VIDEO_CodingAVC;
@@ -923,9 +1044,12 @@
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
compressionFormat = OMX_VIDEO_CodingH263;
} else {
+ status_t err = ExtendedCodec::setVideoInputFormat(mime, &compressionFormat);
+ if(err != OK) {
ALOGE("Not a supported video mime type: %s", mime);
CHECK(!"Should not be here. Not a supported video mime type.");
}
+ }
OMX_COLOR_FORMATTYPE colorFormat;
CHECK_EQ((status_t)OK, findTargetColorFormat(meta, &colorFormat));
@@ -1168,6 +1292,8 @@
h263type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+ ExtendedUtils::HFR::reCalculateHFRParams(meta, frameRate, bitRate);
+
h263type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate);
if (h263type.nPFrames == 0) {
h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
@@ -1219,6 +1345,8 @@
mpeg4type.nAllowedPictureTypes =
OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+ ExtendedUtils::HFR::reCalculateHFRParams(meta, frameRate, bitRate);
+
mpeg4type.nPFrames = setPFramesSpacing(iFramesInterval, frameRate);
if (mpeg4type.nPFrames == 0) {
mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
@@ -1240,6 +1368,7 @@
mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profileLevel.mProfile);
mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(profileLevel.mLevel);
+ ExtendedUtils::setBFrames(mpeg4type, mNumBFrames, mComponentName);
err = mOMX->setParameter(
mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
CHECK_EQ(err, (status_t)OK);
@@ -1277,12 +1406,16 @@
h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profileLevel.mProfile);
h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(profileLevel.mLevel);
+ ExtendedUtils::HFR::reCalculateHFRParams(meta, frameRate, bitRate);
+
// XXX
#ifdef USE_TI_DUCATI_H264_PROFILE
if ((strncmp(mComponentName, "OMX.TI.DUCATI1", 14) != 0)
&& (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline)) {
#else
- if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) {
+ if (ExtendedUtils::isAVCProfileSupported(h264type.eProfile)){
+ ALOGI("Profile type is %d ",h264type.eProfile);
+ } else if (h264type.eProfile != OMX_VIDEO_AVCProfileBaseline) {
#endif
ALOGW("Use baseline profile instead of %d for AVC recording",
h264type.eProfile);
@@ -1308,6 +1441,8 @@
h264type.nCabacInitIdc = 0;
}
+ ExtendedUtils::setBFrames(
+ h264type, mNumBFrames, iFramesInterval, frameRate, mComponentName);
if (h264type.nBFrames != 0) {
h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
}
@@ -1353,9 +1488,13 @@
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
compressionFormat = OMX_VIDEO_CodingMPEG2;
} else {
+ status_t err = ExtendedCodec::setVideoOutputFormat(mime,&compressionFormat);
+
+ if(err != OK) {
ALOGE("Not a supported video mime type: %s", mime);
CHECK(!"Should not be here. Not a supported video mime type.");
}
+ }
status_t err = setVideoPortFormatType(
kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
@@ -1515,6 +1654,7 @@
mState(LOADED),
mInitialBufferSubmit(true),
mSignalledEOS(false),
+ mFinalStatus(OK),
mNoMoreOutputData(false),
mOutputPortSettingsHaveChanged(false),
mSeekTimeUs(-1),
@@ -1527,7 +1667,9 @@
mNativeWindow(
(!strncmp(componentName, "OMX.google.", 11)
|| !strcmp(componentName, "OMX.Nvidia.mpeg2v.decode"))
- ? NULL : nativeWindow) {
+ ? NULL : nativeWindow),
+ mNumBFrames(0),
+ mInSmoothStreamingMode(false) {
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
@@ -1555,6 +1697,10 @@
"audio_decoder.amrnb", "audio_encoder.amrnb" },
{ MEDIA_MIMETYPE_AUDIO_AMR_WB,
"audio_decoder.amrwb", "audio_encoder.amrwb" },
+#ifdef ENABLE_AV_ENHANCEMENTS
+ { MEDIA_MIMETYPE_AUDIO_AMR_WB_PLUS,
+ "audio_decoder.amrwbplus", "audio_encoder.amrwbplus" },
+#endif
{ MEDIA_MIMETYPE_AUDIO_AAC,
"audio_decoder.aac", "audio_encoder.aac" },
{ MEDIA_MIMETYPE_AUDIO_VORBIS,
@@ -1563,6 +1709,12 @@
"audio_decoder.g711mlaw", "audio_encoder.g711mlaw" },
{ MEDIA_MIMETYPE_AUDIO_G711_ALAW,
"audio_decoder.g711alaw", "audio_encoder.g711alaw" },
+#ifdef ENABLE_AV_ENHANCEMENTS
+ { MEDIA_MIMETYPE_AUDIO_EVRC,
+ "audio_decoder.evrchw", "audio_encoder.evrc" },
+ { MEDIA_MIMETYPE_AUDIO_QCELP,
+ "audio_decoder,qcelp13Hw", "audio_encoder.qcelp13" },
+#endif
{ MEDIA_MIMETYPE_VIDEO_AVC,
"video_decoder.avc", "video_encoder.avc" },
{ MEDIA_MIMETYPE_VIDEO_MPEG4,
@@ -1579,6 +1731,16 @@
"audio_decoder.flac", "audio_encoder.flac" },
{ MEDIA_MIMETYPE_AUDIO_MSGSM,
"audio_decoder.gsm", "audio_encoder.gsm" },
+#ifdef ENABLE_AV_ENHANCEMENTS
+ { MEDIA_MIMETYPE_VIDEO_DIVX,
+ "video_decoder.divx", NULL },
+ { MEDIA_MIMETYPE_AUDIO_AC3,
+ "audio_decoder.ac3", NULL },
+ { MEDIA_MIMETYPE_AUDIO_EAC3,
+ "audio_decoder.eac3", NULL },
+ { MEDIA_MIMETYPE_VIDEO_DIVX311,
+ "video_decoder.divx", NULL },
+#endif
};
static const size_t kNumMimeToRole =
@@ -1592,6 +1754,7 @@
}
if (i == kNumMimeToRole) {
+ ExtendedCodec::setSupportedRole(omx, node, isEncoder, mime);
return;
}
@@ -1678,6 +1841,8 @@
return state == LOADED_TO_IDLE
|| state == IDLE_TO_EXECUTING
|| state == EXECUTING_TO_IDLE
+ || state == PAUSING
+ || state == FLUSHING
|| state == IDLE_TO_LOADED
|| state == RECONFIGURING;
}
@@ -2011,6 +2176,13 @@
return err;
}
+ err = mNativeWindow.get()->perform(mNativeWindow.get(),
+ NATIVE_WINDOW_SET_BUFFERS_SIZE, def.nBufferSize);
+ if (err != 0) {
+ ALOGE("native_window_set_buffers_size failed: %s (%d)", strerror(-err),
+ -err);
+ return err;
+ }
CODEC_LOGV("allocating %lu buffers from a native window of size %lu on "
"output port", def.nBufferCountActual, def.nBufferSize);
@@ -2771,11 +2943,18 @@
CODEC_LOGV("FLUSH_DONE(%ld)", portIndex);
+ if (portIndex == (OMX_U32) -1) {
+ CHECK_EQ((int)mPortStatus[kPortIndexInput], (int)SHUTTING_DOWN);
+ mPortStatus[kPortIndexInput] = ENABLED;
+ CHECK_EQ((int)mPortStatus[kPortIndexOutput], (int)SHUTTING_DOWN);
+ mPortStatus[kPortIndexOutput] = ENABLED;
+ } else {
CHECK_EQ((int)mPortStatus[portIndex], (int)SHUTTING_DOWN);
mPortStatus[portIndex] = ENABLED;
CHECK_EQ(countBuffersWeOwn(mPortBuffers[portIndex]),
mPortBuffers[portIndex].size());
+ }
if (mSkipCutBuffer != NULL && mPortStatus[kPortIndexOutput] == ENABLED) {
mSkipCutBuffer->clear();
@@ -2882,13 +3061,29 @@
mPortStatus[kPortIndexInput] = ENABLED;
mPortStatus[kPortIndexOutput] = ENABLED;
- if ((mFlags & kEnableGrallocUsageProtected) &&
- mNativeWindow != NULL) {
- // We push enough 1x1 blank buffers to ensure that one of
- // them has made it to the display. This allows the OMX
- // component teardown to zero out any protected buffers
- // without the risk of scanning out one of those buffers.
- pushBlankBuffersToNativeWindow();
+ if (mNativeWindow != NULL) {
+ /*
+ * reset buffer size field with SurfaceTexture
+ * back to 0. This will ensure proper size
+ * buffers are allocated if the same SurfaceTexture
+ * is re-used in a different decode session
+ */
+ int err =
+ mNativeWindow.get()->perform(mNativeWindow.get(),
+ NATIVE_WINDOW_SET_BUFFERS_SIZE,
+ 0);
+ if (err != 0) {
+ ALOGE("set_buffers_size failed: %s (%d)", strerror(-err),
+ -err);
+ }
+
+ if (mFlags & kEnableGrallocUsageProtected) {
+ // We push enough 1x1 blank buffers to ensure that one of
+ // them has made it to the display. This allows the OMX
+ // component teardown to zero out any protected buffers
+ // without the risk of scanning out one of those buffers.
+ pushBlankBuffersToNativeWindow();
+ }
}
setState(IDLE_TO_LOADED);
@@ -2924,6 +3119,14 @@
break;
}
+ case OMX_StatePause:
+ {
+ CODEC_LOGV("Now paused.");
+ CHECK_EQ((int)mState, (int)PAUSING);
+ setState(PAUSED);
+ break;
+ }
+
case OMX_StateInvalid:
{
setState(ERROR);
@@ -3038,8 +3241,12 @@
bool OMXCodec::flushPortAsync(OMX_U32 portIndex) {
CHECK(mState == EXECUTING || mState == RECONFIGURING
- || mState == EXECUTING_TO_IDLE);
+ || mState == EXECUTING_TO_IDLE || mState == FLUSHING);
+ if (portIndex == (OMX_U32) -1 ) {
+ mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
+ mPortStatus[kPortIndexOutput] = SHUTTING_DOWN;
+ } else {
CODEC_LOGV("flushPortAsync(%ld): we own %d out of %d buffers already.",
portIndex, countBuffersWeOwn(mPortBuffers[portIndex]),
mPortBuffers[portIndex].size());
@@ -3054,6 +3261,7 @@
// flush-complete event in this case.
return false;
+ }
}
status_t err =
@@ -3088,13 +3296,20 @@
}
void OMXCodec::fillOutputBuffers() {
- CHECK_EQ((int)mState, (int)EXECUTING);
+ CHECK(mState == EXECUTING || mState == FLUSHING);
// This is a workaround for some decoders not properly reporting
// end-of-output-stream. If we own all input buffers and also own
// all output buffers and we already signalled end-of-input-stream,
// the end-of-output-stream is implied.
- if (mSignalledEOS
+
+ // NOTE: Thumbnail mode needs a call to fillOutputBuffer in order
+ // to get the decoded frame from the component. Currently,
+ // thumbnail mode calls emptyBuffer with an EOS flag on its first
+ // frame and sets mSignalledEOS to true, so without the check for
+ // !mThumbnailMode, fillOutputBuffer will never be called.
+ if(!ExtendedUtils::checkIsThumbNailMode(mFlags, mComponentName)) {
+ if (mSignalledEOS
&& countBuffersWeOwn(mPortBuffers[kPortIndexInput])
== mPortBuffers[kPortIndexInput].size()
&& countBuffersWeOwn(mPortBuffers[kPortIndexOutput])
@@ -3102,7 +3317,8 @@
mNoMoreOutputData = true;
mBufferFilled.signal();
- return;
+ return;
+ }
}
Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexOutput];
@@ -3115,7 +3331,7 @@
}
void OMXCodec::drainInputBuffers() {
- CHECK(mState == EXECUTING || mState == RECONFIGURING);
+ CHECK(mState == EXECUTING || mState == RECONFIGURING || mState == FLUSHING);
if (mFlags & kUseSecureInputBuffers) {
Vector<BufferInfo> *buffers = &mPortBuffers[kPortIndexInput];
@@ -3138,7 +3354,8 @@
break;
}
- if (mFlags & kOnlySubmitOneInputBufferAtOneTime) {
+ if ((mFlags & kOnlySubmitOneInputBufferAtOneTime)
+ && (i == mNumBFrames)) {
break;
}
}
@@ -3241,6 +3458,8 @@
size_t offset = 0;
int32_t n = 0;
+ int32_t InterlaceFormatDetected = false;
+ int32_t InterlaceFrameCount = 0;
for (;;) {
@@ -3288,6 +3507,9 @@
break;
}
+ sp<MetaData> metaData = mSource->getFormat();
+ InterlaceFormatDetected = ExtendedUtils::checkIsInterlace(metaData);
+
if (mFlags & kUseSecureInputBuffers) {
info = findInputBufferByDataPointer(srcBuffer->data());
CHECK(info != NULL);
@@ -3446,8 +3668,25 @@
OMX_U32 flags = OMX_BUFFERFLAG_ENDOFFRAME;
+ if(InterlaceFormatDetected) {
+ InterlaceFrameCount++;
+ }
+
if (signalEOS) {
flags |= OMX_BUFFERFLAG_EOS;
+ } else if(ExtendedUtils::checkIsThumbNailMode(mFlags, mComponentName)
+ && (!InterlaceFormatDetected || InterlaceFrameCount>= 2)) {
+ // Because we don't get an EOS after getting the first frame, we
+ // need to notify the component with OMX_BUFFERFLAG_EOS, set
+ // mNoMoreOutputData to false so fillOutputBuffer gets called on
+ // the first output buffer (see comment in fillOutputBuffer), and
+ // mSignalledEOS must be true so drainInputBuffer is not executed
+ // on extra frames. Setting mFinalStatus to ERROR_END_OF_STREAM as
+ // we dont want to return OK and NULL buffer in read.
+ flags |= OMX_BUFFERFLAG_EOS;
+ mNoMoreOutputData = false;
+ mSignalledEOS = true;
+ mFinalStatus = ERROR_END_OF_STREAM;
} else {
mNoMoreOutputData = false;
}
@@ -3554,6 +3793,9 @@
return mBufferFilled.wait(mLock);
}
status_t err = mBufferFilled.waitRelative(mLock, kBufferFilledEventTimeOutNs);
+ if ((err == -ETIMEDOUT) && (mPaused == true)){
+ err = OK;
+ }
if (err != OK) {
CODEC_LOGE("Timed out waiting for output buffers: %d/%d",
countBuffersWeOwn(mPortBuffers[kPortIndexInput]),
@@ -3569,6 +3811,7 @@
OMX_PARAM_PORTDEFINITIONTYPE def;
InitOMXParams(&def);
def.nPortIndex = portIndex;
+ def.format.audio.cMIMEType = NULL;
status_t err = mOMX->getParameter(
mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
CHECK_EQ(err, (status_t)OK);
@@ -3901,6 +4144,27 @@
status_t OMXCodec::start(MetaData *meta) {
Mutex::Autolock autoLock(mLock);
+ if (mPaused) {
+ if (!strncmp(mComponentName, "OMX.qcom.", 9)) {
+ while (isIntermediateState(mState)) {
+ mAsyncCompletion.wait(mLock);
+ }
+ CHECK_EQ(mState, (status_t)PAUSED);
+ status_t err = mOMX->sendCommand(mNode,OMX_CommandStateSet, OMX_StateExecuting);
+ CHECK_EQ(err, (status_t)OK);
+ setState(IDLE_TO_EXECUTING);
+ mPaused = false;
+ while (mState != EXECUTING && mState != ERROR) {
+ mAsyncCompletion.wait(mLock);
+ }
+ drainInputBuffers();
+ return mState == ERROR ? UNKNOWN_ERROR : OK;
+ } else { // SW Codec
+ mPaused = false;
+ return OK;
+ }
+ }
+
if (mState != LOADED) {
CODEC_LOGE("called start in the unexpected state: %d", mState);
return UNKNOWN_ERROR;
@@ -4011,6 +4275,7 @@
isError = true;
}
+ case PAUSED:
case EXECUTING:
{
setState(EXECUTING_TO_IDLE);
@@ -4019,6 +4284,14 @@
CODEC_LOGV("This component requires a flush before transitioning "
"from EXECUTING to IDLE...");
+ //DSP supports flushing of ports simultaneously.
+ //Flushing individual port is not supported.
+ if(mQuirks & kRequiresGlobalFlush) {
+ bool emulateFlushCompletion = !flushPortAsync(kPortIndexBoth);
+ if (emulateFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexBoth);
+ }
+ } else {
bool emulateInputFlushCompletion =
!flushPortAsync(kPortIndexInput);
@@ -4031,6 +4304,7 @@
if (emulateOutputFlushCompletion) {
onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
+ }
}
} else {
mPortStatus[kPortIndexInput] = SHUTTING_DOWN;
@@ -4138,7 +4412,22 @@
mFilledBuffers.clear();
CHECK_EQ((int)mState, (int)EXECUTING);
+ setState(FLUSHING);
+ if(mQuirks & kRequiresGlobalFlush) {
+ bool emulateFlushCompletion = !flushPortAsync(kPortIndexBoth);
+ if (emulateFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexBoth);
+ }
+ } else {
+ //DSP supports flushing of ports simultaneously.
+ //Flushing individual port is not supported.
+ if(mQuirks & kRequiresGlobalFlush) {
+ bool emulateFlushCompletion = !flushPortAsync(kPortIndexBoth);
+ if (emulateFlushCompletion) {
+ onCmdComplete(OMX_CommandFlush, kPortIndexBoth);
+ }
+ } else {
bool emulateInputFlushCompletion = !flushPortAsync(kPortIndexInput);
bool emulateOutputFlushCompletion = !flushPortAsync(kPortIndexOutput);
@@ -4149,6 +4438,8 @@
if (emulateOutputFlushCompletion) {
onCmdComplete(OMX_CommandFlush, kPortIndexOutput);
}
+ }
+ }
while (mSeekTimeUs >= 0) {
if ((err = waitForBufferFilled_l()) != OK) {
@@ -4159,6 +4450,13 @@
while (mState != ERROR && !mNoMoreOutputData && mFilledBuffers.empty()) {
if ((err = waitForBufferFilled_l()) != OK) {
+ if ((err == -ETIMEDOUT) && (mPaused == true) && !mIsVideo) {
+ // When the audio playback is paused, the fill buffer maybe timed out
+ // if input data is not available to decode. Hence, considering the
+ // timed out as a valid case.
+ ALOGV("returned OK instead of timedout from read() as mPaused is true");
+ err = OK;
+ }
return err;
}
}
@@ -4167,6 +4465,11 @@
return UNKNOWN_ERROR;
}
+ if (seeking) {
+ CHECK_EQ((int)mState, (int)FLUSHING);
+ setState(EXECUTING);
+ }
+
if (mFilledBuffers.empty()) {
return mSignalledEOS ? mFinalStatus : ERROR_END_OF_STREAM;
}
@@ -4390,6 +4693,9 @@
"OMX_AUDIO_CodingWMA",
"OMX_AUDIO_CodingRA",
"OMX_AUDIO_CodingMIDI",
+#ifdef DOLBY_UDC
+ "OMX_AUDIO_CodingDDP",
+#endif // DOLBY_UDC
};
size_t numNames = sizeof(kNames) / sizeof(kNames[0]);
@@ -4727,7 +5033,21 @@
mOutputFormat->setInt32(kKeySampleRate, sampleRate);
mOutputFormat->setInt32(kKeyBitRate, bitRate);
} else {
+ AString mimeType;
+ if (OK == ExtendedCodec::handleSupportedAudioFormats(
+ audio_def->eEncoding, &mimeType)) {
+ mOutputFormat->setCString(
+ kKeyMIMEType, mimeType.c_str());
+ int32_t numChannels, sampleRate, bitRate;
+ inputFormat->findInt32(kKeyChannelCount, &numChannels);
+ inputFormat->findInt32(kKeySampleRate, &sampleRate);
+ inputFormat->findInt32(kKeyBitRate, &bitRate);
+ mOutputFormat->setInt32(kKeyChannelCount, numChannels);
+ mOutputFormat->setInt32(kKeySampleRate, sampleRate);
+ mOutputFormat->setInt32(kKeyBitRate, bitRate);
+ } else {
CHECK(!"Should not be here. Unknown audio encoding.");
+ }
}
break;
}
@@ -4796,8 +5116,15 @@
}
if (mNativeWindow != NULL) {
- initNativeWindowCrop();
+ if (mInSmoothStreamingMode) {
+ ExtendedUtils::updateNativeWindowBufferGeometry(
+ mNativeWindow.get(), video_def->nFrameWidth,
+ video_def->nFrameHeight, video_def->eColorFormat);
+ }
+ initNativeWindowCrop();
}
+ } else {
+ ExtendedUtils::HFR::copyHFRParams(inputFormat, mOutputFormat);
}
break;
}
@@ -4819,11 +5146,29 @@
}
status_t OMXCodec::pause() {
+ CODEC_LOGV("pause mState=%d", mState);
+
Mutex::Autolock autoLock(mLock);
-
- mPaused = true;
-
- return OK;
+ if (mState != EXECUTING) {
+ return UNKNOWN_ERROR;
+ }
+ while (isIntermediateState(mState)) {
+ mAsyncCompletion.wait(mLock);
+ }
+ if (!strncmp(mComponentName, "OMX.qcom.", 9)) {
+ status_t err = mOMX->sendCommand(mNode,
+ OMX_CommandStateSet, OMX_StatePause);
+ CHECK_EQ(err, (status_t)OK);
+ setState(PAUSING);
+ mPaused = true;
+ while (mState != PAUSED && mState != ERROR) {
+ mAsyncCompletion.wait(mLock);
+ }
+ return mState == ERROR ? UNKNOWN_ERROR : OK;
+ } else {
+ mPaused = true;
+ return OK;
+ }
}
////////////////////////////////////////////////////////////////////////////////
diff --git a/media/libstagefright/SampleTable.cpp b/media/libstagefright/SampleTable.cpp
index 3981290..9d86a0f 100644
--- a/media/libstagefright/SampleTable.cpp
+++ b/media/libstagefright/SampleTable.cpp
@@ -360,8 +360,9 @@
return ERROR_IO;
}
- if (U32_AT(header) != 0) {
- // Expected version = 0, flags = 0.
+ if (U32_AT(header) != 0 &&
+ U32_AT(header) != 0x01000000) {
+ // Expected version = 0/1, flags = 0.
return ERROR_MALFORMED;
}
diff --git a/media/libstagefright/StagefrightMediaScanner.cpp b/media/libstagefright/StagefrightMediaScanner.cpp
index af8186c..98ba1a4 100644
--- a/media/libstagefright/StagefrightMediaScanner.cpp
+++ b/media/libstagefright/StagefrightMediaScanner.cpp
@@ -43,6 +43,9 @@
".wav", ".amr", ".midi", ".xmf", ".rtttl", ".rtx", ".ota",
".mkv", ".mka", ".webm", ".ts", ".fl", ".flac", ".mxmf",
".avi", ".mpeg", ".mpg", ".awb", ".mpga"
+#ifdef ENABLE_AV_ENHANCEMENTS
+ ,".qcp", ".ac3", ".dts", ".wmv", ".ec3"
+#endif
};
static const size_t kNumValidExtensions =
sizeof(kValidExtensions) / sizeof(kValidExtensions[0]);
diff --git a/media/libstagefright/TunnelPlayer.cpp b/media/libstagefright/TunnelPlayer.cpp
new file mode 100644
index 0000000..9aacbb6
--- /dev/null
+++ b/media/libstagefright/TunnelPlayer.cpp
@@ -0,0 +1,798 @@
+/*
+ * Copyright (c) 2009-2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
+ * 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_NDDEBUG 0
+#define LOG_TAG "TunnelPlayer"
+#include <utils/Log.h>
+#include <utils/threads.h>
+
+#include <signal.h>
+#include <sys/prctl.h>
+#include <binder/IPCThreadState.h>
+#include <media/AudioTrack.h>
+
+#include <media/stagefright/TunnelPlayer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/MediaErrors.h>
+#include "include/AwesomePlayer.h"
+#include <cutils/properties.h>
+
+static const char mName[] = "TunnelPlayer";
+#define MEM_METADATA_SIZE 64
+/*
+ * We need to reserve some space in the
+ * ion buffer (used in HAL) to save the
+ * metadata. so read from the extractor
+ * a somewhat smaller number of bytes.
+ * ideally this number should be bufer_size - sizeof(struct output_metadata_t)
+ */
+#define MEM_BUFFER_SIZE (240*1024 - MEM_METADATA_SIZE)
+#define MEM_BUFFER_COUNT 4
+#define TUNNEL_BUFFER_TIME 1500000
+
+namespace android {
+int TunnelPlayer::mTunnelObjectsAlive = 0;
+
+TunnelPlayer::TunnelPlayer(
+ const sp<MediaPlayerBase::AudioSink> &audioSink, bool &initCheck,
+ AwesomePlayer *observer, bool hasVideo)
+:AudioPlayer(audioSink, 0, observer),
+mPositionTimeMediaUs(-1),
+mPositionTimeRealUs(-1),
+mInternalSeeking(false),
+mStarted(false),
+mSampleRate(0),
+mLatencyUs(0),
+mFrameSize(0),
+mNumFramesPlayed(0),
+mNumFramesPlayedSysTimeUs(0),
+mInputBuffer(NULL),
+mSeeking(false),
+mReachedEOS(false),
+mReachedOutputEOS(false),
+mFinalStatus(OK),
+mSeekTimeUs(0),
+mPauseTime(0),
+mIsFirstBuffer(false),
+mFirstBufferResult(OK),
+mFirstBuffer(NULL),
+mAudioSink(audioSink),
+mObserver(observer) {
+ ALOGD("TunnelPlayer::TunnelPlayer()");
+ mTunnelObjectsAlive++;
+ numChannels = 0;
+ mPaused = false;
+ mFormat = AUDIO_FORMAT_MP3;
+ mQueue.start();
+ mQueueStarted = true;
+ mPauseEvent = new TunnelEvent(this, &TunnelPlayer::onPauseTimeOut);
+ mPauseEventPending = false;
+
+ mSeekTimeUs = 0;
+ mIsAudioRouted = false;
+ mExtractorThreadAlive = false;
+
+ mHasVideo = hasVideo;
+ initCheck = true;
+
+}
+const int TunnelPlayer::getTunnelObjectsAliveMax() {
+ char value[PROPERTY_VALUE_MAX];
+ property_get("tunnel.multiple", value, "0");
+ if (strcmp("true",value) == 0) {
+ return 4;
+ }
+ return 1;
+}
+
+TunnelPlayer::~TunnelPlayer() {
+ ALOGD("TunnelPlayer::~TunnelPlayer()");
+ if (mQueueStarted) {
+ mQueue.stop();
+ }
+
+ reset();
+ mTunnelObjectsAlive--;
+
+}
+
+void TunnelPlayer::setSource(const sp<MediaSource> &source) {
+ CHECK(mSource == NULL);
+ ALOGD("Setting source from Tunnel Player");
+ mSource = source;
+}
+
+status_t TunnelPlayer::start(bool sourceAlreadyStarted) {
+ CHECK(!mStarted);
+ CHECK(mSource != NULL);
+
+ ALOGV("start: sourceAlreadyStarted %d", sourceAlreadyStarted);
+ //Check if the source is started, start it
+ status_t err;
+ if (!sourceAlreadyStarted) {
+ err = mSource->start();
+ if (err != OK) {
+ return err;
+ }
+ }
+
+ //Create extractor thread, read and initialize all the
+ //mutexes and coditional variables
+ createThreads();
+ ALOGV("Thread Created.");
+ // We allow an optional INFO_FORMAT_CHANGED at the very beginning
+ // of playback, if there is one, getFormat below will retrieve the
+ // updated format, if there isn't, we'll stash away the valid buffer
+ // of data to be used on the first audio callback.
+
+ CHECK(mFirstBuffer == NULL);
+
+ MediaSource::ReadOptions options;
+ if (mSeeking) {
+ options.setSeekTo(mSeekTimeUs);
+ mSeeking = false;
+ }
+
+ mFirstBufferResult = mSource->read(&mFirstBuffer, &options);
+ if (mFirstBufferResult == INFO_FORMAT_CHANGED) {
+ ALOGV("INFO_FORMAT_CHANGED!!!");
+ CHECK(mFirstBuffer == NULL);
+ mFirstBufferResult = OK;
+ mIsFirstBuffer = false;
+ } else {
+ mIsFirstBuffer = true;
+ }
+
+ sp<MetaData> format = mSource->getFormat();
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_AAC)) {
+ mFormat = AUDIO_FORMAT_AAC;
+ }
+ else if (!strcasecmp(mime,MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ mFormat = AUDIO_FORMAT_MP3;
+ ALOGD("TunnelPlayer::start AUDIO_FORMAT_MP3");
+ } else {
+ ALOGE("TunnelPlayer::UNSUPPORTED");
+ }
+
+
+ CHECK(success);
+
+ success = format->findInt32(kKeySampleRate, &mSampleRate);
+ CHECK(success);
+
+ success = format->findInt32(kKeyChannelCount, &numChannels);
+ CHECK(success);
+
+ if(!format->findInt32(kKeyChannelMask, &mChannelMask)) {
+ // log only when there's a risk of ambiguity of channel mask selection
+ ALOGI_IF(numChannels > 2,
+ "source format didn't specify channel mask, using (%d) channel order", numChannels);
+ mChannelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
+ }
+ audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_TUNNEL |
+ AUDIO_OUTPUT_FLAG_DIRECT);
+ ALOGV("mAudiosink->open() mSampleRate %d, numChannels %d, mChannelMask %d, flags %d",mSampleRate, numChannels, mChannelMask, flags);
+ err = mAudioSink->open(
+ mSampleRate, numChannels, mChannelMask, mFormat,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &TunnelPlayer::AudioSinkCallback,
+ this,
+ flags,
+ NULL);
+
+ if (err != OK) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (!sourceAlreadyStarted) {
+ mSource->stop();
+ }
+
+ ALOGE("Opening a routing session failed");
+ return err;
+ }
+
+ mIsAudioRouted = true;
+ mStarted = true;
+ mAudioSink->start();
+ mLock.lock();
+ ALOGV("Waking up extractor thread");
+ mExtractorCv.signal();
+ mLock.unlock();
+ return OK;
+}
+
+status_t TunnelPlayer::seekTo(int64_t time_us) {
+
+ ALOGD("seekTo: time_us %lld", time_us);
+
+ Mutex::Autolock _l(mLock); //to sync w/ onpausetimeout
+
+ if (seekTooClose(time_us)) {
+ ALOGV("seek time is too close to current playback position");
+ mLock.unlock(); //unlock and post
+ mObserver->postAudioSeekComplete();
+ mLock.lock();
+ return OK;
+ }
+
+ mSeeking = true;
+ mSeekTimeUs = time_us;
+ mPauseTime = mSeekTimeUs;
+ ALOGV("In seekTo(), mSeekTimeUs %lld",mSeekTimeUs);
+
+ if (mIsAudioRouted) {
+ mAudioSink->flush();
+ }
+
+ if (mReachedEOS) {
+ mReachedEOS = false;
+ mReachedOutputEOS = false;
+ if(mPaused == false) {
+ ALOGV("Going to signal extractor thread since playback is already going on ");
+ mExtractorCv.signal();
+ ALOGV("Signalled extractor thread.");
+ }
+ }
+ ALOGV("seek done.");
+ return OK;
+}
+void TunnelPlayer::pause(bool playPendingSamples) {
+ CHECK(mStarted);
+ if (mPaused) {
+ return;
+ }
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("pause: playPendingSamples %d", playPendingSamples);
+ mPaused = true;
+ int64_t playedTime = 0;
+ if(!mPauseEventPending) {
+ ALOGV("Posting an event for Pause timeout");
+ mQueue.postEventWithDelay(mPauseEvent, TUNNEL_PAUSE_TIMEOUT_USEC);
+ mPauseEventPending = true;
+ }
+ getPlayedTimeFromDSP_l(&playedTime);
+ mPauseTime = mSeekTimeUs + playedTime;
+ if (mAudioSink.get() != NULL) {
+ ALOGV("AudioSink pause");
+ mAudioSink->pause();
+ }
+}
+
+status_t TunnelPlayer::resume() {
+ status_t err = NO_ERROR;
+ Mutex::Autolock autoLock(mLock);
+ ALOGV("resume: mPaused %d",mPaused);
+ if ( mPaused) {
+ CHECK(mStarted);
+ if(mPauseEventPending) {
+ ALOGV("Resume(): Cancelling the puaseTimeout event");
+ mPauseEventPending = false;
+ mQueue.cancelEvent(mPauseEvent->eventID());
+ }
+ audio_format_t format;
+
+ if (!mIsAudioRouted) {
+ audio_output_flags_t flags = (audio_output_flags_t) (AUDIO_OUTPUT_FLAG_TUNNEL |
+ AUDIO_OUTPUT_FLAG_DIRECT);
+ err = mAudioSink->open(
+ mSampleRate, numChannels, mChannelMask, mFormat,
+ DEFAULT_AUDIOSINK_BUFFERCOUNT,
+ &TunnelPlayer::AudioSinkCallback,
+ this,
+ flags,
+ NULL);
+ if (err != NO_ERROR) {
+ ALOGE("Audio sink open failed.");
+ }
+ mIsAudioRouted = true;
+ }
+ mPaused = false;
+ ALOGV("Audio sink open succeeded.");
+ mAudioSink->start();
+ ALOGV("Audio sink start succeeded.");
+ mExtractorCv.signal();
+ ALOGV("Audio signalling extractor thread.");
+ }
+ return err;
+}
+
+//static
+size_t TunnelPlayer::AudioSinkCallback(
+ MediaPlayerBase::AudioSink *audioSink,
+ void *buffer, size_t size, void *cookie,
+ MediaPlayerBase::AudioSink::cb_event_t event) {
+ TunnelPlayer *me = (TunnelPlayer *)cookie;
+ if(me != NULL) {
+ ALOGV("postAudioEOS mSeeking %d", me->mSeeking);
+ if (buffer == NULL && size == AudioTrack::EVENT_UNDERRUN) {
+ if(me->mReachedEOS == true) {
+ //in the case of seek all these flags will be reset
+ me->mReachedOutputEOS = true;
+ ALOGV("postAudioEOS mSeeking %d", me->mSeeking);
+ me->mObserver->postAudioEOS(0);
+ }else {
+ ALOGV("postAudioEOS ignored since %d", me->mSeeking);
+ }
+ } else if (size == AudioTrack::EVENT_HW_FAIL) {
+ ALOGV("postAudioEOS in SSR " );
+ me->mReachedOutputEOS = true;
+ me->mReachedEOS = true;
+ me->mKillExtractorThread = true;
+ me->mObserver->postAudioEOS(0);
+ }
+ }
+ return 1;
+}
+
+void TunnelPlayer::reset() {
+ ALOGV("Reset");
+
+ Mutex::Autolock _l(mLock); //to sync w/ onpausetimeout
+
+ //cancel any pending onpause timeout events
+ //doesnt matter if the event is really present or not
+ mPauseEventPending = false;
+ mQueue.cancelEvent(mPauseEvent->eventID());
+
+ mReachedEOS = true;
+
+ // make sure Decoder thread has exited
+ requestAndWaitForExtractorThreadExit_l();
+
+ // Close the audiosink after all the threads exited to make sure
+ if (mIsAudioRouted) {
+ mAudioSink->stop();
+ mAudioSink->close();
+ mIsAudioRouted = false;
+ }
+ //TODO: Release Wake lock
+
+ // Make sure to release any buffer we hold onto so that the
+ // source is able to stop().
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+
+ if (mInputBuffer != NULL) {
+ ALOGV("AudioPlayer releasing input buffer.");
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ if (mStarted)
+ mSource->stop();
+
+ mSource.clear();
+
+ mPositionTimeMediaUs = -1;
+ mPositionTimeRealUs = -1;
+ mSeeking = false;
+ mReachedEOS = false;
+ mReachedOutputEOS = false;
+ mFinalStatus = OK;
+ mStarted = false;
+}
+
+
+bool TunnelPlayer::isSeeking() {
+ Mutex::Autolock autoLock(mLock);
+ return mSeeking;
+}
+
+bool TunnelPlayer::reachedEOS(status_t *finalStatus) {
+ *finalStatus = OK;
+ Mutex::Autolock autoLock(mLock);
+ *finalStatus = mFinalStatus;
+ return mReachedOutputEOS;
+}
+
+
+void *TunnelPlayer::extractorThreadWrapper(void *me) {
+ static_cast<TunnelPlayer *>(me)->extractorThreadEntry();
+ return NULL;
+}
+
+
+void TunnelPlayer::extractorThreadEntry() {
+
+ mLock.lock();
+ uint32_t BufferSizeToUse = MEM_BUFFER_SIZE;
+
+ pid_t tid = gettid();
+ androidSetThreadPriority(tid, mHasVideo ? ANDROID_PRIORITY_NORMAL :
+ ANDROID_PRIORITY_AUDIO);
+ prctl(PR_SET_NAME, (unsigned long)"Extractor Thread", 0, 0, 0);
+
+ ALOGV("extractorThreadEntry wait for signal \n");
+ if (!mStarted && !mKillExtractorThread) {
+ mExtractorCv.wait(mLock);
+ }
+ ALOGV("extractorThreadEntry ready to work \n");
+ mLock.unlock();
+ if (mKillExtractorThread) {
+ return;
+ }
+ if(mSource != NULL) {
+ sp<MetaData> format = mSource->getFormat();
+ const char *mime;
+ bool success = format->findCString(kKeyMIMEType, &mime);
+ }
+ void* local_buf = malloc(BufferSizeToUse);
+ int *lptr = ((int*)local_buf);
+ int bytesWritten = 0;
+ bool lSeeking = false;
+ bool lPaused = false;
+ while (!mKillExtractorThread) {
+
+ if (mReachedEOS || mPaused || !mIsAudioRouted) {
+ ALOGV("Going to sleep before write since "
+ "mReachedEOS %d, mPaused %d, mIsAudioRouted %d",
+ mReachedEOS, mPaused, mIsAudioRouted);
+ mExtractorMutex.lock();
+ mExtractorCv.wait(mExtractorMutex);
+ mExtractorMutex.unlock();
+ ALOGV("Woke up from sleep before write since "
+ "mReachedEOS %d, mPaused %d, mIsAudioRouted %d",
+ mReachedEOS, mPaused, mIsAudioRouted);
+ continue;
+ }
+
+ ALOGV("FillBuffer: MemBuffer size %d", BufferSizeToUse);
+ ALOGV("Fillbuffer started");
+ bytesWritten = fillBuffer(local_buf, BufferSizeToUse);
+ ALOGV("FillBuffer completed bytesToWrite %d", bytesWritten);
+ if(!mKillExtractorThread) {
+ mLock.lock();
+ lPaused = mPaused;
+ mLock.unlock();
+
+ if(lPaused == true) {
+ //write only if player is not in paused state. Sleep on lock
+ // resume is called
+ ALOGV("Going to sleep in decodethreadiwrite since sink is paused");
+ mExtractorMutex.lock();
+ mExtractorCv.wait(mExtractorMutex);
+ ALOGV("Going to unlock n decodethreadwrite since sink "
+ "resumed mPaused %d, mIsAudioRouted %d, mReachedEOS %d",
+ mPaused, mIsAudioRouted, mReachedEOS);
+ mExtractorMutex.unlock();
+ }
+ mLock.lock();
+ lSeeking = mSeeking||mInternalSeeking;
+ mLock.unlock();
+
+ if(lSeeking == false && (mKillExtractorThread == false)){
+ //if we are seeking, ignore write, otherwise write
+ ALOGV("Fillbuffer before seek flag %d", mSeeking);
+ int lWrittenBytes = mAudioSink->write(local_buf, bytesWritten);
+ ALOGV("Fillbuffer after write, written bytes %d and seek flag %d", lWrittenBytes, mSeeking);
+ if(lWrittenBytes > 0) {
+ //send EOS only if write was successful, if is_buffer_available
+ // is flushed out (which returns 0 do not SEND EOS
+ ALOGV("Fillbuffer after write and seek flag %d", mSeeking);
+ mLock.lock();
+ lSeeking = mSeeking||mInternalSeeking;
+ mLock.unlock();
+ //ignore posting zero length buffer is seeking is set
+ if(mReachedEOS && bytesWritten && !lSeeking && (mKillExtractorThread == false)) {
+ ALOGV("Fillbuffer after write sent EOS flag %d", lSeeking);
+ mAudioSink->write(local_buf, 0);
+ } else {
+ ALOGV("Not sending EOS buffer sent since seeking %d, "
+ "kill %d and mReachedEOS %d", \
+ lSeeking, mKillExtractorThread, mReachedEOS);
+ }
+ } else {
+ ALOGV("write exited because of flush %d", mSeeking);
+ }
+ } else {
+ ALOGV("Fillbuffer ignored since we seeked after fillBuffer was set %d", mSeeking);
+ }
+ }
+ }
+
+ free(local_buf);
+
+ //TODO: Call fillbuffer with different size and write to mAudioSink()
+}
+void TunnelPlayer::createThreads() {
+
+ // Create extractor Thread
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mKillExtractorThread = false;
+
+ mExtractorThreadAlive = true;
+
+ ALOGV("Creating Extractor Thread");
+ pthread_create(&mExtractorThread, &attr, extractorThreadWrapper, this);
+
+ pthread_attr_destroy(&attr);
+}
+size_t TunnelPlayer::fillBuffer(void *data, size_t size) {
+
+ if (mReachedEOS) {
+ return 0;
+ }
+
+ bool postSeekComplete = false;
+
+ size_t size_done = 0;
+ size_t size_remaining = size;
+ //clear the flag since we dont know whether we are seeking or not, yet
+ ALOGV("fillBuffer: Clearing seek flag in fill buffer");
+
+ bool yield = !mIsFirstBuffer;
+
+ while (size_remaining > 0) {
+ MediaSource::ReadOptions options;
+ {
+ Mutex::Autolock autoLock(mLock);
+ if(mSeeking) {
+ mInternalSeeking = false;
+ }
+
+ if (mSeeking || mInternalSeeking) {
+ if (mIsFirstBuffer) {
+ if (mFirstBuffer != NULL) {
+ mFirstBuffer->release();
+ mFirstBuffer = NULL;
+ }
+ mIsFirstBuffer = false;
+ }
+
+ MediaSource::ReadOptions::SeekMode seekMode;
+ seekMode = MediaSource::ReadOptions::SEEK_CLOSEST_SYNC;
+ options.setSeekTo(mSeekTimeUs, seekMode );
+ if (mInputBuffer != NULL) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ // This is to ignore the data already filled in the output buffer
+ size_done = 0;
+ size_remaining = size;
+
+ mSeeking = false;
+ if (mObserver && !mInternalSeeking) {
+ ALOGV("fillBuffer: Posting audio seek complete event");
+ postSeekComplete = true;
+ }
+ mInternalSeeking = false;
+ ALOGV("fillBuffer: Setting seek flag in fill buffer");
+ //set the flag since we know that this buffer is the new positions buffer
+ }
+ }
+ if (mInputBuffer == NULL) {
+ status_t err;
+
+ if (mIsFirstBuffer) {
+ mInputBuffer = mFirstBuffer;
+ mFirstBuffer = NULL;
+ err = mFirstBufferResult;
+
+ mIsFirstBuffer = false;
+ } else {
+ err = mSource->read(&mInputBuffer, &options);
+ }
+
+ CHECK((err == OK && mInputBuffer != NULL)
+ || (err != OK && mInputBuffer == NULL));
+ {
+ Mutex::Autolock autoLock(mLock);
+
+ if (err != OK) {
+ ALOGD("fill buffer - reached eos true");
+ mReachedEOS = true;
+ mFinalStatus = err;
+ break;
+ }
+ }
+
+ }
+ 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;
+
+ if (mHasVideo && yield) {
+ sched_yield();
+ }
+ }
+ if(mReachedEOS)
+ memset((char *)data + size_done, 0x0, size_remaining);
+ ALOGV("fill buffer size_done = %d",size_done);
+
+ if (postSeekComplete) {
+ mObserver->postAudioSeekComplete();
+ }
+
+ return size_done;
+}
+
+int64_t TunnelPlayer::getRealTimeUs() {
+ Mutex::Autolock autoLock(mLock);
+
+ /*
+ * If it so happens that the client (e.g. AwesomePlayer),
+ * queries for the current time before compressed
+ * data from the new position is given to the compressed
+ * driver, we need to return the seek position
+ */
+ if (mSeeking || mInternalSeeking) {
+ ALOGV("Seek yet to be processed, return seek time as current time %lld",
+ mSeekTimeUs);
+ return mSeekTimeUs;
+ }
+
+ getOffsetRealTime_l(&mPositionTimeRealUs);
+ //update media time too
+ mPositionTimeMediaUs = mPositionTimeRealUs;
+ return mPositionTimeRealUs;
+}
+
+void TunnelPlayer::getPlayedTimeFromDSP_l(int64_t* timeStamp ) {
+ ALOGV("going to query timestamp");
+ mAudioSink->getTimeStamp((uint64_t*)timeStamp);
+ ALOGV("timestamp returned from DSP %lld ", (*timeStamp));
+ return;
+}
+
+//offset with pause and seek time
+void TunnelPlayer::getOffsetRealTime_l(int64_t* offsetTime) {
+ if (mPaused) {
+ *offsetTime = mPauseTime;
+ ALOGV("getMediaTimeUs() mPaused %d mSeekTimeUs %lld mPauseTime %lld", mPaused, mSeekTimeUs, mPauseTime );
+ } else {
+ getPlayedTimeFromDSP_l(offsetTime);
+ ALOGV("getMediaTimeUs() mPaused %d mSeekTimeUs %lld mPauseTime %lld, timeStamp %lld", mPaused, mSeekTimeUs, mPauseTime, *offsetTime);
+ *offsetTime = mSeekTimeUs + *offsetTime;
+ }
+}
+
+int64_t TunnelPlayer::getMediaTimeUs() {
+ //essentially there is only one time, the real time
+ return getRealTimeUs();
+}
+
+bool TunnelPlayer::getMediaTimeMapping(
+ int64_t *realtime_us, int64_t *mediatime_us) {
+ Mutex::Autolock autoLock(mLock);
+
+ *realtime_us = mPositionTimeRealUs;
+ *mediatime_us = mPositionTimeMediaUs;
+
+ return mPositionTimeRealUs != -1 && mPositionTimeMediaUs != -1;
+}
+
+//lock has been taken in reset() to sync with onpausetimeout
+void TunnelPlayer::requestAndWaitForExtractorThreadExit_l() {
+
+ ALOGV("requestAndWaitForExtractorThreadExit_l -1");
+
+ if (!mExtractorThreadAlive)
+ return;
+
+ mKillExtractorThread = true;
+
+ ALOGV("requestAndWaitForExtractorThreadExit_l +0");
+ if (mIsAudioRouted && !mReachedOutputEOS) {
+ mAudioSink->flush();
+ }
+
+
+ ALOGV("requestAndWaitForExtractorThreadExit_l +1");
+ mExtractorCv.signal();
+ mLock.unlock();
+ ALOGV("requestAndWaitForExtractorThreadExit_l +2");
+ pthread_join(mExtractorThread,NULL);
+ mLock.lock();
+ ALOGV("requestAndWaitForExtractorThreadExit_l +3");
+
+ ALOGV("Extractor thread killed");
+}
+
+void TunnelPlayer::onPauseTimeOut() {
+ Mutex::Autolock autoLock(mLock);
+ int64_t playedTime = 0;
+ ALOGV("onPauseTimeOut");
+ if (!mPauseEventPending) {
+ return;
+ }
+ mPauseEventPending = false;
+ // 1.) Set seek flags
+ mReachedEOS = false;
+ mReachedOutputEOS = false;
+
+ if(mSeeking == false) {
+ ALOGV("onPauseTimeOut +2");
+ mInternalSeeking = true;
+ ALOGV("onPauseTimeOut +3");
+ getPlayedTimeFromDSP_l(&playedTime);
+ mSeekTimeUs += playedTime;
+ } else {
+ ALOGV("Do not update seek time if it was seeked before onpause timeout");
+ }
+
+ // 2.) Close routing Session
+ ALOGV("onPauseTimeOut +4");
+ mAudioSink->flush();
+ ALOGV("onPauseTimeOut +5");
+ mAudioSink->stop();
+ ALOGV("onPauseTimeOut +6");
+ mAudioSink->close();
+ ALOGV("onPauseTimeOut +7");
+ mIsAudioRouted = false;
+}
+
+/*
+ * Returns true if the seek position is too close to the
+ * current playback position.
+ * Caller must acquire mLock
+ */
+bool TunnelPlayer::seekTooClose(int64_t time_us) {
+ int64_t t1 = -1;
+ /* The time as per DSP just before flush is issued */
+ if (mPositionTimeRealUs == -1) {
+ getOffsetRealTime_l(&mPositionTimeRealUs);
+ }
+
+ t1 = mPositionTimeRealUs;
+ /*
+ * empirical
+ * -----------
+ * This constant signifies how much data (in Us) has been rendered by the
+ * DSP in the interval between the moment flush is issued on AudioSink to
+ * after ioctl(PAUSE) returns in Audio HAL. (flush triggers an implicit
+ * pause in audio HAL)
+ *
+ */
+ const int64_t deltaUs = 60000LL; /* 60-70ms on msm8974 */
+ return (time_us > t1) && ((time_us - t1) <= deltaUs);
+}
+
+} //namespace android
diff --git a/media/libstagefright/Utils.cpp b/media/libstagefright/Utils.cpp
old mode 100644
new mode 100755
index 1e85a01..e589fdc
--- a/media/libstagefright/Utils.cpp
+++ b/media/libstagefright/Utils.cpp
@@ -32,6 +32,7 @@
#include <hardware/audio.h>
#include <media/stagefright/Utils.h>
#include <media/AudioParameter.h>
+#include <media/stagefright/ExtendedCodec.h>
#ifdef ENABLE_QC_AV_ENHANCEMENTS
#include "QCMetaData.h"
@@ -281,6 +282,7 @@
msg->setBuffer("csd-1", buffer);
}
+ ExtendedCodec::convertMetaDataToMessage(meta, &msg);
*format = msg;
return OK;
diff --git a/media/libstagefright/WAVEWriter.cpp b/media/libstagefright/WAVEWriter.cpp
new file mode 100644
index 0000000..468765f
--- /dev/null
+++ b/media/libstagefright/WAVEWriter.cpp
@@ -0,0 +1,323 @@
+/*
+ * Copyright (c) 2012 - 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "WAVEWriter"
+#include <utils/Log.h>
+
+#include <media/stagefright/WAVEWriter.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/mediarecorder.h>
+#include <sys/prctl.h>
+#include <sys/types.h>
+#include <sys/stat.h>
+#include <fcntl.h>
+
+namespace android {
+
+static struct wav_header hdr;
+
+
+WAVEWriter::WAVEWriter(const char *filename)
+ : mFd(-1),
+ mInitCheck(NO_INIT),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false) {
+
+ mFd = open(filename, O_CREAT | O_LARGEFILE | O_TRUNC | O_RDWR, S_IRUSR | S_IWUSR);
+ if (mFd >= 0) {
+ mInitCheck = OK;
+ }
+}
+
+WAVEWriter::WAVEWriter(int fd)
+ : mFd(dup(fd)),
+ mInitCheck(mFd < 0? NO_INIT: OK),
+ mStarted(false),
+ mPaused(false),
+ mResumed(false) {
+}
+
+WAVEWriter::~WAVEWriter() {
+ if (mStarted) {
+ stop();
+ }
+
+ if (mFd != -1) {
+ close(mFd);
+ mFd = -1;
+ }
+}
+
+status_t WAVEWriter::initCheck() const {
+ return mInitCheck;
+}
+
+status_t WAVEWriter::addSource(const sp<MediaSource> &source) {
+ uint32_t count;
+ if (mInitCheck != OK) {
+ ALOGE("Init Check not OK, return");
+ return mInitCheck;
+ }
+
+ if (mSource != NULL) {
+ ALOGE("A source already exists, return");
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MetaData> meta = source->getFormat();
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ int32_t channelCount;
+ int32_t sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &channelCount));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ memset(&hdr, 0, sizeof(struct wav_header));
+ hdr.riff_id = ID_RIFF;
+ hdr.riff_fmt = ID_WAVE;
+ hdr.fmt_id = ID_FMT;
+ hdr.fmt_sz = 16;
+ hdr.audio_format = FORMAT_PCM;
+ hdr.num_channels = channelCount;
+ hdr.sample_rate = sampleRate;
+ hdr.bits_per_sample = 16;
+ hdr.byte_rate = (sampleRate * channelCount * hdr.bits_per_sample) / 8;
+ hdr.block_align = ( hdr.bits_per_sample * channelCount ) / 8;
+ hdr.data_id = ID_DATA;
+ hdr.data_sz = 0;
+ hdr.riff_sz = hdr.data_sz + 44 - 8;
+
+ if (write(mFd, &hdr, sizeof(hdr)) != sizeof(hdr)) {
+ ALOGE("Write header error, return ERROR_IO");
+ return -ERROR_IO;
+ }
+
+ mSource = source;
+
+ return OK;
+}
+
+status_t WAVEWriter::start(MetaData *params) {
+ if (mInitCheck != OK) {
+ ALOGE("Init Check not OK, return");
+ return mInitCheck;
+ }
+
+ if (mSource == NULL) {
+ ALOGE("NULL Source");
+ return UNKNOWN_ERROR;
+ }
+
+ if (mStarted && mPaused) {
+ mPaused = false;
+ mResumed = true;
+ return OK;
+ } else if (mStarted) {
+ ALOGE("Already startd, return");
+ return OK;
+ }
+
+ status_t err = mSource->start();
+
+ if (err != OK) {
+ return err;
+ }
+
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+
+ mReachedEOS = false;
+ mDone = false;
+
+ pthread_create(&mThread, &attr, ThreadWrapper, this);
+ pthread_attr_destroy(&attr);
+
+ mStarted = true;
+
+ return OK;
+}
+
+status_t WAVEWriter::pause() {
+ if (!mStarted) {
+ return OK;
+ }
+ mPaused = true;
+ return OK;
+}
+
+status_t WAVEWriter::stop() {
+ if (!mStarted) {
+ return OK;
+ }
+
+ mDone = true;
+
+ void *dummy;
+ pthread_join(mThread, &dummy);
+
+ status_t err = (status_t) dummy;
+ {
+ status_t status = mSource->stop();
+ if (err == OK &&
+ (status != OK && status != ERROR_END_OF_STREAM)) {
+ err = status;
+ }
+ }
+
+ mStarted = false;
+ return err;
+}
+
+bool WAVEWriter::exceedsFileSizeLimit() {
+ if (mMaxFileSizeLimitBytes == 0) {
+ return false;
+ }
+ return mEstimatedSizeBytes >= mMaxFileSizeLimitBytes;
+}
+
+bool WAVEWriter::exceedsFileDurationLimit() {
+ if (mMaxFileDurationLimitUs == 0) {
+ return false;
+ }
+ return mEstimatedDurationUs >= mMaxFileDurationLimitUs;
+}
+
+// static
+void *WAVEWriter::ThreadWrapper(void *me) {
+ return (void *) static_cast<WAVEWriter *>(me)->threadFunc();
+}
+
+status_t WAVEWriter::threadFunc() {
+ mEstimatedDurationUs = 0;
+ mEstimatedSizeBytes = 0;
+ bool stoppedPrematurely = true;
+ int64_t previousPausedDurationUs = 0;
+ int64_t maxTimestampUs = 0;
+ status_t err = OK;
+
+ prctl(PR_SET_NAME, (unsigned long)"WAVEWriter", 0, 0, 0);
+ hdr.data_sz = 0;
+ while (!mDone) {
+ MediaBuffer *buffer;
+ err = mSource->read(&buffer);
+
+ if (err != OK) {
+ break;
+ }
+
+ if (mPaused) {
+ buffer->release();
+ buffer = NULL;
+ continue;
+ }
+
+ mEstimatedSizeBytes += buffer->range_length();
+ if (exceedsFileSizeLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED, 0);
+ break;
+ }
+
+ int64_t timestampUs;
+ CHECK(buffer->meta_data()->findInt64(kKeyTime, ×tampUs));
+ if (timestampUs > mEstimatedDurationUs) {
+ mEstimatedDurationUs = timestampUs;
+ }
+ if (mResumed) {
+ previousPausedDurationUs += (timestampUs - maxTimestampUs - 20000);
+ mResumed = false;
+ }
+ timestampUs -= previousPausedDurationUs;
+ ALOGV("time stamp: %lld, previous paused duration: %lld",
+ timestampUs, previousPausedDurationUs);
+ if (timestampUs > maxTimestampUs) {
+ maxTimestampUs = timestampUs;
+ }
+
+ if (exceedsFileDurationLimit()) {
+ buffer->release();
+ buffer = NULL;
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_INFO_MAX_DURATION_REACHED, 0);
+ break;
+ }
+ ssize_t n = write(mFd,
+ (const uint8_t *)buffer->data() + buffer->range_offset(),
+ buffer->range_length());
+
+ hdr.data_sz += (ssize_t)buffer->range_length();
+ hdr.riff_sz = hdr.data_sz + 44 - 8;
+
+ if (n < (ssize_t)buffer->range_length()) {
+ buffer->release();
+ buffer = NULL;
+
+ break;
+ }
+
+ if (stoppedPrematurely) {
+ stoppedPrematurely = false;
+ }
+
+ buffer->release();
+ buffer = NULL;
+ }
+
+ if (stoppedPrematurely) {
+ notify(MEDIA_RECORDER_EVENT_INFO, MEDIA_RECORDER_TRACK_INFO_COMPLETION_STATUS, UNKNOWN_ERROR);
+ }
+
+ lseek(mFd, 0, SEEK_SET);
+ write(mFd, &hdr, sizeof(hdr));
+ lseek(mFd, 0, SEEK_END);
+
+ close(mFd);
+ mFd = -1;
+ mReachedEOS = true;
+ if (err == ERROR_END_OF_STREAM) {
+ return OK;
+ }
+ return err;
+}
+
+bool WAVEWriter::reachedEOS() {
+ return mReachedEOS;
+}
+
+} // namespace android
diff --git a/media/libstagefright/avc_utils.cpp b/media/libstagefright/avc_utils.cpp
index b822868..c0f7322 100644
--- a/media/libstagefright/avc_utils.cpp
+++ b/media/libstagefright/avc_utils.cpp
@@ -44,7 +44,7 @@
void FindAVCDimensions(
const sp<ABuffer> &seqParamSet,
int32_t *width, int32_t *height,
- int32_t *sarWidth, int32_t *sarHeight) {
+ int32_t *sarWidth, int32_t *sarHeight, int32_t *isInterlaced) {
ABitReader br(seqParamSet->data() + 1, seqParamSet->size() - 1);
unsigned profile_idc = br.getBits(8);
@@ -63,7 +63,11 @@
parseUE(&br); // bit_depth_luma_minus8
parseUE(&br); // bit_depth_chroma_minus8
br.skipBits(1); // qpprime_y_zero_transform_bypass_flag
- CHECK_EQ(br.getBits(1), 0u); // seq_scaling_matrix_present_flag
+ bool seq_scaling_matrix_present = (br.getBits(1) != 0u);
+ if (isInterlaced != NULL && seq_scaling_matrix_present) {
+ return;
+ }
+ CHECK_EQ(seq_scaling_matrix_present, false); // seq_scaling_matrix_present_flag
}
parseUE(&br); // log2_max_frame_num_minus4
@@ -133,6 +137,10 @@
(frame_crop_top_offset + frame_crop_bottom_offset) * cropUnitY;
}
+ if (isInterlaced != NULL) {
+ *isInterlaced = !frame_mbs_only_flag;
+ }
+
if (sarWidth != NULL) {
*sarWidth = 0;
}
diff --git a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
index 3b3ce0b..bdba267 100644
--- a/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/SoftAMR.cpp
@@ -437,8 +437,12 @@
void SoftAMR::onPortFlushCompleted(OMX_U32 portIndex) {
ALOGE("onPortFlushCompleted portindex %d, resetting frame ",portIndex);
- if(portIndex == 0)
- Speech_Decode_Frame_reset(mState);
+ if(portIndex == 0) {
+ if(mMode == MODE_NARROW)
+ Speech_Decode_Frame_reset(mState);
+ else
+ pvDecoder_AmrWb_Reset(mState, 0);
+ }
}
void SoftAMR::onPortEnableCompleted(OMX_U32 portIndex, bool enabled) {
diff --git a/media/libstagefright/codecs/mp3dec/Android.mk b/media/libstagefright/codecs/mp3dec/Android.mk
index 135c715..9e10527 100644
--- a/media/libstagefright/codecs/mp3dec/Android.mk
+++ b/media/libstagefright/codecs/mp3dec/Android.mk
@@ -2,6 +2,7 @@
include $(CLEAR_VARS)
LOCAL_SRC_FILES := \
+ MP3Decoder.cpp \
src/pvmp3_normalize.cpp \
src/pvmp3_alias_reduction.cpp \
src/pvmp3_crc.cpp \
diff --git a/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
new file mode 100644
index 0000000..c757589
--- /dev/null
+++ b/media/libstagefright/codecs/mp3dec/MP3Decoder.cpp
@@ -0,0 +1,601 @@
+/*
+ * 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 "MP3Decoder.h"
+
+#include "include/pvmp3decoder_api.h"
+
+#include <media/stagefright/MediaBufferGroup.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+#include <cutils/log.h>
+
+#define LITERAL_TO_STRING_INTERNAL(x) #x
+#define LITERAL_TO_STRING(x) LITERAL_TO_STRING_INTERNAL(x)
+
+#define CHECK_EQ(x,y) \
+ LOG_ALWAYS_FATAL_IF( \
+ (x) != (y), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x " != " #y)
+
+#define CHECK(x) \
+ LOG_ALWAYS_FATAL_IF( \
+ !(x), \
+ __FILE__ ":" LITERAL_TO_STRING(__LINE__) " " #x)
+
+
+namespace android {
+
+// Everything must match except for
+// protection, bitrate, padding, private bits, mode extension,
+// copyright bit, original bit and emphasis.
+// Yes ... there are things that must indeed match...
+static const uint32_t kMask = 0xfffe0cc0;
+
+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;
+ }
+
+ if (version == 3 /* V1 */) {
+ *frame_size = 144000 * bitrate / sampling_rate + padding;
+ } else {
+ // V2 or V2.5
+ *frame_size = 72000 * 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(
+ uint8_t *data, uint32_t size, uint32_t match_header, off_t *out_pos) {
+
+ bool valid = false;
+ off_t pos = 0;
+ *out_pos = 0;
+ do {
+ if (pos + 4 > size) {
+ // Don't scan forever.
+ ALOGV("no dice, no valid sequence of frames found.");
+ break;
+ }
+
+ uint32_t header = U32_AT(data + pos);
+
+ if (match_header != 0 && (header & kMask) != (match_header & kMask)) {
+ ++pos;
+ continue;
+ }
+
+ ALOGV("found possible frame at %ld (header = 0x%08x)", pos, header);
+
+ // We found what looks like a valid frame,
+ valid = true;
+ *out_pos = pos;
+ } while (!valid);
+
+ return valid;
+}
+
+
+MP3Decoder::MP3Decoder(const sp<MediaSource> &source)
+ : mSource(source),
+ mNumChannels(0),
+ mStarted(false),
+ mBufferGroup(NULL),
+ mConfig(new tPVMP3DecoderExternal),
+ mDecoderBuf(NULL),
+ mAnchorTimeUs(0),
+ mNumFramesOutput(0),
+ mInputBuffer(NULL),
+ mPartialBuffer(NULL),
+ mFixedHeader(0) {
+ init();
+}
+
+void MP3Decoder::init() {
+ sp<MetaData> srcFormat = mSource->getFormat();
+
+ int32_t sampleRate;
+ CHECK(srcFormat->findInt32(kKeyChannelCount, &mNumChannels));
+ CHECK(srcFormat->findInt32(kKeySampleRate, &sampleRate));
+
+ mMeta = new MetaData;
+ mMeta->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_RAW);
+ mMeta->setInt32(kKeyChannelCount, mNumChannels);
+ mMeta->setInt32(kKeySampleRate, sampleRate);
+
+ int64_t durationUs;
+ if (srcFormat->findInt64(kKeyDuration, &durationUs)) {
+ mMeta->setInt64(kKeyDuration, durationUs);
+ }
+
+ mMeta->setCString(kKeyDecoderComponent, "MP3Decoder");
+}
+
+MP3Decoder::~MP3Decoder() {
+ if (mStarted) {
+ stop();
+ }
+
+ delete mConfig;
+ mConfig = NULL;
+}
+
+status_t MP3Decoder::start(MetaData *params) {
+ CHECK(!mStarted);
+
+ mBufferGroup = new MediaBufferGroup;
+ mBufferGroup->add_buffer(new MediaBuffer(4608 * 2));
+
+ mConfig->equalizerType = flat;
+ mConfig->crcEnabled = false;
+
+ uint32_t memRequirements = pvmp3_decoderMemRequirements();
+ mDecoderBuf = malloc(memRequirements);
+
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+
+ mSource->start();
+
+ mAnchorTimeUs = 0;
+ mNumFramesOutput = 0;
+ mStarted = true;
+
+ return OK;
+}
+
+status_t MP3Decoder::stop() {
+ CHECK(mStarted);
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ free(mDecoderBuf);
+ mDecoderBuf = NULL;
+
+ delete mBufferGroup;
+ mBufferGroup = NULL;
+
+ mSource->stop();
+
+ mStarted = false;
+
+ return OK;
+}
+
+sp<MetaData> MP3Decoder::getFormat() {
+ return mMeta;
+}
+
+status_t MP3Decoder::updatePartialFrame() {
+ status_t err = OK;
+ if (mPartialBuffer == NULL) {
+ return err;
+ }
+
+ size_t frameSize = 0;
+ uint32_t partialBufLen = mPartialBuffer->range_length();
+ uint32_t inputBufLen = mInputBuffer->range_length();
+ uint8_t frameHeader[4];
+ uint8_t *frmHdr;
+ uint32_t header;
+
+
+ // Look at the frame size and complete the partial frame
+ // Also check if a vaild header is found after the partial frame
+ if (partialBufLen < 4) { // check if partial frame has the 4 bytes header
+ if (inputBufLen < (4 - partialBufLen)) {
+ // input buffer does not have the frame header bytes
+ // bail out TODO
+ ALOGE("MP3Decoder::updatePartialFrame buffer to small header not found"
+ " partial buffer len %d, input buffer len %d",
+ partialBufLen, inputBufLen);
+ //mPartialBuffer->release();
+ //mPartialBuffer = NULL;
+ return UNKNOWN_ERROR;
+ }
+
+ // copy the header bytes to frameHeader
+ memcpy (frameHeader, mPartialBuffer->data(), partialBufLen);
+ memcpy (frameHeader + partialBufLen, mInputBuffer->data(), (4 - partialBufLen));
+ // get the first 4 bytes of the buffer
+ header = U32_AT((uint8_t *)frameHeader);
+ frmHdr = frameHeader;
+ } else {
+ frmHdr = (uint8_t *)mPartialBuffer->data();
+ }
+
+ // check if its a good frame, and the frame size
+ // get the first 4 bytes of the buffer
+ header = U32_AT(frmHdr);
+ bool curFrame = get_mp3_frame_size(header,&frameSize);
+ if (!curFrame) {
+ ALOGE("MP3Decoder::read - partial frame does not have a vaild header 0x%x",
+ header);
+ return UNKNOWN_ERROR;
+ }
+
+ // check if the following frame is good
+ uint32_t nextFrameOffset = frameSize - partialBufLen;
+ if ((nextFrameOffset + 4) <= inputBufLen) {
+ header = U32_AT((uint8_t *)mInputBuffer->data() + nextFrameOffset);
+ if ((header & 0xffe00000) != 0xffe00000) {
+ // next frame does not have a valid header,
+ // this may not be the next buffer, bail out.
+ ALOGE("MP3Decoder::read - next frame does not have a vaild header 0x%x",
+ header);
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ // next frame header is out of range
+ // assume good header for now
+ ALOGE("MP3Decoder::read - assuming next frame is good");
+ }
+
+ // check if the input buffer has the remaining partial frame
+ if (frameSize > (partialBufLen + inputBufLen)) {
+ // input buffer does not have the remaining partial frame,
+ // discard data here as frame split in 3 buffers not supported
+ ALOGE("MP3Decoder::updatePartialFrame - input buffer does not have the complete frame."
+ " frame size %d, saved partial buffer len %d,"
+ " input buffer len %d", frameSize, partialBufLen, inputBufLen);
+ return UNKNOWN_ERROR;
+ }
+
+ // check if the mPartialBuffer can fit the remaining frame
+ if ((mPartialBuffer->size() - partialBufLen) < (frameSize - partialBufLen)) {
+ // mPartialBuffer is small to hold the reaming frame
+ //TODO
+ ALOGE("MP3Decoder::updatePartialFrame - mPartialBuffer is small, size %d, required &d",
+ (mPartialBuffer->size() - partialBufLen), (frameSize - partialBufLen));
+ return UNKNOWN_ERROR;
+ }
+
+ // done with error checks
+ // copy the partial frames to from a complete frame
+ // Copy the remaining frame from input buffer
+ uint32_t bytesRemaining = frameSize - mPartialBuffer->range_length();
+ memcpy ((uint8_t *)mPartialBuffer->data() + mPartialBuffer->range_length(),
+ (uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset(),
+ bytesRemaining);
+
+ // mark the bytes as consumed from input buffer
+ mInputBuffer->set_range(
+ mInputBuffer->range_offset() + bytesRemaining,
+ mInputBuffer->range_length() - bytesRemaining);
+
+ // set the range and length of mPartialBuffer
+ mPartialBuffer->set_range(0,
+ mPartialBuffer->range_length() + bytesRemaining);
+
+ ALOGE("MP3Decoder::updatePartialFrame - copied the partial frame %d, input buffer length %d",
+ bytesRemaining, mInputBuffer->range_length());
+
+ return err;
+}
+
+status_t MP3Decoder::read(
+ MediaBuffer **out, const ReadOptions *options) {
+ status_t err;
+
+ *out = NULL;
+ bool usedPartialFrame = false;
+ bool seekSource = false;
+
+ int64_t seekTimeUs;
+ ReadOptions::SeekMode mode;
+ if (options && options->getSeekTo(&seekTimeUs, &mode)) {
+ CHECK(seekTimeUs >= 0);
+
+ mNumFramesOutput = 0;
+ seekSource = true;
+
+ if (mInputBuffer) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ if (mPartialBuffer) {
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ }
+
+ // Make sure that the next buffer output does not still
+ // depend on fragments from the last one decoded.
+ pvmp3_InitDecoder(mConfig, mDecoderBuf);
+ } else {
+ seekTimeUs = -1;
+ }
+
+ if (mInputBuffer == NULL) {
+ err = mSource->read(&mInputBuffer, options);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if ((mFixedHeader == 0) && (mInputBuffer->range_length() > 4)) {
+ //save the first 4 bytes as fixed header for the reset of the file
+ mFixedHeader = U32_AT((uint8_t *)mInputBuffer->data());
+ }
+
+ if (seekSource == true) {
+ off_t syncOffset = 0;
+ bool valid = resync((uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset()
+ ,mInputBuffer->range_length(), mFixedHeader, &syncOffset);
+ if (valid) {
+ // consume these bytes, we might find a frame header in next buffer
+ mInputBuffer->set_range(
+ mInputBuffer->range_offset() + syncOffset,
+ mInputBuffer->range_length() - syncOffset);
+ ALOGV("mp3 decoder found a sync point after seek syncOffset %d", syncOffset);
+ } else {
+ ALOGV("NO SYNC POINT found, buffer length %d",mInputBuffer->range_length());
+ }
+ }
+
+ int64_t timeUs;
+ if (mInputBuffer->meta_data()->findInt64(kKeyTime, &timeUs)) {
+ mAnchorTimeUs = timeUs;
+ mNumFramesOutput = 0;
+ } else {
+ // We must have a new timestamp after seeking.
+ CHECK(seekTimeUs < 0);
+ }
+ // check for partial frame
+ if (mPartialBuffer != NULL) {
+ err = updatePartialFrame();
+ if (err != OK) {
+ // updating partial frame failed, discard the previously
+ // saved partial frame and continue
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ err = OK;
+ }
+ }
+ }
+
+ MediaBuffer *buffer;
+ CHECK_EQ(mBufferGroup->acquire_buffer(&buffer), OK);
+
+ if (mPartialBuffer != NULL) {
+ mConfig->pInputBuffer =
+ (uint8_t *)mPartialBuffer->data() + mPartialBuffer->range_offset();
+ mConfig->inputBufferCurrentLength = mPartialBuffer->range_length();
+ usedPartialFrame = true;
+ } else {
+ mConfig->pInputBuffer =
+ (uint8_t *)mInputBuffer->data() + mInputBuffer->range_offset();
+ mConfig->inputBufferCurrentLength = mInputBuffer->range_length();
+ }
+
+ mConfig->inputBufferMaxLength = 0;
+ mConfig->inputBufferUsedLength = 0;
+
+ mConfig->outputFrameSize = buffer->size() / sizeof(int16_t);
+ mConfig->pOutputBuffer = static_cast<int16_t *>(buffer->data());
+
+ ERROR_CODE decoderErr;
+ if ((decoderErr = pvmp3_framedecoder(mConfig, mDecoderBuf))
+ != NO_DECODING_ERROR) {
+ ALOGV("mp3 decoder returned error %d", decoderErr);
+
+ if ((decoderErr != NO_ENOUGH_MAIN_DATA_ERROR) &&
+ (decoderErr != SYNCH_LOST_ERROR)) {
+ buffer->release();
+ buffer = NULL;
+
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ if (mPartialBuffer) {
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ }
+ ALOGE("mp3 decoder returned UNKNOWN_ERROR");
+
+ return UNKNOWN_ERROR;
+ }
+
+ if ((mPartialBuffer == NULL) && (decoderErr == NO_ENOUGH_MAIN_DATA_ERROR)) {
+ // Might be a partial frame, save it
+ mPartialBuffer = new MediaBuffer(mInputBuffer->size());
+ memcpy ((uint8_t *)mPartialBuffer->data(),
+ mConfig->pInputBuffer, mConfig->inputBufferCurrentLength);
+ mPartialBuffer->set_range(0, mConfig->inputBufferCurrentLength);
+ // set output buffer to 0
+ mConfig->outputFrameSize = 0;
+ // consume the copied bytes from input
+ mConfig->inputBufferUsedLength = mConfig->inputBufferCurrentLength;
+ } else if(decoderErr == SYNCH_LOST_ERROR) {
+ // Try to find the mp3 frame header in the current buffer
+ off_t syncOffset = 0;
+ bool valid = resync(mConfig->pInputBuffer, mConfig->inputBufferCurrentLength,
+ mFixedHeader, &syncOffset);
+ if (!valid || !syncOffset) {
+ // consume these bytes, we might find a frame header in next buffer
+ syncOffset = mConfig->inputBufferCurrentLength;
+ }
+ // set output buffer to 0
+ mConfig->outputFrameSize = 0;
+ // consume the junk bytes from input buffer
+ mConfig->inputBufferUsedLength = syncOffset;
+ } else {
+ // This is recoverable, just ignore the current frame and
+ // play silence instead.
+ memset(buffer->data(), 0, mConfig->outputFrameSize * sizeof(int16_t));
+ mConfig->inputBufferUsedLength = mInputBuffer->range_length();
+ }
+ }
+
+ buffer->set_range(
+ 0, mConfig->outputFrameSize * sizeof(int16_t));
+
+ if ((mPartialBuffer != NULL) && usedPartialFrame) {
+ mPartialBuffer->set_range(
+ mPartialBuffer->range_offset() + mConfig->inputBufferUsedLength,
+ mPartialBuffer->range_length() - mConfig->inputBufferUsedLength);
+ mPartialBuffer->release();
+ mPartialBuffer = NULL;
+ } else {
+ mInputBuffer->set_range(
+ mInputBuffer->range_offset() + mConfig->inputBufferUsedLength,
+ mInputBuffer->range_length() - mConfig->inputBufferUsedLength);
+ }
+
+ if (mInputBuffer->range_length() == 0) {
+ mInputBuffer->release();
+ mInputBuffer = NULL;
+ }
+
+ buffer->meta_data()->setInt64(
+ kKeyTime,
+ mAnchorTimeUs
+ + (mNumFramesOutput * 1000000) / mConfig->samplingRate);
+
+ mNumFramesOutput += mConfig->outputFrameSize / mNumChannels;
+
+ *out = buffer;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/include/AwesomePlayer.h b/media/libstagefright/include/AwesomePlayer.h
index 271df8e..dd6c409 100644
--- a/media/libstagefright/include/AwesomePlayer.h
+++ b/media/libstagefright/include/AwesomePlayer.h
@@ -1,4 +1,6 @@
/*
+ * Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ * Not a Contribution.
* Copyright (C) 2009 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
@@ -102,6 +104,7 @@
void postAudioEOS(int64_t delayUs = 0ll);
void postAudioSeekComplete();
void postAudioTearDown();
+ void printFileName(int fd);
status_t dump(int fd, const Vector<String16> &args) const;
private:
@@ -204,6 +207,7 @@
bool mWatchForAudioSeekComplete;
bool mWatchForAudioEOS;
+ static int mTunnelAliveAP;
sp<TimedEventQueue::Event> mVideoEvent;
bool mVideoEventPending;
@@ -241,6 +245,7 @@
sp<DecryptHandle> mDecryptHandle;
int64_t mLastVideoTimeUs;
+ int64_t mFrameDurationUs;
TimedTextDriver *mTextDriver;
sp<WVMExtractor> mWVMExtractor;
@@ -313,6 +318,15 @@
ASSIGN
};
void modifyFlags(unsigned value, FlagMode mode);
+ void checkTunnelExceptions();
+ void logFirstFrame();
+ void logCatchUp(int64_t ts, int64_t clock, int64_t delta);
+ void logLate(int64_t ts, int64_t clock, int64_t delta);
+ void logOnTime(int64_t ts, int64_t clock, int64_t delta);
+ void printStats();
+ int64_t getTimeOfDayUs();
+ bool mStatistics;
+ int64_t mLateAVSyncMargin;
struct TrackStat {
String8 mMIME;
@@ -338,6 +352,23 @@
int32_t mVideoHeight;
uint32_t mFlags;
Vector<TrackStat> mTracks;
+
+ int64_t mConsecutiveFramesDropped;
+ uint32_t mCatchupTimeStart;
+ uint32_t mNumTimesSyncLoss;
+ uint32_t mMaxEarlyDelta;
+ uint32_t mMaxLateDelta;
+ uint32_t mMaxTimeSyncLoss;
+ uint64_t mTotalFrames;
+ int64_t mFirstFrameLatencyStartUs; //first frame latency start
+ int64_t mFirstFrameLatencyUs;
+ int64_t mLastFrameUs;
+ bool mVeryFirstFrame;
+ int64_t mTotalTimeUs;
+ int64_t mLastPausedTimeMs;
+ int64_t mLastSeekToTimeMs;
+ int64_t mResumeDelayStartUs;
+ int64_t mSeekDelayStartUs;
} mStats;
bool mOffloadAudio;
@@ -357,6 +388,9 @@
size_t countTracks() const;
+ bool inSupportedTunnelFormats(const char * mime);
+ //Flag to check if tunnel mode audio is enabled
+ bool mIsTunnelAudio;
AwesomePlayer(const AwesomePlayer &);
AwesomePlayer &operator=(const AwesomePlayer &);
};
diff --git a/media/libstagefright/include/ExtendedExtractor.h b/media/libstagefright/include/ExtendedExtractor.h
new file mode 100644
index 0000000..3bce1ff
--- /dev/null
+++ b/media/libstagefright/include/ExtendedExtractor.h
@@ -0,0 +1,49 @@
+/*Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+
+#ifndef EXTENDED_EXTRACTOR_
+#define EXTENDED_EXTRACTOR_
+
+#include <media/stagefright/DataSource.h>
+
+namespace android {
+
+class MediaExtractor;
+class ExtendedExtractor
+{
+public:
+ static MediaExtractor* Create (
+ const sp<DataSource> &source, const char *mime);
+ static bool Sniff (
+ const sp<DataSource> &source, String8 *mimeType,
+ float *confidence,sp<AMessage> *meta);
+};
+
+} // namespace android
+
+#endif //EXTENDED_EXTRACTOR_
diff --git a/media/libstagefright/include/ExtendedUtils.h b/media/libstagefright/include/ExtendedUtils.h
new file mode 100644
index 0000000..1c3f3aa
--- /dev/null
+++ b/media/libstagefright/include/ExtendedUtils.h
@@ -0,0 +1,151 @@
+/*Copyright (c) 2013, The Linux Foundation. All rights reserved.
+ *
+ * Redistribution and use in source and binary forms, with or without
+ * modification, are permitted provided that the following conditions are
+ * met:
+ * * Redistributions of source code must retain the above copyright
+ * notice, this list of conditions and the following disclaimer.
+ * * Redistributions in binary form must reproduce the above
+ * copyright notice, this list of conditions and the following
+ * disclaimer in the documentation and/or other materials provided
+ * with the distribution.
+ * * Neither the name of The Linux Foundation nor the names of its
+ * contributors may be used to endorse or promote products derived
+ * from this software without specific prior written permission.
+ *
+ * THIS SOFTWARE IS PROVIDED "AS IS" AND ANY EXPRESS OR IMPLIED
+ * WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF
+ * MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NON-INFRINGEMENT
+ * ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT OWNER OR CONTRIBUTORS
+ * BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
+ * CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
+ * SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR
+ * BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY,
+ * WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE
+ * OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN
+ * IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
+ */
+#ifndef QC_UTILS_H_
+#define QC_UTILS_H_
+
+#include <utils/StrongPointer.h>
+#include <media/Metadata.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/foundation/AString.h>
+#include <media/stagefright/MediaCodecList.h>
+
+#include <media/MediaRecorderBase.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <camera/CameraParameters.h>
+#include <OMX_Video.h>
+#include <android/native_window.h>
+
+#define MIN_BITERATE_AAC 24000
+#define MAX_BITERATE_AAC 192000
+
+namespace android {
+
+/*
+ * This class is a placeholder for utility methods for
+ * QC specific changes
+ */
+struct ExtendedUtils {
+
+ /*
+ * This class is a placeholder for set of methods used
+ * to enable HFR (High Frame Rate) Recording
+ *
+ * HFR is a slow-motion recording feature where framerate
+ * is increased at capture, but file is composed to play
+ * back at normal rate, giving a net result of slow-motion.
+ * If HFR factor = N
+ * framerate (at capture and encoder) = N * actual value
+ * timeStamps (at composition) = N * actual value
+ * bitrate = N * actual value
+ * (as the encoder still gets actual timestamps)
+ */
+ struct HFR {
+ // set kKeyHFR when 'video-hfr' paramater is enabled
+ static void setHFRIfEnabled(
+ const CameraParameters& params, sp<MetaData> &meta);
+
+ // recalculate fileduration when hfr is enabled
+ static status_t reCalculateFileDuration(
+ sp<MetaData> &meta, sp<MetaData> &enc_meta,
+ int64_t &maxFileDurationUs, int32_t frameRate,
+ video_encoder videoEncoder);
+
+ // compute timestamp when hfr is enabled
+ static void reCalculateTimeStamp(
+ sp<MetaData> &meta, int64_t ×tampUs);
+
+ // recalculate frameRate and bitrate when hfr is enabled
+ static void reCalculateHFRParams(
+ const sp<MetaData> &meta, int32_t &frameRate,
+ int32_t &bitrate);
+
+ // Copy HFR params (bitrate,framerate) from output to
+ // to input format, if HFR is enabled
+ static void copyHFRParams(
+ const sp<MetaData> &inputFormat,
+ sp<MetaData> &outputFormat);
+ };
+
+ /*
+ * This class is a placeholder for methods to override
+ * default heaviour based on shell properties set
+ */
+ struct ShellProp {
+ // check if shell property to disable audio is set
+ static bool isAudioDisabled();
+
+ //helper function to set encoding profiles
+ static void setEncoderProfile(video_encoder &videoEncoder,
+ int32_t &videoEncoderProfile);
+
+ static bool isSmoothStreamingEnabled();
+
+ static int64_t getMaxAVSyncLateMargin();
+ };
+
+ //set B frames for MPEG4
+ static void setBFrames(OMX_VIDEO_PARAM_MPEG4TYPE &mpeg4type, int32_t &numBFrames,
+ const char* componentName);
+
+ //set B frames for H264
+ static void setBFrames(OMX_VIDEO_PARAM_AVCTYPE &h264type, int32_t &numBFrames,
+ int32_t iFramesInterval, int32_t frameRate, const char* componentName);
+
+ static bool UseQCHWAACEncoder(audio_encoder Encoder, int32_t Channel,
+ int32_t BitRate, int32_t SampleRate);
+
+ static sp<MediaExtractor> MediaExtractor_CreateIfNeeded(
+ sp<MediaExtractor> defaultExt, const sp<DataSource> &source,
+ const char *mime);
+
+ //helper function to add media codecs with specific quirks
+ static void helper_addMediaCodec(Vector<MediaCodecList::CodecInfo> &mCodecInfos,
+ KeyedVector<AString, size_t> &mTypes,
+ bool encoder, const char *name,
+ const char *type, uint32_t quirks);
+
+ //helper function to calculate the value of quirks from strings
+ static uint32_t helper_getCodecSpecificQuirks(KeyedVector<AString, size_t> &mCodecQuirks,
+ Vector<AString> quirks);
+
+ static bool isAVCProfileSupported(int32_t profile);
+
+ //notify stride change to ANW
+ static void updateNativeWindowBufferGeometry(ANativeWindow* anw,
+ OMX_U32 width, OMX_U32 height, OMX_COLOR_FORMATTYPE colorFormat);
+
+ static bool checkIsThumbNailMode(const uint32_t flags, char* componentName);
+
+ static void setArbitraryModeIfInterlaced(
+ const uint8_t *ptr, const sp<MetaData> &meta);
+
+ static int32_t checkIsInterlace(sp<MetaData> &meta);
+};
+
+}
+#endif //QC_UTILS_H_
diff --git a/media/libstagefright/include/MP3Decoder.h b/media/libstagefright/include/MP3Decoder.h
new file mode 100644
index 0000000..8ff570a
--- /dev/null
+++ b/media/libstagefright/include/MP3Decoder.h
@@ -0,0 +1,70 @@
+/*
+ * 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_DECODER_H_
+
+#define MP3_DECODER_H_
+
+#include <media/stagefright/MediaSource.h>
+
+struct tPVMP3DecoderExternal;
+
+namespace android {
+
+struct MediaBufferGroup;
+
+struct MP3Decoder : public MediaSource {
+ MP3Decoder(const sp<MediaSource> &source);
+
+ virtual status_t start(MetaData *params);
+ virtual status_t stop();
+
+ virtual sp<MetaData> getFormat();
+
+ virtual status_t read(
+ MediaBuffer **buffer, const ReadOptions *options);
+
+protected:
+ virtual ~MP3Decoder();
+
+private:
+ sp<MediaSource> mSource;
+ sp<MetaData> mMeta;
+ int32_t mNumChannels;
+
+ bool mStarted;
+
+ MediaBufferGroup *mBufferGroup;
+
+ tPVMP3DecoderExternal *mConfig;
+ void *mDecoderBuf;
+ int64_t mAnchorTimeUs;
+ int64_t mNumFramesOutput;
+ uint32_t mFixedHeader;
+
+ MediaBuffer *mInputBuffer;
+ MediaBuffer *mPartialBuffer;
+
+ void init();
+
+ MP3Decoder(const MP3Decoder &);
+ MP3Decoder &operator=(const MP3Decoder &);
+ status_t updatePartialFrame();
+};
+
+} // namespace android
+
+#endif // MP3_DECODER_H_
diff --git a/media/libstagefright/include/avc_utils.h b/media/libstagefright/include/avc_utils.h
index d517320..8e67308 100644
--- a/media/libstagefright/include/avc_utils.h
+++ b/media/libstagefright/include/avc_utils.h
@@ -40,7 +40,7 @@
void FindAVCDimensions(
const sp<ABuffer> &seqParamSet,
int32_t *width, int32_t *height,
- int32_t *sarWidth = NULL, int32_t *sarHeight = NULL);
+ int32_t *sarWidth = NULL, int32_t *sarHeight = NULL, int32_t *isInterlaced = NULL);
unsigned parseUE(ABitReader *br);
diff --git a/media/libstagefright/mpeg2ts/ESQueue.cpp b/media/libstagefright/mpeg2ts/ESQueue.cpp
index 8f9c9c8..0679a1a 100644
--- a/media/libstagefright/mpeg2ts/ESQueue.cpp
+++ b/media/libstagefright/mpeg2ts/ESQueue.cpp
@@ -504,7 +504,9 @@
if (first) {
timeUs = info->mTimestampUs;
- first = false;
+ if(mMode != AAC) {
+ first = false;
+ }
}
if (info->mLength > size) {
diff --git a/services/audioflinger/Android.mk b/services/audioflinger/Android.mk
index 54377f1..b944062 100644
--- a/services/audioflinger/Android.mk
+++ b/services/audioflinger/Android.mk
@@ -46,6 +46,14 @@
libdl \
libpowermanager
+# SRS Processing
+ifeq ($(strip $(BOARD_USES_SRS_TRUEMEDIA)),true)
+LOCAL_SHARED_LIBRARIES += libsrsprocessing
+LOCAL_CFLAGS += -DSRS_PROCESSING
+LOCAL_C_INCLUDES += $(TARGET_OUT_HEADERS)/mm-audio/audio-effects
+endif
+# SRS Processing
+
LOCAL_STATIC_LIBRARIES := \
libscheduling_policy \
libcpustats \
diff --git a/services/audioflinger/AudioFlinger.cpp b/services/audioflinger/AudioFlinger.cpp
index dd47cf6..cef551c 100644
--- a/services/audioflinger/AudioFlinger.cpp
+++ b/services/audioflinger/AudioFlinger.cpp
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2011-2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -63,6 +64,11 @@
#include <media/AudioParameter.h>
#include <private/android_filesystem_config.h>
+#ifdef SRS_PROCESSING
+#include "srs_processing.h"
+#include "postpro_patch_ics.h"
+#endif
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -148,7 +154,8 @@
mBtNrecIsOff(false),
mIsLowRamDevice(true),
mIsDeviceTypeKnown(false),
- mGlobalEffectEnableTime(0)
+ mGlobalEffectEnableTime(0),
+ mAllChainsLocked(false)
{
getpid_cached = getpid();
char value[PROPERTY_VALUE_MAX];
@@ -563,9 +570,122 @@
return trackHandle;
}
+sp<IDirectTrack> AudioFlinger::createDirectTrack(
+ pid_t pid,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_io_handle_t output,
+ int *sessionId,
+ IDirectTrackClient *client,
+ audio_stream_type_t streamType,
+ status_t *status)
+{
+ *status = NO_ERROR;
+ status_t lStatus = NO_ERROR;
+ sp<IDirectTrack> track = NULL;
+ DirectAudioTrack* directTrack = NULL;
+ Mutex::Autolock _l(mLock);
+
+ ALOGV("createDirectTrack() sessionId: %d sampleRate %d channelMask %d",
+ *sessionId, sampleRate, channelMask);
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output);
+ if(desc == NULL) {
+ ALOGE("Error: Invalid output (%d) to create direct audio track", output);
+ lStatus = BAD_VALUE;
+ goto Exit;
+ }
+ desc->mStreamType = streamType;
+ if (desc->flag & AUDIO_OUTPUT_FLAG_LPA) {
+ if (sessionId != NULL && *sessionId != AUDIO_SESSION_OUTPUT_MIX) {
+ for (size_t i = 0; i < mPlaybackThreads.size(); i++) {
+ sp<PlaybackThread> t = mPlaybackThreads.valueAt(i);
+ // Check if the session ID is already associated with a track
+ uint32_t sessions = t->hasAudioSession(*sessionId);
+
+ // check if an effect with same session ID is waiting for a ssession to be created
+ ALOGV("check if an effect with same session ID is waiting for a ssession to be created");
+ if ((mLPAEffectChain == NULL) && (sessions & PlaybackThread::EFFECT_SESSION)) {
+ // Clear reference to previous effect chain if any
+ t->mLock.lock();
+ ALOGV("getting the LPA effect chain and setting LPA flag to true.");
+ mLPAEffectChain = t->getEffectChain_l(*sessionId);
+ t->mLock.unlock();
+ }
+ }
+ mLPASessionId = *sessionId;
+ if (mLPAEffectChain != NULL) {
+ mLPAEffectChain->setLPAFlag(true);
+ // For LPA, the volume will be applied in DSP. No need for volume
+ // control in the Effect chain, so setting it to unity.
+ uint32_t volume = 0x1000000; // Equals to 1.0 in 8.24 format
+ mLPAEffectChain->setVolume_l(&volume,&volume);
+ } else {
+ ALOGW("There was no effectChain created for the sessionId(%d)", mLPASessionId);
+ }
+ mLPASampleRate = sampleRate;
+ mLPANumChannels = popcount(channelMask);
+ } else {
+ if(sessionId != NULL) {
+ ALOGE("Error: Invalid sessionID (%d) for direct audio track", *sessionId);
+ }
+ }
+ }
+ mLock.unlock();
+ directTrack = new DirectAudioTrack(this, output, desc, client, desc->flag);
+ desc->trackRefPtr = dynamic_cast<void *>(directTrack);
+ mLock.lock();
+ if (directTrack != 0) {
+ track = dynamic_cast<IDirectTrack *>(directTrack);
+ AudioEventObserver* obv = dynamic_cast<AudioEventObserver *>(directTrack);
+ ALOGE("setting observer mOutputDesc track %p, obv %p", track.get(), obv);
+ desc->stream->set_observer(desc->stream, reinterpret_cast<void *>(obv));
+ } else {
+ lStatus = BAD_VALUE;
+ }
+Exit:
+ if(lStatus) {
+ if (track != NULL) {
+ track.clear();
+ }
+ *status = lStatus;
+ }
+ return track;
+}
+
+void AudioFlinger::deleteEffectSession()
+{
+ ALOGV("deleteSession");
+ // -2 is invalid session ID
+ mLPASessionId = -2;
+ if (mLPAEffectChain != NULL) {
+ mLPAEffectChain->lock();
+ mLPAEffectChain->setLPAFlag(false);
+ size_t i, numEffects = mLPAEffectChain->getNumEffects();
+ for(i = 0; i < numEffects; i++) {
+ sp<EffectModule> effect = mLPAEffectChain->getEffectFromIndex_l(i);
+ effect->setInBuffer(mLPAEffectChain->inBuffer());
+ if (i == numEffects-1) {
+ effect->setOutBuffer(mLPAEffectChain->outBuffer());
+ } else {
+ effect->setOutBuffer(mLPAEffectChain->inBuffer());
+ }
+ effect->configure();
+ }
+ mLPAEffectChain->unlock();
+ mLPAEffectChain.clear();
+ mLPAEffectChain = NULL;
+ }
+}
+
uint32_t AudioFlinger::sampleRate(audio_io_handle_t output) const
{
Mutex::Autolock _l(mLock);
+ if (!mDirectAudioTracks.isEmpty()) {
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output);
+ if(desc != NULL) {
+ return desc->stream->common.get_sample_rate(&desc->stream->common);
+ }
+ }
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
ALOGW("sampleRate() unknown thread %d", output);
@@ -577,6 +697,10 @@
int AudioFlinger::channelCount(audio_io_handle_t output) const
{
Mutex::Autolock _l(mLock);
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output);
+ if(desc != NULL) {
+ return desc->stream->common.get_channels(&desc->stream->common);
+ }
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
ALOGW("channelCount() unknown thread %d", output);
@@ -599,6 +723,10 @@
size_t AudioFlinger::frameCount(audio_io_handle_t output) const
{
Mutex::Autolock _l(mLock);
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output);
+ if(desc != NULL) {
+ return desc->stream->common.get_buffer_size(&desc->stream->common);
+ }
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
ALOGW("frameCount() unknown thread %d", output);
@@ -612,6 +740,13 @@
uint32_t AudioFlinger::latency(audio_io_handle_t output) const
{
Mutex::Autolock _l(mLock);
+ if (!mDirectAudioTracks.isEmpty()) {
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output);
+ if (desc != NULL) {
+ return desc->stream->get_latency(desc->stream);
+ }
+ }
+
PlaybackThread *thread = checkPlaybackThread_l(output);
if (thread == NULL) {
ALOGW("latency(): no playback thread found for output handle %d", output);
@@ -802,10 +937,29 @@
}
AutoMutex lock(mLock);
+ ALOGV("setStreamVolume stream %d, output %d, value %f",stream, output, value);
+ AudioSessionDescriptor *desc = NULL;
+ if (!mDirectAudioTracks.isEmpty()) {
+ desc = mDirectAudioTracks.valueFor(output);
+ if (desc != NULL) {
+ ALOGV("setStreamVolume for mAudioTracks size %d desc %p",mDirectAudioTracks.size(),desc);
+ if (desc->mStreamType == stream) {
+ mStreamTypes[stream].volume = value;
+ desc->mVolumeScale = value;
+ desc->stream->set_volume(desc->stream,
+ desc->mVolumeLeft * mStreamTypes[stream].volume,
+ desc->mVolumeRight* mStreamTypes[stream].volume);
+ return NO_ERROR;
+ }
+ }
+ }
PlaybackThread *thread = NULL;
if (output) {
thread = checkPlaybackThread_l(output);
if (thread == NULL) {
+ if (desc != NULL) {
+ return NO_ERROR;
+ }
return BAD_VALUE;
}
}
@@ -888,6 +1042,11 @@
// ioHandle == 0 means the parameters are global to the audio hardware interface
if (ioHandle == 0) {
Mutex::Autolock _l(mLock);
+#ifdef SRS_PROCESSING
+ POSTPRO_PATCH_ICS_PARAMS_SET(keyValuePairs);
+ if (!mDirectAudioTracks.isEmpty())
+ audioConfigChanged_l(AudioSystem::EFFECT_CONFIG_CHANGED, 0, NULL);
+#endif
status_t final_result = NO_ERROR;
{
AutoMutex lock(mHardwareLock);
@@ -935,6 +1094,29 @@
return final_result;
}
+ AudioSessionDescriptor *desc = NULL;
+ if (!mDirectAudioTracks.isEmpty()) {
+ desc = mDirectAudioTracks.valueFor(ioHandle);
+ if (desc != NULL) {
+ ALOGV("setParameters for mAudioTracks size %d desc %p",mDirectAudioTracks.size(),desc);
+ desc->stream->common.set_parameters(&desc->stream->common, keyValuePairs.string());
+ AudioParameter param = AudioParameter(keyValuePairs);
+ String8 key = String8(AudioParameter::keyRouting);
+ int device;
+ if (param.getInt(key, device) == NO_ERROR) {
+ mDirectDevice = device;
+#ifdef SRS_PROCESSING
+ ALOGV("setParameters:: routing change to device %d", device);
+ POSTPRO_PATCH_ICS_OUTPROC_MIX_ROUTE(desc->trackRefPtr, param, device);
+#endif
+ if(mLPAEffectChain != NULL){
+ mLPAEffectChain->setDevice_l(device);
+ audioConfigChanged_l(AudioSystem::EFFECT_CONFIG_CHANGED, 0, NULL);
+ }
+ }
+ }
+ }
+
// hold a strong ref on thread in case closeOutput() or closeInput() is called
// and the thread is exited once the lock is released
sp<ThreadBase> thread;
@@ -971,6 +1153,9 @@
if (ioHandle == 0) {
String8 out_s8;
+#ifdef SRS_PROCESSING
+ POSTPRO_PATCH_ICS_PARAMS_GET(keys, out_s8);
+#endif
for (size_t i = 0; i < mAudioHwDevs.size(); i++) {
char *s;
{
@@ -1099,6 +1284,21 @@
}
}
+status_t AudioFlinger::deregisterClient(const sp<IAudioFlingerClient>& client)
+{
+ ALOGV("deregisterClient() %p, tid %d, calling tid %d", client.get(), gettid(), IPCThreadState::self()->getCallingPid());
+ Mutex::Autolock _l(mLock);
+
+ pid_t pid = IPCThreadState::self()->getCallingPid();
+ int index = mNotificationClients.indexOfKey(pid);
+ if (index >= 0) {
+ mNotificationClients.removeItemsAt(index);
+ return true;
+ }
+
+ return false;
+}
+
void AudioFlinger::removeNotificationClient(pid_t pid)
{
Mutex::Autolock _l(mLock);
@@ -1129,10 +1329,28 @@
// audioConfigChanged_l() must be called with AudioFlinger::mLock held
void AudioFlinger::audioConfigChanged_l(int event, audio_io_handle_t ioHandle, const void *param2)
{
- size_t size = mNotificationClients.size();
- for (size_t i = 0; i < size; i++) {
- mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
- param2);
+ ALOGV("AudioFlinger::audioConfigChanged_l: event %d", event);
+ if (event == AudioSystem::EFFECT_CONFIG_CHANGED) {
+ mIsEffectConfigChanged = true;
+ }
+ if (!mNotificationClients.isEmpty()){
+ size_t size = mNotificationClients.size();
+ for (size_t i = 0; i < size; i++) {
+ mNotificationClients.valueAt(i)->audioFlingerClient()->ioConfigChanged(event, ioHandle,
+ param2);
+ }
+ }
+ if ((!mDirectAudioTracks.isEmpty())&& (event == AudioSystem::EFFECT_CONFIG_CHANGED)){
+ size_t dsize = mDirectAudioTracks.size();
+ for(size_t i = 0; i < dsize; i++) {
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueAt(i);
+ if(desc && ((DirectAudioTrack*)desc->trackRefPtr)) {
+ ALOGV("signalling directAudioTrack ");
+ ((DirectAudioTrack*)desc->trackRefPtr)->signalEffect();
+ } else{
+ ALOGV("not found track to signal directAudioTrack ");
+ }
+ }
}
}
@@ -1254,6 +1472,8 @@
RecordThread *thread;
size_t inFrameCount;
int lSessionId;
+ size_t inputBufferSize = 0;
+ uint32_t channelCount = popcount(channelMask);
// check calling permissions
if (!recordingAllowed()) {
@@ -1262,9 +1482,11 @@
goto Exit;
}
- if (format != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("openRecord() invalid format %d", format);
+ // Check that audio input stream accepts requested audio parameters
+ inputBufferSize = getInputBufferSize(sampleRate, format, channelCount);
+ if (inputBufferSize == 0) {
lStatus = BAD_VALUE;
+ ALOGE("Bad audio input parameters: sampling rate %u, format %d, channels %d", sampleRate, format, channelCount);
goto Exit;
}
@@ -1297,6 +1519,30 @@
*sessionId = lSessionId;
}
}
+
+ // frameCount must be a multiple of input buffer size
+ // Change for Codec type
+ uint8_t channelCount = popcount(channelMask);
+ if (*flags & IAudioFlinger::TRACK_VOICE_COMMUNICATION) {
+ inFrameCount = inputBufferSize/channelCount/sizeof(short);
+ } else {
+ if ((format == AUDIO_FORMAT_PCM_16_BIT) ||
+ (format == AUDIO_FORMAT_PCM_8_BIT)) {
+ inFrameCount = inputBufferSize/channelCount/sizeof(short);
+ } else if (format == AUDIO_FORMAT_AMR_NB) {
+ inFrameCount = inputBufferSize/channelCount/AMR_FRAMESIZE;
+ } else if (format == AUDIO_FORMAT_EVRC) {
+ inFrameCount = inputBufferSize/channelCount/EVRC_FRAMESIZE;
+ } else if (format == AUDIO_FORMAT_QCELP) {
+ inFrameCount = inputBufferSize/channelCount/QCELP_FRAMESIZE;
+ } else if (format == AUDIO_FORMAT_AAC) {
+ inFrameCount = inputBufferSize/AAC_FRAMESIZE;
+ } else if (format == AUDIO_FORMAT_AMR_WB) {
+ inFrameCount = inputBufferSize/channelCount/AMR_WB_FRAMESIZE;
+ }
+ }
+ frameCount = ((frameCount - 1)/inFrameCount + 1) * inFrameCount;
+
// create new record track.
// The record track uses one track in mHardwareMixerThread by convention.
recordTrack = thread->createRecordTrack_l(client, sampleRate, format, channelMask,
@@ -1530,10 +1776,21 @@
if (status == NO_ERROR && outStream != NULL) {
AudioStreamOut *output = new AudioStreamOut(outHwDev, outStream, flags);
-
if (flags & AUDIO_OUTPUT_FLAG_COMPRESS_OFFLOAD) {
thread = new OffloadThread(this, output, id, *pDevices);
ALOGV("openOutput() created offload output: ID %d thread %p", id, thread);
+ }
+ if (flags & AUDIO_OUTPUT_FLAG_LPA || flags & AUDIO_OUTPUT_FLAG_TUNNEL ) {
+ AudioSessionDescriptor *desc = new AudioSessionDescriptor(hwDevHal, outStream, flags);
+ desc->mActive = true;
+ //TODO: no stream type
+ //desc->mStreamType = streamType;
+ desc->mVolumeLeft = 1.0;
+ desc->mVolumeRight = 1.0;
+ desc->device = *pDevices;
+ desc->trackRefPtr = NULL;
+ mDirectAudioTracks.add(id, desc);
+ mDirectDevice = desc->device;
} else if ((flags & AUDIO_OUTPUT_FLAG_DIRECT) ||
(config.format != AUDIO_FORMAT_PCM_16_BIT) ||
(config.channel_mask != AUDIO_CHANNEL_OUT_STEREO)) {
@@ -1543,7 +1800,9 @@
thread = new MixerThread(this, output, id, *pDevices);
ALOGV("openOutput() created mixer output: ID %d thread %p", id, thread);
}
- mPlaybackThreads.add(id, thread);
+ if (thread != NULL) {
+ mPlaybackThreads.add(id, thread);
+ }
if (pSamplingRate != NULL) {
*pSamplingRate = config.sample_rate;
@@ -1554,18 +1813,28 @@
if (pChannelMask != NULL) {
*pChannelMask = config.channel_mask;
}
- if (pLatencyMs != NULL) {
- *pLatencyMs = thread->latency();
+ if (thread != NULL) {
+ if (pLatencyMs != NULL) *pLatencyMs = thread->latency();
+ // notify client processes of the new output creation
+ thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
+ }
+ else {
+ *pLatencyMs = 0;
+ if ((flags & AUDIO_OUTPUT_FLAG_LPA) || (flags & AUDIO_OUTPUT_FLAG_TUNNEL)) {
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(id);
+ *pLatencyMs = desc->stream->get_latency(desc->stream);
+ }
}
- // notify client processes of the new output creation
- thread->audioConfigChanged_l(AudioSystem::OUTPUT_OPENED);
// the first primary output opened designates the primary hw device
if ((mPrimaryHardwareDev == NULL) && (flags & AUDIO_OUTPUT_FLAG_PRIMARY)) {
ALOGI("Using module %d has the primary audio interface", module);
mPrimaryHardwareDev = outHwDev;
-
+#ifdef SRS_PROCESSING
+ SRS_Processing::RawDataSet(NULL, "qdsp hook", &mPrimaryHardwareDev,
+ sizeof(&mPrimaryHardwareDev));
+#endif
AutoMutex lock(mHardwareLock);
mHardwareStatus = AUDIO_HW_SET_MODE;
hwDevHal->set_mode(hwDevHal, mMode);
@@ -1608,6 +1877,20 @@
{
// keep strong reference on the playback thread so that
// it is not destroyed while exit() is executed
+ AudioSessionDescriptor *desc = mDirectAudioTracks.valueFor(output);
+ if (desc) {
+ ALOGV("Closing DirectTrack output %d", output);
+ desc->mActive = false;
+ desc->stream->common.standby(&desc->stream->common);
+ desc->hwDev->close_output_stream(desc->hwDev, desc->stream);
+ desc->trackRefPtr = NULL;
+ desc->stream = NULL;
+ mDirectAudioTracks.removeItem(output);
+ audioConfigChanged_l(AudioSystem::OUTPUT_CLOSED, output, NULL);
+ delete desc;
+ return NO_ERROR;
+ }
+
sp<PlaybackThread> thread;
{
Mutex::Autolock _l(mLock);
@@ -1748,7 +2031,7 @@
if (status == BAD_VALUE &&
reqFormat == config.format && config.format == AUDIO_FORMAT_PCM_16_BIT &&
(config.sample_rate <= 2 * reqSamplingRate) &&
- (popcount(config.channel_mask) <= FCC_2) && (popcount(reqChannels) <= FCC_2)) {
+ (getInputChannelCount(config.channel_mask) <= FCC_2) && (getInputChannelCount(reqChannels) <= FCC_2)) {
ALOGV("openInput() reopening with proposed sampling rate and channel mask");
inStream = NULL;
#ifndef ICS_AUDIO_BLOB
@@ -2563,8 +2846,6 @@
}
#endif
-// ----------------------------------------------------------------------------
-
status_t AudioFlinger::onTransact(
uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
{
diff --git a/services/audioflinger/AudioFlinger.h b/services/audioflinger/AudioFlinger.h
index 2aeb263..6392bed 100644
--- a/services/audioflinger/AudioFlinger.h
+++ b/services/audioflinger/AudioFlinger.h
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2012-2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2007, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -28,6 +29,8 @@
#include <media/IAudioFlinger.h>
#include <media/IAudioFlingerClient.h>
+#include <media/IDirectTrack.h>
+#include <media/IDirectTrackClient.h>
#include <media/IAudioTrack.h>
#include <media/IAudioRecord.h>
#include <media/AudioSystem.h>
@@ -54,7 +57,7 @@
#include "AudioWatchdog.h"
#include <powermanager/IPowerManager.h>
-
+#include <utils/List.h>
#include <media/nbaio/NBLog.h>
#include <private/media/AudioTrackShared.h>
@@ -86,6 +89,10 @@
#define INCLUDING_FROM_AUDIOFLINGER_H
+static uint32_t getInputChannelCount(uint32_t channels) {
+ // only mono, stereo, and 5.1 are supported for input sources
+ return popcount((channels)&(AUDIO_CHANNEL_IN_STEREO|AUDIO_CHANNEL_IN_MONO|AUDIO_CHANNEL_IN_5POINT1));
+}
class AudioFlinger :
public BinderService<AudioFlinger>,
public BnAudioFlinger
@@ -110,7 +117,16 @@
int *sessionId,
String8& name,
status_t *status);
-
+ virtual sp<IDirectTrack> createDirectTrack(
+ pid_t pid,
+ uint32_t sampleRate,
+ audio_channel_mask_t channelMask,
+ audio_io_handle_t output,
+ int *sessionId,
+ IDirectTrackClient* client,
+ audio_stream_type_t streamType,
+ status_t *status);
+ virtual void deleteEffectSession();
virtual sp<IAudioRecord> openRecord(
audio_io_handle_t input,
uint32_t sampleRate,
@@ -151,7 +167,7 @@
virtual String8 getParameters(audio_io_handle_t ioHandle, const String8& keys) const;
virtual void registerClient(const sp<IAudioFlingerClient>& client);
-
+ virtual status_t deregisterClient(const sp<IAudioFlingerClient>& client);
virtual size_t getInputBufferSize(uint32_t sampleRate, audio_format_t format,
audio_channel_mask_t channelMask) const;
@@ -229,6 +245,12 @@
Parcel* reply,
uint32_t flags);
+ bool applyEffectsOn(void *token,
+ int16_t *buffer1,
+ int16_t *buffer2,
+ int size,
+ bool force);
+
// end of IAudioFlinger interface
sp<NBLog::Writer> newWriter_l(size_t size, const char *name);
@@ -374,6 +396,7 @@
class EffectModule;
class EffectHandle;
class EffectChain;
+ struct AudioSessionDescriptor;
struct AudioStreamOut;
struct AudioStreamIn;
@@ -463,7 +486,116 @@
sp<PlaybackThread> getEffectThread_l(int sessionId, int EffectId);
+ // server side of the client's IAudioTrack
+ class DirectAudioTrack : public android::BnDirectTrack,
+ public AudioEventObserver
+ {
+ public:
+ DirectAudioTrack(const sp<AudioFlinger>& audioFlinger,
+ int output, AudioSessionDescriptor *outputDesc,
+ IDirectTrackClient* client, audio_output_flags_t outflag);
+ virtual ~DirectAudioTrack();
+ virtual status_t start();
+ virtual void stop();
+ virtual void flush();
+ virtual void mute(bool);
+ virtual void pause();
+ virtual ssize_t write(const void *buffer, size_t bytes);
+ virtual void setVolume(float left, float right);
+ virtual int64_t getTimeStamp();
+ virtual void postEOS(int64_t delayUs);
+ void signalEffect();
+ virtual status_t onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags);
+ private:
+
+ IDirectTrackClient* mClient;
+ AudioSessionDescriptor *mOutputDesc;
+ int mOutput;
+ bool mIsPaused;
+ audio_output_flags_t mFlag;
+
+ class BufferInfo {
+ public:
+ BufferInfo(void *buf1, void *buf2, int32_t nSize) :
+ localBuf(buf1), dspBuf(buf2), memBufsize(nSize)
+ {}
+
+ void *localBuf;
+ void *dspBuf;
+ uint32_t memBufsize;
+ uint32_t bytesToWrite;
+ };
+ List<BufferInfo> mBufPool;
+ List<BufferInfo> mEffectsPool;
+ void *mEffectsThreadScratchBuffer;
+
+ void allocateBufPool();
+ void deallocateBufPool();
+
+ //******Effects*************
+ static void *EffectsThreadWrapper(void *me);
+ void EffectsThreadEntry();
+ // make sure the Effects thread also exited
+ void requestAndWaitForEffectsThreadExit();
+ void createEffectThread();
+ Condition mEffectCv;
+ Mutex mEffectLock;
+ pthread_t mEffectsThread;
+ bool mKillEffectsThread;
+ bool mEffectsThreadAlive;
+ bool mEffectConfigChanged;
+
+ //Structure to recieve the Effect notification from the flinger.
+ class AudioFlingerDirectTrackClient: public IBinder::DeathRecipient, public BnAudioFlingerClient {
+ public:
+ AudioFlingerDirectTrackClient(void *obj);
+
+ DirectAudioTrack *pBaseClass;
+ // DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ // IAudioFlingerClient
+
+ // indicate a change in the configuration of an output or input: keeps the cached
+ // values for output/input parameters upto date in client process
+ virtual void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2);
+
+ friend class DirectAudioTrack;
+ };
+ // helper function to obtain AudioFlinger service handle
+ sp<AudioFlinger> mAudioFlinger;
+ sp<AudioFlingerDirectTrackClient> mAudioFlingerClient;
+
+ void clearPowerManager();
+
+ class PMDeathRecipient : public IBinder::DeathRecipient {
+ public:
+ PMDeathRecipient(void *obj){parentClass = (DirectAudioTrack *)obj;}
+ virtual ~PMDeathRecipient() {}
+
+ // IBinder::DeathRecipient
+ virtual void binderDied(const wp<IBinder>& who);
+
+ private:
+ DirectAudioTrack *parentClass;
+ PMDeathRecipient(const PMDeathRecipient&);
+ PMDeathRecipient& operator = (const PMDeathRecipient&);
+
+ friend class DirectAudioTrack;
+ };
+
+ friend class PMDeathRecipient;
+
+ Mutex pmLock;
+ void acquireWakeLock();
+ void releaseWakeLock();
+
+ sp<IPowerManager> mPowerManager;
+ sp<IBinder> mWakeLockToken;
+ sp<PMDeathRecipient> mDeathRecipient;
+ };
void removeClient_l(pid_t pid);
void removeNotificationClient(pid_t pid);
@@ -525,6 +657,20 @@
AudioStreamIn(AudioHwDevice *dev, audio_stream_in_t *in) :
audioHwDev(dev), stream(in) {}
};
+ struct AudioSessionDescriptor {
+ bool mActive;
+ int mStreamType;
+ float mVolumeLeft;
+ float mVolumeRight;
+ float mVolumeScale;
+ audio_hw_device_t *hwDev;
+ audio_stream_out_t *stream;
+ audio_output_flags_t flag;
+ void *trackRefPtr;
+ audio_devices_t device;
+ AudioSessionDescriptor(audio_hw_device_t *dev, audio_stream_out_t *out, audio_output_flags_t outflag) :
+ hwDev(dev), stream(out), flag(outflag) {}
+ };
// for mAudioSessionRefs only
struct AudioSessionRef {
@@ -589,10 +735,16 @@
volatile int32_t mNextUniqueId; // updated by android_atomic_inc
audio_mode_t mMode;
bool mBtNrecIsOff;
-
+ DefaultKeyedVector<audio_io_handle_t, AudioSessionDescriptor *> mDirectAudioTracks;
// protected by mLock
+ volatile bool mIsEffectConfigChanged;
Vector<AudioSessionRef*> mAudioSessionRefs;
-
+ sp<EffectChain> mLPAEffectChain;
+ int mLPASessionId;
+ audio_devices_t mDirectDevice;//device for directTrack,used for effects
+ int mLPASampleRate;
+ int mLPANumChannels;
+ volatile bool mAllChainsLocked;
float masterVolume_l() const;
bool masterMute_l() const;
audio_module_handle_t loadHwModule_l(const char *name);
diff --git a/services/audioflinger/AudioMixer.cpp b/services/audioflinger/AudioMixer.cpp
index df4e029..f45476c 100644
--- a/services/audioflinger/AudioMixer.cpp
+++ b/services/audioflinger/AudioMixer.cpp
@@ -561,7 +561,7 @@
(value == 48000 && devSampleRate == 44100))) {
quality = AudioResampler::LOW_QUALITY;
} else {
- quality = AudioResampler::DEFAULT_QUALITY;
+ quality = AudioResampler::VERY_HIGH_QUALITY;
}
resampler = AudioResampler::create(
format,
diff --git a/services/audioflinger/Effects.cpp b/services/audioflinger/Effects.cpp
index a8a5169..f916dfc 100644
--- a/services/audioflinger/Effects.cpp
+++ b/services/audioflinger/Effects.cpp
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -29,6 +30,11 @@
#include "AudioFlinger.h"
#include "ServiceUtilities.h"
+#ifdef SRS_PROCESSING
+#include "srs_processing.h"
+#include "postpro_patch_ics.h"
+#endif
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -66,7 +72,7 @@
mStatus(NO_INIT), mState(IDLE),
// mMaxDisableWaitCnt is set by configure() and not used before then
// mDisableWaitCnt is set by process() and updateState() and not used before then
- mSuspended(false)
+ mSuspended(false), mIsForLPA(false)
{
ALOGV("Constructor %p", this);
int lStatus;
@@ -301,12 +307,14 @@
(*mEffectInterface)->command(mEffectInterface, EFFECT_CMD_RESET, 0, NULL, 0, NULL);
}
-status_t AudioFlinger::EffectModule::configure()
+status_t AudioFlinger::EffectModule::configure(bool isForLPA, int sampleRate, int channelCount, int frameCount)
{
status_t status;
+ status_t cmdStatus = 0;
sp<ThreadBase> thread;
uint32_t size;
audio_channel_mask_t channelMask;
+ uint32_t channels;
if (mEffectInterface == NULL) {
status = NO_INIT;
@@ -321,6 +329,24 @@
// TODO: handle configuration of effects replacing track process
channelMask = thread->channelMask();
+ if(popcount(channelMask) > 2) {
+ ALOGE("Error: Trying to apply effect on %d channel content",popcount(channelMask));
+ return INVALID_OPERATION;
+ }
+ mIsForLPA = isForLPA;
+ if(isForLPA) {
+ if (channelCount == 1) {
+ channels = AUDIO_CHANNEL_OUT_MONO;
+ } else {
+ channels = AUDIO_CHANNEL_OUT_STEREO;
+ }
+ } else {
+ if (thread->channelCount() == 1) {
+ channels = AUDIO_CHANNEL_OUT_MONO;
+ } else {
+ channels = AUDIO_CHANNEL_OUT_STEREO;
+ }
+ }
if ((mDescriptor.flags & EFFECT_FLAG_TYPE_MASK) == EFFECT_FLAG_TYPE_AUXILIARY) {
mConfig.inputCfg.channels = AUDIO_CHANNEL_OUT_MONO;
@@ -330,7 +356,11 @@
mConfig.outputCfg.channels = channelMask;
mConfig.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
mConfig.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
- mConfig.inputCfg.samplingRate = thread->sampleRate();
+ if(isForLPA){
+ mConfig.inputCfg.samplingRate = sampleRate;
+ } else {
+ mConfig.inputCfg.samplingRate = thread->sampleRate();
+ }
mConfig.outputCfg.samplingRate = mConfig.inputCfg.samplingRate;
mConfig.inputCfg.bufferProvider.cookie = NULL;
mConfig.inputCfg.bufferProvider.getBuffer = NULL;
@@ -355,13 +385,16 @@
}
mConfig.inputCfg.mask = EFFECT_CONFIG_ALL;
mConfig.outputCfg.mask = EFFECT_CONFIG_ALL;
- mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+ if(isForLPA) {
+ mConfig.inputCfg.buffer.frameCount = frameCount;
+ } else {
+ mConfig.inputCfg.buffer.frameCount = thread->frameCount();
+ }
mConfig.outputCfg.buffer.frameCount = mConfig.inputCfg.buffer.frameCount;
ALOGV("configure() %p thread %p buffer %p framecount %d",
this, thread.get(), mConfig.inputCfg.buffer.raw, mConfig.inputCfg.buffer.frameCount);
- status_t cmdStatus;
size = sizeof(int);
status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_SET_CONFIG,
@@ -412,7 +445,7 @@
if (mEffectInterface == NULL) {
return NO_INIT;
}
- status_t cmdStatus;
+ status_t cmdStatus = 0;
uint32_t size = sizeof(status_t);
status_t status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_INIT,
@@ -440,7 +473,7 @@
if (mStatus != NO_ERROR) {
return mStatus;
}
- status_t cmdStatus;
+ status_t cmdStatus = 0;
uint32_t size = sizeof(status_t);
status_t status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_ENABLE,
@@ -553,10 +586,11 @@
// must be called with EffectModule::mLock held
status_t AudioFlinger::EffectModule::setEnabled_l(bool enabled)
{
-
+ bool effectStateChanged = false;
ALOGV("setEnabled %p enabled %d", this, enabled);
if (enabled != isEnabled()) {
+ effectStateChanged = true;
status_t status = AudioSystem::setEffectEnabled(mId, enabled);
if (enabled && status != NO_ERROR) {
return status;
@@ -594,6 +628,17 @@
}
}
}
+ /*
+ Send notification event to LPA Player when an effect for
+ LPA output is enabled or disabled.
+ */
+ sp<EffectChain> chain = mChain.promote();
+ if (effectStateChanged && chain->isForLPATrack()) {
+ sp<ThreadBase> thread = mThread.promote();
+ unlock();//Acquire locks in certain sequence to avoid deadlock
+ thread->effectConfigChanged();
+ lock();
+ }
return NO_ERROR;
}
@@ -645,7 +690,7 @@
if (isProcessEnabled() &&
((mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_CTRL ||
(mDescriptor.flags & EFFECT_FLAG_VOLUME_MASK) == EFFECT_FLAG_VOLUME_IND)) {
- status_t cmdStatus;
+ status_t cmdStatus = 0;
uint32_t volume[2];
uint32_t *pVolume = NULL;
uint32_t size = sizeof(volume);
@@ -680,7 +725,7 @@
}
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_DEVICE_MASK) == EFFECT_FLAG_DEVICE_IND) {
- status_t cmdStatus;
+ status_t cmdStatus = 0;
uint32_t size = sizeof(status_t);
uint32_t cmd = audio_is_output_devices(device) ? EFFECT_CMD_SET_DEVICE :
EFFECT_CMD_SET_INPUT_DEVICE;
@@ -702,7 +747,7 @@
}
status_t status = NO_ERROR;
if ((mDescriptor.flags & EFFECT_FLAG_AUDIO_MODE_MASK) == EFFECT_FLAG_AUDIO_MODE_IND) {
- status_t cmdStatus;
+ status_t cmdStatus = 0;
uint32_t size = sizeof(status_t);
status = (*mEffectInterface)->command(mEffectInterface,
EFFECT_CMD_SET_AUDIO_MODE,
@@ -1137,6 +1182,15 @@
return disable();
}
+ if(mEffect->isOnLPA() &&
+ ((cmdCode == EFFECT_CMD_SET_PARAM) || (cmdCode == EFFECT_CMD_SET_PARAM_DEFERRED) ||
+ (cmdCode == EFFECT_CMD_SET_PARAM_COMMIT) || (cmdCode == EFFECT_CMD_SET_DEVICE) ||
+ (cmdCode == EFFECT_CMD_SET_VOLUME) || (cmdCode == EFFECT_CMD_SET_AUDIO_MODE)) ) {
+ // Notify Direct track for the change in Effect module
+ // TODO: check if it is required to send mLPAHandle
+ ALOGV("Notifying Direct Track for the change in effect config %d", cmdCode);
+ mClient->audioFlinger()->audioConfigChanged_l(AudioSystem::EFFECT_CONFIG_CHANGED, 0, NULL);
+ }
return mEffect->command(cmdCode, cmdSize, pCmdData, replySize, pReplyData);
}
@@ -1204,7 +1258,7 @@
int sessionId)
: mThread(thread), mSessionId(sessionId), mActiveTrackCnt(0), mTrackCnt(0), mTailBufferCount(0),
mOwnInBuffer(false), mVolumeCtrlIdx(-1), mLeftVolume(UINT_MAX), mRightVolume(UINT_MAX),
- mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX)
+ mNewLeftVolume(UINT_MAX), mNewRightVolume(UINT_MAX), mIsForLPATrack(false)
{
mStrategy = AudioSystem::getStrategyForStream(AUDIO_STREAM_MUSIC);
if (thread == NULL) {
@@ -1250,6 +1304,18 @@
return 0;
}
+sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromIndex_l(int idx)
+{
+ sp<EffectModule> effect = NULL;
+ if(idx < 0 || idx >= mEffects.size()) {
+ ALOGE("EffectChain::getEffectFromIndex_l: invalid index %d", idx);
+ }
+ if(mEffects.size() > 0){
+ effect = mEffects[idx];
+ }
+ return effect;
+}
+
// getEffectFromType_l() must be called with ThreadBase::mLock held
sp<AudioFlinger::EffectModule> AudioFlinger::EffectChain::getEffectFromType_l(
const effect_uuid_t *type)
@@ -1315,7 +1381,7 @@
}
size_t size = mEffects.size();
- if (doProcess) {
+ if (doProcess || isForLPATrack()) {
for (size_t i = 0; i < size; i++) {
mEffects[i]->process();
}
@@ -1780,6 +1846,7 @@
}
}
+
bool AudioFlinger::EffectChain::isNonOffloadableEnabled()
{
Mutex::Autolock _l(mLock);
@@ -1792,4 +1859,179 @@
return false;
}
+#define DEAFULT_FRAME_COUNT 1200
+bool AudioFlinger::applyEffectsOn(void *token, int16_t *inBuffer,
+ int16_t *outBuffer, int size, bool force)
+{
+ ALOGV("applyEffectsOn: inBuf %p outBuf %p size %d token %p", inBuffer, outBuffer, size, token);
+ // This might be the first buffer to apply effects after effect config change
+ // should not skip effects processing
+ mIsEffectConfigChanged = false;
+
+ volatile size_t numEffects = 0;
+#ifdef SRS_PROCESSING
+ POSTPRO_PATCH_ICS_OUTPROC_DIRECT_SAMPLES(token, AUDIO_FORMAT_PCM_16_BIT, outBuffer, size, mLPASampleRate, mLPANumChannels);
+#endif
+
+ if(mLPAEffectChain != NULL) {
+ numEffects = mLPAEffectChain->getNumEffects();
+ }
+
+ if( numEffects > 0) {
+ size_t i = 0;
+ int16_t *pIn = inBuffer;
+ int16_t *pOut = outBuffer;
+
+ int frameCount = size / (sizeof(int16_t) * mLPANumChannels);
+
+ while(frameCount > 0) {
+ if(mLPAEffectChain == NULL) {
+ ALOGV("LPA Effect Chain is removed - No effects processing !!");
+ numEffects = 0;
+ break;
+ }
+ mLPAEffectChain->lock();
+
+ numEffects = mLPAEffectChain->getNumEffects();
+ if(!numEffects) {
+ ALOGV("applyEffectsOn: All the effects are removed - nothing to process");
+ mLPAEffectChain->unlock();
+ break;
+ }
+
+ int outFrameCount = (frameCount > DEAFULT_FRAME_COUNT ? DEAFULT_FRAME_COUNT: frameCount);
+ bool isEffectEnabled = false;
+ for(i = 0; i < numEffects; i++) {
+ // If effect configuration is changed while applying effects do not process further
+ if(mIsEffectConfigChanged && !force) {
+ mLPAEffectChain->unlock();
+ ALOGV("applyEffectsOn: mIsEffectConfigChanged is set - no further processing %d",frameCount);
+ return false;
+ }
+ sp<EffectModule> effect = mLPAEffectChain->getEffectFromIndex_l(i);
+ if(effect == NULL) {
+ ALOGE("getEffectFromIndex_l(%d) returned NULL ptr", i);
+ mLPAEffectChain->unlock();
+ return false;
+ }
+ if(i == 0) {
+ // For the first set input and output buffers different
+ isEffectEnabled = effect->isProcessEnabled();
+ effect->setInBuffer(pIn);
+ effect->setOutBuffer(pOut);
+ } else {
+ // For the remaining use previous effect's output buffer as input buffer
+ effect->setInBuffer(pOut);
+ effect->setOutBuffer(pOut);
+ }
+ // true indicates that it is being applied on LPA output
+ effect->configure(true, mLPASampleRate, mLPANumChannels, outFrameCount);
+ }
+
+ if(isEffectEnabled) {
+ // Clear the output buffer
+ memset(pOut, 0, (outFrameCount * mLPANumChannels * sizeof(int16_t)));
+ } else {
+ // Copy input buffer content to the output buffer
+ memcpy(pOut, pIn, (outFrameCount * mLPANumChannels * sizeof(int16_t)));
+ }
+
+ mLPAEffectChain->process_l();
+
+ mLPAEffectChain->unlock();
+
+ // Update input and output buffer pointers
+ pIn += (outFrameCount * mLPANumChannels);
+ pOut += (outFrameCount * mLPANumChannels);
+ frameCount -= outFrameCount;
+ }
+ }
+
+ if (!numEffects && !force) {
+ ALOGV("applyEffectsOn: There are no effects to be applied");
+ if(inBuffer != outBuffer) {
+ // No effect applied so just copy input buffer to output buffer
+ memcpy(outBuffer, inBuffer, size);
+ }
+ }
+ return true;
+}
+
+void *AudioFlinger::DirectAudioTrack::EffectsThreadWrapper(void *me) {
+ static_cast<DirectAudioTrack *>(me)->EffectsThreadEntry();
+ return NULL;
+}
+
+void AudioFlinger::DirectAudioTrack::EffectsThreadEntry() {
+ while(1) {
+ mEffectLock.lock();
+ if (!mEffectConfigChanged && !mKillEffectsThread) {
+ mEffectCv.wait(mEffectLock);
+ }
+ if(mKillEffectsThread) {
+ mEffectLock.unlock();
+ break;
+ }
+ if (mEffectConfigChanged) {
+ mEffectConfigChanged = false;
+ if (mFlag & AUDIO_OUTPUT_FLAG_LPA) {
+ for ( List<BufferInfo>::iterator it = mEffectsPool.begin();
+ it != mEffectsPool.end(); it++) {
+ ALOGV("ete: calling applyEffectsOn buff %x",it->localBuf);
+ bool isEffectsApplied = mAudioFlinger->applyEffectsOn(
+ static_cast<void *>(this),
+ (int16_t *)it->localBuf,
+ (int16_t *)mEffectsThreadScratchBuffer,
+ it->bytesToWrite,
+ false);
+ if (isEffectsApplied == true){
+ ALOGV("ete:dsp updated for local buf %x",it->localBuf);
+ memcpy(it->dspBuf, mEffectsThreadScratchBuffer, it->bytesToWrite);
+ }
+ else
+ ALOGV("ete:dsp updated for local buf %x SKIPPED",it->localBuf);
+
+ if (mEffectConfigChanged) {
+ ALOGE("ete:effects changed, abort effects application");
+ break;
+ }
+ }
+#ifdef SRS_PROCESSING
+ } else if (mFlag & AUDIO_OUTPUT_FLAG_TUNNEL) {
+ ALOGV("applying effects for TUNNEL");
+ char buffer[2];
+ //dummy buffer to ensure the SRS processing takes place
+ // The API mandates Sample rate and channel mode. Hence
+ // defaulted the sample rate channel mode to 48000 and 2 respectively
+ POSTPRO_PATCH_ICS_OUTPROC_DIRECT_SAMPLES(static_cast<void *>(this),
+ AUDIO_FORMAT_PCM_16_BIT,
+ (int16_t*)buffer, 2, 48000, 2);
+#endif
+ }
+ }
+ mEffectLock.unlock();
+ }
+ ALOGV("Effects thread is dead");
+ mEffectsThreadAlive = false;
+}
+
+void AudioFlinger::DirectAudioTrack::requestAndWaitForEffectsThreadExit() {
+ if (!mEffectsThreadAlive)
+ return;
+ mKillEffectsThread = true;
+ mEffectCv.signal();
+ pthread_join(mEffectsThread,NULL);
+ ALOGV("effects thread killed");
+}
+
+void AudioFlinger::DirectAudioTrack::createEffectThread() {
+ //Create the effects thread
+ pthread_attr_t attr;
+ pthread_attr_init(&attr);
+ pthread_attr_setdetachstate(&attr, PTHREAD_CREATE_JOINABLE);
+ mEffectsThreadAlive = true;
+ ALOGV("Creating Effects Thread");
+ pthread_create(&mEffectsThread, &attr, EffectsThreadWrapper, this);
+}
+
}; // namespace android
diff --git a/services/audioflinger/Effects.h b/services/audioflinger/Effects.h
index b717857..10c2227 100644
--- a/services/audioflinger/Effects.h
+++ b/services/audioflinger/Effects.h
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -68,7 +69,10 @@
void *pReplyData);
void reset_l();
- status_t configure();
+ status_t configure(bool isForLPA = false,
+ int sampleRate = 0,
+ int channelCount = 0,
+ int frameCount = 0);
status_t init();
effect_state state() const {
return mState;
@@ -119,6 +123,8 @@
{ return (mDescriptor.flags & EFFECT_FLAG_OFFLOAD_SUPPORTED) != 0; }
status_t setOffloaded(bool offloaded, audio_io_handle_t io);
bool isOffloaded() const;
+ bool isOnLPA() { return mIsForLPA;}
+ void setLPAFlag(bool isForLPA) {mIsForLPA = isForLPA; }
void dump(int fd, const Vector<String16>& args);
@@ -153,6 +159,7 @@
uint32_t mDisableWaitCnt; // current process() calls count during disable period.
bool mSuspended; // effect is suspended: temporarily disabled by framework
bool mOffloaded; // effect is currently offloaded to the audio DSP
+ bool mIsForLPA;
};
// The EffectHandle class implements the IEffect interface. It provides resources
@@ -262,12 +269,14 @@
status_t addEffect_l(const sp<EffectModule>& handle);
size_t removeEffect_l(const sp<EffectModule>& handle);
+ size_t getNumEffects() { return mEffects.size(); }
int sessionId() const { return mSessionId; }
void setSessionId(int sessionId) { mSessionId = sessionId; }
sp<EffectModule> getEffectFromDesc_l(effect_descriptor_t *descriptor);
sp<EffectModule> getEffectFromId_l(int id);
+ sp<EffectModule> getEffectFromIndex_l(int idx);
sp<EffectModule> getEffectFromType_l(const effect_uuid_t *type);
bool setVolume_l(uint32_t *left, uint32_t *right);
void setDevice_l(audio_devices_t device);
@@ -317,6 +326,8 @@
void dump(int fd, const Vector<String16>& args);
+ bool isForLPATrack() {return mIsForLPATrack; }
+ void setLPAFlag(bool flag) {mIsForLPATrack = flag;}
protected:
friend class AudioFlinger; // for mThread, mEffects
@@ -365,6 +376,7 @@
uint32_t mNewLeftVolume; // new volume on left channel
uint32_t mNewRightVolume; // new volume on right channel
uint32_t mStrategy; // strategy for this effect chain
+ bool mIsForLPATrack;
// mSuspendedEffects lists all effects currently suspended in the chain.
// Use effect type UUID timelow field as key. There is no real risk of identical
// timeLow fields among effect type UUIDs.
diff --git a/services/audioflinger/RecordTracks.h b/services/audioflinger/RecordTracks.h
index cd8f70c..cb54348 100644
--- a/services/audioflinger/RecordTracks.h
+++ b/services/audioflinger/RecordTracks.h
@@ -28,6 +28,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
+ uint32_t flags,
int sessionId);
virtual ~RecordTrack();
@@ -57,6 +58,7 @@
int64_t pts = kInvalidPTS);
// releaseBuffer() not overridden
+ track_flags_t mFlags;
bool mOverflow; // overflow on most recent attempt to fill client buffer
AudioRecordServerProxy* mAudioRecordServerProxy;
};
diff --git a/services/audioflinger/Threads.cpp b/services/audioflinger/Threads.cpp
index 2135e79..568329f 100644
--- a/services/audioflinger/Threads.cpp
+++ b/services/audioflinger/Threads.cpp
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -64,6 +65,11 @@
#include <cpustats/ThreadCpuUsage.h>
#endif
+#ifdef SRS_PROCESSING
+#include "srs_processing.h"
+#include "postpro_patch_ics.h"
+#endif
+
// ----------------------------------------------------------------------------
// Note: the following macro is used for extremely verbose logging message. In
@@ -79,6 +85,9 @@
#define ALOGVV(a...) do { } while(0)
#endif
+#define DIRECT_TRACK_EOS 1
+#define DIRECT_TRACK_HW_FAIL 6
+static const char lockName[] = "DirectTrack";
namespace android {
// retry counts for buffer fill timeout
@@ -340,6 +349,11 @@
return status;
}
+void AudioFlinger::ThreadBase::effectConfigChanged() {
+ ALOGV("New effect is being added to LPA chain, Notifying LPA Direct Track");
+ mAudioFlinger->audioConfigChanged_l(AudioSystem::EFFECT_CONFIG_CHANGED, 0, NULL);
+}
+
void AudioFlinger::ThreadBase::sendIoConfigEvent(int event, int param)
{
Mutex::Autolock _l(mLock);
@@ -768,6 +782,19 @@
addEffectChain_l(chain);
chain->setStrategy(getStrategyForSession_l(sessionId));
chainCreated = true;
+ if(sessionId == mAudioFlinger->mLPASessionId) {
+ // Clear reference to previous effect chain if any
+ if(mAudioFlinger->mLPAEffectChain.get()) {
+ mAudioFlinger->mLPAEffectChain.clear();
+ }
+ ALOGV("New EffectChain is created for LPA session ID %d", sessionId);
+ mAudioFlinger->mLPAEffectChain = chain;
+ chain->setLPAFlag(true);
+ // For LPA, the volume will be applied in DSP. No need for volume
+ // control in the Effect chain, so setting it to unity.
+ uint32_t volume = 0x1000000; // Equals to 1.0 in 8.24 format
+ chain->setVolume_l(&volume,&volume);
+ }
} else {
effect = chain->getEffectFromDesc_l(desc);
}
@@ -796,7 +823,7 @@
}
effectCreated = true;
- effect->setDevice(mOutDevice);
+ effect->setDevice(mAudioFlinger->mLPASessionId == sessionId ? mAudioFlinger->mDirectDevice:mOutDevice);
effect->setDevice(mInDevice);
effect->setMode(mAudioFlinger->getMode());
effect->setAudioSource(mAudioSource);
@@ -911,8 +938,13 @@
Vector< sp<AudioFlinger::EffectChain> >& effectChains)
{
effectChains = mEffectChains;
+ mAudioFlinger->mAllChainsLocked = true;
for (size_t i = 0; i < mEffectChains.size(); i++) {
- mEffectChains[i]->lock();
+ if (mEffectChains[i] != mAudioFlinger->mLPAEffectChain) {
+ mEffectChains[i]->lock();
+ } else {
+ mAudioFlinger-> mAllChainsLocked = false;
+ }
}
}
@@ -920,7 +952,8 @@
const Vector< sp<AudioFlinger::EffectChain> >& effectChains)
{
for (size_t i = 0; i < effectChains.size(); i++) {
- effectChains[i]->unlock();
+ if (mAudioFlinger->mAllChainsLocked || effectChains[i] != mAudioFlinger->mLPAEffectChain)
+ effectChains[i]->unlock();
}
}
@@ -1220,7 +1253,10 @@
// This is probably too conservative, but legacy application code may depend on it.
// If you change this calculation, also review the start threshold which is related.
uint32_t latencyMs = mOutput->stream->get_latency(mOutput->stream);
- uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ uint32_t minBufCount = 0;
+ if (mSampleRate) {
+ minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ }
if (minBufCount < 2) {
minBufCount = 2;
}
@@ -1232,7 +1268,13 @@
}
if (mType == DIRECT) {
- if ((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM) {
+ if (((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_PCM)
+ ||((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AMR_NB)
+ ||((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_AMR_WB)
+ ||((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_EVRC)
+ ||((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_EVRCB)
+ ||((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_EVRCWB)
+ ||((format & AUDIO_FORMAT_MAIN_MASK) == AUDIO_FORMAT_EVRCNW)) {
if (sampleRate != mSampleRate || format != mFormat || channelMask != mChannelMask) {
ALOGE("createTrack_l() Bad parameter: sampleRate %u format %d, channelMask 0x%08x "
"for output %p with format %d",
@@ -2120,6 +2162,13 @@
standbyTime = systemTime();
+#ifdef SRS_PROCESSING
+if (mType == MIXER) {
+ POSTPRO_PATCH_ICS_OUTPROC_MIX_INIT(this, gettid());
+ } else if (mType == DUPLICATING) {
+ POSTPRO_PATCH_ICS_OUTPROC_DUPE_INIT(this, gettid());
+ }
+#endif
// MIXER
nsecs_t lastWarning = 0;
@@ -2268,8 +2317,10 @@
// only process effects if we're going to write
if (sleepTime == 0 && mType != OFFLOAD) {
for (size_t i = 0; i < effectChains.size(); i ++) {
- effectChains[i]->process_l();
- }
+ if (effectChains[i] != mAudioFlinger->mLPAEffectChain) {
+ effectChains[i]->process_l();
+ }
+ }
}
}
// Process effect chains for offloaded thread even if no audio
@@ -2288,6 +2339,15 @@
if (!waitingAsyncCallback()) {
// sleepTime == 0 means we must write to audio hardware
if (sleepTime == 0) {
+#ifdef SRS_PROCESSING
+ if (mType == MIXER) {
+ POSTPRO_PATCH_ICS_OUTPROC_MIX_SAMPLES(this, mFormat, mMixBuffer,
+ mixBufferSize, mSampleRate, mChannelCount);
+ } else if (mType == DUPLICATING) {
+ POSTPRO_PATCH_ICS_OUTPROC_DUPE_SAMPLES(this, mFormat, mMixBuffer,
+ mixBufferSize, mSampleRate, mChannelCount);
+ }
+#endif
if (mBytesRemaining) {
ssize_t ret = threadLoop_write();
if (ret < 0) {
@@ -2350,6 +2410,13 @@
}
}
+#ifdef SRS_PROCESSING
+ if (mType == MIXER) {
+ POSTPRO_PATCH_ICS_OUTPROC_MIX_EXIT(this, gettid());
+ } else if (mType == DUPLICATING) {
+ POSTPRO_PATCH_ICS_OUTPROC_DUPE_EXIT(this, gettid());
+ }
+#endif
releaseWakeLock();
ALOGV("Thread %p type %d exiting", this, mType);
@@ -2587,7 +2654,10 @@
{
if (mFastMixer != NULL) {
MonoPipe *pipe = (MonoPipe *)mPipeSink.get();
- latency += (pipe->getAvgFrames() * 1000) / mSampleRate;
+ if(mSampleRate)
+ latency += (pipe->getAvgFrames() * 1000) / mSampleRate;
+ else
+ ALOGW("SampleRate is 0");
}
return latency;
}
@@ -2977,7 +3047,12 @@
desiredFrames = mNormalFrameCount;
} else {
// +1 for rounding and +1 for additional sample needed for interpolation
- desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
+ if (mSampleRate) {
+ desiredFrames = (mNormalFrameCount * sr) / mSampleRate + 1 + 1;
+ } else {
+ desiredFrames = 2;
+ ALOGW("SampleRate is 0");
+ }
// add frames already consumed but not yet released by the resampler
// because cblk->framesReady() will include these frames
desiredFrames += mAudioMixer->getUnreleasedFrames(track->name());
@@ -3310,6 +3385,9 @@
AudioParameter param = AudioParameter(keyValuePair);
int value;
+#ifdef SRS_PROCESSING
+ POSTPRO_PATCH_ICS_OUTPROC_MIX_ROUTE(this, param, value);
+#endif
if (param.getInt(String8(AudioParameter::keySamplingRate), value) == NO_ERROR) {
reconfig = true;
}
@@ -3626,6 +3704,7 @@
if (--(track->mRetryCount) <= 0) {
ALOGV("BUFFER TIMEOUT: remove(%d) from active list", track->name());
tracksToRemove->add(track);
+ android_atomic_or(CBLK_DISABLED, &cblk->mFlags);
} else if (last) {
mixerStatus = MIXER_TRACKS_ENABLED;
}
@@ -4209,7 +4288,10 @@
{
Mutex::Autolock _l(mLock);
// FIXME explain this formula
- size_t frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate();
+ int sampleRate = thread->sampleRate();
+ size_t frameCount = 0;
+ if (sampleRate)
+ frameCount = (3 * mNormalFrameCount * mSampleRate) / thread->sampleRate();
OutputTrack *outputTrack = new OutputTrack(thread,
this,
mSampleRate,
@@ -4306,7 +4388,7 @@
ThreadBase(audioFlinger, id, outDevice, inDevice, RECORD),
mInput(input), mResampler(NULL), mRsmpOutBuffer(NULL), mRsmpInBuffer(NULL),
// mRsmpInIndex and mBufferSize set by readInputParameters()
- mReqChannelCount(popcount(channelMask)),
+ mReqChannelCount(getInputChannelCount(channelMask)),
mReqSampleRate(sampleRate)
// mBytesRead is only meaningful while active, and so is cleared in start()
// (but might be better to also clear here for dump?)
@@ -4446,17 +4528,29 @@
}
}
}
- if (framesOut && mFrameCount == mRsmpInIndex) {
+ if (framesOut && (mFrameCount == mRsmpInIndex)) {
void *readInto;
- if (framesOut == mFrameCount && mChannelCount == mReqChannelCount) {
- readInto = buffer.raw;
- framesOut = 0;
- } else {
- readInto = mRsmpInBuffer;
- mRsmpInIndex = 0;
- }
+ int InputBytes;
+ /*Fix me: How does framesOut become 0 in this condition */
+ if (( framesOut != mFrameCount) &&
+ ((mFormat != AUDIO_FORMAT_PCM_16_BIT)&&
+ (!(mActiveTrack->mFlags & TRACK_VOICE_COMMUNICATION)))) {
+ readInto = buffer.raw;
+ InputBytes = buffer.frameCount * mFrameSize;
+ } else if (framesOut == mFrameCount &&
+ (mChannelCount == mReqChannelCount ||
+ ((mFormat != AUDIO_FORMAT_PCM_16_BIT) &&
+ (!(mActiveTrack->mFlags & TRACK_VOICE_COMMUNICATION))))) {
+ readInto = buffer.raw;
+ InputBytes = mBufferSize;
+ framesOut = 0;
+ } else {
+ readInto = mRsmpInBuffer;
+ mRsmpInIndex = 0;
+ InputBytes = mBufferSize;
+ }
mBytesRead = mInput->stream->read(mInput->stream, readInto,
- mBufferSize);
+ InputBytes);
if (mBytesRead <= 0) {
if ((mBytesRead < 0) && (mActiveTrack->mState == TrackBase::ACTIVE))
{
@@ -4636,7 +4730,10 @@
// If you change this calculation, also review the start threshold which is related.
uint32_t latencyMs = 50; // FIXME mInput->stream->get_latency(mInput->stream);
size_t mNormalFrameCount = 2048; // FIXME
- uint32_t minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ uint32_t minBufCount = 0;
+ if (mSampleRate) {
+ minBufCount = latencyMs / ((1000 * mNormalFrameCount) / mSampleRate);
+ }
if (minBufCount < 2) {
minBufCount = 2;
}
@@ -4653,7 +4750,7 @@
Mutex::Autolock _l(mLock);
track = new RecordTrack(this, client, sampleRate,
- format, channelMask, frameCount, sessionId);
+ format, channelMask, frameCount, *flags, sessionId);
if (track->getCblk() == 0) {
ALOGE("createRecordTrack_l() no control block");
@@ -4994,7 +5091,7 @@
}
}
if (param.getInt(String8(AudioParameter::keyChannels), value) == NO_ERROR) {
- reqChannelCount = popcount(value);
+ reqChannelCount = getInputChannelCount(value);
reconfig = true;
}
if (param.getInt(String8(AudioParameter::keyFrameCount), value) == NO_ERROR) {
@@ -5058,7 +5155,7 @@
reqFormat == AUDIO_FORMAT_PCM_16_BIT &&
(mInput->stream->common.get_sample_rate(&mInput->stream->common)
<= (2 * reqSamplingRate)) &&
- popcount(mInput->stream->common.get_channels(&mInput->stream->common))
+ getInputChannelCount(mInput->stream->common.get_channels(&mInput->stream->common))
<= FCC_2 &&
(reqChannelCount <= FCC_2)) {
status = NO_ERROR;
@@ -5127,11 +5224,8 @@
mSampleRate = mInput->stream->common.get_sample_rate(&mInput->stream->common);
mChannelMask = mInput->stream->common.get_channels(&mInput->stream->common);
- mChannelCount = popcount(mChannelMask);
+ mChannelCount = (uint16_t)getInputChannelCount(mChannelMask);
mFormat = mInput->stream->common.get_format(&mInput->stream->common);
- if (mFormat != AUDIO_FORMAT_PCM_16_BIT) {
- ALOGE("HAL format %d not supported; must be AUDIO_FORMAT_PCM_16_BIT", mFormat);
- }
mFrameSize = audio_stream_frame_size(&mInput->stream->common);
mBufferSize = mInput->stream->common.get_buffer_size(&mInput->stream->common);
mFrameCount = mBufferSize / mFrameSize;
@@ -5251,4 +5345,319 @@
return 0;
}
+// ----------------------------------------------------------------------------
+
+AudioFlinger::DirectAudioTrack::DirectAudioTrack(const sp<AudioFlinger>& audioFlinger,
+ int output, AudioSessionDescriptor *outputDesc,
+ IDirectTrackClient* client, audio_output_flags_t outflag)
+ : BnDirectTrack(), mIsPaused(false), mAudioFlinger(audioFlinger), mOutput(output), mOutputDesc(outputDesc),
+ mClient(client), mEffectConfigChanged(false), mKillEffectsThread(false), mFlag(outflag),
+ mEffectsThreadScratchBuffer(NULL)
+{
+#ifdef SRS_PROCESSING
+ ALOGD("SRS_Processing - DirectAudioTrack - OutNotify_Init: %p TID %d\n", this, gettid());
+ POSTPRO_PATCH_ICS_OUTPROC_DIRECT_INIT(this, gettid());
+ SRS_Processing::ProcessOutRoute(SRS_Processing::AUTO, this, outputDesc->device);
+#endif
+ if (mFlag & AUDIO_OUTPUT_FLAG_LPA) {
+ ALOGV("create effects thread for LPA");
+ createEffectThread();
+ allocateBufPool();
+#ifdef SRS_PROCESSING
+ } else if (mFlag & AUDIO_OUTPUT_FLAG_TUNNEL) {
+ ALOGV("create effects thread for TUNNEL");
+ createEffectThread();
+#endif
+ }
+ outputDesc->mVolumeScale = 1.0;
+ mDeathRecipient = new PMDeathRecipient(this);
+ acquireWakeLock();
+}
+
+void AudioFlinger::DirectAudioTrack::signalEffect() {
+ if (mFlag & AUDIO_OUTPUT_FLAG_LPA){
+ mEffectConfigChanged = true;
+ mEffectCv.signal();
+ }
+#ifdef SRS_PROCESSING
+ if (mFlag & AUDIO_OUTPUT_FLAG_TUNNEL){
+ mEffectConfigChanged = true;
+ mEffectCv.signal();
+ }
+#endif
+}
+
+AudioFlinger::DirectAudioTrack::~DirectAudioTrack() {
+#ifdef SRS_PROCESSING
+ ALOGD("SRS_Processing - DirectAudioTrack - OutNotify_Init: %p TID %d\n", this, gettid());
+ POSTPRO_PATCH_ICS_OUTPROC_DIRECT_EXIT(this, gettid());
+#endif
+ if (mFlag & AUDIO_OUTPUT_FLAG_LPA) {
+ requestAndWaitForEffectsThreadExit();
+ mAudioFlinger->deleteEffectSession();
+ deallocateBufPool();
+#ifdef SRS_PROCESSING
+ } else if (mFlag & AUDIO_OUTPUT_FLAG_TUNNEL) {
+ requestAndWaitForEffectsThreadExit();
+ mAudioFlinger->deleteEffectSession();
+#endif
+ }
+ AudioSystem::releaseOutput(mOutput);
+ releaseWakeLock();
+
+ {
+ Mutex::Autolock _l(pmLock);
+ if (mPowerManager != 0) {
+ sp<IBinder> binder = mPowerManager->asBinder();
+ binder->unlinkToDeath(mDeathRecipient);
+ }
+ }
+}
+
+status_t AudioFlinger::DirectAudioTrack::start() {
+ AudioSystem::startOutput(mOutput, (audio_stream_type_t)mOutputDesc->mStreamType);
+ if(mIsPaused) {
+ mIsPaused = false;
+ mOutputDesc->stream->start(mOutputDesc->stream);
+ }
+ mOutputDesc->mActive = true;
+ mOutputDesc->stream->set_volume(mOutputDesc->stream,
+ mOutputDesc->mVolumeLeft * mOutputDesc->mVolumeScale,
+ mOutputDesc->mVolumeRight* mOutputDesc->mVolumeScale);
+ return NO_ERROR;
+}
+
+void AudioFlinger::DirectAudioTrack::stop() {
+ ALOGV("DirectAudioTrack::stop");
+ mOutputDesc->mActive = false;
+ mOutputDesc->stream->stop(mOutputDesc->stream);
+ AudioSystem::stopOutput(mOutput, (audio_stream_type_t)mOutputDesc->mStreamType);
+}
+
+void AudioFlinger::DirectAudioTrack::pause() {
+ if(!mIsPaused) {
+ mIsPaused = true;
+ mOutputDesc->stream->pause(mOutputDesc->stream);
+ mOutputDesc->mActive = false;
+ AudioSystem::stopOutput(mOutput, (audio_stream_type_t)mOutputDesc->mStreamType);
+ }
+}
+
+ssize_t AudioFlinger::DirectAudioTrack::write(const void *buffer, size_t size) {
+ ALOGV("Writing to AudioSessionOut");
+ int isAvail = 0;
+ mOutputDesc->stream->is_buffer_available(mOutputDesc->stream, &isAvail);
+ if (!isAvail) {
+ return 0;
+ }
+
+ if (mFlag & AUDIO_OUTPUT_FLAG_LPA) {
+ mEffectLock.lock();
+ List<BufferInfo>::iterator it = mEffectsPool.begin();
+ BufferInfo buf = *it;
+ mEffectsPool.erase(it);
+ memcpy((char *) buf.localBuf, (char *)buffer, size);
+ buf.bytesToWrite = size;
+ mEffectsPool.push_back(buf);
+ mAudioFlinger->applyEffectsOn(static_cast<void *>(this),
+ (int16_t*)buf.localBuf, (int16_t*)buffer, (int)size, true);
+ mEffectLock.unlock();
+ }
+ ALOGV("out of Writing to AudioSessionOut");
+ return mOutputDesc->stream->write(mOutputDesc->stream, buffer, size);
+}
+
+void AudioFlinger::DirectAudioTrack::flush() {
+ if (mFlag & AUDIO_OUTPUT_FLAG_LPA) {
+ mEffectLock.lock();
+ mEffectsPool.clear();
+ mEffectsPool = mBufPool;
+ mEffectLock.unlock();
+ }
+ mOutputDesc->stream->flush(mOutputDesc->stream);
+}
+
+void AudioFlinger::DirectAudioTrack::mute(bool muted) {
+}
+
+void AudioFlinger::DirectAudioTrack::setVolume(float left, float right) {
+ ALOGV("DirectAudioTrack::setVolume left: %f, right: %f", left, right);
+ if(mOutputDesc) {
+ mOutputDesc->mVolumeLeft = left;
+ mOutputDesc->mVolumeRight = right;
+ if(mOutputDesc->mActive && mOutputDesc->stream) {
+ mOutputDesc->stream->set_volume(mOutputDesc->stream,
+ left * mOutputDesc->mVolumeScale,
+ right* mOutputDesc->mVolumeScale);
+ } else {
+ ALOGD("stream is not active, so cache and send when stream is active");
+ }
+ } else {
+ ALOGD("output descriptor is not valid to set the volume");
+ }
+}
+
+int64_t AudioFlinger::DirectAudioTrack::getTimeStamp() {
+ int64_t time;
+ mOutputDesc->stream->get_next_write_timestamp(mOutputDesc->stream, &time);
+ ALOGV("Timestamp %lld",time);
+ return time;
+}
+
+void AudioFlinger::DirectAudioTrack::postEOS(int64_t delayUs) {
+ if (delayUs == 0 ) {
+ ALOGV("Notify Audio Track of EOS event");
+ mClient->notify(DIRECT_TRACK_EOS);
+ } else {
+ ALOGV("Notify Audio Track of hardware failure event");
+ mClient->notify(DIRECT_TRACK_HW_FAIL);
+ }
+}
+
+void AudioFlinger::DirectAudioTrack::allocateBufPool() {
+ void *dsp_buf = NULL;
+ void *local_buf = NULL;
+
+ //1. get the ion buffer information
+ struct buf_info* buf = NULL;
+ mOutputDesc->stream->get_buffer_info(mOutputDesc->stream, &buf);
+ ALOGV("get buffer info %p",buf);
+ if (!buf) {
+ ALOGV("buffer is NULL");
+ return;
+ }
+ int nSize = buf->bufsize;
+ int bufferCount = buf->nBufs;
+
+ //2. allocate the buffer pool, allocate local buffers
+ for (int i = 0; i < bufferCount; i++) {
+ dsp_buf = (void *)buf->buffers[i];
+ local_buf = malloc(nSize);
+ memset(local_buf, 0, nSize);
+ // Store this information for internal mapping / maintanence
+ BufferInfo buf(local_buf, dsp_buf, nSize);
+ buf.bytesToWrite = 0;
+ mBufPool.push_back(buf);
+ mEffectsPool.push_back(buf);
+
+ ALOGV("The MEM that is allocated buffer is %x, size %d",(unsigned int)dsp_buf,nSize);
+ }
+
+ mEffectsThreadScratchBuffer = malloc(nSize);
+ ALOGV("effectsThreadScratchBuffer = %x",mEffectsThreadScratchBuffer);
+
+ free(buf);
+}
+
+void AudioFlinger::DirectAudioTrack::deallocateBufPool() {
+
+ //1. Deallocate the local memory
+ //2. Remove all the buffers from bufpool
+ mEffectLock.lock();
+ while (!mBufPool.empty()) {
+ List<BufferInfo>::iterator it = mBufPool.begin();
+ BufferInfo &memBuffer = *it;
+ // free the local buffer corresponding to mem buffer
+ if (memBuffer.localBuf) {
+ free(memBuffer.localBuf);
+ memBuffer.localBuf = NULL;
+ }
+ ALOGV("Removing from bufpool");
+ mBufPool.erase(it);
+ }
+ mEffectsPool.clear();
+ mEffectLock.unlock();
+
+ free(mEffectsThreadScratchBuffer);
+ mEffectsThreadScratchBuffer = NULL;
+}
+
+status_t AudioFlinger::DirectAudioTrack::onTransact(
+ uint32_t code, const Parcel& data, Parcel* reply, uint32_t flags)
+{
+ return BnDirectTrack::onTransact(code, data, reply, flags);
+}
+
+AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient::AudioFlingerDirectTrackClient(void *obj)
+{
+ ALOGV("AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient");
+ pBaseClass = (DirectAudioTrack*)obj;
+}
+
+void AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient::binderDied(const wp<IBinder>& who) {
+ pBaseClass->mAudioFlinger.clear();
+ ALOGW("AudioFlinger server died!");
+}
+
+void AudioFlinger::DirectAudioTrack::AudioFlingerDirectTrackClient
+ ::ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2) {
+ ALOGV("ioConfigChanged() event %d", event);
+ if (event == AudioSystem::EFFECT_CONFIG_CHANGED) {
+ ALOGV("Received notification for change in effect module");
+ // Seek to current media time - flush the decoded buffers with the driver
+ pBaseClass->mEffectConfigChanged = true;
+ // Signal effects thread to re-apply effects
+ ALOGV("Signalling Effects Thread");
+ pBaseClass->mEffectCv.signal();
+
+ }
+ ALOGV("ioConfigChanged Out");
+}
+
+void AudioFlinger::DirectAudioTrack::acquireWakeLock()
+{
+ Mutex::Autolock _l(pmLock);
+
+ if (mPowerManager == 0) {
+ // use checkService() to avoid blocking if power service is not up yet
+ sp<IBinder> binder =
+ defaultServiceManager()->checkService(String16("power"));
+ if (binder == 0) {
+ ALOGW("Thread %s cannot connect to the power manager service", lockName);
+ } else {
+ mPowerManager = interface_cast<IPowerManager>(binder);
+ binder->linkToDeath(mDeathRecipient);
+ }
+ }
+ if (mPowerManager != 0 && mWakeLockToken == 0) {
+ sp<IBinder> binder = new BBinder();
+ status_t status = mPowerManager->acquireWakeLock(POWERMANAGER_PARTIAL_WAKE_LOCK,
+ binder,
+ String16(lockName),
+ String16("media"));
+ if (status == NO_ERROR) {
+ mWakeLockToken = binder;
+ }
+ ALOGV("acquireWakeLock() status %d", status);
+ }
+}
+
+void AudioFlinger::DirectAudioTrack::releaseWakeLock()
+{
+ Mutex::Autolock _l(pmLock);
+
+ if (mWakeLockToken != 0) {
+ ALOGV("releaseWakeLock()");
+ if (mPowerManager != 0) {
+ mPowerManager->releaseWakeLock(mWakeLockToken, 0);
+ }
+ mWakeLockToken.clear();
+ }
+}
+
+void AudioFlinger::DirectAudioTrack::clearPowerManager()
+{
+ releaseWakeLock();
+ Mutex::Autolock _l(pmLock);
+ mPowerManager.clear();
+}
+
+void AudioFlinger::DirectAudioTrack::PMDeathRecipient::binderDied(const wp<IBinder>& who)
+{
+ parentClass->clearPowerManager();
+ ALOGW("power manager service died !!!");
+}
+
+// ----------------------------------------------------------------------------
+
}; // namespace android
diff --git a/services/audioflinger/Threads.h b/services/audioflinger/Threads.h
index a5d2169..eff4bb7 100644
--- a/services/audioflinger/Threads.h
+++ b/services/audioflinger/Threads.h
@@ -1,5 +1,6 @@
/*
-**
+** Copyright (c) 2013, The Linux Foundation. All rights reserved.
+** Not a Contribution.
** Copyright 2012, The Android Open Source Project
**
** Licensed under the Apache License, Version 2.0 (the "License");
@@ -137,6 +138,7 @@
virtual status_t setParameters(const String8& keyValuePairs);
virtual String8 getParameters(const String8& keys) = 0;
virtual void audioConfigChanged_l(int event, int param = 0) = 0;
+ void effectConfigChanged();
void sendIoConfigEvent(int event, int param = 0);
void sendIoConfigEvent_l(int event, int param = 0);
void sendPrioConfigEvent_l(pid_t pid, pid_t tid, int32_t prio);
diff --git a/services/audioflinger/TrackBase.h b/services/audioflinger/TrackBase.h
index 523e4b2..065e020 100644
--- a/services/audioflinger/TrackBase.h
+++ b/services/audioflinger/TrackBase.h
@@ -43,6 +43,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
+ uint32_t flags,
const sp<IMemory>& sharedBuffer,
int sessionId,
bool isOut);
@@ -130,7 +131,7 @@
// 8-bit PCM samples are stored as 16-bit
const size_t mFrameCount;// size of track buffer given at createTrack() or
// openRecord(), and then adjusted as needed
-
+ uint32_t mFlags;
const int mSessionId;
Vector < sp<SyncEvent> >mSyncEvents;
const bool mIsOut;
diff --git a/services/audioflinger/Tracks.cpp b/services/audioflinger/Tracks.cpp
index 363bc9d..436d4de 100644
--- a/services/audioflinger/Tracks.cpp
+++ b/services/audioflinger/Tracks.cpp
@@ -66,6 +66,7 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
+ uint32_t flags,
const sp<IMemory>& sharedBuffer,
int sessionId,
bool isOut)
@@ -79,9 +80,10 @@
mFormat(format),
mChannelMask(channelMask),
mChannelCount(popcount(channelMask)),
- mFrameSize(audio_is_linear_pcm(format) ?
- mChannelCount * audio_bytes_per_sample(format) : sizeof(int8_t)),
+ mFrameSize((audio_is_linear_pcm(format) || audio_is_supported_compressed(format)) ?
+ ((flags & IAudioFlinger::TRACK_VOICE_COMMUNICATION)? mChannelCount * sizeof(int16_t) : mChannelCount * audio_bytes_per_sample(format)) : sizeof(int8_t)),
mFrameCount(frameCount),
+ mFlags(0),
mSessionId(sessionId),
mIsOut(isOut),
mServerProxy(NULL),
@@ -96,7 +98,27 @@
// ALOGD("Creating track with %d buffers @ %d bytes", bufferCount, bufferSize);
size_t size = sizeof(audio_track_cblk_t);
- size_t bufferSize = (sharedBuffer == 0 ? roundup(frameCount) : frameCount) * mFrameSize;
+ uint8_t channelCount = popcount(channelMask);
+ size_t bufferSize = 0;
+ if (flags & IAudioFlinger::TRACK_VOICE_COMMUNICATION) {
+ bufferSize = roundup(frameCount) * channelCount * mFrameSize;
+ } else {
+ if ( (format == AUDIO_FORMAT_PCM_16_BIT) ||
+ (format == AUDIO_FORMAT_PCM_8_BIT)) {
+ bufferSize = frameCount * channelCount * sizeof(int16_t);
+ } else if (format == AUDIO_FORMAT_AMR_NB) {
+ bufferSize = frameCount * channelCount * AMR_FRAMESIZE; // full rate frame size
+ } else if (format == AUDIO_FORMAT_EVRC) {
+ bufferSize = frameCount * channelCount * EVRC_FRAMESIZE; // full rate frame size
+ } else if (format == AUDIO_FORMAT_QCELP) {
+ bufferSize = frameCount * channelCount * QCELP_FRAMESIZE; // full rate frame size
+ } else if (format == AUDIO_FORMAT_AAC) {
+ bufferSize = frameCount * AAC_FRAMESIZE; // full rate frame size
+ } else if (format == AUDIO_FORMAT_AMR_WB) {
+ bufferSize = frameCount * channelCount * AMR_WB_FRAMESIZE; // full rate frame size
+ }
+ }
+
if (sharedBuffer == 0) {
size += bufferSize;
}
@@ -124,7 +146,29 @@
mCblk->frameCount_ = frameCount;
if (sharedBuffer == 0) {
mBuffer = (char*)mCblk + sizeof(audio_track_cblk_t);
- memset(mBuffer, 0, bufferSize);
+ if (flags & IAudioFlinger::TRACK_VOICE_COMMUNICATION) {
+ memset(mBuffer, 0, bufferSize);
+ } else {
+ if ((format == AUDIO_FORMAT_PCM_16_BIT) ||
+ (format == AUDIO_FORMAT_PCM_8_BIT)) {
+ memset(mBuffer, 0, bufferSize);
+ } else if (format == AUDIO_FORMAT_AMR_NB) {
+ // full rate frame size
+ memset(mBuffer, 0, frameCount * channelCount * AMR_FRAMESIZE);
+ } else if (format == AUDIO_FORMAT_EVRC) {
+ // full rate frame size
+ memset(mBuffer, 0, frameCount * channelCount * EVRC_FRAMESIZE);
+ } else if (format == AUDIO_FORMAT_QCELP) {
+ // full rate frame size
+ memset(mBuffer, 0, frameCount * channelCount * QCELP_FRAMESIZE);
+ } else if (format == AUDIO_FORMAT_AAC) {
+ // full rate frame size
+ memset(mBuffer, 0, frameCount * AAC_FRAMESIZE);
+ } else if (format == AUDIO_FORMAT_AMR_WB) {
+ // full rate frame size
+ memset(mBuffer, 0, frameCount * channelCount * AMR_WB_FRAMESIZE);
+ }
+ }
} else {
mBuffer = sharedBuffer->pointer();
#if 0
@@ -314,8 +358,9 @@
const sp<IMemory>& sharedBuffer,
int sessionId,
IAudioFlinger::track_flags_t flags)
- : TrackBase(thread, client, sampleRate, format, channelMask, frameCount, sharedBuffer,
- sessionId, true /*isOut*/),
+ : TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
+ ((audio_stream_type_t)streamType == AUDIO_STREAM_VOICE_CALL)? IAudioFlinger::TRACK_VOICE_COMMUNICATION:0x0,
+ sharedBuffer, sessionId, true /*isOut*/),
mFillingUpStatus(FS_INVALID),
// mRetryCount initialized later when needed
mSharedBuffer(sharedBuffer),
@@ -1738,12 +1783,14 @@
audio_format_t format,
audio_channel_mask_t channelMask,
size_t frameCount,
+ uint32_t flags,
int sessionId)
- : TrackBase(thread, client, sampleRate, format,
- channelMask, frameCount, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
+ : TrackBase(thread, client, sampleRate, format, channelMask, frameCount,
+ flags, 0 /*sharedBuffer*/, sessionId, false /*isOut*/),
mOverflow(false)
{
ALOGV("RecordTrack constructor");
+ mFlags = flags;
if (mCblk != NULL) {
mAudioRecordServerProxy = new AudioRecordServerProxy(mCblk, mBuffer, frameCount,
mFrameSize);