安卓实时视频流应用

11

我正在尝试构建一款从Android设备中直播视频的应用程序。

使用MediaRecorder类,我能够以3gp格式捕获视频数据,并且使用h263编解码器。

然而,当我运行我的应用程序并开始流传媒体时,服务器端会有2-3秒的延迟。

为什么会出现这种延迟?是否有内部缓冲区需要清除?是否有其他方法可以直播视频,而不是使用MediaRecorder类?


嗨!我也在尝试实现同样的功能。你能告诉我一些关于如何使用mediarecorder实现它的细节吗? - Vinod Maurya
请问您能否提供更多细节信息?您使用的是哪个库(ffmpeg、live555等...)?采用了哪种方法(RTP、RTSP...)?——flock.dux评论 - Vladimir Ivanov
嗨,弗拉基米尔·伊万诺夫....你能为我建议一下在安卓上进行实时视频流的整个过程吗?我不知道如何开始实时视频流...非常感谢您提前的帮助....等待您的回复... - shyam
1
你知道如何将安卓设备上的实时视频发送到服务器吗?就像Skype一样。 - Dhiral Pandya
请帮我完成这个任务。我无法将视频流传输到服务器。如果您能提供演示代码,那将非常有帮助。提前感谢您。 - Rahul Baradia
3个回答

1
如果您想从Android进行RTMP流媒体传输,最好的解决方案是使用MediaCodec + FFmpeg + librtmp。这避免了任何奇怪的“检测字节流中的NAL单元”的业务,但需要Android 4.3。滑到冰球要去的地方...
我开发了一个开源SDK,演示了使用FFmpeg+librtmp作为预构建共享库的RTMP流媒体传输。该SDK专注于HLS流媒体传输,但支持RTMP。
如果您需要帮助为Android构建FFmpeg(无论是否包含librtmp),请查看我的指南

没有Kickflip账户也能使用吗?我可以流式传输到任何HLS客户端吗? - 4ntoine
当然可以!尝试使用AVRecorder.javaHLSFileMonitor.java来监控.ts片段和.m3u8文件何时准备好上传。Broadcaster.java是Kickflip的参考实现。 - dbro

0
如果Android应用程序运行良好,并且代码已经优化,那么延迟的原因可能是您向服务器发送了大量数据,并且HTTP请求发送的MB有限制。当发生这种情况时,类自动或程序员必须将HTTP请求分成多个HTTP请求,显然速度会变慢。

-3

首先创建这个类。

MediaPlayerDemo_Video.java:-


package com.videostreaming.player;

import android.app.Activity;
import android.content.Intent;
import android.media.AudioManager;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnBufferingUpdateListener;
import android.media.MediaPlayer.OnCompletionListener;
import android.media.MediaPlayer.OnPreparedListener;
import android.media.MediaPlayer.OnVideoSizeChangedListener;
import android.os.Bundle;
import android.util.Log;
import android.view.SurfaceHolder;
import android.view.SurfaceView;
import android.widget.Toast;


public class MediaPlayerDemo_Video extends Activity implements
        OnBufferingUpdateListener, OnCompletionListener,
        OnPreparedListener, OnVideoSizeChangedListener, SurfaceHolder.Callback {

     private String path1 = "http://podcast.20min-tv.ch/podcast/20min/199733.mp4";
    // private String path2 = "http://podcast.20min-tv.ch/podcast/20min/199752.mp4";
     private String path2 = "http://podcast.20min-tv.ch/podcast/20min/199693.mp4";
     private String path = "";

    private static final String TAG = "MediaPlayerDemo";
    private int mVideoWidth;
    private int mVideoHeight;
    private MediaPlayer mMediaPlayer;
    private SurfaceView mPreview;
    private SurfaceHolder holder;
//    private String path;
    private Bundle extras;
    private static final String MEDIA = "media";
    private static final int LOCAL_AUDIO = 1;
    private static final int STREAM_AUDIO = 2;
    private static final int RESOURCES_AUDIO = 3;
    private static final int LOCAL_VIDEO = 4;
    private static final int STREAM_VIDEO = 5;
    private boolean mIsVideoSizeKnown = false;
    private boolean mIsVideoReadyToBePlayed = false;

    private Bundle bdlReceivedData = null;
    private Intent self = null;

    /**
     * 
     * Called when the activity is first created.
     */
    @Override
    public void onCreate(Bundle icicle) {
        super.onCreate(icicle);
        setContentView(R.layout.mediaplayer_2);

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

        extras = getIntent().getExtras();

        self = this.getIntent();
        bdlReceivedData = self.getExtras();

        if (bdlReceivedData != null && bdlReceivedData.getInt("video") > 0)
        {
            if (bdlReceivedData.getInt("video") == 1)
            {
                Toast.makeText(MediaPlayerDemo_Video.this,"playing Video 1", Toast.LENGTH_SHORT);
                path = path1;
            }
            else if (bdlReceivedData.getInt("video") == 2)
            {
                Toast.makeText(MediaPlayerDemo_Video.this,"playing Video 2", Toast.LENGTH_SHORT);
                path = path2;
            }
        }
    }

    private void playVideo(Integer Media) {
        doCleanUp();
        try {

//            switch (Media) {
//                case LOCAL_VIDEO:
//                    /*
//                     * TODO: Set the path variable to a local media file path.
//                     */
//                    path = "";
//                    if (path == "") {
//                        // Tell the user to provide a media file URL.
//                        Toast
//                                .makeText(
//                                        MediaPlayerDemo_Video.this,
//                                        "Please edit MediaPlayerDemo_Video Activity, "
//                                                + "and set the path variable to your media file path."
//                                                + " Your media file must be stored on sdcard.",
//                                        Toast.LENGTH_LONG).show();
//
//                    }
//                    break;
//                case STREAM_VIDEO:
//                    /*
//                     * TODO: Set path variable to progressive streamable mp4 or
//                     * 3gpp format URL. Http protocol should be used.
//                     * Mediaplayer can only play "progressive streamable
//                     * contents" which basically means: 1. the movie atom has to
//                     * precede all the media data atoms. 2. The clip has to be
//                     * reasonably interleaved.
//                     * 
//                     */
//                    path = "";
//                    if (path == "") {
//                        // Tell the user to provide a media file URL.
//                        Toast
//                                .makeText(
//                                        MediaPlayerDemo_Video.this,
//                                        "Please edit MediaPlayerDemo_Video Activity,"
//                                                + " and set the path variable to your media file URL.",
//                                        Toast.LENGTH_LONG).show();
//
//                    }
//
//                    break;
//
//
//            }

            // Create a new media player and set the listeners
            mMediaPlayer = new MediaPlayer();
            mMediaPlayer.setDataSource(path);
            mMediaPlayer.setDisplay(holder);
            mMediaPlayer.prepare();
            mMediaPlayer.setOnBufferingUpdateListener(this);
            mMediaPlayer.setOnCompletionListener(this);
            mMediaPlayer.setOnPreparedListener(this);
            mMediaPlayer.setOnVideoSizeChangedListener(this);
            mMediaPlayer.setAudioStreamType(AudioManager.STREAM_MUSIC);

        } catch (Exception e) {
            Log.e(TAG, "error: " + e.getMessage(), e);
        }
    }

    public void onBufferingUpdate(MediaPlayer arg0, int percent) {
        Log.d(TAG, "onBufferingUpdate percent:" + percent);

    }

    public void onCompletion(MediaPlayer arg0) {
        Log.d(TAG, "onCompletion called");
    }

    public void onVideoSizeChanged(MediaPlayer mp, 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;
        }
        mIsVideoSizeKnown = true;
        mVideoWidth = width;
        mVideoHeight = height;
        if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            startVideoPlayback();
        }
    }

    public void onPrepared(MediaPlayer mediaplayer) {
        Log.d(TAG, "onPrepared called");
        mIsVideoReadyToBePlayed = true;
        if (mIsVideoReadyToBePlayed && mIsVideoSizeKnown) {
            startVideoPlayback();
        }
    }

    public void surfaceChanged(SurfaceHolder surfaceholder, int i, int j, int k) {
        Log.d(TAG, "surfaceChanged called");

    }

    public void surfaceDestroyed(SurfaceHolder surfaceholder) {
        Log.d(TAG, "surfaceDestroyed called");
    }


    public void surfaceCreated(SurfaceHolder holder) {
        Log.d(TAG, "surfaceCreated called");
        playVideo(extras.getInt(MEDIA));
    }

    @Override
    protected void onPause() {
        super.onPause();
        releaseMediaPlayer();
        doCleanUp();
    }

    @Override
    protected void onDestroy() {
        super.onDestroy();
        releaseMediaPlayer();
        doCleanUp();
    }

    private void releaseMediaPlayer() {
        if (mMediaPlayer != null) {
            mMediaPlayer.release();
            mMediaPlayer = null;
        }
    }

    private void doCleanUp() {
        mVideoWidth = 0;
        mVideoHeight = 0;
        mIsVideoReadyToBePlayed = false;
        mIsVideoSizeKnown = false;
    }

    private void startVideoPlayback() {
        Log.v(TAG, "startVideoPlayback");
        holder.setFixedSize(mVideoWidth, mVideoHeight);
        mMediaPlayer.start();
    }
}

现在是下一节课

MainMenu.java


package com.videostreaming.player;

import android.app.Activity;
import android.content.Intent;
import android.os.Bundle;
import android.view.View;
import android.widget.Button;

public class MainMenu extends Activity {

    Button btn_videoViewDemo1;
    Button btn_videoViewDemo2;
    Button btn_MediaPlayerDemo1;    
    Button btn_MediaPlayerDemo2;

    @Override
    protected void onCreate(Bundle savedInstanceState) {
        // TODO Auto-generated method stub
        super.onCreate(savedInstanceState);
        setContentView(R.layout.main);

        btn_videoViewDemo1 = (Button)findViewById(R.id.btn_videoViewDemo1);
        btn_videoViewDemo2 = (Button)findViewById(R.id.btn_videoViewDemo2);
        btn_MediaPlayerDemo1 = (Button)findViewById(R.id.btn_MediaPlayerDemo1);
        btn_MediaPlayerDemo2 = (Button)findViewById(R.id.btn_MediaPlayerDemo2);

        btn_videoViewDemo1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent Navigation1 = new Intent(MainMenu.this,streamplayer.class);
                Navigation1.putExtra("video",1);
                startActivity(Navigation1);             
            }
        });

        btn_videoViewDemo2.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent Navigation1 = new Intent(MainMenu.this,streamplayer.class);
                Navigation1.putExtra("video",2);
                startActivity(Navigation1);
            }
        });

        btn_MediaPlayerDemo1.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent Navigation1 = new Intent(MainMenu.this,MediaPlayerDemo_Video.class);
                Navigation1.putExtra("video",1);
                startActivity(Navigation1);             
            }
        });

        btn_MediaPlayerDemo2.setOnClickListener(new View.OnClickListener(){
            @Override
            public void onClick(View v) {
                // TODO Auto-generated method stub
                Intent Navigation1 = new Intent(MainMenu.this,MediaPlayerDemo_Video.class);
                Navigation1.putExtra("video",2);
                startActivity(Navigation1);             
            }
        });     
    }
}

streamplayer.java

package com.videostreaming.player;

import android.app.Activity;
import android.content.Intent;
import android.net.Uri;
import android.os.Bundle;
import android.view.View;
import android.widget.MediaController;
import android.widget.Toast;
import android.widget.VideoView;

public class streamplayer extends Activity {
    /** Called when the activity is first created. */

     private String path1 = "http://podcast.20min-tv.ch/podcast/20min/199733.mp4";
     //private String path2 = "http://podcast.20min-tv.ch/podcast/20min/199752.mp4";
     private String path2 = "http://podcast.20min-tv.ch/podcast/20min/199693.mp4";
     private String path = "";

     //// Method 1 - Default Method
     private VideoView mVideoView;

     private Bundle bdlReceivedData = null;
     private Intent self = null;

    @Override
    public void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);

        try
        {
            setContentView(R.layout.videoview);
            mVideoView = (VideoView) findViewById(R.id.surface_view);

            self = this.getIntent();
            bdlReceivedData = self.getExtras();

            if (bdlReceivedData != null && bdlReceivedData.getInt("video") > 0)
            {
                if (bdlReceivedData.getInt("video") == 1)
                {
                    Toast.makeText(streamplayer.this,"playing Video 1", Toast.LENGTH_SHORT);
                    path = path1;
                }
                else if (bdlReceivedData.getInt("video") == 2)
                {
                    Toast.makeText(streamplayer.this,"playing Video 2", Toast.LENGTH_SHORT);
                    path = path2;
                }

                /*
                 * Alternatively,for streaming media you can use
                 * mVideoView.setVideoURI(Uri.parse(URLstring));
                 */

                //mVideoView.setVideoPath(path1);
                ///ELSE
                mVideoView.setVideoURI(Uri.parse(path));                
                mVideoView.setMediaController(new MediaController(this));
                mVideoView.requestFocus();              
                mVideoView.postInvalidateDelayed(100);        
                mVideoView.start();

            }

        }catch (Exception e) {
            // TODO: handle exception
            e.printStackTrace();
            Toast.makeText(streamplayer.this,"Error Occured:- " + e.getMessage(),Toast.LENGTH_SHORT).show();
        } 
    }
}

mediaplayer_2.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="match_parent"
    android:layout_height="match_parent">

    <SurfaceView android:id="@+id/surface"
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center">
    </SurfaceView>

</LinearLayout>

videoview.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent">

    <VideoView 
        android:id="@+id/surface_view" 
        android:layout_width="fill_parent"
        android:layout_height="fill_parent"
        android:layout_gravity="center"/>

</LinearLayout>

main.xml

<?xml version="1.0" encoding="utf-8"?>
<LinearLayout xmlns:android="http://schemas.android.com/apk/res/android"
    android:orientation="vertical"
    android:layout_width="fill_parent"
    android:layout_height="fill_parent" >

<TextView  
    android:layout_width="fill_parent" 
    android:layout_height="wrap_content" 
    android:text="@string/hello"/>

    <Button
        android:id="@+id/btn_videoViewDemo1"
        android:layout_width="fill_parent"
        android:layout_height="40dip"
        android:text="VideoView1"
        android:layout_margin="10dip">
    </Button>

    <Button
        android:id="@+id/btn_videoViewDemo2"
        android:layout_width="fill_parent"
        android:layout_height="40dip"
        android:text="VideoView2"
        android:layout_margin="10dip">
    </Button>

    <Button
        android:id="@+id/btn_MediaPlayerDemo1"
        android:layout_width="fill_parent"
        android:layout_height="40dip"
        android:text="MediaPlayerDemo1"
        android:layout_margin="10dip">
    </Button>



    <Button
        android:id="@+id/btn_MediaPlayerDemo2"
        android:layout_width="fill_parent"
        android:layout_height="40dip"
        android:text="MediaPlayerDemo2"
        android:layout_margin="10dip">
    </Button>


</LinearLayout>

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接