Форум программистов, компьютерный форум CyberForum.ru

gstreamer динамическая смена "location" для filesink - C++

Восстановить пароль Регистрация
 
Рейтинг: Рейтинг темы: голосов - 12, средняя оценка - 4.83
cyberguz
6 / 6 / 0
Регистрация: 01.12.2010
Сообщений: 105
05.12.2011, 15:04     gstreamer динамическая смена "location" для filesink #1
Задача была такая - писать видео с камеры, и по мере достижения условий максимальный размер файла или максимальное время записи, а также по команде извне, переходить к записи в новый файл. При этом необходимо чтобы не терялись данные, т.е. (насколько я понимаю) не останавливать поток.

Это, значицца - класс для игр с gstreamer.
Код
class GstRecorder : public BaseWorker <string, string>
{
private:
	GMainLoop* loop;
	gpointer pipeline;
	gpointer src_bin;
	gpointer sink_bin[2];
	gpointer filesink[2];
	gpointer queue_out[2];
	gpointer tee;
	bool running;
	string _path;
	string _currFilename;
	string _currFileEx;
public:
	GstRecorder () : pipeline(0), filesink({0}), sink_bin({0}), src_bin(0), tee(0), queue_out({0}) {}
	virtual void process (string devName, string path);
	void stop (void);
	void rotate (void);
};

Это - функция, выполняющаяся в отдельном потоке.
Код
void GstRecorder::process (string devName, string path)
{
	GstBus* bus;
	GstElement 	*src,
				*queue_input,
				*queue_before_tee,
				*rate,
				*enc,
				*mux,
				*frames,
				*deinterlace;

	TRACE << "GstRecorder::process " << devName << " " << path;

//	int argc = 0;
//	const char* argv[] = {""};
//	gst_init(&argc, &argv);

	TRACE << "init";
	gst_init(0, 0);

	TRACE << "new loop";
	loop = g_main_loop_new(NULL, FALSE);

	TRACE << "create element";
	pipeline 	= gst_pipeline_new ("video-recorder");
	src_bin 	= gst_bin_new ("src_bin");
	sink_bin[0] = gst_bin_new ("sink0_bin");
	sink_bin[1] = gst_bin_new ("sink1_bin");
	src   		= gst_element_factory_make ("dshowvideosrc",     "src");
	queue_input    	= gst_element_factory_make ("queue",       		"queue0");
	queue_before_tee    	= gst_element_factory_make ("queue",       		"queue1");
	queue_out[0]    	= gst_element_factory_make ("queue",       		"queue2");
	queue_out[1]    	= gst_element_factory_make ("queue",       		"queue3");
	tee    		= gst_element_factory_make ("tee",       		"tee");
	deinterlace = gst_element_factory_make ("deinterlace", 	"deinterlace");
	rate	   	= gst_element_factory_make ("videorate",      	"rate");
	frames   	= gst_element_factory_make ("capsfilter", 		"frames");
	enc 	   	= gst_element_factory_make ("xvidenc",     		"enc");
	mux      	= gst_element_factory_make ("matroskamux", 			"mux");
	filesink[0]     	= gst_element_factory_make ("filesink", 			"sink");
	filesink[1]     	= gst_element_factory_make ("filesink", 			"sink1");


	if
	(
		!pipeline || !src || !tee || !queue_input || !queue_before_tee || !queue_out[0]
		|| !queue_out[1] || !rate || !enc || !mux || !filesink[0] || !filesink[1] || !frames || !deinterlace
		|| !src_bin || !sink_bin[0] || !sink_bin[1]
	)
	{
		LOG_ERROR << "One element could not be created. Exiting.";
	}
	else
	{
		_currFilename = "1";
		_currFileEx = ".mkv";
		_path = path;

		TRACE << "setting params";
		g_object_set(G_OBJECT (src), "device-name", devName.c_str(), NULL);
		g_object_set(G_OBJECT (enc), "greyscale", 1, NULL);
//		g_object_set(G_OBJECT (enc), "bitrate", 10000, NULL);
		g_object_set(G_OBJECT (filesink[0]), "location", (_path + _currFilename + _currFileEx).c_str(), NULL);
//		g_object_set(G_OBJECT (sink), "location", (_path + "0" + _currFileEx).c_str(), NULL);
		GstCaps *caps = gst_caps_from_string("video/x-raw-yuv,framerate=12/1");
		g_object_set(G_OBJECT (frames), "caps", caps, NULL);

		TRACE << "add watch";
		bus = gst_pipeline_get_bus(GST_PIPELINE (pipeline));
		gst_bus_add_watch(bus, bus_call, loop);
		gst_object_unref(bus);

		TRACE << "add elements";
		gst_bin_add_many(GST_BIN(src_bin), src, queue_input, queue_before_tee, (GstElement*)tee, rate, frames, enc, mux, deinterlace, NULL);
		gst_bin_add_many(GST_BIN(sink_bin[0]), (GstElement*)queue_out[0], (GstElement*)filesink[0], NULL);
		gst_bin_add_many(GST_BIN(sink_bin[1]), (GstElement*)queue_out[1], (GstElement*)filesink[1], NULL);
		gst_bin_add(GST_BIN(pipeline), (GstElement*)src_bin);
		gst_bin_add(GST_BIN(pipeline), (GstElement*)sink_bin[0]);
		gst_bin_add(GST_BIN(pipeline), (GstElement*)sink_bin[1]);

		TRACE << "linking";
		gst_element_link_many(src, queue_input, deinterlace, rate, frames, enc, mux, queue_before_tee, (GstElement*)tee, NULL);
		gst_element_link_many((GstElement*)queue_out[0], (GstElement*)filesink[0], NULL);
		gst_element_link_many((GstElement*)queue_out[1], (GstElement*)filesink[1], NULL);
		gst_element_link((GstElement*)tee, (GstElement*)queue_out[0]);
		gst_element_link((GstElement*)tee, (GstElement*)queue_out[1]);

		TRACE << "set state PLAYING";
//		gst_element_set_state((GstElement*)pipeline, GST_STATE_PLAYING);
		gst_element_set_state((GstElement*)sink_bin[0], GST_STATE_PLAYING);
		gst_element_set_state((GstElement*)src_bin, GST_STATE_PLAYING);

		TRACE << "running";
		running = true;
		g_main_loop_run(loop);
		running = false;

		TRACE << "set state NULL";
		gst_element_set_state((GstElement*)pipeline, GST_STATE_NULL);

		gst_object_unref(GST_OBJECT(pipeline));
	}

	TRACE << "GstRecorder::process return";
}
Это - обработчик команды "сменить файл для сохранения видеопотока":
Код
void GstRecorder::rotate (void)
{
	//искать элемент по имени надо в бине:		gst_bin_get_by_name()

	//TODO: синхронизация
	TRACE << "GstRecorder::rotate";
	if (running)
	{
		/* следующее имя для файла */
		int n = strtol(_currFilename.c_str(), 0, 0);
		TRACE << "n = " << n;
		n++;
		stringstream ss;
		ss << n;
		ss >> _currFilename;
		TRACE << "_currFilename = " << _currFilename;

		/* меняем местами указатели на синки и бины */
		GstStateChangeReturn ret;
		gboolean ok;
		gpointer tmp = filesink[0];
		filesink[0] = filesink[1];
		filesink[1] = tmp;
		tmp = sink_bin[0];
		sink_bin[0] = sink_bin[1];
		sink_bin[1] = tmp;

		/* устанавливаем новое имя файла для не запущенного синка [0] */
		g_object_set(G_OBJECT(filesink[0]), "location", (_path + _currFilename + _currFileEx).c_str(), NULL);

		/* добавляем бин */
//		ok = gst_bin_add(GST_BIN(pipeline), (GstElement*)sink_bin[0]);
//		TRACE << "ok = " << ok;

		/* соединяем */
//		ok = gst_element_link((GstElement*)tee, (GstElement*)queue_out[0]);
//		TRACE << "ok = " << ok;

		/* запускаем бин */
		ret = gst_element_set_state((GstElement*)sink_bin[0], GST_STATE_PLAYING);
		TRACE << "ret = " << ret;

		/* отправляем запущенному бину [1] конец потока */
		GstEvent* event = gst_event_new_eos();
		ok = gst_element_send_event((GstElement*)sink_bin[1], event);
		TRACE << "ok = " << ok;

		/* останавливаем запущенный ранее бин [1] и освобождаем его ресурсы */
		ret = gst_element_set_state((GstElement*)sink_bin[1], GST_STATE_NULL);
		TRACE << "ret = " << ret;

		/* рассоединяем */
//		gst_element_unlink((GstElement*)tee, (GstElement*)queue_out[1]);

		/* удаляем бин */
//		ok = gst_bin_remove(GST_BIN(pipeline), (GstElement*)sink_bin[1]);
//		TRACE << "ok = " << ok;
	}
	TRACE << "GstRecorder::rotate return";
}

Идея была простая - вывожу через tee в два файла, один включен, другой выключен. Потом меняю, при этом сначала один включаю, потом другой выключаю - по идее куски видео должны перекрываться и ничего не потеряется.
Если не подсоединять второй бин вообще, то при соединении
Код
ok = gst_element_link((GstElement*)tee, (GstElement*)queue_out[0]);
возвращается false - т.е. не соединяется, естественно потом, после остановки работающего бина идёт ошибка, т.к. нет sink.
Если сразу всё соединить, но не запускать второй бин - вообще запись не запускается.


Подскажите, пожалуйста, кто что-нибудь знает?
Подозреваю, что ситуация типичная, но в доках ничего не нашёл, а google находит только неудовлетворённые вопросы на форумах.

Заранее спасибо.

Добавлено через 20 минут
Так, видимо в итоге вопрос сводится к более общему вопросу: позволяет ли gstreamer динамически перестраивать pipeline без его остановки?

Добавлено через 1 час 53 минуты
Урраааа Заработало!

Фишка была в том, что при добавлении элементов - добавляем, запускаем, соединяем; а при удалении - отсоединяем, останавливаем, удаляем.

Код
void GstRecorder::rotate (void)
{
	//TODO: синхронизация
	TRACE << "GstRecorder::rotate";
	if (running)
	{
		/* следующее имя для файла */
		int n = strtol(_currFilename.c_str(), 0, 0);
		TRACE << "n = " << n;
		n++;
		stringstream ss;
		ss << n;
		ss >> _currFilename;
		TRACE << "_currFilename = " << _currFilename;

		/* меняем местами указатели на синки и бины */
		GstStateChangeReturn ret;
		gboolean ok;
		gpointer tmp = filesink[0];
		filesink[0] = filesink[1];
		filesink[1] = tmp;
		tmp = sink_bin[0];
		sink_bin[0] = sink_bin[1];
		sink_bin[1] = tmp;
		tmp = queue_out[0];
		queue_out[0] = queue_out[1];
		queue_out[1] = tmp;

		/* устанавливаем новое имя файла для не запущенного синка [0] */
		g_object_set(G_OBJECT(filesink[0]), "location", (_path + _currFilename + _currFileEx).c_str(), NULL);

		/* добавляем бин */
		ok = gst_bin_add(GST_BIN(pipeline), (GstElement*)sink_bin[0]);
		TRACE << "ok = " << ok;

		/* запускаем бин */
		ok = gst_element_sync_state_with_parent((GstElement*)sink_bin[0]);
		TRACE << "ok = " << ok;

		/* соединяем */
		ok = gst_element_link((GstElement*)tee, (GstElement*)queue_out[0]);
		TRACE << "ok = " << ok;

		/* отправляем запущенному бину [1] конец потока */
		GstEvent* event = gst_event_new_eos();
		ok = gst_element_send_event((GstElement*)queue_out[1], event);
		TRACE << "ok = " << ok;

		/* рассоединяем */
		gst_element_unlink((GstElement*)tee, (GstElement*)queue_out[1]);

		/* останавливаем запущенный ранее бин [1] и освобождаем его ресурсы */
		ret = gst_element_set_state((GstElement*)sink_bin[1], GST_STATE_NULL);
		TRACE << "ret = " << ret;

		/* удаляем бин */
		ok = gst_bin_remove(GST_BIN(pipeline), (GstElement*)sink_bin[1]);
		TRACE << "ok = " << ok;
	}
	TRACE << "GstRecorder::rotate return";
}


Ещй важный момент - для каждого файла - отдельный контейнер, проще всего (на мой взгляд) поставить отдельные matroskamux для каждого sink после tee.
Код
		TRACE << "linking";
		gst_element_link_many(src, queue_input, deinterlace, rate, frames, enc, queue_before_tee, (GstElement*)tee, NULL);
		gst_element_link_many((GstElement*)queue_out[0], mux[0], (GstElement*)filesink[0], NULL);
		gst_element_link_many((GstElement*)queue_out[1], mux[1], (GstElement*)filesink[1], NULL);
		gst_element_link((GstElement*)tee, (GstElement*)queue_out[0]);
Добавлено через 16 минут
Рано обрадовался ( - работает только один раз, при следующем вызове
Код
	/* добавляем бин */
		ok = gst_bin_add(GST_BIN(pipeline), (GstElement*)sink_bin[0]);
		TRACE << "ok = " << ok;
возвращает 0, ну и конечно следующий файл не открывается, а после

Код
/* отправляем запущенному бину [1] конец потока */
		GstEvent* event = gst_event_new_eos();
		ok = gst_element_send_event((GstElement*)queue_out[1], event);
		TRACE << "ok = " << ok;
весь pipeline получает сигнал EOS, видимо из-за того, что отключается последний sink.
Similar
Эксперт
41792 / 34177 / 6122
Регистрация: 12.04.2006
Сообщений: 57,940
05.12.2011, 15:04     gstreamer динамическая смена "location" для filesink
Посмотрите здесь:

C++ Программа "Динамическая модель солнечной системы"
Динамическая структура данных(контейнер) типа "Вектор" C++
Непонятная ошибка "Access violation writing location 0xcdcdcdd5" C++
Как можно найти итерацию, на которой происходит "access violation reading location"? C++
C++ Ошибка "Unhandled exception, Access violation writing location"
Ошибка "Access violation writing location" при работе с массивом. C++
C++ Ошибка компилятора: "Access violation writing location"
Динамическая структура "Стек". Обновить значение элемента C++

Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
После регистрации реклама в сообщениях будет скрыта и будут доступны все возможности форума.
Ответ Создать тему
Опции темы

Текущее время: 23:18. Часовой пояс GMT +3.
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin® Version 3.8.9
Copyright ©2000 - 2016, vBulletin Solutions, Inc.
Рейтинг@Mail.ru