1. 程式人生 > >GStreamer基礎教程07——多執行緒和Pad的有效性

GStreamer基礎教程07——多執行緒和Pad的有效性

目標

      GStreamer會自動處理多執行緒這部分,但在有些情況下,你需要手動對執行緒做解耦。本教程會教你怎樣才能做到這一點,另外也展示了Pad的有效性。主要內容包括:

      如何針對部分的pipeline建立一個新的執行緒

      什麼是Pad的有效性

      如何複製流

介紹

多執行緒

      GStreamer是一個支援多執行緒的框架。這就說明,如果有必要它會在內部自動建立/銷燬執行緒。比如:在應用執行緒中把流解出來。而且,plugin在自身也可以任意的建立執行緒,比如一個視訊解碼器為了充分利用4核CPU的能力,可以建立4個執行緒。

      這裡最重要的是,當應用在建立pipeline時可以明確一個branch(一部分pipeline)在另一個執行緒中執行(比如,讓音訊解碼和視訊解碼同時執行)。

      這個可以用queue element來達到這個目的,執行的時候sink pad僅僅負責把資料放到queue裡面,同時在另一個執行緒裡把資料從queue裡面取出並向下傳送。這個element同樣可以用來做緩衝,這點在後面講述流的教程時可以看到。Queue的大小事可以用設定屬性的方法來設定的。

一個pipeline例子

      這個例子建立的pipeline如下圖所示。


      這裡的源是一個合成的音訊訊號(一個連續的tone),這個音訊訊號會被tee element分成兩路。一路傳送訊號到音效卡,另一個就在螢幕上渲染一個波形。

      從圖上看,queue會建立一個新的執行緒,所以整個pipeline有3個執行緒在執行。通常來說,有多於一個的sink element時就需要多個執行緒。這是因為在同步時,sink通常是阻塞起來等待所有其他的sink都準備好,如果僅僅只有一個執行緒是無法做到這一點的。

Request pads

      在《GStreamer基礎教程03——動態pipeline》裡面,我們看見了一個初始時沒有pad的element(uridecodebin),pad會在資料流到element時才會出現。這種pad被成為Sometimes Pad,平時的那種一直存在的pad被稱為Always Pad。

      第三種pad稱為Request Pad,這是根據需要來建立的。經典的例子就是tee element——有1個輸入pad而沒有輸出pad,需要有申請,tee  element才會生成。通過這種方法,輸入流可以被複製成多份。和Always Pad比起來,Request Pad因為並非一直存在,所以是不能自動連線element的,這點在下面的例子中可以看到。

      另外,在PLAYING或PAUSED狀態下去獲得pad需要注意(Pad阻塞,本教程沒有講到這點),在NULL和READY狀態去獲得pad就沒有這個問題。

      閒話少說,上程式碼。

簡單地多執行緒例子

#include <gst/gst.h>
  
int main(int argc, char *argv[]) {
  GstElement *pipeline, *audio_source, *tee, *audio_queue, *audio_convert, *audio_resample, *audio_sink;
  GstElement *video_queue, *visual, *video_convert, *video_sink;
  GstBus *bus;
  GstMessage *msg;
  GstPadTemplate *tee_src_pad_template;
  GstPad *tee_audio_pad, *tee_video_pad;
  GstPad *queue_audio_pad, *queue_video_pad;
  
  /* Initialize GStreamer */
  gst_init (&argc, &argv);
  
  /* Create the elements */
  audio_source = gst_element_factory_make ("audiotestsrc", "audio_source");
  tee = gst_element_factory_make ("tee", "tee");
  audio_queue = gst_element_factory_make ("queue", "audio_queue");
  audio_convert = gst_element_factory_make ("audioconvert", "audio_convert");
  audio_resample = gst_element_factory_make ("audioresample", "audio_resample");
  audio_sink = gst_element_factory_make ("autoaudiosink", "audio_sink");
  video_queue = gst_element_factory_make ("queue", "video_queue");
  visual = gst_element_factory_make ("wavescope", "visual");
  video_convert = gst_element_factory_make ("ffmpegcolorspace", "csp");
  video_sink = gst_element_factory_make ("autovideosink", "video_sink");
  
  /* Create the empty pipeline */
  pipeline = gst_pipeline_new ("test-pipeline");
  
  if (!pipeline || !audio_source || !tee || !audio_queue || !audio_convert || !audio_resample || !audio_sink ||
      !video_queue || !visual || !video_convert || !video_sink) {
    g_printerr ("Not all elements could be created.\n");
    return -1;
  }
  
  /* Configure elements */
  g_object_set (audio_source, "freq", 215.0f, NULL);
  g_object_set (visual, "shader", 0, "style", 3, NULL);
  
  /* Link all elements that can be automatically linked because they have "Always" pads */
  gst_bin_add_many (GST_BIN (pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,
      video_queue, visual, video_convert, video_sink, NULL);
  if (gst_element_link_many (audio_source, tee, NULL) != TRUE ||
      gst_element_link_many (audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||
      gst_element_link_many (video_queue, visual, video_convert, video_sink, NULL) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
  
  /* Manually link the Tee, which has "Request" pads */
  tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");
  tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for audio branch.\n", gst_pad_get_name (tee_audio_pad));
  queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");
  tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for video branch.\n", gst_pad_get_name (tee_video_pad));
  queue_video_pad = gst_element_get_static_pad (video_queue, "sink");
  if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
      gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {
    g_printerr ("Tee could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
  gst_object_unref (queue_audio_pad);
  gst_object_unref (queue_video_pad);
  
  /* Start playing the pipeline */
  gst_element_set_state (pipeline, GST_STATE_PLAYING);
  
  /* Wait until error or EOS */
  bus = gst_element_get_bus (pipeline);
  msg = gst_bus_timed_pop_filtered (bus, GST_CLOCK_TIME_NONE, GST_MESSAGE_ERROR | GST_MESSAGE_EOS);
  
  /* Release the request pads from the Tee, and unref them */
  gst_element_release_request_pad (tee, tee_audio_pad);
  gst_element_release_request_pad (tee, tee_video_pad);
  gst_object_unref (tee_audio_pad);
  gst_object_unref (tee_video_pad);
  
  /* Free resources */
  if (msg != NULL)
    gst_message_unref (msg);
  gst_object_unref (bus);
  gst_element_set_state (pipeline, GST_STATE_NULL);
  
  gst_object_unref (pipeline);
  return 0;
}

工作流程
  /* Create the elements */
  audio_source = gst_element_factory_make ("audiotestsrc", "audio_source");
  tee = gst_element_factory_make ("tee", "tee");
  audio_queue = gst_element_factory_make ("queue", "audio_queue");
  audio_convert = gst_element_factory_make ("audioconvert", "audio_convert");
  audio_resample = gst_element_factory_make ("audioresample", "audio_resample");
  audio_sink = gst_element_factory_make ("autoaudiosink", "audio_sink");
  video_queue = gst_element_factory_make ("queue", "video_queue");
  visual = gst_element_factory_make ("wavescope", "visual");
  video_convert = gst_element_factory_make ("ffmpegcolorspace", "csp");
  video_sink = gst_element_factory_make ("autovideosink", "video_sink");
      上面圖中所有的element都在這裡建立了。

      audiotestsrc會建立一個合成的tone,wavescope會像示波器一樣用一個音訊訊號來渲染出一個波形。我們前面已經用過autoaudiosink和autovideosink這兩個element了。

      轉換element(audio convert,audioresample和ffmpegcolorspace)也是必須的,它們可以保證pipeline可以正確地連線。事實上,音訊和視訊的sink的Caps是由硬體確定的,所以你在設計時是不知道audiotestsrc和wavescope是否可以匹配上。如果Caps能夠匹配,這些element的行為就類似於直通——對訊號不做任何修改,這對於效率的影響基本可以忽略不計。

  /* Configure elements */
  g_object_set (audio_source, "freq", 215.0f, NULL);
  g_object_set (visual, "shader", 0, "style", 3, NULL);
      為了更好的演示做了小小的調整:audiotestsrc的“freq”屬性設定成215Hz,wavescope設定“shader”和“style”,讓波形連續。用gst-inspect可以更好的瞭解這幾個element的屬性。
  /* Link all elements that can be automatically linked because they have "Always" pads */
  gst_bin_add_many (GST_BIN (pipeline), audio_source, tee, audio_queue, audio_convert, audio_resample, audio_sink,
      video_queue, visual, video_convert, video_sink, NULL);
  if (gst_element_link_many (audio_source, tee, NULL) != TRUE ||
      gst_element_link_many (audio_queue, audio_convert, audio_resample, audio_sink, NULL) != TRUE ||
      gst_element_link_many (video_queue, visual, video_convert, video_sink, NULL) != TRUE) {
    g_printerr ("Elements could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
      這塊程式碼在pipeline里加入了所有的element並且把可以自動連線的element都連線了起來(就是Always Pad)。
  /* Manually link the Tee, which has "Request" pads */
  tee_src_pad_template = gst_element_class_get_pad_template (GST_ELEMENT_GET_CLASS (tee), "src%d");
  tee_audio_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for audio branch.\n", gst_pad_get_name (tee_audio_pad));
  queue_audio_pad = gst_element_get_static_pad (audio_queue, "sink");
  tee_video_pad = gst_element_request_pad (tee, tee_src_pad_template, NULL, NULL);
  g_print ("Obtained request pad %s for video branch.\n", gst_pad_get_name (tee_video_pad));
  queue_video_pad = gst_element_get_static_pad (video_queue, "sink");
  if (gst_pad_link (tee_audio_pad, queue_audio_pad) != GST_PAD_LINK_OK ||
      gst_pad_link (tee_video_pad, queue_video_pad) != GST_PAD_LINK_OK) {
    g_printerr ("Tee could not be linked.\n");
    gst_object_unref (pipeline);
    return -1;
  }
  gst_object_unref (queue_audio_pad);
  gst_object_unref (queue_video_pad);

      為了連線Request Pad,需要獲得對element的“requesting”。一個element可能可以建立不同種類的Request Pad,所以,當請求Pad生成時,必須提供想要的Pad模板。Pad模板可以用gst_element_class_get_pad_template()方法來獲得,而且用它們的名字來區分開。在tee element的文件裡面我們可以看到兩個pad模板,分別被稱為“sink”(sink pad)和“src%d”(Request Pad)。

      一旦我們有了Pad模板,我們就用gst_element_request_pad()方法向tee請求兩個Pad——分別給音訊分支和視訊分支。

      然後我們去獲得下游的element需要連線Request Pad的Pad,這些通常都是Always Pad,所以我們用get_element_get_static_pad()方法去獲得。

      最後,我們用gst_pad_link()方法把pad連線起來。在gst_element_link()和gst_element_link_many()方法裡面也是呼叫這個函式來連線的。

      我們獲得的這個sink pad需要通過gst_object_unref()來釋放。Request Pad是在我們不需要的時候釋放,也就是在程式的最後。

      就像平常一樣,我們設定pipeline到PLAYING狀態,等待一個錯誤訊息或者EOS訊息到達。剩下的所有事情就是清楚request pad。

  /* Release the request pads from the Tee, and unref them */
  gst_element_release_request_pad (tee, tee_audio_pad);
  gst_element_release_request_pad (tee, tee_video_pad);
  gst_object_unref (tee_audio_pad);
  gst_object_unref (tee_video_pad);
      gst_element_release_request_pad()可以釋放tee的pad,但還需要呼叫gst_object_unref()才行。

相關推薦

GStreamer基礎教程07——執行Pad有效性

目標       GStreamer會自動處理多執行緒這部分,但在有些情況下,你需要手動對執行緒做解耦。本教程會教你怎樣才能做到這一點,另外也展示了Pad的有效性。主要內容包括:       如何針對部分的pipeline建立一個新的執行緒       什麼是Pad的有效性

GStreamer基礎教程08 - 執行

摘要   GStreamer框架會自動處理多執行緒的邏輯,但在某些情況下,我們仍然需要根據實際的情況自己將部分Pipeline在單獨的執行緒中執行,本文將介紹如何處理這種情況。 GStreamer多執行緒   GStreamer框架是一個支援多執行緒的框架,執行緒會根據Pipeline的需要自動建立和銷

jfinalQ開發教程08-qiao-util.jar:執行定時任務

多執行緒 多執行緒是java面試中最愛問的一個問題,當然如果工作多年沒準備去面試,正好讓你手寫程式碼,那就只能呵呵了~ QThreadUtil com.uikoo9.util.function.QThreadUtil對java自帶的多執行緒做了封裝,其實java自帶多執行緒已經

JAVA基礎複習(七)執行網路

1、建立執行緒和任務,如: //任務類必須實現Runnable介面 public class TaskClass implements Runnable{ ... public TaskClass(...){ ... } //想要在該執行緒執行的

JAVA執行併發基礎面試問答

原文連結 譯文連線 作者:Pankaj  譯者:鄭旭東  校對:方騰飛 多執行緒和併發問題是Java技術面試中面試官比較喜歡問的問題之一。在這裡,從面試的角度列出了大部分重要的問題,但是你仍然應該牢固的掌握Java多執行緒基礎知識來對應日後碰到的問題。(校對注:非常贊同這個觀點) Java多執

JAVA執行併發基礎面試問答(轉載)

  多執行緒和併發問題是Java技術面試中面試官比較喜歡問的問題之一。在這裡,從面試的角度列出了大部分重要的問題,但是你仍然應該牢固的掌握Java多執行緒基礎知識來對應日後碰到的問題。(校對注:非常贊同這個觀點) Java多執行緒面試問題 1. 程序和執行緒之間有什麼不同? 一個程序是一個獨立(

Java執行記憶體模型(一):程序執行基礎

Java多執行緒和記憶體模型(一) 由於java是執行在 JVM上 的,所以需要涉及到 JVM 的記憶體模型概念,需要理解記憶體模型,就需要多執行緒的基礎; 而執行緒是基於載體執行緒裡的,所以我們藉由作業系統的程序來講一講。 程序 什麼是程序?

JAVA執行併發基礎面試題

多執行緒和併發問題是Java技術面試中面試官比較喜歡問的問題之一。在這裡,從面試的角度列出了大部分重要的問題,但是你仍然應該牢固的掌握Java多執行緒基礎知識來對應日後碰到的問題。(校對注:非常贊同這個觀點) Java多執行緒面試問題 1. 程序和執行緒之間有什麼不同

Objective-C高階程式設計:iOS與OS X執行記憶體管理

這篇文章主要給大家講解一下GCD的平時不太常用的API,以及文末會貼出GCD定時器的一個小例子。 需要學習的朋友可以通過網盤免費下載pdf版 (先點選普通下載-----再選擇普通使用者就能免費下載了)http://putpan.com/fs/cy1i1beebn7s0h4u9/ 1.G

[讀書筆記]iOS與OS X執行記憶體管理 [GCD部分]

3.2 GCD的API 蘋果對GCD的說明:開發者要做的只是定義想執行的任務並追加到適當的Dispatch Queue中。 “Dispatch Queue”是執行處理的等待佇列。通過dispatch_async函式等API,在Block

執行原子操作記憶體柵欄(二)

        這裡記錄下各種鎖的使用和使用場景,在多執行緒場景開發時,我們經常遇到多個執行緒同時讀寫一塊資源爭搶一塊資源的情況,比如同時讀寫同一個欄位屬性,同時對某個集合進行增刪改查,同時對資料庫進行讀寫(這裡

執行原子操作記憶體柵欄(一)

執行緒的定義是執行流的最小單元,而程序是一個邏輯執行緒容器,用來隔離執行緒。 Task類封裝了執行緒池執行緒,啟動的所有線都由執行緒池管理,他提供了很多使用方便的API函式,使多執行緒開發變得容易。 上述程式碼中我啟動了一個執行緒,並在執行緒方法中使用了非同步關鍵字,非同步方法實現了一個狀態

day24總結_執行設計模式

1、多執行緒 ①、JDK5以後的針對執行緒的鎖定操作和釋放操作 // 定義鎖物件 private Lock lock = new ReentrantLock(); // 加鎖 lock.lock(); // 釋放鎖 lock.unlock(); ②、死鎖問題的描述和程式碼體現 *死鎖:兩個或

JAVA執行併發面試問題

1. 程序和執行緒之間有什麼不同? 一個程序是一個獨立(self contained)的執行環境,它可以被看作一個程式或者一個應用。而執行緒是在程序中執行的一個任務。Java執行環境是一個包含了不同的類和程式的單一程序。執行緒可以被稱為輕量級程序。執行緒需要較少的資源來建立和駐留在程

執行匿名內部類的理解

多執行緒 一.多執行緒 好處:提高任務的執行效率 (執行緒本身也會耗費系統資源 建立執行緒要把握度) 程序:一個正在執行的程式 一個程序可以有一個或多個執行緒 分時排程:cpu同

linux執行基礎概念及執行程式設計

Linux中執行緒的概念: 首先,Linux中並不存在真在的執行緒。Linux中的執行緒是使用程序來模擬的。在一個程序需要同時執行多個執行流時,linux並不是開闢多個執行緒來執行,而是通過多個程序來模擬多個執行緒。 Linux中執行緒的實現原理: 首先先看一下張圖: 此時共有

Linux 執行程序的區別(小結)

最近學習Linux,看到“hairetz的專欄”的帖子不錯,特轉來大家一起學習。 很想寫點關於多程序和多執行緒的東西,我確實很愛他們。但是每每想動手寫點關於他們的東西,卻總是求全心理作祟,始終動不了手。 今天終於下了決心,寫點東西,以後可以再修修補補也無妨。一.為何需要多程序(或者多執行緒),為何需

python3執行GIL全域性直譯器所

GIL的全稱是:Global Interpreter Lock,意思就是全域性直譯器鎖,這個GIL並不是python的特性,他是隻在Cpython直譯器裡引入的一個概念,而在其他的語言編寫的直譯器裡就沒有這個GIL例如:Jython,Pypy 為什麼會有gil?:      

執行-day-08執行執行併發工具總結

目錄 多執行緒和執行緒併發工具總結 執行緒基礎、執行緒之間的共享協作 基礎概念 Java執行緒 執行緒常用方法和執行緒狀態 共享執行緒 執行緒間協作 執行緒併發工具類 Fork-Join分而治之、工作密取 Fork-Join標準正規化 Fork-Joi

python執行GIL全域性直譯器鎖

1、執行緒     執行緒被稱為輕量級程序,是最小執行單元,系統排程的單位。執行緒切換需要的資源一般,效率一般。  2、多執行緒         在單個程式中同時執行多個執行緒完成不同的工作,稱為多執行緒 3、