Commit | Line | Data |
---|---|---|
2ba45a60 DM |
1 | /* |
2 | * Copyright (c) 2012 Derek Buitenhuis | |
3 | * | |
4 | * This file is part of FFmpeg. | |
5 | * | |
6 | * FFmpeg is free software; you can redistribute it and/or | |
7 | * modify it under the terms of the GNU General Public | |
8 | * License as published by the Free Software Foundation; | |
9 | * version 2 of the License. | |
10 | * | |
11 | * FFmpeg is distributed in the hope that it will be useful, | |
12 | * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
13 | * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
14 | * General Public License for more details. | |
15 | * | |
16 | * You should have received a copy of the GNU General Public | |
17 | * License along with FFmpeg; if not, write to the Free Software | |
18 | * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA | |
19 | */ | |
20 | ||
21 | /** | |
22 | * @file | |
23 | * Known FOURCCs: | |
24 | * 'ULY0' (YCbCr 4:2:0), 'ULY2' (YCbCr 4:2:2), 'ULRG' (RGB), 'ULRA' (RGBA), | |
25 | * 'ULH0' (YCbCr 4:2:0 BT.709), 'ULH2' (YCbCr 4:2:2 BT.709) | |
26 | */ | |
27 | ||
28 | extern "C" { | |
29 | #include "libavutil/avassert.h" | |
30 | #include "avcodec.h" | |
31 | #include "internal.h" | |
32 | } | |
33 | ||
34 | #include "libutvideo.h" | |
35 | #include "put_bits.h" | |
36 | ||
37 | static av_cold int utvideo_encode_init(AVCodecContext *avctx) | |
38 | { | |
39 | UtVideoContext *utv = (UtVideoContext *)avctx->priv_data; | |
40 | UtVideoExtra *info; | |
41 | uint32_t flags, in_format; | |
42 | ||
43 | switch (avctx->pix_fmt) { | |
44 | case AV_PIX_FMT_YUV420P: | |
45 | in_format = UTVF_YV12; | |
46 | avctx->bits_per_coded_sample = 12; | |
47 | if (avctx->colorspace == AVCOL_SPC_BT709) | |
48 | avctx->codec_tag = MKTAG('U', 'L', 'H', '0'); | |
49 | else | |
50 | avctx->codec_tag = MKTAG('U', 'L', 'Y', '0'); | |
51 | break; | |
52 | case AV_PIX_FMT_YUYV422: | |
53 | in_format = UTVF_YUYV; | |
54 | avctx->bits_per_coded_sample = 16; | |
55 | if (avctx->colorspace == AVCOL_SPC_BT709) | |
56 | avctx->codec_tag = MKTAG('U', 'L', 'H', '2'); | |
57 | else | |
58 | avctx->codec_tag = MKTAG('U', 'L', 'Y', '2'); | |
59 | break; | |
60 | case AV_PIX_FMT_BGR24: | |
61 | in_format = UTVF_NFCC_BGR_BU; | |
62 | avctx->bits_per_coded_sample = 24; | |
63 | avctx->codec_tag = MKTAG('U', 'L', 'R', 'G'); | |
64 | break; | |
65 | case AV_PIX_FMT_RGB32: | |
66 | in_format = UTVF_NFCC_BGRA_BU; | |
67 | avctx->bits_per_coded_sample = 32; | |
68 | avctx->codec_tag = MKTAG('U', 'L', 'R', 'A'); | |
69 | break; | |
70 | default: | |
71 | return AVERROR(EINVAL); | |
72 | } | |
73 | ||
74 | /* Check before we alloc anything */ | |
75 | if (avctx->prediction_method != 0 && avctx->prediction_method != 2) { | |
76 | av_log(avctx, AV_LOG_ERROR, "Invalid prediction method.\n"); | |
77 | return AVERROR(EINVAL); | |
78 | } | |
79 | ||
80 | flags = ((avctx->prediction_method + 1) << 8) | (avctx->thread_count - 1); | |
81 | ||
82 | avctx->priv_data = utv; | |
83 | avctx->coded_frame = av_frame_alloc(); | |
84 | ||
85 | /* Alloc extradata buffer */ | |
86 | info = (UtVideoExtra *)av_malloc(sizeof(*info)); | |
87 | ||
88 | if (!info) { | |
89 | av_log(avctx, AV_LOG_ERROR, "Could not allocate extradata buffer.\n"); | |
90 | return AVERROR(ENOMEM); | |
91 | } | |
92 | ||
93 | /* | |
94 | * We use this buffer to hold the data that Ut Video returns, | |
95 | * since we cannot decode planes separately with it. | |
96 | */ | |
97 | utv->buf_size = avpicture_get_size(avctx->pix_fmt, | |
98 | avctx->width, avctx->height); | |
99 | utv->buffer = (uint8_t *)av_malloc(utv->buf_size); | |
100 | ||
101 | if (utv->buffer == NULL) { | |
102 | av_log(avctx, AV_LOG_ERROR, "Could not allocate output buffer.\n"); | |
103 | return AVERROR(ENOMEM); | |
104 | } | |
105 | ||
106 | /* | |
107 | * Create a Ut Video instance. Since the function wants | |
108 | * an "interface name" string, pass it the name of the lib. | |
109 | */ | |
110 | utv->codec = CCodec::CreateInstance(UNFCC(avctx->codec_tag), "libavcodec"); | |
111 | ||
112 | /* Initialize encoder */ | |
113 | utv->codec->EncodeBegin(in_format, avctx->width, avctx->height, | |
114 | CBGROSSWIDTH_WINDOWS); | |
115 | ||
116 | /* Get extradata from encoder */ | |
117 | avctx->extradata_size = utv->codec->EncodeGetExtraDataSize(); | |
118 | utv->codec->EncodeGetExtraData(info, avctx->extradata_size, in_format, | |
119 | avctx->width, avctx->height, | |
120 | CBGROSSWIDTH_WINDOWS); | |
121 | avctx->extradata = (uint8_t *)info; | |
122 | ||
123 | /* Set flags */ | |
124 | utv->codec->SetState(&flags, sizeof(flags)); | |
125 | ||
126 | return 0; | |
127 | } | |
128 | ||
129 | static int utvideo_encode_frame(AVCodecContext *avctx, AVPacket *pkt, | |
130 | const AVFrame *pic, int *got_packet) | |
131 | { | |
132 | UtVideoContext *utv = (UtVideoContext *)avctx->priv_data; | |
133 | int w = avctx->width, h = avctx->height; | |
134 | int ret, rgb_size, i; | |
135 | bool keyframe; | |
136 | uint8_t *y, *u, *v; | |
137 | uint8_t *dst; | |
138 | ||
139 | /* Alloc buffer */ | |
140 | if ((ret = ff_alloc_packet2(avctx, pkt, utv->buf_size)) < 0) | |
141 | return ret; | |
142 | ||
143 | dst = pkt->data; | |
144 | ||
145 | /* Move input if needed data into Ut Video friendly buffer */ | |
146 | switch (avctx->pix_fmt) { | |
147 | case AV_PIX_FMT_YUV420P: | |
148 | y = utv->buffer; | |
149 | u = y + w * h; | |
150 | v = u + w * h / 4; | |
151 | for (i = 0; i < h; i++) { | |
152 | memcpy(y, pic->data[0] + i * pic->linesize[0], w); | |
153 | y += w; | |
154 | } | |
155 | for (i = 0; i < h / 2; i++) { | |
156 | memcpy(u, pic->data[2] + i * pic->linesize[2], w >> 1); | |
157 | memcpy(v, pic->data[1] + i * pic->linesize[1], w >> 1); | |
158 | u += w >> 1; | |
159 | v += w >> 1; | |
160 | } | |
161 | break; | |
162 | case AV_PIX_FMT_YUYV422: | |
163 | for (i = 0; i < h; i++) | |
164 | memcpy(utv->buffer + i * (w << 1), | |
165 | pic->data[0] + i * pic->linesize[0], w << 1); | |
166 | break; | |
167 | case AV_PIX_FMT_BGR24: | |
168 | case AV_PIX_FMT_RGB32: | |
169 | /* Ut Video takes bottom-up BGR */ | |
170 | rgb_size = avctx->pix_fmt == AV_PIX_FMT_BGR24 ? 3 : 4; | |
171 | for (i = 0; i < h; i++) | |
172 | memcpy(utv->buffer + (h - i - 1) * w * rgb_size, | |
173 | pic->data[0] + i * pic->linesize[0], | |
174 | w * rgb_size); | |
175 | break; | |
176 | default: | |
177 | return AVERROR(EINVAL); | |
178 | } | |
179 | ||
180 | /* Encode frame */ | |
181 | pkt->size = utv->codec->EncodeFrame(dst, &keyframe, utv->buffer); | |
182 | ||
183 | if (!pkt->size) { | |
184 | av_log(avctx, AV_LOG_ERROR, "EncodeFrame failed!\n"); | |
185 | return AVERROR_INVALIDDATA; | |
186 | } | |
187 | ||
188 | /* | |
189 | * Ut Video is intra-only and every frame is a keyframe, | |
190 | * and the API always returns true. In case something | |
191 | * durastic changes in the future, such as inter support, | |
192 | * assert that this is true. | |
193 | */ | |
194 | av_assert2(keyframe == true); | |
195 | avctx->coded_frame->key_frame = 1; | |
196 | avctx->coded_frame->pict_type = AV_PICTURE_TYPE_I; | |
197 | ||
198 | pkt->flags |= AV_PKT_FLAG_KEY; | |
199 | *got_packet = 1; | |
200 | return 0; | |
201 | } | |
202 | ||
203 | static av_cold int utvideo_encode_close(AVCodecContext *avctx) | |
204 | { | |
205 | UtVideoContext *utv = (UtVideoContext *)avctx->priv_data; | |
206 | ||
207 | av_freep(&avctx->coded_frame); | |
208 | av_freep(&avctx->extradata); | |
209 | av_freep(&utv->buffer); | |
210 | ||
211 | utv->codec->EncodeEnd(); | |
212 | CCodec::DeleteInstance(utv->codec); | |
213 | ||
214 | return 0; | |
215 | } | |
216 | ||
217 | AVCodec ff_libutvideo_encoder = { | |
218 | "libutvideo", | |
219 | NULL_IF_CONFIG_SMALL("Ut Video"), | |
220 | AVMEDIA_TYPE_VIDEO, | |
221 | AV_CODEC_ID_UTVIDEO, | |
222 | CODEC_CAP_AUTO_THREADS | CODEC_CAP_LOSSLESS, | |
223 | NULL, /* supported_framerates */ | |
224 | (const enum AVPixelFormat[]) { | |
225 | AV_PIX_FMT_YUV420P, AV_PIX_FMT_YUYV422, AV_PIX_FMT_BGR24, | |
226 | AV_PIX_FMT_RGB32, AV_PIX_FMT_NONE | |
227 | }, | |
228 | NULL, /* supported_samplerates */ | |
229 | NULL, /* sample_fmts */ | |
230 | NULL, /* channel_layouts */ | |
231 | 0, /* max_lowres */ | |
232 | NULL, /* priv_class */ | |
233 | NULL, /* profiles */ | |
234 | sizeof(UtVideoContext), | |
235 | NULL, /* next */ | |
236 | NULL, /* init_thread_copy */ | |
237 | NULL, /* update_thread_context */ | |
238 | NULL, /* defaults */ | |
239 | NULL, /* init_static_data */ | |
240 | utvideo_encode_init, | |
241 | NULL, /* encode */ | |
242 | utvideo_encode_frame, | |
243 | NULL, /* decode */ | |
244 | utvideo_encode_close, | |
245 | NULL, /* flush */ | |
246 | }; |