Before the FFmpeg commit: github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586 FFmpeg would use deprecated variables to calculate the video fps. We don't use these deprecated variables anymore, so ensure that the duration is correct in ffmpeg versions without this fix. Reviewed By: Sergey, Richard Antalik Differential Revision: http://developer.blender.org/D11417
337 lines
10 KiB
C++
337 lines
10 KiB
C++
/*
|
|
* compatibility macros to make every ffmpeg installation appear
|
|
* like the most current installation (wrapping some functionality sometimes)
|
|
* it also includes all ffmpeg header files at once, no need to do it
|
|
* separately.
|
|
*
|
|
* Copyright (c) 2011 Peter Schlaile
|
|
*
|
|
* This program is free software; you can redistribute it and/or modify
|
|
* it under the terms of the GNU General Public License as published by
|
|
* the Free Software Foundation; either version 2 of the License, or
|
|
* (at your option) any later version.
|
|
*
|
|
* This program is distributed in the hope that it will be useful,
|
|
* but WITHOUT ANY WARRANTY; without even the implied warranty of
|
|
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
|
|
* GNU General Public License for more details.
|
|
*/
|
|
|
|
#ifndef __FFMPEG_COMPAT_H__
|
|
#define __FFMPEG_COMPAT_H__
|
|
|
|
#include <libavformat/avformat.h>
|
|
|
|
/* Check if our ffmpeg is new enough, avoids user complaints.
|
|
* Minimum supported version is currently 3.2.0 which mean the following library versions:
|
|
* libavutil > 55.30
|
|
* libavcodec > 57.60
|
|
* libavformat > 57.50
|
|
*
|
|
* We only check for one of these as they are usually updated in tandem.
|
|
*/
|
|
#if (LIBAVFORMAT_VERSION_MAJOR < 57) || \
|
|
((LIBAVFORMAT_VERSION_MAJOR == 57) && (LIBAVFORMAT_VERSION_MINOR <= 50))
|
|
# error "FFmpeg 3.2.0 or newer is needed, Upgrade your FFmpeg or disable it"
|
|
#endif
|
|
/* end sanity check */
|
|
|
|
/* visual studio 2012 does not define inline for C */
|
|
#ifdef _MSC_VER
|
|
# define FFMPEG_INLINE static __inline
|
|
#else
|
|
# define FFMPEG_INLINE static inline
|
|
#endif
|
|
|
|
#if (LIBAVFORMAT_VERSION_MAJOR < 58) || \
|
|
((LIBAVFORMAT_VERSION_MAJOR == 58) && (LIBAVFORMAT_VERSION_MINOR < 76))
|
|
# define FFMPEG_USE_DURATION_WORKAROUND 1
|
|
|
|
/* Before ffmpeg 4.4, package duration calculation used depricated variables to calculate the
|
|
* packet duration. Use the function from commit
|
|
* github.com/FFmpeg/FFmpeg/commit/1c0885334dda9ee8652e60c586fa2e3674056586
|
|
* to calculate the correct framerate for ffmpeg < 4.4.
|
|
*/
|
|
|
|
FFMPEG_INLINE
|
|
void my_guess_pkt_duration(AVFormatContext *s, AVStream *st, AVPacket *pkt)
|
|
{
|
|
if (pkt->duration < 0 && st->codecpar->codec_type != AVMEDIA_TYPE_SUBTITLE) {
|
|
av_log(s,
|
|
AV_LOG_WARNING,
|
|
"Packet with invalid duration %" PRId64 " in stream %d\n",
|
|
pkt->duration,
|
|
pkt->stream_index);
|
|
pkt->duration = 0;
|
|
}
|
|
|
|
if (pkt->duration) {
|
|
return;
|
|
}
|
|
|
|
switch (st->codecpar->codec_type) {
|
|
case AVMEDIA_TYPE_VIDEO:
|
|
if (st->avg_frame_rate.num > 0 && st->avg_frame_rate.den > 0) {
|
|
pkt->duration = av_rescale_q(1, av_inv_q(st->avg_frame_rate), st->time_base);
|
|
}
|
|
else if (st->time_base.num * 1000LL > st->time_base.den) {
|
|
pkt->duration = 1;
|
|
}
|
|
break;
|
|
case AVMEDIA_TYPE_AUDIO: {
|
|
int frame_size = av_get_audio_frame_duration2(st->codecpar, pkt->size);
|
|
if (frame_size && st->codecpar->sample_rate) {
|
|
pkt->duration = av_rescale_q(
|
|
frame_size, (AVRational){1, st->codecpar->sample_rate}, st->time_base);
|
|
}
|
|
break;
|
|
}
|
|
default:
|
|
break;
|
|
}
|
|
}
|
|
#endif
|
|
|
|
FFMPEG_INLINE
|
|
void my_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
|
|
{
|
|
int i;
|
|
|
|
for (i = 0; i < s->nb_streams; i++) {
|
|
AVStream *st = s->streams[i];
|
|
|
|
st->cur_dts = av_rescale(timestamp,
|
|
st->time_base.den * (int64_t)ref_st->time_base.num,
|
|
st->time_base.num * (int64_t)ref_st->time_base.den);
|
|
}
|
|
}
|
|
|
|
FFMPEG_INLINE
|
|
void av_update_cur_dts(AVFormatContext *s, AVStream *ref_st, int64_t timestamp)
|
|
{
|
|
my_update_cur_dts(s, ref_st, timestamp);
|
|
}
|
|
|
|
FFMPEG_INLINE
|
|
int64_t av_get_pts_from_frame(AVFormatContext *avctx, AVFrame *picture)
|
|
{
|
|
int64_t pts;
|
|
pts = picture->pts;
|
|
|
|
if (pts == AV_NOPTS_VALUE) {
|
|
pts = picture->pkt_dts;
|
|
}
|
|
if (pts == AV_NOPTS_VALUE) {
|
|
pts = 0;
|
|
}
|
|
|
|
(void)avctx;
|
|
return pts;
|
|
}
|
|
|
|
/* -------------------------------------------------------------------- */
|
|
/** \name Deinterlace code block
|
|
*
|
|
* NOTE: The code in this block are from FFmpeg 2.6.4, which is licensed by LGPL.
|
|
* \{ */
|
|
|
|
#define MAX_NEG_CROP 1024
|
|
|
|
#define times4(x) x, x, x, x
|
|
#define times256(x) times4(times4(times4(times4(times4(x)))))
|
|
|
|
static const uint8_t ff_compat_crop_tab[256 + 2 * MAX_NEG_CROP] = {
|
|
times256(0x00), 0x00, 0x01, 0x02, 0x03, 0x04, 0x05, 0x06, 0x07, 0x08, 0x09, 0x0A,
|
|
0x0B, 0x0C, 0x0D, 0x0E, 0x0F, 0x10, 0x11, 0x12, 0x13, 0x14, 0x15, 0x16,
|
|
0x17, 0x18, 0x19, 0x1A, 0x1B, 0x1C, 0x1D, 0x1E, 0x1F, 0x20, 0x21, 0x22,
|
|
0x23, 0x24, 0x25, 0x26, 0x27, 0x28, 0x29, 0x2A, 0x2B, 0x2C, 0x2D, 0x2E,
|
|
0x2F, 0x30, 0x31, 0x32, 0x33, 0x34, 0x35, 0x36, 0x37, 0x38, 0x39, 0x3A,
|
|
0x3B, 0x3C, 0x3D, 0x3E, 0x3F, 0x40, 0x41, 0x42, 0x43, 0x44, 0x45, 0x46,
|
|
0x47, 0x48, 0x49, 0x4A, 0x4B, 0x4C, 0x4D, 0x4E, 0x4F, 0x50, 0x51, 0x52,
|
|
0x53, 0x54, 0x55, 0x56, 0x57, 0x58, 0x59, 0x5A, 0x5B, 0x5C, 0x5D, 0x5E,
|
|
0x5F, 0x60, 0x61, 0x62, 0x63, 0x64, 0x65, 0x66, 0x67, 0x68, 0x69, 0x6A,
|
|
0x6B, 0x6C, 0x6D, 0x6E, 0x6F, 0x70, 0x71, 0x72, 0x73, 0x74, 0x75, 0x76,
|
|
0x77, 0x78, 0x79, 0x7A, 0x7B, 0x7C, 0x7D, 0x7E, 0x7F, 0x80, 0x81, 0x82,
|
|
0x83, 0x84, 0x85, 0x86, 0x87, 0x88, 0x89, 0x8A, 0x8B, 0x8C, 0x8D, 0x8E,
|
|
0x8F, 0x90, 0x91, 0x92, 0x93, 0x94, 0x95, 0x96, 0x97, 0x98, 0x99, 0x9A,
|
|
0x9B, 0x9C, 0x9D, 0x9E, 0x9F, 0xA0, 0xA1, 0xA2, 0xA3, 0xA4, 0xA5, 0xA6,
|
|
0xA7, 0xA8, 0xA9, 0xAA, 0xAB, 0xAC, 0xAD, 0xAE, 0xAF, 0xB0, 0xB1, 0xB2,
|
|
0xB3, 0xB4, 0xB5, 0xB6, 0xB7, 0xB8, 0xB9, 0xBA, 0xBB, 0xBC, 0xBD, 0xBE,
|
|
0xBF, 0xC0, 0xC1, 0xC2, 0xC3, 0xC4, 0xC5, 0xC6, 0xC7, 0xC8, 0xC9, 0xCA,
|
|
0xCB, 0xCC, 0xCD, 0xCE, 0xCF, 0xD0, 0xD1, 0xD2, 0xD3, 0xD4, 0xD5, 0xD6,
|
|
0xD7, 0xD8, 0xD9, 0xDA, 0xDB, 0xDC, 0xDD, 0xDE, 0xDF, 0xE0, 0xE1, 0xE2,
|
|
0xE3, 0xE4, 0xE5, 0xE6, 0xE7, 0xE8, 0xE9, 0xEA, 0xEB, 0xEC, 0xED, 0xEE,
|
|
0xEF, 0xF0, 0xF1, 0xF2, 0xF3, 0xF4, 0xF5, 0xF6, 0xF7, 0xF8, 0xF9, 0xFA,
|
|
0xFB, 0xFC, 0xFD, 0xFE, 0xFF, times256(0xFF)};
|
|
|
|
#undef times4
|
|
#undef times256
|
|
|
|
/* filter parameters: [-1 4 2 4 -1] // 8 */
|
|
FFMPEG_INLINE
|
|
void deinterlace_line(uint8_t *dst,
|
|
const uint8_t *lum_m4,
|
|
const uint8_t *lum_m3,
|
|
const uint8_t *lum_m2,
|
|
const uint8_t *lum_m1,
|
|
const uint8_t *lum,
|
|
int size)
|
|
{
|
|
const uint8_t *cm = ff_compat_crop_tab + MAX_NEG_CROP;
|
|
int sum;
|
|
|
|
for (; size > 0; size--) {
|
|
sum = -lum_m4[0];
|
|
sum += lum_m3[0] << 2;
|
|
sum += lum_m2[0] << 1;
|
|
sum += lum_m1[0] << 2;
|
|
sum += -lum[0];
|
|
dst[0] = cm[(sum + 4) >> 3];
|
|
lum_m4++;
|
|
lum_m3++;
|
|
lum_m2++;
|
|
lum_m1++;
|
|
lum++;
|
|
dst++;
|
|
}
|
|
}
|
|
|
|
FFMPEG_INLINE
|
|
void deinterlace_line_inplace(
|
|
uint8_t *lum_m4, uint8_t *lum_m3, uint8_t *lum_m2, uint8_t *lum_m1, uint8_t *lum, int size)
|
|
{
|
|
const uint8_t *cm = ff_compat_crop_tab + MAX_NEG_CROP;
|
|
int sum;
|
|
|
|
for (; size > 0; size--) {
|
|
sum = -lum_m4[0];
|
|
sum += lum_m3[0] << 2;
|
|
sum += lum_m2[0] << 1;
|
|
lum_m4[0] = lum_m2[0];
|
|
sum += lum_m1[0] << 2;
|
|
sum += -lum[0];
|
|
lum_m2[0] = cm[(sum + 4) >> 3];
|
|
lum_m4++;
|
|
lum_m3++;
|
|
lum_m2++;
|
|
lum_m1++;
|
|
lum++;
|
|
}
|
|
}
|
|
|
|
/* deinterlacing : 2 temporal taps, 3 spatial taps linear filter. The
|
|
top field is copied as is, but the bottom field is deinterlaced
|
|
against the top field. */
|
|
FFMPEG_INLINE
|
|
void deinterlace_bottom_field(
|
|
uint8_t *dst, int dst_wrap, const uint8_t *src1, int src_wrap, int width, int height)
|
|
{
|
|
const uint8_t *src_m2, *src_m1, *src_0, *src_p1, *src_p2;
|
|
int y;
|
|
|
|
src_m2 = src1;
|
|
src_m1 = src1;
|
|
src_0 = &src_m1[src_wrap];
|
|
src_p1 = &src_0[src_wrap];
|
|
src_p2 = &src_p1[src_wrap];
|
|
for (y = 0; y < (height - 2); y += 2) {
|
|
memcpy(dst, src_m1, width);
|
|
dst += dst_wrap;
|
|
deinterlace_line(dst, src_m2, src_m1, src_0, src_p1, src_p2, width);
|
|
src_m2 = src_0;
|
|
src_m1 = src_p1;
|
|
src_0 = src_p2;
|
|
src_p1 += 2 * src_wrap;
|
|
src_p2 += 2 * src_wrap;
|
|
dst += dst_wrap;
|
|
}
|
|
memcpy(dst, src_m1, width);
|
|
dst += dst_wrap;
|
|
/* do last line */
|
|
deinterlace_line(dst, src_m2, src_m1, src_0, src_0, src_0, width);
|
|
}
|
|
|
|
FFMPEG_INLINE
|
|
int deinterlace_bottom_field_inplace(uint8_t *src1, int src_wrap, int width, int height)
|
|
{
|
|
uint8_t *src_m1, *src_0, *src_p1, *src_p2;
|
|
int y;
|
|
uint8_t *buf = (uint8_t *)av_malloc(width);
|
|
if (!buf) {
|
|
return AVERROR(ENOMEM);
|
|
}
|
|
|
|
src_m1 = src1;
|
|
memcpy(buf, src_m1, width);
|
|
src_0 = &src_m1[src_wrap];
|
|
src_p1 = &src_0[src_wrap];
|
|
src_p2 = &src_p1[src_wrap];
|
|
for (y = 0; y < (height - 2); y += 2) {
|
|
deinterlace_line_inplace(buf, src_m1, src_0, src_p1, src_p2, width);
|
|
src_m1 = src_p1;
|
|
src_0 = src_p2;
|
|
src_p1 += 2 * src_wrap;
|
|
src_p2 += 2 * src_wrap;
|
|
}
|
|
/* do last line */
|
|
deinterlace_line_inplace(buf, src_m1, src_0, src_0, src_0, width);
|
|
av_free(buf);
|
|
return 0;
|
|
}
|
|
|
|
FFMPEG_INLINE
|
|
int av_image_deinterlace(
|
|
AVFrame *dst, const AVFrame *src, enum AVPixelFormat pix_fmt, int width, int height)
|
|
{
|
|
int i, ret;
|
|
|
|
if (pix_fmt != AV_PIX_FMT_YUV420P && pix_fmt != AV_PIX_FMT_YUVJ420P &&
|
|
pix_fmt != AV_PIX_FMT_YUV422P && pix_fmt != AV_PIX_FMT_YUVJ422P &&
|
|
pix_fmt != AV_PIX_FMT_YUV444P && pix_fmt != AV_PIX_FMT_YUV411P &&
|
|
pix_fmt != AV_PIX_FMT_GRAY8) {
|
|
return -1;
|
|
}
|
|
if ((width & 3) != 0 || (height & 3) != 0) {
|
|
return -1;
|
|
}
|
|
|
|
for (i = 0; i < 3; i++) {
|
|
if (i == 1) {
|
|
switch (pix_fmt) {
|
|
case AV_PIX_FMT_YUVJ420P:
|
|
case AV_PIX_FMT_YUV420P:
|
|
width >>= 1;
|
|
height >>= 1;
|
|
break;
|
|
case AV_PIX_FMT_YUV422P:
|
|
case AV_PIX_FMT_YUVJ422P:
|
|
width >>= 1;
|
|
break;
|
|
case AV_PIX_FMT_YUV411P:
|
|
width >>= 2;
|
|
break;
|
|
default:
|
|
break;
|
|
}
|
|
if (pix_fmt == AV_PIX_FMT_GRAY8) {
|
|
break;
|
|
}
|
|
}
|
|
if (src == dst) {
|
|
ret = deinterlace_bottom_field_inplace(dst->data[i], dst->linesize[i], width, height);
|
|
if (ret < 0) {
|
|
return ret;
|
|
}
|
|
}
|
|
else {
|
|
deinterlace_bottom_field(
|
|
dst->data[i], dst->linesize[i], src->data[i], src->linesize[i], width, height);
|
|
}
|
|
}
|
|
return 0;
|
|
}
|
|
|
|
/** \} Deinterlace code block */
|
|
|
|
#endif
|