Esempio n. 1
0
END_TEST

START_TEST(test_flv_tag_set_timestamp_short) {
    flv_tag tag;
    uint32 val;

    flv_tag_set_timestamp(&tag, 0x00112233);
    val = uint24_be_to_uint32(tag.timestamp);
    fail_if(val != 0x00112233,
        "expected 0x00112233, got 0x%X", val);
    fail_if(tag.timestamp_extended != 0x00,
        "expected 0x00, got 0x%hhX", tag.timestamp_extended);
}
Esempio n. 2
0
END_TEST

START_TEST(test_flv_tag_set_timestamp_extended) {
    flv_tag tag;
    uint32 val;

    flv_tag_set_timestamp(&tag, 0x44332211);
    val = uint24_be_to_uint32(tag.timestamp);
    fail_if(val != 0x00332211,
        "expected 0x00332211, got 0x%X", val);
    fail_if(tag.timestamp_extended != 0x44,
        "expected 0x44, got 0x%hhX", tag.timestamp_extended);
}
Esempio n. 3
0
/*
    Write the flv output file
*/
static int write_flv(flv_stream * flv_in, FILE * flv_out, const flv_info * info, const flv_metadata * meta, const flvmeta_opts * opts) {
    uint32_be size;
    uint32 on_metadata_name_size;
    uint32 on_metadata_size;
    uint32 prev_timestamp_video;
    uint32 prev_timestamp_audio;
    uint32 prev_timestamp_meta;
    uint8 timestamp_extended_video;
    uint8 timestamp_extended_audio;
    uint8 timestamp_extended_meta;
    byte * copy_buffer;
    flv_tag ft, omft;
    int have_on_last_second;

    if (opts->verbose) {
        fprintf(stdout, "Writing %s...\n", opts->output_file);
    }

    /* write the flv header */
    if (flv_write_header(flv_out, &info->header) != 1) {
        return ERROR_WRITE;
    }

    /* first "previous tag size" */
    size = swap_uint32(0);
    if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
        return ERROR_WRITE;
    }

    /* create the onMetaData tag */
    on_metadata_name_size = (uint32)amf_data_size(meta->on_metadata_name);
    on_metadata_size = (uint32)amf_data_size(meta->on_metadata);

    omft.type = FLV_TAG_TYPE_META;
    omft.body_length = uint32_to_uint24_be(on_metadata_name_size + on_metadata_size);
    flv_tag_set_timestamp(&omft, 0);
    omft.stream_id = uint32_to_uint24_be(0);
    
    /* write the computed onMetaData tag first if it doesn't exist in the input file */
    if (info->on_metadata_size == 0) {
        if (flv_write_tag(flv_out, &omft) != 1
        || amf_data_file_write(meta->on_metadata_name, flv_out) < on_metadata_name_size
        || amf_data_file_write(meta->on_metadata, flv_out) < on_metadata_size) {
            return ERROR_WRITE;
        }

        /* previous tag size */
        size = swap_uint32(FLV_TAG_SIZE + on_metadata_name_size + on_metadata_size);
        if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
            return ERROR_WRITE;
        }
    }

    /* extended timestamp initialization */
    prev_timestamp_video = 0;
    prev_timestamp_audio = 0;
    prev_timestamp_meta = 0;
    timestamp_extended_video = 0;
    timestamp_extended_audio = 0;
    timestamp_extended_meta = 0;

    /* copy the tags verbatim */
    flv_reset(flv_in);

    copy_buffer = (byte *)malloc(info->biggest_tag_body_size + FLV_TAG_SIZE);
    have_on_last_second = 0;
    while (flv_read_tag(flv_in, &ft) == FLV_OK) {
        file_offset_t offset;
        uint32 body_length;
        uint32 timestamp;

        offset = flv_get_current_tag_offset(flv_in);
        body_length = flv_tag_get_body_length(ft);
        timestamp = flv_tag_get_timestamp(ft);

        /* extended timestamp fixing */
        if (ft.type == FLV_TAG_TYPE_META) {
            if (timestamp < prev_timestamp_meta
            && prev_timestamp_meta - timestamp > 0xF00000) {
                ++timestamp_extended_meta;
            }
            prev_timestamp_meta = timestamp;
            if (timestamp_extended_meta > 0) {
                timestamp += timestamp_extended_meta << 24;
            }
        }
        else if (ft.type == FLV_TAG_TYPE_AUDIO) {
            if (timestamp < prev_timestamp_audio
            && prev_timestamp_audio - timestamp > 0xF00000) {
                ++timestamp_extended_audio;
            }
            prev_timestamp_audio = timestamp;
            if (timestamp_extended_audio > 0) {
                timestamp += timestamp_extended_audio << 24;
            }
        }
        else if (ft.type == FLV_TAG_TYPE_VIDEO) {
            if (timestamp < prev_timestamp_video
            && prev_timestamp_video - timestamp > 0xF00000) {
                ++timestamp_extended_video;
            }
            prev_timestamp_video = timestamp;
            if (timestamp_extended_video > 0) {
                timestamp += timestamp_extended_video << 24;
            }
        }

        /* non-zero starting timestamp handling */
        if (opts->reset_timestamps && timestamp > 0) {
            timestamp -= info->first_timestamp;
        }

        flv_tag_set_timestamp(&ft, timestamp);

        /* if we're at the offset of the first onMetaData tag in the input file,
           we write the one we computed instead, discarding the old one */
        if (info->on_metadata_offset == offset) {
            if (flv_write_tag(flv_out, &omft) != 1
            || amf_data_file_write(meta->on_metadata_name, flv_out) < on_metadata_name_size
            || amf_data_file_write(meta->on_metadata, flv_out) < on_metadata_size) {
                free(copy_buffer);
                return ERROR_WRITE;
            }

            /* previous tag size */
            size = swap_uint32(FLV_TAG_SIZE + on_metadata_name_size + on_metadata_size);
            if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
                free(copy_buffer);
                return ERROR_WRITE;
            }
        }
        else {
            size_t read_body;
            
            /* insert an onLastSecond metadata tag */
            if (opts->insert_onlastsecond && !have_on_last_second && !info->have_on_last_second && (info->last_timestamp - timestamp) <= 1000) {
                flv_tag tag;
                uint32 on_last_second_name_size = (uint32)amf_data_size(meta->on_last_second_name);
                uint32 on_last_second_size = (uint32)amf_data_size(meta->on_last_second);
                tag.type = FLV_TAG_TYPE_META;
                tag.body_length = uint32_to_uint24_be(on_last_second_name_size + on_last_second_size);
                tag.timestamp = ft.timestamp;
                tag.timestamp_extended = ft.timestamp_extended;
                tag.stream_id = uint32_to_uint24_be(0);
                if (flv_write_tag(flv_out, &tag) != 1
                || amf_data_file_write(meta->on_last_second_name, flv_out) < on_last_second_name_size
                || amf_data_file_write(meta->on_last_second, flv_out) < on_last_second_size) {
                    free(copy_buffer);
                    return ERROR_WRITE;
                }

                /* previous tag size */
                size = swap_uint32(FLV_TAG_SIZE + on_last_second_name_size + on_last_second_size);
                if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
                    free(copy_buffer);
                    return ERROR_WRITE;
                }

                have_on_last_second = 1;
            }

            /* if the tag is bigger than expected, it means that
               it's an unknown tag type. In this case, we only
               copy as much data as the copy buffer can contain */
            if (body_length > info->biggest_tag_body_size) {
                body_length = info->biggest_tag_body_size;
            }

            /* copy the tag verbatim */
            read_body = flv_read_tag_body(flv_in, copy_buffer, body_length);
            if (read_body < body_length) {
                /* we have reached end of file on an incomplete tag */
                if (opts->error_handling == FLVMETA_EXIT_ON_ERROR) {
                    free(copy_buffer);
                    return ERROR_EOF;
                }
                else if (opts->error_handling == FLVMETA_FIX_ERRORS) {
                    /* the tag is bogus, just omit it,
                       even though it will make the whole file length
                       calculation wrong, and the metadata inaccurate */
                    /* TODO : fix it by handling that problem in the first pass */
                    free(copy_buffer);
                    return OK;
                }
                else if (opts->error_handling == FLVMETA_IGNORE_ERRORS) {
                    /* just copy the whole tag and exit */
                    flv_write_tag(flv_out, &ft);
                    fwrite(copy_buffer, 1, read_body, flv_out);
                    free(copy_buffer);
                    size = swap_uint32(FLV_TAG_SIZE + read_body);
                    fwrite(&size, sizeof(uint32_be), 1, flv_out);
                    return OK;
                }
            }
            if (flv_write_tag(flv_out, &ft) != 1
            || fwrite(copy_buffer, 1, body_length, flv_out) < body_length) {
                free(copy_buffer);
                return ERROR_WRITE;
            }

            /* previous tag length */
            size = swap_uint32(FLV_TAG_SIZE + body_length);
            if (fwrite(&size, sizeof(uint32_be), 1, flv_out) != 1) {
                free(copy_buffer);
                return ERROR_WRITE;
            }
        }        
    }

    if (opts->verbose) {
        fprintf(stdout, "%s successfully written\n", opts->output_file);
    }

    free(copy_buffer);
    return OK;
}
Esempio n. 4
0
int main(int argc, char* argv[]) {

  FlvHeader header;
  FlvTag tag;
  byte* tagData;
  FILE *fpdst = NULL, *fpsrc = NULL;
  int i = 0, srccount = argc - 2, headerLength, duration_index = 0, 
    prevSize, dataSize, offset, foundduration = 0, zero = 0, basetimestamp[2], lasttimestamp[2] = {0};
  char** src = argv + 2;
  double duration = 0.0;

  int bts = 0;

  if (argc < 2) {
    fprintf(stderr, "Usage: %s flvtobesaved 1stflv [2ndflv [3rdflv [...]]]\n", argv[0]);
    exit(1);
  }
  if ((fpdst = fopen(argv[1], "wb")) == NULL) {
    fprintf(stderr, "Can't write to file '%s'\n", argv[1]);
    exit(1);
  }
  
  while (i < srccount) {
    if ((fpsrc = fopen(src[i], "rb")) == NULL) {
      fprintf(stderr, "Can't open file '%s'\n", src[i]);
      exit(1);
    }
    
    if(! flv_header_read(fpsrc, &header) || ! flv_is_valid_header(&header)) {
      fprintf(stderr, "The header of file '%s' is broken or is not FLV header.\n", src[i]);
      exit(1);
    }

    if (i == 0) {
      fwrite(&header, sizeof(FlvHeader), 1, fpdst);
      fwrite(&zero, sizeof(int), 1, fpdst); // the first previous tag size is 0
      duration_index = sizeof(FlvHeader);
    }

    headerLength = intval(header.headerLength, 4);
    
    if (0 != fseek(fpsrc, headerLength+4, SEEK_SET)) { // skip to real flv tag data(skip the first previous tag size, +4)
      fprintf(stderr, "The first previousSize(should be 0) of file '%s' is broken.\n", src[i]);
      exit(1);
    }

    bts = (int)(duration * 1000);
    basetimestamp[0] = lasttimestamp[0];
    basetimestamp[1] = lasttimestamp[1];
    if (bts < basetimestamp[0])
      bts = basetimestamp[0];
    if (bts < basetimestamp[1])
      bts = basetimestamp[1];
    foundduration = 0;

    while (tagData = flv_tag_read(fpsrc, &tag, &dataSize, &prevSize)) {

      if (tag.tagType == 0x12 && ! foundduration) { // if script data and duration not found, try to get duration
        duration += flv_tag_get_duration(tagData, dataSize, &offset);
        foundduration = 1;
        if (i == 0) { // prepare the script data for writing, we choose the first FLV file header as sample
          duration_index += 4 + sizeof(FlvTag) + offset;
          
          flv_scriptdata_strip_keyframes(&tag, tagData, &dataSize);
          
          flv_tag_write(fpdst, &tag, tagData, &dataSize, &prevSize);
        }
      } else if (tag.tagType == 0x8 || tag.tagType == 0x9) {

        lasttimestamp[tag.tagType - 0x8] = bts + flv_tag_get_timestamp(&tag);
        flv_tag_set_timestamp(&tag, lasttimestamp[tag.tagType - 0x8]);

        flv_tag_write(fpdst, &tag, tagData, &dataSize, &prevSize);
        if (i == 0 && ! foundduration) {
          duration_index += 4 + sizeof(FlvTag) + dataSize;
        }
      }
    }

    //fprintf(stdout, "base: %d, last: %d\n", basetimestamp[0], lasttimestamp[0]);
    printf("completely merging file '%s' to '%s'\n", src[i], argv[1]);

    fclose(fpsrc);

    ++i;
  }
  if (0 != fseek(fpdst, duration_index, SEEK_SET))
    quit("can't seek to duration\n", 1);
  fwrite(bytevaldouble(duration), 1, 8, fpdst); // save real duration to file
  fclose(fpdst);

  return 0;
}