Skip to content

Commit

Permalink
Fix timing of AVI files dumped on Linux
Browse files Browse the repository at this point in the history
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.
  • Loading branch information
sgadrat committed Oct 23, 2014
1 parent f65bb10 commit 3a12c50
Showing 1 changed file with 39 additions and 11 deletions.
50 changes: 39 additions & 11 deletions Source/Core/VideoCommon/AVIDump.cpp
Expand Up @@ -14,6 +14,7 @@
#include "Common/Logging/Log.h"

#include "Core/CoreTiming.h"
#include "Core/HW/SystemTimers.h"
#include "Core/HW/VideoInterface.h" //for TargetRefreshRate
#include "VideoCommon/AVIDump.h"
#include "VideoCommon/VideoConfig.h"
Expand All @@ -29,7 +30,6 @@

#include "Core/ConfigManager.h" // for EuRGB60
#include "Core/CoreTiming.h"
#include "Core/HW/SystemTimers.h"

static HWND s_emu_wnd;
static LONG s_byte_buffer;
Expand Down Expand Up @@ -335,6 +335,7 @@ static int s_height;
static int s_size;
static u64 s_last_frame;
bool b_start_dumping = false;
static u64 s_last_pts;

static void InitAVCodec()
{
Expand All @@ -352,6 +353,7 @@ bool AVIDump::Start(int w, int h)
s_height = h;

s_last_frame = CoreTiming::GetTicks();
s_last_pts = 0;

InitAVCodec();
bool success = CreateFile();
Expand Down Expand Up @@ -416,14 +418,6 @@ static void PreparePacket(AVPacket* pkt)
av_init_packet(pkt);
pkt->data = nullptr;
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)
Expand All @@ -448,11 +442,45 @@ void AVIDump::AddFrame(const u8* data, int width, int height)
// Encode and write the image.
AVPacket pkt;
PreparePacket(&pkt);
int got_packet;
int error = avcodec_encode_video2(s_stream->codec, &pkt, s_scaled_frame, &got_packet);
int got_packet = 0;
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)
{
// 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);

// Handle delayed frames.
Expand Down

0 comments on commit 3a12c50

Please sign in to comment.