shithub: sox

Download patch

ref: 2c3c95d2b5554390590cba77ddf19ea76fc71785
parent: 158fe58a780379917fd3c118cd0ed913df4a152e
author: Doug Cook <idigdoug@users.sourceforge.net>
date: Sat Mar 17 22:22:23 EDT 2012

Fix mp3 format's direct use of format_t.fp

--- a/src/mp3-util.h
+++ b/src/mp3-util.h
@@ -97,34 +97,143 @@
   return NULL;
 }
 
+struct tag_info_node
+{
+    struct tag_info_node * next;
+    off_t start;
+    off_t end;
+};
+
+struct tag_info {
+  sox_format_t * ft;
+  struct tag_info_node * head;
+  struct id3_tag * tag;
+};
+
+static int add_tag(struct tag_info * info)
+{
+  struct tag_info_node * current;
+  off_t start, end;
+  id3_byte_t query[ID3_TAG_QUERYSIZE];
+  id3_byte_t * buffer;
+  long size;
+  int result = 0;
+
+  /* Ensure we're at the start of a valid tag and get its size. */
+  if (ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
+      !(size = id3_tag_query(query, ID3_TAG_QUERYSIZE))) {
+    return 0;
+  }
+  if (size < 0) {
+    if (0 != lsx_seeki(info->ft, size, SEEK_CUR) ||
+        ID3_TAG_QUERYSIZE != lsx_readbuf(info->ft, query, ID3_TAG_QUERYSIZE) ||
+        (size = id3_tag_query(query, ID3_TAG_QUERYSIZE)) <= 0) {
+      return 0;
+    }
+  }
+
+  /* Don't read a tag more than once. */
+  start = lsx_tell(info->ft);
+  end = start + size;
+  for (current = info->head; current; current = current->next) {
+    if (start == current->start && end == current->end) {
+      return 1;
+    } else if (start < current->end && current->start < end) {
+      return 0;
+    }
+  }
+
+  buffer = lsx_malloc(size);
+  if (!buffer) {
+    return 0;
+  }
+  memcpy(buffer, query, ID3_TAG_QUERYSIZE);
+  if ((unsigned long)size - ID3_TAG_QUERYSIZE ==
+      lsx_readbuf(info->ft, buffer + ID3_TAG_QUERYSIZE, size - ID3_TAG_QUERYSIZE)) {
+    struct id3_tag * tag = id3_tag_parse(buffer, size);
+    if (tag) {
+      current = lsx_malloc(sizeof(struct tag_info_node));
+      if (current) {
+        current->next = info->head;
+        current->start = start;
+        current->end = end;
+        info->head = current;
+        if (info->tag && (info->tag->extendedflags & ID3_TAG_EXTENDEDFLAG_TAGISANUPDATE)) {
+          struct id3_frame * frame;
+          unsigned i;
+          for (i = 0; frame = id3_tag_findframe(tag, NULL, i); i++) {
+            id3_tag_attachframe(info->tag, frame);
+          }
+          id3_tag_delete(tag);
+        } else {
+          if (info->tag) {
+            id3_tag_delete(info->tag);
+          }
+          info->tag = tag;
+        }
+      }
+    }
+  }
+  free(buffer);
+  return result;
+}
+
 static void read_comments(sox_format_t * ft)
 {
-  struct id3_file   * id3struct;
-  struct id3_tag    * tag;
+  struct tag_info   info;
   id3_utf8_t        * utf8;
-  int               i, fd = dup(fileno((FILE*)ft->fp));
+  int               i;
+  int               has_id3v1 = 0;
 
-  if ((id3struct = id3_file_fdopen(fd, ID3_FILE_MODE_READONLY))) {
-    if ((tag = id3_file_tag(id3struct)) && tag->frames)
-      for (i = 0; id3tagmap[i][0]; ++i)
-        if ((utf8 = utf8_id3tag_findframe(tag, id3tagmap[i][0], 0))) {
-          char * comment = lsx_malloc(strlen(id3tagmap[i][1]) + 1 + strlen((char *)utf8) + 1);
-          sprintf(comment, "%s=%s", id3tagmap[i][1], utf8);
-          sox_append_comment(&ft->oob.comments, comment);
-          free(comment);
-          free(utf8);
-        }
-      if ((utf8 = utf8_id3tag_findframe(tag, "TLEN", 0))) {
-        unsigned long tlen = strtoul((char *)utf8, NULL, 10);
-        if (tlen > 0 && tlen < ULONG_MAX) {
-          ft->signal.length= tlen; /* In ms; convert to samples later */
-          lsx_debug("got exact duration from ID3 TLEN");
-        }
+  info.ft = ft;
+  info.head = NULL;
+  info.tag = NULL;
+
+  /*
+  We look for:
+  ID3v1 at end (EOF - 128).
+  ID3v2 at start.
+  ID3v2 at end (but before ID3v1 from end if there was one).
+  */
+
+  if (0 == lsx_seeki(ft, -128, SEEK_END)) {
+    has_id3v1 =
+      add_tag(&info) &&
+      1 == ID3_TAG_VERSION_MAJOR(id3_tag_version(info.tag));
+  }
+  if (0 == lsx_seeki(ft, 0, SEEK_SET)) {
+    add_tag(&info);
+  }
+  if (0 == lsx_seeki(ft, has_id3v1 ? -138 : -10, SEEK_END)) {
+    add_tag(&info);
+  }
+  if (info.tag && info.tag->frames) {
+    for (i = 0; id3tagmap[i][0]; ++i) {
+      if ((utf8 = utf8_id3tag_findframe(info.tag, id3tagmap[i][0], 0))) {
+        char * comment = lsx_malloc(strlen(id3tagmap[i][1]) + 1 + strlen((char *)utf8) + 1);
+        sprintf(comment, "%s=%s", id3tagmap[i][1], utf8);
+        sox_append_comment(&ft->oob.comments, comment);
+        free(comment);
         free(utf8);
       }
-    id3_file_close(id3struct);
+    }
+    if ((utf8 = utf8_id3tag_findframe(info.tag, "TLEN", 0))) {
+      unsigned long tlen = strtoul((char *)utf8, NULL, 10);
+      if (tlen > 0 && tlen < ULONG_MAX) {
+        ft->signal.length= tlen; /* In ms; convert to samples later */
+        lsx_debug("got exact duration from ID3 TLEN");
+      }
+      free(utf8);
+    }
   }
-  else close(fd);
+  while (info.head) {
+    struct tag_info_node * head = info.head;
+    info.head = head->next;
+    free(head);
+  }
+  if (info.tag) {
+    id3_tag_delete(info.tag);
+  }
 }
 
 #endif /* USING_ID3TAG */
--- a/src/mp3.c
+++ b/src/mp3.c
@@ -385,7 +385,7 @@
   if (ft->seekable) {
 #ifdef USING_ID3TAG
     read_comments(ft);
-    rewind((FILE*)ft->fp);
+    lsx_rewind(ft);
     if (!ft->signal.length)
 #endif
       if (!ignore_length)
@@ -404,7 +404,7 @@
    * can be processed later.
    */
   ReadSize = lsx_readbuf(ft, p->mp3_buffer, p->mp3_buffer_size);
-  if (ReadSize != p->mp3_buffer_size && ferror((FILE*)ft->fp))
+  if (ReadSize != p->mp3_buffer_size && lsx_error(ft))
     return SOX_EOF;
 
   p->mad_stream_buffer(&p->Stream, p->mp3_buffer, ReadSize);
@@ -568,7 +568,7 @@
   uint64_t to_skip_samples = 0;
 
   /* Reset all */
-  rewind((FILE*)ft->fp);
+  lsx_rewind(ft);
   mad_timer_reset(&p->Timer);
   p->FrameCount = 0;
 
@@ -585,12 +585,13 @@
   to_skip_samples = offset;
 
   while(sox_true) {  /* Read data from the MP3 file */
-    int read, padding = 0;
+    size_t padding = 0;
+    size_t read;
     size_t leftover = p->Stream.bufend - p->Stream.next_frame;
 
     memcpy(p->mp3_buffer, p->Stream.this_frame, leftover);
-    read = fread(p->mp3_buffer + leftover, (size_t) 1, p->mp3_buffer_size - leftover, (FILE*)ft->fp);
-    if (read <= 0) {
+    read = lsx_readbuf(ft, p->mp3_buffer + leftover, p->mp3_buffer_size - leftover);
+    if (read == 0) {
       lsx_debug("seek failure. unexpected EOF (frames=%" PRIuPTR " leftover=%" PRIuPTR ")", p->FrameCount, leftover);
       break;
     }
@@ -615,7 +616,7 @@
           tagsize = tagtype(p->Stream.this_frame, (size_t) available);
           if (tagsize) {   /* It's some ID3 tags, so just skip */
             if (tagsize >= available) {
-              fseeko((FILE*)ft->fp, (off_t)(tagsize - available), SEEK_CUR);
+              lsx_seeki(ft, (off_t)(tagsize - available), SEEK_CUR);
               depadded = sox_false;
             }
             p->mad_stream_skip(&p->Stream, min(tagsize, available));
@@ -728,18 +729,17 @@
 
 static int get_id3v2_tag_size(sox_format_t * ft)
 {
-  FILE *fp = ft->fp;
   size_t bytes_read;
   int id3v2_size;
   unsigned char id3v2_header[10];
 
-  if (fseeko(fp, (off_t)0, SEEK_SET) != 0) {
+  if (lsx_seeki(ft, (off_t)0, SEEK_SET) != 0) {
     lsx_warn("cannot update id3 tag - failed to seek to beginning");
     return SOX_EOF;
   }
 
   /* read 10 bytes in case there's an ID3 version 2 header here */
-  bytes_read = fread(id3v2_header, (size_t)1, sizeof(id3v2_header), fp);
+  bytes_read = lsx_readbuf(ft, id3v2_header, sizeof(id3v2_header));
   if (bytes_read != sizeof(id3v2_header)) {
     lsx_warn("cannot update id3 tag - failed to read id3 header");
     return SOX_EOF;      /* not readable, maybe opened Write-Only */
@@ -764,7 +764,6 @@
 static void rewrite_id3v2_tag(sox_format_t * ft, size_t id3v2_size, uint64_t num_samples)
 {
   priv_t *p = (priv_t *)ft->priv;
-  FILE *fp = ft->fp;
   size_t new_size;
   unsigned char * buffer;
 
@@ -790,7 +789,7 @@
     num_samples = 0;
   }
   p->lame_set_num_samples(p->gfp, (unsigned long)num_samples);
-  lsx_debug("updated MP3 TLEN to %" PRIu64 " samples", num_samples);
+  lsx_debug("updated MP3 TLEN to %ul samples", (unsigned long)num_samples);
 
   new_size = p->lame_get_id3v2_tag(p->gfp, buffer, id3v2_size);
 
@@ -810,9 +809,9 @@
     else
       lsx_warn("cannot update track length info - failed to adjust tag size");
   } else {
-    fseeko(fp, (off_t)0, SEEK_SET);
+    lsx_seeki(ft, 0, SEEK_SET);
     /* Overwrite the Id3v2 tag (this time TLEN should be accurate) */
-    if (fwrite(buffer, id3v2_size, (size_t)1, fp) != 1) {
+    if (lsx_writebuf(ft, buffer, id3v2_size) != 1) {
       lsx_debug("Rewrote Id3v2 tag (%" PRIuPTR " bytes)", id3v2_size);
     }
   }
@@ -823,18 +822,17 @@
 static void rewrite_tags(sox_format_t * ft, uint64_t num_samples)
 {
   priv_t *p = (priv_t *)ft->priv;
-  FILE *fp = ft->fp;
 
   off_t file_size;
   size_t id3v2_size;
 
-  if (fseeko(fp, (off_t)0, SEEK_END)) {
+  if (lsx_seeki(ft, 0, SEEK_END)) {
     lsx_warn("cannot update tags - seek to end failed");
     return;
   }
 
   /* Get file size */
-  file_size = ftello(fp);
+  file_size = lsx_tell(ft);
 
   if (file_size == 0) {
     lsx_warn("cannot update tags - file size is 0");
@@ -850,7 +848,7 @@
     size_t lametag_size;
     uint8_t buffer[MAXFRAMESIZE];
 
-    if (fseeko(fp, (off_t)id3v2_size, SEEK_SET)) {
+    if (lsx_seeki(ft, (off_t)id3v2_size, SEEK_SET)) {
       lsx_warn("cannot write VBR tag - seek to tag block failed");
       return;
     }
@@ -865,7 +863,7 @@
       return;
     }
 
-    if (fwrite(buffer, lametag_size, (size_t)1, fp) != 1) {
+    if (lsx_writebuf(ft, buffer, lametag_size) != lametag_size) {
       lsx_warn("cannot write VBR tag - VBR tag write failed");
     } else {
       lsx_debug("rewrote VBR tag (%" PRIuPTR " bytes)", lametag_size);