Todos os produtos
Search
Central de documentação

ApsaraVideo VOD:Como adicionar legendas externas

Última atualização: Jun 27, 2026

O ApsaraVideo Player SDK permite adicionar, analisar e renderizar fluxos de legendas externas nos formatos WebVTT, SRT e ASS, além de fluxos de legendas incorporados em arquivos M3U8. Este tópico aborda a implementação para Android e iOS.

Importante

Para obter o código completo e detalhes de implementação, consulte o projeto API-Example e siga suas melhores práticas.

Veja o source do módulo ExternalSubtitle nos projetos API-Example-Android e API-Example-iOS.

Limites

  • Apenas arquivos de legenda externa nos formatos WebVTT, SRT e ASS são suportados.

Exemplo de renderização

Para empacotar fluxos de legendas para renderização, consulte Melhores práticas para transcodificação e empacotamento de múltiplas legendas.

Implementação principal no Android

Renderizar legendas externas

  1. Importe as bibliotecas de dependência.

    import com.aliyun.subtitle.SubTitleBase;
    import com.cicada.player.utils.webVtt.VttSubtitleView;
    import com.aliyun.player.IPlayer;
  2. Defina a visualize de exibição.

    vttSubtitleView = new VttSubtitleView(getContext());
    vttSubtitleView.setId(R.id.cicada_player_vtt_subtitle);
    
    // Set the display position of the subtitles. Add them to the center of the layout.
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
    );
    params.gravity = Gravity.CENTER; // Add to the center of the layout.
    
    // Add the VTT subtitle view to the root layout view.
    mRootFrameLayout.addView(mVttSubtitleView, params);
  3. Configure os callbacks.

    // In the following example, mVideoListPlayer is a listPlayer. The process is the same if you use aliPlayer.
    // 1. Set OnSubtitleDisplayListener.
    mVideoListPlayer.setOnSubtitleDisplayListener(new IPlayer.OnSubtitleDisplayListener() {
        @Override
        public void onSubtitleExtAdded(int trackIndex, String url) {
          // Because we are only testing the addition of a single VTT file, select the subtitle track directly after the VTT file is added.
            mVideoListPlayer.selectExtSubtitle(trackIndex, true);
        }
    
        @Override
        public void onSubtitleShow(int trackIndex, long id, String data) {
            if (vttSubtitleView != null) {
                vttSubtitleView.show(id, data);
            }
        }
    
        @Override
        public void onSubtitleHide(int trackIndex, long id) {
            if (vttSubtitleView != null) {
                vttSubtitleView.dismiss(id);
            }
        }
    
        @Override
        public void onSubtitleHeader(int trackIndex, String header) {
            if (vttSubtitleView != null) {
                vttSubtitleView.setVttHeader(header);
            }
        }
    });
    
    // 2. Set VideoSizeChangedListener. This is a required step. The data must be passed back to vttSubtitleView.
    mVideoListPlayer.setOnVideoSizeChangedListener(new IPlayer.OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(int width, int height) {
            int viewWidth = getWidth();
            int viewHeight = getHeight();
            IPlayer.ScaleMode mode = mVideoListPlayer.getScaleMode();
            SubTitleBase.VideoDimensions videoDimensions = SubTitleBase.getVideoDimensionsWhenRenderChanged(width, height, viewWidth, viewHeight, mode);
            vttSubtitleView.setVideoRenderSize(videoDimensions.videoDisplayWidth, videoDimensions.videoDisplayHeight);
        }
    });
  4. Limpe a visualize de exibição após o player executar a operação prepare/moveTo/moveToNext/moveToPrev.

    source = getSource(someParams); // pseudocode
    
    mVideoListPlayer.moveTo(mCurrentUUID + "");
    if (vttSubtitleView != null) {
        vttSubtitleView.clearAll();
    }
    
    if (!source.getExtSubtitleUrl().isEmpty()) {
        mCurrentExtSubtitle = source.getExtSubtitleUrl(); // Get the external subtitle URL.
    }
  5. Adicione o arquivo de legenda quando o callback onPrepared do player for invocado.

    // Currently, you can call addExtSubtitle only after the player's onPrepared callback is invoked.
    mVideoListPlayer.addExtSubtitle(mCurrentExtSubtitle);

Renderizar legendas empacotadas em M3U8

Importante

Não renderize legendas empacotadas em M3U8 e legendas externas simultaneamente.

  1. Importe as bibliotecas de dependência.

    import com.aliyun.subtitle.SubTitleBase;
    import com.cicada.player.utils.webVtt.VttSubtitleView;
    import com.aliyun.player.IPlayer;
  2. Defina a visualize de exibição.

    vttSubtitleView = new VttSubtitleView(getContext());
    vttSubtitleView.setId(R.id.cicada_player_vtt_subtitle);
    
    // Set the display position of the subtitles. Add them to the center of the layout.
    FrameLayout.LayoutParams params = new FrameLayout.LayoutParams(
            FrameLayout.LayoutParams.WRAP_CONTENT,
            FrameLayout.LayoutParams.WRAP_CONTENT
    );
    params.gravity = Gravity.CENTER; // Add to the center of the layout.
    
    // Add the VTT subtitle view to the root layout view.
    mRootFrameLayout.addView(mVttSubtitleView, params);
  3. Configure os callbacks.

    // In the following example, mVideoListPlayer is a listPlayer. The process is the same if you use aliPlayer.
    // 1. Set OnSubtitleDisplayListener.
    mVideoListPlayer.setOnSubtitleDisplayListener(new IPlayer.OnSubtitleDisplayListener() {
        @Override
        public void onSubtitleExtAdded(int trackIndex, String url) {
          // Because we are only testing the addition of a single VTT file, select the subtitle track directly after the VTT file is added.
            mVideoListPlayer.selectExtSubtitle(trackIndex, true);
        }
    
        @Override
        public void onSubtitleShow(int trackIndex, long id, String data) {
            if (vttSubtitleView != null) {
                vttSubtitleView.show(id, data);
            }
        }
    
        @Override
        public void onSubtitleHide(int trackIndex, long id) {
            if (vttSubtitleView != null) {
                vttSubtitleView.dismiss(id);
            }
        }
    
        @Override
        public void onSubtitleHeader(int trackIndex, String header) {
            if (vttSubtitleView != null) {
                vttSubtitleView.setVttHeader(header);
            }
        }
    });
    
    // 2. Set VideoSizeChangedListener. This is a required step. The data must be passed back to vttSubtitleView.
    mVideoListPlayer.setOnVideoSizeChangedListener(new IPlayer.OnVideoSizeChangedListener() {
        @Override
        public void onVideoSizeChanged(int width, int height) {
            int viewWidth = getWidth();
            int viewHeight = getHeight();
            IPlayer.ScaleMode mode = mVideoListPlayer.getScaleMode();
            SubTitleBase.VideoDimensions videoDimensions = SubTitleBase.getVideoDimensionsWhenRenderChanged(width, height, viewWidth, viewHeight, mode);
            vttSubtitleView.setVideoRenderSize(videoDimensions.videoDisplayWidth, videoDimensions.videoDisplayHeight);
        }
    });
  4. Limpe a visualize de exibição após o player executar a operação prepare/moveTo/moveToNext/moveToPrev.

    source = getSource(someParams); // pseudocode
    
    mVideoListPlayer.moveTo(mCurrentUUID + "");
    if (vttSubtitleView != null) {
        vttSubtitleView.clearAll();
    }
    
    if (!source.getExtSubtitleUrl().isEmpty()) {
        mCurrentExtSubtitle = source.getExtSubtitleUrl(); // Get the external subtitle URL.
    }
  5. Configure o callback onTrackReady para obter as informações do subfluxo.

    mAliPlayer.setOnTrackReadyListener(new IPlayer.OnTrackReadyListener() {
        @Override
        public void onTrackReady(MediaInfo mediaInfo) {
            List<TrackInfo>  trackInfos = mediaInfo.getTrackInfos();
            for (TrackInfo trackInfo : trackInfos) {
                if (trackInfo.getType() == TrackInfo.Type.TYPE_SUBTITLE) {
                // TODO 
                }
            }
        }
    });
  6. Chame selectTrack para alternar a faixa de legenda quando o callback onPrepared do player for invocado.

    // index is the TrackIndex of the target subtitle stream.
    mAliPlayer.selectTrack(index);

Implementação principal no iOS

Renderizar legendas externas

  1. Configure o callback.

    /**
     @brief An external subtitle is added.
     @param player The player pointer.
     @param trackIndex The index of the subtitle track to be displayed.
     @param URL The subtitle URL.
     */
    - (void)onSubtitleExtAdded:(AliPlayer*)player trackIndex:(int)trackIndex URL:(NSString *)URL {
        NSLog(@"onSubtitleExtAdded: %@, trackIndex: %d", URL, trackIndex);
        [self.listPlayer selectExtSubtitle:trackIndex enable:YES];
    }
  2. Adicione o arquivo de legenda quando o callback onPrepared do player for invocado.

    if (self.currentModel.extSubtitleUrl != Nil) {
        [self.listPlayer addExtSubtitle:self.currentModel.extSubtitleUrl];
    }

Renderizar legendas empacotadas em M3U8

Importante

Não renderize legendas empacotadas em M3U8 e legendas externas simultaneamente.

  1. Configure o callback.

    /**
     @brief An external subtitle is added.
     @param player The player pointer.
     @param trackIndex The index of the subtitle track to be displayed.
     @param URL The subtitle URL.
     */
    - (void)onSubtitleExtAdded:(AliPlayer*)player trackIndex:(int)trackIndex URL:(NSString *)URL {
        NSLog(@"onSubtitleExtAdded: %@, trackIndex: %d", URL, trackIndex);
        [self.listPlayer selectExtSubtitle:trackIndex enable:YES];
    }
  2. Configure o callback onTrackReady para obter as informações do subfluxo.

    - (void)onTrackReady:(AliPlayer*)player info:(NSArray<AVPTrackInfo*>*)info {
      //
    }
  3. Chame selectTrack para alternar a faixa de legenda quando o callback prepare done do player for invocado.

    // index is the TrackIndex of the target subtitle stream.
    [self.player selectTrack:index];

Implementar renderização personalizada com base em fluxos WebVTT

O ApsaraVideo Player analisa e renderiza fluxos WebVTT padrão tanto no Android quanto no iOS. O player lê estilos CSS para produzir uma renderização de legendas unificada entre plataformas e suporta múltiplos estilos de texto em um único arquivo. Isso é útil para distinguir diálogos de personagens, enfatizar conteúdo importante e criar efeitos visuais especiais.

O exemplo a seguir mostra a estrutura de um arquivo WebVTT:

  • REGION: Defina a área de exibição.

  • STYLE: Defina o estilo da fonte. O primeiro item é o estilo padrão.

WEBVTT

REGION
id:bottom
width:70.000000%
lines:3
viewportanchor:15.000000%,95.000000%
regionanchor:0.000000%,100.000000%
scroll:up

STYLE
::cue {
  color: white;
  font-size: 70%;
  font-family: Noto Sans;
  font-weight: bold;
  background-color: rgba(0, 0, 0, 0.2);
}
::cue(.font1) {
  color: rgba(255, 255, 255, 1);
  font-weight: bold;
  font-family: Noto Sans;
  outline-width: 3px;
  outline-color: rgba(0, 0, 0, 1);
}
::cue(.font2) {
  color: rgba(252, 255, 101, 1);
  font-style: bold;
  font-family: Noto Sans;
  outline-width: 3px;
  outline-color: rgba(0, 0, 0, 1);
}
::cue(.font3) {
  color: rgba(255, 191  23, 1);
  font-style: bold;
  font-family: Noto Sans;
  outline-width: 3px;
  outline-color: rgba(183, 28, 28, 1);
}

00:00:00.200 --> 00:00:01.800 region:bottom align:center
Defendant Bo Hanshi

00:00:01.800 --> 00:00:03.200 region:bottom align:center
Do you have anything else to say

00:00:04.100 --> 00:00:04.800 region:bottom align:center
I

00:00:11.200 --> 00:00:12.200 region:bottom align:center
<v.font1>I have nothing to say</v.font1>

00:00:14.500 --> 00:00:16.200 region:bottom align:center
Defendant Bo Hanshi

...

Para aplicar um estilo a um segmento de texto específico, utilize a seguinte sintaxe:

<v.font1>I have nothing to say</v.font1>