PageRenderTime 59ms CodeModel.GetById 15ms app.highlight 39ms RepoModel.GetById 1ms app.codeStats 0ms

/src/flv.c

https://code.google.com/
C | 496 lines | 401 code | 61 blank | 34 comment | 222 complexity | 3941bacdb7e93250ce87aabfeabb8d90 MD5 | raw file
  1/*
  2    $Id: flv.c 231 2011-06-27 13:46:19Z marc.noirot $
  3
  4    FLV Metadata updater
  5
  6    Copyright (C) 2007-2012 Marc Noirot <marc.noirot AT gmail.com>
  7
  8    This file is part of FLVMeta.
  9
 10    FLVMeta is free software; you can redistribute it and/or modify
 11    it under the terms of the GNU General Public License as published by
 12    the Free Software Foundation; either version 2 of the License, or
 13    (at your option) any later version.
 14
 15    FLVMeta is distributed in the hope that it will be useful,
 16    but WITHOUT ANY WARRANTY; without even the implied warranty of
 17    MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 18    GNU General Public License for more details.
 19
 20    You should have received a copy of the GNU General Public License
 21    along with FLVMeta; if not, write to the Free Software
 22    Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
 23*/
 24#include "flv.h"
 25
 26#include <string.h>
 27
 28void flv_tag_set_timestamp(flv_tag * tag, uint32 timestamp) {
 29    tag->timestamp = uint32_to_uint24_be(timestamp);
 30    tag->timestamp_extended = (uint8)((timestamp & 0xFF000000) >> 24);
 31}
 32
 33/* FLV stream functions */
 34flv_stream * flv_open(const char * file) {
 35    flv_stream * stream = (flv_stream *) malloc(sizeof(flv_stream));
 36    if (stream == NULL) {
 37        return NULL;
 38    }
 39    stream->flvin = fopen(file, "rb");
 40    if (stream->flvin == NULL) {
 41        free(stream);
 42        return NULL;
 43    }
 44    stream->current_tag_body_length = 0;
 45    stream->current_tag_body_overflow = 0;
 46    stream->current_tag_offset = 0;
 47    stream->state = FLV_STREAM_STATE_START;
 48    return stream;
 49}
 50
 51int flv_read_header(flv_stream * stream, flv_header * header) {
 52    if (stream == NULL
 53    || stream->flvin == NULL
 54    || feof(stream->flvin)
 55    || stream->state != FLV_STREAM_STATE_START) {
 56        return FLV_ERROR_EOF;
 57    }
 58
 59    if (fread(&header->signature, sizeof(header->signature), 1, stream->flvin) == 0
 60    || fread(&header->version, sizeof(header->version), 1, stream->flvin) == 0
 61    || fread(&header->flags, sizeof(header->flags), 1, stream->flvin) == 0
 62    || fread(&header->offset, sizeof(header->offset), 1, stream->flvin) == 0) {
 63        return FLV_ERROR_EOF;
 64    }
 65
 66    if (header->signature[0] != 'F'
 67    || header->signature[1] != 'L'
 68    || header->signature[2] != 'V') {
 69        return FLV_ERROR_NO_FLV;
 70    }
 71    
 72    stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
 73    return FLV_OK;
 74}
 75
 76int flv_read_prev_tag_size(flv_stream * stream, uint32 * prev_tag_size) {
 77    uint32_be val;
 78    if (stream == NULL
 79    || stream->flvin == NULL
 80    || feof(stream->flvin)) {
 81        return FLV_ERROR_EOF;
 82    }
 83
 84    /* skip remaining tag body bytes */
 85    if (stream->state == FLV_STREAM_STATE_TAG_BODY) {
 86        lfs_fseek(stream->flvin, stream->current_tag_offset + FLV_TAG_SIZE + uint24_be_to_uint32(stream->current_tag.body_length), SEEK_SET);
 87        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
 88    }
 89
 90    if (stream->state == FLV_STREAM_STATE_PREV_TAG_SIZE) {
 91        if (fread(&val, sizeof(uint32_be), 1, stream->flvin) == 0) {
 92            return FLV_ERROR_EOF;
 93        }
 94        else {
 95            stream->state = FLV_STREAM_STATE_TAG;
 96            *prev_tag_size = swap_uint32(val);
 97            return FLV_OK;
 98        }
 99    }
100    else {
101        return FLV_ERROR_EOF;
102    }
103}
104
105int flv_read_tag(flv_stream * stream, flv_tag * tag) {
106    if (stream == NULL
107    || stream->flvin == NULL
108    || feof(stream->flvin)) {
109        return FLV_ERROR_EOF;
110    }
111
112    /* skip header */
113    if (stream->state == FLV_STREAM_STATE_START) {
114        lfs_fseek(stream->flvin, FLV_HEADER_SIZE, SEEK_CUR);
115        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
116    }
117
118    /* skip current tag body */
119    if (stream->state == FLV_STREAM_STATE_TAG_BODY) {
120        lfs_fseek(stream->flvin, stream->current_tag_offset + FLV_TAG_SIZE + uint24_be_to_uint32(stream->current_tag.body_length), SEEK_SET);
121        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
122    }
123 
124    /* skip previous tag size */
125    if (stream->state == FLV_STREAM_STATE_PREV_TAG_SIZE) {
126        lfs_fseek(stream->flvin, sizeof(uint32_be), SEEK_CUR);
127        stream->state = FLV_STREAM_STATE_TAG;
128    }
129    
130    if (stream->state == FLV_STREAM_STATE_TAG) {
131        stream->current_tag_offset = lfs_ftell(stream->flvin);
132
133        if (fread(&tag->type, sizeof(tag->type), 1, stream->flvin) == 0
134        || fread(&tag->body_length, sizeof(tag->body_length), 1, stream->flvin) == 0
135        || fread(&tag->timestamp, sizeof(tag->timestamp), 1, stream->flvin) == 0
136        || fread(&tag->timestamp_extended, sizeof(tag->timestamp_extended), 1, stream->flvin) == 0
137        || fread(&tag->stream_id, sizeof(tag->stream_id), 1, stream->flvin) == 0) {
138            return FLV_ERROR_EOF;
139        }
140        else {
141            memcpy(&stream->current_tag, tag, sizeof(flv_tag));
142            stream->current_tag_body_length = uint24_be_to_uint32(tag->body_length);
143            stream->current_tag_body_overflow = 0;
144            stream->state = FLV_STREAM_STATE_TAG_BODY;
145            return FLV_OK;
146        }
147    }
148    else {
149        return FLV_ERROR_EOF;
150    }
151}
152
153int flv_read_audio_tag(flv_stream * stream, flv_audio_tag * tag) {
154    if (stream == NULL
155    || stream->flvin == NULL
156    || feof(stream->flvin)
157    || stream->state != FLV_STREAM_STATE_TAG_BODY) {
158        return FLV_ERROR_EOF;
159    }
160
161    if (stream->current_tag_body_length == 0) {
162        return FLV_ERROR_EMPTY_TAG;
163    }
164
165    if (fread(tag, sizeof(flv_audio_tag), 1, stream->flvin) == 0) {
166        return FLV_ERROR_EOF;
167    }
168    
169    if (stream->current_tag_body_length >= sizeof(flv_audio_tag)) {
170        stream->current_tag_body_length -= sizeof(flv_audio_tag);
171    }
172    else {
173        stream->current_tag_body_overflow = sizeof(flv_audio_tag) - stream->current_tag_body_length;
174        stream->current_tag_body_length = 0;
175    }
176
177    if (stream->current_tag_body_length == 0) {
178        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
179        if (stream->current_tag_body_overflow > 0) {
180            lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
181        }
182    }
183
184    return FLV_OK;
185}
186
187int flv_read_video_tag(flv_stream * stream, flv_video_tag * tag) {
188    if (stream == NULL
189    || stream->flvin == NULL
190    || feof(stream->flvin)
191    || stream->state != FLV_STREAM_STATE_TAG_BODY) {
192        return FLV_ERROR_EOF;
193    }
194
195    if (stream->current_tag_body_length == 0) {
196        return FLV_ERROR_EMPTY_TAG;
197    }
198
199    if (fread(tag, sizeof(flv_video_tag), 1, stream->flvin) == 0) {
200        return FLV_ERROR_EOF;
201    }
202
203    if (stream->current_tag_body_length >= sizeof(flv_video_tag)) {
204        stream->current_tag_body_length -= sizeof(flv_video_tag);
205    }
206    else {
207        stream->current_tag_body_overflow = sizeof(flv_video_tag) - stream->current_tag_body_length;
208        stream->current_tag_body_length = 0;
209    }
210
211    if (stream->current_tag_body_length == 0) {
212        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
213        if (stream->current_tag_body_overflow > 0) {
214            lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
215        }
216    }
217
218    return FLV_OK;
219}
220
221int flv_read_metadata(flv_stream * stream, amf_data ** name, amf_data ** data) {
222    amf_data * d;
223    byte error_code;
224    size_t data_size;
225
226    if (stream == NULL
227    || stream->flvin == NULL
228    || feof(stream->flvin)
229    || stream->state != FLV_STREAM_STATE_TAG_BODY) {
230        return FLV_ERROR_EOF;
231    }
232
233    if (stream->current_tag_body_length == 0) {
234        return FLV_ERROR_EMPTY_TAG;
235    }
236    
237    /* read metadata name */
238    d = amf_data_file_read(stream->flvin);
239    *name = d;
240    error_code = amf_data_get_error_code(d);
241    if (error_code == AMF_ERROR_EOF) {
242        return FLV_ERROR_EOF;
243    }
244    else if (error_code != AMF_ERROR_OK) {
245        return FLV_ERROR_INVALID_METADATA_NAME;
246    }
247    
248    /* if only name can be read, metadata are invalid */
249    data_size = amf_data_size(d);
250    if (stream->current_tag_body_length > data_size) {
251        stream->current_tag_body_length -= (uint32)data_size;
252    }
253    else {
254        stream->current_tag_body_length = 0;
255        stream->current_tag_body_overflow = (uint32)data_size - stream->current_tag_body_length;
256
257        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
258        if (stream->current_tag_body_overflow > 0) {
259            lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
260        }
261
262        return FLV_ERROR_INVALID_METADATA;
263    }
264    
265    /* read metadata contents */
266    d = amf_data_file_read(stream->flvin);
267    *data = d;
268    error_code = amf_data_get_error_code(d);
269    if (error_code == AMF_ERROR_EOF) {
270        return FLV_ERROR_EOF;
271    }
272    if (error_code != AMF_ERROR_OK) {
273        return FLV_ERROR_INVALID_METADATA;
274    }
275    
276    data_size = amf_data_size(d);
277    if (stream->current_tag_body_length >= data_size) {
278        stream->current_tag_body_length -= (uint32)data_size;
279    }
280    else {
281        stream->current_tag_body_overflow = (uint32)data_size - stream->current_tag_body_length;
282        stream->current_tag_body_length = 0;
283    }
284
285    if (stream->current_tag_body_length == 0) {
286        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
287        if (stream->current_tag_body_overflow > 0) {
288            lfs_fseek(stream->flvin, -(file_offset_t)stream->current_tag_body_overflow, SEEK_CUR);
289        }
290    }
291
292    return FLV_OK;
293}
294
295size_t flv_read_tag_body(flv_stream * stream, void * buffer, size_t buffer_size) {
296    size_t bytes_number;
297
298    if (stream == NULL
299    || stream->flvin == NULL
300    || feof(stream->flvin)
301    || stream->state != FLV_STREAM_STATE_TAG_BODY) {
302        return 0;
303    }
304
305    bytes_number = (buffer_size > stream->current_tag_body_length) ? stream->current_tag_body_length : buffer_size;
306    bytes_number = fread(buffer, sizeof(byte), bytes_number, stream->flvin);
307    
308    stream->current_tag_body_length -= (uint32)bytes_number;
309
310    if (stream->current_tag_body_length == 0) {
311        stream->state = FLV_STREAM_STATE_PREV_TAG_SIZE;
312    }
313
314    return bytes_number;
315}
316
317file_offset_t flv_get_current_tag_offset(flv_stream * stream) {
318    return (stream != NULL) ? stream->current_tag_offset : 0;
319}
320
321file_offset_t flv_get_offset(flv_stream * stream) {
322    return (stream != NULL) ? lfs_ftell(stream->flvin) : 0;
323}
324
325void flv_reset(flv_stream * stream) {
326    /* go back to beginning of file */
327    if (stream != NULL && stream->flvin != NULL) {
328        stream->current_tag_body_length = 0;
329        stream->current_tag_offset = 0;
330        stream->state = FLV_STREAM_STATE_START;
331
332        lfs_fseek(stream->flvin, 0, SEEK_SET);
333    }
334}
335
336void flv_close(flv_stream * stream) {
337    if (stream != NULL) {
338        if (stream->flvin != NULL) {
339            fclose(stream->flvin);
340        }
341        free(stream);
342    }
343}
344
345/* FLV stdio writing helper functions */
346size_t flv_write_header(FILE * out, const flv_header * header) {
347    if (fwrite(&header->signature, sizeof(header->signature), 1, out) == 0)
348        return 0;
349    if (fwrite(&header->version, sizeof(header->version), 1, out) == 0)
350        return 0;
351    if (fwrite(&header->flags, sizeof(header->flags), 1, out) == 0)
352        return 0;
353    if (fwrite(&header->offset, sizeof(header->offset), 1, out) == 0)
354        return 0;
355    return 1;
356}
357
358size_t flv_write_tag(FILE * out, const flv_tag * tag) {
359    if (fwrite(&tag->type, sizeof(tag->type), 1, out) == 0)
360        return 0;
361    if (fwrite(&tag->body_length, sizeof(tag->body_length), 1, out) == 0)
362        return 0;
363    if (fwrite(&tag->timestamp, sizeof(tag->timestamp), 1, out) == 0)
364        return 0;
365    if (fwrite(&tag->timestamp_extended, sizeof(tag->timestamp_extended), 1, out) == 0)
366        return 0;
367    if (fwrite(&tag->stream_id, sizeof(tag->stream_id), 1, out) == 0)
368        return 0;
369    return 1;
370}
371
372/* FLV event based parser */
373int flv_parse(const char * file, flv_parser * parser) {
374    flv_header header;
375    flv_tag tag;
376    flv_audio_tag at;
377    flv_video_tag vt;
378    amf_data * name, * data;
379    uint32 prev_tag_size;
380    int retval;
381
382    if (parser == NULL) {
383        return FLV_ERROR_EOF;
384    }
385
386    parser->stream = flv_open(file);
387    if (parser->stream == NULL) {
388        return FLV_ERROR_OPEN_READ;
389    }
390
391    retval = flv_read_header(parser->stream, &header);
392    if (retval != FLV_OK) {
393        flv_close(parser->stream);
394        return retval;
395    }
396
397    if (parser->on_header != NULL) {
398        retval = parser->on_header(&header, parser);
399        if (retval != FLV_OK) {
400            flv_close(parser->stream);
401            return retval;
402        }
403    }
404
405    while (flv_read_tag(parser->stream, &tag) == FLV_OK) {
406        if (parser->on_tag != NULL) {
407            retval = parser->on_tag(&tag, parser);
408            if (retval != FLV_OK) {
409                flv_close(parser->stream);
410                return retval;
411            }
412        }
413
414        if (tag.type == FLV_TAG_TYPE_AUDIO) {
415            retval = flv_read_audio_tag(parser->stream, &at);
416            if (retval == FLV_ERROR_EOF) {
417                flv_close(parser->stream);
418                return retval;
419            }
420            if (retval != FLV_ERROR_EMPTY_TAG && parser->on_audio_tag != NULL) {
421                retval = parser->on_audio_tag(&tag, at, parser);
422                if (retval != FLV_OK) {
423                    flv_close(parser->stream);
424                    return retval;
425                }
426            }
427        }
428        else if (tag.type == FLV_TAG_TYPE_VIDEO) {
429            retval = flv_read_video_tag(parser->stream, &vt);
430            if (retval == FLV_ERROR_EOF) {
431                flv_close(parser->stream);
432                return retval;
433            }
434            if (retval != FLV_ERROR_EMPTY_TAG && parser->on_video_tag != NULL) {
435                retval = parser->on_video_tag(&tag, vt, parser);
436                if (retval != FLV_OK) {
437                    flv_close(parser->stream);
438                    return retval;
439                }
440            }
441        }
442        else if (tag.type == FLV_TAG_TYPE_META) {
443            name = data = NULL;
444            retval = flv_read_metadata(parser->stream, &name, &data);
445            if (retval == FLV_ERROR_EOF) {
446                amf_data_free(name);
447                amf_data_free(data);
448                flv_close(parser->stream);
449                return retval;
450            }
451            else if (retval == FLV_OK && parser->on_metadata_tag != NULL) {
452                retval = parser->on_metadata_tag(&tag, name, data, parser);
453                if (retval != FLV_OK) {
454                    amf_data_free(name);
455                    amf_data_free(data);
456                    flv_close(parser->stream);
457                    return retval;
458                }
459            }
460            amf_data_free(name);
461            amf_data_free(data);
462        }
463        else {
464            if (parser->on_unknown_tag != NULL) {
465                retval = parser->on_unknown_tag(&tag, parser);
466                if (retval != FLV_OK) {
467                    flv_close(parser->stream);
468                    return retval;
469                }
470            }
471        }
472        retval = flv_read_prev_tag_size(parser->stream, &prev_tag_size);
473        if (retval != FLV_OK) {
474            flv_close(parser->stream);
475            return retval;
476        }
477        if (parser->on_prev_tag_size != NULL) {
478            retval = parser->on_prev_tag_size(prev_tag_size, parser);
479            if (retval != FLV_OK) {
480                flv_close(parser->stream);
481                return retval;
482            }
483        }
484    }
485    
486    if (parser->on_stream_end != NULL) {
487        retval = parser->on_stream_end(parser);
488        if (retval != FLV_OK) {
489            flv_close(parser->stream);
490            return retval;
491        }
492    }
493
494    flv_close(parser->stream);
495    return FLV_OK;
496}