blob: a6afbbfc41965dba03d07ab12bfd5daba27975e1
1 | /* |
2 | * V210 encoder |
3 | * |
4 | * Copyright (C) 2009 Michael Niedermayer <michaelni@gmx.at> |
5 | * Copyright (c) 2009 Baptiste Coudurier <baptiste dot coudurier at gmail dot com> |
6 | * |
7 | * This file is part of FFmpeg. |
8 | * |
9 | * FFmpeg 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 | * FFmpeg 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 GNU |
17 | * 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 FFmpeg; if not, write to the Free Software |
21 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA |
22 | */ |
23 | |
24 | #include "avcodec.h" |
25 | #include "bytestream.h" |
26 | #include "internal.h" |
27 | #include "v210enc.h" |
28 | |
29 | #define CLIP(v) av_clip(v, 4, 1019) |
30 | #define CLIP8(v) av_clip(v, 1, 254) |
31 | |
32 | #define WRITE_PIXELS(a, b, c) \ |
33 | do { \ |
34 | val = CLIP(*a++); \ |
35 | val |= (CLIP(*b++) << 10) | \ |
36 | (CLIP(*c++) << 20); \ |
37 | AV_WL32(dst, val); \ |
38 | dst += 4; \ |
39 | } while (0) |
40 | |
41 | #define WRITE_PIXELS8(a, b, c) \ |
42 | do { \ |
43 | val = (CLIP8(*a++) << 2); \ |
44 | val |= (CLIP8(*b++) << 12) | \ |
45 | (CLIP8(*c++) << 22); \ |
46 | AV_WL32(dst, val); \ |
47 | dst += 4; \ |
48 | } while (0) |
49 | |
50 | static void v210_planar_pack_8_c(const uint8_t *y, const uint8_t *u, |
51 | const uint8_t *v, uint8_t *dst, |
52 | ptrdiff_t width) |
53 | { |
54 | uint32_t val; |
55 | int i; |
56 | |
57 | /* unroll this to match the assembly */ |
58 | for (i = 0; i < width - 11; i += 12) { |
59 | WRITE_PIXELS8(u, y, v); |
60 | WRITE_PIXELS8(y, u, y); |
61 | WRITE_PIXELS8(v, y, u); |
62 | WRITE_PIXELS8(y, v, y); |
63 | WRITE_PIXELS8(u, y, v); |
64 | WRITE_PIXELS8(y, u, y); |
65 | WRITE_PIXELS8(v, y, u); |
66 | WRITE_PIXELS8(y, v, y); |
67 | } |
68 | } |
69 | |
70 | static void v210_planar_pack_10_c(const uint16_t *y, const uint16_t *u, |
71 | const uint16_t *v, uint8_t *dst, |
72 | ptrdiff_t width) |
73 | { |
74 | uint32_t val; |
75 | int i; |
76 | |
77 | for (i = 0; i < width - 5; i += 6) { |
78 | WRITE_PIXELS(u, y, v); |
79 | WRITE_PIXELS(y, u, y); |
80 | WRITE_PIXELS(v, y, u); |
81 | WRITE_PIXELS(y, v, y); |
82 | } |
83 | } |
84 | |
85 | av_cold void ff_v210enc_init(V210EncContext *s) |
86 | { |
87 | s->pack_line_8 = v210_planar_pack_8_c; |
88 | s->pack_line_10 = v210_planar_pack_10_c; |
89 | s->sample_factor_8 = 1; |
90 | s->sample_factor_10 = 1; |
91 | |
92 | if (ARCH_X86) |
93 | ff_v210enc_init_x86(s); |
94 | } |
95 | |
96 | static av_cold int encode_init(AVCodecContext *avctx) |
97 | { |
98 | V210EncContext *s = avctx->priv_data; |
99 | |
100 | if (avctx->width & 1) { |
101 | av_log(avctx, AV_LOG_ERROR, "v210 needs even width\n"); |
102 | return AVERROR(EINVAL); |
103 | } |
104 | |
105 | #if FF_API_CODED_FRAME |
106 | FF_DISABLE_DEPRECATION_WARNINGS |
107 | avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; |
108 | FF_ENABLE_DEPRECATION_WARNINGS |
109 | #endif |
110 | |
111 | ff_v210enc_init(s); |
112 | |
113 | avctx->bits_per_coded_sample = 20; |
114 | avctx->bit_rate = ff_guess_coded_bitrate(avctx) * 16 / 15; |
115 | |
116 | return 0; |
117 | } |
118 | |
119 | static int encode_frame(AVCodecContext *avctx, AVPacket *pkt, |
120 | const AVFrame *pic, int *got_packet) |
121 | { |
122 | V210EncContext *s = avctx->priv_data; |
123 | int aligned_width = ((avctx->width + 47) / 48) * 48; |
124 | int stride = aligned_width * 8 / 3; |
125 | int line_padding = stride - ((avctx->width * 8 + 11) / 12) * 4; |
126 | int h, w, ret; |
127 | uint8_t *dst; |
128 | |
129 | ret = ff_alloc_packet2(avctx, pkt, avctx->height * stride, avctx->height * stride); |
130 | if (ret < 0) { |
131 | av_log(avctx, AV_LOG_ERROR, "Error getting output packet.\n"); |
132 | return ret; |
133 | } |
134 | dst = pkt->data; |
135 | |
136 | if (pic->format == AV_PIX_FMT_YUV422P10) { |
137 | const uint16_t *y = (const uint16_t *)pic->data[0]; |
138 | const uint16_t *u = (const uint16_t *)pic->data[1]; |
139 | const uint16_t *v = (const uint16_t *)pic->data[2]; |
140 | |
141 | const int sample_size = 6 * s->sample_factor_10; |
142 | const int sample_w = avctx->width / sample_size; |
143 | |
144 | for (h = 0; h < avctx->height; h++) { |
145 | uint32_t val; |
146 | w = sample_w * sample_size; |
147 | s->pack_line_10(y, u, v, dst, w); |
148 | |
149 | y += w; |
150 | u += w >> 1; |
151 | v += w >> 1; |
152 | dst += sample_w * 16 * s->sample_factor_10; |
153 | |
154 | for (; w < avctx->width - 5; w += 6) { |
155 | WRITE_PIXELS(u, y, v); |
156 | WRITE_PIXELS(y, u, y); |
157 | WRITE_PIXELS(v, y, u); |
158 | WRITE_PIXELS(y, v, y); |
159 | } |
160 | if (w < avctx->width - 1) { |
161 | WRITE_PIXELS(u, y, v); |
162 | |
163 | val = CLIP(*y++); |
164 | if (w == avctx->width - 2) { |
165 | AV_WL32(dst, val); |
166 | dst += 4; |
167 | } |
168 | } |
169 | if (w < avctx->width - 3) { |
170 | val |= (CLIP(*u++) << 10) | (CLIP(*y++) << 20); |
171 | AV_WL32(dst, val); |
172 | dst += 4; |
173 | |
174 | val = CLIP(*v++) | (CLIP(*y++) << 10); |
175 | AV_WL32(dst, val); |
176 | dst += 4; |
177 | } |
178 | |
179 | memset(dst, 0, line_padding); |
180 | dst += line_padding; |
181 | y += pic->linesize[0] / 2 - avctx->width; |
182 | u += pic->linesize[1] / 2 - avctx->width / 2; |
183 | v += pic->linesize[2] / 2 - avctx->width / 2; |
184 | } |
185 | } else if(pic->format == AV_PIX_FMT_YUV422P) { |
186 | const uint8_t *y = pic->data[0]; |
187 | const uint8_t *u = pic->data[1]; |
188 | const uint8_t *v = pic->data[2]; |
189 | |
190 | const int sample_size = 12 * s->sample_factor_8; |
191 | const int sample_w = avctx->width / sample_size; |
192 | |
193 | for (h = 0; h < avctx->height; h++) { |
194 | uint32_t val; |
195 | w = sample_w * sample_size; |
196 | s->pack_line_8(y, u, v, dst, w); |
197 | |
198 | y += w; |
199 | u += w >> 1; |
200 | v += w >> 1; |
201 | dst += sample_w * 32 * s->sample_factor_8; |
202 | |
203 | for (; w < avctx->width - 5; w += 6) { |
204 | WRITE_PIXELS8(u, y, v); |
205 | WRITE_PIXELS8(y, u, y); |
206 | WRITE_PIXELS8(v, y, u); |
207 | WRITE_PIXELS8(y, v, y); |
208 | } |
209 | if (w < avctx->width - 1) { |
210 | WRITE_PIXELS8(u, y, v); |
211 | |
212 | val = CLIP8(*y++) << 2; |
213 | if (w == avctx->width - 2) { |
214 | AV_WL32(dst, val); |
215 | dst += 4; |
216 | } |
217 | } |
218 | if (w < avctx->width - 3) { |
219 | val |= (CLIP8(*u++) << 12) | (CLIP8(*y++) << 22); |
220 | AV_WL32(dst, val); |
221 | dst += 4; |
222 | |
223 | val = (CLIP8(*v++) << 2) | (CLIP8(*y++) << 12); |
224 | AV_WL32(dst, val); |
225 | dst += 4; |
226 | } |
227 | memset(dst, 0, line_padding); |
228 | dst += line_padding; |
229 | |
230 | y += pic->linesize[0] - avctx->width; |
231 | u += pic->linesize[1] - avctx->width / 2; |
232 | v += pic->linesize[2] - avctx->width / 2; |
233 | } |
234 | } |
235 | |
236 | pkt->flags |= AV_PKT_FLAG_KEY; |
237 | *got_packet = 1; |
238 | return 0; |
239 | } |
240 | |
241 | AVCodec ff_v210_encoder = { |
242 | .name = "v210", |
243 | .long_name = NULL_IF_CONFIG_SMALL("Uncompressed 4:2:2 10-bit"), |
244 | .type = AVMEDIA_TYPE_VIDEO, |
245 | .id = AV_CODEC_ID_V210, |
246 | .priv_data_size = sizeof(V210EncContext), |
247 | .init = encode_init, |
248 | .encode2 = encode_frame, |
249 | .pix_fmts = (const enum AVPixelFormat[]){ AV_PIX_FMT_YUV422P10, AV_PIX_FMT_YUV422P, AV_PIX_FMT_NONE }, |
250 | }; |
251 |