做电影解说如何去掉原视频字幕,FFmpeg 视频解码

摘要

这篇文章介绍怎么实现视频解码,具体步骤为读取Sample.mkv视频文件,从中提取视频流,然后解码为YUV图像数据做电影解说如何去掉原视频字幕,把YUV数据存储为PGM灰度图像,或者存储为YUV420p RAW格式视频。

初始化FFmepg和FormatContext

使用FFmpeg API第一个操作就是执行初始化函数:av_register_all注册所有相关组件,然后使用avformat_open_input打开指定的媒体文件,并使用avformat_find_stream_info获取媒体流相关信息,把这些格式信息映射到AVFormatContext *mFormatCtx这个结构中。

使用函数av_dump_format可以从控制台输出媒体文件相关信息。

bool VideoDecoding::init(const char * file)
{
    av_register_all();
    if ((avformat_open_input(&mFormatCtx, file, 0, 0)) < 0) {
        printf("Failed to open input filen");
    }
    if ((avformat_find_stream_info(mFormatCtx, 0)) < 0) {
        printf("Failed to retrieve input stream informationn");
    }
    av_dump_format(mFormatCtx, 0, file, 0);
    return false;
}

查询媒体流序号

多媒体文件一般都有一个视频流和多个音频流或者字幕流,每个媒体流都有序号Index。新版本的API使用av_find_best_stream函数查询相应的媒体流做电影解说如何去掉原视频字幕,第一个参数为初始化后的媒体格式Context,第二个参数即为媒体类型:

– AVMEDIA_TYPE_VIDEO:视频流

– AVMEDIA_TYPE_AUDIO:音频流

– AVMEDIA_TYPE_SUBTITLE:字幕流

后面几个参数是指定流特性的,如果从多个音频流中选择一个的话可以进行相关设置。此时只有一个视频流,所以参数设为-1即可返回默认的媒体流Index,得到这个Index后,接下来可以根据这个Index读取所需要的流。

bool VideoDecoding::findStreamIndex()
{
    // Find video stream in the file
    mVideoStreamIndex = av_find_best_stream(mFormatCtx, AVMEDIA_TYPE_VIDEO, -1, -1, NULL, 0);
    if (mVideoStreamIndex < 0) {
        printf("Could not find stream in input filen");
        return true;
    }
    return false;
}

配置编解码器CodecContext

首先使用avcodec_find_decoder函数根据流Index查找相应的解码器。

然后使用avcodec_alloc_context3函数根据解码器申请一个CodecContext。

接着根据流数据填充CodecContext各项信息。

最后完成CodecContext初始化操作。

// Initialize the AVCodecContext to use the given AVCodec.
bool VideoDecoding::initCodecContext()
{
    // Find a decoder with a matching codec ID
    AVCodec *dec = avcodec_find_decoder(mFormatCtx->streams[mVideoStreamIndex]->codecpar->codec_id);
    if (!dec) {
        printf("Failed to find codec!n");
        return true;
    }
    // Allocate a codec context for the decoder
    if (!(mCodecCtx = avcodec_alloc_context3(dec))) {
        printf("Failed to allocate the codec contextn");
        return true;
    }
    // Fill the codec context based on the supplied codec parameters.
    if (avcodec_parameters_to_context(mCodecCtx, mFormatCtx->streams[mVideoStreamIndex]->codecpar) < 0) {
        printf("Failed to copy codec parameters to decoder context!n");
        return true;
    }
    // Initialize the AVCodecContext to use the given Codec
    if (avcodec_open2(mCodecCtx, dec, NULL) < 0) {
        printf("Failed to open codecn");
        return true;
    }
    return false;
}