summaryrefslogtreecommitdiffstats
path: root/src/jogl/classes/jogamp
diff options
context:
space:
mode:
authorSven Gothel <[email protected]>2013-08-27 19:21:17 +0200
committerSven Gothel <[email protected]>2013-08-27 19:21:17 +0200
commit554ec0576432194f050191bdf248a1462d542a6d (patch)
treec8f096f0cca0c07ae23d9d2cd81f91bd9334210f /src/jogl/classes/jogamp
parent0de489019085246abb437454e9ac8fd6fc238692 (diff)
GLMediaPlayer: Add camera input / FFMPEG: Fix 'av_packet' leak and add missing symbol 'av_realloc'.
- Add camera input - Use URI w/ scheme 'camera' to determine camera input is desired, use URI host as camera id. E.g. 'camera://0' for 1st camera. - AndroidGLMediaPlayerAPI14: Via 'Camera' - FFMPEG*: Via libavdevice, device name and input format - TODO: Add controls to manipulate camera if available - FFMPEG* - Add symbols - avcodec_register_all - av_realloc (was missing) - avdevice_register_all - Load libavdevice (opt) - Camera: - Use <ID> (windows) and /dev/video<ID> other OS - simply find the input format in native code - Support YUYV422 (used in video4linux2, etc.) - Stuff 2x 16bpp (YUYV) into one RGBA pixel! - Add texture format for 16bpp - Add texture lookup shader - Fix av_packet leak in readNextImpl(..) - Restore orig pointer and size values, we may have moved along within packet. Then call av_free_packet(). - Use null AudioSink if audio-id is NONE
Diffstat (limited to 'src/jogl/classes/jogamp')
-rw-r--r--src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java115
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java25
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java40
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java126
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java2
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java2
-rw-r--r--src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java2
7 files changed, 244 insertions, 68 deletions
diff --git a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java
index 39489cff4..056998c0c 100644
--- a/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java
+++ b/src/jogl/classes/jogamp/opengl/android/av/AndroidGLMediaPlayerAPI14.java
@@ -28,6 +28,7 @@
package jogamp.opengl.android.av;
import java.io.IOException;
+import java.util.List;
import javax.media.opengl.GL;
import javax.media.opengl.GLES2;
@@ -45,6 +46,7 @@ import jogamp.opengl.util.av.GLMediaPlayerImpl;
import android.graphics.SurfaceTexture;
import android.graphics.SurfaceTexture.OnFrameAvailableListener;
+import android.hardware.Camera;
import android.media.MediaPlayer;
import android.media.MediaPlayer.OnCompletionListener;
import android.net.Uri;
@@ -85,6 +87,8 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
public static final boolean isAvailable() { return available; }
private MediaPlayer mp;
+ private Camera cam;
+ private long playStart = 0;
private volatile boolean updateSurface = false;
private Object updateSurfaceLock = new Object();
private SurfaceTextureFrame singleSTexFrame = null;
@@ -104,7 +108,6 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
throw new RuntimeException("AndroidGLMediaPlayerAPI14 not available");
}
this.setTextureTarget(GLES2.GL_TEXTURE_EXTERNAL_OES);
- mp = new MediaPlayer();
}
@Override
@@ -130,10 +133,11 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
@Override
protected final boolean playImpl() {
+ playStart = Platform.currentTimeMillis();
if(null != mp) {
try {
mp.start();
- eos = false;
+ eos = false;
mp.setOnCompletionListener(onCompletionListener);
return true;
} catch (IllegalStateException ise) {
@@ -141,6 +145,17 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
ise.printStackTrace();
}
}
+ } else if( null != cam ) {
+ try {
+ if( sTexFrameAttached ) {
+ cam.startPreview();
+ }
+ return true;
+ } catch (IllegalStateException ise) {
+ if(DEBUG) {
+ ise.printStackTrace();
+ }
+ }
}
return false;
}
@@ -157,6 +172,16 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
ise.printStackTrace();
}
}
+ } else if( null != cam ) {
+ wakeUp(false);
+ try {
+ cam.stopPreview();
+ return true;
+ } catch (IllegalStateException ise) {
+ if(DEBUG) {
+ ise.printStackTrace();
+ }
+ }
}
return false;
}
@@ -196,6 +221,18 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
mp.release();
mp = null;
}
+ if( null != cam ) {
+ wakeUp(false);
+ try {
+ cam.stopPreview();
+ } catch (IllegalStateException ise) {
+ if(DEBUG) {
+ ise.printStackTrace();
+ }
+ }
+ cam.release();
+ cam = null;
+ }
}
public static class SurfaceTextureFrame extends TextureSequence.TextureFrame {
@@ -212,7 +249,27 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
@Override
protected final void initStreamImpl(int vid, int aid) throws IOException {
- if(null!=mp && null!=streamLoc) {
+
+ if( null == streamLoc ) {
+ return;
+ }
+ if( null == mp && null == cam ) {
+ if( null == cameraHostPart ) {
+ mp = new MediaPlayer();
+ } else {
+ int cameraId = 0;
+ try {
+ cameraId = Integer.valueOf(cameraHostPart);
+ } catch (NumberFormatException nfe) {}
+ if( 0 <= cameraId && cameraId < Camera.getNumberOfCameras() ) {
+ cam = Camera.open(cameraId);
+ } else {
+ cam = Camera.open();
+ }
+ }
+ }
+
+ if(null!=mp) {
if( GLMediaPlayer.STREAM_ID_NONE == aid ) {
mp.setVolume(0f, 0f);
// FIXME: Disable audio handling
@@ -240,8 +297,31 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
mp.getVideoWidth(), mp.getVideoHeight(), 0,
0, 0, 0f,
0, 0, mp.getDuration(), icodec, icodec);
+ } else if( null != cam ) {
+ final String icodec = "android";
+ final int[] fpsRange = { 0, 0 };
+ final Camera.Parameters p = cam.getParameters();
+ p.getPreviewFpsRange(fpsRange);
+ final Camera.Size size = p.getPreviewSize();
+ if( DEBUG ) {
+ final int picFmt = p.getPictureFormat();
+ final Camera.Size prefSize = p.getPreferredPreviewSizeForVideo();
+ System.err.println("MediaPlayer.Camera: fps "+fpsRange[0]+".."+fpsRange[1]+", size[pref "+camSz2Str(prefSize)+", cur "+camSz2Str(size)+"], fmt "+picFmt);
+ List<Camera.Size> supSizes = p.getSupportedVideoSizes();
+ for(int i=0; i<supSizes.size(); i++) {
+ System.err.println("size #"+i+": "+camSz2Str(supSizes.get(i)));
+ }
+ }
+ updateAttributes(0 /* fake */, GLMediaPlayer.STREAM_ID_NONE,
+ size.width, size.height,
+ 0, 0, 0,
+ fpsRange[1]/1000f,
+ 0, 0, 0, icodec, icodec);
}
}
+ private static String camSz2Str(Camera.Size csize) {
+ return csize.width+"x"+csize.height;
+ }
@Override
protected final void initGLImpl(GL gl) throws IOException, GLException {
// NOP
@@ -261,7 +341,7 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
@Override
protected final int getNextTextureImpl(GL gl, TextureFrame nextFrame) {
int pts = TimeFrameI.INVALID_PTS;
- if(null != mp) {
+ if(null != mp || null != cam) {
final SurfaceTextureFrame sTexFrame = (SurfaceTextureFrame) nextFrame;
final SurfaceTexture surfTex = sTexFrame.surfaceTex;
if( sTexFrame != singleSTexFrame ) {
@@ -269,12 +349,25 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
}
if( !sTexFrameAttached ) {
sTexFrameAttached = true;
- final Surface surface = new Surface(sTexFrame.surfaceTex);
- mp.setSurface(surface);
- surface.release();
+ final Surface surface;
+ if( null != mp ) {
+ surface = new Surface(sTexFrame.surfaceTex);
+ mp.setSurface(surface);
+ } else {
+ surface = null;
+ try {
+ cam.setPreviewTexture(sTexFrame.surfaceTex);
+ cam.startPreview();
+ } catch (IOException ioe) {
+ throw new RuntimeException("MediaPlayer failed to process stream <"+streamLoc.toString()+">: "+ioe.getMessage(), ioe);
+ }
+ }
+ if( null != surface ) {
+ surface.release();
+ }
surfTex.setOnFrameAvailableListener(onFrameAvailableListener);
}
- if( eos || !mp.isPlaying() ) {
+ if( eos || (null != mp && !mp.isPlaying() ) ) {
eos = true;
pts = TimeFrameI.END_OF_STREAM_PTS;
} else {
@@ -297,7 +390,11 @@ public class AndroidGLMediaPlayerAPI14 extends GLMediaPlayerImpl {
if(update) {
surfTex.updateTexImage();
// nextFrame.setPTS( (int) ( nextSTex.getTimestamp() / 1000000L ) ); // nano -9 -> milli -3
- pts = mp.getCurrentPosition();
+ if( null != mp ) {
+ pts = mp.getCurrentPosition();
+ } else {
+ pts = (int) ( Platform.currentTimeMillis() - playStart );
+ }
// stex.getTransformMatrix(atex.getSTMatrix());
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
index 73d5e7748..5286c86b8 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/GLMediaPlayerImpl.java
@@ -85,7 +85,14 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
protected int[] texMinMagFilter = { GL.GL_NEAREST, GL.GL_NEAREST };
protected int[] texWrapST = { GL.GL_CLAMP_TO_EDGE, GL.GL_CLAMP_TO_EDGE };
+ /** User requested URI stream location. */
protected URI streamLoc = null;
+ /**
+ * In case {@link #streamLoc} is a {@link GLMediaPlayer#CameraInputScheme},
+ * {@link #cameraHostPart} holds the URI's path portion
+ * as parsed in {@link #initStream(URI, int, int, int)}.
+ */
+ protected String cameraHostPart = null;
protected volatile float playSpeed = 1.0f;
protected float audioVolume = 1.0f;
@@ -463,6 +470,15 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
presentedFrameCount = 0;
displayedFrameCount = 0;
this.streamLoc = streamLoc;
+
+ // Pre-parse for camera-input scheme
+ final String streamLocScheme = streamLoc.getScheme();
+ if( null != streamLocScheme && streamLocScheme.equals(CameraInputScheme) ) {
+ cameraHostPart = streamLoc.getHost();
+ } else {
+ cameraHostPart = null;
+ }
+
this.vid = vid;
this.aid = aid;
if (this.streamLoc != null) {
@@ -598,7 +614,9 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
{
final int err = gl.glGetError();
if( GL.GL_NO_ERROR != err ) {
- throw new RuntimeException("Couldn't create TexImage2D RGBA "+tWidth+"x"+tHeight+", err "+toHexString(err));
+ throw new RuntimeException("Couldn't create TexImage2D RGBA "+tWidth+"x"+tHeight+", target "+toHexString(textureTarget)+
+ ", ifmt "+toHexString(GL.GL_RGBA)+", fmt "+toHexString(textureFormat)+", type "+toHexString(textureType)+
+ ", err "+toHexString(err));
}
}
if(DEBUG) {
@@ -1303,12 +1321,13 @@ public abstract class GLMediaPlayerImpl implements GLMediaPlayer {
final String loc = ( null != streamLoc ) ? streamLoc.toString() : "<undefined stream>" ;
final int freeVideoFrames = null != videoFramesFree ? videoFramesFree.size() : 0;
final int decVideoFrames = null != videoFramesDecoded ? videoFramesDecoded.size() : 0;
- final int video_scr = video_scr_pts + (int) ( ( Platform.currentTimeMillis() - video_scr_t0 ) * playSpeed );
+ final int video_scr = video_scr_pts + (int) ( ( Platform.currentTimeMillis() - video_scr_t0 ) * playSpeed );
+ final String camPath = null != cameraHostPart ? ", camera: "+cameraHostPart : "";
return "GLMediaPlayer["+state+", vSCR "+video_scr+", frames[p "+presentedFrameCount+", d "+decodedFrameCount+", t "+videoFrames+" ("+tt+" s)], "+
"speed "+playSpeed+", "+bps_stream+" bps, "+
"Texture[count "+textureCount+", free "+freeVideoFrames+", dec "+decVideoFrames+", target "+toHexString(textureTarget)+", format "+toHexString(textureFormat)+", type "+toHexString(textureType)+"], "+
"Video[id "+vid+", <"+vcodec+">, "+width+"x"+height+", "+fps+" fps, "+frame_duration+" fdur, "+bps_video+" bps], "+
- "Audio[id "+aid+", <"+acodec+">, "+bps_audio+" bps, "+audioFrames+" frames], uri "+loc+"]";
+ "Audio[id "+aid+", <"+acodec+">, "+bps_audio+" bps, "+audioFrames+" frames], uri "+loc+camPath+"]";
}
@Override
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java
index ab16c5e5e..040d152f5 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGDynamicLibraryBundleInfo.java
@@ -60,7 +60,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
private static final List<String> glueLibNames = new ArrayList<String>(); // none
- private static final int symbolCount = 51;
+ private static final int symbolCount = 54;
private static final String[] symbolNames = {
"avcodec_version",
"avformat_version",
@@ -68,6 +68,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
/* 4 */ "avresample_version",
// libavcodec
+ "avcodec_register_all",
"avcodec_close",
"avcodec_string",
"avcodec_find_decoder",
@@ -85,16 +86,17 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
"av_free_packet",
"avcodec_decode_audio4", // 53.25.0 (opt)
"avcodec_decode_audio3", // 52.23.0
-/* 22 */ "avcodec_decode_video2", // 52.23.0
+/* 23 */ "avcodec_decode_video2", // 52.23.0
// libavutil
"av_pix_fmt_descriptors",
"av_frame_unref", // 55.0.0 (opt)
+ "av_realloc",
"av_free",
"av_get_bits_per_pixel",
"av_samples_get_buffer_size",
"av_get_bytes_per_sample", // 51.4.0
-/* 29 */ "av_opt_set_int", // 51.12.0
+/* 31 */ "av_opt_set_int", // 51.12.0
// libavformat
"avformat_alloc_context",
@@ -113,14 +115,17 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
"avformat_network_init", // 53.13.0 (opt)
"avformat_network_deinit", // 53.13.0 (opt)
"avformat_find_stream_info", // 53.3.0 (opt)
-/* 46 */ "av_find_stream_info",
+/* 48 */ "av_find_stream_info",
+ // libavdevice
+/* 49 */ "avdevice_register_all", // ???
+
// libavresample
"avresample_alloc_context", // 1.0.1
"avresample_open",
"avresample_close",
"avresample_free",
-/* 51 */ "avresample_convert"
+/* 54 */ "avresample_convert"
};
// alternate symbol names
@@ -139,7 +144,8 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
"avformat_seek_file", // ??? (opt)
"avcodec_free_frame", // 54.28.0 (opt)
"av_frame_unref", // 55.0.0 (opt)
-
+ // libavdevice
+ "avdevice_register_all", // 53.0.0 (opt)
// libavresample
"avresample_version", // 1.0.1
"avresample_alloc_context", // 1.0.1
@@ -153,6 +159,7 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
private static final boolean ready;
private static final boolean libsLoaded;
private static final boolean avresampleLoaded; // optional
+ private static final boolean avdeviceLoaded; // optional
static final VersionNumber avCodecVersion;
static final VersionNumber avFormatVersion;
static final VersionNumber avUtilVersion;
@@ -164,14 +171,16 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
GLProfile.initSingleton();
boolean _ready = false;
boolean[] _libsLoaded= { false };
+ boolean[] _avdeviceLoaded= { false };
boolean[] _avresampleLoaded= { false };
VersionNumber[] _versions = new VersionNumber[4];
try {
- _ready = initSymbols(_libsLoaded, _avresampleLoaded, _versions);
+ _ready = initSymbols(_libsLoaded, _avdeviceLoaded, _avresampleLoaded, _versions);
} catch (Throwable t) {
t.printStackTrace();
}
libsLoaded = _libsLoaded[0];
+ avdeviceLoaded = _avdeviceLoaded[0];
avresampleLoaded = _avresampleLoaded[0];
avCodecVersion = _versions[0];
avFormatVersion = _versions[1];
@@ -205,11 +214,13 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
}
static boolean libsLoaded() { return libsLoaded; }
+ static boolean avDeviceLoaded() { return avdeviceLoaded; }
static boolean avResampleLoaded() { return avresampleLoaded; }
static FFMPEGNatives getNatives() { return natives; }
static boolean initSingleton() { return ready; }
- private static final boolean initSymbols(boolean[] libsLoaded, boolean[] avresampleLoaded, VersionNumber[] versions) {
+ private static final boolean initSymbols(boolean[] libsLoaded, boolean[] avdeviceLoaded, boolean[] avresampleLoaded,
+ VersionNumber[] versions) {
libsLoaded[0] = false;
final DynamicLibraryBundle dl = AccessController.doPrivileged(new PrivilegedAction<DynamicLibraryBundle>() {
public DynamicLibraryBundle run() {
@@ -221,7 +232,8 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
if(!avutilLoaded || !avformatLoaded || !avcodecLoaded) {
throw new RuntimeException("FFMPEG Tool library incomplete: [ avutil "+avutilLoaded+", avformat "+avformatLoaded+", avcodec "+avcodecLoaded+"]");
}
- avresampleLoaded[0] = dl.isToolLibLoaded(3);
+ avdeviceLoaded[0] = dl.isToolLibLoaded(3);
+ avresampleLoaded[0] = dl.isToolLibLoaded(4);
libsLoaded[0] = true;
if(symbolNames.length != symbolCount) {
@@ -361,6 +373,16 @@ class FFMPEGDynamicLibraryBundleInfo implements DynamicLibraryBundleInfo {
avcodec.add("avcodec-52"); // 0.7
libsList.add(avcodec);
+ final List<String> avdevice = new ArrayList<String>();
+ avdevice.add("avdevice"); // default
+
+ avdevice.add("libavdevice.so.54"); // dummy future proof
+ avdevice.add("libavdevice.so.53"); // 8 && 9
+
+ avdevice.add("avdevice-54"); // dummy future proof
+ avdevice.add("avdevice-53"); // 8 && 9
+ libsList.add(avdevice);
+
final List<String> avresample = new ArrayList<String>();
avresample.add("avresample"); // default
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
index bcf4994b5..f46c5900c 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGMediaPlayer.java
@@ -129,7 +129,8 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
System.err.println("LIB_AV Util : "+FFMPEGDynamicLibraryBundleInfo.avUtilVersion+" [cc "+avUtilMajorVersionCC+"]");
System.err.println("LIB_AV Format : "+FFMPEGDynamicLibraryBundleInfo.avFormatVersion+" [cc "+avFormatMajorVersionCC+"]");
System.err.println("LIB_AV Codec : "+FFMPEGDynamicLibraryBundleInfo.avCodecVersion+" [cc "+avCodecMajorVersionCC+"]");
- System.err.println("LIB_AV Resample: "+FFMPEGDynamicLibraryBundleInfo.avResampleVersion+" [cc "+avResampleMajorVersionCC+"]");
+ System.err.println("LIB_AV Device : [loaded "+FFMPEGDynamicLibraryBundleInfo.avDeviceLoaded()+"]");
+ System.err.println("LIB_AV Resample: "+FFMPEGDynamicLibraryBundleInfo.avResampleVersion+" [cc "+avResampleMajorVersionCC+", loaded "+FFMPEGDynamicLibraryBundleInfo.avResampleLoaded()+"]");
libAVVersionGood = avUtilMajorVersionCC == FFMPEGDynamicLibraryBundleInfo.avUtilVersion.getMajor() &&
avFormatMajorVersionCC == FFMPEGDynamicLibraryBundleInfo.avFormatVersion.getMajor() &&
avCodecMajorVersionCC == FFMPEGDynamicLibraryBundleInfo.avCodecVersion.getMajor() &&
@@ -160,6 +161,8 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
// Video
//
+ private String texLookupFuncName = "ffmpegTexture2D";
+ private boolean usesTexLookupShader = false;
private PixelFormat vPixelFmt = null;
private int vPlanes = 0;
private int vBitsPerPixel = 0;
@@ -205,8 +208,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
}
}
- public static final String dev_video = "/dev/video";
- private static final int dev_video_len = dev_video.length();
+ public static final String dev_video_linux = "/dev/video";
@Override
protected final void initStreamImpl(int vid, int aid) throws IOException {
@@ -229,40 +231,34 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
System.err.println("initStream: p2 preferred "+preferredAudioFormat+", "+this);
}
- final int streamLocSLen = streamLocS.length();
- final String inFormat;
+ final boolean isCameraInput = null != cameraHostPart;
final String resStreamLocS;
- if( streamLocSLen == dev_video_len + 1 && streamLocS.startsWith(dev_video) ) {
- final int index = Integer.valueOf( streamLocS.substring(streamLocSLen-1) ).intValue();
+ if( isCameraInput ) {
switch(Platform.OS_TYPE) {
- case ANDROID:
- // ??
- case FREEBSD:
- case HPUX:
- case LINUX:
- case SUNOS:
- resStreamLocS = streamLocS;
- inFormat = "video4linux2";
- break;
- case WINDOWS:
- resStreamLocS = String.valueOf(index);
- inFormat = "vfwcap";
- break;
- case MACOS:
- case OPENKODE:
- default:
- resStreamLocS = streamLocS;
- inFormat = null;
- break;
+ case ANDROID:
+ // ??
+ case FREEBSD:
+ case HPUX:
+ case LINUX:
+ case SUNOS:
+ resStreamLocS = dev_video_linux + cameraHostPart;
+ break;
+ case WINDOWS:
+ resStreamLocS = cameraHostPart;
+ break;
+ case MACOS:
+ case OPENKODE:
+ default:
+ resStreamLocS = streamLocS; // FIXME: ??
+ break;
}
} else {
resStreamLocS = streamLocS;
- inFormat = null;
}
final int aMaxChannelCount = audioSink.getMaxSupportedChannels();
final int aPrefSampleRate = preferredAudioFormat.sampleRate;
// setStream(..) issues updateAttributes*(..), and defines avChosenAudioFormat, vid, aid, .. etc
- natives.setStream0(moviePtr, resStreamLocS, inFormat, vid, aid, aMaxChannelCount, aPrefSampleRate);
+ natives.setStream0(moviePtr, resStreamLocS, isCameraInput, vid, aid, aMaxChannelCount, aPrefSampleRate);
}
@Override
@@ -300,6 +296,10 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
System.err.println("initGL: p3 avChosen "+avChosenAudioFormat);
}
+ if( STREAM_ID_NONE == aid ) {
+ audioSink.destroy();
+ audioSink = AudioSinkFactory.createNull();
+ }
final boolean audioSinkOK = audioSink.init(avChosenAudioFormat, frameDuration, AudioSink.DefaultInitialQueueSize, AudioSink.DefaultQueueGrowAmount, audioQueueLimit);
if( !audioSinkOK ) {
System.err.println("AudioSink "+audioSink.getClass().getName()+" does not support "+avChosenAudioFormat+", using Null");
@@ -314,6 +314,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if( null != gl ) {
int tf, tif=GL.GL_RGBA; // texture format and internal format
+ int tt = GL.GL_UNSIGNED_BYTE;
switch(vBytesPerPixelPerPlane) {
case 1:
if( gl.isGL3ES3() ) {
@@ -324,12 +325,20 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
tf = GL2ES2.GL_ALPHA; tif=GL2ES2.GL_ALPHA; singleTexComp = "a";
}
break;
- case 3: tf = GL2ES2.GL_RGB; tif=GL.GL_RGB; break;
- case 4: tf = GL2ES2.GL_RGBA; tif=GL.GL_RGBA; break;
+
+ case 2: if( vPixelFmt == PixelFormat.YUYV422 ) {
+ // YUYV422: // < packed YUV 4:2:2, 2x 16bpp, Y0 Cb Y1 Cr
+ // Stuffed into RGBA half width texture
+ tf = GL2ES2.GL_RGBA; tif=GL2ES2.GL_RGBA; break;
+ } else {
+ tf = GL2ES2.GL_RG; tif=GL2ES2.GL_RG; break;
+ }
+ case 3: tf = GL2ES2.GL_RGB; tif=GL.GL_RGB; break;
+ case 4: tf = GL2ES2.GL_RGBA; tif=GL.GL_RGBA; break;
default: throw new RuntimeException("Unsupported bytes-per-pixel / plane "+vBytesPerPixelPerPlane);
}
setTextureFormat(tif, tf);
- setTextureType(GL.GL_UNSIGNED_BYTE);
+ setTextureType(tt);
}
}
@Override
@@ -435,7 +444,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
*/
void updateAttributes2(int pixFmt, int planes, int bitsPerPixel, int bytesPerPixelPerPlane,
int lSz0, int lSz1, int lSz2,
- int tWd0, int tWd1, int tWd2, int tH,
+ int tWd0, int tWd1, int tWd2, int vW, int vH,
int audioSampleFmt, int audioSampleRate,
int audioChannels, int audioSamplesPerFrameAndChannel) {
vPixelFmt = PixelFormat.valueOf(pixFmt);
@@ -446,23 +455,28 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
vTexWidth[0] = tWd0; vTexWidth[1] = tWd1; vTexWidth[2] = tWd2;
switch(vPixelFmt) {
- case YUV420P:
+ case YUV420P: // < planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
+ usesTexLookupShader = true;
// YUV420P: Adding U+V on right side of fixed height texture,
// since width is already aligned by decoder.
// Y=w*h, Y=w/2*h/2, U=w/2*h/2
// w*h + 2 ( w/2 * h/2 )
// w*h + w*h/2
// 2*w/2 * h
- texWidth = vTexWidth[0] + vTexWidth[1]; texHeight = tH;
+ texWidth = vTexWidth[0] + vTexWidth[1]; texHeight = vH;
+ break;
+ case YUYV422: // < packed YUV 4:2:2, 2x 16bpp, Y0 Cb Y1 Cr - stuffed into RGBA half width texture
+ usesTexLookupShader = true;
+ texWidth = vTexWidth[0]; texHeight = vH;
break;
- // case PIX_FMT_YUYV422:
case RGB24:
case BGR24:
case ARGB:
case RGBA:
case ABGR:
case BGRA:
- texWidth = vTexWidth[0]; texHeight = tH;
+ usesTexLookupShader = false;
+ texWidth = vTexWidth[0]; texHeight = vH;
break;
default: // FIXME: Add more formats !
throw new RuntimeException("Unsupported pixelformat: "+vPixelFmt);
@@ -474,7 +488,7 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if(DEBUG) {
System.err.println("audio: fmt "+aSampleFmt+", "+avChosenAudioFormat+", aFrameSize/fc "+audioSamplesPerFrameAndChannel);
- System.err.println("video: fmt "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane);
+ System.err.println("video: fmt "+vW+"x"+vH+", "+vPixelFmt+", planes "+vPlanes+", bpp "+vBitsPerPixel+"/"+vBytesPerPixelPerPlane+", usesTexLookupShader "+usesTexLookupShader);
for(int i=0; i<3; i++) {
System.err.println("video: "+i+": "+vTexWidth[i]+"/"+vLinesize[i]);
}
@@ -494,15 +508,14 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if(State.Uninitialized == state) {
throw new IllegalStateException("Instance not initialized: "+this);
}
- if(PixelFormat.YUV420P == vPixelFmt) {
+ if( usesTexLookupShader ) {
if(null != desiredFuncName && desiredFuncName.length()>0) {
- textureLookupFunctionName = desiredFuncName;
+ texLookupFuncName = desiredFuncName;
}
- return textureLookupFunctionName;
+ return texLookupFuncName;
}
return super.getTextureLookupFunctionName(desiredFuncName);
}
- private String textureLookupFunctionName = "ffmpegTexture2D";
/**
* {@inheritDoc}
@@ -515,11 +528,14 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
if(State.Uninitialized == state) {
throw new IllegalStateException("Instance not initialized: "+this);
}
+ if( !usesTexLookupShader ) {
+ return super.getTextureLookupFragmentShaderImpl();
+ }
final float tc_w_1 = (float)getWidth() / (float)texWidth;
switch(vPixelFmt) {
- case YUV420P:
+ case YUV420P: // < planar YUV 4:2:0, 12bpp, (1 Cr & Cb sample per 2x2 Y samples)
return
- "vec4 "+textureLookupFunctionName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+
+ "vec4 "+texLookupFuncName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+
" vec2 u_off = vec2("+tc_w_1+", 0.0);\n"+
" vec2 v_off = vec2("+tc_w_1+", 0.5);\n"+
" vec2 tc_half = texCoord*0.5;\n"+
@@ -536,8 +552,30 @@ public class FFMPEGMediaPlayer extends GLMediaPlayerImpl {
" return vec4(r, g, b, 1);\n"+
"}\n"
;
+ case YUYV422: // < packed YUV 4:2:2, 2 x 16bpp, [Y0 Cb] [Y1 Cr]
+ // Stuffed into RGBA half width texture
+ return
+ "vec4 "+texLookupFuncName+"(in "+getTextureSampler2DType()+" image, in vec2 texCoord) {\n"+
+ " "+
+ " float y1,u,y2,v,y,r,g,b;\n"+
+ " vec2 tc_halfw = vec2(texCoord.x*0.5, texCoord.y);\n"+
+ " vec4 yuyv = texture2D(image, tc_halfw).rgba;\n"+
+ " y1 = yuyv.r;\n"+
+ " u = yuyv.g;\n"+
+ " y2 = yuyv.b;\n"+
+ " v = yuyv.a;\n"+
+ " y = mix( y1, y2, mod(gl_FragCoord.x, 2) ); /* avoid branching! */\n"+
+ " y = 1.1643*(y-0.0625);\n"+
+ " u = u-0.5;\n"+
+ " v = v-0.5;\n"+
+ " r = y+1.5958*v;\n"+
+ " g = y-0.39173*u-0.81290*v;\n"+
+ " b = y+2.017*u;\n"+
+ " return vec4(r, g, b, 1);\n"+
+ "}\n"
+ ;
default: // FIXME: Add more formats !
- return super.getTextureLookupFragmentShaderImpl();
+ throw new InternalError("Add proper mapping of: vPixelFmt "+vPixelFmt+", usesTexLookupShader "+usesTexLookupShader);
}
}
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java
index 89cefbf17..9dd1ac74a 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGNatives.java
@@ -59,7 +59,7 @@ interface FFMPEGNatives {
* @param aPrefChannelCount
* @param aPrefSampleRate
*/
- void setStream0(long moviePtr, String url, String inFormat, int vid, int aid, int aMaxChannelCount, int aPrefSampleRate);
+ void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, int aid, int aMaxChannelCount, int aPrefSampleRate);
void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish);
int getVideoPTS0(long moviePtr);
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java
index 1c0f66e0c..3b2567655 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv08Natives.java
@@ -53,7 +53,7 @@ class FFMPEGv08Natives implements FFMPEGNatives {
public native void destroyInstance0(long moviePtr);
@Override
- public native void setStream0(long moviePtr, String url, String inFormat, int vid, int aid, int aMaxChannelCount, int aPrefSampleRate);
+ public native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, int aid, int aMaxChannelCount, int aPrefSampleRate);
@Override
public native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish);
diff --git a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java
index 0bc26923c..6c56d3ccb 100644
--- a/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java
+++ b/src/jogl/classes/jogamp/opengl/util/av/impl/FFMPEGv09Natives.java
@@ -53,7 +53,7 @@ class FFMPEGv09Natives implements FFMPEGNatives {
public native void destroyInstance0(long moviePtr);
@Override
- public native void setStream0(long moviePtr, String url, String inFormat, int vid, int aid, int aMaxChannelCount, int aPrefSampleRate);
+ public native void setStream0(long moviePtr, String url, boolean isCameraInput, int vid, int aid, int aMaxChannelCount, int aPrefSampleRate);
@Override
public native void setGLFuncs0(long moviePtr, long procAddrGLTexSubImage2D, long procAddrGLGetError, long procAddrGLFlush, long procAddrGLFinish);