/*
* Copyright (c) 2009 Chase Douglas
* Copyright (c) 2011 John Ferlito
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License version 2
* as published by the Free Software Foundation.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301, USA.
*/
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
#include <sys/types.h>
#include <signal.h>
#include <getopt.h>
#include <libavformat/avformat.h>
#include "libav-compat.h"
struct options_t {
const char *input_file;
long segment_duration;
const char *output_prefix;
const char *m3u8_file;
char *tmp_m3u8_file;
const char *url_prefix;
long num_segments;
};
void handler(int signum);
static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream);
int write_index_file(const struct options_t, const unsigned int first_segment, const unsigned int last_segment, const int end);
void display_usage(void);
int terminate = 0;
void handler(int signum) {
(void)signum;
terminate = 1;
}
static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStream *input_stream) {
AVCodecContext *input_codec_context;
AVCodecContext *output_codec_context;
AVStream *output_stream;
output_stream = avformat_new_stream(output_format_context, 0);
if (!output_stream) {
fprintf(stderr, "Could not allocate stream\n");
exit(1);
}
input_codec_context = input_stream->codec;
output_codec_context = output_stream->codec;
output_codec_context->codec_id = input_codec_context->codec_id;
output_codec_context->codec_type = input_codec_context->codec_type;
output_codec_context->codec_tag = input_codec_context->codec_tag;
output_codec_context->bit_rate = input_codec_context->bit_rate;
output_codec_context->extradata = input_codec_context->extradata;
output_codec_context->extradata_size = input_codec_context->extradata_size;
if(av_q2d(input_codec_context->time_base) * input_codec_context->ticks_per_frame > av_q2d(input_stream->time_base) && av_q2d(input_stream->time_base) < 1.0/1000) {
output_codec_context->time_base = input_codec_context->time_base;
output_codec_context->time_base.num *= input_codec_context->ticks_per_frame;
}
else {
output_codec_context->time_base = input_stream->time_base;
}
switch (input_codec_context->codec_type) {
case AVMEDIA_TYPE_AUDIO:
output_codec_context->channel_layout = input_codec_context->channel_layout;
output_codec_context->sample_rate = input_codec_context->sample_rate;
output_codec_context->channels = input_codec_context->channels;
output_codec_context->frame_size = input_codec_context->frame_size;
if ((input_codec_context->block_align == 1 && input_codec_context->codec_id == CODEC_ID_MP3) || input_codec_context->codec_id == CODEC_ID_AC3) {
output_codec_context->block_align = 0;
}
else {
output_codec_context->block_align = input_codec_context->block_align;
}
break;
case AVMEDIA_TYPE_VIDEO:
output_codec_context->pix_fmt = input_codec_context->pix_fmt;
output_codec_context->width = input_codec_context->width;
output_codec_context->height = input_codec_context->height;
output_codec_context->has_b_frames = input_codec_context->has_b_frames;
if (output_format_context->oformat->flags & AVFMT_GLOBALHEADER) {
output_codec_context->flags |= CODEC_FLAG_GLOBAL_HEADER;
}
break;
default:
break;
}
return output_stream;
}
int write_index_file(const struct options_t options, const unsigned int first_segment, const unsigned int last_segment, const int end) {
FILE *index_fp;
char *write_buf;
unsigned int i;
index_fp = fopen(options.tmp_m3u8_file, "w");
if (!index_fp) {
fprintf(stderr, "Could not open temporary m3u8 index file (%s), no index file will be created\n", options.tmp_m3u8_file);
return -1;
}
write_buf = malloc(sizeof(char) * 1024);
if (!write_buf) {
fprintf(stderr, "Could not allocate write buffer for index file, index file will be invalid\n");
fclose(index_fp);
return -1;
}
if (options.num_segments) {
snprintf(write_buf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%lu\n#EXT-X-MEDIA-SEQUENCE:%u\n", options.segment_duration, first_segment);
}
else {
snprintf(write_buf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%lu\n", options.segment_duration);
}
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
for (i = first_segment; i <= last_segment; i++) {
snprintf(write_buf, 1024, "#EXTINF:%lu,\n%s%s-%u.ts\n", options.segment_duration, options.url_prefix, options.output_prefix, i);
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
}
if (end) {
snprintf(write_buf, 1024, "#EXT-X-ENDLIST\n");
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
fprintf(stderr, "Could not write last file and endlist tag to m3u8 index file\n");
free(write_buf);
fclose(index_fp);
return -1;
}
}
free(write_buf);
fclose(index_fp);
return rename(options.tmp_m3u8_file, options.m3u8_file);
}
void display_usage(void)
{
printf("Usage: m3u8-sementer [OPTION]...\n");
printf("\n");
printf("HTTP Live Streaming - Segments TS file and creates M3U8 index.");
printf("\n");
printf("\t-i, --input FILE TS file to segment (Use - for stdin)\n");
printf("\t-d, --duration SECONDS Duration of each segment (default: 10 seconds)\n");
printf("\t-p, --output-prefix PREFIX Prefix for the TS segments, will be appended\n");
printf("\t with -1.ts, -2.ts etc\n");
printf("\t-m, --m3u8-file FILE M3U8 output filename\n");
printf("\t-u, --url-prefix PREFIX Prefix for web address of segments, e.g. http://example.org/video/\n");
printf("\t-n, --num-segment NUMBER Number of segments to keep on disk\n");
printf("\t-h, --help This help\n");
printf("\n");
printf("\n");
exit(0);
}
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;