mirror of https://github.com/mpv-player/mpv
591 lines
21 KiB
C
591 lines
21 KiB
C
|
/*
|
||
|
* video encoding using libavformat
|
||
|
* Copyright (C) 2010 Nicolas George <george@nsup.org>
|
||
|
* Copyright (C) 2011 Rudolf Polzer <divVerent@xonotic.org>
|
||
|
*
|
||
|
* This file is part of MPlayer.
|
||
|
*
|
||
|
* MPlayer 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.
|
||
|
*
|
||
|
* MPlayer 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 MPlayer; if not, write to the Free Software Foundation, Inc.,
|
||
|
* 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA.
|
||
|
*/
|
||
|
|
||
|
#include <stdio.h>
|
||
|
#include <stdlib.h>
|
||
|
#include "mpcommon.h"
|
||
|
#include "options.h"
|
||
|
#include "fmt-conversion.h"
|
||
|
#include "libmpcodecs/mp_image.h"
|
||
|
#include "libmpcodecs/vfcap.h"
|
||
|
#include "subopt-helper.h"
|
||
|
#include "talloc.h"
|
||
|
#include "video_out.h"
|
||
|
|
||
|
#include "encode_lavc.h"
|
||
|
|
||
|
#include "sub/sub.h"
|
||
|
#include "libvo/osd.h"
|
||
|
|
||
|
struct priv {
|
||
|
uint8_t *buffer;
|
||
|
size_t buffer_size;
|
||
|
AVStream *stream;
|
||
|
int have_first_packet;
|
||
|
|
||
|
int harddup;
|
||
|
|
||
|
double lastpts;
|
||
|
int64_t lastipts;
|
||
|
int64_t lastframeipts;
|
||
|
mp_image_t *lastimg;
|
||
|
int lastdisplaycount;
|
||
|
|
||
|
AVRational worst_time_base;
|
||
|
int worst_time_base_is_stream;
|
||
|
|
||
|
struct osd_state *osd;
|
||
|
|
||
|
struct mp_csp_details colorspace;
|
||
|
};
|
||
|
|
||
|
static int preinit(struct vo *vo, const char *arg)
|
||
|
{
|
||
|
struct priv *vc;
|
||
|
if (!encode_lavc_available(vo->encode_lavc_ctx)) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||
|
"vo-lavc: the option -o (output file) must be specified\n");
|
||
|
return -1;
|
||
|
}
|
||
|
vo->priv = talloc_zero(vo, struct priv);
|
||
|
vc = vo->priv;
|
||
|
vc->harddup = vo->encode_lavc_ctx->options->harddup;
|
||
|
vc->colorspace = (struct mp_csp_details) MP_CSP_DETAILS_DEFAULTS;
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
static void draw_image(struct vo *vo, mp_image_t *mpi, double pts);
|
||
|
static void uninit(struct vo *vo)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
if (!vc)
|
||
|
return;
|
||
|
|
||
|
if (vc->lastipts >= 0 && vc->stream)
|
||
|
draw_image(vo, NULL, MP_NOPTS_VALUE);
|
||
|
|
||
|
if (vc->lastimg) {
|
||
|
// palette hack
|
||
|
if (vc->lastimg->imgfmt == IMGFMT_RGB8
|
||
|
|| vc->lastimg->imgfmt == IMGFMT_BGR8)
|
||
|
vc->lastimg->planes[1] = NULL;
|
||
|
free_mp_image(vc->lastimg);
|
||
|
vc->lastimg = NULL;
|
||
|
}
|
||
|
|
||
|
vo->priv = NULL;
|
||
|
}
|
||
|
|
||
|
static int config(struct vo *vo, uint32_t width, uint32_t height,
|
||
|
uint32_t d_width, uint32_t d_height, uint32_t flags,
|
||
|
uint32_t format)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
|
||
|
AVRational display_aspect_ratio, image_aspect_ratio;
|
||
|
AVRational aspect;
|
||
|
|
||
|
if (!vc)
|
||
|
return -1;
|
||
|
|
||
|
display_aspect_ratio.num = d_width;
|
||
|
display_aspect_ratio.den = d_height;
|
||
|
image_aspect_ratio.num = width;
|
||
|
image_aspect_ratio.den = height;
|
||
|
aspect = av_div_q(display_aspect_ratio, image_aspect_ratio);
|
||
|
|
||
|
if (vc->stream) {
|
||
|
/* NOTE:
|
||
|
* in debug builds we get a "comparison between signed and unsigned"
|
||
|
* warning here. We choose to ignore that; just because ffmpeg currently
|
||
|
* uses a plain 'int' for these struct fields, it doesn't mean it always
|
||
|
* will */
|
||
|
if (width == vc->stream->codec->width &&
|
||
|
height == vc->stream->codec->height) {
|
||
|
if (aspect.num != vc->stream->codec->sample_aspect_ratio.num ||
|
||
|
aspect.den != vc->stream->codec->sample_aspect_ratio.den) {
|
||
|
/* aspect-only changes are not critical */
|
||
|
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: unsupported pixel aspect "
|
||
|
"ratio change from %d:%d to %d:%d\n",
|
||
|
vc->stream->codec->sample_aspect_ratio.num,
|
||
|
vc->stream->codec->sample_aspect_ratio.den,
|
||
|
aspect.num, aspect.den);
|
||
|
}
|
||
|
return 0;
|
||
|
}
|
||
|
|
||
|
/* FIXME Is it possible with raw video? */
|
||
|
mp_msg(MSGT_ENCODE, MSGL_ERR,
|
||
|
"vo-lavc: resolution changes not supported.\n");
|
||
|
goto error;
|
||
|
}
|
||
|
|
||
|
vc->lastipts = MP_NOPTS_VALUE;
|
||
|
vc->lastframeipts = MP_NOPTS_VALUE;
|
||
|
|
||
|
if (pix_fmt == PIX_FMT_NONE)
|
||
|
goto error; /* imgfmt2pixfmt already prints something */
|
||
|
|
||
|
vc->stream = encode_lavc_alloc_stream(vo->encode_lavc_ctx,
|
||
|
AVMEDIA_TYPE_VIDEO);
|
||
|
vc->stream->sample_aspect_ratio = vc->stream->codec->sample_aspect_ratio =
|
||
|
aspect;
|
||
|
vc->stream->codec->width = width;
|
||
|
vc->stream->codec->height = height;
|
||
|
vc->stream->codec->pix_fmt = pix_fmt;
|
||
|
|
||
|
encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
|
||
|
encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
|
||
|
vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
|
||
|
vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
|
||
|
|
||
|
if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0)
|
||
|
goto error;
|
||
|
|
||
|
vc->buffer_size = 6 * width * height + 200;
|
||
|
if (vc->buffer_size < FF_MIN_BUFFER_SIZE)
|
||
|
vc->buffer_size = FF_MIN_BUFFER_SIZE;
|
||
|
if (vc->buffer_size < sizeof(AVPicture))
|
||
|
vc->buffer_size = sizeof(AVPicture);
|
||
|
|
||
|
vc->buffer = talloc_size(vc, vc->buffer_size);
|
||
|
|
||
|
vc->lastimg = alloc_mpi(width, height, format);
|
||
|
|
||
|
// palette hack
|
||
|
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
|
||
|
vc->lastimg->imgfmt == IMGFMT_BGR8)
|
||
|
vc->lastimg->planes[1] = talloc_zero_size(vc, 1024);
|
||
|
|
||
|
return 0;
|
||
|
|
||
|
error:
|
||
|
uninit(vo);
|
||
|
return -1;
|
||
|
}
|
||
|
|
||
|
static int query_format(struct vo *vo, uint32_t format)
|
||
|
{
|
||
|
enum PixelFormat pix_fmt = imgfmt2pixfmt(format);
|
||
|
|
||
|
if (!vo->encode_lavc_ctx)
|
||
|
return 0;
|
||
|
|
||
|
return encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt) ?
|
||
|
VFCAP_CSP_SUPPORTED : 0;
|
||
|
}
|
||
|
|
||
|
static void write_packet(struct vo *vo, int size, AVPacket *packet)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
|
||
|
if (size < 0) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error encoding\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
if (size > 0) {
|
||
|
packet->stream_index = vc->stream->index;
|
||
|
if (packet->pts != AV_NOPTS_VALUE) {
|
||
|
packet->pts = av_rescale_q(packet->pts,
|
||
|
vc->stream->codec->time_base,
|
||
|
vc->stream->time_base);
|
||
|
} else {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: codec did not provide pts\n");
|
||
|
packet->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
|
||
|
vc->stream->time_base);
|
||
|
}
|
||
|
if (packet->dts != AV_NOPTS_VALUE) {
|
||
|
packet->dts = av_rescale_q(packet->dts,
|
||
|
vc->stream->codec->time_base,
|
||
|
vc->stream->time_base);
|
||
|
}
|
||
|
if (packet->duration > 0) {
|
||
|
packet->duration = av_rescale_q(packet->duration,
|
||
|
vc->stream->codec->time_base,
|
||
|
vc->stream->time_base);
|
||
|
} else {
|
||
|
// HACK: libavformat calculates dts wrong if the initial packet
|
||
|
// duration is not set, but ONLY if the time base is "high" and if we
|
||
|
// have b-frames!
|
||
|
if (!packet->duration)
|
||
|
if (!vc->have_first_packet)
|
||
|
if (vc->stream->codec->has_b_frames
|
||
|
|| vc->stream->codec->max_b_frames)
|
||
|
if (vc->stream->time_base.num * 1000LL <=
|
||
|
vc->stream->time_base.den)
|
||
|
packet->duration = FFMAX(1, av_rescale_q(1,
|
||
|
vc->stream->codec->time_base, vc->stream->time_base));
|
||
|
}
|
||
|
|
||
|
if (encode_lavc_write_frame(vo->encode_lavc_ctx, packet) < 0) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: error writing\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
vc->have_first_packet = 1;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int encode_video(struct vo *vo, AVFrame *frame, AVPacket *packet)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
if (encode_lavc_oformat_flags(vo->encode_lavc_ctx) & AVFMT_RAWPICTURE) {
|
||
|
if (!frame)
|
||
|
return 0;
|
||
|
memcpy(vc->buffer, frame, sizeof(AVPicture));
|
||
|
mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f\n",
|
||
|
frame->pts * (double) vc->stream->codec->time_base.num /
|
||
|
(double) vc->stream->codec->time_base.den);
|
||
|
packet->size = sizeof(AVPicture);
|
||
|
return packet->size;
|
||
|
} else {
|
||
|
int got_packet = 0;
|
||
|
int status = avcodec_encode_video2(vc->stream->codec, packet,
|
||
|
frame, &got_packet);
|
||
|
int size = (status < 0) ? status : got_packet ? packet->size : 0;
|
||
|
|
||
|
if (frame)
|
||
|
mp_msg(MSGT_ENCODE, MSGL_DBG2, "vo-lavc: got pts %f; out size: %d\n",
|
||
|
frame->pts * (double) vc->stream->codec->time_base.num /
|
||
|
(double) vc->stream->codec->time_base.den, size);
|
||
|
|
||
|
encode_lavc_write_stats(vo->encode_lavc_ctx, vc->stream);
|
||
|
return size;
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void add_osd_to_lastimg_draw_func(void *ctx, int x0,int y0, int w,int h,unsigned char* src, unsigned char *srca, int stride){
|
||
|
struct priv *vc = ctx;
|
||
|
unsigned char* dst;
|
||
|
if(w<=0 || h<=0) return; // nothing to do...
|
||
|
// printf("OSD redraw: %d;%d %dx%d \n",x0,y0,w,h);
|
||
|
dst=vc->lastimg->planes[0]+
|
||
|
vc->lastimg->stride[0]*y0+
|
||
|
(vc->lastimg->bpp>>3)*x0;
|
||
|
switch(vc->lastimg->imgfmt){
|
||
|
case IMGFMT_BGR12:
|
||
|
case IMGFMT_RGB12:
|
||
|
vo_draw_alpha_rgb12(w, h, src, srca, stride, dst, vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_BGR15:
|
||
|
case IMGFMT_RGB15:
|
||
|
vo_draw_alpha_rgb15(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_BGR16:
|
||
|
case IMGFMT_RGB16:
|
||
|
vo_draw_alpha_rgb16(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_BGR24:
|
||
|
case IMGFMT_RGB24:
|
||
|
vo_draw_alpha_rgb24(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_BGR32:
|
||
|
case IMGFMT_RGB32:
|
||
|
vo_draw_alpha_rgb32(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_YV12:
|
||
|
case IMGFMT_I420:
|
||
|
case IMGFMT_IYUV:
|
||
|
case IMGFMT_YVU9:
|
||
|
case IMGFMT_IF09:
|
||
|
case IMGFMT_Y800:
|
||
|
case IMGFMT_Y8:
|
||
|
vo_draw_alpha_yv12(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_YUY2:
|
||
|
vo_draw_alpha_yuy2(w,h,src,srca,stride,dst,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
case IMGFMT_UYVY:
|
||
|
vo_draw_alpha_yuy2(w,h,src,srca,stride,dst+1,vc->lastimg->stride[0]);
|
||
|
break;
|
||
|
default:
|
||
|
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: tried to draw OSD on an usnupported pixel format\n");
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void add_osd_to_lastimg(struct vo *vo)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
if(vc->osd) {
|
||
|
osd_draw_text(vc->osd, vc->lastimg->w, vc->lastimg->h, add_osd_to_lastimg_draw_func, vc);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static void draw_image(struct vo *vo, mp_image_t *mpi, double pts)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
struct encode_lavc_context *ectx = vo->encode_lavc_ctx;
|
||
|
int i, size;
|
||
|
AVFrame *frame;
|
||
|
AVCodecContext *avc;
|
||
|
int64_t frameipts;
|
||
|
double nextpts;
|
||
|
|
||
|
if (!vc)
|
||
|
return;
|
||
|
if (!encode_lavc_start(ectx)) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: skipped initial video frame (probably because audio is not there yet)\n");
|
||
|
return;
|
||
|
}
|
||
|
|
||
|
avc = vc->stream->codec;
|
||
|
|
||
|
if (vc->worst_time_base.den == 0) {
|
||
|
//if (avc->time_base.num / avc->time_base.den >= vc->stream->time_base.num / vc->stream->time_base.den)
|
||
|
if (avc->time_base.num * (double) vc->stream->time_base.den >=
|
||
|
vc->stream->time_base.num * (double) avc->time_base.den) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_V, "vo-lavc: NOTE: using codec time base "
|
||
|
"(%d/%d) for frame dropping; the stream base (%d/%d) is "
|
||
|
"not worse.\n", (int)avc->time_base.num,
|
||
|
(int)avc->time_base.den, (int)vc->stream->time_base.num,
|
||
|
(int)vc->stream->time_base.den);
|
||
|
vc->worst_time_base = avc->time_base;
|
||
|
vc->worst_time_base_is_stream = 0;
|
||
|
} else {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: NOTE: not using codec time "
|
||
|
"base (%d/%d) for frame dropping; the stream base (%d/%d) "
|
||
|
"is worse.\n", (int)avc->time_base.num,
|
||
|
(int)avc->time_base.den, (int)vc->stream->time_base.num,
|
||
|
(int)vc->stream->time_base.den);
|
||
|
vc->worst_time_base = vc->stream->time_base;
|
||
|
vc->worst_time_base_is_stream = 1;
|
||
|
}
|
||
|
|
||
|
// NOTE: we use the following "axiom" of av_rescale_q:
|
||
|
// if time base A is worse than time base B, then
|
||
|
// av_rescale_q(av_rescale_q(x, A, B), B, A) == x
|
||
|
// this can be proven as long as av_rescale_q rounds to nearest, which
|
||
|
// it currently does
|
||
|
|
||
|
// av_rescale_q(x, A, B) * B = "round x*A to nearest multiple of B"
|
||
|
// and:
|
||
|
// av_rescale_q(av_rescale_q(x, A, B), B, A) * A
|
||
|
// == "round av_rescale_q(x, A, B)*B to nearest multiple of A"
|
||
|
// == "round 'round x*A to nearest multiple of B' to nearest multiple of A"
|
||
|
//
|
||
|
// assume this fails. Then there is a value of x*A, for which the
|
||
|
// nearest multiple of B is outside the range [(x-0.5)*A, (x+0.5)*A[.
|
||
|
// Absurd, as this range MUST contain at least one multiple of B.
|
||
|
}
|
||
|
|
||
|
double timeunit = (double)vc->worst_time_base.num / vc->worst_time_base.den;
|
||
|
|
||
|
// fix the discontinuity pts offset
|
||
|
if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
|
||
|
nextpts = pts;
|
||
|
ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
|
||
|
}
|
||
|
|
||
|
// set next allowed output pts value
|
||
|
nextpts = pts + ectx->discontinuity_pts_offset + timeunit;
|
||
|
if (nextpts > ectx->next_in_pts)
|
||
|
ectx->next_in_pts = nextpts;
|
||
|
|
||
|
// vc->lastipts is MP_NOPTS_VALUE, or the start time of vc->lastframe
|
||
|
if (mpi) {
|
||
|
if (pts == MP_NOPTS_VALUE) {
|
||
|
// NOTE: this even applies to ectx->options->copyts!
|
||
|
if (vc->lastipts == MP_NOPTS_VALUE)
|
||
|
frameipts = 0;
|
||
|
else
|
||
|
frameipts = vc->lastipts + 1;
|
||
|
|
||
|
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: pts was missing, using %d - "
|
||
|
"consider using -ofps or -vf fixpts\n", (int) frameipts);
|
||
|
|
||
|
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
|
||
|
ectx->last_video_in_pts += timeunit;
|
||
|
|
||
|
// calculate backwards to set vc->lastpts matchingly
|
||
|
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
|
||
|
} else {
|
||
|
double outpts;
|
||
|
if (ectx->options->rawts)
|
||
|
outpts = pts;
|
||
|
else if (ectx->options->copyts)
|
||
|
outpts = pts + ectx->discontinuity_pts_offset;
|
||
|
else {
|
||
|
double duration = 0;
|
||
|
if (ectx->last_video_in_pts != MP_NOPTS_VALUE)
|
||
|
duration = pts - ectx->last_video_in_pts;
|
||
|
if (duration < 0)
|
||
|
duration = timeunit; // XXX warn about discontinuity?
|
||
|
outpts = vc->lastpts + duration;
|
||
|
if (ectx->audio_pts_offset != MP_NOPTS_VALUE) {
|
||
|
double adj = outpts - pts - ectx->audio_pts_offset;
|
||
|
adj = FFMIN(adj, duration * 0.1);
|
||
|
adj = FFMAX(adj, -duration * 0.1);
|
||
|
outpts -= adj;
|
||
|
}
|
||
|
}
|
||
|
vc->lastpts = outpts;
|
||
|
ectx->last_video_in_pts = pts;
|
||
|
frameipts = floor((outpts + encode_lavc_getoffset(ectx, vc->stream))
|
||
|
/ timeunit + 0.5);
|
||
|
}
|
||
|
} else {
|
||
|
if (vc->lastipts == MP_NOPTS_VALUE)
|
||
|
frameipts = 0;
|
||
|
else
|
||
|
frameipts = vc->lastipts + 1;
|
||
|
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
|
||
|
}
|
||
|
|
||
|
// never-drop mode
|
||
|
if (ectx->options->neverdrop && frameipts <= vc->lastipts) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: -oneverdrop increased pts by %d\n",
|
||
|
(int) (vc->lastipts - frameipts + 1));
|
||
|
frameipts = vc->lastipts + 1;
|
||
|
vc->lastpts = frameipts * timeunit - encode_lavc_getoffset(ectx, vc->stream);
|
||
|
}
|
||
|
|
||
|
if (vc->lastipts != MP_NOPTS_VALUE) {
|
||
|
frame = avcodec_alloc_frame();
|
||
|
|
||
|
// we have a valid image in lastimg
|
||
|
while (vc->lastipts < frameipts) {
|
||
|
int64_t thisduration = vc->harddup ? 1 : (frameipts - vc->lastipts);
|
||
|
AVPacket packet;
|
||
|
|
||
|
avcodec_get_frame_defaults(frame);
|
||
|
|
||
|
// this is a nop, unless the worst time base is the STREAM time base
|
||
|
frame->pts = av_rescale_q(vc->lastipts, vc->worst_time_base,
|
||
|
avc->time_base);
|
||
|
|
||
|
for (i = 0; i < 4; i++) {
|
||
|
frame->data[i] = vc->lastimg->planes[i];
|
||
|
frame->linesize[i] = vc->lastimg->stride[i];
|
||
|
}
|
||
|
frame->quality = avc->global_quality;
|
||
|
|
||
|
av_init_packet(&packet);
|
||
|
packet.data = vc->buffer;
|
||
|
packet.size = vc->buffer_size;
|
||
|
size = encode_video(vo, frame, &packet);
|
||
|
write_packet(vo, size, &packet);
|
||
|
|
||
|
vc->lastipts += thisduration;
|
||
|
++vc->lastdisplaycount;
|
||
|
}
|
||
|
|
||
|
av_free(frame);
|
||
|
}
|
||
|
|
||
|
if (!mpi) {
|
||
|
// finish encoding
|
||
|
do {
|
||
|
AVPacket packet;
|
||
|
av_init_packet(&packet);
|
||
|
packet.data = vc->buffer;
|
||
|
packet.size = vc->buffer_size;
|
||
|
size = encode_video(vo, NULL, &packet);
|
||
|
write_packet(vo, size, &packet);
|
||
|
} while (size > 0);
|
||
|
} else {
|
||
|
if (frameipts >= vc->lastframeipts) {
|
||
|
if (vc->lastframeipts != MP_NOPTS_VALUE && vc->lastdisplaycount != 1)
|
||
|
mp_msg(MSGT_ENCODE, MSGL_INFO,
|
||
|
"vo-lavc: Frame at pts %d got displayed %d times\n",
|
||
|
(int) vc->lastframeipts, vc->lastdisplaycount);
|
||
|
copy_mpi(vc->lastimg, mpi);
|
||
|
add_osd_to_lastimg(vo);
|
||
|
|
||
|
// palette hack
|
||
|
if (vc->lastimg->imgfmt == IMGFMT_RGB8 ||
|
||
|
vc->lastimg->imgfmt == IMGFMT_BGR8)
|
||
|
memcpy(vc->lastimg->planes[1], mpi->planes[1], 1024);
|
||
|
|
||
|
vc->lastframeipts = vc->lastipts = frameipts;
|
||
|
if (ectx->options->rawts && vc->lastipts < 0) {
|
||
|
mp_msg(MSGT_ENCODE, MSGL_ERR, "vo-lavc: why does this happen? DEBUG THIS! vc->lastipts = %lld\n", (long long) vc->lastipts);
|
||
|
vc->lastipts = -1;
|
||
|
}
|
||
|
vc->lastdisplaycount = 0;
|
||
|
} else
|
||
|
mp_msg(MSGT_ENCODE, MSGL_INFO, "vo-lavc: Frame at pts %d got dropped "
|
||
|
"entirely because pts went backwards\n", (int) frameipts);
|
||
|
}
|
||
|
}
|
||
|
|
||
|
static int control(struct vo *vo, uint32_t request, void *data)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
switch (request) {
|
||
|
case VOCTRL_QUERY_FORMAT:
|
||
|
return query_format(vo, *((uint32_t *)data));
|
||
|
case VOCTRL_DRAW_IMAGE:
|
||
|
draw_image(vo, (mp_image_t *)data, vo->next_pts);
|
||
|
return 0;
|
||
|
case VOCTRL_SET_YUV_COLORSPACE:
|
||
|
vc->colorspace = *(struct mp_csp_details *)data;
|
||
|
if (vc->stream) {
|
||
|
encode_lavc_set_csp(vo->encode_lavc_ctx, vc->stream, vc->colorspace.format);
|
||
|
encode_lavc_set_csp_levels(vo->encode_lavc_ctx, vc->stream, vc->colorspace.levels_out);
|
||
|
vc->colorspace.format = encode_lavc_get_csp(vo->encode_lavc_ctx, vc->stream);
|
||
|
vc->colorspace.levels_out = encode_lavc_get_csp_levels(vo->encode_lavc_ctx, vc->stream);
|
||
|
}
|
||
|
return 1;
|
||
|
case VOCTRL_GET_YUV_COLORSPACE:
|
||
|
*(struct mp_csp_details *)data = vc->colorspace;
|
||
|
return 1;
|
||
|
}
|
||
|
return VO_NOTIMPL;
|
||
|
}
|
||
|
|
||
|
static void draw_osd(struct vo *vo, struct osd_state *osd)
|
||
|
{
|
||
|
struct priv *vc = vo->priv;
|
||
|
vc->osd = osd;
|
||
|
if(vc->lastimg)
|
||
|
osd_update(vc->osd, vc->lastimg->w, vc->lastimg->h);
|
||
|
}
|
||
|
|
||
|
static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
static void check_events(struct vo *vo)
|
||
|
{
|
||
|
}
|
||
|
|
||
|
const struct vo_driver video_out_lavc = {
|
||
|
.is_new = true,
|
||
|
.buffer_frames = false,
|
||
|
.info = &(const struct vo_info_s){
|
||
|
"video encoding using libavcodec",
|
||
|
"lavc",
|
||
|
"Nicolas George <george@nsup.org>, Rudolf Polzer <divVerent@xonotic.org>",
|
||
|
""
|
||
|
},
|
||
|
.preinit = preinit,
|
||
|
.config = config,
|
||
|
.control = control,
|
||
|
.uninit = uninit,
|
||
|
.check_events = check_events,
|
||
|
.draw_osd = draw_osd,
|
||
|
.flip_page_timed = flip_page_timed,
|
||
|
};
|
||
|
|
||
|
// vim: sw=4 ts=4 et
|