int main (int argc, char** argv) { flvtag_t tag; FILE* flv = flv_open_read (argv[1]); FILE* out = flv_open_write (argv[3]); int has_audio, has_video; flvtag_init (&tag); if (!flv_read_header (flv,&has_audio,&has_video)) { fprintf (stderr,"%s is not an flv file\n", argv[1]); return EXIT_FAILURE; } srt_t* head = srt_from_file (argv[2]); srt_t* srt = head; if (! head) { fprintf (stderr,"%s is not an srt file\n", argv[2]); return EXIT_FAILURE; } flv_write_header (out,has_audio,has_video); while (flv_read_tag (flv,&tag)) { // TODO handle B freame! if (srt && flvtag_pts_seconds (&tag) >= srt->timestamp && flvtag_avcpackettype_nalu == flvtag_avcpackettype (&tag)) { fprintf (stderr,"%f: %s\n", srt->timestamp, srt_data (srt)); flvtag_addcaption (&tag, srt_data (srt)); srt = srt->next; } flv_write_tag (out,&tag); // Write tag out here } srt_free (head); return EXIT_SUCCESS; }
int main(int argc, char * argv[]) { int i,ret; av_register_all(); nb_files = argc - 1; durations = calloc(nb_files,sizeof( uint64_t )); datasizes = calloc(nb_files,sizeof( double )); nb_keyframes = calloc(nb_files,sizeof( double )); keyframes = calloc(nb_files, sizeof(AVIndexEntry*)); // read metadata of the first package. // we only need to write keyframes, duration , videorate , audiorate , width , height and creator:) //解析第一个文件 for (i = 0; i < 1; i++) { int k; AVFormatContext * flv=NULL; char * filename; filename = argv[i+1]; avformat_open_input(&flv,filename,NULL,NULL); av_find_stream_info(flv); hasAudio = strcmp(av_dict_get(flv->metadata,"hasAudio",NULL,0)->value,"true")==0; hasVideo = strcmp(av_dict_get(flv->metadata,"hasVideo",NULL,0)->value,"true")==0; stereo = strcmp(av_dict_get(flv->metadata,"stereo",NULL,0)->value,"true")==0; durations[i] = flv->duration; duration += durations[i]; width = strtod(av_dict_get(flv->metadata,"width",NULL,0)->value,NULL); height = strtod(av_dict_get(flv->metadata,"height",NULL,0)->value,NULL); audiosamplerate = strtod(av_dict_get(flv->metadata,"audiosamplerate",NULL,0)->value,NULL); audiosamplesize = strtod(av_dict_get(flv->metadata,"audiosamplesize",NULL,0)->value,NULL); audiocodecid = strtod(av_dict_get(flv->metadata,"audiocodecid",NULL,0)->value,NULL); videocodecid = strtod(av_dict_get(flv->metadata,"videocodecid",NULL,0)->value,NULL); videodatarate = strtod(av_dict_get(flv->metadata,"videodatarate",NULL,0)->value,NULL); audiodatarate = strtod(av_dict_get(flv->metadata,"audiodatarate",NULL,0)->value,NULL); //获得 keyframes position nb_keyframes[i] = flv->streams[0]->nb_index_entries; nb_keyframes_all += nb_keyframes[i]; keyframes[i] = calloc(nb_keyframes[i],sizeof(AVIndexEntry)); for(k = 0 ; k < nb_keyframes[i]; k++) { keyframes[i][k] = flv->streams[0]->index_entries[k]; keyframes[i][k].pos -= flv->streams[0]->index_entries[0].pos; } datasizes[i] = strtod(av_dict_get(flv->metadata,"datasize",NULL,0)->value,NULL); avformat_free_context(flv); } for (i = 1; i < nb_files ; i++) { int k; AVFormatContext * flv=NULL; char * filename; filename = argv[i+1]; avformat_open_input(&flv,filename,NULL,NULL); av_find_stream_info(flv); durations[i] = flv->duration; duration += durations[i]; //获得 keyframes position keyframes[i] = calloc(nb_keyframes[i],sizeof(AVIndexEntry)); nb_keyframes_all += nb_keyframes[i]; for(k = 0 ; k < nb_keyframes[i]; k++) { keyframes[i][k] = flv->streams[0]->index_entries[k]; keyframes[i][k].pos -= flv->streams[0]->index_entries[0].pos; } datasizes[i] = strtod(av_dict_get(flv->metadata,"datasize",NULL,0)->value,NULL) - flv->streams[0]->index_entries->pos ; avformat_free_context(flv); } //正常的开始写 flv 文件 吧 out = fopen("out.flv","w"); flv_write_header(); flv_write_metadata(); uint64_t timestamp; uint32_t lastsize; for (lastsize = 0,i = 0 ,timestamp = 0; i < argc - 1; timestamp += durations[i],i++ ) { char * filename; filename = argv[i+1]; FILE * in = fopen(filename,"r"); lastsize = flv_merge_stream(in,out,timestamp,lastsize); } fclose(out); return EXIT_SUCCESS; }
/* 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; }
int main(int argc, char** argv) { flvtag_t tag; srt_t* old_srt = NULL; srt_cue_t* next_cue = NULL; double timestamp, offset = 0, clear_timestamp = 0; int has_audio, has_video; FILE* flv = flv_open_read(argv[1]); int fd = open(argv[2], O_RDWR); FILE* out = flv_open_write(argv[3]); flvtag_init(&tag); if (!flv_read_header(flv, &has_audio, &has_video)) { fprintf(stderr, "%s is not an flv file\n", argv[1]); return EXIT_FAILURE; } flv_write_header(out, has_audio, has_video); fprintf(stderr, "Reading flv from %s\n", argv[1]); fprintf(stderr, "Reading captons from %s\n", argv[2]); fprintf(stderr, "Writing flv to %s\n", argv[3]); while (flv_read_tag(flv, &tag)) { srt_t* cur_srt = srt_from_fd(fd); timestamp = flvtag_pts_seconds(&tag); if (cur_srt) { fprintf(stderr, "Loaded new SRT at time %f\n", timestamp); if (old_srt != NULL) { srt_free(old_srt); } old_srt = cur_srt; offset = timestamp; clear_timestamp = timestamp; next_cue = cur_srt->cue_head; } if (flvtag_avcpackettype_nalu == flvtag_avcpackettype(&tag)) { if (next_cue && (offset + next_cue->timestamp) <= timestamp) { fprintf(stderr, "T: %0.02f (%0.02fs):\n%s\n", (offset + next_cue->timestamp), next_cue->duration, srt_cue_data(next_cue)); clear_timestamp = (offset + next_cue->timestamp) + next_cue->duration; flvtag_addcaption_text(&tag, srt_cue_data(next_cue)); next_cue = next_cue->next; } else if (0 <= clear_timestamp && clear_timestamp <= timestamp) { fprintf(stderr, "T: %0.02f: [CAPTIONS CLEARED]\n", timestamp); flvtag_addcaption_text(&tag, NULL); clear_timestamp = -1; } } flv_write_tag(out, &tag); } srt_free(old_srt); flvtag_free(&tag); flv_close(flv); flv_close(out); return EXIT_SUCCESS; }