Android:MediaPlayer 无缝或无缝视频播放

发布于 2024-09-26 13:34:02 字数 2627 浏览 1 评论 0原文

我可以通过实现 OnCompletionListener 将数据源设置为不同的文件来连续播放视频。那里没有问题。我调用 reset()prepare() 就可以了。

我还没弄清楚的是如何消除数据源更改和新视频开始之间 1-2 秒的间隙屏幕闪烁。该间隙显示黑屏,我还没有找到任何方法来解决它。

我尝试将父视图的背景设置为图像,但它设法绕过了它。即使 SurfaceView 是透明的(默认情况下是透明的。)我也尝试过同时播放多个视频文件,并在一个结束而另一个应该结束时切换媒体播放器的显示开始。

我尝试的最后一件事是在视频“准备”时在背景中临时显示第二个视图,并在视频准备开始时将其删除。这也不是很顺利。

有什么办法可以消除这个差距吗?循环运行视频效果非常好,并且完全符合我的要求,只不过它是浏览同一个视频而不是播放我选择的不同视频。

ma​​in.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:background="@drawable/background"
    android:layout_height="fill_parent">
    <SurfaceView
        android:id="@+id/surface"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center">
    </SurfaceView>
</FrameLayout>

Player.java

public class Player extends Activity implements
        OnCompletionListener, MediaPlayer.OnPreparedListener, SurfaceHolder.Callback {
        private MediaPlayer player;
    private SurfaceView surface;
    private SurfaceHolder holder;

    public void onCreate(Bundle b) {
        super.onCreate(b);
        setContentView(R.layout.main);
        surface = (SurfaceView)findViewById(R.id.surface);
        holder = surface.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);    
    }

    public void onCompletion(MediaPlayer arg0) {
        File clip = new File(Environment.getExternalStorageDirectory(),"file2.mp4");
        playVideo(clip.getAbsolutePath());
    }
    public void onPrepared(MediaPlayer mediaplayer) {
        holder.setFixedSize(player.getVideoWidth(), player.getVideoHeight());
        player.start();
    }

    private void playVideo(String url) {
        try {
            File clip = new File(Environment.getExternalStorageDirectory(),"file1.mp4");

            if (player == null) {
                player = new MediaPlayer();
                player.setScreenOnWhilePlaying(true);
            }
            else {
                player.stop();
                player.reset();
            }
            player.setDataSource(url);
            player.setDisplay(holder);
            player.setOnPreparedListener(this);
            player.prepare();
            player.setOnCompletionListener(this);
        }
        catch (Throwable t) {
            Log.e("ERROR", "Exception Error", t);
        }
    }

I can play the videos fine back to back by implementing the OnCompletionListener to set the data source to a different file. No problems there. I call reset() and prepare() just fine.

What I haven't been able to figure out, is how to get rid of the 1-2 second gap screen flicker between the data source change and the new video starting. The gap shows a black screen, and I haven't found any way to get around it.

I've tried setting the background of the parent view to an image, but it manages to bypass that. Even if the SurfaceView is transparent (which it is by default.) I've also tried to have the multiple video files played at the same time, and switching mediaplayer's display when one ends and the other is supposed to start.

The last thing I tried, was to have a second view in the background that I show temporarily while the video is "preparing" and removing it when the video is ready to start. That also wasn't very seamless.

Is there any way to get rid of that gap. Running a video in a loop works wonderfully and does exactly what I want with the exception that it's looking through the same video instead of playing a different one that I pick.

main.xml

<?xml version="1.0" encoding="utf-8"?>
<FrameLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:layout_width="fill_parent"
    android:background="@drawable/background"
    android:layout_height="fill_parent">
    <SurfaceView
        android:id="@+id/surface"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center">
    </SurfaceView>
</FrameLayout>

Player.java

public class Player extends Activity implements
        OnCompletionListener, MediaPlayer.OnPreparedListener, SurfaceHolder.Callback {
        private MediaPlayer player;
    private SurfaceView surface;
    private SurfaceHolder holder;

    public void onCreate(Bundle b) {
        super.onCreate(b);
        setContentView(R.layout.main);
        surface = (SurfaceView)findViewById(R.id.surface);
        holder = surface.getHolder();
        holder.addCallback(this);
        holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS);    
    }

    public void onCompletion(MediaPlayer arg0) {
        File clip = new File(Environment.getExternalStorageDirectory(),"file2.mp4");
        playVideo(clip.getAbsolutePath());
    }
    public void onPrepared(MediaPlayer mediaplayer) {
        holder.setFixedSize(player.getVideoWidth(), player.getVideoHeight());
        player.start();
    }

    private void playVideo(String url) {
        try {
            File clip = new File(Environment.getExternalStorageDirectory(),"file1.mp4");

            if (player == null) {
                player = new MediaPlayer();
                player.setScreenOnWhilePlaying(true);
            }
            else {
                player.stop();
                player.reset();
            }
            player.setDataSource(url);
            player.setDisplay(holder);
            player.setOnPreparedListener(this);
            player.prepare();
            player.setOnCompletionListener(this);
        }
        catch (Throwable t) {
            Log.e("ERROR", "Exception Error", t);
        }
    }

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

趴在窗边数星星i 2024-10-03 13:34:02

我也有同样的问题,如下面的链接所述

VideoView 消失视频更改时一秒

但如果您尝试使用 Android 4.0+ (ICS),则不会出现此问题。我开始将 VideoView.java 和 MediaPlayer.java 从 4.0 移植到我的应用程序,但这似乎很复杂,到目前为止还没有运气。基本上,这似乎是旧版本的本机代码中的错误。

I too have the same problem as outlined by below link

VideoView Disappears for a second when video is changed

But this issue wont occur if you try using Android 4.0+ (ICS). I started to port VideoView.java and MediaPlayer.java from 4.0 to my app , but thats seems complex and no luck till now. Basically it seems a bug in the native code of the older versions.

习ぎ惯性依靠 2024-10-03 13:34:02

在浪费了太多时间试图找出如何在没有“间隙”的情况下播放连续视频之后,我倾向于不可能。当然,除非您能够深入到本机级别并实现自己的播放器,否则 Android 的媒体播放器目前根本不支持无缝播放。

after too much wasted time trying to figure out how to play consecutive videos without the "gap", i'm leaning towards impossible. unless of course you're able to dig down to the native level and implement your own player, Android's media player simply doesn't support seamless playback as of the moment.

猛虎独行 2024-10-03 13:34:02

我还没有对 MediaPlayer 上的视频播放执行此操作,但当流因用户从 3G 切换到 Wifi 而中断时,我对音频文件执行了类似的操作。

我认为您看到的延迟是在媒体播放器缓冲输入文件时发生的。
那么也许你可以在一开始就定义这两名球员?您应该定义数据源并准备两个播放器,但只启动其中之一。

然后在 onCompletionListener 中,您可以翻转它们,而不是重置现有数据源并设置新的数据源。

  player.release();
  player = flipPlayer;
  flipPlayer = null;
  player.start();

显然,您需要为 FlipPlayer 使用不同的 onPreparedListener 或将 player.start() 从 onPrepare 中取出。 (既然你同步调用它,我不会认为这是一个问题)。

I've not done this with video playback on a MediaPlayer but I've done something similar with audio files when a stream gets interrupted because a user has switched from 3G to Wifi.

I think the delay that you're seeing is whilst the media player is buffering the input file.
So maybe you can define both players at the start? You should do define the datasource and prepare both players but only start one of them.

Then in your onCompletionListener you can flip them over instead of resetting the existing one and setting a new datasource.

  player.release();
  player = flipPlayer;
  flipPlayer = null;
  player.start();

Obviously you'd need to either use a different onPreparedListener for flipPlayer or take the player.start() out of the onPrepare. (Since you're calling it synchronously I wouldn't have thought this was an issue).

Hello爱情风 2024-10-03 13:34:02

我认为这是不可能的。

原因:Mortplayer 也适用于 Windows Mobile,其优势之一是支持无缝播放。然而,它并不存在于应用程序的 android 版本中,并且开发人员自己写道,SDK 不允许在 xda 上使用它:
http://forum.xda-developers.com/showpost.php ?p=5364530&postcount=5

HTH,丹尼尔

I don't think it's possible.

Reason: Mortplayer was also available for windows mobile and one of its strengths was that it supported gapless play. However it doesn't in the android version of the app, and the developer itself writes that the SDK does not allow it on xda:
http://forum.xda-developers.com/showpost.php?p=5364530&postcount=5

HTH, Daniele

音栖息无 2024-10-03 13:34:02

您是否尝试准备(打开/准备)2 个 VideoView,其中一个可见,另一个不可见并停止,很快您就会收到 OnCompletionListener 回调,使第二个视频可见,启动它并隐藏第一个。
同时,当第二个视频播放时,您可以在第一个视频视图上调用打开/准备来打开/准备另一个文件。

Did you try to have ready (opened/prepared) 2 VideoView's, with one being visible, other invisible and stopped, and ass soon you get OnCompletionListener callback, make 2nd one visible, start it, and hide 1st one.
In meantime, as 2nd one plays, you can call open/prepare on 1st VideoView to open/prepare another file.

念三年u 2024-10-03 13:34:02

@user1263019 - 您能够将 Android 4.0 MediaPlayer 移植到您的应用程序吗?我面临着同样的问题,我正在寻找一个好的解决方案。我的情况是 SurfaceView 上有一个图像,应该隐藏该图像以显示视频播放,但调用 start() 和视频的实际开始之间有一个间隙。到目前为止唯一的解决方案是启动一个线程来检查 getCurrentPosition() 是否 > 0.

关于这个主题 - 我认为无缝播放是不可能的,尽管 Winamp 声称具有这种能力。一个肮脏的黑客是在第一个播放器的播放结束前几秒钟准备第二个播放器,然后在播放结束前 100-200ms 调用第二个播放器的 start() 。

@user1263019 - were you able to port Android 4.0 MediaPlayer to your app? I'm facing the same issue and I'm looking for a nice solution. My case is having an image over the SurfaceView which should be hidden in order to show the video playing, but there is a gap between calling start() and the actual start of the video. The only solution so far is to start a Thread that checks if getCurrentPosition() is > 0.

On the topic - I think it is not possible to have gapless playback, though Winamp claim to have such abilities. A dirty hack is to prepare the second player several seconds before the end of the playback of the first player and then call start() of the second player 100-200ms before end of playback.

平生欢 2024-10-03 13:34:02

在您的 exoplayer 实现(或媒体播放器)中,使用处理程序在播放时重复发布可运行文件,获取当前跟踪时间并将其传递给回调:

public interface MediaAction {
  public void onTrackingChanged(long currentPosition);
}

public class ExoPlayerImplementation implements Exoplayer {

  ..

  private MediaAction mediaActionCallback;
  public void setMediaActionCallback(MediaAction ma) {
    mediaActionCallback = ma;
  }
  public void releaseMediaAction() { mediaActionCallback = null; } 

  private Handler trackingCallback = new Handler(Looper.getMainLooper());

  Runnable trackingTask;
  private Runnable getTrackingTask() {
    Runnable r = new Runnable {
      @Override public void run() {
         long currentPosition = getCurrentPosition();
         if (mediaActionCallback != null) {
           mediaActionCallback.onTrackingChanged(currentPosition);
         }
         // 60hz is 16 ms frame delay...
         trackingCallback.postDelayed(getTrackingTask(), 15);
       }
    };
    trackingTask = r;
    return r;
  }

  public void play() {
    // exoplayer start code
    trackingCallback.post(getTrackingTask());
  } 

  public void pause/stop() {
    trackingCallback.removeCallbacks(trackingTask);
  }

  ..

}

现在您可以跟踪当前位置何时实际发生变化 - 如果它发生了变化更改,然后您可以显示视频输出视图/交换视图/删除预览(即,使用 MediaExtractor 抓取初始帧,覆盖在 ImageView 中作为预览,然后在当前位置增加时隐藏)。您可以通过将其与状态侦听器组合来使此代码更加完整:然后您知道您是否正在缓冲(检查缓冲位置与当前位置),是否正在使用适当的回调(在 MediaAction 中)实际播放、暂停或停止。您可以将此方法应用于 MediaPlayer 和 ExoPlayer 类(因为它只是一个接口和处理程序)。

希望有帮助!

In your exoplayer implementation (or mediaplayer) use a handler to repeatedly post a runnable, while playing, that gets the current tracking time and passes it to a callback:

public interface MediaAction {
  public void onTrackingChanged(long currentPosition);
}

public class ExoPlayerImplementation implements Exoplayer {

  ..

  private MediaAction mediaActionCallback;
  public void setMediaActionCallback(MediaAction ma) {
    mediaActionCallback = ma;
  }
  public void releaseMediaAction() { mediaActionCallback = null; } 

  private Handler trackingCallback = new Handler(Looper.getMainLooper());

  Runnable trackingTask;
  private Runnable getTrackingTask() {
    Runnable r = new Runnable {
      @Override public void run() {
         long currentPosition = getCurrentPosition();
         if (mediaActionCallback != null) {
           mediaActionCallback.onTrackingChanged(currentPosition);
         }
         // 60hz is 16 ms frame delay...
         trackingCallback.postDelayed(getTrackingTask(), 15);
       }
    };
    trackingTask = r;
    return r;
  }

  public void play() {
    // exoplayer start code
    trackingCallback.post(getTrackingTask());
  } 

  public void pause/stop() {
    trackingCallback.removeCallbacks(trackingTask);
  }

  ..

}

And now you can track when the current position actually has changed--if it has changed, then you can show your video output view / swap views / remove preview (ie, use MediaExtractor to grab initial frames, overlay as preview in an ImageView then hide once current position is increasing). You can make this code more complete by combining it with the state listener: then you know if you are buffering (check buffering position versus current position), actually playing, paused or stopped with proper callbacks (in MediaAction). You can apply this method to both MediaPlayer and ExoPlayer classes (since it is just an interface and handler).

Hope that helps!

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文