PageRenderTime 77ms CodeModel.GetById 51ms app.highlight 22ms RepoModel.GetById 1ms app.codeStats 1ms

/src/update.c

https://code.google.com/
C | 367 lines | 260 code | 42 blank | 65 comment | 87 complexity | ef431a022bbac24918981eb8d611b203 MD5 | raw file
  1/*
  2    $Id: update.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 "flvmeta.h"
 25#include "flv.h"
 26#include "amf.h"
 27#include "dump.h"
 28#include "info.h"
 29#include "update.h"
 30#include "util.h"
 31
 32#include <stdio.h>
 33#include <string.h>
 34#include <time.h>
 35
 36#define COPY_BUFFER_SIZE 4096
 37
 38#ifdef WIN32
 39# include "win32_tmpfile.h"
 40# define flvmeta_tmpfile win32_tmpfile
 41#else /* WIN32 */
 42# define flvmeta_tmpfile tmpfile
 43#endif /* WIN32 */
 44
 45/*
 46    Write the flv output file
 47*/
 48static int write_flv(flv_stream * flv_in, FILE * flv_out, const flv_info * info, const flv_metadata * meta, const flvmeta_opts * opts) {
 49    uint32_be size;
 50    uint32 on_metadata_name_size;
 51    uint32 on_metadata_size;
 52    uint32 prev_timestamp_video;
 53    uint32 prev_timestamp_audio;
 54    uint32 prev_timestamp_meta;
 55    uint8 timestamp_extended_video;
 56    uint8 timestamp_extended_audio;
 57    uint8 timestamp_extended_meta;
 58    byte * copy_buffer;
 59    flv_tag ft, omft;
 60    int have_on_last_second;
 61
 62    if (opts->verbose) {
 63        fprintf(stdout, "Writing %s...\n", opts->output_file);
 64    }
 65
 66    /* write the flv header */
 67    if (flv_write_header(flv_out, &info->header) != 1) {
 68        return ERROR_WRITE;
 69    }
 70
 71    /* first "previous tag size" */
 72    size = swap_uint32(0);
 73    if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
 74        return ERROR_WRITE;
 75    }
 76
 77    /* create the onMetaData tag */
 78    on_metadata_name_size = (uint32)amf_data_size(meta->on_metadata_name);
 79    on_metadata_size = (uint32)amf_data_size(meta->on_metadata);
 80
 81    omft.type = FLV_TAG_TYPE_META;
 82    omft.body_length = uint32_to_uint24_be(on_metadata_name_size + on_metadata_size);
 83    flv_tag_set_timestamp(&omft, 0);
 84    omft.stream_id = uint32_to_uint24_be(0);
 85    
 86    /* write the computed onMetaData tag first if it doesn't exist in the input file */
 87    if (info->on_metadata_size == 0) {
 88        if (flv_write_tag(flv_out, &omft) != 1
 89        || amf_data_file_write(meta->on_metadata_name, flv_out) < on_metadata_name_size
 90        || amf_data_file_write(meta->on_metadata, flv_out) < on_metadata_size) {
 91            return ERROR_WRITE;
 92        }
 93
 94        /* previous tag size */
 95        size = swap_uint32(FLV_TAG_SIZE + on_metadata_name_size + on_metadata_size);
 96        if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
 97            return ERROR_WRITE;
 98        }
 99    }
100
101    /* extended timestamp initialization */
102    prev_timestamp_video = 0;
103    prev_timestamp_audio = 0;
104    prev_timestamp_meta = 0;
105    timestamp_extended_video = 0;
106    timestamp_extended_audio = 0;
107    timestamp_extended_meta = 0;
108
109    /* copy the tags verbatim */
110    flv_reset(flv_in);
111
112    copy_buffer = (byte *)malloc(info->biggest_tag_body_size + FLV_TAG_SIZE);
113    have_on_last_second = 0;
114    while (flv_read_tag(flv_in, &ft) == FLV_OK) {
115        file_offset_t offset;
116        uint32 body_length;
117        uint32 timestamp;
118
119        offset = flv_get_current_tag_offset(flv_in);
120        body_length = flv_tag_get_body_length(ft);
121        timestamp = flv_tag_get_timestamp(ft);
122
123        /* extended timestamp fixing */
124        if (ft.type == FLV_TAG_TYPE_META) {
125            if (timestamp < prev_timestamp_meta
126            && prev_timestamp_meta - timestamp > 0xF00000) {
127                ++timestamp_extended_meta;
128            }
129            prev_timestamp_meta = timestamp;
130            if (timestamp_extended_meta > 0) {
131                timestamp += timestamp_extended_meta << 24;
132            }
133        }
134        else if (ft.type == FLV_TAG_TYPE_AUDIO) {
135            if (timestamp < prev_timestamp_audio
136            && prev_timestamp_audio - timestamp > 0xF00000) {
137                ++timestamp_extended_audio;
138            }
139            prev_timestamp_audio = timestamp;
140            if (timestamp_extended_audio > 0) {
141                timestamp += timestamp_extended_audio << 24;
142            }
143        }
144        else if (ft.type == FLV_TAG_TYPE_VIDEO) {
145            if (timestamp < prev_timestamp_video
146            && prev_timestamp_video - timestamp > 0xF00000) {
147                ++timestamp_extended_video;
148            }
149            prev_timestamp_video = timestamp;
150            if (timestamp_extended_video > 0) {
151                timestamp += timestamp_extended_video << 24;
152            }
153        }
154
155        /* non-zero starting timestamp handling */
156        if (opts->reset_timestamps && timestamp > 0) {
157            timestamp -= info->first_timestamp;
158        }
159
160        flv_tag_set_timestamp(&ft, timestamp);
161
162        /* if we're at the offset of the first onMetaData tag in the input file,
163           we write the one we computed instead, discarding the old one */
164        if (info->on_metadata_offset == offset) {
165            if (flv_write_tag(flv_out, &omft) != 1
166            || amf_data_file_write(meta->on_metadata_name, flv_out) < on_metadata_name_size
167            || amf_data_file_write(meta->on_metadata, flv_out) < on_metadata_size) {
168                free(copy_buffer);
169                return ERROR_WRITE;
170            }
171
172            /* previous tag size */
173            size = swap_uint32(FLV_TAG_SIZE + on_metadata_name_size + on_metadata_size);
174            if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
175                free(copy_buffer);
176                return ERROR_WRITE;
177            }
178        }
179        else {
180            size_t read_body;
181            
182            /* insert an onLastSecond metadata tag */
183            if (opts->insert_onlastsecond && !have_on_last_second && !info->have_on_last_second && (info->last_timestamp - timestamp) <= 1000) {
184                flv_tag tag;
185                uint32 on_last_second_name_size = (uint32)amf_data_size(meta->on_last_second_name);
186                uint32 on_last_second_size = (uint32)amf_data_size(meta->on_last_second);
187                tag.type = FLV_TAG_TYPE_META;
188                tag.body_length = uint32_to_uint24_be(on_last_second_name_size + on_last_second_size);
189                tag.timestamp = ft.timestamp;
190                tag.timestamp_extended = ft.timestamp_extended;
191                tag.stream_id = uint32_to_uint24_be(0);
192                if (flv_write_tag(flv_out, &tag) != 1
193                || amf_data_file_write(meta->on_last_second_name, flv_out) < on_last_second_name_size
194                || amf_data_file_write(meta->on_last_second, flv_out) < on_last_second_size) {
195                    free(copy_buffer);
196                    return ERROR_WRITE;
197                }
198
199                /* previous tag size */
200                size = swap_uint32(FLV_TAG_SIZE + on_last_second_name_size + on_last_second_size);
201                if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
202                    free(copy_buffer);
203                    return ERROR_WRITE;
204                }
205
206                have_on_last_second = 1;
207            }
208
209            /* if the tag is bigger than expected, it means that
210               it's an unknown tag type. In this case, we only
211               copy as much data as the copy buffer can contain */
212            if (body_length > info->biggest_tag_body_size) {
213                body_length = info->biggest_tag_body_size;
214            }
215
216            /* copy the tag verbatim */
217            read_body = flv_read_tag_body(flv_in, copy_buffer, body_length);
218            if (read_body < body_length) {
219                /* we have reached end of file on an incomplete tag */
220                if (opts->error_handling == FLVMETA_EXIT_ON_ERROR) {
221                    free(copy_buffer);
222                    return ERROR_EOF;
223                }
224                else if (opts->error_handling == FLVMETA_FIX_ERRORS) {
225                    /* the tag is bogus, just omit it,
226                       even though it will make the whole file length
227                       calculation wrong, and the metadata inaccurate */
228                    /* TODO : fix it by handling that problem in the first pass */
229                    free(copy_buffer);
230                    return OK;
231                }
232                else if (opts->error_handling == FLVMETA_IGNORE_ERRORS) {
233                    /* just copy the whole tag and exit */
234                    flv_write_tag(flv_out, &ft);
235                    fwrite(copy_buffer, 1, read_body, flv_out);
236                    free(copy_buffer);
237                    size = swap_uint32(FLV_TAG_SIZE + read_body);
238                    fwrite(&size, sizeof(uint32_be), 1, flv_out);
239                    return OK;
240                }
241            }
242            if (flv_write_tag(flv_out, &ft) != 1
243            || fwrite(copy_buffer, 1, body_length, flv_out) < body_length) {
244                free(copy_buffer);
245                return ERROR_WRITE;
246            }
247
248            /* previous tag length */
249            size = swap_uint32(FLV_TAG_SIZE + body_length);
250            if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
251                free(copy_buffer);
252                return ERROR_WRITE;
253            }
254        }        
255    }
256
257    if (opts->verbose) {
258        fprintf(stdout, "%s successfully written\n", opts->output_file);
259    }
260
261    free(copy_buffer);
262    return OK;
263}
264
265/* copy a FLV file while adding onMetaData and optionnally onLastSecond events */
266int update_metadata(const flvmeta_opts * opts) {
267    int res, in_place_update;
268    flv_stream * flv_in;
269    FILE * flv_out;
270    flv_info info;
271    flv_metadata meta;
272
273    flv_in = flv_open(opts->input_file);
274    if (flv_in == NULL) {
275        return ERROR_OPEN_READ;
276    }
277
278    /*
279        get all necessary information from the flv file
280    */
281    res = get_flv_info(flv_in, &info, opts);
282    if (res != OK) {
283        flv_close(flv_in);
284        amf_data_free(info.keyframes);
285        return res;
286    }
287
288    compute_metadata(&info, &meta, opts);
289
290    /*
291        open output file
292    */
293    /* detect whether we have to overwrite the input file */
294    if (same_file(opts->input_file, opts->output_file)) {
295        in_place_update = 1;
296        flv_out = flvmeta_tmpfile();
297    }
298    else {
299        in_place_update = 0;
300        flv_out = fopen(opts->output_file, "wb");
301    }
302
303    if (flv_out == NULL) {
304        flv_close(flv_in);
305        amf_data_free(meta.on_last_second_name);
306        amf_data_free(meta.on_last_second);
307        amf_data_free(meta.on_metadata_name);
308        amf_data_free(meta.on_metadata);
309        amf_data_free(info.original_on_metadata);
310        return ERROR_OPEN_WRITE;
311    }
312
313    /*
314        write the output file
315    */
316    res = write_flv(flv_in, flv_out, &info, &meta, opts);
317
318    flv_close(flv_in);
319    amf_data_free(meta.on_last_second_name);
320    amf_data_free(meta.on_last_second);
321    amf_data_free(meta.on_metadata_name);
322    amf_data_free(info.original_on_metadata);
323
324    /* copy data into the original file if needed */
325    if (in_place_update == 1) {
326        FILE * flv_out_real;
327        size_t bytes_read;
328        byte copy_buffer[COPY_BUFFER_SIZE];
329
330        flv_out_real = fopen(opts->output_file, "wb");
331        if (flv_out_real == NULL) {
332            amf_data_free(meta.on_metadata);
333            return ERROR_OPEN_WRITE;
334        }
335
336        /* copy temporary file contents into the final file */
337        lfs_fseek(flv_out, 0, SEEK_SET);
338        while (!feof(flv_out)) {
339            bytes_read = fread(copy_buffer, sizeof(byte), COPY_BUFFER_SIZE, flv_out);
340            if (bytes_read > 0) {
341                if (fwrite(copy_buffer, sizeof(byte), bytes_read, flv_out_real) < bytes_read) {
342                    fclose(flv_out_real);
343                    fclose(flv_out);
344                    amf_data_free(meta.on_metadata);
345                    return ERROR_WRITE;
346                }
347            }
348            else {
349                fclose(flv_out_real);
350                fclose(flv_out);
351                amf_data_free(meta.on_metadata);
352                return ERROR_WRITE;
353            }
354        }
355        
356        fclose(flv_out_real);
357    }
358    fclose(flv_out);
359
360    /* dump computed metadata if we have to */
361    if (opts->dump_metadata == 1) {
362        dump_amf_data(meta.on_metadata, opts);
363    }
364    
365    amf_data_free(meta.on_metadata);
366    return res;
367}