mirror of
https://github.com/juce-framework/JUCE.git
synced 2026-01-10 23:44:24 +00:00
Add video playback support for Android and iOS. Update VideoComponent API to support building custom UIs.
This commit is contained in:
parent
dc7217fbbb
commit
315326477d
45 changed files with 4293 additions and 308 deletions
|
|
@ -1467,6 +1467,7 @@ add_library( ${BINARY_NAME}
|
||||||
"../../../../../modules/juce_video/capture/juce_CameraDevice.cpp"
|
"../../../../../modules/juce_video/capture/juce_CameraDevice.cpp"
|
||||||
"../../../../../modules/juce_video/capture/juce_CameraDevice.h"
|
"../../../../../modules/juce_video/capture/juce_CameraDevice.h"
|
||||||
"../../../../../modules/juce_video/native/juce_android_CameraDevice.h"
|
"../../../../../modules/juce_video/native/juce_android_CameraDevice.h"
|
||||||
|
"../../../../../modules/juce_video/native/juce_android_Video.h"
|
||||||
"../../../../../modules/juce_video/native/juce_ios_CameraDevice.h"
|
"../../../../../modules/juce_video/native/juce_ios_CameraDevice.h"
|
||||||
"../../../../../modules/juce_video/native/juce_mac_CameraDevice.h"
|
"../../../../../modules/juce_video/native/juce_mac_CameraDevice.h"
|
||||||
"../../../../../modules/juce_video/native/juce_mac_Video.h"
|
"../../../../../modules/juce_video/native/juce_mac_Video.h"
|
||||||
|
|
@ -2932,6 +2933,7 @@ set_source_files_properties("../../../../../modules/juce_product_unlocking/juce_
|
||||||
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_android_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_android_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_android_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ import android.content.res.Configuration;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.hardware.camera2.*;
|
import android.hardware.camera2.*;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.media.session.*;
|
||||||
|
import android.media.MediaMetadata;
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -94,8 +97,11 @@ public class DemoRunner extends Activity
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public boolean isPermissionDeclaredInManifest (int permissionID)
|
public boolean isPermissionDeclaredInManifest (int permissionID)
|
||||||
{
|
{
|
||||||
String permissionToCheck = getAndroidPermissionName(permissionID);
|
return isPermissionDeclaredInManifest (getAndroidPermissionName (permissionID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPermissionDeclaredInManifest (String permissionToCheck)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||||
|
|
@ -1997,11 +2003,13 @@ public class DemoRunner extends Activity
|
||||||
implements SurfaceHolder.Callback
|
implements SurfaceHolder.Callback
|
||||||
{
|
{
|
||||||
private long nativeContext = 0;
|
private long nativeContext = 0;
|
||||||
|
private boolean forVideo;
|
||||||
|
|
||||||
NativeSurfaceView (Context context, long nativeContextPtr)
|
NativeSurfaceView (Context context, long nativeContextPtr, boolean createdForVideo)
|
||||||
{
|
{
|
||||||
super (context);
|
super (context);
|
||||||
nativeContext = nativeContextPtr;
|
nativeContext = nativeContextPtr;
|
||||||
|
forVideo = createdForVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Surface getNativeSurface()
|
public Surface getNativeSurface()
|
||||||
|
|
@ -2019,38 +2027,51 @@ public class DemoRunner extends Activity
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
||||||
{
|
{
|
||||||
surfaceChangedNative (nativeContext, holder, format, width, height);
|
if (forVideo)
|
||||||
|
surfaceChangedNativeVideo (nativeContext, holder, format, width, height);
|
||||||
|
else
|
||||||
|
surfaceChangedNative (nativeContext, holder, format, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated (SurfaceHolder holder)
|
public void surfaceCreated (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceCreatedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceCreatedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceCreatedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed (SurfaceHolder holder)
|
public void surfaceDestroyed (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceDestroyedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceDestroyedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceDestroyedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispatchDraw (Canvas canvas)
|
protected void dispatchDraw (Canvas canvas)
|
||||||
{
|
{
|
||||||
super.dispatchDraw (canvas);
|
super.dispatchDraw (canvas);
|
||||||
dispatchDrawNative (nativeContext, canvas);
|
|
||||||
|
if (forVideo)
|
||||||
|
dispatchDrawNativeVideo (nativeContext, canvas);
|
||||||
|
else
|
||||||
|
dispatchDrawNative (nativeContext, canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow ()
|
protected void onAttachedToWindow()
|
||||||
{
|
{
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
getHolder().addCallback (this);
|
getHolder().addCallback (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow ()
|
protected void onDetachedFromWindow()
|
||||||
{
|
{
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
getHolder().removeCallback (this);
|
getHolder().removeCallback (this);
|
||||||
|
|
@ -2062,11 +2083,17 @@ public class DemoRunner extends Activity
|
||||||
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
||||||
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
||||||
int format, int width, int height);
|
int format, int width, int height);
|
||||||
|
|
||||||
|
private native void dispatchDrawNativeVideo (long nativeContextPtr, Canvas canvas);
|
||||||
|
private native void surfaceCreatedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceDestroyedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceChangedNativeVideo (long nativeContextptr, SurfaceHolder holder,
|
||||||
|
int format, int width, int height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
|
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr, boolean forVideo)
|
||||||
{
|
{
|
||||||
return new NativeSurfaceView (this, nativeSurfacePtr);
|
return new NativeSurfaceView (this, nativeSurfacePtr, forVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -2826,6 +2853,151 @@ public class DemoRunner extends Activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaControllerCallback extends MediaController.Callback
|
||||||
|
{
|
||||||
|
private native void mediaControllerAudioInfoChanged (long host, MediaController.PlaybackInfo info);
|
||||||
|
private native void mediaControllerMetadataChanged (long host, MediaMetadata metadata);
|
||||||
|
private native void mediaControllerPlaybackStateChanged (long host, PlaybackState state);
|
||||||
|
private native void mediaControllerSessionDestroyed (long host);
|
||||||
|
|
||||||
|
MediaControllerCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioInfoChanged (MediaController.PlaybackInfo info)
|
||||||
|
{
|
||||||
|
mediaControllerAudioInfoChanged (host, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMetadataChanged (MediaMetadata metadata)
|
||||||
|
{
|
||||||
|
mediaControllerMetadataChanged (host, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged (PlaybackState state)
|
||||||
|
{
|
||||||
|
mediaControllerPlaybackStateChanged (host, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueueChanged (List<MediaSession.QueueItem> queue) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionDestroyed()
|
||||||
|
{
|
||||||
|
mediaControllerSessionDestroyed (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaSessionCallback extends MediaSession.Callback
|
||||||
|
{
|
||||||
|
private native void mediaSessionPause (long host);
|
||||||
|
private native void mediaSessionPlay (long host);
|
||||||
|
private native void mediaSessionPlayFromMediaId (long host, String mediaId, Bundle extras);
|
||||||
|
private native void mediaSessionSeekTo (long host, long pos);
|
||||||
|
private native void mediaSessionStop (long host);
|
||||||
|
|
||||||
|
|
||||||
|
MediaSessionCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause()
|
||||||
|
{
|
||||||
|
mediaSessionPause (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay()
|
||||||
|
{
|
||||||
|
mediaSessionPlay (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayFromMediaId (String mediaId, Bundle extras)
|
||||||
|
{
|
||||||
|
mediaSessionPlayFromMediaId (host, mediaId, extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSeekTo (long pos)
|
||||||
|
{
|
||||||
|
mediaSessionSeekTo (host, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop()
|
||||||
|
{
|
||||||
|
mediaSessionStop (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFastForward() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMediaButtonEvent (Intent mediaButtonIntent)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRewind() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToNext() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToPrevious() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToQueueItem (long id) {}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class SystemVolumeObserver extends ContentObserver
|
||||||
|
{
|
||||||
|
private native void mediaSessionSystemVolumeChanged (long host);
|
||||||
|
|
||||||
|
SystemVolumeObserver (Activity activityToUse, long hostToUse)
|
||||||
|
{
|
||||||
|
super (null);
|
||||||
|
|
||||||
|
activity = activityToUse;
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled (boolean shouldBeEnabled)
|
||||||
|
{
|
||||||
|
if (shouldBeEnabled)
|
||||||
|
activity.getApplicationContext().getContentResolver().registerContentObserver (android.provider.Settings.System.CONTENT_URI, true, this);
|
||||||
|
else
|
||||||
|
activity.getApplicationContext().getContentResolver().unregisterContentObserver (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange (boolean selfChange, Uri uri)
|
||||||
|
{
|
||||||
|
if (uri.toString().startsWith ("content://settings/system/volume_music"))
|
||||||
|
mediaSessionSystemVolumeChanged (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Activity activity;
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public static final String getLocaleValue (boolean isRegion)
|
public static final String getLocaleValue (boolean isRegion)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2840,6 +2840,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4827,6 +4827,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -2840,6 +2840,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4827,6 +4827,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -2840,6 +2840,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4827,6 +4827,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -291,6 +291,10 @@
|
||||||
#ifndef JUCE_USE_CAMERA
|
#ifndef JUCE_USE_CAMERA
|
||||||
#define JUCE_USE_CAMERA 1
|
#define JUCE_USE_CAMERA 1
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
//#define JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME 1
|
||||||
|
#endif
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#ifndef JUCE_STANDALONE_APPLICATION
|
#ifndef JUCE_STANDALONE_APPLICATION
|
||||||
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,7 @@
|
||||||
#include "../../../GUI/OpenGLDemo2D.h"
|
#include "../../../GUI/OpenGLDemo2D.h"
|
||||||
#endif
|
#endif
|
||||||
#include "../../../GUI/PropertiesDemo.h"
|
#include "../../../GUI/PropertiesDemo.h"
|
||||||
#if JUCE_MAC || JUCE_WINDOWS
|
#if ! JUCE_LINUX
|
||||||
#include "../../../GUI/VideoDemo.h"
|
#include "../../../GUI/VideoDemo.h"
|
||||||
#endif
|
#endif
|
||||||
#include "../../../GUI/WebBrowserDemo.h"
|
#include "../../../GUI/WebBrowserDemo.h"
|
||||||
|
|
@ -100,7 +100,7 @@ void registerDemos_Two() noexcept
|
||||||
REGISTER_DEMO_WITH_FILENAME (OpenGLDemoClasses::OpenGLDemo, GUI, OpenGLDemo, true)
|
REGISTER_DEMO_WITH_FILENAME (OpenGLDemoClasses::OpenGLDemo, GUI, OpenGLDemo, true)
|
||||||
#endif
|
#endif
|
||||||
REGISTER_DEMO (PropertiesDemo, GUI, false)
|
REGISTER_DEMO (PropertiesDemo, GUI, false)
|
||||||
#if JUCE_MAC || JUCE_WINDOWS
|
#if ! JUCE_LINUX
|
||||||
REGISTER_DEMO (VideoDemo, GUI, true)
|
REGISTER_DEMO (VideoDemo, GUI, true)
|
||||||
#endif
|
#endif
|
||||||
REGISTER_DEMO (WebBrowserDemo, GUI, true)
|
REGISTER_DEMO (WebBrowserDemo, GUI, true)
|
||||||
|
|
|
||||||
|
|
@ -159,6 +159,18 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
setSize (500, 500);
|
setSize (500, 500);
|
||||||
|
|
||||||
|
RuntimePermissions::request (RuntimePermissions::readExternalStorage,
|
||||||
|
[] (bool granted)
|
||||||
|
{
|
||||||
|
if (! granted)
|
||||||
|
{
|
||||||
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||||
|
"Permissions warning",
|
||||||
|
"External storage access permission not granted, some files"
|
||||||
|
" may be inaccessible.");
|
||||||
|
}
|
||||||
|
});
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
|
|
@ -46,6 +46,7 @@
|
||||||
|
|
||||||
#include "../Assets/DemoUtilities.h"
|
#include "../Assets/DemoUtilities.h"
|
||||||
|
|
||||||
|
#if JUCE_MAC || JUCE_WINDOWS
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
// so that we can easily have two video windows each with a file browser, wrap this up as a class..
|
// so that we can easily have two video windows each with a file browser, wrap this up as a class..
|
||||||
class MovieComponentWithFileBrowser : public Component,
|
class MovieComponentWithFileBrowser : public Component,
|
||||||
|
|
@ -54,6 +55,7 @@ class MovieComponentWithFileBrowser : public Component,
|
||||||
{
|
{
|
||||||
public:
|
public:
|
||||||
MovieComponentWithFileBrowser()
|
MovieComponentWithFileBrowser()
|
||||||
|
: videoComp (true)
|
||||||
{
|
{
|
||||||
addAndMakeVisible (videoComp);
|
addAndMakeVisible (videoComp);
|
||||||
|
|
||||||
|
|
@ -110,8 +112,16 @@ private:
|
||||||
|
|
||||||
void filenameComponentChanged (FilenameComponent*) override
|
void filenameComponentChanged (FilenameComponent*) override
|
||||||
{
|
{
|
||||||
|
auto url = URL (fileChooser.getCurrentFile());
|
||||||
|
|
||||||
// this is called when the user changes the filename in the file chooser box
|
// this is called when the user changes the filename in the file chooser box
|
||||||
auto result = videoComp.load (fileChooser.getCurrentFile());
|
auto result = videoComp.load (url);
|
||||||
|
videoLoadingFinished (url, result);
|
||||||
|
}
|
||||||
|
|
||||||
|
void videoLoadingFinished (const URL& url, Result result)
|
||||||
|
{
|
||||||
|
ignoreUnused (url);
|
||||||
|
|
||||||
if (result.wasOk())
|
if (result.wasOk())
|
||||||
{
|
{
|
||||||
|
|
@ -209,6 +219,7 @@ public:
|
||||||
}
|
}
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
std::unique_ptr<FileChooser> fileChooser;
|
||||||
WildcardFileFilter moviesWildcardFilter { "*", "*", "Movies File Filter" };
|
WildcardFileFilter moviesWildcardFilter { "*", "*", "Movies File Filter" };
|
||||||
TimeSliceThread directoryThread { "Movie File Scanner Thread" };
|
TimeSliceThread directoryThread { "Movie File Scanner Thread" };
|
||||||
DirectoryContentsList movieList { &moviesWildcardFilter, directoryThread };
|
DirectoryContentsList movieList { &moviesWildcardFilter, directoryThread };
|
||||||
|
|
@ -231,5 +242,462 @@ private:
|
||||||
void fileDoubleClicked (const File&) override {}
|
void fileDoubleClicked (const File&) override {}
|
||||||
void browserRootChanged (const File&) override {}
|
void browserRootChanged (const File&) override {}
|
||||||
|
|
||||||
|
void selectVideoFile()
|
||||||
|
{
|
||||||
|
fileChooser.reset (new FileChooser ("Choose a file to open...", File::getCurrentWorkingDirectory(),
|
||||||
|
"*", false));
|
||||||
|
|
||||||
|
fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
|
||||||
|
[this] (const FileChooser& chooser)
|
||||||
|
{
|
||||||
|
String chosen;
|
||||||
|
auto results = chooser.getURLResults();
|
||||||
|
|
||||||
|
// TODO: support non local files too
|
||||||
|
if (results.size() > 0)
|
||||||
|
movieCompLeft.setFile (results[0].getLocalFile());
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
|
||||||
};
|
};
|
||||||
|
#elif JUCE_IOS || JUCE_ANDROID
|
||||||
|
//==============================================================================
|
||||||
|
class VideoDemo : public Component,
|
||||||
|
private Timer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
VideoDemo()
|
||||||
|
: videoCompWithNativeControls (true),
|
||||||
|
videoCompNoNativeControls (false)
|
||||||
|
{
|
||||||
|
loadLocalButton .onClick = [this] { selectVideoFile(); };
|
||||||
|
loadUrlButton .onClick = [this] { showVideoUrlPrompt(); };
|
||||||
|
seekToStartButton.onClick = [this] { seekVideoToStart(); };
|
||||||
|
playButton .onClick = [this] { playVideo(); };
|
||||||
|
pauseButton .onClick = [this] { pauseVideo(); };
|
||||||
|
unloadButton .onClick = [this] { unloadVideoFile(); };
|
||||||
|
|
||||||
|
volumeLabel .setColour (Label::textColourId, Colours::white);
|
||||||
|
currentPositionLabel.setColour (Label::textColourId, Colours::white);
|
||||||
|
|
||||||
|
volumeLabel .setJustificationType (Justification::right);
|
||||||
|
currentPositionLabel.setJustificationType (Justification::right);
|
||||||
|
|
||||||
|
volumeSlider .setRange (0.0, 1.0);
|
||||||
|
positionSlider.setRange (0.0, 1.0);
|
||||||
|
|
||||||
|
volumeSlider .setSliderSnapsToMousePosition (false);
|
||||||
|
positionSlider.setSliderSnapsToMousePosition (false);
|
||||||
|
|
||||||
|
volumeSlider.setSkewFactor (1.5);
|
||||||
|
volumeSlider.setValue (1.0, dontSendNotification);
|
||||||
|
#if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
curVideoComp->onGlobalMediaVolumeChanged = [this]() { volumeSlider.setValue (curVideoComp->getAudioVolume(), dontSendNotification); };
|
||||||
|
#endif
|
||||||
|
|
||||||
|
volumeSlider .onValueChange = [this]() { curVideoComp->setAudioVolume ((float) volumeSlider.getValue()); };
|
||||||
|
positionSlider.onValueChange = [this]() { seekVideoToNormalisedPosition (positionSlider.getValue()); };
|
||||||
|
|
||||||
|
positionSlider.onDragStart = [this]()
|
||||||
|
{
|
||||||
|
positionSliderDragging = true;
|
||||||
|
wasPlayingBeforeDragStart = curVideoComp->isPlaying();
|
||||||
|
|
||||||
|
if (wasPlayingBeforeDragStart)
|
||||||
|
curVideoComp->stop();
|
||||||
|
};
|
||||||
|
|
||||||
|
positionSlider.onDragEnd = [this]()
|
||||||
|
{
|
||||||
|
if (wasPlayingBeforeDragStart)
|
||||||
|
curVideoComp->play();
|
||||||
|
|
||||||
|
wasPlayingBeforeDragStart = false;
|
||||||
|
|
||||||
|
// Ensure the slider does not temporarily jump back on consecutive timer callback.
|
||||||
|
Timer::callAfterDelay (500, [this]() { positionSliderDragging = false; });
|
||||||
|
};
|
||||||
|
|
||||||
|
playSpeedComboBox.addItem ("25%", 25);
|
||||||
|
playSpeedComboBox.addItem ("50%", 50);
|
||||||
|
playSpeedComboBox.addItem ("100%", 100);
|
||||||
|
playSpeedComboBox.addItem ("200%", 200);
|
||||||
|
playSpeedComboBox.addItem ("400%", 400);
|
||||||
|
playSpeedComboBox.setSelectedId (100, dontSendNotification);
|
||||||
|
playSpeedComboBox.onChange = [this]() { curVideoComp->setPlaySpeed (playSpeedComboBox.getSelectedId() / 100.0); };
|
||||||
|
|
||||||
|
setTransportControlsEnabled (false);
|
||||||
|
|
||||||
|
addAndMakeVisible (loadLocalButton);
|
||||||
|
addAndMakeVisible (loadUrlButton);
|
||||||
|
addAndMakeVisible (volumeLabel);
|
||||||
|
addAndMakeVisible (volumeSlider);
|
||||||
|
addChildComponent (videoCompWithNativeControls);
|
||||||
|
addChildComponent (videoCompNoNativeControls);
|
||||||
|
addAndMakeVisible (positionSlider);
|
||||||
|
addAndMakeVisible (currentPositionLabel);
|
||||||
|
|
||||||
|
addAndMakeVisible (playSpeedComboBox);
|
||||||
|
addAndMakeVisible (seekToStartButton);
|
||||||
|
addAndMakeVisible (playButton);
|
||||||
|
addAndMakeVisible (unloadButton);
|
||||||
|
addChildComponent (pauseButton);
|
||||||
|
|
||||||
|
setSize (500, 500);
|
||||||
|
|
||||||
|
RuntimePermissions::request (RuntimePermissions::readExternalStorage,
|
||||||
|
[] (bool granted)
|
||||||
|
{
|
||||||
|
if (! granted)
|
||||||
|
{
|
||||||
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||||
|
"Permissions warning",
|
||||||
|
"External storage access permission not granted, some files"
|
||||||
|
" may be inaccessible.");
|
||||||
|
}
|
||||||
|
});
|
||||||
|
|
||||||
|
setPortraitOrientationEnabled (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
~VideoDemo()
|
||||||
|
{
|
||||||
|
curVideoComp->onPlaybackStarted = nullptr;
|
||||||
|
curVideoComp->onPlaybackStopped = nullptr;
|
||||||
|
curVideoComp->onErrorOccurred = nullptr;
|
||||||
|
curVideoComp->onGlobalMediaVolumeChanged = nullptr;
|
||||||
|
|
||||||
|
setPortraitOrientationEnabled (false);
|
||||||
|
}
|
||||||
|
|
||||||
|
void paint (Graphics& g) override
|
||||||
|
{
|
||||||
|
g.fillAll (getUIColourIfAvailable (LookAndFeel_V4::ColourScheme::UIColour::windowBackground));
|
||||||
|
}
|
||||||
|
|
||||||
|
void resized() override
|
||||||
|
{
|
||||||
|
auto area = getLocalBounds();
|
||||||
|
|
||||||
|
int marginSize = 5;
|
||||||
|
int buttonHeight = 20;
|
||||||
|
|
||||||
|
area.reduce (0, marginSize);
|
||||||
|
|
||||||
|
auto topArea = area.removeFromTop (buttonHeight);
|
||||||
|
loadLocalButton.setBounds (topArea.removeFromLeft (topArea.getWidth() / 6));
|
||||||
|
loadUrlButton.setBounds (topArea.removeFromLeft (loadLocalButton.getWidth()));
|
||||||
|
volumeLabel.setBounds (topArea.removeFromLeft (loadLocalButton.getWidth()));
|
||||||
|
volumeSlider.setBounds (topArea.reduced (10, 0));
|
||||||
|
|
||||||
|
auto transportArea = area.removeFromBottom (buttonHeight);
|
||||||
|
auto positionArea = area.removeFromBottom (buttonHeight).reduced (marginSize, 0);
|
||||||
|
|
||||||
|
playSpeedComboBox.setBounds (transportArea.removeFromLeft (jmax (50, transportArea.getWidth() / 5)));
|
||||||
|
|
||||||
|
auto controlWidth = transportArea.getWidth() / 3;
|
||||||
|
|
||||||
|
currentPositionLabel.setBounds (positionArea.removeFromRight (jmax (150, controlWidth)));
|
||||||
|
positionSlider.setBounds (positionArea);
|
||||||
|
|
||||||
|
seekToStartButton.setBounds (transportArea.removeFromLeft (controlWidth));
|
||||||
|
playButton .setBounds (transportArea.removeFromLeft (controlWidth));
|
||||||
|
unloadButton .setBounds (transportArea.removeFromLeft (controlWidth));
|
||||||
|
pauseButton.setBounds (playButton.getBounds());
|
||||||
|
|
||||||
|
area.removeFromTop (marginSize);
|
||||||
|
area.removeFromBottom (marginSize);
|
||||||
|
|
||||||
|
videoCompWithNativeControls.setBounds (area);
|
||||||
|
videoCompNoNativeControls.setBounds (area);
|
||||||
|
|
||||||
|
if (positionSlider.getWidth() > 0)
|
||||||
|
positionSlider.setMouseDragSensitivity (positionSlider.getWidth());
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
TextButton loadLocalButton { "Load Local" };
|
||||||
|
TextButton loadUrlButton { "Load URL" };
|
||||||
|
Label volumeLabel { "volumeLabel", "Vol:" };
|
||||||
|
Slider volumeSlider { Slider::LinearHorizontal, Slider::NoTextBox };
|
||||||
|
|
||||||
|
VideoComponent videoCompWithNativeControls;
|
||||||
|
VideoComponent videoCompNoNativeControls;
|
||||||
|
#if JUCE_IOS || JUCE_MAC
|
||||||
|
VideoComponent* curVideoComp = &videoCompWithNativeControls;
|
||||||
|
#else
|
||||||
|
VideoComponent* curVideoComp = &videoCompNoNativeControls;
|
||||||
|
#endif
|
||||||
|
bool isFirstSetup = true;
|
||||||
|
|
||||||
|
Slider positionSlider { Slider::LinearHorizontal, Slider::NoTextBox };
|
||||||
|
bool positionSliderDragging = false;
|
||||||
|
bool wasPlayingBeforeDragStart = false;
|
||||||
|
|
||||||
|
Label currentPositionLabel { "currentPositionLabel", "-:- / -:-" };
|
||||||
|
|
||||||
|
ComboBox playSpeedComboBox { "playSpeedComboBox" };
|
||||||
|
TextButton seekToStartButton { "|<" };
|
||||||
|
TextButton playButton { "Play" };
|
||||||
|
TextButton pauseButton { "Pause" };
|
||||||
|
TextButton unloadButton { "Unload" };
|
||||||
|
|
||||||
|
std::unique_ptr<FileChooser> fileChooser;
|
||||||
|
|
||||||
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoDemo)
|
||||||
|
JUCE_DECLARE_WEAK_REFERENCEABLE (VideoDemo)
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void setPortraitOrientationEnabled (bool shouldBeEnabled)
|
||||||
|
{
|
||||||
|
auto allowedOrientations = Desktop::getInstance().getOrientationsEnabled();
|
||||||
|
|
||||||
|
if (shouldBeEnabled)
|
||||||
|
allowedOrientations |= Desktop::upright;
|
||||||
|
else
|
||||||
|
allowedOrientations &= ~Desktop::upright;
|
||||||
|
|
||||||
|
Desktop::getInstance().setOrientationsEnabled (allowedOrientations);
|
||||||
|
}
|
||||||
|
|
||||||
|
void setTransportControlsEnabled (bool shouldBeEnabled)
|
||||||
|
{
|
||||||
|
positionSlider .setEnabled (shouldBeEnabled);
|
||||||
|
playSpeedComboBox.setEnabled (shouldBeEnabled);
|
||||||
|
seekToStartButton.setEnabled (shouldBeEnabled);
|
||||||
|
playButton .setEnabled (shouldBeEnabled);
|
||||||
|
unloadButton .setEnabled (shouldBeEnabled);
|
||||||
|
pauseButton .setEnabled (shouldBeEnabled);
|
||||||
|
}
|
||||||
|
|
||||||
|
void selectVideoFile()
|
||||||
|
{
|
||||||
|
fileChooser.reset (new FileChooser ("Choose a video file to open...", File::getCurrentWorkingDirectory(),
|
||||||
|
"*", true));
|
||||||
|
|
||||||
|
fileChooser->launchAsync (FileBrowserComponent::openMode | FileBrowserComponent::canSelectFiles,
|
||||||
|
[this] (const FileChooser& chooser)
|
||||||
|
{
|
||||||
|
auto results = chooser.getURLResults();
|
||||||
|
|
||||||
|
if (results.size() > 0)
|
||||||
|
loadVideo (results[0]);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadVideo (const URL& url)
|
||||||
|
{
|
||||||
|
unloadVideoFile();
|
||||||
|
|
||||||
|
#if JUCE_IOS || JUCE_MAC
|
||||||
|
askIfUseNativeControls (url);
|
||||||
|
#else
|
||||||
|
loadUrl (url);
|
||||||
|
setupVideoComp (false);
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void askIfUseNativeControls (const URL& url)
|
||||||
|
{
|
||||||
|
auto* aw = new AlertWindow ("Choose viewer type", {}, AlertWindow::NoIcon);
|
||||||
|
|
||||||
|
aw->addButton ("Yes", 1, KeyPress (KeyPress::returnKey));
|
||||||
|
aw->addButton ("No", 0, KeyPress (KeyPress::escapeKey));
|
||||||
|
aw->addTextBlock ("Do you want to use the viewer with native controls?");
|
||||||
|
|
||||||
|
auto callback = ModalCallbackFunction::forComponent (videoViewerTypeChosen, this, url);
|
||||||
|
aw->enterModalState (true, callback, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void videoViewerTypeChosen (int result, VideoDemo* owner, URL url)
|
||||||
|
{
|
||||||
|
if (owner != nullptr)
|
||||||
|
{
|
||||||
|
owner->setupVideoComp (result != 0);
|
||||||
|
owner->loadUrl (url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void setupVideoComp (bool useNativeViewerWithNativeControls)
|
||||||
|
{
|
||||||
|
auto* oldVideoComp = curVideoComp;
|
||||||
|
|
||||||
|
if (useNativeViewerWithNativeControls)
|
||||||
|
curVideoComp = &videoCompWithNativeControls;
|
||||||
|
else
|
||||||
|
curVideoComp = &videoCompNoNativeControls;
|
||||||
|
|
||||||
|
if (isFirstSetup || oldVideoComp != curVideoComp)
|
||||||
|
{
|
||||||
|
oldVideoComp->onPlaybackStarted = nullptr;
|
||||||
|
oldVideoComp->onPlaybackStopped = nullptr;
|
||||||
|
oldVideoComp->onErrorOccurred = nullptr;
|
||||||
|
oldVideoComp->setVisible (false);
|
||||||
|
|
||||||
|
curVideoComp->onPlaybackStarted = [this]() { processPlaybackStarted(); };
|
||||||
|
curVideoComp->onPlaybackStopped = [this]() { processPlaybackPaused(); };
|
||||||
|
curVideoComp->onErrorOccurred = [this](const String& errorMessage) { errorOccurred (errorMessage); };
|
||||||
|
curVideoComp->setVisible (true);
|
||||||
|
|
||||||
|
#if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
oldVideoComp->onGlobalMediaVolumeChanged = nullptr;
|
||||||
|
curVideoComp->onGlobalMediaVolumeChanged = [this]() { volumeSlider.setValue (curVideoComp->getAudioVolume(), dontSendNotification); };
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
isFirstSetup = false;
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadUrl (const URL& url)
|
||||||
|
{
|
||||||
|
curVideoComp->loadAsync (url, [this] (const URL& u, Result r) { videoLoadingFinished (u, r); });
|
||||||
|
}
|
||||||
|
|
||||||
|
void showVideoUrlPrompt()
|
||||||
|
{
|
||||||
|
auto* aw = new AlertWindow ("Enter URL for video to load", {}, AlertWindow::NoIcon);
|
||||||
|
|
||||||
|
aw->addButton ("OK", 1, KeyPress (KeyPress::returnKey));
|
||||||
|
aw->addButton ("Cancel", 0, KeyPress (KeyPress::escapeKey));
|
||||||
|
aw->addTextEditor ("videoUrlTextEditor", "https://www.rmp-streaming.com/media/bbb-360p.mp4");
|
||||||
|
|
||||||
|
auto callback = ModalCallbackFunction::forComponent (videoUrlPromptClosed, this, Component::SafePointer<AlertWindow> (aw));
|
||||||
|
aw->enterModalState (true, callback, true);
|
||||||
|
}
|
||||||
|
|
||||||
|
static void videoUrlPromptClosed (int result, VideoDemo* owner, Component::SafePointer<AlertWindow> aw)
|
||||||
|
{
|
||||||
|
if (result != 0 && owner != nullptr && aw != nullptr)
|
||||||
|
{
|
||||||
|
auto url = aw->getTextEditorContents ("videoUrlTextEditor");
|
||||||
|
|
||||||
|
if (url.isNotEmpty())
|
||||||
|
owner->loadVideo (url);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void videoLoadingFinished (const URL& url, Result result)
|
||||||
|
{
|
||||||
|
ignoreUnused (url);
|
||||||
|
|
||||||
|
if (result.wasOk())
|
||||||
|
{
|
||||||
|
resized(); // update to reflect the video's aspect ratio
|
||||||
|
|
||||||
|
setTransportControlsEnabled (true);
|
||||||
|
|
||||||
|
currentPositionLabel.setText (getPositionString (0.0, curVideoComp->getVideoDuration()), sendNotification);
|
||||||
|
positionSlider.setValue (0.0, dontSendNotification);
|
||||||
|
playSpeedComboBox.setSelectedId (100, dontSendNotification);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
AlertWindow::showMessageBoxAsync (AlertWindow::WarningIcon,
|
||||||
|
"Couldn't load the file!",
|
||||||
|
result.getErrorMessage());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
static String getPositionString (double playPositionSeconds, double durationSeconds)
|
||||||
|
{
|
||||||
|
auto positionMs = static_cast<int> (1000 * playPositionSeconds);
|
||||||
|
int posMinutes = positionMs / 60000;
|
||||||
|
int posSeconds = (positionMs % 60000) / 1000;
|
||||||
|
int posMillis = positionMs % 1000;
|
||||||
|
|
||||||
|
auto totalMs = static_cast<int> (1000 * durationSeconds);
|
||||||
|
int totMinutes = totalMs / 60000;
|
||||||
|
int totSeconds = (totalMs % 60000) / 1000;
|
||||||
|
int totMillis = totalMs % 1000;
|
||||||
|
|
||||||
|
return String::formatted ("%02d:%02d:%03d / %02d:%02d:%03d",
|
||||||
|
posMinutes, posSeconds, posMillis,
|
||||||
|
totMinutes, totSeconds, totMillis);
|
||||||
|
}
|
||||||
|
|
||||||
|
void updatePositionSliderAndLabel()
|
||||||
|
{
|
||||||
|
auto position = curVideoComp->getPlayPosition();
|
||||||
|
auto duration = curVideoComp->getVideoDuration();
|
||||||
|
|
||||||
|
currentPositionLabel.setText (getPositionString (position, duration), sendNotification);
|
||||||
|
|
||||||
|
if (! positionSliderDragging)
|
||||||
|
positionSlider.setValue (duration != 0 ? (position / duration) : 0.0, dontSendNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
void seekVideoToStart()
|
||||||
|
{
|
||||||
|
seekVideoToNormalisedPosition (0.0);
|
||||||
|
}
|
||||||
|
|
||||||
|
void seekVideoToNormalisedPosition (double normalisedPos)
|
||||||
|
{
|
||||||
|
normalisedPos = jlimit (0.0, 1.0, normalisedPos);
|
||||||
|
|
||||||
|
auto duration = curVideoComp->getVideoDuration();
|
||||||
|
auto newPos = jlimit (0.0, duration, duration * normalisedPos);
|
||||||
|
|
||||||
|
curVideoComp->setPlayPosition (newPos);
|
||||||
|
currentPositionLabel.setText (getPositionString (newPos, curVideoComp->getVideoDuration()), sendNotification);
|
||||||
|
positionSlider.setValue (normalisedPos, dontSendNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
void playVideo()
|
||||||
|
{
|
||||||
|
curVideoComp->play();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processPlaybackStarted()
|
||||||
|
{
|
||||||
|
playButton.setVisible (false);
|
||||||
|
pauseButton.setVisible (true);
|
||||||
|
|
||||||
|
startTimer (20);
|
||||||
|
}
|
||||||
|
|
||||||
|
void pauseVideo()
|
||||||
|
{
|
||||||
|
curVideoComp->stop();
|
||||||
|
}
|
||||||
|
|
||||||
|
void processPlaybackPaused()
|
||||||
|
{
|
||||||
|
// On seeking to a new pos, the playback may be temporarily paused.
|
||||||
|
if (positionSliderDragging)
|
||||||
|
return;
|
||||||
|
|
||||||
|
pauseButton.setVisible (false);
|
||||||
|
playButton.setVisible (true);
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorOccurred (const String& errorMessage)
|
||||||
|
{
|
||||||
|
AlertWindow::showMessageBoxAsync (AlertWindow::InfoIcon,
|
||||||
|
"An error has occurred",
|
||||||
|
errorMessage + ", video will be unloaded.");
|
||||||
|
|
||||||
|
unloadVideoFile();
|
||||||
|
}
|
||||||
|
|
||||||
|
void unloadVideoFile()
|
||||||
|
{
|
||||||
|
curVideoComp->closeVideo();
|
||||||
|
|
||||||
|
setTransportControlsEnabled (false);
|
||||||
|
stopTimer();
|
||||||
|
|
||||||
|
pauseButton.setVisible (false);
|
||||||
|
playButton.setVisible (true);
|
||||||
|
|
||||||
|
currentPositionLabel.setText ("-:- / -:-", sendNotification);
|
||||||
|
positionSlider.setValue (0.0, dontSendNotification);
|
||||||
|
}
|
||||||
|
|
||||||
|
void timerCallback() override
|
||||||
|
{
|
||||||
|
updatePositionSliderAndLabel();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
#endif
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ import android.content.res.Configuration;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.hardware.camera2.*;
|
import android.hardware.camera2.*;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.media.session.*;
|
||||||
|
import android.media.MediaMetadata;
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -94,8 +97,11 @@ public class AudioPerformanceTest extends Activity
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public boolean isPermissionDeclaredInManifest (int permissionID)
|
public boolean isPermissionDeclaredInManifest (int permissionID)
|
||||||
{
|
{
|
||||||
String permissionToCheck = getAndroidPermissionName(permissionID);
|
return isPermissionDeclaredInManifest (getAndroidPermissionName (permissionID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPermissionDeclaredInManifest (String permissionToCheck)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||||
|
|
@ -1997,11 +2003,13 @@ public class AudioPerformanceTest extends Activity
|
||||||
implements SurfaceHolder.Callback
|
implements SurfaceHolder.Callback
|
||||||
{
|
{
|
||||||
private long nativeContext = 0;
|
private long nativeContext = 0;
|
||||||
|
private boolean forVideo;
|
||||||
|
|
||||||
NativeSurfaceView (Context context, long nativeContextPtr)
|
NativeSurfaceView (Context context, long nativeContextPtr, boolean createdForVideo)
|
||||||
{
|
{
|
||||||
super (context);
|
super (context);
|
||||||
nativeContext = nativeContextPtr;
|
nativeContext = nativeContextPtr;
|
||||||
|
forVideo = createdForVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Surface getNativeSurface()
|
public Surface getNativeSurface()
|
||||||
|
|
@ -2019,38 +2027,51 @@ public class AudioPerformanceTest extends Activity
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
||||||
{
|
{
|
||||||
surfaceChangedNative (nativeContext, holder, format, width, height);
|
if (forVideo)
|
||||||
|
surfaceChangedNativeVideo (nativeContext, holder, format, width, height);
|
||||||
|
else
|
||||||
|
surfaceChangedNative (nativeContext, holder, format, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated (SurfaceHolder holder)
|
public void surfaceCreated (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceCreatedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceCreatedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceCreatedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed (SurfaceHolder holder)
|
public void surfaceDestroyed (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceDestroyedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceDestroyedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceDestroyedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispatchDraw (Canvas canvas)
|
protected void dispatchDraw (Canvas canvas)
|
||||||
{
|
{
|
||||||
super.dispatchDraw (canvas);
|
super.dispatchDraw (canvas);
|
||||||
dispatchDrawNative (nativeContext, canvas);
|
|
||||||
|
if (forVideo)
|
||||||
|
dispatchDrawNativeVideo (nativeContext, canvas);
|
||||||
|
else
|
||||||
|
dispatchDrawNative (nativeContext, canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow ()
|
protected void onAttachedToWindow()
|
||||||
{
|
{
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
getHolder().addCallback (this);
|
getHolder().addCallback (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow ()
|
protected void onDetachedFromWindow()
|
||||||
{
|
{
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
getHolder().removeCallback (this);
|
getHolder().removeCallback (this);
|
||||||
|
|
@ -2062,11 +2083,17 @@ public class AudioPerformanceTest extends Activity
|
||||||
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
||||||
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
||||||
int format, int width, int height);
|
int format, int width, int height);
|
||||||
|
|
||||||
|
private native void dispatchDrawNativeVideo (long nativeContextPtr, Canvas canvas);
|
||||||
|
private native void surfaceCreatedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceDestroyedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceChangedNativeVideo (long nativeContextptr, SurfaceHolder holder,
|
||||||
|
int format, int width, int height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
|
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr, boolean forVideo)
|
||||||
{
|
{
|
||||||
return new NativeSurfaceView (this, nativeSurfacePtr);
|
return new NativeSurfaceView (this, nativeSurfacePtr, forVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -2826,6 +2853,151 @@ public class AudioPerformanceTest extends Activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaControllerCallback extends MediaController.Callback
|
||||||
|
{
|
||||||
|
private native void mediaControllerAudioInfoChanged (long host, MediaController.PlaybackInfo info);
|
||||||
|
private native void mediaControllerMetadataChanged (long host, MediaMetadata metadata);
|
||||||
|
private native void mediaControllerPlaybackStateChanged (long host, PlaybackState state);
|
||||||
|
private native void mediaControllerSessionDestroyed (long host);
|
||||||
|
|
||||||
|
MediaControllerCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioInfoChanged (MediaController.PlaybackInfo info)
|
||||||
|
{
|
||||||
|
mediaControllerAudioInfoChanged (host, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMetadataChanged (MediaMetadata metadata)
|
||||||
|
{
|
||||||
|
mediaControllerMetadataChanged (host, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged (PlaybackState state)
|
||||||
|
{
|
||||||
|
mediaControllerPlaybackStateChanged (host, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueueChanged (List<MediaSession.QueueItem> queue) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionDestroyed()
|
||||||
|
{
|
||||||
|
mediaControllerSessionDestroyed (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaSessionCallback extends MediaSession.Callback
|
||||||
|
{
|
||||||
|
private native void mediaSessionPause (long host);
|
||||||
|
private native void mediaSessionPlay (long host);
|
||||||
|
private native void mediaSessionPlayFromMediaId (long host, String mediaId, Bundle extras);
|
||||||
|
private native void mediaSessionSeekTo (long host, long pos);
|
||||||
|
private native void mediaSessionStop (long host);
|
||||||
|
|
||||||
|
|
||||||
|
MediaSessionCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause()
|
||||||
|
{
|
||||||
|
mediaSessionPause (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay()
|
||||||
|
{
|
||||||
|
mediaSessionPlay (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayFromMediaId (String mediaId, Bundle extras)
|
||||||
|
{
|
||||||
|
mediaSessionPlayFromMediaId (host, mediaId, extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSeekTo (long pos)
|
||||||
|
{
|
||||||
|
mediaSessionSeekTo (host, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop()
|
||||||
|
{
|
||||||
|
mediaSessionStop (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFastForward() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMediaButtonEvent (Intent mediaButtonIntent)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRewind() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToNext() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToPrevious() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToQueueItem (long id) {}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class SystemVolumeObserver extends ContentObserver
|
||||||
|
{
|
||||||
|
private native void mediaSessionSystemVolumeChanged (long host);
|
||||||
|
|
||||||
|
SystemVolumeObserver (Activity activityToUse, long hostToUse)
|
||||||
|
{
|
||||||
|
super (null);
|
||||||
|
|
||||||
|
activity = activityToUse;
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled (boolean shouldBeEnabled)
|
||||||
|
{
|
||||||
|
if (shouldBeEnabled)
|
||||||
|
activity.getApplicationContext().getContentResolver().registerContentObserver (android.provider.Settings.System.CONTENT_URI, true, this);
|
||||||
|
else
|
||||||
|
activity.getApplicationContext().getContentResolver().unregisterContentObserver (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange (boolean selfChange, Uri uri)
|
||||||
|
{
|
||||||
|
if (uri.toString().startsWith ("content://settings/system/volume_music"))
|
||||||
|
mediaSessionSystemVolumeChanged (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Activity activity;
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public static final String getLocaleValue (boolean isRegion)
|
public static final String getLocaleValue (boolean isRegion)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1251,6 +1251,7 @@ add_library( ${BINARY_NAME}
|
||||||
"../../../../../modules/juce_video/capture/juce_CameraDevice.cpp"
|
"../../../../../modules/juce_video/capture/juce_CameraDevice.cpp"
|
||||||
"../../../../../modules/juce_video/capture/juce_CameraDevice.h"
|
"../../../../../modules/juce_video/capture/juce_CameraDevice.h"
|
||||||
"../../../../../modules/juce_video/native/juce_android_CameraDevice.h"
|
"../../../../../modules/juce_video/native/juce_android_CameraDevice.h"
|
||||||
|
"../../../../../modules/juce_video/native/juce_android_Video.h"
|
||||||
"../../../../../modules/juce_video/native/juce_ios_CameraDevice.h"
|
"../../../../../modules/juce_video/native/juce_ios_CameraDevice.h"
|
||||||
"../../../../../modules/juce_video/native/juce_mac_CameraDevice.h"
|
"../../../../../modules/juce_video/native/juce_mac_CameraDevice.h"
|
||||||
"../../../../../modules/juce_video/native/juce_mac_Video.h"
|
"../../../../../modules/juce_video/native/juce_mac_Video.h"
|
||||||
|
|
@ -2491,6 +2492,7 @@ set_source_files_properties("../../../../../modules/juce_opengl/juce_opengl.h" P
|
||||||
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.cpp" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/capture/juce_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_android_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_android_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_android_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_ios_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_CameraDevice.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
set_source_files_properties("../../../../../modules/juce_video/native/juce_mac_Video.h" PROPERTIES HEADER_FILE_ONLY TRUE)
|
||||||
|
|
|
||||||
|
|
@ -31,6 +31,9 @@ import android.content.res.Configuration;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
import android.hardware.camera2.*;
|
import android.hardware.camera2.*;
|
||||||
|
import android.database.ContentObserver;
|
||||||
|
import android.media.session.*;
|
||||||
|
import android.media.MediaMetadata;
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -94,8 +97,11 @@ public class AudioPluginHost extends Activity
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public boolean isPermissionDeclaredInManifest (int permissionID)
|
public boolean isPermissionDeclaredInManifest (int permissionID)
|
||||||
{
|
{
|
||||||
String permissionToCheck = getAndroidPermissionName(permissionID);
|
return isPermissionDeclaredInManifest (getAndroidPermissionName (permissionID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPermissionDeclaredInManifest (String permissionToCheck)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||||
|
|
@ -1997,11 +2003,13 @@ public class AudioPluginHost extends Activity
|
||||||
implements SurfaceHolder.Callback
|
implements SurfaceHolder.Callback
|
||||||
{
|
{
|
||||||
private long nativeContext = 0;
|
private long nativeContext = 0;
|
||||||
|
private boolean forVideo;
|
||||||
|
|
||||||
NativeSurfaceView (Context context, long nativeContextPtr)
|
NativeSurfaceView (Context context, long nativeContextPtr, boolean createdForVideo)
|
||||||
{
|
{
|
||||||
super (context);
|
super (context);
|
||||||
nativeContext = nativeContextPtr;
|
nativeContext = nativeContextPtr;
|
||||||
|
forVideo = createdForVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Surface getNativeSurface()
|
public Surface getNativeSurface()
|
||||||
|
|
@ -2019,38 +2027,51 @@ public class AudioPluginHost extends Activity
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
||||||
{
|
{
|
||||||
surfaceChangedNative (nativeContext, holder, format, width, height);
|
if (forVideo)
|
||||||
|
surfaceChangedNativeVideo (nativeContext, holder, format, width, height);
|
||||||
|
else
|
||||||
|
surfaceChangedNative (nativeContext, holder, format, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated (SurfaceHolder holder)
|
public void surfaceCreated (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceCreatedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceCreatedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceCreatedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed (SurfaceHolder holder)
|
public void surfaceDestroyed (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceDestroyedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceDestroyedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceDestroyedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispatchDraw (Canvas canvas)
|
protected void dispatchDraw (Canvas canvas)
|
||||||
{
|
{
|
||||||
super.dispatchDraw (canvas);
|
super.dispatchDraw (canvas);
|
||||||
dispatchDrawNative (nativeContext, canvas);
|
|
||||||
|
if (forVideo)
|
||||||
|
dispatchDrawNativeVideo (nativeContext, canvas);
|
||||||
|
else
|
||||||
|
dispatchDrawNative (nativeContext, canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow ()
|
protected void onAttachedToWindow()
|
||||||
{
|
{
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
getHolder().addCallback (this);
|
getHolder().addCallback (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow ()
|
protected void onDetachedFromWindow()
|
||||||
{
|
{
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
getHolder().removeCallback (this);
|
getHolder().removeCallback (this);
|
||||||
|
|
@ -2062,11 +2083,17 @@ public class AudioPluginHost extends Activity
|
||||||
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
||||||
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
||||||
int format, int width, int height);
|
int format, int width, int height);
|
||||||
|
|
||||||
|
private native void dispatchDrawNativeVideo (long nativeContextPtr, Canvas canvas);
|
||||||
|
private native void surfaceCreatedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceDestroyedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceChangedNativeVideo (long nativeContextptr, SurfaceHolder holder,
|
||||||
|
int format, int width, int height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
|
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr, boolean forVideo)
|
||||||
{
|
{
|
||||||
return new NativeSurfaceView (this, nativeSurfacePtr);
|
return new NativeSurfaceView (this, nativeSurfacePtr, forVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -2826,6 +2853,151 @@ public class AudioPluginHost extends Activity
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaControllerCallback extends MediaController.Callback
|
||||||
|
{
|
||||||
|
private native void mediaControllerAudioInfoChanged (long host, MediaController.PlaybackInfo info);
|
||||||
|
private native void mediaControllerMetadataChanged (long host, MediaMetadata metadata);
|
||||||
|
private native void mediaControllerPlaybackStateChanged (long host, PlaybackState state);
|
||||||
|
private native void mediaControllerSessionDestroyed (long host);
|
||||||
|
|
||||||
|
MediaControllerCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioInfoChanged (MediaController.PlaybackInfo info)
|
||||||
|
{
|
||||||
|
mediaControllerAudioInfoChanged (host, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMetadataChanged (MediaMetadata metadata)
|
||||||
|
{
|
||||||
|
mediaControllerMetadataChanged (host, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged (PlaybackState state)
|
||||||
|
{
|
||||||
|
mediaControllerPlaybackStateChanged (host, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueueChanged (List<MediaSession.QueueItem> queue) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionDestroyed()
|
||||||
|
{
|
||||||
|
mediaControllerSessionDestroyed (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaSessionCallback extends MediaSession.Callback
|
||||||
|
{
|
||||||
|
private native void mediaSessionPause (long host);
|
||||||
|
private native void mediaSessionPlay (long host);
|
||||||
|
private native void mediaSessionPlayFromMediaId (long host, String mediaId, Bundle extras);
|
||||||
|
private native void mediaSessionSeekTo (long host, long pos);
|
||||||
|
private native void mediaSessionStop (long host);
|
||||||
|
|
||||||
|
|
||||||
|
MediaSessionCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause()
|
||||||
|
{
|
||||||
|
mediaSessionPause (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay()
|
||||||
|
{
|
||||||
|
mediaSessionPlay (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayFromMediaId (String mediaId, Bundle extras)
|
||||||
|
{
|
||||||
|
mediaSessionPlayFromMediaId (host, mediaId, extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSeekTo (long pos)
|
||||||
|
{
|
||||||
|
mediaSessionSeekTo (host, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop()
|
||||||
|
{
|
||||||
|
mediaSessionStop (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFastForward() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMediaButtonEvent (Intent mediaButtonIntent)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRewind() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToNext() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToPrevious() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToQueueItem (long id) {}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class SystemVolumeObserver extends ContentObserver
|
||||||
|
{
|
||||||
|
private native void mediaSessionSystemVolumeChanged (long host);
|
||||||
|
|
||||||
|
SystemVolumeObserver (Activity activityToUse, long hostToUse)
|
||||||
|
{
|
||||||
|
super (null);
|
||||||
|
|
||||||
|
activity = activityToUse;
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled (boolean shouldBeEnabled)
|
||||||
|
{
|
||||||
|
if (shouldBeEnabled)
|
||||||
|
activity.getApplicationContext().getContentResolver().registerContentObserver (android.provider.Settings.System.CONTENT_URI, true, this);
|
||||||
|
else
|
||||||
|
activity.getApplicationContext().getContentResolver().unregisterContentObserver (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange (boolean selfChange, Uri uri)
|
||||||
|
{
|
||||||
|
if (uri.toString().startsWith ("content://settings/system/volume_music"))
|
||||||
|
mediaSessionSystemVolumeChanged (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Activity activity;
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public static final String getLocaleValue (boolean isRegion)
|
public static final String getLocaleValue (boolean isRegion)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -2436,6 +2436,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4065,6 +4065,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -2436,6 +2436,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4065,6 +4065,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -2436,6 +2436,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4065,6 +4065,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -264,6 +264,10 @@
|
||||||
#ifndef JUCE_USE_CAMERA
|
#ifndef JUCE_USE_CAMERA
|
||||||
#define JUCE_USE_CAMERA 0
|
#define JUCE_USE_CAMERA 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
//#define JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME 1
|
||||||
|
#endif
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#ifndef JUCE_STANDALONE_APPLICATION
|
#ifndef JUCE_STANDALONE_APPLICATION
|
||||||
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
||||||
|
|
|
||||||
|
|
@ -87,8 +87,11 @@ public class JUCENetworkGraphicsDemo extends Activity
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public boolean isPermissionDeclaredInManifest (int permissionID)
|
public boolean isPermissionDeclaredInManifest (int permissionID)
|
||||||
{
|
{
|
||||||
String permissionToCheck = getAndroidPermissionName(permissionID);
|
return isPermissionDeclaredInManifest (getAndroidPermissionName (permissionID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPermissionDeclaredInManifest (String permissionToCheck)
|
||||||
|
{
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||||
|
|
@ -1064,11 +1067,13 @@ public class JUCENetworkGraphicsDemo extends Activity
|
||||||
implements SurfaceHolder.Callback
|
implements SurfaceHolder.Callback
|
||||||
{
|
{
|
||||||
private long nativeContext = 0;
|
private long nativeContext = 0;
|
||||||
|
private boolean forVideo;
|
||||||
|
|
||||||
NativeSurfaceView (Context context, long nativeContextPtr)
|
NativeSurfaceView (Context context, long nativeContextPtr, boolean createdForVideo)
|
||||||
{
|
{
|
||||||
super (context);
|
super (context);
|
||||||
nativeContext = nativeContextPtr;
|
nativeContext = nativeContextPtr;
|
||||||
|
forVideo = createdForVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Surface getNativeSurface()
|
public Surface getNativeSurface()
|
||||||
|
|
@ -1086,38 +1091,51 @@ public class JUCENetworkGraphicsDemo extends Activity
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
||||||
{
|
{
|
||||||
surfaceChangedNative (nativeContext, holder, format, width, height);
|
if (forVideo)
|
||||||
|
surfaceChangedNativeVideo (nativeContext, holder, format, width, height);
|
||||||
|
else
|
||||||
|
surfaceChangedNative (nativeContext, holder, format, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated (SurfaceHolder holder)
|
public void surfaceCreated (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceCreatedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceCreatedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceCreatedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed (SurfaceHolder holder)
|
public void surfaceDestroyed (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceDestroyedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceDestroyedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceDestroyedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispatchDraw (Canvas canvas)
|
protected void dispatchDraw (Canvas canvas)
|
||||||
{
|
{
|
||||||
super.dispatchDraw (canvas);
|
super.dispatchDraw (canvas);
|
||||||
dispatchDrawNative (nativeContext, canvas);
|
|
||||||
|
if (forVideo)
|
||||||
|
dispatchDrawNativeVideo (nativeContext, canvas);
|
||||||
|
else
|
||||||
|
dispatchDrawNative (nativeContext, canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow ()
|
protected void onAttachedToWindow()
|
||||||
{
|
{
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
getHolder().addCallback (this);
|
getHolder().addCallback (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow ()
|
protected void onDetachedFromWindow()
|
||||||
{
|
{
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
getHolder().removeCallback (this);
|
getHolder().removeCallback (this);
|
||||||
|
|
@ -1129,11 +1147,17 @@ public class JUCENetworkGraphicsDemo extends Activity
|
||||||
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
||||||
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
||||||
int format, int width, int height);
|
int format, int width, int height);
|
||||||
|
|
||||||
|
private native void dispatchDrawNativeVideo (long nativeContextPtr, Canvas canvas);
|
||||||
|
private native void surfaceCreatedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceDestroyedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceChangedNativeVideo (long nativeContextptr, SurfaceHolder holder,
|
||||||
|
int format, int width, int height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
|
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr, boolean forVideo)
|
||||||
{
|
{
|
||||||
return new NativeSurfaceView (this, nativeSurfacePtr);
|
return new NativeSurfaceView (this, nativeSurfacePtr, forVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
|
||||||
|
|
@ -1050,6 +1050,7 @@ private:
|
||||||
auto midiCode = getMidiCode (javaSourceFolder, className);
|
auto midiCode = getMidiCode (javaSourceFolder, className);
|
||||||
auto webViewCode = getWebViewCode (javaSourceFolder);
|
auto webViewCode = getWebViewCode (javaSourceFolder);
|
||||||
auto cameraCode = getCameraCode (javaSourceFolder);
|
auto cameraCode = getCameraCode (javaSourceFolder);
|
||||||
|
auto videoCode = getVideoCode (javaSourceFolder);
|
||||||
|
|
||||||
auto javaSourceFile = javaSourceFolder.getChildFile ("JuceAppActivity.java");
|
auto javaSourceFile = javaSourceFolder.getChildFile ("JuceAppActivity.java");
|
||||||
auto javaSourceLines = StringArray::fromLines (javaSourceFile.loadFileAsString());
|
auto javaSourceLines = StringArray::fromLines (javaSourceFile.loadFileAsString());
|
||||||
|
|
@ -1075,6 +1076,10 @@ private:
|
||||||
newFile << cameraCode.imports;
|
newFile << cameraCode.imports;
|
||||||
else if (line.contains ("$$JuceAndroidCameraCode$$"))
|
else if (line.contains ("$$JuceAndroidCameraCode$$"))
|
||||||
newFile << cameraCode.main;
|
newFile << cameraCode.main;
|
||||||
|
else if (line.contains ("$$JuceAndroidVideoImports$$"))
|
||||||
|
newFile << videoCode.imports;
|
||||||
|
else if (line.contains ("$$JuceAndroidVideoCode$$"))
|
||||||
|
newFile << videoCode.main;
|
||||||
else
|
else
|
||||||
newFile << line.replace ("$$JuceAppActivityBaseClass$$", androidActivityBaseClassName.get().toString())
|
newFile << line.replace ("$$JuceAppActivityBaseClass$$", androidActivityBaseClassName.get().toString())
|
||||||
.replace ("JuceAppActivity", className)
|
.replace ("JuceAppActivity", className)
|
||||||
|
|
@ -1203,13 +1208,12 @@ private:
|
||||||
String juceCameraImports, juceCameraCode;
|
String juceCameraImports, juceCameraCode;
|
||||||
|
|
||||||
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
|
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
|
||||||
|
{
|
||||||
juceCameraImports << "import android.hardware.camera2.*;" << newLine;
|
juceCameraImports << "import android.hardware.camera2.*;" << newLine;
|
||||||
|
|
||||||
auto javaCameraFile = javaSourceFolder.getChildFile ("AndroidCamera.java");
|
auto javaCameraFile = javaSourceFolder.getChildFile ("AndroidCamera.java");
|
||||||
auto juceCameraCodeAll = javaCameraFile.loadFileAsString();
|
auto juceCameraCodeAll = javaCameraFile.loadFileAsString();
|
||||||
|
|
||||||
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
|
|
||||||
{
|
|
||||||
juceCameraCode << juceCameraCodeAll.fromFirstOccurrenceOf ("$$CameraApi21", false, false)
|
juceCameraCode << juceCameraCodeAll.fromFirstOccurrenceOf ("$$CameraApi21", false, false)
|
||||||
.upToFirstOccurrenceOf ("CameraApi21$$", false, false);
|
.upToFirstOccurrenceOf ("CameraApi21$$", false, false);
|
||||||
}
|
}
|
||||||
|
|
@ -1217,6 +1221,32 @@ private:
|
||||||
return { juceCameraImports, juceCameraCode };
|
return { juceCameraImports, juceCameraCode };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct VideoCode
|
||||||
|
{
|
||||||
|
String imports;
|
||||||
|
String main;
|
||||||
|
};
|
||||||
|
|
||||||
|
VideoCode getVideoCode (const File& javaSourceFolder) const
|
||||||
|
{
|
||||||
|
String juceVideoImports, juceVideoCode;
|
||||||
|
|
||||||
|
if (static_cast<int> (androidMinimumSDK.get()) >= 21)
|
||||||
|
{
|
||||||
|
juceVideoImports << "import android.database.ContentObserver;" << newLine;
|
||||||
|
juceVideoImports << "import android.media.session.*;" << newLine;
|
||||||
|
juceVideoImports << "import android.media.MediaMetadata;" << newLine;
|
||||||
|
|
||||||
|
auto javaVideoFile = javaSourceFolder.getChildFile ("AndroidVideo.java");
|
||||||
|
auto juceVideoCodeAll = javaVideoFile.loadFileAsString();
|
||||||
|
|
||||||
|
juceVideoCode << juceVideoCodeAll.fromFirstOccurrenceOf ("$$VideoApi21", false, false)
|
||||||
|
.upToFirstOccurrenceOf ("VideoApi21$$", false, false);
|
||||||
|
}
|
||||||
|
|
||||||
|
return { juceVideoImports, juceVideoCode };
|
||||||
|
}
|
||||||
|
|
||||||
void copyAdditionalJavaFiles (const File& sourceFolder, const File& targetFolder) const
|
void copyAdditionalJavaFiles (const File& sourceFolder, const File& targetFolder) const
|
||||||
{
|
{
|
||||||
auto inAppBillingJavaFileName = String ("IInAppBillingService.java");
|
auto inAppBillingJavaFileName = String ("IInAppBillingService.java");
|
||||||
|
|
|
||||||
|
|
@ -2642,6 +2642,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_product_unlocking\juce_product_unlocking.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4473,6 +4473,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -290,6 +290,10 @@
|
||||||
#ifndef JUCE_USE_CAMERA
|
#ifndef JUCE_USE_CAMERA
|
||||||
//#define JUCE_USE_CAMERA 0
|
//#define JUCE_USE_CAMERA 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
//#define JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME 1
|
||||||
|
#endif
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#ifndef JUCE_STANDALONE_APPLICATION
|
#ifndef JUCE_STANDALONE_APPLICATION
|
||||||
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
||||||
|
|
|
||||||
|
|
@ -2423,6 +2423,7 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_opengl\juce_opengl.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\capture\juce_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h"/>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_CameraDevice.h"/>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_mac_Video.h"/>
|
||||||
|
|
|
||||||
|
|
@ -4014,6 +4014,9 @@
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_android_Video.h">
|
||||||
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
|
</ClInclude>
|
||||||
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
<ClInclude Include="..\..\..\..\modules\juce_video\native\juce_ios_CameraDevice.h">
|
||||||
<Filter>JUCE Modules\juce_video\native</Filter>
|
<Filter>JUCE Modules\juce_video\native</Filter>
|
||||||
</ClInclude>
|
</ClInclude>
|
||||||
|
|
|
||||||
|
|
@ -262,6 +262,10 @@
|
||||||
#ifndef JUCE_USE_CAMERA
|
#ifndef JUCE_USE_CAMERA
|
||||||
//#define JUCE_USE_CAMERA 0
|
//#define JUCE_USE_CAMERA 0
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
#ifndef JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
//#define JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME 1
|
||||||
|
#endif
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#ifndef JUCE_STANDALONE_APPLICATION
|
#ifndef JUCE_STANDALONE_APPLICATION
|
||||||
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
#if defined(JucePlugin_Name) && defined(JucePlugin_Build_Standalone)
|
||||||
|
|
|
||||||
146
modules/juce_core/native/java/AndroidVideo.java
Normal file
146
modules/juce_core/native/java/AndroidVideo.java
Normal file
|
|
@ -0,0 +1,146 @@
|
||||||
|
$$VideoApi21
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaControllerCallback extends MediaController.Callback
|
||||||
|
{
|
||||||
|
private native void mediaControllerAudioInfoChanged (long host, MediaController.PlaybackInfo info);
|
||||||
|
private native void mediaControllerMetadataChanged (long host, MediaMetadata metadata);
|
||||||
|
private native void mediaControllerPlaybackStateChanged (long host, PlaybackState state);
|
||||||
|
private native void mediaControllerSessionDestroyed (long host);
|
||||||
|
|
||||||
|
MediaControllerCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onAudioInfoChanged (MediaController.PlaybackInfo info)
|
||||||
|
{
|
||||||
|
mediaControllerAudioInfoChanged (host, info);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onMetadataChanged (MediaMetadata metadata)
|
||||||
|
{
|
||||||
|
mediaControllerMetadataChanged (host, metadata);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlaybackStateChanged (PlaybackState state)
|
||||||
|
{
|
||||||
|
mediaControllerPlaybackStateChanged (host, state);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onQueueChanged (List<MediaSession.QueueItem> queue) {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSessionDestroyed()
|
||||||
|
{
|
||||||
|
mediaControllerSessionDestroyed (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class MediaSessionCallback extends MediaSession.Callback
|
||||||
|
{
|
||||||
|
private native void mediaSessionPause (long host);
|
||||||
|
private native void mediaSessionPlay (long host);
|
||||||
|
private native void mediaSessionPlayFromMediaId (long host, String mediaId, Bundle extras);
|
||||||
|
private native void mediaSessionSeekTo (long host, long pos);
|
||||||
|
private native void mediaSessionStop (long host);
|
||||||
|
|
||||||
|
|
||||||
|
MediaSessionCallback (long hostToUse)
|
||||||
|
{
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPause()
|
||||||
|
{
|
||||||
|
mediaSessionPause (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlay()
|
||||||
|
{
|
||||||
|
mediaSessionPlay (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onPlayFromMediaId (String mediaId, Bundle extras)
|
||||||
|
{
|
||||||
|
mediaSessionPlayFromMediaId (host, mediaId, extras);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSeekTo (long pos)
|
||||||
|
{
|
||||||
|
mediaSessionSeekTo (host, pos);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onStop()
|
||||||
|
{
|
||||||
|
mediaSessionStop (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onFastForward() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public boolean onMediaButtonEvent (Intent mediaButtonIntent)
|
||||||
|
{
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onRewind() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToNext() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToPrevious() {}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onSkipToQueueItem (long id) {}
|
||||||
|
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
public class SystemVolumeObserver extends ContentObserver
|
||||||
|
{
|
||||||
|
private native void mediaSessionSystemVolumeChanged (long host);
|
||||||
|
|
||||||
|
SystemVolumeObserver (Activity activityToUse, long hostToUse)
|
||||||
|
{
|
||||||
|
super (null);
|
||||||
|
|
||||||
|
activity = activityToUse;
|
||||||
|
host = hostToUse;
|
||||||
|
}
|
||||||
|
|
||||||
|
void setEnabled (boolean shouldBeEnabled)
|
||||||
|
{
|
||||||
|
if (shouldBeEnabled)
|
||||||
|
activity.getApplicationContext().getContentResolver().registerContentObserver (android.provider.Settings.System.CONTENT_URI, true, this);
|
||||||
|
else
|
||||||
|
activity.getApplicationContext().getContentResolver().unregisterContentObserver (this);
|
||||||
|
}
|
||||||
|
|
||||||
|
@Override
|
||||||
|
public void onChange (boolean selfChange, Uri uri)
|
||||||
|
{
|
||||||
|
if (uri.toString().startsWith ("content://settings/system/volume_music"))
|
||||||
|
mediaSessionSystemVolumeChanged (host);
|
||||||
|
}
|
||||||
|
|
||||||
|
private Activity activity;
|
||||||
|
private long host;
|
||||||
|
}
|
||||||
|
|
||||||
|
VideoApi21$$
|
||||||
|
|
@ -30,7 +30,8 @@ import android.content.Intent;
|
||||||
import android.content.res.Configuration;
|
import android.content.res.Configuration;
|
||||||
import android.content.pm.PackageInfo;
|
import android.content.pm.PackageInfo;
|
||||||
import android.content.pm.PackageManager;
|
import android.content.pm.PackageManager;
|
||||||
$$JuceAndroidCameraImports$$ // If you get an error here, you need to re-save your project with the Projucer!
|
$$JuceAndroidCameraImports$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||||
|
$$JuceAndroidVideoImports$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||||
import android.net.http.SslError;
|
import android.net.http.SslError;
|
||||||
import android.net.Uri;
|
import android.net.Uri;
|
||||||
import android.os.Bundle;
|
import android.os.Bundle;
|
||||||
|
|
@ -87,10 +88,13 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public boolean isPermissionDeclaredInManifest (int permissionID)
|
public boolean isPermissionDeclaredInManifest (int permissionID)
|
||||||
|
{
|
||||||
|
return isPermissionDeclaredInManifest (getAndroidPermissionName (permissionID));
|
||||||
|
}
|
||||||
|
|
||||||
|
public boolean isPermissionDeclaredInManifest (String permissionToCheck)
|
||||||
{
|
{
|
||||||
String permissionToCheck = getAndroidPermissionName(permissionID);
|
|
||||||
|
|
||||||
try
|
try
|
||||||
{
|
{
|
||||||
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
PackageInfo info = getPackageManager().getPackageInfo(getApplicationContext().getPackageName(), PackageManager.GET_PERMISSIONS);
|
||||||
|
|
@ -982,12 +986,14 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$
|
||||||
public static class NativeSurfaceView extends SurfaceView
|
public static class NativeSurfaceView extends SurfaceView
|
||||||
implements SurfaceHolder.Callback
|
implements SurfaceHolder.Callback
|
||||||
{
|
{
|
||||||
private long nativeContext = 0;
|
private long nativeContext = 0;
|
||||||
|
private boolean forVideo;
|
||||||
|
|
||||||
NativeSurfaceView (Context context, long nativeContextPtr)
|
NativeSurfaceView (Context context, long nativeContextPtr, boolean createdForVideo)
|
||||||
{
|
{
|
||||||
super (context);
|
super (context);
|
||||||
nativeContext = nativeContextPtr;
|
nativeContext = nativeContextPtr;
|
||||||
|
forVideo = createdForVideo;
|
||||||
}
|
}
|
||||||
|
|
||||||
public Surface getNativeSurface()
|
public Surface getNativeSurface()
|
||||||
|
|
@ -1004,39 +1010,52 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@Override
|
@Override
|
||||||
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
public void surfaceChanged (SurfaceHolder holder, int format, int width, int height)
|
||||||
{
|
{
|
||||||
surfaceChangedNative (nativeContext, holder, format, width, height);
|
if (forVideo)
|
||||||
|
surfaceChangedNativeVideo (nativeContext, holder, format, width, height);
|
||||||
|
else
|
||||||
|
surfaceChangedNative (nativeContext, holder, format, width, height);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceCreated (SurfaceHolder holder)
|
public void surfaceCreated (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceCreatedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceCreatedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceCreatedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void surfaceDestroyed (SurfaceHolder holder)
|
public void surfaceDestroyed (SurfaceHolder holder)
|
||||||
{
|
{
|
||||||
surfaceDestroyedNative (nativeContext, holder);
|
if (forVideo)
|
||||||
|
surfaceDestroyedNativeVideo (nativeContext, holder);
|
||||||
|
else
|
||||||
|
surfaceDestroyedNative (nativeContext, holder);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void dispatchDraw (Canvas canvas)
|
protected void dispatchDraw (Canvas canvas)
|
||||||
{
|
{
|
||||||
super.dispatchDraw (canvas);
|
super.dispatchDraw (canvas);
|
||||||
dispatchDrawNative (nativeContext, canvas);
|
|
||||||
|
if (forVideo)
|
||||||
|
dispatchDrawNativeVideo (nativeContext, canvas);
|
||||||
|
else
|
||||||
|
dispatchDrawNative (nativeContext, canvas);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
@Override
|
@Override
|
||||||
protected void onAttachedToWindow ()
|
protected void onAttachedToWindow()
|
||||||
{
|
{
|
||||||
super.onAttachedToWindow();
|
super.onAttachedToWindow();
|
||||||
getHolder().addCallback (this);
|
getHolder().addCallback (this);
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onDetachedFromWindow ()
|
protected void onDetachedFromWindow()
|
||||||
{
|
{
|
||||||
super.onDetachedFromWindow();
|
super.onDetachedFromWindow();
|
||||||
getHolder().removeCallback (this);
|
getHolder().removeCallback (this);
|
||||||
|
|
@ -1047,12 +1066,18 @@ public class JuceAppActivity extends $$JuceAppActivityBaseClass$$
|
||||||
private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
|
private native void surfaceCreatedNative (long nativeContextptr, SurfaceHolder holder);
|
||||||
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
private native void surfaceDestroyedNative (long nativeContextptr, SurfaceHolder holder);
|
||||||
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
private native void surfaceChangedNative (long nativeContextptr, SurfaceHolder holder,
|
||||||
int format, int width, int height);
|
int format, int width, int height);
|
||||||
|
|
||||||
|
private native void dispatchDrawNativeVideo (long nativeContextPtr, Canvas canvas);
|
||||||
|
private native void surfaceCreatedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceDestroyedNativeVideo (long nativeContextptr, SurfaceHolder holder);
|
||||||
|
private native void surfaceChangedNativeVideo (long nativeContextptr, SurfaceHolder holder,
|
||||||
|
int format, int width, int height);
|
||||||
}
|
}
|
||||||
|
|
||||||
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr)
|
public NativeSurfaceView createNativeSurfaceView (long nativeSurfacePtr, boolean forVideo)
|
||||||
{
|
{
|
||||||
return new NativeSurfaceView (this, nativeSurfacePtr);
|
return new NativeSurfaceView (this, nativeSurfacePtr, forVideo);
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -1610,7 +1635,8 @@ $$JuceAndroidWebViewNativeCode$$ // If you get an error here, you need to re-sav
|
||||||
private final Object hostLock = new Object();
|
private final Object hostLock = new Object();
|
||||||
}
|
}
|
||||||
|
|
||||||
$$JuceAndroidCameraCode$$ // If you get an error here, you need to re-save your project with the Projucer!
|
$$JuceAndroidCameraCode$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||||
|
$$JuceAndroidVideoCode$$ // If you get an error here, you need to re-save your project with the Projucer!
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
public static final String getLocaleValue (boolean isRegion)
|
public static final String getLocaleValue (boolean isRegion)
|
||||||
|
|
|
||||||
|
|
@ -188,6 +188,13 @@ private:
|
||||||
uri.get(), projection.get(), jSelection.get(),
|
uri.get(), projection.get(), jSelection.get(),
|
||||||
args.get(), nullptr));
|
args.get(), nullptr));
|
||||||
|
|
||||||
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
|
{
|
||||||
|
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (cursor)
|
if (cursor)
|
||||||
{
|
{
|
||||||
if (env->CallBooleanMethod (cursor.get(), AndroidCursor.moveToFirst) != 0)
|
if (env->CallBooleanMethod (cursor.get(), AndroidCursor.moveToFirst) != 0)
|
||||||
|
|
@ -380,6 +387,13 @@ private:
|
||||||
uri.get(), projection.get(), nullptr,
|
uri.get(), projection.get(), nullptr,
|
||||||
nullptr, nullptr));
|
nullptr, nullptr));
|
||||||
|
|
||||||
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
|
{
|
||||||
|
// An exception has occurred, have you acquired RuntimePermission::readExternalStorage permission?
|
||||||
|
jassertfalse;
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
if (cursor == 0)
|
if (cursor == 0)
|
||||||
return {};
|
return {};
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -250,60 +250,79 @@ extern AndroidSystem android;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||||
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
|
METHOD (createNewView, "createNewView", "(ZJ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;") \
|
||||||
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
|
METHOD (deleteView, "deleteView", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$ComponentPeerView;)V") \
|
||||||
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(J)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
|
METHOD (createNativeSurfaceView, "createNativeSurfaceView", "(JZ)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$NativeSurfaceView;") \
|
||||||
METHOD (finish, "finish", "()V") \
|
METHOD (finish, "finish", "()V") \
|
||||||
METHOD (getWindowManager, "getWindowManager", "()Landroid/view/WindowManager;") \
|
METHOD (getWindowManager, "getWindowManager", "()Landroid/view/WindowManager;") \
|
||||||
METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \
|
METHOD (setRequestedOrientation, "setRequestedOrientation", "(I)V") \
|
||||||
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
|
METHOD (getClipboardContent, "getClipboardContent", "()Ljava/lang/String;") \
|
||||||
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
|
METHOD (setClipboardContent, "setClipboardContent", "(Ljava/lang/String;)V") \
|
||||||
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
|
METHOD (excludeClipRegion, "excludeClipRegion", "(Landroid/graphics/Canvas;FFFF)V") \
|
||||||
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
|
METHOD (renderGlyph, "renderGlyph", "(CCLandroid/graphics/Paint;Landroid/graphics/Matrix;Landroid/graphics/Rect;)[I") \
|
||||||
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
|
STATICMETHOD (createHTTPStream, "createHTTPStream", "(Ljava/lang/String;Z[BLjava/lang/String;I[ILjava/lang/StringBuffer;ILjava/lang/String;)L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$HTTPStream;") \
|
||||||
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
|
METHOD (launchURL, "launchURL", "(Ljava/lang/String;)V") \
|
||||||
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
|
METHOD (showMessageBox, "showMessageBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
|
||||||
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \
|
METHOD (showOkCancelBox, "showOkCancelBox", "(Ljava/lang/String;Ljava/lang/String;JLjava/lang/String;Ljava/lang/String;)V") \
|
||||||
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
|
METHOD (showYesNoCancelBox, "showYesNoCancelBox", "(Ljava/lang/String;Ljava/lang/String;J)V") \
|
||||||
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
|
STATICMETHOD (getLocaleValue, "getLocaleValue", "(Z)Ljava/lang/String;") \
|
||||||
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
|
STATICMETHOD (getDocumentsFolder, "getDocumentsFolder", "()Ljava/lang/String;") \
|
||||||
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
|
STATICMETHOD (getPicturesFolder, "getPicturesFolder", "()Ljava/lang/String;") \
|
||||||
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
|
STATICMETHOD (getMusicFolder, "getMusicFolder", "()Ljava/lang/String;") \
|
||||||
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
|
STATICMETHOD (getDownloadsFolder, "getDownloadsFolder", "()Ljava/lang/String;") \
|
||||||
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
|
STATICMETHOD (getMoviesFolder, "getMoviesFolder", "()Ljava/lang/String;") \
|
||||||
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
|
METHOD (getTypeFaceFromAsset, "getTypeFaceFromAsset", "(Ljava/lang/String;)Landroid/graphics/Typeface;") \
|
||||||
METHOD (getTypeFaceFromByteArray, "getTypeFaceFromByteArray", "([B)Landroid/graphics/Typeface;") \
|
METHOD (getTypeFaceFromByteArray, "getTypeFaceFromByteArray", "([B)Landroid/graphics/Typeface;") \
|
||||||
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \
|
METHOD (setScreenSaver, "setScreenSaver", "(Z)V") \
|
||||||
METHOD (getScreenSaver, "getScreenSaver", "()Z") \
|
METHOD (getScreenSaver, "getScreenSaver", "()Z") \
|
||||||
METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \
|
METHOD (getAndroidMidiDeviceManager, "getAndroidMidiDeviceManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$MidiDeviceManager;") \
|
||||||
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
|
METHOD (getAndroidBluetoothManager, "getAndroidBluetoothManager", "()L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$BluetoothManager;") \
|
||||||
STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
|
STATICMETHOD (getAndroidSDKVersion, "getAndroidSDKVersion", "()I") \
|
||||||
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
|
METHOD (audioManagerGetProperty, "audioManagerGetProperty", "(Ljava/lang/String;)Ljava/lang/String;") \
|
||||||
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
|
METHOD (hasSystemFeature, "hasSystemFeature", "(Ljava/lang/String;)Z" ) \
|
||||||
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
|
METHOD (requestRuntimePermission, "requestRuntimePermission", "(IJ)V" ) \
|
||||||
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
|
METHOD (isPermissionGranted, "isPermissionGranted", "(I)Z" ) \
|
||||||
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
|
METHOD (isPermissionDeclaredInManifest, "isPermissionDeclaredInManifest", "(I)Z" ) \
|
||||||
METHOD (getAssets, "getAssets", "()Landroid/content/res/AssetManager;") \
|
METHOD (isPermissionDeclaredInManifestString, "isPermissionDeclaredInManifest", "(Ljava/lang/String;)Z") \
|
||||||
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \
|
METHOD (getAssets, "getAssets", "()Landroid/content/res/AssetManager;") \
|
||||||
METHOD (getPackageManager, "getPackageManager", "()Landroid/content/pm/PackageManager;") \
|
METHOD (getSystemService, "getSystemService", "(Ljava/lang/String;)Ljava/lang/Object;") \
|
||||||
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \
|
METHOD (getPackageManager, "getPackageManager", "()Landroid/content/pm/PackageManager;") \
|
||||||
METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \
|
METHOD (getPackageName, "getPackageName", "()Ljava/lang/String;") \
|
||||||
METHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \
|
METHOD (getResources, "getResources", "()Landroid/content/res/Resources;") \
|
||||||
METHOD (invocationHandlerContextDeleted, "invocationHandlerContextDeleted", "(Ljava/lang/reflect/InvocationHandler;)V") \
|
METHOD (createInvocationHandler, "createInvocationHandler", "(J)Ljava/lang/reflect/InvocationHandler;") \
|
||||||
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \
|
METHOD (invocationHandlerContextDeleted, "invocationHandlerContextDeleted", "(Ljava/lang/reflect/InvocationHandler;)V") \
|
||||||
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \
|
METHOD (bindService, "bindService", "(Landroid/content/Intent;Landroid/content/ServiceConnection;I)Z") \
|
||||||
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \
|
METHOD (unbindService, "unbindService", "(Landroid/content/ServiceConnection;)V") \
|
||||||
METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \
|
METHOD (startIntentSenderForResult, "startIntentSenderForResult", "(Landroid/content/IntentSender;ILandroid/content/Intent;III)V") \
|
||||||
METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \
|
METHOD (moveTaskToBack, "moveTaskToBack", "(Z)Z") \
|
||||||
METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \
|
METHOD (startActivity, "startActivity", "(Landroid/content/Intent;)V") \
|
||||||
METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \
|
METHOD (startActivityForResult, "startActivityForResult", "(Landroid/content/Intent;I)V") \
|
||||||
METHOD (addAppPausedResumedListener, "addAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V") \
|
METHOD (getContentResolver, "getContentResolver", "()Landroid/content/ContentResolver;") \
|
||||||
METHOD (removeAppPausedResumedListener, "removeAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V")
|
METHOD (addAppPausedResumedListener, "addAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V") \
|
||||||
|
METHOD (removeAppPausedResumedListener, "removeAppPausedResumedListener", "(L" JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener;J)V")
|
||||||
|
|
||||||
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH);
|
DECLARE_JNI_CLASS (JuceAppActivity, JUCE_ANDROID_ACTIVITY_CLASSPATH);
|
||||||
#undef JNI_CLASS_MEMBERS
|
#undef JNI_CLASS_MEMBERS
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
#if __ANDROID_API__ >= 21
|
||||||
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||||
|
METHOD (build, "build", "()Landroid/media/AudioAttributes;") \
|
||||||
|
METHOD (constructor, "<init>", "()V") \
|
||||||
|
METHOD (setContentType, "setContentType", "(I)Landroid/media/AudioAttributes$Builder;") \
|
||||||
|
METHOD (setUsage, "setUsage", "(I)Landroid/media/AudioAttributes$Builder;")
|
||||||
|
|
||||||
|
DECLARE_JNI_CLASS (AndroidAudioAttributesBuilder, "android/media/AudioAttributes$Builder")
|
||||||
|
#undef JNI_CLASS_MEMBERS
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||||
|
METHOD (abandonAudioFocus, "abandonAudioFocus", "(Landroid/media/AudioManager$OnAudioFocusChangeListener;)I") \
|
||||||
|
METHOD (requestAudioFocus, "requestAudioFocus", "(Landroid/media/AudioManager$OnAudioFocusChangeListener;II)I")
|
||||||
|
|
||||||
|
DECLARE_JNI_CLASS (AndroidAudioManager, "android/media/AudioManager");
|
||||||
|
#undef JNI_CLASS_MEMBERS
|
||||||
|
|
||||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||||
STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \
|
STATICMETHOD (createBitmap, "createBitmap", "(IILandroid/graphics/Bitmap$Config;)Landroid/graphics/Bitmap;") \
|
||||||
STATICMETHOD (createBitmapFrom, "createBitmap", "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;") \
|
STATICMETHOD (createBitmapFrom, "createBitmap", "(Landroid/graphics/Bitmap;IIIILandroid/graphics/Matrix;Z)Landroid/graphics/Bitmap;") \
|
||||||
|
|
@ -741,6 +760,21 @@ namespace
|
||||||
|
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
inline bool jniCheckHasExceptionOccurredAndClear()
|
||||||
|
{
|
||||||
|
auto* env = getEnv();
|
||||||
|
|
||||||
|
LocalRef<jobject> exception (env->ExceptionOccurred());
|
||||||
|
|
||||||
|
if (exception != nullptr)
|
||||||
|
{
|
||||||
|
env->ExceptionClear();
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -778,4 +812,24 @@ LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||||
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
LocalRef<jobject> CreateJavaInterface (AndroidInterfaceImplementer* implementer,
|
||||||
const String& interfaceName);
|
const String& interfaceName);
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class AppPausedResumedListener : public AndroidInterfaceImplementer
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
struct Owner
|
||||||
|
{
|
||||||
|
virtual ~Owner() {}
|
||||||
|
|
||||||
|
virtual void appPaused() = 0;
|
||||||
|
virtual void appResumed() = 0;
|
||||||
|
};
|
||||||
|
|
||||||
|
AppPausedResumedListener (Owner&);
|
||||||
|
|
||||||
|
jobject invoke (jobject proxy, jobject method, jobjectArray args) override;
|
||||||
|
|
||||||
|
private:
|
||||||
|
Owner& owner;
|
||||||
|
};
|
||||||
|
|
||||||
} // namespace juce
|
} // namespace juce
|
||||||
|
|
|
||||||
|
|
@ -200,6 +200,35 @@ JUCE_JNI_CALLBACK (JUCE_JOIN_MACRO (JUCE_ANDROID_ACTIVITY_CLASSNAME, _00024Nativ
|
||||||
juce_dispatchDelete (env, thisPtr);
|
juce_dispatchDelete (env, thisPtr);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
AppPausedResumedListener::AppPausedResumedListener (Owner& ownerToUse)
|
||||||
|
: owner (ownerToUse)
|
||||||
|
{
|
||||||
|
}
|
||||||
|
|
||||||
|
jobject AppPausedResumedListener::invoke (jobject proxy, jobject method, jobjectArray args)
|
||||||
|
{
|
||||||
|
auto* env = getEnv();
|
||||||
|
|
||||||
|
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
||||||
|
|
||||||
|
int numArgs = args != nullptr ? env->GetArrayLength (args) : 0;
|
||||||
|
|
||||||
|
if (methodName == "appPaused" && numArgs == 0)
|
||||||
|
{
|
||||||
|
owner.appPaused();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
if (methodName == "appResumed" && numArgs == 0)
|
||||||
|
{
|
||||||
|
owner.appResumed();
|
||||||
|
return nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
||||||
|
}
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
JavaVM* androidJNIJavaVM = nullptr;
|
JavaVM* androidJNIJavaVM = nullptr;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -307,14 +307,10 @@ private:
|
||||||
auto inputStream = StreamCloser (LocalRef<jobject> (env->CallObjectMethod (assetFd,
|
auto inputStream = StreamCloser (LocalRef<jobject> (env->CallObjectMethod (assetFd,
|
||||||
AssetFileDescriptor.createInputStream)));
|
AssetFileDescriptor.createInputStream)));
|
||||||
|
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
{
|
{
|
||||||
// Failed to open file stream for resource
|
// Failed to open file stream for resource
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
|
|
||||||
env->ExceptionClear();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -326,14 +322,10 @@ private:
|
||||||
JavaFileOutputStream.constructor,
|
JavaFileOutputStream.constructor,
|
||||||
javaString (tempFile.getFullPathName()).get())));
|
javaString (tempFile.getFullPathName()).get())));
|
||||||
|
|
||||||
exception = LocalRef<jobject> (env->ExceptionOccurred());
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
{
|
{
|
||||||
// Failed to open file stream for temporary file
|
// Failed to open file stream for temporary file
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
|
|
||||||
env->ExceptionClear();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -347,14 +339,10 @@ private:
|
||||||
|
|
||||||
bytesRead = env->CallIntMethod (inputStream.stream, JavaFileInputStream.read, buffer.get());
|
bytesRead = env->CallIntMethod (inputStream.stream, JavaFileInputStream.read, buffer.get());
|
||||||
|
|
||||||
exception = LocalRef<jobject> (env->ExceptionOccurred());
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
{
|
{
|
||||||
// Failed to read from resource file.
|
// Failed to read from resource file.
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
|
|
||||||
env->ExceptionClear();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -363,12 +351,10 @@ private:
|
||||||
|
|
||||||
env->CallVoidMethod (outputStream.stream, JavaFileOutputStream.write, buffer.get(), 0, bytesRead);
|
env->CallVoidMethod (outputStream.stream, JavaFileOutputStream.write, buffer.get(), 0, bytesRead);
|
||||||
|
|
||||||
if (exception != 0)
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
{
|
{
|
||||||
// Failed to write to temporary file.
|
// Failed to write to temporary file.
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
|
|
||||||
env->ExceptionClear();
|
|
||||||
return {};
|
return {};
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -714,14 +700,10 @@ private:
|
||||||
ParcelFileDescriptor.open,
|
ParcelFileDescriptor.open,
|
||||||
javaFile.get(), modeReadOnly));
|
javaFile.get(), modeReadOnly));
|
||||||
|
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
{
|
{
|
||||||
// Failed to create file descriptor. Have you provided a valid file path/resource name?
|
// Failed to create file descriptor. Have you provided a valid file path/resource name?
|
||||||
jassertfalse;
|
jassertfalse;
|
||||||
|
|
||||||
env->ExceptionClear();
|
|
||||||
return nullptr;
|
return nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -27,17 +27,6 @@
|
||||||
namespace juce
|
namespace juce
|
||||||
{
|
{
|
||||||
|
|
||||||
#if __ANDROID_API__ >= 21
|
|
||||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
|
||||||
METHOD (build, "build", "()Landroid/media/AudioAttributes;") \
|
|
||||||
METHOD (constructor, "<init>", "()V") \
|
|
||||||
METHOD (setContentType, "setContentType", "(I)Landroid/media/AudioAttributes$Builder;") \
|
|
||||||
METHOD (setUsage, "setUsage", "(I)Landroid/media/AudioAttributes$Builder;")
|
|
||||||
|
|
||||||
DECLARE_JNI_CLASS (AudioAttributesBuilder, "android/media/AudioAttributes$Builder")
|
|
||||||
#undef JNI_CLASS_MEMBERS
|
|
||||||
#endif
|
|
||||||
|
|
||||||
#if __ANDROID_API__ >= 26
|
#if __ANDROID_API__ >= 26
|
||||||
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
#define JNI_CLASS_MEMBERS(METHOD, STATICMETHOD, FIELD, STATICFIELD) \
|
||||||
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \
|
METHOD (constructor, "<init>", "(Ljava/lang/String;Ljava/lang/CharSequence;I)V") \
|
||||||
|
|
@ -1504,12 +1493,12 @@ struct PushNotifications::Pimpl
|
||||||
env->CallVoidMethod (channel, NotificationChannel.enableVibration, c.enableVibration);
|
env->CallVoidMethod (channel, NotificationChannel.enableVibration, c.enableVibration);
|
||||||
}
|
}
|
||||||
|
|
||||||
auto audioAttributesBuilder = LocalRef<jobject> (env->NewObject (AudioAttributesBuilder, AudioAttributesBuilder.constructor));
|
auto AndroidAudioAttributesBuilder = LocalRef<jobject> (env->NewObject (AndroidAudioAttributesBuilder, AndroidAudioAttributesBuilder.constructor));
|
||||||
const int contentTypeSonification = 4;
|
const int contentTypeSonification = 4;
|
||||||
const int usageNotification = 5;
|
const int usageNotification = 5;
|
||||||
env->CallObjectMethod (audioAttributesBuilder, AudioAttributesBuilder.setContentType, contentTypeSonification);
|
env->CallObjectMethod (AndroidAudioAttributesBuilder, AndroidAudioAttributesBuilder.setContentType, contentTypeSonification);
|
||||||
env->CallObjectMethod (audioAttributesBuilder, AudioAttributesBuilder.setUsage, usageNotification);
|
env->CallObjectMethod (AndroidAudioAttributesBuilder, AndroidAudioAttributesBuilder.setUsage, usageNotification);
|
||||||
auto audioAttributes = LocalRef<jobject> (env->CallObjectMethod (audioAttributesBuilder, AudioAttributesBuilder.build));
|
auto audioAttributes = LocalRef<jobject> (env->CallObjectMethod (AndroidAudioAttributesBuilder, AndroidAudioAttributesBuilder.build));
|
||||||
env->CallVoidMethod (channel, NotificationChannel.setSound, juceUrlToAndroidUri (c.soundToPlay).get(), audioAttributes.get());
|
env->CallVoidMethod (channel, NotificationChannel.setSound, juceUrlToAndroidUri (c.soundToPlay).get(), audioAttributes.get());
|
||||||
|
|
||||||
env->CallVoidMethod (notificationManager, NotificationManagerApi26.createNotificationChannel, channel.get());
|
env->CallVoidMethod (notificationManager, NotificationManagerApi26.createNotificationChannel, channel.get());
|
||||||
|
|
|
||||||
|
|
@ -61,7 +61,8 @@ public:
|
||||||
// create a native surface view
|
// create a native surface view
|
||||||
surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(),
|
surfaceView = GlobalRef (env->CallObjectMethod (android.activity.get(),
|
||||||
JuceAppActivity.createNativeSurfaceView,
|
JuceAppActivity.createNativeSurfaceView,
|
||||||
reinterpret_cast<jlong> (this)));
|
reinterpret_cast<jlong> (this),
|
||||||
|
false));
|
||||||
if (surfaceView.get() == nullptr)
|
if (surfaceView.get() == nullptr)
|
||||||
return;
|
return;
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -79,6 +79,25 @@
|
||||||
#undef JUCE_USE_CAMERA
|
#undef JUCE_USE_CAMERA
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
|
//=============================================================================
|
||||||
|
/** Config: JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
Enables synchronisation between video playback volume and OS media volume.
|
||||||
|
Currently supported on Android only.
|
||||||
|
*/
|
||||||
|
#ifndef JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
#define JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#ifndef JUCE_VIDEO_LOG_ENABLED
|
||||||
|
#define JUCE_VIDEO_LOG_ENABLED 1
|
||||||
|
#endif
|
||||||
|
|
||||||
|
#if JUCE_VIDEO_LOG_ENABLED
|
||||||
|
#define JUCE_VIDEO_LOG(x) DBG(x)
|
||||||
|
#else
|
||||||
|
#define JUCE_VIDEO_LOG(x) {}
|
||||||
|
#endif
|
||||||
|
|
||||||
//=============================================================================
|
//=============================================================================
|
||||||
#include "playback/juce_VideoComponent.h"
|
#include "playback/juce_VideoComponent.h"
|
||||||
#include "capture/juce_CameraDevice.h"
|
#include "capture/juce_CameraDevice.h"
|
||||||
|
|
|
||||||
|
|
@ -442,49 +442,6 @@ private:
|
||||||
Owner& owner;
|
Owner& owner;
|
||||||
};
|
};
|
||||||
|
|
||||||
//==============================================================================
|
|
||||||
class AppPausedResumedListener : public AndroidInterfaceImplementer
|
|
||||||
{
|
|
||||||
public:
|
|
||||||
struct Owner
|
|
||||||
{
|
|
||||||
virtual ~Owner() {}
|
|
||||||
|
|
||||||
virtual void appPaused() = 0;
|
|
||||||
virtual void appResumed() = 0;
|
|
||||||
};
|
|
||||||
|
|
||||||
AppPausedResumedListener (Owner& ownerToUse)
|
|
||||||
: owner (ownerToUse)
|
|
||||||
{}
|
|
||||||
|
|
||||||
jobject invoke (jobject proxy, jobject method, jobjectArray args) override
|
|
||||||
{
|
|
||||||
auto* env = getEnv();
|
|
||||||
|
|
||||||
auto methodName = juceString ((jstring) env->CallObjectMethod (method, JavaMethod.getName));
|
|
||||||
|
|
||||||
int numArgs = args != nullptr ? env->GetArrayLength (args) : 0;
|
|
||||||
|
|
||||||
if (methodName == "appPaused" && numArgs == 0)
|
|
||||||
{
|
|
||||||
owner.appPaused();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (methodName == "appResumed" && numArgs == 0)
|
|
||||||
{
|
|
||||||
owner.appResumed();
|
|
||||||
return nullptr;
|
|
||||||
}
|
|
||||||
|
|
||||||
return AndroidInterfaceImplementer::invoke (proxy, method, args);
|
|
||||||
}
|
|
||||||
|
|
||||||
private:
|
|
||||||
Owner& owner;
|
|
||||||
};
|
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
struct CameraDevice::Pimpl
|
struct CameraDevice::Pimpl
|
||||||
#if __ANDROID_API__ >= 21
|
#if __ANDROID_API__ >= 21
|
||||||
|
|
@ -506,7 +463,6 @@ struct CameraDevice::Pimpl
|
||||||
appPausedResumedListener (*this),
|
appPausedResumedListener (*this),
|
||||||
appPausedResumedListenerNative (CreateJavaInterface (&appPausedResumedListener,
|
appPausedResumedListenerNative (CreateJavaInterface (&appPausedResumedListener,
|
||||||
JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener").get()),
|
JUCE_ANDROID_ACTIVITY_CLASSPATH "$AppPausedResumedListener").get()),
|
||||||
|
|
||||||
cameraManager (initialiseCameraManager()),
|
cameraManager (initialiseCameraManager()),
|
||||||
cameraCharacteristics (initialiseCameraCharacteristics (cameraManager, cameraId)),
|
cameraCharacteristics (initialiseCameraCharacteristics (cameraManager, cameraId)),
|
||||||
streamConfigurationMap (cameraCharacteristics),
|
streamConfigurationMap (cameraCharacteristics),
|
||||||
|
|
@ -869,7 +825,7 @@ private:
|
||||||
|
|
||||||
bool isOutputSupportedForSurface (const LocalRef<jobject>& surface) const
|
bool isOutputSupportedForSurface (const LocalRef<jobject>& surface) const
|
||||||
{
|
{
|
||||||
return getEnv()->CallBooleanMethod (scalerStreamConfigurationMap, AndroidStreamConfigurationMap.isOutputSupportedForSurface, surface.get());
|
return getEnv()->CallBooleanMethod (scalerStreamConfigurationMap, AndroidStreamConfigurationMap.isOutputSupportedForSurface, surface.get()) != 0;
|
||||||
}
|
}
|
||||||
|
|
||||||
static constexpr int jpegImageFormat = 256;
|
static constexpr int jpegImageFormat = 256;
|
||||||
|
|
@ -1460,10 +1416,7 @@ private:
|
||||||
|
|
||||||
// ... ignore RuntimeException that can be thrown if stop() was called after recording
|
// ... ignore RuntimeException that can be thrown if stop() was called after recording
|
||||||
// has started but before any frame was written to a file. This is not an error.
|
// has started but before any frame was written to a file. This is not an error.
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
jniCheckHasExceptionOccurredAndClear();
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
env->ExceptionClear();
|
|
||||||
|
|
||||||
unlockScreenOrientation();
|
unlockScreenOrientation();
|
||||||
}
|
}
|
||||||
|
|
@ -1630,16 +1583,12 @@ private:
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
|
||||||
|
|
||||||
// When exception occurs, CameraCaptureSession.close will never finish, so
|
// When exception occurs, CameraCaptureSession.close will never finish, so
|
||||||
// we should not wait for it. For fatal error an exception does occur, but
|
// we should not wait for it. For fatal error an exception does occur, but
|
||||||
// it is catched internally in Java...
|
// it is catched internally in Java...
|
||||||
if (exception != 0 || scopedCameraDevice.fatalErrorOccurred.get())
|
if (jniCheckHasExceptionOccurredAndClear() || scopedCameraDevice.fatalErrorOccurred.get())
|
||||||
{
|
{
|
||||||
JUCE_CAMERA_LOG ("Exception or fatal error occurred while closing Capture Session, closing by force");
|
JUCE_CAMERA_LOG ("Exception or fatal error occurred while closing Capture Session, closing by force");
|
||||||
|
|
||||||
env->ExceptionClear();
|
|
||||||
}
|
}
|
||||||
else if (calledClose)
|
else if (calledClose)
|
||||||
{
|
{
|
||||||
|
|
@ -1768,7 +1717,7 @@ private:
|
||||||
|
|
||||||
void lockFocus()
|
void lockFocus()
|
||||||
{
|
{
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JUCE_CAMERA_LOG ("Performing auto-focus if possible...");
|
JUCE_CAMERA_LOG ("Performing auto-focus if possible...");
|
||||||
|
|
@ -1796,7 +1745,7 @@ private:
|
||||||
// IllegalStateException can be thrown when accessing CaptureSession,
|
// IllegalStateException can be thrown when accessing CaptureSession,
|
||||||
// claiming that capture session was already closed but we may not
|
// claiming that capture session was already closed but we may not
|
||||||
// get relevant callback yet, so check for this and bailout when needed.
|
// get relevant callback yet, so check for this and bailout when needed.
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
|
|
@ -1902,7 +1851,7 @@ private:
|
||||||
|
|
||||||
void captureStillPictureDelayed()
|
void captureStillPictureDelayed()
|
||||||
{
|
{
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JUCE_CAMERA_LOG ("Still picture capture, device ready, capturing now...");
|
JUCE_CAMERA_LOG ("Still picture capture, device ready, capturing now...");
|
||||||
|
|
@ -1911,12 +1860,12 @@ private:
|
||||||
|
|
||||||
env->CallVoidMethod (captureSession, CameraCaptureSession.stopRepeating);
|
env->CallVoidMethod (captureSession, CameraCaptureSession.stopRepeating);
|
||||||
|
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
env->CallVoidMethod (captureSession, CameraCaptureSession.abortCaptures);
|
env->CallVoidMethod (captureSession, CameraCaptureSession.abortCaptures);
|
||||||
|
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// Delay still picture capture for devices that can't handle it right after
|
// Delay still picture capture for devices that can't handle it right after
|
||||||
|
|
@ -1929,7 +1878,7 @@ private:
|
||||||
|
|
||||||
void runPrecaptureSequence()
|
void runPrecaptureSequence()
|
||||||
{
|
{
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
auto* env = getEnv();
|
auto* env = getEnv();
|
||||||
|
|
@ -1950,7 +1899,7 @@ private:
|
||||||
|
|
||||||
void unlockFocus()
|
void unlockFocus()
|
||||||
{
|
{
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
JUCE_CAMERA_LOG ("Unlocking focus...");
|
JUCE_CAMERA_LOG ("Unlocking focus...");
|
||||||
|
|
@ -1970,7 +1919,7 @@ private:
|
||||||
env->CallIntMethod (captureSession, CameraCaptureSession.capture, resetAutoFocusRequest.get(),
|
env->CallIntMethod (captureSession, CameraCaptureSession.capture, resetAutoFocusRequest.get(),
|
||||||
nullptr, handler.get());
|
nullptr, handler.get());
|
||||||
|
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
// NB: for preview, using preview capture request again
|
// NB: for preview, using preview capture request again
|
||||||
|
|
@ -2233,10 +2182,7 @@ private:
|
||||||
|
|
||||||
// If something went wrong we will be pinged in cameraDeviceStateError()
|
// If something went wrong we will be pinged in cameraDeviceStateError()
|
||||||
// callback, silence the redundant exception.
|
// callback, silence the redundant exception.
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
jniCheckHasExceptionOccurredAndClear();
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
env->ExceptionClear();
|
|
||||||
}
|
}
|
||||||
|
|
||||||
void close()
|
void close()
|
||||||
|
|
@ -2500,12 +2446,12 @@ private:
|
||||||
|
|
||||||
env->CallVoidMethod (session, CameraCaptureSession.stopRepeating);
|
env->CallVoidMethod (session, CameraCaptureSession.stopRepeating);
|
||||||
|
|
||||||
if (Pimpl::checkHasExceptionOccurred())
|
if (jniCheckHasExceptionOccurredAndClear())
|
||||||
return;
|
return;
|
||||||
|
|
||||||
env->CallVoidMethod (session, CameraCaptureSession.abortCaptures);
|
env->CallVoidMethod (session, CameraCaptureSession.abortCaptures);
|
||||||
|
|
||||||
Pimpl::checkHasExceptionOccurred();
|
jniCheckHasExceptionOccurredAndClear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -3064,29 +3010,11 @@ private:
|
||||||
env->CallBooleanMethod (handlerThread, AndroidHandlerThread.quitSafely);
|
env->CallBooleanMethod (handlerThread, AndroidHandlerThread.quitSafely);
|
||||||
env->CallVoidMethod (handlerThread, AndroidHandlerThread.join);
|
env->CallVoidMethod (handlerThread, AndroidHandlerThread.join);
|
||||||
|
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
jniCheckHasExceptionOccurredAndClear();
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
env->ExceptionClear();
|
|
||||||
|
|
||||||
handlerThread.clear();
|
handlerThread.clear();
|
||||||
handler.clear();
|
handler.clear();
|
||||||
}
|
}
|
||||||
|
|
||||||
static bool checkHasExceptionOccurred()
|
|
||||||
{
|
|
||||||
auto* env = getEnv();
|
|
||||||
|
|
||||||
auto exception = LocalRef<jobject> (env->ExceptionOccurred());
|
|
||||||
|
|
||||||
if (exception != 0)
|
|
||||||
{
|
|
||||||
env->ExceptionClear();
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
friend struct CameraDevice::ViewerComponent;
|
friend struct CameraDevice::ViewerComponent;
|
||||||
|
|
|
||||||
1918
modules/juce_video/native/juce_android_Video.h
Normal file
1918
modules/juce_video/native/juce_android_Video.h
Normal file
File diff suppressed because it is too large
Load diff
|
|
@ -2,7 +2,7 @@
|
||||||
==============================================================================
|
==============================================================================
|
||||||
|
|
||||||
This file is part of the JUCE library.
|
This file is part of the JUCE library.
|
||||||
Copyright (c) 2015 - ROLI Ltd.
|
Copyright (c) 2018 - ROLI Ltd.
|
||||||
|
|
||||||
Permission is granted to use this software under the terms of either:
|
Permission is granted to use this software under the terms of either:
|
||||||
a) the GPL v2 (or any later version)
|
a) the GPL v2 (or any later version)
|
||||||
|
|
@ -22,34 +22,26 @@
|
||||||
==============================================================================
|
==============================================================================
|
||||||
*/
|
*/
|
||||||
|
|
||||||
#if JUCE_IOS
|
#if JUCE_MAC
|
||||||
using BaseClass = UIViewComponent;
|
using Base = NSViewComponent;
|
||||||
#else
|
#else
|
||||||
using BaseClass = NSViewComponent;
|
using Base = UIViewComponent;
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
struct VideoComponent::Pimpl : public BaseClass
|
struct VideoComponent::Pimpl : public Base
|
||||||
{
|
{
|
||||||
Pimpl()
|
Pimpl (VideoComponent& ownerToUse, bool useNativeControlsIfAvailable)
|
||||||
|
: owner (ownerToUse),
|
||||||
|
playerController (*this, useNativeControlsIfAvailable)
|
||||||
{
|
{
|
||||||
setVisible (true);
|
setVisible (true);
|
||||||
|
|
||||||
#if JUCE_MAC && JUCE_32BIT
|
auto* view = playerController.getView();
|
||||||
auto view = [[NSView alloc] init]; // 32-bit builds don't have AVPlayerView, so need to use a layer
|
|
||||||
controller = [[AVPlayerLayer alloc] init];
|
|
||||||
setView (view);
|
setView (view);
|
||||||
|
|
||||||
|
#if JUCE_MAC
|
||||||
[view setNextResponder: [view superview]];
|
[view setNextResponder: [view superview]];
|
||||||
[view setWantsLayer: YES];
|
[view setWantsLayer: YES];
|
||||||
[view setLayer: controller];
|
|
||||||
[view release];
|
|
||||||
#elif JUCE_MAC
|
|
||||||
controller = [[AVPlayerView alloc] init];
|
|
||||||
setView (controller);
|
|
||||||
[controller setNextResponder: [controller superview]];
|
|
||||||
[controller setWantsLayer: YES];
|
|
||||||
#else
|
|
||||||
controller = [[AVPlayerViewController alloc] init];
|
|
||||||
setView ([controller view]);
|
|
||||||
#endif
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -57,7 +49,6 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
setView (nil);
|
setView (nil);
|
||||||
[controller release];
|
|
||||||
}
|
}
|
||||||
|
|
||||||
Result load (const File& file)
|
Result load (const File& file)
|
||||||
|
|
@ -85,34 +76,46 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
if (url != nil)
|
if (url != nil)
|
||||||
{
|
{
|
||||||
close();
|
close();
|
||||||
|
return playerController.load (url);
|
||||||
if (auto* player = [AVPlayer playerWithURL: url])
|
|
||||||
{
|
|
||||||
[controller setPlayer: player];
|
|
||||||
return Result::ok();
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
return Result::fail ("Couldn't open movie");
|
return Result::fail ("Couldn't open movie");
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void loadAsync (const URL& url, std::function<void (const URL&, Result)> callback)
|
||||||
|
{
|
||||||
|
if (url.isEmpty())
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentURL = url;
|
||||||
|
|
||||||
|
jassert (callback != nullptr);
|
||||||
|
|
||||||
|
loadFinishedCallback = std::move (callback);
|
||||||
|
|
||||||
|
playerController.loadAsync (url);
|
||||||
|
}
|
||||||
|
|
||||||
void close()
|
void close()
|
||||||
{
|
{
|
||||||
stop();
|
stop();
|
||||||
[controller setPlayer: nil];
|
playerController.close();
|
||||||
currentFile = File();
|
currentFile = File();
|
||||||
currentURL = {};
|
currentURL = {};
|
||||||
}
|
}
|
||||||
|
|
||||||
bool isOpen() const noexcept { return getAVPlayer() != nil; }
|
bool isOpen() const noexcept { return playerController.getPlayer() != nil; }
|
||||||
bool isPlaying() const noexcept { return getSpeed() != 0; }
|
bool isPlaying() const noexcept { return getSpeed() != 0; }
|
||||||
|
|
||||||
void play() noexcept { [getAVPlayer() play]; }
|
void play() noexcept { [playerController.getPlayer() play]; setSpeed (playSpeedMult); }
|
||||||
void stop() noexcept { [getAVPlayer() pause]; }
|
void stop() noexcept { [playerController.getPlayer() pause]; }
|
||||||
|
|
||||||
void setPosition (double newPosition)
|
void setPosition (double newPosition)
|
||||||
{
|
{
|
||||||
if (auto* p = getAVPlayer())
|
if (auto* p = playerController.getPlayer())
|
||||||
{
|
{
|
||||||
CMTime t = { (CMTimeValue) (100000.0 * newPosition),
|
CMTime t = { (CMTimeValue) (100000.0 * newPosition),
|
||||||
(CMTimeScale) 100000, kCMTimeFlags_Valid };
|
(CMTimeScale) 100000, kCMTimeFlags_Valid };
|
||||||
|
|
@ -125,7 +128,7 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
|
|
||||||
double getPosition() const
|
double getPosition() const
|
||||||
{
|
{
|
||||||
if (auto* p = getAVPlayer())
|
if (auto* p = playerController.getPlayer())
|
||||||
return toSeconds ([p currentTime]);
|
return toSeconds ([p currentTime]);
|
||||||
|
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
@ -133,12 +136,16 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
|
|
||||||
void setSpeed (double newSpeed)
|
void setSpeed (double newSpeed)
|
||||||
{
|
{
|
||||||
[getAVPlayer() setRate: (float) newSpeed];
|
playSpeedMult = newSpeed;
|
||||||
|
|
||||||
|
// Calling non 0.0 speed on a paused player would start it...
|
||||||
|
if (isPlaying())
|
||||||
|
[playerController.getPlayer() setRate: (float) playSpeedMult];
|
||||||
}
|
}
|
||||||
|
|
||||||
double getSpeed() const
|
double getSpeed() const
|
||||||
{
|
{
|
||||||
if (auto* p = getAVPlayer())
|
if (auto* p = playerController.getPlayer())
|
||||||
return [p rate];
|
return [p rate];
|
||||||
|
|
||||||
return 0.0;
|
return 0.0;
|
||||||
|
|
@ -146,9 +153,9 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
|
|
||||||
Rectangle<int> getNativeSize() const
|
Rectangle<int> getNativeSize() const
|
||||||
{
|
{
|
||||||
if (auto* player = getAVPlayer())
|
if (auto* p = playerController.getPlayer())
|
||||||
{
|
{
|
||||||
auto s = [[player currentItem] presentationSize];
|
auto s = [[p currentItem] presentationSize];
|
||||||
return { (int) s.width, (int) s.height };
|
return { (int) s.width, (int) s.height };
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -157,20 +164,20 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
|
|
||||||
double getDuration() const
|
double getDuration() const
|
||||||
{
|
{
|
||||||
if (auto* player = getAVPlayer())
|
if (auto* p = playerController.getPlayer())
|
||||||
return toSeconds ([[player currentItem] duration]);
|
return toSeconds ([[p currentItem] duration]);
|
||||||
|
|
||||||
return 0.0;
|
return 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
void setVolume (float newVolume)
|
void setVolume (float newVolume)
|
||||||
{
|
{
|
||||||
[getAVPlayer() setVolume: newVolume];
|
[playerController.getPlayer() setVolume: newVolume];
|
||||||
}
|
}
|
||||||
|
|
||||||
float getVolume() const
|
float getVolume() const
|
||||||
{
|
{
|
||||||
if (auto* p = getAVPlayer())
|
if (auto* p = playerController.getPlayer())
|
||||||
return [p volume];
|
return [p volume];
|
||||||
|
|
||||||
return 0.0f;
|
return 0.0f;
|
||||||
|
|
@ -180,20 +187,633 @@ struct VideoComponent::Pimpl : public BaseClass
|
||||||
URL currentURL;
|
URL currentURL;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
#if JUCE_IOS
|
//==============================================================================
|
||||||
AVPlayerViewController* controller = nil;
|
template <typename Derived>
|
||||||
#elif JUCE_32BIT
|
class PlayerControllerBase
|
||||||
AVPlayerLayer* controller = nil;
|
{
|
||||||
|
public:
|
||||||
|
~PlayerControllerBase()
|
||||||
|
{
|
||||||
|
detachPlayerStatusObserver();
|
||||||
|
detachPlaybackObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
protected:
|
||||||
|
//==============================================================================
|
||||||
|
struct JucePlayerStatusObserverClass : public ObjCClass<NSObject>
|
||||||
|
{
|
||||||
|
JucePlayerStatusObserverClass() : ObjCClass<NSObject> ("JucePlayerStatusObserverClass_")
|
||||||
|
{
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||||
|
addMethod (@selector (observeValueForKeyPath:ofObject:change:context:), valueChanged, "v@:@@@?");
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
addIvar<PlayerAsyncInitialiser*> ("owner");
|
||||||
|
|
||||||
|
registerClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
static PlayerControllerBase& getOwner (id self) { return *getIvar<PlayerControllerBase*> (self, "owner"); }
|
||||||
|
static void setOwner (id self, PlayerControllerBase* p) { object_setInstanceVariable (self, "owner", p); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void valueChanged (id self, SEL, NSString* keyPath, id,
|
||||||
|
NSDictionary<NSKeyValueChangeKey, id>* change, void*)
|
||||||
|
{
|
||||||
|
auto& owner = getOwner (self);
|
||||||
|
|
||||||
|
if ([keyPath isEqualToString: nsStringLiteral ("rate")])
|
||||||
|
{
|
||||||
|
auto oldRate = [change[NSKeyValueChangeOldKey] floatValue];
|
||||||
|
auto newRate = [change[NSKeyValueChangeNewKey] floatValue];
|
||||||
|
|
||||||
|
if (oldRate == 0 && newRate != 0)
|
||||||
|
owner.playbackStarted();
|
||||||
|
else if (oldRate != 0 && newRate == 0)
|
||||||
|
owner.playbackStopped();
|
||||||
|
}
|
||||||
|
else if ([keyPath isEqualToString: nsStringLiteral ("status")])
|
||||||
|
{
|
||||||
|
auto status = [change[NSKeyValueChangeNewKey] intValue];
|
||||||
|
|
||||||
|
if (status == AVPlayerStatusFailed)
|
||||||
|
owner.errorOccurred();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
struct JucePlayerItemPlaybackStatusObserverClass : public ObjCClass<NSObject>
|
||||||
|
{
|
||||||
|
JucePlayerItemPlaybackStatusObserverClass() : ObjCClass<NSObject> ("JucePlayerItemPlaybackStatusObserverClass_")
|
||||||
|
{
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||||
|
addMethod (@selector (processNotification:), notificationReceived, "v@:@");
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
addIvar<PlayerControllerBase*> ("owner");
|
||||||
|
|
||||||
|
registerClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
static PlayerControllerBase& getOwner (id self) { return *getIvar<PlayerControllerBase*> (self, "owner"); }
|
||||||
|
static void setOwner (id self, PlayerControllerBase* p) { object_setInstanceVariable (self, "owner", p); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void notificationReceived (id self, SEL, NSNotification* notification)
|
||||||
|
{
|
||||||
|
if ([notification.name isEqualToString: AVPlayerItemDidPlayToEndTimeNotification])
|
||||||
|
getOwner (self).playbackReachedEndTime();
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
class PlayerAsyncInitialiser
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlayerAsyncInitialiser (PlayerControllerBase& ownerToUse)
|
||||||
|
: owner (ownerToUse),
|
||||||
|
assetKeys ([[NSArray alloc] initWithObjects: nsStringLiteral ("duration"), nsStringLiteral ("tracks"),
|
||||||
|
nsStringLiteral ("playable"), nil])
|
||||||
|
{
|
||||||
|
static JucePlayerItemPreparationStatusObserverClass cls;
|
||||||
|
playerItemPreparationStatusObserver.reset ([cls.createInstance() init]);
|
||||||
|
JucePlayerItemPreparationStatusObserverClass::setOwner (playerItemPreparationStatusObserver.get(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
~PlayerAsyncInitialiser()
|
||||||
|
{
|
||||||
|
detachPreparationStatusObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadAsync (URL url)
|
||||||
|
{
|
||||||
|
auto* nsUrl = [NSURL URLWithString: juceStringToNS (url.toString (true))];
|
||||||
|
asset.reset ([[AVURLAsset alloc] initWithURL: nsUrl options: nil]);
|
||||||
|
|
||||||
|
[asset.get() loadValuesAsynchronouslyForKeys: assetKeys.get()
|
||||||
|
completionHandler: ^() { checkAllKeysReadyFor (asset.get(), url); }];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================
|
||||||
|
struct JucePlayerItemPreparationStatusObserverClass : public ObjCClass<NSObject>
|
||||||
|
{
|
||||||
|
JucePlayerItemPreparationStatusObserverClass() : ObjCClass<NSObject> ("JucePlayerItemStatusObserverClass_")
|
||||||
|
{
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||||
|
addMethod (@selector (observeValueForKeyPath:ofObject:change:context:), valueChanged, "v@:@@@?");
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
|
||||||
|
addIvar<PlayerAsyncInitialiser*> ("owner");
|
||||||
|
|
||||||
|
registerClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
static PlayerAsyncInitialiser& getOwner (id self) { return *getIvar<PlayerAsyncInitialiser*> (self, "owner"); }
|
||||||
|
static void setOwner (id self, PlayerAsyncInitialiser* p) { object_setInstanceVariable (self, "owner", p); }
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void valueChanged (id self, SEL, NSString*, id object,
|
||||||
|
NSDictionary<NSKeyValueChangeKey, id>* change, void* context)
|
||||||
|
{
|
||||||
|
auto& owner = getOwner (self);
|
||||||
|
|
||||||
|
if (context == &owner)
|
||||||
|
{
|
||||||
|
auto* playerItem = (AVPlayerItem*) object;
|
||||||
|
auto* urlAsset = (AVURLAsset*) playerItem.asset;
|
||||||
|
|
||||||
|
URL url (nsStringToJuce (urlAsset.URL.absoluteString));
|
||||||
|
auto oldStatus = [change[NSKeyValueChangeOldKey] intValue];
|
||||||
|
auto newStatus = [change[NSKeyValueChangeNewKey] intValue];
|
||||||
|
|
||||||
|
// Ignore spurious notifications
|
||||||
|
if (oldStatus == newStatus)
|
||||||
|
return;
|
||||||
|
|
||||||
|
if (newStatus == AVPlayerItemStatusFailed)
|
||||||
|
{
|
||||||
|
auto errorMessage = playerItem.error != nil
|
||||||
|
? nsStringToJuce (playerItem.error.localizedDescription)
|
||||||
|
: String();
|
||||||
|
|
||||||
|
owner.notifyOwnerPreparationFinished (url, Result::fail (errorMessage), nullptr);
|
||||||
|
}
|
||||||
|
else if (newStatus == AVPlayerItemStatusReadyToPlay)
|
||||||
|
{
|
||||||
|
owner.notifyOwnerPreparationFinished (url, Result::ok(), owner.player.release());
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
PlayerControllerBase& owner;
|
||||||
|
|
||||||
|
std::unique_ptr<AVURLAsset, NSObjectDeleter> asset;
|
||||||
|
std::unique_ptr<NSArray<NSString*>, NSObjectDeleter> assetKeys;
|
||||||
|
std::unique_ptr<AVPlayerItem, NSObjectDeleter> playerItem;
|
||||||
|
std::unique_ptr<NSObject, NSObjectDeleter> playerItemPreparationStatusObserver;
|
||||||
|
std::unique_ptr<AVPlayer, NSObjectDeleter> player;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void checkAllKeysReadyFor (AVAsset* assetToCheck, const URL& url)
|
||||||
|
{
|
||||||
|
NSError* error = nil;
|
||||||
|
|
||||||
|
int successCount = 0;
|
||||||
|
|
||||||
|
for (NSString* key : assetKeys.get())
|
||||||
|
{
|
||||||
|
switch ([assetToCheck statusOfValueForKey: key error: &error])
|
||||||
|
{
|
||||||
|
case AVKeyValueStatusLoaded:
|
||||||
|
{
|
||||||
|
++successCount;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
case AVKeyValueStatusCancelled:
|
||||||
|
{
|
||||||
|
notifyOwnerPreparationFinished (url, Result::fail ("Loading cancelled"), nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
case AVKeyValueStatusFailed:
|
||||||
|
{
|
||||||
|
auto errorMessage = error != nil ? nsStringToJuce (error.localizedDescription) : String();
|
||||||
|
notifyOwnerPreparationFinished (url, Result::fail (errorMessage), nullptr);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
default:
|
||||||
|
{}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
jassert (successCount == (int) [assetKeys.get() count]);
|
||||||
|
preparePlayerItem();
|
||||||
|
}
|
||||||
|
|
||||||
|
void preparePlayerItem()
|
||||||
|
{
|
||||||
|
playerItem.reset ([[AVPlayerItem alloc] initWithAsset: asset.get()]);
|
||||||
|
|
||||||
|
attachPreparationStatusObserver();
|
||||||
|
|
||||||
|
player.reset ([[AVPlayer alloc] initWithPlayerItem: playerItem.get()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void attachPreparationStatusObserver()
|
||||||
|
{
|
||||||
|
[playerItem.get() addObserver: playerItemPreparationStatusObserver.get()
|
||||||
|
forKeyPath: nsStringLiteral ("status")
|
||||||
|
options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
|
||||||
|
context: this];
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachPreparationStatusObserver()
|
||||||
|
{
|
||||||
|
if (playerItem != nullptr && playerItemPreparationStatusObserver != nullptr)
|
||||||
|
{
|
||||||
|
[playerItem.get() removeObserver: playerItemPreparationStatusObserver.get()
|
||||||
|
forKeyPath: nsStringLiteral ("status")
|
||||||
|
context: this];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void notifyOwnerPreparationFinished (const URL& url, Result r, AVPlayer* preparedPlayer)
|
||||||
|
{
|
||||||
|
WeakReference<PlayerAsyncInitialiser> safeThis (this);
|
||||||
|
|
||||||
|
MessageManager::callAsync ([safeThis, url, r, preparedPlayer]() mutable
|
||||||
|
{
|
||||||
|
if (safeThis != nullptr)
|
||||||
|
safeThis->owner.playerPreparationFinished (url, r, preparedPlayer);
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
JUCE_DECLARE_WEAK_REFERENCEABLE (PlayerAsyncInitialiser)
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
Pimpl& owner;
|
||||||
|
bool useNativeControls;
|
||||||
|
|
||||||
|
PlayerAsyncInitialiser playerAsyncInitialiser;
|
||||||
|
std::unique_ptr<NSObject, NSObjectDeleter> playerStatusObserver;
|
||||||
|
std::unique_ptr<NSObject, NSObjectDeleter> playerItemPlaybackStatusObserver;
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
PlayerControllerBase (Pimpl& ownerToUse, bool useNativeControlsIfAvailable)
|
||||||
|
: owner (ownerToUse),
|
||||||
|
useNativeControls (useNativeControlsIfAvailable),
|
||||||
|
playerAsyncInitialiser (*this)
|
||||||
|
{
|
||||||
|
static JucePlayerStatusObserverClass playerObserverClass;
|
||||||
|
playerStatusObserver.reset ([playerObserverClass.createInstance() init]);
|
||||||
|
JucePlayerStatusObserverClass::setOwner (playerStatusObserver.get(), this);
|
||||||
|
|
||||||
|
static JucePlayerItemPlaybackStatusObserverClass itemObserverClass;
|
||||||
|
playerItemPlaybackStatusObserver.reset ([itemObserverClass.createInstance() init]);
|
||||||
|
JucePlayerItemPlaybackStatusObserverClass::setOwner (playerItemPlaybackStatusObserver.get(), this);
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void attachPlayerStatusObserver()
|
||||||
|
{
|
||||||
|
[crtp().getPlayer() addObserver: playerStatusObserver.get()
|
||||||
|
forKeyPath: nsStringLiteral ("rate")
|
||||||
|
options: NSKeyValueObservingOptionOld | NSKeyValueObservingOptionNew
|
||||||
|
context: this];
|
||||||
|
|
||||||
|
[crtp().getPlayer() addObserver: playerStatusObserver.get()
|
||||||
|
forKeyPath: nsStringLiteral ("status")
|
||||||
|
options: NSKeyValueObservingOptionNew
|
||||||
|
context: this];
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachPlayerStatusObserver()
|
||||||
|
{
|
||||||
|
if (crtp().getPlayer() != nullptr && playerStatusObserver != nullptr)
|
||||||
|
{
|
||||||
|
[crtp().getPlayer() removeObserver: playerStatusObserver.get()
|
||||||
|
forKeyPath: nsStringLiteral ("rate")
|
||||||
|
context: this];
|
||||||
|
|
||||||
|
[crtp().getPlayer() removeObserver: playerStatusObserver.get()
|
||||||
|
forKeyPath: nsStringLiteral ("status")
|
||||||
|
context: this];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
void attachPlaybackObserver()
|
||||||
|
{
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||||
|
[[NSNotificationCenter defaultCenter] addObserver: playerItemPlaybackStatusObserver.get()
|
||||||
|
selector: @selector (processNotification:)
|
||||||
|
name: AVPlayerItemDidPlayToEndTimeNotification
|
||||||
|
object: [crtp().getPlayer() currentItem]];
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
}
|
||||||
|
|
||||||
|
void detachPlaybackObserver()
|
||||||
|
{
|
||||||
|
#pragma clang diagnostic push
|
||||||
|
#pragma clang diagnostic ignored "-Wundeclared-selector"
|
||||||
|
[[NSNotificationCenter defaultCenter] removeObserver: playerItemPlaybackStatusObserver.get()];
|
||||||
|
#pragma clang diagnostic pop
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================
|
||||||
|
Derived& crtp() { return static_cast<Derived&> (*this); }
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void playerPreparationFinished (const URL& url, Result r, AVPlayer* preparedPlayer)
|
||||||
|
{
|
||||||
|
if (preparedPlayer != nil)
|
||||||
|
crtp().setPlayer (preparedPlayer);
|
||||||
|
|
||||||
|
owner.playerPreparationFinished (url, r);
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackReachedEndTime()
|
||||||
|
{
|
||||||
|
WeakReference<PlayerControllerBase> safeThis (this);
|
||||||
|
|
||||||
|
MessageManager::callAsync ([safeThis]() mutable
|
||||||
|
{
|
||||||
|
if (safeThis != nullptr)
|
||||||
|
safeThis->owner.playbackReachedEndTime();
|
||||||
|
});
|
||||||
|
}
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
void errorOccurred()
|
||||||
|
{
|
||||||
|
auto errorMessage = (crtp().getPlayer() != nil && crtp().getPlayer().error != nil)
|
||||||
|
? nsStringToJuce (crtp().getPlayer().error.localizedDescription)
|
||||||
|
: String();
|
||||||
|
|
||||||
|
owner.errorOccurred (errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackStarted()
|
||||||
|
{
|
||||||
|
owner.playbackStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackStopped()
|
||||||
|
{
|
||||||
|
owner.playbackStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
JUCE_DECLARE_WEAK_REFERENCEABLE (PlayerControllerBase)
|
||||||
|
};
|
||||||
|
|
||||||
|
#if JUCE_MAC
|
||||||
|
//==============================================================================
|
||||||
|
class PlayerController : public PlayerControllerBase<PlayerController>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlayerController (Pimpl& ownerToUse, bool useNativeControlsIfAvailable)
|
||||||
|
: PlayerControllerBase (ownerToUse, useNativeControlsIfAvailable)
|
||||||
|
{
|
||||||
|
#if JUCE_32BIT
|
||||||
|
// 32-bit builds don't have AVPlayerView, so need to use a layer
|
||||||
|
useNativeControls = false;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
if (useNativeControls)
|
||||||
|
{
|
||||||
|
#if ! JUCE_32BIT
|
||||||
|
playerView = [[AVPlayerView alloc] init];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
view = [[NSView alloc] init];
|
||||||
|
playerLayer = [[AVPlayerLayer alloc] init];
|
||||||
|
[view setLayer: playerLayer];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
~PlayerController()
|
||||||
|
{
|
||||||
|
#if JUCE_32BIT
|
||||||
|
[view release];
|
||||||
|
[playerLayer release];
|
||||||
|
#else
|
||||||
|
[playerView release];
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
NSView* getView()
|
||||||
|
{
|
||||||
|
#if ! JUCE_32BIT
|
||||||
|
if (useNativeControls)
|
||||||
|
return playerView;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return view;
|
||||||
|
}
|
||||||
|
|
||||||
|
Result load (NSURL* url)
|
||||||
|
{
|
||||||
|
if (auto* player = [AVPlayer playerWithURL: url])
|
||||||
|
{
|
||||||
|
setPlayer (player);
|
||||||
|
return Result::ok();
|
||||||
|
}
|
||||||
|
|
||||||
|
return Result::fail ("Couldn't open movie");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadAsync (URL url)
|
||||||
|
{
|
||||||
|
playerAsyncInitialiser.loadAsync (url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() { setPlayer (nil); }
|
||||||
|
|
||||||
|
void setPlayer (AVPlayer* player)
|
||||||
|
{
|
||||||
|
#if ! JUCE_32BIT
|
||||||
|
if (useNativeControls)
|
||||||
|
{
|
||||||
|
[playerView setPlayer: player];
|
||||||
|
attachPlayerStatusObserver();
|
||||||
|
attachPlaybackObserver();
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
#endif
|
||||||
|
|
||||||
|
[playerLayer setPlayer: player];
|
||||||
|
attachPlayerStatusObserver();
|
||||||
|
attachPlaybackObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
AVPlayer* getPlayer() const
|
||||||
|
{
|
||||||
|
#if ! JUCE_32BIT
|
||||||
|
if (useNativeControls)
|
||||||
|
return [playerView player];
|
||||||
|
#endif
|
||||||
|
|
||||||
|
return [playerLayer player];
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
NSView* view = nil;
|
||||||
|
AVPlayerLayer* playerLayer = nil;
|
||||||
|
#if ! JUCE_32BIT
|
||||||
|
// 32-bit builds don't have AVPlayerView
|
||||||
|
AVPlayerView* playerView = nil;
|
||||||
|
#endif
|
||||||
|
};
|
||||||
#else
|
#else
|
||||||
AVPlayerView* controller = nil;
|
//==============================================================================
|
||||||
|
class PlayerController : public PlayerControllerBase<PlayerController>
|
||||||
|
{
|
||||||
|
public:
|
||||||
|
PlayerController (Pimpl& ownerToUse, bool useNativeControlsIfAvailable)
|
||||||
|
: PlayerControllerBase (ownerToUse, useNativeControlsIfAvailable)
|
||||||
|
{
|
||||||
|
if (useNativeControls)
|
||||||
|
{
|
||||||
|
playerViewController.reset ([[AVPlayerViewController alloc] init]);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
static JuceVideoViewerClass cls;
|
||||||
|
playerView.reset ([cls.createInstance() init]);
|
||||||
|
|
||||||
|
playerLayer.reset ([[AVPlayerLayer alloc] init]);
|
||||||
|
[playerView.get().layer addSublayer: playerLayer.get()];
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
UIView* getView()
|
||||||
|
{
|
||||||
|
if (useNativeControls)
|
||||||
|
return [playerViewController.get() view];
|
||||||
|
|
||||||
|
// Should call getView() only once.
|
||||||
|
jassert (playerView != nil);
|
||||||
|
return playerView.release();
|
||||||
|
}
|
||||||
|
|
||||||
|
Result load (NSURL*)
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
|
return Result::fail ("Synchronous loading is not supported on iOS, use loadAsync()");
|
||||||
|
}
|
||||||
|
|
||||||
|
void loadAsync (URL url)
|
||||||
|
{
|
||||||
|
playerAsyncInitialiser.loadAsync (url);
|
||||||
|
}
|
||||||
|
|
||||||
|
void close() { setPlayer (nil); }
|
||||||
|
|
||||||
|
AVPlayer* getPlayer() const
|
||||||
|
{
|
||||||
|
if (useNativeControls)
|
||||||
|
return [playerViewController.get() player];
|
||||||
|
|
||||||
|
return [playerLayer.get() player];
|
||||||
|
}
|
||||||
|
|
||||||
|
void setPlayer (AVPlayer* playerToUse)
|
||||||
|
{
|
||||||
|
if (useNativeControls)
|
||||||
|
[playerViewController.get() setPlayer: playerToUse];
|
||||||
|
else
|
||||||
|
[playerLayer.get() setPlayer: playerToUse];
|
||||||
|
|
||||||
|
attachPlayerStatusObserver();
|
||||||
|
attachPlaybackObserver();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
//==============================================================================
|
||||||
|
struct JuceVideoViewerClass : public ObjCClass<UIView>
|
||||||
|
{
|
||||||
|
JuceVideoViewerClass() : ObjCClass<UIView> ("JuceVideoViewerClass_")
|
||||||
|
{
|
||||||
|
addMethod (@selector (layoutSubviews), layoutSubviews, "v@:");
|
||||||
|
|
||||||
|
registerClass();
|
||||||
|
}
|
||||||
|
|
||||||
|
private:
|
||||||
|
static void layoutSubviews (id self, SEL)
|
||||||
|
{
|
||||||
|
sendSuperclassMessage (self, @selector (layoutSubviews));
|
||||||
|
|
||||||
|
UIView* asUIView = (UIView*) self;
|
||||||
|
|
||||||
|
if (auto* previewLayer = getPreviewLayer (self))
|
||||||
|
previewLayer.frame = asUIView.bounds;
|
||||||
|
}
|
||||||
|
|
||||||
|
static AVPlayerLayer* getPreviewLayer (id self)
|
||||||
|
{
|
||||||
|
UIView* asUIView = (UIView*) self;
|
||||||
|
|
||||||
|
if (asUIView.layer.sublayers != nil && [asUIView.layer.sublayers count] > 0)
|
||||||
|
if ([asUIView.layer.sublayers[0] isKindOfClass: [AVPlayerLayer class]])
|
||||||
|
return (AVPlayerLayer*) asUIView.layer.sublayers[0];
|
||||||
|
|
||||||
|
return nil;
|
||||||
|
}
|
||||||
|
};
|
||||||
|
|
||||||
|
//==============================================================================
|
||||||
|
std::unique_ptr<AVPlayerViewController, NSObjectDeleter> playerViewController;
|
||||||
|
|
||||||
|
std::unique_ptr<UIView, NSObjectDeleter> playerView;
|
||||||
|
std::unique_ptr<AVPlayerLayer, NSObjectDeleter> playerLayer;
|
||||||
|
};
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
AVPlayer* getAVPlayer() const noexcept { return [controller player]; }
|
//==============================================================================
|
||||||
|
VideoComponent& owner;
|
||||||
|
|
||||||
|
PlayerController playerController;
|
||||||
|
|
||||||
|
std::function<void (const URL&, Result)> loadFinishedCallback;
|
||||||
|
|
||||||
|
double playSpeedMult = 1.0;
|
||||||
|
|
||||||
static double toSeconds (const CMTime& t) noexcept
|
static double toSeconds (const CMTime& t) noexcept
|
||||||
{
|
{
|
||||||
return t.timescale != 0 ? (t.value / (double) t.timescale) : 0.0;
|
return t.timescale != 0 ? (t.value / (double) t.timescale) : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void playerPreparationFinished (const URL& url, Result r)
|
||||||
|
{
|
||||||
|
owner.resized();
|
||||||
|
|
||||||
|
loadFinishedCallback (url, r);
|
||||||
|
loadFinishedCallback = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorOccurred (const String& errorMessage)
|
||||||
|
{
|
||||||
|
if (owner.onErrorOccurred != nullptr)
|
||||||
|
owner.onErrorOccurred (errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackStarted()
|
||||||
|
{
|
||||||
|
if (owner.onPlaybackStarted != nullptr)
|
||||||
|
owner.onPlaybackStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackStopped()
|
||||||
|
{
|
||||||
|
if (owner.onPlaybackStopped != nullptr)
|
||||||
|
owner.onPlaybackStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackReachedEndTime()
|
||||||
|
{
|
||||||
|
stop();
|
||||||
|
setPosition (0.0);
|
||||||
|
}
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (Pimpl)
|
||||||
};
|
};
|
||||||
|
|
|
||||||
|
|
@ -160,7 +160,9 @@ namespace VideoRenderers
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
struct VideoComponent::Pimpl : public Component
|
struct VideoComponent::Pimpl : public Component
|
||||||
{
|
{
|
||||||
Pimpl() : videoLoaded (false)
|
Pimpl (VideoComponent& ownerToUse, bool)
|
||||||
|
: owner (ownerToUse),
|
||||||
|
videoLoaded (false)
|
||||||
{
|
{
|
||||||
setOpaque (true);
|
setOpaque (true);
|
||||||
context.reset (new DirectShowContext (*this));
|
context.reset (new DirectShowContext (*this));
|
||||||
|
|
@ -257,6 +259,11 @@ struct VideoComponent::Pimpl : public Component
|
||||||
context->setSpeed (newSpeed);
|
context->setSpeed (newSpeed);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double getSpeed() const
|
||||||
|
{
|
||||||
|
return videoLoaded ? context->getSpeed() : 0.0;
|
||||||
|
}
|
||||||
|
|
||||||
Rectangle<int> getNativeSize() const
|
Rectangle<int> getNativeSize() const
|
||||||
{
|
{
|
||||||
return videoLoaded ? context->getVideoSize()
|
return videoLoaded ? context->getVideoSize()
|
||||||
|
|
@ -307,10 +314,30 @@ struct VideoComponent::Pimpl : public Component
|
||||||
repaint();
|
repaint();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void playbackStarted()
|
||||||
|
{
|
||||||
|
if (owner.onPlaybackStarted != nullptr)
|
||||||
|
owner.onPlaybackStarted();
|
||||||
|
}
|
||||||
|
|
||||||
|
void playbackStopped()
|
||||||
|
{
|
||||||
|
if (owner.onPlaybackStopped != nullptr)
|
||||||
|
owner.onPlaybackStopped();
|
||||||
|
}
|
||||||
|
|
||||||
|
void errorOccurred (const String& errorMessage)
|
||||||
|
{
|
||||||
|
if (owner.onErrorOccurred != nullptr)
|
||||||
|
owner.onErrorOccurred (errorMessage);
|
||||||
|
}
|
||||||
|
|
||||||
File currentFile;
|
File currentFile;
|
||||||
URL currentURL;
|
URL currentURL;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
|
VideoComponent& owner;
|
||||||
|
|
||||||
bool videoLoaded;
|
bool videoLoaded;
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
|
|
@ -395,12 +422,15 @@ private:
|
||||||
deleteNativeWindow();
|
deleteNativeWindow();
|
||||||
|
|
||||||
mediaEvent->SetNotifyWindow (0, 0, 0);
|
mediaEvent->SetNotifyWindow (0, 0, 0);
|
||||||
|
|
||||||
if (videoRenderer != nullptr)
|
if (videoRenderer != nullptr)
|
||||||
videoRenderer->setVideoWindow (nullptr);
|
videoRenderer->setVideoWindow (nullptr);
|
||||||
|
|
||||||
createNativeWindow();
|
createNativeWindow();
|
||||||
|
|
||||||
|
mediaEvent->CancelDefaultHandling (EC_STATE_CHANGE);
|
||||||
mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
|
mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
|
||||||
|
|
||||||
if (videoRenderer != nullptr)
|
if (videoRenderer != nullptr)
|
||||||
videoRenderer->setVideoWindow (hwnd);
|
videoRenderer->setVideoWindow (hwnd);
|
||||||
}
|
}
|
||||||
|
|
@ -510,7 +540,10 @@ private:
|
||||||
|
|
||||||
// set window to receive events
|
// set window to receive events
|
||||||
if (SUCCEEDED (hr))
|
if (SUCCEEDED (hr))
|
||||||
|
{
|
||||||
|
mediaEvent->CancelDefaultHandling (EC_STATE_CHANGE);
|
||||||
hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
|
hr = mediaEvent->SetNotifyWindow ((OAHWND) hwnd, graphEventID, 0);
|
||||||
|
}
|
||||||
|
|
||||||
if (SUCCEEDED (hr))
|
if (SUCCEEDED (hr))
|
||||||
{
|
{
|
||||||
|
|
@ -586,22 +619,33 @@ private:
|
||||||
|
|
||||||
switch (ec)
|
switch (ec)
|
||||||
{
|
{
|
||||||
case EC_REPAINT:
|
case EC_REPAINT:
|
||||||
component.repaint();
|
component.repaint();
|
||||||
break;
|
break;
|
||||||
|
|
||||||
case EC_COMPLETE:
|
case EC_COMPLETE:
|
||||||
component.stop();
|
component.stop();
|
||||||
break;
|
component.setPosition (0.0);
|
||||||
|
break;
|
||||||
|
|
||||||
case EC_USERABORT:
|
case EC_ERRORABORT:
|
||||||
case EC_ERRORABORT:
|
case EC_ERRORABORTEX:
|
||||||
case EC_ERRORABORTEX:
|
component.errorOccurred (getErrorMessageFromResult ((HRESULT) p1).getErrorMessage());
|
||||||
component.close();
|
// intentional fallthrough
|
||||||
break;
|
case EC_USERABORT:
|
||||||
|
component.close();
|
||||||
|
break;
|
||||||
|
|
||||||
default:
|
case EC_STATE_CHANGE:
|
||||||
break;
|
switch (p1)
|
||||||
|
{
|
||||||
|
case State_Paused: component.playbackStopped(); break;
|
||||||
|
case State_Running: component.playbackStarted(); break;
|
||||||
|
default: break;
|
||||||
|
}
|
||||||
|
|
||||||
|
default:
|
||||||
|
break;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -644,6 +688,13 @@ private:
|
||||||
return duration;
|
return duration;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
double getSpeed() const
|
||||||
|
{
|
||||||
|
double speed;
|
||||||
|
mediaPosition->get_Rate (&speed);
|
||||||
|
return speed;
|
||||||
|
}
|
||||||
|
|
||||||
double getPosition() const
|
double getPosition() const
|
||||||
{
|
{
|
||||||
REFTIME seconds;
|
REFTIME seconds;
|
||||||
|
|
|
||||||
|
|
@ -25,16 +25,19 @@
|
||||||
namespace juce
|
namespace juce
|
||||||
{
|
{
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS || JUCE_MSVC
|
#if ! JUCE_LINUX
|
||||||
|
|
||||||
#if JUCE_MAC || JUCE_IOS
|
#if JUCE_MAC || JUCE_IOS
|
||||||
#include "../native/juce_mac_Video.h"
|
#include "../native/juce_mac_Video.h"
|
||||||
#elif JUCE_WINDOWS
|
#elif JUCE_WINDOWS
|
||||||
#include "../native/juce_win32_Video.h"
|
#include "../native/juce_win32_Video.h"
|
||||||
|
#elif JUCE_ANDROID
|
||||||
|
#include "../native/juce_android_Video.h"
|
||||||
#endif
|
#endif
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
VideoComponent::VideoComponent() : pimpl (new Pimpl())
|
VideoComponent::VideoComponent (bool useNativeControlsIfAvailable)
|
||||||
|
: pimpl (new Pimpl (*this, useNativeControlsIfAvailable))
|
||||||
{
|
{
|
||||||
addAndMakeVisible (pimpl.get());
|
addAndMakeVisible (pimpl.get());
|
||||||
}
|
}
|
||||||
|
|
@ -46,22 +49,55 @@ VideoComponent::~VideoComponent()
|
||||||
|
|
||||||
Result VideoComponent::load (const File& file)
|
Result VideoComponent::load (const File& file)
|
||||||
{
|
{
|
||||||
|
#if JUCE_ANDROID || JUCE_IOS
|
||||||
|
ignoreUnused (file);
|
||||||
|
jassertfalse;
|
||||||
|
return Result::fail ("load() is not supported on this platform. Use loadAsync() instead.");
|
||||||
|
#else
|
||||||
auto r = pimpl->load (file);
|
auto r = pimpl->load (file);
|
||||||
resized();
|
resized();
|
||||||
return r;
|
return r;
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
Result VideoComponent::load (const URL& url)
|
Result VideoComponent::load (const URL& url)
|
||||||
{
|
{
|
||||||
|
#if JUCE_ANDROID || JUCE_IOS
|
||||||
|
// You need to use loadAsync on Android & iOS.
|
||||||
|
ignoreUnused (url);
|
||||||
|
jassertfalse;
|
||||||
|
return Result::fail ("load() is not supported on this platform. Use loadAsync() instead.");
|
||||||
|
#else
|
||||||
auto r = pimpl->load (url);
|
auto r = pimpl->load (url);
|
||||||
resized();
|
resized();
|
||||||
return r;
|
return r;
|
||||||
|
#endif
|
||||||
|
}
|
||||||
|
|
||||||
|
void VideoComponent::loadAsync (const URL& url, std::function<void (const URL&, Result)> callback)
|
||||||
|
{
|
||||||
|
if (callback == nullptr)
|
||||||
|
{
|
||||||
|
jassertfalse;
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
#if JUCE_ANDROID || JUCE_IOS || JUCE_MAC
|
||||||
|
pimpl->loadAsync (url, callback);
|
||||||
|
#else
|
||||||
|
auto result = load (url);
|
||||||
|
callback (url, result);
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
void VideoComponent::closeVideo()
|
void VideoComponent::closeVideo()
|
||||||
{
|
{
|
||||||
pimpl->close();
|
pimpl->close();
|
||||||
|
// Closing on Android is async and resized() will be called internally by pimpl once
|
||||||
|
// close operation finished.
|
||||||
|
#if ! JUCE_ANDROID// TODO JUCE_IOS too?
|
||||||
resized();
|
resized();
|
||||||
|
#endif
|
||||||
}
|
}
|
||||||
|
|
||||||
bool VideoComponent::isVideoOpen() const { return pimpl->isOpen(); }
|
bool VideoComponent::isVideoOpen() const { return pimpl->isOpen(); }
|
||||||
|
|
@ -81,6 +117,8 @@ void VideoComponent::setPlayPosition (double newPos) { pimpl->setPosition
|
||||||
double VideoComponent::getPlayPosition() const { return pimpl->getPosition(); }
|
double VideoComponent::getPlayPosition() const { return pimpl->getPosition(); }
|
||||||
|
|
||||||
void VideoComponent::setPlaySpeed (double newSpeed) { pimpl->setSpeed (newSpeed); }
|
void VideoComponent::setPlaySpeed (double newSpeed) { pimpl->setSpeed (newSpeed); }
|
||||||
|
double VideoComponent::getPlaySpeed() const { return pimpl->getSpeed(); }
|
||||||
|
|
||||||
void VideoComponent::setAudioVolume (float newVolume) { pimpl->setVolume (newVolume); }
|
void VideoComponent::setAudioVolume (float newVolume) { pimpl->setVolume (newVolume); }
|
||||||
float VideoComponent::getAudioVolume() const { return pimpl->getVolume(); }
|
float VideoComponent::getAudioVolume() const { return pimpl->getVolume(); }
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -44,25 +44,55 @@ public:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Creates an empty VideoComponent.
|
/** Creates an empty VideoComponent.
|
||||||
|
|
||||||
Use the load() method to open a video once you've added this component to
|
Use the loadAsync() or load() method to open a video once you've added
|
||||||
a parent (or put it on the desktop).
|
this component to a parent (or put it on the desktop).
|
||||||
|
|
||||||
|
If useNativeControlsIfAvailable is enabled and a target OS has a video view with
|
||||||
|
dedicated controls for transport etc, that view will be used. In opposite
|
||||||
|
case a bare video view without any controls will be presented, allowing you to
|
||||||
|
tailor your own UI. Currently this flag is used on iOS and 64bit macOS.
|
||||||
|
Android, Windows and 32bit macOS will always use plain video views without
|
||||||
|
dedicated controls.
|
||||||
*/
|
*/
|
||||||
VideoComponent();
|
VideoComponent (bool useNativeControlsIfAvailable);
|
||||||
|
|
||||||
/** Destructor. */
|
/** Destructor. */
|
||||||
~VideoComponent();
|
~VideoComponent();
|
||||||
|
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
/** Tries to load a video from a local file.
|
/** Tries to load a video from a local file.
|
||||||
|
|
||||||
|
This function is supported on macOS and Windows. For iOS and Android, use
|
||||||
|
loadAsync() instead.
|
||||||
|
|
||||||
@returns an error if the file failed to be loaded correctly
|
@returns an error if the file failed to be loaded correctly
|
||||||
|
|
||||||
|
@see loadAsync
|
||||||
*/
|
*/
|
||||||
Result load (const File& file);
|
Result load (const File& file);
|
||||||
|
|
||||||
/** Tries to load a video from a URL.
|
/** Tries to load a video from a URL.
|
||||||
|
|
||||||
|
This function is supported on macOS and Windows. For iOS and Android, use
|
||||||
|
loadAsync() instead.
|
||||||
|
|
||||||
@returns an error if the file failed to be loaded correctly
|
@returns an error if the file failed to be loaded correctly
|
||||||
|
|
||||||
|
@see loadAsync
|
||||||
*/
|
*/
|
||||||
Result load (const URL& url);
|
Result load (const URL& url);
|
||||||
|
|
||||||
|
/** Tries to load a video from a URL asynchronously. When finished, invokes the
|
||||||
|
callback supplied to the function on the message thread.
|
||||||
|
|
||||||
|
This is the preferred way of loading content, since it works not only on
|
||||||
|
macOS and Windows, but also on iOS and Android. On Windows, it will internally
|
||||||
|
call load().
|
||||||
|
|
||||||
|
@see load
|
||||||
|
*/
|
||||||
|
void loadAsync (const URL& url, std::function<void (const URL&, Result)> loadFinishedCallback);
|
||||||
|
|
||||||
/** Closes the video and resets the component. */
|
/** Closes the video and resets the component. */
|
||||||
void closeVideo();
|
void closeVideo();
|
||||||
|
|
||||||
|
|
@ -109,6 +139,9 @@ public:
|
||||||
*/
|
*/
|
||||||
void setPlaySpeed (double newSpeed);
|
void setPlaySpeed (double newSpeed);
|
||||||
|
|
||||||
|
/** Returns the current play speed of the video. */
|
||||||
|
double getPlaySpeed() const;
|
||||||
|
|
||||||
/** Changes the video's playback volume.
|
/** Changes the video's playback volume.
|
||||||
@param newVolume the volume in the range 0 (silent) to 1.0 (full)
|
@param newVolume the volume in the range 0 (silent) to 1.0 (full)
|
||||||
*/
|
*/
|
||||||
|
|
@ -119,6 +152,23 @@ public:
|
||||||
*/
|
*/
|
||||||
float getAudioVolume() const;
|
float getAudioVolume() const;
|
||||||
|
|
||||||
|
#if JUCE_SYNC_VIDEO_VOLUME_WITH_OS_MEDIA_VOLUME
|
||||||
|
/** Set this callback to be notified whenever OS global media volume changes.
|
||||||
|
Currently used on Android only.
|
||||||
|
*/
|
||||||
|
std::function<void()> onGlobalMediaVolumeChanged;
|
||||||
|
#endif
|
||||||
|
|
||||||
|
/** Set this callback to be notified whenever the playback starts. */
|
||||||
|
std::function<void()> onPlaybackStarted;
|
||||||
|
|
||||||
|
/** Set this callback to be notified whenever the playback stops. */
|
||||||
|
std::function<void()> onPlaybackStopped;
|
||||||
|
|
||||||
|
/** Set this callback to be notified whenever an error occurs. Upon error, you
|
||||||
|
may need to load the video again. */
|
||||||
|
std::function<void (const String& /*error*/)> onErrorOccurred;
|
||||||
|
|
||||||
private:
|
private:
|
||||||
//==============================================================================
|
//==============================================================================
|
||||||
struct Pimpl;
|
struct Pimpl;
|
||||||
|
|
@ -129,6 +179,24 @@ private:
|
||||||
void resized() override;
|
void resized() override;
|
||||||
void timerCallback() override;
|
void timerCallback() override;
|
||||||
|
|
||||||
|
#if JUCE_ANDROID
|
||||||
|
friend void juce_surfaceChangedNativeVideo (int64, void*);
|
||||||
|
friend void juce_surfaceDestroyedNativeVideo (int64, void*);
|
||||||
|
|
||||||
|
friend void juce_mediaSessionPause (int64);
|
||||||
|
friend void juce_mediaSessionPlay (int64);
|
||||||
|
friend void juce_mediaSessionPlayFromMediaId (int64, void*, void*);
|
||||||
|
friend void juce_mediaSessionSeekTo (int64, int64);
|
||||||
|
friend void juce_mediaSessionStop (int64);
|
||||||
|
|
||||||
|
friend void juce_mediaControllerAudioInfoChanged (int64, void*);
|
||||||
|
friend void juce_mediaControllerMetadataChanged (int64, void*);
|
||||||
|
friend void juce_mediaControllerPlaybackStateChanged (int64, void*);
|
||||||
|
friend void juce_mediaControllerSessionDestroyed (int64);
|
||||||
|
|
||||||
|
friend void juce_mediaSessionSystemVolumeChanged (int64);
|
||||||
|
#endif
|
||||||
|
|
||||||
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoComponent)
|
JUCE_DECLARE_NON_COPYABLE_WITH_LEAK_DETECTOR (VideoComponent)
|
||||||
};
|
};
|
||||||
|
|
||||||
|
|
|
||||||
Loading…
Add table
Add a link
Reference in a new issue