[FFmpeg-devel] [PATCH 3/7] avformat/hlsenc: fix segmentation issues
Nicolas Martyanoff
khaelin at gmail.com
Fri Jul 18 10:57:43 CEST 2014
- Select a reference stream (the first video stream, or the first audio
stream if there is no video stream) instead of using the PTS of any video
stream.
- Control the segment length using the time since the last segment.
---
libavformat/hlsenc.c | 165 +++++++++++++++++++++++++++++++--------------------
1 file changed, 101 insertions(+), 64 deletions(-)
diff --git a/libavformat/hlsenc.c b/libavformat/hlsenc.c
index 30b320f..76cbd0e 100644
--- a/libavformat/hlsenc.c
+++ b/libavformat/hlsenc.c
@@ -41,23 +41,25 @@ typedef struct HLSSegment {
typedef struct HLSContext {
const AVClass *class; // Class for private options.
- unsigned number;
int64_t sequence;
int64_t start_sequence;
+ int nb_entries;
+ int max_nb_segments;
AVOutputFormat *oformat;
AVFormatContext *ctx;
float target_duration;
- int max_nb_segments;
int wrap;
int64_t recording_time;
int has_video;
- int64_t start_pts;
- int64_t end_pts;
- double duration; // last segment duration computed so far, in seconds
- int nb_entries;
+ int64_t first_pts; ///< first PTS found in the input file
+ int64_t last_pts; ///< PTS of the last packet we splitted at
+
+ int ref_stream; ///< the index of the stream used as time reference
+
+ double segment_duration; // last segment duration computed so far, in seconds
HLSSegment *segments;
HLSSegment *last_segment;
@@ -71,10 +73,15 @@ typedef struct HLSContext {
static int hls_mux_init(AVFormatContext *ctx)
{
HLSContext *hls;
- int i;
+ int i, video_ref, audio_ref;
hls = ctx->priv_data;
+ hls->segment_duration = 0.0;
+
+ hls->first_pts = AV_NOPTS_VALUE;
+ hls->last_pts = AV_NOPTS_VALUE;
+
hls->ctx = avformat_alloc_context();
if (!hls->ctx)
return AVERROR(ENOMEM);
@@ -83,21 +90,61 @@ static int hls_mux_init(AVFormatContext *ctx)
hls->ctx->interrupt_callback = ctx->interrupt_callback;
av_dict_copy(&hls->ctx->metadata, ctx->metadata, 0);
+ hls->ref_stream = -1;
+ video_ref = -1;
+ audio_ref = -1;
+
for (i = 0; i < ctx->nb_streams; i++) {
- AVStream *stream;
+ AVStream *stream, *input_stream;
+ input_stream = ctx->streams[i];
+
+ /* Create output stream */
stream = avformat_new_stream(hls->ctx, NULL);
if (!stream)
return AVERROR(ENOMEM);
- avcodec_copy_context(stream->codec, ctx->streams[i]->codec);
- stream->sample_aspect_ratio = ctx->streams[i]->sample_aspect_ratio;
+ avcodec_copy_context(stream->codec, input_stream->codec);
+ stream->sample_aspect_ratio = input_stream->sample_aspect_ratio;
+
+ /* See if we can use this stream as a time reference. We use the first
+ * video stream found, or the first audio stream if there is no video
+ * stream. */
+ switch (input_stream->codec->codec_type) {
+ case AVMEDIA_TYPE_VIDEO:
+ hls->has_video++;
+ if (video_ref == -1)
+ video_ref = i;
+ break;
+
+ case AVMEDIA_TYPE_AUDIO:
+ if (audio_ref == -1)
+ audio_ref = i;
+ break;
+
+ default:
+ break;
+ }
+ }
+
+ if (hls->has_video > 1) {
+ av_log(ctx, AV_LOG_WARNING,
+ "More than a single video stream present, "
+ "expect issues decoding it.\n");
+ }
+
+ if (video_ref >= 0) {
+ hls->ref_stream = video_ref;
+ } else if (audio_ref >= 0) {
+ hls->ref_stream = audio_ref;
+ } else {
+ return AVERROR_STREAM_NOT_FOUND;
}
return 0;
}
-static int hls_append_segment(HLSContext *hls, double duration)
+static int hls_append_segment(HLSContext *hls)
{
const char *basename;
HLSSegment *segment;
@@ -111,7 +158,8 @@ static int hls_append_segment(HLSContext *hls, double duration)
basename = av_basename(hls->ctx->filename);
av_strlcpy(segment->filename, basename, sizeof(segment->filename));
- segment->duration = duration;
+ segment->duration = hls->segment_duration;
+
segment->next = NULL;
if (!hls->segments) {
@@ -214,7 +262,6 @@ static int hls_create_file(AVFormatContext *ctx)
"Invalid segment filename template '%s'\n", hls->media_filename);
return AVERROR(EINVAL);
}
- hls->number++;
ret = avio_open2(&hls->ctx->pb, hls->ctx->filename, AVIO_FLAG_WRITE,
&ctx->interrupt_callback, NULL);
@@ -230,30 +277,15 @@ static int hls_create_file(AVFormatContext *ctx)
static int hls_write_header(AVFormatContext *ctx)
{
HLSContext *hls;
- int ret, i;
+ int ret;
char *dot;
size_t basename_size;
char filename[1024];
hls = ctx->priv_data;
- hls->sequence = hls->start_sequence;
+ hls->sequence = hls->start_sequence;
hls->recording_time = hls->target_duration * AV_TIME_BASE;
- hls->start_pts = AV_NOPTS_VALUE;
-
- /* Findout if the input file contains a video stream */
- for (i = 0; i < ctx->nb_streams; i++) {
- AVStream *stream;
-
- stream = ctx->streams[i];
- hls->has_video += stream->codec->codec_type == AVMEDIA_TYPE_VIDEO;
- }
-
- if (hls->has_video > 1) {
- av_log(ctx, AV_LOG_WARNING,
- "More than a single video stream present, "
- "expect issues decoding it.\n");
- }
/* HLS demands that media files use the MPEG-TS container */
hls->oformat = av_guess_format("mpegts", NULL, NULL);
@@ -262,7 +294,7 @@ static int hls_write_header(AVFormatContext *ctx)
goto fail;
}
- /* Generate the basename of all generated media files */
+ /* Generate the name of the media file(s) */
av_strlcpy(filename, ctx->filename, sizeof(filename));
dot = strrchr(filename, '.');
if (dot)
@@ -289,7 +321,7 @@ static int hls_write_header(AVFormatContext *ctx)
ret = avformat_write_header(hls->ctx, NULL);
if (ret < 0)
- return ret;
+ goto fail;
return 0;
@@ -307,52 +339,53 @@ static int hls_write_packet(AVFormatContext *ctx, AVPacket *pkt)
{
HLSContext *hls;
AVStream *stream;
- int64_t end_pts;
- int64_t pkt_ts;
- int is_ref_pkt;
int ret, can_split;
hls = ctx->priv_data;
stream = ctx->streams[pkt->stream_index];
- is_ref_pkt = 1;
- can_split = 1;
-
- end_pts = hls->recording_time * hls->number;
-
- if (hls->start_pts == AV_NOPTS_VALUE) {
- hls->start_pts = pkt->pts;
- hls->end_pts = pkt->pts;
+ if (hls->first_pts == AV_NOPTS_VALUE) {
+ hls->first_pts = pkt->pts;
+ hls->last_pts = pkt->pts;
}
- if (hls->has_video) {
- can_split = (pkt->flags & AV_PKT_FLAG_KEY)
- && (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO);
- is_ref_pkt = (stream->codec->codec_type == AVMEDIA_TYPE_VIDEO);
- }
+ if (pkt->stream_index == hls->ref_stream
+ && pkt->pts != AV_NOPTS_VALUE) {
+ AVRational time_base;
+ double pts_diff;
+ enum AVMediaType media;
- if (pkt->pts == AV_NOPTS_VALUE)
- is_ref_pkt = can_split = 0;
+ media = stream->codec->codec_type;
- if (is_ref_pkt) {
- double pts_diff;
+ time_base = stream->time_base;
+ pts_diff = pkt->pts - hls->last_pts;
- pts_diff = pkt->pts - hls->end_pts;
+ hls->segment_duration = pts_diff * time_base.num / time_base.den;
- hls->duration = pts_diff * stream->time_base.num
- / stream->time_base.den;
+ if (hls->segment_duration < hls->target_duration) {
+ can_split = 0;
+ } else if (hls->has_video) {
+ /* If there is a video stream, cut before video keyframes */
+ can_split = (media == AVMEDIA_TYPE_VIDEO)
+ && (pkt->flags & AV_PKT_FLAG_KEY);
+ } else {
+ /* In there is no video stream, cut at every audio packet */
+ can_split = (media == AVMEDIA_TYPE_AUDIO);
+ }
+ } else {
+ can_split = 0;
}
- pkt_ts = pkt->pts - hls->start_pts;
-
- if (can_split && av_compare_ts(pkt_ts, stream->time_base,
- end_pts, AV_TIME_BASE_Q) >= 0) {
- ret = hls_append_segment(hls, hls->duration);
+ if (can_split) {
+ /* Write a segment ending just before this packet */
+ ret = hls_append_segment(hls);
if (ret)
return ret;
- hls->end_pts = pkt->pts;
- hls->duration = 0;
+ hls->last_pts = pkt->pts;
+
+ /* Get ready for the next segment starting with this packet */
+ hls->segment_duration = 0.0;
/* Flush any buffered data and close the current file */
av_write_frame(hls->ctx, NULL);
@@ -369,7 +402,10 @@ static int hls_write_packet(AVFormatContext *ctx, AVPacket *pkt)
}
ret = ff_write_chained(hls->ctx, pkt->stream_index, pkt, ctx);
- return ret;
+ if (ret < 0)
+ return ret;
+
+ return 0;
}
static int hls_write_trailer(struct AVFormatContext *ctx)
@@ -382,7 +418,8 @@ static int hls_write_trailer(struct AVFormatContext *ctx)
avio_closep(&hls->ctx->pb);
avformat_free_context(hls->ctx);
av_free(hls->media_filename);
- hls_append_segment(hls, hls->duration);
+ hls_append_segment(hls);
+
hls_generate_playlist(ctx, 1);
hls_free_segments(hls);
--
1.8.5.5
More information about the ffmpeg-devel
mailing list