web-dev-qa-db-fra.com

Vous recherchez un exemple de travail d'Addtimedtextsource pour ajouter de sous-titres à une vidéo à partir d'un fichier .srt in Android 4.1

J'ai essayé d'utiliser un fichier .srt pour une source de texte chronométrée (disponible uniquement dans Android 4.1+ http://developer.android.com/about/versions/andrroid -4.1.html # multimédia ). Le premier problème concerne d'obtenir un descripteur de fichier pour le fichier .srt (dans le dossier d'actifs, comment voudriez-vous le regrouper dans votre application?). Le fichier est compressé automatiquement Donc, vous ne pourrez même pas voir le fichier sans changer de paramètres de compilation ni faire une construction personnalisée. La solution la plus facile consistait à renommer le fichier .srt sur .jpg afin qu'il ne soit pas comprimé et la méthode OpenFD fonctionne toujours. je Je ajoute maintenant le temporisateur avec:

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

Maintenant, le fichier se charge correctement et à l'aide de MyMP.getTrackinfo () pour obtenir une liste de pistes, vous pouvez voir qu'après l'ajout de la source de texte chronométrée, la 6ème piste a le type "3" qui est du type de piste de texte chronométré. J'ai utilisé SelectTrack pour choisir cette piste comme indiqué dans la documentation Google, mais après avoir fait, aucune légendité n'apparaît jamais et sur mon timectetextListener:

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

Les incendies Une seule fois (j'ai comme 20 événements de texte chronométrés dans le fichier), mais le paramètre texte est toujours null. J'ai fait des recherches et je ne trouve pas d'exemple de code de travail unique d'utiliser TimeText et n'apparaît dans aucun exemple de projets, il n'existe littéralement aucune documentation autre que les documents de l'API de Google, mais aussi loin que possible, personne n'a affiché Exemple de travail de celui-ci encore. Je le teste sur Google Nexus mis à jour sur Android 4.2

32
user1489039

J'ai pu obtenir cela pour travailler et car c'est toujours une question ouverte, je comprendra la solution complète ici.

Bien que l'idée de modifier l'extension de fichier pour empêcher la compression est agréable, mais je préfère copier le fichier srt des ressources au répertoire local de l'application sur l'appareil, mais de toute façon par souci d'exhaustivité ici est une Liste des extensions qui ne seront pas comprimées.

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

Les étapes de la solution sont simples:

  1. Créez une instance MediaPlayer et préparez-la en appelant MediaPlayer.create() ou player.setDataSource() puis player.prepare()

  2. Si les fichiers de sous-titres n'existent pas déjà sur le Android Device, copiez-le dans le dossier de ressources sur le périphérique.

  3. Appel player.addTimedTextSource() avec le premier argument d'un String qui contient le chemin complet du fichier de sous-titres sur l'appareil et MediaPlayer.MEDIA_MIMETYPE_TEXT_SUBRIP comme second argument

  4. Sélectionnez la piste TimedText en appelant player.selectTrack() et passer the index of timedTextType en recherchant le retour de TrackInfo[]player.getTrackInfo() (je le trouve habituellement 2)

  5. Configurez un auditeur avec player.setOnTimedTextListener(), puis commencez à lire le fichier multimédia player.start()

Voici la classe complète:

Pour exécuter cette classe exacte que vous aurez besoin de deux fichiers sous votre res/raw dossier sub.srt et video.mp4 (ou tout autre extensions). Définissez ensuite un TextView avec l'identifiant txtDisplay. Enfin votre projet/périphérique/émulateur doit prendre en charge 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);
}
}

et voici le fichier subtitle que j'utilise à titre d'exemple:

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

Voici quelques captures d'écran de l'application de test indiquant que le TextView change automatiquement (c'est-à-dire la lecture du fichier de sous-titres) car le fichier multimédia progresse

TimedText Example

Edit:

Voici le code pour n exemple de projet

28
iTech

EDIT: Je dois souligner que ces dernières années, les versions post-kitkat de Android sont devenues la majeure partie de l'application utilisant Android Part de marché. La mise en œuvre ci-dessous a été une tentative de compatibilité avec des appareils plus anciens. À ce stade, je suggère d'utiliser le cadre temporisé (qui fonctionnait bien à Kitkat) ou de nouvelles alternatives publiées par Android, car une solution personnalisée peut avoir des coûts de maintenance importants.


J'ai traversé 2 jours en regardant Android Source essayant de reproduire tous les insectes que ce cadre temporaire a provoqué.

Ma recommandation est de sauter complètement leur mise en œuvre. C'est incomplet et incompatible. Dans les versions antérieures, beaucoup de synchronisation de texte se fait dans le Native MediaPlayer, il est donc sujet à des erreurs d'État.

Mon alternative est d'utiliser une sous-classe 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;
        }
    }
}

Usage:

//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);

Dans votre fichier XML de layouche, créez simplement un TextView comme vous le souhaitez que les sous-titres affichent, puis changez la classe en ca.yourpagckage.votreapp.subtiterview

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

Bonne chance.

11
MHDante