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; int ret; int listlen; int persist=0; FNHOLDER(tempPlaylistName); 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); } ret = segment(inputFilename, baseDirName, playlistFilename, baseFileName, baseFileExtension, segmentLength, listlen); return ret; }
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; }