Stream live video from phone to phone using socket fd
我是 android 编程的新手,发现自己陷入了困境。我一直在研究各种将实时视频从手机传输到手机的方法,并且似乎大部分功能都可以使用,当然最重要的部分除外:播放流。它似乎是从一部手机发送流,但第二部手机无法播放流。
这是游戏方的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 | public class VideoPlayback extends Activity implements Callback { MediaPlayer mp; private SurfaceView mPreview; private SurfaceHolder holder; private TextView mTextview; public static final int SERVERPORT = 6775; public static String SERVERIP="192.168.1.126"; Socket clientSocket; private Handler handler = new Handler(); /** Called when the activity is first created. */ @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); mPreview = (SurfaceView) findViewById(R.id.surfaceView1); mTextview = (TextView) findViewById(R.id.textView1); holder = mPreview.getHolder(); holder.addCallback(this); holder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); mTextview.setText("Attempting to connect"); mp = new MediaPlayer(); Thread t = new Thread(){ public void run(){ try { clientSocket = new Socket(SERVERIP,SERVERPORT); handler.post(new Runnable() { @Override public void run() { mTextview.setText("Connected to server"); } }); handler.post(new Runnable() { @Override public void run() { try { ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(clientSocket); pfd.getFileDescriptor().sync(); mp.setDataSource(pfd.getFileDescriptor()); pfd.close(); mp.setDisplay(holder); mp.prepareAsync(); mp.start(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }); } catch (UnknownHostException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } } }; t.start(); } |
这是流媒体端的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 | public class VideoStreaming extends Activity{ // User Interface Elements VideoView mView; TextView connectionStatus; SurfaceHolder mHolder; // Video variable MediaRecorder recorder; // Networking variables public static String SERVERIP=""; public static final int SERVERPORT = 6775; private Handler handler = new Handler(); private ServerSocket serverSocket; /** Called when the activity is first created. */ @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.main); // Define UI elements mView = (VideoView) findViewById(R.id.video_preview); connectionStatus = (TextView) findViewById(R.id.connection_status_textview); mHolder = mView.getHolder(); mHolder.setType(SurfaceHolder.SURFACE_TYPE_PUSH_BUFFERS); SERVERIP ="192.168.1.126"; // Run new thread to handle socket communications Thread sendVideo = new Thread(new SendVideoThread()); sendVideo.start(); } public class SendVideoThread implements Runnable{ public void run(){ // From Server.java try { if(SERVERIP!=null){ handler.post(new Runnable() { @Override public void run() { connectionStatus.setText("Listening on IP:" + SERVERIP); } }); serverSocket = new ServerSocket(SERVERPORT); while(true) { //listen for incoming clients Socket client = serverSocket.accept(); handler.post(new Runnable(){ @Override public void run(){ connectionStatus.setText("Connected."); } }); try{ // Begin video communication final ParcelFileDescriptor pfd = ParcelFileDescriptor.fromSocket(client); handler.post(new Runnable(){ @Override public void run(){ recorder = new MediaRecorder(); recorder.setVideoSource(MediaRecorder.VideoSource.CAMERA); recorder.setOutputFormat(MediaRecorder.OutputFormat.THREE_GPP); recorder.setOutputFile(pfd.getFileDescriptor()); recorder.setVideoFrameRate(20); recorder.setVideoSize(176,144); recorder.setVideoEncoder(MediaRecorder.VideoEncoder.H263); recorder.setPreviewDisplay(mHolder.getSurface()); try { recorder.prepare(); } catch (IllegalStateException e) { // TODO Auto-generated catch block e.printStackTrace(); } catch (IOException e) { // TODO Auto-generated catch block e.printStackTrace(); } recorder.start(); } }); } catch (Exception e) { handler.post(new Runnable(){ @Override public void run(){ connectionStatus.setText("Oops.Connection interrupted. Please reconnect your phones."); } }); e.printStackTrace(); } } } else { handler.post(new Runnable() { @Override public void run(){ connectionStatus.setText("Couldn't detect internet connection."); } }); } } catch (Exception e){ handler.post(new Runnable() { @Override public void run() { connectionStatus.setText("Error"); } }); e.printStackTrace(); } // End from server.java } } |
我在尝试创建 MediaPLayer 时收到以下错误
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 | 05-24 16:25:39.360: ERROR/MediaPlayerService(88): offset error 05-24 16:25:39.360: ERROR/MediaPlayer(11895): Unable to to create media player 05-24 16:25:39.360: WARN/System.err(11895): java.io.IOException: setDataSourceFD failed.: status=0x80000000 05-24 16:25:39.360: WARN/System.err(11895): at android.media.MediaPlayer.setDataSource(Native Method) 05-24 16:25:39.360: WARN/System.err(11895): at android.media.MediaPlayer.setDataSource(MediaPlayer.java:811) 05-24 16:25:39.360: WARN/System.err(11895): at com.conti.VideoPlayBack.VideoPlayback$1$2.run(VideoPlayback.java:63) 05-24 16:25:39.360: WARN/System.err(11895): at android.os.Handler.handleCallback(Handler.java:587) 05-24 16:25:39.360: WARN/System.err(11895): at android.os.Handler.dispatchMessage(Handler.java:92) 05-24 16:25:39.360: WARN/System.err(11895): at android.os.Looper.loop(Looper.java:132) 05-24 16:25:39.360: WARN/System.err(11895): at android.app.ActivityThread.main(ActivityThread.java:4025) 05-24 16:25:39.360: WARN/System.err(11895): at java.lang.reflect.Method.invokeNative(Native Method) 05-24 16:25:39.360: WARN/System.err(11895): at java.lang.reflect.Method.invoke(Method.java:491) 05-24 16:25:39.360: WARN/System.err(11895): at com.android.internal.os.ZygoteInit$MethodAndArgsCaller.run(ZygoteInit.java:841) 05-24 16:25:39.360: WARN/System.err(11895): at com.android.internal.os.ZygoteInit.main(ZygoteInit.java:599) 05-24 16:25:39.360: WARN/System.err(11895): at dalvik.system.NativeStart.main(Native Method) |
有人能解决这个问题吗?提前致谢!
我找到了一个开源项目来实现我正在尝试的东西。它通过 IP 摄像机处理带有元数据的视频。虽然它不会直接将视频发送到手机,但它会为各种设备播放视频以供观看。源代码可以在以下项目页面 http://code.google.com/p/ipcamera-for-android/ 中找到。
在 Android 4.4 中,还有另一种播放实时 MJPEG 流的方法。您正在播放的流应该由其他设备通过 UDP 在端口上广播。假设我们有一个在 192.168.0.101:8080 上广播的流。我们可以通过在布局中添加 WebView 来播放流。然后在我们的活动中,我们执行以下操作:
1 2 3 4 5 6 7 8 9 10 | @Override public void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.mjpeg_activity); // Grab instance of WebView WebView webView = (WebView)findViewById(R.id.webViewStream); // Set page content for webview webView.loadData("<html><head><meta name='viewport' content='target-densitydpi=device-dpi,initial-scale=1,minimum-scale=1,user-scalable=yes'/></head><body><center><img src="http://192.168.0.101:8080/" alt="Stream" align="middle"></center></body></html>","text/html", null); webView.getSettings().setBuiltInZoomControls(true); } |
在内容中,我们告诉网页使用设备的 dpi。为了支持用户在网页上捏缩放,我添加了以下 initial-scale=1,minimum-scale=1,user-scalable=yes。最初,图像是原始大小,不能变小。用户现在可以缩放以放大图像并缩小到原始大小。删除最小比例将使用户可以完全控制缩放,但可能会导致图像太小而无法找到。
您必须设置记录器输出格式 8(MPEG-2TS,仅适用于 android 版本 3.0)。
在这种情况下,以这种格式录制视频并将流发送到其他手机并将其保存在文件中。在文件中写入一些数据后播放,就可以看到直播了。
注意-您不能直接通过套接字文件描述符播放,因为套接字 fd 不可搜索。如果您使用套接字 fd,您将收到"偏移错误"。可以录制,但播放受限。
从以下网址下载源代码:http://code.google.com/p/ipcamera-for-android/
(https://github.com/Teaonly/android-eye)
首先构建 jni(使用 ndk-build 和 cygwin)
我可以成功构建这个项目
看看 Streaming to the Android MediaPlayer ,它可能对如何进行流式传输有一些有用的提示。我怀疑问题是Android试图在文件中寻找,但作为网络套接字 - 它不能。也许某种支持查找的磁盘/内存缓冲区会有所帮助?