diff options
author | Rudolf Polzer <divverent@xonotic.org> | 2012-09-14 17:51:26 +0200 |
---|---|---|
committer | wm4 <wm4@nowhere> | 2012-09-18 21:08:20 +0200 |
commit | f5b8b6ac126d8cef3860db16d3db8e72507a2258 (patch) | |
tree | c86a6160cee076d3a632e4d3247e566e8c064390 /libvo | |
parent | 5617bf483e563aae22100c0ca1d8182f71d4f82d (diff) |
encode: video encoding now supported using mencoder-like options
Diffstat (limited to 'libvo')
-rw-r--r-- | libvo/csputils.c | 47 | ||||
-rw-r--r-- | libvo/csputils.h | 4 | ||||
-rw-r--r-- | libvo/video_out.c | 8 | ||||
-rw-r--r-- | libvo/video_out.h | 4 | ||||
-rw-r--r-- | libvo/vo_lavc.c | 590 |
5 files changed, 631 insertions, 22 deletions
diff --git a/libvo/csputils.c b/libvo/csputils.c index d6aed97864..ed74b9ae74 100644 --- a/libvo/csputils.c +++ b/libvo/csputils.c @@ -50,32 +50,39 @@ char * const mp_csp_equalizer_names[MP_CSP_EQ_COUNT] = { enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace) { switch (colorspace) { - case AVCOL_SPC_BT709: - return MP_CSP_BT_709; - break; - case AVCOL_SPC_BT470BG: - case AVCOL_SPC_SMPTE170M: - return MP_CSP_BT_601; - break; - case AVCOL_SPC_SMPTE240M: - return MP_CSP_SMPTE_240M; - break; - default: - return MP_CSP_AUTO; + case AVCOL_SPC_BT709: return MP_CSP_BT_709; + case AVCOL_SPC_BT470BG: return MP_CSP_BT_601; + case AVCOL_SPC_SMPTE170M: return MP_CSP_BT_601; + case AVCOL_SPC_SMPTE240M: return MP_CSP_SMPTE_240M; + default: return MP_CSP_AUTO; } } enum mp_csp_levels avcol_range_to_mp_csp_levels(enum AVColorRange range) { switch (range) { - case AVCOL_RANGE_MPEG: - return MP_CSP_LEVELS_TV; - break; - case AVCOL_RANGE_JPEG: - return MP_CSP_LEVELS_PC; - break; - default: - return MP_CSP_LEVELS_AUTO; + case AVCOL_RANGE_MPEG: return MP_CSP_LEVELS_TV; + case AVCOL_RANGE_JPEG: return MP_CSP_LEVELS_PC; + default: return MP_CSP_LEVELS_AUTO; + } +} + +enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp colorspace) +{ + switch (colorspace) { + case MP_CSP_BT_709: return AVCOL_SPC_BT709; + case MP_CSP_BT_601: return AVCOL_SPC_BT470BG; + case MP_CSP_SMPTE_240M: return AVCOL_SPC_SMPTE240M; + default: return AVCOL_SPC_RGB; + } +} + +enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range) +{ + switch (range) { + case MP_CSP_LEVELS_TV: return AVCOL_RANGE_MPEG; + case MP_CSP_LEVELS_PC: return AVCOL_RANGE_JPEG; + default: return AVCOL_RANGE_UNSPECIFIED; } } diff --git a/libvo/csputils.h b/libvo/csputils.h index da826e84da..4ec0f14ba2 100644 --- a/libvo/csputils.h +++ b/libvo/csputils.h @@ -116,6 +116,10 @@ enum mp_csp avcol_spc_to_mp_csp(enum AVColorSpace colorspace); enum mp_csp_levels avcol_range_to_mp_csp_levels(enum AVColorRange range); +enum AVColorSpace mp_csp_to_avcol_spc(enum mp_csp colorspace); + +enum AVColorRange mp_csp_levels_to_avcol_range(enum mp_csp_levels range); + enum mp_csp mp_csp_guess_colorspace(int width, int height); void mp_gen_gamma_map(unsigned char *map, int size, float gamma); diff --git a/libvo/video_out.c b/libvo/video_out.c index 64b2a605c5..2be311f87c 100644 --- a/libvo/video_out.c +++ b/libvo/video_out.c @@ -78,6 +78,7 @@ extern struct vo_driver video_out_gl; extern struct vo_driver video_out_gl3; extern struct vo_driver video_out_null; extern struct vo_driver video_out_image; +extern struct vo_driver video_out_lavc; extern struct vo_driver video_out_caca; extern struct vo_driver video_out_direct3d; extern struct vo_driver video_out_direct3d_shaders; @@ -116,6 +117,9 @@ const struct vo_driver *video_out_drivers[] = &video_out_null, // should not be auto-selected &video_out_image, +#ifdef CONFIG_ENCODING + &video_out_lavc, +#endif #ifdef CONFIG_X11 #ifdef CONFIG_GL &video_out_gl_nosw, @@ -283,7 +287,8 @@ void list_video_out(void) struct vo *init_best_video_out(struct MPOpts *opts, struct mp_fifo *key_fifo, - struct input_ctx *input_ctx) + struct input_ctx *input_ctx, + struct encode_lavc_context *encode_lavc_ctx) { char **vo_list = opts->video_driver_list; int i; @@ -291,6 +296,7 @@ struct vo *init_best_video_out(struct MPOpts *opts, struct vo initial_values = { .opts = opts, .key_fifo = key_fifo, + .encode_lavc_ctx = encode_lavc_ctx, .input_ctx = input_ctx, .event_fd = -1, .registered_fd = -1, diff --git a/libvo/video_out.h b/libvo/video_out.h index dd2a1f79bd..2cd314f281 100644 --- a/libvo/video_out.h +++ b/libvo/video_out.h @@ -249,6 +249,7 @@ struct vo { struct vo_x11_state *x11; struct vo_w32_state *w32; struct mp_fifo *key_fifo; + struct encode_lavc_context *encode_lavc_ctx; struct input_ctx *input_ctx; int event_fd; // check_events() should be called when this has input int registered_fd; // set to event_fd when registered in input system @@ -278,7 +279,8 @@ struct vo { struct vo *init_best_video_out(struct MPOpts *opts, struct mp_fifo *key_fifo, - struct input_ctx *input_ctx); + struct input_ctx *input_ctx, + struct encode_lavc_context *encode_lavc_ctx); int vo_config(struct vo *vo, uint32_t width, uint32_t height, uint32_t d_width, uint32_t d_height, uint32_t flags, uint32_t format); diff --git a/libvo/vo_lavc.c b/libvo/vo_lavc.c new file mode 100644 index 0000000000..4a1af15eb0 --- /dev/null +++ b/libvo/vo_lavc.c @@ -0,0 +1,590 @@ +/* + * 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 |