static gboolean rc_plugin_tag_flac_change(const gchar *src_path, const gchar *target_path, const GstTagList *tag_list) { GstElement *tag_filesrc = NULL; GstElement *tag_filesink = NULL; GstElement *tagger = NULL; GstBus *bus = NULL; if(src_path==NULL) return FALSE; if(target_path==NULL) return FALSE; if(tag_list==NULL) return FALSE; if(g_access(src_path, W_OK)!=0) return FALSE; tagger = gst_element_factory_make("flactag", "flac-tagger"); if(tagger==NULL) goto error_out; tag_filesrc = gst_element_factory_make("filesrc", "tag-filesrc"); if(tag_filesrc==NULL) goto error_out; tag_filesink = gst_element_factory_make("filesink", "tag-filesink"); if(tag_filesink==NULL) goto error_out; g_object_set(G_OBJECT(tag_filesrc), "location", src_path, NULL); g_object_set(G_OBJECT(tag_filesink), "location", target_path, NULL); tagger_pipeline = gst_pipeline_new("tagger-pipeline"); gst_tag_setter_merge_tags(GST_TAG_SETTER(tagger), tag_list, GST_TAG_MERGE_REPLACE); gst_bin_add_many(GST_BIN(tagger_pipeline), tag_filesrc, tagger, tag_filesink, NULL); if(!gst_element_link_many(tag_filesrc, tagger, tag_filesink, NULL)) goto error_out; bus = gst_pipeline_get_bus(GST_PIPELINE(tagger_pipeline)); gst_bus_add_watch(bus, (GstBusFunc)rc_plugin_tag_writer_bus_cb, NULL); gst_object_unref(bus); gst_element_set_state(tagger_pipeline, GST_STATE_NULL); gst_element_set_state(tagger_pipeline, GST_STATE_READY); if(gst_element_set_state(tagger_pipeline, GST_STATE_PLAYING) ==GST_STATE_CHANGE_FAILURE) goto error_out; return TRUE; error_out: if(tagger!=NULL) gst_object_unref(tagger); if(tag_filesrc!=NULL) gst_object_unref(tag_filesrc); if(tag_filesink!=NULL) gst_object_unref(tag_filesink); if(tagger_pipeline!=NULL) gst_object_unref(tagger_pipeline); tagger_pipeline = NULL; return FALSE; }
static gboolean gst_vorbis_enc_stop (GstAudioEncoder * enc) { GstVorbisEnc *vorbisenc = GST_VORBISENC (enc); GST_DEBUG_OBJECT (enc, "stop"); vorbis_block_clear (&vorbisenc->vb); vorbis_dsp_clear (&vorbisenc->vd); vorbis_info_clear (&vorbisenc->vi); g_free (vorbisenc->last_message); vorbisenc->last_message = NULL; gst_tag_list_free (vorbisenc->tags); vorbisenc->tags = NULL; g_slist_foreach (vorbisenc->headers, (GFunc) gst_buffer_unref, NULL); vorbisenc->headers = NULL; gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; }
static gboolean gst_vp8_enc_sink_event (GstPad * pad, GstEvent * event) { GstVP8Enc *enc = GST_VP8_ENC (gst_pad_get_parent (pad)); gboolean ret; if (GST_EVENT_TYPE (event) == GST_EVENT_TAG) { GstTagList *list; GstTagSetter *setter = GST_TAG_SETTER (enc); const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter); gst_event_parse_tag (event, &list); gst_tag_setter_merge_tags (setter, list, mode); } ret = enc->base_sink_event_func (pad, event); gst_object_unref (enc); return ret; }
static gboolean gst_speex_enc_stop (GstAudioEncoder * benc) { GstSpeexEnc *enc = GST_SPEEX_ENC (benc); GST_DEBUG_OBJECT (enc, "stop"); enc->header_sent = FALSE; if (enc->state) { speex_encoder_destroy (enc->state); enc->state = NULL; } speex_bits_destroy (&enc->bits); speex_bits_set_bit_buffer (&enc->bits, NULL, 0); gst_tag_list_unref (enc->tags); enc->tags = NULL; gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; }
static gboolean gst_opus_enc_stop (GstAudioEncoder * benc) { GstOpusEnc *enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "stop"); enc->header_sent = FALSE; if (enc->state) { opus_multistream_encoder_destroy (enc->state); enc->state = NULL; } gst_tag_list_free (enc->tags); enc->tags = NULL; g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); g_slist_free (enc->headers); enc->headers = NULL; gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; }
static GstStateChangeReturn gst_ffmpegmux_change_state (GstElement * element, GstStateChange transition) { GstFlowReturn ret; GstFFMpegMux *ffmpegmux = (GstFFMpegMux *) (element); switch (transition) { case GST_STATE_CHANGE_NULL_TO_READY: break; case GST_STATE_CHANGE_READY_TO_PAUSED: gst_collect_pads_start (ffmpegmux->collect); break; case GST_STATE_CHANGE_PAUSED_TO_PLAYING: break; case GST_STATE_CHANGE_PAUSED_TO_READY: gst_collect_pads_stop (ffmpegmux->collect); break; default: break; } ret = GST_ELEMENT_CLASS (parent_class)->change_state (element, transition); switch (transition) { case GST_STATE_CHANGE_PLAYING_TO_PAUSED: break; case GST_STATE_CHANGE_PAUSED_TO_READY: gst_tag_setter_reset_tags (GST_TAG_SETTER (ffmpegmux)); if (ffmpegmux->opened) { ffmpegmux->opened = FALSE; url_fclose (ffmpegmux->context->pb); } break; case GST_STATE_CHANGE_READY_TO_NULL: break; default: break; } return ret; }
static gboolean gst_celt_enc_stop (GstAudioEncoder * benc) { GstCeltEnc *enc = GST_CELT_ENC (benc); GST_DEBUG_OBJECT (enc, "stop"); enc->header_sent = FALSE; if (enc->state) { celt_encoder_destroy (enc->state); enc->state = NULL; } if (enc->mode) { celt_mode_destroy (enc->mode); enc->mode = NULL; } memset (&enc->header, 0, sizeof (enc->header)); gst_tag_setter_reset_tags (GST_TAG_SETTER (enc)); return TRUE; }
GST_END_TEST GST_START_TEST (test_merge_modes_skip_empty) { GstTagMergeMode mode; for (mode = GST_TAG_MERGE_REPLACE_ALL; mode < GST_TAG_MERGE_COUNT; mode++) { gint i; for (i = 0; i < 2; i++) { GstElement *enc; GstTagSetter *setter; GstTagList *list1, *list2, *merged; enc = g_object_new (GST_TYPE_DUMMY_ENC, NULL); fail_unless (enc != NULL); setter = GST_TAG_SETTER (enc); list1 = gst_tag_list_new_empty (); list2 = gst_tag_list_new_empty (); if (i == 1) { gst_tag_list_add (list2, GST_TAG_MERGE_APPEND, GST_TAG_ARTIST, "artist2", NULL); } gst_tag_setter_merge_tags (setter, list2, mode); merged = gst_tag_list_merge (list1, list2, mode); fail_unless_equals_int (tag_list_length (gst_tag_setter_get_tag_list (setter)), tag_list_length (merged)); gst_tag_list_unref (list1); gst_tag_list_unref (list2); gst_tag_list_unref (merged); gst_object_unref (enc); } } }
static GstFlowReturn gst_opus_enc_handle_frame (GstAudioEncoder * benc, GstBuffer * buf) { GstOpusEnc *enc; GstFlowReturn ret = GST_FLOW_OK; enc = GST_OPUS_ENC (benc); GST_DEBUG_OBJECT (enc, "handle_frame"); if (!enc->header_sent) { GstCaps *caps; g_slist_foreach (enc->headers, (GFunc) gst_buffer_unref, NULL); g_slist_free (enc->headers); enc->headers = NULL; gst_opus_header_create_caps (&caps, &enc->headers, enc->n_channels, enc->n_stereo_streams, enc->sample_rate, enc->channel_mapping_family, enc->decoding_channel_mapping, gst_tag_setter_get_tag_list (GST_TAG_SETTER (enc))); /* negotiate with these caps */ GST_DEBUG_OBJECT (enc, "here are the caps: %" GST_PTR_FORMAT, caps); gst_pad_set_caps (GST_AUDIO_ENCODER_SRC_PAD (enc), caps); gst_caps_unref (caps); enc->header_sent = TRUE; } GST_DEBUG_OBJECT (enc, "received buffer %p of %u bytes", buf, buf ? GST_BUFFER_SIZE (buf) : 0); ret = gst_opus_enc_encode (enc, buf); return ret; }
static gboolean gst_jif_mux_sink_event (GstPad * pad, GstEvent * event) { GstJifMux *self = GST_JIF_MUX (GST_PAD_PARENT (pad)); gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_TAG: { GstTagList *list; GstTagSetter *setter = GST_TAG_SETTER (self); const GstTagMergeMode mode = gst_tag_setter_get_tag_merge_mode (setter); gst_event_parse_tag (event, &list); gst_tag_setter_merge_tags (setter, list, mode); break; } default: break; } ret = gst_pad_event_default (pad, event); return ret; }
static gboolean gst_speex_enc_sinkevent (GstPad * pad, GstEvent * event) { gboolean res = TRUE; GstSpeexEnc *enc; enc = GST_SPEEX_ENC (gst_pad_get_parent (pad)); switch (GST_EVENT_TYPE (event)) { case GST_EVENT_EOS: if (enc->setup) gst_speex_enc_encode (enc, TRUE); res = gst_pad_event_default (pad, event); break; case GST_EVENT_TAG: { if (enc->tags) { GstTagList *list; gst_event_parse_tag (event, &list); gst_tag_list_insert (enc->tags, list, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (enc))); } else { g_assert_not_reached (); } res = gst_pad_event_default (pad, event); break; } default: res = gst_pad_event_default (pad, event); break; } gst_object_unref (enc); return res; }
static gpointer test_threads_thread_func3 (gpointer data) { GstTagSetter *setter = GST_TAG_SETTER (data); GTimer *timer; timer = g_timer_new (); g_atomic_int_inc (&threads_running); while (g_atomic_int_get (&spin_and_wait)) g_usleep (0); GST_INFO ("Go!"); g_timer_start (timer); while (g_timer_elapsed (timer, NULL) < THREADS_TEST_SECONDS) { gst_tag_setter_reset_tags (setter); } g_timer_destroy (timer); GST_INFO ("Done"); return NULL; }
static GstTagList * gst_tag_mux_get_tags (GstTagMux * mux) { GstTagSetter *tagsetter = GST_TAG_SETTER (mux); const GstTagList *tagsetter_tags; GstTagMergeMode merge_mode; if (mux->priv->final_tags) return mux->priv->final_tags; tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter); merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter); GST_LOG_OBJECT (mux, "merging tags, merge mode = %d", merge_mode); GST_LOG_OBJECT (mux, "event tags: %" GST_PTR_FORMAT, mux->priv->event_tags); GST_LOG_OBJECT (mux, "set tags: %" GST_PTR_FORMAT, tagsetter_tags); mux->priv->final_tags = gst_tag_list_merge (tagsetter_tags, mux->priv->event_tags, merge_mode); GST_LOG_OBJECT (mux, "final tags: %" GST_PTR_FORMAT, mux->priv->final_tags); return mux->priv->final_tags; }
void TransCoder::pipeRun(int ind) { GstElement *source, *dec, *conv, *encoder, *muxer, *fileout, *tagger, *volume; GstBus *bus; GstState state; GstPad *audiopad;//, *encoderpad; numTrack = ind; bool preenc; int vbr = 0; loop = g_main_loop_new (NULL, FALSE); pipeline = gst_pipeline_new ("audio-transcoder"); // Входной файл source = gst_element_factory_make ("filesrc", "file-source"); g_object_set (source, "location", localFileNamesEncoder->fromUnicode(refparser->getSoundFile()).data(), NULL); dec = gst_element_factory_make ("decodebin", "decoder"); g_signal_connect (dec, "new-decoded-pad", G_CALLBACK (cb_newpad), NULL); gst_bin_add_many (GST_BIN (pipeline), source, dec, NULL); gst_element_link (source, dec); audio = gst_bin_new ("audiobin"); conv = gst_element_factory_make ("audioconvert", "aconv"); volume = gst_element_factory_make ("volume", "vol"); audiopad = gst_element_get_static_pad (conv, "sink"); fileout = gst_element_factory_make ("filesink", "file-out"); // Выходной файл QRegExp rxFileSlash("/"); QRegExp rxTilda("^~(.*)"); QString filename; QTextStream out(stdout); QString comment = trUtf8("CuePlayer ") + qApp->applicationVersion(); QString trackName = refparser->getTrackTitle(ind); QString title = refparser->getTitle(); trackName.replace(rxFileSlash, trUtf8("⁄")); title.replace(rxFileSlash, trUtf8("⁄")); if (rxTilda.indexIn(lineEdit->text()) != -1) lineEdit->setText(QDir::homePath() + rxTilda.cap(1)); QDir updir(lineEdit->text()); if (!updir.exists()) updir.mkdir(lineEdit->text()); QString dirname = lineEdit->text() + "/" + title; QDir dir(dirname); if (!dir.exists()) dir.mkdir(dirname); if (ind < 10) filename = dirname + "/" + "0" + QString::number(ind,10) + " - " + trackName + "." + containerBox->currentText(); else filename = dirname + "/" + QString::number(ind,10) + " - " + trackName + "." + containerBox->currentText(); g_object_set (fileout, "location", localFileNamesEncoder->fromUnicode(filename).data(), NULL); tmpfile.setFileName(dirname + "/" + QString::number(ind,10) + " - " + trackName + ".tmp"); switch (codecBox->currentIndex()) { case CODEC_VORBIS: if (tmpfile.exists()) { g_object_set (source, "location", localFileNamesEncoder->fromUnicode(tmpfile.fileName()).data(), NULL); encoder = gst_element_factory_make ("vorbisenc", "audio-encoder"); tagger = gst_element_factory_make ("vorbistag", "tagger"); muxer = gst_element_factory_make ("oggmux", "audio-muxer"); gst_bin_add_many (GST_BIN (audio), conv, encoder, tagger, muxer, fileout, NULL); gst_element_link_many (conv, encoder, tagger, muxer, fileout, NULL); if (!settings.value("preferences/vorbisquality").isNull()) { g_object_set (encoder, "max-bitrate", settings.value("preferences/vorbismaxbitrate").toInt(), "bitrate", settings.value("preferences/vorbisbitrate").toInt(), "min-bitrate", settings.value("preferences/vorbisminbitrate").toInt(), "quality", settings.value("preferences/vorbisquality").toDouble()/10, "managed", settings.value("preferences/vorbismanaged").toBool(), NULL); } gst_tag_setter_add_tags (GST_TAG_SETTER (tagger), GST_TAG_MERGE_REPLACE_ALL, GST_TAG_TITLE, refparser->getTrackTitle(ind).toUtf8().data(), GST_TAG_ARTIST, refparser->getPerformer().toUtf8().data(), GST_TAG_TRACK_NUMBER, ind, GST_TAG_TRACK_COUNT, refparser->getTrackNumber(), GST_TAG_ALBUM, refparser->getAlbum().toUtf8().data(), GST_TAG_ENCODER, APPNAME, GST_TAG_ENCODER_VERSION, qApp->applicationVersion().toUtf8().data(), GST_TAG_COMMENT, comment.toUtf8().data(), GST_TAG_CODEC, "vorbis", NULL); containerBox->setCurrentIndex(CODEC_VORBIS); } else { preenc = true; encoder = gst_element_factory_make ("flacenc", "audio-encoder"); g_object_set(encoder, "quality", 0, NULL); gst_bin_add_many (GST_BIN (audio), conv, volume, encoder, fileout, NULL); gst_element_link_many (conv, volume, encoder, fileout, NULL); g_object_set (volume, "mute", true, NULL); g_object_set (fileout, "location", localFileNamesEncoder->fromUnicode(tmpfile.fileName()).data(), NULL); } break; case CODEC_LAME: encoder = gst_element_factory_make ("lame", "audio-encoder"); muxer = gst_element_factory_make ("id3v2mux", "audio-muxer"); gst_bin_add_many (GST_BIN (audio), conv, encoder, muxer, fileout, NULL); gst_element_link_many (conv, encoder, muxer, fileout, NULL); if (settings.value("preferences/lamevbr").toInt()) vbr = settings.value("preferences/lamevbr").toInt() + 1; if (!settings.value("preferences/lamequality").isNull()) { g_object_set (encoder, "bitrate", bitrateList.at(settings.value("preferences/lamebitrate").toInt()), //"compression-ratio", settings.value("preferences/lamecompressionratio").toInt(), // Баг. Перекрывает VBR "quality", settings.value("preferences/lamequality").toInt(), "mode", settings.value("preferences/lamemode").toInt(), "force-ms", settings.value("preferences/lameforcems").toBool(), "free-format", settings.value("preferences/lamefreeformat").toBool(), "copyright", settings.value("preferences/lamecopyright").toBool(), "original", settings.value("preferences/lameoriginal").toBool(), "error-protection", settings.value("preferences/lameerrprot").toBool(), "padding-type", settings.value("preferences/lamepaddingtype").toInt(), "extension", settings.value("preferences/lameextension").toBool(), "strict-iso", settings.value("preferences/lamestrictiso").toBool(), "vbr", vbr, "disable-reservoir", settings.value("preferences/lamedisrese").toBool(), NULL); } gst_tag_setter_add_tags (GST_TAG_SETTER (muxer), GST_TAG_MERGE_REPLACE_ALL, GST_TAG_TITLE, refparser->getTrackTitle(ind).toUtf8().data(), GST_TAG_ARTIST, refparser->getPerformer().toUtf8().data(), GST_TAG_TRACK_NUMBER, ind, GST_TAG_TRACK_COUNT, refparser->getTrackNumber(), GST_TAG_ALBUM, refparser->getAlbum().toUtf8().data(), GST_TAG_ENCODER, APPNAME, GST_TAG_ENCODER_VERSION, qApp->applicationVersion().toUtf8().data(), GST_TAG_COMMENT, comment.toUtf8().data(), GST_TAG_CODEC, "lame", NULL); containerBox->setCurrentIndex(CODEC_LAME); break; case CODEC_FLAC: encoder = gst_element_factory_make ("flacenc", "audio-encoder"); tagger = gst_element_factory_make ("flactag", "tagger"); if (!settings.value("preferences/flacquality").isNull()) { g_object_set (encoder, "quality", settings.value("preferences/flacquality").toInt(), "streamable-subset", settings.value("preferences/flacstreamablesubset").toBool(), "mid-side-stereo", settings.value("preferences/flacmidsidestereo").toBool(), "loose-mid-side-stereo", settings.value("preferences/flacloosemidsidestereo").toBool(), "blocksize", settings.value("preferences/flacblocksize").toInt(), "max-lpc-order", settings.value("preferences/flacmaxlpcorder").toInt(), "qlp-coeff-precision", settings.value("preferences/flacqlpcoeffprecision").toInt(), "qlp-coeff-prec-search", settings.value("preferences/flacqlpcoeffprecsearch").toBool(), "escape-coding", settings.value("preferences/flacescapecoding").toBool(), "exhaustive-model-search", settings.value("preferences/flacexhaustivemodelsearch").toBool(), "min-residual-partition-order", settings.value("preferences/flacminresidualpartitionorder").toInt(), "max-residual-partition-order", settings.value("preferences/flacmaxresidualpartitionorder").toInt(), "rice-parameter-search-dist", settings.value("preferences/flacriceparametersearchdist").toInt(), NULL); } gst_bin_add_many (GST_BIN (audio), conv, encoder, tagger, fileout, NULL); gst_element_link_many (conv, encoder, tagger, fileout, NULL); gst_tag_setter_add_tags (GST_TAG_SETTER (tagger), GST_TAG_MERGE_REPLACE_ALL, GST_TAG_TITLE, refparser->getTrackTitle(ind).toUtf8().data(), GST_TAG_ARTIST, refparser->getPerformer().toUtf8().data(), GST_TAG_TRACK_NUMBER, ind, GST_TAG_TRACK_COUNT, refparser->getTrackNumber(), GST_TAG_ALBUM, refparser->getAlbum().toUtf8().data(), GST_TAG_ENCODER, APPNAME, GST_TAG_ENCODER_VERSION, qApp->applicationVersion().toUtf8().data(), GST_TAG_COMMENT, comment.toUtf8().data(), GST_TAG_CODEC, "flac", NULL); containerBox->setCurrentIndex(CODEC_FLAC); break; case CODEC_FAAC: encoder = gst_element_factory_make ("faac", "audio-encoder"); gst_bin_add_many (GST_BIN (audio), conv, encoder, fileout, NULL); gst_element_link_many (conv, encoder, fileout, NULL); if (!settings.value("preferences/faacprofile").isNull()) { g_object_set(encoder, "outputformat", settings.value("preferences/faacoutputformat").toInt(), "bitrate", settings.value("preferences/faacbitrate").toInt(), "profile", settings.value("preferences/faacprofile").toInt()+1, "tns", settings.value("preferences/faactns").toBool(), "midside", settings.value("preferences/faacmidside").toBool(), "shortctl", settings.value("preferences/faacshortctl").toInt(), NULL); } containerBox->setCurrentIndex(CODEC_FAAC); break; case CODEC_NO: default: break; } gst_element_add_pad (audio, gst_ghost_pad_new ("sink", audiopad)); gst_object_unref (audiopad); gst_bin_add (GST_BIN (pipeline), audio); bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); gst_bus_add_watch (bus, bus_call, loop); gst_object_unref (bus); //g_signal_connect (pipeline, "deep-notify", G_CALLBACK (gst_object_default_deep_notify), NULL); // Дебаг! out << trUtf8("Кодируется: ") << refparser->getSoundFile() << endl; gst_element_set_state (pipeline, GST_STATE_PLAYING); gst_element_get_state( GST_ELEMENT(pipeline), &state, NULL, GST_MSECOND * 300); if (state == GST_STATE_PLAYING) { timer->start(TIME); if (ind == refparser->getTrackNumber()) setTrackTime(refparser->getTrackIndex(ind),saveTotalTime); else setTrackTime(refparser->getTrackIndex(ind),refparser->getTrackIndex(ind+1)); if (preenc) g_object_set (volume, "mute", false, NULL); g_print ("Запущено...\n"); g_main_loop_run (loop); } else { out << trUtf8("Ошибка перехода в PLAYING\n"); transcode = false; stopAll(); } }
int main (int argc, char *argv[]) { GstElement *bin, *filesrc, *decoder, *encoder, *filesink; gchar *artist, *title, *ext, *filename; /* initialize GStreamer */ gst_init (&argc, &argv); /* check that the argument is there */ if (argc != 2) { g_print ("usage: %s <mp3 file>\n", argv[0]); return 1; } /* parse the mp3 name */ artist = strrchr (argv[1], '/'); if (artist == NULL) artist = argv[1]; artist = g_strdup (artist); ext = strrchr (artist, '.'); if (ext) *ext = '\0'; title = strstr (artist, " - "); if (title == NULL) { g_print ("The format of the mp3 file is invalid.\n"); g_print ("It needs to be in the form of artist - title.mp3.\n"); return 1; } *title = '\0'; title += 3; /* create a new bin to hold the elements */ bin = gst_pipeline_new ("pipeline"); g_assert (bin); /* create a file reader */ filesrc = gst_element_factory_make ("filesrc", "disk_source"); g_assert (filesrc); /* now it's time to get the decoder */ decoder = gst_element_factory_make ("mad", "decode"); if (!decoder) { g_print ("could not find plugin \"mad\""); return 1; } /* create the encoder */ encoder = gst_element_factory_make ("vorbisenc", "encoder"); if (!encoder) { g_print ("cound not find plugin \"vorbisenc\""); return 1; } /* and a file writer */ filesink = gst_element_factory_make ("filesink", "filesink"); g_assert (filesink); /* set the filenames */ filename = g_strdup_printf ("%s.ogg", argv[1]); /* easy solution */ g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); g_object_set (G_OBJECT (filesink), "location", filename, NULL); g_free (filename); /* make sure the tag setter uses our stuff (though that should already be default) */ gst_tag_setter_set_merge_mode (GST_TAG_SETTER (encoder), GST_TAG_MERGE_KEEP); /* set the tagging information */ gst_tag_setter_add (GST_TAG_SETTER (encoder), GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, artist, GST_TAG_TITLE, title, NULL); /* add objects to the main pipeline */ gst_bin_add_many (GST_BIN (bin), filesrc, decoder, encoder, filesink, NULL); /* link the elements */ gst_element_link_many (filesrc, decoder, encoder, filesink, NULL); /* start playing */ gst_element_set_state (bin, GST_STATE_PLAYING); while (gst_bin_iterate (GST_BIN (bin))); /* stop the bin */ gst_element_set_state (bin, GST_STATE_NULL); return 0; }
static gboolean gst_vp8_enc_set_format (GstBaseVideoEncoder * base_video_encoder, GstVideoState * state) { GstVP8Enc *encoder; vpx_codec_enc_cfg_t cfg; vpx_codec_err_t status; vpx_image_t *image; guint8 *data = NULL; GstCaps *caps; gboolean ret; encoder = GST_VP8_ENC (base_video_encoder); GST_DEBUG_OBJECT (base_video_encoder, "set_format"); if (encoder->inited) { GST_DEBUG_OBJECT (base_video_encoder, "refusing renegotiation"); return FALSE; } status = vpx_codec_enc_config_default (&vpx_codec_vp8_cx_algo, &cfg, 0); if (status != VPX_CODEC_OK) { GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, ("Failed to get default encoder configuration"), ("%s", gst_vpx_error_name (status))); return FALSE; } /* Scale default bitrate to our size */ cfg.rc_target_bitrate = gst_util_uint64_scale (cfg.rc_target_bitrate, state->width * state->height, cfg.g_w * cfg.g_h); cfg.g_w = state->width; cfg.g_h = state->height; cfg.g_timebase.num = state->fps_d; cfg.g_timebase.den = state->fps_n; cfg.g_error_resilient = encoder->error_resilient; cfg.g_lag_in_frames = encoder->max_latency; cfg.g_threads = encoder->threads; cfg.rc_end_usage = encoder->mode; cfg.rc_2pass_vbr_minsection_pct = encoder->minsection_pct; cfg.rc_2pass_vbr_maxsection_pct = encoder->maxsection_pct; /* Standalone qp-min do not make any sence, with bitrate=0 and qp-min=1 * encoder will use only default qp-max=63. Also this will make * worst possbile quality. */ if (encoder->bitrate != DEFAULT_BITRATE || encoder->max_quantizer != DEFAULT_MAX_QUANTIZER) { cfg.rc_target_bitrate = encoder->bitrate / 1000; cfg.rc_min_quantizer = encoder->min_quantizer; cfg.rc_max_quantizer = encoder->max_quantizer; } else { cfg.rc_min_quantizer = (gint) (63 - encoder->quality * 6.2); cfg.rc_max_quantizer = (gint) (63 - encoder->quality * 6.2); } cfg.rc_dropframe_thresh = encoder->drop_frame; cfg.rc_resize_allowed = encoder->resize_allowed; cfg.kf_mode = VPX_KF_AUTO; cfg.kf_min_dist = 0; cfg.kf_max_dist = encoder->max_keyframe_distance; cfg.g_pass = encoder->multipass_mode; if (encoder->multipass_mode == VPX_RC_FIRST_PASS) { encoder->first_pass_cache_content = g_byte_array_sized_new (4096); } else if (encoder->multipass_mode == VPX_RC_LAST_PASS) { GError *err = NULL; if (!encoder->multipass_cache_file) { GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ, ("No multipass cache file provided"), (NULL)); return FALSE; } if (!g_file_get_contents (encoder->multipass_cache_file, (gchar **) & encoder->last_pass_cache_content.buf, &encoder->last_pass_cache_content.sz, &err)) { GST_ELEMENT_ERROR (encoder, RESOURCE, OPEN_READ, ("Failed to read multipass cache file provided"), ("%s", err->message)); g_error_free (err); return FALSE; } cfg.rc_twopass_stats_in = encoder->last_pass_cache_content; } status = vpx_codec_enc_init (&encoder->encoder, &vpx_codec_vp8_cx_algo, &cfg, 0); if (status != VPX_CODEC_OK) { GST_ELEMENT_ERROR (encoder, LIBRARY, INIT, ("Failed to initialize encoder"), ("%s", gst_vpx_error_name (status))); return FALSE; } /* FIXME move this to a set_speed() function */ status = vpx_codec_control (&encoder->encoder, VP8E_SET_CPUUSED, (encoder->speed == 0) ? 0 : (encoder->speed - 1)); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_SET_CPUUSED to 0: %s", gst_vpx_error_name (status)); } status = vpx_codec_control (&encoder->encoder, VP8E_SET_NOISE_SENSITIVITY, encoder->noise_sensitivity); status = vpx_codec_control (&encoder->encoder, VP8E_SET_SHARPNESS, encoder->sharpness); status = vpx_codec_control (&encoder->encoder, VP8E_SET_STATIC_THRESHOLD, encoder->static_threshold); status = vpx_codec_control (&encoder->encoder, VP8E_SET_TOKEN_PARTITIONS, encoder->partitions); #if 0 status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_MAXFRAMES, encoder->arnr_maxframes); status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_STRENGTH, encoder->arnr_strength); status = vpx_codec_control (&encoder->encoder, VP8E_SET_ARNR_TYPE, encoder->arnr_type); #endif #ifdef HAVE_VP8ENC_TUNING status = vpx_codec_control (&encoder->encoder, VP8E_SET_TUNING, encoder->tuning); #endif status = vpx_codec_control (&encoder->encoder, VP8E_SET_ENABLEAUTOALTREF, (encoder->auto_alt_ref_frames ? 1 : 0)); if (status != VPX_CODEC_OK) { GST_WARNING_OBJECT (encoder, "Failed to set VP8E_ENABLEAUTOALTREF to %d: %s", (encoder->auto_alt_ref_frames ? 1 : 0), gst_vpx_error_name (status)); } cfg.g_lag_in_frames = encoder->lag_in_frames; gst_base_video_encoder_set_latency (base_video_encoder, 0, gst_util_uint64_scale (encoder->max_latency, state->fps_d * GST_SECOND, state->fps_n)); encoder->inited = TRUE; /* prepare cached image buffer setup */ image = &encoder->image; memset (image, 0, sizeof (*image)); image->fmt = VPX_IMG_FMT_I420; image->bps = 12; image->x_chroma_shift = image->y_chroma_shift = 1; image->w = image->d_w = state->width; image->h = image->d_h = state->height; image->stride[VPX_PLANE_Y] = gst_video_format_get_row_stride (state->format, 0, state->width); image->stride[VPX_PLANE_U] = gst_video_format_get_row_stride (state->format, 1, state->width); image->stride[VPX_PLANE_V] = gst_video_format_get_row_stride (state->format, 2, state->width); image->planes[VPX_PLANE_Y] = data + gst_video_format_get_component_offset (state->format, 0, state->width, state->height); image->planes[VPX_PLANE_U] = data + gst_video_format_get_component_offset (state->format, 1, state->width, state->height); image->planes[VPX_PLANE_V] = data + gst_video_format_get_component_offset (state->format, 2, state->width, state->height); caps = gst_caps_new_simple ("video/x-vp8", "width", G_TYPE_INT, state->width, "height", G_TYPE_INT, state->height, "framerate", GST_TYPE_FRACTION, state->fps_n, state->fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, state->par_n, state->par_d, NULL); { GstStructure *s; GstBuffer *stream_hdr, *vorbiscomment; const GstTagList *iface_tags; GValue array = { 0, }; GValue value = { 0, }; s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); data = GST_BUFFER_DATA (stream_hdr); GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, state->width); GST_WRITE_UINT16_BE (data + 10, state->height); GST_WRITE_UINT24_BE (data + 12, state->par_n); GST_WRITE_UINT24_BE (data + 15, state->par_d); GST_WRITE_UINT32_BE (data + 18, state->fps_n); GST_WRITE_UINT32_BE (data + 22, state->fps_d); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_IN_CAPS); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (base_video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer (iface_tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_IN_CAPS); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); } ret = gst_pad_set_caps (GST_BASE_VIDEO_CODEC_SRC_PAD (encoder), caps); gst_caps_unref (caps); return ret; }
static GstCaps * gst_vp8_enc_get_caps (GstBaseVideoEncoder * base_video_encoder) { GstCaps *caps; const GstVideoState *state; GstTagList *tags = NULL; const GstTagList *iface_tags; GstBuffer *stream_hdr, *vorbiscomment; guint8 *data; GstStructure *s; GValue array = { 0 }; GValue value = { 0 }; state = gst_base_video_encoder_get_state (base_video_encoder); caps = gst_caps_new_simple ("video/x-vp8", "width", G_TYPE_INT, state->width, "height", G_TYPE_INT, state->height, "framerate", GST_TYPE_FRACTION, state->fps_n, state->fps_d, "pixel-aspect-ratio", GST_TYPE_FRACTION, state->par_n, state->par_d, NULL); s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); data = GST_BUFFER_DATA (stream_hdr); GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, state->width); GST_WRITE_UINT16_BE (data + 10, state->height); GST_WRITE_UINT24_BE (data + 12, state->par_n); GST_WRITE_UINT24_BE (data + 15, state->par_d); GST_WRITE_UINT32_BE (data + 18, state->fps_n); GST_WRITE_UINT32_BE (data + 22, state->fps_d); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_IN_CAPS); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (base_video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer ((iface_tags) ? iface_tags : tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_IN_CAPS); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); return caps; }
int main (int argc, char *argv[]) { GstElement *bin, *filesrc, *decoder, *encoder, *filesink; gchar *artist, *title, *ext, *filename; /* initialize GStreamer */ gst_init (&argc, &argv); /* check that the argument is there */ if (argc != 2) { g_print ("usage: %s <mp4 file>\n", argv[0]); return 1; } artist = strrchr (argv[1], '/'); if (artist == NULL) artist = argv[1]; artist = g_strdup (artist); ext = strrchr (artist, '.'); if (ext) *ext = '\0'; title = strstr (artist, " - "); if (title == NULL) { return 1; } *title = '\0'; title += 3; bin = gst_pipeline_new ("pipeline"); g_assert (bin); filesrc = gst_element_factory_make ("filesrc", "disk_source"); g_assert (filesrc); decoder = gst_element_factory_make ("h264dec", "decode"); if (!decoder) { return 1; } /* create the encoder */ encoder = gst_element_factory_make ("theoraenc", "encoder"); if (!encoder) { g_print ("cound not find plugin \"theoraenc\""); return 1; } filesink = gst_element_factory_make ("filesink", "filesink"); g_assert (filesink); filename = g_strdup_printf ("%s.ogg", argv[1]); /* easy solution */ g_object_set (G_OBJECT (filesrc), "location", argv[1], NULL); g_object_set (G_OBJECT (filesink), "location", filename, NULL); g_free (filename); gst_tag_setter_set_merge_mode (GST_TAG_SETTER (encoder), GST_TAG_MERGE_KEEP); gst_tag_setter_add (GST_TAG_SETTER (encoder), GST_TAG_MERGE_REPLACE, GST_TAG_ARTIST, artist, GST_TAG_TITLE, title, NULL); gst_bin_add_many (GST_BIN (bin), filesrc, decoder, encoder, filesink, NULL); /* link the elements */ gst_element_link_many (filesrc, decoder, encoder, filesink, NULL); /* start playing */ gst_element_set_state (bin, GST_STATE_PLAYING); while (gst_bin_iterate (GST_BIN (bin))); /* stop the bin */ gst_element_set_state (bin, GST_STATE_NULL); return 0; }
static GstFlowReturn gst_flac_tag_chain (GstPad * pad, GstObject * parent, GstBuffer * buffer) { GstFlacTag *tag; GstFlowReturn ret; GstMapInfo map; gsize size; ret = GST_FLOW_OK; tag = GST_FLAC_TAG (parent); gst_adapter_push (tag->adapter, buffer); /* Initial state, we don't even know if we are dealing with a flac file */ if (tag->state == GST_FLAC_TAG_STATE_INIT) { GstBuffer *id_buffer; if (gst_adapter_available (tag->adapter) < sizeof (FLAC_MAGIC)) goto cleanup; id_buffer = gst_adapter_take_buffer (tag->adapter, FLAC_MAGIC_SIZE); GST_DEBUG_OBJECT (tag, "looking for " FLAC_MAGIC " identifier"); if (gst_buffer_memcmp (id_buffer, 0, FLAC_MAGIC, FLAC_MAGIC_SIZE) == 0) { GST_DEBUG_OBJECT (tag, "pushing " FLAC_MAGIC " identifier buffer"); ret = gst_pad_push (tag->srcpad, id_buffer); if (ret != GST_FLOW_OK) goto cleanup; tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS; } else { /* FIXME: does that work well with FLAC files containing ID3v2 tags ? */ gst_buffer_unref (id_buffer); GST_ELEMENT_ERROR (tag, STREAM, WRONG_TYPE, (NULL), (NULL)); ret = GST_FLOW_ERROR; } } /* The fLaC magic string has been skipped, try to detect the beginning * of a metadata block */ if (tag->state == GST_FLAC_TAG_STATE_METADATA_BLOCKS) { guint type; gboolean is_last; const guint8 *block_header; g_assert (tag->metadata_block_size == 0); g_assert (tag->metadata_last_block == FALSE); /* The header of a flac metadata block is 4 bytes long: * 1st bit: indicates whether this is the last metadata info block * 7 next bits: 4 if vorbis comment block * 24 next bits: size of the metadata to follow (big endian) */ if (gst_adapter_available (tag->adapter) < 4) goto cleanup; block_header = gst_adapter_map (tag->adapter, 4); is_last = ((block_header[0] & 0x80) == 0x80); type = block_header[0] & 0x7F; size = (block_header[1] << 16) | (block_header[2] << 8) | block_header[3]; gst_adapter_unmap (tag->adapter); /* The 4 bytes long header isn't included in the metadata size */ tag->metadata_block_size = size + 4; tag->metadata_last_block = is_last; GST_DEBUG_OBJECT (tag, "got metadata block: %" G_GSIZE_FORMAT " bytes, type %d, " "is vorbiscomment: %d, is last: %d", size, type, (type == 0x04), is_last); /* Metadata blocks of type 4 are vorbis comment blocks */ if (type == 0x04) { tag->state = GST_FLAC_TAG_STATE_VC_METADATA_BLOCK; } else { tag->state = GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK; } } /* Reads a metadata block */ if ((tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) || (tag->state == GST_FLAC_TAG_STATE_VC_METADATA_BLOCK)) { GstBuffer *metadata_buffer; if (gst_adapter_available (tag->adapter) < tag->metadata_block_size) goto cleanup; metadata_buffer = gst_adapter_take_buffer (tag->adapter, tag->metadata_block_size); /* clear the is-last flag, as the last metadata block will * be the vorbis comment block which we will build ourselves. */ gst_buffer_map (metadata_buffer, &map, GST_MAP_READWRITE); map.data[0] &= (~0x80); gst_buffer_unmap (metadata_buffer, &map); if (tag->state == GST_FLAC_TAG_STATE_WRITING_METADATA_BLOCK) { GST_DEBUG_OBJECT (tag, "pushing metadata block buffer"); ret = gst_pad_push (tag->srcpad, metadata_buffer); if (ret != GST_FLOW_OK) goto cleanup; } else { tag->vorbiscomment = metadata_buffer; } tag->metadata_block_size = 0; tag->state = GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK; } /* This state is mainly used to be able to stop as soon as we read * a vorbiscomment block from the flac file if we are in an only output * tags mode */ if (tag->state == GST_FLAC_TAG_STATE_METADATA_NEXT_BLOCK) { /* Check if in the previous iteration we read a vorbis comment metadata * block, and stop now if the user only wants to read tags */ if (tag->vorbiscomment != NULL) { guint8 id_data[4]; /* We found some tags, try to parse them and notify the other elements * that we encountered some tags */ GST_DEBUG_OBJECT (tag, "emitting vorbiscomment tags"); gst_buffer_extract (tag->vorbiscomment, 0, id_data, 4); tag->tags = gst_tag_list_from_vorbiscomment_buffer (tag->vorbiscomment, id_data, 4, NULL); if (tag->tags != NULL) { gst_pad_push_event (tag->srcpad, gst_event_new_tag (gst_tag_list_copy (tag->tags))); } gst_buffer_unref (tag->vorbiscomment); tag->vorbiscomment = NULL; } /* Skip to next state */ if (tag->metadata_last_block == FALSE) { tag->state = GST_FLAC_TAG_STATE_METADATA_BLOCKS; } else { tag->state = GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT; } } /* Creates a vorbis comment block from the metadata which was set * on the gstreamer element, and add it to the flac stream */ if (tag->state == GST_FLAC_TAG_STATE_ADD_VORBIS_COMMENT) { GstBuffer *buffer; const GstTagList *user_tags; GstTagList *merged_tags; /* merge the tag lists */ user_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (tag)); if (user_tags != NULL) { merged_tags = gst_tag_list_merge (user_tags, tag->tags, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (tag))); } else { merged_tags = gst_tag_list_copy (tag->tags); } if (merged_tags == NULL) { /* If we get a NULL list of tags, we must generate a padding block * which is marked as the last metadata block, otherwise we'll * end up with a corrupted flac file. */ GST_WARNING_OBJECT (tag, "No tags found"); buffer = gst_buffer_new_and_alloc (12); if (buffer == NULL) goto no_buffer; gst_buffer_map (buffer, &map, GST_MAP_WRITE); memset (map.data, 0, map.size); map.data[0] = 0x81; /* 0x80 = Last metadata block, * 0x01 = padding block */ gst_buffer_unmap (buffer, &map); } else { guchar header[4]; guint8 fbit[1]; memset (header, 0, sizeof (header)); header[0] = 0x84; /* 0x80 = Last metadata block, * 0x04 = vorbiscomment block */ buffer = gst_tag_list_to_vorbiscomment_buffer (merged_tags, header, sizeof (header), NULL); GST_DEBUG_OBJECT (tag, "Writing tags %" GST_PTR_FORMAT, merged_tags); gst_tag_list_free (merged_tags); if (buffer == NULL) goto no_comment; size = gst_buffer_get_size (buffer); if ((size < 4) || ((size - 4) > 0xFFFFFF)) goto comment_too_long; fbit[0] = 1; /* Get rid of the framing bit at the end of the vorbiscomment buffer * if it exists since libFLAC seems to lose sync because of this * bit in gstflacdec */ if (gst_buffer_memcmp (buffer, size - 1, fbit, 1) == 0) { buffer = gst_buffer_make_writable (buffer); gst_buffer_resize (buffer, 0, size - 1); } } /* The 4 byte metadata block header isn't accounted for in the total * size of the metadata block */ gst_buffer_map (buffer, &map, GST_MAP_WRITE); map.data[1] = (((map.size - 4) & 0xFF0000) >> 16); map.data[2] = (((map.size - 4) & 0x00FF00) >> 8); map.data[3] = ((map.size - 4) & 0x0000FF); gst_buffer_unmap (buffer, &map); GST_DEBUG_OBJECT (tag, "pushing %" G_GSIZE_FORMAT " byte vorbiscomment " "buffer", map.size); ret = gst_pad_push (tag->srcpad, buffer); if (ret != GST_FLOW_OK) { goto cleanup; } tag->state = GST_FLAC_TAG_STATE_AUDIO_DATA; }
static gboolean rc_plugin_tag_mp3_change(const gchar *src_path, const gchar *target_path, const GstTagList *tag_list) { GstElement *tag_filesrc = NULL; GstElement *tag_filesink = NULL; GstElement *tag_demux1 = NULL; GstElement *tag_demux2 = NULL; GstElement *tag_mux1 = NULL; GstElement *tag_mux2 = NULL; GstBus *bus = NULL; if(src_path==NULL) return FALSE; if(target_path==NULL) return FALSE; if(tag_list==NULL) return FALSE; if(g_access(src_path, W_OK)!=0) return FALSE; tag_mux1 = gst_element_factory_make("id3v2mux", "id3v2-tagger"); tag_mux2 = gst_element_factory_make("apev2mux", "apev2-tagger"); tag_demux1 = gst_element_factory_make("id3demux", "id3-demux"); tag_demux2 = gst_element_factory_make("apedemux", "ape-demux"); if(tag_mux1==NULL || tag_demux1==NULL || tag_demux1==NULL || tag_demux2==NULL) goto error_out; tag_filesrc = gst_element_factory_make("filesrc", "tag-filesrc"); if(tag_filesrc==NULL) goto error_out; tag_filesink = gst_element_factory_make("filesink", "tag-filesink"); if(tag_filesink==NULL) goto error_out; g_object_set(G_OBJECT(tag_filesrc), "location", src_path, NULL); g_object_set(G_OBJECT(tag_filesink), "location", target_path, NULL); tagger_pipeline = gst_pipeline_new("tagger-pipeline"); gst_tag_setter_merge_tags(GST_TAG_SETTER(tag_mux1), tag_list, GST_TAG_MERGE_REPLACE); gst_tag_setter_merge_tags(GST_TAG_SETTER(tag_mux2), tag_list, GST_TAG_MERGE_REPLACE); gst_bin_add_many(GST_BIN(tagger_pipeline), tag_filesrc, tag_demux2, tag_demux1, tag_mux1, tag_mux2, tag_filesink, NULL); g_signal_connect(tag_demux2, "pad-added", G_CALLBACK(rc_plugin_tag_writer_pad_added_cb), tag_demux1); g_signal_connect(tag_demux1, "pad-added", G_CALLBACK(rc_plugin_tag_writer_pad_added_cb), tag_mux1); if(!gst_element_link(tag_filesrc, tag_demux1)) goto error_out; if(!gst_element_link_many(tag_mux1, tag_mux2, tag_filesink, NULL)) goto error_out; bus = gst_pipeline_get_bus(GST_PIPELINE(tagger_pipeline)); gst_bus_add_watch(bus, (GstBusFunc)rc_plugin_tag_writer_bus_cb, NULL); gst_object_unref(bus); gst_element_set_state(tagger_pipeline, GST_STATE_NULL); gst_element_set_state(tagger_pipeline, GST_STATE_READY); if(gst_element_set_state(tagger_pipeline, GST_STATE_PLAYING) ==GST_STATE_CHANGE_FAILURE) goto error_out; return TRUE; error_out: if(tag_demux1!=NULL) gst_object_unref(tag_demux1); if(tag_mux1!=NULL) gst_object_unref(tag_mux1); if(tag_demux2!=NULL) gst_object_unref(tag_demux2); if(tag_mux2!=NULL) gst_object_unref(tag_mux2); if(tag_filesrc!=NULL) gst_object_unref(tag_filesrc); if(tag_filesink!=NULL) gst_object_unref(tag_filesink); if(tagger_pipeline!=NULL) gst_object_unref(tagger_pipeline); tagger_pipeline = NULL; return FALSE; }
static GstBuffer * gst_tag_lib_mux_render_tag (GstTagLibMux * mux) { GstTagLibMuxClass *klass; GstTagMergeMode merge_mode; GstTagSetter *tagsetter; GstBuffer *buffer; const GstTagList *tagsetter_tags; GstTagList *taglist; GstEvent *event; tagsetter = GST_TAG_SETTER (mux); tagsetter_tags = gst_tag_setter_get_tag_list (tagsetter); merge_mode = gst_tag_setter_get_tag_merge_mode (tagsetter); GST_LOG_OBJECT (mux, "merging tags, merge mode = %d", merge_mode); GST_LOG_OBJECT (mux, "event tags: %" GST_PTR_FORMAT, mux->event_tags); GST_LOG_OBJECT (mux, "set tags: %" GST_PTR_FORMAT, tagsetter_tags); taglist = gst_tag_list_merge (tagsetter_tags, mux->event_tags, merge_mode); GST_LOG_OBJECT (mux, "final tags: %" GST_PTR_FORMAT, taglist); klass = GST_TAG_LIB_MUX_CLASS (G_OBJECT_GET_CLASS (mux)); if (klass->render_tag == NULL) goto no_vfunc; buffer = klass->render_tag (mux, taglist); if (buffer == NULL) goto render_error; mux->tag_size = GST_BUFFER_SIZE (buffer); GST_LOG_OBJECT (mux, "tag size = %" G_GSIZE_FORMAT " bytes", mux->tag_size); /* Send newsegment event from byte position 0, so the tag really gets * written to the start of the file, independent of the upstream segment */ gst_pad_push_event (mux->srcpad, gst_event_new_new_segment (FALSE, 1.0, GST_FORMAT_BYTES, 0, -1, 0)); /* Send an event about the new tags to downstream elements */ /* gst_event_new_tag takes ownership of the list, so no need to unref it */ event = gst_event_new_tag (taglist); gst_pad_push_event (mux->srcpad, event); GST_BUFFER_OFFSET (buffer) = 0; return buffer; no_vfunc: { GST_ERROR_OBJECT (mux, "Subclass does not implement render_tag vfunc!"); gst_tag_list_free (taglist); return NULL; } render_error: { GST_ERROR_OBJECT (mux, "Failed to render tag"); gst_tag_list_free (taglist); return NULL; } }
static void gst_vp8_enc_set_stream_info (GstVPXEnc * enc, GstCaps * caps, GstVideoInfo * info) { GstStructure *s; GstVideoEncoder *video_encoder; GstBuffer *stream_hdr, *vorbiscomment; const GstTagList *iface_tags; GValue array = { 0, }; GValue value = { 0, }; guint8 *data = NULL; GstMapInfo map; video_encoder = GST_VIDEO_ENCODER (enc); s = gst_caps_get_structure (caps, 0); /* put buffers in a fixed list */ g_value_init (&array, GST_TYPE_ARRAY); g_value_init (&value, GST_TYPE_BUFFER); /* Create Ogg stream-info */ stream_hdr = gst_buffer_new_and_alloc (26); gst_buffer_map (stream_hdr, &map, GST_MAP_WRITE); data = map.data; GST_WRITE_UINT8 (data, 0x4F); GST_WRITE_UINT32_BE (data + 1, 0x56503830); /* "VP80" */ GST_WRITE_UINT8 (data + 5, 0x01); /* stream info header */ GST_WRITE_UINT8 (data + 6, 1); /* Major version 1 */ GST_WRITE_UINT8 (data + 7, 0); /* Minor version 0 */ GST_WRITE_UINT16_BE (data + 8, GST_VIDEO_INFO_WIDTH (info)); GST_WRITE_UINT16_BE (data + 10, GST_VIDEO_INFO_HEIGHT (info)); GST_WRITE_UINT24_BE (data + 12, GST_VIDEO_INFO_PAR_N (info)); GST_WRITE_UINT24_BE (data + 15, GST_VIDEO_INFO_PAR_D (info)); GST_WRITE_UINT32_BE (data + 18, GST_VIDEO_INFO_FPS_N (info)); GST_WRITE_UINT32_BE (data + 22, GST_VIDEO_INFO_FPS_D (info)); gst_buffer_unmap (stream_hdr, &map); GST_BUFFER_FLAG_SET (stream_hdr, GST_BUFFER_FLAG_HEADER); gst_value_set_buffer (&value, stream_hdr); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (stream_hdr); iface_tags = gst_tag_setter_get_tag_list (GST_TAG_SETTER (video_encoder)); if (iface_tags) { vorbiscomment = gst_tag_list_to_vorbiscomment_buffer (iface_tags, (const guint8 *) "OVP80\2 ", 7, "Encoded with GStreamer vp8enc " PACKAGE_VERSION); GST_BUFFER_FLAG_SET (vorbiscomment, GST_BUFFER_FLAG_HEADER); g_value_init (&value, GST_TYPE_BUFFER); gst_value_set_buffer (&value, vorbiscomment); gst_value_array_append_value (&array, &value); g_value_unset (&value); gst_buffer_unref (vorbiscomment); } gst_structure_set_value (s, "streamheader", &array); g_value_unset (&array); }
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; }
static gboolean gst_kate_enc_sink_event (GstPad * pad, GstObject * parent, GstEvent * event) { GstKateEnc *ke = GST_KATE_ENC (parent); const GstStructure *structure; gboolean ret; switch (GST_EVENT_TYPE (event)) { case GST_EVENT_CAPS: { GstCaps *caps; gst_event_parse_caps (event, &caps); ret = gst_kate_enc_setcaps (ke, caps); gst_event_unref (event); break; } case GST_EVENT_SEGMENT:{ GstSegment seg; GST_LOG_OBJECT (ke, "Got newsegment event"); gst_event_copy_segment (event, &seg); if (!ke->headers_sent) { if (ke->pending_segment) gst_event_unref (ke->pending_segment); ke->pending_segment = event; event = NULL; } if (ke->initialized) { GST_LOG_OBJECT (ke, "ensuring all headers are in"); if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) { GST_WARNING_OBJECT (ke, "Failed to flush headers"); } else { if (seg.format != GST_FORMAT_TIME || !GST_CLOCK_TIME_IS_VALID (seg.start)) { GST_WARNING_OBJECT (ke, "No time in newsegment event %p, format %d, timestamp %" G_GINT64_FORMAT, event, (int) seg.format, seg.start); /* to be safe, we'd need to generate a keepalive anyway, but we'd have to guess at the timestamp to use; a good guess would be the last known timestamp plus the keepalive time, but if we then get a packet with a timestamp less than this, it would fail to encode, which would be Bad. If we don't encode a keepalive, we run the risk of stalling the pipeline and hanging, which is Very Bad. Oh dear. We can't exit(-1), can we ? */ } else { float t = seg.start / (double) GST_SECOND; if (ke->delayed_spu && t - ke->delayed_start / (double) GST_SECOND >= ke->default_spu_duration) { if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke, seg.start) != GST_FLOW_OK)) { GST_WARNING_OBJECT (ke, "Failed to encode delayed packet"); /* continue with new segment handling anyway */ } } GST_LOG_OBJECT (ke, "ts %f, last %f (min %f)", t, ke->last_timestamp / (double) GST_SECOND, ke->keepalive_min_time); if (ke->keepalive_min_time > 0.0f && t - ke->last_timestamp / (double) GST_SECOND >= ke->keepalive_min_time) { /* we only generate a keepalive if there is no SPU waiting, as it would mean out of sequence start times - and granulepos */ if (!ke->delayed_spu) { gst_kate_enc_generate_keepalive (ke, seg.start); } } } } } if (event) ret = gst_pad_push_event (ke->srcpad, event); else ret = TRUE; break; } case GST_EVENT_CUSTOM_DOWNSTREAM: GST_LOG_OBJECT (ke, "Got custom downstream event"); /* adapted from the dvdsubdec element */ structure = gst_event_get_structure (event); if (structure != NULL && gst_structure_has_name (structure, "application/x-gst-dvd")) { if (ke->initialized) { GST_LOG_OBJECT (ke, "ensuring all headers are in"); if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) { GST_WARNING_OBJECT (ke, "Failed to flush headers"); } else { const gchar *event_name = gst_structure_get_string (structure, "event"); if (event_name) { if (!strcmp (event_name, "dvd-spu-clut-change")) { gchar name[16]; int idx; gboolean found; gint value; GST_INFO_OBJECT (ke, "New CLUT received"); for (idx = 0; idx < 16; ++idx) { g_snprintf (name, sizeof (name), "clut%02d", idx); found = gst_structure_get_int (structure, name, &value); if (found) { ke->spu_clut[idx] = value; } else { GST_WARNING_OBJECT (ke, "DVD CLUT event did not contain %s field", name); } } } else if (!strcmp (event_name, "dvd-lang-codes")) { /* we can't know which stream corresponds to us */ } } else { GST_WARNING_OBJECT (ke, "custom downstream event with no name"); } } } } ret = gst_pad_push_event (ke->srcpad, event); break; case GST_EVENT_TAG: GST_LOG_OBJECT (ke, "Got tag event"); if (ke->tags) { GstTagList *list; gst_event_parse_tag (event, &list); gst_tag_list_insert (ke->tags, list, gst_tag_setter_get_tag_merge_mode (GST_TAG_SETTER (ke))); } else { g_assert_not_reached (); } ret = gst_pad_event_default (pad, parent, event); break; case GST_EVENT_EOS: GST_INFO_OBJECT (ke, "Got EOS event"); if (ke->initialized) { GST_LOG_OBJECT (ke, "ensuring all headers are in"); if (gst_kate_enc_flush_headers (ke) != GST_FLOW_OK) { GST_WARNING_OBJECT (ke, "Failed to flush headers"); } else { kate_packet kp; int ret; GstClockTime delayed_end = ke->delayed_start + ke->default_spu_duration * GST_SECOND; if (G_UNLIKELY (gst_kate_enc_flush_waiting (ke, delayed_end) != GST_FLOW_OK)) { GST_WARNING_OBJECT (ke, "Failed to encode delayed packet"); /* continue with EOS handling anyway */ } ret = kate_encode_finish (&ke->k, -1, &kp); if (ret < 0) { GST_WARNING_OBJECT (ke, "Failed to encode EOS packet: %s", gst_kate_util_get_error_message (ret)); } else { kate_int64_t granpos = kate_encode_get_granule (&ke->k); GST_LOG_OBJECT (ke, "EOS packet encoded"); if (gst_kate_enc_push_and_free_kate_packet (ke, &kp, granpos, ke->latest_end_time, 0, FALSE)) { GST_WARNING_OBJECT (ke, "Failed to push EOS packet"); } } } } ret = gst_pad_event_default (pad, parent, event); break; default: GST_LOG_OBJECT (ke, "Got unhandled event"); ret = gst_pad_event_default (pad, parent, event); break; } return ret; }
static void test_taglib_id3mux_with_tags (GstTagList * tags, guint32 mask) { GstMessage *msg; GstTagList *tags_read = NULL; GstElement *pipeline, *id3mux, *id3demux, *fakesrc, *identity, *fakesink; GstBus *bus; guint64 offset; GstBuffer *outbuf = NULL; GstBuffer *tagbuf = NULL; GstStateChangeReturn state_result; pipeline = gst_pipeline_new ("pipeline"); g_assert (pipeline != NULL); fakesrc = gst_element_factory_make ("fakesrc", "fakesrc"); g_assert (fakesrc != NULL); id3mux = gst_element_factory_make ("id3v2mux", "id3v2mux"); g_assert (id3mux != NULL); identity = gst_element_factory_make ("identity", "identity"); g_assert (identity != NULL); id3demux = gst_element_factory_make ("id3demux", "id3demux"); g_assert (id3demux != NULL); fakesink = gst_element_factory_make ("fakesink", "fakesink"); g_assert (fakesink != NULL); /* set up sink */ outbuf = NULL; g_object_set (fakesink, "signal-handoffs", TRUE, NULL); g_signal_connect (fakesink, "handoff", G_CALLBACK (got_buffer), &outbuf); gst_bin_add (GST_BIN (pipeline), fakesrc); gst_bin_add (GST_BIN (pipeline), id3mux); gst_bin_add (GST_BIN (pipeline), identity); gst_bin_add (GST_BIN (pipeline), id3demux); gst_bin_add (GST_BIN (pipeline), fakesink); gst_tag_setter_merge_tags (GST_TAG_SETTER (id3mux), tags, GST_TAG_MERGE_APPEND); gst_element_link_many (fakesrc, id3mux, identity, id3demux, fakesink, NULL); /* set up source */ g_object_set (fakesrc, "signal-handoffs", TRUE, "can-activate-pull", FALSE, "filltype", 2, "sizetype", 2, "sizemax", MP3_FRAME_SIZE, "num-buffers", 16, NULL); offset = 0; g_signal_connect (fakesrc, "handoff", G_CALLBACK (fill_mp3_buffer), &offset); /* set up identity to catch tag buffer */ g_signal_connect (identity, "handoff", G_CALLBACK (identity_cb), &tagbuf); GST_LOG ("setting and getting state ..."); gst_element_set_state (pipeline, GST_STATE_PLAYING); state_result = gst_element_get_state (pipeline, NULL, NULL, -1); fail_unless (state_result == GST_STATE_CHANGE_SUCCESS, "Unexpected result from get_state(). Expected success, got %d", state_result); bus = gst_pipeline_get_bus (GST_PIPELINE (pipeline)); GST_LOG ("Waiting for tag ..."); msg = gst_bus_poll (bus, GST_MESSAGE_TAG | GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { GError *err; gchar *dbg; gst_message_parse_error (msg, &err, &dbg); g_printerr ("ERROR from element %s: %s\n%s\n", GST_OBJECT_NAME (msg->src), err->message, GST_STR_NULL (dbg)); g_error_free (err); g_free (dbg); } else if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_EOS) { g_printerr ("EOS message, but were waiting for TAGS!\n"); } fail_unless (msg->type == GST_MESSAGE_TAG); gst_message_parse_tag (msg, &tags_read); gst_message_unref (msg); GST_LOG ("Got tags: %" GST_PTR_FORMAT, tags_read); test_taglib_id3mux_check_tags (tags_read, mask); gst_tag_list_unref (tags_read); fail_unless (tagbuf != NULL); test_taglib_id3mux_check_tag_buffer (tagbuf, mask); gst_buffer_unref (tagbuf); GST_LOG ("Waiting for EOS ..."); msg = gst_bus_poll (bus, GST_MESSAGE_EOS | GST_MESSAGE_ERROR, -1); if (GST_MESSAGE_TYPE (msg) == GST_MESSAGE_ERROR) { GError *err; gchar *dbg; gst_message_parse_error (msg, &err, &dbg); g_printerr ("ERROR from element %s: %s\n%s\n", GST_OBJECT_NAME (msg->src), err->message, GST_STR_NULL (dbg)); g_error_free (err); g_free (dbg); } fail_unless (msg->type == GST_MESSAGE_EOS); gst_message_unref (msg); gst_object_unref (bus); GST_LOG ("Got EOS, shutting down ..."); gst_element_set_state (pipeline, GST_STATE_NULL); gst_object_unref (pipeline); test_taglib_id3mux_check_output_buffer (outbuf); gst_buffer_unref (outbuf); GST_LOG ("Done"); }
static gboolean gst_jif_mux_mangle_markers (GstJifMux * self) { gboolean modified = FALSE; GstTagList *tags = NULL; gboolean cleanup_tags; GstJifMuxMarker *m; GList *node, *file_hdr = NULL, *frame_hdr = NULL, *scan_hdr = NULL; GList *app0_jfif = NULL, *app1_exif = NULL, *app1_xmp = NULL, *com = NULL; GstBuffer *xmp_data; gchar *str = NULL; gint colorspace = COLORSPACE_UNKNOWN; /* update the APP markers * - put any JFIF APP0 first * - the Exif APP1 next, * - the XMP APP1 next, * - the PSIR APP13 next, * - followed by all other marker segments */ /* find some reference points where we insert before/after */ file_hdr = self->priv->markers; for (node = self->priv->markers; node; node = g_list_next (node)) { m = (GstJifMuxMarker *) node->data; switch (m->marker) { case APP0: if (m->size > 5 && !memcmp (m->data, "JFIF\0", 5)) { GST_DEBUG_OBJECT (self, "found APP0 JFIF"); colorspace |= COLORSPACE_GRAYSCALE | COLORSPACE_YUV; if (!app0_jfif) app0_jfif = node; } break; case APP1: if (m->size > 6 && (!memcmp (m->data, "EXIF\0\0", 6) || !memcmp (m->data, "Exif\0\0", 6))) { GST_DEBUG_OBJECT (self, "found APP1 EXIF"); if (!app1_exif) app1_exif = node; } else if (m->size > 29 && !memcmp (m->data, "http://ns.adobe.com/xap/1.0/\0", 29)) { GST_INFO_OBJECT (self, "found APP1 XMP, will be replaced"); if (!app1_xmp) app1_xmp = node; } break; case APP14: /* check if this contains RGB */ /* * This marker should have: * - 'Adobe\0' * - 2 bytes DCTEncodeVersion * - 2 bytes flags0 * - 2 bytes flags1 * - 1 byte ColorTransform * - 0 means unknown (RGB or CMYK) * - 1 YCbCr * - 2 YCCK */ if ((m->size >= 14) && (strncmp ((gchar *) m->data, "Adobe\0", 6) == 0)) { switch (m->data[11]) { case 0: colorspace |= COLORSPACE_RGB | COLORSPACE_CMYK; break; case 1: colorspace |= COLORSPACE_YUV; break; case 2: colorspace |= COLORSPACE_YCCK; break; default: break; } } break; case COM: GST_INFO_OBJECT (self, "found COM, will be replaced"); if (!com) com = node; break; case DQT: case SOF0: case SOF1: case SOF2: case SOF3: case SOF5: case SOF6: case SOF7: case SOF9: case SOF10: case SOF11: case SOF13: case SOF14: case SOF15: if (!frame_hdr) frame_hdr = node; break; case DAC: case DHT: case DRI: case SOS: if (!scan_hdr) scan_hdr = node; break; } } /* if we want combined or JFIF */ /* check if we don't have JFIF APP0 */ if (!app0_jfif && (colorspace & (COLORSPACE_GRAYSCALE | COLORSPACE_YUV))) { /* build jfif header */ static const struct { gchar id[5]; guint8 ver[2]; guint8 du; guint8 xd[2], yd[2]; guint8 tw, th; } jfif_data = { "JFIF", { 1, 2 }, 0, { 0, 1 }, /* FIXME: check pixel-aspect from caps */ { 0, 1 }, 0, 0 }; m = gst_jif_mux_new_marker (APP0, sizeof (jfif_data), (const guint8 *) &jfif_data, FALSE); /* insert into self->markers list */ self->priv->markers = g_list_insert (self->priv->markers, m, 1); app0_jfif = g_list_nth (self->priv->markers, 1); } /* else */ /* remove JFIF if exists */ /* Existing exif tags will be removed and our own will be added */ if (!tags) { tags = (GstTagList *) gst_tag_setter_get_tag_list (GST_TAG_SETTER (self)); cleanup_tags = FALSE; } if (!tags) { tags = gst_tag_list_new (); cleanup_tags = TRUE; } GST_DEBUG_OBJECT (self, "Tags to be serialized %" GST_PTR_FORMAT, tags); /* FIXME: not happy with those * - else where we would use VIDEO_CODEC = "Jpeg" gst_tag_list_add (tags, GST_TAG_MERGE_REPLACE, GST_TAG_VIDEO_CODEC, "image/jpeg", NULL); */ /* Add EXIF */ { GstBuffer *exif_data; guint8 *data; GstJifMuxMarker *m; GList *pos; /* insert into self->markers list */ exif_data = gst_tag_list_to_exif_buffer_with_tiff_header (tags); if (exif_data && GST_BUFFER_SIZE (exif_data) + 8 >= G_GUINT64_CONSTANT (65536)) { GST_WARNING_OBJECT (self, "Exif tags data size exceed maximum size"); gst_buffer_unref (exif_data); exif_data = NULL; } if (exif_data) { data = g_malloc0 (GST_BUFFER_SIZE (exif_data) + 6); memcpy (data, "Exif", 4); memcpy (data + 6, GST_BUFFER_DATA (exif_data), GST_BUFFER_SIZE (exif_data)); m = gst_jif_mux_new_marker (APP1, GST_BUFFER_SIZE (exif_data) + 6, data, TRUE); gst_buffer_unref (exif_data); if (app1_exif) { gst_jif_mux_marker_free ((GstJifMuxMarker *) app1_exif->data); app1_exif->data = m; } else { pos = file_hdr; if (app0_jfif) pos = app0_jfif; pos = g_list_next (pos); self->priv->markers = g_list_insert_before (self->priv->markers, pos, m); if (pos) { app1_exif = g_list_previous (pos); } else { app1_exif = g_list_last (self->priv->markers); } } modified = TRUE; } } /* add xmp */ xmp_data = gst_tag_xmp_writer_tag_list_to_xmp_buffer (GST_TAG_XMP_WRITER (self), tags, FALSE); if (xmp_data) { guint8 *data, *xmp = GST_BUFFER_DATA (xmp_data); guint size = GST_BUFFER_SIZE (xmp_data); GList *pos; data = g_malloc (size + 29); memcpy (data, "http://ns.adobe.com/xap/1.0/\0", 29); memcpy (&data[29], xmp, size); m = gst_jif_mux_new_marker (APP1, size + 29, data, TRUE); /* * Replace the old xmp marker and not add a new one. * There shouldn't be a xmp packet in the input, but it is better * to be safe than add another one and end up with 2 packets. */ if (app1_xmp) { gst_jif_mux_marker_free ((GstJifMuxMarker *) app1_xmp->data); app1_xmp->data = m; } else { pos = file_hdr; if (app1_exif) pos = app1_exif; else if (app0_jfif) pos = app0_jfif; pos = g_list_next (pos); self->priv->markers = g_list_insert_before (self->priv->markers, pos, m); } gst_buffer_unref (xmp_data); modified = TRUE; } /* add jpeg comment from any of those */ (void) (gst_tag_list_get_string (tags, GST_TAG_COMMENT, &str) || gst_tag_list_get_string (tags, GST_TAG_DESCRIPTION, &str) || gst_tag_list_get_string (tags, GST_TAG_TITLE, &str)); if (str) { GST_DEBUG_OBJECT (self, "set COM marker to '%s'", str); /* insert new marker into self->markers list */ m = gst_jif_mux_new_marker (COM, strlen (str) + 1, (const guint8 *) str, TRUE); /* FIXME: if we have one already, replace */ /* this should go before SOS, maybe at the end of file-header */ self->priv->markers = g_list_insert_before (self->priv->markers, frame_hdr, m); modified = TRUE; } if (tags && cleanup_tags) gst_tag_list_free (tags); return modified; }
static void test_tags (const gchar * tag_str) { GstElement *pipeline; GstBus *bus; GMainLoop *loop; GstTagList *sent_tags; gint i, j, n_recv, n_sent; const gchar *name_sent, *name_recv; const GValue *value_sent, *value_recv; gboolean found, ok; gint comparison; GstElement *videotestsrc, *jpegenc, *metadatamux, *metadatademux, *fakesink; GstTagSetter *setter; GST_DEBUG ("testing tags : %s", tag_str); if (received_tags) { gst_tag_list_free (received_tags); received_tags = NULL; } pipeline = gst_pipeline_new ("pipeline"); fail_unless (pipeline != NULL); videotestsrc = gst_element_factory_make ("videotestsrc", "src"); fail_unless (videotestsrc != NULL); g_object_set (G_OBJECT (videotestsrc), "num-buffers", 1, NULL); jpegenc = gst_element_factory_make ("jpegenc", "enc"); if (jpegenc == NULL) { g_print ("Cannot test - jpegenc not available\n"); return; } metadatamux = gst_element_factory_make ("metadatamux", "mux"); g_object_set (G_OBJECT (metadatamux), "exif", TRUE, NULL); fail_unless (metadatamux != NULL); metadatademux = gst_element_factory_make ("metadatademux", "demux"); fail_unless (metadatademux != NULL); fakesink = gst_element_factory_make ("fakesink", "sink"); fail_unless (fakesink != NULL); gst_bin_add_many (GST_BIN (pipeline), videotestsrc, jpegenc, metadatamux, metadatademux, fakesink, NULL); ok = gst_element_link_many (videotestsrc, jpegenc, metadatamux, metadatademux, fakesink, NULL); fail_unless (ok == TRUE); loop = g_main_loop_new (NULL, TRUE); fail_unless (loop != NULL); bus = gst_element_get_bus (pipeline); fail_unless (bus != NULL); gst_bus_add_watch (bus, bus_handler, loop); gst_object_unref (bus); gst_element_set_state (pipeline, GST_STATE_READY); setter = GST_TAG_SETTER (metadatamux); fail_unless (setter != NULL); sent_tags = gst_structure_from_string (tag_str, NULL); fail_unless (sent_tags != NULL); gst_tag_setter_merge_tags (setter, sent_tags, GST_TAG_MERGE_REPLACE); gst_element_set_state (pipeline, GST_STATE_PLAYING); g_main_loop_run (loop); GST_DEBUG ("mainloop done : %p", received_tags); /* verify tags */ fail_unless (received_tags != NULL); n_recv = gst_structure_n_fields (received_tags); n_sent = gst_structure_n_fields (sent_tags); /* we also get e.g. an exif binary block */ fail_unless (n_recv >= n_sent); /* FIXME: compare taglits values */ for (i = 0; i < n_sent; i++) { name_sent = gst_structure_nth_field_name (sent_tags, i); value_sent = gst_structure_get_value (sent_tags, name_sent); found = FALSE; for (j = 0; j < n_recv; j++) { name_recv = gst_structure_nth_field_name (received_tags, j); if (!strcmp (name_sent, name_recv)) { value_recv = gst_structure_get_value (received_tags, name_recv); comparison = gst_value_compare (value_sent, value_recv); if (comparison != GST_VALUE_EQUAL) { gchar *vs = g_strdup_value_contents (value_sent); gchar *vr = g_strdup_value_contents (value_recv); GST_DEBUG ("sent = %s:'%s', recv = %s:'%s'", G_VALUE_TYPE_NAME (value_sent), vs, G_VALUE_TYPE_NAME (value_recv), vr); g_free (vs); g_free (vr); } fail_unless (comparison == GST_VALUE_EQUAL, "tag item %s has been received with different type or value", name_sent); found = TRUE; break; } } fail_unless (found, "tag item %s is lost", name_sent); } gst_tag_list_free (received_tags); received_tags = NULL; gst_tag_list_free (sent_tags); gst_element_set_state (pipeline, GST_STATE_NULL); g_main_loop_unref (loop); g_object_unref (pipeline); }
gboolean ly_mdh_push(LyMdhMetadata *md) { if(!md||!g_str_has_prefix(md->uri, "file://")) return FALSE; if(!g_mutex_trylock(ly_mdh_put_mutex)) { ly_log_put_with_flag(G_LOG_LEVEL_WARNING, _("An old task is running, Tag Failed!")); return FALSE; } /* * BUILD */ GstElement *filesrc=NULL; GstElement *demux=NULL; GstElement *mux=NULL; GstElement *parse=NULL; GstElement *filesink=NULL; GstElement *tagger=NULL; GstBus *bus=NULL; const gchar *codec=NULL; ly_mdh_put_pipeline=gst_pipeline_new("pipeline"); filesrc=gst_element_factory_make("filesrc","filesrc"); filesink=gst_element_factory_make("filesink","filesink"); if(!ly_mdh_put_pipeline||!filesrc||!filesink) { if(ly_mdh_put_pipeline); gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } //MP3 if(strstr(md->codec,"MP3")!=NULL) { demux=gst_element_factory_make("id3demux","demux"); tagger=gst_element_factory_make("id3v2mux","tagger"); codec = "LAME"; if(!demux||!tagger) { gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } } //OGG else if(strstr(md->codec,"Vorbis")!=NULL) { tagger = gst_element_factory_make("vorbistag", "tagger"); demux=gst_element_factory_make("oggdemux","demux"); mux=gst_element_factory_make("oggmux","mux"); parse = gst_element_factory_make("vorbisparse", "parse"); codec = "Vorbis"; if(!demux||!mux||!tagger||!parse) { gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } } //FLAC else if(strstr(md->codec,"FLAC")!=NULL) { tagger = gst_element_factory_make("flactag", "tagger"); codec="FLAC"; if(!tagger) { gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } } //APE else if(strstr(md->codec,"Monkey's Audio")!=NULL) { demux=gst_element_factory_make("apedemux","demux"); tagger=gst_element_factory_make("apev2mux","tagger"); codec="LAME"; if(!demux||!tagger) { gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } } else { gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } /* * SET */ gchar location_i[1024]=""; gchar location_o[1024]=""; g_snprintf(location_i, sizeof(location_i), "%s", md->uri+7); g_snprintf(location_o, sizeof(location_o), "%s%s-%s.audio", LY_GLA_TEMPDIR, md->artist, md->title); g_object_set(G_OBJECT(filesrc), "location", location_i, NULL); g_object_set(G_OBJECT(filesink), "location", location_o, NULL); gst_tag_setter_add_tags(GST_TAG_SETTER(tagger), GST_TAG_MERGE_REPLACE_ALL, GST_TAG_TITLE, md->title, GST_TAG_ARTIST, md->artist, GST_TAG_ALBUM, md->album, GST_TAG_GENRE, md->genre, GST_TAG_TRACK_NUMBER, md->track, GST_TAG_ENCODER, "Linnya", GST_TAG_ENCODER_VERSION, 1, GST_TAG_CODEC,codec, NULL); /* *LINK */ //MP3 if(strstr(md->codec,"MP3")!=NULL) { gst_bin_add_many(GST_BIN(ly_mdh_put_pipeline), filesrc, demux,tagger,filesink, NULL); g_signal_connect(demux, "pad-added",G_CALLBACK(ly_mdh_push_add_id3_pad_cb), tagger); gst_element_link(filesrc, demux); gst_element_link(tagger, filesink); } //OGG else if(strstr(md->codec,"Vorbis")!=NULL) { gst_bin_add_many(GST_BIN(ly_mdh_put_pipeline), filesrc, demux, tagger, parse, mux, filesink, NULL); g_signal_connect(demux, "pad-added",G_CALLBACK(ly_mdh_push_add_ogg_pad_cb), tagger); gst_element_link(filesrc, demux); gst_element_link_many(tagger, parse, mux, filesink,NULL); } //FLAC else if(strstr(md->codec,"FLAC")!=NULL) { gst_bin_add_many(GST_BIN(ly_mdh_put_pipeline), filesrc, tagger, filesink, NULL); gst_element_link_many(filesrc, tagger, filesink, NULL); } //APE else if(strstr(md->codec,"Monkey's Audio")!=NULL) { gst_bin_add_many(GST_BIN(ly_mdh_put_pipeline), filesrc, demux,tagger,filesink, NULL); g_signal_connect(demux, "pad-added",G_CALLBACK(ly_mdh_push_add_id3_pad_cb), tagger); gst_element_link(filesrc, demux); gst_element_link(tagger, filesink); } else { gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } bus = gst_pipeline_get_bus(GST_PIPELINE(ly_mdh_put_pipeline)); gst_bus_add_watch(bus, (GstBusFunc)ly_mdh_push_handler_cb, g_memdup(md,sizeof(LyMdhMetadata))); gst_object_unref(bus); gst_element_set_state(ly_mdh_put_pipeline, GST_STATE_NULL); gst_element_set_state(ly_mdh_put_pipeline, GST_STATE_READY); if(gst_element_set_state(ly_mdh_put_pipeline, GST_STATE_PLAYING)==GST_STATE_CHANGE_FAILURE) { gst_element_set_state(ly_mdh_put_pipeline, GST_STATE_NULL); gst_object_unref(ly_mdh_put_pipeline); g_mutex_unlock(ly_mdh_put_mutex); return FALSE; } return TRUE; }