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 /* special case: skip 1 byte */
280 ic = SWFInput_getChar(input);
281
282 ic = SWFInput_getChar(input);
283 if(ic >= 0)
284 ui16 = ic << 8;
285
286 ic = SWFInput_getChar(input);
287 if(ic >= 0)
288 ui16 |= ic;
289
290 stream->width = ui16 & 0x0fff;
291
292 ic = SWFInput_getChar(input);
293 if(ic >= 0)
294 ui16 = ic << 8;
295
296 ic = SWFInput_getChar(input);
297 if(ic >= 0)
298 ui16 |= ic;
299
300 stream->height = ui16 & 0x0fff;
301
302 return 0;
303
304 }
305
306
writeSWFVideoStreamToMethod(SWFBlock block,SWFByteOutputMethod method,void * data)307 void writeSWFVideoStreamToMethod(SWFBlock block, SWFByteOutputMethod method, void *data)
308 {
309 SWFVideoStream stream = (SWFVideoStream)block;
310
311 methodWriteUInt16(CHARACTERID(stream), method, data);
312 methodWriteUInt16(stream->numFrames, method, data);
313 methodWriteUInt16(stream->width, method, data);
314 methodWriteUInt16(stream->height, method, data);
315
316 if(stream->embedded) {
317 method(stream->smoothingFlag, data);
318 method(stream->codecId, data);
319 }
320 /*
321 * in case of streaming video, flags and codec id must be 0x0
322 * if not 0 it craches player + browser
323 */
324 else {
325 method(0x0, data);
326 method(0x0, data);
327 }
328 };
329
330
completeSWFVideoStream(SWFBlock block)331 int completeSWFVideoStream(SWFBlock block) {
332 (void) block;
333 return VIDEOFRAME_BLOCK_SIZE;
334 }
335
336
destroySWFVideoStream(SWFVideoStream stream)337 void destroySWFVideoStream(SWFVideoStream stream) {
338 destroySWFCharacter((SWFCharacter) stream);
339 }
340
341
setVP6Dimension(SWFVideoStream stream,FLVTag * tag)342 static int setVP6Dimension(SWFVideoStream stream, FLVTag *tag)
343 {
344 SWFInput input;
345 int ichar;
346 int render_x, render_y;
347
348 input = FLVTag_getPayloadInput(tag);
349 if(input == NULL)
350 return -1;
351
352
353 ichar = SWFInput_getChar(input);
354 if(ichar == EOF)
355 return -1;
356
357 if(ichar >> 7)
358 {
359 SWF_warn("setVP6Dimension: first frame is interframe\n");
360 return -1;
361 }
362
363 if(ichar & 1)
364 {
365 SWF_warn("setVP6Dimension: VP60!\n");
366 return -1;
367 }
368
369 ichar = SWFInput_getChar(input);
370 ichar = SWFInput_getChar(input);
371 render_x = SWFInput_getChar(input);
372 render_y = SWFInput_getChar(input);
373
374 stream->width = render_x * 16;
375 stream->height = render_y * 16;
376 return 0;
377 }
378
379
setStreamProperties(SWFVideoStream stream)380 static int setStreamProperties(SWFVideoStream stream)
381 {
382 int ret;
383 FLVTag tag, *tag_p = NULL;
384
385 stream->numFrames = FLVStream_getNumFrames(stream->flv, FLV_VIDEOTAG);
386
387 while((ret = FLVStream_nextTag(stream->flv, &tag, tag_p)) == 0)
388 {
389 if(tag.tagType == FLV_VIDEOTAG)
390 break;
391
392 tag_p = &tag;
393 }
394
395 if(ret < 0)
396 return -1;
397
398 stream->codecId = tag.hdr.video.codec;
399 switch (stream->codecId) {
400 case VIDEO_CODEC_H263:
401 ret = setH263StreamDimension(stream, &tag);
402 stream->smoothingFlag = VIDEO_SMOOTHING;
403 break;
404 case VIDEO_CODEC_SCREEN:
405 ret = setScreenStreamDimension(stream, &tag);
406 stream->smoothingFlag = 0;
407 break;
408 case VIDEO_CODEC_VP6:
409 setVP6Dimension(stream, &tag);
410 stream->smoothingFlag = VIDEO_SMOOTHING;
411 ret = 0;
412 break;
413 case VIDEO_CODEC_VP6A:
414 case VIDEO_CODEC_SCREEN2:
415 SWF_warn("setStreamProperties: automatic dimension setting is not working with this codec yet!\n");
416 ret = 0;
417 break;
418 default:
419 SWF_warn("Unknown Codec %x\n", stream->codecId);
420 ret = -1;
421 }
422 return ret;
423 }
424
425
onPlace(SWFDisplayItem item,SWFBlockList blocklist)426 static int onPlace(SWFDisplayItem item, SWFBlockList blocklist)
427 {
428 SWFVideoStream stream = (SWFVideoStream)SWFDisplayItem_getCharacter(item);
429 SWFBlock video = SWFVideoStream_getVideoFrame(stream);
430 if(video == NULL)
431 return 0;
432 SWFBlockList_addBlock(blocklist, video);
433 stream->firstFrame = 0;
434 return 1;
435 }
436
onFrame(SWFDisplayItem item,SWFBlockList blocklist)437 static int onFrame(SWFDisplayItem item, SWFBlockList blocklist)
438 {
439 SWFPlaceObject2Block placeVideo;
440 SWFVideoStream stream;
441 SWFBlock video = NULL;
442
443 /* if item is new -> onInit already inserted a frame */
444 if(item->flags != 0)
445 return 0;
446
447 stream = (SWFVideoStream)SWFDisplayItem_getCharacter(item);
448 if(stream->mode == SWFVIDEOSTREAM_MODE_MANUAL &&
449 stream->addFrame == 0)
450 return 0;
451
452 if(stream->mode != SWFVIDEOSTREAM_MODE_MANUAL)
453 stream->frame++;
454
455 if(stream->frame >= stream->framesLoaded)
456 {
457 video = SWFVideoStream_getVideoFrame(stream);
458 if(video == NULL)
459 return 0;
460 }
461
462 placeVideo = newSWFPlaceObject2Block(item->depth);
463 SWFPlaceObject2Block_setRatio(placeVideo, stream->frame);
464 SWFPlaceObject2Block_setMove(placeVideo);
465 SWFBlockList_addBlock(blocklist, (SWFBlock)placeVideo);
466 if(video != NULL)
467 SWFBlockList_addBlock(blocklist, video);
468
469 stream->addFrame = 0;
470 return 2;
471 }
472
473
474 /**
475 * Seek within a video stream.
476 *
477 * This functions allows seeking in video stream. Semantics
478 * like SWFInput_seek();
479 *
480 * Works only with SWFVIDEOSTREAM_MODE_MANUAL!
481 * @return old video position (frame)
482 */
SWFVideoStream_seek(SWFVideoStream stream,int frame,int whence)483 int SWFVideoStream_seek(SWFVideoStream stream, int frame, int whence)
484 {
485 int old, pos;
486
487 if(stream == NULL || stream->embedded == 0)
488 return -1;
489
490 if(stream->mode != SWFVIDEOSTREAM_MODE_MANUAL)
491 return -1;
492
493 old = stream->frame;
494 switch(whence)
495 {
496 case SEEK_SET:
497 if(frame < 0 || frame >= stream->numFrames)
498 return -1;
499 stream->frame = frame;
500 break;
501 case SEEK_END:
502 if(frame < 0 || frame >= stream->numFrames)
503 return -1;
504 stream->frame = stream->numFrames - frame;
505 break;
506 case SEEK_CUR:
507 pos = stream->frame + frame;
508 if(pos < 0 || pos >= stream->numFrames)
509 return -1;
510 break;
511 default:
512 return -1;
513 }
514 stream->addFrame = 1;
515 return old;
516 }
517
518 /**
519 * Display next video frame
520 *
521 * Works only with embedded video streams.
522 *
523 * @return -1 if an error happend.
524 */
SWFVideoStream_nextFrame(SWFVideoStream stream)525 int SWFVideoStream_nextFrame(SWFVideoStream stream)
526 {
527 if(stream == NULL || !stream->embedded)
528 return -1;
529
530 if(stream->mode != SWFVIDEOSTREAM_MODE_MANUAL)
531 return -1;
532
533 if(stream->addFrame == 1 || stream->firstFrame == 1)
534 return 0;
535
536 stream->addFrame = 1;
537 stream->frame++;
538 return 0;
539 }
540
541 /**
542 * switch video stream frame mode
543 * If the mode == SWFVIDEOSTREAM_MODE_AUTO (default) every swfmovie
544 * frame a video frame is added.
545 * In SWFVIDEOSTREAM_MODE_MANUAL mode, the user needs to call
546 * SWFVideoStream_nextFrame() to change the video's frame.
547 *
548 * Works only with embedded video streams.
549 *
550 * @return the previous mode or -1 if an invalid mode was passed.
551 */
SWFVideoStream_setFrameMode(SWFVideoStream stream,int mode)552 int SWFVideoStream_setFrameMode(SWFVideoStream stream, int mode)
553 {
554 int oldmode;
555 if(stream == NULL || !stream->embedded)
556 return -1;
557
558 oldmode = stream->mode;
559 switch(mode)
560 {
561 case SWFVIDEOSTREAM_MODE_AUTO:
562 stream->mode = mode;
563 return oldmode;
564 case SWFVIDEOSTREAM_MODE_MANUAL:
565 stream->mode = mode;
566 return oldmode;
567 default:
568 SWF_warn("SWFVideoStream_setFrameMode: mode %i is unknown", mode);
569 return -1;
570 }
571 }
572
573 /*
574 * create a new SWFVideoSteam object
575 * This function creates a new videostream object from a FLV-file.
576 * Takes a SWFInput object as argument.
577 * Be aware: You need to keep the SWFInput valid until the movie is generated via save() or output()!
578 */
579 SWFVideoStream
newSWFVideoStream_fromInput(SWFInput input)580 newSWFVideoStream_fromInput(SWFInput input) {
581
582 SWFBlock block;
583 SWFVideoStream stream;
584
585 if(!input)
586 return NULL;
587
588 stream = (SWFVideoStream)malloc(sizeof(struct SWFVideoStream_s));
589 if(!stream)
590 return NULL;
591 block = (SWFBlock)stream;
592
593 SWFCharacterInit((SWFCharacter)stream);
594 CHARACTERID(stream) = ++SWF_gNumCharacters;
595 ((SWFCharacter)stream)->onFrame = onFrame;
596 ((SWFCharacter)stream)->onPlace = onPlace;
597 block->type = SWF_DEFINEVIDEOSTREAM;
598 block->writeBlock = writeSWFVideoStreamToMethod;
599 block->complete = completeSWFVideoStream;
600 block->dtor = (destroySWFBlockMethod)destroySWFVideoStream;
601
602 stream->flv = FLVStream_fromInput(input);
603 if(stream->flv == NULL)
604 {
605 free(stream);
606 return NULL;
607 }
608 stream->lastTag = NULL;
609 stream->lastFrame = 0;
610 stream->frame = 0;
611 stream->embedded = 1;
612 stream->mode = SWFVIDEOSTREAM_MODE_AUTO;
613 stream->addFrame = 0;
614 stream->framesLoaded = 0;
615 stream->firstFrame = 1;
616 stream->width = VIDEO_DEF_WIDTH;
617 stream->height = VIDEO_DEF_HEIGHT;
618 if (setStreamProperties(stream) < 0)
619 {
620 free(stream);
621 return NULL;
622 }
623 return stream;
624 }
625
626 /*
627 * creates a new SWFVideoStream object
628 * This function creates an empty videostream object. This object can be adressed via
629 * ActionScript to connect and display a streamed video (progessive download / rtmp).
630 */
newSWFVideoStream()631 SWFVideoStream newSWFVideoStream() {
632 SWFBlock block;
633 SWFVideoStream stream = (SWFVideoStream)malloc(sizeof(struct SWFVideoStream_s));
634 if(!stream)
635 return NULL;
636 block = (SWFBlock)stream;
637
638 SWFCharacterInit((SWFCharacter)stream);
639 CHARACTERID(stream) = ++SWF_gNumCharacters;
640
641 block->type = SWF_DEFINEVIDEOSTREAM;
642 block->writeBlock = writeSWFVideoStreamToMethod;
643 block->complete = completeSWFVideoStream;
644 block->dtor = (destroySWFBlockMethod)destroySWFVideoStream;
645
646 stream->flv = NULL;
647 stream->lastTag = NULL;
648 stream->frame = 0;
649 stream->embedded = 0;
650 stream->numFrames = -1;
651
652 stream->width = VIDEO_DEF_WIDTH;
653 stream->height = VIDEO_DEF_HEIGHT;
654 return stream;
655 }
656
657
658 /*
659 * create a new SWFVideoSteam object
660 * This function creates a new videostream object from a FLV-file.
661 * Takes a FILE * as argument.
662 * Be aware: You need to keep the FILE open until the movie is generated via save() or output()!
663 */
newSWFVideoStream_fromFile(FILE * f)664 SWFVideoStream newSWFVideoStream_fromFile(FILE *f /* FILE pointer to a FLV-file */) {
665 return newSWFVideoStream_fromInput(newSWFInput_file(f));
666 }
667
668
669 /*
670 * sets video dimension
671 * This function set width and height for streamed videos
672 * Works only _streamed_ videos (progressive download or rtmp)
673 */
SWFVideoStream_setDimension(SWFVideoStream stream,int width,int height)674 void SWFVideoStream_setDimension(SWFVideoStream stream /* stream object */,
675 int width, /* width in px */
676 int height /* height in px */)
677 {
678 if(!stream->embedded) {
679 stream->width = width;
680 stream->height = height;
681 }
682 }
683
684
685 /*
686 * returns the number of video-frames
687 * This function returns the number of video-frames of a SWFVideoStream.
688 * Works only for embedded streams!
689 */
SWFVideoStream_getNumFrames(SWFVideoStream stream)690 int SWFVideoStream_getNumFrames(SWFVideoStream stream /* Embedded video stream */) {
691 if(!stream)
692 return -1;
693 return stream->numFrames;
694 }
695
696 /*
697 * returns 1 if embedded video stream has also an audio stream
698 * This functions test if the embedded FLV stream also has audio-data.
699 */
SWFVideoStream_hasAudio(SWFVideoStream stream)700 int SWFVideoStream_hasAudio(SWFVideoStream stream /* Embedded video stream */)
701 {
702 if(stream != NULL && stream->flv != NULL && stream->flv->has_audio)
703 return 1;
704
705 return 0;
706 }
707