summaryrefslogtreecommitdiff
path: root/libavformat/avienc.c
diff options
context:
space:
mode:
Diffstat (limited to 'libavformat/avienc.c')
-rw-r--r--libavformat/avienc.c192
1 files changed, 154 insertions, 38 deletions
diff --git a/libavformat/avienc.c b/libavformat/avienc.c
index 9c2936af60..3b5e5c4413 100644
--- a/libavformat/avienc.c
+++ b/libavformat/avienc.c
@@ -2,29 +2,39 @@
* AVI muxer
* Copyright (c) 2000 Fabrice Bellard
*
- * This file is part of Libav.
+ * This file is part of FFmpeg.
*
- * Libav is free software; you can redistribute it and/or
+ * 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.
*
- * Libav is distributed in the hope that it will be useful,
+ * 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 Libav; if not, write to the Free Software
+ * License along with FFmpeg; if not, write to the Free Software
* Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
*/
+
+//#define DEBUG
+
#include "avformat.h"
#include "internal.h"
#include "avi.h"
#include "avio_internal.h"
#include "riff.h"
+#include "mpegts.h"
+#include "libavformat/avlanguage.h"
+#include "libavutil/avstring.h"
#include "libavutil/intreadwrite.h"
#include "libavutil/dict.h"
+#include "libavutil/avassert.h"
+#include "libavutil/timestamp.h"
+#include "libavutil/pixdesc.h"
+#include "libavcodec/raw.h"
/*
* TODO:
@@ -39,6 +49,7 @@ typedef struct AVIIentry {
typedef struct AVIIndex {
int64_t indx_start;
+ int64_t audio_strm_offset;
int entry;
int ents_allocated;
AVIIentry** cluster;
@@ -52,14 +63,20 @@ typedef struct AVIContext {
typedef struct AVIStream {
int64_t frames_hdr_strm;
- int audio_strm_length;
+ int64_t audio_strm_length;
int packet_count;
int entry;
+ int max_size;
+ int sample_requested;
+
+ int64_t last_dts;
AVIIndex indexes;
} AVIStream;
-static inline AVIIentry *avi_get_ientry(AVIIndex *idx, int ent_id)
+static int avi_write_packet(AVFormatContext *s, AVPacket *pkt);
+
+static inline AVIIentry *avi_get_ientry(const AVIIndex *idx, int ent_id)
{
int cl = ent_id / AVI_INDEX_CLUSTER_SIZE;
int id = ent_id % AVI_INDEX_CLUSTER_SIZE;
@@ -76,6 +93,7 @@ static int64_t avi_start_new_riff(AVFormatContext *s, AVIOContext *pb,
avi->riff_id++;
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
+ avist->indexes.audio_strm_offset = avist->audio_strm_length;
avist->indexes.entry = 0;
}
@@ -117,7 +135,7 @@ static int avi_write_counters(AVFormatContext *s, int riff_id)
for (n = 0; n < s->nb_streams; n++) {
AVIStream *avist = s->streams[n]->priv_data;
- assert(avist->frames_hdr_strm);
+ av_assert0(avist->frames_hdr_strm);
stream = s->streams[n]->codec;
avio_seek(pb, avist->frames_hdr_strm, SEEK_SET);
ff_parse_specific_params(s->streams[n], &au_byterate, &au_ssize, &au_scale);
@@ -129,7 +147,7 @@ static int avi_write_counters(AVFormatContext *s, int riff_id)
nb_frames = FFMAX(nb_frames, avist->packet_count);
}
if (riff_id == 1) {
- assert(avi->frames_hdr_all);
+ av_assert0(avi->frames_hdr_all);
avio_seek(pb, avi->frames_hdr_all, SEEK_SET);
avio_wl32(pb, nb_frames);
}
@@ -147,11 +165,12 @@ static int avi_write_header(AVFormatContext *s)
AVStream *video_st = NULL;
int64_t list1, list2, strh, strf;
AVDictionaryEntry *t = NULL;
+ int padding;
if (s->nb_streams > AVI_MAX_STREAM_COUNT) {
av_log(s, AV_LOG_ERROR, "AVI does not support >%d streams\n",
AVI_MAX_STREAM_COUNT);
- return -1;
+ return AVERROR(EINVAL);
}
for (n = 0; n < s->nb_streams; n++) {
@@ -254,9 +273,18 @@ static int avi_write_header(AVFormatContext *s)
ff_parse_specific_params(st, &au_byterate, &au_ssize, &au_scale);
+ if ( enc->codec_type == AVMEDIA_TYPE_VIDEO
+ && enc->codec_id != AV_CODEC_ID_XSUB
+ && au_byterate > 1000LL*au_scale) {
+ au_byterate = 600;
+ au_scale = 1;
+ }
+ avpriv_set_pts_info(st, 64, au_scale, au_byterate);
+ if (enc->codec_id == AV_CODEC_ID_XSUB)
+ au_scale = au_byterate = 0;
+
avio_wl32(pb, au_scale); /* scale */
avio_wl32(pb, au_byterate); /* rate */
- avpriv_set_pts_info(st, 64, au_scale, au_byterate);
avio_wl32(pb, 0); /* start */
/* remember this offset to fill later */
@@ -267,7 +295,7 @@ static int avi_write_header(AVFormatContext *s)
else
avio_wl32(pb, 0); /* length, XXX: filled later */
- /* suggested buffer size */ //FIXME set at the end to largest chunk
+ /* suggested buffer size, is set to largest chunk size in avi_write_trailer */
if (enc->codec_type == AVMEDIA_TYPE_VIDEO)
avio_wl32(pb, 1024 * 1024);
else if (enc->codec_type == AVMEDIA_TYPE_AUDIO)
@@ -282,6 +310,9 @@ static int avi_write_header(AVFormatContext *s)
ff_end_tag(pb, strh);
if (enc->codec_type != AVMEDIA_TYPE_DATA) {
+ int ret;
+ enum AVPixelFormat pix_fmt;
+
strf = ff_start_tag(pb, "strf");
switch (enc->codec_type) {
case AVMEDIA_TYPE_SUBTITLE:
@@ -290,20 +321,47 @@ static int avi_write_header(AVFormatContext *s)
if (enc->codec_id != AV_CODEC_ID_XSUB)
break;
case AVMEDIA_TYPE_VIDEO:
- ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0);
+ /* WMP expects RGB 5:5:5 rawvideo in avi to have bpp set to 16. */
+ if ( !enc->codec_tag
+ && enc->codec_id == AV_CODEC_ID_RAWVIDEO
+ && enc->pix_fmt == AV_PIX_FMT_RGB555LE
+ && enc->bits_per_coded_sample == 15)
+ enc->bits_per_coded_sample = 16;
+ ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 0, 0);
+ pix_fmt = avpriv_find_pix_fmt(avpriv_pix_fmt_bps_avi,
+ enc->bits_per_coded_sample);
+ if ( !enc->codec_tag
+ && enc->codec_id == AV_CODEC_ID_RAWVIDEO
+ && enc->pix_fmt != pix_fmt
+ && enc->pix_fmt != AV_PIX_FMT_NONE)
+ av_log(s, AV_LOG_ERROR, "%s rawvideo cannot be written to avi, output file will be unreadable\n",
+ av_get_pix_fmt_name(enc->pix_fmt));
break;
case AVMEDIA_TYPE_AUDIO:
- if (ff_put_wav_header(pb, enc) < 0)
- return -1;
+ if ((ret = ff_put_wav_header(pb, enc, 0)) < 0)
+ return ret;
break;
default:
- return -1;
+ av_log(s, AV_LOG_ERROR,
+ "Invalid or not supported codec type '%s' found in the input\n",
+ (char *)av_x_if_null(av_get_media_type_string(enc->codec_type), "?"));
+ return AVERROR(EINVAL);
}
ff_end_tag(pb, strf);
if ((t = av_dict_get(st->metadata, "title", NULL, 0))) {
ff_riff_write_info_tag(s->pb, "strn", t->value);
t = NULL;
}
+ if (enc->codec_id == AV_CODEC_ID_XSUB
+ && (t = av_dict_get(s->streams[i]->metadata, "language", NULL, 0))) {
+ const char* langstr = av_convert_lang_to(t->value, AV_LANG_ISO639_1);
+ t = NULL;
+ if (langstr) {
+ char* str = av_asprintf("Subtitle - %s-xx;02", langstr);
+ ff_riff_write_info_tag(s->pb, "strn", str);
+ av_free(str);
+ }
+ }
}
if (pb->seekable) {
@@ -381,11 +439,18 @@ static int avi_write_header(AVFormatContext *s)
ff_riff_write_info(s);
+
+ padding = s->metadata_header_padding;
+ if (padding < 0)
+ padding = 1016;
+
/* some padding for easier tag editing */
- list2 = ff_start_tag(pb, "JUNK");
- for (i = 0; i < 1016; i += 4)
- avio_wl32(pb, 0);
- ff_end_tag(pb, list2);
+ if (padding) {
+ list2 = ff_start_tag(pb, "JUNK");
+ for (i = padding; i > 0; i -= 4)
+ avio_wl32(pb, 0);
+ ff_end_tag(pb, list2);
+ }
avi->movi_list = ff_start_tag(pb, "LIST");
ffio_wfourcc(pb, "movi");
@@ -403,14 +468,18 @@ static int avi_write_ix(AVFormatContext *s)
char ix_tag[] = "ix00";
int i, j;
- assert(pb->seekable);
+ av_assert0(pb->seekable);
- if (avi->riff_id > AVI_MASTER_INDEX_SIZE)
- return -1;
+ if (avi->riff_id > AVI_MASTER_INDEX_SIZE) {
+ av_log(s, AV_LOG_ERROR, "Invalid riff index %d > %d\n",
+ avi->riff_id, AVI_MASTER_INDEX_SIZE);
+ return AVERROR(EINVAL);
+ }
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
int64_t ix, pos;
+ int au_byterate, au_ssize, au_scale;
avi_stream2fourcc(tag, i, s->streams[i]->codec->codec_type);
ix_tag[3] = '0' + i;
@@ -446,7 +515,16 @@ static int avi_write_ix(AVFormatContext *s)
avio_skip(pb, 16 * avi->riff_id);
avio_wl64(pb, ix); /* qwOffset */
avio_wl32(pb, pos - ix); /* dwSize */
- avio_wl32(pb, avist->indexes.entry); /* dwDuration */
+ ff_parse_specific_params(s->streams[i], &au_byterate, &au_ssize, &au_scale);
+ if (s->streams[i]->codec->codec_type == AVMEDIA_TYPE_AUDIO && au_ssize > 0) {
+ uint32_t audio_segm_size = (avist->audio_strm_length - avist->indexes.audio_strm_offset);
+ if ((audio_segm_size % au_ssize > 0) && !avist->sample_requested) {
+ avpriv_request_sample(s, "OpenDML index duration for audio packets with partial frames");
+ avist->sample_requested = 1;
+ }
+ avio_wl32(pb, audio_segm_size / au_ssize); /* dwDuration (sample count) */
+ } else
+ avio_wl32(pb, avist->indexes.entry); /* dwDuration (packet count) */
avio_seek(pb, pos, SEEK_SET);
}
@@ -504,27 +582,56 @@ static int avi_write_idx1(AVFormatContext *s)
return 0;
}
-static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
+static int write_skip_frames(AVFormatContext *s, int stream_index, int64_t dts)
{
- unsigned char tag[5];
- unsigned int flags = 0;
- const int stream_index = pkt->stream_index;
- int size = pkt->size;
- AVIContext *avi = s->priv_data;
- AVIOContext *pb = s->pb;
AVIStream *avist = s->streams[stream_index]->priv_data;
AVCodecContext *enc = s->streams[stream_index]->codec;
- while (enc->block_align == 0 && pkt->dts != AV_NOPTS_VALUE &&
- pkt->dts > avist->packet_count) {
+ av_dlog(s, "dts:%s packet_count:%d stream_index:%d\n", av_ts2str(dts), avist->packet_count, stream_index);
+ while (enc->block_align == 0 && dts != AV_NOPTS_VALUE &&
+ dts > avist->packet_count && enc->codec_id != AV_CODEC_ID_XSUB && avist->packet_count) {
AVPacket empty_packet;
+ if (dts - avist->packet_count > 60000) {
+ av_log(s, AV_LOG_ERROR, "Too large number of skipped frames %"PRId64" > 60000\n", dts - avist->packet_count);
+ return AVERROR(EINVAL);
+ }
+
av_init_packet(&empty_packet);
empty_packet.size = 0;
empty_packet.data = NULL;
empty_packet.stream_index = stream_index;
avi_write_packet(s, &empty_packet);
+ av_dlog(s, "dup dts:%s packet_count:%d\n", av_ts2str(dts), avist->packet_count);
}
+
+ return 0;
+}
+
+static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
+{
+ unsigned char tag[5];
+ unsigned int flags = 0;
+ const int stream_index = pkt->stream_index;
+ int size = pkt->size;
+ AVIContext *avi = s->priv_data;
+ AVIOContext *pb = s->pb;
+ AVIStream *avist = s->streams[stream_index]->priv_data;
+ AVCodecContext *enc = s->streams[stream_index]->codec;
+ int ret;
+
+ if (enc->codec_id == AV_CODEC_ID_H264 && enc->codec_tag == MKTAG('H','2','6','4') && pkt->size) {
+ ret = ff_check_h264_startcode(s, s->streams[stream_index], pkt);
+ if (ret < 0)
+ return ret;
+ }
+
+ if ((ret = write_skip_frames(s, stream_index, pkt->dts)) < 0)
+ return ret;
+
+ if (pkt->dts != AV_NOPTS_VALUE)
+ avist->last_dts = pkt->dts + pkt->duration;
+
avist->packet_count++;
// Make sure to put an OpenDML chunk when the file size exceeds the limits
@@ -547,27 +654,27 @@ static int avi_write_packet(AVFormatContext *s, AVPacket *pkt)
avist->audio_strm_length += size;
if (s->pb->seekable) {
- int err;
AVIIndex *idx = &avist->indexes;
int cl = idx->entry / AVI_INDEX_CLUSTER_SIZE;
int id = idx->entry % AVI_INDEX_CLUSTER_SIZE;
if (idx->ents_allocated <= idx->entry) {
- if ((err = av_reallocp(&idx->cluster,
- (cl + 1) * sizeof(*idx->cluster))) < 0) {
+ idx->cluster = av_realloc_f(idx->cluster, sizeof(void*), cl+1);
+ if (!idx->cluster) {
idx->ents_allocated = 0;
idx->entry = 0;
- return err;
+ return AVERROR(ENOMEM);
}
idx->cluster[cl] =
av_malloc(AVI_INDEX_CLUSTER_SIZE * sizeof(AVIIentry));
if (!idx->cluster[cl])
- return -1;
+ return AVERROR(ENOMEM);
idx->ents_allocated += AVI_INDEX_CLUSTER_SIZE;
}
idx->cluster[cl][id].flags = flags;
idx->cluster[cl][id].pos = avio_tell(pb) - avi->movi_list;
idx->cluster[cl][id].len = size;
+ avist->max_size = FFMAX(avist->max_size, size);
idx->entry++;
}
@@ -588,6 +695,11 @@ static int avi_write_trailer(AVFormatContext *s)
int i, j, n, nb_frames;
int64_t file_size;
+ for (i = 0; i < s->nb_streams; i++) {
+ AVIStream *avist = s->streams[i]->priv_data;
+ write_skip_frames(s, i, avist->last_dts);
+ }
+
if (pb->seekable) {
if (avi->riff_id == 1) {
ff_end_tag(pb, avi->movi_list);
@@ -626,9 +738,13 @@ static int avi_write_trailer(AVFormatContext *s)
for (i = 0; i < s->nb_streams; i++) {
AVIStream *avist = s->streams[i]->priv_data;
for (j = 0; j < avist->indexes.ents_allocated / AVI_INDEX_CLUSTER_SIZE; j++)
- av_free(avist->indexes.cluster[j]);
+ av_freep(&avist->indexes.cluster[j]);
av_freep(&avist->indexes.cluster);
avist->indexes.ents_allocated = avist->indexes.entry = 0;
+ if (pb->seekable) {
+ avio_seek(pb, avist->frames_hdr_strm + 4, SEEK_SET);
+ avio_wl32(pb, avist->max_size);
+ }
}
return res;