我正在复用视频和音频流。视频流来自生成的图像数据。音频流来自 aac 文件。一些音频文件比我设置的总视频时间长,所以我的策略是在其时间大于总视频时间(我通过编码视频帧数控制的最后一个)时停止音频流复用器。
我不会把整个设置代码放在这里,但它类似于 muxing.c来自最新的 FFMPEG 存储库的示例。唯一的区别是,正如我所说,我使用来自文件的音频流,而不是来自综合生成的编码帧。我很确定问题出在复用器循环期间我的错误同步。这就是我所做的:
void AudioSetup(const char* audioInFileName)
{
AVOutputFormat* outputF = mOutputFormatContext->oformat;
auto audioCodecId = outputF->audio_codec;
if (audioCodecId == AV_CODEC_ID_NONE) {
return false;
}
audio_codec = avcodec_find_encoder(audioCodecId);
avformat_open_input(&mInputAudioFormatContext,
audioInFileName, 0, 0);
avformat_find_stream_info(mInputAudioFormatContext, 0);
av_dump_format(mInputAudioFormatContext, 0, audioInFileName, 0);
for (size_t i = 0; i < mInputAudioFormatContext->nb_streams; i++) {
if (mInputAudioFormatContext->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) {
inAudioStream = mInputAudioFormatContext->streams[i];
AVCodecParameters *in_codecpar = inAudioStream->codecpar;
mAudioOutStream.st = avformat_new_stream(mOutputFormatContext, NULL);
mAudioOutStream.st->id = mOutputFormatContext->nb_streams - 1;
AVCodecContext* c = avcodec_alloc_context3(audio_codec);
mAudioOutStream.enc = c;
c->sample_fmt = audio_codec->sample_fmts[0];
avcodec_parameters_to_context(c, inAudioStream->codecpar);
//copyparams from input to autput audio stream:
avcodec_parameters_copy(mAudioOutStream.st->codecpar, inAudioStream->codecpar);
mAudioOutStream.st->time_base.num = 1;
mAudioOutStream.st->time_base.den = c->sample_rate;
c->time_base = mAudioOutStream.st->time_base;
if (mOutputFormatContext->oformat->flags & AVFMT_GLOBALHEADER) {
c->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
break;
}
}
}
void Encode()
{
int cc = av_compare_ts(mVideoOutStream.next_pts, mVideoOutStream.enc->time_base,
mAudioOutStream.next_pts, mAudioOutStream.enc->time_base);
if (mAudioOutStream.st == NULL || cc <= 0) {
uint8_t* data = GetYUVFrame();//returns ready video YUV frame to work with
int ret = 0;
AVPacket pkt = { 0 };
av_init_packet(&pkt);
pkt.size = packet->dataSize;
pkt.data = data;
const int64_t duration = av_rescale_q(1, mVideoOutStream.enc->time_base, mVideoOutStream.st->time_base);
pkt.duration = duration;
pkt.pts = mVideoOutStream.next_pts;
pkt.dts = mVideoOutStream.next_pts;
mVideoOutStream.next_pts += duration;
pkt.stream_index = mVideoOutStream.st->index;
ret = av_interleaved_write_frame(mOutputFormatContext, &pkt);
} else
if(audio_time < video_time) {
//5 - duration of video in seconds
AVRational r = { 60, 1 };
auto cmp= av_compare_ts(mAudioOutStream.next_pts, mAudioOutStream.enc->time_base, 5, r);
if (cmp >= 0) {
mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max();
return true; //don't mux audio anymore
}
AVPacket a_pkt = { 0 };
av_init_packet(&a_pkt);
int ret = 0;
ret = av_read_frame(mInputAudioFormatContext, &a_pkt);
//if audio file is shorter than stop muxing when at the end of the file
if (ret == AVERROR_EOF) {
mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max();
return true;
}
a_pkt.stream_index = mAudioOutStream.st->index;
av_packet_rescale_ts(&a_pkt, inAudioStream->time_base, mAudioOutStream.st->time_base);
mAudioOutStream.next_pts += a_pkt.pts;
ret = av_interleaved_write_frame(mOutputFormatContext, &a_pkt);
}
}
现在,视频部分完美无缺。但是,如果音轨比视频持续时间长,我的总视频长度会增加大约 5% 到 20%,很明显,音频在其中起到了作用,因为视频帧正好在应该在的地方完成。
我最接近的'hack'是这部分:
AVRational r = { 60 ,1 };
auto cmp= av_compare_ts(mAudioOutStream.next_pts, mAudioOutStream.enc->time_base, 5, r);
if (cmp >= 0) {
mAudioOutStream.next_pts = (int64_t)std::numeric_limits<int64_t>::max();
return true;
}
这里我试图比较音频流的 next_pts 和为视频文件设置的总时间,即 5 秒。通过设置 r = {60,1} 我正在通过音频流的 time_base 转换这些秒数。至少那是我相信我正在做的事情。有了这个 hack,当使用标准 AAC 文件时,我与正确的电影长度的偏差非常小,即 44100 的采样率,立体声。但是,如果我使用更多有问题的样本进行测试,例如 AAC 采样率 16000,单声道 - 那么视频文件的大小几乎会增加一整秒。
如果有人能指出我在这里做错了什么,我将不胜感激。
重要提示:我没有为任何上下文设置持续时间。我根据视频帧数控制多路复用 session 的终止。音频输入流当然有持续时间,但它对我没有帮助,因为视频持续时间是定义电影长度的因素。
更新:
这是第二次赏金尝试。
更新 2:
实际上,我的 {den,num} 音频时间戳是错误的,而 {1,1} 确实是要走的路,正如答案所解释的那样。阻止它工作的是这一行中的一个错误(我的错):
mAudioOutStream.next_pts += a_pkt.pts;
必须是:
mAudioOutStream.next_pts = a_pkt.pts;
该错误导致 pts 呈指数增长,这导致非常早地到达流的末尾(以 pts 而言),因此导致音频流比预期的更早终止。
最佳答案
问题是你告诉它比较给定的音频时间和 5报价 60 seconds per tick .我真的很惊讶它在某些情况下有效,但我想这真的取决于具体的 time_base给定的音频流。
假设音频有 time_base的 1/25并且流在6秒,比你想要的多,所以你想要 av_compare_ts返回 0或 1 .鉴于这些条件,您将获得以下值:
mAudioOutStream.next_pts = 150
mAudioOutStream.enc->time_base = 1/25
因此您调用 av_compare_ts具有以下参数:
ts_a = 150
tb_a = 1/25
ts_b = 5
tb_b = 60/1
现在让我们看看av_compare_ts的实现:
int av_compare_ts(int64_t ts_a, AVRational tb_a, int64_t ts_b, AVRational tb_b)
{
int64_t a = tb_a.num * (int64_t)tb_b.den;
int64_t b = tb_b.num * (int64_t)tb_a.den;
if ((FFABS(ts_a)|a|FFABS(ts_b)|b) <= INT_MAX)
return (ts_a*a > ts_b*b) - (ts_a*a < ts_b*b);
if (av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b)
return -1;
if (av_rescale_rnd(ts_b, b, a, AV_ROUND_DOWN) < ts_a)
return 1;
return 0;
}
给定上面的值,你得到:
a = 1 * 1 = 1
b = 60 * 25 = 1500
然后 av_rescale_rnd使用这些参数调用:
a = 150
b = 1
c = 1500
rnd = AV_ROUND_DOWN
给定我们的参数,我们实际上可以剥离整个函数 av_rescale_rnd到下一行。 (av_rescale_rnd 的函数体比较长,我不会复制整个函数体,你可以看一下 here。)
return (a * b) / c;
这将返回 (150 * 1) / 1500 ,即 0 .
因此 av_rescale_rnd(ts_a, a, b, AV_ROUND_DOWN) < ts_b将解析为 true , 因为 0小于 ts_b ( 5 ),等等 av_compare_ts将返回 -1 ,这完全不是你想要的。
如果您更改 r至1/1它应该可以工作,因为现在你的 5实际上将被视为 5 seconds :
ts_a = 150
tb_a = 1/25
ts_b = 5
tb_b = 1/1
在 av_compare_ts我们现在得到:
a = 1 * 1 = 1
b = 1 * 25 = 25
然后 av_rescale_rnd使用这些参数调用:
a = 150
b = 1
c = 25
rnd = AV_ROUND_DOWN
这将返回 (150 * 1) / 25 ,即 6 .
6大于 5 ,条件失败,av_rescale_rnd再次调用,这次是:
a = 5
b = 25
c = 1
rnd = AV_ROUND_DOWN
这将返回 (5 * 25) / 1 ,即 125 .小于 150 ,因此 1被退回,瞧你的问题就解决了。
如果 step_size 大于 1
如果 step_size您的音频流不是 1 ,你需要修改你的r考虑到这一点,例如step_size = 1024 :
r = { 1, 1024 };
让我们快速回顾一下现在发生的事情:
大约 6 秒:
mAudioOutStream.next_pts = 282
mAudioOutStream.enc->time_base = 1/48000
av_compare_ts获取以下参数:
ts_a = 282
tb_a = 1/48000
ts_b = 5
tb_b = 1/1024
因此:
a = 1 * 1024 = 1024
b = 1 * 48000 = 48000
在 av_rescale_rnd :
a = 282
b = 1024
c = 48000
rnd = AV_ROUND_DOWN
(a * b) / c会给(282 * 1024) / 48000 = 288768 / 48000这是6 .
与 r={1,1}你会得到0再次,因为它会计算 (281 * 1) / 48000 .
关于c++ - FFMPEG:多路复用不同持续时间的流,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/49342379/
我的瘦服务器配置了nginx,我的ROR应用程序正在它们上运行。在我发布代码更新时运行thinrestart会给我的应用程序带来一些停机时间。我试图弄清楚如何优雅地重启正在运行的Thin实例,但找不到好的解决方案。有没有人能做到这一点? 最佳答案 #Restartjustthethinserverdescribedbythatconfigsudothin-C/etc/thin/mysite.ymlrestartNginx将继续运行并代理请求。如果您将Nginx设置为使用多个上游服务器,例如server{listen80;server
我需要检查DateTime是否采用有效的ISO8601格式。喜欢:#iso8601?我检查了ruby是否有特定方法,但没有找到。目前我正在使用date.iso8601==date来检查这个。有什么好的方法吗?编辑解释我的环境,并改变问题的范围。因此,我的项目将使用jsapiFullCalendar,这就是我需要iso8601字符串格式的原因。我想知道更好或正确的方法是什么,以正确的格式将日期保存在数据库中,或者让ActiveRecord完成它们的工作并在我需要时间信息时对其进行操作。 最佳答案 我不太明白你的问题。我假设您想检查
这个问题在这里已经有了答案:Railsformattingdate(4个答案)关闭4年前。我想格式化Time.Now函数以显示YYYY-MM-DDHH:MM:SS而不是:“2018-03-0909:47:19+0000”该函数需要放在时间中.现在功能。require‘roo’require‘roo-xls’require‘byebug’file_name=ARGV.first||“Template.xlsx”excel_file=Roo::Spreadsheet.open(“./#{file_name}“,extension::xlsx)xml=Nokogiri::XML::Build
我正在尝试解析一个CSV文件并使用SQL命令自动为其创建一个表。CSV中的第一行给出了列标题。但我需要推断每个列的类型。Ruby中是否有任何函数可以找到每个字段中内容的类型。例如,CSV行:"12012","Test","1233.22","12:21:22","10/10/2009"应该产生像这样的类型['integer','string','float','time','date']谢谢! 最佳答案 require'time'defto_something(str)if(num=Integer(str)rescueFloat(s
如何将send与+=一起使用?a=20;a.send"+=",10undefinedmethod`+='for20:Fixnuma=20;a+=10=>30 最佳答案 恐怕你不能。+=不是方法,而是语法糖。参见http://www.ruby-doc.org/docs/ProgrammingRuby/html/tut_expressions.html它说Incommonwithmanyotherlanguages,Rubyhasasyntacticshortcut:a=a+2maybewrittenasa+=2.你能做的最好的事情是:
前置步骤我们都操作完了,这篇开始介绍jenkins的集成。话不多说,看操作1、登录进入jenkins后会让你选择安装插件,选择第一个默认的就行。安装完成后设置账号密码,重新登录。2、配置JDK和Git都需要执行路径,所以需要先把执行路径找到,先进入服务器的docker容器,2.1JDK的路径root@69eef9ee86cf:/usr/bin#echo$JAVA_HOME/usr/local/openjdk-82.2Git的路径root@69eef9ee86cf:/#whichgit/usr/bin/git3、先配置JDK和Git。点击:ManageJenkins>>GlobalToolCon
2022/8/4更新支持加入水印水印必须包含透明图像,并且水印图像大小要等于原图像的大小pythonconvert_image_to_video.py-f30-mwatermark.pngim_dirout.mkv2022/6/21更新让命令行参数更加易用新的命令行使用方法pythonconvert_image_to_video.py-f30im_dirout.mkvFFMPEG命令行转换一组JPG图像到视频时,是将这组图像视为MJPG流。我需要转换一组PNG图像到视频,FFMPEG就不认了。pyav内置了ffmpeg库,不需要系统带有ffmpeg工具因此我使用ffmpeg的python包装p
我正在尝试查询我的Rails数据库(Postgres)中的购买表,我想查询时间范围。例如,我想知道在所有日期的下午2点到3点之间进行了多少次购买。此表中有一个created_at列,但我不知道如何在不搜索特定日期的情况下完成此操作。我试过:Purchases.where("created_atBETWEEN?and?",Time.now-1.hour,Time.now)但这最终只会搜索今天与那些时间的日期。 最佳答案 您需要使用PostgreSQL'sdate_part/extractfunction从created_at中提取小时
我基本上来自Java背景并且努力理解Ruby中的模运算。(5%3)(-5%3)(5%-3)(-5%-3)Java中的上述操作产生,2个-22个-2但在Ruby中,相同的表达式会产生21个-1-2.Ruby在逻辑上有多擅长这个?模块操作在Ruby中是如何实现的?如果将同一个操作定义为一个web服务,两个服务如何匹配逻辑。 最佳答案 在Java中,模运算的结果与被除数的符号相同。在Ruby中,它与除数的符号相同。remainder()在Ruby中与被除数的符号相同。您可能还想引用modulooperation.
我对如何计算通过{%assignvar=0%}赋值的变量加一完全感到困惑。这应该是最简单的任务。到目前为止,这是我尝试过的:{%assignamount=0%}{%forvariantinproduct.variants%}{%assignamount=amount+1%}{%endfor%}Amount:{{amount}}结果总是0。也许我忽略了一些明显的东西。也许有更好的方法。我想要存档的只是获取运行的迭代次数。 最佳答案 因为{{incrementamount}}将输出您的变量值并且不会影响{%assign%}定义的变量,我