Android 实现视频字幕Subtitle和横竖屏切换示例

作者:高-富-帅 时间:2023-02-06 07:29:37 

系统自带的VideoView有些视频格式不支持,那么我们可以用第三方实现的VideoView替代系统的来播放视频,比较流行的有ijkplayer、vitamio。

最近有个需求就是需要给视频添加字幕,其实也挺简单的。字幕比较常用的格式是srt,实际它就是文本,把它解析出来,然后根据时间再展示就OK。还有就是实现了即使旋转按钮关闭,根据方向感应器也能做到横竖屏切换。

本文用的是系统VideoView,然后播放sd卡中的视频来作为演示(源码中带有f2.mp4和f2.srt,运行时拷贝到sd卡就行)。下面简单介绍一下源码:

MainActivity包括显示字幕和如何实现横竖屏如何切换:


public class SubtitleActivity extends Activity implements View.OnClickListener,OnTouchListener{
private VideoView videoView ;
TextView tvSrt, mCurrentTime,mTotalTime,resolution_switch,mediacontroller_file_name;
ImageView mediacontroller_play_pause,switch_screen;
private SeekBar progress_seekbar;
private AudioManager mAM;
private long totalDuration;
private boolean mShowing = true, mDragging,isResolution;
private static final int PARSE_SRT = 0;
private static final int FADE_OUT = 1;
private static final int SHOW_PROGRESS = 2;
private static final int CHANGE_VIDEOVIEW_BG = 3;
private static final int SCREEN_ORIENTATION_USER = 4;
private static final int sDefaultTimeout = 3000;
private RelativeLayout videoview_layout, mMediaController;
private ListView resolution_listview;
private boolean isPortraint = true;
private static int LockScreen = -1;// 用于记录是否关闭屏幕旋转,0为关闭1为开启
private int screenWidth,videoViewHeight;
List<VideoPathObject> videopathList=new ArrayList<VideoPathObject>();
Handler mHandler=new Handler(){
public void handleMessage(Message msg){
long pos;
switch (msg.what) {
case PARSE_SRT:
SrtParser.showSRT(videoView,tvSrt) ;
//每隔500ms执行一次showSRT(),根据时间匹配显示哪句字幕
mHandler.sendEmptyMessageDelayed(0, 500);
break;
case FADE_OUT:
showOrHideController();
break;
case SHOW_PROGRESS:
pos = setControllerProgress();
if (!mDragging && mShowing) {
msg = obtainMessage(SHOW_PROGRESS);
sendMessageDelayed(msg, 1000 - (pos % 1000));
}
break;
case CHANGE_VIDEOVIEW_BG:
videoView.setBackgroundColor(0x00000000);
break;
}
}
};
@Override
protected void onCreate(Bundle savedInstanceState) {
super.onCreate(savedInstanceState);
setContentView(R.layout.activity_subtitle);
videoView = (VideoView)this.findViewById(R.id.videoView );
mAM = (AudioManager) getSystemService(Context.AUDIO_SERVICE);
screenWidth = APPApplication.screenWidth;
videoViewHeight = screenWidth * 9 / 16;
tvSrt = (TextView)findViewById(R.id.srt);//项目中显示字幕的控件
mediacontroller_file_name= (TextView)findViewById(R.id.mediacontroller_file_name);
//String[]splitStr=Constant.videoUrl1.split("/");
//mediacontroller_file_name.setText(splitStr[splitStr.length-1]);
mTotalTime = (TextView) findViewById(R.id.mediacontroller_time_total);
mCurrentTime = (TextView) findViewById(R.id.mediacontroller_time_current);
resolution_switch = (TextView) findViewById(R.id.resolution_switch);
mediacontroller_play_pause = (ImageView) findViewById(R.id.mediacontroller_play_pause);
switch_screen = (ImageView) findViewById(R.id.switch_screen);
videoview_layout = (RelativeLayout) findViewById(R.id.videoview_layout);
mediacontroller_play_pause.setOnClickListener(this);
progress_seekbar = (SeekBar) findViewById(R.id.mediacontroller_seekbar);
videoview_layout = (RelativeLayout) findViewById(R.id.videoview_layout);
mMediaController = (RelativeLayout) findViewById(R.id.media_controller);
resolution_listview = (ListView) findViewById(R.id.resolution_listview);
resolution_switch.setOnClickListener(this);
videoView.setOnTouchListener(this);
progress_seekbar.setOnSeekBarChangeListener(mSeekListener);
LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, videoViewHeight);
videoview_layout.setLayoutParams(params);
try {
// 1代表开启自动旋转true,0代表未开启自动旋转false
// Settings.System.getInt(mContext.getContentResolver(),Settings.System.ACCELEROMETER_ROTATION,0);
LockScreen = Settings.System.getInt(getContentResolver(),
Settings.System.ACCELEROMETER_ROTATION);
} catch (SettingNotFoundException e) {
// TODO Auto-generated catch block
e.printStackTrace();
}
//String rawUri = "android.resource://" + getPackageName() + "/" + R.raw.renwei;
Uri uri = Uri.parse(Constant.videoUrl1);
//设置视频控制器
// videoView.setMediaController(new MediaController(this));
//播放完成回调
videoView.setOnCompletionListener( new MyPlayerOnCompletionListener());
videoView.setOnPreparedListener(new OnPreparedListener() {
//@Override
public void onPrepared(MediaPlayer mp) {
totalDuration=videoView.getDuration();
if (mTotalTime != null)
mTotalTime.setText("/"+generateTime(totalDuration));
}
});
//设置视频路径
videoView.setVideoURI(uri);
//开始播放视频
videoView.start();
SrtParser.parseSrt(this);
SrtParser.showSRT(videoView,tvSrt) ;
mHandler.sendEmptyMessageDelayed(0, 500);
initVideoResolution();
}
private void initVideoResolution(){
VideoPathObject object1=new VideoPathObject();
object1.videoStatus="超清";
videopathList.add(object1);
VideoPathObject object2=new VideoPathObject();
object2.videoStatus="高清";
videopathList.add(object2);
VideoPathObject object3=new VideoPathObject();
object3.videoStatus="标清";
videopathList.add(object3);
switchResolution(videopathList);
}
class MyPlayerOnCompletionListener implements MediaPlayer.OnCompletionListener {
@Override
public void onCompletion(MediaPlayer mp) {
Toast.makeText( SubtitleActivity.this, "播放完成了", Toast.LENGTH_SHORT).show();
}
}
private OnSeekBarChangeListener mSeekListener = new OnSeekBarChangeListener() {
public void onStartTrackingTouch(SeekBar bar) {
mDragging = true;
mHandler.removeMessages(SHOW_PROGRESS);
mAM.setStreamMute(AudioManager.STREAM_MUSIC, true);
}
public void onProgressChanged(SeekBar bar, int progress,
boolean fromuser) {
if (!fromuser)
return;
int newposition = (int)(totalDuration * progress) / 1000;
String time = generateTime(newposition);
videoView.seekTo(newposition);
mCurrentTime.setText(time);
}
public void onStopTrackingTouch(SeekBar bar) {
videoView.seekTo(((int)totalDuration * bar.getProgress()) / 1000);
hideMediaController(sDefaultTimeout);
mAM.setStreamMute(AudioManager.STREAM_MUSIC, false);
mDragging = false;
mHandler.sendEmptyMessageDelayed(SHOW_PROGRESS, 1000);
}
};
private void switchResolution(final List<VideoPathObject> videopathList) {
resolution_switch
.setText(videopathList.get(videopathList.size() - 1).videoStatus);
mediacontroller_play_pause.setImageResource(R.drawable.player_play);
final ResolutionAdapter adapter = new ResolutionAdapter(videopathList,
SubtitleActivity.this);
resolution_listview.setAdapter(adapter);
resolution_listview
.setOnItemClickListener(new AdapterView.OnItemClickListener() {
@Override
public void onItemClick(AdapterView<?> arg0, View arg1,
int position, long arg3) {
// TODO Auto-generated method stub
//// currentPosition = videoView.getCurrentPosition();
//currentPosition = videoView.getCurrentPosition();
//Log.d("gaolei", "currentPosition---------1------"
//+ currentPosition);
VideoPathObject pathObject = videopathList
.get(position);
////playVideo(pathObject.videoUrl);
//adapter.changePosition(position);
resolution_switch.setText(pathObject.videoStatus);
resolution_listview.setVisibility(View.GONE);
}
});
}
public void showOrHideController() {
if (mShowing) {
mHandler.removeMessages(SHOW_PROGRESS);
mHandler.removeMessages(FADE_OUT);
mMediaController.setVisibility(View.GONE);
resolution_listview.setVisibility(View.GONE);
mShowing = false;
} else {
mHandler.sendEmptyMessage(SHOW_PROGRESS);
mMediaController.setVisibility(View.VISIBLE);
hideMediaController(sDefaultTimeout);
mShowing = true;
}
}
public void hideMediaController(int sDefaultTimeout) {
mHandler.sendEmptyMessageDelayed(FADE_OUT, sDefaultTimeout);
}
private long setControllerProgress() {
if (videoView == null || mDragging)
return 0;
int position = videoView.getCurrentPosition();
if (progress_seekbar != null) {
if (totalDuration > 0) {
long pos = 1000L * position / totalDuration;
// Log.d("gaolei", "progress--------------" + pos);
progress_seekbar.setProgress((int) pos);
}
int percent = videoView.getBufferPercentage();
progress_seekbar.setSecondaryProgress(percent * 10);
}
if (mCurrentTime != null)
mCurrentTime.setText(generateTime(position));
return position;
}
private static String generateTime(long position) {
int totalSeconds = (int) (position / 1000);
int seconds = totalSeconds % 60;
int minutes = (totalSeconds / 60) % 60;
int hours = totalSeconds / 3600;
if (hours > 0) {
return String.format(Locale.US, "%02d:%02d:%02d", hours, minutes,
seconds).toString();
} else {
return String.format(Locale.US, "%02d:%02d", minutes, seconds)
.toString();
}
}
private void updatePausePlay() {
if (videoView.isPlaying()) {
videoView.pause();
mediacontroller_play_pause
.setImageResource(R.drawable.player_pause);
} else {
videoView.start();
mediacontroller_play_pause.setImageResource(R.drawable.player_play);
}
}
public void showResolution(View view) {
if (!isResolution) {
resolution_listview.setVisibility(View.VISIBLE);
isResolution = true;
} else {
resolution_listview.setVisibility(View.GONE);
isResolution = false;
}
}
public void onConfigurationChanged(Configuration newConfig) {
super.onConfigurationChanged(newConfig);
if (newConfig.orientation == Configuration.ORIENTATION_LANDSCAPE) {
changeToFullScreen();
Log.d("gaolei", "ORIENTATION_LANDSCAPE-------------");
}
if (newConfig.orientation == Configuration.ORIENTATION_PORTRAIT) {
changeToSmallScreen();
Log.d("gaolei", "ORIENTATION_PORTRAIT-------------");
}
}
public void switchScreen(View view) {
if (isPortraint) {
handToFullScreen();
} else {
handToSmallScreen();
}
}
public void handToSmallScreen() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_PORTRAIT);
changeToSmallScreen();
/**
* 这里点击按钮转屏,用户5秒内不转屏幕,将自动识别当前屏幕方向
*/
autoSwitchScreenOrientation();
}
public void handToFullScreen() {
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_SENSOR_LANDSCAPE);
changeToFullScreen();
autoSwitchScreenOrientation();
}
private void changeToFullScreen() {
isPortraint = false;
LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, APPApplication.screenWidth);
videoview_layout.setLayoutParams(params);
videoView.setLayoutParams(params);
WindowManager.LayoutParams windowparams = getWindow().getAttributes();
windowparams.flags |= WindowManager.LayoutParams.FLAG_FULLSCREEN;
getWindow().setAttributes(windowparams);
getWindow().addFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
switch_screen.setImageResource(R.drawable.player_switch_small);
}
private void changeToSmallScreen() {
isPortraint = true;
LayoutParams params = new RelativeLayout.LayoutParams(
LayoutParams.MATCH_PARENT, videoViewHeight);
videoview_layout.setLayoutParams(params);
videoView.setLayoutParams(params);
WindowManager.LayoutParams windowparams = getWindow().getAttributes();
windowparams.flags &= (~WindowManager.LayoutParams.FLAG_FULLSCREEN);
getWindow().setAttributes(windowparams);
getWindow()
.clearFlags(WindowManager.LayoutParams.FLAG_LAYOUT_NO_LIMITS);
switch_screen.setImageResource(R.drawable.player_switch_big);
}
public void autoSwitchScreenOrientation() {
//手动旋转屏幕,5s后会执行感应的方向
new Timer().schedule(new TimerTask() {
@Override
public void run() {
// TODO Auto-generated method stub
setRequestedOrientation(ActivityInfo.SCREEN_ORIENTATION_FULL_SENSOR);
Log.d("gaolei", "SCREEN_ORIENTATION_FULL_SENSOR");}
}, 5000);
}
@Override
public boolean onTouch(View v, MotionEvent event) {
// TODO Auto-generated method stub
switch (event.getAction()) {
case MotionEvent.ACTION_DOWN: {
showOrHideController();
break;
}
}
return false;
}
@Override
public void onClick(View view) {
// TODO Auto-generated method stub
switch(view.getId()){
case R.id.mediacontroller_play_pause:
Log.d("gaolei", "mediacontroller_play_pause");
updatePausePlay();
break;
case R.id.resolution_switch:
resolution_listview.setVisibility(View.VISIBLE);
break;
}
}
public void jumpToMain(View view){
startActivity(new Intent(this, MainActivity.class));
}
public void onRestart(){
super.onRestart();
videoView.start();
mediacontroller_play_pause.setImageResource(R.drawable.player_play);
}
public void onStop(){
super.onStop();
videoView.pause();
mediacontroller_play_pause.setImageResource(R.drawable.player_pause);
}
}

SrtParser就是解析字幕文件的算法:


public class SrtParser {
public static ArrayList<SRT>srtList;
public static int lastEndTime;
/**
* 解析SRT字幕文件
* 字幕路径
*/
public static void parseSrt(Context context) {
InputStream inputStream = null;
try {
//  inputStream=context.getResources().openRawResource(R.raw.renwei2);
 inputStream = new FileInputStream(Constant.srtUrl1);
 // TODO Auto-generated catch block
 BufferedReader br = new BufferedReader(new InputStreamReader(
  inputStream,"GB2312"));
 String line = null;
 srtList = new ArrayList<SRT>();
 StringBuffer sb = new StringBuffer();
 while ((line = br.readLine()) != null) {
 // Log.d("gaolei", "br.readLine()-----------"+br.readLine());
 if (!line.equals("")) {
  Log.d("gaolei","line-------------------"+ line);
  sb.append(line).append("@");
  continue;
 }
 Log.d("gaolei", "sb.toString()-----------"+sb.toString());
 String[] parseStrs = sb.toString().split("@");
 // 该if为了适应一开始就有空行以及其他不符格式的空行情况
 if (parseStrs.length < 3) {
  sb.delete(0, sb.length());// 清空,否则影响下一个字幕元素的解析</i>
  continue;
 }
 SRT srt = new SRT();
 // 解析开始和结束时间
 String timeTotime = parseStrs[1];
 int begin_hour = Integer.parseInt(timeTotime.substring(0, 2));
 int begin_mintue = Integer.parseInt(timeTotime.substring(3, 5));
 int begin_scend = Integer.parseInt(timeTotime.substring(6, 8));
 int begin_milli = Integer.parseInt(timeTotime.substring(9, 12));
 int beginTime = (begin_hour * 3600 + begin_mintue * 60 + begin_scend)
  * 1000 + begin_milli;
 int end_hour = Integer.parseInt(timeTotime.substring(17, 19));
 int end_mintue = Integer.parseInt(timeTotime.substring(20, 22));
 int end_scend = Integer.parseInt(timeTotime.substring(23, 25));
 int end_milli = Integer.parseInt(timeTotime.substring(26, 29));
 int endTime = (end_hour * 3600 + end_mintue * 60 + end_scend)
  * 1000 + end_milli;
 System.out.println("开始:" + begin_hour + ":" + begin_mintue +
  ":"
  + begin_scend + ":" + begin_milli + "=" + beginTime
  + "ms");
 System.out.println("结束:" + end_hour + ":" + end_mintue + ":"
  + end_scend + ":" + end_milli + "=" + endTime + "ms");
 // 解析字幕文字
 String srtBody = "";
 // 可能1句字幕,也可能2句及以上。
 for (int i = 2; i < parseStrs.length; i++) {
  srtBody += parseStrs[i]+ "\n";
 }
 // 删除最后一个"\n"
 srtBody = srtBody.substring(0, srtBody.length() - 1);
 // 设置SRT
 srt.setBeginTime(beginTime);
 srt.setEndTime(endTime);
 srt.setSrtBody(new String(srtBody.getBytes(), "UTF-8"));
 srtList.add(srt);
 sb.delete(0, sb.length());// 清空,否则影响下一个字幕元素的解析
 }
 lastEndTime=srtList.get(srtList.size()-1).getEndTime();
 br.close();
} catch (IOException e) {
 // TODO Auto-generated catch block
 e.printStackTrace();
}
//每隔500ms执行一次()取
}
public static void showSRT(VideoView videoView,TextView tvSrt) {
// Log.d("gaolei", "srt_map.size()--------------"+srt_map.size());
int currentPosition = videoView.getCurrentPosition();//vv是VideoView播放器
if(currentPosition>lastEndTime){
 tvSrt.setVisibility(View.GONE);
 return;
}
for(int i=0;i<srtList.size();i++){
 SRT srtbean =srtList.get(i);
 if (currentPosition > srtbean.getBeginTime()
  && currentPosition < srtbean.getEndTime()) {
 tvSrt.setText(srtbean.getSrtBody());
 //显示过的就删掉,提高查询效率
 srtList.remove(i);
 break;//找到后就没必要继续遍历下去,节约资源
 }
}
}
}

运行效果图:

Android 实现视频字幕Subtitle和横竖屏切换示例

Android 实现视频字幕Subtitle和横竖屏切换示例

项目源码,点击下载......

来源:http://blog.csdn.net/gaolei1201/article/details/62041478

标签:Android,视频字幕,Subtitle,横竖屏,切换
0
投稿

猜你喜欢

  • servlet异步请求的实现

    2023-07-14 17:11:38
  • java进阶解析Springboot上传excel存入数据库步骤

    2022-01-07 15:18:51
  • C# yield在WCF中的错误用法(一)

    2022-06-05 06:36:57
  • 如何使用Jenkins构建GIT+Maven项目

    2021-11-18 04:42:52
  • Android录制按钮源码解析

    2022-06-24 19:24:44
  • 一文详解Java拦截器与过滤器的使用

    2021-12-17 03:40:41
  • Java 泛型总结(一):基本用法与类型擦除

    2023-06-11 20:33:10
  • java实现文件上传下载功能

    2021-11-26 17:19:05
  • Android Textview实现颜色渐变滚动效果

    2023-08-28 05:30:08
  • Java使用JDBC实现Oracle用户认证的方法详解

    2022-10-06 08:59:36
  • C#多线程编程Task用法详解

    2021-10-27 14:26:10
  • Java模拟多线程实现抢票代码实例

    2022-11-17 07:41:51
  • Java的优先队列PriorityQueue原理及实例分析

    2023-03-14 08:11:00
  • spring中bean的生命周期详解

    2021-11-29 23:31:02
  • springboot项目启动的时候参数无效的解决

    2022-09-03 14:50:29
  • AndroidStudio kotlin配置详细介绍

    2022-02-18 14:09:21
  • 深入理解ThreadLocal工作原理及使用示例

    2022-02-27 19:24:14
  • QT5实现简单的TCP通信的实现

    2023-11-02 21:24:48
  • Android开发中MJRefresh自定义刷新动画效果

    2023-11-27 06:04:20
  • 完美实现ExpandableListView二级分栏效果

    2021-07-18 20:04:13
  • asp之家 软件编程 m.aspxhome.com