最近,在写一个项目需要用到VLC搭建组播流服务器作为视频源,我这里使用了UDP协议。UDP协议主要作用是将网络数据流量压缩成数据报的形式,一个典型的数据报就是一个二进制的传输单位。每一个数据报的前8个字节用来包含报头信息,剩余字节则用来包含具体的传输数据。总体来说:UDP任然不失为一项非常实用和可行的网络传输协议。
搭建VLC服务器的流程:
1.打开VLC;
2.点击右上角"媒体",在下拉列表中选择"流(s)"
3.打开后,点击"添加",选择要播放的视频资源。我这里是《摆渡人》;
4.选择好视频源后,点击"串流"选项;
5.查看视频路径,以及文件类型,然后点击"下一个"
6.勾选"在本地显示",选择"文件"下拉框,选择协议模式,我这里是UDP协议;
7.点击"添加",选择目标播放路径(这里的路径我研究了好久,其实有两种地址:1.播放客户端的IP地址; 2.IP组播地址,IP组播地址是一个D类IP地址,范围从224.0.0.0~239.255.255.255 其中还有很多是为特殊地质保留的。而224.0.0.0~224.0.0.255的地址最好不要用,因为他们大多数是为了特殊目的保存的)。点击"下一步".
8.将"激活转码"去掉,点击"下一个"
9. 点击"流",即可进行播放。
这是VLC的本地播放的视频效果:
客户端代码(使用维他命的播放框架):
public class PlayAcyVertical extends Activity implements
OnBufferingUpdateListener, OnCompletionListener, OnPreparedListener,
TextureView.SurfaceTextureListener {
private static final String TAG = PlayAcyVertical.class.getSimpleName();
private TextureView mTextureView;
private String path = "udp://@239.255.255.255:1234";
private Surface surf;
private boolean mIsVideoSizeKnown = false;
private boolean mIsVideoReadyToBePlayed = false;
SurfaceTexture surfaceTexture;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!LibsChecker.checkVitamioLibs(this))
return;
setContentView(R.layout.acy_play_v);
InitPlayer();
}
public void InitPlayer() {
mTextureView = (TextureView) findViewById(R.id.surface);
mTextureView.setSurfaceTextureListener(this);
mTextureView.setFocusable(true);
mTextureView.setOnFocusChangeListener(new OnFocusChangeListener() {
@Override
public void onFocusChange(View v, boolean hasFocus) {
if (hasFocus == true) {// 如果获得到焦点
Toast.makeText(getApplicationContext(), "获取到焦点", 0).show();
} else {
Toast.makeText(getApplicationContext(), "没有获取到焦点", 0)
.show();
}
}
});
}
private void playVideo() {
doCleanUp();
try {
mMediaPlayer = new MediaPlayer(this, true);
mMediaPlayer.setDataSource(path);
if (surf == null) {
surf = new Surface(surfaceTexture);
}
Toast.makeText(getApplicationContext(), "surf=" + surf, 0).show();
mMediaPlayer.setSurface(surf);
mMediaPlayer.prepareAsync();
mMediaPlayer.setTimedTextShown(true);
mMediaPlayer.setOnBufferingUpdateListener(this);// 设置缓冲监听
mMediaPlayer.setOnCompletionListener(this);// 设置播放完毕监听
mMediaPlayer.setOnPreparedListener(this);// 设置准备完毕监听
mMediaPlayer.setBufferSize(512 * 1024);
mMediaPlayer.setVideoQuality(MediaPlayer.VIDEOQUALITY_MEDIUM);
setVolumeControlStream(AudioManager.STREAM_MUSIC);
mMediaPlayer.start();
} catch (Exception e) {
Toast.makeText(getApplicationContext(), e.getMessage(), 0).show();
}
}
private void doCleanUp() {
mIsVideoReadyToBePlayed = false;
mIsVideoSizeKnown = false;
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height) {
surfaceTexture = surface;
playVideo();
}
@Override
public boolean onSurfaceTextureDestroyed(SurfaceTexture arg0) {
return false;
}
@Override
public void onSurfaceTextureSizeChanged(SurfaceTexture arg0, int arg1,
int arg2) {
}
@Override
public void onSurfaceTextureUpdated(SurfaceTexture arg0) {
}
@Override
public void onPrepared(MediaPlayer mp) {
mIsVideoReadyToBePlayed = true;
if (mIsVideoReadyToBePlayed) {
startVideoPlayback();
}
}
@Override
public void onCompletion(MediaPlayer mp) {
}
@Override
public void onBufferingUpdate(MediaPlayer mp, int percent) {
}
private void startVideoPlayback() {
Log.v(TAG, "startVideoPlayback");
adjustAspectRatio(mMediaPlayer.getVideoWidth(),
mMediaPlayer.getVideoHeight());
mMediaPlayer.start();
}
public void adjustAspectRatio(int videoWidth, int videoHeight) {
int viewWidth = mTextureView.getWidth();
int viewHeight = mTextureView.getHeight();
double aspectRatio = (double) videoHeight / videoWidth;
int newWidth, newHeight;
if (viewHeight > (int) (viewWidth * aspectRatio)) {
// limited by narrow width; restrict height
newWidth = viewWidth;
newHeight = (int) (viewWidth * aspectRatio);
} else {
// limited by short height; restrict width
newWidth = (int) (viewHeight / aspectRatio);
newHeight = viewHeight;
}
int xoff = (viewWidth - newWidth) / 2;
int yoff = (viewHeight - newHeight) / 2;
Matrix txform = new Matrix();
mTextureView.getTransform(txform);
txform.setScale((float) newWidth / viewWidth, (float) newHeight
/ viewHeight);
// txform.postRotate(10); // just for fun
txform.postTranslate(xoff, yoff);
mTextureView.setTransform(txform);
}
@Override
protected void onDestroy() {
// TODO Auto-generated method stub
super.onDestroy();
releaseMediaPlayer();
doCleanUp();
}
private void releaseMediaPlayer() {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
}
@Override
protected void onPause() {
// TODO Auto-generated method stub
super.onPause();
releaseMediaPlayer();
doCleanUp();
}
@Override
public boolean onOptionsItemSelected(MenuItem item) {
int id = item.getItemId();
if (id == R.id.action_settings) {
return true;
}
return super.onOptionsItemSelected(item);
}
}
acy_paly_v.xml: