3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

More than 5 years have passed since last update.

macOS アプリ (Cocoa) に GStreamer を入れる手順

Last updated at Posted at 2017-09-29

GStreamer 公式のドキュメントには Cocoa application に GStreamer を組み込む手順は無いんですが、やってみたら思いのほか簡単だった (Android や iOS に似てた) ので紹介します。

ここでは GStreamer の解説をするつもりは全くなく、「Cocoa application にはこんな感じでGStreamer 入れるんだー、へー。」くらいに見てください。

完成形

題材として、 iOS tutorial 3 の macOS 版 + α を作っていきます。

見た目はこんな感じ。

スクリーンショット 2017-09-29 10.26.13.png

完成形のソースコードはこちら↓から

環境

  • Macbook Pro Early 2015
  • macOS Sierra 10.12.6
  • Xcode 8.3.3

1. GStreamer.framework のインストール

https://gstreamer.freedesktop.org/data/pkg/osx/ からパッケージを2つダウンロードします。
※ 偶数バージョン (1.10.*, 1.12.* とか) のうち新しいものをダウンロードしておけばOK

今回ダウンロードしたもの↓

  • gstreamer-1.0-1.12.3-x86_64.pkg
  • gstreamer-1.0-devel-1.12.3-x86_64.pkg

ダウンロードしたら2つともインストールしてください。

2. プロジェクトの作成

Cocoa Application として作ります。

スクリーンショット 2017-09-28 14.27.50.png

3. プロジェクトの設定

プロジェクトの初期設定は Installing on Mac OS X を参考にやっていきます。

3.1. GStreamer.framework の設定

はじめに、Linked Frameworks and Libraries へ /Library/Frameworks/GStreamer.framework を追加します.

スクリーンショット 2017-09-28 14.48.04.png

いれるとこんな感じに。

スクリーンショット 2017-09-28 14.48.11.png

3.2. Build Settings の設定

Build Settings へ移動し、検索窓に header search path と入れます。
その中の Header Search Paths/Library/Frameworks/GStreamer.framework/Headersrecursive で追加します。

スクリーンショット 2017-09-28 14.51.33.png

次に、検索窓に c flags と入れて Other C Flags
-I/Library/Frameworks/GStreamer.framework/Headers を追加します。

スクリーンショット 2017-09-28 14.52.34.png

4. GStreamer のリンク確認

iOS tutorial 3 相当の実装を始める前に、GStreamer のリンクを確認するため、ミニマムな Tutorial 1 (画面なし) の実装をしてみます。

4.1. GStreamerBackend クラスの追加

Cocoa class を選択して追加します。

スクリーンショット 2017-09-28 14.58.26.png

言語は Objective-C を選択します (間違って swift を選択した場合はやり直しです :boom: )

スクリーンショット 2017-09-28 14.58.21.png

「Bridging header いる?」って聞かれるので作っておいてください。

スクリーンショット 2017-09-28 14.56.12.png

4.2. GStreamerBackend クラスの実装

クラス追加ができたらあとは実装です。

GStreamerBackend.h
# import <Foundation/Foundation.h>

@interface GStreamerBackend : NSObject

-(NSString*) getGStreamerVersion;

@end
GStreamerBackend.m
# import "GStreamerBackend.h"

# include <gst/gst.h>

@implementation GStreamerBackend

-(NSString*) getGStreamerVersion
{
    char *version_utf8 = gst_version_string();
    NSString *version_string = [NSString stringWithUTF8String:version_utf8];
    g_free(version_utf8);
    return version_string;
}

@end

4.3. Bridge-Header に GStreamerBackend を追加

Swift から Objective-C のクラスを使用するために Bridge header に GStreamerBackend.h を追加します

SimpleVideoViewer-Bridging-Header.h
# import "GStreamerBackend.h"

4.4. ViewController から GStreamerBackend クラスを使う

GStreamerBackend インスタンスを作って、getGStreamerVersion() を叩きます。

ViewController.swift
import Cocoa

class ViewController: NSViewController {

    override func viewDidLoad() {
        super.viewDidLoad()

        print(GStreamerBackend().getGStreamerVersion())
    }

    override var representedObject: Any? {
        didSet {
        // Update the view, if already loaded.
        }
    }
}

4.5. 動かしてみる

以上で iOS tutorial 1 相当の実装ができました。実際に動かしてみましょう。

スクリーンショット 2017-09-28 15.13.17.png

デバッグログに GStreamer バージョンが表示されるなんとも味気ないアプリの完成です :tada:

5. 画面作り

ここからやっと iOS tutorial 3 相当の実装が始まります。まずは画面を作りましょう。

ボタンの配置や Auto layout を設定するかは自由ですが、以下の 5つの View と 2つの Constraint は必ず必要です。(その他は気分で設定してください笑)

  • Views
    • Button x2 → 再生と一時停止
    • Label x1 → パイプラインの状態を表示するためのメッセージ
    • CustomBox x1 → 動画の表示領域を決定
    • CustomView x1 → GStreamer の描画先
  • Constraints
    • CustomView の width
    • CustomView の height

スクリーンショット 2017-09-28 16.28.33.png

ちなみに、 iOS tutorial 3 では UIView 中に UIView だったんですが、Appkit だと StoryBoard 上で NSView の background color を付けれなかったので Box を使っています。

background color を気にしないのであれば、NSView の中に NSView でも全く問題なしです :)

6. Storyboard と ViewController の連携

iOS アプリ作っている人にはおなじみの アレ です。ペタペタしてやってください。
※ CustomBox に関しては、Box の中にある NSView を Container としてください。

StoryBoard

スクリーンショット 2017-09-28 16.53.37.png

ViewController

ViewController.swift
  import Cocoa

  class ViewController: NSViewController, GStreamerBackendDelegate {

+    @IBOutlet weak var video_container_view: NSView!
+    @IBOutlet weak var video_view: NSView!
+    @IBOutlet weak var video_view_width_constraint: NSLayoutConstraint!
+    @IBOutlet weak var video_view_height_constraint: NSLayoutConstraint!

+    @IBOutlet weak var play_button: NSButton!
+    @IBOutlet weak var pause_button: NSButton!
+    @IBOutlet weak var message_label: NSTextField!

      override func viewDidLoad() {
          super.viewDidLoad()
      }

      [省略]
  }

7. OpenglNSView の実装

GStreamer の描画先として NSView を使うには EaglUIView 相当のもの (= OpenglNSView) を作る必要があります。

iOS tutorial 3: EaglUIView
https://gstreamer.freedesktop.org/documentation/tutorials/ios/video.html#eagluiview

New File... から Swift file を追加し OpenglNSView.swift を作ります。

OpenglNSView.swift に以下のコードをコピペします。

OpenglNSView.swift
import Foundation
import OpenGL
import Cocoa

class OpenglNSView: NSView {
    override func makeBackingLayer() -> CALayer {
        return CAOpenGLLayer();
    }
}

最後に StoryBoard の Video view のクラスを OpenglNSView に変更します。

スクリーンショット 2017-09-28 16.40.25.png

OpenglNSView の詳細が気になる人は チュートリアルApple のドキュメント を読んでみてください。

8. iOS tutorial 3からコードをコピペ

iOS tutorial 3 から以下の3つのコードをコピペしてきます。

9. ビルドを通す

チュートリアルからコピペしただけではビルドが通りません。通すために以下の2つを行います。

  1. UIView を NSView に置換
  2. NSView のために Cocoa/Cocoa.h をインポート

UIView の置換

UIView を NSView に置換します。Xcode の Replace 機能なり、sed なりで行ってください。全部でこのくらいあるはずです。

スクリーンショット 2017-09-28 16.45.09.png

Cocoa/Cocoa.h のインポート

GStreamerBackend.h に Cocoa/Cocoa.h を追加します。

GStreamerBackend.h
  #import <Foundation/Foundation.h>
+ #import <Cocoa/Cocoa.h>
  #import "GStreamerBackendDelegate.h"

  @interface GStreamerBackend : NSObject

  [省略]

  @end

この2ステップを行えば ビルドは通るようになった はずです。

10. ViewController の実装

チュートリアルの ViewController を swift 化します。

ViewController.swift
import Cocoa

class ViewController: NSViewController, GStreamerBackendDelegate {

    @IBOutlet weak var video_container_view: NSView!
    @IBOutlet weak var video_view: NSView!
    @IBOutlet weak var video_view_width_constraint: NSLayoutConstraint!
    @IBOutlet weak var video_view_height_constraint: NSLayoutConstraint!

    @IBOutlet weak var play_button: NSButton!
    @IBOutlet weak var pause_button: NSButton!
    @IBOutlet weak var message_label: NSTextField!

    let media_width: CGFloat = 320
    let media_height: CGFloat = 240
    var gst_backend: GStreamerBackend? = nil

    override func viewDidLoad() {
        super.viewDidLoad()

        play_button.isEnabled = false
        pause_button.isEnabled = false

        gst_backend = GStreamerBackend.init(self, videoView: video_view)
    }

    @IBAction func play(_ sender: Any) {
        gst_backend?.play()
    }

    @IBAction func pause(_ sender: Any) {
        gst_backend?.pause()
    }

    override func viewDidLayout() {
        let view_width: CGFloat = video_container_view.bounds.size.width;
        let view_height: CGFloat = video_container_view.bounds.size.height;
        
        let correct_height = view_width * media_height / media_width;
        let correct_width = view_height * media_width / media_height;
        
        if (correct_height < view_height) {
            video_view_height_constraint.constant = correct_height;
            video_view_width_constraint.constant = view_width;
        } else {
            video_view_width_constraint.constant = correct_width;
            video_view_height_constraint.constant = view_height;
        }
    }

    func gstreamerInitialized() {
        DispatchQueue.main.async {
            self.play_button.isEnabled = true
            self.pause_button.isEnabled = true
            self.message_label.stringValue = "Ready"
        }
    }

    func gstreamerSetUIMessage(_ message: String!) {
        DispatchQueue.main.async {
            self.message_label.stringValue = message
        }
    }
}

11. gst_init() を追加

ViewController を実装して完成!と思いきや、 重要なステップ が残っています。

そう、まだこのコードは gst_init() をしてないんです。gst_init() をしなければ、プラグインのロードも行われないので、「エレメント見つかりません」エラーが出てしまいます。

GStreamerBackend.mapp_function()gst_init(NULL, NULL) 追加しましょう。

GStreamerBackend.m
[省略]

/* Main method for the bus monitoring code */
-(void) app_function
{
    GstBus *bus;
    GSource *bus_source;
    GError *error = NULL;

    gst_init(NULL, NULL);
    
    GST_DEBUG ("Creating pipeline");

    /* Create our own GLib Main Context and make it the default one */
    context = g_main_context_new ();
    g_main_context_push_thread_default(context);

    /* Build pipeline */
    pipeline = gst_parse_launch("playbin uri=https://download.blender.org/durian/trailer/sintel_trailer-480p.mp4", &error);
    if (error) {
        gchar *message = g_strdup_printf("Unable to build pipeline: %s", error->message);
        g_clear_error (&error);
        [self setUIMessage:message];
        g_free (message);
        return;
    }

    [省略]
}

@end

↑ここではついでに gst_parse_launch() のパイプラインも変えておきました。この辺は好みで変えてください。

完成

お疲れ様です、以上で完成です! :tada:

重要なところは、 7. OpenglNSView の実装, 9. ビルドを通す, 11. gst_init() の追加 くらいなものなので iOS → macOS は意外と簡単ですね。

あとは、パイプラインを変えるなり UI をいじるなりして遊んでみてください :)

3
3
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
3
3

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?