1 /*
2    Ming, an SWF output library
3    Copyright (C) 2002  Opaque Industries - http://www.opaque.net/
4 
5    2.2.2007 Klaus Rechert
6 
7    This library is free software; you can redistribute it and/or
8    modify it under the terms of the GNU Lesser General Public
9    License as published by the Free Software Foundation; either
10    version 2.1 of the License, or (at your option) any later version.
11 
12    This library 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 GNU
15    Lesser General Public License for more details.
16 
17    You should have received a copy of the GNU Lesser General Public
18    License along with this library; if not, write to the Free Software
19    Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
20 
21 */
22 #include <stdio.h>
23 #include <stdlib.h>
24 
25 #include "input.h"
26 #include "libming.h"
27 #include "error.h"
28 #include "flv.h"
29 
readAudioHdr(FLVStream * flv,FLVTag * tag)30 static inline int readAudioHdr(FLVStream *flv, FLVTag *tag)
31 {
32 	int ichar;
33 	ichar = SWFInput_getChar(flv->input);
34 	if(ichar == EOF)
35 		return -1;
36 	tag->hdr.audio.format = (0xf0 & ichar);
37 	tag->hdr.audio.samplingRate = (0xc & ichar);
38 	tag->hdr.audio.sampleSize = (0x2 & ichar);
39 	tag->hdr.audio.channel = 0x1 & ichar;
40 	return 0;
41 }
42 
readVideoHdr(FLVStream * flv,FLVTag * tag)43 static inline int readVideoHdr(FLVStream *flv, FLVTag *tag)
44 {
45 	int ichar;
46 	ichar = SWFInput_getChar(flv->input);
47 	if(ichar == EOF)
48 		return -1;
49 
50 	tag->hdr.video.frameType = (0xf0 & ichar);
51 	tag->hdr.video.codec = 0x0f & ichar;
52 	return 0;
53 }
54 
FLVStream_skipTagData(FLVStream * flv,FLVTag * tag)55 long FLVStream_skipTagData(FLVStream *flv, FLVTag *tag)
56 {
57 	if(flv == NULL || tag == NULL)
58 		return -1;
59 
60 	SWFInput_seek(flv->input, tag->data + tag->dataSize + 4, SEEK_SET);
61 
62 	return SWFInput_tell(flv->input);
63 }
64 
destroyFLVStream(FLVStream * flv)65 void destroyFLVStream(FLVStream *flv)
66 {
67 	free(flv);
68 }
69 
FLVStream_fromInput(SWFInput input)70 FLVStream *FLVStream_fromInput(SWFInput input)
71 {
72 	int ichar;
73 	unsigned long ulchar;
74 	FLVStream *flv;
75 
76 	if(!input)
77 		return NULL;
78 
79 	ichar = SWFInput_getChar(input);
80 	if(ichar == EOF || ichar != 'F')
81 		return NULL;
82 
83 	ichar = SWFInput_getChar(input);
84 	if(ichar == EOF || ichar != 'L')
85 		return NULL;
86 
87 	ichar = SWFInput_getChar(input);
88 	if(ichar == EOF || ichar != 'V')
89 		return NULL;
90 
91 	flv = (FLVStream *)malloc(sizeof(FLVStream));
92 	if(flv == NULL)
93 		return  NULL;
94 	flv->input = input;
95 
96 	ichar = SWFInput_getChar(input);
97 	if(ichar != EOF)
98 		flv->version = ichar;
99 
100 	flv->has_video = 0;
101 	flv->has_audio = 0;
102 	ichar = SWFInput_getChar(input);
103 	if(ichar != EOF)
104 	{
105 		if(ichar & FLV_AUDIO_PRESENT)
106 			flv->has_audio = 1;
107 
108 		if(ichar & FLV_VIDEO_PRESENT)
109 			flv->has_video = 1;
110 	}
111 
112 	ulchar = SWFInput_getUInt32_BE(input);
113 	flv->offset = ulchar + 4;
114 	flv->stream_start = flv->offset;
115 	return flv;
116 }
117 
FLVStream_rewind(FLVStream * flv)118 void FLVStream_rewind(FLVStream *flv)
119 {
120 	SWFInput_seek(flv->input, flv->offset, SEEK_SET);
121 }
122 
FLVStream_nextTag(FLVStream * flv,FLVTag * tag,FLVTag * prev)123 int FLVStream_nextTag(FLVStream *flv, FLVTag *tag, FLVTag *prev)
124 {
125 	int ichar;
126 	unsigned long ulchar;
127 
128 	if(prev == NULL)
129 		SWFInput_seek(flv->input, flv->offset, SEEK_SET);
130 	else
131 	{
132 		unsigned long off;
133 		if(prev->data < 0)
134 			return -1;
135 
136 		off = prev->data + prev->dataSize + 4;
137 		SWFInput_seek(flv->input, off, SEEK_SET);
138 	}
139 
140 	tag->offset = SWFInput_tell(flv->input);
141 
142 	tag->stream = flv;
143 
144 	ichar = SWFInput_getChar(flv->input);
145 	if(ichar == EOF)
146 		return -1;
147 	if(ichar != FLV_VIDEOTAG && ichar != FLV_AUDIOTAG && ichar != FLV_SCRIPTTAG)
148 	{
149 		SWF_warn("FLV: stream out of sync!\n");
150 		return -1;
151 	}
152 	tag->tagType = ichar;
153 
154 	ulchar = SWFInput_getUInt24_BE(flv->input);
155 	tag->dataSize = ulchar;
156 
157 	ulchar = SWFInput_getUInt24_BE(flv->input);
158 	tag->timeStamp = ulchar;
159 
160 	ulchar = SWFInput_getUInt32_BE(flv->input);
161 	if(ulchar)
162 	{
163 		SWF_warn("FLV: stream out of sync!\n");
164 		return -1;
165 	}
166 
167 	tag->data = SWFInput_tell(flv->input);
168 	if(tag->tagType == FLV_VIDEOTAG)
169 		readVideoHdr(flv, tag);
170 	else if(tag->tagType == FLV_AUDIOTAG)
171 		readAudioHdr(flv, tag);
172 
173 	return 0;
174 }
175 
FLVStream_nextTagType(FLVStream * flv,FLVTag * tag,FLVTag * prev,int type)176 int FLVStream_nextTagType(FLVStream *flv, FLVTag *tag, FLVTag *prev, int type)
177 {
178 
179 	while(FLVStream_nextTag(flv, tag, prev) == 0)
180 	{
181 		if(tag->tagType == type)
182 			return 0;
183 		prev = tag;
184 	}
185 	return -1;
186 
187 }
188 
FLVStream_getDuration(FLVStream * flv,int type)189 unsigned int FLVStream_getDuration(FLVStream *flv, int type)
190 {
191 	unsigned int duration = 0;
192 	FLVTag tag, *p_tag = NULL;
193 
194 	while(FLVStream_nextTag(flv, &tag, p_tag) == 0)
195 	{
196 		if(tag.tagType == type)
197 		{
198 			/* optimistic approach */
199 			duration = tag.timeStamp;
200 		}
201 		p_tag = &tag;
202 	}
203 	return duration;
204 }
205 
FLVStream_getNumFrames(FLVStream * flv,int type)206 int FLVStream_getNumFrames(FLVStream *flv, int type)
207 {
208 	int numFrames = 0;
209 	FLVTag tag, *p_tag = NULL;
210 
211 	while(FLVStream_nextTag(flv, &tag, p_tag) == 0)
212 	{
213 		if(tag.tagType == type)
214 			numFrames++;
215 		p_tag = &tag;
216 	}
217 	return numFrames;
218 }
219 
FLVTag_getPayloadInput(FLVTag * tag)220 SWFInput FLVTag_getPayloadInput(FLVTag *tag)
221 {
222 	int length;
223 	SWFInput input;
224 	if(tag == NULL || tag->stream == NULL)
225 		return NULL;
226 
227 	input = tag->stream->input;
228 
229 	/* screen video needs this extra byte undocumented! */
230 	if(tag->tagType == FLV_VIDEOTAG
231 		&& tag->hdr.video.codec == VIDEO_CODEC_SCREEN)
232 	{
233 		length = tag->dataSize;
234 		SWFInput_seek(input, tag->data, SEEK_SET);
235 	}
236 	else if(tag->tagType == FLV_VIDEOTAG
237                 && tag->hdr.video.codec == VIDEO_CODEC_VP6)
238 	{
239 		length = tag->dataSize - 2;
240 		SWFInput_seek(input, tag->data + 2, SEEK_SET);
241 	}
242 	else /* skip flv-audio/video-data byte */
243 	{
244 		length = tag->dataSize - 1;
245 		SWFInput_seek(input, tag->data + 1, SEEK_SET);
246 	}
247 
248 	return newSWFInput_input(input, length);
249 }
250 
FLVStream_setStreamOffset(FLVStream * flv,unsigned int msecs)251 int FLVStream_setStreamOffset(FLVStream *flv, unsigned int msecs)
252 {
253 	FLVTag tag, *p_tag = NULL;
254 
255 	/* reset stream offset */
256 	flv->offset = flv->stream_start;
257 
258 	while(FLVStream_nextTag(flv, &tag, p_tag) == 0)
259 	{
260 		if(tag.timeStamp >= msecs)
261 		{
262 			flv->offset = tag.offset;
263 			return 0;
264 		}
265 		p_tag = &tag;
266 	}
267 	return -1;
268 }
269 
270