diff --git a/ios/.gitignore b/ios/.gitignore
index b5c9ce0e3..c41b0cfe1 100644
--- a/ios/.gitignore
+++ b/ios/.gitignore
@@ -2,3 +2,8 @@
/Makefile
/config.status
/configure.tmp
+/.deps/
+/.libs/
+*.trs
+/unittests
+/test-driver
diff --git a/ios/configure b/ios/configure
index 9a0b2edce..a51437717 100755
--- a/ios/configure
+++ b/ios/configure
@@ -9331,20 +9331,20 @@ fi
$as_echo "$lt_cv_ld_force_load" >&6; }
case $host_os in
rhapsody* | darwin1.[012])
- _lt_dar_allow_undefined='' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}suppress' ;;
darwin1.*)
- _lt_dar_allow_undefined='' ;;
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
darwin*) # darwin 5.x on
# if running on 10.5 or later, the deployment target defaults
# to the OS version, if on x86, and 10.4, the deployment
# target defaults to 10.4. Don't you love it?
case ${MACOSX_DEPLOYMENT_TARGET-10.0},$host in
10.0,*86*-darwin8*|10.0,*-darwin[91]*)
- _lt_dar_allow_undefined='${wl}dynamic_lookup' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
10.[012][,.]*)
- _lt_dar_allow_undefined='' ;;
+ _lt_dar_allow_undefined='$wl-flat_namespace $wl-undefined ${wl}suppress' ;;
10.*)
- _lt_dar_allow_undefined='${wl}dynamic_lookup' ;;
+ _lt_dar_allow_undefined='$wl-undefined ${wl}dynamic_lookup' ;;
esac
;;
esac
diff --git a/ios/src/Makefile.am b/ios/src/Makefile.am
index f6c2ef150..182da53e7 100644
--- a/ios/src/Makefile.am
+++ b/ios/src/Makefile.am
@@ -10,21 +10,27 @@ libmobileffmpeg_la_SOURCES = \
fftools_ffmpeg_hw.c \
fftools_ffmpeg_opt.c \
fftools_ffmpeg_videotoolbox.c \
+ MediaInformation.m \
+ MediaInformationParser.m \
MobileFFmpeg.m \
MobileFFmpegConfig.m \
mobileffmpeg_exception.m \
- Statistics.m
+ Statistics.m \
+ StreamInformation.m
include_HEADERS = \
ArchDetect.h \
fftools_cmdutils.h \
fftools_ffmpeg.h \
LogDelegate.h \
+ MediaInformation.h \
+ MediaInformationParser.h \
MobileFFmpeg.h \
MobileFFmpegConfig.h \
mobileffmpeg_exception.h \
Statistics.h \
- StatisticsDelegate.h
+ StatisticsDelegate.h \
+ StreamInformation.h
libmobileffmpeg_la_CFLAGS = $(CFLAGS)
libmobileffmpeg_la_OBJCFLAGS = $(CFLAGS)
diff --git a/ios/src/Makefile.in b/ios/src/Makefile.in
index 1801c163e..1e16410e9 100644
--- a/ios/src/Makefile.in
+++ b/ios/src/Makefile.in
@@ -139,10 +139,13 @@ am_libmobileffmpeg_la_OBJECTS = libmobileffmpeg_la-ArchDetect.lo \
libmobileffmpeg_la-fftools_ffmpeg_hw.lo \
libmobileffmpeg_la-fftools_ffmpeg_opt.lo \
libmobileffmpeg_la-fftools_ffmpeg_videotoolbox.lo \
+ libmobileffmpeg_la-MediaInformation.lo \
+ libmobileffmpeg_la-MediaInformationParser.lo \
libmobileffmpeg_la-MobileFFmpeg.lo \
libmobileffmpeg_la-MobileFFmpegConfig.lo \
libmobileffmpeg_la-mobileffmpeg_exception.lo \
- libmobileffmpeg_la-Statistics.lo
+ libmobileffmpeg_la-Statistics.lo \
+ libmobileffmpeg_la-StreamInformation.lo
libmobileffmpeg_la_OBJECTS = $(am_libmobileffmpeg_la_OBJECTS)
AM_V_lt = $(am__v_lt_@AM_V@)
am__v_lt_ = $(am__v_lt_@AM_DEFAULT_V@)
@@ -168,9 +171,12 @@ DEFAULT_INCLUDES = -I.@am__isrc@
depcomp = $(SHELL) $(top_srcdir)/depcomp
am__maybe_remake_depfiles = depfiles
am__depfiles_remade = ./$(DEPDIR)/libmobileffmpeg_la-ArchDetect.Plo \
+ ./$(DEPDIR)/libmobileffmpeg_la-MediaInformation.Plo \
+ ./$(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Plo \
./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Plo \
./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpegConfig.Plo \
./$(DEPDIR)/libmobileffmpeg_la-Statistics.Plo \
+ ./$(DEPDIR)/libmobileffmpeg_la-StreamInformation.Plo \
./$(DEPDIR)/libmobileffmpeg_la-fftools_cmdutils.Plo \
./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg.Plo \
./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg_filter.Plo \
@@ -375,21 +381,27 @@ libmobileffmpeg_la_SOURCES = \
fftools_ffmpeg_hw.c \
fftools_ffmpeg_opt.c \
fftools_ffmpeg_videotoolbox.c \
+ MediaInformation.m \
+ MediaInformationParser.m \
MobileFFmpeg.m \
MobileFFmpegConfig.m \
mobileffmpeg_exception.m \
- Statistics.m
+ Statistics.m \
+ StreamInformation.m
include_HEADERS = \
ArchDetect.h \
fftools_cmdutils.h \
fftools_ffmpeg.h \
LogDelegate.h \
+ MediaInformation.h \
+ MediaInformationParser.h \
MobileFFmpeg.h \
MobileFFmpegConfig.h \
mobileffmpeg_exception.h \
Statistics.h \
- StatisticsDelegate.h
+ StatisticsDelegate.h \
+ StreamInformation.h
libmobileffmpeg_la_CFLAGS = $(CFLAGS)
libmobileffmpeg_la_OBJCFLAGS = $(CFLAGS)
@@ -474,9 +486,12 @@ distclean-compile:
-rm -f *.tab.c
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-ArchDetect.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-MediaInformation.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpegConfig.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-Statistics.Plo@am__quote@ # am--include-marker
+@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-StreamInformation.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-fftools_cmdutils.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg.Plo@am__quote@ # am--include-marker
@AMDEP_TRUE@@am__include@ @am__quote@./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg_filter.Plo@am__quote@ # am--include-marker
@@ -588,6 +603,20 @@ libmobileffmpeg_la-ArchDetect.lo: ArchDetect.m
@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepOBJC_FALSE@ $(AM_V_OBJC@am__nodep@)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -c -o libmobileffmpeg_la-ArchDetect.lo `test -f 'ArchDetect.m' || echo '$(srcdir)/'`ArchDetect.m
+libmobileffmpeg_la-MediaInformation.lo: MediaInformation.m
+@am__fastdepOBJC_TRUE@ $(AM_V_OBJC)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -MT libmobileffmpeg_la-MediaInformation.lo -MD -MP -MF $(DEPDIR)/libmobileffmpeg_la-MediaInformation.Tpo -c -o libmobileffmpeg_la-MediaInformation.lo `test -f 'MediaInformation.m' || echo '$(srcdir)/'`MediaInformation.m
+@am__fastdepOBJC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmobileffmpeg_la-MediaInformation.Tpo $(DEPDIR)/libmobileffmpeg_la-MediaInformation.Plo
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ $(AM_V_OBJC)source='MediaInformation.m' object='libmobileffmpeg_la-MediaInformation.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepOBJC_FALSE@ $(AM_V_OBJC@am__nodep@)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -c -o libmobileffmpeg_la-MediaInformation.lo `test -f 'MediaInformation.m' || echo '$(srcdir)/'`MediaInformation.m
+
+libmobileffmpeg_la-MediaInformationParser.lo: MediaInformationParser.m
+@am__fastdepOBJC_TRUE@ $(AM_V_OBJC)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -MT libmobileffmpeg_la-MediaInformationParser.lo -MD -MP -MF $(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Tpo -c -o libmobileffmpeg_la-MediaInformationParser.lo `test -f 'MediaInformationParser.m' || echo '$(srcdir)/'`MediaInformationParser.m
+@am__fastdepOBJC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Tpo $(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Plo
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ $(AM_V_OBJC)source='MediaInformationParser.m' object='libmobileffmpeg_la-MediaInformationParser.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepOBJC_FALSE@ $(AM_V_OBJC@am__nodep@)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -c -o libmobileffmpeg_la-MediaInformationParser.lo `test -f 'MediaInformationParser.m' || echo '$(srcdir)/'`MediaInformationParser.m
+
libmobileffmpeg_la-MobileFFmpeg.lo: MobileFFmpeg.m
@am__fastdepOBJC_TRUE@ $(AM_V_OBJC)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -MT libmobileffmpeg_la-MobileFFmpeg.lo -MD -MP -MF $(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Tpo -c -o libmobileffmpeg_la-MobileFFmpeg.lo `test -f 'MobileFFmpeg.m' || echo '$(srcdir)/'`MobileFFmpeg.m
@am__fastdepOBJC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Tpo $(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Plo
@@ -616,6 +645,13 @@ libmobileffmpeg_la-Statistics.lo: Statistics.m
@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
@am__fastdepOBJC_FALSE@ $(AM_V_OBJC@am__nodep@)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -c -o libmobileffmpeg_la-Statistics.lo `test -f 'Statistics.m' || echo '$(srcdir)/'`Statistics.m
+libmobileffmpeg_la-StreamInformation.lo: StreamInformation.m
+@am__fastdepOBJC_TRUE@ $(AM_V_OBJC)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -MT libmobileffmpeg_la-StreamInformation.lo -MD -MP -MF $(DEPDIR)/libmobileffmpeg_la-StreamInformation.Tpo -c -o libmobileffmpeg_la-StreamInformation.lo `test -f 'StreamInformation.m' || echo '$(srcdir)/'`StreamInformation.m
+@am__fastdepOBJC_TRUE@ $(AM_V_at)$(am__mv) $(DEPDIR)/libmobileffmpeg_la-StreamInformation.Tpo $(DEPDIR)/libmobileffmpeg_la-StreamInformation.Plo
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ $(AM_V_OBJC)source='StreamInformation.m' object='libmobileffmpeg_la-StreamInformation.lo' libtool=yes @AMDEPBACKSLASH@
+@AMDEP_TRUE@@am__fastdepOBJC_FALSE@ DEPDIR=$(DEPDIR) $(OBJCDEPMODE) $(depcomp) @AMDEPBACKSLASH@
+@am__fastdepOBJC_FALSE@ $(AM_V_OBJC@am__nodep@)$(LIBTOOL) $(AM_V_lt) $(AM_LIBTOOLFLAGS) $(LIBTOOLFLAGS) --mode=compile $(OBJC) $(DEFS) $(DEFAULT_INCLUDES) $(INCLUDES) $(AM_CPPFLAGS) $(CPPFLAGS) $(libmobileffmpeg_la_OBJCFLAGS) $(OBJCFLAGS) -c -o libmobileffmpeg_la-StreamInformation.lo `test -f 'StreamInformation.m' || echo '$(srcdir)/'`StreamInformation.m
+
mostlyclean-libtool:
-rm -f *.lo
@@ -772,9 +808,12 @@ clean-am: clean-generic clean-libLTLIBRARIES clean-libtool \
distclean: distclean-am
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-ArchDetect.Plo
+ -rm -f ./$(DEPDIR)/libmobileffmpeg_la-MediaInformation.Plo
+ -rm -f ./$(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpegConfig.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-Statistics.Plo
+ -rm -f ./$(DEPDIR)/libmobileffmpeg_la-StreamInformation.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-fftools_cmdutils.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg_filter.Plo
@@ -828,9 +867,12 @@ installcheck-am:
maintainer-clean: maintainer-clean-am
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-ArchDetect.Plo
+ -rm -f ./$(DEPDIR)/libmobileffmpeg_la-MediaInformation.Plo
+ -rm -f ./$(DEPDIR)/libmobileffmpeg_la-MediaInformationParser.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpeg.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-MobileFFmpegConfig.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-Statistics.Plo
+ -rm -f ./$(DEPDIR)/libmobileffmpeg_la-StreamInformation.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-fftools_cmdutils.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg.Plo
-rm -f ./$(DEPDIR)/libmobileffmpeg_la-fftools_ffmpeg_filter.Plo
diff --git a/ios/src/MediaInformation.h b/ios/src/MediaInformation.h
new file mode 100644
index 000000000..f9b1df7f5
--- /dev/null
+++ b/ios/src/MediaInformation.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (c) 2018 Taner Sener
+ *
+ * This file is part of MobileFFmpeg.
+ *
+ * MobileFFmpeg 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MobileFFmpeg 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 MobileFFmpeg. If not, see .
+ */
+
+#include
+#include "StreamInformation.h"
+
+@interface MediaInformation : NSObject
+
+- (instancetype)init;
+
+/**
+ * Returns format.
+ *
+ * \return media format
+ */
+- (NSString*)getFormat;
+
+/**
+ * Sets media format.
+ *
+ * \param media format
+ */
+- (void)setFormat:(NSString*)format;
+
+/**
+ * Returns path.
+ *
+ * \return media path
+ */
+- (NSString*)getPath;
+
+/**
+ * Sets media path.
+ *
+ * \param media path
+ */
+- (void)setPath:(NSString*)path;
+
+/**
+ * Returns duration.
+ *
+ * \return media duration in milliseconds
+ */
+- (NSNumber*)getDuration;
+
+/**
+ * Sets media duration.
+ *
+ * \param media duration in milliseconds
+ */
+- (void)setDuration:(NSNumber*) duration;
+
+/**
+ * Returns start time.
+ *
+ * \return media start time in milliseconds
+ */
+- (NSNumber*)getStartTime;
+
+/**
+ * Sets media start time.
+ *
+ * \param media start time in milliseconds
+ */
+- (void)setStartTime:(NSNumber*)startTime;
+
+/**
+ * Returns bitrate.
+ *
+ * \return media bitrate in kb/s
+ */
+- (NSNumber*)getBitrate;
+
+/**
+ * Sets bitrate.
+ *
+ * \param media bitrate in kb/s
+ */
+- (void)setBitrate:(NSNumber*) bitrate;
+
+/**
+ * Returns unparsed media information.
+ *
+ * \return unparsed media information data
+ */
+- (NSString*)getRawInformation;
+
+/**
+ * Sets unparsed media information.
+ *
+ * \param unparsed media information data
+ */
+- (void)setRawInformation:(NSString*)rawInformation;
+
+/**
+ * Adds metadata.
+ *
+ * \param metadata key and value
+ */
+- (void)addMetadata:(NSString*)key :(NSString*)value;
+
+/**
+ * Returns all metadata entries.
+ *
+ * \return metadata dictionary
+ */
+- (NSDictionary*)getMetadataEntries;
+
+/**
+ * Adds new stream.
+ *
+ * \param new stream information
+ */
+- (void)addStream:(StreamInformation*) stream;
+
+/**
+ * Returns all streams
+ *
+ * \return streams array
+ */
+- (NSArray*)getStreams;
+
+@end
diff --git a/ios/src/MediaInformation.m b/ios/src/MediaInformation.m
new file mode 100644
index 000000000..0db2068e1
--- /dev/null
+++ b/ios/src/MediaInformation.m
@@ -0,0 +1,146 @@
+/*
+ * Copyright (c) 2018 Taner Sener
+ *
+ * This file is part of MobileFFmpeg.
+ *
+ * MobileFFmpeg 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MobileFFmpeg 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 MobileFFmpeg. If not, see .
+ */
+
+#include "MediaInformation.h"
+
+@implementation MediaInformation {
+
+ /**
+ * Format
+ */
+ NSString *format;
+
+ /**
+ * Path
+ */
+ NSString *path;
+
+ /**
+ * Duration, in milliseconds
+ */
+ NSNumber *duration;
+
+ /**
+ * Start time, in milliseconds
+ */
+ NSNumber *startTime;
+
+ /**
+ * Bitrate, kb/s
+ */
+ NSNumber *bitrate;
+
+ /**
+ * Metadata map
+ */
+ NSMutableDictionary *metadata;
+
+ /**
+ * List of streams
+ */
+ NSMutableArray *streams;
+
+ /**
+ * Raw unparsed media information
+ */
+ NSString *rawInformation;
+
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ format = nil;
+ path = nil;
+ duration = nil;
+ startTime = nil;
+ bitrate = nil;
+ metadata = [[NSMutableDictionary alloc] init];
+ streams = [[NSMutableArray alloc] init];
+ rawInformation = nil;
+ }
+
+ return self;
+}
+
+- (NSString*)getFormat {
+ return format;
+}
+
+- (void)setFormat:(NSString*)newFormat {
+ format = newFormat;
+}
+
+- (NSString*)getPath {
+ return path;
+}
+
+- (void)setPath:(NSString*)newPath {
+ path = newPath;
+}
+
+- (NSNumber*)getDuration {
+ return duration;
+}
+
+- (void)setDuration:(NSNumber*)newDuration {
+ duration = newDuration;
+}
+
+- (NSNumber*)getStartTime {
+ return startTime;
+}
+
+- (void)setStartTime:(NSNumber*)newStartTime {
+ startTime = newStartTime;
+}
+
+- (NSNumber*)getBitrate {
+ return bitrate;
+}
+
+- (void)setBitrate:(NSNumber*)newBitrate {
+ bitrate = newBitrate;
+}
+
+- (NSString*)getRawInformation {
+ return rawInformation;
+}
+
+- (void)setRawInformation:(NSString*)newRawInformation {
+ rawInformation = newRawInformation;
+}
+
+- (void)addMetadata:(NSString*)key :(NSString*)value {
+ metadata[key] = value;
+}
+
+- (NSDictionary*)getMetadataEntries {
+ return metadata;
+}
+
+- (void)addStream:(StreamInformation*)stream {
+ [streams addObject:stream];
+}
+
+- (NSArray*)getStreams {
+ return streams;
+}
+
+@end
diff --git a/ios/src/MediaInformationParser.h b/ios/src/MediaInformationParser.h
new file mode 100644
index 000000000..591307c4c
--- /dev/null
+++ b/ios/src/MediaInformationParser.h
@@ -0,0 +1,58 @@
+/*
+ * Copyright (c) 2018 Taner Sener
+ *
+ * This file is part of MobileFFmpeg.
+ *
+ * MobileFFmpeg 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MobileFFmpeg 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 MobileFFmpeg. If not, see .
+ */
+
+#include
+#include "MediaInformation.h"
+
+@interface MediaInformationParser : NSObject
+
+/**
+ * Extracts MediaInformation from given command output.
+ */
++ (MediaInformation*)from: (NSString*)rawCommandOutput;
+
+/**
+ * Extracts StreamInformation from given input.
+ */
++ (StreamInformation*)parseStreamBlock:(NSString*)input;
+
++ (void (^)(NSString *__autoreleasing*, NSString *__autoreleasing*))parseInputBlock:(NSString*)input;
++ (void (^)(NSNumber *__autoreleasing*, NSNumber *__autoreleasing*, NSNumber *__autoreleasing*))parseDurationBlock:(NSString*)input;
++ (void (^)(NSString *__autoreleasing*, NSString *__autoreleasing*))parseMetadataBlock:(NSString*)input;
++ (void (^)(NSNumber *__autoreleasing*, NSNumber *__autoreleasing*))parseVideoDimensions: (NSString*)input;
+
++ (NSString*)parseVideoStreamSampleAspectRatio: (NSString*)input;
++ (NSString*)parseVideoStreamDisplayAspectRatio: (NSString*)input;
++ (NSNumber*)parseAudioStreamSampleRate: (NSString*)input;
++ (NSString*)parseStreamType: (NSString*)input;
++ (NSString*)parseStreamCodec: (NSString*)input;
++ (NSString*)parseStreamFullCodec: (NSString*)input;
++ (NSNumber*)parseStreamIndex: (NSString*)input;
++ (NSNumber*)parseDuration: (NSString*)input;
++ (NSNumber*)parseStartTime: (NSString*)input;
++ (NSString*)substring:(NSString*)string from:(NSString*)start to:(NSString*)end ignoring:(NSArray*)ignoredTokens;
++ (NSString*)substring:(NSString*)string from:(NSString*)start ignoring:(NSArray*)ignoredTokens;
++ (NSString*)substring:(NSString*)string to:(NSString*)start ignoring:(NSArray*)ignoredTokens;
++ (int)index:(NSString*)string of:(NSString*)substring from:(int)startIndex times:(int)n;
++ (int)count:(NSString*)string of:(NSString*)substring;
++ (NSNumber*)toInteger: (NSString*)input;
++ (NSNumber*)toIntegerObject: (NSString*)input;
++ (NSString*)safeGet:(NSArray*)array from:(int)index;
+
+@end
diff --git a/ios/src/MediaInformationParser.m b/ios/src/MediaInformationParser.m
new file mode 100644
index 000000000..b042b99c6
--- /dev/null
+++ b/ios/src/MediaInformationParser.m
@@ -0,0 +1,590 @@
+/*
+ * Copyright (c) 2018 Taner Sener
+ *
+ * This file is part of MobileFFmpeg.
+ *
+ * MobileFFmpeg 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MobileFFmpeg 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 MobileFFmpeg. If not, see .
+ */
+
+#include "MediaInformationParser.h"
+
+static NSDateFormatter* durationFormat;
+static NSDate* referenceDuration;
+static NSRegularExpression *parenthesesRegex;
+static NSRegularExpression *bracketsRegex;
+
+@implementation MediaInformationParser
+
++ (void)initialize {
+ NSError *localError = nil;
+ durationFormat = [[NSDateFormatter alloc] init];
+ [durationFormat setDateFormat:@"HH:mm:ss"];
+
+ referenceDuration = [durationFormat dateFromString: @"00:00:00"];
+ parenthesesRegex = [NSRegularExpression regularExpressionWithPattern:@"\\(.*\\)" options:NSRegularExpressionCaseInsensitive error:&localError];
+ bracketsRegex = [NSRegularExpression regularExpressionWithPattern:@"\\[.*\\]" options:NSRegularExpressionCaseInsensitive error:&localError];
+}
+
++ (MediaInformation*)from: (NSString*)rawCommandOutput {
+ MediaInformation* mediaInformation = [[MediaInformation alloc] init];
+
+ if (rawCommandOutput != nil) {
+ NSArray* split = [rawCommandOutput componentsSeparatedByString:@"\n"];
+ Boolean metadata = false;
+ StreamInformation *lastCreatedStream = nil;
+ NSMutableString *rawInformation = [[NSMutableString alloc] init];
+
+ for (int i=0; i < [split count]; i++) {
+ NSString *outputLine = [split objectAtIndex:i];
+
+ if ([outputLine hasPrefix:@"["]) {
+ metadata = false;
+ continue;
+ }
+
+ NSString *trimmedLine = [outputLine stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+
+ if ([trimmedLine hasPrefix:@"Input"]) {
+ metadata = false;
+ lastCreatedStream = nil;
+
+ void(^blockPair)(NSString **, NSString **) = [MediaInformationParser parseInputBlock:trimmedLine];
+
+ NSString *format;
+ NSString *path;
+ blockPair(&format, &path);
+
+ [mediaInformation setFormat:format];
+ [mediaInformation setPath:path];
+ } else if ([trimmedLine hasPrefix:@"Duration"]) {
+ metadata = false;
+ lastCreatedStream = nil;
+
+ void(^blockTrio)(NSNumber **, NSNumber **, NSNumber **) = [MediaInformationParser parseDurationBlock:trimmedLine];
+
+ NSNumber *duration;
+ NSNumber *startTime;
+ NSNumber *bitrate;
+
+ blockTrio(&duration, &startTime, &bitrate);
+
+ [mediaInformation setDuration:duration];
+ [mediaInformation setStartTime:startTime];
+ [mediaInformation setBitrate:bitrate];
+ } else if ([trimmedLine hasPrefix:@"Metadata"]) {
+ metadata = true;
+ } else if ([trimmedLine hasPrefix:@"Stream mapping"] || [trimmedLine hasPrefix:@"Press [q] to stop"] || [trimmedLine hasPrefix:@"Output"]) {
+ break;
+ } else if ([trimmedLine hasPrefix:@"Stream"]) {
+ metadata = false;
+ lastCreatedStream = [MediaInformationParser parseStreamBlock:trimmedLine];
+ [mediaInformation addStream:lastCreatedStream];
+ } else if (metadata) {
+ void(^blockPair)(NSString **, NSString **) = [MediaInformationParser parseMetadataBlock:trimmedLine];
+
+ NSString *key;
+ NSString *value;
+ blockPair(&key, &value);
+
+ if (key != nil && value != nil) {
+ if (lastCreatedStream != nil) {
+ [lastCreatedStream addMetadata:key:value];
+ } else {
+ [mediaInformation addMetadata:key:value];
+ }
+ }
+ }
+
+ [rawInformation appendString:outputLine];
+ [rawInformation appendString:@"\n"];
+ }
+
+ [mediaInformation setRawInformation:rawInformation];
+ }
+
+ return mediaInformation;
+}
+
++ (void (^)(NSString *__autoreleasing*, NSString *__autoreleasing*))parseInputBlock:(NSString*)input {
+ NSString *format = [MediaInformationParser substring:input from:@"," to:@", from" ignoring:nil];
+ NSString *path = [MediaInformationParser substring:input from:@"'" to:@"'" ignoring:nil];
+
+ return ^(NSString **s1, NSString **s2){
+ *s1 = format;
+ *s2 = path;
+ };
+}
+
++ (void (^)(NSNumber *__autoreleasing*, NSNumber *__autoreleasing*, NSNumber *__autoreleasing*))parseDurationBlock:(NSString*)input {
+ NSNumber *duration = [MediaInformationParser parseDuration:[MediaInformationParser substring:input from:@"Duration:" to:@"," ignoring:[[NSMutableArray alloc] initWithObjects:@"uration:", nil]]];
+ NSNumber *start = [MediaInformationParser parseStartTime:[MediaInformationParser substring:input from:@"start:" to:@"," ignoring:[[NSMutableArray alloc] initWithObjects:@"tart:", nil]]];
+ NSString *bitrateString = [MediaInformationParser substring:input from:@"bitrate:" ignoring:[[NSMutableArray alloc] initWithObjects:@"itrate:", @"kb/s", nil]];
+
+ NSNumber *bitrate = nil;
+ if (bitrateString != nil && ![bitrateString isEqualToString:@"N/A"]) {
+ bitrate = [MediaInformationParser toIntegerObject:bitrateString];
+ }
+
+ return ^(NSNumber **n1, NSNumber **n2, NSNumber **n3){
+ *n1 = duration;
+ *n2 = start;
+ *n3 = bitrate;
+ };
+}
+
++ (void (^)(NSString *__autoreleasing*, NSString *__autoreleasing*))parseMetadataBlock:(NSString*)input {
+ NSString *key = nil;
+ NSString *value = nil;
+
+ if (input != nil) {
+ key = [MediaInformationParser substring:input to:@":" ignoring:[[NSMutableArray alloc] init]];
+ value = [MediaInformationParser substring:input from:@":" ignoring:[[NSMutableArray alloc] init]];
+ }
+
+ return ^(NSString **s1, NSString **s2){
+ *s1 = key;
+ *s2 = value;
+ };
+}
+
++ (StreamInformation*)parseStreamBlock:(NSString*)input {
+ StreamInformation* streamInformation = [[StreamInformation alloc] init];
+
+ if (input != nil) {
+ [streamInformation setIndex:[MediaInformationParser parseStreamIndex:input]];
+
+ int typeBlockStartIndex = [MediaInformationParser index:input of:@":" from:0 times:2];
+ if ((typeBlockStartIndex > -1) && (typeBlockStartIndex < [input length])) {
+
+ NSString *streamBlock = [input substringFromIndex:(typeBlockStartIndex + 1)];
+
+ NSArray* parts = [streamBlock componentsSeparatedByString:@","];
+ NSString *typePart = [MediaInformationParser safeGet:parts from:0];
+ NSString *type = [MediaInformationParser parseStreamType:typePart];
+
+ [streamInformation setType:type];
+ [streamInformation setCodec:[MediaInformationParser parseStreamCodec:typePart]];
+ [streamInformation setFullCodec:[MediaInformationParser parseStreamFullCodec:typePart]];
+
+ NSString *part2 = [MediaInformationParser safeGet:parts from:1];
+ NSString *part3 = [MediaInformationParser safeGet:parts from:2];
+ NSString *part4 = [MediaInformationParser safeGet:parts from:3];
+ NSString *part5 = [MediaInformationParser safeGet:parts from:4];
+
+ if ([@"video" isEqualToString:type]) {
+ int lastUsedPart = 1;
+
+ if (part2 != nil) {
+ int pStart = [MediaInformationParser count:part2 of:@"("];
+ int pEnd = [MediaInformationParser count:part2 of:@")"];
+
+ while (pStart != pEnd) {
+ lastUsedPart++;
+ NSString *newPart = [MediaInformationParser safeGet:parts from:lastUsedPart];
+ if (newPart == nil) {
+ break;
+ }
+ part2 = [NSString stringWithFormat:@"%@,%@", part2, newPart];
+ pStart = [MediaInformationParser count:part2 of:@"("];
+ pEnd = [MediaInformationParser count:part2 of:@")"];
+ }
+
+ part2 = [part2 lowercaseString];
+ part2 = [part2 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+
+ [streamInformation setFullFormat:part2];
+
+ NSString *formattedPart2 = [parenthesesRegex stringByReplacingMatchesInString:part2 options:0 range:NSMakeRange(0, [part2 length]) withTemplate:@""];
+ formattedPart2 = [formattedPart2 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setFormat:formattedPart2];
+ }
+
+ lastUsedPart++;
+ NSString *videoDimensionPart = [MediaInformationParser safeGet:parts from:lastUsedPart];
+ if (videoDimensionPart != nil) {
+ NSString *videoLayout = [videoDimensionPart lowercaseString];
+ videoLayout = [videoLayout stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+
+ void(^dimensions)(NSNumber **, NSNumber **) = [MediaInformationParser parseVideoDimensions:videoLayout];
+ NSNumber *width;
+ NSNumber *height;
+ dimensions(&width, &height);
+
+ [streamInformation setWidth:width];
+ [streamInformation setHeight:height];
+
+ [streamInformation setSampleAspectRatio:[MediaInformationParser parseVideoStreamSampleAspectRatio:videoLayout]];
+ [streamInformation setDisplayAspectRatio:[MediaInformationParser parseVideoStreamDisplayAspectRatio:videoLayout]];
+ }
+
+ for (int i = lastUsedPart + 1; i < [parts count]; i++) {
+ NSString *part = [parts objectAtIndex:i];
+
+ part = [parenthesesRegex stringByReplacingMatchesInString:part options:0 range:NSMakeRange(0, [part length]) withTemplate:@""];
+ part = [part lowercaseString];
+
+ if ([part rangeOfString:@"kb/s"].location != NSNotFound) {
+ part = [part stringByReplacingOccurrencesOfString:@"kb/s" withString:@""];
+ part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ NSNumber *bitrate = [MediaInformationParser toIntegerObject:part];
+ [streamInformation setBitrate:bitrate];
+ } else if ([part rangeOfString:@"fps"].location != NSNotFound) {
+ part = [part stringByReplacingOccurrencesOfString:@"fps" withString:@""];
+ part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setAverageFrameRate:part];
+ } else if ([part rangeOfString:@"tbr"].location != NSNotFound) {
+ part = [part stringByReplacingOccurrencesOfString:@"tbr" withString:@""];
+ part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setRealFrameRate:part];
+ } else if ([part rangeOfString:@"tbn"].location != NSNotFound) {
+ part = [part stringByReplacingOccurrencesOfString:@"tbn" withString:@""];
+ part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setTimeBase:part];
+ } else if ([part rangeOfString:@"tbc"].location != NSNotFound) {
+ part = [part stringByReplacingOccurrencesOfString:@"tbc" withString:@""];
+ part = [part stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setCodecTimeBase:part];
+ }
+ }
+ }
+
+ if ([@"audio" isEqualToString:type]) {
+ if (part2 != nil) {
+ [streamInformation setSampleRate:[MediaInformationParser parseAudioStreamSampleRate:part2]];
+ }
+ if (part3 != nil) {
+ NSString *formattedPart3 = [part3 lowercaseString];
+ formattedPart3 = [formattedPart3 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setChannelLayout:formattedPart3];
+ }
+ if (part4 != nil) {
+ NSString *formattedPart4 = [part4 lowercaseString];
+ formattedPart4 = [formattedPart4 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setSampleFormat:formattedPart4];
+ }
+ if (part5 != nil) {
+ NSString *formattedPart5 = [part5 lowercaseString];
+ formattedPart5 = [parenthesesRegex stringByReplacingMatchesInString:formattedPart5 options:0 range:NSMakeRange(0, [formattedPart5 length]) withTemplate:@""];
+ formattedPart5 = [formattedPart5 stringByReplacingOccurrencesOfString:@"kb/s" withString:@""];
+ formattedPart5 = [formattedPart5 stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ [streamInformation setBitrate:[MediaInformationParser toIntegerObject:formattedPart5]];
+ }
+ }
+ }
+ }
+
+ return streamInformation;
+}
+
++ (void (^)(NSNumber *__autoreleasing*, NSNumber *__autoreleasing*))parseVideoDimensions: (NSString*)input {
+ NSNumber *width = nil;
+ NSNumber *height = nil;
+
+ if (input != nil) {
+
+ NSString *formattedString = [input lowercaseString];
+ formattedString = [bracketsRegex stringByReplacingMatchesInString:formattedString options:0 range:NSMakeRange(0, [formattedString length]) withTemplate:@""];
+
+ NSString *widthString = [MediaInformationParser substring:formattedString to:@"x" ignoring:[[NSMutableArray alloc] init]];
+ NSString *heightString = [MediaInformationParser substring:formattedString from:@"x" ignoring:[[NSMutableArray alloc] init]];
+
+ width = [MediaInformationParser toIntegerObject:widthString];
+ height = [MediaInformationParser toIntegerObject:heightString];
+ }
+
+ return ^(NSNumber **n1, NSNumber **n2){
+ *n1 = width;
+ *n2 = height;
+ };
+}
+
++ (NSString*)parseVideoStreamSampleAspectRatio: (NSString*)input {
+ if (input != nil) {
+
+ NSString *formattedString = [input lowercaseString];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"[" withString:@""];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"]" withString:@""];
+
+ NSArray* parts = [formattedString componentsSeparatedByString:@" "];
+
+ for (int i=0; i < [parts count]; i++) {
+ NSString *token = [parts objectAtIndex:i];
+ if ([token isEqualToString:@"sar"]) {
+ return [MediaInformationParser safeGet:parts from:(i + 1)];
+ }
+ }
+ }
+
+ return nil;
+}
+
++ (NSString*)parseVideoStreamDisplayAspectRatio: (NSString*)input {
+ if (input != nil) {
+
+ NSString *formattedString = [input lowercaseString];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"[" withString:@""];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"]" withString:@""];
+
+ NSArray* parts = [formattedString componentsSeparatedByString:@" "];
+
+ for (int i=0; i < [parts count]; i++) {
+ NSString *token = [parts objectAtIndex:i];
+ if ([token isEqualToString:@"dar"]) {
+ return [MediaInformationParser safeGet:parts from:(i + 1)];
+ }
+ }
+ }
+
+ return nil;
+}
+
++ (NSNumber*)parseAudioStreamSampleRate: (NSString*)input {
+ if (input != nil) {
+ Boolean khz = false;
+ Boolean mhz = false;
+ NSString *lowerCaseString = [input lowercaseString];
+
+ if ([lowerCaseString rangeOfString:@"khz"].location != NSNotFound) {
+ khz = true;
+ }
+ if ([lowerCaseString rangeOfString:@"mhz"].location != NSNotFound) {
+ mhz = true;
+ }
+
+ lowerCaseString = [lowerCaseString stringByReplacingOccurrencesOfString:@"khz" withString:@""];
+ lowerCaseString = [lowerCaseString stringByReplacingOccurrencesOfString:@"mhz" withString:@""];
+ lowerCaseString = [lowerCaseString stringByReplacingOccurrencesOfString:@"hz" withString:@""];
+
+ NSNumber *sampleRate = [MediaInformationParser toInteger:[lowerCaseString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]]];
+ if (khz) {
+ return [NSNumber numberWithInteger:(1000*sampleRate.intValue)];
+ } else if (mhz) {
+ return [NSNumber numberWithInteger:(1000000*sampleRate.intValue)];
+ } else {
+ return sampleRate;
+ }
+ }
+
+ return nil;
+}
+
++ (NSString*)parseStreamType: (NSString*)input {
+ if (input != nil) {
+ NSString *formattedString = [input lowercaseString];
+
+ if ([formattedString rangeOfString:@"audio:"].location != NSNotFound) {
+ return @"audio";
+ } else if ([formattedString rangeOfString:@"video:"].location != NSNotFound) {
+ return @"video";
+ }
+ }
+
+ return nil;
+}
+
++ (NSString*)parseStreamCodec: (NSString*)input {
+ if (input != nil) {
+ NSString *formattedString = [input lowercaseString];
+ formattedString = [parenthesesRegex stringByReplacingMatchesInString:formattedString options:0 range:NSMakeRange(0, [formattedString length]) withTemplate:@""];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"video:" withString:@""];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"audio:" withString:@""];
+
+ return [formattedString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ }
+
+ return nil;
+}
+
++ (NSString*)parseStreamFullCodec: (NSString*)input {
+ if (input != nil) {
+ NSString *formattedString = [input lowercaseString];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"video:" withString:@""];
+ formattedString = [formattedString stringByReplacingOccurrencesOfString:@"audio:" withString:@""];
+
+ return [formattedString stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+ }
+
+ return nil;
+}
+
++ (NSNumber*)parseStreamIndex: (NSString*)input {
+ NSString *substring = [MediaInformationParser substring:input from:@"Stream #0:" to:@":" ignoring:[[NSMutableArray alloc] initWithObjects:@"tream #0", nil]];
+ if (substring != nil) {
+ substring = [substring stringByReplacingOccurrencesOfString:@":" withString:@""];
+ substring = [parenthesesRegex stringByReplacingMatchesInString:substring options:0 range:NSMakeRange(0, [substring length]) withTemplate:@""];
+
+ return [MediaInformationParser toIntegerObject:substring];
+ }
+
+ return nil;
+}
+
++ (NSNumber*)parseDuration: (NSString*)input {
+ if (input != nil && ![input isEqualToString:@"N/A"]) {
+ NSString *seconds = [MediaInformationParser substring:input to:@"." ignoring:[[NSMutableArray alloc] init]];
+ if (seconds != nil) {
+ NSDate* durationDate = [durationFormat dateFromString: seconds];
+ if (durationDate != nil) {
+ NSTimeInterval secondsInMilliseconds = [durationDate timeIntervalSinceDate:referenceDuration]*1000;
+ NSNumber *centiSeconds = [MediaInformationParser toInteger:[MediaInformationParser substring:input from:@"." ignoring:[[NSMutableArray alloc] init]]];
+
+ secondsInMilliseconds += centiSeconds.intValue*10;
+
+ return [NSNumber numberWithInteger:secondsInMilliseconds];
+ }
+ }
+ }
+
+ return nil;
+}
+
++ (NSNumber*)parseStartTime: (NSString*)input {
+ if (input != nil && ![input isEqualToString:@"N/A"] && ([input length]>0)) {
+ double inputAsDouble = [input doubleValue];
+ return [NSNumber numberWithInteger:ceil(inputAsDouble*1000)];
+ }
+
+ return nil;
+}
+
++ (NSString*)substring:(NSString*)string from:(NSString*)start to:(NSString*)end ignoring:(NSArray*)ignoredTokens {
+ NSString *extractedSubstring = nil;
+
+ if (string != nil) {
+ NSRange formatStart = [string rangeOfString:start];
+ if (formatStart.location != NSNotFound && (formatStart.location + start.length < [string length])) {
+
+ NSRange formatEnd = [string rangeOfString:end options:NSLiteralSearch range:NSMakeRange(formatStart.location + start.length, string.length - (formatStart.location + start.length + 1))];
+
+ if (formatEnd.location != NSNotFound) {
+ extractedSubstring = [string substringWithRange:NSMakeRange(formatStart.location + start.length, formatEnd.location - (formatStart.location + start.length))];
+ }
+ }
+ }
+
+ if ((ignoredTokens != nil) && (extractedSubstring != nil)) {
+ for (int i=0; i < [ignoredTokens count]; i++) {
+ NSString *token = [ignoredTokens objectAtIndex:i];
+ extractedSubstring = [extractedSubstring stringByReplacingOccurrencesOfString:token withString:@""];
+ }
+ }
+
+ return (extractedSubstring == nil) ? nil : [extractedSubstring stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+}
+
++ (NSString*)substring:(NSString*)string from:(NSString*)start ignoring:(NSArray*)ignoredTokens {
+ NSString *extractedSubstring = nil;
+
+ if (string != nil) {
+ NSRange formatStart = [string rangeOfString:start];
+ if (formatStart.location != NSNotFound) {
+ extractedSubstring = [string substringWithRange:NSMakeRange(formatStart.location + start.length, string.length - (formatStart.location + start.length))];
+ }
+ }
+
+ if ((ignoredTokens != nil) && (extractedSubstring != nil)) {
+ for (int i=0; i < [ignoredTokens count]; i++) {
+ NSString *token = [ignoredTokens objectAtIndex:i];
+ extractedSubstring = [extractedSubstring stringByReplacingOccurrencesOfString:token withString:@""];
+ }
+ }
+
+ return (extractedSubstring == nil) ? nil : [extractedSubstring stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+}
+
++ (NSString*)substring:(NSString*)string to:(NSString*)start ignoring:(NSArray*)ignoredTokens {
+ NSString *extractedSubstring = nil;
+
+ if (string != nil) {
+ NSRange formatStart = [string rangeOfString:start];
+ if (formatStart.location != NSNotFound) {
+ extractedSubstring = [string substringWithRange:NSMakeRange(0, formatStart.location)];
+ }
+ }
+
+ if ((ignoredTokens != nil) && (extractedSubstring != nil)) {
+ for (int i=0; i < [ignoredTokens count]; i++) {
+ NSString *token = [ignoredTokens objectAtIndex:i];
+ extractedSubstring = [extractedSubstring stringByReplacingOccurrencesOfString:token withString:@""];
+ }
+ }
+
+ return (extractedSubstring == nil) ? nil : [extractedSubstring stringByTrimmingCharactersInSet:[NSCharacterSet whitespaceCharacterSet]];
+}
+
++ (int)index:(NSString*)string of:(NSString*)substring from:(int)startIndex times:(int)n {
+ int count = 1;
+ startIndex -= substring.length;
+
+ while (count <= n) {
+ unsigned long searchIndex = startIndex + (int)substring.length;
+ NSRange currentRange = [string rangeOfString:substring options:NSLiteralSearch range:NSMakeRange(searchIndex, string.length - searchIndex)];
+ if (currentRange.location != NSNotFound) {
+ startIndex = (int)currentRange.location;
+ }
+
+ count++;
+ }
+
+ return startIndex;
+}
+
++ (int)count:(NSString*)string of:(NSString*)substring {
+ int count = 0;
+ int index = 0 - (int)substring.length;
+
+ do {
+ int searchIndex = index + (int)substring.length;
+ NSRange currentRange = [string rangeOfString:substring options:NSLiteralSearch range:NSMakeRange(searchIndex, string.length - searchIndex)];
+ if (currentRange.location != NSNotFound) {
+ count++;
+ index = (int)currentRange.location;
+ } else {
+ index = -1;
+ }
+ } while (index >= 0);
+
+ return count;
+}
+
++ (NSNumber*)toInteger: (NSString*)input {
+ if (input == nil) {
+ return [NSNumber numberWithInt:0];
+ }
+
+ return [NSNumber numberWithInteger:input.integerValue];
+}
+
++ (NSNumber*)toIntegerObject: (NSString*)input {
+ if (input == nil) {
+ return nil;
+ }
+
+ return [NSNumber numberWithInteger:input.integerValue];
+}
+
++ (NSString*)safeGet:(NSArray*)array from:(int)index {
+ if (array == nil) {
+ return nil;
+ }
+
+ unsigned long size = [array count];
+ if (size > index) {
+ return [array objectAtIndex:index];
+ } else {
+ return nil;
+ }
+}
+
+@end
diff --git a/ios/src/StreamInformation.h b/ios/src/StreamInformation.h
new file mode 100644
index 000000000..f92492fdc
--- /dev/null
+++ b/ios/src/StreamInformation.h
@@ -0,0 +1,292 @@
+/*
+ * Copyright (c) 2018 Taner Sener
+ *
+ * This file is part of MobileFFmpeg.
+ *
+ * MobileFFmpeg 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MobileFFmpeg 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 MobileFFmpeg. If not, see .
+ */
+
+#include
+
+@interface StreamInformation : NSObject
+
+- (instancetype)init;
+
+/**
+ * Returns stream index.
+ *
+ * \return stream index, starting from zero
+ */
+- (NSNumber*)getIndex;
+
+/**
+ * Sets stream index.
+ *
+ * \param stream index, starting from zero
+ */
+- (void)setIndex:(NSNumber*) index;
+
+/**
+ * Returns stream type.
+ *
+ * \return stream type; audio or video
+ */
+- (NSString*)getType;
+
+/**
+ * Sets stream type.
+ *
+ * \param stream type; audio or video
+ */
+- (void)setType:(NSString*) type;
+
+/**
+ * Returns stream codec.
+ *
+ * \return stream codec
+ */
+- (NSString*)getCodec;
+
+/**
+ * Sets stream codec.
+ *
+ * \param stream codec
+ */
+- (void)setCodec:(NSString*) codec;
+
+/**
+ * Returns full stream codec.
+ *
+ * \return stream codec with additional profile and mode information
+ */
+- (NSString*)getFullCodec;
+
+/**
+ * Sets full stream codec.
+ *
+ * \param stream codec with additional profile and mode information
+ */
+- (void)setFullCodec:(NSString*) fullCodec;
+
+/**
+ * Returns stream format.
+ *
+ * \return stream format
+ */
+- (NSString*)getFormat;
+
+/**
+ * Sets stream format.
+ *
+ * \param stream format
+ */
+- (void)setFormat:(NSString*) format;
+
+/**
+ * Returns full stream format.
+ *
+ * \return stream format with
+ */
+- (NSString*)getFullFormat;
+
+/**
+ * Sets full stream format.
+ *
+ * \param stream format with
+ */
+- (void)setFullFormat:(NSString*) fullFormat;
+
+/**
+ * Returns width.
+ *
+ * \return width in pixels
+ */
+- (NSNumber*)getWidth;
+
+/**
+ * Sets width.
+ *
+ * \param width in pixels
+ */
+- (void)setWidth:(NSNumber*) width;
+
+/**
+ * Returns height.
+ *
+ * \return height in pixels
+ */
+- (NSNumber*)getHeight;
+
+/**
+ * Sets height.
+ *
+ * \param height in pixels
+ */
+- (void)setHeight:(NSNumber*) height;
+
+/**
+ * Returns bitrate.
+ *
+ * \return bitrate in kb/s
+ */
+- (NSNumber*)getBitrate;
+
+/**
+ * Sets bitrate.
+ *
+ * \param bitrate in kb/s
+ */
+- (void)setBitrate:(NSNumber*) bitrate;
+
+/**
+ * Returns sample rate.
+ *
+ * \return sample rate in hz
+ */
+- (NSNumber*)getSampleRate;
+
+/**
+ * Sets sample rate.
+ *
+ * \param sample rate in hz
+ */
+- (void)setSampleRate:(NSNumber*) sampleRate;
+
+/**
+ * Returns sample format.
+ *
+ * \return sample format
+ */
+- (NSString*)getSampleFormat;
+
+/**
+ * Sets sample format.
+ *
+ * \param sample format
+ */
+- (void)setSampleFormat:(NSString*) sampleFormat;
+
+/**
+ * Returns channel layout.
+ *
+ * \return channel layout
+ */
+- (NSString*)getChannelLayout;
+
+/**
+ * Sets channel layout.
+ *
+ * \param channel layout
+ */
+- (void)setChannelLayout:(NSString*) channelLayout;
+
+/**
+ * Returns sample aspect ratio.
+ *
+ * \return sample aspect ratio
+ */
+- (NSString*)getSampleAspectRatio;
+
+/**
+ * Sets sample aspect ratio.
+ *
+ * \param sample aspect ratio
+ */
+- (void)setSampleAspectRatio:(NSString*) sampleAspectRatio;
+
+/**
+ * Returns display aspect ratio.
+ *
+ * \return display aspect ratio
+ */
+- (NSString*)getDisplayAspectRatio;
+
+/**
+ * Sets display aspect ratio.
+ *
+ * \param display aspect ratio
+ */
+- (void)setDisplayAspectRatio:(NSString*) displayAspectRatio;
+
+/**
+ * Returns average frame rate.
+ *
+ * \return average frame rate in fps
+ */
+- (NSString*)getAverageFrameRate;
+
+/**
+ * Sets average frame rate.
+ *
+ * \param average frame rate in fps
+ */
+- (void)setAverageFrameRate:(NSString*) averageFrameRatehn;
+
+/**
+ * Returns real frame rate.
+ *
+ * \return real frame rate in tbr
+ */
+- (NSString*)getRealFrameRate;
+
+/**
+ * Sets real frame rate.
+ *
+ * \param real frame rate in tbr
+ */
+- (void)setRealFrameRate:(NSString*) realFrameRate;
+
+/**
+ * Returns time base.
+ *
+ * \return time base in tbn
+ */
+- (NSString*)getTimeBase;
+
+/**
+ * Sets time base.
+ *
+ * \param time base in tbn
+ */
+- (void)setTimeBase:(NSString*) timeBase;
+
+/**
+ * Returns codec time base.
+ *
+ * \return codec time base in tbc
+ */
+- (NSString*)getCodecTimeBase;
+
+/**
+ * Sets codec time base.
+ *
+ * \param codec time base in tbc
+ */
+- (void)setCodecTimeBase:(NSString*) codecTimeBase;
+
+/**
+ * Adds metadata.
+ *
+ * \param metadata key and value
+ */
+- (void)addMetadata:(NSString*)key :(NSString*)value;
+
+/**
+ * Returns all metadata entries.
+ *
+ * \return metadata dictionary
+ */
+- (NSDictionary*)getMetadataEntries;
+
+@end
diff --git a/ios/src/StreamInformation.m b/ios/src/StreamInformation.m
new file mode 100644
index 000000000..a422f8e1a
--- /dev/null
+++ b/ios/src/StreamInformation.m
@@ -0,0 +1,259 @@
+/*
+ * Copyright (c) 2018 Taner Sener
+ *
+ * This file is part of MobileFFmpeg.
+ *
+ * MobileFFmpeg 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 3 of the License, or
+ * (at your option) any later version.
+ *
+ * MobileFFmpeg 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 MobileFFmpeg. If not, see .
+ */
+
+#include "StreamInformation.h"
+
+@implementation StreamInformation {
+
+ /**
+ * Stream index
+ */
+ NSNumber *index;
+
+ NSString *type;
+ NSString *codec;
+ NSString *fullCodec;
+ NSString *format;
+ NSString *fullFormat;
+
+ NSNumber *width;
+ NSNumber *height;
+
+ NSNumber *bitrate;
+ NSNumber *sampleRate;
+ NSString *sampleFormat;
+ NSString *channelLayout;
+
+ /**
+ * SAR
+ */
+ NSString *sampleAspectRatio;
+
+ /**
+ * DAR
+ */
+ NSString *displayAspectRatio;
+
+ /**
+ * fps
+ */
+ NSString *averageFrameRate;
+
+ /**
+ * tbr
+ */
+ NSString *realFrameRate;
+
+ /**
+ * tbn
+ */
+ NSString *timeBase;
+
+ /**
+ * tbc
+ */
+ NSString *codecTimeBase;
+
+ /**
+ * Metadata map
+ */
+ NSMutableDictionary *metadata;
+
+}
+
+- (instancetype)init {
+ self = [super init];
+ if (self) {
+ index = nil;
+ type = nil;
+ codec = nil;
+ fullCodec = nil;
+ format = nil;
+ fullFormat = nil;
+ width = nil;
+ height = nil;
+ bitrate = nil;
+ sampleRate = nil;
+ sampleFormat = nil;
+ channelLayout = nil;
+ sampleAspectRatio = nil;
+ displayAspectRatio = nil;
+ averageFrameRate = nil;
+ realFrameRate = nil;
+ timeBase = nil;
+ codecTimeBase = nil;
+ metadata = nil;
+ }
+
+ return self;
+}
+
+- (NSNumber*)getIndex {
+ return index;
+}
+
+- (void)setIndex:(NSNumber*) newIndex {
+ index = newIndex;
+}
+
+- (NSString*)getType {
+ return type;
+}
+
+- (void)setType:(NSString*) newType {
+ type = newType;
+}
+
+- (NSString*)getCodec {
+ return codec;
+}
+
+- (void)setCodec:(NSString*) newCodec {
+ codec = newCodec;
+}
+
+- (NSString*)getFullCodec {
+ return fullCodec;
+}
+
+- (void)setFullCodec:(NSString*) newFullCodec {
+ fullCodec = newFullCodec;
+}
+
+- (NSString*)getFormat {
+ return format;
+}
+
+- (void)setFormat:(NSString*) newFormat {
+ format = newFormat;
+}
+
+- (NSString*)getFullFormat {
+ return fullFormat;
+}
+
+- (void)setFullFormat:(NSString*) newFullFormat {
+ fullFormat = newFullFormat;
+}
+
+- (NSNumber*)getWidth {
+ return width;
+}
+
+- (void)setWidth:(NSNumber*) newWidth {
+ width = newWidth;
+}
+
+- (NSNumber*)getHeight {
+ return height;
+}
+
+- (void)setHeight:(NSNumber*) newHeight {
+ height = newHeight;
+}
+
+- (NSNumber*)getBitrate {
+ return bitrate;
+}
+
+- (void)setBitrate:(NSNumber*) newBitrate {
+ bitrate = newBitrate;
+}
+
+- (NSNumber*)getSampleRate {
+ return sampleRate;
+}
+
+- (void)setSampleRate:(NSNumber*) newSampleRate {
+ sampleRate = newSampleRate;
+}
+
+- (NSString*)getSampleFormat {
+ return sampleFormat;
+}
+
+- (void)setSampleFormat:(NSString*) newSampleFormat {
+ sampleFormat = newSampleFormat;
+}
+
+- (NSString*)getChannelLayout {
+ return channelLayout;
+}
+
+- (void)setChannelLayout:(NSString*) newChannelLayout {
+ channelLayout = newChannelLayout;
+}
+
+- (NSString*)getSampleAspectRatio {
+ return sampleAspectRatio;
+}
+
+- (void)setSampleAspectRatio:(NSString*) newSampleAspectRatio {
+ sampleAspectRatio = newSampleAspectRatio;
+}
+
+- (NSString*)getDisplayAspectRatio {
+ return displayAspectRatio;
+}
+
+- (void)setDisplayAspectRatio:(NSString*) newDisplayAspectRatio {
+ displayAspectRatio = newDisplayAspectRatio;
+}
+
+- (NSString*)getAverageFrameRate {
+ return averageFrameRate;
+}
+
+- (void)setAverageFrameRate:(NSString*) newAverageFrameRatehn {
+ averageFrameRate = newAverageFrameRatehn;
+}
+
+- (NSString*)getRealFrameRate {
+ return realFrameRate;
+}
+
+- (void)setRealFrameRate:(NSString*) newRealFrameRate {
+ realFrameRate = newRealFrameRate;
+}
+
+- (NSString*)getTimeBase {
+ return timeBase;
+}
+
+- (void)setTimeBase:(NSString*) newTimeBase {
+ timeBase = newTimeBase;
+}
+
+- (NSString*)getCodecTimeBase {
+ return codecTimeBase;
+}
+
+- (void)setCodecTimeBase:(NSString*) newCodecTimeBase {
+ codecTimeBase = newCodecTimeBase;
+}
+
+- (void)addMetadata:(NSString*)key :(NSString*)value {
+ metadata[key] = value;
+}
+
+- (NSDictionary*)getMetadataEntries {
+ return metadata;
+}
+
+@end
diff --git a/ios/test-app/MobileFFmpegTest.xcodeproj/project.pbxproj b/ios/test-app/MobileFFmpegTest.xcodeproj/project.pbxproj
index ff07d0f2a..a1551023e 100644
--- a/ios/test-app/MobileFFmpegTest.xcodeproj/project.pbxproj
+++ b/ios/test-app/MobileFFmpegTest.xcodeproj/project.pbxproj
@@ -9,6 +9,8 @@
/* Begin PBXBuildFile section */
340FABBE2115A17600B33CE7 /* VidStabViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 340FABBC2115A17600B33CE7 /* VidStabViewController.h */; };
340FABBF2115A17600B33CE7 /* VidStabViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 340FABBD2115A17600B33CE7 /* VidStabViewController.m */; };
+ 3423D267217F7CC100C3C7ED /* MediaInformationParserTests.m in Sources */ = {isa = PBXBuildFile; fileRef = 3423D266217F7CC100C3C7ED /* MediaInformationParserTests.m */; };
+ 3423D269217F7D4E00C3C7ED /* MediaInformationParserTests.h in Headers */ = {isa = PBXBuildFile; fileRef = 3423D268217F7D4E00C3C7ED /* MediaInformationParserTests.h */; };
343E66B5210784BD00F95E5B /* HttpsViewController.h in Headers */ = {isa = PBXBuildFile; fileRef = 343E66B3210784BD00F95E5B /* HttpsViewController.h */; };
343E66B6210784BD00F95E5B /* HttpsViewController.m in Sources */ = {isa = PBXBuildFile; fileRef = 343E66B4210784BD00F95E5B /* HttpsViewController.m */; };
343E66B9210789DE00F95E5B /* Constants.h in Headers */ = {isa = PBXBuildFile; fileRef = 343E66B7210789DE00F95E5B /* Constants.h */; };
@@ -24,6 +26,22 @@
348439EE20B2EFAF001A3990 /* tajmahal.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 348439EB20B2EFAF001A3990 /* tajmahal.jpg */; };
348439EF20B2EFAF001A3990 /* colosseum.jpg in Resources */ = {isa = PBXBuildFile; fileRef = 348439EC20B2EFAF001A3990 /* colosseum.jpg */; };
34BB547C2109D44800606B7C /* Constants.m in Sources */ = {isa = PBXBuildFile; fileRef = 34BB547B2109D44800606B7C /* Constants.m */; };
+ 34C5E89E217F7F86001DCD99 /* libavfilter.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E896217F7F86001DCD99 /* libavfilter.framework */; };
+ 34C5E89F217F7F86001DCD99 /* libavcodec.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E897217F7F86001DCD99 /* libavcodec.framework */; };
+ 34C5E8A0217F7F86001DCD99 /* libswresample.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E898217F7F86001DCD99 /* libswresample.framework */; };
+ 34C5E8A1217F7F86001DCD99 /* libavutil.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E899217F7F86001DCD99 /* libavutil.framework */; };
+ 34C5E8A2217F7F86001DCD99 /* libswscale.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89A217F7F86001DCD99 /* libswscale.framework */; };
+ 34C5E8A3217F7F86001DCD99 /* libavformat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89B217F7F86001DCD99 /* libavformat.framework */; };
+ 34C5E8A4217F7F86001DCD99 /* mobileffmpeg.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89C217F7F86001DCD99 /* mobileffmpeg.framework */; };
+ 34C5E8A5217F7F86001DCD99 /* libavdevice.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89D217F7F86001DCD99 /* libavdevice.framework */; };
+ 34C5E8A6217F7F92001DCD99 /* libavcodec.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E897217F7F86001DCD99 /* libavcodec.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8A7217F7F92001DCD99 /* libavdevice.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89D217F7F86001DCD99 /* libavdevice.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8A8217F7F92001DCD99 /* libavfilter.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E896217F7F86001DCD99 /* libavfilter.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8A9217F7F92001DCD99 /* libavformat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89B217F7F86001DCD99 /* libavformat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8AA217F7F92001DCD99 /* libavutil.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E899217F7F86001DCD99 /* libavutil.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8AB217F7F92001DCD99 /* libswresample.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E898217F7F86001DCD99 /* libswresample.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8AC217F7F92001DCD99 /* libswscale.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89A217F7F86001DCD99 /* libswscale.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
+ 34C5E8AD217F7F92001DCD99 /* mobileffmpeg.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 34C5E89C217F7F86001DCD99 /* mobileffmpeg.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
34DC789B21215B5800C3486C /* truenorg.otf in Resources */ = {isa = PBXBuildFile; fileRef = 34DC789721215B5800C3486C /* truenorg.otf */; };
34DC789C21215B5800C3486C /* subtitle.srt in Resources */ = {isa = PBXBuildFile; fileRef = 34DC789821215B5800C3486C /* subtitle.srt */; };
34DC789D21215B5800C3486C /* doppioone_regular.ttf in Resources */ = {isa = PBXBuildFile; fileRef = 34DC789921215B5800C3486C /* doppioone_regular.ttf */; };
@@ -45,6 +63,14 @@
dstPath = "";
dstSubfolderSpec = 10;
files = (
+ 34C5E8A6217F7F92001DCD99 /* libavcodec.framework in Embed Frameworks */,
+ 34C5E8A7217F7F92001DCD99 /* libavdevice.framework in Embed Frameworks */,
+ 34C5E8A8217F7F92001DCD99 /* libavfilter.framework in Embed Frameworks */,
+ 34C5E8A9217F7F92001DCD99 /* libavformat.framework in Embed Frameworks */,
+ 34C5E8AA217F7F92001DCD99 /* libavutil.framework in Embed Frameworks */,
+ 34C5E8AB217F7F92001DCD99 /* libswresample.framework in Embed Frameworks */,
+ 34C5E8AC217F7F92001DCD99 /* libswscale.framework in Embed Frameworks */,
+ 34C5E8AD217F7F92001DCD99 /* mobileffmpeg.framework in Embed Frameworks */,
);
name = "Embed Frameworks";
runOnlyForDeploymentPostprocessing = 0;
@@ -55,6 +81,8 @@
09762EB4030C758C5727BD68 /* Pods-MobileFFmpegTest.debug.xcconfig */ = {isa = PBXFileReference; includeInIndex = 1; lastKnownFileType = text.xcconfig; name = "Pods-MobileFFmpegTest.debug.xcconfig"; path = "Pods/Target Support Files/Pods-MobileFFmpegTest/Pods-MobileFFmpegTest.debug.xcconfig"; sourceTree = ""; };
340FABBC2115A17600B33CE7 /* VidStabViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = VidStabViewController.h; sourceTree = ""; };
340FABBD2115A17600B33CE7 /* VidStabViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = VidStabViewController.m; sourceTree = ""; };
+ 3423D266217F7CC100C3C7ED /* MediaInformationParserTests.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = MediaInformationParserTests.m; sourceTree = ""; };
+ 3423D268217F7D4E00C3C7ED /* MediaInformationParserTests.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = MediaInformationParserTests.h; sourceTree = ""; };
343E66B3210784BD00F95E5B /* HttpsViewController.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = HttpsViewController.h; sourceTree = ""; };
343E66B4210784BD00F95E5B /* HttpsViewController.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = HttpsViewController.m; sourceTree = ""; };
343E66B7210789DE00F95E5B /* Constants.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = Constants.h; sourceTree = ""; };
@@ -70,6 +98,14 @@
348439EB20B2EFAF001A3990 /* tajmahal.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = tajmahal.jpg; sourceTree = ""; };
348439EC20B2EFAF001A3990 /* colosseum.jpg */ = {isa = PBXFileReference; lastKnownFileType = image.jpeg; path = colosseum.jpg; sourceTree = ""; };
34BB547B2109D44800606B7C /* Constants.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = Constants.m; sourceTree = ""; };
+ 34C5E896217F7F86001DCD99 /* libavfilter.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libavfilter.framework; sourceTree = ""; };
+ 34C5E897217F7F86001DCD99 /* libavcodec.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libavcodec.framework; sourceTree = ""; };
+ 34C5E898217F7F86001DCD99 /* libswresample.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libswresample.framework; sourceTree = ""; };
+ 34C5E899217F7F86001DCD99 /* libavutil.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libavutil.framework; sourceTree = ""; };
+ 34C5E89A217F7F86001DCD99 /* libswscale.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libswscale.framework; sourceTree = ""; };
+ 34C5E89B217F7F86001DCD99 /* libavformat.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libavformat.framework; sourceTree = ""; };
+ 34C5E89C217F7F86001DCD99 /* mobileffmpeg.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = mobileffmpeg.framework; sourceTree = ""; };
+ 34C5E89D217F7F86001DCD99 /* libavdevice.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; path = libavdevice.framework; sourceTree = ""; };
34DC789721215B5800C3486C /* truenorg.otf */ = {isa = PBXFileReference; lastKnownFileType = file; path = truenorg.otf; sourceTree = ""; };
34DC789821215B5800C3486C /* subtitle.srt */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text; path = subtitle.srt; sourceTree = ""; };
34DC789921215B5800C3486C /* doppioone_regular.ttf */ = {isa = PBXFileReference; lastKnownFileType = file; path = doppioone_regular.ttf; sourceTree = ""; };
@@ -95,7 +131,15 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
+ 34C5E8A1217F7F86001DCD99 /* libavutil.framework in Frameworks */,
+ 34C5E89F217F7F86001DCD99 /* libavcodec.framework in Frameworks */,
+ 34C5E8A0217F7F86001DCD99 /* libswresample.framework in Frameworks */,
+ 34C5E8A2217F7F86001DCD99 /* libswscale.framework in Frameworks */,
+ 34C5E8A5217F7F86001DCD99 /* libavdevice.framework in Frameworks */,
8A776E4D8F4EB85888ADFB99 /* Pods_MobileFFmpegTest.framework in Frameworks */,
+ 34C5E89E217F7F86001DCD99 /* libavfilter.framework in Frameworks */,
+ 34C5E8A3217F7F86001DCD99 /* libavformat.framework in Frameworks */,
+ 34C5E8A4217F7F86001DCD99 /* mobileffmpeg.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -119,6 +163,14 @@
3498DC65209F7F1C005F5883 /* Frameworks */ = {
isa = PBXGroup;
children = (
+ 34C5E897217F7F86001DCD99 /* libavcodec.framework */,
+ 34C5E89D217F7F86001DCD99 /* libavdevice.framework */,
+ 34C5E896217F7F86001DCD99 /* libavfilter.framework */,
+ 34C5E89B217F7F86001DCD99 /* libavformat.framework */,
+ 34C5E899217F7F86001DCD99 /* libavutil.framework */,
+ 34C5E898217F7F86001DCD99 /* libswresample.framework */,
+ 34C5E89A217F7F86001DCD99 /* libswscale.framework */,
+ 34C5E89C217F7F86001DCD99 /* mobileffmpeg.framework */,
F4B824D376F9CD476C761D40 /* Pods_MobileFFmpegTest.framework */,
);
name = Frameworks;
@@ -147,30 +199,32 @@
isa = PBXGroup;
children = (
34FAE213209F7DDD005CE2AE /* AppDelegate.h */,
- 345745492113B8AD00059043 /* AudioViewController.h */,
- 34FAE216209F7DDD005CE2AE /* CommandViewController.h */,
- 343E66B7210789DE00F95E5B /* Constants.h */,
- 343E66B3210784BD00F95E5B /* HttpsViewController.h */,
- 3457454D2113B8D300059043 /* SubtitleViewController.h */,
- 343E67072107C27600F95E5B /* TabBarController.h */,
- 343E67032107AD0100F95E5B /* Util.h */,
- 34FAE219209F7DDD005CE2AE /* VideoViewController.h */,
- 340FABBC2115A17600B33CE7 /* VidStabViewController.h */,
34FAE214209F7DDD005CE2AE /* AppDelegate.m */,
- 3457454A2113B8AD00059043 /* AudioViewController.m */,
- 34FAE217209F7DDD005CE2AE /* CommandViewController.m */,
- 34BB547B2109D44800606B7C /* Constants.m */,
- 343E66B4210784BD00F95E5B /* HttpsViewController.m */,
- 34FAE225209F7DDE005CE2AE /* main.m */,
- 3457454E2113B8D300059043 /* SubtitleViewController.m */,
- 343E67082107C27600F95E5B /* TabBarController.m */,
- 343E67042107AD0100F95E5B /* Util.m */,
- 34FAE21A209F7DDD005CE2AE /* VideoViewController.m */,
- 340FABBD2115A17600B33CE7 /* VidStabViewController.m */,
- 34FAE224209F7DDE005CE2AE /* Info.plist */,
34FAE21F209F7DDE005CE2AE /* Assets.xcassets */,
+ 345745492113B8AD00059043 /* AudioViewController.h */,
+ 3457454A2113B8AD00059043 /* AudioViewController.m */,
+ 34FAE216209F7DDD005CE2AE /* CommandViewController.h */,
+ 34FAE217209F7DDD005CE2AE /* CommandViewController.m */,
+ 343E66B7210789DE00F95E5B /* Constants.h */,
+ 34BB547B2109D44800606B7C /* Constants.m */,
+ 343E66B3210784BD00F95E5B /* HttpsViewController.h */,
+ 343E66B4210784BD00F95E5B /* HttpsViewController.m */,
+ 34FAE224209F7DDE005CE2AE /* Info.plist */,
34FAE221209F7DDE005CE2AE /* LaunchScreen.storyboard */,
+ 34FAE225209F7DDE005CE2AE /* main.m */,
34FAE21C209F7DDD005CE2AE /* Main.storyboard */,
+ 3423D268217F7D4E00C3C7ED /* MediaInformationParserTests.h */,
+ 3423D266217F7CC100C3C7ED /* MediaInformationParserTests.m */,
+ 3457454D2113B8D300059043 /* SubtitleViewController.h */,
+ 3457454E2113B8D300059043 /* SubtitleViewController.m */,
+ 343E67072107C27600F95E5B /* TabBarController.h */,
+ 343E67082107C27600F95E5B /* TabBarController.m */,
+ 343E67032107AD0100F95E5B /* Util.h */,
+ 343E67042107AD0100F95E5B /* Util.m */,
+ 34FAE219209F7DDD005CE2AE /* VideoViewController.h */,
+ 34FAE21A209F7DDD005CE2AE /* VideoViewController.m */,
+ 340FABBC2115A17600B33CE7 /* VidStabViewController.h */,
+ 340FABBD2115A17600B33CE7 /* VidStabViewController.m */,
);
path = MobileFFmpegTest;
sourceTree = "";
@@ -192,6 +246,7 @@
buildActionMask = 2147483647;
files = (
343E67092107C27600F95E5B /* TabBarController.h in Headers */,
+ 3423D269217F7D4E00C3C7ED /* MediaInformationParserTests.h in Headers */,
3457454B2113B8AD00059043 /* AudioViewController.h in Headers */,
3457454F2113B8D300059043 /* SubtitleViewController.h in Headers */,
343E66B9210789DE00F95E5B /* Constants.h in Headers */,
@@ -345,6 +400,7 @@
343E66B6210784BD00F95E5B /* HttpsViewController.m in Sources */,
340FABBF2115A17600B33CE7 /* VidStabViewController.m in Sources */,
34FAE218209F7DDD005CE2AE /* CommandViewController.m in Sources */,
+ 3423D267217F7CC100C3C7ED /* MediaInformationParserTests.m in Sources */,
343E670A2107C27600F95E5B /* TabBarController.m in Sources */,
345745502113B8D300059043 /* SubtitleViewController.m in Sources */,
343E67062107AD0100F95E5B /* Util.m in Sources */,
diff --git a/ios/test-app/MobileFFmpegTest.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate b/ios/test-app/MobileFFmpegTest.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate
index 6240bb8fa..f79875b88 100644
Binary files a/ios/test-app/MobileFFmpegTest.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate and b/ios/test-app/MobileFFmpegTest.xcworkspace/xcuserdata/taner.xcuserdatad/UserInterfaceState.xcuserstate differ
diff --git a/ios/test-app/MobileFFmpegTest/MediaInformationParserTests.h b/ios/test-app/MobileFFmpegTest/MediaInformationParserTests.h
new file mode 100644
index 000000000..776451398
--- /dev/null
+++ b/ios/test-app/MobileFFmpegTest/MediaInformationParserTests.h
@@ -0,0 +1,25 @@
+//
+// MediaInformationParserTests.h
+//
+// Copyright (c) 2018 Taner Sener
+//
+// This file is part of MobileFFmpeg.
+//
+// MobileFFmpeg 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 3 of the License, or
+// (at your option) any later version.
+//
+// MobileFFmpeg 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 MobileFFmpeg. If not, see .
+//
+
+/**
+ * All parsing tests are initiated from this method
+ */
+void runMediaInformationParserTests();
diff --git a/ios/test-app/MobileFFmpegTest/MediaInformationParserTests.m b/ios/test-app/MobileFFmpegTest/MediaInformationParserTests.m
new file mode 100644
index 000000000..4ebdad7a7
--- /dev/null
+++ b/ios/test-app/MobileFFmpegTest/MediaInformationParserTests.m
@@ -0,0 +1,841 @@
+//
+// MediaInformationParserTests.m
+//
+// Copyright (c) 2018 Taner Sener
+//
+// This file is part of MobileFFmpeg.
+//
+// MobileFFmpeg 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 3 of the License, or
+// (at your option) any later version.
+//
+// MobileFFmpeg 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 MobileFFmpeg. If not, see .
+//
+
+#include
+
+#include
+
+static NSString *MEDIA_INFORMATION_MP3;
+static NSString *MEDIA_INFORMATION_JPG;
+static NSString *MEDIA_INFORMATION_GIF;
+static NSString *MEDIA_INFORMATION_H264;
+static NSString *MEDIA_INFORMATION_PNG;
+static NSString *MEDIA_INFORMATION_H264_2;
+static NSString *MEDIA_INFORMATION_MP4;
+static NSString *MEDIA_INFORMATION_MP4_2;
+static NSString *MEDIA_INFORMATION_OGG;
+
+static void initTests() {
+ MEDIA_INFORMATION_MP3 = [NSString stringWithFormat:
+ @"Unknown attached picture mimetype: audio/x-wav, skipping.\n"
+ "[mp3 @ 0x7ffb94805800] Estimating duration from bitrate, this may be inaccurate\n"
+ "Input #0, mp3, from 'beethoven_-_symphony_no_9.mp3':\n"
+ " Metadata:\n"
+ " comment : \n"
+ " album : Symphony No.9\n"
+ " compilation : 0\n"
+ " date : -1\n"
+ " title : Symphony No.9\n"
+ " artist : Beethoven\n"
+ " album_artist : Beethoven\n"
+ " track : -1\n"
+ " lyrics-XXX : \n"
+ " Duration: 00:03:33.24, start: 0.000000, bitrate: 320 kb/s\n"
+ " Stream #0:0: Audio: mp3, 48000 Hz, stereo, fltp, 320 kb/s\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (mp3 (mp3float) -> pcm_s16le (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " comment : \n"
+ " album : Symphony No.9\n"
+ " compilation : 0\n"
+ " date : -1\n"
+ " title : Symphony No.9\n"
+ " artist : Beethoven\n"
+ " album_artist : Beethoven\n"
+ " track : -1\n"
+ " lyrics-XXX : \n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0: Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s\n"
+ " Metadata:\n"
+ " encoder : Lavc58.18.100 pcm_s16le\n"
+ "size=N/A time=00:03:33.24 bitrate=N/A speed= 618x \n"
+ "video:0kB audio:39982kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+
+ MEDIA_INFORMATION_JPG = [NSString stringWithFormat:
+ @"Input #0, image2, from '/data/user/0/com.arthenica.mobileffmpeg.test/cache/colosseum.jpg':\n"
+ " Duration: 00:00:00.04, start: 0.000000, bitrate: 391187 kb/s\n"
+ " Stream #0:0: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 2560x1708 [SAR 1:1 DAR 640:427], 25 tbr, 25 tbn, 25 tbc\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (mjpeg (native) -> wrapped_avframe (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " encoder : Lavf58.18.104\n"
+ " Stream #0:0: Video: wrapped_avframe, yuvj420p, 2560x1708 [SAR 1:1 DAR 640:427], q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc\n"
+ " Metadata:\n"
+ " encoder : Lavc58.31.102 wrapped_avframe\n"
+ "frame= 1 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.04 bitrate=N/A speed=0.668x \n"
+ "video:0kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown\n"];
+
+ MEDIA_INFORMATION_GIF = [NSString stringWithFormat:
+ @"Input #0, gif, from 'advanced_zoom_in_and_pan_with_fade_in_out.gif':\n"
+ " Duration: N/A, bitrate: N/A\n"
+ " Stream #0:0: Video: gif, bgra, 420x236, 6 fps, 6 tbr, 100 tbn, 100 tbc\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (gif (native) -> wrapped_avframe (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0: Video: wrapped_avframe, bgra, 420x236 [SAR 63:64 DAR 6615:3776], q=2-31, 200 kb/s, 6 fps, 6 tbn, 6 tbc\n"
+ " Metadata:\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ "frame= 61 fps=0.0 q=-0.0 Lsize=N/A time=00:00:10.16 bitrate=N/A speed= 219x \n"
+ "video:32kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+
+ MEDIA_INFORMATION_H264 = [NSString stringWithFormat:
+ @"Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'transition_rotate.mp4':\n"
+ " Metadata:\n"
+ " major_brand : isom\n"
+ " minor_version : 512\n"
+ " compatible_brands: isomiso2avc1mp41\n"
+ " encoder : Lavf58.12.100\n"
+ " Duration: 00:00:15.00, start: 0.000000, bitrate: 7764 kb/s\n"
+ " Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 7762 kb/s, 25 fps, 30 tbr, 15360 tbn, 60 tbc (default)\n"
+ " Metadata:\n"
+ " handler_name : VideoHandler\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (h264 (native) -> wrapped_avframe (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " major_brand : isom\n"
+ " minor_version : 512\n"
+ " compatible_brands: isomiso2avc1mp41\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0(und): Video: wrapped_avframe, yuv420p, 1280x720 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 30 fps, 30 tbn, 30 tbc (default)\n"
+ " Metadata:\n"
+ " handler_name : VideoHandler\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ "frame= 375 fps=0.0 q=-0.0 Lsize=N/A time=00:00:15.00 bitrate=N/A speed=35.9x \n"
+ "video:196kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+
+ MEDIA_INFORMATION_PNG = [NSString stringWithFormat:
+ @"Input #0, png_pipe, from 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png':\n"
+ " Duration: N/A, bitrate: N/A\n"
+ " Stream #0:0: Video: png, rgba(pc), 544x184, 25 tbr, 25 tbn, 25 tbc\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (png (native) -> wrapped_avframe (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0: Video: wrapped_avframe, rgba, 544x184, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc\n"
+ " Metadata:\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ "frame= 1 fps=0.0 q=-0.0 Lsize=N/A time=00:00:00.04 bitrate=N/A speed=27.1x \n"
+ "video:1kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+
+ MEDIA_INFORMATION_H264_2 = [NSString stringWithFormat:
+ @"Input #0, h264, from 'test.h264':\n"
+ " Duration: N/A, bitrate: N/A\n"
+ " Stream #0:0: Video: h264 (Main), yuv420p(tv, bt709, progressive), 1920x1080, 25 fps, 25 tbr, 1200k tbn, 50 tbc\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (h264 (native) -> wrapped_avframe (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0: Video: wrapped_avframe, yuv420p, 1920x1080, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc\n"
+ " Metadata:\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ "frame= 360 fps=0.0 q=-0.0 Lsize=N/A time=00:00:14.40 bitrate=N/A speed=25.5x \n"
+ "video:188kB audio:0kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+
+ MEDIA_INFORMATION_MP4 = [NSString stringWithFormat:
+ @"Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_2160p_30fps_stereo_abl.mp4':\n"
+ " Metadata:\n"
+ " major_brand : isom\n"
+ " minor_version : 1\n"
+ " compatible_brands: isomavc1\n"
+ " creation_time : 2013-12-16T17:21:55.000000Z\n"
+ " title : Big Buck Bunny, Sunflower version\n"
+ " artist : Blender Foundation 2008, Janus Bager Kristensen 2013\n"
+ " comment : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net\n"
+ " genre : Animation\n"
+ " composer : Sacha Goedegebure\n"
+ " Duration: 00:10:34.53, start: 0.000000, bitrate: 10385 kb/s\n"
+ " Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 3840x4320 [SAR 1:1 DAR 8:9], 9902 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:21:55.000000Z\n"
+ " handler_name : GPAC ISO Video Handler\n"
+ " Stream #0:1(und): Audio: mp3 (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:21:58.000000Z\n"
+ " handler_name : GPAC ISO Audio Handler\n"
+ " Stream #0:2(und): Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 320 kb/s (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:21:58.000000Z\n"
+ " handler_name : GPAC ISO Audio Handler\n"
+ " Side data:\n"
+ " audio service type: main\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (h264 (native) -> wrapped_avframe (native))\n"
+ " Stream #0:2 -> #0:1 (ac3 (native) -> pcm_s16le (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " major_brand : isom\n"
+ " minor_version : 1\n"
+ " compatible_brands: isomavc1\n"
+ " composer : Sacha Goedegebure\n"
+ " title : Big Buck Bunny, Sunflower version\n"
+ " artist : Blender Foundation 2008, Janus Bager Kristensen 2013\n"
+ " comment : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net\n"
+ " genre : Animation\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0(und): Video: wrapped_avframe, yuv420p(progressive), 3840x4320 [SAR 1:1 DAR 8:9], q=2-31, 200 kb/s, 30 fps, 30 tbn, 30 tbc (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:21:55.000000Z\n"
+ " handler_name : GPAC ISO Video Handler\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ " Stream #0:1(und): Audio: pcm_s16le, 48000 Hz, 5.1(side), s16, 4608 kb/s (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:21:58.000000Z\n"
+ " handler_name : GPAC ISO Audio Handler\n"
+ " encoder : Lavc58.18.100 pcm_s16le\n"
+ " Side data:\n"
+ " audio service type: main\n"
+ "frame= 2798 fps= 85 q=-0.0 size=N/A time=00:01:33.33 bitrate=N/A speed=2.85x \n"];
+
+ MEDIA_INFORMATION_MP4_2 = [NSString stringWithFormat:
+ @"Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_stereo_arcd.mp4':\n"
+ " Metadata:\n"
+ " major_brand : isom\n"
+ " minor_version : 1\n"
+ " compatible_brands: isomavc1\n"
+ " creation_time : 2013-12-16T17:49:59.000000Z\n"
+ " title : Big Buck Bunny, Sunflower version\n"
+ " artist : Blender Foundation 2008, Janus Bager Kristensen 2013\n"
+ " comment : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net\n"
+ " genre : Animation\n"
+ " composer : Sacha Goedegebure\n"
+ " Duration: 00:10:34.53, start: 0.000000, bitrate: 4474 kb/s\n"
+ " Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 3992 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:49:59.000000Z\n"
+ " handler_name : GPAC ISO Video Handler\n"
+ " Stream #0:1(und): Audio: mp3 (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:50:04.000000Z\n"
+ " handler_name : GPAC ISO Audio Handler\n"
+ " Stream #0:2(und): Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 320 kb/s (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:50:04.000000Z\n"
+ " handler_name : GPAC ISO Audio Handler\n"
+ " Side data:\n"
+ " audio service type: main\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (h264 (native) -> wrapped_avframe (native))\n"
+ " Stream #0:2 -> #0:1 (ac3 (native) -> pcm_s16le (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " major_brand : isom\n"
+ " minor_version : 1\n"
+ " compatible_brands: isomavc1\n"
+ " composer : Sacha Goedegebure\n"
+ " title : Big Buck Bunny, Sunflower version\n"
+ " artist : Blender Foundation 2008, Janus Bager Kristensen 2013\n"
+ " comment : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net\n"
+ " genre : Animation\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0(und): Video: wrapped_avframe, yuv420p(progressive), 1920x1080 [SAR 1:1 DAR 16:9], q=2-31, 200 kb/s, 30 fps, 30 tbn, 30 tbc (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:49:59.000000Z\n"
+ " handler_name : GPAC ISO Video Handler\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ " Stream #0:1(und): Audio: pcm_s16le, 48000 Hz, 5.1(side), s16, 4608 kb/s (default)\n"
+ " Metadata:\n"
+ " creation_time : 2013-12-16T17:50:04.000000Z\n"
+ " handler_name : GPAC ISO Audio Handler\n"
+ " encoder : Lavc58.18.100 pcm_s16le\n"
+ " Side data:\n"
+ " audio service type: main\n"
+ "frame=19036 fps=401 q=-0.0 Lsize=N/A time=00:10:34.60 bitrate=N/A speed=13.4x \n"
+ "video:9964kB audio:356706kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+
+ MEDIA_INFORMATION_OGG = [NSString stringWithFormat:
+ @"[theora @ 0x7fa30c026e00] 7 bits left in packet 82\n"
+ "[ogg @ 0x7fa30c005e00] Broken file, keyframe not correctly marked.\n"
+ "Input #0, ogg, from 'trailer_400p.ogg':\n"
+ " Duration: 00:00:33.00, start: 0.000000, bitrate: 1057 kb/s\n"
+ " Stream #0:0: Video: theora, yuv420p(bt470bg/bt470bg/bt709), 720x400, 25 fps, 25 tbr, 25 tbn, 25 tbc\n"
+ " Metadata:\n"
+ " ENCODER : ffmpeg2theora 0.19\n"
+ " Stream #0:1: Audio: vorbis, 48000 Hz, stereo, fltp, 80 kb/s\n"
+ " Metadata:\n"
+ " ENCODER : ffmpeg2theora 0.19\n"
+ "[theora @ 0x7fa30c1bd600] 7 bits left in packet 82\n"
+ "Stream mapping:\n"
+ " Stream #0:0 -> #0:0 (theora (native) -> wrapped_avframe (native))\n"
+ " Stream #0:1 -> #0:1 (vorbis (native) -> pcm_s16le (native))\n"
+ "Press [q] to stop, [?] for help\n"
+ "Output #0, null, to 'pipe:':\n"
+ " Metadata:\n"
+ " encoder : Lavf58.12.100\n"
+ " Stream #0:0: Video: wrapped_avframe, yuv420p(progressive), 720x400, q=2-31, 200 kb/s, 25 fps, 25 tbn, 25 tbc\n"
+ " Metadata:\n"
+ " encoder : Lavc58.18.100 wrapped_avframe\n"
+ " Stream #0:1: Audio: pcm_s16le, 48000 Hz, stereo, s16, 1536 kb/s\n"
+ " Metadata:\n"
+ " encoder : Lavc58.18.100 pcm_s16le\n"
+ "[ogg @ 0x7fa30c005e00] Broken file, keyframe not correctly marked.\n"
+ " Last message repeated 5 times\n"
+ "frame= 813 fps=0.0 q=-0.0 Lsize=N/A time=00:00:33.01 bitrate=N/A speed= 234x \n"
+ "video:426kB audio:6190kB subtitle:0kB other streams:0kB global headers:0kB muxing overhead: unknown"];
+}
+
+void assertNumber(NSNumber *expected, NSNumber *real) {
+ if (expected == nil) {
+ assert(real == nil);
+ } else {
+ assert([expected isEqualToNumber:real]);
+ }
+}
+
+void assertString(NSString *expected, NSString *real) {
+ if (expected == nil) {
+ assert(real == nil);
+ } else {
+ assert([expected isEqualToString:real]);
+ }
+}
+
+void assertVideoStream(StreamInformation *stream, NSNumber *index, NSString *codec, NSString *fullCodec, NSString *format, NSString *fullFormat, NSNumber *width, NSNumber *height, NSString *sampleAspectRatio, NSString *displayAspectRatio, NSNumber *bitrate, NSString *averageFrameRate, NSString *realFrameRate, NSString *timeBase, NSString *codecTimeBase) {
+
+ assert(stream != nil);
+ assertNumber(index, [stream getIndex]);
+ assertString(@"video", [stream getType]);
+ assertString(codec, [stream getCodec]);
+ assertString(fullCodec, [stream getFullCodec]);
+
+ assertString(format, [stream getFormat]);
+ assertString(fullFormat, [stream getFullFormat]);
+
+ assertNumber(width, [stream getWidth]);
+ assertNumber(height, [stream getHeight]);
+ assertString(sampleAspectRatio, [stream getSampleAspectRatio]);
+ assertString(displayAspectRatio, [stream getDisplayAspectRatio]);
+
+ assertNumber(bitrate, [stream getBitrate]);
+ assertString(averageFrameRate, [stream getAverageFrameRate]);
+ assertString(realFrameRate, [stream getRealFrameRate]);
+ assertString(timeBase, [stream getTimeBase]);
+ assertString(codecTimeBase, [stream getCodecTimeBase]);
+}
+
+void parseVideoStreamBlock(NSString *input, NSNumber *index, NSString *codec, NSString *fullCodec, NSString *format, NSString *fullFormat, NSNumber *width, NSNumber *height, NSString *sampleAspectRatio, NSString *displayAspectRatio, NSNumber *bitrate, NSString *averageFrameRate, NSString *realFrameRate, NSString *timeBase, NSString *codecTimeBase) {
+ StreamInformation *stream = [MediaInformationParser parseStreamBlock:input];
+
+ assertVideoStream(stream, index, codec, fullCodec, format, fullFormat, width, height, sampleAspectRatio, displayAspectRatio, bitrate, averageFrameRate, realFrameRate, timeBase, codecTimeBase);
+}
+
+void assertAudioStream(StreamInformation *stream, NSNumber *index, NSString *codec, NSString *fullCodec, NSNumber *sampleRate, NSString *channelLayout, NSString *sampleFormat, NSNumber *bitrate) {
+
+ assert(stream != nil);
+ assertNumber(index, [stream getIndex]);
+ assertString(@"audio", [stream getType]);
+ assertString(codec, [stream getCodec]);
+ assertString(fullCodec, [stream getFullCodec]);
+ assertNumber(sampleRate, [stream getSampleRate]);
+ assertString(channelLayout, [stream getChannelLayout]);
+ assertString(sampleFormat, [stream getSampleFormat]);
+ assertNumber(bitrate, [stream getBitrate]);
+}
+
+void parseAudioStreamBlock(NSString *input, NSNumber *index, NSString *codec, NSString *fullCodec, NSNumber *sampleRate, NSString *channelLayout, NSString *sampleFormat, NSNumber *bitrate) {
+ StreamInformation *stream = [MediaInformationParser parseStreamBlock:input];
+
+ assertAudioStream(stream, index, codec, fullCodec, sampleRate, channelLayout, sampleFormat, bitrate);
+}
+
+void parseVideoDimensions(NSString *input, NSNumber *expectedWidth, NSNumber *expectedHeight) {
+ void(^pair)(NSNumber **, NSNumber **) = [MediaInformationParser parseVideoDimensions:input];
+
+ NSNumber *width;
+ NSNumber *height;
+ pair(&width, &height);
+
+ assert(pair != nil);
+ if (width == nil) {
+ assert(expectedWidth == nil);
+ } else {
+ assert([width isEqualToNumber:expectedWidth]);
+ }
+ if (height == nil) {
+ assert(expectedHeight == nil);
+ } else {
+ assert([height isEqualToNumber:expectedHeight]);
+ }
+}
+
+void parseDuration(NSString *input, NSNumber *expectedDuration) {
+ NSNumber *duration = [MediaInformationParser parseDuration:input];
+
+ if (duration == nil) {
+ assert(expectedDuration == nil);
+ } else {
+ assert([duration isEqualToNumber:expectedDuration]);
+ }
+}
+
+void parseStartTime(NSString *input, NSNumber *expectedStartTime) {
+ NSNumber *startTime = [MediaInformationParser parseStartTime:input];
+
+ if (startTime == nil) {
+ assert(expectedStartTime == nil);
+ } else {
+ assert([startTime isEqualToNumber:expectedStartTime]);
+ }
+}
+
+void parseDurationBlock(NSString *input, NSNumber *expectedDuration, NSNumber *expectedStartTime, NSNumber *expectedBitrate) {
+ void(^trio)(NSNumber **, NSNumber **, NSNumber **) = [MediaInformationParser parseDurationBlock:input];
+
+ NSNumber *duration;
+ NSNumber *startTime;
+ NSNumber *bitrate;
+ trio(&duration, &startTime, &bitrate);
+
+ assert(trio != nil);
+ if (duration == nil) {
+ assert(expectedDuration == nil);
+ } else {
+ assert([duration isEqualToNumber:expectedDuration]);
+ }
+ if (startTime == nil) {
+ assert(expectedStartTime == nil);
+ } else {
+ assert([startTime isEqualToNumber:expectedStartTime]);
+ }
+ if (bitrate == nil) {
+ assert(expectedBitrate == nil);
+ } else {
+ assert([bitrate isEqualToNumber:expectedBitrate]);
+ }
+}
+
+void parseMetadataBlock(NSString *input, NSString *expectedKey, NSString *expectedValue) {
+ void(^pair)(NSString **, NSString **) = [MediaInformationParser parseMetadataBlock:input];
+
+ NSString *key;
+ NSString *value;
+ pair(&key, &value);
+
+ assert(pair != nil);
+ assert(key != nil);
+ assert(value != nil);
+ assert([key isEqualToString:expectedKey]);
+ assert([value isEqualToString:expectedValue]);
+}
+
+void parseInputBlock(NSString *input, NSString *expectedFormat, NSString *expectedPath) {
+ void(^pair)(NSString **, NSString **) = [MediaInformationParser parseInputBlock:input];
+
+ NSString *format;
+ NSString *path;
+ pair(&format, &path);
+
+ assert(pair != nil);
+ if (format == nil) {
+ assert(expectedFormat == nil);
+ } else {
+ assert([format isEqualToString:expectedFormat]);
+ }
+ if (path == nil) {
+ assert(expectedPath == nil);
+ } else {
+ assert([path isEqualToString:expectedPath]);
+ }
+}
+
+void testParseVideoStream() {
+ parseVideoStreamBlock(@" Stream #0:0: Video: mjpeg, yuvj420p(pc, bt470bg/unknown/unknown), 2560x1708 [SAR 1:1 DAR 640:427], 25 tbr, 25 tbn, 25 tbc", [[NSNumber alloc] initWithInt:0], @"mjpeg", @"mjpeg", @"yuvj420p", @"yuvj420p(pc, bt470bg/unknown/unknown)", [[NSNumber alloc] initWithInt:2560], [[NSNumber alloc] initWithInt:1708], @"1:1", @"640:427", nil, nil, @"25", @"25", @"25");
+ parseVideoStreamBlock(@" Stream #0:0: Video: gif, bgra, 420x236, 6 fps, 6 tbr, 100 tbn, 100 tbc", [[NSNumber alloc] initWithInt:0], @"gif", @"gif", @"bgra", @"bgra", [[NSNumber alloc] initWithInt:420], [[NSNumber alloc] initWithInt:236], nil, nil, nil, @"6", @"6", @"100", @"100");
+ parseVideoStreamBlock(@" Stream #0:0(und): Video: h264 (Main) (avc1 / 0x31637661), yuv420p, 1280x720 [SAR 1:1 DAR 16:9], 7762 kb/s, 25 fps, 30 tbr, 15360 tbn, 60 tbc (default)", [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (main) (avc1 / 0x31637661)", @"yuv420p", @"yuv420p", [[NSNumber alloc] initWithInt:1280], [[NSNumber alloc] initWithInt:720], @"1:1", @"16:9", [[NSNumber alloc] initWithInt:7762], @"25", @"30", @"15360", @"60");
+ parseVideoStreamBlock(@" Stream #0:0: Video: png, rgba(pc), 544x184, 25 tbr, 25 tbn, 25 tbc", [[NSNumber alloc] initWithInt:0], @"png", @"png", @"rgba", @"rgba(pc)", [[NSNumber alloc] initWithInt:544], [[NSNumber alloc] initWithInt:184], nil, nil, nil, nil, @"25", @"25", @"25");
+ parseVideoStreamBlock(@" Stream #0:0: Video: h264 (Main), yuv420p(tv, bt709, progressive), 1920x1080, 25 fps, 25 tbr, 1200k tbn, 50 tbc", [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (main)", @"yuv420p", @"yuv420p(tv, bt709, progressive)", [[NSNumber alloc] initWithInt:1920], [[NSNumber alloc] initWithInt:1080], nil, nil, nil, @"25", @"25", @"1200k", @"50");
+ parseVideoStreamBlock(@" Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 3840x4320 [SAR 1:1 DAR 8:9], 9902 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)", [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (high) (avc1 / 0x31637661)", @"yuv420p", @"yuv420p", [[NSNumber alloc] initWithInt:3840], [[NSNumber alloc] initWithInt:4320], @"1:1", @"8:9", [[NSNumber alloc] initWithInt:9902], @"30", @"30", @"30k", @"60");
+ parseVideoStreamBlock(@" Stream #0:0(und): Video: h264 (High) (avc1 / 0x31637661), yuv420p, 1920x1080 [SAR 1:1 DAR 16:9], 3992 kb/s, 30 fps, 30 tbr, 30k tbn, 60 tbc (default)", [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (high) (avc1 / 0x31637661)", @"yuv420p", @"yuv420p", [[NSNumber alloc] initWithInt:1920], [[NSNumber alloc] initWithInt:1080], @"1:1", @"16:9", [[NSNumber alloc] initWithInt:3992], @"30", @"30", @"30k", @"60");
+ parseVideoStreamBlock(@" Stream #0:0: Video: theora, yuv420p(bt470bg/bt470bg/bt709), 720x400, 25 fps, 25 tbr, 25 tbn, 25 tbc", [[NSNumber alloc] initWithInt:0], @"theora", @"theora", @"yuv420p", @"yuv420p(bt470bg/bt470bg/bt709)", [[NSNumber alloc] initWithInt:720], [[NSNumber alloc] initWithInt:400], nil, nil, nil, @"25", @"25", @"25", @"25");
+}
+
+void testParseAudioStream() {
+ parseAudioStreamBlock(@"Stream #0:0: Audio: adpcm_ms ([2][0][0][0] / 0x0002), 22050 Hz, stereo, s16, 176 kb/s", [[NSNumber alloc] initWithInt:0], @"adpcm_ms", @"adpcm_ms ([2][0][0][0] / 0x0002)", [[NSNumber alloc] initWithInt:22050], @"stereo", @"s16", [[NSNumber alloc] initWithInt:176]);
+ parseAudioStreamBlock(@"Stream #0:0: Audio: pcm_s16le ([1][0][0][0] / 0x0001), 44100 Hz, mono, s16, 705 kb/s",[[NSNumber alloc] initWithInt:0], @"pcm_s16le", @"pcm_s16le ([1][0][0][0] / 0x0001)", [[NSNumber alloc] initWithInt:44100], @"mono", @"s16", [[NSNumber alloc] initWithInt:705]);
+ parseAudioStreamBlock(@"Stream #0:0: Audio: pcm_s24le ([1][0][0][0] / 0x0001), 48000 Hz, mono, s32 (24 bit), 1152 kb/s", [[NSNumber alloc] initWithInt:0], @"pcm_s24le", @"pcm_s24le ([1][0][0][0] / 0x0001)", [[NSNumber alloc] initWithInt:48000], @"mono", @"s32 (24 bit)", [[NSNumber alloc] initWithInt:1152]);
+ parseAudioStreamBlock(@"Stream #0:0: Audio: mp3, 48000 Hz, stereo, fltp, 192 kb/s", [[NSNumber alloc] initWithInt:0], @"mp3", @"mp3", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:192]);
+ parseAudioStreamBlock(@"Stream #0:0: Audio: vorbis, 44100 Hz, stereo, fltp, 128 kb/s", [[NSNumber alloc] initWithInt:0], @"vorbis", @"vorbis", [[NSNumber alloc] initWithInt:44100], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:128]);
+ parseAudioStreamBlock(@"Stream #0:0: Audio: pcm_u8 ([1][0][0][0] / 0x0001), 44100 Hz, stereo, u8, 705 kb/s", [[NSNumber alloc] initWithInt:0], @"pcm_u8", @"pcm_u8 ([1][0][0][0] / 0x0001)", [[NSNumber alloc] initWithInt:44100], @"stereo", @"u8", [[NSNumber alloc] initWithInt:705]);
+ parseAudioStreamBlock(@"Stream #0:1(und): Audio: mp3 (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s (default)", [[NSNumber alloc] initWithInt:1], @"mp3", @"mp3 (mp4a / 0x6134706d)", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:160]);
+ parseAudioStreamBlock(@"Stream #0:2(und): Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 320 kb/s (default)", [[NSNumber alloc] initWithInt:2], @"ac3", @"ac3 (ac-3 / 0x332d6361)", [[NSNumber alloc] initWithInt:48000], @"5.1(side)", @"fltp", [[NSNumber alloc] initWithInt:320]);
+ parseAudioStreamBlock(@"Stream #0:1(und): Audio: mp3 (mp4a / 0x6134706D), 48000 Hz, stereo, fltp, 160 kb/s (default)", [[NSNumber alloc] initWithInt:1], @"mp3", @"mp3 (mp4a / 0x6134706d)", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:160]);
+ parseAudioStreamBlock(@"Stream #0:2(und): Audio: ac3 (ac-3 / 0x332D6361), 48000 Hz, 5.1(side), fltp, 320 kb/s (default)", [[NSNumber alloc] initWithInt:2], @"ac3", @"ac3 (ac-3 / 0x332d6361)", [[NSNumber alloc] initWithInt:48000], @"5.1(side)", @"fltp", [[NSNumber alloc] initWithInt:320]);
+}
+
+void testIndex() {
+ assert(7 == [MediaInformationParser index:@"one:two:three:" of:@":" from:0 times:2]);
+ assert(13 == [MediaInformationParser index:@"one:two:three:" of:@":" from:0 times:3]);
+ assert(8 == [MediaInformationParser index:@"one::two::three::" of:@"::" from:0 times:2]);
+}
+
+void testCount() {
+ assert(3 == [MediaInformationParser count:@"one:two:three:" of:@":"]);
+ assert(2 == [MediaInformationParser count:@"one,two:three,four" of:@","]);
+}
+
+void testParseVideoStreamDisplayAspectRatio() {
+ assert(nil == [MediaInformationParser parseVideoStreamDisplayAspectRatio:@""]);
+ assert(nil == [MediaInformationParser parseVideoStreamDisplayAspectRatio:@"544x184"]);
+ assert([@"640:427" isEqualToString:[MediaInformationParser parseVideoStreamDisplayAspectRatio:@"2560x1708 [SAR 1:1 DAR 640:427]"]]);
+ assert([@"8:9" isEqualToString:[MediaInformationParser parseVideoStreamDisplayAspectRatio:@"3840x4320 [SAR 1:1 DAR 8:9]"]]);
+}
+
+void testParseVideoStreamSampleAspectRatio() {
+ assert(nil == [MediaInformationParser parseVideoStreamSampleAspectRatio:@""]);
+ assert(nil == [MediaInformationParser parseVideoStreamSampleAspectRatio:@"544x184"]);
+ assert([@"1:1" isEqualToString:[MediaInformationParser parseVideoStreamSampleAspectRatio:@"2560x1708 [SAR 1:1 DAR 640:427]"]]);
+ assert([@"1:1" isEqualToString:[MediaInformationParser parseVideoStreamSampleAspectRatio:@"3840x4320 [SAR 1:1 DAR 8:9]"]]);
+}
+
+void testParseVideoDimensions() {
+ parseVideoDimensions(@"", nil, nil);
+ parseVideoDimensions(@"544x184", [[NSNumber alloc] initWithInt:544], [[NSNumber alloc] initWithInt:184]);
+ parseVideoDimensions(@"720x400", [[NSNumber alloc] initWithInt:720], [[NSNumber alloc] initWithInt:400]);
+ parseVideoDimensions(@"2560x1708 [SAR 1:1 DAR 640:427]", [[NSNumber alloc] initWithInt:2560], [[NSNumber alloc] initWithInt:1708]);
+ parseVideoDimensions(@"3840x4320 [SAR 1:1 DAR 8:9]", [[NSNumber alloc] initWithInt:3840], [[NSNumber alloc] initWithInt:4320]);
+}
+
+void testParseAudioSampleRate() {
+ assert([[[NSNumber alloc] initWithInt:44000] isEqualToNumber:[MediaInformationParser parseAudioStreamSampleRate:@"44000"]]);
+ assert([[[NSNumber alloc] initWithInt:44000] isEqualToNumber:[MediaInformationParser parseAudioStreamSampleRate:@"44 khz"]]);
+ assert([[[NSNumber alloc] initWithInt:44100] isEqualToNumber:[MediaInformationParser parseAudioStreamSampleRate:@"44100"]]);
+ assert([[[NSNumber alloc] initWithInt:5000000] isEqualToNumber:[MediaInformationParser parseAudioStreamSampleRate:@"5 mhz"]]);
+}
+
+void testParseAudioStreamType() {
+ assert([@"video" isEqualToString:[MediaInformationParser parseStreamType:@"Video: theora"]]);
+ assert([@"video" isEqualToString:[MediaInformationParser parseStreamType:@"Video: png"]]);
+ assert([@"video" isEqualToString:[MediaInformationParser parseStreamType:@"Video: h264 (Main)"]]);
+ assert([@"audio" isEqualToString:[MediaInformationParser parseStreamType:@"Audio: adpcm_ms ([2][0][0][0] / 0x0002)"]]);
+ assert([@"audio" isEqualToString:[MediaInformationParser parseStreamType:@"Audio: mp3 (mp4a / 0x6134706D)"]]);
+ assert([@"audio" isEqualToString:[MediaInformationParser parseStreamType:@"Audio: pcm_u8 ([1][0][0][0] / 0x0001)"]]);
+}
+
+void testParseStreamCodec() {
+ assert([@"theora" isEqualToString:[MediaInformationParser parseStreamCodec:@"Video: theora"]]);
+ assert([@"png" isEqualToString:[MediaInformationParser parseStreamCodec:@"Video: png"]]);
+ assert([@"h264" isEqualToString:[MediaInformationParser parseStreamCodec:@"Video: h264 (Main)"]]);
+ assert([@"adpcm_ms" isEqualToString:[MediaInformationParser parseStreamCodec:@"Audio: adpcm_ms ([2][0][0][0] / 0x0002)"]]);
+ assert([@"mp3" isEqualToString:[MediaInformationParser parseStreamCodec:@"Audio: mp3 (mp4a / 0x6134706D)"]]);
+ assert([@"pcm_u8" isEqualToString:[MediaInformationParser parseStreamCodec:@"Audio: pcm_u8 ([1][0][0][0] / 0x0001)"]]);
+}
+
+void testParseStreamFullCodec() {
+ assert([@"theora" isEqualToString:[MediaInformationParser parseStreamFullCodec:@"Video: theora"]]);
+ assert([@"png" isEqualToString:[MediaInformationParser parseStreamFullCodec:@"Video: png"]]);
+ assert([@"h264 (main)" isEqualToString:[MediaInformationParser parseStreamFullCodec:@"Video: h264 (Main)"]]);
+ assert([@"adpcm_ms ([2][0][0][0] / 0x0002)" isEqualToString:[MediaInformationParser parseStreamFullCodec:@"Audio: adpcm_ms ([2][0][0][0] / 0x0002)"]]);
+ assert([@"mp3 (mp4a / 0x6134706d)" isEqualToString:[MediaInformationParser parseStreamFullCodec:@"Audio: mp3 (mp4a / 0x6134706D)"]]);
+ assert([@"pcm_u8 ([1][0][0][0] / 0x0001)" isEqualToString:[MediaInformationParser parseStreamFullCodec:@"Audio: pcm_u8 ([1][0][0][0] / 0x0001)"]]);
+}
+
+void testParseStreamIndex() {
+ assert([[[NSNumber alloc] initWithInt: 0] isEqualToNumber:[MediaInformationParser parseStreamIndex:@"Stream #0:0(und): Audio"]]);
+ assert([[[NSNumber alloc] initWithInt: 1] isEqualToNumber:[MediaInformationParser parseStreamIndex:@"Stream #0:1(und): Video"]]);
+ assert([[[NSNumber alloc] initWithInt: 2] isEqualToNumber:[MediaInformationParser parseStreamIndex:@"Stream #0:2(und): Audio"]]);
+ assert([[[NSNumber alloc] initWithInt: 0] isEqualToNumber:[MediaInformationParser parseStreamIndex:@"Stream #0:0: Video"]]);
+ assert([[[NSNumber alloc] initWithInt: 1] isEqualToNumber:[MediaInformationParser parseStreamIndex:@"Stream #0:1: Audio"]]);
+ assert([[[NSNumber alloc] initWithInt: 2] isEqualToNumber:[MediaInformationParser parseStreamIndex:@"Stream #0:2: Video"]]);
+}
+
+void testParseDuration() {
+ parseDuration(nil, nil);
+ parseDuration(@"", nil);
+ parseDuration(@"N/A", nil);
+ parseDuration(@"00:03:33.24", [[NSNumber alloc] initWithInt: 213240]);
+ parseDuration(@"00:10:34.53", [[NSNumber alloc] initWithInt: 634530]);
+ parseDuration(@"00:00:00.04", [[NSNumber alloc] initWithInt: 40]);
+ parseDuration(@"00:00:15.00", [[NSNumber alloc] initWithInt: 15000]);
+}
+
+void testParseStartTime() {
+ parseStartTime(nil, nil);
+ parseStartTime(@"", nil);
+ parseStartTime(@"N/A", nil);
+ parseStartTime(@"0.000000", [[NSNumber alloc] initWithInt:0]);
+ parseStartTime(@"10.003000", [[NSNumber alloc] initWithInt:10003]);
+ parseStartTime(@"324.000000", [[NSNumber alloc] initWithInt:324000]);
+ parseStartTime(@"-4.000000", [[NSNumber alloc] initWithInt:-4000]);
+ parseStartTime(@"14.00030", [[NSNumber alloc] initWithInt:14001]);
+ parseStartTime(@"14.00080", [[NSNumber alloc] initWithInt:14001]);
+}
+
+void testParseDurationBlock() {
+ parseDurationBlock(@" Duration: 00:03:33.24, start: 0.000000, bitrate: 320 kb/s", [[NSNumber alloc] initWithInt:213240], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:320]);
+ parseDurationBlock(@" Duration: 00:00:00.04, start: 0.000000, bitrate: 391187 kb/s", [[NSNumber alloc] initWithInt:40], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:391187]);
+ parseDurationBlock(@" Duration: N/A, bitrate: N/A", nil, nil, nil);
+ parseDurationBlock(@" Duration: 00:00:15.00, start: 0.000000, bitrate: 7764 kb/s", [[NSNumber alloc] initWithInt:15000], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:7764]);
+ parseDurationBlock(@" Duration: 00:10:34.53, start: 0.000000, bitrate: 4474 kb/s", [[NSNumber alloc] initWithInt:634530], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:4474]);
+}
+
+void testParseInputBlock() {
+ parseInputBlock(@"Input #0,", nil, nil);
+ parseInputBlock(@"Input #0, ogg, from 'trailer_400p.ogg':", @"ogg", @"trailer_400p.ogg");
+ parseInputBlock(@"Input #0, mp3, from 'beethoven_-_symphony_no_9.mp3':", @"mp3", @"beethoven_-_symphony_no_9.mp3");
+ parseInputBlock(@"Input #0, image2, from '/data/user/0/com.arthenica.mobileffmpeg.test/cache/colosseum.jpg':", @"image2", @"/data/user/0/com.arthenica.mobileffmpeg.test/cache/colosseum.jpg");
+ parseInputBlock(@"Input #0, gif, from 'advanced_zoom_in_and_pan_with_fade_in_out.gif':", @"gif", @"advanced_zoom_in_and_pan_with_fade_in_out.gif");
+ parseInputBlock(@"Input #0, mov,mp4,m4a,3gp,3g2,mj2, from 'transition_rotate.mp4':", @"mov,mp4,m4a,3gp,3g2,mj2", @"transition_rotate.mp4");
+ parseInputBlock(@"Input #0, png_pipe, from 'https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png':", @"png_pipe", @"https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png");
+}
+
+void testParseMetadataBlock() {
+ parseMetadataBlock(@" ENCODER:", @"ENCODER", @"");
+ parseMetadataBlock(@" ENCODER:ffmpeg2theora 0.19", @"ENCODER", @"ffmpeg2theora 0.19");
+ parseMetadataBlock(@" ENCODER : ffmpeg2theora 0.19", @"ENCODER", @"ffmpeg2theora 0.19");
+ parseMetadataBlock(@" creation_time : 2013-12-16T17:50:04.000000Z", @"creation_time", @"2013-12-16T17:50:04.000000Z");
+ parseMetadataBlock(@" handler_name : GPAC ISO Audio Handler", @"handler_name", @"GPAC ISO Audio Handler");
+ parseMetadataBlock(@" comment : Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net", @"comment", @"Creative Commons Attribution 3.0 - http://bbb3d.renderfarming.net");
+ parseMetadataBlock(@" minor_version : 1", @"minor_version", @"1");
+ parseMetadataBlock(@" encoder : Lavf58.12.100", @"encoder", @"Lavf58.12.100");
+ parseMetadataBlock(@" title : Planet X", @"title", @"Planet X");
+ parseMetadataBlock(@" compatible_brands: isomiso2avc1mp41", @"compatible_brands", @"isomiso2avc1mp41");
+}
+
+void assertNotNull(NSObject *object) {
+ assert(object != nil);
+}
+
+void assertMediaInput(MediaInformation *mediaInformation, NSString *expectedFormat, NSString *expectedPath) {
+ NSString *format = [mediaInformation getFormat];
+ NSString *path = [mediaInformation getPath];
+ if (format == nil) {
+ assert(expectedFormat == nil);
+ } else {
+ assert([format isEqualToString:expectedFormat]);
+ }
+ if (path == nil) {
+ assert(expectedPath == nil);
+ } else {
+ assert([path isEqualToString:expectedPath]);
+ }
+}
+
+void assertMediaDuration(MediaInformation *mediaInformation, NSNumber *expectedDuration, NSNumber *expectedStartTime, NSNumber *expectedBitrate) {
+ NSNumber *duration = [mediaInformation getDuration];
+ NSNumber *startTime = [mediaInformation getStartTime];
+ NSNumber *bitrate = [mediaInformation getBitrate];
+
+ if (duration == nil) {
+ assert(expectedDuration == nil);
+ } else {
+ assert([duration isEqualToNumber:expectedDuration]);
+ }
+ if (startTime == nil) {
+ assert(expectedStartTime == nil);
+ } else {
+ assert([startTime isEqualToNumber:expectedStartTime]);
+ }
+ if (bitrate == nil) {
+ assert(expectedBitrate == nil);
+ } else {
+ assert([bitrate isEqualToNumber:expectedBitrate]);
+ }
+}
+
+void testMediaInformationMp3() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_MP3];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"mp3", @"beethoven_-_symphony_no_9.mp3");
+ assertMediaDuration(mediaInformation, [[NSNumber alloc] initWithInt: 213240], [[NSNumber alloc] initWithInt: 0], [[NSNumber alloc] initWithInt: 320]);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(1 == [streams count]);
+
+ assertAudioStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"mp3", @"mp3", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:320]);
+}
+
+void testMediaInformationJpg() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_JPG];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"image2", @"/data/user/0/com.arthenica.mobileffmpeg.test/cache/colosseum.jpg");
+ assertMediaDuration(mediaInformation, [[NSNumber alloc] initWithInt:40], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:391187]);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(1 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"mjpeg", @"mjpeg", @"yuvj420p", @"yuvj420p(pc, bt470bg/unknown/unknown)", [[NSNumber alloc] initWithInt:2560], [[NSNumber alloc] initWithInt:1708], @"1:1", @"640:427", nil, nil, @"25", @"25", @"25");
+}
+
+void testMediaInformationGif() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_GIF];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"gif", @"advanced_zoom_in_and_pan_with_fade_in_out.gif");
+ assertMediaDuration(mediaInformation, nil, nil, nil);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(1 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"gif", @"gif", @"bgra", @"bgra", [[NSNumber alloc] initWithInt:420], [[NSNumber alloc] initWithInt:236], nil, nil, nil, @"6", @"6", @"100", @"100");
+}
+
+void testMediaInformationH264() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_H264];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"mov,mp4,m4a,3gp,3g2,mj2", @"transition_rotate.mp4");
+ assertMediaDuration(mediaInformation, [[NSNumber alloc] initWithInt:15000], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:7764]);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(1 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (main) (avc1 / 0x31637661)", @"yuv420p", @"yuv420p", [[NSNumber alloc] initWithInt:1280], [[NSNumber alloc] initWithInt:720], @"1:1", @"16:9", [[NSNumber alloc] initWithInt:7762], @"25", @"30", @"15360", @"60");
+}
+
+void testMediaInformationPng() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_PNG];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"png_pipe", @"https://www.google.com/images/branding/googlelogo/2x/googlelogo_color_272x92dp.png");
+ assertMediaDuration(mediaInformation, nil, nil, nil);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(1 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"png", @"png", @"rgba", @"rgba(pc)", [[NSNumber alloc] initWithInt:544], [[NSNumber alloc] initWithInt:184], nil, nil, nil, nil, @"25", @"25", @"25");
+}
+
+void testMediaInformationH2642() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_H264_2];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"h264", @"test.h264");
+ assertMediaDuration(mediaInformation, nil, nil, nil);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(1 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (main)", @"yuv420p", @"yuv420p(tv, bt709, progressive)", [[NSNumber alloc] initWithInt:1920], [[NSNumber alloc] initWithInt:1080], nil, nil, nil, @"25", @"25", @"1200k", @"50");
+}
+
+void testMediaInformationMp4() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_MP4];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"mov,mp4,m4a,3gp,3g2,mj2", @"http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_2160p_30fps_stereo_abl.mp4");
+ assertMediaDuration(mediaInformation, [[NSNumber alloc] initWithInt:634530], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:10385]);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(3 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (high) (avc1 / 0x31637661)", @"yuv420p", @"yuv420p", [[NSNumber alloc] initWithInt:3840], [[NSNumber alloc] initWithInt:4320], @"1:1", @"8:9", [[NSNumber alloc] initWithInt:9902], @"30", @"30", @"30k", @"60");
+ assertAudioStream([streams objectAtIndex:1], [[NSNumber alloc] initWithInt:1], @"mp3", @"mp3 (mp4a / 0x6134706d)", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:160]);
+ assertAudioStream([streams objectAtIndex:2], [[NSNumber alloc] initWithInt:2], @"ac3", @"ac3 (ac-3 / 0x332d6361)", [[NSNumber alloc] initWithInt:48000], @"5.1(side)", @"fltp", [[NSNumber alloc] initWithInt:320]);
+}
+
+void testMediaInformationMp42() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_MP4_2];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"mov,mp4,m4a,3gp,3g2,mj2", @"http://distribution.bbb3d.renderfarming.net/video/mp4/bbb_sunflower_1080p_30fps_stereo_arcd.mp4");
+ assertMediaDuration(mediaInformation, [[NSNumber alloc] initWithInt:634530], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:4474]);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(3 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"h264", @"h264 (high) (avc1 / 0x31637661)", @"yuv420p", @"yuv420p", [[NSNumber alloc] initWithInt:1920], [[NSNumber alloc] initWithInt:1080], @"1:1", @"16:9", [[NSNumber alloc] initWithInt:3992], @"30", @"30", @"30k", @"60");
+ assertAudioStream([streams objectAtIndex:1], [[NSNumber alloc] initWithInt:1], @"mp3", @"mp3 (mp4a / 0x6134706d)", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:160]);
+ assertAudioStream([streams objectAtIndex:2], [[NSNumber alloc] initWithInt:2], @"ac3", @"ac3 (ac-3 / 0x332d6361)", [[NSNumber alloc] initWithInt:48000], @"5.1(side)", @"fltp", [[NSNumber alloc] initWithInt:320]);
+}
+
+void testMediaInformationOgg() {
+ MediaInformation *mediaInformation = [MediaInformationParser from:MEDIA_INFORMATION_OGG];
+
+ assertNotNull(mediaInformation);
+ assertMediaInput(mediaInformation, @"ogg", @"trailer_400p.ogg");
+ assertMediaDuration(mediaInformation, [[NSNumber alloc] initWithInt:33000], [[NSNumber alloc] initWithInt:0], [[NSNumber alloc] initWithInt:1057]);
+
+ NSArray *streams = [mediaInformation getStreams];
+ assertNotNull(streams);
+ assert(2 == [streams count]);
+
+ assertVideoStream([streams objectAtIndex:0], [[NSNumber alloc] initWithInt:0], @"theora", @"theora", @"yuv420p", @"yuv420p(bt470bg/bt470bg/bt709)", [[NSNumber alloc] initWithInt:720], [[NSNumber alloc] initWithInt:400], nil, nil, nil, @"25", @"25", @"25", @"25");
+ assertAudioStream([streams objectAtIndex:1], [[NSNumber alloc] initWithInt:1], @"vorbis", @"vorbis", [[NSNumber alloc] initWithInt:48000], @"stereo", @"fltp", [[NSNumber alloc] initWithInt:80]);
+}
+
+/**
+ * All parsing tests are initiated from this method
+ */
+void runMediaInformationParserTests() {
+
+ initTests();
+
+ testParseMetadataBlock();
+ testParseInputBlock();
+
+ testParseDurationBlock();
+ testParseStartTime();
+ testParseDuration();
+
+ testParseStreamIndex();
+ testParseStreamFullCodec();
+ testParseStreamCodec();
+
+ testParseAudioStreamType();
+ testParseAudioSampleRate();
+ testParseVideoDimensions();
+
+ testParseVideoStreamSampleAspectRatio();
+ testParseVideoStreamDisplayAspectRatio();
+
+ testCount();
+ testIndex();
+
+ testParseAudioStream();
+ testParseVideoStream();
+
+ testMediaInformationMp3();
+ testMediaInformationJpg();
+ testMediaInformationGif();
+ testMediaInformationH264();
+ testMediaInformationPng();
+ testMediaInformationH2642();
+ testMediaInformationMp4();
+ testMediaInformationMp42();
+ testMediaInformationOgg();
+
+
+ NSLog(@"MediaInformationParserTests passed.");
+}
diff --git a/ios/test-app/MobileFFmpegTest/main.m b/ios/test-app/MobileFFmpegTest/main.m
index 23308268c..eaaa0ada6 100644
--- a/ios/test-app/MobileFFmpegTest/main.m
+++ b/ios/test-app/MobileFFmpegTest/main.m
@@ -20,10 +20,16 @@
//
#import
+
#import "AppDelegate.h"
+#import "MediaInformationParserTests.h"
int main(int argc, char * argv[]) {
@autoreleasepool {
+
+ // RUN UNIT TESTS BEFORE STARTING THE APPLICATION
+ runMediaInformationParserTests();
+
return UIApplicationMain(argc, argv, nil, NSStringFromClass([AppDelegate class]));
}
}