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