[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
ビデオツールアプリの動画編集機能実装に着手しました。
まずはFFmpegを使って動画をフレーム単位に分解し、GUIに表示させてコマ送りができるようにしました。
動画の分解でアスペクト比がおかしくなっているのでffmpegコマンドの修正が必要です。後は使い勝手がいいようにウィジェットの位置やサイズを調整します。
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
ビデオツールアプリの動画編集機能実装に着手しました。
まずはFFmpegを使って動画をフレーム単位に分解し、GUIに表示させてコマ送りができるようにしました。
動画の分解でアスペクト比がおかしくなっているのでffmpegコマンドの修正が必要です。後は使い勝手がいいようにウィジェットの位置やサイズを調整します。
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
開発中のビデオツールアプリでメニューの階層を2層に増やしました。
2層の場合はFl_Menu_Itemの最後に{0}を3つ付加する必要があります。1層では2つ付加します。
int main(int argc, char **argv) {
static Fl_Menu_Item items[] = {
{ "Menu", 0, 0, 0, FL_SUBMENU },
{ "Window切替", 0, 0, 0, FL_SUBMENU },
{ "Video Tool", 0, switchWindow0},
{ "FFmpeg Command Maker", 0, switchWindow1},
{ "Video Editor", 0, switchWindow2},
{ 0 },
{ 0 },
{ 0 }
};
Fl_Sys_Menu_Bar *menubar;
menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
menubar->box(FL_FLAT_BOX);
menubar->menu(items);
<以下略>
}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
開発中のビデオツールアプリで別ウィンドウへ画面遷移できるようにしました。
これができなければFLTKを使いこなせているとは言えない、と思いながらついつい放置してきましたが、ようやく本腰を入れて取り組みました。
初めは同一Fl_Windowでredrawさせようかとも考えたものの、コンストラクタ内で条件分岐するとウィンドウが真っ黒になり挫折、別WindowとしてSubWindowクラスを作成したらうまくできました。表示する位置が元ウィンドウと完全一致するように工夫を入れています。
着手2時間で挫折、昼寝中に解決方法を着想、起きて30分で実装という流れでした。職場なら2、3時間の昼寝はご法度でしょうが、プログラミングの場合は比較的長時間の馬上(ドライブ・乗り物他)・枕上(昼寝)・厠上(トイレ)の三上を認めてもらいたいものです。あと電話が鳴る環境も思考深化の妨げになるので厳禁です。
ゲームや動画鑑賞など、頭をリラックスさせて着想に導く方法は人それぞれなので、何でも取り入れてみるべきですね。着手して1回泥沼にハマる(周辺知識を一気に詰め込む作業でもある)ことが前提条件です。
Fl_Window *window;
SubWindow *subWindow;
int mode; // 0:メイン、1:サブ
void switchWindow(Fl_Widget*, void*){
if (mode == 1){
mode = 0;
subWindow -> hide();
int xs_win = subWindow->x_root();
int ys_win = subWindow->y_root();
cout<<"xs_win "<< xs_win <<" ys_win "<< ys_win <<endl;
window->resize(xs_win,ys_win,720,480);
window -> show();
} else {
mode = 1;
window -> hide();
int x_win = window->x_root();
int y_win = window->y_root();
cout<<"x_win "<< x_win <<" y_win "<< y_win <<endl;
subWindow->resize(x_win,y_win,720,480);
subWindow -> show();
}
}
int main(int argc, char **argv) {
static Fl_Menu_Item items[] = {
{ "設定", 0, 0, 0, FL_SUBMENU },
{ "ウィンドウ切替", 0, switchWindow, 0, 0 },
{ 0 },
{ 0 }
};
Fl_Sys_Menu_Bar *menubar;
menubar = new Fl_Sys_Menu_Bar(0, 0, 60, 20);
menubar->box(FL_FLAT_BOX);
menubar->menu(items);
window = new Fl_Window(100,100,720,480,"Video Tool");
window->color(fl_rgb_color(0,163,175));
<中略>
window->end();
window->show(argc, argv);
// SubWindow(最初は表示しない)
int x_win = window->x_root();
int y_win = window->y_root();
subWindow = new SubWindow(720,480,"Video Tool 2nd");
subWindow->resize(x_win,y_win,720,480);
return Fl::run();
}
#pragma once
#include <FLstd.h> // 自製ヘッダファイル FLTKライブラリ
#include <cppstd.h> // 自製ヘッダファイル C++標準ライブラリ
class SubWindow: public Fl_Window
{
public:
SubWindow(int w,int h,const char *title);
~SubWindow();
};
#include <SubWindow.h>
SubWindow::SubWindow(int w,int h,const char *title=0)
:Fl_Window(w,h,title){
this->Fl_Widget::color(fl_rgb_color(65,154,202)); // ふじむらさき
}
SubWindow::~SubWindow(){}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
ビデオツールアプリにリサイズ機能を搭載しました。
簡単な動画加工にはFinal Cut Proではなくこのツールを使えるよう、さらに機能を充実させていきたいです。
void resize(){
char cmd[512];
char count[100];
const char* del = ".";
// 元ファイルパス取得
const char* path = input_line->value();
cout << "path "<< path << endl;
// stringへ変換
string path_str = string(path);
// 拡張子を削除
string prefix0 = spt.splitJoin(path_str, del, 0, -2);
cout << "prefix0 "<< prefix0 << endl;
prefix0.pop_back();
// _resizedを付加
string prefix = prefix0 + "_resized";
// 拡張子を付加
vector<string> list_path = spt.splits(path_str, del);
string extension = list_path[list_path.size()-1];
cout << "extension "<< extension << endl;
string newPath = prefix + "." + extension;
cout << "newPath "<< newPath << endl;
// リサイズcmd作成
sprintf(cmd,"/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffmpeg -i %s -s %d:%d %s 2>&1 && echo ffmpeg完了", path_str.c_str(), width2_int, height2_int, newPath.c_str());
cout << "cmd: "<< cmd << endl;
// cmdをtextBufferに追記
string cmd_str(cmd, 512);
string cmdstr2 = "cmd: " + cmd_str + "\n";
textBuffer->append(cmdstr2.c_str());
// 文字数と行数をカウントしtextBufferに追記
int length_buf = textBuffer -> length();
int num_lines = textBuffer -> count_lines(0, length_buf);
printf("length_buf %d num_lines %d\n",length_buf,num_lines);
sprintf(count, "length_buf %d num_lines %d\n",length_buf,num_lines);
textBuffer->append(count);
// textDisplayを最終行表示する
textDisplay->buffer(textBuffer);
textDisplay->scroll(num_lines + 1, 0);
// cmd実行
outputTextMake(cmd);
// browserを最終行表示する
browser->load(outputText);
int line_num = browser->size();
browser->bottomline(line_num);
cout << "リサイズ完了!" << endl;
}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
開発中のビデオツールアプリについて、アスペクト比固定での縦横サイズを算出できるようにしました。
LOCK ASPECT RATIOボタンにチェックを入れると、空白にしている方のサイズを算出してくれます。
リサイズ操作については次回以降書きます。
// LOCK ASPECT RATIOボタンをチェック時の動作
void lock_cb(Fl_Widget*, void*) {
onoffLock = lock_cbtn -> value();
if (onoffLock == 1){
cout << "lock_cb実行" << endl;
width2 = width_input -> value();
height2 = height_input -> value();
if ((string(height2)).empty()){
cout << "height算出" << endl;
width2_int = stoi(string(width2));
cout << "width2_int " << width2_int << endl;
height2_int = std::round(width2_int * ratio);
cout << "height2_int " << height2_int << endl;
height_input -> value((to_string(height2_int)).c_str());
} else {
cout << "width算出" << endl;
height2_int = stoi(string(height2));
cout << "height2_int " << height2_int << endl;
width2_int = std::round(height2_int * (float)(1/ratio));
cout << "width2_int " << width2_int << endl;
width_input -> value((to_string(width2_int)).c_str());
}
} else {
cout << "lock_cb実行なし" << endl;
}
}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
開発中のビデオツールアプリで動画の縦横サイズを取得し、Fl_Inputに表示させました。
ffproveコマンドの出力をFILE構造体として取得、fgets関数でcharとしてデータを取り出し、xを区切り文字にしてリスト化しました。0番目の要素がwidth、1番目の要素がheightになります。
最初、変数resultのメモリ領域を確保せずにchar *resultとしていたところ、ビルドは成功しましたが、アプリとして動作させるとセグメンテーション違反が発生しました。正しくは、char result[受け取るデータの最大バイト数]になります。データを格納する時はポインタ変数を設定するのではなく、あらかじめその領域を確保しておく必要があります。初心者にありがちなミスでした。
void inspect(){
char count[100];
// 対象ファイルパス取得
const char* path = input_line->value();
cout << "path "<< path << endl;
string path_str = string(path);
bufferstr += "path_str: " + path_str + "\n";
// path内半角スペースをアンダースコアへ置き換え
string path2 = underScoreReplace(string(path_str));
bufferstr += "path2: " + path2 + "\n";
// 元ファイルをリネーム
rename(path, path2.c_str());
// ファイル縦横サイズ出力コマンド作成
string cmd = "/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffprobe -v error -select_streams v -show_entries stream=width,height -of csv=p=0:s=x " + path2;
cout << "cmd: "<< cmd << endl;
// cmdをtextBufferに追記
string cmdstr = "cmd: " + cmd + "\n";
textBuffer->append(cmdstr.c_str());
// 文字数と行数をカウントしtextBufferに追記
int length_buf = textBuffer -> length();
int num_lines = textBuffer -> count_lines(0, length_buf);
printf("length_buf %d num_lines %d\n",length_buf,num_lines);
sprintf(count, "length_buf %d num_lines %d\n",length_buf,num_lines);
textBuffer->append(count);
// textDisplayを最終行表示する
textDisplay->buffer(textBuffer);
textDisplay->scroll(num_lines + 1, 0);
// cmd実行
showInspectResult(cmd);
cout << "inspect完了!" << endl;
}
void showInspectResult(string cmd){
char result[10];
FILE* fp = popen(cmd.c_str(), "r");
fgets(result, 10, fp);
pclose(fp);
string result_str = string(result);
cout << "result_str " << result_str << endl;
vector<string> data = spt.splits(result_str,"x"); // sptは自製クラス
cout << "data[0] " << data[0] << endl;
cout << "data[1] " << data[1] << endl;
width = stoi(data[0]);
height = stoi(data[1]);
cout << "width " << width << endl;
cout << "height " << height << endl;
width_input0 -> value((to_string(width)).c_str());
height_input0 -> value((to_string(height)).c_str());
}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
開発中のビデオツールアプリでFl_Text_DisplayとFl_Browserの最終行を常に表示するようにしました。
Fl_Browser(下図下)の方はすぐに方法が分かったものの、Fl_Text_Display(下図右上)はFl_Text_Bufferの方でしか行数を把握できないと知るまで時間がかかり難航しました。
Fl_Text_Display::scroll関数の第1引数を10000行などあり得ない大きな数字に設定しても最終行表示は可能ですが、さすがに荒っぽいやり方なので行数を正確にカウントしました。
これくらいの機能は用意されていて当たり前と考えがちですが、簡易ツールのFLTKには通用しませんでした。使いこなせるかどうかはユーザーの工夫次第でしょう。
人気面でwxWidgetsに水を開けられてしまうのも致し方なしです。
void inspect(){
char count[100];
// 対象ファイルパス取得
const char* path = input_line->value();
cout << "path "<< path << endl;
string path_str = string(path);
bufferstr += "path_str: " + path_str + "\n";
// path内半角スペースをアンダースコアへ置き換え
string path2 = underScoreReplace(string(path_str));
bufferstr += "path2: " + path2 + "\n";
// 元ファイルをリネーム
rename(path, path2.c_str());
// ファイル情報出力コマンド作成
string cmd = "/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffprobe -i " + path2 + " 2>&1 && echo ffprobe完了";
// cmdをtextBufferに追記
string appendstr = "cmd: " + cmd + "\n";
bufferstr += appendstr;
cout << "bufferstr: " << bufferstr << endl;
textBuffer->append(bufferstr.c_str());
// 文字数と行数をカウントしtextBufferに追記
int length_buf = textBuffer -> length();
int num_lines = textBuffer -> count_lines(0, length_buf);
printf("length_buf %d num_lines %d\n",length_buf,num_lines);
sprintf(count, "length_buf %d num_lines %d\n",length_buf,num_lines);
textBuffer->append(count);
// textDisplayを最終行表示する
textDisplay->buffer(textBuffer);
textDisplay->scroll(num_lines + 1, 0);
// cmd実行
outputTextMake(cmd);
// browserを最終行表示する
browser->load(outputText);
int line_num = browser->size();
browser->bottomline(line_num);
cout << "inspect完了!" << endl;
}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
開発中のビデオツールアプリでFFmpegのログをGUI上に表示させました。
手順は以下の通りです。
1.ffmpegコマンド内でFFmpegログの出力先を標準エラー出力から標準出力にリダイレクトする。
2.ffmpegコマンドの標準出力をFILE型データで取得する。
3.ログ用テキストファイルに書き込み、Fl_BrouserでGUIに表示する。
void inspect(){
// 対象ファイルパス取得
const char* path = input_line->value();
cout << "path "<< path << endl;
string path_str = string(path);
bufferstr += "path_str: " + path_str + "\n";
// path内半角スペースをアンダースコアへ置き換え
string path2 = underScoreReplace(string(path_str));
bufferstr += "path2: " + path2 + "\n";
// 元ファイルをリネーム
rename(path, path2.c_str());
// ファイル情報出力コマンド作成(標準出力へリダイレクト)
string cmd = "/opt/homebrew/Cellar/ffmpeg/5.1/bin/ffprobe -i " + path2 + " 2>&1 && echo ffprobe完了";
// cmdをbufferへ追記
string appendstr = "cmd: " + cmd + "\n";
bufferstr += appendstr;
cout << "bufferstr: " << bufferstr << endl;
textBuffer->append(bufferstr.c_str());
textDisplay->buffer(textBuffer);
// cmd実行
outputTextMake(cmd);
browser->load(outputText);
int line_num = browser->size();
browser->bottomline(line_num);
cout << "inspect完了!" << endl;
}
void outputTextMake(string cmd){
char buf[READ_SIZE];
size_t ret;
FILE* fp = popen(cmd.c_str(), "r");
FILE* fo = fopen(outputText, "a+");
while(1){
ret = fread(buf, sizeof(char), READ_SIZE, fp);
fwrite(buf, sizeof(char), ret, fo);
if(ret < READ_SIZE){
break;
}
}
// 文字列追記
string finish = "標準出力ファイル化完了\n";
fprintf(fo, "%s", finish.c_str());
pclose(fp);
fclose(fo);
}
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
通信プロトコルの実験を進めていく過程でなぜかホームディレクトリへの書き込みが不可になっていたため、一部アプリが動かなくなりました。
ホームディレクトリの権限を縮小した覚えは全くないのですが、以下のように読み取り可能、書き込み不可、実行可能になっていたので、chmodコマンドで権限を全て可能に戻しました。
[M1 Mac, Big Sur 11.6.8, clang 13.0.0, FLTK 1.3.8, NO IDE]
前回の続きです。
SFTP補助アプリですが、ファイル名に日本語が含まれる場合はクリップボードへコピーされないことが判明しました。毎度ながら実行ファイルでは問題なく、appファイルでおかしくなります。
解決方法はpbcopyの際のロケール設定です。日本語を全く受け付けないのではなく、文字化けでも構わないのでコピーして欲しかったです。解決の手掛かりになりますので。
AppleScriptで同じトラブルに見舞われた方のブログ記事を参考にしました。まさかpbcopyに変なクセがあるとは思いもよりませんでした。
void cmd_cb(Fl_Widget*, void*) {
string cmd0;
string cmd1;
string cmd;
int onoff_put = put_rbtn->value();
int onoff_dir = dir_rbtn->value();
if (onoff_put == 1){
cmd0 = "put";
} else {
cmd0 = "get";
}
if (onoff_dir == 1){
cmd1 = "-r";
} else {
cmd1 = "";
}
const char* fileName = file_input -> value();
const char* toName = to_input -> value();
cmd = cmd0 + " " + cmd1 + " \"" + string(fileName) + "\" " + string(toName);
cout << "cmd " << cmd << endl;
cmd_input -> value("");
cmd_input -> value(cmd.c_str());
string cmdCopy = "echo '" + cmd + "' | LANG=ja_JP.UTF-8 pbcopy"; // 対応箇所
output_line -> insert(cmdCopy.c_str());
output_line -> insert("\n");
system(cmdCopy.c_str());
}