ZHIYA
Blog

如何使用ffmpeg api来解析视频中的内封字幕

我的一个APP中需要解析视频中的内封内幕并在播放视频的时候显示出来。

视频中的内封字幕也称作内挂内幕,和视频流音频流混合封装成一个视频文件
不需要另外去下载字幕文件。
其中mp4支持:ttxt格式(又称TIMED_TEXT,MOV_TEXT),ttml格式 也以.xml格式存在,vtt格式等
mkv支持:subrip,ass,ssa等格式

一开始以为很简单的,但查了很多资料都是使用ffmpeg 命令行的方式来解析。
显示这种方式不满足我的要求,还好后面想到了ffmpeg源码中的ffplay文件,里头就有字幕解析的相关代码,虽然代码结构不清晰,但总比没有强。所以硬着头皮经过一天的源码阅读,然后经过多次尝试,终于实现基本的字幕读取并解析的api代码。

第一步先打开解码器。
这一步其实和打开视频,音频解码器的流程完全一样的,不得不说ffmpeg api设计的很不错,即使完全不同的解码器也能抽象成一样的步骤。

AVCodecContext* codec_ctx = nullptr;
if (!(codec = avcodec_find_decoder(stream->codecpar->codec_id)))
{
    LOGE << "could not find decoder, name:  " << avcodec_get_name(stream->codecpar->codec_id);
    return kErrorCodecNotFound;
}
codec_ctx = avcodec_alloc_context3(codec);
avcodec_parameters_to_context(codec_ctx, stream->codecpar);

AVDictionary* codec_opts = NULL;
// av_dict_set(&codec_opts, "sub_charenc", "UTF-8", 0);
if (avcodec_open2(codec_ctx, codec, &codec_opts))
{
    LOGE << "unsupported codec";
    return kErrorCodecOpenedFailed;
}
// LOGD << "open subtitle " << stream->index << "  decoder success";
mSubtitleCodecCtx = codec_ctx;
av_dict_free(&codec_opts);

第二步就是循环解码字幕包数据了。

// 读包。
while (true)
{
    AVPacket pkt;
    //            av_init_packet(&pkt);
    AVPacketRefWrap pktwrap(&pkt);
    int ret = ReadFrame(&pkt);
    if (kErrorNone != ret)
    {
        if (ret != kErrorEOF)
        {
            LOGE << "读文件出错 ret = " << ret;
        }
        break;
    }
    // LOGD << "pkt index = " << pkt.stream_index;
    if (IsSubtitlePkt(&pkt)) {
        // LOGD << "subtitle pkt streamindex = " << pkt.stream_index;
        if (pkt.stream_index == subtitleTracker->GetStreamIndex()) {
            // 开始解码。
            AVSubtitle sub;
            int got_frame = 0;
            ret = avcodec_decode_subtitle2(subtitleTracker->mSubtitleCodecCtx, &sub, &got_frame, &pkt);
            if (ret < 0) {
                LOGE << "decode subtitle failed = " << GetFFmpegErroMsg(ret);
                // break;
                continue;
            }
            else {
                if (got_frame && !pkt.data) {
                    // d->packet_pending = 1;
                    LOGE << "packet pending";
                }
                if (sub.num_rects > 0) {
                    for (int i = 0; i < sub.num_rects;i++) {
                        auto item = sub.rects[i];
                        LOGD << "subtitle type = " << item->type;
                        if (item->text != nullptr) {
                            LOGD << "text = " << item->text;
                        }
                        if (item->ass != nullptr) {
                            // LOGD << "ass = " << item->ass;
                        }
                    }
                }
                avsubtitle_free(&sub);
            }
        }
    }
}
版权声明:本文为ZHIYA网站的原创文章,遵循CC 4.0 BY-SA版权协议,转载请附上原文出处链接及本声明。原文链接:如何使用ffmpeg api来解析视频中的内封字幕