//---------------------------------------------------------------- // main_utf8 // int main_utf8(int argc, char **argv) { const char *input = NULL; const char *output_prefix = ""; double target_segment_duration = 0.0; char *segment_duration_check = NULL; const char *playlist_filename = NULL; const char *http_prefix = ""; long max_tsfiles = 0; char *max_tsfiles_check = NULL; double prev_segment_time = 0.0; double segment_duration = 0.0; unsigned int output_index = 0; const AVClass *fc = avformat_get_class(); AVDictionary *format_opts = NULL; AVOutputFormat *ofmt = NULL; AVFormatContext *ic = NULL; AVFormatContext *oc = NULL; AVStream *video_st = NULL; AVStream *audio_st = NULL; AVCodec *codec = NULL; char *output_filename = NULL; char *pid_filename = NULL; int video_index = -1; int audio_index = -1; int kill_file = 0; int decode_done = 0; int ret = 0; int i = 0; TSMStreamLace * streamLace = NULL; TSMPlaylist * playlist = NULL; const double segment_duration_error_tolerance = 0.05; double extra_duration_needed = 0; int strict_segment_duration = 0; av_log_set_level(AV_LOG_INFO); for (int i = 1; i < argc; i++) { if (strcmp(argv[i], "-i") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -i parameter"); i++; input = argv[i]; } else if (strcmp(argv[i], "-o") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -i parameter"); i++; output_prefix = argv[i]; } else if (strcmp(argv[i], "-d") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -d parameter"); i++; target_segment_duration = strtod(argv[i], &segment_duration_check); if (segment_duration_check == argv[i] || target_segment_duration == HUGE_VAL || target_segment_duration == -HUGE_VAL) { usage3(argv, "invalid segment duration: ", argv[i]); } } else if (strcmp(argv[i], "-x") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -x parameter"); i++; playlist_filename = argv[i]; } else if (strcmp(argv[i], "-p") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -p parameter"); i++; http_prefix = argv[i]; } else if (strcmp(argv[i], "-w") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -w parameter"); i++; max_tsfiles = strtol(argv[i], &max_tsfiles_check, 10); if (max_tsfiles_check == argv[i] || max_tsfiles < 0 || max_tsfiles >= INT_MAX) { usage3(argv, "invalid live stream max window size: ", argv[i]); } } else if (strcmp(argv[i], "-P") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -P parameter"); i++; pid_filename = argv[i]; } else if (strcmp(argv[i], "--watch-for-kill-file") == 0) { // end program when it finds a file with name 'kill': kill_file = 1; } else if (strcmp(argv[i], "--strict-segment-duration") == 0) { // force segment creation on non-keyframe boundaries: strict_segment_duration = 1; } else if (strcmp(argv[i], "--avformat-option") == 0) { const AVOption *of; const char *opt; const char *arg; if ((argc - i) <= 1) usage(argv, "could not parse --avformat-option parameter"); i++; opt = argv[i]; if ((argc - i) <= 1) usage(argv, "could not parse --avformat-option parameter"); i++; arg = argv[i]; if ((of = av_opt_find(&fc, opt, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) av_dict_set(&format_opts, opt, arg, (of->type == AV_OPT_TYPE_FLAGS) ? AV_DICT_APPEND : 0); else usage3(argv, "unknown --avformat-option parameter: ", opt); } else if (strcmp(argv[i], "--loglevel") == 0) { const char *arg; if ((argc - i) <= 1) usage(argv, "could not parse --loglevel parameter"); i++; arg = argv[i]; if (loglevel(arg)) usage3(argv, "unknown --loglevel parameter: ", arg); } } if (!input) { usage(argv, "-i input file parameter must be specified"); } if (!playlist_filename) { usage(argv, "-x m3u8 playlist file parameter must be specified"); } if (target_segment_duration == 0.0) { usage(argv, "-d segment duration parameter must be specified"); } // Create PID file if (pid_filename) { FILE* pid_file = fopen_utf8(pid_filename, "wb"); if (pid_file) { fprintf(pid_file, "%d", getpid()); fclose(pid_file); } } av_register_all(); avformat_network_init(); if (!strcmp(input, "-")) { input = "pipe:"; } output_filename = malloc(sizeof(char) * (strlen(output_prefix) + 15)); if (!output_filename) { fprintf(stderr, "Could not allocate space for output filenames\n"); goto error; } playlist = createPlaylist(max_tsfiles, target_segment_duration, http_prefix); if (!playlist) { fprintf(stderr, "Could not allocate space for m3u8 playlist structure\n"); goto error; } ret = avformat_open_input(&ic, input, NULL, (format_opts) ? &format_opts : NULL); if (ret != 0) { fprintf(stderr, "Could not open input file, make sure it is an mpegts or mp4 file: %d\n", ret); goto error; } av_dict_free(&format_opts); if (avformat_find_stream_info(ic, NULL) < 0) { fprintf(stderr, "Could not read stream information\n"); goto error; } #if LIBAVFORMAT_VERSION_MAJOR > 52 || (LIBAVFORMAT_VERSION_MAJOR == 52 && \ LIBAVFORMAT_VERSION_MINOR >= 45) ofmt = av_guess_format("mpegts", NULL, NULL); #else ofmt = guess_format("mpegts", NULL, NULL); #endif if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer\n"); goto error; } oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "Could not allocated output context\n"); goto error; } oc->oformat = ofmt; video_index = -1; audio_index = -1; for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; ic->streams[i]->discard = AVDISCARD_NONE; video_st = add_output_stream(oc, ic->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; ic->streams[i]->discard = AVDISCARD_NONE; audio_st = add_output_stream(oc, ic->streams[i]); break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } av_dump_format(oc, 0, output_prefix, 1); if (video_index >=0) { codec = avcodec_find_decoder(video_st->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder, key frames will not be honored\n"); } if (avcodec_open2(video_st->codec, codec, NULL) < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored\n"); } } snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, ++output_index); if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); goto error; } if (avformat_write_header(oc, NULL)) { fprintf(stderr, "Could not write mpegts header to first output file\n"); goto error; } prev_segment_time = (double)(ic->start_time) / (double)(AV_TIME_BASE); streamLace = createStreamLace(ic->nb_streams); do { double segment_time = 0.0; AVPacket packet; double packetStartTime = 0.0; double packetDuration = 0.0; if (!decode_done) { decode_done = av_read_frame(ic, &packet); if (!decode_done) { if (packet.stream_index != video_index && packet.stream_index != audio_index) { av_free_packet(&packet); continue; } double timeStamp = (double)(packet.pts) * (double)(ic->streams[packet.stream_index]->time_base.num) / (double)(ic->streams[packet.stream_index]->time_base.den); if (av_dup_packet(&packet) < 0) { fprintf(stderr, "Could not duplicate packet\n"); av_free_packet(&packet); break; } insertPacket(streamLace, &packet, timeStamp); } } if (countPackets(streamLace) < 50 && !decode_done) { /* allow the queue to fill up so that the packets can be sorted properly */ continue; } if (!removePacket(streamLace, &packet)) { if (decode_done) { /* the queue is empty, we are done */ break; } assert(decode_done); continue; } packetStartTime = (double)(packet.pts) * (double)(ic->streams[packet.stream_index]->time_base.num) / (double)(ic->streams[packet.stream_index]->time_base.den); packetDuration = (double)(packet.duration) * (double)(ic->streams[packet.stream_index]->time_base.num) / (double)(ic->streams[packet.stream_index]->time_base.den); #if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG)) if (av_log_get_level() >= AV_LOG_VERBOSE) fprintf(stderr, "stream %i, packet [%f, %f)\n", packet.stream_index, packetStartTime, packetStartTime + packetDuration); #endif segment_duration = packetStartTime + packetDuration - prev_segment_time; // NOTE: segments are supposed to start on a keyframe. // If the keyframe interval and segment duration do not match // forcing the segment creation for "better seeking behavior" // will result in decoding artifacts after seeking or stream switching. if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY || strict_segment_duration)) { segment_time = packetStartTime; } else if (video_index < 0) { segment_time = packetStartTime; } else { segment_time = prev_segment_time; } if (segment_time - prev_segment_time + segment_duration_error_tolerance > target_segment_duration + extra_duration_needed) { avio_flush(oc->pb); avio_close(oc->pb); // Keep track of accumulated rounding error to account for it in later chunks. double segment_duration = segment_time - prev_segment_time; int rounded_segment_duration = (int)(segment_duration + 0.5); extra_duration_needed += (double)rounded_segment_duration - segment_duration; updatePlaylist(playlist, playlist_filename, output_filename, output_index, rounded_segment_duration); snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, ++output_index); if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); break; } // close when we find the 'kill' file if (kill_file) { FILE* fp = fopen("kill", "rb"); if (fp) { fprintf(stderr, "user abort: found kill file\n"); fclose(fp); remove("kill"); decode_done = 1; removeAllPackets(streamLace); } } prev_segment_time = segment_time; } ret = av_interleaved_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream\n"); } else if (ret > 0) { fprintf(stderr, "End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done || countPackets(streamLace) > 0); av_write_trailer(oc); if (video_index >= 0) { avcodec_close(video_st->codec); } for(i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } avio_close(oc->pb); av_free(oc); updatePlaylist(playlist, playlist_filename, output_filename, output_index, segment_duration); closePlaylist(playlist); releasePlaylist(&playlist); if (pid_filename) { remove(pid_filename); } return 0; error: if (pid_filename) { remove(pid_filename); } return 1; }
int main(int argc, char **argv) { if (argc != 5) { fprintf(stderr, "Usage: %s <segment length> <output location> <filename prefix> <encoding profile>\n", argv[0]); return 1; } struct config_info config; memset(&config, 0, sizeof(struct config_info)); config.segment_length = atoi(argv[1]); config.temp_directory = argv[2]; config.filename_prefix = argv[3]; config.encoding_profile = argv[4]; config.input_filename = "pipe://1"; char *output_filename = malloc(sizeof(char) * (strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 10)); if (!output_filename) { fprintf(stderr, "Segmenter error: Could not allocate space for output filenames\n"); exit(1); } // ------------------ Done parsing input -------------- av_register_all(); AVInputFormat *input_format = av_find_input_format("mpegts"); if (!input_format) { fprintf(stderr, "Segmenter error: Could not find MPEG-TS demuxer\n"); exit(1); } AVFormatContext *input_context = NULL; int ret = avformat_open_input(&input_context, config.input_filename, input_format, NULL); if (ret != 0) { fprintf(stderr, "Segmenter error: Could not open input file, make sure it is an mpegts file: %d\n", ret); exit(1); } if (avformat_find_stream_info(input_context, NULL) < 0) { fprintf(stderr, "Segmenter error: Could not read stream information\n"); exit(1); } AVOutputFormat *output_format = av_guess_format("mpegts", NULL, NULL); if (!output_format) { fprintf(stderr, "Segmenter error: Could not find MPEG-TS muxer\n"); exit(1); } AVFormatContext *output_context = avformat_alloc_context(); if (!output_context) { fprintf(stderr, "Segmenter error: Could not allocated output context"); exit(1); } output_context->oformat = output_format; // Don't print warnings when PTS and DTS are identical. input_context->flags |= AVFMT_FLAG_IGNDTS; int video_index = -1; int audio_index = -1; AVStream *video_stream; AVStream *audio_stream; int i; for (i = 0; i < input_context->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (input_context->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; input_context->streams[i]->discard = AVDISCARD_NONE; video_stream = add_output_stream(output_context, input_context->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; input_context->streams[i]->discard = AVDISCARD_NONE; audio_stream = add_output_stream(output_context, input_context->streams[i]); break; default: input_context->streams[i]->discard = AVDISCARD_ALL; break; } } #if LIBAVFORMAT_VERSION_MAJOR < 54 if (av_set_parameters(output_context, NULL) < 0) { fprintf(stderr, "Segmenter error: Invalid output format parameters\n"); exit(1); } #endif av_dump_format(output_context, 0, config.filename_prefix, 1); if (video_index >= 0) { AVCodec *codec = avcodec_find_decoder(video_stream->codec->codec_id); if (!codec) { fprintf(stderr, "Segmenter error: Could not find video decoder, key frames will not be honored\n"); } if (avcodec_open2(video_stream->codec, codec, NULL) < 0) { fprintf(stderr, "Segmenter error: Could not open video decoder, key frames will not be honored\n"); } } if (video_stream->codec->ticks_per_frame > 1) { // h264 sets the ticks_per_frame and time_base.den but not time_base.num // since we don't use ticks_per_frame, adjust time_base.num accordingly. video_stream->codec->time_base.num *= video_stream->codec->ticks_per_frame; } unsigned int output_index = 1; snprintf(output_filename, strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 10, "%s/%s-%05u.ts", config.temp_directory, config.filename_prefix, output_index++); if (avio_open(&output_context->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename); exit(1); } if (avformat_write_header(output_context, NULL)) { fprintf(stderr, "Segmenter error: Could not write mpegts header to first output file\n"); exit(1); } // Track initial PTS values so we can subtract them out (removing aduio/video delay, since they seem incorrect). int64_t initial_audio_pts = -1; int64_t initial_video_pts = -1; unsigned int first_segment = 1; unsigned int last_segment = 0; double prev_segment_time = 0; int decode_done; do { double segment_time; AVPacket packet; decode_done = av_read_frame(input_context, &packet); if (decode_done < 0) { break; } if (av_dup_packet(&packet) < 0) { fprintf(stderr, "Segmenter error: Could not duplicate packet"); av_free_packet(&packet); break; } if (packet.stream_index == video_index) { if (initial_video_pts < 0) initial_video_pts = packet.pts; packet.pts -= initial_video_pts; packet.dts = packet.pts; if (packet.flags & AV_PKT_FLAG_KEY) { segment_time = (double)packet.pts * video_stream->time_base.num / video_stream->time_base.den; } else { segment_time = prev_segment_time; } } else if (packet.stream_index == audio_index) { if (initial_audio_pts < 0) initial_audio_pts = packet.pts; packet.pts -= initial_audio_pts; packet.dts = packet.pts; segment_time = prev_segment_time; } else { segment_time = prev_segment_time; } // done writing the current file? if (segment_time - prev_segment_time >= config.segment_length) { avio_flush(output_context->pb); avio_close(output_context->pb); output_transfer_command(first_segment, ++last_segment, 0, config.encoding_profile); snprintf(output_filename, strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 10, "%s/%s-%05u.ts", config.temp_directory, config.filename_prefix, output_index++); if (avio_open (&output_context->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename); break; } prev_segment_time = segment_time; } ret = av_write_frame(output_context, &packet); if (ret < 0) { fprintf(stderr, "Segmenter error: Could not write frame of stream: %d\n", ret); } else if (ret > 0) { fprintf(stderr, "Segmenter info: End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done); av_write_trailer(output_context); if (video_index >= 0) { avcodec_close(video_stream->codec); } for (i = 0; i < output_context->nb_streams; i++) { av_freep(&output_context->streams[i]->codec); av_freep(&output_context->streams[i]); } avio_close(output_context->pb); av_free(output_context); output_transfer_command(first_segment, ++last_segment, 1, config.encoding_profile); return 0; }
int main(int argc, const char *argv[]) { //input parameters FNHOLDER(inputFilename); FNHOLDER(playlistFilename); baseDirName [MAX_FILENAME_LENGTH ]=0; baseFileName[MAX_FILENAME_LENGTH ]=0; currentOutputDirName[MAX_FILENAME_LENGTH ]=0; baseFileExtension[MAX_FILENAME_LENGTH ]=0; baseFileExtension[MAXT_EXT_LENGTH]=0; //either "ts", "aac" or "mp3" int segmentLength, quiet, version,usage; FNHOLDER(tempPlaylistName); //these are used to determine the exact length of the current segment double segment_start_time = 0; unsigned int actual_segment_durations[MAX_SEGMENTS+1]; double packet_time = 0; unsigned int output_index = 1; AVOutputFormat *ofmt=NULL; AVFormatContext *ic = NULL; AVFormatContext *oc; AVStream *in_video_st = NULL; AVStream *in_audio_st = NULL; AVStream *out_video_st = NULL; AVStream *out_audio_st = NULL; AVCodec *codec; unsigned int num_segments = 0; int decode_done; int ret; int i; int listlen; int listofs=1; int persist=0; if ( parseCommandLine(argc, argv,inputFilename, playlistFilename, baseDirName, baseFileName, baseFileExtension, &segmentLength, &listlen, &quiet, &version,&usage,&persist) != 0) return 0; if (usage) printUsage(); if (version) ffmpeg_version(); if (version || usage) return 0; //fprintf(stderr, "Options parsed: inputFilename:%s playlistFilename:%s baseDirName:%s baseFileName:%s baseFileExtension:%s segmentLength:%d\n",inputFilename,playlistFilename,baseDirName,baseFileName,baseFileExtension,segmentLength ); if (listlen>0){ snprintf(tempPlaylistName, MAX_FILENAME_LENGTH, "%s/%s", baseDirName, playlistFilename); strncpy(playlistFilename, tempPlaylistName, MAX_FILENAME_LENGTH); snprintf(tempPlaylistName, MAX_FILENAME_LENGTH, "%s.tmp", playlistFilename); } //if (!quiet) av_log_set_level(AV_LOG_DEBUG); av_register_all(); avformat_network_init(); //just to be safe with later version and be able to handle all kind of input urls while(1) { ret = avformat_open_input(&ic, inputFilename, NULL, NULL); if (ret != 0) { if (persist) { sleep(1); continue; } fprintf(stderr, "Could not open input file %s. Error %d.\n", inputFilename, ret); exit(1); } if (avformat_find_stream_info(ic, NULL) < 0) { fprintf(stderr, "Could not read stream information.\n"); if (persist){ avformat_close_input(&ic); sleep(1); continue; } exit(1); } oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "Could not allocate output context."); if (persist){ avformat_close_input(&ic); sleep(1); continue; } exit(1); } int in_video_index = -1; int in_audio_index = -1; int out_video_index = -1; int out_audio_index = -1; for (i = 0; i < ic->nb_streams; i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: if (!out_video_st) { in_video_st=ic->streams[i]; in_video_index = i; in_video_st->discard = AVDISCARD_NONE; out_video_st = add_output_stream(oc, in_video_st); out_video_index=out_video_st->index; } break; case AVMEDIA_TYPE_AUDIO: if (!out_audio_st) { in_audio_st=ic->streams[i]; in_audio_index = i; in_audio_st->discard = AVDISCARD_NONE; out_audio_st = add_output_stream(oc, in_audio_st); out_audio_index=out_audio_st->index; } break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } if (in_video_index == -1) { fprintf(stderr, "Source stream must have video component.\n"); if (persist){ avformat_close_input(&ic); avformat_free_context(oc); sleep(1); continue; } exit(1); } if (!ofmt) ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer.\n"); exit(1); } oc->oformat = ofmt; if (oc->oformat->flags & AVFMT_GLOBALHEADER) oc->flags |= CODEC_FLAG_GLOBAL_HEADER; av_dump_format(oc, 0, baseFileName, 1); codec = avcodec_find_decoder(in_video_st->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder, key frames will not be honored.\n"); } ret = avcodec_open2(in_video_st->codec, codec, NULL); if (ret < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored.\n"); } if (listlen>0){ snprintf(currentOutputFileName, MAX_FILENAME_LENGTH, "%s/%s-%u%s", baseDirName, baseFileName, output_index, baseFileExtension); } else { //archive mode localtime_r_ex(&start_time); fillofn(); } if (avio_open(&oc->pb, currentOutputFileName,AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'.\n", currentOutputFileName); exit(1); } else if (!quiet) fprintf(stderr, "Starting segment '%s'\n", currentOutputFileName); int r = avformat_write_header(oc, NULL); if (r) { fprintf(stderr, "Could not write mpegts header to first output file.\n"); debugReturnCode(r); exit(1); } int waitfirstpacket=1; time_t first_frame_sec=time(NULL); int iskeyframe=0; double vid_pts2time=(double)in_video_st->time_base.num / in_video_st->time_base.den; //double aud_pts2time=0; //if (in_audio_st) aud_pts2time=(double)in_audio_st->time_base.num / in_audio_st->time_base.den; double prev_packet_time=0; do { AVPacket packet; decode_done = av_read_frame(ic, &packet); if (decode_done < 0) { break; } //a potential memory leak: // if (av_dup_packet(&packet) < 0) { // fprintf(stderr, "Could not duplicate packet."); // av_packet_unref(&packet); // break; // } //get the most recent packet time //this time is used when the time for the final segment is printed. It may not be on the edge of //of a keyframe! if (packet.stream_index == in_video_index) { packet.stream_index = out_video_index; packet_time = (double) packet.pts * vid_pts2time; iskeyframe=packet.flags & AV_PKT_FLAG_KEY; if (iskeyframe && waitfirstpacket) { waitfirstpacket=0; prev_packet_time=packet_time; segment_start_time=packet_time; first_frame_sec=time(NULL); } } else if (packet.stream_index == in_audio_index){ packet.stream_index = out_audio_index; iskeyframe=0; } else { //how this got here?! av_packet_unref(&packet); continue; } if (waitfirstpacket) { av_packet_unref(&packet); continue; } //start looking for segment splits for videos one half second before segment duration expires. This is because the //segments are split on key frames so we cannot expect all segments to be split exactly equally. if (iskeyframe && ((packet_time - segment_start_time) >= (segmentLength - 0.25)) && (time(NULL)!=first_frame_sec)) { //a keyframe near or past segmentLength -> SPLIT avio_flush(oc->pb); avio_close(oc->pb); if (listlen>0){ actual_segment_durations[num_segments] = (unsigned int) rint(prev_packet_time - segment_start_time); num_segments++; if (num_segments>listlen) { //move list to exclude last: snprintf(currentOutputFileName, MAX_FILENAME_LENGTH, "%s/%s-%u%s", baseDirName, baseFileName, listofs, baseFileExtension); unlink (currentOutputFileName); listofs++; num_segments--; memmove(actual_segment_durations,actual_segment_durations+1,num_segments*sizeof(actual_segment_durations[0])); } write_index_file(playlistFilename, tempPlaylistName, segmentLength, num_segments,actual_segment_durations, listofs, baseFileName, baseFileExtension, (num_segments>=MAX_SEGMENTS)); if (num_segments==MAX_SEGMENTS) { fprintf(stderr, "Reached \"hard\" max segment number %u. If this is not live stream increase segment duration. If live segmenting set max list lenth (-m ...)\n", MAX_SEGMENTS); break; } output_index++; snprintf(currentOutputFileName, MAX_FILENAME_LENGTH, "%s/%s-%u%s", baseDirName, baseFileName, output_index, baseFileExtension); } else { //archive mode: localtime_r_ex(&end_time); fixofn(); } if (avio_open(&oc->pb, currentOutputFileName, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", currentOutputFileName); break; } else if (!quiet) fprintf(stderr, "Starting segment '%s'\n", currentOutputFileName); fflush(stderr); segment_start_time = packet_time; first_frame_sec=time(NULL); } if (packet.stream_index == out_video_index) prev_packet_time=packet_time; ret = av_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream.\n"); } else if (ret > 0) { fprintf(stderr, "End of stream requested.\n"); av_packet_unref(&packet); break; } av_packet_unref(&packet); } while (!decode_done); if (in_video_st->codec->codec !=NULL) avcodec_close(in_video_st->codec); if (num_segments<MAX_SEGMENTS) { //make sure all packets are written and then close the last file. avio_flush(oc->pb); av_write_trailer(oc); for (i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } avio_close(oc->pb); av_free(oc); if (num_segments>0){ actual_segment_durations[num_segments] = (unsigned int) rint(packet_time - segment_start_time); if (actual_segment_durations[num_segments] == 0) actual_segment_durations[num_segments] = 1; num_segments++; write_index_file(playlistFilename, tempPlaylistName, segmentLength, num_segments,actual_segment_durations, listofs, baseFileName, baseFileExtension, 1); } else { //archive mode localtime_r_ex(&end_time); fixofn(); } } // struct stat st; // stat(currentOutputFileName, &st); // output_bytes += st.st_size; avformat_close_input(&ic); break; } return 0; }
int main(int argc, char **argv) { if(argc != 5) { fprintf(stderr, "Usage: %s <segment length> <output location> <filename prefix> <encoding profile>\n", argv[0]); return 1; } struct config_info config; memset(&config, 0, sizeof(struct config_info)); config.segment_length = atoi(argv[1]); config.temp_directory = argv[2]; config.filename_prefix = argv[3]; config.encoding_profile = argv[4]; config.input_filename = "pipe://1"; char *output_filename = malloc(sizeof(char) * (strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 10)); if (!output_filename) { fprintf(stderr, "Segmenter error: Could not allocate space for output filenames\n"); exit(1); } // ------------------ Done parsing input -------------- av_register_all(); AVInputFormat *input_format = av_find_input_format("mpegts"); if (!input_format) { fprintf(stderr, "Segmenter error: Could not find MPEG-TS demuxer\n"); exit(1); } AVFormatContext *input_context = NULL; int ret = av_open_input_file(&input_context, config.input_filename, input_format, 0, NULL); if (ret != 0) { fprintf(stderr, "Segmenter error: Could not open input file, make sure it is an mpegts file: %d\n", ret); exit(1); } if (av_find_stream_info(input_context) < 0) { fprintf(stderr, "Segmenter error: Could not read stream information\n"); exit(1); } AVOutputFormat *output_format = av_guess_format("mpegts", NULL, NULL); if (!output_format) { fprintf(stderr, "Segmenter error: Could not find MPEG-TS muxer\n"); exit(1); } AVFormatContext *output_context = avformat_alloc_context(); if (!output_context) { fprintf(stderr, "Segmenter error: Could not allocated output context"); exit(1); } output_context->oformat = output_format; int video_index = -1; int audio_index = -1; AVStream *video_stream; AVStream *audio_stream; int i; for (i = 0; i < input_context->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (input_context->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; input_context->streams[i]->discard = AVDISCARD_NONE; video_stream = add_output_stream(output_context, input_context->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; input_context->streams[i]->discard = AVDISCARD_NONE; audio_stream = add_output_stream(output_context, input_context->streams[i]); break; default: input_context->streams[i]->discard = AVDISCARD_ALL; break; } } if (av_set_parameters(output_context, NULL) < 0) { fprintf(stderr, "Segmenter error: Invalid output format parameters\n"); exit(1); } av_dump_format(output_context, 0, config.filename_prefix, 1); if(video_index >= 0) { AVCodec *codec = avcodec_find_decoder(video_stream->codec->codec_id); if (!codec) { fprintf(stderr, "Segmenter error: Could not find video decoder, key frames will not be honored\n"); } if (avcodec_open(video_stream->codec, codec) < 0) { fprintf(stderr, "Segmenter error: Could not open video decoder, key frames will not be honored\n"); } } unsigned int output_index = 1; snprintf(output_filename, strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 10, "%s/%s-%05u.ts", config.temp_directory, config.filename_prefix, output_index++); if (avio_open(&output_context->pb, output_filename, URL_WRONLY) < 0) { fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename); exit(1); } if (av_write_header(output_context)) { fprintf(stderr, "Segmenter error: Could not write mpegts header to first output file\n"); exit(1); } unsigned int first_segment = 1; unsigned int last_segment = 0; double prev_segment_time = 0; int decode_done; do { double segment_time; AVPacket packet; decode_done = av_read_frame(input_context, &packet); if (decode_done < 0) { break; } if (av_dup_packet(&packet) < 0) { fprintf(stderr, "Segmenter error: Could not duplicate packet"); av_free_packet(&packet); break; } if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY)) { segment_time = (double)video_stream->pts.val * video_stream->time_base.num / video_stream->time_base.den; } else if (video_index < 0) { segment_time = (double)audio_stream->pts.val * audio_stream->time_base.num / audio_stream->time_base.den; } else { segment_time = prev_segment_time; } // done writing the current file? if (segment_time - prev_segment_time >= config.segment_length) { avio_flush(output_context->pb); avio_close(output_context->pb); output_transfer_command(first_segment, ++last_segment, 0, config.encoding_profile); snprintf(output_filename, strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 10, "%s/%s-%05u.ts", config.temp_directory, config.filename_prefix, output_index++); if (avio_open(&output_context->pb, output_filename, URL_WRONLY) < 0) { fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename); break; } prev_segment_time = segment_time; } ret = av_interleaved_write_frame(output_context, &packet); if (ret < 0) { fprintf(stderr, "Segmenter error: Could not write frame of stream: %d\n", ret); } else if (ret > 0) { fprintf(stderr, "Segmenter info: End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done); av_write_trailer(output_context); if (video_index >= 0) { avcodec_close(video_stream->codec); } for(i = 0; i < output_context->nb_streams; i++) { av_freep(&output_context->streams[i]->codec); av_freep(&output_context->streams[i]); } avio_close(output_context->pb); av_free(output_context); output_transfer_command(first_segment, ++last_segment, 1, config.encoding_profile); return 0; }
int segment(char *input_file, char* base_dirpath, char* output_index_file, char *base_file_name, char* base_file_extension, int segmentLength, int listlen) { unsigned int output_index = 1; AVOutputFormat *ofmt = NULL; AVFormatContext *ic = NULL; AVFormatContext *oc; AVStream *in_video_st = NULL; AVStream *in_audio_st = NULL; AVStream *out_video_st = NULL; AVStream *out_audio_st = NULL; AVCodec *codec; int ret; int quiet = 0; char currentOutputFileName[MAX_FILENAME_LENGTH] = {0}; double segment_start_time = 0.0; char tmp_output_index_file[MAX_FILENAME_LENGTH] = {0}; unsigned int actual_segment_durations[MAX_SEGMENTS+1]; double packet_time = 0; sprintf(tmp_output_index_file, "%s.tmp", output_index_file); av_register_all(); avformat_network_init(); //just to be safe with later version and be able to handle all kind of input urls AVStream *in_stream = NULL; AVStream *out_stream = NULL; ret = avformat_open_input(&ic, input_file, NULL, NULL); if (ret != 0) { fprintf(stderr, "Could not open input file %s. Error %d.\n", input_file, ret); exit(1); } if (avformat_find_stream_info(ic, NULL) < 0) { fprintf(stderr, "Could not read stream information.\n"); avformat_close_input(&ic); exit(1); } oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "Could not allocate output context."); avformat_close_input(&ic); exit(1); } int in_video_index = -1; int in_audio_index = -1; int out_video_index = -1; int out_audio_index = -1; int i; int listofs = 1; for (i = 0; i < ic->nb_streams; i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: if (!out_video_st) { in_video_st=ic->streams[i]; in_video_index = i; in_video_st->discard = AVDISCARD_NONE; out_video_st = add_output_stream(oc, in_video_st); out_video_index=out_video_st->index; } break; case AVMEDIA_TYPE_AUDIO: if (!out_audio_st) { in_audio_st=ic->streams[i]; in_audio_index = i; in_audio_st->discard = AVDISCARD_NONE; out_audio_st = add_output_stream(oc, in_audio_st); out_audio_index=out_audio_st->index; } break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } if (in_video_index == -1) { fprintf(stderr, "Source stream must have video component.\n"); avformat_close_input(&ic); avformat_free_context(oc); exit(1); } if (!ofmt) ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer.\n"); exit(1); } oc->oformat = ofmt; if (oc->oformat->flags & AVFMT_GLOBALHEADER) oc->flags |= CODEC_FLAG_GLOBAL_HEADER; av_dump_format(oc, 0, base_file_name, 1); codec = avcodec_find_decoder(in_video_st->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder, key frames will not be honored.\n"); } ret = avcodec_open2(in_video_st->codec, codec, NULL); if (ret < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored.\n"); } snprintf(currentOutputFileName, MAX_FILENAME_LENGTH, "%s/%s-%u%s", base_dirpath, base_file_name, output_index, base_file_extension); if (avio_open(&oc->pb, currentOutputFileName,AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'.\n", currentOutputFileName); exit(1); } else if (!quiet) { fprintf(stderr, "Starting segment '%s'\n", currentOutputFileName); } int r = avformat_write_header(oc, NULL); if (r) { fprintf(stderr, "Could not write mpegts header to first output file.\n"); exit(1); } unsigned int num_segments = 0; int decode_done; int waitfirstpacket = 1; time_t first_frame_sec = time(NULL); int iskeyframe = 0; double vid_pts2time = (double)in_video_st->time_base.num / in_video_st->time_base.den; #if USE_H264BSF AVBitStreamFilterContext* h264bsfc = av_bitstream_filter_init("h264_mp4toannexb"); #endif double prev_packet_time = 0; do { AVPacket packet; decode_done = av_read_frame(ic, &packet); if (decode_done < 0) { break; } //get the most recent packet time //this time is used when the time for the final segment is printed. It may not be on the edge of //of a keyframe! if (packet.stream_index == in_video_index) { #if USE_H264BSF av_bitstream_filter_filter(h264bsfc, ic->streams[in_video_index]->codec, NULL, &packet.data, &packet.size, packet.data, packet.size, 0); #endif packet.stream_index = out_video_index; packet_time = (double) packet.pts * vid_pts2time; iskeyframe = packet.flags & AV_PKT_FLAG_KEY; if (iskeyframe && waitfirstpacket) { waitfirstpacket = 0; prev_packet_time = packet_time; segment_start_time = packet_time; first_frame_sec = time(NULL); } } else if (packet.stream_index == in_audio_index){ packet.stream_index = out_audio_index; iskeyframe=0; } else { //how this got here?! av_free_packet(&packet); continue; } if (waitfirstpacket) { av_free_packet(&packet); continue; } //start looking for segment splits for videos one half second before segment duration expires. This is because the //segments are split on key frames so we cannot expect all segments to be split exactly equally. if (iskeyframe && ((packet_time - segment_start_time) >= (segmentLength - 0.25)) ) { //a keyframe near or past segmentLength -> SPLIT printf("key frame packet time=%f, start time=%f\n", packet_time, segment_start_time); avio_flush(oc->pb); avio_close(oc->pb); actual_segment_durations[num_segments] = (unsigned int) rint(prev_packet_time - segment_start_time); num_segments++; if (num_segments > listlen) { //move list to exclude last: snprintf(currentOutputFileName, MAX_FILENAME_LENGTH, "%s/%s-%u%s", base_dirpath, base_file_name, listofs, base_file_extension); unlink (currentOutputFileName); listofs++; num_segments--; memmove(actual_segment_durations,actual_segment_durations+1,num_segments*sizeof(actual_segment_durations[0])); } write_index_file(output_index_file, tmp_output_index_file, segmentLength, num_segments, actual_segment_durations, listofs, base_file_name, base_file_extension, (num_segments >= MAX_SEGMENTS)); if (num_segments == MAX_SEGMENTS) { fprintf(stderr, "Reached \"hard\" max segment number %u. If this is not live stream increase segment duration. If live segmenting set max list lenth (-m ...)\n", MAX_SEGMENTS); break; } output_index++; snprintf(currentOutputFileName, MAX_FILENAME_LENGTH, "%s/%s-%u%s", base_dirpath, base_file_name, output_index, base_file_extension); if (avio_open(&oc->pb, currentOutputFileName, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", currentOutputFileName); break; } else if (!quiet) { fprintf(stderr, "Starting segment '%s'\n", currentOutputFileName); } fflush(stderr); segment_start_time = packet_time; first_frame_sec=time(NULL); } if (packet.stream_index == out_video_index) { prev_packet_time = packet_time; } in_stream = ic->streams[packet.stream_index]; out_stream = oc->streams[packet.stream_index]; packet.pts = av_rescale_q_rnd(packet.pts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); packet.dts = av_rescale_q_rnd(packet.dts, in_stream->time_base, out_stream->time_base, AV_ROUND_NEAR_INF | AV_ROUND_PASS_MINMAX); packet.duration = av_rescale_q(packet.duration, in_stream->time_base, out_stream->time_base); packet.pos = -1; ret = av_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream.\n"); } else if (ret > 0) { fprintf(stderr, "End of stream requested.\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done); if (in_video_st->codec->codec !=NULL) avcodec_close(in_video_st->codec); if (num_segments < MAX_SEGMENTS) { //make sure all packets are written and then close the last file. avio_flush(oc->pb); av_write_trailer(oc); for (i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } avio_close(oc->pb); av_free(oc); if (num_segments>0){ actual_segment_durations[num_segments] = (unsigned int) rint(packet_time - segment_start_time); if (actual_segment_durations[num_segments] == 0) actual_segment_durations[num_segments] = 1; num_segments++; write_index_file(output_index_file, tmp_output_index_file, segmentLength, num_segments, actual_segment_durations, listofs, base_file_name, base_file_extension, 1); } } avformat_close_input(&ic); return 0; }
void create_segments(struct segment_context * ctx) { long max_tsfiles = 0; char *max_tsfiles_check; double prev_segment_time = -1; unsigned int output_index = ctx->start_from; AVInputFormat *ifmt; AVOutputFormat *ofmt; AVFormatContext *ic = NULL; AVFormatContext *oc; AVStream *video_st = NULL; AVStream *audio_st = NULL;; AVCodec *codec; char *output_filename; char *output_format; int video_index; int audio_index; unsigned int first_segment = 1; unsigned int last_segment = 0; int decode_done; int ret; int i; int remove_file; int write_index = -1; av_register_all(); ifmt = av_find_input_format("mpegts"); if (!ifmt) { fprintf(stderr, "Could not find MPEG-TS demuxer\n"); exit(1); } ret = avformat_open_input(&ic, ctx->input, ifmt, NULL); if (ret != 0) { fprintf(stderr, "Could not open input file, make sure it is an mpegts file: %d\n", ret); exit(1); } if (avformat_find_stream_info(ic, NULL) < 0) { fprintf(stderr, "Could not read stream information\n"); exit(1); } ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer\n"); exit(1); } write_index = index_file_open(ctx); if(write_index >= 0) index_file_write_headers(ctx); oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "Could not allocated output context"); exit(1); } oc->oformat = ofmt; video_index = -1; audio_index = -1; for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; ic->streams[i]->discard = AVDISCARD_NONE; video_st = add_output_stream(oc, ic->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; ic->streams[i]->discard = AVDISCARD_NONE; audio_st = add_output_stream(oc, ic->streams[i]); break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } av_dump_format(oc, 0, ctx->output_prefix, 1); if(video_st) { codec = avcodec_find_decoder(video_st->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder, key frames will not be honored\n"); } if (avcodec_open2(video_st->codec, codec, NULL) < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored\n"); } } output_filename = malloc(sizeof(char) * (strlen(ctx->output_prefix) + 15)); if (!output_filename) { fprintf(stderr, "Could not allocate space for output filename\n"); exit(1); } output_format = malloc(sizeof(char) * (strlen(ctx->output_prefix) + 15)); if (!output_format) { fprintf(stderr, "Could not allocate space for output format\n"); exit(1); } sprintf(output_format, "%%s%s%%0%uu.ts", ctx->separator, ctx->precision); sprintf(output_filename, output_format, ctx->output_prefix, output_index++); if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); exit(1); } if (avformat_write_header(oc, NULL) < 0) { fprintf(stderr, "Could not write mpegts header to first output file\n"); exit(1); } AVPacket packet; do { double segment_time; decode_done = av_read_frame(ic, &packet); if (decode_done < 0) { break; } if (prev_segment_time < 0) { if (packet.stream_index == video_index) { prev_segment_time = packet.pts * av_q2d(video_st->time_base); } else { prev_segment_time = packet.pts * av_q2d(audio_st->time_base); } } if (av_dup_packet(&packet) < 0) { fprintf(stderr, "Could not duplicate packet"); av_free_packet(&packet); break; } if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY)) { segment_time = packet.pts * av_q2d(video_st->time_base); } else if (video_index < 0) { segment_time = packet.pts * av_q2d(audio_st->time_base); } else { segment_time = prev_segment_time; } if (segment_time - prev_segment_time >= ctx->segment_duration) { avio_flush(oc->pb); avio_close(oc->pb); if(write_index >= 0) index_file_write_segment(ctx, floor(segment_time - prev_segment_time), output_filename); sprintf(output_filename, output_format, ctx->output_prefix, output_index++); if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); break; } prev_segment_time = segment_time; } ret = av_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream\n"); break; } else if (ret > 0) { fprintf(stderr, "End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (1); /* loop is exited on break */ double input_duration = (double)ic->duration / 1000000; if(write_index >= 0) index_file_write_segment(ctx, ceil(input_duration - prev_segment_time), output_filename); av_write_trailer(oc); if(video_st) avcodec_close(video_st->codec); for(i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } avio_close(oc->pb); av_free(oc); if(write_index >= 0) index_file_close(ctx); }
int main(int argc, char **argv) { double prev_segment_time = 0; unsigned int output_index = 1; AVInputFormat *ifmt; AVOutputFormat *ofmt; AVFormatContext *ic = NULL; AVFormatContext *oc; AVStream *video_st = NULL; AVStream *audio_st = NULL; AVCodec *codec; char *output_filename; char *remove_filename; int video_index = -1; int audio_index = -1; unsigned int first_segment = 1; unsigned int last_segment = 0; int write_index = 1; int decode_done; char *dot; int ret; unsigned int i; int remove_file; struct sigaction act; int opt; int longindex; char *endptr; struct options_t options; static const char *optstring = "i:d:p:m:u:n:ovh?"; static const struct option longopts[] = { { "input", required_argument, NULL, 'i' }, { "duration", required_argument, NULL, 'd' }, { "output-prefix", required_argument, NULL, 'p' }, { "m3u8-file", required_argument, NULL, 'm' }, { "url-prefix", required_argument, NULL, 'u' }, { "num-segments", required_argument, NULL, 'n' }, { "help", no_argument, NULL, 'h' }, { 0, 0, 0, 0 } }; memset(&options, 0 ,sizeof(options)); /* Set some defaults */ options.segment_duration = 10; options.num_segments = 0; do { opt = getopt_long(argc, argv, optstring, longopts, &longindex ); switch (opt) { case 'i': options.input_file = optarg; if (!strcmp(options.input_file, "-")) { options.input_file = "pipe:"; } break; case 'd': options.segment_duration = strtol(optarg, &endptr, 10); if (optarg == endptr || options.segment_duration < 0 || options.segment_duration == -LONG_MAX) { fprintf(stderr, "Segment duration time (%s) invalid\n", optarg); exit(1); } break; case 'p': options.output_prefix = optarg; break; case 'm': options.m3u8_file = optarg; break; case 'u': options.url_prefix = optarg; break; case 'n': options.num_segments = strtol(optarg, &endptr, 10); if (optarg == endptr || options.num_segments < 0 || options.num_segments >= LONG_MAX) { fprintf(stderr, "Maximum number of ts files (%s) invalid\n", optarg); exit(1); } break; case 'h': display_usage(); break; } } while (opt != -1); /* Check required args where set*/ if (options.input_file == NULL) { fprintf(stderr, "Please specify an input file.\n"); exit(1); } if (options.output_prefix == NULL) { fprintf(stderr, "Please specify an output prefix.\n"); exit(1); } if (options.m3u8_file == NULL) { fprintf(stderr, "Please specify an m3u8 output file.\n"); exit(1); } if (options.url_prefix == NULL) { fprintf(stderr, "Please specify a url prefix.\n"); exit(1); } av_register_all(); remove_filename = malloc(sizeof(char) * (strlen(options.output_prefix) + 15)); if (!remove_filename) { fprintf(stderr, "Could not allocate space for remove filenames\n"); exit(1); } output_filename = malloc(sizeof(char) * (strlen(options.output_prefix) + 15)); if (!output_filename) { fprintf(stderr, "Could not allocate space for output filenames\n"); exit(1); } options.tmp_m3u8_file = malloc(strlen(options.m3u8_file) + 2); if (!options.tmp_m3u8_file) { fprintf(stderr, "Could not allocate space for temporary index filename\n"); exit(1); } // Use a dotfile as a temporary file strncpy(options.tmp_m3u8_file, options.m3u8_file, strlen(options.m3u8_file) + 2); dot = strrchr(options.tmp_m3u8_file, '/'); dot = dot ? dot + 1 : options.tmp_m3u8_file; memmove(dot + 1, dot, strlen(dot)); *dot = '.'; ifmt = av_find_input_format("mpegts"); if (!ifmt) { fprintf(stderr, "Could not find MPEG-TS demuxer\n"); exit(1); } ret = avformat_open_input(&ic, options.input_file, ifmt, NULL); if (ret != 0) { fprintf(stderr, "Could not open input file, make sure it is an mpegts file: %d\n", ret); exit(1); } if (avformat_find_stream_info(ic, NULL) < 0) { fprintf(stderr, "Could not read stream information\n"); exit(1); } ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer\n"); exit(1); } oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "Could not allocated output context"); exit(1); } oc->oformat = ofmt; for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; ic->streams[i]->discard = AVDISCARD_NONE; video_st = add_output_stream(oc, ic->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; ic->streams[i]->discard = AVDISCARD_NONE; audio_st = add_output_stream(oc, ic->streams[i]); break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } // Don't print warnings when PTS and DTS are identical. ic->flags |= AVFMT_FLAG_IGNDTS; av_dump_format(oc, 0, options.output_prefix, 1); if (video_st) { codec = avcodec_find_decoder(video_st->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder %x, key frames will not be honored\n", video_st->codec->codec_id); } if (avcodec_open2(video_st->codec, codec, NULL) < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored\n"); } } snprintf(output_filename, strlen(options.output_prefix) + 15, "%s-%u.ts", options.output_prefix, output_index++); if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); exit(1); } if (avformat_write_header(oc, NULL)) { fprintf(stderr, "Could not write mpegts header to first output file\n"); exit(1); } write_index = !write_index_file(options, first_segment, last_segment, 0); /* Setup signals */ memset(&act, 0, sizeof(act)); act.sa_handler = &handler; sigaction(SIGINT, &act, NULL); sigaction(SIGTERM, &act, NULL); do { double segment_time = prev_segment_time; AVPacket packet; if (terminate) { break; } decode_done = av_read_frame(ic, &packet); if (decode_done < 0) { break; } if (av_dup_packet(&packet) < 0) { fprintf(stderr, "Could not duplicate packet"); av_free_packet(&packet); break; } // Use video stream as time base and split at keyframes. Otherwise use audio stream if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY)) { segment_time = packet.pts * av_q2d(video_st->time_base); } else if (video_index < 0) { segment_time = packet.pts * av_q2d(audio_st->time_base); } else { segment_time = prev_segment_time; } if (segment_time - prev_segment_time >= options.segment_duration) { av_write_trailer(oc); // close ts file and free memory avio_flush(oc->pb); avio_close(oc->pb); if (options.num_segments && (int)(last_segment - first_segment) >= options.num_segments - 1) { remove_file = 1; first_segment++; } else { remove_file = 0; } if (write_index) { write_index = !write_index_file(options, first_segment, ++last_segment, 0); } if (remove_file) { snprintf(remove_filename, strlen(options.output_prefix) + 15, "%s-%u.ts", options.output_prefix, first_segment - 1); remove(remove_filename); } snprintf(output_filename, strlen(options.output_prefix) + 15, "%s-%u.ts", options.output_prefix, output_index++); if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); break; } // Write a new header at the start of each file if (avformat_write_header(oc, NULL)) { fprintf(stderr, "Could not write mpegts header to first output file\n"); exit(1); } prev_segment_time = segment_time; } ret = av_interleaved_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream\n"); } else if (ret > 0) { fprintf(stderr, "End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done); av_write_trailer(oc); if (video_st) { avcodec_close(video_st->codec); } for(i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } avio_close(oc->pb); av_free(oc); if (options.num_segments && (int)(last_segment - first_segment) >= options.num_segments - 1) { remove_file = 1; first_segment++; } else { remove_file = 0; } if (write_index) { write_index_file(options, first_segment, ++last_segment, 1); } if (remove_file) { snprintf(remove_filename, strlen(options.output_prefix) + 15, "%s-%u.ts", options.output_prefix, first_segment - 1); remove(remove_filename); } return 0; }
int main(int argc, char **argv) { const char *input; const char *output_prefix; double segment_duration; char *segment_duration_check; const char *index; char *tmp_index; const char *http_prefix; long max_tsfiles = 0; char *max_tsfiles_check; double prev_segment_time = 0; unsigned int output_index = 1; AVInputFormat *ifmt; AVOutputFormat *ofmt; AVFormatContext *ic = NULL; AVFormatContext *oc; AVStream *video_st; AVStream *audio_st; AVCodec *codec; char *output_filename; char *remove_filename; int video_index; int audio_index; unsigned int first_segment = 1; unsigned int last_segment = 0; int write_index = 1; int decode_done; char *dot; int ret; int i; int remove_file; if (argc < 6 || argc > 7) { fprintf(stderr, "Usage: %s <input MPEG-TS file> <segment duration in seconds> <output MPEG-TS file prefix> <output m3u8 index file> <http prefix> [<segment window size>]\n", argv[0]); exit(1); } av_register_all(); input = argv[1]; if (!strcmp(input, "-")) { input = "pipe:"; } segment_duration = strtod(argv[2], &segment_duration_check); if (segment_duration_check == argv[2] || segment_duration == HUGE_VAL || segment_duration == -HUGE_VAL) { fprintf(stderr, "Segment duration time (%s) invalid\n", argv[2]); exit(1); } output_prefix = argv[3]; index = argv[4]; http_prefix=argv[5]; if (argc == 7) { max_tsfiles = strtol(argv[6], &max_tsfiles_check, 10); if (max_tsfiles_check == argv[6] || max_tsfiles < 0 || max_tsfiles >= INT_MAX) { fprintf(stderr, "Maximum number of ts files (%s) invalid\n", argv[6]); exit(1); } } remove_filename = malloc(sizeof(char) * (strlen(output_prefix) + 15)); if (!remove_filename) { fprintf(stderr, "Could not allocate space for remove filenames\n"); exit(1); } output_filename = malloc(sizeof(char) * (strlen(output_prefix) + 15)); if (!output_filename) { fprintf(stderr, "Could not allocate space for output filenames\n"); exit(1); } tmp_index = malloc(strlen(index) + 2); if (!tmp_index) { fprintf(stderr, "Could not allocate space for temporary index filename\n"); exit(1); } strncpy(tmp_index, index, strlen(index) + 2); dot = strrchr(tmp_index, '/'); dot = dot ? dot + 1 : tmp_index; for (i = strlen(tmp_index) + 1; i > dot - tmp_index; i--) { tmp_index[i] = tmp_index[i - 1]; } *dot = '.'; ifmt = av_find_input_format("mpegts"); if (!ifmt) { fprintf(stderr, "Could not find MPEG-TS demuxer\n"); exit(1); } ret = av_open_input_file(&ic, input, ifmt, 0, NULL); if (ret != 0) { fprintf(stderr, "Could not open input file, make sure it is an mpegts file: %d\n", ret); exit(1); } if (av_find_stream_info(ic) < 0) { fprintf(stderr, "Could not read stream information\n"); exit(1); } ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer\n"); exit(1); } oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "Could not allocated output context"); exit(1); } oc->oformat = ofmt; video_index = -1; audio_index = -1; for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; ic->streams[i]->discard = AVDISCARD_NONE; video_st = add_output_stream(oc, ic->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; ic->streams[i]->discard = AVDISCARD_NONE; audio_st = add_output_stream(oc, ic->streams[i]); break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } if (av_set_parameters(oc, NULL) < 0) { fprintf(stderr, "Invalid output format parameters\n"); exit(1); } dump_format(oc, 0, output_prefix, 1); codec = avcodec_find_decoder(video_st->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder, key frames will not be honored\n"); } if (avcodec_open(video_st->codec, codec) < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored\n"); } snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, output_index++); if (url_fopen(&oc->pb, output_filename, URL_WRONLY) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); exit(1); } if (av_write_header(oc)) { fprintf(stderr, "Could not write mpegts header to first output file\n"); exit(1); } write_index = !write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, last_segment, 0, max_tsfiles); do { double segment_time; AVPacket packet; decode_done = av_read_frame(ic, &packet); if (decode_done < 0) { break; } if (av_dup_packet(&packet) < 0) { fprintf(stderr, "Could not duplicate packet"); av_free_packet(&packet); break; } if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY)) { segment_time = (double)video_st->pts.val * video_st->time_base.num / video_st->time_base.den; } else if (video_index < 0) { segment_time = (double)audio_st->pts.val * audio_st->time_base.num / audio_st->time_base.den; } else { segment_time = prev_segment_time; } if (segment_time - prev_segment_time >= segment_duration) { put_flush_packet(oc->pb); url_fclose(oc->pb); if (max_tsfiles && (int)(last_segment - first_segment) >= max_tsfiles - 1) { remove_file = 1; first_segment++; } else { remove_file = 0; } if (write_index) { write_index = !write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, ++last_segment, 0, max_tsfiles); } if (remove_file) { snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1); remove(remove_filename); } snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, output_index++); if (url_fopen(&oc->pb, output_filename, URL_WRONLY) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); break; } prev_segment_time = segment_time; } ret = av_interleaved_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream\n"); } else if (ret > 0) { fprintf(stderr, "End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done); av_write_trailer(oc); avcodec_close(video_st->codec); for(i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } url_fclose(oc->pb); av_free(oc); if (max_tsfiles && (int)(last_segment - first_segment) >= max_tsfiles - 1) { remove_file = 1; first_segment++; } else { remove_file = 0; } if (write_index) { write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, ++last_segment, 1, max_tsfiles); } if (remove_file) { snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1); remove(remove_filename); } return 0; }
static VALUE segmenter_segment(VALUE klass, VALUE input_, VALUE output_prefix_, VALUE duration_ ) { const char *input; const char *output_prefix; int segment_duration; long max_tsfiles = 0; double prev_segment_time = 0; unsigned int output_index = 1; AVInputFormat *ifmt; AVOutputFormat *ofmt; AVFormatContext *ic = NULL; AVFormatContext *oc; AVStream *video_st; AVStream *audio_st; AVCodec *codec; char *output_filename; char *remove_filename; int video_index; int audio_index; unsigned int first_segment = 1; unsigned int last_segment = 0; int decode_done; int ret; unsigned int i; int remove_file; bool soundOnly = false; VALUE sArray = rb_ary_new(); av_register_all(); av_log_set_level(AV_LOG_PANIC); input = RSTRING_PTR(input_); output_prefix = RSTRING_PTR(output_prefix_); segment_duration = (FIX2INT(duration_)); char *folder = dirname2(strdup(input)); remove_filename = malloc(sizeof(char) * (strlen(output_prefix) + 15)); if (!remove_filename) { rb_raise(rb_eNoMemError, "Could not allocate space for remove filenames"); } output_filename = malloc(sizeof(char) * (strlen(output_prefix) + strlen(folder) + 15)); if (!output_filename) { rb_raise(rb_eNoMemError, "Could not allocate space for output filenames"); } ifmt = av_find_input_format("mpegts"); if (!ifmt) { rb_raise(rb_eException, "Could not find MPEG-TS demuxer"); } ret = av_open_input_file(&ic, input, ifmt, 0, NULL); if (ret != 0) { rb_raise(rb_eException, "Could not open input file, make sure it is an mpegts file: %d %s", ret, input); } if (av_find_stream_info(ic) < 0) { rb_raise(rb_eException, "Could not read stream information"); } ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { rb_raise(rb_eException, "Could not find MPEG-TS muxer"); } oc = avformat_alloc_context(); if (!oc) { rb_raise(rb_eException, "Could not allocated output context"); } oc->oformat = ofmt; ic->flags |= AVFMT_FLAG_IGNDTS; video_index = -1; audio_index = -1; for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (ic->streams[i]->codec->codec_type) { case CODEC_TYPE_VIDEO: video_index = i; ic->streams[i]->discard = AVDISCARD_NONE; video_st = add_output_stream(oc, ic->streams[i]); break; case CODEC_TYPE_AUDIO: audio_index = i; ic->streams[i]->discard = AVDISCARD_NONE; audio_st = add_output_stream(oc, ic->streams[i]); break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } if(!soundOnly) { soundOnly = (video_st != NULL); } if (av_set_parameters(oc, NULL) < 0) { rb_raise(rb_eException, "Invalid output format parameters"); } dump_format(oc, 0, output_prefix, 1); if (!soundOnly) { if(video_st == NULL) { fprintf(stderr, "video_st is fail\n"); rb_raise(rb_eException, "video_st fail"); exit(1); } if(video_st->codec == NULL) { fprintf(stderr,"codec is fail\n"); rb_raise(rb_eException, "codec fail"); exit(1); } if(video_st->codec->codec_id == NULL) { fprintf(stderr, "codec_id is fail\n"); rb_raise(rb_eException, "codec fail"); exit(1); } codec = avcodec_find_decoder(video_st->codec->codec_id); if (!codec) { rb_raise(rb_eException, "Could not find video decoder, key frames will not be honored"); } if (avcodec_open(video_st->codec, codec) < 0) { rb_raise(rb_eException, "Could not open video decoder, key frames will not be honored"); } if (video_st->codec->ticks_per_frame > 1) { // h264 sets the ticks_per_frame and time_base.den but not time_base.num // since we don't use ticks_per_frame, adjust time_base.num accordingly. video_st->codec->time_base.num *= video_st->codec->ticks_per_frame; } } snprintf(output_filename, strlen(output_prefix) + strlen(folder) + 15, "%s/%s-%u.ts", folder, output_prefix, output_index++); if (url_fopen(&oc->pb, output_filename, URL_WRONLY) < 0) { rb_raise(rb_eException, "Could not open '%s'", output_filename); } if (av_write_header(oc)) { rb_raise(rb_eException, "Could not write mpegts header to first output file"); } //write_index = !write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, last_segment, 0, max_tsfiles); int64_t initial_audio_pts = -1; int64_t initial_video_pts = -1; double segment_time; do { AVPacket packet; //av_init_packet(&packet); decode_done = av_read_frame(ic, &packet); if (decode_done < 0) { break; } if (av_dup_packet(&packet) < 0) { rb_raise(rb_eException, "Could not duplicate packet"); av_free_packet(&packet); break; } if (!soundOnly) { if (packet.stream_index == video_index) { if (initial_video_pts < 0) initial_video_pts = packet.pts; packet.pts -= initial_video_pts; packet.dts = packet.pts; if (packet.flags & AV_PKT_FLAG_KEY) { segment_time = (double)packet.pts * video_st->time_base.num / video_st->time_base.den; } else { segment_time = prev_segment_time; } } else if (packet.stream_index == audio_index) { if (initial_audio_pts < 0) initial_audio_pts = packet.pts; packet.pts -= initial_audio_pts; packet.dts = packet.pts; segment_time = prev_segment_time; } else { segment_time = prev_segment_time; segment_time = prev_segment_time; } } else { if (packet.stream_index == audio_index) { if (initial_audio_pts < 0) initial_audio_pts = packet.pts; if (packet.flags & AV_PKT_FLAG_KEY) { segment_time = (double)packet.pts * audio_st->time_base.num / audio_st->time_base.den; } else { segment_time = prev_segment_time; } packet.pts -= initial_audio_pts; packet.dts = packet.pts; } else { segment_time = prev_segment_time; segment_time = prev_segment_time; } } if (segment_time - prev_segment_time >= segment_duration) { put_flush_packet(oc->pb); url_fclose(oc->pb); if (max_tsfiles && (int)(last_segment - first_segment) >= max_tsfiles - 1) { remove_file = 1; first_segment++; } else { remove_file = 0; } // Create Segment object VALUE seg = rb_obj_alloc(rb_cAvSegment); rb_obj_call_init(seg, 0, 0); rb_iv_set(seg, "@index", INT2FIX(++last_segment)); rb_iv_set(seg, "@duration",INT2FIX((int)floor((segment_time - prev_segment_time)))); rb_iv_set(seg, "@filename", rb_str_new2(output_filename)); rb_ary_push(sArray, seg); if (remove_file) { snprintf(remove_filename, strlen(output_prefix) + strlen(folder) + 15, "%s/%s-%u.ts", folder, output_prefix, first_segment - 1); //snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1); remove(remove_filename); } // snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, output_index++); snprintf(output_filename, strlen(output_prefix) + strlen(folder) + 15, "%s/%s-%u.ts", folder, output_prefix, output_index++); if (url_fopen(&oc->pb, output_filename, URL_WRONLY) < 0) { fprintf(stderr, "Could not open '%s'\n", output_filename); break; } prev_segment_time = segment_time; } ret = av_interleaved_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "Warning: Could not write frame of stream\n"); } else if (ret > 0) { fprintf(stderr, "End of stream requested\n"); av_free_packet(&packet); break; } av_free_packet(&packet); } while (!decode_done); av_write_trailer(oc); if (!soundOnly) avcodec_close(video_st->codec); for(i = 0; i < oc->nb_streams; i++) { if(&oc->streams[i]->codec != NULL) av_freep(&oc->streams[i]->codec); if (&oc->streams[i] != NULL) av_freep(&oc->streams[i]); } url_fclose(oc->pb); av_free(oc); if (max_tsfiles && (int)(last_segment - first_segment) >= max_tsfiles - 1) { remove_file = 1; first_segment++; } else { remove_file = 0; } if (remove_file) { snprintf(remove_filename, strlen(output_prefix) + strlen(folder) + 15, "%s/%s-%u.ts", folder, output_prefix, first_segment - 1); remove(remove_filename); } return sArray; }
void open_context(AVFormatContext **ic, const char *input_file, const char *key, AVInputFormat *ifmt, AVOutputFormat **ofmt, AVFormatContext **oc, AVStream **video_st, AVStream **audio_st, int *video_index, int *audio_index){ int ret; int i; AVCodec *codec; ret = avformat_open_input(ic, input_file, ifmt, NULL); if (ret != 0) { fprintf(stderr, "Could not open input file, make sure it is an mpegts file: %d\n", ret); exit(1); } if (avformat_find_stream_info(*ic, NULL) < 0) { fprintf(stderr, "Could not read stream information\n"); exit(1); } *ofmt = av_guess_format("mpegts", NULL, NULL); if (!ofmt) { fprintf(stderr, "Could not find MPEG-TS muxer\n"); exit(1); } *oc = avformat_alloc_context(); if (!*oc) { fprintf(stderr, "Could not allocated output context"); exit(1); } (*oc)->oformat = *ofmt; for (i = 0; i < (*ic)->nb_streams && (*video_index < 0 || *audio_index < 0); i++) { switch ((*ic)->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: *video_index = i; (*ic)->streams[i]->discard = AVDISCARD_NONE; *video_st = add_output_stream(*oc, (*ic)->streams[i]); break; case AVMEDIA_TYPE_AUDIO: *audio_index = i; (*ic)->streams[i]->discard = AVDISCARD_NONE; *audio_st = add_output_stream(*oc, (*ic)->streams[i]); break; default: (*ic)->streams[i]->discard = AVDISCARD_ALL; break; } } // Don't print warnings when PTS and DTS are identical. (*ic)->flags |= AVFMT_FLAG_IGNDTS; av_dump_format(*oc, 0, key, 1); if (*video_st) { codec = avcodec_find_decoder((*video_st)->codec->codec_id); if (!codec) { fprintf(stderr, "Could not find video decoder %x, key frames will not be honored\n", (*video_st)->codec->codec_id); } if (avcodec_open2((*video_st)->codec, codec, NULL) < 0) { fprintf(stderr, "Could not open video decoder, key frames will not be honored\n"); } } }
int segment_process(struct config_info config) { unsigned int output_filename_size = sizeof(char) * (strlen(config.temp_directory) + 1 + strlen(config.filename_prefix) + 100); char *output_filename = malloc(output_filename_size); if (!output_filename) { fprintf(stderr, "Segmenter error: Could not allocate space for output filenames\n"); exit(1); } // we try to approximate the time of first received frame // by taking system timestamp and storing first frame timestamp time_t sys_time; time(&sys_time); double on_start_timestamp = (double)sys_time; av_register_all(); AVInputFormat *input_format = av_find_input_format("mpegts"); if (!input_format) { fprintf(stderr, "Segmenter error: Could not find MPEG-TS demuxer\n"); exit(1); } AVFormatContext *input_context = NULL; int ret; ret = avformat_open_input(&input_context, config.input_filename, input_format, NULL); if (ret != 0) { print_av_error("Could not open input file, make sure it is an mpegts", ret); exit(1); } ret = avformat_find_stream_info(input_context, NULL); if (ret < 0) { print_av_error("Could not read stream information", ret); exit(1); } fprintf(stderr, "segmenter-debug: start time %lld %lld \n", input_context->start_time, input_context->timestamp); AVOutputFormat *output_format = av_guess_format("mpegts", NULL, NULL); if (!output_format) { fprintf(stderr, "Segmenter error: Could not find MPEG-TS muxer\n"); exit(1); } AVFormatContext *output_context = avformat_alloc_context(); if (!output_context) { fprintf(stderr, "Segmenter error: Could not allocated output context"); exit(1); } output_context->oformat = output_format; int video_index = -1; int audio_index = -1; AVStream *video_stream; AVStream *audio_stream; int i; int index_map[20]; memset (index_map, -1, sizeof(int)*20); for (i = 0; i < input_context->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (input_context->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; input_context->streams[i]->discard = AVDISCARD_NONE; video_stream = add_output_stream(output_context, input_context->streams[i]); index_map[i] = video_stream->index; break; case AVMEDIA_TYPE_AUDIO: audio_index = i; input_context->streams[i]->discard = AVDISCARD_NONE; audio_stream = add_output_stream(output_context, input_context->streams[i]); index_map[i] = audio_stream->index; break; default: input_context->streams[i]->discard = AVDISCARD_ALL; fprintf(stderr, "segmenter-warning: stream index %d from source woudl be skipped \n", i); break; } } #if LIBAVFORMAT_VERSION_MAJOR < 54 if (av_set_parameters(output_context, NULL) < 0) { fprintf(stderr, "Segmenter error: Invalid output format parameters\n"); exit(1); } #endif av_dump_format(output_context, 0, config.filename_prefix, 1); if(video_index >= 0) { AVCodec *codec = avcodec_find_decoder(video_stream->codec->codec_id); if (!codec) { fprintf(stderr, "Segmenter error: Could not find video decoder, key frames will not be honored\n"); } if (avcodec_open2(video_stream->codec, codec, NULL) < 0) { fprintf(stderr, "Segmenter error: Could not open video decoder, key frames will not be honored\n"); } } unsigned int output_index = 1; snprintf(output_filename, output_filename_size, "%s/%s-%u-%010u.ts", config.temp_directory, config.filename_prefix, config.run_cycle, output_index++); if (avio_open(&output_context->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename); exit(1); } if (avformat_write_header(output_context, NULL)) { fprintf(stderr, "Segmenter error: Could not write mpegts header to first output file\n"); exit(1); } unsigned int first_segment = 1; unsigned int last_segment = 0; double prev_segment_time = 0; double first_segment_time = -1.0; int decode_done; double segment_time = 0; unsigned int pktWriteFrameErrorCount = 0; do { AVPacket packet; decode_done = av_read_frame(input_context, &packet); if (decode_done < 0) { break; } if ((ret = av_dup_packet(&packet)) < 0) { print_av_error("Could not duplicate packet", ret); av_free_packet(&packet); break; } if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY)) { segment_time = (double)video_stream->pts.val * video_stream->time_base.num / video_stream->time_base.den; if (first_segment_time <= 0) fprintf(stderr, "segmenter-debug: received new time from video I frame : %lf\n", segment_time); } else if (video_index < 0) { segment_time = (double)audio_stream->pts.val * audio_stream->time_base.num / audio_stream->time_base.den; if (first_segment_time <= 0) fprintf(stderr, "segmenter-debug: received new time from audio PTS : %lf\n", segment_time); } else { segment_time = prev_segment_time; } // initialy previous segment time would be zero, avoid receiving huge difference if (prev_segment_time == 0 && segment_time > 0) { prev_segment_time = segment_time; } if (first_segment_time < 0 && segment_time > 0) { first_segment_time = segment_time; // this is relative time in MS } //fprintf(stderr, "segmenter-debug: videoPTS=%lf audioPTS=%lf\n", (double)video_stream->pts.val * video_stream->time_base.num / video_stream->time_base.den, (double)audio_stream->pts.val * audio_stream->time_base.num / audio_stream->time_base.den); //fprintf(stderr, "segmenter-debug: cur=%.02lf prev=%.02lf onstartTS=%.02lf first=%.02lf\n", segment_time, prev_segment_time, on_start_timestamp, first_segment_time); // timestamps in the streams received from IRDs are relative, // if there's a mismatch - time to restart segmentation process, as entire TS may change // done writing the current file? if (segment_time - prev_segment_time >= config.segment_length) { avio_flush(output_context->pb); avio_close(output_context->pb); output_transfer_command(++last_segment, (segment_time-first_segment_time+on_start_timestamp), (segment_time-prev_segment_time), 0, output_filename); snprintf(output_filename, output_filename_size, "%s/%s-%u-%010u.ts", config.temp_directory, config.filename_prefix, config.run_cycle,output_index++); if (avio_open(&output_context->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "Segmenter error: Could not open '%s'\n", output_filename); break; } prev_segment_time = segment_time; } // because indicies from original and destination packets differ, perform correction if (index_map[packet.stream_index] < 0) { fprintf(stderr, "segmenter-debug: skipping packet pos=%lld size=%d stream=%d flags=%d pts=%lld\n", packet.pos, packet.size, packet.stream_index, packet.flags, packet.pts); ret = -1000; }else if (packet.size > 0) { packet.stream_index = index_map[packet.stream_index]; ret = av_interleaved_write_frame(output_context, &packet); // fprintf(stderr, "segmenter-error: [%d] handling packet pos=%lld size=%d stream=%d flags=%d pts=%lld\n", ret, packet.pos, packet.size, packet.stream_index, packet.flags, packet.pts); }else{ fprintf(stderr, "segmenter-debug: empty packet\n"); ret = -1001; } av_free_packet(&packet); if (ret < 0) { // fprintf(stderr, "segmenter-error: handling packet pos=%lld size=%d stream=%d flags=%d pts=%lld\n", packet.pos, packet.size, packet.stream_index, packet.flags, packet.pts); // print_av_error("av_interleaved_write_frame ",ret); pktWriteFrameErrorCount ++; if (first_segment_time > 0 && pktWriteFrameErrorCount > PKT_WRITE_FRAME_ERROR_LIMIT) { fprintf(stderr, "segmenter-error: can't handle last %u packets. requesting process restart.\n", pktWriteFrameErrorCount); break; } } else if (ret > 0) { fprintf(stderr, "Segmenter info: End of stream requested\n"); av_free_packet(&packet); break; }else{ pktWriteFrameErrorCount = 0; // clear av_interleaved_write_frame() error count } } while (!decode_done); av_write_trailer(output_context); if (video_index >= 0) { avcodec_close(video_stream->codec); } for(i = 0; i < output_context->nb_streams; i++) { av_freep(&output_context->streams[i]->codec); av_freep(&output_context->streams[i]); } avio_close(output_context->pb); av_free(output_context); av_close_input_file(input_context); output_transfer_command(++last_segment, (segment_time-first_segment_time+on_start_timestamp), (segment_time-prev_segment_time), 1, output_filename); return 0; }
//---------------------------------------------------------------- // main_utf8 // int main_utf8(int argc, char **argv) { const char *input = NULL; const char *output_prefix = ""; double target_segment_duration = 0.0; char *segment_duration_check = NULL; const char *playlist_filename = NULL; const char *http_prefix = ""; long max_tsfiles = 0; char *max_tsfiles_check = NULL; double prev_segment_time = 0.0; double segment_duration = 0.0; unsigned int output_index = 1; const AVClass *fc = avformat_get_class(); AVDictionary *format_opts = NULL; AVOutputFormat *ofmt = NULL; AVFormatContext *ic = NULL; AVFormatContext *oc = NULL; AVStream *video_st = NULL; AVStream *audio_st = NULL; AVCodec *codec = NULL; char *output_filename = NULL; int if_save_keyframe = 0; //add by wanggm char *keyframeinfo_filename = NULL; //add by wanggm json_object *obj = NULL; //add by wanggm json_object *info_arr_obj = NULL; //add by wanggm int if_monitor_related_process = 0; //add by wanggm pid_t relatedProcessPid = 1; //add by wanggm char *pid_filename = NULL; int video_index = -1; int audio_index = -1; int kill_file = 0; int decode_done = 0; int ret = 0; int i = 0; TSMStreamLace * streamLace = NULL; TSMPlaylist * playlist = NULL; const double segment_duration_error_tolerance = 0.05; double extra_duration_needed = 0; int strict_segment_duration = 0; av_log_set_level(AV_LOG_INFO); for (i = 1; i < argc; i++) { if (strcmp(argv[i], "-i") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -i parameter"); i++; input = argv[i]; } else if (strcmp(argv[i], "-o") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -o parameter"); i++; output_prefix = argv[i]; } else if (strcmp(argv[i], "-d") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -d parameter"); i++; target_segment_duration = strtod(argv[i], &segment_duration_check); if (segment_duration_check == argv[i] || target_segment_duration == HUGE_VAL || target_segment_duration == -HUGE_VAL){ usage3(argv, "invalid segment duration: ", argv[i]); } } else if (strcmp(argv[i], "-x") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -x parameter"); i++; playlist_filename = argv[i]; } else if (strcmp(argv[i], "-p") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -p parameter"); i++; http_prefix = argv[i]; } else if (strcmp(argv[i], "-w") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -w parameter"); i++; max_tsfiles = strtol(argv[i], &max_tsfiles_check, 10); if (max_tsfiles_check == argv[i] || max_tsfiles < 0 || max_tsfiles >= INT_MAX) { usage3(argv, "invalid live stream max window size: ", argv[i]); } } else if (strcmp(argv[i], "-P") == 0) { if ((argc - i) <= 1) usage(argv, "could not parse -P parameter"); i++; pid_filename = argv[i]; } else if (strcmp(argv[i], "--watch-for-kill-file") == 0) { // end program when it finds a file with name 'kill': kill_file = 1; } else if (strcmp(argv[i], "--strict-segment-duration") == 0) { // force segment creation on non-keyframe boundaries: strict_segment_duration = 1; } else if (strcmp(argv[i], "--avformat-option") == 0) { const AVOption *of; const char *opt; const char *arg; if ((argc - i) <= 1) usage(argv, "could not parse --avformat-option parameter"); i++; opt = argv[i]; if ((argc - i) <= 1) usage(argv, "could not parse --avformat-option parameter"); i++; arg = argv[i]; if ((of = av_opt_find(&fc, opt, NULL, 0, AV_OPT_SEARCH_CHILDREN | AV_OPT_SEARCH_FAKE_OBJ))) av_dict_set(&format_opts, opt, arg, (of->type == AV_OPT_TYPE_FLAGS) ? AV_DICT_APPEND : 0); else usage3(argv, "unknown --avformat-option parameter: ", opt); } else if (strcmp(argv[i], "--loglevel") == 0) { const char *arg; if ((argc - i) <= 1) usage(argv, "could not parse --loglevel parameter"); i++; arg = argv[i]; if (loglevel(arg)) usage3(argv, "unknown --loglevel parameter: ", arg); } else if (strcmp(argv[i], "-k") == 0) { //add by wanggm for save key frame information into json file. if ((argc - i) <= 1) usage(argv, "could not parse -k parameter"); i++; if_save_keyframe = atoi(argv[i]); } else if( strcmp(argv[i], "-s") == 0) {//add by wanggm for set the start index of ts file. if ( (argc -i ) <= 1) usage(argv, "could not parse -s parmeter"); i++; char *output_index_check = NULL; output_index = strtol(argv[i], &output_index_check, 10); if ( output_index_check== argv[i] || output_index < 0 || output_index >= INT_MAX) { usage3(argv, "invalid start index of ts file: ", argv[i]); } } else if( strcmp(argv[i], "-m") == 0) { // add by wanggm for exit by monitor the process of which pid is given. if ((argc - i) <= 1) usage(argv, "could not parse -m parmeter"); i++; if_monitor_related_process = 1; unsigned int tmpPid= atoi(argv[i]); if( tmpPid > 0) { relatedProcessPid = (pid_t) tmpPid; fprintf(stdout, "%s I will exit when the process PID= %d exit.\n", getSystemTime(timeChar), relatedProcessPid); } } } if (!input) { usage(argv, "-i input file parameter must be specified"); } if (!playlist_filename) { usage(argv, "-x m3u8 playlist file parameter must be specified"); } if (target_segment_duration == 0.0) { usage(argv, "-d segment duration parameter must be specified"); } if( output_index <= 0 ) { output_index = 1; } if( 1 == if_monitor_related_process) { pthread_t id; pthread_create(&id, NULL, (void*)monitor_process, relatedProcessPid); } // Create PID file if (pid_filename) { FILE* pid_file = fopen_utf8(pid_filename, "wb"); if (pid_file) { fprintf(pid_file, "%d", getpid()); fclose(pid_file); } } av_register_all(); avformat_network_init(); if (!strcmp(input, "-")) { input = "pipe:"; } output_filename = (char*) malloc( sizeof(char) * (strlen(output_prefix) + 15)); //add by wanggm if( if_save_keyframe == 1) { keyframeinfo_filename = (char*) malloc( sizeof(char)* (strlen(output_prefix) + 15)); } if (!output_filename || (1 == if_save_keyframe && !keyframeinfo_filename)) { fprintf(stderr, "%s Could not allocate space for output filenames\n", getSystemTime( timeChar)); goto error; } playlist = createPlaylist(max_tsfiles, target_segment_duration, http_prefix); if (!playlist) { fprintf(stderr, "%s Could not allocate space for m3u8 playlist structure\n", getSystemTime( timeChar)); goto error; } ret = avformat_open_input(&ic, input, NULL, (format_opts) ? &format_opts : NULL); if (ret != 0) { fprintf(stderr, "%sCould not open input file, make sure it is an mpegts or mp4 file: %d\n", getSystemTime(timeChar), ret); goto error; } av_dict_free(&format_opts); if (avformat_find_stream_info(ic, NULL) < 0) { fprintf(stderr, "%s Could not read stream information\n", getSystemTime( timeChar)); goto error; } #if LIBAVFORMAT_VERSION_MAJOR > 52 || (LIBAVFORMAT_VERSION_MAJOR == 52 && \ LIBAVFORMAT_VERSION_MINOR >= 45) ofmt = av_guess_format("mpegts", NULL, NULL); #else ofmt = guess_format("mpegts", NULL, NULL); #endif if (!ofmt) { fprintf(stderr, "%s Could not find MPEG-TS muxer\n", getSystemTime( timeChar)); goto error; } oc = avformat_alloc_context(); if (!oc) { fprintf(stderr, "%s Could not allocated output context\n", getSystemTime( timeChar)); goto error; } oc->oformat = ofmt; video_index = -1; audio_index = -1; for (i = 0; i < ic->nb_streams && (video_index < 0 || audio_index < 0); i++) { switch (ic->streams[i]->codec->codec_type) { case AVMEDIA_TYPE_VIDEO: video_index = i; ic->streams[i]->discard = AVDISCARD_NONE; video_st = add_output_stream(oc, ic->streams[i]); break; case AVMEDIA_TYPE_AUDIO: audio_index = i; ic->streams[i]->discard = AVDISCARD_NONE; audio_st = add_output_stream(oc, ic->streams[i]); break; default: ic->streams[i]->discard = AVDISCARD_ALL; break; } } av_dump_format(oc, 0, output_prefix, 1); if (video_index >= 0) { codec = avcodec_find_decoder(video_st->codec->codec_id); if (!codec) { fprintf(stderr, "%s Could not find video decoder, key frames will not be honored\n", getSystemTime( timeChar)); } if (avcodec_open2(video_st->codec, codec, NULL) < 0) { fprintf(stderr, "%s Could not open video decoder, key frames will not be honored\n", getSystemTime( timeChar)); } } snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, output_index); if( 1 == if_save_keyframe) { snprintf(keyframeinfo_filename, strlen(output_prefix) + 15, "%s-%u.idx", output_prefix, output_index); obj = json_object_new_object(); info_arr_obj = create_json_header(obj); } if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "%s Could not open '%s'\n", getSystemTime( timeChar),output_filename); goto error; } if (avformat_write_header(oc, NULL)) { fprintf(stderr, "%s Could not write mpegts header to first output file\n", getSystemTime( timeChar)); goto error; } prev_segment_time = (double) (ic->start_time) / (double) (AV_TIME_BASE); streamLace = createStreamLace(ic->nb_streams); // add by houmr int continue_error_cnt = 0; //int pushcnt = 0; //int popcnt = 0; int tscnt = 0; int audiopktcnt = 0; int videopktcnt = 0; int kfcnt = 0; int errpktcnt = 0; ///////////////////////// do { double segment_time = 0.0; AVPacket packet; double packetStartTime = 0.0; double packetDuration = 0.0; if (!decode_done) { //fprintf(stdout, "%s av_read_frame() begin.\n", getSystemTime( timeChar)); decode_done = av_read_frame(ic, &packet); //fprintf(stdout, "%s av_read_frame() end. packet.size=%d stream_index=%d duration=%d\n", getSystemTime( timeChar), packet.size, packet.stream_index, packet.duration); //fprintf(stdout, "%s decode_done=%d\n", getSystemTime( timeChar),decode_done); if (!decode_done) { if (packet.stream_index != video_index && packet.stream_index != audio_index) { if( ++errpktcnt >= 10) { decode_done = 1; } fprintf(stderr, "%s packet is not video or audio, packet.stream_index=%d\n", getSystemTime( timeChar), packet.stream_index); av_free_packet(&packet); continue; } errpktcnt = 0; /*printf("orgin : index - %d\t pts = %s\t duration=%d\n", packet.stream_index, av_ts2str(packet.pts), packet.duration);*/ // add by houmr /*if (adjust_pts(&packet, video_index, audio_index) < 0) { av_free_packet(&packet); continue; } */ ///////////////////////////////////// double timeStamp = (double) (packet.pts) * (double) (ic->streams[packet.stream_index]->time_base.num) / (double) (ic->streams[packet.stream_index]->time_base.den); if (av_dup_packet(&packet) < 0) { fprintf(stderr, "%s Could not duplicate packet\n" ,getSystemTime( timeChar)); av_free_packet(&packet); break; } /* for(int i = 0; i < streamLace->numStreams; ++i) { fprintf(stdout, "streamLace[%d].size=%d\t", i, streamLace->streams[i]->size); } fprintf(stdout, "\n"); */ insertPacket(streamLace, &packet, timeStamp); } } if (countPackets(streamLace) < 50 && !decode_done) { /* allow the queue to fill up so that the packets can be sorted properly */ continue; } if (!removePacket(streamLace, &packet)) { fprintf(stdout, "%s get packet failed!!\n", getSystemTime( timeChar)); if (decode_done) { /* the queue is empty, we are done */ break; } assert(decode_done); continue; } //fprintf(stdout, "%s get 1 packet success. packet info: pts=%ld, dts=%ld\n", getSystemTime( timeChar), packet.pts, packet.dts); packetStartTime = (double) (packet.pts) * (double) (ic->streams[packet.stream_index]->time_base.num) / (double) (ic->streams[packet.stream_index]->time_base.den); packetDuration = (double) (packet.duration) * (double) (ic->streams[packet.stream_index]->time_base.num) / (double) (ic->streams[packet.stream_index]->time_base.den); #if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG)) if (av_log_get_level() >= AV_LOG_VERBOSE) fprintf(stderr, "%s stream %i, packet [%f, %f)\n", getSystemTime( timeChar), packet.stream_index, packetStartTime, packetStartTime + packetDuration); #endif segment_duration = packetStartTime + packetDuration - prev_segment_time; // NOTE: segments are supposed to start on a keyframe. // If the keyframe interval and segment duration do not match // forcing the segment creation for "better seeking behavior" // will result in decoding artifacts after seeking or stream switching. if (packet.stream_index == video_index && (packet.flags & AV_PKT_FLAG_KEY || strict_segment_duration)) { //This is video packet and ( packet is key frame or strict time is needed ) segment_time = packetStartTime; } else if (video_index < 0) { //This stream doesn't contain video stream segment_time = packetStartTime; } else { //This is a video packet or a video packet but not key frame segment_time = prev_segment_time; } //fprintf(stdout, "%s extra_duration_needed=%f\n", getSystemTime( timeChar), extra_duration_needed); if (segment_time - prev_segment_time + segment_duration_error_tolerance > target_segment_duration + extra_duration_needed) { fprintf(stdout, "%s segment_time=%lf prev_segment_time=%lf > target_segment_duration=%lf extra_duration_needed=%lf\n", getSystemTime( timeChar), segment_time, prev_segment_time, target_segment_duration, extra_duration_needed); fprintf(stdout, "%s File %s contains %d PES packet, of which %d are audio packet, %d are video packet within %d key frame.\n", getSystemTime( timeChar), output_filename, tscnt, audiopktcnt, videopktcnt, kfcnt); fflush(stdout); /* for(int i = 0; i < streamLace->numStreams; ++i) { fprintf(stdout, "%s streamLace[%d].size=%d\t", getSystemTime( timeChar), i, streamLace->streams[i]->size); } */ tscnt = audiopktcnt = videopktcnt = kfcnt = 0; avio_flush(oc->pb); avio_close(oc->pb); // Keep track of accumulated rounding error to account for it in later chunks. /* double segment_duration = segment_time - prev_segment_time; int rounded_segment_duration = (int) (segment_duration + 0.5); extra_duration_needed += (double) rounded_segment_duration - segment_duration; */ double seg_dur = segment_time - prev_segment_time; int rounded_segment_duration = (int) (seg_dur + 0.5); extra_duration_needed += (target_segment_duration - seg_dur - segment_duration_error_tolerance); //fprintf(stdout, "%s ________extra_duration_needed = %lf\n", getSystemTime( timeChar), extra_duration_needed); updatePlaylist(playlist, playlist_filename, output_filename, output_index, rounded_segment_duration); snprintf(output_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, ++output_index); //add by wanggm //Save the all the keyframe information into json file if( 1 == if_save_keyframe && NULL != obj) { save_json_to_file(keyframeinfo_filename, obj); obj = info_arr_obj = NULL; snprintf(keyframeinfo_filename, strlen(output_prefix) + 15, "%s-%u.idx", output_prefix, output_index); } if (avio_open(&oc->pb, output_filename, AVIO_FLAG_WRITE) < 0) { fprintf(stderr, "%s Could not open '%s'\n", getSystemTime( timeChar), output_filename); break; } // close when we find the 'kill' file if (kill_file) { FILE* fp = fopen("kill", "rb"); if (fp) { fprintf(stderr, "%s user abort: found kill file\n", getSystemTime( timeChar)); fclose(fp); remove("kill"); decode_done = 1; removeAllPackets(streamLace); } } prev_segment_time = segment_time; } //add by wanggm. ++tscnt; if( video_index == packet.stream_index) { ++videopktcnt; if(1 == packet.flags) { ++kfcnt; if( 1 == if_save_keyframe) { //If it is key frame, it's information should be saved. //fprintf(stdout, "%s packet is keyframe, packet.pts=%ld\n", getSystemTime( timeChar), packet.pts); snprintf(keyframeinfo_filename, strlen(output_prefix) + 15, "%s-%u.idx", output_prefix, output_index); if (NULL == obj && NULL == info_arr_obj) { obj = json_object_new_object(); info_arr_obj = create_json_header(obj); } avio_flush(oc->pb); //flush the previous data into ts file. int64_t offset = avio_tell(oc->pb); //Get the offset of this key frame in the file. save_keyframe_info(info_arr_obj, offset, packet.pts); //fprintf(stdout, "%s Keyframe.pos=%ld \tkeyframe.pts=%ld\n", getSystemTime( timeChar), offset, (long)packet.pts); } } }else if( audio_index == packet.stream_index) { ++audiopktcnt; } //fprintf(stdout, "%s packet is not keyframe.\n", getSystemTime( timeChar)); ret = av_interleaved_write_frame(oc, &packet); if (ret < 0) { fprintf(stderr, "%s Warning: Could not write frame of stream\n", getSystemTime( timeChar)); // add by houmr continue_error_cnt++; if (continue_error_cnt > 10) { av_free_packet(&packet); break; } } else if (ret > 0) { fprintf(stderr, "%s End of stream requested\n", getSystemTime( timeChar)); av_free_packet(&packet); break; } else { // add by houmr error continue_error_cnt = 0; //////////////////////// } av_free_packet(&packet); } while (!decode_done || countPackets(streamLace) > 0); av_write_trailer(oc); if (video_index >= 0) { avcodec_close(video_st->codec); } for (i = 0; i < oc->nb_streams; i++) { av_freep(&oc->streams[i]->codec); av_freep(&oc->streams[i]); } avio_close(oc->pb); av_free(oc); updatePlaylist(playlist, playlist_filename, output_filename, output_index, segment_duration); closePlaylist(playlist); releasePlaylist(&playlist); //add by wanggm if( 1 == if_save_keyframe && obj != NULL) { save_json_to_file(keyframeinfo_filename, obj); } if (pid_filename) { remove(pid_filename); } fflush(stdout); fflush(stderr); return 0; error: if (pid_filename) { remove(pid_filename); } return 1; }