mirror of
https://gitlab.freedesktop.org/gstreamer/gstreamer.git
synced 2025-06-07 07:58:51 +00:00
winks: improve framerate fraction conversions
* For instance 7.5 fps should be represented as 15/2 instead of 7/1. * Clamp AvgTimePerFrame and dwBitRate to account for rounding errors.
This commit is contained in:
parent
d282a1d380
commit
bb17394e76
1 changed files with 81 additions and 48 deletions
|
@ -21,6 +21,7 @@
|
||||||
|
|
||||||
#include "ksvideohelpers.h"
|
#include "ksvideohelpers.h"
|
||||||
|
|
||||||
|
#include <math.h>
|
||||||
#include <uuids.h>
|
#include <uuids.h>
|
||||||
#include "kshelpers.h"
|
#include "kshelpers.h"
|
||||||
|
|
||||||
|
@ -269,10 +270,47 @@ guess_aspect (gint width, gint height, gint * par_width, gint * par_height)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* NOTE: would probably be better to use a continued fractions approach here */
|
||||||
|
static void
|
||||||
|
compress_fraction (gint64 in_num, gint64 in_den, gint64 * out_num,
|
||||||
|
gint64 * out_den)
|
||||||
|
{
|
||||||
|
gdouble on, od, orig;
|
||||||
|
guint denominators[] = { 1, 2, 3, 5, 7 }, i;
|
||||||
|
const gdouble max_loss = 0.1;
|
||||||
|
|
||||||
|
on = in_num;
|
||||||
|
od = in_den;
|
||||||
|
orig = on / od;
|
||||||
|
|
||||||
|
for (i = 0; i < G_N_ELEMENTS (denominators); i++) {
|
||||||
|
gint64 cur_n, cur_d;
|
||||||
|
gdouble cur, loss;
|
||||||
|
|
||||||
|
cur_n = floor ((on / (od / (gdouble) denominators[i])) + 0.5);
|
||||||
|
cur_d = denominators[i];
|
||||||
|
cur = (gdouble) cur_n / (gdouble) cur_d;
|
||||||
|
loss = fabs (cur - orig);
|
||||||
|
|
||||||
|
if (loss <= max_loss) {
|
||||||
|
*out_num = cur_n;
|
||||||
|
*out_den = cur_d;
|
||||||
|
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
*out_num = in_num;
|
||||||
|
*out_den = in_den;
|
||||||
|
}
|
||||||
|
|
||||||
static gboolean
|
static gboolean
|
||||||
ks_video_append_video_stream_cfg_fields (GstStructure * structure,
|
ks_video_append_video_stream_cfg_fields (GstStructure * structure,
|
||||||
const KS_VIDEO_STREAM_CONFIG_CAPS * vscc)
|
const KS_VIDEO_STREAM_CONFIG_CAPS * vscc)
|
||||||
{
|
{
|
||||||
|
GValue val = { 0, };
|
||||||
|
gint64 max_n, max_d;
|
||||||
|
|
||||||
g_return_val_if_fail (structure, FALSE);
|
g_return_val_if_fail (structure, FALSE);
|
||||||
g_return_val_if_fail (vscc, FALSE);
|
g_return_val_if_fail (vscc, FALSE);
|
||||||
|
|
||||||
|
@ -297,17 +335,23 @@ ks_video_append_video_stream_cfg_fields (GstStructure * structure,
|
||||||
}
|
}
|
||||||
|
|
||||||
/* framerate */
|
/* framerate */
|
||||||
|
compress_fraction (NANOSECONDS, vscc->MaxFrameInterval, &max_n, &max_d);
|
||||||
|
|
||||||
if (vscc->MinFrameInterval == vscc->MaxFrameInterval) {
|
if (vscc->MinFrameInterval == vscc->MaxFrameInterval) {
|
||||||
gst_structure_set (structure,
|
g_value_init (&val, GST_TYPE_FRACTION);
|
||||||
"framerate", GST_TYPE_FRACTION,
|
gst_value_set_fraction (&val, max_n, max_d);
|
||||||
(gint) (10000000 / vscc->MaxFrameInterval), 1, NULL);
|
|
||||||
} else {
|
} else {
|
||||||
gst_structure_set (structure,
|
gint64 min_n, min_d;
|
||||||
"framerate", GST_TYPE_FRACTION_RANGE,
|
|
||||||
(gint) (10000000 / vscc->MaxFrameInterval), 1,
|
compress_fraction (NANOSECONDS, vscc->MinFrameInterval, &min_n, &min_d);
|
||||||
(gint) (10000000 / vscc->MinFrameInterval), 1, NULL);
|
|
||||||
|
g_value_init (&val, GST_TYPE_FRACTION_RANGE);
|
||||||
|
gst_value_set_fraction_range_full (&val, max_n, max_d, min_n, min_d);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
gst_structure_set_value (structure, "framerate", &val);
|
||||||
|
g_value_unset (&val);
|
||||||
|
|
||||||
{
|
{
|
||||||
gint par_width, par_height;
|
gint par_width, par_height;
|
||||||
|
|
||||||
|
@ -609,58 +653,47 @@ gboolean
|
||||||
ks_video_fixate_media_type (const KSDATARANGE * range,
|
ks_video_fixate_media_type (const KSDATARANGE * range,
|
||||||
guint8 * format, gint width, gint height, gint fps_n, gint fps_d)
|
guint8 * format, gint width, gint height, gint fps_n, gint fps_d)
|
||||||
{
|
{
|
||||||
DWORD dwRate = (width * height * fps_n) / fps_d;
|
KS_DATARANGE_VIDEO *vr;
|
||||||
|
KS_VIDEOINFOHEADER *vih;
|
||||||
|
KS_BITMAPINFOHEADER *bih;
|
||||||
|
DWORD dwRate;
|
||||||
|
|
||||||
g_return_val_if_fail (format != NULL, FALSE);
|
g_return_val_if_fail (format != NULL, FALSE);
|
||||||
|
|
||||||
if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
|
if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo)) {
|
||||||
KS_VIDEOINFOHEADER *vih = (KS_VIDEOINFOHEADER *) format;
|
bih = &((KS_VIDEOINFOHEADER *) format)->bmiHeader;
|
||||||
|
|
||||||
/* FIXME: Need to figure out how to properly handle ranges */
|
|
||||||
if (vih->bmiHeader.biWidth != width || vih->bmiHeader.biHeight != height)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
vih->AvgTimePerFrame = gst_util_uint64_scale_int (10000000, fps_d, fps_n);
|
|
||||||
vih->dwBitRate = dwRate * vih->bmiHeader.biBitCount;
|
|
||||||
} else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
|
} else if (IsEqualGUID (&range->Specifier, &FORMAT_VideoInfo2)) {
|
||||||
KS_VIDEOINFOHEADER2 *vih = (KS_VIDEOINFOHEADER2 *) format;
|
bih = &((KS_VIDEOINFOHEADER2 *) format)->bmiHeader;
|
||||||
|
|
||||||
/* FIXME: see above */
|
|
||||||
if (vih->bmiHeader.biWidth != width || vih->bmiHeader.biHeight != height)
|
|
||||||
return FALSE;
|
|
||||||
|
|
||||||
vih->AvgTimePerFrame = gst_util_uint64_scale_int (10000000, fps_d, fps_n);
|
|
||||||
vih->dwBitRate = dwRate * vih->bmiHeader.biBitCount;
|
|
||||||
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
|
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEGVideo)) {
|
||||||
KS_MPEG1VIDEOINFO *vih = (KS_MPEG1VIDEOINFO *) format;
|
bih = &((KS_MPEG1VIDEOINFO *) format)->hdr.bmiHeader;
|
||||||
|
|
||||||
/* FIXME: see above */
|
|
||||||
if (vih->hdr.bmiHeader.biWidth != width ||
|
|
||||||
vih->hdr.bmiHeader.biHeight != height)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
vih->hdr.AvgTimePerFrame =
|
|
||||||
gst_util_uint64_scale_int (10000000, fps_d, fps_n);
|
|
||||||
vih->hdr.dwBitRate = dwRate * vih->hdr.bmiHeader.biBitCount;
|
|
||||||
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
|
} else if (IsEqualGUID (&range->Specifier, &FORMAT_MPEG2Video)) {
|
||||||
KS_MPEGVIDEOINFO2 *vih = (KS_MPEGVIDEOINFO2 *) format;
|
bih = &((KS_MPEGVIDEOINFO2 *) format)->hdr.bmiHeader;
|
||||||
|
|
||||||
/* FIXME: see above */
|
|
||||||
if (vih->hdr.bmiHeader.biWidth != width ||
|
|
||||||
vih->hdr.bmiHeader.biHeight != height)
|
|
||||||
{
|
|
||||||
return FALSE;
|
|
||||||
}
|
|
||||||
|
|
||||||
vih->hdr.AvgTimePerFrame =
|
|
||||||
gst_util_uint64_scale_int (10000000, fps_d, fps_n);
|
|
||||||
vih->hdr.dwBitRate = dwRate * vih->hdr.bmiHeader.biBitCount;
|
|
||||||
} else {
|
} else {
|
||||||
return FALSE;
|
return FALSE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/* These formats' structures share the most basic stuff */
|
||||||
|
vr = (KS_DATARANGE_VIDEO *) range;
|
||||||
|
vih = (KS_VIDEOINFOHEADER *) format;
|
||||||
|
|
||||||
|
/* FIXME: Need to figure out how to properly handle ranges */
|
||||||
|
if (bih->biWidth != width || bih->biHeight != height)
|
||||||
|
return FALSE;
|
||||||
|
|
||||||
|
/* Framerate, clamped because of fraction conversion rounding errors */
|
||||||
|
vih->AvgTimePerFrame =
|
||||||
|
gst_util_uint64_scale_int_round (NANOSECONDS, fps_d, fps_n);
|
||||||
|
vih->AvgTimePerFrame =
|
||||||
|
MAX (vih->AvgTimePerFrame, vr->ConfigCaps.MinFrameInterval);
|
||||||
|
vih->AvgTimePerFrame =
|
||||||
|
MIN (vih->AvgTimePerFrame, vr->ConfigCaps.MaxFrameInterval);
|
||||||
|
|
||||||
|
/* Bitrate, clamped for the same reason as framerate */
|
||||||
|
dwRate = (width * height * fps_n) / fps_d;
|
||||||
|
vih->dwBitRate = dwRate * bih->biBitCount;
|
||||||
|
vih->dwBitRate = MAX (vih->dwBitRate, vr->ConfigCaps.MinBitsPerSecond);
|
||||||
|
vih->dwBitRate = MIN (vih->dwBitRate, vr->ConfigCaps.MaxBitsPerSecond);
|
||||||
|
|
||||||
return TRUE;
|
return TRUE;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
Loading…
Reference in a new issue