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>
... | ... |
@@ -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 |
*/ |