openFrameworksでのJPEGエンコード

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。

```c#: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で通信
こちらもそのまま。

```c#: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();
        }
```

```c++:ofApp.h
#include "FreeImage.h"

class ofApp : public ofBaseApp{
  //---略
  ofxUDPManager mReceiver;
  ofImage mFrameImage;
  //--略
}
```

```c++: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/) というライブラリが使えるので、こちらを利用しました。


```c++: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圧縮する方法を模索中です、だれか知見があれば教えてください。