diff options
author | Anton Khirnov <anton@khirnov.net> | 2011-07-27 12:57:32 +0200 |
---|---|---|
committer | Anton Khirnov <anton@khirnov.net> | 2011-10-11 08:35:03 +0200 |
commit | 8e9959a28e6ef92e30d4f185a4573590ff6651a1 (patch) | |
tree | d3293ea4170b64ba24fbad2c538498c447403dc0 | |
parent | 056f47282902cc6f1a0e72fae99451c689f4d12c (diff) |
lavf: add libxml2-based ASX playlist demuxer.
-rwxr-xr-x | configure | 5 | ||||
-rw-r--r-- | libavformat/Makefile | 1 | ||||
-rw-r--r-- | libavformat/allformats.c | 1 | ||||
-rw-r--r-- | libavformat/asxdec.c | 181 |
4 files changed, 188 insertions, 0 deletions
@@ -187,6 +187,7 @@ External library support: --enable-libvpx enable VP8 support via libvpx [no] --enable-libx264 enable H.264 encoding via x264 [no] --enable-libxavs enable AVS encoding via xavs [no] + --enable-libxml2 enable libxml2 (needed for asx demuxing) [no] --enable-libxvid enable Xvid encoding via xvidcore, native MPEG-4/Xvid encoder exists [no] --enable-mlib enable Sun medialib [no] @@ -950,6 +951,7 @@ CONFIG_LIST=" libvpx libx264 libxavs + libxml2 libxvid lpc lsp @@ -1419,6 +1421,7 @@ libxvid_encoder_deps="libxvid" # demuxers / muxers ac3_demuxer_select="ac3_parser" asf_stream_muxer_select="asf_muxer" +asx_demuxer_deps="libxml2" avisynth_demuxer_deps="avisynth" dirac_demuxer_select="dirac_parser" eac3_demuxer_select="ac3_parser" @@ -2908,6 +2911,7 @@ enabled libx264 && require libx264 x264.h x264_encoder_encode -lx264 && { check_cpp_condition x264.h "X264_BUILD >= 115" || die "ERROR: libx264 version must be >= 0.115."; } enabled libxavs && require libxavs xavs.h xavs_encoder_encode -lxavs +enabled libxml2 && require_pkg_config libxml-2.0 libxml/xmlreader.h xmlReaderForIO enabled libxvid && require libxvid xvid.h xvid_global -lxvidcore enabled mlib && require mediaLib mlib_types.h mlib_VectorSub_S16_U8_Mod -lmlib @@ -3187,6 +3191,7 @@ echo "libvorbis enabled ${libvorbis-no}" echo "libvpx enabled ${libvpx-no}" echo "libx264 enabled ${libx264-no}" echo "libxavs enabled ${libxavs-no}" +echo "libxml2 enabled ${libxml2-no}" echo "libxvid enabled ${libxvid-no}" echo "zlib enabled ${zlib-no}" echo "bzlib enabled ${bzlib-no}" diff --git a/libavformat/Makefile b/libavformat/Makefile index b1e8ed18d0..9253d13c7a 100644 --- a/libavformat/Makefile +++ b/libavformat/Makefile @@ -35,6 +35,7 @@ OBJS-$(CONFIG_ASF_DEMUXER) += asfdec.o asf.o asfcrypt.o \ OBJS-$(CONFIG_ASF_MUXER) += asfenc.o asf.o riff.o OBJS-$(CONFIG_ASS_DEMUXER) += assdec.o OBJS-$(CONFIG_ASS_MUXER) += assenc.o +OBJS-$(CONFIG_ASX_DEMUXER) += asxdec.o OBJS-$(CONFIG_AU_DEMUXER) += au.o pcm.o OBJS-$(CONFIG_AU_MUXER) += au.o OBJS-$(CONFIG_AVI_DEMUXER) += avidec.o riff.o avi.o diff --git a/libavformat/allformats.c b/libavformat/allformats.c index 5bfd840f3f..bbea23ecfc 100644 --- a/libavformat/allformats.c +++ b/libavformat/allformats.c @@ -62,6 +62,7 @@ void av_register_all(void) REGISTER_MUXDEMUX (ASF, asf); REGISTER_MUXDEMUX (ASS, ass); REGISTER_MUXER (ASF_STREAM, asf_stream); + REGISTER_DEMUXER (ASX, asx); REGISTER_MUXDEMUX (AU, au); REGISTER_MUXDEMUX (AVI, avi); REGISTER_DEMUXER (AVISYNTH, avisynth); diff --git a/libavformat/asxdec.c b/libavformat/asxdec.c new file mode 100644 index 0000000000..a6218697be --- /dev/null +++ b/libavformat/asxdec.c @@ -0,0 +1,181 @@ +/* + * ASX playlist demuxer. + * Copyright (c) 2011 Anton Khirnov + * + * This file is part of Libav. + * + * Libav 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, + * 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 + * Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA + */ + +#include "avformat.h" +#include "internal.h" +#include "playlist.h" + +#include "libavutil/avstring.h" +#include "libavutil/dict.h" +#include "libavutil/log.h" +#include "libavutil/mem.h" + +#include <strings.h> + +#include <libxml/xmlreader.h> + +/** + * Find an attribute using case-insensitive matching. + */ +static int asx_find_attribute(xmlTextReader *reader, const uint8_t *name) +{ + while (xmlTextReaderMoveToNextAttribute(reader) == 1) + if (!strcasecmp(xmlTextReaderConstName(reader), name)) + return 1; + return 0; +} + +static int asx_read_metadata(xmlTextReader *reader, AVDictionary **metadata) +{ + const uint8_t *key = xmlTextReaderConstName(reader); + + if (xmlTextReaderRead(reader) != 1 || + xmlTextReaderNodeType(reader) != XML_READER_TYPE_TEXT) + return 0; + return av_dict_set(metadata, key, xmlTextReaderConstValue(reader), 0); +} + +static int asx_read_ref(AVFormatContext *s, xmlTextReader *reader, AVPlaylistElement **elem) +{ + if (!asx_find_attribute(reader, "HREF")) { + av_log(s, AV_LOG_WARNING, "No URL in a REF element, skipping.\n"); + return 0; + } + + if (*elem) { + AVPlaylistElement *e = *elem; + uint8_t *filename = av_strdup(xmlTextReaderConstValue(reader)); + dynarray_add(&e->filenames, &e->nb_filenames, filename); + } else + *elem = ff_playlist_add_entry(s, xmlTextReaderConstValue(reader)); + + if (!*elem) + return AVERROR(ENOMEM); + + // TODO: parse children + return 0; +} + +static int asx_read_entry(AVFormatContext *s, xmlTextReader *reader) +{ + int ret; + + if (xmlTextReaderIsEmptyElement(reader)) { + // there's an undocumented example in the specs using this syntax + if (asx_find_attribute(reader, "HREF")) + ff_playlist_add_entry(s, xmlTextReaderConstValue(reader)); + else + av_log(s, AV_LOG_WARNING, "Empty ENTRY element.\n"); + } else { + int cur_depth = xmlTextReaderDepth(reader); + AVDictionary *metadata = NULL; + AVPlaylistElement *elem = NULL; + + while (xmlTextReaderRead(reader) == 1 && + !(xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT && + xmlTextReaderDepth(reader) == cur_depth)) { + if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT || + xmlTextReaderDepth(reader) != cur_depth + 1) + continue; + + if (!strcasecmp(xmlTextReaderConstName(reader), "ref")) + ret = asx_read_ref(s, reader, &elem); + else + ret = asx_read_metadata(reader, &metadata); + + if (ret < 0) + return ret; + } + if (elem) + elem->metadata = metadata; + else + av_log(s, AV_LOG_WARNING, "No REF element in an entry, skipping.\n"); + } + + return 0; +} + +/** + * avio_read() wrapper, translates avio errors into libxml2 errors + */ +static int asx_read_callback(AVIOContext *s, char *buf, int len) +{ + int ret = avio_read(s, buf, len); + + if (ret >= 0) + return ret; + else if (ret == AVERROR_EOF) + return 0; + else + return -1; +} + +static int asx_read_header(AVFormatContext *s, AVFormatParameters *ap) +{ + int ret = 0; + xmlTextReader *reader; + + if (!(reader = xmlReaderForIO((xmlInputReadCallback)asx_read_callback, NULL, s->pb, s->filename, NULL, 0))) { + av_log(s, AV_LOG_ERROR, "Could not create XML reader.\n"); + return AVERROR(EIO); + } + + if (xmlTextReaderRead(reader) != 1 || strcasecmp(xmlTextReaderConstName(reader), "ASX")) { + av_log(s, AV_LOG_ERROR, "File does not start with an ASX element.\n"); + ret = AVERROR_INVALIDDATA; + goto finish; + } + + while (xmlTextReaderRead(reader) == 1 && + !(xmlTextReaderNodeType(reader) == XML_READER_TYPE_END_ELEMENT && + xmlTextReaderDepth(reader) == 0)) { + const uint8_t *name; + + // find next top-level element + if (xmlTextReaderNodeType(reader) != XML_READER_TYPE_ELEMENT || + xmlTextReaderDepth(reader) != 1) + continue; + + name = xmlTextReaderConstName(reader); + if (!strcasecmp(name, "entry")) ret = asx_read_entry(s, reader); + else ret = asx_read_metadata(reader, &s->metadata); + + if (ret < 0) + goto finish; + } + +finish: + xmlFreeTextReader(reader); + return ret; +} + +static int asx_read_packet(AVFormatContext *s, AVPacket *pkt) +{ + return AVERROR_EOF; +} + +AVInputFormat ff_asx_demuxer = { + .name = "ASX", + .long_name = "Advanced Stream Redirector", + .read_header = asx_read_header, + .read_packet = asx_read_packet, + .extensions = "asx,wvx,wax", +}; |