- html - 出于某种原因,IE8 对我的 Sass 文件中继承的 html5 CSS 不友好?
- JMeter 在响应断言中使用 span 标签的问题
- html - 在 :hover and :active? 上具有不同效果的 CSS 动画
- html - 相对于居中的 html 内容固定的 CSS 重复背景?
我是解码器和 FFmpeg 的新手。我需要的是实现可以通过某个步骤读取帧的逻辑(例如:20),换句话说,我有一个文件,我需要读取帧 0、20、40、60 ......
我所做的是
AVFrame * m_pAVFrame = nullptr;
int firstFrameIdx = 0;
while(true)
{
if(firstFrameIdx > 0)
{
int64_t seekTarget = FrameToPts(m_pAVStream, firstFrameIdx);
nRet = av_seek_frame(m_pAVFormatCtx, m_streamIdx, seekTarget, AVSEEK_FLAG_FRAME);
}
nRet = av_read_frame(m_pAVFormatCtx, m_pAVPkt);
ret = avcodec_send_packet(m_pAVCodecCtx, m_pAVPkt);
ret = avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame);
firstFrameIdx+=20;
}
但问题是
av_seek_frame
将指针移动到 Iframe,假设视频文件有每个 15 的关键帧(当然它可能是不同的数字),例如 0、15、30 ......所以这意味着如果我尝试寻找第 20 帧,实际上我得到了到第 15 帧。
AVFrame
有房产
coded_picture_number
这在我的情况下可能很有用,我尝试将这些返回值放入 vector 中,我发现这些值无关紧要
0, 15, 30, 45...
例如,如果我可以进行搜索然后获取一个帧来询问订单号(例如:15),那么我可以理解 Iframe 是 15,为了达到第 20 帧,我需要阅读并跳过 5 帧所以结果,我到了第 20 帧,但是正如你在上面看到的那样,我询问订单号并得到奇怪的值,如
0, 2, 1, 3...
没有任何关系...
bool FFmpegDecoder::Init(unsigned char const * pData, int dataSize, int reqId, bool bUseHWAccel, FFmpegDecoderCallback * pCB)
{
Deinit();
// From memory:
if (pData == nullptr || dataSize == 0)
{
printf("FFmpegDecoder::Init FAILED: neither filename nor memory data were given !\n");
return false;
}
m_pIoCtx = std::make_shared<AVIOContextMem>(pData, dataSize);
if (m_pIoCtx->IsValid() == false)
{
printf("FFmpegDecoder::Init FAILED: m_pIoCtx is nullptr !\n");
return false;
}
m_reqId = reqId;
m_bUseHWAccel = bUseHWAccel;
m_pCB = pCB;
m_pData = pData;
m_dataSize = dataSize;
m_bRequestedAbort = false;
m_pAVPkt = av_packet_alloc();
av_init_packet(m_pAVPkt);
m_pAVFrame = av_frame_alloc();
m_pAVFrameRGB = av_frame_alloc();
if (m_bUseHWAccel)
{
m_pSwAVFrameForHw = av_frame_alloc();
}
m_pAVFormatCtx = avformat_alloc_context();
m_pIoCtx->initAVFormatContext(m_pAVFormatCtx);
if (avformat_open_input(&m_pAVFormatCtx, "", nullptr, nullptr) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_open_input\n");
return false;
}
if (avformat_find_stream_info(m_pAVFormatCtx, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_find_stream_info\n");
return false;
}
//av_dump_format(ctx_format, 0, "", false);
for (int i = 0; i < (int)m_pAVFormatCtx->nb_streams; i++)
{
if (m_pAVFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_streamIdx = i;
m_pAVStream = m_pAVFormatCtx->streams[i];
break;
}
}
if (m_pAVStream == nullptr)
{
printf("FFmpegDecoder::InitFFmpeg: failed to find video stream\n");
return false;
}
m_pAVCodec = avcodec_find_decoder(m_pAVStream->codecpar->codec_id);
if (!m_pAVCodec)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_find_decoder\n");
return false;
}
m_pAVCodecCtx = avcodec_alloc_context3(m_pAVCodec);
if (!m_pAVCodecCtx)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_alloc_context3\n");
return false;
}
if (avcodec_parameters_to_context(m_pAVCodecCtx, m_pAVStream->codecpar) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_parameters_to_context\n");
return false;
}
if (m_bUseHWAccel)
{
AVHWDeviceType hwDevType = AV_HWDEVICE_TYPE_DXVA2;
g_hwPixFormat = find_fmt_by_hw_type(hwDevType);
m_pAVCodecCtx->get_format = get_hw_format;
av_opt_set_int(m_pAVCodecCtx, "refcounted_frames", 1, 0);
if (av_hwdevice_ctx_create(&m_pBufferRefForHw, hwDevType, NULL, NULL, 0) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_hwdevice_ctx_create\n");
return false;
}
m_pAVCodecCtx->hw_device_ctx = av_buffer_ref(m_pBufferRefForHw);
}
if (avcodec_open2(m_pAVCodecCtx, m_pAVCodec, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_open2\n");
return false;
}
m_pAVFrameRGB->format = AV_PIX_FMT_BGR24;
m_pAVFrameRGB->width = m_pAVCodecCtx->width;
m_pAVFrameRGB->height = m_pAVCodecCtx->height;
if (av_frame_get_buffer(m_pAVFrameRGB, 32) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_frame_get_buffer\n");
return false;
}
m_streamRotationDegrees = GetAVStreamRotation(m_pAVStream);
m_estimatedFramesCount = 0;
assert(m_pAVFormatCtx->nb_streams > 0);
if (m_pAVFormatCtx->nb_streams > 0)
{
m_estimatedFramesCount = m_pAVFormatCtx->streams[0]->nb_frames;
}
//InitConvertColorSpace
// Init converter from YUV420p to BGR:
if (m_bUseHWAccel)
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_NV12, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
else
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, m_pAVCodecCtx->pix_fmt, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
if (!m_pSwsCtxConvertImg)
{
printf("FFmpegDecoder::InitFFmpeg: error in sws_getContext\n");
return false;
}
m_bInitOK = true;
return true;
}
具有最后更改的解码逻辑
void FFmpegDecoder::DecodeWithStep(int step)
{
step = 20;
int currentFramePos = 0;
int number_of_errors = 0;
const int MAX_ERROR_NUM = 10;
while (true)
{
if (step > 0)
{
int seekPos = currentFramePos + step;
int64_t seekTarget = FrameToPts(m_pAVStream, seekPos);
if (av_seek_frame(m_pAVFormatCtx, m_streamIdx, seekTarget, AVSEEK_FLAG_FRAME) < 0)
{
number_of_errors++;
}
else
{
currentFramePos = seekPos;
m_is_seeked = true;
}
}
if (av_read_frame(m_pAVFormatCtx, m_pAVPkt) == 0)
{
if (m_pAVPkt->stream_index == m_streamIdx) //to make sure that I dont get packets from other streams
{
if (m_is_seeked)
{
avcodec_flush_buffers(m_pAVCodecCtx);
m_is_seeked = false;
}
if (avcodec_send_packet(m_pAVCodecCtx, m_pAVPkt) == 0)
{
printf("----- BATCH\n");
while (avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame) == 0)
{
ProcessFrame(m_pAVFrame);
av_frame_unref(m_pAVFrame);
currentFramePos++;
printf("----- cur position (%d) \n", currentFramePos);
}
}
av_packet_unref(m_pAVPkt);
}
}
else
{
number_of_errors++;
}
if (number_of_errors == MAX_ERROR_NUM)
{
printf("EXIT\n");
break;
}
}
}
更新
bool FFmpegDecoder::Init(unsigned char const * pData, int dataSize, int reqId, bool bUseHWAccel, FFmpegDecoderCallback * pCB)
{
Deinit();
// From memory:
if (pData == nullptr || dataSize == 0)
{
printf("FFmpegDecoder::Init FAILED: neither filename nor memory data were given !\n");
return false;
}
m_pIoCtx = std::make_shared<AVIOContextMem>(pData, dataSize);
if (m_pIoCtx->IsValid() == false)
{
printf("FFmpegDecoder::Init FAILED: m_pIoCtx is nullptr !\n");
return false;
}
m_reqId = reqId;
m_bUseHWAccel = bUseHWAccel;
m_pCB = pCB;
m_pData = pData;
m_dataSize = dataSize;
m_bRequestedAbort = false;
m_pAVPkt = av_packet_alloc();
av_init_packet(m_pAVPkt);
m_pAVFrame = av_frame_alloc();
m_pAVFrameRGB = av_frame_alloc();
if (m_bUseHWAccel)
{
m_pSwAVFrameForHw = av_frame_alloc();
}
m_pAVFormatCtx = avformat_alloc_context();
m_pIoCtx->initAVFormatContext(m_pAVFormatCtx);
if (avformat_open_input(&m_pAVFormatCtx, "", nullptr, nullptr) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_open_input\n");
return false;
}
if (avformat_find_stream_info(m_pAVFormatCtx, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avformat_find_stream_info\n");
return false;
}
//av_dump_format(ctx_format, 0, "", false);
for (int i = 0; i < (int)m_pAVFormatCtx->nb_streams; i++)
{
if (m_pAVFormatCtx->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO)
{
m_streamIdx = i;
m_pAVStream = m_pAVFormatCtx->streams[i];
break;
}
}
if (m_pAVStream == nullptr)
{
printf("FFmpegDecoder::InitFFmpeg: failed to find video stream\n");
return false;
}
m_pAVCodec = avcodec_find_decoder(m_pAVStream->codecpar->codec_id);
if (!m_pAVCodec)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_find_decoder\n");
return false;
}
m_pAVCodecCtx = avcodec_alloc_context3(m_pAVCodec);
if (!m_pAVCodecCtx)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_alloc_context3\n");
return false;
}
if (avcodec_parameters_to_context(m_pAVCodecCtx, m_pAVStream->codecpar) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_parameters_to_context\n");
return false;
}
if (m_bUseHWAccel)
{
AVHWDeviceType hwDevType = AV_HWDEVICE_TYPE_DXVA2;
g_hwPixFormat = find_fmt_by_hw_type(hwDevType);
m_pAVCodecCtx->get_format = get_hw_format;
av_opt_set_int(m_pAVCodecCtx, "refcounted_frames", 1, 0);
if (av_hwdevice_ctx_create(&m_pBufferRefForHw, hwDevType, NULL, NULL, 0) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_hwdevice_ctx_create\n");
return false;
}
m_pAVCodecCtx->hw_device_ctx = av_buffer_ref(m_pBufferRefForHw);
}
if (avcodec_open2(m_pAVCodecCtx, m_pAVCodec, nullptr) < 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in avcodec_open2\n");
return false;
}
m_pAVFrameRGB->format = AV_PIX_FMT_BGR24;
m_pAVFrameRGB->width = m_pAVCodecCtx->width;
m_pAVFrameRGB->height = m_pAVCodecCtx->height;
if (av_frame_get_buffer(m_pAVFrameRGB, 32) != 0)
{
printf("FFmpegDecoder::InitFFmpeg: error in av_frame_get_buffer\n");
return false;
}
m_streamRotationDegrees = GetAVStreamRotation(m_pAVStream);
m_estimatedFramesCount = 0;
assert(m_pAVFormatCtx->nb_streams > 0);
if (m_pAVFormatCtx->nb_streams > 0)
{
m_estimatedFramesCount = m_pAVFormatCtx->streams[0]->nb_frames;
}
//InitConvertColorSpace
// Init converter from YUV420p to BGR:
if (m_bUseHWAccel)
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_NV12, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
else
{
m_pSwsCtxConvertImg = sws_getContext(m_pAVCodecCtx->width, m_pAVCodecCtx->height, m_pAVCodecCtx->pix_fmt, m_pAVCodecCtx->width, m_pAVCodecCtx->height, AV_PIX_FMT_RGB24, SWS_FAST_BILINEAR, NULL, NULL, NULL);
}
if (!m_pSwsCtxConvertImg)
{
printf("FFmpegDecoder::InitFFmpeg: error in sws_getContext\n");
return false;
}
m_bInitOK = true;
return true;
}
void FFmpegDecoder::DecodeWithStep(int step)
{
step = 20;
int currentFramePos = 0;
int number_of_errors = 0;
const int MAX_ERROR_NUM = 10;
int seekPos = 0;
while (true)
{
if (step > 1)
{
seekPos = currentFramePos + step;
int64_t seekTarget = FrameToPts(m_pAVStream, seekPos);
if (av_seek_frame(m_pAVFormatCtx, m_streamIdx, seekTarget, AVSEEK_FLAG_FRAME) < 0)
{
number_of_errors++;
}
else
{
m_is_seeked = true;
}
}
while (true)
{
if (av_read_frame(m_pAVFormatCtx, m_pAVPkt) == 0)
{
if (m_pAVPkt->stream_index == m_streamIdx) //to make sure that I dont get packets from other streams
{
if (m_is_seeked)
{
avcodec_flush_buffers(m_pAVCodecCtx);
m_is_seeked = false;
}
if (avcodec_send_packet(m_pAVCodecCtx, m_pAVPkt) == 0)
{
int ret = 0;
while (ret >= 0)
{
ret = avcodec_receive_frame(m_pAVCodecCtx, m_pAVFrame);
if (ret == AVERROR(EAGAIN) || ret == AVERROR_EOF)
{
av_frame_unref(m_pAVFrame);
break;
}
currentFramePos = m_pAVFrame->display_picture_number; //In order to get position of currect frame (seek move poiter to the key frame)
if (currentFramePos < seekPos) //Some frames need to be skiped in order to reach needed frame
{
printf("----- SKIP : cur position (%d) \n", currentFramePos);
av_frame_unref(m_pAVFrame);
continue;
}
ProcessFrame(m_pAVFrame); //needed frame was processed
av_frame_unref(m_pAVFrame);
printf("----- cur position (%d) \n", currentFramePos);
break;
}
}
av_packet_unref(m_pAVPkt);
}
else
{
av_packet_unref(m_pAVPkt); //we got a frame from the wrong stream
}
}
else
{
number_of_errors++;
}
if (number_of_errors == MAX_ERROR_NUM)
{
printf("EXIT1\n");
break;
}
}
if (number_of_errors == MAX_ERROR_NUM)
{
printf("EXIT2\n");
break;
}
}
}
int64_t FrameToPts(AVStream* pavStream, int frame)
{
return (int64_t(frame) * pavStream->r_frame_rate.den * pavStream->time_base.den) /
(int64_t(pavStream->r_frame_rate.num) * pavStream->time_base.num);
}
最佳答案
您寻求“准确”到特定帧......因此您使用向后标志寻找您想要的帧,以确保您获得该帧或前一个帧。如果案例是前一个案例,您将解码直到获得实际请求的帧。
我可以看到您缺少两个重要步骤:-
while (avcodec_receive_frame(...) == 0) { process frame here }
解决方案 1(无 Seek - 小步骤或未知 fps 并确保准确性)
public void Aleksey()
{
AVPacket* m_pAVPkt = av_packet_alloc();
AVFrame* m_pAVFrame= av_frame_alloc();
int ret;
int step = 1;
int curFrameNumber = 0;
while (true)
{
ret = av_read_frame(demuxer.FormatContext, m_pAVPkt);
if (m_pAVPkt->stream_index != 0) { av_packet_unref(m_pAVPkt); continue; }
ret = avcodec_send_packet(codecCtx, m_pAVPkt);
av_packet_unref(m_pAVPkt);
while (true)
{
ret = avcodec_receive_frame(codecCtx, m_pAVFrame);
if (ret != 0) { av_frame_unref(m_pAVFrame); break; }
curFrameNumber++;
if (curFrameNumber % step == 0)
Console.WriteLine($"[pts: {m_pAVFrame->pts}] [time: {Utils.TicksToTime((long)(m_pAVFrame->pts * demuxer.VideoStreams[0].Timebase))}] [displaynumber: {m_pAVFrame->display_picture_number}] [codednumber: {m_pAVFrame->coded_picture_number}]");
av_frame_unref(m_pAVFrame);
}
}
}
输出(我们看到正确的 pts/cur 帧时间,但我们没有得到带有 display_picture_number/coded_picture_number 的帧号)。看来你可以用
m_pAVCodecCtx->frame_number 尽管如此,但我敢打赌它将在每个 av_seek_frame 上重置,然后在 avcodec_flush_buffers 上重置。
[pts: 0] [time: 00:00:00:000] [displaynumber: 0] [codednumber: 0] [framenumber: 1]
[pts: 42] [time: 00:00:00:042] [displaynumber: 0] [codednumber: 3] [framenumber: 2]
[pts: 83] [time: 00:00:00:083] [displaynumber: 0] [codednumber: 2] [framenumber: 3]
[pts: 125] [time: 00:00:00:125] [displaynumber: 0] [codednumber: 4] [framenumber: 4]
[pts: 167] [time: 00:00:00:167] [displaynumber: 0] [codednumber: 1] [framenumber: 5]
[pts: 209] [time: 00:00:00:209] [displaynumber: 0] [codednumber: 7] [framenumber: 6]
[pts: 250] [time: 00:00:00:250] [displaynumber: 0] [codednumber: 6] [framenumber: 7]
[pts: 292] [time: 00:00:00:292] [displaynumber: 0] [codednumber: 8] [framenumber: 8]
[pts: 334] [time: 00:00:00:334] [displaynumber: 0] [codednumber: 5] [framenumber: 9]
[pts: 375] [time: 00:00:00:375] [displaynumber: 0] [codednumber: 11] [framenumber: 10]
此代码确保您将准确执行 X 步帧,但它根本不使用搜索,这对性能不利。如果您的查找步骤很小,那很好,但如果您想使用大步骤,那么我们可以使用包含的查找。
AVFormatContext*m_pAVFormatCtx;
AVCodecContext* m_pAVCodecCtx;
AVStream* pavStream;
AVPacket* m_pAVPkt;
AVFrame* m_pAVFrame;
int m_streamIdx;
long startTime;
double avgFrameDuration;
double m_streamTimebase;
public void Prepare()
{
// Using your variable names (mapped with mine)
m_pAVFormatCtx = demuxer.FormatContext;
m_pAVCodecCtx = codecCtx;
pavStream = demuxer.VideoStreams[0].AVStream;
m_streamIdx = pavStream->index;
m_streamTimebase= av_q2d(pavStream->time_base) * 1000.0 * 10000.0; // Convert timebase to ticks so we can easily convert stream's timestamps to ticks
startTime = pavStream->start_time != AV_NOPTS_VALUE ? (long)(pavStream->start_time * m_streamTimebase) : 0; // We will need this when we seek (adding it to seek timestamp)
avgFrameDuration= 10000000 / av_q2d(pavStream->avg_frame_rate); // eg. 1 sec / 25 fps = 400.000 ticks (40ms)
// Prepare packet/frame for demux/decode
m_pAVPkt = av_packet_alloc();
m_pAVFrame = av_frame_alloc();
}
public void GetFrame(int index) // Zero-based frame index
{
int ret;
long frameTimestamp = (long) (index * avgFrameDuration); // Calculation of FrameX timestamp (based on fps/avgFrameDuration)
Console.WriteLine($"Searching for {Utils.TicksToTime(frameTimestamp)}");
// Seeking at frameTimestamp or previous I/Key frame and flushing codec
ret = av_seek_frame(m_pAVFormatCtx, -1, (startTime + frameTimestamp) / 10, AVSEEK_FLAG_FRAME | AVSEEK_FLAG_BACKWARD);
avcodec_flush_buffers(m_pAVCodecCtx);
if (ret < 0) return; // handle seek error
while (true)
{
// Demux Packet
ret = av_read_frame(m_pAVFormatCtx, m_pAVPkt);
if (ret != 0) break; // handle EOF/error
if (m_pAVPkt->stream_index != m_streamIdx) { av_packet_unref(m_pAVPkt); continue; } // Exclude other streams
// Send Packet for decoding
ret = avcodec_send_packet(codecCtx, m_pAVPkt);
av_packet_unref(m_pAVPkt);
if (ret != 0) break; // handle EOF/error
while (true)
{
// Receive all available frames for the decoder
ret = avcodec_receive_frame(codecCtx, m_pAVFrame);
if (ret != 0) { av_frame_unref(m_pAVFrame); break; }
// Get frame pts (prefer best_effort_timestamp)
long curPts = m_pAVFrame->best_effort_timestamp == AV_NOPTS_VALUE ? m_pAVFrame->pts : m_pAVFrame->best_effort_timestamp;
if (curPts == AV_NOPTS_VALUE) { { av_frame_unref(m_pAVFrame); continue; } }
// Skip frames before our actual requested frame
Console.WriteLine($"[Skip] [pts: {curPts}] [time: {Utils.TicksToTime((long)(curPts * m_streamTimebase))}]");
if ((long)(curPts * m_streamTimebase) / 10000 < frameTimestamp / 10000) { av_frame_unref(m_pAVFrame); continue; }
Console.WriteLine($"[Found] [pts: {curPts}] [time: {Utils.TicksToTime((long)(curPts * m_streamTimebase))}]");
av_frame_unref(m_pAVFrame);
return;
}
}
}
使用以下代码进行测试
Prepare();
GetFrame(100);
GetFrame(200);
GetFrame(300);
GetFrame(100);
给出输出
Searching for 00:00:04:129
[Skip] [pts: 4004] [time: 00:00:04:004]
[Skip] [pts: 4046] [time: 00:00:04:046]
[Skip] [pts: 4087] [time: 00:00:04:087]
[Skip] [pts: 4129] [time: 00:00:04:129]
[Found] [pts: 4129] [time: 00:00:04:129]
Searching for 00:00:08:299
[Skip] [pts: 8008] [time: 00:00:08:008]
[Skip] [pts: 8050] [time: 00:00:08:050]
[Skip] [pts: 8091] [time: 00:00:08:091]
[Skip] [pts: 8133] [time: 00:00:08:133]
[Skip] [pts: 8175] [time: 00:00:08:175]
[Skip] [pts: 8217] [time: 00:00:08:217]
[Skip] [pts: 8258] [time: 00:00:08:258]
[Skip] [pts: 8300] [time: 00:00:08:300]
[Found] [pts: 8300] [time: 00:00:08:300]
Searching for 00:00:12:470
[Skip] [pts: 12012] [time: 00:00:12:012]
[Skip] [pts: 12054] [time: 00:00:12:054]
[Skip] [pts: 12095] [time: 00:00:12:095]
[Skip] [pts: 12137] [time: 00:00:12:137]
[Skip] [pts: 12179] [time: 00:00:12:179]
[Skip] [pts: 12221] [time: 00:00:12:221]
[Skip] [pts: 12262] [time: 00:00:12:262]
[Skip] [pts: 12304] [time: 00:00:12:304]
[Skip] [pts: 12346] [time: 00:00:12:346]
[Skip] [pts: 12387] [time: 00:00:12:387]
[Skip] [pts: 12429] [time: 00:00:12:429]
[Skip] [pts: 12471] [time: 00:00:12:471]
[Found] [pts: 12471] [time: 00:00:12:471]
Searching for 00:00:04:129
[Skip] [pts: 4004] [time: 00:00:04:004]
[Skip] [pts: 4046] [time: 00:00:04:046]
[Skip] [pts: 4087] [time: 00:00:04:087]
[Skip] [pts: 4129] [time: 00:00:04:129]
[Found] [pts: 4129] [time: 00:00:04:129]
注意:对于第二种解决方案,仍有空间可以更好地处理小步骤(都在相同关键帧内的帧)。您可以通过存储最后一次搜索的关键帧并将其与当前搜索的关键帧进行比较来验证这一点。如果是这种情况,您可以避免刷新编解码器并使用 ANY 标志在前一帧的位置重新搜索以进行精确搜索(从原来的位置继续解码器)。不过,新帧的时间戳必须大于前一个。
关于c++ - 如何在 av_seek_frame 之后获取当前的 AVFrame 序号?,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/67397968/
这个问题在这里已经有了答案: Batch renaming files with Bash (10 个答案) 关闭 6 年前。 我想将包含子目录的目录中的文件重命名为父目录名称+序号。 例如: he
我需要帮助! HTML 最佳答案 试试这个 var array = document.getElementById('wrapperDiv'); for (var i = 0, len = a
我正在本地主机上安装 SSL。当我尝试运行时 openssl genrsa -aes256 -out private.key 2048 cmd 中的命令,我收到错误警告。 The ordinal 3
我想做(某种程度上)与 this old post 相反的事情是关于; 这是我的示例表: ID NAME ---- ---- 1 Apple 2 Apple 3 Apple
我读过一些不错的帖子,比如 this one它解释了在给定 int 时接收序数的方法。 现在,我有一个 LocalDate 对象,我可以使用我的 Thymeleaf 模板中的任何 DateTimeFo
我是解码器和 FFmpeg 的新手。我需要的是实现可以通过某个步骤读取帧的逻辑(例如:20),换句话说,我有一个文件,我需要读取帧 0、20、40、60 ...... 我所做的是 AVFrame *
我安装了 "mysql-installer-community-8.0.16.0.msi" .安装期间一切顺利。我什至可以为我的项目创建一个新的数据库和表。我在 C++ 中使用它。编译和链接一切顺利。
有谁知道传递给 dwmapi.dll 序号 #113 的参数是什么? (Windows 7) 我正在尝试将此方法合并到我正在编写的应用程序中。据我所知,此方法负责为 Windows 执行 Aero p
我有一个在 Hibernate 的 EntityManager 中管理的实体。这个实体确实有一个由 ENUM 表示的状态。 Hibernate 确实将数据库中的 ENUM 值保存为整数。 当 ENUM
关于我的问题:Detect the the vtable offset of a specific virtual function (using Visual C++) : 给定: struct A
我刚刚在 Windows 10 上安装了 Anaconda 5.3 64 位 (Anaconda3-5.3.0-Windows-x86_64),但在尝试运行 Spyder 时出现此错误。 python
我是一名优秀的程序员,十分优秀!