在跟着夏老师学习ffmpeg时遇到一个地方不太懂,就是自己手写生成了500帧AVFrame数据,然后每次write进ofstream绑定的文件时进行一个count计数,
for (int i = 0; i < 500; i++)
{
//生成avframe数据
for (int y = 0; y < c->height; y++)
{
for (int x = 0; x < c->width; x++)
{
frame->data[0][y * frame->linesize[0] + x] = x + y + i * 3;
}
}
for (int y = 0; y < c->height / 2; y++)
{
for (int x = 0; x < c->width / 2; x++)
{
frame->data[1][y * frame->linesize[1] + x] = 128 + y + i * 2;
frame->data[2][y * frame->linesize[2] + x] = 64 + x + i * 5;
}
}
frame->pts = i;//显示的时间
auto pkt = en.Encode(frame);//接收成功的话这里返回了一个AVPacket,不会调用到Encode里的av_packet_free
if (pkt)
{
count++;
ofs.write((char*)pkt->data, pkt->size);//把pkt->data里size大小的内容写到ofs关联的文件里
av_packet_free(&pkt);
}
}
按照这个思路执行下来count应该等于500 但是运行后结果只有440
在课上夏老师说要在结束以后再传个空frame,来释放剩下的缓冲数据,
std::vector<AVPacket*> XEncode::End()
{
std::vector<AVPacket*> res;
unique_lock<mutex>lock(mux_);
if (!c_)return res;
auto re = avcodec_send_frame(c_, NULL);//!!!!!!!!!!!!
if (re!= 0)return res;
while (re >= 0 )
{
auto pkt = av_packet_alloc();
re = avcodec_receive_packet(c_, pkt);
if (re != 0)
{
av_packet_free(&pkt);
break;
}
res.push_back(pkt);
}
return res;
}
在所有的帧都已经被输入后,加上这个传空frame的函数以后,最终的count计数能够到500
auto pkts = en.End();
for (auto pkt : pkts)
{
count++;
ofs.write((char*)pkt->data, pkt->size);
av_packet_free(&pkt);
}
ofs.close();
但是为什么呢,这个空frame到底有什么用呢,还是说只要是传输完所有帧以后固定加上一段通过avcodec_send_frame 函数向编码器传入空 frame的操作就好了?
在找了许多文章以后了解到
当 avcodec_send_frame 函数传入空 frame 时,它会设置一个标志位。这个标志位的作用非常重要。它表示当前是最后一帧数据,后续的数据将无效。在 FFmpeg 的编码过程中,这个标志位可以让编码器知道当前的状态,以便采取相应的行动。 例如,编码器可以根据这个标志位判断是否需要进行 flush 操作,将剩余的帧压缩成 AVPacket。 如果标志位被设置,编码器会进入一种特殊的模式,处理剩余的数据并输出最后的编码结果。在实际应用中,这个标志位可以用于控制编码的结束时机。比如,当我们处理一个视频文件时,可以在确定所有的帧都已经被输入后,传入一个空 frame,触发编码器的结束操作。这意味着编码器应该将其内部缓冲区中的剩余数据全部编码并输出。这种机制在一些特定的场景下非常有用,比如在编码过程结束时,确保所有的数据都被处理完毕。通过传入空 frame,用户可以明确地告诉编码器已经没有更多的数据输入,让编码器将缓冲区中的数据全部处理掉,从而保证编码结果的完整性。同时,这也有助于在一些实时编码的场景中,及时处理剩余的数据,避免数据的积压和丢失!!!!