Mercurial > libavformat.hg
annotate ffm.c @ 744:da5b3b9e898e libavformat
Add in many fields that have been added to the Codec structure. This means
that ffm will now carry most of the important fields over between ffserver
and ffmpeg
author | philipjsg |
---|---|
date | Fri, 06 May 2005 03:19:45 +0000 |
parents | af4e24d6310c |
children | dcb459ca11eb |
rev | line source |
---|---|
0 | 1 /* |
2 * FFM (ffserver live feed) encoder and decoder | |
3 * Copyright (c) 2001 Fabrice Bellard. | |
4 * | |
5 * This library is free software; you can redistribute it and/or | |
6 * modify it under the terms of the GNU Lesser General Public | |
7 * License as published by the Free Software Foundation; either | |
8 * version 2 of the License, or (at your option) any later version. | |
9 * | |
10 * This library is distributed in the hope that it will be useful, | |
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of | |
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU | |
13 * Lesser General Public License for more details. | |
14 * | |
15 * You should have received a copy of the GNU Lesser General Public | |
16 * License along with this library; if not, write to the Free Software | |
17 * Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307 USA | |
18 */ | |
19 #include "avformat.h" | |
20 #include <unistd.h> | |
21 | |
22 /* The FFM file is made of blocks of fixed size */ | |
23 #define FFM_HEADER_SIZE 14 | |
24 #define PACKET_ID 0x666d | |
25 | |
26 /* each packet contains frames (which can span several packets */ | |
27 #define FRAME_HEADER_SIZE 8 | |
28 #define FLAG_KEY_FRAME 0x01 | |
29 | |
30 typedef struct FFMStream { | |
65 | 31 int64_t pts; |
0 | 32 } FFMStream; |
33 | |
34 enum { | |
35 READ_HEADER, | |
36 READ_DATA, | |
37 }; | |
38 | |
39 typedef struct FFMContext { | |
40 /* only reading mode */ | |
41 offset_t write_index, file_size; | |
42 int read_state; | |
65 | 43 uint8_t header[FRAME_HEADER_SIZE]; |
0 | 44 |
45 /* read and write */ | |
46 int first_packet; /* true if first packet, needed to set the discontinuity tag */ | |
47 int packet_size; | |
48 int frame_offset; | |
65 | 49 int64_t pts; |
50 uint8_t *packet_ptr, *packet_end; | |
51 uint8_t packet[FFM_PACKET_SIZE]; | |
0 | 52 } FFMContext; |
53 | |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
54 static int64_t get_pts(AVFormatContext *s, offset_t pos); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
55 |
0 | 56 /* disable pts hack for testing */ |
57 int ffm_nopts = 0; | |
58 | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
59 #ifdef CONFIG_ENCODERS |
0 | 60 static void flush_packet(AVFormatContext *s) |
61 { | |
62 FFMContext *ffm = s->priv_data; | |
63 int fill_size, h; | |
64 ByteIOContext *pb = &s->pb; | |
65 | |
66 fill_size = ffm->packet_end - ffm->packet_ptr; | |
67 memset(ffm->packet_ptr, 0, fill_size); | |
68 | |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
69 if (url_ftell(pb) % ffm->packet_size) |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
70 av_abort(); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
71 |
0 | 72 /* put header */ |
73 put_be16(pb, PACKET_ID); | |
74 put_be16(pb, fill_size); | |
75 put_be64(pb, ffm->pts); | |
76 h = ffm->frame_offset; | |
77 if (ffm->first_packet) | |
78 h |= 0x8000; | |
79 put_be16(pb, h); | |
80 put_buffer(pb, ffm->packet, ffm->packet_end - ffm->packet); | |
81 | |
82 /* prepare next packet */ | |
83 ffm->frame_offset = 0; /* no key frame */ | |
84 ffm->pts = 0; /* no pts */ | |
85 ffm->packet_ptr = ffm->packet; | |
86 ffm->first_packet = 0; | |
87 } | |
88 | |
89 /* 'first' is true if first data of a frame */ | |
90 static void ffm_write_data(AVFormatContext *s, | |
241 | 91 const uint8_t *buf, int size, |
65 | 92 int64_t pts, int first) |
0 | 93 { |
94 FFMContext *ffm = s->priv_data; | |
95 int len; | |
96 | |
97 if (first && ffm->frame_offset == 0) | |
98 ffm->frame_offset = ffm->packet_ptr - ffm->packet + FFM_HEADER_SIZE; | |
99 if (first && ffm->pts == 0) | |
100 ffm->pts = pts; | |
101 | |
102 /* write as many packets as needed */ | |
103 while (size > 0) { | |
104 len = ffm->packet_end - ffm->packet_ptr; | |
105 if (len > size) | |
106 len = size; | |
107 memcpy(ffm->packet_ptr, buf, len); | |
108 | |
109 ffm->packet_ptr += len; | |
110 buf += len; | |
111 size -= len; | |
112 if (ffm->packet_ptr >= ffm->packet_end) { | |
113 /* special case : no pts in packet : we leave the current one */ | |
114 if (ffm->pts == 0) | |
115 ffm->pts = pts; | |
116 | |
117 flush_packet(s); | |
118 } | |
119 } | |
120 } | |
121 | |
122 static int ffm_write_header(AVFormatContext *s) | |
123 { | |
124 FFMContext *ffm = s->priv_data; | |
125 AVStream *st; | |
126 FFMStream *fst; | |
127 ByteIOContext *pb = &s->pb; | |
128 AVCodecContext *codec; | |
129 int bit_rate, i; | |
130 | |
131 ffm->packet_size = FFM_PACKET_SIZE; | |
132 | |
133 /* header */ | |
134 put_tag(pb, "FFM1"); | |
135 put_be32(pb, ffm->packet_size); | |
136 /* XXX: store write position in other file ? */ | |
137 put_be64(pb, ffm->packet_size); /* current write position */ | |
138 | |
139 put_be32(pb, s->nb_streams); | |
140 bit_rate = 0; | |
141 for(i=0;i<s->nb_streams;i++) { | |
142 st = s->streams[i]; | |
143 bit_rate += st->codec.bit_rate; | |
144 } | |
145 put_be32(pb, bit_rate); | |
146 | |
147 /* list of streams */ | |
148 for(i=0;i<s->nb_streams;i++) { | |
149 st = s->streams[i]; | |
150 fst = av_mallocz(sizeof(FFMStream)); | |
151 if (!fst) | |
152 goto fail; | |
502
813b0119a98e
ffserver fixes by (Koos Vriezen <koos.vriezen at xs4all dot nl>)
michael
parents:
468
diff
changeset
|
153 av_set_pts_info(st, 64, 1, 1000000); |
0 | 154 st->priv_data = fst; |
155 | |
156 codec = &st->codec; | |
157 /* generic info */ | |
158 put_be32(pb, codec->codec_id); | |
159 put_byte(pb, codec->codec_type); | |
160 put_be32(pb, codec->bit_rate); | |
5 | 161 put_be32(pb, st->quality); |
0 | 162 put_be32(pb, codec->flags); |
744
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
163 put_be32(pb, codec->flags2); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
164 put_be32(pb, codec->debug); |
0 | 165 /* specific info */ |
166 switch(codec->codec_type) { | |
167 case CODEC_TYPE_VIDEO: | |
743 | 168 put_be32(pb, codec->time_base.num); |
169 put_be32(pb, codec->time_base.den); | |
0 | 170 put_be16(pb, codec->width); |
171 put_be16(pb, codec->height); | |
172 put_be16(pb, codec->gop_size); | |
744
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
173 put_be32(pb, codec->pix_fmt); |
0 | 174 put_byte(pb, codec->qmin); |
175 put_byte(pb, codec->qmax); | |
176 put_byte(pb, codec->max_qdiff); | |
177 put_be16(pb, (int) (codec->qcompress * 10000.0)); | |
178 put_be16(pb, (int) (codec->qblur * 10000.0)); | |
179 put_be32(pb, codec->bit_rate_tolerance); | |
180 put_strz(pb, codec->rc_eq); | |
181 put_be32(pb, codec->rc_max_rate); | |
182 put_be32(pb, codec->rc_min_rate); | |
183 put_be32(pb, codec->rc_buffer_size); | |
184 put_be64_double(pb, codec->i_quant_factor); | |
185 put_be64_double(pb, codec->b_quant_factor); | |
186 put_be64_double(pb, codec->i_quant_offset); | |
187 put_be64_double(pb, codec->b_quant_offset); | |
188 put_be32(pb, codec->dct_algo); | |
744
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
189 put_be32(pb, codec->strict_std_compliance); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
190 put_be32(pb, codec->max_b_frames); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
191 put_be32(pb, codec->luma_elim_threshold); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
192 put_be32(pb, codec->chroma_elim_threshold); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
193 put_be32(pb, codec->mpeg_quant); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
194 put_be32(pb, codec->intra_dc_precision); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
195 put_be32(pb, codec->me_method); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
196 put_be32(pb, codec->mb_decision); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
197 put_be32(pb, codec->nsse_weight); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
198 put_be32(pb, codec->frame_skip_cmp); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
199 put_be64_double(pb, codec->rc_buffer_aggressivity); |
0 | 200 break; |
201 case CODEC_TYPE_AUDIO: | |
202 put_be32(pb, codec->sample_rate); | |
203 put_le16(pb, codec->channels); | |
204 put_le16(pb, codec->frame_size); | |
205 break; | |
206 default: | |
537 | 207 return -1; |
0 | 208 } |
209 /* hack to have real time */ | |
210 if (ffm_nopts) | |
211 fst->pts = 0; | |
212 else | |
213 fst->pts = av_gettime(); | |
214 } | |
215 | |
216 /* flush until end of block reached */ | |
217 while ((url_ftell(pb) % ffm->packet_size) != 0) | |
218 put_byte(pb, 0); | |
219 | |
220 put_flush_packet(pb); | |
221 | |
222 /* init packet mux */ | |
223 ffm->packet_ptr = ffm->packet; | |
224 ffm->packet_end = ffm->packet + ffm->packet_size - FFM_HEADER_SIZE; | |
537 | 225 assert(ffm->packet_end >= ffm->packet); |
0 | 226 ffm->frame_offset = 0; |
227 ffm->pts = 0; | |
228 ffm->first_packet = 1; | |
229 | |
230 return 0; | |
231 fail: | |
232 for(i=0;i<s->nb_streams;i++) { | |
233 st = s->streams[i]; | |
234 av_freep(&st->priv_data); | |
235 } | |
236 return -1; | |
237 } | |
238 | |
468 | 239 static int ffm_write_packet(AVFormatContext *s, AVPacket *pkt) |
0 | 240 { |
468 | 241 AVStream *st = s->streams[pkt->stream_index]; |
0 | 242 FFMStream *fst = st->priv_data; |
65 | 243 int64_t pts; |
244 uint8_t header[FRAME_HEADER_SIZE]; | |
0 | 245 int duration; |
468 | 246 int size= pkt->size; |
0 | 247 |
468 | 248 //XXX/FIXME use duration from pkt |
0 | 249 if (st->codec.codec_type == CODEC_TYPE_AUDIO) { |
250 duration = ((float)st->codec.frame_size / st->codec.sample_rate * 1000000.0); | |
251 } else { | |
743 | 252 duration = (1000000.0 * st->codec.time_base.num / (float)st->codec.time_base.den); |
0 | 253 } |
254 | |
255 pts = fst->pts; | |
256 /* packet size & key_frame */ | |
468 | 257 header[0] = pkt->stream_index; |
0 | 258 header[1] = 0; |
468 | 259 if (pkt->flags & PKT_FLAG_KEY) |
0 | 260 header[1] |= FLAG_KEY_FRAME; |
261 header[2] = (size >> 16) & 0xff; | |
262 header[3] = (size >> 8) & 0xff; | |
263 header[4] = size & 0xff; | |
264 header[5] = (duration >> 16) & 0xff; | |
265 header[6] = (duration >> 8) & 0xff; | |
266 header[7] = duration & 0xff; | |
267 ffm_write_data(s, header, FRAME_HEADER_SIZE, pts, 1); | |
468 | 268 ffm_write_data(s, pkt->data, size, pts, 0); |
0 | 269 |
270 fst->pts += duration; | |
271 return 0; | |
272 } | |
273 | |
274 static int ffm_write_trailer(AVFormatContext *s) | |
275 { | |
276 ByteIOContext *pb = &s->pb; | |
277 FFMContext *ffm = s->priv_data; | |
278 | |
279 /* flush packets */ | |
280 if (ffm->packet_ptr > ffm->packet) | |
281 flush_packet(s); | |
282 | |
283 put_flush_packet(pb); | |
284 | |
285 if (!url_is_streamed(pb)) { | |
65 | 286 int64_t size; |
0 | 287 /* update the write offset */ |
288 size = url_ftell(pb); | |
289 url_fseek(pb, 8, SEEK_SET); | |
290 put_be64(pb, size); | |
291 put_flush_packet(pb); | |
292 } | |
293 | |
294 return 0; | |
295 } | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
296 #endif //CONFIG_ENCODERS |
0 | 297 |
298 /* ffm demux */ | |
299 | |
300 static int ffm_is_avail_data(AVFormatContext *s, int size) | |
301 { | |
302 FFMContext *ffm = s->priv_data; | |
303 offset_t pos, avail_size; | |
304 int len; | |
305 | |
306 len = ffm->packet_end - ffm->packet_ptr; | |
307 if (!ffm_nopts) { | |
308 /* XXX: I don't understand this test, so I disabled it for testing */ | |
309 if (size <= len) | |
310 return 1; | |
311 } | |
312 pos = url_ftell(&s->pb); | |
313 if (pos == ffm->write_index) { | |
314 /* exactly at the end of stream */ | |
315 return 0; | |
316 } else if (pos < ffm->write_index) { | |
317 avail_size = ffm->write_index - pos; | |
318 } else { | |
319 avail_size = (ffm->file_size - pos) + (ffm->write_index - FFM_PACKET_SIZE); | |
320 } | |
321 avail_size = (avail_size / ffm->packet_size) * (ffm->packet_size - FFM_HEADER_SIZE) + len; | |
322 if (size <= avail_size) | |
323 return 1; | |
324 else | |
325 return 0; | |
326 } | |
327 | |
328 /* first is true if we read the frame header */ | |
329 static int ffm_read_data(AVFormatContext *s, | |
65 | 330 uint8_t *buf, int size, int first) |
0 | 331 { |
332 FFMContext *ffm = s->priv_data; | |
333 ByteIOContext *pb = &s->pb; | |
334 int len, fill_size, size1, frame_offset; | |
335 | |
336 size1 = size; | |
337 while (size > 0) { | |
338 redo: | |
339 len = ffm->packet_end - ffm->packet_ptr; | |
340 if (len > size) | |
341 len = size; | |
342 if (len == 0) { | |
343 if (url_ftell(pb) == ffm->file_size) | |
344 url_fseek(pb, ffm->packet_size, SEEK_SET); | |
345 retry_read: | |
346 get_be16(pb); /* PACKET_ID */ | |
347 fill_size = get_be16(pb); | |
348 ffm->pts = get_be64(pb); | |
349 frame_offset = get_be16(pb); | |
350 get_buffer(pb, ffm->packet, ffm->packet_size - FFM_HEADER_SIZE); | |
351 ffm->packet_end = ffm->packet + (ffm->packet_size - FFM_HEADER_SIZE - fill_size); | |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
352 if (ffm->packet_end < ffm->packet) |
537 | 353 return -1; |
0 | 354 /* if first packet or resynchronization packet, we must |
355 handle it specifically */ | |
356 if (ffm->first_packet || (frame_offset & 0x8000)) { | |
357 if (!frame_offset) { | |
358 /* This packet has no frame headers in it */ | |
359 if (url_ftell(pb) >= ffm->packet_size * 3) { | |
360 url_fseek(pb, -ffm->packet_size * 2, SEEK_CUR); | |
361 goto retry_read; | |
362 } | |
363 /* This is bad, we cannot find a valid frame header */ | |
364 return 0; | |
365 } | |
366 ffm->first_packet = 0; | |
367 if ((frame_offset & 0x7ffff) < FFM_HEADER_SIZE) | |
537 | 368 return -1; |
0 | 369 ffm->packet_ptr = ffm->packet + (frame_offset & 0x7fff) - FFM_HEADER_SIZE; |
370 if (!first) | |
371 break; | |
372 } else { | |
373 ffm->packet_ptr = ffm->packet; | |
374 } | |
375 goto redo; | |
376 } | |
377 memcpy(buf, ffm->packet_ptr, len); | |
378 buf += len; | |
379 ffm->packet_ptr += len; | |
380 size -= len; | |
381 first = 0; | |
382 } | |
383 return size1 - size; | |
384 } | |
385 | |
386 | |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
387 static void adjust_write_index(AVFormatContext *s) |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
388 { |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
389 FFMContext *ffm = s->priv_data; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
390 ByteIOContext *pb = &s->pb; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
391 int64_t pts; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
392 //offset_t orig_write_index = ffm->write_index; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
393 offset_t pos_min, pos_max; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
394 int64_t pts_start; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
395 offset_t ptr = url_ftell(pb); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
396 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
397 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
398 pos_min = 0; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
399 pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
400 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
401 pts_start = get_pts(s, pos_min); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
402 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
403 pts = get_pts(s, pos_max); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
404 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
405 if (pts - 100000 > pts_start) |
390
3a40642dc4df
adjust_write_index() fix by ("Curi Fabio Eduardo (SFL)" <curif at TELEFONICA dot COM dot AR>)
michael
parents:
318
diff
changeset
|
406 goto end; |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
407 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
408 ffm->write_index = FFM_PACKET_SIZE; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
409 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
410 pts_start = get_pts(s, pos_min); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
411 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
412 pts = get_pts(s, pos_max); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
413 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
414 if (pts - 100000 <= pts_start) { |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
415 while (1) { |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
416 offset_t newpos; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
417 int64_t newpts; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
418 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
419 newpos = ((pos_max + pos_min) / (2 * FFM_PACKET_SIZE)) * FFM_PACKET_SIZE; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
420 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
421 if (newpos == pos_min) |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
422 break; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
423 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
424 newpts = get_pts(s, newpos); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
425 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
426 if (newpts - 100000 <= pts) { |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
427 pos_max = newpos; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
428 pts = newpts; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
429 } else { |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
430 pos_min = newpos; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
431 } |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
432 } |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
433 ffm->write_index += pos_max; |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
434 } |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
435 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
436 //printf("Adjusted write index from %lld to %lld: pts=%0.6f\n", orig_write_index, ffm->write_index, pts / 1000000.); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
437 //printf("pts range %0.6f - %0.6f\n", get_pts(s, 0) / 1000000. , get_pts(s, ffm->file_size - 2 * FFM_PACKET_SIZE) / 1000000. ); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
438 |
390
3a40642dc4df
adjust_write_index() fix by ("Curi Fabio Eduardo (SFL)" <curif at TELEFONICA dot COM dot AR>)
michael
parents:
318
diff
changeset
|
439 end: |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
440 url_fseek(pb, ptr, SEEK_SET); |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
441 } |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
442 |
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
443 |
0 | 444 static int ffm_read_header(AVFormatContext *s, AVFormatParameters *ap) |
445 { | |
446 FFMContext *ffm = s->priv_data; | |
447 AVStream *st; | |
448 FFMStream *fst; | |
449 ByteIOContext *pb = &s->pb; | |
450 AVCodecContext *codec; | |
187 | 451 int i, nb_streams; |
65 | 452 uint32_t tag; |
0 | 453 |
454 /* header */ | |
455 tag = get_le32(pb); | |
456 if (tag != MKTAG('F', 'F', 'M', '1')) | |
457 goto fail; | |
458 ffm->packet_size = get_be32(pb); | |
459 if (ffm->packet_size != FFM_PACKET_SIZE) | |
460 goto fail; | |
461 ffm->write_index = get_be64(pb); | |
462 /* get also filesize */ | |
463 if (!url_is_streamed(pb)) { | |
464 ffm->file_size = url_filesize(url_fileno(pb)); | |
318
54e915169d48
Add more resilience in reading ffm files. In particular, don't assume
philipjsg
parents:
277
diff
changeset
|
465 adjust_write_index(s); |
0 | 466 } else { |
65 | 467 ffm->file_size = (uint64_t_C(1) << 63) - 1; |
0 | 468 } |
469 | |
187 | 470 nb_streams = get_be32(pb); |
0 | 471 get_be32(pb); /* total bitrate */ |
472 /* read each stream */ | |
187 | 473 for(i=0;i<nb_streams;i++) { |
0 | 474 char rc_eq_buf[128]; |
475 | |
187 | 476 st = av_new_stream(s, 0); |
0 | 477 if (!st) |
478 goto fail; | |
479 fst = av_mallocz(sizeof(FFMStream)); | |
480 if (!fst) | |
481 goto fail; | |
468 | 482 |
483 av_set_pts_info(st, 64, 1, 1000000); | |
484 | |
0 | 485 st->priv_data = fst; |
486 | |
487 codec = &st->codec; | |
488 /* generic info */ | |
489 st->codec.codec_id = get_be32(pb); | |
490 st->codec.codec_type = get_byte(pb); /* codec_type */ | |
491 codec->bit_rate = get_be32(pb); | |
5 | 492 st->quality = get_be32(pb); |
0 | 493 codec->flags = get_be32(pb); |
744
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
494 codec->flags2 = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
495 codec->debug = get_be32(pb); |
0 | 496 /* specific info */ |
497 switch(codec->codec_type) { | |
498 case CODEC_TYPE_VIDEO: | |
743 | 499 codec->time_base.num = get_be32(pb); |
500 codec->time_base.den = get_be32(pb); | |
0 | 501 codec->width = get_be16(pb); |
502 codec->height = get_be16(pb); | |
503 codec->gop_size = get_be16(pb); | |
744
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
504 codec->pix_fmt = get_be32(pb); |
0 | 505 codec->qmin = get_byte(pb); |
506 codec->qmax = get_byte(pb); | |
507 codec->max_qdiff = get_byte(pb); | |
508 codec->qcompress = get_be16(pb) / 10000.0; | |
509 codec->qblur = get_be16(pb) / 10000.0; | |
510 codec->bit_rate_tolerance = get_be32(pb); | |
34 | 511 codec->rc_eq = av_strdup(get_strz(pb, rc_eq_buf, sizeof(rc_eq_buf))); |
0 | 512 codec->rc_max_rate = get_be32(pb); |
513 codec->rc_min_rate = get_be32(pb); | |
514 codec->rc_buffer_size = get_be32(pb); | |
515 codec->i_quant_factor = get_be64_double(pb); | |
516 codec->b_quant_factor = get_be64_double(pb); | |
517 codec->i_quant_offset = get_be64_double(pb); | |
518 codec->b_quant_offset = get_be64_double(pb); | |
519 codec->dct_algo = get_be32(pb); | |
744
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
520 codec->strict_std_compliance = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
521 codec->max_b_frames = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
522 codec->luma_elim_threshold = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
523 codec->chroma_elim_threshold = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
524 codec->mpeg_quant = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
525 codec->intra_dc_precision = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
526 codec->me_method = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
527 codec->mb_decision = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
528 codec->nsse_weight = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
529 codec->frame_skip_cmp = get_be32(pb); |
da5b3b9e898e
Add in many fields that have been added to the Codec structure. This means
philipjsg
parents:
743
diff
changeset
|
530 codec->rc_buffer_aggressivity = get_be64_double(pb); |
0 | 531 break; |
532 case CODEC_TYPE_AUDIO: | |
533 codec->sample_rate = get_be32(pb); | |
534 codec->channels = get_le16(pb); | |
535 codec->frame_size = get_le16(pb); | |
536 break; | |
537 default: | |
538 goto fail; | |
539 } | |
540 | |
541 } | |
542 | |
543 /* get until end of block reached */ | |
544 while ((url_ftell(pb) % ffm->packet_size) != 0) | |
545 get_byte(pb); | |
546 | |
547 /* init packet demux */ | |
548 ffm->packet_ptr = ffm->packet; | |
549 ffm->packet_end = ffm->packet; | |
550 ffm->frame_offset = 0; | |
551 ffm->pts = 0; | |
552 ffm->read_state = READ_HEADER; | |
553 ffm->first_packet = 1; | |
554 return 0; | |
555 fail: | |
556 for(i=0;i<s->nb_streams;i++) { | |
557 st = s->streams[i]; | |
558 if (st) { | |
559 av_freep(&st->priv_data); | |
560 av_free(st); | |
561 } | |
562 } | |
563 return -1; | |
564 } | |
565 | |
566 /* return < 0 if eof */ | |
567 static int ffm_read_packet(AVFormatContext *s, AVPacket *pkt) | |
568 { | |
569 int size; | |
570 FFMContext *ffm = s->priv_data; | |
571 int duration; | |
572 | |
573 switch(ffm->read_state) { | |
574 case READ_HEADER: | |
575 if (!ffm_is_avail_data(s, FRAME_HEADER_SIZE)) { | |
576 return -EAGAIN; | |
577 } | |
578 #if 0 | |
579 printf("pos=%08Lx spos=%Lx, write_index=%Lx size=%Lx\n", | |
580 url_ftell(&s->pb), s->pb.pos, ffm->write_index, ffm->file_size); | |
581 #endif | |
582 if (ffm_read_data(s, ffm->header, FRAME_HEADER_SIZE, 1) != | |
583 FRAME_HEADER_SIZE) | |
584 return -EAGAIN; | |
585 #if 0 | |
586 { | |
587 int i; | |
588 for(i=0;i<FRAME_HEADER_SIZE;i++) | |
589 printf("%02x ", ffm->header[i]); | |
590 printf("\n"); | |
591 } | |
592 #endif | |
593 ffm->read_state = READ_DATA; | |
594 /* fall thru */ | |
595 case READ_DATA: | |
596 size = (ffm->header[2] << 16) | (ffm->header[3] << 8) | ffm->header[4]; | |
597 if (!ffm_is_avail_data(s, size)) { | |
598 return -EAGAIN; | |
599 } | |
600 | |
601 duration = (ffm->header[5] << 16) | (ffm->header[6] << 8) | ffm->header[7]; | |
602 | |
603 av_new_packet(pkt, size); | |
604 pkt->stream_index = ffm->header[0]; | |
605 if (ffm->header[1] & FLAG_KEY_FRAME) | |
606 pkt->flags |= PKT_FLAG_KEY; | |
607 | |
608 ffm->read_state = READ_HEADER; | |
609 if (ffm_read_data(s, pkt->data, size, 0) != size) { | |
610 /* bad case: desynchronized packet. we cancel all the packet loading */ | |
611 av_free_packet(pkt); | |
612 return -EAGAIN; | |
613 } | |
614 pkt->pts = ffm->pts; | |
615 pkt->duration = duration; | |
616 break; | |
617 } | |
618 return 0; | |
619 } | |
620 | |
621 //#define DEBUG_SEEK | |
622 | |
623 /* pos is between 0 and file_size - FFM_PACKET_SIZE. It is translated | |
624 by the write position inside this function */ | |
625 static void ffm_seek1(AVFormatContext *s, offset_t pos1) | |
626 { | |
627 FFMContext *ffm = s->priv_data; | |
628 ByteIOContext *pb = &s->pb; | |
629 offset_t pos; | |
630 | |
631 pos = pos1 + ffm->write_index; | |
632 if (pos >= ffm->file_size) | |
633 pos -= (ffm->file_size - FFM_PACKET_SIZE); | |
634 #ifdef DEBUG_SEEK | |
635 printf("seek to %Lx -> %Lx\n", pos1, pos); | |
636 #endif | |
637 url_fseek(pb, pos, SEEK_SET); | |
638 } | |
639 | |
65 | 640 static int64_t get_pts(AVFormatContext *s, offset_t pos) |
0 | 641 { |
642 ByteIOContext *pb = &s->pb; | |
65 | 643 int64_t pts; |
0 | 644 |
645 ffm_seek1(s, pos); | |
646 url_fskip(pb, 4); | |
647 pts = get_be64(pb); | |
648 #ifdef DEBUG_SEEK | |
649 printf("pts=%0.6f\n", pts / 1000000.0); | |
650 #endif | |
651 return pts; | |
652 } | |
653 | |
654 /* seek to a given time in the file. The file read pointer is | |
655 positionned at or before pts. XXX: the following code is quite | |
656 approximative */ | |
558 | 657 static int ffm_seek(AVFormatContext *s, int stream_index, int64_t wanted_pts, int flags) |
0 | 658 { |
659 FFMContext *ffm = s->priv_data; | |
660 offset_t pos_min, pos_max, pos; | |
65 | 661 int64_t pts_min, pts_max, pts; |
0 | 662 double pos1; |
663 | |
664 #ifdef DEBUG_SEEK | |
665 printf("wanted_pts=%0.6f\n", wanted_pts / 1000000.0); | |
666 #endif | |
667 /* find the position using linear interpolation (better than | |
668 dichotomy in typical cases) */ | |
669 pos_min = 0; | |
670 pos_max = ffm->file_size - 2 * FFM_PACKET_SIZE; | |
671 while (pos_min <= pos_max) { | |
672 pts_min = get_pts(s, pos_min); | |
673 pts_max = get_pts(s, pos_max); | |
674 /* linear interpolation */ | |
675 pos1 = (double)(pos_max - pos_min) * (double)(wanted_pts - pts_min) / | |
676 (double)(pts_max - pts_min); | |
65 | 677 pos = (((int64_t)pos1) / FFM_PACKET_SIZE) * FFM_PACKET_SIZE; |
0 | 678 if (pos <= pos_min) |
679 pos = pos_min; | |
680 else if (pos >= pos_max) | |
681 pos = pos_max; | |
682 pts = get_pts(s, pos); | |
683 /* check if we are lucky */ | |
684 if (pts == wanted_pts) { | |
685 goto found; | |
686 } else if (pts > wanted_pts) { | |
687 pos_max = pos - FFM_PACKET_SIZE; | |
688 } else { | |
689 pos_min = pos + FFM_PACKET_SIZE; | |
690 } | |
691 } | |
558 | 692 pos = (flags & AVSEEK_FLAG_BACKWARD) ? pos_min : pos_max; |
0 | 693 if (pos > 0) |
694 pos -= FFM_PACKET_SIZE; | |
695 found: | |
696 ffm_seek1(s, pos); | |
697 return 0; | |
698 } | |
699 | |
700 offset_t ffm_read_write_index(int fd) | |
701 { | |
65 | 702 uint8_t buf[8]; |
0 | 703 offset_t pos; |
704 int i; | |
705 | |
706 lseek(fd, 8, SEEK_SET); | |
707 read(fd, buf, 8); | |
708 pos = 0; | |
709 for(i=0;i<8;i++) | |
187 | 710 pos |= (int64_t)buf[i] << (56 - i * 8); |
0 | 711 return pos; |
712 } | |
713 | |
714 void ffm_write_write_index(int fd, offset_t pos) | |
715 { | |
65 | 716 uint8_t buf[8]; |
0 | 717 int i; |
718 | |
719 for(i=0;i<8;i++) | |
720 buf[i] = (pos >> (56 - i * 8)) & 0xff; | |
721 lseek(fd, 8, SEEK_SET); | |
722 write(fd, buf, 8); | |
723 } | |
724 | |
725 void ffm_set_write_index(AVFormatContext *s, offset_t pos, offset_t file_size) | |
726 { | |
727 FFMContext *ffm = s->priv_data; | |
728 ffm->write_index = pos; | |
729 ffm->file_size = file_size; | |
730 } | |
731 | |
732 static int ffm_read_close(AVFormatContext *s) | |
733 { | |
734 AVStream *st; | |
735 int i; | |
736 | |
737 for(i=0;i<s->nb_streams;i++) { | |
738 st = s->streams[i]; | |
739 av_freep(&st->priv_data); | |
740 } | |
741 return 0; | |
742 } | |
743 | |
744 static int ffm_probe(AVProbeData *p) | |
745 { | |
746 if (p->buf_size >= 4 && | |
747 p->buf[0] == 'F' && p->buf[1] == 'F' && p->buf[2] == 'M' && | |
748 p->buf[3] == '1') | |
749 return AVPROBE_SCORE_MAX + 1; | |
750 return 0; | |
751 } | |
752 | |
753 static AVInputFormat ffm_iformat = { | |
754 "ffm", | |
755 "ffm format", | |
756 sizeof(FFMContext), | |
757 ffm_probe, | |
758 ffm_read_header, | |
759 ffm_read_packet, | |
760 ffm_read_close, | |
761 ffm_seek, | |
762 }; | |
763 | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
764 #ifdef CONFIG_ENCODERS |
0 | 765 static AVOutputFormat ffm_oformat = { |
766 "ffm", | |
767 "ffm format", | |
768 "", | |
769 "ffm", | |
770 sizeof(FFMContext), | |
771 /* not really used */ | |
772 CODEC_ID_MP2, | |
773 CODEC_ID_MPEG1VIDEO, | |
774 ffm_write_header, | |
775 ffm_write_packet, | |
776 ffm_write_trailer, | |
777 }; | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
778 #endif //CONFIG_ENCODERS |
0 | 779 |
780 int ffm_init(void) | |
781 { | |
782 av_register_input_format(&ffm_iformat); | |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
783 #ifdef CONFIG_ENCODERS |
0 | 784 av_register_output_format(&ffm_oformat); |
277
a313e1080322
disable encoders where appropriate (patch courtesy of BERO
melanson
parents:
241
diff
changeset
|
785 #endif //CONFIG_ENCODERS |
0 | 786 return 0; |
787 } |