Fix timing of AVI files dumped on Linux
The timing information is set on s_scaled_frame->pts, giving precise timing information to the encoder. Frames arriving too early (less than one tick after the previous frame) are droped. The setting of packet's timestamps and flags is done after the call to avcodec_encode_video2() as this function resets these fields according to its documentation.
This commit is contained in:
parent
f65bb10c93
commit
3a12c50dc1
|
@ -14,6 +14,7 @@
|
||||||
#include "Common/Logging/Log.h"
|
#include "Common/Logging/Log.h"
|
||||||
|
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
|
#include "Core/HW/SystemTimers.h"
|
||||||
#include "Core/HW/VideoInterface.h" //for TargetRefreshRate
|
#include "Core/HW/VideoInterface.h" //for TargetRefreshRate
|
||||||
#include "VideoCommon/AVIDump.h"
|
#include "VideoCommon/AVIDump.h"
|
||||||
#include "VideoCommon/VideoConfig.h"
|
#include "VideoCommon/VideoConfig.h"
|
||||||
|
@ -29,7 +30,6 @@
|
||||||
|
|
||||||
#include "Core/ConfigManager.h" // for EuRGB60
|
#include "Core/ConfigManager.h" // for EuRGB60
|
||||||
#include "Core/CoreTiming.h"
|
#include "Core/CoreTiming.h"
|
||||||
#include "Core/HW/SystemTimers.h"
|
|
||||||
|
|
||||||
static HWND s_emu_wnd;
|
static HWND s_emu_wnd;
|
||||||
static LONG s_byte_buffer;
|
static LONG s_byte_buffer;
|
||||||
|
@ -335,6 +335,7 @@ static int s_height;
|
||||||
static int s_size;
|
static int s_size;
|
||||||
static u64 s_last_frame;
|
static u64 s_last_frame;
|
||||||
bool b_start_dumping = false;
|
bool b_start_dumping = false;
|
||||||
|
static u64 s_last_pts;
|
||||||
|
|
||||||
static void InitAVCodec()
|
static void InitAVCodec()
|
||||||
{
|
{
|
||||||
|
@ -352,6 +353,7 @@ bool AVIDump::Start(int w, int h)
|
||||||
s_height = h;
|
s_height = h;
|
||||||
|
|
||||||
s_last_frame = CoreTiming::GetTicks();
|
s_last_frame = CoreTiming::GetTicks();
|
||||||
|
s_last_pts = 0;
|
||||||
|
|
||||||
InitAVCodec();
|
InitAVCodec();
|
||||||
bool success = CreateFile();
|
bool success = CreateFile();
|
||||||
|
@ -416,14 +418,6 @@ static void PreparePacket(AVPacket* pkt)
|
||||||
av_init_packet(pkt);
|
av_init_packet(pkt);
|
||||||
pkt->data = nullptr;
|
pkt->data = nullptr;
|
||||||
pkt->size = 0;
|
pkt->size = 0;
|
||||||
if (s_stream->codec->coded_frame->pts != AV_NOPTS_VALUE)
|
|
||||||
{
|
|
||||||
pkt->pts = av_rescale_q(s_stream->codec->coded_frame->pts,
|
|
||||||
s_stream->codec->time_base, s_stream->time_base);
|
|
||||||
}
|
|
||||||
if (s_stream->codec->coded_frame->key_frame)
|
|
||||||
pkt->flags |= AV_PKT_FLAG_KEY;
|
|
||||||
pkt->stream_index = s_stream->index;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void AVIDump::AddFrame(const u8* data, int width, int height)
|
void AVIDump::AddFrame(const u8* data, int width, int height)
|
||||||
|
@ -448,11 +442,45 @@ void AVIDump::AddFrame(const u8* data, int width, int height)
|
||||||
// Encode and write the image.
|
// Encode and write the image.
|
||||||
AVPacket pkt;
|
AVPacket pkt;
|
||||||
PreparePacket(&pkt);
|
PreparePacket(&pkt);
|
||||||
int got_packet;
|
int got_packet = 0;
|
||||||
int error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet);
|
int error = 0;
|
||||||
|
u64 delta;
|
||||||
|
s64 last_pts;
|
||||||
|
if (!b_start_dumping && s_last_frame <= SystemTimers::GetTicksPerSecond())
|
||||||
|
{
|
||||||
|
delta = CoreTiming::GetTicks();
|
||||||
|
last_pts = AV_NOPTS_VALUE;
|
||||||
|
b_start_dumping = true;
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
delta = CoreTiming::GetTicks() - s_last_frame;
|
||||||
|
last_pts = (s_last_pts * s_stream->codec->time_base.den) / SystemTimers::GetTicksPerSecond();
|
||||||
|
}
|
||||||
|
u64 pts_in_ticks = s_last_pts + delta;
|
||||||
|
s_scaled_frame->pts = (pts_in_ticks * s_stream->codec->time_base.den) / SystemTimers::GetTicksPerSecond();
|
||||||
|
if (s_scaled_frame->pts != last_pts)
|
||||||
|
{
|
||||||
|
s_last_frame = CoreTiming::GetTicks();
|
||||||
|
s_last_pts = pts_in_ticks;
|
||||||
|
error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet);
|
||||||
|
}
|
||||||
while (!error && got_packet)
|
while (!error && got_packet)
|
||||||
{
|
{
|
||||||
// Write the compressed frame in the media file.
|
// Write the compressed frame in the media file.
|
||||||
|
if (pkt.pts != AV_NOPTS_VALUE)
|
||||||
|
{
|
||||||
|
pkt.pts = av_rescale_q(pkt.pts,
|
||||||
|
s_stream->codec->time_base, s_stream->time_base);
|
||||||
|
}
|
||||||
|
if (pkt.dts != AV_NOPTS_VALUE)
|
||||||
|
{
|
||||||
|
pkt.dts = av_rescale_q(pkt.dts,
|
||||||
|
s_stream->codec->time_base, s_stream->time_base);
|
||||||
|
}
|
||||||
|
if (s_stream->codec->coded_frame->key_frame)
|
||||||
|
pkt.flags |= AV_PKT_FLAG_KEY;
|
||||||
|
pkt.stream_index = s_stream->index;
|
||||||
av_interleaved_write_frame(s_format_context, &pkt);
|
av_interleaved_write_frame(s_format_context, &pkt);
|
||||||
|
|
||||||
// Handle delayed frames.
|
// Handle delayed frames.
|
||||||
|
|
Loading…
Reference in New Issue