summaryrefslogtreecommitdiffstats
path: root/video/out/vo_lavc.c
diff options
context:
space:
mode:
authorwm4 <wm4@nowhere>2012-11-05 17:02:04 +0100
committerwm4 <wm4@nowhere>2012-11-12 20:06:14 +0100
commitd4bdd0473d6f43132257c9fb3848d829755167a3 (patch)
tree8021c2f7da1841393c8c832105e20cd527826d6c /video/out/vo_lavc.c
parentbd48deba77bd5582c5829d6fe73a7d2571088aba (diff)
downloadmpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.bz2
mpv-d4bdd0473d6f43132257c9fb3848d829755167a3.tar.xz
Rename directories, move files (step 1 of 2) (does not compile)
Tis drops the silly lib prefixes, and attempts to organize the tree in a more logical way. Make the top-level directory less cluttered as well. Renames the following directories: libaf -> audio/filter libao2 -> audio/out libvo -> video/out libmpdemux -> demux Split libmpcodecs: vf* -> video/filter vd*, dec_video.* -> video/decode mp_image*, img_format*, ... -> video/ ad*, dec_audio.* -> audio/decode libaf/format.* is moved to audio/ - this is similar to how mp_image.* is located in video/. Move most top-level .c/.h files to core. (talloc.c/.h is left on top- level, because it's external.) Park some of the more annoying files in compat/. Some of these are relicts from the time mplayer used ffmpeg internals. sub/ is not split, because it's too much of a mess (subtitle code is mixed with OSD display and rendering). Maybe the organization of core is not ideal: it mixes playback core (like mplayer.c) and utility helpers (like bstr.c/h). Should the need arise, the playback core will be moved somewhere else, while core contains all helper and common code.
Diffstat (limited to 'video/out/vo_lavc.c')
-rw-r--r--video/out/vo_lavc.c553
1 files changed, 553 insertions, 0 deletions
diff --git a/video/out/vo_lavc.c b/video/out/vo_lavc.c
new file mode 100644
index 0000000000..b86cd76509
--- /dev/null
+++ b/video/out/vo_lavc.c
@@ -0,0 +1,553 @@
+/*
+ * 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 "sub/dec_sub.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;
+ double expected_next_pts;
+ mp_image_t *lastimg;
+ int lastimg_wants_osd;
+ int lastdisplaycount;
+
+ AVRational worst_time_base;
+ int worst_time_base_is_stream;
+
+ 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);
+
+ if (encode_lavc_open_codec(vo->encode_lavc_ctx, vc->stream) < 0)
+ goto error;
+
+ 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);
+
+ 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;
+
+ if (!encode_lavc_supports_pixfmt(vo->encode_lavc_ctx, pix_fmt))
+ return 0;
+
+ return
+ VFCAP_CSP_SUPPORTED |
+ // we can do it
+ VFCAP_CSP_SUPPORTED_BY_HW |
+ // we don't convert colorspaces here
+ VFCAP_OSD |
+ // we have OSD
+ VOCAP_NOSLICES;
+ // we don't use slices
+}
+
+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 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;
+ }
+ if (pts == MP_NOPTS_VALUE) {
+ if (mpi)
+ mp_msg(MSGT_ENCODE, MSGL_WARN, "vo-lavc: frame without pts, please report; synthesizing pts instead\n");
+ pts = vc->expected_next_pts;
+ }
+
+ 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;
+
+ double outpts;
+ if (ectx->options->rawts)
+ outpts = pts;
+ else if (ectx->options->copyts) {
+ // fix the discontinuity pts offset
+ nextpts = pts;
+ if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) {
+ ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
+ }
+ else if (fabs(nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts) > 30) {
+ mp_msg(MSGT_ENCODE, MSGL_WARN,
+ "vo-lavc: detected an unexpected discontinuity (pts jumped by "
+ "%f seconds)\n",
+ nextpts + ectx->discontinuity_pts_offset - ectx->next_in_pts);
+ ectx->discontinuity_pts_offset = ectx->next_in_pts - nextpts;
+ }
+
+ outpts = pts + ectx->discontinuity_pts_offset;
+ }
+ else {
+ // adjust pts by knowledge of audio pts vs audio playback time
+ 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);
+
+ // calculate expected pts of next video frame
+ vc->expected_next_pts = pts + timeunit;
+
+ if (!ectx->options->rawts && ectx->options->copyts) {
+ // set next allowed output pts value
+ nextpts = vc->expected_next_pts + ectx->discontinuity_pts_offset;
+ if (nextpts > ectx->next_in_pts)
+ ectx->next_in_pts = nextpts;
+ }
+
+ // 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;
+ }
+
+ avcodec_free_frame(&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);
+ vc->lastimg_wants_osd = true;
+
+ // 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);
+ vc->lastimg_wants_osd = false;
+ }
+ }
+}
+
+static void flip_page_timed(struct vo *vo, unsigned int pts_us, int duration)
+{
+}
+
+static void check_events(struct vo *vo)
+{
+}
+
+static void draw_osd(struct vo *vo, struct osd_state *osd)
+{
+ struct priv *vc = vo->priv;
+
+ if (vc->lastimg && vc->lastimg_wants_osd) {
+ struct aspect_data asp = vo->aspdat;
+ double sar = (double)asp.orgw / asp.orgh;
+ double dar = (double)asp.prew / asp.preh;
+
+ struct mp_osd_res dim = {
+ .w = asp.orgw,
+ .h = asp.orgh,
+ .display_par = sar / dar,
+ .video_par = dar / sar,
+ };
+
+ mp_image_set_colorspace_details(vc->lastimg, &vc->colorspace);
+
+ osd_draw_on_image(osd, dim, osd->vo_pts, OSD_DRAW_SUB_ONLY, vc->lastimg);
+ }
+}
+
+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;
+}
+
+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 tw=80