Вы не даете очень много подробностей о том, что именно вы пробовали и в чем проблемные области, поэтому я просто провел небольшой тест, чтобы посмотреть, смогу ли я воспроизвести что-либо из того, что вы описываете.
У меня нет окончательных выводов, но я могу хотя бы подтвердить, что мой Galaxy Nexus (Android 4.0.2) способен воспроизводить три видео одновременно без каких-либо проблем. С другой стороны, старый Samsung Galaxy Spica (Android 2.1-обновление1), который я лежал без дела, воспроизводит только один файл за раз - он, кажется, всегда является первым SurfaceView
.
Я также исследовал различные уровни API, настроив эмуляторы для Android 3.0, 2.3.3 и 2.2. Похоже, что все эти платформы могут нормально воспроизводить несколько видеофайлов на разных видах поверхности. Я провел еще один финальный тест с эмулятором, работающим под управлением 2.1-update1, который также интересно сыграл тестовый пример без проблем, в отличие от реального телефона. Я заметил некоторые небольшие различия в том, как макет отображался.
Такое поведение заставляет меня подозревать, что на самом деле нет никаких программных ограничений в отношении того, что вам нужно, но, похоже, оно зависит от аппаратного обеспечения, поддерживается ли одновременное воспроизведение нескольких видеофайлов. Следовательно, поддержка этого сценария будет отличаться для каждого устройства. С эмпирической точки зрения, я определенно думаю, что было бы интересно проверить эту гипотезу на еще нескольких физических устройствах.
Просто для справки некоторые детали относительно реализации:
- Я установил две слегка отличающиеся реализации: одну, основанную на трех
MediaPlayer
экземплярах в одном Activity
, и одну, в которой они были разбиты на три отдельных фрагмента с каждым собственным объектом MediaPlayer
. (Кстати, я не нашел никаких различий в воспроизведении для этих двух реализаций)
- Один файл 3gp (спасибо за это, Apple), расположенный в папке
assets
, использовался для воспроизведения на всех проигрывателях.
- Код для обеих реализаций прилагается ниже и в значительной степени основан на примере реализации
MediaPlayerDemo_Video
- я удалил некоторый код, не необходимый для реального тестирования. Результат ни в коем случае не является полным или пригодным для использования в живых приложениях.
Реализация на основе действий:
public class MultipleVideoPlayActivity extends Activity implements
OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback {
private static final String TAG = "MediaPlayer";
private static final int[] SURFACE_RES_IDS = { R.id.video_1_surfaceview, R.id.video_2_surfaceview, R.id.video_3_surfaceview };
private MediaPlayer[] mMediaPlayers = new MediaPlayer[SURFACE_RES_IDS.length];
private SurfaceView[] mSurfaceViews = new SurfaceView[SURFACE_RES_IDS.length];
private SurfaceHolder[] mSurfaceHolders = new SurfaceHolder[SURFACE_RES_IDS.length];
private boolean[] mSizeKnown = new boolean[SURFACE_RES_IDS.length];
private boolean[] mVideoReady = new boolean[SURFACE_RES_IDS.length];
@Override public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.multi_videos_layout);
// create surface holders
for (int i=0; i<mSurfaceViews.length; i++) {
mSurfaceViews[i] = (SurfaceView) findViewById(SURFACE_RES_IDS[i]);
mSurfaceHolders[i] = mSurfaceViews[i].getHolder();
mSurfaceHolders[i].addCallback(this);
mSurfaceHolders[i].setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
}
public void onBufferingUpdate(MediaPlayer player, int percent) {
Log.d(TAG, "MediaPlayer(" + indexOf(player) + "): onBufferingUpdate percent: " + percent);
}
public void onCompletion(MediaPlayer player) {
Log.d(TAG, "MediaPlayer(" + indexOf(player) + "): onCompletion called");
}
public void onVideoSizeChanged(MediaPlayer player, int width, int height) {
Log.v(TAG, "MediaPlayer(" + indexOf(player) + "): onVideoSizeChanged called");
if (width == 0 || height == 0) {
Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");
return;
}
int index = indexOf(player);
if (index == -1) return; // sanity check; should never happen
mSizeKnown[index] = true;
if (mVideoReady[index] && mSizeKnown[index]) {
startVideoPlayback(player);
}
}
public void onPrepared(MediaPlayer player) {
Log.d(TAG, "MediaPlayer(" + indexOf(player) + "): onPrepared called");
int index = indexOf(player);
if (index == -1) return; // sanity check; should never happen
mVideoReady[index] = true;
if (mVideoReady[index] && mSizeKnown[index]) {
startVideoPlayback(player);
}
}
public void surfaceChanged(SurfaceHolder holder, int i, int j, int k) {
Log.d(TAG, "SurfaceHolder(" + indexOf(holder) + "): surfaceChanged called");
}
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "SurfaceHolder(" + indexOf(holder) + "): surfaceDestroyed called");
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "SurfaceHolder(" + indexOf(holder) + "): surfaceCreated called");
int index = indexOf(holder);
if (index == -1) return; // sanity check; should never happen
try {
mMediaPlayers[index] = new MediaPlayer();
AssetFileDescriptor afd = getAssets().openFd("sample.3gp");
mMediaPlayers[index].setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mMediaPlayers[index].setDisplay(mSurfaceHolders[index]);
mMediaPlayers[index].prepare();
mMediaPlayers[index].setOnBufferingUpdateListener(this);
mMediaPlayers[index].setOnCompletionListener(this);
mMediaPlayers[index].setOnPreparedListener(this);
mMediaPlayers[index].setOnVideoSizeChangedListener(this);
mMediaPlayers[index].setAudioStreamType(AudioManager.STREAM_MUSIC);
}
catch (Exception e) { e.printStackTrace(); }
}
@Override protected void onPause() {
super.onPause();
releaseMediaPlayers();
}
@Override protected void onDestroy() {
super.onDestroy();
releaseMediaPlayers();
}
private void releaseMediaPlayers() {
for (int i=0; i<mMediaPlayers.length; i++) {
if (mMediaPlayers[i] != null) {
mMediaPlayers[i].release();
mMediaPlayers[i] = null;
}
}
}
private void startVideoPlayback(MediaPlayer player) {
Log.v(TAG, "MediaPlayer(" + indexOf(player) + "): startVideoPlayback");
player.start();
}
private int indexOf(MediaPlayer player) {
for (int i=0; i<mMediaPlayers.length; i++) if (mMediaPlayers[i] == player) return i;
return -1;
}
private int indexOf(SurfaceHolder holder) {
for (int i=0; i<mSurfaceHolders.length; i++) if (mSurfaceHolders[i] == holder) return i;
return -1;
}
}
R.layout.multi_videos_layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<SurfaceView android:id="@+id/video_1_surfaceview"
android:layout_width="fill_parent" android:layout_height="0dp"
android:layout_weight="1" />
<SurfaceView android:id="@+id/video_2_surfaceview"
android:layout_width="fill_parent" android:layout_height="0dp"
android:layout_weight="1" />
<SurfaceView android:id="@+id/video_3_surfaceview"
android:layout_width="fill_parent" android:layout_height="0dp"
android:layout_weight="1" />
</LinearLayout>
Реализация на основе фрагментов:
public class MultipleVideoPlayFragmentActivity extends FragmentActivity {
private static final String TAG = "MediaPlayer";
@Override public void onCreate(Bundle icicle) {
super.onCreate(icicle);
setContentView(R.layout.multi_videos_activity_layout);
}
public static class VideoFragment extends Fragment implements
OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback {
private MediaPlayer mMediaPlayer;
private SurfaceView mSurfaceView;
private SurfaceHolder mSurfaceHolder;
private boolean mSizeKnown;
private boolean mVideoReady;
@Override public View onCreateView(LayoutInflater inflater, ViewGroup container, Bundle savedInstanceState) {
return inflater.inflate(R.layout.multi_videos_fragment_layout, container, false);
}
@Override public void onActivityCreated(Bundle savedInstanceState) {
super.onActivityCreated(savedInstanceState);
mSurfaceView = (SurfaceView) getView().findViewById(R.id.video_surfaceview);
mSurfaceHolder = mSurfaceView.getHolder();
mSurfaceHolder.addCallback(this);
mSurfaceHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}
public void onBufferingUpdate(MediaPlayer player, int percent) {
Log.d(TAG, "onBufferingUpdate percent: " + percent);
}
public void onCompletion(MediaPlayer player) {
Log.d(TAG, "onCompletion called");
}
public void onVideoSizeChanged(MediaPlayer player, int width, int height) {
Log.v(TAG, "onVideoSizeChanged called");
if (width == 0 || height == 0) {
Log.e(TAG, "invalid video width(" + width + ") or height(" + height + ")");
return;
}
mSizeKnown = true;
if (mVideoReady && mSizeKnown) {
startVideoPlayback();
}
}
public void onPrepared(MediaPlayer player) {
Log.d(TAG, "onPrepared called");
mVideoReady = true;
if (mVideoReady && mSizeKnown) {
startVideoPlayback();
}
}
public void surfaceChanged(SurfaceHolder holder, int i, int j, int k) {
Log.d(TAG, "surfaceChanged called");
}
public void surfaceDestroyed(SurfaceHolder holder) {
Log.d(TAG, "surfaceDestroyed called");
}
public void surfaceCreated(SurfaceHolder holder) {
Log.d(TAG, "surfaceCreated called");
try {
mMediaPlayer = new MediaPlayer();
AssetFileDescriptor afd = getActivity().getAssets().openFd("sample.3gp");
mMediaPlayer.setDataSource(afd.getFileDescriptor(), afd.getStartOffset(), afd.getLength());
mMediaPlayer.setDisplay(mSurfaceHolder);
mMediaPlayer.prepare();
mMediaPlayer.setOnBufferingUpdateListener(this);
mMediaPlayer.setOnCompletionListener(this);
mMediaPlayer.setOnPreparedListener(this);
mMediaPlayer.setOnVideoSizeChangedListener(this);
mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);
}
catch (Exception e) { e.printStackTrace(); }
}
@Override public void onPause() {
super.onPause();
releaseMediaPlayer();
}
@Override public void onDestroy() {
super.onDestroy();
releaseMediaPlayer();
}
private void releaseMediaPlayer() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
private void startVideoPlayback() {
Log.v(TAG, "startVideoPlayback");
mMediaPlayer.start();
}
}
}
R.layout.multi_videos_activity_layout:
<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="match_parent" android:layout_height="match_parent"
android:orientation="vertical">
<fragment class="mh.so.video.MultipleVideoPlayFragmentActivity$VideoFragment"
android:id="@+id/video_1_fragment" android:layout_width="fill_parent"
android:layout_height="0dp" android:layout_weight="1" />
<fragment class="mh.so.video.MultipleVideoPlayFragmentActivity$VideoFragment"
android:id="@+id/video_2_fragment" android:layout_width="fill_parent"
android:layout_height="0dp" android:layout_weight="1" />
<fragment class="mh.so.video.MultipleVideoPlayFragmentActivity$VideoFragment"
android:id="@+id/video_3_fragment" android:layout_width="fill_parent"
android:layout_height="0dp" android:layout_weight="1" />
</LinearLayout>
R.layout.multi_videos_fragment_layout:
<?xml version="1.0" encoding="utf-8"?>
<SurfaceView xmlns:android="http://schemas.android.com/apk/res/android"
android:id="@+id/video_surfaceview" android:layout_width="fill_parent"
android:layout_height="fill_parent" />
Обновление: Хотя это уже давно, я просто подумал, что стоит отметить, что проект Google Grafika демонстрирует 'двойное декодирование' функция, которая "Декодирует два видеопотока одновременно в два TextureView.". Не уверен, насколько хорошо он масштабируется до более чем двух видеофайлов, но тем не менее актуален для исходного вопроса.