summaryrefslogtreecommitdiffstats
path: root/media
diff options
context:
space:
mode:
Diffstat (limited to 'media')
-rw-r--r--media/java/android/media/AudioService.java82
-rw-r--r--media/java/android/media/AudioSystem.java1
-rw-r--r--media/java/android/media/CamcorderProfile.java7
-rw-r--r--media/java/android/media/MediaActionSound.java198
-rw-r--r--media/java/android/media/MediaCodec.java210
-rw-r--r--media/java/android/media/MediaExtractor.java78
-rw-r--r--media/java/android/media/MediaPlayer.java47
-rw-r--r--media/java/android/media/MediaRecorder.java14
-rw-r--r--media/java/android/media/MediaScanner.java249
-rwxr-xr-xmedia/java/android/media/audiofx/Visualizer.java87
-rw-r--r--media/jni/Android.mk5
-rw-r--r--media/jni/android_media_MediaCodec.cpp552
-rw-r--r--media/jni/android_media_MediaCodec.h81
-rw-r--r--media/jni/android_media_MediaExtractor.cpp400
-rw-r--r--media/jni/android_media_MediaExtractor.h60
-rw-r--r--media/jni/android_media_MediaPlayer.cpp54
-rw-r--r--media/jni/android_media_MediaRecorder.cpp2
-rw-r--r--media/jni/android_media_Utils.cpp262
-rw-r--r--media/jni/android_media_Utils.h8
-rw-r--r--media/jni/audioeffect/android_media_Visualizer.cpp108
-rwxr-xr-xmedia/jni/mediaeditor/VideoEditorMain.cpp3
-rw-r--r--media/libaah_rtp/Android.mk40
-rw-r--r--media/libaah_rtp/aah_decoder_pump.cpp520
-rw-r--r--media/libaah_rtp/aah_decoder_pump.h107
-rw-r--r--media/libaah_rtp/aah_rx_player.cpp288
-rw-r--r--media/libaah_rtp/aah_rx_player.h318
-rw-r--r--media/libaah_rtp/aah_rx_player_core.cpp809
-rw-r--r--media/libaah_rtp/aah_rx_player_ring_buffer.cpp366
-rw-r--r--media/libaah_rtp/aah_rx_player_substream.cpp677
-rw-r--r--media/libaah_rtp/aah_tx_packet.cpp344
-rw-r--r--media/libaah_rtp/aah_tx_packet.h213
-rw-r--r--media/libaah_rtp/aah_tx_player.cpp1177
-rw-r--r--media/libaah_rtp/aah_tx_player.h176
-rw-r--r--media/libaah_rtp/aah_tx_sender.cpp603
-rw-r--r--media/libaah_rtp/aah_tx_sender.h162
-rw-r--r--media/libaah_rtp/pipe_event.cpp86
-rw-r--r--media/libaah_rtp/pipe_event.h51
-rw-r--r--media/libeffects/data/audio_effects.conf4
-rw-r--r--media/libeffects/downmix/Android.mk28
-rw-r--r--media/libeffects/downmix/EffectDownmix.c889
-rw-r--r--media/libeffects/downmix/EffectDownmix.h96
-rwxr-xr-xmedia/libeffects/preprocessing/PreProcessing.cpp15
-rw-r--r--media/libmedia/AudioEffect.cpp14
-rw-r--r--media/libmedia/AudioRecord.cpp16
-rw-r--r--media/libmedia/AudioSystem.cpp10
-rw-r--r--media/libmedia/AudioTrack.cpp92
-rw-r--r--media/libmedia/IAudioFlinger.cpp5
-rw-r--r--media/libmedia/IAudioFlingerClient.cpp8
-rw-r--r--media/libmedia/IAudioTrack.cpp81
-rw-r--r--media/libmedia/IEffect.cpp11
-rw-r--r--media/libmedia/IMediaPlayer.cpp37
-rw-r--r--media/libmedia/IMediaRecorder.cpp2
-rw-r--r--media/libmedia/IOMX.cpp2
-rw-r--r--media/libmedia/Visualizer.cpp2
-rw-r--r--media/libmedia/mediaplayer.cpp55
-rw-r--r--media/libmedia/mediarecorder.cpp1
-rw-r--r--media/libmediaplayerservice/Android.mk3
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.cpp237
-rw-r--r--media/libmediaplayerservice/MediaPlayerService.h20
-rw-r--r--media/libmediaplayerservice/MidiFile.cpp3
-rw-r--r--media/libmediaplayerservice/StagefrightRecorder.cpp8
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.cpp16
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayer.h2
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp11
-rw-r--r--media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp7
-rw-r--r--media/libmediaplayerservice/nuplayer/RTSPSource.cpp6
-rw-r--r--media/libstagefright/ACodec.cpp1272
-rw-r--r--media/libstagefright/Android.mk86
-rw-r--r--media/libstagefright/AudioPlayer.cpp27
-rw-r--r--media/libstagefright/AudioSource.cpp57
-rw-r--r--media/libstagefright/AwesomePlayer.cpp5
-rwxr-xr-xmedia/libstagefright/CameraSource.cpp2
-rw-r--r--media/libstagefright/MPEG2TSWriter.cpp12
-rw-r--r--media/libstagefright/MediaCodec.cpp1217
-rw-r--r--media/libstagefright/MediaCodecList.cpp475
-rw-r--r--media/libstagefright/MediaSourceSplitter.cpp234
-rw-r--r--media/libstagefright/NuMediaExtractor.cpp433
-rw-r--r--media/libstagefright/OMXClient.cpp4
-rwxr-xr-xmedia/libstagefright/OMXCodec.cpp339
-rw-r--r--media/libstagefright/SurfaceMediaSource.cpp5
-rw-r--r--media/libstagefright/TimedEventQueue.cpp2
-rw-r--r--media/libstagefright/codecs/aacenc/Android.mk2
-rw-r--r--media/libstagefright/codecs/aacenc/basic_op/basic_op.h197
-rw-r--r--media/libstagefright/codecs/aacenc/basic_op/oper_32b.h2
-rw-r--r--media/libstagefright/codecs/aacenc/basic_op/typedefs.h11
-rw-r--r--media/libstagefright/codecs/aacenc/inc/aacenc_core.h2
-rw-r--r--media/libstagefright/codecs/aacenc/inc/bitbuffer.h2
-rw-r--r--media/libstagefright/codecs/aacenc/inc/psy_configuration.h4
-rw-r--r--media/libstagefright/codecs/aacenc/src/aacenc_core.c2
-rw-r--r--media/libstagefright/codecs/aacenc/src/adj_thr.c2
-rw-r--r--media/libstagefright/codecs/aacenc/src/bitbuffer.c3
-rw-r--r--media/libstagefright/codecs/aacenc/src/dyn_bits.c2
-rw-r--r--media/libstagefright/codecs/aacenc/src/interface.c4
-rw-r--r--media/libstagefright/codecs/aacenc/src/psy_configuration.c4
-rw-r--r--media/libstagefright/codecs/aacenc/src/psy_main.c3
-rw-r--r--media/libstagefright/codecs/aacenc/src/qc_main.c2
-rw-r--r--media/libstagefright/codecs/aacenc/src/quantize.c4
-rw-r--r--media/libstagefright/codecs/aacenc/src/sf_estim.c2
-rw-r--r--media/libstagefright/codecs/aacenc/src/transform.c6
-rw-r--r--media/libstagefright/codecs/amrnb/common/include/az_lsp.h2
-rw-r--r--media/libstagefright/codecs/amrnb/common/include/inv_sqrt.h2
-rw-r--r--media/libstagefright/codecs/amrnb/common/include/log2_norm.h2
-rw-r--r--media/libstagefright/codecs/amrnb/common/include/pow2.h2
-rw-r--r--media/libstagefright/codecs/amrnb/common/include/sqrt_l.h2
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp2
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/bitno_tab.cpp24
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/bitreorder_tab.cpp23
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/bytesused.cpp2
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/c2_9pf_tab.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/gains_tbl.cpp6
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/gray_tbl.cpp6
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/grid_tbl.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/inv_sqrt_tbl.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/log2_tbl.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/lsp_lsf_tbl.cpp6
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/lsp_tab.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/overflow_tbl.cpp2
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/ph_disp_tab.cpp12
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/pow2_tbl.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/q_plsf_5_tbl.cpp13
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/qua_gain_tbl.cpp5
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/sqrt_l_tbl.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/common/src/window_tab.cpp7
-rw-r--r--media/libstagefright/codecs/amrnb/dec/src/dec_input_format_tab.cpp5
-rw-r--r--media/libstagefright/codecs/amrnb/dec/src/qgain475_tab.cpp2
-rw-r--r--media/libstagefright/codecs/amrnb/enc/src/corrwght_tab.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/enc/src/enc_output_format_tab.cpp5
-rw-r--r--media/libstagefright/codecs/amrnb/enc/src/inter_36_tab.cpp3
-rw-r--r--media/libstagefright/codecs/amrnb/enc/src/lag_wind_tab.cpp5
-rw-r--r--media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp4
-rw-r--r--media/libstagefright/codecs/amrnb/enc/src/ton_stab.cpp3
-rw-r--r--media/libstagefright/codecs/amrwb/include/pvamrwbdecoder_api.h2
-rw-r--r--media/libstagefright/codecs/amrwb/src/get_amr_wb_bits.cpp3
-rw-r--r--media/libstagefright/codecs/amrwb/src/homing_amr_wb_dec.cpp20
-rw-r--r--media/libstagefright/codecs/amrwb/src/isp_isf.cpp2
-rw-r--r--media/libstagefright/codecs/amrwb/src/oversamp_12k8_to_16k.cpp6
-rw-r--r--media/libstagefright/codecs/amrwb/src/phase_dispersion.cpp4
-rw-r--r--media/libstagefright/codecs/amrwbenc/inc/isp_isf.tab4
-rw-r--r--media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c2
-rw-r--r--media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp2
-rw-r--r--media/libstagefright/colorconversion/SoftwareRenderer.cpp5
-rw-r--r--media/libstagefright/foundation/AMessage.cpp39
-rw-r--r--media/libstagefright/include/SoftwareRenderer.h2
-rw-r--r--media/libstagefright/rtsp/AAMRAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AAVCAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AH263Assembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/ARTPConnection.cpp8
-rw-r--r--media/libstagefright/rtsp/ARTPSession.cpp6
-rw-r--r--media/libstagefright/rtsp/ARTSPConnection.cpp2
-rw-r--r--media/libstagefright/rtsp/ARawAudioAssembler.cpp2
-rw-r--r--media/libstagefright/rtsp/MyHandler.h13
-rw-r--r--media/libstagefright/tests/SurfaceMediaSource_test.cpp8
-rw-r--r--media/libstagefright/timedtext/Android.mk6
-rw-r--r--media/libstagefright/timedtext/TimedText3GPPSource.cpp (renamed from media/libstagefright/timedtext/TimedTextInBandSource.cpp)67
-rw-r--r--media/libstagefright/timedtext/TimedText3GPPSource.h (renamed from media/libstagefright/timedtext/TimedTextInBandSource.h)14
-rw-r--r--media/libstagefright/timedtext/TimedTextDriver.cpp3
-rw-r--r--media/libstagefright/timedtext/TimedTextDriver.h81
-rw-r--r--media/libstagefright/timedtext/TimedTextPlayer.cpp2
-rw-r--r--media/libstagefright/timedtext/TimedTextSource.cpp13
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java4
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java13
-rw-r--r--media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java111
-rwxr-xr-xmedia/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java17
165 files changed, 13696 insertions, 1494 deletions
diff --git a/media/java/android/media/AudioService.java b/media/java/android/media/AudioService.java
index eae03be..aa60d0a 100644
--- a/media/java/android/media/AudioService.java
+++ b/media/java/android/media/AudioService.java
@@ -591,7 +591,7 @@ public class AudioService extends IAudioService.Stub {
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
- SENDMSG_REPLACE,
+ SENDMSG_QUEUE,
PERSIST_LAST_AUDIBLE,
device,
s,
@@ -606,7 +606,7 @@ public class AudioService extends IAudioService.Stub {
// to persist). Do not change volume if stream is muted.
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
- SENDMSG_NOOP,
+ SENDMSG_QUEUE,
device,
0,
streamState,
@@ -746,7 +746,7 @@ public class AudioService extends IAudioService.Stub {
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
- SENDMSG_REPLACE,
+ SENDMSG_QUEUE,
PERSIST_LAST_AUDIBLE,
device,
streamState,
@@ -758,7 +758,7 @@ public class AudioService extends IAudioService.Stub {
// to persist).
sendMsg(mAudioHandler,
MSG_SET_DEVICE_VOLUME,
- SENDMSG_NOOP,
+ SENDMSG_QUEUE,
device,
0,
streamState,
@@ -2035,20 +2035,11 @@ public class AudioService extends IAudioService.Stub {
}
public void readSettings() {
- int index = Settings.System.getInt(mContentResolver,
- mVolumeIndexSettingName,
- AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
-
- mIndex.clear();
- mIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
-
- index = Settings.System.getInt(mContentResolver,
- mLastAudibleVolumeIndexSettingName,
- (index > 0) ? index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType]);
- mLastAudibleIndex.clear();
- mLastAudibleIndex.put(AudioSystem.DEVICE_OUT_DEFAULT, index);
+ boolean checkSilentVolume = (mRingerMode == AudioManager.RINGER_MODE_NORMAL) &&
+ isStreamAffectedByRingerMode(mStreamType);
int remainingDevices = AudioSystem.DEVICE_OUT_ALL;
+
for (int i = 0; remainingDevices != 0; i++) {
int device = (1 << i);
if ((device & remainingDevices) == 0) {
@@ -2057,17 +2048,58 @@ public class AudioService extends IAudioService.Stub {
remainingDevices &= ~device;
// retrieve current volume for device
- String name = getSettingNameForDevice(false, device);
- index = Settings.System.getInt(mContentResolver, name, -1);
+ String name = getSettingNameForDevice(false /* lastAudible */, device);
+ // if no volume stored for current stream and device, use default volume if default
+ // device, continue otherwise
+ int defaultIndex = (device == AudioSystem.DEVICE_OUT_DEFAULT) ?
+ AudioManager.DEFAULT_STREAM_VOLUME[mStreamType] : -1;
+ int index = Settings.System.getInt(mContentResolver, name, defaultIndex);
if (index == -1) {
continue;
}
- mIndex.put(device, getValidIndex(10 * index));
// retrieve last audible volume for device
- name = getSettingNameForDevice(true, device);
- index = Settings.System.getInt(mContentResolver, name, -1);
- mLastAudibleIndex.put(device, getValidIndex(10 * index));
+ name = getSettingNameForDevice(true /* lastAudible */, device);
+ // use stored last audible index if present, otherwise use current index if not 0
+ // or default index
+ defaultIndex = (index > 0) ?
+ index : AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+ int lastAudibleIndex = Settings.System.getInt(mContentResolver, name, defaultIndex);
+
+ // a last audible index of 0 is never stored, except on non-voice capable devices
+ // (e.g. tablets) for the music stream type, where the music stream volume can reach
+ // 0 without the device being in silent mode
+ if ((lastAudibleIndex == 0) &&
+ (mVoiceCapable ||
+ (STREAM_VOLUME_ALIAS[mStreamType] != AudioSystem.STREAM_MUSIC))) {
+ lastAudibleIndex = AudioManager.DEFAULT_STREAM_VOLUME[mStreamType];
+ // Correct the data base
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_QUEUE,
+ PERSIST_LAST_AUDIBLE,
+ device,
+ this,
+ PERSIST_DELAY);
+ }
+ mLastAudibleIndex.put(device, getValidIndex(10 * lastAudibleIndex));
+ // the initial index should never be 0 for a stream affected by ringer mode if not
+ // in silent or vibrate mode.
+ // this is permitted on tablets for music stream type.
+ if (checkSilentVolume && (index == 0) &&
+ (mVoiceCapable ||
+ (STREAM_VOLUME_ALIAS[mStreamType] != AudioSystem.STREAM_MUSIC))) {
+ index = lastAudibleIndex;
+ // Correct the data base
+ sendMsg(mAudioHandler,
+ MSG_PERSIST_VOLUME,
+ SENDMSG_QUEUE,
+ PERSIST_CURRENT,
+ device,
+ this,
+ PERSIST_DELAY);
+ }
+ mIndex.put(device, getValidIndex(10 * index));
}
}
@@ -2208,7 +2240,7 @@ public class AudioService extends IAudioService.Stub {
}
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
- SENDMSG_NOOP,
+ SENDMSG_QUEUE,
0,
0,
VolumeStreamState.this, 0);
@@ -2252,7 +2284,7 @@ public class AudioService extends IAudioService.Stub {
}
sendMsg(mAudioHandler,
MSG_SET_ALL_VOLUMES,
- SENDMSG_NOOP,
+ SENDMSG_QUEUE,
0,
0,
VolumeStreamState.this, 0);
@@ -2350,7 +2382,7 @@ public class AudioService extends IAudioService.Stub {
// Post a persist volume msg
sendMsg(mAudioHandler,
MSG_PERSIST_VOLUME,
- SENDMSG_REPLACE,
+ SENDMSG_QUEUE,
PERSIST_CURRENT|PERSIST_LAST_AUDIBLE,
device,
streamState,
diff --git a/media/java/android/media/AudioSystem.java b/media/java/android/media/AudioSystem.java
index d354cdb..b5e832c 100644
--- a/media/java/android/media/AudioSystem.java
+++ b/media/java/android/media/AudioSystem.java
@@ -279,6 +279,7 @@ public class AudioSystem
return DEVICE_OUT_ANLG_DOCK_HEADSET_NAME;
case DEVICE_OUT_DGTL_DOCK_HEADSET:
return DEVICE_OUT_DGTL_DOCK_HEADSET_NAME;
+ case DEVICE_IN_DEFAULT:
default:
return "";
}
diff --git a/media/java/android/media/CamcorderProfile.java b/media/java/android/media/CamcorderProfile.java
index e94bddc..511111c 100644
--- a/media/java/android/media/CamcorderProfile.java
+++ b/media/java/android/media/CamcorderProfile.java
@@ -67,6 +67,8 @@ public class CamcorderProfile
/**
* Quality level corresponding to the 480p (720 x 480) resolution.
+ * Note that the horizontal resolution for 480p can also be other
+ * values, such as 640 or 704, instead of 720.
*/
public static final int QUALITY_480P = 4;
@@ -76,7 +78,10 @@ public class CamcorderProfile
public static final int QUALITY_720P = 5;
/**
- * Quality level corresponding to the 1080p (1920 x 1088) resolution.
+ * Quality level corresponding to the 1080p (1920 x 1080) resolution.
+ * Note that the vertical resolution for 1080p can also be 1088,
+ * instead of 1080 (used by some vendors to avoid cropping during
+ * video playback).
*/
public static final int QUALITY_1080P = 6;
diff --git a/media/java/android/media/MediaActionSound.java b/media/java/android/media/MediaActionSound.java
new file mode 100644
index 0000000..7a520fe
--- /dev/null
+++ b/media/java/android/media/MediaActionSound.java
@@ -0,0 +1,198 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package android.media;
+
+import android.media.AudioManager;
+import android.media.SoundPool;
+import android.util.Log;
+
+/**
+ * <p>A class for producing sounds that match those produced by various actions
+ * taken by the media and camera APIs. </p>
+ *
+ * <p>Use this class to play an appropriate camera operation sound when
+ * implementing a custom still or video recording mechanism (through the Camera
+ * preview callbacks with {@link android.hardware.Camera#setPreviewCallback
+ * Camera.setPreviewCallback}, or through GPU processing with {@link
+ * android.hardware.Camera#setPreviewTexture Camera.setPreviewTexture}, for
+ * example), or when implementing some other camera-like function in your
+ * application.</p>
+ *
+ * <p>There is no need to play sounds when using
+ * {@link android.hardware.Camera#takePicture Camera.takePicture} or
+ * {@link android.media.MediaRecorder} for still images or video, respectively,
+ * as the Android framework will play the appropriate sounds when needed for
+ * these calls.</p>
+ *
+ */
+public class MediaActionSound {
+ private static final int NUM_MEDIA_SOUND_STREAMS = 1;
+
+ private SoundPool mSoundPool;
+ private int[] mSoundIds;
+ private int mSoundIdToPlay;
+
+ private static final String[] SOUND_FILES = {
+ "/system/media/audio/ui/camera_click.ogg",
+ "/system/media/audio/ui/camera_focus.ogg",
+ "/system/media/audio/ui/VideoRecord.ogg",
+ "/system/media/audio/ui/VideoRecord.ogg"
+ };
+
+ private static final String TAG = "MediaActionSound";
+ /**
+ * The sound used by
+ * {@link android.hardware.Camera#takePicture Camera.takePicture} to
+ * indicate still image capture.
+ * @see #play
+ */
+ public static final int SHUTTER_CLICK = 0;
+
+ /**
+ * A sound to indicate that focusing has completed. Because deciding
+ * when this occurs is application-dependent, this sound is not used by
+ * any methods in the media or camera APIs.
+ * @see #play
+ */
+ public static final int FOCUS_COMPLETE = 1;
+
+ /**
+ * The sound used by
+ * {@link android.media.MediaRecorder#start MediaRecorder.start()} to
+ * indicate the start of video recording.
+ * @see #play
+ */
+ public static final int START_VIDEO_RECORDING = 2;
+
+ /**
+ * The sound used by
+ * {@link android.media.MediaRecorder#stop MediaRecorder.stop()} to
+ * indicate the end of video recording.
+ * @see #play
+ */
+ public static final int STOP_VIDEO_RECORDING = 3;
+
+ private static final int SOUND_NOT_LOADED = -1;
+
+ /**
+ * Construct a new MediaActionSound instance. Only a single instance is
+ * needed for playing any platform media action sound; you do not need a
+ * separate instance for each sound type.
+ */
+ public MediaActionSound() {
+ mSoundPool = new SoundPool(NUM_MEDIA_SOUND_STREAMS,
+ AudioManager.STREAM_SYSTEM_ENFORCED, 0);
+ mSoundPool.setOnLoadCompleteListener(mLoadCompleteListener);
+ mSoundIds = new int[SOUND_FILES.length];
+ for (int i = 0; i < mSoundIds.length; i++) {
+ mSoundIds[i] = SOUND_NOT_LOADED;
+ }
+ mSoundIdToPlay = SOUND_NOT_LOADED;
+ }
+
+ /**
+ * Preload a predefined platform sound to minimize latency when the sound is
+ * played later by {@link #play}.
+ * @param soundName The type of sound to preload, selected from
+ * SHUTTER_CLICK, FOCUS_COMPLETE, START_VIDEO_RECORDING, or
+ * STOP_VIDEO_RECORDING.
+ * @see #play
+ * @see #SHUTTER_CLICK
+ * @see #FOCUS_COMPLETE
+ * @see #START_VIDEO_RECORDING
+ * @see #STOP_VIDEO_RECORDING
+ */
+ public synchronized void load(int soundName) {
+ if (soundName < 0 || soundName >= SOUND_FILES.length) {
+ throw new RuntimeException("Unknown sound requested: " + soundName);
+ }
+ if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
+ mSoundIds[soundName] =
+ mSoundPool.load(SOUND_FILES[soundName], 1);
+ }
+ }
+
+ /**
+ * <p>Play one of the predefined platform sounds for media actions.</p>
+ *
+ * <p>Use this method to play a platform-specific sound for various media
+ * actions. The sound playback is done asynchronously, with the same
+ * behavior and content as the sounds played by
+ * {@link android.hardware.Camera#takePicture Camera.takePicture},
+ * {@link android.media.MediaRecorder#start MediaRecorder.start}, and
+ * {@link android.media.MediaRecorder#stop MediaRecorder.stop}.</p>
+ *
+ * <p>Using this method makes it easy to match the default device sounds
+ * when recording or capturing data through the preview callbacks, or when
+ * implementing custom camera-like features in your
+ * application.</p>
+ *
+ * <p>If the sound has not been loaded by {@link #load} before calling play,
+ * play will load the sound at the cost of some additional latency before
+ * sound playback begins. </p>
+ *
+ * @param soundName The type of sound to play, selected from
+ * SHUTTER_CLICK, FOCUS_COMPLETE, START_VIDEO_RECORDING, or
+ * STOP_VIDEO_RECORDING.
+ * @see android.hardware.Camera#takePicture
+ * @see android.media.MediaRecorder
+ * @see #SHUTTER_CLICK
+ * @see #FOCUS_COMPLETE
+ * @see #START_VIDEO_RECORDING
+ * @see #STOP_VIDEO_RECORDING
+ */
+ public synchronized void play(int soundName) {
+ if (soundName < 0 || soundName >= SOUND_FILES.length) {
+ throw new RuntimeException("Unknown sound requested: " + soundName);
+ }
+ if (mSoundIds[soundName] == SOUND_NOT_LOADED) {
+ mSoundIdToPlay =
+ mSoundPool.load(SOUND_FILES[soundName], 1);
+ mSoundIds[soundName] = mSoundIdToPlay;
+ } else {
+ mSoundPool.play(mSoundIds[soundName], 1.0f, 1.0f, 0, 0, 1.0f);
+ }
+ }
+
+ private SoundPool.OnLoadCompleteListener mLoadCompleteListener =
+ new SoundPool.OnLoadCompleteListener() {
+ public void onLoadComplete(SoundPool soundPool,
+ int sampleId, int status) {
+ if (status == 0) {
+ if (mSoundIdToPlay == sampleId) {
+ soundPool.play(sampleId, 1.0f, 1.0f, 0, 0, 1.0f);
+ mSoundIdToPlay = SOUND_NOT_LOADED;
+ }
+ } else {
+ Log.e(TAG, "Unable to load sound for playback (status: " +
+ status + ")");
+ }
+ }
+ };
+
+ /**
+ * Free up all audio resources used by this MediaActionSound instance. Do
+ * not call any other methods on a MediaActionSound instance after calling
+ * release().
+ */
+ public void release() {
+ if (mSoundPool != null) {
+ mSoundPool.release();
+ mSoundPool = null;
+ }
+ }
+}
diff --git a/media/java/android/media/MediaCodec.java b/media/java/android/media/MediaCodec.java
new file mode 100644
index 0000000..7f496ca
--- /dev/null
+++ b/media/java/android/media/MediaCodec.java
@@ -0,0 +1,210 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package android.media;
+
+import android.view.Surface;
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * MediaCodec class can be used to access low-level media codec, i.e.
+ * encoder/decoder components.
+ * @hide
+*/
+public class MediaCodec
+{
+ /** Per buffer metadata includes an offset and size specifying
+ the range of valid data in the associated codec buffer.
+ */
+ public final static class BufferInfo {
+ public void set(
+ int offset, int size, long timeUs, int flags) {
+ mOffset = offset;
+ mSize = size;
+ mPresentationTimeUs = timeUs;
+ mFlags = flags;
+ }
+
+ public int mOffset;
+ public int mSize;
+ public long mPresentationTimeUs;
+ public int mFlags;
+ };
+
+ public static int FLAG_SYNCFRAME = 1;
+ public static int FLAG_CODECCONFIG = 2;
+ public static int FLAG_EOS = 4;
+
+ /** Instantiate a codec component by mime type. For decoder components
+ this is the mime type of media that this decoder should be able to
+ decoder, for encoder components it's the type of media this encoder
+ should encode _to_.
+ */
+ public static MediaCodec CreateByType(String type, boolean encoder) {
+ return new MediaCodec(type, true /* nameIsType */, encoder);
+ }
+
+ /** If you know the exact name of the component you want to instantiate
+ use this method to instantiate it. Use with caution.
+ */
+ public static MediaCodec CreateByComponentName(String name) {
+ return new MediaCodec(
+ name, false /* nameIsType */, false /* unused */);
+ }
+
+ private MediaCodec(
+ String name, boolean nameIsType, boolean encoder) {
+ native_setup(name, nameIsType, encoder);
+ }
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ // Make sure you call this when you're done to free up any opened
+ // component instance instead of relying on the garbage collector
+ // to do this for you at some point in the future.
+ public native final void release();
+
+ public static int CONFIGURE_FLAG_ENCODE = 1;
+
+ /** Configures a component.
+ * @param format A map of string/value pairs describing the input format
+ * (decoder) or the desired output format.
+ *
+ * Video formats have the following fields:
+ * "mime" - String
+ * "width" - Integer
+ * "height" - Integer
+ * optional "max-input-size" - Integer
+ * optional "csd-0", "csd-1" ... - ByteBuffer
+ *
+ * Audio formats have the following fields:
+ * "mime" - String
+ * "channel-count" - Integer
+ * "sample-rate" - Integer
+ * optional "max-input-size" - Integer
+ * optional "csd-0", "csd-1" ... - ByteBuffer
+ *
+ * If the format is used to configure an encoder, additional
+ * fields must be included:
+ * "bitrate" - Integer (in bits/sec)
+ *
+ * for video formats:
+ * "color-format" - Integer
+ * "frame-rate" - Integer or Float
+ * "i-frame-interval" - Integer
+ * optional "stride" - Integer, defaults to "width"
+ * optional "slice-height" - Integer, defaults to "height"
+ *
+ * @param surface Specify a surface on which to render the output of this
+ * decoder.
+ * @param flags Specify {@see #CONFIGURE_FLAG_ENCODE} to configure the
+ * component as an encoder.
+ */
+ public void configure(
+ Map<String, Object> format, Surface surface, int flags) {
+ String[] keys = null;
+ Object[] values = null;
+
+ if (format != null) {
+ keys = new String[format.size()];
+ values = new Object[format.size()];
+
+ int i = 0;
+ for (Map.Entry<String, Object> entry: format.entrySet()) {
+ keys[i] = entry.getKey();
+ values[i] = entry.getValue();
+ ++i;
+ }
+ }
+
+ native_configure(keys, values, surface, flags);
+ }
+
+ private native final void native_configure(
+ String[] keys, Object[] values, Surface surface, int flags);
+
+ /** After successfully configuring the component, call start. On return
+ * you can query the component for its input/output buffers.
+ */
+ public native final void start();
+
+ public native final void stop();
+
+ /** Flush both input and output ports of the component, all indices
+ * previously returned in calls to dequeueInputBuffer and
+ * dequeueOutputBuffer become invalid.
+ */
+ public native final void flush();
+
+ /** After filling a range of the input buffer at the specified index
+ * submit it to the component.
+ */
+ public native final void queueInputBuffer(
+ int index,
+ int offset, int size, long presentationTimeUs, int flags);
+
+ // Returns the index of an input buffer to be filled with valid data
+ // or -1 if no such buffer is currently available.
+ // This method will return immediately if timeoutUs == 0, wait indefinitely
+ // for the availability of an input buffer if timeoutUs < 0 or wait up
+ // to "timeoutUs" microseconds if timeoutUs > 0.
+ public native final int dequeueInputBuffer(long timeoutUs);
+
+ // Returns the index of an output buffer that has been successfully
+ // decoded or one of the INFO_* constants below.
+ // The provided "info" will be filled with buffer meta data.
+ public static final int INFO_TRY_AGAIN_LATER = -1;
+ public static final int INFO_OUTPUT_FORMAT_CHANGED = -2;
+ public static final int INFO_OUTPUT_BUFFERS_CHANGED = -3;
+
+ /** Dequeue an output buffer, block at most "timeoutUs" microseconds. */
+ public native final int dequeueOutputBuffer(
+ BufferInfo info, long timeoutUs);
+
+ // If you are done with a buffer, use this call to return the buffer to
+ // the codec. If you previously specified a surface when configuring this
+ // video decoder you can optionally render the buffer.
+ public native final void releaseOutputBuffer(int index, boolean render);
+
+ /** Call this after dequeueOutputBuffer signals a format change by returning
+ * {@see #INFO_OUTPUT_FORMAT_CHANGED}
+ */
+ public native final Map<String, Object> getOutputFormat();
+
+ /** Call this after start() returns and whenever dequeueOutputBuffer
+ * signals an output buffer change by returning
+ * {@see #INFO_OUTPUT_BUFFERS_CHANGED}
+ */
+ public native final ByteBuffer[] getBuffers(boolean input);
+
+ private static native final void native_init();
+
+ private native final void native_setup(
+ String name, boolean nameIsType, boolean encoder);
+
+ private native final void native_finalize();
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaExtractor.java b/media/java/android/media/MediaExtractor.java
new file mode 100644
index 0000000..6a7f2f5
--- /dev/null
+++ b/media/java/android/media/MediaExtractor.java
@@ -0,0 +1,78 @@
+/*
+ * Copyright (C) 2012 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.
+ */
+
+package android.media;
+
+import java.nio.ByteBuffer;
+import java.util.Map;
+
+/**
+ * MediaExtractor
+ * @hide
+*/
+public class MediaExtractor
+{
+ public MediaExtractor(String path) {
+ native_setup(path);
+ }
+
+ @Override
+ protected void finalize() {
+ native_finalize();
+ }
+
+ // Make sure you call this when you're done to free up any resources
+ // instead of relying on the garbage collector to do this for you at
+ // some point in the future.
+ public native final void release();
+
+ public native int countTracks();
+ public native Map<String, Object> getTrackFormat(int index);
+
+ // Subsequent calls to "readSampleData", "getSampleTrackIndex" and
+ // "getSampleTime" only retrieve information for the subset of tracks
+ // selected by the call below.
+ // Selecting the same track multiple times has no effect, the track
+ // is only selected once.
+ public native void selectTrack(int index);
+
+ // All selected tracks seek near the requested time. The next sample
+ // returned for each selected track will be a sync sample.
+ public native void seekTo(long timeUs);
+
+ public native boolean advance();
+
+ // Retrieve the current encoded sample and store it in the byte buffer
+ // starting at the given offset.
+ public native int readSampleData(ByteBuffer byteBuf, int offset);
+
+ // Returns the track index the current sample originates from.
+ public native int getSampleTrackIndex();
+
+ // Returns the current sample's presentation time in microseconds.
+ public native long getSampleTime();
+
+ private static native final void native_init();
+ private native final void native_setup(String path);
+ private native final void native_finalize();
+
+ static {
+ System.loadLibrary("media_jni");
+ native_init();
+ }
+
+ private int mNativeContext;
+}
diff --git a/media/java/android/media/MediaPlayer.java b/media/java/android/media/MediaPlayer.java
index 4c70e9d..e663e91 100644
--- a/media/java/android/media/MediaPlayer.java
+++ b/media/java/android/media/MediaPlayer.java
@@ -35,6 +35,7 @@ import android.media.AudioManager;
import java.io.FileDescriptor;
import java.io.IOException;
+import java.net.InetSocketAddress;
import java.util.Map;
import java.util.Set;
import java.lang.ref.WeakReference;
@@ -1486,6 +1487,52 @@ public class MediaPlayer
*/
public native static int native_pullBatteryData(Parcel reply);
+ /**
+ * Sets the target UDP re-transmit endpoint for the low level player.
+ * Generally, the address portion of the endpoint is an IP multicast
+ * address, although a unicast address would be equally valid. When a valid
+ * retransmit endpoint has been set, the media player will not decode and
+ * render the media presentation locally. Instead, the player will attempt
+ * to re-multiplex its media data using the Android@Home RTP profile and
+ * re-transmit to the target endpoint. Receiver devices (which may be
+ * either the same as the transmitting device or different devices) may
+ * instantiate, prepare, and start a receiver player using a setDataSource
+ * URL of the form...
+ *
+ * aahRX://&lt;multicastIP&gt;:&lt;port&gt;
+ *
+ * to receive, decode and render the re-transmitted content.
+ *
+ * setRetransmitEndpoint may only be called before setDataSource has been
+ * called; while the player is in the Idle state.
+ *
+ * @param endpoint the address and UDP port of the re-transmission target or
+ * null if no re-transmission is to be performed.
+ * @throws IllegalStateException if it is called in an invalid state
+ * @throws IllegalArgumentException if the retransmit endpoint is supplied,
+ * but invalid.
+ *
+ * {@hide} pending API council
+ */
+ public void setRetransmitEndpoint(InetSocketAddress endpoint)
+ throws IllegalStateException, IllegalArgumentException
+ {
+ String addrString = null;
+ int port = 0;
+
+ if (null != endpoint) {
+ addrString = endpoint.getAddress().getHostAddress();
+ port = endpoint.getPort();
+ }
+
+ int ret = native_setRetransmitEndpoint(addrString, port);
+ if (ret != 0) {
+ throw new IllegalArgumentException("Illegal re-transmit endpoint; native ret " + ret);
+ }
+ }
+
+ private native final int native_setRetransmitEndpoint(String addrString, int port);
+
@Override
protected void finalize() { native_finalize(); }
diff --git a/media/java/android/media/MediaRecorder.java b/media/java/android/media/MediaRecorder.java
index 85d99c1..6319630 100644
--- a/media/java/android/media/MediaRecorder.java
+++ b/media/java/android/media/MediaRecorder.java
@@ -303,6 +303,8 @@ public class MediaRecorder
/**
* Uses the settings from a CamcorderProfile object for recording. This method should
* be called after the video AND audio sources are set, and before setOutputFile().
+ * If a time lapse CamcorderProfile is used, audio related source or recording
+ * parameters are ignored.
*
* @param profile the CamcorderProfile to use
* @see android.media.CamcorderProfile
@@ -315,8 +317,8 @@ public class MediaRecorder
setVideoEncoder(profile.videoCodec);
if (profile.quality >= CamcorderProfile.QUALITY_TIME_LAPSE_LOW &&
profile.quality <= CamcorderProfile.QUALITY_TIME_LAPSE_QVGA) {
- // Enable time lapse. Also don't set audio for time lapse.
- setParameter(String.format("time-lapse-enable=1"));
+ // Nothing needs to be done. Call to setCaptureRate() enables
+ // time lapse video recording.
} else {
setAudioEncodingBitRate(profile.audioBitRate);
setAudioChannels(profile.audioChannels);
@@ -327,7 +329,10 @@ public class MediaRecorder
/**
* Set video frame capture rate. This can be used to set a different video frame capture
- * rate than the recorded video's playback rate. Currently this works only for time lapse mode.
+ * rate than the recorded video's playback rate. This method also sets the recording mode
+ * to time lapse. In time lapse video recording, only video is recorded. Audio related
+ * parameters are ignored when a time lapse recording session starts, if an application
+ * sets them.
*
* @param fps Rate at which frames should be captured in frames per second.
* The fps can go as low as desired. However the fastest fps will be limited by the hardware.
@@ -339,6 +344,9 @@ public class MediaRecorder
* possible.
*/
public void setCaptureRate(double fps) {
+ // Make sure that time lapse is enabled when this method is called.
+ setParameter(String.format("time-lapse-enable=1"));
+
double timeBetweenFrameCapture = 1 / fps;
int timeBetweenFrameCaptureMs = (int) (1000 * timeBetweenFrameCapture);
setParameter(String.format("time-between-time-lapse-frame-capture=%d",
diff --git a/media/java/android/media/MediaScanner.java b/media/java/android/media/MediaScanner.java
index 52d31c7..a08d6c3 100644
--- a/media/java/android/media/MediaScanner.java
+++ b/media/java/android/media/MediaScanner.java
@@ -62,6 +62,9 @@ import java.util.Iterator;
import java.util.LinkedHashMap;
import java.util.Locale;
+import libcore.io.ErrnoException;
+import libcore.io.Libcore;
+
/**
* Internal service helper that no-one should use directly.
*
@@ -348,20 +351,18 @@ public class MediaScanner
private final BitmapFactory.Options mBitmapOptions = new BitmapFactory.Options();
- private static class FileCacheEntry {
+ private static class FileEntry {
long mRowId;
String mPath;
long mLastModified;
int mFormat;
- boolean mSeenInFileSystem;
boolean mLastModifiedChanged;
- FileCacheEntry(long rowId, String path, long lastModified, int format) {
+ FileEntry(long rowId, String path, long lastModified, int format) {
mRowId = rowId;
mPath = path;
mLastModified = lastModified;
mFormat = format;
- mSeenInFileSystem = false;
mLastModifiedChanged = false;
}
@@ -373,11 +374,7 @@ public class MediaScanner
private MediaInserter mMediaInserter;
- // hashes file path to FileCacheEntry.
- // path should be lower case if mCaseInsensitivePaths is true
- private LinkedHashMap<String, FileCacheEntry> mFileCache;
-
- private ArrayList<FileCacheEntry> mPlayLists;
+ private ArrayList<FileEntry> mPlayLists;
private DrmManagerClient mDrmManagerClient = null;
@@ -432,7 +429,7 @@ public class MediaScanner
private int mWidth;
private int mHeight;
- public FileCacheEntry beginFile(String path, String mimeType, long lastModified,
+ public FileEntry beginFile(String path, String mimeType, long lastModified,
long fileSize, boolean isDirectory, boolean noMedia) {
mMimeType = mimeType;
mFileType = 0;
@@ -465,11 +462,7 @@ public class MediaScanner
}
}
- String key = path;
- if (mCaseInsensitivePaths) {
- key = path.toLowerCase();
- }
- FileCacheEntry entry = mFileCache.get(key);
+ FileEntry entry = makeEntryFor(path);
// add some slack to avoid a rounding error
long delta = (entry != null) ? (lastModified - entry.mLastModified) : 0;
boolean wasModified = delta > 1 || delta < -1;
@@ -477,13 +470,11 @@ public class MediaScanner
if (wasModified) {
entry.mLastModified = lastModified;
} else {
- entry = new FileCacheEntry(0, path, lastModified,
+ entry = new FileEntry(0, path, lastModified,
(isDirectory ? MtpConstants.FORMAT_ASSOCIATION : 0));
- mFileCache.put(key, entry);
}
entry.mLastModifiedChanged = true;
}
- entry.mSeenInFileSystem = true;
if (mProcessPlaylists && MediaFile.isPlayListFileType(mFileType)) {
mPlayLists.add(entry);
@@ -525,7 +516,7 @@ public class MediaScanner
Uri result = null;
// long t1 = System.currentTimeMillis();
try {
- FileCacheEntry entry = beginFile(path, mimeType, lastModified,
+ FileEntry entry = beginFile(path, mimeType, lastModified,
fileSize, isDirectory, noMedia);
// rescan for metadata if file was modified since last scan
if (entry != null && (entry.mLastModifiedChanged || scanAlways)) {
@@ -778,7 +769,7 @@ public class MediaScanner
return map;
}
- private Uri endFile(FileCacheEntry entry, boolean ringtones, boolean notifications,
+ private Uri endFile(FileEntry entry, boolean ringtones, boolean notifications,
boolean alarms, boolean music, boolean podcasts)
throws RemoteException {
// update database
@@ -1028,55 +1019,94 @@ public class MediaScanner
String where = null;
String[] selectionArgs = null;
- if (mFileCache == null) {
- mFileCache = new LinkedHashMap<String, FileCacheEntry>();
- } else {
- mFileCache.clear();
- }
if (mPlayLists == null) {
- mPlayLists = new ArrayList<FileCacheEntry>();
+ mPlayLists = new ArrayList<FileEntry>();
} else {
mPlayLists.clear();
}
if (filePath != null) {
// query for only one file
- where = Files.FileColumns.DATA + "=?";
- selectionArgs = new String[] { filePath };
+ where = MediaStore.Files.FileColumns._ID + ">?" +
+ " AND " + Files.FileColumns.DATA + "=?";
+ selectionArgs = new String[] { "", filePath };
+ } else {
+ where = MediaStore.Files.FileColumns._ID + ">?";
+ selectionArgs = new String[] { "" };
}
+ // Tell the provider to not delete the file.
+ // If the file is truly gone the delete is unnecessary, and we want to avoid
+ // accidentally deleting files that are really there (this may happen if the
+ // filesystem is mounted and unmounted while the scanner is running).
+ Uri.Builder builder = mFilesUri.buildUpon();
+ builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
+ MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
+
// Build the list of files from the content provider
try {
if (prescanFiles) {
- // First read existing files from the files table
+ // First read existing files from the files table.
+ // Because we'll be deleting entries for missing files as we go,
+ // we need to query the database in small batches, to avoid problems
+ // with CursorWindow positioning.
+ long lastId = Long.MIN_VALUE;
+ Uri limitUri = mFilesUri.buildUpon().appendQueryParameter("limit", "1000").build();
+ mWasEmptyPriorToScan = true;
+
+ while (true) {
+ selectionArgs[0] = "" + lastId;
+ if (c != null) {
+ c.close();
+ c = null;
+ }
+ c = mMediaProvider.query(limitUri, FILES_PRESCAN_PROJECTION,
+ where, selectionArgs, MediaStore.Files.FileColumns._ID, null);
+ if (c == null) {
+ break;
+ }
- c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
- where, selectionArgs, null, null);
+ int num = c.getCount();
- if (c != null) {
- mWasEmptyPriorToScan = c.getCount() == 0;
+ if (num == 0) {
+ break;
+ }
+ mWasEmptyPriorToScan = false;
while (c.moveToNext()) {
long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ lastId = rowId;
// Only consider entries with absolute path names.
// This allows storing URIs in the database without the
// media scanner removing them.
if (path != null && path.startsWith("/")) {
- String key = path;
- if (mCaseInsensitivePaths) {
- key = path.toLowerCase();
+ boolean exists = false;
+ try {
+ exists = Libcore.os.access(path, libcore.io.OsConstants.F_OK);
+ } catch (ErrnoException e1) {
+ }
+ if (!exists && !MtpConstants.isAbstractObject(format)) {
+ // do not delete missing playlists, since they may have been
+ // modified by the user.
+ // The user can delete them in the media player instead.
+ // instead, clear the path and lastModified fields in the row
+ MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
+ int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
+
+ if (!MediaFile.isPlayListFileType(fileType)) {
+ deleter.delete(rowId);
+ if (path.toLowerCase(Locale.US).endsWith("/.nomedia")) {
+ deleter.flush();
+ String parent = new File(path).getParent();
+ mMediaProvider.call(MediaStore.UNHIDE_CALL, parent, null);
+ }
+ }
}
-
- FileCacheEntry entry = new FileCacheEntry(rowId, path,
- lastModified, format);
- mFileCache.put(key, entry);
}
}
- c.close();
- c = null;
}
}
}
@@ -1084,6 +1114,7 @@ public class MediaScanner
if (c != null) {
c.close();
}
+ deleter.flush();
}
// compute original size of images
@@ -1186,57 +1217,6 @@ public class MediaScanner
}
private void postscan(String[] directories) throws RemoteException {
- Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
-
- // Tell the provider to not delete the file.
- // If the file is truly gone the delete is unnecessary, and we want to avoid
- // accidentally deleting files that are really there (this may happen if the
- // filesystem is mounted and unmounted while the scanner is running).
- Uri.Builder builder = mFilesUri.buildUpon();
- builder.appendQueryParameter(MediaStore.PARAM_DELETE_DATA, "false");
- MediaBulkDeleter deleter = new MediaBulkDeleter(mMediaProvider, builder.build());
-
- while (iterator.hasNext()) {
- FileCacheEntry entry = iterator.next();
- String path = entry.mPath;
-
- // remove database entries for files that no longer exist.
- boolean fileMissing = false;
-
- if (!entry.mSeenInFileSystem && !MtpConstants.isAbstractObject(entry.mFormat)) {
- if (inScanDirectory(path, directories)) {
- // we didn't see this file in the scan directory.
- fileMissing = true;
- } else {
- // the file actually a directory or other abstract object
- // or is outside of our scan directory,
- // so we need to check for file existence here.
- File testFile = new File(path);
- if (!testFile.exists()) {
- fileMissing = true;
- }
- }
- }
-
- if (fileMissing) {
- // do not delete missing playlists, since they may have been modified by the user.
- // the user can delete them in the media player instead.
- // instead, clear the path and lastModified fields in the row
- MediaFile.MediaFileType mediaFileType = MediaFile.getFileType(path);
- int fileType = (mediaFileType == null ? 0 : mediaFileType.fileType);
-
- if (!MediaFile.isPlayListFileType(fileType)) {
- deleter.delete(entry.mRowId);
- iterator.remove();
- if (entry.mPath.toLowerCase(Locale.US).endsWith("/.nomedia")) {
- deleter.flush();
- File f = new File(path);
- mMediaProvider.call(MediaStore.UNHIDE_CALL, f.getParent(), null);
- }
- }
- }
- }
- deleter.flush();
// handle playlists last, after we know what media files are on the storage.
if (mProcessPlaylists) {
@@ -1248,7 +1228,6 @@ public class MediaScanner
// allow GC to clean up
mPlayLists = null;
- mFileCache = null;
mMediaProvider = null;
}
@@ -1422,11 +1401,7 @@ public class MediaScanner
// build file cache so we can look up tracks in the playlist
prescan(null, true);
- String key = path;
- if (mCaseInsensitivePaths) {
- key = path.toLowerCase();
- }
- FileCacheEntry entry = mFileCache.get(key);
+ FileEntry entry = makeEntryFor(path);
if (entry != null) {
processPlayList(entry);
}
@@ -1445,6 +1420,37 @@ public class MediaScanner
}
}
+ FileEntry makeEntryFor(String path) {
+ String key = path;
+ String where;
+ String[] selectionArgs;
+ if (mCaseInsensitivePaths) {
+ where = Files.FileColumns.DATA + " LIKE ?";
+ selectionArgs = new String[] { path };
+ } else {
+ where = Files.FileColumns.DATA + "=?";
+ selectionArgs = new String[] { path };
+ }
+
+ Cursor c = null;
+ try {
+ c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ where, selectionArgs, null, null);
+ if (c.moveToNext()) {
+ long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+ int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+ long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+ return new FileEntry(rowId, path, lastModified, format);
+ }
+ } catch (RemoteException e) {
+ } finally {
+ if (c != null) {
+ c.close();
+ }
+ }
+ return null;
+ }
+
// returns the number of matching file/directory names, starting from the right
private int matchPaths(String path1, String path2) {
int result = 0;
@@ -1495,26 +1501,37 @@ public class MediaScanner
//FIXME - should we look for "../" within the path?
// best matching MediaFile for the play list entry
- FileCacheEntry bestMatch = null;
+ FileEntry bestMatch = null;
// number of rightmost file/directory names for bestMatch
int bestMatchLength = 0;
- Iterator<FileCacheEntry> iterator = mFileCache.values().iterator();
- while (iterator.hasNext()) {
- FileCacheEntry cacheEntry = iterator.next();
- String path = cacheEntry.mPath;
+ Cursor c = null;
+ try {
+ c = mMediaProvider.query(mFilesUri, FILES_PRESCAN_PROJECTION,
+ null, null, null, null);
+ } catch (RemoteException e1) {
+ }
- if (path.equalsIgnoreCase(entry)) {
- bestMatch = cacheEntry;
- break; // don't bother continuing search
- }
+ if (c != null) {
+ while (c.moveToNext()) {
+ long rowId = c.getLong(FILES_PRESCAN_ID_COLUMN_INDEX);
+ String path = c.getString(FILES_PRESCAN_PATH_COLUMN_INDEX);
+ int format = c.getInt(FILES_PRESCAN_FORMAT_COLUMN_INDEX);
+ long lastModified = c.getLong(FILES_PRESCAN_DATE_MODIFIED_COLUMN_INDEX);
+
+ if (path.equalsIgnoreCase(entry)) {
+ bestMatch = new FileEntry(rowId, path, lastModified, format);
+ break; // don't bother continuing search
+ }
- int matchLength = matchPaths(path, entry);
- if (matchLength > bestMatchLength) {
- bestMatch = cacheEntry;
- bestMatchLength = matchLength;
+ int matchLength = matchPaths(path, entry);
+ if (matchLength > bestMatchLength) {
+ bestMatch = new FileEntry(rowId, path, lastModified, format);
+ bestMatchLength = matchLength;
+ }
}
+ c.close();
}
if (bestMatch == null) {
@@ -1524,7 +1541,7 @@ public class MediaScanner
try {
// check rowid is set. Rowid may be missing if it is inserted by bulkInsert().
if (bestMatch.mRowId == 0) {
- Cursor c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
+ c = mMediaProvider.query(mAudioUri, ID_PROJECTION,
MediaStore.Files.FileColumns.DATA + "=?",
new String[] { bestMatch.mPath }, null, null);
if (c != null) {
@@ -1677,7 +1694,7 @@ public class MediaScanner
}
}
- private void processPlayList(FileCacheEntry entry) throws RemoteException {
+ private void processPlayList(FileEntry entry) throws RemoteException {
String path = entry.mPath;
ContentValues values = new ContentValues();
int lastSlash = path.lastIndexOf('/');
@@ -1728,9 +1745,9 @@ public class MediaScanner
}
private void processPlayLists() throws RemoteException {
- Iterator<FileCacheEntry> iterator = mPlayLists.iterator();
+ Iterator<FileEntry> iterator = mPlayLists.iterator();
while (iterator.hasNext()) {
- FileCacheEntry entry = iterator.next();
+ FileEntry entry = iterator.next();
// only process playlist files if they are new or have been modified since the last scan
if (entry.mLastModifiedChanged) {
processPlayList(entry);
diff --git a/media/java/android/media/audiofx/Visualizer.java b/media/java/android/media/audiofx/Visualizer.java
index bcf7b89..91d0add 100755
--- a/media/java/android/media/audiofx/Visualizer.java
+++ b/media/java/android/media/audiofx/Visualizer.java
@@ -84,6 +84,7 @@ public class Visualizer {
// to keep in sync with frameworks/base/media/jni/audioeffect/android_media_Visualizer.cpp
private static final int NATIVE_EVENT_PCM_CAPTURE = 0;
private static final int NATIVE_EVENT_FFT_CAPTURE = 1;
+ private static final int NATIVE_EVENT_SERVER_DIED = 2;
// Error codes:
/**
@@ -147,6 +148,10 @@ public class Visualizer {
* PCM and FFT capture listener registered by client
*/
private OnDataCaptureListener mCaptureListener = null;
+ /**
+ * Server Died listener registered by client
+ */
+ private OnServerDiedListener mServerDiedListener = null;
// accessed by native methods
private int mNativeVisualizer;
@@ -396,6 +401,9 @@ public class Visualizer {
public interface OnDataCaptureListener {
/**
* Method called when a new waveform capture is available.
+ * <p>Data in the waveform buffer is valid only within the scope of the callback.
+ * Applications which needs access to the waveform data after returning from the callback
+ * should make a copy of the data instead of holding a reference.
* @param visualizer Visualizer object on which the listener is registered.
* @param waveform array of bytes containing the waveform representation.
* @param samplingRate sampling rate of the audio visualized.
@@ -404,6 +412,9 @@ public class Visualizer {
/**
* Method called when a new frequency capture is available.
+ * <p>Data in the fft buffer is valid only within the scope of the callback.
+ * Applications which needs access to the fft data after returning from the callback
+ * should make a copy of the data instead of holding a reference.
* @param visualizer Visualizer object on which the listener is registered.
* @param fft array of bytes containing the frequency representation.
* @param samplingRate sampling rate of the audio visualized.
@@ -452,6 +463,43 @@ public class Visualizer {
}
/**
+ * @hide
+ *
+ * The OnServerDiedListener interface defines a method called by the Visualizer to indicate that
+ * the connection to the native media server has been broken and that the Visualizer object will
+ * need to be released and re-created.
+ * The client application can implement this interface and register the listener with the
+ * {@link #setServerDiedListener(OnServerDiedListener)} method.
+ */
+ public interface OnServerDiedListener {
+ /**
+ * @hide
+ *
+ * Method called when the native media server has died.
+ * <p>If the native media server encounters a fatal error and needs to restart, the binder
+ * connection from the {@link #Visualizer} to the media server will be broken. Data capture
+ * callbacks will stop happening, and client initiated calls to the {@link #Visualizer}
+ * instance will fail with the error code {@link #DEAD_OBJECT}. To restore functionality,
+ * clients should {@link #release()} their old visualizer and create a new instance.
+ */
+ void onServerDied();
+ }
+
+ /**
+ * @hide
+ *
+ * Registers an OnServerDiedListener interface.
+ * <p>Call this method with a null listener to stop receiving server death notifications.
+ * @return {@link #SUCCESS} in case of success,
+ */
+ public int setServerDiedListener(OnServerDiedListener listener) {
+ synchronized (mListenerLock) {
+ mServerDiedListener = listener;
+ }
+ return SUCCESS;
+ }
+
+ /**
* Helper class to handle the forwarding of native events to the appropriate listeners
*/
private class NativeEventHandler extends Handler
@@ -463,11 +511,7 @@ public class Visualizer {
mVisualizer = v;
}
- @Override
- public void handleMessage(Message msg) {
- if (mVisualizer == null) {
- return;
- }
+ private void handleCaptureMessage(Message msg) {
OnDataCaptureListener l = null;
synchronized (mListenerLock) {
l = mVisualizer.mCaptureListener;
@@ -476,6 +520,7 @@ public class Visualizer {
if (l != null) {
byte[] data = (byte[])msg.obj;
int samplingRate = msg.arg1;
+
switch(msg.what) {
case NATIVE_EVENT_PCM_CAPTURE:
l.onWaveFormDataCapture(mVisualizer, data, samplingRate);
@@ -484,11 +529,41 @@ public class Visualizer {
l.onFftDataCapture(mVisualizer, data, samplingRate);
break;
default:
- Log.e(TAG,"Unknown native event: "+msg.what);
+ Log.e(TAG,"Unknown native event in handleCaptureMessge: "+msg.what);
break;
}
}
}
+
+ private void handleServerDiedMessage(Message msg) {
+ OnServerDiedListener l = null;
+ synchronized (mListenerLock) {
+ l = mVisualizer.mServerDiedListener;
+ }
+
+ if (l != null)
+ l.onServerDied();
+ }
+
+ @Override
+ public void handleMessage(Message msg) {
+ if (mVisualizer == null) {
+ return;
+ }
+
+ switch(msg.what) {
+ case NATIVE_EVENT_PCM_CAPTURE:
+ case NATIVE_EVENT_FFT_CAPTURE:
+ handleCaptureMessage(msg);
+ break;
+ case NATIVE_EVENT_SERVER_DIED:
+ handleServerDiedMessage(msg);
+ break;
+ default:
+ Log.e(TAG,"Unknown native event: "+msg.what);
+ break;
+ }
+ }
}
//---------------------------------------------------------
diff --git a/media/jni/Android.mk b/media/jni/Android.mk
index 23cc0e2..070d2d9 100644
--- a/media/jni/Android.mk
+++ b/media/jni/Android.mk
@@ -2,6 +2,8 @@ LOCAL_PATH:= $(call my-dir)
include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
+ android_media_MediaCodec.cpp \
+ android_media_MediaExtractor.cpp \
android_media_MediaPlayer.cpp \
android_media_MediaRecorder.cpp \
android_media_MediaScanner.cpp \
@@ -25,6 +27,7 @@ LOCAL_SHARED_LIBRARIES := \
libcutils \
libgui \
libstagefright \
+ libstagefright_foundation \
libcamera_client \
libmtp \
libusbhost \
@@ -39,10 +42,12 @@ LOCAL_C_INCLUDES += \
external/tremor/Tremor \
frameworks/base/core/jni \
frameworks/base/media/libmedia \
+ frameworks/base/media/libstagefright \
frameworks/base/media/libstagefright/codecs/amrnb/enc/src \
frameworks/base/media/libstagefright/codecs/amrnb/common \
frameworks/base/media/libstagefright/codecs/amrnb/common/include \
frameworks/base/media/mtp \
+ frameworks/base/include/media/stagefright/openmax \
$(PV_INCLUDES) \
$(JNI_H_INCLUDE) \
$(call include-path-for, corecg graphics)
diff --git a/media/jni/android_media_MediaCodec.cpp b/media/jni/android_media_MediaCodec.cpp
new file mode 100644
index 0000000..71e698f
--- /dev/null
+++ b/media/jni/android_media_MediaCodec.cpp
@@ -0,0 +1,552 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodec-JNI"
+#include <utils/Log.h>
+
+#include "android_media_MediaCodec.h"
+
+#include "android_media_Utils.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "android_runtime/android_view_Surface.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <gui/Surface.h>
+#include <gui/SurfaceTextureClient.h>
+
+#include <media/stagefright/MediaCodec.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaErrors.h>
+
+namespace android {
+
+// Keep these in sync with their equivalents in MediaCodec.java !!!
+enum {
+ DEQUEUE_INFO_TRY_AGAIN_LATER = -1,
+ DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED = -2,
+ DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED = -3,
+};
+
+struct fields_t {
+ jfieldID context;
+};
+
+static fields_t gFields;
+
+////////////////////////////////////////////////////////////////////////////////
+
+JMediaCodec::JMediaCodec(
+ JNIEnv *env, jobject thiz,
+ const char *name, bool nameIsType, bool encoder)
+ : mClass(NULL),
+ mObject(NULL) {
+ jclass clazz = env->GetObjectClass(thiz);
+ CHECK(clazz != NULL);
+
+ mClass = (jclass)env->NewGlobalRef(clazz);
+ mObject = env->NewWeakGlobalRef(thiz);
+
+ mLooper = new ALooper;
+ mLooper->setName("MediaCodec_looper");
+
+ mLooper->start(
+ false, // runOnCallingThread
+ false, // canCallJava
+ PRIORITY_DEFAULT);
+
+ if (nameIsType) {
+ mCodec = MediaCodec::CreateByType(mLooper, name, encoder);
+ } else {
+ mCodec = MediaCodec::CreateByComponentName(mLooper, name);
+ }
+}
+
+status_t JMediaCodec::initCheck() const {
+ return mCodec != NULL ? OK : NO_INIT;
+}
+
+JMediaCodec::~JMediaCodec() {
+ mCodec->release();
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteWeakGlobalRef(mObject);
+ mObject = NULL;
+ env->DeleteGlobalRef(mClass);
+ mClass = NULL;
+}
+
+status_t JMediaCodec::configure(
+ const sp<AMessage> &format,
+ const sp<ISurfaceTexture> &surfaceTexture,
+ int flags) {
+ sp<SurfaceTextureClient> client;
+ if (surfaceTexture != NULL) {
+ client = new SurfaceTextureClient(surfaceTexture);
+ }
+ return mCodec->configure(format, client, flags);
+}
+
+status_t JMediaCodec::start() {
+ return mCodec->start();
+}
+
+status_t JMediaCodec::stop() {
+ return mCodec->stop();
+}
+
+status_t JMediaCodec::flush() {
+ return mCodec->flush();
+}
+
+status_t JMediaCodec::queueInputBuffer(
+ size_t index,
+ size_t offset, size_t size, int64_t timeUs, uint32_t flags) {
+ return mCodec->queueInputBuffer(index, offset, size, timeUs, flags);
+}
+
+status_t JMediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+ return mCodec->dequeueInputBuffer(index, timeoutUs);
+}
+
+status_t JMediaCodec::dequeueOutputBuffer(
+ JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs) {
+ size_t size, offset;
+ int64_t timeUs;
+ uint32_t flags;
+ status_t err;
+ if ((err = mCodec->dequeueOutputBuffer(
+ index, &size, &offset, &timeUs, &flags, timeoutUs)) != OK) {
+ return err;
+ }
+
+ jclass clazz = env->FindClass("android/media/MediaCodec$BufferInfo");
+
+ jmethodID method = env->GetMethodID(clazz, "set", "(IIJI)V");
+ env->CallVoidMethod(bufferInfo, method, offset, size, timeUs, flags);
+
+ return OK;
+}
+
+status_t JMediaCodec::releaseOutputBuffer(size_t index, bool render) {
+ return render
+ ? mCodec->renderOutputBufferAndRelease(index)
+ : mCodec->releaseOutputBuffer(index);
+}
+
+status_t JMediaCodec::getOutputFormat(JNIEnv *env, jobject *format) const {
+ sp<AMessage> msg;
+ status_t err;
+ if ((err = mCodec->getOutputFormat(&msg)) != OK) {
+ return err;
+ }
+
+ return ConvertMessageToMap(env, msg, format);
+}
+
+status_t JMediaCodec::getBuffers(
+ JNIEnv *env, bool input, jobjectArray *bufArray) const {
+ Vector<sp<ABuffer> > buffers;
+
+ status_t err =
+ input
+ ? mCodec->getInputBuffers(&buffers)
+ : mCodec->getOutputBuffers(&buffers);
+
+ if (err != OK) {
+ return err;
+ }
+
+ jclass byteBufferClass = env->FindClass("java/nio/ByteBuffer");
+
+ *bufArray = (jobjectArray)env->NewObjectArray(
+ buffers.size(), byteBufferClass, NULL);
+
+ for (size_t i = 0; i < buffers.size(); ++i) {
+ const sp<ABuffer> &buffer = buffers.itemAt(i);
+
+ jobject byteBuffer =
+ env->NewDirectByteBuffer(
+ buffer->base(),
+ buffer->capacity());
+
+ env->SetObjectArrayElement(
+ *bufArray, i, byteBuffer);
+
+ env->DeleteLocalRef(byteBuffer);
+ byteBuffer = NULL;
+ }
+
+ return OK;
+}
+
+} // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static sp<JMediaCodec> setMediaCodec(
+ JNIEnv *env, jobject thiz, const sp<JMediaCodec> &codec) {
+ sp<JMediaCodec> old = (JMediaCodec *)env->GetIntField(thiz, gFields.context);
+ if (codec != NULL) {
+ codec->incStrong(thiz);
+ }
+ if (old != NULL) {
+ old->decStrong(thiz);
+ }
+ env->SetIntField(thiz, gFields.context, (int)codec.get());
+
+ return old;
+}
+
+static sp<JMediaCodec> getMediaCodec(JNIEnv *env, jobject thiz) {
+ return (JMediaCodec *)env->GetIntField(thiz, gFields.context);
+}
+
+static void android_media_MediaCodec_release(JNIEnv *env, jobject thiz) {
+ setMediaCodec(env, thiz, NULL);
+}
+
+static jint throwExceptionAsNecessary(JNIEnv *env, status_t err) {
+ switch (err) {
+ case OK:
+ return 0;
+
+ case -EAGAIN:
+ return DEQUEUE_INFO_TRY_AGAIN_LATER;
+
+ case INFO_FORMAT_CHANGED:
+ return DEQUEUE_INFO_OUTPUT_FORMAT_CHANGED;
+
+ case INFO_OUTPUT_BUFFERS_CHANGED:
+ return DEQUEUE_INFO_OUTPUT_BUFFERS_CHANGED;
+
+ default:
+ {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static void android_media_MediaCodec_native_configure(
+ JNIEnv *env,
+ jobject thiz,
+ jobjectArray keys, jobjectArray values,
+ jobject jsurface,
+ jint flags) {
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ sp<AMessage> format;
+ status_t err = ConvertKeyValueArraysToMessage(env, keys, values, &format);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ sp<ISurfaceTexture> surfaceTexture;
+ if (jsurface != NULL) {
+ sp<Surface> surface(Surface_getSurface(env, jsurface));
+ if (surface != NULL) {
+ surfaceTexture = surface->getSurfaceTexture();
+ } else {
+ jniThrowException(
+ env,
+ "java/lang/IllegalArgumentException",
+ "The surface has been released");
+ return;
+ }
+ }
+
+ err = codec->configure(format, surfaceTexture, flags);
+
+ throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_start(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_MediaCodec_start");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = codec->start();
+
+ throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_stop(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_MediaCodec_stop");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = codec->stop();
+
+ throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_flush(JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_MediaCodec_flush");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = codec->flush();
+
+ throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_queueInputBuffer(
+ JNIEnv *env,
+ jobject thiz,
+ jint index,
+ jint offset,
+ jint size,
+ jlong timestampUs,
+ jint flags) {
+ ALOGV("android_media_MediaCodec_queueInputBuffer");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = codec->queueInputBuffer(
+ index, offset, size, timestampUs, flags);
+
+ throwExceptionAsNecessary(env, err);
+}
+
+static jint android_media_MediaCodec_dequeueInputBuffer(
+ JNIEnv *env, jobject thiz, jlong timeoutUs) {
+ ALOGV("android_media_MediaCodec_dequeueInputBuffer");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1;
+ }
+
+ size_t index;
+ status_t err = codec->dequeueInputBuffer(&index, timeoutUs);
+
+ if (err == OK) {
+ return index;
+ }
+
+ return throwExceptionAsNecessary(env, err);
+}
+
+static jint android_media_MediaCodec_dequeueOutputBuffer(
+ JNIEnv *env, jobject thiz, jobject bufferInfo, jlong timeoutUs) {
+ ALOGV("android_media_MediaCodec_dequeueOutputBuffer");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ size_t index;
+ status_t err = codec->dequeueOutputBuffer(
+ env, bufferInfo, &index, timeoutUs);
+
+ if (err == OK) {
+ return index;
+ }
+
+ return throwExceptionAsNecessary(env, err);
+}
+
+static void android_media_MediaCodec_releaseOutputBuffer(
+ JNIEnv *env, jobject thiz, jint index, jboolean render) {
+ ALOGV("android_media_MediaCodec_renderOutputBufferAndRelease");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = codec->releaseOutputBuffer(index, render);
+
+ throwExceptionAsNecessary(env, err);
+}
+
+static jobject android_media_MediaCodec_getOutputFormat(
+ JNIEnv *env, jobject thiz) {
+ ALOGV("android_media_MediaCodec_getOutputFormat");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ jobject format;
+ status_t err = codec->getOutputFormat(env, &format);
+
+ if (err == OK) {
+ return format;
+ }
+
+ throwExceptionAsNecessary(env, err);
+
+ return NULL;
+}
+
+static jobjectArray android_media_MediaCodec_getBuffers(
+ JNIEnv *env, jobject thiz, jboolean input) {
+ ALOGV("android_media_MediaCodec_getBuffers");
+
+ sp<JMediaCodec> codec = getMediaCodec(env, thiz);
+
+ if (codec == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ jobjectArray buffers;
+ status_t err = codec->getBuffers(env, input, &buffers);
+
+ if (err == OK) {
+ return buffers;
+ }
+
+ throwExceptionAsNecessary(env, err);
+
+ return NULL;
+}
+
+static void android_media_MediaCodec_native_init(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/media/MediaCodec");
+ CHECK(clazz != NULL);
+
+ gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ CHECK(gFields.context != NULL);
+}
+
+static void android_media_MediaCodec_native_setup(
+ JNIEnv *env, jobject thiz,
+ jstring name, jboolean nameIsType, jboolean encoder) {
+ if (name == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ const char *tmp = env->GetStringUTFChars(name, NULL);
+
+ if (tmp == NULL) {
+ return;
+ }
+
+ sp<JMediaCodec> codec = new JMediaCodec(env, thiz, tmp, nameIsType, encoder);
+
+ status_t err = codec->initCheck();
+
+ env->ReleaseStringUTFChars(name, tmp);
+ tmp = NULL;
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to allocate component instance");
+ return;
+ }
+
+ setMediaCodec(env,thiz, codec);
+}
+
+static void android_media_MediaCodec_native_finalize(
+ JNIEnv *env, jobject thiz) {
+ android_media_MediaCodec_release(env, thiz);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "release", "()V", (void *)android_media_MediaCodec_release },
+
+ { "native_configure",
+ "([Ljava/lang/String;[Ljava/lang/Object;Landroid/view/Surface;I)V",
+ (void *)android_media_MediaCodec_native_configure },
+
+ { "start", "()V", (void *)android_media_MediaCodec_start },
+ { "stop", "()V", (void *)android_media_MediaCodec_stop },
+ { "flush", "()V", (void *)android_media_MediaCodec_flush },
+
+ { "queueInputBuffer", "(IIIJI)V",
+ (void *)android_media_MediaCodec_queueInputBuffer },
+
+ { "dequeueInputBuffer", "(J)I",
+ (void *)android_media_MediaCodec_dequeueInputBuffer },
+
+ { "dequeueOutputBuffer", "(Landroid/media/MediaCodec$BufferInfo;J)I",
+ (void *)android_media_MediaCodec_dequeueOutputBuffer },
+
+ { "releaseOutputBuffer", "(IZ)V",
+ (void *)android_media_MediaCodec_releaseOutputBuffer },
+
+ { "getOutputFormat", "()Ljava/util/Map;",
+ (void *)android_media_MediaCodec_getOutputFormat },
+
+ { "getBuffers", "(Z)[Ljava/nio/ByteBuffer;",
+ (void *)android_media_MediaCodec_getBuffers },
+
+ { "native_init", "()V", (void *)android_media_MediaCodec_native_init },
+
+ { "native_setup", "(Ljava/lang/String;ZZ)V",
+ (void *)android_media_MediaCodec_native_setup },
+
+ { "native_finalize", "()V",
+ (void *)android_media_MediaCodec_native_finalize },
+};
+
+int register_android_media_MediaCodec(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaCodec", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MediaCodec.h b/media/jni/android_media_MediaCodec.h
new file mode 100644
index 0000000..6b1257d
--- /dev/null
+++ b/media/jni/android_media_MediaCodec.h
@@ -0,0 +1,81 @@
+/*
+ * Copyright 2012, 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_MEDIA_MEDIACODEC_H_
+#define _ANDROID_MEDIA_MEDIACODEC_H_
+
+#include "jni.h"
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+namespace android {
+
+struct ALooper;
+struct AMessage;
+struct ISurfaceTexture;
+struct MediaCodec;
+
+struct JMediaCodec : public RefBase {
+ JMediaCodec(
+ JNIEnv *env, jobject thiz,
+ const char *name, bool nameIsType, bool encoder);
+
+ status_t initCheck() const;
+
+ status_t configure(
+ const sp<AMessage> &format,
+ const sp<ISurfaceTexture> &surfaceTexture,
+ int flags);
+
+ status_t start();
+ status_t stop();
+
+ status_t flush();
+
+ status_t queueInputBuffer(
+ size_t index,
+ size_t offset, size_t size, int64_t timeUs, uint32_t flags);
+
+ status_t dequeueInputBuffer(size_t *index, int64_t timeoutUs);
+
+ status_t dequeueOutputBuffer(
+ JNIEnv *env, jobject bufferInfo, size_t *index, int64_t timeoutUs);
+
+ status_t releaseOutputBuffer(size_t index, bool render);
+
+ status_t getOutputFormat(JNIEnv *env, jobject *format) const;
+
+ status_t getBuffers(
+ JNIEnv *env, bool input, jobjectArray *bufArray) const;
+
+protected:
+ virtual ~JMediaCodec();
+
+private:
+ jclass mClass;
+ jweak mObject;
+
+ sp<ALooper> mLooper;
+ sp<MediaCodec> mCodec;
+
+ DISALLOW_EVIL_CONSTRUCTORS(JMediaCodec);
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_MEDIACODEC_H_
diff --git a/media/jni/android_media_MediaExtractor.cpp b/media/jni/android_media_MediaExtractor.cpp
new file mode 100644
index 0000000..4757adf
--- /dev/null
+++ b/media/jni/android_media_MediaExtractor.cpp
@@ -0,0 +1,400 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaExtractor-JNI"
+#include <utils/Log.h>
+
+#include "android_media_MediaExtractor.h"
+
+#include "android_media_Utils.h"
+#include "android_runtime/AndroidRuntime.h"
+#include "jni.h"
+#include "JNIHelp.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/NuMediaExtractor.h>
+
+namespace android {
+
+struct fields_t {
+ jfieldID context;
+};
+
+static fields_t gFields;
+
+////////////////////////////////////////////////////////////////////////////////
+
+JMediaExtractor::JMediaExtractor(JNIEnv *env, jobject thiz)
+ : mClass(NULL),
+ mObject(NULL) {
+ jclass clazz = env->GetObjectClass(thiz);
+ CHECK(clazz != NULL);
+
+ mClass = (jclass)env->NewGlobalRef(clazz);
+ mObject = env->NewWeakGlobalRef(thiz);
+
+ mImpl = new NuMediaExtractor;
+}
+
+JMediaExtractor::~JMediaExtractor() {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->DeleteWeakGlobalRef(mObject);
+ mObject = NULL;
+ env->DeleteGlobalRef(mClass);
+ mClass = NULL;
+}
+
+status_t JMediaExtractor::setDataSource(const char *path) {
+ return mImpl->setDataSource(path);
+}
+
+size_t JMediaExtractor::countTracks() const {
+ return mImpl->countTracks();
+}
+
+status_t JMediaExtractor::getTrackFormat(size_t index, jobject *format) const {
+ sp<AMessage> msg;
+ status_t err;
+ if ((err = mImpl->getTrackFormat(index, &msg)) != OK) {
+ return err;
+ }
+
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ return ConvertMessageToMap(env, msg, format);
+}
+
+status_t JMediaExtractor::selectTrack(size_t index) {
+ return mImpl->selectTrack(index);
+}
+
+status_t JMediaExtractor::seekTo(int64_t timeUs) {
+ return mImpl->seekTo(timeUs);
+}
+
+status_t JMediaExtractor::advance() {
+ return mImpl->advance();
+}
+
+status_t JMediaExtractor::readSampleData(
+ jobject byteBuf, size_t offset, size_t *sampleSize) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ void *dst = env->GetDirectBufferAddress(byteBuf);
+
+ if (dst == NULL) {
+ // XXX if dst is NULL also fall back to "array()"
+ return INVALID_OPERATION;
+ }
+
+ jlong dstSize = env->GetDirectBufferCapacity(byteBuf);
+
+ if (dstSize < offset) {
+ return -ERANGE;
+ }
+
+ sp<ABuffer> buffer = new ABuffer((char *)dst + offset, dstSize - offset);
+
+ status_t err = mImpl->readSampleData(buffer);
+
+ if (err != OK) {
+ return err;
+ }
+
+ *sampleSize = buffer->size();
+
+ return OK;
+}
+
+status_t JMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
+ return mImpl->getSampleTrackIndex(trackIndex);
+}
+
+status_t JMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
+ return mImpl->getSampleTime(sampleTimeUs);
+}
+
+} // namespace android
+
+////////////////////////////////////////////////////////////////////////////////
+
+using namespace android;
+
+static sp<JMediaExtractor> setMediaExtractor(
+ JNIEnv *env, jobject thiz, const sp<JMediaExtractor> &extractor) {
+ sp<JMediaExtractor> old =
+ (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
+
+ if (extractor != NULL) {
+ extractor->incStrong(thiz);
+ }
+ if (old != NULL) {
+ old->decStrong(thiz);
+ }
+ env->SetIntField(thiz, gFields.context, (int)extractor.get());
+
+ return old;
+}
+
+static sp<JMediaExtractor> getMediaExtractor(JNIEnv *env, jobject thiz) {
+ return (JMediaExtractor *)env->GetIntField(thiz, gFields.context);
+}
+
+static void android_media_MediaExtractor_release(JNIEnv *env, jobject thiz) {
+ setMediaExtractor(env, thiz, NULL);
+}
+
+static jint android_media_MediaExtractor_countTracks(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ return extractor->countTracks();
+}
+
+static jobject android_media_MediaExtractor_getTrackFormat(
+ JNIEnv *env, jobject thiz, jint index) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return NULL;
+ }
+
+ jobject format;
+ status_t err = extractor->getTrackFormat(index, &format);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return NULL;
+ }
+
+ return format;
+}
+
+static void android_media_MediaExtractor_selectTrack(
+ JNIEnv *env, jobject thiz, jint index) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = extractor->selectTrack(index);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+}
+
+static void android_media_MediaExtractor_seekTo(
+ JNIEnv *env, jobject thiz, jlong timeUs) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return;
+ }
+
+ status_t err = extractor->seekTo(timeUs);
+
+ if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+}
+
+static jboolean android_media_MediaExtractor_advance(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return false;
+ }
+
+ status_t err = extractor->advance();
+
+ if (err == ERROR_END_OF_STREAM) {
+ return false;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ return true;
+}
+
+static jint android_media_MediaExtractor_readSampleData(
+ JNIEnv *env, jobject thiz, jobject byteBuf, jint offset) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1;
+ }
+
+ size_t sampleSize;
+ status_t err = extractor->readSampleData(byteBuf, offset, &sampleSize);
+
+ if (err == ERROR_END_OF_STREAM) {
+ return -1;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ return sampleSize;
+}
+
+static jint android_media_MediaExtractor_getSampleTrackIndex(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1;
+ }
+
+ size_t trackIndex;
+ status_t err = extractor->getSampleTrackIndex(&trackIndex);
+
+ if (err == ERROR_END_OF_STREAM) {
+ return -1;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ return trackIndex;
+}
+
+static jlong android_media_MediaExtractor_getSampleTime(
+ JNIEnv *env, jobject thiz) {
+ sp<JMediaExtractor> extractor = getMediaExtractor(env, thiz);
+
+ if (extractor == NULL) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return -1ll;
+ }
+
+ int64_t sampleTimeUs;
+ status_t err = extractor->getSampleTime(&sampleTimeUs);
+
+ if (err == ERROR_END_OF_STREAM) {
+ return -1ll;
+ } else if (err != OK) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return false;
+ }
+
+ return sampleTimeUs;
+}
+
+static void android_media_MediaExtractor_native_init(JNIEnv *env) {
+ jclass clazz = env->FindClass("android/media/MediaExtractor");
+ CHECK(clazz != NULL);
+
+ gFields.context = env->GetFieldID(clazz, "mNativeContext", "I");
+ CHECK(gFields.context != NULL);
+
+ DataSource::RegisterDefaultSniffers();
+}
+
+static void android_media_MediaExtractor_native_setup(
+ JNIEnv *env, jobject thiz, jstring path) {
+ sp<JMediaExtractor> extractor = new JMediaExtractor(env, thiz);
+
+ if (path == NULL) {
+ jniThrowException(env, "java/lang/IllegalArgumentException", NULL);
+ return;
+ }
+
+ const char *tmp = env->GetStringUTFChars(path, NULL);
+
+ if (tmp == NULL) {
+ return;
+ }
+
+ status_t err = extractor->setDataSource(tmp);
+
+ env->ReleaseStringUTFChars(path, tmp);
+ tmp = NULL;
+
+ if (err != OK) {
+ jniThrowException(
+ env,
+ "java/io/IOException",
+ "Failed to instantiate extractor.");
+ return;
+ }
+
+ setMediaExtractor(env,thiz, extractor);
+}
+
+static void android_media_MediaExtractor_native_finalize(
+ JNIEnv *env, jobject thiz) {
+ android_media_MediaExtractor_release(env, thiz);
+}
+
+static JNINativeMethod gMethods[] = {
+ { "release", "()V", (void *)android_media_MediaExtractor_release },
+
+ { "countTracks", "()I", (void *)android_media_MediaExtractor_countTracks },
+
+ { "getTrackFormat", "(I)Ljava/util/Map;",
+ (void *)android_media_MediaExtractor_getTrackFormat },
+
+ { "selectTrack", "(I)V", (void *)android_media_MediaExtractor_selectTrack },
+
+ { "seekTo", "(J)V", (void *)android_media_MediaExtractor_seekTo },
+
+ { "advance", "()Z", (void *)android_media_MediaExtractor_advance },
+
+ { "readSampleData", "(Ljava/nio/ByteBuffer;I)I",
+ (void *)android_media_MediaExtractor_readSampleData },
+
+ { "getSampleTrackIndex", "()I",
+ (void *)android_media_MediaExtractor_getSampleTrackIndex },
+
+ { "getSampleTime", "()J",
+ (void *)android_media_MediaExtractor_getSampleTime },
+
+ { "native_init", "()V", (void *)android_media_MediaExtractor_native_init },
+
+ { "native_setup", "(Ljava/lang/String;)V",
+ (void *)android_media_MediaExtractor_native_setup },
+
+ { "native_finalize", "()V",
+ (void *)android_media_MediaExtractor_native_finalize },
+};
+
+int register_android_media_MediaExtractor(JNIEnv *env) {
+ return AndroidRuntime::registerNativeMethods(env,
+ "android/media/MediaExtractor", gMethods, NELEM(gMethods));
+}
diff --git a/media/jni/android_media_MediaExtractor.h b/media/jni/android_media_MediaExtractor.h
new file mode 100644
index 0000000..70e58c6
--- /dev/null
+++ b/media/jni/android_media_MediaExtractor.h
@@ -0,0 +1,60 @@
+/*
+ * Copyright 2012, 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_MEDIA_MEDIAEXTRACTOR_H_
+#define _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/Errors.h>
+#include <utils/RefBase.h>
+
+#include "jni.h"
+
+namespace android {
+
+struct NuMediaExtractor;
+
+struct JMediaExtractor : public RefBase {
+ JMediaExtractor(JNIEnv *env, jobject thiz);
+
+ status_t setDataSource(const char *path);
+
+ size_t countTracks() const;
+ status_t getTrackFormat(size_t index, jobject *format) const;
+
+ status_t selectTrack(size_t index);
+
+ status_t seekTo(int64_t timeUs);
+
+ status_t advance();
+ status_t readSampleData(jobject byteBuf, size_t offset, size_t *sampleSize);
+ status_t getSampleTrackIndex(size_t *trackIndex);
+ status_t getSampleTime(int64_t *sampleTimeUs);
+
+protected:
+ virtual ~JMediaExtractor();
+
+private:
+ jclass mClass;
+ jweak mObject;
+ sp<NuMediaExtractor> mImpl;
+
+ DISALLOW_EVIL_CONSTRUCTORS(JMediaExtractor);
+};
+
+} // namespace android
+
+#endif // _ANDROID_MEDIA_MEDIAEXTRACTOR_H_
diff --git a/media/jni/android_media_MediaPlayer.cpp b/media/jni/android_media_MediaPlayer.cpp
index 8ff9dd3..6ec5d20 100644
--- a/media/jni/android_media_MediaPlayer.cpp
+++ b/media/jni/android_media_MediaPlayer.cpp
@@ -39,7 +39,7 @@
#include "android_util_Binder.h"
#include <binder/Parcel.h>
#include <gui/ISurfaceTexture.h>
-#include <surfaceflinger/Surface.h>
+#include <gui/Surface.h>
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
@@ -722,6 +722,45 @@ android_media_MediaPlayer_pullBatteryData(JNIEnv *env, jobject thiz, jobject jav
return service->pullBatteryData(reply);
}
+static jint
+android_media_MediaPlayer_setRetransmitEndpoint(JNIEnv *env, jobject thiz,
+ jstring addrString, jint port) {
+ sp<MediaPlayer> mp = getMediaPlayer(env, thiz);
+ if (mp == NULL ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ return INVALID_OPERATION;
+ }
+
+ const char *cAddrString = NULL;
+
+ if (NULL != addrString) {
+ cAddrString = env->GetStringUTFChars(addrString, NULL);
+ if (cAddrString == NULL) { // Out of memory
+ return NO_MEMORY;
+ }
+ }
+ ALOGV("setRetransmitEndpoint: %s:%d",
+ cAddrString ? cAddrString : "(null)", port);
+
+ status_t ret;
+ if (cAddrString && (port > 0xFFFF)) {
+ ret = BAD_VALUE;
+ } else {
+ ret = mp->setRetransmitEndpoint(cAddrString,
+ static_cast<uint16_t>(port));
+ }
+
+ if (NULL != addrString) {
+ env->ReleaseStringUTFChars(addrString, cAddrString);
+ }
+
+ if (ret == INVALID_OPERATION ) {
+ jniThrowException(env, "java/lang/IllegalStateException", NULL);
+ }
+
+ return ret;
+}
+
static jboolean
android_media_MediaPlayer_setParameter(JNIEnv *env, jobject thiz, jint key, jobject java_request)
{
@@ -799,6 +838,7 @@ static JNINativeMethod gMethods[] = {
{"native_pullBatteryData", "(Landroid/os/Parcel;)I", (void *)android_media_MediaPlayer_pullBatteryData},
{"setParameter", "(ILandroid/os/Parcel;)Z", (void *)android_media_MediaPlayer_setParameter},
{"getParameter", "(ILandroid/os/Parcel;)V", (void *)android_media_MediaPlayer_getParameter},
+ {"native_setRetransmitEndpoint", "(Ljava/lang/String;I)I", (void *)android_media_MediaPlayer_setRetransmitEndpoint},
};
static const char* const kClassPathName = "android/media/MediaPlayer";
@@ -810,6 +850,8 @@ static int register_android_media_MediaPlayer(JNIEnv *env)
"android/media/MediaPlayer", gMethods, NELEM(gMethods));
}
+extern int register_android_media_MediaCodec(JNIEnv *env);
+extern int register_android_media_MediaExtractor(JNIEnv *env);
extern int register_android_media_MediaMetadataRetriever(JNIEnv *env);
extern int register_android_media_MediaRecorder(JNIEnv *env);
extern int register_android_media_MediaScanner(JNIEnv *env);
@@ -881,6 +923,16 @@ jint JNI_OnLoad(JavaVM* vm, void* reserved)
goto bail;
}
+ if (register_android_media_MediaCodec(env) < 0) {
+ ALOGE("ERROR: MediaCodec native registration failed");
+ goto bail;
+ }
+
+ if (register_android_media_MediaExtractor(env) < 0) {
+ ALOGE("ERROR: MediaCodec native registration failed");
+ goto bail;
+ }
+
/* success -- return valid version number */
result = JNI_VERSION_1_4;
diff --git a/media/jni/android_media_MediaRecorder.cpp b/media/jni/android_media_MediaRecorder.cpp
index acc65f1..b6e6ceb 100644
--- a/media/jni/android_media_MediaRecorder.cpp
+++ b/media/jni/android_media_MediaRecorder.cpp
@@ -18,7 +18,7 @@
#define LOG_TAG "MediaRecorderJNI"
#include <utils/Log.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/Surface.h>
#include <camera/ICameraService.h>
#include <camera/Camera.h>
#include <media/mediarecorder.h>
diff --git a/media/jni/android_media_Utils.cpp b/media/jni/android_media_Utils.cpp
index 47963b1..8b2321c 100644
--- a/media/jni/android_media_Utils.cpp
+++ b/media/jni/android_media_Utils.cpp
@@ -20,6 +20,10 @@
#include <utils/Log.h>
#include "android_media_Utils.h"
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/AMessage.h>
+
namespace android {
bool ConvertKeyValueArraysToKeyedVector(
@@ -71,5 +75,263 @@ bool ConvertKeyValueArraysToKeyedVector(
return true;
}
+static jobject makeIntegerObject(JNIEnv *env, int32_t value) {
+ jclass clazz = env->FindClass("java/lang/Integer");
+ CHECK(clazz != NULL);
+
+ jmethodID integerConstructID = env->GetMethodID(clazz, "<init>", "(I)V");
+ CHECK(integerConstructID != NULL);
+
+ return env->NewObject(clazz, integerConstructID, value);
+}
+
+static jobject makeFloatObject(JNIEnv *env, float value) {
+ jclass clazz = env->FindClass("java/lang/Float");
+ CHECK(clazz != NULL);
+
+ jmethodID floatConstructID = env->GetMethodID(clazz, "<init>", "(F)V");
+ CHECK(floatConstructID != NULL);
+
+ return env->NewObject(clazz, floatConstructID, value);
+}
+
+static jobject makeByteBufferObject(
+ JNIEnv *env, const void *data, size_t size) {
+ jbyteArray byteArrayObj = env->NewByteArray(size);
+ env->SetByteArrayRegion(byteArrayObj, 0, size, (const jbyte *)data);
+
+ jclass clazz = env->FindClass("java/nio/ByteBuffer");
+ CHECK(clazz != NULL);
+
+ jmethodID byteBufWrapID =
+ env->GetStaticMethodID(clazz, "wrap", "([B)Ljava/nio/ByteBuffer;");
+ CHECK(byteBufWrapID != NULL);
+
+ jobject byteBufObj = env->CallStaticObjectMethod(
+ clazz, byteBufWrapID, byteArrayObj);
+
+ env->DeleteLocalRef(byteArrayObj); byteArrayObj = NULL;
+
+ return byteBufObj;
+}
+
+status_t ConvertMessageToMap(
+ JNIEnv *env, const sp<AMessage> &msg, jobject *map) {
+ jclass hashMapClazz = env->FindClass("java/util/HashMap");
+
+ if (hashMapClazz == NULL) {
+ return -EINVAL;
+ }
+
+ jmethodID hashMapConstructID =
+ env->GetMethodID(hashMapClazz, "<init>", "()V");
+
+ if (hashMapConstructID == NULL) {
+ return -EINVAL;
+ }
+
+ jmethodID hashMapPutID =
+ env->GetMethodID(
+ hashMapClazz,
+ "put",
+ "(Ljava/lang/Object;Ljava/lang/Object;)Ljava/lang/Object;");
+
+ if (hashMapPutID == NULL) {
+ return -EINVAL;
+ }
+
+ jobject hashMap = env->NewObject(hashMapClazz, hashMapConstructID);
+
+ for (size_t i = 0; i < msg->countEntries(); ++i) {
+ AMessage::Type valueType;
+ const char *key = msg->getEntryNameAt(i, &valueType);
+
+ jobject valueObj = NULL;
+
+ switch (valueType) {
+ case AMessage::kTypeInt32:
+ {
+ int32_t val;
+ CHECK(msg->findInt32(key, &val));
+
+ valueObj = makeIntegerObject(env, val);
+ break;
+ }
+
+ case AMessage::kTypeFloat:
+ {
+ float val;
+ CHECK(msg->findFloat(key, &val));
+
+ valueObj = makeFloatObject(env, val);
+ break;
+ }
+
+ case AMessage::kTypeString:
+ {
+ AString val;
+ CHECK(msg->findString(key, &val));
+
+ valueObj = env->NewStringUTF(val.c_str());
+ break;
+ }
+
+ case AMessage::kTypeBuffer:
+ {
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer(key, &buffer));
+
+ valueObj = makeByteBufferObject(
+ env, buffer->data(), buffer->size());
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ if (valueObj != NULL) {
+ jstring keyObj = env->NewStringUTF(key);
+
+ jobject res = env->CallObjectMethod(
+ hashMap, hashMapPutID, keyObj, valueObj);
+
+ env->DeleteLocalRef(keyObj); keyObj = NULL;
+ env->DeleteLocalRef(valueObj); valueObj = NULL;
+ }
+ }
+
+ *map = hashMap;
+
+ return OK;
+}
+
+status_t ConvertKeyValueArraysToMessage(
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ sp<AMessage> *out) {
+ jclass stringClass = env->FindClass("java/lang/String");
+ CHECK(stringClass != NULL);
+
+ jclass integerClass = env->FindClass("java/lang/Integer");
+ CHECK(integerClass != NULL);
+
+ jclass floatClass = env->FindClass("java/lang/Float");
+ CHECK(floatClass != NULL);
+
+ jclass byteBufClass = env->FindClass("java/nio/ByteBuffer");
+ CHECK(byteBufClass != NULL);
+
+ sp<AMessage> msg = new AMessage;
+
+ jsize numEntries = 0;
+
+ if (keys != NULL) {
+ if (values == NULL) {
+ return -EINVAL;
+ }
+
+ numEntries = env->GetArrayLength(keys);
+
+ if (numEntries != env->GetArrayLength(values)) {
+ return -EINVAL;
+ }
+ } else if (values != NULL) {
+ return -EINVAL;
+ }
+
+ for (jsize i = 0; i < numEntries; ++i) {
+ jobject keyObj = env->GetObjectArrayElement(keys, i);
+
+ if (!env->IsInstanceOf(keyObj, stringClass)) {
+ return -EINVAL;
+ }
+
+ const char *tmp = env->GetStringUTFChars((jstring)keyObj, NULL);
+
+ if (tmp == NULL) {
+ return -ENOMEM;
+ }
+
+ AString key = tmp;
+
+ env->ReleaseStringUTFChars((jstring)keyObj, tmp);
+ tmp = NULL;
+
+ jobject valueObj = env->GetObjectArrayElement(values, i);
+
+ if (env->IsInstanceOf(valueObj, stringClass)) {
+ const char *value = env->GetStringUTFChars((jstring)valueObj, NULL);
+
+ if (value == NULL) {
+ return -ENOMEM;
+ }
+
+ msg->setString(key.c_str(), value);
+
+ env->ReleaseStringUTFChars((jstring)valueObj, value);
+ value = NULL;
+ } else if (env->IsInstanceOf(valueObj, integerClass)) {
+ jmethodID intValueID =
+ env->GetMethodID(integerClass, "intValue", "()I");
+ CHECK(intValueID != NULL);
+
+ jint value = env->CallIntMethod(valueObj, intValueID);
+
+ msg->setInt32(key.c_str(), value);
+ } else if (env->IsInstanceOf(valueObj, floatClass)) {
+ jmethodID floatValueID =
+ env->GetMethodID(floatClass, "floatValue", "()F");
+ CHECK(floatValueID != NULL);
+
+ jfloat value = env->CallFloatMethod(valueObj, floatValueID);
+
+ msg->setFloat(key.c_str(), value);
+ } else if (env->IsInstanceOf(valueObj, byteBufClass)) {
+ jmethodID positionID =
+ env->GetMethodID(byteBufClass, "position", "()I");
+ CHECK(positionID != NULL);
+
+ jmethodID limitID =
+ env->GetMethodID(byteBufClass, "limit", "()I");
+ CHECK(limitID != NULL);
+
+ jint position = env->CallIntMethod(valueObj, positionID);
+ jint limit = env->CallIntMethod(valueObj, limitID);
+
+ sp<ABuffer> buffer = new ABuffer(limit - position);
+
+ void *data = env->GetDirectBufferAddress(valueObj);
+
+ if (data != NULL) {
+ memcpy(buffer->data(),
+ (const uint8_t *)data + position,
+ buffer->size());
+ } else {
+ jmethodID arrayID =
+ env->GetMethodID(byteBufClass, "array", "()[B");
+ CHECK(arrayID != NULL);
+
+ jbyteArray byteArray =
+ (jbyteArray)env->CallObjectMethod(valueObj, arrayID);
+ CHECK(byteArray != NULL);
+
+ env->GetByteArrayRegion(
+ byteArray,
+ position,
+ buffer->size(),
+ (jbyte *)buffer->data());
+
+ env->DeleteLocalRef(byteArray); byteArray = NULL;
+ }
+
+ msg->setObject(key.c_str(), buffer);
+ }
+ }
+
+ *out = msg;
+
+ return OK;
+}
+
} // namespace android
diff --git a/media/jni/android_media_Utils.h b/media/jni/android_media_Utils.h
index a2c155a..635bceb 100644
--- a/media/jni/android_media_Utils.h
+++ b/media/jni/android_media_Utils.h
@@ -33,6 +33,14 @@ bool ConvertKeyValueArraysToKeyedVector(
JNIEnv *env, jobjectArray keys, jobjectArray values,
KeyedVector<String8, String8>* vector);
+struct AMessage;
+status_t ConvertMessageToMap(
+ JNIEnv *env, const sp<AMessage> &msg, jobject *map);
+
+status_t ConvertKeyValueArraysToMessage(
+ JNIEnv *env, jobjectArray keys, jobjectArray values,
+ sp<AMessage> *msg);
+
}; // namespace android
#endif // _ANDROID_MEDIA_UTILS_H_
diff --git a/media/jni/audioeffect/android_media_Visualizer.cpp b/media/jni/audioeffect/android_media_Visualizer.cpp
index ecd4d07..f015afb 100644
--- a/media/jni/audioeffect/android_media_Visualizer.cpp
+++ b/media/jni/audioeffect/android_media_Visualizer.cpp
@@ -23,6 +23,7 @@
#include <nativehelper/jni.h>
#include <nativehelper/JNIHelp.h>
#include <android_runtime/AndroidRuntime.h>
+#include <utils/threads.h>
#include "media/Visualizer.h"
using namespace android;
@@ -38,6 +39,7 @@ using namespace android;
#define NATIVE_EVENT_PCM_CAPTURE 0
#define NATIVE_EVENT_FFT_CAPTURE 1
+#define NATIVE_EVENT_SERVER_DIED 2
// ----------------------------------------------------------------------------
static const char* const kClassPathName = "android/media/audiofx/Visualizer";
@@ -54,6 +56,43 @@ static fields_t fields;
struct visualizer_callback_cookie {
jclass visualizer_class; // Visualizer class
jobject visualizer_ref; // Visualizer object instance
+
+ // Lazily allocated arrays used to hold callback data provided to java
+ // applications. These arrays are allocated during the first callback and
+ // reallocated when the size of the callback data changes. Allocating on
+ // demand and saving the arrays means that applications cannot safely hold a
+ // reference to the provided data (they need to make a copy if they want to
+ // hold onto outside of the callback scope), but it avoids GC thrash caused
+ // by constantly allocating and releasing arrays to hold callback data.
+ Mutex callback_data_lock;
+ jbyteArray waveform_data;
+ jbyteArray fft_data;
+
+ visualizer_callback_cookie() {
+ waveform_data = NULL;
+ fft_data = NULL;
+ }
+
+ ~visualizer_callback_cookie() {
+ cleanupBuffers();
+ }
+
+ void cleanupBuffers() {
+ AutoMutex lock(&callback_data_lock);
+ if (waveform_data || fft_data) {
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ if (waveform_data) {
+ env->DeleteGlobalRef(waveform_data);
+ waveform_data = NULL;
+ }
+
+ if (fft_data) {
+ env->DeleteGlobalRef(fft_data);
+ fft_data = NULL;
+ }
+ }
+ }
};
// ----------------------------------------------------------------------------
@@ -66,7 +105,6 @@ class visualizerJniStorage {
~visualizerJniStorage() {
}
-
};
@@ -93,6 +131,26 @@ static jint translateError(int code) {
// ----------------------------------------------------------------------------
+static void ensureArraySize(JNIEnv *env, jbyteArray *array, uint32_t size) {
+ if (NULL != *array) {
+ uint32_t len = env->GetArrayLength(*array);
+ if (len == size)
+ return;
+
+ env->DeleteGlobalRef(*array);
+ *array = NULL;
+ }
+
+ jbyteArray localRef = env->NewByteArray(size);
+ if (NULL != localRef) {
+ // Promote to global ref.
+ *array = (jbyteArray)env->NewGlobalRef(localRef);
+
+ // Release our (now pointless) local ref.
+ env->DeleteLocalRef(localRef);
+ }
+}
+
static void captureCallback(void* user,
uint32_t waveformSize,
uint8_t *waveform,
@@ -106,6 +164,7 @@ static void captureCallback(void* user,
visualizer_callback_cookie *callbackInfo = (visualizer_callback_cookie *)user;
JNIEnv *env = AndroidRuntime::getJNIEnv();
+ AutoMutex lock(&callbackInfo->callback_data_lock);
ALOGV("captureCallback: callbackInfo %p, visualizer_ref %p visualizer_class %p",
callbackInfo,
@@ -118,7 +177,11 @@ static void captureCallback(void* user,
}
if (waveformSize != 0 && waveform != NULL) {
- jbyteArray jArray = env->NewByteArray(waveformSize);
+ jbyteArray jArray;
+
+ ensureArraySize(env, &callbackInfo->waveform_data, waveformSize);
+ jArray = callbackInfo->waveform_data;
+
if (jArray != NULL) {
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
memcpy(nArray, waveform, waveformSize);
@@ -131,12 +194,15 @@ static void captureCallback(void* user,
samplingrate,
0,
jArray);
- env->DeleteLocalRef(jArray);
}
}
if (fftSize != 0 && fft != NULL) {
- jbyteArray jArray = env->NewByteArray(fftSize);
+ jbyteArray jArray;
+
+ ensureArraySize(env, &callbackInfo->fft_data, fftSize);
+ jArray = callbackInfo->fft_data;
+
if (jArray != NULL) {
jbyte *nArray = env->GetByteArrayElements(jArray, NULL);
memcpy(nArray, fft, fftSize);
@@ -149,7 +215,6 @@ static void captureCallback(void* user,
samplingrate,
0,
jArray);
- env->DeleteLocalRef(jArray);
}
}
@@ -220,6 +285,23 @@ android_media_visualizer_native_init(JNIEnv *env)
}
+static void android_media_visualizer_effect_callback(int32_t event,
+ void *user,
+ void *info) {
+ if ((event == AudioEffect::EVENT_ERROR) &&
+ (*((status_t*)info) == DEAD_OBJECT)) {
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage*)user;
+ visualizer_callback_cookie* callbackInfo = &lpJniStorage->mCallbackData;
+ JNIEnv *env = AndroidRuntime::getJNIEnv();
+
+ env->CallStaticVoidMethod(
+ callbackInfo->visualizer_class,
+ fields.midPostNativeEvent,
+ callbackInfo->visualizer_ref,
+ NATIVE_EVENT_SERVER_DIED,
+ 0, 0, 0);
+ }
+}
static jint
android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_this,
@@ -255,8 +337,8 @@ android_media_visualizer_native_setup(JNIEnv *env, jobject thiz, jobject weak_th
// create the native Visualizer object
lpVisualizer = new Visualizer(0,
- NULL,
- NULL,
+ android_media_visualizer_effect_callback,
+ lpJniStorage,
sessionId);
if (lpVisualizer == NULL) {
ALOGE("Error creating Visualizer");
@@ -345,7 +427,17 @@ android_media_visualizer_native_setEnabled(JNIEnv *env, jobject thiz, jboolean e
return VISUALIZER_ERROR_NO_INIT;
}
- return translateError(lpVisualizer->setEnabled(enabled));
+ jint retVal = translateError(lpVisualizer->setEnabled(enabled));
+
+ if (!enabled) {
+ visualizerJniStorage* lpJniStorage = (visualizerJniStorage *)env->GetIntField(
+ thiz, fields.fidJniData);
+
+ if (NULL != lpJniStorage)
+ lpJniStorage->mCallbackData.cleanupBuffers();
+ }
+
+ return retVal;
}
static jboolean
diff --git a/media/jni/mediaeditor/VideoEditorMain.cpp b/media/jni/mediaeditor/VideoEditorMain.cpp
index c84a883..b0c1c35 100755
--- a/media/jni/mediaeditor/VideoEditorMain.cpp
+++ b/media/jni/mediaeditor/VideoEditorMain.cpp
@@ -29,8 +29,7 @@
#include <VideoEditorThumbnailMain.h>
#include <M4OSA_Debug.h>
#include <M4xVSS_Internal.h>
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/ISurface.h>
+#include <gui/Surface.h>
#include "VideoEditorPreviewController.h"
#include "VideoEditorMain.h"
diff --git a/media/libaah_rtp/Android.mk b/media/libaah_rtp/Android.mk
new file mode 100644
index 0000000..54fd9ec
--- /dev/null
+++ b/media/libaah_rtp/Android.mk
@@ -0,0 +1,40 @@
+LOCAL_PATH:= $(call my-dir)
+#
+# libaah_rtp
+#
+
+include $(CLEAR_VARS)
+
+LOCAL_MODULE := libaah_rtp
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_SRC_FILES := \
+ aah_decoder_pump.cpp \
+ aah_rx_player.cpp \
+ aah_rx_player_core.cpp \
+ aah_rx_player_ring_buffer.cpp \
+ aah_rx_player_substream.cpp \
+ aah_tx_packet.cpp \
+ aah_tx_player.cpp \
+ aah_tx_sender.cpp \
+ pipe_event.cpp
+
+LOCAL_C_INCLUDES := \
+ frameworks/base/include \
+ frameworks/base/include/media/stagefright/openmax \
+ frameworks/base/media \
+ frameworks/base/media/libstagefright
+
+LOCAL_SHARED_LIBRARIES := \
+ libcommon_time_client \
+ libbinder \
+ libmedia \
+ libstagefright \
+ libstagefright_foundation \
+ libutils
+
+LOCAL_LDLIBS := \
+ -lpthread
+
+include $(BUILD_SHARED_LIBRARY)
+
diff --git a/media/libaah_rtp/aah_decoder_pump.cpp b/media/libaah_rtp/aah_decoder_pump.cpp
new file mode 100644
index 0000000..28b8c7b
--- /dev/null
+++ b/media/libaah_rtp/aah_decoder_pump.cpp
@@ -0,0 +1,520 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <poll.h>
+#include <pthread.h>
+
+#include <common_time/cc_helper.h>
+#include <media/AudioSystem.h>
+#include <media/AudioTrack.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXClient.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/Utils.h>
+#include <utils/Timers.h>
+#include <utils/threads.h>
+
+#include "aah_decoder_pump.h"
+
+namespace android {
+
+static const long long kLongDecodeErrorThreshold = 1000000ll;
+static const uint32_t kMaxLongErrorsBeforeFatal = 3;
+static const uint32_t kMaxErrorsBeforeFatal = 60;
+
+AAH_DecoderPump::AAH_DecoderPump(OMXClient& omx)
+ : omx_(omx)
+ , thread_status_(OK)
+ , renderer_(NULL)
+ , last_queued_pts_valid_(false)
+ , last_queued_pts_(0)
+ , last_ts_transform_valid_(false)
+ , last_volume_(0xFF) {
+ thread_ = new ThreadWrapper(this);
+}
+
+AAH_DecoderPump::~AAH_DecoderPump() {
+ shutdown();
+}
+
+status_t AAH_DecoderPump::initCheck() {
+ if (thread_ == NULL) {
+ ALOGE("Failed to allocate thread");
+ return NO_MEMORY;
+ }
+
+ return OK;
+}
+
+status_t AAH_DecoderPump::queueForDecode(MediaBuffer* buf) {
+ if (NULL == buf) {
+ return BAD_VALUE;
+ }
+
+ if (OK != thread_status_) {
+ return thread_status_;
+ }
+
+ { // Explicit scope for AutoMutex pattern.
+ AutoMutex lock(&thread_lock_);
+ in_queue_.push_back(buf);
+ }
+
+ thread_cond_.signal();
+
+ return OK;
+}
+
+void AAH_DecoderPump::queueToRenderer(MediaBuffer* decoded_sample) {
+ Mutex::Autolock lock(&render_lock_);
+ sp<MetaData> meta;
+ int64_t ts;
+ status_t res;
+
+ // Fetch the metadata and make sure the sample has a timestamp. We
+ // cannot render samples which are missing PTSs.
+ meta = decoded_sample->meta_data();
+ if ((meta == NULL) || (!meta->findInt64(kKeyTime, &ts))) {
+ ALOGV("Decoded sample missing timestamp, cannot render.");
+ CHECK(false);
+ } else {
+ // If we currently are not holding on to a renderer, go ahead and
+ // make one now.
+ if (NULL == renderer_) {
+ renderer_ = new TimedAudioTrack();
+ if (NULL != renderer_) {
+ int frameCount;
+ AudioTrack::getMinFrameCount(&frameCount,
+ AUDIO_STREAM_DEFAULT,
+ static_cast<int>(format_sample_rate_));
+ int ch_format = (format_channels_ == 1)
+ ? AUDIO_CHANNEL_OUT_MONO
+ : AUDIO_CHANNEL_OUT_STEREO;
+
+ res = renderer_->set(AUDIO_STREAM_DEFAULT,
+ format_sample_rate_,
+ AUDIO_FORMAT_PCM_16_BIT,
+ ch_format,
+ frameCount);
+ if (res != OK) {
+ ALOGE("Failed to setup audio renderer. (res = %d)", res);
+ delete renderer_;
+ renderer_ = NULL;
+ } else {
+ CHECK(last_ts_transform_valid_);
+
+ res = renderer_->setMediaTimeTransform(
+ last_ts_transform_, TimedAudioTrack::COMMON_TIME);
+ if (res != NO_ERROR) {
+ ALOGE("Failed to set media time transform on AudioTrack"
+ " (res = %d)", res);
+ delete renderer_;
+ renderer_ = NULL;
+ } else {
+ float volume = static_cast<float>(last_volume_)
+ / 255.0f;
+ if (renderer_->setVolume(volume, volume) != OK) {
+ ALOGW("%s: setVolume failed", __FUNCTION__);
+ }
+
+ renderer_->start();
+ }
+ }
+ } else {
+ ALOGE("Failed to allocate AudioTrack to use as a renderer.");
+ }
+ }
+
+ if (NULL != renderer_) {
+ uint8_t* decoded_data =
+ reinterpret_cast<uint8_t*>(decoded_sample->data());
+ uint32_t decoded_amt = decoded_sample->range_length();
+ decoded_data += decoded_sample->range_offset();
+
+ sp<IMemory> pcm_payload;
+ res = renderer_->allocateTimedBuffer(decoded_amt, &pcm_payload);
+ if (res != OK) {
+ ALOGE("Failed to allocate %d byte audio track buffer."
+ " (res = %d)", decoded_amt, res);
+ } else {
+ memcpy(pcm_payload->pointer(), decoded_data, decoded_amt);
+
+ res = renderer_->queueTimedBuffer(pcm_payload, ts);
+ if (res != OK) {
+ ALOGE("Failed to queue %d byte audio track buffer with"
+ " media PTS %lld. (res = %d)", decoded_amt, ts, res);
+ } else {
+ last_queued_pts_valid_ = true;
+ last_queued_pts_ = ts;
+ }
+ }
+
+ } else {
+ ALOGE("No renderer, dropping audio payload.");
+ }
+ }
+}
+
+void AAH_DecoderPump::stopAndCleanupRenderer() {
+ if (NULL == renderer_) {
+ return;
+ }
+
+ renderer_->stop();
+ delete renderer_;
+ renderer_ = NULL;
+}
+
+void AAH_DecoderPump::setRenderTSTransform(const LinearTransform& trans) {
+ Mutex::Autolock lock(&render_lock_);
+
+ if (last_ts_transform_valid_ && !memcmp(&trans,
+ &last_ts_transform_,
+ sizeof(trans))) {
+ return;
+ }
+
+ last_ts_transform_ = trans;
+ last_ts_transform_valid_ = true;
+
+ if (NULL != renderer_) {
+ status_t res = renderer_->setMediaTimeTransform(
+ last_ts_transform_, TimedAudioTrack::COMMON_TIME);
+ if (res != NO_ERROR) {
+ ALOGE("Failed to set media time transform on AudioTrack"
+ " (res = %d)", res);
+ }
+ }
+}
+
+void AAH_DecoderPump::setRenderVolume(uint8_t volume) {
+ Mutex::Autolock lock(&render_lock_);
+
+ if (volume == last_volume_) {
+ return;
+ }
+
+ last_volume_ = volume;
+ if (renderer_ != NULL) {
+ float volume = static_cast<float>(last_volume_) / 255.0f;
+ if (renderer_->setVolume(volume, volume) != OK) {
+ ALOGW("%s: setVolume failed", __FUNCTION__);
+ }
+ }
+}
+
+// isAboutToUnderflow is something of a hack used to figure out when it might be
+// time to give up on trying to fill in a gap in the RTP sequence and simply
+// move on with a discontinuity. If we had perfect knowledge of when we were
+// going to underflow, it would not be a hack, but unfortunately we do not.
+// Right now, we just take the PTS of the last sample queued, and check to see
+// if its presentation time is within kAboutToUnderflowThreshold from now. If
+// it is, then we say that we are about to underflow. This decision is based on
+// two (possibly invalid) assumptions.
+//
+// 1) The transmitter is leading the clock by more than
+// kAboutToUnderflowThreshold.
+// 2) The delta between the PTS of the last sample queued and the next sample
+// is less than the transmitter's clock lead amount.
+//
+// Right now, the default transmitter lead time is 1 second, which is a pretty
+// large number and greater than the 50mSec that kAboutToUnderflowThreshold is
+// currently set to. This should satisfy assumption #1 for now, but changes to
+// the transmitter clock lead time could effect this.
+//
+// For non-sparse streams with a homogeneous sample rate (the vast majority of
+// streams in the world), the delta between any two adjacent PTSs will always be
+// the homogeneous sample period. It is very uncommon to see a sample period
+// greater than the 1 second clock lead we are currently using, and you
+// certainly will not see it in an MP3 file which should satisfy assumption #2.
+// Sparse audio streams (where no audio is transmitted for long periods of
+// silence) and extremely low framerate video stream (like an MPEG-2 slideshow
+// or the video stream for a pay TV audio channel) are examples of streams which
+// might violate assumption #2.
+bool AAH_DecoderPump::isAboutToUnderflow(int64_t threshold) {
+ Mutex::Autolock lock(&render_lock_);
+
+ // If we have never queued anything to the decoder, we really don't know if
+ // we are going to underflow or not.
+ if (!last_queued_pts_valid_ || !last_ts_transform_valid_) {
+ return false;
+ }
+
+ // Don't have access to Common Time? If so, then things are Very Bad
+ // elsewhere in the system; it pretty much does not matter what we do here.
+ // Since we cannot really tell if we are about to underflow or not, its
+ // probably best to assume that we are not and proceed accordingly.
+ int64_t tt_now;
+ if (OK != cc_helper_.getCommonTime(&tt_now)) {
+ return false;
+ }
+
+ // Transform from media time to common time.
+ int64_t last_queued_pts_tt;
+ if (!last_ts_transform_.doForwardTransform(last_queued_pts_,
+ &last_queued_pts_tt)) {
+ return false;
+ }
+
+ // Check to see if we are underflowing.
+ return ((tt_now + threshold - last_queued_pts_tt) > 0);
+}
+
+void* AAH_DecoderPump::workThread() {
+ // No need to lock when accessing decoder_ from the thread. The
+ // implementation of init and shutdown ensure that other threads never touch
+ // decoder_ while the work thread is running.
+ CHECK(decoder_ != NULL);
+ CHECK(format_ != NULL);
+
+ // Start the decoder and note its result code. If something goes horribly
+ // wrong, callers of queueForDecode and getOutput will be able to detect
+ // that the thread encountered a fatal error and shut down by examining
+ // thread_status_.
+ thread_status_ = decoder_->start(format_.get());
+ if (OK != thread_status_) {
+ ALOGE("AAH_DecoderPump's work thread failed to start decoder"
+ " (res = %d)", thread_status_);
+ return NULL;
+ }
+
+ DurationTimer decode_timer;
+ uint32_t consecutive_long_errors = 0;
+ uint32_t consecutive_errors = 0;
+
+ while (!thread_->exitPending()) {
+ status_t res;
+ MediaBuffer* bufOut = NULL;
+
+ decode_timer.start();
+ res = decoder_->read(&bufOut);
+ decode_timer.stop();
+
+ if (res == INFO_FORMAT_CHANGED) {
+ // Format has changed. Destroy our current renderer so that a new
+ // one can be created during queueToRenderer with the proper format.
+ //
+ // TODO : In order to transition seamlessly, we should change this
+ // to put the old renderer in a queue to play out completely before
+ // we destroy it. We can still create a new renderer, the timed
+ // nature of the renderer should ensure a seamless splice.
+ stopAndCleanupRenderer();
+ res = OK;
+ }
+
+ // Try to be a little nuanced in our handling of actual decode errors.
+ // Errors could happen because of minor stream corruption or because of
+ // transient resource limitations. In these cases, we would rather drop
+ // a little bit of output and ride out the unpleasantness then throw up
+ // our hands and abort everything.
+ //
+ // OTOH - When things are really bad (like we have a non-transient
+ // resource or bookkeeping issue, or the stream being fed to us is just
+ // complete and total garbage) we really want to terminate playback and
+ // raise an error condition all the way up to the application level so
+ // they can deal with it.
+ //
+ // Unfortunately, the error codes returned by the decoder can be a
+ // little non-specific. For example, if an OMXCodec times out
+ // attempting to obtain an output buffer, the error we get back is a
+ // generic -1. Try to distinguish between this resource timeout error
+ // and ES corruption error by timing how long the decode operation
+ // takes. Maintain accounting for both errors and "long errors". If we
+ // get more than a certain number consecutive errors of either type,
+ // consider it fatal and shutdown (which will cause the error to
+ // propagate all of the way up to the application level). The threshold
+ // for "long errors" is deliberately much lower than that of normal
+ // decode errors, both because of how long they take to happen and
+ // because they generally indicate resource limitation errors which are
+ // unlikely to go away in pathologically bad cases (in contrast to
+ // stream corruption errors which might happen 20 times in a row and
+ // then be suddenly OK again)
+ if (res != OK) {
+ consecutive_errors++;
+ if (decode_timer.durationUsecs() >= kLongDecodeErrorThreshold)
+ consecutive_long_errors++;
+
+ CHECK(NULL == bufOut);
+
+ ALOGW("%s: Failed to decode data (res = %d)",
+ __PRETTY_FUNCTION__, res);
+
+ if ((consecutive_errors >= kMaxErrorsBeforeFatal) ||
+ (consecutive_long_errors >= kMaxLongErrorsBeforeFatal)) {
+ ALOGE("%s: Maximum decode error threshold has been reached."
+ " There have been %d consecutive decode errors, and %d"
+ " consecutive decode operations which resulted in errors"
+ " and took more than %lld uSec to process. The last"
+ " decode operation took %lld uSec.",
+ __PRETTY_FUNCTION__,
+ consecutive_errors, consecutive_long_errors,
+ kLongDecodeErrorThreshold, decode_timer.durationUsecs());
+ thread_status_ = res;
+ break;
+ }
+
+ continue;
+ }
+
+ if (NULL == bufOut) {
+ ALOGW("%s: Successful decode, but no buffer produced",
+ __PRETTY_FUNCTION__);
+ continue;
+ }
+
+ // Successful decode (with actual output produced). Clear the error
+ // counters.
+ consecutive_errors = 0;
+ consecutive_long_errors = 0;
+
+ queueToRenderer(bufOut);
+ bufOut->release();
+ }
+
+ decoder_->stop();
+ stopAndCleanupRenderer();
+
+ return NULL;
+}
+
+status_t AAH_DecoderPump::init(const sp<MetaData>& params) {
+ Mutex::Autolock lock(&init_lock_);
+
+ if (decoder_ != NULL) {
+ // already inited
+ return OK;
+ }
+
+ if (params == NULL) {
+ return BAD_VALUE;
+ }
+
+ if (!params->findInt32(kKeyChannelCount, &format_channels_)) {
+ return BAD_VALUE;
+ }
+
+ if (!params->findInt32(kKeySampleRate, &format_sample_rate_)) {
+ return BAD_VALUE;
+ }
+
+ CHECK(OK == thread_status_);
+ CHECK(decoder_ == NULL);
+
+ status_t ret_val = UNKNOWN_ERROR;
+
+ // Cache the format and attempt to create the decoder.
+ format_ = params;
+ decoder_ = OMXCodec::Create(
+ omx_.interface(), // IOMX Handle
+ format_, // Metadata for substream (indicates codec)
+ false, // Make a decoder, not an encoder
+ sp<MediaSource>(this)); // We will be the source for this codec.
+
+ if (decoder_ == NULL) {
+ ALOGE("Failed to allocate decoder in %s", __PRETTY_FUNCTION__);
+ goto bailout;
+ }
+
+ // Fire up the pump thread. It will take care of starting and stopping the
+ // decoder.
+ ret_val = thread_->run("aah_decode_pump", ANDROID_PRIORITY_AUDIO);
+ if (OK != ret_val) {
+ ALOGE("Failed to start work thread in %s (res = %d)",
+ __PRETTY_FUNCTION__, ret_val);
+ goto bailout;
+ }
+
+bailout:
+ if (OK != ret_val) {
+ decoder_ = NULL;
+ format_ = NULL;
+ }
+
+ return OK;
+}
+
+status_t AAH_DecoderPump::shutdown() {
+ Mutex::Autolock lock(&init_lock_);
+ return shutdown_l();
+}
+
+status_t AAH_DecoderPump::shutdown_l() {
+ thread_->requestExit();
+ thread_cond_.signal();
+ thread_->requestExitAndWait();
+
+ for (MBQueue::iterator iter = in_queue_.begin();
+ iter != in_queue_.end();
+ ++iter) {
+ (*iter)->release();
+ }
+ in_queue_.clear();
+
+ last_queued_pts_valid_ = false;
+ last_ts_transform_valid_ = false;
+ last_volume_ = 0xFF;
+ thread_status_ = OK;
+
+ decoder_ = NULL;
+ format_ = NULL;
+
+ return OK;
+}
+
+status_t AAH_DecoderPump::read(MediaBuffer **buffer,
+ const ReadOptions *options) {
+ if (!buffer) {
+ return BAD_VALUE;
+ }
+
+ *buffer = NULL;
+
+ // While its not time to shut down, and we have no data to process, wait.
+ AutoMutex lock(&thread_lock_);
+ while (!thread_->exitPending() && in_queue_.empty())
+ thread_cond_.wait(thread_lock_);
+
+ // At this point, if its not time to shutdown then we must have something to
+ // process. Go ahead and pop the front of the queue for processing.
+ if (!thread_->exitPending()) {
+ CHECK(!in_queue_.empty());
+
+ *buffer = *(in_queue_.begin());
+ in_queue_.erase(in_queue_.begin());
+ }
+
+ // If we managed to get a buffer, then everything must be OK. If not, then
+ // we must be shutting down.
+ return (NULL == *buffer) ? INVALID_OPERATION : OK;
+}
+
+AAH_DecoderPump::ThreadWrapper::ThreadWrapper(AAH_DecoderPump* owner)
+ : Thread(false /* canCallJava*/ )
+ , owner_(owner) {
+}
+
+bool AAH_DecoderPump::ThreadWrapper::threadLoop() {
+ CHECK(NULL != owner_);
+ owner_->workThread();
+ return false;
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_decoder_pump.h b/media/libaah_rtp/aah_decoder_pump.h
new file mode 100644
index 0000000..f5a6529
--- /dev/null
+++ b/media/libaah_rtp/aah_decoder_pump.h
@@ -0,0 +1,107 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __DECODER_PUMP_H__
+#define __DECODER_PUMP_H__
+
+#include <pthread.h>
+
+#include <common_time/cc_helper.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/LinearTransform.h>
+#include <utils/List.h>
+#include <utils/threads.h>
+
+namespace android {
+
+class MetaData;
+class OMXClient;
+class TimedAudioTrack;
+
+class AAH_DecoderPump : public MediaSource {
+ public:
+ explicit AAH_DecoderPump(OMXClient& omx);
+ status_t initCheck();
+
+ status_t queueForDecode(MediaBuffer* buf);
+
+ status_t init(const sp<MetaData>& params);
+ status_t shutdown();
+
+ void setRenderTSTransform(const LinearTransform& trans);
+ void setRenderVolume(uint8_t volume);
+ bool isAboutToUnderflow(int64_t threshold);
+ bool getStatus() const { return thread_status_; }
+
+ // MediaSource methods
+ virtual status_t start(MetaData *params) { return OK; }
+ virtual sp<MetaData> getFormat() { return format_; }
+ virtual status_t stop() { return OK; }
+ virtual status_t read(MediaBuffer **buffer,
+ const ReadOptions *options);
+
+ protected:
+ virtual ~AAH_DecoderPump();
+
+ private:
+ class ThreadWrapper : public Thread {
+ public:
+ friend class AAH_DecoderPump;
+ explicit ThreadWrapper(AAH_DecoderPump* owner);
+
+ private:
+ virtual bool threadLoop();
+ AAH_DecoderPump* owner_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper);
+ };
+
+ void* workThread();
+ virtual status_t shutdown_l();
+ void queueToRenderer(MediaBuffer* decoded_sample);
+ void stopAndCleanupRenderer();
+
+ sp<MetaData> format_;
+ int32_t format_channels_;
+ int32_t format_sample_rate_;
+
+ sp<MediaSource> decoder_;
+ OMXClient& omx_;
+ Mutex init_lock_;
+
+ sp<ThreadWrapper> thread_;
+ Condition thread_cond_;
+ Mutex thread_lock_;
+ status_t thread_status_;
+
+ Mutex render_lock_;
+ TimedAudioTrack* renderer_;
+ bool last_queued_pts_valid_;
+ int64_t last_queued_pts_;
+ bool last_ts_transform_valid_;
+ LinearTransform last_ts_transform_;
+ uint8_t last_volume_;
+ CCHelper cc_helper_;
+
+ // protected by the thread_lock_
+ typedef List<MediaBuffer*> MBQueue;
+ MBQueue in_queue_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AAH_DecoderPump);
+};
+
+} // namespace android
+#endif // __DECODER_PUMP_H__
diff --git a/media/libaah_rtp/aah_rx_player.cpp b/media/libaah_rtp/aah_rx_player.cpp
new file mode 100644
index 0000000..9dd79fd
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player.cpp
@@ -0,0 +1,288 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+
+#include <binder/IServiceManager.h>
+#include <media/MediaPlayerInterface.h>
+#include <utils/Log.h>
+
+#include "aah_rx_player.h"
+
+namespace android {
+
+const uint32_t AAH_RXPlayer::kRTPRingBufferSize = 1 << 10;
+
+sp<MediaPlayerBase> createAAH_RXPlayer() {
+ sp<MediaPlayerBase> ret = new AAH_RXPlayer();
+ return ret;
+}
+
+AAH_RXPlayer::AAH_RXPlayer()
+ : ring_buffer_(kRTPRingBufferSize)
+ , substreams_(NULL) {
+ thread_wrapper_ = new ThreadWrapper(*this);
+
+ is_playing_ = false;
+ multicast_joined_ = false;
+ transmitter_known_ = false;
+ current_epoch_known_ = false;
+ data_source_set_ = false;
+ sock_fd_ = -1;
+
+ substreams_.setCapacity(4);
+
+ memset(&listen_addr_, 0, sizeof(listen_addr_));
+ memset(&transmitter_addr_, 0, sizeof(transmitter_addr_));
+
+ fetchAudioFlinger();
+}
+
+AAH_RXPlayer::~AAH_RXPlayer() {
+ reset_l();
+ CHECK(substreams_.size() == 0);
+ omx_.disconnect();
+}
+
+status_t AAH_RXPlayer::initCheck() {
+ if (thread_wrapper_ == NULL) {
+ ALOGE("Failed to allocate thread wrapper!");
+ return NO_MEMORY;
+ }
+
+ if (!ring_buffer_.initCheck()) {
+ ALOGE("Failed to allocate reassembly ring buffer!");
+ return NO_MEMORY;
+ }
+
+ // Check for the presense of the common time service by attempting to query
+ // for CommonTime's frequency. If we get an error back, we cannot talk to
+ // the service at all and should abort now.
+ status_t res;
+ uint64_t freq;
+ res = cc_helper_.getCommonFreq(&freq);
+ if (OK != res) {
+ ALOGE("Failed to connect to common time service!");
+ return res;
+ }
+
+ return omx_.connect();
+}
+
+status_t AAH_RXPlayer::setDataSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
+ AutoMutex api_lock(&api_lock_);
+ uint32_t a, b, c, d;
+ uint16_t port;
+
+ if (data_source_set_) {
+ return INVALID_OPERATION;
+ }
+
+ if (NULL == url) {
+ return BAD_VALUE;
+ }
+
+ if (5 != sscanf(url, "%*[^:/]://%u.%u.%u.%u:%hu", &a, &b, &c, &d, &port)) {
+ ALOGE("Failed to parse URL \"%s\"", url);
+ return BAD_VALUE;
+ }
+
+ if ((a > 255) || (b > 255) || (c > 255) || (d > 255) || (port == 0)) {
+ ALOGE("Bad multicast address \"%s\"", url);
+ return BAD_VALUE;
+ }
+
+ ALOGI("setDataSource :: %u.%u.%u.%u:%hu", a, b, c, d, port);
+
+ a = (a << 24) | (b << 16) | (c << 8) | d;
+
+ memset(&listen_addr_, 0, sizeof(listen_addr_));
+ listen_addr_.sin_family = AF_INET;
+ listen_addr_.sin_port = htons(port);
+ listen_addr_.sin_addr.s_addr = htonl(a);
+ data_source_set_ = true;
+
+ return OK;
+}
+
+status_t AAH_RXPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+ return INVALID_OPERATION;
+}
+
+status_t AAH_RXPlayer::setVideoSurface(const sp<Surface>& surface) {
+ return OK;
+}
+
+status_t AAH_RXPlayer::setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture) {
+ return OK;
+}
+
+status_t AAH_RXPlayer::prepare() {
+ return OK;
+}
+
+status_t AAH_RXPlayer::prepareAsync() {
+ sendEvent(MEDIA_PREPARED);
+ return OK;
+}
+
+status_t AAH_RXPlayer::start() {
+ AutoMutex api_lock(&api_lock_);
+
+ if (is_playing_) {
+ return OK;
+ }
+
+ status_t res = startWorkThread();
+ is_playing_ = (res == OK);
+ return res;
+}
+
+status_t AAH_RXPlayer::stop() {
+ return pause();
+}
+
+status_t AAH_RXPlayer::pause() {
+ AutoMutex api_lock(&api_lock_);
+ stopWorkThread();
+ CHECK(sock_fd_ < 0);
+ is_playing_ = false;
+ return OK;
+}
+
+bool AAH_RXPlayer::isPlaying() {
+ AutoMutex api_lock(&api_lock_);
+ return is_playing_;
+}
+
+status_t AAH_RXPlayer::seekTo(int msec) {
+ sendEvent(MEDIA_SEEK_COMPLETE);
+ return OK;
+}
+
+status_t AAH_RXPlayer::getCurrentPosition(int *msec) {
+ if (NULL != msec) {
+ *msec = 0;
+ }
+ return OK;
+}
+
+status_t AAH_RXPlayer::getDuration(int *msec) {
+ if (NULL != msec) {
+ *msec = 1;
+ }
+ return OK;
+}
+
+status_t AAH_RXPlayer::reset() {
+ AutoMutex api_lock(&api_lock_);
+ reset_l();
+ return OK;
+}
+
+void AAH_RXPlayer::reset_l() {
+ stopWorkThread();
+ CHECK(sock_fd_ < 0);
+ CHECK(!multicast_joined_);
+ is_playing_ = false;
+ data_source_set_ = false;
+ transmitter_known_ = false;
+ memset(&listen_addr_, 0, sizeof(listen_addr_));
+}
+
+status_t AAH_RXPlayer::setLooping(int loop) {
+ return OK;
+}
+
+player_type AAH_RXPlayer::playerType() {
+ return AAH_RX_PLAYER;
+}
+
+status_t AAH_RXPlayer::setParameter(int key, const Parcel &request) {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_RXPlayer::getParameter(int key, Parcel *reply) {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_RXPlayer::invoke(const Parcel& request, Parcel *reply) {
+ if (!reply) {
+ return BAD_VALUE;
+ }
+
+ int32_t magic;
+ status_t err = request.readInt32(&magic);
+ if (err != OK) {
+ reply->writeInt32(err);
+ return OK;
+ }
+
+ if (magic != 0x12345) {
+ reply->writeInt32(BAD_VALUE);
+ return OK;
+ }
+
+ int32_t methodID;
+ err = request.readInt32(&methodID);
+ if (err != OK) {
+ reply->writeInt32(err);
+ return OK;
+ }
+
+ switch (methodID) {
+ // Get Volume
+ case INVOKE_GET_MASTER_VOLUME: {
+ if (audio_flinger_ != NULL) {
+ reply->writeInt32(OK);
+ reply->writeFloat(audio_flinger_->masterVolume());
+ } else {
+ reply->writeInt32(UNKNOWN_ERROR);
+ }
+ } break;
+
+ // Set Volume
+ case INVOKE_SET_MASTER_VOLUME: {
+ float targetVol = request.readFloat();
+ reply->writeInt32(audio_flinger_->setMasterVolume(targetVol));
+ } break;
+
+ default: return BAD_VALUE;
+ }
+
+ return OK;
+}
+
+void AAH_RXPlayer::fetchAudioFlinger() {
+ if (audio_flinger_ == NULL) {
+ sp<IServiceManager> sm = defaultServiceManager();
+ sp<IBinder> binder;
+ binder = sm->getService(String16("media.audio_flinger"));
+
+ if (binder == NULL) {
+ ALOGW("AAH_RXPlayer failed to fetch handle to audio flinger."
+ " Master volume control will not be possible.");
+ }
+
+ audio_flinger_ = interface_cast<IAudioFlinger>(binder);
+ }
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_rx_player.h b/media/libaah_rtp/aah_rx_player.h
new file mode 100644
index 0000000..ba5617e
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player.h
@@ -0,0 +1,318 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAH_RX_PLAYER_H__
+#define __AAH_RX_PLAYER_H__
+
+#include <common_time/cc_helper.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXClient.h>
+#include <netinet/in.h>
+#include <utils/KeyedVector.h>
+#include <utils/LinearTransform.h>
+#include <utils/threads.h>
+
+#include "aah_decoder_pump.h"
+#include "pipe_event.h"
+
+namespace android {
+
+class AAH_RXPlayer : public MediaPlayerInterface {
+ public:
+ AAH_RXPlayer();
+
+ virtual status_t initCheck();
+ virtual status_t setDataSource(const char *url,
+ const KeyedVector<String8, String8>*
+ headers);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual status_t setVideoSurface(const sp<Surface>& surface);
+ virtual status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>&
+ surfaceTexture);
+ virtual status_t prepare();
+ virtual status_t prepareAsync();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual bool isPlaying();
+ virtual status_t seekTo(int msec);
+ virtual status_t getCurrentPosition(int *msec);
+ virtual status_t getDuration(int *msec);
+ virtual status_t reset();
+ virtual status_t setLooping(int loop);
+ virtual player_type playerType();
+ virtual status_t setParameter(int key, const Parcel &request);
+ virtual status_t getParameter(int key, Parcel *reply);
+ virtual status_t invoke(const Parcel& request, Parcel *reply);
+
+ protected:
+ virtual ~AAH_RXPlayer();
+
+ private:
+ class ThreadWrapper : public Thread {
+ public:
+ friend class AAH_RXPlayer;
+ explicit ThreadWrapper(AAH_RXPlayer& player)
+ : Thread(false /* canCallJava */ )
+ , player_(player) { }
+
+ virtual bool threadLoop() { return player_.threadLoop(); }
+
+ private:
+ AAH_RXPlayer& player_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(ThreadWrapper);
+ };
+
+#pragma pack(push, 1)
+ // PacketBuffers are structures used by the RX ring buffer. The ring buffer
+ // is a ring of pointers to PacketBuffer structures which act as variable
+ // length byte arrays and hold the contents of received UDP packets. Rather
+ // than make this a structure which hold a length and a pointer to another
+ // allocated structure (which would require two allocations), this struct
+ // uses a structure overlay pattern where allocation for the byte array
+ // consists of allocating (arrayLen + sizeof(ssize_t)) bytes of data from
+ // whatever pool/heap the packet buffer pulls from, and then overlaying the
+ // packed PacketBuffer structure on top of the allocation. The one-byte
+ // array at the end of the structure serves as an offset to the the data
+ // portion of the allocation; packet buffers are never allocated on the
+ // stack or using the new operator. Instead, the static allocate-byte-array
+ // and destroy methods handle the allocate and overlay pattern. They also
+ // allow for a potential future optimization where instead of just
+ // allocating blocks from the process global heap and overlaying, the
+ // allocator is replaced with a different implementation (private heap,
+ // free-list, circular buffer, etc) which reduces potential heap
+ // fragmentation issues which might arise from the frequent allocation and
+ // destruction of the received UDP traffic.
+ struct PacketBuffer {
+ ssize_t length_;
+ uint8_t data_[1];
+
+ // TODO : consider changing this to be some form of ring buffer or free
+ // pool system instead of just using the heap in order to avoid heap
+ // fragmentation.
+ static PacketBuffer* allocate(ssize_t length);
+ static void destroy(PacketBuffer* pb);
+
+ private:
+ // Force people to use allocate/destroy instead of new/delete.
+ PacketBuffer() { }
+ ~PacketBuffer() { }
+ };
+
+ struct RetransRequest {
+ uint32_t magic_;
+ uint32_t mcast_ip_;
+ uint16_t mcast_port_;
+ uint16_t start_seq_;
+ uint16_t end_seq_;
+ };
+#pragma pack(pop)
+
+ enum GapStatus {
+ kGS_NoGap = 0,
+ kGS_NormalGap,
+ kGS_FastStartGap,
+ };
+
+ struct SeqNoGap {
+ uint16_t start_seq_;
+ uint16_t end_seq_;
+ };
+
+ class RXRingBuffer {
+ public:
+ explicit RXRingBuffer(uint32_t capacity);
+ ~RXRingBuffer();
+
+ bool initCheck() const { return (ring_ != NULL); }
+ void reset();
+
+ // Push a packet buffer with a given sequence number into the ring
+ // buffer. pushBuffer will always consume the buffer pushed to it,
+ // either destroying it because it was a duplicate or overflow, or
+ // holding on to it in the ring. Callers should not hold any references
+ // to PacketBuffers after they have been pushed to the ring. Returns
+ // false in the case of a serious error (such as ring overflow).
+ // Callers should consider resetting the pipeline entirely in the event
+ // of a serious error.
+ bool pushBuffer(PacketBuffer* buf, uint16_t seq);
+
+ // Fetch the next buffer in the RTP sequence. Returns NULL if there is
+ // no buffer to fetch. If a non-NULL PacketBuffer is returned,
+ // is_discon will be set to indicate whether or not this PacketBuffer is
+ // discontiuous with any previously returned packet buffers. Packet
+ // buffers returned by fetchBuffer are the caller's responsibility; they
+ // must be certain to destroy the buffers when they are done.
+ PacketBuffer* fetchBuffer(bool* is_discon);
+
+ // Returns true and fills out the gap structure if the read pointer of
+ // the ring buffer is currently pointing to a gap which would stall a
+ // fetchBuffer operation. Returns false if the read pointer is not
+ // pointing to a gap in the sequence currently.
+ GapStatus fetchCurrentGap(SeqNoGap* gap);
+
+ // Causes the read pointer to skip over any portion of a gap indicated
+ // by nak. If nak is NULL, any gap currently blocking the read pointer
+ // will be completely skipped. If any portion of a gap is skipped, the
+ // next successful read from fetch buffer will indicate a discontinuity.
+ void processNAK(const SeqNoGap* nak = NULL);
+
+ // Compute the number of milliseconds until the inactivity timer for
+ // this RTP stream. Returns -1 if there is no active timeout, or 0 if
+ // the system has already timed out.
+ int computeInactivityTimeout();
+
+ private:
+ Mutex lock_;
+ PacketBuffer** ring_;
+ uint32_t capacity_;
+ uint32_t rd_;
+ uint32_t wr_;
+
+ uint16_t rd_seq_;
+ bool rd_seq_known_;
+ bool waiting_for_fast_start_;
+ bool fetched_first_packet_;
+
+ uint64_t rtp_activity_timeout_;
+ bool rtp_activity_timeout_valid_;
+
+ DISALLOW_EVIL_CONSTRUCTORS(RXRingBuffer);
+ };
+
+ class Substream : public virtual RefBase {
+ public:
+ Substream(uint32_t ssrc, OMXClient& omx);
+
+ void cleanupBufferInProgress();
+ void shutdown();
+ void processPayloadStart(uint8_t* buf,
+ uint32_t amt,
+ int32_t ts_lower);
+ void processPayloadCont (uint8_t* buf,
+ uint32_t amt);
+ void processTSTransform(const LinearTransform& trans);
+
+ bool isAboutToUnderflow();
+ uint32_t getSSRC() const { return ssrc_; }
+ uint16_t getProgramID() const { return (ssrc_ >> 5) & 0x1F; }
+ status_t getStatus() const { return status_; }
+
+ protected:
+ virtual ~Substream();
+
+ private:
+ void cleanupDecoder();
+ bool shouldAbort(const char* log_tag);
+ void processCompletedBuffer();
+ bool setupSubstreamMeta();
+ bool setupMP3SubstreamMeta();
+ bool setupAACSubstreamMeta();
+ bool setupSubstreamType(uint8_t substream_type,
+ uint8_t codec_type);
+
+ uint32_t ssrc_;
+ bool waiting_for_rap_;
+ status_t status_;
+
+ bool substream_details_known_;
+ uint8_t substream_type_;
+ uint8_t codec_type_;
+ const char* codec_mime_type_;
+ sp<MetaData> substream_meta_;
+
+ MediaBuffer* buffer_in_progress_;
+ uint32_t expected_buffer_size_;
+ uint32_t buffer_filled_;
+
+ Vector<uint8_t> aux_data_in_progress_;
+ uint32_t aux_data_expected_size_;
+
+ sp<AAH_DecoderPump> decoder_;
+
+ static int64_t kAboutToUnderflowThreshold;
+
+ DISALLOW_EVIL_CONSTRUCTORS(Substream);
+ };
+
+ typedef DefaultKeyedVector< uint32_t, sp<Substream> > SubstreamVec;
+
+ status_t startWorkThread();
+ void stopWorkThread();
+ virtual bool threadLoop();
+ bool setupSocket();
+ void cleanupSocket();
+ void resetPipeline();
+ void reset_l();
+ bool processRX(PacketBuffer* pb);
+ void processRingBuffer();
+ void processCommandPacket(PacketBuffer* pb);
+ bool processGaps();
+ int computeNextGapRetransmitTimeout();
+ void fetchAudioFlinger();
+
+ PipeEvent wakeup_work_thread_evt_;
+ sp<ThreadWrapper> thread_wrapper_;
+ Mutex api_lock_;
+ bool is_playing_;
+ bool data_source_set_;
+
+ struct sockaddr_in listen_addr_;
+ int sock_fd_;
+ bool multicast_joined_;
+
+ struct sockaddr_in transmitter_addr_;
+ bool transmitter_known_;
+
+ uint32_t current_epoch_;
+ bool current_epoch_known_;
+
+ SeqNoGap current_gap_;
+ GapStatus current_gap_status_;
+ uint64_t next_retrans_req_time_;
+
+ RXRingBuffer ring_buffer_;
+ SubstreamVec substreams_;
+ OMXClient omx_;
+ CCHelper cc_helper_;
+
+ // Connection to audio flinger used to hack a path to setMasterVolume.
+ sp<IAudioFlinger> audio_flinger_;
+
+ static const uint32_t kRTPRingBufferSize;
+ static const uint32_t kRetransRequestMagic;
+ static const uint32_t kFastStartRequestMagic;
+ static const uint32_t kRetransNAKMagic;
+ static const uint32_t kGapRerequestTimeoutUSec;
+ static const uint32_t kFastStartTimeoutUSec;
+ static const uint32_t kRTPActivityTimeoutUSec;
+
+ static const uint32_t INVOKE_GET_MASTER_VOLUME = 3;
+ static const uint32_t INVOKE_SET_MASTER_VOLUME = 4;
+
+ static uint64_t monotonicUSecNow();
+
+ DISALLOW_EVIL_CONSTRUCTORS(AAH_RXPlayer);
+};
+
+} // namespace android
+
+#endif // __AAH_RX_PLAYER_H__
diff --git a/media/libaah_rtp/aah_rx_player_core.cpp b/media/libaah_rtp/aah_rx_player_core.cpp
new file mode 100644
index 0000000..d6b31fd
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player_core.cpp
@@ -0,0 +1,809 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include <fcntl.h>
+#include <poll.h>
+#include <sys/socket.h>
+#include <time.h>
+#include <utils/misc.h>
+
+#include <media/stagefright/Utils.h>
+
+#include "aah_rx_player.h"
+#include "aah_tx_packet.h"
+
+namespace android {
+
+const uint32_t AAH_RXPlayer::kRetransRequestMagic =
+ FOURCC('T','r','e','q');
+const uint32_t AAH_RXPlayer::kRetransNAKMagic =
+ FOURCC('T','n','a','k');
+const uint32_t AAH_RXPlayer::kFastStartRequestMagic =
+ FOURCC('T','f','s','t');
+const uint32_t AAH_RXPlayer::kGapRerequestTimeoutUSec = 75000;
+const uint32_t AAH_RXPlayer::kFastStartTimeoutUSec = 800000;
+const uint32_t AAH_RXPlayer::kRTPActivityTimeoutUSec = 10000000;
+
+static inline int16_t fetchInt16(uint8_t* data) {
+ return static_cast<int16_t>(U16_AT(data));
+}
+
+static inline int32_t fetchInt32(uint8_t* data) {
+ return static_cast<int32_t>(U32_AT(data));
+}
+
+static inline int64_t fetchInt64(uint8_t* data) {
+ return static_cast<int64_t>(U64_AT(data));
+}
+
+uint64_t AAH_RXPlayer::monotonicUSecNow() {
+ struct timespec now;
+ int res = clock_gettime(CLOCK_MONOTONIC, &now);
+ CHECK(res >= 0);
+
+ uint64_t ret = static_cast<uint64_t>(now.tv_sec) * 1000000;
+ ret += now.tv_nsec / 1000;
+
+ return ret;
+}
+
+status_t AAH_RXPlayer::startWorkThread() {
+ status_t res;
+ stopWorkThread();
+ res = thread_wrapper_->run("TRX_Player", PRIORITY_AUDIO);
+
+ if (res != OK) {
+ ALOGE("Failed to start work thread (res = %d)", res);
+ }
+
+ return res;
+}
+
+void AAH_RXPlayer::stopWorkThread() {
+ thread_wrapper_->requestExit(); // set the exit pending flag
+ wakeup_work_thread_evt_.setEvent();
+
+ status_t res;
+ res = thread_wrapper_->requestExitAndWait(); // block until thread exit.
+ if (res != OK) {
+ ALOGE("Failed to stop work thread (res = %d)", res);
+ }
+
+ wakeup_work_thread_evt_.clearPendingEvents();
+}
+
+void AAH_RXPlayer::cleanupSocket() {
+ if (sock_fd_ >= 0) {
+ if (multicast_joined_) {
+ int res;
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr = listen_addr_.sin_addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ res = setsockopt(sock_fd_,
+ IPPROTO_IP,
+ IP_DROP_MEMBERSHIP,
+ &mreq, sizeof(mreq));
+ if (res < 0) {
+ ALOGW("Failed to leave multicast group. (%d, %d)", res, errno);
+ }
+ multicast_joined_ = false;
+ }
+
+ close(sock_fd_);
+ sock_fd_ = -1;
+ }
+
+ resetPipeline();
+}
+
+void AAH_RXPlayer::resetPipeline() {
+ ring_buffer_.reset();
+
+ // Explicitly shudown all of the active substreams, then call clear out the
+ // collection. Failure to clear out a substream can result in its decoder
+ // holding a reference to itself and therefor not going away when the
+ // collection is cleared.
+ for (size_t i = 0; i < substreams_.size(); ++i)
+ substreams_.valueAt(i)->shutdown();
+
+ substreams_.clear();
+
+ current_gap_status_ = kGS_NoGap;
+}
+
+bool AAH_RXPlayer::setupSocket() {
+ long flags;
+ int res, buf_size;
+ socklen_t opt_size;
+
+ cleanupSocket();
+ CHECK(sock_fd_ < 0);
+
+ // Make the socket
+ sock_fd_ = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sock_fd_ < 0) {
+ ALOGE("Failed to create listen socket (errno %d)", errno);
+ goto bailout;
+ }
+
+ // Set non-blocking operation
+ flags = fcntl(sock_fd_, F_GETFL);
+ res = fcntl(sock_fd_, F_SETFL, flags | O_NONBLOCK);
+ if (res < 0) {
+ ALOGE("Failed to set socket (%d) to non-blocking mode (errno %d)",
+ sock_fd_, errno);
+ goto bailout;
+ }
+
+ // Bind to our port
+ struct sockaddr_in bind_addr;
+ memset(&bind_addr, 0, sizeof(bind_addr));
+ bind_addr.sin_family = AF_INET;
+ bind_addr.sin_addr.s_addr = INADDR_ANY;
+ bind_addr.sin_port = listen_addr_.sin_port;
+ res = bind(sock_fd_,
+ reinterpret_cast<const sockaddr*>(&bind_addr),
+ sizeof(bind_addr));
+ if (res < 0) {
+ uint32_t a = ntohl(bind_addr.sin_addr.s_addr);
+ uint16_t p = ntohs(bind_addr.sin_port);
+ ALOGE("Failed to bind socket (%d) to %d.%d.%d.%d:%hd. (errno %d)",
+ sock_fd_,
+ (a >> 24) & 0xFF,
+ (a >> 16) & 0xFF,
+ (a >> 8) & 0xFF,
+ (a ) & 0xFF,
+ p,
+ errno);
+
+ goto bailout;
+ }
+
+ buf_size = 1 << 16; // 64k
+ res = setsockopt(sock_fd_,
+ SOL_SOCKET, SO_RCVBUF,
+ &buf_size, sizeof(buf_size));
+ if (res < 0) {
+ ALOGW("Failed to increase socket buffer size to %d. (errno %d)",
+ buf_size, errno);
+ }
+
+ buf_size = 0;
+ opt_size = sizeof(buf_size);
+ res = getsockopt(sock_fd_,
+ SOL_SOCKET, SO_RCVBUF,
+ &buf_size, &opt_size);
+ if (res < 0) {
+ ALOGW("Failed to fetch socket buffer size. (errno %d)", errno);
+ } else {
+ ALOGI("RX socket buffer size is now %d bytes", buf_size);
+ }
+
+ if (listen_addr_.sin_addr.s_addr) {
+ // Join the multicast group and we should be good to go.
+ struct ip_mreq mreq;
+ mreq.imr_multiaddr = listen_addr_.sin_addr;
+ mreq.imr_interface.s_addr = htonl(INADDR_ANY);
+ res = setsockopt(sock_fd_,
+ IPPROTO_IP,
+ IP_ADD_MEMBERSHIP,
+ &mreq, sizeof(mreq));
+ if (res < 0) {
+ ALOGE("Failed to join multicast group. (errno %d)", errno);
+ goto bailout;
+ }
+ multicast_joined_ = true;
+ }
+
+ return true;
+
+bailout:
+ cleanupSocket();
+ return false;
+}
+
+bool AAH_RXPlayer::threadLoop() {
+ struct pollfd poll_fds[2];
+ bool process_more_right_now = false;
+
+ if (!setupSocket()) {
+ sendEvent(MEDIA_ERROR);
+ goto bailout;
+ }
+
+ while (!thread_wrapper_->exitPending()) {
+ // Step 1: Wait until there is something to do.
+ int gap_timeout = computeNextGapRetransmitTimeout();
+ int ring_timeout = ring_buffer_.computeInactivityTimeout();
+ int timeout = -1;
+
+ if (!ring_timeout) {
+ ALOGW("RTP inactivity timeout reached, resetting pipeline.");
+ resetPipeline();
+ timeout = gap_timeout;
+ } else {
+ if (gap_timeout < 0) {
+ timeout = ring_timeout;
+ } else if (ring_timeout < 0) {
+ timeout = gap_timeout;
+ } else {
+ timeout = (gap_timeout < ring_timeout) ? gap_timeout
+ : ring_timeout;
+ }
+ }
+
+ if ((0 != timeout) && (!process_more_right_now)) {
+ // Set up the events to wait on. Start with the wakeup pipe.
+ memset(&poll_fds, 0, sizeof(poll_fds));
+ poll_fds[0].fd = wakeup_work_thread_evt_.getWakeupHandle();
+ poll_fds[0].events = POLLIN;
+
+ // Add the RX socket.
+ poll_fds[1].fd = sock_fd_;
+ poll_fds[1].events = POLLIN;
+
+ // Wait for something interesing to happen.
+ int poll_res = poll(poll_fds, NELEM(poll_fds), timeout);
+ if (poll_res < 0) {
+ ALOGE("Fatal error (%d,%d) while waiting on events",
+ poll_res, errno);
+ sendEvent(MEDIA_ERROR);
+ goto bailout;
+ }
+ }
+
+ if (thread_wrapper_->exitPending()) {
+ break;
+ }
+
+ wakeup_work_thread_evt_.clearPendingEvents();
+ process_more_right_now = false;
+
+ // Step 2: Do we have data waiting in the socket? If so, drain the
+ // socket moving valid RTP information into the ring buffer to be
+ // processed.
+ if (poll_fds[1].revents) {
+ struct sockaddr_in from;
+ socklen_t from_len;
+
+ ssize_t res = 0;
+ while (!thread_wrapper_->exitPending()) {
+ // Check the size of any pending packet.
+ res = recv(sock_fd_, NULL, 0, MSG_PEEK | MSG_TRUNC);
+
+ // Error?
+ if (res < 0) {
+ // If the error is anything other than would block,
+ // something has gone very wrong.
+ if ((errno != EAGAIN) && (errno != EWOULDBLOCK)) {
+ ALOGE("Fatal socket error during recvfrom (%d, %d)",
+ (int)res, errno);
+ goto bailout;
+ }
+
+ // Socket is out of data, just break out of processing and
+ // wait for more.
+ break;
+ }
+
+ // Allocate a payload.
+ PacketBuffer* pb = PacketBuffer::allocate(res);
+ if (NULL == pb) {
+ ALOGE("Fatal error, failed to allocate packet buffer of"
+ " length %u", static_cast<uint32_t>(res));
+ goto bailout;
+ }
+
+ // Fetch the data.
+ from_len = sizeof(from);
+ res = recvfrom(sock_fd_, pb->data_, pb->length_, 0,
+ reinterpret_cast<struct sockaddr*>(&from),
+ &from_len);
+ if (res != pb->length_) {
+ ALOGE("Fatal error, fetched packet length (%d) does not"
+ " match peeked packet length (%u). This should never"
+ " happen. (errno = %d)",
+ static_cast<int>(res),
+ static_cast<uint32_t>(pb->length_),
+ errno);
+ }
+
+ bool drop_packet = false;
+ if (transmitter_known_) {
+ if (from.sin_addr.s_addr !=
+ transmitter_addr_.sin_addr.s_addr) {
+ uint32_t a = ntohl(from.sin_addr.s_addr);
+ uint16_t p = ntohs(from.sin_port);
+ ALOGV("Dropping packet from unknown transmitter"
+ " %u.%u.%u.%u:%hu",
+ ((a >> 24) & 0xFF),
+ ((a >> 16) & 0xFF),
+ ((a >> 8) & 0xFF),
+ ( a & 0xFF),
+ p);
+
+ drop_packet = true;
+ } else {
+ transmitter_addr_.sin_port = from.sin_port;
+ }
+ } else {
+ memcpy(&transmitter_addr_, &from, sizeof(from));
+ transmitter_known_ = true;
+ }
+
+ if (!drop_packet) {
+ bool serious_error = !processRX(pb);
+
+ if (serious_error) {
+ // Something went "seriously wrong". Currently, the
+ // only trigger for this should be a ring buffer
+ // overflow. The current failsafe behavior for when
+ // something goes seriously wrong is to just reset the
+ // pipeline. The system should behave as if this
+ // AAH_RXPlayer was just set up for the first time.
+ ALOGE("Something just went seriously wrong with the"
+ " pipeline. Resetting.");
+ resetPipeline();
+ }
+ } else {
+ PacketBuffer::destroy(pb);
+ }
+ }
+ }
+
+ // Step 3: Process any data we mave have accumulated in the ring buffer
+ // so far.
+ if (!thread_wrapper_->exitPending()) {
+ processRingBuffer();
+ }
+
+ // Step 4: At this point in time, the ring buffer should either be
+ // empty, or stalled in front of a gap caused by some dropped packets.
+ // Check on the current gap situation and deal with it in an appropriate
+ // fashion. If processGaps returns true, it means that it has given up
+ // on a gap and that we should try to process some more data
+ // immediately.
+ if (!thread_wrapper_->exitPending()) {
+ process_more_right_now = processGaps();
+ }
+
+ // Step 5: Check for fatal errors. If any of our substreams has
+ // encountered a fatal, unrecoverable, error, then propagate the error
+ // up to user level and shut down.
+ for (size_t i = 0; i < substreams_.size(); ++i) {
+ status_t status;
+ CHECK(substreams_.valueAt(i) != NULL);
+
+ status = substreams_.valueAt(i)->getStatus();
+ if (OK != status) {
+ ALOGE("Substream index %d has encountered an unrecoverable"
+ " error (%d). Signalling application level and shutting"
+ " down.", i, status);
+ sendEvent(MEDIA_ERROR);
+ goto bailout;
+ }
+ }
+ }
+
+bailout:
+ cleanupSocket();
+ return false;
+}
+
+bool AAH_RXPlayer::processRX(PacketBuffer* pb) {
+ CHECK(NULL != pb);
+
+ uint8_t* data = pb->data_;
+ ssize_t amt = pb->length_;
+ uint32_t nak_magic;
+ uint16_t seq_no;
+ uint32_t epoch;
+
+ // Every packet either starts with an RTP header which is at least 12 bytes
+ // long or is a retry NAK which is 14 bytes long. If there are fewer than
+ // 12 bytes here, this cannot be a proper RTP packet.
+ if (amt < 12) {
+ ALOGV("Dropping packet, too short to contain RTP header (%u bytes)",
+ static_cast<uint32_t>(amt));
+ goto drop_packet;
+ }
+
+ // Check to see if this is the special case of a NAK packet.
+ nak_magic = ntohl(*(reinterpret_cast<uint32_t*>(data)));
+ if (nak_magic == kRetransNAKMagic) {
+ // Looks like a NAK packet; make sure its long enough.
+
+ if (amt < static_cast<ssize_t>(sizeof(RetransRequest))) {
+ ALOGV("Dropping packet, too short to contain NAK payload"
+ " (%u bytes)", static_cast<uint32_t>(amt));
+ goto drop_packet;
+ }
+
+ SeqNoGap gap;
+ RetransRequest* rtr = reinterpret_cast<RetransRequest*>(data);
+ gap.start_seq_ = ntohs(rtr->start_seq_);
+ gap.end_seq_ = ntohs(rtr->end_seq_);
+
+ ALOGV("Process NAK for gap at [%hu, %hu]",
+ gap.start_seq_, gap.end_seq_);
+ ring_buffer_.processNAK(&gap);
+
+ return true;
+ }
+
+ // According to the TRTP spec, version should be 2, padding should be 0,
+ // extension should be 0 and CSRCCnt should be 0. If any of these tests
+ // fail, we chuck the packet.
+ if (data[0] != 0x80) {
+ ALOGV("Dropping packet, bad V/P/X/CSRCCnt field (0x%02x)",
+ data[0]);
+ goto drop_packet;
+ }
+
+ // Check the payload type. For TRTP, it should always be 100.
+ if ((data[1] & 0x7F) != 100) {
+ ALOGV("Dropping packet, bad payload type. (%u)",
+ data[1] & 0x7F);
+ goto drop_packet;
+ }
+
+ // Check whether the transmitter has begun a new epoch.
+ epoch = (U32_AT(data + 8) >> 10) & 0x3FFFFF;
+ if (current_epoch_known_) {
+ if (epoch != current_epoch_) {
+ ALOGV("%s: new epoch %u", __PRETTY_FUNCTION__, epoch);
+ current_epoch_ = epoch;
+ resetPipeline();
+ }
+ } else {
+ current_epoch_ = epoch;
+ current_epoch_known_ = true;
+ }
+
+ // Extract the sequence number and hand the packet off to the ring buffer
+ // for dropped packet detection and later processing.
+ seq_no = U16_AT(data + 2);
+ return ring_buffer_.pushBuffer(pb, seq_no);
+
+drop_packet:
+ PacketBuffer::destroy(pb);
+ return true;
+}
+
+void AAH_RXPlayer::processRingBuffer() {
+ PacketBuffer* pb;
+ bool is_discon;
+ sp<Substream> substream;
+ LinearTransform trans;
+ bool foundTrans = false;
+
+ while (NULL != (pb = ring_buffer_.fetchBuffer(&is_discon))) {
+ if (is_discon) {
+ // Abort all partially assembled payloads.
+ for (size_t i = 0; i < substreams_.size(); ++i) {
+ CHECK(substreams_.valueAt(i) != NULL);
+ substreams_.valueAt(i)->cleanupBufferInProgress();
+ }
+ }
+
+ uint8_t* data = pb->data_;
+ ssize_t amt = pb->length_;
+
+ // Should not have any non-RTP packets in the ring buffer. RTP packets
+ // must be at least 12 bytes long.
+ CHECK(amt >= 12);
+
+ // Extract the marker bit and the SSRC field.
+ bool marker = (data[1] & 0x80) != 0;
+ uint32_t ssrc = U32_AT(data + 8);
+
+ // Is this the start of a new TRTP payload? If so, the marker bit
+ // should be set and there are some things we should be checking for.
+ if (marker) {
+ // TRTP headers need to have at least a byte for version, a byte for
+ // payload type and flags, and 4 bytes for length.
+ if (amt < 18) {
+ ALOGV("Dropping packet, too short to contain TRTP header"
+ " (%u bytes)", static_cast<uint32_t>(amt));
+ goto process_next_packet;
+ }
+
+ // Check the TRTP version and extract the payload type/flags.
+ uint8_t trtp_version = data[12];
+ uint8_t payload_type = (data[13] >> 4) & 0xF;
+ uint8_t trtp_flags = data[13] & 0xF;
+
+ if (1 != trtp_version) {
+ ALOGV("Dropping packet, bad trtp version %hhu", trtp_version);
+ goto process_next_packet;
+ }
+
+ // Is there a timestamp transformation present on this packet? If
+ // so, extract it and pass it to the appropriate substreams.
+ if (trtp_flags & 0x02) {
+ ssize_t offset = 18 + ((trtp_flags & 0x01) ? 4 : 0);
+ if (amt < (offset + 24)) {
+ ALOGV("Dropping packet, too short to contain TRTP Timestamp"
+ " Transformation (%u bytes)",
+ static_cast<uint32_t>(amt));
+ goto process_next_packet;
+ }
+
+ trans.a_zero = fetchInt64(data + offset);
+ trans.b_zero = fetchInt64(data + offset + 16);
+ trans.a_to_b_numer = static_cast<int32_t>(
+ fetchInt32 (data + offset + 8));
+ trans.a_to_b_denom = U32_AT(data + offset + 12);
+ foundTrans = true;
+
+ uint32_t program_id = (ssrc >> 5) & 0x1F;
+ for (size_t i = 0; i < substreams_.size(); ++i) {
+ sp<Substream> iter = substreams_.valueAt(i);
+ CHECK(iter != NULL);
+
+ if (iter->getProgramID() == program_id) {
+ iter->processTSTransform(trans);
+ }
+ }
+ }
+
+ // Is this a command packet? If so, its not necessarily associate
+ // with one particular substream. Just give it to the command
+ // packet handler and then move on.
+ if (4 == payload_type) {
+ processCommandPacket(pb);
+ goto process_next_packet;
+ }
+ }
+
+ // If we got to here, then we are a normal packet. Find (or allocate)
+ // the substream we belong to and send the packet off to be processed.
+ substream = substreams_.valueFor(ssrc);
+ if (substream == NULL) {
+ substream = new Substream(ssrc, omx_);
+ if (substream == NULL) {
+ ALOGE("Failed to allocate substream for SSRC 0x%08x", ssrc);
+ goto process_next_packet;
+ }
+ substreams_.add(ssrc, substream);
+
+ if (foundTrans) {
+ substream->processTSTransform(trans);
+ }
+ }
+
+ CHECK(substream != NULL);
+
+ if (marker) {
+ // Start of a new TRTP payload for this substream. Extract the
+ // lower 32 bits of the timestamp and hand the buffer to the
+ // substream for processing.
+ uint32_t ts_lower = U32_AT(data + 4);
+ substream->processPayloadStart(data + 12, amt - 12, ts_lower);
+ } else {
+ // Continuation of an existing TRTP payload. Just hand it off to
+ // the substream for processing.
+ substream->processPayloadCont(data + 12, amt - 12);
+ }
+
+process_next_packet:
+ PacketBuffer::destroy(pb);
+ } // end of main processing while loop.
+}
+
+void AAH_RXPlayer::processCommandPacket(PacketBuffer* pb) {
+ CHECK(NULL != pb);
+
+ uint8_t* data = pb->data_;
+ ssize_t amt = pb->length_;
+
+ // verify that this packet meets the minimum length of a command packet
+ if (amt < 20) {
+ return;
+ }
+
+ uint8_t trtp_version = data[12];
+ uint8_t trtp_flags = data[13] & 0xF;
+
+ if (1 != trtp_version) {
+ ALOGV("Dropping packet, bad trtp version %hhu", trtp_version);
+ return;
+ }
+
+ // calculate the start of the command payload
+ ssize_t offset = 18;
+ if (trtp_flags & 0x01) {
+ // timestamp is present (4 bytes)
+ offset += 4;
+ }
+ if (trtp_flags & 0x02) {
+ // transform is present (24 bytes)
+ offset += 24;
+ }
+
+ // the packet must contain 2 bytes of command payload beyond the TRTP header
+ if (amt < offset + 2) {
+ return;
+ }
+
+ uint16_t command_id = U16_AT(data + offset);
+
+ switch (command_id) {
+ case TRTPControlPacket::kCommandNop:
+ break;
+
+ case TRTPControlPacket::kCommandEOS:
+ case TRTPControlPacket::kCommandFlush: {
+ uint16_t program_id = (U32_AT(data + 8) >> 5) & 0x1F;
+ ALOGI("*** %s flushing program_id=%d",
+ __PRETTY_FUNCTION__, program_id);
+
+ Vector<uint32_t> substreams_to_remove;
+ for (size_t i = 0; i < substreams_.size(); ++i) {
+ sp<Substream> iter = substreams_.valueAt(i);
+ if (iter->getProgramID() == program_id) {
+ iter->shutdown();
+ substreams_to_remove.add(iter->getSSRC());
+ }
+ }
+
+ for (size_t i = 0; i < substreams_to_remove.size(); ++i) {
+ substreams_.removeItem(substreams_to_remove[i]);
+ }
+ } break;
+ }
+}
+
+bool AAH_RXPlayer::processGaps() {
+ // Deal with the current gap situation. Specifically...
+ //
+ // 1) If a new gap has shown up, send a retransmit request to the
+ // transmitter.
+ // 2) If a gap we were working on has had a packet in the middle or at
+ // the end filled in, send another retransmit request for the begining
+ // portion of the gap. TRTP was designed for LANs where packet
+ // re-ordering is very unlikely; so see the middle or end of a gap
+ // filled in before the begining is an almost certain indication that
+ // a retransmission packet was also dropped.
+ // 3) If we have been working on a gap for a while and it still has not
+ // been filled in, send another retransmit request.
+ // 4) If the are no more gaps in the ring, clear the current_gap_status_
+ // flag to indicate that all is well again.
+
+ // Start by fetching the active gap status.
+ SeqNoGap gap;
+ bool send_retransmit_request = false;
+ bool ret_val = false;
+ GapStatus gap_status;
+ if (kGS_NoGap != (gap_status = ring_buffer_.fetchCurrentGap(&gap))) {
+ // Note: checking for a change in the end sequence number should cover
+ // moving on to an entirely new gap for case #1 as well as resending the
+ // begining of a gap range for case #2.
+ send_retransmit_request = (kGS_NoGap == current_gap_status_) ||
+ (current_gap_.end_seq_ != gap.end_seq_);
+
+ // If this is the same gap we have been working on, and it has timed
+ // out, then check to see if our substreams are about to underflow. If
+ // so, instead of sending another retransmit request, just give up on
+ // this gap and move on.
+ if (!send_retransmit_request &&
+ (kGS_NoGap != current_gap_status_) &&
+ (0 == computeNextGapRetransmitTimeout())) {
+
+ // If out current gap is the fast-start gap, don't bother to skip it
+ // because substreams look like the are about to underflow.
+ if ((kGS_FastStartGap != gap_status) ||
+ (current_gap_.end_seq_ != gap.end_seq_)) {
+ for (size_t i = 0; i < substreams_.size(); ++i) {
+ if (substreams_.valueAt(i)->isAboutToUnderflow()) {
+ ALOGV("About to underflow, giving up on gap [%hu, %hu]",
+ gap.start_seq_, gap.end_seq_);
+ ring_buffer_.processNAK();
+ current_gap_status_ = kGS_NoGap;
+ return true;
+ }
+ }
+ }
+
+ // Looks like no one is about to underflow. Just go ahead and send
+ // the request.
+ send_retransmit_request = true;
+ }
+ } else {
+ current_gap_status_ = kGS_NoGap;
+ }
+
+ if (send_retransmit_request) {
+ // If we have been working on a fast start, and it is still not filled
+ // in, even after the extended retransmit time out, give up and skip it.
+ // The system should fall back into its normal slow-start behavior.
+ if ((kGS_FastStartGap == current_gap_status_) &&
+ (current_gap_.end_seq_ == gap.end_seq_)) {
+ ALOGV("Fast start is taking forever; giving up.");
+ ring_buffer_.processNAK();
+ current_gap_status_ = kGS_NoGap;
+ return true;
+ }
+
+ // Send the request.
+ RetransRequest req;
+ uint32_t magic = (kGS_FastStartGap == gap_status)
+ ? kFastStartRequestMagic
+ : kRetransRequestMagic;
+ req.magic_ = htonl(magic);
+ req.mcast_ip_ = listen_addr_.sin_addr.s_addr;
+ req.mcast_port_ = listen_addr_.sin_port;
+ req.start_seq_ = htons(gap.start_seq_);
+ req.end_seq_ = htons(gap.end_seq_);
+
+ {
+ uint32_t a = ntohl(transmitter_addr_.sin_addr.s_addr);
+ uint16_t p = ntohs(transmitter_addr_.sin_port);
+ ALOGV("Sending to transmitter %u.%u.%u.%u:%hu",
+ ((a >> 24) & 0xFF),
+ ((a >> 16) & 0xFF),
+ ((a >> 8) & 0xFF),
+ ( a & 0xFF),
+ p);
+ }
+
+ int res = sendto(sock_fd_, &req, sizeof(req), 0,
+ reinterpret_cast<struct sockaddr*>(&transmitter_addr_),
+ sizeof(transmitter_addr_));
+ if (res < 0) {
+ ALOGE("Error when sending retransmit request (%d)", errno);
+ } else {
+ ALOGV("%s request for range [%hu, %hu] sent",
+ (kGS_FastStartGap == gap_status) ? "Fast Start"
+ : "Retransmit",
+ gap.start_seq_, gap.end_seq_);
+ }
+
+ // Update the current gap info.
+ current_gap_ = gap;
+ current_gap_status_ = gap_status;
+ next_retrans_req_time_ = monotonicUSecNow() +
+ ((kGS_FastStartGap == current_gap_status_)
+ ? kFastStartTimeoutUSec
+ : kGapRerequestTimeoutUSec);
+ }
+
+ return false;
+}
+
+// Compute when its time to send the next gap retransmission in milliseconds.
+// Returns < 0 for an infinite timeout (no gap) and 0 if its time to retransmit
+// right now.
+int AAH_RXPlayer::computeNextGapRetransmitTimeout() {
+ if (kGS_NoGap == current_gap_status_) {
+ return -1;
+ }
+
+ int64_t timeout_delta = next_retrans_req_time_ - monotonicUSecNow();
+
+ timeout_delta /= 1000;
+ if (timeout_delta <= 0) {
+ return 0;
+ }
+
+ return static_cast<uint32_t>(timeout_delta);
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_rx_player_ring_buffer.cpp b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
new file mode 100644
index 0000000..779405e
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player_ring_buffer.cpp
@@ -0,0 +1,366 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+#include <utils/Log.h>
+
+#include "aah_rx_player.h"
+
+namespace android {
+
+AAH_RXPlayer::RXRingBuffer::RXRingBuffer(uint32_t capacity) {
+ capacity_ = capacity;
+ rd_ = wr_ = 0;
+ ring_ = new PacketBuffer*[capacity];
+ memset(ring_, 0, sizeof(PacketBuffer*) * capacity);
+ reset();
+}
+
+AAH_RXPlayer::RXRingBuffer::~RXRingBuffer() {
+ reset();
+ delete[] ring_;
+}
+
+void AAH_RXPlayer::RXRingBuffer::reset() {
+ AutoMutex lock(&lock_);
+
+ if (NULL != ring_) {
+ while (rd_ != wr_) {
+ CHECK(rd_ < capacity_);
+ if (NULL != ring_[rd_]) {
+ PacketBuffer::destroy(ring_[rd_]);
+ ring_[rd_] = NULL;
+ }
+ rd_ = (rd_ + 1) % capacity_;
+ }
+ }
+
+ rd_ = wr_ = 0;
+ rd_seq_known_ = false;
+ waiting_for_fast_start_ = true;
+ fetched_first_packet_ = false;
+ rtp_activity_timeout_valid_ = false;
+}
+
+bool AAH_RXPlayer::RXRingBuffer::pushBuffer(PacketBuffer* buf,
+ uint16_t seq) {
+ AutoMutex lock(&lock_);
+ CHECK(NULL != ring_);
+ CHECK(NULL != buf);
+
+ rtp_activity_timeout_valid_ = true;
+ rtp_activity_timeout_ = monotonicUSecNow() + kRTPActivityTimeoutUSec;
+
+ // If the ring buffer is totally reset (we have never received a single
+ // payload) then we don't know the rd sequence number and this should be
+ // simple. We just store the payload, advance the wr pointer and record the
+ // initial sequence number.
+ if (!rd_seq_known_) {
+ CHECK(rd_ == wr_);
+ CHECK(NULL == ring_[wr_]);
+ CHECK(wr_ < capacity_);
+
+ ring_[wr_] = buf;
+ wr_ = (wr_ + 1) % capacity_;
+ rd_seq_ = seq;
+ rd_seq_known_ = true;
+ return true;
+ }
+
+ // Compute the seqence number of this payload and of the write pointer,
+ // normalized around the read pointer. IOW - transform the payload seq no
+ // and the wr pointer seq no into a space where the rd pointer seq no is
+ // zero. This will define 4 cases we can consider...
+ //
+ // 1) norm_seq == norm_wr_seq
+ // This payload is contiguous with the last. All is good.
+ //
+ // 2) ((norm_seq < norm_wr_seq) && (norm_seq >= norm_rd_seq)
+ // aka ((norm_seq < norm_wr_seq) && (norm_seq >= 0)
+ // This payload is in the past, in the unprocessed region of the ring
+ // buffer. It is probably a retransmit intended to fill in a dropped
+ // payload; it may be a duplicate.
+ //
+ // 3) ((norm_seq - norm_wr_seq) & 0x8000) != 0
+ // This payload is in the past compared to the write pointer (or so very
+ // far in the future that it has wrapped the seq no space), but not in
+ // the unprocessed region of the ring buffer. This could be a duplicate
+ // retransmit; we just drop these payloads unless we are waiting for our
+ // first fast start packet. If we are waiting for fast start, than this
+ // packet is probably the first packet of the fast start retransmission.
+ // If it will fit in the buffer, back up the read pointer to its position
+ // and clear the fast start flag, otherwise just drop it.
+ //
+ // 4) ((norm_seq - norm_wr_seq) & 0x8000) == 0
+ // This payload which is ahead of the next write pointer. This indicates
+ // that we have missed some payloads and need to request a retransmit.
+ // If norm_seq >= (capacity - 1), then the gap is so large that it would
+ // overflow the ring buffer and we should probably start to panic.
+
+ uint16_t norm_wr_seq = ((wr_ + capacity_ - rd_) % capacity_);
+ uint16_t norm_seq = seq - rd_seq_;
+
+ // Check for overflow first.
+ if ((!(norm_seq & 0x8000)) && (norm_seq >= (capacity_ - 1))) {
+ ALOGW("Ring buffer overflow; cap = %u, [rd, wr] = [%hu, %hu],"
+ " seq = %hu", capacity_, rd_seq_, norm_wr_seq + rd_seq_, seq);
+ PacketBuffer::destroy(buf);
+ return false;
+ }
+
+ // Check for case #1
+ if (norm_seq == norm_wr_seq) {
+ CHECK(wr_ < capacity_);
+ CHECK(NULL == ring_[wr_]);
+
+ ring_[wr_] = buf;
+ wr_ = (wr_ + 1) % capacity_;
+
+ CHECK(wr_ != rd_);
+ return true;
+ }
+
+ // Check case #2
+ uint32_t ring_pos = (rd_ + norm_seq) % capacity_;
+ if ((norm_seq < norm_wr_seq) && (!(norm_seq & 0x8000))) {
+ // Do we already have a payload for this slot? If so, then this looks
+ // like a duplicate retransmit. Just ignore it.
+ if (NULL != ring_[ring_pos]) {
+ ALOGD("RXed duplicate retransmit, seq = %hu", seq);
+ PacketBuffer::destroy(buf);
+ } else {
+ // Looks like we were missing this payload. Go ahead and store it.
+ ring_[ring_pos] = buf;
+ }
+
+ return true;
+ }
+
+ // Check case #3
+ if ((norm_seq - norm_wr_seq) & 0x8000) {
+ if (!waiting_for_fast_start_) {
+ ALOGD("RXed duplicate retransmit from before rd pointer, seq = %hu",
+ seq);
+ PacketBuffer::destroy(buf);
+ } else {
+ // Looks like a fast start fill-in. Go ahead and store it, assuming
+ // that we can fit it in the buffer.
+ uint32_t implied_ring_size = static_cast<uint32_t>(norm_wr_seq)
+ + (rd_seq_ - seq);
+
+ if (implied_ring_size >= (capacity_ - 1)) {
+ ALOGD("RXed what looks like a fast start packet (seq = %hu),"
+ " but packet is too far in the past to fit into the ring"
+ " buffer. Dropping.", seq);
+ PacketBuffer::destroy(buf);
+ } else {
+ ring_pos = (rd_ + capacity_ + seq - rd_seq_) % capacity_;
+ rd_seq_ = seq;
+ rd_ = ring_pos;
+ waiting_for_fast_start_ = false;
+
+ CHECK(ring_pos < capacity_);
+ CHECK(NULL == ring_[ring_pos]);
+ ring_[ring_pos] = buf;
+ }
+
+ }
+ return true;
+ }
+
+ // Must be in case #4 with no overflow. This packet fits in the current
+ // ring buffer, but is discontiuguous. Advance the write pointer leaving a
+ // gap behind.
+ uint32_t gap_len = (ring_pos + capacity_ - wr_) % capacity_;
+ ALOGD("Drop detected; %u packets, seq_range [%hu, %hu]",
+ gap_len,
+ rd_seq_ + norm_wr_seq,
+ rd_seq_ + norm_wr_seq + gap_len - 1);
+
+ CHECK(NULL == ring_[ring_pos]);
+ ring_[ring_pos] = buf;
+ wr_ = (ring_pos + 1) % capacity_;
+ CHECK(wr_ != rd_);
+
+ return true;
+}
+
+AAH_RXPlayer::PacketBuffer*
+AAH_RXPlayer::RXRingBuffer::fetchBuffer(bool* is_discon) {
+ AutoMutex lock(&lock_);
+ CHECK(NULL != ring_);
+ CHECK(NULL != is_discon);
+
+ // If the read seqence number is not known, then this ring buffer has not
+ // received a packet since being reset and there cannot be any packets to
+ // return. If we are still waiting for the first fast start packet to show
+ // up, we don't want to let any buffer be consumed yet because we expect to
+ // see a packet before the initial read sequence number show up shortly.
+ if (!rd_seq_known_ || waiting_for_fast_start_) {
+ *is_discon = false;
+ return NULL;
+ }
+
+ PacketBuffer* ret = NULL;
+ *is_discon = !fetched_first_packet_;
+
+ while ((rd_ != wr_) && (NULL == ret)) {
+ CHECK(rd_ < capacity_);
+
+ // If we hit a gap, stall and do not advance the read pointer. Let the
+ // higher level code deal with requesting retries and/or deciding to
+ // skip the current gap.
+ ret = ring_[rd_];
+ if (NULL == ret) {
+ break;
+ }
+
+ ring_[rd_] = NULL;
+ rd_ = (rd_ + 1) % capacity_;
+ ++rd_seq_;
+ }
+
+ if (NULL != ret) {
+ fetched_first_packet_ = true;
+ }
+
+ return ret;
+}
+
+AAH_RXPlayer::GapStatus
+AAH_RXPlayer::RXRingBuffer::fetchCurrentGap(SeqNoGap* gap) {
+ AutoMutex lock(&lock_);
+ CHECK(NULL != ring_);
+ CHECK(NULL != gap);
+
+ // If the read seqence number is not known, then this ring buffer has not
+ // received a packet since being reset and there cannot be any gaps.
+ if (!rd_seq_known_) {
+ return kGS_NoGap;
+ }
+
+ // If we are waiting for fast start, then the current gap is a fast start
+ // gap and it includes all packets before the read sequence number.
+ if (waiting_for_fast_start_) {
+ gap->start_seq_ =
+ gap->end_seq_ = rd_seq_ - 1;
+ return kGS_FastStartGap;
+ }
+
+ // If rd == wr, then the buffer is empty and there cannot be any gaps.
+ if (rd_ == wr_) {
+ return kGS_NoGap;
+ }
+
+ // If rd_ is currently pointing at an unprocessed packet, then there is no
+ // current gap.
+ CHECK(rd_ < capacity_);
+ if (NULL != ring_[rd_]) {
+ return kGS_NoGap;
+ }
+
+ // Looks like there must be a gap here. The start of the gap is the current
+ // rd sequence number, all we need to do now is determine its length in
+ // order to compute the end sequence number.
+ gap->start_seq_ = rd_seq_;
+ uint16_t end = rd_seq_;
+ uint32_t tmp = (rd_ + 1) % capacity_;
+ while ((tmp != wr_) && (NULL == ring_[tmp])) {
+ ++end;
+ tmp = (tmp + 1) % capacity_;
+ }
+ gap->end_seq_ = end;
+
+ return kGS_NormalGap;
+}
+
+void AAH_RXPlayer::RXRingBuffer::processNAK(const SeqNoGap* nak) {
+ AutoMutex lock(&lock_);
+ CHECK(NULL != ring_);
+
+ // If we were waiting for our first fast start fill-in packet, and we
+ // received a NAK, then apparantly we are not getting our fast start. Just
+ // clear the waiting flag and go back to normal behavior.
+ if (waiting_for_fast_start_) {
+ waiting_for_fast_start_ = false;
+ }
+
+ // If we have not received a packet since last reset, or there is no data in
+ // the ring, then there is nothing to skip.
+ if ((!rd_seq_known_) || (rd_ == wr_)) {
+ return;
+ }
+
+ // If rd_ is currently pointing at an unprocessed packet, then there is no
+ // gap to skip.
+ CHECK(rd_ < capacity_);
+ if (NULL != ring_[rd_]) {
+ return;
+ }
+
+ // Looks like there must be a gap here. Advance rd until we have passed
+ // over the portion of it indicated by nak (or all of the gap if nak is
+ // NULL). Then reset fetched_first_packet_ so that the next read will show
+ // up as being discontiguous.
+ uint16_t seq_after_gap = (NULL == nak) ? 0 : nak->end_seq_ + 1;
+ while ((rd_ != wr_) &&
+ (NULL == ring_[rd_]) &&
+ ((NULL == nak) || (seq_after_gap != rd_seq_))) {
+ rd_ = (rd_ + 1) % capacity_;
+ ++rd_seq_;
+ }
+ fetched_first_packet_ = false;
+}
+
+int AAH_RXPlayer::RXRingBuffer::computeInactivityTimeout() {
+ AutoMutex lock(&lock_);
+
+ if (!rtp_activity_timeout_valid_) {
+ return -1;
+ }
+
+ uint64_t now = monotonicUSecNow();
+ if (rtp_activity_timeout_ <= now) {
+ return 0;
+ }
+
+ return (rtp_activity_timeout_ - now) / 1000;
+}
+
+AAH_RXPlayer::PacketBuffer*
+AAH_RXPlayer::PacketBuffer::allocate(ssize_t length) {
+ if (length <= 0) {
+ return NULL;
+ }
+
+ uint32_t alloc_len = sizeof(PacketBuffer) + length;
+ PacketBuffer* ret = reinterpret_cast<PacketBuffer*>(
+ new uint8_t[alloc_len]);
+
+ if (NULL != ret) {
+ ret->length_ = length;
+ }
+
+ return ret;
+}
+
+void AAH_RXPlayer::PacketBuffer::destroy(PacketBuffer* pb) {
+ uint8_t* kill_me = reinterpret_cast<uint8_t*>(pb);
+ delete[] kill_me;
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_rx_player_substream.cpp b/media/libaah_rtp/aah_rx_player_substream.cpp
new file mode 100644
index 0000000..18b0e2b
--- /dev/null
+++ b/media/libaah_rtp/aah_rx_player_substream.cpp
@@ -0,0 +1,677 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+//#define LOG_NDEBUG 0
+
+#include <utils/Log.h>
+
+#include <include/avc_utils.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/OMXCodec.h>
+#include <media/stagefright/Utils.h>
+
+#include "aah_rx_player.h"
+#include "aah_tx_packet.h"
+
+inline uint32_t min(uint32_t a, uint32_t b) {
+ return (a < b ? a : b);
+}
+
+namespace android {
+
+int64_t AAH_RXPlayer::Substream::kAboutToUnderflowThreshold =
+ 50ull * 1000;
+
+AAH_RXPlayer::Substream::Substream(uint32_t ssrc, OMXClient& omx) {
+ ssrc_ = ssrc;
+ substream_details_known_ = false;
+ buffer_in_progress_ = NULL;
+ status_ = OK;
+ codec_mime_type_ = "";
+
+ decoder_ = new AAH_DecoderPump(omx);
+ if (decoder_ == NULL) {
+ ALOGE("%s failed to allocate decoder pump!", __PRETTY_FUNCTION__);
+ }
+ if (OK != decoder_->initCheck()) {
+ ALOGE("%s failed to initialize decoder pump!", __PRETTY_FUNCTION__);
+ }
+
+ // cleanupBufferInProgress will reset most of the internal state variables.
+ // Just need to make sure that buffer_in_progress_ is NULL before calling.
+ cleanupBufferInProgress();
+}
+
+AAH_RXPlayer::Substream::~Substream() {
+ shutdown();
+}
+
+void AAH_RXPlayer::Substream::shutdown() {
+ substream_meta_ = NULL;
+ status_ = OK;
+ cleanupBufferInProgress();
+ cleanupDecoder();
+}
+
+void AAH_RXPlayer::Substream::cleanupBufferInProgress() {
+ if (NULL != buffer_in_progress_) {
+ buffer_in_progress_->release();
+ buffer_in_progress_ = NULL;
+ }
+
+ expected_buffer_size_ = 0;
+ buffer_filled_ = 0;
+ waiting_for_rap_ = true;
+
+ aux_data_in_progress_.clear();
+ aux_data_expected_size_ = 0;
+}
+
+void AAH_RXPlayer::Substream::cleanupDecoder() {
+ if (decoder_ != NULL) {
+ decoder_->shutdown();
+ }
+}
+
+bool AAH_RXPlayer::Substream::shouldAbort(const char* log_tag) {
+ // If we have already encountered a fatal error, do nothing. We are just
+ // waiting for our owner to shut us down now.
+ if (OK != status_) {
+ ALOGV("Skipping %s, substream has encountered fatal error (%d).",
+ log_tag, status_);
+ return true;
+ }
+
+ return false;
+}
+
+void AAH_RXPlayer::Substream::processPayloadStart(uint8_t* buf,
+ uint32_t amt,
+ int32_t ts_lower) {
+ uint32_t min_length = 6;
+
+ if (shouldAbort(__PRETTY_FUNCTION__)) {
+ return;
+ }
+
+ // Do we have a buffer in progress already? If so, abort the buffer. In
+ // theory, this should never happen. If there were a discontinutity in the
+ // stream, the discon in the seq_nos at the RTP level should have already
+ // triggered a cleanup of the buffer in progress. To see a problem at this
+ // level is an indication either of a bug in the transmitter, or some form
+ // of terrible corruption/tampering on the wire.
+ if (NULL != buffer_in_progress_) {
+ ALOGE("processPayloadStart is aborting payload already in progress.");
+ cleanupBufferInProgress();
+ }
+
+ // Parse enough of the header to know where we stand. Since this is a
+ // payload start, it should begin with a TRTP header which has to be at
+ // least 6 bytes long.
+ if (amt < min_length) {
+ ALOGV("Discarding payload too short to contain TRTP header (len = %u)",
+ amt);
+ return;
+ }
+
+ // Check the TRTP version number.
+ if (0x01 != buf[0]) {
+ ALOGV("Unexpected TRTP version (%u) in header. Expected %u.",
+ buf[0], 1);
+ return;
+ }
+
+ // Extract the substream type field and make sure its one we understand (and
+ // one that does not conflict with any previously received substream type.
+ uint8_t header_type = (buf[1] >> 4) & 0xF;
+ switch (header_type) {
+ case TRTPPacket::kHeaderTypeAudio:
+ // Audio, yay! Just break. We understand audio payloads.
+ break;
+ case TRTPPacket::kHeaderTypeVideo:
+ ALOGV("RXed packet with unhandled TRTP header type (Video).");
+ return;
+ case TRTPPacket::kHeaderTypeSubpicture:
+ ALOGV("RXed packet with unhandled TRTP header type (Subpicture).");
+ return;
+ case TRTPPacket::kHeaderTypeControl:
+ ALOGV("RXed packet with unhandled TRTP header type (Control).");
+ return;
+ default:
+ ALOGV("RXed packet with unhandled TRTP header type (%u).",
+ header_type);
+ return;
+ }
+
+ if (substream_details_known_ && (header_type != substream_type_)) {
+ ALOGV("RXed TRTP Payload for SSRC=0x%08x where header type (%u) does"
+ " not match previously received header type (%u)",
+ ssrc_, header_type, substream_type_);
+ return;
+ }
+
+ // Check the flags to see if there is another 32 bits of timestamp present.
+ uint32_t trtp_header_len = 6;
+ bool ts_valid = buf[1] & TRTPPacket::kFlag_TSValid;
+ if (ts_valid) {
+ min_length += 4;
+ trtp_header_len += 4;
+ if (amt < min_length) {
+ ALOGV("Discarding payload too short to contain TRTP timestamp"
+ " (len = %u)", amt);
+ return;
+ }
+ }
+
+ // Extract the TRTP length field and sanity check it.
+ uint32_t trtp_len = U32_AT(buf + 2);
+ if (trtp_len < min_length) {
+ ALOGV("TRTP length (%u) is too short to be valid. Must be at least %u"
+ " bytes.", trtp_len, min_length);
+ return;
+ }
+
+ // Extract the rest of the timestamp field if valid.
+ int64_t ts = 0;
+ uint32_t parse_offset = 6;
+ if (ts_valid) {
+ uint32_t ts_upper = U32_AT(buf + parse_offset);
+ parse_offset += 4;
+ ts = (static_cast<int64_t>(ts_upper) << 32) | ts_lower;
+ }
+
+ // Check the flags to see if there is another 24 bytes of timestamp
+ // transformation present.
+ if (buf[1] & TRTPPacket::kFlag_TSTransformPresent) {
+ min_length += 24;
+ parse_offset += 24;
+ trtp_header_len += 24;
+ if (amt < min_length) {
+ ALOGV("Discarding payload too short to contain TRTP timestamp"
+ " transformation (len = %u)", amt);
+ return;
+ }
+ }
+
+ // TODO : break the parsing into individual parsers for the different
+ // payload types (audio, video, etc).
+ //
+ // At this point in time, we know that this is audio. Go ahead and parse
+ // the basic header, check the codec type, and find the payload portion of
+ // the packet.
+ min_length += 3;
+ if (trtp_len < min_length) {
+ ALOGV("TRTP length (%u) is too short to be a valid audio payload. Must"
+ " be at least %u bytes.", trtp_len, min_length);
+ return;
+ }
+
+ if (amt < min_length) {
+ ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain"
+ " entire TRTP header. TRTP does not currently support"
+ " fragmenting TRTP headers across RTP payloads", amt);
+ return;
+ }
+
+ uint8_t codec_type = buf[parse_offset ];
+ uint8_t flags = buf[parse_offset + 1];
+ uint8_t volume = buf[parse_offset + 2];
+ parse_offset += 3;
+ trtp_header_len += 3;
+
+ if (!setupSubstreamType(header_type, codec_type)) {
+ return;
+ }
+
+ if (decoder_ != NULL) {
+ decoder_->setRenderVolume(volume);
+ }
+
+ if (waiting_for_rap_ && !(flags & TRTPAudioPacket::kFlag_RandomAccessPoint)) {
+ ALOGV("Dropping non-RAP TRTP Audio Payload while waiting for RAP.");
+ return;
+ }
+
+ // Check for the presence of codec aux data.
+ if (flags & TRTPAudioPacket::kFlag_AuxLengthPresent) {
+ min_length += 4;
+ trtp_header_len += 4;
+
+ if (trtp_len < min_length) {
+ ALOGV("TRTP length (%u) is too short to be a valid audio payload. "
+ "Must be at least %u bytes.", trtp_len, min_length);
+ return;
+ }
+
+ if (amt < min_length) {
+ ALOGV("TRTP porttion of RTP payload (%u bytes) too small to contain"
+ " entire TRTP header. TRTP does not currently support"
+ " fragmenting TRTP headers across RTP payloads", amt);
+ return;
+ }
+
+ aux_data_expected_size_ = U32_AT(buf + parse_offset);
+ aux_data_in_progress_.clear();
+ if (aux_data_in_progress_.capacity() < aux_data_expected_size_) {
+ aux_data_in_progress_.setCapacity(aux_data_expected_size_);
+ }
+ } else {
+ aux_data_expected_size_ = 0;
+ }
+
+ if ((aux_data_expected_size_ + trtp_header_len) > trtp_len) {
+ ALOGV("Expected codec aux data length (%u) and TRTP header overhead"
+ " (%u) too large for total TRTP payload length (%u).",
+ aux_data_expected_size_, trtp_header_len, trtp_len);
+ return;
+ }
+
+ // OK - everything left is just payload. Compute the payload size, start
+ // the buffer in progress and pack as much payload as we can into it. If
+ // the payload is finished once we are done, go ahead and send the payload
+ // to the decoder.
+ expected_buffer_size_ = trtp_len
+ - trtp_header_len
+ - aux_data_expected_size_;
+ if (!expected_buffer_size_) {
+ ALOGV("Dropping TRTP Audio Payload with 0 Access Unit length");
+ return;
+ }
+
+ CHECK(amt >= trtp_header_len);
+ uint32_t todo = amt - trtp_header_len;
+ if ((expected_buffer_size_ + aux_data_expected_size_) < todo) {
+ ALOGV("Extra data (%u > %u) present in initial TRTP Audio Payload;"
+ " dropping payload.", todo,
+ expected_buffer_size_ + aux_data_expected_size_);
+ return;
+ }
+
+ buffer_filled_ = 0;
+ buffer_in_progress_ = new MediaBuffer(expected_buffer_size_);
+ if ((NULL == buffer_in_progress_) ||
+ (NULL == buffer_in_progress_->data())) {
+ ALOGV("Failed to allocate MediaBuffer of length %u",
+ expected_buffer_size_);
+ cleanupBufferInProgress();
+ return;
+ }
+
+ sp<MetaData> meta = buffer_in_progress_->meta_data();
+ if (meta == NULL) {
+ ALOGV("Missing metadata structure in allocated MediaBuffer; dropping"
+ " payload");
+ cleanupBufferInProgress();
+ return;
+ }
+
+ meta->setCString(kKeyMIMEType, codec_mime_type_);
+ if (ts_valid) {
+ meta->setInt64(kKeyTime, ts);
+ }
+
+ // Skip over the header we have already extracted.
+ amt -= trtp_header_len;
+ buf += trtp_header_len;
+
+ // Extract as much of the expected aux data as we can.
+ todo = min(aux_data_expected_size_, amt);
+ if (todo) {
+ aux_data_in_progress_.appendArray(buf, todo);
+ buf += todo;
+ amt -= todo;
+ }
+
+ // Extract as much of the expected payload as we can.
+ todo = min(expected_buffer_size_, amt);
+ if (todo > 0) {
+ uint8_t* tgt =
+ reinterpret_cast<uint8_t*>(buffer_in_progress_->data());
+ memcpy(tgt, buf, todo);
+ buffer_filled_ = amt;
+ buf += todo;
+ amt -= todo;
+ }
+
+ if (buffer_filled_ >= expected_buffer_size_) {
+ processCompletedBuffer();
+ }
+}
+
+void AAH_RXPlayer::Substream::processPayloadCont(uint8_t* buf,
+ uint32_t amt) {
+ if (shouldAbort(__PRETTY_FUNCTION__)) {
+ return;
+ }
+
+ if (NULL == buffer_in_progress_) {
+ ALOGV("TRTP Receiver skipping payload continuation; no buffer currently"
+ " in progress.");
+ return;
+ }
+
+ CHECK(aux_data_in_progress_.size() <= aux_data_expected_size_);
+ uint32_t aux_left = aux_data_expected_size_ - aux_data_in_progress_.size();
+ if (aux_left) {
+ uint32_t todo = min(aux_left, amt);
+ aux_data_in_progress_.appendArray(buf, todo);
+ amt -= todo;
+ buf += todo;
+
+ if (!amt)
+ return;
+ }
+
+ CHECK(buffer_filled_ < expected_buffer_size_);
+ uint32_t buffer_left = expected_buffer_size_ - buffer_filled_;
+ if (amt > buffer_left) {
+ ALOGV("Extra data (%u > %u) present in continued TRTP Audio Payload;"
+ " dropping payload.", amt, buffer_left);
+ cleanupBufferInProgress();
+ return;
+ }
+
+ if (amt > 0) {
+ uint8_t* tgt =
+ reinterpret_cast<uint8_t*>(buffer_in_progress_->data());
+ memcpy(tgt + buffer_filled_, buf, amt);
+ buffer_filled_ += amt;
+ }
+
+ if (buffer_filled_ >= expected_buffer_size_) {
+ processCompletedBuffer();
+ }
+}
+
+void AAH_RXPlayer::Substream::processCompletedBuffer() {
+ status_t res;
+
+ CHECK(NULL != buffer_in_progress_);
+
+ if (decoder_ == NULL) {
+ ALOGV("Dropping complete buffer, no decoder pump allocated");
+ goto bailout;
+ }
+
+ // Make sure our metadata used to initialize the decoder has been properly
+ // set up.
+ if (!setupSubstreamMeta())
+ goto bailout;
+
+ // If our decoder has not be set up, do so now.
+ res = decoder_->init(substream_meta_);
+ if (OK != res) {
+ ALOGE("Failed to init decoder (res = %d)", res);
+ cleanupDecoder();
+ substream_meta_ = NULL;
+ goto bailout;
+ }
+
+ // Queue the payload for decode.
+ res = decoder_->queueForDecode(buffer_in_progress_);
+
+ if (res != OK) {
+ ALOGD("Failed to queue payload for decode, resetting decoder pump!"
+ " (res = %d)", res);
+ status_ = res;
+ cleanupDecoder();
+ cleanupBufferInProgress();
+ }
+
+ // NULL out buffer_in_progress before calling the cleanup helper.
+ //
+ // MediaBuffers use something of a hybrid ref-counting pattern which prevent
+ // the AAH_DecoderPump's input queue from adding their own reference to the
+ // MediaBuffer. MediaBuffers start life with a reference count of 0, as
+ // well as an observer which starts as NULL. Before being given an
+ // observer, the ref count cannot be allowed to become non-zero as it will
+ // cause calls to release() to assert. Basically, before a MediaBuffer has
+ // an observer, they behave like non-ref counted obects where release()
+ // serves the roll of delete. After a MediaBuffer has an observer, they
+ // become more like ref counted objects where add ref and release can be
+ // used, and when the ref count hits zero, the MediaBuffer is handed off to
+ // the observer.
+ //
+ // Given all of this, when we give the buffer to the decoder pump to wait in
+ // the to-be-processed queue, the decoder cannot add a ref to the buffer as
+ // it would in a traditional ref counting system. Instead it needs to
+ // "steal" the non-existent ref. In the case of queue failure, we need to
+ // make certain to release this non-existent reference so that the buffer is
+ // cleaned up during the cleanupBufferInProgress helper. In the case of a
+ // successful queue operation, we need to make certain that the
+ // cleanupBufferInProgress helper does not release the buffer since it needs
+ // to remain alive in the queue. We acomplish this by NULLing out the
+ // buffer pointer before calling the cleanup helper.
+ buffer_in_progress_ = NULL;
+
+bailout:
+ cleanupBufferInProgress();
+}
+
+bool AAH_RXPlayer::Substream::setupSubstreamMeta() {
+ switch (codec_type_) {
+ case TRTPAudioPacket::kCodecMPEG1Audio:
+ codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_MPEG;
+ return setupMP3SubstreamMeta();
+
+ case TRTPAudioPacket::kCodecAACAudio:
+ codec_mime_type_ = MEDIA_MIMETYPE_AUDIO_AAC;
+ return setupAACSubstreamMeta();
+
+ default:
+ ALOGV("Failed to setup substream metadata for unsupported codec"
+ " type (%u)", codec_type_);
+ break;
+ }
+
+ return false;
+}
+
+bool AAH_RXPlayer::Substream::setupMP3SubstreamMeta() {
+ const uint8_t* buffer_data = NULL;
+ int sample_rate;
+ int channel_count;
+ size_t frame_size;
+ status_t res;
+
+ buffer_data = reinterpret_cast<const uint8_t*>(buffer_in_progress_->data());
+ if (buffer_in_progress_->size() < 4) {
+ ALOGV("MP3 payload too short to contain header, dropping payload.");
+ return false;
+ }
+
+ // Extract the channel count and the sample rate from the MP3 header. The
+ // stagefright MP3 requires that these be delivered before decoing can
+ // begin.
+ if (!GetMPEGAudioFrameSize(U32_AT(buffer_data),
+ &frame_size,
+ &sample_rate,
+ &channel_count,
+ NULL,
+ NULL)) {
+ ALOGV("Failed to parse MP3 header in payload, droping payload.");
+ return false;
+ }
+
+
+ // Make sure that our substream metadata is set up properly. If there has
+ // been a format change, be sure to reset the underlying decoder. In
+ // stagefright, it seems like the only way to do this is to destroy and
+ // recreate the decoder.
+ if (substream_meta_ == NULL) {
+ substream_meta_ = new MetaData();
+
+ if (substream_meta_ == NULL) {
+ ALOGE("Failed to allocate MetaData structure for MP3 substream");
+ return false;
+ }
+
+ substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_MPEG);
+ substream_meta_->setInt32 (kKeyChannelCount, channel_count);
+ substream_meta_->setInt32 (kKeySampleRate, sample_rate);
+ } else {
+ int32_t prev_sample_rate;
+ int32_t prev_channel_count;
+ substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate);
+ substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
+
+ if ((prev_channel_count != channel_count) ||
+ (prev_sample_rate != sample_rate)) {
+ ALOGW("MP3 format change detected, forcing decoder reset.");
+ cleanupDecoder();
+
+ substream_meta_->setInt32(kKeyChannelCount, channel_count);
+ substream_meta_->setInt32(kKeySampleRate, sample_rate);
+ }
+ }
+
+ return true;
+}
+
+bool AAH_RXPlayer::Substream::setupAACSubstreamMeta() {
+ int32_t sample_rate, channel_cnt;
+ static const size_t overhead = sizeof(sample_rate)
+ + sizeof(channel_cnt);
+
+ if (aux_data_in_progress_.size() < overhead) {
+ ALOGE("Not enough aux data (%u) to initialize AAC substream decoder",
+ aux_data_in_progress_.size());
+ return false;
+ }
+
+ const uint8_t* aux_data = aux_data_in_progress_.array();
+ size_t aux_data_size = aux_data_in_progress_.size();
+ sample_rate = U32_AT(aux_data);
+ channel_cnt = U32_AT(aux_data + sizeof(sample_rate));
+
+ const uint8_t* esds_data = NULL;
+ size_t esds_data_size = 0;
+ if (aux_data_size > overhead) {
+ esds_data = aux_data + overhead;
+ esds_data_size = aux_data_size - overhead;
+ }
+
+ // Do we already have metadata? If so, has it changed at all? If not, then
+ // there should be nothing else to do. Otherwise, release our old stream
+ // metadata and make new metadata.
+ if (substream_meta_ != NULL) {
+ uint32_t type;
+ const void* data;
+ size_t size;
+ int32_t prev_sample_rate;
+ int32_t prev_channel_count;
+ bool res;
+
+ res = substream_meta_->findInt32(kKeySampleRate, &prev_sample_rate);
+ CHECK(res);
+ res = substream_meta_->findInt32(kKeyChannelCount, &prev_channel_count);
+ CHECK(res);
+
+ // If nothing has changed about the codec aux data (esds, sample rate,
+ // channel count), then we can just do nothing and get out. Otherwise,
+ // we will need to reset the decoder and make a new metadata object to
+ // deal with the format change.
+ bool hasData = (esds_data != NULL);
+ bool hadData = substream_meta_->findData(kKeyESDS, &type, &data, &size);
+ bool esds_change = (hadData != hasData);
+
+ if (!esds_change && hasData)
+ esds_change = ((size != esds_data_size) ||
+ memcmp(data, esds_data, size));
+
+ if (!esds_change &&
+ (prev_sample_rate == sample_rate) &&
+ (prev_channel_count == channel_cnt)) {
+ return true; // no change, just get out.
+ }
+
+ ALOGW("AAC format change detected, forcing decoder reset.");
+ cleanupDecoder();
+ substream_meta_ = NULL;
+ }
+
+ CHECK(substream_meta_ == NULL);
+
+ substream_meta_ = new MetaData();
+ if (substream_meta_ == NULL) {
+ ALOGE("Failed to allocate MetaData structure for AAC substream");
+ return false;
+ }
+
+ substream_meta_->setCString(kKeyMIMEType, MEDIA_MIMETYPE_AUDIO_AAC);
+ substream_meta_->setInt32 (kKeySampleRate, sample_rate);
+ substream_meta_->setInt32 (kKeyChannelCount, channel_cnt);
+
+ if (esds_data) {
+ substream_meta_->setData(kKeyESDS, kTypeESDS,
+ esds_data, esds_data_size);
+ }
+
+ return true;
+}
+
+void AAH_RXPlayer::Substream::processTSTransform(const LinearTransform& trans) {
+ if (decoder_ != NULL) {
+ decoder_->setRenderTSTransform(trans);
+ }
+}
+
+bool AAH_RXPlayer::Substream::isAboutToUnderflow() {
+ if (decoder_ == NULL) {
+ return false;
+ }
+
+ return decoder_->isAboutToUnderflow(kAboutToUnderflowThreshold);
+}
+
+bool AAH_RXPlayer::Substream::setupSubstreamType(uint8_t substream_type,
+ uint8_t codec_type) {
+ // Sanity check the codec type. Right now we only support MP3 and AAC.
+ // Also check for conflicts with previously delivered codec types.
+ if (substream_details_known_) {
+ if (codec_type != codec_type_) {
+ ALOGV("RXed TRTP Payload for SSRC=0x%08x where codec type (%u) does"
+ " not match previously received codec type (%u)",
+ ssrc_, codec_type, codec_type_);
+ return false;
+ }
+
+ return true;
+ }
+
+ switch (codec_type) {
+ // MP3 and AAC are all we support right now.
+ case TRTPAudioPacket::kCodecMPEG1Audio:
+ case TRTPAudioPacket::kCodecAACAudio:
+ break;
+
+ default:
+ ALOGV("RXed TRTP Audio Payload for SSRC=0x%08x with unsupported"
+ " codec type (%u)", ssrc_, codec_type);
+ return false;
+ }
+
+ substream_type_ = substream_type;
+ codec_type_ = codec_type;
+ substream_details_known_ = true;
+
+ return true;
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_tx_packet.cpp b/media/libaah_rtp/aah_tx_packet.cpp
new file mode 100644
index 0000000..4cd6e47
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_packet.cpp
@@ -0,0 +1,344 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <utils/Log.h>
+
+#include <arpa/inet.h>
+#include <string.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+
+#include "aah_tx_packet.h"
+
+namespace android {
+
+const int TRTPPacket::kRTPHeaderLen;
+const uint32_t TRTPPacket::kTRTPEpochMask;
+
+TRTPPacket::~TRTPPacket() {
+ delete mPacket;
+}
+
+/*** TRTP packet properties ***/
+
+void TRTPPacket::setSeqNumber(uint16_t val) {
+ mSeqNumber = val;
+
+ if (mIsPacked) {
+ const int kTRTPSeqNumberOffset = 2;
+ uint16_t* buf = reinterpret_cast<uint16_t*>(
+ mPacket + kTRTPSeqNumberOffset);
+ *buf = htons(mSeqNumber);
+ }
+}
+
+uint16_t TRTPPacket::getSeqNumber() const {
+ return mSeqNumber;
+}
+
+void TRTPPacket::setPTS(int64_t val) {
+ CHECK(!mIsPacked);
+ mPTS = val;
+ mPTSValid = true;
+}
+
+int64_t TRTPPacket::getPTS() const {
+ return mPTS;
+}
+
+void TRTPPacket::setEpoch(uint32_t val) {
+ mEpoch = val;
+
+ if (mIsPacked) {
+ const int kTRTPEpochOffset = 8;
+ uint32_t* buf = reinterpret_cast<uint32_t*>(
+ mPacket + kTRTPEpochOffset);
+ uint32_t val = ntohl(*buf);
+ val &= ~(kTRTPEpochMask << kTRTPEpochShift);
+ val |= (mEpoch & kTRTPEpochMask) << kTRTPEpochShift;
+ *buf = htonl(val);
+ }
+}
+
+void TRTPPacket::setProgramID(uint16_t val) {
+ CHECK(!mIsPacked);
+ mProgramID = val;
+}
+
+void TRTPPacket::setSubstreamID(uint16_t val) {
+ CHECK(!mIsPacked);
+ mSubstreamID = val;
+}
+
+
+void TRTPPacket::setClockTransform(const LinearTransform& trans) {
+ CHECK(!mIsPacked);
+ mClockTranform = trans;
+ mClockTranformValid = true;
+}
+
+uint8_t* TRTPPacket::getPacket() const {
+ CHECK(mIsPacked);
+ return mPacket;
+}
+
+int TRTPPacket::getPacketLen() const {
+ CHECK(mIsPacked);
+ return mPacketLen;
+}
+
+void TRTPPacket::setExpireTime(nsecs_t val) {
+ CHECK(!mIsPacked);
+ mExpireTime = val;
+}
+
+nsecs_t TRTPPacket::getExpireTime() const {
+ return mExpireTime;
+}
+
+/*** TRTP audio packet properties ***/
+
+void TRTPAudioPacket::setCodecType(TRTPAudioCodecType val) {
+ CHECK(!mIsPacked);
+ mCodecType = val;
+}
+
+void TRTPAudioPacket::setRandomAccessPoint(bool val) {
+ CHECK(!mIsPacked);
+ mRandomAccessPoint = val;
+}
+
+void TRTPAudioPacket::setDropable(bool val) {
+ CHECK(!mIsPacked);
+ mDropable = val;
+}
+
+void TRTPAudioPacket::setDiscontinuity(bool val) {
+ CHECK(!mIsPacked);
+ mDiscontinuity = val;
+}
+
+void TRTPAudioPacket::setEndOfStream(bool val) {
+ CHECK(!mIsPacked);
+ mEndOfStream = val;
+}
+
+void TRTPAudioPacket::setVolume(uint8_t val) {
+ CHECK(!mIsPacked);
+ mVolume = val;
+}
+
+void TRTPAudioPacket::setAccessUnitData(const void* data, size_t len) {
+ CHECK(!mIsPacked);
+ mAccessUnitData = data;
+ mAccessUnitLen = len;
+}
+
+void TRTPAudioPacket::setAuxData(const void* data, size_t len) {
+ CHECK(!mIsPacked);
+ mAuxData = data;
+ mAuxDataLen = len;
+}
+
+/*** TRTP control packet properties ***/
+
+void TRTPControlPacket::setCommandID(TRTPCommandID val) {
+ CHECK(!mIsPacked);
+ mCommandID = val;
+}
+
+/*** TRTP packet serializers ***/
+
+void TRTPPacket::writeU8(uint8_t*& buf, uint8_t val) {
+ *buf = val;
+ buf++;
+}
+
+void TRTPPacket::writeU16(uint8_t*& buf, uint16_t val) {
+ *reinterpret_cast<uint16_t*>(buf) = htons(val);
+ buf += 2;
+}
+
+void TRTPPacket::writeU32(uint8_t*& buf, uint32_t val) {
+ *reinterpret_cast<uint32_t*>(buf) = htonl(val);
+ buf += 4;
+}
+
+void TRTPPacket::writeU64(uint8_t*& buf, uint64_t val) {
+ buf[0] = static_cast<uint8_t>(val >> 56);
+ buf[1] = static_cast<uint8_t>(val >> 48);
+ buf[2] = static_cast<uint8_t>(val >> 40);
+ buf[3] = static_cast<uint8_t>(val >> 32);
+ buf[4] = static_cast<uint8_t>(val >> 24);
+ buf[5] = static_cast<uint8_t>(val >> 16);
+ buf[6] = static_cast<uint8_t>(val >> 8);
+ buf[7] = static_cast<uint8_t>(val);
+ buf += 8;
+}
+
+void TRTPPacket::writeTRTPHeader(uint8_t*& buf,
+ bool isFirstFragment,
+ int totalPacketLen) {
+ // RTP header
+ writeU8(buf,
+ ((mVersion & 0x03) << 6) |
+ (static_cast<int>(mPadding) << 5) |
+ (static_cast<int>(mExtension) << 4) |
+ (mCsrcCount & 0x0F));
+ writeU8(buf,
+ (static_cast<int>(isFirstFragment) << 7) |
+ (mPayloadType & 0x7F));
+ writeU16(buf, mSeqNumber);
+ if (isFirstFragment && mPTSValid) {
+ writeU32(buf, mPTS & 0xFFFFFFFF);
+ } else {
+ writeU32(buf, 0);
+ }
+ writeU32(buf,
+ ((mEpoch & kTRTPEpochMask) << kTRTPEpochShift) |
+ ((mProgramID & 0x1F) << 5) |
+ (mSubstreamID & 0x1F));
+
+ // TRTP header
+ writeU8(buf, mTRTPVersion);
+ writeU8(buf,
+ ((mTRTPHeaderType & 0x0F) << 4) |
+ (mClockTranformValid ? 0x02 : 0x00) |
+ (mPTSValid ? 0x01 : 0x00));
+ writeU32(buf, totalPacketLen - kRTPHeaderLen);
+ if (mPTSValid) {
+ writeU32(buf, mPTS >> 32);
+ }
+
+ if (mClockTranformValid) {
+ writeU64(buf, mClockTranform.a_zero);
+ writeU32(buf, mClockTranform.a_to_b_numer);
+ writeU32(buf, mClockTranform.a_to_b_denom);
+ writeU64(buf, mClockTranform.b_zero);
+ }
+}
+
+bool TRTPAudioPacket::pack() {
+ if (mIsPacked) {
+ return false;
+ }
+
+ int packetLen = kRTPHeaderLen +
+ mAuxDataLen +
+ mAccessUnitLen +
+ TRTPHeaderLen();
+
+ // TODO : support multiple fragments
+ const int kMaxUDPPayloadLen = 65507;
+ if (packetLen > kMaxUDPPayloadLen) {
+ return false;
+ }
+
+ mPacket = new uint8_t[packetLen];
+ if (!mPacket) {
+ return false;
+ }
+
+ mPacketLen = packetLen;
+
+ uint8_t* cur = mPacket;
+ bool hasAux = mAuxData && mAuxDataLen;
+ uint8_t flags = (static_cast<int>(hasAux) << 4) |
+ (static_cast<int>(mRandomAccessPoint) << 3) |
+ (static_cast<int>(mDropable) << 2) |
+ (static_cast<int>(mDiscontinuity) << 1) |
+ (static_cast<int>(mEndOfStream));
+
+ writeTRTPHeader(cur, true, packetLen);
+ writeU8(cur, mCodecType);
+ writeU8(cur, flags);
+ writeU8(cur, mVolume);
+
+ if (hasAux) {
+ writeU32(cur, mAuxDataLen);
+ memcpy(cur, mAuxData, mAuxDataLen);
+ cur += mAuxDataLen;
+ }
+
+ memcpy(cur, mAccessUnitData, mAccessUnitLen);
+
+ mIsPacked = true;
+ return true;
+}
+
+int TRTPPacket::TRTPHeaderLen() const {
+ // 6 bytes for version, payload type, flags and length. An additional 4 if
+ // there are upper timestamp bits present and another 24 if there is a clock
+ // transformation present.
+ return 6 +
+ (mClockTranformValid ? 24 : 0) +
+ (mPTSValid ? 4 : 0);
+}
+
+int TRTPAudioPacket::TRTPHeaderLen() const {
+ // TRTPPacket::TRTPHeaderLen() for the base TRTPHeader. 3 bytes for audio's
+ // codec type, flags and volume field. Another 5 bytes if the codec type is
+ // PCM and we are sending sample rate/channel count. as well as however long
+ // the aux data (if present) is.
+
+ int pcmParamLength;
+ switch(mCodecType) {
+ case kCodecPCMBigEndian:
+ case kCodecPCMLittleEndian:
+ pcmParamLength = 5;
+ break;
+
+ default:
+ pcmParamLength = 0;
+ break;
+ }
+
+
+ int auxDataLenField = (NULL != mAuxData) ? sizeof(uint32_t) : 0;
+ return TRTPPacket::TRTPHeaderLen() +
+ 3 +
+ auxDataLenField +
+ pcmParamLength;
+}
+
+bool TRTPControlPacket::pack() {
+ if (mIsPacked) {
+ return false;
+ }
+
+ // command packets contain a 2-byte command ID
+ int packetLen = kRTPHeaderLen +
+ TRTPHeaderLen() +
+ 2;
+
+ mPacket = new uint8_t[packetLen];
+ if (!mPacket) {
+ return false;
+ }
+
+ mPacketLen = packetLen;
+
+ uint8_t* cur = mPacket;
+
+ writeTRTPHeader(cur, true, packetLen);
+ writeU16(cur, mCommandID);
+
+ mIsPacked = true;
+ return true;
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_tx_packet.h b/media/libaah_rtp/aah_tx_packet.h
new file mode 100644
index 0000000..7f78ea0
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_packet.h
@@ -0,0 +1,213 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAH_TX_PACKET_H__
+#define __AAH_TX_PACKET_H__
+
+#include <media/stagefright/foundation/ABase.h>
+#include <utils/LinearTransform.h>
+#include <utils/RefBase.h>
+#include <utils/Timers.h>
+
+namespace android {
+
+class TRTPPacket : public RefBase {
+ public:
+ enum TRTPHeaderType {
+ kHeaderTypeAudio = 1,
+ kHeaderTypeVideo = 2,
+ kHeaderTypeSubpicture = 3,
+ kHeaderTypeControl = 4,
+ };
+
+ enum TRTPPayloadFlags {
+ kFlag_TSTransformPresent = 0x02,
+ kFlag_TSValid = 0x01,
+ };
+
+ protected:
+ TRTPPacket(TRTPHeaderType headerType)
+ : mIsPacked(false)
+ , mVersion(2)
+ , mPadding(false)
+ , mExtension(false)
+ , mCsrcCount(0)
+ , mPayloadType(100)
+ , mSeqNumber(0)
+ , mPTSValid(false)
+ , mPTS(0)
+ , mEpoch(0)
+ , mProgramID(0)
+ , mSubstreamID(0)
+ , mClockTranformValid(false)
+ , mTRTPVersion(1)
+ , mTRTPLength(0)
+ , mTRTPHeaderType(headerType)
+ , mPacket(NULL)
+ , mPacketLen(0) { }
+
+ public:
+ virtual ~TRTPPacket();
+
+ void setSeqNumber(uint16_t val);
+ uint16_t getSeqNumber() const;
+
+ void setPTS(int64_t val);
+ int64_t getPTS() const;
+
+ void setEpoch(uint32_t val);
+ void setProgramID(uint16_t val);
+ void setSubstreamID(uint16_t val);
+ void setClockTransform(const LinearTransform& trans);
+
+ uint8_t* getPacket() const;
+ int getPacketLen() const;
+
+ void setExpireTime(nsecs_t val);
+ nsecs_t getExpireTime() const;
+
+ virtual bool pack() = 0;
+
+ // mask for the number of bits in a TRTP epoch
+ static const uint32_t kTRTPEpochMask = (1 << 22) - 1;
+ static const int kTRTPEpochShift = 10;
+
+ protected:
+ static const int kRTPHeaderLen = 12;
+ virtual int TRTPHeaderLen() const;
+
+ void writeTRTPHeader(uint8_t*& buf,
+ bool isFirstFragment,
+ int totalPacketLen);
+
+ void writeU8(uint8_t*& buf, uint8_t val);
+ void writeU16(uint8_t*& buf, uint16_t val);
+ void writeU32(uint8_t*& buf, uint32_t val);
+ void writeU64(uint8_t*& buf, uint64_t val);
+
+ bool mIsPacked;
+
+ uint8_t mVersion;
+ bool mPadding;
+ bool mExtension;
+ uint8_t mCsrcCount;
+ uint8_t mPayloadType;
+ uint16_t mSeqNumber;
+ bool mPTSValid;
+ int64_t mPTS;
+ uint32_t mEpoch;
+ uint16_t mProgramID;
+ uint16_t mSubstreamID;
+ LinearTransform mClockTranform;
+ bool mClockTranformValid;
+ uint8_t mTRTPVersion;
+ uint32_t mTRTPLength;
+ TRTPHeaderType mTRTPHeaderType;
+
+ uint8_t* mPacket;
+ int mPacketLen;
+
+ nsecs_t mExpireTime;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TRTPPacket);
+};
+
+class TRTPAudioPacket : public TRTPPacket {
+ public:
+ enum AudioPayloadFlags {
+ kFlag_AuxLengthPresent = 0x10,
+ kFlag_RandomAccessPoint = 0x08,
+ kFlag_Dropable = 0x04,
+ kFlag_Discontinuity = 0x02,
+ kFlag_EndOfStream = 0x01,
+ };
+
+ TRTPAudioPacket()
+ : TRTPPacket(kHeaderTypeAudio)
+ , mCodecType(kCodecInvalid)
+ , mRandomAccessPoint(false)
+ , mDropable(false)
+ , mDiscontinuity(false)
+ , mEndOfStream(false)
+ , mVolume(0)
+ , mAccessUnitData(NULL)
+ , mAccessUnitLen(0)
+ , mAuxData(NULL)
+ , mAuxDataLen(0) { }
+
+ enum TRTPAudioCodecType {
+ kCodecInvalid = 0,
+ kCodecPCMBigEndian = 1,
+ kCodecPCMLittleEndian = 2,
+ kCodecMPEG1Audio = 3,
+ kCodecAACAudio = 4,
+ };
+
+ void setCodecType(TRTPAudioCodecType val);
+ void setRandomAccessPoint(bool val);
+ void setDropable(bool val);
+ void setDiscontinuity(bool val);
+ void setEndOfStream(bool val);
+ void setVolume(uint8_t val);
+ void setAccessUnitData(const void* data, size_t len);
+ void setAuxData(const void* data, size_t len);
+
+ virtual bool pack();
+
+ protected:
+ virtual int TRTPHeaderLen() const;
+
+ private:
+ TRTPAudioCodecType mCodecType;
+ bool mRandomAccessPoint;
+ bool mDropable;
+ bool mDiscontinuity;
+ bool mEndOfStream;
+ uint8_t mVolume;
+
+ const void* mAccessUnitData;
+ size_t mAccessUnitLen;
+ const void* mAuxData;
+ size_t mAuxDataLen;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TRTPAudioPacket);
+};
+
+class TRTPControlPacket : public TRTPPacket {
+ public:
+ TRTPControlPacket()
+ : TRTPPacket(kHeaderTypeControl)
+ , mCommandID(kCommandNop) {}
+
+ enum TRTPCommandID {
+ kCommandNop = 1,
+ kCommandFlush = 2,
+ kCommandEOS = 3,
+ };
+
+ void setCommandID(TRTPCommandID val);
+
+ virtual bool pack();
+
+ private:
+ TRTPCommandID mCommandID;
+
+ DISALLOW_EVIL_CONSTRUCTORS(TRTPControlPacket);
+};
+
+} // namespace android
+
+#endif // __AAH_TX_PLAYER_H__
diff --git a/media/libaah_rtp/aah_tx_player.cpp b/media/libaah_rtp/aah_tx_player.cpp
new file mode 100644
index 0000000..974805b
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_player.cpp
@@ -0,0 +1,1177 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <utils/Log.h>
+
+#define __STDC_FORMAT_MACROS
+#include <inttypes.h>
+#include <netdb.h>
+#include <netinet/ip.h>
+
+#include <common_time/cc_helper.h>
+#include <media/IMediaPlayer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/FileSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MetaData.h>
+#include <utils/Timers.h>
+
+#include "aah_tx_packet.h"
+#include "aah_tx_player.h"
+
+namespace android {
+
+static int64_t kLowWaterMarkUs = 2000000ll; // 2secs
+static int64_t kHighWaterMarkUs = 10000000ll; // 10secs
+static const size_t kLowWaterMarkBytes = 40000;
+static const size_t kHighWaterMarkBytes = 200000;
+
+// When we start up, how much lead time should we put on the first access unit?
+static const int64_t kAAHStartupLeadTimeUs = 300000LL;
+
+// How much time do we attempt to lead the clock by in steady state?
+static const int64_t kAAHBufferTimeUs = 1000000LL;
+
+// how long do we keep data in our retransmit buffer after sending it.
+const int64_t AAH_TXPlayer::kAAHRetryKeepAroundTimeNs =
+ kAAHBufferTimeUs * 1100;
+
+sp<MediaPlayerBase> createAAH_TXPlayer() {
+ sp<MediaPlayerBase> ret = new AAH_TXPlayer();
+ return ret;
+}
+
+template <typename T> static T clamp(T val, T min, T max) {
+ if (val < min) {
+ return min;
+ } else if (val > max) {
+ return max;
+ } else {
+ return val;
+ }
+}
+
+struct AAH_TXEvent : public TimedEventQueue::Event {
+ AAH_TXEvent(AAH_TXPlayer *player,
+ void (AAH_TXPlayer::*method)()) : mPlayer(player)
+ , mMethod(method) {}
+
+ protected:
+ virtual ~AAH_TXEvent() {}
+
+ virtual void fire(TimedEventQueue *queue, int64_t /* now_us */) {
+ (mPlayer->*mMethod)();
+ }
+
+ private:
+ AAH_TXPlayer *mPlayer;
+ void (AAH_TXPlayer::*mMethod)();
+
+ AAH_TXEvent(const AAH_TXEvent &);
+ AAH_TXEvent& operator=(const AAH_TXEvent &);
+};
+
+AAH_TXPlayer::AAH_TXPlayer()
+ : mQueueStarted(false)
+ , mFlags(0)
+ , mExtractorFlags(0) {
+ DataSource::RegisterDefaultSniffers();
+
+ mBufferingEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onBufferingUpdate);
+ mBufferingEventPending = false;
+
+ mPumpAudioEvent = new AAH_TXEvent(this, &AAH_TXPlayer::onPumpAudio);
+ mPumpAudioEventPending = false;
+
+ mAudioCodecData = NULL;
+
+ reset_l();
+}
+
+AAH_TXPlayer::~AAH_TXPlayer() {
+ if (mQueueStarted) {
+ mQueue.stop();
+ }
+
+ reset_l();
+}
+
+void AAH_TXPlayer::cancelPlayerEvents(bool keepBufferingGoing) {
+ if (!keepBufferingGoing) {
+ mQueue.cancelEvent(mBufferingEvent->eventID());
+ mBufferingEventPending = false;
+
+ mQueue.cancelEvent(mPumpAudioEvent->eventID());
+ mPumpAudioEventPending = false;
+ }
+}
+
+status_t AAH_TXPlayer::initCheck() {
+ // Check for the presense of the common time service by attempting to query
+ // for CommonTime's frequency. If we get an error back, we cannot talk to
+ // the service at all and should abort now.
+ status_t res;
+ uint64_t freq;
+ res = mCCHelper.getCommonFreq(&freq);
+ if (OK != res) {
+ ALOGE("Failed to connect to common time service! (res %d)", res);
+ return res;
+ }
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::setDataSource(
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
+ Mutex::Autolock autoLock(mLock);
+ return setDataSource_l(url, headers);
+}
+
+status_t AAH_TXPlayer::setDataSource_l(
+ const char *url,
+ const KeyedVector<String8, String8> *headers) {
+ reset_l();
+
+ mUri.setTo(url);
+
+ if (headers) {
+ mUriHeaders = *headers;
+
+ ssize_t index = mUriHeaders.indexOfKey(String8("x-hide-urls-from-log"));
+ if (index >= 0) {
+ // Browser is in "incognito" mode, suppress logging URLs.
+
+ // This isn't something that should be passed to the server.
+ mUriHeaders.removeItemsAt(index);
+
+ mFlags |= INCOGNITO;
+ }
+ }
+
+ // The URL may optionally contain a "#" character followed by a Skyjam
+ // cookie. Ideally the cookie header should just be passed in the headers
+ // argument, but the Java API for supplying headers is apparently not yet
+ // exposed in the SDK used by application developers.
+ const char kSkyjamCookieDelimiter = '#';
+ char* skyjamCookie = strrchr(mUri.string(), kSkyjamCookieDelimiter);
+ if (skyjamCookie) {
+ skyjamCookie++;
+ mUriHeaders.add(String8("Cookie"), String8(skyjamCookie));
+ mUri = String8(mUri.string(), skyjamCookie - mUri.string());
+ }
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::setDataSource(int fd, int64_t offset, int64_t length) {
+ Mutex::Autolock autoLock(mLock);
+
+ reset_l();
+
+ sp<DataSource> dataSource = new FileSource(dup(fd), offset, length);
+
+ status_t err = dataSource->initCheck();
+
+ if (err != OK) {
+ return err;
+ }
+
+ mFileSource = dataSource;
+
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ return setDataSource_l(extractor);
+}
+
+status_t AAH_TXPlayer::setVideoSurface(const sp<Surface>& surface) {
+ return OK;
+}
+
+status_t AAH_TXPlayer::setVideoSurfaceTexture(
+ const sp<ISurfaceTexture>& surfaceTexture) {
+ return OK;
+}
+
+status_t AAH_TXPlayer::prepare() {
+ return INVALID_OPERATION;
+}
+
+status_t AAH_TXPlayer::prepareAsync() {
+ Mutex::Autolock autoLock(mLock);
+
+ return prepareAsync_l();
+}
+
+status_t AAH_TXPlayer::prepareAsync_l() {
+ if (mFlags & PREPARING) {
+ return UNKNOWN_ERROR; // async prepare already pending
+ }
+
+ mAAH_Sender = AAH_TXSender::GetInstance();
+ if (mAAH_Sender == NULL) {
+ return NO_MEMORY;
+ }
+
+ if (!mQueueStarted) {
+ mQueue.start();
+ mQueueStarted = true;
+ }
+
+ mFlags |= PREPARING;
+ mAsyncPrepareEvent = new AAH_TXEvent(
+ this, &AAH_TXPlayer::onPrepareAsyncEvent);
+
+ mQueue.postEvent(mAsyncPrepareEvent);
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::finishSetDataSource_l() {
+ sp<DataSource> dataSource;
+
+ if (!strncasecmp("http://", mUri.string(), 7) ||
+ !strncasecmp("https://", mUri.string(), 8)) {
+
+ mConnectingDataSource = HTTPBase::Create(
+ (mFlags & INCOGNITO)
+ ? HTTPBase::kFlagIncognito
+ : 0);
+
+ mLock.unlock();
+ status_t err = mConnectingDataSource->connect(mUri, &mUriHeaders);
+ mLock.lock();
+
+ if (err != OK) {
+ mConnectingDataSource.clear();
+
+ ALOGI("mConnectingDataSource->connect() returned %d", err);
+ return err;
+ }
+
+ mCachedSource = new NuCachedSource2(mConnectingDataSource);
+ mConnectingDataSource.clear();
+
+ dataSource = mCachedSource;
+
+ // We're going to prefill the cache before trying to instantiate
+ // the extractor below, as the latter is an operation that otherwise
+ // could block on the datasource for a significant amount of time.
+ // During that time we'd be unable to abort the preparation phase
+ // without this prefill.
+
+ mLock.unlock();
+
+ for (;;) {
+ status_t finalStatus;
+ size_t cachedDataRemaining =
+ mCachedSource->approxDataRemaining(&finalStatus);
+
+ if (finalStatus != OK ||
+ cachedDataRemaining >= kHighWaterMarkBytes ||
+ (mFlags & PREPARE_CANCELLED)) {
+ break;
+ }
+
+ usleep(200000);
+ }
+
+ mLock.lock();
+
+ if (mFlags & PREPARE_CANCELLED) {
+ ALOGI("Prepare cancelled while waiting for initial cache fill.");
+ return UNKNOWN_ERROR;
+ }
+ } else {
+ dataSource = DataSource::CreateFromURI(mUri.string(), &mUriHeaders);
+ }
+
+ if (dataSource == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ sp<MediaExtractor> extractor = MediaExtractor::Create(dataSource);
+
+ if (extractor == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ return setDataSource_l(extractor);
+}
+
+status_t AAH_TXPlayer::setDataSource_l(const sp<MediaExtractor> &extractor) {
+ // Attempt to approximate overall stream bitrate by summing all
+ // tracks' individual bitrates, if not all of them advertise bitrate,
+ // we have to fail.
+
+ int64_t totalBitRate = 0;
+
+ for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+ int32_t bitrate;
+ if (!meta->findInt32(kKeyBitRate, &bitrate)) {
+ totalBitRate = -1;
+ break;
+ }
+
+ totalBitRate += bitrate;
+ }
+
+ mBitrate = totalBitRate;
+
+ ALOGV("mBitrate = %lld bits/sec", mBitrate);
+
+ bool haveAudio = false;
+ for (size_t i = 0; i < extractor->countTracks(); ++i) {
+ sp<MetaData> meta = extractor->getTrackMetaData(i);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ if (!strncasecmp(mime, "audio/", 6)) {
+ mAudioSource = extractor->getTrack(i);
+ CHECK(mAudioSource != NULL);
+ haveAudio = true;
+ break;
+ }
+ }
+
+ if (!haveAudio) {
+ return UNKNOWN_ERROR;
+ }
+
+ mExtractorFlags = extractor->flags();
+
+ return OK;
+}
+
+void AAH_TXPlayer::abortPrepare(status_t err) {
+ CHECK(err != OK);
+
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, err);
+
+ mPrepareResult = err;
+ mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
+ mPreparedCondition.broadcast();
+}
+
+void AAH_TXPlayer::onPrepareAsyncEvent() {
+ Mutex::Autolock autoLock(mLock);
+
+ if (mFlags & PREPARE_CANCELLED) {
+ ALOGI("prepare was cancelled before doing anything");
+ abortPrepare(UNKNOWN_ERROR);
+ return;
+ }
+
+ if (mUri.size() > 0) {
+ status_t err = finishSetDataSource_l();
+
+ if (err != OK) {
+ abortPrepare(err);
+ return;
+ }
+ }
+
+ mAudioFormat = mAudioSource->getFormat();
+ if (!mAudioFormat->findInt64(kKeyDuration, &mDurationUs))
+ mDurationUs = 1;
+
+ const char* mime_type = NULL;
+ if (!mAudioFormat->findCString(kKeyMIMEType, &mime_type)) {
+ ALOGE("Failed to find audio substream MIME type during prepare.");
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_MPEG)) {
+ mAudioCodec = TRTPAudioPacket::kCodecMPEG1Audio;
+ } else
+ if (!strcmp(mime_type, MEDIA_MIMETYPE_AUDIO_AAC)) {
+ mAudioCodec = TRTPAudioPacket::kCodecAACAudio;
+
+ uint32_t type;
+ int32_t sample_rate;
+ int32_t channel_count;
+ const void* esds_data;
+ size_t esds_len;
+
+ if (!mAudioFormat->findInt32(kKeySampleRate, &sample_rate)) {
+ ALOGE("Failed to find sample rate for AAC substream.");
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ if (!mAudioFormat->findInt32(kKeyChannelCount, &channel_count)) {
+ ALOGE("Failed to find channel count for AAC substream.");
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ if (!mAudioFormat->findData(kKeyESDS, &type, &esds_data, &esds_len)) {
+ ALOGE("Failed to find codec init data for AAC substream.");
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ CHECK(NULL == mAudioCodecData);
+ mAudioCodecDataSize = esds_len
+ + sizeof(sample_rate)
+ + sizeof(channel_count);
+ mAudioCodecData = new uint8_t[mAudioCodecDataSize];
+ if (NULL == mAudioCodecData) {
+ ALOGE("Failed to allocate %u bytes for AAC substream codec aux"
+ " data.", mAudioCodecDataSize);
+ mAudioCodecDataSize = 0;
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ uint8_t* tmp = mAudioCodecData;
+ tmp[0] = static_cast<uint8_t>((sample_rate >> 24) & 0xFF);
+ tmp[1] = static_cast<uint8_t>((sample_rate >> 16) & 0xFF);
+ tmp[2] = static_cast<uint8_t>((sample_rate >> 8) & 0xFF);
+ tmp[3] = static_cast<uint8_t>((sample_rate ) & 0xFF);
+ tmp[4] = static_cast<uint8_t>((channel_count >> 24) & 0xFF);
+ tmp[5] = static_cast<uint8_t>((channel_count >> 16) & 0xFF);
+ tmp[6] = static_cast<uint8_t>((channel_count >> 8) & 0xFF);
+ tmp[7] = static_cast<uint8_t>((channel_count ) & 0xFF);
+
+ memcpy(tmp + 8, esds_data, esds_len);
+ } else {
+ ALOGE("Unsupported MIME type \"%s\" in audio substream", mime_type);
+ abortPrepare(BAD_VALUE);
+ return;
+ }
+
+ status_t err = mAudioSource->start();
+ if (err != OK) {
+ ALOGI("failed to start audio source, err=%d", err);
+ abortPrepare(err);
+ return;
+ }
+
+ mFlags |= PREPARING_CONNECTED;
+
+ if (mCachedSource != NULL) {
+ postBufferingEvent_l();
+ } else {
+ finishAsyncPrepare_l();
+ }
+}
+
+void AAH_TXPlayer::finishAsyncPrepare_l() {
+ notifyListener_l(MEDIA_PREPARED);
+
+ mPrepareResult = OK;
+ mFlags &= ~(PREPARING|PREPARE_CANCELLED|PREPARING_CONNECTED);
+ mFlags |= PREPARED;
+ mPreparedCondition.broadcast();
+}
+
+status_t AAH_TXPlayer::start() {
+ Mutex::Autolock autoLock(mLock);
+
+ mFlags &= ~CACHE_UNDERRUN;
+
+ return play_l();
+}
+
+status_t AAH_TXPlayer::play_l() {
+ if (mFlags & PLAYING) {
+ return OK;
+ }
+
+ if (!(mFlags & PREPARED)) {
+ return INVALID_OPERATION;
+ }
+
+ {
+ Mutex::Autolock lock(mEndpointLock);
+ if (!mEndpointValid) {
+ return INVALID_OPERATION;
+ }
+ if (!mEndpointRegistered) {
+ mProgramID = mAAH_Sender->registerEndpoint(mEndpoint);
+ mEndpointRegistered = true;
+ }
+ }
+
+ mFlags |= PLAYING;
+
+ updateClockTransform_l(false);
+
+ postPumpAudioEvent_l(-1);
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::stop() {
+ status_t ret = pause();
+ sendEOS_l();
+ return ret;
+}
+
+status_t AAH_TXPlayer::pause() {
+ Mutex::Autolock autoLock(mLock);
+
+ mFlags &= ~CACHE_UNDERRUN;
+
+ return pause_l();
+}
+
+status_t AAH_TXPlayer::pause_l(bool doClockUpdate) {
+ if (!(mFlags & PLAYING)) {
+ return OK;
+ }
+
+ cancelPlayerEvents(true /* keepBufferingGoing */);
+
+ mFlags &= ~PLAYING;
+
+ if (doClockUpdate) {
+ updateClockTransform_l(true);
+ }
+
+ return OK;
+}
+
+void AAH_TXPlayer::updateClockTransform_l(bool pause) {
+ // record the new pause status so that onPumpAudio knows what rate to apply
+ // when it initializes the transform
+ mPlayRateIsPaused = pause;
+
+ // if we haven't yet established a valid clock transform, then we can't
+ // do anything here
+ if (!mCurrentClockTransformValid) {
+ return;
+ }
+
+ // sample the current common time
+ int64_t commonTimeNow;
+ if (OK != mCCHelper.getCommonTime(&commonTimeNow)) {
+ ALOGE("updateClockTransform_l get common time failed");
+ mCurrentClockTransformValid = false;
+ return;
+ }
+
+ // convert the current common time to media time using the old
+ // transform
+ int64_t mediaTimeNow;
+ if (!mCurrentClockTransform.doReverseTransform(
+ commonTimeNow, &mediaTimeNow)) {
+ ALOGE("updateClockTransform_l reverse transform failed");
+ mCurrentClockTransformValid = false;
+ return;
+ }
+
+ // calculate a new transform that preserves the old transform's
+ // result for the current time
+ mCurrentClockTransform.a_zero = mediaTimeNow;
+ mCurrentClockTransform.b_zero = commonTimeNow;
+ mCurrentClockTransform.a_to_b_numer = 1;
+ mCurrentClockTransform.a_to_b_denom = pause ? 0 : 1;
+
+ // send a packet announcing the new transform
+ sp<TRTPControlPacket> packet = new TRTPControlPacket();
+ packet->setClockTransform(mCurrentClockTransform);
+ packet->setCommandID(TRTPControlPacket::kCommandNop);
+ queuePacketToSender_l(packet);
+}
+
+void AAH_TXPlayer::sendEOS_l() {
+ sp<TRTPControlPacket> packet = new TRTPControlPacket();
+ packet->setCommandID(TRTPControlPacket::kCommandEOS);
+ queuePacketToSender_l(packet);
+}
+
+bool AAH_TXPlayer::isPlaying() {
+ return (mFlags & PLAYING) || (mFlags & CACHE_UNDERRUN);
+}
+
+status_t AAH_TXPlayer::seekTo(int msec) {
+ if (mExtractorFlags & MediaExtractor::CAN_SEEK) {
+ Mutex::Autolock autoLock(mLock);
+ return seekTo_l(static_cast<int64_t>(msec) * 1000);
+ }
+
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ return OK;
+}
+
+status_t AAH_TXPlayer::seekTo_l(int64_t timeUs) {
+ mIsSeeking = true;
+ mSeekTimeUs = timeUs;
+
+ mCurrentClockTransformValid = false;
+ mLastQueuedMediaTimePTSValid = false;
+
+ // send a flush command packet
+ sp<TRTPControlPacket> packet = new TRTPControlPacket();
+ packet->setCommandID(TRTPControlPacket::kCommandFlush);
+ queuePacketToSender_l(packet);
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::getCurrentPosition(int *msec) {
+ if (!msec) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mLock);
+
+ int position;
+
+ if (mIsSeeking) {
+ position = mSeekTimeUs / 1000;
+ } else if (mCurrentClockTransformValid) {
+ // sample the current common time
+ int64_t commonTimeNow;
+ if (OK != mCCHelper.getCommonTime(&commonTimeNow)) {
+ ALOGE("getCurrentPosition get common time failed");
+ return INVALID_OPERATION;
+ }
+
+ int64_t mediaTimeNow;
+ if (!mCurrentClockTransform.doReverseTransform(commonTimeNow,
+ &mediaTimeNow)) {
+ ALOGE("getCurrentPosition reverse transform failed");
+ return INVALID_OPERATION;
+ }
+
+ position = static_cast<int>(mediaTimeNow / 1000);
+ } else {
+ position = 0;
+ }
+
+ int duration;
+ if (getDuration_l(&duration) == OK) {
+ *msec = clamp(position, 0, duration);
+ } else {
+ *msec = (position >= 0) ? position : 0;
+ }
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::getDuration(int* msec) {
+ if (!msec) {
+ return BAD_VALUE;
+ }
+
+ Mutex::Autolock lock(mLock);
+
+ return getDuration_l(msec);
+}
+
+status_t AAH_TXPlayer::getDuration_l(int* msec) {
+ if (mDurationUs < 0) {
+ return UNKNOWN_ERROR;
+ }
+
+ *msec = (mDurationUs + 500) / 1000;
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::reset() {
+ Mutex::Autolock autoLock(mLock);
+ reset_l();
+ return OK;
+}
+
+void AAH_TXPlayer::reset_l() {
+ if (mFlags & PREPARING) {
+ mFlags |= PREPARE_CANCELLED;
+ if (mConnectingDataSource != NULL) {
+ ALOGI("interrupting the connection process");
+ mConnectingDataSource->disconnect();
+ }
+
+ if (mFlags & PREPARING_CONNECTED) {
+ // We are basically done preparing, we're just buffering
+ // enough data to start playback, we can safely interrupt that.
+ finishAsyncPrepare_l();
+ }
+ }
+
+ while (mFlags & PREPARING) {
+ mPreparedCondition.wait(mLock);
+ }
+
+ cancelPlayerEvents();
+
+ sendEOS_l();
+
+ mCachedSource.clear();
+
+ if (mAudioSource != NULL) {
+ mAudioSource->stop();
+ }
+ mAudioSource.clear();
+ mAudioCodec = TRTPAudioPacket::kCodecInvalid;
+ mAudioFormat = NULL;
+ delete[] mAudioCodecData;
+ mAudioCodecData = NULL;
+ mAudioCodecDataSize = 0;
+
+ mFlags = 0;
+ mExtractorFlags = 0;
+
+ mDurationUs = -1;
+ mIsSeeking = false;
+ mSeekTimeUs = 0;
+
+ mUri.setTo("");
+ mUriHeaders.clear();
+
+ mFileSource.clear();
+
+ mBitrate = -1;
+
+ {
+ Mutex::Autolock lock(mEndpointLock);
+ if (mAAH_Sender != NULL && mEndpointRegistered) {
+ mAAH_Sender->unregisterEndpoint(mEndpoint);
+ }
+ mEndpointRegistered = false;
+ mEndpointValid = false;
+ }
+
+ mProgramID = 0;
+
+ mAAH_Sender.clear();
+ mLastQueuedMediaTimePTSValid = false;
+ mCurrentClockTransformValid = false;
+ mPlayRateIsPaused = false;
+
+ mTRTPVolume = 255;
+}
+
+status_t AAH_TXPlayer::setLooping(int loop) {
+ return OK;
+}
+
+player_type AAH_TXPlayer::playerType() {
+ return AAH_TX_PLAYER;
+}
+
+status_t AAH_TXPlayer::setParameter(int key, const Parcel &request) {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_TXPlayer::getParameter(int key, Parcel *reply) {
+ return ERROR_UNSUPPORTED;
+}
+
+status_t AAH_TXPlayer::invoke(const Parcel& request, Parcel *reply) {
+ return INVALID_OPERATION;
+}
+
+status_t AAH_TXPlayer::getMetadata(const media::Metadata::Filter& ids,
+ Parcel* records) {
+ using media::Metadata;
+
+ Metadata metadata(records);
+
+ metadata.appendBool(Metadata::kPauseAvailable, true);
+ metadata.appendBool(Metadata::kSeekBackwardAvailable, false);
+ metadata.appendBool(Metadata::kSeekForwardAvailable, false);
+ metadata.appendBool(Metadata::kSeekAvailable, false);
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::setVolume(float leftVolume, float rightVolume) {
+ if (leftVolume != rightVolume) {
+ ALOGE("%s does not support per channel volume: %f, %f",
+ __PRETTY_FUNCTION__, leftVolume, rightVolume);
+ }
+
+ float volume = clamp(leftVolume, 0.0f, 1.0f);
+
+ Mutex::Autolock lock(mLock);
+ mTRTPVolume = static_cast<uint8_t>((leftVolume * 255.0) + 0.5);
+
+ return OK;
+}
+
+status_t AAH_TXPlayer::setAudioStreamType(audio_stream_type_t streamType) {
+ return OK;
+}
+
+status_t AAH_TXPlayer::setRetransmitEndpoint(
+ const struct sockaddr_in* endpoint) {
+ Mutex::Autolock lock(mLock);
+
+ if (NULL == endpoint)
+ return BAD_VALUE;
+
+ // Once the endpoint has been registered, it may not be changed.
+ if (mEndpointRegistered)
+ return INVALID_OPERATION;
+
+ mEndpoint.addr = endpoint->sin_addr.s_addr;
+ mEndpoint.port = endpoint->sin_port;
+ mEndpointValid = true;
+
+ return OK;
+}
+
+void AAH_TXPlayer::notifyListener_l(int msg, int ext1, int ext2) {
+ sendEvent(msg, ext1, ext2);
+}
+
+bool AAH_TXPlayer::getBitrate_l(int64_t *bitrate) {
+ off64_t size;
+ if (mDurationUs >= 0 &&
+ mCachedSource != NULL &&
+ mCachedSource->getSize(&size) == OK) {
+ *bitrate = size * 8000000ll / mDurationUs; // in bits/sec
+ return true;
+ }
+
+ if (mBitrate >= 0) {
+ *bitrate = mBitrate;
+ return true;
+ }
+
+ *bitrate = 0;
+
+ return false;
+}
+
+// Returns true iff cached duration is available/applicable.
+bool AAH_TXPlayer::getCachedDuration_l(int64_t *durationUs, bool *eos) {
+ int64_t bitrate;
+
+ if (mCachedSource != NULL && getBitrate_l(&bitrate)) {
+ status_t finalStatus;
+ size_t cachedDataRemaining = mCachedSource->approxDataRemaining(
+ &finalStatus);
+ *durationUs = cachedDataRemaining * 8000000ll / bitrate;
+ *eos = (finalStatus != OK);
+ return true;
+ }
+
+ return false;
+}
+
+void AAH_TXPlayer::ensureCacheIsFetching_l() {
+ if (mCachedSource != NULL) {
+ mCachedSource->resumeFetchingIfNecessary();
+ }
+}
+
+void AAH_TXPlayer::postBufferingEvent_l() {
+ if (mBufferingEventPending) {
+ return;
+ }
+ mBufferingEventPending = true;
+ mQueue.postEventWithDelay(mBufferingEvent, 1000000ll);
+}
+
+void AAH_TXPlayer::postPumpAudioEvent_l(int64_t delayUs) {
+ if (mPumpAudioEventPending) {
+ return;
+ }
+ mPumpAudioEventPending = true;
+ mQueue.postEventWithDelay(mPumpAudioEvent, delayUs < 0 ? 10000 : delayUs);
+}
+
+void AAH_TXPlayer::onBufferingUpdate() {
+ Mutex::Autolock autoLock(mLock);
+ if (!mBufferingEventPending) {
+ return;
+ }
+ mBufferingEventPending = false;
+
+ if (mCachedSource != NULL) {
+ status_t finalStatus;
+ size_t cachedDataRemaining = mCachedSource->approxDataRemaining(
+ &finalStatus);
+ bool eos = (finalStatus != OK);
+
+ if (eos) {
+ if (finalStatus == ERROR_END_OF_STREAM) {
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+ }
+ if (mFlags & PREPARING) {
+ ALOGV("cache has reached EOS, prepare is done.");
+ finishAsyncPrepare_l();
+ }
+ } else {
+ int64_t bitrate;
+ if (getBitrate_l(&bitrate)) {
+ size_t cachedSize = mCachedSource->cachedSize();
+ int64_t cachedDurationUs = cachedSize * 8000000ll / bitrate;
+
+ int percentage = (100.0 * (double) cachedDurationUs)
+ / mDurationUs;
+ if (percentage > 100) {
+ percentage = 100;
+ }
+
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, percentage);
+ } else {
+ // We don't know the bitrate of the stream, use absolute size
+ // limits to maintain the cache.
+
+ if ((mFlags & PLAYING) &&
+ !eos &&
+ (cachedDataRemaining < kLowWaterMarkBytes)) {
+ ALOGI("cache is running low (< %d) , pausing.",
+ kLowWaterMarkBytes);
+ mFlags |= CACHE_UNDERRUN;
+ pause_l();
+ ensureCacheIsFetching_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+ } else if (eos || cachedDataRemaining > kHighWaterMarkBytes) {
+ if (mFlags & CACHE_UNDERRUN) {
+ ALOGI("cache has filled up (> %d), resuming.",
+ kHighWaterMarkBytes);
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+ } else if (mFlags & PREPARING) {
+ ALOGV("cache has filled up (> %d), prepare is done",
+ kHighWaterMarkBytes);
+ finishAsyncPrepare_l();
+ }
+ }
+ }
+ }
+ }
+
+ int64_t cachedDurationUs;
+ bool eos;
+ if (getCachedDuration_l(&cachedDurationUs, &eos)) {
+ ALOGV("cachedDurationUs = %.2f secs, eos=%d",
+ cachedDurationUs / 1E6, eos);
+
+ if ((mFlags & PLAYING) &&
+ !eos &&
+ (cachedDurationUs < kLowWaterMarkUs)) {
+ ALOGI("cache is running low (%.2f secs) , pausing.",
+ cachedDurationUs / 1E6);
+ mFlags |= CACHE_UNDERRUN;
+ pause_l();
+ ensureCacheIsFetching_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_START);
+ } else if (eos || cachedDurationUs > kHighWaterMarkUs) {
+ if (mFlags & CACHE_UNDERRUN) {
+ ALOGI("cache has filled up (%.2f secs), resuming.",
+ cachedDurationUs / 1E6);
+ mFlags &= ~CACHE_UNDERRUN;
+ play_l();
+ notifyListener_l(MEDIA_INFO, MEDIA_INFO_BUFFERING_END);
+ } else if (mFlags & PREPARING) {
+ ALOGV("cache has filled up (%.2f secs), prepare is done",
+ cachedDurationUs / 1E6);
+ finishAsyncPrepare_l();
+ }
+ }
+ }
+
+ postBufferingEvent_l();
+}
+
+void AAH_TXPlayer::onPumpAudio() {
+ while (true) {
+ Mutex::Autolock autoLock(mLock);
+ // If this flag is clear, its because someone has externally canceled
+ // this pump operation (probably because we a resetting/shutting down).
+ // Get out immediately, do not reschedule ourselves.
+ if (!mPumpAudioEventPending) {
+ return;
+ }
+
+ // Start by checking if there is still work to be doing. If we have
+ // never queued a payload (so we don't know what the last queued PTS is)
+ // or we have never established a MediaTime->CommonTime transformation,
+ // then we have work to do (one time through this loop should establish
+ // both). Otherwise, we want to keep a fixed amt of presentation time
+ // worth of data buffered. If we cannot get common time (service is
+ // unavailable, or common time is undefined)) then we don't have a lot
+ // of good options here. For now, signal an error up to the app level
+ // and shut down the transmission pump.
+ int64_t commonTimeNow;
+ if (OK != mCCHelper.getCommonTime(&commonTimeNow)) {
+ // Failed to get common time; either the service is down or common
+ // time is not synced. Raise an error and shutdown the player.
+ ALOGE("*** Cannot pump audio, unable to fetch common time."
+ " Shutting down.");
+ notifyListener_l(MEDIA_ERROR, MEDIA_ERROR_UNKNOWN, UNKNOWN_ERROR);
+ mPumpAudioEventPending = false;
+ break;
+ }
+
+ if (mCurrentClockTransformValid && mLastQueuedMediaTimePTSValid) {
+ int64_t mediaTimeNow;
+ bool conversionResult = mCurrentClockTransform.doReverseTransform(
+ commonTimeNow,
+ &mediaTimeNow);
+ CHECK(conversionResult);
+
+ if ((mediaTimeNow +
+ kAAHBufferTimeUs -
+ mLastQueuedMediaTimePTS) <= 0) {
+ break;
+ }
+ }
+
+ MediaSource::ReadOptions options;
+ if (mIsSeeking) {
+ options.setSeekTo(mSeekTimeUs);
+ }
+
+ MediaBuffer* mediaBuffer;
+ status_t err = mAudioSource->read(&mediaBuffer, &options);
+ if (err != NO_ERROR) {
+ if (err == ERROR_END_OF_STREAM) {
+ ALOGI("*** %s reached end of stream", __PRETTY_FUNCTION__);
+ notifyListener_l(MEDIA_BUFFERING_UPDATE, 100);
+ notifyListener_l(MEDIA_PLAYBACK_COMPLETE);
+ pause_l(false);
+ sendEOS_l();
+ } else {
+ ALOGE("*** %s read failed err=%d", __PRETTY_FUNCTION__, err);
+ }
+ return;
+ }
+
+ if (mIsSeeking) {
+ mIsSeeking = false;
+ notifyListener_l(MEDIA_SEEK_COMPLETE);
+ }
+
+ uint8_t* data = (static_cast<uint8_t*>(mediaBuffer->data()) +
+ mediaBuffer->range_offset());
+ ALOGV("*** %s got media buffer data=[%02hhx %02hhx %02hhx %02hhx]"
+ " offset=%d length=%d", __PRETTY_FUNCTION__,
+ data[0], data[1], data[2], data[3],
+ mediaBuffer->range_offset(), mediaBuffer->range_length());
+
+ int64_t mediaTimeUs;
+ CHECK(mediaBuffer->meta_data()->findInt64(kKeyTime, &mediaTimeUs));
+ ALOGV("*** timeUs=%lld", mediaTimeUs);
+
+ if (!mCurrentClockTransformValid) {
+ if (OK == mCCHelper.getCommonTime(&commonTimeNow)) {
+ mCurrentClockTransform.a_zero = mediaTimeUs;
+ mCurrentClockTransform.b_zero = commonTimeNow +
+ kAAHStartupLeadTimeUs;
+ mCurrentClockTransform.a_to_b_numer = 1;
+ mCurrentClockTransform.a_to_b_denom = mPlayRateIsPaused ? 0 : 1;
+ mCurrentClockTransformValid = true;
+ } else {
+ // Failed to get common time; either the service is down or
+ // common time is not synced. Raise an error and shutdown the
+ // player.
+ ALOGE("*** Cannot begin transmission, unable to fetch common"
+ " time. Dropping sample with pts=%lld", mediaTimeUs);
+ notifyListener_l(MEDIA_ERROR,
+ MEDIA_ERROR_UNKNOWN,
+ UNKNOWN_ERROR);
+ mPumpAudioEventPending = false;
+ break;
+ }
+ }
+
+ ALOGV("*** transmitting packet with pts=%lld", mediaTimeUs);
+
+ sp<TRTPAudioPacket> packet = new TRTPAudioPacket();
+ packet->setPTS(mediaTimeUs);
+ packet->setSubstreamID(1);
+
+ packet->setCodecType(mAudioCodec);
+ packet->setVolume(mTRTPVolume);
+ // TODO : introduce a throttle for this so we can control the
+ // frequency with which transforms get sent.
+ packet->setClockTransform(mCurrentClockTransform);
+ packet->setAccessUnitData(data, mediaBuffer->range_length());
+
+ // TODO : while its pretty much universally true that audio ES payloads
+ // are all RAPs across all codecs, it might be a good idea to throttle
+ // the frequency with which we send codec out of band data to the RXers.
+ // If/when we do, we need to flag only those payloads which have
+ // required out of band data attached to them as RAPs.
+ packet->setRandomAccessPoint(true);
+
+ if (mAudioCodecData && mAudioCodecDataSize) {
+ packet->setAuxData(mAudioCodecData, mAudioCodecDataSize);
+ }
+
+ queuePacketToSender_l(packet);
+ mediaBuffer->release();
+
+ mLastQueuedMediaTimePTSValid = true;
+ mLastQueuedMediaTimePTS = mediaTimeUs;
+ }
+
+ { // Explicit scope for the autolock pattern.
+ Mutex::Autolock autoLock(mLock);
+
+ // If someone externally has cleared this flag, its because we should be
+ // shutting down. Do not reschedule ourselves.
+ if (!mPumpAudioEventPending) {
+ return;
+ }
+
+ // Looks like no one canceled us explicitly. Clear our flag and post a
+ // new event to ourselves.
+ mPumpAudioEventPending = false;
+ postPumpAudioEvent_l(10000);
+ }
+}
+
+void AAH_TXPlayer::queuePacketToSender_l(const sp<TRTPPacket>& packet) {
+ if (mAAH_Sender == NULL) {
+ return;
+ }
+
+ sp<AMessage> message = new AMessage(AAH_TXSender::kWhatSendPacket,
+ mAAH_Sender->handlerID());
+
+ {
+ Mutex::Autolock lock(mEndpointLock);
+ if (!mEndpointValid) {
+ return;
+ }
+
+ message->setInt32(AAH_TXSender::kSendPacketIPAddr, mEndpoint.addr);
+ message->setInt32(AAH_TXSender::kSendPacketPort, mEndpoint.port);
+ }
+
+ packet->setProgramID(mProgramID);
+ packet->setExpireTime(systemTime() + kAAHRetryKeepAroundTimeNs);
+ packet->pack();
+
+ message->setObject(AAH_TXSender::kSendPacketTRTPPacket, packet);
+
+ message->post();
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_tx_player.h b/media/libaah_rtp/aah_tx_player.h
new file mode 100644
index 0000000..2e4b1f7
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_player.h
@@ -0,0 +1,176 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAH_TX_PLAYER_H__
+#define __AAH_TX_PLAYER_H__
+
+#include <common_time/cc_helper.h>
+#include <libstagefright/include/HTTPBase.h>
+#include <libstagefright/include/NuCachedSource2.h>
+#include <libstagefright/include/TimedEventQueue.h>
+#include <media/MediaPlayerInterface.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <utils/LinearTransform.h>
+#include <utils/String8.h>
+#include <utils/threads.h>
+
+#include "aah_tx_sender.h"
+
+namespace android {
+
+class AAH_TXPlayer : public MediaPlayerHWInterface {
+ public:
+ AAH_TXPlayer();
+
+ virtual status_t initCheck();
+ virtual status_t setDataSource(const char *url,
+ const KeyedVector<String8, String8>*
+ headers);
+ virtual status_t setDataSource(int fd, int64_t offset, int64_t length);
+ virtual status_t setVideoSurface(const sp<Surface>& surface);
+ virtual status_t setVideoSurfaceTexture(const sp<ISurfaceTexture>&
+ surfaceTexture);
+ virtual status_t prepare();
+ virtual status_t prepareAsync();
+ virtual status_t start();
+ virtual status_t stop();
+ virtual status_t pause();
+ virtual bool isPlaying();
+ virtual status_t seekTo(int msec);
+ virtual status_t getCurrentPosition(int *msec);
+ virtual status_t getDuration(int *msec);
+ virtual status_t reset();
+ virtual status_t setLooping(int loop);
+ virtual player_type playerType();
+ virtual status_t setParameter(int key, const Parcel &request);
+ virtual status_t getParameter(int key, Parcel *reply);
+ virtual status_t invoke(const Parcel& request, Parcel *reply);
+ virtual status_t getMetadata(const media::Metadata::Filter& ids,
+ Parcel* records);
+ virtual status_t setVolume(float leftVolume, float rightVolume);
+ virtual status_t setAudioStreamType(audio_stream_type_t streamType);
+ virtual status_t setRetransmitEndpoint(
+ const struct sockaddr_in* endpoint);
+
+ static const int64_t kAAHRetryKeepAroundTimeNs;
+
+ protected:
+ virtual ~AAH_TXPlayer();
+
+ private:
+ friend struct AwesomeEvent;
+
+ enum {
+ PLAYING = 1,
+ PREPARING = 8,
+ PREPARED = 16,
+ PREPARE_CANCELLED = 64,
+ CACHE_UNDERRUN = 128,
+
+ // We are basically done preparing but are currently buffering
+ // sufficient data to begin playback and finish the preparation
+ // phase for good.
+ PREPARING_CONNECTED = 2048,
+
+ INCOGNITO = 32768,
+ };
+
+ status_t setDataSource_l(const char *url,
+ const KeyedVector<String8, String8> *headers);
+ status_t setDataSource_l(const sp<MediaExtractor>& extractor);
+ status_t finishSetDataSource_l();
+ status_t prepareAsync_l();
+ void onPrepareAsyncEvent();
+ void finishAsyncPrepare_l();
+ void abortPrepare(status_t err);
+ status_t play_l();
+ status_t pause_l(bool doClockUpdate = true);
+ status_t seekTo_l(int64_t timeUs);
+ void updateClockTransform_l(bool pause);
+ void sendEOS_l();
+ void cancelPlayerEvents(bool keepBufferingGoing = false);
+ void reset_l();
+ void notifyListener_l(int msg, int ext1 = 0, int ext2 = 0);
+ bool getBitrate_l(int64_t* bitrate);
+ status_t getDuration_l(int* msec);
+ bool getCachedDuration_l(int64_t* durationUs, bool* eos);
+ void ensureCacheIsFetching_l();
+ void postBufferingEvent_l();
+ void postPumpAudioEvent_l(int64_t delayUs);
+ void onBufferingUpdate();
+ void onPumpAudio();
+ void queuePacketToSender_l(const sp<TRTPPacket>& packet);
+
+ Mutex mLock;
+
+ TimedEventQueue mQueue;
+ bool mQueueStarted;
+
+ sp<TimedEventQueue::Event> mBufferingEvent;
+ bool mBufferingEventPending;
+
+ uint32_t mFlags;
+ uint32_t mExtractorFlags;
+
+ String8 mUri;
+ KeyedVector<String8, String8> mUriHeaders;
+
+ sp<DataSource> mFileSource;
+
+ sp<TimedEventQueue::Event> mAsyncPrepareEvent;
+ Condition mPreparedCondition;
+ status_t mPrepareResult;
+
+ bool mIsSeeking;
+ int64_t mSeekTimeUs;
+
+ sp<TimedEventQueue::Event> mPumpAudioEvent;
+ bool mPumpAudioEventPending;
+
+ sp<HTTPBase> mConnectingDataSource;
+ sp<NuCachedSource2> mCachedSource;
+
+ sp<MediaSource> mAudioSource;
+ TRTPAudioPacket::TRTPAudioCodecType mAudioCodec;
+ sp<MetaData> mAudioFormat;
+ uint8_t* mAudioCodecData;
+ size_t mAudioCodecDataSize;
+
+ int64_t mDurationUs;
+ int64_t mBitrate;
+
+ sp<AAH_TXSender> mAAH_Sender;
+ LinearTransform mCurrentClockTransform;
+ bool mCurrentClockTransformValid;
+ int64_t mLastQueuedMediaTimePTS;
+ bool mLastQueuedMediaTimePTSValid;
+ bool mPlayRateIsPaused;
+ CCHelper mCCHelper;
+
+ Mutex mEndpointLock;
+ AAH_TXSender::Endpoint mEndpoint;
+ bool mEndpointValid;
+ bool mEndpointRegistered;
+ uint16_t mProgramID;
+ uint8_t mTRTPVolume;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AAH_TXPlayer);
+};
+
+} // namespace android
+
+#endif // __AAH_TX_PLAYER_H__
diff --git a/media/libaah_rtp/aah_tx_sender.cpp b/media/libaah_rtp/aah_tx_sender.cpp
new file mode 100644
index 0000000..08e32d2
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_sender.cpp
@@ -0,0 +1,603 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <media/stagefright/foundation/ADebug.h>
+
+#include <netinet/in.h>
+#include <poll.h>
+#include <sys/types.h>
+#include <sys/socket.h>
+#include <unistd.h>
+
+#include <media/stagefright/foundation/AMessage.h>
+#include <utils/misc.h>
+
+#include "aah_tx_player.h"
+#include "aah_tx_sender.h"
+
+namespace android {
+
+const char* AAH_TXSender::kSendPacketIPAddr = "ipaddr";
+const char* AAH_TXSender::kSendPacketPort = "port";
+const char* AAH_TXSender::kSendPacketTRTPPacket = "trtp";
+
+const int AAH_TXSender::kRetryTrimIntervalUs = 100000;
+const int AAH_TXSender::kHeartbeatIntervalUs = 1000000;
+const int AAH_TXSender::kRetryBufferCapacity = 100;
+const nsecs_t AAH_TXSender::kHeartbeatTimeout = 600ull * 1000000000ull;
+
+Mutex AAH_TXSender::sLock;
+wp<AAH_TXSender> AAH_TXSender::sInstance;
+uint32_t AAH_TXSender::sNextEpoch;
+bool AAH_TXSender::sNextEpochValid = false;
+
+AAH_TXSender::AAH_TXSender() : mSocket(-1) {
+ mLastSentPacketTime = systemTime();
+}
+
+sp<AAH_TXSender> AAH_TXSender::GetInstance() {
+ Mutex::Autolock autoLock(sLock);
+
+ sp<AAH_TXSender> sender = sInstance.promote();
+
+ if (sender == NULL) {
+ sender = new AAH_TXSender();
+ if (sender == NULL) {
+ return NULL;
+ }
+
+ sender->mLooper = new ALooper();
+ if (sender->mLooper == NULL) {
+ return NULL;
+ }
+
+ sender->mReflector = new AHandlerReflector<AAH_TXSender>(sender.get());
+ if (sender->mReflector == NULL) {
+ return NULL;
+ }
+
+ sender->mSocket = socket(AF_INET, SOCK_DGRAM, IPPROTO_UDP);
+ if (sender->mSocket == -1) {
+ ALOGW("%s unable to create socket", __PRETTY_FUNCTION__);
+ return NULL;
+ }
+
+ struct sockaddr_in bind_addr;
+ memset(&bind_addr, 0, sizeof(bind_addr));
+ bind_addr.sin_family = AF_INET;
+ if (bind(sender->mSocket,
+ reinterpret_cast<const sockaddr*>(&bind_addr),
+ sizeof(bind_addr)) < 0) {
+ ALOGW("%s unable to bind socket (errno %d)",
+ __PRETTY_FUNCTION__, errno);
+ return NULL;
+ }
+
+ sender->mRetryReceiver = new RetryReceiver(sender.get());
+ if (sender->mRetryReceiver == NULL) {
+ return NULL;
+ }
+
+ sender->mLooper->setName("AAH_TXSender");
+ sender->mLooper->registerHandler(sender->mReflector);
+ sender->mLooper->start(false, false, PRIORITY_AUDIO);
+
+ if (sender->mRetryReceiver->run("AAH_TXSenderRetry", PRIORITY_AUDIO)
+ != OK) {
+ ALOGW("%s unable to start retry thread", __PRETTY_FUNCTION__);
+ return NULL;
+ }
+
+ sInstance = sender;
+ }
+
+ return sender;
+}
+
+AAH_TXSender::~AAH_TXSender() {
+ mLooper->stop();
+ mLooper->unregisterHandler(mReflector->id());
+
+ if (mRetryReceiver != NULL) {
+ mRetryReceiver->requestExit();
+ mRetryReceiver->mWakeupEvent.setEvent();
+ if (mRetryReceiver->requestExitAndWait() != OK) {
+ ALOGW("%s shutdown of retry receiver failed", __PRETTY_FUNCTION__);
+ }
+ mRetryReceiver->mSender = NULL;
+ mRetryReceiver.clear();
+ }
+
+ if (mSocket != -1) {
+ close(mSocket);
+ }
+}
+
+// Return the next epoch number usable for a newly instantiated endpoint.
+uint32_t AAH_TXSender::getNextEpoch() {
+ Mutex::Autolock autoLock(sLock);
+
+ if (sNextEpochValid) {
+ sNextEpoch = (sNextEpoch + 1) & TRTPPacket::kTRTPEpochMask;
+ } else {
+ sNextEpoch = ns2ms(systemTime()) & TRTPPacket::kTRTPEpochMask;
+ sNextEpochValid = true;
+ }
+
+ return sNextEpoch;
+}
+
+// Notify the sender that a player has started sending to this endpoint.
+// Returns a program ID for use by the calling player.
+uint16_t AAH_TXSender::registerEndpoint(const Endpoint& endpoint) {
+ Mutex::Autolock lock(mEndpointLock);
+
+ EndpointState* eps = mEndpointMap.valueFor(endpoint);
+ if (eps) {
+ eps->playerRefCount++;
+ } else {
+ eps = new EndpointState(getNextEpoch());
+ mEndpointMap.add(endpoint, eps);
+ }
+
+ // if this is the first registered endpoint, then send a message to start
+ // trimming retry buffers and a message to start sending heartbeats.
+ if (mEndpointMap.size() == 1) {
+ sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers,
+ handlerID());
+ trimMessage->post(kRetryTrimIntervalUs);
+
+ sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats,
+ handlerID());
+ heartbeatMessage->post(kHeartbeatIntervalUs);
+ }
+
+ eps->nextProgramID++;
+ return eps->nextProgramID;
+}
+
+// Notify the sender that a player has ceased sending to this endpoint.
+// An endpoint's state can not be deleted until all of the endpoint's
+// registered players have called unregisterEndpoint.
+void AAH_TXSender::unregisterEndpoint(const Endpoint& endpoint) {
+ Mutex::Autolock lock(mEndpointLock);
+
+ EndpointState* eps = mEndpointMap.valueFor(endpoint);
+ if (eps) {
+ eps->playerRefCount--;
+ CHECK(eps->playerRefCount >= 0);
+ }
+}
+
+void AAH_TXSender::onMessageReceived(const sp<AMessage>& msg) {
+ switch (msg->what()) {
+ case kWhatSendPacket:
+ onSendPacket(msg);
+ break;
+
+ case kWhatTrimRetryBuffers:
+ trimRetryBuffers();
+ break;
+
+ case kWhatSendHeartbeats:
+ sendHeartbeats();
+ break;
+
+ default:
+ TRESPASS();
+ break;
+ }
+}
+
+void AAH_TXSender::onSendPacket(const sp<AMessage>& msg) {
+ sp<RefBase> obj;
+ CHECK(msg->findObject(kSendPacketTRTPPacket, &obj));
+ sp<TRTPPacket> packet = static_cast<TRTPPacket*>(obj.get());
+
+ uint32_t ipAddr;
+ CHECK(msg->findInt32(kSendPacketIPAddr,
+ reinterpret_cast<int32_t*>(&ipAddr)));
+
+ int32_t port32;
+ CHECK(msg->findInt32(kSendPacketPort, &port32));
+ uint16_t port = port32;
+
+ Mutex::Autolock lock(mEndpointLock);
+ doSendPacket_l(packet, Endpoint(ipAddr, port));
+ mLastSentPacketTime = systemTime();
+}
+
+void AAH_TXSender::doSendPacket_l(const sp<TRTPPacket>& packet,
+ const Endpoint& endpoint) {
+ EndpointState* eps = mEndpointMap.valueFor(endpoint);
+ if (!eps) {
+ // the endpoint state has disappeared, so the player that sent this
+ // packet must be dead.
+ return;
+ }
+
+ // assign the packet's sequence number
+ packet->setEpoch(eps->epoch);
+ packet->setSeqNumber(eps->trtpSeqNumber++);
+
+ // add the packet to the retry buffer
+ RetryBuffer& retry = eps->retry;
+ retry.push_back(packet);
+
+ // send the packet
+ struct sockaddr_in addr;
+ memset(&addr, 0, sizeof(addr));
+ addr.sin_family = AF_INET;
+ addr.sin_addr.s_addr = endpoint.addr;
+ addr.sin_port = endpoint.port;
+
+ ssize_t result = sendto(mSocket,
+ packet->getPacket(),
+ packet->getPacketLen(),
+ 0,
+ (const struct sockaddr *) &addr,
+ sizeof(addr));
+ if (result == -1) {
+ ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+ }
+}
+
+void AAH_TXSender::trimRetryBuffers() {
+ Mutex::Autolock lock(mEndpointLock);
+
+ nsecs_t localTimeNow = systemTime();
+
+ Vector<Endpoint> endpointsToRemove;
+
+ for (size_t i = 0; i < mEndpointMap.size(); i++) {
+ EndpointState* eps = mEndpointMap.editValueAt(i);
+ RetryBuffer& retry = eps->retry;
+
+ while (!retry.isEmpty()) {
+ if (retry[0]->getExpireTime() < localTimeNow) {
+ retry.pop_front();
+ } else {
+ break;
+ }
+ }
+
+ if (retry.isEmpty() && eps->playerRefCount == 0) {
+ endpointsToRemove.add(mEndpointMap.keyAt(i));
+ }
+ }
+
+ // remove the state for any endpoints that are no longer in use
+ for (size_t i = 0; i < endpointsToRemove.size(); i++) {
+ Endpoint& e = endpointsToRemove.editItemAt(i);
+ ALOGD("*** %s removing endpoint addr=%08x",
+ __PRETTY_FUNCTION__, e.addr);
+ size_t index = mEndpointMap.indexOfKey(e);
+ delete mEndpointMap.valueAt(index);
+ mEndpointMap.removeItemsAt(index);
+ }
+
+ // schedule the next trim
+ if (mEndpointMap.size()) {
+ sp<AMessage> trimMessage = new AMessage(kWhatTrimRetryBuffers,
+ handlerID());
+ trimMessage->post(kRetryTrimIntervalUs);
+ }
+}
+
+void AAH_TXSender::sendHeartbeats() {
+ Mutex::Autolock lock(mEndpointLock);
+
+ if (shouldSendHeartbeats_l()) {
+ for (size_t i = 0; i < mEndpointMap.size(); i++) {
+ EndpointState* eps = mEndpointMap.editValueAt(i);
+ const Endpoint& ep = mEndpointMap.keyAt(i);
+
+ sp<TRTPControlPacket> packet = new TRTPControlPacket();
+ packet->setCommandID(TRTPControlPacket::kCommandNop);
+
+ packet->setExpireTime(systemTime() +
+ AAH_TXPlayer::kAAHRetryKeepAroundTimeNs);
+ packet->pack();
+
+ doSendPacket_l(packet, ep);
+ }
+ }
+
+ // schedule the next heartbeat
+ if (mEndpointMap.size()) {
+ sp<AMessage> heartbeatMessage = new AMessage(kWhatSendHeartbeats,
+ handlerID());
+ heartbeatMessage->post(kHeartbeatIntervalUs);
+ }
+}
+
+bool AAH_TXSender::shouldSendHeartbeats_l() {
+ // assert(holding endpoint lock)
+ return (systemTime() < (mLastSentPacketTime + kHeartbeatTimeout));
+}
+
+// Receiver
+
+// initial 4-byte ID of a retry request packet
+const uint32_t AAH_TXSender::RetryReceiver::kRetryRequestID = 'Treq';
+
+// initial 4-byte ID of a retry NAK packet
+const uint32_t AAH_TXSender::RetryReceiver::kRetryNakID = 'Tnak';
+
+// initial 4-byte ID of a fast start request packet
+const uint32_t AAH_TXSender::RetryReceiver::kFastStartRequestID = 'Tfst';
+
+AAH_TXSender::RetryReceiver::RetryReceiver(AAH_TXSender* sender)
+ : Thread(false),
+ mSender(sender) {}
+
+ AAH_TXSender::RetryReceiver::~RetryReceiver() {
+ mWakeupEvent.clearPendingEvents();
+ }
+
+// Returns true if val is within the interval bounded inclusively by
+// start and end. Also handles the case where there is a rollover of the
+// range between start and end.
+template <typename T>
+static inline bool withinIntervalWithRollover(T val, T start, T end) {
+ return ((start <= end && val >= start && val <= end) ||
+ (start > end && (val >= start || val <= end)));
+}
+
+bool AAH_TXSender::RetryReceiver::threadLoop() {
+ struct pollfd pollFds[2];
+ pollFds[0].fd = mSender->mSocket;
+ pollFds[0].events = POLLIN;
+ pollFds[0].revents = 0;
+ pollFds[1].fd = mWakeupEvent.getWakeupHandle();
+ pollFds[1].events = POLLIN;
+ pollFds[1].revents = 0;
+
+ int pollResult = poll(pollFds, NELEM(pollFds), -1);
+ if (pollResult == -1) {
+ ALOGE("%s poll failed", __PRETTY_FUNCTION__);
+ return false;
+ }
+
+ if (exitPending()) {
+ ALOGI("*** %s exiting", __PRETTY_FUNCTION__);
+ return false;
+ }
+
+ if (pollFds[0].revents) {
+ handleRetryRequest();
+ }
+
+ return true;
+}
+
+void AAH_TXSender::RetryReceiver::handleRetryRequest() {
+ ALOGV("*** RX %s start", __PRETTY_FUNCTION__);
+
+ RetryPacket request;
+ struct sockaddr requestSrcAddr;
+ socklen_t requestSrcAddrLen = sizeof(requestSrcAddr);
+
+ ssize_t result = recvfrom(mSender->mSocket, &request, sizeof(request), 0,
+ &requestSrcAddr, &requestSrcAddrLen);
+ if (result == -1) {
+ ALOGE("%s recvfrom failed, errno=%d", __PRETTY_FUNCTION__, errno);
+ return;
+ }
+
+ if (static_cast<size_t>(result) < sizeof(RetryPacket)) {
+ ALOGW("%s short packet received", __PRETTY_FUNCTION__);
+ return;
+ }
+
+ uint32_t host_request_id = ntohl(request.id);
+ if ((host_request_id != kRetryRequestID) &&
+ (host_request_id != kFastStartRequestID)) {
+ ALOGW("%s received retry request with bogus ID (%08x)",
+ __PRETTY_FUNCTION__, host_request_id);
+ return;
+ }
+
+ Endpoint endpoint(request.endpointIP, request.endpointPort);
+
+ Mutex::Autolock lock(mSender->mEndpointLock);
+
+ EndpointState* eps = mSender->mEndpointMap.valueFor(endpoint);
+
+ if (eps == NULL || eps->retry.isEmpty()) {
+ // we have no retry buffer or an empty retry buffer for this endpoint,
+ // so NAK the entire request
+ RetryPacket nak = request;
+ nak.id = htonl(kRetryNakID);
+ result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+ &requestSrcAddr, requestSrcAddrLen);
+ if (result == -1) {
+ ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+ }
+ return;
+ }
+
+ RetryBuffer& retry = eps->retry;
+
+ uint16_t startSeq = ntohs(request.seqStart);
+ uint16_t endSeq = ntohs(request.seqEnd);
+
+ uint16_t retryFirstSeq = retry[0]->getSeqNumber();
+ uint16_t retryLastSeq = retry[retry.size() - 1]->getSeqNumber();
+
+ // If this is a fast start, then force the start of the retry to match the
+ // start of the retransmit ring buffer (unless the end of the retransmit
+ // ring buffer is already past the point of fast start)
+ if ((host_request_id == kFastStartRequestID) &&
+ !((startSeq - retryFirstSeq) & 0x8000)) {
+ startSeq = retryFirstSeq;
+ }
+
+ int startIndex;
+ if (withinIntervalWithRollover(startSeq, retryFirstSeq, retryLastSeq)) {
+ startIndex = static_cast<uint16_t>(startSeq - retryFirstSeq);
+ } else {
+ startIndex = -1;
+ }
+
+ int endIndex;
+ if (withinIntervalWithRollover(endSeq, retryFirstSeq, retryLastSeq)) {
+ endIndex = static_cast<uint16_t>(endSeq - retryFirstSeq);
+ } else {
+ endIndex = -1;
+ }
+
+ if (startIndex == -1 && endIndex == -1) {
+ // no part of the request range is found in the retry buffer
+ RetryPacket nak = request;
+ nak.id = htonl(kRetryNakID);
+ result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+ &requestSrcAddr, requestSrcAddrLen);
+ if (result == -1) {
+ ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+ }
+ return;
+ }
+
+ if (startIndex == -1) {
+ // NAK a subrange at the front of the request range
+ RetryPacket nak = request;
+ nak.id = htonl(kRetryNakID);
+ nak.seqEnd = htons(retryFirstSeq - 1);
+ result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+ &requestSrcAddr, requestSrcAddrLen);
+ if (result == -1) {
+ ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+ }
+
+ startIndex = 0;
+ } else if (endIndex == -1) {
+ // NAK a subrange at the back of the request range
+ RetryPacket nak = request;
+ nak.id = htonl(kRetryNakID);
+ nak.seqStart = htons(retryLastSeq + 1);
+ result = sendto(mSender->mSocket, &nak, sizeof(nak), 0,
+ &requestSrcAddr, requestSrcAddrLen);
+ if (result == -1) {
+ ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+ }
+
+ endIndex = retry.size() - 1;
+ }
+
+ // send the retry packets
+ for (int i = startIndex; i <= endIndex; i++) {
+ const sp<TRTPPacket>& replyPacket = retry[i];
+
+ result = sendto(mSender->mSocket,
+ replyPacket->getPacket(),
+ replyPacket->getPacketLen(),
+ 0,
+ &requestSrcAddr,
+ requestSrcAddrLen);
+
+ if (result == -1) {
+ ALOGW("%s sendto failed", __PRETTY_FUNCTION__);
+ }
+ }
+}
+
+// Endpoint
+
+AAH_TXSender::Endpoint::Endpoint()
+ : addr(0)
+ , port(0) { }
+
+AAH_TXSender::Endpoint::Endpoint(uint32_t a, uint16_t p)
+ : addr(a)
+ , port(p) {}
+
+bool AAH_TXSender::Endpoint::operator<(const Endpoint& other) const {
+ return ((addr < other.addr) ||
+ (addr == other.addr && port < other.port));
+}
+
+// EndpointState
+
+AAH_TXSender::EndpointState::EndpointState(uint32_t _epoch)
+ : retry(kRetryBufferCapacity)
+ , playerRefCount(1)
+ , trtpSeqNumber(0)
+ , nextProgramID(0)
+ , epoch(_epoch) { }
+
+// CircularBuffer
+
+template <typename T>
+CircularBuffer<T>::CircularBuffer(size_t capacity)
+ : mCapacity(capacity)
+ , mHead(0)
+ , mTail(0)
+ , mFillCount(0) {
+ mBuffer = new T[capacity];
+}
+
+template <typename T>
+CircularBuffer<T>::~CircularBuffer() {
+ delete [] mBuffer;
+}
+
+template <typename T>
+void CircularBuffer<T>::push_back(const T& item) {
+ if (this->isFull()) {
+ this->pop_front();
+ }
+ mBuffer[mHead] = item;
+ mHead = (mHead + 1) % mCapacity;
+ mFillCount++;
+}
+
+template <typename T>
+void CircularBuffer<T>::pop_front() {
+ CHECK(!isEmpty());
+ mBuffer[mTail] = T();
+ mTail = (mTail + 1) % mCapacity;
+ mFillCount--;
+}
+
+template <typename T>
+size_t CircularBuffer<T>::size() const {
+ return mFillCount;
+}
+
+template <typename T>
+bool CircularBuffer<T>::isFull() const {
+ return (mFillCount == mCapacity);
+}
+
+template <typename T>
+bool CircularBuffer<T>::isEmpty() const {
+ return (mFillCount == 0);
+}
+
+template <typename T>
+const T& CircularBuffer<T>::itemAt(size_t index) const {
+ CHECK(index < mFillCount);
+ return mBuffer[(mTail + index) % mCapacity];
+}
+
+template <typename T>
+const T& CircularBuffer<T>::operator[](size_t index) const {
+ return itemAt(index);
+}
+
+} // namespace android
diff --git a/media/libaah_rtp/aah_tx_sender.h b/media/libaah_rtp/aah_tx_sender.h
new file mode 100644
index 0000000..74206c4
--- /dev/null
+++ b/media/libaah_rtp/aah_tx_sender.h
@@ -0,0 +1,162 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __AAH_TX_SENDER_H__
+#define __AAH_TX_SENDER_H__
+
+#include <media/stagefright/foundation/ALooper.h>
+#include <media/stagefright/foundation/AHandlerReflector.h>
+#include <utils/RefBase.h>
+#include <utils/threads.h>
+
+#include "aah_tx_packet.h"
+#include "pipe_event.h"
+
+namespace android {
+
+template <typename T> class CircularBuffer {
+ public:
+ CircularBuffer(size_t capacity);
+ ~CircularBuffer();
+ void push_back(const T& item);;
+ void pop_front();
+ size_t size() const;
+ bool isFull() const;
+ bool isEmpty() const;
+ const T& itemAt(size_t index) const;
+ const T& operator[](size_t index) const;
+
+ private:
+ T* mBuffer;
+ size_t mCapacity;
+ size_t mHead;
+ size_t mTail;
+ size_t mFillCount;
+
+ DISALLOW_EVIL_CONSTRUCTORS(CircularBuffer);
+};
+
+class AAH_TXSender : public virtual RefBase {
+ public:
+ ~AAH_TXSender();
+
+ static sp<AAH_TXSender> GetInstance();
+
+ ALooper::handler_id handlerID() { return mReflector->id(); }
+
+ // an IP address and port
+ struct Endpoint {
+ Endpoint();
+ Endpoint(uint32_t a, uint16_t p);
+ bool operator<(const Endpoint& other) const;
+
+ uint32_t addr;
+ uint16_t port;
+ };
+
+ uint16_t registerEndpoint(const Endpoint& endpoint);
+ void unregisterEndpoint(const Endpoint& endpoint);
+
+ enum {
+ kWhatSendPacket,
+ kWhatTrimRetryBuffers,
+ kWhatSendHeartbeats,
+ };
+
+ // fields for SendPacket messages
+ static const char* kSendPacketIPAddr;
+ static const char* kSendPacketPort;
+ static const char* kSendPacketTRTPPacket;
+
+ private:
+ AAH_TXSender();
+
+ static Mutex sLock;
+ static wp<AAH_TXSender> sInstance;
+ static uint32_t sNextEpoch;
+ static bool sNextEpochValid;
+
+ static uint32_t getNextEpoch();
+
+ typedef CircularBuffer<sp<TRTPPacket> > RetryBuffer;
+
+ // state maintained on a per-endpoint basis
+ struct EndpointState {
+ EndpointState(uint32_t epoch);
+ RetryBuffer retry;
+ int playerRefCount;
+ uint16_t trtpSeqNumber;
+ uint16_t nextProgramID;
+ uint32_t epoch;
+ };
+
+ friend class AHandlerReflector<AAH_TXSender>;
+ void onMessageReceived(const sp<AMessage>& msg);
+ void onSendPacket(const sp<AMessage>& msg);
+ void doSendPacket_l(const sp<TRTPPacket>& packet,
+ const Endpoint& endpoint);
+ void trimRetryBuffers();
+ void sendHeartbeats();
+ bool shouldSendHeartbeats_l();
+
+ sp<ALooper> mLooper;
+ sp<AHandlerReflector<AAH_TXSender> > mReflector;
+
+ int mSocket;
+ nsecs_t mLastSentPacketTime;
+
+ DefaultKeyedVector<Endpoint, EndpointState*> mEndpointMap;
+ Mutex mEndpointLock;
+
+ static const int kRetryTrimIntervalUs;
+ static const int kHeartbeatIntervalUs;
+ static const int kRetryBufferCapacity;
+ static const nsecs_t kHeartbeatTimeout;
+
+ class RetryReceiver : public Thread {
+ private:
+ friend class AAH_TXSender;
+
+ RetryReceiver(AAH_TXSender* sender);
+ virtual ~RetryReceiver();
+ virtual bool threadLoop();
+ void handleRetryRequest();
+
+ static const int kMaxReceiverPacketLen;
+ static const uint32_t kRetryRequestID;
+ static const uint32_t kFastStartRequestID;
+ static const uint32_t kRetryNakID;
+
+ AAH_TXSender* mSender;
+ PipeEvent mWakeupEvent;
+ };
+
+ sp<RetryReceiver> mRetryReceiver;
+
+ DISALLOW_EVIL_CONSTRUCTORS(AAH_TXSender);
+};
+
+struct RetryPacket {
+ uint32_t id;
+ uint32_t endpointIP;
+ uint16_t endpointPort;
+ uint16_t seqStart;
+ uint16_t seqEnd;
+} __attribute__((packed));
+
+} // namespace android
+
+#endif // __AAH_TX_SENDER_H__
diff --git a/media/libaah_rtp/pipe_event.cpp b/media/libaah_rtp/pipe_event.cpp
new file mode 100644
index 0000000..b8e6960
--- /dev/null
+++ b/media/libaah_rtp/pipe_event.cpp
@@ -0,0 +1,86 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#define LOG_TAG "LibAAH_RTP"
+#include <utils/Log.h>
+
+#include <errno.h>
+#include <fcntl.h>
+#include <poll.h>
+#include <unistd.h>
+
+#include "pipe_event.h"
+
+namespace android {
+
+PipeEvent::PipeEvent() {
+ pipe_[0] = -1;
+ pipe_[1] = -1;
+
+ // Create the pipe.
+ if (pipe(pipe_) >= 0) {
+ // Set non-blocking mode on the read side of the pipe so we can
+ // easily drain it whenever we wakeup.
+ fcntl(pipe_[0], F_SETFL, O_NONBLOCK);
+ } else {
+ ALOGE("Failed to create pipe event %d %d %d",
+ pipe_[0], pipe_[1], errno);
+ pipe_[0] = -1;
+ pipe_[1] = -1;
+ }
+}
+
+PipeEvent::~PipeEvent() {
+ if (pipe_[0] >= 0) {
+ close(pipe_[0]);
+ }
+
+ if (pipe_[1] >= 0) {
+ close(pipe_[1]);
+ }
+}
+
+void PipeEvent::clearPendingEvents() {
+ char drain_buffer[16];
+ while (read(pipe_[0], drain_buffer, sizeof(drain_buffer)) > 0) {
+ // No body.
+ }
+}
+
+bool PipeEvent::wait(int timeout) {
+ struct pollfd wait_fd;
+
+ wait_fd.fd = getWakeupHandle();
+ wait_fd.events = POLLIN;
+ wait_fd.revents = 0;
+
+ int res = poll(&wait_fd, 1, timeout);
+
+ if (res < 0) {
+ ALOGE("Wait error in PipeEvent; sleeping to prevent overload!");
+ usleep(1000);
+ }
+
+ return (res > 0);
+}
+
+void PipeEvent::setEvent() {
+ char foo = 'q';
+ write(pipe_[1], &foo, 1);
+}
+
+} // namespace android
+
diff --git a/media/libaah_rtp/pipe_event.h b/media/libaah_rtp/pipe_event.h
new file mode 100644
index 0000000..e53b0fd
--- /dev/null
+++ b/media/libaah_rtp/pipe_event.h
@@ -0,0 +1,51 @@
+/*
+ * Copyright (C) 2011 The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+#ifndef __PIPE_EVENT_H__
+#define __PIPE_EVENT_H__
+
+#include <media/stagefright/foundation/ABase.h>
+
+namespace android {
+
+class PipeEvent {
+ public:
+ PipeEvent();
+ ~PipeEvent();
+
+ bool initCheck() const {
+ return ((pipe_[0] >= 0) && (pipe_[1] >= 0));
+ }
+
+ int getWakeupHandle() const { return pipe_[0]; }
+
+ // block until the event fires; returns true if the event fired and false if
+ // the wait timed out. Timeout is expressed in milliseconds; negative
+ // values mean wait forever.
+ bool wait(int timeout = -1);
+
+ void clearPendingEvents();
+ void setEvent();
+
+ private:
+ int pipe_[2];
+
+ DISALLOW_EVIL_CONSTRUCTORS(PipeEvent);
+};
+
+} // namespace android
+
+#endif // __PIPE_EVENT_H__
diff --git a/media/libeffects/data/audio_effects.conf b/media/libeffects/data/audio_effects.conf
index b8fa487..ce25bc8 100644
--- a/media/libeffects/data/audio_effects.conf
+++ b/media/libeffects/data/audio_effects.conf
@@ -50,11 +50,11 @@ effects {
}
volume {
library bundle
- uuid 119341a0-8469-11df-81f9- 0002a5d5c51b
+ uuid 119341a0-8469-11df-81f9-0002a5d5c51b
}
reverb_env_aux {
library reverb
- uuid 4a387fc0-8ab3-11df-8bad- 0002a5d5c51b
+ uuid 4a387fc0-8ab3-11df-8bad-0002a5d5c51b
}
reverb_env_ins {
library reverb
diff --git a/media/libeffects/downmix/Android.mk b/media/libeffects/downmix/Android.mk
new file mode 100644
index 0000000..0348e1e
--- /dev/null
+++ b/media/libeffects/downmix/Android.mk
@@ -0,0 +1,28 @@
+LOCAL_PATH:= $(call my-dir)
+
+# Multichannel downmix effect library
+include $(CLEAR_VARS)
+
+LOCAL_SRC_FILES:= \
+ EffectDownmix.c
+
+LOCAL_SHARED_LIBRARIES := \
+ libcutils
+
+LOCAL_MODULE:= libdownmix
+
+LOCAL_MODULE_TAGS := optional
+
+LOCAL_MODULE_PATH := $(TARGET_OUT_SHARED_LIBRARIES)/soundfx
+
+ifeq ($(TARGET_OS)-$(TARGET_SIMULATOR),linux-true)
+LOCAL_LDLIBS += -ldl
+endif
+
+LOCAL_C_INCLUDES := \
+ system/media/audio_effects/include \
+ system/media/audio_utils/include
+
+LOCAL_PRELINK_MODULE := false
+
+include $(BUILD_SHARED_LIBRARY)
diff --git a/media/libeffects/downmix/EffectDownmix.c b/media/libeffects/downmix/EffectDownmix.c
new file mode 100644
index 0000000..a325172
--- /dev/null
+++ b/media/libeffects/downmix/EffectDownmix.c
@@ -0,0 +1,889 @@
+/*
+ * Copyright (C) 2012 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 "EffectDownmix"
+#define LOG_NDEBUG 0
+#include <cutils/log.h>
+#include <stdlib.h>
+#include <string.h>
+#include <stdbool.h>
+#include "EffectDownmix.h"
+
+#define MINUS_3_DB_IN_Q19_12 2896 // -3dB = 0.707 * 2^12 = 2896
+
+// effect_handle_t interface implementation for downmix effect
+const struct effect_interface_s gDownmixInterface = {
+ Downmix_Process,
+ Downmix_Command,
+ Downmix_GetDescriptor,
+ NULL /* no process_reverse function, no reference stream needed */
+};
+
+audio_effect_library_t AUDIO_EFFECT_LIBRARY_INFO_SYM = {
+ tag : AUDIO_EFFECT_LIBRARY_TAG,
+ version : EFFECT_LIBRARY_API_VERSION,
+ name : "Downmix Library",
+ implementor : "The Android Open Source Project",
+ query_num_effects : DownmixLib_QueryNumberEffects,
+ query_effect : DownmixLib_QueryEffect,
+ create_effect : DownmixLib_Create,
+ release_effect : DownmixLib_Release,
+ get_descriptor : DownmixLib_GetDescriptor,
+};
+
+
+// AOSP insert downmix UUID: 93f04452-e4fe-41cc-91f9-e475b6d1d69f
+static const effect_descriptor_t gDownmixDescriptor = {
+ EFFECT_UIID_DOWNMIX__, //type
+ {0x93f04452, 0xe4fe, 0x41cc, 0x91f9, {0xe4, 0x75, 0xb6, 0xd1, 0xd6, 0x9f}}, // uuid
+ EFFECT_CONTROL_API_VERSION,
+ EFFECT_FLAG_TYPE_INSERT | EFFECT_FLAG_INSERT_FIRST,
+ 0, //FIXME what value should be reported? // cpu load
+ 0, //FIXME what value should be reported? // memory usage
+ "Multichannel Downmix To Stereo", // human readable effect name
+ "The Android Open Source Project" // human readable effect implementor name
+};
+
+// gDescriptors contains pointers to all defined effect descriptor in this library
+static const effect_descriptor_t * const gDescriptors[] = {
+ &gDownmixDescriptor
+};
+
+// number of effects in this library
+const int kNbEffects = sizeof(gDescriptors) / sizeof(const effect_descriptor_t *);
+
+
+/*----------------------------------------------------------------------------
+ * Effect API implementation
+ *--------------------------------------------------------------------------*/
+
+/*--- Effect Library Interface Implementation ---*/
+
+int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects) {
+ ALOGV("DownmixLib_QueryNumberEffects()");
+ *pNumEffects = kNbEffects;
+ return 0;
+}
+
+int32_t DownmixLib_QueryEffect(uint32_t index, effect_descriptor_t *pDescriptor) {
+ ALOGV("DownmixLib_QueryEffect() index=%d", index);
+ if (pDescriptor == NULL) {
+ return -EINVAL;
+ }
+ if (index >= (uint32_t)kNbEffects) {
+ return -EINVAL;
+ }
+ memcpy(pDescriptor, gDescriptors[index], sizeof(effect_descriptor_t));
+ return 0;
+}
+
+
+int32_t DownmixLib_Create(const effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_handle_t *pHandle) {
+ int ret;
+ int i;
+ downmix_module_t *module;
+ const effect_descriptor_t *desc;
+
+ ALOGV("DownmixLib_Create()");
+
+ if (pHandle == NULL || uuid == NULL) {
+ return -EINVAL;
+ }
+
+ for (i = 0 ; i < kNbEffects ; i++) {
+ desc = gDescriptors[i];
+ if (memcmp(uuid, &desc->uuid, sizeof(effect_uuid_t)) == 0) {
+ break;
+ }
+ }
+
+ if (i == kNbEffects) {
+ return -ENOENT;
+ }
+
+ module = malloc(sizeof(downmix_module_t));
+
+ module->itfe = &gDownmixInterface;
+
+ module->context.state = DOWNMIX_STATE_UNINITIALIZED;
+
+ ret = Downmix_Init(module);
+ if (ret < 0) {
+ ALOGW("DownmixLib_Create() init failed");
+ free(module);
+ return ret;
+ }
+
+ *pHandle = (effect_handle_t) module;
+
+ ALOGV("DownmixLib_Create() %p , size %d", module, sizeof(downmix_module_t));
+
+ return 0;
+}
+
+
+int32_t DownmixLib_Release(effect_handle_t handle) {
+ downmix_module_t *pDwmModule = (downmix_module_t *)handle;
+
+ ALOGV("DownmixLib_Release() %p", handle);
+ if (handle == NULL) {
+ return -EINVAL;
+ }
+
+ pDwmModule->context.state = DOWNMIX_STATE_UNINITIALIZED;
+
+ free(pDwmModule);
+ return 0;
+}
+
+
+int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid, effect_descriptor_t *pDescriptor) {
+ ALOGV("DownmixLib_GetDescriptor()");
+ int i;
+
+ if (pDescriptor == NULL || uuid == NULL){
+ ALOGE("DownmixLib_Create() called with NULL pointer");
+ return -EINVAL;
+ }
+ ALOGV("DownmixLib_GetDescriptor() nb effects=%d", kNbEffects);
+ for (i = 0; i < kNbEffects; i++) {
+ ALOGV("DownmixLib_GetDescriptor() i=%d", i);
+ if (memcmp(uuid, &gDescriptors[i]->uuid, sizeof(effect_uuid_t)) == 0) {
+ memcpy(pDescriptor, gDescriptors[i], sizeof(effect_descriptor_t));
+ ALOGV("EffectGetDescriptor - UUID matched downmix type %d, UUID = %x",
+ i, gDescriptors[i]->uuid.timeLow);
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+
+
+/*--- Effect Control Interface Implementation ---*/
+
+static int Downmix_Process(effect_handle_t self,
+ audio_buffer_t *inBuffer, audio_buffer_t *outBuffer) {
+
+ downmix_object_t *pDownmixer;
+ int16_t *pSrc, *pDst;
+ downmix_module_t *pDwmModule = (downmix_module_t *)self;
+
+ if (pDwmModule == NULL) {
+ return -EINVAL;
+ }
+
+ if (inBuffer == NULL || inBuffer->raw == NULL ||
+ outBuffer == NULL || outBuffer->raw == NULL ||
+ inBuffer->frameCount != outBuffer->frameCount) {
+ return -EINVAL;
+ }
+
+ pDownmixer = (downmix_object_t*) &pDwmModule->context;
+
+ if (pDownmixer->state == DOWNMIX_STATE_UNINITIALIZED) {
+ ALOGE("Downmix_Process error: trying to use an uninitialized downmixer");
+ return -EINVAL;
+ } else if (pDownmixer->state == DOWNMIX_STATE_INITIALIZED) {
+ ALOGE("Downmix_Process error: trying to use a non-configured downmixer");
+ return -ENODATA;
+ }
+
+ pSrc = inBuffer->s16;
+ pDst = outBuffer->s16;
+ size_t numFrames = outBuffer->frameCount;
+
+ const bool accumulate =
+ (pDwmModule->config.outputCfg.accessMode == EFFECT_BUFFER_ACCESS_ACCUMULATE);
+
+ switch(pDownmixer->type) {
+
+ case DOWNMIX_TYPE_STRIP:
+ if (accumulate) {
+ while (numFrames) {
+ pDst[0] = clamp16(pDst[0] + pSrc[0]);
+ pDst[1] = clamp16(pDst[1] + pSrc[1]);
+ pSrc += pDownmixer->input_channel_count;
+ pDst += 2;
+ numFrames--;
+ }
+ } else {
+ while (numFrames) {
+ pDst[0] = pSrc[0];
+ pDst[1] = pSrc[1];
+ pSrc += pDownmixer->input_channel_count;
+ pDst += 2;
+ numFrames--;
+ }
+ }
+ break;
+
+ case DOWNMIX_TYPE_FOLD:
+ // optimize for the common formats
+ switch(pDwmModule->config.inputCfg.channels) {
+ case AUDIO_CHANNEL_OUT_QUAD:
+ Downmix_foldFromQuad(pSrc, pDst, numFrames, accumulate);
+ break;
+ case AUDIO_CHANNEL_OUT_SURROUND:
+ Downmix_foldFromSurround(pSrc, pDst, numFrames, accumulate);
+ break;
+ case AUDIO_CHANNEL_OUT_5POINT1:
+ Downmix_foldFrom5Point1(pSrc, pDst, numFrames, accumulate);
+ break;
+ case AUDIO_CHANNEL_OUT_7POINT1:
+ Downmix_foldFrom7Point1(pSrc, pDst, numFrames, accumulate);
+ break;
+ default:
+ // FIXME implement generic downmix
+ ALOGE("Multichannel configurations other than quad, 4.0, 5.1 and 7.1 are not supported");
+ break;
+ }
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+static int Downmix_Command(effect_handle_t self, uint32_t cmdCode, uint32_t cmdSize,
+ void *pCmdData, uint32_t *replySize, void *pReplyData) {
+
+ downmix_module_t *pDwmModule = (downmix_module_t *) self;
+ downmix_object_t *pDownmixer;
+ int retsize;
+
+ if (pDwmModule == NULL || pDwmModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+
+ pDownmixer = (downmix_object_t*) &pDwmModule->context;
+
+ ALOGV("Downmix_Command command %d cmdSize %d",cmdCode, cmdSize);
+
+ switch (cmdCode) {
+ case EFFECT_CMD_INIT:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) pReplyData = Downmix_Init(pDwmModule);
+ break;
+
+ case EFFECT_CMD_SET_CONFIG:
+ if (pCmdData == NULL || cmdSize != sizeof(effect_config_t)
+ || pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ *(int *) pReplyData = Downmix_Configure(pDwmModule,
+ (effect_config_t *)pCmdData, false);
+ break;
+
+ case EFFECT_CMD_RESET:
+ Downmix_Reset(pDownmixer, false);
+ break;
+
+ case EFFECT_CMD_GET_PARAM:
+ ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM pCmdData %p, *replySize %d, pReplyData: %p",
+ pCmdData, *replySize, pReplyData);
+ if (pCmdData == NULL || cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)) ||
+ pReplyData == NULL ||
+ *replySize < (int) sizeof(effect_param_t) + 2 * sizeof(int32_t)) {
+ return -EINVAL;
+ }
+ effect_param_t *rep = (effect_param_t *) pReplyData;
+ memcpy(pReplyData, pCmdData, sizeof(effect_param_t) + sizeof(int32_t));
+ ALOGV("Downmix_Command EFFECT_CMD_GET_PARAM param %d, replySize %d",
+ *(int32_t *)rep->data, rep->vsize);
+ rep->status = Downmix_getParameter(pDownmixer, *(int32_t *)rep->data, &rep->vsize,
+ rep->data + sizeof(int32_t));
+ *replySize = sizeof(effect_param_t) + sizeof(int32_t) + rep->vsize;
+ break;
+
+ case EFFECT_CMD_SET_PARAM:
+ ALOGV("Downmix_Command EFFECT_CMD_SET_PARAM cmdSize %d pCmdData %p, *replySize %d, " \
+ "pReplyData %p", cmdSize, pCmdData, *replySize, pReplyData);
+ if (pCmdData == NULL || (cmdSize < (int)(sizeof(effect_param_t) + sizeof(int32_t)))
+ || pReplyData == NULL || *replySize != (int)sizeof(int32_t)) {
+ return -EINVAL;
+ }
+ effect_param_t *cmd = (effect_param_t *) pCmdData;
+ *(int *)pReplyData = Downmix_setParameter(pDownmixer, *(int32_t *)cmd->data,
+ cmd->vsize, cmd->data + sizeof(int32_t));
+ break;
+
+ case EFFECT_CMD_SET_PARAM_DEFERRED:
+ //FIXME implement
+ ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_DEFERRED not supported, FIXME");
+ break;
+
+ case EFFECT_CMD_SET_PARAM_COMMIT:
+ //FIXME implement
+ ALOGW("Downmix_Command command EFFECT_CMD_SET_PARAM_COMMIT not supported, FIXME");
+ break;
+
+ case EFFECT_CMD_ENABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pDownmixer->state != DOWNMIX_STATE_INITIALIZED) {
+ return -ENOSYS;
+ }
+ pDownmixer->state = DOWNMIX_STATE_ACTIVE;
+ ALOGV("EFFECT_CMD_ENABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
+
+ case EFFECT_CMD_DISABLE:
+ if (pReplyData == NULL || *replySize != sizeof(int)) {
+ return -EINVAL;
+ }
+ if (pDownmixer->state != DOWNMIX_STATE_ACTIVE) {
+ return -ENOSYS;
+ }
+ pDownmixer->state = DOWNMIX_STATE_INITIALIZED;
+ ALOGV("EFFECT_CMD_DISABLE() OK");
+ *(int *)pReplyData = 0;
+ break;
+
+ case EFFECT_CMD_SET_DEVICE:
+ if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
+ return -EINVAL;
+ }
+ // FIXME change type if playing on headset vs speaker
+ ALOGV("Downmix_Command EFFECT_CMD_SET_DEVICE: 0x%08x", *(uint32_t *)pCmdData);
+ break;
+
+ case EFFECT_CMD_SET_VOLUME: {
+ // audio output is always stereo => 2 channel volumes
+ if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t) * 2) {
+ return -EINVAL;
+ }
+ // FIXME change volume
+ ALOGW("Downmix_Command command EFFECT_CMD_SET_VOLUME not supported, FIXME");
+ float left = (float)(*(uint32_t *)pCmdData) / (1 << 24);
+ float right = (float)(*((uint32_t *)pCmdData + 1)) / (1 << 24);
+ ALOGV("Downmix_Command EFFECT_CMD_SET_VOLUME: left %f, right %f ", left, right);
+ break;
+ }
+
+ case EFFECT_CMD_SET_AUDIO_MODE:
+ if (pCmdData == NULL || cmdSize != (int)sizeof(uint32_t)) {
+ return -EINVAL;
+ }
+ ALOGV("Downmix_Command EFFECT_CMD_SET_AUDIO_MODE: %d", *(uint32_t *)pCmdData);
+ break;
+
+ case EFFECT_CMD_SET_CONFIG_REVERSE:
+ case EFFECT_CMD_SET_INPUT_DEVICE:
+ // these commands are ignored by a downmix effect
+ break;
+
+ default:
+ ALOGW("Downmix_Command invalid command %d",cmdCode);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+
+int Downmix_GetDescriptor(effect_handle_t self, effect_descriptor_t *pDescriptor)
+{
+ downmix_module_t *pDwnmxModule = (downmix_module_t *) self;
+
+ if (pDwnmxModule == NULL ||
+ pDwnmxModule->context.state == DOWNMIX_STATE_UNINITIALIZED) {
+ return -EINVAL;
+ }
+
+ memcpy(pDescriptor, &gDownmixDescriptor, sizeof(effect_descriptor_t));
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix internal functions
+ *--------------------------------------------------------------------------*/
+
+/*----------------------------------------------------------------------------
+ * Downmix_Init()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Initialize downmix context and apply default parameters
+ *
+ * Inputs:
+ * pDwmModule pointer to downmix effect module
+ *
+ * Outputs:
+ *
+ * Returns:
+ * 0 indicates success
+ *
+ * Side Effects:
+ * updates:
+ * pDwmModule->context.type
+ * pDwmModule->context.apply_volume_correction
+ * pDwmModule->config.inputCfg
+ * pDwmModule->config.outputCfg
+ * pDwmModule->config.inputCfg.samplingRate
+ * pDwmModule->config.outputCfg.samplingRate
+ * pDwmModule->context.state
+ * doesn't set:
+ * pDwmModule->itfe
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int Downmix_Init(downmix_module_t *pDwmModule) {
+
+ ALOGV("Downmix_Init module %p", pDwmModule);
+ int ret = 0;
+
+ memset(&pDwmModule->context, 0, sizeof(downmix_object_t));
+
+ pDwmModule->config.inputCfg.accessMode = EFFECT_BUFFER_ACCESS_READ;
+ pDwmModule->config.inputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pDwmModule->config.inputCfg.channels = AUDIO_CHANNEL_OUT_7POINT1;
+ pDwmModule->config.inputCfg.bufferProvider.getBuffer = NULL;
+ pDwmModule->config.inputCfg.bufferProvider.releaseBuffer = NULL;
+ pDwmModule->config.inputCfg.bufferProvider.cookie = NULL;
+ pDwmModule->config.inputCfg.mask = EFFECT_CONFIG_ALL;
+
+ pDwmModule->config.inputCfg.samplingRate = 44100;
+ pDwmModule->config.outputCfg.samplingRate = pDwmModule->config.inputCfg.samplingRate;
+
+ // set a default value for the access mode, but should be overwritten by caller
+ pDwmModule->config.outputCfg.accessMode = EFFECT_BUFFER_ACCESS_ACCUMULATE;
+ pDwmModule->config.outputCfg.format = AUDIO_FORMAT_PCM_16_BIT;
+ pDwmModule->config.outputCfg.channels = AUDIO_CHANNEL_OUT_STEREO;
+ pDwmModule->config.outputCfg.bufferProvider.getBuffer = NULL;
+ pDwmModule->config.outputCfg.bufferProvider.releaseBuffer = NULL;
+ pDwmModule->config.outputCfg.bufferProvider.cookie = NULL;
+ pDwmModule->config.outputCfg.mask = EFFECT_CONFIG_ALL;
+
+ ret = Downmix_Configure(pDwmModule, &pDwmModule->config, true);
+ if (ret != 0) {
+ ALOGV("Downmix_Init error %d on module %p", ret, pDwmModule);
+ } else {
+ pDwmModule->context.state = DOWNMIX_STATE_INITIALIZED;
+ }
+
+ return ret;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_Configure()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Set input and output audio configuration.
+ *
+ * Inputs:
+ * pDwmModule pointer to downmix effect module
+ * pConfig pointer to effect_config_t structure containing input
+ * and output audio parameters configuration
+ * init true if called from init function
+ *
+ * Outputs:
+ *
+ * Returns:
+ * 0 indicates success
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init) {
+
+ downmix_object_t *pDownmixer = &pDwmModule->context;
+
+ // Check configuration compatibility with build options, and effect capabilities
+ if (pConfig->inputCfg.samplingRate != pConfig->outputCfg.samplingRate
+ || pConfig->outputCfg.channels != DOWNMIX_OUTPUT_CHANNELS
+ || pConfig->inputCfg.format != AUDIO_FORMAT_PCM_16_BIT
+ || pConfig->outputCfg.format != AUDIO_FORMAT_PCM_16_BIT) {
+ ALOGE("Downmix_Configure error: invalid config");
+ return -EINVAL;
+ }
+
+ memcpy(&pDwmModule->config, pConfig, sizeof(effect_config_t));
+
+ if (init) {
+ pDownmixer->type = DOWNMIX_TYPE_FOLD;
+ pDownmixer->apply_volume_correction = false;
+ pDownmixer->input_channel_count = 8; // matches default input of AUDIO_CHANNEL_OUT_7POINT1
+ } else {
+ // when configuring the effect, do not allow a blank channel mask
+ if (pConfig->inputCfg.channels == 0) {
+ ALOGE("Downmix_Configure error: input channel mask can't be 0");
+ return -EINVAL;
+ }
+ pDownmixer->input_channel_count = popcount(pConfig->inputCfg.channels);
+ }
+
+ Downmix_Reset(pDownmixer, init);
+
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_Reset()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Reset internal states.
+ *
+ * Inputs:
+ * pDownmixer pointer to downmix context
+ * init true if called from init function
+ *
+ * Outputs:
+*
+ * Returns:
+ * 0 indicates success
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+ */
+
+int Downmix_Reset(downmix_object_t *pDownmixer, bool init) {
+ // nothing to do here
+ return 0;
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_setParameter()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Set a Downmix parameter
+ *
+ * Inputs:
+ * pDownmixer handle to instance data
+ * param parameter
+ * pValue pointer to parameter value
+ * size value size
+ *
+ * Outputs:
+ *
+ * Returns:
+ * 0 indicates success
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+ */
+int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue) {
+
+ int16_t value16;
+ ALOGV("Downmix_setParameter, context %p, param %d, value16 %d, value32 %d",
+ pDownmixer, param, *(int16_t *)pValue, *(int32_t *)pValue);
+
+ switch (param) {
+
+ case DOWNMIX_PARAM_TYPE:
+ if (size != sizeof(downmix_type_t)) {
+ ALOGE("Downmix_setParameter(DOWNMIX_PARAM_TYPE) invalid size %d, should be %d",
+ size, sizeof(downmix_type_t));
+ return -EINVAL;
+ }
+ value16 = *(int16_t *)pValue;
+ ALOGV("set DOWNMIX_PARAM_TYPE, type %d", value16);
+ if (!((value16 > DOWNMIX_TYPE_INVALID) && (value16 < DOWNMIX_TYPE_LAST))) {
+ ALOGE("Downmix_setParameter invalid DOWNMIX_PARAM_TYPE value %d", value16);
+ return -EINVAL;
+ } else {
+ pDownmixer->type = (downmix_type_t) value16;
+ break;
+
+ default:
+ ALOGE("Downmix_setParameter unknown parameter %d", param);
+ return -EINVAL;
+ }
+}
+
+ return 0;
+} /* end Downmix_setParameter */
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_getParameter()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * Get a Downmix parameter
+ *
+ * Inputs:
+ * pDownmixer handle to instance data
+ * param parameter
+ * pValue pointer to variable to hold retrieved value
+ * pSize pointer to value size: maximum size as input
+ *
+ * Outputs:
+ * *pValue updated with parameter value
+ * *pSize updated with actual value size
+ *
+ * Returns:
+ * 0 indicates success
+ *
+ * Side Effects:
+ *
+ *----------------------------------------------------------------------------
+ */
+int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue) {
+ int16_t *pValue16;
+
+ switch (param) {
+
+ case DOWNMIX_PARAM_TYPE:
+ if (*pSize < sizeof(int16_t)) {
+ ALOGE("Downmix_getParameter invalid parameter size %d for DOWNMIX_PARAM_TYPE", *pSize);
+ return -EINVAL;
+ }
+ pValue16 = (int16_t *)pValue;
+ *pValue16 = (int16_t) pDownmixer->type;
+ *pSize = sizeof(int16_t);
+ ALOGV("Downmix_getParameter DOWNMIX_PARAM_TYPE is %d", *pValue16);
+ break;
+
+ default:
+ ALOGE("Downmix_getParameter unknown parameter %d", param);
+ return -EINVAL;
+ }
+
+ return 0;
+} /* end Downmix_getParameter */
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_foldFromQuad()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * downmix a quad signal to stereo
+ *
+ * Inputs:
+ * pSrc quad audio samples to downmix
+ * numFrames the number of quad frames to downmix
+ *
+ * Outputs:
+ * pDst downmixed stereo audio samples
+ *
+ *----------------------------------------------------------------------------
+ */
+void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
+ // sample at index 0 is FL
+ // sample at index 1 is FR
+ // sample at index 2 is RL
+ // sample at index 3 is RR
+ if (accumulate) {
+ while (numFrames) {
+ // FL + RL
+ pDst[0] = clamp16(pDst[0] + pSrc[0] + pSrc[2]);
+ // FR + RR
+ pDst[1] = clamp16(pDst[1] + pSrc[1] + pSrc[3]);
+ pSrc += 4;
+ pDst += 2;
+ numFrames--;
+ }
+ } else { // same code as above but without adding and clamping pDst[i] to itself
+ while (numFrames) {
+ // FL + RL
+ pDst[0] = clamp16(pSrc[0] + pSrc[2]);
+ // FR + RR
+ pDst[1] = clamp16(pSrc[1] + pSrc[3]);
+ pSrc += 4;
+ pDst += 2;
+ numFrames--;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_foldFromSurround()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * downmix a "surround sound" (mono rear) signal to stereo
+ *
+ * Inputs:
+ * pSrc surround signal to downmix
+ * numFrames the number of surround frames to downmix
+ *
+ * Outputs:
+ * pDst downmixed stereo audio samples
+ *
+ *----------------------------------------------------------------------------
+ */
+void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
+ int32_t lt, rt, centerPlusRearContrib; // samples in Q19.12 format
+ // sample at index 0 is FL
+ // sample at index 1 is FR
+ // sample at index 2 is FC
+ // sample at index 3 is RC
+ if (accumulate) {
+ while (numFrames) {
+ // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
+ centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
+ // FL + centerPlusRearContrib
+ lt = (pSrc[0] << 12) + centerPlusRearContrib;
+ // FR + centerPlusRearContrib
+ rt = (pSrc[1] << 12) + centerPlusRearContrib;
+ pDst[0] = clamp16(pDst[0] + (lt >> 12));
+ pDst[1] = clamp16(pDst[1] + (rt >> 12));
+ pSrc += 4;
+ pDst += 2;
+ numFrames--;
+ }
+ } else { // same code as above but without adding and clamping pDst[i] to itself
+ while (numFrames) {
+ // centerPlusRearContrib = FC(-3dB) + RC(-3dB)
+ centerPlusRearContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12) + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
+ // FL + centerPlusRearContrib
+ lt = (pSrc[0] << 12) + centerPlusRearContrib;
+ // FR + centerPlusRearContrib
+ rt = (pSrc[1] << 12) + centerPlusRearContrib;
+ pDst[0] = clamp16(lt >> 12);
+ pDst[1] = clamp16(rt >> 12);
+ pSrc += 4;
+ pDst += 2;
+ numFrames--;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_foldFrom5Point1()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * downmix a 5.1 signal to stereo
+ *
+ * Inputs:
+ * pSrc 5.1 audio samples to downmix
+ * numFrames the number of 5.1 frames to downmix
+ *
+ * Outputs:
+ * pDst downmixed stereo audio samples
+ *
+ *----------------------------------------------------------------------------
+ */
+void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
+ int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
+ // sample at index 0 is FL
+ // sample at index 1 is FR
+ // sample at index 2 is FC
+ // sample at index 3 is LFE
+ // sample at index 4 is RL
+ // sample at index 5 is RR
+ if (accumulate) {
+ while (numFrames) {
+ // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
+ centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
+ + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
+ // FL + centerPlusLfeContrib + RL
+ lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
+ // FR + centerPlusLfeContrib + RR
+ rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
+ pDst[0] = clamp16(pDst[0] + (lt >> 12));
+ pDst[1] = clamp16(pDst[1] + (rt >> 12));
+ pSrc += 6;
+ pDst += 2;
+ numFrames--;
+ }
+ } else { // same code as above but without adding and clamping pDst[i] to itself
+ while (numFrames) {
+ // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
+ centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
+ + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
+ // FL + centerPlusLfeContrib + RL
+ lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[4] << 12);
+ // FR + centerPlusLfeContrib + RR
+ rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[5] << 12);
+ pDst[0] = clamp16(lt >> 12);
+ pDst[1] = clamp16(rt >> 12);
+ pSrc += 6;
+ pDst += 2;
+ numFrames--;
+ }
+ }
+}
+
+
+/*----------------------------------------------------------------------------
+ * Downmix_foldFrom7Point1()
+ *----------------------------------------------------------------------------
+ * Purpose:
+ * downmix a 7.1 signal to stereo
+ *
+ * Inputs:
+ * pSrc 7.1 audio samples to downmix
+ * numFrames the number of 7.1 frames to downmix
+ *
+ * Outputs:
+ * pDst downmixed stereo audio samples
+ *
+ *----------------------------------------------------------------------------
+ */
+void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate) {
+ int32_t lt, rt, centerPlusLfeContrib; // samples in Q19.12 format
+ // sample at index 0 is FL
+ // sample at index 1 is FR
+ // sample at index 2 is FC
+ // sample at index 3 is LFE
+ // sample at index 4 is RL
+ // sample at index 5 is RR
+ // sample at index 6 is SL
+ // sample at index 7 is SR
+ if (accumulate) {
+ while (numFrames) {
+ // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
+ centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
+ + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
+ // FL + centerPlusLfeContrib + SL + RL
+ lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
+ // FR + centerPlusLfeContrib + SR + RR
+ rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
+ pDst[0] = clamp16(lt >> 12);
+ pDst[1] = clamp16(rt >> 12);
+ pSrc += 8;
+ pDst += 2;
+ numFrames--;
+ }
+ } else { // same code as above but without adding and clamping pDst[i] to itself
+ while (numFrames) {
+ // centerPlusLfeContrib = FC(-3dB) + LFE(-3dB)
+ centerPlusLfeContrib = (pSrc[2] * MINUS_3_DB_IN_Q19_12)
+ + (pSrc[3] * MINUS_3_DB_IN_Q19_12);
+ // FL + centerPlusLfeContrib + SL + RL
+ lt = (pSrc[0] << 12) + centerPlusLfeContrib + (pSrc[6] << 12) + (pSrc[4] << 12);
+ // FR + centerPlusLfeContrib + SR + RR
+ rt = (pSrc[1] << 12) + centerPlusLfeContrib + (pSrc[7] << 12) + (pSrc[5] << 12);
+ pDst[0] = clamp16(pDst[0] + (lt >> 12));
+ pDst[1] = clamp16(pDst[1] + (rt >> 12));
+ pSrc += 8;
+ pDst += 2;
+ numFrames--;
+ }
+ }
+}
+
diff --git a/media/libeffects/downmix/EffectDownmix.h b/media/libeffects/downmix/EffectDownmix.h
new file mode 100644
index 0000000..4176b5a
--- /dev/null
+++ b/media/libeffects/downmix/EffectDownmix.h
@@ -0,0 +1,96 @@
+/*
+ * Copyright (C) 2012 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_EFFECTDOWNMIX_H_
+#define ANDROID_EFFECTDOWNMIX_H_
+
+#include <audio_effects/effect_downmix.h>
+#include <audio_utils/primitives.h>
+#include <system/audio.h>
+
+/*------------------------------------
+ * definitions
+ *------------------------------------
+*/
+
+#define DOWNMIX_OUTPUT_CHANNELS AUDIO_CHANNEL_OUT_STEREO
+
+typedef enum {
+ DOWNMIX_STATE_UNINITIALIZED,
+ DOWNMIX_STATE_INITIALIZED,
+ DOWNMIX_STATE_ACTIVE,
+} downmix_state_t;
+
+/* parameters for each downmixer */
+typedef struct {
+ downmix_state_t state;
+ downmix_type_t type;
+ bool apply_volume_correction;
+ uint8_t input_channel_count;
+} downmix_object_t;
+
+
+typedef struct downmix_module_s {
+ const struct effect_interface_s *itfe;
+ effect_config_t config;
+ downmix_object_t context;
+} downmix_module_t;
+
+
+/*------------------------------------
+ * Effect API
+ *------------------------------------
+*/
+int32_t DownmixLib_QueryNumberEffects(uint32_t *pNumEffects);
+int32_t DownmixLib_QueryEffect(uint32_t index,
+ effect_descriptor_t *pDescriptor);
+int32_t DownmixLib_Create(const effect_uuid_t *uuid,
+ int32_t sessionId,
+ int32_t ioId,
+ effect_handle_t *pHandle);
+int32_t DownmixLib_Release(effect_handle_t handle);
+int32_t DownmixLib_GetDescriptor(const effect_uuid_t *uuid,
+ effect_descriptor_t *pDescriptor);
+
+static int Downmix_Process(effect_handle_t self,
+ audio_buffer_t *inBuffer,
+ audio_buffer_t *outBuffer);
+static int Downmix_Command(effect_handle_t self,
+ uint32_t cmdCode,
+ uint32_t cmdSize,
+ void *pCmdData,
+ uint32_t *replySize,
+ void *pReplyData);
+static int Downmix_GetDescriptor(effect_handle_t self,
+ effect_descriptor_t *pDescriptor);
+
+
+/*------------------------------------
+ * internal functions
+ *------------------------------------
+*/
+int Downmix_Init(downmix_module_t *pDwmModule);
+int Downmix_Configure(downmix_module_t *pDwmModule, effect_config_t *pConfig, bool init);
+int Downmix_Reset(downmix_object_t *pDownmixer, bool init);
+int Downmix_setParameter(downmix_object_t *pDownmixer, int32_t param, size_t size, void *pValue);
+int Downmix_getParameter(downmix_object_t *pDownmixer, int32_t param, size_t *pSize, void *pValue);
+
+void Downmix_foldFromQuad(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate);
+void Downmix_foldFromSurround(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate);
+void Downmix_foldFrom5Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate);
+void Downmix_foldFrom7Point1(int16_t *pSrc, int16_t*pDst, size_t numFrames, bool accumulate);
+
+#endif /*ANDROID_EFFECTDOWNMIX_H_*/
diff --git a/media/libeffects/preprocessing/PreProcessing.cpp b/media/libeffects/preprocessing/PreProcessing.cpp
index 098a1a2..dc27d38 100755
--- a/media/libeffects/preprocessing/PreProcessing.cpp
+++ b/media/libeffects/preprocessing/PreProcessing.cpp
@@ -845,6 +845,17 @@ int Session_SetConfig(preproc_session_t *session, effect_config_t *config)
config->inputCfg.samplingRate, config->inputCfg.channels);
int status;
+ // if at least one process is enabled, do not accept configuration changes
+ if (session->enabledMsk) {
+ if (session->samplingRate != config->inputCfg.samplingRate ||
+ session->inChannelCount != inCnl ||
+ session->outChannelCount != outCnl) {
+ return -ENOSYS;
+ } else {
+ return 0;
+ }
+ }
+
// AEC implementation is limited to 16kHz
if (config->inputCfg.samplingRate >= 32000 && !(session->createdMsk & (1 << PREPROC_AEC))) {
session->apmSamplingRate = 32000;
@@ -1287,7 +1298,9 @@ int PreProcessingFx_Command(effect_handle_t self,
if (*(int *)pReplyData != 0) {
break;
}
- *(int *)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG);
+ if (effect->state != PREPROC_EFFECT_STATE_ACTIVE) {
+ *(int *)pReplyData = Effect_SetState(effect, PREPROC_EFFECT_STATE_CONFIG);
+ }
break;
case EFFECT_CMD_GET_CONFIG:
diff --git a/media/libmedia/AudioEffect.cpp b/media/libmedia/AudioEffect.cpp
index f9f997f..6808aa2 100644
--- a/media/libmedia/AudioEffect.cpp
+++ b/media/libmedia/AudioEffect.cpp
@@ -202,7 +202,7 @@ bool AudioEffect::getEnabled() const
status_t AudioEffect::setEnabled(bool enabled)
{
if (mStatus != NO_ERROR) {
- return INVALID_OPERATION;
+ return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
}
status_t status = NO_ERROR;
@@ -231,7 +231,7 @@ status_t AudioEffect::command(uint32_t cmdCode,
{
if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
ALOGV("command() bad status %d", mStatus);
- return INVALID_OPERATION;
+ return mStatus;
}
if (cmdCode == EFFECT_CMD_ENABLE || cmdCode == EFFECT_CMD_DISABLE) {
@@ -263,7 +263,7 @@ status_t AudioEffect::command(uint32_t cmdCode,
status_t AudioEffect::setParameter(effect_param_t *param)
{
if (mStatus != NO_ERROR) {
- return INVALID_OPERATION;
+ return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
}
if (param == NULL || param->psize == 0 || param->vsize == 0) {
@@ -281,7 +281,7 @@ status_t AudioEffect::setParameter(effect_param_t *param)
status_t AudioEffect::setParameterDeferred(effect_param_t *param)
{
if (mStatus != NO_ERROR) {
- return INVALID_OPERATION;
+ return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
}
if (param == NULL || param->psize == 0 || param->vsize == 0) {
@@ -307,7 +307,7 @@ status_t AudioEffect::setParameterDeferred(effect_param_t *param)
status_t AudioEffect::setParameterCommit()
{
if (mStatus != NO_ERROR) {
- return INVALID_OPERATION;
+ return (mStatus == ALREADY_EXISTS) ? (status_t) INVALID_OPERATION : mStatus;
}
Mutex::Autolock _l(mCblk->lock);
@@ -321,7 +321,7 @@ status_t AudioEffect::setParameterCommit()
status_t AudioEffect::getParameter(effect_param_t *param)
{
if (mStatus != NO_ERROR && mStatus != ALREADY_EXISTS) {
- return INVALID_OPERATION;
+ return mStatus;
}
if (param == NULL || param->psize == 0 || param->vsize == 0) {
@@ -341,7 +341,7 @@ status_t AudioEffect::getParameter(effect_param_t *param)
void AudioEffect::binderDied()
{
ALOGW("IEffect died");
- mStatus = NO_INIT;
+ mStatus = DEAD_OBJECT;
if (mCbf != NULL) {
status_t status = DEAD_OBJECT;
mCbf(EVENT_ERROR, mUserData, &status);
diff --git a/media/libmedia/AudioRecord.cpp b/media/libmedia/AudioRecord.cpp
index a4068ff..943f3af 100644
--- a/media/libmedia/AudioRecord.cpp
+++ b/media/libmedia/AudioRecord.cpp
@@ -307,7 +307,7 @@ status_t AudioRecord::start()
pid_t tid;
if (t != 0) {
mReadyToRun = WOULD_BLOCK;
- t->run("ClientRecordThread", ANDROID_PRIORITY_AUDIO);
+ t->run("AudioRecord", ANDROID_PRIORITY_AUDIO);
tid = t->getTid(); // pid_t is unknown until run()
ALOGV("getTid=%d", tid);
if (tid == -1) {
@@ -386,7 +386,7 @@ bool AudioRecord::stopped() const
return !mActive;
}
-uint32_t AudioRecord::getSampleRate()
+uint32_t AudioRecord::getSampleRate() const
{
AutoMutex lock(mLock);
return mCblk->sampleRate;
@@ -402,7 +402,7 @@ status_t AudioRecord::setMarkerPosition(uint32_t marker)
return NO_ERROR;
}
-status_t AudioRecord::getMarkerPosition(uint32_t *marker)
+status_t AudioRecord::getMarkerPosition(uint32_t *marker) const
{
if (marker == NULL) return BAD_VALUE;
@@ -423,7 +423,7 @@ status_t AudioRecord::setPositionUpdatePeriod(uint32_t updatePeriod)
return NO_ERROR;
}
-status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
+status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod) const
{
if (updatePeriod == NULL) return BAD_VALUE;
@@ -432,7 +432,7 @@ status_t AudioRecord::getPositionUpdatePeriod(uint32_t *updatePeriod)
return NO_ERROR;
}
-status_t AudioRecord::getPosition(uint32_t *position)
+status_t AudioRecord::getPosition(uint32_t *position) const
{
if (position == NULL) return BAD_VALUE;
@@ -442,7 +442,7 @@ status_t AudioRecord::getPosition(uint32_t *position)
return NO_ERROR;
}
-unsigned int AudioRecord::getInputFramesLost()
+unsigned int AudioRecord::getInputFramesLost() const
{
if (mActive)
return AudioSystem::getInputFramesLost(mInput);
@@ -597,7 +597,7 @@ void AudioRecord::releaseBuffer(Buffer* audioBuffer)
mCblk->stepUser(audioBuffer->frameCount);
}
-audio_io_handle_t AudioRecord::getInput()
+audio_io_handle_t AudioRecord::getInput() const
{
AutoMutex lock(mLock);
return mInput;
@@ -615,7 +615,7 @@ audio_io_handle_t AudioRecord::getInput_l()
return mInput;
}
-int AudioRecord::getSessionId()
+int AudioRecord::getSessionId() const
{
return mSessionId;
}
diff --git a/media/libmedia/AudioSystem.cpp b/media/libmedia/AudioSystem.cpp
index ec4c044..e0b186a 100644
--- a/media/libmedia/AudioSystem.cpp
+++ b/media/libmedia/AudioSystem.cpp
@@ -405,9 +405,9 @@ void AudioSystem::AudioFlingerClient::binderDied(const wp<IBinder>& who) {
}
void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle_t ioHandle,
- void *param2) {
+ const void *param2) {
ALOGV("ioConfigChanged() event %d", event);
- OutputDescriptor *desc;
+ const OutputDescriptor *desc;
audio_stream_type_t stream;
if (ioHandle == 0) return;
@@ -417,7 +417,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle
switch (event) {
case STREAM_CONFIG_CHANGED:
if (param2 == NULL) break;
- stream = *(audio_stream_type_t *)param2;
+ stream = *(const audio_stream_type_t *)param2;
ALOGV("ioConfigChanged() STREAM_CONFIG_CHANGED stream %d, output %d", stream, ioHandle);
if (gStreamOutputMap.indexOfKey(stream) >= 0) {
gStreamOutputMap.replaceValueFor(stream, ioHandle);
@@ -429,7 +429,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle
break;
}
if (param2 == NULL) break;
- desc = (OutputDescriptor *)param2;
+ desc = (const OutputDescriptor *)param2;
OutputDescriptor *outputDesc = new OutputDescriptor(*desc);
gOutputs.add(ioHandle, outputDesc);
@@ -458,7 +458,7 @@ void AudioSystem::AudioFlingerClient::ioConfigChanged(int event, audio_io_handle
break;
}
if (param2 == NULL) break;
- desc = (OutputDescriptor *)param2;
+ desc = (const OutputDescriptor *)param2;
ALOGV("ioConfigChanged() new config for output %d samplingRate %d, format %d channels %d frameCount %d latency %d",
ioHandle, desc->samplingRate, desc->format,
diff --git a/media/libmedia/AudioTrack.cpp b/media/libmedia/AudioTrack.cpp
index aead9a1..a1c99e5 100644
--- a/media/libmedia/AudioTrack.cpp
+++ b/media/libmedia/AudioTrack.cpp
@@ -80,7 +80,9 @@ status_t AudioTrack::getMinFrameCount(
AudioTrack::AudioTrack()
: mStatus(NO_INIT),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
+ mIsTimed(false),
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+ mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
{
}
@@ -96,7 +98,9 @@ AudioTrack::AudioTrack(
int notificationFrames,
int sessionId)
: mStatus(NO_INIT),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
+ mIsTimed(false),
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+ mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
{
mStatus = set(streamType, sampleRate, format, channelMask,
frameCount, flags, cbf, user, notificationFrames,
@@ -134,7 +138,9 @@ AudioTrack::AudioTrack(
int notificationFrames,
int sessionId)
: mStatus(NO_INIT),
- mPreviousPriority(ANDROID_PRIORITY_NORMAL), mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
+ mIsTimed(false),
+ mPreviousPriority(ANDROID_PRIORITY_NORMAL),
+ mPreviousSchedulingGroup(ANDROID_TGROUP_DEFAULT)
{
mStatus = set(streamType, sampleRate, format, channelMask,
0, flags, cbf, user, notificationFrames,
@@ -364,7 +370,7 @@ void AudioTrack::start()
android_atomic_and(~CBLK_DISABLED_ON, &cblk->flags);
pid_t tid;
if (t != 0) {
- t->run("AudioTrackThread", ANDROID_PRIORITY_AUDIO);
+ t->run("AudioTrack", ANDROID_PRIORITY_AUDIO);
tid = t->getTid(); // pid_t is unknown until run()
ALOGV("getTid=%d", tid);
if (tid == -1) {
@@ -540,6 +546,10 @@ status_t AudioTrack::setSampleRate(int rate)
{
int afSamplingRate;
+ if (mIsTimed) {
+ return INVALID_OPERATION;
+ }
+
if (AudioSystem::getOutputSamplingRate(&afSamplingRate, mStreamType) != NO_ERROR) {
return NO_INIT;
}
@@ -553,6 +563,10 @@ status_t AudioTrack::setSampleRate(int rate)
uint32_t AudioTrack::getSampleRate() const
{
+ if (mIsTimed) {
+ return INVALID_OPERATION;
+ }
+
AutoMutex lock(mLock);
return mCblk->sampleRate;
}
@@ -578,6 +592,10 @@ status_t AudioTrack::setLoop_l(uint32_t loopStart, uint32_t loopEnd, int loopCou
return NO_ERROR;
}
+ if (mIsTimed) {
+ return INVALID_OPERATION;
+ }
+
if (loopStart >= loopEnd ||
loopEnd - loopStart > cblk->frameCount ||
cblk->server > loopStart) {
@@ -641,6 +659,8 @@ status_t AudioTrack::getPositionUpdatePeriod(uint32_t *updatePeriod) const
status_t AudioTrack::setPosition(uint32_t position)
{
+ if (mIsTimed) return INVALID_OPERATION;
+
AutoMutex lock(mLock);
if (!stopped_l()) return INVALID_OPERATION;
@@ -764,12 +784,9 @@ status_t AudioTrack::createTrack_l(
mNotificationFramesAct = frameCount/2;
}
if (frameCount < minFrameCount) {
- if (enforceFrameCount) {
- ALOGE("Invalid buffer size: minFrameCount %d, frameCount %d", minFrameCount, frameCount);
- return BAD_VALUE;
- } else {
- frameCount = minFrameCount;
- }
+ ALOGW_IF(enforceFrameCount, "Minimum buffer size corrected from %d to %d",
+ frameCount, minFrameCount);
+ frameCount = minFrameCount;
}
} else {
// Ensure that buffer alignment matches channelCount
@@ -791,6 +808,7 @@ status_t AudioTrack::createTrack_l(
((uint16_t)flags) << 16,
sharedBuffer,
output,
+ mIsTimed,
&mSessionId,
&status);
@@ -957,6 +975,7 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
{
if (mSharedBuffer != 0) return INVALID_OPERATION;
+ if (mIsTimed) return INVALID_OPERATION;
if (ssize_t(userSize) < 0) {
// Sanity-check: user is most-likely passing an error code, and it would
@@ -1013,6 +1032,59 @@ ssize_t AudioTrack::write(const void* buffer, size_t userSize)
// -------------------------------------------------------------------------
+TimedAudioTrack::TimedAudioTrack() {
+ mIsTimed = true;
+}
+
+status_t TimedAudioTrack::allocateTimedBuffer(size_t size, sp<IMemory>* buffer)
+{
+ status_t result = UNKNOWN_ERROR;
+
+ // If the track is not invalid already, try to allocate a buffer. alloc
+ // fails indicating that the server is dead, flag the track as invalid so
+ // we can attempt to restore in in just a bit.
+ if (!(mCblk->flags & CBLK_INVALID_MSK)) {
+ result = mAudioTrack->allocateTimedBuffer(size, buffer);
+ if (result == DEAD_OBJECT) {
+ android_atomic_or(CBLK_INVALID_ON, &mCblk->flags);
+ }
+ }
+
+ // If the track is invalid at this point, attempt to restore it. and try the
+ // allocation one more time.
+ if (mCblk->flags & CBLK_INVALID_MSK) {
+ mCblk->lock.lock();
+ result = restoreTrack_l(mCblk, false);
+ mCblk->lock.unlock();
+
+ if (result == OK)
+ result = mAudioTrack->allocateTimedBuffer(size, buffer);
+ }
+
+ return result;
+}
+
+status_t TimedAudioTrack::queueTimedBuffer(const sp<IMemory>& buffer,
+ int64_t pts)
+{
+ // restart track if it was disabled by audioflinger due to previous underrun
+ if (mActive && (mCblk->flags & CBLK_DISABLED_MSK)) {
+ android_atomic_and(~CBLK_DISABLED_ON, &mCblk->flags);
+ ALOGW("queueTimedBuffer() track %p disabled, restarting", this);
+ mAudioTrack->start(0);
+ }
+
+ return mAudioTrack->queueTimedBuffer(buffer, pts);
+}
+
+status_t TimedAudioTrack::setMediaTimeTransform(const LinearTransform& xform,
+ TargetTimeline target)
+{
+ return mAudioTrack->setMediaTimeTransform(xform, target);
+}
+
+// -------------------------------------------------------------------------
+
bool AudioTrack::processAudioBuffer(const sp<AudioTrackThread>& thread)
{
Buffer audioBuffer;
diff --git a/media/libmedia/IAudioFlinger.cpp b/media/libmedia/IAudioFlinger.cpp
index 4507e5d..ebadbfa 100644
--- a/media/libmedia/IAudioFlinger.cpp
+++ b/media/libmedia/IAudioFlinger.cpp
@@ -90,6 +90,7 @@ public:
uint32_t flags,
const sp<IMemory>& sharedBuffer,
audio_io_handle_t output,
+ bool isTimed,
int *sessionId,
status_t *status)
{
@@ -105,6 +106,7 @@ public:
data.writeInt32(flags);
data.writeStrongBinder(sharedBuffer->asBinder());
data.writeInt32((int32_t) output);
+ data.writeInt32(isTimed);
int lSessionId = 0;
if (sessionId != NULL) {
lSessionId = *sessionId;
@@ -689,11 +691,12 @@ status_t BnAudioFlinger::onTransact(
uint32_t flags = data.readInt32();
sp<IMemory> buffer = interface_cast<IMemory>(data.readStrongBinder());
audio_io_handle_t output = (audio_io_handle_t) data.readInt32();
+ bool isTimed = data.readInt32();
int sessionId = data.readInt32();
status_t status;
sp<IAudioTrack> track = createTrack(pid,
(audio_stream_type_t) streamType, sampleRate, format,
- channelCount, bufferCount, flags, buffer, output, &sessionId, &status);
+ channelCount, bufferCount, flags, buffer, output, isTimed, &sessionId, &status);
reply->writeInt32(sessionId);
reply->writeInt32(status);
reply->writeStrongBinder(track->asBinder());
diff --git a/media/libmedia/IAudioFlingerClient.cpp b/media/libmedia/IAudioFlingerClient.cpp
index ce28b33..1db39a3 100644
--- a/media/libmedia/IAudioFlingerClient.cpp
+++ b/media/libmedia/IAudioFlingerClient.cpp
@@ -39,18 +39,18 @@ public:
{
}
- void ioConfigChanged(int event, audio_io_handle_t ioHandle, void *param2)
+ void ioConfigChanged(int event, audio_io_handle_t ioHandle, const void *param2)
{
Parcel data, reply;
data.writeInterfaceToken(IAudioFlingerClient::getInterfaceDescriptor());
data.writeInt32(event);
data.writeInt32((int32_t) ioHandle);
if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
- uint32_t stream = *(uint32_t *)param2;
+ 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) {
- AudioSystem::OutputDescriptor *desc = (AudioSystem::OutputDescriptor *)param2;
+ const AudioSystem::OutputDescriptor *desc = (const AudioSystem::OutputDescriptor *)param2;
data.writeInt32(desc->samplingRate);
data.writeInt32(desc->format);
data.writeInt32(desc->channels);
@@ -73,7 +73,7 @@ status_t BnAudioFlingerClient::onTransact(
CHECK_INTERFACE(IAudioFlingerClient, data, reply);
int event = data.readInt32();
audio_io_handle_t ioHandle = (audio_io_handle_t) data.readInt32();
- void *param2 = NULL;
+ const void *param2 = NULL;
AudioSystem::OutputDescriptor desc;
uint32_t stream;
if (event == AudioSystem::STREAM_CONFIG_CHANGED) {
diff --git a/media/libmedia/IAudioTrack.cpp b/media/libmedia/IAudioTrack.cpp
index a7958de..28ebbbf 100644
--- a/media/libmedia/IAudioTrack.cpp
+++ b/media/libmedia/IAudioTrack.cpp
@@ -35,7 +35,10 @@ enum {
FLUSH,
MUTE,
PAUSE,
- ATTACH_AUX_EFFECT
+ ATTACH_AUX_EFFECT,
+ ALLOCATE_TIMED_BUFFER,
+ QUEUE_TIMED_BUFFER,
+ SET_MEDIA_TIME_TRANSFORM,
};
class BpAudioTrack : public BpInterface<IAudioTrack>
@@ -114,6 +117,52 @@ public:
}
return status;
}
+
+ virtual status_t allocateTimedBuffer(size_t size, sp<IMemory>* buffer) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+ data.writeInt32(size);
+ status_t status = remote()->transact(ALLOCATE_TIMED_BUFFER,
+ data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ if (status == NO_ERROR) {
+ *buffer = interface_cast<IMemory>(reply.readStrongBinder());
+ }
+ }
+ return status;
+ }
+
+ virtual status_t queueTimedBuffer(const sp<IMemory>& buffer,
+ int64_t pts) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+ data.writeStrongBinder(buffer->asBinder());
+ data.writeInt64(pts);
+ status_t status = remote()->transact(QUEUE_TIMED_BUFFER,
+ data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ }
+ return status;
+ }
+
+ virtual status_t setMediaTimeTransform(const LinearTransform& xform,
+ int target) {
+ Parcel data, reply;
+ data.writeInterfaceToken(IAudioTrack::getInterfaceDescriptor());
+ data.writeInt64(xform.a_zero);
+ data.writeInt64(xform.b_zero);
+ data.writeInt32(xform.a_to_b_numer);
+ data.writeInt32(xform.a_to_b_denom);
+ data.writeInt32(target);
+ status_t status = remote()->transact(SET_MEDIA_TIME_TRANSFORM,
+ data, &reply);
+ if (status == NO_ERROR) {
+ status = reply.readInt32();
+ }
+ return status;
+ }
};
IMPLEMENT_META_INTERFACE(AudioTrack, "android.media.IAudioTrack");
@@ -159,10 +208,38 @@ status_t BnAudioTrack::onTransact(
reply->writeInt32(attachAuxEffect(data.readInt32()));
return NO_ERROR;
} break;
+ case ALLOCATE_TIMED_BUFFER: {
+ CHECK_INTERFACE(IAudioTrack, data, reply);
+ sp<IMemory> buffer;
+ status_t status = allocateTimedBuffer(data.readInt32(), &buffer);
+ reply->writeInt32(status);
+ if (status == NO_ERROR) {
+ reply->writeStrongBinder(buffer->asBinder());
+ }
+ return NO_ERROR;
+ } break;
+ case QUEUE_TIMED_BUFFER: {
+ CHECK_INTERFACE(IAudioTrack, data, reply);
+ sp<IMemory> buffer = interface_cast<IMemory>(
+ data.readStrongBinder());
+ uint64_t pts = data.readInt64();
+ reply->writeInt32(queueTimedBuffer(buffer, pts));
+ return NO_ERROR;
+ } break;
+ case SET_MEDIA_TIME_TRANSFORM: {
+ CHECK_INTERFACE(IAudioTrack, data, reply);
+ LinearTransform xform;
+ xform.a_zero = data.readInt64();
+ xform.b_zero = data.readInt64();
+ xform.a_to_b_numer = data.readInt32();
+ xform.a_to_b_denom = data.readInt32();
+ int target = data.readInt32();
+ reply->writeInt32(setMediaTimeTransform(xform, target));
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
}
}; // namespace android
-
diff --git a/media/libmedia/IEffect.cpp b/media/libmedia/IEffect.cpp
index d469e28..5d40cc8 100644
--- a/media/libmedia/IEffect.cpp
+++ b/media/libmedia/IEffect.cpp
@@ -83,8 +83,15 @@ public:
size = *pReplySize;
}
data.writeInt32(size);
- remote()->transact(COMMAND, data, &reply);
- status_t status = reply.readInt32();
+
+ status_t status = remote()->transact(COMMAND, data, &reply);
+ if (status != NO_ERROR) {
+ if (pReplySize != NULL)
+ *pReplySize = 0;
+ return status;
+ }
+
+ status = reply.readInt32();
size = reply.readInt32();
if (size != 0 && pReplyData != NULL && pReplySize != NULL) {
reply.read(pReplyData, size);
diff --git a/media/libmedia/IMediaPlayer.cpp b/media/libmedia/IMediaPlayer.cpp
index 64cc919..c47fa41 100644
--- a/media/libmedia/IMediaPlayer.cpp
+++ b/media/libmedia/IMediaPlayer.cpp
@@ -15,6 +15,7 @@
** limitations under the License.
*/
+#include <arpa/inet.h>
#include <stdint.h>
#include <sys/types.h>
@@ -23,8 +24,6 @@
#include <media/IMediaPlayer.h>
#include <media/IStreamSource.h>
-#include <surfaceflinger/ISurface.h>
-#include <surfaceflinger/Surface.h>
#include <gui/ISurfaceTexture.h>
#include <utils/String8.h>
@@ -55,6 +54,7 @@ enum {
SET_VIDEO_SURFACETEXTURE,
SET_PARAMETER,
GET_PARAMETER,
+ SET_RETRANSMIT_ENDPOINT,
};
class BpMediaPlayer: public BpInterface<IMediaPlayer>
@@ -291,6 +291,25 @@ public:
return remote()->transact(GET_PARAMETER, data, reply);
}
+ status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint) {
+ Parcel data, reply;
+ status_t err;
+
+ data.writeInterfaceToken(IMediaPlayer::getInterfaceDescriptor());
+ if (NULL != endpoint) {
+ data.writeInt32(sizeof(*endpoint));
+ data.write(endpoint, sizeof(*endpoint));
+ } else {
+ data.writeInt32(0);
+ }
+
+ err = remote()->transact(SET_RETRANSMIT_ENDPOINT, data, &reply);
+ if (OK != err) {
+ return err;
+ }
+
+ return reply.readInt32();
+ }
};
IMPLEMENT_META_INTERFACE(MediaPlayer, "android.media.IMediaPlayer");
@@ -459,6 +478,20 @@ status_t BnMediaPlayer::onTransact(
CHECK_INTERFACE(IMediaPlayer, data, reply);
return getParameter(data.readInt32(), reply);
} break;
+ case SET_RETRANSMIT_ENDPOINT: {
+ CHECK_INTERFACE(IMediaPlayer, data, reply);
+
+ struct sockaddr_in endpoint;
+ int amt = data.readInt32();
+ if (amt == sizeof(endpoint)) {
+ data.read(&endpoint, sizeof(struct sockaddr_in));
+ reply->writeInt32(setRetransmitEndpoint(&endpoint));
+ } else {
+ reply->writeInt32(setRetransmitEndpoint(NULL));
+ }
+
+ return NO_ERROR;
+ } break;
default:
return BBinder::onTransact(code, data, reply, flags);
}
diff --git a/media/libmedia/IMediaRecorder.cpp b/media/libmedia/IMediaRecorder.cpp
index 42f55c2..2f4e31a 100644
--- a/media/libmedia/IMediaRecorder.cpp
+++ b/media/libmedia/IMediaRecorder.cpp
@@ -19,10 +19,10 @@
#define LOG_TAG "IMediaRecorder"
#include <utils/Log.h>
#include <binder/Parcel.h>
-#include <surfaceflinger/Surface.h>
#include <camera/ICamera.h>
#include <media/IMediaRecorderClient.h>
#include <media/IMediaRecorder.h>
+#include <gui/Surface.h>
#include <gui/ISurfaceTexture.h>
#include <unistd.h>
diff --git a/media/libmedia/IOMX.cpp b/media/libmedia/IOMX.cpp
index 27c7e03..48e427a 100644
--- a/media/libmedia/IOMX.cpp
+++ b/media/libmedia/IOMX.cpp
@@ -22,8 +22,6 @@
#include <binder/Parcel.h>
#include <media/IOMX.h>
#include <media/stagefright/foundation/ADebug.h>
-#include <surfaceflinger/ISurface.h>
-#include <surfaceflinger/Surface.h>
namespace android {
diff --git a/media/libmedia/Visualizer.cpp b/media/libmedia/Visualizer.cpp
index 13b64e9..70f8c0c 100644
--- a/media/libmedia/Visualizer.cpp
+++ b/media/libmedia/Visualizer.cpp
@@ -168,7 +168,7 @@ status_t Visualizer::getWaveForm(uint8_t *waveform)
uint32_t replySize = mCaptureSize;
status = command(VISUALIZER_CMD_CAPTURE, 0, NULL, &replySize, waveform);
ALOGV("getWaveForm() command returned %d", status);
- if (replySize == 0) {
+ if ((status == NO_ERROR) && (replySize == 0)) {
status = NOT_ENOUGH_DATA;
}
} else {
diff --git a/media/libmedia/mediaplayer.cpp b/media/libmedia/mediaplayer.cpp
index 250425b..4ff1862 100644
--- a/media/libmedia/mediaplayer.cpp
+++ b/media/libmedia/mediaplayer.cpp
@@ -32,8 +32,6 @@
#include <media/mediaplayer.h>
#include <media/AudioSystem.h>
-#include <surfaceflinger/Surface.h>
-
#include <binder/MemoryBase.h>
#include <utils/KeyedVector.h>
@@ -63,6 +61,7 @@ MediaPlayer::MediaPlayer()
mAudioSessionId = AudioSystem::newAudioSessionId();
AudioSystem::acquireAudioSessionId(mAudioSessionId);
mSendLevel = 0;
+ mRetransmitEndpointValid = false;
}
MediaPlayer::~MediaPlayer()
@@ -95,6 +94,7 @@ void MediaPlayer::clear_l()
mCurrentPosition = -1;
mSeekPosition = -1;
mVideoWidth = mVideoHeight = 0;
+ mRetransmitEndpointValid = false;
}
status_t MediaPlayer::setListener(const sp<MediaPlayerListener>& listener)
@@ -146,7 +146,8 @@ status_t MediaPlayer::setDataSource(
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
- if (NO_ERROR != player->setDataSource(url, headers)) {
+ if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+ (NO_ERROR != player->setDataSource(url, headers))) {
player.clear();
}
err = attachNewPlayer(player);
@@ -162,7 +163,8 @@ status_t MediaPlayer::setDataSource(int fd, int64_t offset, int64_t length)
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
- if (NO_ERROR != player->setDataSource(fd, offset, length)) {
+ if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+ (NO_ERROR != player->setDataSource(fd, offset, length))) {
player.clear();
}
err = attachNewPlayer(player);
@@ -177,7 +179,8 @@ status_t MediaPlayer::setDataSource(const sp<IStreamSource> &source)
const sp<IMediaPlayerService>& service(getMediaPlayerService());
if (service != 0) {
sp<IMediaPlayer> player(service->create(getpid(), this, mAudioSessionId));
- if (NO_ERROR != player->setDataSource(source)) {
+ if ((NO_ERROR != doSetRetransmitEndpoint(player)) ||
+ (NO_ERROR != player->setDataSource(source))) {
player.clear();
}
err = attachNewPlayer(player);
@@ -471,6 +474,20 @@ status_t MediaPlayer::reset_l()
return NO_ERROR;
}
+status_t MediaPlayer::doSetRetransmitEndpoint(const sp<IMediaPlayer>& player) {
+ Mutex::Autolock _l(mLock);
+
+ if (player == NULL) {
+ return UNKNOWN_ERROR;
+ }
+
+ if (mRetransmitEndpointValid) {
+ return player->setRetransmitEndpoint(&mRetransmitEndpoint);
+ }
+
+ return OK;
+}
+
status_t MediaPlayer::reset()
{
ALOGV("reset");
@@ -599,6 +616,34 @@ status_t MediaPlayer::getParameter(int key, Parcel *reply)
return INVALID_OPERATION;
}
+status_t MediaPlayer::setRetransmitEndpoint(const char* addrString,
+ uint16_t port) {
+ ALOGV("MediaPlayer::setRetransmitEndpoint(%s:%hu)",
+ addrString ? addrString : "(null)", port);
+
+ Mutex::Autolock _l(mLock);
+ if ((mPlayer != NULL) || (mCurrentState != MEDIA_PLAYER_IDLE))
+ return INVALID_OPERATION;
+
+ if (NULL == addrString) {
+ mRetransmitEndpointValid = false;
+ return OK;
+ }
+
+ struct in_addr saddr;
+ if(!inet_aton(addrString, &saddr)) {
+ return BAD_VALUE;
+ }
+
+ memset(&mRetransmitEndpoint, 0, sizeof(&mRetransmitEndpoint));
+ mRetransmitEndpoint.sin_family = AF_INET;
+ mRetransmitEndpoint.sin_addr = saddr;
+ mRetransmitEndpoint.sin_port = htons(port);
+ mRetransmitEndpointValid = true;
+
+ return OK;
+}
+
void MediaPlayer::notify(int msg, int ext1, int ext2, const Parcel *obj)
{
ALOGV("message received msg=%d, ext1=%d, ext2=%d", msg, ext1, ext2);
diff --git a/media/libmedia/mediarecorder.cpp b/media/libmedia/mediarecorder.cpp
index 8d947d8..cc73014 100644
--- a/media/libmedia/mediarecorder.cpp
+++ b/media/libmedia/mediarecorder.cpp
@@ -18,7 +18,6 @@
//#define LOG_NDEBUG 0
#define LOG_TAG "MediaRecorder"
#include <utils/Log.h>
-#include <surfaceflinger/Surface.h>
#include <media/mediarecorder.h>
#include <binder/IServiceManager.h>
#include <utils/String8.h>
diff --git a/media/libmediaplayerservice/Android.mk b/media/libmediaplayerservice/Android.mk
index a3e2517..e521648 100644
--- a/media/libmediaplayerservice/Android.mk
+++ b/media/libmediaplayerservice/Android.mk
@@ -29,7 +29,8 @@ LOCAL_SHARED_LIBRARIES := \
libstagefright_omx \
libstagefright_foundation \
libgui \
- libdl
+ libdl \
+ libaah_rtp
LOCAL_STATIC_LIBRARIES := \
libstagefright_nuplayer \
diff --git a/media/libmediaplayerservice/MediaPlayerService.cpp b/media/libmediaplayerservice/MediaPlayerService.cpp
index 4df7f3d..1a85c9c 100644
--- a/media/libmediaplayerservice/MediaPlayerService.cpp
+++ b/media/libmediaplayerservice/MediaPlayerService.cpp
@@ -70,6 +70,11 @@
#include <OMX.h>
+namespace android {
+sp<MediaPlayerBase> createAAH_TXPlayer();
+sp<MediaPlayerBase> createAAH_RXPlayer();
+}
+
namespace {
using android::media::Metadata;
using android::status_t;
@@ -320,7 +325,7 @@ status_t MediaPlayerService::AudioOutput::dump(int fd, const Vector<String16>& a
mStreamType, mLeftVolume, mRightVolume);
result.append(buffer);
snprintf(buffer, 255, " msec per frame(%f), latency (%d)\n",
- mMsecsPerFrame, mLatency);
+ mMsecsPerFrame, (mTrack != 0) ? mTrack->latency() : -1);
result.append(buffer);
snprintf(buffer, 255, " aux effect id(%d), send level (%f)\n",
mAuxEffectId, mSendLevel);
@@ -487,6 +492,7 @@ MediaPlayerService::Client::Client(
mStatus = NO_INIT;
mAudioSessionId = audioSessionId;
mUID = uid;
+ mRetransmitEndpointValid = false;
#if CALLBACK_ANTAGONIZER
ALOGD("create Antagonizer");
@@ -593,6 +599,10 @@ player_type getPlayerType(const char* url)
return NU_PLAYER;
}
+ if (!strncasecmp("aahRX://", url, 8)) {
+ return AAH_RX_PLAYER;
+ }
+
// use MidiFile for MIDI extensions
int lenURL = strlen(url);
for (int i = 0; i < NELEM(FILE_EXTS); ++i) {
@@ -608,6 +618,44 @@ player_type getPlayerType(const char* url)
return getDefaultPlayerType();
}
+player_type MediaPlayerService::Client::getPlayerType(int fd,
+ int64_t offset,
+ int64_t length)
+{
+ // Until re-transmit functionality is added to the existing core android
+ // players, we use the special AAH TX player whenever we were configured
+ // for retransmission.
+ if (mRetransmitEndpointValid) {
+ return AAH_TX_PLAYER;
+ }
+
+ return android::getPlayerType(fd, offset, length);
+}
+
+player_type MediaPlayerService::Client::getPlayerType(const char* url)
+{
+ // Until re-transmit functionality is added to the existing core android
+ // players, we use the special AAH TX player whenever we were configured
+ // for retransmission.
+ if (mRetransmitEndpointValid) {
+ return AAH_TX_PLAYER;
+ }
+
+ return android::getPlayerType(url);
+}
+
+player_type MediaPlayerService::Client::getPlayerType(
+ const sp<IStreamSource> &source) {
+ // Until re-transmit functionality is added to the existing core android
+ // players, we use the special AAH TX player whenever we were configured
+ // for retransmission.
+ if (mRetransmitEndpointValid) {
+ return AAH_TX_PLAYER;
+ }
+
+ return NU_PLAYER;
+}
+
static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
notify_callback_f notifyFunc)
{
@@ -629,6 +677,14 @@ static sp<MediaPlayerBase> createPlayer(player_type playerType, void* cookie,
ALOGV("Create Test Player stub");
p = new TestPlayerStub();
break;
+ case AAH_RX_PLAYER:
+ ALOGV(" create A@H RX Player");
+ p = createAAH_RXPlayer();
+ break;
+ case AAH_TX_PLAYER:
+ ALOGV(" create A@H TX Player");
+ p = createAAH_TXPlayer();
+ break;
default:
ALOGE("Unknown player type: %d", playerType);
return NULL;
@@ -665,6 +721,49 @@ sp<MediaPlayerBase> MediaPlayerService::Client::createPlayer(player_type playerT
return p;
}
+sp<MediaPlayerBase> MediaPlayerService::Client::setDataSource_pre(
+ player_type playerType)
+{
+ ALOGV("player type = %d", playerType);
+
+ // create the right type of player
+ sp<MediaPlayerBase> p = createPlayer(playerType);
+ if (p == NULL) {
+ return p;
+ }
+
+ if (!p->hardwareOutput()) {
+ mAudioOutput = new AudioOutput(mAudioSessionId);
+ static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
+ }
+
+ return p;
+}
+
+void MediaPlayerService::Client::setDataSource_post(
+ const sp<MediaPlayerBase>& p,
+ status_t status)
+{
+ ALOGV(" setDataSource");
+ mStatus = status;
+ if (mStatus != OK) {
+ ALOGE(" error: %d", mStatus);
+ return;
+ }
+
+ // Set the re-transmission endpoint if one was chosen.
+ if (mRetransmitEndpointValid) {
+ mStatus = p->setRetransmitEndpoint(&mRetransmitEndpoint);
+ if (mStatus != NO_ERROR) {
+ ALOGE("setRetransmitEndpoint error: %d", mStatus);
+ }
+ }
+
+ if (mStatus == OK) {
+ mPlayer = p;
+ }
+}
+
status_t MediaPlayerService::Client::setDataSource(
const char *url, const KeyedVector<String8, String8> *headers)
{
@@ -696,25 +795,12 @@ status_t MediaPlayerService::Client::setDataSource(
return mStatus;
} else {
player_type playerType = getPlayerType(url);
- ALOGV("player type = %d", playerType);
-
- // create the right type of player
- sp<MediaPlayerBase> p = createPlayer(playerType);
- if (p == NULL) return NO_INIT;
-
- if (!p->hardwareOutput()) {
- mAudioOutput = new AudioOutput(mAudioSessionId);
- static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
+ sp<MediaPlayerBase> p = setDataSource_pre(playerType);
+ if (p == NULL) {
+ return NO_INIT;
}
- // now set data source
- ALOGV(" setDataSource");
- mStatus = p->setDataSource(url, headers);
- if (mStatus == NO_ERROR) {
- mPlayer = p;
- } else {
- ALOGE(" error: %d", mStatus);
- }
+ setDataSource_post(p, p->setDataSource(url, headers));
return mStatus;
}
}
@@ -745,46 +831,34 @@ status_t MediaPlayerService::Client::setDataSource(int fd, int64_t offset, int64
ALOGV("calculated length = %lld", length);
}
+ // Until re-transmit functionality is added to the existing core android
+ // players, we use the special AAH TX player whenever we were configured for
+ // retransmission.
player_type playerType = getPlayerType(fd, offset, length);
- ALOGV("player type = %d", playerType);
-
- // create the right type of player
- sp<MediaPlayerBase> p = createPlayer(playerType);
- if (p == NULL) return NO_INIT;
-
- if (!p->hardwareOutput()) {
- mAudioOutput = new AudioOutput(mAudioSessionId);
- static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
+ sp<MediaPlayerBase> p = setDataSource_pre(playerType);
+ if (p == NULL) {
+ return NO_INIT;
}
// now set data source
- mStatus = p->setDataSource(fd, offset, length);
- if (mStatus == NO_ERROR) mPlayer = p;
-
+ setDataSource_post(p, p->setDataSource(fd, offset, length));
return mStatus;
}
status_t MediaPlayerService::Client::setDataSource(
const sp<IStreamSource> &source) {
// create the right type of player
- sp<MediaPlayerBase> p = createPlayer(NU_PLAYER);
-
+ // Until re-transmit functionality is added to the existing core android
+ // players, we use the special AAH TX player whenever we were configured for
+ // retransmission.
+ player_type playerType = getPlayerType(source);
+ sp<MediaPlayerBase> p = setDataSource_pre(playerType);
if (p == NULL) {
return NO_INIT;
}
- if (!p->hardwareOutput()) {
- mAudioOutput = new AudioOutput(mAudioSessionId);
- static_cast<MediaPlayerInterface*>(p.get())->setAudioSink(mAudioOutput);
- }
-
// now set data source
- mStatus = p->setDataSource(source);
-
- if (mStatus == OK) {
- mPlayer = p;
- }
-
+ setDataSource_post(p, p->setDataSource(source));
return mStatus;
}
@@ -1005,6 +1079,7 @@ status_t MediaPlayerService::Client::seekTo(int msec)
status_t MediaPlayerService::Client::reset()
{
ALOGV("[%d] reset", mConnId);
+ mRetransmitEndpointValid = false;
sp<MediaPlayerBase> p = getPlayer();
if (p == 0) return UNKNOWN_ERROR;
return p->reset();
@@ -1031,9 +1106,21 @@ status_t MediaPlayerService::Client::setLooping(int loop)
status_t MediaPlayerService::Client::setVolume(float leftVolume, float rightVolume)
{
ALOGV("[%d] setVolume(%f, %f)", mConnId, leftVolume, rightVolume);
- // TODO: for hardware output, call player instead
- Mutex::Autolock l(mLock);
- if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
+
+ // for hardware output, call player instead
+ sp<MediaPlayerBase> p = getPlayer();
+ {
+ Mutex::Autolock l(mLock);
+ if (p != 0 && p->hardwareOutput()) {
+ MediaPlayerHWInterface* hwp =
+ reinterpret_cast<MediaPlayerHWInterface*>(p.get());
+ return hwp->setVolume(leftVolume, rightVolume);
+ } else {
+ if (mAudioOutput != 0) mAudioOutput->setVolume(leftVolume, rightVolume);
+ return NO_ERROR;
+ }
+ }
+
return NO_ERROR;
}
@@ -1067,6 +1154,36 @@ status_t MediaPlayerService::Client::getParameter(int key, Parcel *reply) {
return p->getParameter(key, reply);
}
+status_t MediaPlayerService::Client::setRetransmitEndpoint(
+ const struct sockaddr_in* endpoint) {
+
+ if (NULL != endpoint) {
+ uint32_t a = ntohl(endpoint->sin_addr.s_addr);
+ uint16_t p = ntohs(endpoint->sin_port);
+ ALOGV("[%d] setRetransmitEndpoint(%u.%u.%u.%u:%hu)", mConnId,
+ (a >> 24), (a >> 16) & 0xFF, (a >> 8) & 0xFF, (a & 0xFF), p);
+ } else {
+ ALOGV("[%d] setRetransmitEndpoint = <none>", mConnId);
+ }
+
+ sp<MediaPlayerBase> p = getPlayer();
+
+ // Right now, the only valid time to set a retransmit endpoint is before
+ // player selection has been made (since the presence or absence of a
+ // retransmit endpoint is going to determine which player is selected during
+ // setDataSource).
+ if (p != 0) return INVALID_OPERATION;
+
+ if (NULL != endpoint) {
+ mRetransmitEndpoint = *endpoint;
+ mRetransmitEndpointValid = true;
+ } else {
+ mRetransmitEndpointValid = false;
+ }
+
+ return NO_ERROR;
+}
+
void MediaPlayerService::Client::notify(
void* cookie, int msg, int ext1, int ext2, const Parcel *obj)
{
@@ -1267,7 +1384,6 @@ MediaPlayerService::AudioOutput::AudioOutput(int sessionId)
mRightVolume = 1.0;
mPlaybackRatePermille = 1000;
mSampleRateHz = 0;
- mLatency = 0;
mMsecsPerFrame = 0;
mAuxEffectId = 0;
mSendLevel = 0.0;
@@ -1326,7 +1442,8 @@ ssize_t MediaPlayerService::AudioOutput::frameSize() const
uint32_t MediaPlayerService::AudioOutput::latency () const
{
- return mLatency;
+ if (mTrack == 0) return 0;
+ return mTrack->latency();
}
float MediaPlayerService::AudioOutput::msecsPerFrame() const
@@ -1341,7 +1458,8 @@ status_t MediaPlayerService::AudioOutput::getPosition(uint32_t *position)
}
status_t MediaPlayerService::AudioOutput::open(
- uint32_t sampleRate, int channelCount, audio_format_t format, int bufferCount,
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
AudioCallback cb, void *cookie)
{
mCallback = cb;
@@ -1353,7 +1471,8 @@ status_t MediaPlayerService::AudioOutput::open(
bufferCount = mMinBufferCount;
}
- ALOGV("open(%u, %d, %d, %d, %d)", sampleRate, channelCount, format, bufferCount,mSessionId);
+ ALOGV("open(%u, %d, 0x%x, %d, %d, %d)", sampleRate, channelCount, channelMask,
+ format, bufferCount, mSessionId);
if (mTrack) close();
int afSampleRate;
int afFrameCount;
@@ -1368,13 +1487,21 @@ status_t MediaPlayerService::AudioOutput::open(
frameCount = (sampleRate*afFrameCount*bufferCount)/afSampleRate;
+ if (channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER) {
+ channelMask = audio_channel_mask_from_count(channelCount);
+ if (0 == channelMask) {
+ ALOGE("open() error, can\'t derive mask for %d audio channels", channelCount);
+ return NO_INIT;
+ }
+ }
+
AudioTrack *t;
if (mCallback != NULL) {
t = new AudioTrack(
mStreamType,
sampleRate,
format,
- (channelCount == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
+ channelMask,
frameCount,
0 /* flags */,
CallbackWrapper,
@@ -1386,7 +1513,7 @@ status_t MediaPlayerService::AudioOutput::open(
mStreamType,
sampleRate,
format,
- (channelCount == 2) ? AUDIO_CHANNEL_OUT_STEREO : AUDIO_CHANNEL_OUT_MONO,
+ channelMask,
frameCount,
0,
NULL,
@@ -1406,7 +1533,6 @@ status_t MediaPlayerService::AudioOutput::open(
mSampleRateHz = sampleRate;
mMsecsPerFrame = mPlaybackRatePermille / (float) sampleRate;
- mLatency = t->latency();
mTrack = t;
status_t res = t->setSampleRate(mPlaybackRatePermille * mSampleRateHz / 1000);
@@ -1634,10 +1760,11 @@ bool CallbackThread::threadLoop() {
////////////////////////////////////////////////////////////////////////////////
status_t MediaPlayerService::AudioCache::open(
- uint32_t sampleRate, int channelCount, audio_format_t format, int bufferCount,
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount,
AudioCallback cb, void *cookie)
{
- ALOGV("open(%u, %d, %d, %d)", sampleRate, channelCount, format, bufferCount);
+ ALOGV("open(%u, %d, 0x%x, %d, %d)", sampleRate, channelCount, channelMask, format, bufferCount);
if (mHeap->getHeapID() < 0) {
return NO_INIT;
}
diff --git a/media/libmediaplayerservice/MediaPlayerService.h b/media/libmediaplayerservice/MediaPlayerService.h
index 52af64d..85cec22 100644
--- a/media/libmediaplayerservice/MediaPlayerService.h
+++ b/media/libmediaplayerservice/MediaPlayerService.h
@@ -18,6 +18,8 @@
#ifndef ANDROID_MEDIAPLAYERSERVICE_H
#define ANDROID_MEDIAPLAYERSERVICE_H
+#include <arpa/inet.h>
+
#include <utils/Log.h>
#include <utils/threads.h>
#include <utils/List.h>
@@ -83,7 +85,7 @@ class MediaPlayerService : public BnMediaPlayerService
virtual int getSessionId();
virtual status_t open(
- uint32_t sampleRate, int channelCount,
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
audio_format_t format, int bufferCount,
AudioCallback cb, void *cookie);
@@ -116,7 +118,6 @@ class MediaPlayerService : public BnMediaPlayerService
int32_t mPlaybackRatePermille;
uint32_t mSampleRateHz; // sample rate of the content, as set in open()
float mMsecsPerFrame;
- uint32_t mLatency;
int mSessionId;
float mSendLevel;
int mAuxEffectId;
@@ -143,8 +144,8 @@ class MediaPlayerService : public BnMediaPlayerService
virtual int getSessionId();
virtual status_t open(
- uint32_t sampleRate, int channelCount, audio_format_t format,
- int bufferCount = 1,
+ uint32_t sampleRate, int channelCount, audio_channel_mask_t channelMask,
+ audio_format_t format, int bufferCount = 1,
AudioCallback cb = NULL, void *cookie = NULL);
virtual void start();
@@ -276,6 +277,7 @@ private:
virtual status_t attachAuxEffect(int effectId);
virtual status_t setParameter(int key, const Parcel &request);
virtual status_t getParameter(int key, Parcel *reply);
+ virtual status_t setRetransmitEndpoint(const struct sockaddr_in* endpoint);
sp<MediaPlayerBase> createPlayer(player_type playerType);
@@ -287,6 +289,14 @@ private:
virtual status_t setDataSource(const sp<IStreamSource> &source);
+ sp<MediaPlayerBase> setDataSource_pre(player_type playerType);
+ void setDataSource_post(const sp<MediaPlayerBase>& p,
+ status_t status);
+
+ player_type getPlayerType(int fd, int64_t offset, int64_t length);
+ player_type getPlayerType(const char* url);
+ player_type getPlayerType(const sp<IStreamSource> &source);
+
static void notify(void* cookie, int msg,
int ext1, int ext2, const Parcel *obj);
@@ -338,6 +348,8 @@ private:
uid_t mUID;
sp<ANativeWindow> mConnectedWindow;
sp<IBinder> mConnectedWindowBinder;
+ struct sockaddr_in mRetransmitEndpoint;
+ bool mRetransmitEndpointValid;
// Metadata filters.
media::Metadata::Filter mMetadataAllow; // protected by mLock
diff --git a/media/libmediaplayerservice/MidiFile.cpp b/media/libmediaplayerservice/MidiFile.cpp
index 7cb8c29..8db5b9b 100644
--- a/media/libmediaplayerservice/MidiFile.cpp
+++ b/media/libmediaplayerservice/MidiFile.cpp
@@ -421,7 +421,8 @@ status_t MidiFile::setLooping(int loop)
}
status_t MidiFile::createOutputTrack() {
- if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) {
+ if (mAudioSink->open(pLibConfig->sampleRate, pLibConfig->numChannels,
+ CHANNEL_MASK_USE_CHANNEL_ORDER, AUDIO_FORMAT_PCM_16_BIT, 2) != NO_ERROR) {
ALOGE("mAudioSink open failed");
return ERROR_OPEN_FAILED;
}
diff --git a/media/libmediaplayerservice/StagefrightRecorder.cpp b/media/libmediaplayerservice/StagefrightRecorder.cpp
index fe519b0..ca79657 100644
--- a/media/libmediaplayerservice/StagefrightRecorder.cpp
+++ b/media/libmediaplayerservice/StagefrightRecorder.cpp
@@ -40,7 +40,7 @@
#include <media/MediaProfiles.h>
#include <camera/ICamera.h>
#include <camera/CameraParameters.h>
-#include <surfaceflinger/Surface.h>
+#include <gui/Surface.h>
#include <utils/Errors.h>
#include <sys/types.h>
@@ -1291,6 +1291,12 @@ status_t StagefrightRecorder::setupCameraSource(
videoSize.width = mVideoWidth;
videoSize.height = mVideoHeight;
if (mCaptureTimeLapse) {
+ if (mTimeBetweenTimeLapseFrameCaptureUs < 0) {
+ ALOGE("Invalid mTimeBetweenTimeLapseFrameCaptureUs value: %lld",
+ mTimeBetweenTimeLapseFrameCaptureUs);
+ return BAD_VALUE;
+ }
+
mCameraSourceTimeLapse = CameraSourceTimeLapse::CreateFromCamera(
mCamera, mCameraProxy, mCameraId,
videoSize, mFrameRate, mPreviewSurface,
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
index b731d0f..526120a 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.cpp
@@ -38,7 +38,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MetaData.h>
-#include <surfaceflinger/Surface.h>
#include <gui/ISurfaceTexture.h>
#include "avc_utils.h"
@@ -337,6 +336,7 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
CHECK_EQ(mAudioSink->open(
sampleRate,
numChannels,
+ CHANNEL_MASK_USE_CHANNEL_ORDER,
AUDIO_FORMAT_PCM_16_BIT,
8 /* bufferCount */),
(status_t)OK);
@@ -387,10 +387,10 @@ void NuPlayer::onMessageReceived(const sp<AMessage> &msg) {
audio ? "audio" : "video");
mRenderer->queueEOS(audio, UNKNOWN_ERROR);
- } else {
- CHECK_EQ((int)what, (int)ACodec::kWhatDrainThisBuffer);
-
+ } else if (what == ACodec::kWhatDrainThisBuffer) {
renderBuffer(audio, codecRequest);
+ } else {
+ ALOGV("Unhandled codec notification %d.", what);
}
break;
@@ -768,7 +768,7 @@ status_t NuPlayer::feedDecoderInputData(bool audio, const sp<AMessage> &msg) {
mediaTimeUs / 1E6);
#endif
- reply->setObject("buffer", accessUnit);
+ reply->setBuffer("buffer", accessUnit);
reply->post();
return OK;
@@ -793,10 +793,8 @@ void NuPlayer::renderBuffer(bool audio, const sp<AMessage> &msg) {
return;
}
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
-
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
int64_t &skipUntilMediaTimeUs =
audio
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayer.h b/media/libmediaplayerservice/nuplayer/NuPlayer.h
index ffc710e..6be14be 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayer.h
+++ b/media/libmediaplayerservice/nuplayer/NuPlayer.h
@@ -21,8 +21,6 @@
#include <media/MediaPlayerInterface.h>
#include <media/stagefright/foundation/AHandler.h>
#include <media/stagefright/NativeWindowWrapper.h>
-#include <gui/SurfaceTextureClient.h>
-#include <surfaceflinger/Surface.h>
namespace android {
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
index 56c2773..460fc98 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerDecoder.cpp
@@ -29,8 +29,6 @@
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/Utils.h>
-#include <surfaceflinger/Surface.h>
-#include <gui/ISurfaceTexture.h>
namespace android {
@@ -214,8 +212,6 @@ sp<AMessage> NuPlayer::Decoder::makeFormat(const sp<MetaData> &meta) {
buffer->meta()->setInt32("csd", true);
mCSD.push(buffer);
-
- msg->setObject("csd", buffer);
} else if (meta->findData(kKeyESDS, &type, &data, &size)) {
ESDS esds((const char *)data, size);
CHECK_EQ(esds.InitCheck(), (status_t)OK);
@@ -242,9 +238,8 @@ void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
CHECK(msg->findMessage("reply", &reply));
#if 0
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
- sp<ABuffer> outBuffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> outBuffer;
+ CHECK(msg->findBuffer("buffer", &outBuffer));
#else
sp<ABuffer> outBuffer;
#endif
@@ -253,7 +248,7 @@ void NuPlayer::Decoder::onFillThisBuffer(const sp<AMessage> &msg) {
outBuffer = mCSD.editItemAt(mCSDIndex++);
outBuffer->meta()->setInt64("timeUs", 0);
- reply->setObject("buffer", outBuffer);
+ reply->setBuffer("buffer", outBuffer);
reply->post();
return;
}
diff --git a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
index 15259cb..5738ecb 100644
--- a/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
+++ b/media/libmediaplayerservice/nuplayer/NuPlayerRenderer.cpp
@@ -60,7 +60,7 @@ void NuPlayer::Renderer::queueBuffer(
const sp<AMessage> &notifyConsumed) {
sp<AMessage> msg = new AMessage(kWhatQueueBuffer, id());
msg->setInt32("audio", static_cast<int32_t>(audio));
- msg->setObject("buffer", buffer);
+ msg->setBuffer("buffer", buffer);
msg->setMessage("notifyConsumed", notifyConsumed);
msg->post();
}
@@ -411,9 +411,8 @@ void NuPlayer::Renderer::onQueueBuffer(const sp<AMessage> &msg) {
return;
}
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
sp<AMessage> notifyConsumed;
CHECK(msg->findMessage("notifyConsumed", &notifyConsumed));
diff --git a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
index 6eb0d07..4c65b65 100644
--- a/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
+++ b/media/libmediaplayerservice/nuplayer/RTSPSource.cpp
@@ -218,10 +218,8 @@ void NuPlayer::RTSPSource::onMessageReceived(const sp<AMessage> &msg) {
CHECK(msg->findSize("trackIndex", &trackIndex));
CHECK_LT(trackIndex, mTracks.size());
- sp<RefBase> obj;
- CHECK(msg->findObject("accessUnit", &obj));
-
- sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("accessUnit", &accessUnit));
int32_t damaged;
if (accessUnit->meta()->findInt32("damaged", &damaged)
diff --git a/media/libstagefright/ACodec.cpp b/media/libstagefright/ACodec.cpp
index ca44ea3..09e4e45 100644
--- a/media/libstagefright/ACodec.cpp
+++ b/media/libstagefright/ACodec.cpp
@@ -26,14 +26,12 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaDefs.h>
#include <media/stagefright/NativeWindowWrapper.h>
#include <media/stagefright/OMXClient.h>
#include <media/stagefright/OMXCodec.h>
-#include <surfaceflinger/Surface.h>
-#include <gui/SurfaceTextureClient.h>
-
#include <OMX_Component.h>
namespace android {
@@ -168,15 +166,36 @@ struct ACodec::UninitializedState : public ACodec::BaseState {
protected:
virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
private:
void onSetup(const sp<AMessage> &msg);
+ bool onAllocateComponent(const sp<AMessage> &msg);
DISALLOW_EVIL_CONSTRUCTORS(UninitializedState);
};
////////////////////////////////////////////////////////////////////////////////
+struct ACodec::LoadedState : public ACodec::BaseState {
+ LoadedState(ACodec *codec);
+
+protected:
+ virtual bool onMessageReceived(const sp<AMessage> &msg);
+ virtual void stateEntered();
+
+private:
+ friend struct ACodec::UninitializedState;
+
+ bool onConfigureComponent(const sp<AMessage> &msg);
+ void onStart();
+ void onShutdown(bool keepComponentAllocated);
+
+ DISALLOW_EVIL_CONSTRUCTORS(LoadedState);
+};
+
+////////////////////////////////////////////////////////////////////////////////
+
struct ACodec::LoadedToIdleState : public ACodec::BaseState {
LoadedToIdleState(ACodec *codec);
@@ -265,6 +284,8 @@ protected:
private:
void changeStateIfWeOwnAllBuffers();
+ bool mComponentNowIdle;
+
DISALLOW_EVIL_CONSTRUCTORS(ExecutingToIdleState);
};
@@ -308,9 +329,13 @@ private:
////////////////////////////////////////////////////////////////////////////////
ACodec::ACodec()
- : mNode(NULL),
- mSentFormat(false) {
+ : mQuirks(0),
+ mNode(NULL),
+ mSentFormat(false),
+ mIsEncoder(false),
+ mShutdownInProgress(false) {
mUninitializedState = new UninitializedState(this);
+ mLoadedState = new LoadedState(this);
mLoadedToIdleState = new LoadedToIdleState(this);
mIdleToExecutingState = new IdleToExecutingState(this);
mExecutingState = new ExecutingState(this);
@@ -341,6 +366,22 @@ void ACodec::initiateSetup(const sp<AMessage> &msg) {
msg->post();
}
+void ACodec::initiateAllocateComponent(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatAllocateComponent);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void ACodec::initiateConfigureComponent(const sp<AMessage> &msg) {
+ msg->setWhat(kWhatConfigureComponent);
+ msg->setTarget(id());
+ msg->post();
+}
+
+void ACodec::initiateStart() {
+ (new AMessage(kWhatStart, id()))->post();
+}
+
void ACodec::signalFlush() {
ALOGV("[%s] signalFlush", mComponentName.c_str());
(new AMessage(kWhatFlush, id()))->post();
@@ -350,8 +391,10 @@ void ACodec::signalResume() {
(new AMessage(kWhatResume, id()))->post();
}
-void ACodec::initiateShutdown() {
- (new AMessage(kWhatShutdown, id()))->post();
+void ACodec::initiateShutdown(bool keepComponentAllocated) {
+ sp<AMessage> msg = new AMessage(kWhatShutdown, id());
+ msg->setInt32("keepComponentAllocated", keepComponentAllocated);
+ msg->post();
}
status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
@@ -360,62 +403,71 @@ status_t ACodec::allocateBuffersOnPort(OMX_U32 portIndex) {
CHECK(mDealer[portIndex] == NULL);
CHECK(mBuffers[portIndex].isEmpty());
+ status_t err;
if (mNativeWindow != NULL && portIndex == kPortIndexOutput) {
- return allocateOutputBuffersFromNativeWindow();
- }
-
- OMX_PARAM_PORTDEFINITIONTYPE def;
- InitOMXParams(&def);
- def.nPortIndex = portIndex;
+ err = allocateOutputBuffersFromNativeWindow();
+ } else {
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = portIndex;
- status_t err = mOMX->getParameter(
- mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
- if (err != OK) {
- return err;
- }
+ if (err == OK) {
+ ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
+ mComponentName.c_str(),
+ def.nBufferCountActual, def.nBufferSize,
+ portIndex == kPortIndexInput ? "input" : "output");
- ALOGV("[%s] Allocating %lu buffers of size %lu on %s port",
- mComponentName.c_str(),
- def.nBufferCountActual, def.nBufferSize,
- portIndex == kPortIndexInput ? "input" : "output");
+ size_t totalSize = def.nBufferCountActual * def.nBufferSize;
+ mDealer[portIndex] = new MemoryDealer(totalSize, "ACodec");
- size_t totalSize = def.nBufferCountActual * def.nBufferSize;
- mDealer[portIndex] = new MemoryDealer(totalSize, "OMXCodec");
+ for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
+ sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
+ CHECK(mem.get() != NULL);
- for (OMX_U32 i = 0; i < def.nBufferCountActual; ++i) {
- sp<IMemory> mem = mDealer[portIndex]->allocate(def.nBufferSize);
- CHECK(mem.get() != NULL);
+ IOMX::buffer_id buffer;
- IOMX::buffer_id buffer;
+ uint32_t requiresAllocateBufferBit =
+ (portIndex == kPortIndexInput)
+ ? OMXCodec::kRequiresAllocateBufferOnInputPorts
+ : OMXCodec::kRequiresAllocateBufferOnOutputPorts;
- if (!strcasecmp(
- mComponentName.c_str(), "OMX.TI.DUCATI1.VIDEO.DECODER")) {
- if (portIndex == kPortIndexInput && i == 0) {
- // Only log this warning once per allocation round.
+ if (mQuirks & requiresAllocateBufferBit) {
+ err = mOMX->allocateBufferWithBackup(
+ mNode, portIndex, mem, &buffer);
+ } else {
+ err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
+ }
- ALOGW("OMX.TI.DUCATI1.VIDEO.DECODER requires the use of "
- "OMX_AllocateBuffer instead of the preferred "
- "OMX_UseBuffer. Vendor must fix this.");
+ BufferInfo info;
+ info.mBufferID = buffer;
+ info.mStatus = BufferInfo::OWNED_BY_US;
+ info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
+ mBuffers[portIndex].push(info);
}
-
- err = mOMX->allocateBufferWithBackup(
- mNode, portIndex, mem, &buffer);
- } else {
- err = mOMX->useBuffer(mNode, portIndex, mem, &buffer);
}
+ }
- if (err != OK) {
- return err;
- }
+ if (err != OK) {
+ return err;
+ }
- BufferInfo info;
- info.mBufferID = buffer;
- info.mStatus = BufferInfo::OWNED_BY_US;
- info.mData = new ABuffer(mem->pointer(), def.nBufferSize);
- mBuffers[portIndex].push(info);
+ sp<AMessage> notify = mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatBuffersAllocated);
+
+ notify->setInt32("portIndex", portIndex);
+ for (size_t i = 0; i < mBuffers[portIndex].size(); ++i) {
+ AString name = StringPrintf("buffer-id_%d", i);
+ notify->setPointer(name.c_str(), mBuffers[portIndex][i].mBufferID);
+
+ name = StringPrintf("data_%d", i);
+ notify->setBuffer(name.c_str(), mBuffers[portIndex][i].mData);
}
+ notify->post();
+
return OK;
}
@@ -671,7 +723,7 @@ ACodec::BufferInfo *ACodec::findBufferByID(
return NULL;
}
-void ACodec::setComponentRole(
+status_t ACodec::setComponentRole(
bool isEncoder, const char *mime) {
struct MimeToRole {
const char *mime;
@@ -700,6 +752,8 @@ void ACodec::setComponentRole(
"video_decoder.mpeg4", "video_encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_H263,
"video_decoder.h263", "video_encoder.h263" },
+ { MEDIA_MIMETYPE_VIDEO_VPX,
+ "video_decoder.vpx", "video_encoder.vpx" },
};
static const size_t kNumMimeToRole =
@@ -713,7 +767,7 @@ void ACodec::setComponentRole(
}
if (i == kNumMimeToRole) {
- return;
+ return ERROR_UNSUPPORTED;
}
const char *role =
@@ -736,50 +790,83 @@ void ACodec::setComponentRole(
if (err != OK) {
ALOGW("[%s] Failed to set standard component role '%s'.",
mComponentName.c_str(), role);
+
+ return err;
}
}
+
+ return OK;
}
-void ACodec::configureCodec(
+status_t ACodec::configureCodec(
const char *mime, const sp<AMessage> &msg) {
- setComponentRole(false /* isEncoder */, mime);
+ int32_t encoder;
+ if (!msg->findInt32("encoder", &encoder)) {
+ encoder = false;
+ }
- if (!strncasecmp(mime, "video/", 6)) {
- int32_t width, height;
- CHECK(msg->findInt32("width", &width));
- CHECK(msg->findInt32("height", &height));
+ mIsEncoder = encoder;
- CHECK_EQ(setupVideoDecoder(mime, width, height),
- (status_t)OK);
+ status_t err = setComponentRole(encoder /* isEncoder */, mime);
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t bitRate = 0;
+ if (encoder && !msg->findInt32("bitrate", &bitRate)) {
+ return INVALID_OPERATION;
+ }
+
+ if (!strncasecmp(mime, "video/", 6)) {
+ if (encoder) {
+ err = setupVideoEncoder(mime, msg);
+ } else {
+ int32_t width, height;
+ if (!msg->findInt32("width", &width)
+ || !msg->findInt32("height", &height)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupVideoDecoder(mime, width, height);
+ }
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AAC)) {
int32_t numChannels, sampleRate;
- CHECK(msg->findInt32("channel-count", &numChannels));
- CHECK(msg->findInt32("sample-rate", &sampleRate));
-
- CHECK_EQ(setupAACDecoder(numChannels, sampleRate), (status_t)OK);
+ if (!msg->findInt32("channel-count", &numChannels)
+ || !msg->findInt32("sample-rate", &sampleRate)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupAACCodec(encoder, numChannels, sampleRate, bitRate);
+ }
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_NB)) {
- CHECK_EQ(setupAMRDecoder(false /* isWAMR */), (status_t)OK);
+ err = setupAMRCodec(encoder, false /* isWAMR */, bitRate);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_AMR_WB)) {
- CHECK_EQ(setupAMRDecoder(true /* isWAMR */), (status_t)OK);
+ err = setupAMRCodec(encoder, true /* isWAMR */, bitRate);
} else if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_ALAW)
|| !strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_G711_MLAW)) {
// These are PCM-like formats with a fixed sample rate but
// a variable number of channels.
int32_t numChannels;
- CHECK(msg->findInt32("channel-count", &numChannels));
+ if (!msg->findInt32("channel-count", &numChannels)) {
+ err = INVALID_OPERATION;
+ } else {
+ err = setupG711Codec(encoder, numChannels);
+ }
+ }
- CHECK_EQ(setupG711Decoder(numChannels), (status_t)OK);
+ if (err != OK) {
+ return err;
}
int32_t maxInputSize;
if (msg->findInt32("max-input-size", &maxInputSize)) {
- CHECK_EQ(setMinBufferSize(kPortIndexInput, (size_t)maxInputSize),
- (status_t)OK);
+ err = setMinBufferSize(kPortIndexInput, (size_t)maxInputSize);
} else if (!strcmp("OMX.Nvidia.aac.decoder", mComponentName.c_str())) {
- CHECK_EQ(setMinBufferSize(kPortIndexInput, 8192), // XXX
- (status_t)OK);
+ err = setMinBufferSize(kPortIndexInput, 8192); // XXX
}
+
+ return err;
}
status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
@@ -819,12 +906,113 @@ status_t ACodec::setMinBufferSize(OMX_U32 portIndex, size_t size) {
return OK;
}
-status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
+status_t ACodec::selectAudioPortFormat(
+ OMX_U32 portIndex, OMX_AUDIO_CODINGTYPE desiredFormat) {
+ OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
+
+ format.nPortIndex = portIndex;
+ for (OMX_U32 index = 0;; ++index) {
+ format.nIndex = index;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioPortFormat,
+ &format, sizeof(format));
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (format.eEncoding == desiredFormat) {
+ break;
+ }
+ }
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamAudioPortFormat, &format, sizeof(format));
+}
+
+status_t ACodec::setupAACCodec(
+ bool encoder,
+ int32_t numChannels, int32_t sampleRate, int32_t bitRate) {
+ status_t err = setupRawAudioFormat(
+ encoder ? kPortIndexInput : kPortIndexOutput,
+ sampleRate,
+ numChannels);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (encoder) {
+ err = selectAudioPortFormat(kPortIndexOutput, OMX_AUDIO_CodingAAC);
+
+ if (err != OK) {
+ return err;
+ }
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+ def.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ def.format.audio.bFlagErrorConcealment = OMX_TRUE;
+ def.format.audio.eEncoding = OMX_AUDIO_CodingAAC;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ OMX_AUDIO_PARAM_AACPROFILETYPE profile;
+ InitOMXParams(&profile);
+ profile.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+ if (err != OK) {
+ return err;
+ }
+
+ profile.nChannels = numChannels;
+
+ profile.eChannelMode =
+ (numChannels == 1)
+ ? OMX_AUDIO_ChannelModeMono: OMX_AUDIO_ChannelModeStereo;
+
+ profile.nSampleRate = sampleRate;
+ profile.nBitRate = bitRate;
+ profile.nAudioBandWidth = 0;
+ profile.nFrameLength = 0;
+ profile.nAACtools = OMX_AUDIO_AACToolAll;
+ profile.nAACERtools = OMX_AUDIO_AACERNone;
+ profile.eAACProfile = OMX_AUDIO_AACObjectLC;
+ profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4FF;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+
+ if (err != OK) {
+ return err;
+ }
+
+ return err;
+ }
+
OMX_AUDIO_PARAM_AACPROFILETYPE profile;
InitOMXParams(&profile);
profile.nPortIndex = kPortIndexInput;
- status_t err = mOMX->getParameter(
+ err = mOMX->getParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
if (err != OK) {
@@ -835,16 +1023,59 @@ status_t ACodec::setupAACDecoder(int32_t numChannels, int32_t sampleRate) {
profile.nSampleRate = sampleRate;
profile.eAACStreamFormat = OMX_AUDIO_AACStreamFormatMP4ADTS;
- err = mOMX->setParameter(
+ return mOMX->setParameter(
mNode, OMX_IndexParamAudioAac, &profile, sizeof(profile));
+}
- return err;
+static OMX_AUDIO_AMRBANDMODETYPE pickModeFromBitRate(
+ bool isAMRWB, int32_t bps) {
+ if (isAMRWB) {
+ if (bps <= 6600) {
+ return OMX_AUDIO_AMRBandModeWB0;
+ } else if (bps <= 8850) {
+ return OMX_AUDIO_AMRBandModeWB1;
+ } else if (bps <= 12650) {
+ return OMX_AUDIO_AMRBandModeWB2;
+ } else if (bps <= 14250) {
+ return OMX_AUDIO_AMRBandModeWB3;
+ } else if (bps <= 15850) {
+ return OMX_AUDIO_AMRBandModeWB4;
+ } else if (bps <= 18250) {
+ return OMX_AUDIO_AMRBandModeWB5;
+ } else if (bps <= 19850) {
+ return OMX_AUDIO_AMRBandModeWB6;
+ } else if (bps <= 23050) {
+ return OMX_AUDIO_AMRBandModeWB7;
+ }
+
+ // 23850 bps
+ return OMX_AUDIO_AMRBandModeWB8;
+ } else { // AMRNB
+ if (bps <= 4750) {
+ return OMX_AUDIO_AMRBandModeNB0;
+ } else if (bps <= 5150) {
+ return OMX_AUDIO_AMRBandModeNB1;
+ } else if (bps <= 5900) {
+ return OMX_AUDIO_AMRBandModeNB2;
+ } else if (bps <= 6700) {
+ return OMX_AUDIO_AMRBandModeNB3;
+ } else if (bps <= 7400) {
+ return OMX_AUDIO_AMRBandModeNB4;
+ } else if (bps <= 7950) {
+ return OMX_AUDIO_AMRBandModeNB5;
+ } else if (bps <= 10200) {
+ return OMX_AUDIO_AMRBandModeNB6;
+ }
+
+ // 12200 bps
+ return OMX_AUDIO_AMRBandModeNB7;
+ }
}
-status_t ACodec::setupAMRDecoder(bool isWAMR) {
+status_t ACodec::setupAMRCodec(bool encoder, bool isWAMR, int32_t bitrate) {
OMX_AUDIO_PARAM_AMRTYPE def;
InitOMXParams(&def);
- def.nPortIndex = kPortIndexInput;
+ def.nPortIndex = encoder ? kPortIndexOutput : kPortIndexInput;
status_t err =
mOMX->getParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
@@ -854,14 +1085,24 @@ status_t ACodec::setupAMRDecoder(bool isWAMR) {
}
def.eAMRFrameFormat = OMX_AUDIO_AMRFrameFormatFSF;
+ def.eAMRBandMode = pickModeFromBitRate(isWAMR, bitrate);
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
- def.eAMRBandMode =
- isWAMR ? OMX_AUDIO_AMRBandModeWB0 : OMX_AUDIO_AMRBandModeNB0;
+ if (err != OK) {
+ return err;
+ }
- return mOMX->setParameter(mNode, OMX_IndexParamAudioAmr, &def, sizeof(def));
+ return setupRawAudioFormat(
+ encoder ? kPortIndexInput : kPortIndexOutput,
+ isWAMR ? 16000 : 8000 /* sampleRate */,
+ 1 /* numChannels */);
}
-status_t ACodec::setupG711Decoder(int32_t numChannels) {
+status_t ACodec::setupG711Codec(bool encoder, int32_t numChannels) {
+ CHECK(!encoder); // XXX TODO
+
return setupRawAudioFormat(
kPortIndexInput, 8000 /* sampleRate */, numChannels);
}
@@ -1001,22 +1242,36 @@ status_t ACodec::setSupportedOutputFormat() {
&format, sizeof(format));
}
-status_t ACodec::setupVideoDecoder(
- const char *mime, int32_t width, int32_t height) {
- OMX_VIDEO_CODINGTYPE compressionFormat = OMX_VIDEO_CodingUnused;
+static status_t GetVideoCodingTypeFromMime(
+ const char *mime, OMX_VIDEO_CODINGTYPE *codingType) {
if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_AVC, mime)) {
- compressionFormat = OMX_VIDEO_CodingAVC;
+ *codingType = OMX_VIDEO_CodingAVC;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG4, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG4;
+ *codingType = OMX_VIDEO_CodingMPEG4;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_H263, mime)) {
- compressionFormat = OMX_VIDEO_CodingH263;
+ *codingType = OMX_VIDEO_CodingH263;
} else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_MPEG2, mime)) {
- compressionFormat = OMX_VIDEO_CodingMPEG2;
+ *codingType = OMX_VIDEO_CodingMPEG2;
+ } else if (!strcasecmp(MEDIA_MIMETYPE_VIDEO_VPX, mime)) {
+ *codingType = OMX_VIDEO_CodingVPX;
} else {
- TRESPASS();
+ *codingType = OMX_VIDEO_CodingUnused;
+ return ERROR_UNSUPPORTED;
}
- status_t err = setVideoPortFormatType(
+ return OK;
+}
+
+status_t ACodec::setupVideoDecoder(
+ const char *mime, int32_t width, int32_t height) {
+ OMX_VIDEO_CODINGTYPE compressionFormat;
+ status_t err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setVideoPortFormatType(
kPortIndexInput, compressionFormat, OMX_COLOR_FormatUnused);
if (err != OK) {
@@ -1046,6 +1301,489 @@ status_t ACodec::setupVideoDecoder(
return OK;
}
+status_t ACodec::setupVideoEncoder(const char *mime, const sp<AMessage> &msg) {
+ int32_t tmp;
+ if (!msg->findInt32("color-format", &tmp)) {
+ return INVALID_OPERATION;
+ }
+
+ OMX_COLOR_FORMATTYPE colorFormat =
+ static_cast<OMX_COLOR_FORMATTYPE>(tmp);
+
+ status_t err = setVideoPortFormatType(
+ kPortIndexInput, OMX_VIDEO_CodingUnused, colorFormat);
+
+ if (err != OK) {
+ ALOGE("[%s] does not support color format %d",
+ mComponentName.c_str(), colorFormat);
+
+ return err;
+ }
+
+ /* Input port configuration */
+
+ OMX_PARAM_PORTDEFINITIONTYPE def;
+ InitOMXParams(&def);
+
+ OMX_VIDEO_PORTDEFINITIONTYPE *video_def = &def.format.video;
+
+ def.nPortIndex = kPortIndexInput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t width, height, bitrate;
+ if (!msg->findInt32("width", &width)
+ || !msg->findInt32("height", &height)
+ || !msg->findInt32("bitrate", &bitrate)) {
+ return INVALID_OPERATION;
+ }
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+
+ int32_t stride;
+ if (!msg->findInt32("stride", &stride)) {
+ stride = width;
+ }
+
+ video_def->nStride = stride;
+
+ int32_t sliceHeight;
+ if (!msg->findInt32("slice-height", &sliceHeight)) {
+ sliceHeight = height;
+ }
+
+ video_def->nSliceHeight = sliceHeight;
+
+ def.nBufferSize = (video_def->nStride * video_def->nSliceHeight * 3) / 2;
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ video_def->xFramerate = (OMX_U32)(frameRate * 65536.0f);
+ video_def->eCompressionFormat = OMX_VIDEO_CodingUnused;
+ video_def->eColorFormat = colorFormat;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ ALOGE("[%s] failed to set input port definition parameters.",
+ mComponentName.c_str());
+
+ return err;
+ }
+
+ /* Output port configuration */
+
+ OMX_VIDEO_CODINGTYPE compressionFormat;
+ err = GetVideoCodingTypeFromMime(mime, &compressionFormat);
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = setVideoPortFormatType(
+ kPortIndexOutput, compressionFormat, OMX_COLOR_FormatUnused);
+
+ if (err != OK) {
+ ALOGE("[%s] does not support compression format %d",
+ mComponentName.c_str(), compressionFormat);
+
+ return err;
+ }
+
+ def.nPortIndex = kPortIndexOutput;
+
+ err = mOMX->getParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ return err;
+ }
+
+ video_def->nFrameWidth = width;
+ video_def->nFrameHeight = height;
+ video_def->xFramerate = 0;
+ video_def->nBitrate = bitrate;
+ video_def->eCompressionFormat = compressionFormat;
+ video_def->eColorFormat = OMX_COLOR_FormatUnused;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamPortDefinition, &def, sizeof(def));
+
+ if (err != OK) {
+ ALOGE("[%s] failed to set output port definition parameters.",
+ mComponentName.c_str());
+
+ return err;
+ }
+
+ switch (compressionFormat) {
+ case OMX_VIDEO_CodingMPEG4:
+ err = setupMPEG4EncoderParameters(msg);
+ break;
+
+ case OMX_VIDEO_CodingH263:
+ err = setupH263EncoderParameters(msg);
+ break;
+
+ case OMX_VIDEO_CodingAVC:
+ err = setupAVCEncoderParameters(msg);
+ break;
+
+ default:
+ break;
+ }
+
+ ALOGI("setupVideoEncoder succeeded");
+
+ return err;
+}
+
+static OMX_U32 setPFramesSpacing(int32_t iFramesInterval, int32_t frameRate) {
+ if (iFramesInterval < 0) {
+ return 0xFFFFFFFF;
+ } else if (iFramesInterval == 0) {
+ return 0;
+ }
+ OMX_U32 ret = frameRate * iFramesInterval;
+ CHECK(ret > 1);
+ return ret;
+}
+
+status_t ACodec::setupMPEG4EncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate, iFrameInterval;
+ if (!msg->findInt32("bitrate", &bitrate)
+ || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ OMX_VIDEO_PARAM_MPEG4TYPE mpeg4type;
+ InitOMXParams(&mpeg4type);
+ mpeg4type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ mpeg4type.nSliceHeaderSpacing = 0;
+ mpeg4type.bSVH = OMX_FALSE;
+ mpeg4type.bGov = OMX_FALSE;
+
+ mpeg4type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ mpeg4type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ if (mpeg4type.nPFrames == 0) {
+ mpeg4type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+ mpeg4type.nBFrames = 0;
+ mpeg4type.nIDCVLCThreshold = 0;
+ mpeg4type.bACPred = OMX_TRUE;
+ mpeg4type.nMaxPacketSize = 256;
+ mpeg4type.nTimeIncRes = 1000;
+ mpeg4type.nHeaderExtension = 0;
+ mpeg4type.bReversibleVLC = OMX_FALSE;
+
+ int32_t profile;
+ if (msg->findInt32("profile", &profile)) {
+ int32_t level;
+ if (!msg->findInt32("level", &level)) {
+ return INVALID_OPERATION;
+ }
+
+ err = verifySupportForProfileAndLevel(profile, level);
+
+ if (err != OK) {
+ return err;
+ }
+
+ mpeg4type.eProfile = static_cast<OMX_VIDEO_MPEG4PROFILETYPE>(profile);
+ mpeg4type.eLevel = static_cast<OMX_VIDEO_MPEG4LEVELTYPE>(level);
+ }
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoMpeg4, &mpeg4type, sizeof(mpeg4type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = configureBitrate(bitrate);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return setupErrorCorrectionParameters();
+}
+
+status_t ACodec::setupH263EncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate, iFrameInterval;
+ if (!msg->findInt32("bitrate", &bitrate)
+ || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ OMX_VIDEO_PARAM_H263TYPE h263type;
+ InitOMXParams(&h263type);
+ h263type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ h263type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ h263type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ if (h263type.nPFrames == 0) {
+ h263type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+ h263type.nBFrames = 0;
+
+ int32_t profile;
+ if (msg->findInt32("profile", &profile)) {
+ int32_t level;
+ if (!msg->findInt32("level", &level)) {
+ return INVALID_OPERATION;
+ }
+
+ err = verifySupportForProfileAndLevel(profile, level);
+
+ if (err != OK) {
+ return err;
+ }
+
+ h263type.eProfile = static_cast<OMX_VIDEO_H263PROFILETYPE>(profile);
+ h263type.eLevel = static_cast<OMX_VIDEO_H263LEVELTYPE>(level);
+ }
+
+ h263type.bPLUSPTYPEAllowed = OMX_FALSE;
+ h263type.bForceRoundingTypeToZero = OMX_FALSE;
+ h263type.nPictureHeaderRepetition = 0;
+ h263type.nGOBHeaderInterval = 0;
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoH263, &h263type, sizeof(h263type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ err = configureBitrate(bitrate);
+
+ if (err != OK) {
+ return err;
+ }
+
+ return setupErrorCorrectionParameters();
+}
+
+status_t ACodec::setupAVCEncoderParameters(const sp<AMessage> &msg) {
+ int32_t bitrate, iFrameInterval;
+ if (!msg->findInt32("bitrate", &bitrate)
+ || !msg->findInt32("i-frame-interval", &iFrameInterval)) {
+ return INVALID_OPERATION;
+ }
+
+ float frameRate;
+ if (!msg->findFloat("frame-rate", &frameRate)) {
+ int32_t tmp;
+ if (!msg->findInt32("frame-rate", &tmp)) {
+ return INVALID_OPERATION;
+ }
+ frameRate = (float)tmp;
+ }
+
+ OMX_VIDEO_PARAM_AVCTYPE h264type;
+ InitOMXParams(&h264type);
+ h264type.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ h264type.nAllowedPictureTypes =
+ OMX_VIDEO_PictureTypeI | OMX_VIDEO_PictureTypeP;
+
+ int32_t profile;
+ if (msg->findInt32("profile", &profile)) {
+ int32_t level;
+ if (!msg->findInt32("level", &level)) {
+ return INVALID_OPERATION;
+ }
+
+ err = verifySupportForProfileAndLevel(profile, level);
+
+ if (err != OK) {
+ return err;
+ }
+
+ h264type.eProfile = static_cast<OMX_VIDEO_AVCPROFILETYPE>(profile);
+ h264type.eLevel = static_cast<OMX_VIDEO_AVCLEVELTYPE>(level);
+ }
+
+ // XXX
+ if (!strncmp(mComponentName.c_str(), "OMX.TI.DUCATI1", 14)) {
+ h264type.eProfile = OMX_VIDEO_AVCProfileBaseline;
+ }
+
+ if (h264type.eProfile == OMX_VIDEO_AVCProfileBaseline) {
+ h264type.nSliceHeaderSpacing = 0;
+ h264type.bUseHadamard = OMX_TRUE;
+ h264type.nRefFrames = 1;
+ h264type.nBFrames = 0;
+ h264type.nPFrames = setPFramesSpacing(iFrameInterval, frameRate);
+ if (h264type.nPFrames == 0) {
+ h264type.nAllowedPictureTypes = OMX_VIDEO_PictureTypeI;
+ }
+ h264type.nRefIdx10ActiveMinus1 = 0;
+ h264type.nRefIdx11ActiveMinus1 = 0;
+ h264type.bEntropyCodingCABAC = OMX_FALSE;
+ h264type.bWeightedPPrediction = OMX_FALSE;
+ h264type.bconstIpred = OMX_FALSE;
+ h264type.bDirect8x8Inference = OMX_FALSE;
+ h264type.bDirectSpatialTemporal = OMX_FALSE;
+ h264type.nCabacInitIdc = 0;
+ }
+
+ if (h264type.nBFrames != 0) {
+ h264type.nAllowedPictureTypes |= OMX_VIDEO_PictureTypeB;
+ }
+
+ h264type.bEnableUEP = OMX_FALSE;
+ h264type.bEnableFMO = OMX_FALSE;
+ h264type.bEnableASO = OMX_FALSE;
+ h264type.bEnableRS = OMX_FALSE;
+ h264type.bFrameMBsOnly = OMX_TRUE;
+ h264type.bMBAFF = OMX_FALSE;
+ h264type.eLoopFilterMode = OMX_VIDEO_AVCLoopFilterEnable;
+
+ if (!strcasecmp("OMX.Nvidia.h264.encoder", mComponentName.c_str())) {
+ h264type.eLevel = OMX_VIDEO_AVCLevelMax;
+ }
+
+ err = mOMX->setParameter(
+ mNode, OMX_IndexParamVideoAvc, &h264type, sizeof(h264type));
+
+ if (err != OK) {
+ return err;
+ }
+
+ return configureBitrate(bitrate);
+}
+
+status_t ACodec::verifySupportForProfileAndLevel(
+ int32_t profile, int32_t level) {
+ OMX_VIDEO_PARAM_PROFILELEVELTYPE params;
+ InitOMXParams(&params);
+ params.nPortIndex = kPortIndexOutput;
+
+ for (params.nProfileIndex = 0;; ++params.nProfileIndex) {
+ status_t err = mOMX->getParameter(
+ mNode,
+ OMX_IndexParamVideoProfileLevelQuerySupported,
+ &params,
+ sizeof(params));
+
+ if (err != OK) {
+ return err;
+ }
+
+ int32_t supportedProfile = static_cast<int32_t>(params.eProfile);
+ int32_t supportedLevel = static_cast<int32_t>(params.eLevel);
+
+ if (profile == supportedProfile && level <= supportedLevel) {
+ return OK;
+ }
+ }
+}
+
+status_t ACodec::configureBitrate(int32_t bitrate) {
+ OMX_VIDEO_PARAM_BITRATETYPE bitrateType;
+ InitOMXParams(&bitrateType);
+ bitrateType.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+
+ if (err != OK) {
+ return err;
+ }
+
+ bitrateType.eControlRate = OMX_Video_ControlRateVariable;
+ bitrateType.nTargetBitrate = bitrate;
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamVideoBitrate,
+ &bitrateType, sizeof(bitrateType));
+}
+
+status_t ACodec::setupErrorCorrectionParameters() {
+ OMX_VIDEO_PARAM_ERRORCORRECTIONTYPE errorCorrectionType;
+ InitOMXParams(&errorCorrectionType);
+ errorCorrectionType.nPortIndex = kPortIndexOutput;
+
+ status_t err = mOMX->getParameter(
+ mNode, OMX_IndexParamVideoErrorCorrection,
+ &errorCorrectionType, sizeof(errorCorrectionType));
+
+ if (err != OK) {
+ return OK; // Optional feature. Ignore this failure
+ }
+
+ errorCorrectionType.bEnableHEC = OMX_FALSE;
+ errorCorrectionType.bEnableResync = OMX_TRUE;
+ errorCorrectionType.nResynchMarkerSpacing = 256;
+ errorCorrectionType.bEnableDataPartitioning = OMX_FALSE;
+ errorCorrectionType.bEnableRVLC = OMX_FALSE;
+
+ return mOMX->setParameter(
+ mNode, OMX_IndexParamVideoErrorCorrection,
+ &errorCorrectionType, sizeof(errorCorrectionType));
+}
+
status_t ACodec::setVideoFormatOnPort(
OMX_U32 portIndex,
int32_t width, int32_t height, OMX_VIDEO_CODINGTYPE compressionFormat) {
@@ -1166,6 +1904,9 @@ void ACodec::sendFormatChange() {
notify->setString("mime", MEDIA_MIMETYPE_VIDEO_RAW);
notify->setInt32("width", videoDef->nFrameWidth);
notify->setInt32("height", videoDef->nFrameHeight);
+ notify->setInt32("stride", videoDef->nStride);
+ notify->setInt32("slice-height", videoDef->nSliceHeight);
+ notify->setInt32("color-format", videoDef->eColorFormat);
OMX_CONFIG_RECTTYPE rect;
InitOMXParams(&rect);
@@ -1241,10 +1982,11 @@ void ACodec::sendFormatChange() {
mSentFormat = true;
}
-void ACodec::signalError(OMX_ERRORTYPE error) {
+void ACodec::signalError(OMX_ERRORTYPE error, status_t internalError) {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", ACodec::kWhatError);
notify->setInt32("omx-error", error);
+ notify->setInt32("err", internalError);
notify->post();
}
@@ -1417,7 +2159,7 @@ void ACodec::BaseState::postFillThisBuffer(BufferInfo *info) {
notify->setPointer("buffer-id", info->mBufferID);
info->mData->meta()->clear();
- notify->setObject("buffer", info->mData);
+ notify->setBuffer("buffer", info->mData);
sp<AMessage> reply = new AMessage(kWhatInputBufferFilled, mCodec->id());
reply->setPointer("buffer-id", info->mBufferID);
@@ -1433,18 +2175,26 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
IOMX::buffer_id bufferID;
CHECK(msg->findPointer("buffer-id", &bufferID));
- sp<RefBase> obj;
+ sp<ABuffer> buffer;
int32_t err = OK;
- if (!msg->findObject("buffer", &obj)) {
+ bool eos = false;
+
+ if (!msg->findBuffer("buffer", &buffer)) {
CHECK(msg->findInt32("err", &err));
ALOGV("[%s] saw error %d instead of an input buffer",
mCodec->mComponentName.c_str(), err);
- obj.clear();
+ buffer.clear();
+
+ eos = true;
}
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ int32_t tmp;
+ if (buffer != NULL && buffer->meta()->findInt32("eos", &tmp) && tmp) {
+ eos = true;
+ err = ERROR_END_OF_STREAM;
+ }
BufferInfo *info = mCodec->findBufferByID(kPortIndexInput, bufferID);
CHECK_EQ((int)info->mStatus, (int)BufferInfo::OWNED_BY_UPSTREAM);
@@ -1456,7 +2206,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
switch (mode) {
case KEEP_BUFFERS:
{
- if (buffer == NULL) {
+ if (eos) {
if (!mCodec->mPortEOS[kPortIndexInput]) {
mCodec->mPortEOS[kPortIndexInput] = true;
mCodec->mInputEOSResult = err;
@@ -1467,9 +2217,7 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
case RESUBMIT_BUFFERS:
{
- if (buffer != NULL) {
- CHECK(!mCodec->mPortEOS[kPortIndexInput]);
-
+ if (buffer != NULL && !mCodec->mPortEOS[kPortIndexInput]) {
int64_t timeUs;
CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
@@ -1480,6 +2228,10 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
flags |= OMX_BUFFERFLAG_CODECCONFIG;
}
+ if (eos) {
+ flags |= OMX_BUFFERFLAG_EOS;
+ }
+
if (buffer != info->mData) {
if (0 && !(flags & OMX_BUFFERFLAG_CODECCONFIG)) {
ALOGV("[%s] Needs to copy input data.",
@@ -1493,6 +2245,9 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
if (flags & OMX_BUFFERFLAG_CODECCONFIG) {
ALOGV("[%s] calling emptyBuffer %p w/ codec specific data",
mCodec->mComponentName.c_str(), bufferID);
+ } else if (flags & OMX_BUFFERFLAG_EOS) {
+ ALOGV("[%s] calling emptyBuffer %p w/ EOS",
+ mCodec->mComponentName.c_str(), bufferID);
} else {
ALOGV("[%s] calling emptyBuffer %p w/ time %lld us",
mCodec->mComponentName.c_str(), bufferID, timeUs);
@@ -1509,7 +2264,15 @@ void ACodec::BaseState::onInputBufferFilled(const sp<AMessage> &msg) {
info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- getMoreInputDataIfPossible();
+ if (!eos) {
+ getMoreInputDataIfPossible();
+ } else {
+ ALOGV("[%s] Signalled EOS on the input port",
+ mCodec->mComponentName.c_str());
+
+ mCodec->mPortEOS[kPortIndexInput] = true;
+ mCodec->mInputEOSResult = err;
+ }
} else if (!mCodec->mPortEOS[kPortIndexInput]) {
if (err != ERROR_END_OF_STREAM) {
ALOGV("[%s] Signalling EOS on the input port "
@@ -1582,8 +2345,8 @@ bool ACodec::BaseState::onOMXFillBufferDone(
int64_t timeUs,
void *platformPrivate,
void *dataPtr) {
- ALOGV("[%s] onOMXFillBufferDone %p time %lld us",
- mCodec->mComponentName.c_str(), bufferID, timeUs);
+ ALOGV("[%s] onOMXFillBufferDone %p time %lld us, flags = 0x%08lx",
+ mCodec->mComponentName.c_str(), bufferID, timeUs, flags);
ssize_t index;
BufferInfo *info =
@@ -1601,46 +2364,48 @@ bool ACodec::BaseState::onOMXFillBufferDone(
case RESUBMIT_BUFFERS:
{
- if (rangeLength == 0) {
- if (!(flags & OMX_BUFFERFLAG_EOS)) {
- ALOGV("[%s] calling fillBuffer %p",
- mCodec->mComponentName.c_str(), info->mBufferID);
+ if (rangeLength == 0 && !(flags & OMX_BUFFERFLAG_EOS)) {
+ ALOGV("[%s] calling fillBuffer %p",
+ mCodec->mComponentName.c_str(), info->mBufferID);
- CHECK_EQ(mCodec->mOMX->fillBuffer(
- mCodec->mNode, info->mBufferID),
- (status_t)OK);
+ CHECK_EQ(mCodec->mOMX->fillBuffer(
+ mCodec->mNode, info->mBufferID),
+ (status_t)OK);
- info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
- }
- } else {
- if (!mCodec->mSentFormat) {
- mCodec->sendFormatChange();
- }
+ info->mStatus = BufferInfo::OWNED_BY_COMPONENT;
+ break;
+ }
- if (mCodec->mNativeWindow == NULL) {
- info->mData->setRange(rangeOffset, rangeLength);
- }
+ if (!mCodec->mIsEncoder && !mCodec->mSentFormat) {
+ mCodec->sendFormatChange();
+ }
- info->mData->meta()->setInt64("timeUs", timeUs);
+ if (mCodec->mNativeWindow == NULL) {
+ info->mData->setRange(rangeOffset, rangeLength);
+ }
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
- notify->setPointer("buffer-id", info->mBufferID);
- notify->setObject("buffer", info->mData);
+ info->mData->meta()->setInt64("timeUs", timeUs);
- sp<AMessage> reply =
- new AMessage(kWhatOutputBufferDrained, mCodec->id());
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatDrainThisBuffer);
+ notify->setPointer("buffer-id", info->mBufferID);
+ notify->setBuffer("buffer", info->mData);
+ notify->setInt32("flags", flags);
- reply->setPointer("buffer-id", info->mBufferID);
+ sp<AMessage> reply =
+ new AMessage(kWhatOutputBufferDrained, mCodec->id());
- notify->setMessage("reply", reply);
+ reply->setPointer("buffer-id", info->mBufferID);
- notify->post();
+ notify->setMessage("reply", reply);
- info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
- }
+ notify->post();
+
+ info->mStatus = BufferInfo::OWNED_BY_DOWNSTREAM;
if (flags & OMX_BUFFERFLAG_EOS) {
+ ALOGV("[%s] saw output EOS", mCodec->mComponentName.c_str());
+
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatEOS);
notify->setInt32("err", mCodec->mInputEOSResult);
@@ -1678,12 +2443,13 @@ void ACodec::BaseState::onOutputBufferDrained(const sp<AMessage> &msg) {
&& msg->findInt32("render", &render) && render != 0) {
// The client wants this buffer to be rendered.
- if (mCodec->mNativeWindow->queueBuffer(
+ status_t err;
+ if ((err = mCodec->mNativeWindow->queueBuffer(
mCodec->mNativeWindow.get(),
- info->mGraphicBuffer.get()) == OK) {
+ info->mGraphicBuffer.get())) == OK) {
info->mStatus = BufferInfo::OWNED_BY_NATIVE_WINDOW;
} else {
- mCodec->signalError();
+ mCodec->signalError(OMX_ErrorUndefined, err);
info->mStatus = BufferInfo::OWNED_BY_US;
}
} else {
@@ -1746,6 +2512,10 @@ ACodec::UninitializedState::UninitializedState(ACodec *codec)
: BaseState(codec) {
}
+void ACodec::UninitializedState::stateEntered() {
+ ALOGV("Now uninitialized");
+}
+
bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
bool handled = false;
@@ -1758,8 +2528,20 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
break;
}
+ case ACodec::kWhatAllocateComponent:
+ {
+ onAllocateComponent(msg);
+ handled = true;
+ break;
+ }
+
case ACodec::kWhatShutdown:
{
+ int32_t keepComponentAllocated;
+ CHECK(msg->findInt32(
+ "keepComponentAllocated", &keepComponentAllocated));
+ CHECK(!keepComponentAllocated);
+
sp<AMessage> notify = mCodec->mNotify->dup();
notify->setInt32("what", ACodec::kWhatShutdownCompleted);
notify->post();
@@ -1787,30 +2569,60 @@ bool ACodec::UninitializedState::onMessageReceived(const sp<AMessage> &msg) {
void ACodec::UninitializedState::onSetup(
const sp<AMessage> &msg) {
+ if (onAllocateComponent(msg)
+ && mCodec->mLoadedState->onConfigureComponent(msg)) {
+ mCodec->mLoadedState->onStart();
+ }
+}
+
+bool ACodec::UninitializedState::onAllocateComponent(const sp<AMessage> &msg) {
+ ALOGV("onAllocateComponent");
+
+ CHECK(mCodec->mNode == NULL);
+
OMXClient client;
CHECK_EQ(client.connect(), (status_t)OK);
sp<IOMX> omx = client.interface();
+ Vector<String8> matchingCodecs;
+ Vector<uint32_t> matchingCodecQuirks;
+
AString mime;
- CHECK(msg->findString("mime", &mime));
- Vector<String8> matchingCodecs;
- OMXCodec::findMatchingCodecs(
- mime.c_str(),
- false, // createEncoder
- NULL, // matchComponentName
- 0, // flags
- &matchingCodecs);
+ AString componentName;
+ uint32_t quirks;
+ if (msg->findString("componentName", &componentName)) {
+ matchingCodecs.push_back(String8(componentName.c_str()));
+
+ if (!OMXCodec::findCodecQuirks(componentName.c_str(), &quirks)) {
+ quirks = 0;
+ }
+ matchingCodecQuirks.push_back(quirks);
+ } else {
+ CHECK(msg->findString("mime", &mime));
+
+ int32_t encoder;
+ if (!msg->findInt32("encoder", &encoder)) {
+ encoder = false;
+ }
+
+ OMXCodec::findMatchingCodecs(
+ mime.c_str(),
+ encoder, // createEncoder
+ NULL, // matchComponentName
+ 0, // flags
+ &matchingCodecs,
+ &matchingCodecQuirks);
+ }
sp<CodecObserver> observer = new CodecObserver;
IOMX::node_id node = NULL;
- AString componentName;
-
for (size_t matchIndex = 0; matchIndex < matchingCodecs.size();
++matchIndex) {
componentName = matchingCodecs.itemAt(matchIndex).string();
+ quirks = matchingCodecQuirks.itemAt(matchIndex);
pid_t tid = androidGetTid();
int prevPriority = androidGetThreadPriority(tid);
@@ -1826,16 +2638,22 @@ void ACodec::UninitializedState::onSetup(
}
if (node == NULL) {
- ALOGE("Unable to instantiate a decoder for type '%s'.", mime.c_str());
+ if (!mime.empty()) {
+ ALOGE("Unable to instantiate a decoder for type '%s'.",
+ mime.c_str());
+ } else {
+ ALOGE("Unable to instantiate decoder '%s'.", componentName.c_str());
+ }
mCodec->signalError(OMX_ErrorComponentNotFound);
- return;
+ return false;
}
sp<AMessage> notify = new AMessage(kWhatOMXMessage, mCodec->id());
observer->setNotificationMessage(notify);
mCodec->mComponentName = componentName;
+ mCodec->mQuirks = quirks;
mCodec->mOMX = omx;
mCodec->mNode = node;
@@ -1844,20 +2662,142 @@ void ACodec::UninitializedState::onSetup(
mCodec->mInputEOSResult = OK;
- mCodec->configureCodec(mime.c_str(), msg);
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatComponentAllocated);
+ notify->setString("componentName", mCodec->mComponentName.c_str());
+ notify->post();
+ }
+
+ mCodec->changeState(mCodec->mLoadedState);
+
+ return true;
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+ACodec::LoadedState::LoadedState(ACodec *codec)
+ : BaseState(codec) {
+}
+
+void ACodec::LoadedState::stateEntered() {
+ ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());
+
+ if (mCodec->mShutdownInProgress) {
+ bool keepComponentAllocated = mCodec->mKeepComponentAllocated;
+
+ mCodec->mShutdownInProgress = false;
+ mCodec->mKeepComponentAllocated = false;
+
+ onShutdown(keepComponentAllocated);
+ }
+}
+
+void ACodec::LoadedState::onShutdown(bool keepComponentAllocated) {
+ if (!keepComponentAllocated) {
+ CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
+
+ mCodec->mNativeWindow.clear();
+ mCodec->mNode = NULL;
+ mCodec->mOMX.clear();
+ mCodec->mQuirks = 0;
+ mCodec->mComponentName.clear();
+
+ mCodec->changeState(mCodec->mUninitializedState);
+ }
+
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatShutdownCompleted);
+ notify->post();
+}
+
+bool ACodec::LoadedState::onMessageReceived(const sp<AMessage> &msg) {
+ bool handled = false;
+
+ switch (msg->what()) {
+ case ACodec::kWhatConfigureComponent:
+ {
+ onConfigureComponent(msg);
+ handled = true;
+ break;
+ }
+
+ case ACodec::kWhatStart:
+ {
+ onStart();
+ handled = true;
+ break;
+ }
+
+ case ACodec::kWhatShutdown:
+ {
+ int32_t keepComponentAllocated;
+ CHECK(msg->findInt32(
+ "keepComponentAllocated", &keepComponentAllocated));
+
+ onShutdown(keepComponentAllocated);
+
+ handled = true;
+ break;
+ }
+
+ case ACodec::kWhatFlush:
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatFlushCompleted);
+ notify->post();
+
+ handled = true;
+ break;
+ }
+
+ default:
+ return BaseState::onMessageReceived(msg);
+ }
+
+ return handled;
+}
+
+bool ACodec::LoadedState::onConfigureComponent(
+ const sp<AMessage> &msg) {
+ ALOGV("onConfigureComponent");
+
+ CHECK(mCodec->mNode != NULL);
+
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ status_t err = mCodec->configureCodec(mime.c_str(), msg);
+
+ if (err != OK) {
+ mCodec->signalError(OMX_ErrorUndefined, err);
+ return false;
+ }
sp<RefBase> obj;
if (msg->findObject("native-window", &obj)
- && strncmp("OMX.google.", componentName.c_str(), 11)) {
+ && strncmp("OMX.google.", mCodec->mComponentName.c_str(), 11)) {
sp<NativeWindowWrapper> nativeWindow(
static_cast<NativeWindowWrapper *>(obj.get()));
CHECK(nativeWindow != NULL);
mCodec->mNativeWindow = nativeWindow->getNativeWindow();
}
-
CHECK_EQ((status_t)OK, mCodec->initNativeWindow());
- CHECK_EQ(omx->sendCommand(node, OMX_CommandStateSet, OMX_StateIdle),
+ {
+ sp<AMessage> notify = mCodec->mNotify->dup();
+ notify->setInt32("what", ACodec::kWhatComponentConfigured);
+ notify->post();
+ }
+
+ return true;
+}
+
+void ACodec::LoadedState::onStart() {
+ ALOGV("onStart");
+
+ CHECK_EQ(mCodec->mOMX->sendCommand(
+ mCodec->mNode, OMX_CommandStateSet, OMX_StateIdle),
(status_t)OK);
mCodec->changeState(mCodec->mLoadedToIdleState);
@@ -1878,7 +2818,7 @@ void ACodec::LoadedToIdleState::stateEntered() {
"(error 0x%08x)",
err);
- mCodec->signalError();
+ mCodec->signalError(OMX_ErrorUndefined, err);
}
}
@@ -2042,6 +2982,13 @@ bool ACodec::ExecutingState::onMessageReceived(const sp<AMessage> &msg) {
switch (msg->what()) {
case kWhatShutdown:
{
+ int32_t keepComponentAllocated;
+ CHECK(msg->findInt32(
+ "keepComponentAllocated", &keepComponentAllocated));
+
+ mCodec->mShutdownInProgress = true;
+ mCodec->mKeepComponentAllocated = keepComponentAllocated;
+
mActive = false;
CHECK_EQ(mCodec->mOMX->sendCommand(
@@ -2202,7 +3149,7 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
"port reconfiguration (error 0x%08x)",
err);
- mCodec->signalError();
+ mCodec->signalError(OMX_ErrorUndefined, err);
// This is technically not correct, since we were unable
// to allocate output buffers and therefore the output port
@@ -2240,7 +3187,8 @@ bool ACodec::OutputPortSettingsChangedState::onOMXEvent(
////////////////////////////////////////////////////////////////////////////////
ACodec::ExecutingToIdleState::ExecutingToIdleState(ACodec *codec)
- : BaseState(codec) {
+ : BaseState(codec),
+ mComponentNowIdle(false) {
}
bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
@@ -2274,6 +3222,7 @@ bool ACodec::ExecutingToIdleState::onMessageReceived(const sp<AMessage> &msg) {
void ACodec::ExecutingToIdleState::stateEntered() {
ALOGV("[%s] Now Executing->Idle", mCodec->mComponentName.c_str());
+ mComponentNowIdle = false;
mCodec->mSentFormat = false;
}
@@ -2285,6 +3234,8 @@ bool ACodec::ExecutingToIdleState::onOMXEvent(
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateIdle);
+ mComponentNowIdle = true;
+
changeStateIfWeOwnAllBuffers();
return true;
@@ -2303,7 +3254,7 @@ bool ACodec::ExecutingToIdleState::onOMXEvent(
}
void ACodec::ExecutingToIdleState::changeStateIfWeOwnAllBuffers() {
- if (mCodec->allYourBuffersAreBelongToUs()) {
+ if (mComponentNowIdle && mCodec->allYourBuffersAreBelongToUs()) {
CHECK_EQ(mCodec->mOMX->sendCommand(
mCodec->mNode, OMX_CommandStateSet, OMX_StateLoaded),
(status_t)OK);
@@ -2375,20 +3326,7 @@ bool ACodec::IdleToLoadedState::onOMXEvent(
CHECK_EQ(data1, (OMX_U32)OMX_CommandStateSet);
CHECK_EQ(data2, (OMX_U32)OMX_StateLoaded);
- ALOGV("[%s] Now Loaded", mCodec->mComponentName.c_str());
-
- CHECK_EQ(mCodec->mOMX->freeNode(mCodec->mNode), (status_t)OK);
-
- mCodec->mNativeWindow.clear();
- mCodec->mNode = NULL;
- mCodec->mOMX.clear();
- mCodec->mComponentName.clear();
-
- mCodec->changeState(mCodec->mUninitializedState);
-
- sp<AMessage> notify = mCodec->mNotify->dup();
- notify->setInt32("what", ACodec::kWhatShutdownCompleted);
- notify->post();
+ mCodec->changeState(mCodec->mLoadedState);
return true;
}
diff --git a/media/libstagefright/Android.mk b/media/libstagefright/Android.mk
index 483e5ab..5aea8d0 100644
--- a/media/libstagefright/Android.mk
+++ b/media/libstagefright/Android.mk
@@ -29,12 +29,14 @@ LOCAL_SRC_FILES:= \
MPEG4Writer.cpp \
MediaBuffer.cpp \
MediaBufferGroup.cpp \
+ MediaCodec.cpp \
+ MediaCodecList.cpp \
MediaDefs.cpp \
MediaExtractor.cpp \
MediaSource.cpp \
- MediaSourceSplitter.cpp \
MetaData.cpp \
NuCachedSource2.cpp \
+ NuMediaExtractor.cpp \
OMXClient.cpp \
OMXCodec.cpp \
OggExtractor.cpp \
@@ -56,25 +58,34 @@ LOCAL_SRC_FILES:= \
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
$(TOP)/frameworks/base/include/media/stagefright/openmax \
+ $(TOP)/frameworks/base/include/media/stagefright/timedtext \
+ $(TOP)/external/expat/lib \
$(TOP)/external/flac/include \
$(TOP)/external/tremolo \
$(TOP)/external/openssl/include \
LOCAL_SHARED_LIBRARIES := \
- libbinder \
- libmedia \
- libutils \
- libcutils \
- libui \
- libsonivox \
- libvorbisidec \
- libstagefright_yuv \
+ libbinder \
libcamera_client \
- libdrmframework \
- libcrypto \
- libssl \
- libgui \
+ libchromium_net \
+ libcrypto \
+ libcutils \
+ libdl \
+ libdrmframework \
+ libexpat \
+ libgui \
+ libicui18n \
+ libicuuc \
+ liblog \
+ libmedia \
+ libsonivox \
+ libssl \
libstagefright_omx \
+ libstagefright_yuv \
+ libui \
+ libutils \
+ libvorbisidec \
+ libz \
LOCAL_STATIC_LIBRARIES := \
libstagefright_color_conversion \
@@ -88,57 +99,14 @@ LOCAL_STATIC_LIBRARIES := \
libstagefright_httplive \
libstagefright_id3 \
libFLAC \
+ libstagefright_chromium_http \
-################################################################################
-
-# The following was shamelessly copied from external/webkit/Android.mk and
-# currently must follow the same logic to determine how webkit was built and
-# if it's safe to link against libchromium.net
-
-# V8 also requires an ARMv7 CPU, and since we must use jsc, we cannot
-# use the Chrome http stack either.
-ifneq ($(strip $(ARCH_ARM_HAVE_ARMV7A)),true)
- USE_ALT_HTTP := true
-endif
-
-# See if the user has specified a stack they want to use
-HTTP_STACK = $(HTTP)
-# We default to the Chrome HTTP stack.
-DEFAULT_HTTP = chrome
-ALT_HTTP = android
-
-ifneq ($(HTTP_STACK),chrome)
- ifneq ($(HTTP_STACK),android)
- # No HTTP stack is specified, pickup the one we want as default.
- ifeq ($(USE_ALT_HTTP),true)
- HTTP_STACK = $(ALT_HTTP)
- else
- HTTP_STACK = $(DEFAULT_HTTP)
- endif
- endif
-endif
-
-ifeq ($(HTTP_STACK),chrome)
-
-LOCAL_SHARED_LIBRARIES += \
- liblog \
- libicuuc \
- libicui18n \
- libz \
- libdl \
-
-LOCAL_STATIC_LIBRARIES += \
- libstagefright_chromium_http
-
-LOCAL_SHARED_LIBRARIES += libstlport libchromium_net
+LOCAL_SHARED_LIBRARIES += libstlport
include external/stlport/libstlport.mk
+# TODO: Chromium is always available, so this flag can be removed.
LOCAL_CPPFLAGS += -DCHROMIUM_AVAILABLE=1
-endif # ifeq ($(HTTP_STACK),chrome)
-
-################################################################################
-
LOCAL_SHARED_LIBRARIES += \
libstagefright_enc_common \
libstagefright_avc_common \
diff --git a/media/libstagefright/AudioPlayer.cpp b/media/libstagefright/AudioPlayer.cpp
index df27566..650b6c4 100644
--- a/media/libstagefright/AudioPlayer.cpp
+++ b/media/libstagefright/AudioPlayer.cpp
@@ -110,13 +110,18 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) {
success = format->findInt32(kKeySampleRate, &mSampleRate);
CHECK(success);
- int32_t numChannels;
+ int32_t numChannels, channelMask;
success = format->findInt32(kKeyChannelCount, &numChannels);
CHECK(success);
+ if(!format->findInt32(kKeyChannelMask, &channelMask)) {
+ ALOGW("source format didn't specify channel mask, using channel order");
+ channelMask = CHANNEL_MASK_USE_CHANNEL_ORDER;
+ }
+
if (mAudioSink.get() != NULL) {
status_t err = mAudioSink->open(
- mSampleRate, numChannels, AUDIO_FORMAT_PCM_16_BIT,
+ mSampleRate, numChannels, channelMask, AUDIO_FORMAT_PCM_16_BIT,
DEFAULT_AUDIOSINK_BUFFERCOUNT,
&AudioPlayer::AudioSinkCallback, this);
if (err != OK) {
@@ -137,11 +142,15 @@ status_t AudioPlayer::start(bool sourceAlreadyStarted) {
mAudioSink->start();
} else {
+ // playing to an AudioTrack, set up mask if necessary
+ audio_channel_mask_t audioMask = channelMask == CHANNEL_MASK_USE_CHANNEL_ORDER ?
+ audio_channel_mask_from_count(numChannels) : channelMask;
+ if (0 == audioMask) {
+ return BAD_VALUE;
+ }
+
mAudioTrack = new AudioTrack(
- AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT,
- (numChannels == 2)
- ? AUDIO_CHANNEL_OUT_STEREO
- : AUDIO_CHANNEL_OUT_MONO,
+ AUDIO_STREAM_MUSIC, mSampleRate, AUDIO_FORMAT_PCM_16_BIT, audioMask,
0, 0, &AudioCallback, this, 0);
if ((err = mAudioTrack->initCheck()) != OK) {
@@ -418,6 +427,12 @@ size_t AudioPlayer::fillBuffer(void *data, size_t size) {
break;
}
+ if (mAudioSink != NULL) {
+ mLatencyUs = (int64_t)mAudioSink->latency() * 1000;
+ } else {
+ mLatencyUs = (int64_t)mAudioTrack->latency() * 1000;
+ }
+
CHECK(mInputBuffer->meta_data()->findInt64(
kKeyTime, &mPositionTimeMediaUs));
diff --git a/media/libstagefright/AudioSource.cpp b/media/libstagefright/AudioSource.cpp
index fef2a00..5b2ea1f 100644
--- a/media/libstagefright/AudioSource.cpp
+++ b/media/libstagefright/AudioSource.cpp
@@ -282,8 +282,6 @@ status_t AudioSource::dataCallbackTimestamp(
mPrevSampleTimeUs = mStartTimeUs;
}
- int64_t timestampUs = mPrevSampleTimeUs;
-
size_t numLostBytes = 0;
if (mNumFramesReceived > 0) { // Ignore earlier frame lost
// getInputFramesLost() returns the number of lost frames.
@@ -293,37 +291,58 @@ status_t AudioSource::dataCallbackTimestamp(
CHECK_EQ(numLostBytes & 1, 0u);
CHECK_EQ(audioBuffer.size & 1, 0u);
- size_t bufferSize = numLostBytes + audioBuffer.size;
- MediaBuffer *buffer = new MediaBuffer(bufferSize);
if (numLostBytes > 0) {
- memset(buffer->data(), 0, numLostBytes);
- memcpy((uint8_t *) buffer->data() + numLostBytes,
- audioBuffer.i16, audioBuffer.size);
- } else {
- if (audioBuffer.size == 0) {
- ALOGW("Nothing is available from AudioRecord callback buffer");
- buffer->release();
- return OK;
+ // Loss of audio frames should happen rarely; thus the LOGW should
+ // not cause a logging spam
+ ALOGW("Lost audio record data: %d bytes", numLostBytes);
+ }
+
+ while (numLostBytes > 0) {
+ size_t bufferSize = numLostBytes;
+ if (numLostBytes > kMaxBufferSize) {
+ numLostBytes -= kMaxBufferSize;
+ bufferSize = kMaxBufferSize;
+ } else {
+ numLostBytes = 0;
}
- memcpy((uint8_t *) buffer->data(),
- audioBuffer.i16, audioBuffer.size);
+ MediaBuffer *lostAudioBuffer = new MediaBuffer(bufferSize);
+ memset(lostAudioBuffer->data(), 0, bufferSize);
+ lostAudioBuffer->set_range(0, bufferSize);
+ queueInputBuffer_l(lostAudioBuffer, timeUs);
+ }
+
+ if (audioBuffer.size == 0) {
+ ALOGW("Nothing is available from AudioRecord callback buffer");
+ return OK;
}
+ const size_t bufferSize = audioBuffer.size;
+ MediaBuffer *buffer = new MediaBuffer(bufferSize);
+ memcpy((uint8_t *) buffer->data(),
+ audioBuffer.i16, audioBuffer.size);
buffer->set_range(0, bufferSize);
- timestampUs += ((1000000LL * (bufferSize >> 1)) +
- (mSampleRate >> 1)) / mSampleRate;
+ queueInputBuffer_l(buffer, timeUs);
+ return OK;
+}
+
+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)) +
+ (mSampleRate >> 1)) / mSampleRate;
if (mNumFramesReceived == 0) {
buffer->meta_data()->setInt64(kKeyAnchorTime, mStartTimeUs);
}
+
buffer->meta_data()->setInt64(kKeyTime, mPrevSampleTimeUs);
buffer->meta_data()->setInt64(kKeyDriftTime, timeUs - mInitialReadTimeUs);
mPrevSampleTimeUs = timestampUs;
- mNumFramesReceived += buffer->range_length() / sizeof(int16_t);
+ mNumFramesReceived += bufferSize / frameSize;
mBuffersReceived.push_back(buffer);
mFrameAvailableCondition.signal();
-
- return OK;
}
void AudioSource::trackMaxAmplitude(int16_t *data, int nSamples) {
diff --git a/media/libstagefright/AwesomePlayer.cpp b/media/libstagefright/AwesomePlayer.cpp
index 9c975b7..9e00bb3 100644
--- a/media/libstagefright/AwesomePlayer.cpp
+++ b/media/libstagefright/AwesomePlayer.cpp
@@ -30,13 +30,12 @@
#include "include/MPEG2TSExtractor.h"
#include "include/WVMExtractor.h"
-#include "timedtext/TimedTextDriver.h"
-
#include <binder/IPCThreadState.h>
#include <binder/IServiceManager.h>
#include <media/IMediaPlayerService.h>
#include <media/stagefright/foundation/hexdump.h>
#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/timedtext/TimedTextDriver.h>
#include <media/stagefright/AudioPlayer.h>
#include <media/stagefright/DataSource.h>
#include <media/stagefright/FileSource.h>
@@ -47,10 +46,8 @@
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
-#include <surfaceflinger/Surface.h>
#include <gui/ISurfaceTexture.h>
#include <gui/SurfaceTextureClient.h>
-#include <surfaceflinger/ISurfaceComposer.h>
#include <media/stagefright/foundation/AMessage.h>
diff --git a/media/libstagefright/CameraSource.cpp b/media/libstagefright/CameraSource.cpp
index ed1d5f4..2df5528 100755
--- a/media/libstagefright/CameraSource.cpp
+++ b/media/libstagefright/CameraSource.cpp
@@ -27,7 +27,7 @@
#include <media/stagefright/MetaData.h>
#include <camera/Camera.h>
#include <camera/CameraParameters.h>
-#include <surfaceflinger/Surface.h>
+#include <gui/Surface.h>
#include <utils/String8.h>
#include <cutils/properties.h>
diff --git a/media/libstagefright/MPEG2TSWriter.cpp b/media/libstagefright/MPEG2TSWriter.cpp
index 0b4ecbe..f702376 100644
--- a/media/libstagefright/MPEG2TSWriter.cpp
+++ b/media/libstagefright/MPEG2TSWriter.cpp
@@ -244,7 +244,7 @@ void MPEG2TSWriter::SourceInfo::extractCodecSpecificData() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kNotifyBuffer);
- notify->setObject("buffer", out);
+ notify->setBuffer("buffer", out);
notify->setInt32("oob", true);
notify->post();
}
@@ -270,7 +270,7 @@ void MPEG2TSWriter::SourceInfo::postAVCFrame(MediaBuffer *buffer) {
copy->meta()->setInt32("isSync", true);
}
- notify->setObject("buffer", copy);
+ notify->setBuffer("buffer", copy);
notify->post();
}
@@ -351,7 +351,7 @@ bool MPEG2TSWriter::SourceInfo::flushAACFrames() {
sp<AMessage> notify = mNotify->dup();
notify->setInt32("what", kNotifyBuffer);
- notify->setObject("buffer", mAACBuffer);
+ notify->setBuffer("buffer", mAACBuffer);
notify->post();
mAACBuffer.clear();
@@ -614,10 +614,8 @@ void MPEG2TSWriter::onMessageReceived(const sp<AMessage> &msg) {
++mNumSourcesDone;
} else if (what == SourceInfo::kNotifyBuffer) {
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
-
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
int32_t oob;
if (msg->findInt32("oob", &oob) && oob) {
diff --git a/media/libstagefright/MediaCodec.cpp b/media/libstagefright/MediaCodec.cpp
new file mode 100644
index 0000000..a9e7f360
--- /dev/null
+++ b/media/libstagefright/MediaCodec.cpp
@@ -0,0 +1,1217 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodec"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaCodec.h>
+
+#include "include/SoftwareRenderer.h"
+
+#include <gui/SurfaceTextureClient.h>
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/ACodec.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/NativeWindowWrapper.h>
+
+namespace android {
+
+// static
+sp<MediaCodec> MediaCodec::CreateByType(
+ const sp<ALooper> &looper, const char *mime, bool encoder) {
+ sp<MediaCodec> codec = new MediaCodec(looper);
+ if (codec->init(mime, true /* nameIsType */, encoder) != OK) {
+ return NULL;
+ }
+
+ return codec;
+}
+
+// static
+sp<MediaCodec> MediaCodec::CreateByComponentName(
+ const sp<ALooper> &looper, const char *name) {
+ sp<MediaCodec> codec = new MediaCodec(looper);
+ if (codec->init(name, false /* nameIsType */, false /* encoder */) != OK) {
+ return NULL;
+ }
+
+ return codec;
+}
+
+MediaCodec::MediaCodec(const sp<ALooper> &looper)
+ : mState(UNINITIALIZED),
+ mLooper(looper),
+ mCodec(new ACodec),
+ mFlags(0),
+ mSoftRenderer(NULL),
+ mDequeueInputTimeoutGeneration(0),
+ mDequeueInputReplyID(0),
+ mDequeueOutputTimeoutGeneration(0),
+ mDequeueOutputReplyID(0) {
+}
+
+MediaCodec::~MediaCodec() {
+ CHECK_EQ(mState, UNINITIALIZED);
+}
+
+// static
+status_t MediaCodec::PostAndAwaitResponse(
+ const sp<AMessage> &msg, sp<AMessage> *response) {
+ status_t err = msg->postAndAwaitResponse(response);
+
+ if (err != OK) {
+ return err;
+ }
+
+ if (!(*response)->findInt32("err", &err)) {
+ err = OK;
+ }
+
+ return err;
+}
+
+status_t MediaCodec::init(const char *name, bool nameIsType, bool encoder) {
+ // Current video decoders do not return from OMX_FillThisBuffer
+ // quickly, violating the OpenMAX specs, until that is remedied
+ // we need to invest in an extra looper to free the main event
+ // queue.
+ bool needDedicatedLooper = false;
+ if (nameIsType && !strncasecmp(name, "video/", 6)) {
+ needDedicatedLooper = true;
+ } else if (!nameIsType && !strncmp(name, "OMX.TI.DUCATI1.VIDEO.", 21)) {
+ needDedicatedLooper = true;
+ }
+
+ if (needDedicatedLooper) {
+ if (mCodecLooper == NULL) {
+ mCodecLooper = new ALooper;
+ mCodecLooper->setName("CodecLooper");
+ mCodecLooper->start(false, false, ANDROID_PRIORITY_AUDIO);
+ }
+
+ mCodecLooper->registerHandler(mCodec);
+ } else {
+ mLooper->registerHandler(mCodec);
+ }
+
+ mLooper->registerHandler(this);
+
+ mCodec->setNotificationMessage(new AMessage(kWhatCodecNotify, id()));
+
+ sp<AMessage> msg = new AMessage(kWhatInit, id());
+ msg->setString("name", name);
+ msg->setInt32("nameIsType", nameIsType);
+
+ if (nameIsType) {
+ msg->setInt32("encoder", encoder);
+ }
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::configure(
+ const sp<AMessage> &format,
+ const sp<SurfaceTextureClient> &nativeWindow,
+ uint32_t flags) {
+ sp<AMessage> msg = new AMessage(kWhatConfigure, id());
+
+ msg->setMessage("format", format);
+ msg->setInt32("flags", flags);
+
+ if (nativeWindow != NULL) {
+ if (!(mFlags & kFlagIsSoftwareCodec)) {
+ msg->setObject(
+ "native-window",
+ new NativeWindowWrapper(nativeWindow));
+ } else {
+ mNativeWindow = nativeWindow;
+ }
+ }
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::start() {
+ sp<AMessage> msg = new AMessage(kWhatStart, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::stop() {
+ sp<AMessage> msg = new AMessage(kWhatStop, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::release() {
+ sp<AMessage> msg = new AMessage(kWhatRelease, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::queueInputBuffer(
+ size_t index,
+ size_t offset,
+ size_t size,
+ int64_t presentationTimeUs,
+ uint32_t flags) {
+ sp<AMessage> msg = new AMessage(kWhatQueueInputBuffer, id());
+ msg->setSize("index", index);
+ msg->setSize("offset", offset);
+ msg->setSize("size", size);
+ msg->setInt64("timeUs", presentationTimeUs);
+ msg->setInt32("flags", flags);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::dequeueInputBuffer(size_t *index, int64_t timeoutUs) {
+ sp<AMessage> msg = new AMessage(kWhatDequeueInputBuffer, id());
+ msg->setInt64("timeoutUs", timeoutUs);
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findSize("index", index));
+
+ return OK;
+}
+
+status_t MediaCodec::dequeueOutputBuffer(
+ size_t *index,
+ size_t *offset,
+ size_t *size,
+ int64_t *presentationTimeUs,
+ uint32_t *flags,
+ int64_t timeoutUs) {
+ sp<AMessage> msg = new AMessage(kWhatDequeueOutputBuffer, id());
+ msg->setInt64("timeoutUs", timeoutUs);
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findSize("index", index));
+ CHECK(response->findSize("offset", offset));
+ CHECK(response->findSize("size", size));
+ CHECK(response->findInt64("timeUs", presentationTimeUs));
+ CHECK(response->findInt32("flags", (int32_t *)flags));
+
+ return OK;
+}
+
+status_t MediaCodec::renderOutputBufferAndRelease(size_t index) {
+ sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+ msg->setSize("index", index);
+ msg->setInt32("render", true);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::releaseOutputBuffer(size_t index) {
+ sp<AMessage> msg = new AMessage(kWhatReleaseOutputBuffer, id());
+ msg->setSize("index", index);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::getOutputFormat(sp<AMessage> *format) const {
+ sp<AMessage> msg = new AMessage(kWhatGetOutputFormat, id());
+
+ sp<AMessage> response;
+ status_t err;
+ if ((err = PostAndAwaitResponse(msg, &response)) != OK) {
+ return err;
+ }
+
+ CHECK(response->findMessage("format", format));
+
+ return OK;
+}
+
+status_t MediaCodec::getInputBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+ msg->setInt32("portIndex", kPortIndexInput);
+ msg->setPointer("buffers", buffers);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::getOutputBuffers(Vector<sp<ABuffer> > *buffers) const {
+ sp<AMessage> msg = new AMessage(kWhatGetBuffers, id());
+ msg->setInt32("portIndex", kPortIndexOutput);
+ msg->setPointer("buffers", buffers);
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+status_t MediaCodec::flush() {
+ sp<AMessage> msg = new AMessage(kWhatFlush, id());
+
+ sp<AMessage> response;
+ return PostAndAwaitResponse(msg, &response);
+}
+
+////////////////////////////////////////////////////////////////////////////////
+
+void MediaCodec::cancelPendingDequeueOperations() {
+ if (mFlags & kFlagDequeueInputPending) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+ response->postReply(mDequeueInputReplyID);
+
+ ++mDequeueInputTimeoutGeneration;
+ mDequeueInputReplyID = 0;
+ mFlags &= ~kFlagDequeueInputPending;
+ }
+
+ if (mFlags & kFlagDequeueOutputPending) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+ response->postReply(mDequeueOutputReplyID);
+
+ ++mDequeueOutputTimeoutGeneration;
+ mDequeueOutputReplyID = 0;
+ mFlags &= ~kFlagDequeueOutputPending;
+ }
+}
+
+bool MediaCodec::handleDequeueInputBuffer(uint32_t replyID, bool newRequest) {
+ if (mState != STARTED
+ || (mFlags & kFlagStickyError)
+ || (newRequest && (mFlags & kFlagDequeueInputPending))) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+
+ return true;
+ }
+
+ ssize_t index = dequeuePortBuffer(kPortIndexInput);
+
+ if (index < 0) {
+ CHECK_EQ(index, -EAGAIN);
+ return false;
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setSize("index", index);
+ response->postReply(replyID);
+
+ return true;
+}
+
+bool MediaCodec::handleDequeueOutputBuffer(uint32_t replyID, bool newRequest) {
+ sp<AMessage> response = new AMessage;
+
+ if (mState != STARTED
+ || (mFlags & kFlagStickyError)
+ || (newRequest && (mFlags & kFlagDequeueOutputPending))) {
+ response->setInt32("err", INVALID_OPERATION);
+ } else if (mFlags & kFlagOutputBuffersChanged) {
+ response->setInt32("err", INFO_OUTPUT_BUFFERS_CHANGED);
+ mFlags &= ~kFlagOutputBuffersChanged;
+ } else if (mFlags & kFlagOutputFormatChanged) {
+ response->setInt32("err", INFO_FORMAT_CHANGED);
+ mFlags &= ~kFlagOutputFormatChanged;
+ } else {
+ ssize_t index = dequeuePortBuffer(kPortIndexOutput);
+
+ if (index < 0) {
+ CHECK_EQ(index, -EAGAIN);
+ return false;
+ }
+
+ const sp<ABuffer> &buffer =
+ mPortBuffers[kPortIndexOutput].itemAt(index).mData;
+
+ response->setSize("index", index);
+ response->setSize("offset", buffer->offset());
+ response->setSize("size", buffer->size());
+
+ int64_t timeUs;
+ CHECK(buffer->meta()->findInt64("timeUs", &timeUs));
+
+ response->setInt64("timeUs", timeUs);
+
+ int32_t omxFlags;
+ CHECK(buffer->meta()->findInt32("omxFlags", &omxFlags));
+
+ uint32_t flags = 0;
+ if (omxFlags & OMX_BUFFERFLAG_SYNCFRAME) {
+ flags |= BUFFER_FLAG_SYNCFRAME;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_CODECCONFIG) {
+ flags |= BUFFER_FLAG_CODECCONFIG;
+ }
+ if (omxFlags & OMX_BUFFERFLAG_EOS) {
+ flags |= BUFFER_FLAG_EOS;
+ }
+
+ response->setInt32("flags", flags);
+ }
+
+ response->postReply(replyID);
+
+ return true;
+}
+
+void MediaCodec::onMessageReceived(const sp<AMessage> &msg) {
+ switch (msg->what()) {
+ case kWhatCodecNotify:
+ {
+ int32_t what;
+ CHECK(msg->findInt32("what", &what));
+
+ switch (what) {
+ case ACodec::kWhatError:
+ {
+ int32_t omxError, internalError;
+ CHECK(msg->findInt32("omx-error", &omxError));
+ CHECK(msg->findInt32("err", &internalError));
+
+ ALOGE("Codec reported an error. "
+ "(omx error 0x%08x, internalError %d)",
+ omxError, internalError);
+
+ bool sendErrorReponse = true;
+
+ switch (mState) {
+ case INITIALIZING:
+ {
+ setState(UNINITIALIZED);
+ break;
+ }
+
+ case CONFIGURING:
+ {
+ setState(INITIALIZED);
+ break;
+ }
+
+ case STARTING:
+ {
+ setState(CONFIGURED);
+ break;
+ }
+
+ case STOPPING:
+ case RELEASING:
+ {
+ // Ignore the error, assuming we'll still get
+ // the shutdown complete notification.
+
+ sendErrorReponse = false;
+ break;
+ }
+
+ case FLUSHING:
+ {
+ setState(STARTED);
+ break;
+ }
+
+ case STARTED:
+ {
+ sendErrorReponse = false;
+
+ mFlags |= kFlagStickyError;
+
+ cancelPendingDequeueOperations();
+ break;
+ }
+
+ default:
+ {
+ sendErrorReponse = false;
+
+ mFlags |= kFlagStickyError;
+ break;
+ }
+ }
+
+ if (sendErrorReponse) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", UNKNOWN_ERROR);
+
+ response->postReply(mReplyID);
+ }
+ break;
+ }
+
+ case ACodec::kWhatComponentAllocated:
+ {
+ CHECK_EQ(mState, INITIALIZING);
+ setState(INITIALIZED);
+
+ AString componentName;
+ CHECK(msg->findString("componentName", &componentName));
+
+ if (componentName.startsWith("OMX.google.")) {
+ mFlags |= kFlagIsSoftwareCodec;
+ } else {
+ mFlags &= ~kFlagIsSoftwareCodec;
+ }
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatComponentConfigured:
+ {
+ CHECK_EQ(mState, CONFIGURING);
+ setState(CONFIGURED);
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatBuffersAllocated:
+ {
+ int32_t portIndex;
+ CHECK(msg->findInt32("portIndex", &portIndex));
+
+ ALOGV("%s buffers allocated",
+ portIndex == kPortIndexInput ? "input" : "output");
+
+ CHECK(portIndex == kPortIndexInput
+ || portIndex == kPortIndexOutput);
+
+ mPortBuffers[portIndex].clear();
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+ for (size_t i = 0;; ++i) {
+ AString name = StringPrintf("buffer-id_%d", i);
+
+ void *bufferID;
+ if (!msg->findPointer(name.c_str(), &bufferID)) {
+ break;
+ }
+
+ name = StringPrintf("data_%d", i);
+
+ BufferInfo info;
+ info.mBufferID = bufferID;
+ info.mOwnedByClient = false;
+ CHECK(msg->findBuffer(name.c_str(), &info.mData));
+
+ buffers->push_back(info);
+ }
+
+ if (portIndex == kPortIndexOutput) {
+ if (mState == STARTING) {
+ // We're always allocating output buffers after
+ // allocating input buffers, so this is a good
+ // indication that now all buffers are allocated.
+ setState(STARTED);
+ (new AMessage)->postReply(mReplyID);
+ } else {
+ mFlags |= kFlagOutputBuffersChanged;
+ }
+ }
+ break;
+ }
+
+ case ACodec::kWhatOutputFormatChanged:
+ {
+ ALOGV("codec output format changed");
+
+ if ((mFlags & kFlagIsSoftwareCodec)
+ && mNativeWindow != NULL) {
+ AString mime;
+ CHECK(msg->findString("mime", &mime));
+
+ if (!strncasecmp("video/", mime.c_str(), 6)) {
+ delete mSoftRenderer;
+ mSoftRenderer = NULL;
+
+ int32_t width, height;
+ CHECK(msg->findInt32("width", &width));
+ CHECK(msg->findInt32("height", &height));
+
+ int32_t colorFormat;
+ CHECK(msg->findInt32(
+ "color-format", &colorFormat));
+
+ sp<MetaData> meta = new MetaData;
+ meta->setInt32(kKeyWidth, width);
+ meta->setInt32(kKeyHeight, height);
+ meta->setInt32(kKeyColorFormat, colorFormat);
+
+ mSoftRenderer =
+ new SoftwareRenderer(mNativeWindow, meta);
+ }
+ }
+
+ mOutputFormat = msg;
+ mFlags |= kFlagOutputFormatChanged;
+ break;
+ }
+
+ case ACodec::kWhatFillThisBuffer:
+ {
+ /* size_t index = */updateBuffers(kPortIndexInput, msg);
+
+ if (mState == FLUSHING
+ || mState == STOPPING
+ || mState == RELEASING) {
+ returnBuffersToCodecOnPort(kPortIndexInput);
+ break;
+ }
+
+ if (mFlags & kFlagDequeueInputPending) {
+ CHECK(handleDequeueInputBuffer(mDequeueInputReplyID));
+
+ ++mDequeueInputTimeoutGeneration;
+ mFlags &= ~kFlagDequeueInputPending;
+ mDequeueInputReplyID = 0;
+ }
+ break;
+ }
+
+ case ACodec::kWhatDrainThisBuffer:
+ {
+ /* size_t index = */updateBuffers(kPortIndexOutput, msg);
+
+ if (mState == FLUSHING
+ || mState == STOPPING
+ || mState == RELEASING) {
+ returnBuffersToCodecOnPort(kPortIndexOutput);
+ break;
+ }
+
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
+
+ int32_t omxFlags;
+ CHECK(msg->findInt32("flags", &omxFlags));
+
+ buffer->meta()->setInt32("omxFlags", omxFlags);
+
+ if (mFlags & kFlagDequeueOutputPending) {
+ CHECK(handleDequeueOutputBuffer(mDequeueOutputReplyID));
+
+ ++mDequeueOutputTimeoutGeneration;
+ mFlags &= ~kFlagDequeueOutputPending;
+ mDequeueOutputReplyID = 0;
+ }
+ break;
+ }
+
+ case ACodec::kWhatEOS:
+ {
+ // We already notify the client of this by using the
+ // corresponding flag in "onOutputBufferReady".
+ break;
+ }
+
+ case ACodec::kWhatShutdownCompleted:
+ {
+ if (mState == STOPPING) {
+ setState(INITIALIZED);
+ } else {
+ CHECK_EQ(mState, RELEASING);
+ setState(UNINITIALIZED);
+ }
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ case ACodec::kWhatFlushCompleted:
+ {
+ CHECK_EQ(mState, FLUSHING);
+ setState(STARTED);
+
+ mCodec->signalResume();
+
+ (new AMessage)->postReply(mReplyID);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+ break;
+ }
+
+ case kWhatInit:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != UNINITIALIZED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(INITIALIZING);
+
+ AString name;
+ CHECK(msg->findString("name", &name));
+
+ int32_t nameIsType;
+ int32_t encoder = false;
+ CHECK(msg->findInt32("nameIsType", &nameIsType));
+ if (nameIsType) {
+ CHECK(msg->findInt32("encoder", &encoder));
+ }
+
+ sp<AMessage> format = new AMessage;
+
+ if (nameIsType) {
+ format->setString("mime", name.c_str());
+ format->setInt32("encoder", encoder);
+ } else {
+ format->setString("componentName", name.c_str());
+ }
+
+ mCodec->initiateAllocateComponent(format);
+ break;
+ }
+
+ case kWhatConfigure:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != INITIALIZED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(CONFIGURING);
+
+ sp<RefBase> obj;
+ if (!msg->findObject("native-window", &obj)) {
+ obj.clear();
+ }
+
+ sp<AMessage> format;
+ CHECK(msg->findMessage("format", &format));
+
+ if (obj != NULL) {
+ format->setObject("native-window", obj);
+ }
+
+ uint32_t flags;
+ CHECK(msg->findInt32("flags", (int32_t *)&flags));
+
+ if (flags & CONFIGURE_FLAG_ENCODE) {
+ format->setInt32("encoder", true);
+ }
+
+ mCodec->initiateConfigureComponent(format);
+ break;
+ }
+
+ case kWhatStart:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != CONFIGURED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(STARTING);
+
+ mCodec->initiateStart();
+ break;
+ }
+
+ case kWhatStop:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != INITIALIZED
+ && mState != CONFIGURED && mState != STARTED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(STOPPING);
+
+ mCodec->initiateShutdown(true /* keepComponentAllocated */);
+ returnBuffersToCodec();
+ break;
+ }
+
+ case kWhatRelease:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != INITIALIZED
+ && mState != CONFIGURED && mState != STARTED) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(RELEASING);
+
+ mCodec->initiateShutdown();
+ returnBuffersToCodec();
+ break;
+ }
+
+ case kWhatDequeueInputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (handleDequeueInputBuffer(replyID, true /* new request */)) {
+ break;
+ }
+
+ int64_t timeoutUs;
+ CHECK(msg->findInt64("timeoutUs", &timeoutUs));
+
+ if (timeoutUs == 0ll) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(replyID);
+ break;
+ }
+
+ mFlags |= kFlagDequeueInputPending;
+ mDequeueInputReplyID = replyID;
+
+ if (timeoutUs > 0ll) {
+ sp<AMessage> timeoutMsg =
+ new AMessage(kWhatDequeueInputTimedOut, id());
+ timeoutMsg->setInt32(
+ "generation", ++mDequeueInputTimeoutGeneration);
+ timeoutMsg->post(timeoutUs);
+ }
+ break;
+ }
+
+ case kWhatDequeueInputTimedOut:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mDequeueInputTimeoutGeneration) {
+ // Obsolete
+ break;
+ }
+
+ CHECK(mFlags & kFlagDequeueInputPending);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(mDequeueInputReplyID);
+
+ mFlags &= ~kFlagDequeueInputPending;
+ mDequeueInputReplyID = 0;
+ break;
+ }
+
+ case kWhatQueueInputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ status_t err = onQueueInputBuffer(msg);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatDequeueOutputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (handleDequeueOutputBuffer(replyID, true /* new request */)) {
+ break;
+ }
+
+ int64_t timeoutUs;
+ CHECK(msg->findInt64("timeoutUs", &timeoutUs));
+
+ if (timeoutUs == 0ll) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(replyID);
+ break;
+ }
+
+ mFlags |= kFlagDequeueOutputPending;
+ mDequeueOutputReplyID = replyID;
+
+ if (timeoutUs > 0ll) {
+ sp<AMessage> timeoutMsg =
+ new AMessage(kWhatDequeueOutputTimedOut, id());
+ timeoutMsg->setInt32(
+ "generation", ++mDequeueOutputTimeoutGeneration);
+ timeoutMsg->post(timeoutUs);
+ }
+ break;
+ }
+
+ case kWhatDequeueOutputTimedOut:
+ {
+ int32_t generation;
+ CHECK(msg->findInt32("generation", &generation));
+
+ if (generation != mDequeueOutputTimeoutGeneration) {
+ // Obsolete
+ break;
+ }
+
+ CHECK(mFlags & kFlagDequeueOutputPending);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", -EAGAIN);
+ response->postReply(mDequeueOutputReplyID);
+
+ mFlags &= ~kFlagDequeueOutputPending;
+ mDequeueOutputReplyID = 0;
+ break;
+ }
+
+ case kWhatReleaseOutputBuffer:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ status_t err = onReleaseOutputBuffer(msg);
+
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", err);
+ response->postReply(replyID);
+ break;
+ }
+
+ case kWhatGetBuffers:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ int32_t portIndex;
+ CHECK(msg->findInt32("portIndex", &portIndex));
+
+ Vector<sp<ABuffer> > *dstBuffers;
+ CHECK(msg->findPointer("buffers", (void **)&dstBuffers));
+
+ dstBuffers->clear();
+ const Vector<BufferInfo> &srcBuffers = mPortBuffers[portIndex];
+
+ for (size_t i = 0; i < srcBuffers.size(); ++i) {
+ const BufferInfo &info = srcBuffers.itemAt(i);
+
+ dstBuffers->push_back(info.mData);
+ }
+
+ (new AMessage)->postReply(replyID);
+ break;
+ }
+
+ case kWhatFlush:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if (mState != STARTED || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ mReplyID = replyID;
+ setState(FLUSHING);
+
+ mCodec->signalFlush();
+ returnBuffersToCodec();
+ break;
+ }
+
+ case kWhatGetOutputFormat:
+ {
+ uint32_t replyID;
+ CHECK(msg->senderAwaitsResponse(&replyID));
+
+ if ((mState != STARTED && mState != FLUSHING)
+ || (mFlags & kFlagStickyError)) {
+ sp<AMessage> response = new AMessage;
+ response->setInt32("err", INVALID_OPERATION);
+
+ response->postReply(replyID);
+ break;
+ }
+
+ sp<AMessage> response = new AMessage;
+ response->setMessage("format", mOutputFormat);
+ response->postReply(replyID);
+ break;
+ }
+
+ default:
+ TRESPASS();
+ }
+}
+
+void MediaCodec::setState(State newState) {
+ if (newState == UNINITIALIZED) {
+ delete mSoftRenderer;
+ mSoftRenderer = NULL;
+
+ mNativeWindow.clear();
+
+ mOutputFormat.clear();
+ mFlags &= ~kFlagOutputFormatChanged;
+ mFlags &= ~kFlagOutputBuffersChanged;
+ mFlags &= ~kFlagStickyError;
+ }
+
+ mState = newState;
+
+ cancelPendingDequeueOperations();
+}
+
+void MediaCodec::returnBuffersToCodec() {
+ returnBuffersToCodecOnPort(kPortIndexInput);
+ returnBuffersToCodecOnPort(kPortIndexOutput);
+}
+
+void MediaCodec::returnBuffersToCodecOnPort(int32_t portIndex) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (info->mNotify != NULL) {
+ sp<AMessage> msg = info->mNotify;
+ info->mNotify = NULL;
+ info->mOwnedByClient = false;
+
+ if (portIndex == kPortIndexInput) {
+ msg->setInt32("err", ERROR_END_OF_STREAM);
+ }
+ msg->post();
+ }
+ }
+
+ mAvailPortBuffers[portIndex].clear();
+}
+
+size_t MediaCodec::updateBuffers(
+ int32_t portIndex, const sp<AMessage> &msg) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ void *bufferID;
+ CHECK(msg->findPointer("buffer-id", &bufferID));
+
+ Vector<BufferInfo> *buffers = &mPortBuffers[portIndex];
+
+ for (size_t i = 0; i < buffers->size(); ++i) {
+ BufferInfo *info = &buffers->editItemAt(i);
+
+ if (info->mBufferID == bufferID) {
+ CHECK(info->mNotify == NULL);
+ CHECK(msg->findMessage("reply", &info->mNotify));
+
+ mAvailPortBuffers[portIndex].push_back(i);
+
+ return i;
+ }
+ }
+
+ TRESPASS();
+
+ return 0;
+}
+
+status_t MediaCodec::onQueueInputBuffer(const sp<AMessage> &msg) {
+ size_t index;
+ size_t offset;
+ size_t size;
+ int64_t timeUs;
+ uint32_t flags;
+ CHECK(msg->findSize("index", &index));
+ CHECK(msg->findSize("offset", &offset));
+ CHECK(msg->findSize("size", &size));
+ CHECK(msg->findInt64("timeUs", &timeUs));
+ CHECK(msg->findInt32("flags", (int32_t *)&flags));
+
+ if (index >= mPortBuffers[kPortIndexInput].size()) {
+ return -ERANGE;
+ }
+
+ BufferInfo *info = &mPortBuffers[kPortIndexInput].editItemAt(index);
+
+ if (info->mNotify == NULL || !info->mOwnedByClient) {
+ return -EACCES;
+ }
+
+ if (offset + size > info->mData->capacity()) {
+ return -EINVAL;
+ }
+
+ sp<AMessage> reply = info->mNotify;
+ info->mNotify = NULL;
+ info->mOwnedByClient = false;
+
+ info->mData->setRange(offset, size);
+ info->mData->meta()->setInt64("timeUs", timeUs);
+
+ if (flags & BUFFER_FLAG_EOS) {
+ info->mData->meta()->setInt32("eos", true);
+ }
+
+ if (flags & BUFFER_FLAG_CODECCONFIG) {
+ info->mData->meta()->setInt32("csd", true);
+ }
+
+ reply->setBuffer("buffer", info->mData);
+ reply->post();
+
+ return OK;
+}
+
+status_t MediaCodec::onReleaseOutputBuffer(const sp<AMessage> &msg) {
+ size_t index;
+ CHECK(msg->findSize("index", &index));
+
+ int32_t render;
+ if (!msg->findInt32("render", &render)) {
+ render = 0;
+ }
+
+ if (mState != STARTED) {
+ return -EINVAL;
+ }
+
+ if (index >= mPortBuffers[kPortIndexOutput].size()) {
+ return -ERANGE;
+ }
+
+ BufferInfo *info = &mPortBuffers[kPortIndexOutput].editItemAt(index);
+
+ if (info->mNotify == NULL || !info->mOwnedByClient) {
+ return -EACCES;
+ }
+
+ if (render) {
+ info->mNotify->setInt32("render", true);
+
+ if (mSoftRenderer != NULL) {
+ mSoftRenderer->render(
+ info->mData->data(), info->mData->size(), NULL);
+ }
+ }
+
+ info->mNotify->post();
+ info->mNotify = NULL;
+ info->mOwnedByClient = false;
+
+ return OK;
+}
+
+ssize_t MediaCodec::dequeuePortBuffer(int32_t portIndex) {
+ CHECK(portIndex == kPortIndexInput || portIndex == kPortIndexOutput);
+
+ List<size_t> *availBuffers = &mAvailPortBuffers[portIndex];
+
+ if (availBuffers->empty()) {
+ return -EAGAIN;
+ }
+
+ size_t index = *availBuffers->begin();
+ availBuffers->erase(availBuffers->begin());
+
+ BufferInfo *info = &mPortBuffers[portIndex].editItemAt(index);
+ CHECK(!info->mOwnedByClient);
+ info->mOwnedByClient = true;
+
+ return index;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaCodecList.cpp b/media/libstagefright/MediaCodecList.cpp
new file mode 100644
index 0000000..6b64e21
--- /dev/null
+++ b/media/libstagefright/MediaCodecList.cpp
@@ -0,0 +1,475 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "MediaCodecList"
+#include <utils/Log.h>
+
+#include <media/stagefright/MediaCodecList.h>
+
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/MediaErrors.h>
+#include <utils/threads.h>
+
+#include <expat.h>
+
+namespace android {
+
+static Mutex sInitMutex;
+
+// static
+MediaCodecList *MediaCodecList::sCodecList;
+
+// static
+const MediaCodecList *MediaCodecList::getInstance() {
+ Mutex::Autolock autoLock(sInitMutex);
+
+ if (sCodecList == NULL) {
+ sCodecList = new MediaCodecList;
+ }
+
+ return sCodecList->initCheck() == OK ? sCodecList : NULL;
+}
+
+MediaCodecList::MediaCodecList()
+ : mInitCheck(NO_INIT) {
+ FILE *file = fopen("/etc/media_codecs.xml", "r");
+
+ if (file == NULL) {
+ ALOGW("unable to open media codecs configuration xml file.");
+ return;
+ }
+
+ parseXMLFile(file);
+
+ if (mInitCheck == OK) {
+ // These are currently still used by the video editing suite.
+
+ addMediaCodec(true /* encoder */, "AACEncoder", "audio/mp4a-latm");
+ addMediaCodec(true /* encoder */, "AVCEncoder", "video/avc");
+
+ addMediaCodec(true /* encoder */, "M4vH263Encoder");
+ addType("video/3gpp");
+ addType("video/mp4v-es");
+ }
+
+#if 0
+ for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+ const CodecInfo &info = mCodecInfos.itemAt(i);
+
+ AString line = info.mName;
+ line.append(" supports ");
+ for (size_t j = 0; j < mTypes.size(); ++j) {
+ uint32_t value = mTypes.valueAt(j);
+
+ if (info.mTypes & (1ul << value)) {
+ line.append(mTypes.keyAt(j));
+ line.append(" ");
+ }
+ }
+
+ ALOGI("%s", line.c_str());
+ }
+#endif
+
+ fclose(file);
+ file = NULL;
+}
+
+MediaCodecList::~MediaCodecList() {
+}
+
+status_t MediaCodecList::initCheck() const {
+ return mInitCheck;
+}
+
+void MediaCodecList::parseXMLFile(FILE *file) {
+ mInitCheck = OK;
+ mCurrentSection = SECTION_TOPLEVEL;
+ mDepth = 0;
+
+ XML_Parser parser = ::XML_ParserCreate(NULL);
+ CHECK(parser != NULL);
+
+ ::XML_SetUserData(parser, this);
+ ::XML_SetElementHandler(
+ parser, StartElementHandlerWrapper, EndElementHandlerWrapper);
+
+ const int BUFF_SIZE = 512;
+ while (mInitCheck == OK) {
+ void *buff = ::XML_GetBuffer(parser, BUFF_SIZE);
+ if (buff == NULL) {
+ ALOGE("failed to in call to XML_GetBuffer()");
+ mInitCheck = UNKNOWN_ERROR;
+ break;
+ }
+
+ int bytes_read = ::fread(buff, 1, BUFF_SIZE, file);
+ if (bytes_read < 0) {
+ ALOGE("failed in call to read");
+ mInitCheck = ERROR_IO;
+ break;
+ }
+
+ if (::XML_ParseBuffer(parser, bytes_read, bytes_read == 0)
+ != XML_STATUS_OK) {
+ mInitCheck = ERROR_MALFORMED;
+ break;
+ }
+
+ if (bytes_read == 0) {
+ break;
+ }
+ }
+
+ ::XML_ParserFree(parser);
+
+ if (mInitCheck == OK) {
+ for (size_t i = mCodecInfos.size(); i-- > 0;) {
+ CodecInfo *info = &mCodecInfos.editItemAt(i);
+
+ if (info->mTypes == 0) {
+ // No types supported by this component???
+
+ ALOGW("Component %s does not support any type of media?",
+ info->mName.c_str());
+
+ mCodecInfos.removeAt(i);
+ }
+ }
+ }
+
+ if (mInitCheck != OK) {
+ mCodecInfos.clear();
+ mCodecQuirks.clear();
+ }
+}
+
+// static
+void MediaCodecList::StartElementHandlerWrapper(
+ void *me, const char *name, const char **attrs) {
+ static_cast<MediaCodecList *>(me)->startElementHandler(name, attrs);
+}
+
+// static
+void MediaCodecList::EndElementHandlerWrapper(void *me, const char *name) {
+ static_cast<MediaCodecList *>(me)->endElementHandler(name);
+}
+
+void MediaCodecList::startElementHandler(
+ const char *name, const char **attrs) {
+ if (mInitCheck != OK) {
+ return;
+ }
+
+ switch (mCurrentSection) {
+ case SECTION_TOPLEVEL:
+ {
+ if (!strcmp(name, "Decoders")) {
+ mCurrentSection = SECTION_DECODERS;
+ } else if (!strcmp(name, "Encoders")) {
+ mCurrentSection = SECTION_ENCODERS;
+ }
+ break;
+ }
+
+ case SECTION_DECODERS:
+ {
+ if (!strcmp(name, "MediaCodec")) {
+ mInitCheck =
+ addMediaCodecFromAttributes(false /* encoder */, attrs);
+
+ mCurrentSection = SECTION_DECODER;
+ }
+ break;
+ }
+
+ case SECTION_ENCODERS:
+ {
+ if (!strcmp(name, "MediaCodec")) {
+ mInitCheck =
+ addMediaCodecFromAttributes(true /* encoder */, attrs);
+
+ mCurrentSection = SECTION_ENCODER;
+ }
+ break;
+ }
+
+ case SECTION_DECODER:
+ case SECTION_ENCODER:
+ {
+ if (!strcmp(name, "Quirk")) {
+ mInitCheck = addQuirk(attrs);
+ } else if (!strcmp(name, "Type")) {
+ mInitCheck = addTypeFromAttributes(attrs);
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ ++mDepth;
+}
+
+void MediaCodecList::endElementHandler(const char *name) {
+ if (mInitCheck != OK) {
+ return;
+ }
+
+ switch (mCurrentSection) {
+ case SECTION_DECODERS:
+ {
+ if (!strcmp(name, "Decoders")) {
+ mCurrentSection = SECTION_TOPLEVEL;
+ }
+ break;
+ }
+
+ case SECTION_ENCODERS:
+ {
+ if (!strcmp(name, "Encoders")) {
+ mCurrentSection = SECTION_TOPLEVEL;
+ }
+ break;
+ }
+
+ case SECTION_DECODER:
+ {
+ if (!strcmp(name, "MediaCodec")) {
+ mCurrentSection = SECTION_DECODERS;
+ }
+ break;
+ }
+
+ case SECTION_ENCODER:
+ {
+ if (!strcmp(name, "MediaCodec")) {
+ mCurrentSection = SECTION_ENCODERS;
+ }
+ break;
+ }
+
+ default:
+ break;
+ }
+
+ --mDepth;
+}
+
+status_t MediaCodecList::addMediaCodecFromAttributes(
+ bool encoder, const char **attrs) {
+ const char *name = NULL;
+ const char *type = NULL;
+
+ size_t i = 0;
+ while (attrs[i] != NULL) {
+ if (!strcmp(attrs[i], "name")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ name = attrs[i + 1];
+ ++i;
+ } else if (!strcmp(attrs[i], "type")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ type = attrs[i + 1];
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+
+ ++i;
+ }
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ addMediaCodec(encoder, name, type);
+
+ return OK;
+}
+
+void MediaCodecList::addMediaCodec(
+ bool encoder, const char *name, const char *type) {
+ mCodecInfos.push();
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mName = name;
+ info->mIsEncoder = encoder;
+ info->mTypes = 0;
+ info->mQuirks = 0;
+
+ if (type != NULL) {
+ addType(type);
+ }
+}
+
+status_t MediaCodecList::addQuirk(const char **attrs) {
+ const char *name = NULL;
+
+ size_t i = 0;
+ while (attrs[i] != NULL) {
+ if (!strcmp(attrs[i], "name")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ name = attrs[i + 1];
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+
+ ++i;
+ }
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ uint32_t bit;
+ ssize_t index = mCodecQuirks.indexOfKey(name);
+ if (index < 0) {
+ bit = mCodecQuirks.size();
+
+ if (bit == 32) {
+ ALOGW("Too many distinct quirk names in configuration.");
+ return OK;
+ }
+
+ mCodecQuirks.add(name, bit);
+ } else {
+ bit = mCodecQuirks.valueAt(index);
+ }
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mQuirks |= 1ul << bit;
+
+ return OK;
+}
+
+status_t MediaCodecList::addTypeFromAttributes(const char **attrs) {
+ const char *name = NULL;
+
+ size_t i = 0;
+ while (attrs[i] != NULL) {
+ if (!strcmp(attrs[i], "name")) {
+ if (attrs[i + 1] == NULL) {
+ return -EINVAL;
+ }
+ name = attrs[i + 1];
+ ++i;
+ } else {
+ return -EINVAL;
+ }
+
+ ++i;
+ }
+
+ if (name == NULL) {
+ return -EINVAL;
+ }
+
+ addType(name);
+
+ return OK;
+}
+
+void MediaCodecList::addType(const char *name) {
+ uint32_t bit;
+ ssize_t index = mTypes.indexOfKey(name);
+ if (index < 0) {
+ bit = mTypes.size();
+
+ if (bit == 32) {
+ ALOGW("Too many distinct type names in configuration.");
+ return;
+ }
+
+ mTypes.add(name, bit);
+ } else {
+ bit = mTypes.valueAt(index);
+ }
+
+ CodecInfo *info = &mCodecInfos.editItemAt(mCodecInfos.size() - 1);
+ info->mTypes |= 1ul << bit;
+}
+
+ssize_t MediaCodecList::findCodecByType(
+ const char *type, bool encoder, size_t startIndex) const {
+ ssize_t typeIndex = mTypes.indexOfKey(type);
+
+ if (typeIndex < 0) {
+ return -ENOENT;
+ }
+
+ uint32_t typeMask = 1ul << mTypes.valueAt(typeIndex);
+
+ while (startIndex < mCodecInfos.size()) {
+ const CodecInfo &info = mCodecInfos.itemAt(startIndex);
+
+ if (info.mIsEncoder == encoder && (info.mTypes & typeMask)) {
+ return startIndex;
+ }
+
+ ++startIndex;
+ }
+
+ return -ENOENT;
+}
+
+ssize_t MediaCodecList::findCodecByName(const char *name) const {
+ for (size_t i = 0; i < mCodecInfos.size(); ++i) {
+ const CodecInfo &info = mCodecInfos.itemAt(i);
+
+ if (info.mName == name) {
+ return i;
+ }
+ }
+
+ return -ENOENT;
+}
+
+const char *MediaCodecList::getCodecName(size_t index) const {
+ if (index >= mCodecInfos.size()) {
+ return NULL;
+ }
+
+ const CodecInfo &info = mCodecInfos.itemAt(index);
+ return info.mName.c_str();
+}
+
+bool MediaCodecList::codecHasQuirk(
+ size_t index, const char *quirkName) const {
+ if (index >= mCodecInfos.size()) {
+ return NULL;
+ }
+
+ const CodecInfo &info = mCodecInfos.itemAt(index);
+
+ if (info.mQuirks != 0) {
+ ssize_t index = mCodecQuirks.indexOfKey(quirkName);
+ if (index >= 0 && info.mQuirks & (1ul << mCodecQuirks.valueAt(index))) {
+ return true;
+ }
+ }
+
+ return false;
+}
+
+} // namespace android
diff --git a/media/libstagefright/MediaSourceSplitter.cpp b/media/libstagefright/MediaSourceSplitter.cpp
deleted file mode 100644
index 3b64ded..0000000
--- a/media/libstagefright/MediaSourceSplitter.cpp
+++ /dev/null
@@ -1,234 +0,0 @@
-/*
- * Copyright (C) 2010 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-//#define LOG_NDEBUG 0
-#define LOG_TAG "MediaSourceSplitter"
-#include <utils/Log.h>
-
-#include <media/stagefright/foundation/ADebug.h>
-#include <media/stagefright/MediaSourceSplitter.h>
-#include <media/stagefright/MediaBuffer.h>
-#include <media/stagefright/MetaData.h>
-
-namespace android {
-
-MediaSourceSplitter::MediaSourceSplitter(sp<MediaSource> mediaSource) {
- mNumberOfClients = 0;
- mSource = mediaSource;
- mSourceStarted = false;
-
- mNumberOfClientsStarted = 0;
- mNumberOfCurrentReads = 0;
- mCurrentReadBit = 0;
- mLastReadCompleted = true;
-}
-
-MediaSourceSplitter::~MediaSourceSplitter() {
-}
-
-sp<MediaSource> MediaSourceSplitter::createClient() {
- Mutex::Autolock autoLock(mLock);
-
- sp<MediaSource> client = new Client(this, mNumberOfClients++);
- mClientsStarted.push(false);
- mClientsDesiredReadBit.push(0);
- return client;
-}
-
-status_t MediaSourceSplitter::start(int clientId, MetaData *params) {
- Mutex::Autolock autoLock(mLock);
-
- ALOGV("start client (%d)", clientId);
- if (mClientsStarted[clientId]) {
- return OK;
- }
-
- mNumberOfClientsStarted++;
-
- if (!mSourceStarted) {
- ALOGV("Starting real source from client (%d)", clientId);
- status_t err = mSource->start(params);
-
- if (err == OK) {
- mSourceStarted = true;
- mClientsStarted.editItemAt(clientId) = true;
- mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit;
- }
-
- return err;
- } else {
- mClientsStarted.editItemAt(clientId) = true;
- if (mLastReadCompleted) {
- // Last read was completed. So join in the threads for the next read.
- mClientsDesiredReadBit.editItemAt(clientId) = !mCurrentReadBit;
- } else {
- // Last read is ongoing. So join in the threads for the current read.
- mClientsDesiredReadBit.editItemAt(clientId) = mCurrentReadBit;
- }
- return OK;
- }
-}
-
-status_t MediaSourceSplitter::stop(int clientId) {
- Mutex::Autolock autoLock(mLock);
-
- ALOGV("stop client (%d)", clientId);
- CHECK(clientId >= 0 && clientId < mNumberOfClients);
- CHECK(mClientsStarted[clientId]);
-
- if (--mNumberOfClientsStarted == 0) {
- ALOGV("Stopping real source from client (%d)", clientId);
- status_t err = mSource->stop();
- mSourceStarted = false;
- mClientsStarted.editItemAt(clientId) = false;
- return err;
- } else {
- mClientsStarted.editItemAt(clientId) = false;
- if (!mLastReadCompleted && (mClientsDesiredReadBit[clientId] == mCurrentReadBit)) {
- // !mLastReadCompleted implies that buffer has been read from source, but all
- // clients haven't read it.
- // mClientsDesiredReadBit[clientId] == mCurrentReadBit implies that this
- // client would have wanted to read from this buffer. (i.e. it has not yet
- // called read() for the current read buffer.)
- // Since other threads may be waiting for all the clients' reads to complete,
- // signal that this read has been aborted.
- signalReadComplete_lock(true);
- }
- return OK;
- }
-}
-
-sp<MetaData> MediaSourceSplitter::getFormat(int clientId) {
- Mutex::Autolock autoLock(mLock);
-
- ALOGV("getFormat client (%d)", clientId);
- return mSource->getFormat();
-}
-
-status_t MediaSourceSplitter::read(int clientId,
- MediaBuffer **buffer, const MediaSource::ReadOptions *options) {
- Mutex::Autolock autoLock(mLock);
-
- CHECK(clientId >= 0 && clientId < mNumberOfClients);
-
- ALOGV("read client (%d)", clientId);
- *buffer = NULL;
-
- if (!mClientsStarted[clientId]) {
- return OK;
- }
-
- if (mCurrentReadBit != mClientsDesiredReadBit[clientId]) {
- // Desired buffer has not been read from source yet.
-
- // If the current client is the special client with clientId = 0
- // then read from source, else wait until the client 0 has finished
- // reading from source.
- if (clientId == 0) {
- // Wait for all client's last read to complete first so as to not
- // corrupt the buffer at mLastReadMediaBuffer.
- waitForAllClientsLastRead_lock(clientId);
-
- readFromSource_lock(options);
- *buffer = mLastReadMediaBuffer;
- } else {
- waitForReadFromSource_lock(clientId);
-
- *buffer = mLastReadMediaBuffer;
- (*buffer)->add_ref();
- }
- CHECK(mCurrentReadBit == mClientsDesiredReadBit[clientId]);
- } else {
- // Desired buffer has already been read from source. Use the cached data.
- CHECK(clientId != 0);
-
- *buffer = mLastReadMediaBuffer;
- (*buffer)->add_ref();
- }
-
- mClientsDesiredReadBit.editItemAt(clientId) = !mClientsDesiredReadBit[clientId];
- signalReadComplete_lock(false);
-
- return mLastReadStatus;
-}
-
-void MediaSourceSplitter::readFromSource_lock(const MediaSource::ReadOptions *options) {
- mLastReadStatus = mSource->read(&mLastReadMediaBuffer , options);
-
- mCurrentReadBit = !mCurrentReadBit;
- mLastReadCompleted = false;
- mReadFromSourceCondition.broadcast();
-}
-
-void MediaSourceSplitter::waitForReadFromSource_lock(int32_t clientId) {
- mReadFromSourceCondition.wait(mLock);
-}
-
-void MediaSourceSplitter::waitForAllClientsLastRead_lock(int32_t clientId) {
- if (mLastReadCompleted) {
- return;
- }
- mAllReadsCompleteCondition.wait(mLock);
- CHECK(mLastReadCompleted);
-}
-
-void MediaSourceSplitter::signalReadComplete_lock(bool readAborted) {
- if (!readAborted) {
- mNumberOfCurrentReads++;
- }
-
- if (mNumberOfCurrentReads == mNumberOfClientsStarted) {
- mLastReadCompleted = true;
- mNumberOfCurrentReads = 0;
- mAllReadsCompleteCondition.broadcast();
- }
-}
-
-status_t MediaSourceSplitter::pause(int clientId) {
- return ERROR_UNSUPPORTED;
-}
-
-// Client
-
-MediaSourceSplitter::Client::Client(
- sp<MediaSourceSplitter> splitter,
- int32_t clientId) {
- mSplitter = splitter;
- mClientId = clientId;
-}
-
-status_t MediaSourceSplitter::Client::start(MetaData *params) {
- return mSplitter->start(mClientId, params);
-}
-
-status_t MediaSourceSplitter::Client::stop() {
- return mSplitter->stop(mClientId);
-}
-
-sp<MetaData> MediaSourceSplitter::Client::getFormat() {
- return mSplitter->getFormat(mClientId);
-}
-
-status_t MediaSourceSplitter::Client::read(
- MediaBuffer **buffer, const ReadOptions *options) {
- return mSplitter->read(mClientId, buffer, options);
-}
-
-status_t MediaSourceSplitter::Client::pause() {
- return mSplitter->pause(mClientId);
-}
-
-} // namespace android
diff --git a/media/libstagefright/NuMediaExtractor.cpp b/media/libstagefright/NuMediaExtractor.cpp
new file mode 100644
index 0000000..afd4763
--- /dev/null
+++ b/media/libstagefright/NuMediaExtractor.cpp
@@ -0,0 +1,433 @@
+/*
+ * Copyright 2012, The Android Open Source Project
+ *
+ * Licensed under the Apache License, Version 2.0 (the "License");
+ * you may not use this file except in compliance with the License.
+ * You may obtain a copy of the License at
+ *
+ * http://www.apache.org/licenses/LICENSE-2.0
+ *
+ * Unless required by applicable law or agreed to in writing, software
+ * distributed under the License is distributed on an "AS IS" BASIS,
+ * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
+ * See the License for the specific language governing permissions and
+ * limitations under the License.
+ */
+
+//#define LOG_NDEBUG 0
+#define LOG_TAG "NuMediaExtractor"
+#include <utils/Log.h>
+
+#include <media/stagefright/NuMediaExtractor.h>
+
+#include "include/ESDS.h"
+
+#include <media/stagefright/foundation/ABuffer.h>
+#include <media/stagefright/foundation/ADebug.h>
+#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaBuffer.h>
+#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaErrors.h>
+#include <media/stagefright/MediaExtractor.h>
+#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
+#include <media/stagefright/Utils.h>
+
+namespace android {
+
+NuMediaExtractor::NuMediaExtractor() {
+}
+
+NuMediaExtractor::~NuMediaExtractor() {
+ releaseTrackSamples();
+
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ CHECK_EQ((status_t)OK, info->mSource->stop());
+ }
+
+ mSelectedTracks.clear();
+}
+
+status_t NuMediaExtractor::setDataSource(const char *path) {
+ sp<DataSource> dataSource = DataSource::CreateFromURI(path);
+
+ if (dataSource == NULL) {
+ return -ENOENT;
+ }
+
+ mImpl = MediaExtractor::Create(dataSource);
+
+ if (mImpl == NULL) {
+ return ERROR_UNSUPPORTED;
+ }
+
+ return OK;
+}
+
+size_t NuMediaExtractor::countTracks() const {
+ return mImpl == NULL ? 0 : mImpl->countTracks();
+}
+
+status_t NuMediaExtractor::getTrackFormat(
+ size_t index, sp<AMessage> *format) const {
+ *format = NULL;
+
+ if (mImpl == NULL) {
+ return -EINVAL;
+ }
+
+ if (index >= mImpl->countTracks()) {
+ return -ERANGE;
+ }
+
+ sp<MetaData> meta = mImpl->getTrackMetaData(index);
+
+ const char *mime;
+ CHECK(meta->findCString(kKeyMIMEType, &mime));
+
+ sp<AMessage> msg = new AMessage;
+ msg->setString("mime", mime);
+
+ if (!strncasecmp("video/", mime, 6)) {
+ int32_t width, height;
+ CHECK(meta->findInt32(kKeyWidth, &width));
+ CHECK(meta->findInt32(kKeyHeight, &height));
+
+ msg->setInt32("width", width);
+ msg->setInt32("height", height);
+ } else {
+ CHECK(!strncasecmp("audio/", mime, 6));
+
+ int32_t numChannels, sampleRate;
+ CHECK(meta->findInt32(kKeyChannelCount, &numChannels));
+ CHECK(meta->findInt32(kKeySampleRate, &sampleRate));
+
+ msg->setInt32("channel-count", numChannels);
+ msg->setInt32("sample-rate", sampleRate);
+ }
+
+ int32_t maxInputSize;
+ if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
+ msg->setInt32("max-input-size", maxInputSize);
+ }
+
+ uint32_t type;
+ const void *data;
+ size_t size;
+ if (meta->findData(kKeyAVCC, &type, &data, &size)) {
+ // Parse the AVCDecoderConfigurationRecord
+
+ const uint8_t *ptr = (const uint8_t *)data;
+
+ CHECK(size >= 7);
+ CHECK_EQ((unsigned)ptr[0], 1u); // configurationVersion == 1
+ uint8_t profile = ptr[1];
+ uint8_t level = ptr[3];
+
+ // There is decodable content out there that fails the following
+ // assertion, let's be lenient for now...
+ // CHECK((ptr[4] >> 2) == 0x3f); // reserved
+
+ size_t lengthSize = 1 + (ptr[4] & 3);
+
+ // commented out check below as H264_QVGA_500_NO_AUDIO.3gp
+ // violates it...
+ // CHECK((ptr[5] >> 5) == 7); // reserved
+
+ size_t numSeqParameterSets = ptr[5] & 31;
+
+ ptr += 6;
+ size -= 6;
+
+ sp<ABuffer> buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ for (size_t i = 0; i < numSeqParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+
+ msg->setBuffer("csd-0", buffer);
+
+ buffer = new ABuffer(1024);
+ buffer->setRange(0, 0);
+
+ CHECK(size >= 1);
+ size_t numPictureParameterSets = *ptr;
+ ++ptr;
+ --size;
+
+ for (size_t i = 0; i < numPictureParameterSets; ++i) {
+ CHECK(size >= 2);
+ size_t length = U16_AT(ptr);
+
+ ptr += 2;
+ size -= 2;
+
+ CHECK(size >= length);
+
+ memcpy(buffer->data() + buffer->size(), "\x00\x00\x00\x01", 4);
+ memcpy(buffer->data() + buffer->size() + 4, ptr, length);
+ buffer->setRange(0, buffer->size() + 4 + length);
+
+ ptr += length;
+ size -= length;
+ }
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-1", buffer);
+ } else if (meta->findData(kKeyESDS, &type, &data, &size)) {
+ ESDS esds((const char *)data, size);
+ CHECK_EQ(esds.InitCheck(), (status_t)OK);
+
+ const void *codec_specific_data;
+ size_t codec_specific_data_size;
+ esds.getCodecSpecificInfo(
+ &codec_specific_data, &codec_specific_data_size);
+
+ sp<ABuffer> buffer = new ABuffer(codec_specific_data_size);
+
+ memcpy(buffer->data(), codec_specific_data,
+ codec_specific_data_size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+ } else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
+ sp<ABuffer> buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-0", buffer);
+
+ if (!meta->findData(kKeyVorbisBooks, &type, &data, &size)) {
+ return -EINVAL;
+ }
+
+ buffer = new ABuffer(size);
+ memcpy(buffer->data(), data, size);
+
+ buffer->meta()->setInt32("csd", true);
+ buffer->meta()->setInt64("timeUs", 0);
+ msg->setBuffer("csd-1", buffer);
+ }
+
+ *format = msg;
+
+ return OK;
+}
+
+status_t NuMediaExtractor::selectTrack(size_t index) {
+ if (mImpl == NULL) {
+ return -EINVAL;
+ }
+
+ if (index >= mImpl->countTracks()) {
+ return -ERANGE;
+ }
+
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ if (info->mTrackIndex == index) {
+ // This track has already been selected.
+ return OK;
+ }
+ }
+
+ sp<MediaSource> source = mImpl->getTrack(index);
+
+ CHECK_EQ((status_t)OK, source->start());
+
+ mSelectedTracks.push();
+ TrackInfo *info = &mSelectedTracks.editItemAt(mSelectedTracks.size() - 1);
+
+ info->mSource = source;
+ info->mTrackIndex = index;
+ info->mFinalResult = OK;
+ info->mSample = NULL;
+ info->mSampleTimeUs = -1ll;
+ info->mFlags = 0;
+
+ const char *mime;
+ CHECK(source->getFormat()->findCString(kKeyMIMEType, &mime));
+
+ if (!strcasecmp(mime, MEDIA_MIMETYPE_AUDIO_VORBIS)) {
+ info->mFlags |= kIsVorbis;
+ }
+
+ return OK;
+}
+
+void NuMediaExtractor::releaseTrackSamples() {
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ if (info->mSample != NULL) {
+ info->mSample->release();
+ info->mSample = NULL;
+
+ info->mSampleTimeUs = -1ll;
+ }
+ }
+}
+
+ssize_t NuMediaExtractor::fetchTrackSamples(int64_t seekTimeUs) {
+ TrackInfo *minInfo = NULL;
+ ssize_t minIndex = -1;
+
+ for (size_t i = 0; i < mSelectedTracks.size(); ++i) {
+ TrackInfo *info = &mSelectedTracks.editItemAt(i);
+
+ if (seekTimeUs >= 0ll) {
+ info->mFinalResult = OK;
+
+ if (info->mSample != NULL) {
+ info->mSample->release();
+ info->mSample = NULL;
+ info->mSampleTimeUs = -1ll;
+ }
+ } else if (info->mFinalResult != OK) {
+ continue;
+ }
+
+ if (info->mSample == NULL) {
+ MediaSource::ReadOptions options;
+ if (seekTimeUs >= 0ll) {
+ options.setSeekTo(seekTimeUs);
+ }
+ status_t err = info->mSource->read(&info->mSample, &options);
+
+ if (err != OK) {
+ CHECK(info->mSample == NULL);
+
+ info->mFinalResult = err;
+ info->mSampleTimeUs = -1ll;
+ continue;
+ } else {
+ CHECK(info->mSample != NULL);
+ CHECK(info->mSample->meta_data()->findInt64(
+ kKeyTime, &info->mSampleTimeUs));
+ }
+ }
+
+ if (minInfo == NULL || info->mSampleTimeUs < minInfo->mSampleTimeUs) {
+ minInfo = info;
+ minIndex = i;
+ }
+ }
+
+ return minIndex;
+}
+
+status_t NuMediaExtractor::seekTo(int64_t timeUs) {
+ return fetchTrackSamples(timeUs);
+}
+
+status_t NuMediaExtractor::advance() {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+
+ info->mSample->release();
+ info->mSample = NULL;
+ info->mSampleTimeUs = -1ll;
+
+ return OK;
+}
+
+status_t NuMediaExtractor::readSampleData(const sp<ABuffer> &buffer) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+
+ size_t sampleSize = info->mSample->range_length();
+
+ if (info->mFlags & kIsVorbis) {
+ // Each sample's data is suffixed by the number of page samples
+ // or -1 if not available.
+ sampleSize += sizeof(int32_t);
+ }
+
+ if (buffer->capacity() < sampleSize) {
+ return -ENOMEM;
+ }
+
+ const uint8_t *src =
+ (const uint8_t *)info->mSample->data()
+ + info->mSample->range_offset();
+
+ memcpy((uint8_t *)buffer->data(), src, info->mSample->range_length());
+
+ if (info->mFlags & kIsVorbis) {
+ int32_t numPageSamples;
+ if (!info->mSample->meta_data()->findInt32(
+ kKeyValidSamples, &numPageSamples)) {
+ numPageSamples = -1;
+ }
+
+ memcpy((uint8_t *)buffer->data() + info->mSample->range_length(),
+ &numPageSamples,
+ sizeof(numPageSamples));
+ }
+
+ buffer->setRange(0, sampleSize);
+
+ return OK;
+}
+
+status_t NuMediaExtractor::getSampleTrackIndex(size_t *trackIndex) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+ *trackIndex = info->mTrackIndex;
+
+ return OK;
+}
+
+status_t NuMediaExtractor::getSampleTime(int64_t *sampleTimeUs) {
+ ssize_t minIndex = fetchTrackSamples();
+
+ if (minIndex < 0) {
+ return ERROR_END_OF_STREAM;
+ }
+
+ TrackInfo *info = &mSelectedTracks.editItemAt(minIndex);
+ *sampleTimeUs = info->mSampleTimeUs;
+
+ return OK;
+}
+
+} // namespace android
diff --git a/media/libstagefright/OMXClient.cpp b/media/libstagefright/OMXClient.cpp
index 7a805aa..7cdb793 100644
--- a/media/libstagefright/OMXClient.cpp
+++ b/media/libstagefright/OMXClient.cpp
@@ -335,6 +335,10 @@ status_t OMXClient::connect() {
}
void OMXClient::disconnect() {
+ if (mOMX.get() != NULL) {
+ mOMX.clear();
+ mOMX = NULL;
+ }
}
} // namespace android
diff --git a/media/libstagefright/OMXCodec.cpp b/media/libstagefright/OMXCodec.cpp
index 470f750..966416e 100755
--- a/media/libstagefright/OMXCodec.cpp
+++ b/media/libstagefright/OMXCodec.cpp
@@ -33,6 +33,7 @@
#include <media/stagefright/MediaBuffer.h>
#include <media/stagefright/MediaBufferGroup.h>
#include <media/stagefright/MediaDefs.h>
+#include <media/stagefright/MediaCodecList.h>
#include <media/stagefright/MediaExtractor.h>
#include <media/stagefright/MetaData.h>
#include <media/stagefright/OMXCodec.h>
@@ -57,11 +58,6 @@ const static int64_t kBufferFilledEventTimeOutNs = 3000000000LL;
// component in question is buggy or not.
const static uint32_t kMaxColorFormatSupported = 1000;
-struct CodecInfo {
- const char *mime;
- const char *codec;
-};
-
#define FACTORY_CREATE_ENCODER(name) \
static sp<MediaSource> Make##name(const sp<MediaSource> &source, const sp<MetaData> &meta) { \
return new name(source, meta); \
@@ -96,83 +92,8 @@ static sp<MediaSource> InstantiateSoftwareEncoder(
return NULL;
}
+#undef FACTORY_CREATE_ENCODER
#undef FACTORY_REF
-#undef FACTORY_CREATE
-
-static const CodecInfo kDecoderInfo[] = {
- { MEDIA_MIMETYPE_IMAGE_JPEG, "OMX.TI.JPEG.decode" },
-// { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.TI.MP3.decode" },
- { MEDIA_MIMETYPE_AUDIO_MPEG, "OMX.google.mp3.decoder" },
- { MEDIA_MIMETYPE_AUDIO_MPEG_LAYER_II, "OMX.Nvidia.mp2.decoder" },
-// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.decode" },
-// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amr.decoder" },
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.decoder" },
-// { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.Nvidia.amrwb.decoder" },
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.decode" },
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.decoder" },
-// { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.Nvidia.aac.decoder" },
- { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.decode" },
- { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.decoder" },
- { MEDIA_MIMETYPE_AUDIO_G711_ALAW, "OMX.google.g711.alaw.decoder" },
- { MEDIA_MIMETYPE_AUDIO_G711_MLAW, "OMX.google.g711.mlaw.decoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.DECODER" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.decode" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.decoder.mpeg4" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.decoder.mpeg4" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.Decoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Decoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.google.mpeg4.decoder" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.DUCATI1.VIDEO.DECODER" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.decode" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.decoder.h263" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.decoder.h263" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Decoder" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.google.h263.decoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.DUCATI1.VIDEO.DECODER" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.decode" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.decoder.avc" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.decoder.avc" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.Decoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Decoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.h264.decoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.google.avc.decoder" },
- { MEDIA_MIMETYPE_AUDIO_VORBIS, "OMX.google.vorbis.decoder" },
- { MEDIA_MIMETYPE_VIDEO_VPX, "OMX.google.vpx.decoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG2, "OMX.Nvidia.mpeg2v.decode" },
-};
-
-static const CodecInfo kEncoderInfo[] = {
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.TI.AMR.encode" },
- { MEDIA_MIMETYPE_AUDIO_AMR_NB, "OMX.google.amrnb.encoder" },
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.TI.WBAMR.encode" },
- { MEDIA_MIMETYPE_AUDIO_AMR_WB, "OMX.google.amrwb.encoder" },
- { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.TI.AAC.encode" },
- { MEDIA_MIMETYPE_AUDIO_AAC, "OMX.google.aac.encoder" },
- { MEDIA_MIMETYPE_AUDIO_AAC, "AACEncoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.7x30.video.encoder.mpeg4" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.qcom.video.encoder.mpeg4" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.TI.Video.encoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.Nvidia.mp4.encoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "OMX.SEC.MPEG4.Encoder" },
- { MEDIA_MIMETYPE_VIDEO_MPEG4, "M4vH263Encoder" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.DUCATI1.VIDEO.MPEG4E" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.7x30.video.encoder.h263" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.qcom.video.encoder.h263" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.TI.Video.encoder" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.Nvidia.h263.encoder" },
- { MEDIA_MIMETYPE_VIDEO_H263, "OMX.SEC.H263.Encoder" },
- { MEDIA_MIMETYPE_VIDEO_H263, "M4vH263Encoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.DUCATI1.VIDEO.H264E" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.7x30.video.encoder.avc" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.qcom.video.encoder.avc" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.TI.Video.encoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.Nvidia.h264.encoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "OMX.SEC.AVC.Encoder" },
- { MEDIA_MIMETYPE_VIDEO_AVC, "AVCEncoder" },
-};
-
-#undef OPTIONAL
#define CODEC_LOGI(x, ...) ALOGI("[%s] "x, mComponentName, ##__VA_ARGS__)
#define CODEC_LOGV(x, ...) ALOGV("[%s] "x, mComponentName, ##__VA_ARGS__)
@@ -207,22 +128,6 @@ private:
OMXCodecObserver &operator=(const OMXCodecObserver &);
};
-static const char *GetCodec(const CodecInfo *info, size_t numInfos,
- const char *mime, int index) {
- CHECK(index >= 0);
- for(size_t i = 0; i < numInfos; ++i) {
- if (!strcasecmp(mime, info[i].mime)) {
- if (index == 0) {
- return info[i].codec;
- }
-
- --index;
- }
- }
-
- return NULL;
-}
-
template<class T>
static void InitOMXParams(T *params) {
params->nSize = sizeof(T);
@@ -278,119 +183,36 @@ static int CompareSoftwareCodecsFirst(
}
// static
-uint32_t OMXCodec::getComponentQuirks(
- const char *componentName, bool isEncoder) {
- uint32_t quirks = 0;
-
- if (!strcmp(componentName, "OMX.Nvidia.amr.decoder") ||
- !strcmp(componentName, "OMX.Nvidia.amrwb.decoder") ||
- !strcmp(componentName, "OMX.Nvidia.aac.decoder") ||
- !strcmp(componentName, "OMX.Nvidia.mp3.decoder")) {
- quirks |= kDecoderLiesAboutNumberOfChannels;
- }
-
- if (!strcmp(componentName, "OMX.TI.MP3.decode")) {
- quirks |= kNeedsFlushBeforeDisable;
- quirks |= kDecoderLiesAboutNumberOfChannels;
- }
- if (!strcmp(componentName, "OMX.TI.AAC.decode")) {
- quirks |= kNeedsFlushBeforeDisable;
- quirks |= kRequiresFlushCompleteEmulation;
- quirks |= kSupportsMultipleFramesPerInputBuffer;
- }
- if (!strncmp(componentName, "OMX.qcom.video.encoder.", 23)) {
- quirks |= kRequiresLoadedToIdleAfterAllocation;
- quirks |= kRequiresAllocateBufferOnInputPorts;
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- if (!strncmp(componentName, "OMX.qcom.video.encoder.avc", 26)) {
-
- // The AVC encoder advertises the size of output buffers
- // based on the input video resolution and assumes
- // the worst/least compression ratio is 0.5. It is found that
- // sometimes, the output buffer size is larger than
- // size advertised by the encoder.
- quirks |= kRequiresLargerEncoderOutputBuffer;
- }
- }
- if (!strncmp(componentName, "OMX.qcom.7x30.video.encoder.", 28)) {
- }
- if (!strncmp(componentName, "OMX.qcom.video.decoder.", 23)) {
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- quirks |= kDefersOutputBufferAllocation;
- }
- if (!strncmp(componentName, "OMX.qcom.7x30.video.decoder.", 28)) {
- quirks |= kRequiresAllocateBufferOnInputPorts;
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- quirks |= kDefersOutputBufferAllocation;
- }
-
- if (!strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.DECODER")) {
- quirks |= kRequiresAllocateBufferOnInputPorts;
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- }
-
- // FIXME:
- // Remove the quirks after the work is done.
- else if (!strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.MPEG4E") ||
- !strcmp(componentName, "OMX.TI.DUCATI1.VIDEO.H264E")) {
-
- quirks |= kRequiresAllocateBufferOnInputPorts;
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- }
- else if (!strncmp(componentName, "OMX.TI.", 7)) {
- // Apparently I must not use OMX_UseBuffer on either input or
- // output ports on any of the TI components or quote:
- // "(I) may have unexpected problem (sic) which can be timing related
- // and hard to reproduce."
-
- quirks |= kRequiresAllocateBufferOnInputPorts;
- quirks |= kRequiresAllocateBufferOnOutputPorts;
- if (!strncmp(componentName, "OMX.TI.Video.encoder", 20)) {
- quirks |= kAvoidMemcopyInputRecordingFrames;
- }
- }
-
- if (!strcmp(componentName, "OMX.TI.Video.Decoder")) {
- quirks |= kInputBufferSizesAreBogus;
- }
-
- if (!strncmp(componentName, "OMX.SEC.", 8) && !isEncoder) {
- // These output buffers contain no video data, just some
- // opaque information that allows the overlay to display their
- // contents.
- quirks |= kOutputBuffersAreUnreadable;
- }
-
- return quirks;
-}
-
-// static
void OMXCodec::findMatchingCodecs(
const char *mime,
bool createEncoder, const char *matchComponentName,
uint32_t flags,
- Vector<String8> *matchingCodecs) {
+ Vector<String8> *matchingCodecs,
+ Vector<uint32_t> *matchingCodecQuirks) {
matchingCodecs->clear();
- for (int index = 0;; ++index) {
- const char *componentName;
+ if (matchingCodecQuirks) {
+ matchingCodecQuirks->clear();
+ }
- if (createEncoder) {
- componentName = GetCodec(
- kEncoderInfo,
- sizeof(kEncoderInfo) / sizeof(kEncoderInfo[0]),
- mime, index);
- } else {
- componentName = GetCodec(
- kDecoderInfo,
- sizeof(kDecoderInfo) / sizeof(kDecoderInfo[0]),
- mime, index);
- }
+ const MediaCodecList *list = MediaCodecList::getInstance();
+ if (list == NULL) {
+ return;
+ }
+
+ size_t index = 0;
+ for (;;) {
+ ssize_t matchIndex =
+ list->findCodecByType(mime, createEncoder, index);
- if (!componentName) {
+ if (matchIndex < 0) {
break;
}
+ index = matchIndex + 1;
+
+ const char *componentName = list->getCodecName(matchIndex);
+
// If a specific codec is requested, skip the non-matching ones.
if (matchComponentName && strcmp(componentName, matchComponentName)) {
continue;
@@ -405,6 +227,10 @@ void OMXCodec::findMatchingCodecs(
(!(flags & (kSoftwareCodecsOnly | kHardwareCodecsOnly)))) {
matchingCodecs->push(String8(componentName));
+
+ if (matchingCodecQuirks) {
+ matchingCodecQuirks->push(getComponentQuirks(list, matchIndex));
+ }
}
}
@@ -414,6 +240,45 @@ void OMXCodec::findMatchingCodecs(
}
// static
+uint32_t OMXCodec::getComponentQuirks(
+ const MediaCodecList *list, size_t index) {
+ uint32_t quirks = 0;
+ if (list->codecHasQuirk(
+ index, "requires-allocate-on-input-ports")) {
+ quirks |= kRequiresAllocateBufferOnInputPorts;
+ }
+ if (list->codecHasQuirk(
+ index, "requires-allocate-on-output-ports")) {
+ quirks |= kRequiresAllocateBufferOnOutputPorts;
+ }
+ if (list->codecHasQuirk(
+ index, "output-buffers-are-unreadable")) {
+ quirks |= kOutputBuffersAreUnreadable;
+ }
+
+ return quirks;
+}
+
+// static
+bool OMXCodec::findCodecQuirks(const char *componentName, uint32_t *quirks) {
+ const MediaCodecList *list = MediaCodecList::getInstance();
+
+ if (list == NULL) {
+ return false;
+ }
+
+ ssize_t index = list->findCodecByName(componentName);
+
+ if (index < 0) {
+ return false;
+ }
+
+ *quirks = getComponentQuirks(list, index);
+
+ return true;
+}
+
+// static
sp<MediaSource> OMXCodec::Create(
const sp<IOMX> &omx,
const sp<MetaData> &meta, bool createEncoder,
@@ -435,8 +300,10 @@ sp<MediaSource> OMXCodec::Create(
CHECK(success);
Vector<String8> matchingCodecs;
+ Vector<uint32_t> matchingCodecQuirks;
findMatchingCodecs(
- mime, createEncoder, matchComponentName, flags, &matchingCodecs);
+ mime, createEncoder, matchComponentName, flags,
+ &matchingCodecs, &matchingCodecQuirks);
if (matchingCodecs.isEmpty()) {
return NULL;
@@ -447,6 +314,7 @@ sp<MediaSource> OMXCodec::Create(
for (size_t i = 0; i < matchingCodecs.size(); ++i) {
const char *componentNameBase = matchingCodecs[i].string();
+ uint32_t quirks = matchingCodecQuirks[i];
const char *componentName = componentNameBase;
AString tmp;
@@ -470,8 +338,6 @@ sp<MediaSource> OMXCodec::Create(
ALOGV("Attempting to allocate OMX node '%s'", componentName);
- uint32_t quirks = getComponentQuirks(componentNameBase, createEncoder);
-
if (!createEncoder
&& (quirks & kOutputBuffersAreUnreadable)
&& (flags & kClientNeedsFramebuffer)) {
@@ -627,16 +493,6 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
CODEC_LOGI(
"AVC profile = %u (%s), level = %u",
profile, AVCProfileToString(profile), level);
-
- if (!strcmp(mComponentName, "OMX.TI.Video.Decoder")
- && (profile != kAVCProfileBaseline || level > 30)) {
- // This stream exceeds the decoder's capabilities. The decoder
- // does not handle this gracefully and would clobber the heap
- // and wreak havoc instead...
-
- ALOGE("Profile and/or level exceed the decoder's capabilities.");
- return ERROR_UNSUPPORTED;
- }
} else if (meta->findData(kKeyVorbisInfo, &type, &data, &size)) {
addCodecSpecificData(data, size);
@@ -692,40 +548,11 @@ status_t OMXCodec::configureCodec(const sp<MetaData> &meta) {
}
}
- if (!strcasecmp(mMIME, MEDIA_MIMETYPE_IMAGE_JPEG)
- && !strcmp(mComponentName, "OMX.TI.JPEG.decode")) {
- OMX_COLOR_FORMATTYPE format =
- OMX_COLOR_Format32bitARGB8888;
- // OMX_COLOR_FormatYUV420PackedPlanar;
- // OMX_COLOR_FormatCbYCrY;
- // OMX_COLOR_FormatYUV411Planar;
-
- int32_t width, height;
- bool success = meta->findInt32(kKeyWidth, &width);
- success = success && meta->findInt32(kKeyHeight, &height);
-
- int32_t compressedSize;
- success = success && meta->findInt32(
- kKeyMaxInputSize, &compressedSize);
-
- CHECK(success);
- CHECK(compressedSize > 0);
-
- setImageOutputFormat(format, width, height);
- setJPEGInputFormat(width, height, (OMX_U32)compressedSize);
- }
-
int32_t maxInputSize;
if (meta->findInt32(kKeyMaxInputSize, &maxInputSize)) {
setMinBufferSize(kPortIndexInput, (OMX_U32)maxInputSize);
}
- if (!strcmp(mComponentName, "OMX.TI.AMR.encode")
- || !strcmp(mComponentName, "OMX.TI.WBAMR.encode")
- || !strcmp(mComponentName, "OMX.TI.AAC.encode")) {
- setMinBufferSize(kPortIndexOutput, 8192); // XXX
- }
-
initOutputFormat(meta);
if ((mFlags & kClientNeedsFramebuffer)
@@ -829,21 +656,6 @@ status_t OMXCodec::setVideoPortFormatType(
index, format.eCompressionFormat, format.eColorFormat);
#endif
- if (!strcmp("OMX.TI.Video.encoder", mComponentName)) {
- if (portIndex == kPortIndexInput
- && colorFormat == format.eColorFormat) {
- // eCompressionFormat does not seem right.
- found = true;
- break;
- }
- if (portIndex == kPortIndexOutput
- && compressionFormat == format.eCompressionFormat) {
- // eColorFormat does not seem right.
- found = true;
- break;
- }
- }
-
if (format.eCompressionFormat == compressionFormat
&& format.eColorFormat == colorFormat) {
found = true;
@@ -906,13 +718,8 @@ status_t OMXCodec::findTargetColorFormat(
int32_t targetColorFormat;
if (meta->findInt32(kKeyColorFormat, &targetColorFormat)) {
*colorFormat = (OMX_COLOR_FORMATTYPE) targetColorFormat;
- } else {
- if (!strcasecmp("OMX.TI.Video.encoder", mComponentName)) {
- *colorFormat = OMX_COLOR_FormatYCbYCr;
- }
}
-
// Check whether the target color format is supported.
return isColorFormatSupported(*colorFormat, kPortIndexInput);
}
@@ -1541,6 +1348,8 @@ void OMXCodec::setComponentRole(
"video_decoder.mpeg4", "video_encoder.mpeg4" },
{ MEDIA_MIMETYPE_VIDEO_H263,
"video_decoder.h263", "video_encoder.h263" },
+ { MEDIA_MIMETYPE_VIDEO_VPX,
+ "video_decoder.vpx", "video_encoder.vpx" },
};
static const size_t kNumMimeToRole =
@@ -3324,13 +3133,6 @@ bool OMXCodec::drainInputBuffer(BufferInfo *info) {
info->mStatus = OWNED_BY_COMPONENT;
- // This component does not ever signal the EOS flag on output buffers,
- // Thanks for nothing.
- if (mSignalledEOS && !strcmp(mComponentName, "OMX.TI.Video.encoder")) {
- mNoMoreOutputData = true;
- mBufferFilled.signal();
- }
-
return true;
}
@@ -3556,6 +3358,7 @@ status_t OMXCodec::setAACFormat(int32_t numChannels, int32_t sampleRate, int32_t
//////////////// output port ////////////////////
// format
OMX_AUDIO_PARAM_PORTFORMATTYPE format;
+ InitOMXParams(&format);
format.nPortIndex = kPortIndexOutput;
format.nIndex = 0;
status_t err = OMX_ErrorNone;
diff --git a/media/libstagefright/SurfaceMediaSource.cpp b/media/libstagefright/SurfaceMediaSource.cpp
index aa047d6..ab2cff0 100644
--- a/media/libstagefright/SurfaceMediaSource.cpp
+++ b/media/libstagefright/SurfaceMediaSource.cpp
@@ -24,9 +24,8 @@
#include <media/stagefright/MetadataBufferType.h>
#include <ui/GraphicBuffer.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
-#include <surfaceflinger/IGraphicBufferAlloc.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/IGraphicBufferAlloc.h>
#include <OMX_Component.h>
#include <utils/Log.h>
diff --git a/media/libstagefright/TimedEventQueue.cpp b/media/libstagefright/TimedEventQueue.cpp
index f4b5d4f..6d345bb 100644
--- a/media/libstagefright/TimedEventQueue.cpp
+++ b/media/libstagefright/TimedEventQueue.cpp
@@ -26,8 +26,6 @@
#include "include/TimedEventQueue.h"
-#include <cutils/sched_policy.h>
-
#include <sys/prctl.h>
#include <sys/time.h>
diff --git a/media/libstagefright/codecs/aacenc/Android.mk b/media/libstagefright/codecs/aacenc/Android.mk
index 34a2796..509193c 100644
--- a/media/libstagefright/codecs/aacenc/Android.mk
+++ b/media/libstagefright/codecs/aacenc/Android.mk
@@ -79,7 +79,7 @@ LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV5E
endif
ifeq ($(VOTT), v7)
-LOCAL_CFLAGS += -DARMV5E -DARMV7Neon -DARM_INASM -DARMV5_INASM
+LOCAL_CFLAGS += -DARMV5E -DARMV7Neon -DARM_INASM -DARMV5_INASM -DARMV6_INASM
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV5E
LOCAL_C_INCLUDES += $(LOCAL_PATH)/src/asm/ARMV7
endif
diff --git a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
index ef3c31b..5cd7e5f 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/basic_op.h
@@ -227,27 +227,18 @@ Word32 L_shr_r (Word32 L_var1, Word16 var2);
#if ARMV4_INASM
__inline Word32 ASM_L_shr(Word32 L_var1, Word16 var2)
{
- Word32 result;
- asm volatile(
- "MOV %[result], %[L_var1], ASR %[var2] \n"
- :[result]"=r"(result)
- :[L_var1]"r"(L_var1), [var2]"r"(var2)
- );
- return result;
+ return L_var1 >> var2;
}
__inline Word32 ASM_L_shl(Word32 L_var1, Word16 var2)
{
Word32 result;
- asm volatile(
- "MOV r2, %[L_var1] \n"
- "MOV r3, #0x7fffffff\n"
+ asm (
"MOV %[result], %[L_var1], ASL %[var2] \n"
- "TEQ r2, %[result], ASR %[var2]\n"
- "EORNE %[result],r3,r2,ASR#31\n"
- :[result]"+r"(result)
- :[L_var1]"r"(L_var1), [var2]"r"(var2)
- :"r2", "r3"
+ "TEQ %[L_var1], %[result], ASR %[var2]\n"
+ "EORNE %[result], %[mask], %[L_var1], ASR #31\n"
+ :[result]"=&r"(result)
+ :[L_var1]"r"(L_var1), [var2]"r"(var2), [mask]"r"(0x7fffffff)
);
return result;
}
@@ -255,10 +246,10 @@ __inline Word32 ASM_L_shl(Word32 L_var1, Word16 var2)
__inline Word32 ASM_shr(Word32 L_var1, Word16 var2)
{
Word32 result;
- asm volatile(
+ asm (
"CMP %[var2], #15\n"
- "MOVGE %[var2], #15\n"
- "MOV %[result], %[L_var1], ASR %[var2]\n"
+ "MOVLT %[result], %[L_var1], ASR %[var2]\n"
+ "MOVGE %[result], %[L_var1], ASR #15\n"
:[result]"=r"(result)
:[L_var1]"r"(L_var1), [var2]"r"(var2)
);
@@ -267,21 +258,32 @@ __inline Word32 ASM_shr(Word32 L_var1, Word16 var2)
__inline Word32 ASM_shl(Word32 L_var1, Word16 var2)
{
+#if ARMV6_SAT
Word32 result;
- asm volatile(
+ asm (
"CMP %[var2], #16\n"
- "MOVGE %[var2], #16\n"
- "MOV %[result], %[L_var1], ASL %[var2]\n"
- "MOV r3, #1\n"
- "MOV r2, %[result], ASR #15\n"
- "RSB r3,r3,r3,LSL #15 \n"
- "TEQ r2, %[result], ASR #31 \n"
- "EORNE %[result], r3, %[result],ASR #31"
- :[result]"+r"(result)
+ "MOVLT %[result], %[L_var1], ASL %[var2]\n"
+ "MOVGE %[result], %[L_var1], ASL #16\n"
+ "SSAT %[result], #16, %[result]\n"
+ :[result]"=r"(result)
:[L_var1]"r"(L_var1), [var2]"r"(var2)
- :"r2", "r3"
);
return result;
+#else
+ Word32 result;
+ Word32 tmp;
+ asm (
+ "CMP %[var2], #16\n"
+ "MOVLT %[result], %[L_var1], ASL %[var2]\n"
+ "MOVGE %[result], %[L_var1], ASL #16\n"
+ "MOV %[tmp], %[result], ASR #15\n"
+ "TEQ %[tmp], %[result], ASR #31 \n"
+ "EORNE %[result], %[mask], %[result],ASR #31"
+ :[result]"=&r"(result), [tmp]"=&r"(tmp)
+ :[L_var1]"r"(L_var1), [var2]"r"(var2), [mask]"r"(0x7fff)
+ );
+ return result;
+#endif
}
#endif
@@ -293,18 +295,24 @@ __inline Word32 ASM_shl(Word32 L_var1, Word16 var2)
#if (SATRUATE_IS_INLINE)
__inline Word16 saturate(Word32 L_var1)
{
-#if ARMV5TE_SAT
+#if ARMV6_SAT
+ Word16 result;
+ asm (
+ "SSAT %[result], #16, %[L_var1]"
+ : [result]"=r"(result)
+ : [L_var1]"r"(L_var1)
+ );
+ return result;
+#elif ARMV5TE_SAT
Word16 result;
+ Word32 tmp;
asm volatile (
- "MOV %[result], %[L_var1]\n"
- "MOV r3, #1\n"
- "MOV r2,%[L_var1],ASR#15\n"
- "RSB r3, r3, r3, LSL #15\n"
- "TEQ r2,%[L_var1],ASR#31\n"
- "EORNE %[result],r3,%[L_var1],ASR#31\n"
- :[result]"+r"(result)
- :[L_var1]"r"(L_var1)
- :"r2", "r3"
+ "MOV %[tmp], %[L_var1],ASR#15\n"
+ "TEQ %[tmp], %[L_var1],ASR#31\n"
+ "EORNE %[result], %[mask],%[L_var1],ASR#31\n"
+ "MOVEQ %[result], %[L_var1]\n"
+ :[result]"=&r"(result), [tmp]"=&r"(tmp)
+ :[L_var1]"r"(L_var1), [mask]"r"(0x7fff)
);
return result;
@@ -420,10 +428,10 @@ __inline Word32 L_mult(Word16 var1, Word16 var2)
{
#if ARMV5TE_L_MULT
Word32 result;
- asm volatile(
+ asm (
"SMULBB %[result], %[var1], %[var2] \n"
"QADD %[result], %[result], %[result] \n"
- :[result]"+r"(result)
+ :[result]"=r"(result)
:[var1]"r"(var1), [var2]"r"(var2)
);
return result;
@@ -450,11 +458,10 @@ __inline Word32 L_msu (Word32 L_var3, Word16 var1, Word16 var2)
{
#if ARMV5TE_L_MSU
Word32 result;
- asm volatile(
+ asm (
"SMULBB %[result], %[var1], %[var2] \n"
- "QADD %[result], %[result], %[result] \n"
- "QSUB %[result], %[L_var3], %[result]\n"
- :[result]"+r"(result)
+ "QDSUB %[result], %[L_var3], %[result]\n"
+ :[result]"=&r"(result)
:[L_var3]"r"(L_var3), [var1]"r"(var1), [var2]"r"(var2)
);
return result;
@@ -474,9 +481,9 @@ __inline Word32 L_sub(Word32 L_var1, Word32 L_var2)
{
#if ARMV5TE_L_SUB
Word32 result;
- asm volatile(
+ asm (
"QSUB %[result], %[L_var1], %[L_var2]\n"
- :[result]"+r"(result)
+ :[result]"=r"(result)
:[L_var1]"r"(L_var1), [L_var2]"r"(L_var2)
);
return result;
@@ -589,16 +596,14 @@ __inline Word16 add (Word16 var1, Word16 var2)
{
#if ARMV5TE_ADD
Word32 result;
- asm volatile(
+ Word32 tmp;
+ asm (
"ADD %[result], %[var1], %[var2] \n"
- "MOV r3, #0x1\n"
- "MOV r2, %[result], ASR #15\n"
- "RSB r3, r3, r3, LSL, #15\n"
- "TEQ r2, %[result], ASR #31\n"
- "EORNE %[result], r3, %[result], ASR #31"
- :[result]"+r"(result)
- :[var1]"r"(var1), [var2]"r"(var2)
- :"r2", "r3"
+ "MOV %[tmp], %[result], ASR #15 \n"
+ "TEQ %[tmp], %[result], ASR #31 \n"
+ "EORNE %[result], %[mask], %[result], ASR #31"
+ :[result]"=&r"(result), [tmp]"=&r"(tmp)
+ :[var1]"r"(var1), [var2]"r"(var2), [mask]"r"(0x7fff)
);
return result;
#else
@@ -619,16 +624,14 @@ __inline Word16 sub(Word16 var1, Word16 var2)
{
#if ARMV5TE_SUB
Word32 result;
- asm volatile(
- "MOV r3, #1\n"
+ Word32 tmp;
+ asm (
"SUB %[result], %[var1], %[var2] \n"
- "RSB r3,r3,r3,LSL#15\n"
- "MOV r2, %[var1], ASR #15 \n"
- "TEQ r2, %[var1], ASR #31 \n"
- "EORNE %[result], r3, %[result], ASR #31 \n"
- :[result]"+r"(result)
- :[var1]"r"(var1), [var2]"r"(var2)
- :"r2", "r3"
+ "MOV %[tmp], %[var1], ASR #15 \n"
+ "TEQ %[tmp], %[var1], ASR #31 \n"
+ "EORNE %[result], %[mask], %[result], ASR #31 \n"
+ :[result]"=&r"(result), [tmp]"=&r"(tmp)
+ :[var1]"r"(var1), [var2]"r"(var2), [mask]"r"(0x7fff)
);
return result;
#else
@@ -682,19 +685,25 @@ __inline Word16 div_s (Word16 var1, Word16 var2)
#if (MULT_IS_INLINE)
__inline Word16 mult (Word16 var1, Word16 var2)
{
-#if ARMV5TE_MULT
+#if ARMV5TE_MULT && ARMV6_SAT
Word32 result;
- asm volatile(
- "SMULBB r2, %[var1], %[var2] \n"
- "MOV r3, #1\n"
- "MOV %[result], r2, ASR #15\n"
- "RSB r3, r3, r3, LSL #15\n"
- "MOV r2, %[result], ASR #15\n"
- "TEQ r2, %[result], ASR #31\n"
- "EORNE %[result], r3, %[result], ASR #31 \n"
- :[result]"+r"(result)
+ asm (
+ "SMULBB %[result], %[var1], %[var2] \n"
+ "SSAT %[result], #16, %[result], ASR #15 \n"
+ :[result]"=r"(result)
:[var1]"r"(var1), [var2]"r"(var2)
- :"r2", "r3"
+ );
+ return result;
+#elif ARMV5TE_MULT
+ Word32 result, tmp;
+ asm (
+ "SMULBB %[tmp], %[var1], %[var2] \n"
+ "MOV %[result], %[tmp], ASR #15\n"
+ "MOV %[tmp], %[result], ASR #15\n"
+ "TEQ %[tmp], %[result], ASR #31\n"
+ "EORNE %[result], %[mask], %[result], ASR #31 \n"
+ :[result]"=&r"(result), [tmp]"=&r"(tmp)
+ :[var1]"r"(var1), [var2]"r"(var2), [mask]"r"(0x7fff)
);
return result;
#else
@@ -719,18 +728,17 @@ __inline Word16 norm_s (Word16 var1)
{
#if ARMV5TE_NORM_S
Word16 result;
- asm volatile(
- "MOV r2,%[var1] \n"
- "CMP r2, #0\n"
- "RSBLT %[var1], %[var1], #0 \n"
- "CLZNE %[result], %[var1]\n"
+ Word32 tmp;
+ asm (
+ "RSBS %[tmp], %[var1], #0 \n"
+ "CLZLT %[result], %[var1]\n"
+ "CLZGT %[result], %[tmp]\n"
"SUBNE %[result], %[result], #17\n"
"MOVEQ %[result], #0\n"
- "CMP r2, #-1\n"
+ "CMP %[var1], #-1\n"
"MOVEQ %[result], #15\n"
- :[result]"+r"(result)
+ :[result]"=&r"(result), [tmp]"=&r"(tmp)
:[var1]"r"(var1)
- :"r2"
);
return result;
#else
@@ -774,7 +782,7 @@ __inline Word16 norm_l (Word32 L_var1)
"CLZNE %[result], %[L_var1]\n"
"SUBNE %[result], %[result], #1\n"
"MOVEQ %[result], #0\n"
- :[result]"+r"(result)
+ :[result]"=r"(result)
:[L_var1]"r"(L_var1)
);
return result;
@@ -979,13 +987,11 @@ __inline Word16 round16(Word32 L_var1)
{
#if ARMV5TE_ROUND
Word16 result;
- asm volatile(
- "MOV r1,#0x00008000\n"
- "QADD %[result], %[L_var1], r1\n"
+ asm (
+ "QADD %[result], %[L_var1], %[bias]\n"
"MOV %[result], %[result], ASR #16 \n"
- :[result]"+r"(result)
- :[L_var1]"r"(L_var1)
- :"r1"
+ :[result]"=r"(result)
+ :[L_var1]"r"(L_var1), [bias]"r"(0x8000)
);
return result;
#else
@@ -1005,11 +1011,10 @@ __inline Word32 L_mac (Word32 L_var3, Word16 var1, Word16 var2)
{
#if ARMV5TE_L_MAC
Word32 result;
- asm volatile(
+ asm (
"SMULBB %[result], %[var1], %[var2]\n"
- "QADD %[result], %[result], %[result]\n"
- "QADD %[result], %[result], %[L_var3]\n"
- :[result]"+r"(result)
+ "QDADD %[result], %[L_var3], %[result]\n"
+ :[result]"=&r"(result)
: [L_var3]"r"(L_var3), [var1]"r"(var1), [var2]"r"(var2)
);
return result;
@@ -1029,9 +1034,9 @@ __inline Word32 L_add (Word32 L_var1, Word32 L_var2)
{
#if ARMV5TE_L_ADD
Word32 result;
- asm volatile(
+ asm (
"QADD %[result], %[L_var1], %[L_var2]\n"
- :[result]"+r"(result)
+ :[result]"=r"(result)
:[L_var1]"r"(L_var1), [L_var2]"r"(L_var2)
);
return result;
diff --git a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h
index 9ebd1c2..6e5844f 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/oper_32b.h
@@ -63,7 +63,7 @@ __inline Word32 L_mpy_wx(Word32 L_var2, Word16 var1)
Word32 result;
asm volatile(
"SMULWB %[result], %[L_var2], %[var1] \n"
- :[result]"+r"(result)
+ :[result]"=r"(result)
:[L_var2]"r"(L_var2), [var1]"r"(var1)
);
return result;
diff --git a/media/libstagefright/codecs/aacenc/basic_op/typedefs.h b/media/libstagefright/codecs/aacenc/basic_op/typedefs.h
index 2d5d956..6059237 100644
--- a/media/libstagefright/codecs/aacenc/basic_op/typedefs.h
+++ b/media/libstagefright/codecs/aacenc/basic_op/typedefs.h
@@ -48,9 +48,7 @@
#define assert(_Expression) ((void)0)
#endif
-#ifdef LINUX
-#define __inline static __inline__
-#endif
+#define __inline static __inline
#define INT_BITS 32
/*
@@ -130,6 +128,13 @@ typedef unsigned __int64 UWord64;
#define ARMV5TE_NORM_L 1
#define ARMV5TE_L_MPY_LS 1
#endif
+#if ARMV6_INASM
+ #undef ARMV5TE_ADD
+ #define ARMV5TE_ADD 0
+ #undef ARMV5TE_SUB
+ #define ARMV5TE_SUB 0
+ #define ARMV6_SAT 1
+#endif
//basic operation functions optimization flags
#define SATRUATE_IS_INLINE 1 //define saturate as inline function
diff --git a/media/libstagefright/codecs/aacenc/inc/aacenc_core.h b/media/libstagefright/codecs/aacenc/inc/aacenc_core.h
index 1acdbbc..bb75b6d 100644
--- a/media/libstagefright/codecs/aacenc/inc/aacenc_core.h
+++ b/media/libstagefright/codecs/aacenc/inc/aacenc_core.h
@@ -102,7 +102,7 @@ Word16 AacEncEncode(AAC_ENCODER *hAacEnc,
const UWord8 *ancBytes, /*!< pointer to ancillary data bytes */
Word16 *numAncBytes, /*!< number of ancillary Data Bytes, send as fill element */
UWord8 *outBytes, /*!< pointer to output buffer */
- Word32 *numOutBytes /*!< number of bytes in output buffer */
+ VO_U32 *numOutBytes /*!< number of bytes in output buffer */
);
/*---------------------------------------------------------------------------
diff --git a/media/libstagefright/codecs/aacenc/inc/bitbuffer.h b/media/libstagefright/codecs/aacenc/inc/bitbuffer.h
index e538064..7c79f07 100644
--- a/media/libstagefright/codecs/aacenc/inc/bitbuffer.h
+++ b/media/libstagefright/codecs/aacenc/inc/bitbuffer.h
@@ -76,7 +76,7 @@ Word16 GetBitsAvail(HANDLE_BIT_BUF hBitBuf);
Word16 WriteBits(HANDLE_BIT_BUF hBitBuf,
- Word32 writeValue,
+ UWord32 writeValue,
Word16 noBitsToWrite);
void ResetBitBuf(HANDLE_BIT_BUF hBitBuf,
diff --git a/media/libstagefright/codecs/aacenc/inc/psy_configuration.h b/media/libstagefright/codecs/aacenc/inc/psy_configuration.h
index 9abfc99..f6981fa 100644
--- a/media/libstagefright/codecs/aacenc/inc/psy_configuration.h
+++ b/media/libstagefright/codecs/aacenc/inc/psy_configuration.h
@@ -31,7 +31,7 @@ typedef struct{
Word16 sfbCnt;
Word16 sfbActive; /* number of sf bands containing energy after lowpass */
- Word16 *sfbOffset;
+ const Word16 *sfbOffset;
Word32 sfbThresholdQuiet[MAX_SFB_LONG];
@@ -61,7 +61,7 @@ typedef struct{
Word16 sfbCnt;
Word16 sfbActive; /* number of sf bands containing energy after lowpass */
- Word16 *sfbOffset;
+ const Word16 *sfbOffset;
Word32 sfbThresholdQuiet[MAX_SFB_SHORT];
diff --git a/media/libstagefright/codecs/aacenc/src/aacenc_core.c b/media/libstagefright/codecs/aacenc/src/aacenc_core.c
index 2b3bd48..cecbc8f 100644
--- a/media/libstagefright/codecs/aacenc/src/aacenc_core.c
+++ b/media/libstagefright/codecs/aacenc/src/aacenc_core.c
@@ -146,7 +146,7 @@ Word16 AacEncEncode(AAC_ENCODER *aacEnc, /*!< an encoder handle */
const UWord8 *ancBytes, /*!< pointer to ancillary data bytes */
Word16 *numAncBytes, /*!< number of ancillary Data Bytes */
UWord8 *outBytes, /*!< pointer to output buffer (must be large MINBITS_COEF/8*MAX_CHANNELS bytes) */
- Word32 *numOutBytes /*!< number of bytes in output buffer after processing */
+ VO_U32 *numOutBytes /*!< number of bytes in output buffer after processing */
)
{
ELEMENT_INFO *elInfo = &aacEnc->elInfo;
diff --git a/media/libstagefright/codecs/aacenc/src/adj_thr.c b/media/libstagefright/codecs/aacenc/src/adj_thr.c
index a8ab809..373b063 100644
--- a/media/libstagefright/codecs/aacenc/src/adj_thr.c
+++ b/media/libstagefright/codecs/aacenc/src/adj_thr.c
@@ -26,6 +26,7 @@
#include "adj_thr.h"
#include "qc_data.h"
#include "line_pe.h"
+#include <string.h>
#define minSnrLimit 0x6666 /* 1 dB */
@@ -1138,6 +1139,7 @@ void AdjustThresholds(ADJ_THR_STATE *adjThrState,
Word16 maxBitresBits = elBits->maxBits;
Word16 sideInfoBits = (qcOE->staticBitsUsed + qcOE->ancBitsUsed);
Word16 ch;
+ memset(&peData, 0, sizeof(peData));
prepareSfbPe(&peData, psyOutChannel, logSfbEnergy, sfbNRelevantLines, nChannels, AdjThrStateElement->peOffset);
diff --git a/media/libstagefright/codecs/aacenc/src/bitbuffer.c b/media/libstagefright/codecs/aacenc/src/bitbuffer.c
index 5615ac3..0ce93d3 100644
--- a/media/libstagefright/codecs/aacenc/src/bitbuffer.c
+++ b/media/libstagefright/codecs/aacenc/src/bitbuffer.c
@@ -138,7 +138,7 @@ Word16 GetBitsAvail(HANDLE_BIT_BUF hBitBuf)
*
*****************************************************************************/
Word16 WriteBits(HANDLE_BIT_BUF hBitBuf,
- Word32 writeValue,
+ UWord32 writeValue,
Word16 noBitsToWrite)
{
Word16 wBitPos;
@@ -152,6 +152,7 @@ Word16 WriteBits(HANDLE_BIT_BUF hBitBuf,
wBitPos = hBitBuf->wBitPos;
wBitPos += noBitsToWrite;
+ writeValue &= ~(0xffffffff << noBitsToWrite); // Mask out everything except the lowest noBitsToWrite bits
writeValue <<= 32 - wBitPos;
writeValue |= hBitBuf->cache;
diff --git a/media/libstagefright/codecs/aacenc/src/dyn_bits.c b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
index 3d2efdc..7769188 100644
--- a/media/libstagefright/codecs/aacenc/src/dyn_bits.c
+++ b/media/libstagefright/codecs/aacenc/src/dyn_bits.c
@@ -281,7 +281,7 @@ noiselessCounter(SECTION_DATA *sectionData,
const Word32 blockType)
{
Word32 grpNdx, i;
- Word16 *sideInfoTab = NULL;
+ const Word16 *sideInfoTab = NULL;
SECTION_INFO *sectionInfo;
/*
diff --git a/media/libstagefright/codecs/aacenc/src/interface.c b/media/libstagefright/codecs/aacenc/src/interface.c
index f2472d8..d0ad433 100644
--- a/media/libstagefright/codecs/aacenc/src/interface.c
+++ b/media/libstagefright/codecs/aacenc/src/interface.c
@@ -99,8 +99,8 @@ void BuildInterface(Word32 *groupedMdctSpectrum,
Word32 i;
Word32 accuSumMS=0;
Word32 accuSumLR=0;
- Word32 *pSumMS = sfbEnergySumMS.sfbShort;
- Word32 *pSumLR = sfbEnergySumLR.sfbShort;
+ const Word32 *pSumMS = sfbEnergySumMS.sfbShort;
+ const Word32 *pSumLR = sfbEnergySumLR.sfbShort;
for (i=TRANS_FAC; i; i--) {
accuSumLR = L_add(accuSumLR, *pSumLR); pSumLR++;
diff --git a/media/libstagefright/codecs/aacenc/src/psy_configuration.c b/media/libstagefright/codecs/aacenc/src/psy_configuration.c
index 02d92ab..dd40f9b 100644
--- a/media/libstagefright/codecs/aacenc/src/psy_configuration.c
+++ b/media/libstagefright/codecs/aacenc/src/psy_configuration.c
@@ -139,7 +139,7 @@ static Word16 BarcLineValue(Word16 noOfLines, Word16 fftLine, Word32 samplingFre
*
*****************************************************************************/
static void initThrQuiet(Word16 numPb,
- Word16 *pbOffset,
+ const Word16 *pbOffset,
Word16 *pbBarcVal,
Word32 *pbThresholdQuiet) {
Word16 i;
@@ -250,7 +250,7 @@ static void initSpreading(Word16 numPb,
*
*****************************************************************************/
static void initBarcValues(Word16 numPb,
- Word16 *pbOffset,
+ const Word16 *pbOffset,
Word16 numLines,
Word32 samplingFrequency,
Word16 *pbBval)
diff --git a/media/libstagefright/codecs/aacenc/src/psy_main.c b/media/libstagefright/codecs/aacenc/src/psy_main.c
index 085acb8..4e9218c 100644
--- a/media/libstagefright/codecs/aacenc/src/psy_main.c
+++ b/media/libstagefright/codecs/aacenc/src/psy_main.c
@@ -658,7 +658,8 @@ static Word16 advancePsychShort(PSY_DATA* psyData,
Word32 normEnergyShift = (psyData->mdctScale + 1) << 1; /* in reference code, mdct spectrum must be multipied with 2, so +1 */
Word32 clipEnergy = hPsyConfShort->clipEnergy >> normEnergyShift;
Word32 wOffset = 0;
- Word32 *data0, *data1;
+ Word32 *data0;
+ const Word32 *data1;
for(w = 0; w < TRANS_FAC; w++) {
Word32 i, tdata;
diff --git a/media/libstagefright/codecs/aacenc/src/qc_main.c b/media/libstagefright/codecs/aacenc/src/qc_main.c
index df6d46e..48ff300 100644
--- a/media/libstagefright/codecs/aacenc/src/qc_main.c
+++ b/media/libstagefright/codecs/aacenc/src/qc_main.c
@@ -163,7 +163,7 @@ void QCOutDelete(QC_OUT* hQC, VO_MEM_OPERATOR *pMemOP)
Word32 i;
if(hQC)
{
- if(hQC->qcChannel[0].quantSpec);
+ if(hQC->qcChannel[0].quantSpec)
mem_free(pMemOP, hQC->qcChannel[0].quantSpec, VO_INDEX_ENC_AAC);
if(hQC->qcChannel[0].maxValueInSfb)
diff --git a/media/libstagefright/codecs/aacenc/src/quantize.c b/media/libstagefright/codecs/aacenc/src/quantize.c
index 54add2f..0d0f550 100644
--- a/media/libstagefright/codecs/aacenc/src/quantize.c
+++ b/media/libstagefright/codecs/aacenc/src/quantize.c
@@ -110,7 +110,7 @@ static void quantizeLines(const Word16 gain,
Word32 m = gain&3;
Word32 g = (gain >> 2) + 4;
Word32 mdctSpeL;
- Word16 *pquat;
+ const Word16 *pquat;
/* gain&3 */
pquat = quantBorders[m];
@@ -333,7 +333,7 @@ Word32 calcSfbDist(const Word32 *spec,
Word32 m = gain&3;
Word32 g = (gain >> 2) + 4;
Word32 g2 = (g << 1) + 1;
- Word16 *pquat, *repquat;
+ const Word16 *pquat, *repquat;
/* gain&3 */
pquat = quantBorders[m];
diff --git a/media/libstagefright/codecs/aacenc/src/sf_estim.c b/media/libstagefright/codecs/aacenc/src/sf_estim.c
index fe40137..bc320ec 100644
--- a/media/libstagefright/codecs/aacenc/src/sf_estim.c
+++ b/media/libstagefright/codecs/aacenc/src/sf_estim.c
@@ -400,7 +400,7 @@ static void assimilateSingleScf(PSY_OUT_CHANNEL *psyOutChan,
Word16 *minScfCalculated,
Flag restartOnSuccess)
{
- Word32 sfbLast, sfbAct, sfbNext, scfAct, scfMin;
+ Word16 sfbLast, sfbAct, sfbNext, scfAct, scfMin;
Word16 *scfLast, *scfNext;
Word32 sfbPeOld, sfbPeNew;
Word32 sfbDistNew;
diff --git a/media/libstagefright/codecs/aacenc/src/transform.c b/media/libstagefright/codecs/aacenc/src/transform.c
index a154a2f..a02336f 100644
--- a/media/libstagefright/codecs/aacenc/src/transform.c
+++ b/media/libstagefright/codecs/aacenc/src/transform.c
@@ -339,6 +339,12 @@ static void PostMDCT(int *buf0, int num, const int *csptr)
*buf1-- = MULHIGH(cosb, tr2) + MULHIGH(sinb, ti2);
}
}
+#else
+void Radix4First(int *buf, int num);
+void Radix8First(int *buf, int num);
+void Radix4FFT(int *buf, int num, int bgn, int *twidTab);
+void PreMDCT(int *buf0, int num, const int *csptr);
+void PostMDCT(int *buf0, int num, const int *csptr);
#endif
diff --git a/media/libstagefright/codecs/amrnb/common/include/az_lsp.h b/media/libstagefright/codecs/amrnb/common/include/az_lsp.h
index 3e15ba3..7c24ca9 100644
--- a/media/libstagefright/codecs/amrnb/common/include/az_lsp.h
+++ b/media/libstagefright/codecs/amrnb/common/include/az_lsp.h
@@ -83,7 +83,7 @@ extern "C"
; EXTERNAL VARIABLES REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/
- extern Word16 grid[];
+ extern const Word16 grid[];
/*----------------------------------------------------------------------------
; SIMPLE TYPEDEF'S
diff --git a/media/libstagefright/codecs/amrnb/common/include/inv_sqrt.h b/media/libstagefright/codecs/amrnb/common/include/inv_sqrt.h
index 4fb2b11..91ab3e4 100644
--- a/media/libstagefright/codecs/amrnb/common/include/inv_sqrt.h
+++ b/media/libstagefright/codecs/amrnb/common/include/inv_sqrt.h
@@ -85,7 +85,7 @@ extern "C"
; EXTERNAL VARIABLES REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/
- extern Word16 inv_sqrt_tbl[];
+ extern const Word16 inv_sqrt_tbl[];
/*----------------------------------------------------------------------------
; SIMPLE TYPEDEF'S
----------------------------------------------------------------------------*/
diff --git a/media/libstagefright/codecs/amrnb/common/include/log2_norm.h b/media/libstagefright/codecs/amrnb/common/include/log2_norm.h
index b104a69..46b4e4d 100644
--- a/media/libstagefright/codecs/amrnb/common/include/log2_norm.h
+++ b/media/libstagefright/codecs/amrnb/common/include/log2_norm.h
@@ -85,7 +85,7 @@ extern "C"
; EXTERNAL VARIABLES REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/
- extern Word16 log2_tbl[];
+ extern const Word16 log2_tbl[];
/*----------------------------------------------------------------------------
; SIMPLE TYPEDEF'S
----------------------------------------------------------------------------*/
diff --git a/media/libstagefright/codecs/amrnb/common/include/pow2.h b/media/libstagefright/codecs/amrnb/common/include/pow2.h
index c96fbdd..9b944eb 100644
--- a/media/libstagefright/codecs/amrnb/common/include/pow2.h
+++ b/media/libstagefright/codecs/amrnb/common/include/pow2.h
@@ -81,7 +81,7 @@ extern "C"
; EXTERNAL VARIABLES REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/
- extern Word16 pow2_tbl[];
+ extern const Word16 pow2_tbl[];
/*----------------------------------------------------------------------------
; SIMPLE TYPEDEF'S
----------------------------------------------------------------------------*/
diff --git a/media/libstagefright/codecs/amrnb/common/include/sqrt_l.h b/media/libstagefright/codecs/amrnb/common/include/sqrt_l.h
index 86209bd..a6a2ee5 100644
--- a/media/libstagefright/codecs/amrnb/common/include/sqrt_l.h
+++ b/media/libstagefright/codecs/amrnb/common/include/sqrt_l.h
@@ -82,7 +82,7 @@ extern "C"
; EXTERNAL VARIABLES REFERENCES
; Declare variables used in this module but defined elsewhere
----------------------------------------------------------------------------*/
- extern Word16 sqrt_l_tbl[];
+ extern const Word16 sqrt_l_tbl[];
/*----------------------------------------------------------------------------
; SIMPLE TYPEDEF'S
diff --git a/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp b/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp
index bd99b30..4135f30 100644
--- a/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/az_lsp.cpp
@@ -299,7 +299,7 @@ static Word16 Chebps(Word16 x,
t0 += (Word32) * (p_f) << 13;
- if ((UWord32)(t0 - 0xfe000000L) < 0x01ffffffL - 0xfe000000L)
+ if ((UWord32)(t0 - 0xfe000000L) < (UWord32)0x03ffffffL)
{
cheb = (Word16)(t0 >> 10);
}
diff --git a/media/libstagefright/codecs/amrnb/common/src/bitno_tab.cpp b/media/libstagefright/codecs/amrnb/common/src/bitno_tab.cpp
index fed684d..4ee04a5 100644
--- a/media/libstagefright/codecs/amrnb/common/src/bitno_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/bitno_tab.cpp
@@ -152,7 +152,7 @@ extern "C"
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
/* number of parameters per modes (values must be <= MAX_PRM_SIZE!) */
- extern const Word16 prmno[N_MODES] =
+ const Word16 prmno[N_MODES] =
{
PRMNO_MR475,
PRMNO_MR515,
@@ -166,7 +166,7 @@ extern "C"
};
/* number of parameters to first subframe per modes */
- extern const Word16 prmnofsf[N_MODES - 1] =
+ const Word16 prmnofsf[N_MODES - 1] =
{
PRMNOFSF_MR475,
PRMNOFSF_MR515,
@@ -179,7 +179,7 @@ extern "C"
};
/* parameter sizes (# of bits), one table per mode */
- extern const Word16 bitno_MR475[PRMNO_MR475] =
+ const Word16 bitno_MR475[PRMNO_MR475] =
{
8, 8, 7, /* LSP VQ */
8, 7, 2, 8, /* first subframe */
@@ -188,7 +188,7 @@ extern "C"
4, 7, 2, /* fourth subframe */
};
- extern const Word16 bitno_MR515[PRMNO_MR515] =
+ const Word16 bitno_MR515[PRMNO_MR515] =
{
8, 8, 7, /* LSP VQ */
8, 7, 2, 6, /* first subframe */
@@ -197,7 +197,7 @@ extern "C"
4, 7, 2, 6, /* fourth subframe */
};
- extern const Word16 bitno_MR59[PRMNO_MR59] =
+ const Word16 bitno_MR59[PRMNO_MR59] =
{
8, 9, 9, /* LSP VQ */
8, 9, 2, 6, /* first subframe */
@@ -206,7 +206,7 @@ extern "C"
4, 9, 2, 6, /* fourth subframe */
};
- extern const Word16 bitno_MR67[PRMNO_MR67] =
+ const Word16 bitno_MR67[PRMNO_MR67] =
{
8, 9, 9, /* LSP VQ */
8, 11, 3, 7, /* first subframe */
@@ -215,7 +215,7 @@ extern "C"
4, 11, 3, 7, /* fourth subframe */
};
- extern const Word16 bitno_MR74[PRMNO_MR74] =
+ const Word16 bitno_MR74[PRMNO_MR74] =
{
8, 9, 9, /* LSP VQ */
8, 13, 4, 7, /* first subframe */
@@ -224,7 +224,7 @@ extern "C"
5, 13, 4, 7, /* fourth subframe */
};
- extern const Word16 bitno_MR795[PRMNO_MR795] =
+ const Word16 bitno_MR795[PRMNO_MR795] =
{
9, 9, 9, /* LSP VQ */
8, 13, 4, 4, 5, /* first subframe */
@@ -233,7 +233,7 @@ extern "C"
6, 13, 4, 4, 5, /* fourth subframe */
};
- extern const Word16 bitno_MR102[PRMNO_MR102] =
+ const Word16 bitno_MR102[PRMNO_MR102] =
{
8, 9, 9, /* LSP VQ */
8, 1, 1, 1, 1, 10, 10, 7, 7, /* first subframe */
@@ -242,7 +242,7 @@ extern "C"
5, 1, 1, 1, 1, 10, 10, 7, 7, /* fourth subframe */
};
- extern const Word16 bitno_MR122[PRMNO_MR122] =
+ const Word16 bitno_MR122[PRMNO_MR122] =
{
7, 8, 9, 8, 6, /* LSP VQ */
9, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 5, /* first subframe */
@@ -251,7 +251,7 @@ extern "C"
6, 4, 4, 4, 4, 4, 4, 3, 3, 3, 3, 3, 5 /* fourth subframe */
};
- extern const Word16 bitno_MRDTX[PRMNO_MRDTX] =
+ const Word16 bitno_MRDTX[PRMNO_MRDTX] =
{
3,
8, 9, 9,
@@ -259,7 +259,7 @@ extern "C"
};
/* overall table with all parameter sizes for all modes */
- extern const Word16 * const bitno[N_MODES] =
+ const Word16 * const bitno[N_MODES] =
{
bitno_MR475,
bitno_MR515,
diff --git a/media/libstagefright/codecs/amrnb/common/src/bitreorder_tab.cpp b/media/libstagefright/codecs/amrnb/common/src/bitreorder_tab.cpp
index 69b20fb..e284bbc 100644
--- a/media/libstagefright/codecs/amrnb/common/src/bitreorder_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/bitreorder_tab.cpp
@@ -123,6 +123,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "bitreorder_tab.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -171,7 +172,7 @@ extern "C"
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
/* number of parameters per modes (values must be <= MAX_PRM_SIZE!) */
- extern const Word16 numOfBits[NUM_MODES] =
+ const Word16 numOfBits[NUM_MODES] =
{
NUMBIT_MR475,
NUMBIT_MR515,
@@ -191,7 +192,7 @@ extern "C"
NUMBIT_NO_DATA
};
- extern const Word16 reorderBits_MR475[NUMBIT_MR475] =
+ const Word16 reorderBits_MR475[NUMBIT_MR475] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 23, 24, 25, 26,
@@ -205,7 +206,7 @@ extern "C"
92, 31, 52, 65, 86
};
- extern const Word16 reorderBits_MR515[NUMBIT_MR515] =
+ const Word16 reorderBits_MR515[NUMBIT_MR515] =
{
7, 6, 5, 4, 3, 2, 1, 0, 15, 14,
13, 12, 11, 10, 9, 8, 23, 24, 25, 26,
@@ -220,7 +221,7 @@ extern "C"
53, 72, 91
};
- extern const Word16 reorderBits_MR59[NUMBIT_MR59] =
+ const Word16 reorderBits_MR59[NUMBIT_MR59] =
{
0, 1, 4, 5, 3, 6, 7, 2, 13, 15,
8, 9, 11, 12, 14, 10, 16, 28, 74, 29,
@@ -236,7 +237,7 @@ extern "C"
38, 59, 84, 105, 37, 58, 83, 104
};
- extern const Word16 reorderBits_MR67[NUMBIT_MR67] =
+ const Word16 reorderBits_MR67[NUMBIT_MR67] =
{
0, 1, 4, 3, 5, 6, 13, 7, 2, 8,
9, 11, 15, 12, 14, 10, 28, 82, 29, 83,
@@ -254,7 +255,7 @@ extern "C"
36, 61, 90, 115
};
- extern const Word16 reorderBits_MR74[NUMBIT_MR74] =
+ const Word16 reorderBits_MR74[NUMBIT_MR74] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 15, 16, 26, 87, 27,
@@ -273,7 +274,7 @@ extern "C"
39, 68, 100, 129, 40, 69, 101, 130
};
- extern const Word16 reorderBits_MR795[NUMBIT_MR795] =
+ const Word16 reorderBits_MR795[NUMBIT_MR795] =
{
8, 7, 6, 5, 4, 3, 2, 14, 16, 9,
10, 12, 13, 15, 11, 17, 20, 22, 24, 23,
@@ -293,7 +294,7 @@ extern "C"
139, 37, 69, 103, 135, 38, 70, 104, 136
};
- extern const Word16 reorderBits_MR102[NUMBIT_MR102] =
+ const Word16 reorderBits_MR102[NUMBIT_MR102] =
{
7, 6, 5, 4, 3, 2, 1, 0, 16, 15,
14, 13, 12, 11, 10, 9, 8, 26, 27, 28,
@@ -318,7 +319,7 @@ extern "C"
63, 46, 55, 56
};
- extern const Word16 reorderBits_MR122[NUMBIT_MR122] =
+ const Word16 reorderBits_MR122[NUMBIT_MR122] =
{
0, 1, 2, 3, 4, 5, 6, 7, 8, 9,
10, 11, 12, 13, 14, 23, 15, 16, 17, 18,
@@ -348,7 +349,7 @@ extern "C"
};
/* overall table with all parameter sizes for all modes */
- extern const Word16 * const reorderBits[NUM_MODES-1] =
+ const Word16 * const reorderBits[NUM_MODES-1] =
{
reorderBits_MR475,
reorderBits_MR515,
@@ -361,7 +362,7 @@ extern "C"
};
/* Number of Frames (16-bit segments sent for each mode */
- extern const Word16 numCompressedBytes[16] =
+ const Word16 numCompressedBytes[16] =
{
13, /*4.75*/
14, /*5.15*/
diff --git a/media/libstagefright/codecs/amrnb/common/src/bytesused.cpp b/media/libstagefright/codecs/amrnb/common/src/bytesused.cpp
index 9552206..b61bac4 100644
--- a/media/libstagefright/codecs/amrnb/common/src/bytesused.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/bytesused.cpp
@@ -152,7 +152,7 @@ extern "C"
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
- extern const short BytesUsed[16] =
+ const short BytesUsed[16] =
{
13, /* 4.75 */
14, /* 5.15 */
diff --git a/media/libstagefright/codecs/amrnb/common/src/c2_9pf_tab.cpp b/media/libstagefright/codecs/amrnb/common/src/c2_9pf_tab.cpp
index 471bee8..20de9d6 100644
--- a/media/libstagefright/codecs/amrnb/common/src/c2_9pf_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/c2_9pf_tab.cpp
@@ -86,7 +86,8 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 startPos[2*4*2] = {0, 2, 0, 3,
+ extern const Word16 startPos[];
+ const Word16 startPos[2*4*2] = {0, 2, 0, 3,
0, 2, 0, 3,
1, 3, 2, 4,
1, 4, 1, 4
diff --git a/media/libstagefright/codecs/amrnb/common/src/gains_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/gains_tbl.cpp
index a08dd2d..a7cd6fb 100644
--- a/media/libstagefright/codecs/amrnb/common/src/gains_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/gains_tbl.cpp
@@ -86,14 +86,16 @@ extern "C"
----------------------------------------------------------------------------*/
- extern const Word16 qua_gain_pitch[NB_QUA_PITCH] =
+ extern const Word16 qua_gain_pitch[];
+ const Word16 qua_gain_pitch[NB_QUA_PITCH] =
{
0, 3277, 6556, 8192, 9830, 11469, 12288, 13107,
13926, 14746, 15565, 16384, 17203, 18022, 18842, 19661
};
- extern const Word16 qua_gain_code[(NB_QUA_CODE+1)*3] =
+ extern const Word16 qua_gain_code[];
+ const Word16 qua_gain_code[(NB_QUA_CODE+1)*3] =
{
/* gain factor (g_fac) and quantized energy error (qua_ener_MR122, qua_ener)
* are stored:
diff --git a/media/libstagefright/codecs/amrnb/common/src/gray_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/gray_tbl.cpp
index 99073d9..c4b2dbc 100644
--- a/media/libstagefright/codecs/amrnb/common/src/gray_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/gray_tbl.cpp
@@ -83,8 +83,10 @@ extern "C"
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 gray[8] = {0, 1, 3, 2, 6, 4, 5, 7};
- extern const Word16 dgray[8] = {0, 1, 3, 2, 5, 6, 4, 7};
+ extern const Word16 gray[];
+ extern const Word16 dgray[];
+ const Word16 gray[8] = {0, 1, 3, 2, 6, 4, 5, 7};
+ const Word16 dgray[8] = {0, 1, 3, 2, 5, 6, 4, 7};
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
diff --git a/media/libstagefright/codecs/amrnb/common/src/grid_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/grid_tbl.cpp
index cd81566..48566cc 100644
--- a/media/libstagefright/codecs/amrnb/common/src/grid_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/grid_tbl.cpp
@@ -63,6 +63,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "az_lsp.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -91,7 +92,7 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 grid[grid_points + 1] =
+ const Word16 grid[grid_points + 1] =
{
32760, 32723, 32588, 32364, 32051, 31651,
31164, 30591, 29935, 29196, 28377, 27481,
diff --git a/media/libstagefright/codecs/amrnb/common/src/inv_sqrt_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/inv_sqrt_tbl.cpp
index bde2c4e..13c3b24 100644
--- a/media/libstagefright/codecs/amrnb/common/src/inv_sqrt_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/inv_sqrt_tbl.cpp
@@ -55,6 +55,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "inv_sqrt.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -82,7 +83,7 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 inv_sqrt_tbl[49] =
+ const Word16 inv_sqrt_tbl[49] =
{
32767, 31790, 30894, 30070, 29309, 28602, 27945, 27330, 26755, 26214,
diff --git a/media/libstagefright/codecs/amrnb/common/src/log2_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/log2_tbl.cpp
index 25d63b2..9b9b099 100644
--- a/media/libstagefright/codecs/amrnb/common/src/log2_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/log2_tbl.cpp
@@ -54,6 +54,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "log2_norm.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -82,7 +83,7 @@ extern "C"
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 log2_tbl[33] =
+ const Word16 log2_tbl[33] =
{
0, 1455, 2866, 4236, 5568, 6863, 8124, 9352, 10549, 11716,
12855, 13967, 15054, 16117, 17156, 18172, 19167, 20142, 21097, 22033,
diff --git a/media/libstagefright/codecs/amrnb/common/src/lsp_lsf_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/lsp_lsf_tbl.cpp
index cee0f32..ddeeba4 100644
--- a/media/libstagefright/codecs/amrnb/common/src/lsp_lsf_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/lsp_lsf_tbl.cpp
@@ -77,7 +77,8 @@ extern "C"
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 table[65] =
+ extern const Word16 table[];
+ const Word16 table[65] =
{
32767, 32729, 32610, 32413, 32138, 31786, 31357, 30853,
30274, 29622, 28899, 28106, 27246, 26320, 25330, 24279,
@@ -94,7 +95,8 @@ extern "C"
/* slope used to compute y = acos(x) */
- extern const Word16 slope[64] =
+ extern const Word16 slope[];
+ const Word16 slope[64] =
{
-26887, -8812, -5323, -3813, -2979, -2444, -2081, -1811,
-1608, -1450, -1322, -1219, -1132, -1059, -998, -946,
diff --git a/media/libstagefright/codecs/amrnb/common/src/lsp_tab.cpp b/media/libstagefright/codecs/amrnb/common/src/lsp_tab.cpp
index deded93..0a32dd7 100644
--- a/media/libstagefright/codecs/amrnb/common/src/lsp_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/lsp_tab.cpp
@@ -117,6 +117,7 @@ terms listed above has been obtained from the copyright holder.
----------------------------------------------------------------------------*/
#include "typedef.h"
#include "cnst.h"
+#include "lsp_tab.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -146,7 +147,7 @@ extern "C"
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
- extern const Word16 lsp_init_data[M] = {30000, 26000, 21000, 15000, 8000,
+ const Word16 lsp_init_data[M] = {30000, 26000, 21000, 15000, 8000,
0, -8000, -15000, -21000, -26000
};
diff --git a/media/libstagefright/codecs/amrnb/common/src/overflow_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/overflow_tbl.cpp
index e5d42d6..c4a016d 100644
--- a/media/libstagefright/codecs/amrnb/common/src/overflow_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/overflow_tbl.cpp
@@ -81,7 +81,7 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word32 overflow_tbl [32] = {0x7fffffffL, 0x3fffffffL,
+ const Word32 overflow_tbl [32] = {0x7fffffffL, 0x3fffffffL,
0x1fffffffL, 0x0fffffffL,
0x07ffffffL, 0x03ffffffL,
0x01ffffffL, 0x00ffffffL,
diff --git a/media/libstagefright/codecs/amrnb/common/src/ph_disp_tab.cpp b/media/libstagefright/codecs/amrnb/common/src/ph_disp_tab.cpp
index 99725df..d568b78 100644
--- a/media/libstagefright/codecs/amrnb/common/src/ph_disp_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/ph_disp_tab.cpp
@@ -81,14 +81,16 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 ph_imp_low_MR795[40] =
+ extern const Word16 ph_imp_low_MR795[];
+ const Word16 ph_imp_low_MR795[40] =
{
26777, 801, 2505, -683, -1382, 582, 604, -1274, 3511, -5894,
4534, -499, -1940, 3011, -5058, 5614, -1990, -1061, -1459, 4442,
-700, -5335, 4609, 452, -589, -3352, 2953, 1267, -1212, -2590,
1731, 3670, -4475, -975, 4391, -2537, 949, -1363, -979, 5734
};
- extern const Word16 ph_imp_mid_MR795[40] =
+ extern const Word16 ph_imp_mid_MR795[];
+ const Word16 ph_imp_mid_MR795[40] =
{
30274, 3831, -4036, 2972, -1048, -1002, 2477, -3043, 2815, -2231,
1753, -1611, 1714, -1775, 1543, -1008, 429, -169, 472, -1264,
@@ -96,14 +98,16 @@ extern "C"
-2063, 2644, -3060, 2897, -1978, 557, 780, -1369, 842, 655
};
- extern const Word16 ph_imp_low[40] =
+ extern const Word16 ph_imp_low[];
+ const Word16 ph_imp_low[40] =
{
14690, 11518, 1268, -2761, -5671, 7514, -35, -2807, -3040, 4823,
2952, -8424, 3785, 1455, 2179, -8637, 8051, -2103, -1454, 777,
1108, -2385, 2254, -363, -674, -2103, 6046, -5681, 1072, 3123,
-5058, 5312, -2329, -3728, 6924, -3889, 675, -1775, 29, 10145
};
- extern const Word16 ph_imp_mid[40] =
+ extern const Word16 ph_imp_mid[];
+ const Word16 ph_imp_mid[40] =
{
30274, 3831, -4036, 2972, -1048, -1002, 2477, -3043, 2815, -2231,
1753, -1611, 1714, -1775, 1543, -1008, 429, -169, 472, -1264,
diff --git a/media/libstagefright/codecs/amrnb/common/src/pow2_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/pow2_tbl.cpp
index e0183a6..902ea0f 100644
--- a/media/libstagefright/codecs/amrnb/common/src/pow2_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/pow2_tbl.cpp
@@ -53,6 +53,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "pow2.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -81,7 +82,7 @@ extern "C"
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 pow2_tbl[33] =
+ const Word16 pow2_tbl[33] =
{
16384, 16743, 17109, 17484, 17867, 18258, 18658, 19066, 19484, 19911,
20347, 20792, 21247, 21713, 22188, 22674, 23170, 23678, 24196, 24726,
diff --git a/media/libstagefright/codecs/amrnb/common/src/q_plsf_5_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/q_plsf_5_tbl.cpp
index ceb1e1e..caa81cb 100644
--- a/media/libstagefright/codecs/amrnb/common/src/q_plsf_5_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/q_plsf_5_tbl.cpp
@@ -56,6 +56,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "q_plsf_5_tbl.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -94,7 +95,7 @@ extern "C"
----------------------------------------------------------------------------*/
/* LSF means ->normalize frequency domain */
- extern const Word16 mean_lsf_5[10] =
+ const Word16 mean_lsf_5[10] =
{
1384,
2077,
@@ -108,7 +109,7 @@ extern "C"
13701
};
- extern const Word16 dico1_lsf_5[DICO1_5_SIZE * 4] =
+ const Word16 dico1_lsf_5[DICO1_5_SIZE * 4] =
{
-451, -1065, -529, -1305,
-450, -756, -497, -863,
@@ -240,7 +241,7 @@ extern "C"
1469, 2181, 1443, 2016
};
- extern const Word16 dico2_lsf_5[DICO2_5_SIZE * 4] =
+ const Word16 dico2_lsf_5[DICO2_5_SIZE * 4] =
{
-1631, -1600, -1796, -2290,
-1027, -1770, -1100, -2025,
@@ -500,7 +501,7 @@ extern "C"
2374, 2787, 1821, 2788
};
- extern const Word16 dico3_lsf_5[DICO3_5_SIZE * 4] =
+ const Word16 dico3_lsf_5[DICO3_5_SIZE * 4] =
{
-1812, -2275, -1879, -2537,
-1640, -1848, -1695, -2004,
@@ -760,7 +761,7 @@ extern "C"
2180, 1975, 2326, 2020
};
- extern const Word16 dico4_lsf_5[DICO4_5_SIZE * 4] =
+ const Word16 dico4_lsf_5[DICO4_5_SIZE * 4] =
{
-1857, -1681, -1857, -1755,
-2056, -1150, -2134, -1654,
@@ -1020,7 +1021,7 @@ extern "C"
1716, 1376, 1948, 1465
};
- extern const Word16 dico5_lsf_5[DICO5_5_SIZE * 4] =
+ const Word16 dico5_lsf_5[DICO5_5_SIZE * 4] =
{
-1002, -929, -1096, -1203,
-641, -931, -604, -961,
diff --git a/media/libstagefright/codecs/amrnb/common/src/qua_gain_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/qua_gain_tbl.cpp
index 52f77e9..2d913b8 100644
--- a/media/libstagefright/codecs/amrnb/common/src/qua_gain_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/qua_gain_tbl.cpp
@@ -54,6 +54,7 @@ terms listed above has been obtained from the copyright holder.
----------------------------------------------------------------------------*/
#include "typedef.h"
#include "qua_gain.h"
+#include "qua_gain_tbl.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -96,7 +97,7 @@ extern "C"
/* table used in 'high' rates: MR67 MR74 */
- extern const Word16 table_gain_highrates[VQ_SIZE_HIGHRATES*4] =
+ const Word16 table_gain_highrates[VQ_SIZE_HIGHRATES*4] =
{
/*
@@ -240,7 +241,7 @@ extern "C"
/* table used in 'low' rates: MR475, MR515, MR59 */
- extern const Word16 table_gain_lowrates[VQ_SIZE_LOWRATES*4] =
+ const Word16 table_gain_lowrates[VQ_SIZE_LOWRATES*4] =
{
/*g_pit, g_fac, qua_ener_MR122, qua_ener */
10813, 28753, 2879, 17333,
diff --git a/media/libstagefright/codecs/amrnb/common/src/sqrt_l_tbl.cpp b/media/libstagefright/codecs/amrnb/common/src/sqrt_l_tbl.cpp
index 5e9898c..5a84b63 100644
--- a/media/libstagefright/codecs/amrnb/common/src/sqrt_l_tbl.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/sqrt_l_tbl.cpp
@@ -58,6 +58,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "sqrt_l.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -85,7 +86,7 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 sqrt_l_tbl[50] =
+ const Word16 sqrt_l_tbl[50] =
{
16384, 16888, 17378, 17854, 18318, 18770, 19212, 19644, 20066, 20480,
20886, 21283, 21674, 22058, 22435, 22806, 23170, 23530, 23884, 24232,
diff --git a/media/libstagefright/codecs/amrnb/common/src/window_tab.cpp b/media/libstagefright/codecs/amrnb/common/src/window_tab.cpp
index fa5faa6..d8fc8cc 100644
--- a/media/libstagefright/codecs/amrnb/common/src/window_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/common/src/window_tab.cpp
@@ -117,6 +117,7 @@ terms listed above has been obtained from the copyright holder.
----------------------------------------------------------------------------*/
#include "typedef.h"
#include "cnst.h"
+#include "window_tab.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -154,7 +155,7 @@ extern "C"
/* window for non-EFR modesm; uses 40 samples lookahead */
- extern const Word16 window_200_40[L_WINDOW] =
+ const Word16 window_200_40[L_WINDOW] =
{
2621, 2623, 2629, 2638, 2651, 2668, 2689, 2713, 2741, 2772,
2808, 2847, 2890, 2936, 2986, 3040, 3097, 3158, 3223, 3291,
@@ -185,7 +186,7 @@ extern "C"
/* window for EFR, first two subframes, no lookahead */
- extern const Word16 window_160_80[L_WINDOW] =
+ const Word16 window_160_80[L_WINDOW] =
{
2621, 2624, 2633, 2648, 2668, 2695, 2727, 2765, 2809, 2859,
2915, 2976, 3043, 3116, 3194, 3279, 3368, 3464, 3565, 3671,
@@ -215,7 +216,7 @@ extern "C"
/* window for EFR, last two subframes, no lookahead */
- extern const Word16 window_232_8[L_WINDOW] =
+ const Word16 window_232_8[L_WINDOW] =
{
2621, 2623, 2627, 2634, 2644, 2656, 2671, 2689, 2710, 2734,
2760, 2789, 2821, 2855, 2893, 2933, 2975, 3021, 3069, 3120,
diff --git a/media/libstagefright/codecs/amrnb/dec/src/dec_input_format_tab.cpp b/media/libstagefright/codecs/amrnb/dec/src/dec_input_format_tab.cpp
index a59f5fa..fffbbfd 100644
--- a/media/libstagefright/codecs/amrnb/dec/src/dec_input_format_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/src/dec_input_format_tab.cpp
@@ -121,6 +121,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "amrdecode.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -152,7 +153,7 @@ extern "C"
----------------------------------------------------------------------------*/
/* Table containing the number of core AMR data bytes for */
/* each codec mode for WMF input format(number excludes frame type byte) */
- extern const Word16 WmfDecBytesPerFrame[16] =
+ const Word16 WmfDecBytesPerFrame[16] =
{
12, /* 4.75 */
13, /* 5.15 */
@@ -174,7 +175,7 @@ extern "C"
/* Table containing the number of core AMR data bytes for */
/* each codec mode for IF2 input format. */
- extern const Word16 If2DecBytesPerFrame[16] =
+ const Word16 If2DecBytesPerFrame[16] =
{
13, /* 4.75 */
14, /* 5.15 */
diff --git a/media/libstagefright/codecs/amrnb/dec/src/qgain475_tab.cpp b/media/libstagefright/codecs/amrnb/dec/src/qgain475_tab.cpp
index fbcd412..1a08efa 100644
--- a/media/libstagefright/codecs/amrnb/dec/src/qgain475_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/dec/src/qgain475_tab.cpp
@@ -92,7 +92,7 @@ extern "C"
* g_fac(2) (Q12) // frame 1 and 3
*
*/
- extern const Word16 table_gain_MR475[MR475_VQ_SIZE*4] =
+ const Word16 table_gain_MR475[MR475_VQ_SIZE*4] =
{
/*g_pit(0), g_fac(0), g_pit(1), g_fac(1) */
812, 128, 542, 140,
diff --git a/media/libstagefright/codecs/amrnb/enc/src/corrwght_tab.cpp b/media/libstagefright/codecs/amrnb/enc/src/corrwght_tab.cpp
index 769e7ba..b3ed02d 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/corrwght_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/corrwght_tab.cpp
@@ -57,6 +57,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "p_ol_wgh.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -84,7 +85,7 @@ extern "C"
; LOCAL VARIABLE DEFINITIONS
; [Variable declaration - defined here and used outside this module]
----------------------------------------------------------------------------*/
- extern const Word16 corrweight[251] =
+ const Word16 corrweight[251] =
{
20473, 20506, 20539, 20572, 20605, 20644, 20677,
20716, 20749, 20788, 20821, 20860, 20893, 20932,
diff --git a/media/libstagefright/codecs/amrnb/enc/src/enc_output_format_tab.cpp b/media/libstagefright/codecs/amrnb/enc/src/enc_output_format_tab.cpp
index 147989f..4551fd7 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/enc_output_format_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/enc_output_format_tab.cpp
@@ -117,6 +117,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "amrencode.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -150,7 +151,7 @@ extern "C"
/* for WMF output format. */
/* Each entry is the sum of the 3GPP frame type byte and the */
/* number of packed core AMR data bytes */
- extern const Word16 WmfEncBytesPerFrame[16] =
+ const Word16 WmfEncBytesPerFrame[16] =
{
13, /* 4.75 */
14, /* 5.15 */
@@ -173,7 +174,7 @@ extern "C"
/* Number of data bytes in an encoder frame for each codec mode */
/* for IF2 output format */
- extern const Word16 If2EncBytesPerFrame[16] =
+ const Word16 If2EncBytesPerFrame[16] =
{
13, /* 4.75 */
14, /* 5.15 */
diff --git a/media/libstagefright/codecs/amrnb/enc/src/inter_36_tab.cpp b/media/libstagefright/codecs/amrnb/enc/src/inter_36_tab.cpp
index 27f33e9..c8d7b13 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/inter_36_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/inter_36_tab.cpp
@@ -123,6 +123,7 @@ terms listed above has been obtained from the copyright holder.
----------------------------------------------------------------------------*/
#include "typedef.h"
#include "cnst.h"
+#include "inter_36_tab.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -162,7 +163,7 @@ extern "C"
inter_3[k] = inter_6[2*k], 0 <= k <= 3*L_INTER_SRCH
*/
- extern const Word16 inter_6[FIR_SIZE] =
+ const Word16 inter_6[FIR_SIZE] =
{
29519,
28316, 24906, 19838, 13896, 7945, 2755,
diff --git a/media/libstagefright/codecs/amrnb/enc/src/lag_wind_tab.cpp b/media/libstagefright/codecs/amrnb/enc/src/lag_wind_tab.cpp
index 53889bb..b0f5b3a 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/lag_wind_tab.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/lag_wind_tab.cpp
@@ -138,6 +138,7 @@ terms listed above has been obtained from the copyright holder.
; INCLUDES
----------------------------------------------------------------------------*/
#include "typedef.h"
+#include "lag_wind_tab.h"
/*--------------------------------------------------------------------------*/
#ifdef __cplusplus
@@ -167,7 +168,7 @@ extern "C"
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
- extern const Word16 lag_h[10] =
+ const Word16 lag_h[10] =
{
32728,
32619,
@@ -181,7 +182,7 @@ extern "C"
29321
};
- extern const Word16 lag_l[10] =
+ const Word16 lag_l[10] =
{
11904,
17280,
diff --git a/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp b/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp
index dedf91a..d626de3 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/set_sign.cpp
@@ -552,10 +552,10 @@ void set_sign12k2(
else
{
*(p_sign--) = -32767; /* sign = -1 */
- cor = - (cor);
+ cor = negate(cor);
/* modify dn[] according to the fixed sign */
- dn[i] = - val;
+ dn[i] = negate(val);
}
*(p_en--) = cor;
diff --git a/media/libstagefright/codecs/amrnb/enc/src/ton_stab.cpp b/media/libstagefright/codecs/amrnb/enc/src/ton_stab.cpp
index 3c4494d..455a510 100644
--- a/media/libstagefright/codecs/amrnb/enc/src/ton_stab.cpp
+++ b/media/libstagefright/codecs/amrnb/enc/src/ton_stab.cpp
@@ -791,7 +791,8 @@ void update_gp_clipping(tonStabState *st, /* i/o : State struct */
)
{
OSCL_UNUSED_ARG(pOverflow);
- for (int i = 0; i < N_FRAME - 1; i++)
+ int i;
+ for (i = 0; i < N_FRAME - 1; i++)
{
st->gp[i] = st->gp[i+1];
}
diff --git a/media/libstagefright/codecs/amrwb/include/pvamrwbdecoder_api.h b/media/libstagefright/codecs/amrwb/include/pvamrwbdecoder_api.h
index 457c21f..eca5ae0 100644
--- a/media/libstagefright/codecs/amrwb/include/pvamrwbdecoder_api.h
+++ b/media/libstagefright/codecs/amrwb/include/pvamrwbdecoder_api.h
@@ -106,7 +106,7 @@ extern "C"
#define NUM_OF_MODES 10
- const int16 AMR_WB_COMPRESSED[NUM_OF_MODES] =
+ static const int16 AMR_WB_COMPRESSED[NUM_OF_MODES] =
{
NBBITS_7k,
NBBITS_9k,
diff --git a/media/libstagefright/codecs/amrwb/src/get_amr_wb_bits.cpp b/media/libstagefright/codecs/amrwb/src/get_amr_wb_bits.cpp
index d7287f3..b325e8f 100644
--- a/media/libstagefright/codecs/amrwb/src/get_amr_wb_bits.cpp
+++ b/media/libstagefright/codecs/amrwb/src/get_amr_wb_bits.cpp
@@ -119,8 +119,9 @@ int16 Serial_parm( /* Return the parameter */
)
{
int16 value = 0;
+ int16 i;
- for (int16 i = no_of_bits >> 1; i != 0; i--)
+ for (i = no_of_bits >> 1; i != 0; i--)
{
value <<= 2;
diff --git a/media/libstagefright/codecs/amrwb/src/homing_amr_wb_dec.cpp b/media/libstagefright/codecs/amrwb/src/homing_amr_wb_dec.cpp
index 59c6c0a..f032a08 100644
--- a/media/libstagefright/codecs/amrwb/src/homing_amr_wb_dec.cpp
+++ b/media/libstagefright/codecs/amrwb/src/homing_amr_wb_dec.cpp
@@ -134,7 +134,7 @@ extern "C"
; LOCAL STORE/BUFFER/POINTER DEFINITIONS
; Variable declaration - defined here and used outside this module
----------------------------------------------------------------------------*/
-const int16 prmnofsf[NUM_OF_SPMODES] =
+static const int16 prmnofsf[NUM_OF_SPMODES] =
{
63, 81, 100,
108, 116, 128,
@@ -142,21 +142,21 @@ const int16 prmnofsf[NUM_OF_SPMODES] =
};
-const int16 dfh_M7k[PRMN_7k] =
+static const int16 dfh_M7k[PRMN_7k] =
{
3168, 29954, 29213, 16121,
64, 13440, 30624, 16430,
19008
};
-const int16 dfh_M9k[PRMN_9k] =
+static const int16 dfh_M9k[PRMN_9k] =
{
3168, 31665, 9943, 9123,
15599, 4358, 20248, 2048,
17040, 27787, 16816, 13888
};
-const int16 dfh_M12k[PRMN_12k] =
+static const int16 dfh_M12k[PRMN_12k] =
{
3168, 31665, 9943, 9128,
3647, 8129, 30930, 27926,
@@ -165,7 +165,7 @@ const int16 dfh_M12k[PRMN_12k] =
13948
};
-const int16 dfh_M14k[PRMN_14k] =
+static const int16 dfh_M14k[PRMN_14k] =
{
3168, 31665, 9943, 9131,
24815, 655, 26616, 26764,
@@ -174,7 +174,7 @@ const int16 dfh_M14k[PRMN_14k] =
221, 20321, 17823
};
-const int16 dfh_M16k[PRMN_16k] =
+static const int16 dfh_M16k[PRMN_16k] =
{
3168, 31665, 9943, 9131,
24815, 700, 3824, 7271,
@@ -184,7 +184,7 @@ const int16 dfh_M16k[PRMN_16k] =
6759, 24576
};
-const int16 dfh_M18k[PRMN_18k] =
+static const int16 dfh_M18k[PRMN_18k] =
{
3168, 31665, 9943, 9135,
14787, 14423, 30477, 24927,
@@ -195,7 +195,7 @@ const int16 dfh_M18k[PRMN_18k] =
0
};
-const int16 dfh_M20k[PRMN_20k] =
+static const int16 dfh_M20k[PRMN_20k] =
{
3168, 31665, 9943, 9129,
8637, 31807, 24646, 736,
@@ -206,7 +206,7 @@ const int16 dfh_M20k[PRMN_20k] =
30249, 29123, 0
};
-const int16 dfh_M23k[PRMN_23k] =
+static const int16 dfh_M23k[PRMN_23k] =
{
3168, 31665, 9943, 9132,
16748, 3202, 28179, 16317,
@@ -218,7 +218,7 @@ const int16 dfh_M23k[PRMN_23k] =
23392, 26053, 31216
};
-const int16 dfh_M24k[PRMN_24k] =
+static const int16 dfh_M24k[PRMN_24k] =
{
3168, 31665, 9943, 9134,
24776, 5857, 18475, 28535,
diff --git a/media/libstagefright/codecs/amrwb/src/isp_isf.cpp b/media/libstagefright/codecs/amrwb/src/isp_isf.cpp
index 41db7e3..0552733 100644
--- a/media/libstagefright/codecs/amrwb/src/isp_isf.cpp
+++ b/media/libstagefright/codecs/amrwb/src/isp_isf.cpp
@@ -108,7 +108,7 @@ terms listed above has been obtained from the copyright holder.
/* table of cos(x) in Q15 */
-const int16 table[129] =
+static const int16 table[129] =
{
32767,
32758, 32729, 32679, 32610, 32522, 32413, 32286, 32138,
diff --git a/media/libstagefright/codecs/amrwb/src/oversamp_12k8_to_16k.cpp b/media/libstagefright/codecs/amrwb/src/oversamp_12k8_to_16k.cpp
index 143c26e..806851e 100644
--- a/media/libstagefright/codecs/amrwb/src/oversamp_12k8_to_16k.cpp
+++ b/media/libstagefright/codecs/amrwb/src/oversamp_12k8_to_16k.cpp
@@ -240,11 +240,11 @@ void AmrWbUp_samp(
{
int32 i;
- int16 frac;
+ int16 frac, j;
int16 * pt_sig_u = sig_u;
frac = 1;
- for (int16 j = 0; j < L_frame; j++)
+ for (j = 0; j < L_frame; j++)
{
i = ((int32)j * INV_FAC5) >> 13; /* integer part = pos * 1/5 */
@@ -337,6 +337,6 @@ int16 AmrWbInterpol( /* return result of interpolation */
L_sum = shl_int32(L_sum, 2); /* saturation can occur here */
- return ((int16(L_sum >> 16)));
+ return ((int16)(L_sum >> 16));
}
diff --git a/media/libstagefright/codecs/amrwb/src/phase_dispersion.cpp b/media/libstagefright/codecs/amrwb/src/phase_dispersion.cpp
index f90a534..7b08a40 100644
--- a/media/libstagefright/codecs/amrwb/src/phase_dispersion.cpp
+++ b/media/libstagefright/codecs/amrwb/src/phase_dispersion.cpp
@@ -109,7 +109,7 @@ terms listed above has been obtained from the copyright holder.
/* impulse response with phase dispersion */
/* 2.0 - 6.4 kHz phase dispersion */
-const int16 ph_imp_low[L_SUBFR] =
+static const int16 ph_imp_low[L_SUBFR] =
{
20182, 9693, 3270, -3437, 2864, -5240, 1589, -1357,
600, 3893, -1497, -698, 1203, -5249, 1199, 5371,
@@ -122,7 +122,7 @@ const int16 ph_imp_low[L_SUBFR] =
};
/* 3.2 - 6.4 kHz phase dispersion */
-const int16 ph_imp_mid[L_SUBFR] =
+static const int16 ph_imp_mid[L_SUBFR] =
{
24098, 10460, -5263, -763, 2048, -927, 1753, -3323,
2212, 652, -2146, 2487, -3539, 4109, -2107, -374,
diff --git a/media/libstagefright/codecs/amrwbenc/inc/isp_isf.tab b/media/libstagefright/codecs/amrwbenc/inc/isp_isf.tab
index 97c3b68..865eea0 100644
--- a/media/libstagefright/codecs/amrwbenc/inc/isp_isf.tab
+++ b/media/libstagefright/codecs/amrwbenc/inc/isp_isf.tab
@@ -21,7 +21,7 @@
/* table of cos(x) in Q15 */
-const static Word16 table[129] = {
+static const Word16 table[129] = {
32767,
32758, 32729, 32679, 32610, 32522, 32413, 32286, 32138,
31972, 31786, 31581, 31357, 31114, 30853, 30572, 30274,
@@ -42,7 +42,7 @@ const static Word16 table[129] = {
/* slope in Q11 used to compute y = acos(x) */
-const static Word16 slope[128] = {
+static const Word16 slope[128] = {
-26214, -9039, -5243, -3799, -2979, -2405, -2064, -1771,
-1579, -1409, -1279, -1170, -1079, -1004, -933, -880,
-827, -783, -743, -708, -676, -647, -621, -599,
diff --git a/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c b/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c
index 0f4d689..ea9da52 100644
--- a/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c
+++ b/media/libstagefright/codecs/amrwbenc/src/voAMRWBEnc.c
@@ -1702,7 +1702,7 @@ VO_U32 VO_API voAMRWB_SetInputData(
gData = (Coder_State *)hCodec;
stream = gData->stream;
- if(NULL == pInput || NULL == pInput->Buffer || 0 > pInput->Length)
+ if(NULL == pInput || NULL == pInput->Buffer)
{
return VO_ERR_INVALID_ARG;
}
diff --git a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
index 74fe478..b3c350f 100644
--- a/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
+++ b/media/libstagefright/codecs/m4v_h263/dec/src/vop.cpp
@@ -1041,7 +1041,7 @@ PV_STATUS DecodeShortHeader(VideoDecData *video, Vop *currVop)
/* Marker Bit */
if (!BitstreamRead1Bits(stream))
{
- mp4dec_log("DecodeShortHeader(): Market bit wrong.\n");
+ mp4dec_log("DecodeShortHeader(): Marker bit wrong.\n");
status = PV_FAIL;
goto return_point;
}
diff --git a/media/libstagefright/colorconversion/SoftwareRenderer.cpp b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
index e892f92..059d6b9 100644
--- a/media/libstagefright/colorconversion/SoftwareRenderer.cpp
+++ b/media/libstagefright/colorconversion/SoftwareRenderer.cpp
@@ -19,12 +19,9 @@
#include "../include/SoftwareRenderer.h"
-#include <binder/MemoryHeapBase.h>
-#include <binder/MemoryHeapPmem.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/MetaData.h>
-#include <surfaceflinger/Surface.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
#include <ui/GraphicBufferMapper.h>
#include <gui/ISurfaceTexture.h>
diff --git a/media/libstagefright/foundation/AMessage.cpp b/media/libstagefright/foundation/AMessage.cpp
index 0a6776e..9a00186 100644
--- a/media/libstagefright/foundation/AMessage.cpp
+++ b/media/libstagefright/foundation/AMessage.cpp
@@ -19,6 +19,7 @@
#include <ctype.h>
#include "AAtomizer.h"
+#include "ABuffer.h"
#include "ADebug.h"
#include "ALooperRoster.h"
#include "AString.h"
@@ -157,14 +158,23 @@ void AMessage::setString(
item->u.stringValue = new AString(s, len < 0 ? strlen(s) : len);
}
-void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+void AMessage::setObjectInternal(
+ const char *name, const sp<RefBase> &obj, Type type) {
Item *item = allocateItem(name);
- item->mType = kTypeObject;
+ item->mType = type;
if (obj != NULL) { obj->incStrong(this); }
item->u.refValue = obj.get();
}
+void AMessage::setObject(const char *name, const sp<RefBase> &obj) {
+ setObjectInternal(name, obj, kTypeObject);
+}
+
+void AMessage::setBuffer(const char *name, const sp<ABuffer> &buffer) {
+ setObjectInternal(name, sp<RefBase>(buffer), kTypeBuffer);
+}
+
void AMessage::setMessage(const char *name, const sp<AMessage> &obj) {
Item *item = allocateItem(name);
item->mType = kTypeMessage;
@@ -203,6 +213,15 @@ bool AMessage::findObject(const char *name, sp<RefBase> *obj) const {
return false;
}
+bool AMessage::findBuffer(const char *name, sp<ABuffer> *buf) const {
+ const Item *item = findItem(name, kTypeBuffer);
+ if (item) {
+ *buf = (ABuffer *)(item->u.refValue);
+ return true;
+ }
+ return false;
+}
+
bool AMessage::findMessage(const char *name, sp<AMessage> *obj) const {
const Item *item = findItem(name, kTypeMessage);
if (item) {
@@ -542,4 +561,20 @@ void AMessage::writeToParcel(Parcel *parcel) const {
}
}
+size_t AMessage::countEntries() const {
+ return mNumItems;
+}
+
+const char *AMessage::getEntryNameAt(size_t index, Type *type) const {
+ if (index >= mNumItems) {
+ *type = kTypeInt32;
+
+ return NULL;
+ }
+
+ *type = mItems[index].mType;
+
+ return mItems[index].mName;
+}
+
} // namespace android
diff --git a/media/libstagefright/include/SoftwareRenderer.h b/media/libstagefright/include/SoftwareRenderer.h
index 8f2ea95..7ab0042 100644
--- a/media/libstagefright/include/SoftwareRenderer.h
+++ b/media/libstagefright/include/SoftwareRenderer.h
@@ -20,7 +20,7 @@
#include <media/stagefright/ColorConverter.h>
#include <utils/RefBase.h>
-#include <ui/android_native_buffer.h>
+#include <system/window.h>
namespace android {
diff --git a/media/libstagefright/rtsp/AAMRAssembler.cpp b/media/libstagefright/rtsp/AAMRAssembler.cpp
index 9d72b1f..fb8abc5 100644
--- a/media/libstagefright/rtsp/AAMRAssembler.cpp
+++ b/media/libstagefright/rtsp/AAMRAssembler.cpp
@@ -211,7 +211,7 @@ ARTPAssembler::AssemblyStatus AAMRAssembler::addPacket(
}
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
queue->erase(queue->begin());
diff --git a/media/libstagefright/rtsp/AAVCAssembler.cpp b/media/libstagefright/rtsp/AAVCAssembler.cpp
index ed8b1df..7ea132e 100644
--- a/media/libstagefright/rtsp/AAVCAssembler.cpp
+++ b/media/libstagefright/rtsp/AAVCAssembler.cpp
@@ -345,7 +345,7 @@ void AAVCAssembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/AH263Assembler.cpp b/media/libstagefright/rtsp/AH263Assembler.cpp
index 498295c..ded70fa 100644
--- a/media/libstagefright/rtsp/AH263Assembler.cpp
+++ b/media/libstagefright/rtsp/AH263Assembler.cpp
@@ -166,7 +166,7 @@ void AH263Assembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
index b0c7007..24c2f30 100644
--- a/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4AudioAssembler.cpp
@@ -571,7 +571,7 @@ void AMPEG4AudioAssembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
index 2f2e2c2..687d72b 100644
--- a/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
+++ b/media/libstagefright/rtsp/AMPEG4ElementaryAssembler.cpp
@@ -368,7 +368,7 @@ void AMPEG4ElementaryAssembler::submitAccessUnit() {
mAccessUnitDamaged = false;
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", accessUnit);
+ msg->setBuffer("access-unit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/rtsp/ARTPConnection.cpp b/media/libstagefright/rtsp/ARTPConnection.cpp
index 8c9dd8d..44988a3 100644
--- a/media/libstagefright/rtsp/ARTPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTPConnection.cpp
@@ -639,7 +639,7 @@ sp<ARTPSource> ARTPConnection::findSource(StreamInfo *info, uint32_t srcId) {
void ARTPConnection::injectPacket(int index, const sp<ABuffer> &buffer) {
sp<AMessage> msg = new AMessage(kWhatInjectPacket, id());
msg->setInt32("index", index);
- msg->setObject("buffer", buffer);
+ msg->setBuffer("buffer", buffer);
msg->post();
}
@@ -647,10 +647,8 @@ void ARTPConnection::onInjectPacket(const sp<AMessage> &msg) {
int32_t index;
CHECK(msg->findInt32("index", &index));
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
-
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
List<StreamInfo>::iterator it = mStreams.begin();
while (it != mStreams.end()
diff --git a/media/libstagefright/rtsp/ARTPSession.cpp b/media/libstagefright/rtsp/ARTPSession.cpp
index 7a05b88..ba4e33c 100644
--- a/media/libstagefright/rtsp/ARTPSession.cpp
+++ b/media/libstagefright/rtsp/ARTPSession.cpp
@@ -145,10 +145,8 @@ void ARTPSession::onMessageReceived(const sp<AMessage> &msg) {
break;
}
- sp<RefBase> obj;
- CHECK(msg->findObject("access-unit", &obj));
-
- sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("access-unit", &accessUnit));
uint64_t ntpTime;
CHECK(accessUnit->meta()->findInt64(
diff --git a/media/libstagefright/rtsp/ARTSPConnection.cpp b/media/libstagefright/rtsp/ARTSPConnection.cpp
index 80a010e..539a888 100644
--- a/media/libstagefright/rtsp/ARTSPConnection.cpp
+++ b/media/libstagefright/rtsp/ARTSPConnection.cpp
@@ -612,7 +612,7 @@ bool ARTSPConnection::receiveRTSPReponse() {
if (mObserveBinaryMessage != NULL) {
sp<AMessage> notify = mObserveBinaryMessage->dup();
- notify->setObject("buffer", buffer);
+ notify->setBuffer("buffer", buffer);
notify->post();
} else {
ALOGW("received binary data, but no one cares.");
diff --git a/media/libstagefright/rtsp/ARawAudioAssembler.cpp b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
index 98bee82..0da5dd2 100644
--- a/media/libstagefright/rtsp/ARawAudioAssembler.cpp
+++ b/media/libstagefright/rtsp/ARawAudioAssembler.cpp
@@ -94,7 +94,7 @@ ARTPAssembler::AssemblyStatus ARawAudioAssembler::addPacket(
}
sp<AMessage> msg = mNotifyMsg->dup();
- msg->setObject("access-unit", buffer);
+ msg->setBuffer("access-unit", buffer);
msg->post();
queue->erase(queue->begin());
diff --git a/media/libstagefright/rtsp/MyHandler.h b/media/libstagefright/rtsp/MyHandler.h
index 9a7dd70..deee30f 100644
--- a/media/libstagefright/rtsp/MyHandler.h
+++ b/media/libstagefright/rtsp/MyHandler.h
@@ -857,10 +857,8 @@ struct MyHandler : public AHandler {
return;
}
- sp<RefBase> obj;
- CHECK(msg->findObject("access-unit", &obj));
-
- sp<ABuffer> accessUnit = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> accessUnit;
+ CHECK(msg->findBuffer("access-unit", &accessUnit));
uint32_t seqNum = (uint32_t)accessUnit->int32Data();
@@ -1005,9 +1003,8 @@ struct MyHandler : public AHandler {
case 'biny':
{
- sp<RefBase> obj;
- CHECK(msg->findObject("buffer", &obj));
- sp<ABuffer> buffer = static_cast<ABuffer *>(obj.get());
+ sp<ABuffer> buffer;
+ CHECK(msg->findBuffer("buffer", &buffer));
int32_t index;
CHECK(buffer->meta()->findInt32("index", &index));
@@ -1488,7 +1485,7 @@ private:
sp<AMessage> msg = mNotify->dup();
msg->setInt32("what", kWhatAccessUnit);
msg->setSize("trackIndex", trackIndex);
- msg->setObject("accessUnit", accessUnit);
+ msg->setBuffer("accessUnit", accessUnit);
msg->post();
}
diff --git a/media/libstagefright/tests/SurfaceMediaSource_test.cpp b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
index d7cec04..3dcd9fc 100644
--- a/media/libstagefright/tests/SurfaceMediaSource_test.cpp
+++ b/media/libstagefright/tests/SurfaceMediaSource_test.cpp
@@ -26,11 +26,11 @@
#include <media/stagefright/SurfaceMediaSource.h>
#include <media/mediarecorder.h>
-#include <gui/SurfaceTextureClient.h>
#include <ui/GraphicBuffer.h>
-#include <surfaceflinger/ISurfaceComposer.h>
-#include <surfaceflinger/Surface.h>
-#include <surfaceflinger/SurfaceComposerClient.h>
+#include <gui/SurfaceTextureClient.h>
+#include <gui/ISurfaceComposer.h>
+#include <gui/Surface.h>
+#include <gui/SurfaceComposerClient.h>
#include <binder/ProcessState.h>
#include <ui/FramebufferNativeWindow.h>
diff --git a/media/libstagefright/timedtext/Android.mk b/media/libstagefright/timedtext/Android.mk
index 8b23dee..d2d5f7b 100644
--- a/media/libstagefright/timedtext/Android.mk
+++ b/media/libstagefright/timedtext/Android.mk
@@ -4,7 +4,7 @@ include $(CLEAR_VARS)
LOCAL_SRC_FILES:= \
TextDescriptions.cpp \
TimedTextDriver.cpp \
- TimedTextInBandSource.cpp \
+ TimedText3GPPSource.cpp \
TimedTextSource.cpp \
TimedTextSRTSource.cpp \
TimedTextPlayer.cpp
@@ -12,8 +12,8 @@ LOCAL_SRC_FILES:= \
LOCAL_CFLAGS += -Wno-multichar
LOCAL_C_INCLUDES:= \
$(JNI_H_INCLUDE) \
- $(TOP)/frameworks/base/media/libstagefright \
- $(TOP)/frameworks/base/include/media/stagefright/openmax
+ $(TOP)/frameworks/base/include/media/stagefright/timedtext \
+ $(TOP)/frameworks/base/media/libstagefright
LOCAL_MODULE:= libstagefright_timedtext
diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.cpp b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
index afb73fb..4a3bfd3 100644
--- a/media/libstagefright/timedtext/TimedTextInBandSource.cpp
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.cpp
@@ -15,7 +15,7 @@
*/
//#define LOG_NDEBUG 0
-#define LOG_TAG "TimedTextInBandSource"
+#define LOG_TAG "TimedText3GPPSource"
#include <utils/Log.h>
#include <binder/Parcel.h>
@@ -26,19 +26,19 @@
#include <media/stagefright/MediaSource.h>
#include <media/stagefright/MetaData.h>
-#include "TimedTextInBandSource.h"
+#include "TimedText3GPPSource.h"
#include "TextDescriptions.h"
namespace android {
-TimedTextInBandSource::TimedTextInBandSource(const sp<MediaSource>& mediaSource)
+TimedText3GPPSource::TimedText3GPPSource(const sp<MediaSource>& mediaSource)
: mSource(mediaSource) {
}
-TimedTextInBandSource::~TimedTextInBandSource() {
+TimedText3GPPSource::~TimedText3GPPSource() {
}
-status_t TimedTextInBandSource::read(
+status_t TimedText3GPPSource::read(
int64_t *timeUs, Parcel *parcel, const MediaSource::ReadOptions *options) {
MediaBuffer *textBuffer = NULL;
status_t err = mSource->read(&textBuffer, options);
@@ -60,7 +60,7 @@ status_t TimedTextInBandSource::read(
// text style for the string of text. These descriptions are present only
// if they are needed. This method is used to extract the modifier
// description and append it at the end of the text.
-status_t TimedTextInBandSource::extractAndAppendLocalDescriptions(
+status_t TimedText3GPPSource::extractAndAppendLocalDescriptions(
int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel) {
const void *data;
size_t size = 0;
@@ -68,51 +68,46 @@ status_t TimedTextInBandSource::extractAndAppendLocalDescriptions(
const char *mime;
CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+ CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
- if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
- data = textBuffer->data();
- size = textBuffer->size();
-
- if (size > 0) {
- parcel->freeData();
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
- }
- return OK;
+ data = textBuffer->data();
+ size = textBuffer->size();
+
+ if (size > 0) {
+ parcel->freeData();
+ flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, timeUs / 1000, parcel);
}
- return ERROR_UNSUPPORTED;
+ return OK;
}
// To extract and send the global text descriptions for all the text samples
// in the text track or text file.
// TODO: send error message to application via notifyListener()...?
-status_t TimedTextInBandSource::extractGlobalDescriptions(Parcel *parcel) {
+status_t TimedText3GPPSource::extractGlobalDescriptions(Parcel *parcel) {
const void *data;
size_t size = 0;
int32_t flag = TextDescriptions::GLOBAL_DESCRIPTIONS;
const char *mime;
CHECK(mSource->getFormat()->findCString(kKeyMIMEType, &mime));
+ CHECK(strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0);
+
+ uint32_t type;
+ // get the 'tx3g' box content. This box contains the text descriptions
+ // used to render the text track
+ if (!mSource->getFormat()->findData(
+ kKeyTextFormatData, &type, &data, &size)) {
+ return ERROR_MALFORMED;
+ }
- // support 3GPP only for now
- if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
- uint32_t type;
- // get the 'tx3g' box content. This box contains the text descriptions
- // used to render the text track
- if (!mSource->getFormat()->findData(
- kKeyTextFormatData, &type, &data, &size)) {
- return ERROR_MALFORMED;
- }
-
- if (size > 0) {
- flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
- return TextDescriptions::getParcelOfDescriptions(
- (const uint8_t *)data, size, flag, 0, parcel);
- }
- return OK;
+ if (size > 0) {
+ flag |= TextDescriptions::IN_BAND_TEXT_3GPP;
+ return TextDescriptions::getParcelOfDescriptions(
+ (const uint8_t *)data, size, flag, 0, parcel);
}
- return ERROR_UNSUPPORTED;
+ return OK;
}
} // namespace android
diff --git a/media/libstagefright/timedtext/TimedTextInBandSource.h b/media/libstagefright/timedtext/TimedText3GPPSource.h
index 26e5737..cb7e47c 100644
--- a/media/libstagefright/timedtext/TimedTextInBandSource.h
+++ b/media/libstagefright/timedtext/TimedText3GPPSource.h
@@ -14,8 +14,8 @@
* limitations under the License.
*/
-#ifndef TIMED_TEXT_IN_BAND_SOURCE_H_
-#define TIMED_TEXT_IN_BAND_SOURCE_H_
+#ifndef TIMED_TEXT_3GPP_SOURCE_H_
+#define TIMED_TEXT_3GPP_SOURCE_H_
#include <media/stagefright/MediaErrors.h>
#include <media/stagefright/MediaSource.h>
@@ -27,9 +27,9 @@ namespace android {
class MediaBuffer;
class Parcel;
-class TimedTextInBandSource : public TimedTextSource {
+class TimedText3GPPSource : public TimedTextSource {
public:
- TimedTextInBandSource(const sp<MediaSource>& mediaSource);
+ TimedText3GPPSource(const sp<MediaSource>& mediaSource);
virtual status_t start() { return mSource->start(); }
virtual status_t stop() { return mSource->stop(); }
virtual status_t read(
@@ -39,7 +39,7 @@ class TimedTextInBandSource : public TimedTextSource {
virtual status_t extractGlobalDescriptions(Parcel *parcel);
protected:
- virtual ~TimedTextInBandSource();
+ virtual ~TimedText3GPPSource();
private:
sp<MediaSource> mSource;
@@ -47,9 +47,9 @@ class TimedTextInBandSource : public TimedTextSource {
status_t extractAndAppendLocalDescriptions(
int64_t timeUs, const MediaBuffer *textBuffer, Parcel *parcel);
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextInBandSource);
+ DISALLOW_EVIL_CONSTRUCTORS(TimedText3GPPSource);
};
} // namespace android
-#endif // TIMED_TEXT_IN_BAND_SOURCE_H_
+#endif // TIMED_TEXT_3GPP_SOURCE_H_
diff --git a/media/libstagefright/timedtext/TimedTextDriver.cpp b/media/libstagefright/timedtext/TimedTextDriver.cpp
index 9ec9415..c70870e 100644
--- a/media/libstagefright/timedtext/TimedTextDriver.cpp
+++ b/media/libstagefright/timedtext/TimedTextDriver.cpp
@@ -27,8 +27,7 @@
#include <media/stagefright/Utils.h>
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/ALooper.h>
-
-#include "TimedTextDriver.h"
+#include <media/stagefright/timedtext/TimedTextDriver.h>
#include "TextDescriptions.h"
#include "TimedTextPlayer.h"
diff --git a/media/libstagefright/timedtext/TimedTextDriver.h b/media/libstagefright/timedtext/TimedTextDriver.h
deleted file mode 100644
index efedb6e..0000000
--- a/media/libstagefright/timedtext/TimedTextDriver.h
+++ /dev/null
@@ -1,81 +0,0 @@
-/*
- * Copyright (C) 2012 The Android Open Source Project
- *
- * Licensed under the Apache License, Version 2.0 (the "License");
- * you may not use this file except in compliance with the License.
- * You may obtain a copy of the License at
- *
- * http://www.apache.org/licenses/LICENSE-2.0
- *
- * Unless required by applicable law or agreed to in writing, software
- * distributed under the License is distributed on an "AS IS" BASIS,
- * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
- * See the License for the specific language governing permissions and
- * limitations under the License.
- */
-
-#ifndef TIMED_TEXT_DRIVER_H_
-#define TIMED_TEXT_DRIVER_H_
-
-#include <media/stagefright/foundation/ABase.h> // for DISALLOW_* macro
-#include <utils/Errors.h> // for status_t
-#include <utils/RefBase.h>
-#include <utils/threads.h>
-
-namespace android {
-
-class ALooper;
-class MediaPlayerBase;
-class MediaSource;
-class Parcel;
-class TimedTextPlayer;
-class TimedTextSource;
-
-class TimedTextDriver {
-public:
- TimedTextDriver(const wp<MediaPlayerBase> &listener);
-
- ~TimedTextDriver();
-
- // TODO: pause-resume pair seems equivalent to stop-start pair.
- // Check if it is replaceable with stop-start.
- status_t start();
- status_t stop();
- status_t pause();
- status_t resume();
-
- status_t seekToAsync(int64_t timeUs);
-
- status_t addInBandTextSource(const sp<MediaSource>& source);
- status_t addOutOfBandTextSource(const Parcel &request);
-
- status_t setTimedTextTrackIndex(int32_t index);
-
-private:
- Mutex mLock;
-
- enum State {
- UNINITIALIZED,
- STOPPED,
- PLAYING,
- PAUSED,
- };
-
- sp<ALooper> mLooper;
- sp<TimedTextPlayer> mPlayer;
- wp<MediaPlayerBase> mListener;
-
- // Variables to be guarded by mLock.
- State mState;
- Vector<sp<TimedTextSource> > mTextInBandVector;
- Vector<sp<TimedTextSource> > mTextOutOfBandVector;
- // -- End of variables to be guarded by mLock
-
- status_t setTimedTextTrackIndex_l(int32_t index);
-
- DISALLOW_EVIL_CONSTRUCTORS(TimedTextDriver);
-};
-
-} // namespace android
-
-#endif // TIMED_TEXT_DRIVER_H_
diff --git a/media/libstagefright/timedtext/TimedTextPlayer.cpp b/media/libstagefright/timedtext/TimedTextPlayer.cpp
index bf7cbf6..bda7b46 100644
--- a/media/libstagefright/timedtext/TimedTextPlayer.cpp
+++ b/media/libstagefright/timedtext/TimedTextPlayer.cpp
@@ -20,12 +20,12 @@
#include <media/stagefright/foundation/ADebug.h>
#include <media/stagefright/foundation/AMessage.h>
+#include <media/stagefright/timedtext/TimedTextDriver.h>
#include <media/stagefright/MediaErrors.h>
#include <media/MediaPlayerInterface.h>
#include "TimedTextPlayer.h"
-#include "TimedTextDriver.h"
#include "TimedTextSource.h"
namespace android {
diff --git a/media/libstagefright/timedtext/TimedTextSource.cpp b/media/libstagefright/timedtext/TimedTextSource.cpp
index 9efe67c..ffbe1c3 100644
--- a/media/libstagefright/timedtext/TimedTextSource.cpp
+++ b/media/libstagefright/timedtext/TimedTextSource.cpp
@@ -18,12 +18,15 @@
#define LOG_TAG "TimedTextSource"
#include <utils/Log.h>
+#include <media/stagefright/foundation/ADebug.h> // CHECK_XX macro
#include <media/stagefright/DataSource.h>
+#include <media/stagefright/MediaDefs.h> // for MEDIA_MIMETYPE_xxx
#include <media/stagefright/MediaSource.h>
+#include <media/stagefright/MetaData.h>
#include "TimedTextSource.h"
-#include "TimedTextInBandSource.h"
+#include "TimedText3GPPSource.h"
#include "TimedTextSRTSource.h"
namespace android {
@@ -31,7 +34,13 @@ namespace android {
// static
sp<TimedTextSource> TimedTextSource::CreateTimedTextSource(
const sp<MediaSource>& mediaSource) {
- return new TimedTextInBandSource(mediaSource);
+ const char *mime;
+ CHECK(mediaSource->getFormat()->findCString(kKeyMIMEType, &mime));
+ if (strcasecmp(mime, MEDIA_MIMETYPE_TEXT_3GPP) == 0) {
+ return new TimedText3GPPSource(mediaSource);
+ }
+ ALOGE("Unsupported mime type for subtitle. : %s", mime);
+ return NULL;
}
// static
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
index e5ecd5c..95e7b5e 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/MediaRecorderStressTestRunner.java
@@ -44,7 +44,9 @@ public class MediaRecorderStressTestRunner extends InstrumentationTestRunner {
public static int mVideoHeight = profile.videoFrameHeight;
public static int mBitRate = profile.videoBitRate;
public static boolean mRemoveVideo = true;
- public static int mDuration = 10000;
+ public static int mDuration = 10 * 1000; // 10 seconds
+ public static int mTimeLapseDuration = 180 * 1000; // 3 minutes
+ public static double mCaptureRate = 0.5; // 2 sec timelapse interval
@Override
public TestSuite getAllTests() {
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
index 6f1959c..d15a535 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/performance/VideoEditorPerformance.java
@@ -196,7 +196,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_001
@LargeTest
public void testPerformanceAddRemoveVideoItem() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH +
@@ -241,7 +240,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_002
@LargeTest
public void testPerformanceAddRemoveImageItem() throws Exception {
final String imageItemFileName = INPUT_FILE_PATH + "IMG_1600x1200.jpg";
@@ -280,7 +278,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_003
@LargeTest
public void testPerformanceAddRemoveTransition() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -360,7 +357,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_004
@LargeTest
public void testPerformanceExport() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -541,7 +537,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_005
@LargeTest
public void testPerformanceThumbnailVideoItem() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -574,7 +569,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_006
@LargeTest
public void testPerformanceOverlayVideoItem() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -629,7 +623,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_007
@LargeTest
public void testPerformanceVideoItemProperties() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -688,7 +681,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_008
@LargeTest
public void testPerformanceGeneratePreviewWithTransitions()
throws Exception {
@@ -740,7 +732,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_009
@LargeTest
public void testPerformanceWithKenBurn() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH +
@@ -795,7 +786,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_010
@LargeTest
public void testPerformanceEffectOverlappingTransition() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -864,7 +854,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_011
@LargeTest
public void testPerformanceTransitionWithEffectOverlapping() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -994,7 +983,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_014
@LargeTest
public void testPerformanceWithAudioTrack() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -1049,7 +1037,6 @@ public class VideoEditorPerformance extends
*
* @throws Exception
*/
- // TODO : remove PRF_015
@LargeTest
public void testPerformanceAddRemoveImageItem640x480() throws Exception {
final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
index e6177ba..5e649e0 100644
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/MediaRecorderStressTest.java
@@ -22,11 +22,13 @@ import com.android.mediaframeworktest.MediaFrameworkTest;
import java.io.BufferedWriter;
import java.io.File;
import java.io.FileWriter;
+import java.io.IOException;
import java.io.Writer;
import java.util.concurrent.Semaphore;
import java.util.concurrent.TimeUnit;
import android.hardware.Camera;
+import android.media.CamcorderProfile;
import android.media.MediaPlayer;
import android.media.MediaRecorder;
import android.os.Handler;
@@ -39,21 +41,21 @@ import com.android.mediaframeworktest.MediaRecorderStressTestRunner;
/**
* Junit / Instrumentation test case for the media player api
-
- */
-public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
-
-
+ */
+public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<MediaFrameworkTest> {
+
private String TAG = "MediaRecorderStressTest";
private MediaRecorder mRecorder;
private Camera mCamera;
-
+
private static final int NUMBER_OF_CAMERA_STRESS_LOOPS = 100;
private static final int NUMBER_OF_RECORDER_STRESS_LOOPS = 100;
private static final int NUMBER_OF_RECORDERANDPLAY_STRESS_LOOPS = 50;
private static final int NUMBER_OF_SWTICHING_LOOPS_BW_CAMERA_AND_RECORDER = 200;
- private static final long WAIT_TIME_CAMERA_TEST = 3000; // 3 second
- private static final long WAIT_TIME_RECORDER_TEST = 6000; // 6 second
+ private static final int NUMBER_OF_TIME_LAPSE_LOOPS = 25;
+ private static final int TIME_LAPSE_PLAYBACK_WAIT_TIME = 5* 1000; // 5 seconds
+ private static final long WAIT_TIME_CAMERA_TEST = 3 * 1000; // 3 seconds
+ private static final long WAIT_TIME_RECORDER_TEST = 6 * 1000; // 6 seconds
private static final String OUTPUT_FILE = "/sdcard/temp";
private static final String OUTPUT_FILE_EXT = ".3gp";
private static final String MEDIA_STRESS_OUTPUT =
@@ -61,7 +63,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
private final CameraErrorCallback mCameraErrorCallback = new CameraErrorCallback();
private final RecorderErrorCallback mRecorderErrorCallback = new RecorderErrorCallback();
- private final static int WAIT_TIMEOUT = 10000;
+ private final static int WAIT_TIMEOUT = 10 * 1000; // 10 seconds
private Thread mLooperThread;
private Handler mHandler;
@@ -306,7 +308,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
}
}
- public void removeRecodedVideo(String filename){
+ public void removeRecordedVideo(String filename){
File video = new File(filename);
Log.v(TAG, "remove recorded video " + filename);
video.delete();
@@ -363,6 +365,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
mRecorder.setVideoSize(video_width, video_height);
mRecorder.setVideoEncoder(video_encoder);
mRecorder.setAudioEncoder(audio_encoder);
+ mRecorder.setVideoEncodingBitRate(bit_rate);
Log.v(TAG, "mediaRecorder setPreview");
mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
mRecorder.prepare();
@@ -381,7 +384,7 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
mp.release();
validateRecordedVideo(filename);
if (remove_video) {
- removeRecodedVideo(filename);
+ removeRecordedVideo(filename);
}
output.write(", " + i);
}
@@ -392,4 +395,90 @@ public class MediaRecorderStressTest extends ActivityInstrumentationTestCase2<Me
output.write("\n\n");
output.close();
}
+
+ // Test case for stressing time lapse
+ @LargeTest
+ public void testStressTimeLapse() throws Exception {
+ SurfaceHolder mSurfaceHolder;
+ mSurfaceHolder = MediaFrameworkTest.mSurfaceView.getHolder();
+ int record_duration = MediaRecorderStressTestRunner.mTimeLapseDuration;
+ boolean remove_video = MediaRecorderStressTestRunner.mRemoveVideo;
+ double captureRate = MediaRecorderStressTestRunner.mCaptureRate;
+ String filename;
+ File stressOutFile = new File(MEDIA_STRESS_OUTPUT);
+ Writer output = new BufferedWriter(new FileWriter(stressOutFile, true));
+ output.write("Start camera time lapse stress:\n");
+ output.write("Total number of loops: " + NUMBER_OF_TIME_LAPSE_LOOPS + "\n");
+
+ try {
+ output.write("No of loop: ");
+ for (int i = 0; i < NUMBER_OF_TIME_LAPSE_LOOPS; i++) {
+ filename = OUTPUT_FILE + i + OUTPUT_FILE_EXT;
+ Log.v(TAG, filename);
+ runOnLooper(new Runnable() {
+ @Override
+ public void run() {
+ mRecorder = new MediaRecorder();
+ }
+ });
+
+ // Set callback
+ mRecorder.setOnErrorListener(mRecorderErrorCallback);
+
+ // Set video source
+ mRecorder.setVideoSource(MediaRecorder.VideoSource.CAMERA);
+
+ // Set camcorder profile for time lapse
+ CamcorderProfile profile =
+ CamcorderProfile.get(CamcorderProfile.QUALITY_TIME_LAPSE_HIGH);
+ mRecorder.setProfile(profile);
+
+ // Set the timelapse setting; 0.1 = 10 sec timelapse, 0.5 = 2 sec timelapse, etc.
+ // http://developer.android.com/guide/topics/media/camera.html#time-lapse-video
+ mRecorder.setCaptureRate(captureRate);
+
+ // Set output file
+ mRecorder.setOutputFile(filename);
+
+ // Set the preview display
+ Log.v(TAG, "mediaRecorder setPreviewDisplay");
+ mRecorder.setPreviewDisplay(mSurfaceHolder.getSurface());
+
+ mRecorder.prepare();
+ mRecorder.start();
+ Thread.sleep(record_duration);
+ Log.v(TAG, "Before stop");
+ mRecorder.stop();
+ mRecorder.release();
+
+ // Start the playback
+ MediaPlayer mp = new MediaPlayer();
+ mp.setDataSource(filename);
+ mp.setDisplay(mSurfaceHolder);
+ mp.prepare();
+ mp.start();
+ Thread.sleep(TIME_LAPSE_PLAYBACK_WAIT_TIME);
+ mp.release();
+ validateRecordedVideo(filename);
+ if(remove_video) {
+ removeRecordedVideo(filename);
+ }
+ output.write(", " + i);
+ }
+ }
+ catch (IllegalStateException e) {
+ assertTrue("Camera time lapse stress test IllegalStateException", false);
+ Log.v(TAG, e.toString());
+ }
+ catch (IOException e) {
+ assertTrue("Camera time lapse stress test IOException", false);
+ Log.v(TAG, e.toString());
+ }
+ catch (Exception e) {
+ assertTrue("Camera time lapse stress test Exception", false);
+ Log.v(TAG, e.toString());
+ }
+ output.write("\n\n");
+ output.close();
+ }
}
diff --git a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java
index 4d30784..7784c7b 100755
--- a/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java
+++ b/media/tests/MediaFrameworkTest/src/com/android/mediaframeworktest/stress/VideoEditorStressTest.java
@@ -167,7 +167,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_001
@LargeTest
public void testStressAddRemoveVideoItem() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -241,7 +240,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_002
@LargeTest
public void testStressAddRemoveImageItem() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -310,7 +308,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_003
@LargeTest
public void testStressAddRemoveTransition() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -428,7 +425,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_004
@LargeTest
public void testStressAddRemoveOverlay() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -493,7 +489,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_005
@LargeTest
public void testStressAddRemoveEffects() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -590,7 +585,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_006
@LargeTest
public void testStressThumbnailVideoItem() throws Exception {
final String videoItemFileName = INPUT_FILE_PATH
@@ -651,7 +645,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_007
@LargeTest
public void testStressMediaProperties() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -747,7 +740,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_008
@LargeTest
public void testStressInsertMovieItems() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -759,7 +751,7 @@ public class VideoEditorStressTest
"MPEG4_SP_640x480_15fps_1200kbps_AACLC_48khz_64kbps_m_1_17.3gp";
final String[] loggingInfo = new String[1];
int i = 0;
- writeTestCaseHeader("testStressInsertMoveItems");
+ writeTestCaseHeader("testStressInsertMovieItems");
final MediaVideoItem mediaItem1 = new MediaVideoItem(mVideoEditor,
"m1", VideoItemFileName1, renderingMode);
@@ -801,7 +793,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_009
@LargeTest
public void testStressLoadAndSave() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -916,7 +907,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_010
@LargeTest
public void testStressMultipleExport() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -1007,7 +997,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_011
@LargeTest
public void testStressOverlayTransKenBurn() throws Exception {
final int renderingMode = MediaItem.RENDERING_MODE_BLACK_BORDER;
@@ -1094,7 +1083,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_012
@LargeTest
public void testStressAudioTrackVideo() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -1147,7 +1135,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_013
@LargeTest
public void testStressStoryBoard() throws Exception {
final String videoItemFileName1 = INPUT_FILE_PATH +
@@ -1237,7 +1224,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_014
@LargeTest
public void testStressAudioTrackOnly() throws Exception {
@@ -1267,7 +1253,6 @@ public class VideoEditorStressTest
*
* @throws Exception
*/
- // TODO : remove TC_STR_016 -- New Test Case
@LargeTest
public void testStressThumbnailImageItem() throws Exception {
final String imageItemFileName = INPUT_FILE_PATH + "IMG_640x480.jpg";