ffmpeg/libavformat/smoothstreamingenc.c

653 lines
23 KiB
C
Raw Normal View History

/*
* Live smooth streaming fragmenter
* Copyright (c) 2012 Martin Storsjo
*
* This file is part of FFmpeg.
*
* FFmpeg is free software; you can redistribute it and/or
* modify it under the terms of the GNU Lesser General Public
* License as published by the Free Software Foundation; either
* version 2.1 of the License, or (at your option) any later version.
*
* FFmpeg 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
* Lesser General Public License for more details.
*
* You should have received a copy of the GNU Lesser General Public
* License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
#include "config.h"
#include <float.h>
#if HAVE_UNISTD_H
#include <unistd.h>
#endif
#include "avformat.h"
#include "avio_internal.h"
#include "internal.h"
#include "os_support.h"
#include "avc.h"
#include "url.h"
#include "isom.h"
#include "libavutil/opt.h"
#include "libavutil/avstring.h"
#include "libavutil/file.h"
#include "libavutil/mathematics.h"
#include "libavutil/intreadwrite.h"
typedef struct Fragment {
char file[1024];
char infofile[1024];
int64_t start_time, duration;
int n;
int64_t start_pos, size;
} Fragment;
typedef struct OutputStream {
AVFormatContext *ctx;
int ctx_inited;
char dirname[1024];
uint8_t iobuf[32768];
URLContext *out; // Current output stream where all output is written
2012-12-19 18:48:21 +01:00
URLContext *out2; // Auxiliary output stream where all output is also written
URLContext *tail_out; // The actual main output stream, if we're currently seeked back to write elsewhere
int64_t tail_pos, cur_pos, cur_start_pos;
int packets_written;
const char *stream_type_tag;
int nb_fragments, fragments_size, fragment_index;
Fragment **fragments;
const char *fourcc;
char *private_str;
int packet_size;
int audio_tag;
} OutputStream;
typedef struct SmoothStreamingContext {
const AVClass *class; /* Class for private options. */
int window_size;
int extra_window_size;
int lookahead_count;
int min_frag_duration;
int remove_at_exit;
OutputStream *streams;
int has_video, has_audio;
int nb_fragments;
} SmoothStreamingContext;
static int ism_write(void *opaque, uint8_t *buf, int buf_size)
{
OutputStream *os = opaque;
if (os->out)
ffurl_write(os->out, buf, buf_size);
if (os->out2)
ffurl_write(os->out2, buf, buf_size);
os->cur_pos += buf_size;
if (os->cur_pos >= os->tail_pos)
os->tail_pos = os->cur_pos;
return buf_size;
}
static int64_t ism_seek(void *opaque, int64_t offset, int whence)
{
OutputStream *os = opaque;
int i;
if (whence != SEEK_SET)
return AVERROR(ENOSYS);
if (os->tail_out) {
if (os->out) {
ffurl_close(os->out);
}
if (os->out2) {
ffurl_close(os->out2);
}
os->out = os->tail_out;
os->out2 = NULL;
os->tail_out = NULL;
}
if (offset >= os->cur_start_pos) {
if (os->out)
ffurl_seek(os->out, offset - os->cur_start_pos, SEEK_SET);
os->cur_pos = offset;
return offset;
}
for (i = os->nb_fragments - 1; i >= 0; i--) {
Fragment *frag = os->fragments[i];
if (offset >= frag->start_pos && offset < frag->start_pos + frag->size) {
int ret;
AVDictionary *opts = NULL;
os->tail_out = os->out;
av_dict_set(&opts, "truncate", "0", 0);
ret = ffurl_open_whitelist(&os->out, frag->file, AVIO_FLAG_WRITE,
&os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist);
av_dict_free(&opts);
if (ret < 0) {
os->out = os->tail_out;
os->tail_out = NULL;
return ret;
}
av_dict_set(&opts, "truncate", "0", 0);
ffurl_open_whitelist(&os->out2, frag->infofile, AVIO_FLAG_WRITE,
&os->ctx->interrupt_callback, &opts, os->ctx->protocol_whitelist, os->ctx->protocol_blacklist);
av_dict_free(&opts);
ffurl_seek(os->out, offset - frag->start_pos, SEEK_SET);
if (os->out2)
ffurl_seek(os->out2, offset - frag->start_pos, SEEK_SET);
os->cur_pos = offset;
return offset;
}
}
return AVERROR(EIO);
}
static void get_private_data(OutputStream *os)
{
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
AVCodecParameters *par = os->ctx->streams[0]->codecpar;
uint8_t *ptr = par->extradata;
int size = par->extradata_size;
int i;
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (par->codec_id == AV_CODEC_ID_H264) {
ff_avc_write_annexb_extradata(ptr, &ptr, &size);
if (!ptr)
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
ptr = par->extradata;
}
if (!ptr)
return;
os->private_str = av_mallocz(2*size + 1);
if (!os->private_str)
goto fail;
for (i = 0; i < size; i++)
snprintf(&os->private_str[2*i], 3, "%02x", ptr[i]);
fail:
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (ptr != par->extradata)
av_free(ptr);
}
static void ism_free(AVFormatContext *s)
{
SmoothStreamingContext *c = s->priv_data;
int i, j;
if (!c->streams)
return;
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
ffurl_close(os->out);
ffurl_close(os->out2);
ffurl_close(os->tail_out);
os->out = os->out2 = os->tail_out = NULL;
if (os->ctx && os->ctx_inited)
av_write_trailer(os->ctx);
if (os->ctx && os->ctx->pb)
av_freep(&os->ctx->pb);
if (os->ctx)
avformat_free_context(os->ctx);
av_freep(&os->private_str);
for (j = 0; j < os->nb_fragments; j++)
av_freep(&os->fragments[j]);
av_freep(&os->fragments);
}
av_freep(&c->streams);
}
static void output_chunk_list(OutputStream *os, AVIOContext *out, int final, int skip, int window_size)
{
int removed = 0, i, start = 0;
if (os->nb_fragments <= 0)
return;
if (os->fragments[0]->n > 0)
removed = 1;
if (final)
skip = 0;
if (window_size)
start = FFMAX(os->nb_fragments - skip - window_size, 0);
for (i = start; i < os->nb_fragments - skip; i++) {
Fragment *frag = os->fragments[i];
if (!final || removed)
avio_printf(out, "<c t=\"%"PRIu64"\" d=\"%"PRIu64"\" />\n", frag->start_time, frag->duration);
else
avio_printf(out, "<c n=\"%d\" d=\"%"PRIu64"\" />\n", frag->n, frag->duration);
}
}
static int write_manifest(AVFormatContext *s, int final)
{
SmoothStreamingContext *c = s->priv_data;
AVIOContext *out;
char filename[1024], temp_filename[1024];
int ret, i, video_chunks = 0, audio_chunks = 0, video_streams = 0, audio_streams = 0;
int64_t duration = 0;
snprintf(filename, sizeof(filename), "%s/Manifest", s->filename);
snprintf(temp_filename, sizeof(temp_filename), "%s/Manifest.tmp", s->filename);
ret = s->io_open(s, &out, temp_filename, AVIO_FLAG_WRITE, NULL);
if (ret < 0) {
av_log(s, AV_LOG_ERROR, "Unable to open %s for writing\n", temp_filename);
return ret;
}
avio_printf(out, "<?xml version=\"1.0\" encoding=\"utf-8\"?>\n");
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
if (os->nb_fragments > 0) {
Fragment *last = os->fragments[os->nb_fragments - 1];
duration = last->start_time + last->duration;
}
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (s->streams[i]->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
video_chunks = os->nb_fragments;
video_streams++;
} else {
audio_chunks = os->nb_fragments;
audio_streams++;
}
}
if (!final) {
duration = 0;
video_chunks = audio_chunks = 0;
}
if (c->window_size) {
video_chunks = FFMIN(video_chunks, c->window_size);
audio_chunks = FFMIN(audio_chunks, c->window_size);
}
avio_printf(out, "<SmoothStreamingMedia MajorVersion=\"2\" MinorVersion=\"0\" Duration=\"%"PRIu64"\"", duration);
if (!final)
avio_printf(out, " IsLive=\"true\" LookAheadFragmentCount=\"%d\" DVRWindowLength=\"0\"", c->lookahead_count);
avio_printf(out, ">\n");
if (c->has_video) {
int last = -1, index = 0;
avio_printf(out, "<StreamIndex Type=\"video\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(video={start time})\">\n", video_streams, video_chunks);
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_VIDEO)
continue;
last = i;
avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64"\" FourCC=\"%s\" MaxWidth=\"%d\" MaxHeight=\"%d\" CodecPrivateData=\"%s\" />\n", index, (int64_t)s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->width, s->streams[i]->codecpar->height, os->private_str);
index++;
}
output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size);
avio_printf(out, "</StreamIndex>\n");
}
if (c->has_audio) {
int last = -1, index = 0;
avio_printf(out, "<StreamIndex Type=\"audio\" QualityLevels=\"%d\" Chunks=\"%d\" Url=\"QualityLevels({bitrate})/Fragments(audio={start time})\">\n", audio_streams, audio_chunks);
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (s->streams[i]->codecpar->codec_type != AVMEDIA_TYPE_AUDIO)
continue;
last = i;
avio_printf(out, "<QualityLevel Index=\"%d\" Bitrate=\"%"PRId64"\" FourCC=\"%s\" SamplingRate=\"%d\" Channels=\"%d\" BitsPerSample=\"16\" PacketSize=\"%d\" AudioTag=\"%d\" CodecPrivateData=\"%s\" />\n", index, (int64_t)s->streams[i]->codecpar->bit_rate, os->fourcc, s->streams[i]->codecpar->sample_rate, s->streams[i]->codecpar->channels, os->packet_size, os->audio_tag, os->private_str);
index++;
}
output_chunk_list(&c->streams[last], out, final, c->lookahead_count, c->window_size);
avio_printf(out, "</StreamIndex>\n");
}
avio_printf(out, "</SmoothStreamingMedia>\n");
avio_flush(out);
ff_format_io_close(s, &out);
return ff_rename(temp_filename, filename, s);
}
static int ism_write_header(AVFormatContext *s)
{
SmoothStreamingContext *c = s->priv_data;
int ret = 0, i;
AVOutputFormat *oformat;
if (mkdir(s->filename, 0777) == -1 && errno != EEXIST) {
ret = AVERROR(errno);
av_log(s, AV_LOG_ERROR, "mkdir failed\n");
goto fail;
}
oformat = av_guess_format("ismv", NULL, NULL);
if (!oformat) {
ret = AVERROR_MUXER_NOT_FOUND;
goto fail;
}
c->streams = av_mallocz_array(s->nb_streams, sizeof(*c->streams));
if (!c->streams) {
ret = AVERROR(ENOMEM);
goto fail;
}
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
AVFormatContext *ctx;
AVStream *st;
AVDictionary *opts = NULL;
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (!s->streams[i]->codecpar->bit_rate) {
av_log(s, AV_LOG_ERROR, "No bit rate set for stream %d\n", i);
ret = AVERROR(EINVAL);
goto fail;
}
snprintf(os->dirname, sizeof(os->dirname), "%s/QualityLevels(%"PRId64")", s->filename, (int64_t)s->streams[i]->codecpar->bit_rate);
if (mkdir(os->dirname, 0777) == -1 && errno != EEXIST) {
ret = AVERROR(errno);
av_log(s, AV_LOG_ERROR, "mkdir failed\n");
goto fail;
}
ctx = avformat_alloc_context();
if (!ctx || ff_copy_whiteblacklists(ctx, s) < 0) {
ret = AVERROR(ENOMEM);
goto fail;
}
os->ctx = ctx;
ctx->oformat = oformat;
ctx->interrupt_callback = s->interrupt_callback;
if (!(st = avformat_new_stream(ctx, NULL))) {
ret = AVERROR(ENOMEM);
goto fail;
}
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
avcodec_parameters_copy(st->codecpar, s->streams[i]->codecpar);
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio;
st->time_base = s->streams[i]->time_base;
ctx->pb = avio_alloc_context(os->iobuf, sizeof(os->iobuf), AVIO_FLAG_WRITE, os, NULL, ism_write, ism_seek);
if (!ctx->pb) {
ret = AVERROR(ENOMEM);
goto fail;
}
av_dict_set_int(&opts, "ism_lookahead", c->lookahead_count, 0);
av_dict_set(&opts, "movflags", "frag_custom", 0);
if ((ret = avformat_write_header(ctx, &opts)) < 0) {
goto fail;
}
os->ctx_inited = 1;
avio_flush(ctx->pb);
av_dict_free(&opts);
s->streams[i]->time_base = st->time_base;
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) {
c->has_video = 1;
os->stream_type_tag = "video";
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (st->codecpar->codec_id == AV_CODEC_ID_H264) {
os->fourcc = "H264";
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
} else if (st->codecpar->codec_id == AV_CODEC_ID_VC1) {
os->fourcc = "WVC1";
} else {
av_log(s, AV_LOG_ERROR, "Unsupported video codec\n");
ret = AVERROR(EINVAL);
goto fail;
}
} else {
c->has_audio = 1;
os->stream_type_tag = "audio";
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if (st->codecpar->codec_id == AV_CODEC_ID_AAC) {
os->fourcc = "AACL";
os->audio_tag = 0xff;
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
} else if (st->codecpar->codec_id == AV_CODEC_ID_WMAPRO) {
os->fourcc = "WMAP";
os->audio_tag = 0x0162;
} else {
av_log(s, AV_LOG_ERROR, "Unsupported audio codec\n");
ret = AVERROR(EINVAL);
goto fail;
}
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
os->packet_size = st->codecpar->block_align ? st->codecpar->block_align : 4;
}
get_private_data(os);
}
if (!c->has_video && c->min_frag_duration <= 0) {
av_log(s, AV_LOG_WARNING, "no video stream and no min frag duration set\n");
ret = AVERROR(EINVAL);
goto fail;
}
ret = write_manifest(s, 0);
fail:
if (ret)
ism_free(s);
return ret;
}
static int parse_fragment(AVFormatContext *s, const char *filename, int64_t *start_ts, int64_t *duration, int64_t *moof_size, int64_t size)
{
AVIOContext *in;
int ret;
uint32_t len;
if ((ret = s->io_open(s, &in, filename, AVIO_FLAG_READ, NULL)) < 0)
return ret;
ret = AVERROR(EIO);
*moof_size = avio_rb32(in);
if (*moof_size < 8 || *moof_size > size)
goto fail;
if (avio_rl32(in) != MKTAG('m','o','o','f'))
goto fail;
len = avio_rb32(in);
if (len > *moof_size)
goto fail;
if (avio_rl32(in) != MKTAG('m','f','h','d'))
goto fail;
avio_seek(in, len - 8, SEEK_CUR);
avio_rb32(in); /* traf size */
if (avio_rl32(in) != MKTAG('t','r','a','f'))
goto fail;
while (avio_tell(in) < *moof_size) {
uint32_t len = avio_rb32(in);
uint32_t tag = avio_rl32(in);
int64_t end = avio_tell(in) + len - 8;
if (len < 8 || len >= *moof_size)
goto fail;
if (tag == MKTAG('u','u','i','d')) {
static const uint8_t tfxd[] = {
0x6d, 0x1d, 0x9b, 0x05, 0x42, 0xd5, 0x44, 0xe6,
0x80, 0xe2, 0x14, 0x1d, 0xaf, 0xf7, 0x57, 0xb2
};
uint8_t uuid[16];
avio_read(in, uuid, 16);
if (!memcmp(uuid, tfxd, 16) && len >= 8 + 16 + 4 + 16) {
avio_seek(in, 4, SEEK_CUR);
*start_ts = avio_rb64(in);
*duration = avio_rb64(in);
ret = 0;
break;
}
}
avio_seek(in, end, SEEK_SET);
}
fail:
ff_format_io_close(s, &in);
return ret;
}
static int add_fragment(OutputStream *os, const char *file, const char *infofile, int64_t start_time, int64_t duration, int64_t start_pos, int64_t size)
{
int err;
Fragment *frag;
if (os->nb_fragments >= os->fragments_size) {
os->fragments_size = (os->fragments_size + 1) * 2;
if ((err = av_reallocp(&os->fragments, sizeof(*os->fragments) *
os->fragments_size)) < 0) {
os->fragments_size = 0;
os->nb_fragments = 0;
return err;
}
}
frag = av_mallocz(sizeof(*frag));
if (!frag)
return AVERROR(ENOMEM);
av_strlcpy(frag->file, file, sizeof(frag->file));
av_strlcpy(frag->infofile, infofile, sizeof(frag->infofile));
frag->start_time = start_time;
frag->duration = duration;
frag->start_pos = start_pos;
frag->size = size;
frag->n = os->fragment_index;
os->fragments[os->nb_fragments++] = frag;
os->fragment_index++;
return 0;
}
static int copy_moof(AVFormatContext *s, const char* infile, const char *outfile, int64_t size)
{
AVIOContext *in, *out;
int ret = 0;
if ((ret = s->io_open(s, &in, infile, AVIO_FLAG_READ, NULL)) < 0)
return ret;
if ((ret = s->io_open(s, &out, outfile, AVIO_FLAG_WRITE, NULL)) < 0) {
ff_format_io_close(s, &in);
return ret;
}
while (size > 0) {
uint8_t buf[8192];
int n = FFMIN(size, sizeof(buf));
n = avio_read(in, buf, n);
if (n <= 0) {
ret = AVERROR(EIO);
break;
}
avio_write(out, buf, n);
size -= n;
}
avio_flush(out);
ff_format_io_close(s, &out);
ff_format_io_close(s, &in);
return ret;
}
static int ism_flush(AVFormatContext *s, int final)
{
SmoothStreamingContext *c = s->priv_data;
int i, ret = 0;
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
char filename[1024], target_filename[1024], header_filename[1024];
int64_t size;
int64_t start_ts, duration, moof_size;
if (!os->packets_written)
continue;
snprintf(filename, sizeof(filename), "%s/temp", os->dirname);
ret = ffurl_open_whitelist(&os->out, filename, AVIO_FLAG_WRITE, &s->interrupt_callback, NULL, s->protocol_whitelist, s->protocol_blacklist);
if (ret < 0)
break;
os->cur_start_pos = os->tail_pos;
av_write_frame(os->ctx, NULL);
avio_flush(os->ctx->pb);
os->packets_written = 0;
if (!os->out || os->tail_out)
return AVERROR(EIO);
ffurl_close(os->out);
os->out = NULL;
size = os->tail_pos - os->cur_start_pos;
if ((ret = parse_fragment(s, filename, &start_ts, &duration, &moof_size, size)) < 0)
break;
snprintf(header_filename, sizeof(header_filename), "%s/FragmentInfo(%s=%"PRIu64")", os->dirname, os->stream_type_tag, start_ts);
snprintf(target_filename, sizeof(target_filename), "%s/Fragments(%s=%"PRIu64")", os->dirname, os->stream_type_tag, start_ts);
copy_moof(s, filename, header_filename, moof_size);
ret = ff_rename(filename, target_filename, s);
if (ret < 0)
break;
add_fragment(os, target_filename, header_filename, start_ts, duration,
os->cur_start_pos, size);
}
if (c->window_size || (final && c->remove_at_exit)) {
for (i = 0; i < s->nb_streams; i++) {
OutputStream *os = &c->streams[i];
int j;
int remove = os->nb_fragments - c->window_size - c->extra_window_size - c->lookahead_count;
if (final && c->remove_at_exit)
remove = os->nb_fragments;
if (remove > 0) {
for (j = 0; j < remove; j++) {
unlink(os->fragments[j]->file);
unlink(os->fragments[j]->infofile);
av_freep(&os->fragments[j]);
}
os->nb_fragments -= remove;
memmove(os->fragments, os->fragments + remove, os->nb_fragments * sizeof(*os->fragments));
}
if (final && c->remove_at_exit)
rmdir(os->dirname);
}
}
if (ret >= 0)
ret = write_manifest(s, final);
return ret;
}
static int ism_write_packet(AVFormatContext *s, AVPacket *pkt)
{
SmoothStreamingContext *c = s->priv_data;
AVStream *st = s->streams[pkt->stream_index];
OutputStream *os = &c->streams[pkt->stream_index];
int64_t end_dts = (c->nb_fragments + 1) * (int64_t) c->min_frag_duration;
int ret;
if (st->first_dts == AV_NOPTS_VALUE)
st->first_dts = pkt->dts;
lavf: replace AVStream.codec with AVStream.codecpar Currently, AVStream contains an embedded AVCodecContext instance, which is used by demuxers to export stream parameters to the caller and by muxers to receive stream parameters from the caller. It is also used internally as the codec context that is passed to parsers. In addition, it is also widely used by the callers as the decoding (when demuxer) or encoding (when muxing) context, though this has been officially discouraged since Libav 11. There are multiple important problems with this approach: - the fields in AVCodecContext are in general one of * stream parameters * codec options * codec state However, it's not clear which ones are which. It is consequently unclear which fields are a demuxer allowed to set or a muxer allowed to read. This leads to erratic behaviour depending on whether decoding or encoding is being performed or not (and whether it uses the AVStream embedded codec context). - various synchronization issues arising from the fact that the same context is used by several different APIs (muxers/demuxers, parsers, bitstream filters and encoders/decoders) simultaneously, with there being no clear rules for who can modify what and the different processes being typically delayed with respect to each other. - avformat_find_stream_info() making it necessary to support opening and closing a single codec context multiple times, thus complicating the semantics of freeing various allocated objects in the codec context. Those problems are resolved by replacing the AVStream embedded codec context with a newly added AVCodecParameters instance, which stores only the stream parameters exported by the demuxers or read by the muxers.
2014-06-18 20:42:52 +02:00
if ((!c->has_video || st->codecpar->codec_type == AVMEDIA_TYPE_VIDEO) &&
av_compare_ts(pkt->dts - st->first_dts, st->time_base,
end_dts, AV_TIME_BASE_Q) >= 0 &&
pkt->flags & AV_PKT_FLAG_KEY && os->packets_written) {
if ((ret = ism_flush(s, 0)) < 0)
return ret;
c->nb_fragments++;
}
os->packets_written++;
return ff_write_chained(os->ctx, 0, pkt, s, 0);
}
static int ism_write_trailer(AVFormatContext *s)
{
SmoothStreamingContext *c = s->priv_data;
ism_flush(s, 1);
if (c->remove_at_exit) {
char filename[1024];
snprintf(filename, sizeof(filename), "%s/Manifest", s->filename);
unlink(filename);
rmdir(s->filename);
}
ism_free(s);
return 0;
}
#define OFFSET(x) offsetof(SmoothStreamingContext, x)
#define E AV_OPT_FLAG_ENCODING_PARAM
static const AVOption options[] = {
{ "window_size", "number of fragments kept in the manifest", OFFSET(window_size), AV_OPT_TYPE_INT, { .i64 = 0 }, 0, INT_MAX, E },
{ "extra_window_size", "number of fragments kept outside of the manifest before removing from disk", OFFSET(extra_window_size), AV_OPT_TYPE_INT, { .i64 = 5 }, 0, INT_MAX, E },
{ "lookahead_count", "number of lookahead fragments", OFFSET(lookahead_count), AV_OPT_TYPE_INT, { .i64 = 2 }, 0, INT_MAX, E },
{ "min_frag_duration", "minimum fragment duration (in microseconds)", OFFSET(min_frag_duration), AV_OPT_TYPE_INT64, { .i64 = 5000000 }, 0, INT_MAX, E },
{ "remove_at_exit", "remove all fragments when finished", OFFSET(remove_at_exit), AV_OPT_TYPE_BOOL, { .i64 = 0 }, 0, 1, E },
{ NULL },
};
static const AVClass ism_class = {
.class_name = "smooth streaming muxer",
.item_name = av_default_item_name,
.option = options,
.version = LIBAVUTIL_VERSION_INT,
};
AVOutputFormat ff_smoothstreaming_muxer = {
.name = "smoothstreaming",
.long_name = NULL_IF_CONFIG_SMALL("Smooth Streaming Muxer"),
.priv_data_size = sizeof(SmoothStreamingContext),
.audio_codec = AV_CODEC_ID_AAC,
.video_codec = AV_CODEC_ID_H264,
.flags = AVFMT_GLOBALHEADER | AVFMT_NOFILE,
.write_header = ism_write_header,
.write_packet = ism_write_packet,
.write_trailer = ism_write_trailer,
.codec_tag = (const AVCodecTag* const []){ ff_mp4_obj_type, 0 },
.priv_class = &ism_class,
};