1 /*
2  * Copyright (C) 2004 Nathan Lutchansky <lutchann@litech.org>
3  *
4  * This program is free software; you can redistribute it and/or modify
5  * it under the terms of the GNU General Public License as published by
6  * the Free Software Foundation; either version 2 of the License, or
7  * (at your option) any later version.
8  *
9  * This program is distributed in the hope that it will be useful,
10  * but WITHOUT ANY WARRANTY; without even the implied warranty of
11  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
12  * GNU General Public License for more details.
13  *
14  * You should have received a copy of the GNU General Public License
15  * along with this program; if not, write to the Free Software Foundation,
16  * Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
17  */
18 
19 #include <sys/types.h>
20 #include <stdlib.h>
21 #include <stdio.h>
22 #include <unistd.h>
23 #include <string.h>
24 #include <fcntl.h>
25 #include <pthread.h>
26 
27 #include <xvid.h>
28 
29 #include <event.h>
30 #include <log.h>
31 #include <frame.h>
32 #include <stream.h>
33 #include <encoders.h>
34 #include <conf_parse.h>
35 
36 struct mpeg4_encoder {
37 	struct stream *output;
38 	struct stream_destination *input;
39 	struct frame_exchanger *ex;
40 	pthread_t encoding_thread;
41 	int running; /* only used by the main thread */
42 
43 	/* parameters from the config file */
44 	int bitrate;
45 
46 	/* reset_pending is used to indicate that there may have been a long
47 	 * break since the last frame, presumably because nobody was listening
48 	 * and the source stopped sending us frames, so the encoding thread
49 	 * should reset the encoder.  It doesn't really need synchronization
50 	 * because if we reset in the middle of a bunch of contiguous frames
51 	 * it's not the end of the world. */
52 	int reset_pending;
53 
54 	/* All the parameters after the handle indicate the settings used
55 	 * to set up the current encoder.  Therefore, they are invalid
56 	 * when xvid_handle == NULL. */
57 	void *xvid_handle;
58 	int width;
59 	int height;
60 };
61 
mpeg4_start(struct mpeg4_encoder * en,struct frame * f)62 static void mpeg4_start( struct mpeg4_encoder *en, struct frame *f )
63 {
64 	xvid_enc_create_t xvid_enc_create;
65 	xvid_enc_plugin_t plugins[1];
66 	xvid_plugin_single_t single;
67 
68 	en->reset_pending = 0;
69 	en->width = f->width;
70 	en->height = f->height;
71 
72 	memset( &xvid_enc_create, 0, sizeof( xvid_enc_create ) );
73 	xvid_enc_create.version = XVID_VERSION;
74 	xvid_enc_create.width = en->width;
75 	xvid_enc_create.height = en->height;
76 	xvid_enc_create.profile = XVID_PROFILE_ARTS_L4;
77 	en->input->stream->get_framerate( en->input->stream,
78 						&xvid_enc_create.fincr,
79 						&xvid_enc_create.fbase );
80 	spook_log( SL_DEBUG, "creating mpeg4 encoder with fincr=%d fbase=%d",
81 			xvid_enc_create.fincr, xvid_enc_create.fbase );
82 	xvid_enc_create.zones = NULL;
83 	xvid_enc_create.num_zones = 0;
84 	xvid_enc_create.plugins = plugins;
85 	xvid_enc_create.num_plugins = 1;
86 	xvid_enc_create.num_threads = 0;
87 	xvid_enc_create.max_key_interval = 300;
88 	xvid_enc_create.max_bframes = 0;
89 	xvid_enc_create.bquant_ratio = 150;
90 	xvid_enc_create.bquant_offset = 100;
91 	xvid_enc_create.frame_drop_ratio = 0;
92 	xvid_enc_create.global = 0;
93 
94 	memset( &single, 0, sizeof( single ) );
95 	single.version = XVID_VERSION;
96 	single.bitrate = en->bitrate * 1000;
97 	plugins[0].func = xvid_plugin_single;
98 	plugins[0].param = &single;
99 
100 	if( xvid_encore( NULL, XVID_ENC_CREATE,
101 				&xvid_enc_create, NULL ) )
102 	{
103 		spook_log( SL_ERR, "mpeg4: unable to start XviD!" );
104 		return;
105 	}
106 
107 	en->xvid_handle = xvid_enc_create.handle;
108 }
109 
mpeg4_stop(struct mpeg4_encoder * en)110 static void mpeg4_stop( struct mpeg4_encoder *en )
111 {
112 	spook_log( SL_DEBUG, "mpeg4: destroying mpeg4 encoder" );
113 
114 	xvid_encore( en->xvid_handle, XVID_ENC_DESTROY, NULL, NULL );
115 	en->xvid_handle = NULL;
116 }
117 
mpeg4_loop(void * d)118 static void *mpeg4_loop( void *d )
119 {
120 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)d;
121 	xvid_enc_frame_t xvid_enc_frame;
122 	struct frame *mpeg, *input;
123 
124 	for(;;)
125 	{
126 		input = get_next_frame( en->ex, 1 );
127 
128 		if( en->reset_pending && en->xvid_handle ) mpeg4_stop( en );
129 		if( ! en->xvid_handle ) mpeg4_start( en, input );
130 
131 		if( input->width != en->width || input->height != en->height )
132 		{
133 			spook_log( SL_WARN,
134 				"mpeg4: image size changed midstream!" );
135 			unref_frame( input );
136 			continue;
137 		}
138 
139 		mpeg = new_frame();
140 
141 		memset( &xvid_enc_frame, 0, sizeof( xvid_enc_frame ) );
142 		xvid_enc_frame.version = XVID_VERSION;
143 		xvid_enc_frame.bitstream = mpeg->d;
144 		xvid_enc_frame.length = -1;
145 		xvid_enc_frame.input.plane[0] = input->d;
146 		switch( input->format )
147 		{
148 		case FORMAT_RAW_BGR24:
149 			xvid_enc_frame.input.csp = XVID_CSP_BGR;
150 			xvid_enc_frame.input.stride[0] = en->width * 3;
151 			break;
152 		case FORMAT_RAW_UYVY:
153 			xvid_enc_frame.input.csp = XVID_CSP_UYVY;
154 			xvid_enc_frame.input.stride[0] = en->width * 2;
155 			break;
156 		}
157 		xvid_enc_frame.vol_flags = 0;
158 		xvid_enc_frame.vop_flags = 0;
159 		xvid_enc_frame.type = XVID_TYPE_AUTO;
160 		xvid_enc_frame.quant = 0;
161 		xvid_enc_frame.motion = XVID_ME_ADVANCEDDIAMOND16;
162 		xvid_enc_frame.quant_intra_matrix = NULL;
163 		xvid_enc_frame.quant_inter_matrix = NULL;
164 
165 		mpeg->length = xvid_encore( en->xvid_handle, XVID_ENC_ENCODE,
166 					&xvid_enc_frame, NULL );
167 		if( mpeg->length < 0 )
168 		{
169 			mpeg->length = 0;
170 			spook_log( SL_WARN, "mpeg4: XviD encoding failed!" );
171 		}
172 
173 		mpeg->format = FORMAT_MPEG4;
174 		mpeg->width = en->width;
175 		mpeg->height = en->height;
176 		mpeg->key = xvid_enc_frame.out_flags & XVID_KEYFRAME;
177 
178 		deliver_frame( en->ex, mpeg );
179 
180 		unref_frame( input );
181 	}
182 
183 	return NULL;
184 }
185 
mpeg4_encode(struct frame * input,void * d)186 static void mpeg4_encode( struct frame *input, void *d )
187 {
188 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)d;
189 
190 	exchange_frame( en->ex, input );
191 }
192 
get_framerate(struct stream * s,int * fincr,int * fbase)193 static void get_framerate( struct stream *s, int *fincr, int *fbase )
194 {
195 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)s->private;
196 
197 	en->input->stream->get_framerate( en->input->stream, fincr, fbase );
198 }
199 
set_running(struct stream * s,int running)200 static void set_running( struct stream *s, int running )
201 {
202 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)s->private;
203 
204 	spook_log( SL_DEBUG,
205 		"mpeg4 encoder is told to set running to %d", running );
206 
207 	if( ! en->running && running ) en->reset_pending = 1;
208 	set_waiting( en->input, running );
209 	en->running = running;
210 }
211 
212 /************************ CONFIGURATION DIRECTIVES ************************/
213 
start_block(void)214 static void *start_block(void)
215 {
216 	struct mpeg4_encoder *en;
217 
218 	en = (struct mpeg4_encoder *)malloc( sizeof( struct mpeg4_encoder ) );
219 	en->output = NULL;
220 	en->xvid_handle = NULL;
221 	en->bitrate = -1;
222 
223 	return en;
224 }
225 
end_block(void * d)226 static int end_block( void *d )
227 {
228 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)d;
229 
230 	if( ! en->output )
231 	{
232 		spook_log( SL_ERR, "mpeg4: missing output stream name" );
233 		return -1;
234 	}
235 	if( ! en->input )
236 	{
237 		spook_log( SL_ERR, "mpeg4: missing input stream name" );
238 		return -1;
239 	}
240 	if( en->bitrate < 0 )
241 	{
242 		spook_log( SL_ERR, "mpeg4: bitrate must be specified" );
243 		return -1;
244 	}
245 
246 	en->ex = new_exchanger( 8, deliver_frame_to_stream, en->output );
247 	pthread_create( &en->encoding_thread, NULL, mpeg4_loop, en );
248 
249 	return 0;
250 }
251 
set_input(int num_tokens,struct token * tokens,void * d)252 static int set_input( int num_tokens, struct token *tokens, void *d )
253 {
254 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)d;
255 	int formats[2] = { FORMAT_RAW_UYVY, FORMAT_RAW_BGR24 };
256 
257 	if( ! ( en->input = connect_to_stream( tokens[1].v.str, mpeg4_encode,
258 						en, formats, 2 ) ) )
259 	{
260 		spook_log( SL_ERR, "mpeg4: unable to connect to stream \"%s\"",
261 				tokens[1].v.str );
262 		return -1;
263 	}
264 	return 0;
265 }
266 
set_output(int num_tokens,struct token * tokens,void * d)267 static int set_output( int num_tokens, struct token *tokens, void *d )
268 {
269 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)d;
270 
271 	en->output = new_stream( tokens[1].v.str, FORMAT_MPEG4, en );
272 	if( ! en->output )
273 	{
274 		spook_log( SL_ERR, "mpeg4: unable to create stream \"%s\"",
275 			tokens[1].v.str );
276 		return -1;
277 	}
278 	en->output->get_framerate = get_framerate;
279 	en->output->set_running = set_running;
280 	return 0;
281 }
282 
set_bitrate(int num_tokens,struct token * tokens,void * d)283 static int set_bitrate( int num_tokens, struct token *tokens, void *d )
284 {
285 	struct mpeg4_encoder *en = (struct mpeg4_encoder *)d;
286 
287 	if( tokens[1].v.num < 10 || tokens[1].v.num > 4000 )
288 	{
289 		spook_log( SL_ERR,
290 			"mpeg4: bitrate must be between 10 and 4000" );
291 		return -1;
292 	}
293 	en->bitrate = tokens[1].v.num;
294 	return 0;
295 }
296 
297 static struct statement config_statements[] = {
298 	/* directive name, process function, min args, max args, arg types */
299 	{ "input", set_input, 1, 1, { TOKEN_STR } },
300 	{ "output", set_output, 1, 1, { TOKEN_STR } },
301 	{ "bitrate", set_bitrate, 1, 1, { TOKEN_NUM } },
302 
303 	/* empty terminator -- do not remove */
304 	{ NULL, NULL, 0, 0, {} }
305 };
306 
mpeg4_init(void)307 int mpeg4_init(void)
308 {
309 	xvid_gbl_init_t xvid_gbl_init;
310 
311 	memset( &xvid_gbl_init, 0, sizeof( xvid_gbl_init ) );
312 	xvid_gbl_init.version = XVID_VERSION;
313 	xvid_global( NULL, XVID_GBL_INIT, &xvid_gbl_init, NULL );
314 	register_config_context( "encoder", "mpeg4", start_block, end_block,
315 					config_statements );
316 	return 0;
317 }
318