这今天做电视端项目,需要写一个可以播放组播流的播放器,可以直接播放像CCTV频道的电视节目。
我们先来说说组播流以及它所用到的协议;
组播:一种组播流分发节点接收组播流的方法,效保证了组播流传输的可靠性以及组播业务不发生间断,并保证了双组播源在组网中的应用;
流媒体协议: (rtp/rtcp/rtsp/rtmp/mms/hls)
这里我在网上找到了一篇很好的博客。里面专门详细讲解流媒体协议。地址: 流媒体协议介绍(rtp/rtcp/rtsp/rtmp/mms/hls)
如果要播放流媒体,我们也可以搭建一个VLC的服务器,来向网段中分发流,我之前写过关于VLC搭建流媒体的博客。以供大家参考; 使用VLC搭建组播服务器,在客户端进行播放
闲话少说,上代码;
这是从vitaimo官网下载的工程依赖,initActivity,其目录结构如下:
将他导入项目后作为工程依赖即可。
下面就是播放器代码:
import io.vov.vitamio.ChannelChangeImp;
import io.vov.vitamio.LibsChecker;
import io.vov.vitamio.MediaPlayer;
import io.vov.vitamio.MediaPlayer.OnBufferingUpdateListener;
import io.vov.vitamio.MediaPlayer.OnCompletionListener;
import io.vov.vitamio.MediaPlayer.OnPreparedListener;
import io.vov.vitamio.SetChannelList;
import java.util.ArrayList;
import java.util.List;
import android.app.Activity;
import android.content.Context;
import android.graphics.Matrix;
import android.graphics.SurfaceTexture;
import android.media.AudioManager;
import android.os.Bundle;
import android.view.KeyEvent;
import android.view.Surface;
import android.view.TextureView;
import android.view.View;
import android.view.View.OnFocusChangeListener;
import android.widget.ListView;
import android.widget.Toast;
public class PlayAcy extends Activity implements OnBufferingUpdateListener,
OnCompletionListener, OnPreparedListener,
TextureView.SurfaceTextureListener {
private static final String TAG = PlayAcy.class.getSimpleName();
private TextureView mTextureView;
private ListView lvAcyPlay;
private String path;
private Surface surf;
private MediaPlayer mMediaPlayer;
private boolean mIsVideoReadyToBePlayed = false;
SurfaceTexture surfaceTexture;
public List ChannelName = new ArrayList();
public List ChannelAddress = new ArrayList();
// 播放位置
private int position = 0;
private SetChannelList setChannelList;
private ChannelChangeImp channelChangeImp;
private Context mContext;
private Adapter adapter;
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
if (!LibsChecker.checkVitamioLibs(this))
return;
setContentView(R.layout.acy_play);
mContext = this;
initView();
initData();
InitPlayer();
}
private void initView() {
lvAcyPlay = (ListView) findViewById(R.id.lv_acy_play);
}
private void initData() {
setChannelList = new SetChannelList(mContext);
channelChangeImp = new ChannelChangeImp();
// 添加频道名称
ChannelName.add("CCTV1");
ChannelName.add("CCTV3");
ChannelName.add("UDP");
//
setChannelList.setChannelNameList(ChannelName);
// 添加播放地址
ChannelAddress.add("http://ivi.bupt.edu.cn/hls/cctv1hd.m3u8");
ChannelAddress.add("http://ivi.bupt.edu.cn/hls/cctv3hd.m3u8");
ChannelAddress.add("udp://@239.255.255.255:1234");
//
setChannelList.setChannelAddressList(ChannelAddress);
adapter = new Adapter(mContext, ChannelName);
lvAcyPlay.setAdapter(adapter);
}
public void InitPlayer() {
path = ChannelAddress.get(2);
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(String path) {
channelChangeImp.release(mMediaPlayer);
try {
mMediaPlayer = new MediaPlayer(this, true);
mMediaPlayer.setDataSource(path);
if (surf == null) {
surf = new Surface(surfaceTexture);
}
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) {
e.printStackTrace();
}
}
@Override
public void onSurfaceTextureAvailable(SurfaceTexture surface, int width,
int height) {
surfaceTexture = surface;
playVideo(ChannelAddress.get(0));
}
@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() {
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)) {
newWidth = viewWidth;
newHeight = (int) (viewWidth * aspectRatio);
} else {
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.postTranslate(xoff, yoff);
mTextureView.setTransform(txform);
}
@Override
protected void onDestroy() {
if (channelChangeImp != null)
channelChangeImp.release(mMediaPlayer);
super.onDestroy();
}
@Override
public boolean onKeyDown(int keyCode, KeyEvent event) {
if (keyCode == 19) {
position = ++position;
} else if (keyCode == 20) {
position = --position;
}
if (position < ChannelAddress.size() && position >= 0) {
playVideo(ChannelAddress.get(position));
} else if (position > ChannelAddress.size()) {
position = ChannelAddress.size() - 1;
} else if (position < 0) {
position = 0;
}
return super.onKeyDown(keyCode, event);
}
@Override
protected void onPause() {
super.onPause();
channelChangeImp.release(mMediaPlayer);
}
}
/**
* 换台接口
*
* @author Administrator
*
*/
public class ChannelChangeImp {
private boolean mIsVideoSizeKnown = false;
private boolean mIsVideoReadyToBePlayed = false;
public ChannelChangeImp() {
super();
}
public void release(MediaPlayer mMediaPlayer) {
if (mMediaPlayer != null) {
mMediaPlayer.release();
mMediaPlayer = null;
}
doCleanUp();
}
private void doCleanUp() {
mIsVideoReadyToBePlayed = false;
mIsVideoSizeKnown = false;
}
}
/**
* 设置频道列表
*
* @author Administrator
*
*/
public class SetChannelList {
// 频道名称
private List ChannelName = new ArrayList();
// 频道地址
private List ChannelAddress = new ArrayList();
private Context context;
public SetChannelList(Context mContext) {
super();
this.context = mContext;
}
// 设置频道名称列表
public void setChannelNameList(List mChannelName) {
this.ChannelName = mChannelName;
}
// 设置频道地址列表
public void setChannelAddressList(List mChannelAddress) {
this.ChannelAddress = mChannelAddress;
}
public List getChannelNameList() {
if (ChannelName.size() == 0 && context != null) {
Toast.makeText(context, "頻道名称为空", 0).show();
} else if (ChannelName.size() >= 0 && context != null) {
return ChannelName;
}
return ChannelName;
}
public List getChannelAddressList() {
if (ChannelAddress.size() == 0) {
Toast.makeText(context, "頻道地址为空", 0).show();
} else if (ChannelName.size() >= 0) {
return ChannelAddress;
}
return ChannelAddress;
}
}
至此,共勉。