pkoshevoy [at] sorensonmedia.com:
fixes writing of #EXTINF tags with correct segment duration for each segment as per apple spec: http://tools.ietf.org/html/draft-pantos-http-live-streaming-01 git-svn-id: https://httpsegmenter.googlecode.com/svn/trunk@9 50c576d4-0ac1-1411-950c-f994e1bbdc23
This commit is contained in:
189
segmenter.c
189
segmenter.c
@@ -1,5 +1,5 @@
|
|||||||
/* $Id$
|
/* $Id$
|
||||||
* $HeadURL
|
* $HeadURL$
|
||||||
*
|
*
|
||||||
* Copyright (c) 2009 Chase Douglas
|
* Copyright (c) 2009 Chase Douglas
|
||||||
*
|
*
|
||||||
@@ -84,75 +84,111 @@ static AVStream *add_output_stream(AVFormatContext *output_format_context, AVStr
|
|||||||
return output_stream;
|
return output_stream;
|
||||||
}
|
}
|
||||||
|
|
||||||
int write_index_file(const char index[], const char tmp_index[], const unsigned int segment_duration, const char output_prefix[], const char http_prefix[], const unsigned int first_segment, const unsigned int last_segment, const int end, const int window) {
|
FILE * start_index_file(const char tmp_index[])
|
||||||
FILE *index_fp;
|
{
|
||||||
char *write_buf;
|
FILE * tmp_index_fp = fopen(tmp_index, "w+b");
|
||||||
unsigned int i;
|
if (!tmp_index_fp)
|
||||||
|
{
|
||||||
index_fp = fopen(tmp_index, "w");
|
|
||||||
if (!index_fp) {
|
|
||||||
fprintf(stderr, "Could not open temporary m3u8 index file (%s), no index file will be created\n", tmp_index);
|
fprintf(stderr, "Could not open temporary m3u8 index file (%s), no index file will be created\n", tmp_index);
|
||||||
return -1;
|
return NULL;
|
||||||
|
};
|
||||||
|
|
||||||
|
return tmp_index_fp;
|
||||||
|
}
|
||||||
|
|
||||||
|
FILE * write_index_file(FILE * tmp_index_fp,
|
||||||
|
const double segment_duration,
|
||||||
|
const char output_prefix[],
|
||||||
|
const char http_prefix[],
|
||||||
|
const unsigned int segment_index)
|
||||||
|
{
|
||||||
|
char write_buf[1024] = { 0 };
|
||||||
|
|
||||||
|
if (!tmp_index_fp)
|
||||||
|
{
|
||||||
|
return NULL;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_buf = malloc(sizeof(char) * 1024);
|
snprintf(write_buf,
|
||||||
if (!write_buf) {
|
sizeof(write_buf),
|
||||||
fprintf(stderr, "Could not allocate write buffer for index file, index file will be invalid\n");
|
"#EXTINF:%u,\n%s%s-%u.ts\n",
|
||||||
fclose(index_fp);
|
(int)(segment_duration + 0.5),
|
||||||
return -1;
|
http_prefix,
|
||||||
}
|
output_prefix,
|
||||||
|
segment_index);
|
||||||
|
|
||||||
if (window) {
|
if (fwrite(write_buf, strlen(write_buf), 1, tmp_index_fp) != 1)
|
||||||
snprintf(write_buf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%u\n#EXT-X-MEDIA-SEQUENCE:%u\n", segment_duration, first_segment);
|
{
|
||||||
}
|
|
||||||
else {
|
|
||||||
snprintf(write_buf, 1024, "#EXTM3U\n#EXT-X-TARGETDURATION:%u\n", 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");
|
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
|
||||||
free(write_buf);
|
fclose(tmp_index_fp);
|
||||||
fclose(index_fp);
|
return NULL;
|
||||||
return -1;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
for (i = first_segment; i <= last_segment; i++) {
|
return tmp_index_fp;
|
||||||
snprintf(write_buf, 1024, "#EXTINF:%u,\n%s%s-%u.ts\n", segment_duration, http_prefix, output_prefix, i);
|
}
|
||||||
if (fwrite(write_buf, strlen(write_buf), 1, index_fp) != 1) {
|
|
||||||
|
void close_index_file(FILE * tmp_index_fp,
|
||||||
|
const char index[],
|
||||||
|
const unsigned int target_segment_duration,
|
||||||
|
const unsigned int first_segment,
|
||||||
|
const int window)
|
||||||
|
{
|
||||||
|
char write_buf[1024] = { 0 };
|
||||||
|
FILE * index_fp = NULL;
|
||||||
|
|
||||||
|
if (!tmp_index_fp)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
index_fp = fopen(index, "wb");
|
||||||
|
if (!index_fp)
|
||||||
|
{
|
||||||
|
fprintf(stderr, "Could not open m3u8 index file (%s), no index file will be created\n", index);
|
||||||
|
return;
|
||||||
|
};
|
||||||
|
|
||||||
|
if (window)
|
||||||
|
{
|
||||||
|
snprintf(write_buf,
|
||||||
|
sizeof(write_buf),
|
||||||
|
"#EXTM3U\n#EXT-X-TARGETDURATION:%u\n#EXT-X-MEDIA-SEQUENCE:%u\n",
|
||||||
|
target_segment_duration,
|
||||||
|
first_segment);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
snprintf(write_buf,
|
||||||
|
sizeof(write_buf),
|
||||||
|
"#EXTM3U\n#EXT-X-TARGETDURATION:%u\n",
|
||||||
|
target_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");
|
fprintf(stderr, "Could not write to m3u8 index file, will not continue writing to index file\n");
|
||||||
free(write_buf);
|
|
||||||
fclose(index_fp);
|
fclose(index_fp);
|
||||||
return -1;
|
return;
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if (end) {
|
/* rewind the temp index file and transfer it's contents into the index file */
|
||||||
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);
|
|
||||||
|
|
||||||
// simple filecopy for windows
|
|
||||||
// remove and rename doesnt work well on windows and apache because of file locking
|
|
||||||
FILE *fs,*ft;
|
|
||||||
char ch;
|
char ch;
|
||||||
fs = fopen(tmp_index,"r");
|
|
||||||
ft = fopen(index,"w");
|
|
||||||
|
|
||||||
while( (ch=getc(fs)) != EOF) {
|
rewind(tmp_index_fp);
|
||||||
putc(ch,ft);
|
while (fread(&ch, 1, 1, tmp_index_fp) == 1)
|
||||||
|
{
|
||||||
|
fwrite(&ch, 1, 1, index_fp);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
fclose(fs);
|
snprintf(write_buf, sizeof(write_buf), "#EXT-X-ENDLIST\n");
|
||||||
fclose(ft);
|
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");
|
||||||
|
}
|
||||||
|
|
||||||
return 0;
|
fclose(index_fp);
|
||||||
}
|
}
|
||||||
|
|
||||||
typedef struct SMPacketLink
|
typedef struct SMPacketLink
|
||||||
@@ -293,14 +329,15 @@ int main(int argc, char **argv)
|
|||||||
{
|
{
|
||||||
const char *input;
|
const char *input;
|
||||||
const char *output_prefix;
|
const char *output_prefix;
|
||||||
double segment_duration;
|
double target_segment_duration;
|
||||||
char *segment_duration_check;
|
char *segment_duration_check;
|
||||||
const char *index;
|
const char *index;
|
||||||
char *tmp_index;
|
char *tmp_index;
|
||||||
const char *http_prefix;
|
const char *http_prefix;
|
||||||
long max_tsfiles = 0;
|
long max_tsfiles = 0;
|
||||||
char *max_tsfiles_check;
|
char *max_tsfiles_check;
|
||||||
double prev_segment_time = 0;
|
double prev_segment_time = 0.0;
|
||||||
|
double segment_duration = 0.0;
|
||||||
unsigned int output_index = 1;
|
unsigned int output_index = 1;
|
||||||
AVInputFormat *ifmt;
|
AVInputFormat *ifmt;
|
||||||
AVOutputFormat *ofmt;
|
AVOutputFormat *ofmt;
|
||||||
@@ -316,16 +353,14 @@ int main(int argc, char **argv)
|
|||||||
int kill_file = 0;
|
int kill_file = 0;
|
||||||
unsigned int first_segment = 1;
|
unsigned int first_segment = 1;
|
||||||
unsigned int last_segment = 0;
|
unsigned int last_segment = 0;
|
||||||
int write_index = 1;
|
|
||||||
int decode_done = 0;
|
int decode_done = 0;
|
||||||
char *dot;
|
char *dot;
|
||||||
int ret;
|
int ret;
|
||||||
int i;
|
int i;
|
||||||
int remove_file;
|
int remove_file;
|
||||||
FILE * pid_file;
|
FILE * pid_file;
|
||||||
double packetStartTime = 0.0;
|
|
||||||
double packetDuration = 0.0;
|
|
||||||
TSMPacketList * packetQueue = createPacketList();
|
TSMPacketList * packetQueue = createPacketList();
|
||||||
|
FILE * tmp_index_fp = NULL;
|
||||||
|
|
||||||
if (argc < 6 || argc > 8) {
|
if (argc < 6 || argc > 8) {
|
||||||
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>] [<search kill file>]\n\nCompiled by Daniel Espendiller - www.espend.de\nbuild on %s %s with %s\n\nTook some code from:\n - source:http://svn.assembla.com/svn/legend/segmenter/\n - iStreamdev:http://projects.vdr-developer.org/git/?p=istreamdev.git;a=tree;f=segmenter;hb=HEAD\n - live_segmenter:http://github.com/carsonmcdonald/HTTP-Live-Video-Stream-Segmenter-and-Distributor", argv[0], __DATE__, __TIME__, __VERSION__);
|
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>] [<search kill file>]\n\nCompiled by Daniel Espendiller - www.espend.de\nbuild on %s %s with %s\n\nTook some code from:\n - source:http://svn.assembla.com/svn/legend/segmenter/\n - iStreamdev:http://projects.vdr-developer.org/git/?p=istreamdev.git;a=tree;f=segmenter;hb=HEAD\n - live_segmenter:http://github.com/carsonmcdonald/HTTP-Live-Video-Stream-Segmenter-and-Distributor", argv[0], __DATE__, __TIME__, __VERSION__);
|
||||||
@@ -333,7 +368,7 @@ int main(int argc, char **argv)
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Create PID file
|
// Create PID file
|
||||||
pid_file=fopen("./segmenter.pid", "w");
|
pid_file=fopen("./segmenter.pid", "wb");
|
||||||
if (pid_file)
|
if (pid_file)
|
||||||
{
|
{
|
||||||
fprintf(pid_file, "%d", getpid());
|
fprintf(pid_file, "%d", getpid());
|
||||||
@@ -346,8 +381,8 @@ int main(int argc, char **argv)
|
|||||||
if (!strcmp(input, "-")) {
|
if (!strcmp(input, "-")) {
|
||||||
input = "pipe:";
|
input = "pipe:";
|
||||||
}
|
}
|
||||||
segment_duration = strtod(argv[2], &segment_duration_check);
|
target_segment_duration = strtod(argv[2], &segment_duration_check);
|
||||||
if (segment_duration_check == argv[2] || segment_duration == HUGE_VAL || segment_duration == -HUGE_VAL) {
|
if (segment_duration_check == argv[2] || target_segment_duration == HUGE_VAL || target_segment_duration == -HUGE_VAL) {
|
||||||
fprintf(stderr, "Segment duration time (%s) invalid\n", argv[2]);
|
fprintf(stderr, "Segment duration time (%s) invalid\n", argv[2]);
|
||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
@@ -475,11 +510,15 @@ int main(int argc, char **argv)
|
|||||||
goto error;
|
goto error;
|
||||||
}
|
}
|
||||||
|
|
||||||
write_index = !write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, last_segment, 0, max_tsfiles);
|
prev_segment_time = (double)(ic->start_time) / (double)(AV_TIME_BASE);
|
||||||
|
|
||||||
|
tmp_index_fp = start_index_file(tmp_index);
|
||||||
|
|
||||||
do {
|
do {
|
||||||
double segment_time;
|
double segment_time = 0.0;
|
||||||
AVPacket packet;
|
AVPacket packet;
|
||||||
|
double packetStartTime = 0.0;
|
||||||
|
double packetDuration = 0.0;
|
||||||
|
|
||||||
if (!decode_done)
|
if (!decode_done)
|
||||||
{
|
{
|
||||||
@@ -528,12 +567,12 @@ int main(int argc, char **argv)
|
|||||||
(double)(ic->streams[packet.stream_index]->time_base.num) /
|
(double)(ic->streams[packet.stream_index]->time_base.num) /
|
||||||
(double)(ic->streams[packet.stream_index]->time_base.den);
|
(double)(ic->streams[packet.stream_index]->time_base.den);
|
||||||
|
|
||||||
#if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG))
|
|
||||||
packetDuration =
|
packetDuration =
|
||||||
(double)(packet.duration) *
|
(double)(packet.duration) *
|
||||||
(double)(ic->streams[packet.stream_index]->time_base.num) /
|
(double)(ic->streams[packet.stream_index]->time_base.num) /
|
||||||
(double)(ic->streams[packet.stream_index]->time_base.den);
|
(double)(ic->streams[packet.stream_index]->time_base.den);
|
||||||
|
|
||||||
|
#if !defined(NDEBUG) && (defined(DEBUG) || defined(_DEBUG))
|
||||||
fprintf(stderr,
|
fprintf(stderr,
|
||||||
"stream %i, packet [%f, %f)\n",
|
"stream %i, packet [%f, %f)\n",
|
||||||
packet.stream_index,
|
packet.stream_index,
|
||||||
@@ -541,6 +580,8 @@ int main(int argc, char **argv)
|
|||||||
packetStartTime + packetDuration);
|
packetStartTime + packetDuration);
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
segment_duration = packetStartTime + packetDuration - prev_segment_time;
|
||||||
|
|
||||||
if (packet.stream_index == video_index && (packet.flags & PKT_FLAG_KEY)) {
|
if (packet.stream_index == video_index && (packet.flags & PKT_FLAG_KEY)) {
|
||||||
segment_time = packetStartTime;
|
segment_time = packetStartTime;
|
||||||
}
|
}
|
||||||
@@ -551,7 +592,7 @@ int main(int argc, char **argv)
|
|||||||
segment_time = prev_segment_time;
|
segment_time = prev_segment_time;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (segment_time - prev_segment_time >= segment_duration) {
|
if (segment_time - prev_segment_time >= target_segment_duration) {
|
||||||
put_flush_packet(oc->pb);
|
put_flush_packet(oc->pb);
|
||||||
url_fclose(oc->pb);
|
url_fclose(oc->pb);
|
||||||
|
|
||||||
@@ -563,9 +604,11 @@ int main(int argc, char **argv)
|
|||||||
remove_file = 0;
|
remove_file = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_index) {
|
tmp_index_fp = write_index_file(tmp_index_fp,
|
||||||
write_index = !write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, ++last_segment, 0, max_tsfiles);
|
segment_time - prev_segment_time,
|
||||||
}
|
output_prefix,
|
||||||
|
http_prefix,
|
||||||
|
++last_segment);
|
||||||
|
|
||||||
if (remove_file) {
|
if (remove_file) {
|
||||||
snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1);
|
snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1);
|
||||||
@@ -580,7 +623,7 @@ int main(int argc, char **argv)
|
|||||||
|
|
||||||
// close when when we find the file 'kill'
|
// close when when we find the file 'kill'
|
||||||
if (kill_file) {
|
if (kill_file) {
|
||||||
FILE* fp = fopen("kill","r");
|
FILE* fp = fopen("kill", "rb");
|
||||||
if (fp) {
|
if (fp) {
|
||||||
fprintf(stderr, "user abort: found kill file\n");
|
fprintf(stderr, "user abort: found kill file\n");
|
||||||
fclose(fp);
|
fclose(fp);
|
||||||
@@ -627,9 +670,9 @@ int main(int argc, char **argv)
|
|||||||
remove_file = 0;
|
remove_file = 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
if (write_index) {
|
tmp_index_fp = write_index_file(tmp_index_fp, segment_duration, output_prefix, http_prefix, ++last_segment);
|
||||||
write_index_file(index, tmp_index, segment_duration, output_prefix, http_prefix, first_segment, ++last_segment, 1, max_tsfiles);
|
close_index_file(tmp_index_fp, index, target_segment_duration, first_segment, max_tsfiles);
|
||||||
}
|
tmp_index_fp = NULL;
|
||||||
|
|
||||||
if (remove_file) {
|
if (remove_file) {
|
||||||
snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1);
|
snprintf(remove_filename, strlen(output_prefix) + 15, "%s-%u.ts", output_prefix, first_segment - 1);
|
||||||
|
|||||||
Reference in New Issue
Block a user