1 /*
2 * encode_lzo.c -- encode video frames individually using LZO.
3 * (C) 2005-2010 Francesco Romani <fromani at gmail dot com>
4 *
5 * This file is part of transcode, a video stream processing tool.
6 *
7 * transcode is free software; you can redistribute it and/or modify
8 * it under the terms of the GNU General Public License as published by
9 * the Free Software Foundation; either version 2 of the License, or
10 * (at your option) any later version.
11 *
12 * transcode is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16 *
17 * You should have received a copy of the GNU General Public License
18 * along with this program. If not, see <http://www.gnu.org/licenses/>.
19 */
20
21
22 #include "transcode.h"
23 #include "aclib/imgconvert.h"
24 #include "libtc/optstr.h"
25 #include "libtc/tc_lzo.h"
26 #include "libtc/tcmodule-plugin.h"
27
28 #include <stdio.h>
29 #include <stdlib.h>
30
31 #define MOD_NAME "encode_lzo.so"
32 #define MOD_VERSION "v0.0.2 (2007-10-27)"
33 #define MOD_CAP "LZO lossless video encoder"
34
35 #define MOD_FEATURES \
36 TC_MODULE_FEATURE_ENCODE|TC_MODULE_FEATURE_VIDEO
37
38 #define MOD_FLAGS \
39 TC_MODULE_FLAG_RECONFIGURABLE
40
41
42 /* tc_lzo_ prefix was used to avoid any possible name clash with liblzo? */
43
44 static const char tc_lzo_help[] = ""
45 "Overview:\n"
46 " this module encodes raw RGB/YUV video frames in LZO, using liblzo V2.\n"
47 "Options:\n"
48 " help produce module overview and options explanations\n";
49
50 typedef struct {
51 lzo_byte work_mem[LZO1X_1_MEM_COMPRESS];
52 /* needed by encoder to work properly */
53
54 int codec;
55 int flush_flag;
56 } LZOPrivateData;
57
tc_lzo_configure(TCModuleInstance * self,const char * options,vob_t * vob)58 static int tc_lzo_configure(TCModuleInstance *self,
59 const char *options, vob_t *vob)
60 {
61 LZOPrivateData *pd = NULL;
62 int ret;
63
64 TC_MODULE_SELF_CHECK(self, "configure");
65
66 pd = self->userdata;
67 pd->codec = vob->im_v_codec;
68 pd->flush_flag = vob->encoder_flush;
69
70 ret = lzo_init();
71 if (ret != LZO_E_OK) {
72 tc_log_error(MOD_NAME, "configure: failed to initialize"
73 " LZO encoder");
74 return TC_ERROR;
75 }
76
77 return TC_OK;
78 }
79
tc_lzo_stop(TCModuleInstance * self)80 static int tc_lzo_stop(TCModuleInstance *self)
81 {
82 LZOPrivateData *pd = NULL;
83
84 TC_MODULE_SELF_CHECK(self, "stop");
85
86 pd = self->userdata;
87
88 return TC_OK;
89 }
90
tc_lzo_init(TCModuleInstance * self,uint32_t features)91 static int tc_lzo_init(TCModuleInstance *self, uint32_t features)
92 {
93 LZOPrivateData *pd = NULL;
94
95 TC_MODULE_SELF_CHECK(self, "init");
96 TC_MODULE_INIT_CHECK(self, MOD_FEATURES, features);
97
98 pd = tc_malloc(sizeof(LZOPrivateData));
99 if (!pd) {
100 tc_log_error(MOD_NAME, "init: can't allocate private data");
101 return TC_ERROR;
102 }
103 /* sane defaults */
104 pd->codec = CODEC_YUV;
105
106 self->userdata = pd;
107 if (verbose) {
108 tc_log_info(MOD_NAME, "%s %s", MOD_VERSION, MOD_CAP);
109 }
110 return TC_OK;
111 }
112
tc_lzo_fini(TCModuleInstance * self)113 static int tc_lzo_fini(TCModuleInstance *self)
114 {
115 TC_MODULE_SELF_CHECK(self, "fini");
116
117 tc_lzo_stop(self);
118
119 tc_free(self->userdata);
120 self->userdata = NULL;
121 return TC_OK;
122 }
123
tc_lzo_inspect(TCModuleInstance * self,const char * param,const char ** value)124 static int tc_lzo_inspect(TCModuleInstance *self,
125 const char *param, const char **value)
126 {
127 LZOPrivateData *pd = NULL;
128
129 TC_MODULE_SELF_CHECK(self, "inspect");
130
131 pd = self->userdata;
132
133 if (optstr_lookup(param, "help")) {
134 *value = tc_lzo_help;
135 }
136
137 return TC_OK;
138 }
139
140 /* ------------------------------------------------------------
141 *
142 * encode and export
143 *
144 * ------------------------------------------------------------*/
145
146 /* assert(len(data) >= TC_LZ_HDR_SIZE) */
tc_lzo_put_header(tc_lzo_header_t * hdr,void * data)147 static void tc_lzo_put_header(tc_lzo_header_t *hdr, void *data)
148 {
149 /* always CPU byte order */
150 uint32_t *ptr = data;
151
152 *(ptr) = hdr->magic;
153 *(ptr + 1) = hdr->size;
154 *(ptr + 2) = hdr->flags;
155 *(ptr + 3) = (uint32_t)(hdr->method << 24 | hdr->level << 16 | hdr->pad);
156 }
157
158 /* maybe translation should go away */
tc_lzo_format_translate(int tc_codec)159 static int tc_lzo_format_translate(int tc_codec)
160 {
161 int ret;
162 switch (tc_codec) {
163 case CODEC_YUV:
164 ret = TC_LZO_FORMAT_YUV420P;
165 break;
166 case CODEC_YUY2:
167 ret = TC_LZO_FORMAT_YUY2;
168 break;
169 case CODEC_RGB:
170 ret = TC_LZO_FORMAT_RGB24;
171 break;
172 default:
173 /* shouldn't happen */
174 ret = 0;
175 break;
176 }
177 return ret;
178 }
179
tc_lzo_flush(TCModuleInstance * self,vframe_list_t * outframe)180 static int tc_lzo_flush(TCModuleInstance *self,
181 vframe_list_t *outframe)
182 {
183 outframe->video_len = 0;
184 return TC_OK;
185 }
186
187
tc_lzo_encode_video(TCModuleInstance * self,vframe_list_t * inframe,vframe_list_t * outframe)188 static int tc_lzo_encode_video(TCModuleInstance *self,
189 vframe_list_t *inframe, vframe_list_t *outframe)
190 {
191 LZOPrivateData *pd = NULL;
192 lzo_uint out_len = 0;
193 tc_lzo_header_t hdr;
194 int ret;
195
196 TC_MODULE_SELF_CHECK(self, "encode_video");
197
198 pd = self->userdata;
199
200 if (inframe == NULL && pd->flush_flag) {
201 return tc_lzo_flush(self, outframe); // FIXME
202 }
203
204 /* invariants */
205 hdr.magic = TC_CODEC_LZO2;
206 hdr.method = 1;
207 hdr.level = 1;
208 hdr.pad = 0;
209 hdr.flags = 0; /* sane default */
210
211 ret = lzo1x_1_compress(inframe->video_buf, inframe->video_size,
212 outframe->video_buf + TC_LZO_HDR_SIZE,
213 &out_len, pd->work_mem);
214 if (ret != LZO_E_OK) {
215 /* this should NEVER happen */
216 tc_log_warn(MOD_NAME, "encode_video: LZO compression failed"
217 " (errcode=%i)", ret);
218 return TC_ERROR;
219 }
220
221 /* check for an incompressible block */
222 if (out_len >= inframe->video_size) {
223 hdr.flags |= TC_LZO_NOT_COMPRESSIBLE;
224 out_len = inframe->video_size;
225 }
226 hdr.size = out_len;
227
228 hdr.flags |= tc_lzo_format_translate(pd->codec);
229 /* always put header */
230 tc_lzo_put_header(&hdr, outframe->video_buf);
231
232 if (hdr.flags & TC_LZO_NOT_COMPRESSIBLE) {
233 /* inframe data not compressible: outframe will hold a copy */
234 if (verbose >= TC_DEBUG) {
235 tc_log_info(MOD_NAME, "encode_video: block contains"
236 " incompressible data");
237 }
238 ac_memcpy(outframe->video_buf + TC_LZO_HDR_SIZE,
239 inframe->video_buf, out_len);
240 } else {
241 /* outframe data already in place */
242 if (verbose >= TC_DEBUG) {
243 tc_log_info(MOD_NAME, "encode_video: compressed %lu bytes"
244 " into %lu bytes",
245 (unsigned long)inframe->video_size,
246 (unsigned long)out_len);
247 }
248 }
249
250 /* only keyframes */
251 outframe->video_len = out_len + TC_LZO_HDR_SIZE;
252 outframe->attributes |= TC_FRAME_IS_KEYFRAME;
253
254 return TC_OK;
255 }
256
257 /*************************************************************************/
258
259 static const TCCodecID tc_lzo_codecs_in[] = {
260 TC_CODEC_YUY2, TC_CODEC_RGB, TC_CODEC_YUV420P, TC_CODEC_ERROR
261 };
262 static const TCCodecID tc_lzo_codecs_out[] = {
263 TC_CODEC_LZO2, TC_CODEC_ERROR
264 };
265 TC_MODULE_CODEC_FORMATS(tc_lzo);
266
267 TC_MODULE_INFO(tc_lzo);
268
269 static const TCModuleClass tc_lzo_class = {
270 TC_MODULE_CLASS_HEAD(tc_lzo),
271
272 .init = tc_lzo_init,
273 .fini = tc_lzo_fini,
274 .configure = tc_lzo_configure,
275 .stop = tc_lzo_stop,
276 .inspect = tc_lzo_inspect,
277
278 .encode_video = tc_lzo_encode_video,
279 };
280
281 TC_MODULE_ENTRY_POINT(tc_lzo)
282
283 /*************************************************************************/
284
285 /*
286 * Local variables:
287 * c-file-style: "stroustrup"
288 * c-file-offsets: ((case-label . *) (statement-case-intro . *))
289 * indent-tabs-mode: nil
290 * End:
291 *
292 * vim: expandtab shiftwidth=4:
293 */
294