diff options
Diffstat (limited to 'libavformat/xmv.c')
-rw-r--r-- | libavformat/xmv.c | 289 |
1 files changed, 150 insertions, 139 deletions
diff --git a/libavformat/xmv.c b/libavformat/xmv.c index b2112b0e95..b85d0ccc43 100644 --- a/libavformat/xmv.c +++ b/libavformat/xmv.c @@ -3,20 +3,20 @@ * Copyright (c) 2011 Sven Hesse <drmccoy@drmccoy.de> * Copyright (c) 2011 Matthew Hoops <clone2727@gmail.com> * - * 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 */ @@ -32,83 +32,87 @@ #include "avformat.h" #include "internal.h" #include "riff.h" +#include "libavutil/avassert.h" +/** The min size of an XMV header. */ #define XMV_MIN_HEADER_SIZE 36 +/** Audio flag: ADPCM'd 5.1 stream, front left / right channels */ #define XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT 1 +/** Audio flag: ADPCM'd 5.1 stream, front center / low frequency channels */ #define XMV_AUDIO_ADPCM51_FRONTCENTERLOW 2 +/** Audio flag: ADPCM'd 5.1 stream, rear left / right channels */ #define XMV_AUDIO_ADPCM51_REARLEFTRIGHT 4 +/** Audio flag: Any of the ADPCM'd 5.1 stream flags. */ #define XMV_AUDIO_ADPCM51 (XMV_AUDIO_ADPCM51_FRONTLEFTRIGHT | \ XMV_AUDIO_ADPCM51_FRONTCENTERLOW | \ XMV_AUDIO_ADPCM51_REARLEFTRIGHT) #define XMV_BLOCK_ALIGN_SIZE 36 -typedef struct XMVAudioTrack { - uint16_t compression; - uint16_t channels; - uint32_t sample_rate; - uint16_t bits_per_sample; - uint32_t bit_rate; - uint16_t flags; - uint16_t block_align; - uint16_t block_samples; - - enum AVCodecID codec_id; -} XMVAudioTrack; - +/** A video packet with an XMV file. */ typedef struct XMVVideoPacket { - /* The decoder stream index for this video packet. */ - int stream_index; - - uint32_t data_size; - uint32_t data_offset; + int created; + int stream_index; ///< The decoder stream index for this video packet. - uint32_t current_frame; - uint32_t frame_count; + uint32_t data_size; ///< The size of the remaining video data. + uint64_t data_offset; ///< The offset of the video data within the file. - /* Does the video packet contain extra data? */ - int has_extradata; + uint32_t current_frame; ///< The current frame within this video packet. + uint32_t frame_count; ///< The amount of frames within this video packet. - /* Extra data */ - uint8_t extradata[4]; + int has_extradata; ///< Does the video packet contain extra data? + uint8_t extradata[4]; ///< The extra data - int64_t last_pts; - int64_t pts; + int64_t last_pts; ///< PTS of the last video frame. + int64_t pts; ///< PTS of the most current video frame. } XMVVideoPacket; +/** An audio packet with an XMV file. */ typedef struct XMVAudioPacket { - /* The decoder stream index for this audio packet. */ - int stream_index; + int created; + int stream_index; ///< The decoder stream index for this audio packet. - /* The audio track this packet encodes. */ - XMVAudioTrack *track; + /* Stream format properties. */ + uint16_t compression; ///< The type of compression. + uint16_t channels; ///< Number of channels. + uint32_t sample_rate; ///< Sampling rate. + uint16_t bits_per_sample; ///< Bits per compressed sample. + uint32_t bit_rate; ///< Bits of compressed data per second. + uint16_t flags; ///< Flags + unsigned block_align; ///< Bytes per compressed block. + uint16_t block_samples; ///< Decompressed samples per compressed block. - uint32_t data_size; - uint32_t data_offset; + enum AVCodecID codec_id; ///< The codec ID of the compression scheme. - uint32_t frame_size; + uint32_t data_size; ///< The size of the remaining audio data. + uint64_t data_offset; ///< The offset of the audio data within the file. - uint32_t block_count; + uint32_t frame_size; ///< Number of bytes to put into an audio frame. + + uint64_t block_count; ///< Running counter of decompressed audio block. } XMVAudioPacket; +/** Context for demuxing an XMV file. */ typedef struct XMVDemuxContext { - uint16_t audio_track_count; + uint16_t audio_track_count; ///< Number of audio track in this file. - XMVAudioTrack *audio_tracks; + uint32_t this_packet_size; ///< Size of the current packet. + uint32_t next_packet_size; ///< Size of the next packet. - uint32_t this_packet_size; - uint32_t next_packet_size; + uint64_t this_packet_offset; ///< Offset of the current packet. + uint64_t next_packet_offset; ///< Offset of the next packet. - uint32_t this_packet_offset; - uint32_t next_packet_offset; + uint16_t current_stream; ///< The index of the stream currently handling. + uint16_t stream_count; ///< The number of streams in this file. - uint16_t current_stream; - uint16_t stream_count; + uint32_t video_duration; + uint32_t video_width; + uint32_t video_height; - XMVVideoPacket video; - XMVAudioPacket *audio; + XMVVideoPacket video; ///< The video packet contained in each packet. + XMVAudioPacket *audio; ///< The audio packets contained in each packet. } XMVDemuxContext; static int xmv_probe(AVProbeData *p) @@ -132,8 +136,7 @@ static int xmv_read_close(AVFormatContext *s) { XMVDemuxContext *xmv = s->priv_data; - av_free(xmv->audio); - av_free(xmv->audio_tracks); + av_freep(&xmv->audio); return 0; } @@ -142,13 +145,14 @@ static int xmv_read_header(AVFormatContext *s) { XMVDemuxContext *xmv = s->priv_data; AVIOContext *pb = s->pb; - AVStream *vst = NULL; uint32_t file_version; uint32_t this_packet_size; uint16_t audio_track; int ret; + s->ctx_flags |= AVFMTCTX_NOHEADER; + avio_skip(pb, 4); /* Next packet size */ this_packet_size = avio_rl32(pb); @@ -160,24 +164,11 @@ static int xmv_read_header(AVFormatContext *s) if ((file_version != 4) && (file_version != 2)) avpriv_request_sample(s, "Uncommon version %"PRIu32"", file_version); + /* Video tracks */ - /* Video track */ - - vst = avformat_new_stream(s, NULL); - if (!vst) - return AVERROR(ENOMEM); - - avpriv_set_pts_info(vst, 32, 1, 1000); - - vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; - vst->codecpar->codec_id = AV_CODEC_ID_WMV2; - vst->codecpar->codec_tag = MKBETAG('W', 'M', 'V', '2'); - vst->codecpar->width = avio_rl32(pb); - vst->codecpar->height = avio_rl32(pb); - - vst->duration = avio_rl32(pb); - - xmv->video.stream_index = vst->index; + xmv->video_width = avio_rl32(pb); + xmv->video_height = avio_rl32(pb); + xmv->video_duration = avio_rl32(pb); /* Audio tracks */ @@ -185,36 +176,29 @@ static int xmv_read_header(AVFormatContext *s) avio_skip(pb, 2); /* Unknown (padding?) */ - xmv->audio_tracks = av_malloc(xmv->audio_track_count * sizeof(XMVAudioTrack)); - if (!xmv->audio_tracks) - return AVERROR(ENOMEM); - - xmv->audio = av_malloc(xmv->audio_track_count * sizeof(XMVAudioPacket)); + xmv->audio = av_mallocz_array(xmv->audio_track_count, sizeof(XMVAudioPacket)); if (!xmv->audio) { ret = AVERROR(ENOMEM); goto fail; } for (audio_track = 0; audio_track < xmv->audio_track_count; audio_track++) { - XMVAudioTrack *track = &xmv->audio_tracks[audio_track]; - XMVAudioPacket *packet = &xmv->audio [audio_track]; - AVStream *ast = NULL; - - track->compression = avio_rl16(pb); - track->channels = avio_rl16(pb); - track->sample_rate = avio_rl32(pb); - track->bits_per_sample = avio_rl16(pb); - track->flags = avio_rl16(pb); - - track->bit_rate = track->bits_per_sample * - track->sample_rate * - track->channels; - track->block_align = XMV_BLOCK_ALIGN_SIZE * track->channels; - track->block_samples = 64; - track->codec_id = ff_wav_codec_get_id(track->compression, - track->bits_per_sample); - - packet->track = track; + XMVAudioPacket *packet = &xmv->audio[audio_track]; + + packet->compression = avio_rl16(pb); + packet->channels = avio_rl16(pb); + packet->sample_rate = avio_rl32(pb); + packet->bits_per_sample = avio_rl16(pb); + packet->flags = avio_rl16(pb); + + packet->bit_rate = packet->bits_per_sample * + packet->sample_rate * + packet->channels; + packet->block_align = XMV_BLOCK_ALIGN_SIZE * packet->channels; + packet->block_samples = 64; + packet->codec_id = ff_wav_codec_get_id(packet->compression, + packet->bits_per_sample); + packet->stream_index = -1; packet->frame_size = 0; @@ -222,42 +206,21 @@ static int xmv_read_header(AVFormatContext *s) /* TODO: ADPCM'd 5.1 sound is encoded in three separate streams. * Those need to be interleaved to a proper 5.1 stream. */ - if (track->flags & XMV_AUDIO_ADPCM51) + if (packet->flags & XMV_AUDIO_ADPCM51) av_log(s, AV_LOG_WARNING, "Unsupported 5.1 ADPCM audio stream " - "(0x%04X)\n", track->flags); + "(0x%04X)\n", packet->flags); - if (!track->channels || !track->sample_rate || - track->channels >= UINT16_MAX / XMV_BLOCK_ALIGN_SIZE) { + if (!packet->channels || !packet->sample_rate || + packet->channels >= UINT16_MAX / XMV_BLOCK_ALIGN_SIZE) { av_log(s, AV_LOG_ERROR, "Invalid parameters for audio track %"PRIu16".\n", audio_track); ret = AVERROR_INVALIDDATA; goto fail; } - - ast = avformat_new_stream(s, NULL); - if (!ast) { - ret = AVERROR(ENOMEM); - goto fail; - } - - ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; - ast->codecpar->codec_id = track->codec_id; - ast->codecpar->codec_tag = track->compression; - ast->codecpar->channels = track->channels; - ast->codecpar->sample_rate = track->sample_rate; - ast->codecpar->bits_per_coded_sample = track->bits_per_sample; - ast->codecpar->bit_rate = track->bit_rate; - ast->codecpar->block_align = 36 * track->channels; - - avpriv_set_pts_info(ast, 32, track->block_samples, track->sample_rate); - - packet->stream_index = ast->index; - - ast->duration = vst->duration; } - /** Initialize the packet context */ + /* Initialize the packet context */ xmv->next_packet_offset = avio_tell(pb); xmv->next_packet_size = this_packet_size - xmv->next_packet_offset; @@ -303,10 +266,11 @@ static int xmv_process_packet_header(AVFormatContext *s) { XMVDemuxContext *xmv = s->priv_data; AVIOContext *pb = s->pb; + int ret; uint8_t data[8]; uint16_t audio_track; - uint32_t data_offset; + uint64_t data_offset; /* Next packet size */ xmv->next_packet_size = avio_rl32(pb); @@ -323,6 +287,26 @@ static int xmv_process_packet_header(AVFormatContext *s) xmv->video.has_extradata = (data[3] & 0x80) != 0; + if (!xmv->video.created) { + AVStream *vst = avformat_new_stream(s, NULL); + if (!vst) + return AVERROR(ENOMEM); + + avpriv_set_pts_info(vst, 32, 1, 1000); + + vst->codecpar->codec_type = AVMEDIA_TYPE_VIDEO; + vst->codecpar->codec_id = AV_CODEC_ID_WMV2; + vst->codecpar->codec_tag = MKBETAG('W', 'M', 'V', '2'); + vst->codecpar->width = xmv->video_width; + vst->codecpar->height = xmv->video_height; + + vst->duration = xmv->video_duration; + + xmv->video.stream_index = vst->index; + + xmv->video.created = 1; + } + /* Adding the audio data sizes and the video data size keeps you 4 bytes * short for every audio track. But as playing around with XMV files with * ADPCM audio showed, taking the extra 4 bytes from the audio data gives @@ -337,7 +321,7 @@ static int xmv_process_packet_header(AVFormatContext *s) xmv->current_stream = 0; if (!xmv->video.frame_count) { xmv->video.frame_count = 1; - xmv->current_stream = 1; + xmv->current_stream = xmv->stream_count > 1; } /* Packet audio header */ @@ -348,6 +332,29 @@ static int xmv_process_packet_header(AVFormatContext *s) if (avio_read(pb, data, 4) != 4) return AVERROR(EIO); + if (!packet->created) { + AVStream *ast = avformat_new_stream(s, NULL); + if (!ast) + return AVERROR(ENOMEM); + + ast->codecpar->codec_type = AVMEDIA_TYPE_AUDIO; + ast->codecpar->codec_id = packet->codec_id; + ast->codecpar->codec_tag = packet->compression; + ast->codecpar->channels = packet->channels; + ast->codecpar->sample_rate = packet->sample_rate; + ast->codecpar->bits_per_coded_sample = packet->bits_per_sample; + ast->codecpar->bit_rate = packet->bit_rate; + ast->codecpar->block_align = 36 * packet->channels; + + avpriv_set_pts_info(ast, 32, packet->block_samples, packet->sample_rate); + + packet->stream_index = ast->index; + + ast->duration = xmv->video_duration; + + packet->created = 1; + } + packet->data_size = AV_RL32(data) & 0x007FFFFF; if ((packet->data_size == 0) && (audio_track != 0)) /* This happens when I create an XMV with several identical audio @@ -357,9 +364,9 @@ static int xmv_process_packet_header(AVFormatContext *s) */ packet->data_size = xmv->audio[audio_track - 1].data_size; - /** Carve up the audio data in frame_count slices */ + /* Carve up the audio data in frame_count slices */ packet->frame_size = packet->data_size / xmv->video.frame_count; - packet->frame_size -= packet->frame_size % packet->track->block_align; + packet->frame_size -= packet->frame_size % packet->block_align; } /* Packet data offsets */ @@ -387,14 +394,13 @@ static int xmv_process_packet_header(AVFormatContext *s) if (xmv->video.stream_index >= 0) { AVStream *vst = s->streams[xmv->video.stream_index]; - assert(xmv->video.stream_index < s->nb_streams); + av_assert0(xmv->video.stream_index < s->nb_streams); if (vst->codecpar->extradata_size < 4) { - av_free(vst->codecpar->extradata); + av_freep(&vst->codecpar->extradata); - vst->codecpar->extradata = - av_malloc(4 + AV_INPUT_BUFFER_PADDING_SIZE); - vst->codecpar->extradata_size = 4; + if ((ret = ff_alloc_extradata(vst->codecpar, 4)) < 0) + return ret; } memcpy(vst->codecpar->extradata, xmv->video.extradata, 4); @@ -411,6 +417,9 @@ static int xmv_fetch_new_packet(AVFormatContext *s) AVIOContext *pb = s->pb; int result; + if (xmv->this_packet_offset == xmv->next_packet_offset) + return AVERROR_EOF; + /* Seek to it */ xmv->this_packet_offset = xmv->next_packet_offset; if (avio_seek(pb, xmv->this_packet_offset, SEEK_SET) != xmv->this_packet_offset) @@ -463,7 +472,7 @@ static int xmv_fetch_audio_packet(AVFormatContext *s, /* Calculate the PTS */ - block_count = data_size / audio->track->block_align; + block_count = data_size / audio->block_align; pkt->duration = block_count; pkt->pts = audio->block_count; @@ -488,7 +497,7 @@ static int xmv_fetch_video_packet(AVFormatContext *s, int result; uint32_t frame_header; uint32_t frame_size, frame_timestamp; - uint32_t i; + uint8_t *data, *end; /* Seek to it */ if (avio_seek(pb, video->data_offset, SEEK_SET) != video->data_offset) @@ -503,17 +512,17 @@ static int xmv_fetch_video_packet(AVFormatContext *s, if ((frame_size + 4) > video->data_size) return AVERROR(EIO); - /* Create the packet */ - result = av_new_packet(pkt, frame_size); - if (result) + /* Get the packet data */ + result = av_get_packet(pb, pkt, frame_size); + if (result != frame_size) return result; /* Contrary to normal WMV2 video, the bit stream in XMV's * WMV2 is little-endian. * TODO: This manual swap is of course suboptimal. */ - for (i = 0; i < frame_size; i += 4) - AV_WB32(pkt->data + i, avio_rl32(pb)); + for (data = pkt->data, end = pkt->data + frame_size; data < end; data += 4) + AV_WB32(data, AV_RL32(data)); pkt->stream_index = video->stream_index; @@ -555,16 +564,17 @@ static int xmv_read_packet(AVFormatContext *s, /* Fetch a video frame */ result = xmv_fetch_video_packet(s, pkt); - if (result) - return result; - } else { /* Fetch an audio frame */ result = xmv_fetch_audio_packet(s, pkt, xmv->current_stream - 1); - if (result) - return result; } + if (result) { + xmv->current_stream = 0; + xmv->video.current_frame = xmv->video.frame_count; + return result; + } + /* Increase our counters */ if (++xmv->current_stream >= xmv->stream_count) { @@ -578,6 +588,7 @@ static int xmv_read_packet(AVFormatContext *s, AVInputFormat ff_xmv_demuxer = { .name = "xmv", .long_name = NULL_IF_CONFIG_SMALL("Microsoft XMV"), + .extensions = "xmv", .priv_data_size = sizeof(XMVDemuxContext), .read_probe = xmv_probe, .read_header = xmv_read_header, |