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