【问题标题】:Live video streaming application on AndroidAndroid上的实时视频流应用程序
【发布时间】:2011-03-17 12:59:56
【问题描述】:

我正在尝试构建一个实时视频流应用程序,用于从 Android 流式传输实时视频。

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

但是,当我运行我的应用程序和流媒体时,服务器端会出现 2-3 秒的延迟。

为什么会出现这种延迟?我需要刷新任何内部缓冲区吗?除了使用 MediaRecorder 类之外,还有其他流式传输视频的方法吗?

【问题讨论】:

  • 嗨!我也在努力实现同样的目标。您能否详细介绍一下您是如何使用 mediarecorder 做到的?
  • 能否提供更多细节?您使用哪个库(ffmpeg、live555 等...)、哪种方法(RTP、RTSP ...)?来自flock.dux的评论
  • 嗨 Vladimir Ivanov ....你能建议我在 android 中直播视频流的整个过程吗..我不知道如何开始直播视频流...vv 谢谢你提前……等待你的回复……
  • 你知道如何将实时视频从android发送到服务器吗?和 Skype 一样。
  • 请帮我做这件事..我无法将视频流式传输到服务器..如果你提供你的演示代码..它会非常有帮助提前谢谢

标签: android video streaming live


【解决方案1】:

如果您设置为来自 Android 的 RTMP 流媒体,最好的解决方案是 MediaCodec + FFmpeg + librtmp。这避免了任何骇人听闻的“检测字节流中的 NAL 单元”业务,但需要 Android 4.3。在冰球要去的地方滑冰......

我开发了一个open source SDK 来演示使用FFmpeg + librtmp as pre-built shared libraries 进行RTMP 流式传输。 SDK 专注于 HLS 流式传输,但提供 RTMP 支持。

如果您想帮助自己为 Android 构建 FFmpeg(带或不带 librtmp),check out my guide

【讨论】:

  • 在没有 kickflip 帐户的情况下可以使用吗?我可以流式传输到任何 HLS 客户端吗?
  • 当然!尝试使用 AVRecorder.javaHLSFileMonitor.java 来监控 .ts 段和 .m3u8 文件何时可以上传。 Broadcaster.java 是 Kickflip 参考实现。
【解决方案2】:

如果 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>
    

    ma​​in.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>
    

    【讨论】:

      猜你喜欢
      • 2021-06-28
      • 2015-07-15
      • 2014-07-15
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 1970-01-01
      • 2014-03-18
      • 1970-01-01
      相关资源
      最近更新 更多