PageRenderTime 77ms CodeModel.GetById 14ms app.highlight 56ms RepoModel.GetById 2ms app.codeStats 0ms

/src/info.c

https://code.google.com/
C | 712 lines | 521 code | 78 blank | 113 comment | 175 complexity | 43237cc5aa038880060d362a291ce80d MD5 | raw file
  1/*
  2    $Id: info.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 "info.h"
 25#include "avc.h"
 26
 27#include <string.h>
 28
 29/*
 30    compute Sorensen H.263 video size
 31*/
 32static int compute_h263_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
 33    byte header[9];
 34    uint24_be psc_be;
 35    uint32 psc;
 36
 37    /* make sure we have enough bytes to read in the current tag */
 38    if (body_length >= 9) {
 39        if (flv_read_tag_body(flv_in, header, 9) < 9) {
 40            return FLV_ERROR_EOF;
 41        }
 42        psc_be.b[0] = header[0];
 43        psc_be.b[1] = header[1];
 44        psc_be.b[2] = header[2];
 45        psc = uint24_be_to_uint32(psc_be) >> 7;
 46        if (psc == 1) {
 47            uint32 psize = ((header[3] & 0x03) << 1) + ((header[4] >> 7) & 0x01);
 48            switch (psize) {
 49                case 0:
 50                    info->video_width  = ((header[4] & 0x7f) << 1) + ((header[5] >> 7) & 0x01);
 51                    info->video_height = ((header[5] & 0x7f) << 1) + ((header[6] >> 7) & 0x01);
 52                    break;
 53                case 1:
 54                    info->video_width  = ((header[4] & 0x7f) << 9) + (header[5] << 1) + ((header[6] >> 7) & 0x01);
 55                    info->video_height = ((header[6] & 0x7f) << 9) + (header[7] << 1) + ((header[8] >> 7) & 0x01);
 56                    break;
 57                case 2:
 58                    info->video_width  = 352;
 59                    info->video_height = 288;
 60                    break;
 61                case 3:
 62                    info->video_width  = 176;
 63                    info->video_height = 144;
 64                    break;
 65                case 4:
 66                    info->video_width  = 128;
 67                    info->video_height = 96;
 68                    break;
 69                case 5:
 70                    info->video_width  = 320;
 71                    info->video_height = 240;
 72                    break;
 73                case 6:
 74                    info->video_width  = 160;
 75                    info->video_height = 120;
 76                    break;
 77                default:
 78                    break;
 79            }
 80        }
 81    }
 82    return FLV_OK;
 83}
 84
 85/*
 86    compute Screen video size
 87*/
 88static int compute_screen_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
 89    byte header[4];
 90
 91    /* make sure we have enough bytes to read in the current tag */
 92    if (body_length >= 4) {
 93        if (flv_read_tag_body(flv_in, header, 4) < 4) {
 94            return FLV_ERROR_EOF;
 95        }
 96        
 97        info->video_width  = ((header[0] & 0x0f) << 8) + header[1];
 98        info->video_height = ((header[2] & 0x0f) << 8) + header[3];
 99    }
100    return FLV_OK;
101}
102
103/*
104    compute On2 VP6 video size
105*/
106static int compute_vp6_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
107    byte header[7], offset;
108
109    /* make sure we have enough bytes to read in the current tag */
110    if (body_length >= 7) {
111        if (flv_read_tag_body(flv_in, header, 7) < 7) {
112            return FLV_ERROR_EOF;
113        }
114        
115        /* two bytes offset if VP6 0 */
116        offset = (header[1] & 0x01 || !(header[2] & 0x06)) << 1;
117        info->video_width  = (header[4 + offset] << 4) - (header[0] >> 4);
118        info->video_height = (header[3 + offset] << 4) - (header[0] & 0x0f);
119        
120    }
121    return FLV_OK;
122}
123
124/*
125    compute On2 VP6 with Alpha video size
126*/
127static int compute_vp6_alpha_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
128    byte header[10], offset;
129
130    /* make sure we have enough bytes to read in the current tag */
131    if (body_length >= 10) {
132        if (flv_read_tag_body(flv_in, header, 10) < 10) {
133            return FLV_ERROR_EOF;
134        }
135        
136        /* two bytes offset if VP6 0 */
137        offset = (header[4] & 0x01 || !(header[5] & 0x06)) << 1;
138        info->video_width  = (header[7 + offset] << 4) - (header[0] >> 4);
139        info->video_height = (header[6 + offset] << 4) - (header[0] & 0x0f);
140    }
141    return FLV_OK;
142}
143
144/*
145    compute AVC (H.264) video size (experimental)
146*/
147static int compute_avc_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
148    return read_avc_resolution(flv_in, body_length, &(info->video_width), &(info->video_height));
149}
150
151/*
152    compute video width and height from the first video frame
153*/
154static int compute_video_size(flv_stream * flv_in, flv_info * info, uint32 body_length) {
155    switch (info->video_codec) {
156        case FLV_VIDEO_TAG_CODEC_SORENSEN_H263:
157            return compute_h263_size(flv_in, info, body_length);
158        case FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO:
159        case FLV_VIDEO_TAG_CODEC_SCREEN_VIDEO_V2:
160            return compute_screen_size(flv_in, info, body_length);
161        case FLV_VIDEO_TAG_CODEC_ON2_VP6:
162            return compute_vp6_size(flv_in, info, body_length);
163        case FLV_VIDEO_TAG_CODEC_ON2_VP6_ALPHA:
164            return compute_vp6_alpha_size(flv_in, info, body_length);
165        case FLV_VIDEO_TAG_CODEC_AVC:
166            return compute_avc_size(flv_in, info, body_length);
167        default:
168            return FLV_OK;
169    }
170}
171
172/*
173    read the flv file thoroughly to get all necessary information.
174
175    we need to check :
176    - timestamp of first audio for audio delay
177    - whether we have audio and video
178    - first frames codecs (audio, video)
179    - total audio and video data sizes
180    - keyframe offsets and timestamps
181    - whether the last video frame is a keyframe
182    - last keyframe timestamp
183    - onMetaData tag total size
184    - total tags size
185    - first tag after onMetaData offset
186    - last timestamp
187    - real video data size, number of frames, duration to compute framerate and video data rate
188    - real audio data size, duration to compute audio data rate
189    - video headers to find width and height. (depends on the encoding)
190*/
191int get_flv_info(flv_stream * flv_in, flv_info * info, const flvmeta_opts * opts) {
192    uint32 prev_timestamp_video;
193    uint32 prev_timestamp_audio;
194    uint32 prev_timestamp_meta;
195    uint8 timestamp_extended_video;
196    uint8 timestamp_extended_audio;
197    uint8 timestamp_extended_meta;
198    uint8 have_video_size;
199    uint8 have_first_timestamp;
200    uint32 tag_number;
201    int result;
202    flv_tag ft;
203
204    info->have_video = 0;
205    info->have_audio = 0;
206    info->video_width = 0;
207    info->video_height = 0;
208    info->video_codec = 0;
209    info->video_frames_number = 0;
210    info->audio_codec = 0;
211    info->audio_size = 0;
212    info->audio_rate = 0;
213    info->audio_stereo = 0;
214    info->video_data_size = 0;
215    info->audio_data_size = 0;
216    info->meta_data_size = 0;
217    info->real_video_data_size = 0;
218    info->real_audio_data_size = 0;
219    info->video_first_timestamp = 0;
220    info->audio_first_timestamp = 0;
221    info->first_timestamp = 0;
222    info->can_seek_to_end = 0;
223    info->have_keyframes = 0;
224    info->last_keyframe_timestamp = 0;
225    info->on_metadata_size = 0;
226    info->on_metadata_offset = 0;
227    info->biggest_tag_body_size = 0;
228    info->last_timestamp = 0;
229    info->video_frame_duration = 0;
230    info->audio_frame_duration = 0;
231    info->total_prev_tags_size = 0;
232    info->have_on_last_second = 0;
233    info->original_on_metadata = NULL;
234    info->keyframes = NULL;
235    info->times = NULL;
236    info->filepositions = NULL;
237
238    if (opts->verbose) {
239        fprintf(stdout, "Parsing %s...\n", opts->input_file);
240    }
241
242    /*
243        read FLV header
244    */
245
246    if (flv_read_header(flv_in, &(info->header)) != FLV_OK) {
247        return ERROR_NO_FLV;
248    }
249
250    info->keyframes = amf_object_new();
251    info->times = amf_array_new();
252    info->filepositions = amf_array_new();
253    amf_object_add(info->keyframes, "times", info->times);
254    amf_object_add(info->keyframes, "filepositions", info->filepositions);
255
256    /* first empty previous tag size */
257    info->total_prev_tags_size = sizeof(uint32_be);
258
259    /* first timestamp */
260    have_first_timestamp = 0;
261
262    /* extended timestamp initialization */
263    prev_timestamp_video = 0;
264    prev_timestamp_audio = 0;
265    prev_timestamp_meta = 0;
266    timestamp_extended_video = 0;
267    timestamp_extended_audio = 0;
268    timestamp_extended_meta = 0;
269    tag_number = 0;
270    have_video_size = 0;
271
272    while (flv_read_tag(flv_in, &ft) == FLV_OK) {
273        file_offset_t offset;
274        uint32 body_length;
275        uint32 timestamp;
276
277        offset = flv_get_current_tag_offset(flv_in);
278        body_length = flv_tag_get_body_length(ft);
279        timestamp = flv_tag_get_timestamp(ft);
280
281        /* extended timestamp fixing */
282        if (ft.type == FLV_TAG_TYPE_META) {
283            if (timestamp < prev_timestamp_meta
284            && prev_timestamp_meta - timestamp > 0xF00000) {
285                ++timestamp_extended_meta;
286            }
287            prev_timestamp_meta = timestamp;
288            if (timestamp_extended_meta > 0) {
289                timestamp += timestamp_extended_meta << 24;
290            }
291        }
292        else if (ft.type == FLV_TAG_TYPE_AUDIO) {
293            if (timestamp < prev_timestamp_audio
294            && prev_timestamp_audio - timestamp > 0xF00000) {
295                ++timestamp_extended_audio;
296            }
297            prev_timestamp_audio = timestamp;
298            if (timestamp_extended_audio > 0) {
299                timestamp += timestamp_extended_audio << 24;
300            }
301        }
302        else if (ft.type == FLV_TAG_TYPE_VIDEO) {
303            if (timestamp < prev_timestamp_video
304            && prev_timestamp_video - timestamp > 0xF00000) {
305                ++timestamp_extended_video;
306            }
307            prev_timestamp_video = timestamp;
308            if (timestamp_extended_video > 0) {
309                timestamp += timestamp_extended_video << 24;
310            }
311        }
312
313        /* non-zero starting timestamp handling */
314        if (!have_first_timestamp && ft.type != FLV_TAG_TYPE_META) {
315            info->first_timestamp = timestamp;
316            have_first_timestamp = 1;
317        }
318        if (opts->reset_timestamps && timestamp > 0) {
319            timestamp -= info->first_timestamp;
320        }
321
322        /* update the info struct only if the tag is valid */
323        if (ft.type == FLV_TAG_TYPE_META
324        || ft.type == FLV_TAG_TYPE_AUDIO
325        || ft.type == FLV_TAG_TYPE_VIDEO) {
326            if (info->biggest_tag_body_size < body_length) {
327                info->biggest_tag_body_size = body_length;
328            }
329            info->last_timestamp = timestamp;
330        }
331
332        if (ft.type == FLV_TAG_TYPE_META) {
333            amf_data *tag_name, *data;
334            int retval;
335            tag_name = data = NULL;
336
337            if (body_length == 0) {
338                if (opts->verbose) {
339                    fprintf(stdout, "Warning: empty metadata tag at 0x%" FILE_OFFSET_PRINTF_FORMAT "X\n", offset);
340                }
341            }
342            else {
343                retval = flv_read_metadata(flv_in, &tag_name, &data);
344                if (retval == FLV_ERROR_EOF) {
345                    amf_data_free(tag_name);
346                    amf_data_free(data);
347                    return ERROR_EOF;
348                }
349                else if (retval == FLV_ERROR_INVALID_METADATA_NAME) {
350                    if (opts->verbose) {
351                        fprintf(stdout, "Warning: invalid metadata name at 0x%" FILE_OFFSET_PRINTF_FORMAT "X\n", offset);
352                    }
353                }
354                else if (retval == FLV_ERROR_INVALID_METADATA) {
355                    if (opts->verbose) {
356                        fprintf(stdout, "Warning: invalid metadata at 0x%" FILE_OFFSET_PRINTF_FORMAT "X\n", offset);
357                    }
358                    if (opts->error_handling == FLVMETA_EXIT_ON_ERROR) {
359                        amf_data_free(tag_name);
360                        amf_data_free(data);
361                        return ERROR_INVALID_TAG;
362                    }
363                }
364            }
365
366            /* check metadata name */
367            if (body_length > 0 && amf_data_get_type(tag_name) == AMF_TYPE_STRING) {
368                char * name = (char *)amf_string_get_bytes(tag_name);
369                size_t len = (size_t)amf_string_get_size(tag_name);
370
371                /* get info only on the first onMetaData we read */
372                if (info->on_metadata_size == 0 && !strncmp(name, "onMetaData", len)) {
373                    info->on_metadata_size = body_length + FLV_TAG_SIZE + sizeof(uint32_be);
374                    info->on_metadata_offset = offset;
375
376                    /* if we want to preserve existing metadata, then extract them */
377                    if (opts->preserve_metadata == 1) {
378                        /* we need an AMF associative array here, so we must
379                           discard errors and mis-typed data */
380                        if (amf_data_get_error_code(data) != AMF_ERROR_OK
381                        || amf_data_get_type(data) != AMF_TYPE_ASSOCIATIVE_ARRAY) {
382                            amf_data_free(data);
383                            data = amf_associative_array_new();
384                        }
385
386                        info->original_on_metadata = data;
387                    }
388                    else {
389                        amf_data_free(data);
390                    }
391                }
392                else {
393                    if (!strncmp(name, "onLastSecond", len)) {
394                        info->have_on_last_second = 1;
395                    }
396                    info->meta_data_size += (body_length + FLV_TAG_SIZE);
397                    info->total_prev_tags_size += sizeof(uint32_be);
398                    if (data != NULL) {
399                        amf_data_free(data);
400                    }
401                }
402            }
403            /* just ignore metadata that don't have a proper name */
404            else {
405                info->meta_data_size += (body_length + FLV_TAG_SIZE);
406                info->total_prev_tags_size += sizeof(uint32_be);
407                amf_data_free(data);
408            }
409            amf_data_free(tag_name);
410        }
411        else if (ft.type == FLV_TAG_TYPE_VIDEO) {
412            flv_video_tag vt;
413
414            /* do not take video frame into account if body length is zero and we ignore errors */
415            if (body_length == 0) {
416                if (opts->verbose) {
417                    fprintf(stdout, "Warning: empty video tag at 0x%" FILE_OFFSET_PRINTF_FORMAT "X\n", offset);
418                }
419            }
420            else {
421                if (flv_read_video_tag(flv_in, &vt) != FLV_OK) {
422                    return ERROR_EOF;
423                }
424
425                if (info->have_video != 1) {
426                    info->have_video = 1;
427                    info->video_codec = flv_video_tag_codec_id(vt);
428                    info->video_first_timestamp = timestamp;
429                }
430
431                if (have_video_size != 1
432                && flv_video_tag_frame_type(vt) == FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME) {
433                    /* read first video frame to get critical info */
434                    result = compute_video_size(flv_in, info, body_length - sizeof(flv_video_tag));
435                    if (result != FLV_OK) {
436                        return result;
437                    }
438
439                    if (info->video_width > 0 && info->video_height > 0) {
440                        have_video_size = 1;
441                    }
442                    /* if we cannot fetch that information from the first tag, we'll try
443                       for each following video key frame */
444                }
445
446                /* add keyframe to list */
447                if (flv_video_tag_frame_type(vt) == FLV_VIDEO_TAG_FRAME_TYPE_KEYFRAME) {
448                    /* do not add keyframe if the previous one has the same timestamp */
449                    if (!info->have_keyframes
450                    || (info->have_keyframes && info->last_keyframe_timestamp != timestamp)
451                    || opts->all_keyframes) {
452                        info->have_keyframes = 1;
453                        info->last_keyframe_timestamp = timestamp;
454                        amf_array_push(info->times, amf_number_new(timestamp / 1000.0));
455                        amf_array_push(info->filepositions, amf_number_new((number64)offset));
456                    }
457                    /* is last frame a key frame ? if so, we can seek to end */
458                    info->can_seek_to_end = 1;
459                }
460                else {
461                    info->can_seek_to_end = 0;
462                }
463
464                info->real_video_data_size += (body_length - 1);    
465            }
466
467            info->video_frames_number++;
468
469            /*
470                we assume all video frames have the same size as the first one:
471                probably bogus but only used in case there's no audio in the file
472            */
473            if (info->video_frame_duration == 0) {
474                info->video_frame_duration = timestamp - info->video_first_timestamp;
475            }
476
477            info->video_data_size += (body_length + FLV_TAG_SIZE);
478            info->total_prev_tags_size += sizeof(uint32_be);
479        }
480        else if (ft.type == FLV_TAG_TYPE_AUDIO) {
481            flv_audio_tag at;
482
483            /* do not take audio frame into account if body length is zero and we ignore errors */
484            if (body_length == 0) {
485                if (opts->verbose) {
486                    fprintf(stdout, "Warning: empty audio tag at 0x%" FILE_OFFSET_PRINTF_FORMAT "X\n", offset);
487                }
488            }
489            else {
490                if (flv_read_audio_tag(flv_in, &at) != FLV_OK) {
491                    return ERROR_EOF;
492                }
493            
494                if (info->have_audio != 1) {
495                    info->have_audio = 1;
496                    info->audio_codec = flv_audio_tag_sound_format(at);
497                    info->audio_rate = flv_audio_tag_sound_rate(at);
498                    info->audio_size = flv_audio_tag_sound_size(at);
499                    info->audio_stereo = flv_audio_tag_sound_type(at);
500                    info->audio_first_timestamp = timestamp;
501                }
502                /* we assume all audio frames have the same size as the first one */
503                if (info->audio_frame_duration == 0) {
504                    info->audio_frame_duration = timestamp - info->audio_first_timestamp;
505                }
506
507                info->real_audio_data_size += (body_length - 1);
508            }
509            
510            info->audio_data_size += (body_length + FLV_TAG_SIZE);
511            info->total_prev_tags_size += sizeof(uint32_be);
512        }
513        else {
514            if (opts->error_handling == FLVMETA_FIX_ERRORS) {
515                /* TODO : fix errors if possible */
516            }
517            else if (opts->error_handling == FLVMETA_IGNORE_ERRORS) {
518                /* let's continue the parsing */
519                if (opts->verbose) {
520                    fprintf(stdout, "Warning: invalid tag at 0x%" FILE_OFFSET_PRINTF_FORMAT "X\n", offset);
521                }
522                info->total_prev_tags_size += sizeof(uint32_be);
523            }
524            else {
525                return ERROR_INVALID_TAG;
526            }
527        }
528        ++tag_number;
529    }
530
531    if (opts->verbose) {
532        fprintf(stdout, "Found %d tags\n", tag_number);
533    }
534
535    return OK;
536}
537
538/*
539    compute the metadata
540*/
541void compute_metadata(flv_info * info, flv_metadata * meta, const flvmeta_opts * opts) {
542    uint32 new_on_metadata_size, on_last_second_size;
543    file_offset_t data_size, total_filesize;
544    number64 duration, video_data_rate, framerate;
545    amf_data * amf_total_filesize;
546    amf_data * amf_total_data_size;
547    amf_node * node_t;
548    amf_node * node_f;
549
550    if (opts->verbose) {
551        fprintf(stdout, "Computing metadata...\n");
552    }
553
554    meta->on_last_second_name = amf_str("onLastSecond");
555    meta->on_last_second = amf_associative_array_new();
556    meta->on_metadata_name = amf_str("onMetaData");
557
558    if (opts->metadata == NULL) {
559        meta->on_metadata = amf_associative_array_new();
560    }
561    else {
562        meta->on_metadata = opts->metadata;
563    }
564
565    amf_associative_array_add(meta->on_metadata, "hasMetadata", amf_boolean_new(1));
566    amf_associative_array_add(meta->on_metadata, "hasVideo", amf_boolean_new(info->have_video));
567    amf_associative_array_add(meta->on_metadata, "hasAudio", amf_boolean_new(info->have_audio));
568    
569    if (info->have_audio) {
570        duration = (info->last_timestamp - (opts->reset_timestamps ? 0 : info->first_timestamp) + info->audio_frame_duration) / 1000.0;
571    }
572    else {
573        duration = (info->last_timestamp - (opts->reset_timestamps ? 0 : info->first_timestamp) + info->video_frame_duration) / 1000.0;
574    }
575    amf_associative_array_add(meta->on_metadata, "duration", amf_number_new(duration));
576
577    amf_associative_array_add(meta->on_metadata, "lasttimestamp", amf_number_new(info->last_timestamp / 1000.0));
578    amf_associative_array_add(meta->on_metadata, "lastkeyframetimestamp", amf_number_new(info->last_keyframe_timestamp / 1000.0));
579    
580    if (info->video_width > 0)
581        amf_associative_array_add(meta->on_metadata, "width", amf_number_new(info->video_width));
582    if (info->video_height > 0)
583        amf_associative_array_add(meta->on_metadata, "height", amf_number_new(info->video_height));
584
585    video_data_rate = ((info->real_video_data_size / 1024.0) * 8.0) / duration;
586    amf_associative_array_add(meta->on_metadata, "videodatarate", amf_number_new(video_data_rate));
587
588    framerate = info->video_frames_number / duration;
589    amf_associative_array_add(meta->on_metadata, "framerate", amf_number_new(framerate));
590
591    if (info->have_audio) {
592        number64 audio_khz, audio_sample_rate;
593        number64 audio_data_rate = ((info->real_audio_data_size / 1024.0) * 8.0) / duration;
594        amf_associative_array_add(meta->on_metadata, "audiodatarate", amf_number_new(audio_data_rate));
595
596        audio_khz = 0.0;
597        switch (info->audio_rate) {
598            case FLV_AUDIO_TAG_SOUND_RATE_5_5: audio_khz = 5500.0; break;
599            case FLV_AUDIO_TAG_SOUND_RATE_11:  audio_khz = 11000.0; break;
600            case FLV_AUDIO_TAG_SOUND_RATE_22:  audio_khz = 22050.0; break;
601            case FLV_AUDIO_TAG_SOUND_RATE_44:  audio_khz = 44100.0; break;
602        }
603        amf_associative_array_add(meta->on_metadata, "audiosamplerate", amf_number_new(audio_khz));
604        audio_sample_rate = 0.0;
605        switch (info->audio_size) {
606            case FLV_AUDIO_TAG_SOUND_SIZE_8:  audio_sample_rate = 8.0; break;
607            case FLV_AUDIO_TAG_SOUND_SIZE_16: audio_sample_rate = 16.0; break;
608        }
609        amf_associative_array_add(meta->on_metadata, "audiosamplesize", amf_number_new(audio_sample_rate));
610        amf_associative_array_add(meta->on_metadata, "stereo", amf_boolean_new(info->audio_stereo == FLV_AUDIO_TAG_SOUND_TYPE_STEREO));
611    }
612
613    /* to be computed later */
614    amf_total_filesize = amf_number_new(0);
615    amf_associative_array_add(meta->on_metadata, "filesize", amf_total_filesize);
616
617    if (info->have_video) {
618        amf_associative_array_add(meta->on_metadata, "videosize", amf_number_new((number64)info->video_data_size));
619    }
620    if (info->have_audio) {
621        amf_associative_array_add(meta->on_metadata, "audiosize", amf_number_new((number64)info->audio_data_size));
622    }
623
624    /* to be computed later */
625    amf_total_data_size = amf_number_new(0);
626    amf_associative_array_add(meta->on_metadata, "datasize", amf_total_data_size);
627
628    amf_associative_array_add(meta->on_metadata, "metadatacreator", amf_str(PACKAGE_STRING));
629
630    amf_associative_array_add(meta->on_metadata, "metadatadate", amf_date_new((number64)time(NULL)*1000, 0));
631    if (info->have_audio) {
632        amf_associative_array_add(meta->on_metadata, "audiocodecid", amf_number_new((number64)info->audio_codec));
633    }
634    if (info->have_video) {
635        amf_associative_array_add(meta->on_metadata, "videocodecid", amf_number_new((number64)info->video_codec));
636    }
637    if (info->have_audio && info->have_video) {
638        number64 audio_delay = ((sint32)info->audio_first_timestamp - (sint32)info->video_first_timestamp) / 1000.0;
639        amf_associative_array_add(meta->on_metadata, "audiodelay", amf_number_new((number64)audio_delay));
640    }
641    amf_associative_array_add(meta->on_metadata, "canSeekToEnd", amf_boolean_new(info->can_seek_to_end));
642    
643    /* only add empty cuepoints if we don't preserve existing tags OR if the existing tags don't have cuepoints */
644    if (opts->preserve_metadata == 0
645    || (opts->preserve_metadata == 1 && amf_associative_array_get(info->original_on_metadata, "cuePoints") == NULL)) {
646        amf_associative_array_add(meta->on_metadata, "hasCuePoints", amf_boolean_new(0));
647        amf_associative_array_add(meta->on_metadata, "cuePoints", amf_array_new());
648    }
649    amf_associative_array_add(meta->on_metadata, "hasKeyframes", amf_boolean_new(info->have_keyframes));
650    amf_associative_array_add(meta->on_metadata, "keyframes", info->keyframes);
651
652    /* merge metadata from input file if we specified the preserve option */
653    if (opts->preserve_metadata) {
654        /* for each tag in the original metadata, we add it if it does not exist */
655        amf_node * node = amf_associative_array_first(info->original_on_metadata);
656        while (node != NULL) {
657            char * name = (char *)amf_string_get_bytes(amf_associative_array_get_name(node));
658            if (amf_associative_array_get(meta->on_metadata, name) == NULL) {
659                /* add metadata */
660                amf_associative_array_add(meta->on_metadata, name, amf_data_clone(amf_associative_array_get_data(node)));
661            }
662
663            node = amf_associative_array_next(node);
664        }
665        /* all old data has been duplicated to the new metadata, we can safely delete it */
666        amf_data_free(info->original_on_metadata);
667        info->original_on_metadata = NULL;
668    }
669
670    /*
671        When we know the final size, we can recompute te offsets for the filepositions, and the final datasize.
672    */
673    new_on_metadata_size = FLV_TAG_SIZE + sizeof(uint32_be) +
674        (uint32)(amf_data_size(meta->on_metadata_name) + amf_data_size(meta->on_metadata));
675    on_last_second_size = (uint32)(amf_data_size(meta->on_last_second_name) + amf_data_size(meta->on_last_second));
676
677    node_t = amf_array_first(info->times);
678    node_f = amf_array_first(info->filepositions);
679    while (node_t != NULL || node_f != NULL) {
680        amf_data * amf_filepos = amf_array_get(node_f);
681        number64 offset = amf_number_get_value(amf_filepos) + new_on_metadata_size - info->on_metadata_size;
682        number64 timestamp = amf_number_get_value(amf_array_get(node_t));
683
684        /* after the onLastSecond event we need to take in account the tag size */
685        if (opts->insert_onlastsecond && !info->have_on_last_second && (info->last_timestamp - timestamp * 1000) <= 1000) {
686            offset += (FLV_TAG_SIZE + on_last_second_size + sizeof(uint32_be));
687        }
688
689        amf_number_set_value(amf_filepos, offset);
690        node_t = amf_array_next(node_t);
691        node_f = amf_array_next(node_f);
692    }
693
694    /* compute data size, ie. size of metadata excluding prev_tag_size */
695    data_size = info->meta_data_size + FLV_TAG_SIZE +
696        (uint32)(amf_data_size(meta->on_metadata_name) + amf_data_size(meta->on_metadata));
697    if (!info->have_on_last_second && opts->insert_onlastsecond) {
698        data_size += (uint32)on_last_second_size + FLV_TAG_SIZE;
699    }
700    amf_number_set_value(amf_total_data_size, (number64)data_size);
701  
702    /* compute total file size */
703    total_filesize = FLV_HEADER_SIZE + info->total_prev_tags_size + info->video_data_size +
704        info->audio_data_size + info->meta_data_size + new_on_metadata_size;
705
706    if (!info->have_on_last_second && opts->insert_onlastsecond) {
707        /* if we have to add onLastSecond, we must count the header and new prevTagSize we add */
708        total_filesize += (uint32)(FLV_TAG_SIZE + on_last_second_size + sizeof(uint32_be));
709    }
710
711    amf_number_set_value(amf_total_filesize, (number64)total_filesize);
712}