-
-
Save rcombs/0d5b38b90b2c10805bdde4d877cfbb7a to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
diff --git a/libavformat/Makefile b/libavformat/Makefile | |
index c49f9de..a217889 100644 | |
--- a/libavformat/Makefile | |
+++ b/libavformat/Makefile | |
@@ -131,6 +131,7 @@ OBJS-$(CONFIG_CDXL_DEMUXER) += cdxl.o | |
OBJS-$(CONFIG_CINE_DEMUXER) += cinedec.o | |
OBJS-$(CONFIG_CONCAT_DEMUXER) += concatdec.o | |
OBJS-$(CONFIG_CRC_MUXER) += crcenc.o | |
+OBJS-$(CONFIG_CUE_DEMUXER) += cuedec.o | |
OBJS-$(CONFIG_DATA_DEMUXER) += rawdec.o | |
OBJS-$(CONFIG_DATA_MUXER) += rawdec.o | |
OBJS-$(CONFIG_DASH_MUXER) += dashenc.o | |
diff --git a/libavformat/allformats.c b/libavformat/allformats.c | |
index d490cc4..d062e35 100644 | |
--- a/libavformat/allformats.c | |
+++ b/libavformat/allformats.c | |
@@ -100,6 +100,7 @@ void av_register_all(void) | |
REGISTER_DEMUXER (CINE, cine); | |
REGISTER_DEMUXER (CONCAT, concat); | |
REGISTER_MUXER (CRC, crc); | |
+ REGISTER_DEMUXER (CUE, cue); | |
REGISTER_MUXER (DASH, dash); | |
REGISTER_MUXDEMUX(DATA, data); | |
REGISTER_MUXDEMUX(DAUD, daud); | |
diff --git a/libavformat/flacenc.c b/libavformat/flacenc.c | |
index 89b21e9..ef0070b 100644 | |
--- a/libavformat/flacenc.c | |
+++ b/libavformat/flacenc.c | |
@@ -21,10 +21,13 @@ | |
#include "libavutil/channel_layout.h" | |
#include "libavutil/opt.h" | |
+#include "libavutil/pixdesc.h" | |
#include "libavcodec/flac.h" | |
#include "avformat.h" | |
#include "avio_internal.h" | |
#include "flacenc.h" | |
+#include "id3v2.h" | |
+#include "internal.h" | |
#include "vorbiscomment.h" | |
#include "libavcodec/bytestream.h" | |
@@ -33,6 +36,12 @@ typedef struct FlacMuxerContext { | |
const AVClass *class; | |
int write_header; | |
+ int audio_stream_idx; | |
+ AVPacket *pics; | |
+ int nb_pics, waiting_pics; | |
+ /* audio packets are queued here until we get all the attached pictures */ | |
+ AVPacketList *queue, *queue_end; | |
+ | |
/* updated streaminfo sent by the encoder at the end */ | |
uint8_t *streaminfo; | |
} FlacMuxerContext; | |
@@ -74,31 +83,138 @@ static int flac_write_block_comment(AVIOContext *pb, AVDictionary **m, | |
return 0; | |
} | |
-static int flac_write_header(struct AVFormatContext *s) | |
+static int flac_write_picture(struct AVFormatContext *s, AVPacket *pkt) | |
{ | |
- int ret; | |
- int padding = s->metadata_header_padding; | |
- AVCodecParameters *par = s->streams[0]->codecpar; | |
- FlacMuxerContext *c = s->priv_data; | |
- | |
- if (!c->write_header) | |
+ AVIOContext *pb = s->pb; | |
+ const AVPixFmtDescriptor *pixdesc; | |
+ const CodecMime *mime = ff_id3v2_mime_tags; | |
+ AVDictionaryEntry *e; | |
+ const char *mimetype = NULL, *desc = ""; | |
+ const AVStream *st = s->streams[pkt->stream_index]; | |
+ int i, mimelen, desclen, type = 0; | |
+ | |
+ if (!pkt->data) | |
return 0; | |
- if (s->nb_streams > 1) { | |
- av_log(s, AV_LOG_ERROR, "only one stream is supported\n"); | |
- return AVERROR(EINVAL); | |
+ while (mime->id != AV_CODEC_ID_NONE) { | |
+ if (mime->id == st->codecpar->codec_id) { | |
+ mimetype = mime->str; | |
+ break; | |
+ } | |
+ mime++; | |
} | |
- if (par->codec_id != AV_CODEC_ID_FLAC) { | |
- av_log(s, AV_LOG_ERROR, "unsupported codec\n"); | |
+ if (!mimetype) { | |
+ av_log(s, AV_LOG_ERROR, "No mimetype is known for stream %d, cannot " | |
+ "write an attached picture.\n", st->index); | |
return AVERROR(EINVAL); | |
} | |
+ mimelen = strlen(mimetype); | |
+ | |
+ /* get the picture type */ | |
+ e = av_dict_get(st->metadata, "comment", NULL, 0); | |
+ for (i = 0; e && i < FF_ARRAY_ELEMS(ff_id3v2_picture_types); i++) { | |
+ if (!av_strcasecmp(e->value, ff_id3v2_picture_types[i])) { | |
+ type = i; | |
+ break; | |
+ } | |
+ } | |
+ | |
+ /* get the description */ | |
+ if ((e = av_dict_get(st->metadata, "title", NULL, 0))) | |
+ desc = e->value; | |
+ desclen = strlen(desc); | |
+ | |
+ avio_w8(pb, 0x06); | |
+ avio_wb24(pb, 4 + 4 + mimelen + 4 + desclen + 4 + 4 + 4 + 4 + 4 + pkt->size); | |
+ avio_wb32(pb, type); | |
+ | |
+ avio_wb32(pb, mimelen); | |
+ avio_write(pb, mimetype, mimelen); | |
+ | |
+ avio_wb32(pb, desclen); | |
+ avio_write(pb, desc, desclen); | |
+ | |
+ avio_wb32(pb, st->codecpar->width); | |
+ avio_wb32(pb, st->codecpar->height); | |
+ if ((pixdesc = av_pix_fmt_desc_get(st->codecpar->format))) | |
+ avio_wb32(pb, av_get_bits_per_pixel(pixdesc)); | |
+ else | |
+ avio_wb32(pb, 0); | |
+ avio_wb32(pb, 0); | |
+ | |
+ avio_wb32(pb, pkt->size); | |
+ avio_write(pb, pkt->data, pkt->size); | |
+ return 0; | |
+} | |
+ | |
+static int flac_finish_header(struct AVFormatContext *s) | |
+{ | |
+ FlacMuxerContext *c = s->priv_data; | |
+ int i, ret, padding = s->metadata_header_padding; | |
if (padding < 0) | |
padding = 8192; | |
/* The FLAC specification states that 24 bits are used to represent the | |
* size of a metadata block so we must clip this value to 2^24-1. */ | |
padding = av_clip_uintp2(padding, 24); | |
+ for (i = 0; i < c->nb_pics; i++) { | |
+ ret = flac_write_picture(s, &c->pics[i]); | |
+ if (ret) | |
+ return ret; | |
+ } | |
+ | |
+ ret = flac_write_block_comment(s->pb, &s->metadata, !padding, | |
+ s->flags & AVFMT_FLAG_BITEXACT); | |
+ if (ret) | |
+ return ret; | |
+ | |
+ /* The command line flac encoder defaults to placing a seekpoint | |
+ * every 10s. So one might add padding to allow that later | |
+ * but there seems to be no simple way to get the duration here. | |
+ * So just add the amount requested by the user. */ | |
+ if (padding) | |
+ flac_write_block_padding(s->pb, padding, 1); | |
+ | |
+ return 0; | |
+} | |
+ | |
+static int flac_write_header(struct AVFormatContext *s) | |
+{ | |
+ int ret, i; | |
+ AVCodecParameters *par; | |
+ FlacMuxerContext *c = s->priv_data; | |
+ | |
+ c->audio_stream_idx = -1; | |
+ for (i = 0; i < s->nb_streams; i++) { | |
+ AVStream *st = s->streams[i]; | |
+ if (st->codecpar->codec_type == AVMEDIA_TYPE_AUDIO) { | |
+ if (c->audio_stream_idx >= 0 || st->codecpar->codec_id != AV_CODEC_ID_FLAC) { | |
+ av_log(s, AV_LOG_ERROR, "Invalid audio stream. Exactly one FLAC " | |
+ "audio stream is required.\n"); | |
+ return AVERROR(EINVAL); | |
+ } | |
+ par = s->streams[i]->codecpar; | |
+ c->audio_stream_idx = i; | |
+ } else if (st->codecpar->codec_type != AVMEDIA_TYPE_VIDEO) { | |
+ av_log(s, AV_LOG_ERROR, "Only audio streams and pictures are allowed in FLAC.\n"); | |
+ return AVERROR(EINVAL); | |
+ } else if (!c->write_header) { | |
+ av_log(s, AV_LOG_ERROR, "Can't write attached pictures without a header.\n"); | |
+ return AVERROR(EINVAL); | |
+ } | |
+ } | |
+ if (c->audio_stream_idx < 0) { | |
+ av_log(s, AV_LOG_ERROR, "No audio stream present.\n"); | |
+ return AVERROR(EINVAL); | |
+ } | |
+ c->waiting_pics = c->nb_pics = s->nb_streams - 1; | |
+ if (c->nb_pics && !(c->pics = av_calloc(c->nb_pics, sizeof(AVPacket)))) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ if (!c->write_header) | |
+ return 0; | |
+ | |
ret = ff_flac_write_header(s->pb, par->extradata, | |
par->extradata_size, 0); | |
if (ret) | |
@@ -121,17 +237,8 @@ static int flac_write_header(struct AVFormatContext *s) | |
} | |
} | |
- ret = flac_write_block_comment(s->pb, &s->metadata, !padding, | |
- s->flags & AVFMT_FLAG_BITEXACT); | |
- if (ret) | |
- return ret; | |
- | |
- /* The command line flac encoder defaults to placing a seekpoint | |
- * every 10s. So one might add padding to allow that later | |
- * but there seems to be no simple way to get the duration here. | |
- * So just add the amount requested by the user. */ | |
- if (padding) | |
- flac_write_block_padding(s->pb, padding, 1); | |
+ if (!c->waiting_pics) | |
+ ret = flac_finish_header(s); | |
return ret; | |
} | |
@@ -163,7 +270,7 @@ static int flac_write_trailer(struct AVFormatContext *s) | |
return 0; | |
} | |
-static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
+static int flac_write_audio_packet(struct AVFormatContext *s, AVPacket *pkt) | |
{ | |
FlacMuxerContext *c = s->priv_data; | |
uint8_t *streaminfo; | |
@@ -186,6 +293,76 @@ static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
return 0; | |
} | |
+static int flac_queue_flush(AVFormatContext *s) | |
+{ | |
+ FlacMuxerContext *c = s->priv_data; | |
+ AVPacketList *pktl; | |
+ int ret = 0, write = 1; | |
+ | |
+ flac_finish_header(s); | |
+ | |
+ while ((pktl = c->queue)) { | |
+ if (write && (ret = flac_write_audio_packet(s, &pktl->pkt)) < 0) | |
+ write = 0; | |
+ av_packet_unref(&pktl->pkt); | |
+ c->queue = pktl->next; | |
+ av_freep(&pktl); | |
+ } | |
+ c->queue_end = NULL; | |
+ return ret; | |
+} | |
+ | |
+static int flac_write_packet(struct AVFormatContext *s, AVPacket *pkt) | |
+{ | |
+ FlacMuxerContext *c = s->priv_data; | |
+ if (pkt->stream_index == c->audio_stream_idx) { | |
+ if (c->waiting_pics) { | |
+ /* buffer audio packets until we get all the pictures */ | |
+ AVPacketList *pktl = av_mallocz(sizeof(*pktl)); | |
+ int ret; | |
+ if (!pktl) | |
+ return AVERROR(ENOMEM); | |
+ | |
+ ret = av_copy_packet(&pktl->pkt, pkt); | |
+ if (ret < 0) { | |
+ av_freep(&pktl); | |
+ return ret; | |
+ } | |
+ | |
+ if (c->queue_end) | |
+ c->queue_end->next = pktl; | |
+ else | |
+ c->queue = pktl; | |
+ c->queue_end = pktl; | |
+ } else | |
+ return flac_write_audio_packet(s, pkt); | |
+ } else { | |
+ int ret, index = pkt->stream_index; | |
+ | |
+ /* warn only once for each stream */ | |
+ if (s->streams[pkt->stream_index]->nb_frames == 1) { | |
+ av_log(s, AV_LOG_WARNING, "Got more than one picture in stream %d," | |
+ " ignoring.\n", pkt->stream_index); | |
+ } | |
+ if (!c->waiting_pics || s->streams[pkt->stream_index]->nb_frames >= 1) | |
+ return 0; | |
+ | |
+ if (index > c->audio_stream_idx) | |
+ index--; | |
+ | |
+ if ((ret = av_copy_packet(&c->pics[index], pkt)) < 0) | |
+ return ret; | |
+ c->waiting_pics--; | |
+ | |
+ /* flush the buffered audio packets */ | |
+ if (!c->waiting_pics && | |
+ (ret = flac_queue_flush(s)) < 0) | |
+ return ret; | |
+ } | |
+ | |
+ return 0; | |
+} | |
+ | |
static const AVOption flacenc_options[] = { | |
{ "write_header", "Write the file header", offsetof(FlacMuxerContext, write_header), AV_OPT_TYPE_BOOL, {.i64 = 1}, 0, 1, AV_OPT_FLAG_ENCODING_PARAM }, | |
{ NULL }, | |
diff --git a/libavformat/segment.c b/libavformat/segment.c | |
index 4c6c6d4..307dfb3 100644 | |
--- a/libavformat/segment.c | |
+++ b/libavformat/segment.c | |
@@ -108,6 +108,8 @@ typedef struct SegmentContext { | |
int frame_count; ///< total number of reference frames | |
int segment_frame_count; ///< number of reference frames in the segment | |
+ int split_chapters; | |
+ | |
int64_t time_delta; | |
int individual_header_trailer; /**< Set by a private option. */ | |
int write_header_trailer; /**< Set by a private option. */ | |
@@ -126,6 +128,8 @@ typedef struct SegmentContext { | |
SegmentListEntry cur_entry; | |
SegmentListEntry *segment_list_entries; | |
SegmentListEntry *segment_list_entries_end; | |
+ | |
+ AVPacket *attached_pics; | |
} SegmentContext; | |
static void print_csv_escaped_str(AVIOContext *ctx, const char *str) | |
@@ -182,6 +186,7 @@ static int segment_mux_init(AVFormatContext *s) | |
} | |
st->sample_aspect_ratio = s->streams[i]->sample_aspect_ratio; | |
st->time_base = s->streams[i]->time_base; | |
+ st->disposition = s->streams[i]->disposition; | |
av_dict_copy(&st->metadata, s->streams[i]->metadata, 0); | |
} | |
@@ -238,6 +243,8 @@ static int segment_start(AVFormatContext *s, int write_header) | |
if ((err = segment_mux_init(s)) < 0) | |
return err; | |
oc = seg->avf; | |
+ if (seg->split_chapters && seg->segment_idx + 1 < s->nb_chapters && (err = av_dict_copy(&oc->metadata, s->chapters[seg->segment_idx + 1]->metadata, 0)) < 0) | |
+ return err; | |
} | |
seg->segment_idx++; | |
@@ -258,12 +265,19 @@ static int segment_start(AVFormatContext *s, int write_header) | |
av_opt_set(oc->priv_data, "mpegts_flags", "+resend_headers", 0); | |
if (write_header) { | |
+ int i; | |
AVDictionary *options = NULL; | |
av_dict_copy(&options, seg->format_options, 0); | |
err = avformat_write_header(oc, &options); | |
av_dict_free(&options); | |
if (err < 0) | |
return err; | |
+ for (i = 0; i < s->nb_streams; i++) { | |
+ if (s->streams[i]->disposition & AV_DISPOSITION_ATTACHED_PIC && | |
+ seg->attached_pics[i].data) { | |
+ av_write_frame(oc, &seg->attached_pics[i]); | |
+ } | |
+ } | |
} | |
seg->segment_frame_count = 0; | |
@@ -664,7 +678,7 @@ static int seg_init(AVFormatContext *s) | |
} else if (seg->frames_str) { | |
if ((ret = parse_frames(s, &seg->frames, &seg->nb_frames, seg->frames_str)) < 0) | |
return ret; | |
- } else { | |
+ } else if (!seg->split_chapters) { | |
/* set default value if not specified */ | |
if (!seg->time_str) | |
seg->time_str = av_strdup("2"); | |
@@ -734,6 +748,9 @@ static int seg_init(AVFormatContext *s) | |
if ((ret = segment_mux_init(s)) < 0) | |
goto fail; | |
+ if (seg->split_chapters && s->nb_chapters && (ret = av_dict_copy(&seg->avf->metadata, s->chapters[0]->metadata, 0)) < 0) | |
+ goto fail; | |
+ | |
if ((ret = set_segment_filename(s)) < 0) | |
goto fail; | |
oc = seg->avf; | |
@@ -774,6 +791,11 @@ static int seg_init(AVFormatContext *s) | |
avpriv_set_pts_info(outer_st, inner_st->pts_wrap_bits, inner_st->time_base.num, inner_st->time_base.den); | |
} | |
+ if (!(seg->attached_pics = av_calloc(s->nb_streams, sizeof(AVPacket)))) { | |
+ ret = AVERROR(ENOMEM); | |
+ goto fail; | |
+ } | |
+ | |
if (oc->avoid_negative_ts > 0 && s->avoid_negative_ts < 0) | |
s->avoid_negative_ts = 1; | |
@@ -812,6 +834,9 @@ static int seg_write_packet(AVFormatContext *s, AVPacket *pkt) | |
if (!seg->avf) | |
return AVERROR(EINVAL); | |
+ if (st->disposition & AV_DISPOSITION_ATTACHED_PIC) | |
+ av_copy_packet(&seg->attached_pics[pkt->stream_index], pkt); | |
+ | |
calc_times: | |
if (seg->times) { | |
end_pts = seg->segment_count < seg->nb_times ? | |
@@ -819,6 +844,9 @@ calc_times: | |
} else if (seg->frames) { | |
start_frame = seg->segment_count < seg->nb_frames ? | |
seg->frames[seg->segment_count] : INT_MAX; | |
+ } else if (seg->split_chapters) { | |
+ end_pts = seg->segment_count + 1 < s->nb_chapters ? | |
+ av_rescale_q(s->chapters[seg->segment_count]->end, s->chapters[seg->segment_count]->time_base, AV_TIME_BASE_Q) : INT64_MAX; | |
} else { | |
if (seg->use_clocktime) { | |
int64_t avgt = av_gettime(); | |
@@ -957,6 +985,17 @@ fail: | |
return ret; | |
} | |
+static void seg_deinit(struct AVFormatContext *s) | |
+{ | |
+ SegmentContext *seg = s->priv_data; | |
+ int i; | |
+ if (seg->attached_pics) { | |
+ for (i = 0; i < s->nb_streams; i++) | |
+ av_packet_unref(&seg->attached_pics[i]); | |
+ av_freep(&seg->attached_pics); | |
+ } | |
+} | |
+ | |
static int seg_check_bitstream(struct AVFormatContext *s, const AVPacket *pkt) | |
{ | |
SegmentContext *seg = s->priv_data; | |
@@ -1006,6 +1045,7 @@ static const AVOption options[] = { | |
{ "segment_time_delta","set approximation value used for the segment times", OFFSET(time_delta), AV_OPT_TYPE_DURATION, {.i64 = 0}, 0, 0, E }, | |
{ "segment_times", "set segment split time points", OFFSET(times_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, | |
{ "segment_frames", "set segment split frame numbers", OFFSET(frames_str),AV_OPT_TYPE_STRING,{.str = NULL}, 0, 0, E }, | |
+ { "segment_chapters", "split segments on chapter start times", OFFSET(split_chapters), AV_OPT_TYPE_BOOL, {.i64 = 0}, 0, 1, E }, | |
{ "segment_wrap", "set number after which the index wraps", OFFSET(segment_idx_wrap), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, | |
{ "segment_list_entry_prefix", "set base url prefix for segments", OFFSET(entry_prefix), AV_OPT_TYPE_STRING, {.str = NULL}, 0, 0, E }, | |
{ "segment_start_number", "set the sequence number of the first segment", OFFSET(segment_idx), AV_OPT_TYPE_INT, {.i64 = 0}, 0, INT_MAX, E }, | |
@@ -1056,6 +1096,7 @@ AVOutputFormat ff_stream_segment_muxer = { | |
.init = seg_init, | |
.write_packet = seg_write_packet, | |
.write_trailer = seg_write_trailer, | |
+ .deinit = seg_deinit, | |
.check_bitstream = seg_check_bitstream, | |
.priv_class = &sseg_class, | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment