Browse code

avformat/id3v2: allow ID3 parsing without AVFormatContext

Add ff_id3v2_read_dict() for parsing without AVFormatContext, but
instead with AVIOContext and AVDictionary.

AVFormatContext is still used for logging, if available.

Chapter parsing is the only non-logging functionality that actually
needs AVFormatContext, and AFAICS it should be modified to write the
data to ID3v2ExtraMeta first, from where it can be implanted to
AVFormatContext by a separate function (like it is done with
read_apic() and ff_id3v2_parse_apic()). That is outside the scope of
this patch, though.

Signed-off-by: Anssi Hannula <anssi.hannula@iki.fi>

Anssi Hannula authored on 2013/12/30 17:02:59
Showing 2 changed files
... ...
@@ -532,6 +532,14 @@ static void read_chapter(AVFormatContext *s, AVIOContext *pb, int len, char *tta
532 532
     int taglen;
533 533
     char tag[5];
534 534
 
535
+    if (!s) {
536
+        /* We should probably just put the chapter data to extra_meta here
537
+         * and do the AVFormatContext-needing part in a separate
538
+         * ff_id3v2_parse_apic()-like function. */
539
+        av_log(NULL, AV_LOG_DEBUG, "No AVFormatContext, skipped ID3 chapter data\n");
540
+        return;
541
+    }
542
+
535 543
     if (decode_str(s, pb, 0, &dst, &len) < 0)
536 544
         return;
537 545
     if (len < 16)
... ...
@@ -650,16 +658,17 @@ static const ID3v2EMFunc *get_extra_meta_func(const char *tag, int isv34)
650 650
     return NULL;
651 651
 }
652 652
 
653
-static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
653
+static void id3v2_parse(AVIOContext *pb, AVDictionary **metadata,
654
+                        AVFormatContext *s, int len, uint8_t version,
654 655
                         uint8_t flags, ID3v2ExtraMeta **extra_meta)
655 656
 {
656 657
     int isv34, unsync;
657 658
     unsigned tlen;
658 659
     char tag[5];
659
-    int64_t next, end = avio_tell(s->pb) + len;
660
+    int64_t next, end = avio_tell(pb) + len;
660 661
     int taghdrlen;
661 662
     const char *reason = NULL;
662
-    AVIOContext pb;
663
+    AVIOContext pb_local;
663 664
     AVIOContext *pbx;
664 665
     unsigned char *buffer = NULL;
665 666
     int buffer_size       = 0;
... ...
@@ -693,7 +702,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
693 693
     unsync = flags & 0x80;
694 694
 
695 695
     if (isv34 && flags & 0x40) { /* Extended header present, just skip over it */
696
-        int extlen = get_size(s->pb, 4);
696
+        int extlen = get_size(pb, 4);
697 697
         if (version == 4)
698 698
             /* In v2.4 the length includes the length field we just read. */
699 699
             extlen -= 4;
... ...
@@ -702,7 +711,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
702 702
             reason = "invalid extended header length";
703 703
             goto error;
704 704
         }
705
-        avio_skip(s->pb, extlen);
705
+        avio_skip(pb, extlen);
706 706
         len -= extlen + 4;
707 707
         if (len < 0) {
708 708
             reason = "extended header too long.";
... ...
@@ -718,20 +727,20 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
718 718
         unsigned long dlen;
719 719
 
720 720
         if (isv34) {
721
-            if (avio_read(s->pb, tag, 4) < 4)
721
+            if (avio_read(pb, tag, 4) < 4)
722 722
                 break;
723 723
             tag[4] = 0;
724 724
             if (version == 3) {
725
-                tlen = avio_rb32(s->pb);
725
+                tlen = avio_rb32(pb);
726 726
             } else
727
-                tlen = get_size(s->pb, 4);
728
-            tflags  = avio_rb16(s->pb);
727
+                tlen = get_size(pb, 4);
728
+            tflags  = avio_rb16(pb);
729 729
             tunsync = tflags & ID3v2_FLAG_UNSYNCH;
730 730
         } else {
731
-            if (avio_read(s->pb, tag, 3) < 3)
731
+            if (avio_read(pb, tag, 3) < 3)
732 732
                 break;
733 733
             tag[3] = 0;
734
-            tlen   = avio_rb24(s->pb);
734
+            tlen   = avio_rb24(pb);
735 735
         }
736 736
         if (tlen > (1<<28))
737 737
             break;
... ...
@@ -740,7 +749,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
740 740
         if (len < 0)
741 741
             break;
742 742
 
743
-        next = avio_tell(s->pb) + tlen;
743
+        next = avio_tell(pb) + tlen;
744 744
 
745 745
         if (!tlen) {
746 746
             if (tag[0])
... ...
@@ -752,7 +761,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
752 752
         if (tflags & ID3v2_FLAG_DATALEN) {
753 753
             if (tlen < 4)
754 754
                 break;
755
-            dlen = avio_rb32(s->pb);
755
+            dlen = avio_rb32(pb);
756 756
             tlen -= 4;
757 757
         } else
758 758
             dlen = tlen;
... ...
@@ -771,12 +780,12 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
771 771
                 type = "encrypted and compressed";
772 772
 
773 773
             av_log(s, AV_LOG_WARNING, "Skipping %s ID3v2 frame %s.\n", type, tag);
774
-            avio_skip(s->pb, tlen);
774
+            avio_skip(pb, tlen);
775 775
         /* check for text tag or supported special meta tag */
776 776
         } else if (tag[0] == 'T' ||
777 777
                    (extra_meta &&
778 778
                     (extra_func = get_extra_meta_func(tag, isv34)))) {
779
-            pbx = s->pb;
779
+            pbx = pb;
780 780
 
781 781
             if (unsync || tunsync || tcomp) {
782 782
                 av_fast_malloc(&buffer, &buffer_size, tlen);
... ...
@@ -786,23 +795,23 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
786 786
                 }
787 787
             }
788 788
             if (unsync || tunsync) {
789
-                int64_t end = avio_tell(s->pb) + tlen;
789
+                int64_t end = avio_tell(pb) + tlen;
790 790
                 uint8_t *b;
791 791
 
792 792
                 b = buffer;
793
-                while (avio_tell(s->pb) < end && b - buffer < tlen && !s->pb->eof_reached) {
794
-                    *b++ = avio_r8(s->pb);
795
-                    if (*(b - 1) == 0xff && avio_tell(s->pb) < end - 1 &&
793
+                while (avio_tell(pb) < end && b - buffer < tlen && !pb->eof_reached) {
794
+                    *b++ = avio_r8(pb);
795
+                    if (*(b - 1) == 0xff && avio_tell(pb) < end - 1 &&
796 796
                         b - buffer < tlen &&
797
-                        !s->pb->eof_reached ) {
798
-                        uint8_t val = avio_r8(s->pb);
799
-                        *b++ = val ? val : avio_r8(s->pb);
797
+                        !pb->eof_reached ) {
798
+                        uint8_t val = avio_r8(pb);
799
+                        *b++ = val ? val : avio_r8(pb);
800 800
                     }
801 801
                 }
802
-                ffio_init_context(&pb, buffer, b - buffer, 0, NULL, NULL, NULL,
802
+                ffio_init_context(&pb_local, buffer, b - buffer, 0, NULL, NULL, NULL,
803 803
                                   NULL);
804 804
                 tlen = b - buffer;
805
-                pbx  = &pb; // read from sync buffer
805
+                pbx  = &pb_local; // read from sync buffer
806 806
             }
807 807
 
808 808
 #if CONFIG_ZLIB
... ...
@@ -818,7 +827,7 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
818 818
                     }
819 819
 
820 820
                     if (!(unsync || tunsync)) {
821
-                        err = avio_read(s->pb, buffer, tlen);
821
+                        err = avio_read(pb, buffer, tlen);
822 822
                         if (err < 0) {
823 823
                             av_log(s, AV_LOG_ERROR, "Failed to read compressed tag\n");
824 824
                             goto seek;
... ...
@@ -831,26 +840,26 @@ static void id3v2_parse(AVFormatContext *s, int len, uint8_t version,
831 831
                         av_log(s, AV_LOG_ERROR, "Failed to uncompress tag: %d\n", err);
832 832
                         goto seek;
833 833
                     }
834
-                    ffio_init_context(&pb, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL);
834
+                    ffio_init_context(&pb_local, uncompressed_buffer, dlen, 0, NULL, NULL, NULL, NULL);
835 835
                     tlen = dlen;
836
-                    pbx = &pb; // read from sync buffer
836
+                    pbx = &pb_local; // read from sync buffer
837 837
                 }
838 838
 #endif
839 839
             if (tag[0] == 'T')
840 840
                 /* parse text tag */
841
-                read_ttag(s, pbx, tlen, &s->metadata, tag);
841
+                read_ttag(s, pbx, tlen, metadata, tag);
842 842
             else
843 843
                 /* parse special meta tag */
844 844
                 extra_func->read(s, pbx, tlen, tag, extra_meta, isv34);
845 845
         } else if (!tag[0]) {
846 846
             if (tag[1])
847 847
                 av_log(s, AV_LOG_WARNING, "invalid frame id, assuming padding\n");
848
-            avio_skip(s->pb, tlen);
848
+            avio_skip(pb, tlen);
849 849
             break;
850 850
         }
851 851
         /* Skip to end of tag */
852 852
 seek:
853
-        avio_seek(s->pb, next, SEEK_SET);
853
+        avio_seek(pb, next, SEEK_SET);
854 854
     }
855 855
 
856 856
     /* Footer preset, always 10 bytes, skip over it */
... ...
@@ -861,14 +870,15 @@ error:
861 861
     if (reason)
862 862
         av_log(s, AV_LOG_INFO, "ID3v2.%d tag skipped, cannot handle %s\n",
863 863
                version, reason);
864
-    avio_seek(s->pb, end, SEEK_SET);
864
+    avio_seek(pb, end, SEEK_SET);
865 865
     av_free(buffer);
866 866
     av_free(uncompressed_buffer);
867 867
     return;
868 868
 }
869 869
 
870
-void ff_id3v2_read(AVFormatContext *s, const char *magic,
871
-                   ID3v2ExtraMeta **extra_meta)
870
+static void id3v2_read_internal(AVIOContext *pb, AVDictionary **metadata,
871
+                                AVFormatContext *s, const char *magic,
872
+                                ID3v2ExtraMeta **extra_meta)
872 873
 {
873 874
     int len, ret;
874 875
     uint8_t buf[ID3v2_HEADER_SIZE];
... ...
@@ -877,10 +887,10 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic,
877 877
 
878 878
     do {
879 879
         /* save the current offset in case there's nothing to read/skip */
880
-        off = avio_tell(s->pb);
881
-        ret = avio_read(s->pb, buf, ID3v2_HEADER_SIZE);
880
+        off = avio_tell(pb);
881
+        ret = avio_read(pb, buf, ID3v2_HEADER_SIZE);
882 882
         if (ret != ID3v2_HEADER_SIZE) {
883
-            avio_seek(s->pb, off, SEEK_SET);
883
+            avio_seek(pb, off, SEEK_SET);
884 884
             break;
885 885
         }
886 886
         found_header = ff_id3v2_match(buf, magic);
... ...
@@ -890,15 +900,27 @@ void ff_id3v2_read(AVFormatContext *s, const char *magic,
890 890
                   ((buf[7] & 0x7f) << 14) |
891 891
                   ((buf[8] & 0x7f) << 7) |
892 892
                    (buf[9] & 0x7f);
893
-            id3v2_parse(s, len, buf[3], buf[5], extra_meta);
893
+            id3v2_parse(pb, metadata, s, len, buf[3], buf[5], extra_meta);
894 894
         } else {
895
-            avio_seek(s->pb, off, SEEK_SET);
895
+            avio_seek(pb, off, SEEK_SET);
896 896
         }
897 897
     } while (found_header);
898
-    ff_metadata_conv(&s->metadata, NULL, ff_id3v2_34_metadata_conv);
899
-    ff_metadata_conv(&s->metadata, NULL, id3v2_2_metadata_conv);
900
-    ff_metadata_conv(&s->metadata, NULL, ff_id3v2_4_metadata_conv);
901
-    merge_date(&s->metadata);
898
+    ff_metadata_conv(metadata, NULL, ff_id3v2_34_metadata_conv);
899
+    ff_metadata_conv(metadata, NULL, id3v2_2_metadata_conv);
900
+    ff_metadata_conv(metadata, NULL, ff_id3v2_4_metadata_conv);
901
+    merge_date(metadata);
902
+}
903
+
904
+void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata,
905
+                        const char *magic, ID3v2ExtraMeta **extra_meta)
906
+{
907
+    id3v2_read_internal(pb, metadata, NULL, magic, extra_meta);
908
+}
909
+
910
+void ff_id3v2_read(AVFormatContext *s, const char *magic,
911
+                   ID3v2ExtraMeta **extra_meta)
912
+{
913
+    id3v2_read_internal(s->pb, &s->metadata, s, magic, extra_meta);
902 914
 }
903 915
 
904 916
 void ff_id3v2_free_extra_meta(ID3v2ExtraMeta **extra_meta)
... ...
@@ -95,7 +95,21 @@ int ff_id3v2_match(const uint8_t *buf, const char *magic);
95 95
 int ff_id3v2_tag_len(const uint8_t *buf);
96 96
 
97 97
 /**
98
- * Read an ID3v2 tag, including supported extra metadata
98
+ * Read an ID3v2 tag into specified dictionary and retrieve supported extra metadata.
99
+ *
100
+ * Chapters are not currently read by this variant.
101
+ *
102
+ * @param metadata Parsed metadata is stored here
103
+ * @param extra_meta If not NULL, extra metadata is parsed into a list of
104
+ * ID3v2ExtraMeta structs and *extra_meta points to the head of the list
105
+ */
106
+void ff_id3v2_read_dict(AVIOContext *pb, AVDictionary **metadata, const char *magic, ID3v2ExtraMeta **extra_meta);
107
+
108
+/**
109
+ * Read an ID3v2 tag, including supported extra metadata and chapters.
110
+ *
111
+ * Data is read from and stored to AVFormatContext.
112
+ *
99 113
  * @param extra_meta If not NULL, extra metadata is parsed into a list of
100 114
  * ID3v2ExtraMeta structs and *extra_meta points to the head of the list
101 115
  */