前言

在我们观看电影或者抖音等短视频平台的视频时一般都会出现字幕,有了字幕那视频的表现形式就更加丰富了,所以为一段视频添加字幕也是一个硬需求。本文的目的就是为一段视频添加字幕,了解如何添加字幕前先了解下字幕的类型:

  • 外挂字幕
    外挂字幕是一个单独的外部字幕文件,格式类型一般有srt、vtt、ass等等。播放视频时,需要把外挂字幕和视频放在同一目录下,并在播放器中选择字幕文件才可以在视频中看到字幕。

  • 软字幕
    软字幕也叫内挂字幕、封装字幕、内封字幕,字幕流等,就是把前面的外挂字幕的字幕文件嵌入到视频中作为流的一部分,如果一个视频有多个字幕流那么播放视频是还得选择对应的字幕流

备注:不管是外挂字幕还是软字幕,字幕要正常显示播放器必须要支持字幕的渲染。

  • 硬字幕
    硬字幕就是嵌入到视频帧里面的字幕,它就像视频水印一样作为视频帧的一分部分了,不管再任何平台字幕看起来都是一样的,而且也不再要求播放器单独对字母进行渲染

总结:
1、外挂字幕和软字幕都要求播放器额外支持字幕的渲染,而硬字幕不需要。外挂字幕和软字幕可以随时更换和取消字幕文件,而硬字幕则不可以取消和更改视频中的字幕
2、如果是字幕流或者外挂字幕则还需要播放器支持字幕流的单独渲染
3、此外嵌入字幕流也需要容器格式支持,比如MKV格式就支持各种格式字幕文件,但是MP4对字幕的支持就不太好(只支持苹果的MOV text)

常见字幕格式

不同的字幕文件有其对应的格式(针对外挂字幕和软字幕),常见的字幕格式有:

  • SRT(标准外挂字幕格式):只包含文字和时间码,没有样式,显示效果由播放器决定,不同的播放器显示出的效果可能差别很大
  • ASS(高级外挂字幕格式):支持样式、字体、字幕定位、淡入淡出、简单的特效。如果不缺字体,不同的播放器显示效果基本一致
  • XML+PNG序列:用来导入Premiere、FCP7、Edius、Vegas、AE,不支持FCPX
    Avid DS Cap字幕格式:AVID专用格式,导入后可以修改文字
  • UTF(会声会影专用格式):可以直接导入会声会影使用

推荐一款字幕制作软件Arctime,下载地址,该软件可以制作各种格式的字幕,如下为各种字幕文件的格式:

ass字幕格式

image.png

ttxt字幕格式

image.png

srt字幕格式

image.png

ffmpeg字幕处理流程

image.png

ffmpeg命令行实现添加字幕

  • 将字幕处理滤镜编译到ffmpeg

如果ffmpeg要实现添加字幕的功能需要在编译时开启--enable-filter=subtitles --enable-libass

--enable-filter=subtitles 代表开启字幕滤镜
--enable-libass 则是字幕滤镜需要依赖的外部库,所以编译时还需要指定该外部库的路径(如x264的编译一样)

libass是一个用来进行字幕处理和渲染的开源库,地址https://github.com/libass/libass.git

完整编译脚本参考:包含subtitles滤镜的编译脚本

  • 添加软字幕
ffmpeg -i test_1280x720_3.mp4 -i test_1280x720_3.srt -c copy output.mkv

添加软字幕的原理和流程就跟给视频添加音频一样,这个过程不需要重新编解码,所以速度非常快。

tips:软字幕只有部分容器格式比如(mkv)才支持,MP4/MOV等不支持,而且也只有部分播放器支持软字幕或者外挂字幕(如VLC播放器)

VLC播放器播放上面命令中合成的带有软字幕的mkv视频

image.png

默认VLC是关闭字幕的,需要手动打开。

输入命令可以看到成功添加了软字幕

ffprobe out.mkv
Input #0, matroska,webm, from '/Users/apple/devoloper/mine/ffmpeg/ffmpeg-demo/filesources/test_1280x720_3_Video_Export/out.mkv':Metadata:DESCRIPTION     : Generated by Arctime Pro 2.4ENCODER         : Lavf58.31.101Duration: 00:01:11.05, start: 0.000000, bitrate: 1435 kb/sStream #0:0: Video: mpeg4 (Simple Profile), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 49.97 fps, 49.97 tbr, 1k tbn, 26635 tbc (default)Metadata:ENCODER         : Lavc58.55.100 mpeg4DURATION        : 00:01:11.046000000Stream #0:1: Audio: ac3, 44100 Hz, stereo, fltp, 192 kb/s (default)Metadata:ENCODER         : Lavc58.55.100 ac3DURATION        : 00:01:10.949000000Stream #0:2: Subtitle: assMetadata:ENCODER         : Lavc58.55.100 ssaDURATION        : 00:00:18.406000000
  • 字幕格式转换
    利用ffmpeg命令也可以实现字幕格式ass/srt/vtt等等的相互转换
ffmpeg -i test_1280x720_3.srt test_1280x720_3_1.vtt
ffmpeg -i test_1280x720_3.srt test_1280x720_3_1.ass
  • 添加硬字幕
ffmpeg -i test_1280x720_3.mkv -vf subtitles=test_1280x720_3.srt out.mp4

test_1280x720_3.srt代表要添加的字幕文件路径,这里也可以写成其它格式字幕文件,比如test_1280x720_3.ass,test_1280x720_3.ttext等等。ffmpeg最终都会将字幕格式先转换成ass字幕流再将字幕嵌入到视频帧中,这个过程需要重新编解码,所以速度比较慢。

输入命令可以看到成功添加了硬字幕

ffprobe out.mp4
Input #0, mov,mp4,m4a,3gp,3g2,mj2, from '/Users/apple/devoloper/mine/ffmpeg/ffmpeg-demo/filesources/test_1280x720_3_Video_Export/out.mp4':Metadata:major_brand     : isomminor_version   : 512compatible_brands: isomiso2mp41encoder         : Lavf58.31.101description     : Generated by Arctime Pro 2.4Duration: 00:01:11.06, start: 0.000000, bitrate: 1374 kb/sStream #0:0(und): Video: mpeg4 (Simple Profile) (mp4v / 0x7634706D), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 1238 kb/s, 49.97 fps, 49.97 tbr, 26635 tbn, 26635 tbc (default)Metadata:handler_name    : VideoHandlerStream #0:1(und): Audio: aac (LC) (mp4a / 0x6134706D), 44100 Hz, stereo, fltp, 128 kb/s (default)Metadata:handler_name    : SoundHandler

代码方式实现添加字幕

  • 1、添加软字幕
void Subtitles::addSubtitleStream(string videopath, string spath, string dstpath)
{if (dstpath.rfind(".mkv") != dstpath.length() - 4) {LOGD("can only suport .mkv file");return;}int ret = 0;// 打开视频流if (avformat_open_input(&vfmt,videopath.c_str(), NULL, NULL) < 0) {LOGD("avformat_open_input failed");return;}if (avformat_find_stream_info(vfmt, NULL) < 0) {LOGD("avformat_find_stream_info");releaseInternal();return;}if ((avformat_alloc_output_context2(&ofmt, NULL, NULL, dstpath.c_str())) < 0) {LOGD("avformat_alloc_output_context2() failed");releaseInternal();return;}int in_video_index = -1,in_audio_index = -1;int ou_video_index = -1,ou_audio_index = -1,ou_subtitle_index = -1;for (int i=0; i<vfmt->nb_streams; i++) {AVStream *stream = vfmt->streams[i];if (stream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {in_video_index = i;AVStream *newstream = avformat_new_stream(ofmt, NULL);avcodec_parameters_copy(newstream->codecpar, stream->codecpar);newstream->codecpar->codec_tag = 0;ou_video_index = newstream->index;} else if (stream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {AVStream *newstream = avformat_new_stream(ofmt, NULL);avcodec_parameters_copy(newstream->codecpar, stream->codecpar);newstream->codecpar->codec_tag = 0;in_audio_index = i;ou_audio_index = newstream->index;}}if (!(ofmt->oformat->flags & AVFMT_NOFILE)) {if (avio_open(&ofmt->pb, dstpath.c_str(), AVIO_FLAG_WRITE) < 0) {LOGD("avio_open failed");releaseInternal();return;}}// 打开字幕流/** 遇到问题:调用avformat_open_input()时提示"avformat_open_input failed -1094995529(Invalid data found when processing input)"*  分析原因:编译ffmpeg库是没有将对应的字幕解析器添加进去比如(ff_ass_demuxer,ff_ass_muxer)*  解决方案:添加对应的编译参数*/if ((ret = avformat_open_input(&sfmt,spath.c_str(), NULL, NULL)) < 0) {LOGD("avformat_open_input failed %d(%s)",ret,av_err2str(ret));return;}if ((ret = avformat_find_stream_info(sfmt, NULL)) < 0) {LOGD("avformat_find_stream_info %d(%s)",ret,av_err2str(ret));releaseInternal();return;}if((ret = av_find_best_stream(sfmt, AVMEDIA_TYPE_SUBTITLE, -1, -1, NULL, 0))<0){LOGD("not find subtitle stream 0");releaseInternal();return;}AVStream *nstream = avformat_new_stream(ofmt, NULL);ret = avcodec_parameters_copy(nstream->codecpar, sfmt->streams[0]->codecpar);nstream->codecpar->codec_tag = 0;/** todo:zsz AV_DISPOSITION_xxx:ffmpeg.c中该选项可以控制字幕默认是否显示,不过这里貌似不可以,原因未知。*/
//    nstream->disposition = sfmt->streams[0]->disposition;ou_subtitle_index = nstream->index;if(avformat_write_header(ofmt, NULL)<0){LOGD("avformat_write_header failed");releaseInternal();return;}av_dump_format(ofmt, 0, dstpath.c_str(), 1);/** 遇到问题:封装后生成的mkv文件字幕无法显示,封装时提示"[matroska @ 0x10381c000] Starting new cluster due to timestamp"*  分析原因:通过和ffmpeg.c中源码进行比对,后发现mvk对字幕写入的顺序有要求*  解决方案:将字幕写入放到音视频之前*/AVPacket *inpkt2 = av_packet_alloc();while (av_read_frame(sfmt, inpkt2) >= 0) {AVStream *srcstream = sfmt->streams[0];AVStream *dststream = ofmt->streams[ou_subtitle_index];av_packet_rescale_ts(inpkt2, srcstream->time_base, dststream->time_base);inpkt2->stream_index = ou_subtitle_index;inpkt2->pos = -1;LOGD("pts %d",inpkt2->pts);if (av_write_frame(ofmt, inpkt2) < 0) {LOGD("subtitle av_write_frame failed");releaseInternal();return;}av_packet_unref(inpkt2);}AVPacket *inpkt = av_packet_alloc();while (av_read_frame(vfmt, inpkt) >= 0) {if (inpkt->stream_index == in_video_index) {AVStream *srcstream = vfmt->streams[in_video_index];AVStream *dststream = ofmt->streams[ou_video_index];av_packet_rescale_ts(inpkt, srcstream->time_base, dststream->time_base);inpkt->stream_index = ou_video_index;LOGD("inpkt %d",inpkt->pts);if (av_write_frame(ofmt, inpkt) < 0) {LOGD("video av_write_frame failed");releaseInternal();return;}} else if (inpkt->stream_index == in_audio_index) {AVStream *srcstream = vfmt->streams[in_audio_index];AVStream *dststream = ofmt->streams[ou_audio_index];av_packet_rescale_ts(inpkt, srcstream->time_base, dststream->time_base);inpkt->stream_index = ou_audio_index;if (av_write_frame(ofmt, inpkt) < 0) {LOGD("audio av_write_frame failed");releaseInternal();return;}}av_packet_unref(inpkt);}LOGD("over");av_write_trailer(ofmt);releaseInternal();}

备注:
对于mkv的封装和解封装要开启ffmpeg的编译参数 --enable-muxer=matroska和--enable-demuxer=matroska
不同格式的字幕ass/srt写入文件后,当用播放器打开的时候字幕的大小以及位置也有区别

  • 2、添加硬字幕
void Subtitles::addSubtitlesForVideo(string vpath, string spath, string dstpath,string confpath)
{int ret = 0;// 打开视频流if (avformat_open_input(&vfmt,vpath.c_str(), NULL, NULL) < 0) {LOGD("avformat_open_input failed");return;}if (avformat_find_stream_info(vfmt, NULL) < 0) {LOGD("avformat_find_stream_info");releaseInternal();return;}if((ret = avformat_alloc_output_context2(&ofmt, NULL, NULL, dstpath.c_str())) < 0) {LOGD("avformat_alloc_output_context2 failed");return;}for (int i=0; i<vfmt->nb_streams; i++) {AVStream *sstream = vfmt->streams[i];if (sstream->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {in_video_index = i;// 添加新的视频流AVStream *nstream = avformat_new_stream(ofmt, NULL);ou_video_index = nstream->index;// 由于视频需要添加字幕,所以需要重新编解码,但是编码信息和源文件中一样AVCodec *codec = avcodec_find_decoder(sstream->codecpar->codec_id);if (!codec) {LOGD("not surport codec!");releaseInternal();return;}de_video_ctx = avcodec_alloc_context3(codec);if (!de_video_ctx) {LOGD("avcodec_alloc_context3 failed");releaseInternal();return;}// 设置解码参数,从源文件拷贝avcodec_parameters_to_context(de_video_ctx, sstream->codecpar);// 初始化解码器上下文if (avcodec_open2(de_video_ctx, codec, NULL) < 0) {LOGD("avcodec_open2 failed");releaseInternal();return;}// 创建编码器AVCodec *encodec = avcodec_find_encoder(sstream->codecpar->codec_id);if (!encodec) {LOGD("not surport encodec!");releaseInternal();return;}en_video_ctx = avcodec_alloc_context3(encodec);if (!en_video_ctx) {LOGD("avcodec_alloc_context3 failed");releaseInternal();return;}// 设置编码相关参数/** 遇到问题:生成视频前面1秒钟是灰色的*  分析原因:直接从源视频流拷贝编码参数到新的编码上下文中(即通过avcodec_parameters_to_context(en_video_ctx, sstream->codecpar);)而部分重要编码参数(如帧率,时间基)并不在codecpar*  中,所以导致参数缺失*  解决方案:额外设置时间基和帧率参数*/avcodec_parameters_to_context(en_video_ctx, sstream->codecpar);// 设置帧率int fps = sstream->r_frame_rate.num;en_video_ctx->framerate = (AVRational){fps,1};// 设置时间基;en_video_ctx->time_base = sstream->time_base;// I帧间隔,决定了压缩率en_video_ctx->gop_size = 12;if (ofmt->oformat->flags & AVFMT_GLOBALHEADER) {en_video_ctx->flags = AV_CODEC_FLAG_GLOBAL_HEADER;}// 初始化编码器上下文if (avcodec_open2(en_video_ctx, encodec, NULL) < 0) {LOGD("avcodec_open2 failed");releaseInternal();return;}// 设置视频流相关参数avcodec_parameters_from_context(nstream->codecpar, en_video_ctx);nstream->codecpar->codec_tag = 0;} else if (sstream->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {// 音频直接进行流拷贝in_audio_index = i;AVStream *nstream = avformat_new_stream(ofmt, NULL);avcodec_parameters_copy(nstream->codecpar, sstream->codecpar);ou_audio_index = nstream->index;nstream->codecpar->codec_tag = 0;}}if (in_video_index == -1) {LOGD("not has video stream");releaseInternal();return;}if (!(ofmt->flags & AVFMT_NOFILE)) {if (avio_open(&ofmt->pb, dstpath.c_str(), AVIO_FLAG_WRITE) < 0) {LOGD("avio_open() failed");releaseInternal();return;}}av_dump_format(ofmt, -1, dstpath.c_str(), 1);// 写入头文件if (avformat_write_header(ofmt, NULL) < 0) {LOGD("avformat_write_header failed");releaseInternal();return;}// 初始化滤镜if (!initFilterGraph(spath,confpath)) {LOGD("");releaseInternal();return;}AVPacket *inpkt = av_packet_alloc();while (av_read_frame(vfmt, inpkt) >= 0) {if (inpkt->stream_index == in_video_index) {doDecodec(inpkt);} else if (inpkt->stream_index == in_audio_index) {// 进行时间基的转换av_packet_rescale_ts(inpkt, vfmt->streams[in_audio_index]->time_base, ofmt->streams[ou_audio_index]->time_base);inpkt->stream_index = ou_audio_index;LOGD("audio pts %d(%s)",inpkt->pts,av_ts2timestr(inpkt->pts,&ofmt->streams[ou_audio_index]->time_base));av_write_frame(ofmt, inpkt);}av_packet_unref(inpkt);}LOGD("finish !");doDecodec(NULL);av_write_trailer(ofmt);releaseInternal();}/** 要使用subtitles和drawtext滤镜到ffmpeg中,则编译ffmpeg库时需要开启如下选项:*  1、字幕编解码器 --enable-encoder=ass --enable-decoder=ass --enable-encoder=srt --enable-decoder=srt --enable-encoder=webvtt --enable-decoder=webvtt;*  2、字幕解封装器 --enable-muxer=ass --enable-demuxer=ass --enable-muxer=srt --enable-demuxer=srt --enable-muxer=webvtt --enable-demuxer=webvtt*  3、滤镜选项  --enable-filter=drawtext --enable-libfreetype --enable-libass --enable-filter=subtitles**  备注:以上字幕编解码器以及字幕解封装器可以只使用一个即可,代表只能使用一个字幕格式。具体参考编译脚本*/
bool Subtitles::initFilterGraph(string spath,string confpath)
{graph = avfilter_graph_alloc();int ret = 0;AVStream *stream = vfmt->streams[in_video_index];// 输入滤镜const AVFilter *src_filter = avfilter_get_by_name("buffer");char desc[400];sprintf(desc,"video_size=%dx%d:pix_fmt=%d:time_base=%d/%d",stream->codecpar->width,stream->codecpar->height,stream->codecpar->format,stream->time_base.num,stream->time_base.den);ret = avfilter_graph_create_filter(&src_filter_ctx, src_filter, "buffer0", desc, NULL, graph);if (ret < 0) {LOGD("init src filter failed");return false;}// 输出滤镜const AVFilter *sink_filter = avfilter_get_by_name("buffersink");ret = avfilter_graph_create_filter(&sink_filter_ctx, sink_filter, "buffersink0", NULL, NULL, graph);if (ret < 0) {LOGD("buffersink init failed");return false;}/** 遇到问题:当使用libass库来合成字幕时无法生成字幕*  分析原因:libass使用fontconfig库来匹配字体,而程序中没有指定字体匹配用的描述文件*  解决方案:设置FONTCONFIG_FILE的值**  fontconfig工作原理:fontconfig通过环境变量FONTCONFIG_FILE来找到指定的fonts.conf文件(该文件的指定了字体文件(ttf,ttc等)的目录,以及字体fallback的规则),最终选择指定的字体文件*  font fallback:如果某个字符在指定的字体库中不存在,那么就需要找到能够显示此字符的备用字体库,fontconfig就是专门做此事的。**  备注:*  1、mac下 系统字体库的路径为:/System/Library/Fonts*  2、iOS下 系统字体库的路径为:ios系统字体不允许访问*  3、安卓下 系统字体库的路为:/system/fonts*  4、Ubuntu下 系统字体库的路径为:/usr/share/fonts*  不同系统支持的字体库可能不一样,由于fontconfig的字体fallback机制,如果不自定义自己的字体库,可能不同系统最终因为选择的字体库不一样导致合成字幕也不一样。*  所以解决办法就是统一用于各个平台的字体库,然后自定义fontconfig的字体库的搜索路径*/// 滤镜描述符setenv("FONTCONFIG_FILE",confpath.c_str(), 0);char filter_des[400];sprintf(filter_des, "subtitles=filename=%s",spath.c_str());AVFilterInOut *inputs = avfilter_inout_alloc();AVFilterInOut *ouputs = avfilter_inout_alloc();inputs->name = av_strdup("out");inputs->filter_ctx = sink_filter_ctx;inputs->next = NULL;inputs->pad_idx = 0;ouputs->name = av_strdup("in");ouputs->filter_ctx = src_filter_ctx;ouputs->next = NULL;ouputs->pad_idx = 0;if (avfilter_graph_parse_ptr(graph, filter_des, &inputs, &ouputs, NULL) < 0) {LOGD("avfilter_graph_parse_ptr failed");return false;}av_buffersink_set_frame_size(sink_filter_ctx, en_video_ctx->frame_size);// 初始化滤镜if (avfilter_graph_config(graph, NULL) < 0) {LOGD("avfilter_graph_config failed");return false;}avfilter_inout_free(&inputs);avfilter_inout_free(&ouputs);return true;
}void Subtitles::doDecodec(AVPacket *pkt)
{if (!de_frame) {de_frame = av_frame_alloc();}int ret = avcodec_send_packet(de_video_ctx, pkt);while (true) {ret = avcodec_receive_frame(de_video_ctx, de_frame);if (ret == AVERROR_EOF) {// 说明已经没有数据了;清空//解码成功送入滤镜进行处理if((ret = av_buffersrc_add_frame_flags(src_filter_ctx, NULL, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {LOGD("av_buffersrc_add_frame_flags failed");break;}break;} else if (ret < 0) {break;}//解码成功送入滤镜进行处理if((ret = av_buffersrc_add_frame_flags(src_filter_ctx, de_frame, AV_BUFFERSRC_FLAG_KEEP_REF)) < 0) {LOGD("av_buffersrc_add_frame_flags failed");break;}while (true) {AVFrame *enframe = av_frame_alloc();ret = av_buffersink_get_frame(sink_filter_ctx, enframe);if (ret == AVERROR_EOF) {// 说明结束了LOGD("avfilter endeof");// 清空编码器doEncodec(NULL);// 释放内存av_frame_unref(enframe);} else if (ret < 0) {// 释放内存av_frame_unref(enframe);break;}// 进行重新编码doEncodec(enframe);// 释放内存av_frame_unref(enframe);}}
}void Subtitles::doEncodec(AVFrame *frame)
{int ret = avcodec_send_frame(en_video_ctx, frame);while (true) {AVPacket *pkt = av_packet_alloc();ret = avcodec_receive_packet(en_video_ctx, pkt);if (ret < 0) {av_packet_unref(pkt);break;}// 写入数据av_packet_rescale_ts(pkt, en_video_ctx->time_base, ofmt->streams[ou_video_index]->time_base);pkt->stream_index = ou_video_index;LOGD("video pts %d(%s)",pkt->pts,av_ts2timestr(pkt->pts,&ofmt->streams[ou_video_index]->time_base));av_write_frame(ofmt, pkt);av_packet_unref(pkt);}
}

ffmpeg中字幕处理的滤镜有两个subtitles和drawtext。
1、要想正确使用subtitles滤镜,编译ffmpeg时需要添加--enable-libass --enable-filter=subtitles配置参数,同时引入libass库。同时由于libass库又引用了freetype,fribidi外部库所以还需要同时编译这两个库,此外
libass库根据操作系统的不同还引入不同的外部库,比如mac os系统则引入了CoreText.framework库,Linux则引入了fontconfig库,windows系统则引入了DirectWrite,或者添加--disable-require-system-font-provider
代表不使用这些系统的库
2、要想正确使用drawtext滤镜,编译ffmpeg时需要添加--enable-filter=drawtext同时要引入freetype和fribidi外部库
3、所以libass和drawtext滤镜从本质上看都是调用freetype生成一张图片,然后再将图片和视频融合
与libass库字幕处理相关的三个库:
1、text shaper相关:用来定义字体形状相关,fribidi和HarfBuzz两个库,其中fribidi速度较快,与字体库形状无关的一个库,libass默认,故HarfBuzz可以选择不编译
2、字体库相关:CoreText(ios/mac);fontconfig(linux/android/ios/mac);DirectWrite(windows),用来创建字体。
3、freetype:用于将字符串按照前面指定的字体以及字体形状渲染为字体图像(RGB格式,备注:它还可以将RGB格式最终输出为PNG,则需要编译libpng库)

遇到问题

1、遇到问题:调用avformat_open_input()时提示"avformat_open_input failed -1094995529(Invalid data found when processing input)"
分析原因:编译ffmpeg库是没有将对应的字幕解析器添加进去比如(ff_ass_demuxer,ff_ass_muxer)
解决方案:添加对应的编译参数

2、遇到问题:封装后生成的mkv文件字幕无法显示,封装时提示"[matroska @ 0x10381c000] Starting new cluster due to timestamp"
分析原因:通过和ffmpeg.c中源码进行比对,后发现mvk对字幕写入的顺序有要求
解决方案:将字幕写入放到音视频之前

3、遇到问题:生成视频前面1秒钟是灰色的
分析原因:直接从源视频流拷贝编码参数到新的编码上下文中(即通过avcodec_parameters_to_context(en_video_ctx, sstream->codecpar);)而部分重要编码参数(如帧率,时间基)并不在codecpar中,所以导致参数缺失
解决方案:额外设置时间基和帧率参数

4、遇到问题:当以静态库方式引入fontconf到ffmpeg中时提示"pkg-conf fontconf not found"
分析原因:fontconf自己生成的pc文件不包含expat库,最终导致了错误
解决方案:自己定义fontconfig库的pc文件

5、遇到问题:以静态库的方式引入android studio时 提示"undefined reference to xxxx"
分析原因:此问题为偶然发现,以静态库方式导入可执行程序时(如果引用的库中又引用了其它库或者各个模块之间有相互引用时)那么就一定要注意连接顺序的问题,所以最后一定要按照如下顺序导入到android中(其中ffmpeg库的顺序也要固定)
libavformat.a libavcodec.a libavfilter.a libavutil.a libswresample.a libswscale.a libass.a libfontconfig.a libexpat.a libfreetype.a libfribidi.a libmp3lame.a libx264.a

6、遇到问题:"引入fontconfig时提示"libtool: link: warning: library `/home/admin/usr/lib/freetype.la' was moved." ";因为fontcong依赖freetype,libass也依赖freetype。而fontconfig如果加入了--with-sysroot=参数
则生成的fontconfig.la文件的dependency_libs字段 是-Lxxx/freetype/lib =/user/xxxxx/freetype.la的格式,导致libtool解析错误,所以这里fontconfig不需要添加"--with-root" 参数

7、遇到问题:mac编译时提示"Undefined symbols _libintl_dgettext"
分析原因:因为fontconfig库依赖intl库而编译时未导入
解决方案:通过编译参数"-lintl"导入即可

8、遇到问题:真机使用fontconfig库时奔溃
分析原因:通过查看fontconfig库源码发现头文件fcatomic.h中有宏定义__IPHONE_VERSION_MIN_REQUIRED时才引入<Availability.h>,所以编译时不加此宏定义就会导致崩溃
解决方案:编译时添加宏定义__IPHONE_VERSION_MIN_REQUIRED

完成添加字幕的功能的ffmpeg代码本身不多,主要的时间都花在解决引入libass、fontconfig等外部库的编译及引入产生的问题上了,所以上面也记录了一下

项目地址

https://github.com/nldzsz/ffmpeg-demo

位于cppsrc目录下文件Subtitles.hpp/Subtitles.cpp

项目下示例可运行于iOS/android/mac平台,工程分别位于demo-ios/demo-android/demo-mac三个目录下,可根据需要选择不同平台

ffmpeg-给视频添加字幕(二十四)相关推荐

  1. Windows下使用ffmpeg为视频添加字幕

    字幕分以下几种形式: 第一种是外挂字幕(软字幕),视频文件和字幕文件分离.当播放某视频文件时,会自动载入相同文件夹下同名的字幕文件,当然也可以用播放器(如:VLC media player)手动载入字 ...

  2. Moviepy模块之视频添加字幕(二)

    文章目录 前言 一.素材 1.1 原视频 1.2 字幕文件 二.视频添加字幕 2.1 引入库 2.2 加载视频文件 2.3 设置字幕的字体.大小.颜色 2.4 加载字幕文件 2.5 设置字幕位置 2. ...

  3. ffmpeg 合并视频 添加字幕 bat命令

    首先将ffmpeg添加进系统环境变量 1.mp4转mkv for %%a in ("*.mp4") do ffmpeg -i %%~na.mp4 -vcodec copy -aco ...

  4. 安卓使用ffmpeg给视频添加字幕

    包含添加字幕能力的ffmpeg和相关so编译 需要下载的源码 https://github.com/tanersener/mobile-ffmpeg https://github.com/taners ...

  5. Mac OS中利用ffmpeg为视频添加字幕

    Mac系统下,利用ffmpeg加字幕 字幕类型 硬字幕,类似视频水印,作为视频的一部分内嵌了. 软字幕,封装字幕,也是内嵌到视频里,不过只是作为渲染,而且需要播放器支持才行. 外挂字幕,就是外部字幕文 ...

  6. FFmpeg之wav转mp3(二十四)

    一.将wav转码为mp3 1.下载开源编码库libmp3lame,然后编译.安装到系统 https://nchc.dl.sourceforge.net/project/lame/lame/3.100/ ...

  7. CYQ.Data 轻量数据层之路 使用篇-MAction 取值赋值 视频[带音乐] F (二十四)

    CYQ.Data 轻量数据层之路 使用篇-MAction 取值赋值 视频[带音乐] F (二十四) 说明: 本次录制主要为使用篇:CYQ.Data 轻量数据层之路 使用篇三曲 MAction 取值赋值 ...

  8. python实现给视频添加字幕,并根据字幕添加语音

    文章目录 前言 一."pyttsx3"实现"文字到音频"的转换 二."pydub"处理音频 1.安装 2.测试 三."moviep ...

  9. java从入门到精通二十四(三层架构完成增删改查)

    java从入门到精通二十四(三层架构完成增删改查) 前言 环境准备 创建web项目结构 导入依赖和配置文件 创建层次模型 实现查询 实现添加 实现修改 完成删除 做一个用户登录验证 会话技术 cook ...

最新文章

  1. 2021年大数据Spark(十四):Spark Core的RDD操作
  2. C# WebBrowser document.execCommand()解析
  3. 深度学习中张量flatten处理(flatten,reshape,reduce)
  4. 正在研究d2010的dcu格式
  5. 肝了这200页!火爆全网的Python学习知识手册!(附下载)
  6. Tomcat 总体结构
  7. 真正的高手,都有增长思维!(深度好文)
  8. 上去很美的 Serverless 在中国落地的怎么样了?
  9. ubuntu双系统导致进windows花屏
  10. lof、etf、qdii基金区别
  11. 洛谷——P1614 爱与愁的心痛
  12. Spring源码之ApplicationContext(一)
  13. Android手机应用CTA认证自测
  14. VGG16系列IV: 参数计算
  15. 怎么把视频做成高清的GIF动态图?简单又快的方法
  16. 对JavaEE的理解
  17. 【ubuntu】使用ubuntu杀死指定端口
  18. 华为手环7和小米手环7的区别 哪个好
  19. 软件测试项目实战,Web测试常用测试点,即拿即用宝典
  20. 程序员面试金典——叠罗汉1_____dp经典案例s

热门文章

  1. GigE Vision简介
  2. 中医与人工智能-基于Protégé构建知识图谱
  3. TOEFL2016-2019独立写作分类
  4. CNCF LFX Mentorship:从对密码学一无所知到在 WasmEdge 中实现 wasi-crypto
  5. 因式分解(2019,牛客 ,7场B)
  6. 计算机专业求职信英语,计算机专业的英语求职信
  7. 齐治堡垒机配置文件服务器,登录齐治堡垒机简易使用手册V
  8. ODOO怎样导入带产品分类和变体的产品数据
  9. 利用 ApsaraDB For SQL Server各版本高效而低廉地实现关键业务需求
  10. 从反汇编的角度看C++语法(构造函数)