/src/mpv/video/out/vo_lavc.c
Line | Count | Source |
1 | | /* |
2 | | * video encoding using libavformat |
3 | | * |
4 | | * Copyright (C) 2010 Nicolas George <george@nsup.org> |
5 | | * Copyright (C) 2011-2012 Rudolf Polzer <divVerent@xonotic.org> |
6 | | * |
7 | | * This file is part of mpv. |
8 | | * |
9 | | * mpv is free software; you can redistribute it and/or |
10 | | * modify it under the terms of the GNU Lesser General Public |
11 | | * License as published by the Free Software Foundation; either |
12 | | * version 2.1 of the License, or (at your option) any later version. |
13 | | * |
14 | | * mpv is distributed in the hope that it will be useful, |
15 | | * but WITHOUT ANY WARRANTY; without even the implied warranty of |
16 | | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the |
17 | | * GNU Lesser General Public License for more details. |
18 | | * |
19 | | * You should have received a copy of the GNU Lesser General Public |
20 | | * License along with mpv. If not, see <http://www.gnu.org/licenses/>. |
21 | | */ |
22 | | |
23 | | #include <stdio.h> |
24 | | #include <stdlib.h> |
25 | | |
26 | | #include <libplacebo/utils/libav.h> |
27 | | |
28 | | #include "common/common.h" |
29 | | #include "options/options.h" |
30 | | #include "misc/lavc_compat.h" |
31 | | #include "video/fmt-conversion.h" |
32 | | #include "video/mp_image.h" |
33 | | #include "mpv_talloc.h" |
34 | | #include "vo.h" |
35 | | |
36 | | #include "common/encode_lavc.h" |
37 | | |
38 | | #include "sub/osd.h" |
39 | | |
40 | | struct priv { |
41 | | struct encoder_context *enc; |
42 | | |
43 | | bool shutdown; |
44 | | }; |
45 | | |
46 | | static int preinit(struct vo *vo) |
47 | 0 | { |
48 | 0 | struct priv *vc = vo->priv; |
49 | 0 | vc->enc = encoder_context_alloc(vo->encode_lavc_ctx, STREAM_VIDEO, vo->log); |
50 | 0 | if (!vc->enc) |
51 | 0 | return -1; |
52 | 0 | talloc_steal(vc, vc->enc); |
53 | 0 | return 0; |
54 | 0 | } |
55 | | |
56 | | static void uninit(struct vo *vo) |
57 | 0 | { |
58 | 0 | struct priv *vc = vo->priv; |
59 | 0 | struct encoder_context *enc = vc->enc; |
60 | |
|
61 | 0 | if (!vc->shutdown) |
62 | 0 | encoder_encode(enc, NULL); // finish encoding |
63 | 0 | } |
64 | | |
65 | | static int reconfig2(struct vo *vo, struct mp_image *img) |
66 | 0 | { |
67 | 0 | struct priv *vc = vo->priv; |
68 | 0 | AVCodecContext *encoder = vc->enc->encoder; |
69 | |
|
70 | 0 | struct mp_image_params *params = &img->params; |
71 | 0 | enum AVPixelFormat pix_fmt = imgfmt2pixfmt(params->imgfmt); |
72 | 0 | AVRational aspect = {params->p_w, params->p_h}; |
73 | 0 | int width = params->w; |
74 | 0 | int height = params->h; |
75 | |
|
76 | 0 | if (vc->shutdown) |
77 | 0 | return -1; |
78 | | |
79 | 0 | if (avcodec_is_open(encoder)) { |
80 | 0 | if (width == encoder->width && height == encoder->height && |
81 | 0 | pix_fmt == encoder->pix_fmt) |
82 | 0 | { |
83 | | // consider these changes not critical |
84 | 0 | MP_ERR(vo, "Ignoring mid-stream parameter changes!\n"); |
85 | 0 | return 0; |
86 | 0 | } |
87 | | |
88 | | /* FIXME Is it possible with raw video? */ |
89 | 0 | MP_ERR(vo, "resolution changes not supported.\n"); |
90 | 0 | goto error; |
91 | 0 | } |
92 | | |
93 | | // When we get here, this must be the first call to reconfigure(). Thus, we |
94 | | // can rely on no existing data in vc having been allocated yet. |
95 | | // Reason: |
96 | | // - Second calls after reconfigure() already failed once fail (due to the |
97 | | // vc->shutdown check above). |
98 | | // - Second calls after reconfigure() already succeeded once return early |
99 | | // (due to the avcodec_is_open() check above). |
100 | | |
101 | 0 | if (pix_fmt == AV_PIX_FMT_NONE) { |
102 | 0 | MP_FATAL(vo, "Format %s not supported by lavc.\n", |
103 | 0 | mp_imgfmt_to_name(params->imgfmt)); |
104 | 0 | goto error; |
105 | 0 | } |
106 | | |
107 | 0 | encoder->sample_aspect_ratio = aspect; |
108 | 0 | encoder->width = width; |
109 | 0 | encoder->height = height; |
110 | 0 | encoder->pix_fmt = pix_fmt; |
111 | 0 | encoder->colorspace = pl_system_to_av(params->repr.sys); |
112 | 0 | encoder->color_range = pl_levels_to_av(params->repr.levels); |
113 | |
|
114 | 0 | AVRational tb; |
115 | | |
116 | | // we want to handle: |
117 | | // 1/25 |
118 | | // 1001/24000 |
119 | | // 1001/30000 |
120 | | // for this we would need 120000fps... |
121 | | // however, mpeg-4 only allows 16bit values |
122 | | // so let's take 1001/30000 out |
123 | 0 | tb.num = 24000; |
124 | 0 | tb.den = 1; |
125 | |
|
126 | 0 | const AVRational *rates; |
127 | 0 | int ret = mp_avcodec_get_supported_config(encoder, NULL, |
128 | 0 | AV_CODEC_CONFIG_FRAME_RATE, |
129 | 0 | (const void **)&rates); |
130 | 0 | if (ret >= 0 && rates && rates[0].den) |
131 | 0 | tb = rates[av_find_nearest_q_idx(tb, rates)]; |
132 | |
|
133 | 0 | encoder->time_base = av_inv_q(tb); |
134 | | |
135 | | // Used for rate control, level selection, etc. |
136 | | // Usually it's not too catastrophic if this isn't exactly correct, |
137 | | // as long as it's not off by orders of magnitude. |
138 | | // If we don't set anything, encoders will use the time base, |
139 | | // and 24000 is so high that the output can end up extremely screwy (see #11215), |
140 | | // so we default to 240 if we don't have a real value. |
141 | 0 | if (img->nominal_fps > 0) |
142 | 0 | encoder->framerate = av_d2q(img->nominal_fps, img->nominal_fps * 1001 + 2); // Hopefully give exact results for NTSC rates |
143 | 0 | else |
144 | 0 | encoder->framerate = (AVRational){ 240, 1 }; |
145 | |
|
146 | 0 | if (!encoder_init_codec_and_muxer(vc->enc)) |
147 | 0 | goto error; |
148 | | |
149 | 0 | return 0; |
150 | | |
151 | 0 | error: |
152 | 0 | vc->shutdown = true; |
153 | 0 | return -1; |
154 | 0 | } |
155 | | |
156 | | static int query_format(struct vo *vo, int format) |
157 | 0 | { |
158 | 0 | struct priv *vc = vo->priv; |
159 | |
|
160 | 0 | enum AVPixelFormat pix_fmt = imgfmt2pixfmt(format); |
161 | 0 | const enum AVPixelFormat *p; |
162 | 0 | int ret = mp_avcodec_get_supported_config(vc->enc->encoder, NULL, |
163 | 0 | AV_CODEC_CONFIG_PIX_FORMAT, |
164 | 0 | (const void **)&p); |
165 | |
|
166 | 0 | if (ret >= 0 && !p) |
167 | 0 | return 1; |
168 | | |
169 | 0 | while (ret >= 0 && p && *p != AV_PIX_FMT_NONE) { |
170 | 0 | if (*p == pix_fmt) |
171 | 0 | return 1; |
172 | 0 | p++; |
173 | 0 | } |
174 | | |
175 | 0 | return 0; |
176 | 0 | } |
177 | | |
178 | | static bool draw_frame(struct vo *vo, struct vo_frame *voframe) |
179 | 0 | { |
180 | 0 | struct priv *vc = vo->priv; |
181 | 0 | struct encoder_context *enc = vc->enc; |
182 | 0 | struct encode_lavc_context *ectx = enc->encode_lavc_ctx; |
183 | 0 | AVCodecContext *avc = enc->encoder; |
184 | |
|
185 | 0 | if (voframe->redraw || voframe->repeat || voframe->num_frames < 1) |
186 | 0 | goto done; |
187 | | |
188 | 0 | struct mp_image *mpi = voframe->frames[0]; |
189 | |
|
190 | 0 | struct mp_osd_res dim = osd_res_from_image_params(vo->params); |
191 | 0 | osd_draw_on_image(vo->osd, dim, mpi->pts, OSD_DRAW_SUB_ONLY, mpi); |
192 | |
|
193 | 0 | if (vc->shutdown) |
194 | 0 | goto done; |
195 | | |
196 | | // Lock for shared timestamp fields. |
197 | 0 | mp_mutex_lock(&ectx->lock); |
198 | |
|
199 | 0 | double pts = mpi->pts; |
200 | 0 | double outpts = pts; |
201 | 0 | if (!enc->options->rawts) { |
202 | | // fix the discontinuity pts offset |
203 | 0 | if (ectx->discontinuity_pts_offset == MP_NOPTS_VALUE) { |
204 | 0 | ectx->discontinuity_pts_offset = ectx->next_in_pts - pts; |
205 | 0 | } else if (fabs(pts + ectx->discontinuity_pts_offset - |
206 | 0 | ectx->next_in_pts) > 30) |
207 | 0 | { |
208 | 0 | MP_WARN(vo, "detected an unexpected discontinuity (pts jumped by " |
209 | 0 | "%f seconds)\n", |
210 | 0 | pts + ectx->discontinuity_pts_offset - ectx->next_in_pts); |
211 | 0 | ectx->discontinuity_pts_offset = ectx->next_in_pts - pts; |
212 | 0 | } |
213 | |
|
214 | 0 | outpts = pts + ectx->discontinuity_pts_offset; |
215 | 0 | } |
216 | |
|
217 | 0 | if (!enc->options->rawts) { |
218 | | // calculate expected pts of next video frame |
219 | 0 | double timeunit = av_q2d(avc->time_base); |
220 | 0 | double expected_next_pts = pts + timeunit; |
221 | | // set next allowed output pts value |
222 | 0 | double nextpts = expected_next_pts + ectx->discontinuity_pts_offset; |
223 | 0 | if (nextpts > ectx->next_in_pts) |
224 | 0 | ectx->next_in_pts = nextpts; |
225 | 0 | } |
226 | |
|
227 | 0 | mp_mutex_unlock(&ectx->lock); |
228 | |
|
229 | 0 | AVFrame *frame = mp_image_to_av_frame(mpi); |
230 | 0 | MP_HANDLE_OOM(frame); |
231 | | |
232 | 0 | frame->pts = rint(outpts * av_q2d(av_inv_q(avc->time_base))); |
233 | 0 | frame->pict_type = 0; // keep this at unknown/undefined |
234 | 0 | frame->quality = avc->global_quality; |
235 | 0 | encoder_encode(enc, frame); |
236 | 0 | av_frame_free(&frame); |
237 | |
|
238 | 0 | done: |
239 | 0 | return VO_TRUE; |
240 | 0 | } |
241 | | |
242 | | static void flip_page(struct vo *vo) |
243 | 0 | { |
244 | 0 | } |
245 | | |
246 | | static int control(struct vo *vo, uint32_t request, void *data) |
247 | 0 | { |
248 | 0 | return VO_NOTIMPL; |
249 | 0 | } |
250 | | |
251 | | const struct vo_driver video_out_lavc = { |
252 | | .encode = true, |
253 | | .description = "video encoding using libavcodec", |
254 | | .name = "lavc", |
255 | | .caps = VO_CAP_UNTIMED, |
256 | | .priv_size = sizeof(struct priv), |
257 | | .preinit = preinit, |
258 | | .query_format = query_format, |
259 | | .reconfig2 = reconfig2, |
260 | | .control = control, |
261 | | .uninit = uninit, |
262 | | .draw_frame = draw_frame, |
263 | | .flip_page = flip_page, |
264 | | }; |
265 | | |
266 | | // vim: sw=4 ts=4 et tw=80 |