yading@11: /* yading@11: * ASF muxer yading@11: * Copyright (c) 2000, 2001 Fabrice Bellard yading@11: * yading@11: * This file is part of FFmpeg. yading@11: * yading@11: * FFmpeg is free software; you can redistribute it and/or yading@11: * modify it under the terms of the GNU Lesser General Public yading@11: * License as published by the Free Software Foundation; either yading@11: * version 2.1 of the License, or (at your option) any later version. yading@11: * yading@11: * FFmpeg is distributed in the hope that it will be useful, yading@11: * but WITHOUT ANY WARRANTY; without even the implied warranty of yading@11: * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU yading@11: * Lesser General Public License for more details. yading@11: * yading@11: * You should have received a copy of the GNU Lesser General Public yading@11: * License along with FFmpeg; if not, write to the Free Software yading@11: * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA yading@11: */ yading@11: yading@11: #include "libavutil/avassert.h" yading@11: #include "libavutil/dict.h" yading@11: #include "avformat.h" yading@11: #include "avio_internal.h" yading@11: #include "internal.h" yading@11: #include "riff.h" yading@11: #include "asf.h" yading@11: yading@11: #undef NDEBUG yading@11: #include yading@11: yading@11: yading@11: #define ASF_INDEXED_INTERVAL 10000000 yading@11: #define ASF_INDEX_BLOCK (1<<9) yading@11: yading@11: #define ASF_PACKET_ERROR_CORRECTION_DATA_SIZE 0x2 yading@11: #define ASF_PACKET_ERROR_CORRECTION_FLAGS \ yading@11: (ASF_PACKET_FLAG_ERROR_CORRECTION_PRESENT | \ yading@11: ASF_PACKET_ERROR_CORRECTION_DATA_SIZE) yading@11: yading@11: #if (ASF_PACKET_ERROR_CORRECTION_FLAGS != 0) yading@11: # define ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE 1 yading@11: #else yading@11: # define ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #define ASF_PPI_PROPERTY_FLAGS \ yading@11: (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE | \ yading@11: ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD | \ yading@11: ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE | \ yading@11: ASF_PL_FLAG_STREAM_NUMBER_LENGTH_FIELD_IS_BYTE) yading@11: yading@11: #define ASF_PPI_LENGTH_TYPE_FLAGS 0 yading@11: yading@11: #define ASF_PAYLOAD_FLAGS ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD yading@11: yading@11: #if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) yading@11: # define ASF_PPI_SEQUENCE_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) yading@11: # define ASF_PPI_SEQUENCE_FIELD_SIZE 2 yading@11: #endif yading@11: #if (ASF_PPI_FLAG_SEQUENCE_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_SEQUENCE_FIELD_SIZE)) yading@11: # define ASF_PPI_SEQUENCE_FIELD_SIZE 4 yading@11: #endif yading@11: #ifndef ASF_PPI_SEQUENCE_FIELD_SIZE yading@11: # define ASF_PPI_SEQUENCE_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 2 yading@11: #endif yading@11: #if (ASF_PPI_FLAG_PACKET_LENGTH_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PACKET_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 4 yading@11: #endif yading@11: #ifndef ASF_PPI_PACKET_LENGTH_FIELD_SIZE yading@11: # define ASF_PPI_PACKET_LENGTH_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 2 yading@11: #endif yading@11: #if (ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_DWORD == (ASF_PPI_LENGTH_TYPE_FLAGS & ASF_PPI_MASK_PADDING_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 4 yading@11: #endif yading@11: #ifndef ASF_PPI_PADDING_LENGTH_FIELD_SIZE yading@11: # define ASF_PPI_PADDING_LENGTH_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 2 yading@11: #endif yading@11: #if (ASF_PL_FLAG_REPLICATED_DATA_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_REPLICATED_DATA_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 4 yading@11: #endif yading@11: #ifndef ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE yading@11: # define ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 2 yading@11: #endif yading@11: #if (ASF_PL_FLAG_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_OFFSET_INTO_MEDIA_OBJECT_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 4 yading@11: #endif yading@11: #ifndef ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE yading@11: # define ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_BYTE == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_WORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 2 yading@11: #endif yading@11: #if (ASF_PL_FLAG_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_IS_DWORD == (ASF_PPI_PROPERTY_FLAGS & ASF_PL_MASK_MEDIA_OBJECT_NUMBER_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 4 yading@11: #endif yading@11: #ifndef ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE yading@11: # define ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #if (ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_BYTE == (ASF_PAYLOAD_FLAGS & ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_LENGTH_FIELD_SIZE 1 yading@11: #endif yading@11: #if (ASF_PL_FLAG_PAYLOAD_LENGTH_FIELD_IS_WORD == (ASF_PAYLOAD_FLAGS & ASF_PL_MASK_PAYLOAD_LENGTH_FIELD_SIZE)) yading@11: # define ASF_PAYLOAD_LENGTH_FIELD_SIZE 2 yading@11: #endif yading@11: #ifndef ASF_PAYLOAD_LENGTH_FIELD_SIZE yading@11: # define ASF_PAYLOAD_LENGTH_FIELD_SIZE 0 yading@11: #endif yading@11: yading@11: #define PACKET_HEADER_MIN_SIZE \ yading@11: (ASF_PACKET_ERROR_CORRECTION_FLAGS_FIELD_SIZE + \ yading@11: ASF_PACKET_ERROR_CORRECTION_DATA_SIZE + \ yading@11: 1 + /* Length Type Flags */ \ yading@11: 1 + /* Property Flags */ \ yading@11: ASF_PPI_PACKET_LENGTH_FIELD_SIZE + \ yading@11: ASF_PPI_SEQUENCE_FIELD_SIZE + \ yading@11: ASF_PPI_PADDING_LENGTH_FIELD_SIZE + \ yading@11: 4 + /* Send Time Field */ \ yading@11: 2) /* Duration Field */ yading@11: yading@11: // Replicated Data shall be at least 8 bytes long. yading@11: #define ASF_PAYLOAD_REPLICATED_DATA_LENGTH 0x08 yading@11: yading@11: #define PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD \ yading@11: (1 + /* Stream Number */ \ yading@11: ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE + \ yading@11: ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE + \ yading@11: ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE + \ yading@11: ASF_PAYLOAD_REPLICATED_DATA_LENGTH) yading@11: yading@11: #define PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS \ yading@11: (1 + /* Stream Number */ \ yading@11: ASF_PAYLOAD_MEDIA_OBJECT_NUMBER_FIELD_SIZE + \ yading@11: ASF_PAYLOAD_OFFSET_INTO_MEDIA_OBJECT_FIELD_SIZE + \ yading@11: ASF_PAYLOAD_REPLICATED_DATA_LENGTH_FIELD_SIZE + \ yading@11: ASF_PAYLOAD_REPLICATED_DATA_LENGTH + \ yading@11: ASF_PAYLOAD_LENGTH_FIELD_SIZE) yading@11: yading@11: #define SINGLE_PAYLOAD_DATA_LENGTH \ yading@11: (PACKET_SIZE - \ yading@11: PACKET_HEADER_MIN_SIZE - \ yading@11: PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD) yading@11: yading@11: #define MULTI_PAYLOAD_CONSTANT \ yading@11: (PACKET_SIZE - \ yading@11: PACKET_HEADER_MIN_SIZE - \ yading@11: 1 - /* Payload Flags */ \ yading@11: 2 * PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS) yading@11: yading@11: typedef struct { yading@11: uint32_t seqno; yading@11: int is_streamed; yading@11: ASFStream streams[128]; ///< it's max number and it's not that big yading@11: /* non streamed additonnal info */ yading@11: uint64_t nb_packets; ///< how many packets are there in the file, invalid if broadcasting yading@11: int64_t duration; ///< in 100ns units yading@11: /* packet filling */ yading@11: unsigned char multi_payloads_present; yading@11: int packet_size_left; yading@11: int64_t packet_timestamp_start; yading@11: int64_t packet_timestamp_end; yading@11: unsigned int packet_nb_payloads; yading@11: uint8_t packet_buf[PACKET_SIZE]; yading@11: AVIOContext pb; yading@11: /* only for reading */ yading@11: uint64_t data_offset; ///< beginning of the first data packet yading@11: yading@11: ASFIndex *index_ptr; yading@11: uint32_t nb_index_memory_alloc; yading@11: uint16_t maximum_packet; yading@11: uint32_t next_packet_number; yading@11: uint16_t next_packet_count; yading@11: int next_start_sec; yading@11: int end_sec; yading@11: } ASFContext; yading@11: yading@11: static const AVCodecTag codec_asf_bmp_tags[] = { yading@11: { AV_CODEC_ID_MPEG4, MKTAG('M', '4', 'S', '2') }, yading@11: { AV_CODEC_ID_MPEG4, MKTAG('M', 'P', '4', 'S') }, yading@11: { AV_CODEC_ID_MSMPEG4V3, MKTAG('M', 'P', '4', '3') }, yading@11: { AV_CODEC_ID_NONE, 0 }, yading@11: }; yading@11: yading@11: #define PREROLL_TIME 3100 yading@11: yading@11: void ff_put_guid(AVIOContext *s, const ff_asf_guid *g) yading@11: { yading@11: av_assert0(sizeof(*g) == 16); yading@11: avio_write(s, *g, sizeof(*g)); yading@11: } yading@11: yading@11: static void put_str16(AVIOContext *s, const char *tag) yading@11: { yading@11: int len; yading@11: uint8_t *pb; yading@11: AVIOContext *dyn_buf; yading@11: if (avio_open_dyn_buf(&dyn_buf) < 0) yading@11: return; yading@11: yading@11: avio_put_str16le(dyn_buf, tag); yading@11: len = avio_close_dyn_buf(dyn_buf, &pb); yading@11: avio_wl16(s, len); yading@11: avio_write(s, pb, len); yading@11: av_freep(&pb); yading@11: } yading@11: yading@11: static int64_t put_header(AVIOContext *pb, const ff_asf_guid *g) yading@11: { yading@11: int64_t pos; yading@11: yading@11: pos = avio_tell(pb); yading@11: ff_put_guid(pb, g); yading@11: avio_wl64(pb, 24); yading@11: return pos; yading@11: } yading@11: yading@11: /* update header size */ yading@11: static void end_header(AVIOContext *pb, int64_t pos) yading@11: { yading@11: int64_t pos1; yading@11: yading@11: pos1 = avio_tell(pb); yading@11: avio_seek(pb, pos + 16, SEEK_SET); yading@11: avio_wl64(pb, pos1 - pos); yading@11: avio_seek(pb, pos1, SEEK_SET); yading@11: } yading@11: yading@11: /* write an asf chunk (only used in streaming case) */ yading@11: static void put_chunk(AVFormatContext *s, int type, yading@11: int payload_length, int flags) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: int length; yading@11: yading@11: length = payload_length + 8; yading@11: avio_wl16(pb, type); yading@11: avio_wl16(pb, length); // size yading@11: avio_wl32(pb, asf->seqno); // sequence number yading@11: avio_wl16(pb, flags); // unknown bytes yading@11: avio_wl16(pb, length); // size_confirm yading@11: asf->seqno++; yading@11: } yading@11: yading@11: /* convert from unix to windows time */ yading@11: static int64_t unix_to_file_time(int ti) yading@11: { yading@11: int64_t t; yading@11: yading@11: t = ti * INT64_C(10000000); yading@11: t += INT64_C(116444736000000000); yading@11: return t; yading@11: } yading@11: yading@11: /* write the header (used two times if non streamed) */ yading@11: static int asf_write_header1(AVFormatContext *s, int64_t file_size, yading@11: int64_t data_chunk_size) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: AVDictionaryEntry *tags[5]; yading@11: int header_size, n, extra_size, extra_size2, wav_extra_size, file_time; yading@11: int has_title; yading@11: int metadata_count; yading@11: AVCodecContext *enc; yading@11: int64_t header_offset, cur_pos, hpos; yading@11: int bit_rate; yading@11: int64_t duration; yading@11: yading@11: ff_metadata_conv(&s->metadata, ff_asf_metadata_conv, NULL); yading@11: yading@11: tags[0] = av_dict_get(s->metadata, "title", NULL, 0); yading@11: tags[1] = av_dict_get(s->metadata, "author", NULL, 0); yading@11: tags[2] = av_dict_get(s->metadata, "copyright", NULL, 0); yading@11: tags[3] = av_dict_get(s->metadata, "comment", NULL, 0); yading@11: tags[4] = av_dict_get(s->metadata, "rating", NULL, 0); yading@11: yading@11: duration = asf->duration + PREROLL_TIME * 10000; yading@11: has_title = tags[0] || tags[1] || tags[2] || tags[3] || tags[4]; yading@11: metadata_count = av_dict_count(s->metadata); yading@11: yading@11: bit_rate = 0; yading@11: for (n = 0; n < s->nb_streams; n++) { yading@11: enc = s->streams[n]->codec; yading@11: yading@11: avpriv_set_pts_info(s->streams[n], 32, 1, 1000); /* 32 bit pts in ms */ yading@11: yading@11: bit_rate += enc->bit_rate; yading@11: } yading@11: yading@11: if (asf->is_streamed) { yading@11: put_chunk(s, 0x4824, 0, 0xc00); /* start of stream (length will be patched later) */ yading@11: } yading@11: yading@11: ff_put_guid(pb, &ff_asf_header); yading@11: avio_wl64(pb, -1); /* header length, will be patched after */ yading@11: avio_wl32(pb, 3 + has_title + !!metadata_count + s->nb_streams); /* number of chunks in header */ yading@11: avio_w8(pb, 1); /* ??? */ yading@11: avio_w8(pb, 2); /* ??? */ yading@11: yading@11: /* file header */ yading@11: header_offset = avio_tell(pb); yading@11: hpos = put_header(pb, &ff_asf_file_header); yading@11: ff_put_guid(pb, &ff_asf_my_guid); yading@11: avio_wl64(pb, file_size); yading@11: file_time = 0; yading@11: avio_wl64(pb, unix_to_file_time(file_time)); yading@11: avio_wl64(pb, asf->nb_packets); /* number of packets */ yading@11: avio_wl64(pb, duration); /* end time stamp (in 100ns units) */ yading@11: avio_wl64(pb, asf->duration); /* duration (in 100ns units) */ yading@11: avio_wl64(pb, PREROLL_TIME); /* start time stamp */ yading@11: avio_wl32(pb, (asf->is_streamed || !pb->seekable) ? 3 : 2); /* ??? */ yading@11: avio_wl32(pb, s->packet_size); /* packet size */ yading@11: avio_wl32(pb, s->packet_size); /* packet size */ yading@11: avio_wl32(pb, bit_rate); /* Nominal data rate in bps */ yading@11: end_header(pb, hpos); yading@11: yading@11: /* unknown headers */ yading@11: hpos = put_header(pb, &ff_asf_head1_guid); yading@11: ff_put_guid(pb, &ff_asf_head2_guid); yading@11: avio_wl32(pb, 6); yading@11: avio_wl16(pb, 0); yading@11: end_header(pb, hpos); yading@11: yading@11: /* title and other infos */ yading@11: if (has_title) { yading@11: int len; yading@11: uint8_t *buf; yading@11: AVIOContext *dyn_buf; yading@11: yading@11: if (avio_open_dyn_buf(&dyn_buf) < 0) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: hpos = put_header(pb, &ff_asf_comment_header); yading@11: yading@11: for (n = 0; n < FF_ARRAY_ELEMS(tags); n++) { yading@11: len = tags[n] ? avio_put_str16le(dyn_buf, tags[n]->value) : 0; yading@11: avio_wl16(pb, len); yading@11: } yading@11: len = avio_close_dyn_buf(dyn_buf, &buf); yading@11: avio_write(pb, buf, len); yading@11: av_freep(&buf); yading@11: end_header(pb, hpos); yading@11: } yading@11: if (metadata_count) { yading@11: AVDictionaryEntry *tag = NULL; yading@11: hpos = put_header(pb, &ff_asf_extended_content_header); yading@11: avio_wl16(pb, metadata_count); yading@11: while ((tag = av_dict_get(s->metadata, "", tag, AV_DICT_IGNORE_SUFFIX))) { yading@11: put_str16(pb, tag->key); yading@11: avio_wl16(pb, 0); yading@11: put_str16(pb, tag->value); yading@11: } yading@11: end_header(pb, hpos); yading@11: } yading@11: yading@11: /* stream headers */ yading@11: for (n = 0; n < s->nb_streams; n++) { yading@11: int64_t es_pos; yading@11: // ASFStream *stream = &asf->streams[n]; yading@11: yading@11: enc = s->streams[n]->codec; yading@11: asf->streams[n].num = n + 1; yading@11: asf->streams[n].seq = 1; yading@11: yading@11: switch (enc->codec_type) { yading@11: case AVMEDIA_TYPE_AUDIO: yading@11: wav_extra_size = 0; yading@11: extra_size = 18 + wav_extra_size; yading@11: extra_size2 = 8; yading@11: break; yading@11: default: yading@11: case AVMEDIA_TYPE_VIDEO: yading@11: wav_extra_size = enc->extradata_size; yading@11: extra_size = 0x33 + wav_extra_size; yading@11: extra_size2 = 0; yading@11: break; yading@11: } yading@11: yading@11: hpos = put_header(pb, &ff_asf_stream_header); yading@11: if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { yading@11: ff_put_guid(pb, &ff_asf_audio_stream); yading@11: ff_put_guid(pb, &ff_asf_audio_conceal_spread); yading@11: } else { yading@11: ff_put_guid(pb, &ff_asf_video_stream); yading@11: ff_put_guid(pb, &ff_asf_video_conceal_none); yading@11: } yading@11: avio_wl64(pb, 0); /* ??? */ yading@11: es_pos = avio_tell(pb); yading@11: avio_wl32(pb, extra_size); /* wav header len */ yading@11: avio_wl32(pb, extra_size2); /* additional data len */ yading@11: avio_wl16(pb, n + 1); /* stream number */ yading@11: avio_wl32(pb, 0); /* ??? */ yading@11: yading@11: if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { yading@11: /* WAVEFORMATEX header */ yading@11: int wavsize = ff_put_wav_header(pb, enc); yading@11: yading@11: if (wavsize < 0) yading@11: return -1; yading@11: if (wavsize != extra_size) { yading@11: cur_pos = avio_tell(pb); yading@11: avio_seek(pb, es_pos, SEEK_SET); yading@11: avio_wl32(pb, wavsize); /* wav header len */ yading@11: avio_seek(pb, cur_pos, SEEK_SET); yading@11: } yading@11: /* ERROR Correction */ yading@11: avio_w8(pb, 0x01); yading@11: if (enc->codec_id == AV_CODEC_ID_ADPCM_G726 || !enc->block_align) { yading@11: avio_wl16(pb, 0x0190); yading@11: avio_wl16(pb, 0x0190); yading@11: } else { yading@11: avio_wl16(pb, enc->block_align); yading@11: avio_wl16(pb, enc->block_align); yading@11: } yading@11: avio_wl16(pb, 0x01); yading@11: avio_w8(pb, 0x00); yading@11: } else { yading@11: avio_wl32(pb, enc->width); yading@11: avio_wl32(pb, enc->height); yading@11: avio_w8(pb, 2); /* ??? */ yading@11: avio_wl16(pb, 40 + enc->extradata_size); /* size */ yading@11: yading@11: /* BITMAPINFOHEADER header */ yading@11: ff_put_bmp_header(pb, enc, ff_codec_bmp_tags, 1); yading@11: } yading@11: end_header(pb, hpos); yading@11: } yading@11: yading@11: /* media comments */ yading@11: yading@11: hpos = put_header(pb, &ff_asf_codec_comment_header); yading@11: ff_put_guid(pb, &ff_asf_codec_comment1_header); yading@11: avio_wl32(pb, s->nb_streams); yading@11: for (n = 0; n < s->nb_streams; n++) { yading@11: AVCodec *p; yading@11: const char *desc; yading@11: int len; yading@11: uint8_t *buf; yading@11: AVIOContext *dyn_buf; yading@11: yading@11: enc = s->streams[n]->codec; yading@11: p = avcodec_find_encoder(enc->codec_id); yading@11: yading@11: if (enc->codec_type == AVMEDIA_TYPE_AUDIO) yading@11: avio_wl16(pb, 2); yading@11: else if (enc->codec_type == AVMEDIA_TYPE_VIDEO) yading@11: avio_wl16(pb, 1); yading@11: else yading@11: avio_wl16(pb, -1); yading@11: yading@11: if (enc->codec_id == AV_CODEC_ID_WMAV2) yading@11: desc = "Windows Media Audio V8"; yading@11: else yading@11: desc = p ? p->name : enc->codec_name; yading@11: yading@11: if (avio_open_dyn_buf(&dyn_buf) < 0) yading@11: return AVERROR(ENOMEM); yading@11: yading@11: avio_put_str16le(dyn_buf, desc); yading@11: len = avio_close_dyn_buf(dyn_buf, &buf); yading@11: avio_wl16(pb, len / 2); // "number of characters" = length in bytes / 2 yading@11: yading@11: avio_write(pb, buf, len); yading@11: av_freep(&buf); yading@11: yading@11: avio_wl16(pb, 0); /* no parameters */ yading@11: yading@11: /* id */ yading@11: if (enc->codec_type == AVMEDIA_TYPE_AUDIO) { yading@11: avio_wl16(pb, 2); yading@11: avio_wl16(pb, enc->codec_tag); yading@11: } else { yading@11: avio_wl16(pb, 4); yading@11: avio_wl32(pb, enc->codec_tag); yading@11: } yading@11: if (!enc->codec_tag) yading@11: return -1; yading@11: } yading@11: end_header(pb, hpos); yading@11: yading@11: /* patch the header size fields */ yading@11: yading@11: cur_pos = avio_tell(pb); yading@11: header_size = cur_pos - header_offset; yading@11: if (asf->is_streamed) { yading@11: header_size += 8 + 30 + 50; yading@11: yading@11: avio_seek(pb, header_offset - 10 - 30, SEEK_SET); yading@11: avio_wl16(pb, header_size); yading@11: avio_seek(pb, header_offset - 2 - 30, SEEK_SET); yading@11: avio_wl16(pb, header_size); yading@11: yading@11: header_size -= 8 + 30 + 50; yading@11: } yading@11: header_size += 24 + 6; yading@11: avio_seek(pb, header_offset - 14, SEEK_SET); yading@11: avio_wl64(pb, header_size); yading@11: avio_seek(pb, cur_pos, SEEK_SET); yading@11: yading@11: /* movie chunk, followed by packets of packet_size */ yading@11: asf->data_offset = cur_pos; yading@11: ff_put_guid(pb, &ff_asf_data_header); yading@11: avio_wl64(pb, data_chunk_size); yading@11: ff_put_guid(pb, &ff_asf_my_guid); yading@11: avio_wl64(pb, asf->nb_packets); /* nb packets */ yading@11: avio_w8(pb, 1); /* ??? */ yading@11: avio_w8(pb, 1); /* ??? */ yading@11: return 0; yading@11: } yading@11: yading@11: static int asf_write_header(AVFormatContext *s) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: yading@11: s->packet_size = PACKET_SIZE; yading@11: asf->nb_packets = 0; yading@11: yading@11: asf->index_ptr = av_malloc(sizeof(ASFIndex) * ASF_INDEX_BLOCK); yading@11: asf->nb_index_memory_alloc = ASF_INDEX_BLOCK; yading@11: asf->maximum_packet = 0; yading@11: yading@11: /* the data-chunk-size has to be 50, which is data_size - asf->data_offset yading@11: * at the moment this function is done. It is needed to use asf as yading@11: * streamable format. */ yading@11: if (asf_write_header1(s, 0, 50) < 0) { yading@11: //av_free(asf); yading@11: return -1; yading@11: } yading@11: yading@11: avio_flush(s->pb); yading@11: yading@11: asf->packet_nb_payloads = 0; yading@11: asf->packet_timestamp_start = -1; yading@11: asf->packet_timestamp_end = -1; yading@11: ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, yading@11: NULL, NULL, NULL, NULL); yading@11: yading@11: if (s->avoid_negative_ts < 0) yading@11: s->avoid_negative_ts = 1; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int asf_write_stream_header(AVFormatContext *s) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: yading@11: asf->is_streamed = 1; yading@11: yading@11: return asf_write_header(s); yading@11: } yading@11: yading@11: static int put_payload_parsing_info(AVFormatContext *s, yading@11: unsigned sendtime, unsigned duration, yading@11: int nb_payloads, int padsize) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: AVIOContext *pb = s->pb; yading@11: int ppi_size, i; yading@11: int64_t start = avio_tell(pb); yading@11: yading@11: int iLengthTypeFlags = ASF_PPI_LENGTH_TYPE_FLAGS; yading@11: yading@11: padsize -= PACKET_HEADER_MIN_SIZE; yading@11: if (asf->multi_payloads_present) yading@11: padsize--; yading@11: av_assert0(padsize >= 0); yading@11: yading@11: avio_w8(pb, ASF_PACKET_ERROR_CORRECTION_FLAGS); yading@11: for (i = 0; i < ASF_PACKET_ERROR_CORRECTION_DATA_SIZE; i++) yading@11: avio_w8(pb, 0x0); yading@11: yading@11: if (asf->multi_payloads_present) yading@11: iLengthTypeFlags |= ASF_PPI_FLAG_MULTIPLE_PAYLOADS_PRESENT; yading@11: yading@11: if (padsize > 0) { yading@11: if (padsize < 256) yading@11: iLengthTypeFlags |= ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE; yading@11: else yading@11: iLengthTypeFlags |= ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD; yading@11: } yading@11: avio_w8(pb, iLengthTypeFlags); yading@11: yading@11: avio_w8(pb, ASF_PPI_PROPERTY_FLAGS); yading@11: yading@11: if (iLengthTypeFlags & ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_WORD) yading@11: avio_wl16(pb, padsize - 2); yading@11: if (iLengthTypeFlags & ASF_PPI_FLAG_PADDING_LENGTH_FIELD_IS_BYTE) yading@11: avio_w8(pb, padsize - 1); yading@11: yading@11: avio_wl32(pb, sendtime); yading@11: avio_wl16(pb, duration); yading@11: if (asf->multi_payloads_present) yading@11: avio_w8(pb, nb_payloads | ASF_PAYLOAD_FLAGS); yading@11: yading@11: ppi_size = avio_tell(pb) - start; yading@11: yading@11: return ppi_size; yading@11: } yading@11: yading@11: static void flush_packet(AVFormatContext *s) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: int packet_hdr_size, packet_filled_size; yading@11: yading@11: av_assert0(asf->packet_timestamp_end >= asf->packet_timestamp_start); yading@11: yading@11: if (asf->is_streamed) yading@11: put_chunk(s, 0x4424, s->packet_size, 0); yading@11: yading@11: packet_hdr_size = put_payload_parsing_info(s, yading@11: asf->packet_timestamp_start, yading@11: asf->packet_timestamp_end - asf->packet_timestamp_start, yading@11: asf->packet_nb_payloads, yading@11: asf->packet_size_left); yading@11: yading@11: packet_filled_size = PACKET_SIZE - asf->packet_size_left; yading@11: av_assert0(packet_hdr_size <= asf->packet_size_left); yading@11: memset(asf->packet_buf + packet_filled_size, 0, asf->packet_size_left); yading@11: yading@11: avio_write(s->pb, asf->packet_buf, s->packet_size - packet_hdr_size); yading@11: yading@11: avio_flush(s->pb); yading@11: asf->nb_packets++; yading@11: asf->packet_nb_payloads = 0; yading@11: asf->packet_timestamp_start = -1; yading@11: asf->packet_timestamp_end = -1; yading@11: ffio_init_context(&asf->pb, asf->packet_buf, s->packet_size, 1, yading@11: NULL, NULL, NULL, NULL); yading@11: } yading@11: yading@11: static void put_payload_header(AVFormatContext *s, ASFStream *stream, yading@11: int64_t presentation_time, int m_obj_size, yading@11: int m_obj_offset, int payload_len, int flags) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: AVIOContext *pb = &asf->pb; yading@11: int val; yading@11: yading@11: val = stream->num; yading@11: if (flags & AV_PKT_FLAG_KEY) yading@11: val |= ASF_PL_FLAG_KEY_FRAME; yading@11: avio_w8(pb, val); yading@11: yading@11: avio_w8(pb, stream->seq); // Media object number yading@11: avio_wl32(pb, m_obj_offset); // Offset Into Media Object yading@11: yading@11: // Replicated Data shall be at least 8 bytes long. yading@11: // The first 4 bytes of data shall contain the yading@11: // Size of the Media Object that the payload belongs to. yading@11: // The next 4 bytes of data shall contain the yading@11: // Presentation Time for the media object that the payload belongs to. yading@11: avio_w8(pb, ASF_PAYLOAD_REPLICATED_DATA_LENGTH); yading@11: yading@11: avio_wl32(pb, m_obj_size); // Replicated Data - Media Object Size yading@11: avio_wl32(pb, (uint32_t) presentation_time); // Replicated Data - Presentation Time yading@11: yading@11: if (asf->multi_payloads_present) { yading@11: avio_wl16(pb, payload_len); // payload length yading@11: } yading@11: } yading@11: yading@11: static void put_frame(AVFormatContext *s, ASFStream *stream, AVStream *avst, yading@11: int64_t timestamp, const uint8_t *buf, yading@11: int m_obj_size, int flags) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: int m_obj_offset, payload_len, frag_len1; yading@11: yading@11: m_obj_offset = 0; yading@11: while (m_obj_offset < m_obj_size) { yading@11: payload_len = m_obj_size - m_obj_offset; yading@11: if (asf->packet_timestamp_start == -1) { yading@11: asf->multi_payloads_present = (payload_len < MULTI_PAYLOAD_CONSTANT); yading@11: yading@11: asf->packet_size_left = PACKET_SIZE; yading@11: if (asf->multi_payloads_present) { yading@11: frag_len1 = MULTI_PAYLOAD_CONSTANT - 1; yading@11: } else { yading@11: frag_len1 = SINGLE_PAYLOAD_DATA_LENGTH; yading@11: } yading@11: asf->packet_timestamp_start = timestamp; yading@11: } else { yading@11: // multi payloads yading@11: frag_len1 = asf->packet_size_left - yading@11: PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS - yading@11: PACKET_HEADER_MIN_SIZE - 1; yading@11: yading@11: if (frag_len1 < payload_len && yading@11: avst->codec->codec_type == AVMEDIA_TYPE_AUDIO) { yading@11: flush_packet(s); yading@11: continue; yading@11: } yading@11: } yading@11: if (frag_len1 > 0) { yading@11: if (payload_len > frag_len1) yading@11: payload_len = frag_len1; yading@11: else if (payload_len == (frag_len1 - 1)) yading@11: payload_len = frag_len1 - 2; // additional byte need to put padding length yading@11: yading@11: put_payload_header(s, stream, timestamp + PREROLL_TIME, yading@11: m_obj_size, m_obj_offset, payload_len, flags); yading@11: avio_write(&asf->pb, buf, payload_len); yading@11: yading@11: if (asf->multi_payloads_present) yading@11: asf->packet_size_left -= (payload_len + PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS); yading@11: else yading@11: asf->packet_size_left -= (payload_len + PAYLOAD_HEADER_SIZE_SINGLE_PAYLOAD); yading@11: asf->packet_timestamp_end = timestamp; yading@11: yading@11: asf->packet_nb_payloads++; yading@11: } else { yading@11: payload_len = 0; yading@11: } yading@11: m_obj_offset += payload_len; yading@11: buf += payload_len; yading@11: yading@11: if (!asf->multi_payloads_present) yading@11: flush_packet(s); yading@11: else if (asf->packet_size_left <= (PAYLOAD_HEADER_SIZE_MULTIPLE_PAYLOADS + PACKET_HEADER_MIN_SIZE + 1)) yading@11: flush_packet(s); yading@11: } yading@11: stream->seq++; yading@11: } yading@11: yading@11: static void update_index(AVFormatContext *s, int start_sec, yading@11: uint32_t packet_number, uint16_t packet_count) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: yading@11: if (start_sec > asf->next_start_sec) { yading@11: int i; yading@11: yading@11: if (!asf->next_start_sec) { yading@11: asf->next_packet_number = packet_number; yading@11: asf->next_packet_count = packet_count; yading@11: } yading@11: yading@11: if (start_sec > asf->nb_index_memory_alloc) { yading@11: asf->nb_index_memory_alloc = (start_sec + ASF_INDEX_BLOCK) & ~(ASF_INDEX_BLOCK - 1); yading@11: asf->index_ptr = av_realloc( asf->index_ptr, sizeof(ASFIndex) * asf->nb_index_memory_alloc ); yading@11: } yading@11: for (i = asf->next_start_sec; i < start_sec; i++) { yading@11: asf->index_ptr[i].packet_number = asf->next_packet_number; yading@11: asf->index_ptr[i].packet_count = asf->next_packet_count; yading@11: } yading@11: } yading@11: asf->maximum_packet = FFMAX(asf->maximum_packet, packet_count); yading@11: asf->next_packet_number = packet_number; yading@11: asf->next_packet_count = packet_count; yading@11: asf->next_start_sec = start_sec; yading@11: } yading@11: yading@11: static int asf_write_packet(AVFormatContext *s, AVPacket *pkt) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: ASFStream *stream; yading@11: AVCodecContext *codec; yading@11: uint32_t packet_number; yading@11: int64_t pts; yading@11: int start_sec; yading@11: int flags = pkt->flags; yading@11: yading@11: codec = s->streams[pkt->stream_index]->codec; yading@11: stream = &asf->streams[pkt->stream_index]; yading@11: yading@11: if (codec->codec_type == AVMEDIA_TYPE_AUDIO) yading@11: flags &= ~AV_PKT_FLAG_KEY; yading@11: yading@11: pts = (pkt->pts != AV_NOPTS_VALUE) ? pkt->pts : pkt->dts; yading@11: av_assert0(pts != AV_NOPTS_VALUE); yading@11: pts *= 10000; yading@11: asf->duration = FFMAX(asf->duration, pts + pkt->duration * 10000); yading@11: yading@11: packet_number = asf->nb_packets; yading@11: put_frame(s, stream, s->streams[pkt->stream_index], yading@11: pkt->dts, pkt->data, pkt->size, flags); yading@11: yading@11: start_sec = (int)((PREROLL_TIME * 10000 + pts + ASF_INDEXED_INTERVAL - 1) yading@11: / ASF_INDEXED_INTERVAL); yading@11: yading@11: /* check index */ yading@11: if ((!asf->is_streamed) && (flags & AV_PKT_FLAG_KEY)) { yading@11: uint16_t packet_count = asf->nb_packets - packet_number; yading@11: update_index(s, start_sec, packet_number, packet_count); yading@11: } yading@11: asf->end_sec = start_sec; yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int asf_write_index(AVFormatContext *s, ASFIndex *index, yading@11: uint16_t max, uint32_t count) yading@11: { yading@11: AVIOContext *pb = s->pb; yading@11: int i; yading@11: yading@11: ff_put_guid(pb, &ff_asf_simple_index_header); yading@11: avio_wl64(pb, 24 + 16 + 8 + 4 + 4 + (4 + 2) * count); yading@11: ff_put_guid(pb, &ff_asf_my_guid); yading@11: avio_wl64(pb, ASF_INDEXED_INTERVAL); yading@11: avio_wl32(pb, max); yading@11: avio_wl32(pb, count); yading@11: for (i = 0; i < count; i++) { yading@11: avio_wl32(pb, index[i].packet_number); yading@11: avio_wl16(pb, index[i].packet_count); yading@11: } yading@11: yading@11: return 0; yading@11: } yading@11: yading@11: static int asf_write_trailer(AVFormatContext *s) yading@11: { yading@11: ASFContext *asf = s->priv_data; yading@11: int64_t file_size, data_size; yading@11: yading@11: /* flush the current packet */ yading@11: if (asf->pb.buf_ptr > asf->pb.buffer) yading@11: flush_packet(s); yading@11: yading@11: /* write index */ yading@11: data_size = avio_tell(s->pb); yading@11: if (!asf->is_streamed && asf->next_start_sec) { yading@11: update_index(s, asf->end_sec + 1, 0, 0); yading@11: asf_write_index(s, asf->index_ptr, asf->maximum_packet, asf->next_start_sec); yading@11: } yading@11: avio_flush(s->pb); yading@11: yading@11: if (asf->is_streamed || !s->pb->seekable) { yading@11: put_chunk(s, 0x4524, 0, 0); /* end of stream */ yading@11: } else { yading@11: /* rewrite an updated header */ yading@11: file_size = avio_tell(s->pb); yading@11: avio_seek(s->pb, 0, SEEK_SET); yading@11: asf_write_header1(s, file_size, data_size - asf->data_offset); yading@11: } yading@11: yading@11: av_free(asf->index_ptr); yading@11: return 0; yading@11: } yading@11: yading@11: #if CONFIG_ASF_MUXER yading@11: AVOutputFormat ff_asf_muxer = { yading@11: .name = "asf", yading@11: .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), yading@11: .mime_type = "video/x-ms-asf", yading@11: .extensions = "asf,wmv,wma", yading@11: .priv_data_size = sizeof(ASFContext), yading@11: .audio_codec = AV_CODEC_ID_WMAV2, yading@11: .video_codec = AV_CODEC_ID_MSMPEG4V3, yading@11: .write_header = asf_write_header, yading@11: .write_packet = asf_write_packet, yading@11: .write_trailer = asf_write_trailer, yading@11: .flags = AVFMT_GLOBALHEADER, yading@11: .codec_tag = (const AVCodecTag * const []) { yading@11: codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0 yading@11: }, yading@11: }; yading@11: #endif /* CONFIG_ASF_MUXER */ yading@11: yading@11: #if CONFIG_ASF_STREAM_MUXER yading@11: AVOutputFormat ff_asf_stream_muxer = { yading@11: .name = "asf_stream", yading@11: .long_name = NULL_IF_CONFIG_SMALL("ASF (Advanced / Active Streaming Format)"), yading@11: .mime_type = "video/x-ms-asf", yading@11: .extensions = "asf,wmv,wma", yading@11: .priv_data_size = sizeof(ASFContext), yading@11: .audio_codec = AV_CODEC_ID_WMAV2, yading@11: .video_codec = AV_CODEC_ID_MSMPEG4V3, yading@11: .write_header = asf_write_stream_header, yading@11: .write_packet = asf_write_packet, yading@11: .write_trailer = asf_write_trailer, yading@11: .flags = AVFMT_GLOBALHEADER, yading@11: .codec_tag = (const AVCodecTag * const []) { yading@11: codec_asf_bmp_tags, ff_codec_bmp_tags, ff_codec_wav_tags, 0 yading@11: }, yading@11: }; yading@11: #endif /* CONFIG_ASF_STREAM_MUXER */