Pytanie Czarny ekran po powrocie do odtwarzania wideo w systemie Android


Obecnie rozwijam aplikację Android ServeStream i napotkałem problem, którego nie mogę naprawić. Moja aplikacja będzie przesyłać strumieniowo muzykę i wideo za pomocą klasy Android Media Player. Modelowałem swoją klasę po przykładzie znalezionym na:

http://developer.android.com/resources/samples/ApiDemos/src/com/example/android/apis/media/MediaPlayerDemo_Video.html

Różnica między tym przykładem a moim własnym kodem jest taka, że ​​mój MediaPlayer działa w usłudze, która pozwala mu kontynuować odtwarzanie w tle. Problem z przykładowym kodem androida polega na tym, że oglądam wideo i opuszczam bieżące okno / działanie (np. Wciskam przycisk menu, itp.) I wracam do działania odtwarzania Otrzymuję czarny ekran, ale nadal odbieram audio z wideo to gra.

Kiedy moja aktywność odtwarzania jest początkowo tworzona, wyświetlany jest poniższy kod. Ten kod zasadniczo tworzy widok używany do odtwarzania, a następnie wiąże go z odtwarzaczem multimediów:

        setContentView(R.layout.mediaplayer_2);
        mPreview = (SurfaceView) findViewById(R.id.surface);
        holder = mPreview.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
...
        mMediaPlayer.setDisplay(holder);

Ważną linią jest mMediaPlayer.setDisplay (uchwyt), ponieważ wiąże bieżący widok / wyświetlacz z odtwarzaczem multimediów. Widok ("posiadacz") zostaje zniszczony po opuszczeniu działania. Po powrocie do działania i ponownym utworzeniu widoku ponowne uruchomienie mMediaPlayer.setDisplay (posiadacza) nie powoduje ponownego dołączenia nowo utworzonego widoku. Zamiast wideo wyświetlany jest czarny ekran.

Czy ktoś ma obejście lub rozwiązanie tego problemu. Byłbym wdzięczny za każdą pomoc lub radę.


12
2018-06-14 18:01


pochodzenie


czy nadpisałeś jakąś metodę cyklu życia, taką jak onStop () onResume ()? Zacząłbym tam ... - bgs
Zastukałem je, jaki kod sugerujesz, aby dołączyć do nadpisanych metod? - William Seemann
Wygląda na to, że robisz to dobrze. Czy używasz funkcji resetowania () w odtwarzaczu multimedialnym ?. Oprócz upewnienia się, że wywołujesz odpowiedni start (), stop (), restartowanie poprawnie itd. W odpowiednich miejscach, w ostateczności dołączę źródło androida i przejdę przez niego w debugerze. Coś w logcat? - bgs
Dobry pomysł, wydaje mi się, że wywołanie funkcji reset () może rozwiązać problem, jednak odtwarzam strumień i nie chcę narzutów na ponowne buforowanie pliku wideo przed wznowieniem odtwarzania. Sprawdziłem inne aplikacje, takie jak vPlayer, które przesyłają filmy wideo w tle bez wywoływania resetowania () podczas wznawiania działania. - William Seemann
Problem wydaje się być rozwiązany w nowszych wersjach Androida (4.x +). Używam tego samego kodu na GB (2.3.6) i JB, KK i okazuje się, że JB i KK odtwarzają wideo dobrze, podczas gdy GB wciąż utknął na czarnym ekranie z tylko wyjściem audio. - Puru Pawar


Odpowiedzi:


Po wielu googlowaniach i head-scratching over diagram stanu z MediaPlayer, Udało mi się wreszcie uniknąć tego tak zwanego czarnego ekranu. Już napisałem w sekcji komentarzy OP, że problem ten wydaje się być rozwiązany w najnowszych wersjach Androida (więcej niż 4.x), więc zdecydowałem się również zachować podobne zachowanie na urządzeniach Gingerbread.

Nie trzeba dodawać że, SurfaceHolder metody wywołania zwrotnego odgrywają bardzo istotną rolę w cyklu życia ograniczonego MediaPlayer-SurfaceView realizacja. A te metody wywołania zwrotnego przydały mi się, gdy wychodziłem z tej sytuacji.

Pierwszy:

wewnątrz onCreate ():   - Podstawowe rzeczy do inicjalizacji ..

    mVideoSurface = (SurfaceView) findViewById(R.id.videoSurface);
    videoHolder = mVideoSurface.getHolder();
    videoHolder.addCallback(this);

    // For Android 2.2

    videoHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

    // Media Player and Controller

    player = new MediaPlayer();
    controller = new VideoControllerView(this);

Następnie, Połączenia zwrotne SurfaceView:

nie zapomnij ustawić Display dla Twojej MediaPlayer i IMHO, surfaceCreated() jest najlepszym miejscem do tego.

@Override
 public void surfaceCreated(SurfaceHolder holder) {
    player.setDisplay(holder);
    try {
    player.prepareAsync();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    }
}

A oto najważniejsza rzecz. Kiedy użytkownik opuszcza bieżącą aktywność, naciskając przycisk Home lub otwierając inną aktywność z oczekiwaniem wyników, nasz SurfaceView zostanie zniszczony. To, co chciałem osiągnąć, to wznowić odtwarzanie bieżącego wideo z pozycji, w której grał, kiedy kontekst został zmieniony. Aby to zrobić,

Zapisz bieżącą pozycję odtwarzania w zmiennej, abyśmy mogli później jej użyć szukać nasze odtwarzanie do tej konkretnej pozycji. I jeszcze jeden. Uwolnij cholerę MediaPlayer instancja. Próbowałem unikać uwalniania MediaPlayerinstancja i jej odtworzenie, ale ciągle mi się nie udaje. Więc punkt został wykonany.

@Override
public void surfaceDestroyed(SurfaceHolder holder) {
    if (player != null) {
        mCurrentVideoPosition = player.getCurrentPosition();
        player.release();
        player = null;
    }

}

Teraz, onPrepared ()  Zainicjuj dowolny MediaController tutaj, jeśli jesteś zainteresowany. Sprawdź, czy wideo już grało, a następnie odszukaj film w tej pozycji.

     @Override
 public void onPrepared(MediaPlayer mp) {
    controller.setMediaPlayer(this);
    controller.setAnchorView((FrameLayout) findViewById(R.id.videoSurfaceContainer));
    player.start();

    if (mCurrentVideoPosition != 0) {
        player.seekTo(mCurrentVideoPosition);
        player.start();
    }

}

Wreszcie, aby odtworzyć wideo:

 void playVideo(){  

   try {
        if (player == null) 
            player = new MediaPlayer();
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setDataSource("SOME_VIDEO_FILE.3gp");
        player.setOnPreparedListener(this);
        player.setOnErrorListener(new OnErrorListener() {

            @Override
            public boolean onError(MediaPlayer mp, int what, int extra) {
                mp.reset();
                return false;
            }
        });

    } catch (IllegalArgumentException e) {
        e.printStackTrace();
    } catch (SecurityException e) {
        e.printStackTrace();
    } catch (IllegalStateException e) {
        e.printStackTrace();
    } catch (IOException e) {
        e.printStackTrace();
    }
     }

I to wszystko. Wiem, że nie ma problemu z nowszymi wersjami i świetnymi, ale ... wiele GB wciąż tam jest.


5
2018-02-04 13:02



Dzięki za potrzebne rozwiązanie - Riskhan


Możesz to zrobić: mMediaPlayer.setDisplay(null) gdy surfaceDestroy i po ponownym wprowadzeniu wideo setDisplay dla mediaPlayer z nowym surfaceHolder.
Pamiętaj, zawsze umieszczaj ten kod poniżej w środku onStart, ponieważ po naciśnięciu przycisku home lub lockscreen wymusi on "sufaceCreate" z nowym uchwytem. Widok zostanie odtworzony, wideo wyświetli czarny ekran

    holder = mPreview.getHolder();
    holder.addCallback(this);
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);

Mam nadzieję, że to pomoże!!


1
2018-05-20 08:49



Ta odpowiedź została dodana w 2014 r., Więc muszę poinformować, że metoda setType jest nieaktualna. - Dhir Pratap


Myślę, że znam przyczynę problemu. Wygląda na to, że mediaplayer inicjalizuje swoją powierzchnię TYLKO podczas wywołania metody prepare / Async (). Jeśli później zmienisz powierzchnię, dostaniesz to, co masz. Tak więc rozwiązaniem byłoby wyłączenie standardowego cyklu życia aktywności przez android: configChanges = "orientation | keyboardHidden". Zapobiegnie to aktywności związanej z odtwarzaniem. W przeciwnym razie powinieneś wezwać do przygotowania za każdym razem, gdy ponownie utworzysz posiadacza.


0
2017-07-11 07:39



Doceniam odpowiedź, ale nie jestem pewien, czy to jest problem. Mam już configChanges = "orientation | keyboardHidden" i ręcznie obsługuję konfigurację zmienioną w moim kodzie. Widzieć: servestream.svn.sourceforge.net/svnroot/servestream/trunk/src/... Jednak po powrocie do aktywności ekran pozostaje czarny. - William Seemann


Mam ten sam problem!

Podczas odtwarzania wideo, naciśnij przycisk HOME, a następnie wróć do aplikacji. Rozumiem:

public void surfaceCreated(SurfaceHolder holder) {
    player.setDisplay(holder);
    playVideo();
}

w playVideo() Mam:

private void playVideo() {
    if (extras.getString("video_path").equals("VIDEO_URI")) {
        showToast("Please, set the video URI in HelloAndroidActivity.java in onClick(View v) method");
    } else {
        try {
            if (flag == 0) {
                player.setDataSource(extras.getString("video_path"));
                player.prepareAsync();
                flag = 1;
            }
            else
            {
                player.start();
            }
        } catch (IllegalArgumentException e) {
            showToast("Error while playing video");
            Log.i(TAG, "========== IllegalArgumentException ===========");
            e.printStackTrace();
        } catch (IllegalStateException e) {
            showToast("Error while playing video");
            Log.i(TAG, "========== IllegalStateException ===========");
            e.printStackTrace();
        } catch (IOException e) {
            showToast("Error while playing video. Please, check your network connection.");
            Log.i(TAG, "========== IOException ===========");
            e.printStackTrace();
        }
    }
}

Mam puste czarne tło i słyszę audiostream.

Zauważyłem, że jeśli nie uruchomię tego działania po raz pierwszy player.setDisplay(holder); będę miał to samo zachowanie.

Spędziłem dwa dni próbując rozwiązać ten problem ...


0
2017-07-12 14:17





EDYCJA: Należy pamiętać, że to rozwiązanie nie działa na urządzeniach 4.x - wideo po prostu nigdy się nie pojawia, tylko pusty ekran i nie mogłem go uruchomić.

Oto maszyna stanu, która wykonuje następujące czynności:
 1. wykonuje krótkie kilka sekund wideo /res/raw/anim_mp4.mp4 (procedura wszczęta w metodzie onCreate ())
 2. jeśli użytkownik naciśnie przycisk domowy, a następnie wróci do aplikacji, będzie szukał wideo do rozpoczęcia pozycji i od razu się zatrzyma (procedura wszczęta w metodzie onResume ())

package com.test.video;
import android.app.Activity;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
import android.os.AsyncTask;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.view.WindowManager;

public class MediaPlayerActivity extends Activity implements OnCompletionListener,
    MediaPlayer.OnPreparedListener, MediaPlayer.OnSeekCompleteListener, SurfaceHolder.Callback {

private final int STATE_NOT_INITIALIZED = 0;
private final int STATE_INITIALIZING = 1;
private final int STATE_INITIALIZED = 2;
private final int STATE_SEEKING = 3;
private final int STATE_DELAYING = 4;
private final int STATE_PLAYING = 5;
private final int STATE_FINISHED = 6;
private final int STATE_RESUMED = 7;
private final int STATE_RESUMED_PREPARING = 8;
private final int STATE_RESUMED_PREPARED = 9;
private final int STATE_RESUMED_SEEKING = 10;
private int state = STATE_NOT_INITIALIZED;

private SurfaceView surface;
private MediaPlayer player;
private SurfaceHolder holder;

@Override
protected void onCreate(Bundle savedInstanceState) {
    Log.d(Constants.TAG, "onCreate()");
    super.onCreate(savedInstanceState);

    getWindow().setFlags(WindowManager.LayoutParams.FLAG_FULLSCREEN,
            WindowManager.LayoutParams.FLAG_FULLSCREEN);
    setContentView(R.layout.mediaplayer);

    surface = (SurfaceView) findViewById(R.id.idSurface);
    holder = surface.getHolder();
    holder.addCallback(this);
    holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);
}

@Override
protected void onStart() {
    Log.d(Constants.TAG, "onStart()");
    super.onStart();
    state();
}

@Override
protected void onResume() {
    Log.d(Constants.TAG, "onResume()");
    super.onResume();
    if (STATE_FINISHED == state) {
        state = STATE_RESUMED;
        state();
    }
}

@Override
protected void onDestroy() {
    Log.d(Constants.TAG, "onDestroy()");
    super.onDestroy();
    if (player != null) {
        player.release();
        player = null;
    }
}

@Override
public void surfaceChanged(SurfaceHolder arg0, int arg1, int arg2, int arg3) {
    Log.d(Constants.TAG, "surfaceChanged()");
}

@Override
public void surfaceCreated(SurfaceHolder arg0) {
    Log.d(Constants.TAG, "surfaceCreated()");
}

@Override
public void surfaceDestroyed(SurfaceHolder arg0) {
    Log.d(Constants.TAG, "surfaceDestroyed()");
}

@Override
public void onPrepared(MediaPlayer mp) {
    Log.d(Constants.TAG, "onPrepared()");
    state();
}

@Override
public void onCompletion(MediaPlayer mp) {
    Log.d(Constants.TAG, "onCompletion()");
    state();
}

@Override
public void onSeekComplete(MediaPlayer mediaplayer) {
    Log.d(Constants.TAG, "onSeekComplete()");
    state();
}

private class ResumeDelayed extends PlayDelayed {
    protected void onPostExecute(Void result) {
        Log.d(Constants.TAG, "ResumeDelayed.onPostExecute()");
        state();
    };
}

private void initPlayer() {
    Log.d(Constants.TAG, "initPlayer()");
    try {
        if (player == null) {
            player = new MediaPlayer();
            player.setScreenOnWhilePlaying(true);
        } else {
            player.stop();
            player.reset();
        }
        String uri = "android.resource://" + getPackageName() + "/" + R.raw.anim_mp4;
        player.setDataSource(this, Uri.parse(uri));
        player.setDisplay(holder);
        player.setAudioStreamType(AudioManager.STREAM_MUSIC);
        player.setOnPreparedListener(this);
        player.prepareAsync();
        player.setOnCompletionListener(this);
        player.setOnSeekCompleteListener(this);
    } catch (Throwable t) {
        Log.e(Constants.TAG, "Exception in media prep", t);
    }
}

private class PlayDelayed extends AsyncTask<Void, Void, Void> {
    @Override
    protected Void doInBackground(Void... arg0) {
        try {
            Thread.sleep(1000);
        } catch (InterruptedException e) {
            Log.e(Constants.TAG, "Can't sleep", e);
        }
        return null;
    }

    protected void onPostExecute(Void result) {
        Log.d(Constants.TAG, "PlayDelayed.onPostExecute()");
        initPlayer();
    };
}

private void state() {
    switch (state) {
        case STATE_NOT_INITIALIZED:
            state = STATE_INITIALIZING;
            initPlayer();
            break;
        case STATE_INITIALIZING:
            state = STATE_INITIALIZED;
            new PlayDelayed().execute();
            break;
        case STATE_INITIALIZED:
            state = STATE_SEEKING;
            player.start();
            player.seekTo(0);
            break;
        case STATE_SEEKING:
            state = STATE_DELAYING;
            player.pause();
            new ResumeDelayed().execute();
            break;
        case STATE_DELAYING:
            state = STATE_PLAYING;
            player.start();
            break;
        case STATE_PLAYING:
            state = STATE_FINISHED;
            break;
        case STATE_RESUMED:
            state = STATE_RESUMED_PREPARING;
            initPlayer();
            break;
        case STATE_RESUMED_PREPARING:
            state = STATE_RESUMED_PREPARED;
            new PlayDelayed().execute();
            break;
        case STATE_RESUMED_PREPARED:
            state = STATE_RESUMED_SEEKING;
            player.start();
            player.seekTo(0);
            break;
        case STATE_RESUMED_SEEKING:
            state = STATE_FINISHED;
            player.pause();
            break;
        default:
            break;
    }
}

układ mediaplayer.xml:

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
android:layout_width="fill_parent"
android:layout_height="fill_parent"
android:background="@android:color/black" >

<SurfaceView
    android:id="@+id/idSurface"
    android:layout_width="wrap_content"
    android:layout_height="wrap_content"
    android:layout_gravity="center" />

AndroidManifest.xml wygląda następująco:

<?xml version="1.0" encoding="utf-8"?>
<manifest xmlns:android="http://schemas.android.com/apk/res/android"
package="com.test.video"
android:versionCode="1"
android:versionName="1.0" >

<uses-sdk android:minSdkVersion="10" />

<application
    android:icon="@drawable/ic_launcher"
    android:theme="@android:style/Theme.Black.NoTitleBar" >
    <activity
        android:name=".MediaPlayerActivity"
        android:label="@string/app_name"
        android:screenOrientation="portrait"
        android:theme="@android:style/Theme.Black.NoTitleBar" >
        <intent-filter>
            <action android:name="android.intent.action.MAIN" />

            <category android:name="android.intent.category.LAUNCHER" />
        </intent-filter>
    </activity>
</application>


0
2017-08-20 15:02





Widziałem ten sam problem, po prostu odtwarzając wideo na moim Galaxy S3 i na Xoom Tablet. Wyłączyłem akcelerację sprzętową (HW) w ustawieniach odtwarzacza i rozwiązałem problem. Nie rozwijam się dla Androida, ale mam nadzieję, że doprowadzi cię to do rozwiązania. Być może możesz przełączyć to ustawienie na obejście. Urządzenie, którego używam, może nie mieć przyspieszenia HW.


0
2017-07-17 07:49





Czy chcesz, aby aktywność nie była wyświetlana po powrocie? jeśli tak, możesz po prostu użyć flagi

i.setFlags(Intent.FLAG_ACTIVITY_CLEAR_TOP);

Jeśli chcesz, aby twoje media były odtwarzane w tle, sugeruję użycie powierzchni wideo, ponieważ irytuje mnie gra w tle, chociaż nie chcę. Są sposoby, ale mam nadzieję, że ta będzie dla ciebie owocna http://www.brightec.co.uk/blog/custom-android-media-controller

ten link w rzeczywistości służy do dostosowywania kontrolerów multimediów, jak tylko chcesz, ale kiedy stworzyłem mój, byłam zaniepokojona, że ​​naciskając klawisz domowy, wideo będzie odtwarzane w tle.


0
2017-09-11 11:03