VideoPlayerに出会った問題たち
インデックス
一、ムービーの上にウィジェットを置けない問題
1.ムービーのビューはcocosのビューではなく、System widgetに配置されており、さらにムービービューが常にトップに固定されているため、ZOrderの調整を行っても変更が反映されない。。。(ZOrderの変更はcocosのビューに対して行うもの)
(たまにZOrderを調整すれば動くと書いてるサイトを見かけるが、それは多分間違ってる。。。)
(ビューのトップ固定を解除したら動くかもしれないが。。。試したことないので機会があればやってみる)
Androidの場合はUIVideoPlayer_android.cppで、iOSの場合はUIVideoPlayer_iOS.mmを参照しており、それぞれ動かしたいのであれば、どっちも対応する必要がある。
■解決法
・iOS
1.新規ルートビューをつかし、動画ビューやcocosビューをそれぞれルートビューに追加する
AppController.mm
_viewController.view.backgroundColor = [UIColor clearColor]
先ずはビューコントローラーの透明度を追加する
UIView* videoView;
次はappController.hに新しいビデオビューを追加する
_viewController.view.tag = 3;
UIViewController* uiVC = [[UIViewController alloc] initWithNibName:nil bundle:nil];
uiVC.view.frame = [UIScreenm
ainScreen].bounds;
_videoView = [[UIView alloc]initWithFrame:[[UIScreen mainScreen]bounds] ];
_videoView.tag = 1;
[uiVC.view addSubview : _videoView];
[uiVC.view addSubview : _viewController.view];
// Set RootViewController to window
if ([[UIDevice currentDevice].systemVersion floatValue] < 6.0)
{
// warning: addSubView doesn't work on iOS6
[window addSubview : uiVC.view];
}
else
{
// use this method on ios6
[window setRootViewController : uiVC];
}
UIVideoPlayer_ios.mm
2.VideoPlayerのデフォルトビューを新規ルートビューに変更
[[[eaglview superview] viewWithTag:10] addSubView:self.moviePlayer.view];
3.エンジンのデフォルトピクセルフォームを変更する
pixelFormat = PixelFormat::RGBA8;
1.ムービービューのトップ固定を解除
Cocos2dxVideoHelper.java
//videoView.setZOrderOnTop(true);
2.ムービービューに透明度を付与
AppActivity.java
二、ムービー機能のマルチ対応
異なるソースファイルを参照しているため、機能が統一していない。。。
→画面をクリックして一時停止Or再開
・iOS
→スキップボタン
ダブルクリックして画面拡大
■解決方法
VideoPlayerの機能を使用せず、自ら機能を実装する。
1.VideoPlayerのタッチ反応を遮断する
videoPlayerの基本機能のタッチを反応するかの方を使用する。
2.機能実装
三、Windowsに対応していない問題
VideoPlayerはWindowsに対応していないため、位置の調整とか、画面をフルスクリーンにするかを変更した場合、Windowsでは確認できないので、実機にビルドするしかない。。。(超時間かかる)
四、ムービーの配置問題
フルスクリーンを指定した場合は動画ビューのサイズや位置に関係なく、画面いっぱいに表示されます。
フルスクリーンを指定しない場合に、ビューの位置を知るためにはGetVisibleOriginを使用してゲットできる。
【最新OS対応】音声割込みを検知しよう
目次
- 経緯
- 主な対応項目
- 対応方法
- 例外
一、経緯
仕事でとあるアプリの最新OS対応を行った時に発生したことです。
昔のアプリでサスペンドせずに音声が割り込まれる場合の対応がされていないので、例えばプッシュ通知にてアラームをスワイプで消した場合、音声が復帰しなくなりました。他にはマルチタスクやピクチャーインピクチャーのような最近のOSで実装された機能としては、もちろん当時はありませんので、対応する必要があります。
つまり、以前のOSでは、何か音声の割り込みがあった時に必ずサスペンドをコールバックするので、サスレジの対応をすれば問題がありませんが、最近のOSではサスペンドせずに音声の割り込みだけ発生することがあります。サスレジの箇所だけで音声操作を対応した場合、音声割込み終了後に音声が復帰しないことがあります(まさに今回発生した内容)。
二、主な対応項目
画面遷移せずに音声が割り込む場合
三、対応方法
- インポート
#import <AVFoundation/AVFoundation.h>
- インスタンスの取得
AVAudioSession *audioSession = [AVAudioSession sharedInstance];
- カテゴリの指定
NSError *error = nil;
[audioSession setCategory:AVAudioSessionCategoryPlayback error:&error];
必要に応じてカテゴリを変更すれば良い、基本的にアプリはサイレントスイッチによりオンオフの切り替えが必要であるため、AVAudioSessionCategorySoloAmbientを指定しています。
また、マルチタスクやピクチャーインピクチャーのような機能も同時に対応したい場合に、同時に音声をサポートすることが必要になります。
[audioSession setCategory:AVAudioSessionCategoryPlayback mode:mixWithOthers error:&error];
- 割り込みの検知
- (void)audioSessionInterrupted:(NSNotification *)notification {
NSNumber *interruptType = [notification.userInfo objectForKey:@"AVAudioSessionInterruptionTypeKey"];
if ([interruptType unsignedIntegerValue] == AVAudioSessionInterruptionTypeBegan){
//割り込み開始時の処理
}else if([interruptType unsignedIntegerValue] == AVAudioSessionInterruptionTypeEnded){
//割り込み終了時の処理
}
}
四、例外
- 音声割込みとサスレジが同時にコールされた場合
音声割込みとサスレジが同時にコールされた場合は、特に問題はないが、同じ処理を二度読んでしまいますので、どちらかを呼ばないようにする方がよろしい気がします。
- OSにより、音声割込みとサスレジの順番が違いことがある
対応している時に、OSにより、音声割り込みとサスレジが呼ばれる順番が異なることがあるみたいです。基本的な順番としては音声割り込むとサスレジのワンセットずつ呼ばれることが望ましいだが、そうならない場合が実際ありました。
※確実ではないだが、OS14からは修正されているようです。(14と以前のOSとの挙動が異なっている)
参考資料
AVAudioSession細かいことまとめ(late 2014)
【最新OS対応】iOS14でSetStatusBarOrientationが動作しなくなった!どうしよー
- ことの経緯
- 原因
- 目標
- 解決
■ことの経緯
仕事でおよそ十年前にリリースされたアプリの最新OS対応を任された(iOS14.5対応)。最新のアップデートはiOS8-iOS11なので、かなり経っている。。。
Xcode12でビルドしたら、なぜか端末の向きが固定されることになった(画面の回転ができるのに)。
これについて調査を行い、Xcode10でビルドしたものは問題なく動作する(iOS12)なので、新しめのOSでのみ発生することが分かった(非推奨の匂いがプンプンしてきた)。
仕様上アプリ内で動的にボタンを押して画面の回転(CGAffineTransform)を制御し、同時にステータスバーの回転処理を行っている(SetStatusBarOrientation)。画面を回転してもステータスバーがついてきてないので、 SetStatusBarOrientationの辺で問題発生しているのでは?っと思いながら、引き続き調査を行った。
それで、原因は分かった。
■原因
SetStatusBarOrientationがiOS9から非推奨になり、iOS14で作動しないことを確認した。iOS13は確認してないが、先輩方によるとiOS13では既に挙動がおかしいらしい。AppleはAutoRotateを対応してほしいみたいなので、なくなって当然か。。。
これにより、AutoRotateを対応せず、動的に端末の回転を制御しているアプリ(CGAffineTransform+SetStatusBarOrietation)では、画面の回転はできるものの、ホームインジケーター、ステータスバー、プッシュ通知などが全部固定のままになってしまう。
■目標
画面の回転をしたときにステータスバー、ホームインジケーターなどがついてきて来るようにしたい!
iOS14にて動的にステータスバー等を制御する方法は未だに見つかっておらず(私の知識不足かも)、ディレクターと先方と相談した結果、AutoRotateを対応することになった。
※ご存じの方がいたら、ぜひご教授を
■解決
ならば話が早い!
・ShouldAutoRotateの対応
・レイアウトの修正
・元の機能の削除
実際の対応はこれぐらいになる。
SetStatusBarOrientationが使えなくなったので、先ずは端末の回転ができるようにしたいと思い、ShouldAutoRotateの対応をした。特に制限を加えることがなく、やり方も簡単で、ソースは以下になる。
/**ここからはソース**/
- (Bool)ShouldAutoRotate{
if(/*制限を加えたい場合*/) return NO;
return YES;
}
/**ここまではソース**/
これを追加した後に自動回転を設定する箇所に回転を許可したい向きを設定すればよい。私の場合はLandscapeLeft、LandscapeRight、Protraitを許可した。
正直ProtraitUpsideDownの存在が微妙。。。端末を逆さまに持つシチュエーションってあまりなくない?っと思いながらも、
作りとして4方向対応が必須なので(Appleより)、ちゃんとできるようにしましょう!
早速確認したところ、ホームインジケーターやプッシュ通知などは固定のままではなく、ちゃんとついてきた!
が、画面がそのまま引き延ばされて、製品として提供できない。。。
という問題点があって、レイアウトの調整が必要になった。以下ソース。
/**ここからはソース**/
- (void)viewWillTransitionToSize:(CGSize)size withTransitionCoordinator:(id<UIViewControllerTransitionCoordinator>) coordinator{
coordinator animateAlongsideTransition:^(id<UIViewControllerTransitionCoordinatorContext>context){
//ここでレイアウトの処理
} completion:nil]; }
/**ここまではソース**/
Viewのサイズを指定する必要があるので、Coordinatorを指定した。
これでレイアウトが調整された。
最後は、元の仕様にあたる部分は今回の対応でいくつか競合する箇所があり、そこのソースの削除やいくつかのレイアウトの修正を行った。
例えばSetStatusBarOrientationは作動しなくなったとはいえ、放置したままはよくないので、コメントアウトを。
これで対応がひとまず完了。
■課題
対応は完了したが、気になる課題がいくつか残っている(実際に執筆した時点でも問題が発生している)。
例えば、元の仕様では動的に端末の回転を行っているので、想定していないシチュエーションで回転を許可するとどうなるか。ViewWillTransitionToSizeの実行タイミングは完全にシステム任せなので、アプリ側からの制御が難しい、描画中やシーン遷移中に実行されるとおかしくなりそう。。。
一応作りによるものなので、SetStatusBarOrientationの対応はこれで良いと思う。
これらの課題に関しては、別途で記事を作る予定。
glfwのエラーコールバック
- glfwのエラーコールバックとは?
OpenGLで初期化に使うGLFWとGLEWに関して、GLFWの方はエラーが発生した場合に指定した関数を呼び出すエラーコールバック関数を用意してくれている。その関数を使えば、GLFWから渡されたエラーメッセージを標準エラー出力に表示することが容易にできる。
- どう使うの?
glfwのエラーコールバックは二つの部分から構成されている。
1.コールバックをGLFWに設定する関数
glfwSetErrorCallback(ErrorCallBack);
これを呼び出せばGLFWに設定することが出来る、簡単です。
2.コールバック関数
void ErrorCallback(int error_code, const char* description){
// エラーを出力するかどうするかはプログラマーによって自由にコードで
//きる。
std::cerr << description << std::endl;
}
関数名や実装は自由に定義できる。普通はエラーメッセージを表示するだけ。
c++ 純粋仮想関数
[目次]
- 純粋仮想関数とは
- 使い方
- 純粋仮想関数のできること
/**************************************************************
virtual bool Initialize() = 0 { }
virtual void ProcessInput() = 0 { }
virtual void Update(float) = 0 { }
virtual void Render() = 0 { }
virtual void Finalize() = 0 { }
/**************************************************************
コード学習の時に怪しげなコードを見つけた。なんやろと思って調べたら、仮想関数の後ろに " = 0 " を付けたこれは純粋仮想関数というものらしい。英語は " pure virtual function "。
1. 純粋仮想関数とは
純粋仮想関数とは、仮想関数の一種で、実装を持たない関数のことを指す。純粋仮想関数を持つクラスは抽象クラスと呼ぶ。その抽象クラスはインスタンス化されず、持つすべての純粋仮想関数を継承し実装して初めて呼び出すことができる。でないと子クラスも抽象クラスになる。
2.使い方
使い方は簡単で、仮想関数の後ろに " = 0 " を付ければ純粋仮想関数になる。
// 宣言
class Parent {
virtual void functionA() = 0 { }
}
class Child {
void functionA() { ... }
}
// 呼び出し:
Child a;
a.functionA();
3.純粋仮想関数のできること
本文の一番最初に乗せたコードはシーンの切り替えや描画などにかかわるクラスで書かれたコードで、流れとしては初期化して、入力を検知し状態を更新して描画する。
このようにシーンは多数あるはずなので、純粋仮想関数でテンプレートみたいの物を先に用意して、後に必要な時に新たなシーンを作れば、コード上はまとまってきれいなような気がするが、そのまま仮想関数を使うのもありかもしれない。。。
つまり、純粋仮想関数は実装を持たない仮想関数で、持つクラスは抽象クラスと呼び、インスタンス化ができない。
今回は基礎的なもので純粋仮想関数と仮想関数の違いは後日研究してまたノートに乗せようと考えている。アルゴリズム的な違いではなく、内の動きみたいな感じでいいのかな。
では、今回はこれで。
参考サイト