寻找一个addTimedTextSource的工作示例,用于在Android 4.1中从.srt文件添加字幕到video

我一直在试图使用一个.srt文件的定时文本源(只适用于Android 4.1 + http://developer.android.com/about/versions/android-4.1.html#Multimedia )。 第一个问题与获取.srt文件的文件描述符有关(在assets文件夹中,你将如何将它捆绑到你的应用程序中?)。 该文件会自动压缩,所以如果不更改编译设置或执行自定义构build,您将无法看到该文件。 最简单的解决scheme是将.srt文件重命名为.jpg,以便它不会被压缩,并且openFD方法仍然有效。 我现在添加TimedTextSource:

_myMP.addTimedTextSource(getAssets().openFd("captions.jpg").getFileDescriptor(), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP); 

现在文件加载正确,使用myMP.getTrackInfo()获取音轨列表,可以看到添加定时文本源后,第6首音轨的types为“3”,这是定时文本音轨types。 我已经使用selectTrack来select这个轨道,如在谷歌文档中所说的,但是这样做之后没有字幕出现在我的TimedTextListener上:

  _myMP.setOnTimedTextListener(new OnTimedTextListener(){ @Override public void onTimedText(MediaPlayer mp, TimedText text) { if (text!=null) Log.d("TimedText", text.getText()); } }); 

只触发一次(我在文件中有20个定时文本事件),但文本参数始终为空。 我已经做了search,找不到一个使用timeText工作代码的例子,它并没有出现在任何示例项目中,除了谷歌的API文档以外没有任何文档,但据我所知,没有人发布了它的工作示例呢。 我正在testing这个在谷歌Nexus更新到Android 4.2

Solutions Collecting From Web of "寻找一个addTimedTextSource的工作示例,用于在Android 4.1中从.srt文件添加字幕到video"

我能够得到这个工作,因为它仍然是一个开放的问题,我将在这里包括完整的解决scheme。

尽pipe更改文件扩展名以防止压缩的想法很好,但是我更喜欢将srt文件从资源复制到设备上的应用程序本地目录,但是为了完整起见,这里是一个扩展名列表不会被压缩。

“.jpg”,“.jpeg”,“.png”,“.gif”,“.wav”,“.mp2”,“.mp3”,“.ogg”,“.aac”,“.mpg”, “.mpeg”,“.mid”,“.midi”,“.smf”,“.jet”,“.rtttl”,“.imy”,“.xmf”,“.mp4”,“.m4a” “.m4v”,“.3gp”,“.3gpp”,“.3g2”,“.3gpp2”,“.amr”,“.awb”,“.wma”,“.wmv”

解决的步骤很简单:

  1. 创build一个MediaPlayer实例,并通过调用MediaPlayer.create()player.setDataSource()然后将player.prepare()

  2. 如果字幕文件在android设备上不存在,请将其从资源文件夹复制到设备

  3. 使用第一个参数调用player.addTimedTextSource() ,该String包含设备上的字幕文件的完整path, MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP作为第二个参数

  4. 通过调用player.selectTrack()selectTimedText轨道,并通过search从player.getTrackInfo()返回的TrackInfo[]传递the index of timedTextType (通常我发现它是2

  5. player.setOnTimedTextListener()设置一个监听器,然后开始播放媒体文件player.start()

这是完整的课程:

要运行这个确切的类,你需要res/raw文件夹下的两个文件sub.srtvideo.mp4 (或任何扩展名)。 然后定义一个id为txtDisplayTextView 最后你的项目/设备/模拟器必须支持API 16

 public class MainActivity extends Activity implements OnTimedTextListener { private static final String TAG = "TimedTextTest"; private TextView txtDisplay; private static Handler handler = new Handler(); @Override protected void onCreate(Bundle savedInstanceState) { super.onCreate(savedInstanceState); setContentView(R.layout.activity_main); txtDisplay = (TextView) findViewById(R.id.txtDisplay); MediaPlayer player = MediaPlayer.create(this, R.raw.video); try { player.addTimedTextSource(getSubtitleFile(R.raw.sub), MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP); int textTrackIndex = findTrackIndexFor( TrackInfo.MEDIA_TRACK_TYPE_TIMEDTEXT, player.getTrackInfo()); if (textTrackIndex >= 0) { player.selectTrack(textTrackIndex); } else { Log.w(TAG, "Cannot find text track!"); } player.setOnTimedTextListener(this); player.start(); } catch (Exception e) { e.printStackTrace(); } } private int findTrackIndexFor(int mediaTrackType, TrackInfo[] trackInfo) { int index = -1; for (int i = 0; i < trackInfo.length; i++) { if (trackInfo[i].getTrackType() == mediaTrackType) { return i; } } return index; } private String getSubtitleFile(int resId) { String fileName = getResources().getResourceEntryName(resId); File subtitleFile = getFileStreamPath(fileName); if (subtitleFile.exists()) { Log.d(TAG, "Subtitle already exists"); return subtitleFile.getAbsolutePath(); } Log.d(TAG, "Subtitle does not exists, copy it from res/raw"); // Copy the file from the res/raw folder to your app folder on the // device InputStream inputStream = null; OutputStream outputStream = null; try { inputStream = getResources().openRawResource(resId); outputStream = new FileOutputStream(subtitleFile, false); copyFile(inputStream, outputStream); return subtitleFile.getAbsolutePath(); } catch (Exception e) { e.printStackTrace(); } finally { closeStreams(inputStream, outputStream); } return ""; } private void copyFile(InputStream inputStream, OutputStream outputStream) throws IOException { final int BUFFER_SIZE = 1024; byte[] buffer = new byte[BUFFER_SIZE]; int length = -1; while ((length = inputStream.read(buffer)) != -1) { outputStream.write(buffer, 0, length); } } // A handy method I use to close all the streams private void closeStreams(Closeable... closeables) { if (closeables != null) { for (Closeable stream : closeables) { if (stream != null) { try { stream.close(); } catch (IOException e) { e.printStackTrace(); } } } } } @Override public void onTimedText(final MediaPlayer mp, final TimedText text) { if (text != null) { handler.post(new Runnable() { @Override public void run() { int seconds = mp.getCurrentPosition() / 1000; txtDisplay.setText("[" + secondsToDuration(seconds) + "] " + text.getText()); } }); } } // To display the seconds in the duration format 00:00:00 public String secondsToDuration(int seconds) { return String.format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds % 60), Locale.US); } } 

这里是我使用的subtitle文件作为例子:

 1 00:00:00,220 --> 00:00:01,215 First Text Example 2 00:00:03,148 --> 00:00:05,053 Second Text Example 3 00:00:08,004 --> 00:00:09,884 Third Text Example 4 00:00:11,300 --> 00:00:12,900 Fourth Text Example 5 00:00:15,500 --> 00:00:16,700 Fifth Text Example 6 00:00:18,434 --> 00:00:20,434 Sixth Text Example 7 00:00:22,600 --> 00:00:23,700 Last Text Example 

以下是testing应用程序的一些屏幕截图,显示TextView随着媒体文件的进展而自动更改(即从字幕文件中读取)

TimedText示例

编辑:

这是一个示例项目的代码

失去灵魂在2015年失去了这个时间:

我经历了两天,看着android源码,试图消除这个TimedText框架造成的所有错误。

我的build议是完全跳过它们的实现。 这是不完整和不一致的。 在较早的版本中,许多文本同步是在本机媒体播放器中完成的,所以很容易出现状态错误。

我的select是使用Textview子类:

 package ca.yourpackage.yourapp; import android.content.Context; import android.media.MediaPlayer; import android.util.AttributeSet; import android.util.Log; import android.widget.TextView; import java.io.IOException; import java.io.InputStream; import java.io.InputStreamReader; import java.io.LineNumberReader; import java.util.Locale; import java.util.Map; import java.util.TreeMap; /** * Created by MHDante on 2015-07-26. */ public class SubtitleView extends TextView implements Runnable{ private static final String TAG = "SubtitleView"; private static final boolean DEBUG = false; private static final int UPDATE_INTERVAL = 300; private MediaPlayer player; private TreeMap<Long, Line> track; public SubtitleView(Context context) { super(context); } public SubtitleView(Context context, AttributeSet attrs) { super(context, attrs); } public SubtitleView(Context context, AttributeSet attrs, int defStyleAttr) { super(context, attrs, defStyleAttr); } @Override public void run() { if (player !=null && track!= null){ int seconds = player.getCurrentPosition() / 1000; setText((DEBUG?"[" + secondsToDuration(seconds) + "] ":"") + getTimedText(player.getCurrentPosition())); } postDelayed(this, UPDATE_INTERVAL); } private String getTimedText(long currentPosition) { String result = ""; for(Map.Entry<Long, Line> entry: track.entrySet()){ if (currentPosition < entry.getKey()) break; if (currentPosition < entry.getValue().to) result = entry.getValue().text; } return result; } // To display the seconds in the duration format 00:00:00 public String secondsToDuration(int seconds) { return String.format("%02d:%02d:%02d", seconds / 3600, (seconds % 3600) / 60, (seconds % 60), Locale.US); } @Override protected void onAttachedToWindow() { super.onAttachedToWindow(); postDelayed(this, 300); } @Override protected void onDetachedFromWindow() { super.onDetachedFromWindow(); removeCallbacks(this); } public void setPlayer(MediaPlayer player) { this.player = player; } public void setSubSource(int ResID, String mime){ if(mime.equals(MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP)) track = getSubtitleFile(ResID); else throw new UnsupportedOperationException("Parser only built for SRT subs"); } /////////////Utility Methods: //Based on https://github.com/sannies/mp4parser/ //Apache 2.0 Licence at: https://github.com/sannies/mp4parser/blob/master/LICENSE public static TreeMap<Long, Line> parse(InputStream is) throws IOException { LineNumberReader r = new LineNumberReader(new InputStreamReader(is, "UTF-8")); TreeMap<Long, Line> track = new TreeMap<>(); while ((r.readLine()) != null) /*Read cue number*/{ String timeString = r.readLine(); String lineString = ""; String s; while (!((s = r.readLine()) == null || s.trim().equals(""))) { lineString += s + "\n"; } long startTime = parse(timeString.split("-->")[0]); long endTime = parse(timeString.split("-->")[1]); track.put(startTime, new Line(startTime, endTime, lineString)); } return track; } private static long parse(String in) { long hours = Long.parseLong(in.split(":")[0].trim()); long minutes = Long.parseLong(in.split(":")[1].trim()); long seconds = Long.parseLong(in.split(":")[2].split(",")[0].trim()); long millies = Long.parseLong(in.split(":")[2].split(",")[1].trim()); return hours * 60 * 60 * 1000 + minutes * 60 * 1000 + seconds * 1000 + millies; } private TreeMap<Long, Line> getSubtitleFile(int resId) { InputStream inputStream = null; try { inputStream = getResources().openRawResource(resId); return parse(inputStream); } catch (Exception e) { e.printStackTrace(); } finally { if (inputStream != null) { try { inputStream.close(); } catch (IOException e) { e.printStackTrace(); } } } return null; } public static class Line { long from; long to; String text; public Line(long from, long to, String text) { this.from = from; this.to = to; this.text = text; } } } 

用法:

 //I used and reccomend asyncPrepare() MediaPlayer mp = MediaPlayer.create(context, R.raw.video); SubtitleView subView = (SubtitleView) getViewbyId(R.id.subs_box); subView.setPlayer(mp); subView.setSubSource(R.raw.subs_intro, MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP); 

在您的布局xml文件中,只需创build一个textView,就像您希望字幕显示的那样,然后将该类更改为ca.yourpagckage.yourapp.SubtitleView

 <ca.yourpagckage.yourapp.SubtitleView android:layout_width="300dp" android:layout_height="300dp" android:text="Subtitles go Here" android:id="@+id/subs_box"/> 

祝你好运。

要使它与.mp3文件一起使用,请调用player.start(); 立即声明一个新的媒体播放器和addtimedtext代码之前。 紧接着下面的一行

 MediaPlayer player = MediaPlayer.create(this, R.raw.video);