You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
1554 lines
51 KiB
1554 lines
51 KiB
4 years ago
|
/*
|
||
|
* encode_lavc.c -- encode A/V frames using libavcodec.
|
||
|
* (C) 2007-2010 Francesco Romani <fromani at gmail dot com>
|
||
|
*
|
||
|
* This file is part of transcode, a video stream processing tool.
|
||
|
*
|
||
|
* transcode 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.
|
||
|
*
|
||
|
* transcode 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.
|
||
|
*
|
||
|
* You should have received a copy of the GNU General Public License
|
||
|
* along with this program. If not, see <http://www.gnu.org/licenses/>.
|
||
|
*/
|
||
|
|
||
|
|
||
|
#include "transcode.h"
|
||
|
#include "framebuffer.h"
|
||
|
#include "filter.h"
|
||
|
|
||
|
#include "aclib/imgconvert.h"
|
||
|
|
||
|
#include "libtc/optstr.h"
|
||
|
#include "libtc/cfgfile.h"
|
||
|
#include "libtc/ratiocodes.h"
|
||
|
#include "libtc/tcframes.h"
|
||
|
|
||
|
#include "libtc/tcmodule-plugin.h"
|
||
|
|
||
|
#include "libtc/tcavcodec.h"
|
||
|
|
||
|
#include <math.h>
|
||
|
|
||
|
#define MOD_NAME "encode_lavc.so"
|
||
|
#define MOD_VERSION "v0.0.7 (2007-10-27)"
|
||
|
#define MOD_CAP "libavcodec based encoder (" LIBAVCODEC_IDENT ")"
|
||
|
|
||
|
#define MOD_FEATURES \
|
||
|
TC_MODULE_FEATURE_ENCODE|TC_MODULE_FEATURE_VIDEO
|
||
|
|
||
|
#define MOD_FLAGS \
|
||
|
TC_MODULE_FLAG_RECONFIGURABLE
|
||
|
|
||
|
#define LAVC_CONFIG_FILE "lavc.cfg"
|
||
|
#define PSNR_LOG_FILE "psnr.log"
|
||
|
|
||
|
static const char tc_lavc_help[] = ""
|
||
|
"Overview:\n"
|
||
|
" this module uses libavcodec to encode given raw frames in\n"
|
||
|
" an huge variety of compressed formats, both audio and video.\n"
|
||
|
"Options:\n"
|
||
|
" help produce module overview and options explanations\n"
|
||
|
" list log out a list of supported A/V codecs\n";
|
||
|
|
||
|
|
||
|
|
||
|
typedef struct tclavcconfigdata_ TCLavcConfigData;
|
||
|
struct tclavcconfigdata_ {
|
||
|
int thread_count;
|
||
|
|
||
|
/*
|
||
|
* following options can't be sect directly on AVCodeContext,
|
||
|
* we need some buffering and translation.
|
||
|
*/
|
||
|
int vrate_tolerance;
|
||
|
int rc_min_rate;
|
||
|
int rc_max_rate;
|
||
|
int rc_buffer_size;
|
||
|
int lmin;
|
||
|
int lmax;
|
||
|
int me_method;
|
||
|
|
||
|
/* same as above for flags */
|
||
|
struct {
|
||
|
uint32_t mv0;
|
||
|
uint32_t cbp;
|
||
|
uint32_t qpel;
|
||
|
uint32_t alt;
|
||
|
uint32_t vdpart;
|
||
|
uint32_t naq;
|
||
|
uint32_t ilme;
|
||
|
uint32_t ildct;
|
||
|
uint32_t aic;
|
||
|
uint32_t aiv;
|
||
|
uint32_t umv;
|
||
|
uint32_t psnr;
|
||
|
uint32_t trell;
|
||
|
uint32_t gray;
|
||
|
uint32_t v4mv;
|
||
|
uint32_t closedgop;
|
||
|
} flags;
|
||
|
|
||
|
/*
|
||
|
* special flags: flags that triggers more than a setting
|
||
|
* FIXME: not yet supported
|
||
|
*/
|
||
|
int turbo_setup;
|
||
|
};
|
||
|
|
||
|
typedef struct tclavcprivatedata_ TCLavcPrivateData;
|
||
|
|
||
|
/* this is to reduce if()s in out encode_video() */
|
||
|
typedef void (*PreEncodeVideoFn)(struct tclavcprivatedata_ *pd,
|
||
|
vframe_list_t *vframe);
|
||
|
|
||
|
struct tclavcprivatedata_ {
|
||
|
int vcodec_id;
|
||
|
TCCodecID tc_pix_fmt;
|
||
|
|
||
|
AVFrame ff_venc_frame;
|
||
|
AVCodecContext ff_vcontext;
|
||
|
|
||
|
AVCodec *ff_vcodec;
|
||
|
|
||
|
TCLavcConfigData confdata;
|
||
|
|
||
|
struct {
|
||
|
int active;
|
||
|
int top_first;
|
||
|
} interlacing;
|
||
|
|
||
|
uint16_t inter_matrix[TC_MATRIX_SIZE];
|
||
|
uint16_t intra_matrix[TC_MATRIX_SIZE];
|
||
|
|
||
|
FILE *stats_file;
|
||
|
FILE *psnr_file;
|
||
|
|
||
|
vframe_list_t *vframe_buf;
|
||
|
/* for colorspace conversions in prepare functions */
|
||
|
PreEncodeVideoFn pre_encode_video;
|
||
|
|
||
|
int flush_flag;
|
||
|
};
|
||
|
|
||
|
/*************************************************************************/
|
||
|
|
||
|
static const TCCodecID tc_lavc_codecs_in[] = {
|
||
|
TC_CODEC_YUV420P, TC_CODEC_YUV422P, TC_CODEC_RGB,
|
||
|
TC_CODEC_ERROR
|
||
|
};
|
||
|
|
||
|
|
||
|
|
||
|
#define FF_VCODEC_ID(pd) (tc_lavc_internal_codecs[(pd)->vcodec_id])
|
||
|
#define TC_VCODEC_ID(pd) (tc_lavc_codecs_out[(pd)->vcodec_id])
|
||
|
|
||
|
/* WARNING: the two arrays below MUST BE KEPT SYNCHRONIZED! */
|
||
|
|
||
|
static const TCCodecID tc_lavc_codecs_out[] = {
|
||
|
TC_CODEC_MPEG1VIDEO, TC_CODEC_MPEG2VIDEO, TC_CODEC_MPEG4VIDEO,
|
||
|
TC_CODEC_H263I, TC_CODEC_H263P,
|
||
|
TC_CODEC_H264,
|
||
|
TC_CODEC_WMV1, TC_CODEC_WMV2,
|
||
|
TC_CODEC_RV10,
|
||
|
TC_CODEC_HUFFYUV, TC_CODEC_FFV1,
|
||
|
TC_CODEC_DV,
|
||
|
TC_CODEC_MJPEG, TC_CODEC_LJPEG,
|
||
|
TC_CODEC_MP42, TC_CODEC_MP43,
|
||
|
TC_CODEC_ERROR
|
||
|
};
|
||
|
|
||
|
static const enum CodecID tc_lavc_internal_codecs[] = {
|
||
|
CODEC_ID_MPEG1VIDEO, CODEC_ID_MPEG2VIDEO, CODEC_ID_MPEG4,
|
||
|
CODEC_ID_H263I, CODEC_ID_H263P,
|
||
|
CODEC_ID_H264,
|
||
|
CODEC_ID_WMV1, CODEC_ID_WMV2,
|
||
|
CODEC_ID_RV10,
|
||
|
CODEC_ID_HUFFYUV, CODEC_ID_FFV1,
|
||
|
CODEC_ID_DVVIDEO,
|
||
|
CODEC_ID_MJPEG, CODEC_ID_LJPEG,
|
||
|
CODEC_ID_MSMPEG4V2, CODEC_ID_MSMPEG4V3,
|
||
|
CODEC_ID_NONE
|
||
|
};
|
||
|
|
||
|
static const TCFormatID tc_lavc_formats[] = { TC_FORMAT_ERROR };
|
||
|
|
||
|
|
||
|
/*************************************************************************/
|
||
|
|
||
|
/*
|
||
|
* following helper private functions adapt stuff and do proper
|
||
|
* colorspace conversion, if needed, preparing data for
|
||
|
* later real encoding
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* pre_encode_video_yuv420p:
|
||
|
* pre_encode_video_yuv420p_huffyuv:
|
||
|
* pre_encode_video_yuv422p:
|
||
|
* pre_encode_video_yuv422p_huffyuv:
|
||
|
* pre_encode_video_rgb24:
|
||
|
* prepare internal structures for actual encoding, doing
|
||
|
* colorspace conversion and/or any needed adaptation.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module dataure
|
||
|
* vframe: pointer to *SOURCE* video data frame
|
||
|
* Return Value:
|
||
|
* none
|
||
|
* Preconditions:
|
||
|
* module initialized and configured;
|
||
|
* auxiliariy buffer space already allocated if needed
|
||
|
* (pix fmt != yuv420p)
|
||
|
* Postconditions:
|
||
|
* video data ready to be encoded through lavc.
|
||
|
*/
|
||
|
|
||
|
static void pre_encode_video_yuv420p(TCLavcPrivateData *pd,
|
||
|
vframe_list_t *vframe)
|
||
|
{
|
||
|
avpicture_fill((AVPicture *)&pd->ff_venc_frame, vframe->video_buf,
|
||
|
PIX_FMT_YUV420P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void pre_encode_video_yuv420p_huffyuv(TCLavcPrivateData *pd,
|
||
|
vframe_list_t *vframe)
|
||
|
{
|
||
|
uint8_t *src[3] = { NULL, NULL, NULL };
|
||
|
|
||
|
YUV_INIT_PLANES(src, vframe->video_buf,
|
||
|
IMG_YUV_DEFAULT,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
avpicture_fill((AVPicture *)&pd->ff_venc_frame, pd->vframe_buf->video_buf,
|
||
|
PIX_FMT_YUV422P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
ac_imgconvert(src, IMG_YUV_DEFAULT,
|
||
|
pd->ff_venc_frame.data, IMG_YUV422P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
}
|
||
|
|
||
|
static void pre_encode_video_yuv422p(TCLavcPrivateData *pd,
|
||
|
vframe_list_t *vframe)
|
||
|
{
|
||
|
uint8_t *src[3] = { NULL, NULL, NULL };
|
||
|
|
||
|
YUV_INIT_PLANES(src, vframe->video_buf,
|
||
|
IMG_YUV422P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
avpicture_fill((AVPicture *)&pd->ff_venc_frame, pd->vframe_buf->video_buf,
|
||
|
PIX_FMT_YUV420P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
ac_imgconvert(src, IMG_YUV422P,
|
||
|
pd->ff_venc_frame.data, IMG_YUV420P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
}
|
||
|
|
||
|
|
||
|
static void pre_encode_video_yuv422p_huffyuv(TCLavcPrivateData *pd,
|
||
|
vframe_list_t *vframe)
|
||
|
{
|
||
|
avpicture_fill((AVPicture *)&pd->ff_venc_frame, vframe->video_buf,
|
||
|
PIX_FMT_YUV422P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
|
||
|
}
|
||
|
|
||
|
|
||
|
static void pre_encode_video_rgb24(TCLavcPrivateData *pd,
|
||
|
vframe_list_t *vframe)
|
||
|
{
|
||
|
avpicture_fill((AVPicture *)&pd->ff_venc_frame, pd->vframe_buf->video_buf,
|
||
|
PIX_FMT_YUV420P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
ac_imgconvert(&vframe->video_buf, IMG_RGB_DEFAULT,
|
||
|
pd->ff_venc_frame.data, IMG_YUV420P,
|
||
|
pd->ff_vcontext.width, pd->ff_vcontext.height);
|
||
|
}
|
||
|
|
||
|
|
||
|
|
||
|
/*************************************************************************/
|
||
|
|
||
|
/* more helpers */
|
||
|
|
||
|
/*
|
||
|
* tc_codec_is_supported:
|
||
|
* scan the module supported output codec looking for given one.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* codec: codec id to check against supported codec list.
|
||
|
* Return Value:
|
||
|
* >= 0: index of codec, if supported, in output list
|
||
|
* TC_NULL_MATCH: given codec isn't supported.
|
||
|
*/
|
||
|
static int tc_codec_is_supported(TCCodecID codec)
|
||
|
{
|
||
|
int i = 0, ret = TC_NULL_MATCH;
|
||
|
|
||
|
for (i = 0; tc_lavc_codecs_out[i] != TC_CODEC_ERROR; i++) {
|
||
|
if (codec == tc_lavc_codecs_out[i]) {
|
||
|
ret = i;
|
||
|
break;
|
||
|
}
|
||
|
}
|
||
|
return ret;
|
||
|
}
|
||
|
|
||
|
#if !defined(INFINITY) && defined(HUGE_VAL)
|
||
|
#define INFINITY HUGE_VAL
|
||
|
#endif
|
||
|
|
||
|
/*
|
||
|
* psnr:
|
||
|
* compute the psnr value of given data.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* d: value to be computed
|
||
|
* Return value:
|
||
|
* psnr of `d'
|
||
|
*/
|
||
|
static double psnr(double d) {
|
||
|
if (d == 0) {
|
||
|
return INFINITY;
|
||
|
}
|
||
|
return -10.0 * log(d) / log(10);
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_list_codecs:
|
||
|
* (NOT Thread safe. But do anybody cares since
|
||
|
* transcode encoder(.c) is single-threaded today
|
||
|
* and in any foreseable future?)
|
||
|
* return a buffer listing all supported codecs with
|
||
|
* respective name and short description.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* None
|
||
|
* Return Value:
|
||
|
* Read-only pointer to a char buffer holding the
|
||
|
* description data. Buffer is guaranted valid
|
||
|
* at least until next call of this function.
|
||
|
* You NEVER need to tc_free() the pointer.
|
||
|
*/
|
||
|
static const char* tc_lavc_list_codecs(void)
|
||
|
{
|
||
|
/* XXX: I feel a bad taste */
|
||
|
static char buf[TC_BUF_MAX];
|
||
|
static int ready = TC_FALSE;
|
||
|
|
||
|
if (!ready) {
|
||
|
size_t used = 0;
|
||
|
int i = 0;
|
||
|
|
||
|
for (i = 0; tc_lavc_codecs_out[i] != TC_CODEC_ERROR; i++) {
|
||
|
char sbuf[TC_BUF_MIN];
|
||
|
size_t slen = 0;
|
||
|
|
||
|
tc_snprintf(sbuf, sizeof(sbuf), "%15s: %s\n",
|
||
|
tc_codec_to_string(tc_lavc_codecs_out[i]),
|
||
|
tc_codec_to_comment(tc_lavc_codecs_out[i]));
|
||
|
slen = strlen(sbuf);
|
||
|
if (used + slen <= sizeof(buf)) {
|
||
|
strlcpy(buf + used, sbuf, sizeof(buf) - used);
|
||
|
used += slen;
|
||
|
/* chomp final '\0' except for first round */
|
||
|
} else {
|
||
|
tc_log_error(MOD_NAME, "too much codecs! this should happen. "
|
||
|
"Please file a bug report.");
|
||
|
strlcpy(buf, "internal error", sizeof(buf));
|
||
|
}
|
||
|
}
|
||
|
ready = TC_TRUE;
|
||
|
}
|
||
|
return buf;
|
||
|
}
|
||
|
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_read_matrices:
|
||
|
* read and fill internal (as in internal module data)
|
||
|
* custom quantization matrices from data stored on
|
||
|
* disk files, using given paths, then passes them
|
||
|
* to libavcodec for usage in encoders if loading was
|
||
|
* succesfull.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to module private data to use.
|
||
|
* intra_matrix_file: path of file containing intra matrix data.
|
||
|
* inter_matrix_file: path of file containing inter matrix data.
|
||
|
* Return Value:
|
||
|
* None
|
||
|
* Side Effects:
|
||
|
* 0-2 files on disk are opend, read, closed
|
||
|
*/
|
||
|
static void tc_lavc_read_matrices(TCLavcPrivateData *pd,
|
||
|
const char *intra_matrix_file,
|
||
|
const char *inter_matrix_file)
|
||
|
{
|
||
|
if (intra_matrix_file != NULL && strlen(intra_matrix_file) > 0) {
|
||
|
/* looks like we've got something... */
|
||
|
int ret = tc_read_matrix(intra_matrix_file, NULL, pd->inter_matrix);
|
||
|
if (ret == 0) {
|
||
|
/* ok, let's give this to lavc */
|
||
|
pd->ff_vcontext.intra_matrix = pd->inter_matrix;
|
||
|
} else {
|
||
|
tc_log_warn(MOD_NAME, "error while reading intra matrix from"
|
||
|
" %s", intra_matrix_file);
|
||
|
pd->ff_vcontext.intra_matrix = NULL; /* paranoia */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (inter_matrix_file != NULL && strlen(inter_matrix_file) > 0) {
|
||
|
/* looks like we've got something... */
|
||
|
int ret = tc_read_matrix(inter_matrix_file, NULL, pd->inter_matrix);
|
||
|
if (ret == 0) {
|
||
|
/* ok, let's give this to lavc */
|
||
|
pd->ff_vcontext.inter_matrix = pd->inter_matrix;
|
||
|
} else {
|
||
|
tc_log_warn(MOD_NAME, "error while reading inter matrix from"
|
||
|
" %s", inter_matrix_file);
|
||
|
pd->ff_vcontext.inter_matrix = NULL; /* paranoia */
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_load_filters:
|
||
|
* request to transcode core filters needed by given parameters.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to module private data.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
*/
|
||
|
static void tc_lavc_load_filters(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
if (TC_VCODEC_ID(pd) == TC_CODEC_MJPEG
|
||
|
|| TC_VCODEC_ID(pd) == TC_CODEC_LJPEG) {
|
||
|
int handle;
|
||
|
|
||
|
tc_log_info(MOD_NAME, "output is mjpeg or ljpeg, extending range from "
|
||
|
"YUV420P to YUVJ420P (full range)");
|
||
|
|
||
|
handle = tc_filter_add("levels", "input=16-240");
|
||
|
if (!handle) {
|
||
|
tc_log_warn(MOD_NAME, "cannot load levels filter");
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
/* PSNR-log stuff */
|
||
|
|
||
|
#define PSNR_REQUESTED(PD) ((PD)->confdata.flags.psnr)
|
||
|
|
||
|
/*
|
||
|
* psnr_open:
|
||
|
* open psnr log file and prepare internal data to write out
|
||
|
* PSNR stats
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull (log file open and avalaible and so on)
|
||
|
* TC_ERROR: otherwise
|
||
|
*/
|
||
|
static int psnr_open(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
pd->psnr_file = NULL;
|
||
|
|
||
|
pd->psnr_file = fopen(PSNR_LOG_FILE, "w");
|
||
|
if (pd->psnr_file != NULL) {
|
||
|
/* add a little reminder */
|
||
|
fprintf(pd->psnr_file, "# Num Qual Size Y U V Tot Type");
|
||
|
} else {
|
||
|
tc_log_warn(MOD_NAME, "can't open psnr log file '%s'",
|
||
|
PSNR_LOG_FILE);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
#define PFRAME(PD) ((PD)->ff_vcontext.coded_frame)
|
||
|
|
||
|
/*
|
||
|
* psnr_write:
|
||
|
* fetch and write to log file, if avalaible, PSNR statistics
|
||
|
* for last encoded frames. Format is human-readable.
|
||
|
* If psnr log file isn't avalaible, silently doesn nothing.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* size: size (bytes) of last encoded frame.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
*/
|
||
|
static void psnr_write(TCLavcPrivateData *pd, int size)
|
||
|
{
|
||
|
if (pd->psnr_file != NULL) {
|
||
|
const char pict_type[5] = { '?', 'I', 'P', 'B', 'S' };
|
||
|
double f = pd->ff_vcontext.width * pd->ff_vcontext.height * 255.0 * 255.0;
|
||
|
double err[3] = {
|
||
|
PFRAME(pd)->error[0],
|
||
|
PFRAME(pd)->error[1],
|
||
|
PFRAME(pd)->error[2]
|
||
|
};
|
||
|
|
||
|
fprintf(pd->psnr_file, "%6d, %2d, %6d, %2.2f,"
|
||
|
" %2.2f, %2.2f, %2.2f %c\n",
|
||
|
PFRAME(pd)->coded_picture_number, PFRAME(pd)->quality, size,
|
||
|
psnr(err[0] / f),
|
||
|
psnr(err[1] * 4 / f), /* FIXME */
|
||
|
psnr(err[2] * 4 / f), /* FIXME */
|
||
|
psnr((err[0] + err[1] + err[2]) / (f * 1.5)),
|
||
|
pict_type[PFRAME(pd)->pict_type]);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#undef PFRAME
|
||
|
|
||
|
/*
|
||
|
* psnr_close:
|
||
|
* close psnr log file, free acquired resource.
|
||
|
* It's safe to perform this call even if psnr_open()
|
||
|
* was NOT called previously.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull (log file closed correctly)
|
||
|
* TC_ERROR: otherwise
|
||
|
*/
|
||
|
static int psnr_close(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
if (pd->psnr_file != NULL) {
|
||
|
if (fclose(pd->psnr_file) != 0) {
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
}
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* psnr_print:
|
||
|
* tc_log out summary of *overall* PSNR stats.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
*/
|
||
|
static void psnr_print(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
double f = pd->ff_vcontext.width * pd->ff_vcontext.height * 255.0 * 255.0;
|
||
|
|
||
|
f *= pd->ff_vcontext.coded_frame->coded_picture_number;
|
||
|
|
||
|
#define ERROR pd->ff_vcontext.error
|
||
|
tc_log_info(MOD_NAME, "PSNR: Y:%2.2f, Cb:%2.2f, Cr:%2.2f, All:%2.2f",
|
||
|
psnr(ERROR[0] / f),
|
||
|
/* FIXME: this is correct if pix_fmt != YUV420P */
|
||
|
psnr(ERROR[1] * 4 / f),
|
||
|
psnr(ERROR[2] * 4 / f),
|
||
|
psnr((ERROR[0] + ERROR[1] + ERROR[2]) / (f * 1.5)));
|
||
|
#undef ERROR
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************************************************************************/
|
||
|
/*
|
||
|
* configure() helpers, libavcodec allow very detailed
|
||
|
* configuration step
|
||
|
*/
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_set_pix_fmt:
|
||
|
* choose the right pixel format and setup all internal module
|
||
|
* fields depending on this value.
|
||
|
* Please note that this function SHALL NOT allocate resources
|
||
|
* (i.e.: buffers) that's job of other specific functions.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* vob: pointer to vob_t structure.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull;
|
||
|
* TC_ERROR: wrong/erroneous/unsupported pixel format.
|
||
|
*
|
||
|
* FIXME: move to TC_CODEC_* colorspaces
|
||
|
*/
|
||
|
static int tc_lavc_set_pix_fmt(TCLavcPrivateData *pd, const vob_t *vob)
|
||
|
{
|
||
|
switch (vob->im_v_codec) {
|
||
|
case CODEC_YUV:
|
||
|
if (TC_VCODEC_ID(pd) == TC_CODEC_HUFFYUV) {
|
||
|
pd->tc_pix_fmt = TC_CODEC_YUV422P;
|
||
|
pd->ff_vcontext.pix_fmt = PIX_FMT_YUV422P;
|
||
|
pd->pre_encode_video = pre_encode_video_yuv420p_huffyuv;
|
||
|
} else {
|
||
|
pd->tc_pix_fmt = TC_CODEC_YUV420P;
|
||
|
pd->ff_vcontext.pix_fmt = (TC_VCODEC_ID(pd) == TC_CODEC_MJPEG)
|
||
|
? PIX_FMT_YUVJ420P
|
||
|
: PIX_FMT_YUV420P;
|
||
|
pd->pre_encode_video = pre_encode_video_yuv420p;
|
||
|
}
|
||
|
break;
|
||
|
case CODEC_YUV422:
|
||
|
pd->tc_pix_fmt = TC_CODEC_YUV422P;
|
||
|
pd->ff_vcontext.pix_fmt = (TC_VCODEC_ID(pd) == TC_CODEC_MJPEG)
|
||
|
? PIX_FMT_YUVJ422P
|
||
|
: PIX_FMT_YUV422P;
|
||
|
if (TC_VCODEC_ID(pd) == TC_CODEC_HUFFYUV) {
|
||
|
pd->pre_encode_video = pre_encode_video_yuv422p_huffyuv;
|
||
|
} else {
|
||
|
pd->pre_encode_video = pre_encode_video_yuv422p;
|
||
|
}
|
||
|
break;
|
||
|
case CODEC_RGB:
|
||
|
pd->tc_pix_fmt = TC_CODEC_RGB;
|
||
|
pd->ff_vcontext.pix_fmt = (TC_VCODEC_ID(pd) == TC_CODEC_HUFFYUV)
|
||
|
? PIX_FMT_YUV422P
|
||
|
: (TC_VCODEC_ID(pd) == TC_CODEC_MJPEG)
|
||
|
? PIX_FMT_YUVJ420P
|
||
|
: PIX_FMT_YUV420P;
|
||
|
pd->pre_encode_video = pre_encode_video_rgb24;
|
||
|
break;
|
||
|
default:
|
||
|
tc_log_warn(MOD_NAME, "Unknown pixel format %i", vob->im_v_codec);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
|
||
|
tc_log_info(MOD_NAME, "internal pixel format: %s",
|
||
|
tc_codec_to_string(pd->tc_pix_fmt));
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define CAN_DO_MULTIPASS(FLAG) do { \
|
||
|
if (!(FLAG)) { \
|
||
|
tc_log_error(MOD_NAME, "This codec does not support multipass " \
|
||
|
"encoding."); \
|
||
|
return TC_ERROR; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_init_multipass:
|
||
|
* setup internal (avcodec) parameters for multipass translating
|
||
|
* values from vob_t structure, and handle multipass log file data,
|
||
|
* reading it or creating it if needed.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* vob: pointer to vob_t structure.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull
|
||
|
* TC_ERROR: error (mostly I/O related; reason will tc_log*()'d out)
|
||
|
* Side effects:
|
||
|
* A file on disk will be open'd, and possibly read.
|
||
|
* Seeks are possible as well.
|
||
|
*/
|
||
|
static int tc_lavc_init_multipass(TCLavcPrivateData *pd, const vob_t *vob)
|
||
|
{
|
||
|
int multipass_flag = tc_codec_is_multipass(TC_VCODEC_ID(pd));
|
||
|
pd->stats_file = NULL;
|
||
|
size_t fsize = 0;
|
||
|
|
||
|
switch (vob->divxmultipass) {
|
||
|
case 1:
|
||
|
CAN_DO_MULTIPASS(multipass_flag);
|
||
|
pd->ff_vcontext.flags |= CODEC_FLAG_PASS1;
|
||
|
pd->stats_file = fopen(vob->divxlogfile, "w");
|
||
|
if (pd->stats_file == NULL) {
|
||
|
tc_log_error(MOD_NAME, "could not create 2pass log file"
|
||
|
" \"%s\".", vob->divxlogfile);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
break;
|
||
|
case 2:
|
||
|
CAN_DO_MULTIPASS(multipass_flag);
|
||
|
pd->ff_vcontext.flags |= CODEC_FLAG_PASS2;
|
||
|
pd->stats_file = fopen(vob->divxlogfile, "r");
|
||
|
if (pd->stats_file == NULL){
|
||
|
tc_log_error(MOD_NAME, "could not open 2pass log file \"%s\""
|
||
|
" for reading.", vob->divxlogfile);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
/* FIXME: we're optimistic here, don't we? */
|
||
|
fseek(pd->stats_file, 0, SEEK_END);
|
||
|
fsize = ftell(pd->stats_file);
|
||
|
fseek(pd->stats_file, 0, SEEK_SET);
|
||
|
|
||
|
pd->ff_vcontext.stats_in = tc_malloc(fsize + 1);
|
||
|
if (pd->ff_vcontext.stats_in == NULL) {
|
||
|
tc_log_error(MOD_NAME, "can't get memory for multipass log");
|
||
|
fclose(pd->stats_file);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
|
||
|
if (fread(pd->ff_vcontext.stats_in, fsize, 1, pd->stats_file) < 1) {
|
||
|
tc_log_error(MOD_NAME, "Could not read the complete 2pass log"
|
||
|
" file \"%s\".", vob->divxlogfile);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
pd->ff_vcontext.stats_in[fsize] = 0; /* paranoia */
|
||
|
fclose(pd->stats_file);
|
||
|
break;
|
||
|
case 3:
|
||
|
/* fixed qscale :p */
|
||
|
pd->ff_vcontext.flags |= CODEC_FLAG_QSCALE;
|
||
|
pd->ff_venc_frame.quality = vob->divxbitrate;
|
||
|
break;
|
||
|
}
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
#undef CAN_DO_MULTIPASS
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_fini_multipass:
|
||
|
* release multipass resources, most notably but NOT exclusively
|
||
|
* close log file open'd on disk.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
*/
|
||
|
static void tc_lavc_fini_multipass(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
if (pd->ff_vcontext.stats_in != NULL) {
|
||
|
tc_free(pd->ff_vcontext.stats_in);
|
||
|
pd->ff_vcontext.stats_in = NULL;
|
||
|
}
|
||
|
if (pd->stats_file != NULL) {
|
||
|
fclose(pd->stats_file); /* XXX */
|
||
|
pd->stats_file = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_init_rc_override:
|
||
|
* parse Rate Control override string given in format understood
|
||
|
* by libavcodec and store result in internal avcodec context.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* str: RC override string to parse.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
* Side Effects:
|
||
|
* some memory will be allocated.
|
||
|
*/
|
||
|
static void tc_lavc_init_rc_override(TCLavcPrivateData *pd, const char *str)
|
||
|
{
|
||
|
int i = 0;
|
||
|
|
||
|
if (str != NULL && strlen(str) > 0) {
|
||
|
const char *p = str;
|
||
|
|
||
|
for (i = 0; p != NULL; i++) {
|
||
|
int start, end, q;
|
||
|
int e = sscanf(p, "%i,%i,%i", &start, &end, &q);
|
||
|
|
||
|
if (e != 3) {
|
||
|
tc_log_warn(MOD_NAME, "Error parsing rc_override (ignored)");
|
||
|
return;
|
||
|
}
|
||
|
pd->ff_vcontext.rc_override =
|
||
|
tc_realloc(pd->ff_vcontext.rc_override,
|
||
|
sizeof(RcOverride) * (i + 1)); /* XXX */
|
||
|
pd->ff_vcontext.rc_override[i].start_frame = start;
|
||
|
pd->ff_vcontext.rc_override[i].end_frame = end;
|
||
|
if (q > 0) {
|
||
|
pd->ff_vcontext.rc_override[i].qscale = q;
|
||
|
pd->ff_vcontext.rc_override[i].quality_factor = 1.0;
|
||
|
} else {
|
||
|
pd->ff_vcontext.rc_override[i].qscale = 0;
|
||
|
pd->ff_vcontext.rc_override[i].quality_factor = -q / 100.0;
|
||
|
}
|
||
|
p = strchr(p, '/');
|
||
|
if (p != NULL) {
|
||
|
p++;
|
||
|
}
|
||
|
}
|
||
|
}
|
||
|
pd->ff_vcontext.rc_override_count = i;
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_fini_rc_override:
|
||
|
* free Rate Control override resources acquired by
|
||
|
* former call of tc_lavc_init_rc_override.
|
||
|
* It's safe to call this function even if
|
||
|
* tc_lavc_init_rc_override was NOT called previously.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
*/
|
||
|
static void tc_lavc_fini_rc_override(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
if (pd->ff_vcontext.rc_override != NULL) {
|
||
|
tc_free(pd->ff_vcontext.rc_override);
|
||
|
pd->ff_vcontext.rc_override = NULL;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_init_buf:
|
||
|
* allocate internal colorspace conversion buffer, if needed
|
||
|
* (depending by internal pixel format),
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* vob: pointer to vob_t structure.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull
|
||
|
* TC_ERROR: error (can't allocate buffers)
|
||
|
* Preconditions:
|
||
|
* INTERNAL pixel format already determined using
|
||
|
* tc_lavc_set_pix_fmt().
|
||
|
*/
|
||
|
|
||
|
static int tc_lavc_init_buf(TCLavcPrivateData *pd, const vob_t *vob)
|
||
|
{
|
||
|
if (pd->tc_pix_fmt != TC_CODEC_YUV420P) { /*yuv420p it's our default */
|
||
|
pd->vframe_buf = tc_new_video_frame(vob->im_v_width, vob->im_v_height,
|
||
|
pd->tc_pix_fmt, TC_TRUE);
|
||
|
if (pd->vframe_buf == NULL) {
|
||
|
tc_log_warn(MOD_NAME, "unable to allocate internal vframe buffer");
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
}
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
/* release internal colorspace conversion buffers. */
|
||
|
#define tc_lavc_fini_buf(PD) do { \
|
||
|
if ((PD) != NULL && (PD)->vframe_buf != NULL) { \
|
||
|
tc_del_video_frame((PD)->vframe_buf); \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_settings_from_vob:
|
||
|
* translate vob settings and store them in module
|
||
|
* private data and in avcodec context, in correct format.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* vob: pointer to vob_t structure.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull
|
||
|
* TC_ERROR: error (various reasons, all will be tc_log*()'d out)
|
||
|
* Side Effects:
|
||
|
* various helper subroutines will be called.
|
||
|
*/
|
||
|
static int tc_lavc_settings_from_vob(TCLavcPrivateData *pd, const vob_t *vob)
|
||
|
{
|
||
|
int ret = 0;
|
||
|
|
||
|
pd->ff_vcontext.bit_rate = vob->divxbitrate * 1000;
|
||
|
pd->ff_vcontext.width = vob->ex_v_width;
|
||
|
pd->ff_vcontext.height = vob->ex_v_height;
|
||
|
pd->ff_vcontext.qmin = vob->min_quantizer;
|
||
|
pd->ff_vcontext.qmax = vob->max_quantizer;
|
||
|
|
||
|
if (vob->export_attributes & TC_EXPORT_ATTRIBUTE_GOP) {
|
||
|
pd->ff_vcontext.gop_size = vob->divxkeyframes;
|
||
|
} else {
|
||
|
if (TC_VCODEC_ID(pd) == TC_CODEC_MPEG1VIDEO
|
||
|
|| TC_VCODEC_ID(pd) == TC_CODEC_MPEG2VIDEO) {
|
||
|
pd->ff_vcontext.gop_size = 15; /* conservative default for mpeg1/2 svcd/dvd */
|
||
|
} else {
|
||
|
pd->ff_vcontext.gop_size = 250; /* reasonable default for mpeg4 (and others) */
|
||
|
}
|
||
|
}
|
||
|
|
||
|
ret = tc_find_best_aspect_ratio(vob,
|
||
|
&pd->ff_vcontext.sample_aspect_ratio.num,
|
||
|
&pd->ff_vcontext.sample_aspect_ratio.den,
|
||
|
MOD_NAME);
|
||
|
if (ret != TC_OK) {
|
||
|
tc_log_error(MOD_NAME, "unable to find sane value for SAR");
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
ret = tc_frc_code_to_ratio(vob->ex_frc,
|
||
|
&pd->ff_vcontext.time_base.den,
|
||
|
&pd->ff_vcontext.time_base.num);
|
||
|
/* watch out here */
|
||
|
if (ret == TC_NULL_MATCH) {
|
||
|
/* legacy */
|
||
|
if ((vob->ex_fps > 29) && (vob->ex_fps < 30)) {
|
||
|
pd->ff_vcontext.time_base.den = 30000;
|
||
|
pd->ff_vcontext.time_base.num = 1001;
|
||
|
} else {
|
||
|
pd->ff_vcontext.time_base.den = (int)(vob->ex_fps * 1000.0);
|
||
|
pd->ff_vcontext.time_base.num = 1000;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
switch(vob->encode_fields) {
|
||
|
case TC_ENCODE_FIELDS_TOP_FIRST:
|
||
|
pd->interlacing.active = 1;
|
||
|
pd->interlacing.top_first = 1;
|
||
|
break;
|
||
|
case TC_ENCODE_FIELDS_BOTTOM_FIRST:
|
||
|
pd->interlacing.active = 1;
|
||
|
pd->interlacing.top_first = 0;
|
||
|
break;
|
||
|
default: /* progressive / unknown */
|
||
|
pd->interlacing.active = 0;
|
||
|
pd->interlacing.top_first = 0;
|
||
|
break;
|
||
|
}
|
||
|
|
||
|
ret = tc_lavc_set_pix_fmt(pd, vob);
|
||
|
if (ret != TC_OK) {
|
||
|
return ret;
|
||
|
}
|
||
|
return tc_lavc_init_multipass(pd, vob);
|
||
|
}
|
||
|
|
||
|
#define PCTX(field) &(pd->ff_vcontext.field)
|
||
|
#define PAUX(field) &(pd->confdata.field)
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_config_defaults:
|
||
|
* setup sane values for auxiliary config, and setup *transcode's*
|
||
|
* AVCodecContext default settings.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* Return Value:
|
||
|
* None
|
||
|
*/
|
||
|
static void tc_lavc_config_defaults(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
/* first of all reinitialize lavc data */
|
||
|
avcodec_get_context_defaults(&pd->ff_vcontext);
|
||
|
|
||
|
pd->confdata.thread_count = 1;
|
||
|
|
||
|
pd->confdata.vrate_tolerance = 8 * 1000;
|
||
|
pd->confdata.rc_min_rate = 0;
|
||
|
pd->confdata.rc_max_rate = 0;
|
||
|
pd->confdata.rc_buffer_size = 0;
|
||
|
pd->confdata.lmin = 2;
|
||
|
pd->confdata.lmax = 31;
|
||
|
pd->confdata.me_method = ME_EPZS;
|
||
|
|
||
|
memset(&pd->confdata.flags, 0, sizeof(pd->confdata.flags));
|
||
|
pd->confdata.turbo_setup = 0;
|
||
|
|
||
|
/*
|
||
|
* context *transcode* (not libavcodec) defaults
|
||
|
*/
|
||
|
pd->ff_vcontext.mb_qmin = 2;
|
||
|
pd->ff_vcontext.mb_qmax = 31;
|
||
|
pd->ff_vcontext.max_qdiff = 3;
|
||
|
pd->ff_vcontext.max_b_frames = 0;
|
||
|
pd->ff_vcontext.me_range = 0;
|
||
|
pd->ff_vcontext.mb_decision = 0;
|
||
|
pd->ff_vcontext.scenechange_threshold = 0;
|
||
|
pd->ff_vcontext.scenechange_factor = 1;
|
||
|
pd->ff_vcontext.b_frame_strategy = 0;
|
||
|
pd->ff_vcontext.b_sensitivity = 40;
|
||
|
pd->ff_vcontext.brd_scale = 0;
|
||
|
pd->ff_vcontext.bidir_refine = 0;
|
||
|
pd->ff_vcontext.rc_strategy = 2;
|
||
|
pd->ff_vcontext.b_quant_factor = 1.25;
|
||
|
pd->ff_vcontext.i_quant_factor = 0.8;
|
||
|
pd->ff_vcontext.b_quant_offset = 1.25;
|
||
|
pd->ff_vcontext.i_quant_offset = 0.0;
|
||
|
pd->ff_vcontext.qblur = 0.5;
|
||
|
pd->ff_vcontext.qcompress = 0.5;
|
||
|
pd->ff_vcontext.mpeg_quant = 0;
|
||
|
pd->ff_vcontext.rc_initial_cplx = 0.0;
|
||
|
pd->ff_vcontext.rc_qsquish = 1.0;
|
||
|
pd->ff_vcontext.luma_elim_threshold = 0;
|
||
|
pd->ff_vcontext.chroma_elim_threshold = 0;
|
||
|
pd->ff_vcontext.strict_std_compliance = 0;
|
||
|
pd->ff_vcontext.dct_algo = FF_DCT_AUTO;
|
||
|
pd->ff_vcontext.idct_algo = FF_IDCT_AUTO;
|
||
|
pd->ff_vcontext.lumi_masking = 0.0;
|
||
|
pd->ff_vcontext.dark_masking = 0.0;
|
||
|
pd->ff_vcontext.temporal_cplx_masking = 0.0;
|
||
|
pd->ff_vcontext.spatial_cplx_masking = 0.0;
|
||
|
pd->ff_vcontext.p_masking = 0.0;
|
||
|
pd->ff_vcontext.border_masking = 0.0;
|
||
|
pd->ff_vcontext.me_pre_cmp = 0;
|
||
|
pd->ff_vcontext.me_cmp = 0;
|
||
|
pd->ff_vcontext.me_sub_cmp = 0;
|
||
|
pd->ff_vcontext.ildct_cmp = FF_CMP_SAD;
|
||
|
pd->ff_vcontext.pre_dia_size = 0;
|
||
|
pd->ff_vcontext.dia_size = 0;
|
||
|
pd->ff_vcontext.mv0_threshold = 256;
|
||
|
pd->ff_vcontext.last_predictor_count = 0;
|
||
|
pd->ff_vcontext.pre_me = 1;
|
||
|
pd->ff_vcontext.me_subpel_quality = 8;
|
||
|
pd->ff_vcontext.refs = 1;
|
||
|
pd->ff_vcontext.intra_quant_bias = FF_DEFAULT_QUANT_BIAS;
|
||
|
pd->ff_vcontext.inter_quant_bias = FF_DEFAULT_QUANT_BIAS;
|
||
|
pd->ff_vcontext.noise_reduction = 0;
|
||
|
pd->ff_vcontext.quantizer_noise_shaping = 0;
|
||
|
pd->ff_vcontext.flags = 0;
|
||
|
}
|
||
|
|
||
|
|
||
|
/* FIXME: it is too nasty? */
|
||
|
#define SET_FLAG(pd, field) (pd)->ff_vcontext.flags |= (pd)->confdata.flags.field
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_dispatch_settings:
|
||
|
* translate auxiliary configuration into context values;
|
||
|
* also does some consistency verifications.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* vob: pointer to vob_t structure.
|
||
|
* Return Value:
|
||
|
* None.
|
||
|
*/
|
||
|
static void tc_lavc_dispatch_settings(TCLavcPrivateData *pd)
|
||
|
{
|
||
|
/* some translation... */
|
||
|
pd->ff_vcontext.bit_rate_tolerance = pd->confdata.vrate_tolerance * 1000;
|
||
|
pd->ff_vcontext.rc_min_rate = pd->confdata.rc_min_rate * 1000;
|
||
|
pd->ff_vcontext.rc_max_rate = pd->confdata.rc_max_rate * 1000;
|
||
|
pd->ff_vcontext.rc_buffer_size = pd->confdata.rc_buffer_size * 1024;
|
||
|
pd->ff_vcontext.lmin = (int)(FF_QP2LAMBDA * pd->confdata.lmin + 0.5);
|
||
|
pd->ff_vcontext.lmax = (int)(FF_QP2LAMBDA * pd->confdata.lmax + 0.5);
|
||
|
pd->ff_vcontext.me_method = ME_ZERO + pd->confdata.me_method;
|
||
|
|
||
|
pd->ff_vcontext.flags = 0;
|
||
|
SET_FLAG(pd, mv0);
|
||
|
SET_FLAG(pd, cbp);
|
||
|
SET_FLAG(pd, qpel);
|
||
|
SET_FLAG(pd, alt);
|
||
|
SET_FLAG(pd, vdpart);
|
||
|
SET_FLAG(pd, naq);
|
||
|
SET_FLAG(pd, ilme);
|
||
|
SET_FLAG(pd, ildct);
|
||
|
SET_FLAG(pd, aic);
|
||
|
SET_FLAG(pd, aiv);
|
||
|
SET_FLAG(pd, umv);
|
||
|
SET_FLAG(pd, psnr);
|
||
|
SET_FLAG(pd, trell);
|
||
|
SET_FLAG(pd, gray);
|
||
|
SET_FLAG(pd, v4mv);
|
||
|
SET_FLAG(pd, closedgop);
|
||
|
|
||
|
#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0)
|
||
|
/* FIXME: coherency check */
|
||
|
if (pd->ff_vcontext.rtp_payload_size > 0) {
|
||
|
pd->ff_vcontext.rtp_mode = 1;
|
||
|
}
|
||
|
#endif
|
||
|
if (pd->confdata.flags.closedgop) {
|
||
|
pd->ff_vcontext.scenechange_threshold = 1000000;
|
||
|
}
|
||
|
if (pd->interlacing.active) {
|
||
|
/* enforce interlacing */
|
||
|
pd->ff_vcontext.flags |= CODEC_FLAG_INTERLACED_DCT;
|
||
|
pd->ff_vcontext.flags |= CODEC_FLAG_INTERLACED_ME;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
#undef SET_FLAG
|
||
|
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_read_config:
|
||
|
* read configuration values from
|
||
|
* 1) configuration file (if found)
|
||
|
* 2) command line (overrides configuration file in case
|
||
|
* of conflicts).
|
||
|
* Also read related informations like RC override string
|
||
|
* and custom quantization matrices; translate all settings
|
||
|
* in libavcodec-friendly values (if needed), then finally
|
||
|
* perform some coherency checks and feed avcodec context
|
||
|
* with gathered data.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* options: command line options of *THIS MODULE*.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull
|
||
|
* TC_ERROR: error. Mostly I/O related or badly broken
|
||
|
* (meaningless) value. Exact reason will tc_log*()'d out.
|
||
|
* Side Effects:
|
||
|
* Quite a lot, since various (and quite complex) subroutines
|
||
|
* are involved. Most notably, various files can be opened/read/closed
|
||
|
* on disk, and some memory could be allocated.
|
||
|
*
|
||
|
* FIXME: I'm a bit worried about heavy stack usage of this function...
|
||
|
*/
|
||
|
static int tc_lavc_read_config(TCLavcPrivateData *pd,
|
||
|
const char *options, const vob_t *vob)
|
||
|
{
|
||
|
char intra_matrix_file[PATH_MAX] = { '\0' };
|
||
|
char inter_matrix_file[PATH_MAX] = { '\0' };
|
||
|
char rc_override_buf[TC_BUF_MIN] = { '\0' }; /* XXX */
|
||
|
/*
|
||
|
* Please note that option names are INTENTIONALLY identical/similar
|
||
|
* to mplayer/mencoder ones
|
||
|
*/
|
||
|
TCConfigEntry lavc_conf[] = {
|
||
|
{ "threads", PAUX(thread_count), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 7 },
|
||
|
// need special handling
|
||
|
// { "keyint", PCTX(gop_size), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 1000 },
|
||
|
// handled by transcode core
|
||
|
// { "vbitrate", PCTX(bit_rate), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, INT_MAX },
|
||
|
// handled by transcode core
|
||
|
// { "vqmin", PCTX(qmin), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 60 },
|
||
|
// handled by transcode core
|
||
|
// { "vqmax", PCTX(qmax), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 60 },
|
||
|
// handled by transcode core
|
||
|
{ "mbqmin", PCTX(mb_qmin), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 60 },
|
||
|
{ "mbqmax", PCTX(mb_qmax), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 60 },
|
||
|
{ "lmin", PAUX(lmin), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.01, 255.0 },
|
||
|
{ "lmax", PAUX(lmax), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.01, 255.0 },
|
||
|
{ "vqdiff", PCTX(max_qdiff), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 31 },
|
||
|
{ "vmax_b_frames", PCTX(max_b_frames), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, FF_MAX_B_FRAMES },
|
||
|
{ "vme", PAUX(me_method), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 16, },
|
||
|
{ "me_range", PCTX(me_range), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 16000 },
|
||
|
{ "mbd", PCTX(mb_decision), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 3 },
|
||
|
{ "sc_threshold", PCTX(scenechange_threshold), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -1000000, 1000000 },
|
||
|
{ "sc_factor", PCTX(scenechange_factor), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 16 },
|
||
|
{ "vb_strategy", PCTX(b_frame_strategy), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 10 },
|
||
|
{ "b_sensitivity", PCTX(b_sensitivity), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 100 },
|
||
|
{ "brd_scale", PCTX(brd_scale), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 10 },
|
||
|
{ "bidir_refine", PCTX(bidir_refine), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 4 },
|
||
|
// { "aspect", },
|
||
|
// handled by transcode core
|
||
|
{ "vratetol", PAUX(vrate_tolerance), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 4, 24000000 },
|
||
|
{ "vrc_maxrate", PAUX(rc_max_rate), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 24000000 },
|
||
|
{ "vrc_minrate", PAUX(rc_min_rate), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 24000000 },
|
||
|
{ "vrc_buf_size", PAUX(rc_buffer_size), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 4, 24000000 },
|
||
|
{ "vrc_strategy", PCTX(rc_strategy), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2 },
|
||
|
{ "vb_qfactor", PCTX(b_quant_factor), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, -31.0, 31.0 },
|
||
|
{ "vi_qfactor", PCTX(i_quant_factor), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, -31.0, 31.0 },
|
||
|
{ "vb_qoffset", PCTX(b_quant_offset), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 31.0 },
|
||
|
{ "vi_qoffset", PCTX(i_quant_offset), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 31.0 },
|
||
|
{ "vqblur", PCTX(qblur), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "vqcomp", PCTX(qcompress), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "mpeg_quant", PCTX(mpeg_quant), TCCONF_TYPE_FLAG, 0, 0, 1 },
|
||
|
// { "vrc_eq", }, // not yet supported
|
||
|
{ "vrc_override", rc_override_buf, TCCONF_TYPE_STRING, 0, 0, 0 },
|
||
|
{ "vrc_init_cplx", PCTX(rc_initial_cplx), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 9999999.0 },
|
||
|
// { "vrc_init_occupancy", }, // not yet supported
|
||
|
{ "vqsquish", PCTX(rc_qsquish), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 99.0 },
|
||
|
{ "vlelim", PCTX(luma_elim_threshold), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -99, 99 },
|
||
|
{ "vcelim", PCTX(chroma_elim_threshold), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -99, 99 },
|
||
|
{ "vstrict", PCTX(strict_std_compliance), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -99, 99 },
|
||
|
{ "vpsize", PCTX(rtp_payload_size), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 100000000 },
|
||
|
{ "dct", PCTX(dct_algo), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 10 },
|
||
|
{ "idct", PCTX(idct_algo), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 20 },
|
||
|
{ "lumi_mask", PCTX(lumi_masking), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "dark_mask", PCTX(dark_masking), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "tcplx_mask", PCTX(temporal_cplx_masking), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "scplx_mask", PCTX(spatial_cplx_masking), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "p_mask", PCTX(p_masking), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "border_mask", PCTX(border_masking), TCCONF_TYPE_FLOAT, TCCONF_FLAG_RANGE, 0.0, 1.0 },
|
||
|
{ "pred", PCTX(prediction_method), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 4 },
|
||
|
{ "precmp", PCTX(me_pre_cmp), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2000 },
|
||
|
{ "cmp", PCTX(me_cmp), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2000 },
|
||
|
{ "subcmp", PCTX(me_sub_cmp), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2000 },
|
||
|
{ "ildctcmp", PCTX(ildct_cmp), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2000 },
|
||
|
{ "predia", PCTX(pre_dia_size), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -2000, 2000 },
|
||
|
{ "dia", PCTX(dia_size), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -2000, 2000 },
|
||
|
{ "mv0_threshold", PCTX(mv0_threshold), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 1000 },
|
||
|
{ "last_pred", PCTX(last_predictor_count), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2000 },
|
||
|
{ "pre_me", PCTX(pre_me), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 2000},
|
||
|
{ "subq", PCTX(me_subpel_quality), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 8 },
|
||
|
{ "refs", PCTX(refs), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 1, 8 },
|
||
|
{ "ibias", PCTX(intra_quant_bias), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -512, 512 },
|
||
|
{ "pbias", PCTX(inter_quant_bias), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, -512, 512 },
|
||
|
{ "nr", PCTX(noise_reduction), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 1000000},
|
||
|
{ "qns", PCTX(quantizer_noise_shaping), TCCONF_TYPE_INT, TCCONF_FLAG_RANGE, 0, 3 },
|
||
|
{ "inter_matrix_file", inter_matrix_file, TCCONF_TYPE_STRING, 0, 0, 0 },
|
||
|
{ "intra_matrix_file", intra_matrix_file, TCCONF_TYPE_STRING, 0, 0, 0 },
|
||
|
|
||
|
{ "mv0", PAUX(flags.mv0), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_MV0 },
|
||
|
{ "cbp", PAUX(flags.cbp), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_CBP_RD },
|
||
|
{ "qpel", PAUX(flags.qpel), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_QPEL },
|
||
|
{ "alt", PAUX(flags.alt), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_ALT_SCAN },
|
||
|
{ "ilme", PAUX(flags.ilme), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_INTERLACED_ME },
|
||
|
{ "ildct", PAUX(flags.ildct), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_INTERLACED_DCT },
|
||
|
{ "naq", PAUX(flags.naq), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_NORMALIZE_AQP },
|
||
|
{ "vdpart", PAUX(flags.vdpart), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_PART },
|
||
|
#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0)
|
||
|
{ "aic", PAUX(flags.aic), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_H263P_AIC },
|
||
|
#else
|
||
|
{ "aic", PAUX(flags.aic), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_AC_PRED },
|
||
|
#endif
|
||
|
{ "aiv", PAUX(flags.aiv), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_H263P_AIV },
|
||
|
{ "umv", PAUX(flags.umv), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_H263P_UMV },
|
||
|
{ "psnr", PAUX(flags.psnr), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_PSNR },
|
||
|
#if LIBAVCODEC_VERSION_INT < ((52<<16)+(0<<8)+0)
|
||
|
{ "trell", PAUX(flags.trell), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_TRELLIS_QUANT },
|
||
|
#else
|
||
|
{ "trell", PCTX(trellis), TCCONF_TYPE_FLAG, 0, 0, 1 },
|
||
|
#endif
|
||
|
{ "gray", PAUX(flags.gray), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_GRAY },
|
||
|
{ "v4mv", PAUX(flags.v4mv), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_4MV },
|
||
|
{ "closedgop", PAUX(flags.closedgop), TCCONF_TYPE_FLAG, 0, 0, CODEC_FLAG_CLOSED_GOP },
|
||
|
|
||
|
// { "turbo", PAUX(turbo_setup), TCCONF_TYPE_FLAG, 0, 0, 1 }, // not yet supported
|
||
|
/* End of the config file */
|
||
|
{ NULL, 0, 0, 0, 0, 0 }
|
||
|
};
|
||
|
|
||
|
module_read_config(LAVC_CONFIG_FILE,
|
||
|
tc_codec_to_string(vob->ex_v_codec),
|
||
|
lavc_conf, MOD_NAME);
|
||
|
|
||
|
if (options && strlen(options) > 0) {
|
||
|
size_t i = 0, n = 0;
|
||
|
char **opts = tc_strsplit(options, ':', &n);
|
||
|
|
||
|
if (opts == NULL) {
|
||
|
tc_log_error(MOD_NAME, "can't split option string");
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
for (i = 0; i < n; i++) {
|
||
|
if (!module_read_config_line(opts[i], lavc_conf, MOD_NAME)) {
|
||
|
tc_log_error(MOD_NAME, "error parsing module options (%s)",
|
||
|
opts[i]);
|
||
|
tc_strfreev(opts);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
}
|
||
|
tc_strfreev(opts);
|
||
|
}
|
||
|
|
||
|
/* gracefully go ahead if no matrices are given */
|
||
|
tc_lavc_read_matrices(pd, intra_matrix_file, inter_matrix_file);
|
||
|
/* gracefully go ahead if no rc override is given */
|
||
|
tc_lavc_init_rc_override(pd, rc_override_buf);
|
||
|
|
||
|
if (verbose >= TC_DEBUG) {
|
||
|
module_print_config(lavc_conf, MOD_NAME);
|
||
|
}
|
||
|
/* only now we can do this safely */
|
||
|
tc_lavc_dispatch_settings(pd);
|
||
|
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
#undef PCTX
|
||
|
#undef PAUX
|
||
|
|
||
|
/*
|
||
|
* tc_lavc_write_logs:
|
||
|
* write on disk file encoding logs. That means encoder
|
||
|
* multipass log file, but that can also include PSNR
|
||
|
* statistics, if requested.
|
||
|
*
|
||
|
* Parameters:
|
||
|
* pd: pointer to private module data.
|
||
|
* size: size of last encoded frame.
|
||
|
* Return Value:
|
||
|
* TC_OK: succesfull
|
||
|
* TC_ERROR: I/O error. Exact reason will tc_log*()'d out.
|
||
|
*/
|
||
|
static int tc_lavc_write_logs(TCLavcPrivateData *pd, int size)
|
||
|
{
|
||
|
/* store stats if there are any */
|
||
|
if (pd->ff_vcontext.stats_out != NULL && pd->stats_file != NULL) {
|
||
|
int ret = fprintf(pd->stats_file, "%s",
|
||
|
pd->ff_vcontext.stats_out);
|
||
|
if (ret < 0) {
|
||
|
tc_log_warn(MOD_NAME, "error while writing multipass log file");
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
if (PSNR_REQUESTED(pd)) {
|
||
|
/* errors not fatal, they can be ignored */
|
||
|
psnr_write(pd, size);
|
||
|
}
|
||
|
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
/* see libtc/tcmodule-data.h for functions meaning and purposes */
|
||
|
|
||
|
|
||
|
static int tc_lavc_init(TCModuleInstance *self, uint32_t features)
|
||
|
{
|
||
|
TCLavcPrivateData *pd = NULL;
|
||
|
|
||
|
TC_MODULE_SELF_CHECK(self, "init");
|
||
|
TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
|
||
|
|
||
|
pd = tc_malloc(sizeof(TCLavcPrivateData));
|
||
|
if (pd == NULL) {
|
||
|
tc_log_error(MOD_NAME, "unable to allocate private data");
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
|
||
|
/* enforce NULL-ness of dangerous (segfault-friendly) stuff */
|
||
|
pd->psnr_file = NULL;
|
||
|
pd->stats_file = NULL;
|
||
|
|
||
|
if (verbose) {
|
||
|
tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
|
||
|
}
|
||
|
self->userdata = pd;
|
||
|
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tc_lavc_fini(TCModuleInstance *self)
|
||
|
{
|
||
|
TC_MODULE_SELF_CHECK(self, "fini");
|
||
|
|
||
|
/* _stop() does the magic; FIXME: recall from here? */
|
||
|
tc_free(self->userdata);
|
||
|
self->userdata = NULL;
|
||
|
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
#define ABORT_IF_NOT_OK(RET) do { \
|
||
|
if ((RET) != TC_OK) { \
|
||
|
goto failed; \
|
||
|
} \
|
||
|
} while (0)
|
||
|
|
||
|
|
||
|
static int tc_lavc_configure(TCModuleInstance *self,
|
||
|
const char *options, vob_t *vob)
|
||
|
{
|
||
|
const char *vcodec_name = tc_codec_to_string(vob->ex_v_codec);
|
||
|
TCLavcPrivateData *pd = NULL;
|
||
|
int ret = TC_OK;
|
||
|
|
||
|
TC_MODULE_SELF_CHECK(self, "configure");
|
||
|
TC_MODULE_SELF_CHECK(options, "configure"); /* paranoia */
|
||
|
|
||
|
pd = self->userdata;
|
||
|
|
||
|
pd->flush_flag = vob->encoder_flush;
|
||
|
|
||
|
/* FIXME: move into core? */
|
||
|
TC_INIT_LIBAVCODEC;
|
||
|
|
||
|
avcodec_get_frame_defaults(&pd->ff_venc_frame);
|
||
|
/*
|
||
|
* auxiliary config data needs to be blanked too
|
||
|
* before any other operation
|
||
|
*/
|
||
|
tc_lavc_config_defaults(pd);
|
||
|
|
||
|
/*
|
||
|
* we must do first since we NEED valid vcodec_name
|
||
|
* ASAP to read right section of configuration file.
|
||
|
*/
|
||
|
pd->vcodec_id = tc_codec_is_supported(vob->ex_v_codec);
|
||
|
if (pd->vcodec_id == TC_NULL_MATCH) {
|
||
|
tc_log_error(MOD_NAME, "unsupported codec `%s'", vcodec_name);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
if (verbose) {
|
||
|
tc_log_info(MOD_NAME, "using video codec '%s'", vcodec_name);
|
||
|
}
|
||
|
|
||
|
ret = tc_lavc_settings_from_vob(pd, vob);
|
||
|
ABORT_IF_NOT_OK(ret);
|
||
|
|
||
|
/* calling WARNING: order matters here */
|
||
|
ret = tc_lavc_init_buf(pd, vob);
|
||
|
ABORT_IF_NOT_OK(ret);
|
||
|
|
||
|
ret = tc_lavc_read_config(pd, options, vob);
|
||
|
ABORT_IF_NOT_OK(ret);
|
||
|
|
||
|
tc_lavc_load_filters(pd);
|
||
|
|
||
|
if (verbose) {
|
||
|
tc_log_info(MOD_NAME, "using %i thread%s",
|
||
|
pd->confdata.thread_count,
|
||
|
(pd->confdata.thread_count > 1) ?"s" :"");
|
||
|
}
|
||
|
avcodec_thread_init(&pd->ff_vcontext, pd->confdata.thread_count);
|
||
|
|
||
|
pd->ff_vcodec = avcodec_find_encoder(FF_VCODEC_ID(pd));
|
||
|
if (pd->ff_vcodec == NULL) {
|
||
|
tc_log_error(MOD_NAME, "unable to find a libavcodec encoder for `%s'",
|
||
|
tc_codec_to_string(TC_VCODEC_ID(pd)));
|
||
|
goto failed;
|
||
|
}
|
||
|
|
||
|
TC_LOCK_LIBAVCODEC;
|
||
|
ret = avcodec_open(&pd->ff_vcontext, pd->ff_vcodec);
|
||
|
TC_UNLOCK_LIBAVCODEC;
|
||
|
|
||
|
if (ret < 0) {
|
||
|
tc_log_error(MOD_NAME, "avcodec_open() failed");
|
||
|
goto failed;
|
||
|
}
|
||
|
/* finally, pass up the extradata, if any */
|
||
|
self->extradata = pd->ff_vcontext.extradata;
|
||
|
self->extradata_size = pd->ff_vcontext.extradata_size;
|
||
|
|
||
|
if (PSNR_REQUESTED(pd)) {
|
||
|
/* errors already logged, and they can be ignored */
|
||
|
psnr_open(pd);
|
||
|
pd->confdata.flags.psnr = 0; /* no longer requested :^) */
|
||
|
}
|
||
|
return TC_OK;
|
||
|
|
||
|
failed:
|
||
|
tc_lavc_fini_buf(pd);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
|
||
|
#undef ABORT_IF_NOT_OK
|
||
|
|
||
|
|
||
|
static int tc_lavc_inspect(TCModuleInstance *self,
|
||
|
const char *param, const char **value)
|
||
|
{
|
||
|
TC_MODULE_SELF_CHECK(self, "inspect");
|
||
|
TC_MODULE_SELF_CHECK(value, "inspect");
|
||
|
|
||
|
if (optstr_lookup(param, "help")) {
|
||
|
*value = tc_lavc_help;
|
||
|
}
|
||
|
|
||
|
if (optstr_lookup(param, "vcodec")) {
|
||
|
*value = "must be selected by user\n";
|
||
|
}
|
||
|
|
||
|
if (optstr_lookup(param, "list")) {
|
||
|
*value = tc_lavc_list_codecs();
|
||
|
}
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
static int tc_lavc_stop(TCModuleInstance *self)
|
||
|
{
|
||
|
TCLavcPrivateData *pd = NULL;
|
||
|
|
||
|
TC_MODULE_SELF_CHECK(self, "stop");
|
||
|
|
||
|
pd = self->userdata;
|
||
|
|
||
|
tc_lavc_fini_buf(pd);
|
||
|
|
||
|
if (PSNR_REQUESTED(pd)) {
|
||
|
psnr_print(pd);
|
||
|
psnr_close(pd);
|
||
|
}
|
||
|
|
||
|
tc_lavc_fini_rc_override(pd);
|
||
|
/* ok, now really start the real teardown */
|
||
|
tc_lavc_fini_multipass(pd);
|
||
|
|
||
|
if (pd->ff_vcodec != NULL) {
|
||
|
avcodec_close(&pd->ff_vcontext);
|
||
|
pd->ff_vcodec = NULL;
|
||
|
}
|
||
|
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
static int tc_lavc_flush_video(TCModuleInstance *self,
|
||
|
vframe_list_t *outframe)
|
||
|
{
|
||
|
outframe->video_len = 0;
|
||
|
return TC_OK;
|
||
|
}
|
||
|
|
||
|
|
||
|
static int tc_lavc_encode_video(TCModuleInstance *self,
|
||
|
vframe_list_t *inframe,
|
||
|
vframe_list_t *outframe)
|
||
|
{
|
||
|
TCLavcPrivateData *pd = NULL;
|
||
|
|
||
|
TC_MODULE_SELF_CHECK(self, "encode_video");
|
||
|
|
||
|
pd = self->userdata;
|
||
|
|
||
|
if (inframe == NULL && pd->flush_flag) {
|
||
|
return tc_lavc_flush_video(self, outframe); // FIXME
|
||
|
}
|
||
|
|
||
|
pd->ff_venc_frame.interlaced_frame = pd->interlacing.active;
|
||
|
pd->ff_venc_frame.top_field_first = pd->interlacing.top_first;
|
||
|
|
||
|
pd->pre_encode_video(pd, inframe);
|
||
|
|
||
|
TC_LOCK_LIBAVCODEC;
|
||
|
outframe->video_len = avcodec_encode_video(&pd->ff_vcontext,
|
||
|
outframe->video_buf,
|
||
|
inframe->video_size,
|
||
|
&pd->ff_venc_frame);
|
||
|
TC_UNLOCK_LIBAVCODEC;
|
||
|
|
||
|
if (outframe->video_len < 0) {
|
||
|
tc_log_warn(MOD_NAME, "encoder error: size (%i)",
|
||
|
outframe->video_len);
|
||
|
return TC_ERROR;
|
||
|
}
|
||
|
|
||
|
if (pd->ff_vcontext.coded_frame->key_frame) {
|
||
|
outframe->attributes |= TC_FRAME_IS_KEYFRAME;
|
||
|
}
|
||
|
|
||
|
return tc_lavc_write_logs(pd, outframe->video_len);
|
||
|
}
|
||
|
|
||
|
|
||
|
/*************************************************************************/
|
||
|
|
||
|
TC_MODULE_CODEC_FORMATS(tc_lavc);
|
||
|
TC_MODULE_INFO(tc_lavc);
|
||
|
|
||
|
static const TCModuleClass tc_lavc_class = {
|
||
|
TC_MODULE_CLASS_HEAD(tc_lavc),
|
||
|
|
||
|
.init = tc_lavc_init,
|
||
|
.fini = tc_lavc_fini,
|
||
|
.configure = tc_lavc_configure,
|
||
|
.stop = tc_lavc_stop,
|
||
|
.inspect = tc_lavc_inspect,
|
||
|
|
||
|
.encode_video = tc_lavc_encode_video,
|
||
|
};
|
||
|
|
||
|
extern const TCModuleClass *tc_plugin_setup(void)
|
||
|
{
|
||
|
return &tc_lavc_class;
|
||
|
}
|
||
|
|
||
|
/*************************************************************************/
|
||
|
|
||
|
/*
|
||
|
* Local variables:
|
||
|
* c-file-style: "stroustrup"
|
||
|
* c-file-offsets: ((case-label . *) (statement-case-intro . *))
|
||
|
* indent-tabs-mode: nil
|
||
|
* End:
|
||
|
*
|
||
|
* vim: expandtab shiftwidth=4:
|
||
|
*/
|