#include "libavutil/avassert.h"
#include "libavutil/dict.h"
#include "libavutil/mathematics.h"
+#include "libavutil/parseutils.h"
+#include "libavutil/opt.h"
#include "avformat.h"
+#include "avlanguage.h"
#include "avio_internal.h"
#include "internal.h"
#include "riff.h"
ASF_PAYLOAD_REPLICATED_DATA_LENGTH + \
ASF_PAYLOAD_LENGTH_FIELD_SIZE)
-#define SINGLE_PAYLOAD_DATA_LENGTH \
- (PACKET_SIZE - \
- PACKET_HEADER_MIN_SIZE - \
+#define SINGLE_PAYLOAD_HEADERS \
+ (PACKET_HEADER_MIN_SIZE + \
PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD)
-#define MULTI_PAYLOAD_CONSTANT \
- (PACKET_SIZE - \
- PACKET_HEADER_MIN_SIZE - \
- 1 - /* Payload Flags */ \
+#define MULTI_PAYLOAD_HEADERS \
+ (PACKET_HEADER_MIN_SIZE + \
+ 1 + /* Payload Flags */ \
2 * PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS)
#define DATA_HEADER_SIZE 50
+#define PACKET_SIZE_MAX 65536
+#define PACKET_SIZE_MIN 100
+
typedef struct ASFPayload {
uint8_t type;
uint16_t size;
} ASFStream;
typedef struct ASFContext {
+ AVClass *av_class;
uint32_t seqno;
int is_streamed;
ASFStream streams[128]; ///< it's max number and it's not that big
+ const char *languages[128];
+ int nb_languages;
+ int64_t creation_time;
/* non streamed additonnal info */
uint64_t nb_packets; ///< how many packets are there in the file, invalid if broadcasting
int64_t duration; ///< in 100ns units
int64_t packet_timestamp_start;
int64_t packet_timestamp_end;
unsigned int packet_nb_payloads;
- uint8_t packet_buf[PACKET_SIZE];
+ uint8_t packet_buf[PACKET_SIZE_MAX];
AVIOContext pb;
/* only for reading */
uint64_t data_offset; ///< beginning of the first data packet
uint64_t next_packet_offset;
int next_start_sec;
int end_sec;
+ int packet_size;
} ASFContext;
static const AVCodecTag codec_asf_bmp_tags[] = {
asf->seqno++;
}
-/* convert from unix to windows time */
-static int64_t unix_to_file_time(int ti)
+/* convert from av time to windows time */
+static int64_t unix_to_file_time(int64_t ti)
{
int64_t t;
- t = ti * INT64_C(10000000);
+ t = ti * INT64_C(10);
t += INT64_C(116444736000000000);
return t;
}
ASFContext *asf = s->priv_data;
AVIOContext *pb = s->pb;
AVDictionaryEntry *tags[5];
- int header_size, n, extra_size, extra_size2, wav_extra_size, file_time;
+ int header_size, n, extra_size, extra_size2, wav_extra_size;
int has_title, has_aspect_ratio = 0;
int metadata_count;
AVCodecContext *enc;
duration = asf->duration + PREROLL_TIME * 10000;
has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4];
+
+ if (!file_size) {
+ if (ff_parse_creation_time_metadata(s, &asf->creation_time, 0) != 0)
+ av_dict_set(&s->metadata, "creation_time", NULL, 0);
+ }
+
metadata_count = av_dict_count(s->metadata);
bit_rate = 0;
for (n = 0; n < s->nb_streams; n++) {
+ AVDictionaryEntry *entry;
enc = s->streams[n]->codec;
avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */
&& enc->sample_aspect_ratio.num > 0
&& enc->sample_aspect_ratio.den > 0)
has_aspect_ratio++;
+
+ entry = av_dict_get(s->streams[n]->metadata, "language", NULL, 0);
+ if (entry) {
+ const char *iso6391lang = av_convert_lang_to(entry->value, AV_LANG_ISO639_1);
+ if (iso6391lang) {
+ int i;
+ for (i = 0; i < asf->nb_languages; i++) {
+ if (!strcmp(asf->languages[i], iso6391lang)) {
+ asf->streams[n].stream_language_index = i;
+ break;
+ }
+ }
+ if (i >= asf->nb_languages) {
+ asf->languages[asf->nb_languages] = iso6391lang;
+ asf->streams[n].stream_language_index = asf->nb_languages;
+ asf->nb_languages++;
+ }
+ }
+ } else {
+ asf->streams[n].stream_language_index = 128;
+ }
}
if (asf->is_streamed) {
hpos = put_header(pb, &ff_asf_file_header);
ff_put_guid(pb, &ff_asf_my_guid);
avio_wl64(pb, file_size);
- file_time = 0;
- avio_wl64(pb, unix_to_file_time(file_time));
+ avio_wl64(pb, unix_to_file_time(asf->creation_time));
avio_wl64(pb, asf->nb_packets); /* number of packets */
avio_wl64(pb, duration); /* end time stamp (in 100ns units) */
avio_wl64(pb, asf->duration); /* duration (in 100ns units) */
avio_wl32(pb, bit_rate ? bit_rate : -1); /* Maximum data rate in bps */
end_header(pb, hpos);
- /* unknown headers */
+ /* header_extension */
hpos = put_header(pb, &ff_asf_head1_guid);
ff_put_guid(pb, &ff_asf_head2_guid);
avio_wl16(pb, 6);
+ avio_wl32(pb, 0); /* length, to be filled later */
+ if (asf->nb_languages) {
+ int64_t hpos2;
+ int i;
+
+ hpos2 = put_header(pb, &ff_asf_language_guid);
+ avio_wl16(pb, asf->nb_languages);
+ for (i = 0; i < asf->nb_languages; i++) {
+ avio_w8(pb, 6);
+ avio_put_str16le(pb, asf->languages[i]);
+ }
+ end_header(pb, hpos2);
+
+ for (n = 0; n < s->nb_streams; n++) {
+ int64_t es_pos;
+ if (asf->streams[n].stream_language_index > 127)
+ continue;
+ es_pos = put_header(pb, &ff_asf_extended_stream_properties_object);
+ avio_wl64(pb, 0); /* start time */
+ avio_wl64(pb, 0); /* end time */
+ avio_wl32(pb, s->streams[n]->codec->bit_rate); /* data bitrate bps */
+ avio_wl32(pb, 5000); /* buffer size ms */
+ avio_wl32(pb, 0); /* initial buffer fullness */
+ avio_wl32(pb, s->streams[n]->codec->bit_rate); /* peak data bitrate */
+ avio_wl32(pb, 5000); /* maximum buffer size ms */
+ avio_wl32(pb, 0); /* max initial buffer fullness */
+ avio_wl32(pb, 0); /* max object size */
+ avio_wl32(pb, (!asf->is_streamed && pb->seekable) << 1); /* flags - seekable */
+ avio_wl16(pb, n + 1); /* stream number */
+ avio_wl16(pb, asf->streams[n].stream_language_index); /* language id index */
+ avio_wl64(pb, 0); /* avg time per frame */
+ avio_wl16(pb, 0); /* stream name count */
+ avio_wl16(pb, 0); /* payload extension system count */
+ end_header(pb, es_pos);
+ }
+ }
if (has_aspect_ratio) {
int64_t hpos2;
- avio_wl32(pb, 26 + has_aspect_ratio * 84);
hpos2 = put_header(pb, &ff_asf_metadata_header);
avio_wl16(pb, 2 * has_aspect_ratio);
for (n = 0; n < s->nb_streams; n++) {
}
}
end_header(pb, hpos2);
- } else {
- avio_wl32(pb, 0);
+ }
+ {
+ int64_t pos1;
+ pos1 = avio_tell(pb);
+ avio_seek(pb, hpos + 42, SEEK_SET);
+ avio_wl32(pb, pos1 - hpos - 46);
+ avio_seek(pb, pos1, SEEK_SET);
}
end_header(pb, hpos);
{
ASFContext *asf = s->priv_data;
- s->packet_size = PACKET_SIZE;
+ s->packet_size = asf->packet_size;
s->max_interleave_delta = 0;
asf->nb_packets = 0;
+ if (s->nb_streams > 127) {
+ av_log(s, AV_LOG_ERROR, "ASF can only handle 127 streams\n");
+ return AVERROR(EINVAL);
+ }
+
asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK);
if (!asf->index_ptr)
return AVERROR(ENOMEM);
asf->packet_nb_payloads,
asf->packet_size_left);
- packet_filled_size = PACKET_SIZE - asf->packet_size_left;
+ packet_filled_size = asf->packet_size - asf->packet_size_left;
av_assert0(packet_hdr_size <= asf->packet_size_left);
memset(asf->packet_buf + packet_filled_size, 0, asf->packet_size_left);
while (m_obj_offset < m_obj_size) {
payload_len = m_obj_size - m_obj_offset;
if (asf->packet_timestamp_start == -1) {
- asf->multi_payloads_present = (payload_len < MULTI_PAYLOAD_CONSTANT);
+ const int multi_payload_constant = (asf->packet_size - MULTI_PAYLOAD_HEADERS);
+ asf->multi_payloads_present = (payload_len < multi_payload_constant);
- asf->packet_size_left = PACKET_SIZE;
+ asf->packet_size_left = asf->packet_size;
if (asf->multi_payloads_present) {
- frag_len1 = MULTI_PAYLOAD_CONSTANT - 1;
+ frag_len1 = multi_payload_constant - 1;
} else {
- frag_len1 = SINGLE_PAYLOAD_DATA_LENGTH;
+ frag_len1 = asf->packet_size - SINGLE_PAYLOAD_HEADERS;
}
asf->packet_timestamp_start = timestamp;
} else {
return 0;
}
+static const AVOption asf_options[] = {
+ { "packet_size", "Packet size", offsetof(ASFContext, packet_size), AV_OPT_TYPE_INT, {.i64 = 3200}, PACKET_SIZE_MIN, PACKET_SIZE_MAX, AV_OPT_FLAG_ENCODING_PARAM },
+ { NULL },
+};
+
#if CONFIG_ASF_MUXER
+static const AVClass asf_muxer_class = {
+ .class_name = "ASF muxer",
+ .item_name = av_default_item_name,
+ .option = asf_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_asf_muxer = {
.name = "asf",
.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"),
.codec_tag = (const AVCodecTag * const []) {
codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0
},
+ .priv_class = &asf_muxer_class,
};
#endif /* CONFIG_ASF_MUXER */
#if CONFIG_ASF_STREAM_MUXER
+static const AVClass asf_stream_muxer_class = {
+ .class_name = "ASF stream muxer",
+ .item_name = av_default_item_name,
+ .option = asf_options,
+ .version = LIBAVUTIL_VERSION_INT,
+};
+
AVOutputFormat ff_asf_stream_muxer = {
.name = "asf_stream",
.long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"),
.codec_tag = (const AVCodecTag * const []) {
codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0
},
+ .priv_class = &asf_stream_muxer_class,
};
#endif /* CONFIG_ASF_STREAM_MUXER */