aboutsummaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorAnton Khirnov <anton@khirnov.net>2011-07-27 12:57:32 +0200
committerAnton Khirnov <anton@khirnov.net>2011-10-11 08:35:03 +0200
commit8e9959a28e6ef92e30d4f185a4573590ff6651a1 (patch)
treed3293ea4170b64ba24fbad2c538498c447403dc0
parent056f47282902cc6f1a0e72fae99451c689f4d12c (diff)
lavf: add libxml2-based ASX playlist demuxer.
-rwxr-xr-xconfigure5
-rw-r--r--libavformat/Makefile1
-rw-r--r--libavformat/allformats.c1
-rw-r--r--libavformat/asxdec.c181
4 files changed, 188 insertions, 0 deletions
diff --git a/configure b/configure
index f5a285e6d1..d9fc8e2645 100755
--- a/configure
+++ b/configure
@@ -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",
+};