Re: Testing Gstmm Element seeking



José Alburquerque wrote:
Hi. I've been working on testing element seeking using gstmm and I've made a few changes (attached) to gstmm. I also developed a mini gtkmm application (included source files) that tests seeking and other things the original ogg player uses, but I can't quite figure out how to include it in the examples directory. Is it possible although it uses gtkmm?

-Jose
If it is, I kind of noticed that the formatting of some files was a little off so I reformatted and am re-attaching the example files.

-Jose
/* -*- Mode: C; indent-tabs-mode: t; c-basic-offset: 4; tab-width: 4 -*- */
/*
 * main.cc
 * Copyright (C) The gstmm development team 2007 <gtkmm-list gnome org>
 * 
 * main.cc is free software: you can redistribute it and/or modify it
 * under the terms of the GNU General Public License as published by the
 * Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 * 
 * main.cc is distributed in the hope that it will be useful, but
 * WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
 * See the GNU General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License along
 * with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

#include <gtkmm/main.h>
#include <gstmm/init.h>
#include <gstmm/element.h>
#include <gstmm/pipeline.h>
#include <iostream>
#include "PlayerWindow.h"

Glib::RefPtr<Gst::Pipeline> pipeline;
Glib::RefPtr<Gst::Element> decoder;

void on_parser_pad_added(const Glib::RefPtr<Gst::Pad>& newPad)
{
    // We can now link this pad with the audio decoder
    Glib::RefPtr<Gst::Pad> sinkPad = decoder->get_pad("sink");
    newPad->link(sinkPad);
}

int
main (int argc, char *argv[])
{
    Gtk::Main kit(argc, argv);
    Gst::init(argc, argv);

    // Create the pipeline
    pipeline = Gst::Pipeline::create("audio-player");

    // Create the elements
    // Reads file from disk
    Glib::RefPtr<Gst::Element> source = Gst::Element::create("filesrc", "file-source");

    // Parses the ogg streams into elementary streams (note that an ogg file may contain a video stream too)
    Glib::RefPtr<Gst::Element> parser = Gst::Element::create("oggdemux", "ogg-parser");

    // Decodes a vorbis stream
    decoder = Gst::Element::create("vorbisdec", "vorbis-decoder");

    // Converts audio() to a format which can be used by the next element
    Glib::RefPtr<Gst::Element> conv = Gst::Element::create("audioconvert", "converter");

    // Outputs sound to an ALSA audio device
    Glib::RefPtr<Gst::Element> sink = Gst::Element::create("alsasink", "alsa-output");

    if (!pipeline || !source || !parser || !decoder || !conv || !sink)
    {
        std::cerr << "One element could not be created" << std::endl;
        return -1;
    }

    // Put all elements in a pipeline:
    try
    {
        pipeline->add(source)->add(parser)->add(decoder)->add(conv)->add(sink);
    }
    catch(const Glib::Error& ex)
    {
        std::cerr << "Error while adding elements to the pipeline: " << ex.what() << std::endl;
        return -1;
    }

    // Link together:
    source->link(parser);

    // We cannot link the parser and decoder yet, 
    // because the parser uses dynamic pads. For that,
    // we set a pad-added signal handler:
    parser->signal_pad_added().connect( sigc::ptr_fun(&on_parser_pad_added) );

    decoder->link(conv)->link(sink);

    PlayerWindow mainWindow(source, pipeline);

    kit.run(mainWindow);

    // Clean up nicely:
    pipeline->set_state(Gst::STATE_NULL);

    return 0;
}
/***************************************************************************
 *            PlayerWindow.h
 *
 *  Tue Dec 18 18:48:33 2007
 *  Copyright  2007  The gstmm development team
 *  <gtkmm-list gnome org>
 ****************************************************************************/

/*
 * This program is free software; you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation; either version 2 of the License, or
 * (at your option) any later version.
 * 
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU Library General Public License for more details.
 * 
 * You should have received a copy of the GNU General Public License
 * along with this program; if not, write to the Free Software
 * Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA
 */
 
#ifndef _PLAYERWINDOW_H
#define _PLAYERWINDOW_H

#include <gtkmm/window.h>
#include <gtkmm/box.h>
#include <gtkmm/buttonbox.h>
#include <gtkmm/label.h>
#include <gtkmm/button.h>
#include <gtkmm/scale.h>
#include <gstmm/element.h>
#include <gstmm/pipeline.h>

class PlayerWindow : public Gtk::Window
{
public:
    PlayerWindow(Glib::RefPtr<Gst::Element> sourceElement,
                 Glib::RefPtr<Gst::Pipeline> mainPipeline);
protected:
    Gtk::VBox vBox;
    Gtk::HButtonBox buttonBox;
    Gtk::Label progressLabel;
    Gtk::HScale progressScale;
    Gtk::Button playButton;
    Gtk::Button pauseButton;
    Gtk::Button stopButton;
    Gtk::Button rewindButton;
    Gtk::Button forwardButton;
    Gtk::Button openButton;
protected:
    virtual bool on_bus_message(const Glib::RefPtr<Gst::Bus>& bus,
                            const Glib::RefPtr<Gst::Message>& message);
    virtual void on_play(void);
    virtual void on_pause(void);
    virtual void on_stop(void);
    virtual bool on_scale_value_changed(Gtk::ScrollType type, double value);
    virtual void on_rewind(void);
    virtual void on_forward(void);
    virtual void on_open(void);
protected:
    bool update_stream_progress(void);
    void display_label_progress(gint64 pos, gint64 len);
private:
    Glib::RefPtr<Gst::Element> sourceElement;
    Glib::RefPtr<Gst::Pipeline> mainPipeline;
    gint64 duration;
    sigc::connection progressConnection;
};

#endif /* _PLAYERWINDOW_H */

 
//           PlayerWindow.cc
//  Tue Dec 18 18:47:47 2007
//  Copyright  2007  The gstmm development team
//  <gtkmm-list gnome org>

// This program is free software; you can redistribute it and/or modify
// it under the terms of the GNU General Public License as published by
// the Free Software Foundation; either version 2 of the License, or
// (at your option) any later version.
// 
// This program is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
// GNU Library General Public License for more details.
// 
// You should have received a copy of the GNU General Public License
// along with this program; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor Boston, MA 02110-1301,  USA

#include <gtkmm/stock.h>
#include <gtkmm/filechooserdialog.h>
#include <gstmm/clock.h>
#include <iostream>
#include <sstream>
#include <iomanip>
#include "PlayerWindow.h"

PlayerWindow::PlayerWindow(Glib::RefPtr<Gst::Element> sourceElement,
                                Glib::RefPtr<Gst::Pipeline> mainPipeline) :
vBox(false, 5),
progressLabel("000:00:00.000000000 / 000:00:00.000000000"),
playButton(Gtk::Stock::MEDIA_PLAY),
pauseButton(Gtk::Stock::MEDIA_PAUSE),
stopButton(Gtk::Stock::MEDIA_STOP),
rewindButton(Gtk::Stock::MEDIA_REWIND),
forwardButton(Gtk::Stock::MEDIA_FORWARD),
openButton(Gtk::Stock::OPEN)
{
    set_title("Gstmm Ogg Player Example");

    add(vBox);
    vBox.pack_start(progressLabel);
    vBox.pack_start(progressScale);
    vBox.pack_start(buttonBox);

    progressLabel.set_alignment(Gtk::ALIGN_CENTER);

    progressScale.set_range(0, 1);
    progressScale.set_draw_value(false);
    progressScale.signal_change_value().connect(
                sigc::mem_fun(*this, &PlayerWindow::on_scale_value_changed));

    buttonBox.pack_start(playButton);
    buttonBox.pack_start(pauseButton);
    buttonBox.pack_start(stopButton);
    buttonBox.pack_start(rewindButton);
    buttonBox.pack_start(forwardButton);
    buttonBox.pack_start(openButton);

    playButton.signal_clicked().connect(sigc::mem_fun(*this,
                                          &PlayerWindow::on_play));
    pauseButton.signal_clicked().connect(sigc::mem_fun(*this,
                                          &PlayerWindow::on_pause));
    stopButton.signal_clicked().connect(sigc::mem_fun(*this,
                                          &PlayerWindow::on_stop));
    rewindButton.signal_clicked().connect(sigc::mem_fun(*this,
                                          &PlayerWindow::on_rewind));
    forwardButton.signal_clicked().connect(sigc::mem_fun(*this,
                                          &PlayerWindow::on_forward));
    openButton.signal_clicked().connect(sigc::mem_fun(*this,
                                          &PlayerWindow::on_open));

	// get the bus from the pipeline
	Glib::RefPtr<Gst::Bus> bus = mainPipeline->get_bus();

	// Add a bus watch to receive messages from pipeline's bus
	bus->add_watch( sigc::mem_fun( *this, &PlayerWindow::on_bus_message) );

    progressScale.set_sensitive(false);
    playButton.set_sensitive(false);
    pauseButton.set_sensitive(false);
    stopButton.set_sensitive(false);
    rewindButton.set_sensitive(false);
    forwardButton.set_sensitive(false);

    this->sourceElement = sourceElement;
    this->mainPipeline = mainPipeline;

    show_all_children();
    pauseButton.hide();
}

// This function is used to receive asynchronous messages from mainPipeline's bus
bool PlayerWindow::on_bus_message(const Glib::RefPtr<Gst::Bus>& bus,
					const Glib::RefPtr<Gst::Message>& message)
{
    switch (message->get_message_type())
    {
        case Gst::MESSAGE_EOS:
            on_stop();
            break;
        case Gst::MESSAGE_ERROR:
        {
            Glib::RefPtr<Gst::MessageError> msgError =
                Glib::RefPtr<Gst::MessageError>::cast_dynamic(message);
            if(msgError)
            {
                Glib::Error err;
                std::string debug; //TODO: Maybe this should be an optional parameter.
                msgError->parse(err, debug);
                std::cerr << "Error: " << err.what() << std::endl;
            }
            else
                std::cerr << "Error." << std::endl;

            on_stop();
            break;
        }
        default:
        {
        //std::cout << "debug: on_bus_message: unhandled message=" << G_OBJECT_TYPE_NAME(message->gobj()) << std::endl;
        }
        break;
    }

    return true;
}

void PlayerWindow::on_play(void)
{
    progressScale.set_sensitive(true);
    playButton.set_sensitive(false);
    pauseButton.set_sensitive(true);
    stopButton.set_sensitive(true);
    rewindButton.set_sensitive(true);
    forwardButton.set_sensitive(true);
    openButton.set_sensitive(false);

    playButton.hide();
    pauseButton.show();

	// Call update_stream_progress function at a 200ms
	// interval to regularly update the position of the stream
	progressConnection = Glib::signal_timeout().connect(sigc::mem_fun(*this,
                             &PlayerWindow::update_stream_progress), 200);

    // set Gstmm pipeline to play mode
	mainPipeline->set_state(Gst::STATE_PLAYING);
}
 
void PlayerWindow::on_pause(void)
{
    playButton.set_sensitive(true);
    pauseButton.set_sensitive(false);

    pauseButton.hide();
    playButton.show();

    // disconnect progress callback
    progressConnection.disconnect();
    
    // set Gstmm pipeline to pause mode
	mainPipeline->set_state(Gst::STATE_PAUSED);
}
 
void PlayerWindow::on_stop(void)
{
    progressScale.set_sensitive(false);
    playButton.set_sensitive(true);
    pauseButton.set_sensitive(false);
    stopButton.set_sensitive(false);
    rewindButton.set_sensitive(false);
    forwardButton.set_sensitive(false);
    openButton.set_sensitive(true);

    pauseButton.hide();
    playButton.show();

    // disconnect progress callback
    progressConnection.disconnect();

    // set Gstmm pipeline to inactive mode
	mainPipeline->set_state(Gst::STATE_NULL);
    display_label_progress(0, duration);
    progressScale.set_value(0);
}

bool PlayerWindow::on_scale_value_changed(Gtk::ScrollType type, double value)
{
    gint64 newPos = gint64(value * duration);

    if (mainPipeline->seek(Gst::FORMAT_TIME, Gst::SEEK_FLAG_FLUSH, newPos))
        display_label_progress(newPos, duration);
    else
        std::cerr << "Could not seek!" << std::endl;
}

void PlayerWindow::on_rewind(void)
{
    static const gint64 skipAmount = GST_SECOND * 2;

    gint64 pos;
    Gst::Format fmt = Gst::FORMAT_TIME;

    if (mainPipeline->query_position(fmt, pos))
    {
        gint64 newPos = (pos > skipAmount) ? (pos - skipAmount) : 0;

        if (mainPipeline->seek(Gst::FORMAT_TIME, Gst::SEEK_FLAG_FLUSH, newPos)) {
            display_label_progress(newPos, duration);
            progressScale.set_value(double(newPos) / duration);
        }
        else
            std::cerr << "Could not seek!" << std::endl;
    }
}

void PlayerWindow::on_forward(void)
{
    static const gint64 skipAmount = GST_SECOND * 3;

    gint64 pos;
    Gst::Format fmt = Gst::FORMAT_TIME;

    if (mainPipeline->query_position(fmt, pos))
    {
        gint64 newPos = ((pos + skipAmount) < duration) ? (pos + skipAmount) :
            duration;

        if (mainPipeline->seek(Gst::FORMAT_TIME, Gst::SEEK_FLAG_FLUSH, newPos))
        {
            progressScale.set_value(double(newPos) / duration);
            display_label_progress(newPos, duration);
        }
        else
            std::cerr << "Could not seek!" << std::endl;
    }
}

void PlayerWindow::on_open(void)
{
    static Glib::ustring workingDir = Glib::get_home_dir();
    
    Gtk::FileChooserDialog chooser(*this,
                        "Select Ogg file", Gtk::FILE_CHOOSER_ACTION_OPEN);

    Gtk::FileFilter filter;
    filter.add_mime_type("application/ogg");
    filter.set_name("Ogg files");
    
    chooser.set_filter(filter);

    chooser.add_button(Gtk::Stock::CANCEL, Gtk::RESPONSE_CANCEL);
    chooser.add_button(Gtk::Stock::OK, Gtk::RESPONSE_OK);
    
    chooser.set_current_folder(workingDir);
    
    int response = chooser.run();
    
    if (response == Gtk::RESPONSE_OK) {
        workingDir = chooser.get_current_folder();

        // Set filename property on the file source. Also add a message handler:
        sourceElement->set_property("location", chooser.get_filename());
        set_title(Glib::filename_display_basename(chooser.get_filename()));

        playButton.set_sensitive(true);
        display_label_progress(0, 0);
    }
}

bool PlayerWindow::update_stream_progress(void)
{
    Gst::Format fmt = Gst::FORMAT_TIME;
    gint64 pos = 0;

    if (mainPipeline->query_position(fmt, pos)
    && mainPipeline->query_duration(fmt, duration)) {
        progressScale.set_value(double(pos) / duration);
        display_label_progress(pos, duration);
    }

   return true;
}

void PlayerWindow::display_label_progress(gint64 pos, gint64 len) {
    std::ostringstream locationStream (std::ostringstream::out);
    std::ostringstream durationStream (std::ostringstream::out);

    locationStream << std::right << std::setfill('0') << 
        std::setw(3) << Gst::get_hours(pos) << ":" <<
        std::setw(2) << Gst::get_minutes(pos) << ":" <<
        std::setw(2) << Gst::get_seconds(pos) << "." <<
        std::setw(9) << std::left << Gst::get_fractional_seconds(pos);

    durationStream << std::right << std::setfill('0') <<
        std::setw(3) << Gst::get_hours(len) << ":" <<
        std::setw(2) << Gst::get_minutes(len) << ":" <<
        std::setw(2) << Gst::get_seconds(len) << "." <<
        std::setw(9) << std::left << Gst::get_fractional_seconds(len);

    progressLabel.set_text(locationStream.str() + " / " + durationStream.str());
}


[Date Prev][Date Next]   [Thread Prev][Thread Next]   [Thread Index] [Date Index] [Author Index]