Subtitles support to Android MediaPlayer across any OS version

One of the key features in video hosting applications is Subtitles availability.

Unfortunately on Android, default media player doesn’t support SRT subtitles until Jellybean. Even on JB, it doesn’t give much flexibility to play around those APIs. I have had my troubles in debugging some intermittent crashes with no help from stack traces.
More over Android TV runs on Honeycomb devices which leaves us no other option than having our own functionality.

I came across a blog which mentioned about a hidden gem Subtitle Converter, a JAVA research project which deals with conversion of subtitle format to one another.
It converts any subtitle format(.SRT/.ASS/.STL/.SCC/.TTML) into ‘TimedTextObject‘, an object representation of a subtitle file and contain all the captions and associated styles.

Once you get hand on this object, you can iterate through Captions and tracks for corresponding timestamps to extract subtitles text 🙂

I have created a demo project with a sample mp4 file along with .srt file saved in raw folder of resources. You can also replace it with any remote url if you wanna test with your own files.

    • Below XML hosts a SurfaceView, a textView to display subtitles and a seek bar.

    • In Player Activity, once Surface is created initiate the Mediaplayer object. In this scenario I’m passing url of a file in raw folder:
 player.setDataSource(getApplicationContext(),   
              Uri.parse("android.resource://" + getPackageName() + "/"   
                   + R.raw.jellies));   
    • When the player is prepared, create an Asynctask to process subtitles file into a TimedtextObject.
 @Override   
       protected Void doInBackground(Void... params) {   
         // int count;   
         try {   
            /*   
            * if you want to download file from Internet, use commented   
            * code.   
            */   
            // URL url = new URL(   
            // "https://*.cloudfront.net/uploads/video/subtitle_file/3533/srt_919a069ace_boss.srt");   
            // InputStream is = url.openStream();   
            // File f = getExternalFile();   
            // FileOutputStream fos = new FileOutputStream(f);   
            // byte data[] = new byte[1024];   
            // while ((count = is.read(data)) != -1) {   
            // fos.write(data, 0, count);   
            // }   
            // is.close();   
            // fos.close();   
            InputStream stream = getResources().openRawResource(   
                 R.raw.jellies_subs);   
            FormatSRT formatSRT = new FormatSRT();   
            srt = formatSRT.parseFile("sample.srt", stream);   
         } catch (Exception e) {   
            e.printStackTrace();   
            Log.e(TAG, "error in downloadinf subs");   
         }   
         return null;   
       }   
    • Create a Runnable which checks for player status, reads position if playing and loop through the Caption’s time tracks to extract corresponding text for timestamp.
      Extracted text is passed to Subtitle TextView
 private Runnable subtitleProcessesor = new Runnable() {   
       @Override   
       public void run() {   
         if (player != null && player.isPlaying()) {   
            int currentPos = player.getCurrentPosition();   
            Collection
 subtitles = srt.captions.values();   
            for (Caption caption : subtitles) {   
              if (currentPos >= caption.start.mseconds   
                   && currentPos <= caption.end.mseconds) {                     onTimedText(caption);                     break;                  } else if (currentPos > caption.end.mseconds) {   
                 onTimedText(null);   
              }   
            }   
         }   
         subtitleDisplayHandler.postDelayed(this, 100);   
       }   
    };   
    public void onTimedText(Caption text) {   
       if (text == null) {   
         subtitleText.setVisibility(View.INVISIBLE);   
         return;   
       }   
       subtitleText.setText(Html.fromHtml(text.content));   
       subtitleText.setVisibility(View.VISIBLE);   
    }   
    • In  OnPostExecute of Asynctask, check for TimedtextObject and create a handler to start above Runnable in a loop.
 @Override   
       protected void onPostExecute(Void result) {   
         if (null != srt) {   
            subtitleText.setText("");   
            Toast.makeText(getApplicationContext(), "subtitles loaded!!",   
                 Toast.LENGTH_SHORT).show();   
            subtitleDisplayHandler.post(subtitleProcessesor);   
         }   
         super.onPostExecute(result);   
       }   
That is all. Easier than we thought and effective, indeed better than native support 🙂
If you have any problems with integration, check out the demo code from GitHub
Screenshot from demo:device-2014-08-04-183930 (1)
Note: Audio may not be in sync, but check .srt file to cross verify the Subtitle Timestamps
Advertisements

3 thoughts on “Subtitles support to Android MediaPlayer across any OS version

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s