LoginSignup
1
0

More than 3 years have passed since last update.

openFrameworksでのJPEGエンコード

Posted at

openFrameworksでの動画再生が重い問題の解決として、外部アプリに動画再生を任せて通信にしてみようと思い立ち、とりあえず以下の手順で実装してみました。

  • WPFで動画再生してJPEG圧縮
  • UDPで通信
  • openFrameworksでofImageへ変換して描画

最後のoFでのJPEGエンコードで苦労したのでメモ。
コードはこちら:
(oF) : https://github.com/ma-ring/oF/tree/jpgEncoder/JpgEncoder
(WPF): https://github.com/ma-ring/oF/tree/jpgEncoder/VideoController

WPFで動画再生してJPEG圧縮

動画の再生はMediaPlayerで行います。
MediaPlayerをDrawingVisualに描いてbitmapを取得。
JpegBitmapEncoderという便利なクラスがあるのでこれでJPEG圧縮までOK。

MainWindow.xaml.cs
private MediaPlayer mPlayer;

public MainWindow()
        {
            SetupClient();

            InitializeComponent();

            mPlayer = new MediaPlayer();
            mPlayer.ScrubbingEnabled = true;
            mPlayer.Open(new Uri(path, UriKind.Relative));
            MediaVideo.Source = new Uri(path, UriKind.Relative);

            play();

        }

void sendFrame()
        {

            int width = (int)(MediaVideo.Width);
            int height = (int)(MediaVideo.Height);

            // 描画用の Visual を用意
            var visual = new DrawingVisual();

            using (var context = visual.RenderOpen())
            {
                context.DrawVideo(mPlayer, new System.Windows.Rect(0, 0, width, height));
            }

            var bitmap = new RenderTargetBitmap(width, height, 96, 96, PixelFormats.Pbgra32);
            bitmap.Render(visual);
            //image.Source = bitmap;
            var encoder = new JpegBitmapEncoder();
            //encoder.QualityLevel = 30;
            encoder.Frames.Add(BitmapFrame.Create(bitmap));


            using (MemoryStream ms = new MemoryStream())
            {
                encoder.Save(ms);
                byte[] data = ms.ToArray();
                Debug.Print(data.ToString());
                mSendClient.Send(data, data.Length, mRemoteIP, mRemotePort);
            }
        }

UDPで通信

こちらもそのまま。

MainWindow.xaml.cs
private string mRemoteIP = "127.0.0.1";
private int mRemotePort = 5000;
private UdpClient mSendClient;
private void SetupClient()
        {
            System.Net.IPAddress ip = System.Net.IPAddress.Parse(mRemoteIP);
            mSendClient = new UdpClient();

            StartSending();
        }
ofApp.h
#include "FreeImage.h"

class ofApp : public ofBaseApp{
  //---略
  ofxUDPManager mReceiver;
  ofImage mFrameImage;
  //--略
}
ofApp.cpp
#define IP "127.0.0.1"
#define PORT 5000
#define VIDEO_FILE_PATH  "video.mp4"
#define MSG_BUFF_SIZE 100000

void ofApp::setup(){
    mReceiver.Create();
    mReceiver.Bind(PORT);
    mReceiver.SetNonBlocking(true);
}

openFrameworksでofImageへ変換して描画

これが本題。
jpgEncoder関数としてまとめました。
結論としては、oFだとFreeImage (https://freeimage.sourceforge.io/) というライブラリが使えるので、こちらを利用しました。

ofApp.cpp
void ofApp::update(){

    //recieve
    char recvMsg[MSG_BUFF_SIZE];
    int recvSize = mReceiver.PeekReceive();
    if (recvSize > 0) {
        mReceiver.Receive(recvMsg, MSG_BUFF_SIZE);
        jpgEncoder(&recvMsg[0], recvSize, mFrameImage);

    }

}
void ofApp::jpgEncoder(char* src, int size, ofImage& dst) {
    vector<unsigned char> data(src, src + size);
    //mFrame = imdecode(data, 1);

    FIMEMORY *hmem;
    FREE_IMAGE_FORMAT fif;
    FIBITMAP *bmp;

    hmem = FreeImage_OpenMemory(&data[0], size);
    fif = FreeImage_GetFileTypeFromMemory(hmem, 0);
    bmp = FreeImage_LoadFromMemory(fif, hmem, 0);
    dst.setFromPixels(FreeImage_GetBits(bmp), 1920 /4, 1080/4 , ofImageType::OF_IMAGE_COLOR, true);
    dst.mirror(true, false);
    dst.getPixelsRef().swapRgb();
    dst.update();
}

ただそのままだとRGBの並びがおかしかったり反転してたりします。
pixelの並びが問題なんだろうなとは思いつつ、今回はお試しなのでmirrorとswapRgbで調整してます。
これはshaderにしてもいいかもしれない。

まとめ

たぶん本当に使うならSyphonとかSpoutを使うべきなんだろうなとは思います。
あとoF側でJPEG圧縮する方法を模索中です、だれか知見があれば教えてください。

1
0
1

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
1
0