1 /*
2 
3    Ming, an SWF output library
4    Copyright (C) 2002  Opaque Industries - http://www.opaque.net/
5 
6    15.12.2003 Klaus Rechert
7 
8    This library is free software; you can redistribute it and/or
9    modify it under the terms of the GNU Lesser General Public
10    License as published by the Free Software Foundation; either
11    version 2.1 of the License, or (at your option) any later version.
12 
13    This library is distributed in the hope that it will be useful,
14    but WITHOUT ANY WARRANTY; without even the implied warranty of
15    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
16    Lesser General Public License for more details.
17 
18    You should have received a copy of the GNU Lesser General Public
19    License along with this library; if not, write to the Free Software
20    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
21 
22 */
23 
24 #include <stdio.h>
25 #include <stdlib.h>
26 
27 #include "input.h"
28 #include "method.h"
29 #include "block.h"
30 #include "character.h"
31 #include "videostream.h"
32 #include "libming.h"
33 #include "../displaylist.h"
34 
35 #include "flv.h"
36 
37 #define VIDEO_SMOOTHING 0x01
38 #define VIDEO_NO_SMOOTHING 0x0
39 #define VIDEOFRAME_BLOCK_SIZE 10
40 
41 struct SWFVideoStream_s
42 {
43 	struct SWFCharacter_s character;
44 
45 	FLVStream *flv;
46 	FLVTag *lastTag;
47 	int lastFrame;
48 
49 	int numFrames;
50 	unsigned int frame;
51 	int width;
52 	int height;
53 	unsigned short embedded;
54 	unsigned char codecId;
55 	unsigned char smoothingFlag;
56 	int mode;
57 	int addFrame;
58 	int framesLoaded;
59 	int firstFrame;
60 };
61 
62 struct SWFVideoFrame_s
63 {
64 	struct SWFBlock_s block;
65 
66 	SWFVideoStream stream;
67 	int frameNum;
68 
69 	FLVTag tag;
70 };
71 
SWFVideoStream_getFrameNumber(SWFVideoFrame frame)72 int SWFVideoStream_getFrameNumber(SWFVideoFrame frame)
73 {
74 	return frame->frameNum;
75 }
76 
completeSWFVideoFrame(SWFBlock block)77 int completeSWFVideoFrame(SWFBlock block)
78 {
79 	SWFVideoFrame frame = (SWFVideoFrame)block;
80 	SWFInput input;
81 
82 	input = FLVTag_getPayloadInput(&frame->tag);
83 	if(input == NULL)
84 		return 4;
85 
86 	return SWFInput_length(input) + 4;
87 }
88 
writeSWFVideoFrameToMethod(SWFBlock block,SWFByteOutputMethod method,void * data)89 void writeSWFVideoFrameToMethod(SWFBlock block, SWFByteOutputMethod method, void *data)
90 {
91 	int i, ichar, len;
92 	SWFVideoFrame frame = (SWFVideoFrame)block;
93 	SWFVideoStream stream;
94 	SWFInput input;
95 
96 	if(!block)
97 		return;
98 
99 	input = FLVTag_getPayloadInput(&frame->tag);
100 	if(input == NULL)
101 		return;
102 	len = SWFInput_length(input);
103 	stream = frame->stream;
104 
105 	methodWriteUInt16(CHARACTERID(stream),method, data);
106 	methodWriteUInt16(frame->frameNum, method, data);
107 
108 	for (i = 0; i < len; i++) {
109 		ichar = SWFInput_getChar(input);
110 		method(ichar, data);
111 	}
112 }
113 
114 /*
115  * embedes a video frame in a SWFBlock object
116  * This function reads a video frame for a FLV source and embeds it in a
117  * SWFBlock object.
118  */
119 SWFBlock
SWFVideoStream_getVideoFrame(SWFVideoStream stream)120 SWFVideoStream_getVideoFrame(SWFVideoStream stream /* associated video stream */) {
121 	SWFVideoFrame block;
122 	int frame;
123 
124 	if(!stream->embedded)
125 		return NULL;
126 
127 	if(stream->frame >= stream->numFrames)
128 	{
129 		SWF_warn("SWFVideoStream_getVideoFrame: frame %i, numFrames %i\n",
130 			stream->frame, stream->numFrames);
131 		return NULL;
132 	}
133 
134 	if(stream->frame < stream->framesLoaded)
135 		return NULL;
136 
137 	block = (SWFVideoFrame) malloc(sizeof(struct SWFVideoFrame_s));
138 
139 	/* If malloc failed, return NULL to signify this */
140 	if (NULL == block)
141 		return NULL;
142 
143 	SWFBlockInit((SWFBlock)block);
144 
145 	BLOCK(block)->complete = completeSWFVideoFrame;
146 	BLOCK(block)->writeBlock = writeSWFVideoFrameToMethod;
147 	BLOCK(block)->dtor = NULL;
148 	BLOCK(block)->type = SWF_VIDEOFRAME;
149 
150 	block->stream = stream;
151 	if(stream->lastTag != NULL && stream->lastFrame < stream->frame)
152 	{
153 		frame = stream->lastFrame;
154 	}
155 	else
156 	{
157 		stream->lastTag = NULL;
158 		frame = -1;
159 	}
160 
161 	do {
162 		if(FLVStream_nextTag(stream->flv, &block->tag, stream->lastTag))
163 		{
164 			free(block);
165 			return NULL;
166 		}
167 		stream->lastTag = &block->tag;
168 		if(block->tag.tagType == FLV_VIDEOTAG)
169 		{
170 			frame++;
171 		}
172 	} while (frame != stream->frame);
173 
174 	block->frameNum = stream->frame;
175 	stream->lastFrame = stream->frame;
176 	stream->framesLoaded = stream->frame + 1;
177 	return (SWFBlock)block;
178 }
179 
180 
setH263CustomDimension(SWFVideoStream stream,SWFInput input,int flags)181 static int setH263CustomDimension(SWFVideoStream stream, SWFInput input, int flags)
182 {
183 	int ichar, rshift, mask;
184 	int (*method) (SWFInput stream);
185 
186 	SWFInput_seek(input, -1, SEEK_CUR);
187 	if(flags == 0) {
188 		method = SWFInput_getChar;
189 		rshift = 7;
190 		mask = 0xff;
191 	}
192 	else if (flags == 1) {
193 		method = SWFInput_getUInt16_BE;
194 		rshift = 15;
195 		mask = 0xffff;
196 	}
197 	else
198 		return -1;
199 
200 	ichar = method(input);
201 	stream->width = ( (ichar << 1) & mask );
202 
203 	ichar = method(input);
204 	stream->width |= ( (ichar >> rshift) & mask );
205 	stream->height = ( (ichar << 1) & mask );
206 
207 	ichar = method(input);
208 	stream->height |= ( (ichar >> rshift) & mask );
209 
210 	return 0;
211 }
212 
setH263StreamDimension(SWFVideoStream stream,FLVTag * tag)213 static int setH263StreamDimension(SWFVideoStream stream, FLVTag *tag)
214 {
215 	SWFInput input;
216 	int ichar, flags;
217 
218 	input = FLVTag_getPayloadInput(tag);
219 	if(input == NULL)
220 		return -1;
221 
222 	/* skip:
223 	 * pictureStartCode UB[17] 2 byte ( 1 bit remaining )
224 	 * Version UB[5]
225 	 * Temporal Reference UB[8]
226 	 * Picture Size UB[3]
227 	 * */
228 	SWFInput_seek(input, 2, SEEK_CUR);
229 	ichar = SWFInput_getUInt16_BE(input);
230 
231 		/* 3-bit flag */
232 	flags = ((0x0003 & ichar) << 1);
233 
234 	ichar = SWFInput_getChar(input);
235 	flags |= ((0x80 & ichar) >> 7);
236 
237 	switch(flags)
238 	{
239 		case 6:
240 			stream->width = 160;
241 			stream->height = 120;
242 			return 0;
243 
244 		case 5:
245 			stream->width = 320;
246 			stream->height = 240;
247 			return 0;
248 
249 		case 4:
250 			stream->width = 128;
251 			stream->height = 96;
252 			return 0;
253 
254 		case 3:
255 			stream->width = 176;
256 			stream->height = 144;
257 			return 0;
258 
259 		case 2:
260 			stream->width = 352;
261 			stream->height = 288;
262 			return 0;
263 
264 		default:
265 			return setH263CustomDimension(stream, input, flags);
266 	}
267 }
268 
setScreenStreamDimension(SWFVideoStream stream,FLVTag * tag)269 static int setScreenStreamDimension(SWFVideoStream stream, FLVTag *tag)
270 {
271 	unsigned int ui16 = 0;
272 	int ic;
273 	SWFInput input;
274 
275 	input = FLVTag_getPayloadInput(tag);
276 	if(input == NULL)
277 		return -1;
278 
279 	/* Skip 1 byte.
280 	 * The first nibble in that byte is frame type,
281 	 * which could be either 1 for keyframe or 2 for inter frame,
282 	 * the next nibble (lower 4 bits) is codec id which should
283 	 * always be 3 for screen video. */
284 	ic  = SWFInput_getChar(input);
285 
286 	ic = SWFInput_getChar(input);
287 	if(ic >= 0)
288 		ui16 = ic << 8;
289 
290 	ic = SWFInput_getChar(input);
291 	if(ic >= 0)
292 		ui16 |= ic;
293 
294 	stream->width = ui16 & 0x0fff;
295 
296 	ic = SWFInput_getChar(input);
297 	if(ic >= 0)
298 		ui16 = ic << 8;
299 
300 	ic = SWFInput_getChar(input);
301 	if(ic >= 0)
302 		ui16 |= ic;
303 
304 	stream->height = ui16 & 0x0fff;
305 
306 	return 0;
307 
308 }
309 
310 
writeSWFVideoStreamToMethod(SWFBlock block,SWFByteOutputMethod method,void * data)311 void writeSWFVideoStreamToMethod(SWFBlock block, SWFByteOutputMethod method, void *data)
312 {
313 	SWFVideoStream stream = (SWFVideoStream)block;
314 
315 	methodWriteUInt16(CHARACTERID(stream), method, data);
316 	methodWriteUInt16(stream->numFrames, method, data);
317 	methodWriteUInt16(stream->width, method, data);
318 	methodWriteUInt16(stream->height, method, data);
319 
320 	if(stream->embedded) {
321 		method(stream->smoothingFlag, data);
322 		method(stream->codecId, data);
323 	}
324 	/*
325 	 * in case of streaming video, flags and codec id must be 0x0
326 	 * if not 0 it craches player + browser
327 	 */
328 	else {
329 		method(0x0, data);
330 		method(0x0, data);
331 	}
332 };
333 
334 
completeSWFVideoStream(SWFBlock block)335 int completeSWFVideoStream(SWFBlock block) {
336 	return VIDEOFRAME_BLOCK_SIZE;
337 }
338 
339 
destroySWFVideoStream(SWFVideoStream stream)340 void destroySWFVideoStream(SWFVideoStream stream) {
341 	destroySWFCharacter((SWFCharacter) stream);
342 }
343 
344 
setVP6Dimension(SWFVideoStream stream,FLVTag * tag)345 static int setVP6Dimension(SWFVideoStream stream, FLVTag *tag)
346 {
347 	SWFInput input;
348 	int ichar;
349 	int render_x, render_y;
350 
351 	input = FLVTag_getPayloadInput(tag);
352 	if(input == NULL)
353 		return -1;
354 
355 
356 	ichar = SWFInput_getChar(input);
357 	if(ichar == EOF)
358 		return -1;
359 
360 	if(ichar >> 7)
361 	{
362 		SWF_warn("setVP6Dimension: first frame is interframe\n");
363 		return -1;
364 	}
365 
366 	if(ichar & 1)
367 	{
368 		SWF_warn("setVP6Dimension: VP60!\n");
369 		return -1;
370 	}
371 
372 	ichar = SWFInput_getChar(input);
373 	ichar = SWFInput_getChar(input);
374 	render_x = SWFInput_getChar(input);
375 	render_y = SWFInput_getChar(input);
376 
377 	stream->width = render_x * 16;
378 	stream->height = render_y * 16;
379 	return 0;
380 }
381 
382 
setStreamProperties(SWFVideoStream stream)383 static int setStreamProperties(SWFVideoStream stream)
384 {
385 	int ret;
386 	FLVTag tag, *tag_p = NULL;
387 
388 	stream->numFrames = FLVStream_getNumFrames(stream->flv, FLV_VIDEOTAG);
389 
390 	while((ret = FLVStream_nextTag(stream->flv, &tag, tag_p)) == 0)
391 	{
392 		if(tag.tagType == FLV_VIDEOTAG)
393 			break;
394 
395 		tag_p = &tag;
396 	}
397 
398 	if(ret < 0)
399 		return -1;
400 
401 	stream->codecId = tag.hdr.video.codec;
402 	switch (stream->codecId) {
403 		case VIDEO_CODEC_H263:
404 			ret = setH263StreamDimension(stream, &tag);
405 			stream->smoothingFlag = VIDEO_SMOOTHING;
406 			break;
407 		case VIDEO_CODEC_SCREEN:
408 			ret = setScreenStreamDimension(stream, &tag);
409 			stream->smoothingFlag = 0;
410 			break;
411 		case VIDEO_CODEC_VP6:
412 			setVP6Dimension(stream, &tag);
413 			stream->smoothingFlag = VIDEO_SMOOTHING;
414 			ret = 0;
415 			break;
416 		case VIDEO_CODEC_VP6A:
417 		case VIDEO_CODEC_SCREEN2:
418 			SWF_warn("setStreamProperties: automatic dimension setting is not working with this codec yet!\n");
419 			ret = 0;
420 			break;
421 		default:
422 			SWF_warn("Unknown Codec %x\n", stream->codecId);
423 			ret = -1;
424 	}
425 	return ret;
426 }
427 
428 
onPlace(SWFDisplayItem item,SWFBlockList blocklist)429 static int onPlace(SWFDisplayItem item, SWFBlockList blocklist)
430 {
431 	SWFVideoStream stream = (SWFVideoStream)SWFDisplayItem_getCharacter(item);
432 	SWFBlock video = SWFVideoStream_getVideoFrame(stream);
433 	if(video == NULL)
434 		return 0;
435         SWFBlockList_addBlock(blocklist, video);
436 	stream->firstFrame = 0;
437 	return 1;
438 }
439 
onFrame(SWFDisplayItem item,SWFBlockList blocklist)440 static int onFrame(SWFDisplayItem item, SWFBlockList blocklist)
441 {
442 	SWFPlaceObject2Block placeVideo;
443 	SWFVideoStream stream;
444 	SWFBlock video = NULL;
445 
446 	/* if item is new -> onInit already inserted a frame */
447 	if(item->flags != 0)
448 		return 0;
449 
450 	stream = (SWFVideoStream)SWFDisplayItem_getCharacter(item);
451 	if(stream->mode == SWFVIDEOSTREAM_MODE_MANUAL &&
452 		stream->addFrame == 0)
453 		return 0;
454 
455 	if(stream->mode != SWFVIDEOSTREAM_MODE_MANUAL)
456 		stream->frame++;
457 
458 	if(stream->frame >= stream->framesLoaded)
459 	{
460 		video = SWFVideoStream_getVideoFrame(stream);
461 		if(video == NULL)
462 			return 0;
463 	}
464 
465 	placeVideo = newSWFPlaceObject2Block(item->depth);
466 	SWFPlaceObject2Block_setRatio(placeVideo, stream->frame);
467 	SWFPlaceObject2Block_setMove(placeVideo);
468 	SWFBlockList_addBlock(blocklist, (SWFBlock)placeVideo);
469 	if(video != NULL)
470 		SWFBlockList_addBlock(blocklist, video);
471 
472 	stream->addFrame = 0;
473 	return 2;
474 }
475 
476 
477 /**
478  * Seek within a video stream.
479  *
480  * This functions allows seeking in video stream. Semantics
481  * like SWFInput_seek();
482  *
483  * Works only with SWFVIDEOSTREAM_MODE_MANUAL!
484  * @return old video position (frame)
485  */
SWFVideoStream_seek(SWFVideoStream stream,int frame,int whence)486 int SWFVideoStream_seek(SWFVideoStream stream, int frame, int whence)
487 {
488 	int old, pos;
489 
490 	if(stream == NULL || stream->embedded == 0)
491 		return -1;
492 
493 	if(stream->mode != SWFVIDEOSTREAM_MODE_MANUAL)
494 		return -1;
495 
496 	old = stream->frame;
497 	switch(whence)
498 	{
499 	case SEEK_SET:
500 		if(frame < 0 || frame >= stream->numFrames)
501 			return -1;
502 		stream->frame = frame;
503 		break;
504 	case SEEK_END:
505 		if(frame < 0 || frame >= stream->numFrames)
506  			return -1;
507 		stream->frame = stream->numFrames - frame;
508 		break;
509 	case SEEK_CUR:
510 		pos = stream->frame + frame;
511 		if(pos < 0 || pos >= stream->numFrames)
512 			return -1;
513 		break;
514 	default:
515 		return -1;
516 	}
517 	stream->addFrame = 1;
518 	return old;
519 }
520 
521 /**
522  * Display next video frame
523  *
524  * Works only with embedded video streams.
525  *
526  * @return -1 if an error happend.
527  */
SWFVideoStream_nextFrame(SWFVideoStream stream)528 int SWFVideoStream_nextFrame(SWFVideoStream stream)
529 {
530 	if(stream == NULL || !stream->embedded)
531 		return -1;
532 
533 	if(stream->mode != SWFVIDEOSTREAM_MODE_MANUAL)
534 		return -1;
535 
536 	if(stream->addFrame == 1 || stream->firstFrame == 1)
537 		return 0;
538 
539 	stream->addFrame = 1;
540 	stream->frame++;
541 	return 0;
542 }
543 
544 /**
545  * switch video stream frame mode
546  * If the mode == SWFVIDEOSTREAM_MODE_AUTO (default) every swfmovie
547  * frame a video frame is added.
548  * In SWFVIDEOSTREAM_MODE_MANUAL mode, the user needs to call
549  * SWFVideoStream_nextFrame() to change the video's frame.
550  *
551  * Works only with embedded video streams.
552  *
553  * @return the previous mode or -1 if an invalid mode was passed.
554  */
SWFVideoStream_setFrameMode(SWFVideoStream stream,int mode)555 int SWFVideoStream_setFrameMode(SWFVideoStream stream, int mode)
556 {
557 	int oldmode;
558 	if(stream == NULL || !stream->embedded)
559 		return -1;
560 
561 	oldmode = stream->mode;
562 	switch(mode)
563 	{
564 	case SWFVIDEOSTREAM_MODE_AUTO:
565 		stream->mode = mode;
566 		return oldmode;
567 	case SWFVIDEOSTREAM_MODE_MANUAL:
568 		stream->mode = mode;
569 		return oldmode;
570 	default:
571 		SWF_warn("SWFVideoStream_setFrameMode: mode %i is unknown", mode);
572 		return -1;
573 	}
574 }
575 
576 /*
577  * create a new SWFVideoSteam object
578  * This function creates a new videostream object from a FLV-file.
579  * Takes a SWFInput object as argument.
580  * Be aware: You need to keep the SWFInput valid until the movie is generated via save() or output()!
581  */
582 SWFVideoStream
newSWFVideoStream_fromInput(SWFInput input)583 newSWFVideoStream_fromInput(SWFInput input) {
584 
585 	SWFBlock block;
586 	SWFVideoStream stream;
587 
588 	if(!input)
589 		return NULL;
590 
591 	stream = (SWFVideoStream)malloc(sizeof(struct SWFVideoStream_s));
592 	if(!stream)
593 		return NULL;
594 	block = (SWFBlock)stream;
595 
596 	SWFCharacterInit((SWFCharacter)stream);
597 	CHARACTERID(stream) = ++SWF_gNumCharacters;
598  	((SWFCharacter)stream)->onFrame = onFrame;
599  	((SWFCharacter)stream)->onPlace = onPlace;
600 	block->type = SWF_DEFINEVIDEOSTREAM;
601 	block->writeBlock = writeSWFVideoStreamToMethod;
602 	block->complete = completeSWFVideoStream;
603 	block->dtor = (destroySWFBlockMethod)destroySWFVideoStream;
604 
605 	stream->flv = FLVStream_fromInput(input);
606 	if(stream->flv == NULL)
607 	{
608 		free(stream);
609 		return NULL;
610 	}
611 	stream->lastTag = NULL;
612 	stream->lastFrame = 0;
613 	stream->frame = 0;
614 	stream->embedded = 1;
615 	stream->mode = SWFVIDEOSTREAM_MODE_AUTO;
616 	stream->addFrame = 0;
617 	stream->framesLoaded = 0;
618 	stream->firstFrame = 1;
619 	stream->width = VIDEO_DEF_WIDTH;
620 	stream->height = VIDEO_DEF_HEIGHT;
621 	if (setStreamProperties(stream) < 0)
622 	{
623 		free(stream);
624 		return NULL;
625 	}
626 	return stream;
627 }
628 
629 /*
630  * creates a new SWFVideoStream object
631  * This function creates an empty videostream object. This object can be adressed via
632  * ActionScript to connect and display a streamed video (progessive download / rtmp).
633  */
newSWFVideoStream()634 SWFVideoStream newSWFVideoStream() {
635 	SWFBlock block;
636 	SWFVideoStream stream = (SWFVideoStream)malloc(sizeof(struct SWFVideoStream_s));
637         if(!stream)
638                 return NULL;
639         block = (SWFBlock)stream;
640 
641 	SWFCharacterInit((SWFCharacter)stream);
642         CHARACTERID(stream) = ++SWF_gNumCharacters;
643 
644 	block->type = SWF_DEFINEVIDEOSTREAM;
645         block->writeBlock = writeSWFVideoStreamToMethod;
646         block->complete = completeSWFVideoStream;
647         block->dtor = (destroySWFBlockMethod)destroySWFVideoStream;
648 
649 	stream->flv = NULL;
650 	stream->lastTag = NULL;
651         stream->frame = 0;
652         stream->embedded = 0;
653 	stream->numFrames = -1;
654 
655 	stream->width = VIDEO_DEF_WIDTH;
656 	stream->height = VIDEO_DEF_HEIGHT;
657 	return stream;
658 }
659 
660 
661 /*
662  * create a new SWFVideoSteam object
663  * This function creates a new videostream object from a FLV-file.
664  * Takes a FILE * as argument.
665  * Be aware: You need to keep the FILE open until the movie is generated via save() or output()!
666  */
newSWFVideoStream_fromFile(FILE * f)667 SWFVideoStream newSWFVideoStream_fromFile(FILE *f /* FILE pointer to a FLV-file */) {
668 	return newSWFVideoStream_fromInput(newSWFInput_file(f));
669 }
670 
671 
672 /*
673  * sets video dimension
674  * This function set width and height for streamed videos
675  * Works only _streamed_ videos (progressive download or rtmp)
676  */
SWFVideoStream_setDimension(SWFVideoStream stream,int width,int height)677 void SWFVideoStream_setDimension(SWFVideoStream stream /* stream object */,
678 				int width, /* width in px */
679 				int height /* height in px */)
680 {
681 	if(!stream->embedded) {
682 		stream->width = width;
683 		stream->height = height;
684 	}
685 }
686 
687 
688 /*
689  * returns the number of video-frames
690  * This function returns the number of video-frames of a SWFVideoStream.
691  * Works only for embedded streams!
692  */
SWFVideoStream_getNumFrames(SWFVideoStream stream)693 int SWFVideoStream_getNumFrames(SWFVideoStream stream /* Embedded video stream */) {
694 	if(!stream)
695 		return -1;
696 	return stream->numFrames;
697 }
698 
699 /*
700  * returns 1 if embedded video stream has also an audio stream
701  * This functions test if the embedded FLV stream also has audio-data.
702  */
SWFVideoStream_hasAudio(SWFVideoStream stream)703 int SWFVideoStream_hasAudio(SWFVideoStream stream /* Embedded video stream */)
704 {
705 	if(stream != NULL && stream->flv != NULL && stream->flv->has_audio)
706 		return 1;
707 
708 	return 0;
709 }
710