Beispiel #1
0
static void
add_metadata_from_tag (GFileInfo         *info,
		       const GstTagList  *list,
		       const char        *tag,
		       const char        *tag_key)
{
	GType tag_type;

	tag_type = gst_tag_get_type (tag);

	if (tag_type == G_TYPE_BOOLEAN) {
		gboolean ret;
		if (gst_tag_list_get_boolean (list, tag, &ret)) {
			if (ret)
				add_metadata (info, tag_key, g_strdup ("TRUE"), NULL);
			else
				add_metadata (info, tag_key, g_strdup ("FALSE"), NULL);
		}
	}

	if (tag_type == G_TYPE_STRING) {
		char *ret = NULL;
		if (gst_tag_list_get_string (list, tag, &ret))
			add_metadata (info, tag_key, ret, NULL);
	}

        if (tag_type == G_TYPE_UCHAR) {
                guint ret = 0;
                if (gst_tag_list_get_uint (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%u", ret), NULL);
        }

        if (tag_type == G_TYPE_CHAR) {
                int ret = 0;
                if (gst_tag_list_get_int (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%d", ret), NULL);
        }

        if (tag_type == G_TYPE_UINT) {
                guint ret = 0;
                if (gst_tag_list_get_uint (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%u", ret), NULL);
        }

        if (tag_type == G_TYPE_INT) {
                gint ret = 0;
                if (gst_tag_list_get_int (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%d", ret), NULL);
        }

        if (tag_type == G_TYPE_ULONG) {
                guint64 ret = 0;
                if (gst_tag_list_get_uint64 (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%" G_GUINT64_FORMAT, ret), NULL);
        }

        if (tag_type == G_TYPE_LONG) {
                gint64 ret = 0;
                if (gst_tag_list_get_int64 (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%" G_GINT64_FORMAT, ret), NULL);
        }

        if (tag_type == G_TYPE_INT64) {
                gint64 ret = 0;
                if (gst_tag_list_get_int64 (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%" G_GINT64_FORMAT, ret), NULL);
        }

        if (tag_type == G_TYPE_UINT64) {
                guint64 ret = 0;
                if (gst_tag_list_get_uint64 (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%" G_GUINT64_FORMAT, ret), NULL);
        }

        if (tag_type == G_TYPE_DOUBLE) {
                gdouble ret = 0;
                if (gst_tag_list_get_double (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%f", ret), NULL);
        }

        if (tag_type == G_TYPE_FLOAT) {
                gfloat ret = 0;
                if (gst_tag_list_get_float (list, tag, &ret))
                        add_metadata (info, tag_key, g_strdup_printf ("%f", ret), NULL);
        }

        if (tag_type == G_TYPE_DATE) {
                GDate *ret = NULL;
                if (gst_tag_list_get_date (list, tag, &ret)) {
			if (ret != NULL) {
				char  buf[128];
				char *raw;
				char *formatted;

				g_date_strftime (buf, 10, "%F %T", ret);
				raw = g_strdup (buf);

				g_date_strftime (buf, 10, "%x %X", ret);
				formatted = g_strdup (buf);
				add_metadata (info, tag_key, raw, formatted);
			}
			g_free (ret);
		}
        }
}
Beispiel #2
0
static void
metadatamux_exif_for_each_tag_in_list (const GstTagList * list,
    const gchar * tag, gpointer user_data)
{
  ExifData *ed = (ExifData *) user_data;
  ExifTag exif_tag;
  GType type = G_TYPE_INVALID;
  ExifEntry *entry = NULL;
  ExifIfd ifd = EXIF_IFD_COUNT;
  const ExifByteOrder byte_order = exif_data_get_byte_order (ed);

  exif_tag = metadatamux_exif_get_exif_from_tag (tag, &type, &ifd);

  if (!exif_tag)
    goto done;

  entry = exif_data_get_entry (ed, exif_tag);

  if (entry)
    exif_entry_ref (entry);
  else {
    entry = exif_entry_new ();
    exif_content_add_entry (ed->ifd[ifd], entry);
    exif_entry_initialize (entry, exif_tag);
  }

  if (entry->data == NULL) {
    if (entry->tag == EXIF_TAG_GPS_ALTITUDE) {
      entry->format = EXIF_FORMAT_RATIONAL;
      entry->components = 1;
      entry->size = exif_format_get_size (entry->format) * entry->components;
      entry->data = g_malloc (entry->size);
    } else if (entry->tag == EXIF_TAG_GPS_LATITUDE
        || entry->tag == EXIF_TAG_GPS_LONGITUDE) {
      entry->format = EXIF_FORMAT_RATIONAL;
      entry->components = 3;
      entry->size = exif_format_get_size (entry->format) * entry->components;
      entry->data = g_malloc (entry->size);
    }
  }

  if (type == GST_TYPE_FRACTION) {
    const GValue *gvalue = gst_tag_list_get_value_index (list, tag, 0);
    gint numerator = gst_value_get_fraction_numerator (gvalue);
    gint denominator = gst_value_get_fraction_denominator (gvalue);

    switch (entry->format) {
      case EXIF_FORMAT_SRATIONAL:
      {
        ExifSRational sr = { numerator, denominator };

        exif_set_srational (entry->data, byte_order, sr);
      }
        break;
      case EXIF_FORMAT_RATIONAL:
      {
        ExifRational r;

        if (entry->tag == EXIF_TAG_X_RESOLUTION ||
            entry->tag == EXIF_TAG_Y_RESOLUTION) {
          ExifEntry *unit_entry = NULL;

          unit_entry = exif_data_get_entry (ed, EXIF_TAG_RESOLUTION_UNIT);

          if (unit_entry) {
            ExifShort vsh = exif_get_short (unit_entry->data, byte_order);

            if (vsh != 2)       /* inches */
              exif_set_short (unit_entry->data, byte_order, 2);
          }
        }
        r.numerator = numerator;
        r.denominator = denominator;
        if (numerator < 0)
          r.numerator = -numerator;
        if (denominator < 0)
          r.denominator = -denominator;
        exif_set_rational (entry->data, byte_order, r);
      }
        break;
      default:
        break;
    }
  } else if (type == GST_TYPE_BUFFER) {
    const GValue *val = NULL;
    GstBuffer *buf;

    val = gst_tag_list_get_value_index (list, tag, 0);
    buf = gst_value_get_buffer (val);
    entry->components = GST_BUFFER_SIZE (buf);
    entry->size = GST_BUFFER_SIZE (buf);
    entry->data = g_malloc (entry->size);
    memcpy (entry->data, GST_BUFFER_DATA (buf), entry->size);
  } else {
    switch (type) {
      case G_TYPE_STRING:
      {
        gchar *value = NULL;

        if (gst_tag_list_get_string (list, tag, &value)) {
          if (entry->tag == EXIF_TAG_DATE_TIME_DIGITIZED
              || entry->tag == EXIF_TAG_DATE_TIME
              || entry->tag == EXIF_TAG_DATE_TIME_ORIGINAL) {
            GString *datetime = g_string_new_len (value,
                20);            /* enough memory to hold "YYYY:MM:DD HH:MM:SS" */

            if (metadatamux_exif_convert_from_datetime (datetime)) {
            } else {
              GST_ERROR ("Unexpected date & time format for %s", tag);
            }
            g_free (value);
            value = datetime->str;
            g_string_free (datetime, FALSE);
          } else if (value) {
            entry->components = strlen (value) + 1;
            entry->size =
                exif_format_get_size (entry->format) * entry->components;
            entry->data = (guint8 *) value;
            value = NULL;
          }
        }
      }
        break;
      case G_TYPE_UINT:
      case G_TYPE_INT:
      {
        gint value;
        ExifShort v_short;

        if (G_TYPE_UINT == type) {
          gst_tag_list_get_uint (list, tag, (guint *) & value);
        } else {
          gst_tag_list_get_int (list, tag, &value);
        }

        switch (entry->format) {
          case EXIF_FORMAT_SHORT:
            if (entry->tag == EXIF_TAG_CONTRAST
                || entry->tag == EXIF_TAG_SATURATION) {
              if (value < -33)
                value = 1;      /* low */
              else if (value < 34)
                value = 0;      /* normal */
              else
                value = 2;      /* high */
            }
            v_short = value;
            exif_set_short (entry->data, byte_order, v_short);
            break;
          case EXIF_FORMAT_LONG:
            exif_set_long (entry->data, byte_order, value);
            break;
          default:
            break;
        }

      }
        break;
      case G_TYPE_DOUBLE:
      {
        gdouble value;

        gst_tag_list_get_double (list, tag, &value);
        if (entry->tag == EXIF_TAG_GPS_LATITUDE
            || entry->tag == EXIF_TAG_GPS_LONGITUDE) {
          ExifRational *rt = (ExifRational *) entry->data;
          gdouble v = fabs (value);
          ExifEntry *ref_entry = NULL;
          char ref;
          const ExifTag ref_tag = entry->tag == EXIF_TAG_GPS_LATITUDE ?
              EXIF_TAG_GPS_LATITUDE_REF : EXIF_TAG_GPS_LONGITUDE_REF;

          /* DDD - degrees */
          rt->numerator = (gulong) v;
          rt->denominator = 1;
          GST_DEBUG ("deg: %lf : %lu / %lu", v, (gulong) rt->numerator,
              (gulong) rt->denominator);
          v -= rt->numerator;
          rt++;

          /* MM - minutes */
          rt->numerator = (gulong) (v * 60.0);
          rt->denominator = 1;
          GST_DEBUG ("min: %lf : %lu / %lu", v, (gulong) rt->numerator,
              (gulong) rt->denominator);
          v -= ((gdouble) rt->numerator / 60.0);
          rt++;

          /* SS - seconds */
          rt->numerator = (gulong) (0.5 + v * 3600.0);
          rt->denominator = 1;
          GST_DEBUG ("sec: %lf : %lu / %lu", v, (gulong) rt->numerator,
              (gulong) rt->denominator);

          if (entry->tag == EXIF_TAG_GPS_LONGITUDE) {
            GST_DEBUG ("longitude : %lf", value);
            ref = (value < 0.0) ? 'W' : 'E';
          } else {
            GST_DEBUG ("latitude : %lf", value);
            ref = (value < 0.0) ? 'S' : 'N';
          }

          ref_entry = exif_data_get_entry (ed, ref_tag);
          if (ref_entry) {
            exif_entry_ref (ref_entry);
          } else {
            ref_entry = exif_entry_new ();
            exif_content_add_entry (ed->ifd[EXIF_IFD_GPS], ref_entry);
            exif_entry_initialize (ref_entry, ref_tag);
          }
          if (ref_entry->data == NULL) {
            ref_entry->format = EXIF_FORMAT_ASCII;
            ref_entry->components = 2;
            ref_entry->size = 2;
            ref_entry->data = g_malloc (2);
          }
          ref_entry->data[0] = ref;
          ref_entry->data[1] = 0;
          exif_entry_unref (ref_entry);
        } else if (entry->tag == EXIF_TAG_GPS_ALTITUDE) {
          ExifEntry *ref_entry = NULL;
          ExifRational *rt = (ExifRational *) entry->data;

          rt->numerator = (gulong) fabs (10.0 * value);
          rt->denominator = 10;

          GST_DEBUG ("altitude : %lf", value);

          ref_entry = exif_data_get_entry (ed, EXIF_TAG_GPS_ALTITUDE_REF);
          if (ref_entry) {
            exif_entry_ref (ref_entry);
          } else {
            ref_entry = exif_entry_new ();
            exif_content_add_entry (ed->ifd[EXIF_IFD_GPS], ref_entry);
            exif_entry_initialize (ref_entry, EXIF_TAG_GPS_ALTITUDE_REF);
          }
          if (ref_entry->data == NULL) {
            ref_entry->format = EXIF_FORMAT_BYTE;
            ref_entry->components = 1;
            ref_entry->size = 1;
            ref_entry->data = g_malloc (1);
          }
          if (value > 0.0) {
            ref_entry->data[0] = 0;
          } else {
            ref_entry->data[0] = 1;
          }
          exif_entry_unref (ref_entry);
        }
      }
        break;
      default:
        break;
    }
  }

done:

  if (entry)
    exif_entry_unref (entry);

}
static GstFlowReturn
gst_ffmpegmux_collected (GstCollectPads * pads, gpointer user_data)
{
  GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) user_data;
  GSList *collected;
  GstFFMpegMuxPad *best_pad;
  GstClockTime best_time;
  const GstTagList *tags;

  /* open "file" (gstreamer protocol to next element) */
  if (!ffmpegmux->opened) {
    int open_flags = URL_WRONLY;

    /* we do need all streams to have started capsnego,
     * or things will go horribly wrong */
    for (collected = ffmpegmux->collect->data; collected;
        collected = g_slist_next (collected)) {
      GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
      AVStream *st = ffmpegmux->context->streams[collect_pad->padnum];

      /* check whether the pad has successfully completed capsnego */
      if (st->codec->codec_id == CODEC_ID_NONE) {
        GST_ELEMENT_ERROR (ffmpegmux, CORE, NEGOTIATION, (NULL),
            ("no caps set on stream %d (%s)", collect_pad->padnum,
                (st->codec->codec_type == CODEC_TYPE_VIDEO) ?
                "video" : "audio"));
        return GST_FLOW_ERROR;
      }
      /* set framerate for audio */
      if (st->codec->codec_type == CODEC_TYPE_AUDIO) {
        switch (st->codec->codec_id) {
          case CODEC_ID_PCM_S16LE:
          case CODEC_ID_PCM_S16BE:
          case CODEC_ID_PCM_U16LE:
          case CODEC_ID_PCM_U16BE:
          case CODEC_ID_PCM_S8:
          case CODEC_ID_PCM_U8:
            st->codec->frame_size = 1;
            break;
          default:
          {
            GstBuffer *buffer;

            /* FIXME : This doesn't work for RAW AUDIO...
             * in fact I'm wondering if it even works for any kind of audio... */
            buffer = gst_collect_pads_peek (ffmpegmux->collect,
                (GstCollectData *) collect_pad);
            if (buffer) {
              st->codec->frame_size =
                  st->codec->sample_rate *
                  GST_BUFFER_DURATION (buffer) / GST_SECOND;
              gst_buffer_unref (buffer);
            }
          }
        }
      }
    }

    /* tags */
    tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (ffmpegmux));
    if (tags) {
      gint i;
      gchar *s;

      /* get the interesting ones */
      if (gst_tag_list_get_string (tags, GST_TAG_TITLE, &s)) {
        strncpy (ffmpegmux->context->title, s,
            sizeof (ffmpegmux->context->title));
      }
      if (gst_tag_list_get_string (tags, GST_TAG_ARTIST, &s)) {
        strncpy (ffmpegmux->context->author, s,
            sizeof (ffmpegmux->context->author));
      }
      if (gst_tag_list_get_string (tags, GST_TAG_COPYRIGHT, &s)) {
        strncpy (ffmpegmux->context->copyright, s,
            sizeof (ffmpegmux->context->copyright));
      }
      if (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &s)) {
        strncpy (ffmpegmux->context->comment, s,
            sizeof (ffmpegmux->context->comment));
      }
      if (gst_tag_list_get_string (tags, GST_TAG_ALBUM, &s)) {
        strncpy (ffmpegmux->context->album, s,
            sizeof (ffmpegmux->context->album));
      }
      if (gst_tag_list_get_string (tags, GST_TAG_GENRE, &s)) {
        strncpy (ffmpegmux->context->genre, s,
            sizeof (ffmpegmux->context->genre));
      }
      if (gst_tag_list_get_int (tags, GST_TAG_TRACK_NUMBER, &i)) {
        ffmpegmux->context->track = i;
      }
    }

    /* set the streamheader flag for gstffmpegprotocol if codec supports it */
    if (!strcmp (ffmpegmux->context->oformat->name, "flv")) {
      open_flags |= GST_FFMPEG_URL_STREAMHEADER;
    }

    if (url_fopen (&ffmpegmux->context->pb,
            ffmpegmux->context->filename, open_flags) < 0) {
      GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, TOO_LAZY, (NULL),
          ("Failed to open stream context in ffmux"));
      return GST_FLOW_ERROR;
    }

    if (av_set_parameters (ffmpegmux->context, NULL) < 0) {
      GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, INIT, (NULL),
          ("Failed to initialize muxer"));
      return GST_FLOW_ERROR;
    }

    /* now open the mux format */
    if (av_write_header (ffmpegmux->context) < 0) {
      GST_ELEMENT_ERROR (ffmpegmux, LIBRARY, SETTINGS, (NULL),
          ("Failed to write file header - check codec settings"));
      return GST_FLOW_ERROR;
    }

    /* we're now opened */
    ffmpegmux->opened = TRUE;

    /* flush the header so it will be used as streamheader */
    put_flush_packet (ffmpegmux->context->pb);
  }

  /* take the one with earliest timestamp,
   * and push it forward */
  best_pad = NULL;
  best_time = GST_CLOCK_TIME_NONE;
  for (collected = ffmpegmux->collect->data; collected;
      collected = g_slist_next (collected)) {
    GstFFMpegMuxPad *collect_pad = (GstFFMpegMuxPad *) collected->data;
    GstBuffer *buffer = gst_collect_pads_peek (ffmpegmux->collect,
        (GstCollectData *) collect_pad);

    /* if there's no buffer, just continue */
    if (buffer == NULL) {
      continue;
    }

    /* if we have no buffer yet, just use the first one */
    if (best_pad == NULL) {
      best_pad = collect_pad;
      best_time = GST_BUFFER_TIMESTAMP (buffer);
      goto next_pad;
    }

    /* if we do have one, only use this one if it's older */
    if (GST_BUFFER_TIMESTAMP (buffer) < best_time) {
      best_time = GST_BUFFER_TIMESTAMP (buffer);
      best_pad = collect_pad;
    }

  next_pad:
    gst_buffer_unref (buffer);

    /* Mux buffers with invalid timestamp first */
    if (!GST_CLOCK_TIME_IS_VALID (best_time))
      break;
  }

  /* now handle the buffer, or signal EOS if we have
   * no buffers left */
  if (best_pad != NULL) {
    GstBuffer *buf;
    AVPacket pkt;
    gboolean need_free = FALSE;

    /* push out current buffer */
    buf = gst_collect_pads_pop (ffmpegmux->collect,
        (GstCollectData *) best_pad);

    ffmpegmux->context->streams[best_pad->padnum]->codec->frame_number++;

    /* set time */
    pkt.pts = gst_ffmpeg_time_gst_to_ff (GST_BUFFER_TIMESTAMP (buf),
        ffmpegmux->context->streams[best_pad->padnum]->time_base);
    pkt.dts = pkt.pts;

    if (strcmp (ffmpegmux->context->oformat->name, "gif") == 0) {
      AVStream *st = ffmpegmux->context->streams[best_pad->padnum];
      AVPicture src, dst;

      need_free = TRUE;
      pkt.size = st->codec->width * st->codec->height * 3;
      pkt.data = g_malloc (pkt.size);

      dst.data[0] = pkt.data;
      dst.data[1] = NULL;
      dst.data[2] = NULL;
      dst.linesize[0] = st->codec->width * 3;

      gst_ffmpeg_avpicture_fill (&src, GST_BUFFER_DATA (buf),
          PIX_FMT_RGB24, st->codec->width, st->codec->height);

      av_picture_copy (&dst, &src, PIX_FMT_RGB24,
          st->codec->width, st->codec->height);
    } else {
      pkt.data = GST_BUFFER_DATA (buf);
      pkt.size = GST_BUFFER_SIZE (buf);
    }

    pkt.stream_index = best_pad->padnum;
    pkt.flags = 0;

    if (!GST_BUFFER_FLAG_IS_SET (buf, GST_BUFFER_FLAG_DELTA_UNIT))
      pkt.flags |= PKT_FLAG_KEY;

    if (GST_BUFFER_DURATION_IS_VALID (buf))
      pkt.duration =
          gst_ffmpeg_time_gst_to_ff (GST_BUFFER_DURATION (buf),
          ffmpegmux->context->streams[best_pad->padnum]->time_base);
    else
      pkt.duration = 0;
    av_write_frame (ffmpegmux->context, &pkt);
    gst_buffer_unref (buf);
    if (need_free)
      g_free (pkt.data);
  } else {
    /* close down */
    av_write_trailer (ffmpegmux->context);
    ffmpegmux->opened = FALSE;
    put_flush_packet (ffmpegmux->context->pb);
    url_fclose (ffmpegmux->context->pb);
    gst_pad_push_event (ffmpegmux->srcpad, gst_event_new_eos ());
    return GST_FLOW_UNEXPECTED;
  }

  return GST_FLOW_OK;
}