From 2a34e88c1e1ced28e75c487cd13402e1c9cf9fa3 Mon Sep 17 00:00:00 2001 From: Michael Goddard Date: Wed, 29 Jun 2011 13:38:46 +1000 Subject: Initial copy of QtMultimediaKit. Comes from original repo, with SHA1: 2c82d5611655e5967f5c5095af50c0991c4378b2 --- src/plugins/audiocapture/audiocapture.pro | 25 + src/plugins/audiocapture/audiocaptureservice.cpp | 90 + src/plugins/audiocapture/audiocaptureservice.h | 74 + .../audiocapture/audiocaptureserviceplugin.cpp | 69 + .../audiocapture/audiocaptureserviceplugin.h | 60 + src/plugins/audiocapture/audiocapturesession.cpp | 358 +++ src/plugins/audiocapture/audiocapturesession.h | 152 + src/plugins/audiocapture/audiocontainercontrol.cpp | 74 + src/plugins/audiocapture/audiocontainercontrol.h | 70 + src/plugins/audiocapture/audioencodercontrol.cpp | 168 ++ src/plugins/audiocapture/audioencodercontrol.h | 79 + src/plugins/audiocapture/audioendpointselector.cpp | 110 + src/plugins/audiocapture/audioendpointselector.h | 77 + .../audiocapture/audiomediarecordercontrol.cpp | 102 + .../audiocapture/audiomediarecordercontrol.h | 82 + src/plugins/directshow/camera/camera.pri | 31 + src/plugins/directshow/camera/directshowglobal.h | 236 ++ src/plugins/directshow/camera/dscameracontrol.cpp | 103 + src/plugins/directshow/camera/dscameracontrol.h | 94 + src/plugins/directshow/camera/dscameraservice.cpp | 114 + src/plugins/directshow/camera/dscameraservice.h | 88 + src/plugins/directshow/camera/dscamerasession.cpp | 1160 ++++++++ src/plugins/directshow/camera/dscamerasession.h | 208 ++ .../directshow/camera/dsimagecapturecontrol.cpp | 83 + .../directshow/camera/dsimagecapturecontrol.h | 80 + .../directshow/camera/dsvideodevicecontrol.cpp | 168 ++ .../directshow/camera/dsvideodevicecontrol.h | 83 + src/plugins/directshow/camera/dsvideorenderer.cpp | 72 + src/plugins/directshow/camera/dsvideorenderer.h | 77 + .../directshow/camera/dsvideowidgetcontrol.cpp | 250 ++ .../directshow/camera/dsvideowidgetcontrol.h | 154 + src/plugins/directshow/directshow.pro | 23 + src/plugins/directshow/dsserviceplugin.cpp | 211 ++ src/plugins/directshow/dsserviceplugin.h | 76 + .../player/directshowaudioendpointcontrol.cpp | 161 ++ .../player/directshowaudioendpointcontrol.h | 82 + .../directshow/player/directshoweventloop.cpp | 150 + .../directshow/player/directshoweventloop.h | 78 + src/plugins/directshow/player/directshowglobal.h | 139 + .../directshow/player/directshowioreader.cpp | 496 ++++ src/plugins/directshow/player/directshowioreader.h | 120 + .../directshow/player/directshowiosource.cpp | 639 +++++ src/plugins/directshow/player/directshowiosource.h | 149 + .../directshow/player/directshowmediatype.cpp | 184 ++ .../directshow/player/directshowmediatype.h | 74 + .../directshow/player/directshowmediatypelist.cpp | 226 ++ .../directshow/player/directshowmediatypelist.h | 69 + .../player/directshowmetadatacontrol.cpp | 352 +++ .../directshow/player/directshowmetadatacontrol.h | 93 + .../directshow/player/directshowpinenum.cpp | 134 + src/plugins/directshow/player/directshowpinenum.h | 72 + .../directshow/player/directshowplayercontrol.cpp | 405 +++ .../directshow/player/directshowplayercontrol.h | 146 + .../directshow/player/directshowplayerservice.cpp | 1408 +++++++++ .../directshow/player/directshowplayerservice.h | 219 ++ .../player/directshowsamplescheduler.cpp | 437 +++ .../directshow/player/directshowsamplescheduler.h | 117 + .../player/directshowvideorenderercontrol.cpp | 86 + .../player/directshowvideorenderercontrol.h | 75 + .../directshow/player/mediasamplevideobuffer.cpp | 86 + .../directshow/player/mediasamplevideobuffer.h | 69 + src/plugins/directshow/player/player.pri | 47 + .../directshow/player/videosurfacefilter.cpp | 631 +++++ src/plugins/directshow/player/videosurfacefilter.h | 176 ++ .../directshow/player/vmr9videowindowcontrol.cpp | 329 +++ .../directshow/player/vmr9videowindowcontrol.h | 108 + src/plugins/gstreamer/camerabin/camerabin.pri | 50 + .../gstreamer/camerabin/camerabinaudioencoder.cpp | 293 ++ .../gstreamer/camerabin/camerabinaudioencoder.h | 105 + .../camerabin/camerabincapturebufferformat.cpp | 78 + .../camerabin/camerabincapturebufferformat.h | 72 + .../camerabin/camerabincapturedestination.cpp | 74 + .../camerabin/camerabincapturedestination.h | 69 + .../gstreamer/camerabin/camerabincontainer.cpp | 122 + .../gstreamer/camerabin/camerabincontainer.h | 103 + .../gstreamer/camerabin/camerabincontrol.cpp | 356 +++ src/plugins/gstreamer/camerabin/camerabincontrol.h | 101 + .../gstreamer/camerabin/camerabinexposure.cpp | 232 ++ .../gstreamer/camerabin/camerabinexposure.h | 83 + src/plugins/gstreamer/camerabin/camerabinflash.cpp | 104 + src/plugins/gstreamer/camerabin/camerabinflash.h | 73 + src/plugins/gstreamer/camerabin/camerabinfocus.cpp | 245 ++ src/plugins/gstreamer/camerabin/camerabinfocus.h | 104 + .../gstreamer/camerabin/camerabinimagecapture.cpp | 347 +++ .../gstreamer/camerabin/camerabinimagecapture.h | 82 + .../gstreamer/camerabin/camerabinimageencoder.cpp | 87 + .../gstreamer/camerabin/camerabinimageencoder.h | 86 + .../camerabin/camerabinimageprocessing.cpp | 171 ++ .../gstreamer/camerabin/camerabinimageprocessing.h | 84 + src/plugins/gstreamer/camerabin/camerabinlocks.cpp | 88 + src/plugins/gstreamer/camerabin/camerabinlocks.h | 79 + .../gstreamer/camerabin/camerabinmetadata.cpp | 198 ++ .../gstreamer/camerabin/camerabinmetadata.h | 75 + .../gstreamer/camerabin/camerabinrecorder.cpp | 225 ++ .../gstreamer/camerabin/camerabinrecorder.h | 85 + .../camerabin/camerabinresourcepolicy.cpp | 188 ++ .../gstreamer/camerabin/camerabinresourcepolicy.h | 84 + .../gstreamer/camerabin/camerabinservice.cpp | 261 ++ src/plugins/gstreamer/camerabin/camerabinservice.h | 96 + .../gstreamer/camerabin/camerabinsession.cpp | 1267 +++++++++ src/plugins/gstreamer/camerabin/camerabinsession.h | 234 ++ .../gstreamer/camerabin/camerabinvideoencoder.cpp | 346 +++ .../gstreamer/camerabin/camerabinvideoencoder.h | 106 + .../gstreamer/camerabuttonlistener_maemo.cpp | 121 + src/plugins/gstreamer/camerabuttonlistener_maemo.h | 64 + .../gstreamer/camerabuttonlistener_meego.cpp | 92 + src/plugins/gstreamer/camerabuttonlistener_meego.h | 65 + src/plugins/gstreamer/gstreamer.pro | 101 + src/plugins/gstreamer/gstvideoconnector.c | 421 +++ src/plugins/gstreamer/gstvideoconnector.h | 87 + .../gstreamer/mediacapture/mediacapture.pri | 27 + .../mediacapture/qgstreameraudioencode.cpp | 292 ++ .../gstreamer/mediacapture/qgstreameraudioencode.h | 97 + .../mediacapture/qgstreamercameracontrol.cpp | 185 ++ .../mediacapture/qgstreamercameracontrol.h | 98 + .../qgstreamercapturemetadatacontrol.cpp | 198 ++ .../qgstreamercapturemetadatacontrol.h | 75 + .../mediacapture/qgstreamercaptureservice.cpp | 185 ++ .../mediacapture/qgstreamercaptureservice.h | 96 + .../mediacapture/qgstreamercapturesession.cpp | 1051 +++++++ .../mediacapture/qgstreamercapturesession.h | 211 ++ .../mediacapture/qgstreamerimagecapturecontrol.cpp | 98 + .../mediacapture/qgstreamerimagecapturecontrol.h | 73 + .../mediacapture/qgstreamerimageencode.cpp | 90 + .../gstreamer/mediacapture/qgstreamerimageencode.h | 80 + .../qgstreamermediacontainercontrol.cpp | 135 + .../mediacapture/qgstreamermediacontainercontrol.h | 84 + .../mediacapture/qgstreamerrecordercontrol.cpp | 289 ++ .../mediacapture/qgstreamerrecordercontrol.h | 91 + .../gstreamer/mediacapture/qgstreamerv4l2input.cpp | 297 ++ .../gstreamer/mediacapture/qgstreamerv4l2input.h | 83 + .../mediacapture/qgstreamervideoencode.cpp | 331 +++ .../gstreamer/mediacapture/qgstreamervideoencode.h | 98 + src/plugins/gstreamer/mediaplayer/mediaplayer.pri | 30 + .../gstreamer/mediaplayer/playerresourcepolicy.cpp | 180 ++ .../gstreamer/mediaplayer/playerresourcepolicy.h | 90 + src/plugins/gstreamer/mediaplayer/qgstappsrc.cpp | 224 ++ src/plugins/gstreamer/mediaplayer/qgstappsrc.h | 106 + .../mediaplayer/qgstreamermetadataprovider.cpp | 192 ++ .../mediaplayer/qgstreamermetadataprovider.h | 74 + .../mediaplayer/qgstreamerplayercontrol.cpp | 748 +++++ .../mediaplayer/qgstreamerplayercontrol.h | 157 + .../mediaplayer/qgstreamerplayerservice.cpp | 134 + .../mediaplayer/qgstreamerplayerservice.h | 90 + .../mediaplayer/qgstreamerplayersession.cpp | 1537 ++++++++++ .../mediaplayer/qgstreamerplayersession.h | 217 ++ .../mediaplayer/qgstreamerstreamscontrol.cpp | 89 + .../mediaplayer/qgstreamerstreamscontrol.h | 71 + src/plugins/gstreamer/qabstractgstbufferpool.h | 75 + .../qgstreameraudioinputendpointselector.cpp | 174 ++ .../qgstreameraudioinputendpointselector.h | 76 + src/plugins/gstreamer/qgstreamerbushelper.cpp | 203 ++ src/plugins/gstreamer/qgstreamerbushelper.h | 77 + .../gstreamer/qgstreamergltexturerenderer.cpp | 578 ++++ .../gstreamer/qgstreamergltexturerenderer.h | 127 + src/plugins/gstreamer/qgstreamermessage.cpp | 93 + src/plugins/gstreamer/qgstreamermessage.h | 68 + src/plugins/gstreamer/qgstreamerserviceplugin.cpp | 401 +++ src/plugins/gstreamer/qgstreamerserviceplugin.h | 86 + .../qgstreamervideoinputdevicecontrol.cpp | 162 ++ .../gstreamer/qgstreamervideoinputdevicecontrol.h | 77 + src/plugins/gstreamer/qgstreamervideooverlay.cpp | 232 ++ src/plugins/gstreamer/qgstreamervideooverlay.h | 119 + src/plugins/gstreamer/qgstreamervideorenderer.cpp | 120 + src/plugins/gstreamer/qgstreamervideorenderer.h | 80 + .../gstreamer/qgstreamervideorendererinterface.cpp | 46 + .../gstreamer/qgstreamervideorendererinterface.h | 79 + src/plugins/gstreamer/qgstreamervideowidget.cpp | 331 +++ src/plugins/gstreamer/qgstreamervideowidget.h | 108 + src/plugins/gstreamer/qgstreamervideowindow.cpp | 342 +++ src/plugins/gstreamer/qgstreamervideowindow.h | 131 + src/plugins/gstreamer/qgstutils.cpp | 165 ++ src/plugins/gstreamer/qgstutils.h | 59 + src/plugins/gstreamer/qgstvideobuffer.cpp | 97 + src/plugins/gstreamer/qgstvideobuffer.h | 72 + src/plugins/gstreamer/qgstxvimagebuffer.cpp | 311 ++ src/plugins/gstreamer/qgstxvimagebuffer.h | 130 + src/plugins/gstreamer/qvideosurfacegstsink.cpp | 772 +++++ src/plugins/gstreamer/qvideosurfacegstsink.h | 162 ++ src/plugins/gstreamer/qx11videosurface.cpp | 535 ++++ src/plugins/gstreamer/qx11videosurface.h | 117 + src/plugins/m3u/m3u.pro | 23 + src/plugins/m3u/main.cpp | 47 + src/plugins/m3u/qm3uhandler.cpp | 237 ++ src/plugins/m3u/qm3uhandler.h | 70 + src/plugins/plugins.pro | 46 + src/plugins/pulseaudio/pulseaudio.pro | 26 + src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp | 105 + src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h | 92 + src/plugins/pulseaudio/qaudioinput_pulse.cpp | 600 ++++ src/plugins/pulseaudio/qaudioinput_pulse.h | 153 + src/plugins/pulseaudio/qaudiooutput_pulse.cpp | 573 ++++ src/plugins/pulseaudio/qaudiooutput_pulse.h | 153 + src/plugins/pulseaudio/qpulseaudioengine.cpp | 354 +++ src/plugins/pulseaudio/qpulseaudioengine.h | 100 + src/plugins/pulseaudio/qpulseaudioplugin.cpp | 89 + src/plugins/pulseaudio/qpulseaudioplugin.h | 71 + src/plugins/pulseaudio/qpulsehelpers.cpp | 220 ++ src/plugins/pulseaudio/qpulsehelpers.h | 73 + src/plugins/qt7/mediaplayer/mediaplayer.pri | 16 + src/plugins/qt7/mediaplayer/qt7playercontrol.h | 109 + src/plugins/qt7/mediaplayer/qt7playercontrol.mm | 191 ++ src/plugins/qt7/mediaplayer/qt7playermetadata.h | 77 + src/plugins/qt7/mediaplayer/qt7playermetadata.mm | 260 ++ src/plugins/qt7/mediaplayer/qt7playerservice.h | 82 + src/plugins/qt7/mediaplayer/qt7playerservice.mm | 129 + src/plugins/qt7/mediaplayer/qt7playersession.h | 194 ++ src/plugins/qt7/mediaplayer/qt7playersession.mm | 751 +++++ src/plugins/qt7/qcvdisplaylink.h | 88 + src/plugins/qt7/qcvdisplaylink.mm | 156 + src/plugins/qt7/qt7.pro | 58 + src/plugins/qt7/qt7backend.h | 68 + src/plugins/qt7/qt7backend.mm | 60 + src/plugins/qt7/qt7ciimagevideobuffer.h | 86 + src/plugins/qt7/qt7ciimagevideobuffer.mm | 107 + src/plugins/qt7/qt7movierenderer.h | 107 + src/plugins/qt7/qt7movierenderer.mm | 479 ++++ src/plugins/qt7/qt7movievideowidget.h | 126 + src/plugins/qt7/qt7movievideowidget.mm | 437 +++ src/plugins/qt7/qt7movieviewoutput.h | 116 + src/plugins/qt7/qt7movieviewoutput.mm | 339 +++ src/plugins/qt7/qt7movieviewrenderer.h | 93 + src/plugins/qt7/qt7movieviewrenderer.mm | 371 +++ src/plugins/qt7/qt7serviceplugin.h | 75 + src/plugins/qt7/qt7serviceplugin.mm | 129 + src/plugins/qt7/qt7videooutput.h | 116 + src/plugins/qt7/qt7videooutput.mm | 91 + src/plugins/simulator/camera/simulatorcamera.pri | 25 + .../simulator/camera/simulatorcameracontrol.cpp | 179 ++ .../simulator/camera/simulatorcameracontrol.h | 87 + .../camera/simulatorcameraexposurecontrol.cpp | 502 ++++ .../camera/simulatorcameraexposurecontrol.h | 125 + .../camera/simulatorcameraimagecapturecontrol.cpp | 120 + .../camera/simulatorcameraimagecapturecontrol.h | 87 + .../simulator/camera/simulatorcameraservice.cpp | 161 ++ .../simulator/camera/simulatorcameraservice.h | 92 + .../simulator/camera/simulatorcamerasession.cpp | 147 + .../simulator/camera/simulatorcamerasession.h | 96 + .../simulator/camera/simulatorcamerasettings.cpp | 174 ++ .../simulator/camera/simulatorcamerasettings.h | 110 + .../camera/simulatorvideoinputdevicecontrol.cpp | 152 + .../camera/simulatorvideoinputdevicecontrol.h | 83 + .../camera/simulatorvideorenderercontrol.cpp | 130 + .../camera/simulatorvideorenderercontrol.h | 82 + .../simulator/qsimulatormultimediaconnection.cpp | 122 + .../simulator/qsimulatormultimediaconnection_p.h | 89 + src/plugins/simulator/qsimulatormultimediadata.cpp | 78 + src/plugins/simulator/qsimulatormultimediadata_p.h | 81 + src/plugins/simulator/qsimulatorserviceplugin.cpp | 127 + src/plugins/simulator/qsimulatorserviceplugin.h | 75 + src/plugins/simulator/simulator.pro | 28 + src/plugins/symbian/ecam/camera_s60.pri | 157 + src/plugins/symbian/ecam/ecam.pro | 40 + .../symbian/ecam/s60audioencodercontrol.cpp | 159 ++ src/plugins/symbian/ecam/s60audioencodercontrol.h | 90 + src/plugins/symbian/ecam/s60cameraconstants.h | 257 ++ src/plugins/symbian/ecam/s60cameracontrol.cpp | 983 +++++++ src/plugins/symbian/ecam/s60cameracontrol.h | 178 ++ src/plugins/symbian/ecam/s60cameraengine.cpp | 824 ++++++ src/plugins/symbian/ecam/s60cameraengine.h | 407 +++ src/plugins/symbian/ecam/s60cameraengineobserver.h | 178 ++ .../symbian/ecam/s60cameraexposurecontrol.cpp | 584 ++++ .../symbian/ecam/s60cameraexposurecontrol.h | 138 + src/plugins/symbian/ecam/s60cameraflashcontrol.cpp | 109 + src/plugins/symbian/ecam/s60cameraflashcontrol.h | 93 + src/plugins/symbian/ecam/s60camerafocuscontrol.cpp | 193 ++ src/plugins/symbian/ecam/s60camerafocuscontrol.h | 112 + .../symbian/ecam/s60cameraimagecapturecontrol.cpp | 124 + .../symbian/ecam/s60cameraimagecapturecontrol.h | 99 + .../ecam/s60cameraimageprocessingcontrol.cpp | 254 ++ .../symbian/ecam/s60cameraimageprocessingcontrol.h | 118 + src/plugins/symbian/ecam/s60cameralockscontrol.cpp | 263 ++ src/plugins/symbian/ecam/s60cameralockscontrol.h | 115 + src/plugins/symbian/ecam/s60cameraservice.cpp | 259 ++ src/plugins/symbian/ecam/s60cameraservice.h | 111 + .../symbian/ecam/s60cameraserviceplugin.cpp | 115 + src/plugins/symbian/ecam/s60cameraserviceplugin.h | 81 + src/plugins/symbian/ecam/s60camerasettings.cpp | 986 +++++++ src/plugins/symbian/ecam/s60camerasettings.h | 177 ++ .../symbian/ecam/s60cameraviewfinderengine.cpp | 789 ++++++ .../symbian/ecam/s60cameraviewfinderengine.h | 182 ++ .../symbian/ecam/s60imagecapturesession.cpp | 1884 ++++++++++++ src/plugins/symbian/ecam/s60imagecapturesession.h | 359 +++ .../symbian/ecam/s60imageencodercontrol.cpp | 128 + src/plugins/symbian/ecam/s60imageencodercontrol.h | 84 + .../symbian/ecam/s60mediacontainercontrol.cpp | 97 + .../symbian/ecam/s60mediacontainercontrol.h | 82 + .../symbian/ecam/s60mediarecordercontrol.cpp | 187 ++ src/plugins/symbian/ecam/s60mediarecordercontrol.h | 118 + .../symbian/ecam/s60videocapturesession.cpp | 2995 ++++++++++++++++++++ src/plugins/symbian/ecam/s60videocapturesession.h | 414 +++ src/plugins/symbian/ecam/s60videodevicecontrol.cpp | 108 + src/plugins/symbian/ecam/s60videodevicecontrol.h | 95 + .../symbian/ecam/s60videoencodercontrol.cpp | 204 ++ src/plugins/symbian/ecam/s60videoencodercontrol.h | 94 + .../symbian/ecam/s60videorenderercontrol.cpp | 78 + src/plugins/symbian/ecam/s60videorenderercontrol.h | 76 + .../symbian/mmf/audiosource/audiosource_s60.pri | 31 + .../mmf/audiosource/s60audiocaptureservice.cpp | 98 + .../mmf/audiosource/s60audiocaptureservice.h | 75 + .../mmf/audiosource/s60audiocapturesession.cpp | 937 ++++++ .../mmf/audiosource/s60audiocapturesession.h | 193 ++ .../mmf/audiosource/s60audiocontainercontrol.cpp | 96 + .../mmf/audiosource/s60audiocontainercontrol.h | 70 + .../mmf/audiosource/s60audioencodercontrol.cpp | 235 ++ .../mmf/audiosource/s60audioencodercontrol.h | 82 + .../mmf/audiosource/s60audioendpointselector.cpp | 100 + .../mmf/audiosource/s60audioendpointselector.h | 76 + .../audiosource/s60audiomediarecordercontrol.cpp | 180 ++ .../mmf/audiosource/s60audiomediarecordercontrol.h | 92 + src/plugins/symbian/mmf/inc/DebugMacros.h | 66 + .../symbian/mmf/mediaplayer/mediaplayer_s60.pri | 92 + .../mmf/mediaplayer/ms60mediaplayerresolver.h | 54 + .../mmf/mediaplayer/s60audioplayersession.cpp | 577 ++++ .../mmf/mediaplayer/s60audioplayersession.h | 136 + .../mmf/mediaplayer/s60mediametadataprovider.cpp | 146 + .../mmf/mediaplayer/s60mediametadataprovider.h | 72 + .../mediaplayer/s60medianetworkaccesscontrol.cpp | 144 + .../mmf/mediaplayer/s60medianetworkaccesscontrol.h | 89 + .../s60mediaplayeraudioendpointselector.cpp | 182 ++ .../s60mediaplayeraudioendpointselector.h | 77 + .../mmf/mediaplayer/s60mediaplayercontrol.cpp | 518 ++++ .../mmf/mediaplayer/s60mediaplayercontrol.h | 148 + .../mmf/mediaplayer/s60mediaplayerservice.cpp | 326 +++ .../mmf/mediaplayer/s60mediaplayerservice.h | 97 + .../mmf/mediaplayer/s60mediaplayersession.cpp | 1054 +++++++ .../mmf/mediaplayer/s60mediaplayersession.h | 187 ++ .../symbian/mmf/mediaplayer/s60mediarecognizer.cpp | 167 ++ .../symbian/mmf/mediaplayer/s60mediarecognizer.h | 79 + .../mmf/mediaplayer/s60mediastreamcontrol.cpp | 201 ++ .../mmf/mediaplayer/s60mediastreamcontrol.h | 79 + .../mmf/mediaplayer/s60videooutputinterface.h | 62 + .../mmf/mediaplayer/s60videoplayersession.cpp | 1124 ++++++++ .../mmf/mediaplayer/s60videoplayersession.h | 218 ++ .../symbian/mmf/mediaplayer/s60videorenderer.cpp | 95 + .../symbian/mmf/mediaplayer/s60videorenderer.h | 66 + .../symbian/mmf/mediaplayer/s60videosurface.cpp | 372 +++ .../symbian/mmf/mediaplayer/s60videosurface.h | 106 + src/plugins/symbian/mmf/mmf.pro | 58 + src/plugins/symbian/mmf/radio/radio.pri | 24 + .../symbian/mmf/radio/s60radiotunercontrol_31.cpp | 603 ++++ .../symbian/mmf/radio/s60radiotunercontrol_31.h | 161 ++ .../mmf/radio/s60radiotunercontrol_since32.cpp | 685 +++++ .../mmf/radio/s60radiotunercontrol_since32.h | 296 ++ .../symbian/mmf/radio/s60radiotunerservice.cpp | 83 + .../symbian/mmf/radio/s60radiotunerservice.h | 71 + src/plugins/symbian/mmf/s60formatsupported.cpp | 121 + src/plugins/symbian/mmf/s60formatsupported.h | 65 + src/plugins/symbian/mmf/s60mediaserviceplugin.cpp | 115 + src/plugins/symbian/mmf/s60mediaserviceplugin.h | 69 + .../symbian/openmaxal/mediaplayer/mediaplayer.pri | 36 + .../mediaplayer/qxamediaplayercontrol.cpp | 288 ++ .../openmaxal/mediaplayer/qxamediaplayercontrol.h | 99 + .../mediaplayer/qxamediastreamscontrol.cpp | 98 + .../openmaxal/mediaplayer/qxamediastreamscontrol.h | 77 + .../openmaxal/mediaplayer/qxametadatacontrol.cpp | 131 + .../openmaxal/mediaplayer/qxametadatacontrol.h | 78 + .../openmaxal/mediaplayer/qxaplaymediaservice.cpp | 117 + .../openmaxal/mediaplayer/qxaplaymediaservice.h | 74 + .../openmaxal/mediaplayer/qxaplaysession.cpp | 610 ++++ .../symbian/openmaxal/mediaplayer/qxaplaysession.h | 192 ++ .../mediaplayer/qxavideowidgetcontrol.cpp | 182 ++ .../openmaxal/mediaplayer/qxavideowidgetcontrol.h | 93 + .../mediaplayer/qxavideowindowcontrol.cpp | 222 ++ .../openmaxal/mediaplayer/qxavideowindowcontrol.h | 105 + .../symbian/openmaxal/mediaplayer/qxawidget.cpp | 64 + .../symbian/openmaxal/mediaplayer/qxawidget.h | 63 + .../openmaxal/mediaplayer/xaplaysessioncommon.h | 58 + .../openmaxal/mediaplayer/xaplaysessionimpl.cpp | 1259 ++++++++ .../openmaxal/mediaplayer/xaplaysessionimpl.h | 210 ++ .../openmaxal/mediarecorder/mediarecorder.pri | 24 + .../mediarecorder/qxaaudioencodercontrol.cpp | 111 + .../mediarecorder/qxaaudioencodercontrol.h | 79 + .../mediarecorder/qxaaudioendpointselector.cpp | 98 + .../mediarecorder/qxaaudioendpointselector.h | 77 + .../mediarecorder/qxamediacontainercontrol.cpp | 81 + .../mediarecorder/qxamediacontainercontrol.h | 71 + .../mediarecorder/qxamediarecordercontrol.cpp | 122 + .../mediarecorder/qxamediarecordercontrol.h | 83 + .../mediarecorder/qxarecordmediaservice.cpp | 86 + .../mediarecorder/qxarecordmediaservice.h | 78 + .../openmaxal/mediarecorder/qxarecordsession.cpp | 766 +++++ .../openmaxal/mediarecorder/qxarecordsession.h | 144 + .../mediarecorder/xarecordsessioncommon.h | 67 + .../mediarecorder/xarecordsessionimpl.cpp | 1378 +++++++++ .../openmaxal/mediarecorder/xarecordsessionimpl.h | 179 ++ src/plugins/symbian/openmaxal/openmaxal.pro | 58 + src/plugins/symbian/openmaxal/qxacommon.h | 203 ++ .../openmaxal/qxamediaserviceproviderplugin.cpp | 87 + .../openmaxal/qxamediaserviceproviderplugin.h | 60 + .../openmaxal/radiotuner/qxaradiocontrol.cpp | 202 ++ .../symbian/openmaxal/radiotuner/qxaradiocontrol.h | 95 + .../openmaxal/radiotuner/qxaradiomediaservice.cpp | 72 + .../openmaxal/radiotuner/qxaradiomediaservice.h | 66 + .../openmaxal/radiotuner/qxaradiosession.cpp | 323 +++ .../symbian/openmaxal/radiotuner/qxaradiosession.h | 118 + .../symbian/openmaxal/radiotuner/radiotuner.pri | 18 + .../openmaxal/radiotuner/xaradiosessionimpl.cpp | 715 +++++ .../openmaxal/radiotuner/xaradiosessionimpl.h | 128 + .../radiotuner/xaradiosessionimplobserver.h | 64 + src/plugins/symbian/openmaxal/xacommon.h | 79 + src/plugins/symbian/symbian.pro | 23 + .../symbian/videooutput/s60videodisplay.cpp | 179 ++ src/plugins/symbian/videooutput/s60videodisplay.h | 188 ++ .../symbian/videooutput/s60videooutpututils.cpp | 119 + .../symbian/videooutput/s60videooutpututils.h | 71 + src/plugins/symbian/videooutput/s60videowidget.cpp | 237 ++ src/plugins/symbian/videooutput/s60videowidget.h | 97 + .../symbian/videooutput/s60videowidgetcontrol.cpp | 171 ++ .../symbian/videooutput/s60videowidgetcontrol.h | 131 + .../symbian/videooutput/s60videowidgetdisplay.cpp | 174 ++ .../symbian/videooutput/s60videowidgetdisplay.h | 85 + .../symbian/videooutput/s60videowindowcontrol.cpp | 178 ++ .../symbian/videooutput/s60videowindowcontrol.h | 102 + .../symbian/videooutput/s60videowindowdisplay.cpp | 140 + .../symbian/videooutput/s60videowindowdisplay.h | 73 + src/plugins/symbian/videooutput/videooutput.pri | 37 + src/plugins/v4l/radio/radio.pri | 29 + src/plugins/v4l/radio/v4lradiocontrol.cpp | 538 ++++ src/plugins/v4l/radio/v4lradiocontrol.h | 134 + src/plugins/v4l/radio/v4lradiocontrol_maemo5.cpp | 755 +++++ src/plugins/v4l/radio/v4lradiocontrol_maemo5.h | 144 + src/plugins/v4l/radio/v4lradioservice.cpp | 71 + src/plugins/v4l/radio/v4lradioservice.h | 67 + src/plugins/v4l/v4l.pro | 13 + src/plugins/v4l/v4lserviceplugin.cpp | 84 + src/plugins/v4l/v4lserviceplugin.h | 63 + src/plugins/wmp/qevrvideooverlay.cpp | 357 +++ src/plugins/wmp/qevrvideooverlay.h | 119 + src/plugins/wmp/qmfactivate.cpp | 296 ++ src/plugins/wmp/qmfactivate.h | 90 + src/plugins/wmp/qwmpevents.cpp | 114 + src/plugins/wmp/qwmpevents.h | 222 ++ src/plugins/wmp/qwmpglobal.cpp | 68 + src/plugins/wmp/qwmpglobal.h | 83 + src/plugins/wmp/qwmpmetadata.cpp | 442 +++ src/plugins/wmp/qwmpmetadata.h | 88 + src/plugins/wmp/qwmpplayercontrol.cpp | 465 +++ src/plugins/wmp/qwmpplayercontrol.h | 141 + src/plugins/wmp/qwmpplayerservice.cpp | 355 +++ src/plugins/wmp/qwmpplayerservice.h | 125 + src/plugins/wmp/qwmpplaylist.cpp | 296 ++ src/plugins/wmp/qwmpplaylist.h | 94 + src/plugins/wmp/qwmpplaylistcontrol.cpp | 153 + src/plugins/wmp/qwmpplaylistcontrol.h | 85 + src/plugins/wmp/qwmpserviceprovider.cpp | 74 + src/plugins/wmp/qwmpserviceprovider.h | 58 + src/plugins/wmp/qwmpvideooverlay.cpp | 462 +++ src/plugins/wmp/qwmpvideooverlay.h | 145 + src/plugins/wmp/wmp.pro | 47 + 450 files changed, 89563 insertions(+) create mode 100644 src/plugins/audiocapture/audiocapture.pro create mode 100644 src/plugins/audiocapture/audiocaptureservice.cpp create mode 100644 src/plugins/audiocapture/audiocaptureservice.h create mode 100644 src/plugins/audiocapture/audiocaptureserviceplugin.cpp create mode 100644 src/plugins/audiocapture/audiocaptureserviceplugin.h create mode 100644 src/plugins/audiocapture/audiocapturesession.cpp create mode 100644 src/plugins/audiocapture/audiocapturesession.h create mode 100644 src/plugins/audiocapture/audiocontainercontrol.cpp create mode 100644 src/plugins/audiocapture/audiocontainercontrol.h create mode 100644 src/plugins/audiocapture/audioencodercontrol.cpp create mode 100644 src/plugins/audiocapture/audioencodercontrol.h create mode 100644 src/plugins/audiocapture/audioendpointselector.cpp create mode 100644 src/plugins/audiocapture/audioendpointselector.h create mode 100644 src/plugins/audiocapture/audiomediarecordercontrol.cpp create mode 100644 src/plugins/audiocapture/audiomediarecordercontrol.h create mode 100644 src/plugins/directshow/camera/camera.pri create mode 100644 src/plugins/directshow/camera/directshowglobal.h create mode 100644 src/plugins/directshow/camera/dscameracontrol.cpp create mode 100644 src/plugins/directshow/camera/dscameracontrol.h create mode 100644 src/plugins/directshow/camera/dscameraservice.cpp create mode 100644 src/plugins/directshow/camera/dscameraservice.h create mode 100644 src/plugins/directshow/camera/dscamerasession.cpp create mode 100644 src/plugins/directshow/camera/dscamerasession.h create mode 100644 src/plugins/directshow/camera/dsimagecapturecontrol.cpp create mode 100644 src/plugins/directshow/camera/dsimagecapturecontrol.h create mode 100644 src/plugins/directshow/camera/dsvideodevicecontrol.cpp create mode 100644 src/plugins/directshow/camera/dsvideodevicecontrol.h create mode 100644 src/plugins/directshow/camera/dsvideorenderer.cpp create mode 100644 src/plugins/directshow/camera/dsvideorenderer.h create mode 100644 src/plugins/directshow/camera/dsvideowidgetcontrol.cpp create mode 100644 src/plugins/directshow/camera/dsvideowidgetcontrol.h create mode 100644 src/plugins/directshow/directshow.pro create mode 100644 src/plugins/directshow/dsserviceplugin.cpp create mode 100644 src/plugins/directshow/dsserviceplugin.h create mode 100644 src/plugins/directshow/player/directshowaudioendpointcontrol.cpp create mode 100644 src/plugins/directshow/player/directshowaudioendpointcontrol.h create mode 100644 src/plugins/directshow/player/directshoweventloop.cpp create mode 100644 src/plugins/directshow/player/directshoweventloop.h create mode 100644 src/plugins/directshow/player/directshowglobal.h create mode 100644 src/plugins/directshow/player/directshowioreader.cpp create mode 100644 src/plugins/directshow/player/directshowioreader.h create mode 100644 src/plugins/directshow/player/directshowiosource.cpp create mode 100644 src/plugins/directshow/player/directshowiosource.h create mode 100644 src/plugins/directshow/player/directshowmediatype.cpp create mode 100644 src/plugins/directshow/player/directshowmediatype.h create mode 100644 src/plugins/directshow/player/directshowmediatypelist.cpp create mode 100644 src/plugins/directshow/player/directshowmediatypelist.h create mode 100644 src/plugins/directshow/player/directshowmetadatacontrol.cpp create mode 100644 src/plugins/directshow/player/directshowmetadatacontrol.h create mode 100644 src/plugins/directshow/player/directshowpinenum.cpp create mode 100644 src/plugins/directshow/player/directshowpinenum.h create mode 100644 src/plugins/directshow/player/directshowplayercontrol.cpp create mode 100644 src/plugins/directshow/player/directshowplayercontrol.h create mode 100644 src/plugins/directshow/player/directshowplayerservice.cpp create mode 100644 src/plugins/directshow/player/directshowplayerservice.h create mode 100644 src/plugins/directshow/player/directshowsamplescheduler.cpp create mode 100644 src/plugins/directshow/player/directshowsamplescheduler.h create mode 100644 src/plugins/directshow/player/directshowvideorenderercontrol.cpp create mode 100644 src/plugins/directshow/player/directshowvideorenderercontrol.h create mode 100644 src/plugins/directshow/player/mediasamplevideobuffer.cpp create mode 100644 src/plugins/directshow/player/mediasamplevideobuffer.h create mode 100644 src/plugins/directshow/player/player.pri create mode 100644 src/plugins/directshow/player/videosurfacefilter.cpp create mode 100644 src/plugins/directshow/player/videosurfacefilter.h create mode 100644 src/plugins/directshow/player/vmr9videowindowcontrol.cpp create mode 100644 src/plugins/directshow/player/vmr9videowindowcontrol.h create mode 100644 src/plugins/gstreamer/camerabin/camerabin.pri create mode 100644 src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinaudioencoder.h create mode 100644 src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h create mode 100644 src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabincapturedestination.h create mode 100644 src/plugins/gstreamer/camerabin/camerabincontainer.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabincontainer.h create mode 100644 src/plugins/gstreamer/camerabin/camerabincontrol.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabincontrol.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinexposure.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinexposure.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinflash.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinflash.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinfocus.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinfocus.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinimagecapture.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinimageencoder.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinimageprocessing.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinlocks.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinlocks.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinmetadata.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinmetadata.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinrecorder.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinrecorder.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinservice.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinservice.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinsession.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinsession.h create mode 100644 src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp create mode 100644 src/plugins/gstreamer/camerabin/camerabinvideoencoder.h create mode 100644 src/plugins/gstreamer/camerabuttonlistener_maemo.cpp create mode 100644 src/plugins/gstreamer/camerabuttonlistener_maemo.h create mode 100644 src/plugins/gstreamer/camerabuttonlistener_meego.cpp create mode 100644 src/plugins/gstreamer/camerabuttonlistener_meego.h create mode 100644 src/plugins/gstreamer/gstreamer.pro create mode 100644 src/plugins/gstreamer/gstvideoconnector.c create mode 100644 src/plugins/gstreamer/gstvideoconnector.h create mode 100644 src/plugins/gstreamer/mediacapture/mediacapture.pri create mode 100644 src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp create mode 100644 src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h create mode 100644 src/plugins/gstreamer/mediaplayer/mediaplayer.pri create mode 100644 src/plugins/gstreamer/mediaplayer/playerresourcepolicy.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/playerresourcepolicy.h create mode 100644 src/plugins/gstreamer/mediaplayer/qgstappsrc.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/qgstappsrc.h create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp create mode 100644 src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h create mode 100644 src/plugins/gstreamer/qabstractgstbufferpool.h create mode 100644 src/plugins/gstreamer/qgstreameraudioinputendpointselector.cpp create mode 100644 src/plugins/gstreamer/qgstreameraudioinputendpointselector.h create mode 100644 src/plugins/gstreamer/qgstreamerbushelper.cpp create mode 100644 src/plugins/gstreamer/qgstreamerbushelper.h create mode 100644 src/plugins/gstreamer/qgstreamergltexturerenderer.cpp create mode 100644 src/plugins/gstreamer/qgstreamergltexturerenderer.h create mode 100644 src/plugins/gstreamer/qgstreamermessage.cpp create mode 100644 src/plugins/gstreamer/qgstreamermessage.h create mode 100644 src/plugins/gstreamer/qgstreamerserviceplugin.cpp create mode 100644 src/plugins/gstreamer/qgstreamerserviceplugin.h create mode 100644 src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.cpp create mode 100644 src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.h create mode 100644 src/plugins/gstreamer/qgstreamervideooverlay.cpp create mode 100644 src/plugins/gstreamer/qgstreamervideooverlay.h create mode 100644 src/plugins/gstreamer/qgstreamervideorenderer.cpp create mode 100644 src/plugins/gstreamer/qgstreamervideorenderer.h create mode 100644 src/plugins/gstreamer/qgstreamervideorendererinterface.cpp create mode 100644 src/plugins/gstreamer/qgstreamervideorendererinterface.h create mode 100644 src/plugins/gstreamer/qgstreamervideowidget.cpp create mode 100644 src/plugins/gstreamer/qgstreamervideowidget.h create mode 100644 src/plugins/gstreamer/qgstreamervideowindow.cpp create mode 100644 src/plugins/gstreamer/qgstreamervideowindow.h create mode 100644 src/plugins/gstreamer/qgstutils.cpp create mode 100644 src/plugins/gstreamer/qgstutils.h create mode 100644 src/plugins/gstreamer/qgstvideobuffer.cpp create mode 100644 src/plugins/gstreamer/qgstvideobuffer.h create mode 100644 src/plugins/gstreamer/qgstxvimagebuffer.cpp create mode 100644 src/plugins/gstreamer/qgstxvimagebuffer.h create mode 100644 src/plugins/gstreamer/qvideosurfacegstsink.cpp create mode 100644 src/plugins/gstreamer/qvideosurfacegstsink.h create mode 100644 src/plugins/gstreamer/qx11videosurface.cpp create mode 100644 src/plugins/gstreamer/qx11videosurface.h create mode 100644 src/plugins/m3u/m3u.pro create mode 100644 src/plugins/m3u/main.cpp create mode 100644 src/plugins/m3u/qm3uhandler.cpp create mode 100644 src/plugins/m3u/qm3uhandler.h create mode 100644 src/plugins/plugins.pro create mode 100644 src/plugins/pulseaudio/pulseaudio.pro create mode 100644 src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp create mode 100644 src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h create mode 100644 src/plugins/pulseaudio/qaudioinput_pulse.cpp create mode 100644 src/plugins/pulseaudio/qaudioinput_pulse.h create mode 100644 src/plugins/pulseaudio/qaudiooutput_pulse.cpp create mode 100644 src/plugins/pulseaudio/qaudiooutput_pulse.h create mode 100644 src/plugins/pulseaudio/qpulseaudioengine.cpp create mode 100644 src/plugins/pulseaudio/qpulseaudioengine.h create mode 100644 src/plugins/pulseaudio/qpulseaudioplugin.cpp create mode 100644 src/plugins/pulseaudio/qpulseaudioplugin.h create mode 100644 src/plugins/pulseaudio/qpulsehelpers.cpp create mode 100644 src/plugins/pulseaudio/qpulsehelpers.h create mode 100644 src/plugins/qt7/mediaplayer/mediaplayer.pri create mode 100644 src/plugins/qt7/mediaplayer/qt7playercontrol.h create mode 100644 src/plugins/qt7/mediaplayer/qt7playercontrol.mm create mode 100644 src/plugins/qt7/mediaplayer/qt7playermetadata.h create mode 100644 src/plugins/qt7/mediaplayer/qt7playermetadata.mm create mode 100644 src/plugins/qt7/mediaplayer/qt7playerservice.h create mode 100644 src/plugins/qt7/mediaplayer/qt7playerservice.mm create mode 100644 src/plugins/qt7/mediaplayer/qt7playersession.h create mode 100644 src/plugins/qt7/mediaplayer/qt7playersession.mm create mode 100644 src/plugins/qt7/qcvdisplaylink.h create mode 100644 src/plugins/qt7/qcvdisplaylink.mm create mode 100644 src/plugins/qt7/qt7.pro create mode 100644 src/plugins/qt7/qt7backend.h create mode 100644 src/plugins/qt7/qt7backend.mm create mode 100644 src/plugins/qt7/qt7ciimagevideobuffer.h create mode 100644 src/plugins/qt7/qt7ciimagevideobuffer.mm create mode 100644 src/plugins/qt7/qt7movierenderer.h create mode 100644 src/plugins/qt7/qt7movierenderer.mm create mode 100644 src/plugins/qt7/qt7movievideowidget.h create mode 100644 src/plugins/qt7/qt7movievideowidget.mm create mode 100644 src/plugins/qt7/qt7movieviewoutput.h create mode 100644 src/plugins/qt7/qt7movieviewoutput.mm create mode 100644 src/plugins/qt7/qt7movieviewrenderer.h create mode 100644 src/plugins/qt7/qt7movieviewrenderer.mm create mode 100644 src/plugins/qt7/qt7serviceplugin.h create mode 100644 src/plugins/qt7/qt7serviceplugin.mm create mode 100644 src/plugins/qt7/qt7videooutput.h create mode 100644 src/plugins/qt7/qt7videooutput.mm create mode 100644 src/plugins/simulator/camera/simulatorcamera.pri create mode 100644 src/plugins/simulator/camera/simulatorcameracontrol.cpp create mode 100644 src/plugins/simulator/camera/simulatorcameracontrol.h create mode 100644 src/plugins/simulator/camera/simulatorcameraexposurecontrol.cpp create mode 100644 src/plugins/simulator/camera/simulatorcameraexposurecontrol.h create mode 100644 src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.cpp create mode 100644 src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.h create mode 100644 src/plugins/simulator/camera/simulatorcameraservice.cpp create mode 100644 src/plugins/simulator/camera/simulatorcameraservice.h create mode 100644 src/plugins/simulator/camera/simulatorcamerasession.cpp create mode 100644 src/plugins/simulator/camera/simulatorcamerasession.h create mode 100644 src/plugins/simulator/camera/simulatorcamerasettings.cpp create mode 100644 src/plugins/simulator/camera/simulatorcamerasettings.h create mode 100644 src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.cpp create mode 100644 src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.h create mode 100644 src/plugins/simulator/camera/simulatorvideorenderercontrol.cpp create mode 100644 src/plugins/simulator/camera/simulatorvideorenderercontrol.h create mode 100644 src/plugins/simulator/qsimulatormultimediaconnection.cpp create mode 100644 src/plugins/simulator/qsimulatormultimediaconnection_p.h create mode 100644 src/plugins/simulator/qsimulatormultimediadata.cpp create mode 100644 src/plugins/simulator/qsimulatormultimediadata_p.h create mode 100644 src/plugins/simulator/qsimulatorserviceplugin.cpp create mode 100644 src/plugins/simulator/qsimulatorserviceplugin.h create mode 100644 src/plugins/simulator/simulator.pro create mode 100644 src/plugins/symbian/ecam/camera_s60.pri create mode 100644 src/plugins/symbian/ecam/ecam.pro create mode 100644 src/plugins/symbian/ecam/s60audioencodercontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60audioencodercontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameraconstants.h create mode 100644 src/plugins/symbian/ecam/s60cameracontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60cameracontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameraengine.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraengine.h create mode 100644 src/plugins/symbian/ecam/s60cameraengineobserver.h create mode 100644 src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraexposurecontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameraflashcontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraflashcontrol.h create mode 100644 src/plugins/symbian/ecam/s60camerafocuscontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60camerafocuscontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameralockscontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60cameralockscontrol.h create mode 100644 src/plugins/symbian/ecam/s60cameraservice.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraservice.h create mode 100644 src/plugins/symbian/ecam/s60cameraserviceplugin.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraserviceplugin.h create mode 100644 src/plugins/symbian/ecam/s60camerasettings.cpp create mode 100644 src/plugins/symbian/ecam/s60camerasettings.h create mode 100644 src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp create mode 100644 src/plugins/symbian/ecam/s60cameraviewfinderengine.h create mode 100644 src/plugins/symbian/ecam/s60imagecapturesession.cpp create mode 100644 src/plugins/symbian/ecam/s60imagecapturesession.h create mode 100644 src/plugins/symbian/ecam/s60imageencodercontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60imageencodercontrol.h create mode 100644 src/plugins/symbian/ecam/s60mediacontainercontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60mediacontainercontrol.h create mode 100644 src/plugins/symbian/ecam/s60mediarecordercontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60mediarecordercontrol.h create mode 100644 src/plugins/symbian/ecam/s60videocapturesession.cpp create mode 100644 src/plugins/symbian/ecam/s60videocapturesession.h create mode 100644 src/plugins/symbian/ecam/s60videodevicecontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60videodevicecontrol.h create mode 100644 src/plugins/symbian/ecam/s60videoencodercontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60videoencodercontrol.h create mode 100644 src/plugins/symbian/ecam/s60videorenderercontrol.cpp create mode 100644 src/plugins/symbian/ecam/s60videorenderercontrol.h create mode 100644 src/plugins/symbian/mmf/audiosource/audiosource_s60.pri create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.cpp create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.h create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiocapturesession.cpp create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiocapturesession.h create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.cpp create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.h create mode 100644 src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.cpp create mode 100644 src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.h create mode 100644 src/plugins/symbian/mmf/audiosource/s60audioendpointselector.cpp create mode 100644 src/plugins/symbian/mmf/audiosource/s60audioendpointselector.h create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.cpp create mode 100644 src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.h create mode 100644 src/plugins/symbian/mmf/inc/DebugMacros.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/mediaplayer_s60.pri create mode 100644 src/plugins/symbian/mmf/mediaplayer/ms60mediaplayerresolver.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60audioplayersession.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60audioplayersession.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videooutputinterface.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videorenderer.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videorenderer.h create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videosurface.cpp create mode 100644 src/plugins/symbian/mmf/mediaplayer/s60videosurface.h create mode 100644 src/plugins/symbian/mmf/mmf.pro create mode 100644 src/plugins/symbian/mmf/radio/radio.pri create mode 100644 src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.cpp create mode 100644 src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.h create mode 100644 src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.cpp create mode 100644 src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.h create mode 100644 src/plugins/symbian/mmf/radio/s60radiotunerservice.cpp create mode 100644 src/plugins/symbian/mmf/radio/s60radiotunerservice.h create mode 100644 src/plugins/symbian/mmf/s60formatsupported.cpp create mode 100644 src/plugins/symbian/mmf/s60formatsupported.h create mode 100644 src/plugins/symbian/mmf/s60mediaserviceplugin.cpp create mode 100644 src/plugins/symbian/mmf/s60mediaserviceplugin.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/mediaplayer.pri create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxawidget.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/qxawidget.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/xaplaysessioncommon.h create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.cpp create mode 100644 src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/mediarecorder.pri create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/xarecordsessioncommon.h create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.cpp create mode 100644 src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.h create mode 100644 src/plugins/symbian/openmaxal/openmaxal.pro create mode 100644 src/plugins/symbian/openmaxal/qxacommon.h create mode 100644 src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.cpp create mode 100644 src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.h create mode 100644 src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.cpp create mode 100644 src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.h create mode 100644 src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.cpp create mode 100644 src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.h create mode 100644 src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.cpp create mode 100644 src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.h create mode 100644 src/plugins/symbian/openmaxal/radiotuner/radiotuner.pri create mode 100644 src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.cpp create mode 100644 src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.h create mode 100644 src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimplobserver.h create mode 100644 src/plugins/symbian/openmaxal/xacommon.h create mode 100644 src/plugins/symbian/symbian.pro create mode 100644 src/plugins/symbian/videooutput/s60videodisplay.cpp create mode 100644 src/plugins/symbian/videooutput/s60videodisplay.h create mode 100644 src/plugins/symbian/videooutput/s60videooutpututils.cpp create mode 100644 src/plugins/symbian/videooutput/s60videooutpututils.h create mode 100644 src/plugins/symbian/videooutput/s60videowidget.cpp create mode 100644 src/plugins/symbian/videooutput/s60videowidget.h create mode 100644 src/plugins/symbian/videooutput/s60videowidgetcontrol.cpp create mode 100644 src/plugins/symbian/videooutput/s60videowidgetcontrol.h create mode 100644 src/plugins/symbian/videooutput/s60videowidgetdisplay.cpp create mode 100644 src/plugins/symbian/videooutput/s60videowidgetdisplay.h create mode 100644 src/plugins/symbian/videooutput/s60videowindowcontrol.cpp create mode 100644 src/plugins/symbian/videooutput/s60videowindowcontrol.h create mode 100644 src/plugins/symbian/videooutput/s60videowindowdisplay.cpp create mode 100644 src/plugins/symbian/videooutput/s60videowindowdisplay.h create mode 100644 src/plugins/symbian/videooutput/videooutput.pri create mode 100644 src/plugins/v4l/radio/radio.pri create mode 100644 src/plugins/v4l/radio/v4lradiocontrol.cpp create mode 100644 src/plugins/v4l/radio/v4lradiocontrol.h create mode 100644 src/plugins/v4l/radio/v4lradiocontrol_maemo5.cpp create mode 100644 src/plugins/v4l/radio/v4lradiocontrol_maemo5.h create mode 100644 src/plugins/v4l/radio/v4lradioservice.cpp create mode 100644 src/plugins/v4l/radio/v4lradioservice.h create mode 100644 src/plugins/v4l/v4l.pro create mode 100644 src/plugins/v4l/v4lserviceplugin.cpp create mode 100644 src/plugins/v4l/v4lserviceplugin.h create mode 100644 src/plugins/wmp/qevrvideooverlay.cpp create mode 100644 src/plugins/wmp/qevrvideooverlay.h create mode 100644 src/plugins/wmp/qmfactivate.cpp create mode 100644 src/plugins/wmp/qmfactivate.h create mode 100644 src/plugins/wmp/qwmpevents.cpp create mode 100644 src/plugins/wmp/qwmpevents.h create mode 100644 src/plugins/wmp/qwmpglobal.cpp create mode 100644 src/plugins/wmp/qwmpglobal.h create mode 100644 src/plugins/wmp/qwmpmetadata.cpp create mode 100644 src/plugins/wmp/qwmpmetadata.h create mode 100644 src/plugins/wmp/qwmpplayercontrol.cpp create mode 100644 src/plugins/wmp/qwmpplayercontrol.h create mode 100644 src/plugins/wmp/qwmpplayerservice.cpp create mode 100644 src/plugins/wmp/qwmpplayerservice.h create mode 100644 src/plugins/wmp/qwmpplaylist.cpp create mode 100644 src/plugins/wmp/qwmpplaylist.h create mode 100644 src/plugins/wmp/qwmpplaylistcontrol.cpp create mode 100644 src/plugins/wmp/qwmpplaylistcontrol.h create mode 100644 src/plugins/wmp/qwmpserviceprovider.cpp create mode 100644 src/plugins/wmp/qwmpserviceprovider.h create mode 100644 src/plugins/wmp/qwmpvideooverlay.cpp create mode 100644 src/plugins/wmp/qwmpvideooverlay.h create mode 100644 src/plugins/wmp/wmp.pro (limited to 'src/plugins') diff --git a/src/plugins/audiocapture/audiocapture.pro b/src/plugins/audiocapture/audiocapture.pro new file mode 100644 index 000000000..ceb554067 --- /dev/null +++ b/src/plugins/audiocapture/audiocapture.pro @@ -0,0 +1,25 @@ +load(qt_module) + +TARGET = qtmedia_audioengine +QT += multimediakit-private +PLUGIN_TYPE=mediaservice + +load(qt_plugin) +DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE} + +# Input +HEADERS += audioencodercontrol.h \ + audiocontainercontrol.h \ + audiomediarecordercontrol.h \ + audioendpointselector.h \ + audiocaptureservice.h \ + audiocaptureserviceplugin.h \ + audiocapturesession.h + +SOURCES += audioencodercontrol.cpp \ + audiocontainercontrol.cpp \ + audiomediarecordercontrol.cpp \ + audioendpointselector.cpp \ + audiocaptureservice.cpp \ + audiocaptureserviceplugin.cpp \ + audiocapturesession.cpp diff --git a/src/plugins/audiocapture/audiocaptureservice.cpp b/src/plugins/audiocapture/audiocaptureservice.cpp new file mode 100644 index 000000000..2859eb557 --- /dev/null +++ b/src/plugins/audiocapture/audiocaptureservice.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "audiocaptureservice.h" +#include "audiocapturesession.h" +#include "audioendpointselector.h" +#include "audioencodercontrol.h" +#include "audiocontainercontrol.h" +#include "audiomediarecordercontrol.h" + +AudioCaptureService::AudioCaptureService(QObject *parent): + QMediaService(parent) +{ + m_session = new AudioCaptureSession(this); + m_encoderControl = new AudioEncoderControl(m_session); + m_containerControl = new AudioContainerControl(m_session); + m_mediaControl = new AudioMediaRecorderControl(m_session); + m_endpointSelector = new AudioEndpointSelector(m_session); +} + +AudioCaptureService::~AudioCaptureService() +{ + delete m_encoderControl; + delete m_containerControl; + delete m_endpointSelector; + delete m_mediaControl; + delete m_session; +} + +QMediaControl *AudioCaptureService::requestControl(const char *name) +{ + if (qstrcmp(name,QMediaRecorderControl_iid) == 0) + return m_mediaControl; + + if (qstrcmp(name,QAudioEncoderControl_iid) == 0) + return m_encoderControl; + + if (qstrcmp(name,QAudioEndpointSelector_iid) == 0) + return m_endpointSelector; + + if (qstrcmp(name,QMediaContainerControl_iid) == 0) + return m_containerControl; + + return 0; +} + +void AudioCaptureService::releaseControl(QMediaControl *control) +{ + Q_UNUSED(control) +} + + diff --git a/src/plugins/audiocapture/audiocaptureservice.h b/src/plugins/audiocapture/audiocaptureservice.h new file mode 100644 index 000000000..22d70029d --- /dev/null +++ b/src/plugins/audiocapture/audiocaptureservice.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOCAPTURESERVICE_H +#define AUDIOCAPTURESERVICE_H + +#include + +#include "qmediaservice.h" + +class AudioCaptureSession; +class AudioEncoderControl; +class AudioContainerControl; +class AudioMediaRecorderControl; +class AudioEndpointSelector; + +QT_USE_NAMESPACE + +class AudioCaptureService : public QMediaService +{ + Q_OBJECT +public: + AudioCaptureService(QObject *parent = 0); + ~AudioCaptureService(); + + QMediaControl *requestControl(const char *interface); + void releaseControl(QMediaControl *control); +private: + AudioCaptureSession *m_session; + AudioEncoderControl *m_encoderControl; + AudioContainerControl *m_containerControl; + AudioEndpointSelector *m_endpointSelector; + AudioMediaRecorderControl *m_mediaControl; +}; + +#endif diff --git a/src/plugins/audiocapture/audiocaptureserviceplugin.cpp b/src/plugins/audiocapture/audiocaptureserviceplugin.cpp new file mode 100644 index 000000000..5ebaf12b9 --- /dev/null +++ b/src/plugins/audiocapture/audiocaptureserviceplugin.cpp @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "audiocaptureserviceplugin.h" +#include "audiocaptureservice.h" + +#include "qmediaserviceprovider.h" + + +QStringList AudioCaptureServicePlugin::keys() const +{ + return QStringList() << QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE); +} + +QMediaService* AudioCaptureServicePlugin::create(QString const& key) +{ + if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) + return new AudioCaptureService; + + return 0; +} + +void AudioCaptureServicePlugin::release(QMediaService *service) +{ + delete service; +} + +Q_EXPORT_PLUGIN2(qtmedia_audioengine, AudioCaptureServicePlugin); + diff --git a/src/plugins/audiocapture/audiocaptureserviceplugin.h b/src/plugins/audiocapture/audiocaptureserviceplugin.h new file mode 100644 index 000000000..e244b9951 --- /dev/null +++ b/src/plugins/audiocapture/audiocaptureserviceplugin.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef AUDIOCAPTURESERVICEPLUGIN_H +#define AUDIOCAPTURESERVICEPLUGIN_H + +#include "qmediaserviceproviderplugin.h" + +QT_USE_NAMESPACE + +class AudioCaptureServicePlugin : public QMediaServiceProviderPlugin +{ + Q_OBJECT + +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); +}; + +#endif // AUDIOCAPTURESERVICEPLUGIN_H diff --git a/src/plugins/audiocapture/audiocapturesession.cpp b/src/plugins/audiocapture/audiocapturesession.cpp new file mode 100644 index 000000000..da6268adc --- /dev/null +++ b/src/plugins/audiocapture/audiocapturesession.cpp @@ -0,0 +1,358 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qmediarecorder.h" + +#include "audiocapturesession.h" + +AudioCaptureSession::AudioCaptureSession(QObject *parent): + QObject(parent) +{ + m_deviceInfo = new QAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice()); + m_audioInput = 0; + m_position = 0; + m_state = QMediaRecorder::StoppedState; + + m_format.setFrequency(8000); + m_format.setChannels(1); + m_format.setSampleSize(8); + m_format.setSampleType(QAudioFormat::UnSignedInt); + m_format.setCodec("audio/pcm"); + wavFile = true; +} + +AudioCaptureSession::~AudioCaptureSession() +{ + stop(); + + if(m_audioInput) + delete m_audioInput; +} + +QAudioDeviceInfo* AudioCaptureSession::deviceInfo() const +{ + return m_deviceInfo; +} + +QAudioFormat AudioCaptureSession::format() const +{ + return m_format; +} + +bool AudioCaptureSession::isFormatSupported(const QAudioFormat &format) const +{ + if(m_deviceInfo) { + if(format.codec().contains(QLatin1String("audio/x-wav"))) { + QAudioFormat fmt = format; + fmt.setCodec("audio/pcm"); + return m_deviceInfo->isFormatSupported(fmt); + } else + return m_deviceInfo->isFormatSupported(format); + } + return false; +} + +bool AudioCaptureSession::setFormat(const QAudioFormat &format) +{ + if(m_deviceInfo) { + + QAudioFormat fmt = format; + + if(m_deviceInfo->isFormatSupported(fmt)) { + m_format = fmt; + if(m_audioInput) delete m_audioInput; + m_audioInput = 0; + QList devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for(int i=0;ideviceName().toLocal8Bit().constData(), + devices.at(i).deviceName().toLocal8Bit().constData()) == 0) { + m_audioInput = new QAudioInput(devices.at(i),m_format); + connect(m_audioInput,SIGNAL(stateChanged(QAudio::State)),this,SLOT(stateChanged(QAudio::State))); + connect(m_audioInput,SIGNAL(notify()),this,SLOT(notify())); + break; + } + } + } else { + m_format = m_deviceInfo->preferredFormat(); + qWarning()<<"failed to setFormat using preferred..."; + } + } + return false; +} + +QStringList AudioCaptureSession::supportedContainers() const +{ + QStringList list; + if(m_deviceInfo) { + if (m_deviceInfo->supportedCodecs().size() > 0) { + list << "audio/x-wav"; + list << "audio/pcm"; + } + } + return list; +} + +QString AudioCaptureSession::containerDescription(const QString &formatMimeType) const +{ + if(m_deviceInfo) { + if (formatMimeType.contains(QLatin1String("audio/pcm"))) + return tr("RAW file format"); + if (formatMimeType.contains(QLatin1String("audio/x-wav"))) + return tr("WAV file format"); + } + return QString(); +} + +void AudioCaptureSession::setContainerMimeType(const QString &formatMimeType) +{ + if (!formatMimeType.contains(QLatin1String("audio/x-wav")) && + !formatMimeType.contains(QLatin1String("audio/pcm")) && + !formatMimeType.isEmpty()) + return; + + if(m_deviceInfo) { + if (!m_deviceInfo->supportedCodecs().contains(QLatin1String("audio/pcm"))) + return; + + if (formatMimeType.isEmpty() || formatMimeType.contains(QLatin1String("audio/x-wav"))) { + wavFile = true; + m_format.setCodec("audio/pcm"); + } else { + wavFile = false; + m_format.setCodec(formatMimeType); + } + } +} + +QString AudioCaptureSession::containerMimeType() const +{ + if(wavFile) + return QString("audio/x-wav"); + + return QString("audio/pcm"); +} + +QUrl AudioCaptureSession::outputLocation() const +{ + return m_actualSink; +} + +bool AudioCaptureSession::setOutputLocation(const QUrl& sink) +{ + m_sink = m_actualSink = sink; + return true; +} + +qint64 AudioCaptureSession::position() const +{ + return m_position; +} + +int AudioCaptureSession::state() const +{ + return int(m_state); +} + +QDir AudioCaptureSession::defaultDir() const +{ + QStringList dirCandidates; + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + dirCandidates << QLatin1String("/home/user/MyDocs"); +#endif + + dirCandidates << QDir::home().filePath("Documents"); + dirCandidates << QDir::home().filePath("My Documents"); + dirCandidates << QDir::homePath(); + dirCandidates << QDir::currentPath(); + dirCandidates << QDir::tempPath(); + + foreach (const QString &path, dirCandidates) { + QDir dir(path); + if (dir.exists() && QFileInfo(path).isWritable()) + return dir; + } + + return QDir(); +} + +QString AudioCaptureSession::generateFileName(const QDir &dir, const QString &ext) const +{ + + int lastClip = 0; + foreach(QString fileName, dir.entryList(QStringList() << QString("clip_*.%1").arg(ext))) { + int imgNumber = fileName.mid(5, fileName.size()-6-ext.length()).toInt(); + lastClip = qMax(lastClip, imgNumber); + } + + QString name = QString("clip_%1.%2").arg(lastClip+1, + 4, //fieldWidth + 10, + QLatin1Char('0')).arg(ext); + + return dir.absoluteFilePath(name); +} + +void AudioCaptureSession::record() +{ + if(!m_audioInput) { + setFormat(m_format); + } + + m_actualSink = m_sink; + + if (m_actualSink.isEmpty()) { + QString ext = wavFile ? QLatin1String("wav") : QLatin1String("raw"); + m_actualSink = generateFileName(defaultDir(), ext); + } + + if(m_actualSink.toLocalFile().length() > 0) + file.setFileName(m_actualSink.toLocalFile()); + else + file.setFileName(m_actualSink.toString()); + + if(m_audioInput) { + if(m_state == QMediaRecorder::StoppedState) { + if(file.open(QIODevice::WriteOnly)) { + memset(&header,0,sizeof(CombinedHeader)); + memcpy(header.riff.descriptor.id,"RIFF",4); + header.riff.descriptor.size = 0xFFFFFFFF; // This should be updated on stop(), filesize-8 + memcpy(header.riff.type,"WAVE",4); + memcpy(header.wave.descriptor.id,"fmt ",4); + header.wave.descriptor.size = 16; + header.wave.audioFormat = 1; // for PCM data + header.wave.numChannels = m_format.channels(); + header.wave.sampleRate = m_format.frequency(); + header.wave.byteRate = m_format.frequency()*m_format.channels()*m_format.sampleSize()/8; + header.wave.blockAlign = m_format.channels()*m_format.sampleSize()/8; + header.wave.bitsPerSample = m_format.sampleSize(); + memcpy(header.data.descriptor.id,"data",4); + header.data.descriptor.size = 0xFFFFFFFF; // This should be updated on stop(),samples*channels*sampleSize/8 + if (wavFile) + file.write((char*)&header,sizeof(CombinedHeader)); + + m_audioInput->start(qobject_cast(&file)); + } else { + emit error(1,QString("can't open source, failed")); + m_state = QMediaRecorder::StoppedState; + emit stateChanged(m_state); + } + } + } + + m_state = QMediaRecorder::RecordingState; +} + +void AudioCaptureSession::pause() +{ + if(m_audioInput) + m_audioInput->stop(); + + m_state = QMediaRecorder::PausedState; +} + +void AudioCaptureSession::stop() +{ + if(m_audioInput) { + m_audioInput->stop(); + file.close(); + if (wavFile) { + qint32 fileSize = file.size()-8; + file.open(QIODevice::ReadWrite | QIODevice::Unbuffered); + file.read((char*)&header,sizeof(CombinedHeader)); + header.riff.descriptor.size = fileSize; // filesize-8 + header.data.descriptor.size = fileSize-44; // samples*channels*sampleSize/8 + file.seek(0); + file.write((char*)&header,sizeof(CombinedHeader)); + file.close(); + } + m_position = 0; + } + m_state = QMediaRecorder::StoppedState; +} + +void AudioCaptureSession::stateChanged(QAudio::State state) +{ + switch(state) { + case QAudio::ActiveState: + emit stateChanged(QMediaRecorder::RecordingState); + break; + default: + if(!((m_state == QMediaRecorder::PausedState)||(m_state == QMediaRecorder::StoppedState))) + m_state = QMediaRecorder::StoppedState; + + emit stateChanged(m_state); + break; + } +} + +void AudioCaptureSession::notify() +{ + m_position += m_audioInput->notifyInterval(); + emit positionChanged(m_position); +} + +void AudioCaptureSession::setCaptureDevice(const QString &deviceName) +{ + m_captureDevice = deviceName; + if(m_deviceInfo) + delete m_deviceInfo; + + m_deviceInfo = 0; + + QList devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for(int i = 0; i < devices.size(); i++) { + if(qstrcmp(m_captureDevice.toLocal8Bit().constData(), + devices.at(i).deviceName().toLocal8Bit().constData())==0){ + m_deviceInfo = new QAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice()); + return; + } + } + m_deviceInfo = new QAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice()); +} + + + diff --git a/src/plugins/audiocapture/audiocapturesession.h b/src/plugins/audiocapture/audiocapturesession.h new file mode 100644 index 000000000..0ce139f9a --- /dev/null +++ b/src/plugins/audiocapture/audiocapturesession.h @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOCAPTURESESSION_H +#define AUDIOCAPTURESESSION_H + +#include +#include +#include + +#include "audioencodercontrol.h" +#include "audioendpointselector.h" +#include "audiomediarecordercontrol.h" + +#include +#include +#include + +QT_USE_NAMESPACE + +class AudioCaptureSession : public QObject +{ + Q_OBJECT + +public: + AudioCaptureSession(QObject *parent = 0); + ~AudioCaptureSession(); + + QAudioFormat format() const; + QAudioDeviceInfo* deviceInfo() const; + bool isFormatSupported(const QAudioFormat &format) const; + bool setFormat(const QAudioFormat &format); + QStringList supportedContainers() const; + QString containerMimeType() const; + void setContainerMimeType(const QString &formatMimeType); + QString containerDescription(const QString &formatMimeType) const; + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl& sink); + qint64 position() const; + int state() const; + void record(); + void pause(); + void stop(); + +public slots: + void setCaptureDevice(const QString &deviceName); + +signals: + void stateChanged(QMediaRecorder::State state); + void positionChanged(qint64 position); + void error(int error, const QString &errorString); + +private slots: + void stateChanged(QAudio::State state); + void notify(); + +private: + QDir defaultDir() const; + QString generateFileName(const QDir &dir, const QString &ext) const; + + QFile file; + QString m_captureDevice; + QUrl m_sink; + QUrl m_actualSink; + QMediaRecorder::State m_state; + QAudioInput *m_audioInput; + QAudioDeviceInfo *m_deviceInfo; + QAudioFormat m_format; + qint64 m_position; + bool wavFile; + + // WAV header stuff + + struct chunk + { + char id[4]; + quint32 size; + }; + + struct RIFFHeader + { + chunk descriptor; + char type[4]; + }; + + struct WAVEHeader + { + chunk descriptor; + quint16 audioFormat; // PCM = 1 + quint16 numChannels; + quint32 sampleRate; + quint32 byteRate; + quint16 blockAlign; + quint16 bitsPerSample; + }; + + struct DATAHeader + { + chunk descriptor; +// quint8 data[]; + }; + + struct CombinedHeader + { + RIFFHeader riff; + WAVEHeader wave; + DATAHeader data; + }; + + CombinedHeader header; +}; + +#endif diff --git a/src/plugins/audiocapture/audiocontainercontrol.cpp b/src/plugins/audiocapture/audiocontainercontrol.cpp new file mode 100644 index 000000000..454f43eda --- /dev/null +++ b/src/plugins/audiocapture/audiocontainercontrol.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "audiocontainercontrol.h" +#include "audiocapturesession.h" + +AudioContainerControl::AudioContainerControl(QObject *parent) + :QMediaContainerControl(parent) +{ + m_session = qobject_cast(parent); +} + +AudioContainerControl::~AudioContainerControl() +{ +} + +QStringList AudioContainerControl::supportedContainers() const +{ + return m_session->supportedContainers(); +} + +QString AudioContainerControl::containerMimeType() const +{ + return m_session->containerMimeType(); +} + +void AudioContainerControl::setContainerMimeType(const QString &formatMimeType) +{ + m_session->setContainerMimeType(formatMimeType); +} + +QString AudioContainerControl::containerDescription(const QString &formatMimeType) const +{ + return m_session->containerDescription(formatMimeType); +} + diff --git a/src/plugins/audiocapture/audiocontainercontrol.h b/src/plugins/audiocapture/audiocontainercontrol.h new file mode 100644 index 000000000..5c8d7b267 --- /dev/null +++ b/src/plugins/audiocapture/audiocontainercontrol.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOCONTAINERCONTROL_H +#define AUDIOCONTAINERCONTROL_H + +#include "qmediacontainercontrol.h" + +#include +#include + +class AudioCaptureSession; + +QT_USE_NAMESPACE + +class AudioContainerControl : public QMediaContainerControl +{ + Q_OBJECT +public: + AudioContainerControl(QObject *parent); + virtual ~AudioContainerControl(); + + QStringList supportedContainers() const; + QString containerMimeType() const; + void setContainerMimeType(const QString &formatMimeType); + QString containerDescription(const QString &formatMimeType) const; + +private: + AudioCaptureSession* m_session; +}; + +#endif diff --git a/src/plugins/audiocapture/audioencodercontrol.cpp b/src/plugins/audiocapture/audioencodercontrol.cpp new file mode 100644 index 000000000..888deac45 --- /dev/null +++ b/src/plugins/audiocapture/audioencodercontrol.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "audioencodercontrol.h" +#include "audiocapturesession.h" + +#include + +#include + +AudioEncoderControl::AudioEncoderControl(QObject *parent) + :QAudioEncoderControl(parent) +{ + m_session = qobject_cast(parent); + + QT_PREPEND_NAMESPACE(QAudioFormat) fmt; + fmt.setSampleSize(8); + fmt.setChannels(1); + fmt.setFrequency(8000); + fmt.setSampleType(QT_PREPEND_NAMESPACE(QAudioFormat)::SignedInt); + fmt.setCodec("audio/pcm"); + fmt.setByteOrder(QAudioFormat::LittleEndian); + m_session->setFormat(fmt); + + m_settings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + m_settings.setCodec("audio/pcm"); + m_settings.setBitRate(8000); + m_settings.setChannelCount(1); + m_settings.setSampleRate(8000); + m_settings.setQuality(QtMultimediaKit::LowQuality); +} + +AudioEncoderControl::~AudioEncoderControl() +{ +} + +QStringList AudioEncoderControl::supportedAudioCodecs() const +{ + QStringList list; + if (m_session->supportedContainers().size() > 0) + list.append("audio/pcm"); + + return list; +} + +QString AudioEncoderControl::codecDescription(const QString &codecName) const +{ + if (codecName.contains(QLatin1String("audio/pcm"))) + return tr("PCM audio data"); + + return QString(); +} + +QStringList AudioEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + Q_UNUSED(codec) + + QStringList list; + return list; +} + +QVariant AudioEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + Q_UNUSED(codec) + Q_UNUSED(name) + + return QVariant(); +} + +void AudioEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + Q_UNUSED(value) + Q_UNUSED(codec) + Q_UNUSED(name) +} + +QList AudioEncoderControl::supportedSampleRates(const QAudioEncoderSettings &, bool *continuous) const +{ + if (continuous) + *continuous = false; + + return m_session->deviceInfo()->supportedFrequencies(); +} + +QAudioEncoderSettings AudioEncoderControl::audioSettings() const +{ + return m_settings; +} + +void AudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings) +{ + QAudioFormat fmt = m_session->format(); + + if (settings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) { + if (settings.quality() == QtMultimediaKit::LowQuality) { + fmt.setSampleSize(8); + fmt.setChannels(1); + fmt.setFrequency(8000); + fmt.setSampleType(QAudioFormat::UnSignedInt); + + } else if (settings.quality() == QtMultimediaKit::NormalQuality) { + fmt.setSampleSize(16); + fmt.setChannels(1); + fmt.setFrequency(22050); + fmt.setSampleType(QAudioFormat::SignedInt); + + } else { + fmt.setSampleSize(16); + fmt.setChannels(1); + fmt.setFrequency(44100); + fmt.setSampleType(QAudioFormat::SignedInt); + } + + } else { + fmt.setChannels(settings.channelCount()); + fmt.setFrequency(settings.sampleRate()); + if (settings.sampleRate() == 8000 && settings.bitRate() == 8000) { + fmt.setSampleType(QAudioFormat::UnSignedInt); + fmt.setSampleSize(8); + } else { + fmt.setSampleSize(16); + fmt.setSampleType(QAudioFormat::SignedInt); + } + } + fmt.setCodec("audio/pcm"); + + m_session->setFormat(fmt); + m_settings = settings; +} diff --git a/src/plugins/audiocapture/audioencodercontrol.h b/src/plugins/audiocapture/audioencodercontrol.h new file mode 100644 index 000000000..09ac8b2b2 --- /dev/null +++ b/src/plugins/audiocapture/audioencodercontrol.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOENCODERCONTROL_H +#define AUDIOENCODERCONTROL_H + +#include "qaudioencodercontrol.h" + +#include +#include + +#include + +class AudioCaptureSession; + +QT_USE_NAMESPACE + +class AudioEncoderControl : public QAudioEncoderControl +{ + Q_OBJECT +public: + AudioEncoderControl(QObject *parent); + virtual ~AudioEncoderControl(); + + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + QList supportedSampleRates(const QAudioEncoderSettings &, bool *continuous = 0) const; + + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings&); + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: + AudioCaptureSession* m_session; + QAudioEncoderSettings m_settings; +}; + +#endif diff --git a/src/plugins/audiocapture/audioendpointselector.cpp b/src/plugins/audiocapture/audioendpointselector.cpp new file mode 100644 index 000000000..dc7ca8e90 --- /dev/null +++ b/src/plugins/audiocapture/audioendpointselector.cpp @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "audiocapturesession.h" +#include "audioendpointselector.h" + +#include + + +AudioEndpointSelector::AudioEndpointSelector(QObject *parent) + :QAudioEndpointSelector(parent) +{ + m_session = qobject_cast(parent); + + update(); + + m_audioInput = defaultEndpoint(); +} + +AudioEndpointSelector::~AudioEndpointSelector() +{ +} + +QList AudioEndpointSelector::availableEndpoints() const +{ + return m_names; +} + +QString AudioEndpointSelector::endpointDescription(const QString& name) const +{ + QString desc; + + for(int i = 0; i < m_names.count(); i++) { + if (m_names.at(i).compare(name) == 0) { + desc = m_names.at(i); + break; + } + } + return desc; +} + +QString AudioEndpointSelector::defaultEndpoint() const +{ + return QAudioDeviceInfo(QAudioDeviceInfo::defaultInputDevice()).deviceName(); +} + +QString AudioEndpointSelector::activeEndpoint() const +{ + return m_audioInput; +} + +void AudioEndpointSelector::setActiveEndpoint(const QString& name) +{ + if (m_audioInput.compare(name) != 0) { + m_audioInput = name; + m_session->setCaptureDevice(name); + emit activeEndpointChanged(name); + } +} + +void AudioEndpointSelector::update() +{ + m_names.clear(); + m_descriptions.clear(); + + QList devices; + devices = QAudioDeviceInfo::availableDevices(QAudio::AudioInput); + for(int i = 0; i < devices.size(); ++i) { + m_names.append(devices.at(i).deviceName()); + m_descriptions.append(devices.at(i).deviceName()); + } +} diff --git a/src/plugins/audiocapture/audioendpointselector.h b/src/plugins/audiocapture/audioendpointselector.h new file mode 100644 index 000000000..dda3c59c7 --- /dev/null +++ b/src/plugins/audiocapture/audioendpointselector.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOENDPOINTSELECTOR_H +#define AUDIOENDPOINTSELECTOR_H + +#include + +#include "qaudioendpointselector.h" + +class AudioCaptureSession; + +QT_USE_NAMESPACE + +class AudioEndpointSelector : public QAudioEndpointSelector +{ +Q_OBJECT +public: + AudioEndpointSelector(QObject *parent); + virtual ~AudioEndpointSelector(); + + QList availableEndpoints() const; + QString endpointDescription(const QString& name) const; + QString defaultEndpoint() const; + QString activeEndpoint() const; + +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +private: + void update(); + + QString m_audioInput; + QList m_names; + QList m_descriptions; + AudioCaptureSession* m_session; +}; + +#endif // AUDIOENDPOINTSELECTOR_H diff --git a/src/plugins/audiocapture/audiomediarecordercontrol.cpp b/src/plugins/audiocapture/audiomediarecordercontrol.cpp new file mode 100644 index 000000000..b1f550a04 --- /dev/null +++ b/src/plugins/audiocapture/audiomediarecordercontrol.cpp @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "audiocapturesession.h" +#include "audiomediarecordercontrol.h" + +#include + +AudioMediaRecorderControl::AudioMediaRecorderControl(QObject *parent) + :QMediaRecorderControl(parent) +{ + m_session = qobject_cast(parent); + connect(m_session,SIGNAL(positionChanged(qint64)),this,SIGNAL(durationChanged(qint64))); + connect(m_session,SIGNAL(stateChanged(QMediaRecorder::State)),this,SIGNAL(stateChanged(QMediaRecorder::State))); + connect(m_session,SIGNAL(error(int,QString)),this,SIGNAL(error(int,QString))); +} + +AudioMediaRecorderControl::~AudioMediaRecorderControl() +{ +} + +QUrl AudioMediaRecorderControl::outputLocation() const +{ + return m_session->outputLocation(); +} + +bool AudioMediaRecorderControl::setOutputLocation(const QUrl& sink) +{ + return m_session->setOutputLocation(sink); +} + +QMediaRecorder::State AudioMediaRecorderControl::state() const +{ + return (QMediaRecorder::State)m_session->state(); +} + +qint64 AudioMediaRecorderControl::duration() const +{ + return m_session->position(); +} + +void AudioMediaRecorderControl::record() +{ + m_session->record(); +} + +void AudioMediaRecorderControl::pause() +{ + m_session->stop(); +} + +void AudioMediaRecorderControl::stop() +{ + m_session->stop(); +} + +bool AudioMediaRecorderControl::isMuted() const +{ + return false; +} + +void AudioMediaRecorderControl::setMuted(bool) +{ +} diff --git a/src/plugins/audiocapture/audiomediarecordercontrol.h b/src/plugins/audiocapture/audiomediarecordercontrol.h new file mode 100644 index 000000000..a3528a875 --- /dev/null +++ b/src/plugins/audiocapture/audiomediarecordercontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOMEDIARECORDERCONTROL_H +#define AUDIOMEDIARECORDERCONTROL_H + +#include + +#include "qmediarecorder.h" +#include "qmediarecordercontrol.h" + +class AudioCaptureSession; + +QT_USE_NAMESPACE + +class AudioMediaRecorderControl : public QMediaRecorderControl +{ + Q_OBJECT +public: + AudioMediaRecorderControl(QObject *parent = 0); + ~AudioMediaRecorderControl(); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &sink); + + QMediaRecorder::State state() const; + + qint64 duration() const; + + bool isMuted() const; + + void applySettings() {} + +public slots: + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private: + AudioCaptureSession* m_session; +}; + +#endif diff --git a/src/plugins/directshow/camera/camera.pri b/src/plugins/directshow/camera/camera.pri new file mode 100644 index 000000000..42f5999a2 --- /dev/null +++ b/src/plugins/directshow/camera/camera.pri @@ -0,0 +1,31 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_DIRECTSHOW_CAMERA + +win32-g++: DEFINES += QT_NO_WMSDK + +win32: DEFINES += _CRT_SECURE_NO_WARNINGS + +HEADERS += \ + $$PWD/dscameraservice.h \ + $$PWD/dscameracontrol.h \ + $$PWD/dsvideorenderer.h \ + $$PWD/dsvideodevicecontrol.h \ + $$PWD/dsimagecapturecontrol.h \ + $$PWD/dscamerasession.h \ + $$PWD/dsvideowidgetcontrol.h \ + $$PWD/dscameraservice.h \ + $$PWD/directshowglobal.h + + +SOURCES += \ + $$PWD/dscameraservice.cpp \ + $$PWD/dscameracontrol.cpp \ + $$PWD/dsvideorenderer.cpp \ + $$PWD/dsvideodevicecontrol.cpp \ + $$PWD/dsimagecapturecontrol.cpp \ + $$PWD/dscamerasession.cpp \ + $$PWD/dsvideowidgetcontrol.cpp + +INCLUDEPATH += $(DXSDK_DIR)/include +LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 diff --git a/src/plugins/directshow/camera/directshowglobal.h b/src/plugins/directshow/camera/directshowglobal.h new file mode 100644 index 000000000..76c143798 --- /dev/null +++ b/src/plugins/directshow/camera/directshowglobal.h @@ -0,0 +1,236 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWGLOBAL_H +#define DIRECTSHOWGLOBAL_H + +#include + +#include + +DEFINE_GUID(MEDIASUBTYPE_I420, + 0x30323449,0x0000,0x0010,0x80,0x00,0x00,0xAA,0x00,0x38,0x9B,0x71); + +extern const GUID MEDIASUBTYPE_RGB24; +extern const GUID MEDIASUBTYPE_RGB32; +extern const GUID MEDIASUBTYPE_YUY2; +extern const GUID MEDIASUBTYPE_MJPG; +extern const GUID MEDIASUBTYPE_RGB555; +extern const GUID MEDIASUBTYPE_YVU9; +extern const GUID MEDIASUBTYPE_UYVY; +extern const GUID PIN_CATEGORY_CAPTURE; +extern const GUID PIN_CATEGORY_PREVIEW; + +extern const IID IID_IPropertyBag; +extern const IID IID_ISampleGrabber; +extern const IID IID_ICaptureGraphBuilder2; +extern const IID IID_IAMStreamConfig; + + +extern const CLSID CLSID_CVidCapClassManager; +extern const CLSID CLSID_VideoInputDeviceCategory; +extern const CLSID CLSID_SampleGrabber; +extern const CLSID CLSID_CaptureGraphBuilder2; + +#define SAFE_RELEASE(x) { if(x) x->Release(); x = NULL; } + +typedef struct IFileSinkFilter *LPFILESINKFILTER; +typedef struct IAMCopyCaptureFileProgress *LPAMCOPYCAPTUREFILEPROGRESS; + +#ifndef __ICaptureGraphBuilder2_INTERFACE_DEFINED__ +#define __ICaptureGraphBuilder2_INTERFACE_DEFINED__ +struct ICaptureGraphBuilder2 : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetFiltergraph( + /* [in] */ IGraphBuilder *pfg) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFiltergraph( + /* [out] */ IGraphBuilder **ppfg) = 0; + + virtual HRESULT STDMETHODCALLTYPE SetOutputFileName( + /* [in] */ const GUID *pType, + /* [in] */ LPCOLESTR lpstrFile, + /* [out] */ IBaseFilter **ppf, + /* [out] */ IFileSinkFilter **ppSink) = 0; + + virtual /* [local] */ HRESULT STDMETHODCALLTYPE FindInterface( + /* [in] */ const GUID *pCategory, + /* [in] */ const GUID *pType, + /* [in] */ IBaseFilter *pf, + /* [in] */ REFIID riid, + /* [out] */ void **ppint) = 0; + + virtual HRESULT STDMETHODCALLTYPE RenderStream( + /* [in] */ const GUID *pCategory, + /* [in] */ const GUID *pType, + /* [in] */ IUnknown *pSource, + /* [in] */ IBaseFilter *pfCompressor, + /* [in] */ IBaseFilter *pfRenderer) = 0; + + virtual HRESULT STDMETHODCALLTYPE ControlStream( + /* [in] */ const GUID *pCategory, + /* [in] */ const GUID *pType, + /* [in] */ IBaseFilter *pFilter, + /* [in] */ REFERENCE_TIME *pstart, + /* [in] */ REFERENCE_TIME *pstop, + /* [in] */ WORD wStartCookie, + /* [in] */ WORD wStopCookie) = 0; + + virtual HRESULT STDMETHODCALLTYPE AllocCapFile( + /* [in] */ LPCOLESTR lpstr, + /* [in] */ DWORDLONG dwlSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE CopyCaptureFile( + /* [in] */ LPOLESTR lpwstrOld, + /* [in] */ LPOLESTR lpwstrNew, + /* [in] */ int fAllowEscAbort, + /* [in] */ IAMCopyCaptureFileProgress *pCallback) = 0; + + virtual HRESULT STDMETHODCALLTYPE FindPin( + /* [in] */ IUnknown *pSource, + /* [in] */ PIN_DIRECTION pindir, + /* [in] */ const GUID *pCategory, + /* [in] */ const GUID *pType, + /* [in] */ BOOL fUnconnected, + /* [in] */ int num, + /* [out] */ IPin **ppPin) = 0; + +}; +#endif + +#ifndef __IAMStreamConfig_INTERFACE_DEFINED__ +#define __IAMStreamConfig_INTERFACE_DEFINED__ +struct IAMStreamConfig : public IUnknown +{ +public: + virtual HRESULT STDMETHODCALLTYPE SetFormat( + /* [in] */ AM_MEDIA_TYPE *pmt) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetFormat( + /* [out] */ AM_MEDIA_TYPE **ppmt) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetNumberOfCapabilities( + /* [out] */ int *piCount, + /* [out] */ int *piSize) = 0; + + virtual HRESULT STDMETHODCALLTYPE GetStreamCaps( + /* [in] */ int iIndex, + /* [out] */ AM_MEDIA_TYPE **ppmt, + /* [out] */ BYTE *pSCC) = 0; + +}; +#endif + +#ifndef __IErrorLog_INTERFACE_DEFINED__ +#define __IErrorLog_INTERFACE_DEFINED__ +struct IErrorLog : public IUnknown + { + public: + virtual HRESULT STDMETHODCALLTYPE AddError( + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ EXCEPINFO *pExcepInfo) = 0; + + }; +#endif + +#ifndef __IPropertyBag_INTERFACE_DEFINED__ +#define __IPropertyBag_INTERFACE_DEFINED__ +struct IPropertyBag : public IUnknown +{ +public: + virtual /* [local] */ HRESULT STDMETHODCALLTYPE Read( + /* [in] */ LPCOLESTR pszPropName, + /* [out][in] */ VARIANT *pVar, + /* [in] */ IErrorLog *pErrorLog) = 0; + + virtual HRESULT STDMETHODCALLTYPE Write( + /* [in] */ LPCOLESTR pszPropName, + /* [in] */ VARIANT *pVar) = 0; + +}; +#endif + +typedef struct IMediaSample *LPMEDIASAMPLE; + +EXTERN_C const IID IID_ISampleGrabberCB; + +#ifndef __ISampleGrabberCB_INTERFACE_DEFINED__ +#define __ISampleGrabberCB_INTERFACE_DEFINED__ + +#undef INTERFACE +#define INTERFACE ISampleGrabberCB +DECLARE_INTERFACE_(ISampleGrabberCB, IUnknown) +{ +// STDMETHOD(QueryInterface) (THIS_ const GUID *, void **) PURE; + STDMETHOD(QueryInterface) (THIS_ REFIID riid, void **) PURE; + STDMETHOD_(ULONG, AddRef) (THIS) PURE; + STDMETHOD_(ULONG, Release) (THIS) PURE; + STDMETHOD_(HRESULT, SampleCB) (THIS_ double, LPMEDIASAMPLE) PURE; + STDMETHOD_(HRESULT, BufferCB) (THIS_ double, BYTE *, long) PURE; +}; +#undef INTERFACE + +#endif + + +#ifndef __ISampleGrabber_INTERFACE_DEFINED__ +#define __ISampleGrabber_INTERFACE_DEFINED__ + +#define INTERFACE ISampleGrabber +DECLARE_INTERFACE_(ISampleGrabber,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(SetOneShot)(THIS_ BOOL) PURE; + STDMETHOD(SetMediaType)(THIS_ const AM_MEDIA_TYPE*) PURE; + STDMETHOD(GetConnectedMediaType)(THIS_ AM_MEDIA_TYPE*) PURE; + STDMETHOD(SetBufferSamples)(THIS_ BOOL) PURE; + STDMETHOD(GetCurrentBuffer)(THIS_ long*,long*) PURE; + STDMETHOD(GetCurrentSample)(THIS_ IMediaSample**) PURE; + STDMETHOD(SetCallback)(THIS_ ISampleGrabberCB *,long) PURE; +}; +#undef INTERFACE +#endif + + +#endif diff --git a/src/plugins/directshow/camera/dscameracontrol.cpp b/src/plugins/directshow/camera/dscameracontrol.cpp new file mode 100644 index 000000000..b09064c2b --- /dev/null +++ b/src/plugins/directshow/camera/dscameracontrol.cpp @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "dscameracontrol.h" +#include "dscameraservice.h" +#include "dscamerasession.h" + +QT_BEGIN_NAMESPACE + +DSCameraControl::DSCameraControl(QObject *parent) + :QCameraControl(parent), m_captureMode(QCamera::CaptureStillImage) +{ + m_session = qobject_cast(parent); + connect(m_session, SIGNAL(stateChanged(QCamera::State)),this, SIGNAL(stateChanged(QCamera::State))); +} + +DSCameraControl::~DSCameraControl() +{ +} + +void DSCameraControl::setState(QCamera::State state) +{ + switch (state) { + case QCamera::ActiveState: + start(); + break; + case QCamera::UnloadedState: /* fall through */ + case QCamera::LoadedState: + stop(); + break; + } +} + +bool DSCameraControl::isCaptureModeSupported(QCamera::CaptureMode mode) const +{ + bool bCaptureSupported = false; + switch (mode) { + case QCamera::CaptureStillImage: + bCaptureSupported = true; + break; + case QCamera::CaptureVideo: + bCaptureSupported = false; + break; + } + return bCaptureSupported; +} + +void DSCameraControl::start() +{ + m_session->record(); +} + +void DSCameraControl::stop() +{ + m_session->stop(); +} + +QCamera::State DSCameraControl::state() const +{ + return (QCamera::State)m_session->state(); +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/camera/dscameracontrol.h b/src/plugins/directshow/camera/dscameracontrol.h new file mode 100644 index 000000000..9b20563d2 --- /dev/null +++ b/src/plugins/directshow/camera/dscameracontrol.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSCAMERACONTROL_H +#define DSCAMERACONTROL_H + +#include +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DSCameraService; +class DSCameraSession; + + +class DSCameraControl : public QCameraControl +{ + Q_OBJECT +public: + DSCameraControl(QObject *parent = 0); + ~DSCameraControl(); + + void start(); + void stop(); + QCamera::State state() const; + + QCamera::CaptureMode captureMode() const { return m_captureMode; } + void setCaptureMode(QCamera::CaptureMode mode) + { + if (m_captureMode != mode) { + m_captureMode = mode; + emit captureModeChanged(mode); + } + } + + void setState(QCamera::State state); + + QCamera::Status status() const { return QCamera::UnavailableStatus; } + bool isCaptureModeSupported(QCamera::CaptureMode mode) const; + bool canChangeProperty(PropertyChangeType /* changeType */, QCamera::Status /* status */) const {return false; } + +private: + DSCameraSession *m_session; + DSCameraService *m_service; + QCamera::CaptureMode m_captureMode; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif + + diff --git a/src/plugins/directshow/camera/dscameraservice.cpp b/src/plugins/directshow/camera/dscameraservice.cpp new file mode 100644 index 000000000..9d73da02c --- /dev/null +++ b/src/plugins/directshow/camera/dscameraservice.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + + +#include "dscameraservice.h" +#include "dscameracontrol.h" +#include "dscamerasession.h" +#include "dsvideorenderer.h" +#include "dsvideodevicecontrol.h" +#include "dsimagecapturecontrol.h" +#include "dsvideowidgetcontrol.h" + +QT_BEGIN_NAMESPACE + +DSCameraService::DSCameraService(QObject *parent): + QMediaService(parent) +{ + m_session = new DSCameraSession(this); + + m_control = new DSCameraControl(m_session); + + m_videoDevice = new DSVideoDeviceControl(m_session); + + m_videoRenderer = new DSVideoRendererControl(m_session, this); + + m_imageCapture = new DSImageCaptureControl(m_session); + + m_viewFinderWidget = new DSVideoWidgetControl(m_session); + + m_device = QByteArray("default"); +} + +DSCameraService::~DSCameraService() +{ + delete m_control; + delete m_videoDevice; + delete m_videoRenderer; + delete m_imageCapture; + delete m_viewFinderWidget; + delete m_session; +} + +QMediaControl* DSCameraService::requestControl(const char *name) +{ + if(qstrcmp(name,QCameraControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return m_imageCapture; + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + if (m_viewFinderWidget) { + return m_viewFinderWidget; + } + } + + if(qstrcmp(name,QVideoRendererControl_iid) == 0) + return m_videoRenderer; + + if(qstrcmp(name,QVideoDeviceControl_iid) == 0) + return m_videoDevice; + + return 0; +} + +void DSCameraService::releaseControl(QMediaControl *control) +{ + // Implemented as a singleton, so we do nothing. +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/camera/dscameraservice.h b/src/plugins/directshow/camera/dscameraservice.h new file mode 100644 index 000000000..e8a9450fb --- /dev/null +++ b/src/plugins/directshow/camera/dscameraservice.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSCAMERASERVICE_H +#define DSCAMERASERVICE_H + +#include + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DSCameraControl; +class DSCameraSession; +class DSVideoOutputControl; +class DSVideoDeviceControl; +class DSVideoRendererControl; +class DSImageCaptureControl; +class DSVideoWidgetControl; + + +class DSCameraService : public QMediaService +{ + Q_OBJECT + +public: + DSCameraService(QObject *parent = 0); + ~DSCameraService(); + + virtual QMediaControl* requestControl(const char *name); + virtual void releaseControl(QMediaControl *control); + +private: + DSCameraControl *m_control; + DSCameraSession *m_session; + DSVideoOutputControl *m_videoOutput; + DSVideoWidgetControl *m_viewFinderWidget; + DSVideoDeviceControl *m_videoDevice; + DSVideoRendererControl *m_videoRenderer; + DSImageCaptureControl *m_imageCapture; + QByteArray m_device; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/directshow/camera/dscamerasession.cpp b/src/plugins/directshow/camera/dscamerasession.cpp new file mode 100644 index 000000000..a08fb318f --- /dev/null +++ b/src/plugins/directshow/camera/dscamerasession.cpp @@ -0,0 +1,1160 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "dscamerasession.h" +#include "dsvideorenderer.h" +#include "directshowglobal.h" + +QT_BEGIN_NAMESPACE + +// If frames come in quicker than we display them, we allow the queue to build +// up to this number before we start dropping them. +const int LIMIT_FRAME = 5; + +namespace { +// DirectShow helper implementation +void _FreeMediaType(AM_MEDIA_TYPE& mt) +{ + if (mt.cbFormat != 0) { + CoTaskMemFree((PVOID)mt.pbFormat); + mt.cbFormat = 0; + mt.pbFormat = NULL; + } + if (mt.pUnk != NULL) { + // pUnk should not be used. + mt.pUnk->Release(); + mt.pUnk = NULL; + } +} + +} // end namespace + +class SampleGrabberCallbackPrivate : public ISampleGrabberCB +{ +public: + STDMETHODIMP_(ULONG) AddRef() { return 1; } + STDMETHODIMP_(ULONG) Release() { return 2; } + + STDMETHODIMP QueryInterface(REFIID riid, void **ppvObject) + { + if (NULL == ppvObject) + return E_POINTER; + if (riid == IID_IUnknown /*__uuidof(IUnknown) */ ) { + *ppvObject = static_cast(this); + return S_OK; + } + if (riid == IID_ISampleGrabberCB /*__uuidof(ISampleGrabberCB)*/ ) { + *ppvObject = static_cast(this); + return S_OK; + } + return E_NOTIMPL; + } + + STDMETHODIMP SampleCB(double Time, IMediaSample *pSample) + { + return E_NOTIMPL; + } + + STDMETHODIMP BufferCB(double Time, BYTE *pBuffer, long BufferLen) + { + if (!cs || active) { + return S_OK; + } + + if ((cs->StillMediaType.majortype != MEDIATYPE_Video) || + (cs->StillMediaType.formattype != FORMAT_VideoInfo) || + (cs->StillMediaType.cbFormat < sizeof(VIDEOINFOHEADER))) { + return VFW_E_INVALIDMEDIATYPE; + } + + active = true; + + if(toggle == true) { + toggle = false; + } + else { + toggle = true; + } + + if(toggle) { + active = false; + return S_OK; + } + + bool check = false; + cs->mutex.lock(); + + if (cs->frames.size() > LIMIT_FRAME) { + check = true; + } + + if (check) { + cs->mutex.unlock(); + // Frames building up. We're going to drop some here + Sleep(100); + active = false; + return S_OK; + } + cs->mutex.unlock(); + + unsigned char* vidData = new unsigned char[BufferLen]; + memcpy(vidData, pBuffer, BufferLen); + + cs->mutex.lock(); + + video_buffer* buf = new video_buffer; + buf->buffer = vidData; + buf->length = BufferLen; + buf->time = (qint64)Time; + + cs->frames.append(buf); + + cs->mutex.unlock(); + + QMetaObject::invokeMethod(cs, "captureFrame", Qt::QueuedConnection); + + active = false; + + return S_OK; + } + + DSCameraSession* cs; + bool active; + bool toggle; +}; + + +DSCameraSession::DSCameraSession(QObject *parent) + : QObject(parent) + ,m_currentImageId(0) +{ + pBuild = NULL; + pGraph = NULL; + pCap = NULL; + pSG_Filter = NULL; + pSG = NULL; + + opened = false; + available = false; + resolutions.clear(); + m_state = QCamera::UnloadedState; + m_device = "default"; + + StillCapCB = new SampleGrabberCallbackPrivate; + StillCapCB->cs = this; + StillCapCB->active = false; + StillCapCB->toggle = false; + + m_output = 0; + m_surface = 0; + m_windowSize = QSize(320,240); + pixelF = QVideoFrame::Format_RGB24; + actualFormat = QVideoSurfaceFormat(m_windowSize,pixelF); + + graph = false; + active = false; + + ::CoInitialize(NULL); +} + +DSCameraSession::~DSCameraSession() +{ + if (opened) { + closeStream(); + } + + CoUninitialize(); + + SAFE_RELEASE(pCap); + SAFE_RELEASE(pSG_Filter); + SAFE_RELEASE(pGraph); + SAFE_RELEASE(pBuild); + + if (StillCapCB) { + delete StillCapCB; + } +} + +int DSCameraSession::captureImage(const QString &fileName) +{ + emit readyForCaptureChanged(false); + + // We're going to do this in one big synchronous call + m_currentImageId++; + if (fileName.isEmpty()) { + m_snapshot = "img.jpg"; + } else { + m_snapshot = fileName; + } + + if (!active) { + startStream(); + } + + return m_currentImageId; +} + +void DSCameraSession::setSurface(QAbstractVideoSurface* surface) +{ + m_surface = surface; +} + +bool DSCameraSession::deviceReady() +{ + return available; +} + +bool DSCameraSession::pictureInProgress() +{ + return m_snapshot.isEmpty(); +} + +int DSCameraSession::framerate() const +{ + return -1; +} + +void DSCameraSession::setFrameRate(int rate) +{ + Q_UNUSED(rate) +} + +int DSCameraSession::brightness() const +{ + return -1; +} + +void DSCameraSession::setBrightness(int b) +{ + Q_UNUSED(b) +} + +int DSCameraSession::contrast() const +{ + return -1; +} + +void DSCameraSession::setContrast(int c) +{ + Q_UNUSED(c) +} + +int DSCameraSession::saturation() const +{ + return -1; +} + +void DSCameraSession::setSaturation(int s) +{ + Q_UNUSED(s) +} + +int DSCameraSession::hue() const +{ + return -1; +} + +void DSCameraSession::setHue(int h) +{ + Q_UNUSED(h) +} + +int DSCameraSession::sharpness() const +{ + return -1; +} + +void DSCameraSession::setSharpness(int s) +{ + Q_UNUSED(s) +} + +int DSCameraSession::zoom() const +{ + return -1; +} + +void DSCameraSession::setZoom(int z) +{ + Q_UNUSED(z) +} + +bool DSCameraSession::backlightCompensation() const +{ + return false; +} + +void DSCameraSession::setBacklightCompensation(bool b) +{ + Q_UNUSED(b) +} + +int DSCameraSession::whitelevel() const +{ + return -1; +} + +void DSCameraSession::setWhitelevel(int w) +{ + Q_UNUSED(w) +} + +int DSCameraSession::rotation() const +{ + return 0; +} + +void DSCameraSession::setRotation(int r) +{ + Q_UNUSED(r) +} + +bool DSCameraSession::flash() const +{ + return false; +} + +void DSCameraSession::setFlash(bool f) +{ + Q_UNUSED(f) +} + +bool DSCameraSession::autofocus() const +{ + return false; +} + +void DSCameraSession::setAutofocus(bool f) +{ + Q_UNUSED(f) +} + +QSize DSCameraSession::frameSize() const +{ + return m_windowSize; +} + +void DSCameraSession::setFrameSize(const QSize& s) +{ + if (supportedResolutions(pixelF).contains(s)) + m_windowSize = s; + else + qWarning() << "frame size if not supported for current pixel format, no change"; +} + +void DSCameraSession::setDevice(const QString &device) +{ + if(opened) + stopStream(); + + if(graph) { + SAFE_RELEASE(pCap); + SAFE_RELEASE(pSG_Filter); + SAFE_RELEASE(pGraph); + SAFE_RELEASE(pBuild); + } + + available = false; + m_state = QCamera::LoadedState; + + CoInitialize(NULL); + + ICreateDevEnum* pDevEnum = NULL; + IEnumMoniker* pEnum = NULL; + + // Create the System device enumerator + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, + reinterpret_cast(&pDevEnum)); + if(SUCCEEDED(hr)) { + // Create the enumerator for the video capture category + hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); + if (S_OK == hr) { + pEnum->Reset(); + // go through and find all video capture devices + IMoniker* pMoniker = NULL; + while(pEnum->Next(1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, + (void**)(&pPropBag)); + if(FAILED(hr)) { + pMoniker->Release(); + continue; // skip this one + } + // Find the description + WCHAR str[120]; + VARIANT varName; + varName.vt = VT_BSTR; + hr = pPropBag->Read(L"Description", &varName, 0); + if(FAILED(hr)) + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if(SUCCEEDED(hr)) { + wcsncpy(str, varName.bstrVal, sizeof(str)/sizeof(str[0])); + QString temp(QString::fromUtf16((unsigned short*)str)); + if(temp.contains(device)) { + available = true; + } + } + pPropBag->Release(); + pMoniker->Release(); + } + pEnum->Release(); + } + pDevEnum->Release(); + } + CoUninitialize(); + + if(available) { + m_device = QByteArray(device.toLocal8Bit().constData()); + graph = createFilterGraph(); + if(!graph) + available = false; + } +} + +QList DSCameraSession::supportedPixelFormats() +{ + return types; +} + +QVideoFrame::PixelFormat DSCameraSession::pixelFormat() const +{ + return pixelF; +} + +void DSCameraSession::setPixelFormat(QVideoFrame::PixelFormat fmt) +{ + pixelF = fmt; +} + +QList DSCameraSession::supportedResolutions(QVideoFrame::PixelFormat format) +{ + if (!resolutions.contains(format)) + return QList(); + return resolutions.value(format); +} + +bool DSCameraSession::setOutputLocation(const QUrl &sink) +{ + m_sink = sink; + + return true; +} + +QUrl DSCameraSession::outputLocation() const +{ + return m_sink; +} + +qint64 DSCameraSession::position() const +{ + return timeStamp.elapsed(); +} + +int DSCameraSession::state() const +{ + return int(m_state); +} + +void DSCameraSession::record() +{ + if(opened) { + return; + } + + if(m_surface) { + bool match = false; + + if (!m_surface->isFormatSupported(actualFormat)) { + QList fmts; + foreach(QVideoFrame::PixelFormat f, types) { + if (fmts.contains(f)) { + match = true; + pixelF = f; + actualFormat = QVideoSurfaceFormat(m_windowSize,pixelF); + break; + } + } + } + if (!m_surface->isFormatSupported(actualFormat) && !match) { + // fallback + if (types.contains(QVideoFrame::Format_RGB24)) { + // get RGB24 from camera and convert to RGB32 for surface! + pixelF = QVideoFrame::Format_RGB32; + actualFormat = QVideoSurfaceFormat(m_windowSize,pixelF); + } + } + + if (m_surface->isFormatSupported(actualFormat)) { + m_surface->start(actualFormat); + m_state = QCamera::ActiveState; + emit stateChanged(QCamera::ActiveState); + } else { + qWarning() << "surface doesn't support camera format, cant start"; + m_state = QCamera::LoadedState; + emit stateChanged(QCamera::LoadedState); + return; + } + } else { + qWarning() << "no video surface, cant start"; + m_state = QCamera::LoadedState; + emit stateChanged(QCamera::LoadedState); + return; + } + + opened = startStream(); + + if (!opened) { + qWarning() << "Stream did not open"; + m_state = QCamera::LoadedState; + emit stateChanged(QCamera::LoadedState); + } +} + +void DSCameraSession::pause() +{ + suspendStream(); +} + +void DSCameraSession::stop() +{ + if(!opened) { + return; + } + + stopStream(); + opened = false; + m_state = QCamera::LoadedState; + emit stateChanged(QCamera::LoadedState); +} + +void DSCameraSession::captureFrame() +{ + if(m_surface && frames.count() > 0) { + + QImage image; + + if(pixelF == QVideoFrame::Format_RGB24) { + + mutex.lock(); + + image = QImage(frames.at(0)->buffer,m_windowSize.width(),m_windowSize.height(), + QImage::Format_RGB888).rgbSwapped().mirrored(true); + + QVideoFrame frame(image); + frame.setStartTime(frames.at(0)->time); + + mutex.unlock(); + + m_surface->present(frame); + + } else if (pixelF == QVideoFrame::Format_RGB32) { + + mutex.lock(); + + image = QImage(frames.at(0)->buffer,m_windowSize.width(),m_windowSize.height(), + QImage::Format_RGB888).rgbSwapped().mirrored(true); + + QVideoFrame frame(image.convertToFormat(QImage::Format_RGB32)); + frame.setStartTime(frames.at(0)->time); + + mutex.unlock(); + + m_surface->present(frame); + + } else { + qWarning() << "TODO:captureFrame() format =" << pixelF; + } + + if (m_snapshot.length() > 0) { + emit imageCaptured(m_currentImageId, image); + image.save(m_snapshot,"JPG"); + emit imageSaved(m_currentImageId, m_snapshot); + m_snapshot.clear(); + emit readyForCaptureChanged(true); + } + + mutex.lock(); + if (frames.isEmpty()) { + qWarning() << "Frames over-run"; + } + + video_buffer* buf = frames.takeFirst(); + delete buf->buffer; + delete buf; + mutex.unlock(); + } +} + +HRESULT DSCameraSession::getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin) +{ + *ppPin = 0; + IEnumPins *pEnum = 0; + IPin *pPin = 0; + + HRESULT hr = pFilter->EnumPins(&pEnum); + if(FAILED(hr)) { + return hr; + } + + pEnum->Reset(); + while(pEnum->Next(1, &pPin, NULL) == S_OK) { + PIN_DIRECTION ThisPinDir; + pPin->QueryDirection(&ThisPinDir); + if(ThisPinDir == PinDir) { + pEnum->Release(); + *ppPin = pPin; + return S_OK; + } + pEnum->Release(); + } + pEnum->Release(); + return E_FAIL; +} + +bool DSCameraSession::createFilterGraph() +{ + HRESULT hr; + IMoniker* pMoniker = NULL; + ICreateDevEnum* pDevEnum = NULL; + IEnumMoniker* pEnum = NULL; + + CoInitialize(NULL); + + // Create the filter graph + hr = CoCreateInstance(CLSID_FilterGraph,NULL,CLSCTX_INPROC, + IID_IGraphBuilder, (void**)&pGraph); + if (FAILED(hr)) { + qWarning()<<"failed to create filter graph"; + return false; + } + + // Create the capture graph builder + hr = CoCreateInstance(CLSID_CaptureGraphBuilder2, NULL, CLSCTX_INPROC, + IID_ICaptureGraphBuilder2, (void**)&pBuild); + if (FAILED(hr)) { + qWarning()<<"failed to create graph builder"; + return false; + } + + // Attach the filter graph to the capture graph + hr = pBuild->SetFiltergraph(pGraph); + if (FAILED(hr)) { + qWarning()<<"failed to connect capture graph and filter graph"; + return false; + } + + // Find the Capture device + hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, + reinterpret_cast(&pDevEnum)); + if (SUCCEEDED(hr)) { + // Create an enumerator for the video capture category + hr = pDevEnum->CreateClassEnumerator(CLSID_VideoInputDeviceCategory, &pEnum, 0); + pDevEnum->Release(); + if (S_OK == hr) { + pEnum->Reset(); + //go through and find all video capture devices + while (pEnum->Next(1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, + IID_IPropertyBag, (void**)(&pPropBag)); + if(FAILED(hr)) { + pMoniker->Release(); + continue; // skip this one + } + // Find the description + WCHAR str[120]; + VARIANT varName; + varName.vt = VT_BSTR; + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if (SUCCEEDED(hr)) { + // check if it is the selected device + wcsncpy(str, varName.bstrVal, sizeof(str)/sizeof(str[0])); + QString output = QString::fromUtf16((unsigned short*)str); + if (m_device.contains(output.toLocal8Bit().constData())) { + hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap); + if (SUCCEEDED(hr)) { + pPropBag->Release(); + pMoniker->Release(); + break; + } + } + } + pPropBag->Release(); + pMoniker->Release(); + } + if (NULL == pCap) + { + if (m_device.contains("default")) + { + pEnum->Reset(); + // still have to loop to discard bind to storage failure case + while (pEnum->Next(1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag = 0; + + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, (void**)(&pPropBag)); + if (FAILED(hr)) { + pMoniker->Release(); + continue; // Don't panic yet + } + + // No need to get the description, just grab it + + hr = pMoniker->BindToObject(0, 0, IID_IBaseFilter, (void**)&pCap); + pPropBag->Release(); + pMoniker->Release(); + if (SUCCEEDED(hr)) { + break; // done, stop looping through + } + else + { + qWarning() << "Object bind failed"; + } + } + } + } + pEnum->Release(); + } + } + + // Sample grabber filter + hr = CoCreateInstance(CLSID_SampleGrabber, NULL,CLSCTX_INPROC, + IID_IBaseFilter, (void**)&pSG_Filter); + if (FAILED(hr)) { + qWarning() << "failed to create sample grabber"; + return false; + } + + pSG_Filter->QueryInterface(IID_ISampleGrabber, (void**)&pSG); + if (FAILED(hr)) { + qWarning() << "failed to get sample grabber"; + return false; + } + pSG->SetOneShot(FALSE); + pSG->SetBufferSamples(TRUE); + pSG->SetCallback(StillCapCB, 1); + + CoUninitialize(); + + return true; +} + +void DSCameraSession::updateProperties() +{ + HRESULT hr; + AM_MEDIA_TYPE *pmt = NULL; + VIDEOINFOHEADER *pvi = NULL; + VIDEO_STREAM_CONFIG_CAPS scc; + IAMStreamConfig* pConfig = 0; + + hr = pBuild->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video,pCap, + IID_IAMStreamConfig, (void**)&pConfig); + if (FAILED(hr)) { + qWarning()<<"failed to get config on capture device"; + return; + } + + int iCount; + int iSize; + hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize); + if (FAILED(hr)) { + qWarning()<<"failed to get capabilities"; + return; + } + + QList sizes; + QVideoFrame::PixelFormat f = QVideoFrame::Format_Invalid; + + types.clear(); + resolutions.clear(); + + for (int iIndex = 0; iIndex < iCount; iIndex++) { + hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast(&scc)); + if (hr == S_OK) { + pvi = (VIDEOINFOHEADER*)pmt->pbFormat; + if ((pmt->majortype == MEDIATYPE_Video) && + (pmt->formattype == FORMAT_VideoInfo)) { + // Add types + if (pmt->subtype == MEDIASUBTYPE_RGB24) { + if (!types.contains(QVideoFrame::Format_RGB24)) { + types.append(QVideoFrame::Format_RGB24); + f = QVideoFrame::Format_RGB24; + } + } else if (pmt->subtype == MEDIASUBTYPE_RGB32) { + if (!types.contains(QVideoFrame::Format_RGB32)) { + types.append(QVideoFrame::Format_RGB32); + f = QVideoFrame::Format_RGB32; + } + } else if (pmt->subtype == MEDIASUBTYPE_YUY2) { + if (!types.contains(QVideoFrame::Format_YUYV)) { + types.append(QVideoFrame::Format_YUYV); + f = QVideoFrame::Format_YUYV; + } + } else if (pmt->subtype == MEDIASUBTYPE_MJPG) { + } else if (pmt->subtype == MEDIASUBTYPE_I420) { + if (!types.contains(QVideoFrame::Format_YUV420P)) { + types.append(QVideoFrame::Format_YUV420P); + f = QVideoFrame::Format_YUV420P; + } + } else if (pmt->subtype == MEDIASUBTYPE_RGB555) { + if (!types.contains(QVideoFrame::Format_RGB555)) { + types.append(QVideoFrame::Format_RGB555); + f = QVideoFrame::Format_RGB555; + } + } else if (pmt->subtype == MEDIASUBTYPE_YVU9) { + } else if (pmt->subtype == MEDIASUBTYPE_UYVY) { + if (!types.contains(QVideoFrame::Format_UYVY)) { + types.append(QVideoFrame::Format_UYVY); + f = QVideoFrame::Format_UYVY; + } + } else { + qWarning() << "UNKNOWN FORMAT: " << pmt->subtype.Data1; + } + // Add resolutions + QSize res(pvi->bmiHeader.biWidth, pvi->bmiHeader.biHeight); + if (!resolutions.contains(f)) { + sizes.clear(); + resolutions.insert(f,sizes); + } + resolutions[f].append(res); + } + } + } + pConfig->Release(); +} + +bool DSCameraSession::setProperties() +{ + CoInitialize(NULL); + + HRESULT hr; + AM_MEDIA_TYPE am_media_type; + AM_MEDIA_TYPE *pmt = NULL; + VIDEOINFOHEADER *pvi = NULL; + VIDEO_STREAM_CONFIG_CAPS scc; + + IAMStreamConfig* pConfig = 0; + hr = pBuild->FindInterface(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, pCap, + IID_IAMStreamConfig, (void**)&pConfig); + if(FAILED(hr)) { + qWarning()<<"failed to get config on capture device"; + return false; + } + + int iCount; + int iSize; + hr = pConfig->GetNumberOfCapabilities(&iCount, &iSize); + if(FAILED(hr)) { + qWarning()<<"failed to get capabilities"; + return false; + } + + bool setFormatOK = false; + for (int iIndex = 0; iIndex < iCount; iIndex++) { + hr = pConfig->GetStreamCaps(iIndex, &pmt, reinterpret_cast(&scc)); + if (hr == S_OK) { + pvi = (VIDEOINFOHEADER*)pmt->pbFormat; + + if ((pmt->majortype == MEDIATYPE_Video) && + (pmt->formattype == FORMAT_VideoInfo)) { + if ((actualFormat.frameWidth() == pvi->bmiHeader.biWidth) && + (actualFormat.frameHeight() == pvi->bmiHeader.biHeight)) { + hr = pConfig->SetFormat(pmt); + _FreeMediaType(*pmt); + if(FAILED(hr)) { + qWarning()<<"failed to set format:" << hr; + qWarning()<<"but going to continue"; + continue; // We going to continue + } else { + setFormatOK = true; + break; + } + } + } + } + } + pConfig->Release(); + + if (!setFormatOK) { + qWarning() << "unable to set any format for camera"; + return false; + } + + // Set Sample Grabber config to match capture + ZeroMemory(&am_media_type, sizeof(am_media_type)); + am_media_type.majortype = MEDIATYPE_Video; + + if (actualFormat.pixelFormat() == QVideoFrame::Format_RGB32) + am_media_type.subtype = MEDIASUBTYPE_RGB24; + else if (actualFormat.pixelFormat() == QVideoFrame::Format_RGB24) + am_media_type.subtype = MEDIASUBTYPE_RGB24; + else if (actualFormat.pixelFormat() == QVideoFrame::Format_YUYV) + am_media_type.subtype = MEDIASUBTYPE_YUY2; + else if (actualFormat.pixelFormat() == QVideoFrame::Format_YUV420P) + am_media_type.subtype = MEDIASUBTYPE_I420; + else if (actualFormat.pixelFormat() == QVideoFrame::Format_RGB555) + am_media_type.subtype = MEDIASUBTYPE_RGB555; + else if (actualFormat.pixelFormat() == QVideoFrame::Format_UYVY) + am_media_type.subtype = MEDIASUBTYPE_UYVY; + else { + qWarning()<<"unknown format? for SG"; + return false; + } + + am_media_type.formattype = FORMAT_VideoInfo; + hr = pSG->SetMediaType(&am_media_type); + if (FAILED(hr)) { + qWarning()<<"failed to set video format on grabber"; + return false; + } + + pSG->GetConnectedMediaType(&StillMediaType); + + CoUninitialize(); + + return true; +} + +bool DSCameraSession::openStream() +{ + //Opens the stream for reading and allocates any necessary resources needed + //Return true if success, false otherwise + + if (opened) { + return true; + } + + if (!graph) { + graph = createFilterGraph(); + if(!graph) { + qWarning()<<"failed to create filter graph in openStream"; + return false; + } + } + + CoInitialize(NULL); + + HRESULT hr; + + hr = pGraph->AddFilter(pCap, L"Capture Filter"); + if (FAILED(hr)) { + qWarning()<<"failed to create capture filter"; + return false; + } + + hr = pGraph->AddFilter(pSG_Filter, L"Sample Grabber"); + if (FAILED(hr)) { + qWarning()<<"failed to add sample grabber"; + return false; + } + + hr = pBuild->RenderStream(&PIN_CATEGORY_CAPTURE, &MEDIATYPE_Video, + pCap, NULL, pSG_Filter); + if (FAILED(hr)) { + qWarning() << "failed to renderstream" << hr; + return false; + } + pSG->GetConnectedMediaType(&StillMediaType); + pSG_Filter->Release(); + + CoUninitialize(); + + return true; +} + +void DSCameraSession::closeStream() +{ + // Closes the stream and internally frees any resources used + HRESULT hr; + IMediaControl* pControl = 0; + + hr = pGraph->QueryInterface(IID_IMediaControl,(void**)&pControl); + if (FAILED(hr)) { + qWarning()<<"failed to get stream control"; + return; + } + + hr = pControl->StopWhenReady(); + if (FAILED(hr)) { + qWarning()<<"failed to stop"; + pControl->Release(); + return; + } + + pControl->Release(); + + opened = false; + IPin *pPin = 0; + + if (pCap) + { + hr = getPin(pCap, PINDIR_OUTPUT, &pPin); + if(FAILED(hr)) { + qWarning()<<"failed to disconnect capture filter"; + return; + } + } + + pGraph->Disconnect(pPin); + if (FAILED(hr)) { + qWarning()<<"failed to disconnect grabber filter"; + return; + } + + hr = getPin(pSG_Filter,PINDIR_INPUT,&pPin); + pGraph->Disconnect(pPin); + pGraph->RemoveFilter(pSG_Filter); + pGraph->RemoveFilter(pCap); + + SAFE_RELEASE(pCap); + SAFE_RELEASE(pSG_Filter); + SAFE_RELEASE(pGraph); + SAFE_RELEASE(pBuild); + + graph = false; +} + +bool DSCameraSession::startStream() +{ + // Starts the stream, by emitting either QVideoPackets + // or QvideoFrames, depending on Format chosen + if (!graph) + graph = createFilterGraph(); + + if (!setProperties()) { + qWarning() << "Couldn't set properties (retrying)"; + closeStream(); + if (!openStream()) { + qWarning() << "Retry to open strean failed"; + return false; + } + } + + if (!opened) { + opened = openStream(); + if (!opened) { + qWarning() << "failed to openStream()"; + return false; + } + } + + HRESULT hr; + IMediaControl* pControl = 0; + + hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); + if (FAILED(hr)) { + qWarning() << "failed to get stream control"; + return false; + } + + hr = pControl->Run(); + pControl->Release(); + + if (FAILED(hr)) { + qWarning() << "failed to start"; + return false; + } + active = true; + return true; +} + +void DSCameraSession::stopStream() +{ + // Stops the stream from emitting packets + HRESULT hr; + + IMediaControl* pControl = 0; + hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); + if (FAILED(hr)) { + qWarning() << "failed to get stream control"; + return; + } + + hr = pControl->Stop(); + pControl->Release(); + if (FAILED(hr)) { + qWarning() << "failed to stop"; + return; + } + active = false; + + if (opened) { + closeStream(); + } +} + +void DSCameraSession::suspendStream() +{ + // Pauses the stream + HRESULT hr; + + IMediaControl* pControl = 0; + hr = pGraph->QueryInterface(IID_IMediaControl, (void**)&pControl); + if (FAILED(hr)) { + qWarning() << "failed to get stream control"; + return; + } + + hr = pControl->Pause(); + pControl->Release(); + if (FAILED(hr)) { + qWarning() << "failed to pause"; + return; + } + + active = false; +} + +void DSCameraSession::resumeStream() +{ + // Resumes a paused stream + startStream(); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/directshow/camera/dscamerasession.h b/src/plugins/directshow/camera/dscamerasession.h new file mode 100644 index 000000000..72a0e5077 --- /dev/null +++ b/src/plugins/directshow/camera/dscamerasession.h @@ -0,0 +1,208 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSCAMERASESSION_H +#define DSCAMERASESSION_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include +#include +#include +#include +#pragma comment(lib, "strmiids.lib") +#pragma comment(lib, "ole32.lib") +#include + +#pragma include_alias("dxtrans.h","qedit.h") +#define __IDxtCompositor_INTERFACE_DEFINED__ +#define __IDxtAlphaSetter_INTERFACE_DEFINED__ +#define __IDxtJpeg_INTERFACE_DEFINED__ +#define __IDxtKey_INTERFACE_DEFINED__ +#include + +struct ICaptureGraphBuilder2; +struct ISampleGrabber; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DSVideoRenderer; +class SampleGrabberCallbackPrivate; + + +struct video_buffer { + unsigned char* buffer; + int length; + qint64 time; +}; + +typedef QMap > FormatResolutionMap; + +class DSCameraSession : public QObject +{ + Q_OBJECT +public: + DSCameraSession(QObject *parent = 0); + ~DSCameraSession(); + + bool deviceReady(); + bool pictureInProgress(); + + // camera controls + + int framerate() const; + void setFrameRate(int rate); + int brightness() const; + void setBrightness(int b); + int contrast() const; + void setContrast(int c); + int saturation() const; + void setSaturation(int s); + int hue() const; + void setHue(int h); + int sharpness() const; + void setSharpness(int s); + int zoom() const; + void setZoom(int z); + bool backlightCompensation() const; + void setBacklightCompensation(bool); + int whitelevel() const; + void setWhitelevel(int w); + int rotation() const; + void setRotation(int r); + bool flash() const; + void setFlash(bool f); + bool autofocus() const; + void setAutofocus(bool f); + + QSize frameSize() const; + void setFrameSize(const QSize& s); + void setDevice(const QString &device); + QList supportedPixelFormats(); + QVideoFrame::PixelFormat pixelFormat() const; + void setPixelFormat(QVideoFrame::PixelFormat fmt); + QList supportedResolutions(QVideoFrame::PixelFormat format); + + // media control + + bool setOutputLocation(const QUrl &sink); + QUrl outputLocation() const; + qint64 position() const; + int state() const; + void record(); + void pause(); + void stop(); + + void setSurface(QAbstractVideoSurface* surface); + + int captureImage(const QString &fileName); + + AM_MEDIA_TYPE StillMediaType; + QList frames; + SampleGrabberCallbackPrivate* StillCapCB; + + QMutex mutex; + +Q_SIGNALS: + void stateChanged(QCamera::State); + void imageCaptured(int id, const QImage &preview); + void imageSaved(int id, const QString &fileName); + void readyForCaptureChanged(bool); + +private Q_SLOTS: + void captureFrame(); + +private: + QVideoSurfaceFormat actualFormat; + QList types; + + QTime timeStamp; + bool graph; + bool active; + bool opened; + bool available; + QCamera::State m_state; + QByteArray m_device; + QUrl m_sink; + DSVideoRenderer* m_output; + QAbstractVideoSurface* m_surface; + QVideoFrame::PixelFormat pixelF; + QSize m_windowSize; + FormatResolutionMap resolutions; + + ICaptureGraphBuilder2* pBuild; + IGraphBuilder* pGraph; + IBaseFilter* pCap; + IBaseFilter* pSG_Filter; + ISampleGrabber *pSG; + + + QString m_snapshot; + int m_currentImageId; +protected: + HRESULT getPin(IBaseFilter *pFilter, PIN_DIRECTION PinDir, IPin **ppPin); + bool createFilterGraph(); + void updateProperties(); + bool setProperties(); + bool openStream(); + void closeStream(); + bool startStream(); + void stopStream(); + void suspendStream(); + void resumeStream(); +}; + +QT_END_NAMESPACE + +QT_END_HEADER + + +#endif diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.cpp b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp new file mode 100644 index 000000000..17654c4a5 --- /dev/null +++ b/src/plugins/directshow/camera/dsimagecapturecontrol.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "dsimagecapturecontrol.h" + +QT_BEGIN_NAMESPACE + +DSImageCaptureControl::DSImageCaptureControl(DSCameraSession *session) + :QCameraImageCaptureControl(session), m_session(session), m_ready(false) +{ + connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState())); + connect(m_session, SIGNAL(imageCaptured(const int, QImage)), + this, SIGNAL(imageCaptured(const int, QImage))); + connect(m_session, SIGNAL(imageSaved(const int, const QString &)), + this, SIGNAL(imageSaved(const int, const QString &))); + connect(m_session, SIGNAL(readyForCaptureChanged(bool)), + this, SIGNAL(readyForCaptureChanged(bool))); +} + +DSImageCaptureControl::~DSImageCaptureControl() +{ +} + +bool DSImageCaptureControl::isReadyForCapture() const +{ + return m_ready; +} + +int DSImageCaptureControl::capture(const QString &fileName) +{ + return m_session->captureImage(fileName); +} + +void DSImageCaptureControl::updateState() +{ + bool ready = (m_session->state() == QCamera::ActiveState) && + !m_session->pictureInProgress(); + if(m_ready != ready) + emit readyForCaptureChanged(m_ready = ready); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/directshow/camera/dsimagecapturecontrol.h b/src/plugins/directshow/camera/dsimagecapturecontrol.h new file mode 100644 index 000000000..8eca7b4e7 --- /dev/null +++ b/src/plugins/directshow/camera/dsimagecapturecontrol.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSIMAGECAPTURECONTROL_H +#define DSIMAGECAPTURECONTROL_H + +#include +#include "dscamerasession.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DSImageCaptureControl : public QCameraImageCaptureControl +{ + Q_OBJECT +public: + DSImageCaptureControl(DSCameraSession *session); + virtual ~DSImageCaptureControl(); + + bool isReadyForCapture() const; + int capture(const QString &fileName); + + virtual QCameraImageCapture::DriveMode driveMode() const { return QCameraImageCapture::SingleImageCapture; } + virtual void setDriveMode(QCameraImageCapture::DriveMode mode) { } + + virtual void cancelCapture() {} + +private slots: + void updateState(); + + +private: + DSCameraSession *m_session; + bool m_ready; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DSCAPTURECONTROL_H diff --git a/src/plugins/directshow/camera/dsvideodevicecontrol.cpp b/src/plugins/directshow/camera/dsvideodevicecontrol.cpp new file mode 100644 index 000000000..8c9b03000 --- /dev/null +++ b/src/plugins/directshow/camera/dsvideodevicecontrol.cpp @@ -0,0 +1,168 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "dsvideodevicecontrol.h" +#include "dscamerasession.h" + +#include +#include +#include +#include +#include +#include + +extern const CLSID CLSID_VideoInputDeviceCategory; + +QT_BEGIN_NAMESPACE + +DSVideoDeviceControl::DSVideoDeviceControl(QObject *parent) + : QVideoDeviceControl(parent) +{ + m_session = qobject_cast(parent); + + devices.clear(); + descriptions.clear(); + + CoInitialize(NULL); + ICreateDevEnum* pDevEnum = NULL; + IEnumMoniker* pEnum = NULL; + // Create the System device enumerator + HRESULT hr = CoCreateInstance(CLSID_SystemDeviceEnum, NULL, + CLSCTX_INPROC_SERVER, IID_ICreateDevEnum, + reinterpret_cast(&pDevEnum)); + if(SUCCEEDED(hr)) { + // Create the enumerator for the video capture category + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, &pEnum, 0); + if (S_OK == hr) { + pEnum->Reset(); + // go through and find all video capture devices + IMoniker* pMoniker = NULL; + while(pEnum->Next(1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0, 0, IID_IPropertyBag, + (void**)(&pPropBag)); + if(FAILED(hr)) { + pMoniker->Release(); + continue; // skip this one + } + // Find the description + WCHAR str[120]; + VARIANT varName; + varName.vt = VT_BSTR; + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if(SUCCEEDED(hr)) { + wcsncpy(str, varName.bstrVal, sizeof(str)/sizeof(str[0])); + QString temp(QString::fromUtf16((unsigned short*)str)); + devices.append(QString("ds:%1").arg(temp).toLocal8Bit().constData()); + hr = pPropBag->Read(L"Description", &varName, 0); + wcsncpy(str, varName.bstrVal, sizeof(str)/sizeof(str[0])); + QString temp2(QString::fromUtf16((unsigned short*)str)); + descriptions.append(temp2.toLocal8Bit().constData()); + } + pPropBag->Release(); + pMoniker->Release(); + } + pEnum->Release(); + } + pDevEnum->Release(); + } + CoUninitialize(); + + selected = 0; +} + +int DSVideoDeviceControl::deviceCount() const +{ + return devices.count(); +} + +QString DSVideoDeviceControl::deviceName(int index) const +{ + if(index >= 0 && index <= devices.count()) + return devices.at(index); + + return QString(); +} + +QString DSVideoDeviceControl::deviceDescription(int index) const +{ + if(index >= 0 && index <= descriptions.count()) + return descriptions.at(index); + + return QString(); +} + +QIcon DSVideoDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index) + + return QIcon(); +} + +int DSVideoDeviceControl::defaultDevice() const +{ + return 0; +} + +int DSVideoDeviceControl::selectedDevice() const +{ + return selected; +} + +void DSVideoDeviceControl::setSelectedDevice(int index) +{ + if(index >= 0 && index <= devices.count()) { + if (m_session) { + QString device = devices.at(index); + if (device.startsWith("ds:")) + device.remove(0,3); + m_session->setDevice(device); + } + selected = index; + } +} + +QT_END_NAMESPACE diff --git a/src/plugins/directshow/camera/dsvideodevicecontrol.h b/src/plugins/directshow/camera/dsvideodevicecontrol.h new file mode 100644 index 000000000..8391c4eda --- /dev/null +++ b/src/plugins/directshow/camera/dsvideodevicecontrol.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSVIDEODEVICECONTROL_H +#define DSVIDEODEVICECONTROL_H + +#include + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE +class DSCameraSession; + +//QTM_USE_NAMESPACE + +class DSVideoDeviceControl : public QVideoDeviceControl +{ + Q_OBJECT +public: + DSVideoDeviceControl(QObject *parent = 0); + + int deviceCount() const; + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + int defaultDevice() const; + int selectedDevice() const; + +public Q_SLOTS: + void setSelectedDevice(int index); + +private: + DSCameraSession* m_session; + + QList devices; + QList descriptions; + + int selected; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif diff --git a/src/plugins/directshow/camera/dsvideorenderer.cpp b/src/plugins/directshow/camera/dsvideorenderer.cpp new file mode 100644 index 000000000..0fbdb15b1 --- /dev/null +++ b/src/plugins/directshow/camera/dsvideorenderer.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "dsvideorenderer.h" + +QT_BEGIN_NAMESPACE + +DSVideoRendererControl::DSVideoRendererControl(DSCameraSession* session, QObject *parent) + :QVideoRendererControl(parent), + m_surface(0), + m_session(session) +{ +} + +DSVideoRendererControl::~DSVideoRendererControl() +{ +} + +QAbstractVideoSurface* DSVideoRendererControl::surface() const +{ + return m_surface; +} + +void DSVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + m_surface = surface; + if(m_session) + m_session->setSurface(m_surface); +} + +QT_END_NAMESPACE + diff --git a/src/plugins/directshow/camera/dsvideorenderer.h b/src/plugins/directshow/camera/dsvideorenderer.h new file mode 100644 index 000000000..b941504ac --- /dev/null +++ b/src/plugins/directshow/camera/dsvideorenderer.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSVIDEORENDERER_H +#define DSVIDEORENDERER_H + +#include +#include "dscamerasession.h" + +class CameraFormatConverter; + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + + +class DSVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + DSVideoRendererControl(DSCameraSession* session, QObject *parent = 0); + ~DSVideoRendererControl(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + void setSession(DSCameraSession* session); + +private: + QAbstractVideoSurface* m_surface; + DSCameraSession* m_session; + CameraFormatConverter* converter; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DSVIDEORENDERER_H diff --git a/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp b/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp new file mode 100644 index 000000000..8298c0275 --- /dev/null +++ b/src/plugins/directshow/camera/dsvideowidgetcontrol.cpp @@ -0,0 +1,250 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "DSVideoWidgetControl.h" +#include "dscamerasession.h" + +QT_BEGIN_NAMESPACE + +DSVideoWidgetSurface::DSVideoWidgetSurface(QLabel *pWidget, QObject *parent) +{ + widget = pWidget; + myPixmap = 0; +} + +QList DSVideoWidgetSurface::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + if (handleType == QAbstractVideoBuffer::NoHandle) { + return QList() + << QVideoFrame::Format_RGB32 + << QVideoFrame::Format_RGB24; + } else { + return QList(); + } +} + + +bool DSVideoWidgetSurface::present(const QVideoFrame &frame) +{ + QVideoFrame myFrame = frame; + myFrame.map(QAbstractVideoBuffer::ReadOnly); + QImage image( + frame.bits(), + frame.width(), + frame.height(), + frame.bytesPerLine(), + imageFormat); + if (image.isNull()) + { + // Try to adapt + QImage image2( + frame.bits(), + frame.width(), + frame.height(), + frame.bytesPerLine(), + QImage::Format_RGB888); + image = image2; + } + myFrame.unmap(); + delete myPixmap; + myPixmap = new QPixmap(QPixmap::fromImage(image).scaled(widget->size())); + widget->setPixmap(*myPixmap); + widget->repaint(); + return true; +} + +void DSVideoWidgetSurface::setImageFormat(QImage::Format fmt) +{ + imageFormat = fmt; +} + +void DSVideoWidgetSurface::updateVideoRect() +{ +} + +void DSVideoWidgetSurface::paint(QPainter *painter) +{ +} + + +DSVideoWidgetControl::DSVideoWidgetControl(DSCameraSession* session, QObject *parent) : + m_session(session), QVideoWidgetControl(parent), + m_widget(new QLabel()), + m_fullScreen(false) +{ + m_widget->setSizePolicy(QSizePolicy::MinimumExpanding, QSizePolicy::MinimumExpanding); + m_widget->setAlignment(Qt::AlignCenter); + m_widget->setAttribute(Qt::WA_NoSystemBackground, true); + + surface = new DSVideoWidgetSurface(m_widget); + + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + m_widget->setPalette(palette); + m_widget->setAutoFillBackground( true ); + + // Request QEvents + m_widget->installEventFilter(this); + m_windowId = m_widget->effectiveWinId(); + + surface->setImageFormat(QImage::Format_RGB888); + session->setSurface(surface); +} + +DSVideoWidgetControl::~DSVideoWidgetControl() +{ + delete m_widget; +} + +bool DSVideoWidgetControl::eventFilter(QObject *object, QEvent *e) +{ + if (object == m_widget) { + switch (e->type()) { + case QEvent::ParentChange: + case QEvent::WinIdChange: + case QEvent::Show: + m_windowId = m_widget->effectiveWinId(); + emit widgetUpdated(); + break; + case QEvent::Resize: + emit widgetResized(m_widget->size()); + break; + case QEvent::PolishRequest: + m_widget->ensurePolished(); + break; + + default: + // Do nothing + break; + } + } + return false; +} + +QWidget *DSVideoWidgetControl::videoWidget() +{ + return m_widget; +} + +Qt::AspectRatioMode DSVideoWidgetControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void DSVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode ratio) +{ + if (m_aspectRatioMode==ratio) { + return; + } + m_aspectRatioMode = ratio; + + if (m_aspectRatioMode == Qt::KeepAspectRatio) + m_widget->setScaledContents(false); + else { + m_widget->setScaledContents(true); + } +} + +bool DSVideoWidgetControl::isFullScreen() const +{ + return m_fullScreen; +} + +void DSVideoWidgetControl::setFullScreen(bool fullScreen) +{ + if (m_widget && !fullScreen && m_fullScreen) { + m_widget->showNormal(); + m_fullScreen = false; + } else if (m_widget && fullScreen) { + m_widget->showFullScreen(); + m_fullScreen = true; + } + + emit fullScreenChanged(fullScreen); +} + +int DSVideoWidgetControl::brightness() const +{ + return 0; +} + +void DSVideoWidgetControl::setBrightness(int brightness) +{ + Q_UNUSED(brightness); +} + +int DSVideoWidgetControl::contrast() const +{ + return 0; +} + +void DSVideoWidgetControl::setContrast(int contrast) +{ + Q_UNUSED(contrast); +} + +int DSVideoWidgetControl::hue() const +{ + return 0; +} + +void DSVideoWidgetControl::setHue(int hue) +{ + Q_UNUSED(hue); +} + +int DSVideoWidgetControl::saturation() const +{ + return 0; +} + +void DSVideoWidgetControl::setSaturation(int saturation) +{ + Q_UNUSED(saturation); +} + +QT_END_NAMESPACE + +// End of file diff --git a/src/plugins/directshow/camera/dsvideowidgetcontrol.h b/src/plugins/directshow/camera/dsvideowidgetcontrol.h new file mode 100644 index 000000000..e17827453 --- /dev/null +++ b/src/plugins/directshow/camera/dsvideowidgetcontrol.h @@ -0,0 +1,154 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSVIDEOWIDGETCONTROL_H +#define DSVIDEOWIDGETCONTROL_H + +#include +#include +#include +#include +#include + +#include +#include "DsCameraControl.h" + +QT_BEGIN_HEADER + +QT_BEGIN_NAMESPACE + +class DSVideoWidgetSurface : public QAbstractVideoSurface +{ + Q_OBJECT + public: + DSVideoWidgetSurface(QLabel *pWidget, QObject *parent = 0); + + QList supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + bool present(const QVideoFrame &frame); + + QRect videoRect() const { return targetRect; } + void updateVideoRect(); + + void paint(QPainter *painter); + void setImageFormat(QImage::Format fmt); + + private: + QLabel *widget; + QImage::Format imageFormat; + QRect targetRect; + QSize imageSize; + QRect sourceRect; + QPixmap* myPixmap; + }; + +class DSVideoWidgetControl : public QVideoWidgetControl +{ + Q_OBJECT + + DSVideoWidgetSurface* surface; +public: // Constructor & Destructor + + DSVideoWidgetControl(DSCameraSession* session, QObject *parent = 0); + virtual ~DSVideoWidgetControl(); + +public: // QVideoWidgetControl + + QWidget *videoWidget(); + + // Aspect Ratio + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode ratio); + + // Full Screen + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + // Brightness + int brightness() const; + void setBrightness(int brightness); + + // Contrast + int contrast() const; + void setContrast(int contrast); + + // Hue + int hue() const; + void setHue(int hue); + + // Saturation + int saturation() const; + void setSaturation(int saturation); + +public: // Internal + + bool eventFilter(QObject *object, QEvent *event); + +/* +Q_SIGNALS: // QVideoWidgetControl + + void fullScreenChanged(bool fullScreen); + void brightnessChanged(int brightness); + void contrastChanged(int contrast); + void hueChanged(int hue); + void saturationChanged(int saturation); +*/ + +Q_SIGNALS: // Internal Signals + + void widgetResized(QSize size); + void widgetUpdated(); + +private: // Data + + DSCameraSession* m_session; + QLabel *m_widget; + WId m_windowId; + Qt::AspectRatioMode m_aspectRatioMode; + bool m_fullScreen; +}; + +QT_END_NAMESPACE + +QT_END_HEADER + +#endif // DSVideoWidgetControl_H diff --git a/src/plugins/directshow/directshow.pro b/src/plugins/directshow/directshow.pro new file mode 100644 index 000000000..58e98c385 --- /dev/null +++ b/src/plugins/directshow/directshow.pro @@ -0,0 +1,23 @@ +TEMPLATE = lib + +CONFIG += plugin +TARGET = $$qtLibraryTarget(dsengine) + +PLUGIN_TYPE=mediaservice + +include (../../../common.pri) +INCLUDEPATH+=../../multimediakit \ + ../../multimediakit/audio \ + ../../multimediakit/video + +qtAddLibrary(QtMultimediaKit) + +DEPENDPATH += . + +HEADERS += dsserviceplugin.h +SOURCES += dsserviceplugin.cpp + +!contains(wmsdk_enabled, yes): DEFINES += QT_NO_WMSDK + +include (player/player.pri) +include (camera/camera.pri) diff --git a/src/plugins/directshow/dsserviceplugin.cpp b/src/plugins/directshow/dsserviceplugin.cpp new file mode 100644 index 000000000..9c0a21449 --- /dev/null +++ b/src/plugins/directshow/dsserviceplugin.cpp @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "dsserviceplugin.h" + + +#ifdef QMEDIA_DIRECTSHOW_CAMERA +#include "dscameraservice.h" +#endif + +#ifdef QMEDIA_DIRECTSHOW_PLAYER +#include "directshowplayerservice.h" +#endif + +#include + + +#ifdef QMEDIA_DIRECTSHOW_CAMERA + +extern const CLSID CLSID_VideoInputDeviceCategory; + + +#ifndef _STRSAFE_H_INCLUDED_ +#include +#endif +#include +#include +#include +#pragma comment(lib, "strmiids.lib") +#pragma comment(lib, "ole32.lib") +#include +#include +#endif + +QT_USE_NAMESPACE + +QStringList DSServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_DIRECTSHOW_CAMERA + << QLatin1String(Q_MEDIASERVICE_CAMERA) +#endif +#ifdef QMEDIA_DIRECTSHOW_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) +#endif + ; +} + +QMediaService* DSServicePlugin::create(QString const& key) +{ +#ifdef QMEDIA_DIRECTSHOW_CAMERA + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new DSCameraService; +#endif +#ifdef QMEDIA_DIRECTSHOW_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new DirectShowPlayerService; +#endif + + qDebug() << "unsupported key:" << key; + return 0; +} + +void DSServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QMediaServiceProviderHint::Features DSServicePlugin::supportedFeatures( + const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_MEDIAPLAYER) + return QMediaServiceProviderHint::StreamPlayback | QMediaServiceProviderHint::VideoSurface; + else + return QMediaServiceProviderHint::Features(); +} + +QList DSServicePlugin::devices(const QByteArray &service) const +{ +#ifdef QMEDIA_DIRECTSHOW_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } +#endif + + return QList(); +} + +QString DSServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ +#ifdef QMEDIA_DIRECTSHOW_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i(&pDevEnum)); + if(SUCCEEDED(hr)) { + // Create the enumerator for the video capture category + hr = pDevEnum->CreateClassEnumerator( + CLSID_VideoInputDeviceCategory, &pEnum, 0); + if (S_OK == hr) { + pEnum->Reset(); + // go through and find all video capture devices + IMoniker* pMoniker = NULL; + while(pEnum->Next(1, &pMoniker, NULL) == S_OK) { + IPropertyBag *pPropBag; + hr = pMoniker->BindToStorage(0,0,IID_IPropertyBag, + (void**)(&pPropBag)); + if(FAILED(hr)) { + pMoniker->Release(); + continue; // skip this one + } + bFound = TRUE; + // Find the description + WCHAR str[120]; + VARIANT varName; + varName.vt = VT_BSTR; + hr = pPropBag->Read(L"FriendlyName", &varName, 0); + if(SUCCEEDED(hr)) { + wcsncpy(str, varName.bstrVal, sizeof(str)/sizeof(str[0])); + QString temp(QString::fromUtf16((unsigned short*)str)); + m_cameraDevices.append(QString("ds:%1").arg(temp).toLocal8Bit().constData()); + hr = pPropBag->Read(L"Description", &varName, 0); + wcsncpy(str, varName.bstrVal, sizeof(str)/sizeof(str[0])); + QString temp2(QString::fromUtf16((unsigned short*)str)); + m_cameraDescriptions.append(temp2); + } else { + qWarning() << "No friendly name"; + } + pPropBag->Release(); + pMoniker->Release(); + } + pEnum->Release(); + } + pDevEnum->Release(); + } + CoUninitialize(); + if (!bFound) { + qWarning() << "No camera devices found"; + } +} +#endif + +Q_EXPORT_PLUGIN2(qtmedia_dsengine, DSServicePlugin); + diff --git a/src/plugins/directshow/dsserviceplugin.h b/src/plugins/directshow/dsserviceplugin.h new file mode 100644 index 000000000..336da0a3f --- /dev/null +++ b/src/plugins/directshow/dsserviceplugin.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DSSERVICEPLUGIN_H +#define DSSERVICEPLUGIN_H + +#include "qmediaserviceproviderplugin.h" + +QT_USE_NAMESPACE + +class DSServicePlugin + : public QMediaServiceProviderPlugin + , public QMediaServiceSupportedDevicesInterface + , public QMediaServiceFeaturesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) + Q_INTERFACES(QMediaServiceFeaturesInterface) +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; + + QList devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: +#ifdef QMEDIA_DIRECTSHOW_CAMERA + void updateDevices() const; + + mutable QList m_cameraDevices; + mutable QStringList m_cameraDescriptions; +#endif +}; + +#endif // DSSERVICEPLUGIN_H diff --git a/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp b/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp new file mode 100644 index 000000000..01c879732 --- /dev/null +++ b/src/plugins/directshow/player/directshowaudioendpointcontrol.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowaudioendpointcontrol.h" + +#include "directshowglobal.h" +#include "directshowplayerservice.h" + +DirectShowAudioEndpointControl::DirectShowAudioEndpointControl( + DirectShowPlayerService *service, QObject *parent) + : QAudioEndpointSelector(parent) + , m_service(service) + , m_bindContext(0) + , m_deviceEnumerator(0) +{ + if (CreateBindCtx(0, &m_bindContext) == S_OK) { + m_deviceEnumerator = com_new(CLSID_SystemDeviceEnum, IID_ICreateDevEnum); + + updateEndpoints(); + + setActiveEndpoint(m_defaultEndpoint); + } +} + +DirectShowAudioEndpointControl::~DirectShowAudioEndpointControl() +{ + foreach (IMoniker *moniker, m_devices) + moniker->Release(); + + if (m_bindContext) + m_bindContext->Release(); + + if (m_deviceEnumerator) + m_deviceEnumerator->Release(); +} + +QList DirectShowAudioEndpointControl::availableEndpoints() const +{ + return m_devices.keys(); +} + +QString DirectShowAudioEndpointControl::endpointDescription(const QString &name) const +{ +#ifdef __IPropertyBag_INTERFACE_DEFINED__ + QString description; + + if (IMoniker *moniker = m_devices.value(name, 0)) { + IPropertyBag *propertyBag = 0; + if (SUCCEEDED(moniker->BindToStorage( + 0, 0, IID_IPropertyBag, reinterpret_cast(&propertyBag)))) { + VARIANT name; + VariantInit(&name); + if (SUCCEEDED(propertyBag->Read(L"FriendlyName", &name, 0))) + description = QString::fromWCharArray(name.bstrVal); + VariantClear(&name); + propertyBag->Release(); + } + } + + return description; +#else + return name.section(QLatin1Char('\\'), -1); +#endif +} + +QString DirectShowAudioEndpointControl::defaultEndpoint() const +{ + return m_defaultEndpoint; +} + +QString DirectShowAudioEndpointControl::activeEndpoint() const +{ + return m_activeEndpoint; +} + +void DirectShowAudioEndpointControl::setActiveEndpoint(const QString &name) +{ + if (m_activeEndpoint == name) + return; + + if (IMoniker *moniker = m_devices.value(name, 0)) { + IBaseFilter *filter = 0; + + if (moniker->BindToObject( + m_bindContext, + 0, + IID_IBaseFilter, + reinterpret_cast(&filter)) == S_OK) { + m_service->setAudioOutput(filter); + + filter->Release(); + } + } +} + +void DirectShowAudioEndpointControl::updateEndpoints() +{ + IMalloc *oleMalloc = 0; + if (m_deviceEnumerator && CoGetMalloc(1, &oleMalloc) == S_OK) { + IEnumMoniker *monikers = 0; + + if (m_deviceEnumerator->CreateClassEnumerator( + CLSID_AudioRendererCategory, &monikers, 0) == S_OK) { + for (IMoniker *moniker = 0; monikers->Next(1, &moniker, 0) == S_OK; moniker->Release()) { + OLECHAR *string = 0; + if (moniker->GetDisplayName(m_bindContext, 0, &string) == S_OK) { + QString deviceId = QString::fromWCharArray(string); + oleMalloc->Free(string); + + moniker->AddRef(); + m_devices.insert(deviceId, moniker); + + if (m_defaultEndpoint.isEmpty() + || deviceId.endsWith(QLatin1String("Default DirectSound Device"))) { + m_defaultEndpoint = deviceId; + } + } + } + monikers->Release(); + } + oleMalloc->Release(); + } +} diff --git a/src/plugins/directshow/player/directshowaudioendpointcontrol.h b/src/plugins/directshow/player/directshowaudioendpointcontrol.h new file mode 100644 index 000000000..62a0bd47f --- /dev/null +++ b/src/plugins/directshow/player/directshowaudioendpointcontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWAUDIOENDPOINTCONTROL_H +#define DIRECTSHOWAUDIOENDPOINTCONTROL_H + +#include "qaudioendpointselector.h" + +#include + +class DirectShowPlayerService; + +QT_USE_NAMESPACE + +class DirectShowAudioEndpointControl : public QAudioEndpointSelector +{ + Q_OBJECT +public: + DirectShowAudioEndpointControl(DirectShowPlayerService *service, QObject *parent = 0); + ~DirectShowAudioEndpointControl(); + + QList availableEndpoints() const; + + QString endpointDescription(const QString &name) const; + + QString defaultEndpoint() const; + QString activeEndpoint() const; + + void setActiveEndpoint(const QString& name); + +private: + void updateEndpoints(); + + DirectShowPlayerService *m_service; + IBindCtx *m_bindContext; + ICreateDevEnum *m_deviceEnumerator; + + QMap m_devices; + QString m_defaultEndpoint; + QString m_activeEndpoint; +}; + +#endif + diff --git a/src/plugins/directshow/player/directshoweventloop.cpp b/src/plugins/directshow/player/directshoweventloop.cpp new file mode 100644 index 000000000..f863aa835 --- /dev/null +++ b/src/plugins/directshow/player/directshoweventloop.cpp @@ -0,0 +1,150 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include + +class DirectShowPostedEvent +{ +public: + DirectShowPostedEvent(QObject *receiver, QEvent *event) + : receiver(receiver) + , event(event) + , next(0) + { + } + + ~DirectShowPostedEvent() + { + delete event; + } + + QObject *receiver; + QEvent *event; + DirectShowPostedEvent *next; +}; + +DirectShowEventLoop::DirectShowEventLoop(QObject *parent) + : QObject(parent) + , m_postsHead(0) + , m_postsTail(0) + , m_eventHandle(::CreateEvent(0, 0, 0, 0)) + , m_waitHandle(::CreateEvent(0, 0, 0, 0)) +{ +} + +DirectShowEventLoop::~DirectShowEventLoop() +{ + ::CloseHandle(m_eventHandle); + ::CloseHandle(m_waitHandle); + + for (DirectShowPostedEvent *post = m_postsHead; post; post = m_postsHead) { + m_postsHead = m_postsHead->next; + + delete post; + } +} + +void DirectShowEventLoop::wait(QMutex *mutex) +{ + ::ResetEvent(m_waitHandle); + + mutex->unlock(); + + HANDLE handles[] = { m_eventHandle, m_waitHandle }; + while (::WaitForMultipleObjects(2, handles, false, INFINITE) == WAIT_OBJECT_0) + processEvents(); + + mutex->lock(); +} + +void DirectShowEventLoop::wake() +{ + ::SetEvent(m_waitHandle); +} + +void DirectShowEventLoop::postEvent(QObject *receiver, QEvent *event) +{ + QMutexLocker locker(&m_mutex); + + DirectShowPostedEvent *post = new DirectShowPostedEvent(receiver, event); + + if (m_postsTail) + m_postsTail->next = post; + else + m_postsHead = post; + + m_postsTail = post; + + QCoreApplication::postEvent(this, new QEvent(QEvent::User)); + ::SetEvent(m_eventHandle); +} + +void DirectShowEventLoop::customEvent(QEvent *event) +{ + if (event->type() == QEvent::User) { + processEvents(); + } else { + QObject::customEvent(event); + } +} + +void DirectShowEventLoop::processEvents() +{ + QMutexLocker locker(&m_mutex); + + ::ResetEvent(m_eventHandle); + + while(m_postsHead) { + DirectShowPostedEvent *post = m_postsHead; + m_postsHead = m_postsHead->next; + + if (!m_postsHead) + m_postsTail = 0; + + locker.unlock(); + QCoreApplication::sendEvent(post->receiver, post->event); + delete post; + locker.relock(); + } +} diff --git a/src/plugins/directshow/player/directshoweventloop.h b/src/plugins/directshow/player/directshoweventloop.h new file mode 100644 index 000000000..6f1a51cd5 --- /dev/null +++ b/src/plugins/directshow/player/directshoweventloop.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWEVENTLOOP_H +#define DIRECTSHOWEVENTLOOP_H + +#include +#include +#include + +#include + +class DirectShowPostedEvent; + +class DirectShowEventLoop : public QObject +{ + Q_OBJECT +public: + DirectShowEventLoop(QObject *parent = 0); + ~DirectShowEventLoop(); + + void wait(QMutex *mutex); + void wake(); + + void postEvent(QObject *object, QEvent *event); + +protected: + void customEvent(QEvent *event); + +private: + void processEvents(); + + DirectShowPostedEvent *m_postsHead; + DirectShowPostedEvent *m_postsTail; + HANDLE m_eventHandle; + HANDLE m_waitHandle; + QMutex m_mutex; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowglobal.h b/src/plugins/directshow/player/directshowglobal.h new file mode 100644 index 000000000..d14c117ec --- /dev/null +++ b/src/plugins/directshow/player/directshowglobal.h @@ -0,0 +1,139 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWGLOBAL_H +#define DIRECTSHOWGLOBAL_H + +#include + +#include + +template T *com_cast(IUnknown *unknown, const IID &iid) +{ + T *iface = 0; + return unknown && unknown->QueryInterface(iid, reinterpret_cast(&iface)) == S_OK + ? iface + : 0; +} + +template T *com_new(const IID &clsid, const IID &iid) +{ + T *object = 0; + return CoCreateInstance( + clsid, + NULL, + CLSCTX_INPROC_SERVER, + iid, + reinterpret_cast(&object)) == S_OK + ? object + : 0; +} + +#ifndef __IFilterGraph2_INTERFACE_DEFINED__ +#define __IFilterGraph2_INTERFACE_DEFINED__ +#define INTERFACE IFilterGraph2 +DECLARE_INTERFACE_(IFilterGraph2 ,IGraphBuilder) +{ + STDMETHOD(AddSourceFilterForMoniker)(THIS_ IMoniker *, IBindCtx *, LPCWSTR,IBaseFilter **) PURE; + STDMETHOD(ReconnectEx)(THIS_ IPin *, const AM_MEDIA_TYPE *) PURE; + STDMETHOD(RenderEx)(IPin *, DWORD, DWORD *) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IAMFilterMiscFlags_INTERFACE_DEFINED__ +#define __IAMFilterMiscFlags_INTERFACE_DEFINED__ +#define INTERFACE IAMFilterMiscFlags +DECLARE_INTERFACE_(IAMFilterMiscFlags ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD_(ULONG,GetMiscFlags)(THIS) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IFileSourceFilter_INTERFACE_DEFINED__ +#define __IFileSourceFilter_INTERFACE_DEFINED__ +#define INTERFACE IFileSourceFilter +DECLARE_INTERFACE_(IFileSourceFilter ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(Load)(THIS_ LPCOLESTR, const AM_MEDIA_TYPE *) PURE; + STDMETHOD(GetCurFile)(THIS_ LPOLESTR *ppszFileName, AM_MEDIA_TYPE *) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IAMOpenProgress_INTERFACE_DEFINED__ +#define __IAMOpenProgress_INTERFACE_DEFINED__ +#define INTERFACE IAMOpenProgress +DECLARE_INTERFACE_(IAMOpenProgress ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(QueryProgress)(THIS_ LONGLONG *, LONGLONG *) PURE; + STDMETHOD(AbortOperation)(THIS) PURE; +}; +#undef INTERFACE +#endif + +#ifndef __IFilterChain_INTERFACE_DEFINED__ +#define __IFilterChain_INTERFACE_DEFINED__ +#define INTERFACE IFilterChain +DECLARE_INTERFACE_(IFilterChain ,IUnknown) +{ + STDMETHOD(QueryInterface)(THIS_ REFIID,PVOID*) PURE; + STDMETHOD_(ULONG,AddRef)(THIS) PURE; + STDMETHOD_(ULONG,Release)(THIS) PURE; + STDMETHOD(StartChain)(IBaseFilter *, IBaseFilter *) PURE; + STDMETHOD(PauseChain)(IBaseFilter *, IBaseFilter *) PURE; + STDMETHOD(StopChain)(IBaseFilter *, IBaseFilter *) PURE; + STDMETHOD(RemoveChain)(IBaseFilter *, IBaseFilter *) PURE; +}; +#undef INTERFACE +#endif + +#endif diff --git a/src/plugins/directshow/player/directshowioreader.cpp b/src/plugins/directshow/player/directshowioreader.cpp new file mode 100644 index 000000000..5442f30d9 --- /dev/null +++ b/src/plugins/directshow/player/directshowioreader.cpp @@ -0,0 +1,496 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowioreader.h" + +#include "directshoweventloop.h" +#include "directshowglobal.h" +#include "directshowiosource.h" + +#include +#include +#include +#include + +class DirectShowSampleRequest +{ +public: + DirectShowSampleRequest( + IMediaSample *sample, DWORD_PTR userData, LONGLONG position, LONG length, BYTE *buffer) + : next(0) + , sample(sample) + , userData(userData) + , position(position) + , length(length) + , buffer(buffer) + , result(S_FALSE) + { + } + + DirectShowSampleRequest *remove() { DirectShowSampleRequest *n = next; delete this; return n; } + + DirectShowSampleRequest *next; + IMediaSample *sample; + DWORD_PTR userData; + LONGLONG position; + LONG length; + BYTE *buffer; + HRESULT result; +}; + +DirectShowIOReader::DirectShowIOReader( + QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop) + : m_source(source) + , m_device(device) + , m_loop(loop) + , m_pendingHead(0) + , m_pendingTail(0) + , m_readyHead(0) + , m_readyTail(0) + , m_synchronousPosition(0) + , m_synchronousLength(0) + , m_synchronousBytesRead(0) + , m_synchronousBuffer(0) + , m_synchronousResult(S_OK) + , m_totalLength(0) + , m_availableLength(0) + , m_flushing(false) +{ + moveToThread(device->thread()); + + connect(device, SIGNAL(readyRead()), this, SLOT(readyRead())); +} + +DirectShowIOReader::~DirectShowIOReader() +{ + flushRequests(); +} + +HRESULT DirectShowIOReader::QueryInterface(REFIID riid, void **ppvObject) +{ + return m_source->QueryInterface(riid, ppvObject); +} + +ULONG DirectShowIOReader::AddRef() +{ + return m_source->AddRef(); +} + +ULONG DirectShowIOReader::Release() +{ + return m_source->Release(); +} + +// IAsyncReader +HRESULT DirectShowIOReader::RequestAllocator( + IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps, IMemAllocator **ppActual) +{ + if (!ppActual || !pProps) { + return E_POINTER; + } else { + ALLOCATOR_PROPERTIES actualProperties; + + if (pProps->cbAlign == 0) + pProps->cbAlign = 1; + + if (pPreferred && pPreferred->SetProperties(pProps, &actualProperties) == S_OK) { + pPreferred->AddRef(); + + *ppActual = pPreferred; + + m_source->setAllocator(*ppActual); + + return S_OK; + } else { + *ppActual = com_new(CLSID_MemoryAllocator, IID_IMemAllocator); + + if (*ppActual) { + if ((*ppActual)->SetProperties(pProps, &actualProperties) != S_OK) { + (*ppActual)->Release(); + } else { + m_source->setAllocator(*ppActual); + + return S_OK; + } + } + } + ppActual = 0; + + return E_FAIL; + } +} + +HRESULT DirectShowIOReader::Request(IMediaSample *pSample, DWORD_PTR dwUser) +{ + QMutexLocker locker(&m_mutex); + + if (!pSample) { + return E_POINTER; + } else if (m_flushing) { + return VFW_E_WRONG_STATE; + } else { + REFERENCE_TIME startTime = 0; + REFERENCE_TIME endTime = 0; + BYTE *buffer; + + if (pSample->GetTime(&startTime, &endTime) != S_OK + || pSample->GetPointer(&buffer) != S_OK) { + return VFW_E_SAMPLE_TIME_NOT_SET; + } else { + LONGLONG position = startTime / 10000000; + LONG length = (endTime - startTime) / 10000000; + + DirectShowSampleRequest *request = new DirectShowSampleRequest( + pSample, dwUser, position, length, buffer); + + if (m_pendingTail) { + m_pendingTail->next = request; + } else { + m_pendingHead = request; + + m_loop->postEvent(this, new QEvent(QEvent::User)); + } + m_pendingTail = request; + + return S_OK; + } + } +} + +HRESULT DirectShowIOReader::WaitForNext( + DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser) +{ + if (!ppSample || !pdwUser) + return E_POINTER; + + QMutexLocker locker(&m_mutex); + + do { + if (m_readyHead) { + DirectShowSampleRequest *request = m_readyHead; + + *ppSample = request->sample; + *pdwUser = request->userData; + + HRESULT hr = request->result; + + m_readyHead = request->next; + + if (!m_readyHead) + m_readyTail = 0; + + delete request; + + return hr; + } else if (m_flushing) { + *ppSample = 0; + *pdwUser = 0; + + return VFW_E_WRONG_STATE; + } + } while (m_wait.wait(&m_mutex, dwTimeout)); + + *ppSample = 0; + *pdwUser = 0; + + return VFW_E_TIMEOUT; +} + +HRESULT DirectShowIOReader::SyncReadAligned(IMediaSample *pSample) +{ + if (!pSample) { + return E_POINTER; + } else { + REFERENCE_TIME startTime = 0; + REFERENCE_TIME endTime = 0; + BYTE *buffer; + + if (pSample->GetTime(&startTime, &endTime) != S_OK + || pSample->GetPointer(&buffer) != S_OK) { + return VFW_E_SAMPLE_TIME_NOT_SET; + } else { + LONGLONG position = startTime / 10000000; + LONG length = (endTime - startTime) / 10000000; + + QMutexLocker locker(&m_mutex); + + if (thread() == QThread::currentThread()) { + qint64 bytesRead = 0; + + HRESULT hr = blockingRead(position, length, buffer, &bytesRead); + + if (SUCCEEDED(hr)) + pSample->SetActualDataLength(bytesRead); + + return hr; + } else { + m_synchronousPosition = position; + m_synchronousLength = length; + m_synchronousBuffer = buffer; + + m_loop->postEvent(this, new QEvent(QEvent::User)); + + m_wait.wait(&m_mutex); + + m_synchronousBuffer = 0; + + if (SUCCEEDED(m_synchronousResult)) + pSample->SetActualDataLength(m_synchronousBytesRead); + + return m_synchronousResult; + } + } + } +} + +HRESULT DirectShowIOReader::SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer) +{ + if (!pBuffer) { + return E_POINTER; + } else { + if (thread() == QThread::currentThread()) { + qint64 bytesRead; + + return blockingRead(llPosition, lLength, pBuffer, &bytesRead); + } else { + QMutexLocker locker(&m_mutex); + + m_synchronousPosition = llPosition; + m_synchronousLength = lLength; + m_synchronousBuffer = pBuffer; + + m_loop->postEvent(this, new QEvent(QEvent::User)); + + m_wait.wait(&m_mutex); + + m_synchronousBuffer = 0; + + return m_synchronousResult; + } + } +} + +HRESULT DirectShowIOReader::Length(LONGLONG *pTotal, LONGLONG *pAvailable) +{ + if (!pTotal || !pAvailable) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + *pTotal = m_totalLength; + *pAvailable = m_availableLength; + + return S_OK; + } +} + + +HRESULT DirectShowIOReader::BeginFlush() +{ + QMutexLocker locker(&m_mutex); + + if (m_flushing) + return S_FALSE; + + m_flushing = true; + + flushRequests(); + + m_wait.wakeAll(); + + return S_OK; +} + +HRESULT DirectShowIOReader::EndFlush() +{ + QMutexLocker locker(&m_mutex); + + if (!m_flushing) + return S_FALSE; + + m_flushing = false; + + return S_OK; +} + +void DirectShowIOReader::customEvent(QEvent *event) +{ + if (event->type() == QEvent::User) { + readyRead(); + } else { + QObject::customEvent(event); + } +} + +void DirectShowIOReader::readyRead() +{ + QMutexLocker locker(&m_mutex); + + m_availableLength = m_device->bytesAvailable() + m_device->pos(); + m_totalLength = m_device->size(); + + if (m_synchronousBuffer) { + if (nonBlockingRead( + m_synchronousPosition, + m_synchronousLength, + m_synchronousBuffer, + &m_synchronousBytesRead, + &m_synchronousResult)) { + m_wait.wakeAll(); + } + } else { + qint64 bytesRead = 0; + + while (m_pendingHead && nonBlockingRead( + m_pendingHead->position, + m_pendingHead->length, + m_pendingHead->buffer, + &bytesRead, + &m_pendingHead->result)) { + m_pendingHead->sample->SetActualDataLength(bytesRead); + + if (m_readyTail) + m_readyTail->next = m_pendingHead; + m_readyTail = m_pendingHead; + + m_pendingHead = m_pendingHead->next; + + m_readyTail->next = 0; + + if (!m_pendingHead) + m_pendingTail = 0; + + if (!m_readyHead) + m_readyHead = m_readyTail; + + m_wait.wakeAll(); + } + } +} + +HRESULT DirectShowIOReader::blockingRead( + LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead) +{ + *bytesRead = 0; + + if (qint64(position) > m_device->size()) + return S_FALSE; + + const qint64 maxSize = qMin(m_device->size(), position + length); + + while (m_device->bytesAvailable() + m_device->pos() < maxSize) { + if (!m_device->waitForReadyRead(-1)) + return S_FALSE; + } + + if (m_device->pos() != position && !m_device->seek(position)) + return S_FALSE; + + const qint64 maxBytes = qMin(length, m_device->bytesAvailable()); + + *bytesRead = m_device->read(reinterpret_cast(buffer), maxBytes); + + if (*bytesRead != length) { + qMemSet(buffer + *bytesRead, 0, length - *bytesRead); + + return S_FALSE; + } else { + return S_OK; + } +} + +bool DirectShowIOReader::nonBlockingRead( + LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result) +{ + const qint64 maxSize = qMin(m_device->size(), position + length); + + if (position > m_device->size()) { + *bytesRead = 0; + *result = S_FALSE; + + return true; + } else if (m_device->bytesAvailable() + m_device->pos() >= maxSize) { + if (m_device->pos() != position && !m_device->seek(position)) { + *bytesRead = 0; + *result = S_FALSE; + + return true; + } else { + const qint64 maxBytes = qMin(length, m_device->bytesAvailable()); + + *bytesRead = m_device->read(reinterpret_cast(buffer), maxBytes); + + if (*bytesRead != length) { + qMemSet(buffer + *bytesRead, 0, length - *bytesRead); + + *result = S_FALSE; + } else { + *result = S_OK; + } + + return true; + } + } else { + return false; + } +} + +void DirectShowIOReader::flushRequests() +{ + while (m_pendingHead) { + m_pendingHead->result = VFW_E_WRONG_STATE; + + if (m_readyTail) + m_readyTail->next = m_pendingHead; + + m_readyTail = m_pendingHead; + + m_pendingHead = m_pendingHead->next; + + m_readyTail->next = 0; + + if (!m_pendingHead) + m_pendingTail = 0; + + if (!m_readyHead) + m_readyHead = m_readyTail; + } +} diff --git a/src/plugins/directshow/player/directshowioreader.h b/src/plugins/directshow/player/directshowioreader.h new file mode 100644 index 000000000..ca398f12e --- /dev/null +++ b/src/plugins/directshow/player/directshowioreader.h @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWIOREADER_H +#define DIRECTSHOWIOREADER_H + +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QIODevice; +QT_END_NAMESPACE + +class DirectShowEventLoop; +class DirectShowIOSource; +class DirectShowSampleRequest; + +class DirectShowIOReader : public QObject, public IAsyncReader +{ + Q_OBJECT +public: + DirectShowIOReader(QIODevice *device, DirectShowIOSource *source, DirectShowEventLoop *loop); + ~DirectShowIOReader(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IAsyncReader + HRESULT STDMETHODCALLTYPE RequestAllocator( + IMemAllocator *pPreferred, ALLOCATOR_PROPERTIES *pProps, IMemAllocator **ppActual); + + HRESULT STDMETHODCALLTYPE Request(IMediaSample *pSample, DWORD_PTR dwUser); + + HRESULT STDMETHODCALLTYPE WaitForNext( + DWORD dwTimeout, IMediaSample **ppSample, DWORD_PTR *pdwUser); + + HRESULT STDMETHODCALLTYPE SyncReadAligned(IMediaSample *pSample); + + HRESULT STDMETHODCALLTYPE SyncRead(LONGLONG llPosition, LONG lLength, BYTE *pBuffer); + + HRESULT STDMETHODCALLTYPE Length(LONGLONG *pTotal, LONGLONG *pAvailable); + + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + +protected: + void customEvent(QEvent *event); + +private Q_SLOTS: + void readyRead(); + +private: + HRESULT blockingRead(LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead); + bool nonBlockingRead( + LONGLONG position, LONG length, BYTE *buffer, qint64 *bytesRead, HRESULT *result); + void flushRequests(); + + DirectShowIOSource *m_source; + QIODevice *m_device; + DirectShowEventLoop *m_loop; + DirectShowSampleRequest *m_pendingHead; + DirectShowSampleRequest *m_pendingTail; + DirectShowSampleRequest *m_readyHead; + DirectShowSampleRequest *m_readyTail; + LONGLONG m_synchronousPosition; + LONG m_synchronousLength; + qint64 m_synchronousBytesRead; + BYTE *m_synchronousBuffer; + HRESULT m_synchronousResult; + LONGLONG m_totalLength; + LONGLONG m_availableLength; + bool m_flushing; + QMutex m_mutex; + QWaitCondition m_wait; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowiosource.cpp b/src/plugins/directshow/player/directshowiosource.cpp new file mode 100644 index 000000000..c8f3e6a97 --- /dev/null +++ b/src/plugins/directshow/player/directshowiosource.cpp @@ -0,0 +1,639 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowiosource.h" + +#include "directshowglobal.h" +#include "directshowmediatype.h" +#include "directshowpinenum.h" + +#include +#include + +static const GUID directshow_subtypes[] = +{ + MEDIASUBTYPE_Avi, + MEDIASUBTYPE_WAVE, + MEDIASUBTYPE_NULL +}; + +DirectShowIOSource::DirectShowIOSource(DirectShowEventLoop *loop) + : m_ref(1) + , m_state(State_Stopped) + , m_reader(0) + , m_loop(loop) + , m_graph(0) + , m_clock(0) + , m_allocator(0) + , m_peerPin(0) + , m_pinId(QLatin1String("Data")) +{ + QVector mediaTypes; + + AM_MEDIA_TYPE type = + { + MEDIATYPE_Stream, // majortype + MEDIASUBTYPE_NULL, // subtype + TRUE, // bFixedSizeSamples + FALSE, // bTemporalCompression + 1, // lSampleSize + GUID_NULL, // formattype + 0, // pUnk + 0, // cbFormat + 0, // pbFormat + }; + + static const int count = sizeof(directshow_subtypes) / sizeof(GUID); + + for (int i = 0; i < count; ++i) { + type.subtype = directshow_subtypes[i]; + mediaTypes.append(type); + } + + setMediaTypes(mediaTypes); +} + +DirectShowIOSource::~DirectShowIOSource() +{ + Q_ASSERT(m_ref == 0); + + delete m_reader; +} + +void DirectShowIOSource::setDevice(QIODevice *device) +{ + Q_ASSERT(!m_reader); + + m_reader = new DirectShowIOReader(device, this, m_loop); +} + +void DirectShowIOSource::setAllocator(IMemAllocator *allocator) +{ + if (m_allocator) + m_allocator->Release(); + + m_allocator = allocator; + + if (m_allocator) + m_allocator->AddRef(); +} + +// IUnknown +HRESULT DirectShowIOSource::QueryInterface(REFIID riid, void **ppvObject) +{ + // 2dd74950-a890-11d1-abe8-00a0c905f375 + static const GUID iid_IAmFilterMiscFlags = { + 0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75}}; + + if (!ppvObject) { + return E_POINTER; + } else if (riid == IID_IUnknown + || riid == IID_IPersist + || riid == IID_IMediaFilter + || riid == IID_IBaseFilter) { + *ppvObject = static_cast(this); + } else if (riid == iid_IAmFilterMiscFlags) { + *ppvObject = static_cast(this); + } else if (riid == IID_IPin) { + *ppvObject = static_cast(this); + } else if (riid == IID_IAsyncReader) { + *ppvObject = static_cast(m_reader); + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG DirectShowIOSource::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DirectShowIOSource::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + if (ref == 0) { + delete this; + } + + return ref; +} + +// IPersist +HRESULT DirectShowIOSource::GetClassID(CLSID *pClassID) +{ + *pClassID = CLSID_NULL; + + return S_OK; +} + +// IMediaFilter +HRESULT DirectShowIOSource::Run(REFERENCE_TIME tStart) +{ + QMutexLocker locker(&m_mutex); + + m_state = State_Running; + + return S_OK; +} + +HRESULT DirectShowIOSource::Pause() +{ + QMutexLocker locker(&m_mutex); + + m_state = State_Paused; + + return S_OK; +} + +HRESULT DirectShowIOSource::Stop() +{ + QMutexLocker locker(&m_mutex); + + m_state = State_Stopped; + + return S_OK; +} + +HRESULT DirectShowIOSource::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState) +{ + Q_UNUSED(dwMilliSecsTimeout); + + if (!pState) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + *pState = m_state; + + return S_OK; + } +} + +HRESULT DirectShowIOSource::SetSyncSource(IReferenceClock *pClock) +{ + QMutexLocker locker(&m_mutex); + + if (m_clock) + m_clock->Release(); + + m_clock = pClock; + + if (m_clock) + m_clock->AddRef(); + + return S_OK; +} + +HRESULT DirectShowIOSource::GetSyncSource(IReferenceClock **ppClock) +{ + if (!ppClock) { + return E_POINTER; + } else { + if (!m_clock) { + *ppClock = 0; + + return S_FALSE; + } else { + m_clock->AddRef(); + + *ppClock = m_clock; + + return S_OK; + } + } +} + +// IBaseFilter +HRESULT DirectShowIOSource::EnumPins(IEnumPins **ppEnum) +{ + if (!ppEnum) { + return E_POINTER; + } else { + *ppEnum = new DirectShowPinEnum(QList() << this); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::FindPin(LPCWSTR Id, IPin **ppPin) +{ + if (!ppPin || !Id) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + if (QString::fromWCharArray(Id) == m_pinId) { + AddRef(); + + *ppPin = this; + + return S_OK; + } else { + *ppPin = 0; + + return VFW_E_NOT_FOUND; + } + } +} + +HRESULT DirectShowIOSource::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) +{ + QMutexLocker locker(&m_mutex); + + m_graph = pGraph; + m_filterName = QString::fromWCharArray(pName); + + return S_OK; +} + +HRESULT DirectShowIOSource::QueryFilterInfo(FILTER_INFO *pInfo) +{ + if (!pInfo) { + return E_POINTER; + } else { + QString name = m_filterName; + + if (name.length() >= MAX_FILTER_NAME) + name.truncate(MAX_FILTER_NAME - 1); + + int length = name.toWCharArray(pInfo->achName); + pInfo->achName[length] = '\0'; + + if (m_graph) + m_graph->AddRef(); + + pInfo->pGraph = m_graph; + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryVendorInfo(LPWSTR *pVendorInfo) +{ + Q_UNUSED(pVendorInfo); + + return E_NOTIMPL; +} + +// IAMFilterMiscFlags +ULONG DirectShowIOSource::GetMiscFlags() +{ + return AM_FILTER_MISC_FLAGS_IS_SOURCE; +} + +// IPin +HRESULT DirectShowIOSource::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + QMutexLocker locker(&m_mutex); + if (!pReceivePin) { + return E_POINTER; + } else if (m_state != State_Stopped) { + return VFW_E_NOT_STOPPED; + } else if (m_peerPin) { + return VFW_E_ALREADY_CONNECTED; + } else { + HRESULT hr = VFW_E_TYPE_NOT_ACCEPTED; + + m_peerPin = pReceivePin; + m_peerPin->AddRef(); + + if (!pmt) { + IEnumMediaTypes *mediaTypes = 0; + if (pReceivePin->EnumMediaTypes(&mediaTypes) == S_OK) { + for (AM_MEDIA_TYPE *type = 0; + mediaTypes->Next(1, &type, 0) == S_OK; + DirectShowMediaType::deleteType(type)) { + switch (tryConnect(pReceivePin, type)) { + case S_OK: + DirectShowMediaType::freeData(type); + mediaTypes->Release(); + return S_OK; + case VFW_E_NO_TRANSPORT: + hr = VFW_E_NO_TRANSPORT; + break; + default: + break; + } + } + mediaTypes->Release(); + } + AM_MEDIA_TYPE type = + { + MEDIATYPE_Stream, // majortype + MEDIASUBTYPE_NULL, // subtype + TRUE, // bFixedSizeSamples + FALSE, // bTemporalCompression + 1, // lSampleSize + GUID_NULL, // formattype + 0, // pUnk + 0, // cbFormat + 0, // pbFormat + }; + + static const int count = sizeof(directshow_subtypes) / sizeof(GUID); + + for (int i = 0; i < count; ++i) { + type.subtype = directshow_subtypes[i]; + + switch (tryConnect(pReceivePin, &type)) { + case S_OK: + return S_OK; + case VFW_E_NO_TRANSPORT: + hr = VFW_E_NO_TRANSPORT; + break; + default: + break; + } + } + } else if (pmt->majortype == MEDIATYPE_Stream && (hr = tryConnect(pReceivePin, pmt))) { + return S_OK; + } + + m_peerPin->Release(); + m_peerPin = 0; + + m_mediaType.clear(); + + return hr; + } +} + +HRESULT DirectShowIOSource::tryConnect(IPin *pin, const AM_MEDIA_TYPE *type) +{ + m_mediaType = *type; + + HRESULT hr = pin->ReceiveConnection(this, type); + + if (!SUCCEEDED(hr)) { + if (m_allocator) { + m_allocator->Release(); + m_allocator = 0; + } + } else if (!m_allocator) { + hr = VFW_E_NO_TRANSPORT; + + if (IMemInputPin *memPin = com_cast(pin, IID_IMemInputPin)) { + if ((m_allocator = com_new(CLSID_MemoryAllocator, IID_IMemAllocator))) { + ALLOCATOR_PROPERTIES properties; + if (memPin->GetAllocatorRequirements(&properties) == S_OK + || m_allocator->GetProperties(&properties) == S_OK) { + if (properties.cbAlign == 0) + properties.cbAlign = 1; + + ALLOCATOR_PROPERTIES actualProperties; + if (SUCCEEDED(hr = m_allocator->SetProperties(&properties, &actualProperties))) + hr = memPin->NotifyAllocator(m_allocator, TRUE); + } + if (!SUCCEEDED(hr)) { + m_allocator->Release(); + m_allocator = 0; + } + } + memPin->Release(); + } + if (!SUCCEEDED(hr)) + pin->Disconnect(); + } + return hr; +} + +HRESULT DirectShowIOSource::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + Q_UNUSED(pConnector); + Q_UNUSED(pmt); + // Output pin. + return E_NOTIMPL; +} + +HRESULT DirectShowIOSource::Disconnect() +{ + if (!m_peerPin) { + return S_FALSE; + } else if (m_state != State_Stopped) { + return VFW_E_NOT_STOPPED; + } else { + HRESULT hr = m_peerPin->Disconnect(); + + if (!SUCCEEDED(hr)) + return hr; + + if (m_allocator) { + m_allocator->Release(); + m_allocator = 0; + } + + m_peerPin->Release(); + m_peerPin = 0; + + m_mediaType.clear(); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::ConnectedTo(IPin **ppPin) +{ + if (!ppPin) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + *ppPin = 0; + + return VFW_E_NOT_CONNECTED; + } else { + m_peerPin->AddRef(); + + *ppPin = m_peerPin; + + return S_OK; + } + } +} + +HRESULT DirectShowIOSource::ConnectionMediaType(AM_MEDIA_TYPE *pmt) +{ + if (!pmt) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + pmt = 0; + + return VFW_E_NOT_CONNECTED; + } else { + DirectShowMediaType::copy(pmt, m_mediaType); + + return S_OK; + } + } +} + +HRESULT DirectShowIOSource::QueryPinInfo(PIN_INFO *pInfo) +{ + if (!pInfo) { + return E_POINTER; + } else { + AddRef(); + + pInfo->pFilter = this; + pInfo->dir = PINDIR_OUTPUT; + + const int bytes = qMin(MAX_FILTER_NAME, (m_pinId.length() + 1) * 2); + + qMemCopy(pInfo->achName, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryId(LPWSTR *Id) +{ + if (!Id) { + return E_POINTER; + } else { + const int bytes = (m_pinId.length() + 1) * 2; + + *Id = static_cast(::CoTaskMemAlloc(bytes)); + + qMemCopy(*Id, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryAccept(const AM_MEDIA_TYPE *pmt) +{ + if (!pmt) { + return E_POINTER; + } else if (pmt->majortype == MEDIATYPE_Stream) { + return S_OK; + } else { + return S_FALSE; + } +} + +HRESULT DirectShowIOSource::EnumMediaTypes(IEnumMediaTypes **ppEnum) +{ + if (!ppEnum) { + return E_POINTER; + } else { + *ppEnum = createMediaTypeEnum(); + + return S_OK; + } +} + +HRESULT DirectShowIOSource::QueryInternalConnections(IPin **apPin, ULONG *nPin) +{ + Q_UNUSED(apPin); + Q_UNUSED(nPin); + + return E_NOTIMPL; +} + +HRESULT DirectShowIOSource::EndOfStream() +{ + return S_OK; +} + +HRESULT DirectShowIOSource::BeginFlush() +{ + return m_reader->BeginFlush(); +} + +HRESULT DirectShowIOSource::EndFlush() +{ + return m_reader->EndFlush(); +} + +HRESULT DirectShowIOSource::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) +{ + Q_UNUSED(tStart); + Q_UNUSED(tStop); + Q_UNUSED(dRate); + + return S_OK; +} + +HRESULT DirectShowIOSource::QueryDirection(PIN_DIRECTION *pPinDir) +{ + if (!pPinDir) { + return E_POINTER; + } else { + *pPinDir = PINDIR_OUTPUT; + + return S_OK; + } +} + +DirectShowRcSource::DirectShowRcSource(DirectShowEventLoop *loop) + : DirectShowIOSource(loop) +{ +} + +bool DirectShowRcSource::open(const QUrl &url) +{ + m_file.moveToThread(QCoreApplication::instance()->thread()); + + m_file.setFileName(QLatin1Char(':') + url.path()); + + qDebug("qrc file %s", qPrintable(m_file.fileName())); + + if (m_file.open(QIODevice::ReadOnly)) { + qDebug("Size %d", m_file.size()); + qDebug("Sequential %d", int(m_file.isSequential())); + + setDevice(&m_file); + + return true; + } else { + return false; + } +} diff --git a/src/plugins/directshow/player/directshowiosource.h b/src/plugins/directshow/player/directshowiosource.h new file mode 100644 index 000000000..3019bdbe9 --- /dev/null +++ b/src/plugins/directshow/player/directshowiosource.h @@ -0,0 +1,149 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWIOSOURCE_H +#define DIRECTSHOWIOSOURCE_H + +#include "directshowglobal.h" +#include "directshowioreader.h" +#include "directshowmediatype.h" +#include "directshowmediatypelist.h" + +#include + +class DirectShowIOSource + : public DirectShowMediaTypeList + , public IBaseFilter + , public IAMFilterMiscFlags + , public IPin +{ +public: + DirectShowIOSource(DirectShowEventLoop *loop); + ~DirectShowIOSource(); + + void setDevice(QIODevice *device); + void setAllocator(IMemAllocator *allocator); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IPersist + HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID); + + // IMediaFilter + HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart); + HRESULT STDMETHODCALLTYPE Pause(); + HRESULT STDMETHODCALLTYPE Stop(); + + HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState); + + HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock); + HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock **ppClock); + + // IBaseFilter + HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins **ppEnum); + HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin **ppPin); + + HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName); + + HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR *pVendorInfo); + + // IAMFilterMiscFlags + ULONG STDMETHODCALLTYPE GetMiscFlags(); + + // IPin + HRESULT STDMETHODCALLTYPE Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE Disconnect(); + HRESULT STDMETHODCALLTYPE ConnectedTo(IPin **ppPin); + + HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryId(LPWSTR *Id); + + HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes **ppEnum); + + HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin **apPin, ULONG *nPin); + + HRESULT STDMETHODCALLTYPE EndOfStream(); + + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + + HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); + + HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir); + +private: + HRESULT tryConnect(IPin *pin, const AM_MEDIA_TYPE *type); + + volatile LONG m_ref; + FILTER_STATE m_state; + DirectShowIOReader *m_reader; + DirectShowEventLoop *m_loop; + IFilterGraph *m_graph; + IReferenceClock *m_clock; + IMemAllocator *m_allocator; + IPin *m_peerPin; + DirectShowMediaType m_mediaType; + QString m_filterName; + const QString m_pinId; + QMutex m_mutex; +}; + +class DirectShowRcSource : public DirectShowIOSource +{ +public: + DirectShowRcSource(DirectShowEventLoop *loop); + + bool open(const QUrl &url); + +private: + QFile m_file; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowmediatype.cpp b/src/plugins/directshow/player/directshowmediatype.cpp new file mode 100644 index 000000000..667d9491e --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatype.cpp @@ -0,0 +1,184 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowmediatype.h" + +namespace +{ + struct TypeLookup + { + QVideoFrame::PixelFormat pixelFormat; + GUID mediaType; + }; + + static const TypeLookup qt_typeLookup[] = + { + { QVideoFrame::Format_RGB32, /*MEDIASUBTYPE_RGB32*/ {0xe436eb7e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_BGR24, /*MEDIASUBTYPE_RGB24*/ {0xe436eb7d, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_RGB565, /*MEDIASUBTYPE_RGB565*/ {0xe436eb7b, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_RGB555, /*MEDIASUBTYPE_RGB555*/ {0xe436eb7c, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70}} }, + { QVideoFrame::Format_AYUV444, /*MEDIASUBTYPE_AYUV*/ {0x56555941, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_YUYV, /*MEDIASUBTYPE_YUY2*/ {0x32595559, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_UYVY, /*MEDIASUBTYPE_UYVY*/ {0x59565955, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC1, /*MEDIASUBTYPE_IMC1*/ {0x31434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC2, /*MEDIASUBTYPE_IMC2*/ {0x32434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC3, /*MEDIASUBTYPE_IMC3*/ {0x33434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_IMC4, /*MEDIASUBTYPE_IMC4*/ {0x34434D49, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_YV12, /*MEDIASUBTYPE_YV12*/ {0x32315659, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_NV12, /*MEDIASUBTYPE_NV12*/ {0x3231564E, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} }, + { QVideoFrame::Format_YUV420P, /*MEDIASUBTYPE_IYUV*/ {0x56555949, 0x0000, 0x0010, {0x80, 0x00, 0x00, 0xaa, 0x00, 0x38, 0x9b, 0x71}} } + }; +} + +void DirectShowMediaType::copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source) +{ + *target = source; + + if (source.cbFormat > 0) { + target->pbFormat = reinterpret_cast(CoTaskMemAlloc(source.cbFormat)); + memcpy(target->pbFormat, source.pbFormat, source.cbFormat); + } + if (target->pUnk) + target->pUnk->AddRef(); +} + +void DirectShowMediaType::deleteType(AM_MEDIA_TYPE *type) +{ + freeData(type); + + CoTaskMemFree(type); +} + +void DirectShowMediaType::freeData(AM_MEDIA_TYPE *type) +{ + if (type->cbFormat > 0) + CoTaskMemFree(type->pbFormat); + + if (type->pUnk) + type->pUnk->Release(); +} + + +GUID DirectShowMediaType::convertPixelFormat(QVideoFrame::PixelFormat format) +{ + // MEDIASUBTYPE_None; + static const GUID none = { + 0xe436eb8e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} }; + + const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup); + + for (int i = 0; i < count; ++i) + if (qt_typeLookup[i].pixelFormat == format) + return qt_typeLookup[i].mediaType; + return none; +} + +QVideoSurfaceFormat DirectShowMediaType::formatFromType(const AM_MEDIA_TYPE &type) +{ + const int count = sizeof(qt_typeLookup) / sizeof(TypeLookup); + + for (int i = 0; i < count; ++i) { + if (IsEqualGUID(qt_typeLookup[i].mediaType, type.subtype) && type.cbFormat > 0) { + if (IsEqualGUID(type.formattype, FORMAT_VideoInfo)) { + VIDEOINFOHEADER *header = reinterpret_cast(type.pbFormat); + + QVideoSurfaceFormat format( + QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)), + qt_typeLookup[i].pixelFormat); + + if (header->AvgTimePerFrame > 0) + format.setFrameRate(10000 /header->AvgTimePerFrame); + + format.setScanLineDirection(header->bmiHeader.biHeight < 0 + ? QVideoSurfaceFormat::TopToBottom + : QVideoSurfaceFormat::BottomToTop); + + return format; + } else if (IsEqualGUID(type.formattype, FORMAT_VideoInfo2)) { + VIDEOINFOHEADER2 *header = reinterpret_cast(type.pbFormat); + + QVideoSurfaceFormat format( + QSize(header->bmiHeader.biWidth, qAbs(header->bmiHeader.biHeight)), + qt_typeLookup[i].pixelFormat); + + if (header->AvgTimePerFrame > 0) + format.setFrameRate(10000 / header->AvgTimePerFrame); + + format.setScanLineDirection(header->bmiHeader.biHeight < 0 + ? QVideoSurfaceFormat::TopToBottom + : QVideoSurfaceFormat::BottomToTop); + + return format; + } + } + } + return QVideoSurfaceFormat(); +} + +int DirectShowMediaType::bytesPerLine(const QVideoSurfaceFormat &format) +{ + switch (format.pixelFormat()) { + // 32 bpp packed formats. + case QVideoFrame::Format_RGB32: + case QVideoFrame::Format_AYUV444: + return format.frameWidth() * 4; + // 24 bpp packed formats. + case QVideoFrame::Format_RGB24: + return format.frameWidth() * 3 + 3 - format.frameWidth() % 4; + // 16 bpp packed formats. + case QVideoFrame::Format_RGB565: + case QVideoFrame::Format_RGB555: + case QVideoFrame::Format_YUYV: + case QVideoFrame::Format_UYVY: + return format.frameWidth() * 2 + 3 - format.frameWidth() % 4; + // Planar formats. + case QVideoFrame::Format_IMC1: + case QVideoFrame::Format_IMC2: + case QVideoFrame::Format_IMC3: + case QVideoFrame::Format_IMC4: + case QVideoFrame::Format_YV12: + case QVideoFrame::Format_NV12: + case QVideoFrame::Format_YUV420P: + return format.frameWidth() + 3 - format.frameWidth() % 4; + default: + return 0; + } +} diff --git a/src/plugins/directshow/player/directshowmediatype.h b/src/plugins/directshow/player/directshowmediatype.h new file mode 100644 index 000000000..7d37a5c29 --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatype.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWMEDIATYPE_H +#define DIRECTSHOWMEDIATYPE_H + +#include + +#include +#include + +class DirectShowMediaType : public AM_MEDIA_TYPE +{ +public: + DirectShowMediaType() { memset(this, 0, sizeof(DirectShowMediaType)); } + DirectShowMediaType(const AM_MEDIA_TYPE &type) { copy(this, type); } + DirectShowMediaType(const DirectShowMediaType &other) { copy(this, other); } + DirectShowMediaType &operator =(const AM_MEDIA_TYPE &type) { + freeData(this); copy(this, type); return *this; } + DirectShowMediaType &operator =(const DirectShowMediaType &other) { + freeData(this); copy(this, other); return *this; } + ~DirectShowMediaType() { freeData(this); } + + void clear() { freeData(this); memset(this, 0, sizeof(DirectShowMediaType)); } + + static void copy(AM_MEDIA_TYPE *target, const AM_MEDIA_TYPE &source); + static void freeData(AM_MEDIA_TYPE *type); + static void deleteType(AM_MEDIA_TYPE *type); + + static GUID convertPixelFormat(QVideoFrame::PixelFormat format); + static QVideoSurfaceFormat formatFromType(const AM_MEDIA_TYPE &type); + + static int bytesPerLine(const QVideoSurfaceFormat &format); +}; + +#endif diff --git a/src/plugins/directshow/player/directshowmediatypelist.cpp b/src/plugins/directshow/player/directshowmediatypelist.cpp new file mode 100644 index 000000000..3060975fc --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatypelist.cpp @@ -0,0 +1,226 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowmediatypelist.h" + +#include "directshowmediatype.h" +#include "videosurfacefilter.h" + + +class DirectShowMediaTypeEnum : public IEnumMediaTypes +{ +public: + DirectShowMediaTypeEnum(DirectShowMediaTypeList *list, int token, int index = 0); + ~DirectShowMediaTypeEnum(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IEnumMediaTypes + HRESULT STDMETHODCALLTYPE Next( + ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched); + HRESULT STDMETHODCALLTYPE Skip(ULONG cMediaTypes); + HRESULT STDMETHODCALLTYPE Reset(); + + HRESULT STDMETHODCALLTYPE Clone(IEnumMediaTypes **ppEnum); + +private: + LONG m_ref; + DirectShowMediaTypeList *m_list; + int m_mediaTypeToken; + int m_index; +}; + + +DirectShowMediaTypeEnum::DirectShowMediaTypeEnum( + DirectShowMediaTypeList *list, int token, int index) + : m_ref(1) + , m_list(list) + , m_mediaTypeToken(token) + , m_index(index) +{ + m_list->AddRef(); +} + +DirectShowMediaTypeEnum::~DirectShowMediaTypeEnum() +{ + m_list->Release(); +} + +HRESULT DirectShowMediaTypeEnum::QueryInterface(REFIID riid, void **ppvObject) +{ + if (!ppvObject) { + return E_POINTER; + } else if (riid == IID_IUnknown + || riid == IID_IEnumMediaTypes) { + *ppvObject = static_cast(this); + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG DirectShowMediaTypeEnum::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DirectShowMediaTypeEnum::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + if (ref == 0) { + delete this; + } + + return ref; +} + +HRESULT DirectShowMediaTypeEnum::Next( + ULONG cMediaTypes, AM_MEDIA_TYPE **ppMediaTypes, ULONG *pcFetched) +{ + return m_list->nextMediaType(m_mediaTypeToken, &m_index, cMediaTypes, ppMediaTypes, pcFetched); +} + +HRESULT DirectShowMediaTypeEnum::Skip(ULONG cMediaTypes) +{ + return m_list->skipMediaType(m_mediaTypeToken, &m_index, cMediaTypes); +} + +HRESULT DirectShowMediaTypeEnum::Reset() +{ + m_mediaTypeToken = m_list->currentMediaTypeToken(); + m_index = 0; + + return S_OK; +} + +HRESULT DirectShowMediaTypeEnum::Clone(IEnumMediaTypes **ppEnum) +{ + return m_list->cloneMediaType(m_mediaTypeToken, m_index, ppEnum); +} + + +DirectShowMediaTypeList::DirectShowMediaTypeList() + : m_mediaTypeToken(0) +{ +} + +IEnumMediaTypes *DirectShowMediaTypeList::createMediaTypeEnum() +{ + return new DirectShowMediaTypeEnum(this, m_mediaTypeToken, 0); +} + + +void DirectShowMediaTypeList::setMediaTypes(const QVector &types) +{ + ++m_mediaTypeToken; + + m_mediaTypes = types; +} + + +int DirectShowMediaTypeList::currentMediaTypeToken() +{ + return m_mediaTypeToken; +} + +HRESULT DirectShowMediaTypeList::nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount) +{ + if (!types || (count != 1 && !fetchedCount)) { + return E_POINTER; + } else if (m_mediaTypeToken != token) { + return VFW_E_ENUM_OUT_OF_SYNC; + } else { + int boundedCount = qBound(0, count, m_mediaTypes.count() - *index); + + for (int i = 0; i < boundedCount; ++i, ++(*index)) { + types[i] = reinterpret_cast(CoTaskMemAlloc(sizeof(AM_MEDIA_TYPE))); + + if (types[i]) { + DirectShowMediaType::copy(types[i], m_mediaTypes.at(*index)); + } else { + for (--i; i >= 0; --i) + CoTaskMemFree(types[i]); + + if (fetchedCount) + *fetchedCount = 0; + + return E_OUTOFMEMORY; + } + } + if (fetchedCount) + *fetchedCount = boundedCount; + + return boundedCount == count ? S_OK : S_FALSE; + } +} + +HRESULT DirectShowMediaTypeList::skipMediaType(int token, int *index, ULONG count) +{ + if (m_mediaTypeToken != token) { + return VFW_E_ENUM_OUT_OF_SYNC; + } else { + *index = qMin(*index + count, m_mediaTypes.size()); + + return *index < m_mediaTypes.size() ? S_OK : S_FALSE; + } +} + +HRESULT DirectShowMediaTypeList::cloneMediaType(int token, int index, IEnumMediaTypes **enumeration) +{ + if (m_mediaTypeToken != token) { + return VFW_E_ENUM_OUT_OF_SYNC; + } else { + *enumeration = new DirectShowMediaTypeEnum(this, token, index); + + return S_OK; + } +} + diff --git a/src/plugins/directshow/player/directshowmediatypelist.h b/src/plugins/directshow/player/directshowmediatypelist.h new file mode 100644 index 000000000..1b33606cb --- /dev/null +++ b/src/plugins/directshow/player/directshowmediatypelist.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWMEDIATYPELIST_H +#define DIRECTSHOWMEDIATYPELIST_H + +#include + +#include + +class DirectShowMediaTypeList : public IUnknown +{ +public: + DirectShowMediaTypeList(); + + IEnumMediaTypes *createMediaTypeEnum(); + + void setMediaTypes(const QVector &types); + + virtual int currentMediaTypeToken(); + virtual HRESULT nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount); + virtual HRESULT skipMediaType(int token, int *index, ULONG count); + virtual HRESULT cloneMediaType(int token, int index, IEnumMediaTypes **enumeration); + +private: + int m_mediaTypeToken; + QVector m_mediaTypes; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.cpp b/src/plugins/directshow/player/directshowmetadatacontrol.cpp new file mode 100644 index 000000000..40c3920b4 --- /dev/null +++ b/src/plugins/directshow/player/directshowmetadatacontrol.cpp @@ -0,0 +1,352 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "directshowmetadatacontrol.h" + +#include "directshowplayerservice.h" + +#include + +#ifndef QT_NO_WMSDK +namespace +{ + struct QWMMetaDataKeyLookup + { + QtMultimediaKit::MetaData key; + const wchar_t *token; + }; +} + +static const QWMMetaDataKeyLookup qt_wmMetaDataKeys[] = +{ + { QtMultimediaKit::Title, L"Title" }, + { QtMultimediaKit::SubTitle, L"WM/SubTitle" }, + { QtMultimediaKit::Author, L"Author" }, + { QtMultimediaKit::Comment, L"Comment" }, + { QtMultimediaKit::Description, L"Description" }, + { QtMultimediaKit::Category, L"WM/Category" }, + { QtMultimediaKit::Genre, L"WM/Genre" }, + //{ QtMultimediaKit::Date, 0 }, + { QtMultimediaKit::Year, L"WM/Year" }, + { QtMultimediaKit::UserRating, L"UserRating" }, + //{ QtMultimediaKit::MetaDatawords, 0 }, + { QtMultimediaKit::Language, L"Language" }, + { QtMultimediaKit::Publisher, L"WM/Publisher" }, + { QtMultimediaKit::Copyright, L"Copyright" }, + { QtMultimediaKit::ParentalRating, L"ParentalRating" }, + { QtMultimediaKit::RatingOrganisation, L"RatingOrganisation" }, + + // Media + { QtMultimediaKit::Size, L"FileSize" }, + { QtMultimediaKit::MediaType, L"MediaType" }, + { QtMultimediaKit::Duration, L"Duration" }, + + // Audio + { QtMultimediaKit::AudioBitRate, L"AudioBitRate" }, + { QtMultimediaKit::AudioCodec, L"AudioCodec" }, + { QtMultimediaKit::ChannelCount, L"ChannelCount" }, + { QtMultimediaKit::SampleRate, L"Frequency" }, + + // Music + { QtMultimediaKit::AlbumTitle, L"WM/AlbumTitle" }, + { QtMultimediaKit::AlbumArtist, L"WM/AlbumArtist" }, + { QtMultimediaKit::ContributingArtist, L"Author" }, + { QtMultimediaKit::Composer, L"WM/Composer" }, + { QtMultimediaKit::Conductor, L"WM/Conductor" }, + { QtMultimediaKit::Lyrics, L"WM/Lyrics" }, + { QtMultimediaKit::Mood, L"WM/Mood" }, + { QtMultimediaKit::TrackNumber, L"WM/TrackNumber" }, + //{ QtMultimediaKit::TrackCount, 0 }, + //{ QtMultimediaKit::CoverArtUriSmall, 0 }, + //{ QtMultimediaKit::CoverArtUriLarge, 0 }, + + // Image/Video + //{ QtMultimediaKit::Resolution, 0 }, + //{ QtMultimediaKit::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimediaKit::FrameRate, 0 }, + { QtMultimediaKit::VideoBitRate, L"VideoBitRate" }, + { QtMultimediaKit::VideoCodec, L"VideoCodec" }, + + //{ QtMultimediaKit::PosterUri, 0 }, + + // Movie + { QtMultimediaKit::ChapterNumber, L"ChapterNumber" }, + { QtMultimediaKit::Director, L"WM/Director" }, + { QtMultimediaKit::LeadPerformer, L"LeadPerformer" }, + { QtMultimediaKit::Writer, L"WM/Writer" }, + + // Photos + { QtMultimediaKit::CameraManufacturer, L"CameraManufacturer" }, + { QtMultimediaKit::CameraModel, L"CameraModel" }, + { QtMultimediaKit::Event, L"Event" }, + { QtMultimediaKit::Subject, L"Subject" } +}; + +static QVariant getValue(IWMHeaderInfo *header, const wchar_t *key) +{ + WORD streamNumber = 0; + WMT_ATTR_DATATYPE type = WMT_TYPE_DWORD; + WORD size = 0; + + if (header->GetAttributeByName(&streamNumber, key, &type, 0, &size) == S_OK) { + switch (type) { + case WMT_TYPE_DWORD: + if (size == sizeof(DWORD)) { + DWORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast(&word), + &size) == S_OK) { + return int(word); + } + } + break; + case WMT_TYPE_STRING: + { + QString string; + string.resize(size / 2 - 1); + + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast(const_cast(string.utf16())), + &size) == S_OK) { + return string; + } + } + break; + case WMT_TYPE_BINARY: + { + QByteArray bytes; + bytes.resize(size); + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast(bytes.data()), + &size) == S_OK) { + return bytes; + } + } + break; + case WMT_TYPE_BOOL: + if (size == sizeof(DWORD)) { + DWORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast(&word), + &size) == S_OK) { + return bool(word); + } + } + break; + case WMT_TYPE_QWORD: + if (size == sizeof(QWORD)) { + QWORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast(&word), + &size) == S_OK) { + return qint64(word); + } + } + break; + case WMT_TYPE_WORD: + if (size == sizeof(WORD)){ + WORD word; + if (header->GetAttributeByName( + &streamNumber, + key, + &type, + reinterpret_cast(&word), + &size) == S_OK) { + return short(word); + } + } + break; + case WMT_TYPE_GUID: + if (size == 16) { + } + break; + default: + break; + } + } + return QVariant(); +} +#endif + +DirectShowMetaDataControl::DirectShowMetaDataControl(QObject *parent) + : QMetaDataReaderControl(parent) + , m_content(0) +#ifndef QT_NO_WMSDK + , m_headerInfo(0) +#endif +{ +} + +DirectShowMetaDataControl::~DirectShowMetaDataControl() +{ +} + +bool DirectShowMetaDataControl::isMetaDataAvailable() const +{ +#ifndef QT_NO_WMSDK + return m_content || m_headerInfo; +#else + return m_content; +#endif +} + +QVariant DirectShowMetaDataControl::metaData(QtMultimediaKit::MetaData key) const +{ + QVariant value; + +#ifndef QT_NO_WMSDK + if (m_headerInfo) { + static const int count = sizeof(qt_wmMetaDataKeys) / sizeof(QWMMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + if (qt_wmMetaDataKeys[i].key == key) { + value = getValue(m_headerInfo, qt_wmMetaDataKeys[i].token); + break; + } + } + } else if (m_content) { +#else + if (m_content) { +#endif + BSTR string = 0; + + switch (key) { + case QtMultimediaKit::Author: + m_content->get_AuthorName(&string); + break; + case QtMultimediaKit::Title: + m_content->get_Title(&string); + break; + case QtMultimediaKit::ParentalRating: + m_content->get_Rating(&string); + break; + case QtMultimediaKit::Description: + m_content->get_Description(&string); + break; + case QtMultimediaKit::Copyright: + m_content->get_Copyright(&string); + break; + default: + break; + } + + if (string) { + value = QString::fromUtf16(reinterpret_cast(string), ::SysStringLen(string)); + + ::SysFreeString(string); + } + } + return value; +} + +QList DirectShowMetaDataControl::availableMetaData() const +{ + return QList(); +} + +QVariant DirectShowMetaDataControl::extendedMetaData(const QString &) const +{ + return QVariant(); +} + +QStringList DirectShowMetaDataControl::availableExtendedMetaData() const +{ + return QStringList(); +} + +void DirectShowMetaDataControl::updateGraph(IFilterGraph2 *graph, IBaseFilter *source) +{ + if (m_content) + m_content->Release(); + + if (!graph || graph->QueryInterface( + IID_IAMMediaContent, reinterpret_cast(&m_content)) != S_OK) { + m_content = 0; + } + +#ifdef QT_NO_WMSDK + Q_UNUSED(source); +#else + if (m_headerInfo) + m_headerInfo->Release(); + + m_headerInfo = com_cast(source, IID_IWMHeaderInfo); +#endif + // DirectShowMediaPlayerService holds a lock at this point so defer emitting signals to a later + // time. + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(MetaDataChanged))); +} + +void DirectShowMetaDataControl::customEvent(QEvent *event) +{ + if (event->type() == QEvent::Type(MetaDataChanged)) { + event->accept(); + + emit metaDataChanged(); +#ifndef QT_NO_WMSDK + emit metaDataAvailableChanged(m_content || m_headerInfo); +#else + emit metaDataAvailableChanged(m_content); +#endif + } else { + QMetaDataReaderControl::customEvent(event); + } +} diff --git a/src/plugins/directshow/player/directshowmetadatacontrol.h b/src/plugins/directshow/player/directshowmetadatacontrol.h new file mode 100644 index 000000000..6826d783d --- /dev/null +++ b/src/plugins/directshow/player/directshowmetadatacontrol.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWMETADATACONTROL_H +#define DIRECTSHOWMETADATACONTROL_H + +#include + +#include "directshowglobal.h" + +#include + +#ifndef QT_NO_WMSDK +#include +#endif + +#include + +class DirectShowPlayerService; + +QT_USE_NAMESPACE + +class DirectShowMetaDataControl : public QMetaDataReaderControl +{ + Q_OBJECT +public: + DirectShowMetaDataControl(QObject *parent = 0); + ~DirectShowMetaDataControl(); + + bool isMetaDataAvailable() const; + + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const; + QStringList availableExtendedMetaData() const; + + void updateGraph(IFilterGraph2 *graph, IBaseFilter *source); + +protected: + void customEvent(QEvent *event); + +private: + enum Event + { + MetaDataChanged = QEvent::User + }; + + IAMMediaContent *m_content; +#ifndef QT_NO_WMSDK + IWMHeaderInfo *m_headerInfo; +#endif +}; + +#endif diff --git a/src/plugins/directshow/player/directshowpinenum.cpp b/src/plugins/directshow/player/directshowpinenum.cpp new file mode 100644 index 000000000..28093eaee --- /dev/null +++ b/src/plugins/directshow/player/directshowpinenum.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowpinenum.h" + + +DirectShowPinEnum::DirectShowPinEnum(const QList &pins) + : m_ref(1) + , m_pins(pins) + , m_index(0) +{ + foreach (IPin *pin, m_pins) + pin->AddRef(); +} + +DirectShowPinEnum::~DirectShowPinEnum() +{ + foreach (IPin *pin, m_pins) + pin->Release(); +} + +HRESULT DirectShowPinEnum::QueryInterface(REFIID riid, void **ppvObject) +{ + if (riid == IID_IUnknown + || riid == IID_IEnumPins) { + AddRef(); + + *ppvObject = static_cast(this); + + return S_OK; + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } +} + +ULONG DirectShowPinEnum::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG DirectShowPinEnum::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + if (ref == 0) { + delete this; + } + + return ref; +} + +HRESULT DirectShowPinEnum::Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched) +{ + if (ppPins && (pcFetched || cPins == 1)) { + ULONG count = qBound(0, cPins, m_pins.count() - m_index); + + for (ULONG i = 0; i < count; ++i, ++m_index) { + ppPins[i] = m_pins.at(m_index); + ppPins[i]->AddRef(); + } + + if (pcFetched) + *pcFetched = count; + + return count == cPins ? S_OK : S_FALSE; + } else { + return E_POINTER; + } +} + +HRESULT DirectShowPinEnum::Skip(ULONG cPins) +{ + m_index = qMin(int(m_index + cPins), m_pins.count()); + + return m_index < m_pins.count() ? S_OK : S_FALSE; +} + +HRESULT DirectShowPinEnum::Reset() +{ + m_index = 0; + + return S_OK; +} + +HRESULT DirectShowPinEnum::Clone(IEnumPins **ppEnum) +{ + if (ppEnum) { + *ppEnum = new DirectShowPinEnum(m_pins); + + return S_OK; + } else { + return E_POINTER; + } +} diff --git a/src/plugins/directshow/player/directshowpinenum.h b/src/plugins/directshow/player/directshowpinenum.h new file mode 100644 index 000000000..9fba3e84e --- /dev/null +++ b/src/plugins/directshow/player/directshowpinenum.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWPINENUM_H +#define DIRECTSHOWPINENUM_H + +#include + +#include + +class DirectShowPinEnum : public IEnumPins +{ +public: + DirectShowPinEnum(const QList &pins); + ~DirectShowPinEnum(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IEnumPins + HRESULT STDMETHODCALLTYPE Next(ULONG cPins, IPin **ppPins, ULONG *pcFetched); + HRESULT STDMETHODCALLTYPE Skip(ULONG cPins); + HRESULT STDMETHODCALLTYPE Reset(); + HRESULT STDMETHODCALLTYPE Clone(IEnumPins **ppEnum); + +private: + LONG m_ref; + QList m_pins; + int m_index; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowplayercontrol.cpp b/src/plugins/directshow/player/directshowplayercontrol.cpp new file mode 100644 index 000000000..9035a8b21 --- /dev/null +++ b/src/plugins/directshow/player/directshowplayercontrol.cpp @@ -0,0 +1,405 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowplayercontrol.h" + +#include "directshowplayerservice.h" + +#include +#include + +static int volumeToDecibels(int volume) +{ + if (volume == 0) { + return -10000; + } else if (volume == 100) { + return 0; +#ifdef QT_USE_MATH_H_FLOATS + } else if (sizeof(qreal) == sizeof(float)) { + return qRound(::log10f(float(volume) / 100) * 5000); +#endif + } else { + return qRound(::log10(qreal(volume) / 100) * 5000); + } +} + +static int decibelsToVolume(int dB) +{ + if (dB == -10000) { + return 0; + } else if (dB == 0) { + return 100; + } else { + return qRound(100 * qPow(10, qreal(dB) / 5000)); + } +} + +DirectShowPlayerControl::DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent) + : QMediaPlayerControl(parent) + , m_service(service) + , m_audio(0) + , m_updateProperties(0) + , m_state(QMediaPlayer::StoppedState) + , m_status(QMediaPlayer::NoMedia) + , m_error(QMediaPlayer::NoError) + , m_streamTypes(0) + , m_muteVolume(-1) + , m_position(0) + , m_duration(0) + , m_playbackRate(0) + , m_seekable(false) +{ +} + +DirectShowPlayerControl::~DirectShowPlayerControl() +{ + if (m_audio) + m_audio->Release(); +} + +QMediaPlayer::State DirectShowPlayerControl::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus DirectShowPlayerControl::mediaStatus() const +{ + return m_status; +} + +qint64 DirectShowPlayerControl::duration() const +{ + return m_duration; +} + +qint64 DirectShowPlayerControl::position() const +{ + return const_cast(m_position) = m_service->position(); +} + +void DirectShowPlayerControl::setPosition(qint64 position) +{ + m_service->seek(position); +} + +int DirectShowPlayerControl::volume() const +{ + if (m_muteVolume >= 0) { + return m_muteVolume; + } else if (m_audio) { + long dB = 0; + + m_audio->get_Volume(&dB); + + return decibelsToVolume(dB); + } else { + return 0; + } +} + +void DirectShowPlayerControl::setVolume(int volume) +{ + int boundedVolume = qBound(0, volume, 100); + + if (m_muteVolume >= 0) { + m_muteVolume = boundedVolume; + + emit volumeChanged(m_muteVolume); + } else if (m_audio) { + m_audio->put_Volume(volumeToDecibels(volume)); + + emit volumeChanged(boundedVolume); + } +} + +bool DirectShowPlayerControl::isMuted() const +{ + return m_muteVolume >= 0; +} + +void DirectShowPlayerControl::setMuted(bool muted) +{ + if (muted && m_muteVolume < 0) { + if (m_audio) { + long dB = 0; + + m_audio->get_Volume(&dB); + + m_muteVolume = decibelsToVolume(dB); + + m_audio->put_Volume(-10000); + } else { + m_muteVolume = 0; + } + + emit mutedChanged(muted); + } else if (!muted && m_muteVolume >= 0) { + if (m_audio) { + m_audio->put_Volume(volumeToDecibels(m_muteVolume)); + } + m_muteVolume = -1; + + emit mutedChanged(muted); + } +} + +int DirectShowPlayerControl::bufferStatus() const +{ + return m_service->bufferStatus(); +} + +bool DirectShowPlayerControl::isAudioAvailable() const +{ + return m_streamTypes & DirectShowPlayerService::AudioStream; +} + +bool DirectShowPlayerControl::isVideoAvailable() const +{ + return m_streamTypes & DirectShowPlayerService::VideoStream; +} + +bool DirectShowPlayerControl::isSeekable() const +{ + return m_seekable; +} + +QMediaTimeRange DirectShowPlayerControl::availablePlaybackRanges() const +{ + return m_service->availablePlaybackRanges(); +} + +qreal DirectShowPlayerControl::playbackRate() const +{ + return m_playbackRate; +} + +void DirectShowPlayerControl::setPlaybackRate(qreal rate) +{ + if (m_playbackRate != rate) { + m_service->setRate(rate); + + emit playbackRateChanged(m_playbackRate = rate); + } +} + +QMediaContent DirectShowPlayerControl::media() const +{ + return m_media; +} + +const QIODevice *DirectShowPlayerControl::mediaStream() const +{ + return m_stream; +} + +void DirectShowPlayerControl::setMedia(const QMediaContent &media, QIODevice *stream) +{ + m_media = media; + m_stream = stream; + + m_updateProperties &= PlaybackRateProperty; + + m_service->load(media, stream); + + emit mediaChanged(m_media); + emitPropertyChanges(); +} + +void DirectShowPlayerControl::play() +{ + if (m_status == QMediaPlayer::NoMedia) + return; + if (m_status == QMediaPlayer::InvalidMedia) { + setMedia(m_media, m_stream); + if (m_error != QMediaPlayer::NoError) + return; + } + m_service->play(); + emit stateChanged(m_state = QMediaPlayer::PlayingState); +} + +void DirectShowPlayerControl::pause() +{ + if (m_status == QMediaPlayer::NoMedia) + return; + if (m_status == QMediaPlayer::InvalidMedia) { + setMedia(m_media, m_stream); + if (m_error != QMediaPlayer::NoError) + return; + } + m_service->pause(); + emit stateChanged(m_state = QMediaPlayer::PausedState); +} + +void DirectShowPlayerControl::stop() +{ + m_service->stop(); + emit stateChanged(m_state = QMediaPlayer::StoppedState); +} + +void DirectShowPlayerControl::customEvent(QEvent *event) +{ + if (event->type() == QEvent::Type(PropertiesChanged)) { + emitPropertyChanges(); + + event->accept(); + } else { + QMediaPlayerControl::customEvent(event); + } +} + +void DirectShowPlayerControl::emitPropertyChanges() +{ + int properties = m_updateProperties; + m_updateProperties = 0; + + if ((properties & ErrorProperty) && m_error != QMediaPlayer::NoError) + emit error(m_error, m_errorString); + + if (properties & PlaybackRateProperty) + emit playbackRateChanged(m_playbackRate); + + if (properties & StreamTypesProperty) { + emit audioAvailableChanged(m_streamTypes & DirectShowPlayerService::AudioStream); + emit videoAvailableChanged(m_streamTypes & DirectShowPlayerService::VideoStream); + } + + if (properties & PositionProperty) + emit positionChanged(m_position); + + if (properties & DurationProperty) + emit durationChanged(m_duration); + + if (properties & SeekableProperty) + emit seekableChanged(m_seekable); + + if (properties & StatusProperty) + emit mediaStatusChanged(m_status); + + if (properties & StateProperty) + emit stateChanged(m_state); +} + +void DirectShowPlayerControl::scheduleUpdate(int properties) +{ + if (m_updateProperties == 0) + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(PropertiesChanged))); + + m_updateProperties |= properties; +} + +void DirectShowPlayerControl::updateState(QMediaPlayer::State state) +{ + if (m_state != state) { + m_state = state; + + scheduleUpdate(StateProperty); + } +} + +void DirectShowPlayerControl::updateStatus(QMediaPlayer::MediaStatus status) +{ + if (m_status != status) { + m_status = status; + + scheduleUpdate(StatusProperty); + } +} + +void DirectShowPlayerControl::updateMediaInfo(qint64 duration, int streamTypes, bool seekable) +{ + int properties = 0; + + if (m_duration != duration) { + m_duration = duration; + + properties |= DurationProperty; + } + if (m_streamTypes != streamTypes) { + m_streamTypes = streamTypes; + + properties |= StreamTypesProperty; + } + + if (m_seekable != seekable) { + m_seekable = seekable; + + properties |= SeekableProperty; + } + + if (properties != 0) + scheduleUpdate(properties); +} + +void DirectShowPlayerControl::updatePlaybackRate(qreal rate) +{ + if (m_playbackRate != rate) { + m_playbackRate = rate; + + scheduleUpdate(PlaybackRateProperty); + } +} + +void DirectShowPlayerControl::updateAudioOutput(IBaseFilter *filter) +{ + if (m_audio) + m_audio->Release(); + + m_audio = com_cast(filter, IID_IBasicAudio); +} + +void DirectShowPlayerControl::updateError(QMediaPlayer::Error error, const QString &errorString) +{ + m_error = error; + m_errorString = errorString; + + if (m_error != QMediaPlayer::NoError) + scheduleUpdate(ErrorProperty); +} + +void DirectShowPlayerControl::updatePosition(qint64 position) +{ + if (m_position != position) { + m_position = position; + + scheduleUpdate(PositionProperty); + } +} diff --git a/src/plugins/directshow/player/directshowplayercontrol.h b/src/plugins/directshow/player/directshowplayercontrol.h new file mode 100644 index 000000000..2c1cd6ad7 --- /dev/null +++ b/src/plugins/directshow/player/directshowplayercontrol.h @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWPLAYERCONTROL_H +#define DIRECTSHOWPLAYERCONTROL_H + +#include "qmediacontent.h" +#include "qmediaplayercontrol.h" + +#include + +#include "directshowplayerservice.h" + +QT_USE_NAMESPACE + +class DirectShowPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT +public: + DirectShowPlayerControl(DirectShowPlayerService *service, QObject *parent = 0); + ~DirectShowPlayerControl(); + + QMediaPlayer::State state() const; + + QMediaPlayer::MediaStatus mediaStatus() const; + + qint64 duration() const; + + qint64 position() const; + void setPosition(qint64 position); + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + int bufferStatus() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &media, QIODevice *stream); + + void play(); + void pause(); + void stop(); + + void updateState(QMediaPlayer::State state); + void updateStatus(QMediaPlayer::MediaStatus status); + void updateMediaInfo(qint64 duration, int streamTypes, bool seekable); + void updatePlaybackRate(qreal rate); + void updateAudioOutput(IBaseFilter *filter); + void updateError(QMediaPlayer::Error error, const QString &errorString); + void updatePosition(qint64 position); + +protected: + void customEvent(QEvent *event); + +private: + enum Properties + { + StateProperty = 0x01, + StatusProperty = 0x02, + StreamTypesProperty = 0x04, + DurationProperty = 0x08, + PlaybackRateProperty = 0x10, + SeekableProperty = 0x20, + ErrorProperty = 0x40, + PositionProperty = 0x80 + }; + + enum Event + { + PropertiesChanged = QEvent::User + }; + + void scheduleUpdate(int properties); + void emitPropertyChanges(); + + DirectShowPlayerService *m_service; + IBasicAudio *m_audio; + QIODevice *m_stream; + int m_updateProperties; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_status; + QMediaPlayer::Error m_error; + int m_streamTypes; + int m_muteVolume; + qint64 m_position; + qint64 m_duration; + qreal m_playbackRate; + bool m_seekable; + QMediaContent m_media; + QString m_errorString; + +}; + +#endif diff --git a/src/plugins/directshow/player/directshowplayerservice.cpp b/src/plugins/directshow/player/directshowplayerservice.cpp new file mode 100644 index 000000000..ac93f592c --- /dev/null +++ b/src/plugins/directshow/player/directshowplayerservice.cpp @@ -0,0 +1,1408 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowplayerservice.h" + +#include "directshowaudioendpointcontrol.h" +#include "directshowiosource.h" +#include "directshowmetadatacontrol.h" +#include "directshowplayercontrol.h" +#include "directshowvideorenderercontrol.h" +#ifndef Q_WS_SIMULATOR +#include "vmr9videowindowcontrol.h" +#endif + +#include "qmediacontent.h" + +#include +#include +#include +#include + +Q_GLOBAL_STATIC(DirectShowEventLoop, qt_directShowEventLoop) + + +// QMediaPlayer uses millisecond time units, direct show uses 100 nanosecond units. +static const int qt_directShowTimeScale = 10000; + +class DirectShowPlayerServiceThread : public QThread +{ +public: + DirectShowPlayerServiceThread(DirectShowPlayerService *service) + : m_service(service) + { + } + +protected: + void run() { m_service->run(); } + +private: + DirectShowPlayerService *m_service; +}; + +DirectShowPlayerService::DirectShowPlayerService(QObject *parent) + : QMediaService(parent) + , m_playerControl(0) + , m_metaDataControl(0) + , m_videoRendererControl(0) +#ifndef Q_WS_SIMULATOR + , m_videoWindowControl(0) +#endif + , m_audioEndpointControl(0) + , m_taskThread(0) + , m_loop(qt_directShowEventLoop()) + , m_pendingTasks(0) + , m_executingTask(0) + , m_executedTasks(0) + , m_taskHandle(::CreateEvent(0, 0, 0, 0)) + , m_eventHandle(0) + , m_graphStatus(NoMedia) + , m_stream(0) + , m_graph(0) + , m_source(0) + , m_audioOutput(0) + , m_videoOutput(0) + , m_rate(1.0) + , m_position(0) + , m_duration(0) + , m_buffering(false) + , m_seekable(false) + , m_atEnd(false) +{ + CoInitialize(NULL); + m_playerControl = new DirectShowPlayerControl(this); + m_metaDataControl = new DirectShowMetaDataControl(this); + m_audioEndpointControl = new DirectShowAudioEndpointControl(this); + + m_taskThread = new DirectShowPlayerServiceThread(this); + m_taskThread->start(); +} + +DirectShowPlayerService::~DirectShowPlayerService() +{ + { + QMutexLocker locker(&m_mutex); + + releaseGraph(); + + m_pendingTasks = Shutdown; + ::SetEvent(m_taskHandle); + } + + m_taskThread->wait(); + delete m_taskThread; + + if (m_audioOutput) { + m_audioOutput->Release(); + m_audioOutput = 0; + } + + if (m_videoOutput) { + m_videoOutput->Release(); + m_videoOutput = 0; + } + + delete m_playerControl; + delete m_audioEndpointControl; + delete m_metaDataControl; + delete m_videoRendererControl; +#ifndef Q_WS_SIMULATOR + delete m_videoWindowControl; +#endif + + ::CloseHandle(m_taskHandle); + CoUninitialize(); +} + +QMediaControl *DirectShowPlayerService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) { + return m_playerControl; + } else if (qstrcmp(name, QAudioEndpointSelector_iid) == 0) { + return m_audioEndpointControl; + } else if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) { + return m_metaDataControl; + } else if (qstrcmp(name, QVideoRendererControl_iid) == 0) { +#ifndef Q_WS_SIMULATOR + if (!m_videoRendererControl && !m_videoWindowControl) { +#else + if (!m_videoRendererControl) { +#endif + m_videoRendererControl = new DirectShowVideoRendererControl(m_loop); + + connect(m_videoRendererControl, SIGNAL(filterChanged()), + this, SLOT(videoOutputChanged())); + + return m_videoRendererControl; + } +#ifndef Q_WS_SIMULATOR + } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + if (!m_videoRendererControl && !m_videoWindowControl) { + m_videoWindowControl = new Vmr9VideoWindowControl; + + setVideoOutput(m_videoWindowControl->filter()); + + return m_videoWindowControl; + } +#endif + } + return 0; +} + +void DirectShowPlayerService::releaseControl(QMediaControl *control) +{ + if (!control) { + qWarning("QMediaService::releaseControl():" + " Attempted release of null control"); + } else if (control == m_videoRendererControl) { + setVideoOutput(0); + + delete m_videoRendererControl; + + m_videoRendererControl = 0; +#ifndef Q_WS_SIMULATOR + } else if (control == m_videoWindowControl) { + setVideoOutput(0); + + delete m_videoWindowControl; + + m_videoWindowControl = 0; +#endif + } +} + +void DirectShowPlayerService::load(const QMediaContent &media, QIODevice *stream) +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks = 0; + + if (m_graph) + releaseGraph(); + + m_resources = media.resources(); + m_stream = stream; + m_error = QMediaPlayer::NoError; + m_errorString = QString(); + m_position = 0; + m_duration = 0; + m_streamTypes = 0; + m_executedTasks = 0; + m_buffering = false; + m_seekable = false; + m_atEnd = false; + m_metaDataControl->updateGraph(0, 0); + + if (m_resources.isEmpty() && !stream) { + m_pendingTasks = 0; + m_graphStatus = NoMedia; + + m_url.clear(); + } else if (stream && (!stream->isReadable() || stream->isSequential())) { + m_pendingTasks = 0; + m_graphStatus = InvalidMedia; + m_error = QMediaPlayer::ResourceError; + } else { + // {36b73882-c2c8-11cf-8b46-00805f6cef60} + static const GUID iid_IFilterGraph2 = { + 0x36b73882, 0xc2c8, 0x11cf, {0x8b, 0x46, 0x00, 0x80, 0x5f, 0x6c, 0xef, 0x60} }; + m_graphStatus = Loading; + + m_graph = com_new(CLSID_FilterGraph, iid_IFilterGraph2); + + if (stream) + m_pendingTasks = SetStreamSource; + else + m_pendingTasks = SetUrlSource; + + ::SetEvent(m_taskHandle); + } + + m_playerControl->updateError(m_error, m_errorString); + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + m_playerControl->updateState(QMediaPlayer::StoppedState); + m_playerControl->updatePosition(m_position); + updateStatus(); +} + +void DirectShowPlayerService::doSetUrlSource(QMutexLocker *locker) +{ + IBaseFilter *source = 0; + + QMediaResource resource = m_resources.takeFirst(); + QUrl url = resource.url(); + + HRESULT hr = E_FAIL; + + if (url.scheme() == QLatin1String("http") || url.scheme() == QLatin1String("https")) { + static const GUID clsid_WMAsfReader = { + 0x187463a0, 0x5bb7, 0x11d3, {0xac, 0xbe, 0x00, 0x80, 0xc7, 0x5e, 0x24, 0x6e} }; + + // {56a868a6-0ad4-11ce-b03a-0020af0ba770} + static const GUID iid_IFileSourceFilter = { + 0x56a868a6, 0x0ad4, 0x11ce, {0xb0, 0x3a, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} }; + + if (IFileSourceFilter *fileSource = com_new( + clsid_WMAsfReader, iid_IFileSourceFilter)) { + locker->unlock(); + hr = fileSource->Load(reinterpret_cast(url.toString().utf16()), 0); + + if (SUCCEEDED(hr)) { + source = com_cast(fileSource, IID_IBaseFilter); + + if (!SUCCEEDED(hr = m_graph->AddFilter(source, L"Source")) && source) { + source->Release(); + source = 0; + } + } + fileSource->Release(); + locker->relock(); + } + } else if (url.scheme() == QLatin1String("qrc")) { + DirectShowRcSource *rcSource = new DirectShowRcSource(m_loop); + + locker->unlock(); + if (rcSource->open(url) && SUCCEEDED(hr = m_graph->AddFilter(rcSource, L"Source"))) + source = rcSource; + else + rcSource->Release(); + locker->relock(); + } + + if (!SUCCEEDED(hr)) { + locker->unlock(); + hr = m_graph->AddSourceFilter( + reinterpret_cast(url.toString().utf16()), L"Source", &source); + locker->relock(); + } + + if (SUCCEEDED(hr)) { + m_executedTasks = SetSource; + m_pendingTasks |= Render; + + if (m_audioOutput) + m_pendingTasks |= SetAudioOutput; + if (m_videoOutput) + m_pendingTasks |= SetVideoOutput; + + if (m_rate != 1.0) + m_pendingTasks |= SetRate; + + m_source = source; + } else if (!m_resources.isEmpty()) { + m_pendingTasks |= SetUrlSource; + } else { + m_pendingTasks = 0; + m_graphStatus = InvalidMedia; + + switch (hr) { + case VFW_E_UNKNOWN_FILE_TYPE: + m_error = QMediaPlayer::FormatError; + m_errorString = QString(); + break; + case E_OUTOFMEMORY: + case VFW_E_CANNOT_LOAD_SOURCE_FILTER: + case VFW_E_NOT_FOUND: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + break; + default: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doSetUrlSource: Unresolved error code %x", uint(hr)); + break; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } +} + +void DirectShowPlayerService::doSetStreamSource(QMutexLocker *locker) +{ + DirectShowIOSource *source = new DirectShowIOSource(m_loop); + source->setDevice(m_stream); + + if (SUCCEEDED(m_graph->AddFilter(source, L"Source"))) { + m_executedTasks = SetSource; + m_pendingTasks |= Render; + + if (m_audioOutput) + m_pendingTasks |= SetAudioOutput; + if (m_videoOutput) + m_pendingTasks |= SetVideoOutput; + + if (m_rate != 1.0) + m_pendingTasks |= SetRate; + + m_source = source; + } else { + source->Release(); + + m_pendingTasks = 0; + m_graphStatus = InvalidMedia; + + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } +} + +void DirectShowPlayerService::doRender(QMutexLocker *locker) +{ + m_pendingTasks |= m_executedTasks & (Play | Pause); + + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + if (m_pendingTasks & SetAudioOutput) { + m_graph->AddFilter(m_audioOutput, L"AudioOutput"); + + m_pendingTasks ^= SetAudioOutput; + m_executedTasks |= SetAudioOutput; + } + if (m_pendingTasks & SetVideoOutput) { + m_graph->AddFilter(m_videoOutput, L"VideoOutput"); + + m_pendingTasks ^= SetVideoOutput; + m_executedTasks |= SetVideoOutput; + } + + IFilterGraph2 *graph = m_graph; + graph->AddRef(); + + QVarLengthArray filters; + m_source->AddRef(); + filters.append(m_source); + + bool rendered = false; + + HRESULT renderHr = S_OK; + + while (!filters.isEmpty()) { + IEnumPins *pins = 0; + IBaseFilter *filter = filters[filters.size() - 1]; + filters.removeLast(); + + if (!(m_pendingTasks & ReleaseFilters) && SUCCEEDED(filter->EnumPins(&pins))) { + int outputs = 0; + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION direction; + if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) { + ++outputs; + + IPin *peer = 0; + if (pin->ConnectedTo(&peer) == S_OK) { + PIN_INFO peerInfo; + if (SUCCEEDED(peer->QueryPinInfo(&peerInfo))) + filters.append(peerInfo.pFilter); + peer->Release(); + } else { + locker->unlock(); + HRESULT hr; + if (SUCCEEDED(hr = graph->RenderEx( + pin, /*AM_RENDEREX_RENDERTOEXISTINGRENDERERS*/ 1, 0))) { + rendered = true; + } else if (renderHr == S_OK || renderHr == VFW_E_NO_DECOMPRESSOR){ + renderHr = hr; + } + locker->relock(); + } + } + } + + pins->Release(); + + if (outputs == 0) + rendered = true; + } + filter->Release(); + } + + if (m_audioOutput && !isConnected(m_audioOutput, PINDIR_INPUT)) { + graph->RemoveFilter(m_audioOutput); + + m_executedTasks &= ~SetAudioOutput; + } + + if (m_videoOutput && !isConnected(m_videoOutput, PINDIR_INPUT)) { + graph->RemoveFilter(m_videoOutput); + + m_executedTasks &= ~SetVideoOutput; + } + + graph->Release(); + + if (!(m_pendingTasks & ReleaseFilters)) { + if (rendered) { + if (!(m_executedTasks & FinalizeLoad)) + m_pendingTasks |= FinalizeLoad; + } else { + m_pendingTasks = 0; + + m_graphStatus = InvalidMedia; + + if (!m_audioOutput && !m_videoOutput) { + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + } else { + switch (renderHr) { + case VFW_E_UNSUPPORTED_AUDIO: + case VFW_E_UNSUPPORTED_VIDEO: + case VFW_E_UNSUPPORTED_STREAM: + m_error = QMediaPlayer::FormatError; + m_errorString = QString(); + break; + default: + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doRender: Unresolved error code %x", + uint(renderHr)); + } + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + + m_executedTasks |= Render; + } + + m_loop->wake(); +} + +void DirectShowPlayerService::doFinalizeLoad(QMutexLocker *locker) +{ + if (m_graphStatus != Loaded) { + if (IMediaEvent *event = com_cast(m_graph, IID_IMediaEvent)) { + event->GetEventHandle(reinterpret_cast(&m_eventHandle)); + event->Release(); + } + if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG duration = 0; + seeking->GetDuration(&duration); + m_duration = duration / qt_directShowTimeScale; + + DWORD capabilities = 0; + seeking->GetCapabilities(&capabilities); + m_seekable = capabilities & AM_SEEKING_CanSeekAbsolute; + + seeking->Release(); + } + } + + if ((m_executedTasks & SetOutputs) == SetOutputs) { + m_streamTypes = AudioStream | VideoStream; + } else { + m_streamTypes = findStreamTypes(m_source); + } + + m_executedTasks |= FinalizeLoad; + + m_graphStatus = Loaded; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(FinalizedLoad))); +} + +void DirectShowPlayerService::releaseGraph() +{ + if (m_graph) { + if (m_executingTask != 0) { + // {8E1C39A1-DE53-11cf-AA63-0080C744528D} + static const GUID iid_IAMOpenProgress = { + 0x8E1C39A1, 0xDE53, 0x11cf, {0xAA, 0x63, 0x00, 0x80, 0xC7, 0x44, 0x52, 0x8D} }; + + if (IAMOpenProgress *progress = com_cast( + m_graph, iid_IAMOpenProgress)) { + progress->AbortOperation(); + progress->Release(); + } + m_graph->Abort(); + } + + m_pendingTasks = ReleaseGraph; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } +} + +void DirectShowPlayerService::doReleaseGraph(QMutexLocker *locker) +{ + Q_UNUSED(locker); + + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + if (m_source) { + m_source->Release(); + m_source = 0; + } + + m_eventHandle = 0; + + m_graph->Release(); + m_graph = 0; + + m_loop->wake(); +} + +int DirectShowPlayerService::findStreamTypes(IBaseFilter *source) const +{ + QVarLengthArray filters; + source->AddRef(); + filters.append(source); + + int streamTypes = 0; + + while (!filters.isEmpty()) { + IEnumPins *pins = 0; + IBaseFilter *filter = filters[filters.size() - 1]; + filters.removeLast(); + + if (SUCCEEDED(filter->EnumPins(&pins))) { + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION direction; + if (pin->QueryDirection(&direction) == S_OK && direction == PINDIR_OUTPUT) { + AM_MEDIA_TYPE connectionType; + if (SUCCEEDED(pin->ConnectionMediaType(&connectionType))) { + IPin *peer = 0; + + if (connectionType.majortype == MEDIATYPE_Audio) { + streamTypes |= AudioStream; + } else if (connectionType.majortype == MEDIATYPE_Video) { + streamTypes |= VideoStream; + } else if (SUCCEEDED(pin->ConnectedTo(&peer))) { + PIN_INFO peerInfo; + if (SUCCEEDED(peer->QueryPinInfo(&peerInfo))) + filters.append(peerInfo.pFilter); + peer->Release(); + } + } else { + streamTypes |= findStreamType(pin); + } + } + } + } + filter->Release(); + } + return streamTypes; +} + +int DirectShowPlayerService::findStreamType(IPin *pin) const +{ + IEnumMediaTypes *types; + + if (SUCCEEDED(pin->EnumMediaTypes(&types))) { + bool video = false; + bool audio = false; + bool other = false; + + for (AM_MEDIA_TYPE *type = 0; + types->Next(1, &type, 0) == S_OK; + DirectShowMediaType::deleteType(type)) { + if (type->majortype == MEDIATYPE_Audio) + audio = true; + else if (type->majortype == MEDIATYPE_Video) + video = true; + else + other = true; + } + types->Release(); + + if (other) + return 0; + else if (audio && !video) + return AudioStream; + else if (!audio && video) + return VideoStream; + else + return 0; + } else { + return 0; + } +} + +void DirectShowPlayerService::play() +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks &= ~Pause; + m_pendingTasks |= Play; + + if (m_executedTasks & Render) { + if (m_executedTasks & Stop) { + m_atEnd = false; + m_position = 0; + m_pendingTasks |= Seek; + m_executedTasks ^= Stop; + } + + ::SetEvent(m_taskHandle); + } + + updateStatus(); +} + +void DirectShowPlayerService::doPlay(QMutexLocker *locker) +{ + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + locker->unlock(); + HRESULT hr = control->Run(); + locker->relock(); + + control->Release(); + + if (SUCCEEDED(hr)) { + m_executedTasks |= Play; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + } else { + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doPlay: Unresolved error code %x", uint(hr)); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + } +} + +void DirectShowPlayerService::pause() +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks &= ~Play; + m_pendingTasks |= Pause; + + if (m_executedTasks & Render) { + if (m_executedTasks & Stop) { + m_atEnd = false; + m_position = 0; + m_pendingTasks |= Seek; + m_executedTasks ^= Stop; + } + + ::SetEvent(m_taskHandle); + } + + updateStatus(); +} + +void DirectShowPlayerService::doPause(QMutexLocker *locker) +{ + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + locker->unlock(); + HRESULT hr = control->Pause(); + locker->relock(); + + control->Release(); + + if (SUCCEEDED(hr)) { + if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / qt_directShowTimeScale; + } else { + m_position = 0; + } + + m_executedTasks |= Pause; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + } else { + m_error = QMediaPlayer::ResourceError; + m_errorString = QString(); + qWarning("DirectShowPlayerService::doPause: Unresolved error code %x", uint(hr)); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(Error))); + } + } +} + +void DirectShowPlayerService::stop() +{ + QMutexLocker locker(&m_mutex); + + m_pendingTasks &= ~(Play | Pause | Seek); + + if ((m_executingTask | m_executedTasks) & (Play | Pause | Seek)) { + m_pendingTasks |= Stop; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } + + updateStatus(); +} + +void DirectShowPlayerService::doStop(QMutexLocker *locker) +{ + if (m_executedTasks & (Play | Pause)) { + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + m_position = 0; + m_pendingTasks |= Seek; + + m_executedTasks &= ~(Play | Pause); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + } + + m_executedTasks |= Stop; + + m_loop->wake(); +} + +void DirectShowPlayerService::setRate(qreal rate) +{ + QMutexLocker locker(&m_mutex); + + m_rate = rate; + + m_pendingTasks |= SetRate; + + if (m_executedTasks & FinalizeLoad) + ::SetEvent(m_taskHandle); +} + +void DirectShowPlayerService::doSetRate(QMutexLocker *locker) +{ + if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + // Cache current values as we can't query IMediaSeeking during a seek due to the + // possibility of a deadlock when flushing the VideoSurfaceFilter. + LONGLONG currentPosition = 0; + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / qt_directShowTimeScale; + + LONGLONG minimum = 0; + LONGLONG maximum = 0; + m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum)) + ? QMediaTimeRange(minimum / qt_directShowTimeScale, maximum / qt_directShowTimeScale) + : QMediaTimeRange(); + + locker->unlock(); + HRESULT hr = seeking->SetRate(m_rate); + locker->relock(); + + if (!SUCCEEDED(hr)) { + double rate = 0.0; + m_rate = seeking->GetRate(&rate) + ? rate + : 1.0; + } + + seeking->Release(); + } else if (m_rate != 1.0) { + m_rate = 1.0; + } + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(RateChange))); +} + +qint64 DirectShowPlayerService::position() const +{ + QMutexLocker locker(const_cast(&m_mutex)); + + if (m_graphStatus == Loaded) { + if (m_executingTask == Seek || m_executingTask == SetRate || (m_pendingTasks & Seek)) { + return m_position; + } else if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + const_cast(m_position) = position / qt_directShowTimeScale; + + return m_position; + } + } + return 0; +} + +QMediaTimeRange DirectShowPlayerService::availablePlaybackRanges() const +{ + QMutexLocker locker(const_cast(&m_mutex)); + + if (m_graphStatus == Loaded) { + if (m_executingTask == Seek || m_executingTask == SetRate || (m_pendingTasks & Seek)) { + return m_playbackRange; + } else if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG minimum = 0; + LONGLONG maximum = 0; + + HRESULT hr = seeking->GetAvailable(&minimum, &maximum); + seeking->Release(); + + if (SUCCEEDED(hr)) + return QMediaTimeRange(minimum, maximum); + } + } + return QMediaTimeRange(); +} + +void DirectShowPlayerService::seek(qint64 position) +{ + QMutexLocker locker(&m_mutex); + + m_position = position; + + m_pendingTasks |= Seek; + + if (m_executedTasks & FinalizeLoad) + ::SetEvent(m_taskHandle); +} + +void DirectShowPlayerService::doSeek(QMutexLocker *locker) +{ + if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG seekPosition = LONGLONG(m_position) * qt_directShowTimeScale; + + // Cache current values as we can't query IMediaSeeking during a seek due to the + // possibility of a deadlock when flushing the VideoSurfaceFilter. + LONGLONG currentPosition = 0; + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / qt_directShowTimeScale; + + LONGLONG minimum = 0; + LONGLONG maximum = 0; + m_playbackRange = SUCCEEDED(seeking->GetAvailable(&minimum, &maximum)) + ? QMediaTimeRange( + minimum / qt_directShowTimeScale, maximum / qt_directShowTimeScale) + : QMediaTimeRange(); + + locker->unlock(); + seeking->SetPositions( + &seekPosition, AM_SEEKING_AbsolutePositioning, 0, AM_SEEKING_NoPositioning); + locker->relock(); + + seeking->GetCurrentPosition(¤tPosition); + m_position = currentPosition / qt_directShowTimeScale; + + seeking->Release(); + } else { + m_position = 0; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(PositionChange))); +} + +int DirectShowPlayerService::bufferStatus() const +{ +#ifndef QT_NO_WMSDK + QMutexLocker locker(const_cast(&m_mutex)); + + if (IWMReaderAdvanced2 *reader = com_cast( + m_source, IID_IWMReaderAdvanced2)) { + DWORD percentage = 0; + + reader->GetBufferProgress(&percentage, 0); + reader->Release(); + + return percentage; + } else { + return 0; + } +#else + return 0; +#endif +} + +void DirectShowPlayerService::setAudioOutput(IBaseFilter *filter) +{ + QMutexLocker locker(&m_mutex); + + if (m_graph) { + if (m_audioOutput) { + if (m_executedTasks & SetAudioOutput) { + m_pendingTasks |= ReleaseAudioOutput; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } + m_audioOutput->Release(); + } + + m_audioOutput = filter; + + if (m_audioOutput) { + m_audioOutput->AddRef(); + + m_pendingTasks |= SetAudioOutput; + + if (m_executedTasks & SetSource) { + m_pendingTasks |= Render; + + ::SetEvent(m_taskHandle); + } + } else { + m_pendingTasks &= ~ SetAudioOutput; + } + } else { + if (m_audioOutput) + m_audioOutput->Release(); + + m_audioOutput = filter; + + if (m_audioOutput) + m_audioOutput->AddRef(); + } + + m_playerControl->updateAudioOutput(m_audioOutput); +} + +void DirectShowPlayerService::doReleaseAudioOutput(QMutexLocker *locker) +{ + m_pendingTasks |= m_executedTasks & (Play | Pause); + + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + IBaseFilter *decoder = getConnected(m_audioOutput, PINDIR_INPUT); + if (!decoder) { + decoder = m_audioOutput; + decoder->AddRef(); + } + + // {DCFBDCF6-0DC2-45f5-9AB2-7C330EA09C29} + static const GUID iid_IFilterChain = { + 0xDCFBDCF6, 0x0DC2, 0x45f5, {0x9A, 0xB2, 0x7C, 0x33, 0x0E, 0xA0, 0x9C, 0x29} }; + + if (IFilterChain *chain = com_cast(m_graph, iid_IFilterChain)) { + chain->RemoveChain(decoder, m_audioOutput); + chain->Release(); + } else { + m_graph->RemoveFilter(m_audioOutput); + } + + decoder->Release(); + + m_executedTasks &= ~SetAudioOutput; + + m_loop->wake(); +} + +void DirectShowPlayerService::setVideoOutput(IBaseFilter *filter) +{ + QMutexLocker locker(&m_mutex); + + if (m_graph) { + if (m_videoOutput) { + if (m_executedTasks & SetVideoOutput) { + m_pendingTasks |= ReleaseVideoOutput; + + ::SetEvent(m_taskHandle); + + m_loop->wait(&m_mutex); + } + m_videoOutput->Release(); + } + + m_videoOutput = filter; + + if (m_videoOutput) { + m_videoOutput->AddRef(); + + m_pendingTasks |= SetVideoOutput; + + if (m_executedTasks & SetSource) { + m_pendingTasks |= Render; + + ::SetEvent(m_taskHandle); + } + } + } else { + if (m_videoOutput) + m_videoOutput->Release(); + + m_videoOutput = filter; + + if (m_videoOutput) + m_videoOutput->AddRef(); + } +} + +void DirectShowPlayerService::doReleaseVideoOutput(QMutexLocker *locker) +{ + m_pendingTasks |= m_executedTasks & (Play | Pause); + + if (IMediaControl *control = com_cast(m_graph, IID_IMediaControl)) { + control->Stop(); + control->Release(); + } + + IBaseFilter *intermediate = 0; + if (!SUCCEEDED(m_graph->FindFilterByName(L"Color Space Converter", &intermediate))) { + intermediate = m_videoOutput; + intermediate->AddRef(); + } + + IBaseFilter *decoder = getConnected(intermediate, PINDIR_INPUT); + if (!decoder) { + decoder = intermediate; + decoder->AddRef(); + } + + // {DCFBDCF6-0DC2-45f5-9AB2-7C330EA09C29} + static const GUID iid_IFilterChain = { + 0xDCFBDCF6, 0x0DC2, 0x45f5, {0x9A, 0xB2, 0x7C, 0x33, 0x0E, 0xA0, 0x9C, 0x29} }; + + if (IFilterChain *chain = com_cast(m_graph, iid_IFilterChain)) { + chain->RemoveChain(decoder, m_videoOutput); + chain->Release(); + } else { + m_graph->RemoveFilter(m_videoOutput); + } + + intermediate->Release(); + decoder->Release(); + + m_executedTasks &= ~SetVideoOutput; + + m_loop->wake(); +} + +void DirectShowPlayerService::customEvent(QEvent *event) +{ + if (event->type() == QEvent::Type(FinalizedLoad)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + m_metaDataControl->updateGraph(m_graph, m_source); + + updateStatus(); + } else if (event->type() == QEvent::Type(Error)) { + QMutexLocker locker(&m_mutex); + + if (m_error != QMediaPlayer::NoError) { + m_playerControl->updateError(m_error, m_errorString); + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + m_playerControl->updateState(QMediaPlayer::StoppedState); + updateStatus(); + } + } else if (event->type() == QEvent::Type(RateChange)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updatePlaybackRate(m_rate); + } else if (event->type() == QEvent::Type(StatusChange)) { + QMutexLocker locker(&m_mutex); + + updateStatus(); + m_playerControl->updatePosition(m_position); + } else if (event->type() == QEvent::Type(DurationChange)) { + QMutexLocker locker(&m_mutex); + + m_playerControl->updateMediaInfo(m_duration, m_streamTypes, m_seekable); + } else if (event->type() == QEvent::Type(EndOfMedia)) { + QMutexLocker locker(&m_mutex); + + if (m_atEnd) { + m_playerControl->updateState(QMediaPlayer::StoppedState); + m_playerControl->updateStatus(QMediaPlayer::EndOfMedia); + m_playerControl->updatePosition(m_position); + } + } else if (event->type() == QEvent::Type(PositionChange)) { + QMutexLocker locker(&m_mutex); + + if (m_playerControl->mediaStatus() == QMediaPlayer::EndOfMedia) + m_playerControl->updateStatus(QMediaPlayer::LoadedMedia); + m_playerControl->updatePosition(m_position); + } else { + QMediaService::customEvent(event); + } +} + +void DirectShowPlayerService::videoOutputChanged() +{ + setVideoOutput(m_videoRendererControl->filter()); +} + +void DirectShowPlayerService::graphEvent(QMutexLocker *locker) +{ + if (IMediaEvent *event = com_cast(m_graph, IID_IMediaEvent)) { + long eventCode; + LONG_PTR param1; + LONG_PTR param2; + + while (event->GetEvent(&eventCode, ¶m1, ¶m2, 0) == S_OK) { + switch (eventCode) { + case EC_BUFFERING_DATA: + m_buffering = param1; + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(StatusChange))); + break; + case EC_COMPLETE: + m_executedTasks &= ~(Play | Pause); + m_executedTasks |= Stop; + + m_buffering = false; + m_atEnd = true; + + if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG position = 0; + + seeking->GetCurrentPosition(&position); + seeking->Release(); + + m_position = position / qt_directShowTimeScale; + } + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(EndOfMedia))); + break; + case EC_LENGTH_CHANGED: + if (IMediaSeeking *seeking = com_cast(m_graph, IID_IMediaSeeking)) { + LONGLONG duration = 0; + seeking->GetDuration(&duration); + m_duration = duration / qt_directShowTimeScale; + + DWORD capabilities = 0; + seeking->GetCapabilities(&capabilities); + m_seekable = capabilities & AM_SEEKING_CanSeekAbsolute; + + seeking->Release(); + + QCoreApplication::postEvent(this, new QEvent(QEvent::Type(DurationChange))); + } + break; + default: + break; + } + + event->FreeEventParams(eventCode, param1, param2); + } + event->Release(); + } +} + +void DirectShowPlayerService::updateStatus() +{ + switch (m_graphStatus) { + case NoMedia: + m_playerControl->updateStatus(QMediaPlayer::NoMedia); + break; + case Loading: + m_playerControl->updateStatus(QMediaPlayer::LoadingMedia); + break; + case Loaded: + if ((m_pendingTasks | m_executingTask | m_executedTasks) & (Play | Pause)) { + if (m_buffering) + m_playerControl->updateStatus(QMediaPlayer::BufferingMedia); + else + m_playerControl->updateStatus(QMediaPlayer::BufferedMedia); + } else { + m_playerControl->updateStatus(QMediaPlayer::LoadedMedia); + } + break; + case InvalidMedia: + m_playerControl->updateStatus(QMediaPlayer::InvalidMedia); + break; + default: + m_playerControl->updateStatus(QMediaPlayer::UnknownMediaStatus); + } +} + +bool DirectShowPlayerService::isConnected(IBaseFilter *filter, PIN_DIRECTION direction) const +{ + bool connected = false; + + IEnumPins *pins = 0; + + if (SUCCEEDED(filter->EnumPins(&pins))) { + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION dir; + if (SUCCEEDED(pin->QueryDirection(&dir)) && dir == direction) { + IPin *peer = 0; + if (SUCCEEDED(pin->ConnectedTo(&peer))) { + connected = true; + + peer->Release(); + } + } + } + pins->Release(); + } + return connected; +} + +IBaseFilter *DirectShowPlayerService::getConnected( + IBaseFilter *filter, PIN_DIRECTION direction) const +{ + IBaseFilter *connected = 0; + + IEnumPins *pins = 0; + + if (SUCCEEDED(filter->EnumPins(&pins))) { + for (IPin *pin = 0; pins->Next(1, &pin, 0) == S_OK; pin->Release()) { + PIN_DIRECTION dir; + if (SUCCEEDED(pin->QueryDirection(&dir)) && dir == direction) { + IPin *peer = 0; + if (SUCCEEDED(pin->ConnectedTo(&peer))) { + PIN_INFO info; + + if (SUCCEEDED(peer->QueryPinInfo(&info))) { + if (connected) { + qWarning("DirectShowPlayerService::getConnected: " + "Multiple connected filters"); + connected->Release(); + } + connected = info.pFilter; + } + peer->Release(); + } + } + } + pins->Release(); + } + return connected; +} + +void DirectShowPlayerService::run() +{ + QMutexLocker locker(&m_mutex); + + for (;;) { + ::ResetEvent(m_taskHandle); + + while (m_pendingTasks == 0) { + DWORD result = 0; + + locker.unlock(); + if (m_eventHandle) { + HANDLE handles[] = { m_taskHandle, m_eventHandle }; + + result = ::WaitForMultipleObjects(2, handles, false, INFINITE); + } else { + result = ::WaitForSingleObject(m_taskHandle, INFINITE); + } + locker.relock(); + + if (result == WAIT_OBJECT_0 + 1) { + graphEvent(&locker); + } + } + + if (m_pendingTasks & ReleaseGraph) { + m_pendingTasks ^= ReleaseGraph; + m_executingTask = ReleaseGraph; + + doReleaseGraph(&locker); + //if the graph is released, we should not process other operations later + if (m_pendingTasks & Shutdown) { + m_pendingTasks = 0; + return; + } + m_pendingTasks = 0; + } else if (m_pendingTasks & Shutdown) { + return; + } else if (m_pendingTasks & ReleaseAudioOutput) { + m_pendingTasks ^= ReleaseAudioOutput; + m_executingTask = ReleaseAudioOutput; + + doReleaseAudioOutput(&locker); + } else if (m_pendingTasks & ReleaseVideoOutput) { + m_pendingTasks ^= ReleaseVideoOutput; + m_executingTask = ReleaseVideoOutput; + + doReleaseVideoOutput(&locker); + } else if (m_pendingTasks & SetUrlSource) { + m_pendingTasks ^= SetUrlSource; + m_executingTask = SetUrlSource; + + doSetUrlSource(&locker); + } else if (m_pendingTasks & SetStreamSource) { + m_pendingTasks ^= SetStreamSource; + m_executingTask = SetStreamSource; + + doSetStreamSource(&locker); + } else if (m_pendingTasks & Render) { + m_pendingTasks ^= Render; + m_executingTask = Render; + + doRender(&locker); + } else if (!(m_executedTasks & Render)) { + m_pendingTasks &= ~(FinalizeLoad | SetRate | Stop | Pause | Seek | Play); + } else if (m_pendingTasks & FinalizeLoad) { + m_pendingTasks ^= FinalizeLoad; + m_executingTask = FinalizeLoad; + + doFinalizeLoad(&locker); + } else if (m_pendingTasks & Stop) { + m_pendingTasks ^= Stop; + m_executingTask = Stop; + + doStop(&locker); + } else if (m_pendingTasks & SetRate) { + m_pendingTasks ^= SetRate; + m_executingTask = SetRate; + + doSetRate(&locker); + } else if (m_pendingTasks & Pause) { + m_pendingTasks ^= Pause; + m_executingTask = Pause; + + doPause(&locker); + } else if (m_pendingTasks & Seek) { + m_pendingTasks ^= Seek; + m_executingTask = Seek; + + doSeek(&locker); + } else if (m_pendingTasks & Play) { + m_pendingTasks ^= Play; + m_executingTask = Play; + + doPlay(&locker); + } + m_executingTask = 0; + } +} diff --git a/src/plugins/directshow/player/directshowplayerservice.h b/src/plugins/directshow/player/directshowplayerservice.h new file mode 100644 index 000000000..cc0cac1cf --- /dev/null +++ b/src/plugins/directshow/player/directshowplayerservice.h @@ -0,0 +1,219 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWPLAYERSERVICE_H +#define DIRECTSHOWPLAYERSERVICE_H + +#include "qmediaplayer.h" +#include "qmediaresource.h" +#include "qmediaservice.h" +#include "qmediatimerange.h" + +#include "directshoweventloop.h" +#include "directshowglobal.h" + +#include +#include +#include +#include + +class DirectShowAudioEndpointControl; +class DirectShowMetaDataControl; +class DirectShowPlayerControl; +class DirectShowVideoRendererControl; +#ifndef Q_WS_SIMULATOR +class Vmr9VideoWindowControl; +#endif + +QT_BEGIN_NAMESPACE +class QMediaContent; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class DirectShowPlayerService : public QMediaService +{ + Q_OBJECT +public: + enum StreamType + { + AudioStream = 0x01, + VideoStream = 0x02 + }; + + DirectShowPlayerService(QObject *parent = 0); + ~DirectShowPlayerService(); + + QMediaControl* requestControl(const char *name); + void releaseControl(QMediaControl *control); + + void load(const QMediaContent &media, QIODevice *stream); + void play(); + void pause(); + void stop(); + + qint64 position() const; + QMediaTimeRange availablePlaybackRanges() const; + + void seek(qint64 position); + void setRate(qreal rate); + + int bufferStatus() const; + + void setAudioOutput(IBaseFilter *filter); + void setVideoOutput(IBaseFilter *filter); + +protected: + void customEvent(QEvent *event); + +private Q_SLOTS: + void videoOutputChanged(); + +private: + void releaseGraph(); + void updateStatus(); + + int findStreamTypes(IBaseFilter *source) const; + int findStreamType(IPin *pin) const; + + bool isConnected(IBaseFilter *filter, PIN_DIRECTION direction) const; + IBaseFilter *getConnected(IBaseFilter *filter, PIN_DIRECTION direction) const; + + void run(); + + void doSetUrlSource(QMutexLocker *locker); + void doSetStreamSource(QMutexLocker *locker); + void doRender(QMutexLocker *locker); + void doFinalizeLoad(QMutexLocker *locker); + void doSetRate(QMutexLocker *locker); + void doSeek(QMutexLocker *locker); + void doPlay(QMutexLocker *locker); + void doPause(QMutexLocker *locker); + void doStop(QMutexLocker *locker); + void doReleaseAudioOutput(QMutexLocker *locker); + void doReleaseVideoOutput(QMutexLocker *locker); + void doReleaseGraph(QMutexLocker *locker); + + void graphEvent(QMutexLocker *locker); + + enum Task + { + Shutdown = 0x0001, + SetUrlSource = 0x0002, + SetStreamSource = 0x0004, + SetSource = SetUrlSource | SetStreamSource, + SetAudioOutput = 0x0008, + SetVideoOutput = 0x0010, + SetOutputs = SetAudioOutput | SetVideoOutput, + Render = 0x0020, + FinalizeLoad = 0x0040, + SetRate = 0x0080, + Seek = 0x0100, + Play = 0x0200, + Pause = 0x0400, + Stop = 0x0800, + ReleaseGraph = 0x1000, + ReleaseAudioOutput = 0x2000, + ReleaseVideoOutput = 0x4000, + ReleaseFilters = ReleaseGraph | ReleaseAudioOutput | ReleaseVideoOutput + }; + + enum Event + { + FinalizedLoad = QEvent::User, + Error, + RateChange, + Started, + Paused, + DurationChange, + StatusChange, + EndOfMedia, + PositionChange + }; + + enum GraphStatus + { + NoMedia, + Loading, + Loaded, + InvalidMedia + }; + + DirectShowPlayerControl *m_playerControl; + DirectShowMetaDataControl *m_metaDataControl; + DirectShowVideoRendererControl *m_videoRendererControl; +#ifndef Q_WS_SIMULATOR + Vmr9VideoWindowControl *m_videoWindowControl; +#endif + DirectShowAudioEndpointControl *m_audioEndpointControl; + + QThread *m_taskThread; + DirectShowEventLoop *m_loop; + int m_pendingTasks; + int m_executingTask; + int m_executedTasks; + HANDLE m_taskHandle; + HANDLE m_eventHandle; + GraphStatus m_graphStatus; + QMediaPlayer::Error m_error; + QIODevice *m_stream; + IFilterGraph2 *m_graph; + IBaseFilter *m_source; + IBaseFilter *m_audioOutput; + IBaseFilter *m_videoOutput; + int m_streamTypes; + qreal m_rate; + qint64 m_position; + qint64 m_duration; + bool m_buffering; + bool m_seekable; + bool m_atEnd; + QMediaTimeRange m_playbackRange; + QUrl m_url; + QMediaResourceList m_resources; + QString m_errorString; + QMutex m_mutex; + + friend class DirectShowPlayerServiceThread; +}; + + +#endif diff --git a/src/plugins/directshow/player/directshowsamplescheduler.cpp b/src/plugins/directshow/player/directshowsamplescheduler.cpp new file mode 100644 index 000000000..48b7899c6 --- /dev/null +++ b/src/plugins/directshow/player/directshowsamplescheduler.cpp @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowsamplescheduler.h" + +#include +#include + +class DirectShowTimedSample +{ +public: + DirectShowTimedSample(IMediaSample *sample) + : m_next(0) + , m_sample(sample) + , m_cookie(0) + , m_lastSample(false) + { + m_sample->AddRef(); + } + + ~DirectShowTimedSample() + { + m_sample->Release(); + } + + IMediaSample *sample() const { return m_sample; } + + DirectShowTimedSample *nextSample() const { return m_next; } + void setNextSample(DirectShowTimedSample *sample) { Q_ASSERT(!m_next); m_next = sample; } + + DirectShowTimedSample *remove() { + DirectShowTimedSample *next = m_next; delete this; return next; } + + bool schedule(IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle); + void unschedule(IReferenceClock *clock); + + bool isReady(IReferenceClock *clock) const; + + bool isLast() const { return m_lastSample; } + void setLast() { m_lastSample = true; } + +private: + DirectShowTimedSample *m_next; + IMediaSample *m_sample; + DWORD_PTR m_cookie; + bool m_lastSample; +}; + +bool DirectShowTimedSample::schedule( + IReferenceClock *clock, REFERENCE_TIME startTime, HANDLE handle) +{ + REFERENCE_TIME sampleStartTime; + REFERENCE_TIME sampleEndTime; + if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) { + if (clock->AdviseTime( + startTime, sampleStartTime, reinterpret_cast(handle), &m_cookie) == S_OK) { + return true; + } + } + return false; +} + +void DirectShowTimedSample::unschedule(IReferenceClock *clock) +{ + clock->Unadvise(m_cookie); +} + +bool DirectShowTimedSample::isReady(IReferenceClock *clock) const +{ + REFERENCE_TIME sampleStartTime; + REFERENCE_TIME sampleEndTime; + REFERENCE_TIME currentTime; + if (m_sample->GetTime(&sampleStartTime, &sampleEndTime) == S_OK) { + if (clock->GetTime(¤tTime) == S_OK) + return currentTime >= sampleStartTime; + } + return true; +} + +DirectShowSampleScheduler::DirectShowSampleScheduler(IUnknown *pin, QObject *parent) + : QObject(parent) + , m_pin(pin) + , m_clock(0) + , m_allocator(0) + , m_head(0) + , m_tail(0) + , m_maximumSamples(1) + , m_state(Stopped) + , m_startTime(0) + , m_timeoutEvent(::CreateEvent(0, 0, 0, 0)) + , m_flushEvent(::CreateEvent(0, 0, 0, 0)) +{ + m_semaphore.release(m_maximumSamples); +} + +DirectShowSampleScheduler::~DirectShowSampleScheduler() +{ + ::CloseHandle(m_timeoutEvent); + ::CloseHandle(m_flushEvent); + + Q_ASSERT(!m_clock); + Q_ASSERT(!m_allocator); +} + +HRESULT DirectShowSampleScheduler::QueryInterface(REFIID riid, void **ppvObject) +{ + return m_pin->QueryInterface(riid, ppvObject); +} + +ULONG DirectShowSampleScheduler::AddRef() +{ + return m_pin->AddRef(); +} + +ULONG DirectShowSampleScheduler::Release() +{ + return m_pin->Release(); +} + +// IMemInputPin +HRESULT DirectShowSampleScheduler::GetAllocator(IMemAllocator **ppAllocator) +{ + if (!ppAllocator) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_allocator) { + return VFW_E_NO_ALLOCATOR; + } else { + *ppAllocator = m_allocator; + + return S_OK; + } + } +} + +HRESULT DirectShowSampleScheduler::NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly) +{ + Q_UNUSED(bReadOnly); + + HRESULT hr; + ALLOCATOR_PROPERTIES properties; + + if (!pAllocator) { + if (m_allocator) + m_allocator->Release(); + + m_allocator = 0; + + return S_OK; + } else if ((hr = pAllocator->GetProperties(&properties)) != S_OK) { + return hr; + } else { + if (properties.cBuffers == 1) { + ALLOCATOR_PROPERTIES actual; + + properties.cBuffers = 2; + if ((hr = pAllocator->SetProperties(&properties, &actual)) != S_OK) + return hr; + } + + QMutexLocker locker(&m_mutex); + + if (m_allocator) + m_allocator->Release(); + + m_allocator = pAllocator; + m_allocator->AddRef(); + + return S_OK; + } +} + +HRESULT DirectShowSampleScheduler::GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps) +{ + if (!pProps) + return E_POINTER; + + pProps->cBuffers = 2; + + return S_OK; +} + +HRESULT DirectShowSampleScheduler::Receive(IMediaSample *pSample) +{ + if (!pSample) + return E_POINTER; + + m_semaphore.acquire(1); + + QMutexLocker locker(&m_mutex); + + if (m_state & Flushing) { + m_semaphore.release(1); + + return S_FALSE; + } else if (m_state == Stopped) { + m_semaphore.release(); + + return VFW_E_WRONG_STATE; + } else { + DirectShowTimedSample *timedSample = new DirectShowTimedSample(pSample); + + if (m_tail) + m_tail->setNextSample(timedSample); + else + m_head = timedSample; + + m_tail = timedSample; + + if (m_state == Running) { + if (!timedSample->schedule(m_clock, m_startTime, m_timeoutEvent)) { + // Timing information is unavailable, so schedule frames immediately. + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } else { + locker.unlock(); + HANDLE handles[] = { m_flushEvent, m_timeoutEvent }; + DWORD result = ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + locker.relock(); + + if (result == WAIT_OBJECT_0 + 1) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + } + } else if (m_tail == m_head) { + // If this is the first frame make it available. + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + + if (m_state == Paused) { + ::ResetEvent(m_timeoutEvent); + + locker.unlock(); + HANDLE handles[] = { m_flushEvent, m_timeoutEvent }; + ::WaitForMultipleObjects(2, handles, FALSE, INFINITE); + locker.relock(); + } + } + + return S_OK; + } +} + +HRESULT DirectShowSampleScheduler::ReceiveMultiple( + IMediaSample **pSamples, long nSamples, long *nSamplesProcessed) +{ + if (!pSamples || !nSamplesProcessed) + return E_POINTER; + + for (*nSamplesProcessed = 0; *nSamplesProcessed < nSamples; ++(*nSamplesProcessed)) { + HRESULT hr = Receive(pSamples[*nSamplesProcessed]); + + if (hr != S_OK) + return hr; + } + return S_OK; +} + +HRESULT DirectShowSampleScheduler::ReceiveCanBlock() +{ + return S_OK; +} + +void DirectShowSampleScheduler::run(REFERENCE_TIME startTime) +{ + QMutexLocker locker(&m_mutex); + + m_state = (m_state & Flushing) | Running; + m_startTime = startTime; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) { + sample->schedule(m_clock, m_startTime, m_timeoutEvent); + } + + if (!(m_state & Flushing)) + ::ResetEvent(m_flushEvent); + + if (!m_head) + ::SetEvent(m_timeoutEvent); + +} + +void DirectShowSampleScheduler::pause() +{ + QMutexLocker locker(&m_mutex); + + m_state = (m_state & Flushing) | Paused; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->nextSample()) + sample->unschedule(m_clock); + + if (!(m_state & Flushing)) + ::ResetEvent(m_flushEvent); +} + +void DirectShowSampleScheduler::stop() +{ + QMutexLocker locker(&m_mutex); + + m_state = m_state & Flushing; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) { + sample->unschedule(m_clock); + + m_semaphore.release(1); + } + + m_head = 0; + m_tail = 0; + + ::SetEvent(m_flushEvent); +} + +void DirectShowSampleScheduler::setFlushing(bool flushing) +{ + QMutexLocker locker(&m_mutex); + + const bool isFlushing = m_state & Flushing; + + if (isFlushing != flushing) { + if (flushing) { + m_state |= Flushing; + + for (DirectShowTimedSample *sample = m_head; sample; sample = sample->remove()) { + sample->unschedule(m_clock); + + m_semaphore.release(1); + } + m_head = 0; + m_tail = 0; + + ::SetEvent(m_flushEvent); + } else { + m_state &= ~Flushing; + + if (m_state != Stopped) + ::ResetEvent(m_flushEvent); + } + } +} + +void DirectShowSampleScheduler::setClock(IReferenceClock *clock) +{ + QMutexLocker locker(&m_mutex); + + if (m_clock) + m_clock->Release(); + + m_clock = clock; + + if (m_clock) + m_clock->AddRef(); +} + +IMediaSample *DirectShowSampleScheduler::takeSample(bool *eos) +{ + QMutexLocker locker(&m_mutex); + + if (m_head && m_head->isReady(m_clock)) { + IMediaSample *sample = m_head->sample(); + sample->AddRef(); + + *eos = m_head->isLast(); + + m_head = m_head->remove(); + + if (!m_head) + m_tail = 0; + + m_semaphore.release(1); + + return sample; + } else { + return 0; + } +} + +bool DirectShowSampleScheduler::scheduleEndOfStream() +{ + QMutexLocker locker(&m_mutex); + + if (m_tail) { + m_tail->setLast(); + + return true; + } else { + return false; + } +} + +bool DirectShowSampleScheduler::event(QEvent *event) +{ + if (event->type() == QEvent::UpdateRequest) { + emit sampleReady(); + + return true; + } else { + return QObject::event(event); + } +} diff --git a/src/plugins/directshow/player/directshowsamplescheduler.h b/src/plugins/directshow/player/directshowsamplescheduler.h new file mode 100644 index 000000000..bea833ef9 --- /dev/null +++ b/src/plugins/directshow/player/directshowsamplescheduler.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWSAMPLESCHEDULER_H +#define DIRECTSHOWSAMPLESCHEDULER_H + +#include +#include +#include + +#include + +class DirectShowTimedSample; + +class DirectShowSampleScheduler : public QObject, public IMemInputPin +{ + Q_OBJECT +public: + + enum State + { + Stopped = 0x00, + Running = 0x01, + Paused = 0x02, + RunMask = 0x03, + Flushing = 0x04 + }; + + DirectShowSampleScheduler(IUnknown *pin, QObject *parent = 0); + ~DirectShowSampleScheduler(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IMemInputPin + HRESULT STDMETHODCALLTYPE GetAllocator(IMemAllocator **ppAllocator); + HRESULT STDMETHODCALLTYPE NotifyAllocator(IMemAllocator *pAllocator, BOOL bReadOnly); + HRESULT STDMETHODCALLTYPE GetAllocatorRequirements(ALLOCATOR_PROPERTIES *pProps); + + HRESULT STDMETHODCALLTYPE Receive(IMediaSample *pSample); + HRESULT STDMETHODCALLTYPE ReceiveMultiple(IMediaSample **pSamples, long nSamples, long *nSamplesProcessed); + HRESULT STDMETHODCALLTYPE ReceiveCanBlock(); + + void run(REFERENCE_TIME startTime); + void pause(); + void stop(); + void setFlushing(bool flushing); + + IReferenceClock *clock() const { return m_clock; } + void setClock(IReferenceClock *clock); + + bool schedule(IMediaSample *sample); + bool scheduleEndOfStream(); + + IMediaSample *takeSample(bool *eos); + + bool event(QEvent *event); + +Q_SIGNALS: + void sampleReady(); + +private: + IUnknown *m_pin; + IReferenceClock *m_clock; + IMemAllocator *m_allocator; + DirectShowTimedSample *m_head; + DirectShowTimedSample *m_tail; + int m_maximumSamples; + int m_state; + REFERENCE_TIME m_startTime; + HANDLE m_timeoutEvent; + HANDLE m_flushEvent; + QSemaphore m_semaphore; + QMutex m_mutex; +}; + +#endif diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.cpp b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp new file mode 100644 index 000000000..429d5e1ec --- /dev/null +++ b/src/plugins/directshow/player/directshowvideorenderercontrol.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "directshowvideorenderercontrol.h" + +#include "videosurfacefilter.h" + +DirectShowVideoRendererControl::DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent) + : QVideoRendererControl(parent) + , m_loop(loop) + , m_surface(0) + , m_filter(0) +{ +} + +DirectShowVideoRendererControl::~DirectShowVideoRendererControl() +{ + delete m_filter; +} + +QAbstractVideoSurface *DirectShowVideoRendererControl::surface() const +{ + return m_surface; +} + +void DirectShowVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (surface != m_surface) { + m_surface = surface; + + VideoSurfaceFilter *existingFilter = m_filter; + + if (surface) { + m_filter = new VideoSurfaceFilter(surface, m_loop); + } else { + m_filter = 0; + } + + emit filterChanged(); + + delete existingFilter; + } +} + +IBaseFilter *DirectShowVideoRendererControl::filter() +{ + return m_filter; +} diff --git a/src/plugins/directshow/player/directshowvideorenderercontrol.h b/src/plugins/directshow/player/directshowvideorenderercontrol.h new file mode 100644 index 000000000..5057a94e0 --- /dev/null +++ b/src/plugins/directshow/player/directshowvideorenderercontrol.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef DIRECTSHOWVIDEORENDERERCONTROL_H +#define DIRECTSHOWVIDEORENDERERCONTROL_H + +#include "qvideorenderercontrol.h" + +#include + +class DirectShowEventLoop; +class VideoSurfaceFilter; + +QT_USE_NAMESPACE + +class DirectShowVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT +public: + DirectShowVideoRendererControl(DirectShowEventLoop *loop, QObject *parent = 0); + ~DirectShowVideoRendererControl(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + IBaseFilter *filter(); + +Q_SIGNALS: + void filterChanged(); + +private: + DirectShowEventLoop *m_loop; + QAbstractVideoSurface *m_surface; + VideoSurfaceFilter *m_filter; +}; + +#endif diff --git a/src/plugins/directshow/player/mediasamplevideobuffer.cpp b/src/plugins/directshow/player/mediasamplevideobuffer.cpp new file mode 100644 index 000000000..a2d1d7cee --- /dev/null +++ b/src/plugins/directshow/player/mediasamplevideobuffer.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "mediasamplevideobuffer.h" + +MediaSampleVideoBuffer::MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) + , m_sample(sample) + , m_bytesPerLine(bytesPerLine) + , m_mapMode(NotMapped) +{ + m_sample->AddRef(); +} + +MediaSampleVideoBuffer::~MediaSampleVideoBuffer() +{ + m_sample->Release(); +} + +uchar *MediaSampleVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + if (m_mapMode == NotMapped && mode != NotMapped) { + if (numBytes) + *numBytes = m_sample->GetActualDataLength(); + + if (bytesPerLine) + *bytesPerLine = m_bytesPerLine; + + BYTE *bytes = 0; + + if (m_sample->GetPointer(&bytes) == S_OK) { + m_mapMode = mode; + + return reinterpret_cast(bytes); + } + } + return 0; +} + +void MediaSampleVideoBuffer::unmap() +{ + m_mapMode = NotMapped; +} + +QAbstractVideoBuffer::MapMode MediaSampleVideoBuffer::mapMode() const +{ + return m_mapMode; +} diff --git a/src/plugins/directshow/player/mediasamplevideobuffer.h b/src/plugins/directshow/player/mediasamplevideobuffer.h new file mode 100644 index 000000000..0d44e7a1f --- /dev/null +++ b/src/plugins/directshow/player/mediasamplevideobuffer.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef MEDIASAMPLEVIDEOBUFFER_H +#define MEDIASAMPLEVIDEOBUFFER_H + +#include + +#include + +class MediaSampleVideoBuffer : public QAbstractVideoBuffer +{ +public: + MediaSampleVideoBuffer(IMediaSample *sample, int bytesPerLine); + ~MediaSampleVideoBuffer(); + + IMediaSample *sample() { return m_sample; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); + + MapMode mapMode() const; + +private: + IMediaSample *m_sample; + int m_bytesPerLine; + MapMode m_mapMode; +}; + + +#endif diff --git a/src/plugins/directshow/player/player.pri b/src/plugins/directshow/player/player.pri new file mode 100644 index 000000000..a058b0659 --- /dev/null +++ b/src/plugins/directshow/player/player.pri @@ -0,0 +1,47 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_DIRECTSHOW_PLAYER + +HEADERS += \ + $$PWD/directshowaudioendpointcontrol.h \ + $$PWD/directshoweventloop.h \ + $$PWD/directshowglobal.h \ + $$PWD/directshowioreader.h \ + $$PWD/directshowiosource.h \ + $$PWD/directshowmediatype.h \ + $$PWD/directshowmediatypelist.h \ + $$PWD/directshowmetadatacontrol.h \ + $$PWD/directshowpinenum.h \ + $$PWD/directshowplayercontrol.h \ + $$PWD/directshowplayerservice.h \ + $$PWD/directshowsamplescheduler.h \ + $$PWD/directshowvideorenderercontrol.h \ + $$PWD/mediasamplevideobuffer.h \ + $$PWD/videosurfacefilter.h + +SOURCES += \ + $$PWD/directshowaudioendpointcontrol.cpp \ + $$PWD/directshoweventloop.cpp \ + $$PWD/directshowioreader.cpp \ + $$PWD/directshowiosource.cpp \ + $$PWD/directshowmediatype.cpp \ + $$PWD/directshowmediatypelist.cpp \ + $$PWD/directshowmetadatacontrol.cpp \ + $$PWD/directshowpinenum.cpp \ + $$PWD/directshowplayercontrol.cpp \ + $$PWD/directshowplayerservice.cpp \ + $$PWD/directshowsamplescheduler.cpp \ + $$PWD/directshowvideorenderercontrol.cpp \ + $$PWD/mediasamplevideobuffer.cpp \ + $$PWD/videosurfacefilter.cpp + +!simulator { +HEADERS += \ + $$PWD/vmr9videowindowcontrol.h + +SOURCES += \ + $$PWD/vmr9videowindowcontrol.cpp +} + +LIBS += -lstrmiids -ldmoguids -luuid -lmsdmo -lole32 -loleaut32 -lgdi32 + diff --git a/src/plugins/directshow/player/videosurfacefilter.cpp b/src/plugins/directshow/player/videosurfacefilter.cpp new file mode 100644 index 000000000..a6a3c1b4f --- /dev/null +++ b/src/plugins/directshow/player/videosurfacefilter.cpp @@ -0,0 +1,631 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "videosurfacefilter.h" + +#include "directshoweventloop.h" +#include "directshowglobal.h" +#include "directshowpinenum.h" +#include "mediasamplevideobuffer.h" + +#include +#include +#include +#include + +#include + +// { e23cad72-153d-406c-bf3f-4c4b523d96f2 } +DEFINE_GUID(CLSID_VideoSurfaceFilter, +0xe23cad72, 0x153d, 0x406c, 0xbf, 0x3f, 0x4c, 0x4b, 0x52, 0x3d, 0x96, 0xf2); + +VideoSurfaceFilter::VideoSurfaceFilter( + QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent) + : QObject(parent) + , m_ref(1) + , m_state(State_Stopped) + , m_surface(surface) + , m_loop(loop) + , m_graph(0) + , m_peerPin(0) + , m_bytesPerLine(0) + , m_startResult(S_OK) + , m_pinId(QString::fromLatin1("reference")) + , m_sampleScheduler(static_cast(this)) +{ + connect(surface, SIGNAL(supportedFormatsChanged()), this, SLOT(supportedFormatsChanged())); + connect(&m_sampleScheduler, SIGNAL(sampleReady()), this, SLOT(sampleReady())); +} + +VideoSurfaceFilter::~VideoSurfaceFilter() +{ + Q_ASSERT(m_ref == 1); +} + +HRESULT VideoSurfaceFilter::QueryInterface(REFIID riid, void **ppvObject) +{ + // 2dd74950-a890-11d1-abe8-00a0c905f375 + static const GUID iid_IAmFilterMiscFlags = { + 0x2dd74950, 0xa890, 0x11d1, {0xab, 0xe8, 0x00, 0xa0, 0xc9, 0x05, 0xf3, 0x75} }; + + if (!ppvObject) { + return E_POINTER; + } else if (riid == IID_IUnknown + || riid == IID_IPersist + || riid == IID_IMediaFilter + || riid == IID_IBaseFilter) { + *ppvObject = static_cast(this); + } else if (riid == iid_IAmFilterMiscFlags) { + *ppvObject = static_cast(this); + } else if (riid == IID_IPin) { + *ppvObject = static_cast(this); + } else if (riid == IID_IMemInputPin) { + *ppvObject = static_cast(&m_sampleScheduler); + } else { + *ppvObject = 0; + + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG VideoSurfaceFilter::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG VideoSurfaceFilter::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + Q_ASSERT(ref != 0); + + return ref; +} + +HRESULT VideoSurfaceFilter::GetClassID(CLSID *pClassID) +{ + *pClassID = CLSID_VideoSurfaceFilter; + + return S_OK; +} + +HRESULT VideoSurfaceFilter::Run(REFERENCE_TIME tStart) +{ + m_state = State_Running; + + m_sampleScheduler.run(tStart); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::Pause() +{ + m_state = State_Paused; + + m_sampleScheduler.pause(); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::Stop() +{ + m_state = State_Stopped; + + m_sampleScheduler.stop(); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState) +{ + if (!pState) + return E_POINTER; + + *pState = m_state; + + return S_OK; +} + +HRESULT VideoSurfaceFilter::SetSyncSource(IReferenceClock *pClock) +{ + + m_sampleScheduler.setClock(pClock); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::GetSyncSource(IReferenceClock **ppClock) +{ + if (!ppClock) { + return E_POINTER; + } else { + *ppClock = m_sampleScheduler.clock(); + + if (*ppClock) { + (*ppClock)->AddRef(); + + return S_OK; + } else { + return S_FALSE; + } + } +} + +HRESULT VideoSurfaceFilter::EnumPins(IEnumPins **ppEnum) +{ + if (ppEnum) { + *ppEnum = new DirectShowPinEnum(QList() << this); + + return S_OK; + } else { + return E_POINTER; + } +} + +HRESULT VideoSurfaceFilter::FindPin(LPCWSTR pId, IPin **ppPin) +{ + if (!ppPin || !pId) { + return E_POINTER; + } else if (QString::fromWCharArray(pId) == m_pinId) { + AddRef(); + + *ppPin = this; + + return S_OK; + } else { + return VFW_E_NOT_FOUND; + } +} + +HRESULT VideoSurfaceFilter::JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName) +{ + m_graph = pGraph; + m_name = QString::fromWCharArray(pName); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::QueryFilterInfo(FILTER_INFO *pInfo) +{ + if (pInfo) { + QString name = m_name; + + if (name.length() >= MAX_FILTER_NAME) + name.truncate(MAX_FILTER_NAME - 1); + + int length = name.toWCharArray(pInfo->achName); + pInfo->achName[length] = '\0'; + + if (m_graph) + m_graph->AddRef(); + + pInfo->pGraph = m_graph; + + return S_OK; + } else { + return E_POINTER; + } +} + +HRESULT VideoSurfaceFilter::QueryVendorInfo(LPWSTR *pVendorInfo) +{ + Q_UNUSED(pVendorInfo); + + return E_NOTIMPL; +} + +ULONG VideoSurfaceFilter::GetMiscFlags() +{ + return AM_FILTER_MISC_FLAGS_IS_RENDERER; +} + + +HRESULT VideoSurfaceFilter::Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt) +{ + // This is an input pin, you shouldn't be calling Connect on it. + return E_POINTER; +} + +HRESULT VideoSurfaceFilter::ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt) +{ + if (!pConnector) { + return E_POINTER; + } else if (!pmt) { + return E_POINTER; + } else { + HRESULT hr; + QMutexLocker locker(&m_mutex); + + if (m_peerPin) { + hr = VFW_E_ALREADY_CONNECTED; + } else if (pmt->majortype != MEDIATYPE_Video) { + hr = VFW_E_TYPE_NOT_ACCEPTED; + } else { + m_surfaceFormat = DirectShowMediaType::formatFromType(*pmt); + m_bytesPerLine = DirectShowMediaType::bytesPerLine(m_surfaceFormat); + + if (thread() == QThread::currentThread()) { + hr = start(); + } else { + m_loop->postEvent(this, new QEvent(QEvent::Type(StartSurface))); + + m_wait.wait(&m_mutex); + + hr = m_startResult; + } + } + if (hr == S_OK) { + m_peerPin = pConnector; + m_peerPin->AddRef(); + + DirectShowMediaType::copy(&m_mediaType, *pmt); + } + return hr; + } +} + +HRESULT VideoSurfaceFilter::start() +{ + if (!m_surface->isFormatSupported(m_surfaceFormat)) { + return VFW_E_TYPE_NOT_ACCEPTED; + } + if (!m_surface->start(m_surfaceFormat)) { + return VFW_E_TYPE_NOT_ACCEPTED; + } else { + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::Disconnect() +{ + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) + return S_FALSE; + + if (thread() == QThread::currentThread()) { + stop(); + } else { + m_loop->postEvent(this, new QEvent(QEvent::Type(StopSurface))); + + m_wait.wait(&m_mutex); + } + + m_mediaType.clear(); + + m_sampleScheduler.NotifyAllocator(0, FALSE); + + m_peerPin->Release(); + m_peerPin = 0; + + return S_OK; +} + +void VideoSurfaceFilter::stop() +{ + m_surface->stop(); +} + +HRESULT VideoSurfaceFilter::ConnectedTo(IPin **ppPin) +{ + if (!ppPin) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + return VFW_E_NOT_CONNECTED; + } else { + m_peerPin->AddRef(); + + *ppPin = m_peerPin; + + return S_OK; + } + } +} + +HRESULT VideoSurfaceFilter::ConnectionMediaType(AM_MEDIA_TYPE *pmt) +{ + if (!pmt) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + if (!m_peerPin) { + return VFW_E_NOT_CONNECTED; + } else { + DirectShowMediaType::copy(pmt, m_mediaType); + + return S_OK; + } + } +} + +HRESULT VideoSurfaceFilter::QueryPinInfo(PIN_INFO *pInfo) +{ + if (!pInfo) { + return E_POINTER; + } else { + AddRef(); + + pInfo->pFilter = this; + pInfo->dir = PINDIR_INPUT; + + const int bytes = qMin(MAX_FILTER_NAME, (m_pinId.length() + 1) * 2); + + qMemCopy(pInfo->achName, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::QueryId(LPWSTR *Id) +{ + if (!Id) { + return E_POINTER; + } else { + const int bytes = (m_pinId.length() + 1) * 2; + + *Id = static_cast(::CoTaskMemAlloc(bytes)); + + qMemCopy(*Id, m_pinId.utf16(), bytes); + + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::QueryAccept(const AM_MEDIA_TYPE *pmt) +{ + return !m_surface->isFormatSupported(DirectShowMediaType::formatFromType(*pmt)) + ? S_OK + : S_FALSE; +} + +HRESULT VideoSurfaceFilter::EnumMediaTypes(IEnumMediaTypes **ppEnum) +{ + if (!ppEnum) { + return E_POINTER; + } else { + QMutexLocker locker(&m_mutex); + + *ppEnum = createMediaTypeEnum(); + + return S_OK; + } +} + +HRESULT VideoSurfaceFilter::QueryInternalConnections(IPin **apPin, ULONG *nPin) +{ + Q_UNUSED(apPin); + Q_UNUSED(nPin); + + return E_NOTIMPL; +} + +HRESULT VideoSurfaceFilter::EndOfStream() +{ + QMutexLocker locker(&m_mutex); + + if (!m_sampleScheduler.scheduleEndOfStream()) { + if (IMediaEventSink *sink = com_cast(m_graph, IID_IMediaEventSink)) { + sink->Notify( + EC_COMPLETE, + S_OK, + reinterpret_cast(static_cast(this))); + sink->Release(); + } + } + + return S_OK; +} + +HRESULT VideoSurfaceFilter::BeginFlush() +{ + QMutexLocker locker(&m_mutex); + + m_sampleScheduler.setFlushing(true); + + if (thread() == QThread::currentThread()) { + flush(); + } else { + m_loop->postEvent(this, new QEvent(QEvent::Type(FlushSurface))); + + m_wait.wait(&m_mutex); + } + + return S_OK; +} + +HRESULT VideoSurfaceFilter::EndFlush() +{ + QMutexLocker locker(&m_mutex); + + m_sampleScheduler.setFlushing(false); + + return S_OK; +} + +void VideoSurfaceFilter::flush() +{ + m_surface->present(QVideoFrame()); + + m_wait.wakeAll(); +} + +HRESULT VideoSurfaceFilter::NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate) +{ + Q_UNUSED(tStart); + Q_UNUSED(tStop); + Q_UNUSED(dRate); + + return S_OK; +} + +HRESULT VideoSurfaceFilter::QueryDirection(PIN_DIRECTION *pPinDir) +{ + if (!pPinDir) { + return E_POINTER; + } else { + *pPinDir = PINDIR_INPUT; + + return S_OK; + } +} + +int VideoSurfaceFilter::currentMediaTypeToken() +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::currentMediaTypeToken(); +} + +HRESULT VideoSurfaceFilter::nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount) +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::nextMediaType(token, index, count, types, fetchedCount); + +} + +HRESULT VideoSurfaceFilter::skipMediaType(int token, int *index, ULONG count) +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::skipMediaType(token, index, count); +} + +HRESULT VideoSurfaceFilter::cloneMediaType(int token, int index, IEnumMediaTypes **enumeration) +{ + QMutexLocker locker(&m_mutex); + + return DirectShowMediaTypeList::cloneMediaType(token, index, enumeration); +} + +void VideoSurfaceFilter::customEvent(QEvent *event) +{ + if (event->type() == StartSurface) { + QMutexLocker locker(&m_mutex); + + m_startResult = start(); + + m_wait.wakeAll(); + } else if (event->type() == StopSurface) { + QMutexLocker locker(&m_mutex); + + stop(); + + m_wait.wakeAll(); + } else if (event->type() == FlushSurface) { + QMutexLocker locker(&m_mutex); + + flush(); + + m_wait.wakeAll(); + } else { + QObject::customEvent(event); + } +} + +void VideoSurfaceFilter::supportedFormatsChanged() +{ + QMutexLocker locker(&m_mutex); + + // MEDIASUBTYPE_None; + static const GUID none = { + 0xe436eb8e, 0x524f, 0x11ce, {0x9f, 0x53, 0x00, 0x20, 0xaf, 0x0b, 0xa7, 0x70} }; + + QList formats = m_surface->supportedPixelFormats(); + + QVector mediaTypes; + mediaTypes.reserve(formats.count()); + + AM_MEDIA_TYPE type; + type.majortype = MEDIATYPE_Video; + type.bFixedSizeSamples = TRUE; + type.bTemporalCompression = FALSE; + type.lSampleSize = 0; + type.formattype = GUID_NULL; + type.pUnk = 0; + type.cbFormat = 0; + type.pbFormat = 0; + + foreach (QVideoFrame::PixelFormat format, formats) { + type.subtype = DirectShowMediaType::convertPixelFormat(format); + + if (type.subtype != none) + mediaTypes.append(type); + } + + setMediaTypes(mediaTypes); +} + +void VideoSurfaceFilter::sampleReady() +{ + bool eos = false; + + IMediaSample *sample = m_sampleScheduler.takeSample(&eos); + + if (sample) { + m_surface->present(QVideoFrame( + new MediaSampleVideoBuffer(sample, m_bytesPerLine), + m_surfaceFormat.frameSize(), + m_surfaceFormat.pixelFormat())); + + sample->Release(); + + if (eos) { + if (IMediaEventSink *sink = com_cast(m_graph, IID_IMediaEventSink)) { + sink->Notify( + EC_COMPLETE, + S_OK, + reinterpret_cast(static_cast(this))); + sink->Release(); + } + } + } +} + diff --git a/src/plugins/directshow/player/videosurfacefilter.h b/src/plugins/directshow/player/videosurfacefilter.h new file mode 100644 index 000000000..a58971630 --- /dev/null +++ b/src/plugins/directshow/player/videosurfacefilter.h @@ -0,0 +1,176 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIDEOSURFACEFILTER_H +#define VIDEOSURFACEFILTER_H + +#include "directshowglobal.h" +#include "directshowmediatypelist.h" +#include "directshowsamplescheduler.h" +#include "directshowmediatype.h" + +#include +#include +#include +#include +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; +QT_END_NAMESPACE + +class DirectShowEventLoop; + +class VideoSurfaceFilter + : public QObject + , public DirectShowMediaTypeList + , public IBaseFilter + , public IAMFilterMiscFlags + , public IPin +{ + Q_OBJECT +public: + VideoSurfaceFilter( + QAbstractVideoSurface *surface, DirectShowEventLoop *loop, QObject *parent = 0); + ~VideoSurfaceFilter(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IPersist + HRESULT STDMETHODCALLTYPE GetClassID(CLSID *pClassID); + + // IMediaFilter + HRESULT STDMETHODCALLTYPE Run(REFERENCE_TIME tStart); + HRESULT STDMETHODCALLTYPE Pause(); + HRESULT STDMETHODCALLTYPE Stop(); + + HRESULT STDMETHODCALLTYPE GetState(DWORD dwMilliSecsTimeout, FILTER_STATE *pState); + + HRESULT STDMETHODCALLTYPE SetSyncSource(IReferenceClock *pClock); + HRESULT STDMETHODCALLTYPE GetSyncSource(IReferenceClock **ppClock); + + // IBaseFilter + HRESULT STDMETHODCALLTYPE EnumPins(IEnumPins **ppEnum); + HRESULT STDMETHODCALLTYPE FindPin(LPCWSTR Id, IPin **ppPin); + + HRESULT STDMETHODCALLTYPE JoinFilterGraph(IFilterGraph *pGraph, LPCWSTR pName); + + HRESULT STDMETHODCALLTYPE QueryFilterInfo(FILTER_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryVendorInfo(LPWSTR *pVendorInfo); + + // IAMFilterMiscFlags + ULONG STDMETHODCALLTYPE GetMiscFlags(); + + // IPin + HRESULT STDMETHODCALLTYPE Connect(IPin *pReceivePin, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE ReceiveConnection(IPin *pConnector, const AM_MEDIA_TYPE *pmt); + HRESULT STDMETHODCALLTYPE Disconnect(); + HRESULT STDMETHODCALLTYPE ConnectedTo(IPin **ppPin); + + HRESULT STDMETHODCALLTYPE ConnectionMediaType(AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE QueryPinInfo(PIN_INFO *pInfo); + HRESULT STDMETHODCALLTYPE QueryId(LPWSTR *Id); + + HRESULT STDMETHODCALLTYPE QueryAccept(const AM_MEDIA_TYPE *pmt); + + HRESULT STDMETHODCALLTYPE EnumMediaTypes(IEnumMediaTypes **ppEnum); + + HRESULT STDMETHODCALLTYPE QueryInternalConnections(IPin **apPin, ULONG *nPin); + + HRESULT STDMETHODCALLTYPE EndOfStream(); + + HRESULT STDMETHODCALLTYPE BeginFlush(); + HRESULT STDMETHODCALLTYPE EndFlush(); + + HRESULT STDMETHODCALLTYPE NewSegment(REFERENCE_TIME tStart, REFERENCE_TIME tStop, double dRate); + + HRESULT STDMETHODCALLTYPE QueryDirection(PIN_DIRECTION *pPinDir); + + int currentMediaTypeToken(); + HRESULT nextMediaType( + int token, int *index, ULONG count, AM_MEDIA_TYPE **types, ULONG *fetchedCount); + HRESULT skipMediaType(int token, int *index, ULONG count); + HRESULT cloneMediaType(int token, int index, IEnumMediaTypes **enumeration); + +protected: + void customEvent(QEvent *event); + +private Q_SLOTS: + void supportedFormatsChanged(); + void sampleReady(); + +private: + HRESULT start(); + void stop(); + void flush(); + + enum + { + StartSurface = QEvent::User, + StopSurface, + FlushSurface + }; + + LONG m_ref; + FILTER_STATE m_state; + QAbstractVideoSurface *m_surface; + DirectShowEventLoop *m_loop; + IFilterGraph *m_graph; + IPin *m_peerPin; + int m_bytesPerLine; + HRESULT m_startResult; + QString m_name; + QString m_pinId; + DirectShowMediaType m_mediaType; + QVideoSurfaceFormat m_surfaceFormat; + QMutex m_mutex; + QWaitCondition m_wait; + DirectShowSampleScheduler m_sampleScheduler; +}; + +#endif diff --git a/src/plugins/directshow/player/vmr9videowindowcontrol.cpp b/src/plugins/directshow/player/vmr9videowindowcontrol.cpp new file mode 100644 index 000000000..5e729844b --- /dev/null +++ b/src/plugins/directshow/player/vmr9videowindowcontrol.cpp @@ -0,0 +1,329 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "vmr9videowindowcontrol.h" + +#include "directshowglobal.h" + +Vmr9VideoWindowControl::Vmr9VideoWindowControl(QObject *parent) + : QVideoWindowControl(parent) + , m_filter(com_new(CLSID_VideoMixingRenderer9, IID_IBaseFilter)) + , m_windowId(0) + , m_dirtyValues(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_brightness(0) + , m_contrast(0) + , m_hue(0) + , m_saturation(0) + , m_fullScreen(false) +{ + if (IVMRFilterConfig9 *config = com_cast(m_filter, IID_IVMRFilterConfig9)) { + config->SetRenderingMode(VMR9Mode_Windowless); + config->SetNumberOfStreams(1); + config->Release(); + } +} + +Vmr9VideoWindowControl::~Vmr9VideoWindowControl() +{ + if (m_filter) + m_filter->Release(); +} + + +WId Vmr9VideoWindowControl::winId() const +{ + return m_windowId; + +} + +void Vmr9VideoWindowControl::setWinId(WId id) +{ + m_windowId = id; + + if (QWidget *widget = QWidget::find(m_windowId)) { + const QColor color = widget->palette().color(QPalette::Window); + + m_windowColor = RGB(color.red(), color.green(), color.blue()); + } + + if (IVMRWindowlessControl9 *control = com_cast( + m_filter, IID_IVMRWindowlessControl9)) { + control->SetVideoClippingWindow(m_windowId); + control->SetBorderColor(m_windowColor); + control->Release(); + } +} + +QRect Vmr9VideoWindowControl::displayRect() const +{ + return m_displayRect; +} + +void Vmr9VideoWindowControl::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + if (IVMRWindowlessControl9 *control = com_cast( + m_filter, IID_IVMRWindowlessControl9)) { + RECT sourceRect = { 0, 0, 0, 0 }; + RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 }; + + control->GetNativeVideoSize(&sourceRect.right, &sourceRect.bottom, 0, 0); + + if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { + QSize clippedSize = rect.size(); + clippedSize.scale(sourceRect.right, sourceRect.bottom, Qt::KeepAspectRatio); + + sourceRect.left = (sourceRect.right - clippedSize.width()) / 2; + sourceRect.top = (sourceRect.bottom - clippedSize.height()) / 2; + sourceRect.right = sourceRect.left + clippedSize.width(); + sourceRect.bottom = sourceRect.top + clippedSize.height(); + } + + control->SetVideoPosition(&sourceRect, &displayRect); + control->Release(); + } +} + +bool Vmr9VideoWindowControl::isFullScreen() const +{ + return m_fullScreen; +} + +void Vmr9VideoWindowControl::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +void Vmr9VideoWindowControl::repaint() +{ + PAINTSTRUCT paint; + + if (HDC dc = ::BeginPaint(m_windowId, &paint)) { + HRESULT hr = E_FAIL; + + if (IVMRWindowlessControl9 *control = com_cast( + m_filter, IID_IVMRWindowlessControl9)) { + hr = control->RepaintVideo(m_windowId, dc); + control->Release(); + } + + if (!SUCCEEDED(hr)) { + HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor); + HBRUSH brush = ::CreateSolidBrush(m_windowColor); + ::SelectObject(dc, pen); + ::SelectObject(dc, brush); + + ::Rectangle( + dc, + m_displayRect.left(), + m_displayRect.top(), + m_displayRect.right() + 1, + m_displayRect.bottom() + 1); + + ::DeleteObject(pen); + ::DeleteObject(brush); + } + ::EndPaint(m_windowId, &paint); + } +} + +QSize Vmr9VideoWindowControl::nativeSize() const +{ + QSize size; + + if (IVMRWindowlessControl9 *control = com_cast( + m_filter, IID_IVMRWindowlessControl9)) { + LONG width; + LONG height; + + if (control->GetNativeVideoSize(&width, &height, 0, 0) == S_OK) + size = QSize(width, height); + control->Release(); + } + return size; +} + +Qt::AspectRatioMode Vmr9VideoWindowControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void Vmr9VideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + if (IVMRWindowlessControl9 *control = com_cast( + m_filter, IID_IVMRWindowlessControl9)) { + switch (mode) { + case Qt::IgnoreAspectRatio: + control->SetAspectRatioMode(VMR9ARMode_None); + break; + case Qt::KeepAspectRatio: + control->SetAspectRatioMode(VMR9ARMode_LetterBox); + break; + case Qt::KeepAspectRatioByExpanding: + control->SetAspectRatioMode(VMR9ARMode_LetterBox); + setDisplayRect(m_displayRect); + break; + default: + break; + } + control->Release(); + } +} + +int Vmr9VideoWindowControl::brightness() const +{ + return m_brightness; +} + +void Vmr9VideoWindowControl::setBrightness(int brightness) +{ + m_brightness = brightness; + + m_dirtyValues |= ProcAmpControl9_Brightness; + + setProcAmpValues(); + + emit brightnessChanged(brightness); +} + +int Vmr9VideoWindowControl::contrast() const +{ + return m_contrast; +} + +void Vmr9VideoWindowControl::setContrast(int contrast) +{ + m_contrast = contrast; + + m_dirtyValues |= ProcAmpControl9_Contrast; + + setProcAmpValues(); + + emit contrastChanged(contrast); +} + +int Vmr9VideoWindowControl::hue() const +{ + return m_hue; +} + +void Vmr9VideoWindowControl::setHue(int hue) +{ + m_hue = hue; + + m_dirtyValues |= ProcAmpControl9_Hue; + + setProcAmpValues(); + + emit hueChanged(hue); +} + +int Vmr9VideoWindowControl::saturation() const +{ + return m_saturation; +} + +void Vmr9VideoWindowControl::setSaturation(int saturation) +{ + m_saturation = saturation; + + m_dirtyValues |= ProcAmpControl9_Saturation; + + setProcAmpValues(); + + emit saturationChanged(saturation); +} + +void Vmr9VideoWindowControl::setProcAmpValues() +{ + if (IVMRMixerControl9 *control = com_cast(m_filter, IID_IVMRMixerControl9)) { + VMR9ProcAmpControl procAmp; + procAmp.dwSize = sizeof(VMR9ProcAmpControl); + procAmp.dwFlags = m_dirtyValues; + + if (m_dirtyValues & ProcAmpControl9_Brightness) { + procAmp.Brightness = scaleProcAmpValue( + control, ProcAmpControl9_Brightness, m_brightness); + } + if (m_dirtyValues & ProcAmpControl9_Contrast) { + procAmp.Contrast = scaleProcAmpValue( + control, ProcAmpControl9_Contrast, m_contrast); + } + if (m_dirtyValues & ProcAmpControl9_Hue) { + procAmp.Hue = scaleProcAmpValue( + control, ProcAmpControl9_Hue, m_hue); + } + if (m_dirtyValues & ProcAmpControl9_Saturation) { + procAmp.Saturation = scaleProcAmpValue( + control, ProcAmpControl9_Saturation, m_saturation); + } + + if (SUCCEEDED(control->SetProcAmpControl(0, &procAmp))) { + m_dirtyValues = 0; + } + + control->Release(); + } +} + +float Vmr9VideoWindowControl::scaleProcAmpValue( + IVMRMixerControl9 *control, VMR9ProcAmpControlFlags property, int value) const +{ + float scaledValue = 0.0; + + VMR9ProcAmpControlRange range; + range.dwSize = sizeof(VMR9ProcAmpControlRange); + range.dwProperty = property; + + if (SUCCEEDED(control->GetProcAmpControlRange(0, &range))) { + scaledValue = range.DefaultValue; + if (value > 0) + scaledValue += float(value) * (range.MaxValue - range.DefaultValue) / 100; + else if (value < 0) + scaledValue -= float(value) * (range.MinValue - range.DefaultValue) / 100; + } + + return scaledValue; +} diff --git a/src/plugins/directshow/player/vmr9videowindowcontrol.h b/src/plugins/directshow/player/vmr9videowindowcontrol.h new file mode 100644 index 000000000..b4e39a7fd --- /dev/null +++ b/src/plugins/directshow/player/vmr9videowindowcontrol.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VMR9VIDEOWINDOWCONTROL_H +#define VMR9VIDEOWINDOWCONTROL_H + +#include "qvideowindowcontrol.h" + +#include +#include +#include + +QT_USE_NAMESPACE + +class Vmr9VideoWindowControl : public QVideoWindowControl +{ + Q_OBJECT +public: + Vmr9VideoWindowControl(QObject *parent = 0); + ~Vmr9VideoWindowControl(); + + IBaseFilter *filter() const { return m_filter; } + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + void repaint(); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + +private: + void setProcAmpValues(); + float scaleProcAmpValue( + IVMRMixerControl9 *control, VMR9ProcAmpControlFlags property, int value) const; + + IBaseFilter *m_filter; + WId m_windowId; + COLORREF m_windowColor; + DWORD m_dirtyValues; + Qt::AspectRatioMode m_aspectRatioMode; + QRect m_displayRect; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; + bool m_fullScreen; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabin/camerabin.pri b/src/plugins/gstreamer/camerabin/camerabin.pri new file mode 100644 index 000000000..5c266e784 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabin.pri @@ -0,0 +1,50 @@ +INCLUDEPATH += $$PWD \ + $${SOURCE_DIR}/src/multimedia + +INCLUDEPATH += camerabin + +DEFINES += QMEDIA_GSTREAMER_CAMERABIN + +LIBS += -lgstphotography-0.10 + +DEFINES += GST_USE_UNSTABLE_API #prevents warnings because of unstable photography API + +HEADERS += \ + $$PWD/camerabinservice.h \ + $$PWD/camerabinsession.h \ + $$PWD/camerabincontrol.h \ + $$PWD/camerabinaudioencoder.h \ + $$PWD/camerabinfocus.h \ + $$PWD/camerabinimageencoder.h \ + $$PWD/camerabinlocks.h \ + $$PWD/camerabinrecorder.h \ + $$PWD/camerabincontainer.h \ + $$PWD/camerabinexposure.h \ + $$PWD/camerabinflash.h \ + $$PWD/camerabinimagecapture.h \ + $$PWD/camerabinimageprocessing.h \ + $$PWD/camerabinmetadata.h \ + $$PWD/camerabinvideoencoder.h \ + $$PWD/camerabinresourcepolicy.h \ + $$PWD/camerabincapturedestination.h \ + $$PWD/camerabincapturebufferformat.h + +SOURCES += \ + $$PWD/camerabinservice.cpp \ + $$PWD/camerabinsession.cpp \ + $$PWD/camerabincontrol.cpp \ + $$PWD/camerabinaudioencoder.cpp \ + $$PWD/camerabincontainer.cpp \ + $$PWD/camerabinexposure.cpp \ + $$PWD/camerabinflash.cpp \ + $$PWD/camerabinfocus.cpp \ + $$PWD/camerabinimagecapture.cpp \ + $$PWD/camerabinimageencoder.cpp \ + $$PWD/camerabinimageprocessing.cpp \ + $$PWD/camerabinlocks.cpp \ + $$PWD/camerabinmetadata.cpp \ + $$PWD/camerabinrecorder.cpp \ + $$PWD/camerabinvideoencoder.cpp \ + $$PWD/camerabinresourcepolicy.cpp \ + $$PWD/camerabincapturedestination.cpp \ + $$PWD/camerabincapturebufferformat.cpp diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp new file mode 100644 index 000000000..d028fb3e7 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.cpp @@ -0,0 +1,293 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinaudioencoder.h" +#include "camerabincontainer.h" + +#include + +CameraBinAudioEncoder::CameraBinAudioEncoder(QObject *parent) + :QAudioEncoderControl(parent) +{ + QList codecCandidates; + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + codecCandidates << "audio/AAC" << "audio/PCM" << "audio/AMR" << "audio/AMR-WB" << "audio/speex" + << "audio/ADPCM" << "audio/iLBC" << "audio/vorbis" << "audio/mpeg" << "audio/FLAC"; + + m_elementNames["audio/AAC"] = "nokiaaacenc"; + m_elementNames["audio/speex"] = "speexenc"; + m_elementNames["audio/PCM"] = "audioresample"; + m_elementNames["audio/AMR"] = "nokiaamrnbenc"; + m_elementNames["audio/AMR-WB"] = "nokiaamrwbenc"; + m_elementNames["audio/ADPCM"] = "nokiaadpcmenc"; + m_elementNames["audio/iLBC"] = "nokiailbcenc"; + m_elementNames["audio/vorbis"] = "vorbisenc"; + m_elementNames["audio/FLAC"] = "flacenc"; + m_elementNames["audio/mpeg"] = "ffenc_mp2"; +#else + codecCandidates << "audio/mpeg" << "audio/vorbis" << "audio/speex" << "audio/GSM" + << "audio/PCM" << "audio/AMR" << "audio/AMR-WB"; + + m_elementNames["audio/mpeg"] = "lamemp3enc"; + m_elementNames["audio/vorbis"] = "vorbisenc"; + m_elementNames["audio/speex"] = "speexenc"; + m_elementNames["audio/GSM"] = "gsmenc"; + m_elementNames["audio/PCM"] = "audioresample"; + m_elementNames["audio/AMR"] = "amrnbenc"; + m_elementNames["audio/AMR-WB"] = "amrwbenc"; + + m_codecOptions["audio/vorbis"] = QStringList() << "min-bitrate" << "max-bitrate"; + m_codecOptions["audio/mpeg"] = QStringList() << "mode"; + m_codecOptions["audio/speex"] = QStringList() << "mode" << "vbr" << "vad" << "dtx"; + m_codecOptions["audio/GSM"] = QStringList(); + m_codecOptions["audio/PCM"] = QStringList(); + m_codecOptions["audio/AMR"] = QStringList(); + m_codecOptions["audio/AMR-WB"] = QStringList(); +#endif + + foreach( const QByteArray& codecName, codecCandidates ) { + QByteArray elementName = m_elementNames[codecName]; + GstElementFactory *factory = gst_element_factory_find(elementName.constData()); + + if (factory) { + m_codecs.append(codecName); + const gchar *descr = gst_element_factory_get_description(factory); + + if (codecName == QByteArray("audio/PCM")) + m_codecDescriptions.insert(codecName, tr("Raw PCM audio")); + else + m_codecDescriptions.insert(codecName, QString::fromUtf8(descr)); + + m_streamTypes.insert(codecName, + CameraBinContainer::supportedStreamTypes(factory, GST_PAD_SRC)); + + gst_object_unref(GST_OBJECT(factory)); + } + } +} + +CameraBinAudioEncoder::~CameraBinAudioEncoder() +{ +} + +QStringList CameraBinAudioEncoder::supportedAudioCodecs() const +{ + return m_codecs; +} + +QString CameraBinAudioEncoder::codecDescription(const QString &codecName) const +{ + return m_codecDescriptions.value(codecName); +} + +QStringList CameraBinAudioEncoder::supportedEncodingOptions(const QString &codec) const +{ + return m_codecOptions.value(codec); +} + +QVariant CameraBinAudioEncoder::encodingOption( + const QString &codec, const QString &name) const +{ + return m_options[codec].value(name); +} + +void CameraBinAudioEncoder::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + m_options[codec][name] = value; +} + +QList CameraBinAudioEncoder::supportedSampleRates(const QAudioEncoderSettings &, bool *) const +{ + //TODO check element caps to find actual values + + return QList(); +} + +QAudioEncoderSettings CameraBinAudioEncoder::audioSettings() const +{ + return m_audioSettings; +} + +void CameraBinAudioEncoder::setAudioSettings(const QAudioEncoderSettings &settings) +{ + m_userSettings = settings; + m_audioSettings = settings; + emit settingsChanged(); +} + +void CameraBinAudioEncoder::setActualAudioSettings(const QAudioEncoderSettings &settings) +{ + m_audioSettings = settings; +} + +void CameraBinAudioEncoder::resetActualSettings() +{ + m_audioSettings = m_userSettings; +} + +GstElement *CameraBinAudioEncoder::createEncoder() +{ + QString codec = m_audioSettings.codec(); + QByteArray encoderElementName = m_elementNames.value(codec); + GstElement *encoderElement = gst_element_factory_make(encoderElementName.constData(), NULL); + if (!encoderElement) + return 0; + + GstBin * encoderBin = GST_BIN(gst_bin_new("audio-encoder-bin")); + GstElement *capsFilter = gst_element_factory_make("capsfilter", NULL); + + gst_bin_add(encoderBin, capsFilter); + gst_bin_add(encoderBin, encoderElement); + gst_element_link(capsFilter, encoderElement); + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(capsFilter, "sink"); + gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + pad = gst_element_get_static_pad(encoderElement, "src"); + gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad)); + gst_object_unref(GST_OBJECT(pad)); + + if (m_audioSettings.sampleRate() > 0 || m_audioSettings.channelCount() > 0) { + GstCaps *caps = gst_caps_new_empty(); + GstStructure *structure = gst_structure_new("audio/x-raw-int", NULL); + + if (m_audioSettings.sampleRate() > 0) + gst_structure_set(structure, "rate", G_TYPE_INT, m_audioSettings.sampleRate(), NULL ); + + if (m_audioSettings.channelCount() > 0) + gst_structure_set(structure, "channels", G_TYPE_INT, m_audioSettings.channelCount(), NULL ); + + gst_caps_append_structure(caps,structure); + + g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); + } + + if (encoderElement) { + if (m_audioSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) { + QtMultimediaKit::EncodingQuality qualityValue = m_audioSettings.quality(); + + if (encoderElementName == "lamemp3enc") { + g_object_set(G_OBJECT(encoderElement), "target", 0, NULL); //constant quality mode + qreal quality[] = { + 10.0, //VeryLow + 6.0, //Low + 4.0, //Normal + 2.0, //High + 0.0 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quality", quality[qualityValue], NULL); + } else if (encoderElementName == "ffenc_mp2") { + int quality[] = { + 8000, //VeryLow + 64000, //Low + 128000, //Normal + 192000, //High + 320000 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "bitrate", quality[qualityValue], NULL); + } else if (codec == QLatin1String("audio/speex")) { + //0-10 range with default 8 + double qualityTable[] = { + 2, //VeryLow + 5, //Low + 8, //Normal + 9, //High + 10 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL); + } else if (codec.startsWith("audio/AMR")) { + int band[] = { + 0, //VeryLow + 2, //Low + 4, //Normal + 6, //High + 7 //VeryHigh + }; + + g_object_set(G_OBJECT(encoderElement), "band-mode", band[qualityValue], NULL); + } + } else { + int bitrate = m_audioSettings.bitRate(); + if (bitrate > 0) { + g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL); + } + } + + QMap options = m_options.value(codec); + QMapIterator it(options); + while (it.hasNext()) { + it.next(); + QString option = it.key(); + QVariant value = it.value(); + + switch (value.type()) { + case QVariant::Int: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toInt(), NULL); + break; + case QVariant::Bool: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toBool(), NULL); + break; + case QVariant::Double: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toDouble(), NULL); + break; + case QVariant::String: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toString().toUtf8().constData(), NULL); + break; + default: + qWarning() << "unsupported option type:" << option << value; + break; + } + } + } + + return GST_ELEMENT(encoderBin); + +} + + +QSet CameraBinAudioEncoder::supportedStreamTypes(const QString &codecName) const +{ + return m_streamTypes.value(codecName); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h new file mode 100644 index 000000000..1790fc37d --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinaudioencoder.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINAUDIOENCODE_H +#define CAMERABINAUDIOENCODE_H + +#include +class CameraBinSession; + +#include +#include +#include + +#include + +#include + +QT_USE_NAMESPACE + +class CameraBinAudioEncoder : public QAudioEncoderControl +{ + Q_OBJECT +public: + CameraBinAudioEncoder(QObject *parent); + virtual ~CameraBinAudioEncoder(); + + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + + QList supportedSampleRates(const QAudioEncoderSettings &settings = QAudioEncoderSettings(), + bool *isContinuous = 0) const; + QList supportedChannelCounts(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const; + QList supportedSampleSizes(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const; + + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings&); + + + GstElement *createEncoder(); + + QSet supportedStreamTypes(const QString &codecName) const; + + void setActualAudioSettings(const QAudioEncoderSettings&); + void resetActualSettings(); + +Q_SIGNALS: + void settingsChanged(); + +private: + QStringList m_codecs; + QMap m_elementNames; + QMap m_codecDescriptions; + QMap m_codecOptions; + + QMap > m_options; + + QMap > m_streamTypes; + + QAudioEncoderSettings m_audioSettings; + QAudioEncoderSettings m_userSettings; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp b/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp new file mode 100644 index 000000000..b99024f22 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabincapturebufferformat.h" +#include "camerabinsession.h" + +CameraBinCaptureBufferFormat::CameraBinCaptureBufferFormat(CameraBinSession *session) + :QCameraCaptureBufferFormatControl(session) + , m_session(session) + , m_format(QVideoFrame::Format_Jpeg) +{ +} + +CameraBinCaptureBufferFormat::~CameraBinCaptureBufferFormat() +{ +} + +QList CameraBinCaptureBufferFormat::supportedBufferFormats() const +{ + //the exact YUV format is unknown with camerabin until the first capture is requested + return QList() + << QVideoFrame::Format_Jpeg +#ifdef Q_WS_MAEMO_6 + << QVideoFrame::Format_UYVY +#endif + ; +} + +QVideoFrame::PixelFormat CameraBinCaptureBufferFormat::bufferFormat() const +{ + return m_format; +} + +void CameraBinCaptureBufferFormat::setBufferFormat(QVideoFrame::PixelFormat format) +{ + if (m_format != format) { + m_format = format; + emit bufferFormatChanged(format); + } +} diff --git a/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h b/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h new file mode 100644 index 000000000..579ed239e --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincapturebufferformat.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINCAPTUREBUFFERFORMAT_H +#define CAMERABINCAPTUREBUFFERFORMAT_H + +#include +#include + +#include +#include + +class CameraBinSession; + +QT_USE_NAMESPACE + +class Q_MULTIMEDIA_EXPORT CameraBinCaptureBufferFormat : public QCameraCaptureBufferFormatControl +{ + Q_OBJECT +public: + CameraBinCaptureBufferFormat(CameraBinSession *session); + virtual ~CameraBinCaptureBufferFormat(); + + QList supportedBufferFormats() const; + + QVideoFrame::PixelFormat bufferFormat() const; + void setBufferFormat(QVideoFrame::PixelFormat format); + +private: + CameraBinSession *m_session; + QVideoFrame::PixelFormat m_format; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp b/src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp new file mode 100644 index 000000000..2a83637b4 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincapturedestination.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabincapturedestination.h" +#include "camerabinsession.h" + +CameraBinCaptureDestination::CameraBinCaptureDestination(CameraBinSession *session) + :QCameraCaptureDestinationControl(session) + , m_session(session) + , m_destination(QCameraImageCapture::CaptureToFile) +{ +} + +CameraBinCaptureDestination::~CameraBinCaptureDestination() +{ +} + + +bool CameraBinCaptureDestination::isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const +{ + //capture to buffer, file and both are supported. + return destination & (QCameraImageCapture::CaptureToFile | QCameraImageCapture::CaptureToBuffer); +} + +QCameraImageCapture::CaptureDestinations CameraBinCaptureDestination::captureDestination() const +{ + return m_destination; +} + +void CameraBinCaptureDestination::setCaptureDestination(QCameraImageCapture::CaptureDestinations destination) +{ + if (m_destination != destination) { + m_destination = destination; + emit captureDestinationChanged(m_destination); + } +} diff --git a/src/plugins/gstreamer/camerabin/camerabincapturedestination.h b/src/plugins/gstreamer/camerabin/camerabincapturedestination.h new file mode 100644 index 000000000..92c02f1b0 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincapturedestination.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINCAPTUREDESTINATION_H +#define CAMERABINCAPTUREDESTINATION_H + +#include +#include + + +class CameraBinSession; + +QT_USE_NAMESPACE + +class Q_MULTIMEDIA_EXPORT CameraBinCaptureDestination : public QCameraCaptureDestinationControl +{ + Q_OBJECT +public: + CameraBinCaptureDestination(CameraBinSession *session); + virtual ~CameraBinCaptureDestination(); + + bool isCaptureDestinationSupported(QCameraImageCapture::CaptureDestinations destination) const; + QCameraImageCapture::CaptureDestinations captureDestination() const; + void setCaptureDestination(QCameraImageCapture::CaptureDestinations destination); + +private: + CameraBinSession *m_session; + QCameraImageCapture::CaptureDestinations m_destination; +}; + +#endif // CAMERABINFLASHCONTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabincontainer.cpp b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp new file mode 100644 index 000000000..97349204d --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincontainer.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabincontainer.h" + + +#include + +CameraBinContainer::CameraBinContainer(QObject *parent) + :QMediaContainerControl(parent) +{ + QList formatCandidates; + formatCandidates << "mp4" << "ogg" << "wav" << "amr" << "mkv" + << "avi" << "3gp" << "3gp2" << "webm" << "mjpeg" << "asf" << "mov"; + + QMap elementNames; + + elementNames.insertMulti("mp4", "ffmux_mp4"); + elementNames.insertMulti("mp4", "hantromp4mux"); + elementNames.insertMulti("mp4", "mp4mux"); + elementNames.insert("ogg", "oggmux"); + elementNames["wav"] = "wavenc"; + elementNames["amr"] = "ffmux_amr"; + elementNames["mkv"] = "matroskamux"; + elementNames["avi"] = "avimux"; + elementNames["3gp"] = "ffmux_3gp"; + elementNames["3gp2"] = "ffmux_3g2"; + elementNames["webm"] = "webmmux"; + elementNames["mjpeg"] = "ffmux_mjpeg"; + elementNames["asf"] = "ffmux_asf"; + elementNames["mov"] = "qtmux"; + + QSet allTypes; + + foreach(const QByteArray &formatName, formatCandidates) { + foreach(const QByteArray &elementName, elementNames.values(formatName)) { + GstElementFactory *factory = gst_element_factory_find(elementName.constData()); + if (factory) { + m_supportedContainers.append(formatName); + const gchar *descr = gst_element_factory_get_description(factory); + m_containerDescriptions.insert(formatName, QString::fromUtf8(descr)); + + + if (formatName == QByteArray("raw")) { + m_streamTypes.insert(formatName, allTypes); + } else { + QSet types = supportedStreamTypes(factory, GST_PAD_SINK); + m_streamTypes.insert(formatName, types); + allTypes.unite(types); + } + + gst_object_unref(GST_OBJECT(factory)); + + m_elementNames.insert(formatName, elementName); + break; + } + } + } +} + +QSet CameraBinContainer::supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction) +{ + QSet types; + const GList *pads = gst_element_factory_get_static_pad_templates(factory); + for (const GList *pad = pads; pad; pad = g_list_next(pad)) { + GstStaticPadTemplate *templ = (GstStaticPadTemplate*)pad->data; + if (templ->direction == direction) { + GstCaps *caps = gst_static_caps_get(&templ->static_caps); + for (uint i=0; i CameraBinContainer::supportedStreamTypes(const QString &container) const +{ + return m_streamTypes.value(container); +} diff --git a/src/plugins/gstreamer/camerabin/camerabincontainer.h b/src/plugins/gstreamer/camerabin/camerabincontainer.h new file mode 100644 index 000000000..3eac48342 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincontainer.h @@ -0,0 +1,103 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef CAMERABINMEDIACONTAINERCONTROL_H +#define CAMERABINMEDIACONTAINERCONTROL_H + +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +class CameraBinContainer : public QMediaContainerControl +{ +Q_OBJECT +public: + CameraBinContainer(QObject *parent); + virtual ~CameraBinContainer() {} + + virtual QStringList supportedContainers() const { return m_supportedContainers; } + virtual QString containerMimeType() const { return m_format; } + virtual void setContainerMimeType(const QString &formatMimeType) + { + m_format = formatMimeType; + + if (m_userFormat != formatMimeType) { + m_userFormat = formatMimeType; + emit settingsChanged(); + } + } + + void setActualContainer(const QString &formatMimeType) + { + m_format = formatMimeType; + } + + void resetActualContainer() + { + m_format = m_userFormat; + } + + virtual QString containerDescription(const QString &formatMimeType) const { return m_containerDescriptions.value(formatMimeType); } + + QByteArray formatElementName() const { return m_elementNames.value(containerMimeType()); } + + QSet supportedStreamTypes(const QString &container) const; + + static QSet supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction); + +Q_SIGNALS: + void settingsChanged(); + +private: + QString m_format; // backend selected format, using m_userFormat + QString m_userFormat; + QStringList m_supportedContainers; + QMap m_elementNames; + QMap m_containerDescriptions; + QMap > m_streamTypes; +}; + +#endif // CAMERABINMEDIACONTAINERCONTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.cpp b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp new file mode 100644 index 000000000..2c30fd656 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincontrol.cpp @@ -0,0 +1,356 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabincontrol.h" +#include "camerabincontainer.h" +#include "camerabinaudioencoder.h" +#include "camerabinvideoencoder.h" +#include "camerabinimageencoder.h" +#include "camerabinresourcepolicy.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//#define CAMEABIN_DEBUG 1 +#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) + +CameraBinControl::CameraBinControl(CameraBinSession *session) + :QCameraControl(session), + m_session(session), + m_state(QCamera::UnloadedState), + m_status(QCamera::UnloadedStatus), + m_reloadPending(false) +{ + connect(m_session, SIGNAL(stateChanged(QCamera::State)), + this, SLOT(updateStatus())); + + connect(m_session->audioEncodeControl(), SIGNAL(settingsChanged()), + SLOT(reloadLater())); + connect(m_session->videoEncodeControl(), SIGNAL(settingsChanged()), + SLOT(reloadLater())); + connect(m_session->mediaContainerControl(), SIGNAL(settingsChanged()), + SLOT(reloadLater())); + connect(m_session->imageEncodeControl(), SIGNAL(settingsChanged()), + SLOT(reloadLater())); + connect(m_session, SIGNAL(viewfinderChanged()), + SLOT(reloadLater())); + connect(m_session, SIGNAL(readyChanged(bool)), + SLOT(reloadLater())); + connect(m_session, SIGNAL(error(int,QString)), + SLOT(handleCameraError(int,QString))); + + m_resourcePolicy = new CamerabinResourcePolicy(this); + connect(m_resourcePolicy, SIGNAL(resourcesGranted()), + SLOT(handleResourcesGranted())); + connect(m_resourcePolicy, SIGNAL(resourcesDenied()), + SLOT(handleResourcesLost())); + connect(m_resourcePolicy, SIGNAL(resourcesLost()), + SLOT(handleResourcesLost())); + + connect(m_session, SIGNAL(busyChanged(bool)), + SLOT(handleBusyChanged(bool))); +} + +CameraBinControl::~CameraBinControl() +{ +} + +QCamera::CaptureMode CameraBinControl::captureMode() const +{ + return m_session->captureMode(); +} + +void CameraBinControl::setCaptureMode(QCamera::CaptureMode mode) +{ + if (m_session->captureMode() != mode) { + m_session->setCaptureMode(mode); + reloadLater(); + + if (m_state == QCamera::ActiveState) { + m_resourcePolicy->setResourceSet( + captureMode() == QCamera::CaptureStillImage ? + CamerabinResourcePolicy::ImageCaptureResources : + CamerabinResourcePolicy::VideoCaptureResources); + } + emit captureModeChanged(mode); + } +} + +bool CameraBinControl::isCaptureModeSupported(QCamera::CaptureMode mode) const +{ +#ifdef Q_WS_MAEMO_5 + //Front camera on N900 supports only video capture + if (m_session->cameraRole() == CameraBinSession::FrontCamera) + return mode == QCamera::CaptureVideo; +#endif + + return mode == QCamera::CaptureStillImage || mode == QCamera::CaptureVideo; +} + +void CameraBinControl::setState(QCamera::State state) +{ +#ifdef CAMEABIN_DEBUG + qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", state); +#endif + if (m_state != state) { + m_state = state; + + //special case for stopping the camera while it's busy, + //it should be delayed until the camera is idle + if (state == QCamera::LoadedState && + m_session->state() == QCamera::ActiveState && + m_session->isBusy()) { +#ifdef CAMEABIN_DEBUG + qDebug() << Q_FUNC_INFO << "Camera is busy, QCamera::stop() is delayed"; +#endif + emit stateChanged(m_state); + return; + } + + CamerabinResourcePolicy::ResourceSet resourceSet; + switch (state) { + case QCamera::UnloadedState: + resourceSet = CamerabinResourcePolicy::NoResources; + break; + case QCamera::LoadedState: + resourceSet = CamerabinResourcePolicy::LoadedResources; + break; + case QCamera::ActiveState: + resourceSet = captureMode() == QCamera::CaptureStillImage ? + CamerabinResourcePolicy::ImageCaptureResources : + CamerabinResourcePolicy::VideoCaptureResources; + break; + } + + m_resourcePolicy->setResourceSet(resourceSet); + + if (m_resourcePolicy->isResourcesGranted()) { + //postpone changing to Active if the session is nor ready yet + if (state == QCamera::ActiveState) { + if (m_session->isReady()) { + m_session->setState(state); + } else { +#ifdef CAMEABIN_DEBUG + qDebug() << "Camera session is not ready yet, postpone activating"; +#endif + } + } else + m_session->setState(state); + } + + emit stateChanged(m_state); + } +} + +QCamera::State CameraBinControl::state() const +{ + return m_state; +} + +void CameraBinControl::updateStatus() +{ + QCamera::State sessionState = m_session->state(); + QCamera::Status oldStatus = m_status; + + switch (m_state) { + case QCamera::UnloadedState: + m_status = QCamera::UnloadedStatus; + break; + case QCamera::LoadedState: + switch (sessionState) { + case QCamera::UnloadedState: + m_status = QCamera::LoadingStatus; + break; + case QCamera::LoadedState: + m_status = QCamera::LoadedStatus; + break; + case QCamera::ActiveState: + m_status = QCamera::ActiveStatus; + break; + } + break; + case QCamera::ActiveState: + switch (sessionState) { + case QCamera::UnloadedState: + m_status = QCamera::LoadingStatus; + break; + case QCamera::LoadedState: + m_status = QCamera::StartingStatus; + break; + case QCamera::ActiveState: + m_status = QCamera::ActiveStatus; + break; + } + } + + if (m_status != oldStatus) { +#ifdef CAMEABIN_DEBUG + qDebug() << "Camera status changed" << ENUM_NAME(QCamera, "Status", m_status); +#endif + emit statusChanged(m_status); + } +} + +void CameraBinControl::reloadLater() +{ +#ifdef CAMEABIN_DEBUG + qDebug() << "CameraBinControl: reload pipeline requested" << ENUM_NAME(QCamera, "State", m_state); +#endif + if (!m_reloadPending && m_state == QCamera::ActiveState) { + m_reloadPending = true; + + if (!m_session->isBusy()) { + m_session->setState(QCamera::LoadedState); + QMetaObject::invokeMethod(this, "delayedReload", Qt::QueuedConnection); + } + } +} + +void CameraBinControl::handleResourcesLost() +{ +#ifdef CAMEABIN_DEBUG + qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", m_state); +#endif + m_session->setState(QCamera::UnloadedState); +} + +void CameraBinControl::handleResourcesGranted() +{ +#ifdef CAMEABIN_DEBUG + qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", m_state); +#endif + + //camera will be started soon by delayedReload() + if (m_reloadPending && m_state == QCamera::ActiveState) + return; + + if (m_state == QCamera::ActiveState && m_session->isReady()) + m_session->setState(QCamera::ActiveState); + else if (m_state == QCamera::LoadedState) + m_session->setState(QCamera::LoadedState); +} + +void CameraBinControl::handleBusyChanged(bool busy) +{ + if (!busy && m_session->state() == QCamera::ActiveState) { + if (m_state == QCamera::LoadedState) { + //handle delayed stop() because of busy camera + m_resourcePolicy->setResourceSet(CamerabinResourcePolicy::LoadedResources); + m_session->setState(QCamera::LoadedState); + } else if (m_state == QCamera::ActiveState && m_reloadPending) { + //handle delayed reload because of busy camera + m_session->setState(QCamera::LoadedState); + QMetaObject::invokeMethod(this, "delayedReload", Qt::QueuedConnection); + } + } +} + +void CameraBinControl::handleCameraError(int errorCode, const QString &errorString) +{ + emit error(errorCode, errorString); + setState(QCamera::UnloadedState); +} + +void CameraBinControl::delayedReload() +{ +#ifdef CAMEABIN_DEBUG + qDebug() << "CameraBinControl: reload pipeline"; +#endif + if (m_reloadPending) { + m_reloadPending = false; + if (m_state == QCamera::ActiveState && + m_session->isReady() && + m_resourcePolicy->isResourcesGranted()) { + m_session->setState(QCamera::ActiveState); + } + } +} + +bool CameraBinControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const +{ + Q_UNUSED(status); + + switch (changeType) { + case QCameraControl::CaptureMode: + case QCameraControl::ImageEncodingSettings: + case QCameraControl::VideoEncodingSettings: + case QCameraControl::Viewfinder: + return true; + default: + return false; + } +} + +#define VIEWFINDER_COLORSPACE_CONVERSION 0x00000004 + +bool CameraBinControl::viewfinderColorSpaceConversion() const +{ + gint flags = 0; + g_object_get(G_OBJECT(m_session->cameraBin()), "flags", &flags, NULL); + + return flags & VIEWFINDER_COLORSPACE_CONVERSION; +} + +void CameraBinControl::setViewfinderColorSpaceConversion(bool enabled) +{ + gint flags = 0; + g_object_get(G_OBJECT(m_session->cameraBin()), "flags", &flags, NULL); + + if (enabled) + flags |= VIEWFINDER_COLORSPACE_CONVERSION; + else + flags &= ~VIEWFINDER_COLORSPACE_CONVERSION; + + g_object_set(G_OBJECT(m_session->cameraBin()), "flags", flags, NULL); +} diff --git a/src/plugins/gstreamer/camerabin/camerabincontrol.h b/src/plugins/gstreamer/camerabin/camerabincontrol.h new file mode 100644 index 000000000..1e5f28e01 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabincontrol.h @@ -0,0 +1,101 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef CAMERABINCONTROL_H +#define CAMERABINCONTROL_H + +#include +#include +#include "camerabinsession.h" + +QT_USE_NAMESPACE + +class CamerabinResourcePolicy; + +class CameraBinControl : public QCameraControl +{ + Q_OBJECT + Q_PROPERTY(bool viewfinderColorSpaceConversion READ viewfinderColorSpaceConversion WRITE setViewfinderColorSpaceConversion) +public: + CameraBinControl( CameraBinSession *session ); + virtual ~CameraBinControl(); + + bool isValid() const { return true; } + + QCamera::State state() const; + void setState(QCamera::State state); + + QCamera::Status status() const { return m_status; } + + QCamera::CaptureMode captureMode() const; + void setCaptureMode(QCamera::CaptureMode mode); + + bool isCaptureModeSupported(QCamera::CaptureMode mode) const; + bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const; + bool viewfinderColorSpaceConversion() const; + +public slots: + void reloadLater(); + void setViewfinderColorSpaceConversion(bool enabled); + +private slots: + void updateStatus(); + void delayedReload(); + + void handleResourcesGranted(); + void handleResourcesLost(); + + void handleBusyChanged(bool); + void handleCameraError(int error, const QString &errorString); + +private: + void updateSupportedResolutions(const QString &device); + + CameraBinSession *m_session; + QCamera::State m_state; + QCamera::Status m_status; + CamerabinResourcePolicy *m_resourcePolicy; + + bool m_reloadPending; +}; + +#endif // CAMERABINCONTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabinexposure.cpp b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp new file mode 100644 index 000000000..3401d07fc --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinexposure.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinexposure.h" +#include "camerabinsession.h" +#include + +#include + +CameraBinExposure::CameraBinExposure(CameraBinSession *session) + :QCameraExposureControl(session), + m_session(session) +{ +} + +CameraBinExposure::~CameraBinExposure() +{ +} + +QCameraExposure::ExposureMode CameraBinExposure::exposureMode() const +{ + GstSceneMode sceneMode; + gst_photography_get_scene_mode(m_session->photography(), &sceneMode); + + switch (sceneMode) { + case GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT: return QCameraExposure::ExposurePortrait; + case GST_PHOTOGRAPHY_SCENE_MODE_SPORT: return QCameraExposure::ExposureSports; + case GST_PHOTOGRAPHY_SCENE_MODE_NIGHT: return QCameraExposure::ExposureNight; + case GST_PHOTOGRAPHY_SCENE_MODE_MANUAL: return QCameraExposure::ExposureManual; + case GST_PHOTOGRAPHY_SCENE_MODE_CLOSEUP: //no direct mapping available so mapping to auto mode + case GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE: //no direct mapping available so mapping to auto mode + case GST_PHOTOGRAPHY_SCENE_MODE_AUTO: + default: + return QCameraExposure::ExposureAuto; + } +} + +void CameraBinExposure::setExposureMode(QCameraExposure::ExposureMode mode) +{ + GstSceneMode sceneMode; + gst_photography_get_scene_mode(m_session->photography(), &sceneMode); + + switch (mode) { + case QCameraExposure::ExposureManual: sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_MANUAL; break; + case QCameraExposure::ExposurePortrait: sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_PORTRAIT; break; + case QCameraExposure::ExposureSports: sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_SPORT; break; + case QCameraExposure::ExposureNight: sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_NIGHT; break; + case QCameraExposure::ExposureAuto: sceneMode = GST_PHOTOGRAPHY_SCENE_MODE_AUTO; break; + default: + break; + } + + gst_photography_set_scene_mode(m_session->photography(), sceneMode); +} + +bool CameraBinExposure::isExposureModeSupported(QCameraExposure::ExposureMode mode) const +{ + //Similar mode names can be found in gst as GstSceneMode + return mode == QCameraExposure::ExposureAuto || + mode == QCameraExposure::ExposurePortrait || + mode == QCameraExposure::ExposureSports || + mode == QCameraExposure::ExposureNight; + + //No direct mapping available for GST_PHOTOGRAPHY_SCENE_MODE_CLOSEUP and + //GST_PHOTOGRAPHY_SCENE_MODE_LANDSCAPE +} + +QCameraExposure::MeteringMode CameraBinExposure::meteringMode() const +{ + return QCameraExposure::MeteringMatrix; +} + +void CameraBinExposure::setMeteringMode(QCameraExposure::MeteringMode mode) +{ + Q_UNUSED(mode); +} + +bool CameraBinExposure::isMeteringModeSupported(QCameraExposure::MeteringMode mode) const +{ + return mode == QCameraExposure::MeteringMatrix; +} + +bool CameraBinExposure::isParameterSupported(ExposureParameter parameter) const +{ + switch (parameter) { + case QCameraExposureControl::ExposureCompensation: + case QCameraExposureControl::ISO: + case QCameraExposureControl::Aperture: + case QCameraExposureControl::ShutterSpeed: + return true; + default: + return false; + } +} + +QVariant CameraBinExposure::exposureParameter(ExposureParameter parameter) const +{ + switch (parameter) { + case QCameraExposureControl::ExposureCompensation: + { + gfloat ev; + gst_photography_get_ev_compensation(m_session->photography(), &ev); + return QVariant(ev); + } + case QCameraExposureControl::ISO: + { + guint isoSpeed = 0; + gst_photography_get_iso_speed(m_session->photography(), &isoSpeed); + return QVariant(isoSpeed); + } + case QCameraExposureControl::Aperture: + return QVariant(2.8); + case QCameraExposureControl::ShutterSpeed: + { + guint32 shutterSpeed = 0; + gst_photography_get_exposure(m_session->photography(), &shutterSpeed); + + return QVariant(shutterSpeed/1000000.0); + } + default: + return QVariant(); + } +} + +QCameraExposureControl::ParameterFlags CameraBinExposure::exposureParameterFlags(ExposureParameter parameter) const +{ + QCameraExposureControl::ParameterFlags flags = 0; + + switch (parameter) { + case QCameraExposureControl::ExposureCompensation: + flags |= ContinuousRange; + break; + case QCameraExposureControl::Aperture: + flags |= ReadOnly; + break; + default: + break; + } + + return flags; +} + +QVariantList CameraBinExposure::supportedParameterRange(ExposureParameter parameter) const +{ + QVariantList res; + switch (parameter) { + case QCameraExposureControl::ExposureCompensation: + res << -2.0 << 2.0; + break; + case QCameraExposureControl::ISO: + res << 100 << 200 << 400; + break; + case QCameraExposureControl::Aperture: + res << 2.8; + break; + default: + break; + } + + return res; +} + +bool CameraBinExposure::setExposureParameter(ExposureParameter parameter, const QVariant& value) +{ + QVariant oldValue = exposureParameter(parameter); + + switch (parameter) { + case QCameraExposureControl::ExposureCompensation: + gst_photography_set_ev_compensation(m_session->photography(), value.toReal()); + break; + case QCameraExposureControl::ISO: + gst_photography_set_iso_speed(m_session->photography(), value.toInt()); + break; + case QCameraExposureControl::Aperture: + gst_photography_set_aperture(m_session->photography(), guint(value.toReal()*1000000)); + break; + case QCameraExposureControl::ShutterSpeed: + gst_photography_set_exposure(m_session->photography(), guint(value.toReal()*1000000)); + break; + default: + return false; + } + + QVariant newValue = exposureParameter(parameter); + if (!qFuzzyCompare(oldValue.toReal(), newValue.toReal())) + emit exposureParameterChanged(parameter); + + return true; +} + +QString CameraBinExposure::extendedParameterName(ExposureParameter) +{ + return QString(); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinexposure.h b/src/plugins/gstreamer/camerabin/camerabinexposure.h new file mode 100644 index 000000000..44439253d --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinexposure.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINEXPOSURECONTROL_MAEMO_H +#define CAMERABINEXPOSURECONTROL_MAEMO_H + +#include +#include + +#include +#include + +class CameraBinSession; + +QT_USE_NAMESPACE + +class Q_MULTIMEDIA_EXPORT CameraBinExposure : public QCameraExposureControl +{ + Q_OBJECT + +public: + CameraBinExposure(CameraBinSession *session); + virtual ~CameraBinExposure(); + + QCameraExposure::ExposureMode exposureMode() const; + void setExposureMode(QCameraExposure::ExposureMode mode); + bool isExposureModeSupported(QCameraExposure::ExposureMode mode) const; + + QCameraExposure::MeteringMode meteringMode() const; + void setMeteringMode(QCameraExposure::MeteringMode mode); + bool isMeteringModeSupported(QCameraExposure::MeteringMode mode) const; + + bool isParameterSupported(ExposureParameter parameter) const; + QVariant exposureParameter(ExposureParameter parameter) const; + ParameterFlags exposureParameterFlags(ExposureParameter parameter) const; + QVariantList supportedParameterRange(ExposureParameter parameter) const; + bool setExposureParameter(ExposureParameter parameter, const QVariant& value); + + QString extendedParameterName(ExposureParameter parameter); + +private: + CameraBinSession *m_session; +}; + +#endif // CAMERABINEXPOSURECONTROL_MAEMO_H diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.cpp b/src/plugins/gstreamer/camerabin/camerabinflash.cpp new file mode 100644 index 000000000..f5b76524d --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinflash.cpp @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinflash.h" +#include "camerabinsession.h" +#include + +#include + +CameraBinFlash::CameraBinFlash(CameraBinSession *session) + :QCameraFlashControl(session), + m_session(session) +{ +} + +CameraBinFlash::~CameraBinFlash() +{ +} + +QCameraExposure::FlashModes CameraBinFlash::flashMode() const +{ + GstFlashMode flashMode; + gst_photography_get_flash_mode(m_session->photography(), &flashMode); + + QCameraExposure::FlashModes modes; + switch (flashMode) { + case GST_PHOTOGRAPHY_FLASH_MODE_AUTO: modes |= QCameraExposure::FlashAuto; break; + case GST_PHOTOGRAPHY_FLASH_MODE_OFF: modes |= QCameraExposure::FlashOff; break; + case GST_PHOTOGRAPHY_FLASH_MODE_ON: modes |= QCameraExposure::FlashOn; break; + case GST_PHOTOGRAPHY_FLASH_MODE_FILL_IN: modes |= QCameraExposure::FlashFill; break; + case GST_PHOTOGRAPHY_FLASH_MODE_RED_EYE: modes |= QCameraExposure::FlashRedEyeReduction; break; + default: + modes |= QCameraExposure::FlashAuto; + break; + } + return modes; +} + +void CameraBinFlash::setFlashMode(QCameraExposure::FlashModes mode) +{ + GstFlashMode flashMode; + gst_photography_get_flash_mode(m_session->photography(), &flashMode); + + if (mode.testFlag(QCameraExposure::FlashAuto)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_AUTO; + else if (mode.testFlag(QCameraExposure::FlashOff)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_OFF; + else if (mode.testFlag(QCameraExposure::FlashOn)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_ON; + else if (mode.testFlag(QCameraExposure::FlashFill)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_FILL_IN; + else if (mode.testFlag(QCameraExposure::FlashRedEyeReduction)) flashMode = GST_PHOTOGRAPHY_FLASH_MODE_RED_EYE; + + gst_photography_set_flash_mode(m_session->photography(), flashMode); +} + +bool CameraBinFlash::isFlashModeSupported(QCameraExposure::FlashModes mode) const +{ + return mode == QCameraExposure::FlashOff || + mode == QCameraExposure::FlashOn || + mode == QCameraExposure::FlashAuto || + mode == QCameraExposure::FlashRedEyeReduction || + mode == QCameraExposure::FlashFill; +} + +bool CameraBinFlash::isFlashReady() const +{ + return true; +} + diff --git a/src/plugins/gstreamer/camerabin/camerabinflash.h b/src/plugins/gstreamer/camerabin/camerabinflash.h new file mode 100644 index 000000000..7c566becf --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinflash.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINFLASHCONTROL_H +#define CAMERABINFLASHCONTROL_H + +#include +#include + +#include +#include + +class CameraBinSession; + +QT_USE_NAMESPACE + +class Q_MULTIMEDIA_EXPORT CameraBinFlash : public QCameraFlashControl +{ + Q_OBJECT +public: + CameraBinFlash(CameraBinSession *session); + virtual ~CameraBinFlash(); + + QCameraExposure::FlashModes flashMode() const; + void setFlashMode(QCameraExposure::FlashModes mode); + bool isFlashModeSupported(QCameraExposure::FlashModes mode) const; + + bool isFlashReady() const; + +private: + CameraBinSession *m_session; +}; + +#endif // CAMERABINFLASHCONTROL_H + diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.cpp b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp new file mode 100644 index 000000000..cf7c9ecd8 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinfocus.cpp @@ -0,0 +1,245 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinfocus.h" +#include "camerabinsession.h" + +#include + +#include +#include + +//#define CAMERABIN_DEBUG 1 +#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) + +CameraBinFocus::CameraBinFocus(CameraBinSession *session) + :QCameraFocusControl(session), + m_session(session), + m_focusMode(QCameraFocus::AutoFocus), + m_focusStatus(QCamera::Unlocked), + m_focusZoneStatus(QCameraFocusZone::Selected) +{ + connect(m_session, SIGNAL(stateChanged(QCamera::State)), + this, SLOT(_q_handleCameraStateChange(QCamera::State))); + connect(m_session, SIGNAL(imageCaptured(int,QImage)), + this, SLOT(_q_handleCapturedImage())); +} + +CameraBinFocus::~CameraBinFocus() +{ +} + +QCameraFocus::FocusMode CameraBinFocus::focusMode() const +{ + return m_focusMode; +} + +void CameraBinFocus::setFocusMode(QCameraFocus::FocusMode mode) +{ + if (isFocusModeSupported(mode)) { + m_focusMode = mode; + } +} + +bool CameraBinFocus::isFocusModeSupported(QCameraFocus::FocusMode mode) const +{ + return mode & QCameraFocus::AutoFocus; +} + +qreal CameraBinFocus::maximumOpticalZoom() const +{ + return 1.0; +} + +qreal CameraBinFocus::maximumDigitalZoom() const +{ + return 10; +} + +qreal CameraBinFocus::opticalZoom() const +{ + return 1.0; +} + +qreal CameraBinFocus::digitalZoom() const +{ +#ifdef Q_WS_MAEMO_5 + gint zoomFactor = 0; + g_object_get(GST_BIN(m_session->cameraBin()), "zoom", &zoomFactor, NULL); + return zoomFactor/100.0; +#else + gfloat zoomFactor = 1.0; + g_object_get(GST_BIN(m_session->cameraBin()), "zoom", &zoomFactor, NULL); + return zoomFactor; +#endif +} + +void CameraBinFocus::zoomTo(qreal optical, qreal digital) +{ + Q_UNUSED(optical); + digital = qBound(qreal(1.0), digital, qreal(10.0)); +#ifdef Q_WS_MAEMO_5 + g_object_set(GST_BIN(m_session->cameraBin()), "zoom", qRound(digital*100.0), NULL); +#else + g_object_set(GST_BIN(m_session->cameraBin()), "zoom", digital, NULL); +#endif + emit digitalZoomChanged(digital); +} + +QCameraFocus::FocusPointMode CameraBinFocus::focusPointMode() const +{ + return QCameraFocus::FocusPointAuto; +} + +void CameraBinFocus::setFocusPointMode(QCameraFocus::FocusPointMode mode) +{ + Q_UNUSED(mode); +} + +bool CameraBinFocus::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const +{ + return mode == QCameraFocus::FocusPointAuto; +} + +QPointF CameraBinFocus::customFocusPoint() const +{ + return QPointF(0.5, 0.5); +} + +void CameraBinFocus::setCustomFocusPoint(const QPointF &point) +{ + Q_UNUSED(point); +} + +QCameraFocusZoneList CameraBinFocus::focusZones() const +{ + return QCameraFocusZoneList() << QCameraFocusZone(QRectF(0.35, 0.35, 0.3, 0.3), m_focusZoneStatus); +} + + +void CameraBinFocus::handleFocusMessage(GstMessage *gm) +{ + //it's a sync message, so it's called from non main thread + if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) { + gint status = GST_PHOTOGRAPHY_FOCUS_STATUS_NONE; + gst_structure_get_int (gm->structure, "status", &status); + QCamera::LockStatus focusStatus = m_focusStatus; + QCamera::LockChangeReason reason = QCamera::UserRequest; + + switch (status) { + case GST_PHOTOGRAPHY_FOCUS_STATUS_FAIL: + focusStatus = QCamera::Unlocked; + reason = QCamera::LockFailed; + break; + case GST_PHOTOGRAPHY_FOCUS_STATUS_SUCCESS: + focusStatus = QCamera::Locked; + break; + case GST_PHOTOGRAPHY_FOCUS_STATUS_NONE: + break; + case GST_PHOTOGRAPHY_FOCUS_STATUS_RUNNING: + focusStatus = QCamera::Searching; + break; + default: + break; + } + + static int signalIndex = metaObject()->indexOfSlot( + "_q_setFocusStatus(QCamera::LockStatus,QCamera::LockChangeReason)"); + metaObject()->method(signalIndex).invoke(this, + Qt::QueuedConnection, + Q_ARG(QCamera::LockStatus,focusStatus), + Q_ARG(QCamera::LockChangeReason,reason)); + } +} + +void CameraBinFocus::_q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason) +{ +#ifdef CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << "Current:" + << ENUM_NAME(QCamera, "LockStatus", m_focusStatus) + << "New:" + << ENUM_NAME(QCamera, "LockStatus", status) << ENUM_NAME(QCamera, "LockChangeReason", reason); +#endif + + if (m_focusStatus != status) { + m_focusStatus = status; + + QCameraFocusZone::FocusZoneStatus zonesStatus = + m_focusStatus == QCamera::Locked ? + QCameraFocusZone::Focused : QCameraFocusZone::Selected; + + if (m_focusZoneStatus != zonesStatus) { + m_focusZoneStatus = zonesStatus; + emit focusZonesChanged(); + } + + emit _q_focusStatusChanged(m_focusStatus, reason); + } +} + +void CameraBinFocus::_q_handleCameraStateChange(QCamera::State state) +{ + if (state != QCamera::ActiveState) + _q_setFocusStatus(QCamera::Unlocked, QCamera::LockLost); +} + +void CameraBinFocus::_q_handleCapturedImage() +{ +#ifdef Q_WS_MAEMO_5 + //N900 lost focus after image capture + if (m_focusStatus != QCamera::Unlocked) { + m_focusStatus = QCamera::Unlocked; + emit _q_focusStatusChanged(QCamera::Unlocked, QCamera::LockLost); + } +#endif +} + +void CameraBinFocus::_q_startFocusing() +{ + _q_setFocusStatus(QCamera::Searching, QCamera::UserRequest); + gst_photography_set_autofocus(m_session->photography(), TRUE); +} + +void CameraBinFocus::_q_stopFocusing() +{ + gst_photography_set_autofocus(m_session->photography(), FALSE); + _q_setFocusStatus(QCamera::Unlocked, QCamera::UserRequest); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinfocus.h b/src/plugins/gstreamer/camerabin/camerabinfocus.h new file mode 100644 index 000000000..e496d2e3b --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinfocus.h @@ -0,0 +1,104 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINFOCUSCONTROL_H +#define CAMERABINFOCUSCONTROL_H + +#include +#include + +#include +#include + +class CameraBinSession; + +QT_USE_NAMESPACE + +class CameraBinFocus : public QCameraFocusControl +{ + Q_OBJECT + +public: + CameraBinFocus(CameraBinSession *session); + virtual ~CameraBinFocus(); + + QCameraFocus::FocusMode focusMode() const; + void setFocusMode(QCameraFocus::FocusMode mode); + bool isFocusModeSupported(QCameraFocus::FocusMode mode) const; + + qreal maximumOpticalZoom() const; + qreal maximumDigitalZoom() const; + qreal opticalZoom() const; + qreal digitalZoom() const; + + void zoomTo(qreal optical, qreal digital) ; + + QCameraFocus::FocusPointMode focusPointMode() const; + void setFocusPointMode(QCameraFocus::FocusPointMode mode) ; + bool isFocusPointModeSupported(QCameraFocus::FocusPointMode) const; + QPointF customFocusPoint() const; + void setCustomFocusPoint(const QPointF &point); + + QCameraFocusZoneList focusZones() const; + + void handleFocusMessage(GstMessage*); + QCamera::LockStatus focusStatus() const { return m_focusStatus; } + +Q_SIGNALS: + void _q_focusStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason); + +public Q_SLOTS: + void _q_startFocusing(); + void _q_stopFocusing(); + +private Q_SLOTS: + void _q_setFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason); + void _q_handleCameraStateChange(QCamera::State state); + void _q_handleCapturedImage(); + +private: + CameraBinSession *m_session; + QCameraFocus::FocusMode m_focusMode; + QCamera::LockStatus m_focusStatus; + QCameraFocusZone::FocusZoneStatus m_focusZoneStatus; +}; + +#endif // CAMERABINFOCUSCONTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp new file mode 100644 index 000000000..3df1105bc --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.cpp @@ -0,0 +1,347 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinimagecapture.h" +#include "camerabincapturedestination.h" +#include "camerabincapturebufferformat.h" +#include "camerabinsession.h" +#include "qgstvideobuffer.h" +#include "qvideosurfacegstsink.h" +#include "qgstutils.h" +#include +#include +#include + +//#define DEBUG_CAPTURE + +#ifdef Q_WS_MAEMO_5 +#define IMAGE_DONE_SIGNAL "img-done" +#else +#define IMAGE_DONE_SIGNAL "image-done" +#endif + + +Q_DECLARE_METATYPE(QVideoFrame) +Q_DECLARE_METATYPE(QtMultimediaKit::MetaData) + +namespace +{ +class CameraRegisterMetaTypes +{ +public: + CameraRegisterMetaTypes() + { + qRegisterMetaType("QVideoFrame"); + qRegisterMetaType("QtMultimediaKit::MetaData"); + } +} _registerCameraMetaTypes; +} + + +CameraBinImageCapture::CameraBinImageCapture(CameraBinSession *session) + :QCameraImageCaptureControl(session) + , m_session(session) + , m_ready(false) + , m_requestId(0) + , m_jpegEncoderElement(0) + , m_metadataMuxerElement(0) +{ + connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState())); + connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); + connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage))); + connect(m_session, SIGNAL(busMessage(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage))); + + g_signal_connect(G_OBJECT(m_session->cameraBin()), IMAGE_DONE_SIGNAL, G_CALLBACK(handleImageSaved), this); +} + +CameraBinImageCapture::~CameraBinImageCapture() +{ +} + +bool CameraBinImageCapture::isReadyForCapture() const +{ + return m_ready; +} + +int CameraBinImageCapture::capture(const QString &fileName) +{ + m_requestId++; + + if (!m_ready) { + emit error(m_requestId, QCameraImageCapture::NotReadyError, tr("Camera not ready")); + return m_requestId; + } + +#ifdef DEBUG_CAPTURE + qDebug() << Q_FUNC_INFO << m_requestId << fileName; +#endif + m_session->captureImage(m_requestId, fileName); + return m_requestId; +} + +void CameraBinImageCapture::cancelCapture() +{ +} + +void CameraBinImageCapture::updateState() +{ + bool ready = m_session->state() == QCamera::ActiveState; + if (m_ready != ready) { +#ifdef DEBUG_CAPTURE + qDebug() << "readyForCaptureChanged" << ready; +#endif + emit readyForCaptureChanged(m_ready = ready); + } +} + +gboolean CameraBinImageCapture::handleImageSaved(GstElement *camera, + const gchar *filename, + CameraBinImageCapture *self) +{ +#ifdef DEBUG_CAPTURE + qDebug() << "Image saved" << filename; +#endif + + Q_UNUSED(camera); + + if (self->m_session->captureDestinationControl()->captureDestination() & QCameraImageCapture::CaptureToFile) { + QMetaObject::invokeMethod(self, "imageSaved", + Qt::QueuedConnection, + Q_ARG(int, self->m_requestId), + Q_ARG(QString, QString::fromUtf8(filename))); + } else { +#ifdef DEBUG_CAPTURE + qDebug() << Q_FUNC_INFO << "Dropped saving file" << filename; +#endif + //camerabin creates an empty file when captured buffer is dropped, + //let's remove it + QFileInfo info(QString::fromUtf8(filename)); + if (info.isFile() && + info.filePath().startsWith("/home") && + info.size() == 0) { + QFile(info.absoluteFilePath()).remove(); + } + } + return true; +} + +gboolean CameraBinImageCapture::metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *self) +{ + + if (GST_EVENT_TYPE(event) == GST_EVENT_TAG) { + GstTagList *gstTags; + gst_event_parse_tag(event, &gstTags); + QMap extendedTags = QGstUtils::gstTagListToMap(gstTags); + +#ifdef DEBUG_CAPTURE + qDebug() << QString(gst_structure_to_string(gst_event_get_structure(event))).right(768); + qDebug() << "Capture event probe" << extendedTags; +#endif + + QMap tags; + tags[QtMultimediaKit::ISOSpeedRatings] = extendedTags.value("capturing-iso-speed"); + tags[QtMultimediaKit::DigitalZoomRatio] = extendedTags.value("capturing-digital-zoom-ratio"); + tags[QtMultimediaKit::ExposureTime] = extendedTags.value("capturing-shutter-speed"); + tags[QtMultimediaKit::WhiteBalance] = extendedTags.value("capturing-white-balance"); + tags[QtMultimediaKit::Flash] = extendedTags.value("capturing-flash-fired"); + tags[QtMultimediaKit::FocalLengthIn35mmFilm] = extendedTags.value("capturing-focal-length"); + tags[QtMultimediaKit::MeteringMode] = extendedTags.value("capturing-metering-mode"); + tags[QtMultimediaKit::ExposureMode] = extendedTags.value("capturing-exposure-mode"); + tags[QtMultimediaKit::FNumber] = extendedTags.value("capturing-focal-ratio"); + tags[QtMultimediaKit::ExposureMode] = extendedTags.value("capturing-exposure-mode"); + + QMapIterator i(tags); + while (i.hasNext()) { + i.next(); + if (i.value().isValid()) { + QMetaObject::invokeMethod(self, "imageMetadataAvailable", + Qt::QueuedConnection, + Q_ARG(int, self->m_requestId), + Q_ARG(QtMultimediaKit::MetaData, i.key()), + Q_ARG(QVariant, i.value())); + } + } + } + + return true; +} + +gboolean CameraBinImageCapture::uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self) +{ + Q_UNUSED(pad); + CameraBinSession *session = self->m_session; + +#ifdef DEBUG_CAPTURE + qDebug() << "Uncompressed buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer)); +#endif + + QCameraImageCapture::CaptureDestinations destination = + session->captureDestinationControl()->captureDestination(); + QVideoFrame::PixelFormat format = session->captureBufferFormatControl()->bufferFormat(); + + if (destination & QCameraImageCapture::CaptureToBuffer) { + if (format != QVideoFrame::Format_Jpeg) { + GstCaps *caps = GST_BUFFER_CAPS(buffer); + int bytesPerLine = -1; + QVideoSurfaceFormat format = QVideoSurfaceGstSink::formatForCaps(caps, &bytesPerLine); +#ifdef DEBUG_CAPTURE + qDebug() << "imageAvailable(uncompressed):" << format; +#endif + QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, bytesPerLine); + + QVideoFrame frame(videoBuffer, + format.frameSize(), + format.pixelFormat()); + + QMetaObject::invokeMethod(self, "imageAvailable", + Qt::QueuedConnection, + Q_ARG(int, self->m_requestId), + Q_ARG(QVideoFrame, frame)); + } + } + + //keep the buffer if capture to file or jpeg buffer capture was reuqsted + bool keepBuffer = (destination & QCameraImageCapture::CaptureToFile) || + ((destination & QCameraImageCapture::CaptureToBuffer) && + format == QVideoFrame::Format_Jpeg); + + return keepBuffer; +} + +gboolean CameraBinImageCapture::jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *self) +{ + Q_UNUSED(pad); + CameraBinSession *session = self->m_session; + +#ifdef DEBUG_CAPTURE + qDebug() << "Jpeg buffer probe" << gst_caps_to_string(GST_BUFFER_CAPS(buffer)); +#endif + + QCameraImageCapture::CaptureDestinations destination = + session->captureDestinationControl()->captureDestination(); + + if ((destination & QCameraImageCapture::CaptureToBuffer) && + session->captureBufferFormatControl()->bufferFormat() == QVideoFrame::Format_Jpeg) { + QGstVideoBuffer *videoBuffer = new QGstVideoBuffer(buffer, + -1); //bytesPerLine is not available for jpegs + + QSize resolution = QGstUtils::capsCorrectedResolution(GST_BUFFER_CAPS(buffer)); + //if resolution is not presented in caps, try to find it from encoded jpeg data: + if (resolution.isEmpty()) { + QBuffer data; + data.setData(reinterpret_cast(GST_BUFFER_DATA(buffer)), GST_BUFFER_SIZE(buffer)); + QImageReader reader(&data, "JPEG"); + resolution = reader.size(); + } + + QVideoFrame frame(videoBuffer, + resolution, + QVideoFrame::Format_Jpeg); + + QMetaObject::invokeMethod(self, "imageAvailable", + Qt::QueuedConnection, + Q_ARG(int, self->m_requestId), + Q_ARG(QVideoFrame, frame)); + } + + //drop the buffer if capture to file was disabled + return destination & QCameraImageCapture::CaptureToFile; +} + +void CameraBinImageCapture::handleBusMessage(const QGstreamerMessage &message) +{ + //Install metadata event and buffer probes + + //The image capture pipiline is built dynamically, + //it's necessary to wait until jpeg encoder is added to pipeline + + GstMessage *gm = message.rawMessage(); + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) { + GstState oldState; + GstState newState; + GstState pending; + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + + if (newState == GST_STATE_READY) { + GstElement *element = GST_ELEMENT(GST_MESSAGE_SRC(gm)); + if (!element) + return; + + QString elementName = QString::fromLatin1(gst_element_get_name(element)); + if (elementName.contains("jpegenc") && element != m_jpegEncoderElement) { + m_jpegEncoderElement = element; + GstPad *sinkpad = gst_element_get_static_pad(element, "sink"); + + //metadata event probe is installed before jpeg encoder + //to emit metadata available signal as soon as possible. +#ifdef DEBUG_CAPTURE + qDebug() << "install metadata probe"; +#endif + gst_pad_add_event_probe(sinkpad, + G_CALLBACK(CameraBinImageCapture::metadataEventProbe), + this); + +#ifdef DEBUG_CAPTURE + qDebug() << "install uncompressed buffer probe"; +#endif + gst_pad_add_buffer_probe(sinkpad, + G_CALLBACK(CameraBinImageCapture::uncompressedBufferProbe), + this); + + gst_object_unref(sinkpad); + } else if ((elementName.contains("jifmux") || elementName.startsWith("metadatamux")) + && element != m_metadataMuxerElement) { + //Jpeg encoded buffer probe is added after jifmux/metadatamux + //element to ensure the resulting jpeg buffer contains capture metadata + m_metadataMuxerElement = element; + + GstPad *srcpad = gst_element_get_static_pad(element, "src"); +#ifdef DEBUG_CAPTURE + qDebug() << "install jpeg buffer probe"; +#endif + gst_pad_add_buffer_probe(srcpad, + G_CALLBACK(CameraBinImageCapture::jpegBufferProbe), + this); + gst_object_unref(srcpad); + } + } + } +} diff --git a/src/plugins/gstreamer/camerabin/camerabinimagecapture.h b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h new file mode 100644 index 000000000..4aa5e998f --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinimagecapture.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef CAMERABINIMAGECAPTURECONTROL_H +#define CAMERABINIMAGECAPTURECONTROL_H + +#include +#include "camerabinsession.h" + +QT_USE_NAMESPACE + +class CameraBinImageCapture : public QCameraImageCaptureControl +{ + Q_OBJECT +public: + CameraBinImageCapture(CameraBinSession *session); + virtual ~CameraBinImageCapture(); + + QCameraImageCapture::DriveMode driveMode() const { return QCameraImageCapture::SingleImageCapture; } + void setDriveMode(QCameraImageCapture::DriveMode) {} + + bool isReadyForCapture() const; + int capture(const QString &fileName); + void cancelCapture(); + +private slots: + void updateState(); + void handleBusMessage(const QGstreamerMessage &message); + +private: + static gboolean metadataEventProbe(GstPad *pad, GstEvent *event, CameraBinImageCapture *); + static gboolean uncompressedBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); + static gboolean jpegBufferProbe(GstPad *pad, GstBuffer *buffer, CameraBinImageCapture *); + static gboolean handleImageSaved(GstElement *camera, const gchar *filename, CameraBinImageCapture *); + + CameraBinSession *m_session; + bool m_ready; + int m_requestId; + GstElement *m_jpegEncoderElement; + GstElement *m_metadataMuxerElement; +}; + +#endif // CAMERABINCAPTURECORNTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp new file mode 100644 index 000000000..2def12666 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinimageencoder.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinimageencoder.h" +#include "camerabinsession.h" + +#include + +CameraBinImageEncoder::CameraBinImageEncoder(CameraBinSession *session) + :QImageEncoderControl(session), m_session(session) +{ +} + +CameraBinImageEncoder::~CameraBinImageEncoder() +{ +} + +QList CameraBinImageEncoder::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const +{ + qDebug() << "CameraBinImageEncoder::supportedResolutions()"; + if (continuous) + *continuous = false; + + return m_session->supportedResolutions(qMakePair(0,0), continuous, QCamera::CaptureStillImage); +} + +QStringList CameraBinImageEncoder::supportedImageCodecs() const +{ + return QStringList() << "jpeg"; +} + +QString CameraBinImageEncoder::imageCodecDescription(const QString &codecName) const +{ + if (codecName == "jpeg") + return tr("JPEG image"); + + return QString(); +} + +QImageEncoderSettings CameraBinImageEncoder::imageSettings() const +{ + return m_settings; +} + +void CameraBinImageEncoder::setImageSettings(const QImageEncoderSettings &settings) +{ + m_settings = settings; + emit settingsChanged(); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinimageencoder.h b/src/plugins/gstreamer/camerabin/camerabinimageencoder.h new file mode 100644 index 000000000..ddb06a668 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinimageencoder.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINIMAGEENCODE_H +#define CAMERABINIMAGEENCODE_H + +class CameraBinSession; + +#include + +#include +#include + +#include +QT_USE_NAMESPACE + +class CameraBinImageEncoder : public QImageEncoderControl +{ + Q_OBJECT +public: + CameraBinImageEncoder(CameraBinSession *session); + virtual ~CameraBinImageEncoder(); + + QList supportedResolutions(const QImageEncoderSettings &settings = QImageEncoderSettings(), + bool *continuous = 0) const; + + QStringList supportedImageCodecs() const; + QString imageCodecDescription(const QString &formatName) const; + + QImageEncoderSettings imageSettings() const; + void setImageSettings(const QImageEncoderSettings &settings); + +Q_SIGNALS: + void settingsChanged(); + +private: + QImageEncoderSettings m_settings; + + CameraBinSession *m_session; + + // Added + QStringList m_codecs; + QMap m_elementNames; + QMap m_codecDescriptions; + QMap m_codecOptions; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp new file mode 100644 index 000000000..075ff2a67 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinimageprocessing.h" +#include "camerabinsession.h" + +CameraBinImageProcessing::CameraBinImageProcessing(CameraBinSession *session) + :QCameraImageProcessingControl(session), + m_session(session) +{ + m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_AUTO] = QCameraImageProcessing::WhiteBalanceAuto; + m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_DAYLIGHT] = QCameraImageProcessing::WhiteBalanceSunlight; + m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_CLOUDY] = QCameraImageProcessing::WhiteBalanceCloudy; + m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_SUNSET] = QCameraImageProcessing::WhiteBalanceSunset; + m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_TUNGSTEN] = QCameraImageProcessing::WhiteBalanceTungsten; + m_mappedWbValues[GST_PHOTOGRAPHY_WB_MODE_FLUORESCENT] = QCameraImageProcessing::WhiteBalanceFluorescent; + + updateColorBalanceValues(); +} + +CameraBinImageProcessing::~CameraBinImageProcessing() +{ +} + +void CameraBinImageProcessing::updateColorBalanceValues() +{ + if (!GST_IS_COLOR_BALANCE(m_session->cameraBin())) { + // Camerabin doesn't implement gstcolorbalance interface + return; + } + + GstColorBalance *balance = GST_COLOR_BALANCE(m_session->cameraBin()); + const GList *controls = gst_color_balance_list_channels(balance); + + const GList *item; + GstColorBalanceChannel *channel; + gint cur_value; + + for (item = controls; item; item = g_list_next (item)) { + channel = (GstColorBalanceChannel *)item->data; + cur_value = gst_color_balance_get_value (balance, channel); + + if (!g_ascii_strcasecmp (channel->label, "brightness")) { + m_values[QCameraImageProcessingControl::Brightness] = cur_value; + } else if (!g_ascii_strcasecmp (channel->label, "contrast")) { + m_values[QCameraImageProcessingControl::Contrast] = cur_value; + } else if (!g_ascii_strcasecmp (channel->label, "saturation")) { + m_values[QCameraImageProcessingControl::Saturation] = cur_value; + } + } +} + +bool CameraBinImageProcessing::setColorBalanceValue(const QString& channel, int value) +{ + + if (!GST_IS_COLOR_BALANCE(m_session->cameraBin())) { + // Camerabin doesn't implement gstcolorbalance interface + return false; + } + + GstColorBalance *balance = GST_COLOR_BALANCE(m_session->cameraBin()); + const GList *controls = gst_color_balance_list_channels(balance); + + const GList *item; + GstColorBalanceChannel *colorBalanceChannel; + + for (item = controls; item; item = g_list_next (item)) { + colorBalanceChannel = (GstColorBalanceChannel *)item->data; + + if (!g_ascii_strcasecmp (colorBalanceChannel->label, channel.toAscii())) { + gst_color_balance_set_value (balance, colorBalanceChannel, value); + return true; + } + } + + return false; +} + +QCameraImageProcessing::WhiteBalanceMode CameraBinImageProcessing::whiteBalanceMode() const +{ + GstWhiteBalanceMode wbMode; + gst_photography_get_white_balance_mode(m_session->photography(), &wbMode); + return m_mappedWbValues[wbMode]; +} + +void CameraBinImageProcessing::setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode) +{ + if (isWhiteBalanceModeSupported(mode)) + gst_photography_set_white_balance_mode(m_session->photography(), m_mappedWbValues.key(mode)); +} + +bool CameraBinImageProcessing::isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const +{ + return m_mappedWbValues.values().contains(mode); +} + +bool CameraBinImageProcessing::isProcessingParameterSupported(QCameraImageProcessingControl::ProcessingParameter parameter) const +{ + return parameter == QCameraImageProcessingControl::Contrast + || parameter == QCameraImageProcessingControl::Brightness + || parameter == QCameraImageProcessingControl::Saturation; +} + +QVariant CameraBinImageProcessing::processingParameter( + QCameraImageProcessingControl::ProcessingParameter parameter) const +{ + if (m_values.contains(parameter)) + return m_values.value(parameter); + else + return QVariant(); +} + +void CameraBinImageProcessing::setProcessingParameter( + QCameraImageProcessingControl::ProcessingParameter parameter, + QVariant value) +{ + switch (parameter) { + case Contrast: + setColorBalanceValue("contrast", value.toInt()); + break; + case Brightness: + setColorBalanceValue("brightness", value.toInt()); + break; + case Saturation: + setColorBalanceValue("saturation", value.toInt()); + break; + default: + break; + } + + updateColorBalanceValues(); +} + diff --git a/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h new file mode 100644 index 000000000..661d0d9d4 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinimageprocessing.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINIMAGEPROCESSINGCONTROL_H +#define CAMERABINIMAGEPROCESSINGCONTROL_H + +#include +#include + +#include +#include + +#include +#include + +class CameraBinSession; + +QT_USE_NAMESPACE + +class CameraBinImageProcessing : public QCameraImageProcessingControl +{ + Q_OBJECT + +public: + CameraBinImageProcessing(CameraBinSession *session); + virtual ~CameraBinImageProcessing(); + + QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode() const; + void setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode); + bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const; + + bool isProcessingParameterSupported(ProcessingParameter) const; + QVariant processingParameter(ProcessingParameter parameter) const; + void setProcessingParameter(ProcessingParameter parameter, QVariant value); + +private: + bool setColorBalanceValue(const QString& channel, int value); + void updateColorBalanceValues(); + +private: + CameraBinSession *m_session; + QMap m_values; + QMap m_mappedWbValues; +}; + +#endif // CAMERABINIMAGEPROCESSINGCONTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabinlocks.cpp b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp new file mode 100644 index 000000000..66da126f8 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinlocks.cpp @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinlocks.h" +#include "camerabinsession.h" +#include "camerabinfocus.h" + +#include + +#include + +CameraBinLocks::CameraBinLocks(CameraBinSession *session) + :QCameraLocksControl(session), + m_session(session), + m_focus(m_session->cameraFocusControl()) +{ + connect(m_focus, SIGNAL(_q_focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(updateFocusStatus(QCamera::LockStatus, QCamera::LockChangeReason))); +} + +CameraBinLocks::~CameraBinLocks() +{ +} + +QCamera::LockTypes CameraBinLocks::supportedLocks() const +{ + return QCamera::LockFocus; +} + +QCamera::LockStatus CameraBinLocks::lockStatus(QCamera::LockType lock) const +{ + return lock == QCamera::LockFocus ? m_focus->focusStatus() : QCamera::Unlocked; +} + +void CameraBinLocks::searchAndLock(QCamera::LockTypes locks) +{ + if (locks & QCamera::LockFocus) + m_focus->_q_startFocusing(); +} + +void CameraBinLocks::unlock(QCamera::LockTypes locks) +{ + if (locks & QCamera::LockFocus) + m_focus->_q_stopFocusing(); +} + +void CameraBinLocks::updateFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason) +{ + emit lockStatusChanged(QCamera::LockFocus, status, reason); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinlocks.h b/src/plugins/gstreamer/camerabin/camerabinlocks.h new file mode 100644 index 000000000..29c4f2ece --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinlocks.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINLOCKSCONTROL_H +#define CAMERABINLOCKSCONTROL_H + +#include +#include + +#include +#include + +class CameraBinSession; +class CameraBinFocus; + +QT_USE_NAMESPACE + +class CameraBinLocks : public QCameraLocksControl +{ + Q_OBJECT + +public: + CameraBinLocks(CameraBinSession *session); + virtual ~CameraBinLocks(); + + QCamera::LockTypes supportedLocks() const; + + QCamera::LockStatus lockStatus(QCamera::LockType lock) const; + + void searchAndLock(QCamera::LockTypes locks); + void unlock(QCamera::LockTypes locks); + +private slots: + void updateFocusStatus(QCamera::LockStatus status, QCamera::LockChangeReason reason); + +private: + CameraBinSession *m_session; + CameraBinFocus *m_focus; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp new file mode 100644 index 000000000..d7036ebc2 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinmetadata.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinmetadata.h" + +#include +#include + +struct QGstreamerMetaDataKeyLookup +{ + QtMultimediaKit::MetaData key; + const char *token; +}; + +static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = +{ + { QtMultimediaKit::Title, GST_TAG_TITLE }, + //{ QtMultimediaKit::SubTitle, 0 }, + //{ QtMultimediaKit::Author, 0 }, + { QtMultimediaKit::Comment, GST_TAG_COMMENT }, + { QtMultimediaKit::Description, GST_TAG_DESCRIPTION }, + //{ QtMultimediaKit::Category, 0 }, + { QtMultimediaKit::Genre, GST_TAG_GENRE }, + //{ QtMultimediaKit::Year, 0 }, + //{ QtMultimediaKit::UserRating, 0 }, + + { QtMultimediaKit::Language, GST_TAG_LANGUAGE_CODE }, + + { QtMultimediaKit::Publisher, GST_TAG_ORGANIZATION }, + { QtMultimediaKit::Copyright, GST_TAG_COPYRIGHT }, + //{ QtMultimediaKit::ParentalRating, 0 }, + //{ QtMultimediaKit::RatingOrganisation, 0 }, + + // Media + //{ QtMultimediaKit::Size, 0 }, + //{ QtMultimediaKit::MediaType, 0 }, + { QtMultimediaKit::Duration, GST_TAG_DURATION }, + + // Audio + { QtMultimediaKit::AudioBitRate, GST_TAG_BITRATE }, + { QtMultimediaKit::AudioCodec, GST_TAG_AUDIO_CODEC }, + //{ QtMultimediaKit::ChannelCount, 0 }, + //{ QtMultimediaKit::SampleRate, 0 }, + + // Music + { QtMultimediaKit::AlbumTitle, GST_TAG_ALBUM }, + { QtMultimediaKit::AlbumArtist, GST_TAG_ARTIST}, + { QtMultimediaKit::ContributingArtist, GST_TAG_PERFORMER }, +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + { QtMultimediaKit::Composer, GST_TAG_COMPOSER }, +#endif + //{ QtMultimediaKit::Conductor, 0 }, + //{ QtMultimediaKit::Lyrics, 0 }, + //{ QtMultimediaKit::Mood, 0 }, + { QtMultimediaKit::TrackNumber, GST_TAG_TRACK_NUMBER }, + + //{ QtMultimediaKit::CoverArtUrlSmall, 0 }, + //{ QtMultimediaKit::CoverArtUrlLarge, 0 }, + + // Image/Video + //{ QtMultimediaKit::Resolution, 0 }, + //{ QtMultimediaKit::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimediaKit::VideoFrameRate, 0 }, + //{ QtMultimediaKit::VideoBitRate, 0 }, + { QtMultimediaKit::VideoCodec, GST_TAG_VIDEO_CODEC }, + + //{ QtMultimediaKit::PosterUrl, 0 }, + + // Movie + //{ QtMultimediaKit::ChapterNumber, 0 }, + //{ QtMultimediaKit::Director, 0 }, + { QtMultimediaKit::LeadPerformer, GST_TAG_PERFORMER }, + //{ QtMultimediaKit::Writer, 0 }, + + // Photos + //{ QtMultimediaKit::CameraManufacturer, 0 }, + //{ QtMultimediaKit::CameraModel, 0 }, + //{ QtMultimediaKit::Event, 0 }, + //{ QtMultimediaKit::Subject, 0 } +}; + +CameraBinMetaData::CameraBinMetaData(QObject *parent) + :QMetaDataWriterControl(parent) +{ +} + +QVariant CameraBinMetaData::metaData(QtMultimediaKit::MetaData key) const +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + const char *name = qt_gstreamerMetaDataKeys[i].token; + + return m_values.value(QByteArray::fromRawData(name, qstrlen(name))); + } + } + return QVariant(); +} + +void CameraBinMetaData::setMetaData(QtMultimediaKit::MetaData key, const QVariant &value) +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + const char *name = qt_gstreamerMetaDataKeys[i].token; + + m_values.insert(QByteArray::fromRawData(name, qstrlen(name)), value); + + emit QMetaDataWriterControl::metaDataChanged(); + emit metaDataChanged(m_values); + + return; + } + } +} + +QList CameraBinMetaData::availableMetaData() const +{ + static QMap keysMap; + if (keysMap.isEmpty()) { + const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + keysMap[QByteArray(qt_gstreamerMetaDataKeys[i].token)] = qt_gstreamerMetaDataKeys[i].key; + } + } + + QList res; + foreach (const QByteArray &key, m_values.keys()) { + QtMultimediaKit::MetaData tag = keysMap.value(key, QtMultimediaKit::MetaData(-1)); + if (tag != -1) + res.append(tag); + } + + return res; +} + +QVariant CameraBinMetaData::extendedMetaData(QString const &name) const +{ + return m_values.value(name.toLatin1()); +} + +void CameraBinMetaData::setExtendedMetaData(QString const &name, QVariant const &value) +{ + m_values.insert(name.toLatin1(), value); + emit QMetaDataWriterControl::metaDataChanged(); + emit metaDataChanged(m_values); +} + +QStringList CameraBinMetaData::availableExtendedMetaData() const +{ + QStringList res; + foreach (const QByteArray &key, m_values.keys()) + res.append(QString(key)); + + return res; +} diff --git a/src/plugins/gstreamer/camerabin/camerabinmetadata.h b/src/plugins/gstreamer/camerabin/camerabinmetadata.h new file mode 100644 index 000000000..be00da839 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinmetadata.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINCAPTUREMETADATACONTROL_H +#define CAMERABINCAPTUREMETADATACONTROL_H + +#include + +QT_USE_NAMESPACE + +class CameraBinMetaData : public QMetaDataWriterControl +{ + Q_OBJECT +public: + CameraBinMetaData(QObject *parent); + virtual ~CameraBinMetaData() {} + + + bool isMetaDataAvailable() const { return true; } + bool isWritable() const { return true; } + + QVariant metaData(QtMultimediaKit::MetaData key) const; + void setMetaData(QtMultimediaKit::MetaData key, const QVariant &value); + QList availableMetaData() const; + + QVariant extendedMetaData(QString const &name) const; + void setExtendedMetaData(QString const &name, QVariant const &value); + QStringList availableExtendedMetaData() const; + +Q_SIGNALS: + void metaDataChanged(const QMap&); + +private: + QMap m_values; +}; + +#endif // CAMERABINCAPTUREMETADATACONTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp new file mode 100644 index 000000000..0cd8a5aaf --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinrecorder.cpp @@ -0,0 +1,225 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinrecorder.h" +#include "camerabinaudioencoder.h" +#include "camerabinvideoencoder.h" +#include "camerabincontainer.h" +#include + +CameraBinRecorder::CameraBinRecorder(CameraBinSession *session) + :QMediaRecorderControl(session), + m_session(session), + m_state(QMediaRecorder::StoppedState) +{ + connect(m_session, SIGNAL(stateChanged(QCamera::State)), SLOT(updateState())); + connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); +} + +CameraBinRecorder::~CameraBinRecorder() +{ +} + +QUrl CameraBinRecorder::outputLocation() const +{ + return m_session->outputLocation(); +} + +bool CameraBinRecorder::setOutputLocation(const QUrl &sink) +{ + m_session->setOutputLocation(sink); + return true; +} + +QMediaRecorder::State CameraBinRecorder::state() const +{ + return m_state; +} + +void CameraBinRecorder::updateState() +{ + if (m_session->state() != QCamera::ActiveState && + m_state != QMediaRecorder::StoppedState) { + m_session->stopVideoRecording(); + emit stateChanged(m_state = QMediaRecorder::StoppedState); + } +} + +qint64 CameraBinRecorder::duration() const +{ + return m_session->duration(); +} + +void CameraBinRecorder::record() +{ + if (m_session->state() == QCamera::ActiveState) { + if (m_state == QMediaRecorder::PausedState) + m_session->resumeVideoRecording(); + else + m_session->recordVideo(); + emit stateChanged(m_state = QMediaRecorder::RecordingState); + } else + emit error(QMediaRecorder::ResourceError, tr("Service has not been started")); +} + +void CameraBinRecorder::pause() +{ + if (m_session->state() == QCamera::ActiveState) { + m_session->pauseVideoRecording(); + emit stateChanged(m_state = QMediaRecorder::PausedState); + } else + emit error(QMediaRecorder::ResourceError, tr("Service has not been started")); +} + +void CameraBinRecorder::stop() +{ + if (m_session->state() == QCamera::ActiveState) { + m_session->stopVideoRecording(); + emit stateChanged(m_state = QMediaRecorder::StoppedState); + } +} + +bool CameraBinRecorder::findCodecs() +{ + //Check the codecs are compatible with container, + //and choose the compatible codecs/container if omitted + CameraBinAudioEncoder *audioEncodeControl = m_session->audioEncodeControl(); + CameraBinVideoEncoder *videoEncodeControl = m_session->videoEncodeControl(); + CameraBinContainer *mediaContainerControl = m_session->mediaContainerControl(); + + audioEncodeControl->resetActualSettings(); + videoEncodeControl->resetActualSettings(); + mediaContainerControl->resetActualContainer(); + + QStringList containerCandidates; + if (mediaContainerControl->containerMimeType().isEmpty()) + containerCandidates = mediaContainerControl->supportedContainers(); + else + containerCandidates << mediaContainerControl->containerMimeType(); + + + QStringList audioCandidates; + QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings(); + if (audioSettings.codec().isEmpty()) + audioCandidates = audioEncodeControl->supportedAudioCodecs(); + else + audioCandidates << audioSettings.codec(); + + QStringList videoCandidates; + QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings(); + if (videoSettings.codec().isEmpty()) + videoCandidates = videoEncodeControl->supportedVideoCodecs(); + else + videoCandidates << videoSettings.codec(); + + QString container; + QString audioCodec; + QString videoCodec; + + foreach (const QString &containerCandidate, containerCandidates) { + QSet supportedTypes = mediaContainerControl->supportedStreamTypes(containerCandidate); + + audioCodec.clear(); + videoCodec.clear(); + + bool found = false; + foreach (const QString &audioCandidate, audioCandidates) { + QSet audioTypes = audioEncodeControl->supportedStreamTypes(audioCandidate); + if (!audioTypes.intersect(supportedTypes).isEmpty()) { + found = true; + audioCodec = audioCandidate; + break; + } + } + if (!found) + continue; + + found = false; + foreach (const QString &videoCandidate, videoCandidates) { + QSet videoTypes = videoEncodeControl->supportedStreamTypes(videoCandidate); + if (!videoTypes.intersect(supportedTypes).isEmpty()) { + found = true; + videoCodec = videoCandidate; + break; + } + } + if (!found) + continue; + + + container = containerCandidate; + break; + } + + if (container.isEmpty()) { + qWarning() << "Camera error: Not compatible codecs and container format."; + emit error(QMediaRecorder::FormatError, tr("Not compatible codecs and container format.")); + return false; + } else { + mediaContainerControl->setActualContainer(container); + + QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings(); + audioSettings.setCodec(audioCodec); + audioEncodeControl->setActualAudioSettings(audioSettings); + + QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings(); + videoSettings.setCodec(videoCodec); + videoEncodeControl->setActualVideoSettings(videoSettings); + } + + return true; +} + +void CameraBinRecorder::applySettings() +{ + findCodecs(); +} + +bool CameraBinRecorder::isMuted() const +{ + return m_session->isMuted(); +} + +void CameraBinRecorder::setMuted(bool muted) +{ + m_session->setMuted(muted); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinrecorder.h b/src/plugins/gstreamer/camerabin/camerabinrecorder.h new file mode 100644 index 000000000..a6faf9b64 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinrecorder.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef CAMERABINRECORDERCONTROL_H +#define CAMERABINRECORDERCONTROL_H + +#include +#include "camerabinsession.h" +QT_USE_NAMESPACE + +class CameraBinRecorder : public QMediaRecorderControl +{ + Q_OBJECT + +public: + CameraBinRecorder(CameraBinSession *session); + virtual ~CameraBinRecorder(); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &sink); + + QMediaRecorder::State state() const; + + qint64 duration() const; + + bool isMuted() const; + + bool findCodecs(); + + void applySettings(); + +public slots: + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private slots: + void updateState(); + +private: + CameraBinSession *m_session; + QMediaRecorder::State m_state; +}; + +#endif // CAMERABINCAPTURECORNTROL_H diff --git a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp new file mode 100644 index 000000000..db9218c4a --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.cpp @@ -0,0 +1,188 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinresourcepolicy.h" + +#ifdef Q_WS_MAEMO_6 +#define HAVE_RESOURCE_POLICY +#endif + +//#define DEBUG_RESOURCE_POLICY +#include +#include + +#ifdef HAVE_RESOURCE_POLICY +#include +#include +#include +#endif + +CamerabinResourcePolicy::CamerabinResourcePolicy(QObject *parent) : + QObject(parent), + m_resourceSet(NoResources), + m_releasingResources(false) +{ +#ifdef HAVE_RESOURCE_POLICY + //loaded resource set is also kept requested for image and video capture sets + m_resource = new ResourcePolicy::ResourceSet("camera"); + m_resource->setAlwaysReply(); + m_resource->initAndConnect(); + + connect(m_resource, SIGNAL(resourcesGranted(const QList)), + SIGNAL(resourcesGranted())); + connect(m_resource, SIGNAL(resourcesDenied()), SIGNAL(resourcesDenied())); + connect(m_resource, SIGNAL(lostResources()), SIGNAL(resourcesLost())); + connect(m_resource, SIGNAL(resourcesReleased()), SLOT(handleResourcesReleased())); +#endif +} + +CamerabinResourcePolicy::~CamerabinResourcePolicy() +{ +#ifdef HAVE_RESOURCE_POLICY + //ensure the resources are released + if (m_resourceSet != NoResources) + setResourceSet(NoResources); + + //don't delete the resource set until resources are released + if (m_releasingResources) { + m_resource->connect(m_resource, SIGNAL(resourcesReleased()), + SLOT(deleteLater())); + } else { + delete m_resource; + m_resource = 0; + } +#endif +} + +CamerabinResourcePolicy::ResourceSet CamerabinResourcePolicy::resourceSet() const +{ + return m_resourceSet; +} + +void CamerabinResourcePolicy::setResourceSet(CamerabinResourcePolicy::ResourceSet set) +{ + CamerabinResourcePolicy::ResourceSet oldSet = m_resourceSet; + m_resourceSet = set; + +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO << set; +#endif + +#ifdef HAVE_RESOURCE_POLICY + QSet requestedTypes; + + switch (set) { + case NoResources: + break; + case LoadedResources: + requestedTypes << ResourcePolicy::LensCoverType //to detect lens cover is opened/closed + << ResourcePolicy::VideoRecorderType //to open camera device + << ResourcePolicy::SnapButtonType; //to detect capture button events + break; + case ImageCaptureResources: + requestedTypes << ResourcePolicy::LensCoverType + << ResourcePolicy::VideoPlaybackType + << ResourcePolicy::VideoRecorderType + << ResourcePolicy::AudioPlaybackType + << ResourcePolicy::ScaleButtonType + << ResourcePolicy::LedsType + << ResourcePolicy::SnapButtonType; + break; + case VideoCaptureResources: + requestedTypes << ResourcePolicy::LensCoverType + << ResourcePolicy::VideoPlaybackType + << ResourcePolicy::VideoRecorderType + << ResourcePolicy::AudioPlaybackType + << ResourcePolicy::AudioRecorderType + << ResourcePolicy::ScaleButtonType + << ResourcePolicy::LedsType + << ResourcePolicy::SnapButtonType; + break; + } + + QSet currentTypes; + foreach (ResourcePolicy::Resource *resource, m_resource->resources()) + currentTypes << resource->type(); + + foreach (ResourcePolicy::ResourceType resourceType, currentTypes - requestedTypes) + m_resource->deleteResource(resourceType); + + foreach (ResourcePolicy::ResourceType resourceType, requestedTypes - currentTypes) { + if (resourceType == ResourcePolicy::LensCoverType) { + ResourcePolicy::LensCoverResource *lensCoverResource = new ResourcePolicy::LensCoverResource; + lensCoverResource->setOptional(true); + m_resource->addResourceObject(lensCoverResource); + } else { + m_resource->addResource(resourceType); + } + } + + m_resource->update(); + if (set != NoResources) { + m_resource->acquire(); + } else { + if (oldSet != NoResources) { + m_releasingResources = true; + m_resource->release(); + } + } +#endif +} + +bool CamerabinResourcePolicy::isResourcesGranted() const +{ +#ifdef HAVE_RESOURCE_POLICY + foreach (ResourcePolicy::Resource *resource, m_resource->resources()) + if (!resource->isOptional() && !resource->isGranted()) + return false; +#endif + return true; +} + +void CamerabinResourcePolicy::handleResourcesReleased() +{ +#ifdef HAVE_RESOURCE_POLICY +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO; +#endif + m_releasingResources = false; +#endif +} diff --git a/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h new file mode 100644 index 000000000..cd2d84688 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinresourcepolicy.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERARESOURCEPOLICY_H +#define CAMERARESOURCEPOLICY_H + +#include + +namespace ResourcePolicy { +class ResourceSet; +}; + +class CamerabinResourcePolicy : public QObject +{ + Q_OBJECT +public: + enum ResourceSet { + NoResources, + LoadedResources, + ImageCaptureResources, + VideoCaptureResources + }; + + CamerabinResourcePolicy(QObject *parent); + ~CamerabinResourcePolicy(); + + ResourceSet resourceSet() const; + void setResourceSet(ResourceSet set); + + bool isResourcesGranted() const; + +Q_SIGNALS: + void resourcesDenied(); + void resourcesGranted(); + void resourcesLost(); + +private Q_SLOTS: + void handleResourcesReleased(); + +private: + ResourceSet m_resourceSet; + ResourcePolicy::ResourceSet *m_resource; + bool m_releasingResources; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.cpp b/src/plugins/gstreamer/camerabin/camerabinservice.cpp new file mode 100644 index 000000000..261a9c308 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinservice.cpp @@ -0,0 +1,261 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinservice.h" +#include "camerabinsession.h" +#include "camerabinrecorder.h" +#include "camerabincontainer.h" +#include "camerabinaudioencoder.h" +#include "camerabinvideoencoder.h" +#include "camerabinimageencoder.h" +#include "qgstreamerbushelper.h" +#include "camerabincontrol.h" +#include "camerabinlocks.h" +#include "camerabinmetadata.h" +#include "camerabinexposure.h" +#include "camerabinflash.h" +#include "camerabinfocus.h" +#include "camerabinimagecapture.h" +#include "camerabinimageprocessing.h" +#include "camerabincapturebufferformat.h" +#include "camerabincapturedestination.h" + +#include "qgstreameraudioinputendpointselector.h" +#include "qgstreamervideoinputdevicecontrol.h" + +#include "qgstreamervideooverlay.h" +#include "qgstreamervideowindow.h" +#include "qgstreamervideorenderer.h" + +#if defined(Q_WS_MAEMO_6) && defined(__arm__) +#include "qgstreamergltexturerenderer.h" +#endif + +#include "qgstreamervideowidget.h" + +#include + +#include +#include + +#if defined(Q_WS_MAEMO_5) +#include "camerabuttonlistener_maemo.h" +#endif + +#if defined(Q_WS_MAEMO_6) +#include "camerabuttonlistener_meego.h" +#endif + +CameraBinService::CameraBinService(const QString &service, QObject *parent): + QMediaService(parent) +{ + m_captureSession = 0; + m_cameraControl = 0; + m_metaDataControl = 0; + + m_audioInputEndpointSelector = 0; + m_videoInputDevice = 0; + + m_videoOutput = 0; + m_videoRenderer = 0; + m_videoWindow = 0; + m_videoWidgetControl = 0; + m_imageCaptureControl = 0; + + if (service == Q_MEDIASERVICE_CAMERA) { + m_captureSession = new CameraBinSession(this); + m_cameraControl = new CameraBinControl(m_captureSession); + m_videoInputDevice = new QGstreamerVideoInputDeviceControl(m_captureSession); + m_imageCaptureControl = new CameraBinImageCapture(m_captureSession); + + connect(m_videoInputDevice, SIGNAL(selectedDeviceChanged(QString)), + m_captureSession, SLOT(setDevice(QString))); + + if (m_videoInputDevice->deviceCount()) + m_captureSession->setDevice(m_videoInputDevice->deviceName(m_videoInputDevice->selectedDevice())); + +#if defined(Q_WS_MAEMO_6) && defined(__arm__) + m_videoRenderer = new QGstreamerGLTextureRenderer(this); +#else + m_videoRenderer = new QGstreamerVideoRenderer(this); +#endif + + +#ifdef Q_WS_MAEMO_6 + m_videoWindow = new QGstreamerVideoWindow(this, "omapxvsink"); + //m_videoWindow = new QGstreamerVideoWindow(this); +#else + m_videoWindow = new QGstreamerVideoOverlay(this); +#endif + + m_videoWidgetControl = new QGstreamerVideoWidgetControl(this); + + } + + if (!m_captureSession) { + qWarning() << Q_FUNC_INFO << "Service type is not supported:" << service; + return; + } + + m_audioInputEndpointSelector = new QGstreamerAudioInputEndpointSelector(this); + connect(m_audioInputEndpointSelector, SIGNAL(activeEndpointChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString))); + + if (m_captureSession && m_audioInputEndpointSelector->availableEndpoints().size() > 0) + m_captureSession->setCaptureDevice(m_audioInputEndpointSelector->defaultEndpoint()); + + m_metaDataControl = new CameraBinMetaData(this); + connect(m_metaDataControl, SIGNAL(metaDataChanged(QMap)), + m_captureSession, SLOT(setMetaData(QMap))); + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + new CameraButtonListener(this); +#endif + +#if defined(Q_WS_MAEMO_5) + //disable the system camera application + QProcess::execute("/usr/sbin/dsmetool -k /usr/bin/camera-ui"); +#endif +} + +CameraBinService::~CameraBinService() +{ +#if defined(Q_WS_MAEMO_5) + //restore the system camera application + QProcess::execute("/usr/sbin/dsmetool -U user -o /usr/bin/camera-ui"); +#endif +} + +QMediaControl *CameraBinService::requestControl(const char *name) +{ + if (!m_captureSession) + return 0; + + //qDebug() << "Request control" << name; + + if (!m_videoOutput) { + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + m_videoOutput = m_videoRenderer; + m_captureSession->setViewfinder(m_videoRenderer); + } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + m_videoOutput = m_videoWindow; + m_captureSession->setViewfinder(m_videoWindow); + } else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + m_captureSession->setViewfinder(m_videoWidgetControl); + m_videoOutput = m_videoWidgetControl; + } + + if (m_videoOutput) + return m_videoOutput; + } + + if (qstrcmp(name,QAudioEndpointSelector_iid) == 0) + return m_audioInputEndpointSelector; + + if (qstrcmp(name,QVideoDeviceControl_iid) == 0) + return m_videoInputDevice; + + if (qstrcmp(name,QMediaRecorderControl_iid) == 0) + return m_captureSession->recorderControl(); + + if (qstrcmp(name,QAudioEncoderControl_iid) == 0) + return m_captureSession->audioEncodeControl(); + + if (qstrcmp(name,QVideoEncoderControl_iid) == 0) + return m_captureSession->videoEncodeControl(); + + if (qstrcmp(name,QImageEncoderControl_iid) == 0) + return m_captureSession->imageEncodeControl(); + + + if (qstrcmp(name,QMediaContainerControl_iid) == 0) + return m_captureSession->mediaContainerControl(); + + if (qstrcmp(name,QCameraControl_iid) == 0) + return m_cameraControl; + + if (qstrcmp(name,QMetaDataWriterControl_iid) == 0) + return m_metaDataControl; + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return m_imageCaptureControl; + + if (qstrcmp(name, QCameraExposureControl_iid) == 0) + return m_captureSession->cameraExposureControl(); + + if (qstrcmp(name, QCameraFlashControl_iid) == 0) + return m_captureSession->cameraFlashControl(); + + if (qstrcmp(name, QCameraFocusControl_iid) == 0) + return m_captureSession->cameraFocusControl(); + + if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0) + return m_captureSession->imageProcessingControl(); + + if (qstrcmp(name, QCameraLocksControl_iid) == 0) + return m_captureSession->cameraLocksControl(); + + if (qstrcmp(name, QCameraCaptureDestinationControl_iid) == 0) + return m_captureSession->captureDestinationControl(); + + if (qstrcmp(name, QCameraCaptureBufferFormatControl_iid) == 0) + return m_captureSession->captureBufferFormatControl(); + + return 0; +} + +void CameraBinService::releaseControl(QMediaControl *control) +{ + if (control && control == m_videoOutput) { + m_videoOutput = 0; + m_captureSession->setViewfinder(0); + } +} + +bool CameraBinService::isCameraBinAvailable() +{ + GstElementFactory *factory = gst_element_factory_find("camerabin"); + if (factory) { + gst_object_unref(GST_OBJECT(factory)); + return true; + } + + return false; +} diff --git a/src/plugins/gstreamer/camerabin/camerabinservice.h b/src/plugins/gstreamer/camerabin/camerabinservice.h new file mode 100644 index 000000000..11c5aeadf --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinservice.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINCAPTURESERVICE_H +#define CAMERABINCAPTURESERVICE_H + +#include + +#include +QT_BEGIN_NAMESPACE +class QAudioEndpointSelector; +class QVideoDeviceControl; +QT_END_NAMESPACE + +class CameraBinSession; +class CameraBinControl; +class QGstreamerMessage; +class QGstreamerBusHelper; +class QGstreamerVideoRenderer; +class QGstreamerVideoOverlay; +class QGstreamerVideoWidgetControl; +class QGstreamerElementFactory; +class CameraBinMetaData; +class CameraBinImageCapture; +class CameraBinMetaData; + +class CameraBinService : public QMediaService +{ + Q_OBJECT + +public: + CameraBinService(const QString &service, QObject *parent = 0); + virtual ~CameraBinService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *); + + static bool isCameraBinAvailable(); + +private: + void setAudioPreview(GstElement*); + + CameraBinSession *m_captureSession; + CameraBinControl *m_cameraControl; + CameraBinMetaData *m_metaDataControl; + + QAudioEndpointSelector *m_audioInputEndpointSelector; + QVideoDeviceControl *m_videoInputDevice; + + QMediaControl *m_videoOutput; + + QMediaControl *m_videoRenderer; + QMediaControl *m_videoWindow; + QGstreamerVideoWidgetControl *m_videoWidgetControl; + CameraBinImageCapture *m_imageCaptureControl; +}; + +#endif // CAMERABINCAPTURESERVICE_H diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.cpp b/src/plugins/gstreamer/camerabin/camerabinsession.cpp new file mode 100644 index 000000000..e84a5af24 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinsession.cpp @@ -0,0 +1,1267 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "camerabinsession.h" +#include "camerabinrecorder.h" +#include "camerabincontainer.h" +#include "camerabinaudioencoder.h" +#include "camerabinvideoencoder.h" +#include "camerabinimageencoder.h" +#include "camerabinexposure.h" +#include "camerabinflash.h" +#include "camerabinfocus.h" +#include "camerabinimageprocessing.h" +#include "camerabinlocks.h" +#include "camerabincapturedestination.h" +#include "camerabincapturebufferformat.h" +#include "qgstreamerbushelper.h" +#include "qgstreamervideorendererinterface.h" +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +//#define CAMERABIN_DEBUG 1 +#define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) + +#ifdef Q_WS_MAEMO_5 +#define FILENAME_PROPERTY "filename" +#define MODE_PROPERTY "mode" +#define MUTE_PROPERTY "mute" +#define ZOOM_PROPERTY "zoom" +#define IMAGE_PP_PROPERTY "imagepp" +#define IMAGE_ENCODER_PROPERTY "imageenc" +#define VIDEO_PP_PROPERTY "videopp" +#define VIDEO_ENCODER_PROPERTY "videoenc" +#define AUDIO_ENCODER_PROPERTY "audioenc" +#define VIDEO_MUXER_PROPERTY "videomux" +#define VIEWFINDER_SINK_PROPERTY "vfsink" +#define VIDEO_SOURCE_PROPERTY "videosrc" +#define AUDIO_SOURCE_PROPERTY "audiosrc" +#define VIDEO_SOURCE_CAPS_PROPERTY "inputcaps" +#define FILTER_CAPS_PROPERTY "filter-caps" +#define PREVIEW_CAPS_PROPERTY "preview-caps" + +#define IMAGE_DONE_SIGNAL "img-done" +#define CAPTURE_START "user-start" +#define CAPTURE_STOP "user-stop" +#define CAPTURE_PAUSE "user-pause" +#define SET_VIDEO_RESOLUTION_FPS "user-res-fps" +#define SET_IMAGE_RESOLUTION "user-image-res" + +#else + +#define FILENAME_PROPERTY "filename" +#define MODE_PROPERTY "mode" +#define MUTE_PROPERTY "mute" +#define ZOOM_PROPERTY "zoom" +#define IMAGE_PP_PROPERTY "image-post-processing" +#define IMAGE_ENCODER_PROPERTY "image-encoder" +#define VIDEO_PP_PROPERTY "video-post-processing" +#define VIDEO_ENCODER_PROPERTY "video-encoder" +#define AUDIO_ENCODER_PROPERTY "audio-encoder" +#define VIDEO_MUXER_PROPERTY "video-muxer" +#define VIEWFINDER_SINK_PROPERTY "viewfinder-sink" +#define VIDEO_SOURCE_PROPERTY "video-source" +#define AUDIO_SOURCE_PROPERTY "audio-source" +#define VIDEO_SOURCE_CAPS_PROPERTY "video-source-caps" +#define FILTER_CAPS_PROPERTY "filter-caps" +#define PREVIEW_CAPS_PROPERTY "preview-caps" + +#define IMAGE_DONE_SIGNAL "image-done" +#define CAPTURE_START "capture-start" +#define CAPTURE_STOP "capture-stop" +#define CAPTURE_PAUSE "capture-pause" +#define SET_VIDEO_RESOLUTION_FPS "set-video-resolution-fps" +#define SET_IMAGE_RESOLUTION "set-image-resolution" +#endif + +#define CAMERABIN_IMAGE_MODE 0 +#define CAMERABIN_VIDEO_MODE 1 + +#define gstRef(element) { gst_object_ref(GST_OBJECT(element)); gst_object_sink(GST_OBJECT(element)); } +#define gstUnref(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } } + +#define PREVIEW_CAPS_4_3 \ + "video/x-raw-rgb, width = (int) 640, height = (int) 480" + +#define VIEWFINDER_RESOLUTION_4x3 QSize(640, 480) +#define VIEWFINDER_RESOLUTION_3x2 QSize(720, 480) +#define VIEWFINDER_RESOLUTION_16x9 QSize(800, 450) + +//using GST_STATE_READY for QCamera::LoadedState +//doesn't work reliably at least with some webcams. +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) +#define USE_READY_STATE_ON_LOADED +#endif + +CameraBinSession::CameraBinSession(QObject *parent) + :QObject(parent), + m_state(QCamera::UnloadedState), + m_pendingState(QCamera::UnloadedState), + m_recordingActive(false), + m_pendingResolutionUpdate(false), + m_muted(false), + m_busy(false), + m_captureMode(QCamera::CaptureStillImage), + m_audioInputFactory(0), + m_videoInputFactory(0), + m_viewfinder(0), + m_viewfinderInterface(0), + m_pipeline(0), + m_videoSrc(0), + m_viewfinderElement(0), + m_viewfinderHasChanged(true), + m_videoInputHasChanged(true), + m_sourceCaps(0), + m_audioSrc(0), + m_audioConvert(0), + m_capsFilter(0), + m_fileSink(0), + m_audioEncoder(0), + m_muxer(0) +{ + m_pipeline = gst_element_factory_make("camerabin", "camerabin"); + g_signal_connect(G_OBJECT(m_pipeline), "notify::idle", G_CALLBACK(updateBusyStatus), this); + + gstRef(m_pipeline); + + m_bus = gst_element_get_bus(m_pipeline); + + m_busHelper = new QGstreamerBusHelper(m_bus, this); + m_busHelper->installSyncEventFilter(this); + connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(handleBusMessage(QGstreamerMessage))); + m_audioEncodeControl = new CameraBinAudioEncoder(this); + m_videoEncodeControl = new CameraBinVideoEncoder(this); + m_imageEncodeControl = new CameraBinImageEncoder(this); + m_recorderControl = new CameraBinRecorder(this); + m_mediaContainerControl = new CameraBinContainer(this); + m_cameraExposureControl = new CameraBinExposure(this); + m_cameraFlashControl = new CameraBinFlash(this); + m_cameraFocusControl = new CameraBinFocus(this); + m_imageProcessingControl = new CameraBinImageProcessing(this); + m_cameraLocksControl = new CameraBinLocks(this); + m_captureDestinationControl = new CameraBinCaptureDestination(this); + m_captureBufferFormatControl = new CameraBinCaptureBufferFormat(this); +} + +CameraBinSession::~CameraBinSession() +{ + if (m_pipeline) { + if (m_viewfinderInterface) + m_viewfinderInterface->stopRenderer(); + + gst_element_set_state(m_pipeline, GST_STATE_NULL); + gst_element_get_state(m_pipeline, NULL, NULL, GST_CLOCK_TIME_NONE); + gstUnref(m_pipeline); + gstUnref(m_viewfinderElement); + } +} + +GstPhotography *CameraBinSession::photography() +{ + if (GST_IS_PHOTOGRAPHY(m_pipeline)) { + return GST_PHOTOGRAPHY(m_pipeline); + } + + if (!m_videoSrc) { + m_videoSrc = buildVideoSrc(); + + if (m_videoSrc) + g_object_set(m_pipeline, VIDEO_SOURCE_PROPERTY, m_videoSrc, NULL); + else + g_object_get(m_pipeline, VIDEO_SOURCE_PROPERTY, &m_videoSrc, NULL); + + updateVideoSourceCaps(); + m_videoInputHasChanged = false; + } + + if (m_videoSrc && GST_IS_PHOTOGRAPHY(m_videoSrc)) + return GST_PHOTOGRAPHY(m_videoSrc); + + return 0; +} + +CameraBinSession::CameraRole CameraBinSession::cameraRole() const +{ +#ifdef Q_WS_MAEMO_5 + return m_inputDevice == QLatin1String("/dev/video1") ? + FrontCamera : BackCamera; +#endif + + return BackCamera; +} + +bool CameraBinSession::setupCameraBin() +{ + if (m_captureMode == QCamera::CaptureStillImage) { + g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL); + } + + if (m_captureMode == QCamera::CaptureVideo) { + g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL); + + if (!m_recorderControl->findCodecs()) + return false; + + g_object_set(m_pipeline, VIDEO_ENCODER_PROPERTY, m_videoEncodeControl->createEncoder(), NULL); + g_object_set(m_pipeline, AUDIO_ENCODER_PROPERTY, m_audioEncodeControl->createEncoder(), NULL); + g_object_set(m_pipeline, VIDEO_MUXER_PROPERTY, + gst_element_factory_make(m_mediaContainerControl->formatElementName().constData(), NULL), NULL); + } + + if (m_videoInputHasChanged) { + m_videoSrc = buildVideoSrc(); + + if (m_videoSrc) + g_object_set(m_pipeline, VIDEO_SOURCE_PROPERTY, m_videoSrc, NULL); + else + g_object_get(m_pipeline, VIDEO_SOURCE_PROPERTY, &m_videoSrc, NULL); + + updateVideoSourceCaps(); + m_videoInputHasChanged = false; + } + + + if (m_viewfinderHasChanged) { + if (m_viewfinderElement) + gst_object_unref(GST_OBJECT(m_viewfinderElement)); + + m_viewfinderElement = m_viewfinderInterface ? m_viewfinderInterface->videoSink() : 0; +#if CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << "Viewfinder changed, reconfigure."; +#endif + m_viewfinderHasChanged = false; + if (!m_viewfinderElement) { + qWarning() << "Staring camera without viewfinder available"; + m_viewfinderElement = gst_element_factory_make("fakesink", NULL); + } + gst_object_ref(GST_OBJECT(m_viewfinderElement)); + gst_element_set_state(m_pipeline, GST_STATE_NULL); + g_object_set(G_OBJECT(m_pipeline), VIEWFINDER_SINK_PROPERTY, m_viewfinderElement, NULL); + } + + GstCaps *previewCaps = gst_caps_from_string(PREVIEW_CAPS_4_3); + g_object_set(G_OBJECT(m_pipeline), PREVIEW_CAPS_PROPERTY, previewCaps, NULL); + gst_caps_unref(previewCaps); + + return true; +} + +void CameraBinSession::updateVideoSourceCaps() +{ + if (m_sourceCaps) { + gst_caps_unref(m_sourceCaps); + m_sourceCaps = 0; + } + + g_object_get(G_OBJECT(m_pipeline), VIDEO_SOURCE_CAPS_PROPERTY, &m_sourceCaps, NULL); +} + +void CameraBinSession::setupCaptureResolution() +{ + if (m_captureMode == QCamera::CaptureStillImage) { + QSize resolution = m_imageEncodeControl->imageSettings().resolution(); + + //by default select the maximum supported resolution + if (resolution.isEmpty()) { + updateVideoSourceCaps(); + bool continuous = false; + QList resolutions = supportedResolutions(qMakePair(0,0), + &continuous, + QCamera::CaptureStillImage); + if (!resolutions.isEmpty()) + resolution = resolutions.last(); + } + + QString previewCapsString = PREVIEW_CAPS_4_3; + QSize viewfinderResolution = VIEWFINDER_RESOLUTION_4x3; + + if (!resolution.isEmpty()) { +#if CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << "set image resolution" << resolution; +#endif + g_signal_emit_by_name(G_OBJECT(m_pipeline), SET_IMAGE_RESOLUTION, resolution.width(), resolution.height(), NULL); + + previewCapsString = QString("video/x-raw-rgb, width = (int) %1, height = (int) 480") + .arg(resolution.width()*480/resolution.height()); + + if (!resolution.isEmpty()) { + qreal aspectRatio = qreal(resolution.width()) / resolution.height(); + if (aspectRatio < 1.4) + viewfinderResolution = VIEWFINDER_RESOLUTION_4x3; + else if (aspectRatio > 1.7) + viewfinderResolution = VIEWFINDER_RESOLUTION_16x9; + else + viewfinderResolution = VIEWFINDER_RESOLUTION_3x2; + } + } + + GstCaps *previewCaps = gst_caps_from_string(previewCapsString.toLatin1()); + g_object_set(G_OBJECT(m_pipeline), PREVIEW_CAPS_PROPERTY, previewCaps, NULL); + gst_caps_unref(previewCaps); + + //on low res cameras the viewfinder resolution should not be bigger + //then capture resolution + if (viewfinderResolution.width() > resolution.width()) + viewfinderResolution = resolution; + +#if CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << "set viewfinder resolution" << viewfinderResolution; +#endif + g_signal_emit_by_name(G_OBJECT(m_pipeline), + SET_VIDEO_RESOLUTION_FPS, + viewfinderResolution.width(), + viewfinderResolution.height(), + 0, // maximum framerate + 1, // framerate denom + NULL); + } + + if (m_captureMode == QCamera::CaptureVideo) { + QSize resolution = m_videoEncodeControl->videoSettings().resolution(); + qreal framerate = m_videoEncodeControl->videoSettings().frameRate(); + + if (resolution.isEmpty()) { + //select the hightest supported resolution + + updateVideoSourceCaps(); + bool continuous = false; + QList resolutions = supportedResolutions(qMakePair(0,0), + &continuous, + QCamera::CaptureVideo); + if (!resolutions.isEmpty()) + resolution = resolutions.last(); + } + + if (!resolution.isEmpty() || framerate > 0) { +#if CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << "set video resolution" << resolution; +#endif + g_signal_emit_by_name(G_OBJECT(m_pipeline), + SET_VIDEO_RESOLUTION_FPS, + resolution.width(), + resolution.height(), + 0, //framerate nom == max rate + 1, // framerate denom == max rate + NULL); + } + } +} + +GstElement *CameraBinSession::buildVideoSrc() +{ + GstElement *videoSrc = 0; + if (m_videoInputFactory) { + videoSrc = m_videoInputFactory->buildElement(); + } else { + QList candidates; + candidates << "subdevsrc" + << "v4l2camsrc" + << "v4l2src" + << "autovideosrc"; + QByteArray sourceElementName; + + foreach(sourceElementName, candidates) { + videoSrc = gst_element_factory_make(sourceElementName.constData(), "camera_source"); + if (videoSrc) + break; + } + + if (videoSrc && !m_inputDevice.isEmpty()) { +#if CAMERABIN_DEBUG + qDebug() << "set camera device" << m_inputDevice; +#endif + if (sourceElementName == "subdevsrc") { + if (m_inputDevice == QLatin1String("secondary")) + g_object_set(G_OBJECT(videoSrc), "camera-device", 1, NULL); + else + g_object_set(G_OBJECT(videoSrc), "camera-device", 0, NULL); + } else { + g_object_set(G_OBJECT(videoSrc), "device", m_inputDevice.toLocal8Bit().constData(), NULL); + } + } + } + + return videoSrc; +} + +void CameraBinSession::captureImage(int requestId, const QString &fileName) +{ + QString actualFileName = fileName; + if (actualFileName.isEmpty()) + actualFileName = generateFileName("img_", defaultDir(QCamera::CaptureStillImage), "jpg"); + + m_requestId = requestId; + + g_object_set(G_OBJECT(m_pipeline), FILENAME_PROPERTY, actualFileName.toLocal8Bit().constData(), NULL); + + g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_START, NULL); + + m_imageFileName = actualFileName; +} + +void CameraBinSession::setCaptureMode(QCamera::CaptureMode mode) +{ + m_captureMode = mode; + + switch (m_captureMode) { + case QCamera::CaptureStillImage: + g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_IMAGE_MODE, NULL); + break; + case QCamera::CaptureVideo: + g_object_set(m_pipeline, MODE_PROPERTY, CAMERABIN_VIDEO_MODE, NULL); + break; + } +} + +QUrl CameraBinSession::outputLocation() const +{ + //return the location service wrote data to, not one set by user, it can be empty. + return m_actualSink; +} + +bool CameraBinSession::setOutputLocation(const QUrl& sink) +{ + m_sink = m_actualSink = sink; + return true; +} + +QDir CameraBinSession::defaultDir(QCamera::CaptureMode mode) const +{ + QStringList dirCandidates; + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + dirCandidates << QLatin1String("/home/user/MyDocs/DCIM"); + dirCandidates << QLatin1String("/home/user/MyDocs/"); +#endif + + if (mode == QCamera::CaptureVideo) { + dirCandidates << QDesktopServices::storageLocation(QDesktopServices::MoviesLocation); + dirCandidates << QDir::home().filePath("Documents/Video"); + dirCandidates << QDir::home().filePath("Documents/Videos"); + } else { + dirCandidates << QDesktopServices::storageLocation(QDesktopServices::PicturesLocation); + dirCandidates << QDir::home().filePath("Documents/Photo"); + dirCandidates << QDir::home().filePath("Documents/Photos"); + dirCandidates << QDir::home().filePath("Documents/photo"); + dirCandidates << QDir::home().filePath("Documents/photos"); + dirCandidates << QDir::home().filePath("Documents/Images"); + } + + dirCandidates << QDir::home().filePath("Documents"); + dirCandidates << QDir::home().filePath("My Documents"); + dirCandidates << QDir::homePath(); + dirCandidates << QDir::currentPath(); + dirCandidates << QDir::tempPath(); + + foreach (const QString &path, dirCandidates) { + if (QFileInfo(path).isWritable()) + return QDir(path); + } + + return QDir(); +} + +QString CameraBinSession::generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const +{ + int lastClip = 0; + foreach(QString fileName, dir.entryList(QStringList() << QString("%1*.%2").arg(prefix).arg(ext))) { + int imgNumber = fileName.mid(prefix.length(), fileName.size()-prefix.length()-ext.length()-1).toInt(); + lastClip = qMax(lastClip, imgNumber); + } + + QString name = QString("%1%2.%3").arg(prefix) + .arg(lastClip+1, + 4, //fieldWidth + 10, + QLatin1Char('0')) + .arg(ext); + + return dir.absoluteFilePath(name); +} + +void CameraBinSession::setDevice(const QString &device) +{ + if (m_inputDevice != device) { + m_inputDevice = device; + m_videoInputHasChanged = true; + } +} + +void CameraBinSession::setAudioInput(QGstreamerElementFactory *audioInput) +{ + m_audioInputFactory = audioInput; +} + +void CameraBinSession::setVideoInput(QGstreamerElementFactory *videoInput) +{ + m_videoInputFactory = videoInput; + m_videoInputHasChanged = true; +} + +bool CameraBinSession::isReady() const +{ + //it's possible to use QCamera without any viewfinder attached + return !m_viewfinderInterface || m_viewfinderInterface->isReady(); +} + +void CameraBinSession::setViewfinder(QObject *viewfinder) +{ + if (m_viewfinderInterface) + m_viewfinderInterface->stopRenderer(); + + m_viewfinderInterface = qobject_cast(viewfinder); + if (!m_viewfinderInterface) + viewfinder = 0; + + if (m_viewfinder != viewfinder) { + bool oldReady = isReady(); + + if (m_viewfinder) { + disconnect(m_viewfinder, SIGNAL(sinkChanged()), + this, SLOT(handleViewfinderChange())); + disconnect(m_viewfinder, SIGNAL(readyChanged(bool)), + this, SIGNAL(readyChanged(bool))); + } + + m_viewfinder = viewfinder; + m_viewfinderHasChanged = true; + + if (m_viewfinder) { + connect(m_viewfinder, SIGNAL(sinkChanged()), + this, SLOT(handleViewfinderChange())); + connect(m_viewfinder, SIGNAL(readyChanged(bool)), + this, SIGNAL(readyChanged(bool))); + } + + emit viewfinderChanged(); + if (oldReady != isReady()) + emit readyChanged(isReady()); + } +} + +void CameraBinSession::handleViewfinderChange() +{ + //the viewfinder will be reloaded + //shortly when the pipeline is started + m_viewfinderHasChanged = true; + emit viewfinderChanged(); +} + +QCamera::State CameraBinSession::state() const +{ + return m_state; +} + +void CameraBinSession::setState(QCamera::State newState) +{ + if (newState == m_pendingState) + return; + + m_pendingState = newState; + +#if CAMERABIN_DEBUG + qDebug() << Q_FUNC_INFO << ENUM_NAME(QCamera, "State", newState); +#endif + + switch (newState) { + case QCamera::UnloadedState: + if (m_recordingActive) + stopVideoRecording(); + + if (m_viewfinderInterface) + m_viewfinderInterface->stopRenderer(); + + gst_element_set_state(m_pipeline, GST_STATE_NULL); + m_state = newState; + if (m_busy) + emit busyChanged(m_busy = false); + + emit stateChanged(m_state); + break; + case QCamera::LoadedState: + if (m_recordingActive) + stopVideoRecording(); + + if (m_videoInputHasChanged) { + if (m_viewfinderInterface) + m_viewfinderInterface->stopRenderer(); + + gst_element_set_state(m_pipeline, GST_STATE_NULL); + m_videoSrc = buildVideoSrc(); + g_object_set(m_pipeline, VIDEO_SOURCE_PROPERTY, m_videoSrc, NULL); + updateVideoSourceCaps(); + m_videoInputHasChanged = false; + } +#ifdef USE_READY_STATE_ON_LOADED + gst_element_set_state(m_pipeline, GST_STATE_READY); +#else + m_state = QCamera::LoadedState; + if (m_viewfinderInterface) + m_viewfinderInterface->stopRenderer(); + gst_element_set_state(m_pipeline, GST_STATE_NULL); + emit stateChanged(m_state); +#endif + break; + case QCamera::ActiveState: + if (setupCameraBin()) { + GstState binState = GST_STATE_NULL; + GstState pending = GST_STATE_NULL; + gst_element_get_state(m_pipeline, &binState, &pending, 0); + + if (pending == GST_STATE_VOID_PENDING && binState == GST_STATE_READY) { + m_pendingResolutionUpdate = false; + setupCaptureResolution(); + gst_element_set_state(m_pipeline, GST_STATE_PLAYING); + } else { + m_pendingResolutionUpdate = true; + gst_element_set_state(m_pipeline, GST_STATE_READY); + } + } + } +} + +bool CameraBinSession::isBusy() const +{ + return m_busy; +} + +void CameraBinSession::updateBusyStatus(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(p); + CameraBinSession *session = reinterpret_cast(d); + + bool idle = false; + g_object_get(o, "idle", &idle, NULL); + bool busy = !idle; + + if (session->m_busy != busy) { + session->m_busy = busy; + QMetaObject::invokeMethod(session, "busyChanged", + Qt::QueuedConnection, + Q_ARG(bool, busy)); + } +} + +qint64 CameraBinSession::duration() const +{ + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; + + if ( m_pipeline && gst_element_query_position(m_pipeline, &format, &duration)) + return duration / 1000000; + else + return 0; +} + +bool CameraBinSession::isMuted() const +{ + return m_muted; +} + +void CameraBinSession::setMuted(bool muted) +{ + if (m_muted != muted) { + m_muted = muted; + + if (m_pipeline) + g_object_set(G_OBJECT(m_pipeline), MUTE_PROPERTY, m_muted, NULL); + emit mutedChanged(m_muted); + } +} + +void CameraBinSession::setCaptureDevice(const QString &deviceName) +{ + m_captureDevice = deviceName; +} + +void CameraBinSession::setMetaData(const QMap &data) +{ + m_metaData = data; + + if (m_pipeline) { + GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_pipeline), GST_TYPE_TAG_SETTER); + GstElement *element = 0; + while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + const QString tagName = it.key(); + const QVariant tagValue = it.value(); + + switch(tagValue.type()) { + case QVariant::String: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE_ALL, + tagName.toUtf8().constData(), + tagValue.toString().toUtf8().constData(), + NULL); + break; + case QVariant::Int: + case QVariant::LongLong: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE_ALL, + tagName.toUtf8().constData(), + tagValue.toInt(), + NULL); + break; + case QVariant::Double: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE_ALL, + tagName.toUtf8().constData(), + tagValue.toDouble(), + NULL); + break; + default: + break; + } + } + } + } +} + +bool CameraBinSession::processSyncMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + const GstStructure *st; + const GValue *image; + GstBuffer *buffer = NULL; + + if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) { + if (m_captureMode == QCamera::CaptureStillImage && + gst_structure_has_name(gm->structure, "preview-image")) { + st = gst_message_get_structure(gm); + if (gst_structure_has_field_typed(st, "buffer", GST_TYPE_BUFFER)) { + image = gst_structure_get_value(st, "buffer"); + if (image) { + buffer = gst_value_get_buffer(image); + + QImage img; + + GstCaps *caps = gst_buffer_get_caps(buffer); + if (caps) { + GstStructure *structure = gst_caps_get_structure(caps, 0); + gint width = 0; + gint height = 0; + + if (structure && + gst_structure_get_int(structure, "width", &width) && + gst_structure_get_int(structure, "height", &height) && + width > 0 && height > 0) { + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { + QImage::Format format = QImage::Format_Invalid; + int bpp = 0; + gst_structure_get_int(structure, "bpp", &bpp); + + if (bpp == 24) + format = QImage::Format_RGB888; + else if (bpp == 32) + format = QImage::Format_RGB32; + + if (format != QImage::Format_Invalid) { + img = QImage((const uchar *)buffer->data, width, height, format); + img.bits(); //detach + } + } + } + gst_caps_unref(caps); + + static int exposedSignalIndex = metaObject()->indexOfSignal("imageExposed(int)"); + metaObject()->method(exposedSignalIndex).invoke(this, + Qt::QueuedConnection, + Q_ARG(int,m_requestId)); + + static int signalIndex = metaObject()->indexOfSignal("imageCaptured(int,QImage)"); + metaObject()->method(signalIndex).invoke(this, + Qt::QueuedConnection, + Q_ARG(int,m_requestId), + Q_ARG(QImage,img)); + } + + } + return true; + } + } + + if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) { + if (m_viewfinderInterface) + m_viewfinderInterface->precessNewStream(); + + return true; + } + + if (gst_structure_has_name(gm->structure, GST_PHOTOGRAPHY_AUTOFOCUS_DONE)) + m_cameraFocusControl->handleFocusMessage(gm); + + if (m_viewfinderInterface && GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_viewfinderElement)) + m_viewfinderInterface->handleSyncMessage(gm); + } + + return false; +} + +void CameraBinSession::handleBusMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm) { + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { + GError *err; + gchar *debug; + gst_message_parse_error (gm, &err, &debug); + + QString message; + + if (err && err->message) { + message = QString::fromUtf8(err->message); + qWarning() << "CameraBin error:" << message; + } + + //only report error messager from camerabin + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { + if (message.isEmpty()) + message = tr("Camera error"); + + emit error(int(QMediaRecorder::ResourceError), message); + } + + if (err) + g_error_free (err); + + if (debug) + g_free (debug); + } + + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_WARNING) { + GError *err; + gchar *debug; + gst_message_parse_warning (gm, &err, &debug); + + if (err && err->message) + qWarning() << "CameraBin warning:" << QString::fromUtf8(err->message); + + if (err) + g_error_free (err); + if (debug) + g_free (debug); + } + + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { + switch (GST_MESSAGE_TYPE(gm)) { + case GST_MESSAGE_DURATION: + break; + + case GST_MESSAGE_STATE_CHANGED: + { + + GstState oldState; + GstState newState; + GstState pending; + + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + + +#if CAMERABIN_DEBUG + QStringList states; + states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING"; + + + qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \ + .arg(states[oldState]) \ + .arg(states[newState]) \ + .arg(states[pending]); +#endif + + switch (newState) { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + if (m_state != QCamera::UnloadedState) + emit stateChanged(m_state = QCamera::UnloadedState); + break; + case GST_STATE_READY: + if (m_pendingResolutionUpdate) { + m_pendingResolutionUpdate = false; + setupCaptureResolution(); + gst_element_set_state(m_pipeline, GST_STATE_PLAYING); + } + if (m_state != QCamera::LoadedState) + emit stateChanged(m_state = QCamera::LoadedState); + break; + case GST_STATE_PAUSED: + case GST_STATE_PLAYING: + emit stateChanged(m_state = QCamera::ActiveState); + break; + } + } + break; + default: + break; + } + //qDebug() << "New session state:" << ENUM_NAME(CameraBinSession,"State",m_state); + } + + if (m_viewfinderInterface && GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_viewfinderElement)) + m_viewfinderInterface->handleBusMessage(gm); + + emit busMessage(message); + } +} + +void CameraBinSession::recordVideo() +{ + m_recordingActive = true; + m_actualSink = m_sink; + if (m_actualSink.isEmpty()) { + QString ext = m_mediaContainerControl->containerMimeType(); + m_actualSink = generateFileName("clip_", defaultDir(QCamera::CaptureVideo), ext); + } + + g_object_set(G_OBJECT(m_pipeline), FILENAME_PROPERTY, m_actualSink.toEncoded().constData(), NULL); + + g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_START, NULL); +} + +void CameraBinSession::resumeVideoRecording() +{ + m_recordingActive = true; + g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_START, NULL); +} + + +void CameraBinSession::pauseVideoRecording() +{ + g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_PAUSE, NULL); +} + +void CameraBinSession::stopVideoRecording() +{ + m_recordingActive = false; + g_signal_emit_by_name(G_OBJECT(m_pipeline), CAPTURE_STOP, NULL); +} + +//internal, only used by CameraBinSession::supportedFrameRates. +//recursively fills the list of framerates res from value data. +static void readValue(const GValue *value, QList< QPair > *res, bool *continuous) +{ + if (GST_VALUE_HOLDS_FRACTION(value)) { + int num = gst_value_get_fraction_numerator(value); + int denum = gst_value_get_fraction_denominator(value); + + *res << QPair(num, denum); + } else if (GST_VALUE_HOLDS_FRACTION_RANGE(value)) { + const GValue *rateValueMin = gst_value_get_fraction_range_min(value); + const GValue *rateValueMax = gst_value_get_fraction_range_max(value); + + if (continuous) + *continuous = true; + + readValue(rateValueMin, res, continuous); + readValue(rateValueMax, res, continuous); + } else if (GST_VALUE_HOLDS_LIST(value)) { + for (uint i=0; i &r1, const QPair &r2) +{ + return r1.first*r2.second < r2.first*r1.second; +} + +QList< QPair > CameraBinSession::supportedFrameRates(const QSize &frameSize, bool *continuous) const +{ + QList< QPair > res; + + if (!m_sourceCaps) + return res; + + GstCaps *caps = 0; + + if (frameSize.isEmpty()) { + caps = gst_caps_copy(m_sourceCaps); + } else { + GstCaps *filter = gst_caps_new_full( + gst_structure_new( + "video/x-raw-rgb", + "width" , G_TYPE_INT , frameSize.width(), + "height" , G_TYPE_INT, frameSize.height(), NULL), + gst_structure_new( + "video/x-raw-yuv", + "width" , G_TYPE_INT, frameSize.width(), + "height" , G_TYPE_INT, frameSize.height(), NULL), + gst_structure_new( + "image/jpeg", + "width" , G_TYPE_INT, frameSize.width(), + "height" , G_TYPE_INT, frameSize.height(), NULL), + NULL); + + caps = gst_caps_intersect(m_sourceCaps, filter); + gst_caps_unref(filter); + } + + //simplify to the list of rates only: + gst_caps_make_writable(caps); + for (uint i=0; i valueRange(const GValue *value, bool *continuous) +{ + int minValue = 0; + int maxValue = 0; + + if (g_value_type_compatible(G_VALUE_TYPE(value), G_TYPE_INT)) { + minValue = maxValue = g_value_get_int(value); + } else if (GST_VALUE_HOLDS_INT_RANGE(value)) { + minValue = gst_value_get_int_range_min(value); + maxValue = gst_value_get_int_range_max(value); + *continuous = true; + } else if (GST_VALUE_HOLDS_LIST(value)) { + for (uint i=0; i res = valueRange(gst_value_list_get_value(value, i), continuous); + + if (res.first > 0 && minValue > 0) + minValue = qMin(minValue, res.first); + else //select non 0 valid value + minValue = qMax(minValue, res.first); + + maxValue = qMax(maxValue, res.second); + } + } + + return QPair(minValue, maxValue); +} + +static bool resolutionLessThan(const QSize &r1, const QSize &r2) +{ + return r1.width()*r1.height() < r2.width()*r2.height(); +} + + +QList CameraBinSession::supportedResolutions(QPair rate, + bool *continuous, + QCamera::CaptureMode mode) const +{ + QList res; + + if (continuous) + *continuous = false; + + if (!m_sourceCaps) + return res; + +#if CAMERABIN_DEBUG + qDebug() << "Source caps:" << gst_caps_to_string(m_sourceCaps); +#endif + + GstCaps *caps = 0; + bool isContinuous = false; + + if (rate.first <= 0 || rate.second <= 0) { + caps = gst_caps_copy(m_sourceCaps); + } else { + GstCaps *filter = gst_caps_new_full( + gst_structure_new( + "video/x-raw-rgb", + "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), + gst_structure_new( + "video/x-raw-yuv", + "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), + gst_structure_new( + "image/jpeg", + "framerate" , GST_TYPE_FRACTION , rate.first, rate.second, NULL), + NULL); + + caps = gst_caps_intersect(m_sourceCaps, filter); + gst_caps_unref(filter); + } + + //simplify to the list of resolutions only: + gst_caps_make_writable(caps); + for (uint i=0; i wRange = valueRange(wValue, &isContinuous); + QPair hRange = valueRange(hValue, &isContinuous); + + QSize minSize(wRange.first, hRange.first); + QSize maxSize(wRange.second, hRange.second); + + if (!minSize.isEmpty()) + res << minSize; + + if (minSize != maxSize && !maxSize.isEmpty()) + res << maxSize; + } + + + qSort(res.begin(), res.end(), resolutionLessThan); + + //if the range is continuos, populate is with the common rates + if (isContinuous && res.size() >= 2) { + //fill the ragne with common value + static QList commonSizes = + QList() << QSize(128, 96) + << QSize(160,120) + << QSize(176, 144) + << QSize(320, 240) + << QSize(352, 288) + << QSize(640, 480) + << QSize(848, 480) + << QSize(854, 480) + << QSize(1024, 768) + << QSize(1280, 720) // HD 720 + << QSize(1280, 1024) + << QSize(1600, 1200) + << QSize(1920, 1080) // HD + << QSize(1920, 1200) + << QSize(2048, 1536) + << QSize(2560, 1600) + << QSize(2580, 1936); + QSize minSize = res.first(); + QSize maxSize = res.last(); + +#ifdef Q_WS_MAEMO_5 + if (mode == QCamera::CaptureVideo && cameraRole() == BackCamera) + maxSize = QSize(848, 480); + if (mode == QCamera::CaptureStillImage) + minSize = QSize(640, 480); +#elif defined(Q_WS_MAEMO_6) + if (cameraRole() == FrontCamera && maxSize.width() > 640) + maxSize = QSize(640, 480); + else if (mode == QCamera::CaptureVideo && maxSize.width() > 1280) + maxSize = QSize(1280, 720); +#else + Q_UNUSED(mode); +#endif + + res.clear(); + + foreach (const QSize &candidate, commonSizes) { + int w = candidate.width(); + int h = candidate.height(); + + if (w > maxSize.width() && h > maxSize.height()) + break; + + if (w >= minSize.width() && h >= minSize.height() && + w <= maxSize.width() && h <= maxSize.height()) + res << candidate; + } + + if (res.isEmpty() || res.first() != minSize) + res.prepend(minSize); + + if (res.last() != maxSize) + res.append(maxSize); + } + +#if CAMERABIN_DEBUG + qDebug() << "Supported resolutions:" << gst_caps_to_string(caps); + qDebug() << res; +#endif + + gst_caps_unref(caps); + + if (continuous) + *continuous = isContinuous; + + return res; +} diff --git a/src/plugins/gstreamer/camerabin/camerabinsession.h b/src/plugins/gstreamer/camerabin/camerabinsession.h new file mode 100644 index 000000000..9597ea873 --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinsession.h @@ -0,0 +1,234 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINCAPTURESESSION_MAEMO_H +#define CAMERABINCAPTURESESSION_MAEMO_H + +#include + +#include +#include + +#include +#include + +#include "qgstreamerbushelper.h" +#include "qcamera.h" + + +class QGstreamerMessage; +class QGstreamerBusHelper; +class CameraBinAudioEncoder; +class CameraBinVideoEncoder; +class CameraBinImageEncoder; +class CameraBinRecorder; +class CameraBinContainer; +class CameraBinExposure; +class CameraBinFlash; +class CameraBinFocus; +class CameraBinImageProcessing; +class CameraBinLocks; +class CameraBinCaptureDestination; +class CameraBinCaptureBufferFormat; +class QGstreamerVideoRendererInterface; + +class QGstreamerElementFactory +{ +public: + virtual GstElement *buildElement() = 0; +}; + +class CameraBinSession : public QObject, public QGstreamerSyncEventFilter +{ + Q_OBJECT + Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) +public: + enum CameraRole { + FrontCamera, // Secondary camera + BackCamera // Main photo camera + }; + + CameraBinSession(QObject *parent); + ~CameraBinSession(); + + GstPhotography *photography(); + GstElement *cameraBin() { return m_pipeline; } + + CameraRole cameraRole() const; + + QList< QPair > supportedFrameRates(const QSize &frameSize, bool *continuous) const; + QList supportedResolutions( QPair rate, bool *continuous, QCamera::CaptureMode mode) const; + + QCamera::CaptureMode captureMode() { return m_captureMode; } + void setCaptureMode(QCamera::CaptureMode mode); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl& sink); + + QDir defaultDir(QCamera::CaptureMode mode) const; + QString generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const; + + CameraBinAudioEncoder *audioEncodeControl() const { return m_audioEncodeControl; } + CameraBinVideoEncoder *videoEncodeControl() const { return m_videoEncodeControl; } + CameraBinImageEncoder *imageEncodeControl() const { return m_imageEncodeControl; } + CameraBinExposure *cameraExposureControl() const { return m_cameraExposureControl; } + CameraBinFlash *cameraFlashControl() const { return m_cameraFlashControl; } + CameraBinFocus *cameraFocusControl() const { return m_cameraFocusControl; } + CameraBinImageProcessing *imageProcessingControl() const { return m_imageProcessingControl; } + CameraBinLocks *cameraLocksControl() const { return m_cameraLocksControl; } + CameraBinCaptureDestination *captureDestinationControl() const { return m_captureDestinationControl; } + CameraBinCaptureBufferFormat *captureBufferFormatControl() const { return m_captureBufferFormatControl; } + + + CameraBinRecorder *recorderControl() const { return m_recorderControl; } + CameraBinContainer *mediaContainerControl() const { return m_mediaContainerControl; } + + QGstreamerElementFactory *audioInput() const { return m_audioInputFactory; } + void setAudioInput(QGstreamerElementFactory *audioInput); + + QGstreamerElementFactory *videoInput() const { return m_videoInputFactory; } + void setVideoInput(QGstreamerElementFactory *videoInput); + bool isReady() const; + + QObject *viewfinder() const { return m_viewfinder; } + void setViewfinder(QObject *viewfinder); + + void captureImage(int requestId, const QString &fileName); + + QCamera::State state() const; + bool isBusy() const; + + qint64 duration() const; + + void recordVideo(); + void pauseVideoRecording(); + void resumeVideoRecording(); + void stopVideoRecording(); + + bool isMuted() const; + + bool processSyncMessage(const QGstreamerMessage &message); + +signals: + void stateChanged(QCamera::State state); + void durationChanged(qint64 duration); + void error(int error, const QString &errorString); + void imageExposed(int requestId); + void imageCaptured(int requestId, const QImage &img); + void mutedChanged(bool); + void viewfinderChanged(); + void readyChanged(bool); + void busyChanged(bool); + void busMessage(const QGstreamerMessage &message); + +public slots: + void setDevice(const QString &device); + void setState(QCamera::State); + void setCaptureDevice(const QString &deviceName); + void setMetaData(const QMap&); + void setMuted(bool); + +private slots: + void handleBusMessage(const QGstreamerMessage &message); + void handleViewfinderChange(); + +private: + bool setupCameraBin(); + void setupCaptureResolution(); + void updateVideoSourceCaps(); + GstElement *buildVideoSrc(); + static void updateBusyStatus(GObject *o, GParamSpec *p, gpointer d); + + QUrl m_sink; + QUrl m_actualSink; + bool m_recordingActive; + QString m_captureDevice; + QCamera::State m_state; + QCamera::State m_pendingState; + QString m_inputDevice; + bool m_pendingResolutionUpdate; + bool m_muted; + bool m_busy; + + QCamera::CaptureMode m_captureMode; + QMap m_metaData; + + QGstreamerElementFactory *m_audioInputFactory; + QGstreamerElementFactory *m_videoInputFactory; + QObject *m_viewfinder; + QGstreamerVideoRendererInterface *m_viewfinderInterface; + + CameraBinAudioEncoder *m_audioEncodeControl; + CameraBinVideoEncoder *m_videoEncodeControl; + CameraBinImageEncoder *m_imageEncodeControl; + CameraBinRecorder *m_recorderControl; + CameraBinContainer *m_mediaContainerControl; + CameraBinExposure *m_cameraExposureControl; + CameraBinFlash *m_cameraFlashControl; + CameraBinFocus *m_cameraFocusControl; + CameraBinImageProcessing *m_imageProcessingControl; + CameraBinLocks *m_cameraLocksControl; + CameraBinCaptureDestination *m_captureDestinationControl; + CameraBinCaptureBufferFormat *m_captureBufferFormatControl; + + QGstreamerBusHelper *m_busHelper; + GstBus* m_bus; + GstElement *m_pipeline; + GstElement *m_videoSrc; + GstElement *m_viewfinderElement; + bool m_viewfinderHasChanged; + bool m_videoInputHasChanged; + + GstCaps *m_sourceCaps; + + GstElement *m_audioSrc; + GstElement *m_audioConvert; + GstElement *m_capsFilter; + GstElement *m_fileSink; + GstElement *m_audioEncoder; + GstElement *m_muxer; + +public: + QString m_imageFileName; + int m_requestId; +}; + +#endif // CAMERABINCAPTURESESSION_MAEMO_H diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp new file mode 100644 index 000000000..b1809abcb --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.cpp @@ -0,0 +1,346 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabinvideoencoder.h" +#include "camerabinsession.h" +#include "camerabincontainer.h" + +#include + +CameraBinVideoEncoder::CameraBinVideoEncoder(CameraBinSession *session) + :QVideoEncoderControl(session), m_session(session) +{ + QList codecCandidates; +#if defined(Q_WS_MAEMO_5) + codecCandidates << "video/mpeg4" << "video/h264" << "video/h263" << "video/theora" + << "video/mpeg2" << "video/mpeg1" << "video/mjpeg" << "video/VP8" << "video/h261"; + + m_elementNames["video/h264"] = "dsph264enc"; + m_elementNames["video/mpeg4"] = "dspmp4venc"; + m_elementNames["video/h263"] = "dsph263enc"; + m_elementNames["video/theora"] = "theoraenc"; + m_elementNames["video/mpeg2"] = "ffenc_mpeg2video"; + m_elementNames["video/mpeg1"] = "ffenc_mpeg1video"; + m_elementNames["video/mjpeg"] = "ffenc_mjpeg"; + m_elementNames["video/VP8"] = "vp8enc"; + m_elementNames["video/h261"] = "ffenc_h261"; + + m_codecOptions["video/mpeg4"] = QStringList() << "mode" << "keyframe-interval"; +#elif defined(Q_WS_MAEMO_6) + codecCandidates << "video/mpeg4" << "video/h264" << "video/h263"; + + m_elementNames["video/h264"] = "dsph264enc"; + m_elementNames["video/mpeg4"] = "dsphdmp4venc"; + m_elementNames["video/h263"] = "dsph263enc"; + + QStringList options = QStringList() << "mode" << "keyframe-interval" << "max-bitrate" << "intra-refresh"; + m_codecOptions["video/h264"] = options; + m_codecOptions["video/mpeg4"] = options; + m_codecOptions["video/h263"] = options; +#else + codecCandidates << "video/h264" << "video/xvid" << "video/mpeg4" + << "video/mpeg1" << "video/mpeg2" << "video/theora" + << "video/VP8" << "video/h261" << "video/mjpeg"; + + m_elementNames["video/h264"] = "x264enc"; + m_elementNames["video/xvid"] = "xvidenc"; + m_elementNames["video/mpeg4"] = "ffenc_mpeg4"; + m_elementNames["video/mpeg1"] = "ffenc_mpeg1video"; + m_elementNames["video/mpeg2"] = "ffenc_mpeg2video"; + m_elementNames["video/theora"] = "theoraenc"; + m_elementNames["video/mjpeg"] = "ffenc_mjpeg"; + m_elementNames["video/VP8"] = "vp8enc"; + m_elementNames["video/h261"] = "ffenc_h261"; + + m_codecOptions["video/h264"] = QStringList() << "quantizer"; + m_codecOptions["video/xvid"] = QStringList() << "quantizer" << "profile"; + m_codecOptions["video/mpeg4"] = QStringList() << "quantizer"; + m_codecOptions["video/mpeg1"] = QStringList() << "quantizer"; + m_codecOptions["video/mpeg2"] = QStringList() << "quantizer"; + m_codecOptions["video/theora"] = QStringList(); + +#endif + + foreach( const QByteArray& codecName, codecCandidates ) { + QByteArray elementName = m_elementNames[codecName]; + GstElementFactory *factory = gst_element_factory_find(elementName.constData()); + if (factory) { + m_codecs.append(codecName); + const gchar *descr = gst_element_factory_get_description(factory); + m_codecDescriptions.insert(codecName, QString::fromUtf8(descr)); + + m_streamTypes.insert(codecName, + CameraBinContainer::supportedStreamTypes(factory, GST_PAD_SRC)); + + gst_object_unref(GST_OBJECT(factory)); + } + } +} + +CameraBinVideoEncoder::~CameraBinVideoEncoder() +{ +} + +QList CameraBinVideoEncoder::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const +{ + if (continuous) + *continuous = false; + + QPair rate = rateAsRational(settings.frameRate()); + + //select the closest supported rational rate to settings.frameRate() + + return m_session->supportedResolutions(rate, continuous, QCamera::CaptureVideo); +} + +QList< qreal > CameraBinVideoEncoder::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const +{ + if (continuous) + *continuous = false; + + QList< qreal > res; + QPair rate; + + foreach(rate, m_session->supportedFrameRates(settings.resolution(), continuous)) { + if (rate.second > 0) + res << qreal(rate.first)/rate.second; + } + + return res; +} + +QStringList CameraBinVideoEncoder::supportedVideoCodecs() const +{ + return m_codecs; +} + +QString CameraBinVideoEncoder::videoCodecDescription(const QString &codecName) const +{ + return m_codecDescriptions.value(codecName); +} + +QStringList CameraBinVideoEncoder::supportedEncodingOptions(const QString &codec) const +{ + return m_codecOptions.value(codec); +} + +QVariant CameraBinVideoEncoder::encodingOption(const QString &codec, const QString &name) const +{ + return m_options[codec].value(name); +} + +void CameraBinVideoEncoder::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + m_options[codec][name] = value; +} + +QVideoEncoderSettings CameraBinVideoEncoder::videoSettings() const +{ + return m_videoSettings; +} + +void CameraBinVideoEncoder::setVideoSettings(const QVideoEncoderSettings &settings) +{ + m_videoSettings = settings; + m_userSettings = settings; + emit settingsChanged(); +} + +void CameraBinVideoEncoder::setActualVideoSettings(const QVideoEncoderSettings &settings) +{ + m_videoSettings = settings; +} + +void CameraBinVideoEncoder::resetActualSettings() +{ + m_videoSettings = m_userSettings; +} + +GstElement *CameraBinVideoEncoder::createEncoder() +{ + QString codec = m_videoSettings.codec(); + QByteArray elementName = m_elementNames.value(codec); + + GstElement *encoderElement = gst_element_factory_make( elementName.constData(), "video-encoder"); + + if (encoderElement) { + if (m_videoSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) { + QtMultimediaKit::EncodingQuality qualityValue = m_videoSettings.quality(); + + if (elementName == "x264enc") { + //constant quantizer mode + g_object_set(G_OBJECT(encoderElement), "pass", 4, NULL); + int qualityTable[] = { + 50, //VeryLow + 35, //Low + 21, //Normal + 15, //High + 8 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quantizer", qualityTable[qualityValue], NULL); + } else if (elementName == "xvidenc") { + //constant quantizer mode + g_object_set(G_OBJECT(encoderElement), "pass", 3, NULL); + int qualityTable[] = { + 32, //VeryLow + 12, //Low + 5, //Normal + 3, //High + 2 //VeryHigh + }; + int quant = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL); + } else if (elementName == "ffenc_mpeg4" || + elementName == "ffenc_mpeg1video" || + elementName == "ffenc_mpeg2video" ) { + //constant quantizer mode + g_object_set(G_OBJECT(encoderElement), "pass", 2, NULL); + //quant from 1 to 30, default ~3 + double qualityTable[] = { + 20, //VeryLow + 8.0, //Low + 3.0, //Normal + 2.5, //High + 2.0 //VeryHigh + }; + double quant = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL); + } else if (elementName == "theoraenc") { + int qualityTable[] = { + 8, //VeryLow + 16, //Low + 32, //Normal + 45, //High + 60 //VeryHigh + }; + //quality from 0 to 63 + int quality = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "quality", quality, NULL); + } else if (elementName == "dsph264enc" || + elementName == "dspmp4venc" || + elementName == "dsphdmp4venc" || + elementName == "dsph263enc") { + //only bitrate parameter is supported + int qualityTable[] = { + 1000000, //VeryLow + 2000000, //Low + 4000000, //Normal + 8000000, //High + 16000000 //VeryHigh + }; + int bitrate = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL); + } + } else { + int bitrate = m_videoSettings.bitRate(); + if (bitrate > 0) { + g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL); + } + } + + QMap options = m_options.value(codec); + QMapIterator it(options); + while (it.hasNext()) { + it.next(); + QString option = it.key(); + QVariant value = it.value(); + + switch (value.type()) { + case QVariant::Int: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toInt(), NULL); + break; + case QVariant::Bool: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toBool(), NULL); + break; + case QVariant::Double: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toDouble(), NULL); + break; + case QVariant::String: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toString().toUtf8().constData(), NULL); + break; + default: + qWarning() << "unsupported option type:" << option << value; + break; + } + + } + } + + return encoderElement; +} + +QPair CameraBinVideoEncoder::rateAsRational(qreal frameRate) const +{ + if (frameRate > 0.001) { + //convert to rational number + QList denumCandidates; + denumCandidates << 1 << 2 << 3 << 5 << 10 << 25 << 30 << 50 << 100 << 1001 << 1000; + + qreal error = 1.0; + int num = 1; + int denum = 1; + + foreach (int curDenum, denumCandidates) { + int curNum = qRound(frameRate*curDenum); + qreal curError = qAbs(qreal(curNum)/curDenum - frameRate); + + if (curError < error) { + error = curError; + num = curNum; + denum = curDenum; + } + + if (curError < 1e-8) + break; + } + + return QPair(num,denum); + } + + return QPair(); +} + + +QSet CameraBinVideoEncoder::supportedStreamTypes(const QString &codecName) const +{ + return m_streamTypes.value(codecName); +} diff --git a/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h new file mode 100644 index 000000000..0dd9ffbad --- /dev/null +++ b/src/plugins/gstreamer/camerabin/camerabinvideoencoder.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef CAMERABINVIDEOENCODE_H +#define CAMERABINVIDEOENCODE_H + +#include +class CameraBinSession; + +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +class CameraBinVideoEncoder : public QVideoEncoderControl +{ + Q_OBJECT +public: + CameraBinVideoEncoder(CameraBinSession *session); + virtual ~CameraBinVideoEncoder(); + + QList supportedResolutions(const QVideoEncoderSettings &settings = QVideoEncoderSettings(), + bool *continuous = 0) const; + + QList< qreal > supportedFrameRates(const QVideoEncoderSettings &settings = QVideoEncoderSettings(), + bool *continuous = 0) const; + + QPair rateAsRational(qreal) const; + + QStringList supportedVideoCodecs() const; + QString videoCodecDescription(const QString &codecName) const; + + QVideoEncoderSettings videoSettings() const; + void setVideoSettings(const QVideoEncoderSettings &settings); + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + + GstElement *createEncoder(); + + QSet supportedStreamTypes(const QString &codecName) const; + + void setActualVideoSettings(const QVideoEncoderSettings&); + void resetActualSettings(); + +Q_SIGNALS: + void settingsChanged(); + +private: + CameraBinSession *m_session; + + QStringList m_codecs; + QMap m_codecDescriptions; + QMap m_elementNames; + QMap m_codecOptions; + + QVideoEncoderSettings m_videoSettings; // backend selected settings, using m_userSettings + QVideoEncoderSettings m_userSettings; + + QMap > m_options; + QMap > m_streamTypes; +}; + +#endif diff --git a/src/plugins/gstreamer/camerabuttonlistener_maemo.cpp b/src/plugins/gstreamer/camerabuttonlistener_maemo.cpp new file mode 100644 index 000000000..c10156d7a --- /dev/null +++ b/src/plugins/gstreamer/camerabuttonlistener_maemo.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabuttonlistener_maemo.h" + +#include +#include + +#include +#include +#include + + +CameraButtonListener::CameraButtonListener(QObject *parent) : + QObject(parent), + m_focusPressed(false), + m_shutterPressed(false) +{ + QDBusConnection::systemBus().connect( + QString(), + "/org/freedesktop/Hal/devices/platform_cam_launch", + "org.freedesktop.Hal.Device", + "PropertyModified", + this, + SLOT(updateShuterButtonState())); + + QDBusConnection::systemBus().connect( + QString(), + "/org/freedesktop/Hal/devices/platform_cam_focus", + "org.freedesktop.Hal.Device", + "PropertyModified", + this, + SLOT(updateFocusButtonState())); +} + + +CameraButtonListener::~CameraButtonListener() +{ +} + +void CameraButtonListener::updateFocusButtonState() +{ + QDBusInterface propertyInterface("org.freedesktop.Hal", + "/org/freedesktop/Hal/devices/platform_cam_focus", + "org.freedesktop.Hal.Device", + QDBusConnection::systemBus()); + + bool pressed = propertyInterface.call("GetProperty", "button.state.value").arguments().at(0).toBool(); + + if (m_focusPressed != pressed) { + m_focusPressed = pressed; + QWidget *window = QApplication::focusWidget(); + + if (window) { + QApplication::postEvent(window, + new QKeyEvent(pressed ? QEvent::KeyPress : QEvent::KeyRelease, + 0x01100021, //Qt::Key_CameraFocus since Qt 4.7.0 + Qt::NoModifier)); + } + } +} + +void CameraButtonListener::updateShuterButtonState() +{ + QDBusInterface propertyInterface("org.freedesktop.Hal", + "/org/freedesktop/Hal/devices/platform_cam_launch", + "org.freedesktop.Hal.Device", + QDBusConnection::systemBus()); + + bool pressed = propertyInterface.call("GetProperty", "button.state.value").arguments().at(0).toBool(); + + if (m_shutterPressed != pressed) { + m_shutterPressed = pressed; + QWidget *window = QApplication::focusWidget(); + + if (window) { + QApplication::postEvent(window, + new QKeyEvent(pressed ? QEvent::KeyPress : QEvent::KeyRelease, + 0x01100020, //Qt::Key_Camera since Qt 4.7.0 + Qt::NoModifier)); + } + } +} diff --git a/src/plugins/gstreamer/camerabuttonlistener_maemo.h b/src/plugins/gstreamer/camerabuttonlistener_maemo.h new file mode 100644 index 000000000..a4d0153ad --- /dev/null +++ b/src/plugins/gstreamer/camerabuttonlistener_maemo.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef CAMERABUTTONLISTENER_MAEMO_H +#define CAMERABUTTONLISTENER_MAEMO_H + +#include + +class CameraButtonListener : public QObject +{ + Q_OBJECT +public: + CameraButtonListener(QObject *parent = 0); + virtual ~CameraButtonListener(); + +private slots: + void updateFocusButtonState(); + void updateShuterButtonState(); + +private: + bool m_focusPressed; + bool m_shutterPressed; +}; + +#endif // CAMERABUTTONLISTENER_MAEMO_H diff --git a/src/plugins/gstreamer/camerabuttonlistener_meego.cpp b/src/plugins/gstreamer/camerabuttonlistener_meego.cpp new file mode 100644 index 000000000..8be24ba2a --- /dev/null +++ b/src/plugins/gstreamer/camerabuttonlistener_meego.cpp @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "camerabuttonlistener_meego.h" + +#include +#include +#include +#include + +CameraButtonListener::CameraButtonListener(QObject *parent) : + QObject(parent), + m_focusPressed(false), + m_shutterPressed(false) +{ + m_keys = new MeeGo::QmKeys(this); + connect(m_keys, SIGNAL(keyEvent(MeeGo::QmKeys::Key, MeeGo::QmKeys::State)), + this, SLOT(handleQmKeyEvent(MeeGo::QmKeys::Key,MeeGo::QmKeys::State))); +} + +CameraButtonListener::~CameraButtonListener() +{ +} + +void CameraButtonListener::handleQmKeyEvent(MeeGo::QmKeys::Key key, MeeGo::QmKeys::State state) +{ + if (key == MeeGo::QmKeys::Camera) { + QWidget *window = QApplication::focusWidget(); + + bool focusPressed = (state == MeeGo::QmKeys::KeyHalfDown) || + (state == MeeGo::QmKeys::KeyDown); + + if (m_focusPressed != focusPressed) { + m_focusPressed = focusPressed; + if (window) { + QApplication::postEvent(window, + new QKeyEvent(focusPressed ? QEvent::KeyPress : QEvent::KeyRelease, + Qt::Key_CameraFocus, + Qt::NoModifier)); + } + } + + bool shutterPressed = (state == MeeGo::QmKeys::KeyDown); + if (m_shutterPressed != shutterPressed) { + m_shutterPressed = shutterPressed; + if (window) { + QApplication::postEvent(window, + new QKeyEvent(shutterPressed ? QEvent::KeyPress : QEvent::KeyRelease, + Qt::Key_Camera, + Qt::NoModifier)); + } + } + } +} diff --git a/src/plugins/gstreamer/camerabuttonlistener_meego.h b/src/plugins/gstreamer/camerabuttonlistener_meego.h new file mode 100644 index 000000000..88ca26788 --- /dev/null +++ b/src/plugins/gstreamer/camerabuttonlistener_meego.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef CAMERABUTTONLISTENER_MEEGO_H +#define CAMERABUTTONLISTENER_MEEGO_H + +#include +#include + +class CameraButtonListener : public QObject +{ + Q_OBJECT +public: + CameraButtonListener(QObject *parent = 0); + ~CameraButtonListener(); + +private slots: + void handleQmKeyEvent(MeeGo::QmKeys::Key key, MeeGo::QmKeys::State state); + +private: + MeeGo::QmKeys *m_keys; + bool m_focusPressed; + bool m_shutterPressed; +}; + +#endif // CAMERABUTTONLISTENER_MEEGO_H diff --git a/src/plugins/gstreamer/gstreamer.pro b/src/plugins/gstreamer/gstreamer.pro new file mode 100644 index 000000000..681fac4e2 --- /dev/null +++ b/src/plugins/gstreamer/gstreamer.pro @@ -0,0 +1,101 @@ + +load(qt_module) + +TARGET = qgstengine +QT += multimediakit-private network +PLUGIN_TYPE=mediaservice + +load(qt_plugin) +DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE} + +unix:contains(QT_CONFIG, alsa) { +DEFINES += HAVE_ALSA +LIBS += \ + -lasound +} + +CONFIG += link_pkgconfig + +PKGCONFIG += \ + gstreamer-0.10 \ + gstreamer-base-0.10 \ + gstreamer-interfaces-0.10 \ + gstreamer-audio-0.10 \ + gstreamer-video-0.10 + +maemo*:PKGCONFIG +=gstreamer-plugins-bad-0.10 +contains(gstreamer-appsrc_enabled, yes): PKGCONFIG += gstreamer-app-0.10 + +maemo5 { + HEADERS += camerabuttonlistener_maemo.h + SOURCES += camerabuttonlistener_maemo.cpp + + QT += dbus +} + +maemo6 { + HEADERS += camerabuttonlistener_meego.h + SOURCES += camerabuttonlistener_meego.cpp + + PKGCONFIG += qmsystem2 libresourceqt1 + + isEqual(QT_ARCH,armv6) { + HEADERS += qgstreamergltexturerenderer.h + SOURCES += qgstreamergltexturerenderer.cpp + QT += opengl + LIBS += -lEGL -lgstmeegointerfaces-0.10 + } +} + +# Input +HEADERS += \ + qgstreamermessage.h \ + qgstreamerbushelper.h \ + qgstreamervideorendererinterface.h \ + qgstreamerserviceplugin.h \ + qgstreameraudioinputendpointselector.h \ + qgstreamervideorenderer.h \ + qgstvideobuffer.h \ + qvideosurfacegstsink.h \ + qgstreamervideoinputdevicecontrol.h \ + gstvideoconnector.h \ + qabstractgstbufferpool.h \ + qgstutils.h + +SOURCES += \ + qgstreamermessage.cpp \ + qgstreamerbushelper.cpp \ + qgstreamervideorendererinterface.cpp \ + qgstreamerserviceplugin.cpp \ + qgstreameraudioinputendpointselector.cpp \ + qgstreamervideorenderer.cpp \ + qgstvideobuffer.cpp \ + qvideosurfacegstsink.cpp \ + qgstreamervideoinputdevicecontrol.cpp \ + gstvideoconnector.c \ + qgstutils.cpp + + +!win32:!contains(QT_CONFIG,embedded):!mac:!symbian:!simulator:!contains(QT_CONFIG, qpa) { + LIBS += -lXv -lX11 -lXext + + HEADERS += \ + qgstreamervideooverlay.h \ + qgstreamervideowindow.h \ + qgstreamervideowidget.h \ + qx11videosurface.h \ + qgstxvimagebuffer.h + + SOURCES += \ + qgstreamervideooverlay.cpp \ + qgstreamervideowindow.cpp \ + qgstreamervideowidget.cpp \ + qx11videosurface.cpp \ + qgstxvimagebuffer.cpp +} +include(mediaplayer/mediaplayer.pri) +include(mediacapture/mediacapture.pri) + +contains(gstreamer-photography_enabled, yes) { + include(camerabin/camerabin.pri) +} diff --git a/src/plugins/gstreamer/gstvideoconnector.c b/src/plugins/gstreamer/gstvideoconnector.c new file mode 100644 index 000000000..55570873e --- /dev/null +++ b/src/plugins/gstreamer/gstvideoconnector.c @@ -0,0 +1,421 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "gstvideoconnector.h" + +/* signals */ +enum +{ + SIGNAL_RESEND_NEW_SEGMENT, + SIGNAL_CONNECTION_FAILED, + LAST_SIGNAL +}; +static guint gst_video_connector_signals[LAST_SIGNAL] = { 0 }; + + +GST_DEBUG_CATEGORY_STATIC (video_connector_debug); +#define GST_CAT_DEFAULT video_connector_debug + +static GstStaticPadTemplate gst_video_connector_sink_factory = +GST_STATIC_PAD_TEMPLATE ("sink", + GST_PAD_SINK, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +static GstStaticPadTemplate gst_video_connector_src_factory = +GST_STATIC_PAD_TEMPLATE ("src", + GST_PAD_SRC, + GST_PAD_ALWAYS, + GST_STATIC_CAPS_ANY); + +#define _do_init(bla) \ + GST_DEBUG_CATEGORY_INIT (video_connector_debug, \ + "video-connector", 0, "An identity like element for reconnecting video stream"); + +GST_BOILERPLATE_FULL (GstVideoConnector, gst_video_connector, GstElement, + GST_TYPE_ELEMENT, _do_init); + +static void gst_video_connector_dispose (GObject * object); +static GstFlowReturn gst_video_connector_chain (GstPad * pad, GstBuffer * buf); +static GstFlowReturn gst_video_connector_buffer_alloc (GstPad * pad, + guint64 offset, guint size, GstCaps * caps, GstBuffer ** buf); +static GstStateChangeReturn gst_video_connector_change_state (GstElement * + element, GstStateChange transition); +static gboolean gst_video_connector_handle_sink_event (GstPad * pad, + GstEvent * event); +static gboolean gst_video_connector_new_buffer_probe(GstObject *pad, GstBuffer *buffer, guint * object); +static void gst_video_connector_resend_new_segment(GstElement * element, gboolean emitFailedSignal); +static gboolean gst_video_connector_setcaps (GstPad *pad, GstCaps *caps); +static GstCaps *gst_video_connector_getcaps (GstPad * pad); +static gboolean gst_video_connector_acceptcaps (GstPad * pad, GstCaps * caps); + +static void +gst_video_connector_base_init (gpointer g_class) +{ + GstElementClass *element_class = GST_ELEMENT_CLASS (g_class); + + gst_element_class_set_details_simple (element_class, "Video Connector", + "Generic", + "An identity like element used for reconnecting video stream", + "Dmytro Poplavskiy "); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_connector_sink_factory)); + gst_element_class_add_pad_template (element_class, + gst_static_pad_template_get (&gst_video_connector_src_factory)); +} + +static void +gst_video_connector_class_init (GstVideoConnectorClass * klass) +{ + GObjectClass *gobject_class = G_OBJECT_CLASS (klass); + GstElementClass *gstelement_class = GST_ELEMENT_CLASS (klass); + + parent_class = g_type_class_peek_parent (klass); + + gobject_class->dispose = gst_video_connector_dispose; + gstelement_class->change_state = gst_video_connector_change_state; + klass->resend_new_segment = gst_video_connector_resend_new_segment; + + gst_video_connector_signals[SIGNAL_RESEND_NEW_SEGMENT] = + g_signal_new ("resend-new-segment", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST | G_SIGNAL_ACTION, + G_STRUCT_OFFSET (GstVideoConnectorClass, resend_new_segment), NULL, NULL, + g_cclosure_marshal_VOID__BOOLEAN, G_TYPE_NONE, 1, G_TYPE_BOOLEAN); + + gst_video_connector_signals[SIGNAL_CONNECTION_FAILED] = + g_signal_new ("connection-failed", G_TYPE_FROM_CLASS (klass), + G_SIGNAL_RUN_LAST, + 0, NULL, NULL, + g_cclosure_marshal_VOID__VOID, G_TYPE_NONE, 0); +} + +static void +gst_video_connector_init (GstVideoConnector *element, + GstVideoConnectorClass *g_class) +{ + element->sinkpad = + gst_pad_new_from_static_template (&gst_video_connector_sink_factory, + "sink"); + gst_pad_set_chain_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_chain)); + gst_pad_set_event_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_handle_sink_event)); + gst_pad_set_bufferalloc_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_buffer_alloc)); + gst_pad_set_setcaps_function(element->sinkpad, + GST_DEBUG_FUNCPTR (gst_video_connector_setcaps)); + gst_pad_set_getcaps_function(element->sinkpad, + GST_DEBUG_FUNCPTR(gst_video_connector_getcaps)); + gst_pad_set_acceptcaps_function(element->sinkpad, + GST_DEBUG_FUNCPTR(gst_video_connector_acceptcaps)); + + gst_element_add_pad (GST_ELEMENT (element), element->sinkpad); + + element->srcpad = + gst_pad_new_from_static_template (&gst_video_connector_src_factory, + "src"); + gst_pad_add_buffer_probe(element->srcpad, + G_CALLBACK(gst_video_connector_new_buffer_probe), element); + gst_element_add_pad (GST_ELEMENT (element), element->srcpad); + + element->relinked = FALSE; + element->failedSignalEmited = FALSE; + gst_segment_init (&element->segment, GST_FORMAT_TIME); + element->latest_buffer = NULL; +} + +static void +gst_video_connector_reset (GstVideoConnector * element) +{ + element->relinked = FALSE; + element->failedSignalEmited = FALSE; + if (element->latest_buffer != NULL) { + gst_buffer_unref (element->latest_buffer); + element->latest_buffer = NULL; + } + gst_segment_init (&element->segment, GST_FORMAT_UNDEFINED); +} + +static void +gst_video_connector_dispose (GObject * object) +{ + GstVideoConnector *element = GST_VIDEO_CONNECTOR (object); + + gst_video_connector_reset (element); + + G_OBJECT_CLASS (parent_class)->dispose (object); +} + +static GstFlowReturn +gst_video_connector_buffer_alloc (GstPad * pad, guint64 offset, guint size, + GstCaps * caps, GstBuffer ** buf) +{ + GstVideoConnector *element; + GstFlowReturn res = GST_FLOW_OK; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + + if (!buf) + return GST_FLOW_ERROR; + *buf = NULL; + + GST_OBJECT_LOCK (element); + gst_object_ref(element->srcpad); + GST_OBJECT_UNLOCK (element); + + res = gst_pad_alloc_buffer(element->srcpad, offset, size, caps, buf); + gst_object_unref (element->srcpad); + + GST_DEBUG_OBJECT (element, "buffer alloc finished: %s", gst_flow_get_name (res)); + + return res; +} + +static gboolean +gst_video_connector_setcaps (GstPad *pad, GstCaps *caps) +{ + GstVideoConnector *element; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + + /* forward-negotiate */ + gboolean res = gst_pad_set_caps(element->srcpad, caps); + + GST_DEBUG_OBJECT(element, "gst_video_connector_setcaps %s %i", gst_caps_to_string(caps), res); + + if (!res) { + //if set_caps failed, emit "connection-failed" signal + //so colorspace transformation elemnt can be inserted + GST_INFO_OBJECT(element, "gst_video_connector_setcaps failed, emit connection-failed signal"); + g_signal_emit(G_OBJECT(element), gst_video_connector_signals[SIGNAL_CONNECTION_FAILED], 0); + + return gst_pad_set_caps(element->srcpad, caps); + } + + return TRUE; +} + +static GstCaps *gst_video_connector_getcaps (GstPad * pad) +{ + GstVideoConnector *element; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + +#if (GST_VERSION_MICRO > 25) + GstCaps *caps = gst_pad_peer_get_caps_reffed(element->srcpad); +#else + GstCaps *caps = gst_pad_peer_get_caps(element->srcpad); +#endif + + if (!caps) + caps = gst_caps_new_any(); + + return caps; +} + +static gboolean gst_video_connector_acceptcaps (GstPad * pad, GstCaps * caps) +{ + GstVideoConnector *element; + element = GST_VIDEO_CONNECTOR (GST_PAD_PARENT (pad)); + + return gst_pad_peer_accept_caps(element->srcpad, caps); +} + +static void +gst_video_connector_resend_new_segment(GstElement * element, gboolean emitFailedSignal) +{ + GST_INFO_OBJECT(element, "New segment requested, failed signal enabled: %i", emitFailedSignal); + GstVideoConnector *connector = GST_VIDEO_CONNECTOR(element); + connector->relinked = TRUE; + if (emitFailedSignal) + connector->failedSignalEmited = FALSE; +} + + +static gboolean gst_video_connector_new_buffer_probe(GstObject *pad, GstBuffer *buffer, guint * object) +{ + GstVideoConnector *element = GST_VIDEO_CONNECTOR (object); + + /* + If relinking is requested, the current buffer should be rejected and + the new segment + previous buffer should be pushed first + */ + + if (element->relinked) + GST_LOG_OBJECT(element, "rejected buffer because of new segment request"); + + return !element->relinked; +} + + +static GstFlowReturn +gst_video_connector_chain (GstPad * pad, GstBuffer * buf) +{ + GstFlowReturn res; + GstVideoConnector *element; + + element = GST_VIDEO_CONNECTOR (gst_pad_get_parent (pad)); + + do { + /* + Resend the segment message and last buffer to preroll the new sink. + Sinks can be changed multiple times while paused, + while loop allows to send the segment message and preroll + all of them with the same buffer. + */ + while (element->relinked) { + element->relinked = FALSE; + + gint64 pos = element->segment.last_stop; + + if (element->latest_buffer && GST_BUFFER_TIMESTAMP_IS_VALID(element->latest_buffer)) { + pos = GST_BUFFER_TIMESTAMP (element->latest_buffer); + } + + //push a new segment and last buffer + GstEvent *ev = gst_event_new_new_segment (TRUE, + element->segment.rate, + element->segment.format, + pos, //start + element->segment.stop, + pos); + + GST_DEBUG_OBJECT (element, "Pushing new segment event"); + if (!gst_pad_push_event (element->srcpad, ev)) { + GST_WARNING_OBJECT (element, + "Newsegment handling failed in %" GST_PTR_FORMAT, + element->srcpad); + } + + if (element->latest_buffer) { + GST_DEBUG_OBJECT (element, "Pushing latest buffer..."); + gst_buffer_ref(element->latest_buffer); + gst_pad_push(element->srcpad, element->latest_buffer); + } + } + + gst_buffer_ref(buf); + + //it's possible video sink is changed during gst_pad_push blocked by + //pad lock, in this case ( element->relinked == TRUE ) + //the buffer should be rejected by the buffer probe and + //the new segment + prev buffer should be sent before + + GST_LOG_OBJECT (element, "Pushing buffer..."); + res = gst_pad_push (element->srcpad, buf); + GST_LOG_OBJECT (element, "Pushed buffer: %s", gst_flow_get_name (res)); + + //if gst_pad_push failed give the service another chance, + //it may still work with the colorspace element added + if (!element->failedSignalEmited && res == GST_FLOW_NOT_NEGOTIATED) { + element->failedSignalEmited = TRUE; + GST_INFO_OBJECT(element, "gst_pad_push failed, emit connection-failed signal"); + g_signal_emit(G_OBJECT(element), gst_video_connector_signals[SIGNAL_CONNECTION_FAILED], 0); + } + + } while (element->relinked); + + + if (element->latest_buffer) { + gst_buffer_unref (element->latest_buffer); + element->latest_buffer = NULL; + } + + //don't save the last video buffer on maemo6 because of buffers shortage + //with omapxvsink +#ifndef Q_WS_MAEMO_6 + element->latest_buffer = gst_buffer_ref(buf); +#endif + + gst_buffer_unref(buf); + gst_object_unref (element); + + return res; +} + +static GstStateChangeReturn +gst_video_connector_change_state (GstElement * element, + GstStateChange transition) +{ + GstVideoConnector *connector; + GstStateChangeReturn result; + + connector = GST_VIDEO_CONNECTOR(element); + result = GST_ELEMENT_CLASS (parent_class)->change_state(element, transition); + + switch (transition) { + case GST_STATE_CHANGE_PAUSED_TO_READY: + gst_video_connector_reset (connector); + break; + case GST_STATE_CHANGE_READY_TO_PAUSED: + connector->relinked = FALSE; + break; + default: + break; + } + + return result; +} + +static gboolean +gst_video_connector_handle_sink_event (GstPad * pad, GstEvent * event) +{ + if (GST_EVENT_TYPE (event) == GST_EVENT_NEWSEGMENT) { + GstVideoConnector *element = GST_VIDEO_CONNECTOR (gst_pad_get_parent (pad)); + + gboolean update; + GstFormat format; + gdouble rate, arate; + gint64 start, stop, time; + + gst_event_parse_new_segment_full (event, &update, &rate, &arate, &format, + &start, &stop, &time); + + GST_LOG_OBJECT (element, + "NEWSEGMENT update %d, rate %lf, applied rate %lf, " + "format %d, " "%" G_GINT64_FORMAT " -- %" G_GINT64_FORMAT ", time %" + G_GINT64_FORMAT, update, rate, arate, format, start, stop, time); + + gst_segment_set_newsegment_full (&element->segment, update, + rate, arate, format, start, stop, time); + + gst_object_unref (element); + } + + return gst_pad_event_default (pad, event); +} diff --git a/src/plugins/gstreamer/gstvideoconnector.h b/src/plugins/gstreamer/gstvideoconnector.h new file mode 100644 index 000000000..00d057e06 --- /dev/null +++ b/src/plugins/gstreamer/gstvideoconnector.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTVIDEOCONNECTOR_H +#define QGSTVIDEOCONNECTOR_H + +#include + +G_BEGIN_DECLS + +#define GST_TYPE_VIDEO_CONNECTOR \ + (gst_video_connector_get_type()) +#define GST_VIDEO_CONNECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_CAST ((obj), GST_TYPE_VIDEO_CONNECTOR, GstVideoConnector)) +#define GST_VIDEO_CONNECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_CAST ((klass), GST_TYPE_VIDEO_CONNECTOR, GstVideoConnectorClass)) +#define GST_IS_VIDEO_CONNECTOR(obj) \ + (G_TYPE_CHECK_INSTANCE_TYPE ((obj), GST_TYPE_VIDEO_CONNECTOR)) +#define GST_IS_VIDEO_CONNECTOR_CLASS(klass) \ + (G_TYPE_CHECK_CLASS_TYPE ((klass), GST_TYPE_VIDEO_CONNECTOR)) + +typedef struct _GstVideoConnector GstVideoConnector; +typedef struct _GstVideoConnectorClass GstVideoConnectorClass; + +struct _GstVideoConnector { + GstElement element; + + GstPad *srcpad; + GstPad *sinkpad; + + gboolean relinked; + gboolean failedSignalEmited; + GstSegment segment; + GstBuffer *latest_buffer; +}; + +struct _GstVideoConnectorClass { + GstElementClass parent_class; + + /* action signal to resend new segment */ + void (*resend_new_segment) (GstElement * element, gboolean emitFailedSignal); +}; + +GType gst_video_connector_get_type (void); + +G_END_DECLS + +#endif + diff --git a/src/plugins/gstreamer/mediacapture/mediacapture.pri b/src/plugins/gstreamer/mediacapture/mediacapture.pri new file mode 100644 index 000000000..b7f7794f9 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/mediacapture.pri @@ -0,0 +1,27 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_GSTREAMER_CAPTURE + +HEADERS += $$PWD/qgstreamercaptureservice.h \ + $$PWD/qgstreamercapturesession.h \ + $$PWD/qgstreameraudioencode.h \ + $$PWD/qgstreamervideoencode.h \ + $$PWD/qgstreamerrecordercontrol.h \ + $$PWD/qgstreamermediacontainercontrol.h \ + $$PWD/qgstreamercameracontrol.h \ + $$PWD/qgstreamerv4l2input.h \ + $$PWD/qgstreamercapturemetadatacontrol.h \ + $$PWD/qgstreamerimagecapturecontrol.h \ + $$PWD/qgstreamerimageencode.h + +SOURCES += $$PWD/qgstreamercaptureservice.cpp \ + $$PWD/qgstreamercapturesession.cpp \ + $$PWD/qgstreameraudioencode.cpp \ + $$PWD/qgstreamervideoencode.cpp \ + $$PWD/qgstreamerrecordercontrol.cpp \ + $$PWD/qgstreamermediacontainercontrol.cpp \ + $$PWD/qgstreamercameracontrol.cpp \ + $$PWD/qgstreamerv4l2input.cpp \ + $$PWD/qgstreamercapturemetadatacontrol.cpp \ + $$PWD/qgstreamerimagecapturecontrol.cpp \ + $$PWD/qgstreamerimageencode.cpp diff --git a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp new file mode 100644 index 000000000..e548393eb --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.cpp @@ -0,0 +1,292 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreameraudioencode.h" +#include "qgstreamercapturesession.h" +#include "qgstreamermediacontainercontrol.h" + +#include + +#include + +QGstreamerAudioEncode::QGstreamerAudioEncode(QObject *parent) + :QAudioEncoderControl(parent) +{ + QList codecCandidates; + +#if defined(Q_WS_MAEMO_5) + codecCandidates << "audio/PCM"; //<< "audio/AMR" << "audio/AMR-WB" << "audio/speex"; +#elif defined(Q_WS_MAEMO_6) + codecCandidates << "audio/AAC" << "audio/mpeg" << "audio/vorbis" << "audio/speex" << "audio/GSM" + << "audio/PCM" << "audio/AMR" << "audio/AMR-WB" << "audio/FLAC"; +#else + codecCandidates << "audio/mpeg" << "audio/vorbis" << "audio/speex" << "audio/GSM" + << "audio/PCM" << "audio/AMR" << "audio/AMR-WB" << "audio/FLAC"; +#endif + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + m_elementNames["audio/AMR"] = "nokiaamrnbenc"; + m_elementNames["audio/AMR-WB"] = "nokiaamrwbenc"; + m_elementNames["audio/AAC"] = "nokiaaacenc"; +#else + m_elementNames["audio/mpeg"] = "lamemp3enc"; + m_elementNames["audio/AMR"] = "amrnbenc"; + m_elementNames["audio/AMR-WB"] = "amrwbenc"; +#endif + + m_elementNames["audio/vorbis"] = "vorbisenc"; + m_elementNames["audio/speex"] = "speexenc"; + m_elementNames["audio/PCM"] = "audioresample"; + m_elementNames["audio/FLAC"] = "flacenc"; + m_elementNames["audio/GSM"] = "gsmenc"; + + m_codecOptions["audio/vorbis"] = QStringList() << "min-bitrate" << "max-bitrate"; + m_codecOptions["audio/mpeg"] = QStringList() << "mode"; + m_codecOptions["audio/speex"] = QStringList() << "mode" << "vbr" << "vad" << "dtx"; + m_codecOptions["audio/GSM"] = QStringList(); + m_codecOptions["audio/PCM"] = QStringList(); + m_codecOptions["audio/AMR"] = QStringList(); + m_codecOptions["audio/AMR-WB"] = QStringList(); + + foreach( const QByteArray& codecName, codecCandidates ) { + QByteArray elementName = m_elementNames[codecName]; + GstElementFactory *factory = gst_element_factory_find(elementName.constData()); + + if (factory) { + m_codecs.append(codecName); + const gchar *descr = gst_element_factory_get_description(factory); + + if (codecName == QByteArray("audio/PCM")) + m_codecDescriptions.insert(codecName, tr("Raw PCM audio")); + else + m_codecDescriptions.insert(codecName, QString::fromUtf8(descr)); + + m_streamTypes.insert(codecName, + QGstreamerMediaContainerControl::supportedStreamTypes(factory, GST_PAD_SRC)); + + gst_object_unref(GST_OBJECT(factory)); + } + } + + //if (!m_codecs.isEmpty()) + // m_audioSettings.setCodec(m_codecs[0]); +} + +QGstreamerAudioEncode::~QGstreamerAudioEncode() +{ +} + +QStringList QGstreamerAudioEncode::supportedAudioCodecs() const +{ + return m_codecs; +} + +QString QGstreamerAudioEncode::codecDescription(const QString &codecName) const +{ + return m_codecDescriptions.value(codecName); +} + +QStringList QGstreamerAudioEncode::supportedEncodingOptions(const QString &codec) const +{ + return m_codecOptions.value(codec); +} + +QVariant QGstreamerAudioEncode::encodingOption( + const QString &codec, const QString &name) const +{ + return m_options[codec].value(name); +} + +void QGstreamerAudioEncode::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + m_options[codec][name] = value; +} + +QList QGstreamerAudioEncode::supportedSampleRates(const QAudioEncoderSettings &, bool *) const +{ + //TODO check element caps to find actual values + + return QList(); +} + +QAudioEncoderSettings QGstreamerAudioEncode::audioSettings() const +{ + return m_audioSettings; +} + +void QGstreamerAudioEncode::setAudioSettings(const QAudioEncoderSettings &settings) +{ + m_audioSettings = settings; +} + + +GstElement *QGstreamerAudioEncode::createEncoder() +{ + QString codec = m_audioSettings.codec(); + GstElement *encoderElement = gst_element_factory_make(m_elementNames.value(codec).constData(), NULL); + if (!encoderElement) + return 0; + + GstBin * encoderBin = GST_BIN(gst_bin_new("audio-encoder-bin")); + + GstElement *capsFilter = gst_element_factory_make("capsfilter", NULL); + + gst_bin_add(encoderBin, capsFilter); + gst_bin_add(encoderBin, encoderElement); + gst_element_link(capsFilter, encoderElement); + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(capsFilter, "sink"); + gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + pad = gst_element_get_static_pad(encoderElement, "src"); + gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad)); + gst_object_unref(GST_OBJECT(pad)); + + if (m_audioSettings.sampleRate() > 0 || m_audioSettings.channelCount() > 0) { + GstCaps *caps = gst_caps_new_empty(); + GstStructure *structure = gst_structure_new("audio/x-raw-int", NULL); + + if (m_audioSettings.sampleRate() > 0) + gst_structure_set(structure, "rate", G_TYPE_INT, m_audioSettings.sampleRate(), NULL ); + + if (m_audioSettings.channelCount() > 0) + gst_structure_set(structure, "channels", G_TYPE_INT, m_audioSettings.channelCount(), NULL ); + + gst_caps_append_structure(caps,structure); + + //qDebug() << "set caps filter:" << gst_caps_to_string(caps); + + g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); + } + + if (encoderElement) { + if (m_audioSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) { + QtMultimediaKit::EncodingQuality qualityValue = m_audioSettings.quality(); + + if (codec == QLatin1String("audio/vorbis")) { + double qualityTable[] = { + 0.1, //VeryLow + 0.3, //Low + 0.5, //Normal + 0.7, //High + 1.0 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL); + } else if (codec == QLatin1String("audio/mpeg")) { + g_object_set(G_OBJECT(encoderElement), "target", 0, NULL); //constant quality mode + qreal quality[] = { + 1, //VeryLow + 3, //Low + 5, //Normal + 7, //High + 9 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quality", quality[qualityValue], NULL); + } else if (codec == QLatin1String("audio/speex")) { + //0-10 range with default 8 + double qualityTable[] = { + 2, //VeryLow + 5, //Low + 8, //Normal + 9, //High + 10 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quality", qualityTable[qualityValue], NULL); + } else if (codec.startsWith("audio/AMR")) { + int band[] = { + 0, //VeryLow + 2, //Low + 4, //Normal + 6, //High + 7 //VeryHigh + }; + + g_object_set(G_OBJECT(encoderElement), "band-mode", band[qualityValue], NULL); + } + } else { + int bitrate = m_audioSettings.bitRate(); + if (bitrate > 0) { + if (codec == QLatin1String("audio/mpeg")) { + g_object_set(G_OBJECT(encoderElement), "target", 1, NULL); //constant bitrate mode + } + g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL); + } + } + + QMap options = m_options.value(codec); + QMapIterator it(options); + while (it.hasNext()) { + it.next(); + QString option = it.key(); + QVariant value = it.value(); + + switch (value.type()) { + case QVariant::Int: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toInt(), NULL); + break; + case QVariant::Bool: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toBool(), NULL); + break; + case QVariant::Double: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toDouble(), NULL); + break; + case QVariant::String: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toString().toUtf8().constData(), NULL); + break; + default: + qWarning() << "unsupported option type:" << option << value; + break; + } + + } + } + + return GST_ELEMENT(encoderBin); +} + + +QSet QGstreamerAudioEncode::supportedStreamTypes(const QString &codecName) const +{ + return m_streamTypes.value(codecName); +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h new file mode 100644 index 000000000..e2c48234d --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreameraudioencode.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERAUDIOENCODE_H +#define QGSTREAMERAUDIOENCODE_H + +#include +class QGstreamerCaptureSession; + +#include +#include +#include + +#include + +#include + +QT_USE_NAMESPACE + +class QGstreamerAudioEncode : public QAudioEncoderControl +{ + Q_OBJECT +public: + QGstreamerAudioEncode(QObject *parent); + virtual ~QGstreamerAudioEncode(); + + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + + QList supportedSampleRates(const QAudioEncoderSettings &settings = QAudioEncoderSettings(), + bool *isContinuous = 0) const; + QList supportedChannelCounts(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const; + QList supportedSampleSizes(const QAudioEncoderSettings &settings = QAudioEncoderSettings()) const; + + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings&); + + GstElement *createEncoder(); + + QSet supportedStreamTypes(const QString &codecName) const; + +private: + QStringList m_codecs; + QMap m_elementNames; + QMap m_codecDescriptions; + QMap m_codecOptions; + + QMap > m_options; + + QMap > m_streamTypes; + + QAudioEncoderSettings m_audioSettings; +}; + +#endif diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp new file mode 100644 index 000000000..927a827dd --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamercameracontrol.h" +#include "qgstreamerimageencode.h" + +#include +#include + + +QGstreamerCameraControl::QGstreamerCameraControl(QGstreamerCaptureSession *session) + :QCameraControl(session), + m_captureMode(QCamera::CaptureStillImage), + m_session(session), + m_state(QCamera::UnloadedState), + m_status(QCamera::UnloadedStatus), + m_reloadPending(false) + +{ + connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), + this, SLOT(updateStatus())); + + connect(m_session->imageEncodeControl(), SIGNAL(settingsChanged()), + SLOT(reloadLater())); + connect(m_session, SIGNAL(viewfinderChanged()), + SLOT(reloadLater())); + connect(m_session, SIGNAL(readyChanged(bool)), + SLOT(reloadLater())); +} + +QGstreamerCameraControl::~QGstreamerCameraControl() +{ +} + +void QGstreamerCameraControl::setCaptureMode(QCamera::CaptureMode mode) +{ + if (m_captureMode == mode) + return; + + switch (mode) { + case QCamera::CaptureStillImage: + m_session->setCaptureMode(QGstreamerCaptureSession::Image); + break; + case QCamera::CaptureVideo: + m_session->setCaptureMode(QGstreamerCaptureSession::AudioAndVideo); + break; + } + + emit captureModeChanged(mode); + updateStatus(); + reloadLater(); +} + +void QGstreamerCameraControl::setState(QCamera::State state) +{ + if (m_state == state) + return; + + m_state = state; + switch (state) { + case QCamera::UnloadedState: + case QCamera::LoadedState: + m_session->setState(QGstreamerCaptureSession::StoppedState); + break; + case QCamera::ActiveState: + //postpone changing to Active if the session is nor ready yet + if (m_session->isReady()) { + m_session->setState(QGstreamerCaptureSession::PreviewState); + } else { +#ifdef CAMEABIN_DEBUG + qDebug() << "Camera session is not ready yet, postpone activating"; +#endif + } + break; + default: + emit error(QCamera::NotSupportedFeatureError, tr("State not supported.")); + } + + updateStatus(); + emit stateChanged(m_state); +} + +QCamera::State QGstreamerCameraControl::state() const +{ + return m_state; +} + +void QGstreamerCameraControl::updateStatus() +{ + QCamera::Status oldStatus = m_status; + + switch (m_state) { + case QCamera::UnloadedState: + m_status = QCamera::UnloadedStatus; + break; + case QCamera::LoadedState: + m_status = QCamera::LoadedStatus; + break; + case QCamera::ActiveState: + if (m_session->state() == QGstreamerCaptureSession::StoppedState) + m_status = QCamera::StartingStatus; + else + m_status = QCamera::ActiveStatus; + break; + } + + if (oldStatus != m_status) { + //qDebug() << "Status changed:" << m_status; + emit statusChanged(m_status); + } +} + +void QGstreamerCameraControl::reloadLater() +{ + //qDebug() << "reload pipeline requested"; + if (!m_reloadPending && m_state == QCamera::ActiveState) { + m_reloadPending = true; + m_session->setState(QGstreamerCaptureSession::StoppedState); + QMetaObject::invokeMethod(this, "reloadPipeline", Qt::QueuedConnection); + } +} + +void QGstreamerCameraControl::reloadPipeline() +{ + //qDebug() << "reload pipeline"; + if (m_reloadPending) { + m_reloadPending = false; + if (m_state == QCamera::ActiveState && m_session->isReady()) { + m_session->setState(QGstreamerCaptureSession::PreviewState); + } + } +} + +bool QGstreamerCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const +{ + Q_UNUSED(status); + + switch (changeType) { + case QCameraControl::CaptureMode: + case QCameraControl::ImageEncodingSettings: + case QCameraControl::VideoEncodingSettings: + case QCameraControl::Viewfinder: + return true; + default: + return false; + } +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h new file mode 100644 index 000000000..c07ffcd19 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercameracontrol.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERCAMERACONTROL_H +#define QGSTREAMERCAMERACONTROL_H + +#include +#include +#include "qgstreamercapturesession.h" + +QT_USE_NAMESPACE +QT_USE_NAMESPACE + +class QGstreamerCameraControl : public QCameraControl +{ + Q_OBJECT +public: + QGstreamerCameraControl( QGstreamerCaptureSession *session ); + virtual ~QGstreamerCameraControl(); + + bool isValid() const { return true; } + + QCamera::State state() const; + void setState(QCamera::State state); + + QCamera::Status status() const { return m_status; } + + QCamera::CaptureMode captureMode() const { return m_captureMode; } + void setCaptureMode(QCamera::CaptureMode mode); + + bool isCaptureModeSupported(QCamera::CaptureMode mode) const + { + return mode == QCamera::CaptureStillImage || mode == QCamera::CaptureVideo; + } + + QCamera::LockTypes supportedLocks() const + { + return QCamera::NoLock; + } + + bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const; + +public slots: + void reloadLater(); + +private slots: + void updateStatus(); + void reloadPipeline(); + + +private: + QCamera::CaptureMode m_captureMode; + QGstreamerCaptureSession *m_session; + QCamera::State m_state; + QCamera::Status m_status; + bool m_reloadPending; +}; + +#endif // QGSTREAMERCAMERACONTROL_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp new file mode 100644 index 000000000..b0e589dc8 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.cpp @@ -0,0 +1,198 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamercapturemetadatacontrol.h" + +#include +#include + +struct QGstreamerMetaDataKeyLookup +{ + QtMultimediaKit::MetaData key; + const char *token; +}; + +static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = +{ + { QtMultimediaKit::Title, GST_TAG_TITLE }, + //{ QtMultimediaKit::SubTitle, 0 }, + //{ QtMultimediaKit::Author, 0 }, + { QtMultimediaKit::Comment, GST_TAG_COMMENT }, + { QtMultimediaKit::Description, GST_TAG_DESCRIPTION }, + //{ QtMultimediaKit::Category, 0 }, + { QtMultimediaKit::Genre, GST_TAG_GENRE }, + //{ QtMultimediaKit::Year, 0 }, + //{ QtMultimediaKit::UserRating, 0 }, + + { QtMultimediaKit::Language, GST_TAG_LANGUAGE_CODE }, + + { QtMultimediaKit::Publisher, GST_TAG_ORGANIZATION }, + { QtMultimediaKit::Copyright, GST_TAG_COPYRIGHT }, + //{ QtMultimediaKit::ParentalRating, 0 }, + //{ QtMultimediaKit::RatingOrganisation, 0 }, + + // Media + //{ QtMultimediaKit::Size, 0 }, + //{ QtMultimediaKit::MediaType, 0 }, + { QtMultimediaKit::Duration, GST_TAG_DURATION }, + + // Audio + { QtMultimediaKit::AudioBitRate, GST_TAG_BITRATE }, + { QtMultimediaKit::AudioCodec, GST_TAG_AUDIO_CODEC }, + //{ QtMultimediaKit::ChannelCount, 0 }, + //{ QtMultimediaKit::SampleRate, 0 }, + + // Music + { QtMultimediaKit::AlbumTitle, GST_TAG_ALBUM }, + { QtMultimediaKit::AlbumArtist, GST_TAG_ARTIST}, + { QtMultimediaKit::ContributingArtist, GST_TAG_PERFORMER }, +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + { QtMultimediaKit::Composer, GST_TAG_COMPOSER }, +#endif + //{ QtMultimediaKit::Conductor, 0 }, + //{ QtMultimediaKit::Lyrics, 0 }, + //{ QtMultimediaKit::Mood, 0 }, + { QtMultimediaKit::TrackNumber, GST_TAG_TRACK_NUMBER }, + + //{ QtMultimediaKit::CoverArtUrlSmall, 0 }, + //{ QtMultimediaKit::CoverArtUrlLarge, 0 }, + + // Image/Video + //{ QtMultimediaKit::Resolution, 0 }, + //{ QtMultimediaKit::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimediaKit::VideoFrameRate, 0 }, + //{ QtMultimediaKit::VideoBitRate, 0 }, + { QtMultimediaKit::VideoCodec, GST_TAG_VIDEO_CODEC }, + + //{ QtMultimediaKit::PosterUrl, 0 }, + + // Movie + //{ QtMultimediaKit::ChapterNumber, 0 }, + //{ QtMultimediaKit::Director, 0 }, + { QtMultimediaKit::LeadPerformer, GST_TAG_PERFORMER }, + //{ QtMultimediaKit::Writer, 0 }, + + // Photos + //{ QtMultimediaKit::CameraManufacturer, 0 }, + //{ QtMultimediaKit::CameraModel, 0 }, + //{ QtMultimediaKit::Event, 0 }, + //{ QtMultimediaKit::Subject, 0 } +}; + +QGstreamerCaptureMetaDataControl::QGstreamerCaptureMetaDataControl(QObject *parent) + :QMetaDataWriterControl(parent) +{ +} + +QVariant QGstreamerCaptureMetaDataControl::metaData(QtMultimediaKit::MetaData key) const +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + const char *name = qt_gstreamerMetaDataKeys[i].token; + + return m_values.value(QByteArray::fromRawData(name, qstrlen(name))); + } + } + return QVariant(); +} + +void QGstreamerCaptureMetaDataControl::setMetaData(QtMultimediaKit::MetaData key, const QVariant &value) +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + const char *name = qt_gstreamerMetaDataKeys[i].token; + + m_values.insert(QByteArray::fromRawData(name, qstrlen(name)), value); + + emit QMetaDataWriterControl::metaDataChanged(); + emit metaDataChanged(m_values); + + return; + } + } +} + +QList QGstreamerCaptureMetaDataControl::availableMetaData() const +{ + static QMap keysMap; + if (keysMap.isEmpty()) { + const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + keysMap[QByteArray(qt_gstreamerMetaDataKeys[i].token)] = qt_gstreamerMetaDataKeys[i].key; + } + } + + QList res; + foreach (const QByteArray &key, m_values.keys()) { + QtMultimediaKit::MetaData tag = keysMap.value(key, QtMultimediaKit::MetaData(-1)); + if (tag != -1) + res.append(tag); + } + + return res; +} + +QVariant QGstreamerCaptureMetaDataControl::extendedMetaData(QString const &name) const +{ + return m_values.value(name.toLatin1()); +} + +void QGstreamerCaptureMetaDataControl::setExtendedMetaData(QString const &name, QVariant const &value) +{ + m_values.insert(name.toLatin1(), value); + emit QMetaDataWriterControl::metaDataChanged(); + emit metaDataChanged(m_values); +} + +QStringList QGstreamerCaptureMetaDataControl::availableExtendedMetaData() const +{ + QStringList res; + foreach (const QByteArray &key, m_values.keys()) + res.append(QString(key)); + + return res; +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h new file mode 100644 index 000000000..e859da486 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturemetadatacontrol.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERCAPTUREMETADATACONTROL_H +#define QGSTREAMERCAPTUREMETADATACONTROL_H + +#include + +QT_USE_NAMESPACE + +class QGstreamerCaptureMetaDataControl : public QMetaDataWriterControl +{ + Q_OBJECT +public: + QGstreamerCaptureMetaDataControl(QObject *parent); + virtual ~QGstreamerCaptureMetaDataControl() {}; + + + bool isMetaDataAvailable() const { return true; } + bool isWritable() const { return true; } + + QVariant metaData(QtMultimediaKit::MetaData key) const; + void setMetaData(QtMultimediaKit::MetaData key, const QVariant &value); + QList availableMetaData() const; + + QVariant extendedMetaData(QString const &name) const; + void setExtendedMetaData(QString const &name, QVariant const &value); + QStringList availableExtendedMetaData() const; + +Q_SIGNALS: + void metaDataChanged(const QMap&); + +private: + QMap m_values; +}; + +#endif // QGSTREAMERCAPTUREMETADATACONTROL_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp new file mode 100644 index 000000000..97310d199 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.cpp @@ -0,0 +1,185 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamercaptureservice.h" +#include "qgstreamercapturesession.h" +#include "qgstreamerrecordercontrol.h" +#include "qgstreamermediacontainercontrol.h" +#include "qgstreameraudioencode.h" +#include "qgstreamervideoencode.h" +#include "qgstreamerimageencode.h" +#include "qgstreamerbushelper.h" +#include "qgstreamercameracontrol.h" +#include "qgstreamerv4l2input.h" +#include "qgstreamercapturemetadatacontrol.h" + +#include "qgstreameraudioinputendpointselector.h" +#include "qgstreamervideoinputdevicecontrol.h" +#include "qgstreamerimagecapturecontrol.h" + +#include "qgstreamervideooverlay.h" +#include "qgstreamervideorenderer.h" + +#include "qgstreamervideowidget.h" + +#include + + +QGstreamerCaptureService::QGstreamerCaptureService(const QString &service, QObject *parent): + QMediaService(parent) +{ + m_captureSession = 0; + m_cameraControl = 0; + m_metaDataControl = 0; + + m_videoInput = 0; + m_audioInputEndpointSelector = 0; + m_videoInputDevice = 0; + + m_videoOutput = 0; + m_videoRenderer = 0; + m_videoWindow = 0; + m_videoWidgetControl = 0; + m_imageCaptureControl = 0; + + if (service == Q_MEDIASERVICE_AUDIOSOURCE) { + m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::Audio, this); + } + + if (service == Q_MEDIASERVICE_CAMERA) { + m_captureSession = new QGstreamerCaptureSession(QGstreamerCaptureSession::AudioAndVideo, this); + m_cameraControl = new QGstreamerCameraControl(m_captureSession); + m_videoInput = new QGstreamerV4L2Input(this); + m_captureSession->setVideoInput(m_videoInput); + m_videoInputDevice = new QGstreamerVideoInputDeviceControl(this); + + connect(m_videoInputDevice, SIGNAL(selectedDeviceChanged(QString)), + m_videoInput, SLOT(setDevice(QString))); + + if (m_videoInputDevice->deviceCount()) + m_videoInput->setDevice(m_videoInputDevice->deviceName(m_videoInputDevice->selectedDevice())); + + m_videoRenderer = new QGstreamerVideoRenderer(this); + +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) + m_videoWindow = new QGstreamerVideoOverlay(this); + m_videoWidgetControl = new QGstreamerVideoWidgetControl(this); +#endif + m_imageCaptureControl = new QGstreamerImageCaptureControl(m_captureSession); + } + + m_audioInputEndpointSelector = new QGstreamerAudioInputEndpointSelector(this); + connect(m_audioInputEndpointSelector, SIGNAL(activeEndpointChanged(QString)), m_captureSession, SLOT(setCaptureDevice(QString))); + + if (m_captureSession && m_audioInputEndpointSelector->availableEndpoints().size() > 0) + m_captureSession->setCaptureDevice(m_audioInputEndpointSelector->defaultEndpoint()); + + m_metaDataControl = new QGstreamerCaptureMetaDataControl(this); + connect(m_metaDataControl, SIGNAL(metaDataChanged(QMap)), + m_captureSession, SLOT(setMetaData(QMap))); +} + +QGstreamerCaptureService::~QGstreamerCaptureService() +{ +} + +QMediaControl *QGstreamerCaptureService::requestControl(const char *name) +{ + if (!m_captureSession) + return 0; + + if (qstrcmp(name,QAudioEndpointSelector_iid) == 0) + return m_audioInputEndpointSelector; + + if (qstrcmp(name,QVideoDeviceControl_iid) == 0) + return m_videoInputDevice; + + if (qstrcmp(name,QMediaRecorderControl_iid) == 0) + return m_captureSession->recorderControl(); + + if (qstrcmp(name,QAudioEncoderControl_iid) == 0) + return m_captureSession->audioEncodeControl(); + + if (qstrcmp(name,QVideoEncoderControl_iid) == 0) + return m_captureSession->videoEncodeControl(); + + if (qstrcmp(name,QImageEncoderControl_iid) == 0) + return m_captureSession->imageEncodeControl(); + + + if (qstrcmp(name,QMediaContainerControl_iid) == 0) + return m_captureSession->mediaContainerControl(); + + if (qstrcmp(name,QCameraControl_iid) == 0) + return m_cameraControl; + + if (qstrcmp(name,QMetaDataWriterControl_iid) == 0) + return m_metaDataControl; + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return m_imageCaptureControl; + + if (!m_videoOutput) { + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + m_videoOutput = m_videoRenderer; + m_captureSession->setVideoPreview(m_videoRenderer); + } else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + m_videoOutput = m_videoWindow; + m_captureSession->setVideoPreview(m_videoWindow); + } else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + m_captureSession->setVideoPreview(m_videoWidgetControl); + m_videoOutput = m_videoWidgetControl; + } + + if (m_videoOutput) + return m_videoOutput; + } + + return 0; +} + +void QGstreamerCaptureService::releaseControl(QMediaControl *control) +{ + if (control && control == m_videoOutput) { + m_videoOutput = 0; + m_captureSession->setVideoPreview(0); + } +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h new file mode 100644 index 000000000..09b275c35 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercaptureservice.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERCAPTURESERVICE_H +#define QGSTREAMERCAPTURESERVICE_H + +#include +#include + +#include +QT_BEGIN_NAMESPACE +class QAudioEndpointSelector; +class QVideoDeviceControl; +QT_END_NAMESPACE + +class QGstreamerCaptureSession; +class QGstreamerCameraControl; +class QGstreamerMessage; +class QGstreamerBusHelper; +class QGstreamerVideoRenderer; +class QGstreamerVideoOverlay; +class QGstreamerVideoWidgetControl; +class QGstreamerElementFactory; +class QGstreamerCaptureMetaDataControl; +class QGstreamerImageCaptureControl; +class QGstreamerV4L2Input; + +class QGstreamerCaptureService : public QMediaService +{ + Q_OBJECT + +public: + QGstreamerCaptureService(const QString &service, QObject *parent = 0); + virtual ~QGstreamerCaptureService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *); + +private: + void setAudioPreview(GstElement*); + + QGstreamerCaptureSession *m_captureSession; + QGstreamerCameraControl *m_cameraControl; + QGstreamerV4L2Input *m_videoInput; + QGstreamerCaptureMetaDataControl *m_metaDataControl; + + QAudioEndpointSelector *m_audioInputEndpointSelector; + QVideoDeviceControl *m_videoInputDevice; + + QMediaControl *m_videoOutput; + + QGstreamerVideoRenderer *m_videoRenderer; + QMediaControl *m_videoWindow; + QMediaControl *m_videoWidgetControl; + QGstreamerImageCaptureControl *m_imageCaptureControl; +}; + +#endif // QGSTREAMERCAPTURESERVICE_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp new file mode 100644 index 000000000..f8e73c7af --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.cpp @@ -0,0 +1,1051 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamercapturesession.h" +#include "qgstreamerrecordercontrol.h" +#include "qgstreamermediacontainercontrol.h" +#include "qgstreamervideorendererinterface.h" +#include "qgstreameraudioencode.h" +#include "qgstreamervideoencode.h" +#include "qgstreamerimageencode.h" +#include "qgstreamerbushelper.h" +#include + +#include +#include + +#include +#include +#include +#include +#include +#include + +#include + +#define gstRef(element) { gst_object_ref(GST_OBJECT(element)); gst_object_sink(GST_OBJECT(element)); } +#define gstUnref(element) { if (element) { gst_object_unref(GST_OBJECT(element)); element = 0; } } + +QGstreamerCaptureSession::QGstreamerCaptureSession(QGstreamerCaptureSession::CaptureMode captureMode, QObject *parent) + :QObject(parent), + m_state(StoppedState), + m_pendingState(StoppedState), + m_waitingForEos(false), + m_pipelineMode(EmptyPipeline), + m_captureMode(captureMode), + m_audioInputFactory(0), + m_audioPreviewFactory(0), + m_videoInputFactory(0), + m_viewfinder(0), + m_viewfinderInterface(0), + m_audioSrc(0), + m_audioTee(0), + m_audioPreviewQueue(0), + m_audioPreview(0), + m_audioVolume(0), + m_muted(false), + m_videoSrc(0), + m_videoTee(0), + m_videoPreviewQueue(0), + m_videoPreview(0), + m_imageCaptureBin(0), + m_encodeBin(0), + m_passImage(false), + m_passPrerollImage(false) +{ + m_pipeline = gst_pipeline_new("media-capture-pipeline"); + gstRef(m_pipeline); + + m_bus = gst_element_get_bus(m_pipeline); + m_busHelper = new QGstreamerBusHelper(m_bus, this); + m_busHelper->installSyncEventFilter(this); + connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage))); + m_audioEncodeControl = new QGstreamerAudioEncode(this); + m_videoEncodeControl = new QGstreamerVideoEncode(this); + m_imageEncodeControl = new QGstreamerImageEncode(this); + m_recorderControl = new QGstreamerRecorderControl(this); + m_mediaContainerControl = new QGstreamerMediaContainerControl(this); + + setState(StoppedState); +} + +QGstreamerCaptureSession::~QGstreamerCaptureSession() +{ + setState(StoppedState); + gst_element_set_state(m_pipeline, GST_STATE_NULL); + gst_object_unref(GST_OBJECT(m_pipeline)); +} + +void QGstreamerCaptureSession::setCaptureMode(CaptureMode mode) +{ + m_captureMode = mode; +} + +GstElement *QGstreamerCaptureSession::buildEncodeBin() +{ + GstElement *encodeBin = gst_bin_new("encode-bin"); + + GstElement *muxer = gst_element_factory_make( m_mediaContainerControl->formatElementName().constData(), "muxer"); + if (!muxer) { + qWarning() << "Could not create a media muxer element:" << m_mediaContainerControl->formatElementName(); + gst_object_unref(encodeBin); + return 0; + } + + GstElement *fileSink = gst_element_factory_make("filesink", "filesink"); + g_object_set(G_OBJECT(fileSink), "location", m_sink.toString().toLocal8Bit().constData(), NULL); + gst_bin_add_many(GST_BIN(encodeBin), muxer, fileSink, NULL); + + if (!gst_element_link(muxer, fileSink)) { + gst_object_unref(encodeBin); + return 0; + } + + if (m_captureMode & Audio) { + GstElement *audioConvert = gst_element_factory_make("audioconvert", "audioconvert"); + GstElement *audioQueue = gst_element_factory_make("queue", "audio-encode-queue"); + m_audioVolume = gst_element_factory_make("volume", "volume"); + gst_bin_add_many(GST_BIN(encodeBin), audioConvert, audioQueue, m_audioVolume, NULL); + + GstElement *audioEncoder = m_audioEncodeControl->createEncoder(); + if (!audioEncoder) { + gst_object_unref(encodeBin); + qWarning() << "Could not create an audio encoder element:" << m_audioEncodeControl->audioSettings().codec(); + return 0; + } + + gst_bin_add(GST_BIN(encodeBin), audioEncoder); + + if (!gst_element_link_many(audioConvert, audioQueue, m_audioVolume, audioEncoder, muxer, NULL)) { + gst_object_unref(encodeBin); + return 0; + } + + g_object_set(G_OBJECT(m_audioVolume), "volume", (m_muted ? 0.0 : 1.0), NULL); + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(audioConvert, "sink"); + gst_element_add_pad(GST_ELEMENT(encodeBin), gst_ghost_pad_new("audiosink", pad)); + gst_object_unref(GST_OBJECT(pad)); + } + + if (m_captureMode & Video) { + GstElement *videoQueue = gst_element_factory_make("queue", "video-encode-queue"); + GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-encoder"); + GstElement *videoscale = gst_element_factory_make("videoscale","videoscale-encoder"); + gst_bin_add_many(GST_BIN(encodeBin), videoQueue, colorspace, videoscale, NULL); + + GstElement *videoEncoder = m_videoEncodeControl->createEncoder(); + if (!videoEncoder) { + gst_object_unref(encodeBin); + qWarning() << "Could not create a video encoder element:" << m_videoEncodeControl->videoSettings().codec(); + return 0; + } + + gst_bin_add(GST_BIN(encodeBin), videoEncoder); + + if (!gst_element_link_many(videoQueue, colorspace, videoscale, videoEncoder, muxer, NULL)) { + gst_object_unref(encodeBin); + return 0; + } + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(videoQueue, "sink"); + gst_element_add_pad(GST_ELEMENT(encodeBin), gst_ghost_pad_new("videosink", pad)); + gst_object_unref(GST_OBJECT(pad)); + } + + return encodeBin; +} + +GstElement *QGstreamerCaptureSession::buildAudioSrc() +{ + GstElement *audioSrc = 0; + if (m_audioInputFactory) + audioSrc = m_audioInputFactory->buildElement(); + else { + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + audioSrc = gst_element_factory_make("pulsesrc", "audio_src"); +#elif defined(QT_QWS_N810) + audioSrc = gst_element_factory_make("dsppcmsrc", "audio_src"); +#else + QString elementName = "alsasrc"; + QString device; + + if (m_captureDevice.startsWith("alsa:")) { + device = m_captureDevice.mid(QString("alsa:").length()); + } else if (m_captureDevice.startsWith("oss:")) { + elementName = "osssrc"; + device = m_captureDevice.mid(QString("oss:").length()); + } else if (m_captureDevice.startsWith("pulseaudio:")) { + elementName = "pulsesrc"; + } else { + elementName = "autoaudiosrc"; + } + + audioSrc = gst_element_factory_make(elementName.toAscii().constData(), "audio_src"); + if (audioSrc && !device.isEmpty()) + g_object_set(G_OBJECT(audioSrc), "device", device.toLocal8Bit().constData(), NULL); +#endif + } + + if (!audioSrc) { + emit error(int(QMediaRecorder::ResourceError), tr("Could not create an audio source element")); + audioSrc = gst_element_factory_make("fakesrc", NULL); + } + + return audioSrc; +} + +GstElement *QGstreamerCaptureSession::buildAudioPreview() +{ + GstElement *previewElement = 0; + + if (m_audioPreviewFactory) { + previewElement = m_audioPreviewFactory->buildElement(); + } else { + + +#if 1 + previewElement = gst_element_factory_make("fakesink", "audio-preview"); +#else + GstElement *bin = gst_bin_new("audio-preview-bin"); + GstElement *visual = gst_element_factory_make("libvisual_lv_scope", "audio-preview"); + GstElement *sink = gst_element_factory_make("ximagesink", NULL); + gst_bin_add_many(GST_BIN(bin), visual, sink, NULL); + gst_element_link_many(visual,sink, NULL); + + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(visual, "sink"); + Q_ASSERT(pad); + gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("audiosink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + previewElement = bin; +#endif + } + + return previewElement; +} + +GstElement *QGstreamerCaptureSession::buildVideoSrc() +{ + GstElement *videoSrc = 0; + if (m_videoInputFactory) { + videoSrc = m_videoInputFactory->buildElement(); + } else { + videoSrc = gst_element_factory_make("videotestsrc", "video_test_src"); + //videoSrc = gst_element_factory_make("v4l2src", "video_test_src"); + } + + return videoSrc; +} + +GstElement *QGstreamerCaptureSession::buildVideoPreview() +{ + GstElement *previewElement = 0; + + if (m_viewfinderInterface) { + GstElement *bin = gst_bin_new("video-preview-bin"); + GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview"); + GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video-preview"); + GstElement *preview = m_viewfinderInterface->videoSink(); + + gst_bin_add_many(GST_BIN(bin), colorspace, capsFilter, preview, NULL); + gst_element_link(colorspace,capsFilter); + gst_element_link(capsFilter,preview); + + QSize resolution; + qreal frameRate = 0; + + if (m_captureMode & Video) { + QVideoEncoderSettings videoSettings = m_videoEncodeControl->videoSettings(); + resolution = videoSettings.resolution(); + frameRate = videoSettings.frameRate(); + } else if (m_captureMode & Image) { + resolution = m_imageEncodeControl->imageSettings().resolution(); + } + + if (!resolution.isEmpty() || frameRate > 0.001) { + GstCaps *caps = gst_caps_new_empty(); + QStringList structureTypes; + structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb"; + + foreach(const QString &structureType, structureTypes) { + GstStructure *structure = gst_structure_new(structureType.toAscii().constData(), NULL); + + if (!resolution.isEmpty()) { + gst_structure_set(structure, "width", G_TYPE_INT, resolution.width(), NULL); + gst_structure_set(structure, "height", G_TYPE_INT, resolution.height(), NULL); + } + + if (frameRate > 0.001) { + QPair rate = m_videoEncodeControl->rateAsRational(); + + //qDebug() << "frame rate:" << num << denum; + + gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); + } + + gst_caps_append_structure(caps,structure); + } + + //qDebug() << "set video preview caps filter:" << gst_caps_to_string(caps); + + g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); + + } + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(colorspace, "sink"); + Q_ASSERT(pad); + gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("videosink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + previewElement = bin; + } else { +#if 1 + previewElement = gst_element_factory_make("fakesink", "video-preview"); +#else + GstElement *bin = gst_bin_new("video-preview-bin"); + GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-preview"); + GstElement *preview = gst_element_factory_make("ximagesink", "video-preview"); + gst_bin_add_many(GST_BIN(bin), colorspace, preview, NULL); + gst_element_link(colorspace,preview); + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(colorspace, "sink"); + Q_ASSERT(pad); + gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("videosink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + previewElement = bin; +#endif + } + + return previewElement; +} + + +static gboolean passImageFilter(GstElement *element, + GstBuffer *buffer, + void *appdata) +{ + Q_UNUSED(element); + Q_UNUSED(buffer); + + QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata; + if (session->m_passImage || session->m_passPrerollImage) { + session->m_passImage = false; + + if (session->m_passPrerollImage) { + session->m_passPrerollImage = false; + return TRUE; + } + session->m_passPrerollImage = false; + + QImage img; + + GstCaps *caps = gst_buffer_get_caps(buffer); + if (caps) { + GstStructure *structure = gst_caps_get_structure (caps, 0); + gint width = 0; + gint height = 0; + + if (structure && + gst_structure_get_int(structure, "width", &width) && + gst_structure_get_int(structure, "height", &height) && + width > 0 && height > 0) { + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { + guint32 fourcc = 0; + gst_structure_get_fourcc(structure, "format", &fourcc); + + if (fourcc == GST_MAKE_FOURCC('I','4','2','0')) { + img = QImage(width/2, height/2, QImage::Format_RGB32); + + const uchar *data = (const uchar *)buffer->data; + + for (int y=0; ydata, + width, + height, + format); + img.bits(); //detach + } + } + } + gst_caps_unref(caps); + } + + static int exposedSignalIndex = session->metaObject()->indexOfSignal("imageExposed(int)"); + session->metaObject()->method(exposedSignalIndex).invoke(session, + Qt::QueuedConnection, + Q_ARG(int,session->m_imageRequestId)); + + static int capturedSignalIndex = session->metaObject()->indexOfSignal("imageCaptured(int,QImage)"); + session->metaObject()->method(capturedSignalIndex).invoke(session, + Qt::QueuedConnection, + Q_ARG(int,session->m_imageRequestId), + Q_ARG(QImage,img)); + + return TRUE; + } else { + return FALSE; + } +} + +static gboolean saveImageFilter(GstElement *element, + GstBuffer *buffer, + GstPad *pad, + void *appdata) +{ + Q_UNUSED(element); + Q_UNUSED(pad); + QGstreamerCaptureSession *session = (QGstreamerCaptureSession *)appdata; + + QString fileName = session->m_imageFileName; + + if (!fileName.isEmpty()) { + QFile f(fileName); + if (f.open(QFile::WriteOnly)) { + f.write((const char *)buffer->data, buffer->size); + f.close(); + + static int signalIndex = session->metaObject()->indexOfSignal("imageSaved(int,QString)"); + session->metaObject()->method(signalIndex).invoke(session, + Qt::QueuedConnection, + Q_ARG(int,session->m_imageRequestId), + Q_ARG(QString,fileName)); + } + } + + return TRUE; +} + +GstElement *QGstreamerCaptureSession::buildImageCapture() +{ + GstElement *bin = gst_bin_new("image-capture-bin"); + GstElement *queue = gst_element_factory_make("queue", "queue-image-capture"); + GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-image-capture"); + GstElement *encoder = gst_element_factory_make("jpegenc", "image-encoder"); + GstElement *sink = gst_element_factory_make("fakesink","sink-image-capture"); + + GstPad *pad = gst_element_get_static_pad(queue, "src"); + Q_ASSERT(pad); + gst_pad_add_buffer_probe(pad, G_CALLBACK(passImageFilter), this); + + g_object_set(G_OBJECT(sink), "signal-handoffs", TRUE, NULL); + g_signal_connect(G_OBJECT(sink), "handoff", + G_CALLBACK(saveImageFilter), this); + + gst_bin_add_many(GST_BIN(bin), queue, colorspace, encoder, sink, NULL); + gst_element_link_many(queue, colorspace, encoder, sink, NULL); + + // add ghostpads + pad = gst_element_get_static_pad(queue, "sink"); + Q_ASSERT(pad); + gst_element_add_pad(GST_ELEMENT(bin), gst_ghost_pad_new("imagesink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + m_passImage = false; + m_passPrerollImage = true; + m_imageFileName = QString(); + + return bin; +} + +void QGstreamerCaptureSession::captureImage(int requestId, const QString &fileName) +{ + m_imageRequestId = requestId; + m_imageFileName = fileName; + m_passImage = true; +} + + +#define REMOVE_ELEMENT(element) { if (element) {gst_bin_remove(GST_BIN(m_pipeline), element); element = 0;} } + +bool QGstreamerCaptureSession::rebuildGraph(QGstreamerCaptureSession::PipelineMode newMode) +{ + REMOVE_ELEMENT(m_audioSrc); + REMOVE_ELEMENT(m_audioPreview); + REMOVE_ELEMENT(m_audioPreviewQueue); + REMOVE_ELEMENT(m_audioTee); + REMOVE_ELEMENT(m_videoSrc); + REMOVE_ELEMENT(m_videoPreview); + REMOVE_ELEMENT(m_videoPreviewQueue); + REMOVE_ELEMENT(m_videoTee); + REMOVE_ELEMENT(m_encodeBin); + REMOVE_ELEMENT(m_imageCaptureBin); + m_audioVolume = 0; + + bool ok = true; + + switch (newMode) { + case EmptyPipeline: + break; + case PreviewPipeline: + if (m_captureMode & Audio) { + m_audioSrc = buildAudioSrc(); + m_audioPreview = buildAudioPreview(); + + ok &= m_audioSrc && m_audioPreview; + + if (ok) { + gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioPreview, NULL); + ok &= gst_element_link(m_audioSrc, m_audioPreview); + } + } + if (m_captureMode & Video || m_captureMode & Image) { + m_videoSrc = buildVideoSrc(); + m_videoTee = gst_element_factory_make("tee", "video-preview-tee"); + m_videoPreviewQueue = gst_element_factory_make("queue", "video-preview-queue"); + m_videoPreview = buildVideoPreview(); + m_imageCaptureBin = buildImageCapture(); + + ok &= m_videoSrc && m_videoTee && m_videoPreviewQueue && m_videoPreview && m_imageCaptureBin; + + if (ok) { + gst_bin_add_many(GST_BIN(m_pipeline), m_videoSrc, m_videoTee, + m_videoPreviewQueue, m_videoPreview, + m_imageCaptureBin, NULL); + + ok &= gst_element_link(m_videoSrc, m_videoTee); + ok &= gst_element_link(m_videoTee, m_videoPreviewQueue); + ok &= gst_element_link(m_videoPreviewQueue, m_videoPreview); + ok &= gst_element_link(m_videoTee, m_imageCaptureBin); + } + } + break; + case RecordingPipeline: + m_encodeBin = buildEncodeBin(); + gst_bin_add(GST_BIN(m_pipeline), m_encodeBin); + + if (m_captureMode & Audio) { + m_audioSrc = buildAudioSrc(); + ok &= m_audioSrc != 0; + + gst_bin_add(GST_BIN(m_pipeline), m_audioSrc); + ok &= gst_element_link(m_audioSrc, m_encodeBin); + } + + if (m_captureMode & Video) { + m_videoSrc = buildVideoSrc(); + ok &= m_videoSrc != 0; + + gst_bin_add(GST_BIN(m_pipeline), m_videoSrc); + ok &= gst_element_link(m_videoSrc, m_encodeBin); + } + + if (!m_metaData.isEmpty()) + setMetaData(m_metaData); + + break; + case PreviewAndRecordingPipeline: + m_encodeBin = buildEncodeBin(); + if (m_encodeBin) + gst_bin_add(GST_BIN(m_pipeline), m_encodeBin); + + ok &= m_encodeBin != 0; + + if (ok && m_captureMode & Audio) { + m_audioSrc = buildAudioSrc(); + m_audioPreview = buildAudioPreview(); + m_audioTee = gst_element_factory_make("tee", NULL); + m_audioPreviewQueue = gst_element_factory_make("queue", NULL); + + ok &= m_audioSrc && m_audioPreview && m_audioTee && m_audioPreviewQueue; + + if (ok) { + gst_bin_add_many(GST_BIN(m_pipeline), m_audioSrc, m_audioTee, + m_audioPreviewQueue, m_audioPreview, NULL); + ok &= gst_element_link(m_audioSrc, m_audioTee); + ok &= gst_element_link(m_audioTee, m_audioPreviewQueue); + ok &= gst_element_link(m_audioPreviewQueue, m_audioPreview); + ok &= gst_element_link(m_audioTee, m_encodeBin); + } + } + + if (ok && (m_captureMode & Video || m_captureMode & Image)) { + m_videoSrc = buildVideoSrc(); + m_videoPreview = buildVideoPreview(); + m_videoTee = gst_element_factory_make("tee", NULL); + m_videoPreviewQueue = gst_element_factory_make("queue", NULL); + + ok &= m_videoSrc && m_videoPreview && m_videoTee && m_videoPreviewQueue; + + if (ok) { + gst_bin_add_many(GST_BIN(m_pipeline), m_videoSrc, m_videoTee, + m_videoPreviewQueue, m_videoPreview, NULL); + ok &= gst_element_link(m_videoSrc, m_videoTee); + ok &= gst_element_link(m_videoTee, m_videoPreviewQueue); + ok &= gst_element_link(m_videoPreviewQueue, m_videoPreview); + } + + if (ok && (m_captureMode & Video)) + ok &= gst_element_link(m_videoTee, m_encodeBin); + } + + if (!m_metaData.isEmpty()) + setMetaData(m_metaData); + + + break; + } + + if (!ok) { + emit error(int(QMediaRecorder::FormatError),tr("Failed to build media capture pipeline.")); + } + + dumpGraph( QString("rebuild_graph_%1_%2").arg(m_pipelineMode).arg(newMode) ); + if (m_encodeBin) { + QString fileName = QString("rebuild_graph_encode_%1_%2").arg(m_pipelineMode).arg(newMode); +#if !(GST_DISABLE_GST_DEBUG) && (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + _gst_debug_bin_to_dot_file(GST_BIN(m_encodeBin), GST_DEBUG_GRAPH_SHOW_ALL, fileName.toAscii()); +#endif + } + + if (ok) { + m_pipelineMode = newMode; + } else { + m_pipelineMode = EmptyPipeline; + + REMOVE_ELEMENT(m_audioSrc); + REMOVE_ELEMENT(m_audioPreview); + REMOVE_ELEMENT(m_audioPreviewQueue); + REMOVE_ELEMENT(m_audioTee); + REMOVE_ELEMENT(m_videoSrc); + REMOVE_ELEMENT(m_videoPreview); + REMOVE_ELEMENT(m_videoPreviewQueue); + REMOVE_ELEMENT(m_videoTee); + REMOVE_ELEMENT(m_encodeBin); + } + + return ok; +} + +void QGstreamerCaptureSession::dumpGraph(const QString &fileName) +{ +#if !(GST_DISABLE_GST_DEBUG) && (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + _gst_debug_bin_to_dot_file(GST_BIN(m_pipeline), + GstDebugGraphDetails(/*GST_DEBUG_GRAPH_SHOW_ALL |*/ GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES), + fileName.toAscii()); +#endif +} + +QUrl QGstreamerCaptureSession::outputLocation() const +{ + return m_sink; +} + +bool QGstreamerCaptureSession::setOutputLocation(const QUrl& sink) +{ + m_sink = sink; + return true; +} + +void QGstreamerCaptureSession::setAudioInput(QGstreamerElementFactory *audioInput) +{ + m_audioInputFactory = audioInput; +} + +void QGstreamerCaptureSession::setAudioPreview(QGstreamerElementFactory *audioPreview) +{ + m_audioPreviewFactory = audioPreview; +} + +void QGstreamerCaptureSession::setVideoInput(QGstreamerVideoInput *videoInput) +{ + m_videoInputFactory = videoInput; +} + +void QGstreamerCaptureSession::setVideoPreview(QObject *viewfinder) +{ + m_viewfinderInterface = qobject_cast(viewfinder); + if (!m_viewfinderInterface) + viewfinder = 0; + + if (m_viewfinder != viewfinder) { + bool oldReady = isReady(); + + if (m_viewfinder) { + disconnect(m_viewfinder, SIGNAL(sinkChanged()), + this, SIGNAL(viewfinderChanged())); + disconnect(m_viewfinder, SIGNAL(readyChanged(bool)), + this, SIGNAL(readyChanged(bool))); + } + + m_viewfinder = viewfinder; + //m_viewfinderHasChanged = true; + + if (m_viewfinder) { + connect(m_viewfinder, SIGNAL(sinkChanged()), + this, SIGNAL(viewfinderChanged())); + connect(m_viewfinder, SIGNAL(readyChanged(bool)), + this, SIGNAL(readyChanged(bool))); + } + + emit viewfinderChanged(); + if (oldReady != isReady()) + emit readyChanged(isReady()); + } +} + +bool QGstreamerCaptureSession::isReady() const +{ + return m_viewfinderInterface != 0 && m_viewfinderInterface->isReady(); +} + +QGstreamerCaptureSession::State QGstreamerCaptureSession::state() const +{ + return m_state; +} + +void QGstreamerCaptureSession::waitForStopped() +{ + GstState state = GST_STATE_PLAYING; + gst_element_get_state(m_pipeline, &state, 0, 0); + + while (state != GST_STATE_NULL) { + qApp->processEvents(); + gst_element_get_state(m_pipeline, &state, 0, 0); + } +} + +void QGstreamerCaptureSession::setState(QGstreamerCaptureSession::State newState) +{ + if (newState == m_pendingState && !m_waitingForEos) + return; + + m_pendingState = newState; + + PipelineMode newMode = EmptyPipeline; + + switch (newState) { + case PausedState: + case RecordingState: + newMode = PreviewAndRecordingPipeline; + break; + case PreviewState: + newMode = PreviewPipeline; + break; + case StoppedState: + newMode = EmptyPipeline; + break; + } + + if (newMode != m_pipelineMode) { + if (m_pipelineMode == PreviewAndRecordingPipeline) { + if (!m_waitingForEos) { + m_waitingForEos = true; + //qDebug() << "Waiting for EOS"; + //with live sources it's necessary to send EOS even to pipeline + //before going to STOPPED state + gst_element_send_event(m_pipeline, gst_event_new_eos()); + // Unless gstreamer is in GST_STATE_PLAYING our EOS message will not be received. + gst_element_set_state(m_pipeline, GST_STATE_PLAYING); + + return; + } else { + m_waitingForEos = false; + //qDebug() << "EOS received"; + } + } + + //select suitable default codecs/containers, if necessary + m_recorderControl->applySettings(); + + gst_element_set_state(m_pipeline, GST_STATE_NULL); + + //It would be better to do this async. but + //gstreamer doesn't notify about pipeline went to NULL state + waitForStopped(); + if (!rebuildGraph(newMode)) { + m_pendingState = StoppedState; + m_state = StoppedState; + emit stateChanged(StoppedState); + + return; + } + } + + switch (newState) { + case PausedState: + gst_element_set_state(m_pipeline, GST_STATE_PAUSED); + break; + case RecordingState: + case PreviewState: + gst_element_set_state(m_pipeline, GST_STATE_PLAYING); + break; + case StoppedState: + gst_element_set_state(m_pipeline, GST_STATE_NULL); + } + + //we have to do it here, since gstreamer will not emit bus messages any more + if (newState == StoppedState) { + m_state = StoppedState; + emit stateChanged(StoppedState); + } +} + + +qint64 QGstreamerCaptureSession::duration() const +{ + GstFormat format = GST_FORMAT_TIME; + gint64 duration = 0; + + if ( m_encodeBin && gst_element_query_position(m_encodeBin, &format, &duration)) + return duration / 1000000; + else + return 0; +} + +void QGstreamerCaptureSession::setCaptureDevice(const QString &deviceName) +{ + m_captureDevice = deviceName; +} + +void QGstreamerCaptureSession::setMetaData(const QMap &data) +{ + //qDebug() << "QGstreamerCaptureSession::setMetaData" << data; + m_metaData = data; + + if (m_encodeBin) { + GstIterator *elements = gst_bin_iterate_all_by_interface(GST_BIN(m_encodeBin), GST_TYPE_TAG_SETTER); + GstElement *element = 0; + while (gst_iterator_next(elements, (void**)&element) == GST_ITERATOR_OK) { + //qDebug() << "found element with tag setter interface:" << gst_element_get_name(element); + QMapIterator it(data); + while (it.hasNext()) { + it.next(); + const QString tagName = it.key(); + const QVariant tagValue = it.value(); + + + switch(tagValue.type()) { + case QVariant::String: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE_ALL, + tagName.toUtf8().constData(), + tagValue.toString().toUtf8().constData(), + NULL); + break; + case QVariant::Int: + case QVariant::LongLong: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE_ALL, + tagName.toUtf8().constData(), + tagValue.toInt(), + NULL); + break; + case QVariant::Double: + gst_tag_setter_add_tags(GST_TAG_SETTER(element), + GST_TAG_MERGE_REPLACE_ALL, + tagName.toUtf8().constData(), + tagValue.toDouble(), + NULL); + break; + default: + break; + } + + } + + } + } +} + +bool QGstreamerCaptureSession::processSyncMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) { + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoPreview)) + m_viewfinderInterface->handleSyncMessage(gm); + + if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) { + if (m_audioPreviewFactory) + m_audioPreviewFactory->prepareWinId(); + + if (m_viewfinderInterface) + m_viewfinderInterface->precessNewStream(); + + return true; + } + } + + return false; +} + +void QGstreamerCaptureSession::busMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm) { + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { + GError *err; + gchar *debug; + gst_message_parse_error (gm, &err, &debug); + emit error(int(QMediaRecorder::ResourceError),QString::fromUtf8(err->message)); + g_error_free (err); + g_free (debug); + } + + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_pipeline)) { + switch (GST_MESSAGE_TYPE(gm)) { + case GST_MESSAGE_DURATION: + break; + + case GST_MESSAGE_EOS: + if (m_waitingForEos) + setState(m_pendingState); + break; + + case GST_MESSAGE_STATE_CHANGED: + { + + GstState oldState; + GstState newState; + GstState pending; + + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + + QStringList states; + states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING"; + + /* + qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \ + .arg(states[oldState]) \ + .arg(states[newState]) \ + .arg(states[pending]); + + #define ENUM_NAME(c,e,v) (c::staticMetaObject.enumerator(c::staticMetaObject.indexOfEnumerator(e)).valueToKey((v))) + + qDebug() << "Current session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state); + qDebug() << "Pending session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_pendingState); + */ + + switch (newState) { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + case GST_STATE_READY: + if (m_state != StoppedState && m_pendingState == StoppedState) { + emit stateChanged(m_state = StoppedState); + dumpGraph("stopped"); + } + break; + case GST_STATE_PAUSED: + if (m_state != PausedState && m_pendingState == PausedState) + emit stateChanged(m_state = PausedState); + dumpGraph("paused"); + + if (m_pipelineMode == RecordingPipeline && !m_metaData.isEmpty()) + setMetaData(m_metaData); + break; + case GST_STATE_PLAYING: + { + if ((m_pendingState == PreviewState || m_pendingState == RecordingState) && + m_state != m_pendingState) + { + m_state = m_pendingState; + emit stateChanged(m_state); + } + + if (m_pipelineMode == PreviewPipeline) + dumpGraph("preview"); + else + dumpGraph("recording"); + } + break; + } + } + break; + default: + break; + } + //qDebug() << "New session state:" << ENUM_NAME(QGstreamerCaptureSession,"State",m_state); + } + + if (m_videoPreview && m_viewfinderInterface && + GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoPreview)) + m_viewfinderInterface->handleBusMessage(gm); + } +} + +void QGstreamerCaptureSession::setMuted(bool muted) +{ + if (m_muted != muted) { + m_muted = muted; + if (m_audioVolume) + g_object_set(G_OBJECT(m_audioVolume), "volume", (m_muted ? 0.0 : 1.0), NULL); + emit mutedChanged(muted); + } +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h new file mode 100644 index 000000000..f04a49be2 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamercapturesession.h @@ -0,0 +1,211 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERCAPTURESESSION_H +#define QGSTREAMERCAPTURESESSION_H + +#include +#include + +#include + +#include + +#include "qgstreamerbushelper.h" + +QT_USE_NAMESPACE + +class QGstreamerMessage; +class QGstreamerBusHelper; +class QGstreamerAudioEncode; +class QGstreamerVideoEncode; +class QGstreamerImageEncode; +class QGstreamerRecorderControl; +class QGstreamerMediaContainerControl; +class QGstreamerVideoRendererInterface; + +class QGstreamerElementFactory +{ +public: + virtual GstElement *buildElement() = 0; + virtual void prepareWinId() {} +}; + +class QGstreamerVideoInput : public QGstreamerElementFactory +{ +public: + virtual QList supportedFrameRates(const QSize &frameSize = QSize()) const = 0; + virtual QList supportedResolutions(qreal frameRate = -1) const = 0; +}; + +class QGstreamerCaptureSession : public QObject, public QGstreamerSyncEventFilter +{ + Q_OBJECT + Q_PROPERTY(qint64 duration READ duration NOTIFY durationChanged) + Q_ENUMS(State) + Q_ENUMS(CaptureMode) +public: + enum CaptureMode { Audio = 1, Video = 2, Image=4, AudioAndVideo = Audio | Video }; + enum State { StoppedState, PreviewState, PausedState, RecordingState }; + + QGstreamerCaptureSession(CaptureMode captureMode, QObject *parent); + ~QGstreamerCaptureSession(); + + CaptureMode captureMode() const { return m_captureMode; } + void setCaptureMode(CaptureMode); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl& sink); + + QGstreamerAudioEncode *audioEncodeControl() const { return m_audioEncodeControl; } + QGstreamerVideoEncode *videoEncodeControl() const { return m_videoEncodeControl; } + QGstreamerImageEncode *imageEncodeControl() const { return m_imageEncodeControl; } + + QGstreamerRecorderControl *recorderControl() const { return m_recorderControl; } + QGstreamerMediaContainerControl *mediaContainerControl() const { return m_mediaContainerControl; } + + QGstreamerElementFactory *audioInput() const { return m_audioInputFactory; } + void setAudioInput(QGstreamerElementFactory *audioInput); + + QGstreamerElementFactory *audioPreview() const { return m_audioPreviewFactory; } + void setAudioPreview(QGstreamerElementFactory *audioPreview); + + QGstreamerVideoInput *videoInput() const { return m_videoInputFactory; } + void setVideoInput(QGstreamerVideoInput *videoInput); + + QObject *videoPreview() const { return m_viewfinder; } + void setVideoPreview(QObject *viewfinder); + + void captureImage(int requestId, const QString &fileName); + + State state() const; + qint64 duration() const; + bool isMuted() const { return m_muted; } + + bool isReady() const; + + bool processSyncMessage(const QGstreamerMessage &message); + +signals: + void stateChanged(QGstreamerCaptureSession::State state); + void durationChanged(qint64 duration); + void error(int error, const QString &errorString); + void imageExposed(int requestId); + void imageCaptured(int requestId, const QImage &img); + void imageSaved(int requestId, const QString &path); + void mutedChanged(bool); + void readyChanged(bool); + void viewfinderChanged(); + +public slots: + void setState(QGstreamerCaptureSession::State); + void setCaptureDevice(const QString &deviceName); + + void dumpGraph(const QString &fileName); + + void setMetaData(const QMap&); + void setMuted(bool); + +private slots: + void busMessage(const QGstreamerMessage &message); + +private: + enum PipelineMode { EmptyPipeline, PreviewPipeline, RecordingPipeline, PreviewAndRecordingPipeline }; + + GstElement *buildEncodeBin(); + GstElement *buildAudioSrc(); + GstElement *buildAudioPreview(); + GstElement *buildVideoSrc(); + GstElement *buildVideoPreview(); + GstElement *buildImageCapture(); + + void waitForStopped(); + bool rebuildGraph(QGstreamerCaptureSession::PipelineMode newMode); + + QUrl m_sink; + QString m_captureDevice; + State m_state; + State m_pendingState; + bool m_waitingForEos; + PipelineMode m_pipelineMode; + QGstreamerCaptureSession::CaptureMode m_captureMode; + QMap m_metaData; + + QGstreamerElementFactory *m_audioInputFactory; + QGstreamerElementFactory *m_audioPreviewFactory; + QGstreamerVideoInput *m_videoInputFactory; + QObject *m_viewfinder; + QGstreamerVideoRendererInterface *m_viewfinderInterface; + + QGstreamerAudioEncode *m_audioEncodeControl; + QGstreamerVideoEncode *m_videoEncodeControl; + QGstreamerImageEncode *m_imageEncodeControl; + QGstreamerRecorderControl *m_recorderControl; + QGstreamerMediaContainerControl *m_mediaContainerControl; + + QGstreamerBusHelper *m_busHelper; + GstBus* m_bus; + GstElement *m_pipeline; + + GstElement *m_audioSrc; + GstElement *m_audioTee; + GstElement *m_audioPreviewQueue; + GstElement *m_audioPreview; + GstElement *m_audioVolume; + bool m_muted; + + GstElement *m_videoSrc; + GstElement *m_videoTee; + GstElement *m_videoPreviewQueue; + GstElement *m_videoPreview; + + GstElement *m_imageCaptureBin; + + GstElement *m_encodeBin; + +public: + bool m_passImage; + bool m_passPrerollImage; + QString m_imageFileName; + int m_imageRequestId; +}; + +#endif // QGSTREAMERCAPTURESESSION_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp new file mode 100644 index 000000000..99c3b1665 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerimagecapturecontrol.h" +#include +#include + +QGstreamerImageCaptureControl::QGstreamerImageCaptureControl(QGstreamerCaptureSession *session) + :QCameraImageCaptureControl(session), m_session(session), m_ready(false), m_lastId(0) +{ + connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateState())); + connect(m_session, SIGNAL(imageExposed(int)), this, SIGNAL(imageExposed(int))); + connect(m_session, SIGNAL(imageCaptured(int,QImage)), this, SIGNAL(imageCaptured(int,QImage))); + connect(m_session, SIGNAL(imageSaved(int,QString)), this, SIGNAL(imageSaved(int,QString))); +} + +QGstreamerImageCaptureControl::~QGstreamerImageCaptureControl() +{ +} + +bool QGstreamerImageCaptureControl::isReadyForCapture() const +{ + return m_ready; +} + +int QGstreamerImageCaptureControl::capture(const QString &fileName) +{ + QString path = fileName; + if (path.isEmpty()) { + int lastImage = 0; + QDir outputDir = QDir::currentPath(); + foreach(QString fileName, outputDir.entryList(QStringList() << "img_*.jpg")) { + int imgNumber = fileName.mid(4, fileName.size()-8).toInt(); + lastImage = qMax(lastImage, imgNumber); + } + + path = QString("img_%1.jpg").arg(lastImage+1, + 4, //fieldWidth + 10, + QLatin1Char('0')); + } + m_lastId++; + + m_session->captureImage(m_lastId, path); + + return m_lastId; +} + +void QGstreamerImageCaptureControl::cancelCapture() +{ + +} + +void QGstreamerImageCaptureControl::updateState() +{ + bool ready = m_session->state() == QGstreamerCaptureSession::PreviewState; + if (m_ready != ready) { + emit readyForCaptureChanged(m_ready = ready); + } +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h new file mode 100644 index 000000000..898162440 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerimagecapturecontrol.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERIMAGECAPTURECONTROL_H +#define QGSTREAMERIMAGECAPTURECONTROL_H + +#include +#include "qgstreamercapturesession.h" +QT_USE_NAMESPACE + +class QGstreamerImageCaptureControl : public QCameraImageCaptureControl +{ + Q_OBJECT +public: + QGstreamerImageCaptureControl(QGstreamerCaptureSession *session); + virtual ~QGstreamerImageCaptureControl(); + + QCameraImageCapture::DriveMode driveMode() const { return QCameraImageCapture::SingleImageCapture; } + void setDriveMode(QCameraImageCapture::DriveMode) {} + + bool isReadyForCapture() const; + int capture(const QString &fileName); + void cancelCapture(); + +private slots: + void updateState(); + +private: + QGstreamerCaptureSession *m_session; + bool m_ready; + int m_lastId; +}; + +#endif // QGSTREAMERCAPTURECORNTROL_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp new file mode 100644 index 000000000..5aef97794 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.cpp @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerimageencode.h" +#include "qgstreamercapturesession.h" + +#include + +#include + +QGstreamerImageEncode::QGstreamerImageEncode(QGstreamerCaptureSession *session) + :QImageEncoderControl(session), m_session(session) +{ +} + +QGstreamerImageEncode::~QGstreamerImageEncode() +{ +} + +QList QGstreamerImageEncode::supportedResolutions(const QImageEncoderSettings &, bool *continuous) const +{ + if (continuous) + *continuous = m_session->videoInput() != 0; + + return m_session->videoInput() ? m_session->videoInput()->supportedResolutions() : QList(); +} + +QStringList QGstreamerImageEncode::supportedImageCodecs() const +{ + return QStringList() << "jpeg"; +} + +QString QGstreamerImageEncode::imageCodecDescription(const QString &codecName) const +{ + if (codecName == "jpeg") + return tr("JPEG image encoder"); + + return QString(); +} + +QImageEncoderSettings QGstreamerImageEncode::imageSettings() const +{ + return m_settings; +} + +void QGstreamerImageEncode::setImageSettings(const QImageEncoderSettings &settings) +{ + if (m_settings != settings) { + m_settings = settings; + emit settingsChanged(); + } +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h new file mode 100644 index 000000000..b66ba75f8 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerimageencode.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERIMAGEENCODE_H +#define QGSTREAMERIMAGEENCODE_H + +class QGstreamerCaptureSession; + +#include + +#include +#include + +#include +QT_USE_NAMESPACE + +class QGstreamerImageEncode : public QImageEncoderControl +{ + Q_OBJECT +public: + QGstreamerImageEncode(QGstreamerCaptureSession *session); + virtual ~QGstreamerImageEncode(); + + QList supportedResolutions(const QImageEncoderSettings &settings = QImageEncoderSettings(), + bool *continuous = 0) const; + + QStringList supportedImageCodecs() const; + QString imageCodecDescription(const QString &codecName) const; + + QImageEncoderSettings imageSettings() const; + void setImageSettings(const QImageEncoderSettings &settings); + +Q_SIGNALS: + void settingsChanged(); + +private: + QImageEncoderSettings m_settings; + + QGstreamerCaptureSession *m_session; +}; + +#endif diff --git a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp new file mode 100644 index 000000000..34c43e3ac --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.cpp @@ -0,0 +1,135 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamermediacontainercontrol.h" + + +#include + +QGstreamerMediaContainerControl::QGstreamerMediaContainerControl(QObject *parent) + :QMediaContainerControl(parent) +{ + QList formatCandidates; + formatCandidates << "matroska" << "ogg" << "mp4" << "wav" << "quicktime" << "avi" << "3gpp"; + formatCandidates << "flv" << "amr" << "asf" << "dv" << "gif"; + formatCandidates << "mpeg" << "vob" << "mpegts" << "3g2" << "3gp"; + formatCandidates << "raw"; + + m_elementNames["matroska"] = "matroskamux"; + m_elementNames["ogg"] = "oggmux"; + m_elementNames["mp4"] = "ffmux_mp4"; + m_elementNames["quicktime"] = "ffmux_mov"; + m_elementNames["avi"] = "avimux"; + m_elementNames["3gpp"] = "gppmux"; + m_elementNames["flv"] = "flvmux"; + m_elementNames["wav"] = "wavenc"; + m_elementNames["amr"] = "ffmux_amr"; + m_elementNames["asf"] = "ffmux_asf"; + m_elementNames["dv"] = "ffmux_dv"; + m_elementNames["gif"] = "ffmux_gif"; + m_elementNames["mpeg"] = "ffmux_mpeg"; + m_elementNames["vob"] = "ffmux_vob"; + m_elementNames["mpegts"] = "ffmux_mpegts"; + m_elementNames["3g2"] = "ffmux_3g2"; + m_elementNames["3gp"] = "ffmux_3gp"; + m_elementNames["raw"] = "identity"; + + m_containerExtensions["matroska"] = "mkv"; + m_containerExtensions["quicktime"] = "mov"; + m_containerExtensions["mpegts"] = "m2t"; + m_containerExtensions["mpeg"] = "mpg"; + + QSet allTypes; + + foreach( const QByteArray& formatName, formatCandidates ) { + QByteArray elementName = m_elementNames[formatName]; + GstElementFactory *factory = gst_element_factory_find(elementName.constData()); + if (factory) { + m_supportedContainers.append(formatName); + const gchar *descr = gst_element_factory_get_description(factory); + m_containerDescriptions.insert(formatName, QString::fromUtf8(descr)); + + + if (formatName == QByteArray("raw")) { + m_streamTypes.insert(formatName, allTypes); + } else { + QSet types = supportedStreamTypes(factory, GST_PAD_SINK); + m_streamTypes.insert(formatName, types); + allTypes.unite(types); + } + + gst_object_unref(GST_OBJECT(factory)); + } + } + + //if (!m_supportedContainers.isEmpty()) + // setContainerMimeType(m_supportedContainers[0]); +} + +QSet QGstreamerMediaContainerControl::supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction) +{ + QSet types; + const GList *pads = gst_element_factory_get_static_pad_templates(factory); + for (const GList *pad = pads; pad; pad = g_list_next(pad)) { + GstStaticPadTemplate *templ = (GstStaticPadTemplate*)pad->data; + if (templ->direction == direction) { + GstCaps *caps = gst_static_caps_get(&templ->static_caps); + for (uint i=0; i QGstreamerMediaContainerControl::supportedStreamTypes(const QString &container) const +{ + return m_streamTypes.value(container); +} + +QString QGstreamerMediaContainerControl::containerExtension() const +{ + return m_containerExtensions.value(m_format, m_format); +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h new file mode 100644 index 000000000..57e0a5bf1 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamermediacontainercontrol.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERMEDIACONTAINERCONTROL_H +#define QGSTREAMERMEDIACONTAINERCONTROL_H + +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +class QGstreamerMediaContainerControl : public QMediaContainerControl +{ +Q_OBJECT +public: + QGstreamerMediaContainerControl(QObject *parent); + virtual ~QGstreamerMediaContainerControl() {}; + + virtual QStringList supportedContainers() const { return m_supportedContainers; } + virtual QString containerMimeType() const { return m_format; } + virtual void setContainerMimeType(const QString &formatMimeType) { m_format = formatMimeType; } + + virtual QString containerDescription(const QString &formatMimeType) const { return m_containerDescriptions.value(formatMimeType); } + + QByteArray formatElementName() const { return m_elementNames.value(containerMimeType()); } + + QSet supportedStreamTypes(const QString &container) const; + + static QSet supportedStreamTypes(GstElementFactory *factory, GstPadDirection direction); + + QString containerExtension() const; + +private: + QString m_format; + QStringList m_supportedContainers; + QMap m_elementNames; + QMap m_containerDescriptions; + QMap m_containerExtensions; + QMap > m_streamTypes; +}; + +#endif // QGSTREAMERMEDIACONTAINERCONTROL_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp new file mode 100644 index 000000000..24b28dead --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.cpp @@ -0,0 +1,289 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerrecordercontrol.h" +#include "qgstreameraudioencode.h" +#include "qgstreamervideoencode.h" +#include "qgstreamermediacontainercontrol.h" +#include + +QGstreamerRecorderControl::QGstreamerRecorderControl(QGstreamerCaptureSession *session) + :QMediaRecorderControl(session), m_session(session), m_state(QMediaRecorder::StoppedState) +{ + connect(m_session, SIGNAL(stateChanged(QGstreamerCaptureSession::State)), SLOT(updateState())); + connect(m_session, SIGNAL(error(int,QString)), SIGNAL(error(int,QString))); + connect(m_session, SIGNAL(durationChanged(qint64)), SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedChanged(bool)), SIGNAL(mutedChanged(bool))); + m_hasPreviewState = m_session->captureMode() != QGstreamerCaptureSession::Audio; +} + +QGstreamerRecorderControl::~QGstreamerRecorderControl() +{ +} + +QUrl QGstreamerRecorderControl::outputLocation() const +{ + return m_session->outputLocation(); +} + +bool QGstreamerRecorderControl::setOutputLocation(const QUrl &sink) +{ + m_outputLocation = sink; + m_session->setOutputLocation(sink); + return true; +} + + +QMediaRecorder::State QGstreamerRecorderControl::state() const +{ + switch ( m_session->state() ) { + case QGstreamerCaptureSession::RecordingState: + return QMediaRecorder::RecordingState; + case QGstreamerCaptureSession::PausedState: + return QMediaRecorder::PausedState; + case QGstreamerCaptureSession::PreviewState: + case QGstreamerCaptureSession::StoppedState: + return QMediaRecorder::StoppedState; + } + + return QMediaRecorder::StoppedState; + +} + +void QGstreamerRecorderControl::updateState() +{ + QMediaRecorder::State newState = state(); + if (m_state != newState) { + m_state = newState; + emit stateChanged(m_state); + } +} + +qint64 QGstreamerRecorderControl::duration() const +{ + return m_session->duration(); +} + +void QGstreamerRecorderControl::record() +{ + if (m_outputLocation.isEmpty()) { + QString container = m_session->mediaContainerControl()->containerExtension(); + if (container.isEmpty()) + container = "raw"; + + m_session->setOutputLocation(QUrl(generateFileName(defaultDir(), container))); + } + + m_session->dumpGraph("before-record"); + if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) { + m_session->setState(QGstreamerCaptureSession::RecordingState); + } else + emit error(QMediaRecorder::ResourceError, tr("Service has not been started")); + + m_session->dumpGraph("after-record"); +} + +void QGstreamerRecorderControl::pause() +{ + m_session->dumpGraph("before-pause"); + if (!m_hasPreviewState || m_session->state() != QGstreamerCaptureSession::StoppedState) { + m_session->setState(QGstreamerCaptureSession::PausedState); + } else + emit error(QMediaRecorder::ResourceError, tr("Service has not been started")); +} + +void QGstreamerRecorderControl::stop() +{ + if (!m_hasPreviewState) { + m_session->setState(QGstreamerCaptureSession::StoppedState); + } else { + if (m_session->state() != QGstreamerCaptureSession::StoppedState) + m_session->setState(QGstreamerCaptureSession::PreviewState); + } +} + +void QGstreamerRecorderControl::applySettings() +{ + //Check the codecs are compatible with container, + //and choose the compatible codecs/container if omitted + QGstreamerAudioEncode *audioEncodeControl = m_session->audioEncodeControl(); + QGstreamerVideoEncode *videoEncodeControl = m_session->videoEncodeControl(); + QGstreamerMediaContainerControl *mediaContainerControl = m_session->mediaContainerControl(); + + bool needAudio = m_session->captureMode() & QGstreamerCaptureSession::Audio; + bool needVideo = m_session->captureMode() & QGstreamerCaptureSession::Video; + + QStringList containerCandidates; + if (mediaContainerControl->containerMimeType().isEmpty()) + containerCandidates = mediaContainerControl->supportedContainers(); + else + containerCandidates << mediaContainerControl->containerMimeType(); + + + QStringList audioCandidates; + if (needAudio) { + QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings(); + if (audioSettings.codec().isEmpty()) + audioCandidates = audioEncodeControl->supportedAudioCodecs(); + else + audioCandidates << audioSettings.codec(); + } + + QStringList videoCandidates; + if (needVideo) { + QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings(); + if (videoSettings.codec().isEmpty()) + videoCandidates = videoEncodeControl->supportedVideoCodecs(); + else + videoCandidates << videoSettings.codec(); + } + + QString container; + QString audioCodec; + QString videoCodec; + + foreach (const QString &containerCandidate, containerCandidates) { + QSet supportedTypes = mediaContainerControl->supportedStreamTypes(containerCandidate); + + audioCodec.clear(); + videoCodec.clear(); + + if (needAudio) { + bool found = false; + foreach (const QString &audioCandidate, audioCandidates) { + QSet audioTypes = audioEncodeControl->supportedStreamTypes(audioCandidate); + if (!audioTypes.intersect(supportedTypes).isEmpty()) { + found = true; + audioCodec = audioCandidate; + break; + } + } + if (!found) + continue; + } + + if (needVideo) { + bool found = false; + foreach (const QString &videoCandidate, videoCandidates) { + QSet videoTypes = videoEncodeControl->supportedStreamTypes(videoCandidate); + if (!videoTypes.intersect(supportedTypes).isEmpty()) { + found = true; + videoCodec = videoCandidate; + break; + } + } + if (!found) + continue; + } + + container = containerCandidate; + break; + } + + if (container.isEmpty()) { + emit error(QMediaRecorder::FormatError, tr("Not compatible codecs and container format.")); + } else { + mediaContainerControl->setContainerMimeType(container); + + if (needAudio) { + QAudioEncoderSettings audioSettings = audioEncodeControl->audioSettings(); + audioSettings.setCodec(audioCodec); + audioEncodeControl->setAudioSettings(audioSettings); + } + + if (needVideo) { + QVideoEncoderSettings videoSettings = videoEncodeControl->videoSettings(); + videoSettings.setCodec(videoCodec); + videoEncodeControl->setVideoSettings(videoSettings); + } + } +} + + +bool QGstreamerRecorderControl::isMuted() const +{ + return m_session->isMuted(); +} + +void QGstreamerRecorderControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QDir QGstreamerRecorderControl::defaultDir() const +{ + QStringList dirCandidates; + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) + dirCandidates << QLatin1String("/home/user/MyDocs"); +#endif + + dirCandidates << QDir::home().filePath("Documents"); + dirCandidates << QDir::home().filePath("My Documents"); + dirCandidates << QDir::homePath(); + dirCandidates << QDir::currentPath(); + dirCandidates << QDir::tempPath(); + + foreach (const QString &path, dirCandidates) { + QDir dir(path); + if (dir.exists() && QFileInfo(path).isWritable()) + return dir; + } + + return QDir(); +} + +QString QGstreamerRecorderControl::generateFileName(const QDir &dir, const QString &ext) const +{ + + int lastClip = 0; + foreach(QString fileName, dir.entryList(QStringList() << QString("clip_*.%1").arg(ext))) { + int imgNumber = fileName.mid(5, fileName.size()-6-ext.length()).toInt(); + lastClip = qMax(lastClip, imgNumber); + } + + QString name = QString("clip_%1.%2").arg(lastClip+1, + 4, //fieldWidth + 10, + QLatin1Char('0')).arg(ext); + + return dir.absoluteFilePath(name); +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h new file mode 100644 index 000000000..c252f1543 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerrecordercontrol.h @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERRECORDERCONTROL_H +#define QGSTREAMERRECORDERCONTROL_H + +#include + +#include +#include "qgstreamercapturesession.h" + +QT_USE_NAMESPACE + +class QGstreamerRecorderControl : public QMediaRecorderControl +{ + Q_OBJECT + +public: + QGstreamerRecorderControl(QGstreamerCaptureSession *session); + virtual ~QGstreamerRecorderControl(); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &sink); + + QMediaRecorder::State state() const; + + qint64 duration() const; + + bool isMuted() const; + + void applySettings(); + +public slots: + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private slots: + void updateState(); + +private: + QDir defaultDir() const; + QString generateFileName(const QDir &dir, const QString &ext) const; + + QUrl m_outputLocation; + QGstreamerCaptureSession *m_session; + QMediaRecorder::State m_state; + bool m_hasPreviewState; +}; + +#endif // QGSTREAMERCAPTURECORNTROL_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp new file mode 100644 index 000000000..70907b2dd --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.cpp @@ -0,0 +1,297 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerv4l2input.h" + +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QT_BEGIN_NAMESPACE +static inline uint qHash(const QSize& key) { return uint(key.width()*256+key.height()); } + +static bool operator<(const QSize &s1, const QSize s2) +{ + return s1.width()*s1.height() < s2.width()*s2.height(); +} +QT_END_NAMESPACE + +QGstreamerV4L2Input::QGstreamerV4L2Input(QObject *parent) + :QObject(parent) +{ +} + +QGstreamerV4L2Input::~QGstreamerV4L2Input() +{ +} + +GstElement *QGstreamerV4L2Input::buildElement() +{ +#ifndef Q_WS_MAEMO_5 + GstElement *camera = gst_element_factory_make("v4l2src", "camera_source"); +#else + GstElement *camera = gst_element_factory_make("v4l2camsrc", "camera_source"); +#endif + if (camera && !m_device.isEmpty() ) + g_object_set(G_OBJECT(camera), "device", m_device.constData(), NULL); + + return camera; +} + +void QGstreamerV4L2Input::setDevice(const QByteArray &newDevice) +{ + if (m_device != newDevice) { + m_device = newDevice; + updateSupportedResolutions(newDevice); + } +} + +void QGstreamerV4L2Input::setDevice(const QString &device) +{ + setDevice(QFile::encodeName(device)); +} + +void QGstreamerV4L2Input::updateSupportedResolutions(const QByteArray &device) +{ + m_frameRates.clear(); + m_resolutions.clear(); + m_ratesByResolution.clear(); + + QSet allResolutions; + QSet allFrameRates; + + QFile f(device); + + if (!f.open(QFile::ReadOnly)) + return; + + int fd = f.handle(); + + //get the list of formats: + QList supportedFormats; + + { + v4l2_fmtdesc fmt; + memset(&fmt, 0, sizeof(v4l2_fmtdesc)); + + fmt.type = V4L2_BUF_TYPE_VIDEO_CAPTURE; + int sanity = 0; + + for (fmt.index = 0;; fmt.index++) { + if (sanity++ > 8) + break; + if( ::ioctl(fd, VIDIOC_ENUM_FMT, &fmt) == -1) { + if(errno == EINVAL) + break; + } + supportedFormats.append(fmt.pixelformat); + } + } + + QList commonSizes; + commonSizes << QSize(128, 96) + < commonRates; + commonRates << 05*1000 << 75*1000 << 10*1000 << 15*1000 << 20*1000 + << 24*1000 << 25*1000 << 30*1000 << 50*1000 << 60*1000; + + + //get the list of resolutions: + + foreach (quint32 format, supportedFormats) { + struct v4l2_frmsizeenum formatSize; + memset(&formatSize, 0, sizeof(formatSize)); + formatSize.pixel_format = format; + + QList sizeList; + + if (0) { + char formatStr[5]; + memcpy(formatStr, &format, 4); + formatStr[4] = 0; + //qDebug() << "trying format" << formatStr; + } + + for (int i=0;;i++) { + formatSize.index = i; + if (ioctl (fd, VIDIOC_ENUM_FRAMESIZES, &formatSize) < 0) + break; + + if (formatSize.type == V4L2_FRMSIZE_TYPE_DISCRETE) { + sizeList.append(QSize(formatSize.discrete.width, formatSize.discrete.height)); + } else { + + foreach (const QSize& candidate, commonSizes) { + if (candidate.width() <= (int)formatSize.stepwise.max_width && + candidate.height() >= (int)formatSize.stepwise.min_width && + candidate.width() % formatSize.stepwise.step_width == 0 && + candidate.height() <= (int)formatSize.stepwise.max_height && + candidate.height() >= (int)formatSize.stepwise.min_height && + candidate.height() % formatSize.stepwise.step_height == 0) { + sizeList.append(candidate); + } + } + + if (!sizeList.contains(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height))) + sizeList.prepend(QSize(formatSize.stepwise.min_width, formatSize.stepwise.min_height)); + + if (!sizeList.contains(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height))) + sizeList.append(QSize(formatSize.stepwise.max_width, formatSize.stepwise.max_height)); + + break; //stepwise values are returned only for index 0 + } + + } + + //and frameRates for each resolution. + + foreach (const QSize &s, sizeList) { + allResolutions.insert(s); + + struct v4l2_frmivalenum formatInterval; + memset(&formatInterval, 0, sizeof(formatInterval)); + formatInterval.pixel_format = format; + formatInterval.width = s.width(); + formatInterval.height = s.height(); + + QList frameRates; //in 1/1000 of fps + + for (int i=0; ; i++) { + formatInterval.index = i; + + if (ioctl(fd, VIDIOC_ENUM_FRAMEINTERVALS, &formatInterval) < 0) + break; + + if (formatInterval.type == V4L2_FRMIVAL_TYPE_DISCRETE) { + //converts seconds to fps*1000 + if (formatInterval.discrete.numerator) + frameRates.append(qRound(formatInterval.discrete.denominator*1000.0 / formatInterval.discrete.numerator)); + } else { + if (formatInterval.stepwise.min.numerator == 0 || + formatInterval.stepwise.max.numerator == 0) { + qWarning() << "received invalid frame interval"; + break; + } + + + int minRate = qRound(formatInterval.stepwise.min.denominator*1000.0 / + formatInterval.stepwise.min.numerator); + + int maxRate = qRound(formatInterval.stepwise.max.denominator*1000.0 / + formatInterval.stepwise.max.numerator); + + + foreach (int candidate, commonRates) { + if (candidate >= minRate && candidate <= maxRate) + frameRates.append(candidate); + } + + if (!frameRates.contains(minRate)) + frameRates.prepend(minRate); + + if (!frameRates.contains(maxRate)) + frameRates.append(maxRate); + + break; //stepwise values are returned only for index 0 + } + } + allFrameRates.unite(frameRates.toSet()); + m_ratesByResolution[s].unite(frameRates.toSet()); + } + } + + f.close(); + + foreach(int rate, allFrameRates) { + m_frameRates.append(rate/1000.0); + } + + qSort(m_frameRates); + + m_resolutions = allResolutions.toList(); + qSort(m_resolutions); + + //qDebug() << "frame rates:" << m_frameRates; + //qDebug() << "resolutions:" << m_resolutions; +} + + +QList QGstreamerV4L2Input::supportedFrameRates(const QSize &frameSize) const +{ + if (frameSize.isEmpty()) + return m_frameRates; + else { + QList res; + foreach(int rate, m_ratesByResolution[frameSize]) { + res.append(rate/1000.0); + } + return res; + } +} + +QList QGstreamerV4L2Input::supportedResolutions(qreal frameRate) const +{ + Q_UNUSED(frameRate); + return m_resolutions; +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h new file mode 100644 index 000000000..5dbdd5453 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamerv4l2input.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERV4L2INPUT_H +#define QGSTREAMERV4L2INPUT_H + +#include +#include +#include +#include +#include "qgstreamercapturesession.h" + +QT_USE_NAMESPACE + +class QGstreamerV4L2Input : public QObject, public QGstreamerVideoInput +{ + Q_OBJECT +public: + QGstreamerV4L2Input(QObject *parent = 0); + virtual ~QGstreamerV4L2Input(); + + GstElement *buildElement(); + + QList supportedFrameRates(const QSize &frameSize = QSize()) const; + QList supportedResolutions(qreal frameRate = -1) const; + + QByteArray device() const; + +public slots: + void setDevice(const QByteArray &device); + void setDevice(const QString &device); + +private: + void updateSupportedResolutions(const QByteArray &device); + + QList m_frameRates; + QList m_resolutions; + + QHash > m_ratesByResolution; + + QByteArray m_device; +}; + +#endif // QGSTREAMERV4L2INPUT_H diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp new file mode 100644 index 000000000..a491f14ae --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.cpp @@ -0,0 +1,331 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideoencode.h" +#include "qgstreamercapturesession.h" +#include "qgstreamermediacontainercontrol.h" + +#include + +#include + +QGstreamerVideoEncode::QGstreamerVideoEncode(QGstreamerCaptureSession *session) + :QVideoEncoderControl(session), m_session(session) +{ + QList codecCandidates; + codecCandidates << "video/h264" << "video/xvid" << "video/mpeg4" << "video/mpeg1" << "video/mpeg2" << "video/theora"; + + m_elementNames["video/h264"] = "x264enc"; + m_elementNames["video/xvid"] = "xvidenc"; + m_elementNames["video/mpeg4"] = "ffenc_mpeg4"; + m_elementNames["video/mpeg1"] = "ffenc_mpeg1video"; + m_elementNames["video/mpeg2"] = "ffenc_mpeg2video"; + m_elementNames["video/theora"] = "theoraenc"; + + m_codecOptions["video/h264"] = QStringList() << "quantizer"; + m_codecOptions["video/xvid"] = QStringList() << "quantizer" << "profile"; + m_codecOptions["video/mpeg4"] = QStringList() << "quantizer"; + m_codecOptions["video/mpeg1"] = QStringList() << "quantizer"; + m_codecOptions["video/mpeg2"] = QStringList() << "quantizer"; + m_codecOptions["video/theora"] = QStringList(); + + foreach( const QByteArray& codecName, codecCandidates ) { + QByteArray elementName = m_elementNames[codecName]; + GstElementFactory *factory = gst_element_factory_find(elementName.constData()); + if (factory) { + m_codecs.append(codecName); + const gchar *descr = gst_element_factory_get_description(factory); + m_codecDescriptions.insert(codecName, QString::fromUtf8(descr)); + + m_streamTypes.insert(codecName, + QGstreamerMediaContainerControl::supportedStreamTypes(factory, GST_PAD_SRC)); + + gst_object_unref(GST_OBJECT(factory)); + } + } + + //if (!m_codecs.isEmpty()) + // m_videoSettings.setCodec(m_codecs[0]); +} + +QGstreamerVideoEncode::~QGstreamerVideoEncode() +{ +} + +QList QGstreamerVideoEncode::supportedResolutions(const QVideoEncoderSettings &, bool *continuous) const +{ + if (continuous) + *continuous = m_session->videoInput() != 0; + + return m_session->videoInput() ? m_session->videoInput()->supportedResolutions() : QList(); +} + +QList< qreal > QGstreamerVideoEncode::supportedFrameRates(const QVideoEncoderSettings &, bool *continuous) const +{ + if (continuous) + *continuous = false; + + return m_session->videoInput() ? m_session->videoInput()->supportedFrameRates() : QList(); +} + +QStringList QGstreamerVideoEncode::supportedVideoCodecs() const +{ + return m_codecs; +} + +QString QGstreamerVideoEncode::videoCodecDescription(const QString &codecName) const +{ + return m_codecDescriptions.value(codecName); +} + +QStringList QGstreamerVideoEncode::supportedEncodingOptions(const QString &codec) const +{ + return m_codecOptions.value(codec); +} + +QVariant QGstreamerVideoEncode::encodingOption(const QString &codec, const QString &name) const +{ + return m_options[codec].value(name); +} + +void QGstreamerVideoEncode::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + m_options[codec][name] = value; +} + +QVideoEncoderSettings QGstreamerVideoEncode::videoSettings() const +{ + return m_videoSettings; +} + +void QGstreamerVideoEncode::setVideoSettings(const QVideoEncoderSettings &settings) +{ + m_videoSettings = settings; +} + +GstElement *QGstreamerVideoEncode::createEncoder() +{ + QString codec = m_videoSettings.codec(); + //qDebug() << "create encoder for video codec" << codec; + GstElement *encoderElement = gst_element_factory_make( m_elementNames.value(codec).constData(), "video-encoder"); + if (!encoderElement) + return 0; + + GstBin *encoderBin = GST_BIN(gst_bin_new("video-encoder-bin")); + + GstElement *capsFilter = gst_element_factory_make("capsfilter", "capsfilter-video"); + gst_bin_add(encoderBin, capsFilter); + + GstElement *colorspace = gst_element_factory_make("ffmpegcolorspace", NULL); + gst_bin_add(encoderBin, colorspace); + gst_bin_add(encoderBin, encoderElement); + + gst_element_link_many(capsFilter, colorspace, encoderElement, NULL); + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(capsFilter, "sink"); + gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("sink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + pad = gst_element_get_static_pad(encoderElement, "src"); + gst_element_add_pad(GST_ELEMENT(encoderBin), gst_ghost_pad_new("src", pad)); + gst_object_unref(GST_OBJECT(pad)); + + if (encoderElement) { + if (m_videoSettings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) { + QtMultimediaKit::EncodingQuality qualityValue = m_videoSettings.quality(); + + if (codec == QLatin1String("video/h264")) { + //constant quantizer mode + g_object_set(G_OBJECT(encoderElement), "pass", 4, NULL); + int qualityTable[] = { + 50, //VeryLow + 35, //Low + 21, //Normal + 15, //High + 8 //VeryHigh + }; + g_object_set(G_OBJECT(encoderElement), "quantizer", qualityTable[qualityValue], NULL); + } else if (codec == QLatin1String("video/xvid")) { + //constant quantizer mode + g_object_set(G_OBJECT(encoderElement), "pass", 3, NULL); + int qualityTable[] = { + 32, //VeryLow + 12, //Low + 5, //Normal + 3, //High + 2 //VeryHigh + }; + int quant = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL); + } else if (codec == QLatin1String("video/mpeg4") || + codec == QLatin1String("video/mpeg1") || + codec == QLatin1String("video/mpeg2") ) { + //constant quantizer mode + g_object_set(G_OBJECT(encoderElement), "pass", 2, NULL); + //quant from 1 to 30, default ~3 + double qualityTable[] = { + 20, //VeryLow + 8.0, //Low + 3.0, //Normal + 2.5, //High + 2.0 //VeryHigh + }; + double quant = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "quantizer", quant, NULL); + } else if (codec == QLatin1String("video/theora")) { + int qualityTable[] = { + 8, //VeryLow + 16, //Low + 32, //Normal + 45, //High + 60 //VeryHigh + }; + //quality from 0 to 63 + int quality = qualityTable[qualityValue]; + g_object_set(G_OBJECT(encoderElement), "quality", quality, NULL); + } + } else { + int bitrate = m_videoSettings.bitRate(); + if (bitrate > 0) { + g_object_set(G_OBJECT(encoderElement), "bitrate", bitrate, NULL); + } + } + + QMap options = m_options.value(codec); + QMapIterator it(options); + while (it.hasNext()) { + it.next(); + QString option = it.key(); + QVariant value = it.value(); + + switch (value.type()) { + case QVariant::Int: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toInt(), NULL); + break; + case QVariant::Bool: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toBool(), NULL); + break; + case QVariant::Double: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toDouble(), NULL); + break; + case QVariant::String: + g_object_set(G_OBJECT(encoderElement), option.toAscii(), value.toString().toUtf8().constData(), NULL); + break; + default: + qWarning() << "unsupported option type:" << option << value; + break; + } + + } + } + + if (!m_videoSettings.resolution().isEmpty() || m_videoSettings.frameRate() > 0.001) { + GstCaps *caps = gst_caps_new_empty(); + QStringList structureTypes; + structureTypes << "video/x-raw-yuv" << "video/x-raw-rgb"; + + foreach(const QString &structureType, structureTypes) { + GstStructure *structure = gst_structure_new(structureType.toAscii().constData(), NULL); + + if (!m_videoSettings.resolution().isEmpty()) { + gst_structure_set(structure, "width", G_TYPE_INT, m_videoSettings.resolution().width(), NULL); + gst_structure_set(structure, "height", G_TYPE_INT, m_videoSettings.resolution().height(), NULL); + } + + if (m_videoSettings.frameRate() > 0.001) { + QPair rate = rateAsRational(); + + //qDebug() << "frame rate:" << num << denum; + + gst_structure_set(structure, "framerate", GST_TYPE_FRACTION, rate.first, rate.second, NULL); + } + + gst_caps_append_structure(caps,structure); + } + + //qDebug() << "set video caps filter:" << gst_caps_to_string(caps); + + g_object_set(G_OBJECT(capsFilter), "caps", caps, NULL); + } + + return GST_ELEMENT(encoderBin); +} + +QPair QGstreamerVideoEncode::rateAsRational() const +{ + qreal frameRate = m_videoSettings.frameRate(); + + if (frameRate > 0.001) { + //convert to rational number + QList denumCandidates; + denumCandidates << 1 << 2 << 3 << 5 << 10 << 1001 << 1000; + + qreal error = 1.0; + int num = 1; + int denum = 1; + + foreach (int curDenum, denumCandidates) { + int curNum = qRound(frameRate*curDenum); + qreal curError = qAbs(qreal(curNum)/curDenum - frameRate); + + if (curError < error) { + error = curError; + num = curNum; + denum = curDenum; + } + + if (curError < 1e-8) + break; + } + + return QPair(num,denum); + } + + return QPair(); +} + + +QSet QGstreamerVideoEncode::supportedStreamTypes(const QString &codecName) const +{ + return m_streamTypes.value(codecName); +} diff --git a/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h new file mode 100644 index 000000000..c042efd25 --- /dev/null +++ b/src/plugins/gstreamer/mediacapture/qgstreamervideoencode.h @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOENCODE_H +#define QGSTREAMERVIDEOENCODE_H + +#include +class QGstreamerCaptureSession; + +#include +#include +#include + +#include + +QT_USE_NAMESPACE + +class QGstreamerVideoEncode : public QVideoEncoderControl +{ + Q_OBJECT +public: + QGstreamerVideoEncode(QGstreamerCaptureSession *session); + virtual ~QGstreamerVideoEncode(); + + QList supportedResolutions(const QVideoEncoderSettings &settings = QVideoEncoderSettings(), + bool *continuous = 0) const; + + QList< qreal > supportedFrameRates(const QVideoEncoderSettings &settings = QVideoEncoderSettings(), + bool *continuous = 0) const; + + QPair rateAsRational() const; + + QStringList supportedVideoCodecs() const; + QString videoCodecDescription(const QString &codecName) const; + + QVideoEncoderSettings videoSettings() const; + void setVideoSettings(const QVideoEncoderSettings &settings); + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + + GstElement *createEncoder(); + + QSet supportedStreamTypes(const QString &codecName) const; + +private: + QGstreamerCaptureSession *m_session; + + QStringList m_codecs; + QMap m_codecDescriptions; + QMap m_elementNames; + QMap m_codecOptions; + + QVideoEncoderSettings m_videoSettings; + QMap > m_options; + QMap > m_streamTypes; +}; + +#endif diff --git a/src/plugins/gstreamer/mediaplayer/mediaplayer.pri b/src/plugins/gstreamer/mediaplayer/mediaplayer.pri new file mode 100644 index 000000000..9045a80dd --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/mediaplayer.pri @@ -0,0 +1,30 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_GSTREAMER_PLAYER + +contains(gstreamer-appsrc_enabled, yes) { + HEADERS += $$PWD/qgstappsrc.h + SOURCES += $$PWD/qgstappsrc.cpp + + DEFINES += HAVE_GST_APPSRC + + LIBS += -lgstapp-0.10 +} + +HEADERS += \ + $$PWD/qgstreamerplayercontrol.h \ + $$PWD/qgstreamerplayerservice.h \ + $$PWD/qgstreamerplayersession.h \ + $$PWD/qgstreamerstreamscontrol.h \ + $$PWD/qgstreamermetadataprovider.h \ + $$PWD/playerresourcepolicy.h + +SOURCES += \ + $$PWD/qgstreamerplayercontrol.cpp \ + $$PWD/qgstreamerplayerservice.cpp \ + $$PWD/qgstreamerplayersession.cpp \ + $$PWD/qgstreamerstreamscontrol.cpp \ + $$PWD/qgstreamermetadataprovider.cpp \ + $$PWD/playerresourcepolicy.cpp + + diff --git a/src/plugins/gstreamer/mediaplayer/playerresourcepolicy.cpp b/src/plugins/gstreamer/mediaplayer/playerresourcepolicy.cpp new file mode 100644 index 000000000..b2cf3498d --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/playerresourcepolicy.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "playerresourcepolicy.h" + +#ifdef Q_WS_MAEMO_6 +#define HAVE_RESOURCE_POLICY +#endif + +//#define DEBUG_RESOURCE_POLICY +#include + +#ifdef HAVE_RESOURCE_POLICY +#include +#include +#include +#endif + +PlayerResourcePolicy::PlayerResourcePolicy(QObject *parent) : + QObject(parent), + m_videoEnabled(true), + m_resourceSet(0), + m_status(PlayerResourcePolicy::Initial) +{ +#ifdef HAVE_RESOURCE_POLICY + m_resourceSet = new ResourcePolicy::ResourceSet("player", this); + m_resourceSet->setAlwaysReply(); + + ResourcePolicy::AudioResource *audioResource = new ResourcePolicy::AudioResource("player"); + audioResource->setProcessID(QCoreApplication::applicationPid()); + audioResource->setStreamTag("media.name", "*"); + m_resourceSet->addResourceObject(audioResource); + + m_resourceSet->addResource(ResourcePolicy::VideoPlaybackType); + m_resourceSet->update(); + + connect(m_resourceSet, SIGNAL(resourcesGranted(const QList)), + this, SLOT(handleResourcesGranted())); + connect(m_resourceSet, SIGNAL(resourcesDenied()), + this, SLOT(handleResourcesDenied())); + connect(m_resourceSet, SIGNAL(lostResources()), + this, SLOT(handleResourcesLost())); + connect(m_resourceSet, SIGNAL(resourcesReleasedByManager()), + this, SLOT(handleResourcesLost())); +#endif +} + +PlayerResourcePolicy::~PlayerResourcePolicy() +{ +} + +bool PlayerResourcePolicy::isVideoEnabled() const +{ + return m_videoEnabled; +} + +void PlayerResourcePolicy::setVideoEnabled(bool enabled) +{ + if (m_videoEnabled != enabled) { + m_videoEnabled = enabled; + +#ifdef HAVE_RESOURCE_POLICY + if (enabled) + m_resourceSet->addResource(ResourcePolicy::VideoPlaybackType); + else + m_resourceSet->deleteResource(ResourcePolicy::VideoPlaybackType); + + m_resourceSet->update(); +#endif + } +} + +void PlayerResourcePolicy::acquire() +{ +#ifdef HAVE_RESOURCE_POLICY + +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO << "Acquire resource"; +#endif + m_status = RequestedResource; + m_resourceSet->acquire(); +#else + m_status = GrantedResource; +#endif +} + +void PlayerResourcePolicy::release() +{ +#ifdef HAVE_RESOURCE_POLICY + +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO << "Release resource"; +#endif + + m_resourceSet->release(); +#endif + m_status = Initial; + +} + +bool PlayerResourcePolicy::isGranted() const +{ + return m_status == GrantedResource; +} + +bool PlayerResourcePolicy::isRequested() const +{ + return m_status == RequestedResource; +} + +void PlayerResourcePolicy::handleResourcesGranted() +{ + m_status = GrantedResource; +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO << "Resource granted"; +#endif + emit resourcesGranted(); +} + +void PlayerResourcePolicy::handleResourcesDenied() +{ + m_status = Initial; +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO << "Resource denied"; +#endif + emit resourcesDenied(); +} + +void PlayerResourcePolicy::handleResourcesLost() +{ +#ifdef DEBUG_RESOURCE_POLICY + qDebug() << Q_FUNC_INFO << "Resource lost"; +#endif + if (m_status != Initial) { + m_status = Initial; + emit resourcesLost(); + } + +#ifdef HAVE_RESOURCE_POLICY + m_resourceSet->release(); +#endif +} diff --git a/src/plugins/gstreamer/mediaplayer/playerresourcepolicy.h b/src/plugins/gstreamer/mediaplayer/playerresourcepolicy.h new file mode 100644 index 000000000..fecf5fa80 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/playerresourcepolicy.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef PLAYERRESOURCEPOLICY_H +#define PLAYERRESOURCEPOLICY_H + +#include + +namespace ResourcePolicy { +class ResourceSet; +}; + +class PlayerResourcePolicy : public QObject +{ + Q_OBJECT +public: + PlayerResourcePolicy(QObject *parent = 0); + ~PlayerResourcePolicy(); + + bool isVideoEnabled() const; + bool isGranted() const; + bool isRequested() const; + +Q_SIGNALS: + void resourcesDenied(); + void resourcesGranted(); + void resourcesLost(); + +public Q_SLOTS: + void acquire(); + void release(); + + void setVideoEnabled(bool enabled); + +private Q_SLOTS: + void handleResourcesGranted(); + void handleResourcesDenied(); + void handleResourcesLost(); + +private: + enum ResourceStatus { + Initial = 0, + RequestedResource, + GrantedResource + }; + + bool m_videoEnabled; + ResourcePolicy::ResourceSet *m_resourceSet; + ResourceStatus m_status; +}; + +#endif diff --git a/src/plugins/gstreamer/mediaplayer/qgstappsrc.cpp b/src/plugins/gstreamer/mediaplayer/qgstappsrc.cpp new file mode 100644 index 000000000..cf814c462 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstappsrc.cpp @@ -0,0 +1,224 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qgstappsrc.h" +#include + +QGstAppSrc::QGstAppSrc(QObject *parent) + :QObject(parent) + ,m_stream(0) + ,m_appSrc(0) + ,m_sequential(false) + ,m_maxBytes(0) + ,m_setup(false) + ,m_dataRequestSize(-1) + ,m_dataRequested(false) + ,m_enoughData(false) + ,m_forceData(false) +{ +} + +QGstAppSrc::~QGstAppSrc() +{ + if (m_appSrc) + gst_object_unref(G_OBJECT(m_appSrc)); +} + +bool QGstAppSrc::setup(GstElement* appsrc) +{ + if (m_setup || m_stream == 0 || appsrc == 0) + return false; + + m_appSrc = GST_APP_SRC(appsrc); + m_callbacks.need_data = &QGstAppSrc::on_need_data; + m_callbacks.enough_data = &QGstAppSrc::on_enough_data; + m_callbacks.seek_data = &QGstAppSrc::on_seek_data; + gst_app_src_set_callbacks(m_appSrc, (GstAppSrcCallbacks*)&m_callbacks, this, (GDestroyNotify)&QGstAppSrc::destroy_notify); + + g_object_get(G_OBJECT(m_appSrc), "max-bytes", &m_maxBytes, NULL); + + if (m_sequential) + m_streamType = GST_APP_STREAM_TYPE_STREAM; + else + m_streamType = GST_APP_STREAM_TYPE_RANDOM_ACCESS; + gst_app_src_set_stream_type(m_appSrc, m_streamType); + gst_app_src_set_size(m_appSrc, (m_sequential) ? -1 : m_stream->size()); + + return m_setup = true; +} + +void QGstAppSrc::setStream(QIODevice *stream) +{ + if (stream == 0) + return; + if (m_stream) { + disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); + disconnect(m_stream, SIGNAL(destroyed()), this, SLOT(streamDestroyed())); + } + if (m_appSrc) + gst_object_unref(G_OBJECT(m_appSrc)); + + m_dataRequestSize = -1; + m_dataRequested = false; + m_enoughData = false; + m_forceData = false; + m_maxBytes = 0; + + m_appSrc = 0; + m_stream = stream; + connect(m_stream, SIGNAL(destroyed()), SLOT(streamDestroyed())); + connect(m_stream, SIGNAL(readyRead()), this, SLOT(onDataReady())); + m_sequential = m_stream->isSequential(); + m_setup = false; +} + +QIODevice *QGstAppSrc::stream() const +{ + return m_stream; +} + +GstAppSrc *QGstAppSrc::element() +{ + return m_appSrc; +} + +void QGstAppSrc::onDataReady() +{ + if (!m_enoughData) { + m_dataRequested = true; + pushDataToAppSrc(); + } +} + +void QGstAppSrc::streamDestroyed() +{ + if (sender() == m_stream) { + m_stream = 0; + sendEOS(); + } +} + +void QGstAppSrc::pushDataToAppSrc() +{ + if (!isStreamValid() || !m_setup) + return; + + if (m_dataRequested && !m_enoughData) { + qint64 size; + if (m_dataRequestSize == (unsigned int)-1) + size = qMin(m_stream->bytesAvailable(), queueSize()); + else + size = qMin(m_stream->bytesAvailable(), (qint64)m_dataRequestSize); + void *data = g_malloc(size); + GstBuffer* buffer = gst_app_buffer_new(data, size, g_free, data); + buffer->offset = m_stream->pos(); + qint64 bytesRead = m_stream->read((char*)GST_BUFFER_DATA(buffer), size); + buffer->offset_end = buffer->offset + bytesRead - 1; + + if (bytesRead > 0) { + m_dataRequested = false; + m_enoughData = false; + GstFlowReturn ret = gst_app_src_push_buffer (GST_APP_SRC (element()), buffer); + if (ret == GST_FLOW_ERROR) { + qWarning()<<"appsrc: push buffer error"; + } else if (ret == GST_FLOW_WRONG_STATE) { + qWarning()<<"appsrc: push buffer wrong state"; + } else if (ret == GST_FLOW_RESEND) { + qWarning()<<"appsrc: push buffer resend"; + } + } + } else if (m_stream->atEnd()) { + sendEOS(); + } +} + +bool QGstAppSrc::doSeek(qint64 value) +{ + if (isStreamValid()) + return stream()->seek(value); + return false; +} + + +gboolean QGstAppSrc::on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata) +{ + QGstAppSrc *self = reinterpret_cast(userdata); + if (self && self->isStreamValid()) { + if (!self->stream()->isSequential()) + QMetaObject::invokeMethod(self, "doSeek", Qt::AutoConnection, Q_ARG(qint64, arg0)); + } + else + return false; + + return true; +} + +void QGstAppSrc::on_enough_data(GstAppSrc *element, gpointer userdata) +{ + QGstAppSrc *self = reinterpret_cast(userdata); + if (self) + self->enoughData() = true; +} + +void QGstAppSrc::on_need_data(GstAppSrc *element, guint arg0, gpointer userdata) +{ + QGstAppSrc *self = reinterpret_cast(userdata); + if (self) { + self->dataRequested() = true; + self->enoughData() = false; + self->dataRequestSize()= arg0; + QMetaObject::invokeMethod(self, "pushDataToAppSrc", Qt::AutoConnection); + } +} + +void QGstAppSrc::destroy_notify(gpointer data) +{ + Q_UNUSED(data); +} + +void QGstAppSrc::sendEOS() +{ + gst_app_src_end_of_stream(GST_APP_SRC(m_appSrc)); + if (isStreamValid() && !stream()->isSequential()) + stream()->reset(); +} diff --git a/src/plugins/gstreamer/mediaplayer/qgstappsrc.h b/src/plugins/gstreamer/mediaplayer/qgstappsrc.h new file mode 100644 index 000000000..17ee2af72 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstappsrc.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTAPPSRC_H +#define QGSTAPPSRC_H + +#include +#include + +#include +#include +#include + +class QGstAppSrc : public QObject +{ + Q_OBJECT +public: + QGstAppSrc(QObject *parent = 0); + ~QGstAppSrc(); + + bool setup(GstElement *); + bool isReady() const { return m_setup; } + + void setStream(QIODevice *); + QIODevice *stream() const; + + GstAppSrc *element(); + + qint64 queueSize() const { return m_maxBytes; } + + bool& enoughData() { return m_enoughData; } + bool& dataRequested() { return m_dataRequested; } + unsigned int& dataRequestSize() { return m_dataRequestSize; } + + bool isStreamValid() const + { + return m_stream != 0 && + m_stream->isOpen(); + } + +private slots: + void pushDataToAppSrc(); + bool doSeek(qint64); + void onDataReady(); + + void streamDestroyed(); +private: + static gboolean on_seek_data(GstAppSrc *element, guint64 arg0, gpointer userdata); + static void on_enough_data(GstAppSrc *element, gpointer userdata); + static void on_need_data(GstAppSrc *element, uint arg0, gpointer userdata); + static void destroy_notify(gpointer data); + + void sendEOS(); + + QIODevice *m_stream; + GstAppSrc *m_appSrc; + bool m_sequential; + GstAppStreamType m_streamType; + GstAppSrcCallbacks m_callbacks; + qint64 m_maxBytes; + bool m_setup; + unsigned int m_dataRequestSize; + bool m_dataRequested; + bool m_enoughData; + bool m_forceData; +}; + +#endif diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp new file mode 100644 index 000000000..cdd6d89b4 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.cpp @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamermetadataprovider.h" +#include "qgstreamerplayersession.h" +#include + +#include + +struct QGstreamerMetaDataKeyLookup +{ + QtMultimediaKit::MetaData key; + const char *token; +}; + +static const QGstreamerMetaDataKeyLookup qt_gstreamerMetaDataKeys[] = +{ + { QtMultimediaKit::Title, GST_TAG_TITLE }, + //{ QtMultimediaKit::SubTitle, 0 }, + //{ QtMultimediaKit::Author, 0 }, + { QtMultimediaKit::Comment, GST_TAG_COMMENT }, + { QtMultimediaKit::Description, GST_TAG_DESCRIPTION }, + //{ QtMultimediaKit::Category, 0 }, + { QtMultimediaKit::Genre, GST_TAG_GENRE }, + { QtMultimediaKit::Year, "year" }, + //{ QtMultimediaKit::UserRating, 0 }, + + { QtMultimediaKit::Language, GST_TAG_LANGUAGE_CODE }, + + { QtMultimediaKit::Publisher, GST_TAG_ORGANIZATION }, + { QtMultimediaKit::Copyright, GST_TAG_COPYRIGHT }, + //{ QtMultimediaKit::ParentalRating, 0 }, + //{ QtMultimediaKit::RatingOrganisation, 0 }, + + // Media + //{ QtMultimediaKit::Size, 0 }, + //{ QtMultimediaKit::MediaType, 0 }, + { QtMultimediaKit::Duration, GST_TAG_DURATION }, + + // Audio + { QtMultimediaKit::AudioBitRate, GST_TAG_BITRATE }, + { QtMultimediaKit::AudioCodec, GST_TAG_AUDIO_CODEC }, + //{ QtMultimediaKit::ChannelCount, 0 }, + //{ QtMultimediaKit::SampleRate, 0 }, + + // Music + { QtMultimediaKit::AlbumTitle, GST_TAG_ALBUM }, + { QtMultimediaKit::AlbumArtist, GST_TAG_ARTIST}, + { QtMultimediaKit::ContributingArtist, GST_TAG_PERFORMER }, +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 19) + { QtMultimediaKit::Composer, GST_TAG_COMPOSER }, +#endif + //{ QtMultimediaKit::Conductor, 0 }, + //{ QtMultimediaKit::Lyrics, 0 }, + //{ QtMultimediaKit::Mood, 0 }, + { QtMultimediaKit::TrackNumber, GST_TAG_TRACK_NUMBER }, + + //{ QtMultimediaKit::CoverArtUrlSmall, 0 }, + //{ QtMultimediaKit::CoverArtUrlLarge, 0 }, + + // Image/Video + { QtMultimediaKit::Resolution, "resolution" }, + { QtMultimediaKit::PixelAspectRatio, "pixel-aspect-ratio" }, + + // Video + //{ QtMultimediaKit::VideoFrameRate, 0 }, + //{ QtMultimediaKit::VideoBitRate, 0 }, + { QtMultimediaKit::VideoCodec, GST_TAG_VIDEO_CODEC }, + + //{ QtMultimediaKit::PosterUrl, 0 }, + + // Movie + //{ QtMultimediaKit::ChapterNumber, 0 }, + //{ QtMultimediaKit::Director, 0 }, + { QtMultimediaKit::LeadPerformer, GST_TAG_PERFORMER }, + //{ QtMultimediaKit::Writer, 0 }, + + // Photos + //{ QtMultimediaKit::CameraManufacturer, 0 }, + //{ QtMultimediaKit::CameraModel, 0 }, + //{ QtMultimediaKit::Event, 0 }, + //{ QtMultimediaKit::Subject, 0 } +}; + +QGstreamerMetaDataProvider::QGstreamerMetaDataProvider(QGstreamerPlayerSession *session, QObject *parent) + :QMetaDataReaderControl(parent), m_session(session) +{ + connect(m_session, SIGNAL(tagsChanged()), SLOT(updateTags())); +} + +QGstreamerMetaDataProvider::~QGstreamerMetaDataProvider() +{ +} + +bool QGstreamerMetaDataProvider::isMetaDataAvailable() const +{ + return !m_session->tags().isEmpty(); +} + +bool QGstreamerMetaDataProvider::isWritable() const +{ + return false; +} + +QVariant QGstreamerMetaDataProvider::metaData(QtMultimediaKit::MetaData key) const +{ + static const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + + for (int i = 0; i < count; ++i) { + if (qt_gstreamerMetaDataKeys[i].key == key) { + return m_session->tags().value(QByteArray(qt_gstreamerMetaDataKeys[i].token)); + } + } + return QVariant(); +} + +QList QGstreamerMetaDataProvider::availableMetaData() const +{ + static QMap keysMap; + if (keysMap.isEmpty()) { + const int count = sizeof(qt_gstreamerMetaDataKeys) / sizeof(QGstreamerMetaDataKeyLookup); + for (int i = 0; i < count; ++i) { + keysMap[QByteArray(qt_gstreamerMetaDataKeys[i].token)] = qt_gstreamerMetaDataKeys[i].key; + } + } + + QList res; + foreach (const QByteArray &key, m_session->tags().keys()) { + QtMultimediaKit::MetaData tag = keysMap.value(key, QtMultimediaKit::MetaData(-1)); + if (tag != -1) + res.append(tag); + } + + return res; +} + +QVariant QGstreamerMetaDataProvider::extendedMetaData(const QString &key) const +{ + return m_session->tags().value(key.toLatin1()); +} + +QStringList QGstreamerMetaDataProvider::availableExtendedMetaData() const +{ + QStringList res; + foreach (const QByteArray &key, m_session->tags().keys()) + res.append(QString(key)); + + return res; +} + +void QGstreamerMetaDataProvider::updateTags() +{ + emit metaDataChanged(); +} diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h new file mode 100644 index 000000000..fa0c0243f --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamermetadataprovider.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMETADATAPROVIDER_H +#define QGSTREAMERMETADATAPROVIDER_H + +#include + +QT_USE_NAMESPACE + +class QGstreamerPlayerSession; + +class QGstreamerMetaDataProvider : public QMetaDataReaderControl +{ + Q_OBJECT +public: + QGstreamerMetaDataProvider( QGstreamerPlayerSession *session, QObject *parent ); + virtual ~QGstreamerMetaDataProvider(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const ; + QStringList availableExtendedMetaData() const; + +private slots: + void updateTags(); + +private: + QGstreamerPlayerSession *m_session; +}; + +#endif // QGSTREAMERMETADATAPROVIDER_H diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp new file mode 100644 index 000000000..f36dd08a3 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.cpp @@ -0,0 +1,748 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerplayercontrol.h" +#include "qgstreamerplayersession.h" +#include "playerresourcepolicy.h" + +#include + + +#include +#include +#include +#include + +#include +#include +#include +#include + +//#define DEBUG_PLAYBIN + +QGstreamerPlayerControl::QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent) + : QMediaPlayerControl(parent) + , m_ownStream(false) + , m_session(session) + , m_state(QMediaPlayer::StoppedState) + , m_mediaStatus(QMediaPlayer::NoMedia) + , m_bufferProgress(-1) + , m_seekToStartPending(false) + , m_pendingSeekPosition(-1) + , m_stream(0) + , m_fifoNotifier(0) + , m_fifoCanWrite(false) + , m_bufferSize(0) + , m_bufferOffset(0) +{ + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + m_resources = new PlayerResourcePolicy(this); + + connect(m_session, SIGNAL(positionChanged(qint64)), + this, SIGNAL(positionChanged(qint64))); + connect(m_session, SIGNAL(durationChanged(qint64)), + this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedStateChanged(bool)), + this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(volumeChanged(int)), + this, SIGNAL(volumeChanged(int))); + connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SLOT(updateSessionState(QMediaPlayer::State))); + connect(m_session,SIGNAL(bufferingProgressChanged(int)), + this, SLOT(setBufferProgress(int))); + connect(m_session, SIGNAL(playbackFinished()), + this, SLOT(processEOS())); + connect(m_session, SIGNAL(audioAvailableChanged(bool)), + this, SIGNAL(audioAvailableChanged(bool))); + connect(m_session, SIGNAL(videoAvailableChanged(bool)), + this, SIGNAL(videoAvailableChanged(bool))); + connect(m_session, SIGNAL(seekableChanged(bool)), + this, SIGNAL(seekableChanged(bool))); + connect(m_session, SIGNAL(error(int,QString)), + this, SIGNAL(error(int,QString))); + connect(m_session, SIGNAL(invalidMedia()), + this, SLOT(handleInvalidMedia())); + connect(m_session, SIGNAL(playbackRateChanged(qreal)), + this, SIGNAL(playbackRateChanged(qreal))); + connect(m_session, SIGNAL(seekableChanged(bool)), + this, SLOT(applyPendingSeek(bool))); + + connect(m_resources, SIGNAL(resourcesGranted()), SLOT(handleResourcesGranted())); + connect(m_resources, SIGNAL(resourcesDenied()), SLOT(handleResourcesLost())); + connect(m_resources, SIGNAL(resourcesLost()), SLOT(handleResourcesLost())); +} + +QGstreamerPlayerControl::~QGstreamerPlayerControl() +{ + if (m_fifoFd[0] >= 0) { + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + } +} + +qint64 QGstreamerPlayerControl::position() const +{ + return m_seekToStartPending ? 0 : m_session->position(); +} + +qint64 QGstreamerPlayerControl::duration() const +{ + return m_session->duration(); +} + +QMediaPlayer::State QGstreamerPlayerControl::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus QGstreamerPlayerControl::mediaStatus() const +{ + return m_mediaStatus; +} + +int QGstreamerPlayerControl::bufferStatus() const +{ + if (m_bufferProgress == -1) { + return m_session->state() == QMediaPlayer::StoppedState ? 0 : 100; + } else + return m_bufferProgress; +} + +int QGstreamerPlayerControl::volume() const +{ + return m_session->volume(); +} + +bool QGstreamerPlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool QGstreamerPlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange QGstreamerPlayerControl::availablePlaybackRanges() const +{ + return m_session->availablePlaybackRanges(); +} + +qreal QGstreamerPlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void QGstreamerPlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void QGstreamerPlayerControl::setPosition(qint64 pos) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << pos/1000.0; +#endif + + pushState(); + + if (m_mediaStatus == QMediaPlayer::EndOfMedia) { + m_mediaStatus = QMediaPlayer::LoadedMedia; + m_seekToStartPending = true; + } + + if (m_session->isSeekable() && m_session->seek(pos)) { + m_seekToStartPending = false; + m_pendingSeekPosition = -1; + } else { + m_pendingSeekPosition = pos; + } + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::play() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + + playOrPause(QMediaPlayer::PlayingState); +} + +void QGstreamerPlayerControl::pause() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + + playOrPause(QMediaPlayer::PausedState); +} + +void QGstreamerPlayerControl::playOrPause(QMediaPlayer::State newState) +{ + if (m_mediaStatus == QMediaPlayer::NoMedia) + return; + + pushState(); +#ifdef Q_WS_MAEMO_6 + //this is a work around for the gstreamer bug, + //should be remove once it get fixed + if (newState == QMediaPlayer::PlayingState && m_mediaStatus == QMediaPlayer::InvalidMedia) { + setMedia(m_currentResource, m_stream); + } +#endif + + if (m_mediaStatus == QMediaPlayer::EndOfMedia) { + m_mediaStatus = QMediaPlayer::BufferedMedia; + m_seekToStartPending = true; + } + + if (!m_resources->isGranted() && !m_resources->isRequested()) + m_resources->acquire(); + + if (m_resources->isGranted()) { + if (m_seekToStartPending) { + m_session->pause(); + if (!m_session->seek(0)) { + m_bufferProgress = -1; + m_session->stop(); + m_mediaStatus = QMediaPlayer::LoadingMedia; + } + m_seekToStartPending = false; + } + + bool ok = false; + + if (newState == QMediaPlayer::PlayingState) + ok = m_session->play(); + else + ok = m_session->pause(); + + if (!ok) + newState = QMediaPlayer::StoppedState; + } + + if (m_mediaStatus == QMediaPlayer::InvalidMedia) + m_mediaStatus = QMediaPlayer::LoadingMedia; + + m_state = newState; + + if (m_mediaStatus == QMediaPlayer::EndOfMedia || m_mediaStatus == QMediaPlayer::LoadedMedia) { + if (m_bufferProgress == -1 || m_bufferProgress == 100) + m_mediaStatus = QMediaPlayer::BufferedMedia; + else + m_mediaStatus = QMediaPlayer::BufferingMedia; + } + + popAndNotifyState(); + + emit positionChanged(position()); +} + +void QGstreamerPlayerControl::stop() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + + pushState(); + + if (m_state != QMediaPlayer::StoppedState) { + m_state = QMediaPlayer::StoppedState; + if (m_resources->isGranted()) + m_session->pause(); + + if (m_mediaStatus != QMediaPlayer::EndOfMedia) { + m_seekToStartPending = true; + emit positionChanged(position()); + } + } + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void QGstreamerPlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QMediaContent QGstreamerPlayerControl::media() const +{ + return m_currentResource; +} + +const QIODevice *QGstreamerPlayerControl::mediaStream() const +{ + return m_stream; +} + +void QGstreamerPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO; +#endif + + pushState(); + + m_state = QMediaPlayer::StoppedState; + QMediaContent oldMedia = m_currentResource; + m_pendingSeekPosition = -1; + + if (!content.isNull() || stream) { + if (!m_resources->isRequested() && !m_resources->isGranted()) + m_resources->acquire(); + + if (!m_resources->isGranted()) { + m_currentResource = content; + m_stream = stream; + + m_state = QMediaPlayer::StoppedState; + m_mediaStatus = QMediaPlayer::LoadingMedia; + if (m_currentResource != oldMedia) + emit mediaChanged(m_currentResource); + popAndNotifyState(); + return; + } + } else { + m_resources->release(); + } + + m_session->stop(); + + bool userStreamValid = false; + + if (m_bufferProgress != -1) { + m_bufferProgress = -1; + emit bufferStatusChanged(0); + } + + if (m_stream) { +#if !defined(HAVE_GST_APPSRC) + closeFifo(); + + disconnect(m_stream, SIGNAL(readyRead()), this, SLOT(writeFifo())); +#endif + + if (m_ownStream) + delete m_stream; + m_stream = 0; + m_ownStream = false; + } + + // If the canonical URL refers to a Qt resource, open with QFile and use + // the stream playback capability to play. + if (stream == 0 && content.canonicalUrl().scheme() == QLatin1String("qrc")) { + stream = new QFile(QLatin1Char(':') + content.canonicalUrl().path(), this); + if (!stream->open(QIODevice::ReadOnly)) { + delete stream; + m_mediaStatus = QMediaPlayer::InvalidMedia; + m_currentResource = content; + emit mediaChanged(m_currentResource); + emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid Qt resource")); + if (m_state != QMediaPlayer::PlayingState) + m_resources->release(); + popAndNotifyState(); + return; + } + m_ownStream = true; + } + + m_currentResource = content; + m_stream = stream; + m_seekToStartPending = false; + + QNetworkRequest request; + + if (m_stream) { +#if !defined(HAVE_GST_APPSRC) + if (m_stream->isReadable() && openFifo()) { + request = QNetworkRequest(QUrl(QString(QLatin1String("fd://%1")).arg(m_fifoFd[0]))); + } +#else + userStreamValid = stream->isOpen() && m_stream->isReadable(); + request = content.canonicalRequest(); +#endif + } else if (!content.isNull()) { + request = content.canonicalRequest(); + } + +#if !defined(HAVE_GST_APPSRC) + m_session->loadFromUri(request); +#else + if (m_stream) { + if (userStreamValid){ + m_session->loadFromStream(request, m_stream); + } else { + m_mediaStatus = QMediaPlayer::InvalidMedia; + emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid user stream")); + if (m_state != QMediaPlayer::PlayingState) + m_resources->release(); + popAndNotifyState(); + return; + } + } else + m_session->loadFromUri(request); +#endif + +#if !defined(HAVE_GST_APPSRC) + if (m_fifoFd[1] >= 0) { + m_fifoCanWrite = true; + + writeFifo(); + } +#endif + +#if defined(HAVE_GST_APPSRC) + if (!request.url().isEmpty() || userStreamValid) { +#else + if (!request.url().isEmpty()) { +#endif + m_mediaStatus = QMediaPlayer::LoadingMedia; + m_session->pause(); + } else { + m_mediaStatus = QMediaPlayer::NoMedia; + setBufferProgress(0); + } + + if (m_currentResource != oldMedia) + emit mediaChanged(m_currentResource); + + emit positionChanged(position()); + + if (content.isNull() && !stream) + m_resources->release(); + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::setVideoOutput(QObject *output) +{ + m_session->setVideoRenderer(output); +} + +bool QGstreamerPlayerControl::isAudioAvailable() const +{ + return m_session->isAudioAvailable(); +} + +bool QGstreamerPlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + +void QGstreamerPlayerControl::updateSessionState(QMediaPlayer::State state) +{ + pushState(); + + if (state == QMediaPlayer::StoppedState) + m_state = QMediaPlayer::StoppedState; + + updateMediaStatus(); + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::updateMediaStatus() +{ + pushState(); + QMediaPlayer::MediaStatus oldStatus = m_mediaStatus; + + switch (m_session->state()) { + case QMediaPlayer::StoppedState: + if (m_currentResource.isNull()) + m_mediaStatus = QMediaPlayer::NoMedia; + else if (oldStatus != QMediaPlayer::InvalidMedia) + m_mediaStatus = QMediaPlayer::LoadingMedia; + break; + + case QMediaPlayer::PlayingState: + case QMediaPlayer::PausedState: + if (m_state == QMediaPlayer::StoppedState) { + m_mediaStatus = QMediaPlayer::LoadedMedia; + } else { + if (m_bufferProgress == -1 || m_bufferProgress == 100) + m_mediaStatus = QMediaPlayer::BufferedMedia; + else + m_mediaStatus = QMediaPlayer::StalledMedia; + } + break; + } + + if (m_state == QMediaPlayer::PlayingState && !m_resources->isGranted()) + m_mediaStatus = QMediaPlayer::StalledMedia; + + //EndOfMedia status should be kept, until reset by pause, play or setMedia + if (oldStatus == QMediaPlayer::EndOfMedia) + m_mediaStatus = QMediaPlayer::EndOfMedia; + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::processEOS() +{ + pushState(); + m_mediaStatus = QMediaPlayer::EndOfMedia; + emit positionChanged(position()); + stop(); + popAndNotifyState(); +} + +void QGstreamerPlayerControl::setBufferProgress(int progress) +{ + if (m_bufferProgress == progress || m_mediaStatus == QMediaPlayer::NoMedia) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << progress; +#endif + m_bufferProgress = progress; + + if (m_resources->isGranted()) { + if (m_state == QMediaPlayer::PlayingState && + m_bufferProgress == 100 && + m_session->state() != QMediaPlayer::PlayingState) + m_session->play(); + + if (m_bufferProgress < 100 && + (m_session->state() == QMediaPlayer::PlayingState || + m_session->pendingState() == QMediaPlayer::PlayingState)) + m_session->pause(); + } + + updateMediaStatus(); + + emit bufferStatusChanged(m_bufferProgress); +} + +void QGstreamerPlayerControl::writeFifo() +{ + if (m_fifoCanWrite) { + qint64 bytesToRead = qMin( + m_stream->bytesAvailable(), PIPE_BUF - m_bufferSize); + + if (bytesToRead > 0) { + int bytesRead = m_stream->read(&m_buffer[m_bufferOffset + m_bufferSize], bytesToRead); + + if (bytesRead > 0) + m_bufferSize += bytesRead; + } + + if (m_bufferSize > 0) { + int bytesWritten = ::write(m_fifoFd[1], &m_buffer[m_bufferOffset], size_t(m_bufferSize)); + + if (bytesWritten > 0) { + m_bufferOffset += bytesWritten; + m_bufferSize -= bytesWritten; + + if (m_bufferSize == 0) + m_bufferOffset = 0; + } else if (errno == EAGAIN) { + m_fifoCanWrite = false; + } else { + closeFifo(); + } + } + } + + m_fifoNotifier->setEnabled(m_stream->bytesAvailable() > 0); +} + +void QGstreamerPlayerControl::fifoReadyWrite(int socket) +{ + if (socket == m_fifoFd[1]) { + m_fifoCanWrite = true; + + writeFifo(); + } +} + +bool QGstreamerPlayerControl::openFifo() +{ + Q_ASSERT(m_fifoFd[0] < 0); + Q_ASSERT(m_fifoFd[1] < 0); + + if (::pipe(m_fifoFd) == 0) { + int flags = ::fcntl(m_fifoFd[1], F_GETFD); + + if (::fcntl(m_fifoFd[1], F_SETFD, flags | O_NONBLOCK) >= 0) { + m_fifoNotifier = new QSocketNotifier(m_fifoFd[1], QSocketNotifier::Write); + + connect(m_fifoNotifier, SIGNAL(activated(int)), this, SLOT(fifoReadyWrite(int))); + + return true; + } else { + qWarning("Failed to make pipe non blocking %d", errno); + + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + return false; + } + } else { + qWarning("Failed to create pipe %d", errno); + + return false; + } +} + +void QGstreamerPlayerControl::closeFifo() +{ + if (m_fifoFd[0] >= 0) { + delete m_fifoNotifier; + m_fifoNotifier = 0; + + ::close(m_fifoFd[0]); + ::close(m_fifoFd[1]); + m_fifoFd[0] = -1; + m_fifoFd[1] = -1; + + m_fifoCanWrite = false; + + m_bufferSize = 0; + m_bufferOffset = 0; + } +} + +void QGstreamerPlayerControl::applyPendingSeek(bool isSeekable) +{ + if (isSeekable && m_pendingSeekPosition != -1) + setPosition(m_pendingSeekPosition); +} + +void QGstreamerPlayerControl::handleInvalidMedia() +{ + pushState(); + m_mediaStatus = QMediaPlayer::InvalidMedia; + m_state = QMediaPlayer::StoppedState; + popAndNotifyState(); +} + +void QGstreamerPlayerControl::handleResourcesGranted() +{ + pushState(); + + QMediaPlayer::State state = m_state; + + //preserve m_pendingSeekPosition, it's reset on setMedia + qint64 pos = m_pendingSeekPosition; + setMedia(m_currentResource, m_stream); + + if (pos != -1) + setPosition(pos); + + if (state != QMediaPlayer::StoppedState) + playOrPause(state); + else + updateMediaStatus(); + + popAndNotifyState(); +} + +void QGstreamerPlayerControl::handleResourcesLost() +{ + //on resource lost the pipeline should be stopped + //player status is changed to paused + + pushState(); + QMediaPlayer::State oldState = m_state; + + qint64 pos = m_session->position(); + m_session->stop(); + m_pendingSeekPosition = pos; + + if (oldState != QMediaPlayer::StoppedState ) + m_state = QMediaPlayer::PausedState; + + popAndNotifyState(); +} + +bool QGstreamerPlayerControl::isMediaDownloadEnabled() const +{ + return m_session->property("mediaDownloadEnabled").toBool(); +} + +void QGstreamerPlayerControl::setMediaDownloadEnabled(bool enabled) +{ + m_session->setProperty("mediaDownloadEnabled", enabled); +} + +void QGstreamerPlayerControl::pushState() +{ + m_stateStack.push(m_state); + m_mediaStatusStack.push(m_mediaStatus); +} + +void QGstreamerPlayerControl::popAndNotifyState() +{ + Q_ASSERT(!m_stateStack.isEmpty()); + + QMediaPlayer::State oldState = m_stateStack.pop(); + QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatusStack.pop(); + + if (m_stateStack.isEmpty()) { + if (m_state != oldState) { +#ifdef DEBUG_PLAYBIN + qDebug() << "State changed:" << m_state; +#endif + emit stateChanged(m_state); + } + + if (m_mediaStatus != oldMediaStatus) { +#ifdef DEBUG_PLAYBIN + qDebug() << "Media status changed:" << m_mediaStatus; +#endif + emit mediaStatusChanged(m_mediaStatus); + } + } +} diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h new file mode 100644 index 000000000..5a53a0713 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayercontrol.h @@ -0,0 +1,157 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERCONTROL_H +#define QGSTREAMERPLAYERCONTROL_H + +#include +#include + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QSocketNotifier; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class QGstreamerPlayerSession; +class QGstreamerPlayerService; +class PlayerResourcePolicy; + +class QGstreamerPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT + Q_PROPERTY(bool mediaDownloadEnabled READ isMediaDownloadEnabled WRITE setMediaDownloadEnabled) + +public: + QGstreamerPlayerControl(QGstreamerPlayerSession *session, QObject *parent = 0); + ~QGstreamerPlayerControl(); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + void setVideoOutput(QObject *output); + + bool isSeekable() const; + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent&, QIODevice *); + + bool isMediaDownloadEnabled() const; + void setMediaDownloadEnabled(bool enabled); + +public Q_SLOTS: + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + +private Q_SLOTS: + void writeFifo(); + void fifoReadyWrite(int socket); + + void updateSessionState(QMediaPlayer::State state); + void updateMediaStatus(); + void processEOS(); + void setBufferProgress(int progress); + void applyPendingSeek(bool isSeekable); + + void handleInvalidMedia(); + + void handleResourcesGranted(); + void handleResourcesLost(); + +private: + bool openFifo(); + void closeFifo(); + void playOrPause(QMediaPlayer::State state); + + void pushState(); + void popAndNotifyState(); + + bool m_ownStream; + QGstreamerPlayerSession *m_session; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QStack m_stateStack; + QStack m_mediaStatusStack; + + int m_bufferProgress; + bool m_seekToStartPending; + qint64 m_pendingSeekPosition; + QMediaContent m_currentResource; + QIODevice *m_stream; + QSocketNotifier *m_fifoNotifier; + int m_fifoFd[2]; + bool m_fifoCanWrite; + int m_bufferSize; + int m_bufferOffset; + char m_buffer[PIPE_BUF]; + + PlayerResourcePolicy *m_resources; +}; + +#endif diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp new file mode 100644 index 000000000..6976c18ae --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.cpp @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "qgstreamerplayerservice.h" +#include "qgstreamerplayercontrol.h" +#include "qgstreamerplayersession.h" +#include "qgstreamermetadataprovider.h" + +#include "qgstreamervideooverlay.h" +#include "qgstreamervideowindow.h" +#include "qgstreamervideorenderer.h" + +#if defined(Q_WS_MAEMO_6) && defined(__arm__) +#include "qgstreamergltexturerenderer.h" +#endif + +#include "qgstreamervideowidget.h" +#include "qgstreamerstreamscontrol.h" + +#include +#include + +QGstreamerPlayerService::QGstreamerPlayerService(QObject *parent): + QMediaService(parent), + m_videoOutput(0), + m_videoRenderer(0), + m_videoWindow(0), + m_videoWidget(0) +{ + m_session = new QGstreamerPlayerSession(this); + m_control = new QGstreamerPlayerControl(m_session, this); + m_metaData = new QGstreamerMetaDataProvider(m_session, this); + m_streamsControl = new QGstreamerStreamsControl(m_session,this); + +#if defined(Q_WS_MAEMO_6) && defined(__arm__) + m_videoRenderer = new QGstreamerGLTextureRenderer(this); +#else + m_videoRenderer = new QGstreamerVideoRenderer(this); +#endif + +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) + +#ifdef Q_WS_MAEMO_6 + m_videoWindow = new QGstreamerVideoWindow(this, "omapxvsink"); +#else + m_videoWindow = new QGstreamerVideoOverlay(this); +#endif + + m_videoWidget = new QGstreamerVideoWidgetControl(this); +#endif +} + +QGstreamerPlayerService::~QGstreamerPlayerService() +{ +} + +QMediaControl *QGstreamerPlayerService::requestControl(const char *name) +{ + if (qstrcmp(name,QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name,QMetaDataReaderControl_iid) == 0) + return m_metaData; + + if (qstrcmp(name,QMediaStreamsControl_iid) == 0) + return m_streamsControl; + + if (!m_videoOutput) { + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) + m_videoOutput = m_videoWidget; + else if (qstrcmp(name, QVideoRendererControl_iid) == 0) + m_videoOutput = m_videoRenderer; + else if (qstrcmp(name, QVideoWindowControl_iid) == 0) + m_videoOutput = m_videoWindow; + + if (m_videoOutput) { + m_control->setVideoOutput(m_videoOutput); + return m_videoOutput; + } + } + + return 0; +} + +void QGstreamerPlayerService::releaseControl(QMediaControl *control) +{ + if (control == m_videoOutput) { + m_videoOutput = 0; + m_control->setVideoOutput(0); + } +} + diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h new file mode 100644 index 000000000..92ea9ffa4 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayerservice.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSERVICE_H +#define QGSTREAMERPLAYERSERVICE_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +QT_END_NAMESPACE + +class QGstreamerMetaData; +class QGstreamerPlayerControl; +class QGstreamerPlayerSession; +class QGstreamerMetaDataProvider; +class QGstreamerStreamsControl; +class QGstreamerVideoRenderer; +class QGstreamerVideoOverlay; +class QGstreamerVideoWidgetControl; + +QT_USE_NAMESPACE + +class QGstreamerPlayerService : public QMediaService +{ + Q_OBJECT +public: + QGstreamerPlayerService(QObject *parent = 0); + ~QGstreamerPlayerService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *control); + +private: + QGstreamerPlayerControl *m_control; + QGstreamerPlayerSession *m_session; + QGstreamerMetaDataProvider *m_metaData; + QGstreamerStreamsControl *m_streamsControl; + + QMediaControl *m_videoOutput; + QMediaControl *m_videoRenderer; + QMediaControl *m_videoWindow; + QMediaControl *m_videoWidget; +}; + +#endif diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp new file mode 100644 index 000000000..d12bb2697 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.cpp @@ -0,0 +1,1537 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerplayersession.h" +#include "qgstreamerbushelper.h" + +#include "qgstreamervideorendererinterface.h" +#include "gstvideoconnector.h" +#include "qgstutils.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + +#if defined(Q_WS_MAEMO_5) || defined(Q_WS_MAEMO_6) || (GST_VERSION_MICRO > 20) +#define USE_PLAYBIN2 +#endif + +//#define DEBUG_PLAYBIN +//#define DEBUG_VO_BIN_DUMP + +typedef enum { + GST_PLAY_FLAG_VIDEO = 0x00000001, + GST_PLAY_FLAG_AUDIO = 0x00000002, + GST_PLAY_FLAG_TEXT = 0x00000004, + GST_PLAY_FLAG_VIS = 0x00000008, + GST_PLAY_FLAG_SOFT_VOLUME = 0x00000010, + GST_PLAY_FLAG_NATIVE_AUDIO = 0x00000020, + GST_PLAY_FLAG_NATIVE_VIDEO = 0x00000040, + GST_PLAY_FLAG_DOWNLOAD = 0x00000080, + GST_PLAY_FLAG_BUFFERING = 0x000000100 +} GstPlayFlags; + +QGstreamerPlayerSession::QGstreamerPlayerSession(QObject *parent) + :QObject(parent), + m_state(QMediaPlayer::StoppedState), + m_pendingState(QMediaPlayer::StoppedState), + m_busHelper(0), + m_playbin(0), + m_usePlaybin2(false), + m_usingColorspaceElement(false), + m_videoSink(0), + m_pendingVideoSink(0), + m_nullVideoSink(0), + m_bus(0), + m_videoOutput(0), + m_renderer(0), + m_haveQueueElement(false), +#if defined(HAVE_GST_APPSRC) + m_appSrc(0), +#endif + m_volume(100), + m_playbackRate(1.0), + m_muted(false), + m_audioAvailable(false), + m_videoAvailable(false), + m_seekable(false), + m_lastPosition(0), + m_duration(-1), + m_durationQueries(0), + m_everPlayed(false) , + m_sourceType(UnknownSrc) +{ +#ifdef USE_PLAYBIN2 + m_playbin = gst_element_factory_make("playbin2", NULL); +#endif + + if (m_playbin) { + m_usePlaybin2 = true; + + //GST_PLAY_FLAG_NATIVE_VIDEO omits configuration of ffmpegcolorspace and videoscale, + //since those elements are included in the video output bin when necessary. +#ifdef Q_WS_MAEMO_6 + int flags = GST_PLAY_FLAG_VIDEO | GST_PLAY_FLAG_AUDIO | + GST_PLAY_FLAG_NATIVE_VIDEO | GST_PLAY_FLAG_NATIVE_AUDIO; +#else + int flags = 0; + g_object_get(G_OBJECT(m_playbin), "flags", &flags, NULL); + flags |= GST_PLAY_FLAG_NATIVE_VIDEO; +#endif + g_object_set(G_OBJECT(m_playbin), "flags", flags, NULL); + } else { + m_usePlaybin2 = false; + m_playbin = gst_element_factory_make("playbin", NULL); + } + + m_videoOutputBin = gst_bin_new("video-output-bin"); + gst_object_ref(GST_OBJECT(m_videoOutputBin)); + + m_videoIdentity = GST_ELEMENT(g_object_new(gst_video_connector_get_type(), 0)); + g_signal_connect(G_OBJECT(m_videoIdentity), "connection-failed", G_CALLBACK(insertColorSpaceElement), (gpointer)this); + m_colorSpace = gst_element_factory_make("ffmpegcolorspace", "ffmpegcolorspace-vo"); + gst_object_ref(GST_OBJECT(m_colorSpace)); + + m_nullVideoSink = gst_element_factory_make("fakesink", NULL); + g_object_set(G_OBJECT(m_nullVideoSink), "sync", true, NULL); + gst_object_ref(GST_OBJECT(m_nullVideoSink)); + gst_bin_add_many(GST_BIN(m_videoOutputBin), m_videoIdentity, m_nullVideoSink, NULL); + gst_element_link(m_videoIdentity, m_nullVideoSink); + + m_videoSink = m_nullVideoSink; + + // add ghostpads + GstPad *pad = gst_element_get_static_pad(m_videoIdentity,"sink"); + gst_element_add_pad(GST_ELEMENT(m_videoOutputBin), gst_ghost_pad_new("videosink", pad)); + gst_object_unref(GST_OBJECT(pad)); + + if (m_playbin != 0) { + // Sort out messages + m_bus = gst_element_get_bus(m_playbin); + m_busHelper = new QGstreamerBusHelper(m_bus, this); + connect(m_busHelper, SIGNAL(message(QGstreamerMessage)), SLOT(busMessage(QGstreamerMessage))); + m_busHelper->installSyncEventFilter(this); + + g_object_set(G_OBJECT(m_playbin), "video-sink", m_videoOutputBin, NULL); + + g_signal_connect(G_OBJECT(m_playbin), "notify::source", G_CALLBACK(playbinNotifySource), this); + g_signal_connect(G_OBJECT(m_playbin), "element-added", G_CALLBACK(handleElementAdded), this); + + // Initial volume + double volume = 1.0; + g_object_get(G_OBJECT(m_playbin), "volume", &volume, NULL); + m_volume = int(volume*100); + + g_signal_connect(G_OBJECT(m_playbin), "notify::volume", G_CALLBACK(handleVolumeChange), this); + if (m_usePlaybin2) + g_signal_connect(G_OBJECT(m_playbin), "notify::mute", G_CALLBACK(handleMutedChange), this); + } +} + +QGstreamerPlayerSession::~QGstreamerPlayerSession() +{ + if (m_playbin) { + stop(); + + delete m_busHelper; + gst_object_unref(GST_OBJECT(m_bus)); + gst_object_unref(GST_OBJECT(m_playbin)); + gst_object_unref(GST_OBJECT(m_colorSpace)); + gst_object_unref(GST_OBJECT(m_nullVideoSink)); + gst_object_unref(GST_OBJECT(m_videoOutputBin)); + } +} + +#if defined(HAVE_GST_APPSRC) +void QGstreamerPlayerSession::configureAppSrcElement(GObject* object, GObject *orig, GParamSpec *pspec, QGstreamerPlayerSession* self) +{ + if (self->appsrc()->isReady()) + return; + + GstElement *appsrc; + g_object_get(orig, "source", &appsrc, NULL); + + if (!self->appsrc()->setup(appsrc)) + qWarning()<<"Could not setup appsrc element"; +} +#endif + +void QGstreamerPlayerSession::loadFromStream(const QNetworkRequest &request, QIODevice *appSrcStream) +{ +#if defined(HAVE_GST_APPSRC) + m_request = request; + m_duration = -1; + m_lastPosition = 0; + m_haveQueueElement = false; + + if (m_appSrc) + m_appSrc->deleteLater(); + m_appSrc = new QGstAppSrc(this); + m_appSrc->setStream(appSrcStream); + + if (m_playbin) { + m_tags.clear(); + emit tagsChanged(); + + g_signal_connect(G_OBJECT(m_playbin), "deep-notify::source", (GCallback) &QGstreamerPlayerSession::configureAppSrcElement, (gpointer)this); + g_object_set(G_OBJECT(m_playbin), "uri", "appsrc://", NULL); + + if (!m_streamTypes.isEmpty()) { + m_streamProperties.clear(); + m_streamTypes.clear(); + + emit streamsChanged(); + } + } +#endif +} + +void QGstreamerPlayerSession::loadFromUri(const QNetworkRequest &request) +{ + m_request = request; + m_duration = -1; + m_lastPosition = 0; + m_haveQueueElement = false; + + if (m_playbin) { + m_tags.clear(); + emit tagsChanged(); + + g_object_set(G_OBJECT(m_playbin), "uri", m_request.url().toEncoded().constData(), NULL); + + if (!m_streamTypes.isEmpty()) { + m_streamProperties.clear(); + m_streamTypes.clear(); + + emit streamsChanged(); + } + } +} + +qint64 QGstreamerPlayerSession::duration() const +{ + return m_duration; +} + +qint64 QGstreamerPlayerSession::position() const +{ + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + + if ( m_playbin && gst_element_query_position(m_playbin, &format, &position)) + m_lastPosition = position / 1000000; + + return m_lastPosition; +} + +qreal QGstreamerPlayerSession::playbackRate() const +{ + return m_playbackRate; +} + +void QGstreamerPlayerSession::setPlaybackRate(qreal rate) +{ + if (!qFuzzyCompare(m_playbackRate, rate)) { + m_playbackRate = rate; + if (m_playbin) { + gst_element_seek(m_playbin, rate, GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH), + GST_SEEK_TYPE_NONE,0, + GST_SEEK_TYPE_NONE,0 ); + } + emit playbackRateChanged(m_playbackRate); + } +} + +QMediaTimeRange QGstreamerPlayerSession::availablePlaybackRanges() const +{ + QMediaTimeRange ranges; +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 31) + //GST_FORMAT_TIME would be more appropriate, but unfortunately it's not supported. + //with GST_FORMAT_PERCENT media is treated as encoded with constant bitrate. + GstQuery* query = gst_query_new_buffering(GST_FORMAT_PERCENT); + + if (gst_element_query(m_playbin, query)) { + for (guint index = 0; index < gst_query_get_n_buffering_ranges(query); index++) { + gint64 rangeStart = 0; + gint64 rangeStop = 0; + + //This query should return values in GST_FORMAT_PERCENT_MAX range, + //but queue2 returns values in 0..100 range instead + if (gst_query_parse_nth_buffering_range(query, index, &rangeStart, &rangeStop)) + ranges.addInterval(rangeStart * duration() / 100, + rangeStop * duration() / 100); + } + } + + gst_query_unref(query); +#endif + + //without queue2 element in pipeline all the media is considered available + if (ranges.isEmpty() && duration() > 0 && !m_haveQueueElement) + ranges.addInterval(0, duration()); + +#ifdef DEBUG_PLAYBIN + qDebug() << ranges; +#endif + + return ranges; +} + +int QGstreamerPlayerSession::activeStream(QMediaStreamsControl::StreamType streamType) const +{ + int streamNumber = -1; + if (m_playbin) { + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_object_get(G_OBJECT(m_playbin), "current-audio", streamNumber, NULL); + break; + case QMediaStreamsControl::VideoStream: + g_object_get(G_OBJECT(m_playbin), "current-video", streamNumber, NULL); + break; + case QMediaStreamsControl::SubPictureStream: + g_object_get(G_OBJECT(m_playbin), "current-text", streamNumber, NULL); + break; + default: + break; + } + } + + if (m_usePlaybin2 && streamNumber >= 0) + streamNumber += m_playbin2StreamOffset.value(streamType,0); + + return streamNumber; +} + +void QGstreamerPlayerSession::setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber) +{ + + if (m_usePlaybin2 && streamNumber >= 0) + streamNumber -= m_playbin2StreamOffset.value(streamType,0); + + if (m_playbin) { + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_object_set(G_OBJECT(m_playbin), "current-audio", &streamNumber, NULL); + break; + case QMediaStreamsControl::VideoStream: + g_object_set(G_OBJECT(m_playbin), "current-video", &streamNumber, NULL); + break; + case QMediaStreamsControl::SubPictureStream: + g_object_set(G_OBJECT(m_playbin), "current-text", &streamNumber, NULL); + break; + default: + break; + } + } +} + + +bool QGstreamerPlayerSession::isBuffering() const +{ + return false; +} + +int QGstreamerPlayerSession::bufferingProgress() const +{ + return 0; +} + +int QGstreamerPlayerSession::volume() const +{ + return m_volume; +} + +bool QGstreamerPlayerSession::isMuted() const +{ + return m_muted; +} + +bool QGstreamerPlayerSession::isAudioAvailable() const +{ + return m_audioAvailable; +} + +static void block_pad_cb(GstPad *pad, gboolean blocked, gpointer user_data) +{ + Q_UNUSED(pad); +#ifdef DEBUG_PLAYBIN + qDebug() << "block_pad_cb, blocked:" << blocked; +#endif + + if (blocked && user_data) { + QGstreamerPlayerSession *session = reinterpret_cast(user_data); + QMetaObject::invokeMethod(session, "finishVideoOutputChange", Qt::QueuedConnection); + } +} + +void QGstreamerPlayerSession::updateVideoRenderer() +{ +#ifdef DEBUG_PLAYBIN + qDebug() << "Video sink has chaged, reload video output"; +#endif + + if (m_videoOutput) + setVideoRenderer(m_videoOutput); +} + +void QGstreamerPlayerSession::setVideoRenderer(QObject *videoOutput) +{ + if (m_videoOutput != videoOutput) { + if (m_videoOutput) { + disconnect(m_videoOutput, SIGNAL(sinkChanged()), + this, SLOT(updateVideoRenderer())); + disconnect(m_videoOutput, SIGNAL(readyChanged(bool)), + this, SLOT(updateVideoRenderer())); + } + + if (videoOutput) { + connect(videoOutput, SIGNAL(sinkChanged()), + this, SLOT(updateVideoRenderer())); + connect(videoOutput, SIGNAL(readyChanged(bool)), + this, SLOT(updateVideoRenderer())); + } + + m_videoOutput = videoOutput; + } + + QGstreamerVideoRendererInterface* renderer = qobject_cast(videoOutput); + + m_renderer = renderer; + +#ifdef DEBUG_VO_BIN_DUMP + _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), + GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/), + "playbin_set"); +#endif + + GstElement *videoSink = 0; + if (m_renderer && m_renderer->isReady()) + videoSink = m_renderer->videoSink(); + + if (!videoSink) + videoSink = m_nullVideoSink; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Set video output:" << videoOutput; + qDebug() << "Current sink:" << (m_videoSink ? GST_ELEMENT_NAME(m_videoSink) : "") << m_videoSink + << "pending:" << (m_pendingVideoSink ? GST_ELEMENT_NAME(m_pendingVideoSink) : "") << m_pendingVideoSink + << "new sink:" << (videoSink ? GST_ELEMENT_NAME(videoSink) : "") << videoSink; +#endif + + if (m_pendingVideoSink == videoSink || + (m_pendingVideoSink == 0 && m_videoSink == videoSink)) { +#ifdef DEBUG_PLAYBIN + qDebug() << "Video sink has not changed, skip video output reconfiguration"; +#endif + return; + } + +#ifdef DEBUG_PLAYBIN + qDebug() << "Reconfigure video output"; +#endif + + if (m_state == QMediaPlayer::StoppedState) { +#ifdef DEBUG_PLAYBIN + qDebug() << "The pipeline has not started yet, pending state:" << m_pendingState; +#endif + //the pipeline has not started yet + m_pendingVideoSink = 0; + gst_element_set_state(m_videoSink, GST_STATE_NULL); + gst_element_set_state(m_playbin, GST_STATE_NULL); + + if (m_usingColorspaceElement) { + gst_element_unlink(m_colorSpace, m_videoSink); + gst_bin_remove(GST_BIN(m_videoOutputBin), m_colorSpace); + } else { + gst_element_unlink(m_videoIdentity, m_videoSink); + } + + gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink); + + m_videoSink = videoSink; + + gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); + + m_usingColorspaceElement = false; + bool linked = gst_element_link(m_videoIdentity, m_videoSink); + if (!linked) { + m_usingColorspaceElement = true; +#ifdef DEBUG_PLAYBIN + qDebug() << "Failed to connect video output, inserting the colorspace element."; +#endif + gst_bin_add(GST_BIN(m_videoOutputBin), m_colorSpace); + linked = gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoSink, NULL); + } + + switch (m_pendingState) { + case QMediaPlayer::PausedState: + gst_element_set_state(m_playbin, GST_STATE_PAUSED); + break; + case QMediaPlayer::PlayingState: + gst_element_set_state(m_playbin, GST_STATE_PLAYING); + break; + default: + break; + } + } else { + if (m_pendingVideoSink) { +#ifdef DEBUG_PLAYBIN + qDebug() << "already waiting for pad to be blocked, just change the pending sink"; +#endif + m_pendingVideoSink = videoSink; + return; + } + + m_pendingVideoSink = videoSink; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Blocking the video output pad..."; +#endif + + //block pads, async to avoid locking in paused state + GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); + gst_pad_set_blocked_async(srcPad, true, &block_pad_cb, this); + gst_object_unref(GST_OBJECT(srcPad)); + + //Unpause the sink to avoid waiting until the buffer is processed + //while the sink is paused. The pad will be blocked as soon as the current + //buffer is processed. + if (m_state == QMediaPlayer::PausedState) { +#ifdef DEBUG_PLAYBIN + qDebug() << "Starting video output to avoid blocking in paused state..."; +#endif + gst_element_set_state(m_videoSink, GST_STATE_PLAYING); + } + } +} + +void QGstreamerPlayerSession::finishVideoOutputChange() +{ + if (!m_pendingVideoSink) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << "finishVideoOutputChange" << m_pendingVideoSink; +#endif + + GstPad *srcPad = gst_element_get_static_pad(m_videoIdentity, "src"); + + if (!gst_pad_is_blocked(srcPad)) { + //pad is not blocked, it's possible to swap outputs only in the null state + qWarning() << "Pad is not blocked yet, could not switch video sink"; + GstState identityElementState = GST_STATE_NULL; + gst_element_get_state(m_videoIdentity, &identityElementState, NULL, GST_CLOCK_TIME_NONE); + if (identityElementState != GST_STATE_NULL) { + gst_object_unref(GST_OBJECT(srcPad)); + return; //can't change vo yet, received async call from the previous change + } + } + + if (m_pendingVideoSink == m_videoSink) { + //video output was change back to the current one, + //no need to torment the pipeline, just unblock the pad + if (gst_pad_is_blocked(srcPad)) + gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); + + m_pendingVideoSink = 0; + gst_object_unref(GST_OBJECT(srcPad)); + return; + } + + if (m_usingColorspaceElement) { + gst_element_set_state(m_colorSpace, GST_STATE_NULL); + gst_element_set_state(m_videoSink, GST_STATE_NULL); + + gst_element_unlink(m_colorSpace, m_videoSink); + gst_bin_remove(GST_BIN(m_videoOutputBin), m_colorSpace); + } else { + gst_element_set_state(m_videoSink, GST_STATE_NULL); + gst_element_unlink(m_videoIdentity, m_videoSink); + } + + gst_bin_remove(GST_BIN(m_videoOutputBin), m_videoSink); + + m_videoSink = m_pendingVideoSink; + m_pendingVideoSink = 0; + + gst_bin_add(GST_BIN(m_videoOutputBin), m_videoSink); + + m_usingColorspaceElement = false; + bool linked = gst_element_link(m_videoIdentity, m_videoSink); + if (!linked) { + m_usingColorspaceElement = true; +#ifdef DEBUG_PLAYBIN + qDebug() << "Failed to connect video output, inserting the colorspace element."; +#endif + gst_bin_add(GST_BIN(m_videoOutputBin), m_colorSpace); + linked = gst_element_link_many(m_videoIdentity, m_colorSpace, m_videoSink, NULL); + } + + if (!linked) + qWarning() << "Linking video output element failed"; + +#ifdef DEBUG_PLAYBIN + qDebug() << "notify the video connector it has to emit a new segment message..."; +#endif + //it's necessary to send a new segment event just before + //the first buffer pushed to the new sink + g_signal_emit_by_name(m_videoIdentity, + "resend-new-segment", + true //emit connection-failed signal + //to have a chance to insert colorspace element + ); + + + GstState state; + + switch (m_pendingState) { + case QMediaPlayer::StoppedState: + state = GST_STATE_NULL; + break; + case QMediaPlayer::PausedState: + state = GST_STATE_PAUSED; + break; + case QMediaPlayer::PlayingState: + state = GST_STATE_PLAYING; + break; + } + + if (m_usingColorspaceElement) + gst_element_set_state(m_colorSpace, state); + + gst_element_set_state(m_videoSink, state); + + // Set state change that was deferred due the video output + // change being pending + gst_element_set_state(m_playbin, state); + + //don't have to wait here, it will unblock eventually + if (gst_pad_is_blocked(srcPad)) + gst_pad_set_blocked_async(srcPad, false, &block_pad_cb, 0); + gst_object_unref(GST_OBJECT(srcPad)); + +#ifdef DEBUG_VO_BIN_DUMP + _gst_debug_bin_to_dot_file_with_ts(GST_BIN(m_playbin), + GstDebugGraphDetails(GST_DEBUG_GRAPH_SHOW_ALL /* GST_DEBUG_GRAPH_SHOW_MEDIA_TYPE | GST_DEBUG_GRAPH_SHOW_NON_DEFAULT_PARAMS | GST_DEBUG_GRAPH_SHOW_STATES*/), + "playbin_finish"); +#endif +} + +void QGstreamerPlayerSession::insertColorSpaceElement(GstElement *element, gpointer data) +{ + Q_UNUSED(element); + QGstreamerPlayerSession* session = reinterpret_cast(data); + + if (session->m_usingColorspaceElement) + return; + session->m_usingColorspaceElement = true; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Failed to connect video output, inserting the colorspace elemnt."; + qDebug() << "notify the video connector it has to emit a new segment message..."; +#endif + //it's necessary to send a new segment event just before + //the first buffer pushed to the new sink + g_signal_emit_by_name(session->m_videoIdentity, + "resend-new-segment", + false // don't emit connection-failed signal + ); + + gst_element_unlink(session->m_videoIdentity, session->m_videoSink); + gst_bin_add(GST_BIN(session->m_videoOutputBin), session->m_colorSpace); + gst_element_link_many(session->m_videoIdentity, session->m_colorSpace, session->m_videoSink, NULL); + + GstState state; + + switch (session->m_pendingState) { + case QMediaPlayer::StoppedState: + state = GST_STATE_NULL; + break; + case QMediaPlayer::PausedState: + state = GST_STATE_PAUSED; + break; + case QMediaPlayer::PlayingState: + state = GST_STATE_PLAYING; + break; + } + + gst_element_set_state(session->m_colorSpace, state); +} + + +bool QGstreamerPlayerSession::isVideoAvailable() const +{ + return m_videoAvailable; +} + +bool QGstreamerPlayerSession::isSeekable() const +{ + return m_seekable; +} + +bool QGstreamerPlayerSession::play() +{ + m_everPlayed = false; + if (m_playbin) { + m_pendingState = QMediaPlayer::PlayingState; + if (gst_element_set_state(m_playbin, GST_STATE_PLAYING) == GST_STATE_CHANGE_FAILURE) { + qWarning() << "GStreamer; Unable to play -" << m_request.url().toString(); + m_pendingState = m_state = QMediaPlayer::StoppedState; + + emit stateChanged(m_state); + } else + return true; + } + + return false; +} + +bool QGstreamerPlayerSession::pause() +{ + if (m_playbin) { + m_pendingState = QMediaPlayer::PausedState; + if (m_pendingVideoSink != 0) + return true; + + if (gst_element_set_state(m_playbin, GST_STATE_PAUSED) == GST_STATE_CHANGE_FAILURE) { + qWarning() << "GStreamer; Unable to pause -" << m_request.url().toString(); + m_pendingState = m_state = QMediaPlayer::StoppedState; + + emit stateChanged(m_state); + } else { + return true; + } + } + + return false; +} + +void QGstreamerPlayerSession::stop() +{ + m_everPlayed = false; + if (m_playbin) { + if (m_renderer) + m_renderer->stopRenderer(); + + gst_element_set_state(m_playbin, GST_STATE_NULL); + + m_lastPosition = 0; + QMediaPlayer::State oldState = m_state; + m_pendingState = m_state = QMediaPlayer::StoppedState; + + finishVideoOutputChange(); + + //we have to do it here, since gstreamer will not emit bus messages any more + setSeekable(false); + if (oldState != m_state) + emit stateChanged(m_state); + } +} + +bool QGstreamerPlayerSession::seek(qint64 ms) +{ + //seek locks when the video output sink is changing and pad is blocked + if (m_playbin && !m_pendingVideoSink && m_state != QMediaPlayer::StoppedState) { + ms = qMax(ms,qint64(0)); + gint64 position = ms * 1000000; + bool isSeeking = gst_element_seek(m_playbin, + m_playbackRate, + GST_FORMAT_TIME, + GstSeekFlags(GST_SEEK_FLAG_ACCURATE | GST_SEEK_FLAG_FLUSH), + GST_SEEK_TYPE_SET, + position, + GST_SEEK_TYPE_NONE, + 0); + if (isSeeking) + m_lastPosition = ms; + + return isSeeking; + } + + return false; +} + +void QGstreamerPlayerSession::setVolume(int volume) +{ + if (m_volume != volume) { + m_volume = volume; + + if (m_playbin) { + //playbin2 allows to set volume and muted independently, + //with playbin1 it's necessary to keep volume at 0 while muted + if (!m_muted || m_usePlaybin2) + g_object_set(G_OBJECT(m_playbin), "volume", m_volume/100.0, NULL); + } + + emit volumeChanged(m_volume); + } +} + +void QGstreamerPlayerSession::setMuted(bool muted) +{ + if (m_muted != muted) { + m_muted = muted; + + if (m_usePlaybin2) + g_object_set(G_OBJECT(m_playbin), "mute", m_muted, NULL); + else + g_object_set(G_OBJECT(m_playbin), "volume", (m_muted ? 0 : m_volume/100.0), NULL); + + emit mutedStateChanged(m_muted); + } +} + + +void QGstreamerPlayerSession::setSeekable(bool seekable) +{ + if (seekable != m_seekable) { + m_seekable = seekable; + emit seekableChanged(m_seekable); + } +} + +bool QGstreamerPlayerSession::processSyncMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm && GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT) { + if (m_renderer) { + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) + m_renderer->handleSyncMessage(gm); + + if (gst_structure_has_name(gm->structure, "prepare-xwindow-id")) { + m_renderer->precessNewStream(); + return true; + } + } + } + + return false; +} + +void QGstreamerPlayerSession::busMessage(const QGstreamerMessage &message) +{ + GstMessage* gm = message.rawMessage(); + + if (gm) { + //tag message comes from elements inside playbin, not from playbin itself + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_TAG) { + //qDebug() << "tag message"; + GstTagList *tag_list; + gst_message_parse_tag(gm, &tag_list); + m_tags.unite(QGstUtils::gstTagListToMap(tag_list)); + + //qDebug() << m_tags; + + emit tagsChanged(); + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_DURATION) { + updateDuration(); + } + +#ifdef DEBUG_PLAYBIN + if (m_sourceType == MMSSrc && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { + qDebug() << "Message from MMSSrc: " << GST_MESSAGE_TYPE(gm); + } +#endif + + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_BUFFERING) { + int progress = 0; + gst_message_parse_buffering(gm, &progress); + emit bufferingProgressChanged(progress); + } + + bool handlePlaybin2 = false; + if (GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_playbin)) { + switch (GST_MESSAGE_TYPE(gm)) { + case GST_MESSAGE_STATE_CHANGED: + { + GstState oldState; + GstState newState; + GstState pending; + + gst_message_parse_state_changed(gm, &oldState, &newState, &pending); + +#ifdef DEBUG_PLAYBIN + QStringList states; + states << "GST_STATE_VOID_PENDING" << "GST_STATE_NULL" << "GST_STATE_READY" << "GST_STATE_PAUSED" << "GST_STATE_PLAYING"; + + qDebug() << QString("state changed: old: %1 new: %2 pending: %3") \ + .arg(states[oldState]) \ + .arg(states[newState]) \ + .arg(states[pending]); +#endif + + switch (newState) { + case GST_STATE_VOID_PENDING: + case GST_STATE_NULL: + setSeekable(false); + finishVideoOutputChange(); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_READY: + setSeekable(false); + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + break; + case GST_STATE_PAUSED: + { + QMediaPlayer::State prevState = m_state; + m_state = QMediaPlayer::PausedState; + + //check for seekable + if (oldState == GST_STATE_READY) { + if (m_sourceType == SoupHTTPSrc || m_sourceType == MMSSrc) { + //since udpsrc is a live source, it is not applicable here + m_everPlayed = true; + } + + getStreamsInfo(); + updateVideoResolutionTag(); + + //gstreamer doesn't give a reliable indication the duration + //information is ready, GST_MESSAGE_DURATION is not sent by most elements + //the duration is queried up to 5 times with increasing delay + m_durationQueries = 5; + updateDuration(); + + /* + //gst_element_seek_simple doesn't work reliably here, have to find a better solution + + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + bool seekable = false; + if (gst_element_query_position(m_playbin, &format, &position)) { + seekable = gst_element_seek_simple(m_playbin, format, GST_SEEK_FLAG_NONE, position); + } + + setSeekable(seekable); + */ + + setSeekable(true); + + if (!qFuzzyCompare(m_playbackRate, qreal(1.0))) { + qreal rate = m_playbackRate; + m_playbackRate = 1.0; + setPlaybackRate(rate); + } + } + + if (m_state != prevState) + emit stateChanged(m_state); + + break; + } + case GST_STATE_PLAYING: + m_everPlayed = true; + if (m_state != QMediaPlayer::PlayingState) + emit stateChanged(m_state = QMediaPlayer::PlayingState); + + break; + } + } + break; + + case GST_MESSAGE_EOS: + emit playbackFinished(); + break; + + case GST_MESSAGE_TAG: + case GST_MESSAGE_STREAM_STATUS: + case GST_MESSAGE_UNKNOWN: + break; + case GST_MESSAGE_ERROR: { + GError *err; + gchar *debug; + gst_message_parse_error(gm, &err, &debug); + if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) + processInvalidMedia(QMediaPlayer::FormatError, tr("Cannot play stream of type: ")); + else + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + qWarning() << "Error:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } + break; + case GST_MESSAGE_WARNING: + { + GError *err; + gchar *debug; + gst_message_parse_warning (gm, &err, &debug); + qWarning() << "Warning:" << QString::fromUtf8(err->message); + g_error_free (err); + g_free (debug); + } + break; + case GST_MESSAGE_INFO: +#ifdef DEBUG_PLAYBIN + { + GError *err; + gchar *debug; + gst_message_parse_info (gm, &err, &debug); + qDebug() << "Info:" << QString::fromUtf8(err->message); + g_error_free (err); + g_free (debug); + } +#endif + break; + case GST_MESSAGE_BUFFERING: + case GST_MESSAGE_STATE_DIRTY: + case GST_MESSAGE_STEP_DONE: + case GST_MESSAGE_CLOCK_PROVIDE: + case GST_MESSAGE_CLOCK_LOST: + case GST_MESSAGE_NEW_CLOCK: + case GST_MESSAGE_STRUCTURE_CHANGE: + case GST_MESSAGE_APPLICATION: + case GST_MESSAGE_ELEMENT: + break; + case GST_MESSAGE_SEGMENT_START: + { + const GstStructure *structure = gst_message_get_structure(gm); + qint64 position = g_value_get_int64(gst_structure_get_value(structure, "position")); + position /= 1000000; + m_lastPosition = position; + emit positionChanged(position); + } + break; + case GST_MESSAGE_SEGMENT_DONE: + break; + case GST_MESSAGE_LATENCY: +#if (GST_VERSION_MAJOR >= 0) && (GST_VERSION_MINOR >= 10) && (GST_VERSION_MICRO >= 13) + case GST_MESSAGE_ASYNC_START: + break; + case GST_MESSAGE_ASYNC_DONE: + { + GstFormat format = GST_FORMAT_TIME; + gint64 position = 0; + if (gst_element_query_position(m_playbin, &format, &position)) { + position /= 1000000; + m_lastPosition = position; + emit positionChanged(position); + } + break; + } +#if GST_VERSION_MICRO >= 23 + case GST_MESSAGE_REQUEST_STATE: +#endif +#endif + case GST_MESSAGE_ANY: + break; + default: + break; + } + } else if (m_videoSink + && m_renderer + && GST_MESSAGE_SRC(gm) == GST_OBJECT_CAST(m_videoSink)) { + + m_renderer->handleBusMessage(gm); + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) { + GstState oldState; + GstState newState; + gst_message_parse_state_changed(gm, &oldState, &newState, 0); + + if (oldState == GST_STATE_READY && newState == GST_STATE_PAUSED) + m_renderer->precessNewStream(); + } + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { + GError *err; + gchar *debug; + gst_message_parse_error(gm, &err, &debug); + // If the source has given up, so do we. + if (qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0) { + bool everPlayed = m_everPlayed; + // Try and differentiate network related resource errors from the others + if (!m_request.url().isRelative() && m_request.url().scheme().compare(QLatin1String("file"), Qt::CaseInsensitive) != 0 ) { + if (everPlayed || + (err->domain == GST_RESOURCE_ERROR && ( + err->code == GST_RESOURCE_ERROR_BUSY || + err->code == GST_RESOURCE_ERROR_OPEN_READ || + err->code == GST_RESOURCE_ERROR_READ || + err->code == GST_RESOURCE_ERROR_SEEK || + err->code == GST_RESOURCE_ERROR_SYNC))) { + processInvalidMedia(QMediaPlayer::NetworkError, QString::fromUtf8(err->message)); + } else { + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + } + } + else + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + } else if (err->domain == GST_STREAM_ERROR + && (err->code == GST_STREAM_ERROR_DECRYPT || err->code == GST_STREAM_ERROR_DECRYPT_NOKEY)) { + processInvalidMedia(QMediaPlayer::AccessDeniedError, QString::fromUtf8(err->message)); + } else { + handlePlaybin2 = m_usePlaybin2; + } + if (!handlePlaybin2) + qWarning() << "Error:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT + && qstrcmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "source") == 0 + && m_sourceType == UDPSrc + && gst_structure_has_name(gst_message_get_structure(gm), "GstUDPSrcTimeout")) { + //since udpsrc will not generate an error for the timeout event, + //we need to process its element message here and treat it as an error. + processInvalidMedia(m_everPlayed ? QMediaPlayer::NetworkError : QMediaPlayer::ResourceError, + tr("UDP source timeout")); + } else { + handlePlaybin2 = m_usePlaybin2; + } + + if (handlePlaybin2) { + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_WARNING) { + GError *err; + gchar *debug; + gst_message_parse_warning(gm, &err, &debug); + if (err->domain == GST_STREAM_ERROR && err->code == GST_STREAM_ERROR_CODEC_NOT_FOUND) + emit error(int(QMediaPlayer::FormatError), tr("Cannot play stream of type: ")); + qWarning() << "Warning:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } else if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ERROR) { + GError *err; + gchar *debug; + gst_message_parse_error(gm, &err, &debug); + if (qstrncmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "decodebin2", 10) == 0 + || qstrncmp(GST_OBJECT_NAME(GST_MESSAGE_SRC(gm)), "uridecodebin", 12) == 0) { + processInvalidMedia(QMediaPlayer::ResourceError, QString::fromUtf8(err->message)); + } else if (err->domain == GST_STREAM_ERROR + && (err->code == GST_STREAM_ERROR_DECRYPT || err->code == GST_STREAM_ERROR_DECRYPT_NOKEY)) { + processInvalidMedia(QMediaPlayer::AccessDeniedError, QString::fromUtf8(err->message)); + } + qWarning() << "Error:" << QString::fromUtf8(err->message); + g_error_free(err); + g_free(debug); + } + } + } +} + +void QGstreamerPlayerSession::getStreamsInfo() +{ + //check if video is available: + bool haveAudio = false; + bool haveVideo = false; + m_streamProperties.clear(); + m_streamTypes.clear(); + + if (m_usePlaybin2) { + gint audioStreamsCount = 0; + gint videoStreamsCount = 0; + gint textStreamsCount = 0; + + g_object_get(G_OBJECT(m_playbin), "n-audio", &audioStreamsCount, NULL); + g_object_get(G_OBJECT(m_playbin), "n-video", &videoStreamsCount, NULL); + g_object_get(G_OBJECT(m_playbin), "n-text", &textStreamsCount, NULL); + + haveAudio = audioStreamsCount > 0; + haveVideo = videoStreamsCount > 0; + + m_playbin2StreamOffset[QMediaStreamsControl::AudioStream] = 0; + m_playbin2StreamOffset[QMediaStreamsControl::VideoStream] = audioStreamsCount; + m_playbin2StreamOffset[QMediaStreamsControl::SubPictureStream] = audioStreamsCount+videoStreamsCount; + + for (int i=0; i streamProperties; + + int streamIndex = i - m_playbin2StreamOffset[streamType]; + + GstTagList *tags = 0; + switch (streamType) { + case QMediaStreamsControl::AudioStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-audio-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::VideoStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-video-tags", streamIndex, &tags); + break; + case QMediaStreamsControl::SubPictureStream: + g_signal_emit_by_name(G_OBJECT(m_playbin), "get-text-tags", streamIndex, &tags); + break; + default: + break; + } + + if (tags && gst_is_tag_list(tags)) { + gchar *languageCode = 0; + if (gst_tag_list_get_string(tags, GST_TAG_LANGUAGE_CODE, &languageCode)) + streamProperties[QtMultimediaKit::Language] = QString::fromUtf8(languageCode); + + //qDebug() << "language for setream" << i << QString::fromUtf8(languageCode); + g_free (languageCode); + } + + m_streamProperties.append(streamProperties); + } + } else { // PlayBin 1 + enum { + GST_STREAM_TYPE_UNKNOWN, + GST_STREAM_TYPE_AUDIO, + GST_STREAM_TYPE_VIDEO, + GST_STREAM_TYPE_TEXT, + GST_STREAM_TYPE_SUBPICTURE, + GST_STREAM_TYPE_ELEMENT + }; + + GList* streamInfoList; + g_object_get(G_OBJECT(m_playbin), "stream-info", &streamInfoList, NULL); + + for (; streamInfoList != 0; streamInfoList = g_list_next(streamInfoList)) { + gint type; + gchar *languageCode = 0; + + GObject* streamInfo = G_OBJECT(streamInfoList->data); + + g_object_get(streamInfo, "type", &type, NULL); + g_object_get(streamInfo, "language-code", &languageCode, NULL); + + QMediaStreamsControl::StreamType streamType = QMediaStreamsControl::UnknownStream; + + switch (type) { + case GST_STREAM_TYPE_VIDEO: + streamType = QMediaStreamsControl::VideoStream; + haveVideo = true; + break; + case GST_STREAM_TYPE_AUDIO: + streamType = QMediaStreamsControl::AudioStream; + haveAudio = true; + break; + case GST_STREAM_TYPE_SUBPICTURE: + streamType = QMediaStreamsControl::SubPictureStream; + break; + case GST_STREAM_TYPE_UNKNOWN: { + GstCaps *caps = 0; + g_object_get(streamInfo, "caps", &caps, NULL); + const GstStructure *structure = gst_caps_get_structure(caps, 0); + const gchar *media_type = gst_structure_get_name(structure); + emit error(int(QMediaPlayer::FormatError), QString::fromLatin1("Cannot play stream of type: %1").arg(QString::fromUtf8(media_type))); +#ifdef DEBUG_PLAYBIN + qDebug() << "Encountered unknown stream type"; +#endif + gst_caps_unref(caps); + } + default: + streamType = QMediaStreamsControl::UnknownStream; + break; + } + + QMap streamProperties; + streamProperties[QtMultimediaKit::Language] = QString::fromUtf8(languageCode); + + m_streamProperties.append(streamProperties); + m_streamTypes.append(streamType); + } + } + + + if (haveAudio != m_audioAvailable) { + m_audioAvailable = haveAudio; + emit audioAvailableChanged(m_audioAvailable); + } + if (haveVideo != m_videoAvailable) { + m_videoAvailable = haveVideo; + emit videoAvailableChanged(m_videoAvailable); + } + + emit streamsChanged(); +} + +void QGstreamerPlayerSession::updateVideoResolutionTag() +{ + QSize size; + QSize aspectRatio; + + GstPad *pad = gst_element_get_static_pad(m_videoIdentity, "src"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (!size.isEmpty() && gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + aspectRatio = QSize(aspectNum, aspectDenum); + } + gst_caps_unref(caps); + } + + gst_object_unref(GST_OBJECT(pad)); + + QSize currentSize = m_tags.value("resolution").toSize(); + QSize currentAspectRatio = m_tags.value("pixel-aspect-ratio").toSize(); + + if (currentSize != size || currentAspectRatio != aspectRatio) { + if (aspectRatio.isEmpty()) + m_tags.remove("pixel-aspect-ratio"); + + if (size.isEmpty()) { + m_tags.remove("resolution"); + } else { + m_tags.insert("resolution", QVariant(size)); + if (!aspectRatio.isEmpty()) + m_tags.insert("pixel-aspect-ratio", QVariant(aspectRatio)); + } + + emit tagsChanged(); + } +} + +void QGstreamerPlayerSession::updateDuration() +{ + GstFormat format = GST_FORMAT_TIME; + gint64 gstDuration = 0; + int duration = -1; + + if (m_playbin && gst_element_query_duration(m_playbin, &format, &gstDuration)) + duration = gstDuration / 1000000; + + if (m_duration != duration) { + m_duration = duration; + emit durationChanged(m_duration); + } + + if (m_duration > 0) + m_durationQueries = 0; + + if (m_durationQueries > 0) { + //increase delay between duration requests + int delay = 25 << (5 - m_durationQueries); + QTimer::singleShot(delay, this, SLOT(updateDuration())); + m_durationQueries--; + } +} + +void QGstreamerPlayerSession::playbinNotifySource(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(p); + + GstElement *source = 0; + g_object_get(o, "source", &source, NULL); + if (source == 0) + return; + +#ifdef DEBUG_PLAYBIN + qDebug() << "Playbin source added:" << G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)); +#endif + + // Turn off icecast metadata request, will be re-set if in QNetworkRequest + // (souphttpsrc docs say is false by default, but header appears in request + // @version 0.10.21) + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "iradio-mode") != 0) + g_object_set(G_OBJECT(source), "iradio-mode", FALSE, NULL); + + + // Set Headers + const QByteArray userAgentString("User-Agent"); + + QGstreamerPlayerSession *self = reinterpret_cast(d); + + // User-Agent - special case, souphhtpsrc will always set something, even if + // defined in extra-headers + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "user-agent") != 0) { + g_object_set(G_OBJECT(source), "user-agent", + self->m_request.rawHeader(userAgentString).constData(), NULL); + } + + // The rest + if (g_object_class_find_property(G_OBJECT_GET_CLASS(source), "extra-headers") != 0) { + GstStructure *extras = gst_structure_empty_new("extras"); + + foreach (const QByteArray &rawHeader, self->m_request.rawHeaderList()) { + if (rawHeader == userAgentString) // Filter User-Agent + continue; + else { + GValue headerValue; + + memset(&headerValue, 0, sizeof(GValue)); + g_value_init(&headerValue, G_TYPE_STRING); + + g_value_set_string(&headerValue, + self->m_request.rawHeader(rawHeader).constData()); + + gst_structure_set_value(extras, rawHeader.constData(), &headerValue); + } + } + + if (gst_structure_n_fields(extras) > 0) + g_object_set(G_OBJECT(source), "extra-headers", extras, NULL); + + gst_structure_free(extras); + } + + //set timeout property to 5 seconds + if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstUDPSrc") == 0) { + //udpsrc timeout unit = microsecond + g_object_set(G_OBJECT(source), "timeout", G_GUINT64_CONSTANT(5000000), NULL); + self->m_sourceType = UDPSrc; + } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstSoupHTTPSrc") == 0) { + //souphttpsrc timeout unit = second + g_object_set(G_OBJECT(source), "timeout", guint(5), NULL); + self->m_sourceType = SoupHTTPSrc; + } else if (qstrcmp(G_OBJECT_CLASS_NAME(G_OBJECT_GET_CLASS(source)), "GstMMSSrc") == 0) { + self->m_sourceType = MMSSrc; + g_object_set(G_OBJECT(source), "tcp-timeout", G_GUINT64_CONSTANT(5000000), NULL); + } else { + self->m_sourceType = UnknownSrc; + } + + gst_object_unref(source); +} + +void QGstreamerPlayerSession::handleVolumeChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstreamerPlayerSession *session = reinterpret_cast(d); + QMetaObject::invokeMethod(session, "updateVolume", Qt::QueuedConnection); +} + +void QGstreamerPlayerSession::updateVolume() +{ + double volume = 1.0; + g_object_get(m_playbin, "volume", &volume, NULL); + + //special case for playbin1 volume changes in muted state + //playbin1 has no separate muted state, + //it's emulated with volume value saved and set to 0 + //this change should not be reported to user + if (!m_usePlaybin2 && m_muted) { + if (volume > 0.001) { + //volume is changed, player in not muted any more + m_muted = false; + emit mutedStateChanged(m_muted = false); + } else { + //don't emit volume changed to 0 when player is muted + return; + } + } + + if (m_volume != int(volume*100)) { + m_volume = int(volume*100); +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << m_muted; +#endif + emit volumeChanged(m_volume); + } +} + +void QGstreamerPlayerSession::handleMutedChange(GObject *o, GParamSpec *p, gpointer d) +{ + Q_UNUSED(o); + Q_UNUSED(p); + QGstreamerPlayerSession *session = reinterpret_cast(d); + QMetaObject::invokeMethod(session, "updateMuted", Qt::QueuedConnection); +} + +void QGstreamerPlayerSession::updateMuted() +{ + gboolean muted = false; + g_object_get(G_OBJECT(m_playbin), "mute", &muted, NULL); + if (m_muted != muted) { + m_muted = muted; +#ifdef DEBUG_PLAYBIN + qDebug() << Q_FUNC_INFO << m_muted; +#endif + emit mutedStateChanged(muted); + } +} + + +void QGstreamerPlayerSession::handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session) +{ + Q_UNUSED(bin); + //we have to configure queue2 element to enable media downloading + //and reporting available ranges, + //but it's added dynamically to playbin2 + + gchar *elementName = gst_element_get_name(element); + + if (g_str_has_prefix(elementName, "queue2")) { + session->m_haveQueueElement = true; + + if (session->property("mediaDownloadEnabled").toBool()) { + QDir cacheDir(QDesktopServices::storageLocation(QDesktopServices::CacheLocation)); + QString cacheLocation = cacheDir.absoluteFilePath("gstmedia__XXXXXX"); +#ifdef DEBUG_PLAYBIN + qDebug() << "set queue2 temp-location" << cacheLocation; +#endif + g_object_set(G_OBJECT(element), "temp-template", cacheLocation.toUtf8().constData(), NULL); + } else { + g_object_set(G_OBJECT(element), "temp-template", NULL, NULL); + } + } else if (g_str_has_prefix(elementName, "uridecodebin") || + g_str_has_prefix(elementName, "decodebin2")) { + //listen for queue2 element added to uridecodebin/decodebin2 as well. + //Don't touch other bins since they may have unrelated queues + g_signal_connect(element, "element-added", + G_CALLBACK(handleElementAdded), session); + } + + g_free(elementName); +} + +//doing proper operations when detecting an invalidMedia: change media status before signal the erorr +void QGstreamerPlayerSession::processInvalidMedia(QMediaPlayer::Error errorCode, const QString& errorString) +{ + emit invalidMedia(); + stop(); + emit error(int(errorCode), errorString); +} diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h new file mode 100644 index 000000000..e6fa996b0 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerplayersession.h @@ -0,0 +1,217 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERPLAYERSESSION_H +#define QGSTREAMERPLAYERSESSION_H + +#include +#include +#include "qgstreamerplayercontrol.h" +#include "qgstreamerbushelper.h" +#include +#include + +#if defined(HAVE_GST_APPSRC) +#include "qgstappsrc.h" +#endif + +#include + +class QGstreamerBusHelper; +class QGstreamerMessage; + +class QGstreamerVideoRendererInterface; + +QT_USE_NAMESPACE + +class QGstreamerPlayerSession : public QObject, public QGstreamerSyncEventFilter +{ +Q_OBJECT + +public: + QGstreamerPlayerSession(QObject *parent); + virtual ~QGstreamerPlayerSession(); + + QNetworkRequest request() const; + + QMediaPlayer::State state() const { return m_state; } + QMediaPlayer::State pendingState() const { return m_pendingState; } + + qint64 duration() const; + qint64 position() const; + + bool isBuffering() const; + + int bufferingProgress() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + + void setVideoRenderer(QObject *renderer); + bool isVideoAvailable() const; + + bool isSeekable() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + QMediaTimeRange availablePlaybackRanges() const; + + QMap tags() const { return m_tags; } + QMap streamProperties(int streamNumber) const { return m_streamProperties[streamNumber]; } + int streamCount() const { return m_streamProperties.count(); } + QMediaStreamsControl::StreamType streamType(int streamNumber) { return m_streamTypes.value(streamNumber, QMediaStreamsControl::UnknownStream); } + + int activeStream(QMediaStreamsControl::StreamType streamType) const; + void setActiveStream(QMediaStreamsControl::StreamType streamType, int streamNumber); + + bool processSyncMessage(const QGstreamerMessage &message); + +#if defined(HAVE_GST_APPSRC) + QGstAppSrc *appsrc() const { return m_appSrc; } + static void configureAppSrcElement(GObject*, GObject*, GParamSpec*,QGstreamerPlayerSession* _this); +#endif + +public slots: + void loadFromUri(const QNetworkRequest &url); + void loadFromStream(const QNetworkRequest &url, QIODevice *stream); + bool play(); + bool pause(); + void stop(); + + bool seek(qint64 pos); + + void setVolume(int volume); + void setMuted(bool muted); + +signals: + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State state); + void volumeChanged(int volume); + void mutedStateChanged(bool muted); + void audioAvailableChanged(bool audioAvailable); + void videoAvailableChanged(bool videoAvailable); + void bufferingChanged(bool buffering); + void bufferingProgressChanged(int percentFilled); + void playbackFinished(); + void tagsChanged(); + void streamsChanged(); + void seekableChanged(bool); + void error(int error, const QString &errorString); + void invalidMedia(); + void playbackRateChanged(qreal); + +private slots: + void busMessage(const QGstreamerMessage &message); + void getStreamsInfo(); + void setSeekable(bool); + void finishVideoOutputChange(); + void updateVideoRenderer(); + void updateVideoResolutionTag(); + void updateVolume(); + void updateMuted(); + void updateDuration(); + +private: + static void playbinNotifySource(GObject *o, GParamSpec *p, gpointer d); + static void handleVolumeChange(GObject *o, GParamSpec *p, gpointer d); + static void handleMutedChange(GObject *o, GParamSpec *p, gpointer d); + static void insertColorSpaceElement(GstElement *element, gpointer data); + static void handleElementAdded(GstBin *bin, GstElement *element, QGstreamerPlayerSession *session); + void processInvalidMedia(QMediaPlayer::Error errorCode, const QString& errorString); + + QNetworkRequest m_request; + QMediaPlayer::State m_state; + QMediaPlayer::State m_pendingState; + QGstreamerBusHelper* m_busHelper; + GstElement* m_playbin; + bool m_usePlaybin2; + + GstElement* m_videoOutputBin; + GstElement* m_videoIdentity; + GstElement* m_colorSpace; + bool m_usingColorspaceElement; + GstElement* m_videoSink; + GstElement* m_pendingVideoSink; + GstElement* m_nullVideoSink; + + GstBus* m_bus; + QObject *m_videoOutput; + QGstreamerVideoRendererInterface *m_renderer; + + bool m_haveQueueElement; + +#if defined(HAVE_GST_APPSRC) + QGstAppSrc *m_appSrc; +#endif + + QMap m_tags; + QList< QMap > m_streamProperties; + QList m_streamTypes; + QMap m_playbin2StreamOffset; + + + int m_volume; + qreal m_playbackRate; + bool m_muted; + bool m_audioAvailable; + bool m_videoAvailable; + bool m_seekable; + + mutable qint64 m_lastPosition; + qint64 m_duration; + int m_durationQueries; + + enum SourceType + { + UnknownSrc, + SoupHTTPSrc, + UDPSrc, + MMSSrc + }; + SourceType m_sourceType; + bool m_everPlayed; +}; + +#endif // QGSTREAMERPLAYERSESSION_H diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp b/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp new file mode 100644 index 000000000..0ab93022b --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamerstreamscontrol.h" +#include "qgstreamerplayersession.h" + +QGstreamerStreamsControl::QGstreamerStreamsControl(QGstreamerPlayerSession *session, QObject *parent) + :QMediaStreamsControl(parent), m_session(session) +{ + connect(m_session, SIGNAL(streamsChanged()), SIGNAL(streamsChanged())); +} + +QGstreamerStreamsControl::~QGstreamerStreamsControl() +{ +} + +int QGstreamerStreamsControl::streamCount() +{ + return m_session->streamCount(); +} + +QMediaStreamsControl::StreamType QGstreamerStreamsControl::streamType(int streamNumber) +{ + return m_session->streamType(streamNumber); +} + +QVariant QGstreamerStreamsControl::metaData(int streamNumber, QtMultimediaKit::MetaData key) +{ + return m_session->streamProperties(streamNumber).value(key); +} + +bool QGstreamerStreamsControl::isActive(int streamNumber) +{ + return streamNumber != -1 && streamNumber == m_session->activeStream(streamType(streamNumber)); +} + +void QGstreamerStreamsControl::setActive(int streamNumber, bool state) +{ + QMediaStreamsControl::StreamType type = m_session->streamType(streamNumber); + if (type == QMediaStreamsControl::UnknownStream) + return; + + if (state) + m_session->setActiveStream(type, streamNumber); + else { + //only one active stream of certain type is supported + if (m_session->activeStream(type) == streamNumber) + m_session->setActiveStream(type, -1); + } +} + diff --git a/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h b/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h new file mode 100644 index 000000000..1213455b9 --- /dev/null +++ b/src/plugins/gstreamer/mediaplayer/qgstreamerstreamscontrol.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERSTREAMSCONTROL_H +#define QGSTREAMERSTREAMSCONTROL_H + +#include + +QT_USE_NAMESPACE + +class QGstreamerPlayerSession; + +class QGstreamerStreamsControl : public QMediaStreamsControl +{ + Q_OBJECT +public: + QGstreamerStreamsControl(QGstreamerPlayerSession *session, QObject *parent); + virtual ~QGstreamerStreamsControl(); + + virtual int streamCount(); + virtual StreamType streamType(int streamNumber); + + virtual QVariant metaData(int streamNumber, QtMultimediaKit::MetaData key); + + virtual bool isActive(int streamNumber); + virtual void setActive(int streamNumber, bool state); + +private: + QGstreamerPlayerSession *m_session; +}; + +#endif // QGSTREAMERSTREAMSCONTROL_H + diff --git a/src/plugins/gstreamer/qabstractgstbufferpool.h b/src/plugins/gstreamer/qabstractgstbufferpool.h new file mode 100644 index 000000000..8681dac71 --- /dev/null +++ b/src/plugins/gstreamer/qabstractgstbufferpool.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTBUFFERPOOL_H +#define QGSTBUFFERPOOL_H + +#include +#include + +#include + +/*! + Abstract interface for video buffers allocation. +*/ +class QAbstractGstBufferPool +{ +public: + virtual ~QAbstractGstBufferPool() {} + + virtual bool isFormatSupported(const QVideoSurfaceFormat &format) const = 0; + + virtual GType bufferType() const = 0; + virtual GstBuffer *takeBuffer(const QVideoSurfaceFormat &format, GstCaps *caps) = 0; + virtual void clear() = 0; + + virtual QAbstractVideoBuffer::HandleType handleType() const = 0; + + /*! + Build an QAbstractVideoBuffer instance from compatible (mathcing gst buffer type) + GstBuffer. + + This method is called from gstreamer video sink thread. + */ + virtual QAbstractVideoBuffer *prepareVideoBuffer(GstBuffer *buffer, int bytesPerLine) = 0; +}; + +#endif diff --git a/src/plugins/gstreamer/qgstreameraudioinputendpointselector.cpp b/src/plugins/gstreamer/qgstreameraudioinputendpointselector.cpp new file mode 100644 index 000000000..606f06052 --- /dev/null +++ b/src/plugins/gstreamer/qgstreameraudioinputendpointselector.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreameraudioinputendpointselector.h" + +#include +#include +#include + +#include + +#ifdef HAVE_ALSA +#include +#endif + +QGstreamerAudioInputEndpointSelector::QGstreamerAudioInputEndpointSelector(QObject *parent) + :QAudioEndpointSelector(parent) +{ + update(); +} + +QGstreamerAudioInputEndpointSelector::~QGstreamerAudioInputEndpointSelector() +{ +} + +QList QGstreamerAudioInputEndpointSelector::availableEndpoints() const +{ + return m_names; +} + +QString QGstreamerAudioInputEndpointSelector::endpointDescription(const QString& name) const +{ + QString desc; + + for (int i = 0; i < m_names.size(); i++) { + if (m_names.at(i).compare(name) == 0) { + desc = m_descriptions.at(i); + break; + } + } + return desc; +} + +QString QGstreamerAudioInputEndpointSelector::defaultEndpoint() const +{ + if (m_names.size() > 0) + return m_names.at(0); + + return QString(); +} + +QString QGstreamerAudioInputEndpointSelector::activeEndpoint() const +{ + return m_audioInput; +} + +void QGstreamerAudioInputEndpointSelector::setActiveEndpoint(const QString& name) +{ + if (m_audioInput.compare(name) != 0) { + m_audioInput = name; + emit activeEndpointChanged(name); + } +} + +void QGstreamerAudioInputEndpointSelector::update() +{ + m_names.clear(); + m_descriptions.clear(); +#ifndef Q_WS_MAEMO_5 + updateAlsaDevices(); + updateOssDevices(); +#endif + updatePulseDevices(); + if (m_names.size() > 0) + m_audioInput = m_names.at(0); +} + +void QGstreamerAudioInputEndpointSelector::updateAlsaDevices() +{ +#ifdef HAVE_ALSA + void **hints, **n; + if (snd_device_name_hint(-1, "pcm", &hints) < 0) { + qWarning()<<"no alsa devices available"; + return; + } + n = hints; + + while (*n != NULL) { + char *name = snd_device_name_get_hint(*n, "NAME"); + char *descr = snd_device_name_get_hint(*n, "DESC"); + char *io = snd_device_name_get_hint(*n, "IOID"); + + if ((name != NULL) && (descr != NULL)) { + if ( io == NULL || qstrcmp(io,"Input") == 0 ) { + m_names.append(QLatin1String("alsa:")+QString::fromUtf8(name)); + m_descriptions.append(QString::fromUtf8(descr)); + } + } + + if (name != NULL) + free(name); + if (descr != NULL) + free(descr); + if (io != NULL) + free(io); + n++; + } + snd_device_name_free_hint(hints); +#endif +} + +void QGstreamerAudioInputEndpointSelector::updateOssDevices() +{ + QDir devDir("/dev"); + devDir.setFilter(QDir::System); +#ifndef QT_QWS_N810 + QFileInfoList entries = devDir.entryInfoList(QStringList() << "dsp*"); + foreach(const QFileInfo& entryInfo, entries) { + m_names.append(QLatin1String("oss:")+entryInfo.filePath()); + m_descriptions.append(QString("OSS device %1").arg(entryInfo.fileName())); + } +#else + m_names.append("dsppcm"); + m_descriptions.append("PCM audio input"); +#endif +} + +void QGstreamerAudioInputEndpointSelector::updatePulseDevices() +{ + GstElementFactory *factory = gst_element_factory_find("pulsesrc"); + if (factory) { + m_names.append("pulseaudio:"); + m_descriptions.append("PulseAudio device."); + gst_object_unref(GST_OBJECT(factory)); + } +} diff --git a/src/plugins/gstreamer/qgstreameraudioinputendpointselector.h b/src/plugins/gstreamer/qgstreameraudioinputendpointselector.h new file mode 100644 index 000000000..c9587fcb8 --- /dev/null +++ b/src/plugins/gstreamer/qgstreameraudioinputendpointselector.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERAUDIOINPUTENDPOINTSELECTOR_H +#define QGSTREAMERAUDIOINPUTENDPOINTSELECTOR_H + +#include +#include + +QT_USE_NAMESPACE + +class QGstreamerAudioInputEndpointSelector : public QAudioEndpointSelector +{ +Q_OBJECT +public: + QGstreamerAudioInputEndpointSelector(QObject *parent); + ~QGstreamerAudioInputEndpointSelector(); + + QList availableEndpoints() const; + QString endpointDescription(const QString& name) const; + QString defaultEndpoint() const; + QString activeEndpoint() const; + +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +private: + void update(); + void updateAlsaDevices(); + void updateOssDevices(); + void updatePulseDevices(); + + QString m_audioInput; + QList m_names; + QList m_descriptions; +}; + +#endif // QGSTREAMERAUDIOINPUTENDPOINTSELECTOR_H diff --git a/src/plugins/gstreamer/qgstreamerbushelper.cpp b/src/plugins/gstreamer/qgstreamerbushelper.cpp new file mode 100644 index 000000000..a60ec7fab --- /dev/null +++ b/src/plugins/gstreamer/qgstreamerbushelper.cpp @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "qgstreamerbushelper.h" + + +#ifndef QT_NO_GLIB +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT + +public: + void addWatch(GstBus* bus, QGstreamerBusHelper* helper) + { + setParent(helper); + m_tag = gst_bus_add_watch_full(bus, 0, busCallback, this, NULL); + m_helper = helper; + filter = 0; + } + + void removeWatch(QGstreamerBusHelper* helper) + { + Q_UNUSED(helper); + g_source_remove(m_tag); + } + + static QGstreamerBusHelperPrivate* instance() + { + return new QGstreamerBusHelperPrivate; + } + +private: + void processMessage(GstBus* bus, GstMessage* message) + { + Q_UNUSED(bus); + emit m_helper->message(message); + } + + static gboolean busCallback(GstBus *bus, GstMessage *message, gpointer data) + { + reinterpret_cast(data)->processMessage(bus, message); + return TRUE; + } + + guint m_tag; + QGstreamerBusHelper* m_helper; + +public: + GstBus* bus; + QGstreamerSyncEventFilter *filter; + QMutex filterMutex; +}; + +#else + +class QGstreamerBusHelperPrivate : public QObject +{ + Q_OBJECT + typedef QMap HelperMap; + +public: + void addWatch(GstBus* bus, QGstreamerBusHelper* helper) + { + m_helperMap.insert(helper, bus); + + if (m_helperMap.size() == 1) + m_intervalTimer->start(); + } + + void removeWatch(QGstreamerBusHelper* helper) + { + m_helperMap.remove(helper); + + if (m_helperMap.size() == 0) + m_intervalTimer->stop(); + } + + static QGstreamerBusHelperPrivate* instance() + { + static QGstreamerBusHelperPrivate self; + + return &self; + } + +private slots: + void interval() + { + for (HelperMap::iterator it = m_helperMap.begin(); it != m_helperMap.end(); ++it) { + GstMessage* message; + + while ((message = gst_bus_poll(it.value(), GST_MESSAGE_ANY, 0)) != 0) { + emit it.key()->message(message); + gst_message_unref(message); + } + + emit it.key()->message(QGstreamerMessage()); + } + } + +private: + QGstreamerBusHelperPrivate() + { + m_intervalTimer = new QTimer(this); + m_intervalTimer->setInterval(250); + + connect(m_intervalTimer, SIGNAL(timeout()), SLOT(interval())); + } + + HelperMap m_helperMap; + QTimer* m_intervalTimer; + +public: + GstBus* bus; + QGstreamerSyncEventFilter *filter; + QMutex filterMutex; +}; +#endif + + +static GstBusSyncReply syncGstBusFilter(GstBus* bus, GstMessage* message, QGstreamerBusHelperPrivate *d) +{ + Q_UNUSED(bus); + QMutexLocker lock(&d->filterMutex); + + bool res = false; + + if (d->filter) + res = d->filter->processSyncMessage(QGstreamerMessage(message)); + + return res ? GST_BUS_DROP : GST_BUS_PASS; +} + + +/*! + \class gstreamer::QGstreamerBusHelper + \internal +*/ + +QGstreamerBusHelper::QGstreamerBusHelper(GstBus* bus, QObject* parent): + QObject(parent), + d(QGstreamerBusHelperPrivate::instance()) +{ + d->bus = bus; + d->addWatch(bus, this); + + gst_bus_set_sync_handler(bus, (GstBusSyncHandler)syncGstBusFilter, d); +} + +QGstreamerBusHelper::~QGstreamerBusHelper() +{ + d->removeWatch(this); + gst_bus_set_sync_handler(d->bus,0,0); +} + +void QGstreamerBusHelper::installSyncEventFilter(QGstreamerSyncEventFilter *filter) +{ + QMutexLocker lock(&d->filterMutex); + d->filter = filter; +} + +#include "qgstreamerbushelper.moc" diff --git a/src/plugins/gstreamer/qgstreamerbushelper.h b/src/plugins/gstreamer/qgstreamerbushelper.h new file mode 100644 index 000000000..5375c44ae --- /dev/null +++ b/src/plugins/gstreamer/qgstreamerbushelper.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERBUSHELPER_H +#define QGSTREAMERBUSHELPER_H + +#include + +#include +#include + +class QGstreamerSyncEventFilter { +public: + //returns true if message was processed and should be dropped, false otherwise + virtual bool processSyncMessage(const QGstreamerMessage &message) = 0; +}; + +class QGstreamerBusHelperPrivate; + +class QGstreamerBusHelper : public QObject +{ + Q_OBJECT + friend class QGstreamerBusHelperPrivate; + +public: + QGstreamerBusHelper(GstBus* bus, QObject* parent = 0); + ~QGstreamerBusHelper(); + + void installSyncEventFilter(QGstreamerSyncEventFilter *filter); + +signals: + void message(QGstreamerMessage const& message); + + +private: + QGstreamerBusHelperPrivate* d; +}; + +#endif diff --git a/src/plugins/gstreamer/qgstreamergltexturerenderer.cpp b/src/plugins/gstreamer/qgstreamergltexturerenderer.cpp new file mode 100644 index 000000000..5d1a11ffb --- /dev/null +++ b/src/plugins/gstreamer/qgstreamergltexturerenderer.cpp @@ -0,0 +1,578 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qvideosurfacegstsink.h" +#include "qabstractvideosurface.h" +#include "qgstutils.h" + +#include +#include +#include +#include +#include + +#include + +#include +#include +#include +#include +#include + + +#include +#include + +#include "qgstreamergltexturerenderer.h" + +//#define GL_TEXTURE_SINK_DEBUG 1 + +//from extdefs.h +typedef void *EGLSyncKHR; +typedef khronos_utime_nanoseconds_t EGLTimeKHR; + +#define GL_TEXTURE_EXTERNAL_OES 0x8D65 +#define EGL_SYNC_FENCE_KHR 0x30F9 + +typedef EGLSyncKHR (EGLAPIENTRYP _PFNEGLCREATESYNCKHRPROC) (EGLDisplay dpy, + EGLenum type, const EGLint * attrib_list); +typedef EGLBoolean (EGLAPIENTRYP _PFNEGLDESTROYSYNCKHRPROC) (EGLDisplay dpy, + EGLSyncKHR sync); + + +const QAbstractVideoBuffer::HandleType EGLImageTextureHandle = + QAbstractVideoBuffer::HandleType(QAbstractVideoBuffer::UserHandle+3434); + +// EGLSync functions +_PFNEGLCREATESYNCKHRPROC eglCreateSyncKHR; +_PFNEGLDESTROYSYNCKHRPROC eglDestroySyncKHR; + +class QGStreamerGLTextureBuffer : public QAbstractVideoBuffer +{ +public: + QGStreamerGLTextureBuffer(MeegoGstVideoTexture *textureSink, int frameNumber) : + QAbstractVideoBuffer(EGLImageTextureHandle), + m_textureSink(MEEGO_GST_VIDEO_TEXTURE(textureSink)), + m_frameNumber(frameNumber) + { + } + + ~QGStreamerGLTextureBuffer() + { + } + + + MapMode mapMode() const { return NotMapped; } + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + Q_UNUSED(mode); + Q_UNUSED(numBytes); + Q_UNUSED(bytesPerLine); + + //acquire_frame should really be called at buffer construction time + //but it conflicts with id-less implementation of gst texture sink. +#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 + qDebug() << "acquire frame" << m_frameNumber; +#endif + if (!meego_gst_video_texture_acquire_frame(m_textureSink,m_frameNumber)) + qWarning() << Q_FUNC_INFO << "acquire-frame failed" << m_frameNumber; + + +#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 + qDebug() << "map frame" << m_frameNumber; +#endif + + gboolean bind_status = meego_gst_video_texture_bind_frame(m_textureSink, GL_TEXTURE_EXTERNAL_OES, m_frameNumber); + if (!bind_status) + qWarning() << Q_FUNC_INFO << "bind-frame failed"; + + return (uchar*)1; + } + + void unmap() + { + gboolean bind_status = meego_gst_video_texture_bind_frame(m_textureSink, GL_TEXTURE_EXTERNAL_OES, -1); + +#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 + qDebug() << "unmap frame" << m_frameNumber; +#endif + + if (!bind_status) + qWarning() << Q_FUNC_INFO << "unbind-frame failed"; + + //release_frame should really be called in destructor + //but this conflicts with id-less implementation of gst texture sink. +#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 + qDebug() << "release frame" << m_frameNumber; +#endif + EGLSyncKHR sync = eglCreateSyncKHR(eglGetDisplay((EGLNativeDisplayType)QX11Info::display()), EGL_SYNC_FENCE_KHR, NULL); + meego_gst_video_texture_release_frame(m_textureSink, m_frameNumber, sync); + } + + QVariant handle() const + { + return m_frameNumber; + } + +private: + MeegoGstVideoTexture *m_textureSink; + int m_frameNumber; +}; + + +QGstreamerGLTextureRenderer::QGstreamerGLTextureRenderer(QObject *parent) : + QVideoRendererControl(parent), + m_videoSink(0), + m_surface(0), + m_context(0), + m_winId(0), + m_colorKey(49,0,49), + m_overlayEnabled(false), + m_bufferProbeId(-1) +{ + eglCreateSyncKHR = + (_PFNEGLCREATESYNCKHRPROC)eglGetProcAddress("eglCreateSyncKHR"); + eglDestroySyncKHR = + (_PFNEGLDESTROYSYNCKHRPROC)eglGetProcAddress("eglDestroySyncKHR"); +} + +QGstreamerGLTextureRenderer::~QGstreamerGLTextureRenderer() +{ + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); +} + +GstElement *QGstreamerGLTextureRenderer::videoSink() +{ + if (!m_videoSink && isReady()) { + if (m_context && !m_surface->supportedPixelFormats(EGLImageTextureHandle).isEmpty()) { +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << ": using gltexture sink"; +#endif + if (m_context) + m_context->makeCurrent(); + m_videoSink = gst_element_factory_make("gltexturesink", "egl-texture-sink"); + g_object_set(G_OBJECT(m_videoSink), + "x-display", QX11Info::display(), + "egl-display", eglGetDisplay((EGLNativeDisplayType)QX11Info::display()), + "egl-context", eglGetCurrentContext(), + "colorkey", m_colorKey.rgb(), + "autopaint-colorkey", false, + "use-framebuffer-memory", true, + "render-mode", m_overlayEnabled ? VIDEO_RENDERSWITCH_XOVERLAY_MODE + : VIDEO_RENDERSWITCH_TEXTURE_STREAMING_MODE, + (char*)NULL); + + g_signal_connect(G_OBJECT(m_videoSink), "frame-ready", G_CALLBACK(handleFrameReady), (gpointer)this); + } else { + qWarning() << Q_FUNC_INFO << ": Fallback to QVideoSurfaceGstSink since EGLImageTextureHandle is not supported"; + m_videoSink = reinterpret_cast(QVideoSurfaceGstSink::createSink(m_surface)); + } + + if (m_videoSink) { + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); + } + } + + return m_videoSink; +} + +QAbstractVideoSurface *QGstreamerGLTextureRenderer::surface() const +{ + return m_surface; +} + +void QGstreamerGLTextureRenderer::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface != surface) { +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << surface; +#endif + + bool oldReady = isReady(); + + m_context = const_cast(QGLContext::currentContext()); + + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = 0; + + if (m_surface) { + disconnect(m_surface, SIGNAL(supportedFormatsChanged()), + this, SLOT(handleFormatChange())); + } + + m_surface = surface; + + if (oldReady != isReady()) + emit readyChanged(!oldReady); + + if (m_surface) { + connect(m_surface, SIGNAL(supportedFormatsChanged()), + this, SLOT(handleFormatChange())); + } + + emit sinkChanged(); + } +} + +void QGstreamerGLTextureRenderer::handleFormatChange() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = 0; + emit sinkChanged(); +} + +void QGstreamerGLTextureRenderer::handleFrameReady(GstElement *sink, gint frame, gpointer data) +{ + Q_UNUSED(sink); + QGstreamerGLTextureRenderer* renderer = reinterpret_cast(data); + + QMutexLocker locker(&renderer->m_mutex); + QMetaObject::invokeMethod(renderer, "renderGLFrame", + Qt::QueuedConnection, + Q_ARG(int, frame)); + + //we have to wait to ensure the frame is not reused, + //timeout is added to avoid deadlocks when the main thread is + //waiting for rendering to complete, this is possible for example during state chages. + //If frame is not rendered during 60ms (~1-2 frames interval) it's better to unblock and drop it if necessary + renderer->m_renderCondition.wait(&renderer->m_mutex, 60); +} + +void QGstreamerGLTextureRenderer::renderGLFrame(int frame) +{ +#if defined(GL_TEXTURE_SINK_DEBUG) && GL_TEXTURE_SINK_DEBUG > 1 + qDebug() << Q_FUNC_INFO << "frame:" << frame << "surface active:" << m_surface->isActive(); +#endif + QMutexLocker locker(&m_mutex); + + if (!m_surface) { + m_renderCondition.wakeAll(); + return; + } + + MeegoGstVideoTexture *textureSink = MEEGO_GST_VIDEO_TEXTURE(m_videoSink); + + if (m_context) + m_context->makeCurrent(); + + //don't try to render the frame if state is changed to NULL or READY + GstState pendingState = GST_STATE_NULL; + GstState newState = GST_STATE_NULL; + GstStateChangeReturn res = gst_element_get_state(m_videoSink, + &newState, + &pendingState, + 0);//don't block and return immediately + + if (res == GST_STATE_CHANGE_FAILURE || + newState == GST_STATE_NULL || + pendingState == GST_STATE_NULL) { + stopRenderer(); + m_renderCondition.wakeAll(); + return; + } + + if (!m_surface->isActive()) { + //find the native video size + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + QSize newNativeSize = QGstUtils::capsCorrectedResolution(caps); + if (m_nativeSize != newNativeSize) { + m_nativeSize = newNativeSize; + emit nativeSizeChanged(); + } + gst_caps_unref(caps); + } + + //start the surface... + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, EGLImageTextureHandle); + if (!m_surface->start(format)) { + qWarning() << Q_FUNC_INFO << "failed to start video surface" << format; + m_renderCondition.wakeAll(); + return; + } + } + + QGStreamerGLTextureBuffer *buffer = new QGStreamerGLTextureBuffer(textureSink, frame); + QVideoFrame videoFrame(buffer, + m_surface->surfaceFormat().frameSize(), + m_surface->surfaceFormat().pixelFormat()); + m_surface->present(videoFrame); + m_renderCondition.wakeAll(); +} + +bool QGstreamerGLTextureRenderer::isReady() const +{ + if (!m_surface) + return false; + + if (m_winId > 0) + return true; + + //winId is required only for EGLImageTextureHandle compatible surfaces + return m_surface->supportedPixelFormats(EGLImageTextureHandle).isEmpty(); +} + +void QGstreamerGLTextureRenderer::handleBusMessage(GstMessage* gm) +{ +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << GST_MESSAGE_TYPE_NAME(gm); +#endif + + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_STATE_CHANGED) { + GstState oldState; + GstState newState; + gst_message_parse_state_changed(gm, &oldState, &newState, 0); + +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << "State changed:" << oldState << newState; +#endif + + if (newState == GST_STATE_READY || newState == GST_STATE_NULL) { + stopRenderer(); + } + + if (oldState == GST_STATE_READY && newState == GST_STATE_PAUSED) { + updateNativeVideoSize(); + } + } +} + +void QGstreamerGLTextureRenderer::handleSyncMessage(GstMessage* gm) +{ +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO; +#endif + + if (GST_MESSAGE_TYPE(gm) == GST_MESSAGE_ELEMENT && + gst_structure_has_name(gm->structure, "prepare-xwindow-id")) + precessNewStream(); +} + +void QGstreamerGLTextureRenderer::precessNewStream() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + GstXOverlay *overlay = GST_X_OVERLAY(m_videoSink); + + gst_x_overlay_set_xwindow_id(overlay, m_winId); + + if (!m_displayRect.isEmpty()) { + gst_x_overlay_set_render_rectangle(overlay, + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + } + + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); + } +} + +void QGstreamerGLTextureRenderer::stopRenderer() +{ +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO; +#endif + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + if (!m_nativeSize.isEmpty()) { + m_nativeSize = QSize(); + emit nativeSizeChanged(); + } +} + +bool QGstreamerGLTextureRenderer::overlayEnabled() const +{ + return m_overlayEnabled; +} + +void QGstreamerGLTextureRenderer::setOverlayEnabled(bool enabled) +{ + + if (m_videoSink && (m_overlayEnabled != enabled)) { + qDebug() << Q_FUNC_INFO << enabled; + g_object_set(G_OBJECT(m_videoSink), + "render-mode", + enabled ? VIDEO_RENDERSWITCH_XOVERLAY_MODE : VIDEO_RENDERSWITCH_TEXTURE_STREAMING_MODE, + (char *)NULL); + } + + m_overlayEnabled = enabled; +} + + +WId QGstreamerGLTextureRenderer::winId() const +{ + return m_winId; +} + +void QGstreamerGLTextureRenderer::setWinId(WId id) +{ +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << id; +#endif + + if (m_winId == id) + return; + + bool oldReady = isReady(); + + m_winId = id; + + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + //don't set winId in NULL state, + //texture sink opens xvideo port on set_xwindow_id, + //this fails if video resource is not granted by resource policy yet. + //state is changed to READY/PAUSED/PLAYING only after resource is granted. + GstState pendingState = GST_STATE_NULL; + GstState newState = GST_STATE_NULL; + GstStateChangeReturn res = gst_element_get_state(m_videoSink, + &newState, + &pendingState, + 0);//don't block and return immediately + + if (res != GST_STATE_CHANGE_FAILURE && + newState != GST_STATE_NULL && + pendingState != GST_STATE_NULL) + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_winId); + } + + if (oldReady != isReady()) + emit readyChanged(!oldReady); +} + +QRect QGstreamerGLTextureRenderer::overlayGeometry() const +{ + return m_displayRect; +} + +void QGstreamerGLTextureRenderer::setOverlayGeometry(const QRect &geometry) +{ + if (m_displayRect != geometry) { +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << geometry; +#endif + m_displayRect = geometry; + + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + if (m_displayRect.isEmpty()) + gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), -1, -1, -1, -1); + else + gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + repaintOverlay(); + } + } +} + +QColor QGstreamerGLTextureRenderer::colorKey() const +{ + return m_colorKey; +} + +void QGstreamerGLTextureRenderer::repaintOverlay() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + //don't call gst_x_overlay_expose if the sink is in null state + GstState state = GST_STATE_NULL; + GstStateChangeReturn res = gst_element_get_state(m_videoSink, &state, NULL, 1000000); + if (res != GST_STATE_CHANGE_FAILURE && state != GST_STATE_NULL) { + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); + } + } +} + +QSize QGstreamerGLTextureRenderer::nativeSize() const +{ + return m_nativeSize; +} + +gboolean QGstreamerGLTextureRenderer::padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) +{ + QGstreamerGLTextureRenderer *control = reinterpret_cast(user_data); + QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); + gst_pad_remove_buffer_probe(pad, control->m_bufferProbeId); + + return TRUE; +} + +void QGstreamerGLTextureRenderer::updateNativeVideoSize() +{ + const QSize oldSize = m_nativeSize; + + if (m_videoSink) { + //find video native size to update video widget size hint + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + m_nativeSize = QGstUtils::capsCorrectedResolution(caps); + gst_caps_unref(caps); + } + } else { + m_nativeSize = QSize(); + } +#ifdef GL_TEXTURE_SINK_DEBUG + qDebug() << Q_FUNC_INFO << oldSize << m_nativeSize << m_videoSink; +#endif + + if (m_nativeSize != oldSize) + emit nativeSizeChanged(); +} diff --git a/src/plugins/gstreamer/qgstreamergltexturerenderer.h b/src/plugins/gstreamer/qgstreamergltexturerenderer.h new file mode 100644 index 000000000..92beb1a17 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamergltexturerenderer.h @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERGLTEXTURERENDERER_H +#define QGSTREAMERGLTEXTURERENDERER_H + +#include +#include "qvideosurfacegstsink.h" + +#include "qgstreamervideorendererinterface.h" +#include + +#include + +QT_USE_NAMESPACE + +class QGLContext; + +class QGstreamerGLTextureRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) + + Q_PROPERTY(bool overlayEnabled READ overlayEnabled WRITE setOverlayEnabled) + Q_PROPERTY(qulonglong winId READ winId WRITE setWinId) + Q_PROPERTY(QRect overlayGeometry READ overlayGeometry WRITE setOverlayGeometry) + Q_PROPERTY(QColor colorKey READ colorKey) + Q_PROPERTY(QSize nativeSize READ nativeSize NOTIFY nativeSizeChanged) + +public: + QGstreamerGLTextureRenderer(QObject *parent = 0); + virtual ~QGstreamerGLTextureRenderer(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + GstElement *videoSink(); + + bool isReady() const; + void handleBusMessage(GstMessage* gm); + void handleSyncMessage(GstMessage* gm); + void precessNewStream(); + void stopRenderer(); + + int framebufferNumber() const; + + bool overlayEnabled() const; + WId winId() const; + QRect overlayGeometry() const; + QColor colorKey() const; + QSize nativeSize() const; + +public slots: + void renderGLFrame(int); + + void setOverlayEnabled(bool); + void setWinId(WId id); + void setOverlayGeometry(const QRect &geometry); + void repaintOverlay(); + +signals: + void sinkChanged(); + void readyChanged(bool); + void nativeSizeChanged(); + +private slots: + void handleFormatChange(); + void updateNativeVideoSize(); + +private: + static void handleFrameReady(GstElement *sink, gint frame, gpointer data); + static gboolean padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); + + GstElement *m_videoSink; + QAbstractVideoSurface *m_surface; + QGLContext *m_context; + QSize m_nativeSize; + + WId m_winId; + QColor m_colorKey; + QRect m_displayRect; + bool m_overlayEnabled; + int m_bufferProbeId; + + QMutex m_mutex; + QWaitCondition m_renderCondition; +}; + +#endif // QGSTREAMERVIDEORENDRER_H diff --git a/src/plugins/gstreamer/qgstreamermessage.cpp b/src/plugins/gstreamer/qgstreamermessage.cpp new file mode 100644 index 000000000..3b8057e09 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamermessage.cpp @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qgstreamermessage.h" + + +static int wuchi = qRegisterMetaType(); + + +/*! + \class gstreamer::QGstreamerMessage + \internal +*/ + +QGstreamerMessage::QGstreamerMessage(): + m_message(0) +{ +} + +QGstreamerMessage::QGstreamerMessage(GstMessage* message): + m_message(message) +{ + gst_message_ref(m_message); +} + +QGstreamerMessage::QGstreamerMessage(QGstreamerMessage const& m): + m_message(m.m_message) +{ + gst_message_ref(m_message); +} + + +QGstreamerMessage::~QGstreamerMessage() +{ + if (m_message != 0) + gst_message_unref(m_message); +} + +GstMessage* QGstreamerMessage::rawMessage() const +{ + return m_message; +} + +QGstreamerMessage& QGstreamerMessage::operator=(QGstreamerMessage const& rhs) +{ + if (m_message != 0) + gst_message_unref(m_message); + + if ((m_message = rhs.m_message) != 0) + gst_message_ref(m_message); + + return *this; +} diff --git a/src/plugins/gstreamer/qgstreamermessage.h b/src/plugins/gstreamer/qgstreamermessage.h new file mode 100644 index 000000000..2bdc2a9c8 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamermessage.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERMESSAGE_H +#define QGSTREAMERMESSAGE_H + +#include + +#include + + +class QGstreamerMessage +{ +public: + QGstreamerMessage(); + QGstreamerMessage(GstMessage* message); + QGstreamerMessage(QGstreamerMessage const& m); + ~QGstreamerMessage(); + + GstMessage* rawMessage() const; + + QGstreamerMessage& operator=(QGstreamerMessage const& rhs); + +private: + GstMessage* m_message; +}; + +Q_DECLARE_METATYPE(QGstreamerMessage); + +#endif diff --git a/src/plugins/gstreamer/qgstreamerserviceplugin.cpp b/src/plugins/gstreamer/qgstreamerserviceplugin.cpp new file mode 100644 index 000000000..e74085664 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamerserviceplugin.cpp @@ -0,0 +1,401 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "qgstreamerserviceplugin.h" + +//#define QT_SUPPORTEDMIMETYPES_DEBUG + +#ifdef QMEDIA_GSTREAMER_PLAYER +#include "qgstreamerplayerservice.h" +#endif + +#if defined(QMEDIA_GSTREAMER_CAPTURE) +#include "qgstreamercaptureservice.h" +#endif + +#ifdef QMEDIA_GSTREAMER_CAMERABIN +#include "camerabinservice.h" +#endif + +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + + +QStringList QGstreamerServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_GSTREAMER_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) +#endif + +#ifdef QMEDIA_GSTREAMER_CAPTURE + << QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE) + << QLatin1String(Q_MEDIASERVICE_CAMERA) +#elif defined(QMEDIA_GSTREAMER_CAMERABIN) + << QLatin1String(Q_MEDIASERVICE_CAMERA) +#endif + ; + +} + +QMediaService* QGstreamerServicePlugin::create(const QString &key) +{ + static bool initialized = false; + if (!initialized) { + initialized = true; + gst_init(NULL, NULL); + } + +#ifdef QMEDIA_GSTREAMER_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new QGstreamerPlayerService; +#endif + +#ifdef QMEDIA_GSTREAMER_CAMERABIN + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA) && CameraBinService::isCameraBinAvailable()) + return new CameraBinService(key); +#endif + +#ifdef QMEDIA_GSTREAMER_CAPTURE + if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) + return new QGstreamerCaptureService(key); + + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new QGstreamerCaptureService(key); +#endif + + qWarning() << "Gstreamer service plugin: unsupported key:" << key; + return 0; +} + +void QGstreamerServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QMediaServiceProviderHint::Features QGstreamerServicePlugin::supportedFeatures( + const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_MEDIAPLAYER) + return QMediaServiceProviderHint::StreamPlayback | QMediaServiceProviderHint::VideoSurface; + else if (service == Q_MEDIASERVICE_CAMERA) + return QMediaServiceProviderHint::VideoSurface; + else + return QMediaServiceProviderHint::Features(); +} + +QList QGstreamerServicePlugin::devices(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } + + return QList(); +} + +QString QGstreamerServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i= 0; ++input.index) { + if(input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { + isCamera = ::ioctl(fd, VIDIOC_S_INPUT, input.index) != 0; + break; + } + } + + if (isCamera) { + // find out its driver "name" + QString name; + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(struct v4l2_capability)); + + if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) + name = entryInfo.fileName(); + else + name = QString((const char*)vcap.card); + //qDebug() << "found camera: " << name; + + m_cameraDevices.append(entryInfo.filePath().toLocal8Bit()); + m_cameraDescriptions.append(name); + } + ::close(fd); + } +} + +namespace { + const char* getCodecAlias(const QString &codec) + { + if (codec.startsWith("avc1.")) + return "video/x-h264"; + + if (codec.startsWith("mp4a.")) + return "audio/mpeg4"; + + if (codec.startsWith("mp4v.20.")) + return "video/mpeg4"; + + if (codec == "samr") + return "audio/amr"; + + return 0; + } + + const char* getMimeTypeAlias(const QString &mimeType) + { + if (mimeType == "video/mp4") + return "video/mpeg4"; + + if (mimeType == "audio/mp4") + return "audio/mpeg4"; + + if (mimeType == "video/ogg" + || mimeType == "audio/ogg") + return "application/ogg"; + + return 0; + } +} + +QtMultimediaKit::SupportEstimate QGstreamerServicePlugin::hasSupport(const QString &mimeType, + const QStringList& codecs) const +{ + if (m_supportedMimeTypeSet.isEmpty()) + updateSupportedMimeTypes(); + + QString mimeTypeLowcase = mimeType.toLower(); + bool containsMimeType = m_supportedMimeTypeSet.contains(mimeTypeLowcase); + if (!containsMimeType) { + const char* mimeTypeAlias = getMimeTypeAlias(mimeTypeLowcase); + containsMimeType = m_supportedMimeTypeSet.contains(mimeTypeAlias); + if (!containsMimeType) { + containsMimeType = m_supportedMimeTypeSet.contains("video/" + mimeTypeLowcase) + || m_supportedMimeTypeSet.contains("video/x-" + mimeTypeLowcase) + || m_supportedMimeTypeSet.contains("audio/" + mimeTypeLowcase) + || m_supportedMimeTypeSet.contains("audio/x-" + mimeTypeLowcase); + } + } + + int supportedCodecCount = 0; + foreach(const QString &codec, codecs) { + QString codecLowcase = codec.toLower(); + const char* codecAlias = getCodecAlias(codecLowcase); + if (codecAlias) { + if (m_supportedMimeTypeSet.contains(codecAlias)) + supportedCodecCount++; + } else if (m_supportedMimeTypeSet.contains("video/" + codecLowcase) + || m_supportedMimeTypeSet.contains("video/x-" + codecLowcase) + || m_supportedMimeTypeSet.contains("audio/" + codecLowcase) + || m_supportedMimeTypeSet.contains("audio/x-" + codecLowcase)) { + supportedCodecCount++; + } + } + if (supportedCodecCount > 0 && supportedCodecCount == codecs.size()) + return QtMultimediaKit::ProbablySupported; + + if (supportedCodecCount == 0 && !containsMimeType) + return QtMultimediaKit::NotSupported; + + return QtMultimediaKit::MaybeSupported; +} + +void QGstreamerServicePlugin::updateSupportedMimeTypes() const +{ + //enumerate supported mime types + gst_init(NULL, NULL); + + GList *plugins, *orig_plugins; + orig_plugins = plugins = gst_default_registry_get_plugin_list (); + + while (plugins) { + GList *features, *orig_features; + + GstPlugin *plugin = (GstPlugin *) (plugins->data); + plugins = g_list_next (plugins); + + if (plugin->flags & (1<<1)) //GST_PLUGIN_FLAG_BLACKLISTED + continue; + + orig_features = features = gst_registry_get_feature_list_by_plugin(gst_registry_get_default (), + plugin->desc.name); + while (features) { + if (!G_UNLIKELY(features->data == NULL)) { + GstPluginFeature *feature = GST_PLUGIN_FEATURE(features->data); + if (GST_IS_ELEMENT_FACTORY (feature)) { + GstElementFactory *factory = GST_ELEMENT_FACTORY(gst_plugin_feature_load(feature)); + if (factory + && factory->numpadtemplates > 0 + && (qstrcmp(factory->details.klass, "Codec/Decoder/Audio") == 0 + || qstrcmp(factory->details.klass, "Codec/Decoder/Video") == 0 + || qstrcmp(factory->details.klass, "Codec/Demux") == 0 )) { + const GList *pads = factory->staticpadtemplates; + while (pads) { + GstStaticPadTemplate *padtemplate = (GstStaticPadTemplate*)(pads->data); + pads = g_list_next (pads); + if (padtemplate->direction != GST_PAD_SINK) + continue; + if (padtemplate->static_caps.string) { + GstCaps *caps = gst_static_caps_get(&padtemplate->static_caps); + if (!gst_caps_is_any (caps) && ! gst_caps_is_empty (caps)) { + for (guint i = 0; i < gst_caps_get_size(caps); i++) { + GstStructure *structure = gst_caps_get_structure(caps, i); + QString nameLowcase = QString(gst_structure_get_name (structure)).toLower(); + + m_supportedMimeTypeSet.insert(nameLowcase); + if (nameLowcase.contains("mpeg")) { + //Because mpeg version number is only included in the detail + //description, it is necessary to manually extract this information + //in order to match the mime type of mpeg4. + const GValue *value = gst_structure_get_value(structure, "mpegversion"); + if (value) { + gchar *str = gst_value_serialize (value); + QString versions(str); + QStringList elements = versions.split(QRegExp("\\D+"), QString::SkipEmptyParts); + foreach(const QString &e, elements) + m_supportedMimeTypeSet.insert(nameLowcase + e); + g_free (str); + } + } + } + } + } + } + gst_object_unref (factory); + } + } else if (GST_IS_TYPE_FIND_FACTORY(feature)) { + QString name(gst_plugin_feature_get_name(feature)); + if (name.contains('/')) //filter out any string without '/' which is obviously not a mime type + m_supportedMimeTypeSet.insert(name.toLower()); + } + } + features = g_list_next (features); + } + gst_plugin_feature_list_free (orig_features); + } + gst_plugin_list_free (orig_plugins); + +#if defined QT_SUPPORTEDMIMETYPES_DEBUG + QStringList list = m_supportedMimeTypeSet.toList(); + list.sort(); + if (qgetenv("QT_DEBUG_PLUGINS").toInt() > 0) { + foreach(const QString &type, list) + qDebug() << type; + } +#endif +} + +QStringList QGstreamerServicePlugin::supportedMimeTypes() const +{ + return QStringList(); +} + +Q_EXPORT_PLUGIN2(qtmedia_gstengine, QGstreamerServicePlugin); diff --git a/src/plugins/gstreamer/qgstreamerserviceplugin.h b/src/plugins/gstreamer/qgstreamerserviceplugin.h new file mode 100644 index 000000000..0ce0bbd84 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamerserviceplugin.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QGSTREAMERSERVICEPLUGIN_H +#define QGSTREAMERSERVICEPLUGIN_H + +#include +#include + +QT_USE_NAMESPACE + + +class QGstreamerServicePlugin + : public QMediaServiceProviderPlugin + , public QMediaServiceSupportedDevicesInterface + , public QMediaServiceFeaturesInterface + , public QMediaServiceSupportedFormatsInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) + Q_INTERFACES(QMediaServiceFeaturesInterface) + Q_INTERFACES(QMediaServiceSupportedFormatsInterface) +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; + + QList devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + QVariant deviceProperty(const QByteArray &service, const QByteArray &device, const QByteArray &property); + + QtMultimediaKit::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const; + QStringList supportedMimeTypes() const; + +private: + void updateDevices() const; + + mutable QList m_cameraDevices; + mutable QStringList m_cameraDescriptions; + mutable QSet m_supportedMimeTypeSet; //for fast access + + void updateSupportedMimeTypes() const; +}; + +#endif // QGSTREAMERSERVICEPLUGIN_H diff --git a/src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.cpp b/src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.cpp new file mode 100644 index 000000000..d53497d30 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.cpp @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideoinputdevicecontrol.h" + +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include + +QGstreamerVideoInputDeviceControl::QGstreamerVideoInputDeviceControl(QObject *parent) + :QVideoDeviceControl(parent), m_selectedDevice(0) +{ + update(); +} + +QGstreamerVideoInputDeviceControl::~QGstreamerVideoInputDeviceControl() +{ +} + +int QGstreamerVideoInputDeviceControl::deviceCount() const +{ + return m_names.size(); +} + +QString QGstreamerVideoInputDeviceControl::deviceName(int index) const +{ + return m_names[index]; +} + +QString QGstreamerVideoInputDeviceControl::deviceDescription(int index) const +{ + return m_descriptions[index]; +} + +QIcon QGstreamerVideoInputDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index); + return QIcon(); +} + +int QGstreamerVideoInputDeviceControl::defaultDevice() const +{ + return 0; +} + +int QGstreamerVideoInputDeviceControl::selectedDevice() const +{ + return m_selectedDevice; +} + + +void QGstreamerVideoInputDeviceControl::setSelectedDevice(int index) +{ + if (index != m_selectedDevice) { + m_selectedDevice = index; + emit selectedDeviceChanged(index); + emit selectedDeviceChanged(deviceName(index)); + } +} + + +void QGstreamerVideoInputDeviceControl::update() +{ + m_names.clear(); + m_descriptions.clear(); + +#ifdef Q_WS_MAEMO_6 + m_names << QLatin1String("primary") << QLatin1String("secondary"); + m_descriptions << tr("Main camera") << tr("Front camera"); +#else + QDir devDir("/dev"); + devDir.setFilter(QDir::System); + + QFileInfoList entries = devDir.entryInfoList(QStringList() << "video*"); + + foreach( const QFileInfo &entryInfo, entries ) { + //qDebug() << "Try" << entryInfo.filePath(); + + int fd = ::open(entryInfo.filePath().toLatin1().constData(), O_RDWR ); + if (fd == -1) + continue; + + bool isCamera = false; + + v4l2_input input; + memset(&input, 0, sizeof(input)); + for (; ::ioctl(fd, VIDIOC_ENUMINPUT, &input) >= 0; ++input.index) { + if(input.type == V4L2_INPUT_TYPE_CAMERA || input.type == 0) { + isCamera = ::ioctl(fd, VIDIOC_S_INPUT, input.index) != 0; + break; + } + } + + if (isCamera) { + // find out its driver "name" + QString name; + struct v4l2_capability vcap; + memset(&vcap, 0, sizeof(struct v4l2_capability)); + + if (ioctl(fd, VIDIOC_QUERYCAP, &vcap) != 0) + name = entryInfo.fileName(); + else + name = QString((const char*)vcap.card); + //qDebug() << "found camera: " << name; + + m_names.append(entryInfo.filePath()); + m_descriptions.append(name); + } + ::close(fd); + } +#endif +} diff --git a/src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.h b/src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.h new file mode 100644 index 000000000..a31636239 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideoinputdevicecontrol.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOINPUTDEVICECONTROL_H +#define QGSTREAMERVIDEOINPUTDEVICECONTROL_H + +#include +#include + +QT_USE_NAMESPACE + +class QGstreamerVideoInputDeviceControl : public QVideoDeviceControl +{ +Q_OBJECT +public: + QGstreamerVideoInputDeviceControl(QObject *parent); + ~QGstreamerVideoInputDeviceControl(); + + int deviceCount() const; + + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + + int defaultDevice() const; + int selectedDevice() const; + +public Q_SLOTS: + void setSelectedDevice(int index); + +private: + void update(); + + int m_selectedDevice; + QStringList m_names; + QStringList m_descriptions; +}; + +#endif // QGSTREAMERAUDIOINPUTDEVICECONTROL_H diff --git a/src/plugins/gstreamer/qgstreamervideooverlay.cpp b/src/plugins/gstreamer/qgstreamervideooverlay.cpp new file mode 100644 index 000000000..3e19dd075 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideooverlay.cpp @@ -0,0 +1,232 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideooverlay.h" +#include "qvideosurfacegstsink.h" + +#include + +#include "qx11videosurface.h" + +#ifndef QT_NO_XVIDEO + +QGstreamerVideoOverlay::QGstreamerVideoOverlay(QObject *parent) + : QVideoWindowControl(parent) + , m_surface(new QX11VideoSurface) + , m_videoSink(reinterpret_cast(QVideoSurfaceGstSink::createSink(m_surface))) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_fullScreen(false) +{ + if (m_videoSink) { + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + } + + connect(m_surface, SIGNAL(surfaceFormatChanged(QVideoSurfaceFormat)), + this, SLOT(surfaceFormatChanged())); +} + +QGstreamerVideoOverlay::~QGstreamerVideoOverlay() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_surface; +} + +WId QGstreamerVideoOverlay::winId() const +{ + return m_surface->winId(); +} + +void QGstreamerVideoOverlay::setWinId(WId id) +{ + bool wasReady = isReady(); + m_surface->setWinId(id); + + if (isReady() != wasReady) + emit readyChanged(!wasReady); +} + +QRect QGstreamerVideoOverlay::displayRect() const +{ + return m_displayRect; +} + +void QGstreamerVideoOverlay::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + setScaledDisplayRect(); +} + +Qt::AspectRatioMode QGstreamerVideoOverlay::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + setScaledDisplayRect(); +} + +void QGstreamerVideoOverlay::repaint() +{ +} + +int QGstreamerVideoOverlay::brightness() const +{ + return m_surface->brightness(); +} + +void QGstreamerVideoOverlay::setBrightness(int brightness) +{ + m_surface->setBrightness(brightness); + + emit brightnessChanged(m_surface->brightness()); +} + +int QGstreamerVideoOverlay::contrast() const +{ + return m_surface->contrast(); +} + +void QGstreamerVideoOverlay::setContrast(int contrast) +{ + m_surface->setContrast(contrast); + + emit contrastChanged(m_surface->contrast()); +} + +int QGstreamerVideoOverlay::hue() const +{ + return m_surface->hue(); +} + +void QGstreamerVideoOverlay::setHue(int hue) +{ + m_surface->setHue(hue); + + emit hueChanged(m_surface->hue()); +} + +int QGstreamerVideoOverlay::saturation() const +{ + return m_surface->saturation(); +} + +void QGstreamerVideoOverlay::setSaturation(int saturation) +{ + m_surface->setSaturation(saturation); + + emit saturationChanged(m_surface->saturation()); +} + +bool QGstreamerVideoOverlay::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoOverlay::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QGstreamerVideoOverlay::nativeSize() const +{ + return m_surface->surfaceFormat().sizeHint(); +} + +QAbstractVideoSurface *QGstreamerVideoOverlay::surface() const +{ + return m_surface; +} + +GstElement *QGstreamerVideoOverlay::videoSink() +{ + return m_videoSink; +} + +void QGstreamerVideoOverlay::surfaceFormatChanged() +{ + setScaledDisplayRect(); + + emit nativeSizeChanged(); +} + +void QGstreamerVideoOverlay::setScaledDisplayRect() +{ + QRect formatViewport = m_surface->surfaceFormat().viewport(); + + switch (m_aspectRatioMode) { + case Qt::KeepAspectRatio: + { + QSize size = m_surface->surfaceFormat().sizeHint(); + size.scale(m_displayRect.size(), Qt::KeepAspectRatio); + + QRect rect(QPoint(0, 0), size); + rect.moveCenter(m_displayRect.center()); + + m_surface->setDisplayRect(rect); + m_surface->setViewport(formatViewport); + } + break; + case Qt::IgnoreAspectRatio: + m_surface->setDisplayRect(m_displayRect); + m_surface->setViewport(formatViewport); + break; + case Qt::KeepAspectRatioByExpanding: + { + QSize size = m_displayRect.size(); + size.scale(m_surface->surfaceFormat().sizeHint(), Qt::KeepAspectRatio); + + QRect viewport(QPoint(0, 0), size); + viewport.moveCenter(formatViewport.center()); + m_surface->setDisplayRect(m_displayRect); + m_surface->setViewport(viewport); + } + break; + }; +} + +#endif //QT_NO_XVIDEO diff --git a/src/plugins/gstreamer/qgstreamervideooverlay.h b/src/plugins/gstreamer/qgstreamervideooverlay.h new file mode 100644 index 000000000..194c4ea32 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideooverlay.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOVERLAY_H +#define QGSTREAMERVIDEOOVERLAY_H + +#include + +#include "qgstreamervideorendererinterface.h" + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; +QT_END_NAMESPACE +class QX11VideoSurface; + +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) + +QT_USE_NAMESPACE + +class QGstreamerVideoOverlay : public QVideoWindowControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoOverlay(QObject *parent = 0); + ~QGstreamerVideoOverlay(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + void repaint(); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QAbstractVideoSurface *surface() const; + + GstElement *videoSink(); + + bool isReady() const { return winId() != 0; } + +signals: + void sinkChanged(); + void readyChanged(bool); + +private slots: + void surfaceFormatChanged(); + +private: + void setScaledDisplayRect(); + + QX11VideoSurface *m_surface; + GstElement *m_videoSink; + Qt::AspectRatioMode m_aspectRatioMode; + QRect m_displayRect; + bool m_fullScreen; +}; + +#endif //QT_NO_XVIDEO + +#endif diff --git a/src/plugins/gstreamer/qgstreamervideorenderer.cpp b/src/plugins/gstreamer/qgstreamervideorenderer.cpp new file mode 100644 index 000000000..6c4e6b90c --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideorenderer.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideorenderer.h" +#include "qvideosurfacegstsink.h" +#include "qabstractvideosurface.h" + +#include +#include +#include + +#include + +QGstreamerVideoRenderer::QGstreamerVideoRenderer(QObject *parent) + :QVideoRendererControl(parent),m_videoSink(0), m_surface(0) +{ +} + +QGstreamerVideoRenderer::~QGstreamerVideoRenderer() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); +} + +GstElement *QGstreamerVideoRenderer::videoSink() +{ + if (!m_videoSink && m_surface) { + m_videoSink = QVideoSurfaceGstSink::createSink(m_surface); + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + } + + return reinterpret_cast(m_videoSink); +} + + +QAbstractVideoSurface *QGstreamerVideoRenderer::surface() const +{ + return m_surface; +} + +void QGstreamerVideoRenderer::setSurface(QAbstractVideoSurface *surface) +{ + if (m_surface != surface) { + //qDebug() << Q_FUNC_INFO << surface; + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = 0; + + if (m_surface) { + disconnect(m_surface, SIGNAL(supportedFormatsChanged()), + this, SLOT(handleFormatChange())); + } + + m_surface = surface; + + if (surface && !m_surface) + emit readyChanged(true); + + if (!surface && m_surface) + emit readyChanged(false); + + if (m_surface) { + connect(m_surface, SIGNAL(supportedFormatsChanged()), + this, SLOT(handleFormatChange())); + } + + emit sinkChanged(); + } +} + +void QGstreamerVideoRenderer::handleFormatChange() +{ + //qDebug() << "Supported formats list has changed, reload video output"; + + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + m_videoSink = 0; + emit sinkChanged(); +} diff --git a/src/plugins/gstreamer/qgstreamervideorenderer.h b/src/plugins/gstreamer/qgstreamervideorenderer.h new file mode 100644 index 000000000..b6c23ba51 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideorenderer.h @@ -0,0 +1,80 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEORENDERER_H +#define QGSTREAMERVIDEORENDERER_H + +#include +#include "qvideosurfacegstsink.h" + +#include "qgstreamervideorendererinterface.h" + +QT_USE_NAMESPACE + +class QGstreamerVideoRenderer : public QVideoRendererControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoRenderer(QObject *parent = 0); + virtual ~QGstreamerVideoRenderer(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + GstElement *videoSink(); + void precessNewStream() {} + + bool isReady() const { return m_surface != 0; } + +signals: + void sinkChanged(); + void readyChanged(bool); + +private slots: + void handleFormatChange(); + +private: + QVideoSurfaceGstSink *m_videoSink; + QAbstractVideoSurface *m_surface; +}; + +#endif // QGSTREAMERVIDEORENDRER_H diff --git a/src/plugins/gstreamer/qgstreamervideorendererinterface.cpp b/src/plugins/gstreamer/qgstreamervideorendererinterface.cpp new file mode 100644 index 000000000..b8358ba1c --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideorendererinterface.cpp @@ -0,0 +1,46 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideorendererinterface.h" + +QGstreamerVideoRendererInterface::~QGstreamerVideoRendererInterface() +{ +} diff --git a/src/plugins/gstreamer/qgstreamervideorendererinterface.h b/src/plugins/gstreamer/qgstreamervideorendererinterface.h new file mode 100644 index 000000000..08b046d9b --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideorendererinterface.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOOUTPUTCONTROL_H +#define QGSTREAMERVIDEOOUTPUTCONTROL_H + +#include + +#include + +class QGstreamerVideoRendererInterface +{ +public: + virtual ~QGstreamerVideoRendererInterface(); + virtual GstElement *videoSink() = 0; + virtual void precessNewStream() {} + + //stopRenderer() is called when the renderer element is stopped. + //it can be reimplemented when video renderer can't detect + //changes to NULL state but has to free video resources. + virtual void stopRenderer() {} + + //the video output is configured, usually after the first paint event + //(winId is known, + virtual bool isReady() const { return true; } + + //video renderer may handle video sink specific gstreamer messages. + virtual void handleBusMessage(GstMessage*) {}; + virtual void handleSyncMessage(GstMessage*) {}; + + //signals: + //void sinkChanged(); + //void readyChanged(bool); +}; + +#define QGstreamerVideoRendererInterface_iid "com.nokia.Qt.QGstreamerVideoRendererInterface/1.0" +QT_BEGIN_NAMESPACE +Q_DECLARE_INTERFACE(QGstreamerVideoRendererInterface, QGstreamerVideoRendererInterface_iid) +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/gstreamer/qgstreamervideowidget.cpp b/src/plugins/gstreamer/qgstreamervideowidget.cpp new file mode 100644 index 000000000..38fa5ba8e --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideowidget.cpp @@ -0,0 +1,331 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideowidget.h" +#include "qgstutils.h" + +#include +#include +#include +#include + +#ifdef Q_WS_X11 +# include +#endif +#include +#include +#include + +class QGstreamerVideoWidget : public QWidget +{ +public: + QGstreamerVideoWidget(QWidget *parent = 0) + :QWidget(parent) + { + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + QPalette palette; + palette.setColor(QPalette::Background, Qt::black); + setPalette(palette); + } + + virtual ~QGstreamerVideoWidget() {} + + QSize sizeHint() const + { + return m_nativeSize; + } + + void setNativeSize( const QSize &size) + { + if (size != m_nativeSize) { + m_nativeSize = size; + if (size.isEmpty()) + setMinimumSize(0,0); + else + setMinimumSize(160,120); + + updateGeometry(); + } + } + +protected: + void paintEvent(QPaintEvent *) + { + QPainter painter(this); + painter.fillRect(rect(), palette().background()); + } + + QSize m_nativeSize; +}; + +QGstreamerVideoWidgetControl::QGstreamerVideoWidgetControl(QObject *parent) + : QVideoWidgetControl(parent) + , m_videoSink(0) + , m_widget(0) + , m_fullScreen(false) +{ +} + +QGstreamerVideoWidgetControl::~QGstreamerVideoWidgetControl() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); + + delete m_widget; +} + +void QGstreamerVideoWidgetControl::createVideoWidget() +{ + if (m_widget) + return; + + m_widget = new QGstreamerVideoWidget; + + m_widget->installEventFilter(this); + m_windowId = m_widget->winId(); + + m_videoSink = gst_element_factory_make ("xvimagesink", NULL); + if (m_videoSink) { + // Check if the xv sink is usable + if (gst_element_set_state(m_videoSink, GST_STATE_READY) != GST_STATE_CHANGE_SUCCESS) { + gst_object_unref(GST_OBJECT(m_videoSink)); + m_videoSink = 0; + } else { + gst_element_set_state(m_videoSink, GST_STATE_NULL); + + g_object_set(G_OBJECT(m_videoSink), "force-aspect-ratio", 1, (const char*)NULL); +#ifdef Q_WS_MAEMO_5 + //the overlay xvideo adapter fails to switch winId, + //use "SGX Textured Video" adapter instead + g_object_set(G_OBJECT(m_videoSink), "device", "1", NULL); +#endif + } + } + + if (!m_videoSink) + m_videoSink = gst_element_factory_make ("ximagesink", NULL); + + gst_object_ref (GST_OBJECT (m_videoSink)); //Take ownership + gst_object_sink (GST_OBJECT (m_videoSink)); + + +} + +GstElement *QGstreamerVideoWidgetControl::videoSink() +{ + createVideoWidget(); + return m_videoSink; +} + +bool QGstreamerVideoWidgetControl::eventFilter(QObject *object, QEvent *e) +{ + if (m_widget && object == m_widget) { + if (e->type() == QEvent::ParentChange || e->type() == QEvent::Show) { + WId newWId = m_widget->winId(); + if (newWId != m_windowId) { + m_windowId = newWId; + // Even if we have created a winId at this point, other X applications + // need to be aware of it. + QApplication::syncX(); + setOverlay(); + } + } + + if (e->type() == QEvent::Show) { + // Setting these values ensures smooth resizing since it + // will prevent the system from clearing the background + m_widget->setAttribute(Qt::WA_NoSystemBackground, true); + m_widget->setAttribute(Qt::WA_PaintOnScreen, true); + } else if (e->type() == QEvent::Resize) { + // This is a workaround for missing background repaints + // when reducing window size + windowExposed(); + } + } + + return false; +} + +void QGstreamerVideoWidgetControl::precessNewStream() +{ + setOverlay(); + QMetaObject::invokeMethod(this, "updateNativeVideoSize", Qt::QueuedConnection); +} + +void QGstreamerVideoWidgetControl::setOverlay() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + } +} + +void QGstreamerVideoWidgetControl::updateNativeVideoSize() +{ + if (m_videoSink) { + //find video native size to update video widget size hint + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + m_widget->setNativeSize(QGstUtils::capsCorrectedResolution(caps)); + gst_caps_unref(caps); + } + } else { + if (m_widget) + m_widget->setNativeSize(QSize()); + } +} + + +void QGstreamerVideoWidgetControl::windowExposed() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); +} + +QWidget *QGstreamerVideoWidgetControl::videoWidget() +{ + createVideoWidget(); + return m_widget; +} + +Qt::AspectRatioMode QGstreamerVideoWidgetControl::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + if (m_videoSink) { + g_object_set(G_OBJECT(m_videoSink), + "force-aspect-ratio", + (mode == Qt::KeepAspectRatio), + (const char*)NULL); + } + + m_aspectRatioMode = mode; +} + +bool QGstreamerVideoWidgetControl::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoWidgetControl::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +int QGstreamerVideoWidgetControl::brightness() const +{ + int brightness = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); + + return brightness / 10; +} + +void QGstreamerVideoWidgetControl::setBrightness(int brightness) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) { + g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, NULL); + + emit brightnessChanged(brightness); + } +} + +int QGstreamerVideoWidgetControl::contrast() const +{ + int contrast = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); + + return contrast / 10; +} + +void QGstreamerVideoWidgetControl::setContrast(int contrast) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) { + g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, NULL); + + emit contrastChanged(contrast); + } +} + +int QGstreamerVideoWidgetControl::hue() const +{ + int hue = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); + + return hue / 10; +} + +void QGstreamerVideoWidgetControl::setHue(int hue) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) { + g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, NULL); + + emit hueChanged(hue); + } +} + +int QGstreamerVideoWidgetControl::saturation() const +{ + int saturation = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); + + return saturation / 10; +} + +void QGstreamerVideoWidgetControl::setSaturation(int saturation) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) { + g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, NULL); + + emit saturationChanged(saturation); + } +} diff --git a/src/plugins/gstreamer/qgstreamervideowidget.h b/src/plugins/gstreamer/qgstreamervideowidget.h new file mode 100644 index 000000000..3342351d1 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideowidget.h @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOWIDGET_H +#define QGSTREAMERVIDEOWIDGET_H + +#include + +#include "qgstreamervideorendererinterface.h" + +QT_USE_NAMESPACE + +class QGstreamerVideoWidget; + +class QGstreamerVideoWidgetControl + : public QVideoWidgetControl + , public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) +public: + QGstreamerVideoWidgetControl(QObject *parent = 0); + virtual ~QGstreamerVideoWidgetControl(); + + GstElement *videoSink(); + void precessNewStream(); + + QWidget *videoWidget(); + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + void setOverlay(); + + bool eventFilter(QObject *object, QEvent *event); + +public slots: + void updateNativeVideoSize(); + +signals: + void sinkChanged(); + void readyChanged(bool); + +private: + void createVideoWidget(); + void windowExposed(); + + GstElement *m_videoSink; + QGstreamerVideoWidget *m_widget; + WId m_windowId; + Qt::AspectRatioMode m_aspectRatioMode; + bool m_fullScreen; +}; + +#endif // QGSTREAMERVIDEOWIDGET_H diff --git a/src/plugins/gstreamer/qgstreamervideowindow.cpp b/src/plugins/gstreamer/qgstreamervideowindow.cpp new file mode 100644 index 000000000..565000176 --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideowindow.cpp @@ -0,0 +1,342 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstreamervideowindow.h" +#include "qgstutils.h" + +#include + +#include +#include +#include + + +#ifndef QT_NO_XVIDEO + +/* + QGstreamerVideoWindow is similar to QGstreamerVideoOverlay, + but uses xvimagesink like gstreamer element instead of QX11VideoSurface. + + This allows to use the accelerated elements if available on the target platform, + but requires at least 0.10.29 gstreamer version + with gst_x_overlay_set_render_rectangle to set display rect. +*/ + +QGstreamerVideoWindow::QGstreamerVideoWindow(QObject *parent, const char *elementName) + : QVideoWindowControl(parent) + , m_videoSink(0) + , m_windowId(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_fullScreen(false) + , m_colorKey(QColor::Invalid) +{ + if (elementName) + m_videoSink = gst_element_factory_make(elementName, NULL); + else + m_videoSink = gst_element_factory_make("xvimagesink", NULL); + + if (m_videoSink) { + gst_object_ref(GST_OBJECT(m_videoSink)); //Take ownership + gst_object_sink(GST_OBJECT(m_videoSink)); + + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); + } +} + +QGstreamerVideoWindow::~QGstreamerVideoWindow() +{ + if (m_videoSink) + gst_object_unref(GST_OBJECT(m_videoSink)); +} + +WId QGstreamerVideoWindow::winId() const +{ + return m_windowId; +} + +void QGstreamerVideoWindow::setWinId(WId id) +{ + if (m_windowId == id) + return; + + qDebug() << Q_FUNC_INFO << id; + + WId oldId = m_windowId; + + m_windowId = id; + + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + } + + if (!oldId) + emit readyChanged(true); + + if (!id) + emit readyChanged(false); +} + +void QGstreamerVideoWindow::precessNewStream() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + gst_x_overlay_set_xwindow_id(GST_X_OVERLAY(m_videoSink), m_windowId); + + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + m_bufferProbeId = gst_pad_add_buffer_probe(pad, G_CALLBACK(padBufferProbe), this); + } +} + +QRect QGstreamerVideoWindow::displayRect() const +{ + return m_displayRect; +} + +void QGstreamerVideoWindow::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { +#if GST_VERSION_MICRO >= 29 + if (m_displayRect.isEmpty()) + gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), -1, -1, -1, -1); + else + gst_x_overlay_set_render_rectangle(GST_X_OVERLAY(m_videoSink), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + repaint(); +#endif + } +} + +Qt::AspectRatioMode QGstreamerVideoWindow::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QGstreamerVideoWindow::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + if (m_videoSink) { + g_object_set(G_OBJECT(m_videoSink), + "force-aspect-ratio", + (m_aspectRatioMode == Qt::KeepAspectRatio), + (const char*)NULL); + } +} + +void QGstreamerVideoWindow::repaint() +{ + if (m_videoSink && GST_IS_X_OVERLAY(m_videoSink)) { + //don't call gst_x_overlay_expose if the sink is in null state + GstState state = GST_STATE_NULL; + GstStateChangeReturn res = gst_element_get_state(m_videoSink, &state, NULL, 1000000); + if (res != GST_STATE_CHANGE_FAILURE && state != GST_STATE_NULL) { + gst_x_overlay_expose(GST_X_OVERLAY(m_videoSink)); + } + } +} + +QColor QGstreamerVideoWindow::colorKey() const +{ + if (!m_colorKey.isValid()) { + gint colorkey = 0; + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "colorkey")) + g_object_get(G_OBJECT(m_videoSink), "colorkey", &colorkey, NULL); + + if (colorkey > 0) + m_colorKey.setRgb(colorkey); + } + + return m_colorKey; +} + +void QGstreamerVideoWindow::setColorKey(const QColor &color) +{ + m_colorKey = color; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "colorkey")) + g_object_set(G_OBJECT(m_videoSink), "colorkey", color.rgba(), NULL); +} + +bool QGstreamerVideoWindow::autopaintColorKey() const +{ + bool enabled = true; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "autopaint-colorkey")) + g_object_get(G_OBJECT(m_videoSink), "autopaint-colorkey", &enabled, NULL); + + return enabled; +} + +void QGstreamerVideoWindow::setAutopaintColorKey(bool enabled) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "autopaint-colorkey")) + g_object_set(G_OBJECT(m_videoSink), "autopaint-colorkey", enabled, NULL); +} + +int QGstreamerVideoWindow::brightness() const +{ + int brightness = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) + g_object_get(G_OBJECT(m_videoSink), "brightness", &brightness, NULL); + + return brightness / 10; +} + +void QGstreamerVideoWindow::setBrightness(int brightness) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "brightness")) { + g_object_set(G_OBJECT(m_videoSink), "brightness", brightness * 10, NULL); + + emit brightnessChanged(brightness); + } +} + +int QGstreamerVideoWindow::contrast() const +{ + int contrast = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) + g_object_get(G_OBJECT(m_videoSink), "contrast", &contrast, NULL); + + return contrast / 10; +} + +void QGstreamerVideoWindow::setContrast(int contrast) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "contrast")) { + g_object_set(G_OBJECT(m_videoSink), "contrast", contrast * 10, NULL); + + emit contrastChanged(contrast); + } +} + +int QGstreamerVideoWindow::hue() const +{ + int hue = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) + g_object_get(G_OBJECT(m_videoSink), "hue", &hue, NULL); + + return hue / 10; +} + +void QGstreamerVideoWindow::setHue(int hue) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "hue")) { + g_object_set(G_OBJECT(m_videoSink), "hue", hue * 10, NULL); + + emit hueChanged(hue); + } +} + +int QGstreamerVideoWindow::saturation() const +{ + int saturation = 0; + + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) + g_object_get(G_OBJECT(m_videoSink), "saturation", &saturation, NULL); + + return saturation / 10; +} + +void QGstreamerVideoWindow::setSaturation(int saturation) +{ + if (m_videoSink && g_object_class_find_property(G_OBJECT_GET_CLASS(m_videoSink), "saturation")) { + g_object_set(G_OBJECT(m_videoSink), "saturation", saturation * 10, NULL); + + emit saturationChanged(saturation); + } +} + +bool QGstreamerVideoWindow::isFullScreen() const +{ + return m_fullScreen; +} + +void QGstreamerVideoWindow::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QGstreamerVideoWindow::nativeSize() const +{ + return m_nativeSize; +} + +void QGstreamerVideoWindow::padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data) +{ + QGstreamerVideoWindow *control = reinterpret_cast(user_data); + QMetaObject::invokeMethod(control, "updateNativeVideoSize", Qt::QueuedConnection); + gst_pad_remove_buffer_probe(pad, control->m_bufferProbeId); +} + +void QGstreamerVideoWindow::updateNativeVideoSize() +{ + const QSize oldSize = m_nativeSize; + m_nativeSize = QSize(); + + if (m_videoSink) { + //find video native size to update video widget size hint + GstPad *pad = gst_element_get_static_pad(m_videoSink,"sink"); + GstCaps *caps = gst_pad_get_negotiated_caps(pad); + + if (caps) { + m_nativeSize = QGstUtils::capsCorrectedResolution(caps); + gst_caps_unref(caps); + } + } + + if (m_nativeSize != oldSize) + emit nativeSizeChanged(); +} + +GstElement *QGstreamerVideoWindow::videoSink() +{ + return m_videoSink; +} + +#endif //QT_NO_XVIDEO diff --git a/src/plugins/gstreamer/qgstreamervideowindow.h b/src/plugins/gstreamer/qgstreamervideowindow.h new file mode 100644 index 000000000..e2229ae9e --- /dev/null +++ b/src/plugins/gstreamer/qgstreamervideowindow.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTREAMERVIDEOWINDOW_H +#define QGSTREAMERVIDEOWINDOW_H + +#include + +#include "qgstreamervideorendererinterface.h" + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; +QT_END_NAMESPACE +class QX11VideoSurface; + +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) + +QT_USE_NAMESPACE + +class QGstreamerVideoWindow : public QVideoWindowControl, public QGstreamerVideoRendererInterface +{ + Q_OBJECT + Q_INTERFACES(QGstreamerVideoRendererInterface) + Q_PROPERTY(QColor colorKey READ colorKey WRITE setColorKey) + Q_PROPERTY(bool autopaintColorKey READ autopaintColorKey WRITE setAutopaintColorKey) +public: + QGstreamerVideoWindow(QObject *parent = 0, const char *elementName = 0); + ~QGstreamerVideoWindow(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + QColor colorKey() const; + void setColorKey(const QColor &); + + bool autopaintColorKey() const; + void setAutopaintColorKey(bool); + + void repaint(); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QAbstractVideoSurface *surface() const; + + GstElement *videoSink(); + + void precessNewStream(); + bool isReady() const { return m_windowId != 0; } + +signals: + void sinkChanged(); + void readyChanged(bool); + +private slots: + void updateNativeVideoSize(); + +private: + static void padBufferProbe(GstPad *pad, GstBuffer *buffer, gpointer user_data); + + GstElement *m_videoSink; + WId m_windowId; + Qt::AspectRatioMode m_aspectRatioMode; + QRect m_displayRect; + bool m_fullScreen; + QSize m_nativeSize; + mutable QColor m_colorKey; + int m_bufferProbeId; +}; + +#endif //QT_NO_XVIDEO + +#endif diff --git a/src/plugins/gstreamer/qgstutils.cpp b/src/plugins/gstreamer/qgstutils.cpp new file mode 100644 index 000000000..a0f2a9832 --- /dev/null +++ b/src/plugins/gstreamer/qgstutils.cpp @@ -0,0 +1,165 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstutils.h" + +#include +#include +#include +#include + +//internal +static void addTagToMap(const GstTagList *list, + const gchar *tag, + gpointer user_data) +{ + QMap *map = reinterpret_cast* >(user_data); + + GValue val; + val.g_type = 0; + gst_tag_list_copy_value(&val,list,tag); + + switch( G_VALUE_TYPE(&val) ) { + case G_TYPE_STRING: + { + const gchar *str_value = g_value_get_string(&val); + map->insert(QByteArray(tag), QString::fromUtf8(str_value)); + break; + } + case G_TYPE_INT: + map->insert(QByteArray(tag), g_value_get_int(&val)); + break; + case G_TYPE_UINT: + map->insert(QByteArray(tag), g_value_get_uint(&val)); + break; + case G_TYPE_LONG: + map->insert(QByteArray(tag), qint64(g_value_get_long(&val))); + break; + case G_TYPE_BOOLEAN: + map->insert(QByteArray(tag), g_value_get_boolean(&val)); + break; + case G_TYPE_CHAR: + map->insert(QByteArray(tag), g_value_get_char(&val)); + break; + case G_TYPE_DOUBLE: + map->insert(QByteArray(tag), g_value_get_double(&val)); + break; + default: + // GST_TYPE_DATE is a function, not a constant, so pull it out of the switch + if (G_VALUE_TYPE(&val) == GST_TYPE_DATE) { + const GDate *date = gst_value_get_date(&val); + if (g_date_valid(date)) { + int year = g_date_get_year(date); + int month = g_date_get_month(date); + int day = g_date_get_day(date); + map->insert(QByteArray(tag), QDate(year,month,day)); + if (!map->contains("year")) + map->insert("year", year); + } + } else if (G_VALUE_TYPE(&val) == GST_TYPE_FRACTION) { + int nom = gst_value_get_fraction_numerator(&val); + int denom = gst_value_get_fraction_denominator(&val); + + if (denom > 0) { + map->insert(QByteArray(tag), double(nom)/denom); + } + } + break; + } + + g_value_unset(&val); +} + +/*! + Convert GstTagList structure to QMap. + + Mapping to int, bool, char, string, fractions and date are supported. + Fraction values are converted to doubles. +*/ +QMap QGstUtils::gstTagListToMap(const GstTagList *tags) +{ + QMap res; + gst_tag_list_foreach(tags, addTagToMap, &res); + + return res; +} + +/*! + Returns resolution of \a caps. + If caps doesn't have a valid size, and ampty QSize is returned. +*/ +QSize QGstUtils::capsResolution(const GstCaps *caps) +{ + QSize size; + + if (caps) { + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + } + + return size; +} + +/*! + Returns aspect ratio corrected resolution of \a caps. + If caps doesn't have a valid size, and ampty QSize is returned. +*/ +QSize QGstUtils::capsCorrectedResolution(const GstCaps *caps) +{ + QSize size; + + if (caps) { + const GstStructure *structure = gst_caps_get_structure(caps, 0); + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (!size.isEmpty() && gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + size.setWidth(qRound(size.width()*aspectNum/aspectDenum)); + } + } + + return size; +} diff --git a/src/plugins/gstreamer/qgstutils.h b/src/plugins/gstreamer/qgstutils.h new file mode 100644 index 000000000..396b32b98 --- /dev/null +++ b/src/plugins/gstreamer/qgstutils.h @@ -0,0 +1,59 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTUTILS_H +#define QGSTUTILS_H + +#include +#include + +class QSize; +class QVariant; +class QByteArray; + +namespace QGstUtils { + QMap gstTagListToMap(const GstTagList *list); + + QSize capsResolution(const GstCaps *caps); + QSize capsCorrectedResolution(const GstCaps *caps); +} + +#endif diff --git a/src/plugins/gstreamer/qgstvideobuffer.cpp b/src/plugins/gstreamer/qgstvideobuffer.cpp new file mode 100644 index 000000000..4ea6a7004 --- /dev/null +++ b/src/plugins/gstreamer/qgstvideobuffer.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qgstvideobuffer.h" + + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) + , m_mode(NotMapped) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + QGstVideoBuffer::HandleType handleType, + const QVariant &handle) + : QAbstractVideoBuffer(handleType) + , m_buffer(buffer) + , m_bytesPerLine(bytesPerLine) + , m_mode(NotMapped) + , m_handle(handle) +{ + gst_buffer_ref(m_buffer); +} + +QGstVideoBuffer::~QGstVideoBuffer() +{ + gst_buffer_unref(m_buffer); +} + + +QAbstractVideoBuffer::MapMode QGstVideoBuffer::mapMode() const +{ + return m_mode; +} + +uchar *QGstVideoBuffer::map(MapMode mode, int *numBytes, int *bytesPerLine) +{ + if (mode != NotMapped && m_mode == NotMapped) { + if (numBytes) + *numBytes = m_buffer->size; + + if (bytesPerLine) + *bytesPerLine = m_bytesPerLine; + + m_mode = mode; + + return m_buffer->data; + } else { + return 0; + } +} +void QGstVideoBuffer::unmap() +{ + m_mode = NotMapped; +} + diff --git a/src/plugins/gstreamer/qgstvideobuffer.h b/src/plugins/gstreamer/qgstvideobuffer.h new file mode 100644 index 000000000..8d5a36e53 --- /dev/null +++ b/src/plugins/gstreamer/qgstvideobuffer.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTVIDEOBUFFER_H +#define QGSTVIDEOBUFFER_H + +#include +#include + +#include + +class QGstVideoBuffer : public QAbstractVideoBuffer +{ +public: + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine); + QGstVideoBuffer(GstBuffer *buffer, int bytesPerLine, + HandleType handleType, const QVariant &handle); + ~QGstVideoBuffer(); + + MapMode mapMode() const; + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); + + QVariant handle() const { return m_handle; } +private: + GstBuffer *m_buffer; + int m_bytesPerLine; + MapMode m_mode; + QVariant m_handle; +}; + + +#endif diff --git a/src/plugins/gstreamer/qgstxvimagebuffer.cpp b/src/plugins/gstreamer/qgstxvimagebuffer.cpp new file mode 100644 index 000000000..393456291 --- /dev/null +++ b/src/plugins/gstreamer/qgstxvimagebuffer.cpp @@ -0,0 +1,311 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "qgstxvimagebuffer.h" +#include "qvideosurfacegstsink.h" +#include "qgstvideobuffer.h" + +#ifndef QT_NO_XVIDEO + +GstBufferClass *QGstXvImageBuffer::parent_class = NULL; + +GType QGstXvImageBuffer::get_type(void) +{ + static GType buffer_type = 0; + + if (buffer_type == 0) { + static const GTypeInfo buffer_info = { + sizeof (GstBufferClass), + NULL, + NULL, + QGstXvImageBuffer::class_init, + NULL, + NULL, + sizeof(QGstXvImageBuffer), + 0, + (GInstanceInitFunc)QGstXvImageBuffer::buffer_init, + NULL + }; + buffer_type = g_type_register_static(GST_TYPE_BUFFER, + "QGstXvImageBuffer", &buffer_info, GTypeFlags(0)); + } + return buffer_type; +} + +void QGstXvImageBuffer::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + GST_MINI_OBJECT_CLASS(g_class)->finalize = + (GstMiniObjectFinalizeFunction)buffer_finalize; + parent_class = (GstBufferClass*)g_type_class_peek_parent(g_class); +} + +void QGstXvImageBuffer::buffer_init(QGstXvImageBuffer *xvImage, gpointer g_class) +{ + Q_UNUSED(g_class); + xvImage->pool = 0; + xvImage->shmInfo.shmaddr = ((char *) -1); + xvImage->shmInfo.shmid = -1; + xvImage->markedForDeletion = false; +} + +void QGstXvImageBuffer::buffer_finalize(QGstXvImageBuffer * xvImage) +{ + if (xvImage->pool) { + if (xvImage->markedForDeletion) + xvImage->pool->destroyBuffer(xvImage); + else + xvImage->pool->recycleBuffer(xvImage); + } +} + + +QGstXvImageBufferPool::QGstXvImageBufferPool(QObject *parent) + :QObject(parent) +{ + m_threadId = QThread::currentThreadId(); +} + +QGstXvImageBufferPool::~QGstXvImageBufferPool() +{ +} + +bool QGstXvImageBufferPool::isFormatSupported(const QVideoSurfaceFormat &surfaceFormat) const +{ + bool ok = true; + surfaceFormat.property("portId").toULongLong(&ok); + if (!ok) + return false; + + int xvFormatId = surfaceFormat.property("xvFormatId").toInt(&ok); + if (!ok || xvFormatId < 0) + return false; + + int dataSize = surfaceFormat.property("dataSize").toInt(&ok); + if (!ok || dataSize<=0) + return false; + + return true; +} + +GType QGstXvImageBufferPool::bufferType() const +{ + return QGstXvImageBuffer::get_type(); +} + +GstBuffer *QGstXvImageBufferPool::takeBuffer( + const QVideoSurfaceFormat &format, GstCaps *caps) +{ + m_poolMutex.lock(); + + m_caps = caps; + if (format != m_format) { + doClear(); + m_format = format; + } + + + if (m_pool.isEmpty()) { + //qDebug() << "QGstXvImageBufferPool::takeBuffer: no buffer available, allocate the new one" << QThread::currentThreadId() << m_threadId; + if (QThread::currentThreadId() == m_threadId) { + doAlloc(); + } else { + QMetaObject::invokeMethod(this, "queuedAlloc", Qt::QueuedConnection); + m_allocWaitCondition.wait(&m_poolMutex, 300); + } + } + QGstXvImageBuffer *res = 0; + + if (!m_pool.isEmpty()) { + res = m_pool.takeLast(); + } + + m_poolMutex.unlock(); + + return GST_BUFFER(res); +} + +QAbstractVideoBuffer::HandleType QGstXvImageBufferPool::handleType() const +{ + return QAbstractVideoBuffer::XvShmImageHandle; +} + +QAbstractVideoBuffer *QGstXvImageBufferPool::prepareVideoBuffer(GstBuffer *buffer, int bytesPerLine) +{ + QGstXvImageBuffer *xvBuffer = reinterpret_cast(buffer); + QVariant handle = QVariant::fromValue(xvBuffer->xvImage); + return new QGstVideoBuffer(buffer, bytesPerLine, QAbstractVideoBuffer::XvShmImageHandle, handle); +} + +void QGstXvImageBufferPool::queuedAlloc() +{ + QMutexLocker lock(&m_poolMutex); + doAlloc(); + m_allocWaitCondition.wakeOne(); +} + +void QGstXvImageBufferPool::doAlloc() +{ + //should be always called from the main thread with m_poolMutex locked + //Q_ASSERT(QThread::currentThread() == thread()); + + XSync(QX11Info::display(), false); + + QGstXvImageBuffer *xvBuffer = (QGstXvImageBuffer *)gst_mini_object_new(QGstXvImageBuffer::get_type()); + + quint64 portId = m_format.property("portId").toULongLong(); + int xvFormatId = m_format.property("xvFormatId").toInt(); + + xvBuffer->xvImage = XvShmCreateImage( + QX11Info::display(), + portId, + xvFormatId, + 0, + m_format.frameWidth(), + m_format.frameHeight(), + &xvBuffer->shmInfo + ); + + if (!xvBuffer->xvImage) { + qWarning() << "QGstXvImageBufferPool: XvShmCreateImage failed"; + return; + } + + XSync(QX11Info::display(), false); + + xvBuffer->shmInfo.shmid = shmget(IPC_PRIVATE, xvBuffer->xvImage->data_size, IPC_CREAT | 0777); + xvBuffer->shmInfo.shmaddr = xvBuffer->xvImage->data = (char*)shmat(xvBuffer->shmInfo.shmid, 0, 0); + xvBuffer->shmInfo.readOnly = False; + + if (!XShmAttach(QX11Info::display(), &xvBuffer->shmInfo)) { + qWarning() << "QGstXvImageBufferPool: XShmAttach failed"; + return; + } + + XSync(QX11Info::display(), false); + + shmctl (xvBuffer->shmInfo.shmid, IPC_RMID, NULL); + + xvBuffer->pool = this; + GST_MINI_OBJECT_CAST(xvBuffer)->flags = 0; + gst_buffer_set_caps(GST_BUFFER_CAST(xvBuffer), m_caps); + GST_BUFFER_DATA(xvBuffer) = (uchar*)xvBuffer->xvImage->data; + GST_BUFFER_SIZE(xvBuffer) = xvBuffer->xvImage->data_size; + + m_allBuffers.append(xvBuffer); + m_pool.append(xvBuffer); + + XSync(QX11Info::display(), false); +} + + +void QGstXvImageBufferPool::clear() +{ + QMutexLocker lock(&m_poolMutex); + doClear(); +} + +void QGstXvImageBufferPool::doClear() +{ + foreach (QGstXvImageBuffer *xvBuffer, m_allBuffers) { + xvBuffer->markedForDeletion = true; + } + m_allBuffers.clear(); + + foreach (QGstXvImageBuffer *xvBuffer, m_pool) { + gst_buffer_unref(GST_BUFFER(xvBuffer)); + } + m_pool.clear(); + + m_format = QVideoSurfaceFormat(); +} + +void QGstXvImageBufferPool::queuedDestroy() +{ + QMutexLocker lock(&m_destroyMutex); + + XSync(QX11Info::display(), false); + + foreach(XvShmImage xvImage, m_imagesToDestroy) { + if (xvImage.shmInfo.shmaddr != ((void *) -1)) { + XShmDetach(QX11Info::display(), &xvImage.shmInfo); + XSync(QX11Info::display(), false); + + shmdt(xvImage.shmInfo.shmaddr); + } + + if (xvImage.xvImage) + XFree(xvImage.xvImage); + } + + m_imagesToDestroy.clear(); + + XSync(QX11Info::display(), false); +} + +void QGstXvImageBufferPool::recycleBuffer(QGstXvImageBuffer *xvBuffer) +{ + QMutexLocker lock(&m_poolMutex); + gst_buffer_ref(GST_BUFFER_CAST(xvBuffer)); + m_pool.append(xvBuffer); +} + +void QGstXvImageBufferPool::destroyBuffer(QGstXvImageBuffer *xvBuffer) +{ + XvShmImage imageToDestroy; + imageToDestroy.xvImage = xvBuffer->xvImage; + imageToDestroy.shmInfo = xvBuffer->shmInfo; + + m_destroyMutex.lock(); + m_imagesToDestroy.append(imageToDestroy); + m_destroyMutex.unlock(); + + if (m_imagesToDestroy.size() == 1) + QMetaObject::invokeMethod(this, "queuedDestroy", Qt::QueuedConnection); +} + +#endif //QT_NO_XVIDEO + diff --git a/src/plugins/gstreamer/qgstxvimagebuffer.h b/src/plugins/gstreamer/qgstxvimagebuffer.h new file mode 100644 index 000000000..2ab5ea03b --- /dev/null +++ b/src/plugins/gstreamer/qgstxvimagebuffer.h @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QGSTXVIMAGEBUFFER_H +#define QGSTXVIMAGEBUFFER_H + +#include +#include +#include +#include +#include + +#ifndef QT_NO_XVIDEO + +#include +#include +#include +#include +#include +#include +#include + + +#include +#include "qabstractgstbufferpool.h" + +class QGstXvImageBufferPool; + +struct QGstXvImageBuffer { + GstBuffer buffer; + QGstXvImageBufferPool *pool; + XvImage *xvImage; + XShmSegmentInfo shmInfo; + bool markedForDeletion; + + static GType get_type(void); + static void class_init(gpointer g_class, gpointer class_data); + static void buffer_init(QGstXvImageBuffer *xvimage, gpointer g_class); + static void buffer_finalize(QGstXvImageBuffer * xvimage); + static GstBufferClass *parent_class; +}; + +Q_DECLARE_METATYPE(XvImage*) + +class QGstXvImageBufferPool : public QObject, public QAbstractGstBufferPool { +Q_OBJECT +friend class QGstXvImageBuffer; +public: + QGstXvImageBufferPool(QObject *parent = 0); + virtual ~QGstXvImageBufferPool(); + + bool isFormatSupported(const QVideoSurfaceFormat &format) const; + + GType bufferType() const; + GstBuffer *takeBuffer(const QVideoSurfaceFormat &format, GstCaps *caps); + void clear(); + + QAbstractVideoBuffer::HandleType handleType() const; + QAbstractVideoBuffer *prepareVideoBuffer(GstBuffer *buffer, int bytesPerLine); + +private slots: + void queuedAlloc(); + void queuedDestroy(); + + void doClear(); + + void recycleBuffer(QGstXvImageBuffer *); + void destroyBuffer(QGstXvImageBuffer *); + +private: + void doAlloc(); + + struct XvShmImage { + XvImage *xvImage; + XShmSegmentInfo shmInfo; + }; + + QMutex m_poolMutex; + QMutex m_allocMutex; + QWaitCondition m_allocWaitCondition; + QMutex m_destroyMutex; + QVideoSurfaceFormat m_format; + GstCaps *m_caps; + QList m_pool; + QList m_allBuffers; + QList m_imagesToDestroy; + Qt::HANDLE m_threadId; +}; + +#endif //QT_NO_XVIDEO + +#endif diff --git a/src/plugins/gstreamer/qvideosurfacegstsink.cpp b/src/plugins/gstreamer/qvideosurfacegstsink.cpp new file mode 100644 index 000000000..f3c0e3468 --- /dev/null +++ b/src/plugins/gstreamer/qvideosurfacegstsink.cpp @@ -0,0 +1,772 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include +#include + +#include "qgstvideobuffer.h" + +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) +#include +#include "qgstxvimagebuffer.h" +#endif + +#include "qvideosurfacegstsink.h" + +//#define DEBUG_VIDEO_SURFACE_SINK + + +Q_DECLARE_METATYPE(QVideoSurfaceFormat) + +QVideoSurfaceGstDelegate::QVideoSurfaceGstDelegate( + QAbstractVideoSurface *surface) + : m_surface(surface) + , m_pool(0) + , m_renderReturn(GST_FLOW_ERROR) + , m_bytesPerLine(0) +{ + if (m_surface) { +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) + m_pools.append(new QGstXvImageBufferPool()); +#endif + updateSupportedFormats(); + connect(m_surface, SIGNAL(supportedFormatsChanged()), this, SLOT(updateSupportedFormats())); + } +} + +QVideoSurfaceGstDelegate::~QVideoSurfaceGstDelegate() +{ + qDeleteAll(m_pools); +} + +QList QVideoSurfaceGstDelegate::supportedPixelFormats(QAbstractVideoBuffer::HandleType handleType) const +{ + QMutexLocker locker(const_cast(&m_mutex)); + + if (!m_surface) + return QList(); + else if (handleType == QAbstractVideoBuffer::NoHandle) + return m_supportedPixelFormats; + else if (handleType == m_pool->handleType()) + return m_supportedPoolPixelFormats; + else + return m_surface->supportedPixelFormats(handleType); +} + +QVideoSurfaceFormat QVideoSurfaceGstDelegate::surfaceFormat() const +{ + QMutexLocker locker(const_cast(&m_mutex)); + return m_format; +} + +bool QVideoSurfaceGstDelegate::start(const QVideoSurfaceFormat &format, int bytesPerLine) +{ + if (!m_surface) + return false; + + QMutexLocker locker(&m_mutex); + + m_format = format; + m_bytesPerLine = bytesPerLine; + + if (QThread::currentThread() == thread()) { + m_started = !m_surface.isNull() ? m_surface->start(m_format) : false; + } else { + QMetaObject::invokeMethod(this, "queuedStart", Qt::QueuedConnection); + + m_setupCondition.wait(&m_mutex); + } + + m_format = m_surface->surfaceFormat(); + + return m_started; +} + +void QVideoSurfaceGstDelegate::stop() +{ + if (!m_surface) + return; + + QMutexLocker locker(&m_mutex); + + if (QThread::currentThread() == thread()) { + if (!m_surface.isNull()) + m_surface->stop(); + } else { + QMetaObject::invokeMethod(this, "queuedStop", Qt::QueuedConnection); + + m_setupCondition.wait(&m_mutex); + } + + m_started = false; +} + +bool QVideoSurfaceGstDelegate::isActive() +{ + QMutexLocker locker(&m_mutex); + return !m_surface.isNull() && m_surface->isActive(); +} + +GstFlowReturn QVideoSurfaceGstDelegate::render(GstBuffer *buffer) +{ + if (!m_surface) { + qWarning() << "Rendering video frame to deleted surface, skip."; + //return GST_FLOW_NOT_NEGOTIATED; + return GST_FLOW_OK; + } + + QMutexLocker locker(&m_mutex); + + QAbstractVideoBuffer *videoBuffer = 0; + + if (m_pool && G_TYPE_CHECK_INSTANCE_TYPE(buffer, m_pool->bufferType())) + videoBuffer = m_pool->prepareVideoBuffer(buffer, m_bytesPerLine); + else + videoBuffer = new QGstVideoBuffer(buffer, m_bytesPerLine); + + m_frame = QVideoFrame( + videoBuffer, + m_format.frameSize(), + m_format.pixelFormat()); + + qint64 startTime = GST_BUFFER_TIMESTAMP(buffer); + + if (startTime >= 0) { + m_frame.setStartTime(startTime/G_GINT64_CONSTANT (1000000)); + + qint64 duration = GST_BUFFER_DURATION(buffer); + + if (duration >= 0) + m_frame.setEndTime((startTime + duration)/G_GINT64_CONSTANT (1000000)); + } + + QMetaObject::invokeMethod(this, "queuedRender", Qt::QueuedConnection); + + if (!m_renderCondition.wait(&m_mutex, 300)) { + m_frame = QVideoFrame(); + + return GST_FLOW_OK; + } else { + return m_renderReturn; + } +} + +void QVideoSurfaceGstDelegate::queuedStart() +{ + QMutexLocker locker(&m_mutex); + + m_started = m_surface->start(m_format); + + m_setupCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::queuedStop() +{ + QMutexLocker locker(&m_mutex); + + m_surface->stop(); + + m_setupCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::queuedRender() +{ + QMutexLocker locker(&m_mutex); + + if (m_surface.isNull()) { + qWarning() << "Rendering video frame to deleted surface, skip the frame"; + m_renderReturn = GST_FLOW_OK; + } else if (m_surface->present(m_frame)) { + m_renderReturn = GST_FLOW_OK; + } else { + switch (m_surface->error()) { + case QAbstractVideoSurface::NoError: + m_renderReturn = GST_FLOW_OK; + break; + case QAbstractVideoSurface::StoppedError: + //It's likely we are in process of changing video output + //and the surface is already stopped, ignore the frame + m_renderReturn = GST_FLOW_OK; + break; + default: + qWarning() << "Failed to render video frame:" << m_surface->error(); + m_renderReturn = GST_FLOW_OK; + break; + } + } + + m_renderCondition.wakeAll(); +} + +void QVideoSurfaceGstDelegate::updateSupportedFormats() +{ + QAbstractGstBufferPool *newPool = 0; + foreach (QAbstractGstBufferPool *pool, m_pools) { + if (!m_surface->supportedPixelFormats(pool->handleType()).isEmpty()) { + newPool = pool; + break; + } + } + + if (newPool != m_pool) { + QMutexLocker lock(&m_poolMutex); + + if (m_pool) + m_pool->clear(); + m_pool = newPool; + } + + QMutexLocker locker(&m_mutex); + + m_supportedPixelFormats.clear(); + m_supportedPoolPixelFormats.clear(); + if (m_surface) { + m_supportedPixelFormats = m_surface->supportedPixelFormats(); + if (m_pool) + m_supportedPoolPixelFormats = m_surface->supportedPixelFormats(m_pool->handleType()); + } +} + +struct YuvFormat +{ + QVideoFrame::PixelFormat pixelFormat; + guint32 fourcc; + int bitsPerPixel; +}; + +static const YuvFormat qt_yuvColorLookup[] = +{ + { QVideoFrame::Format_YUV420P, GST_MAKE_FOURCC('I','4','2','0'), 8 }, + { QVideoFrame::Format_YV12, GST_MAKE_FOURCC('Y','V','1','2'), 8 }, + { QVideoFrame::Format_UYVY, GST_MAKE_FOURCC('U','Y','V','Y'), 16 }, + { QVideoFrame::Format_YUYV, GST_MAKE_FOURCC('Y','U','Y','2'), 16 }, + { QVideoFrame::Format_NV12, GST_MAKE_FOURCC('N','V','1','2'), 8 }, + { QVideoFrame::Format_NV21, GST_MAKE_FOURCC('N','V','2','1'), 8 }, + { QVideoFrame::Format_AYUV444, GST_MAKE_FOURCC('A','Y','U','V'), 32 } +}; + +static int indexOfYuvColor(QVideoFrame::PixelFormat format) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].pixelFormat == format) + return i; + + return -1; +} + +static int indexOfYuvColor(guint32 fourcc) +{ + const int count = sizeof(qt_yuvColorLookup) / sizeof(YuvFormat); + + for (int i = 0; i < count; ++i) + if (qt_yuvColorLookup[i].fourcc == fourcc) + return i; + + return -1; +} + +struct RgbFormat +{ + QVideoFrame::PixelFormat pixelFormat; + int bitsPerPixel; + int depth; + int endianness; + int red; + int green; + int blue; + int alpha; +}; + +static const RgbFormat qt_rgbColorLookup[] = +{ + { QVideoFrame::Format_RGB32 , 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x00000000 }, + { QVideoFrame::Format_RGB32 , 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_BGR32 , 32, 24, 4321, 0xFF000000, 0x00FF0000, 0x0000FF00, 0x00000000 }, + { QVideoFrame::Format_BGR32 , 32, 24, 1234, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, + { QVideoFrame::Format_ARGB32, 32, 24, 4321, 0x0000FF00, 0x00FF0000, 0xFF000000, 0x000000FF }, + { QVideoFrame::Format_ARGB32, 32, 24, 1234, 0x00FF0000, 0x0000FF00, 0x000000FF, 0xFF000000 }, + { QVideoFrame::Format_RGB24 , 24, 24, 4321, 0x00FF0000, 0x0000FF00, 0x000000FF, 0x00000000 }, + { QVideoFrame::Format_BGR24 , 24, 24, 4321, 0x000000FF, 0x0000FF00, 0x00FF0000, 0x00000000 }, + { QVideoFrame::Format_RGB565, 16, 16, 1234, 0x0000F800, 0x000007E0, 0x0000001F, 0x00000000 } +}; + +static int indexOfRgbColor( + int bits, int depth, int endianness, int red, int green, int blue, int alpha) +{ + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].bitsPerPixel == bits + && qt_rgbColorLookup[i].depth == depth + && qt_rgbColorLookup[i].endianness == endianness + && qt_rgbColorLookup[i].red == red + && qt_rgbColorLookup[i].green == green + && qt_rgbColorLookup[i].blue == blue + && qt_rgbColorLookup[i].alpha == alpha) { + return i; + } + } + return -1; +} + +static GstVideoSinkClass *sink_parent_class; + +#define VO_SINK(s) QVideoSurfaceGstSink *sink(reinterpret_cast(s)) + +QVideoSurfaceGstSink *QVideoSurfaceGstSink::createSink(QAbstractVideoSurface *surface) +{ + QVideoSurfaceGstSink *sink = reinterpret_cast( + g_object_new(QVideoSurfaceGstSink::get_type(), 0)); + + sink->delegate = new QVideoSurfaceGstDelegate(surface); + + return sink; +} + +GType QVideoSurfaceGstSink::get_type() +{ + static GType type = 0; + + if (type == 0) { + static const GTypeInfo info = + { + sizeof(QVideoSurfaceGstSinkClass), // class_size + base_init, // base_init + NULL, // base_finalize + class_init, // class_init + NULL, // class_finalize + NULL, // class_data + sizeof(QVideoSurfaceGstSink), // instance_size + 0, // n_preallocs + instance_init, // instance_init + 0 // value_table + }; + + type = g_type_register_static( + GST_TYPE_VIDEO_SINK, "QVideoSurfaceGstSink", &info, GTypeFlags(0)); + } + + return type; +} + +void QVideoSurfaceGstSink::class_init(gpointer g_class, gpointer class_data) +{ + Q_UNUSED(class_data); + + sink_parent_class = reinterpret_cast(g_type_class_peek_parent(g_class)); + + GstBaseSinkClass *base_sink_class = reinterpret_cast(g_class); + base_sink_class->get_caps = QVideoSurfaceGstSink::get_caps; + base_sink_class->set_caps = QVideoSurfaceGstSink::set_caps; + base_sink_class->buffer_alloc = QVideoSurfaceGstSink::buffer_alloc; + base_sink_class->start = QVideoSurfaceGstSink::start; + base_sink_class->stop = QVideoSurfaceGstSink::stop; + // base_sink_class->unlock = QVideoSurfaceGstSink::unlock; // Not implemented. + // base_sink_class->event = QVideoSurfaceGstSink::event; // Not implemented. + base_sink_class->preroll = QVideoSurfaceGstSink::preroll; + base_sink_class->render = QVideoSurfaceGstSink::render; + + GstElementClass *element_class = reinterpret_cast(g_class); + element_class->change_state = QVideoSurfaceGstSink::change_state; + + GObjectClass *object_class = reinterpret_cast(g_class); + object_class->finalize = QVideoSurfaceGstSink::finalize; +} + +void QVideoSurfaceGstSink::base_init(gpointer g_class) +{ + static GstStaticPadTemplate sink_pad_template = GST_STATIC_PAD_TEMPLATE( + "sink", GST_PAD_SINK, GST_PAD_ALWAYS, GST_STATIC_CAPS( + "video/x-raw-rgb, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]; " + "video/x-raw-yuv, " + "framerate = (fraction) [ 0, MAX ], " + "width = (int) [ 1, MAX ], " + "height = (int) [ 1, MAX ]")); + + gst_element_class_add_pad_template( + GST_ELEMENT_CLASS(g_class), gst_static_pad_template_get(&sink_pad_template)); +} + +void QVideoSurfaceGstSink::instance_init(GTypeInstance *instance, gpointer g_class) +{ + VO_SINK(instance); + + Q_UNUSED(g_class); + + sink->delegate = 0; + + sink->lastRequestedCaps = 0; + sink->lastBufferCaps = 0; + sink->lastSurfaceFormat = new QVideoSurfaceFormat; +} + +void QVideoSurfaceGstSink::finalize(GObject *object) +{ + VO_SINK(object); + + delete sink->lastSurfaceFormat; + sink->lastSurfaceFormat = 0; + + if (sink->lastBufferCaps) + gst_caps_unref(sink->lastBufferCaps); + sink->lastBufferCaps = 0; + + if (sink->lastRequestedCaps) + gst_caps_unref(sink->lastRequestedCaps); + sink->lastRequestedCaps = 0; +} + +GstStateChangeReturn QVideoSurfaceGstSink::change_state( + GstElement *element, GstStateChange transition) +{ + Q_UNUSED(element); + + return GST_ELEMENT_CLASS(sink_parent_class)->change_state( + element, transition); +} + +GstCaps *QVideoSurfaceGstSink::get_caps(GstBaseSink *base) +{ + VO_SINK(base); + + GstCaps *caps = gst_caps_new_empty(); + + foreach (QVideoFrame::PixelFormat format, sink->delegate->supportedPixelFormats()) { + int index = indexOfYuvColor(format); + + if (index != -1) { + gst_caps_append_structure(caps, gst_structure_new( + "video/x-raw-yuv", + "framerate", GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "format" , GST_TYPE_FOURCC, qt_yuvColorLookup[index].fourcc, + NULL)); + continue; + } + + const int count = sizeof(qt_rgbColorLookup) / sizeof(RgbFormat); + + for (int i = 0; i < count; ++i) { + if (qt_rgbColorLookup[i].pixelFormat == format) { + GstStructure *structure = gst_structure_new( + "video/x-raw-rgb", + "framerate" , GST_TYPE_FRACTION_RANGE, 0, 1, INT_MAX, 1, + "width" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "height" , GST_TYPE_INT_RANGE, 1, INT_MAX, + "bpp" , G_TYPE_INT, qt_rgbColorLookup[i].bitsPerPixel, + "depth" , G_TYPE_INT, qt_rgbColorLookup[i].depth, + "endianness", G_TYPE_INT, qt_rgbColorLookup[i].endianness, + "red_mask" , G_TYPE_INT, qt_rgbColorLookup[i].red, + "green_mask", G_TYPE_INT, qt_rgbColorLookup[i].green, + "blue_mask" , G_TYPE_INT, qt_rgbColorLookup[i].blue, + NULL); + + if (qt_rgbColorLookup[i].alpha != 0) { + gst_structure_set( + structure, "alpha_mask", G_TYPE_INT, qt_rgbColorLookup[i].alpha, NULL); + } + gst_caps_append_structure(caps, structure); + } + } + } + + return caps; +} + +gboolean QVideoSurfaceGstSink::set_caps(GstBaseSink *base, GstCaps *caps) +{ + VO_SINK(base); + +#ifdef DEBUG_VIDEO_SURFACE_SINK + qDebug() << "set_caps:"; + qDebug() << gst_caps_to_string(caps); +#endif + + if (!caps) { + sink->delegate->stop(); + + return TRUE; + } else { + int bytesPerLine = 0; + QVideoSurfaceFormat format = formatForCaps(caps, &bytesPerLine); + + if (sink->delegate->isActive()) { + QVideoSurfaceFormat surfaceFormst = sink->delegate->surfaceFormat(); + + if (format.pixelFormat() == surfaceFormst.pixelFormat() && + format.frameSize() == surfaceFormst.frameSize()) + return TRUE; + else + sink->delegate->stop(); + } + + if (sink->lastRequestedCaps) + gst_caps_unref(sink->lastRequestedCaps); + sink->lastRequestedCaps = 0; + +#ifdef DEBUG_VIDEO_SURFACE_SINK + qDebug() << "Staring video surface, format:"; + qDebug() << format; + qDebug() << "bytesPerLine:" << bytesPerLine; +#endif + + if (sink->delegate->start(format, bytesPerLine)) + return TRUE; + else + qWarning() << "Failed to start video surface"; + } + + return FALSE; +} + +QVideoSurfaceFormat QVideoSurfaceGstSink::formatForCaps(GstCaps *caps, int *bytesPerLine) +{ + const GstStructure *structure = gst_caps_get_structure(caps, 0); + + QVideoFrame::PixelFormat pixelFormat = QVideoFrame::Format_Invalid; + int bitsPerPixel = 0; + + QSize size; + gst_structure_get_int(structure, "width", &size.rwidth()); + gst_structure_get_int(structure, "height", &size.rheight()); + + if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-yuv") == 0) { + guint32 fourcc = 0; + gst_structure_get_fourcc(structure, "format", &fourcc); + + int index = indexOfYuvColor(fourcc); + if (index != -1) { + pixelFormat = qt_yuvColorLookup[index].pixelFormat; + bitsPerPixel = qt_yuvColorLookup[index].bitsPerPixel; + } + } else if (qstrcmp(gst_structure_get_name(structure), "video/x-raw-rgb") == 0) { + int depth = 0; + int endianness = 0; + int red = 0; + int green = 0; + int blue = 0; + int alpha = 0; + + gst_structure_get_int(structure, "bpp", &bitsPerPixel); + gst_structure_get_int(structure, "depth", &depth); + gst_structure_get_int(structure, "endianness", &endianness); + gst_structure_get_int(structure, "red_mask", &red); + gst_structure_get_int(structure, "green_mask", &green); + gst_structure_get_int(structure, "blue_mask", &blue); + gst_structure_get_int(structure, "alpha_mask", &alpha); + + int index = indexOfRgbColor(bitsPerPixel, depth, endianness, red, green, blue, alpha); + + if (index != -1) + pixelFormat = qt_rgbColorLookup[index].pixelFormat; + } + + if (pixelFormat != QVideoFrame::Format_Invalid) { + QVideoSurfaceFormat format(size, pixelFormat); + + QPair rate; + gst_structure_get_fraction(structure, "framerate", &rate.first, &rate.second); + + if (rate.second) + format.setFrameRate(qreal(rate.first)/rate.second); + + gint aspectNum = 0; + gint aspectDenum = 0; + if (gst_structure_get_fraction( + structure, "pixel-aspect-ratio", &aspectNum, &aspectDenum)) { + if (aspectDenum > 0) + format.setPixelAspectRatio(aspectNum, aspectDenum); + } + + if (bytesPerLine) + *bytesPerLine = ((size.width() * bitsPerPixel / 8) + 3) & ~3; + + return format; + } + + return QVideoSurfaceFormat(); +} + + +GstFlowReturn QVideoSurfaceGstSink::buffer_alloc( + GstBaseSink *base, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer) +{ + VO_SINK(base); + + Q_UNUSED(offset); + Q_UNUSED(size); + + if (!buffer) + return GST_FLOW_ERROR; + + *buffer = NULL; + + if (!sink->delegate->pool()) + return GST_FLOW_OK; + + QMutexLocker poolLock(sink->delegate->poolMutex()); + QAbstractGstBufferPool *pool = sink->delegate->pool(); + + if (!pool) + return GST_FLOW_OK; + + if (sink->lastRequestedCaps && gst_caps_is_equal(sink->lastRequestedCaps, caps)) { + //qDebug() << "reusing last caps"; + *buffer = GST_BUFFER(pool->takeBuffer(*sink->lastSurfaceFormat, sink->lastBufferCaps)); + return GST_FLOW_OK; + } + + if (sink->delegate->supportedPixelFormats(pool->handleType()).isEmpty()) { + //qDebug() << "sink doesn't support native pool buffers, skip buffers allocation"; + return GST_FLOW_OK; + } + + GstCaps *intersection = gst_caps_intersect(get_caps(GST_BASE_SINK(sink)), caps); + + if (gst_caps_is_empty (intersection)) { + gst_caps_unref(intersection); + return GST_FLOW_NOT_NEGOTIATED; + } + + poolLock.unlock(); + + if (sink->delegate->isActive()) { + //if format was changed, restart the surface + QVideoSurfaceFormat format = formatForCaps(intersection); + QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); + + if (format.pixelFormat() != surfaceFormat.pixelFormat() || + format.frameSize() != surfaceFormat.frameSize()) { +#ifdef DEBUG_VIDEO_SURFACE_SINK + qDebug() << "new format requested, restart video surface"; +#endif + sink->delegate->stop(); + } + } + + if (!sink->delegate->isActive()) { + int bytesPerLine = 0; + QVideoSurfaceFormat format = formatForCaps(intersection, &bytesPerLine); + + if (!sink->delegate->start(format, bytesPerLine)) { + qWarning() << "failed to start video surface"; + return GST_FLOW_NOT_NEGOTIATED; + } + } + + poolLock.relock(); + pool = sink->delegate->pool(); + + QVideoSurfaceFormat surfaceFormat = sink->delegate->surfaceFormat(); + + if (!pool->isFormatSupported(surfaceFormat)) { + //qDebug() << "sink doesn't support native pool format, skip custom buffers allocation"; + return GST_FLOW_OK; + } + + if (sink->lastRequestedCaps) + gst_caps_unref(sink->lastRequestedCaps); + sink->lastRequestedCaps = caps; + gst_caps_ref(sink->lastRequestedCaps); + + if (sink->lastBufferCaps) + gst_caps_unref(sink->lastBufferCaps); + sink->lastBufferCaps = intersection; + gst_caps_ref(sink->lastBufferCaps); + + *sink->lastSurfaceFormat = surfaceFormat; + + *buffer = GST_BUFFER(pool->takeBuffer(surfaceFormat, intersection)); + + return GST_FLOW_OK; +} + +gboolean QVideoSurfaceGstSink::start(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::stop(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::unlock(GstBaseSink *base) +{ + Q_UNUSED(base); + + return TRUE; +} + +gboolean QVideoSurfaceGstSink::event(GstBaseSink *base, GstEvent *event) +{ + Q_UNUSED(base); + Q_UNUSED(event); + + return TRUE; +} + +GstFlowReturn QVideoSurfaceGstSink::preroll(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + return sink->delegate->render(buffer); +} + +GstFlowReturn QVideoSurfaceGstSink::render(GstBaseSink *base, GstBuffer *buffer) +{ + VO_SINK(base); + return sink->delegate->render(buffer); +} + diff --git a/src/plugins/gstreamer/qvideosurfacegstsink.h b/src/plugins/gstreamer/qvideosurfacegstsink.h new file mode 100644 index 000000000..531a76e0f --- /dev/null +++ b/src/plugins/gstreamer/qvideosurfacegstsink.h @@ -0,0 +1,162 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef VIDEOSURFACEGSTSINK_H +#define VIDEOSURFACEGSTSINK_H + +#include + +#include +#include +#include +#include +#include +#include +#include +#include + +#include "qabstractgstbufferpool.h" + +QT_BEGIN_NAMESPACE +class QAbstractVideoSurface; +QT_END_NAMESPACE + +#if defined(Q_WS_X11) && !defined(QT_NO_XVIDEO) +class QGstXvImageBuffer; +class QGstXvImageBufferPool; +#endif + +class QVideoSurfaceGstDelegate : public QObject +{ + Q_OBJECT +public: + QVideoSurfaceGstDelegate(QAbstractVideoSurface *surface); + ~QVideoSurfaceGstDelegate(); + + QList supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + QVideoSurfaceFormat surfaceFormat() const; + + bool start(const QVideoSurfaceFormat &format, int bytesPerLine); + void stop(); + + bool isActive(); + + QAbstractGstBufferPool *pool() { return m_pool; } + QMutex *poolMutex() { return &m_poolMutex; } + + GstFlowReturn render(GstBuffer *buffer); + +private slots: + void queuedStart(); + void queuedStop(); + void queuedRender(); + + void updateSupportedFormats(); + +private: + QPointer m_surface; + QList m_supportedPixelFormats; + //pixel formats of buffers pool native type + QList m_supportedPoolPixelFormats; + QAbstractGstBufferPool *m_pool; + QList m_pools; + QMutex m_poolMutex; + QMutex m_mutex; + QWaitCondition m_setupCondition; + QWaitCondition m_renderCondition; + QVideoSurfaceFormat m_format; + QVideoFrame m_frame; + GstFlowReturn m_renderReturn; + int m_bytesPerLine; + bool m_started; +}; + +class QVideoSurfaceGstSink +{ +public: + GstVideoSink parent; + + static QVideoSurfaceGstSink *createSink(QAbstractVideoSurface *surface); + static QVideoSurfaceFormat formatForCaps(GstCaps *caps, int *bytesPerLine = 0); + +private: + static GType get_type(); + static void class_init(gpointer g_class, gpointer class_data); + static void base_init(gpointer g_class); + static void instance_init(GTypeInstance *instance, gpointer g_class); + + static void finalize(GObject *object); + + static GstStateChangeReturn change_state(GstElement *element, GstStateChange transition); + + static GstCaps *get_caps(GstBaseSink *sink); + static gboolean set_caps(GstBaseSink *sink, GstCaps *caps); + + static GstFlowReturn buffer_alloc( + GstBaseSink *sink, guint64 offset, guint size, GstCaps *caps, GstBuffer **buffer); + + static gboolean start(GstBaseSink *sink); + static gboolean stop(GstBaseSink *sink); + + static gboolean unlock(GstBaseSink *sink); + + static gboolean event(GstBaseSink *sink, GstEvent *event); + static GstFlowReturn preroll(GstBaseSink *sink, GstBuffer *buffer); + static GstFlowReturn render(GstBaseSink *sink, GstBuffer *buffer); + +private: + QVideoSurfaceGstDelegate *delegate; + + GstCaps *lastRequestedCaps; + GstCaps *lastBufferCaps; + QVideoSurfaceFormat *lastSurfaceFormat; +}; + + +class QVideoSurfaceGstSinkClass +{ +public: + GstVideoSinkClass parent_class; +}; + +#endif diff --git a/src/plugins/gstreamer/qx11videosurface.cpp b/src/plugins/gstreamer/qx11videosurface.cpp new file mode 100644 index 000000000..ea2a1a3a2 --- /dev/null +++ b/src/plugins/gstreamer/qx11videosurface.cpp @@ -0,0 +1,535 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#ifndef QT_NO_XVIDEO + +#include "qx11videosurface.h" + +Q_DECLARE_METATYPE(XvImage*); + +struct XvFormatRgb +{ + QVideoFrame::PixelFormat pixelFormat; + int bits_per_pixel; + int format; + int num_planes; + + int depth; + unsigned int red_mask; + unsigned int green_mask; + unsigned int blue_mask; + +}; + +bool operator ==(const XvImageFormatValues &format, const XvFormatRgb &rgb) +{ + return format.type == XvRGB + && format.bits_per_pixel == rgb.bits_per_pixel + && format.format == rgb.format + && format.num_planes == rgb.num_planes + && format.depth == rgb.depth + && format.red_mask == rgb.red_mask + && format.blue_mask == rgb.blue_mask; +} + +static const XvFormatRgb qt_xvRgbLookup[] = +{ + { QVideoFrame::Format_ARGB32, 32, XvPacked, 1, 32, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_RGB565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F }, + { QVideoFrame::Format_BGRA32, 32, XvPacked, 1, 32, 0xFF000000, 0x00FF0000, 0x0000FF00 }, + { QVideoFrame::Format_BGR32 , 32, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_BGR24 , 24, XvPacked, 1, 24, 0x00FF0000, 0x0000FF00, 0x000000FF }, + { QVideoFrame::Format_BGR565, 16, XvPacked, 1, 16, 0x0000F800, 0x000007E0, 0x0000001F } +}; + +struct XvFormatYuv +{ + QVideoFrame::PixelFormat pixelFormat; + int bits_per_pixel; + int format; + int num_planes; + + unsigned int y_sample_bits; + unsigned int u_sample_bits; + unsigned int v_sample_bits; + unsigned int horz_y_period; + unsigned int horz_u_period; + unsigned int horz_v_period; + unsigned int vert_y_period; + unsigned int vert_u_period; + unsigned int vert_v_period; + char component_order[32]; +}; + +bool operator ==(const XvImageFormatValues &format, const XvFormatYuv &yuv) +{ + return format.type == XvYUV + && format.bits_per_pixel == yuv.bits_per_pixel + && format.format == yuv.format + && format.num_planes == yuv.num_planes + && format.y_sample_bits == yuv.y_sample_bits + && format.u_sample_bits == yuv.u_sample_bits + && format.v_sample_bits == yuv.v_sample_bits + && format.horz_y_period == yuv.horz_y_period + && format.horz_u_period == yuv.horz_u_period + && format.horz_v_period == yuv.horz_v_period + && format.horz_y_period == yuv.vert_y_period + && format.vert_u_period == yuv.vert_u_period + && format.vert_v_period == yuv.vert_v_period + && qstrncmp(format.component_order, yuv.component_order, 32) == 0; +} + +static const XvFormatYuv qt_xvYuvLookup[] = +{ + { QVideoFrame::Format_YUV444 , 24, XvPacked, 1, 8, 8, 8, 1, 1, 1, 1, 1, 1, "YUV" }, + { QVideoFrame::Format_YUV420P, 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" }, + { QVideoFrame::Format_YV12 , 12, XvPlanar, 3, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" }, + { QVideoFrame::Format_UYVY , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "UYVY" }, + { QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUY2" }, + { QVideoFrame::Format_YUYV , 16, XvPacked, 1, 8, 8, 8, 1, 2, 2, 1, 1, 1, "YUYV" }, + { QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YUV" }, + { QVideoFrame::Format_NV12 , 12, XvPlanar, 2, 8, 8, 8, 1, 2, 2, 1, 2, 2, "YVU" }, + { QVideoFrame::Format_Y8 , 8 , XvPlanar, 1, 8, 0, 0, 1, 0, 0, 1, 0, 0, "Y" } +}; + +QX11VideoSurface::QX11VideoSurface(QObject *parent) + : QAbstractVideoSurface(parent) + , m_winId(0) + , m_portId(0) + , m_gc(0) + , m_image(0) +{ +} + +QX11VideoSurface::~QX11VideoSurface() +{ + if (m_gc) + XFreeGC(QX11Info::display(), m_gc); + + if (m_portId != 0) + XvUngrabPort(QX11Info::display(), m_portId, 0); +} + +WId QX11VideoSurface::winId() const +{ + return m_winId; +} + +void QX11VideoSurface::setWinId(WId id) +{ + //qDebug() << "setWinID:" << id; + + if (id == m_winId) + return; + + if (m_image) + XFree(m_image); + + if (m_gc) { + XFreeGC(QX11Info::display(), m_gc); + m_gc = 0; + } + + if (m_portId != 0) + XvUngrabPort(QX11Info::display(), m_portId, 0); + + m_supportedPixelFormats.clear(); + m_formatIds.clear(); + + m_winId = id; + + if (m_winId && findPort()) { + querySupportedFormats(); + + m_gc = XCreateGC(QX11Info::display(), m_winId, 0, 0); + + if (m_image) { + m_image = 0; + + if (!start(surfaceFormat())) { + QAbstractVideoSurface::stop(); + qWarning() << "Failed to start video surface with format" << surfaceFormat(); + } + } + } else { + qWarning() << "Failed to find XVideo port"; + if (m_image) { + m_image = 0; + + QAbstractVideoSurface::stop(); + } + } + + emit supportedFormatsChanged(); +} + +QRect QX11VideoSurface::displayRect() const +{ + return m_displayRect; +} + +void QX11VideoSurface::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; +} + +QRect QX11VideoSurface::viewport() const +{ + return m_viewport; +} + +void QX11VideoSurface::setViewport(const QRect &rect) +{ + m_viewport = rect; +} + +int QX11VideoSurface::brightness() const +{ + return getAttribute("XV_BRIGHTNESS", m_brightnessRange.first, m_brightnessRange.second); +} + +void QX11VideoSurface::setBrightness(int brightness) +{ + setAttribute("XV_BRIGHTNESS", brightness, m_brightnessRange.first, m_brightnessRange.second); +} + +int QX11VideoSurface::contrast() const +{ + return getAttribute("XV_CONTRAST", m_contrastRange.first, m_contrastRange.second); +} + +void QX11VideoSurface::setContrast(int contrast) +{ + setAttribute("XV_CONTRAST", contrast, m_contrastRange.first, m_contrastRange.second); +} + +int QX11VideoSurface::hue() const +{ + return getAttribute("XV_HUE", m_hueRange.first, m_hueRange.second); +} + +void QX11VideoSurface::setHue(int hue) +{ + setAttribute("XV_HUE", hue, m_hueRange.first, m_hueRange.second); +} + +int QX11VideoSurface::saturation() const +{ + return getAttribute("XV_SATURATION", m_saturationRange.first, m_saturationRange.second); +} + +void QX11VideoSurface::setSaturation(int saturation) +{ + setAttribute("XV_SATURATION", saturation, m_saturationRange.first, m_saturationRange.second); +} + +int QX11VideoSurface::getAttribute(const char *attribute, int minimum, int maximum) const +{ + if (m_portId != 0) { + Display *display = QX11Info::display(); + + Atom atom = XInternAtom(display, attribute, True); + + int value = 0; + + XvGetPortAttribute(display, m_portId, atom, &value); + + return redistribute(value, minimum, maximum, -100, 100); + } else { + return 0; + } +} + +void QX11VideoSurface::setAttribute(const char *attribute, int value, int minimum, int maximum) +{ + if (m_portId != 0) { + Display *display = QX11Info::display(); + + Atom atom = XInternAtom(display, attribute, True); + + XvSetPortAttribute( + display, m_portId, atom, redistribute(value, -100, 100, minimum, maximum)); + } +} + +int QX11VideoSurface::redistribute( + int value, int fromLower, int fromUpper, int toLower, int toUpper) +{ + return fromUpper != fromLower + ? ((value - fromLower) * (toUpper - toLower) / (fromUpper - fromLower)) + toLower + : 0; +} + +QList QX11VideoSurface::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + return handleType == QAbstractVideoBuffer::NoHandle || handleType == QAbstractVideoBuffer::XvShmImageHandle + ? m_supportedPixelFormats + : QList(); +} + +bool QX11VideoSurface::start(const QVideoSurfaceFormat &format) +{ + if (m_image) + XFree(m_image); + + int xvFormatId = 0; + for (int i = 0; i < m_supportedPixelFormats.count(); ++i) { + if (m_supportedPixelFormats.at(i) == format.pixelFormat()) { + xvFormatId = m_formatIds.at(i); + break; + } + } + + if (xvFormatId == 0) { + setError(UnsupportedFormatError); + } else { + XvImage *image = XvCreateImage( + QX11Info::display(), + m_portId, + xvFormatId, + 0, + format.frameWidth(), + format.frameHeight()); + + if (!image) { + setError(ResourceError); + } else { + m_viewport = format.viewport(); + m_image = image; + + QVideoSurfaceFormat newFormat = format; + newFormat.setProperty("portId", QVariant(quint64(m_portId))); + newFormat.setProperty("xvFormatId", xvFormatId); + newFormat.setProperty("dataSize", image->data_size); + + return QAbstractVideoSurface::start(newFormat); + } + } + + if (m_image) { + m_image = 0; + + QAbstractVideoSurface::stop(); + } + + return false; +} + +void QX11VideoSurface::stop() +{ + if (m_image) { + XFree(m_image); + m_image = 0; + + QAbstractVideoSurface::stop(); + } +} + +bool QX11VideoSurface::present(const QVideoFrame &frame) +{ + if (!m_image) { + setError(StoppedError); + return false; + } else if (m_image->width != frame.width() || m_image->height != frame.height()) { + setError(IncorrectFormatError); + return false; + } else { + QVideoFrame frameCopy(frame); + + if (!frameCopy.map(QAbstractVideoBuffer::ReadOnly)) { + setError(IncorrectFormatError); + return false; + } else { + bool presented = false; + + if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle && + m_image->data_size > frame.mappedBytes()) { + qWarning("Insufficient frame buffer size"); + setError(IncorrectFormatError); + } else if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle && + m_image->num_planes > 0 && + m_image->pitches[0] != frame.bytesPerLine()) { + qWarning("Incompatible frame pitches"); + setError(IncorrectFormatError); + } else { + if (frame.handleType() != QAbstractVideoBuffer::XvShmImageHandle) { + m_image->data = reinterpret_cast(frameCopy.bits()); + + //qDebug() << "copy frame"; + XvPutImage( + QX11Info::display(), + m_portId, + m_winId, + m_gc, + m_image, + m_viewport.x(), + m_viewport.y(), + m_viewport.width(), + m_viewport.height(), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height()); + + m_image->data = 0; + } else { + XvImage *img = frame.handle().value(); + + //qDebug() << "render directly"; + if (img) + XvShmPutImage( + QX11Info::display(), + m_portId, + m_winId, + m_gc, + img, + m_viewport.x(), + m_viewport.y(), + m_viewport.width(), + m_viewport.height(), + m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height(), + false); + } + + presented = true; + } + + frameCopy.unmap(); + + return presented; + } + } +} + +bool QX11VideoSurface::findPort() +{ + unsigned int count = 0; + XvAdaptorInfo *adaptors = 0; + bool portFound = false; + + if (XvQueryAdaptors(QX11Info::display(), m_winId, &count, &adaptors) == Success) { +#ifdef Q_WS_MAEMO_5 + //the overlay xvideo adapter fails to switch winId, + //prefer the "SGX Textured Video" adapter instead + for (int i = count-1; i >= 0 && !portFound; --i) { +#else + for (unsigned int i = 0; i < count && !portFound; ++i) { +#endif + if (adaptors[i].type & XvImageMask) { + m_portId = adaptors[i].base_id; + + for (unsigned int j = 0; j < adaptors[i].num_ports && !portFound; ++j, ++m_portId) + portFound = XvGrabPort(QX11Info::display(), m_portId, 0) == Success; + } + } + XvFreeAdaptorInfo(adaptors); + } + + return portFound; +} + +void QX11VideoSurface::querySupportedFormats() +{ + int count = 0; + if (XvImageFormatValues *imageFormats = XvListImageFormats( + QX11Info::display(), m_portId, &count)) { + const int rgbCount = sizeof(qt_xvRgbLookup) / sizeof(XvFormatRgb); + const int yuvCount = sizeof(qt_xvYuvLookup) / sizeof(XvFormatYuv); + + for (int i = 0; i < count; ++i) { + switch (imageFormats[i].type) { + case XvRGB: + for (int j = 0; j < rgbCount; ++j) { + if (imageFormats[i] == qt_xvRgbLookup[j]) { + m_supportedPixelFormats.append(qt_xvRgbLookup[j].pixelFormat); + m_formatIds.append(imageFormats[i].id); + break; + } + } + break; + case XvYUV: + for (int j = 0; j < yuvCount; ++j) { + if (imageFormats[i] == qt_xvYuvLookup[j]) { + m_supportedPixelFormats.append(qt_xvYuvLookup[j].pixelFormat); + m_formatIds.append(imageFormats[i].id); + break; + } + } + break; + } + } + XFree(imageFormats); + } + + m_brightnessRange = qMakePair(0, 0); + m_contrastRange = qMakePair(0, 0); + m_hueRange = qMakePair(0, 0); + m_saturationRange = qMakePair(0, 0); + + if (XvAttribute *attributes = XvQueryPortAttributes(QX11Info::display(), m_portId, &count)) { + for (int i = 0; i < count; ++i) { + if (qstrcmp(attributes[i].name, "XV_BRIGHTNESS") == 0) + m_brightnessRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_CONTRAST") == 0) + m_contrastRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_HUE") == 0) + m_hueRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + else if (qstrcmp(attributes[i].name, "XV_SATURATION") == 0) + m_saturationRange = qMakePair(attributes[i].min_value, attributes[i].max_value); + } + + XFree(attributes); + } +} + +#endif //QT_NO_XVIDEO + diff --git a/src/plugins/gstreamer/qx11videosurface.h b/src/plugins/gstreamer/qx11videosurface.h new file mode 100644 index 000000000..7bebe2f67 --- /dev/null +++ b/src/plugins/gstreamer/qx11videosurface.h @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QX11VIDEOSURFACE_H +#define QX11VIDEOSURFACE_H + +#include +#include + +#ifndef QT_NO_XVIDEO + +#include +#include +#include + +QT_USE_NAMESPACE + +class QX11VideoSurface : public QAbstractVideoSurface +{ + Q_OBJECT +public: + QX11VideoSurface(QObject *parent = 0); + ~QX11VideoSurface(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + QRect viewport() const; + void setViewport(const QRect &rect); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QList supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + bool start(const QVideoSurfaceFormat &format); + void stop(); + + bool present(const QVideoFrame &frame); + +private: + WId m_winId; + XvPortID m_portId; + GC m_gc; + XvImage *m_image; + QList m_supportedPixelFormats; + QVector m_formatIds; + QRect m_viewport; + QRect m_displayRect; + QPair m_brightnessRange; + QPair m_contrastRange; + QPair m_hueRange; + QPair m_saturationRange; + + bool findPort(); + void querySupportedFormats(); + + int getAttribute(const char *attribute, int minimum, int maximum) const; + void setAttribute(const char *attribute, int value, int minimum, int maximum); + + static int redistribute(int value, int fromLower, int fromUpper, int toLower, int toUpper); +}; + +#endif //QT_NO_XVIDEO + +#endif diff --git a/src/plugins/m3u/m3u.pro b/src/plugins/m3u/m3u.pro new file mode 100644 index 000000000..f639445ee --- /dev/null +++ b/src/plugins/m3u/m3u.pro @@ -0,0 +1,23 @@ +load(qt_module) + +TARGET = qtmultimediakit_m3u +QT += multimediakit-private +PLUGIN_TYPE=playlistformats + +load(qt_plugin) +DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE} + + +HEADERS += qm3uhandler.h +SOURCES += main.cpp \ + qm3uhandler.cpp +symbian { + TARGET.UID3 = 0x2002BFC7 + TARGET.CAPABILITY = ALL -TCB + TARGET.EPOCALLOWDLLDATA = 1 + + #make a sis package from plugin + stub (plugin) + pluginDep.sources = $${TARGET}.dll + pluginDep.path = $${QT_PLUGINS_BASE_DIR}/$${PLUGIN_TYPE} + DEPLOYMENT += pluginDep +} diff --git a/src/plugins/m3u/main.cpp b/src/plugins/m3u/main.cpp new file mode 100644 index 000000000..4fd78d7dd --- /dev/null +++ b/src/plugins/m3u/main.cpp @@ -0,0 +1,47 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qm3uhandler.h" +#include + + +Q_EXPORT_STATIC_PLUGIN(QM3uPlaylistPlugin) +Q_EXPORT_PLUGIN2(qtmultimediakit_m3u, QM3uPlaylistPlugin) diff --git a/src/plugins/m3u/qm3uhandler.cpp b/src/plugins/m3u/qm3uhandler.cpp new file mode 100644 index 000000000..69e86a1a9 --- /dev/null +++ b/src/plugins/m3u/qm3uhandler.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qm3uhandler.h" +#include +#include +#include +#include +#include +#include + + +class QM3uPlaylistReader : public QMediaPlaylistReader +{ +public: + QM3uPlaylistReader(QIODevice *device) + :m_ownDevice(false), m_device(device), m_textStream(new QTextStream(m_device)) + { + readItem(); + } + + QM3uPlaylistReader(const QUrl& location) + :m_location(location), m_ownDevice(true) + { + QFile *f = new QFile(location.toLocalFile()); + if (f->open(QIODevice::ReadOnly | QIODevice::Text)) { + m_device = f; + m_textStream = new QTextStream(m_device); + readItem(); + } else { + delete f; + m_device = 0; + m_textStream = 0; + } + } + + virtual ~QM3uPlaylistReader() + { + if (m_ownDevice) { + delete m_device; + } + delete m_textStream; + } + + virtual bool atEnd() const + { + //we can't just use m_textStream->atEnd(), + //for files with empty lines/comments at end + return nextResource.isNull(); + } + + virtual QMediaContent readItem() + { + QMediaContent item; + if (!nextResource.isNull()) + item = QMediaContent(nextResource); + + nextResource = QMediaContent(); + + while (m_textStream && !m_textStream->atEnd()) { + QString line = m_textStream->readLine().trimmed(); + if (line.isEmpty() || line[0] == '#' || line.size() > 4096) + continue; + + QUrl fileUrl = QUrl::fromLocalFile(line); + QUrl url(line); + + //m3u may contain url encoded entries or absolute/relative file names + //prefer existing file if any + QList candidates; + if (!m_location.isEmpty()) { + candidates << m_location.resolved(fileUrl); + candidates << m_location.resolved(url); + } + candidates << fileUrl; + candidates << url; + + foreach (const QUrl &candidate, candidates) { + if (QFile::exists(candidate.toLocalFile())) { + nextResource = candidate; + break; + } + } + + if (nextResource.isNull()) { + //assume the relative urls are file names, not encoded urls if m3u is local file + if (!m_location.isEmpty() && url.isRelative()) { + if (m_location.scheme() == QLatin1String("file")) + nextResource = m_location.resolved(fileUrl); + else + nextResource = m_location.resolved(url); + } else { + nextResource = QMediaContent(QUrl::fromUserInput(line)); + } + } + + break; + } + + return item; + } + + virtual void close() + { + } + +private: + QUrl m_location; + bool m_ownDevice; + QIODevice *m_device; + QTextStream *m_textStream; + QMediaContent nextResource; +}; + +class QM3uPlaylistWriter : public QMediaPlaylistWriter +{ +public: + QM3uPlaylistWriter(QIODevice *device) + :m_device(device), m_textStream(new QTextStream(m_device)) + { + } + + virtual ~QM3uPlaylistWriter() + { + delete m_textStream; + } + + virtual bool writeItem(const QMediaContent& item) + { + *m_textStream << item.canonicalUrl().toString() << endl; + return true; + } + + virtual void close() + { + } + +private: + QIODevice *m_device; + QTextStream *m_textStream; +}; + + +QM3uPlaylistPlugin::QM3uPlaylistPlugin(QObject *parent) + :QMediaPlaylistIOPlugin(parent) +{ +} + +QM3uPlaylistPlugin::~QM3uPlaylistPlugin() +{ +} + +bool QM3uPlaylistPlugin::canRead(QIODevice *device, const QByteArray &format) const +{ + return device->isReadable() && (format == "m3u" || format.isEmpty()); +} + +bool QM3uPlaylistPlugin::canRead(const QUrl& location, const QByteArray &format) const +{ + if (!QFileInfo(location.toLocalFile()).isReadable()) + return false; + + if (format == "m3u") + return true; + + if (!format.isEmpty()) + return false; + else + return location.toLocalFile().toLower().endsWith(QLatin1String("m3u")); +} + +bool QM3uPlaylistPlugin::canWrite(QIODevice *device, const QByteArray &format) const +{ + return device->isOpen() && device->isWritable() && format == "m3u"; +} + +QStringList QM3uPlaylistPlugin::keys() const +{ + return QStringList() << QLatin1String("m3u"); +} + +QMediaPlaylistReader *QM3uPlaylistPlugin::createReader(QIODevice *device, const QByteArray &format) +{ + Q_UNUSED(format); + return new QM3uPlaylistReader(device); +} + +QMediaPlaylistReader *QM3uPlaylistPlugin::createReader(const QUrl& location, const QByteArray &format) +{ + Q_UNUSED(format); + return new QM3uPlaylistReader(location); +} + +QMediaPlaylistWriter *QM3uPlaylistPlugin::createWriter(QIODevice *device, const QByteArray &format) +{ + Q_UNUSED(format); + return new QM3uPlaylistWriter(device); +} + diff --git a/src/plugins/m3u/qm3uhandler.h b/src/plugins/m3u/qm3uhandler.h new file mode 100644 index 000000000..18d088ddd --- /dev/null +++ b/src/plugins/m3u/qm3uhandler.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QM3UHANDLER_H +#define QM3UHANDLER_H + +#include "qmediaplaylistioplugin.h" +#include + +QT_USE_NAMESPACE + +class QM3uPlaylistPlugin : public QMediaPlaylistIOPlugin +{ +Q_OBJECT +public: + explicit QM3uPlaylistPlugin(QObject *parent = 0); + virtual ~QM3uPlaylistPlugin(); + + virtual bool canRead(QIODevice *device, const QByteArray &format = QByteArray() ) const; + virtual bool canRead(const QUrl& location, const QByteArray &format = QByteArray()) const; + + virtual bool canWrite(QIODevice *device, const QByteArray &format) const; + + virtual QStringList keys() const; + + virtual QMediaPlaylistReader *createReader(QIODevice *device, const QByteArray &format = QByteArray()); + virtual QMediaPlaylistReader *createReader(const QUrl& location, const QByteArray &format = QByteArray()); + + virtual QMediaPlaylistWriter *createWriter(QIODevice *device, const QByteArray &format); +}; + +#endif // QM3UHANDLER_H diff --git a/src/plugins/plugins.pro b/src/plugins/plugins.pro new file mode 100644 index 000000000..5a96fded0 --- /dev/null +++ b/src/plugins/plugins.pro @@ -0,0 +1,46 @@ +###################################################################### +# +# Mobility API project - multimedia +# +###################################################################### + +TEMPLATE = subdirs + +SUBDIRS += m3u + +win32 { + SUBDIRS += audiocapture +} + +win32:!wince* { + contains(directshow_enabled, yes): SUBDIRS += directshow +} + +simulator: SUBDIRS += simulator + +unix:!mac:!symbian { + TMP_GST_LIBS = \ + gstreamer-0.10 >= 0.10.19 \ + gstreamer-base-0.10 >= 0.10.19 \ + gstreamer-interfaces-0.10 >= 0.10.19 \ + gstreamer-audio-0.10 >= 0.10.19 \ + gstreamer-video-0.10 >= 0.10.19 + + system(pkg-config --exists \'$${TMP_GST_LIBS}\' --print-errors): { + SUBDIRS += gstreamer + } else { + SUBDIRS += audiocapture + } + + !maemo*:SUBDIRS += v4l + + contains(pulseaudio_enabled, yes) { + SUBDIRS += pulseaudio + } +} + +mac:!simulator { + SUBDIRS += audiocapture qt7 +} + +symbian:SUBDIRS += symbian diff --git a/src/plugins/pulseaudio/pulseaudio.pro b/src/plugins/pulseaudio/pulseaudio.pro new file mode 100644 index 000000000..2f9b7a854 --- /dev/null +++ b/src/plugins/pulseaudio/pulseaudio.pro @@ -0,0 +1,26 @@ +load(qt_module) + +TARGET = qtmedia_pulse +QT += multimediakit-private +PLUGIN_TYPE = audio + +load(qt_plugin) +DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE} + +CONFIG += link_pkgconfig +PKGCONFIG += libpulse + +# Input +HEADERS += qpulseaudioplugin.h \ + qaudiodeviceinfo_pulse.h \ + qaudiooutput_pulse.h \ + qaudioinput_pulse.h \ + qpulseaudioengine.h \ + qpulsehelpers.h + +SOURCES += qpulseaudioplugin.cpp \ + qaudiodeviceinfo_pulse.cpp \ + qaudiooutput_pulse.cpp \ + qaudioinput_pulse.cpp \ + qpulseaudioengine.cpp \ + qpulsehelpers.cpp diff --git a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp new file mode 100644 index 000000000..54296861d --- /dev/null +++ b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.cpp @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qaudiodeviceinfo_pulse.h" +#include "qpulseaudioengine.h" +#include "qpulsehelpers.h" + +QT_BEGIN_NAMESPACE + +QPulseAudioDeviceInfo::QPulseAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode) + : m_device(device) + , m_mode(mode) +{ +} + +bool QPulseAudioDeviceInfo::isFormatSupported(const QAudioFormat &format) const +{ + pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(format); + if (!pa_sample_spec_valid(&spec)) + return false; + + return true; +} + +QAudioFormat QPulseAudioDeviceInfo::preferredFormat() const +{ + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + QAudioFormat format = pulseEngine->m_preferredFormats.value(m_device); + return format; +} + +QString QPulseAudioDeviceInfo::deviceName() const +{ + return m_device; +} + +QStringList QPulseAudioDeviceInfo::supportedCodecs() +{ + return QStringList() << "audio/pcm"; +} + +QList QPulseAudioDeviceInfo::supportedSampleRates() +{ + return QList() << 8000 << 11025 << 22050 << 44100 << 48000; +} + +QList QPulseAudioDeviceInfo::supportedChannelCounts() +{ + return QList() << 1 << 2 << 4 << 6 << 8; +} + +QList QPulseAudioDeviceInfo::supportedSampleSizes() +{ + return QList() << 8 << 16 << 24 << 32; +} + +QList QPulseAudioDeviceInfo::supportedByteOrders() +{ + return QList() << QAudioFormat::BigEndian << QAudioFormat::LittleEndian; +} + +QList QPulseAudioDeviceInfo::supportedSampleTypes() +{ + return QList() << QAudioFormat::SignedInt << QAudioFormat::UnSignedInt << QAudioFormat::Float; +} + +QT_END_NAMESPACE diff --git a/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h new file mode 100644 index 000000000..f76913ca3 --- /dev/null +++ b/src/plugins/pulseaudio/qaudiodeviceinfo_pulse.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIODEVICEINFOPULSE_H +#define QAUDIODEVICEINFOPULSE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include + +#include "qaudio.h" +#include "qaudiodeviceinfo.h" +#include "qaudiosystem.h" + +QT_BEGIN_NAMESPACE + +class QPulseAudioDeviceInfo : public QAbstractAudioDeviceInfo +{ + Q_OBJECT + +public: + QPulseAudioDeviceInfo(const QByteArray &device, QAudio::Mode mode); + ~QPulseAudioDeviceInfo() {} + + QAudioFormat preferredFormat() const; + bool isFormatSupported(const QAudioFormat &format) const; + QString deviceName() const; + QStringList supportedCodecs(); + QList supportedSampleRates(); + QList supportedChannelCounts(); + QList supportedSampleSizes(); + QList supportedByteOrders(); + QList supportedSampleTypes(); + +private: + QByteArray m_device; + QAudio::Mode m_mode; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.cpp b/src/plugins/pulseaudio/qaudioinput_pulse.cpp new file mode 100644 index 000000000..083bbabb6 --- /dev/null +++ b/src/plugins/pulseaudio/qaudioinput_pulse.cpp @@ -0,0 +1,600 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qaudioinput_pulse.h" +#include "qaudiodeviceinfo_pulse.h" +#include "qpulseaudioengine.h" +#include "qpulsehelpers.h" + +QT_BEGIN_NAMESPACE + +const int PeriodTimeMs = 50; + +static void inputStreamReadCallback(pa_stream *stream, size_t length, void *userdata) +{ + Q_UNUSED(userdata); + Q_UNUSED(length); + Q_UNUSED(stream); + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); +} + +static void inputStreamStateCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(userdata); + pa_stream_state_t state = pa_stream_get_state(stream); +#ifdef DEBUG_PULSE + qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state); +#endif + switch (state) { + case PA_STREAM_CREATING: + break; + case PA_STREAM_READY: { +#ifdef DEBUG_PULSE + QPulseAudioInput *audioInput = static_cast(userdata); + const pa_buffer_attr *buffer_attr = pa_stream_get_buffer_attr(stream); + qDebug() << "*** maxlength: " << buffer_attr->maxlength; + qDebug() << "*** prebuf: " << buffer_attr->prebuf; + qDebug() << "*** fragsize: " << buffer_attr->fragsize; + qDebug() << "*** minreq: " << buffer_attr->minreq; + qDebug() << "*** tlength: " << buffer_attr->tlength; + + pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(audioInput->format()); + qDebug() << "*** bytes_to_usec: " << pa_bytes_to_usec(buffer_attr->fragsize, &spec); +#endif + } + break; + case PA_STREAM_TERMINATED: + break; + case PA_STREAM_FAILED: + default: + qWarning() << QString("Stream error: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(stream)))); + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); + break; + } +} + +static void inputStreamUnderflowCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(userdata) + Q_UNUSED(stream) + qWarning() << "Got a buffer underflow!"; +} + +static void inputStreamOverflowCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(stream) + Q_UNUSED(userdata) + qWarning() << "Got a buffer overflow!"; +} + +static void inputStreamSuccessCallback(pa_stream *stream, int success, void *userdata) +{ + Q_UNUSED(stream); + Q_UNUSED(userdata); + Q_UNUSED(success); + + //if (!success) + //TODO: Is cork success? i->operation_success = success; + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); +} + +QPulseAudioInput::QPulseAudioInput(const QByteArray &device) + : m_totalTimeValue(0) + , m_audioSource(0) + , m_errorState(QAudio::NoError) + , m_deviceState(QAudio::StoppedState) + , m_pullMode(true) + , m_opened(false) + , m_bytesAvailable(0) + , m_bufferSize(0) + , m_periodSize(0) + , m_intervalTime(1000) + , m_stream(0) + , m_device(device) +{ + m_timer = new QTimer(this); + connect(m_timer, SIGNAL(timeout()), SLOT(userFeed())); +} + +QPulseAudioInput::~QPulseAudioInput() +{ + close(); + disconnect(m_timer, SIGNAL(timeout())); + QCoreApplication::processEvents(); + delete m_timer; +} + +QAudio::Error QPulseAudioInput::error() const +{ + return m_errorState; +} + +QAudio::State QPulseAudioInput::state() const +{ + return m_deviceState; +} + +void QPulseAudioInput::setFormat(const QAudioFormat &format) +{ + if (m_deviceState == QAudio::StoppedState) + m_format = format; +} + +QAudioFormat QPulseAudioInput::format() const +{ + return m_format; +} + +void QPulseAudioInput::start(QIODevice *device) +{ + if (m_deviceState != QAudio::StoppedState) + close(); + + if (!m_pullMode && m_audioSource) + delete m_audioSource; + + m_pullMode = true; + m_audioSource = device; + + m_deviceState = QAudio::ActiveState; + + if (!open()) + return; + + emit stateChanged(m_deviceState); +} + +QIODevice *QPulseAudioInput::start() +{ + if (m_deviceState != QAudio::StoppedState) + close(); + + if (!m_pullMode && m_audioSource) + delete m_audioSource; + + m_pullMode = false; + m_audioSource = new InputPrivate(this); + m_audioSource->open(QIODevice::ReadOnly | QIODevice::Unbuffered); + + m_deviceState = QAudio::IdleState; + + if (!open()) + return 0; + + emit stateChanged(m_deviceState); + + return m_audioSource; +} + +void QPulseAudioInput::stop() +{ + if (m_deviceState == QAudio::StoppedState) + return; + + m_errorState = QAudio::NoError; + m_deviceState = QAudio::StoppedState; + + close(); + emit stateChanged(m_deviceState); +} + +bool QPulseAudioInput::open() +{ + if (m_opened) + return false; + +#ifdef DEBUG_PULSE +// QTime now(QTime::currentTime()); +// qDebug()<mainloop()); + pa_channel_map channel_map; + + pa_channel_map_init_extend(&channel_map, spec.channels, PA_CHANNEL_MAP_DEFAULT); + + if (!pa_channel_map_compatible(&channel_map, &spec)) { + qWarning() << "Channel map doesn't match sample specification!"; + } + + m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, &channel_map); + + pa_stream_set_state_callback(m_stream, inputStreamStateCallback, this); + pa_stream_set_read_callback(m_stream, inputStreamReadCallback, this); + + pa_stream_set_underflow_callback(m_stream, inputStreamUnderflowCallback, this); + pa_stream_set_overflow_callback(m_stream, inputStreamOverflowCallback, this); + + m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec); + + int flags = 0; + pa_buffer_attr buffer_attr; + buffer_attr.maxlength = (uint32_t) -1; + buffer_attr.prebuf = (uint32_t) -1; + buffer_attr.tlength = (uint32_t) -1; + buffer_attr.minreq = (uint32_t) -1; + flags |= PA_STREAM_ADJUST_LATENCY; + + if (m_bufferSize > 0) + buffer_attr.fragsize = (uint32_t) m_bufferSize; + else + buffer_attr.fragsize = (uint32_t) m_periodSize; + + if (pa_stream_connect_record(m_stream, m_device.data(), &buffer_attr, (pa_stream_flags_t)flags) < 0) { + qWarning() << "pa_stream_connect_record() failed!"; + m_errorState = QAudio::FatalError; + return false; + } + + while (pa_stream_get_state(m_stream) != PA_STREAM_READY) { + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + } + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + + m_opened = true; + m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec); + m_timer->start(PeriodTimeMs); + m_errorState = QAudio::NoError; + + m_totalTimeValue = 0; + + return true; +} + +void QPulseAudioInput::close() +{ + m_timer->stop(); + + if (m_stream) { + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + + pa_stream_set_read_callback(m_stream, 0, 0); + + pa_stream_disconnect(m_stream); + pa_stream_unref(m_stream); + m_stream = 0; + + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + } + + if (!m_pullMode && m_audioSource) { + delete m_audioSource; + m_audioSource = 0; + } + m_opened = false; +} + +int QPulseAudioInput::checkBytesReady() +{ + if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) { + m_bytesAvailable = 0; + } else { + m_bytesAvailable = pa_stream_readable_size(m_stream); + } + + return m_bytesAvailable; +} + +int QPulseAudioInput::bytesReady() const +{ + return qMax(m_bytesAvailable, 0); +} + +qint64 QPulseAudioInput::read(char *data, qint64 len) +{ + m_bytesAvailable = checkBytesReady(); + + if (m_deviceState != QAudio::ActiveState) { + m_errorState = QAudio::NoError; + m_deviceState = QAudio::ActiveState; + emit stateChanged(m_deviceState); + } + + size_t readBytes = 0; + + if (!m_pullMode && !m_tempBuffer.isEmpty()) { + readBytes = qMin(static_cast(len), m_tempBuffer.size()); + memcpy(data, m_tempBuffer.constData(), readBytes); + m_totalTimeValue += readBytes; + + if (readBytes < m_tempBuffer.size()) { + m_tempBuffer.remove(0, readBytes); + return readBytes; + } + + m_tempBuffer.clear(); + } + + while (pa_stream_readable_size(m_stream) > 0) { + size_t readLength = 0; + +#ifdef DEBUG_PULSE + qDebug() << "QPulseAudioInput::read -- " << pa_stream_readable_size(m_stream) << " bytes available from pulse audio"; +#endif + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + const void *audioBuffer; + + // Second and third parameters (audioBuffer and length) to pa_stream_peek are output parameters, + // the audioBuffer pointer is set to point to the actual pulse audio data, + // and the length is set to the length of this data. + if (pa_stream_peek(m_stream, &audioBuffer, &readLength) < 0) { + qWarning() << QString("pa_stream_peek() failed: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream)))); + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + return 0; + } + + qint64 actualLength = 0; + if (m_pullMode) { + actualLength = m_audioSource->write(static_cast(audioBuffer), readLength); + + if (actualLength < readLength) { + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + + m_errorState = QAudio::UnderrunError; + m_deviceState = QAudio::IdleState; + emit stateChanged(m_deviceState); + + return actualLength; + } + } else { + actualLength = qMin(static_cast(len - readBytes), static_cast(readLength)); + memcpy(data + readBytes, audioBuffer, actualLength); + } + +#ifdef DEBUG_PULSE + qDebug() << "QPulseAudioInput::read -- wrote " << actualLength << " to client"; +#endif + + if (actualLength < readLength) { +#ifdef DEBUG_PULSE + qDebug() << "QPulseAudioInput::read -- appending " << readLength - actualLength << " bytes of data to temp buffer"; +#endif + m_tempBuffer.append(static_cast(audioBuffer) + actualLength, readLength - actualLength); + QMetaObject::invokeMethod(this, "userFeed", Qt::QueuedConnection); + } + + m_totalTimeValue += actualLength; + readBytes += actualLength; + + pa_stream_drop(m_stream); + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + + if (!m_pullMode && readBytes >= len) + break; + } + +#ifdef DEBUG_PULSE + qDebug() << "QPulseAudioInput::read -- returning after reading " << readBytes << " bytes"; +#endif + + return readBytes; +} + +void QPulseAudioInput::resume() +{ + if (m_deviceState == QAudio::SuspendedState || m_deviceState == QAudio::IdleState) { + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_operation *operation; + + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + + operation = pa_stream_cork(m_stream, 0, inputStreamSuccessCallback, 0); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + + m_timer->start(PeriodTimeMs); + + m_deviceState = QAudio::ActiveState; + + emit stateChanged(m_deviceState); + } +} + +void QPulseAudioInput::setBufferSize(int value) +{ + m_bufferSize = value; +} + +int QPulseAudioInput::bufferSize() const +{ + return m_bufferSize; +} + +int QPulseAudioInput::periodSize() const +{ + return m_periodSize; +} + +void QPulseAudioInput::setNotifyInterval(int ms) +{ + m_intervalTime = qMax(0, ms); +} + +int QPulseAudioInput::notifyInterval() const +{ + return m_intervalTime; +} + +qint64 QPulseAudioInput::processedUSecs() const +{ + pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); + qint64 result = pa_bytes_to_usec(m_totalTimeValue, &spec); + + return result; +} + +void QPulseAudioInput::suspend() +{ + if (m_deviceState == QAudio::ActiveState) { + m_timer->stop(); + m_deviceState = QAudio::SuspendedState; + emit stateChanged(m_deviceState); + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_operation *operation; + + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + + operation = pa_stream_cork(m_stream, 1, inputStreamSuccessCallback, 0); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + } +} + +void QPulseAudioInput::userFeed() +{ + if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) + return; +#ifdef DEBUG_PULSE +// QTime now(QTime::currentTime()); +// qDebug()<< now.second() << "s " << now.msec() << "ms :userFeed() IN"; +#endif + deviceReady(); +} + +bool QPulseAudioInput::deviceReady() +{ + if (m_pullMode) { + // reads some audio data and writes it to QIODevice + read(0,0); + } else { + // emits readyRead() so user will call read() on QIODevice to get some audio data + if (m_audioSource != 0) { + InputPrivate *a = qobject_cast(m_audioSource); + a->trigger(); + } + } + m_bytesAvailable = checkBytesReady(); + + if (m_deviceState != QAudio::ActiveState) + return true; + + if (m_intervalTime && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_intervalTime) { + emit notify(); + m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_intervalTime; + m_timeStamp.restart(); + } + + return true; +} + +qint64 QPulseAudioInput::elapsedUSecs() const +{ + if (m_deviceState == QAudio::StoppedState) + return 0; + + return m_clockStamp.elapsed() * 1000; +} + +void QPulseAudioInput::reset() +{ + stop(); + m_bytesAvailable = 0; +} + +InputPrivate::InputPrivate(QPulseAudioInput *audio) +{ + m_audioDevice = qobject_cast(audio); +} + +qint64 InputPrivate::readData(char *data, qint64 len) +{ + return m_audioDevice->read(data, len); +} + +qint64 InputPrivate::writeData(const char *data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + return 0; +} + +void InputPrivate::trigger() +{ + emit readyRead(); +} + +QT_END_NAMESPACE + +#include "moc_qaudioinput_pulse.cpp" diff --git a/src/plugins/pulseaudio/qaudioinput_pulse.h b/src/plugins/pulseaudio/qaudioinput_pulse.h new file mode 100644 index 000000000..c186f91ab --- /dev/null +++ b/src/plugins/pulseaudio/qaudioinput_pulse.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists for the convenience +// of other Qt classes. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#ifndef QAUDIOINPUTPULSE_H +#define QAUDIOINPUTPULSE_H + +#include +#include +#include +#include +#include + +#include "qaudio.h" +#include "qaudiodeviceinfo.h" +#include "qaudiosystem.h" + +#include + +QT_BEGIN_NAMESPACE + +class InputPrivate; + +class QPulseAudioInput : public QAbstractAudioInput +{ + Q_OBJECT + +public: + QPulseAudioInput(const QByteArray &device); + ~QPulseAudioInput(); + + qint64 read(char *data, qint64 len); + + void start(QIODevice *device); + QIODevice *start(); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesReady() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + void setFormat(const QAudioFormat &format); + QAudioFormat format() const; + + qint64 m_totalTimeValue; + QIODevice *m_audioSource; + QAudioFormat m_format; + QAudio::Error m_errorState; + QAudio::State m_deviceState; + +private slots: + void userFeed(); + bool deviceReady(); + +private: + int checkBytesReady(); + bool open(); + void close(); + + bool m_pullMode; + bool m_opened; + int m_bytesAvailable; + int m_bufferSize; + int m_periodSize; + int m_intervalTime; + unsigned int m_bufferTime; + unsigned int m_periodTime; + QTimer *m_timer; + qint64 m_elapsedTimeOffset; + char *audioBuffer; + pa_stream *m_stream; + QTime m_timeStamp; + QTime m_clockStamp; + QByteArray m_streamName; + QByteArray m_device; + QByteArray m_tempBuffer; +}; + +class InputPrivate : public QIODevice +{ + Q_OBJECT +public: + InputPrivate(QPulseAudioInput *audio); + ~InputPrivate() {}; + + qint64 readData(char *data, qint64 len); + qint64 writeData(const char *data, qint64 len); + + void trigger(); + +private: + QPulseAudioInput *m_audioDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.cpp b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp new file mode 100644 index 000000000..8e5b3999c --- /dev/null +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.cpp @@ -0,0 +1,573 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "qaudiooutput_pulse.h" +#include "qaudiodeviceinfo_pulse.h" +#include "qpulseaudioengine.h" +#include "qpulsehelpers.h" + +QT_BEGIN_NAMESPACE + +const int PeriodTimeMs = 20; + +static void outputStreamWriteCallback(pa_stream *stream, size_t length, void *userdata) +{ + Q_UNUSED(stream); + Q_UNUSED(length); + Q_UNUSED(userdata); + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); +} + +static void outputStreamStateCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(userdata) + pa_stream_state_t state = pa_stream_get_state(stream); +#ifdef DEBUG_PULSE + qDebug() << "Stream state: " << QPulseAudioInternal::stateToQString(state); +#endif + switch (state) { + case PA_STREAM_CREATING: + case PA_STREAM_READY: + case PA_STREAM_TERMINATED: + break; + + case PA_STREAM_FAILED: + default: + qWarning() << QString("Stream error: %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(stream)))); + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); + break; + } +} + +static void outputStreamUnderflowCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(stream) + ((QPulseAudioOutput*)userdata)->streamUnderflowCallback(); + qWarning() << "Got a buffer underflow!"; +} + +static void outputStreamOverflowCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(stream) + Q_UNUSED(userdata) + qWarning() << "Got a buffer overflow!"; +} + +static void outputStreamLatencyCallback(pa_stream *stream, void *userdata) +{ + Q_UNUSED(stream) + Q_UNUSED(userdata) + +#ifdef DEBUG_PULSE + const pa_timing_info *info = pa_stream_get_timing_info(stream); + + qDebug() << "Write index corrupt: " << info->write_index_corrupt; + qDebug() << "Write index: " << info->write_index; + qDebug() << "Read index corrupt: " << info->read_index_corrupt; + qDebug() << "Read index: " << info->read_index; + qDebug() << "Sink usec: " << info->sink_usec; + qDebug() << "Configured sink usec: " << info->configured_sink_usec; +#endif +} + +static void outputStreamSuccessCallback(pa_stream *stream, int success, void *userdata) +{ + Q_UNUSED(stream); + Q_UNUSED(success); + Q_UNUSED(userdata); + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); +} + +static void outputStreamDrainComplete(pa_stream *stream, int success, void *userdata) +{ + Q_UNUSED(stream); + Q_UNUSED(success); + Q_UNUSED(userdata); + +#ifdef DEBUG_PULSE + qDebug() << "Draining completed successfully: " << (bool)success; +#endif +} + +QPulseAudioOutput::QPulseAudioOutput(const QByteArray &device) + : m_device(device) + , m_errorState(QAudio::NoError) + , m_deviceState(QAudio::StoppedState) + , m_pullMode(true) + , m_opened(false) + , m_audioSource(0) + , m_bytesAvailable(0) + , m_stream(0) + , m_notifyInterval(1000) + , m_periodSize(0) + , m_bufferSize(0) + , m_totalTimeValue(0) + , m_tickTimer(new QTimer(this)) + , m_audioBuffer(0) + , m_resuming(false) +{ + connect(m_tickTimer, SIGNAL(timeout()), SLOT(userFeed())); +} + +QPulseAudioOutput::~QPulseAudioOutput() +{ + close(); + disconnect(m_tickTimer, SIGNAL(timeout())); + QCoreApplication::processEvents(); +} + +QAudio::Error QPulseAudioOutput::error() const +{ + return m_errorState; +} + +QAudio::State QPulseAudioOutput::state() const +{ + return m_deviceState; +} + +void QPulseAudioOutput::streamUnderflowCallback() +{ + if (m_deviceState != QAudio::IdleState && !m_resuming) { + m_errorState = QAudio::UnderrunError; + emit errorChanged(m_errorState); + m_deviceState = QAudio::IdleState; + emit stateChanged(m_deviceState); + } +} + +void QPulseAudioOutput::start(QIODevice *device) +{ + if (m_deviceState != QAudio::StoppedState) + m_deviceState = QAudio::StoppedState; + + m_errorState = QAudio::NoError; + + // Handle change of mode + if (m_audioSource && !m_pullMode) { + delete m_audioSource; + m_audioSource = 0; + } + + close(); + + m_pullMode = true; + m_audioSource = device; + + m_deviceState = QAudio::ActiveState; + + open(); + + emit stateChanged(m_deviceState); +} + +QIODevice *QPulseAudioOutput::start() +{ + if (m_deviceState != QAudio::StoppedState) + m_deviceState = QAudio::StoppedState; + + m_errorState = QAudio::NoError; + + // Handle change of mode + if (m_audioSource && !m_pullMode) { + delete m_audioSource; + m_audioSource = 0; + } + + close(); + + m_audioSource = new OutputPrivate(this); + m_audioSource->open(QIODevice::WriteOnly|QIODevice::Unbuffered); + m_pullMode = false; + + m_deviceState = QAudio::IdleState; + + open(); + + emit stateChanged(m_deviceState); + + return m_audioSource; +} + +bool QPulseAudioOutput::open() +{ + if (m_opened) + return false; + + pa_sample_spec spec = QPulseAudioInternal::audioFormatToSampleSpec(m_format); + + if (!pa_sample_spec_valid(&spec)) { + m_errorState = QAudio::OpenError; + m_deviceState = QAudio::StoppedState; + return false; + } + + m_totalTimeValue = 0; + m_elapsedTimeOffset = 0; + m_timeStamp.restart(); + + if (m_streamName.isNull()) + m_streamName = QString(QLatin1String("QtmPulseStream-%1-%2")).arg(::getpid()).arg(quintptr(this)).toUtf8(); + +#ifdef DEBUG_PULSE + qDebug() << "Format: " << QPulseAudioInternal::sampleFormatToQString(spec.format); + qDebug() << "Rate: " << spec.rate; + qDebug() << "Channels: " << spec.channels; + qDebug() << "Frame size: " << pa_frame_size(&spec); +#endif + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + m_stream = pa_stream_new(pulseEngine->context(), m_streamName.constData(), &spec, 0); + + pa_stream_set_state_callback(m_stream, outputStreamStateCallback, this); + pa_stream_set_write_callback(m_stream, outputStreamWriteCallback, this); + + pa_stream_set_underflow_callback(m_stream, outputStreamUnderflowCallback, this); + pa_stream_set_overflow_callback(m_stream, outputStreamOverflowCallback, this); + pa_stream_set_latency_update_callback(m_stream, outputStreamLatencyCallback, this); + + if (pa_stream_connect_playback(m_stream, m_device.data(), NULL, (pa_stream_flags_t)0, NULL, NULL) < 0) { + qWarning() << "pa_stream_connect_playback() failed!"; + return false; + } + + while (pa_stream_get_state(m_stream) != PA_STREAM_READY) { + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + } + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + +#ifdef DEBUG_PULSE + const pa_buffer_attr *buffer = pa_stream_get_buffer_attr(m_stream); + + qDebug() << "Buffering info:"; + qDebug() << "\tMax length: " << buffer->maxlength; + qDebug() << "\tTarget length: " << buffer->tlength; + qDebug() << "\tPre-buffering: " << buffer->prebuf; + qDebug() << "\tMinimum request: " << buffer->minreq; + qDebug() << "\tFragment size: " << buffer->fragsize; +#endif + + m_periodSize = pa_usec_to_bytes(PeriodTimeMs*1000, &spec); + m_opened = true; + m_tickTimer->start(PeriodTimeMs); + + m_elapsedTimeOffset = 0; + m_timeStamp.restart(); + m_clockStamp.restart(); + + return true; +} + +void QPulseAudioOutput::userFeed() +{ + if (m_deviceState == QAudio::StoppedState || m_deviceState == QAudio::SuspendedState) + return; + + if (m_deviceState == QAudio::IdleState) + m_bytesAvailable = bytesFree(); + + deviceReady(); +} + +void QPulseAudioOutput::close() +{ + m_tickTimer->stop(); + + if (m_stream) { + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + pa_operation *o; + + pa_stream_set_write_callback(m_stream, NULL, NULL); + + if (!(o = pa_stream_drain(m_stream, outputStreamDrainComplete, NULL))) { + qWarning() << QString("pa_stream_drain(): %1").arg(pa_strerror(pa_context_errno(pa_stream_get_context(m_stream)))); + return; + } + pa_operation_unref(o); + + pa_stream_disconnect(m_stream); + pa_stream_unref(m_stream); + m_stream = NULL; + + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + } + + if (!m_pullMode && m_audioSource) { + delete m_audioSource; + m_audioSource = 0; + } + m_opened = false; +} + +bool QPulseAudioOutput::deviceReady() +{ + m_resuming = false; + + if (m_pullMode) { + int l = 0; + int chunks = m_bytesAvailable/m_periodSize; + if (chunks==0) { + m_bytesAvailable = bytesFree(); + return false; + } + + char buffer[m_periodSize]; + + l = m_audioSource->read(buffer, m_periodSize); + if (l > 0) { + if (m_deviceState != QAudio::ActiveState) + return true; + + qint64 bytesWritten = write(buffer, l); + Q_UNUSED(bytesWritten); + } + } + + if (m_deviceState != QAudio::ActiveState) + return true; + + if (m_notifyInterval && (m_timeStamp.elapsed() + m_elapsedTimeOffset) > m_notifyInterval) { + emit notify(); + m_elapsedTimeOffset = m_timeStamp.elapsed() + m_elapsedTimeOffset - m_notifyInterval; + m_timeStamp.restart(); + } + + return true; +} + +qint64 QPulseAudioOutput::write(const char *data, qint64 len) +{ + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + len = qMin(len, static_cast(pa_stream_writable_size(m_stream))); + pa_stream_write(m_stream, data, len, 0, 0, PA_SEEK_RELATIVE); + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + m_totalTimeValue += len; + + m_errorState = QAudio::NoError; + if (m_deviceState != QAudio::ActiveState) { + m_deviceState = QAudio::ActiveState; + emit stateChanged(m_deviceState); + } + + return len; +} + +void QPulseAudioOutput::stop() +{ + if (m_deviceState == QAudio::StoppedState) + return; + + m_errorState = QAudio::NoError; + m_deviceState = QAudio::StoppedState; + close(); + emit stateChanged(m_deviceState); +} + +int QPulseAudioOutput::bytesFree() const +{ + if (m_deviceState != QAudio::ActiveState && m_deviceState != QAudio::IdleState) + return 0; + + return pa_stream_writable_size(m_stream); +} + +int QPulseAudioOutput::periodSize() const +{ + return m_periodSize; +} + +void QPulseAudioOutput::setBufferSize(int value) +{ + m_bufferSize = value; +} + +int QPulseAudioOutput::bufferSize() const +{ + return m_bufferSize; +} + +void QPulseAudioOutput::setNotifyInterval(int ms) +{ + m_notifyInterval = qMax(0, ms); +} + +int QPulseAudioOutput::notifyInterval() const +{ + return m_notifyInterval; +} + +qint64 QPulseAudioOutput::processedUSecs() const +{ + qint64 result = qint64(1000000) * m_totalTimeValue / + (m_format.channels() * (m_format.sampleSize() / 8)) / + m_format.frequency(); + + return result; +} + +void QPulseAudioOutput::resume() +{ + if (m_deviceState == QAudio::SuspendedState) { + m_resuming = true; + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + + pa_operation *operation = pa_stream_cork(m_stream, 0, outputStreamSuccessCallback, NULL); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + + pa_operation_unref(operation); + + operation = pa_stream_trigger(m_stream, outputStreamSuccessCallback, NULL); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + + m_deviceState = QAudio::ActiveState; + + m_errorState = QAudio::NoError; + m_tickTimer->start(PeriodTimeMs); + + emit stateChanged(m_deviceState); + } +} + +void QPulseAudioOutput::setFormat(const QAudioFormat &format) +{ + m_format = format; +} + +QAudioFormat QPulseAudioOutput::format() const +{ + return m_format; +} + +void QPulseAudioOutput::suspend() +{ + if (m_deviceState == QAudio::ActiveState || m_deviceState == QAudio::IdleState) { + m_tickTimer->stop(); + m_deviceState = QAudio::SuspendedState; + m_errorState = QAudio::NoError; + emit stateChanged(m_deviceState); + + QPulseAudioEngine *pulseEngine = QPulseAudioEngine::instance(); + pa_operation *operation; + + pa_threaded_mainloop_lock(pulseEngine->mainloop()); + + operation = pa_stream_cork(m_stream, 1, outputStreamSuccessCallback, NULL); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(pulseEngine->mainloop()); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(pulseEngine->mainloop()); + } +} + +qint64 QPulseAudioOutput::elapsedUSecs() const +{ + if (m_deviceState == QAudio::StoppedState) + return 0; + + return m_clockStamp.elapsed() * 1000; +} + +void QPulseAudioOutput::reset() +{ + stop(); +} + +OutputPrivate::OutputPrivate(QPulseAudioOutput *audio) +{ + m_audioDevice = qobject_cast(audio); +} + +qint64 OutputPrivate::readData(char *data, qint64 len) +{ + Q_UNUSED(data) + Q_UNUSED(len) + + return 0; +} + +qint64 OutputPrivate::writeData(const char *data, qint64 len) +{ + int retry = 0; + qint64 written = 0; + + if ((m_audioDevice->m_deviceState == QAudio::ActiveState) + ||(m_audioDevice->m_deviceState == QAudio::IdleState)) { + while(written < len) { + int chunk = m_audioDevice->write(data+written, (len-written)); + if (chunk <= 0) + retry++; + written+=chunk; + if (retry > 10) + return written; + } + } + + return written; +} + +QT_END_NAMESPACE + +#include "moc_qaudiooutput_pulse.cpp" diff --git a/src/plugins/pulseaudio/qaudiooutput_pulse.h b/src/plugins/pulseaudio/qaudiooutput_pulse.h new file mode 100644 index 000000000..064754dbc --- /dev/null +++ b/src/plugins/pulseaudio/qaudiooutput_pulse.h @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QAUDIOOUTPUTPULSE_H +#define QAUDIOOUTPUTPULSE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include + +#include "qaudio.h" +#include "qaudiodeviceinfo.h" +#include "qaudiosystem.h" + +#include + +QT_BEGIN_NAMESPACE + +class QPulseAudioOutput : public QAbstractAudioOutput +{ + friend class OutputPrivate; + Q_OBJECT + +public: + QPulseAudioOutput(const QByteArray &device); + ~QPulseAudioOutput(); + + void start(QIODevice *device); + QIODevice *start(); + void stop(); + void reset(); + void suspend(); + void resume(); + int bytesFree() const; + int periodSize() const; + void setBufferSize(int value); + int bufferSize() const; + void setNotifyInterval(int milliSeconds); + int notifyInterval() const; + qint64 processedUSecs() const; + qint64 elapsedUSecs() const; + QAudio::Error error() const; + QAudio::State state() const; + void setFormat(const QAudioFormat &format); + QAudioFormat format() const; + +public: + void streamUnderflowCallback(); + +private: + bool open(); + void close(); + qint64 write(const char *data, qint64 len); + +private Q_SLOTS: + bool deviceReady(); + void userFeed(); + +private: + QByteArray m_device; + QByteArray m_streamName; + QAudioFormat m_format; + QAudio::Error m_errorState; + QAudio::State m_deviceState; + bool m_pullMode; + bool m_opened; + QIODevice *m_audioSource; + int m_bytesAvailable; + QTimer m_periodTimer; + pa_stream *m_stream; + int m_notifyInterval; + int m_periodSize; + int m_bufferSize; + QTime m_clockStamp; + qint64 m_totalTimeValue; + QTimer *m_tickTimer; + char *m_audioBuffer; + QTime m_timeStamp; + qint64 m_elapsedTimeOffset; + bool m_resuming; +}; + +class OutputPrivate : public QIODevice +{ + friend class QPulseAudioOutput; + Q_OBJECT + +public: + OutputPrivate(QPulseAudioOutput *audio); + virtual ~OutputPrivate() {} + +protected: + qint64 readData(char *data, qint64 len); + qint64 writeData(const char *data, qint64 len); + +private: + QPulseAudioOutput *m_audioDevice; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/pulseaudio/qpulseaudioengine.cpp b/src/plugins/pulseaudio/qpulseaudioengine.cpp new file mode 100644 index 000000000..da0963fa9 --- /dev/null +++ b/src/plugins/pulseaudio/qpulseaudioengine.cpp @@ -0,0 +1,354 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include +#include "qpulseaudioengine.h" +#include "qaudiodeviceinfo_pulse.h" +#include "qaudiooutput_pulse.h" +#include "qpulsehelpers.h" + +QT_BEGIN_NAMESPACE + +static void serverInfoCallback(pa_context *context, const pa_server_info *info, void *userdata) +{ + if (!info) { + qWarning() << QString("Failed to get server information: %s").arg(pa_strerror(pa_context_errno(context))); + return; + } + +#ifdef DEBUG_PULSE + char ss[PA_SAMPLE_SPEC_SNPRINT_MAX], cm[PA_CHANNEL_MAP_SNPRINT_MAX]; + + pa_sample_spec_snprint(ss, sizeof(ss), &info->sample_spec); + pa_channel_map_snprint(cm, sizeof(cm), &info->channel_map); + + qDebug() << QString("User name: %1\n" + "Host Name: %2\n" + "Server Name: %3\n" + "Server Version: %4\n" + "Default Sample Specification: %5\n" + "Default Channel Map: %6\n" + "Default Sink: %7\n" + "Default Source: %8\n").arg( + info->user_name, + info->host_name, + info->server_name, + info->server_version, + ss, + cm, + info->default_sink_name, + info->default_source_name); +#endif + + QPulseAudioEngine *pulseEngine = static_cast(userdata); + pulseEngine->m_defaultSink = info->default_sink_name; + pulseEngine->m_defaultSource = info->default_source_name; + + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); +} + +static void sinkInfoCallback(pa_context *context, const pa_sink_info *info, int isLast, void *userdata) +{ + QPulseAudioEngine *pulseEngine = static_cast(userdata); + QMap stateMap; + stateMap[PA_SINK_INVALID_STATE] = "n/a"; + stateMap[PA_SINK_RUNNING] = "RUNNING"; + stateMap[PA_SINK_IDLE] = "IDLE"; + stateMap[PA_SINK_SUSPENDED] = "SUSPENDED"; + + if (isLast < 0) { + qWarning() << QString("Failed to get sink information: %s").arg(pa_strerror(pa_context_errno(context))); + return; + } + + if (isLast) { + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); + return; + } + + Q_ASSERT(info); + +#ifdef DEBUG_PULSE + qDebug() << QString("Sink #%1\n" + "\tState: %2\n" + "\tName: %3\n" + "\tDescription: %4\n" + ).arg(QString::number(info->index), + stateMap.value(info->state), + info->name, + info->description); +#endif + + QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec); + pulseEngine->m_preferredFormats.insert(info->name, format); + pulseEngine->m_sinks.append(info->name); +} + +static void sourceInfoCallback(pa_context *context, const pa_source_info *info, int isLast, void *userdata) +{ + Q_UNUSED(context) + QPulseAudioEngine *pulseEngine = static_cast(userdata); + + QMap stateMap; + stateMap[PA_SOURCE_INVALID_STATE] = "n/a"; + stateMap[PA_SOURCE_RUNNING] = "RUNNING"; + stateMap[PA_SOURCE_IDLE] = "IDLE"; + stateMap[PA_SOURCE_SUSPENDED] = "SUSPENDED"; + + if (isLast) { + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); + return; + } + + Q_ASSERT(info); + +#ifdef DEBUG_PULSE + qDebug() << QString("Source #%1\n" + "\tState: %2\n" + "\tName: %3\n" + "\tDescription: %4\n" + ).arg(QString::number(info->index), + stateMap.value(info->state), + info->name, + info->description); +#endif + + QAudioFormat format = QPulseAudioInternal::sampleSpecToAudioFormat(info->sample_spec); + pulseEngine->m_preferredFormats.insert(info->name, format); + pulseEngine->m_sources.append(info->name); +} + +static void contextStateCallbackInit(pa_context *context, void *userdata) +{ + Q_UNUSED(context); +#ifdef DEBUG_PULSE + qDebug() << QPulseAudioInternal::stateToQString(pa_context_get_state(context)); +#endif + QPulseAudioEngine *pulseEngine = reinterpret_cast(userdata); + pa_threaded_mainloop_signal(pulseEngine->mainloop(), 0); +} + +static void contextStateCallback(pa_context *context, void *userdata) +{ + Q_UNUSED(userdata); + Q_UNUSED(context); + +#ifdef DEBUG_PULSE + pa_context_state_t state = pa_context_get_state(context); + qDebug() << QPulseAudioInternal::stateToQString(state); +#endif +} + +Q_GLOBAL_STATIC(QPulseAudioEngine, pulseEngine); + +QPulseAudioEngine::QPulseAudioEngine(QObject *parent) + : QObject(parent) +{ + bool keepGoing = true; + bool ok = true; + + m_mainLoop = pa_threaded_mainloop_new(); + if (m_mainLoop == 0) { + qWarning("Unable to create pulseaudio mainloop"); + return; + } + + if (pa_threaded_mainloop_start(m_mainLoop) != 0) { + qWarning("Unable to start pulseaudio mainloop"); + pa_threaded_mainloop_free(m_mainLoop); + return; + } + + m_mainLoopApi = pa_threaded_mainloop_get_api(m_mainLoop); + + pa_threaded_mainloop_lock(m_mainLoop); + + m_context = pa_context_new(m_mainLoopApi, QString(QLatin1String("QtmPulseContext:%1")).arg(::getpid()).toAscii().constData()); + pa_context_set_state_callback(m_context, contextStateCallbackInit, this); + + if (!m_context) { + qWarning("Unable to create new pulseaudio context"); + pa_threaded_mainloop_free(m_mainLoop); + return; + } + + if (pa_context_connect(m_context, NULL, (pa_context_flags_t)0, NULL) < 0) { + qWarning("Unable to create a connection to the pulseaudio context"); + pa_context_unref(m_context); + pa_threaded_mainloop_free(m_mainLoop); + return; + } + + pa_threaded_mainloop_wait(m_mainLoop); + + while (keepGoing) { + switch (pa_context_get_state(m_context)) { + case PA_CONTEXT_CONNECTING: + case PA_CONTEXT_AUTHORIZING: + case PA_CONTEXT_SETTING_NAME: + break; + + case PA_CONTEXT_READY: +#ifdef DEBUG_PULSE + qDebug("Connection established."); +#endif + keepGoing = false; + break; + + case PA_CONTEXT_TERMINATED: + qCritical("Context terminated."); + keepGoing = false; + ok = false; + break; + + case PA_CONTEXT_FAILED: + default: + qCritical() << QString("Connection failure: %1").arg(pa_strerror(pa_context_errno(m_context))); + keepGoing = false; + ok = false; + } + + if (keepGoing) { + pa_threaded_mainloop_wait(m_mainLoop); + } + } + + if (ok) { + pa_context_set_state_callback(m_context, contextStateCallback, this); + } else { + if (m_context) { + pa_context_unref(m_context); + m_context = 0; + } + } + + pa_threaded_mainloop_unlock(m_mainLoop); + + serverInfo(); + sinks(); + sources(); +} + +QPulseAudioEngine::~QPulseAudioEngine() +{ + if (m_context) { + pa_threaded_mainloop_lock(m_mainLoop); + pa_context_disconnect(m_context); + pa_threaded_mainloop_unlock(m_mainLoop); + m_context = 0; + } + + if (m_mainLoop) { + pa_threaded_mainloop_stop(m_mainLoop); + pa_threaded_mainloop_free(m_mainLoop); + m_mainLoop = 0; + } +} + +void QPulseAudioEngine::serverInfo() +{ + pa_operation *operation; + + pa_threaded_mainloop_lock(m_mainLoop); + + operation = pa_context_get_server_info(m_context, serverInfoCallback, this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(m_mainLoop); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(m_mainLoop); +} + +void QPulseAudioEngine::sinks() +{ + pa_operation *operation; + + pa_threaded_mainloop_lock(m_mainLoop); + + operation = pa_context_get_sink_info_list(m_context, sinkInfoCallback, this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(m_mainLoop); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(m_mainLoop); + + // Swap the default sink to index 0 + m_sinks.removeOne(m_defaultSink); + m_sinks.prepend(m_defaultSink); +} + +void QPulseAudioEngine::sources() +{ + pa_operation *operation; + + pa_threaded_mainloop_lock(m_mainLoop); + + operation = pa_context_get_source_info_list(m_context, sourceInfoCallback, this); + + while (pa_operation_get_state(operation) == PA_OPERATION_RUNNING) + pa_threaded_mainloop_wait(m_mainLoop); + + pa_operation_unref(operation); + + pa_threaded_mainloop_unlock(m_mainLoop); + + // Swap the default source to index 0 + m_sources.removeOne(m_defaultSource); + m_sources.prepend(m_defaultSource); +} + +QPulseAudioEngine *QPulseAudioEngine::instance() +{ + return pulseEngine(); +} + +QList QPulseAudioEngine::availableDevices(QAudio::Mode mode) const +{ + return mode == QAudio::AudioOutput ? m_sinks : m_sources; +} + +QT_END_NAMESPACE diff --git a/src/plugins/pulseaudio/qpulseaudioengine.h b/src/plugins/pulseaudio/qpulseaudioengine.h new file mode 100644 index 000000000..3519be79d --- /dev/null +++ b/src/plugins/pulseaudio/qpulseaudioengine.h @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPULSEAUDIOENGINE_H +#define QPULSEAUDIOENGINE_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include +#include +#include +#include +#include "qpulsehelpers.h" +#include + +QT_BEGIN_NAMESPACE + +class QPulseAudioEngine : public QObject +{ + Q_OBJECT + +public: + QPulseAudioEngine(QObject *parent = 0); + ~QPulseAudioEngine(); + + static QPulseAudioEngine *instance(); + pa_threaded_mainloop *mainloop() { return m_mainLoop; } + pa_context *context() { return m_context; } + + QList availableDevices(QAudio::Mode mode) const; + +private: + void serverInfo(); + void sinks(); + void sources(); + +public: + QList m_sinks; + QList m_sources; + QMap m_preferredFormats; + + QByteArray m_defaultSink; + QByteArray m_defaultSource; + +private: + pa_mainloop_api *m_mainLoopApi; + pa_threaded_mainloop *m_mainLoop; + pa_context *m_context; + }; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/pulseaudio/qpulseaudioplugin.cpp b/src/plugins/pulseaudio/qpulseaudioplugin.cpp new file mode 100644 index 000000000..99ff09211 --- /dev/null +++ b/src/plugins/pulseaudio/qpulseaudioplugin.cpp @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qpulseaudioplugin.h" +#include "qaudiodeviceinfo_pulse.h" +#include "qaudiooutput_pulse.h" +#include "qaudioinput_pulse.h" +#include "qpulseaudioengine.h" + +QT_BEGIN_NAMESPACE + +QPulseAudioPlugin::QPulseAudioPlugin(QObject *parent) + : QAudioSystemPlugin(parent) + , m_pulseEngine(QPulseAudioEngine::instance()) +{ +} + +QStringList QPulseAudioPlugin::keys() const +{ + return QStringList() << "default"; +} + +QList QPulseAudioPlugin::availableDevices(QAudio::Mode mode) const +{ + return m_pulseEngine->availableDevices(mode); +} + +QAbstractAudioInput *QPulseAudioPlugin::createInput(const QByteArray &device) +{ + QPulseAudioInput *input = new QPulseAudioInput(device); + return input; +} + +QAbstractAudioOutput *QPulseAudioPlugin::createOutput(const QByteArray &device) +{ + + QPulseAudioOutput *output = new QPulseAudioOutput(device); + return output; +} + +QAbstractAudioDeviceInfo *QPulseAudioPlugin::createDeviceInfo(const QByteArray &device, QAudio::Mode mode) +{ + QPulseAudioDeviceInfo *deviceInfo = new QPulseAudioDeviceInfo(device, mode); + return deviceInfo; +} + +Q_EXPORT_PLUGIN2(qtmedia_pulse, QPulseAudioPlugin); + +QT_END_NAMESPACE diff --git a/src/plugins/pulseaudio/qpulseaudioplugin.h b/src/plugins/pulseaudio/qpulseaudioplugin.h new file mode 100644 index 000000000..e90270e0c --- /dev/null +++ b/src/plugins/pulseaudio/qpulseaudioplugin.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPULSEAUDIOPLUGIN_H +#define QPULSEAUDIOPLUGIN_H + +#include + +QT_BEGIN_NAMESPACE + +class QPulseAudioEngine; + +class QPulseAudioPlugin : public QAudioSystemPlugin +{ + Q_OBJECT + +public: + QPulseAudioPlugin(QObject *parent = 0); + ~QPulseAudioPlugin() {} + + QStringList keys() const; + QList availableDevices(QAudio::Mode mode) const; + QAbstractAudioInput *createInput(const QByteArray &device); + QAbstractAudioOutput *createOutput(const QByteArray &device); + QAbstractAudioDeviceInfo *createDeviceInfo(const QByteArray &device, QAudio::Mode mode); + +private: + QPulseAudioEngine *m_pulseEngine; +}; + +QT_BEGIN_NAMESPACE + +#endif diff --git a/src/plugins/pulseaudio/qpulsehelpers.cpp b/src/plugins/pulseaudio/qpulsehelpers.cpp new file mode 100644 index 000000000..57e50758f --- /dev/null +++ b/src/plugins/pulseaudio/qpulsehelpers.cpp @@ -0,0 +1,220 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qpulsehelpers.h" + +QT_BEGIN_NAMESPACE + +namespace QPulseAudioInternal +{ +pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format) +{ + pa_sample_spec spec; + + spec.rate = format.frequency(); + spec.channels = format.channels(); + + if (format.sampleSize() == 8) { + spec.format = PA_SAMPLE_U8; + } else if (format.sampleSize() == 16) { + switch (format.byteOrder()) { + case QAudioFormat::BigEndian: spec.format = PA_SAMPLE_S16BE; break; + case QAudioFormat::LittleEndian: spec.format = PA_SAMPLE_S16LE; break; + } + } else if (format.sampleSize() == 24) { + switch (format.byteOrder()) { + case QAudioFormat::BigEndian: spec.format = PA_SAMPLE_S24BE; break; + case QAudioFormat::LittleEndian: spec.format = PA_SAMPLE_S24LE; break; + } + } else if (format.sampleSize() == 32) { + switch (format.byteOrder()) { + case QAudioFormat::BigEndian: + format.sampleType() == QAudioFormat::Float ? spec.format = PA_SAMPLE_FLOAT32BE : spec.format = PA_SAMPLE_S32BE; + break; + case QAudioFormat::LittleEndian: + format.sampleType() == QAudioFormat::Float ? spec.format = PA_SAMPLE_FLOAT32LE : spec.format = PA_SAMPLE_S32LE; + break; + } + } else { + spec.format = PA_SAMPLE_INVALID; + } + + return spec; +} + +#ifdef DEBUG_PULSE +QString stateToQString(pa_stream_state_t state) +{ + switch (state) + { + case PA_STREAM_UNCONNECTED: return "Unconnected"; + case PA_STREAM_CREATING: return "Creating"; + case PA_STREAM_READY: return "Ready"; + case PA_STREAM_FAILED: return "Failed"; + case PA_STREAM_TERMINATED: return "Terminated"; + } + + return QString("Unknown state: %0").arg(state); +} + +QString sampleFormatToQString(pa_sample_format format) +{ + switch (format) + { + case PA_SAMPLE_U8: return "Unsigned 8 Bit PCM."; + case PA_SAMPLE_ALAW: return "8 Bit a-Law "; + case PA_SAMPLE_ULAW: return "8 Bit mu-Law"; + case PA_SAMPLE_S16LE: return "Signed 16 Bit PCM, little endian (PC)."; + case PA_SAMPLE_S16BE: return "Signed 16 Bit PCM, big endian."; + case PA_SAMPLE_FLOAT32LE: return "32 Bit IEEE floating point, little endian (PC), range -1.0 to 1.0"; + case PA_SAMPLE_FLOAT32BE: return "32 Bit IEEE floating point, big endian, range -1.0 to 1.0"; + case PA_SAMPLE_S32LE: return "Signed 32 Bit PCM, little endian (PC)."; + case PA_SAMPLE_S32BE: return "Signed 32 Bit PCM, big endian."; + case PA_SAMPLE_S24LE: return "Signed 24 Bit PCM packed, little endian (PC)."; + case PA_SAMPLE_S24BE: return "Signed 24 Bit PCM packed, big endian."; + case PA_SAMPLE_S24_32LE: return "Signed 24 Bit PCM in LSB of 32 Bit words, little endian (PC)."; + case PA_SAMPLE_S24_32BE: return "Signed 24 Bit PCM in LSB of 32 Bit words, big endian."; + case PA_SAMPLE_MAX: return "Upper limit of valid sample types."; + case PA_SAMPLE_INVALID: return "Invalid sample format"; + } + + return QString("Invalid value: %0").arg(format); +} + +QString stateToQString(pa_context_state_t state) +{ + switch (state) + { + case PA_CONTEXT_UNCONNECTED: return "Unconnected"; + case PA_CONTEXT_CONNECTING: return "Connecting"; + case PA_CONTEXT_AUTHORIZING: return "Authorizing"; + case PA_CONTEXT_SETTING_NAME: return "Setting Name"; + case PA_CONTEXT_READY: return "Ready"; + case PA_CONTEXT_FAILED: return "Failed"; + case PA_CONTEXT_TERMINATED: return "Terminated"; + } + + return QString("Unknown state: %0").arg(state); +} +#endif + +QAudioFormat sampleSpecToAudioFormat(pa_sample_spec spec) +{ + QAudioFormat format; + format.setFrequency(spec.rate); + format.setChannelCount(spec.channels); + format.setCodec("audio/pcm"); + + switch (spec.format) { + case PA_SAMPLE_U8: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::UnSignedInt); + format.setSampleSize(8); + break; + case PA_SAMPLE_ALAW: + // TODO: + break; + case PA_SAMPLE_ULAW: + // TODO: + break; + case PA_SAMPLE_S16LE: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(16); + break; + case PA_SAMPLE_S16BE: + format.setByteOrder(QAudioFormat::BigEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(16); + break; + case PA_SAMPLE_FLOAT32LE: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::Float); + format.setSampleSize(32); + break; + case PA_SAMPLE_FLOAT32BE: + format.setByteOrder(QAudioFormat::BigEndian); + format.setSampleType(QAudioFormat::Float); + format.setSampleSize(32); + break; + case PA_SAMPLE_S32LE: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(32); + break; + case PA_SAMPLE_S32BE: + format.setByteOrder(QAudioFormat::BigEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(32); + break; + case PA_SAMPLE_S24LE: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(24); + break; + case PA_SAMPLE_S24BE: + format.setByteOrder(QAudioFormat::BigEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(24); + break; + case PA_SAMPLE_S24_32LE: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(24); + break; + case PA_SAMPLE_S24_32BE: + format.setByteOrder(QAudioFormat::BigEndian); + format.setSampleType(QAudioFormat::SignedInt); + format.setSampleSize(24); + break; + case PA_SAMPLE_MAX: + case PA_SAMPLE_INVALID: + default: + format.setByteOrder(QAudioFormat::LittleEndian); + format.setSampleType(QAudioFormat::Unknown); + format.setSampleSize(0); + } + + return format; +} +} + +QT_END_NAMESPACE diff --git a/src/plugins/pulseaudio/qpulsehelpers.h b/src/plugins/pulseaudio/qpulsehelpers.h new file mode 100644 index 000000000..255f0cf90 --- /dev/null +++ b/src/plugins/pulseaudio/qpulsehelpers.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QPULSEHELPER_H +#define QPULSEHELPER_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qaudiodeviceinfo.h" +#include +#include + +QT_BEGIN_NAMESPACE + +namespace QPulseAudioInternal +{ +pa_sample_spec audioFormatToSampleSpec(const QAudioFormat &format); +QString stateToQString(pa_stream_state_t state); +QString stateToQString(pa_context_state_t state); +QString sampleFormatToQString(pa_sample_format format); +QAudioFormat sampleSpecToAudioFormat(pa_sample_spec spec); +} + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/mediaplayer/mediaplayer.pri b/src/plugins/qt7/mediaplayer/mediaplayer.pri new file mode 100644 index 000000000..2edb1d2c7 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/mediaplayer.pri @@ -0,0 +1,16 @@ +INCLUDEPATH += $$PWD + +DEFINES += QMEDIA_QT7_PLAYER + +HEADERS += \ + $$PWD/qt7playercontrol.h \ + $$PWD/qt7playermetadata.h \ + $$PWD/qt7playerservice.h \ + $$PWD/qt7playersession.h + +OBJECTIVE_SOURCES += \ + $$PWD/qt7playercontrol.mm \ + $$PWD/qt7playermetadata.mm \ + $$PWD/qt7playerservice.mm \ + $$PWD/qt7playersession.mm + diff --git a/src/plugins/qt7/mediaplayer/qt7playercontrol.h b/src/plugins/qt7/mediaplayer/qt7playercontrol.h new file mode 100644 index 000000000..064c0ccd3 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playercontrol.h @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERCONTROL_H +#define QT7PLAYERCONTROL_H + +#include + +#include +#include + +#include + + +QT_BEGIN_NAMESPACE + +class QT7PlayerSession; +class QT7PlayerService; +class QMediaPlaylist; +class QMediaPlaylistNavigator; + +class QT7PlayerControl : public QMediaPlayerControl +{ +Q_OBJECT +public: + QT7PlayerControl(QObject *parent = 0); + ~QT7PlayerControl(); + + void setSession(QT7PlayerSession *session); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &content, QIODevice *stream); + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + +public Q_SLOTS: + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + +private: + QT7PlayerSession *m_session; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playercontrol.mm b/src/plugins/qt7/mediaplayer/qt7playercontrol.mm new file mode 100644 index 000000000..ddfcc3570 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playercontrol.mm @@ -0,0 +1,191 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7playercontrol.h" +#include "qt7playersession.h" + +#include + +#include +#include + +QT_USE_NAMESPACE + +QT7PlayerControl::QT7PlayerControl(QObject *parent) + : QMediaPlayerControl(parent) +{ +} + +QT7PlayerControl::~QT7PlayerControl() +{ +} + +void QT7PlayerControl::setSession(QT7PlayerSession *session) +{ + m_session = session; + + connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(positionChanged(qint64))); + connect(m_session, SIGNAL(durationChanged(qint64)), this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(m_session, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int))); + connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(audioAvailableChanged(bool)), this, SIGNAL(audioAvailableChanged(bool))); + connect(m_session, SIGNAL(videoAvailableChanged(bool)), this, SIGNAL(videoAvailableChanged(bool))); + connect(m_session, SIGNAL(error(int,QString)), this, SIGNAL(error(int,QString))); +} + +qint64 QT7PlayerControl::position() const +{ + return m_session->position(); +} + +qint64 QT7PlayerControl::duration() const +{ + return m_session->duration(); +} + +QMediaPlayer::State QT7PlayerControl::state() const +{ + return m_session->state(); +} + +QMediaPlayer::MediaStatus QT7PlayerControl::mediaStatus() const +{ + return m_session->mediaStatus(); +} + +int QT7PlayerControl::bufferStatus() const +{ + return m_session->bufferStatus(); +} + +int QT7PlayerControl::volume() const +{ + return m_session->volume(); +} + +bool QT7PlayerControl::isMuted() const +{ + return m_session->isMuted(); +} + +bool QT7PlayerControl::isSeekable() const +{ + return m_session->isSeekable(); +} + +QMediaTimeRange QT7PlayerControl::availablePlaybackRanges() const +{ + return m_session->availablePlaybackRanges(); +} + +qreal QT7PlayerControl::playbackRate() const +{ + return m_session->playbackRate(); +} + +void QT7PlayerControl::setPlaybackRate(qreal rate) +{ + m_session->setPlaybackRate(rate); +} + +void QT7PlayerControl::setPosition(qint64 pos) +{ + m_session->setPosition(pos); +} + +void QT7PlayerControl::play() +{ + m_session->play(); +} + +void QT7PlayerControl::pause() +{ + m_session->pause(); +} + +void QT7PlayerControl::stop() +{ + m_session->stop(); +} + +void QT7PlayerControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +void QT7PlayerControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +QMediaContent QT7PlayerControl::media() const +{ + return m_session->media(); +} + +const QIODevice *QT7PlayerControl::mediaStream() const +{ + return m_session->mediaStream(); +} + +void QT7PlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + m_session->setMedia(content, stream); + + emit mediaChanged(content); +} + +bool QT7PlayerControl::isAudioAvailable() const +{ + return m_session->isAudioAvailable(); +} + +bool QT7PlayerControl::isVideoAvailable() const +{ + return m_session->isVideoAvailable(); +} + + +#include "moc_qt7playercontrol.cpp" diff --git a/src/plugins/qt7/mediaplayer/qt7playermetadata.h b/src/plugins/qt7/mediaplayer/qt7playermetadata.h new file mode 100644 index 000000000..441ca07f6 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playermetadata.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERMETADATACONTROL_H +#define QT7PLAYERMETADATACONTROL_H + +#include + +QT_BEGIN_NAMESPACE + +class QT7PlayerSession; + +class QT7PlayerMetaDataControl : public QMetaDataReaderControl +{ + Q_OBJECT +public: + QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent); + virtual ~QT7PlayerMetaDataControl(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const ; + QStringList availableExtendedMetaData() const; + +private slots: + void updateTags(); + +private: + QT7PlayerSession *m_session; + QMap m_tags; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playermetadata.mm b/src/plugins/qt7/mediaplayer/qt7playermetadata.mm new file mode 100644 index 000000000..ec0d41cc2 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playermetadata.mm @@ -0,0 +1,260 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7backend.h" +#include "qt7playermetadata.h" +#include "qt7playersession.h" +#include + +#import + +#ifdef QUICKTIME_C_API_AVAILABLE + #include + #undef check // avoid name clash; +#endif + +QT_USE_NAMESPACE + +QT7PlayerMetaDataControl::QT7PlayerMetaDataControl(QT7PlayerSession *session, QObject *parent) + :QMetaDataReaderControl(parent), m_session(session) +{ +} + +QT7PlayerMetaDataControl::~QT7PlayerMetaDataControl() +{ +} + +bool QT7PlayerMetaDataControl::isMetaDataAvailable() const +{ + return !m_tags.isEmpty(); +} + +bool QT7PlayerMetaDataControl::isWritable() const +{ + return false; +} + +QVariant QT7PlayerMetaDataControl::metaData(QtMultimediaKit::MetaData key) const +{ + return m_tags.value(key); +} + +QList QT7PlayerMetaDataControl::availableMetaData() const +{ + return m_tags.keys(); +} + +QVariant QT7PlayerMetaDataControl::extendedMetaData(const QString &key) const +{ + Q_UNUSED(key); + return QVariant(); +} + +QStringList QT7PlayerMetaDataControl::availableExtendedMetaData() const +{ + return QStringList(); +} + +#ifdef QUICKTIME_C_API_AVAILABLE + +static QString stripCopyRightSymbol(const QString &key) +{ + return key.right(key.length()-1); +} + +static QString convertQuickTimeKeyToUserKey(const QString &key) +{ + if (key == QLatin1String("com.apple.quicktime.displayname")) + return QLatin1String("nam"); + else if (key == QLatin1String("com.apple.quicktime.album")) + return QLatin1String("alb"); + else if (key == QLatin1String("com.apple.quicktime.artist")) + return QLatin1String("ART"); + else + return QLatin1String("???"); +} + +static OSStatus readMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, QTPropertyClass propClass, + QTPropertyID id, QTPropertyValuePtr *value, ByteCount *size) +{ + QTPropertyValueType type; + ByteCount propSize; + UInt32 propFlags; + OSStatus err = QTMetaDataGetItemPropertyInfo(metaDataRef, item, propClass, id, &type, &propSize, &propFlags); + + if (err == noErr) { + *value = malloc(propSize); + if (*value != 0) { + err = QTMetaDataGetItemProperty(metaDataRef, item, propClass, id, propSize, *value, size); + + if (err == noErr && (type == 'code' || type == 'itsk' || type == 'itlk')) { + // convert from native endian to big endian + OSTypePtr pType = (OSTypePtr)*value; + *pType = EndianU32_NtoB(*pType); + } + } + else + return -1; + } + + return err; +} + +static UInt32 getMetaType(QTMetaDataRef metaDataRef, QTMetaDataItem item) +{ + QTPropertyValuePtr value = 0; + ByteCount ignore = 0; + OSStatus err = readMetaValue( + metaDataRef, item, kPropertyClass_MetaDataItem, kQTMetaDataItemPropertyID_DataType, &value, &ignore); + + if (err == noErr) { + UInt32 type = *((UInt32 *) value); + if (value) + free(value); + return type; + } + + return 0; +} + +static QString cFStringToQString(CFStringRef str) +{ + if(!str) + return QString(); + CFIndex length = CFStringGetLength(str); + const UniChar *chars = CFStringGetCharactersPtr(str); + if (chars) + return QString(reinterpret_cast(chars), length); + + QVarLengthArray buffer(length); + CFStringGetCharacters(str, CFRangeMake(0, length), buffer.data()); + return QString(reinterpret_cast(buffer.constData()), length); +} + + +static QString getMetaValue(QTMetaDataRef metaDataRef, QTMetaDataItem item, SInt32 id) +{ + QTPropertyValuePtr value = 0; + ByteCount size = 0; + OSStatus err = readMetaValue(metaDataRef, item, kPropertyClass_MetaDataItem, id, &value, &size); + QString string; + + if (err == noErr) { + UInt32 dataType = getMetaType(metaDataRef, item); + switch (dataType){ + case kQTMetaDataTypeUTF8: + case kQTMetaDataTypeMacEncodedText: + string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF8, false)); + break; + case kQTMetaDataTypeUTF16BE: + string = cFStringToQString(CFStringCreateWithBytes(0, (UInt8*)value, size, kCFStringEncodingUTF16BE, false)); + break; + default: + break; + } + + if (value) + free(value); + } + + return string; +} + + +static void readFormattedData(QTMetaDataRef metaDataRef, OSType format, QMultiMap &result) +{ + QTMetaDataItem item = kQTMetaDataItemUninitialized; + OSStatus err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + while (err == noErr){ + QString key = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Key); + if (format == kQTMetaDataStorageFormatQuickTime) + key = convertQuickTimeKeyToUserKey(key); + else + key = stripCopyRightSymbol(key); + + if (!result.contains(key)){ + QString val = getMetaValue(metaDataRef, item, kQTMetaDataItemPropertyID_Value); + result.insert(key, val); + } + err = QTMetaDataGetNextItem(metaDataRef, format, item, kQTMetaDataKeyFormatWildcard, 0, 0, &item); + } +} +#endif + + +void QT7PlayerMetaDataControl::updateTags() +{ + bool wasEmpty = m_tags.isEmpty(); + m_tags.clear(); + + QTMovie *movie = (QTMovie*)m_session->movie(); + + if (movie) { + QMultiMap metaMap; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTMetaDataRef metaDataRef; + OSStatus err = QTCopyMovieMetaData([movie quickTimeMovie], &metaDataRef); + if (err == noErr) { + readFormattedData(metaDataRef, kQTMetaDataStorageFormatUserData, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatQuickTime, metaMap); + readFormattedData(metaDataRef, kQTMetaDataStorageFormatiTunes, metaMap); + } +#else + AutoReleasePool pool; + NSString *name = [movie attributeForKey:@"QTMovieDisplayNameAttribute"]; + metaMap.insert(QLatin1String("nam"), QString::fromUtf8([name UTF8String])); +#endif // QUICKTIME_C_API_AVAILABLE + + m_tags.insert(QtMultimediaKit::AlbumArtist, metaMap.value(QLatin1String("ART"))); + m_tags.insert(QtMultimediaKit::AlbumTitle, metaMap.value(QLatin1String("alb"))); + m_tags.insert(QtMultimediaKit::Title, metaMap.value(QLatin1String("nam"))); + m_tags.insert(QtMultimediaKit::Date, metaMap.value(QLatin1String("day"))); + m_tags.insert(QtMultimediaKit::Genre, metaMap.value(QLatin1String("gnre"))); + m_tags.insert(QtMultimediaKit::TrackNumber, metaMap.value(QLatin1String("trk"))); + m_tags.insert(QtMultimediaKit::Description, metaMap.value(QLatin1String("des"))); + } + + if (!wasEmpty || !m_tags.isEmpty()) + emit metaDataChanged(); +} + +#include "moc_qt7playermetadata.cpp" diff --git a/src/plugins/qt7/mediaplayer/qt7playerservice.h b/src/plugins/qt7/mediaplayer/qt7playerservice.h new file mode 100644 index 000000000..65415f0a9 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playerservice.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERSERVICE_H +#define QT7PLAYERSERVICE_H + +#include +#include +#include + + +QT_BEGIN_NAMESPACE +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QT7PlayerControl; +class QT7PlayerMetaDataControl; +class QT7VideoWindowControl; +class QT7VideoWidgetControl; +class QT7VideoRendererControl; +class QT7VideoOutput; +class QT7PlayerSession; + +class QT7PlayerService : public QMediaService +{ +Q_OBJECT +public: + QT7PlayerService(QObject *parent = 0); + ~QT7PlayerService(); + + QMediaControl* requestControl(const char *name); + void releaseControl(QMediaControl *control); + +private: + QT7PlayerSession *m_session; + QT7PlayerControl *m_control; + QMediaControl * m_videoOutput; + QT7PlayerMetaDataControl *m_playerMetaDataControl; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playerservice.mm b/src/plugins/qt7/mediaplayer/qt7playerservice.mm new file mode 100644 index 000000000..39d06a4f3 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playerservice.mm @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "qt7backend.h" +#include "qt7playerservice.h" +#include "qt7playercontrol.h" +#include "qt7playersession.h" +#include "qt7videooutput.h" +#include "qt7movieviewoutput.h" +#include "qt7movieviewrenderer.h" +#include "qt7movierenderer.h" +#include "qt7movievideowidget.h" +#include "qt7playermetadata.h" + +#include +#include + +QT_USE_NAMESPACE + +QT7PlayerService::QT7PlayerService(QObject *parent): + QMediaService(parent), + m_videoOutput(0) +{ + m_session = new QT7PlayerSession(this); + + m_control = new QT7PlayerControl(this); + m_control->setSession(m_session); + + m_playerMetaDataControl = new QT7PlayerMetaDataControl(m_session, this); + connect(m_control, SIGNAL(mediaChanged(QMediaContent)), m_playerMetaDataControl, SLOT(updateTags())); +} + +QT7PlayerService::~QT7PlayerService() +{ +} + +QMediaControl *QT7PlayerService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) + return m_playerMetaDataControl; + +#ifndef QT_NO_OPENGL + if (!m_videoOutput) { + if (qstrcmp(name, QVideoWindowControl_iid) == 0) { +#if defined(QT_MAC_USE_COCOA) + m_videoOutput = new QT7MovieViewOutput(this); +#endif + } + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { +#ifdef QUICKTIME_C_API_AVAILABLE + m_videoOutput = new QT7MovieRenderer(this); +#else + m_videoOutput = new QT7MovieViewRenderer(this); +#endif + } + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { +#ifdef QUICKTIME_C_API_AVAILABLE + m_videoOutput = new QT7MovieVideoWidget(this); +#endif + } + + if (m_videoOutput) { + QT7VideoOutput *videoOutput = qobject_cast(m_videoOutput); + m_session->setVideoOutput(videoOutput); + return m_videoOutput; + } + } +#endif // !defined(QT_NO_OPENGL) + + return 0; +} + +void QT7PlayerService::releaseControl(QMediaControl *control) +{ + if (m_videoOutput == control) { + m_videoOutput = 0; + m_session->setVideoOutput(0); + delete control; + } +} + +#include "moc_qt7playerservice.cpp" diff --git a/src/plugins/qt7/mediaplayer/qt7playersession.h b/src/plugins/qt7/mediaplayer/qt7playersession.h new file mode 100644 index 000000000..0b18748a4 --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playersession.h @@ -0,0 +1,194 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7PLAYERSESSION_H +#define QT7PLAYERSESSION_H + +#include +#include +#include +#include + +#include +#include + +#include + + +QT_BEGIN_NAMESPACE + +class QT7PlayerControl; +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QT7VideoOutput; +class QT7PlayerSession; +class QT7PlayerService; + + +class QT7PlayerSession : public QObject +{ + Q_OBJECT +public: + QT7PlayerSession(QObject *parent = 0); + ~QT7PlayerSession(); + + void *movie() const; + + void setControl(QT7PlayerControl *control); + + void setVideoOutput(QT7VideoOutput *output); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &content, QIODevice *stream); + + qint64 position() const; + qint64 duration() const; + + int bufferStatus() const; + + int volume() const; + bool isMuted() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + QMediaTimeRange availablePlaybackRanges() const; + + qreal playbackRate() const; + +public slots: + void setPlaybackRate(qreal rate); + + void setPosition(qint64 pos); + + void play(); + void pause(); + void stop(); + + void setVolume(int volume); + void setMuted(bool muted); + + void processEOS(); + void processLoadStateChange(); + void processVolumeChange(); + void processNaturalSizeChange(); + void processPositionChange(); + +signals: + void positionChanged(qint64 position); + void durationChanged(qint64 duration); + void stateChanged(QMediaPlayer::State newState); + void mediaStatusChanged(QMediaPlayer::MediaStatus status); + void volumeChanged(int volume); + void mutedChanged(bool muted); + void audioAvailableChanged(bool audioAvailable); + void videoAvailableChanged(bool videoAvailable); + void error(int error, const QString &errorString); + +private: + class ResourceHandler { + public: + ResourceHandler():resource(0) {} + ~ResourceHandler() { clear(); } + void setResourceFile(const QString &file) { + if (resource) { + if (resource->fileName() == file) + return; + delete resource; + rawData.clear(); + } + resource = new QResource(file); + } + bool isValid() const { return resource && resource->isValid() && resource->data() != 0; } + const uchar *data() { + if (!isValid()) + return 0; + if (resource->isCompressed()) { + if (rawData.size() == 0) + rawData = qUncompress(resource->data(), resource->size()); + return (const uchar *)rawData.constData(); + } + return resource->data(); + } + qint64 size() { + if (data() == 0) + return 0; + return resource->isCompressed() ? rawData.size() : resource->size(); + } + void clear() { + delete resource; + rawData.clear(); + } + QResource *resource; + QByteArray rawData; + }; + + void openMovie(bool tryAsync); + + void *m_QTMovie; + void *m_movieObserver; + + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QIODevice *m_mediaStream; + QMediaContent m_resources; + ResourceHandler m_resourceHandler; + + QT7VideoOutput * m_videoOutput; + + bool m_muted; + bool m_tryingAsync; + int m_volume; + qreal m_rate; + + qint64 m_duration; + bool m_videoAvailable; + bool m_audioAvailable; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/mediaplayer/qt7playersession.mm b/src/plugins/qt7/mediaplayer/qt7playersession.mm new file mode 100644 index 000000000..c4eef5c9d --- /dev/null +++ b/src/plugins/qt7/mediaplayer/qt7playersession.mm @@ -0,0 +1,751 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import +#import + +#include "qt7backend.h" + +#include "qt7playersession.h" +#include "qt7playercontrol.h" +#include "qt7videooutput.h" + +#include +#include + +#include +#include + +#include +#include + +#include + +QT_USE_NAMESPACE + +//#define QT_DEBUG_QT7 + +@interface QTMovieObserver : NSObject +{ +@private + QT7PlayerSession *m_session; + QTMovie *m_movie; +} + +- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session; +- (void) setMovie:(QTMovie *)movie; +- (void) processEOS:(NSNotification *)notification; +- (void) processLoadStateChange:(NSNotification *)notification; +- (void) processVolumeChange:(NSNotification *)notification; +- (void) processNaturalSizeChange :(NSNotification *)notification; +- (void) processPositionChange :(NSNotification *)notification; +@end + +@implementation QTMovieObserver + +- (QTMovieObserver *) initWithPlayerSession:(QT7PlayerSession*)session +{ + if (!(self = [super init])) + return nil; + + self->m_session = session; + return self; +} + +- (void) setMovie:(QTMovie *)movie +{ + if (m_movie == movie) + return; + + if (m_movie) { + [[NSNotificationCenter defaultCenter] removeObserver:self]; + [m_movie release]; + } + + m_movie = movie; + + if (movie) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processEOS:) + name:QTMovieDidEndNotification + object:m_movie]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processLoadStateChange:) + name:QTMovieLoadStateDidChangeNotification + object:m_movie]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processVolumeChange:) + name:QTMovieVolumeDidChangeNotification + object:m_movie]; + + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processPositionChange:) + name:QTMovieTimeDidChangeNotification + object:m_movie]; + + if (QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processNaturalSizeChange:) + name:@"QTMovieNaturalSizeDidChangeNotification" + object:m_movie]; + + } + else { + [[NSNotificationCenter defaultCenter] addObserver:self + selector:@selector(processNaturalSizeChange:) + name:QTMovieEditedNotification + object:m_movie]; + } + + [movie retain]; + } +} + +- (void) processEOS:(NSNotification *)notification +{ + Q_UNUSED(notification); + QMetaObject::invokeMethod(m_session, "processEOS", Qt::AutoConnection); +} + +- (void) processLoadStateChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + QMetaObject::invokeMethod(m_session, "processLoadStateChange", Qt::AutoConnection); +} + +- (void) processVolumeChange:(NSNotification *)notification +{ + Q_UNUSED(notification); + QMetaObject::invokeMethod(m_session, "processVolumeChange", Qt::AutoConnection); +} + +- (void) processNaturalSizeChange :(NSNotification *)notification +{ + Q_UNUSED(notification); + QMetaObject::invokeMethod(m_session, "processNaturalSizeChange", Qt::AutoConnection); +} + +- (void) processPositionChange :(NSNotification *)notification +{ + Q_UNUSED(notification); + QMetaObject::invokeMethod(m_session, "processPositionChange", Qt::AutoConnection); +} + +@end + +static inline NSString *qString2CFStringRef(const QString &string) +{ + return [NSString stringWithCharacters:reinterpret_cast(string.unicode()) length:string.length()]; +} + +QT7PlayerSession::QT7PlayerSession(QObject *parent) + : QObject(parent) + , m_QTMovie(0) + , m_state(QMediaPlayer::StoppedState) + , m_mediaStatus(QMediaPlayer::NoMedia) + , m_mediaStream(0) + , m_videoOutput(0) + , m_muted(false) + , m_tryingAsync(false) + , m_volume(100) + , m_rate(1.0) + , m_duration(0) + , m_videoAvailable(false) + , m_audioAvailable(false) +{ + m_movieObserver = [[QTMovieObserver alloc] initWithPlayerSession:this]; +} + +QT7PlayerSession::~QT7PlayerSession() +{ + if (m_videoOutput) + m_videoOutput->setMovie(0); + + [(QTMovieObserver*)m_movieObserver setMovie:nil]; + [(QTMovieObserver*)m_movieObserver release]; + [(QTMovie*)m_QTMovie release]; +} + +void *QT7PlayerSession::movie() const +{ + return m_QTMovie; +} + +void QT7PlayerSession::setVideoOutput(QT7VideoOutput *output) +{ + if (m_videoOutput == output) + return; + + if (m_videoOutput) + m_videoOutput->setMovie(0); + + m_videoOutput = output; + + if (m_videoOutput && m_state != QMediaPlayer::StoppedState) + m_videoOutput->setMovie(m_QTMovie); +} + +qint64 QT7PlayerSession::position() const +{ + if (!m_QTMovie) + return 0; + + QTTime qtTime = [(QTMovie*)m_QTMovie currentTime]; + + return static_cast(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); +} + +qint64 QT7PlayerSession::duration() const +{ + if (!m_QTMovie) + return 0; + + QTTime qtTime = [(QTMovie*)m_QTMovie duration]; + + return static_cast(float(qtTime.timeValue) / float(qtTime.timeScale) * 1000.0f); +} + +QMediaPlayer::State QT7PlayerSession::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus QT7PlayerSession::mediaStatus() const +{ + return m_mediaStatus; +} + +int QT7PlayerSession::bufferStatus() const +{ + return 100; +} + +int QT7PlayerSession::volume() const +{ + return m_volume; +} + +bool QT7PlayerSession::isMuted() const +{ + return m_muted; +} + +bool QT7PlayerSession::isSeekable() const +{ + return true; +} + +#ifndef QUICKTIME_C_API_AVAILABLE +@interface QTMovie(QtExtensions) +- (NSArray*)loadedRanges; +- (QTTime)maxTimeLoaded; +@end +#endif + +QMediaTimeRange QT7PlayerSession::availablePlaybackRanges() const +{ + QTMovie *movie = (QTMovie*)m_QTMovie; +#ifndef QUICKTIME_C_API_AVAILABLE + AutoReleasePool pool; + if ([movie respondsToSelector:@selector(loadedRanges)]) { + QMediaTimeRange rc; + NSArray *r = [movie loadedRanges]; + for (NSValue *tr in r) { + QTTimeRange timeRange = [tr QTTimeRangeValue]; + qint64 startTime = qint64(float(timeRange.time.timeValue) / timeRange.time.timeScale * 1000.0); + rc.addInterval(startTime, startTime + qint64(float(timeRange.duration.timeValue) / timeRange.duration.timeScale * 1000.0)); + } + return rc; + } + else if ([movie respondsToSelector:@selector(maxTimeLoaded)]) { + QTTime loadedTime = [movie maxTimeLoaded]; + return QMediaTimeRange(0, qint64(float(loadedTime.timeValue) / loadedTime.timeScale * 1000.0)); + } +#else + TimeValue loadedTime; + TimeScale scale; + Movie m = [movie quickTimeMovie]; + if (GetMaxLoadedTimeInMovie(m, &loadedTime) == noErr) { + scale = GetMovieTimeScale(m); + return QMediaTimeRange(0, qint64(float(loadedTime) / scale * 1000.0f)); + } +#endif + return QMediaTimeRange(0, duration()); +} + +qreal QT7PlayerSession::playbackRate() const +{ + return m_rate; +} + +void QT7PlayerSession::setPlaybackRate(qreal rate) +{ + if (qFuzzyCompare(m_rate, rate)) + return; + + m_rate = rate; + + if (m_QTMovie != 0 && m_state == QMediaPlayer::PlayingState) { + AutoReleasePool pool; + float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; + [(QTMovie*)m_QTMovie setRate:preferredRate * m_rate]; + } +} + +void QT7PlayerSession::setPosition(qint64 pos) +{ + if ( !isSeekable() || pos == position()) + return; + + if (duration() > 0) + pos = qMin(pos, duration()); + + QTTime newQTTime = [(QTMovie*)m_QTMovie currentTime]; + newQTTime.timeValue = (pos / 1000.0f) * newQTTime.timeScale; + [(QTMovie*)m_QTMovie setCurrentTime:newQTTime]; + + //reset the EndOfMedia status position is changed after playback is finished + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + processLoadStateChange(); +} + +void QT7PlayerSession::play() +{ + if (m_state == QMediaPlayer::PlayingState) + return; + + m_state = QMediaPlayer::PlayingState; + + if (m_videoOutput) + m_videoOutput->setMovie(m_QTMovie); + + //reset the EndOfMedia status if the same file is played again + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + processLoadStateChange(); + + AutoReleasePool pool; + float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; + [(QTMovie*)m_QTMovie setRate:preferredRate * m_rate]; + + processLoadStateChange(); + emit stateChanged(m_state); +} + +void QT7PlayerSession::pause() +{ + if (m_state == QMediaPlayer::PausedState) + return; + + m_state = QMediaPlayer::PausedState; + + if (m_videoOutput) + m_videoOutput->setMovie(m_QTMovie); + + //reset the EndOfMedia status if the same file is played again + if (m_mediaStatus == QMediaPlayer::EndOfMedia) + processLoadStateChange(); + + [(QTMovie*)m_QTMovie setRate:0]; + + processLoadStateChange(); + emit stateChanged(m_state); +} + +void QT7PlayerSession::stop() +{ + if (m_state == QMediaPlayer::StoppedState) + return; + + m_state = QMediaPlayer::StoppedState; + + [(QTMovie*)m_QTMovie setRate:0]; + setPosition(0); + + if (m_videoOutput) + m_videoOutput->setMovie(0); + + processLoadStateChange(); + emit stateChanged(m_state); + emit positionChanged(position()); +} + +void QT7PlayerSession::setVolume(int volume) +{ + if (m_volume == volume) + return; + + m_volume = volume; + + if (m_QTMovie != 0) + [(QTMovie*)m_QTMovie setVolume:m_volume / 100.0f]; + + emit volumeChanged(m_volume); +} + +void QT7PlayerSession::setMuted(bool muted) +{ + if (m_muted == muted) + return; + + m_muted = muted; + + if (m_QTMovie != 0) + [(QTMovie*)m_QTMovie setMuted:m_muted]; + + emit mutedChanged(muted); +} + +QMediaContent QT7PlayerSession::media() const +{ + return m_resources; +} + +const QIODevice *QT7PlayerSession::mediaStream() const +{ + return m_mediaStream; +} + +void QT7PlayerSession::setMedia(const QMediaContent &content, QIODevice *stream) +{ + AutoReleasePool pool; + +#ifdef QT_DEBUG_QT7 + qDebug() << Q_FUNC_INFO << content.canonicalUrl(); +#endif + + if (m_QTMovie) { + [(QTMovieObserver*)m_movieObserver setMovie:nil]; + + if (m_videoOutput) + m_videoOutput->setMovie(0); + + [(QTMovie*)m_QTMovie release]; + m_QTMovie = 0; + m_resourceHandler.clear(); + } + + m_resources = content; + m_mediaStream = stream; + QMediaPlayer::MediaStatus oldMediaStatus = m_mediaStatus; + + if (content.isNull()) { + m_mediaStatus = QMediaPlayer::NoMedia; + if (m_state != QMediaPlayer::StoppedState) + emit stateChanged(m_state = QMediaPlayer::StoppedState); + + if (m_mediaStatus != oldMediaStatus) + emit mediaStatusChanged(m_mediaStatus); + emit positionChanged(position()); + return; + } + + m_mediaStatus = QMediaPlayer::LoadingMedia; + if (m_mediaStatus != oldMediaStatus) + emit mediaStatusChanged(m_mediaStatus); + + QNetworkRequest request = content.canonicalResource().request(); + + QVariant cookies = request.header(QNetworkRequest::CookieHeader); + if (cookies.isValid()) { + NSHTTPCookieStorage *store = [NSHTTPCookieStorage sharedHTTPCookieStorage]; + QList cookieList = cookies.value >(); + + foreach (const QNetworkCookie &requestCookie, cookieList) { + NSMutableDictionary *p = [NSMutableDictionary dictionaryWithObjectsAndKeys: + qString2CFStringRef(requestCookie.name()), NSHTTPCookieName, + qString2CFStringRef(requestCookie.value()), NSHTTPCookieValue, + qString2CFStringRef(requestCookie.domain()), NSHTTPCookieDomain, + qString2CFStringRef(requestCookie.path()), NSHTTPCookiePath, + nil + ]; + if (requestCookie.isSessionCookie()) + [p setObject:[NSString stringWithUTF8String:"TRUE"] forKey:NSHTTPCookieDiscard]; + else + [p setObject:[NSDate dateWithTimeIntervalSince1970:requestCookie.expirationDate().toTime_t()] forKey:NSHTTPCookieExpires]; + + [store setCookie:[NSHTTPCookie cookieWithProperties:p]]; + } + } + + // Attempt multiple times to open the movie. + // First try - attempt open in async mode + openMovie(true); + + emit positionChanged(position()); +} + +void QT7PlayerSession::openMovie(bool tryAsync) +{ + QUrl requestUrl = m_resources.canonicalResource().request().url(); + if (requestUrl.scheme().isEmpty()) + requestUrl.setScheme(QLatin1String("file")); + +#ifdef QT_DEBUG_QT7 + qDebug() << Q_FUNC_INFO << requestUrl; +#endif + + NSError *err = 0; + NSString *urlString = [NSString stringWithUTF8String:requestUrl.toEncoded().constData()]; + + NSMutableDictionary *attr = [NSMutableDictionary dictionaryWithObjectsAndKeys: + [NSNumber numberWithBool:YES], QTMovieOpenAsyncOKAttribute, + [NSNumber numberWithBool:YES], QTMovieIsActiveAttribute, + [NSNumber numberWithBool:YES], QTMovieResolveDataRefsAttribute, + [NSNumber numberWithBool:YES], QTMovieDontInteractWithUserAttribute, + nil]; + + + if (requestUrl.scheme() == QLatin1String("qrc")) { + // Load from Qt resource + m_resourceHandler.setResourceFile(QLatin1Char(':') + requestUrl.path()); + if (!m_resourceHandler.isValid()) { + emit error(QMediaPlayer::FormatError, tr("Attempting to play invalid Qt resource")); + return; + } + + CFDataRef resourceData = + CFDataCreateWithBytesNoCopy(0, m_resourceHandler.data(), m_resourceHandler.size(), kCFAllocatorNull); + + QTDataReference *dataReference = + [QTDataReference dataReferenceWithReferenceToData:(NSData*)resourceData + name:qString2CFStringRef(requestUrl.path()) + MIMEType:nil]; + + [attr setObject:dataReference forKey:QTMovieDataReferenceAttribute]; + + CFRelease(resourceData); + } else { + [attr setObject:[NSURL URLWithString:urlString] forKey:QTMovieURLAttribute]; + } + + if (tryAsync && QSysInfo::MacintoshVersion >= QSysInfo::MV_10_6) { + [attr setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenAsyncRequiredAttribute"]; +// XXX: This is disabled for now. causes some problems with video playback for some formats +// [attr setObject:[NSNumber numberWithBool:YES] forKey:@"QTMovieOpenForPlaybackAttribute"]; + m_tryingAsync = true; + } + else + m_tryingAsync = false; + + m_QTMovie = [QTMovie movieWithAttributes:attr error:&err]; + if (err != nil) { + // First attempt to test for inability to perform async +// if ([err code] == QTErrorMovieOpeningCannotBeAsynchronous) { XXX: error code unknown! + if (m_tryingAsync) { + m_tryingAsync = false; + err = nil; + [attr removeObjectForKey:@"QTMovieOpenAsyncRequiredAttribute"]; + m_QTMovie = [QTMovie movieWithAttributes:attr error:&err]; + } + } + + if (err != nil) { + m_QTMovie = 0; + QString description = QString::fromUtf8([[err localizedDescription] UTF8String]); + emit error(QMediaPlayer::FormatError, description); + +#ifdef QT_DEBUG_QT7 + qDebug() << Q_FUNC_INFO << description; +#endif + } + else { + [(QTMovie*)m_QTMovie retain]; + + [(QTMovieObserver*)m_movieObserver setMovie:(QTMovie*)m_QTMovie]; + + if (m_state != QMediaPlayer::StoppedState && m_videoOutput) + m_videoOutput->setMovie(m_QTMovie); + + processLoadStateChange(); + + [(QTMovie*)m_QTMovie setMuted:m_muted]; + [(QTMovie*)m_QTMovie setVolume:m_volume / 100.0f]; + } +} + +bool QT7PlayerSession::isAudioAvailable() const +{ + if (!m_QTMovie) + return false; + + AutoReleasePool pool; + return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasAudioAttribute"] boolValue] == YES; +} + +bool QT7PlayerSession::isVideoAvailable() const +{ + if (!m_QTMovie) + return false; + + AutoReleasePool pool; + return [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieHasVideoAttribute"] boolValue] == YES; +} + +void QT7PlayerSession::processEOS() +{ +#ifdef QT_DEBUG_QT7 + qDebug() << Q_FUNC_INFO; +#endif + emit positionChanged(position()); + m_mediaStatus = QMediaPlayer::EndOfMedia; + if (m_videoOutput) + m_videoOutput->setMovie(0); + emit stateChanged(m_state = QMediaPlayer::StoppedState); + emit mediaStatusChanged(m_mediaStatus); +} + +void QT7PlayerSession::processLoadStateChange() +{ + if (!m_QTMovie) + return; + + AutoReleasePool pool; + + long state = [[(QTMovie*)m_QTMovie attributeForKey:QTMovieLoadStateAttribute] longValue]; + +#ifdef QT_DEBUG_QT7 + qDebug() << Q_FUNC_INFO << state; +#endif + +#ifndef QUICKTIME_C_API_AVAILABLE + enum { + kMovieLoadStateError = -1L, + kMovieLoadStateLoading = 1000, + kMovieLoadStateLoaded = 2000, + kMovieLoadStatePlayable = 10000, + kMovieLoadStatePlaythroughOK = 20000, + kMovieLoadStateComplete = 100000 + }; +#endif + + if (state == kMovieLoadStateError) { + if (m_tryingAsync) { + NSError *error = [(QTMovie*)m_QTMovie attributeForKey:@"QTMovieLoadStateErrorAttribute"]; + if ([error code] == componentNotThreadSafeErr) { + // Last Async check, try again with no such flag + openMovie(false); + } + } + else { + if (m_videoOutput) + m_videoOutput->setMovie(0); + + emit error(QMediaPlayer::FormatError, tr("Failed to load media")); + emit mediaStatusChanged(m_mediaStatus = QMediaPlayer::InvalidMedia); + emit stateChanged(m_state = QMediaPlayer::StoppedState); + } + + return; + } + + QMediaPlayer::MediaStatus newStatus = QMediaPlayer::NoMedia; + bool isPlaying = (m_state != QMediaPlayer::StoppedState); + + if (state >= kMovieLoadStatePlaythroughOK) { + newStatus = isPlaying ? QMediaPlayer::BufferedMedia : QMediaPlayer::LoadedMedia; + } else if (state >= kMovieLoadStatePlayable) + newStatus = isPlaying ? QMediaPlayer::BufferingMedia : QMediaPlayer::LoadingMedia; + else if (state >= kMovieLoadStateLoading) { + if (!isPlaying) + newStatus = QMediaPlayer::LoadingMedia; + else if (m_mediaStatus >= QMediaPlayer::LoadedMedia) + newStatus = QMediaPlayer::StalledMedia; + else + newStatus = QMediaPlayer::LoadingMedia; + } + + if (state >= kMovieLoadStatePlayable && + m_state == QMediaPlayer::PlayingState && + [(QTMovie*)m_QTMovie rate] == 0) { + + float preferredRate = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMoviePreferredRateAttribute"] floatValue]; + + [(QTMovie*)m_QTMovie setRate:preferredRate * m_rate]; + } + + if (state >= kMovieLoadStateLoaded) { + qint64 currentDuration = duration(); + if (m_duration != currentDuration) + emit durationChanged(m_duration = currentDuration); + + if (m_audioAvailable != isAudioAvailable()) + emit audioAvailableChanged(m_audioAvailable = !m_audioAvailable); + + if (m_videoAvailable != isVideoAvailable()) + emit videoAvailableChanged(m_videoAvailable = !m_videoAvailable); + } + + if (newStatus != m_mediaStatus) + emit mediaStatusChanged(m_mediaStatus = newStatus); +} + +void QT7PlayerSession::processVolumeChange() +{ + if (!m_QTMovie) + return; + + int newVolume = qRound(100.0f * [((QTMovie*)m_QTMovie) volume]); + + if (newVolume != m_volume) { + emit volumeChanged(m_volume = newVolume); + } +} + +void QT7PlayerSession::processNaturalSizeChange() +{ + AutoReleasePool pool; + NSSize size = [[(QTMovie*)m_QTMovie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; +#ifdef QT_DEBUG_QT7 + qDebug() << Q_FUNC_INFO << QSize(size.width, size.height); +#endif + + if (m_videoOutput) + m_videoOutput->updateNaturalSize(QSize(size.width, size.height)); +} + +void QT7PlayerSession::processPositionChange() +{ + emit positionChanged(position()); +} + +#include "moc_qt7playersession.cpp" diff --git a/src/plugins/qt7/qcvdisplaylink.h b/src/plugins/qt7/qcvdisplaylink.h new file mode 100644 index 000000000..536abbf58 --- /dev/null +++ b/src/plugins/qt7/qcvdisplaylink.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QCVDISPLAYLINK_H +#define QCVDISPLAYLINK_H + +#include +#include + +#include + +#include + +QT_BEGIN_NAMESPACE + +class QCvDisplayLink : public QObject +{ +Q_OBJECT +public: + QCvDisplayLink(QObject *parent = 0); + virtual ~QCvDisplayLink(); + + bool isValid(); + bool isActive() const; + +public slots: + void start(); + void stop(); + +signals: + void tick(const CVTimeStamp &ts); + +public: + void displayLinkEvent(const CVTimeStamp *); + +protected: + virtual bool event(QEvent *); + +private: + CVDisplayLinkRef m_displayLink; + QMutex m_displayLinkMutex; + bool m_pendingDisplayLinkEvent; + bool m_isActive; + CVTimeStamp m_frameTimeStamp; +}; + +QT_END_NAMESPACE + +#endif + diff --git a/src/plugins/qt7/qcvdisplaylink.mm b/src/plugins/qt7/qcvdisplaylink.mm new file mode 100644 index 000000000..ecc4235bd --- /dev/null +++ b/src/plugins/qt7/qcvdisplaylink.mm @@ -0,0 +1,156 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qcvdisplaylink.h" + +#include +#include + +QT_USE_NAMESPACE + +static CVReturn CVDisplayLinkCallback(CVDisplayLinkRef displayLink, + const CVTimeStamp *inNow, + const CVTimeStamp *inOutputTime, + CVOptionFlags flagsIn, + CVOptionFlags *flagsOut, + void *displayLinkContext) +{ + Q_UNUSED(displayLink); + Q_UNUSED(inNow); + Q_UNUSED(flagsIn); + Q_UNUSED(flagsOut); + + QCvDisplayLink *link = (QCvDisplayLink *)displayLinkContext; + + link->displayLinkEvent(inOutputTime); + return kCVReturnSuccess; +} + + +QCvDisplayLink::QCvDisplayLink(QObject *parent) + :QObject(parent), + m_pendingDisplayLinkEvent(false), + m_isActive(false) +{ + // create display link for the main display + CVDisplayLinkCreateWithCGDisplay(kCGDirectMainDisplay, &m_displayLink); + if (m_displayLink) { + // set the current display of a display link. + CVDisplayLinkSetCurrentCGDisplay(m_displayLink, kCGDirectMainDisplay); + + // set the renderer output callback function + CVDisplayLinkSetOutputCallback(m_displayLink, &CVDisplayLinkCallback, this); + } +} + +QCvDisplayLink::~QCvDisplayLink() +{ + if (m_displayLink) { + CVDisplayLinkStop(m_displayLink); + CVDisplayLinkRelease(m_displayLink); + m_displayLink = NULL; + } +} + +bool QCvDisplayLink::isValid() +{ + return m_displayLink != 0; +} + +bool QCvDisplayLink::isActive() const +{ + return m_isActive; +} + +void QCvDisplayLink::start() +{ + if (m_displayLink && !m_isActive) { + CVDisplayLinkStart(m_displayLink); + m_isActive = true; + } +} + +void QCvDisplayLink::stop() +{ + if (m_displayLink && m_isActive) { + CVDisplayLinkStop(m_displayLink); + m_isActive = false; + } +} + +void QCvDisplayLink::displayLinkEvent(const CVTimeStamp *ts) +{ + // This function is called from a + // thread != gui thread. So we post the event. + // But we need to make sure that we don't post faster + // than the event loop can eat: + m_displayLinkMutex.lock(); + bool pending = m_pendingDisplayLinkEvent; + m_pendingDisplayLinkEvent = true; + m_frameTimeStamp = *ts; + m_displayLinkMutex.unlock(); + + if (!pending) + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); +} + +bool QCvDisplayLink::event(QEvent *event) +{ + switch (event->type()){ + case QEvent::User: { + m_displayLinkMutex.lock(); + m_pendingDisplayLinkEvent = false; + CVTimeStamp ts = m_frameTimeStamp; + m_displayLinkMutex.unlock(); + + emit tick(ts); + + return false; + } + break; + default: + break; + } + return QObject::event(event); +} + +#include "moc_qcvdisplaylink.cpp" + diff --git a/src/plugins/qt7/qt7.pro b/src/plugins/qt7/qt7.pro new file mode 100644 index 000000000..db3728c33 --- /dev/null +++ b/src/plugins/qt7/qt7.pro @@ -0,0 +1,58 @@ +load(qt_module) + +TARGET = qqt7engine +QT += multimediakit-private network +PLUGIN_TYPE = mediaservice + +load(qt_plugin) +DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE} + +!simulator { +QT += opengl +} + +#DEFINES += QT_DEBUG_QT7 + +LIBS += -framework AppKit -framework AudioUnit \ + -framework AudioToolbox -framework CoreAudio \ + -framework QuartzCore -framework QTKit + +# The Quicktime framework is only awailable for 32-bit builds, so we +# need to check for this before linking against it. +# QMAKE_MAC_XARCH is not awailable on Tiger, but at the same time, +# we never build for 64-bit architechtures on Tiger either: +contains(QMAKE_MAC_XARCH, no) { + LIBS += -framework QuickTime +} else { + LIBS += -Xarch_i386 -framework QuickTime -Xarch_ppc -framework QuickTime +} + +HEADERS += \ + qt7backend.h \ + qt7videooutput.h \ + qt7serviceplugin.h + +OBJECTIVE_SOURCES += \ + qt7backend.mm \ + qt7serviceplugin.mm + +!simulator { + HEADERS += \ + qt7movieviewoutput.h \ + qt7movievideowidget.h \ + qt7movieviewrenderer.h \ + qt7movierenderer.h \ + qt7ciimagevideobuffer.h \ + qcvdisplaylink.h + + OBJECTIVE_SOURCES += \ + qt7movieviewoutput.mm \ + qt7movievideowidget.mm \ + qt7movieviewrenderer.mm \ + qt7movierenderer.mm \ + qt7videooutput.mm \ + qt7ciimagevideobuffer.mm \ + qcvdisplaylink.mm +} + +include(mediaplayer/mediaplayer.pri) diff --git a/src/plugins/qt7/qt7backend.h b/src/plugins/qt7/qt7backend.h new file mode 100644 index 000000000..d52f0396a --- /dev/null +++ b/src/plugins/qt7/qt7backend.h @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7BACKEND_H +#define QT7BACKEND_H + +#include "qmobilityglobal.h" + +#include + +#ifndef Q_WS_SIMULATOR +#ifndef Q_WS_MAC64 +#define QUICKTIME_C_API_AVAILABLE +#endif +#endif // !defined(Q_WS_SIMULATOR) + +QT_BEGIN_NAMESPACE + +class AutoReleasePool +{ +private: + void *pool; +public: + AutoReleasePool(); + ~AutoReleasePool(); +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7backend.mm b/src/plugins/qt7/qt7backend.mm new file mode 100644 index 000000000..83a044dab --- /dev/null +++ b/src/plugins/qt7/qt7backend.mm @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7backend.h" + +#import +#include + + +QT_BEGIN_NAMESPACE + +AutoReleasePool::AutoReleasePool() +{ + pool = (void*)[[NSAutoreleasePool alloc] init]; +} + +AutoReleasePool::~AutoReleasePool() +{ + [(NSAutoreleasePool*)pool release]; +} + +QT_END_NAMESPACE diff --git a/src/plugins/qt7/qt7ciimagevideobuffer.h b/src/plugins/qt7/qt7ciimagevideobuffer.h new file mode 100644 index 000000000..d6ae19f80 --- /dev/null +++ b/src/plugins/qt7/qt7ciimagevideobuffer.h @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7CIIMAGEVIDEOBUFFER_H +#define QT7CIIMAGEVIDEOBUFFER_H + +#include "qt7backend.h" +#import + +#include +#include + + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +QT_BEGIN_NAMESPACE + +class QT7CIImageVideoBuffer : public QAbstractVideoBuffer +{ +public: + QT7CIImageVideoBuffer(CIImage *image); + + virtual ~QT7CIImageVideoBuffer(); + + MapMode mapMode() const; + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine); + void unmap(); + QVariant handle() const; + +private: + CIImage *m_image; + NSBitmapImageRep *m_buffer; + MapMode m_mode; +}; + + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7ciimagevideobuffer.mm b/src/plugins/qt7/qt7ciimagevideobuffer.mm new file mode 100644 index 000000000..e7758c0a6 --- /dev/null +++ b/src/plugins/qt7/qt7ciimagevideobuffer.mm @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7ciimagevideobuffer.h" + +#include +#include + +QT7CIImageVideoBuffer::QT7CIImageVideoBuffer(CIImage *image) + : QAbstractVideoBuffer(CoreImageHandle) + , m_image(image) + , m_buffer(0) + , m_mode(NotMapped) +{ + [m_image retain]; +} + +QT7CIImageVideoBuffer::~QT7CIImageVideoBuffer() +{ + [m_image release]; + [m_buffer release]; +} + +QAbstractVideoBuffer::MapMode QT7CIImageVideoBuffer::mapMode() const +{ + return m_mode; +} + +uchar *QT7CIImageVideoBuffer::map(QAbstractVideoBuffer::MapMode mode, int *numBytes, int *bytesPerLine) +{ + if (mode == NotMapped || m_mode != NotMapped || !m_image) + return 0; + + if (!m_buffer) { + //swap R and B channels + CIFilter *colorSwapFilter = [CIFilter filterWithName: @"CIColorMatrix" keysAndValues: + @"inputImage", m_image, + @"inputRVector", [CIVector vectorWithX: 0 Y: 0 Z: 1 W: 0], + @"inputGVector", [CIVector vectorWithX: 0 Y: 1 Z: 0 W: 0], + @"inputBVector", [CIVector vectorWithX: 1 Y: 0 Z: 0 W: 0], + @"inputAVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 1], + @"inputBiasVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 0], + nil]; + CIImage *img = [colorSwapFilter valueForKey: @"outputImage"]; + + m_buffer = [[NSBitmapImageRep alloc] initWithCIImage:img]; + } + + if (numBytes) + *numBytes = [m_buffer bytesPerPlane]; + + if (bytesPerLine) + *bytesPerLine = [m_buffer bytesPerRow]; + + m_mode = mode; + + return [m_buffer bitmapData]; +} + +void QT7CIImageVideoBuffer::unmap() +{ + m_mode = NotMapped; +} + +QVariant QT7CIImageVideoBuffer::handle() const +{ + return QVariant::fromValue(m_image); +} + diff --git a/src/plugins/qt7/qt7movierenderer.h b/src/plugins/qt7/qt7movierenderer.h new file mode 100644 index 000000000..42a2732da --- /dev/null +++ b/src/plugins/qt7/qt7movierenderer.h @@ -0,0 +1,107 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIERENDERER_H +#define QT7MOVIERENDERER_H + +#include "qt7backend.h" + +#include +#include + +#include +#include + +#include +#include "qt7videooutput.h" + +#include +#include + +QT_BEGIN_NAMESPACE + +class QGLContext; + +class QCvDisplayLink; +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieRenderer : public QT7VideoRendererControl +{ +Q_OBJECT +public: + QT7MovieRenderer(QObject *parent = 0); + virtual ~QT7MovieRenderer(); + + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + QSize nativeSize() const; + +private slots: + void updateVideoFrame(const CVTimeStamp &ts); + +private: + void setupVideoOutput(); + bool createPixelBufferVisualContext(); + bool createGLVisualContext(); + + void *m_movie; + + QMutex m_mutex; + + QCvDisplayLink *m_displayLink; +#ifdef QUICKTIME_C_API_AVAILABLE + QTVisualContextRef m_visualContext; + bool m_usingGLContext; + const QGLContext *m_currentGLContext; + QSize m_pixelBufferContextGeometry; +#endif + QAbstractVideoSurface *m_surface; + QSize m_nativeSize; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7movierenderer.mm b/src/plugins/qt7/qt7movierenderer.mm new file mode 100644 index 000000000..6818f02d1 --- /dev/null +++ b/src/plugins/qt7/qt7movierenderer.mm @@ -0,0 +1,479 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movierenderer.h" +#include "qt7playersession.h" +#include "qt7ciimagevideobuffer.h" +#include "qcvdisplaylink.h" +#include +#include + +#include + +#include +#include +#include + +QT_USE_NAMESPACE + +//#define USE_MAIN_MONITOR_COLOR_SPACE 1 + +class CVGLTextureVideoBuffer : public QAbstractVideoBuffer +{ +public: + CVGLTextureVideoBuffer(CVOpenGLTextureRef buffer) + : QAbstractVideoBuffer(GLTextureHandle) + , m_buffer(buffer) + , m_mode(NotMapped) + { + CVOpenGLTextureRetain(m_buffer); + } + + virtual ~CVGLTextureVideoBuffer() + { + CVOpenGLTextureRelease(m_buffer); + } + + QVariant handle() const + { + GLuint id = CVOpenGLTextureGetName(m_buffer); + return QVariant(int(id)); + } + + MapMode mapMode() const { return m_mode; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (numBytes) + *numBytes = 0; + + if (bytesPerLine) + *bytesPerLine = 0; + + m_mode = mode; + return 0; + } + + void unmap() { m_mode = NotMapped; } + +private: + CVOpenGLTextureRef m_buffer; + MapMode m_mode; +}; + + +class CVPixelBufferVideoBuffer : public QAbstractVideoBuffer +{ +public: + CVPixelBufferVideoBuffer(CVPixelBufferRef buffer) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_mode(NotMapped) + { + CVPixelBufferRetain(m_buffer); + } + + virtual ~CVPixelBufferVideoBuffer() + { + CVPixelBufferRelease(m_buffer); + } + + MapMode mapMode() const { return m_mode; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (mode != NotMapped && m_mode == NotMapped) { + CVPixelBufferLockBaseAddress(m_buffer, 0); + + if (numBytes) + *numBytes = CVPixelBufferGetDataSize(m_buffer); + + if (bytesPerLine) + *bytesPerLine = CVPixelBufferGetBytesPerRow(m_buffer); + + m_mode = mode; + + return (uchar*)CVPixelBufferGetBaseAddress(m_buffer); + } else { + return 0; + } + } + + void unmap() + { + if (m_mode != NotMapped) { + m_mode = NotMapped; + CVPixelBufferUnlockBaseAddress(m_buffer, 0); + } + } + +private: + CVPixelBufferRef m_buffer; + MapMode m_mode; +}; + + + +QT7MovieRenderer::QT7MovieRenderer(QObject *parent) + :QT7VideoRendererControl(parent), + m_movie(0), +#ifdef QUICKTIME_C_API_AVAILABLE + m_visualContext(0), + m_usingGLContext(false), + m_currentGLContext(0), +#endif + m_surface(0) +{ +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieRenderer"; +#endif + m_displayLink = new QCvDisplayLink(this); + connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); +} + + +bool QT7MovieRenderer::createGLVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + AutoReleasePool pool; + CGLContextObj cglContext = CGLGetCurrentContext(); + NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; + CGLPixelFormatObj cglPixelFormat = static_cast([nsglPixelFormat CGLPixelFormatObj]); + + OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, cglContext, + cglPixelFormat, NULL, &m_visualContext); + if (err != noErr) + qWarning() << "Could not create visual context (OpenGL)"; + + return (err == noErr); +#endif // QUICKTIME_C_API_AVAILABLE + + return false; +} + +#ifdef QUICKTIME_C_API_AVAILABLE +static bool DictionarySetValue(CFMutableDictionaryRef dict, CFStringRef key, SInt32 value) +{ + CFNumberRef number = CFNumberCreate(kCFAllocatorDefault, kCFNumberSInt32Type, &value); + + if (number) { + CFDictionarySetValue( dict, key, number ); + CFRelease( number ); + return true; + } + return false; +} +#endif // QUICKTIME_C_API_AVAILABLE + +bool QT7MovieRenderer::createPixelBufferVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_visualContext) { + QTVisualContextRelease(m_visualContext); + m_visualContext = 0; + } + + m_pixelBufferContextGeometry = m_nativeSize; + + CFMutableDictionaryRef pixelBufferOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + //DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32ARGBPixelFormat ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferPixelFormatTypeKey, k32BGRAPixelFormat ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferWidthKey, m_nativeSize.width() ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferHeightKey, m_nativeSize.height() ); + DictionarySetValue(pixelBufferOptions, kCVPixelBufferBytesPerRowAlignmentKey, 16); + //CFDictionarySetValue(pixelBufferOptions, kCVPixelBufferOpenGLCompatibilityKey, kCFBooleanTrue); + + CFMutableDictionaryRef visualContextOptions = CFDictionaryCreateMutable(kCFAllocatorDefault, 0, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + CFDictionarySetValue(visualContextOptions, kQTVisualContextPixelBufferAttributesKey, pixelBufferOptions); + + CGColorSpaceRef colorSpace = NULL; + +#if USE_MAIN_MONITOR_COLOR_SPACE + CMProfileRef sysprof = NULL; + + // Get the Systems Profile for the main display + if (CMGetSystemProfile(&sysprof) == noErr) { + // Create a colorspace with the systems profile + colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof); + CMCloseProfile(sysprof); + } +#endif + + if (!colorSpace) + colorSpace = CGColorSpaceCreateDeviceRGB(); + + CFDictionarySetValue(visualContextOptions, kQTVisualContextOutputColorSpaceKey, colorSpace); + + OSStatus err = QTPixelBufferContextCreate(kCFAllocatorDefault, + visualContextOptions, + &m_visualContext); + CFRelease(pixelBufferOptions); + CFRelease(visualContextOptions); + + if (err != noErr) { + qWarning() << "Could not create visual context (PixelBuffer)"; + return false; + } + + return true; +#endif // QUICKTIME_C_API_AVAILABLE + + return false; +} + + +QT7MovieRenderer::~QT7MovieRenderer() +{ + m_displayLink->stop(); +} + +void QT7MovieRenderer::setupVideoOutput() +{ + AutoReleasePool pool; + +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieRenderer::setupVideoOutput" << m_movie; +#endif + + if (m_movie == 0 || m_surface == 0) { + m_displayLink->stop(); + return; + } + + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + m_nativeSize = QSize(size.width, size.height); + +#ifdef QUICKTIME_C_API_AVAILABLE + bool usedGLContext = m_usingGLContext; + + if (!m_nativeSize.isEmpty()) { + + bool glSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty(); + + //Try rendering using opengl textures first: + if (glSupported) { + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, QAbstractVideoBuffer::GLTextureHandle); + + if (m_surface->isActive()) + m_surface->stop(); + + if (!m_surface->start(format)) { + qWarning() << "failed to start video surface" << m_surface->error(); + qWarning() << "Surface format:" << format; + glSupported = false; + } else { + m_usingGLContext = true; + } + + } + + if (!glSupported) { + m_usingGLContext = false; + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32); + + if (m_surface->isActive() && m_surface->surfaceFormat() != format) { +#ifdef QT_DEBUG_QT7 + qDebug() << "Surface format was changed, stop the surface."; +#endif + m_surface->stop(); + } + + if (!m_surface->isActive()) { +#ifdef QT_DEBUG_QT7 + qDebug() << "Starting the surface with format" << format; +#endif + if (!m_surface->start(format)) { + qWarning() << "failed to start video surface" << m_surface->error(); + qWarning() << "Surface format:" << format; + } + } + } + } + + + if (m_visualContext) { + //check if the visual context still can be reused + if (usedGLContext != m_usingGLContext || + (m_usingGLContext && (m_currentGLContext != QGLContext::currentContext())) || + (!m_usingGLContext && (m_pixelBufferContextGeometry != m_nativeSize))) { + QTVisualContextRelease(m_visualContext); + m_pixelBufferContextGeometry = QSize(); + m_visualContext = 0; + } + } + + if (!m_nativeSize.isEmpty()) { + if (!m_visualContext) { + if (m_usingGLContext) { +#ifdef QT_DEBUG_QT7 + qDebug() << "Building OpenGL visual context" << m_nativeSize; +#endif + m_currentGLContext = QGLContext::currentContext(); + if (!createGLVisualContext()) { + qWarning() << "QT7MovieRenderer: failed to create visual context"; + return; + } + } else { +#ifdef QT_DEBUG_QT7 + qDebug() << "Building Pixel Buffer visual context" << m_nativeSize; +#endif + if (!createPixelBufferVisualContext()) { + qWarning() << "QT7MovieRenderer: failed to create visual context"; + return; + } + } + } + + // targets a Movie to render into a visual context + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext); + + m_displayLink->start(); + } +#endif + +} + +void QT7MovieRenderer::setMovie(void *movie) +{ +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieRenderer::setMovie" << movie; +#endif + +#ifdef QUICKTIME_C_API_AVAILABLE + QMutexLocker locker(&m_mutex); + + if (m_movie != movie) { + if (m_movie) { + //ensure the old movie doesn't hold the visual context, otherwise it can't be reused + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil); + [(QTMovie*)m_movie release]; + } + + m_movie = movie; + [(QTMovie*)m_movie retain]; + + setupVideoOutput(); + } +#endif +} + +void QT7MovieRenderer::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + setupVideoOutput(); + } +} + +QAbstractVideoSurface *QT7MovieRenderer::surface() const +{ + return m_surface; +} + +void QT7MovieRenderer::setSurface(QAbstractVideoSurface *surface) +{ +#ifdef QT_DEBUG_QT7 + qDebug() << "Set video surface" << surface; +#endif + + if (surface == m_surface) + return; + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + setupVideoOutput(); +} + + +QSize QT7MovieRenderer::nativeSize() const +{ + return m_nativeSize; +} + +void QT7MovieRenderer::updateVideoFrame(const CVTimeStamp &ts) +{ +#ifdef QUICKTIME_C_API_AVAILABLE + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive() && + m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) { + + CVImageBufferRef imageBuffer = NULL; + + OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, &imageBuffer); + + if (status == noErr && imageBuffer) { + QAbstractVideoBuffer *buffer = 0; + + if (m_usingGLContext) { + buffer = new QT7CIImageVideoBuffer([CIImage imageWithCVImageBuffer:imageBuffer]); + CVOpenGLTextureRelease((CVOpenGLTextureRef)imageBuffer); + } else { + buffer = new CVPixelBufferVideoBuffer((CVPixelBufferRef)imageBuffer); + //buffer = new QT7CIImageVideoBuffer( [CIImage imageWithCVImageBuffer:imageBuffer] ); + CVPixelBufferRelease((CVPixelBufferRef)imageBuffer); + } + + QVideoFrame frame(buffer, m_nativeSize, QVideoFrame::Format_RGB32); + m_surface->present(frame); + QTVisualContextTask(m_visualContext); + } + } +#else + Q_UNUSED(ts); +#endif +} + +#include "moc_qt7movierenderer.cpp" diff --git a/src/plugins/qt7/qt7movievideowidget.h b/src/plugins/qt7/qt7movievideowidget.h new file mode 100644 index 000000000..d69ead655 --- /dev/null +++ b/src/plugins/qt7/qt7movievideowidget.h @@ -0,0 +1,126 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIEVIDEOWIDGET_H +#define QT7MOVIEVIDEOWIDGET_H + +#include +#include + +#include +#include + +#include +#include "qt7videooutput.h" + +#include +#include + +class GLVideoWidget; + +QT_BEGIN_NAMESPACE + +class QCvDisplayLink; +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieVideoWidget : public QT7VideoWidgetControl +{ +Q_OBJECT +public: + QT7MovieVideoWidget(QObject *parent = 0); + virtual ~QT7MovieVideoWidget(); + + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + QWidget *videoWidget(); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + +private slots: + void updateVideoFrame(const CVTimeStamp &ts); + +private: + void setupVideoOutput(); + bool createVisualContext(); + + void updateColors(); + + void *m_movie; + GLVideoWidget *m_videoWidget; + + QCvDisplayLink *m_displayLink; + +#ifdef QUICKTIME_C_API_AVAILABLE + QTVisualContextRef m_visualContext; +#endif + + bool m_fullscreen; + QSize m_nativeSize; + Qt::AspectRatioMode m_aspectRatioMode; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7movievideowidget.mm b/src/plugins/qt7/qt7movievideowidget.mm new file mode 100644 index 000000000..a0858c7fc --- /dev/null +++ b/src/plugins/qt7/qt7movievideowidget.mm @@ -0,0 +1,437 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7backend.h" + +#import +#import +#import +#import +#import +#import + + +#include "qt7playercontrol.h" +#include "qt7movievideowidget.h" +#include "qt7playersession.h" +#include "qcvdisplaylink.h" +#include +#include + +#include + +#include + +#import + +#include "math.h" + +QT_USE_NAMESPACE + +class GLVideoWidget : public QGLWidget +{ +public: + + GLVideoWidget(QWidget *parent, const QGLFormat &format) + : QGLWidget(format, parent), + m_texRef(0), + m_nativeSize(640,480), + m_aspectRatioMode(Qt::KeepAspectRatio) + { + setAutoFillBackground(false); + } + + void initializeGL() + { + QColor bgColor = palette().color(QPalette::Background); + glClearColor(bgColor.redF(), bgColor.greenF(), bgColor.blueF(), bgColor.alphaF()); + } + + void resizeGL(int w, int h) + { + glMatrixMode(GL_MODELVIEW); + glLoadIdentity(); + glMatrixMode(GL_PROJECTION); + glLoadIdentity(); + glViewport(0, 0, GLsizei(w), GLsizei(h)); + gluOrtho2D(0, GLsizei(w), 0, GLsizei(h)); + updateGL(); + } + + void paintGL() + { + glClear(GL_COLOR_BUFFER_BIT); + if (!m_texRef) + return; + + glPushMatrix(); + glDisable(GL_CULL_FACE); + GLenum target = CVOpenGLTextureGetTarget(m_texRef); + glEnable(target); + + glBindTexture(target, CVOpenGLTextureGetName(m_texRef)); + glTexParameterf(target, GL_TEXTURE_MIN_FILTER, GL_LINEAR); + glTexParameterf(target, GL_TEXTURE_MAG_FILTER, GL_LINEAR); + GLfloat lowerLeft[2], lowerRight[2], upperRight[2], upperLeft[2]; + CVOpenGLTextureGetCleanTexCoords(m_texRef, lowerLeft, lowerRight, upperRight, upperLeft); + + glBegin(GL_QUADS); + QRect rect = displayRect(); + glTexCoord2f(lowerLeft[0], lowerLeft[1]); + glVertex2i(rect.topLeft().x(), rect.topLeft().y()); + glTexCoord2f(lowerRight[0], lowerRight[1]); + glVertex2i(rect.topRight().x() + 1, rect.topRight().y()); + glTexCoord2f(upperRight[0], upperRight[1]); + glVertex2i(rect.bottomRight().x() + 1, rect.bottomRight().y() + 1); + glTexCoord2f(upperLeft[0], upperLeft[1]); + glVertex2i(rect.bottomLeft().x(), rect.bottomLeft().y() + 1); + glEnd(); + glPopMatrix(); + } + + void setCVTexture(CVOpenGLTextureRef texRef) + { + if (m_texRef) + CVOpenGLTextureRelease(m_texRef); + + m_texRef = texRef; + + if (m_texRef) + CVOpenGLTextureRetain(m_texRef); + + if (isVisible()) { + makeCurrent(); + paintGL(); + swapBuffers(); + } + } + + QSize sizeHint() const + { + return m_nativeSize; + } + + void setNativeSize(const QSize &size) + { + m_nativeSize = size; + } + + void setAspectRatioMode(Qt::AspectRatioMode mode) + { + if (m_aspectRatioMode != mode) { + m_aspectRatioMode = mode; + update(); + } + } + +private: + QRect displayRect() const + { + QRect displayRect = rect(); + + if (m_aspectRatioMode == Qt::KeepAspectRatio) { + QSize size = m_nativeSize; + size.scale(displayRect.size(), Qt::KeepAspectRatio); + + displayRect = QRect(QPoint(0, 0), size); + displayRect.moveCenter(rect().center()); + } + return displayRect; + } + + CVOpenGLTextureRef m_texRef; + QSize m_nativeSize; + Qt::AspectRatioMode m_aspectRatioMode; +}; + +QT7MovieVideoWidget::QT7MovieVideoWidget(QObject *parent) + :QT7VideoWidgetControl(parent), + m_movie(0), + m_videoWidget(0), + m_fullscreen(false), + m_aspectRatioMode(Qt::KeepAspectRatio), + m_brightness(0), + m_contrast(0), + m_hue(0), + m_saturation(0) +{ +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieVideoWidget"; +#endif + + QGLFormat format = QGLFormat::defaultFormat(); + format.setSwapInterval(1); // Vertical sync (avoid tearing) + m_videoWidget = new GLVideoWidget(0, format); + + m_displayLink = new QCvDisplayLink(this); + + connect(m_displayLink, SIGNAL(tick(CVTimeStamp)), SLOT(updateVideoFrame(CVTimeStamp))); + + if (!createVisualContext()) { + qWarning() << "QT7MovieVideoWidget: failed to create visual context"; + } +} + +bool QT7MovieVideoWidget::createVisualContext() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + m_videoWidget->makeCurrent(); + + AutoReleasePool pool; + CGLContextObj cglContext = CGLGetCurrentContext(); + NSOpenGLPixelFormat *nsglPixelFormat = [NSOpenGLView defaultPixelFormat]; + CGLPixelFormatObj cglPixelFormat = static_cast([nsglPixelFormat CGLPixelFormatObj]); + + CFTypeRef keys[] = { kQTVisualContextOutputColorSpaceKey }; + CGColorSpaceRef colorSpace = NULL; + CMProfileRef sysprof = NULL; + + // Get the Systems Profile for the main display + if (CMGetSystemProfile(&sysprof) == noErr) { + // Create a colorspace with the systems profile + colorSpace = CGColorSpaceCreateWithPlatformColorSpace(sysprof); + CMCloseProfile(sysprof); + } + + if (!colorSpace) + colorSpace = CGColorSpaceCreateDeviceRGB(); + + CFDictionaryRef textureContextAttributes = CFDictionaryCreate(kCFAllocatorDefault, + (const void **)keys, + (const void **)&colorSpace, 1, + &kCFTypeDictionaryKeyCallBacks, + &kCFTypeDictionaryValueCallBacks); + + OSStatus err = QTOpenGLTextureContextCreate(kCFAllocatorDefault, + cglContext, + cglPixelFormat, + textureContextAttributes, + &m_visualContext); + if (err != noErr) + qWarning() << "Could not create visual context (OpenGL)"; + + + return (err == noErr); +#endif // QUICKTIME_C_API_AVAILABLE + + return false; +} + +QT7MovieVideoWidget::~QT7MovieVideoWidget() +{ + m_displayLink->stop(); + [(QTMovie*)m_movie release]; + delete m_videoWidget; +} + +QWidget *QT7MovieVideoWidget::videoWidget() +{ + return m_videoWidget; +} + +void QT7MovieVideoWidget::setupVideoOutput() +{ + AutoReleasePool pool; + +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieVideoWidget::setupVideoOutput" << m_movie; +#endif + + if (m_movie == 0) { + m_displayLink->stop(); + return; + } + + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + m_nativeSize = QSize(size.width, size.height); + m_videoWidget->setNativeSize(m_nativeSize); + +#ifdef QUICKTIME_C_API_AVAILABLE + // targets a Movie to render into a visual context + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], m_visualContext); +#endif + + m_displayLink->start(); +} + +void QT7MovieVideoWidget::setMovie(void *movie) +{ + if (m_movie == movie) + return; + + if (m_movie) { +#ifdef QUICKTIME_C_API_AVAILABLE + SetMovieVisualContext([(QTMovie*)m_movie quickTimeMovie], nil); +#endif + [(QTMovie*)m_movie release]; + } + + m_movie = movie; + [(QTMovie*)m_movie retain]; + + setupVideoOutput(); +} + +void QT7MovieVideoWidget::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + setupVideoOutput(); + } +} + +bool QT7MovieVideoWidget::isFullScreen() const +{ + return m_fullscreen; +} + +void QT7MovieVideoWidget::setFullScreen(bool fullScreen) +{ + m_fullscreen = fullScreen; +} + +QSize QT7MovieVideoWidget::nativeSize() const +{ + return m_nativeSize; +} + +Qt::AspectRatioMode QT7MovieVideoWidget::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QT7MovieVideoWidget::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + m_videoWidget->setAspectRatioMode(mode); +} + +int QT7MovieVideoWidget::brightness() const +{ + return m_brightness; +} + +void QT7MovieVideoWidget::setBrightness(int brightness) +{ + m_brightness = brightness; + updateColors(); +} + +int QT7MovieVideoWidget::contrast() const +{ + return m_contrast; +} + +void QT7MovieVideoWidget::setContrast(int contrast) +{ + m_contrast = contrast; + updateColors(); +} + +int QT7MovieVideoWidget::hue() const +{ + return m_hue; +} + +void QT7MovieVideoWidget::setHue(int hue) +{ + m_hue = hue; + updateColors(); +} + +int QT7MovieVideoWidget::saturation() const +{ + return m_saturation; +} + +void QT7MovieVideoWidget::setSaturation(int saturation) +{ + m_saturation = saturation; + updateColors(); +} + +void QT7MovieVideoWidget::updateColors() +{ +#ifdef QUICKTIME_C_API_AVAILABLE + if (m_movie) { + QTMovie *movie = (QTMovie*)m_movie; + + Float32 value; + value = m_brightness/100.0; + SetMovieVisualBrightness([movie quickTimeMovie], value, 0); + value = pow(2, m_contrast/50.0); + SetMovieVisualContrast([movie quickTimeMovie], value, 0); + value = m_hue/100.0; + SetMovieVisualHue([movie quickTimeMovie], value, 0); + value = 1.0+m_saturation/100.0; + SetMovieVisualSaturation([movie quickTimeMovie], value, 0); + } +#endif +} + +void QT7MovieVideoWidget::updateVideoFrame(const CVTimeStamp &ts) +{ +#ifdef QUICKTIME_C_API_AVAILABLE + AutoReleasePool pool; + // check for new frame + if (m_visualContext && QTVisualContextIsNewImageAvailable(m_visualContext, &ts)) { + CVOpenGLTextureRef currentFrame = NULL; + + // get a "frame" (image buffer) from the Visual Context, indexed by the provided time + OSStatus status = QTVisualContextCopyImageForTime(m_visualContext, NULL, &ts, ¤tFrame); + + // the above call may produce a null frame so check for this first + // if we have a frame, then draw it + if (status == noErr && currentFrame) { +#ifdef QT_DEBUG_QT7 + qDebug() << "render video frame"; +#endif + m_videoWidget->setCVTexture(currentFrame); + CVOpenGLTextureRelease(currentFrame); + } + QTVisualContextTask(m_visualContext); + } +#else + Q_UNUSED(ts); +#endif +} + +#include "moc_qt7movievideowidget.cpp" diff --git a/src/plugins/qt7/qt7movieviewoutput.h b/src/plugins/qt7/qt7movieviewoutput.h new file mode 100644 index 000000000..4b9cff3ea --- /dev/null +++ b/src/plugins/qt7/qt7movieviewoutput.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIEVIEWOUTPUT_H +#define QT7MOVIEVIEWOUTPUT_H + +#include + +#include +#include + +#include +#include "qt7videooutput.h" + + +QT_BEGIN_NAMESPACE + +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieViewOutput : public QT7VideoWindowControl +{ +public: + QT7MovieViewOutput(QObject *parent = 0); + ~QT7MovieViewOutput(); + + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + void repaint(); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + +private: + void setupVideoOutput(); + + void *m_movie; + void *m_movieView; + bool m_layouted; + + WId m_winId; + QRect m_displayRect; + bool m_fullscreen; + QSize m_nativeSize; + Qt::AspectRatioMode m_aspectRatioMode; + int m_brightness; + int m_contrast; + int m_hue; + int m_saturation; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7movieviewoutput.mm b/src/plugins/qt7/qt7movieviewoutput.mm new file mode 100644 index 000000000..6d38d6148 --- /dev/null +++ b/src/plugins/qt7/qt7movieviewoutput.mm @@ -0,0 +1,339 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movieviewoutput.h" +#include "qt7playersession.h" +#include + +#include +#include + +QT_USE_NAMESPACE + +#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} + +@interface TransparentQTMovieView : QTMovieView +{ +@private + QRect *m_drawRect; + qreal m_brightness, m_contrast, m_saturation, m_hue; +} + +- (TransparentQTMovieView *) init; +- (void) setDrawRect:(QRect &)rect; +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img; +- (void) setContrast:(qreal) contrast; +@end + +@implementation TransparentQTMovieView + +- (TransparentQTMovieView *) init +{ + self = [super initWithFrame:NSZeroRect]; + if (self) { + [self setControllerVisible:NO]; + [self setContrast:1.0]; + [self setDelegate:self]; + } + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void) setContrast:(qreal) contrast +{ + m_hue = 0.0; + m_brightness = 0.0; + m_contrast = contrast; + m_saturation = 1.0; +} + + +- (void) setDrawRect:(QRect &)rect +{ + *m_drawRect = rect; + + NSRect nsrect; + nsrect.origin.x = m_drawRect->x(); + nsrect.origin.y = m_drawRect->y(); + nsrect.size.width = m_drawRect->width(); + nsrect.size.height = m_drawRect->height(); + [self setFrame:nsrect]; +} + +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img +{ + // This method is called from QTMovieView just + // before the image will be drawn. + Q_UNUSED(view); + + if ( !qFuzzyCompare(m_brightness, 0.0) || + !qFuzzyCompare(m_contrast, 1.0) || + !qFuzzyCompare(m_saturation, 1.0)){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIColorControls"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_brightness] forKey:@"inputBrightness"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_contrast < 1) ? m_contrast : 1 + ((m_contrast-1)*3)] forKey:@"inputContrast"]; + [colorFilter setValue:[NSNumber numberWithFloat:m_saturation] forKey:@"inputSaturation"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + } + + /*if (m_hue){ + CIFilter *colorFilter = [CIFilter filterWithName:@"CIHueAdjust"]; + [colorFilter setValue:[NSNumber numberWithFloat:(m_hue * 3.14)] forKey:@"inputAngle"]; + [colorFilter setValue:img forKey:@"inputImage"]; + img = [colorFilter valueForKey:@"outputImage"]; + }*/ + + return img; +} + + +VIDEO_TRANSPARENT(mouseDown); +VIDEO_TRANSPARENT(mouseDragged); +VIDEO_TRANSPARENT(mouseUp); +VIDEO_TRANSPARENT(mouseMoved); +VIDEO_TRANSPARENT(mouseEntered); +VIDEO_TRANSPARENT(mouseExited); +VIDEO_TRANSPARENT(rightMouseDown); +VIDEO_TRANSPARENT(rightMouseDragged); +VIDEO_TRANSPARENT(rightMouseUp); +VIDEO_TRANSPARENT(otherMouseDown); +VIDEO_TRANSPARENT(otherMouseDragged); +VIDEO_TRANSPARENT(otherMouseUp); +VIDEO_TRANSPARENT(keyDown); +VIDEO_TRANSPARENT(keyUp); +VIDEO_TRANSPARENT(scrollWheel) + +@end + + +QT7MovieViewOutput::QT7MovieViewOutput(QObject *parent) + :QT7VideoWindowControl(parent), + m_movie(0), + m_movieView(0), + m_layouted(false), + m_winId(0), + m_fullscreen(false), + m_aspectRatioMode(Qt::KeepAspectRatio), + m_brightness(0), + m_contrast(0), + m_hue(0), + m_saturation(0) +{ +} + +QT7MovieViewOutput::~QT7MovieViewOutput() +{ + [(QTMovieView*)m_movieView release]; + [(QTMovie*)m_movie release]; +} + +void QT7MovieViewOutput::setupVideoOutput() +{ + AutoReleasePool pool; + +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieViewOutput::setupVideoOutput" << m_movie << m_winId; +#endif + if (m_movie == 0 || m_winId <= 0) + return; + + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + m_nativeSize = QSize(size.width, size.height); + + if (!m_movieView) + m_movieView = [[TransparentQTMovieView alloc] init]; + + [(QTMovieView*)m_movieView setControllerVisible:NO]; + [(QTMovieView*)m_movieView setMovie:(QTMovie*)m_movie]; + + [(NSView *)m_winId addSubview:(QTMovieView*)m_movieView]; + m_layouted = true; + + setDisplayRect(m_displayRect); +} + +void QT7MovieViewOutput::setMovie(void *movie) +{ + if (m_movie != movie) { + if (m_movie) { + if (m_movieView) + [(QTMovieView*)m_movieView setMovie:nil]; + + [(QTMovie*)m_movie release]; + } + + m_movie = movie; + + if (m_movie) + [(QTMovie*)m_movie retain]; + + setupVideoOutput(); + } +} + +void QT7MovieViewOutput::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + emit nativeSizeChanged(); + } +} + +WId QT7MovieViewOutput::winId() const +{ + return m_winId; +} + +void QT7MovieViewOutput::setWinId(WId id) +{ + if (m_winId != id) { + if (m_movieView && m_layouted) { + [(QTMovieView*)m_movieView removeFromSuperview]; + m_layouted = false; + } + + m_winId = id; + setupVideoOutput(); + } +} + +QRect QT7MovieViewOutput::displayRect() const +{ + return m_displayRect; +} + +void QT7MovieViewOutput::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + if (m_movieView) { + AutoReleasePool pool; + [(QTMovieView*)m_movieView setPreservesAspectRatio:(m_aspectRatioMode == Qt::KeepAspectRatio ? YES : NO)]; + [(QTMovieView*)m_movieView setFrame:NSMakeRect(m_displayRect.x(), + m_displayRect.y(), + m_displayRect.width(), + m_displayRect.height())]; + } + +} + +bool QT7MovieViewOutput::isFullScreen() const +{ + return m_fullscreen; +} + +void QT7MovieViewOutput::setFullScreen(bool fullScreen) +{ + m_fullscreen = fullScreen; + setDisplayRect(m_displayRect); +} + +void QT7MovieViewOutput::repaint() +{ +} + +QSize QT7MovieViewOutput::nativeSize() const +{ + return m_nativeSize; +} + +Qt::AspectRatioMode QT7MovieViewOutput::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QT7MovieViewOutput::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + setDisplayRect(m_displayRect); +} + +int QT7MovieViewOutput::brightness() const +{ + return m_brightness; +} + +void QT7MovieViewOutput::setBrightness(int brightness) +{ + m_brightness = brightness; +} + +int QT7MovieViewOutput::contrast() const +{ + return m_contrast; +} + +void QT7MovieViewOutput::setContrast(int contrast) +{ + m_contrast = contrast; + [(TransparentQTMovieView*)m_movieView setContrast:(contrast/100.0+1.0)]; +} + +int QT7MovieViewOutput::hue() const +{ + return m_hue; +} + +void QT7MovieViewOutput::setHue(int hue) +{ + m_hue = hue; +} + +int QT7MovieViewOutput::saturation() const +{ + return m_saturation; +} + +void QT7MovieViewOutput::setSaturation(int saturation) +{ + m_saturation = saturation; +} + diff --git a/src/plugins/qt7/qt7movieviewrenderer.h b/src/plugins/qt7/qt7movieviewrenderer.h new file mode 100644 index 000000000..f95f6097d --- /dev/null +++ b/src/plugins/qt7/qt7movieviewrenderer.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7MOVIEVIEWRENDERER_H +#define QT7MOVIEVIEWRENDERER_H + +#include +#include + +#include +#include + +#include +#include "qt7videooutput.h" +#include + +QT_BEGIN_NAMESPACE + +class QVideoFrame; + +class QT7PlayerSession; +class QT7PlayerService; + +class QT7MovieViewRenderer : public QT7VideoRendererControl +{ +public: + QT7MovieViewRenderer(QObject *parent = 0); + ~QT7MovieViewRenderer(); + + void setMovie(void *movie); + void updateNaturalSize(const QSize &newSize); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + void renderFrame(const QVideoFrame &); + +protected: + bool event(QEvent *event); + +private: + void setupVideoOutput(); + + void *m_movie; + void *m_movieView; + QSize m_nativeSize; + QAbstractVideoSurface *m_surface; + QVideoFrame m_currentFrame; + bool m_pendingRenderEvent; + QMutex m_mutex; +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7movieviewrenderer.mm b/src/plugins/qt7/qt7movieviewrenderer.mm new file mode 100644 index 000000000..b9d4f64b9 --- /dev/null +++ b/src/plugins/qt7/qt7movieviewrenderer.mm @@ -0,0 +1,371 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import + +#include "qt7backend.h" + +#include "qt7playercontrol.h" +#include "qt7movieviewrenderer.h" +#include "qt7playersession.h" +#include "qt7ciimagevideobuffer.h" +#include +#include +#include + +#include + +#include +#include +#include + +#include +#include + +QT_USE_NAMESPACE + +class NSBitmapVideoBuffer : public QAbstractVideoBuffer +{ +public: + NSBitmapVideoBuffer(NSBitmapImageRep *buffer) + : QAbstractVideoBuffer(NoHandle) + , m_buffer(buffer) + , m_mode(NotMapped) + { + [m_buffer retain]; + } + + virtual ~NSBitmapVideoBuffer() + { + [m_buffer release]; + } + + MapMode mapMode() const { return m_mode; } + + uchar *map(MapMode mode, int *numBytes, int *bytesPerLine) + { + if (mode != NotMapped && m_mode == NotMapped) { + if (numBytes) + *numBytes = [m_buffer bytesPerPlane]; + + if (bytesPerLine) + *bytesPerLine = [m_buffer bytesPerRow]; + + m_mode = mode; + + return [m_buffer bitmapData]; + } else { + return 0; + } + } + + void unmap() { m_mode = NotMapped; } + +private: + NSBitmapImageRep *m_buffer; + MapMode m_mode; +}; + + +#define VIDEO_TRANSPARENT(m) -(void)m:(NSEvent *)e{[[self superview] m:e];} + +@interface HiddenQTMovieView : QTMovieView +{ +@private + QWidget *m_window; + QT7MovieViewRenderer *m_renderer; + QReadWriteLock m_rendererLock; +} + +- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer; +- (void) setRenderer:(QT7MovieViewRenderer *)renderer; +- (void) setDrawRect:(const QRect &)rect; +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img; +@end + +@implementation HiddenQTMovieView + +- (HiddenQTMovieView *) initWithRenderer:(QT7MovieViewRenderer *)renderer +{ + self = [super initWithFrame:NSZeroRect]; + if (self) { + [self setControllerVisible:NO]; + [self setDelegate:self]; + + QWriteLocker lock(&self->m_rendererLock); + self->m_renderer = renderer; + + self->m_window = new QWidget; + self->m_window->setWindowOpacity(0.0); + self->m_window->show(); + self->m_window->hide(); + + [(NSView *)(self->m_window->winId()) addSubview:self]; + [self setDrawRect:QRect(0,0,1,1)]; + } + return self; +} + +- (void) dealloc +{ + [super dealloc]; +} + +- (void) setRenderer:(QT7MovieViewRenderer *)renderer +{ + QWriteLocker lock(&m_rendererLock); + m_renderer = renderer; +} + +- (void) setDrawRect:(const QRect &)rect +{ + NSRect nsrect; + nsrect.origin.x = rect.x(); + nsrect.origin.y = rect.y(); + nsrect.size.width = rect.width(); + nsrect.size.height = rect.height(); + [self setFrame:nsrect]; +} + +- (CIImage *) view:(QTMovieView *)view willDisplayImage:(CIImage *)img +{ + // This method is called from QTMovieView just + // before the image will be drawn. + Q_UNUSED(view); + QReadLocker lock(&m_rendererLock); + + if (m_renderer) { + CGRect bounds = [img extent]; + int w = bounds.size.width; + int h = bounds.size.height; + + QVideoFrame frame; + + QAbstractVideoSurface *surface = m_renderer->surface(); + if (!surface || !surface->isActive()) + return img; + + if (surface->surfaceFormat().handleType() == QAbstractVideoBuffer::CoreImageHandle) { + //surface supports rendering of opengl based CIImage + frame = QVideoFrame(new QT7CIImageVideoBuffer(img), QSize(w,h), QVideoFrame::Format_RGB32 ); + } else { + //Swap R and B colors + CIFilter *colorSwapFilter = [CIFilter filterWithName: @"CIColorMatrix" keysAndValues: + @"inputImage", img, + @"inputRVector", [CIVector vectorWithX: 0 Y: 0 Z: 1 W: 0], + @"inputGVector", [CIVector vectorWithX: 0 Y: 1 Z: 0 W: 0], + @"inputBVector", [CIVector vectorWithX: 1 Y: 0 Z: 0 W: 0], + @"inputAVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 1], + @"inputBiasVector", [CIVector vectorWithX: 0 Y: 0 Z: 0 W: 0], + nil]; + CIImage *img = [colorSwapFilter valueForKey: @"outputImage"]; + NSBitmapImageRep *bitmap =[[NSBitmapImageRep alloc] initWithCIImage:img]; + //requesting the bitmap data is slow, + //but it's better to do it here to avoid blocking the main thread for a long. + [bitmap bitmapData]; + frame = QVideoFrame(new NSBitmapVideoBuffer(bitmap), QSize(w,h), QVideoFrame::Format_RGB32 ); + [bitmap release]; + } + + m_renderer->renderFrame(frame); + } + + return img; +} + +// Override this method so that the movie doesn't stop if +// the window becomes invisible +- (void)viewWillMoveToWindow:(NSWindow *)newWindow +{ + Q_UNUSED(newWindow); +} + + +VIDEO_TRANSPARENT(mouseDown); +VIDEO_TRANSPARENT(mouseDragged); +VIDEO_TRANSPARENT(mouseUp); +VIDEO_TRANSPARENT(mouseMoved); +VIDEO_TRANSPARENT(mouseEntered); +VIDEO_TRANSPARENT(mouseExited); +VIDEO_TRANSPARENT(rightMouseDown); +VIDEO_TRANSPARENT(rightMouseDragged); +VIDEO_TRANSPARENT(rightMouseUp); +VIDEO_TRANSPARENT(otherMouseDown); +VIDEO_TRANSPARENT(otherMouseDragged); +VIDEO_TRANSPARENT(otherMouseUp); +VIDEO_TRANSPARENT(keyDown); +VIDEO_TRANSPARENT(keyUp); +VIDEO_TRANSPARENT(scrollWheel) + +@end + + +QT7MovieViewRenderer::QT7MovieViewRenderer(QObject *parent) + :QT7VideoRendererControl(parent), + m_movie(0), + m_movieView(0), + m_surface(0), + m_pendingRenderEvent(false) +{ +} + +QT7MovieViewRenderer::~QT7MovieViewRenderer() +{ + [(HiddenQTMovieView*)m_movieView setRenderer:0]; + + QMutexLocker locker(&m_mutex); + m_currentFrame = QVideoFrame(); + [(HiddenQTMovieView*)m_movieView release]; +} + +void QT7MovieViewRenderer::setupVideoOutput() +{ + AutoReleasePool pool; + +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7MovieViewRenderer::setupVideoOutput" << m_movie << m_surface; +#endif + + HiddenQTMovieView *movieView = (HiddenQTMovieView*)m_movieView; + + if (movieView && !m_movie) { + [movieView setMovie:nil]; + } + + if (m_movie) { + NSSize size = [[(QTMovie*)m_movie attributeForKey:@"QTMovieNaturalSizeAttribute"] sizeValue]; + + m_nativeSize = QSize(size.width, size.height); + + if (!movieView) { + movieView = [[HiddenQTMovieView alloc] initWithRenderer:this]; + m_movieView = movieView; + [movieView setControllerVisible:NO]; + } + + [movieView setMovie:(QTMovie*)m_movie]; + [movieView setDrawRect:QRect(QPoint(0,0), m_nativeSize)]; + } + + if (m_surface && !m_nativeSize.isEmpty()) { + bool coreImageFrameSupported = !m_surface->supportedPixelFormats(QAbstractVideoBuffer::CoreImageHandle).isEmpty() && + !m_surface->supportedPixelFormats(QAbstractVideoBuffer::GLTextureHandle).isEmpty(); + + QVideoSurfaceFormat format(m_nativeSize, QVideoFrame::Format_RGB32, + coreImageFrameSupported ? QAbstractVideoBuffer::CoreImageHandle : QAbstractVideoBuffer::NoHandle); + + if (m_surface->isActive() && m_surface->surfaceFormat() != format) { +#ifdef QT_DEBUG_QT7 + qDebug() << "Surface format was changed, stop the surface."; +#endif + m_surface->stop(); + } + + if (!m_surface->isActive()) { +#ifdef QT_DEBUG_QT7 + qDebug() << "Starting the surface with format" << format; +#endif + if (!m_surface->start(format)) + qWarning() << "failed to start video surface" << m_surface->error(); + } + } +} + +void QT7MovieViewRenderer::setMovie(void *movie) +{ + if (movie == m_movie) + return; + + QMutexLocker locker(&m_mutex); + m_movie = movie; + setupVideoOutput(); +} + +void QT7MovieViewRenderer::updateNaturalSize(const QSize &newSize) +{ + if (m_nativeSize != newSize) { + m_nativeSize = newSize; + setupVideoOutput(); + } +} + +QAbstractVideoSurface *QT7MovieViewRenderer::surface() const +{ + return m_surface; +} + +void QT7MovieViewRenderer::setSurface(QAbstractVideoSurface *surface) +{ + if (surface == m_surface) + return; + + QMutexLocker locker(&m_mutex); + + if (m_surface && m_surface->isActive()) + m_surface->stop(); + + m_surface = surface; + setupVideoOutput(); +} + +void QT7MovieViewRenderer::renderFrame(const QVideoFrame &frame) +{ + + QMutexLocker locker(&m_mutex); + m_currentFrame = frame; + + if (!m_pendingRenderEvent) + qApp->postEvent(this, new QEvent(QEvent::User), Qt::HighEventPriority); + + m_pendingRenderEvent = true; +} + +bool QT7MovieViewRenderer::event(QEvent *event) +{ + if (event->type() == QEvent::User) { + QMutexLocker locker(&m_mutex); + m_pendingRenderEvent = false; + if (m_surface->isActive()) + m_surface->present(m_currentFrame); + } + + return QT7VideoRendererControl::event(event); +} diff --git a/src/plugins/qt7/qt7serviceplugin.h b/src/plugins/qt7/qt7serviceplugin.h new file mode 100644 index 000000000..fb0a546b3 --- /dev/null +++ b/src/plugins/qt7/qt7serviceplugin.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QT7SERVICEPLUGIN_H +#define QT7SERVICEPLUGIN_H + +#include + +QT_BEGIN_NAMESPACE + +class QT7ServicePlugin + : public QMediaServiceProviderPlugin + , public QMediaServiceSupportedFormatsInterface + , public QMediaServiceFeaturesInterface +{ + Q_INTERFACES(QMediaServiceFeaturesInterface) +public: + QT7ServicePlugin(); + + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QMediaServiceProviderHint::Features supportedFeatures(const QByteArray &service) const; + QtMultimediaKit::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const; + QStringList supportedMimeTypes() const; + +private: + void buildSupportedTypes(); + + QStringList m_supportedMimeTypes; +}; + +QT_END_NAMESPACE + +#endif // QGSTREAMERSERVICEPLUGIN_H diff --git a/src/plugins/qt7/qt7serviceplugin.mm b/src/plugins/qt7/qt7serviceplugin.mm new file mode 100644 index 000000000..a692e190f --- /dev/null +++ b/src/plugins/qt7/qt7serviceplugin.mm @@ -0,0 +1,129 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#import +#import + +#include +#include + +#include "qt7backend.h" +#include "qt7serviceplugin.h" +#include "qt7playerservice.h" + +#include + +QT_BEGIN_NAMESPACE + + +QT7ServicePlugin::QT7ServicePlugin() +{ + buildSupportedTypes(); +} + +QStringList QT7ServicePlugin::keys() const +{ + return QStringList() +#ifdef QMEDIA_QT7_PLAYER + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) +#endif + ; +} + +QMediaService* QT7ServicePlugin::create(QString const& key) +{ +#ifdef QT_DEBUG_QT7 + qDebug() << "QT7ServicePlugin::create" << key; +#endif +#ifdef QMEDIA_QT7_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new QT7PlayerService; +#endif + qWarning() << "unsupported key:" << key; + + return 0; +} + +void QT7ServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QMediaServiceProviderHint::Features QT7ServicePlugin::supportedFeatures( + const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_MEDIAPLAYER) + return QMediaServiceProviderHint::VideoSurface; + else + return QMediaServiceProviderHint::Features(); +} + +QtMultimediaKit::SupportEstimate QT7ServicePlugin::hasSupport(const QString &mimeType, const QStringList& codecs) const +{ + Q_UNUSED(codecs); + + if (m_supportedMimeTypes.contains(mimeType)) + return QtMultimediaKit::ProbablySupported; + + return QtMultimediaKit::MaybeSupported; +} + +QStringList QT7ServicePlugin::supportedMimeTypes() const +{ + return m_supportedMimeTypes; +} + +void QT7ServicePlugin::buildSupportedTypes() +{ + AutoReleasePool pool; + NSArray *utis = [QTMovie movieTypesWithOptions:QTIncludeCommonTypes]; + for (NSString *uti in utis) { + NSString* mimeType = (NSString*)UTTypeCopyPreferredTagWithClass((CFStringRef)uti, kUTTagClassMIMEType); + if (mimeType != 0) { + m_supportedMimeTypes.append(QString::fromUtf8([mimeType UTF8String])); + [mimeType release]; + } + } +} + +Q_EXPORT_PLUGIN2(qtmedia_qt7engine, QT7ServicePlugin); + +QT_END_NAMESPACE diff --git a/src/plugins/qt7/qt7videooutput.h b/src/plugins/qt7/qt7videooutput.h new file mode 100644 index 000000000..060f7b910 --- /dev/null +++ b/src/plugins/qt7/qt7videooutput.h @@ -0,0 +1,116 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QT7VIDEOOUTPUTCONTROL_H +#define QT7VIDEOOUTPUTCONTROL_H + +#include +#include + +#include +#include +#include +#include + +#include + + +QT_BEGIN_NAMESPACE + +class QMediaPlaylist; +class QMediaPlaylistNavigator; +class QT7PlayerSession; +class QT7PlayerService; + + +class QT7VideoOutput { +public: + virtual ~QT7VideoOutput() {} + virtual void setMovie(void *movie) = 0; + virtual void updateNaturalSize(const QSize &newSize) = 0; +}; + +#define QT7VideoOutput_iid \ + "com.nokia.Qt.QT7VideoOutput/1.0" +Q_DECLARE_INTERFACE(QT7VideoOutput, QT7VideoOutput_iid) + +class QT7VideoWindowControl : public QVideoWindowControl, public QT7VideoOutput +{ +Q_OBJECT +Q_INTERFACES(QT7VideoOutput) +public: + virtual ~QT7VideoWindowControl() {} + +protected: + QT7VideoWindowControl(QObject *parent) + :QVideoWindowControl(parent) + {} +}; + +class QT7VideoRendererControl : public QVideoRendererControl, public QT7VideoOutput +{ +Q_OBJECT +Q_INTERFACES(QT7VideoOutput) +public: + virtual ~QT7VideoRendererControl() {} + +protected: + QT7VideoRendererControl(QObject *parent) + :QVideoRendererControl(parent) + {} +}; + +class QT7VideoWidgetControl : public QVideoWidgetControl, public QT7VideoOutput +{ +Q_OBJECT +Q_INTERFACES(QT7VideoOutput) +public: + virtual ~QT7VideoWidgetControl() {} + +protected: + QT7VideoWidgetControl(QObject *parent) + :QVideoWidgetControl(parent) + {} +}; + +QT_END_NAMESPACE + +#endif diff --git a/src/plugins/qt7/qt7videooutput.mm b/src/plugins/qt7/qt7videooutput.mm new file mode 100644 index 000000000..2be053669 --- /dev/null +++ b/src/plugins/qt7/qt7videooutput.mm @@ -0,0 +1,91 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qt7playercontrol.h" +#include "qt7playersession.h" +#include + +QT_USE_NAMESPACE + +/* +QT7VideoOutputControl::QT7VideoOutputControl(QObject *parent) + :QVideoOutputControl(parent), + m_session(0), + m_output(QVideoOutputControl::NoOutput) +{ +} + +QT7VideoOutputControl::~QT7VideoOutputControl() +{ +} + +void QT7VideoOutputControl::setSession(QT7PlayerSession *session) +{ + m_session = session; +} + +QList QT7VideoOutputControl::availableOutputs() const +{ + return m_outputs; +} + +void QT7VideoOutputControl::enableOutput(QVideoOutputControl::Output output) +{ + if (!m_outputs.contains(output)) + m_outputs.append(output); +} + +QVideoOutputControl::Output QT7VideoOutputControl::output() const +{ + return m_output; +} + +void QT7VideoOutputControl::setOutput(Output output) +{ + if (m_output != output) { + m_output = output; + emit videoOutputChanged(m_output); + } +} + +#include "moc_qt7videooutputcontrol.cpp" + +*/ diff --git a/src/plugins/simulator/camera/simulatorcamera.pri b/src/plugins/simulator/camera/simulatorcamera.pri new file mode 100644 index 000000000..867fc4b31 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcamera.pri @@ -0,0 +1,25 @@ +INCLUDEPATH += $$PWD \ + $${SOURCE_DIR}/src/multimedia + +INCLUDEPATH += camera + +HEADERS += \ + $$PWD/simulatorcameraservice.h \ + $$PWD/simulatorcamerasession.h \ + $$PWD/simulatorcameracontrol.h \ + $$PWD/simulatorvideorenderercontrol.h \ + $$PWD/simulatorvideoinputdevicecontrol.h \ + $$PWD/simulatorcameraimagecapturecontrol.h \ + $$PWD/simulatorcameraexposurecontrol.h \ + $$PWD/simulatorcamerasettings.h + +SOURCES += \ + $$PWD/simulatorcameraservice.cpp \ + $$PWD/simulatorcamerasession.cpp \ + $$PWD/simulatorcameracontrol.cpp \ + $$PWD/simulatorvideorenderercontrol.cpp \ + $$PWD/simulatorvideoinputdevicecontrol.cpp \ + $$PWD/simulatorcameraimagecapturecontrol.cpp \ + $$PWD/simulatorcameraexposurecontrol.cpp \ + $$PWD/simulatorcamerasettings.cpp + diff --git a/src/plugins/simulator/camera/simulatorcameracontrol.cpp b/src/plugins/simulator/camera/simulatorcameracontrol.cpp new file mode 100644 index 000000000..384215295 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameracontrol.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "simulatorcameracontrol.h" + +#include + +SimulatorCameraControl::SimulatorCameraControl(SimulatorCameraSession *session) + :QCameraControl(session), + m_session(session), + mState(QCamera::UnloadedState), + mStatus(QCamera::UnloadedStatus) +{ +} + +SimulatorCameraControl::~SimulatorCameraControl() +{ +} + +QCamera::CaptureMode SimulatorCameraControl::captureMode() const +{ + return m_session->captureMode(); +} + +void SimulatorCameraControl::setCaptureMode(QCamera::CaptureMode mode) +{ + if (m_session->captureMode() != mode) { + m_session->setCaptureMode(mode); + emit captureModeChanged(mode); + } +} + +void SimulatorCameraControl::setState(QCamera::State state) +{ + if (mState == state) { + return; + } + + // Simulator only supports these status + Q_ASSERT(mStatus == QCamera::UnloadedStatus || mStatus == QCamera::LoadedStatus + || mStatus == QCamera::ActiveStatus); + + switch (state) { + case QCamera::UnloadedState: // To UnloadedState - Release resources + switch (mStatus) { + case QCamera::LoadedStatus: + // Unload + break; + case QCamera::ActiveStatus: + // Stop and Unload + emit stopCamera(); + break; + default: + // Unrecognized internal state (Status) + return; + } + mStatus = QCamera::UnloadedStatus; + emit statusChanged(mStatus); + break; + + case QCamera::LoadedState: // To LoadedState - Reserve resources OR Stop ViewFinder and Cancel Capture + switch (mStatus) { + case QCamera::UnloadedStatus: + // Load + mStatus = QCamera::LoadingStatus; + emit statusChanged(mStatus); + break; + case QCamera::ActiveStatus: + // Stop + emit stopCamera(); + break; + + default: + // Unregocnized internal state (Status) + return; + } + mStatus = QCamera::LoadedStatus; + emit statusChanged(mStatus); + break; + + case QCamera::ActiveState: // To ActiveState - (Reserve Resources and) Start ViewFinder + switch (mStatus) { + case QCamera::UnloadedStatus: + // Load and Start (setting state handles starting) + mStatus = QCamera::LoadingStatus; + emit statusChanged(mStatus); + mStatus = QCamera::LoadedStatus; + emit statusChanged(mStatus); + mStatus = QCamera::StartingStatus; + emit statusChanged(mStatus); + emit startCamera(); + break; + case QCamera::LoadedStatus: + // Start + mStatus = QCamera::StartingStatus; + emit statusChanged(mStatus); + emit startCamera(); + break; + default: + // Unregocnized internal state (Status) + return; + } + mStatus = QCamera::ActiveStatus; + emit statusChanged(mStatus); + break; + + default: + return; + } + + mState = state; + emit stateChanged(mState); +} + +QCamera::State SimulatorCameraControl::state() const +{ + return mState; +} + +bool SimulatorCameraControl::canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const +{ + Q_UNUSED(status); + + switch (changeType) { + case QCameraControl::CaptureMode: + case QCameraControl::Viewfinder: + return true; + default: + return false; + } +} + +bool SimulatorCameraControl::isCaptureModeSupported(QCamera::CaptureMode mode) const +{ + return mode == QCamera::CaptureStillImage; +} + +QCamera::Status SimulatorCameraControl::status() const +{ + return mStatus; +} diff --git a/src/plugins/simulator/camera/simulatorcameracontrol.h b/src/plugins/simulator/camera/simulatorcameracontrol.h new file mode 100644 index 000000000..779f8150c --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameracontrol.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef SIMULATORCAMERACONTROL_H +#define SIMULATORCAMERACONTROL_H + +#include +#include +#include "simulatorcamerasession.h" + +QT_USE_NAMESPACE +QT_USE_NAMESPACE + +class SimulatorCameraControl : public QCameraControl +{ + Q_OBJECT +public: + SimulatorCameraControl(SimulatorCameraSession *session ); + virtual ~SimulatorCameraControl(); + + bool isValid() const { return true; } + + QCamera::State state() const; + void setState(QCamera::State state); + + QCamera::Status status() const; + + QCamera::CaptureMode captureMode() const; + void setCaptureMode(QCamera::CaptureMode mode); + + bool isCaptureModeSupported(QCamera::CaptureMode mode) const; + + bool canChangeProperty(PropertyChangeType changeType, QCamera::Status status) const; + +signals: + void startCamera(); + void stopCamera(); + +private: + void updateSupportedResolutions(const QString &device); + + SimulatorCameraSession *m_session; + QCamera::State mState; + QCamera::Status mStatus; + bool m_reloadPending; +}; + +#endif // CAMERACONTROL_H diff --git a/src/plugins/simulator/camera/simulatorcameraexposurecontrol.cpp b/src/plugins/simulator/camera/simulatorcameraexposurecontrol.cpp new file mode 100644 index 000000000..d99d521d1 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameraexposurecontrol.cpp @@ -0,0 +1,502 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "simulatorcameraexposurecontrol.h" +#include "simulatorcamerasession.h" + +SimulatorCameraExposureControl::SimulatorCameraExposureControl(SimulatorCameraSession *session, QObject *parent) : + QCameraExposureControl(parent), + mExposureMode(QCameraExposure::ExposureAuto), + mMeteringMode(QCameraExposure::MeteringAverage), + mSession(session), + mSettings(0) +{ + mSettings = mSession->settings(); + + connect(mSettings, SIGNAL(apertureChanged()), this, SLOT(apertureChanged())); + connect(mSettings, SIGNAL(apertureRangeChanged()), this, SLOT(apertureRangeChanged())); + connect(mSettings, SIGNAL(shutterSpeedChanged()), this, SLOT(shutterSpeedChanged())); + connect(mSettings, SIGNAL(isoSensitivityChanged()), this, SLOT(isoSensitivityChanged())); +} + +SimulatorCameraExposureControl::~SimulatorCameraExposureControl() +{ +} + +void SimulatorCameraExposureControl::apertureChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::Aperture); +} + +void SimulatorCameraExposureControl::apertureRangeChanged() +{ + emit exposureParameterRangeChanged(QCameraExposureControl::Aperture); +} + +void SimulatorCameraExposureControl::shutterSpeedChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ShutterSpeed); +} + +void SimulatorCameraExposureControl::isoSensitivityChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ISO); +} + +QCameraExposure::ExposureMode SimulatorCameraExposureControl::exposureMode() const +{ + return mExposureMode; +} + +void SimulatorCameraExposureControl::setExposureMode(QCameraExposure::ExposureMode mode) +{ + if (isExposureModeSupported(mode)) + mExposureMode = mode; +} + +bool SimulatorCameraExposureControl::isExposureModeSupported(QCameraExposure::ExposureMode mode) const +{ + switch (mode) { + case QCameraExposure::ExposureAuto: + case QCameraExposure::ExposureManual: + return true; + default: + return false; + } + + return false; +} + +QCameraExposure::MeteringMode SimulatorCameraExposureControl::meteringMode() const +{ + return mMeteringMode; +} + +void SimulatorCameraExposureControl::setMeteringMode(QCameraExposure::MeteringMode mode) +{ + if (isMeteringModeSupported(mode)) + mMeteringMode = mode; +} + +bool SimulatorCameraExposureControl::isMeteringModeSupported(QCameraExposure::MeteringMode mode) const +{ + switch (mode) { + case QCameraExposure::MeteringAverage: + case QCameraExposure::MeteringSpot: + case QCameraExposure::MeteringMatrix: + return true; + default: + return false; + } + return false; +} + +bool SimulatorCameraExposureControl::isParameterSupported(ExposureParameter parameter) const +{ + switch (parameter) { + case QCameraExposureControl::ISO: + case QCameraExposureControl::Aperture: + case QCameraExposureControl::ShutterSpeed: + case QCameraExposureControl::ExposureCompensation: + return true; + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + return false; + + default: + return false; + } + + return false; +} + +QVariant SimulatorCameraExposureControl::exposureParameter(ExposureParameter parameter) const +{ + switch (parameter) { + case QCameraExposureControl::ISO: + return QVariant(isoSensitivity()); + case QCameraExposureControl::Aperture: + return QVariant(aperture()); + case QCameraExposureControl::ShutterSpeed: + return QVariant(shutterSpeed()); + case QCameraExposureControl::ExposureCompensation: + return QVariant(exposureCompensation()); + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Not supported + return QVariant(); + + default: + // Not supported + return QVariant(); + } +} + +QCameraExposureControl::ParameterFlags SimulatorCameraExposureControl::exposureParameterFlags(ExposureParameter parameter) const +{ + QCameraExposureControl::ParameterFlags flags; + + /* + * ISO, Aperture, ShutterSpeed: + * - Automatic/Manual + * - Read/Write + * - Discrete range + * + * ExposureCompensation: + * - Automatic/Manual + * - Read/Write + * - Continuous range + * + * FlashPower, FlashCompensation: + * - Not supported + */ + switch (parameter) { + case QCameraExposureControl::ISO: + case QCameraExposureControl::Aperture: + case QCameraExposureControl::ShutterSpeed: + flags |= QCameraExposureControl::AutomaticValue; + break; + case QCameraExposureControl::ExposureCompensation: + flags |= QCameraExposureControl::AutomaticValue; + flags |= QCameraExposureControl::ContinuousRange; + break; + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Do nothing - no flags + break; + + default: + // Do nothing - no flags + break; + } + + return flags; +} + +QVariantList SimulatorCameraExposureControl::supportedParameterRange(ExposureParameter parameter) const +{ + QVariantList valueList; + switch (parameter) { + case QCameraExposureControl::ISO: { + QList exposureValues = mSettings->supportedIsoSensitivities(); + for (int i = 0; i < exposureValues.count(); ++i) { + valueList.append(QVariant(exposureValues[i])); + } + break; + } + case QCameraExposureControl::Aperture: { + QList apertureValues = mSettings->supportedApertures(); + for (int i = 0; i < apertureValues.count(); ++i) { + valueList.append(QVariant(apertureValues[i])); + } + break; + } + case QCameraExposureControl::ShutterSpeed: { + QList shutterSpeedValues = mSettings->supportedShutterSpeeds(); + for (int i = 0; i < shutterSpeedValues.count(); ++i) { + valueList.append(QVariant(shutterSpeedValues[i])); + } + break; + } + case QCameraExposureControl::ExposureCompensation: { + QList evValues = mSettings->supportedExposureCompensationValues(); + for (int i = 0; i < evValues.count(); ++i) { + valueList.append(QVariant(evValues[i])); + } + break; + } + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Not supported + break; + + default: + // Not supported + return QVariantList(); + } + + return valueList; +} + +bool SimulatorCameraExposureControl::setExposureParameter(ExposureParameter parameter, const QVariant& value) +{ + bool useDefaultValue = false; + + if (value.isNull()) + useDefaultValue = true; + + switch (parameter) { + case QCameraExposureControl::ISO: + if (useDefaultValue) { + setAutoIsoSensitivity(); + return false; + } + else + return setManualIsoSensitivity(value.toInt()); + + case QCameraExposureControl::Aperture: + if (useDefaultValue) { + setAutoAperture(); + return false; + } + else + return setManualAperture(value.toReal()); + + case QCameraExposureControl::ShutterSpeed: + if (useDefaultValue) { + setAutoShutterSpeed(); + return false; + } + else + return setManualShutterSpeed(value.toReal()); + + case QCameraExposureControl::ExposureCompensation: + if (useDefaultValue) { + setAutoExposureCompensation(); + return false; + } + else + return setManualExposureCompensation(value.toReal()); + + case QCameraExposureControl::FlashPower: + return false; + case QCameraExposureControl::FlashCompensation: + return false; + + default: + // Not supported + return false; + } +} + +QString SimulatorCameraExposureControl::extendedParameterName(ExposureParameter parameter) +{ + switch (parameter) { + case QCameraExposureControl::ISO: + return QString("ISO Sensitivity"); + case QCameraExposureControl::Aperture: + return QString("Aperture"); + case QCameraExposureControl::ShutterSpeed: + return QString("Shutter Speed"); + case QCameraExposureControl::ExposureCompensation: + return QString("Exposure Compensation"); + case QCameraExposureControl::FlashPower: + return QString("Flash Power"); + case QCameraExposureControl::FlashCompensation: + return QString("Flash Compensation"); + + default: + return QString(); + } +} + +int SimulatorCameraExposureControl::isoSensitivity() const +{ + return mSettings->isoSensitivity(); +} + +bool SimulatorCameraExposureControl::isIsoSensitivitySupported(const int iso) const +{ + return mSettings->supportedIsoSensitivities().contains(iso); +} + +bool SimulatorCameraExposureControl::setManualIsoSensitivity(int iso) +{ + if (isIsoSensitivitySupported(iso)) { + mSettings->setManualIsoSensitivity(iso); + return true; + } else { + QList supportedIsoValues = mSettings->supportedIsoSensitivities(); + int minIso = supportedIsoValues.first(); + int maxIso = supportedIsoValues.last(); + + if (iso < minIso) { // Smaller than minimum + iso = minIso; + } else if (iso > maxIso) { // Bigger than maximum + iso = maxIso; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 10000000; // Sensible max diff + for(int i = 0; i < supportedIsoValues.count(); ++i) { + int currentDiff = qAbs(iso - supportedIsoValues[i]); + if(currentDiff < smallestDiff) { + smallestDiff = currentDiff; + indexOfClosest = i; + } + } + iso = supportedIsoValues[indexOfClosest]; + } + mSettings->setManualIsoSensitivity(iso); + } + + return false; +} + +void SimulatorCameraExposureControl::setAutoIsoSensitivity() +{ + mSettings->setAutoIsoSensitivity(); +} + + +qreal SimulatorCameraExposureControl::aperture() const +{ + return mSettings->aperture(); +} + +bool SimulatorCameraExposureControl::isApertureSupported(const qreal aperture) const +{ + return mSettings->supportedApertures().contains(aperture); +} + +bool SimulatorCameraExposureControl::setManualAperture(qreal aperture) +{ + if (isApertureSupported(aperture)) { + mSettings->setManualAperture(aperture); + return true; + } else { + QList supportedApertureValues = mSettings->supportedApertures(); + qreal minAperture = supportedApertureValues.first(); + qreal maxAperture = supportedApertureValues.last(); + + if (aperture < minAperture) { // Smaller than minimum + aperture = minAperture; + } else if (aperture > maxAperture) { // Bigger than maximum + aperture = maxAperture; + } else { // Find closest + int indexOfClosest = 0; + qreal smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedApertureValues.count(); ++i) { + qreal currentDiff = qAbs(aperture - supportedApertureValues[i]); + if(currentDiff < smallestDiff) { + smallestDiff = currentDiff; + indexOfClosest = i; + } + } + aperture = supportedApertureValues[indexOfClosest]; + } + mSettings->setManualAperture(aperture); + } + + return false; +} + +void SimulatorCameraExposureControl::setAutoAperture() +{ + mSettings->setAutoAperture(); +} + +qreal SimulatorCameraExposureControl::shutterSpeed() const +{ + return mSettings->shutterSpeed(); +} + +bool SimulatorCameraExposureControl::isShutterSpeedSupported(const qreal seconds) const +{ + return mSettings->supportedShutterSpeeds().contains(seconds); +} + +bool SimulatorCameraExposureControl::setManualShutterSpeed(qreal seconds) +{ + if (isShutterSpeedSupported(seconds)) { + mSettings->setManualShutterSpeed(seconds); + return true; + } else { + QList supportedShutterSpeeds = mSettings->supportedShutterSpeeds(); + + qreal minShutterSpeed = supportedShutterSpeeds.first(); + qreal maxShutterSpeed = supportedShutterSpeeds.last(); + + if (seconds < minShutterSpeed) { // Smaller than minimum + seconds = minShutterSpeed; + } else if (seconds > maxShutterSpeed) { // Bigger than maximum + seconds = maxShutterSpeed; + } else { // Find closest + int indexOfClosest = 0; + qreal smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedShutterSpeeds.count(); ++i) { + qreal currentDiff = qAbs(seconds - supportedShutterSpeeds[i]); + if(currentDiff < smallestDiff) { + smallestDiff = currentDiff; + indexOfClosest = i; + } + } + seconds = supportedShutterSpeeds[indexOfClosest]; + } + mSettings->setManualShutterSpeed(seconds); + } + + return false; +} + +void SimulatorCameraExposureControl::setAutoShutterSpeed() +{ + mSettings->setAutoShutterSpeed(); +} + +qreal SimulatorCameraExposureControl::exposureCompensation() const +{ + return mSettings->exposureCompensation(); +} + +bool SimulatorCameraExposureControl::isExposureCompensationSupported(const qreal ev) const +{ + QList supportedValues = mSettings->supportedExposureCompensationValues(); + return (ev >= supportedValues.first() && ev <= supportedValues.last()); +} + +bool SimulatorCameraExposureControl::setManualExposureCompensation(qreal ev) +{ + if (isExposureCompensationSupported(ev)) { + mSettings->setExposureCompensation(ev); + return true; + } + + return false; +} + +void SimulatorCameraExposureControl::setAutoExposureCompensation() +{ + mSettings->setAutoExposureCompensation(); +} + +// End of file diff --git a/src/plugins/simulator/camera/simulatorcameraexposurecontrol.h b/src/plugins/simulator/camera/simulatorcameraexposurecontrol.h new file mode 100644 index 000000000..5ffa362e1 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameraexposurecontrol.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMULATORCAMERAEXPOSURECONTROL_H +#define SIMULATORCAMERAEXPOSURECONTROL_H + +#include + +#include "simulatorcamerasettings.h" + +QT_BEGIN_NAMESPACE + +class SimulatorCameraSession; + +/* + * Control for exposure related camera operation. + */ +class SimulatorCameraExposureControl : public QCameraExposureControl +{ + Q_OBJECT + +public: + + SimulatorCameraExposureControl(SimulatorCameraSession *session, QObject *parent = 0); + ~SimulatorCameraExposureControl(); + + // QCameraExposureControl + // Exposure Mode + QCameraExposure::ExposureMode exposureMode() const; + void setExposureMode(QCameraExposure::ExposureMode mode); + bool isExposureModeSupported(QCameraExposure::ExposureMode mode) const; + + // Metering Mode + QCameraExposure::MeteringMode meteringMode() const; + void setMeteringMode(QCameraExposure::MeteringMode mode); + bool isMeteringModeSupported(QCameraExposure::MeteringMode mode) const; + + // Exposure Parameter + bool isParameterSupported(ExposureParameter parameter) const; + QVariant exposureParameter(ExposureParameter parameter) const; + QCameraExposureControl::ParameterFlags exposureParameterFlags(ExposureParameter parameter) const; + QVariantList supportedParameterRange(ExposureParameter parameter) const; + bool setExposureParameter(ExposureParameter parameter, const QVariant& value); + + QString extendedParameterName(ExposureParameter parameter); + +private Q_SLOTS: // Internal Slots + void apertureChanged(); + void apertureRangeChanged(); + void shutterSpeedChanged(); + void isoSensitivityChanged(); + +private: // Internal - Implementing ExposureParameter + // ISO Sensitivity + int isoSensitivity() const; + bool setManualIsoSensitivity(int iso); + void setAutoIsoSensitivity(); + bool isIsoSensitivitySupported(const int iso) const; + + // Aperture + qreal aperture() const; + bool setManualAperture(qreal aperture); + void setAutoAperture(); + bool isApertureSupported(const qreal aperture) const; + + // Shutter Speed + qreal shutterSpeed() const; + bool setManualShutterSpeed(qreal seconds); + void setAutoShutterSpeed(); + bool isShutterSpeedSupported(const qreal seconds) const; + + // Exposure Compensation + qreal exposureCompensation() const; + bool setManualExposureCompensation(qreal ev); + void setAutoExposureCompensation(); + bool isExposureCompensationSupported(const qreal ev) const; + +private: // Data + QCameraExposure::ExposureMode mExposureMode; + QCameraExposure::MeteringMode mMeteringMode; + SimulatorCameraSession *mSession; + SimulatorCameraSettings *mSettings; +}; + +QT_END_NAMESPACE + +#endif // SIMULATORCAMERAEXPOSURECONTROL_H diff --git a/src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.cpp b/src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.cpp new file mode 100644 index 000000000..a0c8dce66 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.cpp @@ -0,0 +1,120 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "simulatorcameraimagecapturecontrol.h" +#include "simulatorcameraservice.h" +#include "simulatorcamerasession.h" +#include "simulatorcameracontrol.h" + +SimulatorCameraImageCaptureControl::SimulatorCameraImageCaptureControl(SimulatorCameraSession *session, SimulatorCameraService *service) : + QCameraImageCaptureControl(service), + mReadyForCapture(true), + m_driveMode(QCameraImageCapture::SingleImageCapture) // Default DriveMode +{ + m_session = session; + + m_service = service; + m_cameraControl = qobject_cast(m_service->requestControl(QCameraControl_iid)); + + // Chain these signals from session class + connect(m_session, SIGNAL(imageCaptured(const int, QImage)), + this, SIGNAL(imageCaptured(const int, QImage))); + connect(m_session, SIGNAL(imageSaved(const int, const QString&)), + this, SIGNAL(imageSaved(const int, const QString&))); + connect(m_session, SIGNAL(imageExposed(int)), + this, SIGNAL(imageExposed(int))); + connect(m_session, SIGNAL(captureError(int, int, const QString&)), + this, SIGNAL(error(int, int, const QString&))); +} + +SimulatorCameraImageCaptureControl::~SimulatorCameraImageCaptureControl() +{ +} + +bool SimulatorCameraImageCaptureControl::isReadyForCapture() const +{ + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureStillImage) { + return false; + } + + return mReadyForCapture; +} + +QCameraImageCapture::DriveMode SimulatorCameraImageCaptureControl::driveMode() const +{ + return m_driveMode; +} + +void SimulatorCameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode) +{ + if (mode != QCameraImageCapture::SingleImageCapture) { + emit error(0, QCamera::NotSupportedFeatureError, tr("DriveMode not supported.")); + return; + } + + m_driveMode = mode; +} + +int SimulatorCameraImageCaptureControl::capture(const QString &fileName) +{ + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureStillImage) { + emit error(0, QCameraImageCapture::NotReadyError, tr("Incorrect CaptureMode.")); + return 0; + } + updateReadyForCapture(false); + int imageId = m_session->captureImage(fileName); + updateReadyForCapture(true); + return imageId; +} + +void SimulatorCameraImageCaptureControl::cancelCapture() +{ +} + +void SimulatorCameraImageCaptureControl::updateReadyForCapture(bool ready) +{ + mReadyForCapture = ready; + emit readyForCaptureChanged(mReadyForCapture); +} + +// End of file diff --git a/src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.h b/src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.h new file mode 100644 index 000000000..e45483e72 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameraimagecapturecontrol.h @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMULATORCAMERAIMAGECAPTURECONTROL_H +#define SIMULATORCAMERAIMAGECAPTURECONTROL_H + +#include "qcameraimagecapturecontrol.h" + +QT_USE_NAMESPACE + +class SimulatorCameraService; +class SimulatorCameraSession; +class SimulatorCameraControl; + +/* + * Control for image capture operations. + */ +class SimulatorCameraImageCaptureControl : public QCameraImageCaptureControl +{ + Q_OBJECT + +public: // Contructors & Destrcutor + + SimulatorCameraImageCaptureControl(SimulatorCameraSession *session, SimulatorCameraService *service); + ~SimulatorCameraImageCaptureControl(); + +public: // QCameraImageCaptureControl + + bool isReadyForCapture() const; + + // Drive Mode + QCameraImageCapture::DriveMode driveMode() const; + void setDriveMode(QCameraImageCapture::DriveMode mode); + + // Capture + int capture(const QString &fileName); + void cancelCapture(); + +private: + void updateReadyForCapture(bool ready); + + bool mReadyForCapture; + SimulatorCameraSession *m_session; + SimulatorCameraService *m_service; + SimulatorCameraControl *m_cameraControl; + QCameraImageCapture::DriveMode m_driveMode; +}; + +#endif // SIMULATORCAMERAIMAGECAPTURECONTROL_H diff --git a/src/plugins/simulator/camera/simulatorcameraservice.cpp b/src/plugins/simulator/camera/simulatorcameraservice.cpp new file mode 100644 index 000000000..6eee2ec02 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameraservice.cpp @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "simulatorcameraservice.h" +#include "simulatorcamerasession.h" +#include "simulatorcameracontrol.h" +#include "simulatorcameraimagecapturecontrol.h" +#include "simulatorcameraexposurecontrol.h" + +#include "simulatorvideoinputdevicecontrol.h" +#include "simulatorvideorenderercontrol.h" +#include "../qsimulatormultimediaconnection_p.h" + +#include +#include + +QTM_USE_NAMESPACE; +using namespace Simulator; + +SimulatorCameraService::SimulatorCameraService(const QString &service, MultimediaConnection *multimediaConnection, + QObject *parent): + QMediaService(parent) +{ + Q_UNUSED(service) + mCaptureSession = new SimulatorCameraSession(this); + mCameraControl = new SimulatorCameraControl(mCaptureSession); + mVideoInputDeviceControl = new QSimulatorVideoInputDeviceControl(mCaptureSession); + mVideoInputDeviceControl->updateDeviceList(get_qtCameraData()); + mVideoRendererControl = new SimulatorVideoRendererControl(mCaptureSession, this); + mImageCaptureControl = new SimulatorCameraImageCaptureControl(mCaptureSession, this); + mExposureControl = new SimulatorCameraExposureControl(mCaptureSession, this); + + connect(multimediaConnection, SIGNAL(cameraDataChanged(QtMobility::QCameraData)), + SLOT(updateCameraData(QtMobility::QCameraData))); + connect(multimediaConnection, SIGNAL(cameraAdded(QString,QtMobility::QCameraData::QCameraDetails)), + mVideoInputDeviceControl, SLOT(addDevice(QString,QtMobility::QCameraData::QCameraDetails))); + connect(multimediaConnection, SIGNAL(cameraRemoved(QString)), + mVideoInputDeviceControl, SLOT(removeDevice(QString))); + connect(multimediaConnection, SIGNAL(cameraChanged(QString,QtMobility::QCameraData::QCameraDetails)), + mVideoInputDeviceControl, SLOT(changeDevice(QString,QtMobility::QCameraData::QCameraDetails))); + connect(multimediaConnection, SIGNAL(cameraChanged(QString,QtMobility::QCameraData::QCameraDetails)), + SLOT(changeCamera(QString,QtMobility::QCameraData::QCameraDetails))); + connect(mCameraControl, SIGNAL(startCamera()), + mVideoRendererControl, SLOT(showImage())); + connect(mCameraControl, SIGNAL(stopCamera()), + mVideoRendererControl, SLOT(stop())); + connect(mVideoInputDeviceControl, SIGNAL(selectedDeviceChanged(QString)), + SLOT(updateCameraPicture(QString))); + connect(mCaptureSession->settings(), SIGNAL(isoSensitivityChanged()), mVideoRendererControl, SLOT(showImage())); + connect(mCaptureSession->settings(), SIGNAL(apertureChanged()), mVideoRendererControl, SLOT(showImage())); + connect(mCaptureSession->settings(), SIGNAL(shutterSpeedChanged()), mVideoRendererControl, SLOT(showImage())); + connect(mCaptureSession->settings(), SIGNAL(exposureCompensationChanged()), mVideoRendererControl, SLOT(showImage())); + mCaptureSession->setImage(mVideoRendererControl->image()); + mVideoInputDeviceControl->setSelectedDevice(mVideoInputDeviceControl->defaultDevice()); +} + +SimulatorCameraService::~SimulatorCameraService() +{ +} + +QMediaControl *SimulatorCameraService::requestControl(const char *name) +{ + if (!mCaptureSession) + return 0; + + if (qstrcmp(name,QCameraControl_iid) == 0) + return mCameraControl; + + if (qstrcmp(name,QVideoDeviceControl_iid) == 0) + return mVideoInputDeviceControl; + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) + return mVideoRendererControl; + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return mImageCaptureControl; + + if (qstrcmp(name, QCameraExposureControl_iid) == 0) + return mExposureControl; + + return 0; +} + +void SimulatorCameraService::releaseControl(QMediaControl *control) +{ + Q_UNUSED(control) +} + +void SimulatorCameraService::updateCameraData(const QtMobility::QCameraData &data) +{ + mVideoInputDeviceControl->updateDeviceList(data); + QString currentDevice = mVideoInputDeviceControl->deviceName(mVideoInputDeviceControl->selectedDevice()); + if (!data.cameras.contains(currentDevice)) + return; + + updateCurrentDeviceImage(data.cameras.value(currentDevice).imagePath); +} + +void SimulatorCameraService::changeCamera(const QString &name, const QtMobility::QCameraData::QCameraDetails &details) +{ + QString currentDevice = mVideoInputDeviceControl->deviceName(mVideoInputDeviceControl->selectedDevice()); + if (currentDevice != name) + return; + + updateCurrentDeviceImage(details.imagePath); +} + +void SimulatorCameraService::updateCameraPicture(const QString &name) +{ + QtMobility::QCameraData data = QtMobility::get_qtCameraData(); + if (!data.cameras.contains(name)) + return; + + updateCurrentDeviceImage(data.cameras.value(name).imagePath); +} + +void SimulatorCameraService::updateCurrentDeviceImage(const QString &imagePath) +{ + mVideoRendererControl->setImagePath(imagePath); + mCaptureSession->setImage(mVideoRendererControl->image()); +} + +#include "moc_simulatorcameraservice.cpp" diff --git a/src/plugins/simulator/camera/simulatorcameraservice.h b/src/plugins/simulator/camera/simulatorcameraservice.h new file mode 100644 index 000000000..b159eb393 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcameraservice.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMULATORCAMERACAPTURESERVICE_H +#define SIMULATORCAMERACAPTURESERVICE_H + +#include + +#include "../qsimulatormultimediaconnection_p.h" + +QTM_BEGIN_NAMESPACE +namespace Simulator { +class MultimediaConnection; +} +QTM_END_NAMESPACE +class SimulatorCameraSession; +class SimulatorCameraControl; +class SimulatorCameraImageCaptureControl; +class SimulatorCameraExposureControl; +class SimulatorVideoRendererControl; +class QSimulatorVideoInputDeviceControl; + +class SimulatorCameraService : public QMediaService +{ + Q_OBJECT + +public: + SimulatorCameraService(const QString &service, QTM_PREPEND_NAMESPACE(Simulator::MultimediaConnection) *cameraConnection, + QObject *parent = 0); + virtual ~SimulatorCameraService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *); + +private slots: + void updateCameraData(const QtMobility::QCameraData &data); + void changeCamera(const QString &name, const QtMobility::QCameraData::QCameraDetails &details); + void updateCameraPicture(const QString &name); + +private: + void updateCurrentDeviceImage(const QString &imagePath); + SimulatorCameraSession *mCaptureSession; + SimulatorCameraControl *mCameraControl; + SimulatorCameraImageCaptureControl *mImageCaptureControl; + SimulatorCameraExposureControl *mExposureControl; + + QSimulatorVideoInputDeviceControl *mVideoInputDeviceControl; + + QMediaControl *mVideoOutput; + + SimulatorVideoRendererControl *mVideoRendererControl; +}; + +#endif // CAMERACAPTURESERVICE_H diff --git a/src/plugins/simulator/camera/simulatorcamerasession.cpp b/src/plugins/simulator/camera/simulatorcamerasession.cpp new file mode 100644 index 000000000..17dd2f762 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcamerasession.cpp @@ -0,0 +1,147 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#include "simulatorcamerasession.h" +#include "simulatorcamerasettings.h" +#include "../qsimulatormultimediaconnection_p.h" +#include +#include + +#include +#include +#include + +#include + +//#define CAMERA_DEBUG 1 + +SimulatorCameraSession::SimulatorCameraSession(QObject *parent) + :QObject(parent), + mViewfinder(0), + mImage(0), + mRequestId(0) +{ + mSettings = new SimulatorCameraSettings(this); +} + +SimulatorCameraSession::~SimulatorCameraSession() +{ +} + +int SimulatorCameraSession::captureImage(const QString &fileName) +{ + QTM_USE_NAMESPACE; + ++mRequestId; + emit imageExposed(mRequestId); + + QString actualFileName = fileName; + if (actualFileName.isEmpty()) { + actualFileName = generateFileName("img_", defaultDir(QCamera::CaptureStillImage), "jpg"); + } + + emit imageCaptured(mRequestId, *mImage); + + if (!mImage->save(actualFileName)) { + emit captureError(mRequestId, QCameraImageCapture::ResourceError, "Could not save file"); + return mRequestId; + } + emit imageSaved(mRequestId, actualFileName); + return mRequestId; +} + +void SimulatorCameraSession::setCaptureMode(QCamera::CaptureMode mode) +{ + mCaptureMode = mode; +} + +QDir SimulatorCameraSession::defaultDir(QCamera::CaptureMode) const +{ + const QString temp = QDir::tempPath(); + if (QFileInfo(temp).isWritable()) + return QDir(temp); + + return QDir(); +} + +QString SimulatorCameraSession::generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const +{ + int lastClip = 0; + foreach(QString fileName, dir.entryList(QStringList() << QString("%1*.%2").arg(prefix).arg(ext))) { + int imgNumber = fileName.mid(prefix.length(), fileName.size()-prefix.length()-ext.length()-1).toInt(); + lastClip = qMax(lastClip, imgNumber); + } + + QString name = QString("%1%2.%3").arg(prefix) + .arg(lastClip+1, + 4, //fieldWidth + 10, + QLatin1Char('0')) + .arg(ext); + + return dir.absoluteFilePath(name); +} + +void SimulatorCameraSession::setViewfinder(QObject *viewfinder) +{ + if (mViewfinder != viewfinder) { + mViewfinder = viewfinder; + emit viewfinderChanged(); + } +} + +QCamera::CaptureMode SimulatorCameraSession::captureMode() +{ + return mCaptureMode; +} + +void SimulatorCameraSession::setImage(const QImage *image) +{ + mImage = image; +} + +QObject *SimulatorCameraSession::viewfinder() const +{ + return mViewfinder; +} + +SimulatorCameraSettings *SimulatorCameraSession::settings() const +{ + return mSettings; +} diff --git a/src/plugins/simulator/camera/simulatorcamerasession.h b/src/plugins/simulator/camera/simulatorcamerasession.h new file mode 100644 index 000000000..6d1078e8d --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcamerasession.h @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMULATORCAMERACAPTURESESSION_H +#define SIMULATORCAMERACAPTURESESSION_H + +#include + +#include +#include + +#include "qcamera.h" + +class SimulatorCameraSettings; + +class SimulatorCameraSession : public QObject +{ + Q_OBJECT +public: + SimulatorCameraSession(QObject *parent); + ~SimulatorCameraSession(); + + QCamera::CaptureMode captureMode(); + void setCaptureMode(QCamera::CaptureMode mode); + + QDir defaultDir(QCamera::CaptureMode mode) const; + QString generateFileName(const QString &prefix, const QDir &dir, const QString &ext) const; + + void setImage(const QImage *image); + QObject *viewfinder() const; + void setViewfinder(QObject *viewfinder); + + int captureImage(const QString &fileName); + + SimulatorCameraSettings *settings() const; + +signals: + void stateChanged(QCamera::State state); + void captureError(int id, int error, const QString &errorString); + void error(int error, const QString &errorString); + void imageExposed(int requestId); + void imageCaptured(int requestId, const QImage &img); + void imageSaved(int requestId, const QString &fileName); + void viewfinderChanged(); + +private: + QCamera::CaptureMode mCaptureMode; + + QObject *mViewfinder; + const QImage *mImage; + + SimulatorCameraSettings *mSettings; + +public: + int mRequestId; +}; + +#endif // SIMULATORCAMERACAPTURESESSION_H diff --git a/src/plugins/simulator/camera/simulatorcamerasettings.cpp b/src/plugins/simulator/camera/simulatorcamerasettings.cpp new file mode 100644 index 000000000..dd617549b --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcamerasettings.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "simulatorcamerasettings.h" + +SimulatorCameraSettings::SimulatorCameraSettings(QObject *parent) + : QObject(parent) + , mIsoSensitivity(400) + , mAperture(4) + , mShutterSpeed(0.008) + , mExposureCompensation(0) +{ + mSupportedIsoSensitivities << 50 << 100 << 200 << 400 << 800; + mSupportedApertures << 1.8 << 2.8 << 4 << 5.6 << 8; + mSupportedShutterSpeeds << 0.002 << 0.004 << 0.008 << 1./60 << 1./30; + mSupportedExposureCompensations << -2 << 2; +} + +SimulatorCameraSettings::~SimulatorCameraSettings() +{ +} + +int SimulatorCameraSettings::isoSensitivity() const +{ + return mIsoSensitivity; +} + +QList SimulatorCameraSettings::supportedIsoSensitivities() const +{ + return mSupportedIsoSensitivities; +} + +void SimulatorCameraSettings::setManualIsoSensitivity(int iso) +{ + if (iso != mIsoSensitivity && supportedIsoSensitivities().contains(iso)) { + mIsoSensitivity = iso; + emit isoSensitivityChanged(); + } +} + +void SimulatorCameraSettings::setAutoIsoSensitivity() +{ + setManualIsoSensitivity(defaultIsoSensitivity()); +} + +qreal SimulatorCameraSettings::aperture() const +{ + return mAperture; +} + +QList SimulatorCameraSettings::supportedApertures() const +{ + return mSupportedApertures; +} + +void SimulatorCameraSettings::setManualAperture(qreal aperture) +{ + if (aperture != mAperture && supportedApertures().contains(aperture)) { + mAperture = aperture; + emit apertureChanged(); + } +} + +void SimulatorCameraSettings::setAutoAperture() +{ + setManualAperture(defaultAperture()); +} + +qreal SimulatorCameraSettings::shutterSpeed() const +{ + return mShutterSpeed; +} + +QList SimulatorCameraSettings::supportedShutterSpeeds() const +{ + return mSupportedShutterSpeeds; +} + +void SimulatorCameraSettings::setManualShutterSpeed(qreal speed) +{ + if (speed != mShutterSpeed && supportedShutterSpeeds().contains(speed)) { + mShutterSpeed = speed; + emit shutterSpeedChanged(); + } +} + +void SimulatorCameraSettings::setAutoShutterSpeed() +{ + setManualShutterSpeed(defaultShutterSpeed()); +} + +void SimulatorCameraSettings::setExposureCompensation(qreal ev) +{ + if (ev != mExposureCompensation && ev >= mSupportedExposureCompensations.first() + && ev <= mSupportedExposureCompensations.last()) { + mExposureCompensation = ev; + emit exposureCompensationChanged(); + } +} + +qreal SimulatorCameraSettings::exposureCompensation() const +{ + return mExposureCompensation; +} + +QList SimulatorCameraSettings::supportedExposureCompensationValues() const +{ + return mSupportedExposureCompensations; +} + +void SimulatorCameraSettings::setAutoExposureCompensation() +{ + setExposureCompensation(defaultExposureCompensation()); +} + +int SimulatorCameraSettings::defaultIsoSensitivity() const +{ + return 400; +} + +qreal SimulatorCameraSettings::defaultAperture() const +{ + return 4; +} + +qreal SimulatorCameraSettings::defaultShutterSpeed() const +{ + return 0.008; +} + +qreal SimulatorCameraSettings::defaultExposureCompensation() const +{ + return 0; +} + +// End of file diff --git a/src/plugins/simulator/camera/simulatorcamerasettings.h b/src/plugins/simulator/camera/simulatorcamerasettings.h new file mode 100644 index 000000000..173a11549 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorcamerasettings.h @@ -0,0 +1,110 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMULATORCAMERASETTINGS_H +#define SIMULATORCAMERASETTINGS_H + +#include "qcamera.h" + +QT_BEGIN_NAMESPACE + +class SimulatorCameraSettings : public QObject +{ + Q_OBJECT + +public: + SimulatorCameraSettings(QObject *parent); + ~SimulatorCameraSettings(); + + // ISO Sensitivity + int isoSensitivity() const; + int defaultIsoSensitivity() const; + void setManualIsoSensitivity(int iso); + void setAutoIsoSensitivity(); + QList supportedIsoSensitivities() const; + + // Aperture + qreal aperture() const; + qreal defaultAperture() const; + void setManualAperture(qreal aperture); + void setAutoAperture(); + QList supportedApertures() const; + + // Shutter Speed + qreal shutterSpeed() const; + qreal defaultShutterSpeed() const; + void setManualShutterSpeed(qreal speed); + void setAutoShutterSpeed(); + QList supportedShutterSpeeds() const; + + // ExposureCompensation + qreal exposureCompensation() const; + qreal defaultExposureCompensation() const; + void setExposureCompensation(qreal ev); + void setAutoExposureCompensation(); + QList supportedExposureCompensationValues() const; + +Q_SIGNALS: // Notifications + // For QCameraExposureControl + void flashReady(bool ready); + void apertureChanged(); + void apertureRangeChanged(); + void shutterSpeedChanged(); + void isoSensitivityChanged(); + void exposureCompensationChanged(); + + // Errors + void error(int, const QString&); + +private: // Data + int mIsoSensitivity; + QList mSupportedIsoSensitivities; + qreal mAperture; + QList mSupportedApertures; + qreal mShutterSpeed; + QList mSupportedShutterSpeeds; + qreal mExposureCompensation; + QList mSupportedExposureCompensations; +}; + +QT_END_NAMESPACE + +#endif // SIMULATORCAMERASETTINGS_H diff --git a/src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.cpp b/src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.cpp new file mode 100644 index 000000000..154777392 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.cpp @@ -0,0 +1,152 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "simulatorvideoinputdevicecontrol.h" + +#include +#include +#include +#include + +using namespace QTM_NAMESPACE; +QSimulatorVideoInputDeviceControl::QSimulatorVideoInputDeviceControl(QObject *parent) + : QVideoDeviceControl(parent) + , mSelectedDevice(-1) +{ +} + +QSimulatorVideoInputDeviceControl::~QSimulatorVideoInputDeviceControl() +{ +} + +int QSimulatorVideoInputDeviceControl::deviceCount() const +{ + return mDevices.count(); +} + +QString QSimulatorVideoInputDeviceControl::deviceName(int index) const +{ + if (index >= mDevices.count() || index < 0) + return QString(); + return mDevices.at(index); +} + +QString QSimulatorVideoInputDeviceControl::deviceDescription(int index) const +{ + if (index >= mDevices.count() || index < 0) + return QString(); + + return mDescriptions[index]; +} + +QIcon QSimulatorVideoInputDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index); + return QIcon(); +} + +int QSimulatorVideoInputDeviceControl::defaultDevice() const +{ + if (mDevices.isEmpty()) + return -1; + return 0; +} + +int QSimulatorVideoInputDeviceControl::selectedDevice() const +{ + return mSelectedDevice; +} + + +void QSimulatorVideoInputDeviceControl::setSelectedDevice(int index) +{ + if (index != mSelectedDevice) { + mSelectedDevice = index; + emit selectedDeviceChanged(index); + emit selectedDeviceChanged(deviceName(index)); + } +} + +void QSimulatorVideoInputDeviceControl::updateDeviceList(const QtMobility::QCameraData &data) +{ + mDevices.clear(); + mDescriptions.clear(); + QHashIterator iter(data.cameras); + while(iter.hasNext()) { + iter.next(); + mDevices.append(iter.key()); + mDescriptions.append(iter.value().description); + } + emit devicesChanged(); +} + +void QSimulatorVideoInputDeviceControl::addDevice(const QString &name, const QtMobility::QCameraData::QCameraDetails &details) +{ + if (mDevices.contains(name)) + return; + + mDevices.append(name); + mDescriptions.append(details.description); + emit devicesChanged(); +} + +void QSimulatorVideoInputDeviceControl::removeDevice(const QString &name) +{ + int index = mDevices.indexOf(name); + if (index == -1) + return; + + mDevices.removeAt(index); + mDescriptions.removeAt(index); + if (index == mSelectedDevice) + setSelectedDevice(defaultDevice()); + emit devicesChanged(); +} + +void QSimulatorVideoInputDeviceControl::changeDevice(const QString &name, const QtMobility::QCameraData::QCameraDetails &details) +{ + int index = mDevices.indexOf(name); + if (index == -1) + return; + + if (mDescriptions.at(index) != details.description) + mDescriptions[index] = details.description; +} diff --git a/src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.h b/src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.h new file mode 100644 index 000000000..cd5b7d04e --- /dev/null +++ b/src/plugins/simulator/camera/simulatorvideoinputdevicecontrol.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIMULATORVIDEOINPUTDEVICECONTROL_H +#define QSIMULATORVIDEOINPUTDEVICECONTROL_H + +#include +#include +#include + +#include "../qsimulatormultimediaconnection_p.h" + +QT_USE_NAMESPACE + +class QSimulatorVideoInputDeviceControl : public QVideoDeviceControl +{ +Q_OBJECT +public: + QSimulatorVideoInputDeviceControl(QObject *parent); + ~QSimulatorVideoInputDeviceControl(); + + int deviceCount() const; + + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + + int defaultDevice() const; + int selectedDevice() const; + + void updateDeviceList(const QtMobility::QCameraData &data); + +public Q_SLOTS: + void setSelectedDevice(int index); + void addDevice(const QString &name, const QtMobility::QCameraData::QCameraDetails &details); + void removeDevice(const QString &name); + void changeDevice(const QString &name, const QtMobility::QCameraData::QCameraDetails &details); + +private: + int mSelectedDevice; + QList mDevices; + QList mDescriptions; +}; + +#endif // QSIMULATORVIDEOINPUTDEVICECONTROL_H diff --git a/src/plugins/simulator/camera/simulatorvideorenderercontrol.cpp b/src/plugins/simulator/camera/simulatorvideorenderercontrol.cpp new file mode 100644 index 000000000..65cf38334 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorvideorenderercontrol.cpp @@ -0,0 +1,130 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "simulatorvideorenderercontrol.h" +#include "simulatorcamerasettings.h" +#include "simulatorcamerasession.h" +#include +#include +#include +#include +#include +#include + +SimulatorVideoRendererControl::SimulatorVideoRendererControl(SimulatorCameraSession *session, QObject *parent) : + QVideoRendererControl(parent) + , mSession(session) + , mSurface(0) + , mRunning(false) +{ +} + +SimulatorVideoRendererControl::~SimulatorVideoRendererControl() +{ +} + +QAbstractVideoSurface *SimulatorVideoRendererControl::surface() const +{ + return mSurface; +} + +void SimulatorVideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + mSurface = surface; +} + +void SimulatorVideoRendererControl::setImagePath(const QString &imagePath) +{ + if (QFile::exists(imagePath)) { + mImage = QImage(imagePath); + } else { + mImage = QImage(800, 600, QImage::Format_RGB32); + mImage.fill(qRgb(200, 50, 50)); + QPainter painter(&mImage); + painter.drawText(0, 0, 800, 600, Qt::AlignCenter, imagePath); + } + if (mRunning) + showImage(); +} + +void SimulatorVideoRendererControl::showImage() +{ + if (!mSurface) + return; + stop(); + mShownImage = mImage; + SimulatorCameraSettings *settings = mSession->settings(); + qreal colorDiff = .0; + colorDiff += settings->aperture() - settings->defaultAperture(); + colorDiff += (settings->shutterSpeed() - settings->defaultShutterSpeed()) * 400; + colorDiff += ((qreal)settings->isoSensitivity() - settings->defaultIsoSensitivity()) / 100; + colorDiff += settings->exposureCompensation(); + int diffToApply = qAbs(colorDiff * 20); + QPainter painter(&mShownImage); + if (colorDiff < 0) + painter.fillRect(mShownImage.rect(), QColor(0, 0, 0, qMin(diffToApply, 255))); + else + painter.fillRect(mShownImage.rect(), QColor(255, 255, 255, qMin(diffToApply, 255))); + QVideoFrame::PixelFormat pixelFormat = QVideoFrame::pixelFormatFromImageFormat(mShownImage.format()); + if (pixelFormat == QVideoFrame::Format_Invalid) { + mShownImage = mShownImage.convertToFormat(QImage::Format_RGB32); + pixelFormat = QVideoFrame::Format_RGB32; + } + QVideoSurfaceFormat format(mShownImage.size(), pixelFormat); + mSurface->start(format); + mSurface->present(mShownImage); + mRunning = true; +} + +void SimulatorVideoRendererControl::stop() +{ + if (!mSurface) + return; + + if (mSurface->isActive()) + mSurface->stop(); + mRunning = false; +} + +const QImage * SimulatorVideoRendererControl::image() const +{ + return &mShownImage; +} diff --git a/src/plugins/simulator/camera/simulatorvideorenderercontrol.h b/src/plugins/simulator/camera/simulatorvideorenderercontrol.h new file mode 100644 index 000000000..3085ad079 --- /dev/null +++ b/src/plugins/simulator/camera/simulatorvideorenderercontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIMULATORVIDEORENDERERCONTROL_H +#define QSIMULATORVIDEORENDERERCONTROL_H + +#include +#include + + +class SimulatorCameraSession; +/* + * Control for QGraphicsVideoItem. Viewfinder frames are streamed to a surface + * which is drawn to the display by the Qt Graphics Vide Framework. + */ +class SimulatorVideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT + +public: + SimulatorVideoRendererControl(SimulatorCameraSession *session, QObject *parent = 0); + virtual ~SimulatorVideoRendererControl(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + + void setImagePath(const QString &imagePath); + const QImage *image() const; + +public slots: + void showImage(); + +private slots: + void stop(); + +private: + SimulatorCameraSession *mSession; + QAbstractVideoSurface *mSurface; + QImage mImage; + QImage mShownImage; + bool mRunning; +}; + +#endif // QSIMULATORVIDEORENDERERCONTROL_H diff --git a/src/plugins/simulator/qsimulatormultimediaconnection.cpp b/src/plugins/simulator/qsimulatormultimediaconnection.cpp new file mode 100644 index 000000000..ff9e0508e --- /dev/null +++ b/src/plugins/simulator/qsimulatormultimediaconnection.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "../qsimulatormultimediaconnection_p.h" +#include "mobilitysimulatorglobal.h" +#include + +#include + +#include + +QTM_BEGIN_NAMESPACE + +using namespace QtSimulatorPrivate; + +Q_GLOBAL_STATIC(QCameraData, qtCameraData) + +namespace Simulator +{ + MultimediaConnection::MultimediaConnection(MobilityConnection *mobilityCon) + : QObject(mobilityCon) + , mConnection(mobilityCon) + , mInitialDataReceived(false) + { + qt_registerCameraTypes(); + mobilityCon->addMessageHandler(this); + } + + void MultimediaConnection::getInitialData() + { + RemoteMetacall::call(mConnection->sendSocket(), NoSync, "setRequestsCameras"); + + while (!mInitialDataReceived) + mConnection->receiveSocket()->waitForReadyRead(100); + } + + void MultimediaConnection::initialCameraDataSent() + { + mInitialDataReceived = true; + } + + void MultimediaConnection::setCameraData(const QCameraData &data) + { + *qtCameraData() = data; + emit cameraDataChanged(data); + } + + void MultimediaConnection::addCamera(const QString &name, const QCameraData::QCameraDetails &details) + { + if (qtCameraData()->cameras.contains(name)) + return; + + qtCameraData()->cameras.insert(name, details); + emit cameraAdded(name, details); + } + + void MultimediaConnection::removeCamera(const QString &name) + { + if (!qtCameraData()->cameras.contains(name)) + return; + + qtCameraData()->cameras.remove(name); + emit cameraRemoved(name); + } + + void MultimediaConnection::changeCamera(const QString &name, const QCameraData::QCameraDetails &details) + { + if (!qtCameraData()->cameras.contains(name)) + return; + + qtCameraData()->cameras[name] = details; + emit cameraChanged(name, details); + } + +} // namespace + +QCameraData get_qtCameraData() +{ + return *qtCameraData(); +} + +#include "moc_qsimulatormultimediaconnection_p.cpp" + +QTM_END_NAMESPACE diff --git a/src/plugins/simulator/qsimulatormultimediaconnection_p.h b/src/plugins/simulator/qsimulatormultimediaconnection_p.h new file mode 100644 index 000000000..752c0bfcb --- /dev/null +++ b/src/plugins/simulator/qsimulatormultimediaconnection_p.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef SIMULATORMULTIMEDIACONNECTION_H +#define SIMULATORMULTIMEDIACONNECTION_H + +#include "qsimulatormultimediadata_p.h" + +QT_BEGIN_HEADER + +QTM_BEGIN_NAMESPACE + +namespace Simulator +{ + class MobilityConnection; + + class MultimediaConnection : public QObject + { + Q_OBJECT + public: + MultimediaConnection (MobilityConnection *mobilityCon); + virtual ~MultimediaConnection () {} + + void getInitialData(); + + private slots: + void setCameraData(const QtMobility::QCameraData &); + void addCamera(const QString &, const QtMobility::QCameraData::QCameraDetails &); + void removeCamera(const QString &); + void changeCamera(const QString &, const QtMobility::QCameraData::QCameraDetails &); + void initialCameraDataSent(); + + private: + MobilityConnection *mConnection; + bool mInitialDataReceived; + + signals: + void cameraDataChanged(const QtMobility::QCameraData &data); + void cameraAdded(const QString &name, const QtMobility::QCameraData::QCameraDetails &details); + void cameraRemoved(const QString &name); + void cameraChanged(const QString &name, const QtMobility::QCameraData::QCameraDetails &details); + }; +} // end namespace Simulator + +QCameraData get_qtCameraData(); + +QTM_END_NAMESPACE +QT_END_HEADER + +#endif + diff --git a/src/plugins/simulator/qsimulatormultimediadata.cpp b/src/plugins/simulator/qsimulatormultimediadata.cpp new file mode 100644 index 000000000..4985bf5d1 --- /dev/null +++ b/src/plugins/simulator/qsimulatormultimediadata.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qsimulatormultimediadata_p.h" + +#include + +QTM_BEGIN_NAMESPACE + +void qt_registerCameraTypes() +{ + qRegisterMetaTypeStreamOperators("QtMobility::QCameraData::QCameraDetails"); + qRegisterMetaTypeStreamOperators("QtMobility::QCameraData"); +} + +QDataStream &operator<<(QDataStream &out, const QCameraData &s) +{ + out << s.cameras; + return out; +} + +QDataStream &operator>>(QDataStream &in, QCameraData &s) +{ + in >> s.cameras; + return in; +} + +QDataStream &operator<<(QDataStream &out, const QCameraData::QCameraDetails &s) +{ + out << s.description << s.imagePath; + return out; +} + +QDataStream &operator>>(QDataStream &in, QCameraData::QCameraDetails &s) +{ + in >> s.description >> s.imagePath; + return in; +} + +QTM_END_NAMESPACE diff --git a/src/plugins/simulator/qsimulatormultimediadata_p.h b/src/plugins/simulator/qsimulatormultimediadata_p.h new file mode 100644 index 000000000..e0fcb046a --- /dev/null +++ b/src/plugins/simulator/qsimulatormultimediadata_p.h @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QSIMULATORMULTIMEDIADATA_P_H +#define QSIMULATORMULTIMEDIADATA_P_H + +// +// W A R N I N G +// ------------- +// +// This file is not part of the Qt API. It exists purely as an +// implementation detail. This header file may change from version to +// version without notice, or even be removed. +// +// We mean it. +// + +#include "qmobilityglobal.h" +#include +#include +#include + +QT_BEGIN_HEADER +QTM_BEGIN_NAMESPACE + +struct QCameraData { + struct QCameraDetails { + QString description; + QString imagePath; + }; + QHash cameras; +}; + +void qt_registerCameraTypes(); + +QTM_END_NAMESPACE + +Q_DECLARE_METATYPE(QtMobility::QCameraData) +Q_DECLARE_METATYPE(QtMobility::QCameraData::QCameraDetails) + +QT_END_HEADER + +#endif // QMULTIMEDIADATA_SIMULATOR_P_H diff --git a/src/plugins/simulator/qsimulatorserviceplugin.cpp b/src/plugins/simulator/qsimulatorserviceplugin.cpp new file mode 100644 index 000000000..2039276ba --- /dev/null +++ b/src/plugins/simulator/qsimulatorserviceplugin.cpp @@ -0,0 +1,127 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "qsimulatorserviceplugin.h" +#include + +#include "simulatorcameraservice.h" + +#include + +#include +#include +#include +#include + +QTM_USE_NAMESPACE + +Simulator::MultimediaConnection *QSimulatorServicePlugin::mMultimediaConnection = 0; + +QSimulatorServicePlugin::QSimulatorServicePlugin() +{ + ensureSimulatorConnection(); +} + +QStringList QSimulatorServicePlugin::keys() const +{ + QStringList retList; + retList << QLatin1String(Q_MEDIASERVICE_CAMERA); + return retList; +} + +QMediaService* QSimulatorServicePlugin::create(const QString &key) +{ + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new SimulatorCameraService(key, mMultimediaConnection); + + qWarning() << "Simulator service plugin: unsupported key:" << key; + return 0; +} + +void QSimulatorServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList QSimulatorServicePlugin::devices(const QByteArray &service) const +{ + if (service == Q_MEDIASERVICE_CAMERA) { + QCameraData cams = get_qtCameraData(); + QList returnList; + foreach(const QString &key, cams.cameras.keys()) + returnList.append(key.toAscii()); + return returnList; + } + + return QList(); +} + +QString QSimulatorServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + if (service == Q_MEDIASERVICE_CAMERA) { + QCameraData cams = get_qtCameraData(); + return cams.cameras.value(device).description; + } + + return QString(); +} + +void QSimulatorServicePlugin::ensureSimulatorConnection() +{ + using namespace QtMobility::Simulator; + + static bool connected = false; + if (connected) + return; + + connected = true; + MobilityConnection *connection = MobilityConnection::instance(); + mMultimediaConnection = new MultimediaConnection(connection); + mMultimediaConnection->getInitialData(); +} + +Q_EXPORT_PLUGIN2(qtmedia_simulatorengine, QSimulatorServicePlugin); diff --git a/src/plugins/simulator/qsimulatorserviceplugin.h b/src/plugins/simulator/qsimulatorserviceplugin.h new file mode 100644 index 000000000..02e4d6321 --- /dev/null +++ b/src/plugins/simulator/qsimulatorserviceplugin.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QSIMULATORSERVICEPLUGIN_H +#define QSIMULATORSERVICEPLUGIN_H + +#include +#include "qsimulatormultimediaconnection_p.h" + +QT_USE_NAMESPACE + +class QSimulatorServicePlugin : public QMediaServiceProviderPlugin, public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) +public: + QSimulatorServicePlugin(); + + // QMediaServiceProviderPlugin + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + // QMediaServiceSupportedDevicesInterface + QList devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: + static void ensureSimulatorConnection(); + static QTM_PREPEND_NAMESPACE(Simulator::MultimediaConnection) *mMultimediaConnection; + +signals: + void cameraDataChanged(); +}; + +#endif // QSIMULATORSERVICEPLUGIN_H diff --git a/src/plugins/simulator/simulator.pro b/src/plugins/simulator/simulator.pro new file mode 100644 index 000000000..81117ca82 --- /dev/null +++ b/src/plugins/simulator/simulator.pro @@ -0,0 +1,28 @@ +TEMPLATE = lib +CONFIG += plugin +TARGET = $$qtLibraryTarget(qsimulatorengine) +PLUGIN_TYPE=mediaservice + +include(../../../common.pri) +INCLUDEPATH+=$${SOURCE_DIR}/src/multimedia \ + $${SOURCE_DIR}/src/multimedia/video \ + $${SOURCE_DIR}/src/multimedia/audio \ + $${SOURCE_DIR}/src/mobilitysimulator + +CONFIG += mobility +MOBILITY = multimedia + +DEPENDPATH += . + +# Input +HEADERS += \ + qsimulatormultimediaconnection_p.h \ + qsimulatormultimediadata_p.h \ + qsimulatorserviceplugin.h + +SOURCES += \ + qsimulatormultimediaconnection.cpp \ + qsimulatormultimediadata.cpp \ + qsimulatorserviceplugin.cpp \ + +include(camera/simulatorcamera.pri) diff --git a/src/plugins/symbian/ecam/camera_s60.pri b/src/plugins/symbian/ecam/camera_s60.pri new file mode 100644 index 000000000..beb44db8d --- /dev/null +++ b/src/plugins/symbian/ecam/camera_s60.pri @@ -0,0 +1,157 @@ +INCLUDEPATH += $$PWD + +include (../videooutput/videooutput.pri) + +# Camera Service +DEFINES += QMEDIA_SYMBIAN_CAMERA + +# S60 3.1 platform +contains(S60_VERSION, 3.1) { + DEFINES += S60_31_PLATFORM + DEFINES *= S60_3X_PLATFORM +} + +# S60 3.2 platform +contains(S60_VERSION, 3.2) { + DEFINES += S60_32_PLATFORM + DEFINES *= S60_3X_PLATFORM +} + +# S60 5.0 platform +!contains(DEFINES, S60_31_PLATFORM) { + !contains(DEFINES, S60_32_PLATFORM) { + !contains(DEFINES, SYMBIAN_3_PLATFORM) { + DEFINES += S60_50_PLATFORM + } + } +} + +# Symbian 3 platform +contains(DEFINES, VIDEOOUTPUT_GRAPHICS_SURFACES) { + DEFINES += SYMBIAN_3_PLATFORM +} + +# AutoFocusing (CamAutoFocus) from ForumNokia example +contains(symbian_camera_camautofocus_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\CCamAutoFocus.h) { + message ("CameraBE: Using S60 3.1 autofocusing") + MMP_RULES += \ + "$${LITERAL_HASH}ifdef WINSCW" \ + "LIBRARY camautofocus.lib" \ + "$${LITERAL_HASH}else" \ + "STATICLIBRARY camautofocus_s.lib" \ + "$${LITERAL_HASH}endif // WINS" \ + "MACRO S60_CAM_AUTOFOCUS_SUPPORT" + } +} + +# ECam AdvancedSettings +contains(symbian_camera_ecamadvsettings_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\ecamadvancedsettings.h) { + MMP_RULES += \ + "$${LITERAL_HASH}ifndef WINSCW" \ + "LIBRARY ecamadvsettings.lib" \ + "MACRO USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER" \ + "$${LITERAL_HASH}endif" + message("CameraBE: Using from S60 3.2 CCameraAdvancedSettings header") + } + exists($${EPOCROOT}epoc32\\include\\ecamadvsettings.h) { + symbian:LIBS += -lecamadvsettings + DEFINES += USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + message("CameraBE: Using CCameraAdvancedSettings header from S60 5.0 or later") + } +} + +# DevVideo API Check (Requires both, DevVideoPlay and DevVideoRecord plugins): +# DevVideoConstants has been problematic since not being included in SDK plugins +# For S60 5.0 this has changed with plugin extension 1.1 +# But for S60 3.2 this is still a problem +contains(symbian_camera_devvideorecord_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\mmf\\devvideo\\devvideorecord.h) { + exists($${EPOCROOT}epoc32\\include\\mmf\\devvideo\\devvideobase.h) { + exists($${EPOCROOT}epoc32\\include\\mmf\\devvideo\\devvideoconstants.h) { + symbian:LIBS += -ldevvideo + DEFINES += S60_DEVVIDEO_RECORDING_SUPPORTED + message("CameraBE: Devvideo API supported") + } + } + } +} + +# ECam Snapshot API: +contains(symbian_camera_snapshot_enabled, yes) { + exists($${EPOCROOT}epoc32\\include\\platform\\ecam\\camerasnapshot.h) { + DEFINES += ECAM_PREVIEW_API + message("CameraBE: Using CCameraSnapshot API") + symbian:LIBS += -lecamsnapshot + } else { + message("CameraBE: Using custom snapshot proving methods") + } +} else { + message("CameraBE: Using custom snapshot proving methods") +} + +# Libraries: +symbian:LIBS += -lfbscli \ + -lmediaclientvideo \ + -lecam \ + -lbafl \ + -lPlatformEnv \ + -lcharconv \ + -lconvnames \ + -lgb2312_shared \ + -ljisx0201 \ + -ljisx0208 \ + -lmmfcontrollerframework \ + -lfbscli \ + -lefsrv \ + -lcone \ + -lws32 \ + -limageconversion + +# Source: +HEADERS += $$PWD/s60cameraconstants.h \ + $$PWD/s60cameralockscontrol.h \ + $$PWD/s60camerafocuscontrol.h \ + $$PWD/s60cameraexposurecontrol.h \ + $$PWD/s60cameraflashcontrol.h \ + $$PWD/s60cameracontrol.h \ + $$PWD/s60mediarecordercontrol.h \ + $$PWD/s60videocapturesession.h \ + $$PWD/s60imagecapturesession.h \ + $$PWD/s60mediacontainercontrol.h \ + $$PWD/s60videoencodercontrol.h \ + $$PWD/s60audioencodercontrol.h \ + $$PWD/s60cameraservice.h \ + $$PWD/s60cameraimageprocessingcontrol.h \ + $$PWD/s60cameraimagecapturecontrol.h \ + $$PWD/s60videodevicecontrol.h \ + $$PWD/s60imageencodercontrol.h \ + $$PWD/s60camerasettings.h \ + $$PWD/s60cameraengine.h \ + $$PWD/s60cameraviewfinderengine.h \ + $$PWD/s60cameraengineobserver.h \ + $$PWD/s60videorenderercontrol.h + +SOURCES += $$PWD/s60cameralockscontrol.cpp \ + $$PWD/s60camerafocuscontrol.cpp \ + $$PWD/s60cameraexposurecontrol.cpp \ + $$PWD/s60cameraflashcontrol.cpp \ + $$PWD/s60cameracontrol.cpp \ + $$PWD/s60mediarecordercontrol.cpp \ + $$PWD/s60videocapturesession.cpp \ + $$PWD/s60imagecapturesession.cpp \ + $$PWD/s60mediacontainercontrol.cpp \ + $$PWD/s60videoencodercontrol.cpp \ + $$PWD/s60audioencodercontrol.cpp \ + $$PWD/s60cameraservice.cpp \ + $$PWD/s60cameraimageprocessingcontrol.cpp \ + $$PWD/s60cameraimagecapturecontrol.cpp \ + $$PWD/s60videodevicecontrol.cpp \ + $$PWD/s60imageencodercontrol.cpp \ + $$PWD/s60camerasettings.cpp \ + $$PWD/s60cameraengine.cpp \ + $$PWD/s60cameraviewfinderengine.cpp \ + $$PWD/s60videorenderercontrol.cpp + +# End of file diff --git a/src/plugins/symbian/ecam/ecam.pro b/src/plugins/symbian/ecam/ecam.pro new file mode 100644 index 000000000..31f30b61c --- /dev/null +++ b/src/plugins/symbian/ecam/ecam.pro @@ -0,0 +1,40 @@ +###################################################################### +# +# Mobility API project - Symbian Camera backend +# +###################################################################### + +TEMPLATE = lib +CONFIG += plugin + +TARGET = $$qtLibraryTarget(qtmultimediakit_ecamengine) +PLUGIN_TYPE = mediaservice +include (../../../../common.pri) + +CONFIG += mobility +MOBILITY += multimedia + +# Include here so that all defines are added here also +include(camera_s60.pri) + +DEPENDPATH += . + +INCLUDEPATH += . \ + $${SOURCE_DIR}/include \ + $${SOURCE_DIR}/src/multimedia \ + $${SOURCE_DIR}/src/multimedia/audio \ + $${SOURCE_DIR}/src/multimedia/video \ + $${SOURCE_DIR} + +HEADERS += s60cameraserviceplugin.h +SOURCES += s60cameraserviceplugin.cpp + +load(data_caging_paths) +TARGET.EPOCALLOWDLLDATA = 1 +TARGET.UID3 = 0x2002BFC2 +TARGET.CAPABILITY = ALL -TCB + +# Make a sis package from plugin + api + stub (plugin) +pluginDep.sources = $${TARGET}.dll +pluginDep.path = $${QT_PLUGINS_BASE_DIR}/$${PLUGIN_TYPE} +DEPLOYMENT += pluginDep diff --git a/src/plugins/symbian/ecam/s60audioencodercontrol.cpp b/src/plugins/symbian/ecam/s60audioencodercontrol.cpp new file mode 100644 index 000000000..bd8f0147d --- /dev/null +++ b/src/plugins/symbian/ecam/s60audioencodercontrol.cpp @@ -0,0 +1,159 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60audioencodercontrol.h" +#include "s60videocapturesession.h" + +S60AudioEncoderControl::S60AudioEncoderControl(QObject *parent) : + QAudioEncoderControl(parent) +{ +} + +S60AudioEncoderControl::S60AudioEncoderControl(S60VideoCaptureSession *session, QObject *parent) : + QAudioEncoderControl(parent) +{ + m_session = session; +} + +S60AudioEncoderControl::~S60AudioEncoderControl() +{ +} + +QStringList S60AudioEncoderControl::supportedAudioCodecs() const +{ + return m_session->supportedAudioCaptureCodecs(); +} + +QString S60AudioEncoderControl::codecDescription(const QString &codecName) const +{ + // According to ForumNokia MMF camcorder plugin supports AAC, AMR and QCELP + // QCELP is speech codec and can be discarded + if (qstrcmp(codecName.toLocal8Bit().constData(), "audio/aac") == 0) + return QLatin1String("Advanced Audio Coding"); + else if (qstrcmp(codecName.toLocal8Bit().constData(), "audio/amr") == 0) + return QLatin1String("Adaptive Multi-Rate Audio Codec"); + + return QString(); +} + +QStringList S60AudioEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + // Possible settings: EncodingMode, Codec, BitRate, ChannelCount, SampleRate, Quality + // Possible (codec specific) Options: None + Q_UNUSED(codec); + return QStringList(); +} + +QVariant S60AudioEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + // Possible settings: EncodingMode, Codec, BitRate, ChannelCount, SampleRate, Quality + // Possible (codec specific) Options: None + Q_UNUSED(codec); + Q_UNUSED(name); + return QVariant(); +} + +void S60AudioEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + m_session->setError(KErrNotSupported, tr("Audio encoding option is not supported")); + + // The audio settings can currently be set only using setAudioSettings() function + Q_UNUSED(value) + Q_UNUSED(codec) + Q_UNUSED(name) +} + +QList S60AudioEncoderControl::supportedSampleRates( + const QAudioEncoderSettings &settings, bool *continuous) const +{ + return m_session->supportedSampleRates(settings, continuous); +} + +QAudioEncoderSettings S60AudioEncoderControl::audioSettings() const +{ + QAudioEncoderSettings settings; + m_session->audioEncoderSettings(settings); + + return settings; +} + +void S60AudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings) +{ + // Notify that settings have been implicitly set and there's no need to + // initialize them in case camera is changed + m_session->notifySettingsSet(); + + // Quality defines SampleRate/BitRate combination if either or both are missing + if (settings.codec().isEmpty()) { // Empty settings + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EOnlyAudioQuality); + + } else if (settings.bitRate() == -1 && settings.sampleRate() != -1) { // Only SampleRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioSampleRate(settings.sampleRate()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EAudioQualityAndSampleRate); + + } else if (settings.bitRate() != -1 && settings.sampleRate() == -1) { // Only BitRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioBitRate(settings.bitRate()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EAudioQualityAndBitRate); + + } else if (settings.bitRate() == -1 && settings.sampleRate() == -1) { // No BitRate or SampleRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::EOnlyAudioQuality); + + } else { // Both SampleRate and BitRate set + m_session->setAudioCaptureCodec(settings.codec()); + m_session->setAudioChannelCount(settings.channelCount()); + m_session->setAudioSampleRate(settings.sampleRate()); + m_session->setAudioBitRate(settings.bitRate()); + m_session->setAudioEncodingMode(settings.encodingMode()); + m_session->setAudioCaptureQuality(settings.quality(), S60VideoCaptureSession::ENoAudioQuality); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60audioencodercontrol.h b/src/plugins/symbian/ecam/s60audioencodercontrol.h new file mode 100644 index 000000000..db6ada5d1 --- /dev/null +++ b/src/plugins/symbian/ecam/s60audioencodercontrol.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOENCODERCONTROL_H +#define S60AUDIOENCODERCONTROL_H + +#include +#include + +#include + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; + +/* + * Control for audio settings when recording video using QMediaRecorder. + */ +class S60AudioEncoderControl : public QAudioEncoderControl +{ + Q_OBJECT + +public: // Constructor & Destructor + + S60AudioEncoderControl(QObject *parent = 0); + S60AudioEncoderControl(S60VideoCaptureSession *session, QObject *parent = 0); + virtual ~S60AudioEncoderControl(); + +public: // QAudioEncoderControl + + // Audio Codec + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + + // Sample Rate + QList supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const; + + // Audio Settings + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings &settings); + + // Encoding Option + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: // Data + + S60VideoCaptureSession* m_session; +}; + +#endif // S60AUDIOENCODERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraconstants.h b/src/plugins/symbian/ecam/s60cameraconstants.h new file mode 100644 index 000000000..4b415c3e8 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraconstants.h @@ -0,0 +1,257 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERACONSTANTS_H +#define S60CAMERACONSTANTS_H + +//============================================================================= + +// GENERAL SETTINGS + +#define KDefaultCameraDevice 0 +#define KECamCameraPriority 0 +#define KInactivityTimerTimeout 30000 // msec +#define KSymbianFineResolutionFactor 100.0 +#define KDefaultOpticalZoom 1.0 +#define KDefaultDigitalZoom 1.0 +#define KSmoothZoomStep 1 +#define KDefaultFocusMode QCameraFocus::AutoFocus + +#define KDefaultViewfinderSize QSize(320,240) +#define KDefaultSizePreview_Normal TSize(640,480) +#define KDefaultSizePreview_Wide TSize(640,360) +#define KDefaultSizePreview_CIF TSize(352,288) +#define KDefaultSizePreview_PAL TSize(640,512) +#define KDefaultSizePreview_NTSC TSize(640,426) +#define KDefaultFormatPreview CCamera::EFormatFbsBitmapColor16MU +#define KViewfinderFrameRate 30 +#define KMaxVFErrorsSignalled 3 + +//============================================================================= + +// IMAGE SETTINGS + +#define KDefaultImagePath QLatin1String("c:\\Data\\Images") +#define KDefaultImageFileName QLatin1String("image.jpg") +#define KDefaultImageCodec QLatin1String("image/jpeg") +#define KDefaultImageFormatPrimaryCam CCamera::EFormatExif +#ifdef SYMBIAN_3_PLATFORM +#define KDefaultImageFormatSecondaryCam CCamera::EFormatExif +#define KDefaultImageResolution QSize(3264, 2448) +#else // Pre-Symbian3 Platforms +#define KDefaultImageFormatSecondaryCam CCamera::EFormatFbsBitmapColor64K +#define KDefaultImageResolution QSize(2048, 1536) +#endif // SYMBIAN_3_PLATFORM +#define KSymbianImageQualityCoefficient 25 +// This must be divisible by 4 and creater or equal to 8 +#define KSnapshotDownScaleFactor 8 +#define KSnapshotMinWidth 640 +#define KSnapshotMinHeight 360 +#define KJpegQualityVeryLow 40 +#define KJpegQualityLow 50 +#define KJpegQualityNormal 75 +#define KJpegQualityHigh 85 +#define KJpegQualityVeryHigh 95 +#define KDefaultImageQuality KJpegQualityHigh + +//============================================================================= + +// VIDEO SETTINGS + +// ================ +// General settings +// ================ + +// Dummy file name to execute CVideoRecorderUtility::OpenFileL() without +// knowing the actual outputLocation. This is needed to be able to query/set +// supported video settings. +_LIT(KDummyVideoFile, "c:\\data\\temp"); + +// Default container MIME type +#define KMimeTypeDefaultContainer QLatin1String("video/mp4") +#define KDefaultVideoPath QLatin1String("c:\\Data\\Videos") +#define KDefaultVideoFileName QLatin1String("video.mp4") +#define KDurationChangedInterval 1000 // 1 second + +// ============== +// Audio Settings +// ============== + +// Default audio codec MIME type +#define KMimeTypeDefaultAudioCodec QLatin1String("audio/aac") + +// Default audio settings for video recording +#define KDefaultChannelCount -1 // Not Supported on Symbian +#define KDefaultBitRate 32000 // 32kbps +#define KDefaultSampleRate -1 // Not Supported on Symbian + +// ============== +// Video Settings +// ============== + +// Default video codec MIME type +#ifdef SYMBIAN_3_PLATFORM + // H.264: BaselineProfile Level 3.1, Max resolution: 1280x720 + #define KMimeTypeDefaultVideoCodec QLatin1String("video/H264; profile-level-id=42801F") +#else + // MPEG-4: Simple Profile, Level 4, Max resolution: 640x480 + #define KMimeTypeDefaultVideoCodec QLatin1String("video/mp4v-es; profile-level-id=4") +#endif + +// Maximum resolutions for encoder MIME Types +// H.263 +#define KResH263 QSize(176,144); +#define KResH263_Profile0 QSize(176,144); +#define KResH263_Profile0_Level10 QSize(176,144); +#define KResH263_Profile0_Level20 QSize(352,288); +#define KResH263_Profile0_Level30 QSize(352,288); +#define KResH263_Profile0_Level40 QSize(352,288); +#define KResH263_Profile0_Level45 QSize(176,144); +#define KResH263_Profile0_Level50 QSize(352,288); +#define KResH263_Profile3 QSize(176,144); +// MPEG-4 +#define KResMPEG4 QSize(176,144); +#define KResMPEG4_PLID_1 QSize(176,144); +#define KResMPEG4_PLID_2 QSize(352,288); +#define KResMPEG4_PLID_3 QSize(352,288); +#define KResMPEG4_PLID_4 QSize(640,480); +#define KResMPEG4_PLID_5 QSize(720,576); +#define KResMPEG4_PLID_6 QSize(1280,720); +#define KResMPEG4_PLID_8 QSize(176,144); +#define KResMPEG4_PLID_9 QSize(176,144); +// H.264 (Baseline Profile, same resolutions apply to Main and High Profile) +#define KResH264 QSize(176,144); +#define KResH264_PLID_42800A QSize(176,144); +#define KResH264_PLID_42900B QSize(176,144); +#define KResH264_PLID_42800B QSize(352,288); +#define KResH264_PLID_42800C QSize(352,288); +#define KResH264_PLID_42800D QSize(352,288); +#define KResH264_PLID_428014 QSize(352,288); +#define KResH264_PLID_428015 QSize(352,288); +#define KResH264_PLID_428016 QSize(640,480); +#define KResH264_PLID_42801E QSize(640,480); +#define KResH264_PLID_42801F QSize(1280,720); +#define KResH264_PLID_428020 QSize(1280,720); +#define KResH264_PLID_428028 QSize(1920,1080); + +// Maximum framerates for encoder MIME Types +// H.263 +#define KFrR_H263 qreal(15); +#define KFrR_H263_Profile0 qreal(15); +#define KFrR_H263_Profile0_Level10 qreal(15); +#define KFrR_H263_Profile0_Level20 qreal(15); +#define KFrR_H263_Profile0_Level30 qreal(30); +#define KFrR_H263_Profile0_Level40 qreal(30); +#define KFrR_H263_Profile0_Level45 qreal(15); +#define KFrR_H263_Profile0_Level50 qreal(15); +#define KFrR_H263_Profile3 qreal(15); +// MPEG-4 +#define KFrR_MPEG4 qreal(15); +#define KFrR_MPEG4_PLID_1 qreal(15); +#define KFrR_MPEG4_PLID_2 qreal(15); +#define KFrR_MPEG4_PLID_3 qreal(30); +// This is a workaround for a known platform bug +#if (defined(S60_31_PLATFORM) | defined(S60_32_PLATFORM)) +#define KFrR_MPEG4_PLID_4 qreal(15); +#else // All other platforms +#define KFrR_MPEG4_PLID_4 qreal(30); +#endif // S60 3.1 or 3.2 +#define KFrR_MPEG4_PLID_5 qreal(30); +#define KFrR_MPEG4_PLID_6 qreal(30); +#define KFrR_MPEG4_PLID_8 qreal(15); +#define KFrR_MPEG4_PLID_9 qreal(15); +// H.264 (Baseline Profile, same framerates apply to Main and High Profile) +#define KFrR_H264 qreal(15); +#define KFrR_H264_PLID_42800A qreal(15); +#define KFrR_H264_PLID_42900B qreal(15); +#define KFrR_H264_PLID_42800B qreal(7.5); +#define KFrR_H264_PLID_42800C qreal(15); +#define KFrR_H264_PLID_42800D qreal(30); +#define KFrR_H264_PLID_428014 qreal(30); +#define KFrR_H264_PLID_428015 qreal(50); +#define KFrR_H264_PLID_428016 qreal(16.9); +#define KFrR_H264_PLID_42801E qreal(33.8); +#define KFrR_H264_PLID_42801F qreal(30); +#define KFrR_H264_PLID_428020 qreal(60); +#define KFrR_H264_PLID_428028 qreal(30); + +// Maximum bitrates for encoder MIME Types +// H.263 +#define KBiR_H263 int(64000); +#define KBiR_H263_Profile0 int(64000); +#define KBiR_H263_Profile0_Level10 int(64000); +#define KBiR_H263_Profile0_Level20 int(128000); +#define KBiR_H263_Profile0_Level30 int(384000); +#define KBiR_H263_Profile0_Level40 int(2048000); +#define KBiR_H263_Profile0_Level45 int(128000); +#define KBiR_H263_Profile0_Level50 int(4096000); +#define KBiR_H263_Profile3 int(64000); +// MPEG-4 +#define KBiR_MPEG4 int(64000); +#define KBiR_MPEG4_PLID_1 int(64000); +#define KBiR_MPEG4_PLID_2 int(128000); +#define KBiR_MPEG4_PLID_3 int(384000); +// This is a workaround for a known platform bug +#if (defined(S60_31_PLATFORM) | defined(S60_32_PLATFORM)) +#define KBiR_MPEG4_PLID_4 int(2000000); +#else // All other platforms +#define KBiR_MPEG4_PLID_4 int(4000000); +#endif // S60 3.1 or 3.2 +#define KBiR_MPEG4_PLID_5 int(8000000); +#define KBiR_MPEG4_PLID_6 int(12000000); +#define KBiR_MPEG4_PLID_8 int(64000); +#define KBiR_MPEG4_PLID_9 int(128000); +// H.264 (Baseline Profile, same bitrates apply to Main and High Profile) +#define KBiR_H264 int(64000); +#define KBiR_H264_PLID_42800A int(64000); +#define KBiR_H264_PLID_42900B int(128000); +#define KBiR_H264_PLID_42800B int(192000); +#define KBiR_H264_PLID_42800C int(384000); +#define KBiR_H264_PLID_42800D int(768000); +#define KBiR_H264_PLID_428014 int(2000000); +#define KBiR_H264_PLID_428015 int(4000000); +#define KBiR_H264_PLID_428016 int(4000000); +#define KBiR_H264_PLID_42801E int(10000000); +#define KBiR_H264_PLID_42801F int(14000000); +#define KBiR_H264_PLID_428020 int(20000000); +#define KBiR_H264_PLID_428028 int(20000000); + +#endif // S60CAMERACONSTANTS_H diff --git a/src/plugins/symbian/ecam/s60cameracontrol.cpp b/src/plugins/symbian/ecam/s60cameracontrol.cpp new file mode 100644 index 000000000..64a3ff37e --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameracontrol.cpp @@ -0,0 +1,983 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "s60cameraservice.h" +#include "s60cameraengine.h" +#include "s60cameracontrol.h" +#include "s60imagecapturesession.h" +#include "s60videowidgetcontrol.h" +#include "s60cameraviewfinderengine.h" +#include "s60cameraconstants.h" + +S60CameraControl::S60CameraControl(QObject *parent) : + QCameraControl(parent) +{ +} + +S60CameraControl::S60CameraControl(S60VideoCaptureSession *videosession, + S60ImageCaptureSession *imagesession, + QObject *parent): + QCameraControl(parent), + m_cameraEngine(0), + m_viewfinderEngine(0), + m_imageSession(0), + m_videoSession(0), + m_advancedSettings(0), + m_videoOutput(0), + m_inactivityTimer(0), + m_captureMode(QCamera::CaptureStillImage), // Default CaptureMode + m_requestedCaptureMode(QCamera::CaptureStillImage), + m_settingCaptureModeInternally(false), + m_internalState(QCamera::UnloadedStatus), // Default Status + m_requestedState(QCamera::UnloadedState), // Default State + m_deviceIndex(KDefaultCameraDevice), + m_error(KErrNone), + m_changeCaptureModeWhenReady(false), + m_rotateCameraWhenReady(false), + m_videoCaptureState(S60VideoCaptureSession::ENotInitialized) +{ + m_videoSession = videosession; + m_imageSession = imagesession; + + m_inactivityTimer = new QTimer; + if (m_inactivityTimer) + m_inactivityTimer->setSingleShot(true); + + TRAPD(err, m_cameraEngine = CCameraEngine::NewL(m_deviceIndex, KECamCameraPriority, this)); + if (err) { + m_error = err; + if (err == KErrPermissionDenied) + qWarning("Failed to create camera. Possibly missing capabilities."); + else + qWarning("Failed to create camera."); + return; + } + + m_viewfinderEngine = new S60CameraViewfinderEngine(this, m_cameraEngine, this); + if (m_viewfinderEngine == 0) { + m_error = KErrNoMemory; + qWarning("Failed to create viewfinder engine."); + return; + } + + // Connect signals + connect(m_inactivityTimer, SIGNAL(timeout()), this, SLOT(toStandByStatus())); + connect(this, SIGNAL(statusChanged(QCamera::Status)), + m_imageSession, SLOT(cameraStatusChanged(QCamera::Status))); + connect(this, SIGNAL(statusChanged(QCamera::Status)), + m_videoSession, SLOT(cameraStatusChanged(QCamera::Status))); + connect(m_videoSession, SIGNAL(stateChanged(S60VideoCaptureSession::TVideoCaptureState)), + this, SLOT(videoStateChanged(S60VideoCaptureSession::TVideoCaptureState))); + connect(m_imageSession, SIGNAL(advancedSettingChanged()), this, SLOT(advancedSettingsCreated())); + connect(this, SIGNAL(cameraReadyChanged(bool)), m_imageSession, SIGNAL(readyForCaptureChanged(bool))); + connect(m_viewfinderEngine, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int,const QString&))); + connect(m_imageSession, SIGNAL(cameraError(int, const QString&)), this, SIGNAL(error(int, const QString&))); + connect(m_imageSession, SIGNAL(captureSizeChanged(const QSize&)), + m_viewfinderEngine, SLOT(handleContentAspectRatioChange(const QSize&))); + connect(m_videoSession, SIGNAL(captureSizeChanged(const QSize&)), + m_viewfinderEngine, SLOT(handleContentAspectRatioChange(const QSize&))); + + setCameraHandles(); +} + +S60CameraControl::~S60CameraControl() +{ + unloadCamera(); + + if (m_viewfinderEngine) { + delete m_viewfinderEngine; + m_viewfinderEngine = 0; + } + + // Make sure AdvancedSettings are destructed + m_imageSession->deleteAdvancedSettings(); + + if (m_cameraEngine) { + delete m_cameraEngine; + m_cameraEngine = 0; + } + + if (m_inactivityTimer) { + delete m_inactivityTimer; + m_inactivityTimer = 0; + } +} + +void S60CameraControl::setState(QCamera::State state) +{ + if (m_error) { // Most probably failure in contructor + setError(m_error, tr("Unexpected camera error.")); + return; + } + + if (m_requestedState == state) + return; + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + // Save the target state + m_requestedState = state; + emit stateChanged(m_requestedState); + + switch (state) { + case QCamera::UnloadedState: // To UnloadedState - Release resources + switch (m_internalState) { + case QCamera::UnloadedStatus: + // Do nothing + break; + case QCamera::LoadingStatus: + case QCamera::StartingStatus: + // Release resources when ready (setting state handles this) + return; + case QCamera::LoadedStatus: + case QCamera::StandbyStatus: + // Unload + unloadCamera(); + break; + case QCamera::ActiveStatus: + // Stop and Unload + stopCamera(); + unloadCamera(); + break; + + default: + // Unrecognized internal state (Status) + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + break; + + case QCamera::LoadedState: // To LoadedState - Reserve resources OR Stop ViewFinder and Cancel Capture + switch (m_internalState) { + case QCamera::UnloadedStatus: + case QCamera::StandbyStatus: + // Load + loadCamera(); + break; + case QCamera::LoadingStatus: + // Discard, already moving to LoadedStatus + return; + case QCamera::StartingStatus: + // Stop when ready (setting state handles this) + return; + case QCamera::LoadedStatus: + m_inactivityTimer->start(KInactivityTimerTimeout); + break; + case QCamera::ActiveStatus: + // Stop + stopCamera(); + break; + + default: + // Unregocnized internal state (Status) + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + break; + + case QCamera::ActiveState: // To ActiveState - (Reserve Resources and) Start ViewFinder + switch (m_internalState) { + case QCamera::UnloadedStatus: + case QCamera::StandbyStatus: + // Load and Start (setting state handles starting) + loadCamera(); + break; + case QCamera::LoadingStatus: + // Start when loaded (setting state handles this) + break; + case QCamera::StartingStatus: + // Discard, already moving to ActiveStatus + return; + case QCamera::LoadedStatus: + // Start + startCamera(); + break; + case QCamera::ActiveStatus: + // Do nothing + break; + + default: + // Unregocnized internal state (Status) + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + break; + + default: + setError(KErrNotSupported, tr("Requested state is not supported.")); + return; + } +} + +QCamera::State S60CameraControl::state() const +{ + return m_requestedState; +} + +QCamera::Status S60CameraControl::status() const +{ + return m_internalState; +} + +QCamera::CaptureMode S60CameraControl::captureMode() const +{ + return m_captureMode; +} + +void S60CameraControl::setCaptureMode(QCamera::CaptureMode mode) +{ + if (m_error) { // Most probably failure in contructor + setError(m_error, tr("Unexpected camera error.")); + return; + } + + if (m_captureMode == mode) + return; + + // Setting CaptureMode Internally or Externally (Client) + if (!m_settingCaptureModeInternally) { + // Save the requested mode + m_requestedCaptureMode = mode; + + // CaptureMode change pending (backend busy), wait + if (m_changeCaptureModeWhenReady) + return; + } else { + m_changeCaptureModeWhenReady = false; // Reset + } + m_settingCaptureModeInternally = false; // Reset + + if (!isCaptureModeSupported(mode)) { + setError(KErrNotSupported, tr("Requested capture mode is not supported.")); + return; + } + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + switch (m_internalState) { + case QCamera::UnloadedStatus: + case QCamera::LoadedStatus: + case QCamera::StandbyStatus: + switch (mode) { + case QCamera::CaptureStillImage: + m_videoSession->releaseVideoRecording(); + m_captureMode = QCamera::CaptureStillImage; + if (m_internalState == QCamera::LoadedStatus) + m_inactivityTimer->start(KInactivityTimerTimeout); + else if (m_internalState == QCamera::StandbyStatus) + loadCamera(); + break; + case QCamera::CaptureVideo: + m_imageSession->releaseImageCapture(); + m_captureMode = QCamera::CaptureVideo; + if (m_internalState == QCamera::LoadedStatus) { + // Revet InternalState as we need to wait for the video + // side initialization to complete + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video capture failed.")); + } else if (m_internalState == QCamera::StandbyStatus) + loadCamera(); + break; + } + break; + case QCamera::LoadingStatus: + case QCamera::StartingStatus: + m_changeCaptureModeWhenReady = true; + return; + case QCamera::ActiveStatus: + // Stop, Change Mode and Start again + stopCamera(); + switch (mode) { + case QCamera::CaptureStillImage: + m_videoSession->releaseVideoRecording(); + m_captureMode = QCamera::CaptureStillImage; + startCamera(); + break; + case QCamera::CaptureVideo: + m_imageSession->releaseImageCapture(); + m_captureMode = QCamera::CaptureVideo; + // Revet InternalState as we need to wait for the video + // side initialization to complete + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video recorder failed.")); + break; + } + break; + + default: + // Unregocnized internal state (Status) + setError(KErrNotSupported, tr("Requested capture mode is not supported.")); + break; + } + + emit captureModeChanged(mode); +} + +bool S60CameraControl::isCaptureModeSupported(QCamera::CaptureMode mode) const +{ + switch (mode) { + case QCamera::CaptureStillImage: + return true; + case QCamera::CaptureVideo: + return true; + + default: + return false; + } +} + +bool S60CameraControl::canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const +{ + Q_UNUSED(status); + + bool returnValue = false; + + switch (changeType) { + case QCameraControl::CaptureMode: + case QCameraControl::VideoEncodingSettings: + case QCameraControl::ImageEncodingSettings: + returnValue = true; + break; + + case QCameraControl::Viewfinder: + returnValue = false; + break; + + default: + // Safer to revert state before the unknown operation + returnValue = false; + break; + } + + return returnValue; +} + +void S60CameraControl::setVideoOutput(QObject *output, + S60CameraViewfinderEngine::ViewfinderOutputType type) +{ + if (!m_viewfinderEngine) { + setError(KErrGeneral, tr("Failed to set viewfinder")); + return; + } + + switch (type) { + case S60CameraViewfinderEngine::OutputTypeVideoWidget: + m_viewfinderEngine->setVideoWidgetControl(output); + break; + case S60CameraViewfinderEngine::OutputTypeRenderer: + m_viewfinderEngine->setVideoRendererControl(output); + break; + case S60CameraViewfinderEngine::OutputTypeVideoWindow: + m_viewfinderEngine->setVideoWindowControl(output); + break; + + default: + break; + } +} + +void S60CameraControl::releaseVideoOutput(const S60CameraViewfinderEngine::ViewfinderOutputType type) +{ + m_viewfinderEngine->releaseControl(type); +} + +void S60CameraControl::loadCamera() +{ + if (m_internalState < QCamera::LoadingStatus) { + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState == QCamera::LoadedStatus + || m_internalState >= QCamera::StartingStatus) { + // Nothing to load (already loaded) + return; + } + // Status = Loading or Standby + + m_cameraEngine->ReserveAndPowerOn(); + + // Completion notified in MceoCameraReady() +} + +void S60CameraControl::unloadCamera() +{ + if (m_internalState > QCamera::LoadingStatus) { + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState < QCamera::LoadingStatus) { + // Nothing to unload + return; + } + // Status = Loading + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + m_cameraEngine->ReleaseAndPowerOff(); + + m_internalState = QCamera::UnloadedStatus; + emit statusChanged(m_internalState); +} + +void S60CameraControl::startCamera() +{ + if (m_internalState < QCamera::StartingStatus) { + m_internalState = QCamera::StartingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState > QCamera::StartingStatus) { + // Nothing to start (already started) + return; + } + // Status = Starting + + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + if (m_viewfinderEngine) + m_viewfinderEngine->startViewfinder(); + else + setError(KErrGeneral, tr("Failed to start viewfinder.")); + + m_internalState = QCamera::ActiveStatus; + emit statusChanged(m_internalState); + + emit cameraReadyChanged(true); + +#ifdef Q_CC_NOKIAX86 // Emulator + MceoCameraReady(); // Signal that we are ready +#endif +} + +void S60CameraControl::stopCamera() +{ + if (m_internalState > QCamera::StartingStatus) { + m_internalState = QCamera::StartingStatus; + emit statusChanged(m_internalState); + } else if (m_internalState < QCamera::StartingStatus) { + // Nothing to stop + return; + } + // Status = Starting + + // Cancel ongoing operations if any + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(); + + emit cameraReadyChanged(false); + if (m_viewfinderEngine) + m_viewfinderEngine->stopViewfinder(); + else + setError(KErrGeneral, tr("Failed to stop viewfinder.")); + + m_internalState = QCamera::LoadedStatus; + emit statusChanged(m_internalState); + + m_inactivityTimer->start(KInactivityTimerTimeout); +} + +void S60CameraControl::videoStateChanged(const S60VideoCaptureSession::TVideoCaptureState state) +{ + // Save video state + m_videoCaptureState = state; + + if (m_rotateCameraWhenReady) { + if (m_videoCaptureState != S60VideoCaptureSession::ERecording && + m_videoCaptureState != S60VideoCaptureSession::EPaused) + resetCameraOrientation(); + } + + // If video recording was stopped, video state reverts back to + // Initializing. In that case revert also Camera status to notify that + // video initialization needs to be completed. + if (state == S60VideoCaptureSession::EInitializing) { + if (m_internalState > QCamera::LoadingStatus) { + m_internalState = QCamera::LoadingStatus; + emit statusChanged(m_internalState); + } + + // Handle video initialization completion + } else if (state == S60VideoCaptureSession::EInitialized) { + + // Make sure state is not downgraded + if (m_internalState == QCamera::LoadedStatus + || m_internalState == QCamera::ActiveStatus) { + // Do nothing (already in target state) + } else if (m_internalState == QCamera::StartingStatus) { + m_internalState = QCamera::ActiveStatus; + emit statusChanged(m_internalState); + } else { + m_internalState = QCamera::LoadedStatus; + emit statusChanged(m_internalState); + } + + switch (m_requestedState) { + case QCamera::UnloadedState: + stopCamera(); + unloadCamera(); + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + break; + case QCamera::LoadedState: + stopCamera(); + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + m_inactivityTimer->start(KInactivityTimerTimeout); + break; + case QCamera::ActiveState: + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + startCamera(); + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + } +} + +void S60CameraControl::imageCaptured(const int imageId, const QImage& preview) +{ + Q_UNUSED(imageId); + Q_UNUSED(preview); + + // Unsubscribe the readyForCaptureChanged notification + disconnect(m_imageSession, SIGNAL(imageCaptured(const int, const QImage&)), + this, SLOT(imageCaptured(const int, const QImage&))); + + if (m_rotateCameraWhenReady) + resetCameraOrientation(); +} + +void S60CameraControl::advancedSettingsCreated() +{ + m_advancedSettings = m_imageSession->advancedSettings(); + + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int, const QString&))); +} + +void S60CameraControl::MceoCameraReady() +{ + // Rotate camera if requested + if (m_rotateCameraWhenReady) { + resetCameraOrientation(); + return; + } + + if (m_internalState != QCamera::LoadedStatus) { + + switch (m_requestedState) { + case QCamera::UnloadedState: + m_internalState = QCamera::LoadedStatus; + emit statusChanged(QCamera::LoadedStatus); + + stopCamera(); + unloadCamera(); + + if (m_changeCaptureModeWhenReady) { + m_settingCaptureModeInternally = true; + setCaptureMode(m_requestedCaptureMode); + } + break; + + case QCamera::LoadedState: + if (m_captureMode == QCamera::CaptureVideo) { + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video capture failed.")); + + // State change signalled when reservation is complete (in videoStateChanged()) + return; + } + m_internalState = QCamera::LoadedStatus; + emit statusChanged(QCamera::LoadedStatus); + + if (m_changeCaptureModeWhenReady) { + setCaptureMode(m_requestedCaptureMode); + m_changeCaptureModeWhenReady = false; // Reset + } + + if (m_requestedState == QCamera::LoadedStatus && + m_internalState == QCamera::LoadedStatus) + m_inactivityTimer->start(KInactivityTimerTimeout); + break; + + case QCamera::ActiveState: + if (m_captureMode == QCamera::CaptureVideo) { + int prepareSuccess = m_videoSession->initializeVideoRecording(); + setError(prepareSuccess, tr("Loading video capture failed.")); + + // State change signalled when reservation is complete (in videoStateChanged()) + return; + } + + m_internalState = QCamera::LoadedStatus; + emit statusChanged(QCamera::LoadedStatus); + + if (m_changeCaptureModeWhenReady) { + setCaptureMode(m_requestedCaptureMode); + m_changeCaptureModeWhenReady = false; // Reset + } + startCamera(); + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + } +} + +void S60CameraControl::MceoHandleError(TCameraEngineError aErrorType, TInt aError) +{ + Q_UNUSED(aErrorType); + + if (aError == KErrAccessDenied) { + setError(KErrGeneral, tr("Access to camera device was rejected.")); + } else if (aError == KErrHardwareNotAvailable) { + setError(aError, tr("Camera resources were lost.")); + toStandByStatus(); + } + else + setError(aError, tr("Unexpected camera error.")); +} + +void S60CameraControl::setError(const TInt error, const QString &description) +{ + if (error == KErrNone) + return; + + m_error = error; + QCamera::Error cameraError = fromSymbianErrorToQtMultimediaError(m_error); + + emit this->error(int(cameraError), description); + + // Reset everything, if other than not supported error or resource loss + if (error != KErrNotSupported && error != KErrHardwareNotAvailable) + resetCamera(true); // Try to recover from error + else + m_error = KErrNone; // Reset error +} + +QCamera::Error S60CameraControl::fromSymbianErrorToQtMultimediaError(int aError) +{ + switch(aError) { + case KErrNone: + return QCamera::NoError; // No errors have occurred + + case KErrNotSupported: + return QCamera::NotSupportedFeatureError; // The feature is not supported + case KErrNotFound: + case KErrBadHandle: + return QCamera::ServiceMissingError; // No camera service available + case KErrArgument: + case KErrNotReady: + return QCamera::InvalidRequestError; // Invalid parameter or state + + default: + return QCamera::CameraError; // An error has occurred (i.e. General Error) + } +} + +// For S60CameraVideoDeviceControl +int S60CameraControl::deviceCount() +{ +#ifdef Q_CC_NOKIAX86 // Emulator + return 1; +#endif + + return CCameraEngine::CamerasAvailable(); +} + +int S60CameraControl::defaultDevice() const +{ + return KDefaultCameraDevice; +} + +int S60CameraControl::selectedDevice() const +{ + return m_deviceIndex; +} + +void S60CameraControl::setSelectedDevice(const int index) +{ + if (m_deviceIndex != index) { + if (index >= 0 && index < deviceCount()) { + m_deviceIndex = index; + resetCamera(); + } else { + setError(KErrNotSupported, tr("Requested camera is not available.")); + } + } +} + +QString S60CameraControl::name(const int index) +{ + QString cameraName; + switch (index) { + case 0: + cameraName = tr("Primary camera"); + break; + case 1: + cameraName = tr("Secondary camera"); + break; + case 2: + cameraName = tr("Tertiary camera"); + break; + + default: + cameraName = tr("Unidentified Camera"); + break; + } + + return cameraName; +} + +QString S60CameraControl::description(const int index) +{ + QString cameraDesc; + switch (index) { + case 0: + cameraDesc = tr("Device primary camera"); + break; + case 1: + cameraDesc = tr("Device secondary camera"); + break; + case 2: + cameraDesc = tr("Device tertiary camera"); + break; + + default: + cameraDesc = tr("Unidentified Camera"); + break; + } + + return cameraDesc; +} + +void S60CameraControl::resetCamera(bool errorHandling) +{ + if (m_inactivityTimer->isActive()) + m_inactivityTimer->stop(); + + // Cancel ongoing activity + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(false); // Don't re-initialize video + + // Advanced settings must be destructed before the camera + m_imageSession->deleteAdvancedSettings(); + + // Release resources + stopCamera(); + unloadCamera(); + + disconnect(m_viewfinderEngine, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int,const QString&))); + if (m_viewfinderEngine) { + delete m_viewfinderEngine; + m_viewfinderEngine = 0; + } + + if (m_cameraEngine) { + delete m_cameraEngine; + m_cameraEngine = 0; + } + + TRAPD(err, m_cameraEngine = CCameraEngine::NewL(m_deviceIndex, 0, this)); + if (err) { + m_cameraEngine = 0; + if (errorHandling) { + qWarning("Failed to recover from error."); + if (err == KErrPermissionDenied) + emit error(int(QCamera::ServiceMissingError), tr("Recovering from error failed. Possibly missing capabilities.")); + else + emit error(int(QCamera::CameraError), tr("Recovering from error failed.")); + } else { + if (err == KErrPermissionDenied) + setError(err, tr("Camera device creation failed. Possibly missing capabilities.")); + else + setError(err, tr("Camera device creation failed.")); + } + return; + } + + // Notify list of available camera devices has been updated + emit devicesChanged(); + + m_viewfinderEngine = new S60CameraViewfinderEngine(this, m_cameraEngine, this); + if (m_viewfinderEngine == 0) + setError(KErrNoMemory, tr("Viewfinder device creation failed.")); + connect(m_viewfinderEngine, SIGNAL(error(int, const QString&)), this, SIGNAL(error(int,const QString&))); + + setCameraHandles(); + + // Reset state + //setState(QCamera::UnloadedState); + if (m_internalState != QCamera::UnloadedStatus) { + m_internalState = QCamera::UnloadedStatus; + emit statusChanged(m_internalState); + } + if (m_requestedState != QCamera::UnloadedState) { + m_requestedState = QCamera::UnloadedState; + emit stateChanged(m_requestedState); + } + + // Reset error + m_error = KErrNone; +} + +/* + * Reset everything else than viewfinder engine and errors. + */ +void S60CameraControl::resetCameraOrientation() +{ + // If camera has not been created, it will be created automatically to correct orientation + if (!m_cameraEngine) + return; + + // Check Image/VideoCapture allow rotation + if ((!m_cameraEngine->IsCameraReady() && m_internalState != QCamera::UnloadedStatus) || + m_videoCaptureState == S60VideoCaptureSession::ERecording || + m_videoCaptureState == S60VideoCaptureSession::EPaused) { + + // If image capture is ongoing, request notification about the + // completion (imageCaptured() is used because that comes asynchronously + // after the image is captured) + // Obs! If preview creation is changed to be synchnonously done during + // the image capture this implementation needs to be changed) + if (m_videoCaptureState != S60VideoCaptureSession::ERecording && + m_videoCaptureState != S60VideoCaptureSession::EPaused && + m_internalState == QCamera::ActiveStatus) + connect(m_imageSession, SIGNAL(imageCaptured(const int, const QImage&)), + this, SLOT(imageCaptured(const int, const QImage&))); + + m_rotateCameraWhenReady = true; + return; + } + + m_rotateCameraWhenReady = false; // Reset + + QCamera::State originalState = m_requestedState; + + // Cancel ongoing activity + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(false); // Don't re-initialize video + + // Advanced settings must be destructed before the camera + m_imageSession->deleteAdvancedSettings(); + + // Release resources + stopCamera(); + unloadCamera(); + + // Unset CameraEngine to ViewfinderEngine + m_viewfinderEngine->setNewCameraEngine(0); + if (m_cameraEngine) { + delete m_cameraEngine; + m_cameraEngine = 0; + } + + TRAPD(err, m_cameraEngine = CCameraEngine::NewL(m_deviceIndex, 0, this)); + if (err) { + setError(err, tr("Camera device creation failed.")); + return; + } + // Reset CameraEngine to ViewfinderEngine + m_viewfinderEngine->setNewCameraEngine(m_cameraEngine); + + // Notify list of available camera devices has been updated + emit devicesChanged(); + + setCameraHandles(); + + // Reset state + if (m_internalState != QCamera::UnloadedStatus) { + m_internalState = QCamera::UnloadedStatus; + emit statusChanged(m_internalState); + } + if (m_requestedState != QCamera::UnloadedState) { + m_requestedState = QCamera::UnloadedState; + emit stateChanged(m_requestedState); + } + + setState(originalState); +} + +void S60CameraControl::setCameraHandles() +{ + m_imageSession->setCurrentDevice(m_deviceIndex); + m_imageSession->setCameraHandle(m_cameraEngine); + m_cameraEngine->SetImageCaptureObserver(m_imageSession); + m_videoSession->setCameraHandle(m_cameraEngine); +} + +void S60CameraControl::toStandByStatus() +{ + // Cancel ongoing operations if any + m_imageSession->cancelCapture(); + m_videoSession->stopRecording(false); // Don't re-initialize video + + emit cameraReadyChanged(false); + if (m_viewfinderEngine) + m_viewfinderEngine->stopViewfinder(); + else + setError(KErrGeneral, tr("Failed to stop viewfinder.")); + + m_cameraEngine->ReleaseAndPowerOff(); + + m_internalState = QCamera::StandbyStatus; + emit statusChanged(m_internalState); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameracontrol.h b/src/plugins/symbian/ecam/s60cameracontrol.h new file mode 100644 index 000000000..95c31fb34 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameracontrol.h @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERACONTROL_H +#define S60CAMERACONTROL_H + +#include + +#include "s60cameraengineobserver.h" // MCameraEngineObserver +#include "s60videocapturesession.h" // TVideoCaptureState +#include "s60cameraviewfinderengine.h" // ViewfinderOutputType + +#include +#include + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; +class S60VideoCaptureSession; +class S60CameraSettings; +class CCameraEngine; +class S60CameraViewfinderEngine; +class QTimer; + +/* + * Control for controlling camera base operations (e.g. start/stop and capture + * mode). + */ +class S60CameraControl : public QCameraControl, public MCameraEngineObserver +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraControl(QObject *parent = 0); + S60CameraControl(S60VideoCaptureSession *videosession, + S60ImageCaptureSession *imagesession, + QObject *parent = 0); + ~S60CameraControl(); + +public: // QCameraControl + + // State + QCamera::State state() const; + void setState(QCamera::State state); + + // Status + QCamera::Status status() const; + + // Capture Mode + QCamera::CaptureMode captureMode() const; + void setCaptureMode(QCamera::CaptureMode); + bool isCaptureModeSupported(QCamera::CaptureMode mode) const; + + // Property Setting + bool canChangeProperty(QCameraControl::PropertyChangeType changeType, QCamera::Status status) const; + +/* +Q_SIGNALS: + void stateChanged(QCamera::State); + void statusChanged(QCamera::Status); + void error(int error, const QString &errorString); + void captureModeChanged(QCamera::CaptureMode); +*/ + +public: // Internal + + void setError(const TInt error, const QString &description); + void resetCameraOrientation(); + + // To provide QVideoDeviceControl info + static int deviceCount(); + static QString name(const int index); + static QString description(const int index); + int defaultDevice() const; + int selectedDevice() const; + void setSelectedDevice(const int index); + + void setVideoOutput(QObject *output, + const S60CameraViewfinderEngine::ViewfinderOutputType type); + void releaseVideoOutput(const S60CameraViewfinderEngine::ViewfinderOutputType type); + +private slots: // Internal Slots + + void videoStateChanged(const S60VideoCaptureSession::TVideoCaptureState state); + // Needed to detect image capture completion when trying to rotate the camera + void imageCaptured(const int imageId, const QImage& preview); + /* + * This method moves the camera to the StandBy status: + * - If camera access was lost + * - If camera has been inactive in LoadedStatus for a long time + */ + void toStandByStatus(); + void advancedSettingsCreated(); + +protected: // MCameraEngineObserver + + void MceoCameraReady(); + void MceoHandleError(TCameraEngineError aErrorType, TInt aError); + +private: // Internal + + QCamera::Error fromSymbianErrorToQtMultimediaError(int aError); + + void loadCamera(); + void unloadCamera(); + void startCamera(); + void stopCamera(); + + void resetCamera(bool errorHandling = false); + void setCameraHandles(); + +signals: // Internal Signals + + void cameraReadyChanged(bool); + void devicesChanged(); + +private: // Data + + CCameraEngine *m_cameraEngine; + S60CameraViewfinderEngine *m_viewfinderEngine; + S60ImageCaptureSession *m_imageSession; + S60VideoCaptureSession *m_videoSession; + S60CameraSettings *m_advancedSettings; + QObject *m_videoOutput; + QTimer *m_inactivityTimer; + QCamera::CaptureMode m_captureMode; + QCamera::CaptureMode m_requestedCaptureMode; + bool m_settingCaptureModeInternally; + QCamera::Status m_internalState; + QCamera::State m_requestedState; + int m_deviceIndex; + mutable int m_error; + bool m_changeCaptureModeWhenReady; + bool m_rotateCameraWhenReady; + S60VideoCaptureSession::TVideoCaptureState m_videoCaptureState; +}; + +#endif // S60CAMERACONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraengine.cpp b/src/plugins/symbian/ecam/s60cameraengine.cpp new file mode 100644 index 000000000..dbd5fb33e --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraengine.cpp @@ -0,0 +1,824 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 2.1 requirements + ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ + +#include "s60cameraengine.h" +#include "s60cameraengineobserver.h" +#include "s60cameraconstants.h" +#include +#include // CFbsBitmap +#ifdef ECAM_PREVIEW_API + #include +#endif // ECAM_PREVIEW_API + +CCameraEngine::CCameraEngine() +{ +} + +CCameraEngine::CCameraEngine(TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver) : + // CBase initializes member variables to NULL + iObserver(aObserver), + iCameraIndex(aCameraHandle), + iPriority(aPriority), + iEngineState(EEngineNotReady), + iCaptureResolution(TSize(0,0)), + iNew2LImplementation(false), + iLatestImageBufferIndex(1) // Thus we start from index 0 +{ + // Observer is mandatory + ASSERT(aObserver != NULL); +} + +CCameraEngine::~CCameraEngine() +{ + StopViewFinder(); + ReleaseViewFinderBuffer(); // Releases iViewFinderBuffer + ReleaseImageBuffer(); // Releases iImageBuffer + iImageBitmap + + iAdvancedSettingsObserver = NULL; + iImageCaptureObserver = NULL; + iViewfinderObserver = NULL; + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + delete iAutoFocus; +#endif // S60_CAM_AUTOFOCUS_SUPPORT + + if (iCamera) { + iCamera->Release(); + delete iCamera; + iCamera = NULL; + } +} + +TInt CCameraEngine::CamerasAvailable() +{ + return CCamera::CamerasAvailable(); +} + +CCameraEngine* CCameraEngine::NewL(TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver) +{ + CCameraEngine* self = new (ELeave) CCameraEngine(aCameraHandle, aPriority, aObserver); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +void CCameraEngine::ConstructL() +{ + if (!CCamera::CamerasAvailable()) + User::Leave(KErrHardwareNotAvailable); + +#ifndef Q_CC_NOKIAX86 // Not Emulator + TInt err(KErrNone); +#else // Emulator + TInt err(KErrNotFound); +#endif // !(Q_CC_NOKIAX86) + +#ifdef S60_31_PLATFORM + // Construct CCamera object for S60 3.1 (NewL) + iNew2LImplementation = false; + TRAP(err, iCamera = CCamera::NewL(*this, iCameraIndex)); + if (err) + User::Leave(err); +#else // For S60 3.2 onwards - use this constructor (New2L) + iNew2LImplementation = true; + TRAP(err, iCamera = CCamera::New2L(*this, iCameraIndex, iPriority)); + if (err) + User::Leave(err); +#endif // S60_31_PLATFORM + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + // Might not be supported for secondary camera, discard errors + TRAP(err, iAutoFocus = CCamAutoFocus::NewL(iCamera)); +#endif // S60_CAM_AUTOFOCUS_SUPPORT + + if (iCamera == NULL) + User::Leave(KErrNoMemory); + + iCamera->CameraInfo(iCameraInfo); +} + +void CCameraEngine::SetAdvancedObserver(MAdvancedSettingsObserver* aAdvancedSettingsObserver) +{ + iAdvancedSettingsObserver = aAdvancedSettingsObserver; +} + +void CCameraEngine::SetImageCaptureObserver(MCameraEngineImageCaptureObserver* aImageCaptureObserver) +{ + iImageCaptureObserver = aImageCaptureObserver; +} + +void CCameraEngine::SetViewfinderObserver(MCameraViewfinderObserver* aViewfinderObserver) +{ + iViewfinderObserver = aViewfinderObserver; +} + +void CCameraEngine::ReserveAndPowerOn() +{ + if (!iCamera || iEngineState > EEngineNotReady) { + iObserver->MceoHandleError(EErrReserve, KErrNotReady); + return; + } + + iCamera->Reserve(); +} + +void CCameraEngine::ReleaseAndPowerOff() +{ + if (iEngineState >= EEngineIdle) { + CancelCapture(); + StopViewFinder(); + FocusCancel(); + iCamera->PowerOff(); + iCamera->Release(); + } + iEngineState = EEngineNotReady; +} + +void CCameraEngine::StartViewFinderL(TSize& aSize) +{ + if (iEngineState < EEngineIdle) + User::Leave(KErrNotReady); + + if (0 == (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderBitmapsSupported)) + User::Leave(KErrNotSupported); + + if (!iCamera->ViewFinderActive()) { + if (iCameraIndex != 0) + iCamera->SetViewFinderMirrorL(true); + iCamera->StartViewFinderBitmapsL(aSize); + } +} + +void CCameraEngine::StopViewFinder() +{ + if (iCamera && iCamera->ViewFinderActive()) + iCamera->StopViewFinder(); +} + +void CCameraEngine::StartDirectViewFinderL(RWsSession& aSession, + CWsScreenDevice& aScreenDevice, + RWindowBase& aWindow, + TRect& aScreenRect, + TRect& aClipRect) +{ + if (iEngineState < EEngineIdle) + User::Leave(KErrNotReady); + + if (0 == (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderDirectSupported)) + User::Leave(KErrNotSupported); + + if (!iCamera->ViewFinderActive()) { + // Viewfinder extent needs to be clipped according to the clip rect. + // This is because the native camera framework does not support + // clipping and starting viewfinder with bigger than the display(S60 + // 5.0 and older)/window(Symbian^3 and later) would cause viewfinder + // starting to fail entirely. This causes shrinking effect in some + // cases, but is better than not having the viewfinder at all. + if (aScreenRect.Intersects(aClipRect)) + aScreenRect.Intersection(aClipRect); + + if (iCameraIndex != 0) + iCamera->SetViewFinderMirrorL(true); + if (aScreenRect.Width() > 0 && aScreenRect.Height() > 0) { + iCamera->StartViewFinderDirectL(aSession, aScreenDevice, aWindow, aScreenRect); + } else { + if (iObserver) + iObserver->MceoHandleError(EErrViewFinderReady, KErrArgument); + } + } +} + +void CCameraEngine::PrepareL(TSize& aCaptureSize, CCamera::TFormat aFormat) +{ + iImageCaptureFormat = aFormat; + + TInt closestVar = KMaxTInt, selected = 0; + TSize size; + + // Scan through supported capture sizes and select the closest match + for (TInt index = 0; index < iCameraInfo.iNumImageSizesSupported; index++) { + + iCamera->EnumerateCaptureSizes(size, index, aFormat); + if (size == aCaptureSize) { + selected = index; + break; + } + + TSize varSz = size - aCaptureSize; + TInt variation = varSz.iWidth * varSz.iHeight; + if (variation < closestVar) { + closestVar = variation; + selected = index; + } + } + + iCamera->EnumerateCaptureSizes(aCaptureSize, selected, aFormat); + iCaptureResolution = aCaptureSize; + iCamera->PrepareImageCaptureL(aFormat, selected); +} + +void CCameraEngine::CaptureL() +{ + if (iEngineState < EEngineIdle) + User::Leave(KErrNotReady); + + iCamera->CaptureImage(); + iEngineState = EEngineCapturing; +} + +void CCameraEngine::CancelCapture() +{ + if (iEngineState == EEngineCapturing) { + iCamera->CancelCaptureImage(); + iEngineState = EEngineIdle; + } +} + +void CCameraEngine::HandleEvent(const TECAMEvent &aEvent) +{ + if (aEvent.iEventType == KUidECamEventReserveComplete) { + ReserveComplete(aEvent.iErrorCode); + return; + } + + if (aEvent.iEventType == KUidECamEventPowerOnComplete) { + PowerOnComplete(aEvent.iErrorCode); + return; + } + + if (aEvent.iEventType == KUidECamEventCameraNoLongerReserved) { + // All camera related operations need to be stopped + iObserver->MceoHandleError(EErrReserve, KErrHardwareNotAvailable); + return; + } + +#ifdef ECAM_PREVIEW_API + if (aEvent.iEventType == KUidECamEventCameraSnapshot) { + HandlePreview(); + return; + } +#endif // ECAM_PREVIEW_API + +#if !defined(Q_CC_NOKIAX86) // Not Emulator + // Other events; Exposure, Zoom, etc. (See ecamadvancedsettings.h) + if (iAdvancedSettingsObserver) + iAdvancedSettingsObserver->HandleAdvancedEvent(aEvent); + + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleOtherEvent(aEvent); +#endif // !Q_CC_NOKIAX86 +} + +void CCameraEngine::ReserveComplete(TInt aError) +{ + if (aError == KErrNone) { + iCamera->PowerOn(); +#ifdef S60_31_PLATFORM + } else if (aError == KErrAlreadyExists) { // Known Issue on some S60 3.1 devices + User::After(500000); // Wait for 0,5 second and try again + iCamera->Reserve(); +#endif // S60_31_PLATFORM + } else { + iObserver->MceoHandleError(EErrReserve, aError); + } +} + +void CCameraEngine::PowerOnComplete(TInt aError) +{ + if (aError) { + iObserver->MceoHandleError(EErrPowerOn, aError); + iEngineState = EEngineNotReady; + return; + } + + // Init AutoFocus +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + if( iAutoFocus ) { + TRAPD(afErr, iAutoFocus->InitL( *this )); + if (afErr) { + delete iAutoFocus; + iAutoFocus = 0; + } + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 + + iEngineState = EEngineIdle; + iObserver->MceoCameraReady(); +} + +#ifdef ECAM_PREVIEW_API +/** + * This method creates the CCameraPreview object and requests the previews to + * be provided during the image or video capture + */ +void CCameraEngine::EnablePreviewProvider(MCameraPreviewObserver *aPreviewObserver) +{ + // Delete old one if exists + if (iCameraSnapshot) + delete iCameraSnapshot; + + iPreviewObserver = aPreviewObserver; + + TInt error = KErrNone; + + if (iCamera) { + TRAP(error, iCameraSnapshot = CCamera::CCameraSnapshot::NewL(*iCamera)); + if (error) { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, error); + return; + } + + TRAP(error, iCameraSnapshot->PrepareSnapshotL(KDefaultFormatPreview, SelectPreviewResolution(), EFalse)); + if (error) { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, error); + return; + } + + iCameraSnapshot->StartSnapshot(); + } else { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, KErrNotReady); + } +} + +/** + * This method disables and destroys the CCameraPreview object. Thus previews + * will not be provided during the image or video capture. + */ +void CCameraEngine::DisablePreviewProvider() +{ + if (!iCameraSnapshot) + return; + + iCameraSnapshot->StopSnapshot(); + + delete iCameraSnapshot; + iCameraSnapshot = 0; + + iPreviewObserver = 0; +} +#endif // ECAM_PREVIEW_API + +/* + * MCameraObserver2: + * New viewfinder frame available + */ +void CCameraEngine::ViewFinderReady(MCameraBuffer &aCameraBuffer, TInt aError) +{ + iViewFinderBuffer = &aCameraBuffer; + + if (aError == KErrNone) { + if (iViewfinderObserver) { + TRAPD(err, iViewfinderObserver->MceoViewFinderFrameReady(aCameraBuffer.BitmapL(0))); + if (err) + iObserver->MceoHandleError(EErrViewFinderReady, err); + } else { + iObserver->MceoHandleError(EErrViewFinderReady, KErrNotReady); + } + } + else { + iObserver->MceoHandleError(EErrViewFinderReady, aError); + } +} + +/* + * MCameraObserver: + * New viewfinder frame available + */ +void CCameraEngine::ViewFinderFrameReady(CFbsBitmap& aFrame) +{ + if (iViewfinderObserver) + iViewfinderObserver->MceoViewFinderFrameReady(aFrame); + else + iObserver->MceoHandleError(EErrViewFinderReady, KErrNotReady); +} + +void CCameraEngine::ReleaseViewFinderBuffer() +{ + if (iNew2LImplementation) { // NewL Implementation does not use MCameraBuffer + if (iViewFinderBuffer) { + iViewFinderBuffer->Release(); + iViewFinderBuffer = NULL; + } + } +} + +void CCameraEngine::ReleaseImageBuffer() +{ + // Reset Bitmap + if (iLatestImageBufferIndex == 1 || iImageBitmap2 == NULL) { + if (iImageBitmap1) { + if (!iNew2LImplementation) { // NewL - Ownership transferred + iImageBitmap1->Reset(); // Reset/Delete Bitmap + delete iImageBitmap1; + } + iImageBitmap1 = NULL; + } + } else { + if (iImageBitmap2) { + if (!iNew2LImplementation) { // NewL - Ownership transferred + iImageBitmap2->Reset(); // Reset/Delete Bitmap + delete iImageBitmap2; + } + iImageBitmap2 = NULL; + } + } + + // Reset Data pointers + if (iLatestImageBufferIndex == 1 || iImageData2 == NULL) { + if (!iNew2LImplementation) // NewL - Ownership transfers with buffer + delete iImageData1; + iImageData1 = NULL; + } else { + if (!iNew2LImplementation) // NewL - Ownership transfers with buffer + delete iImageData2; + iImageData2 = NULL; + } + + // Reset ImageBuffer - New2L Implementation only + if (iLatestImageBufferIndex == 1 || iImageBuffer2 == NULL) { + if (iImageBuffer1) { + iImageBuffer1->Release(); + iImageBuffer1 = NULL; + } + } else { + if (iImageBuffer2) { + iImageBuffer2->Release(); + iImageBuffer2 = NULL; + } + } +} + +/* + * MCameraObserver2 + * Captured image is ready (New2L version) + */ +void CCameraEngine::ImageBufferReady(MCameraBuffer &aCameraBuffer, TInt aError) +{ + // Use the buffer that is available + if (!iImageBuffer1) { + iLatestImageBufferIndex = 0; + iImageBuffer1 = &aCameraBuffer; + } else { + iLatestImageBufferIndex = 1; + iImageBuffer2 = &aCameraBuffer; + } + + bool isBitmap = true; + TInt err = KErrNone; + + switch (iImageCaptureFormat) { + case CCamera::EFormatFbsBitmapColor4K: + case CCamera::EFormatFbsBitmapColor64K: + case CCamera::EFormatFbsBitmapColor16M: + case CCamera::EFormatFbsBitmapColor16MU: + if (iLatestImageBufferIndex == 0) { + TRAP(err, iImageBitmap1 = &iImageBuffer1->BitmapL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } else { + TRAP(err, iImageBitmap2 = &iImageBuffer2->BitmapL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } + isBitmap = true; + break; + case CCamera::EFormatExif: + if (iLatestImageBufferIndex == 0) { + TRAP(err, iImageData1 = iImageBuffer1->DataL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } else { + TRAP(err, iImageData2 = iImageBuffer2->DataL(0)); + if (err) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, err); + } + } + isBitmap = false; + break; + + default: + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, KErrNotSupported); + return; + } + + // Handle captured image + HandleImageReady(aError, isBitmap); +} + +/* + * MCameraObserver + * Captured image is ready (NewL version) + */ +void CCameraEngine::ImageReady(CFbsBitmap* aBitmap, HBufC8* aData, TInt aError) +{ + bool isBitmap = true; + + // Toggle between the 2 buffers + if (iLatestImageBufferIndex == 1) { + iLatestImageBufferIndex = 0; + } else { + iLatestImageBufferIndex = 1; + } + + switch (iImageCaptureFormat) { + case CCamera::EFormatFbsBitmapColor4K: + case CCamera::EFormatFbsBitmapColor64K: + case CCamera::EFormatFbsBitmapColor16M: + case CCamera::EFormatFbsBitmapColor16MU: + if (iLatestImageBufferIndex == 0) + iImageBitmap1 = aBitmap; + else + iImageBitmap2 = aBitmap; + isBitmap = true; + break; + case CCamera::EFormatExif: + if (iLatestImageBufferIndex == 0) + iImageData1 = aData; + else + iImageData2 = aData; + isBitmap = false; + break; + + default: + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, KErrNotSupported); + return; + } + + // Handle captured image + HandleImageReady(aError, isBitmap); +} + +void CCameraEngine::HandleImageReady(const TInt aError, const bool isBitmap) +{ + iEngineState = EEngineIdle; + + if (aError == KErrNone) { + if (isBitmap) + if (iImageCaptureObserver) { + if (iLatestImageBufferIndex == 0) + iImageCaptureObserver->MceoCapturedBitmapReady(iImageBitmap1); + else + iImageCaptureObserver->MceoCapturedBitmapReady(iImageBitmap2); + } + else + ReleaseImageBuffer(); + else { + if (iImageCaptureObserver) { + if (iLatestImageBufferIndex == 0) + iImageCaptureObserver->MceoCapturedDataReady(iImageData1); + else + iImageCaptureObserver->MceoCapturedDataReady(iImageData2); + } + else + ReleaseImageBuffer(); + } + } else { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrImageReady, aError); + } +} + +#ifdef ECAM_PREVIEW_API +void CCameraEngine::HandlePreview() +{ + if (!iCameraSnapshot) { + if (iObserver) + iObserver->MceoHandleError(EErrPreview, KErrGeneral); + return; + } + + RArray previewIndices; + CleanupClosePushL(previewIndices); + + MCameraBuffer &newPreview = iCameraSnapshot->SnapshotDataL(previewIndices); + + for (TInt i = 0; i < previewIndices.Count(); ++i) + iPreviewObserver->MceoPreviewReady(newPreview.BitmapL(0)); + + CleanupStack::PopAndDestroy(); // RArray previewIndices +} + +TSize CCameraEngine::SelectPreviewResolution() +{ + TSize currentResolution(iCaptureResolution); + + TSize previewResolution(0, 0); + if (currentResolution == TSize(4000,2248) || + currentResolution == TSize(3264,1832) || + currentResolution == TSize(2592,1456) || + currentResolution == TSize(1920,1080) || + currentResolution == TSize(1280,720)) { + previewResolution = KDefaultSizePreview_Wide; + } else if (currentResolution == TSize(352,288) || + currentResolution == TSize(176,144)) { + previewResolution = KDefaultSizePreview_CIF; + } else if (currentResolution == TSize(720,576)) { + previewResolution = KDefaultSizePreview_PAL; + } else if (currentResolution == TSize(720,480)) { + previewResolution = KDefaultSizePreview_NTSC; + } else { + previewResolution = KDefaultSizePreview_Normal; + } + + return previewResolution; +} +#endif // ECAM_PREVIEW_API + +//============================================================================= +// S60 3.1 - AutoFocus support (Other platforms, see S60CameraSettings class) +//============================================================================= + +void CCameraEngine::InitComplete(TInt aError) +{ + if (aError) { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrAutoFocusInit, aError); + } +} + +void CCameraEngine::OptimisedFocusComplete(TInt aError) +{ + iEngineState = EEngineIdle; + + if (aError == KErrNone) + if (iImageCaptureObserver) + iImageCaptureObserver->MceoFocusComplete(); + else { + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrOptimisedFocusComplete, aError); + } +} + +TBool CCameraEngine::IsCameraReady() const +{ + // If reserved and powered on, but not focusing or capturing + if (iEngineState == EEngineIdle) + return ETrue; + + return EFalse; +} + +TBool CCameraEngine::IsDirectViewFinderSupported() const +{ + if (iCameraInfo.iOptionsSupported & TCameraInfo::EViewFinderDirectSupported) + return true; + else + return false; +} + +TCameraInfo *CCameraEngine::CameraInfo() +{ + return &iCameraInfo; +} + +TBool CCameraEngine::IsAutoFocusSupported() const +{ +#ifndef Q_CC_NOKIAX86 // Not Emulator + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + return (iAutoFocus) ? ETrue : EFalse; +#else // !S60_CAM_AUTOFOCUS_SUPPORT + return EFalse; +#endif // S60_CAM_AUTOFOCUS_SUPPORT + +#else // Q_CC_NOKIAX86 - Emulator + return EFalse; +#endif // !Q_CC_NOKIAX86 +} + +/* + * This function is used for focusing in S60 3.1 platform. Platforms from S60 + * 3.2 onwards should use the focusing provided by the S60CameraSettings class. + */ +void CCameraEngine::StartFocusL() +{ + if (iEngineState != EEngineIdle) + return; + +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + if (iAutoFocus) { + if (!iAFRange) { + iAFRange = CCamAutoFocus::ERangeNormal; + iAutoFocus->SetFocusRangeL(iAFRange); + } + + iAutoFocus->AttemptOptimisedFocusL(); + iEngineState = EEngineFocusing; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 +} + +/* + * This function is used for cancelling focusing in S60 3.1 platform. Platforms + * from S60 3.2 onwards should use the focusing provided by the + * S60CameraSettings class. + */ +void CCameraEngine::FocusCancel() +{ +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + if (iAutoFocus) { + iAutoFocus->Cancel(); + iEngineState = EEngineIdle; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 +} + +void CCameraEngine::SupportedFocusRanges(TInt& aSupportedRanges) const +{ + aSupportedRanges = 0; + +#ifndef Q_CC_NOKIAX86 // Not Emulator +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + if (iAutoFocus) { + // CCamAutoFocus doesn't provide a method for getting supported ranges! + // Assume everything is supported (rather optimistic) + aSupportedRanges = CCamAutoFocus::ERangeMacro | + CCamAutoFocus::ERangePortrait | + CCamAutoFocus::ERangeNormal | + CCamAutoFocus::ERangeInfinite; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT +#endif // !Q_CC_NOKIAX86 +} + +void CCameraEngine::SetFocusRange(TInt aFocusRange) +{ +#if !defined(Q_CC_NOKIAX86) // Not Emulator + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + if (iAutoFocus) { + TRAPD(focusErr, iAutoFocus->SetFocusRangeL((CCamAutoFocus::TAutoFocusRange)aFocusRange)); + if (focusErr) + iObserver->MceoHandleError(EErrAutoFocusRange, focusErr); + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT + +#else // Q_CC_NOKIAX86 // Emulator + Q_UNUSED(aFocusRange); + if (iImageCaptureObserver) + iImageCaptureObserver->MceoHandleError(EErrAutoFocusRange, KErrNotSupported); +#endif // !Q_CC_NOKIAX86 +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraengine.h b/src/plugins/symbian/ecam/s60cameraengine.h new file mode 100644 index 000000000..7a925c0ba --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraengine.h @@ -0,0 +1,407 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 2.1 requirements + ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ +#ifndef S60CCAMERAENGINE_H +#define S60CCAMERAENGINE_H + +// INCLUDES +#include +#include // for MCameraObserver(2) +#ifdef S60_CAM_AUTOFOCUS_SUPPORT +#include // for CCamAutoFocus, MCamAutoFocusObserver +#endif + +// FORWARD DECLARATIONS +class MCameraEngineObserver; +class MCameraEngineImageCaptureObserver; +class MAdvancedSettingsObserver; +class MCameraViewfinderObserver; +class MCameraPreviewObserver; + +/* + * CameraEngine handling ECam operations needed. + */ +NONSHARABLE_CLASS( CCameraEngine ) : public CBase, + public MCameraObserver, + public MCameraObserver2 +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + ,public MCamAutoFocusObserver +#endif + +{ +public: // Enums + + enum TCameraEngineState + { + EEngineNotReady = 0, // 0 - No resources reserved + EEngineInitializing, // 1 - Reserving and Powering On + EEngineIdle, // 2 - Reseved and Powered On + EEngineCapturing, // 3 - Capturing Still Image + EEngineFocusing // 4 - Focusing + }; + +public: // Constructor & Destructor + + static CCameraEngine* NewL( TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver ); + ~CCameraEngine(); + +public: + + /** + * External Advanced Settings callback observer. + */ + void SetAdvancedObserver(MAdvancedSettingsObserver *aAdvancedSettingsObserver); + + /** + * External Image Capture callback observer. + */ + void SetImageCaptureObserver(MCameraEngineImageCaptureObserver *aImageCaptureObserver); + + /** + * External Viewfinder callback observer. + */ + void SetViewfinderObserver(MCameraViewfinderObserver *aViewfinderObserver); + + /** + * Static function that returns the number of cameras on the device. + */ + static TInt CamerasAvailable(); + + /** + * Returns the index of the currently active camera device + */ + TInt CurrentCameraIndex() const { return iCameraIndex; } + + /** + * Returns the current state (TCameraEngineState) + * of the camera engine. + */ + TCameraEngineState State() const { return iEngineState; } + + /** + * Returns true if the camera has been reserved and + * powered on, and not recording or capturing image + */ + TBool IsCameraReady() const; + + /** + * Returns whether DirectScreen ViewFinder is supported by the platform + */ + TBool IsDirectViewFinderSupported() const; + + /** + * Returns true if the camera supports AutoFocus. + */ + TBool IsAutoFocusSupported() const; + + /** + * Returns camera info + */ + TCameraInfo *CameraInfo(); + + /** + * Captures an image. When complete, observer will receive + * MceoCapturedDataReady() or MceoCapturedBitmapReady() callback, + * depending on which image format was used in PrepareL(). + * @leave May leave with KErrNotReady if camera is not + * reserved or prepared for capture. + */ + void CaptureL(); + + /** + * Cancels ongoing image capture + */ + void CancelCapture(); + + /** + * Reserves and powers on the camera. When complete, + * observer will receive MceoCameraReady() callback + * + */ + void ReserveAndPowerOn(); + + /** + * Releases and powers off the camera + * + */ + void ReleaseAndPowerOff(); + + /** + * Prepares for image capture. + * @param aCaptureSize requested capture size. On return, + * contains the selected size (closest match) + * @param aFormat Image format to use. Default is JPEG with + * EXIF information as provided by the camera module + * @leave KErrNotSupported, KErrNoMemory, KErrNotReady + */ + void PrepareL( TSize& aCaptureSize, + CCamera::TFormat aFormat = CCamera::EFormatExif ); + + /** + * Starts the viewfinder. Observer will receive + * MceoViewFinderFrameReady() callbacks periodically. + * @param aSize requested viewfinder size. On return, + * contains the selected size. + * + * @leave KErrNotSupported is viewfinding with bitmaps is not + * supported, KErrNotReady + */ + void StartViewFinderL( TSize& aSize ); + + /** + * Stops the viewfinder if active. + */ + void StopViewFinder(); + + void StartDirectViewFinderL(RWsSession& aSession, + CWsScreenDevice& aScreenDevice, + RWindowBase& aWindow, + TRect& aScreenRect, + TRect& aClipRect); + + /** + * Releases memory for the last received viewfinder frame. + * Client must call this in response to MceoViewFinderFrameReady() + * callback, after drawing the viewfinder frame is complete. + */ + void ReleaseViewFinderBuffer(); + + /** + * Releases memory for the last captured image. + * Client must call this in response to MceoCapturedDataReady() + * or MceoCapturedBitmapReady()callback, after processing the + * data/bitmap is complete. + */ + void ReleaseImageBuffer(); + + /** + * Starts focusing. Does nothing if AutoFocus is not supported. + * When complete, observer will receive MceoFocusComplete() + * callback. + * @leave KErrInUse, KErrNotReady + */ + void StartFocusL(); + + /** + * Cancels the ongoing focusing operation. + */ + void FocusCancel(); + + /** + * Gets a bitfield of supported focus ranges. + * @param aSupportedRanges a bitfield of either TAutoFocusRange + * (S60 3.0/3.1 devices) or TFocusRange (S60 3.2 and onwards) values + */ + void SupportedFocusRanges( TInt& aSupportedRanges ) const; + + /** + * Sets the focus range + * @param aFocusRange one of the values returned by + * SupportedFocusRanges(). + */ + void SetFocusRange( TInt aFocusRange ); + + /** + * Returns a pointer to CCamera object used by the engine. + * Allows getting access to additional functionality + * from CCamera - do not use for functionality already provided + * by CCameraEngine methods. + */ + CCamera* Camera() { return iCamera; } + +#ifdef ECAM_PREVIEW_API + /** + * This enables the preview creation during the capture (image or video). + */ + void EnablePreviewProvider(MCameraPreviewObserver *aPreviewObserver); + + /** + * This disabled the preview creation during the capture (image or video) + */ + void DisablePreviewProvider(); +#endif // ECAM_PREVIEW_API + +protected: // Protected constructors + + CCameraEngine(); + CCameraEngine( TInt aCameraHandle, + TInt aPriority, + MCameraEngineObserver* aObserver ); + void ConstructL(); + +protected: // MCameraObserver + + /** + * From MCameraObserver + * Gets called when CCamera::Reserve() is completed. + * (V2: Called internally from HandleEvent) + */ + virtual void ReserveComplete(TInt aError); + + /** + * From MCameraObserver. + * Gets called when CCamera::PowerOn() is completed. + * (V2: Called internally from HandleEvent) + */ + virtual void PowerOnComplete(TInt aError); + + /** + * From MCameraObserver. + * Gets called when CCamera::StartViewFinderBitmapsL() is completed. + * (V2: Called internally from ViewFinderReady) + */ + virtual void ViewFinderFrameReady( CFbsBitmap& aFrame ); + + /** + * From MCameraObserver. + * Gets called when CCamera::CaptureImage() is completed. + */ + virtual void ImageReady( CFbsBitmap* aBitmap, HBufC8* aData, TInt aError ); + + /** + * From MCameraObserver. + * Video capture not implemented. + */ + virtual void FrameBufferReady( MFrameBuffer* /*aFrameBuffer*/, TInt /*aError*/ ) {} + +protected: // MCameraObserver2 + + /** + * From MCameraObserver2 + * Camera event handler + */ + virtual void HandleEvent(const TECAMEvent &aEvent); + + /** + * From MCameraObserver2 + * Notifies the client of new viewfinder data + */ + virtual void ViewFinderReady(MCameraBuffer &aCameraBuffer, TInt aError); + + /** + * From MCameraObserver2 + * Notifies the client of a new captured image + */ + virtual void ImageBufferReady(MCameraBuffer &aCameraBuffer, TInt aError); + + /** + * From MCameraObserver2 + * Video capture not implemented. + */ + virtual void VideoBufferReady(MCameraBuffer& /*aCameraBuffer*/, TInt /*aError*/) {} + +protected: // MCamAutoFocusObserver + + /** + * From MCamAutoFocusObserver. + * Delivers notification of completion of auto focus initialisation to + * an interested party. + * @param aError Reason for completion of focus request. + */ + virtual void InitComplete( TInt aError ); + + /** + * From MCamAutoFocusObserver. + * Gets called when CCamAutoFocus::AttemptOptimisedFocusL() is + * completed. + * (V2: Called internally from HandleEvent) + */ + virtual void OptimisedFocusComplete( TInt aError ); + +private: // Internal functions + + /** + * Internal function to handle ImageReady callbacks from + * both observer (V1 & V2) interfaces + */ + void HandleImageReady(const TInt aError, const bool isBitmap); + +#ifdef ECAM_PREVIEW_API + /** + * Handle preview. Retrieve preview data and notify observer about the + * preview availability. + */ + void HandlePreview(); + + /** + * Calculate proper resolution for the SnapShot (Preview) image. + */ + TSize SelectPreviewResolution(); +#endif // ECAM_PREVIEW_API + +private: // Data + + CCamera *iCamera; + MCameraEngineObserver *iObserver; + MCameraEngineImageCaptureObserver *iImageCaptureObserver; + MAdvancedSettingsObserver *iAdvancedSettingsObserver; + MCameraViewfinderObserver *iViewfinderObserver; + MCameraPreviewObserver *iPreviewObserver; + MCameraBuffer *iViewFinderBuffer; + /* + * Following pointers are for the image buffers: + * * Makes buffering of 2 concurrent image buffers possible + */ + MCameraBuffer *iImageBuffer1; + MCameraBuffer *iImageBuffer2; + TDesC8 *iImageData1; + TDesC8 *iImageData2; + CFbsBitmap *iImageBitmap1; + CFbsBitmap *iImageBitmap2; + TInt iCameraIndex; + TInt iPriority; + TCameraEngineState iEngineState; + TCameraInfo iCameraInfo; + CCamera::TFormat iImageCaptureFormat; + TSize iCaptureResolution; + bool iNew2LImplementation; + int iLatestImageBufferIndex; // 0 = Buffer1, 1 = Buffer2 +#ifdef ECAM_PREVIEW_API + CCamera::CCameraSnapshot *iCameraSnapshot; +#endif // ECAM_PREVIEW_API +#ifdef S60_CAM_AUTOFOCUS_SUPPORT + CCamAutoFocus* iAutoFocus; + CCamAutoFocus::TAutoFocusRange iAFRange; +#endif // S60_CAM_AUTOFOCUS_SUPPORT +}; + +#endif // S60CCAMERAENGINE_H diff --git a/src/plugins/symbian/ecam/s60cameraengineobserver.h b/src/plugins/symbian/ecam/s60cameraengineobserver.h new file mode 100644 index 000000000..b1e669d70 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraengineobserver.h @@ -0,0 +1,178 @@ +/**************************************************************************** + ** + ** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 2.1 requirements + ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ +#ifndef S60CCAMERAENGINEOBSERVER_H +#define S60CCAMERAENGINEOBSERVER_H + +// FORWARD DECLARATIONS +class CFbsBitmap; +class TECAMEvent; + +enum TCameraEngineError +{ + EErrReserve, + EErrPowerOn, + EErrViewFinderReady, + EErrImageReady, + EErrPreview, + EErrAutoFocusInit, + EErrAutoFocusMode, + EErrAutoFocusArea, + EErrAutoFocusRange, + EErrAutoFocusType, + EErrOptimisedFocusComplete, +}; + +/* + * CameraEngine Observer class towards Camera AdvancedSettings + */ +class MAdvancedSettingsObserver +{ +public: + + virtual void HandleAdvancedEvent( const TECAMEvent &aEvent ) = 0; + +}; + +//============================================================================= + +/* + * CameraEngine Observer class towards Camera Control + */ +class MCameraEngineObserver +{ +public: + + /** + * Camera is ready to use for capturing images. + */ + virtual void MceoCameraReady() = 0; + + /** + * Notifies clients about errors in camera engine + * @param aErrorType type of error (see TCameraEngineError) + * @param aError Symbian system-wide error code + */ + virtual void MceoHandleError( TCameraEngineError aErrorType, TInt aError ) = 0; + +}; + +//============================================================================= + +/* + * CameraEngine Observer class towards Camera ImageCaptureSession + */ +class MCameraEngineImageCaptureObserver +{ +public: + + /** + * Camera AF lens has attained optimal focus + */ + virtual void MceoFocusComplete() = 0; + + /** + * Captured data is ready - call CCameraEngine::ReleaseImageBuffer() + * after processing/saving the data (typically, JPG-encoded image) + * @param aData Pointer to a descriptor containing a frame of camera data. + */ + virtual void MceoCapturedDataReady( TDesC8* aData ) = 0; + + /** + * Captured bitmap is ready. + * after processing/saving the image, call + * CCameraEngine::ReleaseImageBuffer() to free the bitmap. + * @param aBitmap Pointer to an FBS bitmap containing a captured image. + */ + virtual void MceoCapturedBitmapReady( CFbsBitmap* aBitmap ) = 0; + + /** + * Notifies clients about errors in camera engine + * @param aErrorType type of error (see TCameraEngineError) + * @param aError Symbian system-wide error code + */ + virtual void MceoHandleError( TCameraEngineError aErrorType, TInt aError ) = 0; + + /** + * Notifies client about other events not recognized by camera engine. + * The default implementation is empty. + * @param aEvent camera event (see MCameraObserver2::HandleEvent()) + */ + virtual void MceoHandleOtherEvent( const TECAMEvent& /*aEvent*/ ) {} +}; + +//============================================================================= + +/* + * CameraEngine Observer class towards Camera ViewFinderEngine + */ +class MCameraViewfinderObserver +{ +public: + /** + * A new viewfinder frame is ready. + * after displaying the frame, call + * CCameraEngine::ReleaseViewFinderBuffer() + * to free the bitmap. + * @param aFrame Pointer to an FBS bitmap containing a viewfinder frame. + */ + virtual void MceoViewFinderFrameReady( CFbsBitmap& aFrame ) = 0; +}; + +//============================================================================= + +#ifdef ECAM_PREVIEW_API +/* + * CameraEngine Observer class towards Camera ViewFinderEngine + */ +class MCameraPreviewObserver +{ +public: + /** + * A new preview is available. + * @param aPreview Pointer to an FBS bitmap containing a preview. + */ + virtual void MceoPreviewReady( CFbsBitmap& aPreview ) = 0; +}; +#endif // ECAM_PREVIEW_API + +#endif // CCAMERAENGINEOBSERVER_H + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp b/src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp new file mode 100644 index 000000000..9ebdd2c6e --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraexposurecontrol.cpp @@ -0,0 +1,584 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60cameraexposurecontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" + +S60CameraExposureControl::S60CameraExposureControl(QObject *parent) : + QCameraExposureControl(parent) +{ +} + +S60CameraExposureControl::S60CameraExposureControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraExposureControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_exposureMode(QCameraExposure::ExposureAuto), + m_meteringMode(QCameraExposure::MeteringMatrix) +{ + m_session = session; + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + if (m_advancedSettings) { + connect(m_advancedSettings, SIGNAL(apertureChanged()), this, SLOT(apertureChanged())); + connect(m_advancedSettings, SIGNAL(apertureRangeChanged()), this, SLOT(apertureRangeChanged())); + connect(m_advancedSettings, SIGNAL(shutterSpeedChanged()), this, SLOT(shutterSpeedChanged())); + connect(m_advancedSettings, SIGNAL(isoSensitivityChanged()), this, SLOT(isoSensitivityChanged())); + connect(m_advancedSettings, SIGNAL(evChanged()), this, SLOT(evChanged())); + } +} + +S60CameraExposureControl::~S60CameraExposureControl() +{ + m_advancedSettings = 0; +} + +void S60CameraExposureControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); + if (m_advancedSettings) { + connect(m_advancedSettings, SIGNAL(apertureChanged()), this, SLOT(apertureChanged())); + connect(m_advancedSettings, SIGNAL(apertureRangeChanged()), this, SLOT(apertureRangeChanged())); + connect(m_advancedSettings, SIGNAL(shutterSpeedChanged()), this, SLOT(shutterSpeedChanged())); + connect(m_advancedSettings, SIGNAL(isoSensitivityChanged()), this, SLOT(isoSensitivityChanged())); + connect(m_advancedSettings, SIGNAL(evChanged()), this, SLOT(evChanged())); + } +} + +void S60CameraExposureControl::apertureChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::Aperture); +} + +void S60CameraExposureControl::apertureRangeChanged() +{ + emit exposureParameterRangeChanged(QCameraExposureControl::Aperture); +} + +void S60CameraExposureControl::shutterSpeedChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ShutterSpeed); +} + +void S60CameraExposureControl::isoSensitivityChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ISO); +} + +void S60CameraExposureControl::evChanged() +{ + emit exposureParameterChanged(QCameraExposureControl::ExposureCompensation); +} + +QCameraExposure::ExposureMode S60CameraExposureControl::exposureMode() const +{ + return m_session->exposureMode(); +} + +void S60CameraExposureControl::setExposureMode(QCameraExposure::ExposureMode mode) +{ + if (isExposureModeSupported(mode)) { + m_exposureMode = mode; + m_session->setExposureMode(m_exposureMode); + return; + } + + m_session->setError(KErrNotSupported, tr("Requested exposure mode is not supported.")); +} + +bool S60CameraExposureControl::isExposureModeSupported(QCameraExposure::ExposureMode mode) const +{ + if (m_session->isExposureModeSupported(mode)) + return true; + + return false; +} + +QCameraExposure::MeteringMode S60CameraExposureControl::meteringMode() const +{ + if (m_advancedSettings) + return m_advancedSettings->meteringMode(); + + return QCameraExposure::MeteringMode(); +} + +void S60CameraExposureControl::setMeteringMode(QCameraExposure::MeteringMode mode) +{ + if (m_advancedSettings) { + if (isMeteringModeSupported(mode)) { + m_meteringMode = mode; + m_advancedSettings->setMeteringMode(mode); + return; + } + } + + m_session->setError(KErrNotSupported, tr("Requested metering mode is not supported.")); +} + +bool S60CameraExposureControl::isMeteringModeSupported(QCameraExposure::MeteringMode mode) const +{ + if (m_advancedSettings) + return m_advancedSettings->isMeteringModeSupported(mode); + + return false; +} + +bool S60CameraExposureControl::isParameterSupported(ExposureParameter parameter) const +{ + // Settings supported only if advanced settings available + if (m_advancedSettings) { + switch (parameter) { + case QCameraExposureControl::ISO: + if (m_advancedSettings->supportedIsoSensitivities().count() > 0) + return true; + else + return false; + case QCameraExposureControl::Aperture: + if (m_advancedSettings->supportedApertures().count() > 0) + return true; + else + return false; + case QCameraExposureControl::ShutterSpeed: + if (m_advancedSettings->supportedShutterSpeeds().count() > 0) + return true; + else + return false; + case QCameraExposureControl::ExposureCompensation: + if (m_advancedSettings->supportedExposureCompensationValues().count() > 0) + return true; + else + return false; + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + return false; + + default: + return false; + } + } + + return false; +} + +QVariant S60CameraExposureControl::exposureParameter(ExposureParameter parameter) const +{ + switch (parameter) { + case QCameraExposureControl::ISO: + return QVariant(isoSensitivity()); + case QCameraExposureControl::Aperture: + return QVariant(aperture()); + case QCameraExposureControl::ShutterSpeed: + return QVariant(shutterSpeed()); + case QCameraExposureControl::ExposureCompensation: + return QVariant(exposureCompensation()); + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Not supported in Symbian + return QVariant(); + + default: + // Not supported in Symbian + return QVariant(); + } +} + +QCameraExposureControl::ParameterFlags S60CameraExposureControl::exposureParameterFlags(ExposureParameter parameter) const +{ + QCameraExposureControl::ParameterFlags flags; + + /* + * ISO, ExposureCompensation: + * - Automatic/Manual + * - Read/Write + * - Discrete range + * + * Aperture, ShutterSpeed, FlashPower, FlashCompensation: + * - Not supported + */ + switch (parameter) { + case QCameraExposureControl::ISO: + case QCameraExposureControl::ExposureCompensation: + flags |= QCameraExposureControl::AutomaticValue; + break; + case QCameraExposureControl::Aperture: + case QCameraExposureControl::ShutterSpeed: + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Do nothing - no flags + break; + + default: + // Do nothing - no flags + break; + } + + return flags; +} + +QVariantList S60CameraExposureControl::supportedParameterRange(ExposureParameter parameter) const +{ + QVariantList valueList; + + if (m_advancedSettings) { + switch (parameter) { + case QCameraExposureControl::ISO: { + foreach (int iso, m_advancedSettings->supportedIsoSensitivities()) + valueList << QVariant(iso); + break; + } + case QCameraExposureControl::Aperture: { + foreach (qreal aperture, m_advancedSettings->supportedApertures()) + valueList << QVariant(aperture); + break; + } + case QCameraExposureControl::ShutterSpeed: { + foreach (qreal shutterSpeed, m_advancedSettings->supportedShutterSpeeds()) + valueList << QVariant(shutterSpeed); + break; + } + case QCameraExposureControl::ExposureCompensation: { + foreach (qreal ev, m_advancedSettings->supportedExposureCompensationValues()) + valueList << QVariant(ev); + break; + } + case QCameraExposureControl::FlashPower: + case QCameraExposureControl::FlashCompensation: + // Not supported in Symbian + break; + + default: + // Not supported in Symbian + break; + } + } + + return valueList; +} + +bool S60CameraExposureControl::setExposureParameter(ExposureParameter parameter, const QVariant& value) +{ + bool useDefaultValue = false; + + if (value.isNull()) + useDefaultValue = true; + + switch (parameter) { + case QCameraExposureControl::ISO: + if (useDefaultValue) { + setAutoIsoSensitivity(); + return false; + } + else + return setManualIsoSensitivity(value.toInt()); + + case QCameraExposureControl::Aperture: + if (useDefaultValue) { + setAutoAperture(); + return false; + } + else + return setManualAperture(value.toFloat()); + + case QCameraExposureControl::ShutterSpeed: + if (useDefaultValue) { + setAutoShutterSpeed(); + return false; + } + else + return setManualShutterSpeed(value.toFloat()); + + case QCameraExposureControl::ExposureCompensation: + if (useDefaultValue) { + setAutoExposureCompensation(); + return false; + } + else + return setManualExposureCompensation(value.toFloat()); + + case QCameraExposureControl::FlashPower: + return false; + case QCameraExposureControl::FlashCompensation: + return false; + + default: + // Not supported in Symbian + return false; + } +} + +QString S60CameraExposureControl::extendedParameterName(ExposureParameter parameter) +{ + switch (parameter) { + case QCameraExposureControl::ISO: + return QLatin1String("ISO Sensitivity"); + case QCameraExposureControl::Aperture: + return QLatin1String("Aperture"); + case QCameraExposureControl::ShutterSpeed: + return QLatin1String("Shutter Speed"); + case QCameraExposureControl::ExposureCompensation: + return QLatin1String("Exposure Compensation"); + case QCameraExposureControl::FlashPower: + return QLatin1String("Flash Power"); + case QCameraExposureControl::FlashCompensation: + return QLatin1String("Flash Compensation"); + + default: + return QString(); + } +} + +int S60CameraExposureControl::isoSensitivity() const +{ + if (m_advancedSettings) + return m_advancedSettings->isoSensitivity(); + return 0; +} + +bool S60CameraExposureControl::isIsoSensitivitySupported(const int iso) const +{ + if (m_advancedSettings && + m_advancedSettings->supportedIsoSensitivities().contains(iso)) + return true; + else + return false; +} + +bool S60CameraExposureControl::setManualIsoSensitivity(int iso) +{ + if (m_advancedSettings) { + if (isIsoSensitivitySupported(iso)) { + m_advancedSettings->setManualIsoSensitivity(iso); + return true; + } + } + + return false; +} + +void S60CameraExposureControl::setAutoIsoSensitivity() +{ + if (m_advancedSettings) + m_advancedSettings->setAutoIsoSensitivity(); +} + +qreal S60CameraExposureControl::aperture() const +{ + if (m_advancedSettings) + return m_advancedSettings->aperture(); + return 0.0; +} + +bool S60CameraExposureControl::isApertureSupported(const qreal aperture) const +{ + if (m_advancedSettings) { + QList supportedValues = m_advancedSettings->supportedApertures(); + if(supportedValues.indexOf(aperture) != -1) + return true; + } + + return false; +} + +bool S60CameraExposureControl::setManualAperture(qreal aperture) +{ + if (m_advancedSettings) { + if (isApertureSupported(aperture)) { + m_advancedSettings->setManualAperture(aperture); + return true; + } else { + QList supportedApertureValues = m_advancedSettings->supportedApertures(); + int minAperture = supportedApertureValues.first(); + int maxAperture = supportedApertureValues.last(); + + if (aperture < minAperture) { // Smaller than minimum + aperture = minAperture; + } else if (aperture > maxAperture) { // Bigger than maximum + aperture = maxAperture; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedApertureValues.count(); ++i) { + if((abs((aperture*100) - (supportedApertureValues[i]*100))) < smallestDiff) { + smallestDiff = abs((aperture*100) - (supportedApertureValues[i]*100)); + indexOfClosest = i; + } + } + aperture = supportedApertureValues[indexOfClosest]; + } + m_advancedSettings->setManualAperture(aperture); + } + } + + return false; +} + +void S60CameraExposureControl::setAutoAperture() +{ + // Not supported in Symbian +} + +qreal S60CameraExposureControl::shutterSpeed() const +{ + if (m_advancedSettings) + return m_advancedSettings->shutterSpeed(); + return 0.0; +} + +bool S60CameraExposureControl::isShutterSpeedSupported(const qreal seconds) const +{ + if (m_advancedSettings) { + QList supportedValues = m_advancedSettings->supportedShutterSpeeds(); + if(supportedValues.indexOf(seconds) != -1) + return true; + } + + return false; +} + +bool S60CameraExposureControl::setManualShutterSpeed(qreal seconds) +{ + if (m_advancedSettings) { + if (isShutterSpeedSupported(seconds)) { + m_advancedSettings->setManualShutterSpeed(seconds); + return true; + } else { + QList supportedShutterSpeeds = m_advancedSettings->supportedShutterSpeeds(); + + if (supportedShutterSpeeds.count() == 0) + return false; + + int minShutterSpeed = supportedShutterSpeeds.first(); + int maxShutterSpeed = supportedShutterSpeeds.last(); + + if (seconds < minShutterSpeed) { // Smaller than minimum + seconds = minShutterSpeed; + } else if (seconds > maxShutterSpeed) { // Bigger than maximum + seconds = maxShutterSpeed; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedShutterSpeeds.count(); ++i) { + if((abs((seconds*100) - (supportedShutterSpeeds[i]*100))) < smallestDiff) { + smallestDiff = abs((seconds*100) - (supportedShutterSpeeds[i]*100)); + indexOfClosest = i; + } + } + seconds = supportedShutterSpeeds[indexOfClosest]; + } + m_advancedSettings->setManualShutterSpeed(seconds); + } + } + + return false; +} + +void S60CameraExposureControl::setAutoShutterSpeed() +{ + // Not supported in Symbian +} + +qreal S60CameraExposureControl::exposureCompensation() const +{ + if (m_advancedSettings) + return m_advancedSettings->exposureCompensation(); + return 0.0; +} + +bool S60CameraExposureControl::isExposureCompensationSupported(const qreal ev) const +{ + if (m_advancedSettings) { + QList supportedValues = m_advancedSettings->supportedExposureCompensationValues(); + if(supportedValues.indexOf(ev) != -1) + return true; + } + + return false; +} + +bool S60CameraExposureControl::setManualExposureCompensation(qreal ev) +{ + if (m_advancedSettings) { + if (isExposureCompensationSupported(ev)) { + m_advancedSettings->setExposureCompensation(ev); + return true; + } else { + QList supportedEVs = m_advancedSettings->supportedExposureCompensationValues(); + + if (supportedEVs.count() == 0) + return false; + + int minEV = supportedEVs.first(); + int maxEV = supportedEVs.last(); + + if (ev < minEV) { // Smaller than minimum + ev = minEV; + } else if (ev > maxEV) { // Bigger than maximum + ev = maxEV; + } else { // Find closest + int indexOfClosest = 0; + int smallestDiff = 100000000; // Sensible max diff + for(int i = 0; i < supportedEVs.count(); ++i) { + if((abs((ev*100) - (supportedEVs[i]*100))) < smallestDiff) { + smallestDiff = abs((ev*100) - (supportedEVs[i]*100)); + indexOfClosest = i; + } + } + ev = supportedEVs[indexOfClosest]; + } + m_advancedSettings->setExposureCompensation(ev); + } + } + + return false; +} + +void S60CameraExposureControl::setAutoExposureCompensation() +{ + // Not supported in Symbian +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraexposurecontrol.h b/src/plugins/symbian/ecam/s60cameraexposurecontrol.h new file mode 100644 index 000000000..1c623c774 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraexposurecontrol.h @@ -0,0 +1,138 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAEXPOSURECONTROL_H +#define S60CAMERAEXPOSURECONTROL_H + +#include + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control for exposure related camera operation. + */ +class S60CameraExposureControl : public QCameraExposureControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraExposureControl(QObject *parent = 0); + S60CameraExposureControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraExposureControl(); + +public: // QCameraExposureControl + + // Exposure Mode + QCameraExposure::ExposureMode exposureMode() const; + void setExposureMode(QCameraExposure::ExposureMode mode); + bool isExposureModeSupported(QCameraExposure::ExposureMode mode) const; + + // Metering Mode + QCameraExposure::MeteringMode meteringMode() const; + void setMeteringMode(QCameraExposure::MeteringMode mode); + bool isMeteringModeSupported(QCameraExposure::MeteringMode mode) const; + + // Exposure Parameter + bool isParameterSupported(ExposureParameter parameter) const; + QVariant exposureParameter(ExposureParameter parameter) const; + QCameraExposureControl::ParameterFlags exposureParameterFlags(ExposureParameter parameter) const; + QVariantList supportedParameterRange(ExposureParameter parameter) const; + bool setExposureParameter(ExposureParameter parameter, const QVariant& value); + + QString extendedParameterName(ExposureParameter parameter); + +/* +Q_SIGNALS: // QCameraExposureControl + void exposureParameterChanged(int parameter); + void exposureParameterRangeChanged(int parameter); +*/ + +private slots: // Internal Slots + + void resetAdvancedSetting(); + void apertureChanged(); + void apertureRangeChanged(); + void shutterSpeedChanged(); + void isoSensitivityChanged(); + void evChanged(); + +private: // Internal - Implementing ExposureParameter + + // ISO Sensitivity + int isoSensitivity() const; + bool setManualIsoSensitivity(int iso); + void setAutoIsoSensitivity(); + bool isIsoSensitivitySupported(const int iso) const; + + // Aperture + qreal aperture() const; + bool setManualAperture(qreal aperture); + void setAutoAperture(); + bool isApertureSupported(const qreal aperture) const; + + // Shutter Speed + qreal shutterSpeed() const; + bool setManualShutterSpeed(qreal seconds); + void setAutoShutterSpeed(); + bool isShutterSpeedSupported(const qreal seconds) const; + + // Exposure Compensation + qreal exposureCompensation() const; + bool setManualExposureCompensation(qreal ev); + void setAutoExposureCompensation(); + bool isExposureCompensationSupported(const qreal ev) const; + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + QCameraExposure::ExposureMode m_exposureMode; + QCameraExposure::MeteringMode m_meteringMode; +}; + +#endif // S60CAMERAEXPOSURECONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraflashcontrol.cpp b/src/plugins/symbian/ecam/s60cameraflashcontrol.cpp new file mode 100644 index 000000000..a18c57a03 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraflashcontrol.cpp @@ -0,0 +1,109 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60cameraflashcontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" + +S60CameraFlashControl::S60CameraFlashControl(QObject *parent) : + QCameraFlashControl(parent) +{ +} + +S60CameraFlashControl::S60CameraFlashControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraFlashControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_flashMode(QCameraExposure::FlashOff) +{ + m_session = session; + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(flashReady(bool)), this, SIGNAL(flashReady(bool))); +} + +S60CameraFlashControl::~S60CameraFlashControl() +{ + m_advancedSettings = 0; +} + +void S60CameraFlashControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(flashReady(bool)), this, SIGNAL(flashReady(bool))); +} + +QCameraExposure::FlashModes S60CameraFlashControl::flashMode() const +{ + return m_session->flashMode(); +} + +void S60CameraFlashControl::setFlashMode(QCameraExposure::FlashModes mode) +{ + if (isFlashModeSupported(mode)) { + m_flashMode = mode; + m_session->setFlashMode(m_flashMode); + } + else + m_session->setError(KErrNotSupported, tr("Requested flash mode is not supported.")); +} + +bool S60CameraFlashControl::isFlashModeSupported(QCameraExposure::FlashModes mode) const +{ + return m_session->supportedFlashModes() & mode; +} + +bool S60CameraFlashControl::isFlashReady() const +{ + if (m_advancedSettings) + return m_advancedSettings->isFlashReady(); + + return false; +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraflashcontrol.h b/src/plugins/symbian/ecam/s60cameraflashcontrol.h new file mode 100644 index 000000000..50dbc41dc --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraflashcontrol.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAFLASHCONTROL_H +#define S60CAMERAFLASHCONTROL_H + +#include + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control to setup Flash related camera settings. + */ +class S60CameraFlashControl : public QCameraFlashControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraFlashControl(QObject *parent = 0); + S60CameraFlashControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraFlashControl(); + +public: // QCameraExposureControl + + // Flash Mode + QCameraExposure::FlashModes flashMode() const; + void setFlashMode(QCameraExposure::FlashModes mode); + bool isFlashModeSupported(QCameraExposure::FlashModes mode) const; + + bool isFlashReady() const; + +/* +Q_SIGNALS: // QCameraExposureControl + void flashReady(bool); +*/ + +private slots: // Internal Slots + + void resetAdvancedSetting(); + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + QCameraExposure::FlashModes m_flashMode; +}; + +#endif // S60CAMERAFLASHCONTROL_H diff --git a/src/plugins/symbian/ecam/s60camerafocuscontrol.cpp b/src/plugins/symbian/ecam/s60camerafocuscontrol.cpp new file mode 100644 index 000000000..a7941ce20 --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerafocuscontrol.cpp @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60camerafocuscontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" +#include "s60cameraconstants.h" + +S60CameraFocusControl::S60CameraFocusControl(QObject *parent) : + QCameraFocusControl(parent) +{ +} + +S60CameraFocusControl::S60CameraFocusControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraFocusControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_isFocusLocked(false), + m_opticalZoomValue(KDefaultOpticalZoom), + m_digitalZoomValue(KDefaultDigitalZoom), + m_focusMode(KDefaultFocusMode) +{ + m_session = session; + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + TRAPD(err, m_session->doSetZoomFactorL(m_opticalZoomValue, m_digitalZoomValue)); + if (err) + m_session->setError(KErrNotSupported, tr("Setting default zoom factors failed.")); +} + +S60CameraFocusControl::~S60CameraFocusControl() +{ +} + +QCameraFocus::FocusMode S60CameraFocusControl::focusMode() const +{ + return m_focusMode; +} + +void S60CameraFocusControl::setFocusMode(QCameraFocus::FocusMode mode) +{ + if (isFocusModeSupported(mode)) { + // FocusMode and FocusRange are set. Focusing is triggered by setting + // the corresponding FocusType active by calling searchAndLock in LocksControl. + m_focusMode = mode; + if (m_advancedSettings) + m_advancedSettings->setFocusMode(m_focusMode); + else + m_session->setError(KErrGeneral, tr("Unable to set focus mode before camera is started.")); + } else { + m_session->setError(KErrNotSupported, tr("Requested focus mode is not supported.")); + } +} + +bool S60CameraFocusControl::isFocusModeSupported(QCameraFocus::FocusMode mode) const +{ + if (m_advancedSettings) { + return m_advancedSettings->supportedFocusModes() & mode; + } else { + if (mode == QCameraFocus::AutoFocus) + return m_session->isFocusSupported(); + } + + return false; +} + +qreal S60CameraFocusControl::maximumOpticalZoom() const +{ + return m_session->maximumZoom(); +} + +qreal S60CameraFocusControl::maximumDigitalZoom() const +{ + return m_session->maxDigitalZoom(); +} + +qreal S60CameraFocusControl::opticalZoom() const +{ + return m_session->opticalZoomFactor(); +} + +qreal S60CameraFocusControl::digitalZoom() const +{ + return m_session->digitalZoomFactor(); +} + +void S60CameraFocusControl::zoomTo(qreal optical, qreal digital) +{ + TRAPD(err, m_session->doSetZoomFactorL(optical, digital)); + if (err) + m_session->setError(KErrNotSupported, tr("Requested zoom factor is not supported.")); + + // Query new values + if (m_opticalZoomValue != m_session->opticalZoomFactor()) { + m_opticalZoomValue = m_session->opticalZoomFactor(); + emit opticalZoomChanged(m_opticalZoomValue); + } + if (m_digitalZoomValue != m_session->digitalZoomFactor()) { + m_digitalZoomValue = m_session->digitalZoomFactor(); + emit digitalZoomChanged(m_digitalZoomValue); + } +} + +void S60CameraFocusControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); +} + +QCameraFocus::FocusPointMode S60CameraFocusControl::focusPointMode() const +{ + // Not supported in Symbian + return QCameraFocus::FocusPointAuto; +} + +void S60CameraFocusControl::setFocusPointMode(QCameraFocus::FocusPointMode mode) +{ + if (mode != QCameraFocus::FocusPointAuto) + m_session->setError(KErrNotSupported, tr("Requested focus point mode is not supported.")); +} + +bool S60CameraFocusControl::isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const +{ + // Not supported in Symbian + if (mode == QCameraFocus::FocusPointAuto) + return true; + else + return false; +} + +QPointF S60CameraFocusControl::customFocusPoint() const +{ + // Not supported in Symbian, return image center + return QPointF(0.5, 0.5); +} + +void S60CameraFocusControl::setCustomFocusPoint(const QPointF &point) +{ + // Not supported in Symbian + Q_UNUSED(point); + m_session->setError(KErrNotSupported, tr("Setting custom focus point is not supported.")); +} + +QCameraFocusZoneList S60CameraFocusControl::focusZones() const +{ + // Not supported in Symbian + return QCameraFocusZoneList(); // Return empty list +} + +// End of file + diff --git a/src/plugins/symbian/ecam/s60camerafocuscontrol.h b/src/plugins/symbian/ecam/s60camerafocuscontrol.h new file mode 100644 index 000000000..28c83ed70 --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerafocuscontrol.h @@ -0,0 +1,112 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAFOCUSCONTROL_H +#define S60CAMERAFOCUSCONTROL_H + +#include + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control for focusing related operations (inc. zooming) + */ +class S60CameraFocusControl : public QCameraFocusControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraFocusControl(QObject *parent = 0); + S60CameraFocusControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraFocusControl(); + +public: // QCameraFocusControl + + // Focus Mode + QCameraFocus::FocusMode focusMode() const; + void setFocusMode(QCameraFocus::FocusMode mode); + bool isFocusModeSupported(QCameraFocus::FocusMode) const; + + // Zoom + qreal maximumOpticalZoom() const; + qreal maximumDigitalZoom() const; + qreal opticalZoom() const; + qreal digitalZoom() const; + + void zoomTo(qreal optical, qreal digital); + + // Focus Point + QCameraFocus::FocusPointMode focusPointMode() const; + void setFocusPointMode(QCameraFocus::FocusPointMode mode); + bool isFocusPointModeSupported(QCameraFocus::FocusPointMode mode) const; + QPointF customFocusPoint() const; + void setCustomFocusPoint(const QPointF &point); + + QCameraFocusZoneList focusZones() const; + +/* +Q_SIGNALS: // QCameraFocusControl + void opticalZoomChanged(qreal opticalZoom); + void digitalZoomChanged(qreal digitalZoom); + void focusZonesChanged(); +*/ + +private slots: // Internal Slots + + void resetAdvancedSetting(); + +private: // Data + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + bool m_isFocusLocked; + qreal m_opticalZoomValue; + qreal m_digitalZoomValue; + QCameraFocus::FocusMode m_focusMode; +}; + +#endif // S60CAMERAFOCUSCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp new file mode 100644 index 000000000..427a3bd97 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.cpp @@ -0,0 +1,124 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60cameraimagecapturecontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" +#include "s60cameracontrol.h" + +S60CameraImageCaptureControl::S60CameraImageCaptureControl(QObject *parent) : + QCameraImageCaptureControl(parent) +{ +} + +S60CameraImageCaptureControl::S60CameraImageCaptureControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent) : + QCameraImageCaptureControl(parent), + m_driveMode(QCameraImageCapture::SingleImageCapture) // Default DriveMode +{ + m_session = session; + m_service = service; + m_cameraControl = qobject_cast(m_service->requestControl(QCameraControl_iid)); + + if (!m_cameraControl) + m_session->setError(KErrGeneral, tr("Unexpected camera error.")); + + // Chain these signals from session class + connect(m_session, SIGNAL(imageCaptured(const int, QImage)), + this, SIGNAL(imageCaptured(const int, QImage))); + connect(m_session, SIGNAL(readyForCaptureChanged(bool)), + this, SIGNAL(readyForCaptureChanged(bool)), Qt::QueuedConnection); + connect(m_session, SIGNAL(imageSaved(const int, const QString&)), + this, SIGNAL(imageSaved(const int, const QString&))); + connect(m_session, SIGNAL(imageExposed(int)), + this, SIGNAL(imageExposed(int))); + connect(m_session, SIGNAL(captureError(int, int, const QString&)), + this, SIGNAL(error(int, int, const QString&))); +} + +S60CameraImageCaptureControl::~S60CameraImageCaptureControl() +{ +} + +bool S60CameraImageCaptureControl::isReadyForCapture() const +{ + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureStillImage) + return false; + + return m_session->isDeviceReady(); +} + +QCameraImageCapture::DriveMode S60CameraImageCaptureControl::driveMode() const +{ + return m_driveMode; +} + +void S60CameraImageCaptureControl::setDriveMode(QCameraImageCapture::DriveMode mode) +{ + if (mode != QCameraImageCapture::SingleImageCapture) { + emit error((m_session->currentImageId() + 1), QCamera::NotSupportedFeatureError, tr("DriveMode not supported.")); + return; + } + + m_driveMode = mode; +} + +int S60CameraImageCaptureControl::capture(const QString &fileName) +{ + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureStillImage) { + emit error((m_session->currentImageId() + 1), QCameraImageCapture::NotReadyError, tr("Incorrect CaptureMode.")); + return 0; + } + + int imageId = m_session->capture(fileName); + + return imageId; +} + +void S60CameraImageCaptureControl::cancelCapture() +{ + m_session->cancelCapture(); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h new file mode 100644 index 000000000..4c369e807 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimagecapturecontrol.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAIMAGECAPTURECONTROL_H +#define S60CAMERAIMAGECAPTURECONTROL_H + +#include "qcameraimagecapturecontrol.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; +class S60CameraControl; + +/* + * Control for image capture operations. + */ +class S60CameraImageCaptureControl : public QCameraImageCaptureControl +{ + Q_OBJECT + +public: // Contructors & Destrcutor + + S60CameraImageCaptureControl(QObject *parent = 0); + S60CameraImageCaptureControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent = 0); + ~S60CameraImageCaptureControl(); + +public: // QCameraImageCaptureControl + + bool isReadyForCapture() const; + + // Drive Mode + QCameraImageCapture::DriveMode driveMode() const; + void setDriveMode(QCameraImageCapture::DriveMode mode); + + // Capture + int capture(const QString &fileName); + void cancelCapture(); + +/* +Q_SIGNALS: // QCameraImageCaptureControl + void readyForCaptureChanged(bool); + + void imageExposed(int id); + void imageCaptured(int id, const QImage &preview); + void imageSaved(int id, const QString &fileName); + + void error(int id, int error, const QString &errorString); +*/ + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraControl *m_cameraControl; + QCameraImageCapture::DriveMode m_driveMode; +}; + +#endif // S60CAMERAIMAGECAPTURECONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp new file mode 100644 index 000000000..ae2c4535a --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.cpp @@ -0,0 +1,254 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60cameraimageprocessingcontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" + +S60CameraImageProcessingControl::S60CameraImageProcessingControl(QObject *parent) : + QCameraImageProcessingControl(parent) +{ +} + +S60CameraImageProcessingControl::S60CameraImageProcessingControl(S60ImageCaptureSession *session, QObject *parent) : + QCameraImageProcessingControl(parent), + m_session(0), + m_advancedSettings(0) +{ + m_session = session; + m_advancedSettings = m_session->advancedSettings(); +} + +S60CameraImageProcessingControl::~S60CameraImageProcessingControl() +{ + m_advancedSettings = 0; +} + +void S60CameraImageProcessingControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); +} + +QCameraImageProcessing::WhiteBalanceMode S60CameraImageProcessingControl::whiteBalanceMode() const +{ + return m_session->whiteBalanceMode(); +} + +void S60CameraImageProcessingControl::setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode) +{ + if (isWhiteBalanceModeSupported(mode)) + m_session->setWhiteBalanceMode(mode); + else + m_session->setError(KErrNotSupported, tr("Requested white balance mode is not supported.")); +} + +bool S60CameraImageProcessingControl::isWhiteBalanceModeSupported( + QCameraImageProcessing::WhiteBalanceMode mode) const +{ + return m_session->isWhiteBalanceModeSupported(mode); +} + +int S60CameraImageProcessingControl::manualWhiteBalance() const +{ + return 0; +} + +void S60CameraImageProcessingControl::setManualWhiteBalance(int colorTemperature) +{ + m_session->setError(KErrNotSupported, tr("Setting manual white balance is not supported.")); + Q_UNUSED(colorTemperature) +} + +bool S60CameraImageProcessingControl::isProcessingParameterSupported(ProcessingParameter parameter) const +{ + // First check settings requiring Adv. Settings + if (m_advancedSettings) { + switch (parameter) { + case QCameraImageProcessingControl::Saturation: + return true; + case QCameraImageProcessingControl::Sharpening: + return isSharpeningSupported(); + case QCameraImageProcessingControl::Denoising: + return isDenoisingSupported(); + case QCameraImageProcessingControl::ColorTemperature: + return false; + } + } + + // Then the rest + switch (parameter) { + case QCameraImageProcessingControl::Contrast: + case QCameraImageProcessingControl::Brightness: + return true; + + default: + return false; + } +} + +QVariant S60CameraImageProcessingControl::processingParameter( + QCameraImageProcessingControl::ProcessingParameter parameter) const +{ + switch (parameter) { + case QCameraImageProcessingControl::Contrast: + return QVariant(contrast()); + case QCameraImageProcessingControl::Saturation: + return QVariant(saturation()); + case QCameraImageProcessingControl::Brightness: + return QVariant(brightness()); + case QCameraImageProcessingControl::Sharpening: + return QVariant(sharpeningLevel()); + case QCameraImageProcessingControl::Denoising: + return QVariant(denoisingLevel()); + case QCameraImageProcessingControl::ColorTemperature: + return QVariant(manualWhiteBalance()); + + default: + return QVariant(); + } +} + +void S60CameraImageProcessingControl::setProcessingParameter( + QCameraImageProcessingControl::ProcessingParameter parameter, QVariant value) +{ + switch (parameter) { + case QCameraImageProcessingControl::Contrast: + setContrast(value.toInt()); + break; + case QCameraImageProcessingControl::Saturation: + setSaturation(value.toInt()); + break; + case QCameraImageProcessingControl::Brightness: + setBrightness(value.toInt()); + break; + case QCameraImageProcessingControl::Sharpening: + if (isSharpeningSupported()) + setSharpeningLevel(value.toInt()); + break; + case QCameraImageProcessingControl::Denoising: + if (isDenoisingSupported()) + setDenoisingLevel(value.toInt()); + break; + case QCameraImageProcessingControl::ColorTemperature: + setManualWhiteBalance(value.toInt()); + break; + + default: + break; + } +} + +void S60CameraImageProcessingControl::setContrast(int value) +{ + m_session->setContrast(value); +} + +int S60CameraImageProcessingControl::contrast() const +{ + return m_session->contrast(); +} + +void S60CameraImageProcessingControl::setBrightness(int value) +{ + m_session->setBrightness(value); +} + +int S60CameraImageProcessingControl::brightness() const +{ + return m_session->brightness(); +} + +void S60CameraImageProcessingControl::setSaturation(int value) +{ + if (m_advancedSettings) + m_advancedSettings->setSaturation(value); + else + m_session->setError(KErrNotSupported, tr("Setting saturation is not supported.")); +} + +int S60CameraImageProcessingControl::saturation() const +{ + if (m_advancedSettings) + return m_advancedSettings->saturation(); + return 0; +} + +void S60CameraImageProcessingControl::setDenoisingLevel(int value) +{ + m_session->setError(KErrNotSupported, tr("Setting denoising level is not supported.")); + Q_UNUSED(value); // Not supported for Symbian +} + +bool S60CameraImageProcessingControl::isDenoisingSupported() const +{ + return false; // Not supported for Symbian +} + +int S60CameraImageProcessingControl::denoisingLevel() const +{ + return 0; // Not supported for Symbian +} + +void S60CameraImageProcessingControl::setSharpeningLevel(int value) +{ + if (m_advancedSettings) + m_advancedSettings->setSharpeningLevel(value); + else + m_session->setError(KErrNotSupported, tr("Setting sharpening level is not supported.")); +} + +bool S60CameraImageProcessingControl::isSharpeningSupported() const +{ + if (m_advancedSettings) + return m_advancedSettings->isSharpeningSupported(); + return false; +} + +int S60CameraImageProcessingControl::sharpeningLevel() const +{ + if (m_advancedSettings) + return m_advancedSettings->sharpeningLevel(); + return 0; +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h new file mode 100644 index 000000000..7fc6b8900 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraimageprocessingcontrol.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERAIMAGEPROCESSINGCONTROL_H +#define S60CAMERAIMAGEPROCESSINGCONTROL_H + +#include +#include + +#include "s60camerasettings.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; + +/* + * Control for image processing related camera operations (inc. white balance). + */ +class S60CameraImageProcessingControl : public QCameraImageProcessingControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60CameraImageProcessingControl(QObject *parent = 0); + S60CameraImageProcessingControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60CameraImageProcessingControl(); + +public: // QCameraImageProcessingControl + + // White Balance + QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode() const; + void setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode); + bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const; + + // Processing Parameter + bool isProcessingParameterSupported(ProcessingParameter parameter) const; + QVariant processingParameter(QCameraImageProcessingControl::ProcessingParameter parameter) const; + void setProcessingParameter(QCameraImageProcessingControl::ProcessingParameter parameter, QVariant value); + +private slots: // Internal Slots + + void resetAdvancedSetting(); + +private: // Internal operations - Implementing ProcessingParameter + + // Manual White Balance (Color Temperature) + int manualWhiteBalance() const; + void setManualWhiteBalance(int colorTemperature); + + // Contrast + int contrast() const; + void setContrast(int value); + + // Brightness + int brightness() const; + void setBrightness(int value); + + // Saturation + int saturation() const; + void setSaturation(int value); + + // Sharpening + bool isSharpeningSupported() const; + int sharpeningLevel() const; + void setSharpeningLevel(int value); + + // Denoising + bool isDenoisingSupported() const; + int denoisingLevel() const; + void setDenoisingLevel(int value); + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraSettings *m_advancedSettings; +}; + +#endif // S60CAMERAIMAGEPROCESSINGCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameralockscontrol.cpp b/src/plugins/symbian/ecam/s60cameralockscontrol.cpp new file mode 100644 index 000000000..cce030f22 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameralockscontrol.cpp @@ -0,0 +1,263 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include // FocusMode + +#include "s60cameralockscontrol.h" +#include "s60cameraservice.h" +#include "s60imagecapturesession.h" +#include "s60camerasettings.h" +#include "s60camerafocuscontrol.h" + +S60CameraLocksControl::S60CameraLocksControl(QObject *parent) : + QCameraLocksControl(parent) +{ +} + +S60CameraLocksControl::S60CameraLocksControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent) : + QCameraLocksControl(parent), + m_session(0), + m_service(0), + m_advancedSettings(0), + m_focusControl(0), + m_focusStatus(QCamera::Unlocked), + m_exposureStatus(QCamera::Unlocked), + m_whiteBalanceStatus(QCamera::Unlocked) +{ + m_session = session; + m_service = service; + m_focusControl = qobject_cast(m_service->requestControl(QCameraFocusControl_iid)); + + connect(m_session, SIGNAL(advancedSettingChanged()), this, SLOT(resetAdvancedSetting())); + m_advancedSettings = m_session->advancedSettings(); + + // Exposure Lock Signals + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + + // Focus Lock Signal + // * S60 3.2 and later (through Adv. Settings) + if (m_advancedSettings) + connect(m_advancedSettings, SIGNAL(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + // * S60 3.1 (through ImageSession) + connect(m_session, SIGNAL(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); +} + +S60CameraLocksControl::~S60CameraLocksControl() +{ + m_advancedSettings = 0; +} + +QCamera::LockTypes S60CameraLocksControl::supportedLocks() const +{ + QCamera::LockTypes supportedLocks = 0; + +#ifdef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.1 + if (m_session) + if (m_session->isFocusSupported()) + supportedLocks |= QCamera::LockFocus; +#else // S60 3.2 and later + if (m_advancedSettings) { + QCameraFocus::FocusModes supportedFocusModes = m_advancedSettings->supportedFocusModes(); + if (supportedFocusModes & QCameraFocus::AutoFocus) + supportedLocks |= QCamera::LockFocus; + + // Exposure/WhiteBalance Locking not implemented in Symbian + // supportedLocks |= QCamera::LockExposure; + // supportedLocks |= QCamera::LockWhiteBalance; + } +#endif // S60_CAM_AUTOFOCUS_SUPPORT + + return supportedLocks; +} + +QCamera::LockStatus S60CameraLocksControl::lockStatus(QCamera::LockType lock) const +{ + switch (lock) { + case QCamera::LockExposure: + return m_exposureStatus; + case QCamera::LockWhiteBalance: + return m_whiteBalanceStatus; + case QCamera::LockFocus: + return m_focusStatus; + + default: + // Unsupported lock + return QCamera::Unlocked; + } +} + +void S60CameraLocksControl::searchAndLock(QCamera::LockTypes locks) +{ + if (locks & QCamera::LockExposure) { + // Not implemented in Symbian + //startExposureLocking(); + } + if (locks & QCamera::LockWhiteBalance) { + // Not implemented in Symbian + } + if (locks & QCamera::LockFocus) + startFocusing(); +} + +void S60CameraLocksControl::unlock(QCamera::LockTypes locks) +{ + if (locks & QCamera::LockExposure) { + // Not implemented in Symbian + //cancelExposureLocking(); + } + + if (locks & QCamera::LockFocus) + cancelFocusing(); +} + +void S60CameraLocksControl::resetAdvancedSetting() +{ + m_advancedSettings = m_session->advancedSettings(); + + // Reconnect Lock Signals + if (m_advancedSettings) { + connect(m_advancedSettings, SIGNAL(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + connect(m_advancedSettings, SIGNAL(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason)), + this, SLOT(focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason))); + } +} + +void S60CameraLocksControl::exposureStatusChanged(QCamera::LockStatus status, + QCamera::LockChangeReason reason) +{ + if(status != m_exposureStatus) { + m_exposureStatus = status; + emit lockStatusChanged(QCamera::LockExposure, status, reason); + } +} + +void S60CameraLocksControl::focusStatusChanged(QCamera::LockStatus status, + QCamera::LockChangeReason reason) +{ + if(status != m_focusStatus) { + m_focusStatus = status; + emit lockStatusChanged(QCamera::LockFocus, status, reason); + } +} + +void S60CameraLocksControl::startFocusing() +{ +#ifndef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.2 or later + // Focusing is triggered on Symbian by setting the FocusType corresponding + // to the FocusMode set to FocusControl + if (m_focusControl) { + if (m_advancedSettings) { + m_advancedSettings->startFocusing(); + m_focusStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Searching, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); + +#else // S60 3.1 + if (m_focusControl && m_focusControl->focusMode() == QCameraFocus::AutoFocus) { + m_session->startFocus(); + m_focusStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Searching, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); +#endif // S60_CAM_AUTOFOCUS_SUPPORT +} + +void S60CameraLocksControl::cancelFocusing() +{ + if (m_focusStatus == QCamera::Unlocked) + return; + +#ifndef S60_CAM_AUTOFOCUS_SUPPORT // S60 3.2 or later + if (m_advancedSettings) { + m_advancedSettings->cancelFocusing(); + m_focusStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::LockFailed); + +#else // S60 3.1 + m_session->cancelFocus(); + m_focusStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockFocus, QCamera::Unlocked, QCamera::UserRequest); +#endif // S60_CAM_AUTOFOCUS_SUPPORT +} + +void S60CameraLocksControl::startExposureLocking() +{ + if (m_advancedSettings) { + m_advancedSettings->lockExposure(true); + m_exposureStatus = QCamera::Searching; + emit lockStatusChanged(QCamera::LockExposure, QCamera::Searching, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::LockFailed); +} + +void S60CameraLocksControl::cancelExposureLocking() +{ + if (m_exposureStatus == QCamera::Unlocked) + return; + + if (m_advancedSettings) { + m_advancedSettings->lockExposure(false); + m_exposureStatus = QCamera::Unlocked; + emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::UserRequest); + } + else + emit lockStatusChanged(QCamera::LockExposure, QCamera::Unlocked, QCamera::LockFailed); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameralockscontrol.h b/src/plugins/symbian/ecam/s60cameralockscontrol.h new file mode 100644 index 000000000..3b49cbaba --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameralockscontrol.h @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERALOCKSCONTROL_H +#define S60CAMERALOCKSCONTROL_H + +#include +#include "qcameralockscontrol.h" + +QT_USE_NAMESPACE + +class S60CameraService; +class S60ImageCaptureSession; +class S60CameraSettings; +class S60CameraFocusControl; + +/* + * Control for searching and locking 3A algorithms (AutoFocus, AutoExposure + * and AutoWhitebalance). + */ +class S60CameraLocksControl : public QCameraLocksControl +{ + Q_OBJECT + +public: // Contructors & Destrcutor + + S60CameraLocksControl(QObject *parent = 0); + S60CameraLocksControl(S60CameraService *service, + S60ImageCaptureSession *session, + QObject *parent = 0); + ~S60CameraLocksControl(); + +public: // QCameraLocksControl + + QCamera::LockTypes supportedLocks() const; + + QCamera::LockStatus lockStatus(QCamera::LockType lock) const; + + void searchAndLock(QCamera::LockTypes locks); + void unlock(QCamera::LockTypes locks); + +/* +Q_SIGNALS: // QCameraLocksControl + + void lockStatusChanged(QCamera::LockType type, + QCamera::LockStatus status, + QCamera::LockChangeReason reason); +*/ + +private slots: // Internal Slots + + void exposureStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason); + void focusStatusChanged(QCamera::LockStatus status, QCamera::LockChangeReason reason); + void resetAdvancedSetting(); + +private: // Internal + + // Focus + void startFocusing(); + void cancelFocusing(); + + // Exposure + void startExposureLocking(); + void cancelExposureLocking(); + +private: // Data + + S60ImageCaptureSession *m_session; + S60CameraService *m_service; + S60CameraSettings *m_advancedSettings; + S60CameraFocusControl *m_focusControl; + QCamera::LockStatus m_focusStatus; + QCamera::LockStatus m_exposureStatus; + QCamera::LockStatus m_whiteBalanceStatus; +}; + +#endif // S60CAMERALOCKSCONTROL_H diff --git a/src/plugins/symbian/ecam/s60cameraservice.cpp b/src/plugins/symbian/ecam/s60cameraservice.cpp new file mode 100644 index 000000000..5cc4485c1 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraservice.cpp @@ -0,0 +1,259 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "s60cameraservice.h" +#include "s60cameracontrol.h" +#include "s60videodevicecontrol.h" +#include "s60camerafocuscontrol.h" +#include "s60cameraexposurecontrol.h" +#include "s60cameraflashcontrol.h" +#include "s60cameraimageprocessingcontrol.h" +#include "s60cameraimagecapturecontrol.h" +#include "s60mediarecordercontrol.h" +#include "s60videocapturesession.h" +#include "s60imagecapturesession.h" +#include "s60videowidgetcontrol.h" +#include "s60mediacontainercontrol.h" +#include "s60videoencodercontrol.h" +#include "s60audioencodercontrol.h" +#include "s60imageencodercontrol.h" +#include "s60cameralockscontrol.h" +#include "s60videorenderercontrol.h" +#include "s60videowindowcontrol.h" + +#include "s60cameraviewfinderengine.h" // ViewfinderOutputType + +S60CameraService::S60CameraService(QObject *parent) : + QMediaService(parent) +{ + // Session classes for video and image capturing + m_imagesession = new S60ImageCaptureSession(this); + m_videosession = new S60VideoCaptureSession(this); + + if (m_imagesession && m_videosession) { + // Different control classes implementing the Camera API + m_control = new S60CameraControl(m_videosession, m_imagesession, this); + m_videoDeviceControl = new S60VideoDeviceControl(m_control, this); + m_focusControl = new S60CameraFocusControl(m_imagesession, this); + m_exposureControl = new S60CameraExposureControl(m_imagesession, this); + m_flashControl = new S60CameraFlashControl(m_imagesession, this); + m_imageProcessingControl = new S60CameraImageProcessingControl(m_imagesession, this); + m_imageCaptureControl = new S60CameraImageCaptureControl(this, m_imagesession, this); + m_media = new S60MediaRecorderControl(this, m_videosession, this); + m_mediaFormat = new S60MediaContainerControl(m_videosession, this); + m_videoEncoder = new S60VideoEncoderControl(m_videosession, this); + m_audioEncoder = new S60AudioEncoderControl(m_videosession, this); + m_viewFinderWidget = new S60VideoWidgetControl(this); + m_imageEncoderControl = new S60ImageEncoderControl(m_imagesession, this); + m_locksControl = new S60CameraLocksControl(this, m_imagesession, this); + m_rendererControl = new S60VideoRendererControl(this); + m_windowControl = new S60VideoWindowControl(this); + } +} + +S60CameraService::~S60CameraService() +{ + // Delete controls + if (m_videoDeviceControl) + delete m_videoDeviceControl; + if (m_focusControl) + delete m_focusControl; + if (m_exposureControl) + delete m_exposureControl; + if (m_flashControl) + delete m_flashControl; + if (m_imageProcessingControl) + delete m_imageProcessingControl; + if (m_imageCaptureControl) + delete m_imageCaptureControl; + if (m_media) + delete m_media; + if (m_mediaFormat) + delete m_mediaFormat; + if (m_videoEncoder) + delete m_videoEncoder; + if (m_audioEncoder) + delete m_audioEncoder; + if (m_imageEncoderControl) + delete m_imageEncoderControl; + if (m_locksControl) + delete m_locksControl; + + // CameraControl destroys: + // * ViewfinderEngine + // * CameraEngine + if (m_control) + delete m_control; + + // Delete viewfinder controls after CameraControl to be sure that + // ViewFinder gets stopped before widget (and window) is destroyed + if (m_viewFinderWidget) + delete m_viewFinderWidget; + if (m_rendererControl) + delete m_rendererControl; + if (m_windowControl) + delete m_windowControl; + + // Delete sessions + if (m_videosession) + delete m_videosession; + if (m_imagesession) + delete m_imagesession; +} + +QMediaControl *S60CameraService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaRecorderControl_iid) == 0) + return m_media; + + if (qstrcmp(name, QCameraControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QVideoEncoderControl_iid) == 0) + return m_videoEncoder; + + if (qstrcmp(name, QAudioEncoderControl_iid) == 0) + return m_audioEncoder; + + if (qstrcmp(name, QMediaContainerControl_iid) == 0) + return m_mediaFormat; + + if (qstrcmp(name, QCameraExposureControl_iid) == 0) + return m_exposureControl; + + if (qstrcmp(name, QCameraFlashControl_iid) == 0) + return m_flashControl; + + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + if (m_viewFinderWidget) { + m_control->setVideoOutput(m_viewFinderWidget, + S60CameraViewfinderEngine::OutputTypeVideoWidget); + return m_viewFinderWidget; + } + else + return 0; + } + + if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + if (m_rendererControl) { + m_control->setVideoOutput(m_rendererControl, + S60CameraViewfinderEngine::OutputTypeRenderer); + return m_rendererControl; + } + else + return 0; + } + + if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + if (m_windowControl) { + m_control->setVideoOutput(m_windowControl, + S60CameraViewfinderEngine::OutputTypeVideoWindow); + return m_windowControl; + } + else + return 0; + } + + + if (qstrcmp(name, QCameraFocusControl_iid) == 0) + return m_focusControl; + + if (qstrcmp(name, QCameraImageProcessingControl_iid) == 0) + return m_imageProcessingControl; + + if (qstrcmp(name, QCameraImageCaptureControl_iid) == 0) + return m_imageCaptureControl; + + if (qstrcmp(name, QVideoDeviceControl_iid) == 0) + return m_videoDeviceControl; + + if (qstrcmp(name, QImageEncoderControl_iid) == 0) + return m_imageEncoderControl; + + if (qstrcmp(name, QCameraLocksControl_iid) == 0) + return m_locksControl; + + return 0; +} + +void S60CameraService::releaseControl(QMediaControl *control) +{ + if (control == 0) + return; + + // Release viewfinder output + if (control == m_viewFinderWidget) { + if (m_viewFinderWidget) + m_control->releaseVideoOutput(S60CameraViewfinderEngine::OutputTypeVideoWidget); + } + + if (control == m_rendererControl) { + if (m_rendererControl) + m_control->releaseVideoOutput(S60CameraViewfinderEngine::OutputTypeRenderer); + } + + if (control == m_windowControl) { + if (m_windowControl) + m_control->releaseVideoOutput(S60CameraViewfinderEngine::OutputTypeVideoWindow); + } +} + +int S60CameraService::deviceCount() +{ + return S60CameraControl::deviceCount(); +} + +QString S60CameraService::deviceDescription(const int index) +{ + return S60CameraControl::description(index); +} + +QString S60CameraService::deviceName(const int index) +{ + return S60CameraControl::name(index); +} + +// End of file + diff --git a/src/plugins/symbian/ecam/s60cameraservice.h b/src/plugins/symbian/ecam/s60cameraservice.h new file mode 100644 index 000000000..a2744c1fa --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraservice.h @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERASERVICE_H +#define S60CAMERASERVICE_H + +#include +#include + +QT_USE_NAMESPACE + +class S60MediaContainerControl; +class S60VideoEncoderControl; +class S60AudioEncoderControl; +class S60CameraControl; +class S60VideoDeviceControl; +class S60MediaRecorderControl; +class S60ImageCaptureSession; +class S60VideoCaptureSession; +class S60CameraFocusControl; +class S60CameraExposureControl; +class S60CameraFlashControl; +class S60CameraImageProcessingControl; +class S60CameraImageCaptureControl; +class S60VideoWidgetControl; +class S60ImageEncoderControl; +class S60CameraLocksControl; +class S60VideoRendererControl; +class S60VideoWindowControl; + +class S60CameraService : public QMediaService +{ + Q_OBJECT + +public: // Contructor & Destructor + + S60CameraService(QObject *parent = 0); + ~S60CameraService(); + +public: // QMediaService + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *control); + +public: // Static Device Info + + static int deviceCount(); + static QString deviceName(const int index); + static QString deviceDescription(const int index); + +private: // Data + + S60ImageCaptureSession *m_imagesession; + S60VideoCaptureSession *m_videosession; + S60MediaContainerControl *m_mediaFormat; + S60VideoEncoderControl *m_videoEncoder; + S60AudioEncoderControl *m_audioEncoder; + S60CameraControl *m_control; + S60VideoDeviceControl *m_videoDeviceControl; + S60CameraFocusControl *m_focusControl; + S60CameraExposureControl *m_exposureControl; + S60CameraFlashControl *m_flashControl; + S60CameraImageProcessingControl *m_imageProcessingControl; + S60CameraImageCaptureControl *m_imageCaptureControl; + S60MediaRecorderControl *m_media; + S60VideoWidgetControl *m_viewFinderWidget; + S60ImageEncoderControl *m_imageEncoderControl; + S60CameraLocksControl *m_locksControl; + S60VideoRendererControl *m_rendererControl; + S60VideoWindowControl *m_windowControl; +}; + +#endif // S60CAMERASERVICE_H diff --git a/src/plugins/symbian/ecam/s60cameraserviceplugin.cpp b/src/plugins/symbian/ecam/s60cameraserviceplugin.cpp new file mode 100644 index 000000000..8f22fd205 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraserviceplugin.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60cameraserviceplugin.h" +#ifdef QMEDIA_SYMBIAN_CAMERA +#include "s60cameraservice.h" +#endif + +QStringList S60CameraServicePlugin::keys() const +{ + QStringList list; +#ifdef QMEDIA_SYMBIAN_CAMERA + list << QLatin1String(Q_MEDIASERVICE_CAMERA); +#endif + return list; +} + +QMediaService* S60CameraServicePlugin::create(QString const& key) +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + if (key == QLatin1String(Q_MEDIASERVICE_CAMERA)) + return new S60CameraService; +#endif + return 0; +} + +void S60CameraServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList S60CameraServicePlugin::devices(const QByteArray &service) const +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + return m_cameraDevices; + } +#endif + return QList(); +} + +QString S60CameraServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ +#ifdef QMEDIA_SYMBIAN_CAMERA + if (service == Q_MEDIASERVICE_CAMERA) { + if (m_cameraDevices.isEmpty()) + updateDevices(); + + for (int i=0; i +#include + +QT_USE_NAMESPACE + +/* + * Plugin implementation for the Camera Service + */ +class S60CameraServicePlugin : public QMediaServiceProviderPlugin, + public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) + +public: // QMediaServiceProviderPlugin + + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + +public: // QMediaServiceSupportedDevicesInterface + + QList devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); + +private: // Internal + + void updateDevices() const; + +private: // Data + + mutable QList m_cameraDevices; + mutable QStringList m_cameraDescriptions; +}; + +#endif // S60CAMERASERVICEPLUGIN_H diff --git a/src/plugins/symbian/ecam/s60camerasettings.cpp b/src/plugins/symbian/ecam/s60camerasettings.cpp new file mode 100644 index 000000000..a5918078f --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerasettings.cpp @@ -0,0 +1,986 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60camerasettings.h" +#include "s60cameraconstants.h" + +// S60 3.2 Platform +#ifdef USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER +#define POST_31_PLATFORM +#include // CCameraAdvancedSettings (inc. TValueInfo) +#endif // S60 3.2 + +// S60 5.0 or later +#ifdef USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER +#define POST_31_PLATFORM +#include // CCameraAdvancedSettings +#include // TValueInfo +#endif // S60 5.0 or later + +S60CameraSettings::S60CameraSettings(QObject *parent, CCameraEngine *engine) : + QObject(parent), +#ifndef S60_31_PLATFORM // Post S60 3.1 Platforms + m_advancedSettings(0), + m_imageProcessingSettings(0), +#endif // S60_31_PLATFORM + m_cameraEngine(engine), + m_continuousFocusing(false) +{ +} + +S60CameraSettings::~S60CameraSettings() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + delete m_advancedSettings; + m_advancedSettings = 0; + } + + if (m_imageProcessingSettings) { + delete m_imageProcessingSettings; + m_imageProcessingSettings = 0; + } +#endif // POST_31_PLATFORM +} + +/* + * This is Symbian NewL kind of consructor, but unlike Symbian version this + * constructor will not leave, but instead it will return possible errors in + * the error variable. This is to be able to write the class without deriving + * it form CBase. Also CleanupStack is cleaned here if the ConstructL leaves. + */ +S60CameraSettings* S60CameraSettings::New(int &error, QObject *parent, CCameraEngine *engine) +{ + S60CameraSettings* self = new S60CameraSettings(parent, engine); + if (!self) { + error = KErrNoMemory; + return 0; + } + + TRAPD(err, self->ConstructL()); + if (err) { + // Clean created object + delete self; + self = 0; + error = err; + return 0; + } + + error = KErrNone; + return self; +} + +void S60CameraSettings::ConstructL() +{ +#ifdef POST_31_PLATFORM + if (!m_cameraEngine) + User::Leave(KErrGeneral); + // From now on it is safe to assume engine exists + + // If no AdvancedSettings is available, there's no benefit of S60CameraSettings + // Leave if creation fails + m_advancedSettings = CCamera::CCameraAdvancedSettings::NewL(*m_cameraEngine->Camera()); + CleanupStack::PushL(m_advancedSettings); + + // ImageProcessing module may not be supported, don't Leave + TRAPD(err, m_imageProcessingSettings = CCamera::CCameraImageProcessing::NewL(*m_cameraEngine->Camera())); + if (err == KErrNone && m_imageProcessingSettings) { + CleanupStack::PushL(m_imageProcessingSettings); + } else { + if (err == KErrNotSupported) + m_imageProcessingSettings = 0; + else { + // Leave with error + if (!m_imageProcessingSettings) + User::Leave(KErrNoMemory); + else + User::Leave(err); + } + } + + if (m_advancedSettings) { + RArray digitalZoomFactors; + CleanupClosePushL(digitalZoomFactors); + + TValueInfo info = ENotActive; + m_advancedSettings->GetDigitalZoomStepsL(digitalZoomFactors, info); + + for (int i = 0; i < digitalZoomFactors.Count(); ++i) + m_supportedSymbianDigitalZoomFactors << digitalZoomFactors[i]; + + CleanupStack::PopAndDestroy(); // RArray digitalZoomFactors + } + + // Pop objects from CleanupStack + if (m_imageProcessingSettings) + CleanupStack::Pop(m_imageProcessingSettings); + CleanupStack::Pop(m_advancedSettings); + +#else // S60 3.1 + // AdvancedSettings are not suppoted on S60 3.1 (There's no use for S60CameraSettings) + User::Leave(KErrNotSupported); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setFocusMode(QCameraFocus::FocusMode mode) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + switch (mode) { + case QCameraFocus::ManualFocus: // Manual focus mode + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeManual); + m_continuousFocusing = false; + break; + case QCameraFocus::AutoFocus: // Single-shot AutoFocus mode + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeAuto); + m_continuousFocusing = false; + break; + case QCameraFocus::HyperfocalFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeHyperfocal); + m_continuousFocusing = false; + break; + case QCameraFocus::InfinityFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeInfinite); + m_continuousFocusing = false; + break; + case QCameraFocus::ContinuousFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeAuto); + m_continuousFocusing = true; + break; + case QCameraFocus::MacroFocus: + m_advancedSettings->SetFocusMode(CCamera::CCameraAdvancedSettings::EFocusModeAuto); + m_advancedSettings->SetFocusRange(CCamera::CCameraAdvancedSettings::EFocusRangeMacro); + m_continuousFocusing = false; + break; + + default: + emit error(QCamera::NotSupportedFeatureError, tr("Requested focus mode is not supported.")); + break; + } + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 + Q_UNUSED(mode); + emit error(QCamera::NotSupportedFeatureError, tr("Settings focus mode is not supported.")); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::startFocusing() +{ +#ifdef POST_31_PLATFORM + // Setting AutoFocusType triggers the focusing on Symbian + if (m_advancedSettings) { + if (m_continuousFocusing) + m_advancedSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeContinuous); + else + m_advancedSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeSingle); + } else { + emit error(QCamera::CameraError, tr("Unable to focus.")); + } +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::cancelFocusing() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) + m_advancedSettings->SetAutoFocusType(CCamera::CCameraAdvancedSettings::EAutoFocusTypeOff); + else + emit error(QCamera::CameraError, tr("Unable to cancel focusing.")); +#endif // POST_31_PLATFORM +} + +QCameraFocus::FocusMode S60CameraSettings::focusMode() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + // First request needed info + CCamera::CCameraAdvancedSettings::TFocusMode mode = m_advancedSettings->FocusMode(); + CCamera::CCameraAdvancedSettings::TFocusRange range = m_advancedSettings->FocusRange(); + CCamera::CCameraAdvancedSettings::TAutoFocusType autoType = m_advancedSettings->AutoFocusType(); + + switch (mode) { + case CCamera::CCameraAdvancedSettings::EFocusModeManual: + case CCamera::CCameraAdvancedSettings::EFocusModeFixed: + return QCameraFocus::ManualFocus; + + case CCamera::CCameraAdvancedSettings::EFocusModeAuto: + if (autoType == CCamera::CCameraAdvancedSettings::EAutoFocusTypeContinuous) { + return QCameraFocus::ContinuousFocus; + } else { + // Single-shot focusing + switch (range) { + case CCamera::CCameraAdvancedSettings::EFocusRangeMacro: + case CCamera::CCameraAdvancedSettings::EFocusRangeSuperMacro: + return QCameraFocus::MacroFocus; + case CCamera::CCameraAdvancedSettings::EFocusRangeHyperfocal: + return QCameraFocus::HyperfocalFocus; + case CCamera::CCameraAdvancedSettings::EFocusRangeInfinite: + return QCameraFocus::InfinityFocus; + case CCamera::CCameraAdvancedSettings::EFocusRangeAuto: + case CCamera::CCameraAdvancedSettings::EFocusRangeNormal: + return QCameraFocus::AutoFocus; + + default: + return QCameraFocus::AutoFocus; + } + } + default: + return QCameraFocus::AutoFocus; // Return automatic focusing + } + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#endif // POST_31_PLATFORM + return QCameraFocus::AutoFocus; // Return automatic focusing +} + +QCameraFocus::FocusModes S60CameraSettings::supportedFocusModes() +{ + QCameraFocus::FocusModes modes = 0; + +#ifdef POST_31_PLATFORM + TInt supportedModes = 0; + TInt autoFocusTypes = 0; + TInt supportedRanges = 0; + + if (m_advancedSettings) { + supportedModes = m_advancedSettings->SupportedFocusModes(); + autoFocusTypes = m_advancedSettings->SupportedAutoFocusTypes(); + supportedRanges = m_advancedSettings->SupportedFocusRanges(); + + if (supportedModes == 0 || autoFocusTypes == 0 || supportedRanges == 0) + return modes; + + // EFocusModeAuto is the only supported on Symbian + if (supportedModes & CCamera::CCameraAdvancedSettings::EFocusModeAuto) { + // Check supported types (Single-shot Auto vs. Continuous) + if (autoFocusTypes & CCamera::CCameraAdvancedSettings::EAutoFocusTypeSingle) + modes |= QCameraFocus::AutoFocus; + if (autoFocusTypes & CCamera::CCameraAdvancedSettings::EAutoFocusTypeContinuous) + modes |= QCameraFocus::ContinuousFocus; + + // Check supported ranges (Note! Some are actually fixed focuses + // even though the mode is Auto on Symbian) + if (supportedRanges & CCamera::CCameraAdvancedSettings::EFocusRangeMacro) + modes |= QCameraFocus::MacroFocus; + if (supportedRanges & CCamera::CCameraAdvancedSettings::EFocusRangeHyperfocal) + modes |= QCameraFocus::HyperfocalFocus; + if (supportedRanges & CCamera::CCameraAdvancedSettings::EFocusRangeInfinite) + modes |= QCameraFocus::InfinityFocus; + } + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#endif // POST_31_PLATFORM + + return modes; +} + +qreal S60CameraSettings::opticalZoomFactorL() const +{ + // Not supported on Symbian + return 1.0; +} + +void S60CameraSettings::setOpticalZoomFactorL(const qreal zoomFactor) +{ + // Not supported on Symbian + Q_UNUSED(zoomFactor); +} + +QList S60CameraSettings::supportedDigitalZoomFactors() const +{ + QList zoomFactors; + foreach (int factor, m_supportedSymbianDigitalZoomFactors) + zoomFactors << qreal(factor) / KSymbianFineResolutionFactor; + + return zoomFactors; +} + +qreal S60CameraSettings::digitalZoomFactorL() const +{ + qreal factor = 1.0; + +#ifdef POST_31_PLATFORM + int symbianFactor = 0; + if (m_advancedSettings) + symbianFactor = m_advancedSettings->DigitalZoom(); + else + User::Leave(KErrNotSupported); + + if (symbianFactor != 0) + factor = qreal(symbianFactor) / KSymbianFineResolutionFactor; +#endif // POST_31_PLATFORM + + return factor; +} + +void S60CameraSettings::setDigitalZoomFactorL(const qreal zoomFactor) +{ +#ifdef POST_31_PLATFORM + int symbianFactor = zoomFactor * KSymbianFineResolutionFactor; + + // Find closest supported Symbian ZoomFactor if needed + if (!m_supportedSymbianDigitalZoomFactors.contains(symbianFactor)) { + int closestIndex = -1; + int closestDiff = 1000000; // Sensible maximum + for (int i = 0; i < m_supportedSymbianDigitalZoomFactors.count(); ++i) { + int diff = abs(m_supportedSymbianDigitalZoomFactors.at(i) - symbianFactor); + if (diff < closestDiff) { + closestDiff = diff; + closestIndex = i; + } + } + if (closestIndex != -1) + symbianFactor = m_supportedSymbianDigitalZoomFactors.at(closestIndex); + else + User::Leave(KErrGeneral); + } + if (m_advancedSettings) + m_advancedSettings->SetDigitalZoom(symbianFactor); + else + User::Leave(KErrNotSupported); +#else // S60 3.1 Platform + Q_UNUSED(zoomFactor); + emit error(QCamera::NotSupportedFeatureError, tr("Settings digital zoom factor is not supported.")); +#endif // POST_31_PLATFORM +} + +// MCameraObserver2 +void S60CameraSettings::HandleAdvancedEvent(const TECAMEvent& aEvent) +{ +#ifdef POST_31_PLATFORM + + if (aEvent.iErrorCode != KErrNone) { + switch (aEvent.iErrorCode) { + case KErrECamCameraDisabled: + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return; + case KErrECamSettingDisabled: + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return; + case KErrECamParameterNotInRange: + emit error(QCamera::NotSupportedFeatureError, tr("Requested value is not in supported range.")); + return; + case KErrECamSettingNotSupported: + emit error(QCamera::NotSupportedFeatureError, tr("Requested setting is not supported.")); + return; + case KErrECamNotOptimalFocus: + if (m_continuousFocusing) + emit focusStatusChanged(QCamera::Searching, QCamera::LockTemporaryLost); + else + emit focusStatusChanged(QCamera::Unlocked, QCamera::LockFailed); + return; + } + + if (aEvent.iEventType == KUidECamEventCameraSettingFocusRange || + aEvent.iEventType == KUidECamEventCameraSettingAutoFocusType2) { + emit focusStatusChanged(QCamera::Unlocked, QCamera::LockFailed); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingIsoRate) { + if (aEvent.iErrorCode == KErrNotSupported) + emit error(QCamera::NotSupportedFeatureError, tr("Requested ISO value is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting ISO value failed.")); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingAperture) { + if (aEvent.iErrorCode == KErrNotSupported) + emit error(QCamera::NotSupportedFeatureError, tr("Requested aperture value is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting aperture value failed.")); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingExposureCompensation) { + if (aEvent.iErrorCode == KErrNotSupported) + emit error(QCamera::NotSupportedFeatureError, tr("Requested exposure compensation is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting exposure compensation failed.")); + return; + } else if (aEvent.iEventType == KUidECamEventCameraSettingOpticalZoom || + aEvent.iEventType == KUidECamEventCameraSettingDigitalZoom) { + if (aEvent.iErrorCode == KErrNotSupported) + return; // Discard + else { + emit error(QCamera::CameraError, tr("Setting zoom factor failed.")); + return; + } + } else if (aEvent.iEventType == KUidECamEventCameraSettingFocusMode) { + if (aEvent.iErrorCode == KErrNotSupported) + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() != 0) + emit error(QCamera::NotSupportedFeatureError, tr("Focusing is not supported with this camera.")); + else + emit error(QCamera::NotSupportedFeatureError, tr("Requested focus mode is not supported.")); + else + emit error(QCamera::CameraError, tr("Setting focus mode failed.")); + return; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return; + } + } + + if (aEvent.iEventType == KUidECamEventCameraSettingExposureLock) { + if (m_advancedSettings) { + if (m_advancedSettings->ExposureLockOn()) + emit exposureStatusChanged(QCamera::Locked, QCamera::LockAcquired); + else + emit exposureStatusChanged(QCamera::Unlocked, QCamera::LockLost); + } + else + emit exposureStatusChanged(QCamera::Unlocked, QCamera::LockLost); + } + else if (aEvent.iEventType == KUidECamEventCameraSettingAperture) + emit apertureChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingApertureRange) + emit apertureRangeChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingIsoRateType) + emit isoSensitivityChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingShutterSpeed) + emit shutterSpeedChanged(); + + else if (aEvent.iEventType == KUidECamEventCameraSettingExposureCompensationStep) + emit evChanged(); + + else if (aEvent.iEventType == KUidECamEventFlashReady) + emit flashReady(true); + + else if (aEvent.iEventType == KUidECamEventFlashNotReady) + emit flashReady(false); + + else if (aEvent.iEventType.iUid == KUidECamEventCameraSettingsOptimalFocusUidValue) + emit focusStatusChanged(QCamera::Locked, QCamera::LockAcquired); + +#else // S60 3.1 Platform + Q_UNUSED(aEvent); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isFlashReady() +{ + TBool isReady = false; +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + int flashErr = m_advancedSettings->IsFlashReady(isReady); + if(flashErr != KErrNone) { + if (flashErr != KErrNotSupported) + emit error(QCamera::CameraError, tr("Unexpected error with flash.")); + return false; + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#endif + return isReady; +} + +QCameraExposure::MeteringMode S60CameraSettings::meteringMode() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + CCamera::CCameraAdvancedSettings::TMeteringMode mode = m_advancedSettings->MeteringMode(); + switch (mode) { + case CCamera::CCameraAdvancedSettings::EMeteringModeCenterWeighted: + return QCameraExposure::MeteringAverage; + case CCamera::CCameraAdvancedSettings::EMeteringModeEvaluative: + return QCameraExposure::MeteringMatrix; + case CCamera::CCameraAdvancedSettings::EMeteringModeSpot: + return QCameraExposure::MeteringSpot; + + default: + return QCameraExposure::MeteringAverage; + } + }else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return QCameraExposure::MeteringAverage; + } +#else // S60 3.1 Platform + return QCameraExposure::MeteringAverage; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setMeteringMode(QCameraExposure::MeteringMode mode) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + switch(mode) { + case QCameraExposure::MeteringAverage: + m_advancedSettings->SetMeteringMode(CCamera::CCameraAdvancedSettings::EMeteringModeCenterWeighted); + break; + case QCameraExposure::MeteringMatrix: + m_advancedSettings->SetMeteringMode(CCamera::CCameraAdvancedSettings::EMeteringModeEvaluative); + break; + case QCameraExposure::MeteringSpot: + m_advancedSettings->SetMeteringMode(CCamera::CCameraAdvancedSettings::EMeteringModeSpot); + break; + default: + break; + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(mode); + emit error(QCamera::NotSupportedFeatureError, tr("Setting metering mode is not supported.")); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isMeteringModeSupported(QCameraExposure::MeteringMode mode) +{ +#ifdef POST_31_PLATFORM + TInt supportedModes = 0; + + if (m_advancedSettings) { + supportedModes = m_advancedSettings->SupportedMeteringModes(); + if (supportedModes == 0) + return false; + + switch (mode) { + case QCameraExposure::MeteringMatrix: + if (supportedModes & CCamera::CCameraAdvancedSettings::EMeteringModeEvaluative) + return true; + else + return false; + case QCameraExposure::MeteringAverage: + if (supportedModes & CCamera::CCameraAdvancedSettings::EMeteringModeCenterWeighted) + return true; + else + return false; + case QCameraExposure::MeteringSpot: + if (supportedModes & CCamera::CCameraAdvancedSettings::EMeteringModeSpot) + return true; + else + return false; + + default: + return false; + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(mode); +#endif // POST_31_PLATFORM + + return false; +} + +int S60CameraSettings::isoSensitivity() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + CCamera::CCameraAdvancedSettings::TISORateType isoRateType; + TInt param = 0; + TInt isoRate = 0; + TRAPD(err, m_advancedSettings->GetISORateL(isoRateType, param, isoRate)); + if (err) + return 0; + if (isoRate != KErrNotFound) + return isoRate; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#endif // POST_31_PLATFORM + return 0; +} + +QList S60CameraSettings::supportedIsoSensitivities() +{ + QList isoSentitivities; +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray supportedIsoRates; + CleanupClosePushL(supportedIsoRates); + + TRAPD(err, m_advancedSettings->GetSupportedIsoRatesL(supportedIsoRates)); + if (err != KErrNone) { + if (err != KErrNotSupported) // Don's emit error if ISO is not supported + emit error(QCamera::CameraError, tr("Failure while querying supported iso sensitivities.")); + } else { + for (int i = 0; i < supportedIsoRates.Count(); ++i) + isoSentitivities << supportedIsoRates[i]; + } + CleanupStack::PopAndDestroy(); // RArray supportedIsoRates + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } + + return isoSentitivities; +#else // S60 3.1 Platform + return isoSentitivities; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setManualIsoSensitivity(int iso) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TRAPD(err, m_advancedSettings->SetISORateL(CCamera::CCameraAdvancedSettings::EISOManual, iso)); + if (err) + emit error(QCamera::CameraError, tr("Setting manual iso sensitivity failed.")); + return; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 Platform + Q_UNUSED(iso); + emit error(QCamera::NotSupportedFeatureError, tr("Setting manual iso sensitivity is not supported.")); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setAutoIsoSensitivity() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TRAPD(err, m_advancedSettings->SetISORateL(CCamera::CCameraAdvancedSettings::EISOAutoUnPrioritised, 0)); + if (err) + emit error(QCamera::CameraError, tr("Setting auto iso sensitivity failed.")); + return; + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 Platform + emit error(QCamera::NotSupportedFeatureError, tr("Setting auto iso sensitivity is not supported.")); +#endif // POST_31_PLATFORM +} + +qreal S60CameraSettings::aperture() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) + return qreal(m_advancedSettings->Aperture()) / KSymbianFineResolutionFactor; + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +QList S60CameraSettings::supportedApertures() +{ + QList apertures; + +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray supportedApertures; + TValueInfo info = ENotActive; + + TRAPD(err, m_advancedSettings->GetAperturesL(supportedApertures, info)); + if (err != KErrNone) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported apertures.")); + else { + for (int i = 0; i < supportedApertures.Count(); i++) { + qreal q = qreal(supportedApertures[i]) / KSymbianFineResolutionFactor; + apertures.append(q); + } + } + supportedApertures.Close(); + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return apertures; +#else // S60 3.1 Platform + return apertures; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setManualAperture(qreal aperture) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + int symbianAperture = (aperture * KSymbianFineResolutionFactor); // KSymbianFineResolutionFactor = 100 + m_advancedSettings->SetAperture(symbianAperture); + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(aperture); + emit error(QCamera::NotSupportedFeatureError, tr("Setting manual aperture is not supported.")); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::lockExposure(bool lock) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + m_advancedSettings->SetExposureLockOn(lock); + return; + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#else // S60 3.1 + Q_UNUSED(lock); + emit error(QCamera::NotSupportedFeatureError, tr("Locking exposure is not supported.")); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isExposureLocked() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) + return m_advancedSettings->ExposureLockOn(); + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); +#endif // POST_31_PLATFORM + return false; +} + +qreal S60CameraSettings::shutterSpeed() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + qreal shutterSpeed = qreal(m_advancedSettings->ShutterSpeed()) / 1000000.0; + return shutterSpeed; // In seconds + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +QList S60CameraSettings::supportedShutterSpeeds() +{ + QList speeds; + +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray supportedSpeeds; + TValueInfo info = ENotActive; + + TRAPD(err, m_advancedSettings->GetShutterSpeedsL(supportedSpeeds, info)); + if (err != KErrNone) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported shutter speeds.")); + else { + for (int i = 0; i < supportedSpeeds.Count(); i++) { + qreal q = qreal(supportedSpeeds[i]) / 1000000.0; + speeds.append(q); // In seconds + } + } + supportedSpeeds.Close(); + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return speeds; +#else // S60 3.1 Platform + return speeds; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setManualShutterSpeed(qreal speed) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TInt shutterSpeed = speed * 1000000; // From seconds to microseconds + m_advancedSettings->SetShutterSpeed(shutterSpeed); + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 + emit error(QCamera::NotSupportedFeatureError, tr("Setting manual shutter speed is not supported.")); + Q_UNUSED(speed); +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setExposureCompensation(qreal ev) +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TInt evStep = ev * KSymbianFineResolutionFactor; + m_advancedSettings->SetExposureCompensationStep(evStep); + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } +#else // S60 3.1 Platform + Q_UNUSED(ev); + emit error(QCamera::NotSupportedFeatureError, tr("Setting exposure compensation is not supported.")); +#endif // POST_31_PLATFORM +} + +qreal S60CameraSettings::exposureCompensation() +{ +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + TInt evStepSymbian = 0; + m_advancedSettings->GetExposureCompensationStep(evStepSymbian); + qreal evStep = evStepSymbian; + evStep /= KSymbianFineResolutionFactor; + return evStep; + } else { + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + } + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +QList S60CameraSettings::supportedExposureCompensationValues() +{ + QList valueList; + +#ifdef POST_31_PLATFORM + if (m_advancedSettings) { + RArray evSteps; + TValueInfo info; + TRAPD(err, m_advancedSettings->GetExposureCompensationStepsL(evSteps, info)); + if (err) { + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported exposure compensation values.")); + return valueList; + } + + if (info == ENotActive || evSteps.Count() == 0) { + // EV not supported, return empty list + return valueList; + } + + for (int i = 0; i < evSteps.Count(); ++i) { + qreal appendValue = evSteps[i]; + appendValue /= KSymbianFineResolutionFactor; + valueList.append(appendValue); + } + } + else + emit error(QCamera::CameraError, tr("Unexpected camera error.")); + return valueList; +#else // S60 3.1 Platform + return valueList; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setSharpeningLevel(int value) +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings && isSharpeningSupported()) + m_imageProcessingSettings->SetTransformationValue(KUidECamEventImageProcessingAdjustSharpness, value); + else + emit error(QCamera::NotSupportedFeatureError, tr("Setting sharpening level is not supported.")); +#else // S60 3.1 + Q_UNUSED(value); + emit error(QCamera::NotSupportedFeatureError, tr("Setting sharpening level is not supported.")); +#endif // POST_31_PLATFORM +} + +bool S60CameraSettings::isSharpeningSupported() const +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings) { + RArray suppTransforms; + TRAPD(err, m_imageProcessingSettings->GetSupportedTransformationsL(suppTransforms)); + if (err) + return false; + + if (suppTransforms.Find(KUidECamEventImageProcessingAdjustSharpness)) + return true; + } + return false; +#else // S60 3.1 Platform + return false; +#endif // POST_31_PLATFORM +} + +int S60CameraSettings::sharpeningLevel() const +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings && isSharpeningSupported()) + return m_imageProcessingSettings->TransformationValue(KUidECamEventImageProcessingAdjustSharpness); + else + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +void S60CameraSettings::setSaturation(int value) +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings) { + RArray suppTransforms; + TRAPD(err, m_imageProcessingSettings->GetSupportedTransformationsL(suppTransforms)); + if (err) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported transformations.")); + + if (suppTransforms.Find(KUidECamEventtImageProcessingAdjustSaturation)) + m_imageProcessingSettings->SetTransformationValue(KUidECamEventtImageProcessingAdjustSaturation, value == -1 ? 0 : value*2-100); + else + emit error(QCamera::NotSupportedFeatureError, tr("Setting saturation is not supported.")); + } + else + emit error(QCamera::NotSupportedFeatureError, tr("Setting saturation is not supported.")); +#else // S60 3.1 + Q_UNUSED(value); + emit error(QCamera::NotSupportedFeatureError, tr("Setting saturation is not supported.")); +#endif // POST_31_PLATFORM +} + +int S60CameraSettings::saturation() +{ +#ifdef POST_31_PLATFORM + if (m_imageProcessingSettings) { + RArray suppTransforms; + TRAPD(err, m_imageProcessingSettings->GetSupportedTransformationsL(suppTransforms)); + if (err) + if (err != KErrNotSupported) + emit error(QCamera::CameraError, tr("Failure while querying supported transformations.")); + + if (suppTransforms.Find(KUidECamEventtImageProcessingAdjustSaturation)) + return m_imageProcessingSettings->TransformationValue(KUidECamEventtImageProcessingAdjustSaturation); + } + return 0; +#else // S60 3.1 Platform + return 0; +#endif // POST_31_PLATFORM +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60camerasettings.h b/src/plugins/symbian/ecam/s60camerasettings.h new file mode 100644 index 000000000..4ecb131b2 --- /dev/null +++ b/src/plugins/symbian/ecam/s60camerasettings.h @@ -0,0 +1,177 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60CAMERASETTINGS_H +#define S60CAMERASETTINGS_H + +#include "qcamera.h" + +#include "s60cameraengine.h" +#include "s60cameraengineobserver.h" + +#include + +QT_USE_NAMESPACE + +/* + * Class handling CCamera AdvancedSettings and ImageProcessing operations. + */ +class S60CameraSettings : public QObject, + public MAdvancedSettingsObserver +{ + Q_OBJECT + +public: // Static Contructor & Destructor + + static S60CameraSettings* New(int &error, QObject *parent = 0, CCameraEngine *engine = 0); + ~S60CameraSettings(); + +public: // Methods + + // Focus + QCameraFocus::FocusMode focusMode(); + void setFocusMode(QCameraFocus::FocusMode mode); + QCameraFocus::FocusModes supportedFocusModes(); + void startFocusing(); + void cancelFocusing(); + + // Zoom + qreal opticalZoomFactorL() const; + void setOpticalZoomFactorL(const qreal zoomFactor); + QList supportedDigitalZoomFactors() const; + qreal digitalZoomFactorL() const; + void setDigitalZoomFactorL(const qreal zoomFactor); + + // Flash + bool isFlashReady(); + + // Exposure + void setExposureMode(QCameraExposure::ExposureMode mode); + void lockExposure(bool lock); + bool isExposureLocked(); + + // Metering Mode + QCameraExposure::MeteringMode meteringMode(); + void setMeteringMode(QCameraExposure::MeteringMode mode); + bool isMeteringModeSupported(QCameraExposure::MeteringMode mode); + + // ISO Sensitivity + int isoSensitivity(); + void setManualIsoSensitivity(int iso); + void setAutoIsoSensitivity(); + QList supportedIsoSensitivities(); + + // Aperture + qreal aperture(); + void setManualAperture(qreal aperture); + QList supportedApertures(); + + // Shutter Speed + qreal shutterSpeed(); + void setManualShutterSpeed(qreal speed); + QList supportedShutterSpeeds(); + + // ExposureCompensation + qreal exposureCompensation(); + void setExposureCompensation(qreal ev); + QList supportedExposureCompensationValues(); + + // Sharpening Level + int sharpeningLevel() const; + void setSharpeningLevel(int value); + bool isSharpeningSupported() const; + + // Saturation + int saturation(); + void setSaturation(int value); + +signals: // Notifications + + // For QCameraExposureControl + void flashReady(bool ready); + void apertureChanged(); + void apertureRangeChanged(); + void shutterSpeedChanged(); + void isoSensitivityChanged(); + void evChanged(); + + // For QCameraLocksControl + void exposureStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason); + void focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason); + + // Errors + void error(int, const QString&); + +protected: // Protected constructors + + S60CameraSettings(QObject *parent, CCameraEngine *engine); + void ConstructL(); + +protected: // MAdvancedSettingsObserver + + void HandleAdvancedEvent(const TECAMEvent& aEvent); + +private: // Internal + + bool queryAdvancedSettingsInfo(); + +private: // Enums + + enum EcamErrors { + KErrECamCameraDisabled = -12100, // The camera has been disabled, hence calls do not succeed + KErrECamSettingDisabled = -12101, // This parameter or operation is supported, but presently is disabled. + KErrECamParameterNotInRange = -12102, // This value is out of range. + KErrECamSettingNotSupported = -12103, // This parameter or operation is not supported. + KErrECamNotOptimalFocus = -12104 // The optimum focus is lost + }; + +private: // Data + +#ifndef S60_31_PLATFORM // Post S60 3.1 Platforms + CCamera::CCameraAdvancedSettings *m_advancedSettings; + CCamera::CCameraImageProcessing *m_imageProcessingSettings; +#endif // S60_31_PLATFORM + CCameraEngine *m_cameraEngine; + QList m_supportedSymbianDigitalZoomFactors; + bool m_continuousFocusing; +}; + +#endif // S60CAMERASETTINGS_H diff --git a/src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp b/src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp new file mode 100644 index 000000000..55d7cbc67 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraviewfinderengine.cpp @@ -0,0 +1,789 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "s60cameraviewfinderengine.h" +#include "s60cameraengine.h" +#include "s60cameracontrol.h" +#include "s60videowidgetcontrol.h" +#include "s60videowidgetdisplay.h" +#include "s60videorenderercontrol.h" +#include "s60videowindowcontrol.h" +#include "s60videowindowdisplay.h" +#include "s60cameraconstants.h" + +#include // CCoeEnv +#include // CCoeControl +#include + +// Helper function +TRect qRect2TRect(const QRect &qr) +{ + return TRect(TPoint(qr.left(), qr.top()), TSize(qr.width(), qr.height())); +} + + +S60CameraViewfinderEngine::S60CameraViewfinderEngine(S60CameraControl *control, + CCameraEngine *engine, + QObject *parent): + QObject(parent), + m_cameraEngine(engine), + m_cameraControl(0), + m_viewfinderOutput(0), + m_viewfinderDisplay(0), + m_viewfinderSurface(0), + m_wsSession(CCoeEnv::Static()->WsSession()), + m_screenDevice(*CCoeEnv::Static()->ScreenDevice()), + m_window(0), + m_desktopWidget(0), + m_vfState(EVFNotConnectedNotStarted), + m_viewfinderSize(KDefaultViewfinderSize), + m_actualViewFinderSize(KDefaultViewfinderSize), + m_viewfinderAspectRatio(0.0), + m_viewfinderType(OutputTypeNotSet), + m_viewfinderNativeType(EBitmapViewFinder), // Default type + m_isViewFinderVisible(true), // True by default (only QVideoWidgetControl supports being hidden) + m_uiLandscape(true), + m_vfErrorsSignalled(0) +{ + m_cameraControl = control; + + // Check whether platform supports DirectScreen ViewFinder + if (m_cameraEngine) { + if (m_cameraEngine->IsDirectViewFinderSupported()) + m_viewfinderNativeType = EDirectScreenViewFinder; + else + m_viewfinderNativeType = EBitmapViewFinder; + + MCameraViewfinderObserver *vfObserver = this; + m_cameraEngine->SetViewfinderObserver(vfObserver); + } + else + m_cameraControl->setError(KErrGeneral, tr("Unexpected camera error.")); + // From now on it is safe to assume engine exists + + // Check the UI orientation + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect screenRect = desktopWidget->screenGeometry(); + if (screenRect.width() > screenRect.height()) + m_uiLandscape = true; + else + m_uiLandscape = false; + + // Detect UI Rotations + m_desktopWidget = QApplication::desktop(); + if (m_desktopWidget) + connect(m_desktopWidget, SIGNAL(resized(int)), this, SLOT(handleDesktopResize(int))); +} + +S60CameraViewfinderEngine::~S60CameraViewfinderEngine() +{ + // No need to stop viewfinder: + // Engine has stopped it already + // Surface will be stopped by VideoRendererControl + + m_viewfinderOutput = 0; + m_viewfinderSurface = 0; +} + +void S60CameraViewfinderEngine::setNewCameraEngine(CCameraEngine *engine) +{ + m_cameraEngine = engine; + + if (m_cameraEngine) { + // And set observer to the new CameraEngine + MCameraViewfinderObserver *vfObserver = this; + m_cameraEngine->SetViewfinderObserver(vfObserver); + } +} + +void S60CameraViewfinderEngine::handleDesktopResize(int screen) +{ + Q_UNUSED(screen); + // UI Rotation is handled by the QVideoWidgetControl, thus this is needed + // only for the QVideoRendererControl + if (m_viewfinderType == OutputTypeRenderer) { + QSize newResolution(-1,-1); + if (m_viewfinderSurface) + newResolution = m_viewfinderSurface->nativeResolution(); + + if (newResolution.width() == -1 || newResolution.height() == -1) { + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect screenRect = desktopWidget->screenGeometry(); + newResolution = QSize(screenRect.width(), screenRect.height()); + } + + resetViewfinderSize(newResolution); + } + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); +} + +void S60CameraViewfinderEngine::setVideoWidgetControl(QObject *viewfinderOutput) +{ + // Release old control if it has not already been done + if (m_viewfinderOutput) + releaseControl(m_viewfinderType); + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); + + S60VideoWidgetControl* viewFinderWidgetControl = + qobject_cast(viewfinderOutput); + + if (viewFinderWidgetControl) { + // Check whether platform supports DirectScreen ViewFinder + if (m_cameraEngine) { + if (m_cameraEngine->IsDirectViewFinderSupported()) + m_viewfinderNativeType = EDirectScreenViewFinder; + else + m_viewfinderNativeType = EBitmapViewFinder; + } + else + return; + + m_viewfinderDisplay = viewFinderWidgetControl->display(); + + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + m_viewfinderDisplay->setPaintingEnabled(false); // No Qt Painter painting - Direct rendering + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow *)), this, SLOT(resetViewfinderDisplay())); + } else { + m_viewfinderDisplay->setPaintingEnabled(true); // Qt Painter painting - Bitmap rendering + connect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), m_viewfinderDisplay, SLOT(setFrame(const CFbsBitmap &))); + } + + connect(m_viewfinderDisplay, SIGNAL(visibilityChanged(bool)), this, SLOT(handleVisibilityChange(bool))); + connect(m_viewfinderDisplay, SIGNAL(displayRectChanged(QRect, QRect)), this, SLOT(resetVideoWindowSize())); + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow*)), this, SLOT(handleWindowChange(RWindow*))); + + m_viewfinderSize = m_viewfinderDisplay->extentRect().size(); + m_viewfinderOutput = viewfinderOutput; + m_viewfinderType = OutputTypeVideoWidget; + m_isViewFinderVisible = m_viewfinderDisplay->isVisible(); + + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFIsConnectedNotStarted; + break; + case EVFNotConnectedIsStarted: + if (m_isViewFinderVisible) + m_vfState = EVFIsConnectedIsStartedIsVisible; + else + m_vfState = EVFIsConnectedIsStartedNotVisible; + break; + case EVFIsConnectedNotStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already connected, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + startViewfinder(true); // Internal start (i.e. start if started externally) + } +} + +void S60CameraViewfinderEngine::setVideoRendererControl(QObject *viewfinderOutput) +{ + // Release old control if it has not already been done + if (m_viewfinderOutput) + releaseControl(m_viewfinderType); + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); + + S60VideoRendererControl* viewFinderRenderControl = + qobject_cast(viewfinderOutput); + + if (viewFinderRenderControl) { + m_viewfinderNativeType = EBitmapViewFinder; // Always Bitmap + + connect(viewFinderRenderControl, SIGNAL(viewFinderSurfaceSet()), + this, SLOT(rendererSurfaceSet())); + + Q_ASSERT(!viewFinderRenderControl->surface()); + m_viewfinderOutput = viewfinderOutput; + m_viewfinderType = OutputTypeRenderer; + // RendererControl viewfinder is "visible" when surface is set + m_isViewFinderVisible = false; + if (EVFIsConnectedIsStartedIsVisible) + m_vfState = EVFIsConnectedIsStartedNotVisible; + + // Use display resolution as default viewfinder resolution + m_viewfinderSize = QApplication::desktop()->screenGeometry().size(); + + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFIsConnectedNotStarted; + break; + case EVFNotConnectedIsStarted: + m_vfState = EVFIsConnectedIsStartedIsVisible; // GraphicsItem "always visible" (FrameWork decides to draw/not draw) + break; + case EVFIsConnectedNotStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already connected, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + startViewfinder(true); + } +} + +void S60CameraViewfinderEngine::setVideoWindowControl(QObject *viewfinderOutput) +{ + // Release old control if it has not already been done + if (m_viewfinderOutput) + releaseControl(m_viewfinderType); + + // Rotate Camera if UI has rotated + checkAndRotateCamera(); + + S60VideoWindowControl* viewFinderWindowControl = + qobject_cast(viewfinderOutput); + + if (viewFinderWindowControl) { + // Check whether platform supports DirectScreen ViewFinder + if (m_cameraEngine) { + if (m_cameraEngine->IsDirectViewFinderSupported()) + m_viewfinderNativeType = EDirectScreenViewFinder; + else + m_viewfinderNativeType = EBitmapViewFinder; + } else { + return; + } + + m_viewfinderDisplay = viewFinderWindowControl->display(); + + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + m_viewfinderDisplay->setPaintingEnabled(false); // No Qt Painter painting - Direct rendering + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow *)), this, SLOT(resetViewfinderDisplay())); + } else { + m_viewfinderDisplay->setPaintingEnabled(true); // Qt Painter painting - Bitmap rendering + connect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), m_viewfinderDisplay, SLOT(setFrame(const CFbsBitmap &))); + } + + connect(m_viewfinderDisplay, SIGNAL(displayRectChanged(QRect, QRect)), this, SLOT(resetVideoWindowSize())); + connect(m_viewfinderDisplay, SIGNAL(visibilityChanged(bool)), this, SLOT(handleVisibilityChange(bool))); + connect(m_viewfinderDisplay, SIGNAL(windowHandleChanged(RWindow*)), this, SLOT(handleWindowChange(RWindow*))); + + m_viewfinderSize = m_viewfinderDisplay->extentRect().size(); + m_viewfinderOutput = viewfinderOutput; + m_viewfinderType = OutputTypeVideoWindow; + m_isViewFinderVisible = m_viewfinderDisplay->isVisible(); + + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFIsConnectedNotStarted; + break; + case EVFNotConnectedIsStarted: + if (m_isViewFinderVisible) + m_vfState = EVFIsConnectedIsStartedIsVisible; + else + m_vfState = EVFIsConnectedIsStartedNotVisible; + break; + case EVFIsConnectedNotStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already connected, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + startViewfinder(true); // Internal start (i.e. start if started externally) + } +} + +void S60CameraViewfinderEngine::releaseControl(ViewfinderOutputType type) +{ + if (m_vfState == EVFIsConnectedIsStartedIsVisible) + stopViewfinder(true); + + if (m_viewfinderOutput) { + switch (type) { + case OutputTypeNotSet: + return; + case OutputTypeVideoWidget: + if (m_viewfinderType != OutputTypeVideoWidget) + return; + disconnect(m_viewfinderOutput); + m_viewfinderOutput->disconnect(this); + Q_ASSERT(m_viewfinderDisplay); + disconnect(m_viewfinderDisplay); + m_viewfinderDisplay->disconnect(this); + m_viewfinderDisplay = 0; + // Invalidate the extent rect + qobject_cast(m_viewfinderOutput)->setExtentRect(QRect()); + break; + case OutputTypeVideoWindow: + if (m_viewfinderType != OutputTypeVideoWindow) + return; + disconnect(m_viewfinderOutput); + m_viewfinderOutput->disconnect(this); + Q_ASSERT(m_viewfinderDisplay); + disconnect(m_viewfinderDisplay); + m_viewfinderDisplay->disconnect(this); + m_viewfinderDisplay = 0; + break; + case OutputTypeRenderer: + if (m_viewfinderType != OutputTypeRenderer) + return; + disconnect(m_viewfinderOutput); + m_viewfinderOutput->disconnect(this); + if (m_viewfinderSurface) + m_viewfinderSurface->disconnect(this); + disconnect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), + this, SLOT(viewFinderBitmapReady(const CFbsBitmap &))); + break; + default: + emit error(QCamera::CameraError, tr("Unexpected viewfinder error.")); + return; + } + } + + Q_ASSERT(!m_viewfinderDisplay); + m_viewfinderOutput = 0; + m_viewfinderType = OutputTypeNotSet; + + // Update state + switch (m_vfState) { + case EVFNotConnectedNotStarted: + case EVFNotConnectedIsStarted: + // Do nothing + break; + case EVFIsConnectedNotStarted: + m_vfState = EVFNotConnectedNotStarted; + break; + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + m_vfState = EVFNotConnectedIsStarted; + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } +} + +void S60CameraViewfinderEngine::startViewfinder(const bool internalStart) +{ + if (!internalStart) { + switch (m_vfState) { + case EVFNotConnectedNotStarted: + m_vfState = EVFNotConnectedIsStarted; + break; + case EVFIsConnectedNotStarted: + if (m_isViewFinderVisible) + m_vfState = EVFIsConnectedIsStartedIsVisible; + else + m_vfState = EVFIsConnectedIsStartedNotVisible; + break; + case EVFNotConnectedIsStarted: + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + // Already started, state does not change + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + } + + // Start viewfinder + if (m_vfState == EVFIsConnectedIsStartedIsVisible) { + + if (!m_cameraEngine) + return; + + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + + if (RWindow *window = m_viewfinderDisplay ? m_viewfinderDisplay->windowHandle() : 0) { + m_window = window; + } else { + emit error(QCamera::CameraError, tr("Requesting window for viewfinder failed.")); + return; + } + + const QRect extentRect = m_viewfinderDisplay ? m_viewfinderDisplay->extentRect() : QRect(); + const QRect clipRect = m_viewfinderDisplay ? m_viewfinderDisplay->clipRect() : QRect(); + + TRect extentRectSymbian = qRect2TRect(extentRect); + TRect clipRectSymbian = qRect2TRect(clipRect); + TRAPD(err, m_cameraEngine->StartDirectViewFinderL(m_wsSession, m_screenDevice, *m_window, extentRectSymbian, clipRectSymbian)); + if (err) { + if (err == KErrNotSupported) { + emit error(QCamera::NotSupportedFeatureError, tr("Requested viewfinder size is not supported.")); + } else { + emit error(QCamera::CameraError, tr("Starting viewfinder failed.")); + } + return; + } + + m_actualViewFinderSize = QSize(extentRectSymbian.Size().iWidth, extentRectSymbian.Size().iHeight); + m_viewfinderAspectRatio = qreal(m_actualViewFinderSize.width()) / qreal(m_actualViewFinderSize.height()); + + } else { // Bitmap ViewFinder + TSize size = TSize(m_viewfinderSize.width(), m_viewfinderSize.height()); + + if( m_viewfinderType == OutputTypeRenderer && m_viewfinderSurface) { + if (!m_surfaceFormat.isValid()) { + emit error(QCamera::NotSupportedFeatureError, tr("Invalid surface format.")); + return; + } + + // Start rendering to surface with correct size and format + if (!m_viewfinderSurface->isFormatSupported(m_surfaceFormat) || + !m_viewfinderSurface->start(m_surfaceFormat)) { + emit error(QCamera::NotSupportedFeatureError, tr("Failed to start surface.")); + return; + } + + if (!m_viewfinderSurface->isActive()) + return; + } + + TRAPD(vfErr, m_cameraEngine->StartViewFinderL(size)); + if (vfErr) { + if (vfErr == KErrNotSupported) { + emit error(QCamera::NotSupportedFeatureError, tr("Requested viewfinder size is not supported.")); + } else { + emit error(QCamera::CameraError, tr("Starting viewfinder failed.")); + } + return; + } + + m_actualViewFinderSize = QSize(size.iWidth, size.iHeight); + m_viewfinderAspectRatio = qreal(m_actualViewFinderSize.width()) / qreal(m_actualViewFinderSize.height()); + + // Notify control about the frame size (triggers frame position calculation) + if (m_viewfinderDisplay) { + m_viewfinderDisplay->setNativeSize(m_actualViewFinderSize); + } else { + if (m_viewfinderType == OutputTypeRenderer && m_viewfinderSurface) { + m_viewfinderSurface->stop(); + QVideoSurfaceFormat format = m_viewfinderSurface->surfaceFormat(); + format.setFrameSize(QSize(m_actualViewFinderSize)); + format.setViewport(QRect(0, 0, m_actualViewFinderSize.width(), m_actualViewFinderSize.height())); + m_viewfinderSurface->start(format); + } + } + } + } +} + +void S60CameraViewfinderEngine::stopViewfinder(const bool internalStop) +{ + // Stop if viewfinder is started + if (m_vfState == EVFIsConnectedIsStartedIsVisible) { + if (m_viewfinderOutput && m_viewfinderType == OutputTypeRenderer && m_viewfinderSurface) { + // Stop surface if one still exists + m_viewfinderSurface->stop(); + } + + if (m_cameraEngine) + m_cameraEngine->StopViewFinder(); + } + + // Update state + if (!internalStop) { + switch (m_vfState) { + case EVFNotConnectedNotStarted: + case EVFIsConnectedNotStarted: + // Discard + break; + case EVFNotConnectedIsStarted: + m_vfState = EVFNotConnectedNotStarted; + break; + case EVFIsConnectedIsStartedNotVisible: + case EVFIsConnectedIsStartedIsVisible: + m_vfState = EVFIsConnectedNotStarted; + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + } +} + +void S60CameraViewfinderEngine::MceoViewFinderFrameReady(CFbsBitmap& aFrame) +{ + emit viewFinderFrameReady(aFrame); + if (m_cameraEngine) + m_cameraEngine->ReleaseViewFinderBuffer(); +} + +void S60CameraViewfinderEngine::resetViewfinderSize(const QSize size) +{ + m_viewfinderSize = size; + + if(m_vfState != EVFIsConnectedIsStartedIsVisible) { + // Set native size to Window/Renderer Control + if (m_viewfinderDisplay) + m_viewfinderDisplay->setNativeSize(m_actualViewFinderSize); + return; + } + + stopViewfinder(true); + + startViewfinder(true); +} + +void S60CameraViewfinderEngine::resetVideoWindowSize() +{ + if (m_viewfinderDisplay) + resetViewfinderSize(m_viewfinderDisplay->extentRect().size()); +} + +void S60CameraViewfinderEngine::resetViewfinderDisplay() +{ + if (m_viewfinderNativeType == EDirectScreenViewFinder) { + + switch (m_viewfinderType) { + case OutputTypeVideoWidget: { + if (!m_viewfinderOutput) + return; + + // First stop viewfinder + stopViewfinder(true); + + RWindow *window = m_viewfinderDisplay->windowHandle(); + if (!window) { + return; + } + + // Then start it with the new WindowID + startViewfinder(true); + break; + } + case OutputTypeRenderer: + case OutputTypeVideoWindow: + // Do nothing + break; + + default: + // Not ViewFinder Output has been set, Discard + break; + } + } +} + +void S60CameraViewfinderEngine::rendererSurfaceSet() +{ + S60VideoRendererControl* viewFinderRenderControl = + qobject_cast(m_viewfinderOutput); + + // Reset old surface if needed + if (m_viewfinderSurface) { + handleVisibilityChange(false); + disconnect(m_viewfinderSurface); + if (viewFinderRenderControl->surface()) + stopViewfinder(true); // Temporary stop + else + stopViewfinder(); // Stop for good + m_viewfinderSize = QApplication::desktop()->screenGeometry().size(); + m_viewfinderSurface = 0; + } + + // Set new surface + m_viewfinderSurface = viewFinderRenderControl->surface(); + if (!m_viewfinderSurface) + return; + if (!m_viewfinderSurface->nativeResolution().isEmpty()) { + if (m_viewfinderSurface->nativeResolution() != m_viewfinderSize) + resetViewfinderSize(m_viewfinderSurface->nativeResolution()); + } + + connect(m_viewfinderSurface, SIGNAL(nativeResolutionChanged(const QSize&)), + this, SLOT(resetViewfinderSize(QSize))); + + // Set Surface Properties + if (m_viewfinderSurface->supportedPixelFormats().contains(QVideoFrame::Format_RGB32)) + m_surfaceFormat = QVideoSurfaceFormat(m_actualViewFinderSize, QVideoFrame::Format_RGB32); + else if (m_viewfinderSurface->supportedPixelFormats().contains(QVideoFrame::Format_ARGB32)) + m_surfaceFormat = QVideoSurfaceFormat(m_actualViewFinderSize, QVideoFrame::Format_ARGB32); + else { + return; + } + m_surfaceFormat.setFrameRate(KViewfinderFrameRate); + m_surfaceFormat.setYCbCrColorSpace(QVideoSurfaceFormat::YCbCr_Undefined); // EColor16MU (compatible with EColor16MA) + m_surfaceFormat.setPixelAspectRatio(1,1); // PAR 1:1 + + + connect(this, SIGNAL(viewFinderFrameReady(const CFbsBitmap &)), + this, SLOT(viewFinderBitmapReady(const CFbsBitmap &))); + + // Surface set, viewfinder is "visible" + handleVisibilityChange(true); +} + +void S60CameraViewfinderEngine::viewFinderBitmapReady(const CFbsBitmap &bitmap) +{ + CFbsBitmap *bitmapPtr = const_cast(&bitmap); + QPixmap pixmap = QPixmap::fromSymbianCFbsBitmap(bitmapPtr); + + QImage newImage = pixmap.toImage(); + if (newImage.format() != QImage::Format_ARGB32 && + newImage.format() != QImage::Format_RGB32) { + newImage = newImage.convertToFormat(QImage::Format_RGB32); + } + + if (!newImage.isNull()) { + QVideoFrame newFrame(newImage); + if (newFrame.isValid()) { + if (!m_viewfinderSurface->present(newFrame)) { + // Presenting may fail even if there are no errors (e.g. busy) + if (m_viewfinderSurface->error()) { + if (m_vfErrorsSignalled < KMaxVFErrorsSignalled) { + emit error(QCamera::CameraError, tr("Presenting viewfinder frame failed.")); + ++m_vfErrorsSignalled; + } + } + } + } else { + if (m_vfErrorsSignalled < KMaxVFErrorsSignalled) { + emit error(QCamera::CameraError, tr("Invalid viewfinder frame was received.")); + ++m_vfErrorsSignalled; + } + } + + } else { + if (m_vfErrorsSignalled < KMaxVFErrorsSignalled) { + emit error(QCamera::CameraError, tr("Failed to convert viewfinder frame to presentable image.")); + ++m_vfErrorsSignalled; + } + } +} + +void S60CameraViewfinderEngine::handleVisibilityChange(const bool isVisible) +{ + if (m_isViewFinderVisible == isVisible) + return; + + m_isViewFinderVisible = isVisible; + + if (m_isViewFinderVisible) { + switch (m_vfState) { + case EVFNotConnectedNotStarted: + case EVFIsConnectedNotStarted: + case EVFNotConnectedIsStarted: + case EVFIsConnectedIsStartedIsVisible: + // Discard + break; + case EVFIsConnectedIsStartedNotVisible: + m_vfState = EVFIsConnectedIsStartedIsVisible; + break; + default: + emit error(QCamera::CameraError, tr("General viewfinder error.")); + break; + } + startViewfinder(true); + } else { + // Stopping takes care of the state change + stopViewfinder(true); + } +} + +void S60CameraViewfinderEngine::handleWindowChange(RWindow *handle) +{ + stopViewfinder(true); + + if (handle) // New handle available, start viewfinder + startViewfinder(true); +} + +void S60CameraViewfinderEngine::checkAndRotateCamera() +{ + bool isUiNowLandscape = false; + QDesktopWidget* desktopWidget = QApplication::desktop(); + QRect screenRect = desktopWidget->screenGeometry(); + + if (screenRect.width() > screenRect.height()) + isUiNowLandscape = true; + else + isUiNowLandscape = false; + + // Rotate camera if possible + if (isUiNowLandscape != m_uiLandscape) { + stopViewfinder(true); + + // Request orientation reset + m_cameraControl->resetCameraOrientation(); + } + m_uiLandscape = isUiNowLandscape; +} + +void S60CameraViewfinderEngine::handleContentAspectRatioChange(const QSize& newSize) +{ + qreal newAspectRatio = qreal(newSize.width()) / qreal(newSize.height()); + // Check if aspect ratio changed + if (qFuzzyCompare(newAspectRatio, m_viewfinderAspectRatio)) + return; + + // Resize viewfinder by reducing either width or height to comply with the new aspect ratio + QSize newNativeResolution; + if (newAspectRatio > m_viewfinderAspectRatio) { // New AspectRatio is wider => Reduce height + newNativeResolution = QSize(m_actualViewFinderSize.width(), (m_actualViewFinderSize.width() / newAspectRatio)); + } else { // New AspectRatio is higher => Reduce width + newNativeResolution = QSize((m_actualViewFinderSize.height() * newAspectRatio), m_actualViewFinderSize.height()); + } + + // Notify aspect ratio change (use actual content size to notify that) + // This triggers item size/position re-calculation + if (m_viewfinderDisplay) + m_viewfinderDisplay->setNativeSize(newNativeResolution); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60cameraviewfinderengine.h b/src/plugins/symbian/ecam/s60cameraviewfinderengine.h new file mode 100644 index 000000000..c5df760d9 --- /dev/null +++ b/src/plugins/symbian/ecam/s60cameraviewfinderengine.h @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ +#ifndef S60CAMERAVIEWFINDERENGINE_H +#define S60CAMERAVIEWFINDERENGINE_H + +#include +#include + +#include + +#include "s60cameraengineobserver.h" + +class CCameraEngine; +class S60CameraControl; +class QAbstractVideoSurface; + +// For DirectScreen ViewFinder +class RWsSession; +class CWsScreenDevice; +class RWindowBase; +class RWindow; +class QDesktopWidget; + +class S60VideoDisplay; + +/* + * Class implementing video output selection for the viewfinder and the handler of + * all common viewfinder operations. + */ +class S60CameraViewfinderEngine : public QObject, public MCameraViewfinderObserver +{ + Q_OBJECT + +public: // Enums + + /* + * Defines whether viewfinder output backend control is of type + * QVideoWidgetControl, QVideoRendererControl or QVideoWindowControl + */ + enum ViewfinderOutputType { + OutputTypeNotSet = 0, // No viewfinder output connected + OutputTypeVideoWidget, // Using QVideoWidget + OutputTypeRenderer, // (Using QGraphicsVideoItem with) QVideoRendererControl + OutputTypeVideoWindow // Using QGraphicsVideoItem with QVideoWindow + }; + +public: // Constructor & Destructor + + S60CameraViewfinderEngine(S60CameraControl *control, + CCameraEngine *engine, + QObject *parent = 0); + ~S60CameraViewfinderEngine(); + +public: // Methods + + // Setting Viewfinder Output + void setVideoWidgetControl(QObject *viewfinderOutput); + void setVideoRendererControl(QObject *viewfinderOutput); + void setVideoWindowControl(QObject *viewfinderOutput); + void releaseControl(ViewfinderOutputType type); + + // Controls + void startViewfinder(const bool internalStart = false); + void stopViewfinder(const bool internalStop = false); + + // Start using new CameraEngine + void setNewCameraEngine(CCameraEngine *engine); + +protected: // MCameraViewfinderObserver + + void MceoViewFinderFrameReady(CFbsBitmap& aFrame); + +private: // Internal operation + + void checkAndRotateCamera(); + +signals: + + void error(int error, const QString &errorString); + void viewFinderFrameReady(const CFbsBitmap &bitmap); + +private slots: + + void resetViewfinderSize(const QSize size); + void resetVideoWindowSize(); + void resetViewfinderDisplay(); + void viewFinderBitmapReady(const CFbsBitmap &bitmap); + void handleVisibilityChange(const bool isVisible); + void handleWindowChange(RWindow *handle); + void handleDesktopResize(int screen); + void handleContentAspectRatioChange(const QSize& newSize); + void rendererSurfaceSet(); + +private: // Enums + + /* + * Defines the internal state of the viewfinder. ViewFinder will only be + * started if output is connected to Camera and Camera is started (and + * ViewFinder widget is visible in case of QVideoWidget). + */ + enum ViewFinderState { + EVFNotConnectedNotStarted = 0, // 0 - No output connected, viewfinder is not started + EVFNotConnectedIsStarted, // 1 - No output connected, viewfinder is started + EVFIsConnectedNotStarted, // 2 - Output is connected, viewfinder is not started + EVFIsConnectedIsStartedNotVisible, // 3 - Output is connected, viewfinder is started but is not visible + EVFIsConnectedIsStartedIsVisible // 4 - Output is connected, viewfinder is started and is visible + }; + + /* + * The native type of ViewFinder. DirectScreen ViewFinder is used with + * QVideoWidget if support for it is available in the platform. For + * QGraphicsVideoItem Bitmap ViewFinder is always used. + */ + enum NativeViewFinderType { + EBitmapViewFinder = 0, + EDirectScreenViewFinder + }; + +private: // Data + + CCameraEngine *m_cameraEngine; + S60CameraControl *m_cameraControl; + QObject *m_viewfinderOutput; + S60VideoDisplay *m_viewfinderDisplay; + QAbstractVideoSurface *m_viewfinderSurface; // Used only by QVideoRendererControl + RWsSession &m_wsSession; + CWsScreenDevice &m_screenDevice; + RWindowBase *m_window; + QDesktopWidget *m_desktopWidget; + ViewFinderState m_vfState; + QSize m_viewfinderSize; + // Actual viewfinder size, which may differ from requested + // (m_viewfinderSize), if the size/aspect ratio was not supported. + QSize m_actualViewFinderSize; + qreal m_viewfinderAspectRatio; + ViewfinderOutputType m_viewfinderType; + NativeViewFinderType m_viewfinderNativeType; + QVideoSurfaceFormat m_surfaceFormat; // Used only by QVideoRendererControl + bool m_isViewFinderVisible; + bool m_uiLandscape; // For detecting UI rotation + int m_vfErrorsSignalled; +}; + +#endif // S60CAMERAVIEWFINDERENGINE_H diff --git a/src/plugins/symbian/ecam/s60imagecapturesession.cpp b/src/plugins/symbian/ecam/s60imagecapturesession.cpp new file mode 100644 index 000000000..16d240c7b --- /dev/null +++ b/src/plugins/symbian/ecam/s60imagecapturesession.cpp @@ -0,0 +1,1884 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "s60imagecapturesession.h" +#include "s60videowidgetcontrol.h" +#include "s60cameraservice.h" +#include "s60cameraconstants.h" + +#include // CFbsBitmap +#include +#include // ICL Decoder (for SnapShot) & Encoder (for Bitmap Images) + +S60ImageCaptureDecoder::S60ImageCaptureDecoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC8 *data, + const TDesC16 *fileName) : + CActive(CActive::EPriorityStandard), + m_imageSession(imageSession), + m_fs(fileSystemAccess), + m_jpegImageData(data), + m_jpegImageFile(fileName), + m_fileInput(false) +{ + CActiveScheduler::Add(this); +} + +S60ImageCaptureDecoder::~S60ImageCaptureDecoder() +{ + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } +} + +S60ImageCaptureDecoder *S60ImageCaptureDecoder::FileNewL(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName) +{ + S60ImageCaptureDecoder* self = new (ELeave) S60ImageCaptureDecoder(imageSession, + fileSystemAccess, + 0, + fileName); + CleanupStack::PushL(self); + self->ConstructL(true); + CleanupStack::Pop(self); + return self; +} + +S60ImageCaptureDecoder *S60ImageCaptureDecoder::DataNewL(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC8 *data) +{ + S60ImageCaptureDecoder* self = new (ELeave) S60ImageCaptureDecoder(imageSession, + fileSystemAccess, + data, + 0); + CleanupStack::PushL(self); + self->ConstructL(false); + CleanupStack::Pop(self); + return self; +} + +void S60ImageCaptureDecoder::ConstructL(const bool fileInput) +{ + if (fileInput) { + if (!m_imageSession || !m_fs || !m_jpegImageFile) + User::Leave(KErrGeneral); + m_imageDecoder = CImageDecoder::FileNewL(*m_fs, *m_jpegImageFile); + } else { + if (!m_imageSession || !m_fs || !m_jpegImageData) + User::Leave(KErrGeneral); + m_imageDecoder = CImageDecoder::DataNewL(*m_fs, *m_jpegImageData); + } +} + +void S60ImageCaptureDecoder::decode(CFbsBitmap *destBitmap) +{ + if (m_imageDecoder) { + m_imageDecoder->Convert(&iStatus, *destBitmap, 0); + SetActive(); + } + else + m_imageSession->setError(KErrGeneral, QLatin1String("Preview image creation failed.")); +} + +TFrameInfo *S60ImageCaptureDecoder::frameInfo() +{ + if (m_imageDecoder) { + m_frameInfo = m_imageDecoder->FrameInfo(); + return &m_frameInfo; + } + else + return 0; +} + +void S60ImageCaptureDecoder::RunL() +{ + m_imageSession->handleImageDecoded(iStatus.Int()); +} + +void S60ImageCaptureDecoder::DoCancel() +{ + if (m_imageDecoder) + m_imageDecoder->Cancel(); +} + +TInt S60ImageCaptureDecoder::RunError(TInt aError) +{ + m_imageSession->setError(aError, QLatin1String("Preview image creation failed.")); + return KErrNone; +} + +//============================================================================= + +S60ImageCaptureEncoder::S60ImageCaptureEncoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName, + TInt jpegQuality) : + CActive(CActive::EPriorityStandard), + m_imageSession(imageSession), + m_fileSystemAccess(fileSystemAccess), + m_fileName(fileName), + m_jpegQuality(jpegQuality) +{ + CActiveScheduler::Add(this); +} + +S60ImageCaptureEncoder::~S60ImageCaptureEncoder() +{ + if (m_frameImageData) { + delete m_frameImageData; + m_frameImageData = 0; + } + if (m_imageEncoder) { + delete m_imageEncoder; + m_imageEncoder = 0; + } +} + +S60ImageCaptureEncoder *S60ImageCaptureEncoder::NewL(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName, + TInt jpegQuality) +{ + S60ImageCaptureEncoder* self = new (ELeave) S60ImageCaptureEncoder(imageSession, + fileSystemAccess, + fileName, + jpegQuality); + CleanupStack::PushL(self); + self->ConstructL(); + CleanupStack::Pop(self); + return self; +} + +void S60ImageCaptureEncoder::ConstructL() +{ + if (!m_imageSession || !m_fileSystemAccess || !m_fileName) + User::Leave(KErrGeneral); + + m_imageEncoder = CImageEncoder::FileNewL(*m_fileSystemAccess, + *m_fileName, + CImageEncoder::EOptionNone, + KImageTypeJPGUid); + CleanupStack::PushL(m_imageEncoder); + + // Set Jpeg Quality + m_frameImageData = CFrameImageData::NewL(); + CleanupStack::PushL(m_frameImageData); + + TJpegImageData* jpegFormat = new( ELeave ) TJpegImageData; + CleanupStack::PushL(jpegFormat); + + jpegFormat->iQualityFactor = m_jpegQuality; + + // jpegFormat (TJpegImageData) ownership transferred to m_frameImageData (CFrameImageData) + User::LeaveIfError( m_frameImageData->AppendImageData(jpegFormat)); + + CleanupStack::Pop(jpegFormat); + CleanupStack::Pop(m_frameImageData); + CleanupStack::Pop(m_imageEncoder); +} + +void S60ImageCaptureEncoder::encode(CFbsBitmap *sourceBitmap) +{ + if (m_imageEncoder) { + m_imageEncoder->Convert(&iStatus, *sourceBitmap, m_frameImageData); + SetActive(); + } + else + m_imageSession->setError(KErrGeneral, QLatin1String("Saving image to file failed.")); +} + +void S60ImageCaptureEncoder::RunL() +{ + m_imageSession->handleImageEncoded(iStatus.Int()); +} + +void S60ImageCaptureEncoder::DoCancel() +{ + if (m_imageEncoder) + m_imageEncoder->Cancel(); +} + +TInt S60ImageCaptureEncoder::RunError(TInt aError) +{ + m_imageSession->setError(aError, QLatin1String("Saving image to file failed.")); + return KErrNone; +} + +//============================================================================= + +S60ImageCaptureSession::S60ImageCaptureSession(QObject *parent) : + QObject(parent), + m_cameraEngine(0), + m_advancedSettings(0), + m_cameraInfo(0), + m_previewBitmap(0), + m_activeScheduler(0), + m_fileSystemAccess(0), + m_imageDecoder(0), + m_imageEncoder(0), + m_error(KErrNone), + m_activeDeviceIndex(KDefaultCameraDevice), + m_cameraStarted(false), + m_icState(EImageCaptureNotPrepared), + m_currentCodec(QString()), + m_captureSize(QSize()), + m_symbianImageQuality(QtMultimediaKit::HighQuality * KSymbianImageQualityCoefficient), + m_captureSettingsSet(false), + m_stillCaptureFileName(QString()), + m_requestedStillCaptureFileName(QString()), + m_currentImageId(0), + m_captureWhenReady(false), + m_previewDecodingOngoing(false), + m_previewInWaitLoop(false) +{ + // Define supported image codecs + m_supportedImageCodecs << "image/jpeg"; + + initializeImageCaptureSettings(); + + // Install ActiveScheduler if needed + if (!CActiveScheduler::Current()) { + m_activeScheduler = new CActiveScheduler; + CActiveScheduler::Install(m_activeScheduler); + } +} + +S60ImageCaptureSession::~S60ImageCaptureSession() +{ + // Delete AdvancedSettings (Should already be destroyed by CameraControl) + deleteAdvancedSettings(); + + m_formats.clear(); + m_supportedImageCodecs.clear(); + + if (m_imageDecoder) { + m_imageDecoder->Cancel(); + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_imageEncoder) { + m_imageEncoder->Cancel(); + delete m_imageEncoder; + m_imageEncoder = 0; + } + + if (m_previewBitmap) { + delete m_previewBitmap; + m_previewBitmap = 0; + } + + // Uninstall ActiveScheduler if needed + if (m_activeScheduler) { + CActiveScheduler::Install(0); + delete m_activeScheduler; + m_activeScheduler = 0; + } +} + +CCamera::TFormat S60ImageCaptureSession::defaultImageFormat() +{ + // Primary Camera + if (m_activeDeviceIndex == 0) + return KDefaultImageFormatPrimaryCam; + + // Secondary Camera or other + else + return KDefaultImageFormatSecondaryCam; +} + +bool S60ImageCaptureSession::isDeviceReady() +{ +#ifdef Q_CC_NOKIAX86 // Emulator + return true; +#endif + + if (m_cameraEngine) + return m_cameraEngine->IsCameraReady(); + + return false; +} + +void S60ImageCaptureSession::deleteAdvancedSettings() +{ + if (m_advancedSettings) { + delete m_advancedSettings; + m_advancedSettings = 0; + emit advancedSettingChanged(); + } +} + +void S60ImageCaptureSession::setCameraHandle(CCameraEngine* camerahandle) +{ + if (camerahandle) { + m_cameraEngine = camerahandle; + resetSession(); + + // Set default settings + initializeImageCaptureSettings(); + } +} + +void S60ImageCaptureSession::setCurrentDevice(TInt deviceindex) +{ + m_activeDeviceIndex = deviceindex; +} + +void S60ImageCaptureSession::notifySettingsSet() +{ + m_captureSettingsSet = true; +} + +void S60ImageCaptureSession::resetSession(bool errorHandling) +{ + // Delete old AdvancedSettings + deleteAdvancedSettings(); + + m_captureWhenReady = false; + m_previewDecodingOngoing = false; + m_previewInWaitLoop = false; + m_stillCaptureFileName = QString(); + m_requestedStillCaptureFileName = QString(); + m_icState = EImageCaptureNotPrepared; + + m_error = KErrNone; + m_currentFormat = defaultImageFormat(); + + int err = KErrNone; + m_advancedSettings = S60CameraSettings::New(err, this, m_cameraEngine); + if (err == KErrNotSupported) { + m_advancedSettings = 0; +#ifndef S60_31_PLATFORM // Post S60 3.1 Platform + // Adv. settings may not be supported for other than the Primary Camera + if (m_cameraEngine->CurrentCameraIndex() == 0) + setError(err, tr("Unexpected camera error.")); +#endif // !S60_31_PLATFORM + } else if (err != KErrNone) { // Other errors + m_advancedSettings = 0; + qWarning("Failed to create camera settings handler."); + if (errorHandling) + emit cameraError(QCamera::ServiceMissingError, tr("Failed to recover from error.")); + else + setError(err, tr("Unexpected camera error.")); + return; + } + + if (m_advancedSettings) { + if (m_cameraEngine) + m_cameraEngine->SetAdvancedObserver(m_advancedSettings); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + } + + updateImageCaptureFormats(); + + emit advancedSettingChanged(); +} + +S60CameraSettings* S60ImageCaptureSession::advancedSettings() +{ + return m_advancedSettings; +} + +/* + * This function can be used both internally and from Control classes using + * this session. The error notification will go to the client application + * either through QCameraImageCapture (if captureError is true) or QCamera (if + * captureError is false, default) error signal. + */ +void S60ImageCaptureSession::setError(const TInt error, + const QString &description, + const bool captureError) +{ + if (error == KErrNone) + return; + + m_error = error; + QCameraImageCapture::Error cameraError = fromSymbianErrorToQtMultimediaError(error); + + if (captureError) { + emit this->captureError(m_currentImageId, cameraError, description); + if (cameraError != QCameraImageCapture::NotSupportedFeatureError) + resetSession(true); + } else { + emit this->cameraError(cameraError, description); + if (cameraError != QCamera::NotSupportedFeatureError) + resetSession(true); + } +} + +QCameraImageCapture::Error S60ImageCaptureSession::fromSymbianErrorToQtMultimediaError(int aError) +{ + switch(aError) { + case KErrNone: + return QCameraImageCapture::NoError; // No errors have occurred + case KErrNotReady: + return QCameraImageCapture::NotReadyError; // Not ready for operation + case KErrNotSupported: + return QCameraImageCapture::NotSupportedFeatureError; // The feature is not supported + case KErrNoMemory: + return QCameraImageCapture::OutOfSpaceError; // Out of disk space + case KErrNotFound: + case KErrBadHandle: + return QCameraImageCapture::ResourceError; // No resources available + + default: + return QCameraImageCapture::ResourceError; // Other error has occurred + } +} + +int S60ImageCaptureSession::currentImageId() const +{ + return m_currentImageId; +} + +void S60ImageCaptureSession::initializeImageCaptureSettings() +{ + if (m_captureSettingsSet) + return; + + m_currentCodec = KDefaultImageCodec; + m_captureSize = QSize(-1, -1); + m_currentFormat = defaultImageFormat(); + + // Resolution + if (m_cameraEngine) { + QList resolutions = supportedCaptureSizesForCodec(imageCaptureCodec()); + foreach (QSize reso, resolutions) { + if ((reso.width() * reso.height()) > (m_captureSize.width() * m_captureSize.height())) + m_captureSize = reso; + } + } else { + m_captureSize = KDefaultImageResolution; + } + + m_symbianImageQuality = KDefaultImageQuality; +} + +/* + * This function selects proper format to be used for the captured image based + * on the requested image codec. + */ +CCamera::TFormat S60ImageCaptureSession::selectFormatForCodec(const QString &codec) +{ + CCamera::TFormat format = CCamera::EFormatMonochrome; + + if (codec == "image/jpg" || codec == "image/jpeg") { + // Primary Camera + if (m_activeDeviceIndex == 0) + format = KDefaultImageFormatPrimaryCam; + + // Secondary Camera or other + else + format = KDefaultImageFormatSecondaryCam; + + return format; + } + + setError(KErrNotSupported, tr("Failed to select color format to be used with image codec.")); + return format; +} + +int S60ImageCaptureSession::prepareImageCapture() +{ + if (m_cameraEngine) { + if (!m_cameraEngine->IsCameraReady()) { + // Reset state to make sure camera is prepared before capturing image + m_icState = EImageCaptureNotPrepared; + return KErrNotReady; + } + + // First set the quality + CCamera *camera = m_cameraEngine->Camera(); + if (camera) + camera->SetJpegQuality(m_symbianImageQuality); + else + setError(KErrNotReady, tr("Setting image quality failed."), true); + + // Then prepare with correct resolution and format + TSize captureSize = TSize(m_captureSize.width(), m_captureSize.height()); + TRAPD(symbianError, m_cameraEngine->PrepareL(captureSize, m_currentFormat)); + if (!symbianError) + m_icState = EImageCapturePrepared; + + // Check if CaptureSize was modified + if (captureSize.iWidth != m_captureSize.width() || captureSize.iHeight != m_captureSize.height()) + m_captureSize = QSize(captureSize.iWidth, captureSize.iHeight); + emit captureSizeChanged(m_captureSize); + +#ifdef ECAM_PREVIEW_API + // Subscribe previews + MCameraPreviewObserver *observer = this; + m_cameraEngine->EnablePreviewProvider(observer); +#endif // ECAM_PREVIEW_API + + return symbianError; + } + + return KErrGeneral; +} + +void S60ImageCaptureSession::releaseImageCapture() +{ + // Make sure ImageCapture is prepared the next time it is being activated + m_icState = EImageCaptureNotPrepared; + +#ifdef ECAM_PREVIEW_API + // Cancel preview subscription + m_cameraEngine->DisablePreviewProvider(); +#endif // ECAM_PREVIEW_API +} + +int S60ImageCaptureSession::capture(const QString &fileName) +{ + if (!m_cameraStarted) { + m_captureWhenReady = true; + m_requestedStillCaptureFileName = fileName; // Save name, it will be processed during actual capture + return 0; + } + + if (m_icState < EImageCapturePrepared) { + int prepareSuccess = prepareImageCapture(); + if (prepareSuccess) { + setError(prepareSuccess, tr("Failure during image capture preparation."), true); + return 0; + } + } else if (m_icState > EImageCapturePrepared) { + setError(KErrNotReady, tr("Previous operation is still ongoing."), true); + return 0; + } + + m_icState = EImageCaptureCapturing; + + // Give new ID for the new image + m_currentImageId += 1; + + emit readyForCaptureChanged(false); + + processFileName(fileName); + + if (m_cameraEngine) { + TRAPD(err, m_cameraEngine->CaptureL()); + setError(err, tr("Image capture failed."), true); + } else { + setError(KErrNotReady, tr("Unexpected camera error."), true); + } + +#ifdef Q_CC_NOKIAX86 // Emulator + QImage *snapImage = new QImage(QLatin1String("C:/Data/testimage.jpg")); + emit imageExposed(m_currentImageId); + emit imageCaptured(m_currentImageId, *snapImage); + emit imageSaved(m_currentImageId, m_stillCaptureFileName); +#endif // Q_CC_NOKIAX86 + + return m_currentImageId; +} + +void S60ImageCaptureSession::cancelCapture() +{ + if (m_icState != EImageCaptureCapturing) + return; + + if (m_cameraEngine) + m_cameraEngine->CancelCapture(); + + m_icState = EImageCapturePrepared; +} + +void S60ImageCaptureSession::processFileName(const QString &fileName) +{ + // Empty FileName - Use default file name and path (C:\Data\Images\image.jpg) + if (fileName.isEmpty()) { + // Make sure default directory exists + QDir videoDir(QDir::rootPath()); + if (!videoDir.exists(KDefaultImagePath)) + videoDir.mkpath(KDefaultImagePath); + QString defaultFile = KDefaultImagePath; + defaultFile.append("\\"); + defaultFile.append(KDefaultImageFileName); + m_stillCaptureFileName = defaultFile; + + } else { // Not empty + + QString fullFileName; + + // Relative FileName + if (!fileName.contains(":")) { + // Extract file name and path from the URL + fullFileName = KDefaultImagePath; + if (fileName.at(0) != '\\') + fullFileName.append("\\"); + fullFileName.append(QDir::toNativeSeparators(QDir::cleanPath(fileName))); + + // Absolute FileName + } else { + // Extract file name and path from the given location + fullFileName = QDir::toNativeSeparators(QDir::cleanPath(fileName)); + } + + QString fileNameOnly = fullFileName.right(fullFileName.length() - fullFileName.lastIndexOf("\\") - 1); + QString directory = fullFileName.left(fullFileName.lastIndexOf("\\")); + if (directory.lastIndexOf("\\") == (directory.length() - 1)) + directory = directory.left(directory.length() - 1); + + // URL is Absolute path, not including file name + if (!fileNameOnly.contains(".")) { + if (fileNameOnly != "") { + directory.append("\\"); + directory.append(fileNameOnly); + } + fileNameOnly = KDefaultImageFileName; + } + + // Make sure absolute directory exists + QDir imageDir(QDir::rootPath()); + if (!imageDir.exists(directory)) + imageDir.mkpath(directory); + + QString resolvedFileName = directory; + resolvedFileName.append("\\"); + resolvedFileName.append(fileNameOnly); + m_stillCaptureFileName = resolvedFileName; + } +} + +void S60ImageCaptureSession::MceoFocusComplete() +{ + emit focusStatusChanged(QCamera::Locked, QCamera::LockAcquired); +} + +void S60ImageCaptureSession::MceoCapturedDataReady(TDesC8* aData) +{ + emit imageExposed(m_currentImageId); + + m_icState = EImageCaptureWritingImage; + + TFileName path = convertImagePath(); + + // Try to save image and inform if it was succcesful + TRAPD(err, saveImageL(aData, path)); + if (err) { + if (m_previewDecodingOngoing) + m_previewDecodingOngoing = false; // Reset + + setError(err, tr("Writing captured image to a file failed."), true); + m_icState = EImageCapturePrepared; + return; + } + + m_icState = EImageCapturePrepared; + +} + +void S60ImageCaptureSession::MceoCapturedBitmapReady(CFbsBitmap* aBitmap) +{ + emit imageExposed(m_currentImageId); + + m_icState = EImageCaptureWritingImage; + + if(aBitmap) + { +#ifndef ECAM_PREVIEW_API + if (m_previewDecodingOngoing) { + m_previewInWaitLoop = true; + CActiveScheduler::Start(); // Wait for the completion of the previous Preview generation + } + + // Delete old instances if needed + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_previewBitmap) { + delete m_previewBitmap; + m_previewBitmap = 0; + } +#endif // ECAM_CAMERA_API + if (m_imageEncoder) { + delete m_imageEncoder; + m_imageEncoder = 0; + } + if (m_fileSystemAccess) { + m_fileSystemAccess->Close(); + delete m_fileSystemAccess; + m_fileSystemAccess = 0; + } + + TInt saveError = KErrNone; + TFileName path = convertImagePath(); + + // Create FileSystem access + m_fileSystemAccess = new RFs; + if (!m_fileSystemAccess) { + setError(KErrNoMemory, tr("Failed to write captured image to a file.")); + return; + } + saveError = m_fileSystemAccess->Connect(); + if (saveError) { + setError(saveError, tr("Failed to write captured image to a file.")); + return; + } + + TRAP(saveError, m_imageEncoder = S60ImageCaptureEncoder::NewL(this, + m_fileSystemAccess, + &path, + m_symbianImageQuality)); + if (saveError) + setError(saveError, tr("Saving captured image failed."), true); + m_previewDecodingOngoing = true; + m_imageEncoder->encode(aBitmap); + + } else { + setError(KErrBadHandle, tr("Unexpected camera error."), true); + } + + m_icState = EImageCapturePrepared; +} + +void S60ImageCaptureSession::MceoHandleError(TCameraEngineError aErrorType, TInt aError) +{ + Q_UNUSED(aErrorType); + setError(aError, tr("General camera error.")); +} + +TFileName S60ImageCaptureSession::convertImagePath() +{ + TFileName path = KNullDesC(); + + // Convert to Symbian path + TPtrC16 attachmentPath(KNullDesC); + + // Path is already included in filename + attachmentPath.Set(reinterpret_cast(QDir::toNativeSeparators(m_stillCaptureFileName).utf16())); + path.Append(attachmentPath); + + return path; +} + +/* + * Creates (asynchronously) Preview Image from Jpeg ImageBuffer and also + * writes Jpeg (synchronously) to a file. + */ +void S60ImageCaptureSession::saveImageL(TDesC8 *aData, TFileName &aPath) +{ + if (aData == 0) + setError(KErrGeneral, tr("Captured image data is not available."), true); + + if (aPath.Size() > 0) { +#ifndef ECAM_PREVIEW_API + if (m_previewDecodingOngoing) { + m_previewInWaitLoop = true; + CActiveScheduler::Start(); // Wait for the completion of the previous Preview generation + } + + // Delete old instances if needed + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_previewBitmap) { + delete m_previewBitmap; + m_previewBitmap = 0; + } +#endif // ECAM_PREVIEW_API + if (m_fileSystemAccess) { + m_fileSystemAccess->Close(); + delete m_fileSystemAccess; + m_fileSystemAccess = 0; + } + + RFs *fileSystemAccess = new (ELeave) RFs; + User::LeaveIfError(fileSystemAccess->Connect()); + CleanupClosePushL(*fileSystemAccess); + +#ifndef ECAM_PREVIEW_API + // Generate Thumbnail to be used as Preview + S60ImageCaptureDecoder *imageDecoder = S60ImageCaptureDecoder::DataNewL(this, fileSystemAccess, aData); + CleanupStack::PushL(imageDecoder); + + // Set proper Preview Size + TSize scaledSize((m_captureSize.width() / KSnapshotDownScaleFactor), (m_captureSize.height() / KSnapshotDownScaleFactor)); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/2)), (m_captureSize.height() / (KSnapshotDownScaleFactor/2))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/4)), (m_captureSize.height() / (KSnapshotDownScaleFactor/4))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize(m_captureSize.width(), m_captureSize.height()); + + TFrameInfo *info = imageDecoder->frameInfo(); + if (!info) { + setError(KErrGeneral, tr("Preview image creation failed.")); + return; + } + + CFbsBitmap *previewBitmap = new (ELeave) CFbsBitmap; + CleanupStack::PushL(previewBitmap); + TInt bitmapCreationErr = previewBitmap->Create(scaledSize, info->iFrameDisplayMode); + if (bitmapCreationErr) { + setError(bitmapCreationErr, tr("Preview creation failed.")); + return; + } + + // Jpeg conversion completes in RunL + m_previewDecodingOngoing = true; + imageDecoder->decode(previewBitmap); +#endif // ECAM_PREVIEW_API + + RFile file; + TInt fileWriteErr = KErrNone; + fileWriteErr = file.Replace(*fileSystemAccess, aPath, EFileWrite); + if (fileWriteErr) + User::Leave(fileWriteErr); + CleanupClosePushL(file); // Close if Leaves + + fileWriteErr = file.Write(*aData); + if (fileWriteErr) + User::Leave(fileWriteErr); + + CleanupStack::PopAndDestroy(&file); +#ifdef ECAM_PREVIEW_API + CleanupStack::PopAndDestroy(fileSystemAccess); +#else // !ECAM_PREVIEW_API + // Delete when Image is decoded + CleanupStack::Pop(previewBitmap); + CleanupStack::Pop(imageDecoder); + CleanupStack::Pop(fileSystemAccess); + + // Set member variables (Cannot leave any more) + m_previewBitmap = previewBitmap; + m_imageDecoder = imageDecoder; + m_fileSystemAccess = fileSystemAccess; +#endif // ECAM_PREVIEW_API + + emit imageSaved(m_currentImageId, m_stillCaptureFileName); + + // Inform that we can continue taking more pictures + emit readyForCaptureChanged(true); + + // For custom preview generation, image buffer gets released in RunL() +#ifdef ECAM_PREVIEW_API + releaseImageBuffer(); +#endif // ECAM_PREVIEW_API + + } else { + setError(KErrPathNotFound, tr("Invalid path given."), true); + } +} + +void S60ImageCaptureSession::releaseImageBuffer() +{ + if (m_cameraEngine) + m_cameraEngine->ReleaseImageBuffer(); + else + setError(KErrNotReady, tr("Unexpected camera error."), true); +} + +/* + * Queries camera properties + * Results are returned to member variable m_info + * + * @return boolean indicating if querying the info was a success + */ +bool S60ImageCaptureSession::queryCurrentCameraInfo() +{ + if (m_cameraEngine) { + m_cameraInfo = m_cameraEngine->CameraInfo(); + return true; + } + + return false; +} + +/* + * This function handles different camera status changes + */ +void S60ImageCaptureSession::cameraStatusChanged(QCamera::Status status) +{ + if (status == QCamera::ActiveStatus) { + m_cameraStarted = true; + if (m_captureWhenReady) + capture(m_requestedStillCaptureFileName); + }else if (status == QCamera::UnloadedStatus) { + m_cameraStarted = false; + m_icState = EImageCaptureNotPrepared; + } + else + m_cameraStarted = false; +} + +QSize S60ImageCaptureSession::captureSize() const +{ + return m_captureSize; +} + +QSize S60ImageCaptureSession::minimumCaptureSize() +{ + return supportedCaptureSizesForCodec(formatMap().key(m_currentFormat)).first(); +} +QSize S60ImageCaptureSession::maximumCaptureSize() +{ + return supportedCaptureSizesForCodec(formatMap().key(m_currentFormat)).last(); +} + +void S60ImageCaptureSession::setCaptureSize(const QSize &size) +{ + if (size.isNull() || + size.isEmpty() || + size == QSize(-1,-1)) { + // An empty QSize indicates the encoder should make an optimal choice based on what is + // available from the image source and the limitations of the codec. + m_captureSize = supportedCaptureSizesForCodec(formatMap().key(m_currentFormat)).last(); + } + else + m_captureSize = size; +} + +QList S60ImageCaptureSession::supportedCaptureSizesForCodec(const QString &codecName) +{ + QList list; + + // If we have CameraEngine loaded and we can update CameraInfo + if (m_cameraEngine && queryCurrentCameraInfo()) { + CCamera::TFormat format; + if (codecName == "") + format = defaultImageFormat(); + else + format = selectFormatForCodec(codecName); + + CCamera *camera = m_cameraEngine->Camera(); + TSize imageSize; + if (camera) { + for (int i = 0; i < m_cameraInfo->iNumImageSizesSupported; i++) { + camera->EnumerateCaptureSizes(imageSize, i, format); + list << QSize(imageSize.iWidth, imageSize.iHeight); // Add resolution to the list + } + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + // Add some for testing purposes + list << QSize(50, 50); + list << QSize(100, 100); + list << QSize(800,600); +#endif + + return list; +} + +QMap S60ImageCaptureSession::formatMap() +{ + QMap formats; + + // Format list copied from CCamera::TFormat (in ecam.h) + formats.insert("Monochrome", 0x0001); + formats.insert("16bitRGB444", 0x0002); + formats.insert("16BitRGB565", 0x0004); + formats.insert("32BitRGB888", 0x0008); + formats.insert("Jpeg", 0x0010); + formats.insert("Exif", 0x0020); + formats.insert("FbsBitmapColor4K", 0x0040); + formats.insert("FbsBitmapColor64K", 0x0080); + formats.insert("FbsBitmapColor16M", 0x0100); + formats.insert("UserDefined", 0x0200); + formats.insert("YUV420Interleaved", 0x0400); + formats.insert("YUV420Planar", 0x0800); + formats.insert("YUV422", 0x1000); + formats.insert("YUV422Reversed", 0x2000); + formats.insert("YUV444", 0x4000); + formats.insert("YUV420SemiPlanar", 0x8000); + formats.insert("FbsBitmapColor16MU", 0x00010000); + formats.insert("MJPEG", 0x00020000); + formats.insert("EncodedH264", 0x00040000); + + return formats; +} + +QMap S60ImageCaptureSession::codecDescriptionMap() +{ + QMap formats; + + formats.insert("image/jpg", "JPEG image codec"); + + return formats; +} + +QStringList S60ImageCaptureSession::supportedImageCaptureCodecs() +{ +#ifdef Q_CC_NOKIAX86 // Emulator + return formatMap().keys(); +#endif + + return m_supportedImageCodecs; +} + +void S60ImageCaptureSession::updateImageCaptureFormats() +{ + m_formats.clear(); + if (m_cameraEngine && queryCurrentCameraInfo()) { + TUint32 supportedFormats = m_cameraInfo->iImageFormatsSupported; + +#ifdef S60_3X_PLATFORM // S60 3.1 & 3.2 + int maskEnd = CCamera::EFormatFbsBitmapColor16MU; +#else // S60 5.0 or later + int maskEnd = CCamera::EFormatEncodedH264; +#endif // S60_3X_PLATFORM + + for (int mask = CCamera::EFormatMonochrome; mask <= maskEnd; mask <<= 1) { + if (supportedFormats & mask) + m_formats << mask; // Store mask of supported format + } + } +} + +QString S60ImageCaptureSession::imageCaptureCodec() +{ + return m_currentCodec; +} +void S60ImageCaptureSession::setImageCaptureCodec(const QString &codecName) +{ + if (!codecName.isEmpty()) { + if (supportedImageCaptureCodecs().contains(codecName, Qt::CaseInsensitive) || + codecName == "image/jpg") { + m_currentCodec = codecName; + m_currentFormat = selectFormatForCodec(m_currentCodec); + } else { + setError(KErrNotSupported, tr("Requested image codec is not supported")); + } + } else { + m_currentCodec = KDefaultImageCodec; + m_currentFormat = selectFormatForCodec(m_currentCodec); + } +} + +QString S60ImageCaptureSession::imageCaptureCodecDescription(const QString &codecName) +{ + QString description = codecDescriptionMap().value(codecName); + return description; +} + +QtMultimediaKit::EncodingQuality S60ImageCaptureSession::captureQuality() const +{ + switch (m_symbianImageQuality) { + case KJpegQualityVeryLow: + return QtMultimediaKit::VeryLowQuality; + case KJpegQualityLow: + return QtMultimediaKit::LowQuality; + case KJpegQualityNormal: + return QtMultimediaKit::NormalQuality; + case KJpegQualityHigh: + return QtMultimediaKit::HighQuality; + case KJpegQualityVeryHigh: + return QtMultimediaKit::VeryHighQuality; + + default: + // Return normal as default + return QtMultimediaKit::NormalQuality; + } +} + +void S60ImageCaptureSession::setCaptureQuality(const QtMultimediaKit::EncodingQuality &quality) +{ + // Use sensible presets + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_symbianImageQuality = KJpegQualityVeryLow; + break; + case QtMultimediaKit::LowQuality: + m_symbianImageQuality = KJpegQualityLow; + break; + case QtMultimediaKit::NormalQuality: + m_symbianImageQuality = KJpegQualityNormal; + break; + case QtMultimediaKit::HighQuality: + m_symbianImageQuality = KJpegQualityHigh; + break; + case QtMultimediaKit::VeryHighQuality: + m_symbianImageQuality = KJpegQualityVeryHigh; + break; + + default: + m_symbianImageQuality = quality * KSymbianImageQualityCoefficient; + break; + } +} + +qreal S60ImageCaptureSession::maximumZoom() +{ + qreal maxZoomFactor = 1.0; + + if (queryCurrentCameraInfo()) { + maxZoomFactor = m_cameraInfo->iMaxZoomFactor; + + if (maxZoomFactor == 0.0 || maxZoomFactor == 1.0) { + return 1.0; // Not supported + } else { + return maxZoomFactor; + } + } else { + return 1.0; + } +} + +qreal S60ImageCaptureSession::minZoom() +{ + qreal minZoomValue = 1.0; + + if (queryCurrentCameraInfo()) { + minZoomValue = m_cameraInfo->iMinZoomFactor; + if (minZoomValue == 0.0 || minZoomValue == 1.0) + return 1.0; // Macro Zoom is not supported + else { + return minZoomValue; + } + + } else { + return 1.0; + } +} + +qreal S60ImageCaptureSession::maxDigitalZoom() +{ + qreal maxDigitalZoomFactor = 1.0; + + if (queryCurrentCameraInfo()) { + maxDigitalZoomFactor = m_cameraInfo->iMaxDigitalZoomFactor; + return maxDigitalZoomFactor; + } else { + return 1.0; + } +} + +void S60ImageCaptureSession::doSetZoomFactorL(qreal optical, qreal digital) +{ +#if !defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) & !defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + // Convert Zoom Factor to Zoom Value if AdvSettings are not available + int digitalSymbian = (digital * m_cameraInfo->iMaxDigitalZoom) / maxDigitalZoom(); + if (m_cameraInfo->iMaxDigitalZoom != 0 && digital == 1.0) + digitalSymbian = 1; // Make sure zooming out to initial value if requested +#endif // !USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER & !USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + + if (m_cameraEngine && !m_cameraEngine->IsCameraReady()) + return; + + if (m_cameraEngine && queryCurrentCameraInfo()) { + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + + // Optical Zoom + if (!qFuzzyCompare(optical, qreal(1.0)) && !qFuzzyCompare(optical, qreal(0.0))) { + setError(KErrNotSupported, tr("Requested optical zoom factor is not supported.")); + return; + } + + // Digital Zoom (Smooth Zoom - Zoom value set in steps) + if (digital != digitalZoomFactor()) { + if ((digital > 1.0 || qFuzzyCompare(digital, qreal(1.0))) && + digital <= m_cameraInfo->iMaxDigitalZoomFactor) { +#if defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) | defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + if (m_advancedSettings) { + qreal currentZoomFactor = m_advancedSettings->digitalZoomFactorL(); + + QList smoothZoomSetValues; + QList factors = m_advancedSettings->supportedDigitalZoomFactors(); + if (currentZoomFactor < digital) { + for (int i = 0; i < factors.count(); ++i) { + if (factors.at(i) > currentZoomFactor && factors.at(i) < digital) + smoothZoomSetValues << factors.at(i); + } + + for (int i = 0; i < smoothZoomSetValues.count(); i = i + KSmoothZoomStep) { + m_advancedSettings->setDigitalZoomFactorL(smoothZoomSetValues[i]); // Using Zoom Factor + } + + } else { + for (int i = 0; i < factors.count(); ++i) { + if (factors.at(i) < currentZoomFactor && factors.at(i) > digital) + smoothZoomSetValues << factors.at(i); + } + + for (int i = (smoothZoomSetValues.count() - 1); i >= 0; i = i - KSmoothZoomStep) { + m_advancedSettings->setDigitalZoomFactorL(smoothZoomSetValues[i]); // Using Zoom Factor + } + } + + // Set final value + m_advancedSettings->setDigitalZoomFactorL(digital); + } + else + setError(KErrNotReady, tr("Zooming failed.")); +#else // No advanced settigns + // Define zoom steps + int currentZoomFactor = camera->DigitalZoomFactor(); + int difference = abs(currentZoomFactor - digitalSymbian); + int midZoomValue = currentZoomFactor; + + if (currentZoomFactor < digitalSymbian) { + while (midZoomValue < (digitalSymbian - KSmoothZoomStep)) { + midZoomValue = midZoomValue + KSmoothZoomStep; + camera->SetDigitalZoomFactorL(midZoomValue); + } + } else { + while (midZoomValue > (digitalSymbian + KSmoothZoomStep)) { + midZoomValue = midZoomValue - KSmoothZoomStep; + camera->SetDigitalZoomFactorL(midZoomValue); + } + } + + // Set final and emit signal + camera->SetDigitalZoomFactorL(digitalSymbian); +#endif // USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER | USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + } else { + setError(KErrNotSupported, tr("Requested digital zoom factor is not supported.")); + return; + } + } + } + } else { + setError(KErrGeneral, tr("Unexpected camera error.")); + } +} + +qreal S60ImageCaptureSession::opticalZoomFactor() +{ + qreal factor = 1.0; + +#if defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) | defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + if (m_advancedSettings) { + TRAPD(err, factor = m_advancedSettings->opticalZoomFactorL()); + if (err) + return 1.0; + } +#else // No advanced settigns + if (m_cameraEngine && m_cameraInfo) { + if (m_cameraEngine->Camera()) { + if (m_cameraInfo->iMaxZoom != 0) + factor = (m_cameraEngine->Camera()->ZoomFactor()* maximumZoom()) / m_cameraInfo->iMaxZoom; + else + factor = 1.0; + } + } +#endif // USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER | USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + + if (factor == 0.0) // If not supported + factor = 1.0; + + return factor; +} + +qreal S60ImageCaptureSession::digitalZoomFactor() +{ + qreal factor = 1.0; + +#if defined(USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER) | defined(USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER) + if (m_advancedSettings) { + TRAPD(err, factor = m_advancedSettings->digitalZoomFactorL()); + if (err) + return 1.0; + } +#else // No advanced settigns + if (m_cameraEngine && m_cameraInfo) { + if (m_cameraEngine->Camera()) { + if (m_cameraInfo->iMaxDigitalZoom != 0) + factor = (m_cameraEngine->Camera()->DigitalZoomFactor()* maxDigitalZoom()) / m_cameraInfo->iMaxDigitalZoom; + else + factor = 1.0; + } + } +#endif // USE_S60_32_ECAM_ADVANCED_SETTINGS_HEADER | USE_S60_50_ECAM_ADVANCED_SETTINGS_HEADER + + if (factor == 0.0) + factor = 1.0; + + return factor; +} + +void S60ImageCaptureSession::setFlashMode(QCameraExposure::FlashModes mode) +{ + TRAPD(err, doSetFlashModeL(mode)); + setError(err, tr("Failed to set flash mode.")); +} + +void S60ImageCaptureSession::doSetFlashModeL(QCameraExposure::FlashModes mode) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera *camera = m_cameraEngine->Camera(); + switch(mode) { + case QCameraExposure::FlashOff: + camera->SetFlashL(CCamera::EFlashNone); + break; + case QCameraExposure::FlashAuto: + camera->SetFlashL(CCamera::EFlashAuto); + break; + case QCameraExposure::FlashOn: + camera->SetFlashL(CCamera::EFlashForced); + break; + case QCameraExposure::FlashRedEyeReduction: + camera->SetFlashL(CCamera::EFlashRedEyeReduce); + break; + case QCameraExposure::FlashFill: + camera->SetFlashL(CCamera::EFlashFillIn); + break; + + default: + setError(KErrNotSupported, tr("Requested flash mode is not suported")); + break; + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +QCameraExposure::FlashMode S60ImageCaptureSession::flashMode() +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera *camera = m_cameraEngine->Camera(); + switch(camera->Flash()) { + case CCamera::EFlashAuto: + return QCameraExposure::FlashAuto; + case CCamera::EFlashForced: + return QCameraExposure::FlashOn; + case CCamera::EFlashRedEyeReduce: + return QCameraExposure::FlashRedEyeReduction; + case CCamera::EFlashFillIn: + return QCameraExposure::FlashFill; + case CCamera::EFlashNone: + return QCameraExposure::FlashOff; + + default: + return QCameraExposure::FlashAuto; // Most probable default + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + + return QCameraExposure::FlashOff; +} + +QCameraExposure::FlashModes S60ImageCaptureSession::supportedFlashModes() +{ + QCameraExposure::FlashModes modes = QCameraExposure::FlashOff; + + if (queryCurrentCameraInfo()) { + TInt supportedModes = m_cameraInfo->iFlashModesSupported; + + if (supportedModes == 0) + return modes; + + if (supportedModes & CCamera::EFlashManual) + modes |= QCameraExposure::FlashOff; + if (supportedModes & CCamera::EFlashForced) + modes |= QCameraExposure::FlashOn; + if (supportedModes & CCamera::EFlashAuto) + modes |= QCameraExposure::FlashAuto; + if (supportedModes & CCamera::EFlashFillIn) + modes |= QCameraExposure::FlashFill; + if (supportedModes & CCamera::EFlashRedEyeReduce) + modes |= QCameraExposure::FlashRedEyeReduction; + } + + return modes; +} + +QCameraExposure::ExposureMode S60ImageCaptureSession::exposureMode() +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera* camera = m_cameraEngine->Camera(); + switch(camera->Exposure()) { + case CCamera::EExposureManual: + return QCameraExposure::ExposureManual; + case CCamera::EExposureAuto: + return QCameraExposure::ExposureAuto; + case CCamera::EExposureNight: + return QCameraExposure::ExposureNight; + case CCamera::EExposureBacklight: + return QCameraExposure::ExposureBacklight; + case CCamera::EExposureSport: + return QCameraExposure::ExposureSports; + case CCamera::EExposureSnow: + return QCameraExposure::ExposureSnow; + case CCamera::EExposureBeach: + return QCameraExposure::ExposureBeach; + + default: + return QCameraExposure::ExposureAuto; + } + } + + return QCameraExposure::ExposureAuto; +} + +bool S60ImageCaptureSession::isExposureModeSupported(QCameraExposure::ExposureMode mode) const +{ + TInt supportedModes = m_cameraInfo->iExposureModesSupported; + + if (supportedModes == 0) + return false; + + switch (mode) { + case QCameraExposure::ExposureManual: + if(supportedModes & CCamera::EExposureManual) + return true; + else + return false; + case QCameraExposure::ExposureAuto: + return true; // Always supported + case QCameraExposure::ExposureNight: + if(supportedModes & CCamera::EExposureNight) + return true; + else + return false; + case QCameraExposure::ExposureBacklight: + if(supportedModes & CCamera::EExposureBacklight) + return true; + else + return false; + case QCameraExposure::ExposureSports: + if(supportedModes & CCamera::EExposureSport) + return true; + else + return false; + case QCameraExposure::ExposureSnow: + if(supportedModes & CCamera::EExposureSnow) + return true; + else + return false; + case QCameraExposure::ExposureBeach: + if(supportedModes & CCamera::EExposureBeach) + return true; + else + return false; + + default: + return false; + } +} + +void S60ImageCaptureSession::setExposureMode(QCameraExposure::ExposureMode mode) +{ + TRAPD(err, doSetExposureModeL(mode)); + setError(err, tr("Failed to set exposure mode.")); +} + +void S60ImageCaptureSession::doSetExposureModeL( QCameraExposure::ExposureMode mode) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera *camera = m_cameraEngine->Camera(); + switch(mode) { + case QCameraExposure::ExposureManual: + camera->SetExposureL(CCamera::EExposureManual); + break; + case QCameraExposure::ExposureAuto: + camera->SetExposureL(CCamera::EExposureAuto); + break; + case QCameraExposure::ExposureNight: + camera->SetExposureL(CCamera::EExposureNight); + break; + case QCameraExposure::ExposureBacklight: + camera->SetExposureL(CCamera::EExposureBacklight); + break; + case QCameraExposure::ExposureSports: + camera->SetExposureL(CCamera::EExposureSport); + break; + case QCameraExposure::ExposureSnow: + camera->SetExposureL(CCamera::EExposureSnow); + break; + case QCameraExposure::ExposureBeach: + camera->SetExposureL(CCamera::EExposureBeach); + break; + case QCameraExposure::ExposureLargeAperture: + case QCameraExposure::ExposureSmallAperture: + break; + case QCameraExposure::ExposurePortrait: + case QCameraExposure::ExposureSpotlight: + default: + setError(KErrNotSupported, tr("Requested exposure mode is not suported")); + break; + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +int S60ImageCaptureSession::contrast() const +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + return m_cameraEngine->Camera()->Contrast(); + } else { + return 0; + } +} + +void S60ImageCaptureSession::setContrast(int value) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + TRAPD(err, m_cameraEngine->Camera()->SetContrastL(value)); + setError(err, tr("Failed to set contrast.")); + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +int S60ImageCaptureSession::brightness() const +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + return m_cameraEngine->Camera()->Brightness(); + } else { + return 0; + } +} + +void S60ImageCaptureSession::setBrightness(int value) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + TRAPD(err, m_cameraEngine->Camera()->SetBrightnessL(value)); + setError(err, tr("Failed to set brightness.")); + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + + QCameraImageProcessing::WhiteBalanceMode S60ImageCaptureSession::whiteBalanceMode() +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera::TWhiteBalance mode = m_cameraEngine->Camera()->WhiteBalance(); + switch(mode) { + case CCamera::EWBAuto: + return QCameraImageProcessing::WhiteBalanceAuto; + case CCamera::EWBDaylight: + return QCameraImageProcessing::WhiteBalanceSunlight; + case CCamera::EWBCloudy: + return QCameraImageProcessing::WhiteBalanceCloudy; + case CCamera::EWBTungsten: + return QCameraImageProcessing::WhiteBalanceTungsten; + case CCamera::EWBFluorescent: + return QCameraImageProcessing::WhiteBalanceFluorescent; + case CCamera::EWBFlash: + return QCameraImageProcessing::WhiteBalanceFlash; + case CCamera::EWBBeach: + return QCameraImageProcessing::WhiteBalanceSunset; + case CCamera::EWBManual: + return QCameraImageProcessing::WhiteBalanceManual; + case CCamera::EWBShade: + return QCameraImageProcessing::WhiteBalanceShade; + + default: + return QCameraImageProcessing::WhiteBalanceAuto; + } + } + + return QCameraImageProcessing::WhiteBalanceAuto; +} + +void S60ImageCaptureSession::setWhiteBalanceMode( QCameraImageProcessing::WhiteBalanceMode mode) +{ + TRAPD(err, doSetWhiteBalanceModeL(mode)); + setError(err, tr("Failed to set white balance mode.")); +} + +void S60ImageCaptureSession::doSetWhiteBalanceModeL( QCameraImageProcessing::WhiteBalanceMode mode) +{ + if (m_cameraEngine && m_cameraEngine->Camera()) { + CCamera* camera = m_cameraEngine->Camera(); + switch(mode) { + case QCameraImageProcessing::WhiteBalanceAuto: + camera->SetWhiteBalanceL(CCamera::EWBAuto); + break; + case QCameraImageProcessing::WhiteBalanceSunlight: + camera->SetWhiteBalanceL(CCamera::EWBDaylight); + break; + case QCameraImageProcessing::WhiteBalanceCloudy: + camera->SetWhiteBalanceL(CCamera::EWBCloudy); + break; + case QCameraImageProcessing::WhiteBalanceTungsten: + camera->SetWhiteBalanceL(CCamera::EWBTungsten); + break; + case QCameraImageProcessing::WhiteBalanceFluorescent: + camera->SetWhiteBalanceL(CCamera::EWBFluorescent); + break; + case QCameraImageProcessing::WhiteBalanceFlash: + camera->SetWhiteBalanceL(CCamera::EWBFlash); + break; + case QCameraImageProcessing::WhiteBalanceSunset: + camera->SetWhiteBalanceL(CCamera::EWBBeach); + break; + case QCameraImageProcessing::WhiteBalanceManual: + camera->SetWhiteBalanceL(CCamera::EWBManual); + break; + case QCameraImageProcessing::WhiteBalanceShade: + camera->SetWhiteBalanceL(CCamera::EWBShade); + break; + + default: + setError(KErrNotSupported, tr("Requested white balance mode is not suported")); + break; + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +bool S60ImageCaptureSession::isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const +{ + if (m_cameraEngine) { + TInt supportedModes = m_cameraInfo->iWhiteBalanceModesSupported; + switch (mode) { + case QCameraImageProcessing::WhiteBalanceManual: + if (supportedModes & CCamera::EWBManual) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceAuto: + if (supportedModes & CCamera::EWBAuto) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceSunlight: + if (supportedModes & CCamera::EWBDaylight) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceCloudy: + if (supportedModes & CCamera::EWBCloudy) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceShade: + if (supportedModes & CCamera::EWBShade) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceTungsten: + if (supportedModes & CCamera::EWBTungsten) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceFluorescent: + if (supportedModes & CCamera::EWBFluorescent) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceIncandescent: // Not available in Symbian + return false; + case QCameraImageProcessing::WhiteBalanceFlash: + if (supportedModes & CCamera::EWBFlash) + return true; + else + return false; + case QCameraImageProcessing::WhiteBalanceSunset: + if (supportedModes & CCamera::EWBBeach) + return true; + else + return false; + + default: + return false; + } + } + + return false; +} + +/* + * ==================== + * S60 3.1 AutoFocosing + * ==================== + */ +bool S60ImageCaptureSession::isFocusSupported() const +{ + return m_cameraEngine->IsAutoFocusSupported(); +} + +void S60ImageCaptureSession::startFocus() +{ + if (m_cameraEngine) { + TRAPD(err, m_cameraEngine->StartFocusL()); + setError(err, tr("Failed to start focusing.")); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +void S60ImageCaptureSession::cancelFocus() +{ + if (m_cameraEngine) { + TRAPD(err, m_cameraEngine->FocusCancel()); + setError(err, tr("Failed to cancel focusing.")); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +void S60ImageCaptureSession::handleImageDecoded(int error) +{ + // Delete unneeded objects + if (m_imageDecoder) { + delete m_imageDecoder; + m_imageDecoder = 0; + } + if (m_fileSystemAccess) { + m_fileSystemAccess->Close(); + delete m_fileSystemAccess; + m_fileSystemAccess = 0; + } + + // Check status of decoding + if (error != KErrNone) { + if (m_previewBitmap) { + m_previewBitmap->Reset(); + delete m_previewBitmap; + m_previewBitmap = 0; + } + releaseImageBuffer(); + if (m_previewInWaitLoop) { + CActiveScheduler::Stop(); // Notify to continue execution of next Preview Image generation + m_previewInWaitLoop = false; // Reset + } + setError(error, tr("Preview creation failed.")); + return; + } + + m_previewDecodingOngoing = false; + + QPixmap prevPixmap = QPixmap::fromSymbianCFbsBitmap(m_previewBitmap); + QImage preview = prevPixmap.toImage(); + + if (m_previewBitmap) { + m_previewBitmap->Reset(); + delete m_previewBitmap; + m_previewBitmap = 0; + } + + QT_TRYCATCH_LEAVING( emit imageCaptured(m_currentImageId, preview) ); + + // Release image resources (if not already done) + releaseImageBuffer(); + + if (m_previewInWaitLoop) { + CActiveScheduler::Stop(); // Notify to continue execution of next Preview Image generation + m_previewInWaitLoop = false; // Reset + } +} + +void S60ImageCaptureSession::handleImageEncoded(int error) +{ + // Check status of encoding + if (error != KErrNone) { + releaseImageBuffer(); + if (m_previewInWaitLoop) { + CActiveScheduler::Stop(); // Notify to continue execution of next Preview Image generation + m_previewInWaitLoop = false; // Reset + } + setError(error, tr("Saving captured image to file failed.")); + return; + } else { + QT_TRYCATCH_LEAVING( emit imageSaved(m_currentImageId, m_stillCaptureFileName) ); + } + + if (m_imageEncoder) { + delete m_imageEncoder; + m_imageEncoder = 0; + } + +#ifndef ECAM_PREVIEW_API + // Start preview generation + TInt previewError = KErrNone; + TFileName fileName = convertImagePath(); + TRAP(previewError, m_imageDecoder = S60ImageCaptureDecoder::FileNewL(this, m_fileSystemAccess, &fileName)); + if (previewError) { + setError(previewError, tr("Failed to create preview image.")); + return; + } + + // Set proper Preview Size + TSize scaledSize((m_captureSize.width() / KSnapshotDownScaleFactor), (m_captureSize.height() / KSnapshotDownScaleFactor)); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/2)), (m_captureSize.height() / (KSnapshotDownScaleFactor/2))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize((m_captureSize.width() / (KSnapshotDownScaleFactor/4)), (m_captureSize.height() / (KSnapshotDownScaleFactor/4))); + if (scaledSize.iWidth < KSnapshotMinWidth || scaledSize.iHeight < KSnapshotMinHeight) + scaledSize.SetSize(m_captureSize.width(), m_captureSize.height()); + + TFrameInfo *info = m_imageDecoder->frameInfo(); + if (!info) { + setError(KErrGeneral, tr("Preview image creation failed.")); + return; + } + + m_previewBitmap = new CFbsBitmap; + if (!m_previewBitmap) { + setError(KErrNoMemory, tr("Failed to create preview image.")); + return; + } + previewError = m_previewBitmap->Create(scaledSize, info->iFrameDisplayMode); + if (previewError) { + setError(previewError, tr("Preview creation failed.")); + return; + } + + // Jpeg decoding completes in handleImageDecoded() + m_imageDecoder->decode(m_previewBitmap); +#endif // ECAM_PREVIEW_API + + // Buffer can be released since Preview is created from file + releaseImageBuffer(); + + // Inform that we can continue taking more pictures + QT_TRYCATCH_LEAVING( emit readyForCaptureChanged(true) ); +} + +#ifdef ECAM_PREVIEW_API +void S60ImageCaptureSession::MceoPreviewReady(CFbsBitmap& aPreview) +{ + QPixmap previewPixmap = QPixmap::fromSymbianCFbsBitmap(&aPreview); + QImage preview = previewPixmap.toImage(); + + // Notify preview availability + emit imageCaptured(m_currentImageId, preview); +} +#endif // ECAM_PREVIEW_API + +// End of file + diff --git a/src/plugins/symbian/ecam/s60imagecapturesession.h b/src/plugins/symbian/ecam/s60imagecapturesession.h new file mode 100644 index 000000000..9cae05fc9 --- /dev/null +++ b/src/plugins/symbian/ecam/s60imagecapturesession.h @@ -0,0 +1,359 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60IMAGECAPTURESESSION_H +#define S60IMAGECAPTURESESSION_H + +#include +#include +#include +#include + +#include +#include +#include +#include + +#include "s60camerasettings.h" +#include "s60cameraengine.h" +#include "s60cameraengineobserver.h" +#include "s60cameraconstants.h" // Default Jpeg Quality + +#include // TFrameInfo + +QT_USE_NAMESPACE + +class S60CameraService; +class CImageDecoder; +class CImageEncoder; +class CFrameImageData; +class RFs; +class S60ImageCaptureSession; + +/* + * This class implements asynchronous image decoding service for the + * S60ImageCaptureSession. + */ +class S60ImageCaptureDecoder : public CActive +{ +public: // Static Contructor & Destructor + + static S60ImageCaptureDecoder* FileNewL(S60ImageCaptureSession *imageSession = 0, + RFs *fileSystemAccess = 0, + const TDesC16 *fileName = 0); + static S60ImageCaptureDecoder* DataNewL(S60ImageCaptureSession *imageSession = 0, + RFs *fileSystemAccess = 0, + const TDesC8 *data = 0); + ~S60ImageCaptureDecoder(); + +public: // Operations + + void decode(CFbsBitmap *destBitmap); + TFrameInfo *frameInfo(); + +protected: // CActive + + void RunL(); + void DoCancel(); + TInt RunError(TInt aError); + +protected: // Protected constructors + + S60ImageCaptureDecoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC8 *data, + const TDesC16 *fileName); + void ConstructL(const bool fileInput = false); + +private: // Data + + S60ImageCaptureSession *m_imageSession; + CImageDecoder *m_imageDecoder; + RFs *m_fs; + const TDesC8 *m_jpegImageData; + const TDesC16 *m_jpegImageFile; + bool m_fileInput; + TFrameInfo m_frameInfo; + +}; + +//============================================================================= + +/* + * This class implements asynchronous image encoding service for the + * S60ImageCaptureSession. + */ +class S60ImageCaptureEncoder : public CActive +{ +public: // Static Contructor & Destructor + + static S60ImageCaptureEncoder* NewL(S60ImageCaptureSession *imageSession = 0, + RFs *fileSystemAccess = 0, + const TDesC16 *fileName = 0, + TInt jpegQuality = KDefaultImageQuality); + ~S60ImageCaptureEncoder(); + +public: // Operations + + void encode(CFbsBitmap *sourceBitmap); + +protected: // CActive + + void RunL(); + void DoCancel(); + TInt RunError(TInt aError); + +protected: // Protected constructors + + S60ImageCaptureEncoder(S60ImageCaptureSession *imageSession, + RFs *fileSystemAccess, + const TDesC16 *fileName, + TInt jpegQuality); + void ConstructL(); + +private: // Data + + S60ImageCaptureSession *m_imageSession; + CImageEncoder *m_imageEncoder; + RFs *m_fileSystemAccess; + const TDesC16 *m_fileName; + CFrameImageData *m_frameImageData; + TInt m_jpegQuality; + +}; + +//============================================================================= + +/* + * Session handling all image capture activities. + */ +class S60ImageCaptureSession : public QObject, +#ifdef ECAM_PREVIEW_API + public MCameraPreviewObserver, +#endif // ECAM_PREVIEW_API + public MCameraEngineImageCaptureObserver +{ + Q_OBJECT + +public: // Enums + + enum ImageCaptureState { + EImageCaptureNotPrepared = 0, // 0 - ImageCapture has not been prepared + EImageCapturePrepared, // 1 - ImageCapture has been prepared + EImageCaptureCapturing, // 2 - Image capture ongoing + EImageCaptureWritingImage // 3 - Image captured and image writing to file ongoing + }; + +public: // Constructor & Destructor + + S60ImageCaptureSession(QObject *parent = 0); + ~S60ImageCaptureSession(); + +public: // Methods + + void setError(const TInt error, const QString &description, const bool captureError = false); + int currentImageId() const; + + bool isDeviceReady(); + void setCameraHandle(CCameraEngine* camerahandle); + void setCurrentDevice(TInt deviceindex); + void notifySettingsSet(); + + // Ecam Advanced Settings + S60CameraSettings* advancedSettings(); + void deleteAdvancedSettings(); + + // Controls + int prepareImageCapture(); + void releaseImageCapture(); + int capture(const QString &fileName); + void cancelCapture(); + void releaseImageBuffer(); + + // Image Resolution + QSize captureSize() const; + QSize minimumCaptureSize(); + QSize maximumCaptureSize(); + QList supportedCaptureSizesForCodec(const QString &codecName); + void setCaptureSize(const QSize &size); + + // Image Codec + QStringList supportedImageCaptureCodecs(); + QString imageCaptureCodec(); + void setImageCaptureCodec(const QString &codecName); + QString imageCaptureCodecDescription(const QString &codecName); + + // Image Quality + QtMultimediaKit::EncodingQuality captureQuality() const; + void setCaptureQuality(const QtMultimediaKit::EncodingQuality &quality); + + // S60 3.1 Focus Control (S60 3.2 and later via S60CameraSettings class) + bool isFocusSupported() const; + void startFocus(); + void cancelFocus(); + + // Zoom Control + qreal maximumZoom(); + qreal minZoom(); + qreal maxDigitalZoom(); + void doSetZoomFactorL(qreal optical, qreal digital); + qreal opticalZoomFactor(); + qreal digitalZoomFactor(); + + // Exposure Mode Control + QCameraExposure::ExposureMode exposureMode(); + void setExposureMode(QCameraExposure::ExposureMode mode); + bool isExposureModeSupported(QCameraExposure::ExposureMode mode) const; + + // Flash Mode Control + QCameraExposure::FlashMode flashMode(); + void setFlashMode(QCameraExposure::FlashModes mode); + QCameraExposure::FlashModes supportedFlashModes(); + + // Contrast Control + int contrast() const; + void setContrast(int value); + + // Brightness Control + int brightness() const; + void setBrightness(int value); + + // White Balance Mode Control + QCameraImageProcessing::WhiteBalanceMode whiteBalanceMode(); + void setWhiteBalanceMode(QCameraImageProcessing::WhiteBalanceMode mode); + bool isWhiteBalanceModeSupported(QCameraImageProcessing::WhiteBalanceMode mode) const; + +public: // Image Decoding & Encoding Notifications + + void handleImageDecoded(int error); + void handleImageEncoded(int error); + +protected: // MCameraEngineObserver + + void MceoFocusComplete(); + void MceoCapturedDataReady(TDesC8* aData); + void MceoCapturedBitmapReady(CFbsBitmap* aBitmap); + void MceoHandleError(TCameraEngineError aErrorType, TInt aError); + +#ifdef ECAM_PREVIEW_API +protected: // MCameraPreviewObserver + + void MceoPreviewReady(CFbsBitmap& aPreview); +#endif // ECAM_PREVIEW_API + +private: // Internal + + QCameraImageCapture::Error fromSymbianErrorToQtMultimediaError(int aError); + + void initializeImageCaptureSettings(); + void resetSession(bool errorHandling = false); + + CCamera::TFormat selectFormatForCodec(const QString &codec); + CCamera::TFormat defaultImageFormat(); + bool queryCurrentCameraInfo(); + QMap formatMap(); + QMap codecDescriptionMap(); + void updateImageCaptureFormats(); + + void doSetWhiteBalanceModeL(QCameraImageProcessing::WhiteBalanceMode mode); + + void doSetFlashModeL(QCameraExposure::FlashModes mode); + void doSetExposureModeL(QCameraExposure::ExposureMode mode); + + void saveImageL(TDesC8 *aData, TFileName &aPath); + void processFileName(const QString &fileName); + TFileName convertImagePath(); + +signals: // Notifications + + void stateChanged(QCamera::State); + void advancedSettingChanged(); + void captureSizeChanged(const QSize&); + + // Error signals + void cameraError(int, const QString&); // For QCamera::error + void captureError(int, int, const QString&); // For QCameraImageCapture::error + + // Capture notifications + void readyForCaptureChanged(bool); + void imageExposed(int); + void imageCaptured(const int, const QImage&); + void imageSaved(const int, const QString&); + + // Focus notifications + void focusStatusChanged(QCamera::LockStatus, QCamera::LockChangeReason); + +private slots: // Internal Slots + + void cameraStatusChanged(QCamera::Status); + +private: // Data + + CCameraEngine *m_cameraEngine; + S60CameraSettings *m_advancedSettings; + mutable TCameraInfo *m_cameraInfo; + CFbsBitmap *m_previewBitmap; + CActiveScheduler *m_activeScheduler; + RFs *m_fileSystemAccess; + S60ImageCaptureDecoder *m_imageDecoder; + S60ImageCaptureEncoder *m_imageEncoder; + mutable int m_error; // Symbian ErrorCode + TInt m_activeDeviceIndex; + bool m_cameraStarted; + ImageCaptureState m_icState; + QStringList m_supportedImageCodecs; + QString m_currentCodec; + CCamera::TFormat m_currentFormat; + QSize m_captureSize; + int m_symbianImageQuality; + bool m_captureSettingsSet; + QString m_stillCaptureFileName; + QString m_requestedStillCaptureFileName; + mutable int m_currentImageId; + QList m_formats; + // This indicates that image capture should be triggered right after + // camera and image setting initialization has completed + bool m_captureWhenReady; + bool m_previewDecodingOngoing; + bool m_previewInWaitLoop; +}; + +#endif // S60IMAGECAPTURESESSION_H diff --git a/src/plugins/symbian/ecam/s60imageencodercontrol.cpp b/src/plugins/symbian/ecam/s60imageencodercontrol.cpp new file mode 100644 index 000000000..bfbf8d36b --- /dev/null +++ b/src/plugins/symbian/ecam/s60imageencodercontrol.cpp @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60imageencodercontrol.h" +#include "s60imagecapturesession.h" + +S60ImageEncoderControl::S60ImageEncoderControl(QObject *parent) : + QImageEncoderControl(parent) +{ +} + +S60ImageEncoderControl::S60ImageEncoderControl(S60ImageCaptureSession *session, QObject *parent) : + QImageEncoderControl(parent) +{ + m_session = session; +} + +S60ImageEncoderControl::~S60ImageEncoderControl() +{ +} + +QList S60ImageEncoderControl::supportedResolutions( + const QImageEncoderSettings &settings, bool *continuous) const +{ + QList resolutions = m_session->supportedCaptureSizesForCodec(settings.codec()); + + // Discrete resolutions are returned + if (continuous) + *continuous = false; + + return resolutions; +} +QStringList S60ImageEncoderControl::supportedImageCodecs() const +{ + return m_session->supportedImageCaptureCodecs(); +} + +QString S60ImageEncoderControl::imageCodecDescription(const QString &codec) const +{ + return m_session->imageCaptureCodecDescription(codec); +} + +QImageEncoderSettings S60ImageEncoderControl::imageSettings() const +{ + // Update setting values from session + QImageEncoderSettings settings; + settings.setCodec(m_session->imageCaptureCodec()); + settings.setResolution(m_session->captureSize()); + settings.setQuality(m_session->captureQuality()); + + return settings; +} +void S60ImageEncoderControl::setImageSettings(const QImageEncoderSettings &settings) +{ + // Notify that settings have been implicitly set and there's no need to + // initialize them in case camera is changed + m_session->notifySettingsSet(); + + if (!settings.isNull()) { + if (!settings.codec().isEmpty()) { + if (settings.resolution() != QSize(-1,-1)) { // Codec, Resolution & Quality + m_session->setImageCaptureCodec(settings.codec()); + m_session->setCaptureSize(settings.resolution()); + m_session->setCaptureQuality(settings.quality()); + } else { // Codec and Quality + m_session->setImageCaptureCodec(settings.codec()); + m_session->setCaptureQuality(settings.quality()); + } + } else { + if (settings.resolution() != QSize(-1,-1)) { // Resolution & Quality + m_session->setCaptureSize(settings.resolution()); + m_session->setCaptureQuality(settings.quality()); + } + else // Only Quality + m_session->setCaptureQuality(settings.quality()); + } + + // Prepare ImageCapture with the settings and set error if needed + int prepareSuccess = m_session->prepareImageCapture(); + + // Preparation fails with KErrNotReady if camera has not been started. + // That can be ignored since settings are set internally in that case. + if (prepareSuccess != KErrNotReady && prepareSuccess != KErrNone) + m_session->setError(prepareSuccess, tr("Failure in preparation of image capture.")); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60imageencodercontrol.h b/src/plugins/symbian/ecam/s60imageencodercontrol.h new file mode 100644 index 000000000..99a308286 --- /dev/null +++ b/src/plugins/symbian/ecam/s60imageencodercontrol.h @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60IMAGEENCODERCONTROL_H +#define S60IMAGEENCODERCONTROL_H + +#include +#include "qimageencodercontrol.h" + +QT_USE_NAMESPACE + +class S60ImageCaptureSession; + +/* + * Control for setting encoding settings for the captured image. + */ +class S60ImageEncoderControl : public QImageEncoderControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60ImageEncoderControl(QObject *parent = 0); + S60ImageEncoderControl(S60ImageCaptureSession *session, QObject *parent = 0); + ~S60ImageEncoderControl(); + +public: // QImageEncoderControl + + // Codec + QStringList supportedImageCodecs() const; + QString imageCodecDescription(const QString &codec) const; + + // Resolution + QList supportedResolutions(const QImageEncoderSettings &settings, + bool *continuous = 0) const; + + // Settings + QImageEncoderSettings imageSettings() const; + void setImageSettings(const QImageEncoderSettings &settings); + +private: // Data + + S60ImageCaptureSession *m_session; +}; + +#endif // S60IMAGEENCODERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60mediacontainercontrol.cpp b/src/plugins/symbian/ecam/s60mediacontainercontrol.cpp new file mode 100644 index 000000000..f0d20f4e3 --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediacontainercontrol.cpp @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60mediacontainercontrol.h" +#include "s60videocapturesession.h" +#include "s60cameraconstants.h" + +S60MediaContainerControl::S60MediaContainerControl(QObject *parent): + QMediaContainerControl(parent) +{ +} + +S60MediaContainerControl::S60MediaContainerControl(S60VideoCaptureSession *session, QObject *parent): + QMediaContainerControl(parent) +{ + m_session = session; + + // Set default video container + m_supportedContainers = m_session->supportedVideoContainers(); + + if (!m_supportedContainers.isEmpty()) { + // Check if default container is supported + if (m_supportedContainers.indexOf(KMimeTypeDefaultContainer) != -1) + setContainerMimeType(KMimeTypeDefaultContainer); + // Otherwise use first in the list + else + setContainerMimeType(m_supportedContainers[0]); // First as default + } else { + m_session->setError(KErrGeneral, tr("No supported video containers found.")); + } +} + +S60MediaContainerControl::~S60MediaContainerControl() +{ + m_supportedContainers.clear(); + m_containerDescriptions.clear(); +} + +QStringList S60MediaContainerControl::supportedContainers() const +{ + return m_session->supportedVideoContainers(); +} + +QString S60MediaContainerControl::containerMimeType() const +{ + return m_session->videoContainer(); +} + +void S60MediaContainerControl::setContainerMimeType(const QString &containerMimeType) +{ + m_session->setVideoContainer(containerMimeType); +} + +QString S60MediaContainerControl::containerDescription(const QString &containerMimeType) const +{ + return m_session->videoContainerDescription(containerMimeType); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60mediacontainercontrol.h b/src/plugins/symbian/ecam/s60mediacontainercontrol.h new file mode 100644 index 000000000..057b4bc43 --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediacontainercontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIACONTAINERCONTROL_H +#define S60MEDIACONTAINERCONTROL_H + +#include +#include +#include + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; + +/* + * Control for setting container (file format) for video recorded using + * QMediaRecorder. + */ +class S60MediaContainerControl : public QMediaContainerControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60MediaContainerControl(QObject *parent = 0); + S60MediaContainerControl(S60VideoCaptureSession *session, QObject *parent = 0); + virtual ~S60MediaContainerControl(); + +public: // QMediaContainerControl + + QStringList supportedContainers() const; + QString containerMimeType() const; + void setContainerMimeType(const QString &containerMimeType); + + QString containerDescription(const QString &containerMimeType) const; + +private: // Data + + S60VideoCaptureSession *m_session; + QStringList m_supportedContainers; + QMap m_containerDescriptions; +}; + +#endif // S60MEDIACONTAINERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60mediarecordercontrol.cpp b/src/plugins/symbian/ecam/s60mediarecordercontrol.cpp new file mode 100644 index 000000000..2a1394fa5 --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediarecordercontrol.cpp @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60cameraservice.h" +#include "s60mediarecordercontrol.h" +#include "s60cameracontrol.h" +#include "s60videocapturesession.h" + +S60MediaRecorderControl::S60MediaRecorderControl(QObject *parent) : + QMediaRecorderControl(parent) +{ +} + +S60MediaRecorderControl::S60MediaRecorderControl(S60CameraService *service, + S60VideoCaptureSession *session, + QObject *parent): + QMediaRecorderControl(parent), + m_state(QMediaRecorder::StoppedState) // Default RecorderState +{ + m_session = session; + m_service = service; + m_cameraControl = qobject_cast(m_service->requestControl(QCameraControl_iid)); + + // Connect signals + connect(m_session, SIGNAL(stateChanged(S60VideoCaptureSession::TVideoCaptureState)), + this, SLOT(updateState(S60VideoCaptureSession::TVideoCaptureState))); + connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); + connect(m_session, SIGNAL(error(int,const QString &)), this, SIGNAL(error(int,const QString &))); +} + +S60MediaRecorderControl::~S60MediaRecorderControl() +{ + // Release requested control + if (m_cameraControl) + m_service->releaseControl(m_cameraControl); +} + +QUrl S60MediaRecorderControl::outputLocation() const +{ + return m_session->outputLocation(); +} + +bool S60MediaRecorderControl::setOutputLocation(const QUrl& sink) +{ + // Output location can only be set in StoppedState + if (m_state == QMediaRecorder::StoppedState) + return m_session->setOutputLocation(sink); + + // Do not signal error, but notify that setting was not effective + return false; +} + +QMediaRecorder::State S60MediaRecorderControl::convertInternalStateToQtState(S60VideoCaptureSession::TVideoCaptureState aState) const +{ + QMediaRecorder::State state; + + switch (aState) { + case S60VideoCaptureSession::ERecording: + state = QMediaRecorder::RecordingState; + break; + case S60VideoCaptureSession::EPaused: + state = QMediaRecorder::PausedState; + break; + + default: + // All others + state = QMediaRecorder::StoppedState; + break; + } + + return state; +} + +void S60MediaRecorderControl::updateState(S60VideoCaptureSession::TVideoCaptureState state) +{ + QMediaRecorder::State newState = convertInternalStateToQtState(state); + + if (m_state != newState) { + m_state = newState; + emit stateChanged(m_state); + } +} + +QMediaRecorder::State S60MediaRecorderControl::state() const +{ + return m_state; +} + +qint64 S60MediaRecorderControl::duration() const +{ + return m_session->position(); +} + +/* +This method is called after encoder configuration is done. +Encoder can load necessary resources at this point, +to reduce delay before recording is started. Calling this method reduces the +latency when calling record() to start video recording. +*/ +void S60MediaRecorderControl::applySettings() +{ + m_session->applyAllSettings(); +} + +void S60MediaRecorderControl::record() +{ + if (m_state == QMediaRecorder::RecordingState) + return; + + if (m_cameraControl && m_cameraControl->captureMode() != QCamera::CaptureVideo) { + emit error(QCamera::CameraError, tr("Video capture mode is not selected.")); + return; + } + + m_session->startRecording(); +} + +void S60MediaRecorderControl::pause() +{ + if (m_state != QMediaRecorder::RecordingState) { + // Discard + return; + } + + m_session->pauseRecording(); +} + +void S60MediaRecorderControl::stop() +{ + if (m_state == QMediaRecorder::StoppedState) { + // Ignore + return; + } + + m_session->stopRecording(); +} + +bool S60MediaRecorderControl::isMuted() const +{ + return m_session->isMuted(); +} + +void S60MediaRecorderControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60mediarecordercontrol.h b/src/plugins/symbian/ecam/s60mediarecordercontrol.h new file mode 100644 index 000000000..5db7477bd --- /dev/null +++ b/src/plugins/symbian/ecam/s60mediarecordercontrol.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIARECORDERCONTROL_H +#define S60MEDIARECORDERCONTROL_H + +#include +#include +#include + +#include "s60videocapturesession.h" + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; +class S60CameraService; +class S60CameraControl; + +/* + * Control for video recording operations. + */ +class S60MediaRecorderControl : public QMediaRecorderControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60MediaRecorderControl(QObject *parent = 0); + S60MediaRecorderControl(S60CameraService *service, + S60VideoCaptureSession *session, + QObject *parent = 0); + ~S60MediaRecorderControl(); + +public: // QMediaRecorderControl + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &sink); + + QMediaRecorder::State state() const; + + qint64 duration() const; + + bool isMuted() const; + + void applySettings(); + +/* +Q_SIGNALS: // QMediaRecorderControl + void stateChanged(QMediaRecorder::State state); + void durationChanged(qint64 position); + void mutedChanged(bool muted); + void error(int error, const QString &errorString); +*/ + +public slots: // QMediaRecorderControl + + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private: + + QMediaRecorder::State convertInternalStateToQtState( + S60VideoCaptureSession::TVideoCaptureState aState) const; + +private slots: + + void updateState(S60VideoCaptureSession::TVideoCaptureState state); + +private: // Data + + S60VideoCaptureSession *m_session; + S60CameraService *m_service; + S60CameraControl *m_cameraControl; + QMediaRecorder::State m_state; + +}; + +#endif // S60MEDIARECORDERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60videocapturesession.cpp b/src/plugins/symbian/ecam/s60videocapturesession.cpp new file mode 100644 index 000000000..7158f8696 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videocapturesession.cpp @@ -0,0 +1,2995 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include + +#include "s60videocapturesession.h" +#include "s60cameraconstants.h" + +#include +#include + +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +#include +#endif + +S60VideoCaptureSession::S60VideoCaptureSession(QObject *parent) : + QObject(parent), + m_cameraEngine(0), + m_videoRecorder(0), + m_position(0), + m_error(KErrNone), + m_cameraStarted(false), + m_captureState(ENotInitialized), // Default state + m_sink(QUrl()), + m_requestedSink(QUrl()), + m_captureSettingsSet(false), + m_container(QString()), + m_requestedContainer(QString()), + m_muted(false), + m_maxClipSize(-1), + m_videoControllerMap(QHash >()), + m_videoParametersForEncoder(QList()), + m_openWhenReady(false), + m_prepareAfterOpenComplete(false), + m_startAfterPrepareComplete(false), + m_uncommittedSettings(false), + m_commitSettingsWhenReady(false) +{ +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED + // Populate info of supported codecs, and their resolution, etc. + TRAPD(err, doPopulateVideoCodecsDataL()); + setError(err, tr("Failed to gather video codec information.")); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + + initializeVideoCaptureSettings(); + + m_durationTimer = new QTimer; + m_durationTimer->setInterval(KDurationChangedInterval); + connect(m_durationTimer, SIGNAL(timeout()), this, SLOT(durationTimerTriggered())); +} + +S60VideoCaptureSession::~S60VideoCaptureSession() +{ + if (m_captureState >= ERecording) + m_videoRecorder->Stop(); + + if (m_captureState >= EInitialized) + m_videoRecorder->Close(); + + if (m_videoRecorder) { + delete m_videoRecorder; + m_videoRecorder = 0; + } + + if (m_durationTimer) { + delete m_durationTimer; + m_durationTimer = 0; + } + + // Clear all data structures + foreach (MaxResolutionRatesAndTypes structure, m_videoParametersForEncoder) { + structure.frameRatePictureSizePair.clear(); + structure.mimeTypes.clear(); + } + m_videoParametersForEncoder.clear(); + + m_videoCodecList.clear(); + m_audioCodecList.clear(); + + QList controllers = m_videoControllerMap.keys(); + for (int i = 0; i < controllers.size(); ++i) { + foreach(VideoFormatData data, m_videoControllerMap[controllers[i]]){ + data.supportedMimeTypes.clear(); + } + m_videoControllerMap[controllers[i]].clear(); + } + m_videoControllerMap.clear(); +} + +/* + * This function can be used both internally and from Control classes using this session. + * The error notification will go to client application through QMediaRecorder error signal. + */ +void S60VideoCaptureSession::setError(const TInt error, const QString &description) +{ + if (error == KErrNone) + return; + + m_error = error; + QMediaRecorder::Error recError = fromSymbianErrorToQtMultimediaError(m_error); + + // Stop/Close/Reset only of other than "not supported" error + if (m_error != KErrNotSupported) { + if (m_captureState >= ERecording) + m_videoRecorder->Stop(); + + if (m_captureState >= EInitialized) + m_videoRecorder->Close(); + + // Reset state + if (m_captureState != ENotInitialized) { + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + } + } + + emit this->error(recError, description); + + // Reset only of other than "not supported" error + if (m_error != KErrNotSupported) + resetSession(true); + else + m_error = KErrNone; // Reset error +} + +QMediaRecorder::Error S60VideoCaptureSession::fromSymbianErrorToQtMultimediaError(int aError) +{ + switch(aError) { + case KErrNone: + return QMediaRecorder::NoError; // No errors have occurred + case KErrArgument: + case KErrNotSupported: + return QMediaRecorder::FormatError; // The feature/format is not supported + case KErrNoMemory: + case KErrNotFound: + case KErrBadHandle: + return QMediaRecorder::ResourceError; // Not able to use camera/recorder resources + + default: + return QMediaRecorder::ResourceError; // Other error has occurred + } +} + +/* + * This function applies all recording settings to make latency during the + * start of the recording as short as possible. After this it is not possible to + * set settings (inc. output location) before stopping the recording. + */ +void S60VideoCaptureSession::applyAllSettings() +{ + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + m_commitSettingsWhenReady = true; + return; + case EInitialized: + setOutputLocation(QUrl()); + m_prepareAfterOpenComplete = true; + return; + case EOpening: + m_prepareAfterOpenComplete = true; + return; + case EOpenComplete: + // Do nothing, ready to commit + break; + case EPreparing: + m_commitSettingsWhenReady = true; + return; + case EPrepared: + // Revert state internally, since logically applying settings means going + // from OpenComplete ==> Preparing ==> Prepared. + m_captureState = EOpenComplete; + break; + case ERecording: + case EPaused: + setError(KErrNotReady, tr("Cannot apply settings while recording.")); + return; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + + // Commit settings - State is now OpenComplete (possibly reverted from Prepared) + commitVideoEncoderSettings(); + + // If capture state has been changed to: + // * Opening: A different media container has been requested + // * Other: Failure during the setting committing + // ==> Return + if (m_captureState != EOpenComplete) + return; + + // Start preparing + m_captureState = EPreparing; + emit stateChanged(m_captureState); + + if (m_cameraEngine->IsCameraReady()) + m_videoRecorder->Prepare(); +} + +void S60VideoCaptureSession::setCameraHandle(CCameraEngine* cameraHandle) +{ + m_cameraEngine = cameraHandle; + + // Initialize settings for the new camera + initializeVideoCaptureSettings(); + + resetSession(); +} + +void S60VideoCaptureSession::notifySettingsSet() +{ + m_captureSettingsSet = true; +} + +void S60VideoCaptureSession::doInitializeVideoRecorderL() +{ + if (m_captureState > ENotInitialized) + resetSession(); + + m_captureState = EInitializing; + emit stateChanged(m_captureState); + + // Open Dummy file to be able to query supported settings + int cameraHandle = m_cameraEngine->Camera() ? m_cameraEngine->Camera()->Handle() : 0; + + TUid controllerUid; + TUid formatUid; + selectController(m_requestedContainer, controllerUid, formatUid); + + if (m_videoRecorder) { + // File open completes in MvruoOpenComplete + TRAPD(err, m_videoRecorder->OpenFileL(KDummyVideoFile, cameraHandle, controllerUid, formatUid)); + setError(err, tr("Failed to initialize video recorder.")); + m_container = m_requestedContainer; + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +void S60VideoCaptureSession::resetSession(bool errorHandling) +{ + if (m_videoRecorder) { + delete m_videoRecorder; + m_videoRecorder = 0; + } + + if (m_captureState != ENotInitialized) { + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + } + + // Reset error to be able to recover + m_error = KErrNone; + + // Reset flags + m_openWhenReady = false; + m_prepareAfterOpenComplete = false; + m_startAfterPrepareComplete = false; + m_uncommittedSettings = false; + m_commitSettingsWhenReady = false; + + TRAPD(err, m_videoRecorder = CVideoRecorderUtility::NewL(*this)); + if (err) { + qWarning("Failed to create video recorder."); + if (errorHandling) + emit error(QMediaRecorder::ResourceError, tr("Failed to recover from video error.")); + else + setError(err, tr("Failure in creation of video recorder device.")); + return; + } + + updateVideoCaptureContainers(); +} + +QList S60VideoCaptureSession::supportedVideoResolutions(bool *continuous) +{ + QList resolutions; + + // Secondary Camera + if (m_cameraEngine->CurrentCameraIndex() != 0) { + TCameraInfo *info = m_cameraEngine->CameraInfo(); + if (info) { + TInt videoResolutionCount = info->iNumVideoFrameSizesSupported; + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + for (TInt i = 0; i < videoResolutionCount; ++i) { + TSize checkedResolution; + camera->EnumerateVideoFrameSizes(checkedResolution, i, CCamera::EFormatYUV420Planar); + QSize qtResolution(checkedResolution.iWidth, checkedResolution.iHeight); + if (!resolutions.contains(qtResolution)) + resolutions.append(qtResolution); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + + // Primary Camera + } else { + + if (m_videoParametersForEncoder.count() > 0) { + + // Also arbitrary resolutions are supported + if (continuous) + *continuous = true; + + // Append all supported resolutions to the list + foreach (MaxResolutionRatesAndTypes parameters, m_videoParametersForEncoder) + for (int i = 0; i < parameters.frameRatePictureSizePair.count(); ++i) + if (!resolutions.contains(parameters.frameRatePictureSizePair[i].frameSize)) + resolutions.append(parameters.frameRatePictureSizePair[i].frameSize); + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + resolutions << QSize(160, 120); + resolutions << QSize(352, 288); + resolutions << QSize(640,480); +#endif // Q_CC_NOKIAX86 + + return resolutions; +} + +QList S60VideoCaptureSession::supportedVideoResolutions(const QVideoEncoderSettings &settings, bool *continuous) +{ + QList supportedFrameSizes; + + // Secondary Camera + if (m_cameraEngine->CurrentCameraIndex() != 0) { + TCameraInfo *info = m_cameraEngine->CameraInfo(); + if (info) { + TInt videoResolutionCount = info->iNumVideoFrameSizesSupported; + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + for (TInt i = 0; i < videoResolutionCount; ++i) { + TSize checkedResolution; + camera->EnumerateVideoFrameSizes(checkedResolution, i, CCamera::EFormatYUV420Planar); + QSize qtResolution(checkedResolution.iWidth, checkedResolution.iHeight); + if (!supportedFrameSizes.contains(qtResolution)) + supportedFrameSizes.append(qtResolution); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + } else { + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + + // Primary Camera + } else { + + if (settings.codec().isEmpty()) + return supportedFrameSizes; + + if (!m_videoCodecList.contains(settings.codec(), Qt::CaseInsensitive)) + return supportedFrameSizes; + + // Also arbitrary resolutions are supported + if (continuous) + *continuous = true; + + // Find maximum resolution (using defined framerate if set) + for (int i = 0; i < m_videoParametersForEncoder.count(); ++i) { + // Check if encoder supports the requested codec + if (!m_videoParametersForEncoder[i].mimeTypes.contains(settings.codec(), Qt::CaseInsensitive)) + continue; + + foreach (SupportedFrameRatePictureSize pair, m_videoParametersForEncoder[i].frameRatePictureSizePair) { + if (!supportedFrameSizes.contains(pair.frameSize)) { + QSize maxForMime = maximumResolutionForMimeType(settings.codec()); + if (settings.frameRate() != 0) { + if (settings.frameRate() <= pair.frameRate) { + if ((pair.frameSize.width() * pair.frameSize.height()) <= (maxForMime.width() * maxForMime.height())) + supportedFrameSizes.append(pair.frameSize); + } + } else { + if ((pair.frameSize.width() * pair.frameSize.height()) <= (maxForMime.width() * maxForMime.height())) + supportedFrameSizes.append(pair.frameSize); + } + } + } + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + supportedFrameSizes << QSize(160, 120); + supportedFrameSizes << QSize(352, 288); + supportedFrameSizes << QSize(640,480); +#endif + + return supportedFrameSizes; +} + +QList S60VideoCaptureSession::supportedVideoFrameRates(bool *continuous) +{ + QList supportedRatesList; + + if (m_videoParametersForEncoder.count() > 0) { + // Insert min and max to the list + supportedRatesList.append(1.0); // Use 1fps as sensible minimum + qreal foundMaxFrameRate(0.0); + + // Also arbitrary framerates are supported + if (continuous) + *continuous = true; + + // Find max framerate + foreach (MaxResolutionRatesAndTypes parameters, m_videoParametersForEncoder) { + for (int i = 0; i < parameters.frameRatePictureSizePair.count(); ++i) { + qreal maxFrameRate = parameters.frameRatePictureSizePair[i].frameRate; + if (maxFrameRate > foundMaxFrameRate) + foundMaxFrameRate = maxFrameRate; + } + } + + supportedRatesList.append(foundMaxFrameRate); + } + + // Add also other standard framerates to the list + if (!supportedRatesList.isEmpty()) { + if (supportedRatesList.last() > 30.0) { + if (!supportedRatesList.contains(30.0)) + supportedRatesList.insert(1, 30.0); + } + if (supportedRatesList.last() > 25.0) { + if (!supportedRatesList.contains(25.0)) + supportedRatesList.insert(1, 25.0); + } + if (supportedRatesList.last() > 15.0) { + if (!supportedRatesList.contains(15.0)) + supportedRatesList.insert(1, 15.0); + } + if (supportedRatesList.last() > 10.0) { + if (!supportedRatesList.contains(10)) + supportedRatesList.insert(1, 10.0); + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + supportedRatesList << 30.0 << 25.0 << 15.0 << 10.0 << 5.0; +#endif + + return supportedRatesList; +} + +QList S60VideoCaptureSession::supportedVideoFrameRates(const QVideoEncoderSettings &settings, bool *continuous) +{ + QList supportedFrameRates; + + if (settings.codec().isEmpty()) + return supportedFrameRates; + if (!m_videoCodecList.contains(settings.codec(), Qt::CaseInsensitive)) + return supportedFrameRates; + + // Also arbitrary framerates are supported + if (continuous) + *continuous = true; + + // Find maximum framerate (using defined resolution if set) + for (int i = 0; i < m_videoParametersForEncoder.count(); ++i) { + // Check if encoder supports the requested codec + if (!m_videoParametersForEncoder[i].mimeTypes.contains(settings.codec(), Qt::CaseInsensitive)) + continue; + + foreach (SupportedFrameRatePictureSize pair, m_videoParametersForEncoder[i].frameRatePictureSizePair) { + if (!supportedFrameRates.contains(pair.frameRate)) { + qreal maxRateForMime = maximumFrameRateForMimeType(settings.codec()); + if (settings.resolution().width() != 0 && settings.resolution().height() != 0) { + if((settings.resolution().width() * settings.resolution().height()) <= (pair.frameSize.width() * pair.frameSize.height())) { + if (pair.frameRate <= maxRateForMime) + supportedFrameRates.append(pair.frameRate); + } + } else { + if (pair.frameRate <= maxRateForMime) + supportedFrameRates.append(pair.frameRate); + } + } + } + } + + // Add also other standard framerates to the list + if (!supportedFrameRates.isEmpty()) { + if (supportedFrameRates.last() > 30.0) { + if (!supportedFrameRates.contains(30.0)) + supportedFrameRates.insert(1, 30.0); + } + if (supportedFrameRates.last() > 25.0) { + if (!supportedFrameRates.contains(25.0)) + supportedFrameRates.insert(1, 25.0); + } + if (supportedFrameRates.last() > 15.0) { + if (!supportedFrameRates.contains(15.0)) + supportedFrameRates.insert(1, 15.0); + } + if (supportedFrameRates.last() > 10.0) { + if (!supportedFrameRates.contains(10)) + supportedFrameRates.insert(1, 10.0); + } + } + +#ifdef Q_CC_NOKIAX86 // Emulator + supportedFrameRates << 30.0 << 25.0 << 15.0 << 10.0 << 5.0; +#endif + + return supportedFrameRates; +} + +bool S60VideoCaptureSession::setOutputLocation(const QUrl &sink) +{ + m_requestedSink = sink; + + if (m_error) + return false; + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EOpening: + case EPreparing: + m_openWhenReady = true; + return true; + + case EInitialized: + case EOpenComplete: + case EPrepared: + // Continue + break; + + case ERecording: + case EPaused: + setError(KErrNotReady, tr("Cannot set file name while recording.")); + return false; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return false; + } + + // Empty URL - Use default file name and path (C:\Data\Videos\video.mp4) + if (sink.isEmpty()) { + + // Make sure default directory exists + QDir videoDir(QDir::rootPath()); + if (!videoDir.exists(KDefaultVideoPath)) + videoDir.mkpath(KDefaultVideoPath); + QString defaultFile = KDefaultVideoPath; + defaultFile.append("\\"); + defaultFile.append(KDefaultVideoFileName); + m_sink.setUrl(defaultFile); + + } else { // Non-empty URL + + QString fullUrl = sink.scheme(); + + // Relative URL + if (sink.isRelative()) { + + // Extract file name and path from the URL + fullUrl = KDefaultVideoPath; + fullUrl.append("\\"); + fullUrl.append(QDir::toNativeSeparators(sink.path())); + + // Absolute URL + } else { + + // Extract file name and path from the URL + if (fullUrl == "file") { + fullUrl = QDir::toNativeSeparators(sink.path().right(sink.path().length() - 1)); + } else { + fullUrl.append(":"); + fullUrl.append(QDir::toNativeSeparators(sink.path())); + } + } + + QString fileName = fullUrl.right(fullUrl.length() - fullUrl.lastIndexOf("\\") - 1); + QString directory = fullUrl.left(fullUrl.lastIndexOf("\\")); + if (directory.lastIndexOf("\\") == (directory.length() - 1)) + directory = directory.left(directory.length() - 1); + + // URL is Absolute path, not including file name + if (!fileName.contains(".")) { + if (fileName != "") { + directory.append("\\"); + directory.append(fileName); + } + fileName = KDefaultVideoFileName; + } + + // Make sure absolute directory exists + QDir videoDir(QDir::rootPath()); + if (!videoDir.exists(directory)) + videoDir.mkpath(directory); + + QString resolvedURL = directory; + resolvedURL.append("\\"); + resolvedURL.append(fileName); + m_sink = QUrl(resolvedURL); + } + + // State is either Initialized, OpenComplete or Prepared, Close previously opened file + if (m_videoRecorder) + m_videoRecorder->Close(); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + // Open file + + QString fileName = QDir::toNativeSeparators(m_sink.toString()); + TPtrC16 fileSink(reinterpret_cast(fileName.utf16())); + + int cameraHandle = m_cameraEngine->Camera() ? m_cameraEngine->Camera()->Handle() : 0; + + TUid controllerUid; + TUid formatUid; + selectController(m_requestedContainer, controllerUid, formatUid); + + if (m_videoRecorder) { + // File open completes in MvruoOpenComplete + TRAPD(err, m_videoRecorder->OpenFileL(fileSink, cameraHandle, controllerUid, formatUid)); + setError(err, tr("Failed to initialize video recorder.")); + m_container = m_requestedContainer; + m_captureState = EOpening; + emit stateChanged(m_captureState); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + m_uncommittedSettings = true; + return true; +} + +QUrl S60VideoCaptureSession::outputLocation() const +{ + return m_sink; +} + +qint64 S60VideoCaptureSession::position() +{ + // Update position only if recording is ongoing + if ((m_captureState == ERecording) && m_videoRecorder) { + // Signal will be automatically emitted of position changes + TRAPD(err, m_position = m_videoRecorder->DurationL().Int64() / 1000); + setError(err, tr("Cannot retrieve video position.")); + } + + return m_position; +} + +S60VideoCaptureSession::TVideoCaptureState S60VideoCaptureSession::state() const +{ + return m_captureState; +} + +bool S60VideoCaptureSession::isMuted() const +{ + return m_muted; +} + +void S60VideoCaptureSession::setMuted(const bool muted) +{ + // CVideoRecorderUtility can mute/unmute only if not recording + if (m_captureState > EPrepared) { + if (muted) + setError(KErrNotSupported, tr("Muting audio is not supported during recording.")); + else + setError(KErrNotSupported, tr("Unmuting audio is not supported during recording.")); + return; + } + + // Check if request is already active + if (muted == isMuted()) + return; + + m_muted = muted; + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::commitVideoEncoderSettings() +{ + if (m_captureState == EOpenComplete) { + + if (m_container != m_requestedContainer) { + setOutputLocation(m_requestedSink); + return; + } + + TRAPD(err, doSetCodecsL()); + if (err) { + setError(err, tr("Failed to set audio or video codec.")); + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + } + + doSetVideoResolution(m_videoSettings.resolution()); + doSetFrameRate(m_videoSettings.frameRate()); + doSetBitrate(m_videoSettings.bitRate()); + + // Audio/Video EncodingMode are not supported in Symbian + +#ifndef S60_31_PLATFORM + if (m_audioSettings.sampleRate() != -1 && m_audioSettings.sampleRate() != 0) { + TRAP(err, m_videoRecorder->SetAudioSampleRateL((TInt)m_audioSettings.sampleRate())); + if (err != KErrNotSupported) { + setError(err, tr("Setting audio sample rate failed.")); + } else { + setError(err, tr("Setting audio sample rate is not supported.")); + m_audioSettings.setSampleRate(KDefaultSampleRate); // Reset + } + } +#endif // S60_31_PLATFORM + + TRAP(err, m_videoRecorder->SetAudioBitRateL((TInt)m_audioSettings.bitRate())); + if (err != KErrNotSupported) { + if (err == KErrArgument) { + setError(KErrNotSupported, tr("Requested audio bitrate is not supported or previously set codec is not supported with requested bitrate.")); + int fallback = 16000; + TRAP(err, m_videoRecorder->SetAudioBitRateL(TInt(fallback))); + if (err == KErrNone) + m_audioSettings.setBitRate(fallback); + } else { + setError(err, tr("Setting audio bitrate failed.")); + } + } + +#ifndef S60_31_PLATFORM + if (m_audioSettings.channelCount() != -1) { + TRAP(err, m_videoRecorder->SetAudioChannelsL(TUint(m_audioSettings.channelCount()))); + if (err != KErrNotSupported) { + setError(err, tr("Setting audio channel count failed.")); + } else { + setError(err, tr("Setting audio channel count is not supported.")); + m_audioSettings.setChannelCount(KDefaultChannelCount); // Reset + } + } +#endif // S60_31_PLATFORM + + TBool isAudioMuted = EFalse; + TRAP(err, isAudioMuted = !m_videoRecorder->AudioEnabledL()); + if (err != KErrNotSupported && err != KErrNone) + setError(err, tr("Failure when checking if audio is enabled.")); + + if (m_muted != (bool)isAudioMuted) { + TRAP(err, m_videoRecorder->SetAudioEnabledL(TBool(!m_muted))); + if (err) { + if (err != KErrNotSupported) { + setError(err, tr("Failed to mute/unmute audio.")); + } else { + setError(err, tr("Muting/unmuting audio is not supported.")); + } + } + else + emit mutedChanged(m_muted); + } + + m_uncommittedSettings = false; // Reset + } +} + +void S60VideoCaptureSession::queryAudioEncoderSettings() +{ + if (!m_videoRecorder) + return; + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EOpening: + case EPreparing: + return; + + // Possible to query settings from CVideoRecorderUtility + case EInitialized: + case EOpenComplete: + case EPrepared: + case ERecording: + case EPaused: + break; + + default: + return; + } + + TInt err = KErrNone; + + // Codec + TFourCC audioCodec; + TRAP(err, audioCodec = m_videoRecorder->AudioTypeL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio codec failed.")); + } + QString codec = ""; + foreach (TFourCC aCodec, m_audioCodecList) { + if (audioCodec == aCodec) + codec = m_audioCodecList.key(aCodec); + } + m_audioSettings.setCodec(codec); + +#ifndef S60_31_PLATFORM + // Samplerate + TInt sampleRate = -1; + TRAP(err, sampleRate = m_videoRecorder->AudioSampleRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio sample rate failed.")); + } + m_audioSettings.setSampleRate(int(sampleRate)); +#endif // S60_31_PLATFORM + + // BitRate + TInt bitRate = -1; + TRAP(err, bitRate = m_videoRecorder->AudioBitRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio bitrate failed.")); + } + m_audioSettings.setBitRate(int(bitRate)); + +#ifndef S60_31_PLATFORM + // ChannelCount + TUint channelCount = 0; + TRAP(err, channelCount = m_videoRecorder->AudioChannelsL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying audio channel count failed.")); + } + if (channelCount != 0) + m_audioSettings.setChannelCount(int(channelCount)); + else + m_audioSettings.setChannelCount(-1); +#endif // S60_31_PLATFORM + + // EncodingMode + m_audioSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + + // IsMuted + TBool isEnabled = ETrue; + TRAP(err, isEnabled = m_videoRecorder->AudioEnabledL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying whether audio is muted failed.")); + } + m_muted = bool(!isEnabled); +} + +void S60VideoCaptureSession::queryVideoEncoderSettings() +{ + if (!m_videoRecorder) + return; + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EOpening: + case EPreparing: + return; + + // Possible to query settings from CVideoRecorderUtility + case EInitialized: + case EOpenComplete: + case EPrepared: + case ERecording: + case EPaused: + break; + + default: + return; + } + + TInt err = KErrNone; + + // Codec + const TDesC8 &videoMimeType = m_videoRecorder->VideoFormatMimeType(); + QString videoMimeTypeString = ""; + if (videoMimeType.Length() > 0) { + // First convert the 8-bit descriptor to Unicode + HBufC16* videoCodec; + videoCodec = CnvUtfConverter::ConvertToUnicodeFromUtf8L(videoMimeType); + CleanupStack::PushL(videoCodec); + + // Then deep copy QString from that + videoMimeTypeString = QString::fromUtf16(videoCodec->Ptr(), videoCodec->Length()); + m_videoSettings.setCodec(videoMimeTypeString); + + CleanupStack::PopAndDestroy(videoCodec); + } + + // Resolution + TSize symbianResolution; + TRAP(err, m_videoRecorder->GetVideoFrameSizeL(symbianResolution)); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying video resolution failed.")); + } + QSize resolution = QSize(symbianResolution.iWidth, symbianResolution.iHeight); + m_videoSettings.setResolution(resolution); + + // FrameRate + TReal32 frameRate = 0; + TRAP(err, frameRate = m_videoRecorder->VideoFrameRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying video framerate failed.")); + } + m_videoSettings.setFrameRate(qreal(frameRate)); + + // BitRate + TInt bitRate = -1; + TRAP(err, bitRate = m_videoRecorder->VideoBitRateL()); + if (err) { + if (err != KErrNotSupported) + setError(err, tr("Querying video bitrate failed.")); + } + m_videoSettings.setBitRate(int(bitRate)); + + // EncodingMode + m_audioSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); +} + +void S60VideoCaptureSession::videoEncoderSettings(QVideoEncoderSettings &videoSettings) +{ + switch (m_captureState) { + // CVideoRecorderUtility, return requested settings + case ENotInitialized: + case EInitializing: + case EInitialized: + case EOpening: + case EOpenComplete: + case EPreparing: + break; + + // Possible to query settings from CVideoRecorderUtility + case EPrepared: + case ERecording: + case EPaused: + queryVideoEncoderSettings(); + break; + + default: + videoSettings = QVideoEncoderSettings(); + setError(KErrGeneral, tr("Unexpected video error.")); + return; + } + + videoSettings = m_videoSettings; +} + +void S60VideoCaptureSession::audioEncoderSettings(QAudioEncoderSettings &audioSettings) +{ + switch (m_captureState) { + // CVideoRecorderUtility, return requested settings + case ENotInitialized: + case EInitializing: + case EInitialized: + case EOpening: + case EOpenComplete: + case EPreparing: + break; + + // Possible to query settings from CVideoRecorderUtility + case EPrepared: + case ERecording: + case EPaused: + queryAudioEncoderSettings(); + break; + + default: + audioSettings = QAudioEncoderSettings(); + setError(KErrGeneral, tr("Unexpected video error.")); + return; + } + + audioSettings = m_audioSettings; +} + +void S60VideoCaptureSession::validateRequestedCodecs() +{ + if (!m_audioCodecList.contains(m_audioSettings.codec())) { + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); + setError(KErrNotSupported, tr("Currently selected audio codec is not supported by the platform.")); + } + if (!m_videoCodecList.contains(m_videoSettings.codec())) { + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + setError(KErrNotSupported, tr("Currently selected video codec is not supported by the platform.")); + } +} + +void S60VideoCaptureSession::setVideoCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const VideoQualityDefinition mode) +{ + // Sensible presets + switch (mode) { + case ENoVideoQuality: + // Do nothing + break; + case EOnlyVideoQuality: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + m_videoSettings.setFrameRate(10); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndResolution: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setFrameRate(10); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + m_videoSettings.setFrameRate(15); + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndFrameRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndBitRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + m_videoSettings.setFrameRate(10); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + m_videoSettings.setFrameRate(15); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndResolutionAndBitRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setFrameRate(10); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setFrameRate(15); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + m_videoSettings.setFrameRate(15); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndResolutionAndFrameRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setBitRate(64000); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setBitRate(128000); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setBitRate(384000); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + m_videoSettings.setBitRate(2000000); + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + case EVideoQualityAndFrameRateAndBitRate: + if (quality == QtMultimediaKit::VeryLowQuality) { + m_videoSettings.setResolution(QSize(128,96)); + } else if (quality == QtMultimediaKit::LowQuality) { + m_videoSettings.setResolution(QSize(176,144)); + } else if (quality == QtMultimediaKit::NormalQuality) { + m_videoSettings.setResolution(QSize(176,144)); + } else if (quality == QtMultimediaKit::HighQuality) { + m_videoSettings.setResolution(QSize(352,288)); + } else if (quality == QtMultimediaKit::VeryHighQuality) { + if (m_cameraEngine && m_cameraEngine->CurrentCameraIndex() == 0) + m_videoSettings.setResolution(QSize(640,480)); // Primary camera + else + m_videoSettings.setResolution(QSize(352,288)); // Other cameras + } else { + m_videoSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported video quality.")); + return; + } + break; + } + + m_videoSettings.setQuality(quality); + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const AudioQualityDefinition mode) +{ + // Based on audio quality definition mode, select proper SampleRate and BitRate + switch (mode) { + case EOnlyAudioQuality: + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_audioSettings.setBitRate(16000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::LowQuality: + m_audioSettings.setBitRate(16000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::NormalQuality: + m_audioSettings.setBitRate(32000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::HighQuality: + m_audioSettings.setBitRate(64000); + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::VeryHighQuality: + m_audioSettings.setBitRate(64000); + m_audioSettings.setSampleRate(-1); + break; + default: + m_audioSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported audio quality.")); + return; + } + break; + case EAudioQualityAndBitRate: + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::LowQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::NormalQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::HighQuality: + m_audioSettings.setSampleRate(-1); + break; + case QtMultimediaKit::VeryHighQuality: + m_audioSettings.setSampleRate(-1); + break; + default: + m_audioSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported audio quality.")); + return; + } + break; + case EAudioQualityAndSampleRate: + switch (quality) { + case QtMultimediaKit::VeryLowQuality: + m_audioSettings.setBitRate(16000); + break; + case QtMultimediaKit::LowQuality: + m_audioSettings.setBitRate(16000); + break; + case QtMultimediaKit::NormalQuality: + m_audioSettings.setBitRate(32000); + break; + case QtMultimediaKit::HighQuality: + m_audioSettings.setBitRate(64000); + break; + case QtMultimediaKit::VeryHighQuality: + m_audioSettings.setBitRate(64000); + break; + default: + m_audioSettings.setQuality(QtMultimediaKit::NormalQuality); + setError(KErrNotSupported, tr("Unsupported audio quality.")); + return; + } + break; + case ENoAudioQuality: + // No actions required, just set quality parameter + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + + m_audioSettings.setQuality(quality); + m_uncommittedSettings = true; +} + +int S60VideoCaptureSession::initializeVideoRecording() +{ + if (m_error) + return m_error; + + TRAPD(symbianError, doInitializeVideoRecorderL()); + setError(symbianError, tr("Failed to initialize video recorder.")); + + return symbianError; +} + +void S60VideoCaptureSession::releaseVideoRecording() +{ + if (m_captureState >= ERecording) { + m_videoRecorder->Stop(); + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + } + + if (m_captureState >= EInitialized) + m_videoRecorder->Close(); + + // Reset state + m_captureState = ENotInitialized; + + // Reset error to be able to recover from error + m_error = KErrNone; + + // Reset flags + m_openWhenReady = false; + m_prepareAfterOpenComplete = false; + m_startAfterPrepareComplete = false; + m_uncommittedSettings = false; + m_commitSettingsWhenReady = false; +} + +void S60VideoCaptureSession::startRecording() +{ + if (m_error) { + setError(m_error, tr("Unexpected recording error.")); + return; + } + + switch (m_captureState) { + case ENotInitialized: + case EInitializing: + case EInitialized: + if (m_captureState == EInitialized) + setOutputLocation(m_requestedSink); + m_startAfterPrepareComplete = true; + return; + + case EOpening: + case EPreparing: + // Execute FileOpenL() and Prepare() asap and then start recording + m_startAfterPrepareComplete = true; + return; + case EOpenComplete: + case EPrepared: + if (m_captureState == EPrepared && !m_uncommittedSettings) + break; + + // Revert state internally, since logically applying settings means going + // from OpenComplete ==> Preparing ==> Prepared. + m_captureState = EOpenComplete; + m_startAfterPrepareComplete = true; + + // Commit settings and prepare with them + applyAllSettings(); + return; + case ERecording: + // Discard + return; + case EPaused: + // Continue + break; + + default: + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + + // State should now be either Prepared with no Uncommitted Settings or Paused + + if (!m_cameraStarted) { + m_startAfterPrepareComplete = true; + return; + } + + if (m_cameraEngine && !m_cameraEngine->IsCameraReady()) { + setError(KErrNotReady, tr("Camera not ready to start video recording.")); + return; + } + + if (m_videoRecorder) { + m_videoRecorder->Record(); + m_captureState = ERecording; + emit stateChanged(m_captureState); + m_durationTimer->start(); + + // Reset all flags + m_openWhenReady = false; + m_prepareAfterOpenComplete = false; + m_startAfterPrepareComplete = false; + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +void S60VideoCaptureSession::pauseRecording() +{ + if (m_captureState == ERecording) { + if (m_videoRecorder) { + TRAPD(err, m_videoRecorder->PauseL()); + setError(err, tr("Pausing video recording failed.")); + m_captureState = EPaused; + emit stateChanged(m_captureState); + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + + // Notify last duration + TRAP(err, m_position = m_videoRecorder->DurationL().Int64() / 1000); + setError(err, tr("Cannot retrieve video position.")); + emit positionChanged(m_position); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); + } +} + +void S60VideoCaptureSession::stopRecording(const bool reInitialize) +{ + if (m_captureState != ERecording && m_captureState != EPaused) + return; // Ignore + + if (m_videoRecorder) { + m_videoRecorder->Stop(); + m_videoRecorder->Close(); + + // Notify muting is disabled if needed + if (m_muted) + emit mutedChanged(false); + + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + + // VideoRecording will be re-initialized unless explicitly requested not to do so + if (reInitialize) { + if (m_cameraEngine->IsCameraReady()) + initializeVideoRecording(); + } + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +void S60VideoCaptureSession::updateVideoCaptureContainers() +{ + TRAPD(err, doUpdateVideoCaptureContainersL()); + setError(err, tr("Failed to gather video container information.")); +} + +void S60VideoCaptureSession::doUpdateVideoCaptureContainersL() +{ + // Clear container data structure + QList mapControllers = m_videoControllerMap.keys(); + for (int i = 0; i < mapControllers.size(); ++i) { + foreach(VideoFormatData data, m_videoControllerMap[mapControllers[i]]){ + data.supportedMimeTypes.clear(); + } + m_videoControllerMap[mapControllers[i]].clear(); + } + m_videoControllerMap.clear(); + + // Resolve the supported video format and retrieve a list of controllers + CMMFControllerPluginSelectionParameters* pluginParameters = + CMMFControllerPluginSelectionParameters::NewLC(); + CMMFFormatSelectionParameters* format = + CMMFFormatSelectionParameters::NewLC(); + + // Set the play and record format selection parameters to be blank. + // Format support is only retrieved if requested. + pluginParameters->SetRequiredPlayFormatSupportL(*format); + pluginParameters->SetRequiredRecordFormatSupportL(*format); + + // Set the media IDs + RArray mediaIds; + CleanupClosePushL(mediaIds); + + User::LeaveIfError(mediaIds.Append(KUidMediaTypeVideo)); + + // Get plugins that support at least video + pluginParameters->SetMediaIdsL(mediaIds, + CMMFPluginSelectionParameters::EAllowOtherMediaIds); + pluginParameters->SetPreferredSupplierL(KNullDesC, + CMMFPluginSelectionParameters::EPreferredSupplierPluginsFirstInList); + + // Array to hold all the controllers support the match data + RMMFControllerImplInfoArray controllers; + CleanupResetAndDestroyPushL(controllers); + pluginParameters->ListImplementationsL(controllers); + + // Find the first controller with at least one record format available + for (TInt index = 0; index < controllers.Count(); ++index) { + + m_videoControllerMap.insert(controllers[index]->Uid().iUid, QHash()); + + const RMMFFormatImplInfoArray& recordFormats = controllers[index]->RecordFormats(); + for (TInt j = 0; j < recordFormats.Count(); ++j) { + VideoFormatData formatData; + formatData.description = QString::fromUtf16( + recordFormats[j]->DisplayName().Ptr(), + recordFormats[j]->DisplayName().Length()); + + const CDesC8Array& mimeTypes = recordFormats[j]->SupportedMimeTypes(); + for (int k = 0; k < mimeTypes.Count(); ++k) { + TPtrC8 mimeType = mimeTypes[k]; + QString type = QString::fromUtf8((char *)mimeType.Ptr(), + mimeType.Length()); + formatData.supportedMimeTypes.append(type); + } + + m_videoControllerMap[controllers[index]->Uid().iUid].insert(recordFormats[j]->Uid().iUid, formatData); + } + } + + CleanupStack::PopAndDestroy(&controllers); + CleanupStack::PopAndDestroy(&mediaIds); + CleanupStack::PopAndDestroy(format); + CleanupStack::PopAndDestroy(pluginParameters); +} + +/* + * This goes through the available controllers and selects proper one based + * on the format. Function sets proper UIDs to be used for controller and format. + */ +void S60VideoCaptureSession::selectController(const QString &format, + TUid &controllerUid, + TUid &formatUid) +{ + QList controllers = m_videoControllerMap.keys(); + QList formats; + + for (int i = 0; i < controllers.count(); ++i) { + formats = m_videoControllerMap[controllers[i]].keys(); + for (int j = 0; j < formats.count(); ++j) { + VideoFormatData formatData = m_videoControllerMap[controllers[i]][formats[j]]; + if (formatData.supportedMimeTypes.contains(format, Qt::CaseInsensitive)) { + controllerUid = TUid::Uid(controllers[i]); + formatUid = TUid::Uid(formats[j]); + } + } + } +} + +QStringList S60VideoCaptureSession::supportedVideoCaptureCodecs() +{ + return m_videoCodecList; +} + +QStringList S60VideoCaptureSession::supportedAudioCaptureCodecs() +{ + QStringList keys = m_audioCodecList.keys(); + keys.sort(); + return keys; +} + +QList S60VideoCaptureSession::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) +{ + QList rates; + + TRAPD(err, rates = doGetSupportedSampleRatesL(settings, continuous)); + if (err != KErrNotSupported) + setError(err, tr("Failed to query information of supported sample rates.")); + + return rates; +} + +QList S60VideoCaptureSession::doGetSupportedSampleRatesL(const QAudioEncoderSettings &settings, bool *continuous) +{ + QList sampleRates; + + if (m_captureState < EOpenComplete) + return sampleRates; + +#ifndef S60_31_PLATFORM + RArray supportedSampleRates; + CleanupClosePushL(supportedSampleRates); + + if (!settings.codec().isEmpty()) { + + TFourCC currentAudioCodec; + currentAudioCodec = m_videoRecorder->AudioTypeL(); + + TFourCC requestedAudioCodec; + if (qstrcmp(settings.codec().toLocal8Bit().constData(), "audio/aac") == 0) + requestedAudioCodec.Set(KMMFFourCCCodeAAC); + else if (qstrcmp(settings.codec().toLocal8Bit().constData(), "audio/amr") == 0) + requestedAudioCodec.Set(KMMFFourCCCodeAMR); + m_videoRecorder->SetAudioTypeL(requestedAudioCodec); + + m_videoRecorder->GetSupportedAudioSampleRatesL(supportedSampleRates); + + m_videoRecorder->SetAudioTypeL(currentAudioCodec); + } + else + m_videoRecorder->GetSupportedAudioSampleRatesL(supportedSampleRates); + + for (int i = 0; i < supportedSampleRates.Count(); ++i) + sampleRates.append(int(supportedSampleRates[i])); + + CleanupStack::PopAndDestroy(); // RArray supportedSampleRates +#else // S60 3.1 Platform + Q_UNUSED(settings); +#endif // S60_31_PLATFORM + + if (continuous) + *continuous = false; + + return sampleRates; +} + +void S60VideoCaptureSession::setAudioSampleRate(const int sampleRate) +{ + if (sampleRate != -1) + m_audioSettings.setSampleRate(sampleRate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioBitRate(const int bitRate) +{ + if (bitRate != -1) + m_audioSettings.setBitRate(bitRate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioChannelCount(const int channelCount) +{ + if (channelCount != -1) + m_audioSettings.setChannelCount(channelCount); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setVideoCaptureCodec(const QString &codecName) +{ + if (codecName == m_videoSettings.codec()) + return; + + if (codecName.isEmpty()) + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); // Use default + else + m_videoSettings.setCodec(codecName); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioCaptureCodec(const QString &codecName) +{ + if (codecName == m_audioSettings.codec()) + return; + + if (codecName.isEmpty()) { + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); // Use default + } else { + // If information of supported codecs is already available check that + // given codec is supported + if (m_captureState >= EOpenComplete) { + if (m_audioCodecList.contains(codecName)) { + m_audioSettings.setCodec(codecName); + m_uncommittedSettings = true; + } else { + setError(KErrNotSupported, tr("Requested audio codec is not supported")); + } + } else { + m_audioSettings.setCodec(codecName); + m_uncommittedSettings = true; + } + } +} + +QString S60VideoCaptureSession::videoCaptureCodecDescription(const QString &codecName) +{ + QString codecDescription; + if (codecName.contains("video/H263-2000", Qt::CaseInsensitive)) + codecDescription.append("H.263 Video Codec"); + else if (codecName.contains("video/mp4v-es", Qt::CaseInsensitive)) + codecDescription.append("MPEG-4 Part 2 Video Codec"); + else if (codecName.contains("video/H264", Qt::CaseInsensitive)) + codecDescription.append("H.264 AVC (MPEG-4 Part 10) Video Codec"); + else + codecDescription.append("Video Codec"); + + return codecDescription; +} + +void S60VideoCaptureSession::doSetCodecsL() +{ + // Determine Profile and Level for the video codec if needed + // (MimeType/Profile-level-id contains "profile" if profile/level info is available) + if (!m_videoSettings.codec().contains(QString("profile"), Qt::CaseInsensitive)) + m_videoSettings.setCodec(determineProfileAndLevel()); + + if (m_videoRecorder) { + TPtrC16 str(reinterpret_cast(m_videoSettings.codec().utf16())); + HBufC8* videoCodec(0); + videoCodec = CnvUtfConverter::ConvertFromUnicodeToUtf8L(str); + CleanupStack::PushL(videoCodec); + + TFourCC audioCodec = m_audioCodecList[m_audioSettings.codec()]; + + TInt vErr = KErrNone; + TInt aErr = KErrNone; + TRAP(vErr, m_videoRecorder->SetVideoTypeL(*videoCodec)); + TRAP(aErr, m_videoRecorder->SetAudioTypeL(audioCodec)); + + User::LeaveIfError(vErr); + User::LeaveIfError(aErr); + + CleanupStack::PopAndDestroy(videoCodec); + } + else + setError(KErrNotReady, tr("Unexpected camera error.")); +} + +QString S60VideoCaptureSession::determineProfileAndLevel() +{ + QString determinedMimeType = m_videoSettings.codec(); + + // H.263 + if (determinedMimeType.contains(QString("video/H263-2000"), Qt::CaseInsensitive)) { + if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (176*144)) { + if (m_videoSettings.frameRate() > 15.0) + determinedMimeType.append("; profile=0; level=20"); + else + determinedMimeType.append("; profile=0; level=40"); + } else { + if (m_videoSettings.bitRate() > 64000) + determinedMimeType.append("; profile=0; level=45"); + else + determinedMimeType.append("; profile=0; level=10"); + } + + // MPEG-4 + } else if (determinedMimeType.contains(QString("video/mp4v-es"), Qt::CaseInsensitive)) { + if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (720*480)) { + determinedMimeType.append("; profile-level-id=6"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (640*480)) { + determinedMimeType.append("; profile-level-id=5"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (352*288)) { + determinedMimeType.append("; profile-level-id=4"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (176*144)) { + if (m_videoSettings.frameRate() > 15.0) + determinedMimeType.append("; profile-level-id=3"); + else + determinedMimeType.append("; profile-level-id=2"); + } else { + if (m_videoSettings.bitRate() > 64000) + determinedMimeType.append("; profile-level-id=9"); + else + determinedMimeType.append("; profile-level-id=1"); + } + + // H.264 + } else if (determinedMimeType.contains(QString("video/H264"), Qt::CaseInsensitive)) { + if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (640*480)) { + determinedMimeType.append("; profile-level-id=42801F"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (352*288)) { + determinedMimeType.append("; profile-level-id=42801E"); + } else if ((m_videoSettings.resolution().width() * m_videoSettings.resolution().height()) > (176*144)) { + if (m_videoSettings.frameRate() > 15.0) + determinedMimeType.append("; profile-level-id=428015"); + else + determinedMimeType.append("; profile-level-id=42800C"); + } else { + determinedMimeType.append("; profile-level-id=42900B"); + } + } + + return determinedMimeType; +} + +void S60VideoCaptureSession::setBitrate(const int bitrate) +{ + m_videoSettings.setBitRate(bitrate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::doSetBitrate(const int &bitrate) +{ + if (bitrate != -1) { + if (m_videoRecorder) { + TRAPD(err, m_videoRecorder->SetVideoBitRateL(bitrate)); + if (err) { + if (err == KErrNotSupported || err == KErrArgument) { + setError(KErrNotSupported, tr("Requested video bitrate is not supported.")); + m_videoSettings.setBitRate(64000); // Reset + } else { + setError(err, tr("Failed to set video bitrate.")); + } + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + } +} + +void S60VideoCaptureSession::setVideoResolution(const QSize &resolution) +{ + m_videoSettings.setResolution(resolution); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::doSetVideoResolution(const QSize &resolution) +{ + TSize size((TInt)resolution.width(), (TInt)resolution.height()); + + // Make sure resolution is not too big if main camera is not used + if (m_cameraEngine->CurrentCameraIndex() != 0) { + TCameraInfo *info = m_cameraEngine->CameraInfo(); + if (info) { + TInt videoResolutionCount = info->iNumVideoFrameSizesSupported; + TSize maxCameraVideoResolution = TSize(0,0); + CCamera *camera = m_cameraEngine->Camera(); + if (camera) { + for (TInt i = 0; i < videoResolutionCount; ++i) { + TSize checkedResolution; + // Use YUV video max frame size in the check (Through + // CVideoRecorderUtility/DevVideoRecord it is possible to + // query only encoder maximums) + camera->EnumerateVideoFrameSizes(checkedResolution, i, CCamera::EFormatYUV420Planar); + if ((checkedResolution.iWidth * checkedResolution.iHeight) > + (maxCameraVideoResolution.iWidth * maxCameraVideoResolution.iHeight)) + maxCameraVideoResolution = checkedResolution; + } + if ((maxCameraVideoResolution.iWidth * maxCameraVideoResolution.iHeight) < + (size.iWidth * size.iHeight)) { + size = maxCameraVideoResolution; + setError(KErrNotSupported, tr("Requested resolution is not supported for this camera.")); + } + } + else + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + }else + setError(KErrGeneral, tr("Could not query supported video resolutions.")); + } + + if (resolution.width() != -1 && resolution.height() != -1) { + if (m_videoRecorder) { + TRAPD(err, m_videoRecorder->SetVideoFrameSizeL((TSize)size)); + if (err == KErrNotSupported || err == KErrArgument) { + setError(KErrNotSupported, tr("Requested video resolution is not supported.")); + TSize fallBack(640,480); + TRAPD(err, m_videoRecorder->SetVideoFrameSizeL(fallBack)); + if (err == KErrNone) { + m_videoSettings.setResolution(QSize(fallBack.iWidth,fallBack.iHeight)); + } else { + fallBack = TSize(176,144); + TRAPD(err, m_videoRecorder->SetVideoFrameSizeL(fallBack)); + if (err == KErrNone) + m_videoSettings.setResolution(QSize(fallBack.iWidth,fallBack.iHeight)); + } + } else { + setError(err, tr("Failed to set video resolution.")); + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + } +} + +void S60VideoCaptureSession::setFrameRate(qreal rate) +{ + m_videoSettings.setFrameRate(rate); + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::doSetFrameRate(qreal rate) +{ + if (rate != 0) { + if (m_videoRecorder) { + bool continuous = false; + QList list = supportedVideoFrameRates(&continuous); + qreal maxRate = 0.0; + foreach (qreal fRate, list) + if (fRate > maxRate) + maxRate = fRate; + if (maxRate >= rate && rate > 0) { + TRAPD(err, m_videoRecorder->SetVideoFrameRateL((TReal32)rate)); + if (err == KErrNotSupported) { + setError(KErrNotSupported, tr("Requested framerate is not supported.")); + TReal32 fallBack = 15.0; + TRAPD(err, m_videoRecorder->SetVideoFrameRateL(fallBack)); + if (err == KErrNone) + m_videoSettings.setFrameRate((qreal)fallBack); + } else { + if (err == KErrArgument) { + setError(KErrNotSupported, tr("Requested framerate is not supported.")); + m_videoSettings.setFrameRate(15.0); // Reset + } else { + setError(err, tr("Failed to set video framerate.")); + } + } + } else { + setError(KErrNotSupported, tr("Requested framerate is not supported.")); + m_videoSettings.setFrameRate(15.0); // Reset + } + } else { + setError(KErrNotReady, tr("Unexpected camera error.")); + } + } +} + +void S60VideoCaptureSession::setVideoEncodingMode(const QtMultimediaKit::EncodingMode mode) +{ + // This has no effect as it has no support in Symbian + + if (mode == QtMultimediaKit::ConstantQualityEncoding) { + m_videoSettings.setEncodingMode(mode); + return; + } + + setError(KErrNotSupported, tr("Requested video encoding mode is not supported")); + + // m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::setAudioEncodingMode(const QtMultimediaKit::EncodingMode mode) +{ + // This has no effect as it has no support in Symbian + + if (mode == QtMultimediaKit::ConstantQualityEncoding) { + m_audioSettings.setEncodingMode(mode); + return; + } + + setError(KErrNotSupported, tr("Requested audio encoding mode is not supported")); + + // m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::initializeVideoCaptureSettings() +{ + // Check if user has already requested some settings + if (m_captureSettingsSet) + return; + + QSize resolution(-1, -1); + qreal frameRate(0); + int bitRate(-1); + + if (m_cameraEngine) { + + if (m_videoRecorder && m_captureState >= EInitialized) { + + // Resolution + QList resos = supportedVideoResolutions(0); + foreach (QSize reso, resos) { + if ((reso.width() * reso.height()) > (resolution.width() * resolution.height())) + resolution = reso; + } + + // Needed to query supported framerates for this codec/resolution pair + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + m_videoSettings.setResolution(resolution); + + // FrameRate + QList fRates = supportedVideoFrameRates(m_videoSettings, 0); + foreach (qreal rate, fRates) { + if (rate > frameRate) + frameRate = rate; + } + + // BitRate +#ifdef SYMBIAN_3_PLATFORM + if (m_cameraEngine->CurrentCameraIndex() == 0) + bitRate = KBiR_H264_PLID_42801F // 14Mbps + else + bitRate = KBiR_H264_PLID_428016 // 4Mbps +#else // Other platforms + if (m_cameraEngine->CurrentCameraIndex() == 0) + bitRate = KBiR_MPEG4_PLID_4 // 2/4Mbps + else + bitRate = KBiR_MPEG4_PLID_3 // 384kbps +#endif // SYMBIAN_3_PLATFORM + + } else { +#ifdef SYMBIAN_3_PLATFORM + if (m_cameraEngine->CurrentCameraIndex() == 0) { + // Primary camera + resolution = KResH264_PLID_42801F; // 1280x720 + frameRate = KFrR_H264_PLID_42801F; // 30fps + bitRate = KBiR_H264_PLID_42801F; // 14Mbps + } else { + // Other cameras + resolution = KResH264_PLID_42801E; // 640x480 + frameRate = KFrR_H264_PLID_428014; // 30fps + bitRate = KBiR_H264_PLID_428016; // 4Mbps + } +#else // Other platforms + if (m_cameraEngine->CurrentCameraIndex() == 0) { + // Primary camera + resolution = KResMPEG4_PLID_4; // 640x480 + frameRate = KFrR_MPEG4_PLID_4; // 15/30fps + bitRate = KBiR_MPEG4_PLID_4; // 2/4Mbps + } else { + // Other cameras + resolution = KResMPEG4_PLID_3; // 352x288 + frameRate = KFrR_MPEG4; // 15fps + bitRate = KBiR_MPEG4_PLID_3; // 384kbps + } +#endif // SYMBIAN_3_PLATFORM + } + } else { +#ifdef SYMBIAN_3_PLATFORM + resolution = KResH264_PLID_42801F; + frameRate = KFrR_H264_PLID_42801F; + bitRate = KBiR_H264_PLID_42801F; +#else // Pre-Symbian3 Platforms + resolution = KResMPEG4_PLID_4; + frameRate = KFrR_MPEG4_PLID_4; + bitRate = KBiR_MPEG4_PLID_4; +#endif // SYMBIAN_3_PLATFORM + } + + // Set specified settings (Resolution, FrameRate and BitRate) + m_videoSettings.setResolution(resolution); + m_videoSettings.setFrameRate(frameRate); + m_videoSettings.setBitRate(bitRate); + + // Video Settings: Codec, EncodingMode and Quality + m_videoSettings.setCodec(KMimeTypeDefaultVideoCodec); + m_videoSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + m_videoSettings.setQuality(QtMultimediaKit::VeryHighQuality); + + // Audio Settings + m_audioSettings.setCodec(KMimeTypeDefaultAudioCodec); + m_audioSettings.setBitRate(KDefaultBitRate); + m_audioSettings.setSampleRate(KDefaultSampleRate); + m_audioSettings.setChannelCount(KDefaultChannelCount); + m_audioSettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + m_audioSettings.setQuality(QtMultimediaKit::VeryHighQuality); +} + +QSize S60VideoCaptureSession::pixelAspectRatio() +{ +#ifndef S60_31_PLATFORM + TVideoAspectRatio par; + TRAPD(err, m_videoRecorder->GetPixelAspectRatioL(par)); + if (err) + setError(err, tr("Failed to query current pixel aspect ratio.")); + return QSize(par.iNumerator, par.iDenominator); +#else // S60_31_PLATFORM + return QSize(); +#endif // !S60_31_PLATFORM +} + +void S60VideoCaptureSession::setPixelAspectRatio(const QSize par) +{ +#ifndef S60_31_PLATFORM + + const TVideoAspectRatio videoPar(par.width(), par.height()); + TRAPD(err, m_videoRecorder->SetPixelAspectRatioL(videoPar)); + if (err) + setError(err, tr("Failed to set pixel aspect ratio.")); +#else // S60_31_PLATFORM + Q_UNUSED(par); +#endif // !S60_31_PLATFORM + + m_uncommittedSettings = true; +} + +int S60VideoCaptureSession::gain() +{ + TInt gain = 0; + TRAPD(err, gain = m_videoRecorder->GainL()); + if (err) + setError(err, tr("Failed to query video gain.")); + return (int)gain; +} + +void S60VideoCaptureSession::setGain(const int gain) +{ + TRAPD(err, m_videoRecorder->SetGainL(gain)); + if (err) + setError(err, tr("Failed to set video gain.")); + + m_uncommittedSettings = true; +} + +int S60VideoCaptureSession::maxClipSizeInBytes() const +{ + return m_maxClipSize; +} + +void S60VideoCaptureSession::setMaxClipSizeInBytes(const int size) +{ + TRAPD(err, m_videoRecorder->SetMaxClipSizeL(size)); + if (err) { + setError(err, tr("Failed to set maximum video size.")); + } else + m_maxClipSize = size; + + m_uncommittedSettings = true; +} + +void S60VideoCaptureSession::MvruoOpenComplete(TInt aError) +{ + if (m_error) + return; + + if (aError == KErrNone && m_videoRecorder) { + if (m_captureState == EInitializing) { + // Dummy file open completed, initialize settings + TRAPD(err, doPopulateAudioCodecsL()); + setError(err, tr("Failed to gather information of supported audio codecs.")); + + // For DevVideoRecord codecs are populated during + // doPopulateVideoCodecsDataL() + TRAP(err, doPopulateVideoCodecsL()); + setError(err, tr("Failed to gather information of supported video codecs.")); +#ifndef S60_DEVVIDEO_RECORDING_SUPPORTED + // Max parameters needed to be populated, if not using DevVideoRecord + // Otherwise done already in constructor + doPopulateMaxVideoParameters(); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + + m_captureState = EInitialized; + emit stateChanged(m_captureState); + + // Initialize settings if not already done + initializeVideoCaptureSettings(); + + // Validate codecs to be used + validateRequestedCodecs(); + + if (m_openWhenReady || m_prepareAfterOpenComplete || m_startAfterPrepareComplete) { + setOutputLocation(m_requestedSink); + m_openWhenReady = false; // Reset + } + if (m_commitSettingsWhenReady) { + applyAllSettings(); + m_commitSettingsWhenReady = false; // Reset + } + return; + + } else if (m_captureState == EOpening) { + // Actual file open completed + m_captureState = EOpenComplete; + emit stateChanged(m_captureState); + + // Prepare right away + if (m_startAfterPrepareComplete || m_prepareAfterOpenComplete) { + m_prepareAfterOpenComplete = false; // Reset + + // Commit settings and prepare with them + applyAllSettings(); + } + return; + + } else if (m_captureState == ENotInitialized) { + // Resources released while waiting OpenFileL to complete + m_videoRecorder->Close(); + return; + + } else { + setError(KErrGeneral, tr("Unexpected camera error.")); + return; + } + } + + m_videoRecorder->Close(); + if (aError == KErrNotFound || aError == KErrNotSupported || aError == KErrArgument) + setError(KErrGeneral, tr("Requested video container or controller is not supported.")); + else + setError(KErrGeneral, tr("Failure during video recorder initialization.")); +} + +void S60VideoCaptureSession::MvruoPrepareComplete(TInt aError) +{ + if (m_error) + return; + + if(aError == KErrNone) { + if (m_captureState == ENotInitialized) { + // Resources released while waiting for Prepare to complete + m_videoRecorder->Close(); + return; + } + + emit captureSizeChanged(m_videoSettings.resolution()); + + m_captureState = EPrepared; + emit stateChanged(EPrepared); + + // Check the actual active settings + queryAudioEncoderSettings(); + queryVideoEncoderSettings(); + + if (m_openWhenReady == true) { + setOutputLocation(m_requestedSink); + m_openWhenReady = false; // Reset + } + + if (m_commitSettingsWhenReady) { + applyAllSettings(); + m_commitSettingsWhenReady = false; // Reset + } + + if (m_startAfterPrepareComplete) { + m_startAfterPrepareComplete = false; // Reset + startRecording(); + } + } else { + m_videoRecorder->Close(); + if (aError == KErrNotSupported) + setError(aError, tr("Camera preparation for video recording failed because of unsupported setting.")); + else + setError(aError, tr("Failed to prepare camera for video recording.")); + } +} + +void S60VideoCaptureSession::MvruoRecordComplete(TInt aError) +{ + if (!m_videoRecorder) { + setError(KErrNotReady, tr("Unexpected camera error.")); + return; + } + + if((aError == KErrNone || aError == KErrCompletion)) { + m_videoRecorder->Stop(); + + // Reset state + if (m_captureState != ENotInitialized) { + m_captureState = ENotInitialized; + emit stateChanged(m_captureState); + if (m_durationTimer->isActive()) + m_durationTimer->stop(); + } + + if (m_cameraEngine->IsCameraReady()) + initializeVideoRecording(); + } + m_videoRecorder->Close(); + + // Notify muting is disabled if needed + if (m_muted) + emit mutedChanged(false); + + if (aError == KErrDiskFull) + setError(aError, tr("Not enough space for video, recording stopped.")); + else + setError(aError, tr("Recording stopped due to unexpected error.")); +} + +void S60VideoCaptureSession::MvruoEvent(const TMMFEvent& aEvent) +{ + Q_UNUSED(aEvent); +} + +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +void S60VideoCaptureSession::MdvroReturnPicture(TVideoPicture *aPicture) +{ + // Not used + Q_UNUSED(aPicture); +} + +void S60VideoCaptureSession::MdvroSupplementalInfoSent() +{ + // Not used +} + +void S60VideoCaptureSession::MdvroNewBuffers() +{ + // Not used +} + +void S60VideoCaptureSession::MdvroFatalError(TInt aError) +{ + setError(aError, tr("Unexpected camera error.")); +} + +void S60VideoCaptureSession::MdvroInitializeComplete(TInt aError) +{ + // Not used + Q_UNUSED(aError); +} + +void S60VideoCaptureSession::MdvroStreamEnd() +{ + // Not used +} + +/* + * This populates video codec information (supported codecs, resolutions, + * framerates, etc.) using DevVideoRecord API. + */ +void S60VideoCaptureSession::doPopulateVideoCodecsDataL() +{ + RArray encoders; + CleanupClosePushL(encoders); + + CMMFDevVideoRecord *mDevVideoRecord = CMMFDevVideoRecord::NewL(*this); + CleanupStack::PushL(mDevVideoRecord); + + // Retrieve list of all encoders provided by the platform + mDevVideoRecord->GetEncoderListL(encoders); + + for (int i = 0; i < encoders.Count(); ++i ) { + + CVideoEncoderInfo *encoderInfo = mDevVideoRecord->VideoEncoderInfoLC(encoders[i]); + + // Discard encoders that are not HW accelerated and do not support direct capture + if (encoderInfo->Accelerated() == false || encoderInfo->SupportsDirectCapture() == false) { + CleanupStack::Check(encoderInfo); + CleanupStack::PopAndDestroy(encoderInfo); + continue; + } + + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); + int newIndex = m_videoParametersForEncoder.count() - 1; + + m_videoParametersForEncoder[newIndex].bitRate = (int)encoderInfo->MaxBitrate(); + + // Get supported MIME Types + const RPointerArray &videoFormats = encoderInfo->SupportedOutputFormats(); + for(int x = 0; x < videoFormats.Count(); ++x) { + QString codecMimeType = QString::fromUtf8((char *)videoFormats[x]->MimeType().Ptr(),videoFormats[x]->MimeType().Length()); + + m_videoParametersForEncoder[newIndex].mimeTypes.append(codecMimeType); + } + + // Get supported maximum Resolution/Framerate pairs + const RArray &ratesAndSizes = encoderInfo->MaxPictureRates(); + SupportedFrameRatePictureSize data; + for(int j = 0; j < ratesAndSizes.Count(); ++j) { + data.frameRate = ratesAndSizes[j].iPictureRate; + data.frameSize = QSize(ratesAndSizes[j].iPictureSize.iWidth, ratesAndSizes[j].iPictureSize.iHeight); + + // Save data to the hash + m_videoParametersForEncoder[newIndex].frameRatePictureSizePair.append(data); + } + + CleanupStack::Check(encoderInfo); + CleanupStack::PopAndDestroy(encoderInfo); + } + + CleanupStack::Check(mDevVideoRecord); + CleanupStack::PopAndDestroy(mDevVideoRecord); + CleanupStack::PopAndDestroy(); // RArray encoders +} +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +QStringList S60VideoCaptureSession::supportedVideoContainers() +{ + QStringList containers; + + QList controllers = m_videoControllerMap.keys(); + for (int i = 0; i < controllers.count(); ++i) { + foreach (VideoFormatData formatData, m_videoControllerMap[controllers[i]]) { + for (int j = 0; j < formatData.supportedMimeTypes.count(); ++j) { + if (containers.contains(formatData.supportedMimeTypes[j], Qt::CaseInsensitive) == false) + containers.append(formatData.supportedMimeTypes[j]); + } + } + } + + return containers; +} + +bool S60VideoCaptureSession::isSupportedVideoContainer(const QString &containerName) +{ + return supportedVideoContainers().contains(containerName, Qt::CaseInsensitive); +} + +QString S60VideoCaptureSession::videoContainer() const +{ + return m_container; +} + +void S60VideoCaptureSession::setVideoContainer(const QString &containerName) +{ + if (containerName == m_requestedContainer) + return; + + if (containerName.isEmpty()) { + m_requestedContainer = KMimeTypeDefaultContainer; // Use default + } else { + if (supportedVideoContainers().contains(containerName)) { + m_requestedContainer = containerName; + } else { + setError(KErrNotSupported, tr("Requested video container is not supported.")); + m_requestedContainer = KMimeTypeDefaultContainer; // Reset to default + } + } + + m_uncommittedSettings = true; +} + +QString S60VideoCaptureSession::videoContainerDescription(const QString &containerName) +{ + QList formats; + QList encoders = m_videoControllerMap.keys(); + for (int i = 0; i < encoders.count(); ++i) { + formats = m_videoControllerMap[encoders[i]].keys(); + for (int j = 0; j < formats.count(); ++j) { + if (m_videoControllerMap[encoders[i]][formats[j]].supportedMimeTypes.contains(containerName, Qt::CaseInsensitive)) + return m_videoControllerMap[encoders[i]][formats[j]].description; + } + } + + return QString(); +} + +void S60VideoCaptureSession::cameraStatusChanged(QCamera::Status status) +{ + if (status == QCamera::ActiveStatus) { + m_cameraStarted = true; + + // Continue preparation or start recording if previously requested + if (m_captureState == EInitialized + && (m_openWhenReady || m_prepareAfterOpenComplete || m_startAfterPrepareComplete)) { + setOutputLocation(m_requestedSink); + m_openWhenReady = false; // Reset + } else if ((m_captureState == EOpenComplete || m_captureState == EPrepared) + && (m_prepareAfterOpenComplete || m_startAfterPrepareComplete)) { + startRecording(); + m_prepareAfterOpenComplete = false; // Reset + } + + } else if (status == QCamera::UnloadedStatus) { + m_cameraStarted = false; + releaseVideoRecording(); + } else { + m_cameraStarted = false; + } +} + +void S60VideoCaptureSession::durationTimerTriggered() +{ + // Update position only if recording is ongoing + if ((m_captureState == ERecording) && m_videoRecorder) { + // Signal will be automatically emitted of position changes + TRAPD(err, m_position = m_videoRecorder->DurationL().Int64() / 1000); + setError(err, tr("Cannot retrieve video position.")); + + emit positionChanged(m_position); + } +} + +void S60VideoCaptureSession::doPopulateAudioCodecsL() +{ + if (m_captureState == EInitializing) { + m_audioCodecList.clear(); + + RArray audioTypes; + CleanupClosePushL(audioTypes); + + if (m_videoRecorder) + m_videoRecorder->GetSupportedAudioTypesL(audioTypes); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + for (TInt i = 0; i < audioTypes.Count(); i++) { + TUint32 codec = audioTypes[i].FourCC(); + + if (codec == KMMFFourCCCodeAMR) + m_audioCodecList.insert(QString("audio/amr"), KMMFFourCCCodeAMR); + if (codec == KMMFFourCCCodeAAC) + m_audioCodecList.insert(QString("audio/aac"), KMMFFourCCCodeAAC); + } + CleanupStack::PopAndDestroy(&audioTypes); + } +} + +void S60VideoCaptureSession::doPopulateVideoCodecsL() +{ + if (m_captureState == EInitializing) { + m_videoCodecList.clear(); + + CDesC8ArrayFlat* videoTypes = new (ELeave) CDesC8ArrayFlat(10); + CleanupStack::PushL(videoTypes); + + if (m_videoRecorder) + m_videoRecorder->GetSupportedVideoTypesL(*videoTypes); + else + setError(KErrNotReady, tr("Unexpected camera error.")); + + for (TInt i = 0; i < videoTypes->Count(); i++) { + TPtrC8 videoType = videoTypes->MdcaPoint(i); + QString codecMimeType = QString::fromUtf8((char *)videoType.Ptr(), videoType.Length()); +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED + for (int j = 0; j < m_videoParametersForEncoder.size(); ++j) { + if (m_videoParametersForEncoder[j].mimeTypes.contains(codecMimeType, Qt::CaseInsensitive)) { + m_videoCodecList << codecMimeType; + break; + } + } +#else // CVideoRecorderUtility + m_videoCodecList << codecMimeType; +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + } + CleanupStack::PopAndDestroy(videoTypes); + } +} + +#ifndef S60_DEVVIDEO_RECORDING_SUPPORTED +/* + * Maximum resolution, framerate and bitrate can not be queried via MMF or + * ECam, but needs to be set according to the definitions of the video + * standard in question. In video standards, the values often depend on each + * other, but the below function defines constant maximums. + */ +void S60VideoCaptureSession::doPopulateMaxVideoParameters() +{ + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); // For H.263 + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); // For MPEG-4 + m_videoParametersForEncoder.append(MaxResolutionRatesAndTypes()); // For H.264 + + for (int i = 0; i < m_videoCodecList.count(); ++i) { + + // Use all lower case for comparisons + QString codec = m_videoCodecList[i].toLower(); + + if (codec.contains("video/h263-2000", Qt::CaseInsensitive)) { + // H.263 + if (codec == "video/h263-2000" || + codec == "video/h263-2000; profile=0" || + codec == "video/h263-2000; profile=0; level=10" || + codec == "video/h263-2000; profile=3") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 64000) + m_videoParametersForEncoder[0].bitRate = 64000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=20") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=30") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 384000) + m_videoParametersForEncoder[0].bitRate = 384000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=40") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 2048000) + m_videoParametersForEncoder[0].bitRate = 2048000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=45") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/h263-2000; profile=0; level=50") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4096000) + m_videoParametersForEncoder[0].bitRate = 4096000; + continue; + } + + } else if (codec.contains("video/mp4v-es", Qt::CaseInsensitive)) { + // Mpeg-4 + if (codec == "video/mp4v-es" || + codec == "video/mp4v-es; profile-level-id=1" || + codec == "video/mp4v-es; profile-level-id=8") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 64000) + m_videoParametersForEncoder[0].bitRate = 64000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=2" || + codec == "video/mp4v-es; profile-level-id=9") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=3") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 384000) + m_videoParametersForEncoder[0].bitRate = 384000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=4") { +#if (defined(S60_31_PLATFORM) | defined(S60_32_PLATFORM)) + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(640,480))); +#else // S60 5.0 and later platforms + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(640,480))); +#endif + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4000000) + m_videoParametersForEncoder[0].bitRate = 4000000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=5") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(25.0, QSize(720,576))); + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(720,480))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 8000000) + m_videoParametersForEncoder[0].bitRate = 8000000; + continue; + } else if (codec == "video/mp4v-es; profile-level-id=6") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(1280,720))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 12000000) + m_videoParametersForEncoder[0].bitRate = 12000000; + continue; + } + + } else if (codec.contains("video/h264", Qt::CaseInsensitive)) { + // H.264 + if (codec == "video/h264" || + codec == "video/h264; profile-level-id=42800a") { + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 64000) + m_videoParametersForEncoder[0].bitRate = 64000; + continue; + } else if (codec == "video/h264; profile-level-id=42900b") { // BP, L1b + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(176,144))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 128000) + m_videoParametersForEncoder[0].bitRate = 128000; + continue; + } else if (codec == "video/h264; profile-level-id=42800b") { // BP, L1.1 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(7.5, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 192000) + m_videoParametersForEncoder[0].bitRate = 192000; + continue; + } else if (codec == "video/h264; profile-level-id=42800c") { // BP, L1.2 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(15.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 384000) + m_videoParametersForEncoder[0].bitRate = 384000; + continue; + } else if (codec == "video/h264; profile-level-id=42800d") { // BP, L1.3 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 768000) + m_videoParametersForEncoder[0].bitRate = 768000; + continue; + } else if (codec == "video/h264; profile-level-id=428014") { // BP, L2 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 2000000) + m_videoParametersForEncoder[0].bitRate = 2000000; + continue; + } else if (codec == "video/h264; profile-level-id=428015") { // BP, L2.1 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(50.0, QSize(352,288))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4000000) + m_videoParametersForEncoder[0].bitRate = 4000000; + continue; + } else if (codec == "video/h264; profile-level-id=428016") { // BP, L2.2 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(16.9, QSize(640,480))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 4000000) + m_videoParametersForEncoder[0].bitRate = 4000000; + continue; + } else if (codec == "video/h264; profile-level-id=42801e") { // BP, L3 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(33.8, QSize(640,480))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 10000000) + m_videoParametersForEncoder[0].bitRate = 10000000; + continue; + } else if (codec == "video/h264; profile-level-id=42801f") { // BP, L3.1 + m_videoParametersForEncoder[0].frameRatePictureSizePair.append(SupportedFrameRatePictureSize(30.0, QSize(1280,720))); + m_videoParametersForEncoder[0].mimeTypes.append(codec); + if (m_videoParametersForEncoder[0].bitRate < 14000000) + m_videoParametersForEncoder[0].bitRate = 14000000; + continue; + } + } + } +} +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +/* + * This function returns the maximum resolution defined by the video standards + * for different MIME Types. + */ +QSize S60VideoCaptureSession::maximumResolutionForMimeType(const QString &mimeType) const +{ + QSize maxSize(-1,-1); + // Use all lower case for comparisons + QString lowerMimeType = mimeType.toLower(); + + if (lowerMimeType == "video/h263-2000") { + maxSize = KResH263; + } else if (lowerMimeType == "video/h263-2000; profile=0") { + maxSize = KResH263_Profile0; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=10") { + maxSize = KResH263_Profile0_Level10; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=20") { + maxSize = KResH263_Profile0_Level20; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=30") { + maxSize = KResH263_Profile0_Level30; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=40") { + maxSize = KResH263_Profile0_Level40; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=45") { + maxSize = KResH263_Profile0_Level45; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=50") { + maxSize = KResH263_Profile0_Level50; + } else if (lowerMimeType == "video/h263-2000; profile=3") { + maxSize = KResH263_Profile3; + } else if (lowerMimeType == "video/mp4v-es") { + maxSize = KResMPEG4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=1") { + maxSize = KResMPEG4_PLID_1; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=2") { + maxSize = KResMPEG4_PLID_2; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=3") { + maxSize = KResMPEG4_PLID_3; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=4") { + maxSize = KResMPEG4_PLID_4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=5") { + maxSize = KResMPEG4_PLID_5; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=6") { + maxSize = KResMPEG4_PLID_6; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=8") { + maxSize = KResMPEG4_PLID_8; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=9") { + maxSize = KResMPEG4_PLID_9; + } else if (lowerMimeType == "video/h264") { + maxSize = KResH264; + } else if (lowerMimeType == "video/h264; profile-level-id=42800a" || + lowerMimeType == "video/h264; profile-level-id=4d400a" || + lowerMimeType == "video/h264; profile-level-id=64400a") { // L1 + maxSize = KResH264_PLID_42800A; + } else if (lowerMimeType == "video/h264; profile-level-id=42900b" || + lowerMimeType == "video/h264; profile-level-id=4d500b" || + lowerMimeType == "video/h264; profile-level-id=644009") { // L1.b + maxSize = KResH264_PLID_42900B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800b" || + lowerMimeType == "video/h264; profile-level-id=4d400b" || + lowerMimeType == "video/h264; profile-level-id=64400b") { // L1.1 + maxSize = KResH264_PLID_42800B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800c" || + lowerMimeType == "video/h264; profile-level-id=4d400c" || + lowerMimeType == "video/h264; profile-level-id=64400c") { // L1.2 + maxSize = KResH264_PLID_42800C; + } else if (lowerMimeType == "video/h264; profile-level-id=42800d" || + lowerMimeType == "video/h264; profile-level-id=4d400d" || + lowerMimeType == "video/h264; profile-level-id=64400d") { // L1.3 + maxSize = KResH264_PLID_42800D; + } else if (lowerMimeType == "video/h264; profile-level-id=428014" || + lowerMimeType == "video/h264; profile-level-id=4d4014" || + lowerMimeType == "video/h264; profile-level-id=644014") { // L2 + maxSize = KResH264_PLID_428014; + } else if (lowerMimeType == "video/h264; profile-level-id=428015" || + lowerMimeType == "video/h264; profile-level-id=4d4015" || + lowerMimeType == "video/h264; profile-level-id=644015") { // L2.1 + maxSize = KResH264_PLID_428015; + } else if (lowerMimeType == "video/h264; profile-level-id=428016" || + lowerMimeType == "video/h264; profile-level-id=4d4016" || + lowerMimeType == "video/h264; profile-level-id=644016") { // L2.2 + maxSize = KResH264_PLID_428016; + } else if (lowerMimeType == "video/h264; profile-level-id=42801e" || + lowerMimeType == "video/h264; profile-level-id=4d401e" || + lowerMimeType == "video/h264; profile-level-id=64401e") { // L3 + maxSize = KResH264_PLID_42801E; + } else if (lowerMimeType == "video/h264; profile-level-id=42801f" || + lowerMimeType == "video/h264; profile-level-id=4d401f" || + lowerMimeType == "video/h264; profile-level-id=64401f") { // L3.1 + maxSize = KResH264_PLID_42801F; + } else if (lowerMimeType == "video/h264; profile-level-id=428020" || + lowerMimeType == "video/h264; profile-level-id=4d4020" || + lowerMimeType == "video/h264; profile-level-id=644020") { // L3.2 + maxSize = KResH264_PLID_428020; + } else if (lowerMimeType == "video/h264; profile-level-id=428028" || + lowerMimeType == "video/h264; profile-level-id=4d4028" || + lowerMimeType == "video/h264; profile-level-id=644028") { // L4 + maxSize = KResH264_PLID_428028; + } + + return maxSize; +} + +/* + * This function returns the maximum framerate defined by the video standards + * for different MIME Types. + */ + +qreal S60VideoCaptureSession::maximumFrameRateForMimeType(const QString &mimeType) const +{ + qreal maxRate(-1.0); + // Use all lower case for comparisons + QString lowerMimeType = mimeType.toLower(); + + if (lowerMimeType == "video/h263-2000") { + maxRate = KFrR_H263; + } else if (lowerMimeType == "video/h263-2000; profile=0") { + maxRate = KFrR_H263_Profile0; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=10") { + maxRate = KFrR_H263_Profile0_Level10; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=20") { + maxRate = KFrR_H263_Profile0_Level20; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=30") { + maxRate = KFrR_H263_Profile0_Level30; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=40") { + maxRate = KFrR_H263_Profile0_Level40; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=45") { + maxRate = KFrR_H263_Profile0_Level45; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=50") { + maxRate = KFrR_H263_Profile0_Level50; + } else if (lowerMimeType == "video/h263-2000; profile=3") { + maxRate = KFrR_H263_Profile3; + } else if (lowerMimeType == "video/mp4v-es") { + maxRate = KFrR_MPEG4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=1") { + maxRate = KFrR_MPEG4_PLID_1; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=2") { + maxRate = KFrR_MPEG4_PLID_2; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=3") { + maxRate = KFrR_MPEG4_PLID_3; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=4") { + maxRate = KFrR_MPEG4_PLID_4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=5") { + maxRate = KFrR_MPEG4_PLID_5; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=6") { + maxRate = KFrR_MPEG4_PLID_6; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=8") { + maxRate = KFrR_MPEG4_PLID_8; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=9") { + maxRate = KFrR_MPEG4_PLID_9; + } else if (lowerMimeType == "video/h264") { + maxRate = KFrR_H264; + } else if (lowerMimeType == "video/h264; profile-level-id=42800a" || + lowerMimeType == "video/h264; profile-level-id=4d400a" || + lowerMimeType == "video/h264; profile-level-id=64400a") { // L1 + maxRate = KFrR_H264_PLID_42800A; + } else if (lowerMimeType == "video/h264; profile-level-id=42900b" || + lowerMimeType == "video/h264; profile-level-id=4d500b" || + lowerMimeType == "video/h264; profile-level-id=644009") { // L1.b + maxRate = KFrR_H264_PLID_42900B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800b" || + lowerMimeType == "video/h264; profile-level-id=4d400b" || + lowerMimeType == "video/h264; profile-level-id=64400b") { // L1.1 + maxRate = KFrR_H264_PLID_42800B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800c" || + lowerMimeType == "video/h264; profile-level-id=4d400c" || + lowerMimeType == "video/h264; profile-level-id=64400c") { // L1.2 + maxRate = KFrR_H264_PLID_42800C; + } else if (lowerMimeType == "video/h264; profile-level-id=42800d" || + lowerMimeType == "video/h264; profile-level-id=4d400d" || + lowerMimeType == "video/h264; profile-level-id=64400d") { // L1.3 + maxRate = KFrR_H264_PLID_42800D; + } else if (lowerMimeType == "video/h264; profile-level-id=428014" || + lowerMimeType == "video/h264; profile-level-id=4d4014" || + lowerMimeType == "video/h264; profile-level-id=644014") { // L2 + maxRate = KFrR_H264_PLID_428014; + } else if (lowerMimeType == "video/h264; profile-level-id=428015" || + lowerMimeType == "video/h264; profile-level-id=4d4015" || + lowerMimeType == "video/h264; profile-level-id=644015") { // L2.1 + maxRate = KFrR_H264_PLID_428015; + } else if (lowerMimeType == "video/h264; profile-level-id=428016" || + lowerMimeType == "video/h264; profile-level-id=4d4016" || + lowerMimeType == "video/h264; profile-level-id=644016") { // L2.2 + maxRate = KFrR_H264_PLID_428016; + } else if (lowerMimeType == "video/h264; profile-level-id=42801e" || + lowerMimeType == "video/h264; profile-level-id=4d401e" || + lowerMimeType == "video/h264; profile-level-id=64401e") { // L3 + maxRate = KFrR_H264_PLID_42801E; + } else if (lowerMimeType == "video/h264; profile-level-id=42801f" || + lowerMimeType == "video/h264; profile-level-id=4d401f" || + lowerMimeType == "video/h264; profile-level-id=64401f") { // L3.1 + maxRate = KFrR_H264_PLID_42801F; + } else if (lowerMimeType == "video/h264; profile-level-id=428020" || + lowerMimeType == "video/h264; profile-level-id=4d4020" || + lowerMimeType == "video/h264; profile-level-id=644020") { // L3.2 + maxRate = KFrR_H264_PLID_428020; + } else if (lowerMimeType == "video/h264; profile-level-id=428028" || + lowerMimeType == "video/h264; profile-level-id=4d4028" || + lowerMimeType == "video/h264; profile-level-id=644028") { // L4 + maxRate = KFrR_H264_PLID_428028; + } + + return maxRate; +} + +/* + * This function returns the maximum bitrate defined by the video standards + * for different MIME Types. + */ +int S60VideoCaptureSession::maximumBitRateForMimeType(const QString &mimeType) const +{ + int maxRate(-1.0); + // Use all lower case for comparisons + QString lowerMimeType = mimeType.toLower(); + + if (lowerMimeType == "video/h263-2000") { + maxRate = KBiR_H263; + } else if (lowerMimeType == "video/h263-2000; profile=0") { + maxRate = KBiR_H263_Profile0; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=10") { + maxRate = KBiR_H263_Profile0_Level10; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=20") { + maxRate = KBiR_H263_Profile0_Level20; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=30") { + maxRate = KBiR_H263_Profile0_Level30; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=40") { + maxRate = KBiR_H263_Profile0_Level40; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=45") { + maxRate = KBiR_H263_Profile0_Level45; + } else if (lowerMimeType == "video/h263-2000; profile=0; level=50") { + maxRate = KBiR_H263_Profile0_Level50; + } else if (lowerMimeType == "video/h263-2000; profile=3") { + maxRate = KBiR_H263_Profile3; + } else if (lowerMimeType == "video/mp4v-es") { + maxRate = KBiR_MPEG4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=1") { + maxRate = KBiR_MPEG4_PLID_1; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=2") { + maxRate = KBiR_MPEG4_PLID_2; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=3") { + maxRate = KBiR_MPEG4_PLID_3; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=4") { + maxRate = KBiR_MPEG4_PLID_4; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=5") { + maxRate = KBiR_MPEG4_PLID_5; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=6") { + maxRate = KBiR_MPEG4_PLID_6; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=8") { + maxRate = KBiR_MPEG4_PLID_8; + } else if (lowerMimeType == "video/mp4v-es; profile-level-id=9") { + maxRate = KBiR_MPEG4_PLID_9; + } else if (lowerMimeType == "video/h264") { + maxRate = KBiR_H264; + } else if (lowerMimeType == "video/h264; profile-level-id=42800a" || + lowerMimeType == "video/h264; profile-level-id=4d400a" || + lowerMimeType == "video/h264; profile-level-id=64400a") { // L1 + maxRate = KBiR_H264_PLID_42800A; + } else if (lowerMimeType == "video/h264; profile-level-id=42900b" || + lowerMimeType == "video/h264; profile-level-id=4d500b" || + lowerMimeType == "video/h264; profile-level-id=644009") { // L1.b + maxRate = KBiR_H264_PLID_42900B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800b" || + lowerMimeType == "video/h264; profile-level-id=4d400b" || + lowerMimeType == "video/h264; profile-level-id=64400b") { // L1.1 + maxRate = KBiR_H264_PLID_42800B; + } else if (lowerMimeType == "video/h264; profile-level-id=42800c" || + lowerMimeType == "video/h264; profile-level-id=4d400c" || + lowerMimeType == "video/h264; profile-level-id=64400c") { // L1.2 + maxRate = KBiR_H264_PLID_42800C; + } else if (lowerMimeType == "video/h264; profile-level-id=42800d" || + lowerMimeType == "video/h264; profile-level-id=4d400d" || + lowerMimeType == "video/h264; profile-level-id=64400d") { // L1.3 + maxRate = KBiR_H264_PLID_42800D; + } else if (lowerMimeType == "video/h264; profile-level-id=428014" || + lowerMimeType == "video/h264; profile-level-id=4d4014" || + lowerMimeType == "video/h264; profile-level-id=644014") { // L2 + maxRate = KBiR_H264_PLID_428014; + } else if (lowerMimeType == "video/h264; profile-level-id=428015" || + lowerMimeType == "video/h264; profile-level-id=4d4015" || + lowerMimeType == "video/h264; profile-level-id=644015") { // L2.1 + maxRate = KBiR_H264_PLID_428015; + } else if (lowerMimeType == "video/h264; profile-level-id=428016" || + lowerMimeType == "video/h264; profile-level-id=4d4016" || + lowerMimeType == "video/h264; profile-level-id=644016") { // L2.2 + maxRate = KBiR_H264_PLID_428016; + } else if (lowerMimeType == "video/h264; profile-level-id=42801e" || + lowerMimeType == "video/h264; profile-level-id=4d401e" || + lowerMimeType == "video/h264; profile-level-id=64401e") { // L3 + maxRate = KBiR_H264_PLID_42801E; + } else if (lowerMimeType == "video/h264; profile-level-id=42801f" || + lowerMimeType == "video/h264; profile-level-id=4d401f" || + lowerMimeType == "video/h264; profile-level-id=64401f") { // L3.1 + maxRate = KBiR_H264_PLID_42801F; + } else if (lowerMimeType == "video/h264; profile-level-id=428020" || + lowerMimeType == "video/h264; profile-level-id=4d4020" || + lowerMimeType == "video/h264; profile-level-id=644020") { // L3.2 + maxRate = KBiR_H264_PLID_428020; + } else if (lowerMimeType == "video/h264; profile-level-id=428028" || + lowerMimeType == "video/h264; profile-level-id=4d4028" || + lowerMimeType == "video/h264; profile-level-id=644028") { // L4 + maxRate = KBiR_H264_PLID_428028; + } + + return maxRate; +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videocapturesession.h b/src/plugins/symbian/ecam/s60videocapturesession.h new file mode 100644 index 000000000..cfa101f57 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videocapturesession.h @@ -0,0 +1,414 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOCAPTURESESSION_H +#define S60VIDEOCAPTURESESSION_H + +#include +#include + +#include +#include +#include + +#include "s60cameraengine.h" + +#include +#include // CVideoRecorderUtility +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +#include +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +QT_USE_NAMESPACE + +class QTimer; + +/* + * VideoSession is the main class handling all video recording related + * operations. It uses mainly CVideoRecorderUtility to do it's tasks, but if + * DevVideoRecord is available it is used to provide more detailed + * information of the supported video settings. + */ +class S60VideoCaptureSession : public QObject, + public MVideoRecorderUtilityObserver +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED + ,public MMMFDevVideoRecordObserver +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED +{ + Q_OBJECT + Q_ENUMS(Error) + Q_ENUMS(EcamErrors) + Q_ENUMS(TVideoCaptureState) + +public: // Enums + + enum TVideoCaptureState + { + ENotInitialized = 0, // 0 - VideoRecording is not initialized, instance may or may not be created + EInitializing, // 1 - Initialization is ongoing + EInitialized, // 2 - VideoRecording is initialized, OpenFile is called with dummy file + EOpening, // 3 - OpenFile called with actual output location, waiting completion + EOpenComplete, // 4 - OpenFile completed with the actual output location + EPreparing, // 5 - Preparing VideoRecording to use set video settings + EPrepared, // 6 - VideoRecording is prepared with the set settings, ready to record + ERecording, // 7 - Video recording is ongoing + EPaused // 8 - Video recording has been started and paused + }; + + enum AudioQualityDefinition + { + ENoAudioQuality = 0, // 0 - Both BitRate and SampleRate settings available + EOnlyAudioQuality, // 1 - No BitRate or SampleRate settings available, use Quality to set them + EAudioQualityAndBitRate, // 2 - BitRate setting available, use Quality to set SampleRate + EAudioQualityAndSampleRate, // 3 - SampleRate setting available, use Quality to set BitRate + }; + + enum VideoQualityDefinition + { + ENoVideoQuality = 0, // 0 - All, Resolution, FrameRate and BitRate available + EOnlyVideoQuality, // 1 - None available, use Quality to set Resolution, FrameRate and BitRate + EVideoQualityAndResolution, // 2 - Only Resolution available, use Quality to set FrameRate and BitRate + EVideoQualityAndFrameRate, // 3 - Only FrameRate available, use Quality to set Resolution and BitRate + EVideoQualityAndBitRate, // 4 - Only BitRate available, use Quality to set Resolution and FrameRate + EVideoQualityAndResolutionAndBitRate, // 5 - No FrameRate available, use Quality to set it + EVideoQualityAndResolutionAndFrameRate, // 6 - No BitRate available, use Quality to set it + EVideoQualityAndFrameRateAndBitRate // 7 - No Resolution available, use Quality to set it + }; + +public: // Constructor & Destructor + + S60VideoCaptureSession(QObject *parent = 0); + ~S60VideoCaptureSession(); + +public: // MVideoRecorderUtilityObserver + + void MvruoOpenComplete(TInt aError); + void MvruoPrepareComplete(TInt aError); + void MvruoRecordComplete(TInt aError); + void MvruoEvent(const TMMFEvent& aEvent); + +#ifdef S60_DEVVIDEO_RECORDING_SUPPORTED +public: // MMMFDevVideoRecordObserver + void MdvroReturnPicture(TVideoPicture *aPicture); + void MdvroSupplementalInfoSent(); + void MdvroNewBuffers(); + void MdvroFatalError(TInt aError); + void MdvroInitializeComplete(TInt aError); + void MdvroStreamEnd(); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + +public: // Methods + + void setError(const TInt error, const QString &description); + void setCameraHandle(CCameraEngine* cameraHandle); + void notifySettingsSet(); + + qint64 position(); + TVideoCaptureState state() const; + bool isMuted() const; + + // Controls + int initializeVideoRecording(); + void releaseVideoRecording(); + void applyAllSettings(); + + void startRecording(); + void pauseRecording(); + void stopRecording(const bool reInitialize = true); + void setMuted(const bool muted); + + // Output Location + bool setOutputLocation(const QUrl &sink); + QUrl outputLocation() const; + + // Resolution + void setVideoResolution(const QSize &resolution); + QList supportedVideoResolutions(bool *continuous); + QList supportedVideoResolutions(const QVideoEncoderSettings &settings, bool *continuous); + + // Framerate + void setFrameRate(const qreal rate); + QList supportedVideoFrameRates(bool *continuous); + QList supportedVideoFrameRates(const QVideoEncoderSettings &settings, bool *continuous); + + // Other Video Settings + void setBitrate(const int bitrate); + void setVideoEncodingMode(const QtMultimediaKit::EncodingMode mode); + + // Video Codecs + void setVideoCaptureCodec(const QString &codecName); + QStringList supportedVideoCaptureCodecs(); + QString videoCaptureCodecDescription(const QString &codecName); + + // Audio Codecs + void setAudioCaptureCodec(const QString &codecName); + QStringList supportedAudioCaptureCodecs(); + + // Encoder Settings + void videoEncoderSettings(QVideoEncoderSettings &videoSettings); + void audioEncoderSettings(QAudioEncoderSettings &audioSettings); + + // Quality + void setVideoCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const VideoQualityDefinition mode); + void setAudioCaptureQuality(const QtMultimediaKit::EncodingQuality quality, + const AudioQualityDefinition mode); + + // Video Containers + QString videoContainer() const; + void setVideoContainer(const QString &containerName); + QStringList supportedVideoContainers(); + bool isSupportedVideoContainer(const QString &containerName); + QString videoContainerDescription(const QString &containerName); + + // Audio Settings + QList supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous); + void setAudioSampleRate(const int sampleRate); + void setAudioBitRate(const int bitRate); + void setAudioChannelCount(const int channelCount); + void setAudioEncodingMode(const QtMultimediaKit::EncodingMode mode); + + // Video Options + QSize pixelAspectRatio(); + void setPixelAspectRatio(const QSize par); + int gain(); + void setGain(const int gain); + int maxClipSizeInBytes() const; + void setMaxClipSizeInBytes(const int size); + +private: // Internal + + QMediaRecorder::Error fromSymbianErrorToQtMultimediaError(int aError); + + void initializeVideoCaptureSettings(); + void doInitializeVideoRecorderL(); + void commitVideoEncoderSettings(); + void queryAudioEncoderSettings(); + void queryVideoEncoderSettings(); + void validateRequestedCodecs(); + void resetSession(bool errorHandling = false); + + void doSetCodecsL(); + QString determineProfileAndLevel(); + void doSetVideoResolution(const QSize &resolution); + void doSetFrameRate(qreal rate); + void doSetBitrate(const int &bitrate); + + void updateVideoCaptureContainers(); + void doUpdateVideoCaptureContainersL(); + void selectController(const QString &format, + TUid &controllerUid, + TUid &formatUid); + + void doPopulateVideoCodecsDataL(); + void doPopulateVideoCodecsL(); +#ifndef S60_DEVVIDEO_RECORDING_SUPPORTED + void doPopulateMaxVideoParameters(); +#endif // S60_DEVVIDEO_RECORDING_SUPPORTED + void doPopulateAudioCodecsL(); + + QList doGetSupportedSampleRatesL(const QAudioEncoderSettings &settings, + bool *continuous); + QSize maximumResolutionForMimeType(const QString &mimeType) const; + qreal maximumFrameRateForMimeType(const QString &mimeType) const; + int maximumBitRateForMimeType(const QString &mimeType) const; + +signals: // Notification Signals + + void stateChanged(S60VideoCaptureSession::TVideoCaptureState); + void positionChanged(qint64); + void mutedChanged(bool); + void captureSizeChanged(const QSize&); + void error(int, const QString&); + +private slots: // Internal Slots + + void cameraStatusChanged(QCamera::Status); + void durationTimerTriggered(); + +private: // Structs + + /* + * This structure holds the information of supported video mime types for + * the format and also description for it. + */ + struct VideoFormatData { + QString description; + QStringList supportedMimeTypes; + }; + + /* + * This structure is used to define supported resolutions and framerate + * (depending on each other) for each supported encoder mime type (defining + * encoder, profile and level) + */ + struct SupportedFrameRatePictureSize { + SupportedFrameRatePictureSize() {} + SupportedFrameRatePictureSize(qreal rate, QSize size): + frameRate(rate), + frameSize(size) {} + qreal frameRate; + QSize frameSize; + }; + + /* + * This structure defines supported resolution/framerate pairs and maximum + * bitrate for a single encodec device. It also the supported mime types + * (codec, profile and level) of the encoder device. + * + * Structure defines 2 contructors: + * - First with no attributes + * - Second, which will construct the sructure appending one + * resolution/framerate pair to the list of + * SupportedFrameRatePictureSizes and setting the given bitrate as + * maximum. This second constructor is for convenience. + * + * This struct is used in m_videoParametersForEncoder (QList). + * + * Here's a visualization of an example strcuture: + * STRUCT: + * |-- Resolution/FrameRate Pairs: + * | |- VGA / 30fps + * | |- 720p / 25fps + * | |- Etc. + * | + * |-- MimeTypes: + * | |- video/mp4v-es; profile-level-id=1 + * | |- video/mp4v-es; profile-level-id=2 + * | |- Etc. + * | + * |-- Max BitRate: 1Mbps + */ + struct MaxResolutionRatesAndTypes { + MaxResolutionRatesAndTypes() {} + MaxResolutionRatesAndTypes(QSize size, qreal fRate, int bRate): + bitRate(bRate) + { + frameRatePictureSizePair.append(SupportedFrameRatePictureSize(fRate,size)); + } + QList frameRatePictureSizePair; + QStringList mimeTypes; + int bitRate; + }; + +private: // Data + + CCameraEngine *m_cameraEngine; + CVideoRecorderUtility *m_videoRecorder; + QTimer *m_durationTimer; + qint64 m_position; + // Symbian ErrorCode + mutable int m_error; + // This defines whether Camera is in ActiveStatus or not + bool m_cameraStarted; + // Internal state of the video recorder + TVideoCaptureState m_captureState; + // Actual output file name/path + QUrl m_sink; + // Requested output file name/path, this may be different from m_sink if + // asynchronous operation was ongoing in the CVideoRecorderUtility when new + // outputLocation was set. + QUrl m_requestedSink; + // Requested videoSettings. The may not be active settings before those are + // committed (with commitVideoEncoderSettings()) + QVideoEncoderSettings m_videoSettings; + // Requested audioSettings. The may not be active settings before those are + // committed (with commitVideoEncoderSettings()) + QAudioEncoderSettings m_audioSettings; + // Tells whether settings should be initialized when changing the camera + bool m_captureSettingsSet; + // Active container + QString m_container; + // Requested container, this may be different from m_container if + // asynchronous operation was ongoing in the CVideoRecorderUtility when new + // container was set. + QString m_requestedContainer; + // Requested muted value. This may not be active value before settings are + // committed (with commitVideoEncoderSettings()) + bool m_muted; + // Maximum ClipSize in Bytes + int m_maxClipSize; + // List of supported video codec mime types + QStringList m_videoCodecList; + // Hash of supported video codec mime types and corresponding FourCC codes + QHash m_audioCodecList; + // Map of video capture controllers information. It is populated during + // doUpdateVideoCaptureContainersL(). + // + // Here's a visualization of an example strcuture: + // m_videoControllerMap(HASH): + // | + // |-- Controller 1 : HASH + // | |- Container 1 (UID) : FormatData + // | | |- Description + // | | |- List of supported MimeTypes + // | |- Container 2 (UID) : FormatData + // | | |- Description + // | | |- List of supported MimeTypes + // | |- Etc. + // | + // |-- Controller 2: HASH + // | |- Container 1 (UID) : FormatData + // | | |- Description + // | | |- List of supported MimeTypes + // | |- Etc. + // + QHash > m_videoControllerMap; + // List of Encoder information. If DevVideoRecord is available info is + // gathered during doPopulateVideoCodecsDataL() for each encoder (hw + // accelerated and supporting camera input) found. If DevVideoRecord is not + // available, the info is set in doPopulateMaxVideoParameters() based on + // supported codec list received from CVideoRecorderUtility. + QList m_videoParametersForEncoder; + // Set if OpenFileL should be executed when currently ongoing operation + // is completed. + bool m_openWhenReady; + // Set if video capture should be prepared after OpenFileL has completed + bool m_prepareAfterOpenComplete; + // Set if video capture should be started when Prepare has completed + bool m_startAfterPrepareComplete; + // Tells if settings have been set after last Prepare() + bool m_uncommittedSettings; + // Tells if settings need to be applied after ongoing operation has finished + bool m_commitSettingsWhenReady; +}; + +#endif // S60VIDEOCAPTURESESSION_H diff --git a/src/plugins/symbian/ecam/s60videodevicecontrol.cpp b/src/plugins/symbian/ecam/s60videodevicecontrol.cpp new file mode 100644 index 000000000..3d55d3110 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videodevicecontrol.cpp @@ -0,0 +1,108 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "s60videodevicecontrol.h" +#include "s60cameracontrol.h" +#include "s60cameraconstants.h" + +S60VideoDeviceControl::S60VideoDeviceControl(QObject *parent) : + QVideoDeviceControl(parent) +{ +} + +S60VideoDeviceControl::S60VideoDeviceControl(S60CameraControl *control, QObject *parent) : + QVideoDeviceControl(parent), + m_selectedDevice(KDefaultCameraDevice) +{ + m_control = control; + connect(m_control, SIGNAL(devicesChanged()), this, SIGNAL(devicesChanged())); +} + +S60VideoDeviceControl::~S60VideoDeviceControl() +{ +} + +int S60VideoDeviceControl::deviceCount() const +{ + return S60CameraControl::deviceCount(); +} + +QString S60VideoDeviceControl::deviceName(int index) const +{ + return S60CameraControl::name(index); +} + +QString S60VideoDeviceControl::deviceDescription(int index) const +{ + return S60CameraControl::description(index); +} + +QIcon S60VideoDeviceControl::deviceIcon(int index) const +{ + Q_UNUSED(index); + return QIcon(); +} + +int S60VideoDeviceControl::defaultDevice() const +{ + return KDefaultCameraDevice; +} + +int S60VideoDeviceControl::selectedDevice() const +{ + return m_selectedDevice; +} + +void S60VideoDeviceControl::setSelectedDevice(int index) +{ + // Inform that we selected new device + if (m_selectedDevice != index) { + m_control->setSelectedDevice(index); + m_selectedDevice = index; + emit selectedDeviceChanged(m_selectedDevice); + emit selectedDeviceChanged(deviceName(m_selectedDevice)); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videodevicecontrol.h b/src/plugins/symbian/ecam/s60videodevicecontrol.h new file mode 100644 index 000000000..03249441a --- /dev/null +++ b/src/plugins/symbian/ecam/s60videodevicecontrol.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEODEVICECONTROL_H +#define S60VIDEODEVICECONTROL_H + +#include "qvideodevicecontrol.h" + +QT_USE_NAMESPACE + +class S60CameraControl; +class QString; +class QIcon; + +/* + * Control for providing information of the video device (r. camera) and to + * enable other camera device (e.g. secondary camera if one exists). + */ +class S60VideoDeviceControl : public QVideoDeviceControl +{ + Q_OBJECT + +public: // Constructors & Destructor + + S60VideoDeviceControl(QObject *parent); + S60VideoDeviceControl(S60CameraControl *control, QObject *parent = 0); + virtual ~S60VideoDeviceControl(); + +public: // QVideoDeviceControl + + int deviceCount() const; + + QString deviceName(int index) const; + QString deviceDescription(int index) const; + QIcon deviceIcon(int index) const; + + int defaultDevice() const; + int selectedDevice() const; + +public slots: // QVideoDeviceControl + + void setSelectedDevice(int index); + +/* +Q_SIGNALS: +void selectedDeviceChanged(int index); +void selectedDeviceChanged(const QString &deviceName); +void devicesChanged(); +*/ + +private: // Data + + S60CameraControl *m_control; + int m_selectedDevice; +}; + +#endif // S60VIDEODEVICECONTROL_H diff --git a/src/plugins/symbian/ecam/s60videoencodercontrol.cpp b/src/plugins/symbian/ecam/s60videoencodercontrol.cpp new file mode 100644 index 000000000..34b28e28e --- /dev/null +++ b/src/plugins/symbian/ecam/s60videoencodercontrol.cpp @@ -0,0 +1,204 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videoencodercontrol.h" +#include "s60videocapturesession.h" + +S60VideoEncoderControl::S60VideoEncoderControl(QObject *parent) : + QVideoEncoderControl(parent) +{ +} + +S60VideoEncoderControl::S60VideoEncoderControl(S60VideoCaptureSession *session, QObject *parent) : + QVideoEncoderControl(parent) +{ + m_session = session; +} + +S60VideoEncoderControl::~S60VideoEncoderControl() +{ +} + +QStringList S60VideoEncoderControl::supportedVideoCodecs() const +{ + return m_session->supportedVideoCaptureCodecs(); +} + +QString S60VideoEncoderControl::videoCodecDescription(const QString &codecName) const +{ + return m_session->videoCaptureCodecDescription(codecName); +} + +QList S60VideoEncoderControl::supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous) const +{ + if (!settings.isNull()) + return m_session->supportedVideoFrameRates(settings, continuous); + return m_session->supportedVideoFrameRates(continuous); +} + +QList S60VideoEncoderControl::supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous) const +{ + if (!settings.isNull()) + return m_session->supportedVideoResolutions(settings, continuous); + return m_session->supportedVideoResolutions(continuous); +} + +QStringList S60VideoEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + // Possible settings: EncodingMode, Codec, Resolution, FrameRate, BitRate, Quality + // Possible (codec specific) options: PixelAspectRatio, Gain, MaxClipSizeInBytes + + // Following options are valid for all codecs + Q_UNUSED(codec); + + QStringList options; + options.append("pixelAspectRatio"); + options.append("gain"); + options.append("maxClipSizeInBytes"); + + return options; +} + +QVariant S60VideoEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + Q_UNUSED(codec); + + // Possible settings: EncodingMode, Codec, Resolution, FrameRate, BitRate, Quality + // Possible (codec specific) options: PixelAspectRatio, Gain, MaxClipSizeInBytes + + QVariant returnValue; + + if (qstrcmp(name.toLocal8Bit().constData(), "pixelAspectRatio") == 0) + returnValue.setValue(m_session->pixelAspectRatio()); + else if (qstrcmp(name.toLocal8Bit().constData(), "gain") == 0) + returnValue.setValue((int)m_session->gain()); + else if (qstrcmp(name.toLocal8Bit().constData(), "maxClipSizeInBytes") == 0) + returnValue.setValue(m_session->maxClipSizeInBytes()); + + return returnValue; +} + +void S60VideoEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + // Set the codec first if not already set + m_session->setVideoCaptureCodec(codec); + + if (qstrcmp(name.toLocal8Bit().constData(), "pixelAspectRatio") == 0) + m_session->setPixelAspectRatio(value.toSize()); + else if (qstrcmp(name.toLocal8Bit().constData(), "gain") == 0) + m_session->setGain(value.toInt()); + else if (qstrcmp(name.toLocal8Bit().constData(), "maxClipSizeInBytes") == 0) + m_session->setMaxClipSizeInBytes(value.toInt()); + else + m_session->setError(KErrNotSupported, tr("Requested encoding option is not supported")); +} + +QVideoEncoderSettings S60VideoEncoderControl::videoSettings() const +{ + QVideoEncoderSettings settings; + m_session->videoEncoderSettings(settings); + + return settings; +} + +void S60VideoEncoderControl::setVideoSettings(const QVideoEncoderSettings &settings) +{ + // Notify that settings have been implicitly set and there's no need to + // initialize them in case camera is changed + m_session->notifySettingsSet(); + + if (settings.codec().isEmpty() + || (settings.resolution() == QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() == -1)) { + if (!settings.codec().isEmpty()) + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EOnlyVideoQuality); + } else if (settings.resolution() != QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() == -1) { // Only Resolution + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndResolution); + + } else if (settings.resolution() == QSize(-1,-1) && settings.frameRate() != 0 && settings.bitRate() == -1) { // Only Framerate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setFrameRate(settings.frameRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndFrameRate); + + } else if (settings.resolution() == QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() != -1) { // Only BitRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndBitRate); + + } else if (settings.resolution() != QSize(-1,-1) && settings.frameRate() != 0 && settings.bitRate() == -1) { // Resolution and FrameRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setFrameRate(settings.frameRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndResolutionAndFrameRate); + + } else if (settings.resolution() != QSize(-1,-1) && settings.frameRate() == 0 && settings.bitRate() != -1) { // Resolution and BitRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndResolutionAndBitRate); + + } else if (settings.resolution() == QSize(-1,-1) && settings.frameRate() != 0 && settings.bitRate() != -1) { // FrameRate and BitRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setFrameRate(settings.frameRate()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::EVideoQualityAndFrameRateAndBitRate); + + } else { // All: Resolution, BitRate and FrameRate + m_session->setVideoCaptureCodec(settings.codec()); + m_session->setVideoEncodingMode(settings.encodingMode()); + m_session->setVideoResolution(settings.resolution()); + m_session->setFrameRate(settings.frameRate()); + m_session->setBitrate(settings.bitRate()); + m_session->setVideoCaptureQuality(settings.quality(), S60VideoCaptureSession::ENoVideoQuality); + } +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videoencodercontrol.h b/src/plugins/symbian/ecam/s60videoencodercontrol.h new file mode 100644 index 000000000..4554d9291 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videoencodercontrol.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOENCODERCONTROL_H +#define S60VIDEOENCODERCONTROL_H + +#include +#include + +#include "qvideoencodercontrol.h" + +QT_USE_NAMESPACE + +class S60VideoCaptureSession; + +/* + * Control for video settings when recording video using QMediaRecorder. + */ +class S60VideoEncoderControl : public QVideoEncoderControl +{ + Q_OBJECT + +public: // Contructors & Destructor + + S60VideoEncoderControl(QObject *parent = 0); + S60VideoEncoderControl(S60VideoCaptureSession *session, QObject *parent = 0); + virtual ~S60VideoEncoderControl(); + +public: // QVideoEncoderControl + + // Resolution + QList supportedResolutions(const QVideoEncoderSettings &settings, bool *continuous = 0) const; + + // Framerate + QList supportedFrameRates(const QVideoEncoderSettings &settings, bool *continuous = 0) const; + + // Video Codec + QStringList supportedVideoCodecs() const; + QString videoCodecDescription(const QString &codecName) const; + + // Video Settings + QVideoEncoderSettings videoSettings() const; + void setVideoSettings(const QVideoEncoderSettings &settings); + + // Encoding Options + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: // Data + + S60VideoCaptureSession* m_session; + +}; + +#endif // S60VIDEOENCODERCONTROL_H diff --git a/src/plugins/symbian/ecam/s60videorenderercontrol.cpp b/src/plugins/symbian/ecam/s60videorenderercontrol.cpp new file mode 100644 index 000000000..8cc3546b5 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videorenderercontrol.cpp @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "s60videorenderercontrol.h" + +S60VideoRendererControl::S60VideoRendererControl(QObject *parent) : + QVideoRendererControl(parent), + m_surface(0) +{ +} + +S60VideoRendererControl::~S60VideoRendererControl() +{ + // Stop surface if still active + if (m_surface) + if (m_surface->isActive()) + m_surface->stop(); +} + +QAbstractVideoSurface *S60VideoRendererControl::surface() const +{ + return m_surface; +} + +void S60VideoRendererControl::setSurface(QAbstractVideoSurface *surface) +{ + if (surface == 0) { + // Stop current surface if needed + if (m_surface) + if (m_surface->isActive()) + m_surface->stop(); + } + + m_surface = surface; + emit viewFinderSurfaceSet(); +} + +// End of file diff --git a/src/plugins/symbian/ecam/s60videorenderercontrol.h b/src/plugins/symbian/ecam/s60videorenderercontrol.h new file mode 100644 index 000000000..b35433f86 --- /dev/null +++ b/src/plugins/symbian/ecam/s60videorenderercontrol.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEORENDERERCONTROL_H +#define S60VIDEORENDERERCONTROL_H + +#include + +/* + * Control for QGraphicsVideoItem. Viewfinder frames are streamed to a surface + * which is drawn to the display by the Qt Graphics Vide Framework. + * VideoRendererControl uses only Bitmap Viewfinder. + */ +class S60VideoRendererControl : public QVideoRendererControl +{ + Q_OBJECT + +public: // Constructor & Destructor + + S60VideoRendererControl(QObject *parent = 0); + virtual ~S60VideoRendererControl(); + +public: // S60VideoRendererControl + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + +signals: // Internal Signals + + void viewFinderSurfaceSet(); + +private: // Data + + QAbstractVideoSurface *m_surface; + +}; + +#endif // S60VIDEORENDERERCONTROL_H diff --git a/src/plugins/symbian/mmf/audiosource/audiosource_s60.pri b/src/plugins/symbian/mmf/audiosource/audiosource_s60.pri new file mode 100644 index 000000000..7732600fa --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/audiosource_s60.pri @@ -0,0 +1,31 @@ +INCLUDEPATH += $$PWD + +DEFINES += AUDIOSOURCEUSED + +symbian:LIBS += -lmediaclientaudio \ + -lmmfcontrollerframework \ + -lefsrv \ + -lbafl \ + +!contains(S60_VERSION, 3.1) { + contains(audiorouting_s60_enabled,yes) { + #We use audioinputrouting.lib for recording audio from different sources -lmediaclientaudioinputstream \ -lcone \ + DEFINES += AUDIOINPUT_ROUTING + message("Audio Input Routing enabled onwards 3.2 SDK") + LIBS += -laudioinputrouting + } +} + +HEADERS += $$PWD/s60audioencodercontrol.h \ + $$PWD/s60audiomediarecordercontrol.h \ + $$PWD/s60audioendpointselector.h \ + $$PWD/s60audiocaptureservice.h \ + $$PWD/s60audiocapturesession.h \ + $$PWD/S60audiocontainercontrol.h + +SOURCES += $$PWD/s60audioencodercontrol.cpp \ + $$PWD/s60audiomediarecordercontrol.cpp \ + $$PWD/s60audioendpointselector.cpp \ + $$PWD/s60audiocaptureservice.cpp \ + $$PWD/s60audiocapturesession.cpp \ + $$PWD/S60audiocontainercontrol.cpp diff --git a/src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.cpp b/src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.cpp new file mode 100644 index 000000000..742ac8f82 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" +#include + +#include "s60audiocaptureservice.h" +#include "s60audiocapturesession.h" +#include "s60audioendpointselector.h" +#include "s60audioencodercontrol.h" +#include "s60audiomediarecordercontrol.h" +#include "s60audiocontainercontrol.h" + +S60AudioCaptureService::S60AudioCaptureService(QObject *parent): + QMediaService(parent) +{ + DP0("S60AudioCaptureService::S60AudioCaptureService +++"); + + m_session = new S60AudioCaptureSession(this); + m_encoderControl = new S60AudioEncoderControl(m_session,this); + m_recorderControl = new S60AudioMediaRecorderControl(m_session,this); + m_endpointSelector = new S60AudioEndpointSelector(m_session,this); + m_containerControl = new S60AudioContainerControl(m_session, this); + + DP0("S60AudioCaptureService::S60AudioCaptureService ---"); +} + +S60AudioCaptureService::~S60AudioCaptureService() +{ + DP0("S60AudioCaptureService::~S60AudioCaptureService +++"); + DP0("S60AudioCaptureService::~S60AudioCaptureService ---"); +} + +QMediaControl *S60AudioCaptureService::requestControl(const char *name) +{ + DP0("S60AudioCaptureService::requestControl"); + + if (qstrcmp(name,QMediaRecorderControl_iid) == 0) + return m_recorderControl; + + if (qstrcmp(name,QAudioEncoderControl_iid) == 0) + return m_encoderControl; + + if (qstrcmp(name,QAudioEndpointSelector_iid) == 0) + return m_endpointSelector; + + if (qstrcmp(name,QMediaContainerControl_iid) == 0) + return m_containerControl; + + return 0; +} + +void S60AudioCaptureService::releaseControl(QMediaControl *control) +{ + DP0("S60AudioCaptureService::releaseControl +++"); + + Q_UNUSED(control) + + DP0("S60AudioCaptureService::releaseControl ---"); +} diff --git a/src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.h b/src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.h new file mode 100644 index 000000000..86d71a994 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiocaptureservice.h @@ -0,0 +1,75 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOCAPTURESERVICE_H +#define S60AUDIOCAPTURESERVICE_H + +#include + +#include + +QT_USE_NAMESPACE + +class S60AudioCaptureSession; +class S60AudioEncoderControl; +class S60AudioMediaRecorderControl; +class S60AudioEndpointSelector; +class S60AudioContainerControl; + + +class S60AudioCaptureService : public QMediaService +{ + Q_OBJECT +public: + S60AudioCaptureService(QObject *parent = 0); + ~S60AudioCaptureService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *control); +private: + S60AudioCaptureSession *m_session; + S60AudioEncoderControl *m_encoderControl; + S60AudioEndpointSelector *m_endpointSelector; + S60AudioMediaRecorderControl *m_recorderControl; + S60AudioContainerControl *m_containerControl; +}; + +#endif // S60AUDIOCAPTURESERVICE_H diff --git a/src/plugins/symbian/mmf/audiosource/s60audiocapturesession.cpp b/src/plugins/symbian/mmf/audiosource/s60audiocapturesession.cpp new file mode 100644 index 000000000..79b6a53a0 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiocapturesession.cpp @@ -0,0 +1,937 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60audiocapturesession.h" +#include +#include +#include + +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#ifdef AUDIOINPUT_ROUTING +const QString S60AudioCaptureSession::microPhone("Microphone"); +const QString S60AudioCaptureSession::voiceCall("Voice Call"); +const QString S60AudioCaptureSession::fmRadio("FM Radio"); +#endif + +S60AudioCaptureSession::S60AudioCaptureSession(QObject *parent): + QObject(parent) + , m_recorderUtility(NULL) + , m_captureState(ENotInitialized) + , m_controllerIdMap(QHash()) + , m_audioCodeclist(QHash()) + , m_error(QMediaRecorder::NoError) + , m_isMuted(false) +{ + DP0("S60AudioCaptureSession::S60AudioCaptureSession +++"); +#ifdef AUDIOINPUT_ROUTING + m_audioInput = NULL; + m_setActiveEndPoint = FALSE; + m_audioEndpoint = S60AudioCaptureSession::microPhone; +#endif //AUDIOINPUT_ROUTING + TRAPD(err, initializeSessionL()); + setError(err); + + DP0("S60AudioCaptureSession::S60AudioCaptureSession ---"); +} + +void S60AudioCaptureSession::initializeSessionL() +{ + DP0("S60AudioCaptureSession::initializeSessionL +++"); + + m_recorderUtility = CMdaAudioRecorderUtility::NewL(*this, 0, EMdaPriorityNormal, EMdaPriorityPreferenceTimeAndQuality); + updateAudioContainersL(); + populateAudioCodecsDataL(); + setDefaultSettings(); +#ifdef AUDIOINPUT_ROUTING + initAudioInputs(); +#endif + User::LeaveIfError(m_fsSession.Connect()); + m_captureState = EInitialized; + emit stateChanged(m_captureState); + + DP0("S60AudioCaptureSession::initializeSessionL ---"); +} + +void S60AudioCaptureSession::setError(TInt aError) +{ + DP0("S60AudioCaptureSession::setError +++"); + + DP1("S60AudioCaptureSession::setError:", aError); + + if (aError == KErrNone) + return; + + m_error = aError; + QMediaRecorder::Error recorderError = fromSymbianErrorToMultimediaError(m_error); + + // TODO: fix to user friendly string at some point + // These error string are only dev usable + QString symbianError; + symbianError.append("Symbian:"); + symbianError.append(QString::number(m_error)); + stop(); + emit error(recorderError, symbianError); + + DP0("S60AudioCaptureSession::setError ---"); +} + +QMediaRecorder::Error S60AudioCaptureSession::fromSymbianErrorToMultimediaError(int error) +{ + DP0("S60AudioCaptureSession::fromSymbianErrorToMultimediaError +++"); + + DP1("S60AudioCaptureSession::fromSymbianErrorToMultimediaError:", error); + + switch(error) { + case KErrNoMemory: + case KErrNotFound: + case KErrBadHandle: + case KErrAbort: + case KErrCorrupt: + case KErrGeneral: + case KErrPathNotFound: + case KErrUnknown: + case KErrNotReady: + case KErrInUse: + case KErrAccessDenied: + case KErrLocked: + case KErrPermissionDenied: + case KErrAlreadyExists: + return QMediaRecorder::ResourceError; + case KErrNotSupported: + case KErrArgument: + return QMediaRecorder::FormatError; + case KErrNone: + default: + DP0("S60AudioCaptureSession::fromSymbianErrorToMultimediaError: ---"); + return QMediaRecorder::NoError; + } +} + +S60AudioCaptureSession::~S60AudioCaptureSession() +{ + DP0("S60AudioCaptureSession::~S60AudioCaptureSession +++"); + //stop the utility before deleting it + stop(); + if (m_recorderUtility) + delete m_recorderUtility; + m_fsSession.Close(); + DP0("S60AudioCaptureSession::~S60AudioCaptureSession ---"); +} + +QAudioFormat S60AudioCaptureSession::format() const +{ + DP0("S60AudioCaptureSession::format"); + + return m_format; +} + +bool S60AudioCaptureSession::setFormat(const QAudioFormat &format) +{ + DP0("S60AudioCaptureSession::setFormat +++"); + + m_format = format; + + DP0("S60AudioCaptureSession::setFormat ---"); + + return true; +} + +QAudioEncoderSettings S60AudioCaptureSession::settings() const +{ + DP0("S60AudioCaptureSession::settings"); + + return m_audioEncoderSettings; +} + +bool S60AudioCaptureSession::setEncoderSettings(const QAudioEncoderSettings &setting) +{ + DP0("S60AudioCaptureSession::setEncoderSettings +++"); + + m_audioEncoderSettings = setting; + + DP0("S60AudioCaptureSession::setEncoderSettings ---"); + + return true; +} + +QStringList S60AudioCaptureSession::supportedAudioCodecs() const +{ + DP0("S60AudioCaptureSession::supportedAudioCodecs"); + + return m_audioCodeclist.keys(); +} + +QStringList S60AudioCaptureSession::supportedAudioContainers() const +{ + DP0("S60AudioCaptureSession::supportedAudioContainers"); + + return m_controllerIdMap.keys(); +} + +QString S60AudioCaptureSession::codecDescription(const QString &codecName) +{ + DP0("S60AudioCaptureSession::codecDescription +++"); + + if (m_audioCodeclist.keys().contains(codecName)) { + + DP0("S60AudioCaptureSession::codecDescription ---"); + return m_audioCodeclist.value(codecName).codecDescription; + } + else { + DP0("S60AudioCaptureSession::codecDescription ---"); + + return QString(); + } +} + +QString S60AudioCaptureSession::audioContainerDescription(const QString &containerName) +{ + DP0("S60AudioCaptureSession::audioContainerDescription +++"); + + if (m_controllerIdMap.keys().contains(containerName)) { + DP0("S60AudioCaptureSession::audioContainerDescription ---"); + + return m_controllerIdMap.value(containerName).destinationFormatDescription; + } + else { + DP0("S60AudioCaptureSession::audioContainerDescription ---"); + + return QString(); + } +} + +bool S60AudioCaptureSession::setAudioCodec(const QString &codecName) +{ + DP0("S60AudioCaptureSession::setAudioCodec"); + + QStringList codecs = supportedAudioCodecs(); + if(codecs.contains(codecName)) { + m_format.setCodec(codecName); + return true; + } + return false; +} + +bool S60AudioCaptureSession::setAudioContainer(const QString &containerMimeType) +{ + DP0("S60AudioCaptureSession::setAudioContainer"); + + QStringList containers = supportedAudioContainers(); + if (containerMimeType == "audio/mpeg") + { + m_container = "audio/mp4"; + return true; + } + if(containers.contains(containerMimeType)) { + m_container = containerMimeType; + return true; + } + return false; +} + +QString S60AudioCaptureSession::audioCodec() const +{ + DP0("S60AudioCaptureSession::audioCodec"); + + return m_format.codec(); +} + +QString S60AudioCaptureSession::audioContainer() const +{ + DP0("S60AudioCaptureSession::audioContainer"); + + return m_container; +} + +QUrl S60AudioCaptureSession::outputLocation() const +{ + DP0("S60AudioCaptureSession::outputLocation"); + + return m_sink; +} + +bool S60AudioCaptureSession::setOutputLocation(const QUrl& sink) +{ + DP0("S60AudioCaptureSession::setOutputLocation"); + + QString filename = QDir::toNativeSeparators(sink.toString()); + TPtrC16 path(reinterpret_cast(filename.utf16())); + TRAPD(err, BaflUtils::EnsurePathExistsL(m_fsSession,path)); + if (err == KErrNone) { + m_sink = sink; + setError(err); + return true; + }else { + setError(err); + return false; + } +} + +qint64 S60AudioCaptureSession::position() const +{ + DP0("S60AudioCaptureSession::position"); + + if ((m_captureState != ERecording) || !m_recorderUtility) + return 0; + + return m_recorderUtility->Duration().Int64() / 1000; +} + +void S60AudioCaptureSession::prepareSinkL() +{ + DP0("S60AudioCaptureSession::prepareSinkL +++"); + + /* If m_outputLocation is null, set a default location */ + if (m_sink.isEmpty()) { + QDir outputDir(QDir::rootPath()); + int lastImage = 0; + int fileCount = 0; + foreach(QString fileName, outputDir.entryList(QStringList() << "recordclip_*")) { + int imgNumber = fileName.mid(5, fileName.size() - 9).toInt(); + lastImage = qMax(lastImage, imgNumber); + if (outputDir.exists(fileName)) + fileCount += 1; + } + lastImage += fileCount; + m_sink = QUrl(QDir::toNativeSeparators(outputDir.canonicalPath() + QString("/recordclip_%1").arg(lastImage + 1, 4, 10, QLatin1Char('0')))); + } + + QString sink = QDir::toNativeSeparators(m_sink.toString()); + TPtrC16 path(reinterpret_cast(sink.utf16())); + if (BaflUtils::FileExists(m_fsSession, path)) + BaflUtils::DeleteFile(m_fsSession, path); + + int index = sink.lastIndexOf('.'); + if (index != -1) + sink.chop(sink.length()-index); + + sink.append(m_controllerIdMap.value(m_container).fileExtension); + m_sink.setUrl(sink); + + DP0("S60AudioCaptureSession::prepareSinkL ---"); +} + +void S60AudioCaptureSession::record() +{ + DP0("S60AudioCaptureSession::record +++"); + + if (!m_recorderUtility) + return; + + if (m_captureState == EInitialized || m_captureState == ERecordComplete) { + prepareSinkL(); + QString filename = m_sink.toString(); + TPtrC16 sink(reinterpret_cast(filename.utf16())); + TUid controllerUid(TUid::Uid(m_controllerIdMap.value(m_container).controllerUid)); + TUid formatUid(TUid::Uid(m_controllerIdMap.value(m_container).destinationFormatUid)); + + TRAPD(err,m_recorderUtility->OpenFileL(sink)); + setError(err); + }else if (m_captureState == EPaused) { + m_recorderUtility->SetPosition(m_pausedPosition); + TRAPD(error, m_recorderUtility->RecordL()); + setError(error); + m_captureState = ERecording; + emit stateChanged(m_captureState); + } + + DP0("S60AudioCaptureSession::record ---"); +} + +void S60AudioCaptureSession::mute(bool muted) +{ + DP0("S60AudioCaptureSession::mute +++"); + + if (!m_recorderUtility) + return; + + if (muted) + m_recorderUtility->SetGain(0); + else + m_recorderUtility->SetGain(m_recorderUtility->MaxGain()); + + m_isMuted = muted; + + DP0("S60AudioCaptureSession::mute ---"); +} + +bool S60AudioCaptureSession::muted() +{ + DP0("S60AudioCaptureSession::muted"); + + return m_isMuted; +} + +void S60AudioCaptureSession::setDefaultSettings() +{ + DP0("S60AudioCaptureSession::setDefaultSettings +++"); + + // Setting AMR to default format if supported + if (m_controllerIdMap.count() > 0) { + if ( m_controllerIdMap.contains("audio/amr")) + m_container = QString("audio/amr"); + else + m_container = m_controllerIdMap.keys()[0]; + } + if (m_audioCodeclist.keys().count() > 0) { + if (m_audioCodeclist.keys().contains("AMR")) { + m_format.setSampleSize(8); + m_format.setChannels(1); + m_format.setFrequency(8000); + m_format.setSampleType(QAudioFormat::SignedInt); + m_format.setCodec("AMR"); + }else + m_format.setCodec(m_audioCodeclist.keys()[0]); + } + + DP0("S60AudioCaptureSession::setDefaultSettings ---"); +} + +void S60AudioCaptureSession::pause() +{ + DP0("S60AudioCaptureSession::pause +++"); + + if (!m_recorderUtility) + return; + + m_pausedPosition = m_recorderUtility->Position(); + m_recorderUtility->Stop(); + m_captureState = EPaused; + emit stateChanged(m_captureState); + + DP0("S60AudioCaptureSession::pause ---"); +} + +void S60AudioCaptureSession::stop() +{ + DP0("S60AudioCaptureSession::stop +++"); + + if (!m_recorderUtility) + return; + + m_recorderUtility->Stop(); + +#ifdef AUDIOINPUT_ROUTING + //delete audio input instance before closing the utility. + if (m_audioInput) + { + delete m_audioInput; + m_audioInput = NULL; + } +#endif //AUDIOINPUT_ROUTING + + m_recorderUtility->Close(); + m_captureState = ERecordComplete; + emit stateChanged(m_captureState); +} + +#ifdef AUDIOINPUT_ROUTING + +void S60AudioCaptureSession::initAudioInputs() +{ + DP0(" S60AudioCaptureSession::initAudioInputs +++"); + + m_audioInputs[S60AudioCaptureSession::microPhone] = QString("Microphone associated with the currently active speaker."); + m_audioInputs[S60AudioCaptureSession::voiceCall] = QString("Audio stream associated with the current phone call."); + m_audioInputs[S60AudioCaptureSession::fmRadio] = QString("Audio of the currently tuned FM radio station."); + + DP0(" S60AudioCaptureSession::initAudioInputs ---"); +} + +#endif //AUDIOINPUT_ROUTING + +void S60AudioCaptureSession::setActiveEndpoint(const QString& audioEndpoint) +{ + DP0(" S60AudioCaptureSession::setActiveEndpoint +++"); + + if (!m_audioInputs.keys().contains(audioEndpoint)) + return; + + if (activeEndpoint().compare(audioEndpoint) != 0) { + m_audioEndpoint = audioEndpoint; +#ifdef AUDIOINPUT_ROUTING + m_setActiveEndPoint = TRUE; +#endif + } + + DP0(" S60AudioCaptureSession::setActiveEndpoint ---"); +} + +QList S60AudioCaptureSession::availableEndpoints() const +{ + DP0(" S60AudioCaptureSession::availableEndpoints"); + + return m_audioInputs.keys(); +} + +QString S60AudioCaptureSession::endpointDescription(const QString& name) const +{ + DP0(" S60AudioCaptureSession::endpointDescription +++"); + + if (m_audioInputs.keys().contains(name)) + return m_audioInputs.value(name); + return QString(); +} + +QString S60AudioCaptureSession::activeEndpoint() const +{ + DP0(" S60AudioCaptureSession::activeEndpoint"); + + QString inputSourceName = NULL; +#ifdef AUDIOINPUT_ROUTING + if (m_audioInput) { + CAudioInput::TAudioInputArray input = m_audioInput->AudioInput(); + inputSourceName = qStringFromTAudioInputPreference(input[0]); + } +#endif //AUDIOINPUT_ROUTING + return inputSourceName; +} + +QString S60AudioCaptureSession::defaultEndpoint() const +{ + DP0(" S60AudioCaptureSession::defaultEndpoint"); + +#ifdef AUDIOINPUT_ROUTING + return QString(S60AudioCaptureSession::microPhone); +#else + return NULL; +#endif +} + +#ifdef AUDIOINPUT_ROUTING + +void S60AudioCaptureSession::doSetAudioInputL(const QString& name) +{ + DP0(" S60AudioCaptureSession::doSetAudioInputL +++"); + DP1(" S60AudioCaptureSession::doSetAudioInputL:", name); + TInt err(KErrNone); + + if (!m_recorderUtility) + return; + + CAudioInput::TAudioInputPreference input = CAudioInput::EDefaultMic; + + if (name.compare(S60AudioCaptureSession::voiceCall) == 0) + input = CAudioInput::EVoiceCall; +// commented because they are not supported on 9.2 + else if (name.compare(S60AudioCaptureSession::fmRadio) == 0) + input = CAudioInput::EFMRadio; + else // S60AudioCaptureSession::microPhone + input = CAudioInput::EDefaultMic; + + RArray inputArray; + inputArray.Append(input); + + if (m_audioInput){ + TRAP(err,m_audioInput->SetAudioInputL(inputArray.Array())); + + if (err == KErrNone) { + emit activeEndpointChanged(name); + } + else{ + setError(err); + } + } + inputArray.Close(); + + DP0(" S60AudioCaptureSession::doSetAudioInputL ---"); +} + + +QString S60AudioCaptureSession::qStringFromTAudioInputPreference(CAudioInput::TAudioInputPreference input) const +{ + DP0(" S60AudioCaptureSession::qStringFromTAudioInputPreference"); + + if (input == CAudioInput::EVoiceCall) + return S60AudioCaptureSession::voiceCall; + else if (input == CAudioInput::EFMRadio) + return S60AudioCaptureSession::fmRadio; + else + return S60AudioCaptureSession::microPhone; // CAudioInput::EDefaultMic +} +#endif //AUDIOINPUT_ROUTING + + +void S60AudioCaptureSession::MoscoStateChangeEvent(CBase* aObject, + TInt aPreviousState, TInt aCurrentState, TInt aErrorCode) +{ + DP0("S60AudioCaptureSession::MoscoStateChangeEvent +++"); + + if (aErrorCode==KErrNone) { + TRAPD(err, MoscoStateChangeEventL(aObject, aPreviousState, aCurrentState, NULL)); + setError(err); + } + else { + setError(aErrorCode); + } + DP1("S60AudioCaptureSession::MoscoStateChangeEvent, aErrorCode:", aErrorCode); + DP0("S60AudioCaptureSession::MoscoStateChangeEvent ---"); +} + +void S60AudioCaptureSession::MoscoStateChangeEventL(CBase* aObject, + TInt aPreviousState, TInt aCurrentState, TInt aErrorCode) +{ + DP0("S60AudioCaptureSession::MoscoStateChangeEventL +++"); + + DP5("S60AudioCaptureSession::MoscoStateChangeEventL - aPreviousState:", aPreviousState, + "aCurrentState:", aCurrentState, "aErrorCode:", aErrorCode); + if (aObject != m_recorderUtility) + return; + + switch(aCurrentState) { + case CMdaAudioClipUtility::EOpen: { + if(aPreviousState == CMdaAudioClipUtility::ENotReady) { + applyAudioSettingsL(); + m_recorderUtility->SetGain(m_recorderUtility->MaxGain()); + TRAPD(err, m_recorderUtility->RecordL()); + setError(err); + m_captureState = EOpenCompelete; + emit stateChanged(m_captureState); + } + break; + } + case CMdaAudioClipUtility::ENotReady: { + m_captureState = EInitialized; + emit stateChanged(m_captureState); + break; + } + case CMdaAudioClipUtility::ERecording: { + m_captureState = ERecording; + emit stateChanged(m_captureState); + break; + } + default: { + break; + } + } + + DP0("S60AudioCaptureSession::MoscoStateChangeEventL ---"); +} + +void S60AudioCaptureSession::updateAudioContainersL() +{ + DP0("S60AudioCaptureSession::updateAudioContainersL +++"); + + CMMFControllerPluginSelectionParameters* pluginParameters = + CMMFControllerPluginSelectionParameters::NewLC(); + CMMFFormatSelectionParameters* formatParameters = + CMMFFormatSelectionParameters::NewLC(); + + pluginParameters->SetRequiredRecordFormatSupportL(*formatParameters); + + RArray ids; + CleanupClosePushL(ids); + User::LeaveIfError(ids.Append(KUidMediaTypeAudio)); + + pluginParameters->SetMediaIdsL(ids, + CMMFPluginSelectionParameters::EAllowOnlySuppliedMediaIds); + + RMMFControllerImplInfoArray controllers; + CleanupResetAndDestroyPushL(controllers); + + //Get all audio record controllers/formats that are supported + pluginParameters->ListImplementationsL(controllers); + + for (TInt index=0; indexRecordFormats(); + for (TInt j=0; jSupportedMimeTypes(); + const CDesC8Array& fileExtensions = recordFormats[j]->SupportedFileExtensions(); + TInt mimeCount = mimeTypes.Count(); + TInt fileExtCount = fileExtensions.Count(); + + if (mimeCount > 0 && fileExtCount > 0) { + TPtrC8 extension = fileExtensions[0]; + TPtrC8 mimeType = mimeTypes[0]; + QString type = QString::fromUtf8((char *)mimeType.Ptr(), mimeType.Length()); + + if (type != "audio/basic") { + ControllerData data; + data.controllerUid = controllers[index]->Uid().iUid; + data.destinationFormatUid = recordFormats[j]->Uid().iUid; + data.destinationFormatDescription = QString::fromUtf16( + recordFormats[j]->DisplayName().Ptr(), + recordFormats[j]->DisplayName().Length()); + data.fileExtension = QString::fromUtf8((char *)extension.Ptr(), extension.Length()); + m_controllerIdMap[type] = data; + } + } + } + } + CleanupStack::PopAndDestroy(4);//controllers, ids, formatParameters, pluginParameters + + DP0("S60AudioCaptureSession::updateAudioContainersL ---"); +} + +void S60AudioCaptureSession::retrieveSupportedAudioSampleRatesL() +{ + DP0("S60AudioCaptureSession::retrieveSupportedAudioSampleRatesL +++"); + + if (!m_recorderUtility) { + DP0("No RecorderUtility"); + return; + } + + m_supportedSampleRates.clear(); + + RArray supportedSampleRates; + CleanupClosePushL(supportedSampleRates); + m_recorderUtility->GetSupportedSampleRatesL(supportedSampleRates); + for (TInt j = 0; j < supportedSampleRates.Count(); j++ ) + m_supportedSampleRates.append(supportedSampleRates[j]); + + CleanupStack::PopAndDestroy(&supportedSampleRates); + + DP0("S60AudioCaptureSession::retrieveSupportedAudioSampleRatesL ---"); +} + +QList S60AudioCaptureSession::supportedAudioSampleRates(const QAudioEncoderSettings &settings) const +{ + DP0("S60AudioCaptureSession::supportedAudioSampleRates +++"); + + QList supportedSampleRates; + + if (!settings.codec().isEmpty()) { + if (settings.codec() == "AMR") + supportedSampleRates.append(8000); + else + supportedSampleRates = m_supportedSampleRates; + }else + supportedSampleRates = m_supportedSampleRates; + + DP0("S60AudioCaptureSession::supportedAudioSampleRates ---"); + + return supportedSampleRates; +} + +void S60AudioCaptureSession::populateAudioCodecsDataL() +{ + DP0("S60AudioCaptureSession::populateAudioCodecsDataL +++"); + + if (!m_recorderUtility) { + DP0("No RecorderUtility"); + + return; + } + + if (m_controllerIdMap.contains("audio/amr")) { + CodecData data; + data.codecDescription = QString("GSM AMR Codec"); + m_audioCodeclist[QString("AMR")]=data; + } + if (m_controllerIdMap.contains("audio/basic")) { + CodecData data; + data.fourCC = KMMFFourCCCodeALAW; + data.codecDescription = QString("Sun/Next ""Au"" audio codec"); + m_audioCodeclist[QString("AULAW")]=data; + } + if (m_controllerIdMap.contains("audio/wav")) { + CodecData data; + data.fourCC = KMMFFourCCCodePCM16; + data.codecDescription = QString("Pulse code modulation"); + m_audioCodeclist[QString("PCM")]=data; + } + if (m_controllerIdMap.contains("audio/mp4")) { + CodecData data; + data.fourCC = KMMFFourCCCodeAAC; + data.codecDescription = QString("Advanced Audio Codec"); + m_audioCodeclist[QString("AAC")]=data; + } + + // default samplerates + m_supportedSampleRates << 96000 << 88200 << 64000 << 48000 << 44100 << 32000 << 24000 << 22050 << 16000 << 12000 << 11025 << 8000; + + DP0("S60AudioCaptureSession::populateAudioCodecsDataL ---"); +} + +void S60AudioCaptureSession::applyAudioSettingsL() +{ + DP0("S60AudioCaptureSession::applyAudioSettingsL +++"); + + if (!m_recorderUtility) + return; + +#ifdef AUDIOINPUT_ROUTING + //CAudioInput needs to be re-initialized every time recording starts + if (m_audioInput) { + delete m_audioInput; + m_audioInput = NULL; + } + + if (m_setActiveEndPoint) { + m_audioInput = CAudioInput::NewL(*m_recorderUtility); + doSetAudioInputL(m_audioEndpoint); + } +#endif //AUDIOINPUT_ROUTING + + if (m_format.codec() == "AMR") + return; + + TFourCC fourCC = m_audioCodeclist.value(m_format.codec()).fourCC; + + if (m_format.codec() == "PCM") + fourCC = determinePCMFormat(); + + RArray supportedDataTypes; + CleanupClosePushL(supportedDataTypes); + TRAPD(err,m_recorderUtility->GetSupportedDestinationDataTypesL(supportedDataTypes)); + TInt num = supportedDataTypes.Count(); + if (num > 0 ) { + supportedDataTypes.SortUnsigned(); + int index = supportedDataTypes.Find(fourCC.FourCC()); + if (index != KErrNotFound) { + TRAPD(err,m_recorderUtility->SetDestinationDataTypeL(supportedDataTypes[index])); + } + } + + supportedDataTypes.Reset(); + CleanupStack::PopAndDestroy(&supportedDataTypes); + + if (m_recorderUtility->DestinationSampleRateL() != m_format.frequency()) { + + RArray supportedSampleRates; + CleanupClosePushL(supportedSampleRates); + m_recorderUtility->GetSupportedSampleRatesL(supportedSampleRates); + for (TInt i = 0; i < supportedSampleRates.Count(); i++ ) { + TUint supportedSampleRate = supportedSampleRates[i]; + if (supportedSampleRate == m_format.frequency()) { + m_recorderUtility->SetDestinationSampleRateL(m_format.frequency()); + break; + } + } + supportedSampleRates.Reset(); + CleanupStack::PopAndDestroy(&supportedSampleRates); + } + + /* If requested channel setting is different than current one */ + if (m_recorderUtility->DestinationNumberOfChannelsL() != m_format.channels()) { + RArray supportedChannels; + CleanupClosePushL(supportedChannels); + m_recorderUtility->GetSupportedNumberOfChannelsL(supportedChannels); + for (TInt l = 0; l < supportedChannels.Count(); l++ ) { + if (supportedChannels[l] == m_format.channels()) { + m_recorderUtility->SetDestinationNumberOfChannelsL(m_format.channels()); + break; + } + } + supportedChannels.Reset(); + CleanupStack::PopAndDestroy(&supportedChannels); + } + + if (!(m_format.codec().compare("pcm",Qt::CaseInsensitive) == 0)) { + if (m_recorderUtility->DestinationBitRateL() != m_audioEncoderSettings.bitRate()) { + RArray supportedBitRates; + CleanupClosePushL(supportedBitRates); + m_recorderUtility->GetSupportedBitRatesL(supportedBitRates); + for (TInt l = 0; l < supportedBitRates.Count(); l++ ) { + if (supportedBitRates[l] == m_audioEncoderSettings.bitRate()) { + m_recorderUtility->SetDestinationBitRateL(m_audioEncoderSettings.bitRate()); + break; + } + } + supportedBitRates.Reset(); + CleanupStack::PopAndDestroy(&supportedBitRates); + } + } + + DP0("S60AudioCaptureSession::applyAudioSettingsL ---"); +} + +TFourCC S60AudioCaptureSession::determinePCMFormat() +{ + DP0("S60AudioCaptureSession::determinePCMFormat +++"); + + TFourCC fourCC; + + if (m_format.sampleSize() == 8) { + // 8 bit + switch (m_format.sampleType()) { + case QAudioFormat::SignedInt: { + fourCC.Set(KMMFFourCCCodePCM8); + break; + } + case QAudioFormat::UnSignedInt: { + fourCC.Set(KMMFFourCCCodePCMU8); + break; + } + case QAudioFormat::Float: + case QAudioFormat::Unknown: + default: { + fourCC.Set(KMMFFourCCCodePCM8); + break; + } + } + } else if (m_format.sampleSize() == 16) { + // 16 bit + switch (m_format.sampleType()) { + case QAudioFormat::SignedInt: { + fourCC.Set(m_format.byteOrder()==QAudioFormat::BigEndian? + KMMFFourCCCodePCM16B:KMMFFourCCCodePCM16); + break; + } + case QAudioFormat::UnSignedInt: { + fourCC.Set(m_format.byteOrder()==QAudioFormat::BigEndian? + KMMFFourCCCodePCMU16B:KMMFFourCCCodePCMU16); + break; + } + default: { + fourCC.Set(KMMFFourCCCodePCM16); + break; + } + } + } + + DP0("S60AudioCaptureSession::determinePCMFormat ---"); + + return fourCC; +} diff --git a/src/plugins/symbian/mmf/audiosource/s60audiocapturesession.h b/src/plugins/symbian/mmf/audiosource/s60audiocapturesession.h new file mode 100644 index 000000000..a541446e9 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiocapturesession.h @@ -0,0 +1,193 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOCAPTURESESSION_H +#define S60AUDIOCAPTURESESSION_H + +#include +#include +#include +#include +#include +#include +#include +#include "qaudioformat.h" +#include + +#include +#include +#include +#include +#include + +#ifdef AUDIOINPUT_ROUTING +#include +#endif //AUDIOINPUT_ROUTING + +QT_BEGIN_NAMESPACE +struct ControllerData +{ + int controllerUid; + int destinationFormatUid; + QString destinationFormatDescription; + QString fileExtension; +}; + +struct CodecData +{ + TFourCC fourCC; + QString codecDescription; +}; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class S60AudioCaptureSession : public QObject, public MMdaObjectStateChangeObserver +{ + Q_OBJECT + Q_PROPERTY(qint64 position READ position NOTIFY positionChanged) + Q_ENUMS(TAudioCaptureState) +public: + + enum TAudioCaptureState + { + ENotInitialized = 0, + EInitialized, + EOpenCompelete, + ERecording, + EPaused, + ERecordComplete + }; + + S60AudioCaptureSession(QObject *parent = 0); + ~S60AudioCaptureSession(); + + QAudioFormat format() const; + bool setFormat(const QAudioFormat &format); + QAudioEncoderSettings settings() const; + bool setEncoderSettings(const QAudioEncoderSettings &setting); + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName); + bool setAudioCodec(const QString &codecName); + QString audioCodec() const; + QString audioContainer() const; + QStringList supportedAudioContainers() const; + bool setAudioContainer(const QString &containerMimeType); + QString audioContainerDescription(const QString &containerName); + QList supportedAudioSampleRates(const QAudioEncoderSettings &settings) const; + QUrl outputLocation() const; + bool setOutputLocation(const QUrl& sink); + qint64 position() const; + void record(); + void pause(); + void stop(); + void mute(bool muted); + bool muted(); + + QString activeEndpoint() const; + QString defaultEndpoint() const; + QList availableEndpoints() const; + QString endpointDescription(const QString& name) const; + +#ifdef AUDIOINPUT_ROUTING + static const QString microPhone; + static const QString voiceCall; + static const QString fmRadio; +#endif //AUDIOINPUT_ROUTING +private: + void initializeSessionL(); + void setError(TInt aError); + QMediaRecorder::Error fromSymbianErrorToMultimediaError(int error); + void prepareSinkL(); + void updateAudioContainersL(); + void populateAudioCodecsDataL(); + void retrieveSupportedAudioSampleRatesL(); + void applyAudioSettingsL(); + TFourCC determinePCMFormat(); + void setDefaultSettings(); + // MMdaObjectStateChangeObserver + void MoscoStateChangeEvent(CBase* aObject, TInt aPreviousState, + TInt aCurrentState, TInt aErrorCode); + void MoscoStateChangeEventL(CBase* aObject, TInt aPreviousState, + TInt aCurrentState, TInt aErrorCode); + +#ifdef AUDIOINPUT_ROUTING + QString qStringFromTAudioInputPreference(CAudioInput::TAudioInputPreference input) const; + void initAudioInputs(); + void doSetAudioInputL(const QString& name); +#endif //AUDIOINPUT_ROUTING + +public Q_SLOTS: + void setActiveEndpoint(const QString& audioEndpoint); + + +Q_SIGNALS: + void stateChanged(S60AudioCaptureSession::TAudioCaptureState); + void positionChanged(qint64 position); + void error(int error, const QString &errorString); + void activeEndpointChanged(const QString &audioEndpoint); +private: + QString m_container; + QUrl m_sink; + TTimeIntervalMicroSeconds m_pausedPosition; + CMdaAudioRecorderUtility *m_recorderUtility; + TAudioCaptureState m_captureState; + QAudioFormat m_format; + QAudioEncoderSettings m_audioEncoderSettings; + QHash m_controllerIdMap; + QHash m_audioCodeclist; + QList m_supportedSampleRates; + int m_error; + bool m_isMuted; + RFs m_fsSession; + +#ifdef AUDIOINPUT_ROUTING + bool m_setActiveEndPoint; + CAudioInput *m_audioInput; + +#endif //AUDIOINPUT_ROUTING + QMap m_audioInputs; + QString m_audioEndpoint; + + +}; + +#endif // S60AUDIOCAPTURESESSION_H diff --git a/src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.cpp b/src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.cpp new file mode 100644 index 000000000..d6c2d5db2 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.cpp @@ -0,0 +1,96 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60audiocontainercontrol.h" +#include "s60audiocapturesession.h" +#include + +S60AudioContainerControl::S60AudioContainerControl(QObject *parent) + : QMediaContainerControl(parent) +{ + DP0("S60AudioContainerControl::S60AudioContainerControl(QObject *parent) +++"); + + DP0("S60AudioContainerControl::S60AudioContainerControl(QObject *parent) ---"); + +} + +S60AudioContainerControl::S60AudioContainerControl(QObject *session, QObject *parent) + : QMediaContainerControl(parent) +{ + DP0("S60AudioContainerControl::S60AudioContainerControl(QObject *session, QObject *parent) +++"); + + m_session = qobject_cast(session); + + DP0("S60AudioContainerControl::S60AudioContainerControl(QObject *session, QObject *parent) ---"); +} + +QStringList S60AudioContainerControl::supportedContainers() const +{ + DP0("S60AudioContainerControl::supportedContainers"); + + return m_session->supportedAudioContainers(); +} + +QString S60AudioContainerControl::containerMimeType() const +{ + DP0("S60AudioContainerControl::containerMimeType"); + + return m_session->audioContainer(); +} + +void S60AudioContainerControl::setContainerMimeType(const QString &containerMimeType) +{ + DP0("S60AudioContainerControl::setContainerMimeType +++"); + + m_session->setAudioContainer(containerMimeType); + + DP0("S60AudioContainerControl::setContainerMimeType ---"); +} + +QString S60AudioContainerControl::containerDescription(const QString &containerMimeType) const +{ + DP0("S60AudioContainerControl::containerDescription"); + + return m_session->audioContainerDescription(containerMimeType); +} + diff --git a/src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.h b/src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.h new file mode 100644 index 000000000..4c3498d0f --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiocontainercontrol.h @@ -0,0 +1,70 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOFORMATCONTROL_H +#define S60AUDIOFORMATCONTROL_H + +#include "qmediacontainercontrol.h" +#include + + +QT_USE_NAMESPACE + +class S60AudioCaptureSession; + +class S60AudioContainerControl : public QMediaContainerControl +{ +Q_OBJECT +public: + S60AudioContainerControl(QObject *parent = 0); + S60AudioContainerControl(QObject *session, QObject *parent = 0); + virtual ~S60AudioContainerControl() {}; + + QStringList supportedContainers() const; + QString containerMimeType() const; + void setContainerMimeType(const QString &containerMimeType); + QString containerDescription(const QString &containerMimeType) const; + +private: + S60AudioCaptureSession* m_session; +}; + +#endif // S60AUDIOFORMATCONTROL_H diff --git a/src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.cpp b/src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.cpp new file mode 100644 index 000000000..b3d02a94a --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.cpp @@ -0,0 +1,235 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60audioencodercontrol.h" +#include "s60audiocapturesession.h" + +#include "qaudioformat.h" + +#include + +S60AudioEncoderControl::S60AudioEncoderControl(QObject *session, QObject *parent) + :QAudioEncoderControl(parent), m_quality(QtMultimediaKit::NormalQuality) +{ + DP0("S60AudioEncoderControl::S60AudioEncoderControl +++"); + + m_session = qobject_cast(session); + QAudioFormat fmt = m_session->format(); + // medium, 22050Hz mono S16 + fmt.setSampleType(QAudioFormat::SignedInt); + if (fmt.codec().compare("PCM", Qt::CaseInsensitive) == 0) { + fmt.setSampleSize(16); + fmt.setFrequency(22050); + } + fmt.setChannels(1); + m_session->setFormat(fmt); + m_settings.setChannelCount(fmt.channels()); + m_settings.setCodec(fmt.codec()); + m_settings.setSampleRate(fmt.sampleRate()); + + DP0("S60AudioEncoderControl::S60AudioEncoderControl ---"); +} + +S60AudioEncoderControl::~S60AudioEncoderControl() +{ + DP0("S60AudioEncoderControl::~S60AudioEncoderControl +++"); + + DP0("S60AudioEncoderControl::~S60AudioEncoderControl ---"); +} + +QStringList S60AudioEncoderControl::supportedAudioCodecs() const +{ + DP0("S60AudioEncoderControl::supportedAudioCodecs"); + + return m_session->supportedAudioCodecs(); +} + +QString S60AudioEncoderControl::codecDescription(const QString &codecName) const +{ + DP0("S60AudioEncoderControl::codecDescription"); + + return m_session->codecDescription(codecName); +} + +QtMultimediaKit::EncodingQuality S60AudioEncoderControl::quality() const +{ + DP0("S60AudioEncoderControl::quality"); + + return m_quality; +} + +void S60AudioEncoderControl::setQuality(QtMultimediaKit::EncodingQuality value, QAudioFormat &fmt) +{ + DP0("S60AudioEncoderControl::setQuality +++"); + + switch (value) { + case QtMultimediaKit::VeryLowQuality: + case QtMultimediaKit::LowQuality: + // low, 8000Hz mono U8 + fmt.setSampleType(QAudioFormat::UnSignedInt); + fmt.setSampleSize(8); + fmt.setFrequency(8000); + fmt.setChannels(1); + break; + case QtMultimediaKit::NormalQuality: + // medium, 22050Hz mono S16 + fmt.setSampleType(QAudioFormat::SignedInt); + fmt.setSampleSize(16); + fmt.setFrequency(22050); + fmt.setChannels(1); + break; + case QtMultimediaKit::HighQuality: + case QtMultimediaKit::VeryHighQuality: + // high, 44100Hz mono S16 + fmt.setSampleType(QAudioFormat::SignedInt); + fmt.setSampleSize(16); + fmt.setFrequency(44100); + fmt.setChannels(2); + break; + default: + break; + } + + DP0("S60AudioEncoderControl::setQuality ---"); +} + +QStringList S60AudioEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + DP0("S60AudioEncoderControl::supportedEncodingOptions"); + + Q_UNUSED(codec) + QStringList list; + if (codec == "PCM") + list << "quality" << "channels" << "samplerate"; + return list; +} + +QVariant S60AudioEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + DP0("S60AudioEncoderControl::encodingOption"); + + if (codec == "PCM") { + QAudioFormat fmt = m_session->format(); + + if(qstrcmp(name.toLocal8Bit().constData(), "quality") == 0) { + return QVariant(quality()); + } + else if(qstrcmp(name.toLocal8Bit().constData(), "channels") == 0) { + return QVariant(fmt.channels()); + } + else if(qstrcmp(name.toLocal8Bit().constData(), "samplerate") == 0) { + return QVariant(fmt.frequency()); + } + } + return QVariant(); +} + +void S60AudioEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + DP0("S60AudioEncoderControl::setEncodingOption +++"); + + if (codec == "PCM") { + QAudioFormat fmt = m_session->format(); + + if(qstrcmp(name.toLocal8Bit().constData(), "quality") == 0) { + setQuality((QtMultimediaKit::EncodingQuality)value.toInt(), fmt); + } else if(qstrcmp(name.toLocal8Bit().constData(), "channels") == 0) { + fmt.setChannels(value.toInt()); + } else if(qstrcmp(name.toLocal8Bit().constData(), "samplerate") == 0) { + fmt.setFrequency(value.toInt()); + } + m_session->setFormat(fmt); + } + + DP0("S60AudioEncoderControl::setEncodingOption ---"); +} + +QList S60AudioEncoderControl::supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous) const +{ + DP0("S60AudioEncoderControl::supportedSampleRates"); + + if (continuous) + *continuous = false; + + return m_session->supportedAudioSampleRates(settings); +} + +QAudioEncoderSettings S60AudioEncoderControl::audioSettings() const +{ + DP0("S60AudioEncoderControl::audioSettings"); + + return m_settings; +} + +void S60AudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings) +{ + DP0("S60AudioEncoderControl::setAudioSettings +++"); + + QAudioFormat fmt = m_session->format(); + if (settings.encodingMode() == QtMultimediaKit::ConstantQualityEncoding) { + fmt.setCodec(settings.codec()); + setQuality(settings.quality(), fmt); + if (settings.sampleRate() > 0) { + fmt.setFrequency(settings.sampleRate()); + } + if (settings.channelCount() > 0) + fmt.setChannels(settings.channelCount()); + }else { + if (settings.sampleRate() == 8000) { + fmt.setSampleType(QAudioFormat::UnSignedInt); + fmt.setSampleSize(8); + } else { + fmt.setSampleType(QAudioFormat::SignedInt); + fmt.setSampleSize(16); + } + fmt.setCodec(settings.codec()); + fmt.setFrequency(settings.sampleRate()); + fmt.setChannels(settings.channelCount()); + } + m_session->setFormat(fmt); + m_session->setEncoderSettings(settings); + m_settings = settings; + + DP0("S60AudioEncoderControl::setAudioSettings ---"); +} diff --git a/src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.h b/src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.h new file mode 100644 index 000000000..236d7d522 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audioencodercontrol.h @@ -0,0 +1,82 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef AUDIOENCODERCONTROL_H +#define AUDIOENCODERCONTROL_H + +#include +#include +#include "qaudioformat.h" + +QT_USE_NAMESPACE + +class S60AudioCaptureSession; + +class S60AudioEncoderControl : public QAudioEncoderControl +{ + Q_OBJECT +public: + S60AudioEncoderControl(QObject *session, QObject *parent = 0); + virtual ~S60AudioEncoderControl(); + + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + + QList supportedSampleRates(const QAudioEncoderSettings &settings, bool *continuous = 0) const; + + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings&); + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: + QtMultimediaKit::EncodingQuality quality() const; + void setQuality(QtMultimediaKit::EncodingQuality, QAudioFormat &format); + +private: + S60AudioCaptureSession* m_session; + QAudioEncoderSettings m_settings; + QtMultimediaKit::EncodingQuality m_quality; +}; + +#endif diff --git a/src/plugins/symbian/mmf/audiosource/s60audioendpointselector.cpp b/src/plugins/symbian/mmf/audiosource/s60audioendpointselector.cpp new file mode 100644 index 000000000..a2f909316 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audioendpointselector.cpp @@ -0,0 +1,100 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60audiocapturesession.h" +#include "s60audioendpointselector.h" + +#include + +S60AudioEndpointSelector::S60AudioEndpointSelector(QObject *session, QObject *parent) + :QAudioEndpointSelector(parent) +{ + DP0("S60AudioEndpointSelector::S60AudioEndpointSelector +++"); + m_session = qobject_cast(session); + + connect(m_session, SIGNAL(activeEndpointChanged(const QString &)), this, SIGNAL(activeEndpointChanged(const QString &))); + + DP0("S60AudioEndpointSelector::S60AudioEndpointSelector ---"); +} + +S60AudioEndpointSelector::~S60AudioEndpointSelector() +{ + DP0("S60AudioEndpointSelector::~S60AudioEndpointSelector +++"); + + DP0("S60AudioEndpointSelector::~S60AudioEndpointSelector ---"); +} + +QList S60AudioEndpointSelector::availableEndpoints() const +{ + DP0("S60AudioEndpointSelector::availableEndpoints"); + + return m_session->availableEndpoints(); +} + +QString S60AudioEndpointSelector::endpointDescription(const QString& name) const +{ + DP0("S60AudioEndpointSelector::endpointDescription"); + + return m_session->endpointDescription(name); +} + +QString S60AudioEndpointSelector::defaultEndpoint() const +{ + DP0("S60AudioEndpointSelector::defaultEndpoint"); + + return m_session->defaultEndpoint(); +} + +QString S60AudioEndpointSelector::activeEndpoint() const +{ + DP0("S60AudioEndpointSelector::activeEndpoint"); + + return m_session->activeEndpoint(); +} + +void S60AudioEndpointSelector::setActiveEndpoint(const QString& name) +{ + DP0("S60AudioEndpointSelector::setActiveEndpoint +++"); + m_session->setActiveEndpoint(name); + DP0("S60AudioEndpointSelector::setActiveEndpoint ---"); +} diff --git a/src/plugins/symbian/mmf/audiosource/s60audioendpointselector.h b/src/plugins/symbian/mmf/audiosource/s60audioendpointselector.h new file mode 100644 index 000000000..d89ce8765 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audioendpointselector.h @@ -0,0 +1,76 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOENDPOINTSELECTOR_H +#define S60AUDIOENDPOINTSELECTOR_H + +#include + +#include + +QT_USE_NAMESPACE + +class S60AudioCaptureSession; + +class S60AudioEndpointSelector : public QAudioEndpointSelector +{ + +Q_OBJECT + +public: + S60AudioEndpointSelector(QObject *session, QObject *parent = 0); + ~S60AudioEndpointSelector(); + + QList availableEndpoints() const; + QString endpointDescription(const QString& name) const; + QString defaultEndpoint() const; + QString activeEndpoint() const; + + +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +private: + + S60AudioCaptureSession* m_session; +}; + +#endif // S60AUDIOENDPOINTSELECTOR_H diff --git a/src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.cpp b/src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.cpp new file mode 100644 index 000000000..5d80033b3 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.cpp @@ -0,0 +1,180 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60audiomediarecordercontrol.h" +#include "s60audiocapturesession.h" + +#include + +S60AudioMediaRecorderControl::S60AudioMediaRecorderControl(QObject *session, QObject *parent) + :QMediaRecorderControl(parent), m_state(QMediaRecorder::StoppedState) +{ + DP0("S60AudioMediaRecorderControl::S60AudioMediaRecorderControl +++"); + + m_session = qobject_cast(session); + connect(m_session, SIGNAL(positionChanged(qint64)), this, SIGNAL(durationChanged(qint64))); + connect(m_session, SIGNAL(stateChanged(S60AudioCaptureSession::TAudioCaptureState)), this, SLOT(updateState(S60AudioCaptureSession::TAudioCaptureState))); + connect(m_session,SIGNAL(error(int,const QString &)),this,SIGNAL(error(int,const QString &))); + + DP0("S60AudioMediaRecorderControl::S60AudioMediaRecorderControl ---"); +} + +S60AudioMediaRecorderControl::~S60AudioMediaRecorderControl() +{ + DP0("S60AudioMediaRecorderControl::~S60AudioMediaRecorderControl +++"); + + DP0("S60AudioMediaRecorderControl::~S60AudioMediaRecorderControl - - "); +} + +QUrl S60AudioMediaRecorderControl::outputLocation() const +{ + DP0("S60AudioMediaRecorderControl::outputLocation"); + + return m_session->outputLocation(); +} + +bool S60AudioMediaRecorderControl::setOutputLocation(const QUrl& sink) +{ + DP0("S60AudioMediaRecorderControl::setOutputLocation"); + + return m_session->setOutputLocation(sink); +} + +QMediaRecorder::State S60AudioMediaRecorderControl::convertState(S60AudioCaptureSession::TAudioCaptureState aState) const +{ + DP0("S60AudioMediaRecorderControl::convertState +++"); + + QMediaRecorder::State state = QMediaRecorder::StoppedState;; + switch (aState) { + case S60AudioCaptureSession::ERecording: + state = QMediaRecorder::RecordingState; + break; + case S60AudioCaptureSession::EPaused: + state = QMediaRecorder::PausedState; + break; + case S60AudioCaptureSession::ERecordComplete: + case S60AudioCaptureSession::ENotInitialized: + case S60AudioCaptureSession::EOpenCompelete: + case S60AudioCaptureSession::EInitialized: + state = QMediaRecorder::StoppedState; + break; + } + + DP1("S60AudioMediaRecorderControl::convertState:", state); + + DP0("S60AudioMediaRecorderControl::convertState ---"); + + return state; +} + +void S60AudioMediaRecorderControl::updateState(S60AudioCaptureSession::TAudioCaptureState aState) +{ + DP0("S60AudioMediaRecorderControl::updateState +++"); + + QMediaRecorder::State newState = convertState(aState); + if (m_state != newState) { + m_state = newState; + emit stateChanged(m_state); + } + + DP0("S60AudioMediaRecorderControl::updateState ---"); +} + +QMediaRecorder::State S60AudioMediaRecorderControl::state() const +{ + DP0("S60AudioMediaRecorderControl::state"); + + return m_state; +} + +qint64 S60AudioMediaRecorderControl::duration() const +{ + // DP0("S60AudioMediaRecorderControl::duration +++"); + + return m_session->position(); +} + +void S60AudioMediaRecorderControl::record() +{ + DP0("S60AudioMediaRecorderControl::record +++"); + + m_session->record(); + + DP0("S60AudioMediaRecorderControl::record ---"); +} + +void S60AudioMediaRecorderControl::pause() +{ + DP0("S60AudioMediaRecorderControl::pause +++"); + + m_session->pause(); + + DP0("S60AudioMediaRecorderControl::pause ---"); +} + +void S60AudioMediaRecorderControl::stop() +{ + DP0("S60AudioMediaRecorderControl::stop +++"); + + m_session->stop(); + + DP0("S60AudioMediaRecorderControl::stop ---"); +} + +bool S60AudioMediaRecorderControl::isMuted() const +{ + DP0("S60AudioMediaRecorderControl::isMuted"); + + return m_session->muted(); +} + +void S60AudioMediaRecorderControl::setMuted(bool muted) +{ + DP0("S60AudioMediaRecorderControl::setMuted +++"); + + DP1("S60AudioMediaRecorderControl::setMuted:", muted); + + m_session->mute(muted); + + DP0("S60AudioMediaRecorderControl::setMuted +++"); +} diff --git a/src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.h b/src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.h new file mode 100644 index 000000000..78e8f4870 --- /dev/null +++ b/src/plugins/symbian/mmf/audiosource/s60audiomediarecordercontrol.h @@ -0,0 +1,92 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOMEDIARECORDERCONTROL_H +#define S60AUDIOMEDIARECORDERCONTROL_H + +#include +#include + +#include "qmediarecorder.h" +#include "qmediarecordercontrol.h" + +#include "s60audiocapturesession.h" + +QT_USE_NAMESPACE + +//class S60AudioCaptureSession; + +class S60AudioMediaRecorderControl : public QMediaRecorderControl +{ + Q_OBJECT +public: + S60AudioMediaRecorderControl(QObject *session,QObject *parent = 0); + ~S60AudioMediaRecorderControl(); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &sink); + + QMediaRecorder::State state() const; + + qint64 duration() const; + + bool isMuted() const; + + void applySettings() {} + +private: + QMediaRecorder::State convertState(S60AudioCaptureSession::TAudioCaptureState aState) const; + +public slots: + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private slots: + void updateState(S60AudioCaptureSession::TAudioCaptureState aState); + +private: + S60AudioCaptureSession* m_session; + QMediaRecorder::State m_state; +}; + +#endif diff --git a/src/plugins/symbian/mmf/inc/DebugMacros.h b/src/plugins/symbian/mmf/inc/DebugMacros.h new file mode 100644 index 000000000..449bef088 --- /dev/null +++ b/src/plugins/symbian/mmf/inc/DebugMacros.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#ifndef __DEBUGMACROS_H__ +#define __DEBUGMACROS_H__ + +// MACROS +#ifdef _DEBUG +#define DP0(string) qDebug()< +#include + +/*! + Constructs the CMdaAudioPlayerUtility object with given \a parent QObject. + + And Registers for Audio Loading Notifications. + +*/ + +S60AudioPlayerSession::S60AudioPlayerSession(QObject *parent) + : S60MediaPlayerSession(parent) + , m_player(0) + , m_audioEndpoint("Default") +{ + DP0("S60AudioPlayerSession::S60AudioPlayerSession +++"); + +#ifdef HAS_AUDIOROUTING + m_audioOutput = 0; +#endif //HAS_AUDIOROUTING + QT_TRAP_THROWING(m_player = CAudioPlayer::NewL(*this, 0, EMdaPriorityPreferenceNone)); + m_player->RegisterForAudioLoadingNotification(*this); + + DP0("S60AudioPlayerSession::S60AudioPlayerSession ---"); +} + + +/*! + Destroys the CMdaAudioPlayerUtility object. + + And Unregister the observer. + +*/ + +S60AudioPlayerSession::~S60AudioPlayerSession() +{ + DP0("S60AudioPlayerSession::~S60AudioPlayerSession +++"); +#ifdef HAS_AUDIOROUTING + if (m_audioOutput) + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; +#endif + m_player->Close(); + delete m_player; + + DP0("S60AudioPlayerSession::~S60AudioPlayerSession ---"); +} + +/*! + + Opens the a file from \a path. + +*/ + +void S60AudioPlayerSession::doLoadL(const TDesC &path) +{ + DP0("S60AudioPlayerSession::doLoadL +++"); + +#ifdef HAS_AUDIOROUTING + // m_audioOutput needs to be reinitialized after MapcInitComplete + if (m_audioOutput) + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; + m_audioOutput = NULL; +#endif //HAS_AUDIOROUTING + m_player->OpenFileL(path); + + DP0("S60AudioPlayerSession::doLoadL ---"); +} + +/*! + + Returns the duration of the audio sample in microseconds. + +*/ + +qint64 S60AudioPlayerSession::doGetDurationL() const +{ + // DP0("S60AudioPlayerSession::doGetDurationL"); + + return m_player->Duration().Int64() / qint64(1000); +} + +/*! + * Returns the current playback position in microseconds from the start of the clip. + +*/ + +qint64 S60AudioPlayerSession::doGetPositionL() const +{ + // DP0("S60AudioPlayerSession::doGetPositionL"); + + TTimeIntervalMicroSeconds ms = 0; + m_player->GetPosition(ms); + return ms.Int64() / qint64(1000); +} + +/*! + Returns TRUE if Video available or else FALSE + */ + +bool S60AudioPlayerSession::isVideoAvailable() +{ + DP0("S60AudioPlayerSession::isVideoAvailable"); + + return false; +} + +/*! + Returns TRUE if Audio available or else FALSE + */ +bool S60AudioPlayerSession::isAudioAvailable() +{ + DP0("S60AudioPlayerSession::isAudioAvailable"); + + return true; // this is a bit happy scenario, but we do emit error that we can't play +} + +/*! + Starts loading Media and sets media status to Buffering. + + */ + +void S60AudioPlayerSession::MaloLoadingStarted() +{ + DP0("S60AudioPlayerSession::MaloLoadingStarted +++"); + + buffering(); + + DP0("S60AudioPlayerSession::MaloLoadingStarted ---"); +} + + +/*! + Indicates loading Media is completed. + + And sets media status to Buffered. + + */ + +void S60AudioPlayerSession::MaloLoadingComplete() +{ + DP0("S60AudioPlayerSession::MaloLoadingComplete +++"); + + buffered(); + + DP0("S60AudioPlayerSession::MaloLoadingComplete ---"); +} + +/*! + Start or resume playing the current source. +*/ + +void S60AudioPlayerSession::doPlay() +{ + DP0("S60AudioPlayerSession::doPlay +++"); + + // For some reason loading progress callback are not called on emulator + // Same is the case with hardware. Will be fixed as part of QTMOBILITY-782. + + //#ifdef __WINSCW__ + buffering(); + //#endif + m_player->Play(); + //#ifdef __WINSCW__ + buffered(); + //#endif + + DP0("S60AudioPlayerSession::doPlay ---"); +} + + +/*! + Pause playing the current source. +*/ + + +void S60AudioPlayerSession::doPauseL() +{ + DP0("S60AudioPlayerSession::doPauseL +++"); + + m_player->Pause(); + + DP0("S60AudioPlayerSession::doPauseL ---"); +} + + +/*! + + Stop playing, and reset the play position to the beginning. +*/ + +void S60AudioPlayerSession::doStop() +{ + DP0("S60AudioPlayerSession::doStop +++"); + + m_player->Stop(); + + DP0("S60AudioPlayerSession::doStop ---"); +} + +/*! + Closes the current audio clip (allowing another clip to be opened) +*/ + +void S60AudioPlayerSession::doClose() +{ + DP0("S60AudioPlayerSession::doClose +++"); + +#ifdef HAS_AUDIOROUTING + if (m_audioOutput) { + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; + m_audioOutput = NULL; + } +#endif + m_player->Close(); + + DP0("S60AudioPlayerSession::doClose ---"); +} + +/*! + + Changes the current playback volume to specified \a value. +*/ + +void S60AudioPlayerSession::doSetVolumeL(int volume) +{ + DP0("S60AudioPlayerSession::doSetVolumeL +++"); + + DP1("S60AudioPlayerSession::doSetVolumeL, Volume:", volume); + + m_player->SetVolume(volume * m_player->MaxVolume() / 100); + + DP0("S60AudioPlayerSession::doSetVolumeL ---"); +} + +/*! + Sets the current playback position to \a microSeconds from the start of the clip. +*/ + +void S60AudioPlayerSession::doSetPositionL(qint64 microSeconds) +{ + DP0("S60AudioPlayerSession::doSetPositionL +++"); + + DP1("S60AudioPlayerSession::doSetPositionL, Microseconds:", microSeconds); + + m_player->SetPosition(TTimeIntervalMicroSeconds(microSeconds)); + + DP0("S60AudioPlayerSession::doSetPositionL ---"); +} + +/*! + + Updates meta data entries in the current audio clip. +*/ + +void S60AudioPlayerSession::updateMetaDataEntriesL() +{ + DP0("S60AudioPlayerSession::updateMetaDataEntriesL +++"); + + metaDataEntries().clear(); + int numberOfMetaDataEntries = 0; + + //User::LeaveIfError(m_player->GetNumberOfMetaDataEntries(numberOfMetaDataEntries)); + m_player->GetNumberOfMetaDataEntries(numberOfMetaDataEntries); + + for (int i = 0; i < numberOfMetaDataEntries; i++) { + CMMFMetaDataEntry *entry = NULL; + entry = m_player->GetMetaDataEntryL(i); + metaDataEntries().insert(QString::fromUtf16(entry->Name().Ptr(), entry->Name().Length()), QString::fromUtf16(entry->Value().Ptr(), entry->Value().Length())); + delete entry; + } + emit metaDataChanged(); + + DP0("S60AudioPlayerSession::updateMetaDataEntriesL ---"); +} + +/*! + Sets the playbackRate with \a rate. +*/ + +void S60AudioPlayerSession::setPlaybackRate(qreal rate) +{ + DP0("S60AudioPlayerSession::setPlaybackRate +++"); + DP1("S60AudioPlayerSession::setPlaybackRate, Rate:", rate); + /*Since AudioPlayerUtility doesn't support set playback rate hence + * setPlaybackRate emits playbackRateChanged signal for 1.0x ie normal playback. + * For all other playBackRates it sets and emits error signal. + */ + if (rate == 1.0) { + emit playbackRateChanged(rate); + return; + } else { + int err = KErrNotSupported; + setAndEmitError(err); + } + DP0("S60AudioPlayerSession::setPlaybackRate ---"); +} + +/*! + + Returns the percentage of the audio clip loaded. +*/ + +int S60AudioPlayerSession::doGetBufferStatusL() const +{ + DP0("S60AudioPlayerSession::doGetBufferStatusL +++"); + + int progress = 0; + m_player->GetAudioLoadingProgressL(progress); + + DP0("S60AudioPlayerSession::doGetBufferStatusL ---"); + + return progress; +} + +/*! + + Defines required client behaviour when an attempt to open and initialise an audio sample has completed, + successfully or not. + + \a aError if KErrNone the sample is ready to play or else system wide error. + + \a aDuration The duration of the audio sample. +*/ + +#ifdef S60_DRM_SUPPORTED +void S60AudioPlayerSession::MdapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration) +#else +void S60AudioPlayerSession::MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration) +#endif +{ + DP0("S60AudioPlayerSession::MdapcInitComplete +++"); + + DP1("S60AudioPlayerSession::MdapcInitComplete - aError", aError); + + Q_UNUSED(aDuration); + setError(aError); + if (KErrNone != aError) + return; +#ifdef HAS_AUDIOROUTING + TRAPD(err, + m_audioOutput = CAudioOutput::NewL(*m_player); + m_audioOutput->RegisterObserverL(*this); + ); + setActiveEndpoint(m_audioEndpoint); + setError(err); +#endif //HAS_AUDIOROUTING + if (KErrNone == aError) + loaded(); + + DP0("S60AudioPlayerSession::MdapcInitComplete ---"); +} + +/*! + Defines required client behaviour when an attempt to playback an audio sample has completed, + successfully or not. + + \a aError if KErrNone the playback completed or else system wide error. +*/ + + +#ifdef S60_DRM_SUPPORTED +void S60AudioPlayerSession::MdapcPlayComplete(TInt aError) +#else +void S60AudioPlayerSession::MapcPlayComplete(TInt aError) +#endif +{ + DP0("S60AudioPlayerSession::MdapcPlayComplete +++"); + + DP1("S60AudioPlayerSession::MdapcPlayComplete", aError); + + if (KErrNone == aError) + endOfMedia(); + else + setError(aError); + + DP0("S60AudioPlayerSession::MdapcPlayComplete ---"); +} + +/*! + Defiens which Audio End point to use. + + \a audioEndpoint audioEndpoint name. +*/ + +void S60AudioPlayerSession::doSetAudioEndpoint(const QString& audioEndpoint) +{ + DP0("S60AudioPlayerSession::doSetAudioEndpoint +++"); + + DP1("S60AudioPlayerSession::doSetAudioEndpoint - ", audioEndpoint); + + m_audioEndpoint = audioEndpoint; + + DP0("S60AudioPlayerSession::doSetAudioEndpoint ---"); +} + +/*! + + Returns audioEndpoint name. +*/ + +QString S60AudioPlayerSession::activeEndpoint() const +{ + DP0("S60AudioPlayerSession::activeEndpoint +++"); + + QString outputName = QString("Default"); +#ifdef HAS_AUDIOROUTING + if (m_audioOutput) { + CAudioOutput::TAudioOutputPreference output = m_audioOutput->AudioOutput(); + outputName = qStringFromTAudioOutputPreference(output); + } +#endif + DP1("S60AudioPlayerSession::activeEndpoint is :", outputName); + + DP0("S60AudioPlayerSession::activeEndpoint ---"); + return outputName; +} + +/*! + * Returns default Audio End point in use. +*/ + +QString S60AudioPlayerSession::defaultEndpoint() const +{ + DP0("S60AudioPlayerSession::defaultEndpoint +++"); + + QString outputName = QString("Default"); +#ifdef HAS_AUDIOROUTING + if (m_audioOutput) { + CAudioOutput::TAudioOutputPreference output = m_audioOutput->DefaultAudioOutput(); + outputName = qStringFromTAudioOutputPreference(output); + } +#endif + DP1("S60AudioPlayerSession::defaultEndpoint is :", outputName); + + DP0("S60AudioPlayerSession::defaultEndpoint ---"); + return outputName; +} + +/*! + Sets active end \a name as an Audio End point. +*/ + +void S60AudioPlayerSession::setActiveEndpoint(const QString& name) +{ + DP0("S60AudioPlayerSession::setActiveEndpoint +++"); + + DP1("S60AudioPlayerSession::setActiveEndpoint - ", name); + +#ifdef HAS_AUDIOROUTING + CAudioOutput::TAudioOutputPreference output = CAudioOutput::ENoPreference; + + if (name == QString("Default")) + output = CAudioOutput::ENoPreference; + else if (name == QString("All")) + output = CAudioOutput::EAll; + else if (name == QString("None")) + output = CAudioOutput::ENoOutput; + else if (name == QString("Earphone")) + output = CAudioOutput::EPrivate; + else if (name == QString("Speaker")) + output = CAudioOutput::EPublic; + + if (m_audioOutput) { + TRAPD(err, m_audioOutput->SetAudioOutputL(output)); + setError(err); + } +#endif + + DP0("S60AudioPlayerSession::setActiveEndpoint ---"); +} + +/*! + The default audio output has been changed. + + \a aAudioOutput Audio Output object. + + \a aNewDefault is CAudioOutput::TAudioOutputPreference. +*/ + + +#ifdef HAS_AUDIOROUTING +void S60AudioPlayerSession::DefaultAudioOutputChanged(CAudioOutput& aAudioOutput, + CAudioOutput::TAudioOutputPreference aNewDefault) +{ + DP0("S60AudioPlayerSession::DefaultAudioOutputChanged +++"); + + // Emit already implemented in setActiveEndpoint function + Q_UNUSED(aAudioOutput) + Q_UNUSED(aNewDefault) + + DP0("S60AudioPlayerSession::DefaultAudioOutputChanged ---"); +} + + +/*! + Converts CAudioOutput::TAudioOutputPreference enum to QString. + + \a output is CAudioOutput::TAudioOutputPreference enum value. + +*/ + +QString S60AudioPlayerSession::qStringFromTAudioOutputPreference(CAudioOutput::TAudioOutputPreference output) const +{ + DP0("S60AudioPlayerSession::qStringFromTAudioOutputPreference"); + + if (output == CAudioOutput::ENoPreference) + return QString("Default"); + else if (output == CAudioOutput::EAll) + return QString("All"); + else if (output == CAudioOutput::ENoOutput) + return QString("None"); + else if (output == CAudioOutput::EPrivate) + return QString("Earphone"); + else if (output == CAudioOutput::EPublic) + return QString("Speaker"); + return QString("Default"); +} +#endif + +/*! + Return True if its Seekable or else False. +*/ + +bool S60AudioPlayerSession::getIsSeekable() const +{ + DP0("S60AudioPlayerSession::getIsSeekable"); + + return ETrue; +} + diff --git a/src/plugins/symbian/mmf/mediaplayer/s60audioplayersession.h b/src/plugins/symbian/mmf/mediaplayer/s60audioplayersession.h new file mode 100644 index 000000000..80228ef0a --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60audioplayersession.h @@ -0,0 +1,136 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60AUDIOPLAYERSESSION_H +#define S60AUDIOPLAYERSESSION_H + +#include +#include "s60mediaplayersession.h" + +#ifdef S60_DRM_SUPPORTED +#include +typedef CDrmPlayerUtility CAudioPlayer; +typedef MDrmAudioPlayerCallback MAudioPlayerObserver; +#else +#include +typedef CMdaAudioPlayerUtility CAudioPlayer; +typedef MMdaAudioPlayerCallback MAudioPlayerObserver; +#endif + +#ifdef HAS_AUDIOROUTING +#include +#include +#endif //HAS_AUDIOROUTING + +class S60AudioPlayerSession : public S60MediaPlayerSession + , public MAudioPlayerObserver + , public MAudioLoadingObserver +#ifdef HAS_AUDIOROUTING + , public MAudioOutputObserver +#endif +{ + Q_OBJECT +public: + S60AudioPlayerSession(QObject *parent); + ~S60AudioPlayerSession(); + + //From S60MediaPlayerSession + bool isVideoAvailable(); + bool isAudioAvailable(); + + // From MAudioLoadingObserver + void MaloLoadingStarted(); + void MaloLoadingComplete(); + +#ifdef HAS_AUDIOROUTING + // From MAudioOutputObserver + void DefaultAudioOutputChanged( CAudioOutput& aAudioOutput, + CAudioOutput::TAudioOutputPreference aNewDefault ); +#endif //HAS_AUDIOROUTING + +public: + // From S60MediaPlayerAudioEndpointSelector + QString activeEndpoint() const; + QString defaultEndpoint() const; + void setPlaybackRate(qreal rate); +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +protected: + //From S60MediaPlayerSession + void doLoadL(const TDesC &path); + void doLoadUrlL(const TDesC &path){Q_UNUSED(path)/*empty implementation*/} + void doPlay(); + void doStop(); + void doClose(); + void doPauseL(); + void doSetVolumeL(int volume); + qint64 doGetPositionL() const; + void doSetPositionL(qint64 microSeconds); + void updateMetaDataEntriesL(); + int doGetBufferStatusL() const; + qint64 doGetDurationL() const; + void doSetAudioEndpoint(const QString& audioEndpoint); + bool getIsSeekable() const; +private: +#ifdef S60_DRM_SUPPORTED + // From MMdaAudioPlayerCallback + void MdapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration); + void MdapcPlayComplete(TInt aError); +#else + // From MDrmAudioPlayerCallback + void MapcInitComplete(TInt aError, const TTimeIntervalMicroSeconds& aDuration); + void MapcPlayComplete(TInt aError); +#endif + +#ifdef HAS_AUDIOROUTING + QString qStringFromTAudioOutputPreference(CAudioOutput::TAudioOutputPreference output) const; +#endif //HAS_AUDIOROUTING + +private: + CAudioPlayer *m_player; +#ifdef HAS_AUDIOROUTING + CAudioOutput *m_audioOutput; +#endif //HAS_AUDIOROUTING + QString m_audioEndpoint; +}; + +#endif diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.cpp new file mode 100644 index 000000000..85bfe65a3 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.cpp @@ -0,0 +1,146 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60mediametadataprovider.h" +#include "s60mediaplayercontrol.h" +#include "s60mediaplayersession.h" +#include + +/*! + * Typecasts the \a control object to S60MediaPlayerControl object. +*/ +S60MediaMetaDataProvider::S60MediaMetaDataProvider(QObject *control, QObject *parent) + : QMetaDataReaderControl(parent) + , m_control(NULL) +{ + DP0("S60MediaMetaDataProvider::S60MediaMetaDataProvider +++"); + + m_control = qobject_cast(control); + + DP0("S60MediaMetaDataProvider::S60MediaMetaDataProvider ---"); +} + +/*! + * Destructor +*/ + +S60MediaMetaDataProvider::~S60MediaMetaDataProvider() +{ + DP0("S60MediaMetaDataProvider::~S60MediaMetaDataProvider +++"); + DP0("S60MediaMetaDataProvider::~S60MediaMetaDataProvider ---"); +} + +/*! + * Returns TRUE if MetaData is Available or else FALSE. +*/ + +bool S60MediaMetaDataProvider::isMetaDataAvailable() const +{ + DP0("S60MediaMetaDataProvider::isMetaDataAvailable"); + + if (m_control->session()) + return m_control->session()->isMetadataAvailable(); + return false; +} + +/*! + * Always returns FLASE. +*/ +bool S60MediaMetaDataProvider::isWritable() const +{ + DP0("S60MediaMetaDataProvider::isWritable"); + + return false; +} + +/*! + * Returns when \a key meta data is found in metaData. +*/ + +QVariant S60MediaMetaDataProvider::metaData(QtMultimediaKit::MetaData key) const +{ + DP0("S60MediaMetaDataProvider::metaData"); + + if (m_control->session()) + return m_control->session()->metaData(key); + return QVariant(); +} + +/*! + * Returns available metaData. +*/ + +QList S60MediaMetaDataProvider::availableMetaData() const +{ + DP0("S60MediaMetaDataProvider::availableMetaData"); + + if (m_control->session()) + return m_control->session()->availableMetaData(); + return QList(); +} + +/*! + * Returns when \a key string is found in extended metaData. +*/ + +QVariant S60MediaMetaDataProvider::extendedMetaData(const QString &key) const +{ + DP0("S60MediaMetaDataProvider::extendedMetaData"); + + if (m_control->session()) + return m_control->session()->metaData(key); + return QVariant(); +} + +/*! + * Returns available Extended MetaData. +*/ + +QStringList S60MediaMetaDataProvider::availableExtendedMetaData() const +{ + DP0("S60MediaMetaDataProvider::availableExtendedMetaData"); + + if (m_control->session()) + return m_control->session()->availableExtendedMetaData(); + return QStringList(); +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.h b/src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.h new file mode 100644 index 000000000..eb995080d --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediametadataprovider.h @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIAMETADATAPROVIDER_H +#define S60MEDIAMETADATAPROVIDER_H + +#include +#include "s60mediaplayercontrol.h" + +QT_USE_NAMESPACE + +class S60MediaPlayerControl; + +class S60MediaMetaDataProvider : public QMetaDataReaderControl +{ + Q_OBJECT + +public: + S60MediaMetaDataProvider(QObject *control, QObject *parent = 0); + ~S60MediaMetaDataProvider(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList availableMetaData() const; + QVariant extendedMetaData(const QString &key) const ; + QStringList availableExtendedMetaData() const; + +private: + S60MediaPlayerControl *m_control; +}; + +#endif // S60VIDEOMETADATAPROVIDER_H diff --git a/src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.cpp b/src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.cpp new file mode 100644 index 000000000..dad69a691 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.cpp @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60medianetworkaccesscontrol.h" + +#define KBuffersize 512 + +S60MediaNetworkAccessControl::S60MediaNetworkAccessControl(QObject *parent) + : QMediaNetworkAccessControl(parent) + , m_iapId(KUseDefaultIap) + , m_currentIndex(0) +{ +} + +void S60MediaNetworkAccessControl::accessPointChanged(int id) +{ + if (!m_IapIdList.isEmpty()) + m_NetworkObject = m_NetworkObjectList.at(m_IapIdList.indexOf(id)); + emit configurationChanged(m_NetworkObject); +} + +S60MediaNetworkAccessControl::~S60MediaNetworkAccessControl() +{ + m_NetworkObjectList.clear(); + m_IapIdList.clear(); +} + +void S60MediaNetworkAccessControl::resetIndex() +{ + m_currentIndex = 0; +} + +void S60MediaNetworkAccessControl::setConfigurations(const QList &configurations) +{ + if (!configurations.isEmpty()) { + m_currentIndex =0; + TRAPD(error, retriveAccesspointIDL(configurations)); + if (error != KErrNone) { + m_NetworkObjectList.clear(); + m_IapIdList.clear(); + } + } +} + +TBool S60MediaNetworkAccessControl::isLastAccessPoint() +{ + if (m_currentIndex == m_NetworkObjectList.size()) + return TRUE; + else + return FALSE; +} + +int S60MediaNetworkAccessControl::accessPointId() +{ + if (m_IapIdList.isEmpty()) + return m_iapId; + + m_iapId = m_IapIdList.at(m_currentIndex); + + if (isLastAccessPoint()) + m_currentIndex = 0; + else + m_currentIndex ++; + + return m_iapId; +} + +QNetworkConfiguration S60MediaNetworkAccessControl::currentConfiguration() const +{ + return m_NetworkObject; +} + +void S60MediaNetworkAccessControl::retriveAccesspointIDL(const QList &configurationList) +{ + m_NetworkObjectList.clear(); + m_IapIdList.clear(); + TBuf iapName; + TUint32 iapId; + TInt err; + + // open the IAP communications database + CCommsDatabase* commDB = CCommsDatabase::NewL(); + CleanupStack::PushL(commDB); + + // Open the IAP table + CCommsDbTableView* view = commDB->OpenTableLC(TPtrC(IAP)); + + for (int i=0;i<=configurationList.size()- 1;i++) { + QString accesspointname = configurationList.at(i).name(); + TBuf accesspointbuffer(accesspointname.utf16()); + // Point to the first entry + if (view->GotoFirstRecord() == KErrNone) { + do { + view->ReadTextL(TPtrC(COMMDB_NAME), iapName); + view->ReadUintL(TPtrC(COMMDB_ID), iapId); + if (accesspointbuffer == iapName) { + m_NetworkObjectList<GotoNextRecord(), err == KErrNone); + } + } + CleanupStack::PopAndDestroy(); // view + CleanupStack::PopAndDestroy(); // commDB +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.h b/src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.h new file mode 100644 index 000000000..aea4dcab5 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60medianetworkaccesscontrol.h @@ -0,0 +1,89 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIANETWORKACCESSCONTROL_H_ +#define S60MEDIANETWORKACCESSCONTROL_H_ + + +#include +#include +#include +#include +#include +#include +#include +#include +#include +#include "s60mediaplayercontrol.h" + +QT_BEGIN_NAMESPACE +class QMediaPlayerControl; +class QMediaNetworkAccessControl; +class QNetworkConfiguration; +QT_END_NAMESPACE + +class S60MediaNetworkAccessControl : public QMediaNetworkAccessControl +{ + Q_OBJECT + +public: + + S60MediaNetworkAccessControl(QObject *parent = 0); + ~S60MediaNetworkAccessControl(); + + virtual void setConfigurations(const QList &configurations); + virtual QNetworkConfiguration currentConfiguration() const; + int accessPointId(); + TBool isLastAccessPoint(); + void resetIndex(); + +public Q_SLOTS: + void accessPointChanged(int); + +private: + void retriveAccesspointIDL(const QList &); + QList m_IapIdList; + QList m_NetworkObjectList; + QNetworkConfiguration m_NetworkObject; + int m_iapId; + int m_currentIndex; +}; +#endif /* S60MEDIANETWORKACCESSCONTROL_H_ */ diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.cpp new file mode 100644 index 000000000..3ad64ef3b --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60mediaplayercontrol.h" +#include "s60mediaplayersession.h" +#include "s60mediaplayeraudioendpointselector.h" + +#include +#include + +/*! + Constructs a new audio endpoint selector with the given \a parent. +*/ + +S60MediaPlayerAudioEndpointSelector::S60MediaPlayerAudioEndpointSelector(QObject *control, QObject *parent) + :QAudioEndpointSelector(parent) + , m_control(0) +{ + DP0("S60MediaPlayerAudioEndpointSelector::S60MediaPlayerAudioEndpointSelector +++"); + + m_control = qobject_cast(control); + m_audioEndpointNames.append("Default"); + m_audioEndpointNames.append("All"); + m_audioEndpointNames.append("None"); + m_audioEndpointNames.append("Earphone"); + m_audioEndpointNames.append("Speaker"); + + DP0("S60MediaPlayerAudioEndpointSelector::S60MediaPlayerAudioEndpointSelector ---"); +} + +/*! + Destroys an audio endpoint selector. +*/ + +S60MediaPlayerAudioEndpointSelector::~S60MediaPlayerAudioEndpointSelector() +{ + DP0("S60MediaPlayerAudioEndpointSelector::~S60MediaPlayerAudioEndpointSelector +++"); + DP0("S60MediaPlayerAudioEndpointSelector::~S60MediaPlayerAudioEndpointSelector ---"); +} + +/*! + \return a list of available audio endpoints. +*/ + +QList S60MediaPlayerAudioEndpointSelector::availableEndpoints() const +{ + DP0("S60MediaPlayerAudioEndpointSelector::availableEndpoints"); + + return m_audioEndpointNames; +} + +/*! + \return the description of the endpoint name. +*/ + +QString S60MediaPlayerAudioEndpointSelector::endpointDescription(const QString& name) const +{ + DP0("S60MediaPlayerAudioEndpointSelector::endpointDescription"); + + if (name == QString("Default")) //ENoPreference + return QString("Used to indicate that the playing audio can be routed to" + "any speaker. This is the default value for audio."); + else if (name == QString("All")) //EAll + return QString("Used to indicate that the playing audio should be routed to all speakers."); + else if (name == QString("None")) //ENoOutput + return QString("Used to indicate that the playing audio should not be routed to any output."); + else if (name == QString("Earphone")) //EPrivate + return QString("Used to indicate that the playing audio should be routed to" + "the default private speaker. A private speaker is one that can only" + "be heard by one person."); + else if (name == QString("Speaker")) //EPublic + return QString("Used to indicate that the playing audio should be routed to" + "the default public speaker. A public speaker is one that can " + "be heard by multiple people."); + + return QString(); +} + +/*! + \return the name of the currently selected audio endpoint. +*/ + +QString S60MediaPlayerAudioEndpointSelector::activeEndpoint() const +{ + DP0("S60MediaPlayerAudioEndpointSelector::activeEndpoint"); + + if (m_control->session()) { + DP1("S60MediaPlayerAudioEndpointSelector::activeEndpoint - ", + m_control->session()->activeEndpoint()); + return m_control->session()->activeEndpoint(); + } + else { + DP1("S60MediaPlayerAudioEndpointSelector::activeEndpoint - ", + m_control->mediaControlSettings().audioEndpoint()); + return m_control->mediaControlSettings().audioEndpoint(); + } +} + +/*! + \return the name of the default audio endpoint. +*/ + +QString S60MediaPlayerAudioEndpointSelector::defaultEndpoint() const +{ + DP0("S60MediaPlayerAudioEndpointSelector::defaultEndpoint"); + + if (m_control->session()) { + DP1("S60MediaPlayerAudioEndpointSelector::defaultEndpoint - ", + m_control->session()->defaultEndpoint()); + return m_control->session()->defaultEndpoint(); + } + else { + DP1("S60MediaPlayerAudioEndpointSelector::defaultEndpoint - ", + m_control->mediaControlSettings().audioEndpoint()); + return m_control->mediaControlSettings().audioEndpoint(); + } +} + +/*! + Set the audio endpoint to \a name. +*/ + +void S60MediaPlayerAudioEndpointSelector::setActiveEndpoint(const QString& name) +{ + DP0("S60MediaPlayerAudioEndpointSelector::setActiveEndpoin +++"); + + DP1("S60MediaPlayerAudioEndpointSelector::setActiveEndpoint - ", name); + + QString oldEndpoint = m_control->mediaControlSettings().audioEndpoint(); + + if (name != oldEndpoint && (name == QString("Default") || name == QString("All") || + name == QString("None") || name == QString("Earphone") || name == QString("Speaker"))) { + + if (m_control->session()) { + m_control->session()->setActiveEndpoint(name); + emit activeEndpointChanged(name); + } + m_control->setAudioEndpoint(name); + } + + DP0("S60MediaPlayerAudioEndpointSelector::setActiveEndpoin ---"); +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.h b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.h new file mode 100644 index 000000000..eff49d47a --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayeraudioendpointselector.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIAPLAYERAUDIOENDPOINTSELECTOR_H +#define S60MEDIAPLAYERAUDIOENDPOINTSELECTOR_H + +#include + +#include + +QT_USE_NAMESPACE + +class S60MediaPlayerControl; +class S60MediaPlayerSession; + +class S60MediaPlayerAudioEndpointSelector : public QAudioEndpointSelector +{ + +Q_OBJECT + +public: + S60MediaPlayerAudioEndpointSelector(QObject *control, QObject *parent = 0); + ~S60MediaPlayerAudioEndpointSelector(); + + QList availableEndpoints() const ; + QString endpointDescription(const QString& name) const; + QString defaultEndpoint() const; + QString activeEndpoint() const; + +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +private: + S60MediaPlayerControl* m_control; + QString m_audioInput; + QList m_audioEndpointNames; +}; + +#endif // S60MEDIAPLAYERAUDIOENDPOINTSELECTOR_H diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.cpp new file mode 100644 index 000000000..2eeceedd8 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.cpp @@ -0,0 +1,518 @@ + +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60mediaplayercontrol.h" +#include "s60mediaplayersession.h" + +#include +#include +#include + +/*! + Constructs a new media player control with the given \a parent. +*/ + +S60MediaPlayerControl::S60MediaPlayerControl(MS60MediaPlayerResolver& mediaPlayerResolver, QObject *parent) + : QMediaPlayerControl(parent), + m_mediaPlayerResolver(mediaPlayerResolver), + m_session(NULL), + m_stream(NULL) +{ + DP0("S60MediaPlayerControl::S60MediaPlayerControl +++"); + + DP0("S60MediaPlayerControl::S60MediaPlayerControl ---"); + +} + +/*! + Destroys a media player control. +*/ + +S60MediaPlayerControl::~S60MediaPlayerControl() +{ + DP0("S60MediaPlayerControl::~S60MediaPlayerControl +++"); + DP0("S60MediaPlayerControl::~S60MediaPlayerControl ---"); +} + +/*! + \return the current playback position in milliseconds. +*/ + +qint64 S60MediaPlayerControl::position() const +{ + // DP0("S60MediaPlayerControl::position"); + + if (m_session) + return m_session->position(); + return 0; +} + +/*! + \return the duration of the current media in milliseconds. +*/ + +qint64 S60MediaPlayerControl::duration() const +{ + // DP0("S60MediaPlayerControl::duration"); + + if (m_session) + return m_session->duration(); + return -1; +} + +/*! + \return the state of a player control. +*/ + +QMediaPlayer::State S60MediaPlayerControl::state() const +{ + DP0("S60MediaPlayerControl::state"); + + if (m_session) + return m_session->state(); + return QMediaPlayer::StoppedState; +} + +/*! + \return the status of the current media. +*/ + +QMediaPlayer::MediaStatus S60MediaPlayerControl::mediaStatus() const +{ + DP0("QMediaPlayer::mediaStatus"); + + if (m_session) + return m_session->mediaStatus(); + return m_mediaSettings.mediaStatus(); +} + +/*! + \return the buffering progress of the current media. Progress is measured in the percentage + of the buffer filled. +*/ + +int S60MediaPlayerControl::bufferStatus() const +{ + // DP0("S60MediaPlayerControl::bufferStatus"); + + if (m_session) + return m_session->bufferStatus(); + return 0; +} + +/*! + \return the audio volume of a player control. +*/ + +int S60MediaPlayerControl::volume() const +{ + DP0("S60MediaPlayerControl::volume"); + + if (m_session) + return m_session->volume(); + return m_mediaSettings.volume(); +} + +/*! + \return the mute state of a player control. +*/ + +bool S60MediaPlayerControl::isMuted() const +{ + DP0("S60MediaPlayerControl::isMuted"); + + if (m_session) + return m_session->isMuted(); + return m_mediaSettings.isMuted(); +} + +/*! + Identifies if the current media is seekable. + + \return true if it possible to seek within the current media, and false otherwise. +*/ + +bool S60MediaPlayerControl::isSeekable() const +{ + DP0("S60MediaPlayerControl::isSeekable"); + + if (m_session) + return m_session->isSeekable(); + return false; +} + +/*! + \return a range of times in milliseconds that can be played back. + + Usually for local files this is a continuous interval equal to [0..duration()] + or an empty time range if seeking is not supported, but for network sources + it refers to the buffered parts of the media. +*/ + +QMediaTimeRange S60MediaPlayerControl::availablePlaybackRanges() const +{ + DP0("S60MediaPlayerControl::availablePlaybackRanges"); + + QMediaTimeRange ranges; + + if(m_session && m_session->isSeekable()) + ranges.addInterval(0, m_session->duration()); + + return ranges; +} + +/*! + \return the rate of playback. +*/ + +qreal S60MediaPlayerControl::playbackRate() const +{ + DP0("S60MediaPlayerControl::playbackRate"); + + return m_mediaSettings.playbackRate(); +} + +/*! + Sets the \a rate of playback. +*/ + +void S60MediaPlayerControl::setPlaybackRate(qreal rate) +{ + DP0("S60MediaPlayerControl::setPlaybackRate +++"); + + DP1("S60MediaPlayerControl::setPlaybackRate - ", rate); + + //getting the current playbackrate + qreal currentPBrate = m_mediaSettings.playbackRate(); + //checking if we need to change the Playback rate + if (!qFuzzyCompare(currentPBrate,rate)) + { + if(m_session) + m_session->setPlaybackRate(rate); + + m_mediaSettings.setPlaybackRate(rate); + } + + DP0("S60MediaPlayerControl::setPlaybackRate ---"); +} + +/*! + Sets the playback \a pos of the current media. This will initiate a seek and it may take + some time for playback to reach the position set. +*/ + +void S60MediaPlayerControl::setPosition(qint64 pos) +{ + DP0("S60MediaPlayerControl::setPosition +++"); + + DP1("S60MediaPlayerControl::setPosition, Position:", pos); + + if (m_session) + m_session->setPosition(pos); + + DP0("S60MediaPlayerControl::setPosition ---"); +} + +/*! + Starts playback of the current media. + + If successful the player control will immediately enter the \l {QMediaPlayer::PlayingState} + {playing} state. +*/ + +void S60MediaPlayerControl::play() +{ + DP0("S60MediaPlayerControl::play +++"); + + if (m_session) + m_session->play(); + + DP0("S60MediaPlayerControl::play ---"); +} + +/*! + Pauses playback of the current media. + + If sucessful the player control will immediately enter the \l {QMediaPlayer::PausedState} + {paused} state. +*/ + +void S60MediaPlayerControl::pause() +{ + DP0("S60MediaPlayerControl::pause +++"); + + if (m_session) + m_session->pause(); + + DP0("S60MediaPlayerControl::pause ---"); +} + +/*! + Stops playback of the current media. + + If successful the player control will immediately enter the \l {QMediaPlayer::StoppedState} + {stopped} state. +*/ + +void S60MediaPlayerControl::stop() +{ + DP0("S60MediaPlayerControl::stop +++"); + + if (m_session) + m_session->stop(); + + DP0("S60MediaPlayerControl::stop ---"); +} + +/*! + Sets the audio \a volume of a player control. +*/ + +void S60MediaPlayerControl::setVolume(int volume) +{ + DP0("S60MediaPlayerControl::setVolume +++"); + + DP1("S60MediaPlayerControl::setVolume", volume); + + int boundVolume = qBound(0, volume, 100); + if (boundVolume == m_mediaSettings.volume()) + return; + + m_mediaSettings.setVolume(boundVolume); + + if (m_session) + m_session->setVolume(boundVolume); + + DP0("S60MediaPlayerControl::setVolume ---"); +} + +/*! + Sets the \a muted state of a player control. +*/ + +void S60MediaPlayerControl::setMuted(bool muted) +{ + DP0("S60MediaPlayerControl::setMuted +++"); + + DP1("S60MediaPlayerControl::setMuted - ", muted); + + if (m_mediaSettings.isMuted() == muted) + return; + + m_mediaSettings.setMuted(muted); + + if (m_session) + m_session->setMuted(muted); + + DP0("S60MediaPlayerControl::setMuted ---"); +} + +/*! + * \return the current media source. +*/ + +QMediaContent S60MediaPlayerControl::media() const +{ + DP0("S60MediaPlayerControl::media"); + + return m_currentResource; +} + +/*! + \return the current media stream. This is only a valid if a stream was passed to setMedia(). + + \sa setMedia() +*/ + +const QIODevice *S60MediaPlayerControl::mediaStream() const +{ + DP0("S60MediaPlayerControl::mediaStream"); + + return m_stream; +} + +/*! + Sets the current \a source media source. If a \a stream is supplied; data will be read from that + instead of attempting to resolve the media source. The media source may still be used to + supply media information such as mime type. + + Setting the media to a null QMediaContent will cause the control to discard all + information relating to the current media source and to cease all I/O operations related + to that media. +*/ + +void S60MediaPlayerControl::setMedia(const QMediaContent &source, QIODevice *stream) +{ + DP0("S60MediaPlayerControl::setMedia +++"); + + Q_UNUSED(stream) + + if ((m_session && m_currentResource == source) && m_session->isStreaming()) + { + m_session->load(source); + return; + } + + // we don't want to set & load media again when it is already loaded + if (m_session && m_currentResource == source) + return; + + // store to variable as session is created based on the content type. + m_currentResource = source; + S60MediaPlayerSession *newSession = m_mediaPlayerResolver.PlayerSession(); + m_mediaSettings.setMediaStatus(QMediaPlayer::UnknownMediaStatus); + + if (m_session) + m_session->reset(); + else { + emit mediaStatusChanged(QMediaPlayer::UnknownMediaStatus); + emit error(QMediaPlayer::NoError, QString()); + } + + m_session = newSession; + + if (m_session) + m_session->load(source); + else { + QMediaPlayer::MediaStatus status = (source.isNull()) ? QMediaPlayer::NoMedia : QMediaPlayer::InvalidMedia; + m_mediaSettings.setMediaStatus(status); + emit stateChanged(QMediaPlayer::StoppedState); + emit error((source.isNull()) ? QMediaPlayer::NoError : QMediaPlayer::ResourceError, + (source.isNull()) ? "" : tr("Media couldn't be resolved")); + emit mediaStatusChanged(status); + } + emit mediaChanged(m_currentResource); + + DP0("S60MediaPlayerControl::setMedia ---"); +} + +/*! + * \return media player session. +*/ +S60MediaPlayerSession* S60MediaPlayerControl::session() +{ + DP0("S60MediaPlayerControl::session"); + + return m_session; +} + +/*! + * Sets \a output as a VideoOutput. +*/ + +void S60MediaPlayerControl::setVideoOutput(QObject *output) +{ + DP0("S60MediaPlayerControl::setVideoOutput +++"); + + m_mediaPlayerResolver.VideoPlayerSession()->setVideoRenderer(output); + + DP0("S60MediaPlayerControl::setVideoOutput ---"); +} + +/*! + * \return TRUE if Audio available or else FALSE. +*/ + +bool S60MediaPlayerControl::isAudioAvailable() const +{ + DP0("S60MediaPlayerControl::isAudioAvailable"); + + if (m_session) + return m_session->isAudioAvailable(); + return false; +} + +/*! + * \return TRUE if Video available or else FALSE. +*/ + +bool S60MediaPlayerControl::isVideoAvailable() const +{ + DP0("S60MediaPlayerControl::isVideoAvailable"); + + if (m_session) + return m_session->isVideoAvailable(); + return false; +} + +/*! + * \return media settings. + * + * Media Settings include volume, muted, playbackRate, mediaStatus, audioEndpoint. +*/ +const S60MediaSettings& S60MediaPlayerControl::mediaControlSettings() const +{ + DP0("S60MediaPlayerControl::mediaControlSettings"); + return m_mediaSettings; +} + +/*! + * Set the audio endpoint to \a name. +*/ + +void S60MediaPlayerControl::setAudioEndpoint(const QString& name) +{ + DP0("S60MediaPlayerControl::setAudioEndpoint +++"); + + DP1("S60MediaPlayerControl::setAudioEndpoint - ", name); + + m_mediaSettings.setAudioEndpoint(name); + + DP0("S60MediaPlayerControl::setAudioEndpoint ---"); +} + +/*! + * Sets media type \a type as Unknown, Video, Audio, Data. +*/ + +void S60MediaPlayerControl::setMediaType(S60MediaSettings::TMediaType type) +{ + DP0("S60MediaPlayerControl::setMediaType +++"); + + DP1("S60MediaPlayerControl::setMediaType - ", type); + + m_mediaSettings.setMediaType(type); + + DP0("S60MediaPlayerControl::setMediaType ---"); +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.h b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.h new file mode 100644 index 000000000..caf6631a8 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayercontrol.h @@ -0,0 +1,148 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIAPLAYERCONTROL_H +#define S60MEDIAPLAYERCONTROL_H + +#include + +#include + +#include "ms60mediaplayerresolver.h" +#include + +QT_BEGIN_NAMESPACE +class QMediaPlayer; +class QMediaTimeRange; +class QMediaContent; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class S60MediaPlayerSession; +class S60MediaPlayerService; + +class S60MediaSettings +{ + +public: + S60MediaSettings() + : m_volume(30) + , m_muted(false) + , m_playbackRate(0) + , m_mediaStatus(QMediaPlayer::NoMedia) + , m_audioEndpoint(QString("Default")) + { + } + + enum TMediaType {Unknown, Video, Audio, Data}; + + void setVolume(int volume) { m_volume = volume; } + void setMuted(bool muted) { m_muted = muted; } + void setPlaybackRate(qreal rate) { m_playbackRate = rate; } + void setMediaStatus(QMediaPlayer::MediaStatus status) {m_mediaStatus=status;} + void setAudioEndpoint(const QString& audioEndpoint) { m_audioEndpoint = audioEndpoint; } + void setMediaType(S60MediaSettings::TMediaType type) { m_mediaType = type; } + + int volume() const { return m_volume; } + bool isMuted() const { return m_muted; } + qreal playbackRate() const { return m_playbackRate; } + QMediaPlayer::MediaStatus mediaStatus() const {return m_mediaStatus;} + QString audioEndpoint() const { return m_audioEndpoint; } + S60MediaSettings::TMediaType mediaType() const { return m_mediaType; } + +private: + int m_volume; + bool m_muted; + qreal m_playbackRate; + QMediaPlayer::MediaStatus m_mediaStatus; + QString m_audioEndpoint; + S60MediaSettings::TMediaType m_mediaType; +}; + +class S60MediaPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT + +public: + S60MediaPlayerControl(MS60MediaPlayerResolver& mediaPlayerResolver, QObject *parent = 0); + ~S60MediaPlayerControl(); + + // from QMediaPlayerControl + virtual QMediaPlayer::State state() const; + virtual QMediaPlayer::MediaStatus mediaStatus() const; + virtual qint64 duration() const; + virtual qint64 position() const; + virtual void setPosition(qint64 pos); + virtual int volume() const; + virtual void setVolume(int volume); + virtual bool isMuted() const; + virtual void setMuted(bool muted); + virtual int bufferStatus() const; + virtual bool isAudioAvailable() const; + virtual bool isVideoAvailable() const; + virtual bool isSeekable() const; + virtual QMediaTimeRange availablePlaybackRanges() const; + virtual qreal playbackRate() const; + virtual void setPlaybackRate(qreal rate); + virtual QMediaContent media() const; + virtual const QIODevice *mediaStream() const; + virtual void setMedia(const QMediaContent&, QIODevice *); + virtual void play(); + virtual void pause(); + virtual void stop(); + + // Own methods + S60MediaPlayerSession* session(); + void setVideoOutput(QObject *output); + const S60MediaSettings& mediaControlSettings() const; + void setAudioEndpoint(const QString& name); + void setMediaType(S60MediaSettings::TMediaType type); + +private: + MS60MediaPlayerResolver &m_mediaPlayerResolver; + S60MediaPlayerSession *m_session; + QMediaContent m_currentResource; + QIODevice *m_stream; + S60MediaSettings m_mediaSettings; +}; + +#endif diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.cpp new file mode 100644 index 000000000..a1aabef90 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.cpp @@ -0,0 +1,326 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include +#include +#include + +#include "s60mediaplayerservice.h" +#include "s60mediaplayercontrol.h" +#include "s60videoplayersession.h" +#include "s60audioplayersession.h" +#include "s60mediametadataprovider.h" +#include "s60mediarecognizer.h" +#include "s60videowidgetcontrol.h" +#include "s60videowindowcontrol.h" +#ifdef HAS_VIDEORENDERERCONTROL_IN_VIDEOPLAYER +#include "s60videorenderer.h" +#endif +#include "s60mediaplayeraudioendpointselector.h" +#include "s60medianetworkaccesscontrol.h" +#include "s60mediastreamcontrol.h" + +#include +#include + +/*! + Construct a media service with the given \a parent. +*/ + +S60MediaPlayerService::S60MediaPlayerService(QObject *parent) + : QMediaService(parent) + , m_control(NULL) + , m_videoPlayerSession(NULL) + , m_audioPlayerSession(NULL) + , m_metaData(NULL) + , m_audioEndpointSelector(NULL) + , m_streamControl(NULL) + , m_networkAccessControl(NULL) + , m_videoOutput(NULL) +{ + DP0("S60MediaPlayerService::S60MediaPlayerService +++"); + + m_control = new S60MediaPlayerControl(*this, this); + m_metaData = new S60MediaMetaDataProvider(m_control, this); + m_audioEndpointSelector = new S60MediaPlayerAudioEndpointSelector(m_control, this); + m_streamControl = new S60MediaStreamControl(m_control, this); + m_networkAccessControl = new S60MediaNetworkAccessControl(this); + + DP0("S60MediaPlayerService::S60MediaPlayerService ---"); +} + +/*! + Destroys a media service. +*/ + +S60MediaPlayerService::~S60MediaPlayerService() +{ + DP0("S60MediaPlayerService::~S60MediaPlayerService +++"); + DP0("S60MediaPlayerService::~S60MediaPlayerService ---"); +} + +/*! + \return a pointer to the media control, which matches the controller \a name. + + If the service does not implement the control, or if it is unavailable a + null pointer is returned instead. + + Controls must be returned to the service when no longer needed using the + releaseControl() function. +*/ + +QMediaControl *S60MediaPlayerService::requestControl(const char *name) +{ + DP0("S60MediaPlayerService::requestControl"); + + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) + return m_control; + + if (qstrcmp(name, QMediaNetworkAccessControl_iid) == 0) + return m_networkAccessControl; + + if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) + return m_metaData; + + if (qstrcmp(name, QAudioEndpointSelector_iid) == 0) + return m_audioEndpointSelector; + + if (qstrcmp(name, QMediaStreamsControl_iid) == 0) + return m_streamControl; + + if (!m_videoOutput) { + if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + m_videoOutput = new S60VideoWidgetControl(this); + } +#ifdef HAS_VIDEORENDERERCONTROL_IN_VIDEOPLAYER + else if (qstrcmp(name, QVideoRendererControl_iid) == 0) { + m_videoOutput = new S60VideoRenderer(this); + } +#endif /* HAS_VIDEORENDERERCONTROL_IN_VIDEOPLAYER */ + else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + m_videoOutput = new S60VideoWindowControl(this); + } + + if (m_videoOutput) { + m_control->setVideoOutput(m_videoOutput); + return m_videoOutput; + } + }else { + if (qstrcmp(name, QVideoWidgetControl_iid) == 0 || +#ifdef HAS_VIDEORENDERERCONTROL_IN_VIDEOPLAYER + qstrcmp(name, QVideoRendererControl_iid) == 0 || +#endif /* HAS_VIDEORENDERERCONTROL_IN_VIDEOPLAYER */ + qstrcmp(name, QVideoWindowControl_iid) == 0){ + return m_videoOutput; + } + } + return 0; +} + +/*! + Releases a \a control back to the service. +*/ + +void S60MediaPlayerService::releaseControl(QMediaControl *control) +{ + DP0("S60MediaPlayerService::releaseControl ++"); + + if (control == m_videoOutput) { + m_videoOutput = 0; + m_control->setVideoOutput(m_videoOutput); + } + + DP0("S60MediaPlayerService::releaseControl --"); +} + +/*! + * \return media player session(audio playersesion/video playersession) + * by recognizing whether media is audio or video and sets it on media type. +*/ +S60MediaPlayerSession* S60MediaPlayerService::PlayerSession() +{ + DP0("S60MediaPlayerService::PlayerSession"); + + QUrl url = m_control->media().canonicalUrl(); + + if (url.isEmpty() == true) { + return NULL; + } + + QScopedPointer mediaRecognizer(new S60MediaRecognizer); + S60MediaRecognizer::MediaType mediaType = mediaRecognizer->mediaType(url); + mediaRecognizer.reset(); + + switch (mediaType) { + case S60MediaRecognizer::Video: + case S60MediaRecognizer::Url: { + m_control->setMediaType(S60MediaSettings::Video); + return VideoPlayerSession(); + } + case S60MediaRecognizer::Audio: { + m_control->setMediaType(S60MediaSettings::Audio); + return AudioPlayerSession(); + } + default: + m_control->setMediaType(S60MediaSettings::Unknown); + break; + } + + return NULL; +} + +/*! + * \return media playersession (videoplayersession). + * constructs the videoplayersession object and connects all the respective signals and slots. + * and initialises all the media settings. +*/ + +S60MediaPlayerSession* S60MediaPlayerService::VideoPlayerSession() +{ + DP0("S60MediaPlayerService::VideoPlayerSession +++"); + + if (!m_videoPlayerSession) { + m_videoPlayerSession = new S60VideoPlayerSession(this, m_networkAccessControl); + + connect(m_videoPlayerSession, SIGNAL(positionChanged(qint64)), + m_control, SIGNAL(positionChanged(qint64))); + connect(m_videoPlayerSession, SIGNAL(playbackRateChanged(qreal)), + m_control, SIGNAL(playbackRateChanged(qreal))); + connect(m_videoPlayerSession, SIGNAL(volumeChanged(int)), + m_control, SIGNAL(volumeChanged(int))); + connect(m_videoPlayerSession, SIGNAL(mutedChanged(bool)), + m_control, SIGNAL(mutedChanged(bool))); + connect(m_videoPlayerSession, SIGNAL(durationChanged(qint64)), + m_control, SIGNAL(durationChanged(qint64))); + connect(m_videoPlayerSession, SIGNAL(stateChanged(QMediaPlayer::State)), + m_control, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(m_videoPlayerSession, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + m_control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_videoPlayerSession,SIGNAL(bufferStatusChanged(int)), + m_control, SIGNAL(bufferStatusChanged(int))); + connect(m_videoPlayerSession, SIGNAL(videoAvailableChanged(bool)), + m_control, SIGNAL(videoAvailableChanged(bool))); + connect(m_videoPlayerSession, SIGNAL(audioAvailableChanged(bool)), + m_control, SIGNAL(audioAvailableChanged(bool))); + connect(m_videoPlayerSession, SIGNAL(seekableChanged(bool)), + m_control, SIGNAL(seekableChanged(bool))); + connect(m_videoPlayerSession, SIGNAL(availablePlaybackRangesChanged(const QMediaTimeRange&)), + m_control, SIGNAL(availablePlaybackRangesChanged(const QMediaTimeRange&))); + connect(m_videoPlayerSession, SIGNAL(error(int, const QString &)), + m_control, SIGNAL(error(int, const QString &))); + connect(m_videoPlayerSession, SIGNAL(metaDataChanged()), + m_metaData, SIGNAL(metaDataChanged())); + connect(m_videoPlayerSession, SIGNAL(activeEndpointChanged(const QString&)), + m_audioEndpointSelector, SIGNAL(activeEndpointChanged(const QString&))); + connect(m_videoPlayerSession, SIGNAL(mediaChanged()), + m_streamControl, SLOT(handleStreamsChanged())); + connect(m_videoPlayerSession, SIGNAL(accessPointChanged(int)), + m_networkAccessControl, SLOT(accessPointChanged(int))); + + } + + m_videoPlayerSession->setVolume(m_control->mediaControlSettings().volume()); + m_videoPlayerSession->setMuted(m_control->mediaControlSettings().isMuted()); + m_videoPlayerSession->setAudioEndpoint(m_control->mediaControlSettings().audioEndpoint()); + + DP0("S60MediaPlayerService::VideoPlayerSession ---"); + + return m_videoPlayerSession; +} + +/*! + * \return media playersession (audioplayersession). + * constructs the audioplayersession object and connects all the respective signals and slots. + * and initialises all the media settings. +*/ + +S60MediaPlayerSession* S60MediaPlayerService::AudioPlayerSession() +{ + DP0("S60MediaPlayerService::AudioPlayerSession +++"); + + if (!m_audioPlayerSession) { + m_audioPlayerSession = new S60AudioPlayerSession(this); + + connect(m_audioPlayerSession, SIGNAL(positionChanged(qint64)), + m_control, SIGNAL(positionChanged(qint64))); + connect(m_audioPlayerSession, SIGNAL(playbackRateChanged(qreal)), + m_control, SIGNAL(playbackRateChanged(qreal))); + connect(m_audioPlayerSession, SIGNAL(volumeChanged(int)), + m_control, SIGNAL(volumeChanged(int))); + connect(m_audioPlayerSession, SIGNAL(mutedChanged(bool)), + m_control, SIGNAL(mutedChanged(bool))); + connect(m_audioPlayerSession, SIGNAL(durationChanged(qint64)), + m_control, SIGNAL(durationChanged(qint64))); + connect(m_audioPlayerSession, SIGNAL(stateChanged(QMediaPlayer::State)), + m_control, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(m_audioPlayerSession, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + m_control, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(m_audioPlayerSession,SIGNAL(bufferStatusChanged(int)), + m_control, SIGNAL(bufferStatusChanged(int))); + connect(m_audioPlayerSession, SIGNAL(videoAvailableChanged(bool)), + m_control, SIGNAL(videoAvailableChanged(bool))); + connect(m_audioPlayerSession, SIGNAL(audioAvailableChanged(bool)), + m_control, SIGNAL(audioAvailableChanged(bool))); + connect(m_audioPlayerSession, SIGNAL(seekableChanged(bool)), + m_control, SIGNAL(seekableChanged(bool))); + connect(m_audioPlayerSession, SIGNAL(availablePlaybackRangesChanged(const QMediaTimeRange&)), + m_control, SIGNAL(availablePlaybackRangesChanged(const QMediaTimeRange&))); + connect(m_audioPlayerSession, SIGNAL(error(int, const QString &)), + m_control, SIGNAL(error(int, const QString &))); + connect(m_audioPlayerSession, SIGNAL(metaDataChanged()), + m_metaData, SIGNAL(metaDataChanged())); + connect(m_audioPlayerSession, SIGNAL(activeEndpointChanged(const QString&)), + m_audioEndpointSelector, SIGNAL(activeEndpointChanged(const QString&))); + connect(m_audioPlayerSession, SIGNAL(mediaChanged()), + m_streamControl, SLOT(handleStreamsChanged())); + + } + + m_audioPlayerSession->setVolume(m_control->mediaControlSettings().volume()); + m_audioPlayerSession->setMuted(m_control->mediaControlSettings().isMuted()); + m_audioPlayerSession->setAudioEndpoint(m_control->mediaControlSettings().audioEndpoint()); + + DP0("S60MediaPlayerService::AudioPlayerSession ---"); + + return m_audioPlayerSession; +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.h b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.h new file mode 100644 index 000000000..d45ad45cc --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayerservice.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOPLAYERSERVICE_H +#define S60VIDEOPLAYERSERVICE_H + +#include +#include + +#include "ms60mediaplayerresolver.h" +#include "s60mediaplayeraudioendpointselector.h" + +QT_BEGIN_NAMESPACE +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +QT_END_NAMESPACE + +QT_USE_NAMESPACE + +class S60VideoPlayerSession; +class S60AudioPlayerSession; +class S60MediaPlayerControl; +class S60MediaMetaDataProvider; +class S60MediaStreamControl; +class S60MediaRecognizer; + +class QMediaPlaylistNavigator; +class S60MediaNetworkAccessControl; + +class S60MediaPlayerService : public QMediaService, public MS60MediaPlayerResolver +{ + Q_OBJECT + +public: + + S60MediaPlayerService(QObject *parent = 0); + ~S60MediaPlayerService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *control); + +protected: // From MS60MediaPlayerResolver + S60MediaPlayerSession* PlayerSession(); + S60MediaPlayerSession* VideoPlayerSession(); + S60MediaPlayerSession* AudioPlayerSession(); + +private: + S60MediaPlayerControl *m_control; + S60VideoPlayerSession *m_videoPlayerSession; + S60AudioPlayerSession *m_audioPlayerSession; + S60MediaMetaDataProvider *m_metaData; + S60MediaPlayerAudioEndpointSelector *m_audioEndpointSelector; + S60MediaStreamControl *m_streamControl; + S60MediaNetworkAccessControl *m_networkAccessControl; + QMediaControl *m_videoOutput; +}; + +#endif diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.cpp new file mode 100644 index 000000000..49a840a2d --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.cpp @@ -0,0 +1,1054 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60mediaplayersession.h" + +#include +#include +#include +#include +#include +#include + +/*! + Construct a media playersession with the given \a parent. +*/ + +S60MediaPlayerSession::S60MediaPlayerSession(QObject *parent) + : QObject(parent) + , m_stream(false) + , m_playbackRate(0) + , m_muted(false) + , m_volume(0) + , m_state(QMediaPlayer::StoppedState) + , m_mediaStatus(QMediaPlayer::NoMedia) + , m_progressTimer(new QTimer(this)) + , m_stalledTimer(new QTimer(this)) + , m_error(KErrNone) + , m_play_requested(false) + , m_seekable(true) +{ + DP0("S60MediaPlayerSession::S60MediaPlayerSession +++"); + + connect(m_progressTimer, SIGNAL(timeout()), this, SLOT(tick())); + connect(m_stalledTimer, SIGNAL(timeout()), this, SLOT(stalled())); + + DP0("S60MediaPlayerSession::S60MediaPlayerSession ---"); +} + +/*! + Destroys a media playersession. +*/ + +S60MediaPlayerSession::~S60MediaPlayerSession() +{ + DP0("S60MediaPlayerSession::~S60MediaPlayerSession +++"); + DP0("S60MediaPlayerSession::~S60MediaPlayerSession ---"); +} + +/*! + * \return the audio volume of a player session. +*/ +int S60MediaPlayerSession::volume() const +{ + DP1("S60MediaPlayerSession::volume", m_volume); + + return m_volume; +} + +/*! + Sets the audio \a volume of a player session. +*/ + +void S60MediaPlayerSession::setVolume(int volume) +{ + DP0("S60MediaPlayerSession::setVolume +++"); + + DP1("S60MediaPlayerSession::setVolume - ", volume); + + if (m_volume == volume) + return; + + m_volume = volume; + emit volumeChanged(m_volume); + + // Dont set symbian players volume until media loaded. + // Leaves with KerrNotReady although documentation says otherwise. + if (!m_muted && + ( mediaStatus() == QMediaPlayer::LoadedMedia + || (mediaStatus() == QMediaPlayer::StalledMedia && state() != QMediaPlayer::StoppedState) + || mediaStatus() == QMediaPlayer::BufferingMedia + || mediaStatus() == QMediaPlayer::BufferedMedia + || mediaStatus() == QMediaPlayer::EndOfMedia)) { + TRAPD(err, doSetVolumeL(m_volume)); + setError(err); + } + DP0("S60MediaPlayerSession::setVolume ---"); +} + +/*! + \return the mute state of a player session. +*/ + +bool S60MediaPlayerSession::isMuted() const +{ + DP1("S60MediaPlayerSession::isMuted", m_muted); + + return m_muted; +} + +/*! + Identifies if the current media is seekable. + + \return true if it possible to seek within the current media, and false otherwise. +*/ + +bool S60MediaPlayerSession::isSeekable() const +{ + DP1("S60MediaPlayerSession::isSeekable", m_seekable); + + return m_seekable; +} + +/*! + Sets the \a status of the current media. +*/ + +void S60MediaPlayerSession::setMediaStatus(QMediaPlayer::MediaStatus status) +{ + DP0("S60MediaPlayerSession::setMediaStatus +++"); + + if (m_mediaStatus == status) + return; + + m_mediaStatus = status; + + emit mediaStatusChanged(m_mediaStatus); + + if (m_play_requested && m_mediaStatus == QMediaPlayer::LoadedMedia) + play(); + + DP0("S60MediaPlayerSession::setMediaStatus ---"); +} + +/*! + Sets the \a state on media player. +*/ + +void S60MediaPlayerSession::setState(QMediaPlayer::State state) +{ + DP0("S60MediaPlayerSession::setState +++"); + + if (m_state == state) + return; + + m_state = state; + emit stateChanged(m_state); + + DP0("S60MediaPlayerSession::setState ---"); +} + +/*! + \return the state of a player session. +*/ + +QMediaPlayer::State S60MediaPlayerSession::state() const +{ + DP1("S60MediaPlayerSession::state", m_state); + + return m_state; +} + +/*! + \return the status of the current media. +*/ + +QMediaPlayer::MediaStatus S60MediaPlayerSession::mediaStatus() const +{ + DP1("S60MediaPlayerSession::mediaStatus", m_mediaStatus); + + return m_mediaStatus; +} + +/*! + * Loads the \a url for playback. + * If \a url is local file then it loads audio playersesion if its audio file. + * If it is a local video file then loads the video playersession. +*/ + +void S60MediaPlayerSession::load(const QMediaContent source) +{ + DP0("S60MediaPlayerSession::load +++"); + + m_source = source; + setMediaStatus(QMediaPlayer::LoadingMedia); + startStalledTimer(); + m_stream = (source.canonicalUrl().scheme() == "file")?false:true; + m_UrlPath = source.canonicalUrl(); + TRAPD(err, + if (m_stream) + doLoadUrlL(QString2TPtrC(source.canonicalUrl().toString())); + else + doLoadL(QString2TPtrC(QDir::toNativeSeparators(source.canonicalUrl().toLocalFile())))); + setError(err); + + DP0("S60MediaPlayerSession::load ---"); +} + +TBool S60MediaPlayerSession::isStreaming() +{ + return m_stream; +} + +/*! + Start or resume playing the current source. +*/ +void S60MediaPlayerSession::play() +{ + DP0("S60MediaPlayerSession::play +++"); + + if ( (state() == QMediaPlayer::PlayingState && m_play_requested == false) + || mediaStatus() == QMediaPlayer::UnknownMediaStatus + || mediaStatus() == QMediaPlayer::NoMedia + || mediaStatus() == QMediaPlayer::InvalidMedia) + return; + + setState(QMediaPlayer::PlayingState); + + if (mediaStatus() == QMediaPlayer::LoadingMedia || + (mediaStatus() == QMediaPlayer::StalledMedia && + state() == QMediaPlayer::StoppedState)) + { + m_play_requested = true; + return; + } + + m_play_requested = false; + m_duration = duration(); + setVolume(m_volume); + setMuted(m_muted); + startProgressTimer(); + doPlay(); + + DP0("S60MediaPlayerSession::play ---"); +} + +/*! + Pause playing the current source. +*/ + +void S60MediaPlayerSession::pause() +{ + DP0("S60MediaPlayerSession::pause +++"); + + if (state() != QMediaPlayer::PlayingState) + return; + + if (mediaStatus() == QMediaPlayer::NoMedia || + mediaStatus() == QMediaPlayer::InvalidMedia) + return; + + setState(QMediaPlayer::PausedState); + stopProgressTimer(); + TRAP_IGNORE(doPauseL()); + m_play_requested = false; + + DP0("S60MediaPlayerSession::pause ---"); +} + +/*! + Stop playing, and reset the play position to the beginning. +*/ + +void S60MediaPlayerSession::stop() +{ + DP0("S60MediaPlayerSession::stop +++"); + + if (state() == QMediaPlayer::StoppedState) + return; + + m_play_requested = false; + m_state = QMediaPlayer::StoppedState; + if (mediaStatus() == QMediaPlayer::BufferingMedia || + mediaStatus() == QMediaPlayer::BufferedMedia || + mediaStatus() == QMediaPlayer::StalledMedia) + setMediaStatus(QMediaPlayer::LoadedMedia); + if (mediaStatus() == QMediaPlayer::LoadingMedia) + setMediaStatus(QMediaPlayer::UnknownMediaStatus); + stopProgressTimer(); + stopStalledTimer(); + doStop(); + emit positionChanged(0); + emit stateChanged(m_state); + + DP0("S60MediaPlayerSession::stop ---"); +} + +/*! + * Stops the playback and closes the controllers. + * And resets all the flags and status, state to default values. +*/ + +void S60MediaPlayerSession::reset() +{ + DP0("S60MediaPlayerSession::reset +++"); + + m_play_requested = false; + setError(KErrNone, QString(), true); + stopProgressTimer(); + stopStalledTimer(); + doStop(); + doClose(); + setState(QMediaPlayer::StoppedState); + setMediaStatus(QMediaPlayer::UnknownMediaStatus); + setPosition(0); + + DP0("S60MediaPlayerSession::reset ---"); +} + +/*! + * Sets \a renderer as video renderer. +*/ + +void S60MediaPlayerSession::setVideoRenderer(QObject *renderer) +{ + DP0("S60MediaPlayerSession::setVideoRenderer +++"); + + Q_UNUSED(renderer); + + DP0("S60MediaPlayerSession::setVideoRenderer ---"); +} + +/*! + * the percentage of the temporary buffer filled before playback begins. + + When the player object is buffering; this property holds the percentage of + the temporary buffer that is filled. The buffer will need to reach 100% + filled before playback can resume, at which time the MediaStatus will be + BufferedMedia. + + \sa mediaStatus() +*/ + +int S60MediaPlayerSession::bufferStatus() +{ + DP0("S60MediaPlayerSession::bufferStatus"); + + if (state() ==QMediaPlayer::StoppedState) + return 0; + + if( mediaStatus() == QMediaPlayer::LoadingMedia + || mediaStatus() == QMediaPlayer::UnknownMediaStatus + || mediaStatus() == QMediaPlayer::NoMedia + || mediaStatus() == QMediaPlayer::InvalidMedia) + return 0; + + int progress = 0; + TRAPD(err, progress = doGetBufferStatusL()); + // If buffer status query not supported by codec return 100 + // do not set error + if(err == KErrNotSupported) + return 100; + + setError(err); + return progress; +} + +/*! + * return TRUE if Meta data is available in current media source. +*/ + +bool S60MediaPlayerSession::isMetadataAvailable() const +{ + DP0("S60MediaPlayerSession::isMetadataAvailable"); + + return !m_metaDataMap.isEmpty(); +} + +/*! + * \return the \a key meta data. +*/ +QVariant S60MediaPlayerSession::metaData(const QString &key) const +{ + DP0("S60MediaPlayerSession::metaData (const QString &key) const"); + + return m_metaDataMap.value(key); +} + +/*! + * \return the \a key meta data as QString. +*/ + +QVariant S60MediaPlayerSession::metaData(QtMultimediaKit::MetaData key) const +{ + DP0("S60MediaPlayerSession::metaData (QtMultimediaKit::MetaData key) const"); + + return metaData(metaDataKeyAsString(key)); +} + +/*! + * \return List of all available meta data from current media source. +*/ + +QList S60MediaPlayerSession::availableMetaData() const +{ + DP0("S60MediaPlayerSession::availableMetaData +++"); + + QList metaDataTags; + if (isMetadataAvailable()) { + for (int i = QtMultimediaKit::Title; i <= QtMultimediaKit::ThumbnailImage; i++) { + QString metaDataItem = metaDataKeyAsString((QtMultimediaKit::MetaData)i); + if (!metaDataItem.isEmpty()) { + if (!metaData(metaDataItem).isNull()) { + metaDataTags.append((QtMultimediaKit::MetaData)i); + } + } + } + } + + DP0("S60MediaPlayerSession::availableMetaData ---"); + + return metaDataTags; +} + +/*! + * \return all available extended meta data of current media source. +*/ + +QStringList S60MediaPlayerSession::availableExtendedMetaData() const +{ + DP0("S60MediaPlayerSession::availableExtendedMetaData"); + + return m_metaDataMap.keys(); +} + +/*! + * \return meta data \a key as QString. +*/ + +QString S60MediaPlayerSession::metaDataKeyAsString(QtMultimediaKit::MetaData key) const +{ + DP1("S60MediaPlayerSession::metaDataKeyAsString", key); + + switch(key) { + case QtMultimediaKit::Title: return "title"; + case QtMultimediaKit::AlbumArtist: return "artist"; + case QtMultimediaKit::Comment: return "comment"; + case QtMultimediaKit::Genre: return "genre"; + case QtMultimediaKit::Year: return "year"; + case QtMultimediaKit::Copyright: return "copyright"; + case QtMultimediaKit::AlbumTitle: return "album"; + case QtMultimediaKit::Composer: return "composer"; + case QtMultimediaKit::TrackNumber: return "albumtrack"; + case QtMultimediaKit::AudioBitRate: return "audiobitrate"; + case QtMultimediaKit::VideoBitRate: return "videobitrate"; + case QtMultimediaKit::Duration: return "duration"; + case QtMultimediaKit::MediaType: return "contenttype"; + case QtMultimediaKit::CoverArtImage: return "attachedpicture"; + case QtMultimediaKit::SubTitle: // TODO: Find the matching metadata keys + case QtMultimediaKit::Description: + case QtMultimediaKit::Category: + case QtMultimediaKit::Date: + case QtMultimediaKit::UserRating: + case QtMultimediaKit::Keywords: + case QtMultimediaKit::Language: + case QtMultimediaKit::Publisher: + case QtMultimediaKit::ParentalRating: + case QtMultimediaKit::RatingOrganisation: + case QtMultimediaKit::Size: + case QtMultimediaKit::AudioCodec: + case QtMultimediaKit::AverageLevel: + case QtMultimediaKit::ChannelCount: + case QtMultimediaKit::PeakValue: + case QtMultimediaKit::SampleRate: + case QtMultimediaKit::Author: + case QtMultimediaKit::ContributingArtist: + case QtMultimediaKit::Conductor: + case QtMultimediaKit::Lyrics: + case QtMultimediaKit::Mood: + case QtMultimediaKit::TrackCount: + case QtMultimediaKit::CoverArtUrlSmall: + case QtMultimediaKit::CoverArtUrlLarge: + case QtMultimediaKit::Resolution: + case QtMultimediaKit::PixelAspectRatio: + case QtMultimediaKit::VideoFrameRate: + case QtMultimediaKit::VideoCodec: + case QtMultimediaKit::PosterUrl: + case QtMultimediaKit::ChapterNumber: + case QtMultimediaKit::Director: + case QtMultimediaKit::LeadPerformer: + case QtMultimediaKit::Writer: + case QtMultimediaKit::CameraManufacturer: + case QtMultimediaKit::CameraModel: + case QtMultimediaKit::Event: + case QtMultimediaKit::Subject: + default: + break; + } + + return QString(); +} + +/*! + Sets the \a muted state of a player session. +*/ + +void S60MediaPlayerSession::setMuted(bool muted) +{ + DP0("S60MediaPlayerSession::setMuted +++"); + DP1("S60MediaPlayerSession::setMuted - ", muted); + + m_muted = muted; + emit mutedChanged(m_muted); + + if( m_mediaStatus == QMediaPlayer::LoadedMedia + || (m_mediaStatus == QMediaPlayer::StalledMedia && state() != QMediaPlayer::StoppedState) + || m_mediaStatus == QMediaPlayer::BufferingMedia + || m_mediaStatus == QMediaPlayer::BufferedMedia + || m_mediaStatus == QMediaPlayer::EndOfMedia) { + TRAPD(err, doSetVolumeL((m_muted)?0:m_volume)); + setError(err); + } + DP0("S60MediaPlayerSession::setMuted ---"); +} + +/*! + \return the duration of the current media in milliseconds. +*/ + +qint64 S60MediaPlayerSession::duration() const +{ + // DP0("S60MediaPlayerSession::duration"); + + if( mediaStatus() == QMediaPlayer::LoadingMedia + || mediaStatus() == QMediaPlayer::UnknownMediaStatus + || mediaStatus() == QMediaPlayer::NoMedia + || (mediaStatus() == QMediaPlayer::StalledMedia && state() == QMediaPlayer::StoppedState) + || mediaStatus() == QMediaPlayer::InvalidMedia) + return -1; + + qint64 pos = 0; + TRAP_IGNORE(pos = doGetDurationL()); + return pos; +} + +/*! + \return the current playback position in milliseconds. +*/ + +qint64 S60MediaPlayerSession::position() const +{ + // DP0("S60MediaPlayerSession::position"); + + if( mediaStatus() == QMediaPlayer::LoadingMedia + || mediaStatus() == QMediaPlayer::UnknownMediaStatus + || mediaStatus() == QMediaPlayer::NoMedia + || (mediaStatus() == QMediaPlayer::StalledMedia && state() == QMediaPlayer::StoppedState) + || mediaStatus() == QMediaPlayer::InvalidMedia) + return 0; + + qint64 pos = 0; + TRAP_IGNORE(pos = doGetPositionL()); + if (!m_play_requested && pos ==0 + && mediaStatus() != QMediaPlayer::LoadedMedia) + return m_duration; + return pos; +} + +/*! + Sets the playback \a pos of the current media. This will initiate a seek and it may take + some time for playback to reach the position set. +*/ + +void S60MediaPlayerSession::setPosition(qint64 pos) +{ + DP0("S60MediaPlayerSession::setPosition +++"); + + DP1("S60MediaPlayerSession::setPosition - ", pos); + + if (position() == pos) + return; + + QMediaPlayer::State originalState = state(); + + if (originalState == QMediaPlayer::PlayingState) + pause(); + + TRAPD(err, doSetPositionL(pos * 1000)); + setError(err); + + if (err == KErrNone) { + if (mediaStatus() == QMediaPlayer::EndOfMedia) + setMediaStatus(QMediaPlayer::LoadedMedia); + } + else if (err == KErrNotSupported) { + m_seekable = false; + emit seekableChanged(m_seekable); + } + + if (originalState == QMediaPlayer::PlayingState) + play(); + + emit positionChanged(position()); + + DP0("S60MediaPlayerSession::setPosition ---"); +} + +/*! + * Set the audio endpoint to \a audioEndpoint. +*/ + +void S60MediaPlayerSession::setAudioEndpoint(const QString& audioEndpoint) +{ + DP0("S60MediaPlayerSession::setAudioEndpoint +++"); + + DP1("S60MediaPlayerSession::setAudioEndpoint - ", audioEndpoint); + + doSetAudioEndpoint(audioEndpoint); + + DP0("S60MediaPlayerSession::setAudioEndpoint ---"); +} + +/*! + * Loading of media source is completed. + * And ready for playback. Updates all the media status, state, settings etc. + * And emits the signals. +*/ + +void S60MediaPlayerSession::loaded() +{ + DP0("S60MediaPlayerSession::loaded +++"); + + stopStalledTimer(); + if (m_error == KErrNone || m_error == KErrMMPartialPlayback) { + setMediaStatus(QMediaPlayer::LoadedMedia); + TRAPD(err, updateMetaDataEntriesL()); + setError(err); + emit durationChanged(duration()); + emit positionChanged(0); + emit videoAvailableChanged(isVideoAvailable()); + emit audioAvailableChanged(isAudioAvailable()); + emit mediaChanged(); + + m_seekable = getIsSeekable(); + } + + DP0("S60MediaPlayerSession::loaded ---"); +} + +/*! + * Playback is completed as medai source reached end of media. +*/ +void S60MediaPlayerSession::endOfMedia() +{ + DP0("S60MediaPlayerSession::endOfMedia +++"); + + m_state = QMediaPlayer::StoppedState; + setMediaStatus(QMediaPlayer::EndOfMedia); + //there is a chance that user might have called play from EOF callback + //if we are already in playing state, do not send state change callback + if(m_state == QMediaPlayer::StoppedState) + emit stateChanged(QMediaPlayer::StoppedState); + emit positionChanged(m_duration); + + DP0("S60MediaPlayerSession::endOfMedia ---"); +} + +/*! + * The percentage of the temporary buffer filling before playback begins. + + When the player object is buffering; this property holds the percentage of + the temporary buffer that is filled. The buffer will need to reach 100% + filled before playback can resume, at which time the MediaStatus will be + BufferedMedia. + + \sa mediaStatus() +*/ + +void S60MediaPlayerSession::buffering() +{ + DP0("S60MediaPlayerSession::buffering +++"); + + startStalledTimer(); + setMediaStatus(QMediaPlayer::BufferingMedia); + +//Buffering cannot happen in stopped state. Hence update the state + if (state() == QMediaPlayer::StoppedState) + setState(QMediaPlayer::PausedState); + + DP0("S60MediaPlayerSession::buffering ---"); +} + +/*! + * Buffer is filled with data and to for continuing/start playback. +*/ + +void S60MediaPlayerSession::buffered() +{ + DP0("S60MediaPlayerSession::buffered +++"); + + stopStalledTimer(); + setMediaStatus(QMediaPlayer::BufferedMedia); + + DP0("S60MediaPlayerSession::buffered ---"); +} + +/*! + * Sets media status as stalled as waiting for the buffer to be filled to start playback. +*/ + +void S60MediaPlayerSession::stalled() +{ + DP0("S60MediaPlayerSession::stalled +++"); + + setMediaStatus(QMediaPlayer::StalledMedia); + + DP0("S60MediaPlayerSession::stalled ---"); +} + +/*! + * \return all the meta data entries in the current media source. +*/ + +QMap& S60MediaPlayerSession::metaDataEntries() +{ + DP0("S60MediaPlayerSession::metaDataEntries"); + + return m_metaDataMap; +} + +/*! + * \return Error by converting Symbian specific error to Multimedia error. +*/ + +QMediaPlayer::Error S60MediaPlayerSession::fromSymbianErrorToMultimediaError(int error) +{ + DP0("S60MediaPlayerSession::fromSymbianErrorToMultimediaError"); + + DP1("S60MediaPlayerSession::fromSymbianErrorToMultimediaError - ", error); + + switch(error) { + case KErrNoMemory: + case KErrNotFound: + case KErrBadHandle: + case KErrAbort: + case KErrNotSupported: + case KErrCorrupt: + case KErrGeneral: + case KErrArgument: + case KErrPathNotFound: + case KErrDied: + case KErrServerTerminated: + case KErrServerBusy: + case KErrCompletion: + case KErrBadPower: + case KErrMMInvalidProtocol: + case KErrMMInvalidURL: + return QMediaPlayer::ResourceError; + + case KErrMMPartialPlayback: + return QMediaPlayer::FormatError; + + case KErrMMAudioDevice: + case KErrMMVideoDevice: + case KErrMMDecoder: + case KErrUnknown: + return QMediaPlayer::ServiceMissingError; + + case KErrMMNotEnoughBandwidth: + case KErrMMSocketServiceNotFound: + case KErrMMNetworkRead: + case KErrMMNetworkWrite: + case KErrMMServerSocket: + case KErrMMServerNotSupported: + case KErrMMUDPReceive: + case KErrMMMulticast: + case KErrMMProxyServer: + case KErrMMProxyServerNotSupported: + case KErrMMProxyServerConnect: + case KErrCouldNotConnect: + return QMediaPlayer::NetworkError; + + case KErrNotReady: + case KErrInUse: + case KErrAccessDenied: + case KErrLocked: + case KErrMMDRMNotAuthorized: + case KErrPermissionDenied: + case KErrCancel: + case KErrAlreadyExists: + return QMediaPlayer::AccessDeniedError; + + case KErrNone: + return QMediaPlayer::NoError; + + default: + return QMediaPlayer::ResourceError; + } +} + +/*! + * \return error. + */ + +int S60MediaPlayerSession::error() const +{ + DP1("S60MediaPlayerSession::error", m_error); + + return m_error; +} + +/*! + * Sets the error. + * * If playback complete/prepare complete ..., etc with successful then sets error as ZERO + * else Multimedia error. +*/ + +void S60MediaPlayerSession::setError(int error, const QString &errorString, bool forceReset) +{ + DP0("S60MediaPlayerSession::setError +++"); + + DP5("S60MediaPlayerSession::setError - error:", error,"errorString:", errorString, "forceReset:", forceReset); + + if( forceReset ) { + m_error = KErrNone; + emit this->error(QMediaPlayer::NoError, QString()); + return; + } + + // If error does not change and m_error is reseted without forceReset flag + if (error == m_error || + (m_error != KErrNone && error == KErrNone)) + return; + + m_error = error; + QMediaPlayer::Error mediaError = fromSymbianErrorToMultimediaError(m_error); + QString symbianError = QString(errorString); + + if (mediaError != QMediaPlayer::NoError) { + // TODO: fix to user friendly string at some point + // These error string are only dev usable + symbianError.append("Symbian:"); + symbianError.append(QString::number(m_error)); + } + + emit this->error(mediaError, symbianError); + + if (m_error == KErrInUse) { + pause(); + } else if (mediaError != QMediaPlayer::NoError) { + m_play_requested = false; + setMediaStatus(QMediaPlayer::InvalidMedia); + stop(); + } +} + +void S60MediaPlayerSession::setAndEmitError(int error) +{ + m_error = error; + QMediaPlayer::Error rateError = fromSymbianErrorToMultimediaError(error); + QString symbianError; + symbianError.append("Symbian:"); + symbianError.append(QString::number(error)); + emit this->error(rateError, symbianError); + + DP0("S60MediaPlayerSession::setError ---"); +} + +/*! + * emits the signal if there is a changes in position and buffering status. + */ + +void S60MediaPlayerSession::tick() +{ + DP0("S60MediaPlayerSession::tick +++"); + + emit positionChanged(position()); + + if (bufferStatus() < 100) + emit bufferStatusChanged(bufferStatus()); + + DP0("S60MediaPlayerSession::tick ---"); +} + +/*! + * Starts the timer once the media source starts buffering. +*/ + +void S60MediaPlayerSession::startProgressTimer() +{ + DP0("S60MediaPlayerSession::startProgressTimer +++"); + + m_progressTimer->start(500); + + DP0("S60MediaPlayerSession::startProgressTimer ---"); +} + +/*! + * Stops the timer once the media source finished buffering. +*/ + +void S60MediaPlayerSession::stopProgressTimer() +{ + DP0("S60MediaPlayerSession::stopProgressTimer +++"); + + m_progressTimer->stop(); + + DP0("S60MediaPlayerSession::stopProgressTimer ---"); +} + +/*! + * Starts the timer while waiting for some events to happen like source buffering or call backs etc. + * So that if the events doesn't occur before stalled timer stops, it'll set the error/media status etc. +*/ + +void S60MediaPlayerSession::startStalledTimer() +{ + DP0("S60MediaPlayerSession::startStalledTimer +++"); + + m_stalledTimer->start(30000); + + DP0("S60MediaPlayerSession::startStalledTimer ---"); +} + +/*! + * Stops the timer when some events occurred while waiting for them. + * media source started buffering or call back is received etc. +*/ + +void S60MediaPlayerSession::stopStalledTimer() +{ + DP0("S60MediaPlayerSession::stopStalledTimer +++"); + + m_stalledTimer->stop(); + + DP0("S60MediaPlayerSession::stopStalledTimer ---"); +} + +/*! + * \return Converted Symbian specific Descriptor to QString. +*/ + +QString S60MediaPlayerSession::TDesC2QString(const TDesC& aDescriptor) +{ + DP0("S60MediaPlayerSession::TDesC2QString"); + + return QString::fromUtf16(aDescriptor.Ptr(), aDescriptor.Length()); +} + +/*! + * \return Converted QString to non-modifiable pointer Descriptor. +*/ + +TPtrC S60MediaPlayerSession::QString2TPtrC( const QString& string ) +{ + DP0("S60MediaPlayerSession::QString2TPtrC"); + + // Returned TPtrC is valid as long as the given parameter is valid and unmodified + return TPtrC16(static_cast(string.utf16()), string.length()); +} + +/*! + * \return Converted Symbian TRect object to QRect object. +*/ + +QRect S60MediaPlayerSession::TRect2QRect(const TRect& tr) +{ + DP0("S60MediaPlayerSession::TRect2QRect"); + + return QRect(tr.iTl.iX, tr.iTl.iY, tr.Width(), tr.Height()); +} + +/*! + * \return converted QRect object to Symbian specific TRec object. + */ + +TRect S60MediaPlayerSession::QRect2TRect(const QRect& qr) +{ + DP0("S60MediaPlayerSession::QRect2TRect"); + + return TRect(TPoint(qr.left(), qr.top()), TSize(qr.width(), qr.height())); +} + +/*! + \fn bool S60MediaPlayerSession::isVideoAvailable(); + + + Returns TRUE if Video is available. +*/ + +/*! + \fn bool S60MediaPlayerSession::isAudioAvailable(); + + + Returns TRUE if Audio is available. +*/ + +/*! + \fn void S60MediaPlayerSession::setPlaybackRate (qreal rate); + + + Sets \a rate play back rate on media source. getIsSeekable +*/ + +/*! + \fn bool S60MediaPlayerSession::getIsSeekable () const; + + + \return TRUE if Seekable possible on current media source else FALSE. +*/ + +/*! + \fn QString S60MediaPlayerSession::activeEndpoint () const; + + + \return active end point name.. +*/ + +/*! + \fn QString S60MediaPlayerSession::defaultEndpoint () const; + + + \return default end point name. +*/ + diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.h b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.h new file mode 100644 index 000000000..e6889d101 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediaplayersession.h @@ -0,0 +1,187 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIAPLAYERSESSION_H +#define S60MEDIAPLAYERSESSION_H + +#include +#include +#include +#include +#include // for TDesC +#include +#include "s60mediaplayerservice.h" + + +_LIT( KSeekable, "Seekable" ); +_LIT( KFalse, "0"); + +QT_BEGIN_NAMESPACE +class QMediaTimeRange; +QT_END_NAMESPACE + +class QTimer; + +class S60MediaPlayerSession : public QObject +{ + Q_OBJECT + +public: + S60MediaPlayerSession(QObject *parent); + virtual ~S60MediaPlayerSession(); + + // for player control interface to use + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + qint64 duration() const; + qint64 position() const; + void setPosition(qint64 pos); + int volume() const; + void setVolume(int volume); + bool isMuted() const; + void setMuted(bool muted); + virtual bool isVideoAvailable() = 0; + virtual bool isAudioAvailable() = 0; + bool isSeekable() const; + void play(); + void pause(); + void stop(); + void reset(); + bool isMetadataAvailable() const; + QVariant metaData(const QString &key) const; + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList availableMetaData() const; + QStringList availableExtendedMetaData() const; + QString metaDataKeyAsString(QtMultimediaKit::MetaData key) const; + void load(const QMediaContent source); + int bufferStatus(); + virtual void setVideoRenderer(QObject *renderer); + void setMediaStatus(QMediaPlayer::MediaStatus); + void setState(QMediaPlayer::State state); + void setAudioEndpoint(const QString& audioEndpoint); + virtual void setPlaybackRate(qreal rate) = 0; + virtual bool getIsSeekable() const { return ETrue; } + TBool isStreaming(); + +protected: + virtual void doLoadL(const TDesC &path) = 0; + virtual void doLoadUrlL(const TDesC &path) = 0; + virtual void doPlay() = 0; + virtual void doStop() = 0; + virtual void doClose() = 0; + virtual void doPauseL() = 0; + virtual void doSetVolumeL(int volume) = 0; + virtual void doSetPositionL(qint64 microSeconds) = 0; + virtual qint64 doGetPositionL() const = 0; + virtual void updateMetaDataEntriesL() = 0; + virtual int doGetBufferStatusL() const = 0; + virtual qint64 doGetDurationL() const = 0; + virtual void doSetAudioEndpoint(const QString& audioEndpoint) = 0; + +public: + // From S60MediaPlayerAudioEndpointSelector + virtual QString activeEndpoint() const = 0; + virtual QString defaultEndpoint() const = 0; +public Q_SLOTS: + virtual void setActiveEndpoint(const QString& name) = 0; + +protected: + int error() const; + void setError(int error, const QString &errorString = QString(), bool forceReset = false); + void setAndEmitError(int error); + void loaded(); + void buffering(); + void buffered(); + void endOfMedia(); + QMap& metaDataEntries(); + QMediaPlayer::Error fromSymbianErrorToMultimediaError(int error); + void startProgressTimer(); + void stopProgressTimer(); + void startStalledTimer(); + void stopStalledTimer(); + QString TDesC2QString(const TDesC& aDescriptor); + TPtrC QString2TPtrC( const QString& string ); + QRect TRect2QRect(const TRect& tr); + TRect QRect2TRect(const QRect& qr); + +protected slots: + void tick(); + void stalled(); + +signals: + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State state); + void mediaStatusChanged(QMediaPlayer::MediaStatus mediaStatus); + void videoAvailableChanged(bool videoAvailable); + void audioAvailableChanged(bool audioAvailable); + void bufferStatusChanged(int percentFilled); + void seekableChanged(bool); + void availablePlaybackRangesChanged(const QMediaTimeRange&); + void metaDataChanged(); + void error(int error, const QString &errorString); + void activeEndpointChanged(const QString &name); + void mediaChanged(); + void playbackRateChanged(qreal rate); + void volumeChanged(int volume); + void mutedChanged(bool muted); + +protected: + QUrl m_UrlPath; + bool m_stream; + QMediaContent m_source; + +private: + qreal m_playbackRate; + QMap m_metaDataMap; + bool m_muted; + int m_volume; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + QTimer *m_progressTimer; + QTimer *m_stalledTimer; + int m_error; + bool m_play_requested; + bool m_seekable; + qint64 m_duration; +}; + +#endif diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.cpp new file mode 100644 index 000000000..48b565c34 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.cpp @@ -0,0 +1,167 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60mediarecognizer.h" +#include +#include +#include +#include +#include + +#include + +static const TInt KMimeTypePrefixLength = 6; // "audio/" or "video/" + +_LIT(KMimeTypePrefixAudio, "audio/"); +_LIT(KMimeTypePrefixVideo, "video/"); +_LIT(KMimeTypeRingingTone, "application/vnd.nokia.ringing-tone"); + +/*! + Construct a media Recognizer with the given \a parent. +*/ + +S60MediaRecognizer::S60MediaRecognizer(QObject *parent) : QObject(parent) +{ + DP0("S60MediaRecognizer::S60MediaRecognizer +++"); + DP0("S60MediaRecognizer::S60MediaRecognizer ---"); +} + +/*! + Destroys a media Recognizer. +*/ + +S60MediaRecognizer::~S60MediaRecognizer() +{ + DP0("S60MediaRecognizer::~S60MediaRecognizer +++"); + + m_file.Close(); + m_fileServer.Close(); + m_recognizer.Close(); + + DP0("S60MediaRecognizer::~S60MediaRecognizer ---"); +} + +/*! + * \return media type of \a url. + * \a url may be a streaming link or a local file. + * If \a url is local file then identifies the media type and returns it. +*/ + +S60MediaRecognizer::MediaType S60MediaRecognizer::mediaType(const QUrl &url) +{ + DP0("S60MediaRecognizer::mediaType"); + + bool isStream = (url.scheme() == "file")?false:true; + + if (isStream) + return Url; + else + return identifyMediaType(QDir::cleanPath(url.toLocalFile())); +} + +/*! + * \return Media type of \a file name by recognizing its mimetype whether its audio or video. +*/ + +S60MediaRecognizer::MediaType S60MediaRecognizer::identifyMediaType(const QString& fileName) +{ + DP0("S60MediaRecognizer::identifyMediaType +++"); + + DP1("S60MediaRecognizer::identifyMediaType - ", fileName); + + S60MediaRecognizer::MediaType result = Video; // default to videoplayer + bool recognizerOpened = false; + + TInt err = m_recognizer.Connect(); + if (err == KErrNone) { + recognizerOpened = true; + } + + err = m_fileServer.Connect(); + if (err == KErrNone) { + recognizerOpened = true; + } + + // This is needed for sharing file handles for the recognizer + err = m_fileServer.ShareProtected(); + if (err == KErrNone) { + recognizerOpened = true; + } + + if (recognizerOpened) { + m_file.Close(); + err = m_file.Open(m_fileServer, QString2TPtrC(QDir::toNativeSeparators(fileName)), EFileRead | + EFileShareReadersOnly); + + if (err == KErrNone) { + TDataRecognitionResult recognizerResult; + err = m_recognizer.RecognizeData(m_file, recognizerResult); + if (err == KErrNone) { + const TPtrC mimeType = recognizerResult.iDataType.Des(); + + if (mimeType.Left(KMimeTypePrefixLength).Compare(KMimeTypePrefixAudio) == 0 || + mimeType.Compare(KMimeTypeRingingTone) == 0) { + result = Audio; + } else if (mimeType.Left(KMimeTypePrefixLength).Compare(KMimeTypePrefixVideo) == 0) { + result = Video; + } + } + } + } + + DP0("S60MediaRecognizer::identifyMediaType ---"); + + return result; +} + +/*! + * \return Symbian modifiable pointer descriptor from a QString \a string. + */ + +TPtrC S60MediaRecognizer::QString2TPtrC( const QString& string ) +{ + DP1("S60MediaRecognizer::QString2TPtrC - ", string); + + // Returned TPtrC is valid as long as the given parameter is valid and unmodified + return TPtrC16(static_cast(string.utf16()), string.length()); +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.h b/src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.h new file mode 100644 index 000000000..bdd0caabe --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediarecognizer.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIARECOGNIZER_H_ +#define S60MEDIARECOGNIZER_H_ + +#include + +#include +#include + +class QUrl; + +class S60MediaRecognizer : public QObject +{ + Q_OBJECT + +public: + enum MediaType { + Audio, + Video, + Url, + NotSupported = -1 + }; + + S60MediaRecognizer(QObject *parent = 0); + ~S60MediaRecognizer(); + + S60MediaRecognizer::MediaType mediaType(const QUrl &url); + S60MediaRecognizer::MediaType identifyMediaType(const QString& fileName); + +protected: + TPtrC QString2TPtrC( const QString& string ); + +private: + RApaLsSession m_recognizer; + RFile m_file; + RFs m_fileServer; +}; + +#endif /* S60MEDIARECOGNIZER_H_ */ diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.cpp b/src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.cpp new file mode 100644 index 000000000..9a2ce5c02 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.cpp @@ -0,0 +1,201 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60mediastreamcontrol.h" +#include "s60mediaplayersession.h" +#include "s60mediaplayercontrol.h" +#include + +#include +#include +#include + +/*! + Constructs a new media streams control with the given \a control. +*/ + +S60MediaStreamControl::S60MediaStreamControl(QObject *control, QObject *parent) + : QMediaStreamsControl(parent) + , m_control(NULL) + , m_mediaType(S60MediaSettings::Unknown) +{ + DP0("S60MediaStreamControl::S60MediaStreamControl +++"); + + m_control = qobject_cast(control); + m_mediaType = m_control->mediaControlSettings().mediaType(); + + DP0("S60MediaStreamControl::S60MediaStreamControl ---"); +} + +/*! + Destroys a media streams control. +*/ + +S60MediaStreamControl::~S60MediaStreamControl() +{ + DP0("S60MediaStreamControl::~S60MediaStreamControl +++"); + DP0("S60MediaStreamControl::~S60MediaStreamControl ---"); +} + +/*! + \return the number of media streams. +*/ + +int S60MediaStreamControl::streamCount() +{ + DP0("S60MediaStreamControl::streamCount"); + + int streamCount = 0; + if (m_control->isAudioAvailable()) + streamCount++; + if (m_control->isVideoAvailable()) + streamCount++; + DP1("S60MediaStreamControl::streamCount", streamCount); + + return streamCount; +} + +/*! + \return the type of a media \a streamNumber. +*/ + +QMediaStreamsControl::StreamType S60MediaStreamControl::streamType(int streamNumber) +{ + DP0("S60MediaStreamControl::streamType +++"); + + DP1("S60MediaStreamControl::streamType - ", streamNumber); + + Q_UNUSED(streamNumber); + + QMediaStreamsControl::StreamType type = QMediaStreamsControl::UnknownStream; + + if (m_control->mediaControlSettings().mediaType() == S60MediaSettings::Video) + type = QMediaStreamsControl::VideoStream; + else + type = QMediaStreamsControl::AudioStream; + + DP0("S60MediaStreamControl::streamType ---"); + + return type; +} + +/*! + \return the meta-data value of \a key for a given \a streamNumber. + + Useful metadata keya are QtMultimediaKit::Title, QtMultimediaKit::Description and QtMultimediaKit::Language. +*/ + +QVariant S60MediaStreamControl::metaData(int streamNumber, QtMultimediaKit::MetaData key) +{ + DP0("S60MediaStreamControl::metaData"); + + Q_UNUSED(streamNumber); + + if (m_control->session()) { + if (m_control->session()->isMetadataAvailable()) + return m_control->session()->metaData(key); + } + return QVariant(); +} + +/*! + \return true if the media \a streamNumber is active else false. +*/ + +bool S60MediaStreamControl::isActive(int streamNumber) +{ + DP0("S60MediaStreamControl::isActive +++"); + + DP1("S60MediaStreamControl::isActive - ", streamNumber); + + if (m_control->mediaControlSettings().mediaType() == S60MediaSettings::Video) { + switch (streamNumber) { + case 1: + return m_control->isVideoAvailable(); + case 2: + return m_control->isAudioAvailable(); + default: + break; + } + } + + DP0("S60MediaStreamControl::isActive ---"); + + return m_control->isAudioAvailable(); +} + +/*! + Sets the active \a streamNumber of a media \a state. + + Symbian MMF does not support enabling or disabling specific media streams. + + Setting the active state of a media stream to true will activate it. If any other stream + of the same type was previously active it will be deactivated. Setting the active state fo a + media stream to false will deactivate it. +*/ + +void S60MediaStreamControl::setActive(int streamNumber, bool state) +{ + DP0("S60MediaStreamControl::setActive +++"); + + DP2("S60MediaStreamControl::setActive - ", streamNumber, state); + + Q_UNUSED(streamNumber); + Q_UNUSED(state); + // Symbian MMF does not support enabling or disabling specific media streams + + DP0("S60MediaStreamControl::setActive ---"); +} + +/*! + The signal is emitted when the available streams list is changed. +*/ + +void S60MediaStreamControl::handleStreamsChanged() +{ + DP0("S60MediaStreamControl::handleStreamsChanged +++"); + + emit streamsChanged(); + + DP0("S60MediaStreamControl::handleStreamsChanged ---"); +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.h b/src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.h new file mode 100644 index 000000000..702ebc7b2 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60mediastreamcontrol.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60MEDIASTREAMCONTROL_H +#define S60MEDIASTREAMCONTROL_H + +#include + +#include "s60mediaplayercontrol.h" + +#include +#include + +QT_USE_NAMESPACE + +class S60MediaPlayerControl; +class S60MediaSettings; + +class S60MediaStreamControl : public QMediaStreamsControl +{ + Q_OBJECT +public: + S60MediaStreamControl(QObject *session, QObject *parent = 0); + ~S60MediaStreamControl(); + + // from QMediaStreamsControl + int streamCount(); + QMediaStreamsControl::StreamType streamType(int streamNumber); + QVariant metaData(int streamNumber, QtMultimediaKit::MetaData key); + bool isActive(int streamNumber); + void setActive(int streamNumber, bool state); + +public Q_SLOTS: + void handleStreamsChanged(); + +private: + S60MediaPlayerControl *m_control; + S60MediaSettings::TMediaType m_mediaType; +}; + +#endif //S60MEDIASTREAMCONTROL_H diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videooutputinterface.h b/src/plugins/symbian/mmf/mediaplayer/s60videooutputinterface.h new file mode 100644 index 000000000..f5388dbbc --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videooutputinterface.h @@ -0,0 +1,62 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOOUTPUTINTERFACE_H +#define S60VIDEOOUTPUTINTERFACE_H + +#include +#include +#include + +class S60VideoOutputInterface +{ +public: + RWindow *videoWindowHandle() const { return videoWinId() ? static_cast(videoWinId()->DrawableWindow()) : 0 ; } + virtual WId videoWinId() const = 0; + // If VIDEOOUTPUT_GRAPHICS_SURFACES is defined, the return value is the video + // rectangle relative to the video window. If not, the return value is the + // absolute screen rectangle. + virtual QRect videoDisplayRect() const = 0; + virtual Qt::AspectRatioMode videoAspectRatio() const = 0; +}; + +#endif // S60VIDEOOUTPUTINTERFACE_H + diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.cpp b/src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.cpp new file mode 100644 index 000000000..49d511ca2 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.cpp @@ -0,0 +1,1124 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60videoplayersession.h" +#include "s60mediaplayerservice.h" +#include "s60videowidgetcontrol.h" +#include "s60videowidgetdisplay.h" +#include "s60videowindowcontrol.h" +#include "s60videowindowdisplay.h" + +#include +#include +#include +#include +#include + +#include +#include // For CCoeEnv +#include +#include +#include +#include +#ifdef HTTP_COOKIES_ENABLED +#include +#endif + +const QString DefaultAudioEndpoint = QLatin1String("Default"); +const TUid KHelixUID = {0x101F8514}; + +//Hard-coding the command to support older versions. +const TInt KMMFROPControllerEnablePausedLoadingStatus = 7; + +TVideoRotation videoRotation(qreal angle) +{ + // Convert to clockwise + angle = 360.0f - angle; + while (angle >= 360.0f) + angle -= 360.0f; + TVideoRotation result = EVideoRotationNone; + if (angle >= 45.0f && angle < 135.0f) + result = EVideoRotationClockwise90; + else if (angle >= 135.0f && angle < 225.0f) + result = EVideoRotationClockwise180; + else if (angle >= 225.0f && angle < 315.0f) + result = EVideoRotationClockwise270; + return result; +} + +S60VideoPlayerEventHandler *S60VideoPlayerEventHandler::m_instance = 0; +QCoreApplication::EventFilter S60VideoPlayerEventHandler::m_eventFilter = 0; +QList S60VideoPlayerEventHandler::m_applicationFocusObservers; + +S60VideoPlayerEventHandler *S60VideoPlayerEventHandler::instance() +{ + if (!m_instance) + m_instance = new S60VideoPlayerEventHandler(); + return m_instance; +} + +S60VideoPlayerEventHandler::S60VideoPlayerEventHandler() +{ + m_eventFilter = QCoreApplication::instance()->setEventFilter(filterEvent); +} + +S60VideoPlayerEventHandler::~S60VideoPlayerEventHandler() +{ + QCoreApplication::instance()->setEventFilter(m_eventFilter); +} + +void S60VideoPlayerEventHandler::addApplicationFocusObserver(ApplicationFocusObserver *observer) +{ + m_applicationFocusObservers.append(observer); +} + +void S60VideoPlayerEventHandler::removeApplicationFocusObserver(ApplicationFocusObserver *observer) +{ + m_applicationFocusObservers.removeAt(m_applicationFocusObservers.indexOf(observer)); + if (m_applicationFocusObservers.count() == 0) { + delete m_instance; + m_instance = 0; + } +} + +bool S60VideoPlayerEventHandler::filterEvent(void *message, long *result) +{ + if (const QSymbianEvent *symbianEvent = reinterpret_cast(message)) { + switch (symbianEvent->type()) { + case QSymbianEvent::WindowServerEvent: + { + const TWsEvent *wsEvent = symbianEvent->windowServerEvent(); + if (EEventFocusLost == wsEvent->Type() || EEventFocusGained == wsEvent->Type()) { + for (QList::const_iterator it = m_applicationFocusObservers.constBegin(); + it != m_applicationFocusObservers.constEnd(); ++it) { + if (EEventFocusLost == wsEvent->Type()) + (*it)->applicationLostFocus(); + else if (EEventFocusGained == wsEvent->Type()) + (*it)->applicationGainedFocus(); + } + } + } + break; + default: + break; + } + } + bool ret = false; + if (m_eventFilter) + ret = m_eventFilter(message, result); + return ret; +} + +/*! + Constructs the CVideoPlayerUtility2 object with given \a service and \a object. + And Registers for Video Loading Notifications. +*/ +S60VideoPlayerSession::S60VideoPlayerSession(QMediaService *service, S60MediaNetworkAccessControl *object) + : S60MediaPlayerSession(service) + , m_accessPointId(0) + , m_wsSession(&CCoeEnv::Static()->WsSession()) + , m_screenDevice(CCoeEnv::Static()->ScreenDevice()) + , m_service(service) + , m_player(0) +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + , m_dsaActive(false) + , m_dsaStopped(false) +#endif + , m_videoOutputControl(0) + , m_videoOutputDisplay(0) + , m_displayWindow(0) +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + , m_audioOutput(0) +#endif + , m_audioEndpoint(DefaultAudioEndpoint) + , m_pendingChanges(0) + , m_backendInitiatedPause(false) +#ifdef HTTP_COOKIES_ENABLED + , m_destinationPckg(KUidInterfaceMMFControllerSessionInfo) +#endif +{ + DP0("S60VideoPlayerSession::S60VideoPlayerSession +++"); + + m_networkAccessControl = object; +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + QT_TRAP_THROWING(m_player = CVideoPlayerUtility2::NewL( + *this, + 0, + EMdaPriorityPreferenceNone + )); + m_player->RegisterForVideoLoadingNotification(*this); +#else + RWindow *window = 0; + QRect extentRect; + QWidget *widget = QApplication::activeWindow(); + if (!widget) + widget = QApplication::allWidgets().at(0); + Q_ASSERT(widget); + WId wid = widget->effectiveWinId(); + if (!wid) + wid = widget->winId(); + window = static_cast(wid->DrawableWindow()); + extentRect = QRect(widget->mapToGlobal(widget->pos()), widget->size()); + TRect clipRect = QRect2TRect(extentRect); + const TRect desktopRect = QRect2TRect(QApplication::desktop()->screenGeometry()); + clipRect.Intersection(desktopRect); + QT_TRAP_THROWING(m_player = CVideoPlayerUtility::NewL( + *this, + 0, + EMdaPriorityPreferenceNone, + *m_wsSession, + *m_screenDevice, + *window, + QRect2TRect(extentRect), + clipRect)); + m_dsaActive = true; + m_player->RegisterForVideoLoadingNotification(*this); +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + S60VideoPlayerEventHandler::instance()->addApplicationFocusObserver(this); + DP0("S60VideoPlayerSession::S60VideoPlayerSession ---"); +} + +/*! + Destroys the CVideoPlayerUtility2 object. + + And Unregister the observer. +*/ + +S60VideoPlayerSession::~S60VideoPlayerSession() +{ + DP0("S60VideoPlayerSession::~S60VideoPlayerSession +++"); + S60VideoPlayerEventHandler::instance()->removeApplicationFocusObserver(this); +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + if (m_audioOutput) + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; +#endif + m_player->Close(); + delete m_player; + + DP0("S60VideoPlayerSession::~S60VideoPlayerSession ---"); +} + +void S60VideoPlayerSession::applicationGainedFocus() +{ + if (m_backendInitiatedPause) { + m_backendInitiatedPause = false; + play(); + } + if (QMediaPlayer::PausedState == state()) { + TRAPD(err, m_player->RefreshFrameL()); + setError(err); + } +} + +void S60VideoPlayerSession::applicationLostFocus() +{ + if (QMediaPlayer::PlayingState == state()) { + m_backendInitiatedPause = true; + pause(); + } +} + +/*! + + Opens the a file from \a path. +*/ + +void S60VideoPlayerSession::doLoadL(const TDesC &path) +{ + DP0("S60VideoPlayerSession::doLoadL +++"); + +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + // m_audioOutput needs to be reinitialized after MapcInitComplete + if (m_audioOutput) + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; + m_audioOutput = NULL; +#endif + m_player->OpenFileL(path, KHelixUID); + + DP0("S60VideoPlayerSession::doLoadL ---"); +} + +/*! + Sets the playbackRate with \a rate. +*/ + +void S60VideoPlayerSession::setPlaybackRate(qreal rate) +{ + DP0("S60VideoPlayerSession::setPlaybackRate +++"); + + DP1("S60VideoPlayerSession::setPlaybackRate - ", rate); + + /* + * setPlaybackRate is not supported in S60 3.1 and 3.2 + * This flag will be defined for 3.1 and 3.2 + */ +#ifndef PLAY_RATE_NOT_SUPPORTED + //setPlayVelocity requires rate in the form of + //50 = 0.5x ;100 = 1.x ; 200 = 2.x ; 300 = 3.x + //so multiplying rate with 100 + TRAPD(err, m_player->SetPlayVelocityL((TInt)(rate*100))); + if (KErrNone == err) + emit playbackRateChanged(rate); + else + setError(err); +#endif + + DP0("S60VideoPlayerSession::setPlaybackRate ---"); +} + +/*! + + Opens the a Url from \a path for streaming the source. +*/ + +void S60VideoPlayerSession::doLoadUrlL(const TDesC &path) +{ + DP0("S60VideoPlayerSession::doLoadUrlL +++"); + +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + // m_audioOutput needs to be reinitialized after MapcInitComplete + if (m_audioOutput) + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; + m_audioOutput = NULL; +#endif + m_accessPointId = m_networkAccessControl->accessPointId(); + m_player->OpenUrlL(path, m_accessPointId, KNullDesC8, KHelixUID); + + DP0("S60VideoPlayerSession::doLoadUrlL ---"); +} + +/*! + + Returns the percentage of the video clip loaded. +*/ + +int S60VideoPlayerSession::doGetBufferStatusL() const +{ + // DP0("S60VideoPlayerSession::doGetBufferStatusL +++"); + + int progress = 0; + m_player->GetVideoLoadingProgressL(progress); + + // DP0("S60VideoPlayerSession::doGetBufferStatusL ---"); + + return progress; +} + +/*! + Returns the duration of the video sample in microseconds. +*/ + +qint64 S60VideoPlayerSession::doGetDurationL() const +{ + // DP0("S60VideoPlayerSession::doGetDurationL"); + + return m_player->DurationL().Int64() / qint64(1000); +} + +/*! + * Sets the \a videooutput for video rendering. +*/ + +void S60VideoPlayerSession::setVideoRenderer(QObject *videoOutput) +{ + DP0("S60VideoPlayerSession::setVideoRenderer +++"); + if (videoOutput != m_videoOutputControl) { + if (m_videoOutputDisplay) { + disconnect(m_videoOutputDisplay); + m_videoOutputDisplay->disconnect(this); + m_videoOutputDisplay = 0; + } + if (videoOutput) { + if (S60VideoWidgetControl *control = qobject_cast(videoOutput)) + m_videoOutputDisplay = control->display(); + if (!m_videoOutputDisplay) + return; + m_videoOutputDisplay->setNativeSize(m_nativeSize); + connect(this, SIGNAL(nativeSizeChanged(QSize)), m_videoOutputDisplay, SLOT(setNativeSize(QSize))); + connect(m_videoOutputDisplay, SIGNAL(windowHandleChanged(RWindow *)), this, SLOT(windowHandleChanged())); + connect(m_videoOutputDisplay, SIGNAL(displayRectChanged(QRect, QRect)), this, SLOT(displayRectChanged())); + connect(m_videoOutputDisplay, SIGNAL(aspectRatioModeChanged(Qt::AspectRatioMode)), this, SLOT(aspectRatioChanged())); + connect(m_videoOutputDisplay, SIGNAL(rotationChanged(qreal)), this, SLOT(rotationChanged())); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + connect(m_videoOutputDisplay, SIGNAL(beginVideoWindowNativePaint()), this, SLOT(suspendDirectScreenAccess())); + connect(m_videoOutputDisplay, SIGNAL(endVideoWindowNativePaint()), this, SLOT(resumeDirectScreenAccess())); +#endif + } + m_videoOutputControl = videoOutput; + windowHandleChanged(); + } + + DP0("S60VideoPlayerSession::setVideoRenderer ---"); +} + +/*! + * Apply the pending changes for window. +*/ +void S60VideoPlayerSession::applyPendingChanges(bool force) +{ + DP0("S60VideoPlayerSession::applyPendingChanges +++"); + + if ( force + || QMediaPlayer::LoadedMedia == mediaStatus() + || QMediaPlayer::StalledMedia == mediaStatus() + || QMediaPlayer::BufferingMedia == mediaStatus() + || QMediaPlayer::BufferedMedia == mediaStatus() + || QMediaPlayer::EndOfMedia == mediaStatus()) { + int error = KErrNone; + RWindow *const window = m_videoOutputDisplay ? m_videoOutputDisplay->windowHandle() : 0; + const QRect extentRect = m_videoOutputDisplay ? m_videoOutputDisplay->extentRect() : QRect(); + const QRect clipRect = m_videoOutputDisplay ? m_videoOutputDisplay->clipRect() : QRect(); +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + if (m_pendingChanges & WindowHandle) { + if (m_displayWindow) { + m_player->RemoveDisplayWindow(*m_displayWindow); + m_displayWindow = 0; + } + if (window) { + TRAP(error, m_player->AddDisplayWindowL(*m_wsSession, *m_screenDevice, + *window, + QRect2TRect(extentRect), + QRect2TRect(clipRect))); + if (KErrNone == error) + m_displayWindow = window; + } + m_pendingChanges = ScaleFactors; + } + if (KErrNone == error && (m_pendingChanges & DisplayRect) && m_displayWindow) { + TRAP(error, m_player->SetVideoExtentL(*m_displayWindow, QRect2TRect(extentRect))); + if (KErrNone == error) + TRAP(error, m_player->SetWindowClipRectL(*m_displayWindow, QRect2TRect(clipRect))); + m_pendingChanges ^= DisplayRect; + m_pendingChanges |= ScaleFactors; + } +#else + if (m_pendingChanges & WindowHandle || m_pendingChanges & DisplayRect) { + if (window) { + TRAP(error, m_player->SetDisplayWindowL(*m_wsSession, *m_screenDevice, + *window, + QRect2TRect(extentRect), + QRect2TRect(clipRect))); + if (KErrNone == error) + m_displayWindow = window; + } + m_dsaActive = (KErrNone == error); + m_dsaStopped = false; + m_pendingChanges = ScaleFactors; + } + +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + if (KErrNone == error && (m_pendingChanges & ScaleFactors) && m_displayWindow && m_videoOutputDisplay) { + const TVideoRotation rotation = videoRotation(m_videoOutputDisplay->rotation()); + const bool swap = (rotation == EVideoRotationClockwise90 || rotation == EVideoRotationClockwise270); + const QSize extentSize = swap ? QSize(extentRect.height(), extentRect.width()) : extentRect.size(); + QSize scaled = m_nativeSize; + if (m_videoOutputDisplay->aspectRatioMode() == Qt::IgnoreAspectRatio) + scaled.scale(extentSize, Qt::IgnoreAspectRatio); + else if (m_videoOutputDisplay->aspectRatioMode() == Qt::KeepAspectRatio) + scaled.scale(extentSize, Qt::KeepAspectRatio); + else if (m_videoOutputDisplay->aspectRatioMode() == Qt::KeepAspectRatioByExpanding) + scaled.scale(extentSize, Qt::KeepAspectRatioByExpanding); + const qreal width = qreal(scaled.width()) / qreal(m_nativeSize.width()) * qreal(100); + const qreal height = qreal(scaled.height()) / qreal(m_nativeSize.height()) * qreal(100); +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + TRAP(error, m_player->SetScaleFactorL(*m_displayWindow, width, height)); +#else + static const TBool antialias = ETrue; + TRAP(error, m_player->SetScaleFactorL(width, height, antialias)); +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + m_pendingChanges ^= ScaleFactors; + } + if (KErrNone == error && (m_pendingChanges && Rotation) && m_displayWindow && m_videoOutputDisplay) { + const TVideoRotation rotation = videoRotation(m_videoOutputDisplay->rotation()); +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + TRAP(error, m_player->SetRotationL(*m_displayWindow, rotation)); +#else + TRAP(error, m_player->SetRotationL(rotation)); +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + m_pendingChanges ^= Rotation; + } + setError(error); + } + + DP0("S60VideoPlayerSession::applyPendingChanges ---"); +} + +/*! + * \return TRUE if video is available. +*/ + +bool S60VideoPlayerSession::isVideoAvailable() +{ + DP0("S60VideoPlayerSession::isVideoAvailable"); + +#ifdef PRE_S60_50_PLATFORM + return true; // this is not supported in pre 5th platforms +#else + if ( mediaStatus() == QMediaPlayer::LoadingMedia + || mediaStatus() == QMediaPlayer::UnknownMediaStatus + || mediaStatus() == QMediaPlayer::NoMedia + || (mediaStatus() == QMediaPlayer::StalledMedia && state() == QMediaPlayer::StoppedState) + || mediaStatus() == QMediaPlayer::InvalidMedia) + return false; + + if (m_player) { + bool videoAvailable = false; + TRAPD(err, videoAvailable = m_player->VideoEnabledL()); + setError(err); + return videoAvailable; + } else { + return false; + } +#endif + +} + +/*! + * \return TRUE if Audio available. +*/ + +bool S60VideoPlayerSession::isAudioAvailable() +{ + DP0("S60VideoPlayerSession::isAudioAvailable"); + + if ( mediaStatus() == QMediaPlayer::LoadingMedia + || mediaStatus() == QMediaPlayer::UnknownMediaStatus + || mediaStatus() == QMediaPlayer::NoMedia + || (mediaStatus() == QMediaPlayer::StalledMedia && state() == QMediaPlayer::StoppedState) + || mediaStatus() == QMediaPlayer::InvalidMedia) + return false; + + if (m_player) { + bool audioAvailable = false; + TRAPD(err, audioAvailable = m_player->AudioEnabledL()); + setError(err); + return audioAvailable; + } else { + return false; + } +} + +/*! + Start or resume playing the current source. +*/ + +void S60VideoPlayerSession::doPlay() +{ + DP0("S60VideoPlayerSession::doPlay +++"); + + m_player->Play(); + + DP0("S60VideoPlayerSession::doPlay ---"); +} + +/*! + Pause playing the current source. +*/ + +void S60VideoPlayerSession::doPauseL() +{ + DP0("S60VideoPlayerSession::doPauseL +++"); + + m_player->PauseL(); + + DP0("S60VideoPlayerSession::doPauseL ---"); +} + +/*! + + Stop playing, and reset the play position to the beginning. +*/ + +void S60VideoPlayerSession::doStop() +{ + DP0("S60VideoPlayerSession::doStop +++"); + + if (m_stream) + m_networkAccessControl->resetIndex(); + + m_player->Stop(); + + DP0("S60VideoPlayerSession::doStop ---"); +} + +/*! + Closes the current audio clip (allowing another clip to be opened) +*/ + +void S60VideoPlayerSession::doClose() +{ + DP0("S60VideoPlayerSession::doClose +++"); + +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + if (m_audioOutput) { + m_audioOutput->UnregisterObserver(*this); + delete m_audioOutput; + m_audioOutput = NULL; + } +#endif + + m_player->Close(); + +// close will remove the window handle in media clint video. +// So mark it in pending changes. + m_pendingChanges |= WindowHandle; + + DP0("S60VideoPlayerSession::doClose ---"); +} + +/*! + * Returns the current playback position in microseconds from the start of the clip. + +*/ + +qint64 S60VideoPlayerSession::doGetPositionL() const +{ + // DP0("S60VideoPlayerSession::doGetPositionL"); + + return m_player->PositionL().Int64() / qint64(1000); +} + +/*! + Sets the current playback position to \a microSeconds from the start of the clip. +*/ + +void S60VideoPlayerSession::doSetPositionL(qint64 microSeconds) +{ + // DP0("S60VideoPlayerSession::doSetPositionL"); + + m_player->SetPositionL(TTimeIntervalMicroSeconds(microSeconds)); +} + +/*! + + Changes the current playback volume to specified \a value. +*/ + +void S60VideoPlayerSession::doSetVolumeL(int volume) +{ + DP0("S60VideoPlayerSession::doSetVolumeL +++"); + + DP1("S60VideoPlayerSession::doSetVolumeL - ", volume); + + m_player->SetVolumeL(volume * m_player->MaxVolume() / 100); + + DP0("S60VideoPlayerSession::doSetVolumeL ---"); +} + +/*! + * Notification to the client that the opening of the video clip has completed. + * If successful then an \a aError will be ZERO else system wide error. +*/ + +void S60VideoPlayerSession::MvpuoOpenComplete(TInt aError) +{ + DP0("S60VideoPlayerSession::MvpuoOpenComplete +++"); + + DP1("S60VideoPlayerSession::MvpuoOpenComplete - aError:", aError); + + setError(aError); +#ifdef HTTP_COOKIES_ENABLED + if (KErrNone == aError) { + TInt err(KErrNone); + const QByteArray userAgentString("User-Agent"); + TInt uasize = m_source.canonicalRequest().rawHeader(userAgentString).size(); + TPtrC8 userAgent((const unsigned char*)(m_source.canonicalRequest().rawHeader(userAgentString).constData()), uasize); + if (userAgent.Length()) { + err = m_player->CustomCommandSync(m_destinationPckg, EMMFSetSessionInfo, _L8("User-Agent"), userAgent); + if (err != KErrNone) { + setError(err); + return; + } + } + const QByteArray refererString("Referer"); + TInt refsize = m_source.canonicalRequest().rawHeader(refererString).size(); + TPtrC8 referer((const unsigned char*)m_source.canonicalRequest().rawHeader(refererString).constData(),refsize); + if (referer.Length()) { + err = m_player->CustomCommandSync(m_destinationPckg, EMMFSetSessionInfo, _L8("Referer"), referer); + if (err != KErrNone) { + setError(err); + return; + } + } + const QByteArray cookieString("Cookie"); + TInt cksize = m_source.canonicalRequest().rawHeader(cookieString).size(); + TPtrC8 cookie((const unsigned char*)m_source.canonicalRequest().rawHeader(cookieString).constData(),cksize); + if (cookie.Length()) { + err = m_player->CustomCommandSync(m_destinationPckg, EMMFSetSessionInfo, _L8("Cookie"), cookie); + if (err != KErrNone) { + setError(err); + return; + } + } + m_player->Prepare(); + } +#else + if (KErrNone == aError) + m_player->Prepare(); +#endif + const TMMFMessageDestinationPckg dest( KUidInterfaceMMFROPController ); + TRAP_IGNORE(m_player->CustomCommandSync(dest, KMMFROPControllerEnablePausedLoadingStatus, KNullDesC8, KNullDesC8)); + + DP0("S60VideoPlayerSession::MvpuoOpenComplete ---"); +} + +/*! + * Notification to the client that the opening of the video clip has been preapred. + * If successful then an \a aError will be ZERO else system wide error. +*/ + +void S60VideoPlayerSession::MvpuoPrepareComplete(TInt aError) +{ + DP0("S60VideoPlayerSession::MvpuoPrepareComplete +++"); + + DP1("S60VideoPlayerSession::MvpuoPrepareComplete - aError:", aError); + + if (KErrNone == aError && m_stream) { + emit accessPointChanged(m_accessPointId); + } + if (KErrCouldNotConnect == aError && !(m_networkAccessControl->isLastAccessPoint())) { + load(m_source); + return; + } + TInt error = aError; + if (KErrNone == error || KErrMMPartialPlayback == error) { + TSize originalSize; + TRAP(error, m_player->VideoFrameSizeL(originalSize)); + if (KErrNone == error) { + m_nativeSize = QSize(originalSize.iWidth, originalSize.iHeight); + emit nativeSizeChanged(m_nativeSize); + m_pendingChanges |= ScaleFactors; +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + Q_ASSERT(!m_audioOutput); + TRAP(error, m_audioOutput = CAudioOutput::NewL(*m_player)); + if (KErrNone == error) { + TRAP(error, m_audioOutput->RegisterObserverL(*this)); + if (KErrNone == error) + setActiveEndpoint(m_audioEndpoint); + } +#endif + } + if (KErrNone == error) { + applyPendingChanges(true); // force apply even though state is not Loaded + if (KErrNone == this->error()) // applyPendingChanges() can call setError() + loaded(); + } + } else { + setError(error); + } + + DP0("S60VideoPlayerSession::MvpuoPrepareComplete ---"); +} + +/*! + * Notification that frame requested by a call to GetFrameL is ready. +*/ + +void S60VideoPlayerSession::MvpuoFrameReady(CFbsBitmap &aFrame, TInt aError) +{ + DP0("S60VideoPlayerSession::MvpuoFrameReady +++"); + + Q_UNUSED(aFrame); + Q_UNUSED(aError); + + DP0("S60VideoPlayerSession::MvpuoFrameReady ---"); +} + +/*! + * Notification that video playback has completed. + * If successful then \a aError will be ZERO else system wide error. + * This not called if playback is explicitly stopped by calling stop. +*/ + +void S60VideoPlayerSession::MvpuoPlayComplete(TInt aError) +{ + DP0("S60VideoPlayerSession::MvpuoPlayComplete +++"); + + DP1("S60VideoPlayerSession::MvpuoPlayComplete - aError", aError); + + if (m_stream) + m_networkAccessControl->resetIndex(); + + if (aError != KErrNone) { + setError(aError); + doClose(); + } else { + endOfMedia(); + } + + DP0("S60VideoPlayerSession::MvpuoPlayComplete ---"); +} + + +/*! + * General \a event notification from controller. + * These events are specified by the supplier of the controller. +*/ + +void S60VideoPlayerSession::MvpuoEvent(const TMMFEvent &aEvent) +{ + DP0("S60VideoPlayerSession::MvpuoEvent +++"); + + Q_UNUSED(aEvent); + + DP0("S60VideoPlayerSession::MvpuoEvent ---"); +} + +/*! + + Updates meta data entries in the current video clip. +*/ + +void S60VideoPlayerSession::updateMetaDataEntriesL() +{ + DP0("S60VideoPlayerSession::updateMetaDataEntriesL +++"); + + metaDataEntries().clear(); + int numberOfMetaDataEntries = 0; + numberOfMetaDataEntries = m_player->NumberOfMetaDataEntriesL(); + for (int i = 0; i < numberOfMetaDataEntries; i++) { + CMMFMetaDataEntry *entry = NULL; + entry = m_player->MetaDataEntryL(i); + metaDataEntries().insert(TDesC2QString(entry->Name()), TDesC2QString(entry->Value())); + delete entry; + } + emit metaDataChanged(); + + DP0("S60VideoPlayerSession::updateMetaDataEntriesL ---"); +} + +/*! + * Apply the window changes when window handle changes. +*/ + +void S60VideoPlayerSession::windowHandleChanged() +{ + DP0("S60VideoPlayerSession::windowHandleChanged +++"); + + m_pendingChanges |= WindowHandle; + applyPendingChanges(); + + DP0("S60VideoPlayerSession::windowHandleChanged ---"); +} + +/*! + * Apply the window changes when display Rect changes. +*/ + +void S60VideoPlayerSession::displayRectChanged() +{ + DP0("S60VideoPlayerSession::displayRectChanged +++"); + + m_pendingChanges |= DisplayRect; + applyPendingChanges(); + + DP0("S60VideoPlayerSession::displayRectChanged ---"); +} + +/*! + * Apply the window changes when aspect Ratio changes. +*/ + +void S60VideoPlayerSession::aspectRatioChanged() +{ + DP0("S60VideoPlayerSession::aspectRatioChanged +++"); + + m_pendingChanges |= ScaleFactors; + applyPendingChanges(); + + DP0("S60VideoPlayerSession::aspectRatioChanged ---"); +} + +void S60VideoPlayerSession::rotationChanged() +{ + m_pendingChanges |= ScaleFactors; + m_pendingChanges |= Rotation; + applyPendingChanges(); +} + +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES +void S60VideoPlayerSession::suspendDirectScreenAccess() +{ + DP0("S60VideoPlayerSession::suspendDirectScreenAccess +++"); + + m_dsaStopped = stopDirectScreenAccess(); + + DP0("S60VideoPlayerSession::suspendDirectScreenAccess ---"); +} + +void S60VideoPlayerSession::resumeDirectScreenAccess() +{ + DP0("S60VideoPlayerSession::resumeDirectScreenAccess +++"); + + if (!m_dsaStopped) + return; + startDirectScreenAccess(); + m_dsaStopped = false; + + DP0("S60VideoPlayerSession::resumeDirectScreenAccess ---"); +} + +void S60VideoPlayerSession::startDirectScreenAccess() +{ + DP0("S60VideoPlayerSession::startDirectScreenAccess +++"); + + if (m_dsaActive) + return; + TRAPD(err, m_player->StartDirectScreenAccessL()); + if (err == KErrNone) + m_dsaActive = true; + setError(err); + + DP0("S60VideoPlayerSession::startDirectScreenAccess ---"); +} + +bool S60VideoPlayerSession::stopDirectScreenAccess() +{ + DP0("S60VideoPlayerSession::stopDirectScreenAccess"); + + if (!m_dsaActive) + return false; + TRAPD(err, m_player->StopDirectScreenAccessL()); + if (err == KErrNone) + m_dsaActive = false; + setError(err); + return true; +} +#endif + +/*! + * The percentage of the temporary buffer filling before playback begins. +*/ + +void S60VideoPlayerSession::MvloLoadingStarted() +{ + DP0("S60VideoPlayerSession::MvloLoadingStarted +++"); + + buffering(); + + DP0("S60VideoPlayerSession::MvloLoadingStarted ---"); +} + +/*! + * Buffer is filled with data and to for continuing/start playback. +*/ + +void S60VideoPlayerSession::MvloLoadingComplete() +{ + DP0("S60VideoPlayerSession::MvloLoadingComplete +++"); + + buffered(); + + DP0("S60VideoPlayerSession::MvloLoadingComplete ---"); +} + +/*! + Defiens which Audio End point to use. + + \a audioEndpoint audioEndpoint name. +*/ + +void S60VideoPlayerSession::doSetAudioEndpoint(const QString& audioEndpoint) +{ + DP0("S60VideoPlayerSession::doSetAudioEndpoint +++"); + + DP1("S60VideoPlayerSession::doSetAudioEndpoint - ", audioEndpoint); + + m_audioEndpoint = audioEndpoint; + + DP0("S60VideoPlayerSession::doSetAudioEndpoint ---"); +} + +/*! + + Returns audioEndpoint name. +*/ + +QString S60VideoPlayerSession::activeEndpoint() const +{ + DP0("S60VideoPlayerSession::activeEndpoint +++"); + + QString outputName = m_audioEndpoint; +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + if (m_audioOutput) { + CAudioOutput::TAudioOutputPreference output = m_audioOutput->AudioOutput(); + outputName = qStringFromTAudioOutputPreference(output); + } +#endif + + DP1("S60VideoPlayerSession::activeEndpoint- outputName:", outputName); + DP0("S60VideoPlayerSession::activeEndpoint ---"); + return outputName; +} + +/*! + * Returns default Audio End point in use. +*/ + +QString S60VideoPlayerSession::defaultEndpoint() const +{ + DP0("S60VideoPlayerSession::defaultEndpoint +++"); + + QString outputName = DefaultAudioEndpoint; +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + if (m_audioOutput) { + CAudioOutput::TAudioOutputPreference output = m_audioOutput->DefaultAudioOutput(); + outputName = qStringFromTAudioOutputPreference(output); + } +#endif + + DP1("S60VideoPlayerSession::defaultEndpoint, outputName:", outputName); + DP0("S60VideoPlayerSession::defaultEndpoint ---"); + + return outputName; +} + +/*! + Sets active end \a name as an Audio End point. +*/ + +void S60VideoPlayerSession::setActiveEndpoint(const QString& name) +{ + DP0("S60VideoPlayerSession::setActiveEndpoint +++"); + + DP1("S60VideoPlayerSession::setActiveEndpoint - ", name); + +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + CAudioOutput::TAudioOutputPreference output = CAudioOutput::ENoPreference; + if (name == DefaultAudioEndpoint) + output = CAudioOutput::ENoPreference; + else if (name == QString("All")) + output = CAudioOutput::EAll; + else if (name == QString("None")) + output = CAudioOutput::ENoOutput; + else if (name == QString("Earphone")) + output = CAudioOutput::EPrivate; + else if (name == QString("Speaker")) + output = CAudioOutput::EPublic; + if (m_audioOutput) { + TRAPD(err, m_audioOutput->SetAudioOutputL(output)); + setError(err); + } +#endif + + DP0("S60VideoPlayerSession::setActiveEndpoint ---"); +} + +/*! + The default Audio output has been changed. + + \a aAudioOutput Audio Output object. + + \a aNewDefault is CAudioOutput::TAudioOutputPreference. +*/ + +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER +void S60VideoPlayerSession::DefaultAudioOutputChanged( CAudioOutput& aAudioOutput, + CAudioOutput::TAudioOutputPreference aNewDefault) +{ + DP0("S60VideoPlayerSession::DefaultAudioOutputChanged +++"); + + // Emit already implemented in setActiveEndpoint function + Q_UNUSED(aAudioOutput) + Q_UNUSED(aNewDefault) + + DP0("S60VideoPlayerSession::DefaultAudioOutputChanged ---"); +} + +/*! + * \return CAudioOutput::ENoOutput by converting it to QString. +*/ + +QString S60VideoPlayerSession::qStringFromTAudioOutputPreference(CAudioOutput::TAudioOutputPreference output) const +{ + DP0("S60VideoPlayerSession::qStringFromTAudioOutputPreference"); + + if (output == CAudioOutput::ENoPreference) + return QString("Default"); + else if (output == CAudioOutput::EAll) + return QString("All"); + else if (output == CAudioOutput::ENoOutput) + return QString("None"); + else if (output == CAudioOutput::EPrivate) + return QString("Earphone"); + else if (output == CAudioOutput::EPublic) + return QString("Speaker"); + return QString("Default"); +} +#endif //HAS_AUDIOROUTING_IN_VIDEOPLAYER) + +/*! + * \return TRUE if video is Seekable else FALSE. +*/ + +bool S60VideoPlayerSession::getIsSeekable() const +{ + DP0("S60VideoPlayerSession::getIsSeekable +++"); + + bool seekable = ETrue; + int numberOfMetaDataEntries = 0; + + TRAPD(err, numberOfMetaDataEntries = m_player->NumberOfMetaDataEntriesL()); + if (err) + return seekable; + + for (int i = 0; i < numberOfMetaDataEntries; i++) { + CMMFMetaDataEntry *entry = NULL; + TRAP(err, entry = m_player->MetaDataEntryL(i)); + + if (err) + return seekable; + + if (!entry->Name().Compare(KSeekable)) { + if (!entry->Value().Compare(KFalse)) + seekable = EFalse; + break; + } + } + DP0("S60VideoPlayerSession::getIsSeekable ---"); + + return seekable; +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.h b/src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.h new file mode 100644 index 000000000..f73683af8 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videoplayersession.h @@ -0,0 +1,218 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOPLAYERSESSION_H +#define S60VIDEOPLAYERSESSION_H + +#include "s60mediaplayersession.h" +#include "s60mediaplayeraudioendpointselector.h" +#include "s60medianetworkaccesscontrol.h" +#include "s60videodisplay.h" + +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES +#include +#else +#include +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + +#include +#include +#include + +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER +#include +#include +#endif // HAS_AUDIOROUTING_IN_VIDEOPLAYER + +class QTimer; +class S60MediaNetworkAccessControl; +class S60VideoDisplay; + +// Helper classes to pass Symbian events from WServ to the S60VideoPlayerSession +// so it can control video player on certain events if required + +class ApplicationFocusObserver +{ +public: + virtual void applicationGainedFocus() = 0; + virtual void applicationLostFocus() = 0; +}; + +class S60VideoPlayerEventHandler : public QObject +{ +public: + static S60VideoPlayerEventHandler *instance(); + static bool filterEvent(void *message, long *result); + void addApplicationFocusObserver(ApplicationFocusObserver* observer); + void removeApplicationFocusObserver(ApplicationFocusObserver* observer); +private: + S60VideoPlayerEventHandler(); + ~S60VideoPlayerEventHandler(); +private: + static S60VideoPlayerEventHandler *m_instance; + static QList m_applicationFocusObservers; + static QCoreApplication::EventFilter m_eventFilter; +}; + +class S60VideoPlayerSession : public S60MediaPlayerSession + , public MVideoPlayerUtilityObserver + , public MVideoLoadingObserver +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + , public MAudioOutputObserver +#endif // HAS_AUDIOROUTING_IN_VIDEOPLAYER + , public ApplicationFocusObserver +{ + Q_OBJECT +public: + S60VideoPlayerSession(QMediaService *service, S60MediaNetworkAccessControl *object); + ~S60VideoPlayerSession(); + + // From S60MediaPlayerSession + bool isVideoAvailable(); + bool isAudioAvailable(); + void setVideoRenderer(QObject *renderer); + + // From MVideoLoadingObserver + void MvloLoadingStarted(); + void MvloLoadingComplete(); + void setPlaybackRate(qreal rate); +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + // From MAudioOutputObserver + void DefaultAudioOutputChanged(CAudioOutput& aAudioOutput, + CAudioOutput::TAudioOutputPreference aNewDefault); +#endif + + // From S60MediaPlayerAudioEndpointSelector + QString activeEndpoint() const; + QString defaultEndpoint() const; + + // ApplicationFocusObserver + void applicationGainedFocus(); + void applicationLostFocus(); + +signals: + void nativeSizeChanged(QSize); + +public Q_SLOTS: + void setActiveEndpoint(const QString& name); + +signals: + void accessPointChanged(int); + +protected: + // From S60MediaPlayerSession + void doLoadL(const TDesC &path); + void doLoadUrlL(const TDesC &path); + void doPlay(); + void doStop(); + void doClose(); + void doPauseL(); + void doSetVolumeL(int volume); + qint64 doGetPositionL() const; + void doSetPositionL(qint64 microSeconds); + void updateMetaDataEntriesL(); + int doGetBufferStatusL() const; + qint64 doGetDurationL() const; + void doSetAudioEndpoint(const QString& audioEndpoint); + bool getIsSeekable() const; + +private slots: + void windowHandleChanged(); + void displayRectChanged(); + void aspectRatioChanged(); + void rotationChanged(); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + void suspendDirectScreenAccess(); + void resumeDirectScreenAccess(); +#endif + +private: + void applyPendingChanges(bool force = false); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + void startDirectScreenAccess(); + bool stopDirectScreenAccess(); +#endif +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + QString qStringFromTAudioOutputPreference(CAudioOutput::TAudioOutputPreference output) const; +#endif + + // From MVideoPlayerUtilityObserver + void MvpuoOpenComplete(TInt aError); + void MvpuoPrepareComplete(TInt aError); + void MvpuoFrameReady(CFbsBitmap &aFrame, TInt aError); + void MvpuoPlayComplete(TInt aError); + void MvpuoEvent(const TMMFEvent &aEvent); + +private: + int m_accessPointId; + S60MediaNetworkAccessControl* m_networkAccessControl; + RWsSession *const m_wsSession; + CWsScreenDevice *const m_screenDevice; + QMediaService *const m_service; +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + CVideoPlayerUtility2 *m_player; +#else + CVideoPlayerUtility *m_player; + bool m_dsaActive; + bool m_dsaStopped; +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + QObject *m_videoOutputControl; + S60VideoDisplay *m_videoOutputDisplay; + RWindow *m_displayWindow; + QSize m_nativeSize; +#ifdef HTTP_COOKIES_ENABLED + TMMFMessageDestinationPckg m_destinationPckg; +#endif +#ifdef HAS_AUDIOROUTING_IN_VIDEOPLAYER + CAudioOutput *m_audioOutput; +#endif + QString m_audioEndpoint; + enum Parameter { + WindowHandle = 0x1, + DisplayRect = 0x2, + ScaleFactors = 0x4, + Rotation = 0x8 + }; + QFlags m_pendingChanges; + bool m_backendInitiatedPause; +}; + +#endif diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videorenderer.cpp b/src/plugins/symbian/mmf/mediaplayer/s60videorenderer.cpp new file mode 100644 index 000000000..2de6896a0 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videorenderer.cpp @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60videorenderer.h" + +#include +#include + +/*! + Constructs a new video renderer media end point with the given \a parent. +*/ + +S60VideoRenderer::S60VideoRenderer(QObject *parent) + : QVideoRendererControl(parent) +{ + DP0("S60VideoRenderer::S60VideoRenderer +++"); + + DP0("S60VideoRenderer::S60VideoRenderer ---"); + +} + +/*! + Destroys a video renderer media end point. +*/ + +S60VideoRenderer::~S60VideoRenderer() +{ + DP0("S60VideoRenderer::~S60VideoRenderer +++"); + DP0("S60VideoRenderer::~S60VideoRenderer ---"); +} + +/*! + \return the surface a video producer renders to. +*/ + +QAbstractVideoSurface *S60VideoRenderer::surface() const +{ + DP0("S60VideoRenderer::surface"); + + return m_surface; +} + +/*! + Sets the \a surface a video producer renders to. +*/ + +void S60VideoRenderer::setSurface(QAbstractVideoSurface *surface) +{ + DP0("S60VideoRenderer::setSurface +++"); + + m_surface = surface; + + DP0("S60VideoRenderer::setSurface ---"); +} + diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videorenderer.h b/src/plugins/symbian/mmf/mediaplayer/s60videorenderer.h new file mode 100644 index 000000000..6e90d42c3 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videorenderer.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEORENDERER_H +#define S60VIDEORENDERER_H + +#include +#include + +QT_USE_NAMESPACE + +class S60VideoRenderer : public QVideoRendererControl +{ + Q_OBJECT + +public: + S60VideoRenderer(QObject *parent = 0); + virtual ~S60VideoRenderer(); + + QAbstractVideoSurface *surface() const; + void setSurface(QAbstractVideoSurface *surface); + +private: + + QAbstractVideoSurface *m_surface; +}; + +#endif // S60VIDEORENDERER_H diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videosurface.cpp b/src/plugins/symbian/mmf/mediaplayer/s60videosurface.cpp new file mode 100644 index 000000000..563d33b40 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videosurface.cpp @@ -0,0 +1,372 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include + +#include "s60videosurface.h" +/*! + * Constructs a video surface with the given \a parent. +*/ + +S60VideoSurface::S60VideoSurface(QObject *parent) + : QAbstractVideoSurface(parent) + , m_winId(0) +{ + DP0("S60VideoSurface::S60VideoSurface +++"); + DP0("S60VideoSurface::S60VideoSurface ---"); +} + +/*! + * Destroys video surface. +*/ + +S60VideoSurface::~S60VideoSurface() +{ + DP0("S60VideoSurface::~S60VideoSurface +++"); + DP0("S60VideoSurface::~S60VideoSurface ---"); +} + +/*! + \return the ID of the window a video surface end point renders to. +*/ + +WId S60VideoSurface::winId() const +{ + DP0("S60VideoSurface::winId"); + + return m_winId; +} + +/*! + Sets the \a id of the window a video surface end point renders to. +*/ + +void S60VideoSurface::setWinId(WId id) +{ + DP0("S60VideoSurface::setWinId +++"); + + m_winId = id; + + DP0("S60VideoSurface::setWinId ---"); +} + +/*! + \return the sub-rect of a window where video is displayed. +*/ + +QRect S60VideoSurface::displayRect() const +{ + DP0("S60VideoSurface::displayRect"); + + return m_displayRect; +} + +/*! + Sets the sub-\a rect of a window where video is displayed. +*/ + +void S60VideoSurface::setDisplayRect(const QRect &rect) +{ + DP0("S60VideoSurface::setDisplayRect +++"); + + m_displayRect = rect; + + DP0("S60VideoSurface::setDisplayRect ---"); +} + +/*! + \return the brightness adjustment applied to a video surface. + + Valid brightness values range between -100 and 100, the default is 0. +*/ + +int S60VideoSurface::brightness() const +{ + DP0("S60VideoSurface::brightness"); + + return 0; +} + +/*! + Sets a \a brightness adjustment for a video surface. + + Valid brightness values range between -100 and 100, the default is 0. +*/ + +void S60VideoSurface::setBrightness(int brightness) +{ + DP0("S60VideoSurface::setBrightness +++"); + + DP1("S60VideoSurface::setBrightness - brightness:", brightness); + + Q_UNUSED(brightness); + + DP0("S60VideoSurface::setBrightness ---"); +} + +/*! + \return the contrast adjustment applied to a video surface. + + Valid contrast values range between -100 and 100, the default is 0. +*/ + +int S60VideoSurface::contrast() const +{ + DP0("S60VideoSurface::contrast"); + + return 0; +} + +/*! + Sets the \a contrast adjustment for a video surface. + + Valid contrast values range between -100 and 100, the default is 0. +*/ + +void S60VideoSurface::setContrast(int contrast) +{ + DP0("S60VideoSurface::setContrast +++"); + + DP1("S60VideoSurface::setContrast - ", contrast); + + Q_UNUSED(contrast); + + DP0("S60VideoSurface::setContrast ---"); +} + +/*! + \return the hue adjustment applied to a video surface. + + Value hue values range between -100 and 100, the default is 0. +*/ + +int S60VideoSurface::hue() const +{ + DP0("S60VideoSurface::hue"); + + return 0; +} + +/*! + Sets a \a hue adjustment for a video surface. + + Valid hue values range between -100 and 100, the default is 0. +*/ + +void S60VideoSurface::setHue(int hue) +{ + DP0("S60VideoSurface::setHue +++"); + + DP1("S60VideoSurface::setHue - ", hue); + + Q_UNUSED(hue); + + DP0("S60VideoSurface::setHue ---"); +} + +/*! + \return the saturation adjustment applied to a video surface. + + Value saturation values range between -100 and 100, the default is 0. +*/ + +int S60VideoSurface::saturation() const +{ + DP0("S60VideoSurface::saturation"); + + return 0; +} + +/*! + Sets a \a saturation adjustment for a video surface. + + Valid saturation values range between -100 and 100, the default is 0. +*/ + +void S60VideoSurface::setSaturation(int saturation) +{ + DP0("S60VideoSurface::setSaturation +++"); + + DP1("S60VideoSurface::setSaturation - ", saturation); + + Q_UNUSED(saturation); + + DP0("S60VideoSurface::setSaturation ---"); +} + +/*! + * \return ZERO. \a attribute, \a minimum, \a maximum are not used. +*/ +int S60VideoSurface::getAttribute(const char *attribute, int minimum, int maximum) const +{ + DP0("S60VideoSurface::getAttribute +++"); + + Q_UNUSED(attribute); + Q_UNUSED(minimum); + Q_UNUSED(maximum); + + DP0("S60VideoSurface::getAttribute ---"); + + return 0; +} + +/*! + * Sets the \a attribute, \a minimum, \a maximum. + * But never used. +*/ + +void S60VideoSurface::setAttribute(const char *attribute, int value, int minimum, int maximum) +{ + DP0("S60VideoSurface::setAttribute +++"); + + Q_UNUSED(attribute); + Q_UNUSED(value); + Q_UNUSED(minimum); + Q_UNUSED(maximum); + + DP0("S60VideoSurface::setAttribute ---"); + +} + +/*! + * \return ZERO. + * \a value, \a fromLower, \a fromUpper, \a toLower, \a toUpper are never used. +*/ + +int S60VideoSurface::redistribute( + int value, int fromLower, int fromUpper, int toLower, int toUpper) +{ + DP0("S60VideoSurface::redistribute +++"); + + Q_UNUSED(value); + Q_UNUSED(fromLower); + Q_UNUSED(fromUpper); + Q_UNUSED(toLower); + Q_UNUSED(toUpper); + + DP0("S60VideoSurface::redistribute ---"); + + return 0; +} + +/*! + * \return List of video surface supported Pixel Formats. +*/ + +QList S60VideoSurface::supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType) const +{ + DP0("S60VideoSurface::supportedPixelFormats +++"); + + Q_UNUSED(handleType); + QList list; + + DP0("S60VideoSurface::supportedPixelFormats ---"); + + return list; +} + +/*! + * \return always FALSE, as \a format never used. +*/ + +bool S60VideoSurface::start(const QVideoSurfaceFormat &format) +{ + DP0("S60VideoSurface::start"); + + Q_UNUSED(format); + return false; +} + +/*! + * Stops video surface. +*/ +void S60VideoSurface::stop() +{ + DP0("S60VideoSurface::stop +++"); + + DP0("S60VideoSurface::stop ---"); + +} + +/*! + * \return always FALS, as \a format is never used. +*/ +bool S60VideoSurface::present(const QVideoFrame &frame) +{ + DP0("S60VideoSurface::present"); + + Q_UNUSED(frame); + return false; +} + +/*! + * \return always FALSE. +*/ + +bool S60VideoSurface::findPort() +{ + DP0("S60VideoSurface::findPort"); + + return false; +} + +void S60VideoSurface::querySupportedFormats() +{ + DP0("S60VideoSurface::querySupportedFormats +++"); + + DP0("S60VideoSurface::querySupportedFormats ---"); + +} + +/*! + * \return always FLASE, as \a format never used. +*/ + +bool S60VideoSurface::isFormatSupported(const QVideoSurfaceFormat &format) const +{ + DP0("S60VideoSurface::isFormatSupported"); + + Q_UNUSED(format); + return false; +} diff --git a/src/plugins/symbian/mmf/mediaplayer/s60videosurface.h b/src/plugins/symbian/mmf/mediaplayer/s60videosurface.h new file mode 100644 index 000000000..9f13755b7 --- /dev/null +++ b/src/plugins/symbian/mmf/mediaplayer/s60videosurface.h @@ -0,0 +1,106 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOSURFACE_H +#define S60VIDEOSURFACE_H + +#include +#include + +class S60VideoSurface : public QAbstractVideoSurface +{ + Q_OBJECT +public: + S60VideoSurface(QObject *parent = 0); + ~S60VideoSurface(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + QList supportedPixelFormats( + QAbstractVideoBuffer::HandleType handleType = QAbstractVideoBuffer::NoHandle) const; + + bool isFormatSupported(const QVideoSurfaceFormat &format) const; + + bool start(const QVideoSurfaceFormat &format); + void stop(); + + bool present(const QVideoFrame &frame); + +private: + WId m_winId; + //XvPortID m_portId; + //GC m_gc; + //XvImage *m_image; + QList m_supportedPixelFormats; + QVector m_formatIds; + QRect m_viewport; + QRect m_displayRect; + QPair m_brightnessRange; + QPair m_contrastRange; + QPair m_hueRange; + QPair m_saturationRange; + + bool findPort(); + void querySupportedFormats(); + + int getAttribute(const char *attribute, int minimum, int maximum) const; + void setAttribute(const char *attribute, int value, int minimum, int maximum); + + static int redistribute(int value, int fromLower, int fromUpper, int toLower, int toUpper); +}; + +#endif diff --git a/src/plugins/symbian/mmf/mmf.pro b/src/plugins/symbian/mmf/mmf.pro new file mode 100644 index 000000000..2bae03e59 --- /dev/null +++ b/src/plugins/symbian/mmf/mmf.pro @@ -0,0 +1,58 @@ +TEMPLATE = lib + +CONFIG += plugin +TARGET = $$qtLibraryTarget(qtmultimediakit_mmfengine) +PLUGIN_TYPE = mediaservice +include (../../../../common.pri) +qtAddLibrary(QtMultimediaKit) + +#includes here so that all defines are added here also +include(mediaplayer/mediaplayer_s60.pri) +include(radio/radio.pri) + +QT += network + +# we include mmf audiorecording only if we are not building openmaxal based backend +!contains(openmaxal_symbian_enabled, yes) { + message("Enabling mmf mediarecording backend") + include(audiosource/audiosource_s60.pri) +} + +DEPENDPATH += . +INCLUDEPATH += . \ + $${SOURCE_DIR}/include \ + $${SOURCE_DIR}/src/multimedia \ + $${SOURCE_DIR}/src/multimedia/audio \ + $${SOURCE_DIR}/src/multimedia/video \ + $${SOURCE_DIR}/plugins/multimedia/symbian/mmf/inc \ + $${SOURCE_DIR} + + +HEADERS += s60mediaserviceplugin.h \ + s60formatsupported.h + +SOURCES += s60mediaserviceplugin.cpp \ + s60formatsupported.cpp + +contains(S60_VERSION, 3.2)|contains(S60_VERSION, 3.1) { + DEFINES += PRE_S60_50_PLATFORM +} +contains(mmf_http_cookies_enabled, yes) { + DEFINES += HTTP_COOKIES_ENABLED +} +load(data_caging_paths) +TARGET.EPOCALLOWDLLDATA = 1 +TARGET.UID3=0x2002AC76 +TARGET.CAPABILITY = ALL -TCB +MMP_RULES += EXPORTUNFROZEN + +#make a sis package from plugin + api + stub (plugin) +pluginDep.sources = $${TARGET}.dll +pluginDep.path = $${QT_PLUGINS_BASE_DIR}/$${PLUGIN_TYPE} +DEPLOYMENT += pluginDep + +#Media API spesific deployment +QtMediaDeployment.sources = QtMultimediaKit.dll +QtMediaDeployment.path = /sys/bin + +DEPLOYMENT += QtMediaDeployment diff --git a/src/plugins/symbian/mmf/radio/radio.pri b/src/plugins/symbian/mmf/radio/radio.pri new file mode 100644 index 000000000..a4703d126 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/radio.pri @@ -0,0 +1,24 @@ +INCLUDEPATH += $$PWD + +contains(tunerlib_s60_enabled, yes) { + + LIBS += -ltunerutility + DEFINES += TUNERLIBUSED + INCLUDEPATH += $${EPOCROOT}epoc32/include/mmf/common + + HEADERS += $$PWD/s60radiotunercontrol_31.h + SOURCES += $$PWD/s60radiotunercontrol_31.cpp +} + +contains(radioutility_s60_enabled, yes) { + LIBS += -lradio_utility + DEFINES += RADIOUTILITYLIBUSED + + HEADERS += $$PWD/s60radiotunercontrol_since32.h + SOURCES += $$PWD/s60radiotunercontrol_since32.cpp +} + +contains(tunerlib_s60_enabled, yes)|contains(radioutility_s60_enabled, yes) { + HEADERS += $$PWD/s60radiotunerservice.h + SOURCES += $$PWD/s60radiotunerservice.cpp +} diff --git a/src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.cpp b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.cpp new file mode 100644 index 000000000..b7627e312 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.cpp @@ -0,0 +1,603 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60radiotunercontrol_31.h" +#include "s60radiotunerservice.h" + +#include +#include + +// from AudioPreference.h +const TInt KAudioPriorityFMRadio = 79; +const TUint KAudioPrefRadioAudioEvent = 0x03000001; + +S60RadioTunerControl::S60RadioTunerControl(QObject *parent) + : QRadioTunerControl(parent) + , m_error(0) + , m_tunerState(0) + , m_apiTunerState(QRadioTuner::StoppedState) + , m_audioInitializationComplete(false) + , m_radioError(QRadioTuner::NoError) + , m_muted(false) + , m_isStereo(true) + , m_stereoMode(QRadioTuner::Auto) + , m_signal(0) + , m_currentBand(QRadioTuner::FM) + , m_currentFreq(87500000) + , m_scanning(false) + , m_vol(50) +{ + DP0("S60RadioTunerControl::S60RadioTunerControl +++"); + + initRadio(); + + DP0("S60RadioTunerControl::S60RadioTunerControl ---"); +} + +S60RadioTunerControl::~S60RadioTunerControl() +{ + DP0("S60RadioTunerControl::~S60RadioTunerControl +++"); + + if (m_tunerUtility) { + m_tunerUtility->Close(); + m_tunerUtility->CancelNotifyChange(); + m_tunerUtility->CancelNotifySignalStrength(); + m_tunerUtility->CancelNotifyStereoChange(); + delete m_tunerUtility; + } + if (m_audioPlayerUtility) { + m_audioPlayerUtility = NULL; + } + + DP0("S60RadioTunerControl::~S60RadioTunerControl ---"); +} + +bool S60RadioTunerControl::initRadio() +{ + DP0("S60RadioTunerControl::initRadio +++"); + + m_available = false; + + TRAPD(tunerError, m_tunerUtility = CMMTunerUtility::NewL(*this, CMMTunerUtility::ETunerBandFm, 1, + CMMTunerUtility::ETunerAccessPriorityNormal)); + if (tunerError != KErrNone) { + m_radioError = QRadioTuner::OpenError; + return m_available; + } + + TRAPD(playerError, m_audioPlayerUtility = m_tunerUtility->TunerPlayerUtilityL(*this)); + if (playerError != KErrNone) { + m_radioError = QRadioTuner::OpenError; + return m_available; + } + + TRAPD(initializeError, m_audioPlayerUtility->InitializeL(KAudioPriorityFMRadio, + TMdaPriorityPreference(KAudioPrefRadioAudioEvent))); + if (initializeError != KErrNone) { + m_radioError = QRadioTuner::OpenError; + return m_available; + } + + m_tunerUtility->NotifyChange(*this); + m_tunerUtility->NotifyStereoChange(*this); + m_tunerUtility->NotifySignalStrength(*this); + + TFrequency freq(m_currentFreq); + m_tunerUtility->Tune(freq); + + m_available = true; + + DP0("S60RadioTunerControl::initRadio ---"); + + return m_available; +} + +void S60RadioTunerControl::start() +{ + DP0("S60RadioTunerControl::start +++"); + + if (!m_audioInitializationComplete) { + TFrequency freq(m_currentFreq); + m_tunerUtility->Tune(freq); + } else { + m_audioPlayerUtility->Play(); + } + + m_apiTunerState = QRadioTuner::ActiveState; + emit stateChanged(m_apiTunerState); + + DP0("S60RadioTunerControl::start ---"); +} + +void S60RadioTunerControl::stop() +{ + DP0("S60RadioTunerControl::stop +++"); + + if (m_audioPlayerUtility) { + m_audioPlayerUtility->Stop(); + m_apiTunerState = QRadioTuner::StoppedState; + emit stateChanged(m_apiTunerState); + } + + DP0("S60RadioTunerControl::stop ---"); +} + +QRadioTuner::State S60RadioTunerControl::state() const +{ + DP0("S60RadioTunerControl::state"); + + return m_apiTunerState; +} + +QRadioTuner::Band S60RadioTunerControl::band() const +{ + DP0("S60RadioTunerControl::band"); + + return m_currentBand; +} + +bool S60RadioTunerControl::isBandSupported(QRadioTuner::Band b) const +{ + DP0("S60RadioTunerControl::isBandSupported"); + + if(b == QRadioTuner::FM) + return true; + else if(b == QRadioTuner::LW) + return false; + else if(b == QRadioTuner::AM) + return true; + else if(b == QRadioTuner::SW) + return false; + else + return false; +} + +void S60RadioTunerControl::setBand(QRadioTuner::Band b) +{ + DP0("S60RadioTunerControl::setBand +++"); + + QRadioTuner::Band tempBand = b; + if (tempBand != m_currentBand) { + m_currentBand = b; + emit bandChanged(m_currentBand); + } + + DP0("S60RadioTunerControl::setBand ---"); +} + +int S60RadioTunerControl::frequency() const +{ + DP0("S60RadioTunerControl::frequency"); + + return m_currentFreq; +} + +void S60RadioTunerControl::setFrequency(int frequency) +{ + DP0("S60RadioTunerControl::setFrequency +++"); + + m_currentFreq = frequency; + TFrequency freq(m_currentFreq); + m_tunerUtility->Tune(freq); + + DP0("S60RadioTunerControl::setFrequency ---"); +} + +int S60RadioTunerControl::frequencyStep(QRadioTuner::Band b) const +{ + DP0("S60RadioTunerControl::frequencyStep +++"); + + int step = 0; + + if(b == QRadioTuner::FM) + step = 100000; // 100kHz steps + else if(b == QRadioTuner::LW) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::AM) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::SW) + step = 500; // 500Hz steps + + DP1("S60RadioTunerControl::frequencyStep, Step:", step); + DP0("S60RadioTunerControl::frequencyStep ---"); + + return step; +} + +QPair S60RadioTunerControl::frequencyRange(QRadioTuner::Band band) const +{ + DP0("S60RadioTunerControl::frequencyRange +++"); + + TFrequency bottomFreq; + TFrequency topFreq; + int bandError = KErrNone; + + if (m_tunerUtility){ + bandError = m_tunerUtility->GetFrequencyBandRange(bottomFreq, topFreq); + if (!bandError) { + return qMakePair(bottomFreq.iFrequency, topFreq.iFrequency); + } + } + + DP0("S60RadioTunerControl::frequencyRange ---"); + + return qMakePair(0,0); +} + +CMMTunerUtility::TTunerBand S60RadioTunerControl::getNativeBand(QRadioTuner::Band b) const +{ + DP0("S60RadioTunerControl::getNativeBand"); + + // api match to native s60 bands + if (b == QRadioTuner::AM) + return CMMTunerUtility::ETunerBandAm; + else if (b == QRadioTuner::FM) + return CMMTunerUtility::ETunerBandFm; + else if (b == QRadioTuner::LW) + return CMMTunerUtility::ETunerBandLw; + else + return CMMTunerUtility::ETunerNoBand; +} + +bool S60RadioTunerControl::isStereo() const +{ + DP0("S60RadioTunerControl::isStereo"); + + return m_isStereo; +} + +QRadioTuner::StereoMode S60RadioTunerControl::stereoMode() const +{ + DP0("S60RadioTunerControl::stereoMode"); + + return m_stereoMode; +} + +void S60RadioTunerControl::setStereoMode(QRadioTuner::StereoMode mode) +{ + DP0("S60RadioTunerControl::setStereoMode +++"); + + m_stereoMode = mode; + if (m_tunerUtility) { + if (QRadioTuner::ForceMono == mode) + m_tunerUtility->ForceMonoReception(true); + else + m_tunerUtility->ForceMonoReception(false); + } + + DP0("S60RadioTunerControl::setStereoMode ---"); +} + +int S60RadioTunerControl::signalStrength() const +{ + DP0("S60RadioTunerControl::signalStrength +++"); + + // return value is a percentage value + if (m_tunerUtility) { + TInt maxSignalStrength; + TInt currentSignalStrength; + m_error = m_tunerUtility->GetMaxSignalStrength(maxSignalStrength); + if (m_error == KErrNone) { + m_error = m_tunerUtility->GetSignalStrength(currentSignalStrength); + if (m_error == KErrNone) { + if (maxSignalStrength == 0 || currentSignalStrength == 0) { + return 0; + } + m_signal = ((TInt64)currentSignalStrength) * 100 / maxSignalStrength; + } + } + } + + DP1("S60RadioTunerControl::signalStrength, m_signal:", m_signal); + DP0("S60RadioTunerControl::signalStrength ---"); + + return m_signal; +} + +int S60RadioTunerControl::volume() const +{ + DP0("S60RadioTunerControl::volume"); + + return m_vol; +} + +void S60RadioTunerControl::setVolume(int volume) +{ + DP0("S60RadioTunerControl::setVolume +++"); + DP1("S60RadioTunerControl::setVolume: ", volume); + + if (m_audioPlayerUtility) { + m_vol = volume; + TInt error = m_audioPlayerUtility->SetVolume(volume/10); + emit volumeChanged(m_vol); + } + + DP0("S60RadioTunerControl::setVolume ---"); +} + +bool S60RadioTunerControl::isMuted() const +{ + DP0("S60RadioTunerControl::isMuted"); + + return m_muted; +} + +void S60RadioTunerControl::setMuted(bool muted) +{ + DP0("S60RadioTunerControl::setMuted +++"); + + DP1("S60RadioTunerControl::setMuted:", muted); + + if (m_audioPlayerUtility && m_audioInitializationComplete) { + m_muted = muted; + m_audioPlayerUtility->Mute(m_muted); + emit mutedChanged(m_muted); + } + + DP0("S60RadioTunerControl::setMuted ---"); +} + +bool S60RadioTunerControl::isSearching() const +{ + DP0("S60RadioTunerControl::isSearching"); + + if (m_tunerUtility) { + TUint32 tempState; + m_tunerUtility->GetState(tempState); + if (tempState == CMMTunerUtility::ETunerStateRetuning || m_scanning) { + return true; + } else + return false; + } + return true; +} + +void S60RadioTunerControl::cancelSearch() +{ + DP0("S60RadioTunerControl::cancelSearch +++"); + + m_tunerUtility->CancelRetune(); + m_scanning = false; + emit searchingChanged(false); + + DP0("S60RadioTunerControl::cancelSearch ---"); +} + +void S60RadioTunerControl::searchForward() +{ + DP0("S60RadioTunerControl::searchForward +++"); + + m_scanning = true; + setVolume(m_vol); + m_tunerUtility->StationSeek(CMMTunerUtility::ESearchDirectionUp); + emit searchingChanged(true); + + DP0("S60RadioTunerControl::searchForward ---"); +} + +void S60RadioTunerControl::searchBackward() +{ + DP0("S60RadioTunerControl::searchBackward +++"); + + m_scanning = true; + setVolume(m_vol); + m_tunerUtility->StationSeek(CMMTunerUtility::ESearchDirectionDown); + emit searchingChanged(true); + + DP0("S60RadioTunerControl::searchBackward ---"); +} + +bool S60RadioTunerControl::isValid() const +{ + DP0("S60RadioTunerControl::isValid"); + + return m_available; +} + +bool S60RadioTunerControl::isAvailable() const +{ + DP0("S60RadioTunerControl::isAvailable"); + + return m_available; +} + +QtMultimediaKit::AvailabilityError S60RadioTunerControl::availabilityError() const +{ + DP0("S60RadioTunerControl::availabilityError"); + + if (m_available) + return QtMultimediaKit::NoError; + else + return QtMultimediaKit::ResourceError; +} + +QRadioTuner::Error S60RadioTunerControl::error() const +{ + DP1("QtMultimediaKit::NoError", m_radioError); + + return m_radioError; +} + +QString S60RadioTunerControl::errorString() const +{ + DP1("S60RadioTunerControl::errorString", m_errorString); + + return m_errorString; +} + +void S60RadioTunerControl::MToTuneComplete(TInt aError) +{ + DP0("S60RadioTunerControl::MToTuneComplete +++"); + DP1("S60RadioTunerControl::MToTuneComplete, aError:",aError); + + if (aError == KErrNone) { + m_scanning = false; + m_audioPlayerUtility->Play(); + if (!m_audioInitializationComplete) { + TRAPD(initializeError, m_audioPlayerUtility->InitializeL(KAudioPriorityFMRadio, + TMdaPriorityPreference(KAudioPrefRadioAudioEvent))); + if (initializeError != KErrNone) { + m_radioError = QRadioTuner::OpenError; + } + } + } + + DP0("S60RadioTunerControl::MToTuneComplete ---"); +} + +void S60RadioTunerControl::MTcoFrequencyChanged(const TFrequency& aOldFrequency, const TFrequency& aNewFrequency) +{ + DP0("S60RadioTunerControl::MTcoFrequencyChanged +++"); + + m_currentFreq = aNewFrequency.iFrequency; + m_scanning = false; + emit frequencyChanged(m_currentFreq); + + DP0("S60RadioTunerControl::MTcoFrequencyChanged ---"); +} + +void S60RadioTunerControl::MTcoStateChanged(const TUint32& aOldState, const TUint32& aNewState) +{ + DP0("S60RadioTunerControl::MTcoStateChanged +++"); + + if (aNewState == CMMTunerUtility::ETunerStateActive) { + m_apiTunerState = QRadioTuner::ActiveState; + } + if (aNewState == CMMTunerUtility::ETunerStatePlaying) { + m_apiTunerState = QRadioTuner::ActiveState; + } + if (aOldState != aNewState){ + emit stateChanged(m_apiTunerState); + } + + DP0("S60RadioTunerControl::MTcoStateChanged ---"); +} + +void S60RadioTunerControl::MTcoAntennaDetached() +{ + DP0("S60RadioTunerControl::MTcoAntennaDetached +++"); + + DP0("S60RadioTunerControl::MTcoAntennaDetached ---"); + + // no actions +} + +void S60RadioTunerControl::MTcoAntennaAttached() +{ + DP0("S60RadioTunerControl::MTcoAntennaAttached +++"); + + DP0("S60RadioTunerControl::MTcoAntennaAttached ---"); + + // no actions +} + +void S60RadioTunerControl::FlightModeChanged(TBool aFlightMode) +{ + DP0("S60RadioTunerControl::FlightModeChanged +++"); + + DP0("S60RadioTunerControl::FlightModeChanged ---"); + + // no actions +} + +void S60RadioTunerControl::MTsoStereoReceptionChanged(TBool aStereo) +{ + DP0("S60RadioTunerControl::MTsoStereoReceptionChanged +++"); + DP1("S60RadioTunerControl::MTsoStereoReceptionChanged, aStereo:", aStereo); + m_isStereo = aStereo; + emit stereoStatusChanged(aStereo); + + DP0("S60RadioTunerControl::MTsoStereoReceptionChanged ---"); +} + +void S60RadioTunerControl::MTsoForcedMonoChanged(TBool aForcedMono) +{ + DP0("S60RadioTunerControl::MTsoForcedMonoChanged +++"); + DP1("S60RadioTunerControl::MTsoForcedMonoChanged, aForcedMono:", aForcedMono); + + if (aForcedMono) { + m_stereoMode = QRadioTuner::ForceMono; + } + + DP0("S60RadioTunerControl::MTsoForcedMonoChanged ---"); +} + +void S60RadioTunerControl::MssoSignalStrengthChanged(TInt aNewSignalStrength) +{ + DP0("S60RadioTunerControl::MssoSignalStrengthChanged +++"); + DP1("S60RadioTunerControl::MssoSignalStrengthChanged, aNewSignalStrength:", aNewSignalStrength); + + m_signal = aNewSignalStrength; + emit signalStrengthChanged(m_signal); + + DP0("S60RadioTunerControl::MssoSignalStrengthChanged ---"); +} + +void S60RadioTunerControl::MTapoInitializeComplete(TInt aError) +{ + DP0("S60RadioTunerControl::MTapoInitializeComplete +++"); + DP1("S60RadioTunerControl::MTapoInitializeComplete, aError:", aError); + if (aError == KErrNone) { + m_audioInitializationComplete = true; + m_available = true; + m_audioPlayerUtility->Play(); + m_apiTunerState = QRadioTuner::ActiveState; + emit stateChanged(m_apiTunerState); + } else if (aError != KErrNone) { + m_radioError = QRadioTuner::OpenError; + } + + DP0("S60RadioTunerControl::MTapoInitializeComplete ---"); +} + +void S60RadioTunerControl::MTapoPlayEvent(TEventType aEvent, TInt aError, TAny* aAdditionalInfo) +{ + DP0("S60RadioTunerControl::MTapoPlayEvent +++"); + + DP0("S60RadioTunerControl::MTapoPlayEvent ---"); + + // no actions +} + + + diff --git a/src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.h b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.h new file mode 100644 index 000000000..c8bb8d362 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_31.h @@ -0,0 +1,161 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60RADIOTUNERCONTROL_H +#define S60RADIOTUNERCONTROL_H + +#include +#include +#include +#include + +class S60RadioTunerService; + +QT_USE_NAMESPACE + +class S60RadioTunerControl + : public QRadioTunerControl + , public MMMTunerObserver + , public MMMTunerStereoObserver + , public MMMSignalStrengthObserver + , public MMMTunerChangeObserver + , public MMMTunerAudioPlayerObserver +{ + Q_OBJECT +public: + S60RadioTunerControl(QObject *parent = 0); + ~S60RadioTunerControl(); + + QRadioTuner::State state() const; + + QRadioTuner::Band band() const; + void setBand(QRadioTuner::Band b); + bool isBandSupported(QRadioTuner::Band b) const; + + int frequency() const; + int frequencyStep(QRadioTuner::Band b) const; + QPair frequencyRange(QRadioTuner::Band b) const; + void setFrequency(int frequency); + + bool isStereo() const; + QRadioTuner::StereoMode stereoMode() const; + void setStereoMode(QRadioTuner::StereoMode mode); + + int signalStrength() const; + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + bool isSearching() const; + void searchForward(); + void searchBackward(); + void cancelSearch(); + + bool isValid() const; + + bool isAvailable() const; + QtMultimediaKit::AvailabilityError availabilityError() const; + + void start(); + void stop(); + + QRadioTuner::Error error() const; + QString errorString() const; + + //MMMTunerObserver + void MToTuneComplete(TInt aError); + + //MMMTunerChangeObserver + void MTcoFrequencyChanged(const TFrequency& aOldFrequency, const TFrequency& aNewFrequency); + void MTcoStateChanged(const TUint32& aOldState, const TUint32& aNewState); + void MTcoAntennaDetached(); + void MTcoAntennaAttached(); + void FlightModeChanged(TBool aFlightMode); + + //MMMTunerStereoObserver + void MTsoStereoReceptionChanged(TBool aStereo); + void MTsoForcedMonoChanged(TBool aForcedMono); + + //MMMSignalStrengthObserver + void MssoSignalStrengthChanged(TInt aNewSignalStrength); + + //MMMTunerAudioPlayerObserver + void MTapoInitializeComplete(TInt aError); + void MTapoPlayEvent(TEventType aEvent, TInt aError, TAny* aAdditionalInfo); + +private slots: + + +private: + bool initRadio(); + CMMTunerUtility::TTunerBand getNativeBand(QRadioTuner::Band b) const; + + mutable int m_error; + CMMTunerUtility *m_tunerUtility; + CMMTunerAudioPlayerUtility *m_audioPlayerUtility; + + bool m_audioInitializationComplete; + bool m_muted; + bool m_isStereo; + bool m_available; + int m_step; + int m_vol; + mutable int m_signal; + bool m_scanning; + bool forward; + QRadioTuner::Band m_currentBand; + qint64 m_currentFreq; + + QRadioTuner::Error m_radioError; + QRadioTuner::StereoMode m_stereoMode; + QString m_errorString; + //caps meaning what the tuner can do. + TTunerCapabilities m_currentTunerCapabilities; + long m_tunerState; + QRadioTuner::State m_apiTunerState; + +}; + +#endif + diff --git a/src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.cpp b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.cpp new file mode 100644 index 000000000..991c6b8e4 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.cpp @@ -0,0 +1,685 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60radiotunercontrol_since32.h" +#include "s60radiotunerservice.h" + +#include +#include + +S60RadioTunerControl::S60RadioTunerControl(QObject *parent) + : QRadioTunerControl(parent) + , m_error(0) + , m_radioUtility(NULL) + , m_fmTunerUtility(NULL) + , m_playerUtility(NULL) + , m_maxVolume(100) + , m_audioInitializationComplete(false) + , m_muted(false) + , m_isStereo(true) + , m_vol(50) + , m_signal(0) + , m_scanning(false) + , m_currentBand(QRadioTuner::FM) + , m_currentFreq(87500000) + , m_radioError(QRadioTuner::NoError) + , m_stereoMode(QRadioTuner::Auto) + , m_apiTunerState(QRadioTuner::StoppedState) + , m_previousSignal(0) + , m_volChangeRequired(false) + , m_signalStrengthTimer(new QTimer(this)) +{ + DP0("S60RadioTunerControl::S60RadioTunerControl +++"); + bool retValue = initRadio(); + if (!retValue) { + m_errorString = QString(tr("Initialize Error.")); + emit error(QRadioTuner::ResourceError); + } else { + connect(m_signalStrengthTimer, SIGNAL(timeout()), this, SLOT(changeSignalStrength())); + } + DP0("S60RadioTunerControl::S60RadioTunerControl ---"); +} + +S60RadioTunerControl::~S60RadioTunerControl() +{ + DP0("S60RadioTunerControl::~S60RadioTunerControl +++"); + + if (m_fmTunerUtility) { + m_fmTunerUtility->Close(); + } + + if (m_playerUtility) { + m_playerUtility->Close(); + } + + delete m_radioUtility; + + DP0("S60RadioTunerControl::~S60RadioTunerControl ---"); +} + +QRadioTuner::State S60RadioTunerControl::state() const +{ + DP0("S60RadioTunerControl::state"); + + return m_apiTunerState; +} + +QRadioTuner::Band S60RadioTunerControl::band() const +{ + DP0("S60RadioTunerControl::band"); + + return m_currentBand; +} + +bool S60RadioTunerControl::isBandSupported(QRadioTuner::Band b) const +{ + DP0("S60RadioTunerControl::isBandSupported"); + if (b == QRadioTuner::FM) + return true; + else if (b == QRadioTuner::LW) + return false; + else if (b == QRadioTuner::AM) + return false; + else if (b == QRadioTuner::SW) + return false; + else if (b == QRadioTuner::FM2) + return false; + else + return false; +} + +void S60RadioTunerControl::changeSignalStrength() + { + + int currentSignal = signalStrength(); + if (currentSignal != m_previousSignal) + { + m_previousSignal = currentSignal; + emit signalStrengthChanged(currentSignal); + } + } +void S60RadioTunerControl::setBand(QRadioTuner::Band b) +{ + DP0("S60RadioTunerControl::setBand +++"); + QRadioTuner::Band tempBand = b; + if (tempBand != m_currentBand ) { + if (isBandSupported(tempBand)){ + m_currentBand = b; + emit bandChanged(m_currentBand); + } + else { + switch(tempBand) + { + case QRadioTuner::FM : + m_errorString = QString(tr("Band FM not Supported")); + break; + case QRadioTuner::AM : + m_errorString = QString(tr("Band AM not Supported")); + break; + case QRadioTuner::SW : + m_errorString = QString(tr("Band SW not Supported")); + break; + case QRadioTuner::LW : + m_errorString = QString(tr("Band LW not Supported")); + break; + case QRadioTuner::FM2 : + m_errorString = QString(tr("Band FM2 not Supported")); + break; + default : + m_errorString = QString("Band %1 not Supported").arg(tempBand); + break; + } + emit error(QRadioTuner::OutOfRangeError); + } + } + + DP0("S60RadioTunerControl::setBand ---"); +} + +int S60RadioTunerControl::frequency() const +{ + DP0("S60RadioTunerControl::frequency"); + + return m_currentFreq; +} + +void S60RadioTunerControl::setFrequency(int frequency) +{ + DP0("S60RadioTunerControl::setFrequency +++"); + DP1("S60RadioTunerControl::setFrequency, frequency:", frequency); + + m_currentFreq = frequency; + m_fmTunerUtility->SetFrequency(m_currentFreq); + + DP0("S60RadioTunerControl::setFrequency ---"); +} +int S60RadioTunerControl::frequencyStep(QRadioTuner::Band b) const +{ + DP0("S60RadioTunerControl::frequencyStep +++"); + + int step = 0; + if (b == QRadioTuner::FM) + step = 100000; // 100kHz steps + else if(b == QRadioTuner::LW) + step = 1000; // 1kHz steps + else if (b == QRadioTuner::AM) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::SW) + step = 500; // 500Hz steps + DP1("S60RadioTunerControl::frequencyStep, Step:", step); + DP0("S60RadioTunerControl::frequencyStep ---"); + + return step; +} + +QPair S60RadioTunerControl::frequencyRange(QRadioTuner::Band band) const +{ + DP0("S60RadioTunerControl::frequencyRange +++"); + + int bottomFreq; + int topFreq; + + int bandError = KErrNone; + TFmRadioFrequencyRange range; + + if (m_fmTunerUtility) { + bandError = m_fmTunerUtility->GetFrequencyRange(range, bottomFreq, topFreq); + } + if (!bandError) { + return qMakePair(bottomFreq, topFreq); + } + + DP0("S60RadioTunerControl::frequencyRange ---"); + + return qMakePair(0,0); +} + +bool S60RadioTunerControl::isStereo() const +{ + DP0("S60RadioTunerControl::isStereo"); + + return m_isStereo; +} + +QRadioTuner::StereoMode S60RadioTunerControl::stereoMode() const +{ + DP0("S60RadioTunerControl::stereoMode"); + + return m_stereoMode; +} + +void S60RadioTunerControl::setStereoMode(QRadioTuner::StereoMode mode) +{ + DP0("S60RadioTunerControl::setStereoMode +++"); + + if (m_fmTunerUtility) { + if (QRadioTuner::ForceMono == mode) { + m_fmTunerUtility->ForceMonoReception(true); + m_stereoMode = QRadioTuner::ForceMono; + m_isStereo = false; + } else { + m_fmTunerUtility->ForceMonoReception(false); + m_isStereo = true; + m_stereoMode = QRadioTuner::ForceStereo; + } + } + + DP0("S60RadioTunerControl::setStereoMode ---"); +} + +int S60RadioTunerControl::signalStrength() const +{ + DP0("S60RadioTunerControl::signalStrength +++"); + + // return value is a percentage value + if (m_fmTunerUtility) { + TInt maxSignalStrength; + TInt currentSignalStrength; + m_error = m_fmTunerUtility->GetMaxSignalStrength(maxSignalStrength); + + if (m_error == KErrNone) { + m_error = m_fmTunerUtility->GetSignalStrength(currentSignalStrength); + if (m_error == KErrNone) { + if (currentSignalStrength == 0 || maxSignalStrength == 0) { + return currentSignalStrength; + } + m_signal = ((TInt64)currentSignalStrength) * 100 / maxSignalStrength; + } + } + } + + DP1("S60RadioTunerControl::signalStrength, m_signal:", m_signal); + DP0("S60RadioTunerControl::signalStrength ---"); + + return m_signal; +} + +int S60RadioTunerControl::volume() const +{ + DP0("S60RadioTunerControl::volume"); + + return m_vol; +} + +void S60RadioTunerControl::setVolume(int volume) +{ + DP0("S60RadioTunerControl::setVolume +++"); + DP1("S60RadioTunerControl::setVolume, Volume:", volume); + + int boundVolume = qBound(0, volume, 100); + + if (m_vol == boundVolume ) + return; + + if (!m_muted && m_playerUtility) { + m_vol = boundVolume; + // Don't set volume until State is in Active State. + if (state() == QRadioTuner::ActiveState ) { + m_playerUtility->SetVolume(m_vol*m_volMultiplier); + + } else { + m_volChangeRequired = TRUE; + emit volumeChanged(boundVolume); + } + } + DP0("S60RadioTunerControl::setVolume ---"); +} + +bool S60RadioTunerControl::isMuted() const +{ + DP0("S60RadioTunerControl::isMuted"); + + return m_muted; +} + +void S60RadioTunerControl::setMuted(bool muted) +{ + DP0("S60RadioTunerControl::setMuted +++"); + DP1("S60RadioTunerControl::setMuted, Muted:", muted); + if (m_playerUtility) { + m_muted = muted; + m_playerUtility->Mute(m_muted); + } + DP0("S60RadioTunerControl::setMuted ---"); +} + +bool S60RadioTunerControl::isSearching() const +{ + DP0("S60RadioTunerControl::isSearching"); + + return m_scanning; +} + +void S60RadioTunerControl::cancelSearch() +{ + DP0("S60RadioTunerControl::cancelSearch +++"); + + m_fmTunerUtility->CancelStationSeek(); + m_scanning = false; + emit searchingChanged(false); + + DP0("S60RadioTunerControl::cancelSearch ---"); +} + +void S60RadioTunerControl::searchForward() +{ + DP0("S60RadioTunerControl::searchForward +++"); + m_fmTunerUtility->StationSeek(true); + m_scanning = true; + emit searchingChanged(m_scanning); + DP0("S60RadioTunerControl::searchForward ---"); +} + +void S60RadioTunerControl::searchBackward() +{ + DP0("S60RadioTunerControl::searchBackward +++"); + m_fmTunerUtility->StationSeek(false); + m_scanning = true; + emit searchingChanged(m_scanning); + DP0("S60RadioTunerControl::searchBackward ---"); +} + +bool S60RadioTunerControl::isValid() const +{ + DP0("S60RadioTunerControl::isValid"); + + return m_available; +} + +bool S60RadioTunerControl::initRadio() +{ + DP0("S60RadioTunerControl::initRadio +++"); + m_available = false; + // create an instance of Radio Utility factory and indicate + // FM Radio is a primary client + TRAPD(utilityError, + m_radioUtility = CRadioUtility::NewL(ETrue); + // Get a tuner utility + m_fmTunerUtility = &m_radioUtility->RadioFmTunerUtilityL(*this); + // we want to listen radio in offline mode too + m_fmTunerUtility->EnableTunerInOfflineMode(ETrue); + // Get a player utility + m_playerUtility = &m_radioUtility->RadioPlayerUtilityL(*this); + ); + if (utilityError != KErrNone) { + m_radioError = QRadioTuner::ResourceError; + return m_available; + } + + m_tunerControl = false; + + m_available = true; + DP1("S60RadioTunerControl::initRadio, m_available:", m_available); + DP0("S60RadioTunerControl::initRadio ---"); + return m_available; +} + +bool S60RadioTunerControl::isAvailable() const +{ + DP0("S60RadioTunerControl::isAvailable"); + + return m_available; +} + +QtMultimediaKit::AvailabilityError S60RadioTunerControl::availabilityError() const +{ + DP0("S60RadioTunerControl::availabilityError"); + if (m_available) + return QtMultimediaKit::NoError; + else + return QtMultimediaKit::ResourceError; +} + +void S60RadioTunerControl::start() +{ + DP0("S60RadioTunerControl::start +++"); + if (!m_tunerControl) { + m_fmTunerUtility->RequestTunerControl(); + } else { + m_playerUtility->Play(); + } + m_signalStrengthTimer->start(3000); + + DP0("S60RadioTunerControl::start ---"); +} + +void S60RadioTunerControl::stop() +{ + DP0("S60RadioTunerControl::stop +++"); + if (m_playerUtility) { + m_playerUtility->Stop(); + } + m_signalStrengthTimer->stop(); + DP0("S60RadioTunerControl::stop ---"); +} + +QRadioTuner::Error S60RadioTunerControl::error() const +{ + DP1("S60RadioTunerControl::error", m_radioError); + + return m_radioError; +} +QString S60RadioTunerControl::errorString() const +{ + DP1("S60RadioTunerControl::errorString", m_errorString); + + return m_errorString; +} + +void S60RadioTunerControl::MrpoStateChange(TPlayerState aState, TInt aError) +{ + DP0("S60RadioTunerControl::MrpoStateChange +++"); + if (aError == KErrNone){ + m_radioError = QRadioTuner::NoError; + if (aState == ERadioPlayerIdle) { + m_apiTunerState = QRadioTuner::StoppedState; + } else if (aState == ERadioPlayerPlaying) { + m_apiTunerState = QRadioTuner::ActiveState; + //Apply pending volume changes. + if(m_volChangeRequired){ + setVolume(m_vol); + } + } + } else { + m_apiTunerState = QRadioTuner::StoppedState; + } + emit stateChanged(m_apiTunerState); + DP0("S60RadioTunerControl::MrpoStateChange ---"); +} + +void S60RadioTunerControl::MrpoVolumeChange(TInt aVolume) +{ + DP0("S60RadioTunerControl::MrpoVolumeChange +++"); + DP1("S60RadioTunerControl::MrpoVolumeChange, aVolume:", aVolume); + m_vol = (aVolume/m_volMultiplier); + if (!m_volChangeRequired) { + emit volumeChanged(m_vol); + + } else { + m_volChangeRequired = false; + } + DP0("S60RadioTunerControl::MrpoVolumeChange ---"); +} + +void S60RadioTunerControl::MrpoMuteChange(TBool aMute) +{ + DP0("S60RadioTunerControl::MrpoMuteChange +++"); + DP1("S60RadioTunerControl::MrpoMuteChange, aMute:", aMute); + m_muted = aMute; + emit mutedChanged(m_muted); + DP0("S60RadioTunerControl::MrpoMuteChange ---"); +} + +void S60RadioTunerControl::MrpoBalanceChange(TInt aLeftPercentage, TInt aRightPercentage) +{ + DP0("S60RadioTunerControl::MrpoBalanceChange +++"); + + DP0("S60RadioTunerControl::MrpoBalanceChange ---"); + + // no actions +} + +void S60RadioTunerControl::MrftoRequestTunerControlComplete(TInt aError) +{ + DP0("S60RadioTunerControl::MrftoRequestTunerControlComplete +++"); + DP1("S60RadioTunerControl::MrftoRequestTunerControlComplete, aError:", aError); + if (aError == KErrNone) { + m_playerUtility->GetMaxVolume(m_maxVolume); + m_volMultiplier = float(m_maxVolume)/float(100); + m_radioError = QRadioTuner::NoError; + m_tunerControl = true; + m_available = true; + m_fmTunerUtility->SetFrequency(m_currentFreq); + m_playerUtility->Play(); + int signal = signalStrength(); + if (m_signal != signal) { + emit signalStrengthChanged(signal); + m_signal = signal; + } + + } else if (aError == KFmRadioErrAntennaNotConnected) { + m_radioError = QRadioTuner::OpenError; + m_errorString = QString(tr("Antenna Not Connected")); + emit error(m_radioError); + } else if (aError == KErrAlreadyExists){ + m_radioError = QRadioTuner::ResourceError; + m_errorString = QString(tr("Resource Error.")); + emit error(m_radioError); + } else if (aError == KFmRadioErrFrequencyOutOfBandRange) { + m_radioError = QRadioTuner::OutOfRangeError; + m_errorString = QString(tr("Frequency out of band range")); + emit error(m_radioError); + }else{ + m_radioError = QRadioTuner::OpenError; + m_errorString = QString(tr("Unknown Error.")); + emit error(m_radioError); + } + + DP0("S60RadioTunerControl::MrftoRequestTunerControlComplete ---"); +} + +void S60RadioTunerControl::MrftoSetFrequencyRangeComplete(TInt aError) +{ + DP0("S60RadioTunerControl::MrftoSetFrequencyRangeComplete +++"); + DP1("S60RadioTunerControl::MrftoSetFrequencyRangeComplete, aError:", aError); + if (aError == KFmRadioErrFrequencyOutOfBandRange || KFmRadioErrFrequencyNotValid) { + m_radioError = QRadioTuner::OutOfRangeError; + m_errorString = QString(tr("Frequency Out of Band Range or Frequency Not Valid")); + emit error(m_radioError); + } else if (aError == KFmRadioErrHardwareFaulty || KFmRadioErrOfflineMode) { + m_radioError = QRadioTuner::OpenError; + m_errorString = QString(tr("Hardware failure or RadioInOfflineMode")); + emit error(m_radioError); + } + DP0("S60RadioTunerControl::MrftoSetFrequencyRangeComplete ---"); +} + +void S60RadioTunerControl::MrftoSetFrequencyComplete(TInt aError) +{ + DP0("S60RadioTunerControl::MrftoSetFrequencyComplete +++"); + DP1("S60RadioTunerControl::MrftoSetFrequencyComplete, aError", aError); + if (aError == KErrNone) { + m_radioError = QRadioTuner::NoError; + } else if (aError == KFmRadioErrFrequencyOutOfBandRange || KFmRadioErrFrequencyNotValid) { + m_radioError = QRadioTuner::OutOfRangeError; + m_errorString = QString(tr("Frequency Out of range or not Valid.")); + emit error(m_radioError); + } else if (aError == KFmRadioErrHardwareFaulty || KFmRadioErrOfflineMode) { + m_radioError = QRadioTuner::OpenError; + m_errorString = QString("Hardware failure or Radio In Offline Mode"); + emit error(m_radioError); + } + DP0("S60RadioTunerControl::MrftoSetFrequencyComplete ---"); +} + +void S60RadioTunerControl::MrftoStationSeekComplete(TInt aError, TInt aFrequency) +{ + DP0("S60RadioTunerControl::MrftoStationSeekComplete +++"); + DP3("S60RadioTunerControl::MrftoStationSeekComplete, aError:", aError, " Frequency:", aFrequency); + m_scanning = false; + if (aError == KErrNone) { + m_radioError = QRadioTuner::NoError; + m_currentFreq = aFrequency; + emit searchingChanged(m_scanning); + } else { + m_radioError = QRadioTuner::OpenError; + emit searchingChanged(m_scanning); + m_errorString = QString("Scanning Error"); + emit error(m_radioError); + } + DP0("S60RadioTunerControl::MrftoStationSeekComplete ---"); +} + +void S60RadioTunerControl::MrftoFmTransmitterStatusChange(TBool aActive) +{ + DP0("S60RadioTunerControl::MrftoFmTransmitterStatusChange +++"); + + DP0("S60RadioTunerControl::MrftoFmTransmitterStatusChange ---"); + + //no actions +} + +void S60RadioTunerControl::MrftoAntennaStatusChange(TBool aAttached) +{ + DP0("S60RadioTunerControl::MrftoAntennaStatusChange +++"); + DP1("S60RadioTunerControl::MrftoAntennaStatusChange, aAttached:", aAttached); + if (aAttached && m_tunerControl) { + m_playerUtility->Play(); + } + DP0("S60RadioTunerControl::MrftoAntennaStatusChange ---"); +} + +void S60RadioTunerControl::MrftoOfflineModeStatusChange(TBool /*aOfflineMode*/) +{ + DP0("S60RadioTunerControl::MrftoOfflineModeStatusChange +++"); + + DP0("S60RadioTunerControl::MrftoOfflineModeStatusChange ---"); + + +} + +void S60RadioTunerControl::MrftoFrequencyRangeChange(TFmRadioFrequencyRange aBand /*, TInt aMinFreq, TInt aMaxFreq*/) +{ + DP0("S60RadioTunerControl::MrftoFrequencyRangeChange +++"); + if (aBand == EFmRangeEuroAmerica) { + setBand(QRadioTuner::FM); + } + DP0("S60RadioTunerControl::MrftoFrequencyRangeChange ---"); +} + +void S60RadioTunerControl::MrftoFrequencyChange(TInt aNewFrequency) +{ + DP0("S60RadioTunerControl::MrftoFrequencyChange +++"); + DP1("S60RadioTunerControl::MrftoFrequencyChange, aNewFrequency:", aNewFrequency); + m_currentFreq = aNewFrequency; + emit frequencyChanged(m_currentFreq); + + int signal = signalStrength(); + if (m_signal != signal) { + emit signalStrengthChanged(signal); + m_signal = signal; + } + DP0("S60RadioTunerControl::MrftoFrequencyChange ---"); +} + +void S60RadioTunerControl::MrftoForcedMonoChange(TBool aForcedMono) +{ + DP0("S60RadioTunerControl::MrftoForcedMonoChange +++"); + DP1("S60RadioTunerControl::MrftoForcedMonoChange, aForcedMono:", aForcedMono); + if (aForcedMono) { + m_stereoMode = QRadioTuner::ForceMono; + } else { + m_stereoMode = QRadioTuner::ForceStereo; + } + emit stereoStatusChanged(!aForcedMono); + DP0("S60RadioTunerControl::MrftoForcedMonoChange ---"); +} + +void S60RadioTunerControl::MrftoSquelchChange(TBool aSquelch) +{ + DP0("S60RadioTunerControl::MrftoSquelchChange"); + + DP1("S60RadioTunerControl::MrftoSquelchChange, aSquelch:", aSquelch); + + // no actions +} diff --git a/src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.h b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.h new file mode 100644 index 000000000..481d64c67 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/s60radiotunercontrol_since32.h @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60RADIOTUNERCONTROL_H +#define S60RADIOTUNERCONTROL_H + +#include +#include +#include +#include + +#include +#include +#include + +class S60RadioTunerService; +class CFMRadioEngineCallObserver; + +QT_USE_NAMESPACE + +class S60RadioTunerControl + : public QRadioTunerControl + , public MRadioPlayerObserver + , public MRadioFmTunerObserver +{ + Q_OBJECT +public: + S60RadioTunerControl(QObject *parent = 0); + ~S60RadioTunerControl(); + + QRadioTuner::State state() const; + + QRadioTuner::Band band() const; + void setBand(QRadioTuner::Band b); + bool isBandSupported(QRadioTuner::Band b) const; + + int frequency() const; + int frequencyStep(QRadioTuner::Band b) const; + QPair frequencyRange(QRadioTuner::Band b) const; + void setFrequency(int frequency); + + bool isStereo() const; + QRadioTuner::StereoMode stereoMode() const; + void setStereoMode(QRadioTuner::StereoMode mode); + + int signalStrength() const; + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + bool isSearching() const; + void searchForward(); + void searchBackward(); + void cancelSearch(); + + bool isValid() const; + + bool isAvailable() const; + QtMultimediaKit::AvailabilityError availabilityError() const; + + void start(); + void stop(); + + QRadioTuner::Error error() const; + QString errorString() const; + + /** + * From MRadioPlayerObserver. + * Called when Radio state changed. + * + * @since S60 3.2 + * @param aState Radio player state + * @param aError A standard system error code, only used when aState is ERadioPlayerIdle + */ + void MrpoStateChange(TPlayerState aState, TInt aError); + + /** + * From MRadioPlayerObserver. + * Called when volume changes. This may be caused by other applications. + * + * @since S60 3.2 + * @param aVolume Current volume. + */ + void MrpoVolumeChange(TInt aVolume); + + /** + * From MRadioPlayerObserver. + * Called when mute setting changes. This may be caused by other applications. + * + * @since S60 3.2 + * @param aMute ETrue indicates audio is muted. + */ + void MrpoMuteChange(TBool aMute); + + /** + * From MRadioPlayerObserver. + * Called when mute setting changes. This may be caused by other applications. + * + * Called when balance setting changes. This may be caused by other applications. + * + * @since S60 3.2 + * Left speaker volume percentage. This can be any value from zero to 100. + * Zero value means left speaker is muted. + * @param aRightPercentage + * Right speaker volume percentage. This can be any value from zero to 100. + * Zero value means right speaker is muted. + */ + void MrpoBalanceChange(TInt aLeftPercentage, TInt aRightPercentage); + + + /** + * From MRadioFmTunerObserver. + * Called when Request for tuner control completes. + * + * @since S60 3.2 + * @param aError A standard system error code or FM tuner error (TFmRadioTunerError). + */ + void MrftoRequestTunerControlComplete(TInt aError); + + /** + * From MRadioFmTunerObserver. + * Set frequency range complete event. This event is asynchronous and is received after + * a call to CRadioFmTunerUtility::SetFrequencyRange. + * + * @since S60 3.2 + * @param aError A standard system error code or FM tuner error (TFmRadioTunerError). + */ + void MrftoSetFrequencyRangeComplete(TInt aError); + + /** + * From MRadioFmTunerObserver. + * Set frequency complete event. This event is asynchronous and is received after a call to + * CRadioFmTunerUtility::SetFrequency. + * + * @since S60 3.2 + * @param aError A standard system error code or FM tuner error (TFmRadioTunerError). + */ + void MrftoSetFrequencyComplete(TInt aError); + + /** + * From MRadioFmTunerObserver. + * Station seek complete event. This event is asynchronous and is received after a call to + * CRadioFmTunerUtility::StationSeek. + * + * @since S60 3.2 + * @param aError A standard system error code or FM tuner error (TFmRadioTunerError). + * @param aFrequency The frequency(Hz) of the radio station that was found. + */ + void MrftoStationSeekComplete(TInt aError, TInt aFrequency); + + /** + * From MRadioFmTunerObserver. + * Called when FM Transmitter status changes (if one is present in the device). Tuner receiver + * is forced to be turned off due to hardware conflicts when FM transmitter is activated. + * + * @since S60 3.2 + * @param aActive ETrue if FM transmitter is active; EFalse otherwise. + */ + void MrftoFmTransmitterStatusChange(TBool aActive); + + /** + * From MRadioFmTunerObserver. + * Called when antenna status changes. + * + * @since S60 3.2 + * @param aAttached ETrue if antenna is attached; EFalse otherwise. + */ + void MrftoAntennaStatusChange(TBool aAttached); + + /** + * From MRadioFmTunerObserver. + * Called when offline mode status changes. + * @since S60 3.2 + * + ** @param aAttached ETrue if offline mode is enabled; EFalse otherwise. + */ + void MrftoOfflineModeStatusChange(TBool aOfflineMode); + + /** + * From MRadioFmTunerObserver. + * Called when the frequency range changes. This may be caused by other applications. + * + * @since S60 3.2 + * @param aNewRange New frequency range. + */ + void MrftoFrequencyRangeChange(TFmRadioFrequencyRange aBand /*, TInt aMinFreq, TInt aMaxFreq*/); + + /** + * From MRadioFmTunerObserver. + * Called when the tuned frequency changes. This may be caused by other + * applications or RDS if AF/TA is enabled. + * + * @since S60 3.2 + * @param aNewFrequency The new tuned frequency(Hz). + */ + void MrftoFrequencyChange(TInt aNewFrequency); + + /** + * From MRadioFmTunerObserver. + * Called when the forced mono status change. This may be caused by other applications. + * + * @since S60 3.2 + * @param aForcedMono ETrue if forced mono mode is enabled; EFalse otherwise. + */ + void MrftoForcedMonoChange(TBool aForcedMono); + + /** + * From MRadioFmTunerObserver. + * Called when the squelch (muting the frequencies without broadcast) status change. + * This may be caused by other applications. + * + * @since S60 3.2 + * @param aSquelch ETrue if squelch is enabled; EFalse otherwise. + */ + void MrftoSquelchChange(TBool aSquelch); + +private: + bool initRadio(); + + mutable int m_error; + + CRadioUtility* m_radioUtility; + CRadioFmTunerUtility* m_fmTunerUtility; + CRadioPlayerUtility* m_playerUtility; + TInt m_maxVolume; + TReal m_volMultiplier; + + bool m_tunerControl; + bool m_audioInitializationComplete; + bool m_muted; + bool m_isStereo; + bool m_available; + int m_vol; + bool m_volChangeRequired; + mutable int m_signal; + int m_previousSignal; + bool m_scanning; + QRadioTuner::Band m_currentBand; + qint64 m_currentFreq; + + QRadioTuner::Error m_radioError; + QRadioTuner::StereoMode m_stereoMode; + QString m_errorString; + QRadioTuner::State m_apiTunerState; + QTimer *m_signalStrengthTimer; + +Q_SIGNALS: + void error(QRadioTuner::Error) const; + +protected slots: + void changeSignalStrength(); +}; + +#endif + diff --git a/src/plugins/symbian/mmf/radio/s60radiotunerservice.cpp b/src/plugins/symbian/mmf/radio/s60radiotunerservice.cpp new file mode 100644 index 000000000..99b0bf0d2 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/s60radiotunerservice.cpp @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "DebugMacros.h" + +#include "s60radiotunerservice.h" + + +S60RadioTunerService::S60RadioTunerService(QObject *parent) + : QMediaService(parent) +{ + DP0("S60RadioTunerService::S60RadioTunerService +++"); + + m_playerControl = new S60RadioTunerControl(this); + + DP0("S60RadioTunerService::S60RadioTunerService ---"); +} + +S60RadioTunerService::~S60RadioTunerService() +{ + DP0("S60RadioTunerService::~S60RadioTunerService +++"); + + delete m_playerControl; + + DP0("S60RadioTunerService::~S60RadioTunerService ---"); +} + +QMediaControl *S60RadioTunerService::requestControl(const char* name) +{ + DP0("S60RadioTunerService::requestControl"); + + if (qstrcmp(name, QRadioTunerControl_iid) == 0) + return m_playerControl; + + return 0; +} + +void S60RadioTunerService::releaseControl(QMediaControl *control) +{ + DP0("S60RadioTunerService::releaseControl +++"); + + Q_UNUSED(control); + + DP0("S60RadioTunerService::releaseControl ---"); +} diff --git a/src/plugins/symbian/mmf/radio/s60radiotunerservice.h b/src/plugins/symbian/mmf/radio/s60radiotunerservice.h new file mode 100644 index 000000000..92e3eb7f8 --- /dev/null +++ b/src/plugins/symbian/mmf/radio/s60radiotunerservice.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60RADIOTUNERSERVICE_H +#define S60RADIOTUNERSERVICE_H + +#include + +#include + +#ifdef TUNERLIBUSED +#include "s60radiotunercontrol_31.h" +#else +#include "s60radiotunercontrol_since32.h" +#endif + +QT_USE_NAMESPACE + +class S60RadioTunerService : public QMediaService +{ + Q_OBJECT +public: + S60RadioTunerService(QObject *parent = 0); + ~S60RadioTunerService(); + + QMediaControl *requestControl(const char* name); + void releaseControl(QMediaControl *control); + +private: + S60RadioTunerControl *m_playerControl; +}; + +#endif diff --git a/src/plugins/symbian/mmf/s60formatsupported.cpp b/src/plugins/symbian/mmf/s60formatsupported.cpp new file mode 100644 index 000000000..e892008ab --- /dev/null +++ b/src/plugins/symbian/mmf/s60formatsupported.cpp @@ -0,0 +1,121 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include "s60formatsupported.h" + + + +S60FormatSupported::S60FormatSupported() +{} + +S60FormatSupported::~S60FormatSupported() +{ + if (m_controllerparam) { + delete m_controllerparam; + m_controllerparam = NULL; + } +} + +QStringList S60FormatSupported::supportedPlayMimeTypesL() +{ + + RArray mediaIds; //search for both audio and video + + RMMFControllerImplInfoArray iControllers; + + m_controllerparam = CMMFControllerPluginSelectionParameters::NewL(); + + m_playformatparam = CMMFFormatSelectionParameters::NewL(); + + mediaIds.Append(KUidMediaTypeAudio); + + mediaIds.Append(KUidMediaTypeVideo); + + m_controllerparam->SetMediaIdsL(mediaIds, CMMFPluginSelectionParameters::EAllowOtherMediaIds); + + m_controllerparam->SetRequiredPlayFormatSupportL(*m_playformatparam); + + m_controllerparam->ListImplementationsL(iControllers); + + CDesC8ArrayFlat* controllerArray = new (ELeave) CDesC8ArrayFlat(1); + + for (TInt i = 0; i < iControllers.Count(); i++) { + for (TInt j = 0; j < (iControllers[i]->PlayFormats()).Count(); j++) { + const CDesC8Array& iarr = (iControllers[i]->PlayFormats()[j]->SupportedMimeTypes()); + + TInt count = iarr.Count(); + + for (TInt k = 0; k < count; k++) { + TPtrC8 ptr = iarr.MdcaPoint(k); + + HBufC8* n = HBufC8::NewL(ptr.Length()); + + TPtr8 ptr1 = n->Des(); + + ptr1.Copy((TUint8*) ptr.Ptr(), ptr.Length()); + + controllerArray->AppendL(ptr1); + } + } + } + +// converting CDesC8Array to QStringList + for (TInt x = 0; x < controllerArray->Count(); x++) { + m_supportedplaymime.append(QString::fromUtf8( + (const char*) (controllerArray->MdcaPoint(x).Ptr()), + controllerArray->MdcaPoint(x).Length())); + } + + // populating the list with only audio and controller mime types + QStringList tempaudio = m_supportedplaymime.filter(QString("audio")); + QStringList tempvideo = m_supportedplaymime.filter(QString("video")); + + m_supportedplaymime.clear(); + + m_supportedplaymime = tempaudio + tempvideo; + + mediaIds.Close(); + delete controllerArray; + iControllers.ResetAndDestroy(); + + return m_supportedplaymime; +} diff --git a/src/plugins/symbian/mmf/s60formatsupported.h b/src/plugins/symbian/mmf/s60formatsupported.h new file mode 100644 index 000000000..3b5a3ffe6 --- /dev/null +++ b/src/plugins/symbian/mmf/s60formatsupported.h @@ -0,0 +1,65 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60FORMATSUPPORTED_H_ +#define S60FORMATSUPPORTED_H_ + +#include +#include +#include +#include +#include + +class S60FormatSupported +{ +public: + S60FormatSupported(); + ~S60FormatSupported(); + + QStringList supportedPlayMimeTypesL(); + +private: + + CMMFFormatSelectionParameters* m_playformatparam; + CMMFControllerPluginSelectionParameters* m_controllerparam; + QStringList m_supportedplaymime; +}; +#endif /* S60FORMATSUPPORTED_H_ */ diff --git a/src/plugins/symbian/mmf/s60mediaserviceplugin.cpp b/src/plugins/symbian/mmf/s60mediaserviceplugin.cpp new file mode 100644 index 000000000..cfb77255f --- /dev/null +++ b/src/plugins/symbian/mmf/s60mediaserviceplugin.cpp @@ -0,0 +1,115 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include + +#include "s60mediaserviceplugin.h" +#if defined(TUNERLIBUSED) || defined(RADIOUTILITYLIBUSED) +#include "s60radiotunerservice.h" +#endif +#ifdef HAS_MEDIA_PLAYER +#include "s60mediaplayerservice.h" +#endif +#ifdef AUDIOSOURCEUSED +#include "s60audiocaptureservice.h" +#endif /* AUDIOSOURCEUSED */ + +QStringList S60MediaServicePlugin::keys() const +{ + QStringList list; +#if defined(TUNERLIBUSED) || defined(RADIOUTILITYLIBUSED) + list << QLatin1String(Q_MEDIASERVICE_RADIO); +#endif + +#ifdef HAS_MEDIA_PLAYER + list << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER); +#endif +#ifdef AUDIOSOURCEUSED + list << QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE); +#endif /* AUDIOSOURCEUSED */ + return list; +} + +QMediaService* S60MediaServicePlugin::create(QString const& key) +{ +#ifdef HAS_MEDIA_PLAYER + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER)) + return new S60MediaPlayerService; +#endif +#ifdef AUDIOSOURCEUSED + if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) + return new S60AudioCaptureService; +#endif /* AUDIOSOURCEUSED */ +#if defined(TUNERLIBUSED) || defined(RADIOUTILITYLIBUSED) + if (key == QLatin1String(Q_MEDIASERVICE_RADIO)) + return new S60RadioTunerService; +#endif + + return 0; +} + +void S60MediaServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QtMultimediaKit::SupportEstimate S60MediaServicePlugin::hasSupport(const QString &mimeType, const QStringList& codecs) const +{ + Q_UNUSED(mimeType); + Q_UNUSED(codecs); + return QtMultimediaKit::PreferredService; +} + +QStringList S60MediaServicePlugin::supportedMimeTypes() const +{ + if (m_supportedmimetypes.isEmpty()) { + TInt err; + S60FormatSupported* formats = new (ELeave) S60FormatSupported(); + if (formats) { + TRAP(err, m_supportedmimetypes = formats->supportedPlayMimeTypesL()); + delete formats; + } + } + return m_supportedmimetypes; +} + +Q_EXPORT_PLUGIN2(qtmultimediakit_mmfengine, S60MediaServicePlugin); diff --git a/src/plugins/symbian/mmf/s60mediaserviceplugin.h b/src/plugins/symbian/mmf/s60mediaserviceplugin.h new file mode 100644 index 000000000..b2140dd6f --- /dev/null +++ b/src/plugins/symbian/mmf/s60mediaserviceplugin.h @@ -0,0 +1,69 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef S60SERVICEPLUGIN_H +#define S60SERVICEPLUGIN_H + +#include +#include +#include +#include "s60formatsupported.h" + +QT_USE_NAMESPACE + +class S60MediaServicePlugin : public QMediaServiceProviderPlugin,public QMediaServiceSupportedFormatsInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedFormatsInterface) +public: + + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QtMultimediaKit::SupportEstimate hasSupport(const QString &mimeType, const QStringList& codecs) const; + QStringList supportedMimeTypes() const; +private: + mutable QStringList m_supportedmimetypes; +}; + +#endif // S60SERVICEPLUGIN_H diff --git a/src/plugins/symbian/openmaxal/mediaplayer/mediaplayer.pri b/src/plugins/symbian/openmaxal/mediaplayer/mediaplayer.pri new file mode 100644 index 000000000..7c6843e19 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/mediaplayer.pri @@ -0,0 +1,36 @@ +INCLUDEPATH += $$PWD + +LIBS += \ + -lws32 \ + -lcone + +#DEFINES += USE_VIDEOPLAYERUTILITY + +HEADERS += \ + $$PWD/qxametadatacontrol.h \ + $$PWD/qxamediastreamscontrol.h \ + $$PWD/qxamediaplayercontrol.h \ + $$PWD/qxaplaymediaservice.h \ + $$PWD/qxaplaysession.h \ + $$PWD/xaplaysessioncommon.h \ + $$PWD/qxavideowidgetcontrol.h \ + $$PWD/qxavideowindowcontrol.h \ + $$PWD/qxawidget.h \ + $$PWD/xaplaysessionimpl.h + +SOURCES += \ + $$PWD/qxamediaplayercontrol.cpp \ + $$PWD/qxametadatacontrol.cpp \ + $$PWD/qxamediastreamscontrol.cpp \ + $$PWD/qxaplaymediaservice.cpp \ + $$PWD/qxaplaysession.cpp \ + $$PWD/qxavideowidgetcontrol.cpp \ + $$PWD/qxavideowindowcontrol.cpp \ + $$PWD/qxawidget.cpp \ + $$PWD/xaplaysessionimpl.cpp + +# check for USE_VIDEOPLAYERUTILITY +contains(DEFINES, USE_VIDEOPLAYERUTILITY) { + message("Using VideoPlayerUtility instead of OpenMAX AL.") + LIBS += -lmediaclientvideo +} diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.cpp new file mode 100644 index 000000000..ee192105b --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.cpp @@ -0,0 +1,288 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include "qxamediaplayercontrol.h" +#include "qxaplaysession.h" +#include "qxacommon.h" + +QXAMediaPlayerControl::QXAMediaPlayerControl(QXAPlaySession *session, QObject *parent) + :QMediaPlayerControl(parent), mSession(session) +{ + QT_TRACE_FUNCTION_ENTRY; + connect(mSession, SIGNAL(mediaChanged(const QMediaContent &)), + this, SIGNAL(mediaChanged(const QMediaContent& ))); + connect(mSession, SIGNAL(durationChanged(qint64)), + this, SIGNAL(durationChanged(qint64))); + connect(mSession, SIGNAL(positionChanged(qint64)), + this, SIGNAL(positionChanged(qint64))); + connect(mSession, SIGNAL(stateChanged(QMediaPlayer::State)), + this, SIGNAL(stateChanged(QMediaPlayer::State))); + connect(mSession, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus)), + this, SIGNAL(mediaStatusChanged(QMediaPlayer::MediaStatus))); + connect(mSession, SIGNAL(volumeChanged(int)), + this, SIGNAL(volumeChanged(int))); + connect(mSession, SIGNAL(mutedChanged(bool)), + this, SIGNAL(mutedChanged(bool))); + connect(mSession, SIGNAL(audioAvailableChanged(bool)), + this, SIGNAL(audioAvailableChanged(bool))); + connect(mSession, SIGNAL(videoAvailableChanged(bool)), + this, SIGNAL(videoAvailableChanged(bool))); + connect(mSession,SIGNAL(bufferStatusChanged(int)), + this, SIGNAL(bufferStatusChanged(int))); + connect(mSession, SIGNAL(seekableChanged(bool)), + this, SIGNAL(seekableChanged(bool))); + connect(mSession, SIGNAL(availablePlaybackRangesChanged(const QMediaTimeRange&)), + this, SIGNAL(availablePlaybackRangesChanged(const QMediaTimeRange&))); + connect(mSession, SIGNAL(playbackRateChanged(qreal)), + this, SIGNAL(playbackRateChanged(qreal))); + connect(mSession, SIGNAL(error(int, const QString &)), + this, SIGNAL(error(int, const QString &))); + QT_TRACE_FUNCTION_EXIT; +} + +QXAMediaPlayerControl::~QXAMediaPlayerControl() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QMediaPlayer::State QXAMediaPlayerControl::state() const +{ + QT_TRACE_FUNCTION_ENTRY; + QMediaPlayer::State retVal = QMediaPlayer::StoppedState; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->state(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +QMediaPlayer::MediaStatus QXAMediaPlayerControl::mediaStatus() const +{ + QT_TRACE_FUNCTION_ENTRY; + QMediaPlayer::MediaStatus retVal = QMediaPlayer::NoMedia; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->mediaStatus(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +qint64 QXAMediaPlayerControl::duration() const +{ + QT_TRACE_FUNCTION_ENTRY; + qint64 retVal = 0; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->duration(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +qint64 QXAMediaPlayerControl::position() const +{ + QT_TRACE_FUNCTION_ENTRY; + qint64 retVal = 0; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->position(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +void QXAMediaPlayerControl::setPosition(qint64 pos) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setPosition(pos); + QT_TRACE_FUNCTION_EXIT; +} + +int QXAMediaPlayerControl::volume() const +{ + QT_TRACE_FUNCTION_ENTRY; + int retVal = 0; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->volume(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +void QXAMediaPlayerControl::setVolume(int volume) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setVolume(volume); + QT_TRACE_FUNCTION_EXIT; +} + +bool QXAMediaPlayerControl::isMuted() const +{ + QT_TRACE_FUNCTION_ENTRY; + bool retVal = false; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->isMuted(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +void QXAMediaPlayerControl::setMuted(bool muted) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setMuted(muted); + QT_TRACE_FUNCTION_EXIT; +} + +int QXAMediaPlayerControl::bufferStatus() const +{ + QT_TRACE_FUNCTION_ENTRY; + int retVal = 0; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->bufferStatus(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +bool QXAMediaPlayerControl::isAudioAvailable() const +{ + QT_TRACE_FUNCTION_ENTRY; + bool retVal = false; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->isAudioAvailable(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +bool QXAMediaPlayerControl::isVideoAvailable() const +{ + QT_TRACE_FUNCTION_ENTRY; + bool retVal = false; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->isVideoAvailable(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +bool QXAMediaPlayerControl::isSeekable() const +{ + QT_TRACE_FUNCTION_ENTRY; + bool retVal = false; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->isSeekable(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +QMediaTimeRange QXAMediaPlayerControl::availablePlaybackRanges() const +{ + QT_TRACE_FUNCTION_ENTRY; + QMediaTimeRange retVal; + RET_s_IF_p_IS_NULL(mSession, retVal); + if (mSession->isSeekable()) + retVal.addInterval(0, mSession->duration()); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +float QXAMediaPlayerControl::playbackRate() const +{ + QT_TRACE_FUNCTION_ENTRY; + float retVal = 0; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->playbackRate(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +void QXAMediaPlayerControl::setPlaybackRate(float rate) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setPlaybackRate(rate); + QT_TRACE_FUNCTION_EXIT; +} + +QMediaContent QXAMediaPlayerControl::media() const +{ + QT_TRACE_FUNCTION_ENTRY; + QMediaContent retVal; + RET_s_IF_p_IS_NULL(mSession, retVal); + retVal = mSession->media(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +const QIODevice *QXAMediaPlayerControl::mediaStream() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mStream; +} + +void QXAMediaPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setMedia(content); + mStream = stream; + QT_TRACE_FUNCTION_EXIT; +} + +void QXAMediaPlayerControl::play() +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->play(); + QT_TRACE_FUNCTION_EXIT; +} + +void QXAMediaPlayerControl::pause() +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->pause(); + QT_TRACE_FUNCTION_EXIT; +} + +void QXAMediaPlayerControl::stop() +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->stop(); + QT_TRACE_FUNCTION_EXIT; +} diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.h b/src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.h new file mode 100644 index 000000000..b9d7802bd --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxamediaplayercontrol.h @@ -0,0 +1,99 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAMEDIAPLAYERCONTROL_H +#define QXAMEDIAPLAYERCONTROL_H + +#include "qmediaplayercontrol.h" +#include "qmediaplayer.h" + +QT_USE_NAMESPACE + +class QXAPlaySession; + +class QXAMediaPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT +public: + QXAMediaPlayerControl(QXAPlaySession *session, QObject *parent = 0); + ~QXAMediaPlayerControl(); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + qint64 duration() const; + + qint64 position() const; + void setPosition(qint64 position); + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + int bufferStatus() const; + + bool isAudioAvailable() const; + bool isVideoAvailable() const; + + bool isSeekable() const; + + QMediaTimeRange availablePlaybackRanges() const; + + float playbackRate() const; + void setPlaybackRate(float rate); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent&, QIODevice *); + + void play(); + void pause(); + void stop(); + + +private: + QXAPlaySession *mSession; + QIODevice *mStream; +}; + +#endif /* QXAMEDIAPLAYERCONTROL_H */ diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.cpp new file mode 100644 index 000000000..528fb1837 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include + +#include "qxamediastreamscontrol.h" +#include "qxaplaysession.h" +#include "qxacommon.h" + +QXAMediaStreamsControl::QXAMediaStreamsControl(QXAPlaySession *session, QObject *parent) + :QMediaStreamsControl(parent), mSession(session) +{ + QT_TRACE_FUNCTION_ENTRY; + connect(mSession, SIGNAL(activeStreamsChanged()), + this, SIGNAL(activeStreamsChanged())); + connect(mSession, SIGNAL(streamsChanged()), + this, SIGNAL(streamsChanged())); + QT_TRACE_FUNCTION_EXIT; +} + +QXAMediaStreamsControl::~QXAMediaStreamsControl() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +bool QXAMediaStreamsControl::isActive (int stream) +{ + RET_s_IF_p_IS_NULL(mSession, false); + return mSession->isStreamActive(stream); +} + +QVariant QXAMediaStreamsControl::metaData (int stream, QtMultimediaKit::MetaData key) +{ + QVariant var; + RET_s_IF_p_IS_NULL(mSession, var); + QT_TRACE_FUNCTION_ENTRY; + var = mSession->metaData(stream, key); + QT_TRACE_FUNCTION_EXIT; + return var; +} + +void QXAMediaStreamsControl::setActive (int stream, bool state) +{ + Q_UNUSED(stream); + Q_UNUSED(state); +} + +int QXAMediaStreamsControl::streamCount() +{ + RET_s_IF_p_IS_NULL(mSession, 0); + return mSession->streamCount(); +} + +QMediaStreamsControl::StreamType QXAMediaStreamsControl::streamType (int stream) +{ + RET_s_IF_p_IS_NULL(mSession, QMediaStreamsControl::UnknownStream); + return mSession->streamType(stream); +} + diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.h b/src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.h new file mode 100644 index 000000000..d77b89ca5 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxamediastreamscontrol.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef QXAMEDIASTREAMSCONTROL_H +#define QXAMEDIASTREAMSCONTROL_H + + + +#include +#include +#include +#include + +#include + +#include + +QT_USE_NAMESPACE + +class QXAPlaySession; + +class QXAMediaStreamsControl : public QMediaStreamsControl +{ + Q_OBJECT +public: + QXAMediaStreamsControl(QXAPlaySession *session, QObject *parent = 0); + ~QXAMediaStreamsControl(); + + bool isActive (int stream); + QVariant metaData (int stream, QtMultimediaKit::MetaData key); + void setActive (int stream, bool state); + int streamCount(); + QMediaStreamsControl::StreamType streamType (int stream); +private: + QXAPlaySession *mSession; +}; + +#endif //QXAMEDIASTREAMSCONTROL_H diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.cpp new file mode 100644 index 000000000..9c6b906cf --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.cpp @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qxametadatacontrol.h" +#include "qxaplaysession.h" +#include "qxacommon.h" + +QXAMetaDataControl::QXAMetaDataControl(QXAPlaySession *session, QObject *parent) + :QMetaDataReaderControl(parent), mSession(session) +{ + QT_TRACE_FUNCTION_ENTRY; + connect(mSession, SIGNAL(metaDataAvailableChanged(bool)), + this, SIGNAL(metaDataAvailableChanged(bool))); + connect(mSession, SIGNAL(metaDataChanged()), + this, SIGNAL(metaDataChanged())); + QT_TRACE_FUNCTION_EXIT; +} + +QXAMetaDataControl::~QXAMetaDataControl() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QStringList QXAMetaDataControl::availableExtendedMetaData ()const +{ + QStringList list; + RET_s_IF_p_IS_NULL(mSession, list); + QT_TRACE_FUNCTION_ENTRY; + list = mSession->availableExtendedMetaData(); + QT_TRACE_FUNCTION_EXIT; + return list; +} + +QList QXAMetaDataControl::availableMetaData () const +{ + QList list; + RET_s_IF_p_IS_NULL(mSession, list); + QT_TRACE_FUNCTION_ENTRY; + list = mSession->availableMetaData(); + QT_TRACE_FUNCTION_EXIT; + return list; +} + +QVariant QXAMetaDataControl::extendedMetaData(const QString & key ) const +{ + QVariant var; + RET_s_IF_p_IS_NULL(mSession, var); + QT_TRACE_FUNCTION_ENTRY; + var = mSession->extendedMetaData(key); + QT_TRACE_FUNCTION_EXIT; + return var; +} + +bool QXAMetaDataControl::isMetaDataAvailable() const +{ + RET_s_IF_p_IS_NULL(mSession, false); + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mSession->isMetaDataAvailable(); +} + +bool QXAMetaDataControl::isWritable() const +{ + RET_s_IF_p_IS_NULL(mSession, false); + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mSession->isWritable(); +} + +QVariant QXAMetaDataControl::metaData( QtMultimediaKit::MetaData key ) const +{ + QVariant var; + RET_s_IF_p_IS_NULL(mSession, var); + QT_TRACE_FUNCTION_ENTRY; + var = mSession->metaData(key); + QT_TRACE_FUNCTION_ENTRY_EXIT; + return var; +} + +void QXAMetaDataControl::setExtendedMetaData( const QString & key, const QVariant & value ) +{ + RET_IF_p_IS_NULL(mSession); + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mSession->setExtendedMetaData(key,value); +} + +void QXAMetaDataControl::setMetaData( QtMultimediaKit::MetaData key, const QVariant & value ) +{ + RET_IF_p_IS_NULL(mSession); + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mSession->setMetaData(key,value); +} + diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.h b/src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.h new file mode 100644 index 000000000..8904a5ff4 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxametadatacontrol.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAMETADATACONTROL_H +#define QXAMETADATACONTROL_H + + + +#include +#include +#include +#include + +#include +#include +QT_USE_NAMESPACE + +class QXAPlaySession; + +class QXAMetaDataControl : public QMetaDataReaderControl +{ + Q_OBJECT +public: + QXAMetaDataControl(QXAPlaySession *session, QObject *parent = 0); + ~QXAMetaDataControl(); + + QStringList availableExtendedMetaData () const; + QList availableMetaData () const; + QVariant extendedMetaData(const QString & key ) const; + bool isMetaDataAvailable() const; + bool isWritable() const; + QVariant metaData( QtMultimediaKit::MetaData key ) const; + void setExtendedMetaData( const QString & key, const QVariant & value ); + void setMetaData( QtMultimediaKit::MetaData key, const QVariant & value ); + +private: + QXAPlaySession *mSession; +}; + +#endif //QXAMETADATACONTROL_H diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.cpp new file mode 100644 index 000000000..f2d3c4b15 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.cpp @@ -0,0 +1,117 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#include +#include "qxaplaymediaservice.h" +#include "qxaplaysession.h" +#include "qxamediaplayercontrol.h" +#include "qxacommon.h" +#include "qxavideowidgetcontrol.h" +#include "qxavideowindowcontrol.h" +#include "qxametadatacontrol.h" +#include "qxamediastreamscontrol.h" + +QXAPlayMediaService::QXAPlayMediaService(QObject *parent) : QMediaService(parent) +{ + mSession = NULL; + mMediaPlayerControl = NULL; + mVideowidgetControl = NULL; + mVideoWindowControl = NULL; + mMetaDataControl = NULL; + mMediaStreamsControl = NULL; + + mSession = new QXAPlaySession(this); + mMediaPlayerControl = new QXAMediaPlayerControl(mSession, this); + mMetaDataControl = new QXAMetaDataControl(mSession, this); + mMediaStreamsControl = new QXAMediaStreamsControl(mSession, this); +} + +QXAPlayMediaService::~QXAPlayMediaService() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QMediaControl *QXAPlayMediaService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) { + return mMediaPlayerControl; + } + else if (qstrcmp(name, QVideoWidgetControl_iid) == 0) { + if (!mVideowidgetControl) { + mVideowidgetControl = new QXAVideoWidgetControl(mSession, this); + if (mSession && mVideowidgetControl) + mSession->setVideoWidgetControl(mVideowidgetControl); + } + return mVideowidgetControl; + } + else if (qstrcmp(name, QVideoWindowControl_iid) == 0) { + if (!mVideoWindowControl) { + mVideoWindowControl = new QXAVideoWindowControl(mSession, this); + if (mSession && mVideoWindowControl) + mSession->setVideoWindowControl(mVideoWindowControl); + } + return mVideoWindowControl; + } + else if (qstrcmp(name,QMetaDataReaderControl_iid) == 0) { + return mMetaDataControl; + } + else if (qstrcmp(name,QMediaStreamsControl_iid) == 0) { + return mMediaStreamsControl; + } + + return 0; +} + +void QXAPlayMediaService::releaseControl(QMediaControl *control) +{ + if (control == mVideowidgetControl) { + if (mSession) + mSession->unsetVideoWidgetControl(qobject_cast(control)); + mVideowidgetControl = NULL; + } + else if (control == mVideoWindowControl) { + if (mSession) + mSession->unsetVideoWindowControl(qobject_cast(control)); + mVideoWindowControl = NULL; + } +} + diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.h b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.h new file mode 100644 index 000000000..4a344b087 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaymediaservice.h @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAPLAYMEDIASERVICE_H +#define QXAPLAYMEDIASERVICE_H + +#include +#include + +QT_USE_NAMESPACE + +class QXAPlaySession; +class QXAMediaPlayerControl; +class QXAVideoWidgetControl; +class QXAVideoWindowControl; +class QXAMetaDataControl; +class QXAMediaStreamsControl; + +class QXAPlayMediaService : public QMediaService +{ + Q_OBJECT +public: + QXAPlayMediaService(QObject *parent = 0); + ~QXAPlayMediaService(); + QMediaControl *requestControl(const char *name); + void releaseControl( QMediaControl *control); +private: + QXAPlaySession *mSession; + QXAMediaPlayerControl *mMediaPlayerControl; + QXAVideoWidgetControl *mVideowidgetControl; + QXAVideoWindowControl *mVideoWindowControl; + QXAMetaDataControl *mMetaDataControl; + QXAMediaStreamsControl *mMediaStreamsControl; +}; + +#endif /* QXAPLAYMEDIASERVICE_H */ diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.cpp new file mode 100644 index 000000000..2254a9363 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.cpp @@ -0,0 +1,610 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qxaplaysession.h" +#include "xaplaysessionimpl.h" +#include "qxacommon.h" +#include + +QXAPlaySession::QXAPlaySession(QObject *parent):QObject(parent), +m_state(QMediaPlayer::StoppedState), +m_mediaStatus(QMediaPlayer::NoMedia), +mSeekable(-1), +mNumStreams(0), +mbAudioAvailable(EFalse), +mbVideoAvailable(EFalse), +mImpl(NULL), +mVideowidgetControl(NULL), +mWidgetCtrlWindow(NULL), +mWidgetCtrlWindowId(NULL), +mVideoWindowControl(NULL), +mWindowCtrlWindow(NULL), +mWindowCtrlWindowId(NULL), +mWsSession(&(CCoeEnv::Static()->WsSession())) +{ + QT_TRACE_FUNCTION_ENTRY; + mImpl = new XAPlaySessionImpl(*this); + + if (mImpl && (mImpl->postConstruct() != KErrNone)) { + delete mImpl; + mImpl = NULL; + QT_TRACE1("Error initializing implementation"); + } + + if (!mImpl) + emit error(QMediaPlayer::ResourceError, tr("Service has not been started")); + + QT_TRACE_FUNCTION_EXIT; +} + +QXAPlaySession::~QXAPlaySession() +{ + QT_TRACE_FUNCTION_ENTRY; + delete mImpl; + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::setVideoWidgetControl( QXAVideoWidgetControl * videoWidgetControl ) +{ + QT_TRACE_FUNCTION_ENTRY; + if (mVideowidgetControl) { + disconnect(mVideowidgetControl, SIGNAL(widgetUpdated()), + this, SLOT(videoWidgetControlWidgetUpdated())); + RWindow* window = static_cast(mVideowidgetControl->videoWidgetWId()->DrawableWindow()); + mImpl->removeNativeDisplay(window, mWsSession); + } + mVideowidgetControl = videoWidgetControl; + if (mVideowidgetControl) + connect(mVideowidgetControl, SIGNAL(widgetUpdated()), + this, SLOT(videoWidgetControlWidgetUpdated())); + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::unsetVideoWidgetControl( QXAVideoWidgetControl * videoWidgetControl ) +{ + QT_TRACE_FUNCTION_ENTRY; + if ((mVideowidgetControl == videoWidgetControl) && (mImpl)) { + disconnect(mVideowidgetControl, SIGNAL(widgetUpdated()), + this, SLOT(videoWidgetControlWidgetUpdated())); + RWindow* window = static_cast(mVideowidgetControl->videoWidgetWId()->DrawableWindow()); + mImpl->removeNativeDisplay(window, mWsSession); + } + mVideowidgetControl = NULL; + mWidgetCtrlWindow = NULL; + mWidgetCtrlWindowId = NULL; + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::setVideoWindowControl( QXAVideoWindowControl * videoWindowControl ) +{ + QT_TRACE_FUNCTION_ENTRY; + if (mVideoWindowControl) { + disconnect(mVideoWindowControl, SIGNAL(windowUpdated()), + this, SLOT(videoWindowControlWindowUpdated())); + RWindow* window = static_cast(mVideoWindowControl->winId()->DrawableWindow()); + mImpl->removeNativeDisplay(window, mWsSession); + } + mVideoWindowControl = videoWindowControl; + if (mVideoWindowControl) { + connect(mVideoWindowControl, SIGNAL(windowUpdated()), + this, SLOT(videoWindowControlWindowUpdated())); + videoWindowControlWindowUpdated(); + } + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::unsetVideoWindowControl( QXAVideoWindowControl * videoWindowControl ) +{ + QT_TRACE_FUNCTION_ENTRY; + if ((mVideoWindowControl == videoWindowControl) && (mImpl)) { + disconnect(mVideoWindowControl, SIGNAL(windowUpdated()), + this, SLOT(videoWindowControlWindowUpdated())); + RWindow* window = static_cast(mVideoWindowControl->winId()->DrawableWindow()); + mImpl->removeNativeDisplay(window, mWsSession); + } + mVideoWindowControl = NULL; + mWindowCtrlWindow = NULL; + mWindowCtrlWindowId = NULL; + QT_TRACE_FUNCTION_EXIT; +} + +qint64 QXAPlaySession::duration() +{ + TInt64 dur(0); + if (mImpl) + mImpl->duration(dur); + + return (qint64)dur; +} + +qint64 QXAPlaySession::position() +{ + TInt64 pos(0); + if (mImpl) + mImpl->position(pos); + + return (qint64)pos; +} + +void QXAPlaySession::setPosition(qint64 ms) +{ + if (mImpl) { + qint64 currPos = position(); + mImpl->seek(ms); + + if(currPos != position()) { + emit positionChanged(position()); + + if(position()>=duration()) { + setMediaStatus(QMediaPlayer::EndOfMedia); + stop(); + } + } + } +} + +int QXAPlaySession::volume() +{ + if(mImpl) { + TInt v(0); + + TInt err = mImpl->volume(v); + if(KErrNone == err) + return v; + } + + return 50; +} + +void QXAPlaySession::setVolume(int v) +{ + if(mImpl) { + if(v != volume()) { + TInt err = mImpl->setVolume(v); + if(KErrNone == err) + emit volumeChanged(volume()); + } + } +} + + +bool QXAPlaySession::isMuted() +{ + if(mImpl) { + TBool bCurrMute = EFalse; + TInt err = mImpl->getMute(bCurrMute); + if(err == KErrNone) + return bCurrMute; + } + + return EFalse; +} + +void QXAPlaySession::setMuted(bool muted) +{ + if(muted != isMuted()) + { + if(mImpl) + { + TInt err = mImpl->setMute(muted); + + if(KErrNone == err) + { + emit mutedChanged(muted); + } + } + } +} + +int QXAPlaySession::bufferStatus() +{ + if(mImpl) { + TInt fillLevel = 0; + TInt err = mImpl->bufferStatus(fillLevel); + if(err == KErrNone) + return fillLevel; + } + + return 100;//default +} + +bool QXAPlaySession::isAudioAvailable() +{ + return mbAudioAvailable; +} + +bool QXAPlaySession::isVideoAvailable() +{ + return mbVideoAvailable; +} + +bool QXAPlaySession::isSeekable() +{ + return ((mSeekable==1) || (mSeekable==-1));//default seekable +} + +float QXAPlaySession::playbackRate() +{ + if(mImpl) { + TReal32 currPBRate = 0.0; + TInt ret = mImpl->getPlaybackRate(currPBRate); + if(ret == KErrNone) + return currPBRate; + } + + return 1.0; +} + +void QXAPlaySession::setPlaybackRate(float rate) +{ + if(mImpl) { + TReal32 currPBRate = 0.0; + TInt ret = mImpl->getPlaybackRate(currPBRate); + if( (ret == KErrNone) && + (rate!=currPBRate)) { + ret = mImpl->setPlaybackRate(rate); + if(ret == KErrNone) + emit playbackRateChanged(rate); + } + } +} + +QMediaContent QXAPlaySession::media() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mMediaContent; +} + +void QXAPlaySession::setMedia(const QMediaContent& media) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL_EMIT_PLAYER_RESOURCE_ERROR(mImpl); + + if (media.isNull() || + mMediaContent == media) { + return; + } + + setMediaStatus(QMediaPlayer::NoMedia); + + QString urlStr = media.canonicalUrl().toString(); + TPtrC16 urlPtr(reinterpret_cast(urlStr.utf16())); + + setMediaStatus(QMediaPlayer::LoadingMedia); + if (mImpl->load(urlPtr) == 0) { + setMediaStatus(QMediaPlayer::LoadedMedia); + emit error(QMediaPlayer::NoError, ""); + mMediaContent = media; + setPlayerState(QMediaPlayer::StoppedState); + emit mediaChanged(mMediaContent); + + if(mImpl->isMetaDataAvailable()) { + emit metaDataAvailableChanged(true); + emit metaDataChanged(); + } + else { + emit metaDataAvailableChanged(false); + } + } + else { + setMediaStatus(QMediaPlayer::NoMedia); + emit error(QMediaPlayer::ResourceError, tr("Unable to load media")); + } + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::play() +{ + if (mImpl) { + setMediaStatus(QMediaPlayer::BufferingMedia); + + TInt err = mImpl->play(); + if (err != KErrNone) { + setMediaStatus(QMediaPlayer::NoMedia); + RET_IF_ERROR(err); + } + setPlayerState(QMediaPlayer::PlayingState); + + TInt fillLevel = 0; + err = mImpl->bufferStatus(fillLevel); + RET_IF_ERROR(err); + if (fillLevel == 100) { + setMediaStatus(QMediaPlayer::BufferedMedia); + } + } +} + +void QXAPlaySession::pause() +{ + if (mImpl) { + TInt err = mImpl->pause(); + RET_IF_ERROR(err); + setPlayerState(QMediaPlayer::PausedState); + } +} + +void QXAPlaySession::stop() +{ + if (mImpl) { + TInt err = mImpl->stop(); + RET_IF_ERROR(err); + setPlayerState(QMediaPlayer::StoppedState); + } +} + +void QXAPlaySession::cbDurationChanged(TInt64 new_dur) +{ + emit durationChanged((qint64)new_dur); +} + +void QXAPlaySession::cbPositionChanged(TInt64 new_pos) +{ + emit positionChanged((qint64)new_pos); +} + +void QXAPlaySession::cbSeekableChanged(TBool seekable) +{ + if( (mSeekable==-1) || + (seekable != (TBool)mSeekable)) { + mSeekable = seekable?1:0; + emit seekableChanged((bool)seekable); + } +} + +void QXAPlaySession::cbPlaybackStopped(TInt err) +{ + if (err) { + emit error(QMediaPlayer::ResourceError, tr("Resources Unavailable")); + SIGNAL_EMIT_TRACE1("emit error(QMediaPlayer::ResourceError, tr(\"Resources Unavailable\"))"); + emit positionChanged(position()); + setPlayerState(QMediaPlayer::StoppedState); + setMediaStatus(QMediaPlayer::NoMedia); + } + else { + setMediaStatus(QMediaPlayer::EndOfMedia); + /* Set player state to Stopped */ + stop(); + } +} + +void QXAPlaySession::cbPrefetchStatusChanged() +{ + if(mImpl) { + TInt fillLevel = 0; + TInt err = mImpl->bufferStatus(fillLevel); + if(err == KErrNone) { + emit bufferStatusChanged(fillLevel); + + if(fillLevel == 100) + setMediaStatus(QMediaPlayer::BufferedMedia); + else if(fillLevel ==0) + setMediaStatus(QMediaPlayer::StalledMedia); + } + } +} + +void QXAPlaySession::cbStreamInformation(TBool bFirstTime) +{ + updateStreamInfo(bFirstTime); +} + + + +void QXAPlaySession::videoWidgetControlWidgetUpdated() +{ + QT_TRACE_FUNCTION_ENTRY; + if (mVideowidgetControl) { + WId newId = mVideowidgetControl->videoWidgetWId(); + if ((newId != NULL) && (newId != mWidgetCtrlWindowId)) { + mWidgetCtrlWindow = static_cast(newId->DrawableWindow()); + if (mWidgetCtrlWindowId == NULL) + mImpl->addNativeDisplay(mWidgetCtrlWindow, mWsSession); + else + mImpl->updateNativeDisplay(mWidgetCtrlWindow, mWsSession); + mWidgetCtrlWindowId = newId; + } + } + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::videoWindowControlWindowUpdated() +{ + QT_TRACE_FUNCTION_ENTRY; + if (mVideoWindowControl) { + WId newId = mVideoWindowControl->winId(); + if ((newId != NULL) && (newId != mWindowCtrlWindowId)) { + mWidgetCtrlWindow = static_cast(newId->DrawableWindow()); + if (mWindowCtrlWindowId == NULL) + mImpl->addNativeDisplay(mWidgetCtrlWindow, mWsSession); + else + mImpl->updateNativeDisplay(mWidgetCtrlWindow, mWsSession); + mWindowCtrlWindowId = newId; + } + } + QT_TRACE_FUNCTION_EXIT; +} + +void QXAPlaySession::setMediaStatus(QMediaPlayer::MediaStatus status) +{ + if (m_mediaStatus != status) { + m_mediaStatus = status; + emit mediaStatusChanged(status); + } +} + +void QXAPlaySession::setPlayerState(QMediaPlayer::State state) +{ + if (m_state != state) { + m_state = state; + emit stateChanged(m_state); + } +} + +QStringList QXAPlaySession::availableExtendedMetaData () const +{ + QStringList list; + RET_s_IF_p_IS_NULL(mImpl, list); + list = mImpl->availableExtendedMetaData(); + return list; +} + +QList QXAPlaySession::availableMetaData () const +{ + QList list; + RET_s_IF_p_IS_NULL(mImpl, list); + return mImpl->availableMetaData(); +} + +QVariant QXAPlaySession::extendedMetaData(const QString & key ) const +{ + QVariant var; + RET_s_IF_p_IS_NULL(mImpl, var); + return mImpl->extendedMetaData(key); +} + +bool QXAPlaySession::isMetaDataAvailable() const +{ + RET_s_IF_p_IS_NULL(mImpl, false); + return mImpl->isMetaDataAvailable(); +} + +bool QXAPlaySession::isWritable() const +{ + RET_s_IF_p_IS_NULL(mImpl, false); + return mImpl->isWritable(); +} + +QVariant QXAPlaySession::metaData( QtMultimediaKit::MetaData key ) const +{ + QVariant var; + RET_s_IF_p_IS_NULL(mImpl, var); + return mImpl->metaData(key); +} + +void QXAPlaySession::setExtendedMetaData( const QString & key, const QVariant & value ) +{ + RET_IF_p_IS_NULL(mImpl); + mImpl->setExtendedMetaData(key, value); +} + +void QXAPlaySession::setMetaData( QtMultimediaKit::MetaData key, const QVariant & value ) +{ + RET_IF_p_IS_NULL(mImpl); + mImpl->setMetaData(key, value); +} + +void QXAPlaySession::updateStreamInfo(TBool emitSignal) +{ + if(mImpl) { + mNumStreams = 0; + TInt ret = mImpl->numMediaStreams(mNumStreams); + if(ret == KErrNone) { + TBool bAudioAvailable = EFalse;//lcoal variable + TBool bVideoAvailable = EFalse;//lcvoal variable + + for(TUint i = 0; i < mNumStreams; i++) { + QMediaStreamsControl::StreamType strType; + mImpl->streamType(i, strType); + if(strType == QMediaStreamsControl::AudioStream) + bAudioAvailable = ETrue; + else if(strType == QMediaStreamsControl::VideoStream) + bVideoAvailable = ETrue; + } + + if(emitSignal || (bAudioAvailable != mbAudioAvailable)) { + emit audioAvailableChanged(bAudioAvailable); + mbAudioAvailable = bAudioAvailable; + } + + if(emitSignal || (bVideoAvailable != mbVideoAvailable)) { + emit videoAvailableChanged(bVideoAvailable); + mbVideoAvailable = bVideoAvailable; + } + + emit streamsChanged(); + } + } +} + +bool QXAPlaySession::isStreamActive ( int stream ) +{ + RET_s_IF_p_IS_NULL(mImpl, false); + TBool isActive = EFalse; + mImpl->isStreamActive(stream,isActive); + return isActive; +} + +QVariant QXAPlaySession::metaData ( int /*stream*/, QtMultimediaKit::MetaData key ) +{ + return this->metaData(key); +} + +int QXAPlaySession::streamCount() +{ + return mNumStreams; +} + +QMediaStreamsControl::StreamType QXAPlaySession::streamType ( int stream ) +{ + QMediaStreamsControl::StreamType strType = QMediaStreamsControl::UnknownStream; + RET_s_IF_p_IS_NULL(mImpl, strType); + if(mImpl->streamType(stream, strType) == KErrNone) { + return strType; + } + + return QMediaStreamsControl::UnknownStream; +} + +////AspectRatioMode +void QXAPlaySession::setAspectRatioMode(Qt::AspectRatioMode aspectRatioMode) +{ + RET_IF_p_IS_NULL(mImpl); + mImpl->setAspectRatioMode(aspectRatioMode); +} + +Qt::AspectRatioMode QXAPlaySession::getAspectRatioMode() +{ + RET_s_IF_p_IS_NULL(mImpl, Qt::KeepAspectRatio); + return mImpl->getAspectRatioMode(); +} + + diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.h b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.h new file mode 100644 index 000000000..0d366518f --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxaplaysession.h @@ -0,0 +1,192 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAPLAYSESSION_H +#define QXAPLAYSESSION_H + +#include +#include +#include + +#include "qxamediaplayercontrol.h" +#include "qxametadatacontrol.h" +#include "qmediaplayer.h" +#include "xaplaysessioncommon.h" +#include "qxavideowidgetcontrol.h" +#include "qxavideowindowcontrol.h" +#include "qmediastreamscontrol.h" + + +QT_USE_NAMESPACE + +class XAPlaySessionImpl; +class RWindow; +class RWsSession; +class QXAVideoWidgetControl; + +class QXAPlaySession : public QObject, + public XAPlayObserver +{ + Q_OBJECT +public: + QXAPlaySession(QObject *parent); + virtual ~QXAPlaySession(); + + void setVideoWidgetControl( QXAVideoWidgetControl * videoWidgetControl ); + void unsetVideoWidgetControl( QXAVideoWidgetControl * videoWidgetControl ); + void setVideoWindowControl( QXAVideoWindowControl * videoWindowControl ); + void unsetVideoWindowControl( QXAVideoWindowControl * videoWindowControl ); + + //QXAMediaPlayerControl + QMediaPlayer::State state() const { return m_state; } + QMediaPlayer::MediaStatus mediaStatus() const { return m_mediaStatus; } + qint64 duration(); + qint64 position(); + void setPosition(qint64 position); + int volume(); + void setVolume(int volume); + bool isMuted(); + void setMuted(bool muted); + int bufferStatus(); + bool isAudioAvailable(); + bool isVideoAvailable(); + bool isSeekable(); + float playbackRate(); + void setPlaybackRate(float rate); + QMediaContent media(); + void setMedia(const QMediaContent& media); + void play(); + void pause(); + void stop(); + + // Callback from XAPlaySessionImpl + void cbDurationChanged(TInt64 new_dur); + void cbPositionChanged(TInt64 new_pos); + void cbSeekableChanged(TBool seekable); + void cbPlaybackStopped(TInt error); + void cbPrefetchStatusChanged(); + void cbStreamInformation(TBool); + + //MetadataControl methods + QStringList availableExtendedMetaData () const; + QList availableMetaData () const; + QVariant extendedMetaData(const QString & key ) const; + bool isMetaDataAvailable() const; + bool isWritable() const; + QVariant metaData( QtMultimediaKit::MetaData key ) const; + void setExtendedMetaData( const QString & key, const QVariant & value ); + void setMetaData( QtMultimediaKit::MetaData key, const QVariant & value ); + + //QMediaStreamsControl + bool isStreamActive ( int stream ) ; + QVariant metaData ( int stream, QtMultimediaKit::MetaData key ); + int streamCount(); + QMediaStreamsControl::StreamType streamType ( int stream ); + + //QVideoWidgetControl + void setAspectRatioMode(Qt::AspectRatioMode); + Qt::AspectRatioMode getAspectRatioMode(); + +public Q_SLOTS: + void videoWidgetControlWidgetUpdated(); + void videoWindowControlWindowUpdated(); + +Q_SIGNALS: + void mediaChanged(const QMediaContent& content); + void durationChanged(qint64 duration); + void positionChanged(qint64 position); + void stateChanged(QMediaPlayer::State newState); + void mediaStatusChanged(QMediaPlayer::MediaStatus status); + void volumeChanged(int volume); + void mutedChanged(bool muted); + void audioAvailableChanged(bool audioAvailable); + void videoAvailableChanged(bool videoAvailable); + void bufferStatusChanged(int percentFilled); + void seekableChanged(bool); + void availablePlaybackRangesChanged(const QMediaTimeRange&); + void error(int errorCode, const QString &errorString); + void playbackRateChanged(qreal rate); + + //metadata + void metaDataAvailableChanged(bool); + void metaDataChanged(); + void writableChanged(bool); + + //QMediaStreamsControl + void streamsChanged(); + void activeStreamsChanged(); + +private: + void setMediaStatus(QMediaPlayer::MediaStatus); + void setPlayerState(QMediaPlayer::State state); + void updateStreamInfo(TBool emitSignal = EFalse); + +private: + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_mediaStatus; + + QMap m_tags; + QList< QMap > m_streamProperties; + + //seekable + int mSeekable; //-1 =unintialized, 0=nonseekable, 1=seekable + + //StreamInfo + TUint mNumStreams; + TBool mbAudioAvailable; + TBool mbVideoAvailable; + + //Own + XAPlaySessionImpl* mImpl; + + // Does not own + QXAVideoWidgetControl *mVideowidgetControl; + RWindow *mWidgetCtrlWindow; + WId mWidgetCtrlWindowId; + QXAVideoWindowControl *mVideoWindowControl; + RWindow *mWindowCtrlWindow; + WId mWindowCtrlWindowId; + RWsSession *mWsSession; + + QMediaContent mMediaContent; +}; + +#endif /* QXAPLAYSESSION_H */ diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.cpp new file mode 100644 index 000000000..6e9c833fd --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.cpp @@ -0,0 +1,182 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxavideowidgetcontrol.h" +#include "qxacommon.h" +#include "qxawidget.h" +#include + +QXAVideoWidgetControl::QXAVideoWidgetControl(QXAPlaySession *session, QObject *parent) + : QVideoWidgetControl(parent), mSession(session) +{ + QT_TRACE_FUNCTION_ENTRY; + mWidget = new QXAWidget; + if (mWidget) + mWidget->installEventFilter(this); + QT_TRACE_FUNCTION_EXIT; +} + +QXAVideoWidgetControl::~QXAVideoWidgetControl() +{ + QT_TRACE_FUNCTION_ENTRY; + delete mWidget; + QT_TRACE_FUNCTION_EXIT; +} + +QWidget* QXAVideoWidgetControl::videoWidget() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mWidget; +} + +Qt::AspectRatioMode QXAVideoWidgetControl::aspectRatioMode() const +{ + QT_TRACE_FUNCTION_ENTRY; + RET_s_IF_p_IS_NULL(mSession, Qt::IgnoreAspectRatio); + QT_TRACE_FUNCTION_EXIT; + return mSession->getAspectRatioMode(); +} + +void QXAVideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setAspectRatioMode(mode); + QT_TRACE_FUNCTION_EXIT; +} + +bool QXAVideoWidgetControl::isFullScreen() const +{ + QT_TRACE_FUNCTION_ENTRY; + bool retVal = false; + RET_s_IF_p_IS_NULL(mWidget, retVal); + retVal = mWidget->isFullScreen(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +void QXAVideoWidgetControl::setFullScreen(bool fullScreen) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mWidget); + if (fullScreen == mWidget->isFullScreen()) + return; + else if (fullScreen) + mWidget->showFullScreen(); + else + mWidget->showNormal(); + + emit fullScreenChanged(fullScreen); + + QT_TRACE_FUNCTION_EXIT; +} + +int QXAVideoWidgetControl::brightness() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWidgetControl::setBrightness(int brightness) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(brightness); +} + +int QXAVideoWidgetControl::contrast() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWidgetControl::setContrast(int contrast) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(contrast); +} + +int QXAVideoWidgetControl::hue() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWidgetControl::setHue(int hue) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(hue); +} + +int QXAVideoWidgetControl::saturation() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWidgetControl::setSaturation(int saturation) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(saturation); +} + +bool QXAVideoWidgetControl::eventFilter(QObject *object, QEvent *event) +{ + if (object == mWidget) { + if ( event->type() == QEvent::Resize + || event->type() == QEvent::Move + || event->type() == QEvent::WinIdChange + || event->type() == QEvent::ParentChange + || event->type() == QEvent::Show) { + emit widgetUpdated(); + } + } + return false; +} + +WId QXAVideoWidgetControl::videoWidgetWId() +{ + if (mWidget->internalWinId()) + return mWidget->internalWinId(); + else if (mWidget->effectiveWinId()) + return mWidget->effectiveWinId(); + + return NULL; +} diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.h b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.h new file mode 100644 index 000000000..7a0eda765 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowidgetcontrol.h @@ -0,0 +1,93 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAVIDEOWIDGETCONTROL_H +#define QXAVIDEOWIDGETCONTROL_H + +#include +#include +#include "qxaplaysession.h" + +QT_USE_NAMESPACE + +class QXAWidget; + +class QXAVideoWidgetControl : public QVideoWidgetControl +{ + Q_OBJECT +public: + QXAVideoWidgetControl(QXAPlaySession *session, QObject *parent = 0); + ~QXAVideoWidgetControl(); + + QWidget *videoWidget(); + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + bool eventFilter(QObject *object, QEvent *event); + + WId videoWidgetWId(); + +Q_SIGNALS: + void widgetUpdated(); + +private: + QXAPlaySession *mSession; + QXAWidget *mWidget; + +}; + +#endif // QXAVIDEOWIDGETCONTROL_H diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.cpp new file mode 100644 index 000000000..c19b949ac --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.cpp @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxavideowindowcontrol.h" +#include "qxacommon.h" +#include +#include "qxaplaysession.h" + +QXAVideoWindowControl::QXAVideoWindowControl(QXAPlaySession* session, QObject *parent) + :QVideoWindowControl(parent), + mWid(NULL), + mWidget(NULL), + mAspectRatioMode(Qt::IgnoreAspectRatio), + mSession(session) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QXAVideoWindowControl::~QXAVideoWindowControl() +{ + QT_TRACE_FUNCTION_ENTRY; + if (mWidget) + mWidget->removeEventFilter(this); + QT_TRACE_FUNCTION_EXIT; +} + +WId QXAVideoWindowControl::winId() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mWid; +} + +void QXAVideoWindowControl::setWinId(WId id) +{ + QT_TRACE_FUNCTION_ENTRY; + if (mWid != id) { + if (mWidget) + mWidget->removeEventFilter(this); + mWid = id; + mWidget = QWidget::find(mWid); + if (mWidget) + mWidget->installEventFilter(this); + emit windowUpdated(); + } + QT_TRACE_FUNCTION_EXIT; +} + +QRect QXAVideoWindowControl::displayRect() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return mDisplayRect; +} + +void QXAVideoWindowControl::setDisplayRect(const QRect &rect) +{ + QT_TRACE_FUNCTION_ENTRY; + mDisplayRect = rect; + QT_TRACE_FUNCTION_EXIT; +} + +bool QXAVideoWindowControl::isFullScreen() const +{ + QT_TRACE_FUNCTION_ENTRY; + bool retVal(false); + if (mWidget) + retVal = mWidget->isFullScreen(); + QT_TRACE_FUNCTION_EXIT; + return retVal; +} + +void QXAVideoWindowControl::setFullScreen(bool fullScreen) +{ + QT_TRACE_FUNCTION_ENTRY; + if (mWidget && (fullScreen != mWidget->isFullScreen())) { + if (fullScreen) + mWidget->showFullScreen(); + else + mWidget->showNormal(); + emit fullScreenChanged(fullScreen); + } + QT_TRACE_FUNCTION_EXIT; +} + +void QXAVideoWindowControl::repaint() +{ +} + +QSize QXAVideoWindowControl::nativeSize() const +{ + QT_TRACE_FUNCTION_ENTRY; + QSize size(0, 0); + RET_s_IF_p_IS_NULL(mSession, size); + QVariant sizeBundle = mSession->metaData(QtMultimediaKit::Resolution); + QString metadata = sizeBundle.toString(); + if (!metadata.isNull() && !metadata.isEmpty()) { + int xIndex = metadata.indexOf('x'); + if (xIndex > 0) { + size.setWidth(metadata.left(xIndex).toInt()); + xIndex = metadata.length() - (xIndex + 1); + if (xIndex > 0) + size.setHeight(metadata.right(xIndex).toInt()); + } + } + QT_TRACE_FUNCTION_EXIT; + return size; +} + +Qt::AspectRatioMode QXAVideoWindowControl::aspectRatioMode() const +{ + QT_TRACE_FUNCTION_ENTRY; + RET_s_IF_p_IS_NULL(mSession, Qt::IgnoreAspectRatio); + QT_TRACE_FUNCTION_EXIT; + return mSession->getAspectRatioMode(); +} + +void QXAVideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + QT_TRACE_FUNCTION_ENTRY; + RET_IF_p_IS_NULL(mSession); + mSession->setAspectRatioMode(mode); + QT_TRACE_FUNCTION_EXIT; +} + +int QXAVideoWindowControl::brightness() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWindowControl::setBrightness(int brightness) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(brightness); +} + +int QXAVideoWindowControl::contrast() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWindowControl::setContrast(int contrast) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(contrast); +} + +int QXAVideoWindowControl::hue() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWindowControl::setHue(int hue) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(hue); +} + +int QXAVideoWindowControl::saturation() const +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + return 0; +} + +void QXAVideoWindowControl::setSaturation(int saturation) +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; + Q_UNUSED(saturation); +} + +bool QXAVideoWindowControl::eventFilter(QObject *object, QEvent *event) +{ + if (object == mWidget) { + if (event->type() == QEvent::Resize + || event->type() == QEvent::Move + || event->type() == QEvent::WinIdChange + || event->type() == QEvent::ParentChange + || event->type() == QEvent::Show) { + emit windowUpdated(); + } + } + return false; +} diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.h b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.h new file mode 100644 index 000000000..879349822 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxavideowindowcontrol.h @@ -0,0 +1,105 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAVIDEOWINDOWCONTROL_H +#define QXAVIDEOWINDOWCONTROL_H + +#include +#include + +QT_USE_NAMESPACE + +class QXAPlaySession; + +class QXAVideoWindowControl : public QVideoWindowControl +{ + Q_OBJECT + +public: + QXAVideoWindowControl(QXAPlaySession* session, QObject *parent = 0); + ~QXAVideoWindowControl(); + + // QVideoWindowControl virtual functions + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + void repaint(); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + //Callback function to receive event from Qt framework + //for object represented by mWid + bool eventFilter(QObject *object, QEvent *event); + +Q_SIGNALS: + void windowUpdated(); + +private: + WId mWid; + QWidget* mWidget; /* QWidget represented by mWid */ + QRect mDisplayRect; + Qt::AspectRatioMode mAspectRatioMode; + + QXAPlaySession* mSession; +}; + +#endif /* QXAVIDEOWINDOWCONTROL_H */ diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxawidget.cpp b/src/plugins/symbian/openmaxal/mediaplayer/qxawidget.cpp new file mode 100644 index 000000000..4685226fd --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxawidget.cpp @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxawidget.h" +#include + +QXAWidget::QXAWidget(QWidget *parent) + : QWidget(parent) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setAttribute(Qt::WA_OpaquePaintEvent, true); + setAttribute(Qt::WA_NoSystemBackground, true); + setAutoFillBackground(false); + setPalette(QPalette(Qt::black)); + /* Initialize the native window*/ + winId(); +} + +QXAWidget::~QXAWidget() +{ +} + +void QXAWidget::paintEvent(QPaintEvent *event) +{ + Q_UNUSED(event); +} diff --git a/src/plugins/symbian/openmaxal/mediaplayer/qxawidget.h b/src/plugins/symbian/openmaxal/mediaplayer/qxawidget.h new file mode 100644 index 000000000..d36be1fa4 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/qxawidget.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAWIDGET_H +#define QXAWIDGET_H + +#include +#include + +QT_USE_NAMESPACE + +class QXAWidget : public QWidget +{ + Q_OBJECT + +public: + QXAWidget(QWidget *parent = 0); + virtual ~QXAWidget(); + +protected: + void paintEvent(QPaintEvent *event); +}; + + +#endif /* QXAWIDGET_H */ diff --git a/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessioncommon.h b/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessioncommon.h new file mode 100644 index 000000000..a9497e475 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessioncommon.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XAPLAYSESSIONCOMMON_H +#define XAPLAYSESSIONCOMMON_H + +#include + +class XAPlayObserver + { +public: + virtual void cbDurationChanged(TInt64 new_dur) = 0; + virtual void cbPositionChanged(TInt64 new_pos) = 0; + virtual void cbSeekableChanged(TBool seekable) = 0; + virtual void cbPlaybackStopped(TInt error) = 0; + virtual void cbPrefetchStatusChanged() = 0; + virtual void cbStreamInformation(TBool bFirstTime) = 0; + }; + +#endif /*XAPLAYSESSIONCOMMON_H*/ diff --git a/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.cpp b/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.cpp new file mode 100644 index 000000000..88a06807f --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.cpp @@ -0,0 +1,1259 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include +#include + +#include "xaplaysessionimpl.h" +#include "xaplaysessioncommon.h" +#include "xacommon.h" + +#ifdef USE_VIDEOPLAYERUTILITY +#include +#endif + +_LIT8(K8WAVMIMETYPE, "audio/wav"); + +#define RET_ERR_IF_ERR(e) \ + if (e != 0) {\ + return e; \ + } \ + +#define MAX_NUMBER_INTERFACES 20 +const TUint KPlayPosUpdatePeriod = 1000; + +/* Local functions for callback registation */ +void MediaPlayerCallback( XAObjectItf caller, + const void *pContext, + XAuint32 event, + XAresult result, + XAuint32 param, + void *pInterface); + +void PlayItfCallback( XAPlayItf caller, + void *pContext, + XAuint32 event); + +void PrefetchItfCallback( XAPrefetchStatusItf caller, + void * pContext, + XAuint32 event); + +void StreamInformationItfCallback( XAStreamInformationItf caller, + XAuint32 eventId, + XAuint32 streamIndex, + void * pEventData, + void * pContext); + +XAPlaySessionImpl::XAPlaySessionImpl(XAPlayObserver& parent) +:mParent(parent), +mEOEngine(NULL), +mMOPlayer(NULL), +mPlayItf(NULL), +mSeekItf(NULL), +mURIName(NULL), +mWAVMime(NULL), +mbMetadataAvailable(EFalse), +mbVolEnabled(EFalse), +mbMuteEnabled(EFalse), +mbPrefetchStatusChange(EFalse), +mbStreamInfoAvailable(EFalse), +mbPlaybackRateItfAvailable(EFalse), +mbScalable(EFalse), +mCurrAspectRatioMode(Qt::KeepAspectRatio) +#ifdef USE_VIDEOPLAYERUTILITY +, mVideoPlayUtil(NULL) +, mActiveSchedulerWait(NULL) +#endif +{ +} + +XAPlaySessionImpl::~XAPlaySessionImpl() +{ + if (mMOPlayer) + (*mMOPlayer)->Destroy(mMOPlayer); + + if (mEOEngine) + (*mEOEngine)->Destroy(mEOEngine); + + delete mURIName; + delete mWAVMime; + + //clear metadata datastructures + alKeyMap.clear(); + keyMap.clear(); + extendedKeyMap.clear(); + +#ifdef USE_VIDEOPLAYERUTILITY + delete mVideoPlayUtil; + if (mActiveSchedulerWait && \ + mActiveSchedulerWait->IsStarted()) { + mActiveSchedulerWait->AsyncStop(); + } + + delete mActiveSchedulerWait; +#endif + +} + +TInt XAPlaySessionImpl::postConstruct() +{ + TInt retVal; + XAresult xaRes; + XAEngineOption engineOption[] = { (XAuint32) XA_ENGINEOPTION_THREADSAFE, + (XAuint32) XA_BOOLEAN_TRUE + }; + XAEngineItf engineItf; + + mNativeDisplay.locatorType = XA_DATALOCATOR_NATIVEDISPLAY; + mNativeDisplay.hWindow = NULL; + mNativeDisplay.hDisplay = NULL; + mVideoSink.pLocator = (void*)&mNativeDisplay; + mVideoSink.pFormat = NULL; + + // Create and realize Engine object + xaRes = xaCreateEngine (&mEOEngine, 1, engineOption, 0, NULL, NULL); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + xaRes = (*mEOEngine)->Realize(mEOEngine, XA_BOOLEAN_FALSE); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + // Create and realize Output Mix object to be used by player + xaRes = (*mEOEngine)->GetInterface(mEOEngine, XA_IID_ENGINE, (void**) &engineItf); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + TRAP(retVal, mWAVMime = HBufC8::NewL(K8WAVMIMETYPE().Length() + 1)); + RET_ERR_IF_ERR(retVal); + TPtr8 ptr = mWAVMime->Des(); + ptr = K8WAVMIMETYPE(); // copy uri name into local variable + ptr.PtrZ(); // append zero terminator to end of URI + +#ifdef USE_VIDEOPLAYERUTILITY + TRAP(retVal, mVideoPlayUtil = + CVideoPlayerUtility2::NewL( *this, + EMdaPriorityNormal, + EMdaPriorityPreferenceTimeAndQuality) + ); + mActiveSchedulerWait = new CActiveSchedulerWait; +#endif + + return retVal; +} + +TInt XAPlaySessionImpl::addNativeDisplay(RWindow* window, RWsSession* wssession) + { + TInt retVal(KErrNotReady); + + if (!mMOPlayer && !mPlayItf) { + // window can only be set before player creation + mNativeDisplay.locatorType = XA_DATALOCATOR_NATIVEDISPLAY; + mNativeDisplay.hWindow = (void*)window; + mNativeDisplay.hDisplay = (void*)wssession; + retVal = KErrNone; + } + return retVal; +} + +TInt XAPlaySessionImpl::updateNativeDisplay(RWindow* /*window*/, RWsSession* /*wssession*/) +{ + return KErrNone; +} + +TInt XAPlaySessionImpl::removeNativeDisplay(RWindow* /*window*/, RWsSession* /*wssession*/) +{ + return KErrNone; +} + +TInt XAPlaySessionImpl::load(const TDesC& aURI) +{ + TInt retVal; + XAresult xaRes; + XAEngineItf engineItf; + XADynamicSourceItf dynamicSourceItf; + XAboolean required[MAX_NUMBER_INTERFACES]; + XAInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + XAuint32 noOfInterfaces = 0; + TInt i; + + XAmillisecond dur(0); + TPtr8 uriPtr(0,0,0); + TPtr8 mimeTypePtr(0,0,0); + +#ifdef USE_VIDEOPLAYERUTILITY + TRAP(m_VPError, mVideoPlayUtil->OpenFileL(_L("C:\\data\\test.3gp"))); + if (m_VPError) + return 0; + + if(!mActiveSchedulerWait->IsStarted()) + mActiveSchedulerWait->Start(); + + if (m_VPError) + return 0; + + mVideoPlayUtil->Prepare(); + + if(!mActiveSchedulerWait->IsStarted()) + mActiveSchedulerWait->Start(); + + return 0; +#endif + + delete mURIName; + mURIName = NULL; + TRAP(retVal, mURIName = HBufC8::NewL(aURI.Length()+1)); + RET_ERR_IF_ERR(retVal); + uriPtr.Set(mURIName->Des()); + + // This has to be done here since we can not destroy the Player + // in the Resource Lost callback. + if (mbMediaPlayerUnrealized) { + if (mMOPlayer) { + (*mMOPlayer)->Destroy(mMOPlayer); + mMOPlayer = NULL; + } + } + + //py uri name into local variable + //TODO fix copy issue from 16 bit to 8 bit + uriPtr.Copy(aURI); + + //If media player object already exists, just switch source + //using dynamic source interface + if (mMOPlayer && mPlayItf) { + dynamicSourceItf = NULL; + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_DYNAMICSOURCE, &dynamicSourceItf); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + //Setup the data source + //TODO Hard coded locator type + mUri.locatorType = XA_DATALOCATOR_URI; + + //append zero terminator to end of URI + mUri.URI = (XAchar*) uriPtr.PtrZ(); + + //TODO Hard coded locator type + mMime.containerType = XA_CONTAINERTYPE_WAV; + + //TODO Hard coded locator type + mMime.formatType = XA_DATAFORMAT_MIME; + mimeTypePtr.Set(mWAVMime->Des()); + mMime.mimeType = (XAchar*)mimeTypePtr.Ptr(); + mDataSource.pFormat = (void*)&mMime; + mDataSource.pLocator = (void*)&mUri; + + xaRes = (*dynamicSourceItf)->SetSource(dynamicSourceItf, &mDataSource); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + } + else { // Create media player object + + // Setup the data source + // TODO Hard coded locator type + mUri.locatorType = XA_DATALOCATOR_URI; + + //append zero terminator to end of URI + mUri.URI = (XAchar*) uriPtr.PtrZ(); + + //TODO Hard coded locator type + mMime.containerType = XA_CONTAINERTYPE_WAV; + + //TODO Hard coded locator type + mMime.formatType = XA_DATAFORMAT_MIME; + mimeTypePtr.Set(mWAVMime->Des()); + mMime.mimeType = (XAchar*)mimeTypePtr.Ptr(); + mDataSource.pFormat = (void*)&mMime; + mDataSource.pLocator = (void*)&mUri; + + //Setup the audio data sink + mLocatorOutputDevice.locatorType = XA_DATALOCATOR_IODEVICE; + mLocatorOutputDevice.deviceType = 6; + mAudioSink.pLocator = (void*) &mLocatorOutputDevice; + mAudioSink.pFormat = NULL; + + //Init arrays required[] and iidArray[] + for (i = 0; i < MAX_NUMBER_INTERFACES; i++) { + required[i] = XA_BOOLEAN_FALSE; + iidArray[i] = XA_IID_NULL; + } + + noOfInterfaces = 0; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_SEEK; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_DYNAMICSOURCE; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_METADATAEXTRACTION; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_NOKIALINEARVOLUME; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_NOKIAVOLUMEEXT; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_PREFETCHSTATUS; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_STREAMINFORMATION; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_PLAYBACKRATE; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_VIDEOPOSTPROCESSING; + noOfInterfaces++; + + xaRes = (*mEOEngine)->GetInterface(mEOEngine, XA_IID_ENGINE, (void**) &engineItf); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*engineItf)->CreateMediaPlayer(engineItf, + &mMOPlayer, + &mDataSource, + NULL, + &mAudioSink, + &mVideoSink, + NULL, + NULL, + noOfInterfaces, + iidArray, + required); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*mMOPlayer)->Realize(mMOPlayer, XA_BOOLEAN_FALSE); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + mbMediaPlayerUnrealized = FALSE; + + xaRes = (*mMOPlayer)->RegisterCallback(mMOPlayer, MediaPlayerCallback, (void*)this); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_PLAY, &mPlayItf); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*mPlayItf)->RegisterCallback(mPlayItf, PlayItfCallback, (void*)this); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*mPlayItf)->SetPositionUpdatePeriod(mPlayItf, (XAmillisecond)KPlayPosUpdatePeriod); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*mPlayItf)->SetCallbackEventsMask( mPlayItf, + ( XA_PLAYEVENT_HEADATEND | + XA_PLAYEVENT_HEADATNEWPOS | + XA_PLAYEVENT_HEADMOVING ) + ); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_SEEK, &mSeekItf); + retVal = mapError(xaRes, ETrue); + + //Metadata + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_METADATAEXTRACTION, &mMetadataExtItf); + if(mapError(xaRes, ETrue)==KErrNone) { + mbMetadataAvailable = ETrue; + setupALKeyMap(); //done only once at creation of meadia player + } + + //volume + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_NOKIALINEARVOLUME, &mNokiaLinearVolumeItf); + if(mapError(xaRes, ETrue)==KErrNone) + mbVolEnabled = ETrue; + + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_NOKIAVOLUMEEXT, &mNokiaVolumeExtItf); + if(mapError(xaRes, ETrue)==KErrNone) + mbMuteEnabled = ETrue; + + //buffer status + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_PREFETCHSTATUS, &mPrefetchStatusItf); + if(mapError(xaRes, ETrue)==KErrNone) { + mbPrefetchStatusChange = ETrue; + (*mPrefetchStatusItf)->RegisterCallback(mPrefetchStatusItf, PrefetchItfCallback, (void*)this); + (*mPrefetchStatusItf)->SetCallbackEventsMask(mPrefetchStatusItf, XA_PREFETCHEVENT_FILLLEVELCHANGE); + } + + //stream information + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_STREAMINFORMATION, &mStreamInformationItf); + if(mapError(xaRes, ETrue)==KErrNone) { + mbStreamInfoAvailable = ETrue; + mParent.cbStreamInformation(ETrue); //indicate first time + (*mStreamInformationItf)->RegisterStreamChangeCallback(mStreamInformationItf, StreamInformationItfCallback, (void*)this); + } + + //playback rate + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_PLAYBACKRATE, &mPlaybackRateItf); + if(mapError(xaRes, ETrue)==KErrNone) + mbPlaybackRateItfAvailable = ETrue; + + + //videopostprocessing + xaRes = (*mMOPlayer)->GetInterface(mMOPlayer, XA_IID_VIDEOPOSTPROCESSING, &mVideoPostProcessingItf); + if(mapError(xaRes, ETrue)==KErrNone) + mbScalable = ETrue; + + } + + if(mbMetadataAvailable) { + keyMap.clear(); + extendedKeyMap.clear(); + setupMetaData(); //done every time source is changed + } + else { //send signal for seekable + mParent.cbSeekableChanged(ETrue); + } + + mCurPosition = 0; + mParent.cbPositionChanged(mCurPosition); + + xaRes = (*mPlayItf)->GetDuration(mPlayItf, &dur); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + mDuration = dur; + mParent.cbDurationChanged(mDuration); + + return retVal; +} + +void XAPlaySessionImpl::unload() +{ + mPlayItf = NULL; + mSeekItf = NULL; + + //Metadata + mbMetadataAvailable = FALSE; + mMetadataExtItf = NULL; + alKeyMap.clear(); + keyMap.clear(); + extendedKeyMap.clear(); + //Volume + mNokiaLinearVolumeItf = NULL; + mbVolEnabled = FALSE; + mNokiaVolumeExtItf = NULL; + mbMuteEnabled = NULL; + + //buffer status + mPrefetchStatusItf = NULL; + mbPrefetchStatusChange = FALSE; + + //stream information + mStreamInformationItf = NULL; + mbStreamInfoAvailable = FALSE; + mbAudioStream = FALSE; + mbVideoStream = FALSE; + mNumStreams = 0; + + //Playbackrate + mPlaybackRateItf = NULL; + mbPlaybackRateItfAvailable = FALSE; + + mVideoPostProcessingItf = NULL; + mbScalable = FALSE; + mCurrAspectRatioMode = Qt::KeepAspectRatio; + + //internal + mCurPosition = 0; // in milliseconds + mDuration = 0; // in milliseconds + + + mbMediaPlayerUnrealized = TRUE; + + delete mURIName; + mURIName = NULL; + +} + +TInt XAPlaySessionImpl::play() +{ + TInt retVal(KErrGeneral); + XAresult xaRes; + XAuint32 state; + +#ifdef USE_VIDEOPLAYERUTILITY + mVideoPlayUtil->Play(); + return 0; +#endif + + if (!mMOPlayer || !mPlayItf) + return retVal; + + xaRes = (*mPlayItf)->GetPlayState(mPlayItf, &state); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + if ((state == XA_PLAYSTATE_STOPPED) || + (state == XA_PLAYSTATE_PAUSED)) { + xaRes = (*mPlayItf)->SetPlayState(mPlayItf, XA_PLAYSTATE_PLAYING); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + } + return retVal; +} + +TInt XAPlaySessionImpl::pause() +{ + TInt retVal(KErrGeneral); + XAresult xaRes; + XAuint32 state; + +#ifdef USE_VIDEOPLAYERUTILITY + TRAPD(err, mVideoPlayUtil->PauseL()); + return 0; +#endif + + if (!mMOPlayer || !mPlayItf) + return retVal; + + xaRes = (*mPlayItf)->GetPlayState(mPlayItf, &state); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + if ((state == XA_PLAYSTATE_STOPPED) || + (state == XA_PLAYSTATE_PLAYING)) { + xaRes = (*mPlayItf)->SetPlayState(mPlayItf, XA_PLAYSTATE_PAUSED); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + } + + return retVal; +} + +TInt XAPlaySessionImpl::stop() +{ + TInt retVal(KErrGeneral); + XAresult xaRes; + XAuint32 state; + +#ifdef USE_VIDEOPLAYERUTILITY + mVideoPlayUtil->Stop(); + return 0; +#endif + + if (!mMOPlayer || !mPlayItf) + return retVal; + + xaRes = (*mPlayItf)->GetPlayState(mPlayItf, &state); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + + if ((state == XA_PLAYSTATE_PAUSED) || + (state == XA_PLAYSTATE_PLAYING)) { + xaRes = (*mPlayItf)->SetPlayState(mPlayItf, XA_PLAYSTATE_STOPPED); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + mCurPosition += KPlayPosUpdatePeriod; + mParent.cbPositionChanged(mCurPosition); + } + + return retVal; +} + +TInt XAPlaySessionImpl::duration(TInt64& aDur) +{ + TInt retVal(KErrGeneral); + +#ifdef USE_VIDEOPLAYERUTILITY + TTimeIntervalMicroSeconds dur(0); + TRAPD(err, mVideoPlayUtil->DurationL()); + if (!err) + aDur = dur.Int64() / 1000; + return 0; +#endif + if (!mMOPlayer || !mPlayItf) + return retVal; + aDur = mDuration; + return retVal; +} + +TInt XAPlaySessionImpl::position(TInt64& aPos) +{ + TInt retVal(KErrGeneral); +#ifdef USE_VIDEOPLAYERUTILITY + TTimeIntervalMicroSeconds dur(0); + TRAPD(err, mVideoPlayUtil->PositionL()); + if (!err) + aPos = dur.Int64() / 1000; + return 0; +#endif + +/* XAresult xaRes; + XAmillisecond pos(0);*/ + + if (!mMOPlayer || !mPlayItf) + return retVal; + + aPos = mCurPosition; + return retVal; +} + +TInt XAPlaySessionImpl::getSeekable(TBool& seekable) +{ + TInt retVal(KErrGeneral); + + if (!mMOPlayer || !mSeekItf) + return retVal; + + retVal = ETrue; + seekable = ETrue; + + return retVal; +} + +TInt XAPlaySessionImpl::seek(TInt64 pos) +{ + TInt retVal(KErrGeneral); + XAresult xaRes; + + if (!mMOPlayer || !mSeekItf) + return retVal; + + xaRes = (*mSeekItf)->SetPosition(mSeekItf, (XAmillisecond)pos, XA_SEEKMODE_FAST); + retVal = mapError(xaRes, ETrue); + RET_ERR_IF_ERR(retVal); + mCurPosition = pos; + + return retVal; +} + +void XAPlaySessionImpl::cbMediaPlayer(XAObjectItf /*caller*/, + const void */*pContext*/, + XAuint32 event, + XAresult result, + XAuint32 /*param*/, + void */*pInterface*/) + +{ + switch (event) { + case XA_OBJECT_EVENT_RESOURCES_LOST: + unload(); + mParent.cbPlaybackStopped(result); + break; + case XA_OBJECT_EVENT_RUNTIME_ERROR: + { + switch (result) { + case XA_RESULT_RESOURCE_LOST: + unload(); + mParent.cbPlaybackStopped(result); + break; + default: + break; + }; /* of switch (result) */ + } + default: + break; + } /* of switch (event) */ +} + +void XAPlaySessionImpl::cbPlayItf(XAPlayItf /*caller*/, + void */*pContext*/, + XAuint32 event) +{ + switch(event) { + case XA_PLAYEVENT_HEADATEND: + mParent.cbPlaybackStopped(KErrNone); + break; + case XA_PLAYEVENT_HEADATMARKER: + break; + case XA_PLAYEVENT_HEADATNEWPOS: + mCurPosition += KPlayPosUpdatePeriod; + mParent.cbPositionChanged(mCurPosition); + break; + case XA_PLAYEVENT_HEADMOVING: + break; + case XA_PLAYEVENT_HEADSTALLED: + break; + default: + break; + } +} + +void XAPlaySessionImpl::cbPrefetchItf(XAuint32 event) +{ + if(event == XA_PREFETCHEVENT_FILLLEVELCHANGE) + mParent.cbPrefetchStatusChanged(); +} + +void XAPlaySessionImpl::cbStreamInformationItf( XAuint32 /*eventId*/, + XAuint32 /*streamIndex*/, + void * /*pEventData*/) +{ + mParent.cbStreamInformation(EFalse); +} + +void XAPlaySessionImpl::setAspectRatioMode(Qt::AspectRatioMode aspectRatioMode) +{ + if( !mbScalable || + (mCurrAspectRatioMode == aspectRatioMode)) { + return; + } + + XAuint32 scaleOptions;; + XAuint32 backgrndColor = 1; + XAuint32 renderingHints = 1; + + switch(aspectRatioMode) { + case Qt::IgnoreAspectRatio: + scaleOptions = XA_VIDEOSCALE_STRETCH; + break; + case Qt::KeepAspectRatio: + scaleOptions = XA_VIDEOSCALE_FIT; + break; + case Qt::KeepAspectRatioByExpanding: + scaleOptions = XA_VIDEOSCALE_CROP; + break; + default: + return; + } + + XAresult xaRes = (*mVideoPostProcessingItf)->SetScaleOptions(mVideoPostProcessingItf, \ + scaleOptions, backgrndColor, renderingHints); + if(mapError(xaRes, ETrue) == KErrNone) + xaRes = (*mVideoPostProcessingItf)->Commit(mVideoPostProcessingItf); + + if(mapError(xaRes, ETrue) == KErrNone) + mCurrAspectRatioMode = aspectRatioMode; +} + +Qt::AspectRatioMode XAPlaySessionImpl::getAspectRatioMode() +{ + return mCurrAspectRatioMode; +} + + +#ifdef USE_VIDEOPLAYERUTILITY +void XAPlaySessionImpl::MvpuoOpenComplete(TInt aError) +{ + TRACE_FUNCTION_ENTRY; + m_VPError = aError; + if (mActiveSchedulerWait->IsStarted()) + mActiveSchedulerWait->AsyncStop(); + TRACE_FUNCTION_EXIT; +} + +void XAPlaySessionImpl::MvpuoPrepareComplete(TInt aError) +{ + TRACE_FUNCTION_ENTRY; + m_VPError = aError; + if (mActiveSchedulerWait->IsStarted()) + mActiveSchedulerWait->AsyncStop(); + + RWindow* window = (RWindow*)mNativeDisplay.hWindow; + RWsSession* wssession = (RWsSession*)mNativeDisplay.hDisplay; + + if (window) { + TRect videoExtent = TRect(window->Size()); + TRect clipRect = TRect(window->Size()); + TRAP_IGNORE(mVideoPlayUtil->AddDisplayWindowL(*wssession, *(CCoeEnv::Static()->ScreenDevice()), *window, videoExtent, clipRect)); + TRAP_IGNORE(mVideoPlayUtil->SetAutoScaleL(*window, EAutoScaleBestFit)); + } + TRACE_FUNCTION_EXIT; +} + +void XAPlaySessionImpl::MvpuoFrameReady(CFbsBitmap& /*aFrame*/,TInt /*aError*/) +{ + TRACE_FUNCTION_ENTRY_EXIT; +} + +void XAPlaySessionImpl::MvpuoPlayComplete(TInt /*aError*/) +{ + TRACE_FUNCTION_ENTRY; + mParent.cbPlaybackStopped_EOS(); + TRACE_FUNCTION_EXIT; +} + +void XAPlaySessionImpl::MvpuoEvent(const TMMFEvent& /*aEvent*/) +{ + TRACE_FUNCTION_ENTRY_EXIT; +} + +#endif + +TInt XAPlaySessionImpl::mapError(XAresult xa_err, TBool /*debPrn*/) +{ + TInt retVal(KErrGeneral); + + switch(xa_err) { + case XA_RESULT_SUCCESS: + retVal = KErrNone; + break; + case XA_RESULT_PRECONDITIONS_VIOLATED: + break; + case XA_RESULT_PARAMETER_INVALID: + break; + case XA_RESULT_MEMORY_FAILURE: + break; + case XA_RESULT_RESOURCE_ERROR: + break; + case XA_RESULT_RESOURCE_LOST: + break; + case XA_RESULT_IO_ERROR: + break; + case XA_RESULT_BUFFER_INSUFFICIENT: + break; + case XA_RESULT_CONTENT_CORRUPTED: + break; + case XA_RESULT_CONTENT_UNSUPPORTED: + break; + case XA_RESULT_CONTENT_NOT_FOUND: + break; + case XA_RESULT_PERMISSION_DENIED: + break; + case XA_RESULT_FEATURE_UNSUPPORTED: + break; + case XA_RESULT_INTERNAL_ERROR: + break; + case XA_RESULT_UNKNOWN_ERROR: + break; + case XA_RESULT_OPERATION_ABORTED: + break; + case XA_RESULT_CONTROL_LOST: + break; + default: + break; + } + + return retVal; +} + + +QStringList XAPlaySessionImpl::availableExtendedMetaData () const +{ + QStringList retList; + + //create a qlist with all keys in keyMap hash + QHashIterator it(extendedKeyMap); + while(it.hasNext()) { + it.next(); + retList << it.key(); + } + + return retList; +} + +QList XAPlaySessionImpl::availableMetaData () const +{ + QList retList; + + //create a qlist with all keys in keyMap hash + QHashIterator it(keyMap); + while( it.hasNext() ) { + it.next(); + retList << it.key(); + } + + return retList; +} + +QVariant XAPlaySessionImpl::getMetaData( int alIndex ) const +{ + QVariant ret; //invalid variant + + //find index for the given key + if(mMetadataExtItf) { + XAuint32 valueSize = 0; + XAresult res = (*mMetadataExtItf)->GetValueSize(mMetadataExtItf, alIndex, &valueSize); + if(res == XA_RESULT_SUCCESS) { + XAMetadataInfo * value = (XAMetadataInfo*)calloc(valueSize, 1); + if(value) { + res = (*mMetadataExtItf)->GetValue(mMetadataExtItf, alIndex, valueSize, value); + if(res == XA_RESULT_SUCCESS) { + if(value->encoding == XA_CHARACTERENCODING_ASCII) + ret = QVariant ((const char*)value->data); + else if(value->encoding == XA_CHARACTERENCODING_UTF16LE) + ret = QVariant(QString::fromUtf16((ushort*)value->data, (value->size/2)-1)); //dont include null terminating character + else if(value->encoding == XA_CHARACTERENCODING_BINARY) + ret = QVariant(QImage::fromData(value->data, value->size)); + } + + free(value); + } + } + } + + return ret; +} + +QVariant XAPlaySessionImpl::metaData( QtMultimediaKit::MetaData key ) const +{ + QVariant ret; + if(keyMap.contains(key)) + ret = getMetaData(keyMap[key]); + return ret; +} + +QVariant XAPlaySessionImpl::extendedMetaData(const QString & key ) const +{ + QVariant ret; + if(extendedKeyMap.contains(key)) + ret = getMetaData(extendedKeyMap[key]); + + return ret; +} + +bool XAPlaySessionImpl::isMetaDataAvailable() const +{ + return ((keyMap.size()>0) || (extendedKeyMap.size()>0)); +} + +bool XAPlaySessionImpl::isWritable() const +{ + return false; +} + +void XAPlaySessionImpl::setExtendedMetaData( const QString&, const QVariant&) +{ + //Do Nothing +} + +void XAPlaySessionImpl::setMetaData( QtMultimediaKit::MetaData, const QVariant& ) +{ + //Do Nothing +} + +void XAPlaySessionImpl::setupALKeyMap() +{ + alKeyMap["KhronosTitle"] = QtMultimediaKit::Title; + alKeyMap["KhronosComment"] = QtMultimediaKit::Comment; + alKeyMap["KhronosTrackNumber"] = QtMultimediaKit::TrackNumber; + alKeyMap["KhronosAlbumArtJPEG"] = QtMultimediaKit::CoverArtImage; + alKeyMap["KhronosAlbumArtPNG"] = QtMultimediaKit::CoverArtImage; + alKeyMap["KhronosAlbum"] = QtMultimediaKit::AlbumTitle; + alKeyMap["KhronosArtist"] = QtMultimediaKit::AlbumArtist; + alKeyMap["KhronosGenre"] = QtMultimediaKit::Genre; + alKeyMap["KhronosYear"] = QtMultimediaKit::Year; + alKeyMap["KhronosYear"] = QtMultimediaKit::Date; + alKeyMap["KhronosRating"] = QtMultimediaKit::UserRating; + alKeyMap["KhronosCopyright"] = QtMultimediaKit::Copyright; + alKeyMap["Author"] = QtMultimediaKit::Author; + alKeyMap["Duration"] = QtMultimediaKit::Duration; + alKeyMap["Stream Count"] = QtMultimediaKit::ChannelCount; + alKeyMap["Composer"] = QtMultimediaKit::Composer; + alKeyMap["Resolution"] = QtMultimediaKit::Resolution; + alKeyMap["FrameRate"] = QtMultimediaKit::VideoFrameRate; + alKeyMap["ClipBitRate"] = QtMultimediaKit::VideoBitRate; + alKeyMap["Codec"] = QtMultimediaKit::VideoCodec; + alKeyMap["attachedpicture"] = QtMultimediaKit::CoverArtImage; + + /*Keys not available + QtMedia::SubTitle + QtMedia::Description + QtMedia::Category + QtMedia::Keywords + QtMedia::Language + QtMedia::Publisher + QtMedia::ParentalRating + QtMedia::RatingOrganisation + QtMedia::Size + QtMedia::MediaType + QtMedia::AudioBitrate + QtMedia::AudioCodec + QtMedia::AverageLevel + QtMedia::PeakValue + QtMedia::Frequency + QtMedia::ContributingArtist + QtMedia::Conductor + QtMedia::Lyrics + QtMedia::Mood + QtMedia::TrackCount + QtMedia::PixelAspectRatio + QtMedia::PosterUri + QtMedia::ChapterNumber + QtMedia::Director + QtMedia::LeadPerformer + QtMedia::Writer */ +} + +TInt XAPlaySessionImpl::mapMetaDataKey(const char* asckey, QtMultimediaKit::MetaData& key) +{ + if(alKeyMap.contains(asckey)) { + key = alKeyMap[asckey]; + return KErrNone; + } + + return KErrNotFound; +} + +TInt XAPlaySessionImpl::setupMetaData() +{ + XAresult res; + if(mMetadataExtItf) { + XAuint32 numItems = 0; + res = (*mMetadataExtItf)->GetItemCount(mMetadataExtItf, &numItems); + RET_ERR_IF_ERR(mapError(res, ETrue)); + + for(int i=0; iGetKeySize(mMetadataExtItf, i, &keySize); + RET_ERR_IF_ERR(mapError(res, ETrue)); + + XAMetadataInfo *key = (XAMetadataInfo *)calloc(keySize,1); + if(key) { + res = (*mMetadataExtItf)->GetKey(mMetadataExtItf, i, keySize, key); + RET_ERR_IF_ERR(mapError(res, ETrue)); + + if(key->encoding == XA_CHARACTERENCODING_ASCII) { //only handle ASCII keys ignore others + QtMultimediaKit::MetaData qtKey; + if(mapMetaDataKey((const char*)key->data, qtKey) == KErrNone)//qt metadata + keyMap[qtKey] = i; + else //extended metadata + extendedKeyMap[(const char*)key->data] = i; + } + + free(key); + } + } + + //check for seek property to generate seekable signal + QVariant var = extendedMetaData("Seekable"); + if(!var.isValid() || (var.toString() == "1")) + mParent.cbSeekableChanged(ETrue); + else + mParent.cbSeekableChanged(EFalse); + + } + + return KErrGeneral; +} + +//Volume +TInt XAPlaySessionImpl::volume(TInt& v) +{ + if(mbVolEnabled) { + XAresult res = (*mNokiaLinearVolumeItf)->GetVolumeLevel(mNokiaLinearVolumeItf, (XAuint32 *)&v); + return mapError(res, ETrue); + } + + return KErrNotFound; +} + +TInt XAPlaySessionImpl::setVolume(TInt v) +{ + if(mbVolEnabled) { + XAresult res = (*mNokiaLinearVolumeItf)->SetVolumeLevel(mNokiaLinearVolumeItf, (XAuint32*)&v); + return mapError(res, ETrue); + } + + return KErrNotFound; +} + +TInt XAPlaySessionImpl::setMute(TBool bMute) +{ + if(mbMuteEnabled) { + XAresult res = (*mNokiaVolumeExtItf)->SetMute(mNokiaVolumeExtItf, (XAboolean)bMute); + return mapError(res, ETrue); + } + + return KErrNotFound; + +} + +TInt XAPlaySessionImpl::getMute(TBool& bIsMute) +{ + if(mbMuteEnabled) { + XAboolean xaMute; + XAresult res = (*mNokiaVolumeExtItf)->GetMute(mNokiaVolumeExtItf, &xaMute); + bIsMute = xaMute; + return mapError(res, ETrue); + } + + return KErrNotFound; +} + + +TInt XAPlaySessionImpl::bufferStatus(TInt &bs) +{ + TInt ret = KErrNotFound; + + if(mbPrefetchStatusChange) { + XApermille satusPerThousand; + XAresult res = (*mPrefetchStatusItf)->GetFillLevel(mPrefetchStatusItf, &satusPerThousand); + ret = mapError(res, ETrue); + if(ret == KErrNone) + bs = satusPerThousand/10.0; //convert to parts per hundred + } + return ret; +} + +QMediaStreamsControl::StreamType XAPlaySessionImpl::mapStreamType(XAuint32& alStreamType) +{ + switch(alStreamType) { + case XA_DOMAINTYPE_AUDIO: + return QMediaStreamsControl::AudioStream; + case XA_DOMAINTYPE_VIDEO: + return QMediaStreamsControl::VideoStream; + case XA_DOMAINTYPE_IMAGE: + return QMediaStreamsControl::DataStream; + } + return QMediaStreamsControl::UnknownStream; +} + + +TInt XAPlaySessionImpl::numMediaStreams(TUint& numStreams) +{ + TInt ret = KErrNotFound; + numStreams = 0; + if(mbStreamInfoAvailable) { + XAMediaContainerInformation mediaContainerInfo; + XAresult res = (*mStreamInformationItf)->QueryMediaContainerInformation(mStreamInformationItf, &mediaContainerInfo); + ret = mapError(res, ETrue); + if(ret == KErrNone) + numStreams = mediaContainerInfo.numStreams; + } + return ret; +} + +TInt XAPlaySessionImpl::streamType(TUint index, QMediaStreamsControl::StreamType& type) +{ + TInt ret = KErrNotFound; + type = QMediaStreamsControl::UnknownStream; + if(mbStreamInfoAvailable) { + XAuint32 strType; + XAresult res = (*mStreamInformationItf)->QueryStreamType(mStreamInformationItf, (XAuint32)(index+1), &strType); + ret = mapError(res, ETrue); + if(ret == KErrNone) + type = mapStreamType(strType); + } + return ret; +} + +TInt XAPlaySessionImpl::isStreamActive(TUint index, TBool& isActive) +{ + TUint numStreams; + TInt ret = numMediaStreams(numStreams); + if((ret == KErrNone) && (index < numStreams)) { + isActive = EFalse; + if(numStreams > 0) { + //create array of bools + XAboolean *activeStreams = new XAboolean[numStreams+1]; + XAresult res = (*mStreamInformationItf)->QueryActiveStreams(mStreamInformationItf, (XAuint32*)&numStreams, activeStreams); + ret = mapError(res, ETrue); + if(ret == KErrNone) + isActive = activeStreams[index+1]; + delete[] activeStreams; + } + } + return ret; +} + +TInt XAPlaySessionImpl::getPlaybackRate(TReal32 &rate) +{ + TInt ret = KErrNotFound; + + if(mbPlaybackRateItfAvailable) { + XApermille perMilleRate = 0; + ret = (*mPlaybackRateItf)->GetRate(mPlaybackRateItf, &perMilleRate); + rate = perMilleRate / 1000.0; + } + return ret; +} + +TInt XAPlaySessionImpl::setPlaybackRate(TReal32 rate) +{ + TInt ret = KErrNotFound; + if(mbPlaybackRateItfAvailable) + ret = (*mPlaybackRateItf)->SetRate(mPlaybackRateItf, (XApermille)(rate * 1000.0)); + return ret; +} + + +/* Local function implementation */ +void MediaPlayerCallback( XAObjectItf caller, + const void *pContext, + XAuint32 event, + XAresult result, + XAuint32 param, + void *pInterface) +{ + if (pContext) + ((XAPlaySessionImpl*)pContext)->cbMediaPlayer( caller, + pContext, + event, + result, + param, + pInterface); +} + +void PlayItfCallback( XAPlayItf caller, + void *pContext, + XAuint32 event) +{ + if (pContext) + ((XAPlaySessionImpl*)pContext)->cbPlayItf(caller, + pContext, + event); +} + +void PrefetchItfCallback( XAPrefetchStatusItf /*caller*/, + void *pContext, + XAuint32 event) +{ + if (pContext) + ((XAPlaySessionImpl*)pContext)->cbPrefetchItf(event); +} + +void StreamInformationItfCallback( XAStreamInformationItf /*caller*/, + XAuint32 eventId, + XAuint32 streamIndex, + void * pEventData, + void * pContext) +{ + if (pContext) + ((XAPlaySessionImpl*)pContext)->cbStreamInformationItf( eventId, + streamIndex, + pEventData); +} + + + +// End of file diff --git a/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.h b/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.h new file mode 100644 index 000000000..30144712a --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediaplayer/xaplaysessionimpl.h @@ -0,0 +1,210 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XAPLAYSESSIONIMPL_H +#define XAPLAYSESSIONIMPL_H + +#include +#include +#include +#ifdef USE_VIDEOPLAYERUTILITY +#include +#endif + +#include "qtmedianamespace.h" +#include "qmediastreamscontrol.h" + +class XAPlayObserver; +class RWindow; +class RWsSession; + +class XAPlaySessionImpl +#ifdef USE_VIDEOPLAYERUTILITY + : public MVideoPlayerUtilityObserver +#endif +{ +public: + XAPlaySessionImpl(XAPlayObserver& parent); + ~XAPlaySessionImpl(); + TInt postConstruct(); + TInt addNativeDisplay(RWindow* window, RWsSession* wssession); + TInt updateNativeDisplay(RWindow* window, RWsSession* wssession); + TInt removeNativeDisplay(RWindow* window, RWsSession* wssession); + TInt load(const TDesC& aURI); + void unload(); + TInt play(); + TInt pause(); + TInt stop(); + TInt duration(TInt64& aDur); + TInt position(TInt64& aPos); + TInt getSeekable(TBool& seekable); + TInt seek(TInt64 pos); + + //Metadata + QStringList availableExtendedMetaData () const; + QList availableMetaData () const; + QVariant extendedMetaData(const QString & key ) const; + bool isMetaDataAvailable() const; + bool isWritable() const; + QVariant metaData( QtMultimediaKit::MetaData key ) const; + void setExtendedMetaData( const QString & key, const QVariant & value ); + void setMetaData( QtMultimediaKit::MetaData key, const QVariant & value ); + + TInt volume(TInt&); + TInt setVolume(TInt); + TInt setMute(TBool); + TInt getMute(TBool&); + + TInt bufferStatus(TInt &); + + + TInt numMediaStreams(TUint& numStreams); + TInt streamType(TUint index, QMediaStreamsControl::StreamType& type); + TInt isStreamActive(TUint index, TBool& isActive); + + TInt getPlaybackRate(TReal32 &rate); + TInt setPlaybackRate(TReal32 rate); + + //AspectRatioMode + void setAspectRatioMode(Qt::AspectRatioMode); + Qt::AspectRatioMode getAspectRatioMode(); + +public: + void cbMediaPlayer( XAObjectItf caller, + const void *pContext, + XAuint32 event, + XAresult result, + XAuint32 param, + void *pInterface); + + void cbPlayItf(XAPlayItf caller, + void *pContext, + XAuint32 event); + + + void cbPrefetchItf(XAuint32); + + void cbStreamInformationItf(XAuint32, XAuint32, void*); + +#ifdef USE_VIDEOPLAYERUTILITY + //MVideoPlayerUtilityObserver + void MvpuoOpenComplete(TInt aError); + void MvpuoPrepareComplete(TInt aError); + void MvpuoFrameReady(CFbsBitmap& aFrame,TInt aError); + void MvpuoPlayComplete(TInt aError); + void MvpuoEvent(const TMMFEvent& aEvent); +#endif + +private: + TInt mapError(XAresult xa_err, + TBool debPrn); + void setupALKeyMap(); + TInt setupMetaData(); + TInt mapMetaDataKey(const char* asckey, QtMultimediaKit::MetaData& key); + QVariant getMetaData( int alIndex ) const; + + QMediaStreamsControl::StreamType mapStreamType(XAuint32& alStreamType); + + +private: + XAPlayObserver& mParent; + XAObjectItf mEOEngine; + XAObjectItf mMOPlayer; + XAPlayItf mPlayItf; + XASeekItf mSeekItf; + HBufC8* mURIName; + HBufC8* mWAVMime; + // Audio Source + XADataSource mDataSource; + XADataFormat_MIME mMime; + XADataLocator_URI mUri; + //Audio Sink + XADataSink mAudioSink; + XADataLocator_IODevice mLocatorOutputDevice; + //Video Sink + XADataSink mVideoSink; + XADataLocator_NativeDisplay mNativeDisplay; + + //Metadata + TBool mbMetadataAvailable; + XAMetadataExtractionItf mMetadataExtItf; + QHash alKeyMap; + QHash keyMap; + QHash extendedKeyMap; + + //Volume + XANokiaLinearVolumeItf mNokiaLinearVolumeItf; + TBool mbVolEnabled; + XANokiaVolumeExtItf mNokiaVolumeExtItf; + TBool mbMuteEnabled; + + //buffer status + XAPrefetchStatusItf mPrefetchStatusItf; + TBool mbPrefetchStatusChange; + + //stream information + XAStreamInformationItf mStreamInformationItf; + TBool mbStreamInfoAvailable; + TBool mbAudioStream; + TBool mbVideoStream; + TInt mNumStreams; + + //Playbackrate + XAPlaybackRateItf mPlaybackRateItf; + TBool mbPlaybackRateItfAvailable; + + XAVideoPostProcessingItf mVideoPostProcessingItf; + TBool mbScalable; + Qt::AspectRatioMode mCurrAspectRatioMode; + + //internal + TInt64 mCurPosition; // in milliseconds + TInt64 mDuration; // in milliseconds + + TBool mbMediaPlayerUnrealized; +#ifdef USE_VIDEOPLAYERUTILITY + CVideoPlayerUtility2* mVideoPlayUtil; + CActiveSchedulerWait* mActiveSchedulerWait; + TInt m_VPError; +#endif +}; + +#endif /* XAPLAYSESSIONIMPL_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/mediarecorder.pri b/src/plugins/symbian/openmaxal/mediarecorder/mediarecorder.pri new file mode 100644 index 000000000..ee78e8348 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/mediarecorder.pri @@ -0,0 +1,24 @@ +INCLUDEPATH += $$PWD + +# Input +HEADERS += \ + $$PWD/qxarecordmediaservice.h \ + $$PWD/qxarecordsession.h \ + $$PWD/qxaaudioendpointselector.h \ + $$PWD/qxaaudioencodercontrol.h \ + $$PWD/qxamediacontainercontrol.h \ + $$PWD/qxamediarecordercontrol.h \ + $$PWD/xarecordsessionimpl.h \ + $$PWD/xarecordsessioncommon.h + +SOURCES += \ + $$PWD/qxarecordmediaservice.cpp \ + $$PWD/qxarecordsession.cpp \ + $$PWD/qxaaudioendpointselector.cpp \ + $$PWD/qxaaudioencodercontrol.cpp \ + $$PWD/qxamediacontainercontrol.cpp \ + $$PWD/qxamediarecordercontrol.cpp \ + $$PWD/xarecordsessionimpl.cpp + +LIBS += \ + -lbafl diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.cpp b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.cpp new file mode 100644 index 000000000..2826f79a2 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.cpp @@ -0,0 +1,111 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxaaudioencodercontrol.h" +#include "qxarecordsession.h" +#include "qxacommon.h" + +QXAAudioEncoderControl::QXAAudioEncoderControl(QXARecordSession *session, QObject *parent) +:QAudioEncoderControl(parent), m_session(session) +{ +} + +QXAAudioEncoderControl::~QXAAudioEncoderControl() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QStringList QXAAudioEncoderControl::supportedAudioCodecs() const +{ + if (m_session) + return m_session->supportedAudioCodecs(); + return QStringList(); +} + +QString QXAAudioEncoderControl::codecDescription(const QString &codecName) const +{ + if (m_session) + return m_session->codecDescription(codecName); + return QString(); +} + +QList QXAAudioEncoderControl::supportedSampleRates( + const QAudioEncoderSettings &settings, + bool *continuous) const +{ + if (m_session) + return m_session->supportedSampleRates(settings, continuous); + return QList(); +} + +QAudioEncoderSettings QXAAudioEncoderControl::audioSettings() const +{ + if (m_session) + return m_session->audioSettings(); + return QAudioEncoderSettings(); +} + +void QXAAudioEncoderControl::setAudioSettings(const QAudioEncoderSettings &settings) +{ + if (m_session) + m_session->setAudioSettings(settings); +} + +QStringList QXAAudioEncoderControl::supportedEncodingOptions(const QString &codec) const +{ + if (m_session) + return m_session->supportedEncodingOptions(codec); + return QStringList(); +} + +QVariant QXAAudioEncoderControl::encodingOption(const QString &codec, const QString &name) const +{ + if (m_session) + return m_session->encodingOption(codec, name); + return QVariant(); +} + +void QXAAudioEncoderControl::setEncodingOption( + const QString &codec, const QString &name, const QVariant &value) +{ + if (m_session) + m_session->setEncodingOption(codec, name, value); +} diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.h b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.h new file mode 100644 index 000000000..117d36fdc --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioencodercontrol.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAAUDIOENCODERCONTROL_H +#define QXAAUDIOENCODERCONTROL_H + +#include + +QT_USE_NAMESPACE + +/* + * This class implements QAudioEncoderControl interface. + */ +class QXARecordSession; + +class QXAAudioEncoderControl : public QAudioEncoderControl +{ + Q_OBJECT + +public: + QXAAudioEncoderControl(QXARecordSession *session, QObject *parent = 0); + virtual ~QXAAudioEncoderControl(); + + QStringList supportedAudioCodecs() const; + QString codecDescription(const QString &codecName) const; + + QList supportedSampleRates(const QAudioEncoderSettings &settings, + bool *continuous = 0) const; + + QAudioEncoderSettings audioSettings() const; + void setAudioSettings(const QAudioEncoderSettings &settings); + + QStringList supportedEncodingOptions(const QString &codec) const; + QVariant encodingOption(const QString &codec, const QString &name) const; + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + +private: + QXARecordSession *m_session; +}; + +#endif /* QXAAUDIOENCODERCONTROL_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.cpp b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.cpp new file mode 100644 index 000000000..7e546595c --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.cpp @@ -0,0 +1,98 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxaaudioendpointselector.h" +#include "qxarecordsession.h" +#include "qxacommon.h" + +QXAAudioEndpointSelector::QXAAudioEndpointSelector(QXARecordSession *session, QObject *parent) +:QAudioEndpointSelector(parent), m_session(session) +{ + connect(m_session, SIGNAL(availableAudioInputsChanged()), + this, SLOT(availableAudioInputsChanged())); + connect(m_session, SIGNAL(activeEndpointChanged(QString)), + this, SIGNAL(activeEndpointChanged(QString))); +} + +QXAAudioEndpointSelector::~QXAAudioEndpointSelector() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QList QXAAudioEndpointSelector::availableEndpoints() const +{ + if (m_session) + return m_session->availableEndpoints(); + return QList(); +} + +QString QXAAudioEndpointSelector::endpointDescription(const QString &name) const +{ + if (m_session) + return m_session->endpointDescription(name); + return QString(); +} + +QString QXAAudioEndpointSelector::defaultEndpoint() const +{ + if (m_session) + return m_session->defaultEndpoint(); + return QString(); +} + +QString QXAAudioEndpointSelector::activeEndpoint() const +{ + if (m_session) + return m_session->activeEndpoint(); + return QString(); +} + +void QXAAudioEndpointSelector::setActiveEndpoint(const QString &name) +{ + if (m_session) + m_session->setActiveEndpoint(name); +} + +void QXAAudioEndpointSelector::availableAudioInputsChanged() + { + emit availableEndpointsChanged(); + } + diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.h b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.h new file mode 100644 index 000000000..5fbadbc64 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxaaudioendpointselector.h @@ -0,0 +1,77 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAAUDIOENDPOINTSELECTOR_H +#define QXAAUDIOENDPOINTSELECTOR_H + +#include + +QT_USE_NAMESPACE + +/* + * This class implements QAudioEncoderControl interface. + */ +class QXARecordSession; + +class QXAAudioEndpointSelector : public QAudioEndpointSelector +{ + Q_OBJECT + +public: + QXAAudioEndpointSelector(QXARecordSession *session, QObject *parent); + ~QXAAudioEndpointSelector(); + + QList availableEndpoints() const; + QString endpointDescription(const QString &name) const; + QString defaultEndpoint() const; + QString activeEndpoint() const; + +public Q_SLOTS: + void setActiveEndpoint(const QString &name); + +private Q_SLOTS: + void availableAudioInputsChanged(); + +private: + QXARecordSession *m_session; +}; + +#endif /* QXAAUDIOENDPOINTSELECTOR_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.cpp b/src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.cpp new file mode 100644 index 000000000..0d97fd5e5 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.cpp @@ -0,0 +1,81 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxamediacontainercontrol.h" +#include "qxarecordsession.h" +#include "qxacommon.h" + +QXAMediaContainerControl::QXAMediaContainerControl(QXARecordSession *session, QObject *parent) +:QMediaContainerControl(parent), m_session(session) +{ +} + +QXAMediaContainerControl::~QXAMediaContainerControl() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QStringList QXAMediaContainerControl::supportedContainers() const +{ + if (m_session) + return m_session->supportedContainers(); + return QStringList(); +} + +QString QXAMediaContainerControl::containerMimeType() const +{ + if (m_session) + return m_session->containerMimeType(); + return QString(); +} + +void QXAMediaContainerControl::setContainerMimeType(const QString &formatMimeType) +{ + if (m_session) + m_session->setContainerMimeType(formatMimeType); +} + +QString QXAMediaContainerControl::containerDescription(const QString &formatMimeType) const +{ + if (m_session) + return m_session->containerDescription(formatMimeType); + return QString(); +} diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.h b/src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.h new file mode 100644 index 000000000..4b05fc190 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxamediacontainercontrol.h @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAMEDIACONTAINERCONTROL_H +#define QXAMEDIACONTAINERCONTROL_H + +#include + +QT_USE_NAMESPACE + +/* + * This class implements QMediaContainerControl interface. + */ +class QXARecordSession; + +class QXAMediaContainerControl : public QMediaContainerControl +{ + Q_OBJECT + +public: + QXAMediaContainerControl(QXARecordSession *session, QObject *parent = 0); + virtual ~QXAMediaContainerControl(); + + QStringList supportedContainers() const; + QString containerMimeType() const; + void setContainerMimeType(const QString &formatMimeType); + QString containerDescription(const QString &formatMimeType) const; + +private: + QXARecordSession *m_session; +}; + +#endif /* QXAMEDIACONTAINERCONTROL_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.cpp b/src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.cpp new file mode 100644 index 000000000..330edf008 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.cpp @@ -0,0 +1,122 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxamediarecordercontrol.h" +#include "qxarecordsession.h" +#include "qxacommon.h" + +QXAMediaRecoderControl::QXAMediaRecoderControl(QXARecordSession *session, QObject *parent) +:QMediaRecorderControl(parent), m_session(session) +{ + connect(m_session, SIGNAL(stateChanged(QMediaRecorder::State)), + this, SIGNAL(stateChanged(QMediaRecorder::State))); + connect(m_session, SIGNAL(error(int,QString)), + this,SIGNAL(error(int,QString))); + connect(m_session, SIGNAL(durationChanged(qint64)), + this, SIGNAL(durationChanged(qint64))); +} + +QXAMediaRecoderControl::~QXAMediaRecoderControl() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QUrl QXAMediaRecoderControl::outputLocation() const +{ + if (m_session) + return m_session->outputLocation(); + return QUrl(); +} + +bool QXAMediaRecoderControl::setOutputLocation(const QUrl &location) +{ + if (m_session) + return m_session->setOutputLocation(location); + return false; +} + +QMediaRecorder::State QXAMediaRecoderControl::state() const +{ + if (m_session) + return m_session->state(); + return QMediaRecorder::StoppedState; +} + +qint64 QXAMediaRecoderControl::duration() const +{ + if (m_session) + return m_session->duration(); + return 0; +} + +void QXAMediaRecoderControl::record() +{ + if (m_session) + m_session->record(); +} + +void QXAMediaRecoderControl::pause() +{ + if (m_session) + m_session->pause(); +} + +void QXAMediaRecoderControl::stop() +{ + if (m_session) + m_session->stop(); +} + +void QXAMediaRecoderControl::applySettings() +{ + if (m_session) + m_session->applySettings(); +} + +bool QXAMediaRecoderControl::isMuted() const +{ + return false; +} + +void QXAMediaRecoderControl::setMuted(bool) +{ + +} diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.h b/src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.h new file mode 100644 index 000000000..c6495f2e4 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxamediarecordercontrol.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAMEDIARECORDERCONTROL_H +#define QXAMEDIARECORDERCONTROL_H + +#include +#include + +QT_USE_NAMESPACE + +/* + * This class implements QMediaRecorderControl interface. + */ + +class QXARecordSession; + +class QXAMediaRecoderControl : public QMediaRecorderControl +{ + Q_OBJECT + +public: + QXAMediaRecoderControl(QXARecordSession *session, QObject *parent = 0); + virtual ~QXAMediaRecoderControl(); + + QUrl outputLocation() const; + bool setOutputLocation(const QUrl &location); + + QMediaRecorder::State state() const; + + qint64 duration() const; + bool isMuted() const; + void applySettings(); + +public Q_SLOTS: + void record(); + void pause(); + void stop(); + void setMuted(bool); + +private: + QXARecordSession *m_session; +}; + +#endif /* QXAMEDIARECORDERCONTROL_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.cpp b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.cpp new file mode 100644 index 000000000..05c57feb7 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.cpp @@ -0,0 +1,86 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qxarecordmediaservice.h" +#include "qxarecordsession.h" +#include "qxamediarecordercontrol.h" +#include "qxaaudioendpointselector.h" +#include "qxaaudioencodercontrol.h" +#include "qxamediacontainercontrol.h" +#include "qxacommon.h" + +QXARecodMediaService::QXARecodMediaService(QObject *parent) +:QMediaService(parent) +{ + QT_TRACE_FUNCTION_ENTRY; + m_session = new QXARecordSession(this); + m_control = new QXAMediaRecoderControl(m_session, this); + m_endpoint = new QXAAudioEndpointSelector(m_session, this); + m_encoder = new QXAAudioEncoderControl(m_session, this); + m_container = new QXAMediaContainerControl(m_session, this); +} + +QXARecodMediaService::~QXARecodMediaService() +{ + QT_TRACE_FUNCTION_ENTRY_EXIT; +} + +QMediaControl* QXARecodMediaService::requestControl(const char *name) +{ + QT_TRACE_FUNCTION_ENTRY; + if (qstrcmp(name, QMediaRecorderControl_iid) == 0) + return m_control; + else if (qstrcmp(name, QAudioEndpointSelector_iid) == 0) + return m_endpoint; + else if (qstrcmp(name, QAudioEncoderControl_iid) == 0) + return m_encoder; + else if (qstrcmp(name, QMediaContainerControl_iid) == 0) + return m_container; + QT_TRACE_FUNCTION_EXIT; + return 0; +} + +void QXARecodMediaService::releaseControl(QMediaControl *control) +{ + Q_UNUSED(control) +} diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.h b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.h new file mode 100644 index 000000000..98f5136e8 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordmediaservice.h @@ -0,0 +1,78 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXARECORDMEDIASERVICE_H +#define QXARECORDMEDIASERVICE_H + +#include +#include + +QT_USE_NAMESPACE + +/* + * This class implements QMediaService interface. + */ + +class QXARecordSession; +class QXAMediaRecoderControl; +class QXAAudioEndpointSelector; +class QXAAudioEncoderControl; +class QXAMediaContainerControl; + +class QXARecodMediaService : public QMediaService +{ + + Q_OBJECT + +public: + QXARecodMediaService(QObject *parent = 0); + ~QXARecodMediaService(); + QMediaControl *requestControl(const char *name); + void releaseControl( QMediaControl *control); +private: + QXARecordSession *m_session; + QXAMediaRecoderControl *m_control; + QXAAudioEndpointSelector *m_endpoint; + QXAAudioEncoderControl *m_encoder; + QXAMediaContainerControl *m_container; +}; + +#endif /* QXARECORDMEDIASERVICE_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.cpp b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.cpp new file mode 100644 index 000000000..76cdfc3da --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.cpp @@ -0,0 +1,766 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include "qxarecordsession.h" +#include "xarecordsessionimpl.h" +#include "qxacommon.h" + +/* The following declaration is required to allow QList to be added to + * QVariant + */ +Q_DECLARE_METATYPE(QList) + +/* This macro checks for m_impl null pointer. If it is, emits an error signal + * error(QMediaRecorder::ResourceError, tr("Service has not been started")); + * and returns from function immediately with value 's'. + */ + +#define RETURN_s_IF_m_impl_IS_NULL(s) \ + if (!m_impl) { \ + emit error(QMediaRecorder::ResourceError, QXARecordSession::tr("Service has not been started")); \ + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Service has not been started\"))"); \ + return s; \ + } + +/* This macro checks for m_impl null pointer. If it is, emits an error signal + * error(QMediaRecorder::ResourceError, tr("Service has not been started")); + * and returns from function immediately. + */ +#define RETURN_IF_m_impl_IS_NULL \ + if (!m_impl) { \ + emit error(QMediaRecorder::ResourceError, QXARecordSession::tr("Service has not been started")); \ + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Service has not been started\"))"); \ + return; \ + } + +QXARecordSession::QXARecordSession(QObject *parent) +:QObject(parent), +m_state(QMediaRecorder::StoppedState), +m_previousState(QMediaRecorder::StoppedState) +{ + QT_TRACE_FUNCTION_ENTRY; + m_impl = NULL; + m_impl = new XARecordSessionImpl(*this); + if (m_impl) { + if (m_impl->postConstruct() == KErrNone) { + initCodecsList(); + initContainersList(); + m_containerMimeType = QString("audio/wav"); + m_audioencodersettings.setCodec("pcm"); + m_audioencodersettings.setBitRate(0); + m_audioencodersettings.setChannelCount(-1); + m_audioencodersettings.setEncodingMode(QtMultimediaKit::ConstantQualityEncoding); + m_audioencodersettings.setQuality(QtMultimediaKit::NormalQuality); + m_audioencodersettings.setSampleRate(-1); + setEncoderSettingsToImpl(); + m_URItoImplSet = false; + QT_TRACE1("Initialized implementation"); + } + else { + delete m_impl; + m_impl = NULL; + QT_TRACE1("Error initializing implementation"); + } + } + else { + emit error(QMediaRecorder::ResourceError, tr("Unable to start Service")); + } + QT_TRACE_FUNCTION_EXIT; +} + +QXARecordSession::~QXARecordSession() +{ + QT_TRACE_FUNCTION_ENTRY; + delete m_impl; + QT_TRACE_FUNCTION_EXIT; +} + +QUrl QXARecordSession::outputLocation() +{ + return m_outputLocation; +} + +bool QXARecordSession::setOutputLocation(const QUrl &location) +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_s_IF_m_impl_IS_NULL(false); + + // Location can be set only when recorder is in stopped state. + if (state() != QMediaRecorder::StoppedState) + return false; + + // Validate URL + if (!location.isValid()) + return false; + + // If old and new locations are same, do nothing. + QString newUrlStr = (QUrl::fromUserInput(location.toString())).toString(); + QString curUrlStr = (QUrl::fromUserInput(m_outputLocation.toString())).toString(); + if (curUrlStr.compare(newUrlStr) == KErrNone) + return true; + + QT_TRACE2("Location:", newUrlStr); + m_outputLocation = location; + /* New file, so user can set new settings */ + m_previousState = QMediaRecorder::StoppedState; + m_URItoImplSet = false; + + QT_TRACE_FUNCTION_EXIT; + return true; +} + +QMediaRecorder::State QXARecordSession::state() +{ + return m_state; +} + +qint64 QXARecordSession::duration() +{ + TInt64 dur(0); + + QT_TRACE_FUNCTION_ENTRY; + + RETURN_s_IF_m_impl_IS_NULL(dur); + + m_impl->duration(dur); + + QT_TRACE_FUNCTION_EXIT; + return (qint64)dur; +} + +void QXARecordSession::applySettings() +{ + /* Settings can only be applied when the recorder is in the stopped + * state after creation. */ + if ((state() == QMediaRecorder::StoppedState) && (m_state == m_previousState)) { + if (m_appliedaudioencodersettings != m_audioencodersettings) + setEncoderSettingsToImpl(); + } + else { + emit error(QMediaRecorder::FormatError, tr("Settings cannot be changed once recording started")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Settings cannot be changed once recording started\"))"); + } +} + +void QXARecordSession::record() +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_IF_m_impl_IS_NULL; + + /* No op if object is already in recording state */ + if (state() == QMediaRecorder::RecordingState) + return; + + /* 1. Set encoder settings here */ + if (m_appliedaudioencodersettings != m_audioencodersettings) + RET_IF_FALSE(setEncoderSettingsToImpl()); + + /* 2. Set URI to impl */ + RET_IF_FALSE(setURIToImpl()); + + /* 3. Start recording... + * If successful, setRecorderState(QMediaRecorder::RecordingState); + * will be called from the callback cbRecordingStarted() + */ + if (m_impl->record() != KErrNone) { + emit error(QMediaRecorder::ResourceError, tr("Generic error")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Generic error\"))"); + } + + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::pause() +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_IF_m_impl_IS_NULL; + + /* No op if object is already in paused/stopped state */ + if ((state() == QMediaRecorder::PausedState) || (state() == QMediaRecorder::StoppedState)) { + return; + } + + if (m_impl->pause() == KErrNone) { + setRecorderState(QMediaRecorder::PausedState); + } + else { + emit error(QMediaRecorder::ResourceError, tr("Unable to pause")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Unable to pause\"))"); + } + + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::stop() +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_IF_m_impl_IS_NULL; + + /* No op if object is already in paused state */ + if (state() == QMediaRecorder::StoppedState) + return; + + if ((m_impl->stop() == KErrNone)) { + setRecorderState(QMediaRecorder::StoppedState); + } + else { + emit error(QMediaRecorder::ResourceError, tr("Unable to stop")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Unable to stop\"))"); + } + + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::cbDurationChanged(TInt64 new_pos) +{ + QT_TRACE_FUNCTION_ENTRY; + + emit durationChanged((qint64)new_pos); + SIGNAL_EMIT_TRACE1("emit durationChanged((qint64)new_pos);"); + + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::cbAvailableAudioInputsChanged() +{ + QT_TRACE_FUNCTION_ENTRY; + + emit availableAudioInputsChanged(); + SIGNAL_EMIT_TRACE1("emit availableAudioInputsChanged();"); + + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::cbRecordingStarted() +{ + QT_TRACE_FUNCTION_ENTRY; + + setRecorderState(QMediaRecorder::RecordingState); + + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::cbRecordingStopped() +{ + QT_TRACE_FUNCTION_ENTRY; + + emit error(QMediaRecorder::ResourceError, tr("Resources Unavailable")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Resources Unavailable\"))"); + setRecorderState(QMediaRecorder::StoppedState); + /* Set record state to Stopped */ + if (m_impl) + m_impl->stop(); + + QT_TRACE_FUNCTION_EXIT; +} + +/* For QAudioEndpointSelector begin */ +QList QXARecordSession::availableEndpoints() +{ + QT_TRACE_FUNCTION_ENTRY; + + QList strList; + + RETURN_s_IF_m_impl_IS_NULL(strList); + + QString str; + RArray names; + m_impl->getAudioInputDeviceNames(names); + for (TInt index = 0; index < names.Count(); index++) { + str = QString((QChar*)names[index].Ptr(), names[index].Length()); + strList.append(str); + } + + QT_TRACE_FUNCTION_EXIT; + return strList; +} + +QString QXARecordSession::endpointDescription(const QString &name) +{ + /* From AL we get only device name */ + return name; +} + +QString QXARecordSession::defaultEndpoint() +{ + QT_TRACE_FUNCTION_ENTRY; + + QString str; + + RETURN_s_IF_m_impl_IS_NULL(str); + + TPtrC name; + if(m_impl->defaultAudioInputDevice(name) == KErrNone) + str = QString((QChar*)name.Ptr(), name.Length()); + + QT_TRACE_FUNCTION_EXIT; + return str; +} + +QString QXARecordSession::activeEndpoint() +{ + QT_TRACE_FUNCTION_ENTRY; + + QString str; + + RETURN_s_IF_m_impl_IS_NULL(str); + + TPtrC name; + if(m_impl->activeAudioInputDevice(name) == KErrNone) + str = QString((QChar*)name.Ptr(), name.Length()); + + QT_TRACE_FUNCTION_EXIT; + return str; +} + +void QXARecordSession::setActiveEndpoint(const QString &name) +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_IF_m_impl_IS_NULL; + + if (name.isNull() || name.isEmpty()) + return; + + TPtrC16 tempPtr(reinterpret_cast(name.utf16())); + if (m_impl->setAudioInputDevice(tempPtr) == true) { + emit activeEndpointChanged(name); + SIGNAL_EMIT_TRACE1("emit activeEndpointChanged(name)"); + } + else { + emit error(QMediaRecorder::ResourceError, tr("Invalid endpoint")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Invalid endpoint\"))"); + } + + QT_TRACE_FUNCTION_EXIT; +} +/* For QAudioEndpointSelector end */ + +/* For QAudioEncoderControl begin */ +QStringList QXARecordSession::supportedAudioCodecs() +{ + return m_codecs; +} + +QString QXARecordSession::codecDescription(const QString &codecName) +{ + if (m_codecs.contains(codecName)) + return QString(codecName); + return QString(); +} + +QList QXARecordSession::supportedSampleRates( + const QAudioEncoderSettings &settings, + bool *continuous) +{ + QT_TRACE_FUNCTION_ENTRY; + + QList srList; + + RETURN_s_IF_m_impl_IS_NULL(srList); + + QString selectedCodec = settings.codec(); + if (selectedCodec.isNull() || selectedCodec.isEmpty()) + selectedCodec = QString("pcm"); + + if (m_codecs.indexOf(selectedCodec) >= 0) { + RArray sampleRates; + TBool isContinuous; + TPtrC16 tempPtr(reinterpret_cast(selectedCodec.utf16())); + if (m_impl->getSampleRates(tempPtr, sampleRates, isContinuous) == KErrNone) { + for (TInt index = 0; index < sampleRates.Count(); index++) + srList.append(sampleRates[index]); + sampleRates.Close(); + if (continuous) + { + *continuous = false; + if (isContinuous == true) + *continuous = true; + } + } + } + + QT_TRACE_FUNCTION_EXIT; + return srList; +} + +QAudioEncoderSettings QXARecordSession::audioSettings() +{ + return m_appliedaudioencodersettings; +} + +void QXARecordSession::setAudioSettings(const QAudioEncoderSettings &settings) +{ + /* Settings can only be set when the recorder is in the stopped + * state after creation. */ + if ((state() == QMediaRecorder::StoppedState) && (m_state == m_previousState)) { + /* Validate and ignore rest of the settings */ + m_audioencodersettings = settings; + } + else { + emit error(QMediaRecorder::FormatError, tr("Settings cannot be changed once recording started")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Settings cannot be changed once recording started\"))"); + } +} + +QStringList QXARecordSession::supportedEncodingOptions(const QString &codec) +{ + QT_TRACE_FUNCTION_ENTRY; + Q_UNUSED(codec); + QStringList options; + if ((codec.compare("aac") == 0) || + (codec.compare("amr") == 0)) + { + options << "bitrate" << "quality"; + } + + + QT_TRACE_FUNCTION_EXIT; + return options; +} + +QVariant QXARecordSession::encodingOption(const QString &codec, const QString &name) +{ + QT_TRACE_FUNCTION_ENTRY; + + QVariant encodingOption; + QMap map; + RETURN_s_IF_m_impl_IS_NULL(encodingOption); + + if (name.compare("bitrate") == 0) { + TPtrC16 tempPtr(reinterpret_cast(codec.utf16())); + QList bitrateList; + RArray bitrates; + TBool continuous; + if (m_impl->getBitrates(tempPtr, bitrates, continuous) == KErrNone) { + for (TInt index = 0; index < bitrates.Count(); index++) + bitrateList.append(bitrates[index]); + bitrates.Close(); + } + encodingOption.setValue(bitrateList); + map.insert("continuous", QVariant(continuous)); + map.insert("bitrates", encodingOption); + } + + QT_TRACE_FUNCTION_EXIT; + return map; +} + +void QXARecordSession::setEncodingOption( + const QString &codec, + const QString &name, + const QVariant &value) +{ + /* + * Currently nothing can be set via this function. + * Bitrate is set via QAudioEncoderSettings::setBitrate(). + */ + Q_UNUSED(codec); + Q_UNUSED(name); + Q_UNUSED(value); +} +/* For QAudioEncoderControl end */ + +QStringList QXARecordSession::supportedContainers() +{ + return m_containers; +} + +QString QXARecordSession::containerMimeType() +{ + return m_containerMimeType; +} + +void QXARecordSession::setContainerMimeType(const QString &formatMimeType) +{ + if (formatMimeType.isNull() || formatMimeType.isEmpty()) + return; + else if (m_containers.indexOf(formatMimeType) >= 0 ) + m_containerMimeType = formatMimeType; + else { + emit error(QMediaRecorder::FormatError, tr("Invalid container")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Invalid container\"))"); + } +} + +QString QXARecordSession::containerDescription(const QString &formatMimeType) +{ + int index = m_containers.indexOf(formatMimeType); + if (index >= 0) { + return m_containersDesc.at(index); + } + else { + emit error(QMediaRecorder::FormatError, tr("Invalid container")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Invalid container\"))"); + } + return QString(); +} + +void QXARecordSession::setRecorderState(QMediaRecorder::State state) +{ + if (state != m_state) { + m_previousState = m_state; + m_state = state; + emit stateChanged(m_state); + SIGNAL_EMIT_TRACE1("emit stateChanged(m_state);"); + } +} + +void QXARecordSession::initCodecsList() +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_IF_m_impl_IS_NULL; + + m_codecs.clear(); + + const RArray& names = m_impl->getAudioEncoderNames(); + QString str; + + for (TInt index = 0; index < names.Count(); index++) { + str = QString((QChar*)names[index].Ptr(), names[index].Length()); + m_codecs.append(str); + } + QT_TRACE_FUNCTION_EXIT; +} + +void QXARecordSession::initContainersList() +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_IF_m_impl_IS_NULL; + + m_containers.clear(); + m_containersDesc.clear(); + + const RArray& names = m_impl->getContainerNames(); + const RArray& descs = m_impl->getContainerDescs(); + QString str; + + for (TInt32 index = 0; index < names.Count(); index++) { + str = QString((QChar*)names[index].Ptr(), names[index].Length()); + m_containers.append(str); + str = QString((QChar*)descs[index].Ptr(), descs[index].Length()); + m_containersDesc.append(str); + } + QT_TRACE_FUNCTION_EXIT; +} + +bool QXARecordSession::setEncoderSettingsToImpl() +{ + QT_TRACE_FUNCTION_ENTRY; + + RETURN_s_IF_m_impl_IS_NULL(false); + + m_impl->resetEncoderAttributes(); + + /* m_containerMimeType is alredy validated in ::setContainerMimeType() */ + QString tempStr = m_containerMimeType; + TPtrC16 tempPtr(reinterpret_cast(tempStr.utf16())); + m_impl->setContainerType(tempPtr); + + /* vaidate and assign codec */ + if (m_audioencodersettings.codec().isNull() || m_audioencodersettings.codec().isEmpty()) { + m_audioencodersettings.setCodec(m_appliedaudioencodersettings.codec()); + } + tempStr = m_audioencodersettings.codec(); + if (m_codecs.indexOf(tempStr) >= 0) { + tempPtr.Set(reinterpret_cast(tempStr.utf16())); + /* We already did validation above, so function always returns true */ + m_impl->setCodec(tempPtr); + } + else { + QT_TRACE2("Codec selected is :", m_audioencodersettings.codec()); + emit error(QMediaRecorder::FormatError, tr("Invalid codec")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Invalid codec\"));"); + return false; + } + + /* Validate and set bitrate only if encoding mode is other than quality encoding and container type is not wav*/ + if ((m_audioencodersettings.encodingMode() != QtMultimediaKit::ConstantQualityEncoding) && + (m_containerMimeType.compare("audio/wav") != 0)) { + m_impl->setBitRate(m_audioencodersettings.bitRate()); + m_audioencodersettings.setBitRate(m_impl->getBitRate()); + } + + if (m_audioencodersettings.channelCount() == -1) { + m_impl->setOptimalChannelCount(); + } + else { + m_impl->setChannels(m_audioencodersettings.channelCount()); + m_audioencodersettings.setChannelCount(m_impl->getChannels()); + } + + switch (m_audioencodersettings.encodingMode()) { + case QtMultimediaKit::ConstantQualityEncoding: { + switch (m_audioencodersettings.quality()) { + case QtMultimediaKit::VeryLowQuality: + m_impl->setVeryLowQuality(); + m_audioencodersettings.setBitRate(m_impl->getBitRate()); + break; + case QtMultimediaKit::LowQuality: + m_impl->setLowQuality(); + m_audioencodersettings.setBitRate(m_impl->getBitRate()); + break; + case QtMultimediaKit::NormalQuality: + m_impl->setNormalQuality(); + m_audioencodersettings.setBitRate(m_impl->getBitRate()); + break; + case QtMultimediaKit::HighQuality: + m_impl->setHighQuality(); + m_audioencodersettings.setBitRate(m_impl->getBitRate()); + break; + case QtMultimediaKit::VeryHighQuality: + m_impl->setVeryHighQuality(); + m_audioencodersettings.setBitRate(m_impl->getBitRate()); + break; + default: + break; + }; /* end of switch (m_audioencodersettings.quality())*/ + } + break; + case QtMultimediaKit::ConstantBitRateEncoding: { + TInt32 status = m_impl->setCBRMode(); + if (status == KErrNotSupported) { + emit error(QMediaRecorder::FormatError, tr("Invalid encoding mode setting")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Invalid encoding mode setting\"));"); + return false; + } + else if (status != KErrNone) { + emit error(QMediaRecorder::ResourceError, tr("Internal error")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Internal error\"));"); + return false; + } + } + break; + case QtMultimediaKit::AverageBitRateEncoding: { + TInt32 status = m_impl->setVBRMode(); + if (status == KErrNotSupported) { + emit error(QMediaRecorder::FormatError, tr("Invalid encoding mode setting")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Invalid encoding mode setting\"));"); + return false; + } + else if (status != KErrNone) { + emit error(QMediaRecorder::ResourceError, tr("Internal error")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Internal error\"));"); + return false; + } + } + break; + case QtMultimediaKit::TwoPassEncoding: + // fall through + default: { + emit error(QMediaRecorder::FormatError, tr("Invalid encoding mode setting")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::FormatError, tr(\"Invalid encoding mode setting\"));"); + return false; + } + }; /* switch (m_audioencodersettings.encodingMode()) */ + + if (m_audioencodersettings.sampleRate() == -1) { + m_impl->setOptimalSampleRate(); + } + else { + m_impl->setSampleRate(m_audioencodersettings.sampleRate()); + m_audioencodersettings.setSampleRate(m_impl->getSampleRate()); + } + m_appliedaudioencodersettings = m_audioencodersettings; + + QT_TRACE_FUNCTION_EXIT; + return true; +} + +bool QXARecordSession::setURIToImpl() +{ + QT_TRACE_FUNCTION_ENTRY; + if (m_URItoImplSet) + return true; + + /* If m_outputLocation is null, set a default location */ + if (m_outputLocation.isEmpty()) { + QDir outputDir(QDir::rootPath()); + + int lastImage = 0; + int fileCount = 0; + foreach(QString fileName, outputDir.entryList(QStringList() << "recordclip_*")) { + int imgNumber = fileName.mid(5, fileName.size() - 9).toInt(); + lastImage = qMax(lastImage, imgNumber); + if (outputDir.exists(fileName)) + fileCount += 1; + } + lastImage += fileCount; + m_outputLocation = QUrl(QDir::toNativeSeparators(outputDir.canonicalPath() + QString("/recordclip_%1").arg(lastImage + 1, 4, 10, QLatin1Char('0')))); + } + + QString newUrlStr = (QUrl::fromUserInput(m_outputLocation.toString())).toString(); + // append file prefix if required + if (newUrlStr.lastIndexOf('.') == -1) { + QString fileExtension; + if ((m_containerMimeType.compare("audio/wav")) == KErrNone) { + fileExtension = QString(".wav"); + } + else if ((m_containerMimeType.compare("audio/amr")) == KErrNone) { + fileExtension = QString(".amr"); + } + else if ((m_containerMimeType.compare("audio/mpeg")) == KErrNone) { + fileExtension = QString(".mp4"); + } + newUrlStr.append(fileExtension); + } + + QT_TRACE2("Filename selected is :", newUrlStr); + TPtrC16 tempPtr(reinterpret_cast(newUrlStr.utf16())); + if (m_impl->setURI(tempPtr) != 0) { + emit error(QMediaRecorder::ResourceError, tr("Generic error")); + SIGNAL_EMIT_TRACE1("emit error(QMediaRecorder::ResourceError, tr(\"Generic error\"))"); + return false; + } + m_URItoImplSet = true; + m_outputLocation = QUrl(newUrlStr); + QT_TRACE_FUNCTION_EXIT; + return true; +} diff --git a/src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.h b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.h new file mode 100644 index 000000000..cabe58fc9 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/qxarecordsession.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXARECORDSESSION_H +#define QXARECORDSESSION_H + +#include +#include +#include "qmediarecorder.h" +#include "xarecordsessioncommon.h" + +QT_USE_NAMESPACE + +class XARecordSessionImpl; + +/* + * This is a backend class for all QXXXControl objects. + * This class contains actual implementation of recording functionality + * from the control object's perspective. + */ + + +class QXARecordSession : public QObject, + public XARecordObserver +{ +Q_OBJECT + +public: + QXARecordSession(QObject *parent); + virtual ~QXARecordSession(); + + /* For QMediaRecorderControl begin */ + QUrl outputLocation(); + bool setOutputLocation(const QUrl &location); + QMediaRecorder::State state(); + qint64 duration(); + void applySettings(); + + void record(); + void pause(); + void stop(); + + void cbDurationChanged(TInt64 new_pos); + void cbAvailableAudioInputsChanged(); + void cbRecordingStarted(); + void cbRecordingStopped(); + /* For QMediaRecorderControl end */ + + /* For QAudioEndpointSelector begin */ + QList availableEndpoints(); + QString endpointDescription(const QString &name); + QString defaultEndpoint(); + QString activeEndpoint(); + void setActiveEndpoint(const QString &name); + /* For QAudioEndpointSelector end */ + + /* For QAudioEncoderControl begin */ + QStringList supportedAudioCodecs(); + QString codecDescription(const QString &codecName); + QList supportedSampleRates( + const QAudioEncoderSettings &settings, + bool *continuous); + QAudioEncoderSettings audioSettings(); + void setAudioSettings(const QAudioEncoderSettings &settings); + QStringList supportedEncodingOptions(const QString &codec); + QVariant encodingOption(const QString &codec, const QString &name); + void setEncodingOption(const QString &codec, const QString &name, const QVariant &value); + /* For QAudioEncoderControl end */ + + /* For QMediaContainerControl begin */ + QStringList supportedContainers(); + QString containerMimeType(); + void setContainerMimeType(const QString &formatMimeType); + QString containerDescription(const QString &formatMimeType); + /* For QMediaContainerControl end */ + +Q_SIGNALS: + void stateChanged(QMediaRecorder::State state); + void durationChanged(qint64 duration); + void error(int error, const QString &errorString); + void activeEndpointChanged(const QString& name); + void availableAudioInputsChanged(); + +private: + void setRecorderState(QMediaRecorder::State state); + void initCodecsList(); + void initContainersList(); + bool setEncoderSettingsToImpl(); + bool setURIToImpl(); + +private: + /* Own */ + XARecordSessionImpl *m_impl; + QUrl m_outputLocation; + bool m_URItoImplSet; + QMediaRecorder::State m_state; + QMediaRecorder::State m_previousState; + QStringList m_codecs; + QAudioEncoderSettings m_audioencodersettings; + QAudioEncoderSettings m_appliedaudioencodersettings; + QStringList m_containers; + QStringList m_containersDesc; + QString m_containerMimeType; +}; + +#endif /* QXARECORDSESSION_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessioncommon.h b/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessioncommon.h new file mode 100644 index 000000000..cdf8c1886 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessioncommon.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XARECORDSESSIONCOMMON_H +#define XARECORDSESSIONCOMMON_H + +#include +#include "xacommon.h" + +#define MAX_NUMBER_INTERFACES 20 +#define MAX_NUMBER_INPUT_DEVICES 10 +#define MAX_NUMBER_ENCODERS 10 + +//const TInt32 KExtErr = (TInt32)(-2147483648); +const TInt32 KExtErr = -32768; +const TInt32 KExtErrUnspecifiedCodecForContainer = (KExtErr+1); +const TInt32 KExtErrUnsupportedCodecForContainer = (KExtErr+2); +const TInt32 KExtErrUnsupportedURISuffixForContainer = (KExtErr+3); + +class XARecordObserver +{ +public: + virtual void cbDurationChanged(TInt64 new_pos) = 0; + virtual void cbAvailableAudioInputsChanged() = 0; + virtual void cbRecordingStarted() = 0; + virtual void cbRecordingStopped() = 0; +}; + +#endif /* XARECORDSESSIONCOMMON_H */ diff --git a/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.cpp b/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.cpp new file mode 100644 index 000000000..d18c781d4 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.cpp @@ -0,0 +1,1378 @@ +/**************************************************************************** + ** + ** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). + ** All rights reserved. + ** Contact: Nokia Corporation (qt-info@nokia.com) + ** + ** This file is part of the Qt Mobility Components. + ** + ** $QT_BEGIN_LICENSE:LGPL$ + ** No Commercial Usage + ** This file contains pre-release code and may not be distributed. + ** You may use this file in accordance with the terms and conditions + ** contained in the Technology Preview License Agreement accompanying + ** this package. + ** + ** GNU Lesser General Public License Usage + ** Alternatively, this file may be used under the terms of the GNU Lesser + ** General Public License version 2.1 as published by the Free Software + ** Foundation and appearing in the file LICENSE.LGPL included in the + ** packaging of this file. Please review the following information to + ** ensure the GNU Lesser General Public License version 2.1 requirements + ** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. + ** + ** In addition, as a special exception, Nokia gives you certain additional + ** rights. These rights are described in the Nokia Qt LGPL Exception + ** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. + ** + ** If you have questions regarding the use of this file, please contact + ** Nokia at qt-info@nokia.com. + ** + ** + ** + ** + ** + ** + ** + ** + ** $QT_END_LICENSE$ + ** + ****************************************************************************/ +#include "xarecordsessionimpl.h" +#include "xarecordsessioncommon.h" +_LIT8(K8WAVMIMETYPE, "audio/x-wav"); +/* + * These codec names are not part of AL. Hence we need to define names here. + * */ +_LIT(KAUDIOCODECPCM, "pcm"); +_LIT(KAUDIOCODECAMR, "amr"); +_LIT(KAUDIOCODECAAC, "aac"); +_LIT(KCONTAINERWAV, "audio/wav"); +_LIT(KCONTAINERWAVDESC, "wav container"); +_LIT(KCONTAINERAMR, "audio/amr"); +_LIT(KCONTAINERAMRDESC, "amr File format"); +_LIT(KCONTAINERMP4, "audio/mpeg"); +_LIT(KCONTAINERMP4DESC, "mpeg container"); + +const TUint KRecordPosUpdatePeriod = 1000; +const TUint KMilliToHz = 1000; +const TUint KMaxNameLength = 256; + +/* Local functions for callback registation */ +void cbXAObjectItf( + XAObjectItf caller, + const void *pContext, + XAuint32 event, + XAresult result, + XAuint32 param, + void *pInterface); + +void cbXARecordItf( + XARecordItf caller, + void *pContext, + XAuint32 event); + +void cbXAAvailableAudioInputsChanged( + XAAudioIODeviceCapabilitiesItf caller, + void *pContext, + XAuint32 deviceID, + XAint32 numInputs, + XAboolean isNew); + +XARecordSessionImpl::XARecordSessionImpl(XARecordObserver &parent) : + m_Parent(parent), + m_EOEngine(NULL), + m_MORecorder(NULL), + m_RecordItf(NULL), + m_AudioEncItf(NULL), + m_WAVMime(NULL), + m_URIName(NULL), + m_InputDeviceId(0), + m_ContainerType(0), + m_BitRate(0), + m_RateControl(0), + m_ChannelsOut(1), + m_SampleRate(0), + m_AudioIODevCapsItf(NULL), + m_AudioInputDeviceNames(NULL), + m_DefaultAudioInputDeviceNames(NULL), + m_AudioEncCapsItf(NULL) +{ + TRACE_FUNCTION_ENTRY_EXIT; +} + +XARecordSessionImpl::~XARecordSessionImpl() +{ + TRACE_FUNCTION_ENTRY; + + if (m_MORecorder) + (*m_MORecorder)->Destroy(m_MORecorder); + + if (m_EOEngine) + (*m_EOEngine)->Destroy(m_EOEngine); + + delete m_WAVMime; + delete m_URIName; + + m_InputDeviceIDs.Close(); + if (m_AudioInputDeviceNames) + m_AudioInputDeviceNames->Reset(); + delete m_AudioInputDeviceNames; + m_DefaultInputDeviceIDs.Close(); + if (m_DefaultAudioInputDeviceNames) + m_DefaultAudioInputDeviceNames->Reset(); + delete m_DefaultAudioInputDeviceNames; + m_EncoderIds.Close(); + m_EncoderNames.Close(); + m_ContainerNames.Close(); + m_ContainerDescs.Close(); + + TRACE_FUNCTION_EXIT; +} + +TInt32 XARecordSessionImpl::postConstruct() +{ + TRACE_FUNCTION_ENTRY; + + XAEngineOption engineOption[] = { (XAuint32) XA_ENGINEOPTION_THREADSAFE, (XAuint32) XA_BOOLEAN_TRUE}; + + /* Create and realize Engine object */ + TRACE_LOG(_L("XARecordSessionImpl: Creating Engine...")); + XAresult xa_result = xaCreateEngine(&m_EOEngine, 1, engineOption, 0, NULL, NULL); + TInt returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + TRACE_LOG(_L("XARecordSessionImpl: Realizing engine...")); + xa_result = (*m_EOEngine)->Realize(m_EOEngine, XA_BOOLEAN_FALSE); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + TRACE_LOG(_L("XARecordSessionImpl: OMX AL Engine realized successfully")); + + XAEngineItf engineItf; + xa_result = (*m_EOEngine)->GetInterface(m_EOEngine, XA_IID_ENGINE, (void**) &engineItf); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + xa_result = (*m_EOEngine)->GetInterface(m_EOEngine, + XA_IID_AUDIOIODEVICECAPABILITIES, + (void**) &m_AudioIODevCapsItf); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + xa_result = (*m_AudioIODevCapsItf)->RegisterAvailableAudioInputsChangedCallback( + m_AudioIODevCapsItf, + cbXAAvailableAudioInputsChanged, + (void*)this); + + xa_result = (*m_EOEngine)->GetInterface( + m_EOEngine, + XA_IID_AUDIOENCODERCAPABILITIES, + (void**) &m_AudioEncCapsItf); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRAP(returnValue, m_WAVMime = HBufC8::NewL(K8WAVMIMETYPE().Length() + 1)); + RET_ERR_IF_ERR(returnValue); + TPtr8 ptr = m_WAVMime->Des(); + ptr = K8WAVMIMETYPE(); // copy uri name into local variable + ptr.PtrZ(); // append zero terminator to end of URI + + m_AudioInputDeviceNames = new CDesC16ArrayFlat(2); + if (m_AudioInputDeviceNames == NULL) + returnValue = KErrNoMemory; + RET_ERR_IF_ERR(returnValue); + + m_DefaultAudioInputDeviceNames = new CDesC16ArrayFlat(2); + if (m_DefaultAudioInputDeviceNames == NULL) + returnValue = KErrNoMemory; + RET_ERR_IF_ERR(returnValue); + + returnValue = initContainersList(); + RET_ERR_IF_ERR(returnValue); + returnValue = initAudioEncodersList(); + RET_ERR_IF_ERR(returnValue); + returnValue = initAudioInputDevicesList(); + RET_ERR_IF_ERR(returnValue); + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::setURI(const TDesC &aURI) +{ + TRACE_FUNCTION_ENTRY; + + /* This function will only get called when aURI is different than m_URIName + * and only when recorder is in stopped state. + * If the recorder object was created for a different URI (than aURI), we + * need to tear it down here. + */ + if (m_MORecorder) { + (*m_MORecorder)->Destroy(m_MORecorder); + m_MORecorder = NULL; + m_RecordItf = NULL; + } + + delete m_URIName; + m_URIName = NULL; + TRAPD(returnValue, m_URIName = HBufC8::NewL(aURI.Length()+1)); + RET_ERR_IF_ERR(returnValue); + + TPtr8 uriPtr = m_URIName->Des(); + /* copy uri name into local variable */ + uriPtr.Copy(aURI); + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::record() +{ + TRACE_FUNCTION_ENTRY; + + TInt32 returnValue(KErrGeneral); + if (!m_MORecorder || !m_RecordItf) { + TRACE_LOG(_L("XARecordSessionImpl::Record: MORecorder/RecordItf is not created")); + returnValue = createMediaRecorderObject(); + RET_ERR_IF_ERR(returnValue); + + returnValue = setEncoderSettingsToMediaRecorder(); + RET_ERR_IF_ERR(returnValue); + } + + XAuint32 state; + XAresult xa_result = (*m_RecordItf)->GetRecordState(m_RecordItf, &state); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + if ((state == XA_RECORDSTATE_STOPPED) + || (state == XA_RECORDSTATE_PAUSED)) { + TRACE_LOG(_L("XARecordSessionImpl::Record: Setting State to Recording...")); + xa_result = (*m_RecordItf)->SetRecordState(m_RecordItf, XA_RECORDSTATE_RECORDING); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + TRACE_LOG(_L("XARecordSessionImpl::Record: SetState to Recording")); + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::pause() +{ + TRACE_FUNCTION_ENTRY; + + TInt32 returnValue(KErrGeneral); + if (!m_MORecorder || !m_RecordItf) { + TRACE_LOG(_L("XARecordSessionImpl::Record: MORecorder/RecordItf is not created")); + return returnValue; + } + + XAuint32 state; + XAresult xa_result = (*m_RecordItf)->GetRecordState(m_RecordItf, &state); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + if ((state == XA_RECORDSTATE_STOPPED) + || (state == XA_RECORDSTATE_RECORDING)) { + TRACE_LOG(_L("XARecordSessionImpl::Record: Setting State to Paused...")); + xa_result = (*m_RecordItf)->SetRecordState(m_RecordItf, XA_RECORDSTATE_PAUSED); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + TRACE_LOG(_L("XARecordSessionImpl::Record: SetState to Paused")); + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::stop() +{ + TRACE_FUNCTION_ENTRY; + + TInt32 returnValue(KErrGeneral); + if (!m_MORecorder || !m_RecordItf) { + TRACE_LOG(_L("XARecordSessionImpl::Record: MORecorder/RecordItf is not created")); + return returnValue; + } + + XAuint32 state; + XAresult xa_result = (*m_RecordItf)->GetRecordState(m_RecordItf, &state); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + if ((state == XA_RECORDSTATE_PAUSED) + || (state == XA_RECORDSTATE_RECORDING)) { + TRACE_LOG(_L("XARecordSessionImpl::Record: Setting State to Stopped...")); + xa_result = (*m_RecordItf)->SetRecordState(m_RecordItf, XA_RECORDSTATE_STOPPED); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + TRACE_LOG(_L("XARecordSessionImpl::Record: SetState to Stopped")); + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::duration(TInt64 &aDur) +{ + TRACE_FUNCTION_ENTRY; + + TInt32 returnValue(KErrGeneral); + + if (!m_MORecorder || !m_RecordItf) { + TRACE_LOG(_L("XARecordSessionImpl::Duration: MORecoder/RecordItf is not created")); + return returnValue; + } + + XAmillisecond milliSec; + XAresult xa_result = (*m_RecordItf)->GetPosition(m_RecordItf, &milliSec); + returnValue = mapError(xa_result, ETrue); + if (returnValue == KErrNone) + aDur = (TInt64)milliSec; + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +void XARecordSessionImpl::cbMediaRecorder( + XAObjectItf /*caller*/, + const void */*pContext*/, + XAuint32 event, + XAresult result, + XAuint32 /*param*/, + void */*pInterface*/) +{ + TRACE_FUNCTION_ENTRY; + + switch (event) { + case XA_OBJECT_EVENT_RESOURCES_LOST: + m_Parent.cbRecordingStopped(); + break; + case XA_OBJECT_EVENT_RUNTIME_ERROR: { + switch (result) { + case XA_RESULT_RESOURCE_LOST: + m_Parent.cbRecordingStopped(); + break; + default: + break; + }; /* of switch (result) */ + } + default: + break; + } /* of switch (event) */ + + TRACE_FUNCTION_EXIT; +} + +void XARecordSessionImpl::cbRecordItf( + XARecordItf /*caller*/, + void */*pContext*/, + XAuint32 event) +{ + TRACE_FUNCTION_ENTRY; + + switch(event) { + case XA_RECORDEVENT_HEADATLIMIT: + TRACE_LOG(_L("XA_RECORDEVENT_HEADATLIMIT")); + break; + case XA_RECORDEVENT_HEADATMARKER: + TRACE_LOG(_L("XA_RECORDEVENT_HEADATMARKER")); + break; + case XA_RECORDEVENT_HEADATNEWPOS: { + TInt32 returnValue; + XAresult xa_result; + XAmillisecond milliSec; + xa_result = (*m_RecordItf)->GetPosition(m_RecordItf, &milliSec); + returnValue = mapError(xa_result, ETrue); + if (returnValue == KErrNone) + m_Parent.cbDurationChanged((TInt64) milliSec); + } + break; + case XA_RECORDEVENT_HEADMOVING: + TRACE_LOG(_L("XA_RECORDEVENT_HEADMOVING")); + m_Parent.cbRecordingStarted(); + break; + case XA_RECORDEVENT_HEADSTALLED: + TRACE_LOG(_L("XA_RECORDEVENT_HEADSTALLED")); + break; + case XA_RECORDEVENT_BUFFER_FULL: + TRACE_LOG(_L("XA_RECORDEVENT_BUFFER_FULL")); + break; + default: + TRACE_LOG(_L("UNKNOWN RECORDEVENT EVENT")); + break; + } /* of switch(event) */ + + TRACE_FUNCTION_EXIT; +} + +/* For QAudioEndpointSelector begin */ +void XARecordSessionImpl::getAudioInputDeviceNames(RArray &aArray) +{ + TRACE_FUNCTION_ENTRY; + + for (TInt index = 0; index < m_AudioInputDeviceNames->MdcaCount(); index++) + aArray.Append(m_AudioInputDeviceNames->MdcaPoint(index)); + TRACE_FUNCTION_EXIT; +} + +TInt32 XARecordSessionImpl::defaultAudioInputDevice(TPtrC &endPoint) +{ + TRACE_FUNCTION_ENTRY; + + TInt32 err(KErrGeneral); + if (m_DefaultAudioInputDeviceNames->MdcaCount() >= 0) { + endPoint.Set(m_DefaultAudioInputDeviceNames->MdcaPoint(0)); + err = KErrNone; + } + + TRACE_FUNCTION_EXIT; + return err; +} + +TInt32 XARecordSessionImpl::activeAudioInputDevice(TPtrC &endPoint) +{ + TRACE_FUNCTION_ENTRY; + + TInt32 returnValue(KErrGeneral); + TBool found(EFalse); + TInt index = 0; + for (; index < m_InputDeviceIDs.Count(); index++) { + if (m_InputDeviceIDs[index] == m_InputDeviceId) { + found = ETrue; + break; + } + } + + /* Comparing found with ETrue produces linker error */ + if (found == true) { + endPoint.Set(m_AudioInputDeviceNames->MdcaPoint(index)); + returnValue = KErrNone; + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TBool XARecordSessionImpl::setAudioInputDevice(const TDesC &aDevice) +{ + TRACE_FUNCTION_ENTRY; + + /* validate if we can set input device id */ + TBool found(EFalse); + m_InputDeviceId = 0; + TInt index = 0; + for (; index < m_AudioInputDeviceNames->MdcaCount(); index++) { + if (m_AudioInputDeviceNames->MdcaPoint(index).Compare(aDevice) == 0) { + found = ETrue; + break; + } + } + if (found == true) { + m_InputDeviceId = m_InputDeviceIDs[index]; + } + + TRACE_FUNCTION_EXIT; + return found; +} + +void XARecordSessionImpl::cbAvailableAudioInputsChanged( + XAAudioIODeviceCapabilitiesItf /*caller*/, + void */*pContext*/, + XAuint32 deviceID, + XAint32 /*numInputs*/, + XAboolean isNew) +{ + TRACE_FUNCTION_ENTRY; + + /* If a new device is added into the system, append it to available input list */ + if (isNew == XA_BOOLEAN_TRUE) { + XAAudioInputDescriptor audioInputDescriptor; + m_InputDeviceIDs.Append(deviceID); + + XAresult xa_result = (*m_AudioIODevCapsItf)->QueryAudioInputCapabilities( + m_AudioIODevCapsItf, + deviceID, + &audioInputDescriptor); + + if ((mapError(xa_result, ETrue)) == KErrNone) { + TUint8* inDevNamePtr = audioInputDescriptor.deviceName; + TUint8* tempPtr = audioInputDescriptor.deviceName; + TInt32 inDevNameLength = 0; + while (*tempPtr++) + inDevNameLength++; + TPtrC8 ptr(inDevNamePtr, inDevNameLength); + /* Convert 8 bit to 16 bit */ + TBuf16 name; + name.Copy(ptr); + /* Using TRAP with returnValue results in compiler error */ + TRAP_IGNORE(m_AudioInputDeviceNames->AppendL(name)); + } + } + else { + /* an available device has been removed from the the system, remove it from + * available input list and also default list */ + TBool found(EFalse); + TInt index = 0; + for (; index < m_InputDeviceIDs.Count(); index++) { + if (deviceID == m_InputDeviceIDs[index]) { + found = ETrue; + break; + } + } + if (found == true) { + m_InputDeviceIDs.Remove(index); + m_AudioInputDeviceNames->Delete(index); + } + if (deviceID == m_InputDeviceId) + m_InputDeviceId = 0; + + found = EFalse; + for (index = 0; index < m_DefaultInputDeviceIDs.Count(); index++) { + if (deviceID == m_DefaultInputDeviceIDs[index]) { + found = ETrue; + break; + } + } + if (found == true) { + m_DefaultInputDeviceIDs.Remove(index); + m_DefaultAudioInputDeviceNames->Delete(index); + } + } + m_Parent.cbAvailableAudioInputsChanged(); + + TRACE_FUNCTION_EXIT; +} +/* For QAudioEndpointSelector end */ + +/* For QAudioEncoderControl begin */ +const RArray& XARecordSessionImpl::getAudioEncoderNames() +{ + TRACE_FUNCTION_ENTRY_EXIT; + return m_EncoderNames; +} + +TInt32 XARecordSessionImpl::getSampleRates( + const TDesC& aEncoder, + RArray &aSampleRates, + TBool &aIsContinuous) +{ + TRACE_FUNCTION_ENTRY; + + aSampleRates.Reset(); + aIsContinuous = EFalse; + + XAuint32 encoderId = 0; + TBool found(EFalse); + for (TInt index = 0; index < m_EncoderIds.Count(); index++) { + if (m_EncoderNames[index].Compare(aEncoder) == 0) { + found = ETrue; + encoderId = m_EncoderIds[index]; + break; + } + } + + TInt32 returnValue(KErrGeneral); + if (found == false) + return returnValue; + + returnValue = getSampleRatesByAudioCodecID(encoderId, aSampleRates); + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::getBitrates( + const TDesC& aEncoder, + RArray &aBitrates, + TBool& aContinuous) +{ + TRACE_FUNCTION_ENTRY; + + aBitrates.Reset(); + + XAuint32 encoderId = 0; + TBool found(EFalse); + for (TInt index = 0; index < m_EncoderIds.Count(); index++) { + if (m_EncoderNames[index].Compare(aEncoder) == 0) { + found = ETrue; + encoderId = m_EncoderIds[index]; + break; + } + } + + TInt32 returnValue(KErrNotSupported); + XAboolean cont; + if (found == false) + return returnValue; + + returnValue = getBitratesByAudioCodecID(encoderId, aBitrates, cont); + aContinuous = cont; + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +/* For QAudioEncoderControl end */ + +/* For QMediaContainerControl begin */ +const RArray& XARecordSessionImpl::getContainerNames() +{ + TRACE_FUNCTION_ENTRY_EXIT; + return m_ContainerNames; +} + +const RArray& XARecordSessionImpl::getContainerDescs() +{ + TRACE_FUNCTION_ENTRY_EXIT; + return m_ContainerDescs; +} + +/* For QMediaContainerControl end */ + +void XARecordSessionImpl::resetEncoderAttributes() +{ + m_ContainerType = 0; + m_AudioEncoderId = 0; + m_ProfileSetting = 0; + m_BitRate = 0; + m_ChannelsOut = 1; + m_SampleRate = 0; + m_RateControl = 0; +} + +void XARecordSessionImpl::setContainerType(const TDesC &aURI) +{ + TRACE_FUNCTION_ENTRY; + + if (aURI.Compare(KCONTAINERWAV()) == 0) + m_ContainerType = XA_CONTAINERTYPE_WAV; + else if (aURI.Compare(KCONTAINERAMR()) == 0) + m_ContainerType = XA_CONTAINERTYPE_AMR; + else if (aURI.Compare(KCONTAINERMP4()) == 0) + m_ContainerType = XA_CONTAINERTYPE_MP4; + + TRACE_FUNCTION_EXIT; +} + +TBool XARecordSessionImpl::setCodec(const TDesC &aCodec) +{ + TRACE_FUNCTION_ENTRY; + + TBool returnValue(EFalse); + if (aCodec.Compare(KAUDIOCODECPCM()) == 0) { + m_AudioEncoderId = XA_AUDIOCODEC_PCM; + m_ProfileSetting = XA_AUDIOPROFILE_PCM; + returnValue = ETrue; + } + else if (aCodec.Compare(KAUDIOCODECAAC()) == 0) { + m_AudioEncoderId = XA_AUDIOCODEC_AAC; + m_ProfileSetting = XA_AUDIOPROFILE_AAC_AAC; + returnValue = ETrue; + } + else if (aCodec.Compare(KAUDIOCODECAMR()) == 0) { + m_AudioEncoderId = XA_AUDIOCODEC_AMR; + m_ProfileSetting = XA_AUDIOPROFILE_AMR; + returnValue = ETrue; + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TUint32 XARecordSessionImpl::getBitRate() +{ + return m_BitRate; +} + +void XARecordSessionImpl::setBitRate(TUint32 aBitRate) +{ + TRACE_FUNCTION_ENTRY; + RArray bitrates; + XAboolean isContinuous; + m_BitRate = 0; + if (getBitratesByAudioCodecID(m_AudioEncoderId, bitrates, isContinuous) == KErrNone) { + bitrates.SortUnsigned(); + TInt loopIndex(0); + while (loopIndex < bitrates.Count() + && aBitRate <= bitrates[loopIndex]) { + m_BitRate = bitrates[loopIndex]; + loopIndex++; + } + bitrates.Close(); + } + TRACE_LOG((_L("BitRate[%d]"), m_BitRate)); + TRACE_FUNCTION_EXIT; +} + +TUint32 XARecordSessionImpl::getChannels() +{ + return m_ChannelsOut; +} + +void XARecordSessionImpl::setChannels(TUint32 aChannels) +{ + TRACE_FUNCTION_ENTRY; + switch (m_AudioEncoderId) { + case XA_AUDIOCODEC_PCM: + case XA_AUDIOCODEC_AAC: + m_ChannelsOut = 1; + if ((aChannels >= 1) && (aChannels <= 2)) + m_ChannelsOut = aChannels; + break; + case XA_AUDIOCODEC_AMR: + m_ChannelsOut = 1; + break; + default: + break; + } + TRACE_LOG((_L("ChannelCount[%d]"), m_ChannelsOut)); + TRACE_FUNCTION_EXIT; +} + +void XARecordSessionImpl::setOptimalChannelCount() +{ + TRACE_FUNCTION_ENTRY; + m_ChannelsOut = 1; + TRACE_FUNCTION_EXIT; +} + +TUint32 XARecordSessionImpl::getSampleRate() +{ + return m_SampleRate; +} + +void XARecordSessionImpl::setSampleRate(TUint32 aSampleRate) +{ + TRACE_FUNCTION_ENTRY; + + m_SampleRate = 0; + + RArray samplerates; + if (getSampleRatesByAudioCodecID(m_AudioEncoderId, samplerates) == KErrNone) { + samplerates.SortUnsigned(); + TInt loopIndex(0); + while (loopIndex < samplerates.Count()) { + m_SampleRate = samplerates[loopIndex]; + if (samplerates[loopIndex] > aSampleRate) + break; + loopIndex++; + } + samplerates.Close(); + } + + /* convert Hz to MilliHz */ + m_SampleRate *= KMilliToHz; + TRACE_LOG((_L("SampleRate[%d]"), m_SampleRate)); + TRACE_FUNCTION_EXIT; +} + +void XARecordSessionImpl::setOptimalSampleRate() +{ + TRACE_FUNCTION_ENTRY; + m_SampleRate = 0; + + if (m_AudioEncoderId == XA_AUDIOCODEC_AAC) { + m_SampleRate = 32000 * KMilliToHz; + } + else if (m_AudioEncoderId == XA_AUDIOCODEC_AMR) { + m_SampleRate = 8000 * KMilliToHz; + } + else { + RArray sampleRates; + TInt res = getSampleRatesByAudioCodecID(m_AudioEncoderId, sampleRates); + if ((res == KErrNone) && (sampleRates.Count() > 0)) { + /* Sort the array and pick the middle range sample rate */ + sampleRates.SortUnsigned(); + m_SampleRate = sampleRates[sampleRates.Count() / 2] * KMilliToHz; + } + sampleRates.Close(); + } + + TRACE_FUNCTION_EXIT; +} + +TInt32 XARecordSessionImpl::setCBRMode() +{ + TRACE_FUNCTION_ENTRY; + + m_RateControl = XA_RATECONTROLMODE_CONSTANTBITRATE; + + TRACE_FUNCTION_EXIT; + return KErrNone; +} + +TInt32 XARecordSessionImpl::setVBRMode() +{ + TRACE_FUNCTION_ENTRY; + + m_RateControl = XA_RATECONTROLMODE_VARIABLEBITRATE; + + TRACE_FUNCTION_EXIT; + return KErrNone; +} + +void XARecordSessionImpl::setVeryLowQuality() +{ + /* Set to very low quality encoder preset */ + RArray bitrates; + XAboolean continuous; + TInt res = getBitratesByAudioCodecID(m_AudioEncoderId, bitrates, continuous); + if ((res == KErrNone) && (bitrates.Count() > 0)) { + /* Sort the array and pick the lowest bit rate */ + bitrates.SortUnsigned(); + m_BitRate = bitrates[0]; + } + bitrates.Close(); +} + +void XARecordSessionImpl::setLowQuality() +{ + /* Set to low quality encoder preset */ + RArray bitrates; + XAboolean continuous; + TInt res = getBitratesByAudioCodecID(m_AudioEncoderId, bitrates, continuous); + if ((res == KErrNone) && (bitrates.Count() > 0)) { + /* Sort the array and pick the low quality bit rate */ + bitrates.SortUnsigned(); + if (continuous == XA_BOOLEAN_FALSE) + m_BitRate = bitrates[bitrates.Count() / 4]; + else + m_BitRate = (bitrates[1] - bitrates[0]) / 4; + } + bitrates.Close(); +} + +void XARecordSessionImpl::setNormalQuality() +{ + /* Set to normal quality encoder preset */ + RArray bitrates; + XAboolean continuous; + TInt res = getBitratesByAudioCodecID(m_AudioEncoderId, bitrates, continuous); + if ((res == KErrNone) && (bitrates.Count() > 0)) { + /* Sort the array and pick the middle range bit rate */ + bitrates.SortUnsigned(); + if (continuous == XA_BOOLEAN_FALSE) + m_BitRate = bitrates[bitrates.Count() / 2]; + else + m_BitRate = (bitrates[1] - bitrates[0]) / 2; + } + bitrates.Close(); +} + +void XARecordSessionImpl::setHighQuality() +{ + /* Set to high quality encoder preset */ + RArray bitrates; + XAboolean continuous; + TInt res = getBitratesByAudioCodecID(m_AudioEncoderId, bitrates, continuous); + if ((res == KErrNone) && (bitrates.Count() > 0)) { + /* Sort the array and pick the high quality bit rate */ + bitrates.SortUnsigned(); + if (continuous == XA_BOOLEAN_FALSE) + m_BitRate = bitrates[bitrates.Count() * 3 / 4]; + else + m_BitRate = (bitrates[1] - bitrates[0]) * 3 / 4; + } + bitrates.Close(); +} + +void XARecordSessionImpl::setVeryHighQuality() +{ + /* Set to very high quality encoder preset */ + RArray bitrates; + XAboolean continuous; + TInt res = getBitratesByAudioCodecID(m_AudioEncoderId, bitrates, continuous); + if ((res == KErrNone) && (bitrates.Count() > 0)) { + /* Sort the array and pick the highest bit rate */ + bitrates.SortUnsigned(); + m_BitRate = bitrates[bitrates.Count() - 1]; + } + bitrates.Close(); +} + +/* Internal function */ +TInt32 XARecordSessionImpl::createMediaRecorderObject() +{ + TRACE_FUNCTION_ENTRY; + + if (!m_EOEngine) + return KErrGeneral; + + TInt32 returnValue(KErrNone); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject")); + if (!m_MORecorder && !m_RecordItf) { + + /* Setup the data source */ + m_LocatorMic.locatorType = XA_DATALOCATOR_IODEVICE; + m_LocatorMic.deviceType = XA_IODEVICE_AUDIOINPUT; + m_LocatorMic.deviceID = m_InputDeviceId; + m_LocatorMic.device = NULL; + m_DataSource.pLocator = (void*) &m_LocatorMic; + m_DataSource.pFormat = NULL; + + /* Setup the data sink structure */ + m_Uri.locatorType = XA_DATALOCATOR_URI; + /* append zero terminator to end of URI */ + TPtr8 uriPtr = m_URIName->Des(); + m_Uri.URI = (XAchar*) uriPtr.PtrZ(); + m_Mime.formatType = XA_DATAFORMAT_MIME; + m_Mime.containerType = m_ContainerType; + TPtr8 mimeTypePtr(m_WAVMime->Des()); + m_Mime.mimeType = (XAchar*) mimeTypePtr.Ptr(); + m_DataSink.pLocator = (void*) &m_Uri; + m_DataSink.pFormat = (void*) &m_Mime; + + /* Init arrays required[] and iidArray[] */ + XAboolean required[MAX_NUMBER_INTERFACES]; + XAInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + for (TInt32 i = 0; i < MAX_NUMBER_INTERFACES; i++) { + required[i] = XA_BOOLEAN_FALSE; + iidArray[i] = XA_IID_NULL; + } + XAuint32 noOfInterfaces = 0; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_RECORD; + noOfInterfaces++; + required[noOfInterfaces] = XA_BOOLEAN_FALSE; + iidArray[noOfInterfaces] = XA_IID_AUDIOENCODER; + noOfInterfaces++; + + XAEngineItf engineItf; + XAresult xa_result = (*m_EOEngine)->GetInterface(m_EOEngine, XA_IID_ENGINE, (void**) &engineItf); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: Create Media Recorder...")); + + /* Create recorder with NULL for a the image/video source, since this is for audio-only recording */ + xa_result = (*engineItf)->CreateMediaRecorder( + engineItf, + &m_MORecorder, + &m_DataSource, + NULL, + &m_DataSink, + noOfInterfaces, + iidArray, + required); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: Realize Media Recorder...")); + xa_result = (*m_MORecorder)->Realize(m_MORecorder, XA_BOOLEAN_FALSE); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: Register Callback on recorder...")); + xa_result = (*m_MORecorder)->RegisterCallback(m_MORecorder, cbXAObjectItf, (void*) this); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: Getting Record Interface...")); + xa_result = (*m_MORecorder)->GetInterface(m_MORecorder, XA_IID_RECORD, &m_RecordItf); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: Registering Callback on record Interface...")); + xa_result = (*m_RecordItf)->RegisterCallback(m_RecordItf, cbXARecordItf, (void*) this); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: SetPositionUpdatePeriod on record Interface...")); + xa_result = (*m_RecordItf)->SetPositionUpdatePeriod(m_RecordItf, (XAmillisecond)KRecordPosUpdatePeriod); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: SetCallbackEventsMask on record Interface...")); + xa_result = (*m_RecordItf)->SetCallbackEventsMask(m_RecordItf, XA_RECORDEVENT_HEADATNEWPOS | + XA_RECORDEVENT_HEADMOVING | + XA_RECORDEVENT_HEADSTALLED); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + TRACE_LOG(_L("XARecordSessionImpl::CreateMediaRecorderObject: Getting Audio Encoder Interface...")); + xa_result = (*m_MORecorder)->GetInterface(m_MORecorder, XA_IID_AUDIOENCODER, &m_AudioEncItf); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::mapError(XAresult xa_err, TBool debPrn) +{ + TInt32 returnValue(KErrGeneral); + switch (xa_err) { + case XA_RESULT_SUCCESS: + returnValue = KErrNone; + break; + case XA_RESULT_PRECONDITIONS_VIOLATED: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_PRECONDITIONS_VIOLATED")); + break; + case XA_RESULT_PARAMETER_INVALID: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_PARAMETER_INVALID")); + break; + case XA_RESULT_MEMORY_FAILURE: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_MEMORY_FAILURE")); + break; + case XA_RESULT_RESOURCE_ERROR: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_RESOURCE_ERROR")); + break; + case XA_RESULT_RESOURCE_LOST: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_RESOURCE_LOST")); + break; + case XA_RESULT_IO_ERROR: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_IO_ERROR")); + break; + case XA_RESULT_BUFFER_INSUFFICIENT: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_BUFFER_INSUFFICIENT")); + break; + case XA_RESULT_CONTENT_CORRUPTED: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_CONTENT_CORRUPTED")); + break; + case XA_RESULT_CONTENT_UNSUPPORTED: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_CONTENT_UNSUPPORTED")); + break; + case XA_RESULT_CONTENT_NOT_FOUND: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_CONTENT_NOT_FOUND")); + break; + case XA_RESULT_PERMISSION_DENIED: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_PERMISSION_DENIED")); + break; + case XA_RESULT_FEATURE_UNSUPPORTED: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_FEATURE_UNSUPPORTED")); + break; + case XA_RESULT_INTERNAL_ERROR: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_INTERNAL_ERROR")); + break; + case XA_RESULT_UNKNOWN_ERROR: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_UNKNOWN_ERROR")); + break; + case XA_RESULT_OPERATION_ABORTED: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_OPERATION_ABORTED")); + break; + case XA_RESULT_CONTROL_LOST: + if (debPrn) + TRACE_LOG(_L("XA_RESULT_CONTROL_LOST")); + break; + default: + if (debPrn) + TRACE_LOG(_L("Unknown Error!!!")); + break; + } + return returnValue; +} + +TInt32 XARecordSessionImpl::initContainersList() +{ + TRACE_FUNCTION_ENTRY; + + m_ContainerNames.Reset(); + m_ContainerDescs.Reset(); + + m_ContainerNames.Append(KCONTAINERWAV()); + m_ContainerNames.Append(KCONTAINERAMR()); + m_ContainerNames.Append(KCONTAINERMP4()); + + m_ContainerDescs.Append(KCONTAINERWAVDESC()); + m_ContainerDescs.Append(KCONTAINERAMRDESC()); + m_ContainerDescs.Append(KCONTAINERMP4DESC()); + + TRACE_FUNCTION_EXIT; + return KErrNone; +} + +TInt32 XARecordSessionImpl::initAudioEncodersList() +{ + TRACE_FUNCTION_ENTRY; + + m_EncoderIds.Reset(); + m_EncoderNames.Reset(); + + XAuint32 encoderIds[MAX_NUMBER_ENCODERS]; + + for (TInt index = 0; index < MAX_NUMBER_ENCODERS; index++) + encoderIds[index] = 0; + + XAuint32 numEncoders = MAX_NUMBER_ENCODERS; + XAresult xa_result = (*m_AudioEncCapsItf)->GetAudioEncoders( + m_AudioEncCapsItf, + &numEncoders, + encoderIds); + TInt32 returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + for (TInt index = 0; index < numEncoders; index++) { + m_EncoderIds.Append(encoderIds[index]); + switch (encoderIds[index]) { + case XA_AUDIOCODEC_PCM: + m_EncoderNames.Append(KAUDIOCODECPCM()); + break; + case XA_AUDIOCODEC_AMR: + m_EncoderNames.Append(KAUDIOCODECAMR()); + break; + case XA_AUDIOCODEC_AAC: + m_EncoderNames.Append(KAUDIOCODECAAC()); + break; + default: + break; + }; + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::initAudioInputDevicesList() +{ + TRACE_FUNCTION_ENTRY; + + m_InputDeviceIDs.Reset(); + + XAuint32 deviceIds[MAX_NUMBER_INPUT_DEVICES]; + for (TInt index = 0; index < MAX_NUMBER_INPUT_DEVICES; index++) + deviceIds[index] = 0; + + XAint32 numInputs = MAX_NUMBER_INPUT_DEVICES; + XAresult xa_result = (*m_AudioIODevCapsItf)->GetAvailableAudioInputs( + m_AudioIODevCapsItf, + &numInputs, + deviceIds); + TInt32 returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + XAAudioInputDescriptor audioInputDescriptor; + for (TInt index = 0; index < numInputs; index++) { + xa_result = (*m_AudioIODevCapsItf)->QueryAudioInputCapabilities( + m_AudioIODevCapsItf, + deviceIds[index], + &audioInputDescriptor); + returnValue = mapError(xa_result, ETrue); + if (returnValue != KErrNone) + continue; + + TUint8 * inDevNamePtr = audioInputDescriptor.deviceName; + TUint8 * tempPtr = audioInputDescriptor.deviceName; + TInt32 inDevNameLength = 0; + while (*tempPtr++) + inDevNameLength++; + TPtrC8 ptr(inDevNamePtr, inDevNameLength); + /* Convert 8 bit to 16 bit */ + TBuf16 name; + name.Copy(ptr); + /* Using TRAP with returnValue results in compiler error */ + TRAPD(err2, m_AudioInputDeviceNames->AppendL(name)); + returnValue = err2; + if (returnValue != KErrNone) + continue; + m_InputDeviceIDs.Append(deviceIds[index]); + } + + numInputs = MAX_NUMBER_INPUT_DEVICES; + for (TInt index = 0; index < MAX_NUMBER_INPUT_DEVICES; index++) + deviceIds[index] = 0; + xa_result = (*m_AudioIODevCapsItf)->GetDefaultAudioDevices( + m_AudioIODevCapsItf, + XA_DEFAULTDEVICEID_AUDIOINPUT, + &numInputs, + deviceIds); + returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + for (TInt index = 0; index < numInputs; index++) { + xa_result = (*m_AudioIODevCapsItf)->QueryAudioInputCapabilities( + m_AudioIODevCapsItf, + deviceIds[index], + &audioInputDescriptor); + returnValue = mapError(xa_result, ETrue); + if (returnValue != KErrNone) + continue; + TUint8* inDevNamePtr = audioInputDescriptor.deviceName; + TUint8* tempPtr = audioInputDescriptor.deviceName; + TInt32 inDevNameLength = 0; + while (*tempPtr++) + inDevNameLength++; + TPtrC8 ptr(inDevNamePtr, inDevNameLength); + /* Convert 8 bit to 16 bit */ + TBuf16 name; + name.Copy(ptr); + /* Using TRAP with returnValue results in compiler error */ + TRAPD(err2, m_DefaultAudioInputDeviceNames->AppendL(name)); + returnValue = err2; + if (returnValue != KErrNone) + continue; + m_DefaultInputDeviceIDs.Append(deviceIds[index]); + m_InputDeviceId = deviceIds[index]; + } + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::setEncoderSettingsToMediaRecorder() +{ + TRACE_FUNCTION_EXIT; + + /* Get current settings */ + XAAudioEncoderSettings settings; + XAresult xa_result = (*m_AudioEncItf)->GetEncoderSettings( + m_AudioEncItf, + &settings); + TInt32 returnValue = mapError(xa_result, ETrue); + + settings.encoderId = m_AudioEncoderId; + settings.channelsOut = m_ChannelsOut; + if ((m_SampleRate != 0) && (m_SampleRate != 0xffffffff)) + settings.sampleRate = m_SampleRate; + if ((m_BitRate != 0) && (m_BitRate != 0xffffffff)) + settings.bitRate = m_BitRate; + if (m_RateControl != 0) + settings.rateControl = m_RateControl; + settings.profileSetting = m_ProfileSetting; + xa_result = (*m_AudioEncItf)->SetEncoderSettings( + m_AudioEncItf, + &settings); + returnValue = mapError(xa_result, ETrue); + + TRACE_FUNCTION_EXIT; + return returnValue; +} + +TInt32 XARecordSessionImpl::getBitratesByAudioCodecID( + XAuint32 encoderId, + RArray &aBitrates, + XAboolean& aContinuous) +{ + TRACE_FUNCTION_ENTRY; + + if (!m_AudioEncCapsItf) + return KErrGeneral; + + XAuint32 numCaps = 0; + XAAudioCodecDescriptor codecDesc; + XAresult xa_result = (*m_AudioEncCapsItf)->GetAudioEncoderCapabilities( + m_AudioEncCapsItf, + encoderId, + &numCaps, + &codecDesc); + TInt32 returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + aContinuous = codecDesc.isBitrateRangeContinuous; + /* TODO What do we do if we have more than one caps?? */ + if (codecDesc.isBitrateRangeContinuous == XA_BOOLEAN_TRUE) { + aBitrates.Append(codecDesc.minBitRate); + aBitrates.Append(codecDesc.maxBitRate); + } + else { + XAuint32 numBrSupported = codecDesc.numBitratesSupported; + XAuint32 * pBitratesSupported(NULL); + pBitratesSupported = codecDesc.pBitratesSupported; + TInt32 index = 0; + for (index = 0; index < numBrSupported; index++) + aBitrates.Append(*(pBitratesSupported + index)); + } + + TRACE_FUNCTION_ENTRY; + return returnValue; +} + +TInt32 XARecordSessionImpl::getSampleRatesByAudioCodecID(XAuint32 encoderId, + RArray &aSampleRates) +{ + TRACE_FUNCTION_ENTRY; + + if (!m_AudioEncCapsItf) + return KErrGeneral; + + XAuint32 numCaps = 0; + XAAudioCodecDescriptor codecDesc; + XAresult xa_result = (*m_AudioEncCapsItf)->GetAudioEncoderCapabilities( + m_AudioEncCapsItf, + encoderId, + &numCaps, + &codecDesc); + TInt returnValue = mapError(xa_result, ETrue); + RET_ERR_IF_ERR(returnValue); + + /* TODO What do we do if we have more than one caps?? */ + if (codecDesc.isFreqRangeContinuous == XA_BOOLEAN_TRUE) { + aSampleRates.Append(codecDesc.minSampleRate / KMilliToHz); + aSampleRates.Append(codecDesc.maxSampleRate / KMilliToHz); + } + else { + XAuint32 numSRSupported = codecDesc.numSampleRatesSupported; + XAmilliHertz *pSampleRatesSupported(NULL); + pSampleRatesSupported = codecDesc.pSampleRatesSupported; + for (TInt index = 0; index < numSRSupported; index++) + aSampleRates.Append((*(pSampleRatesSupported + index)) / KMilliToHz); + } + + TRACE_FUNCTION_ENTRY; + return returnValue; +} + +/* Local function implementation */ +void cbXAObjectItf( + XAObjectItf caller, + const void *pContext, + XAuint32 event, + XAresult result, + XAuint32 param, + void *pInterface) +{ + if (pContext) { + ((XARecordSessionImpl*)pContext)->cbMediaRecorder( + caller, + pContext, + event, + result, + param, + pInterface); + } +} + +void cbXARecordItf( + XARecordItf caller, + void *pContext, + XAuint32 event) +{ + if (pContext) { + ((XARecordSessionImpl*)pContext)->cbRecordItf( + caller, + pContext, + event); + } +} + +void cbXAAvailableAudioInputsChanged( + XAAudioIODeviceCapabilitiesItf caller, + void * pContext, + XAuint32 deviceID, + XAint32 numInputs, + XAboolean isNew) +{ + if (pContext) { + ((XARecordSessionImpl*)pContext)->cbAvailableAudioInputsChanged( + caller, + pContext, + deviceID, + numInputs, + isNew); + } +} diff --git a/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.h b/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.h new file mode 100644 index 000000000..81e4ac763 --- /dev/null +++ b/src/plugins/symbian/openmaxal/mediarecorder/xarecordsessionimpl.h @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XARECORDSESSIONIMPL_H +#define XARECORDSESSIONIMPL_H + +#include +#include + + +class XARecordObserver; +class XARecordSessionImpl +{ +public: + XARecordSessionImpl(XARecordObserver &parent); + ~XARecordSessionImpl(); + TInt32 postConstruct(); + + /* For QMediaRecorderControl begin */ + TInt32 setURI(const TDesC &aURI); + TInt32 record(); + TInt32 pause(); + TInt32 stop(); + TInt32 duration(TInt64 &aDur); + /* For QMediaRecorderControl end */ + + void cbMediaRecorder(XAObjectItf caller, + const void *pContext, + XAuint32 event, + XAresult result, + XAuint32 param, + void *pInterface); + void cbRecordItf(XARecordItf caller, + void *pContext, + XAuint32 event); + + /* For QAudioEndpointSelector begin */ + void getAudioInputDeviceNames(RArray &aArray); + TInt32 defaultAudioInputDevice(TPtrC &endPoint); + TInt32 activeAudioInputDevice(TPtrC &endPoint); + TBool setAudioInputDevice(const TDesC &aDevice); + void cbAvailableAudioInputsChanged(XAAudioIODeviceCapabilitiesItf caller, + void *pContext, + XAuint32 deviceID, + XAint32 numInputs, + XAboolean isNew); + /* For QAudioEndpointSelector end */ + + /* For QAudioEncoderControl begin */ + const RArray& getAudioEncoderNames(); + TInt32 getSampleRates(const TDesC &aEncoder, + RArray &aSampleRates, + TBool &aIsContinuous); + TInt32 getBitrates(const TDesC &aEncoder, + RArray &aBitrates, + TBool& aContinuous); + /* For QAudioEncoderControl end */ + + /* For QMediaContainerControl begin */ + const RArray& getContainerNames(); + const RArray& getContainerDescs(); + /* For QMediaContainerControl end */ + + void resetEncoderAttributes(); + void setContainerType(const TDesC &aURI); + TBool setCodec(const TDesC &aURI); + TUint32 getBitRate(); + void setBitRate(TUint32 aBitRate); + TUint32 getChannels(); + void setChannels(TUint32 aChannels); + void setOptimalChannelCount(); + TUint32 getSampleRate(); + void setSampleRate(TUint32 aSampleRate); + void setOptimalSampleRate(); + TInt32 setCBRMode(); + TInt32 setVBRMode(); + void setVeryLowQuality(); + void setLowQuality(); + void setNormalQuality(); + void setHighQuality(); + void setVeryHighQuality(); + +private: + TInt32 createMediaRecorderObject(); + TInt32 mapError(XAresult xa_err, + TBool debPrn); + TInt32 initContainersList(); + TInt32 initAudioEncodersList(); + TInt32 initAudioInputDevicesList(); + TInt32 setEncoderSettingsToMediaRecorder(); + TInt32 getBitratesByAudioCodecID(XAuint32 encoderId, + RArray &aBitrates, + XAboolean& aContinuous); + TInt32 getSampleRatesByAudioCodecID(XAuint32 encoderId, + RArray &aSampleRates); + + +private: + XARecordObserver &m_Parent; + XAObjectItf m_EOEngine; + XAObjectItf m_MORecorder; + XARecordItf m_RecordItf; + XAAudioEncoderItf m_AudioEncItf; + /* Audio Source */ + XADataSource m_DataSource; + XADataLocator_IODevice m_LocatorMic; + XADataFormat_MIME m_Mime; + XADataLocator_URI m_Uri; + /*Audio Sink*/ + XADataSink m_DataSink; + HBufC8 *m_WAVMime; + + /* Set by client*/ + HBufC8 *m_URIName; + XAuint32 m_AudioEncoderId; + XAuint32 m_InputDeviceId; + XAuint32 m_ContainerType; + XAuint32 m_BitRate; + XAuint32 m_RateControl; + XAuint32 m_ProfileSetting; + XAuint32 m_ChannelsOut; + XAuint32 m_SampleRate; + + /* For QAudioEndpointSelector begin */ + XAAudioIODeviceCapabilitiesItf m_AudioIODevCapsItf; + RArray m_InputDeviceIDs; + CDesC16ArrayFlat *m_AudioInputDeviceNames; + RArray m_DefaultInputDeviceIDs; + CDesC16ArrayFlat *m_DefaultAudioInputDeviceNames; + /* For QAudioEndpointSelector end */ + + /* For QAudioEncoderControl begin */ + XAAudioEncoderCapabilitiesItf m_AudioEncCapsItf; + RArray m_EncoderIds; + RArray m_EncoderNames; + RArray m_ContainerNames; + RArray m_ContainerDescs; + /* For QAudioEncoderControl begin */ +}; + +#endif /* XARECORDSESSIONIMPL_H */ diff --git a/src/plugins/symbian/openmaxal/openmaxal.pro b/src/plugins/symbian/openmaxal/openmaxal.pro new file mode 100644 index 000000000..0565536a4 --- /dev/null +++ b/src/plugins/symbian/openmaxal/openmaxal.pro @@ -0,0 +1,58 @@ +TEMPLATE = lib + +CONFIG += plugin +TARGET = $$qtLibraryTarget(qtmultimediakit_openmaxalengine) +PLUGIN_TYPE = mediaservice +include (../../../../common.pri) +qtAddLibrary(QtMultimediaKit) + +#includes here so that all defines are added here also +include(mediaplayer/mediaplayer.pri) +include(mediarecorder/mediarecorder.pri) +include(radiotuner/radiotuner.pri) + +DEPENDPATH += . + +HEADERS += qxamediaserviceproviderplugin.h \ + qxacommon.h \ + xacommon.h + +SOURCES += qxamediaserviceproviderplugin.cpp + +# Input parameters for the generated bld.inf file +# ----------------------------------------------- +SYMBIAN_PLATFORMS = DEFAULT + +# Input parameters for the generated mmp file +# ------------------------------------------- +load(data_caging_paths) +TARGET.UID3 = 0x10207CA1 +TARGET.CAPABILITY = ALL -TCB +TARGET.EPOCALLOWDLLDATA = 1 +MMP_RULES += EXPORTUNFROZEN + +# Macros controlling debug traces +#DEFINES += PROFILE_TIME +#DEFINES += PROFILE_RAM_USAGE +#DEFINES += PROFILE_HEAP_USAGE +#DEFINES += PLUGIN_QT_TRACE_ENABLED +#DEFINES += PLUGIN_QT_SIGNAL_EMIT_TRACE_ENABLED +#DEFINES += PLUGIN_SYMBIAN_TRACE_ENABLED + +INCLUDEPATH += $$MW_LAYER_SYSTEMINCLUDE +INCLUDEPATH += /epoc32/include/platform/mw/khronos + + +# Input parameters for qmake to make the dll a qt plugin +pluginDep.sources = $${TARGET}.dll +pluginDep.path = $${QT_PLUGINS_BASE_DIR}/$${PLUGIN_TYPE} +DEPLOYMENT += pluginDep + +LIBS += \ + -lQtMultimediaKit \ + -lopenmaxal + +# check for PROFILE_RAM_USAGE +contains(DEFINES, PROFILE_RAM_USAGE) { + LIBS += -lhal +} diff --git a/src/plugins/symbian/openmaxal/qxacommon.h b/src/plugins/symbian/openmaxal/qxacommon.h new file mode 100644 index 000000000..f19a75d1c --- /dev/null +++ b/src/plugins/symbian/openmaxal/qxacommon.h @@ -0,0 +1,203 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXACOMMON_H +#define QXACOMMON_H + +#if defined(PLUGIN_QT_TRACE_ENABLED) \ + || defined(PLUGIN_QT_SIGNAL_EMIT_TRACE_ENABLED) \ + || defined(PROFILE_TIME) \ + || defined(PROFILE_RAM_USAGE) \ + || defined(PROFILE_HEAP_USAGE) +# include +#endif /* PLUGIN_QT_TRACE_ENABLED */ + +#ifdef PROFILE_RAM_USAGE +# include +#endif + + +#ifdef PLUGIN_QT_TRACE_ENABLED +# define QT_TRACE_FUNCTION_ENTRY qDebug() << __PRETTY_FUNCTION__ << ">" +# define QT_TRACE_FUNCTION_EXIT qDebug() << __PRETTY_FUNCTION__ << "<" +# define QT_TRACE_FUNCTION_ENTRY_EXIT qDebug() << __PRETTY_FUNCTION__ << "><" +# define QT_TRACE1(v1) qDebug() << v1 +# define QT_TRACE2(v1, v2) qDebug() << v1 << v2 +#else +# define QT_TRACE_FUNCTION_ENTRY +# define QT_TRACE_FUNCTION_EXIT +# define QT_TRACE_FUNCTION_ENTRY_EXIT +# define QT_TRACE1(v1) +# define QT_TRACE2(v1, v2) +#endif /*PLUGIN_QT_TRACE_ENABLED*/ + +#ifdef PLUGIN_QT_SIGNAL_EMIT_TRACE_ENABLED +# define SIGNAL_EMIT_TRACE1(v1) qDebug() << __PRETTY_FUNCTION__ << v1 +#else +# define SIGNAL_EMIT_TRACE1(v1) +#endif /*PLUGIN_QT_SIGNAL_EMIT_TRACE_ENABLED*/ + +#ifdef PROFILE_TIME_ELAPSED +# define TAG_TIME_PROFILING_BEGIN \ + TTime beginProfilingTime; \ + beginProfilingTime.HomeTime() + +# define TAG_TIME_PROFILING_END \ + TTime endProfilingTime; \ + endProfilingTime.HomeTime(); \ + TTimeIntervalMicroSeconds diffInMicroSecs = endProfilingTime.MicroSecondsFrom(beginProfilingTime) + +# define QT_PRINT_TO_CONSOLE_TIME_DIFF \ + qDebug() << "VPROFILEDAT: " << __PRETTY_FUNCTION__ << ": Time taken " << diffInMicroSecs.Int64() << " microseconds" +#else /* Empty macros */ +# define TAG_TIME_PROFILING_BEGIN +# define TAG_TIME_PROFILING_END +# define QT_PRINT_TO_CONSOLE_TIME_DIFF +#endif /*PROFILE_TIME_ELAPSED*/ + +#ifdef PROFILE_RAM_USAGE +# define TAG_RAM_PROFILING_BEGIN \ + TInt beginProfilingRAM; \ + TInt err1 = HAL::Get(HALData::EMemoryRAMFree, beginProfilingRAM) + +# define TAG_RAM_PROFILING_END \ + TInt endProfilingRAM; \ + TInt err2 = HAL::Get(HALData::EMemoryRAMFree, endProfilingRAM) + +# define QT_PRINT_TO_CONSOLE_RAM_DIFF \ + if ((err1 == KErrNone) && (err2 == KErrNone)) \ + { \ + TInt diffRAM = (beginProfilingRAM - endProfilingRAM); \ + if ( diffRAM > 0 ) \ + { \ + qDebug() << "VPROFILEDAT: " << __PRETTY_FUNCTION__ << ": " << diffRAM << " bytes of RAM used"; \ + } \ + else \ + { \ + qDebug() << "VPROFILEDAT: " << __PRETTY_FUNCTION__ << ": " << -(diffRAM) << " bytes of RAM freed"; \ + } \ + } \ + else \ + { \ + qDebug() << "VPROFILEDAT: " << __PRETTY_FUNCTION__ << "Error1[" << err1 << "] Error2[" << err2; \ + } + +#else /* Empty macros */ +# define TAG_RAM_PROFILING_BEGIN +# define TAG_RAM_PROFILING_END +# define QT_PRINT_TO_CONSOLE_RAM_DIFF +#endif /*PROFILE_RAM_USAGE*/ + +#ifdef PROFILE_HEAP_USAGE +# define TAG_DEFAULT_HEAP_PROFILING_BEGIN \ + TInt beginProfilingHEAPBiggestBlock; \ + TInt beginProfilingHEAP = User::Available(beginProfilingHEAPBiggestBlock) \ + +# define TAG_DEFAULT_HEAP_PROFILING_END \ + TInt endProfilingHEAPBiggestBlock; \ + TInt endProfilingHEAP = User::Available(endProfilingHEAPBiggestBlock) \ + +# define QT_PRINT_TO_CONSOLE_HEAP_DIFF \ + TInt diffHEAP = beginProfilingHEAP - endProfilingHEAP; \ + if ( diffHEAP > 0 ) \ + { \ + qDebug() << "VPROFILEDAT: " << __PRETTY_FUNCTION__ << ": " << diffHEAP << " bytes in default HEAP used"; \ + } \ + else \ + { \ + qDebug() << "VPROFILEDAT: " << __PRETTY_FUNCTION__ << ": " << -(diffHEAP) << " bytes in default HEAP freed"; \ + } +#else /* Empty macros */ +# define TAG_DEFAULT_HEAP_PROFILING_BEGIN +# define TAG_DEFAULT_HEAP_PROFILING_END +# define QT_PRINT_TO_CONSOLE_HEAP_DIFF +#endif /*PROFILE_HEAP_USAGE*/ + +/* This macro checks p pointer for null. If it is, returns value 's' from + * function immediately. + */ +#define RET_s_IF_p_IS_NULL(p, s) \ + if (p == NULL) { \ + return s; \ + } + +/* This macro checks p pointer for null. If it is, returns from function + * immediately. + */ +#define RET_IF_p_IS_NULL(p) \ + if (p == NULL) { \ + return; \ + } + +/* This macro checks p pointer for null. If it is, emits an error signal + * error(QMediaPlayer::ResourceError, tr("Resource Error")); + * and returns value 's' from function immediately. + */ +#define RET_s_IF_p_IS_NULL_EMIT_PLAYER_RESOURCE_ERROR(p, s) \ + if (p == NULL) { \ + emit error(QMediaPlayer::ResourceError, tr("Resource Error")); \ + SIGNAL_EMIT_TRACE1("emit error(QMediaPlayer::ResourceError, tr(\"Resource Error\"))"); \ + return s; \ + } + +/* This macro checks p pointer for null. If it is, emits an error signal + * error(QMediaPlayer::ResourceError, tr("Resource Error")); + * and returns from function immediately. + */ +#define RET_IF_p_IS_NULL_EMIT_PLAYER_RESOURCE_ERROR(p) \ + if (p == NULL) { \ + emit error(QMediaPlayer::ResourceError, tr("Resource Error")); \ + SIGNAL_EMIT_TRACE1("emit error(QMediaPlayer::ResourceError, tr(\"Resource Error\"))"); \ + return; \ + } + +/* This macro checks p pointer for null. If it is, emits an error signal + * error(QMediaPlayer::ResourceError, tr("Resource Error")); + * and returns from function immediately. + */ +#define RET_IF_ERROR(p) \ + if (p != KErrNone) { \ + emit error(QMediaPlayer::ResourceError, tr("Resource Error")); \ + SIGNAL_EMIT_TRACE1("emit error(QMediaPlayer::ResourceError, tr(\"Resource Error\"))"); \ + return; \ + } + +#endif /* QXACOMMON_H */ diff --git a/src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.cpp b/src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.cpp new file mode 100644 index 000000000..bfbb4333a --- /dev/null +++ b/src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.cpp @@ -0,0 +1,87 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qxamediaserviceproviderplugin.h" +#include "qxaplaymediaservice.h" +#include "qxarecordmediaservice.h" +#include "qxaradiomediaservice.h" +#include "qxacommon.h" + +QStringList QXAMediaServiceProviderPlugin::keys() const +{ + return QStringList() + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) + << QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE) + << QLatin1String(Q_MEDIASERVICE_RADIO); +} + +QMediaService* QXAMediaServiceProviderPlugin::create(QString const& key) +{ + QT_TRACE_FUNCTION_ENTRY; + QMediaService* service = NULL; + if (key == QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) ) { + service = new QXAPlayMediaService; + QT_TRACE1("Created QXAPlayMediaService"); + } + else if (key == QLatin1String(Q_MEDIASERVICE_AUDIOSOURCE)) { + service = new QXARecodMediaService; + QT_TRACE1("Created QXARecodMediaService"); + } + else if (key == QLatin1String(Q_MEDIASERVICE_RADIO) ) { + service = new QXARadioMediaService; + QT_TRACE1("Created QXARadioMediaService"); + } + else { + QT_TRACE2("unsupported key:", key); + } + QT_TRACE_FUNCTION_EXIT; + return service; +} + +void QXAMediaServiceProviderPlugin::release(QMediaService *service) +{ + QT_TRACE_FUNCTION_ENTRY; + delete service; + QT_TRACE_FUNCTION_EXIT; +} + +Q_EXPORT_PLUGIN2(qtmultimediakit_openmaxalengine, QXAMediaServiceProviderPlugin); diff --git a/src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.h b/src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.h new file mode 100644 index 000000000..6d3530349 --- /dev/null +++ b/src/plugins/symbian/openmaxal/qxamediaserviceproviderplugin.h @@ -0,0 +1,60 @@ +/**************************************************************************** +** +** Copyright (C) 2009 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXAMEDIASERVICEPROVIDERPLUGIN_H +#define QXAMEDIASERVICEPROVIDERPLUGIN_H + +#include +#include +#include + +QT_USE_NAMESPACE + +class QXAMediaServiceProviderPlugin : public QMediaServiceProviderPlugin +{ + Q_OBJECT +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); +}; + +#endif /* QXAMEDIASERVICEPROVIDERPLUGIN_H */ diff --git a/src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.cpp b/src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.cpp new file mode 100644 index 000000000..9666b0410 --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.cpp @@ -0,0 +1,202 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qxaradiocontrol.h" +#include "qxaradiosession.h" +#include "xaradiosessionimpl.h" + +QXARadioControl::QXARadioControl(QXARadioSession *session, QObject *parent) +:QRadioTunerControl(parent), m_session(session) +{ + + connect(m_session, SIGNAL(stateChanged(QRadioTuner::State)), this, SIGNAL(stateChanged(QRadioTuner::State))); + + connect(m_session, SIGNAL(bandChanged(QRadioTuner::Band)), this, SIGNAL(bandChanged(QRadioTuner::Band))); + + connect(m_session, SIGNAL(frequencyChanged(int)), this, SIGNAL(frequencyChanged(int))); + + connect(m_session, SIGNAL(stereoStatusChanged(bool)), this, SIGNAL(stereoStatusChanged(bool))); + + connect(m_session, SIGNAL(searchingChanged(bool)), this, SIGNAL(searchingChanged(bool))); + + connect(m_session, SIGNAL(signalStrengthChanged(int)), this, SIGNAL(signalStrengthChanged(int))); + + connect(m_session, SIGNAL(volumeChanged(int)), this, SIGNAL(volumeChanged(int))); + + connect(m_session, SIGNAL(mutedChanged(bool)), this, SIGNAL(mutedChanged(bool))); + +// connect(m_session, SIGNAL(error(int,QString)), this,SIGNAL(error(int,QString))); +} + +QXARadioControl::~QXARadioControl() +{ + +} + +QtMultimediaKit::AvailabilityError QXARadioControl::availabilityError() const +{ + return m_session->availabilityError(); +} + +bool QXARadioControl::isAvailable() const +{ + return m_session->isAvailable(); +} + +QRadioTuner::State QXARadioControl::state() const +{ + return m_session->state(); +} + +QRadioTuner::Band QXARadioControl::band() const +{ + return m_session->band(); +} + +void QXARadioControl::setBand(QRadioTuner::Band band) +{ + m_session->setBand(band); +} + +bool QXARadioControl::isBandSupported(QRadioTuner::Band band) const +{ + return m_session->isBandSupported(band); +} + +int QXARadioControl::frequency() const +{ + return m_session->frequency(); +} + +int QXARadioControl::frequencyStep(QRadioTuner::Band band) const +{ + return m_session->frequencyStep(band); +} + +QPair QXARadioControl::frequencyRange(QRadioTuner::Band band) const +{ + return m_session->frequencyRange(band); +} + +void QXARadioControl::setFrequency(int freq) +{ + m_session->setFrequency(freq); +} + +bool QXARadioControl::isStereo() const +{ + return m_session->isStereo(); +} + +QRadioTuner::StereoMode QXARadioControl::stereoMode() const +{ + return m_session->stereoMode(); +} + +void QXARadioControl::setStereoMode(QRadioTuner::StereoMode stereoMode) +{ + m_session->setStereoMode(stereoMode); +} + +int QXARadioControl::signalStrength() const +{ + return m_session->signalStrength(); +} + +int QXARadioControl::volume() const +{ + return m_session->volume(); +} + +void QXARadioControl::setVolume(int volume) +{ + m_session->setVolume(volume); +} + +bool QXARadioControl::isMuted() const +{ + return m_session->isMuted(); +} + +void QXARadioControl::setMuted(bool muted) +{ + m_session->setMuted(muted); +} + +bool QXARadioControl::isSearching() const +{ + return m_session->isSearching(); +} + +void QXARadioControl::searchForward() +{ + m_session->searchForward(); +} + +void QXARadioControl::searchBackward() +{ + m_session->searchBackward(); +} + +void QXARadioControl::cancelSearch() +{ + m_session->cancelSearch(); +} + +void QXARadioControl::start() +{ + m_session->start(); +} + +void QXARadioControl::stop() +{ + m_session->stop(); +} + +QRadioTuner::Error QXARadioControl::error() const +{ + return m_session->error(); +} + +QString QXARadioControl::errorString() const +{ + return m_session->errorString(); +} diff --git a/src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.h b/src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.h new file mode 100644 index 000000000..47d4f827d --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/qxaradiocontrol.h @@ -0,0 +1,95 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXARADIOCONTROL_H +#define QXARADIOCONTROL_H + +#include +#include + +QT_USE_NAMESPACE + +class QXARadioSession; + +class QXARadioControl : public QRadioTunerControl +{ + Q_OBJECT + +public: + QXARadioControl(QXARadioSession *session, QObject *parent = 0); + virtual ~QXARadioControl(); + QRadioTuner::State state() const; + + QRadioTuner::Band band() const; + void setBand(QRadioTuner::Band band); + bool isBandSupported(QRadioTuner::Band band) const; + int frequency() const; + int frequencyStep(QRadioTuner::Band band) const; + QPair frequencyRange(QRadioTuner::Band band) const; + void setFrequency(int freq); + bool isStereo() const; + QRadioTuner::StereoMode stereoMode() const; + void setStereoMode(QRadioTuner::StereoMode stereoMode); + int signalStrength() const; + int volume() const; + void setVolume(int volume); + bool isMuted() const; + void setMuted(bool muted); + bool isSearching() const; + void searchForward(); + void searchBackward(); + void cancelSearch(); + bool isValid() const; + bool isAvailable() const; + QtMultimediaKit::AvailabilityError availabilityError() const; + void start(); + void stop(); + QRadioTuner::Error error() const; + QString errorString() const; + +private: + QXARadioSession *m_session; + +protected: + QXARadioControl(QObject* parent = 0); +}; + +#endif /* QXARADIOCONTROL_H */ diff --git a/src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.cpp b/src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.cpp new file mode 100644 index 000000000..f399ef8c5 --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.cpp @@ -0,0 +1,72 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include + +#include "qxaradiomediaservice.h" +#include "qxaradiosession.h" +#include "qxaradiocontrol.h" +#include + +QXARadioMediaService::QXARadioMediaService(QObject *parent) + : QMediaService(parent) +{ + m_session = new QXARadioSession(this); + m_control = new QXARadioControl(m_session, this); +} + +QXARadioMediaService::~QXARadioMediaService() +{ +} + +QMediaControl* QXARadioMediaService::requestControl(const char *name) +{ + + if (qstrcmp(name, QRadioTunerControl_iid) == 0) { + return m_control; + } + return 0; +} + +void QXARadioMediaService::releaseControl(QMediaControl *control) +{ + Q_UNUSED(control) +} diff --git a/src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.h b/src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.h new file mode 100644 index 000000000..7ac014f8f --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/qxaradiomediaservice.h @@ -0,0 +1,66 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXARADIOMEDIASERVICE_H +#define QXARADIOMEDIASERVICE_H + +#include +#include + +QT_USE_NAMESPACE + +class QXARadioSession; +class QXARadioControl; + +class QXARadioMediaService : public QMediaService +{ + Q_OBJECT +public: + QXARadioMediaService(QObject *parent = 0); + ~QXARadioMediaService(); + QMediaControl *requestControl(const char *name); + void releaseControl( QMediaControl *control); +private: + QXARadioSession *m_session; + QXARadioControl *m_control; +}; + +#endif /*QXARADIOMEDIASERVICE_H*/ diff --git a/src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.cpp b/src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.cpp new file mode 100644 index 000000000..6822813c4 --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.cpp @@ -0,0 +1,323 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include "qxaradiosession.h" +#include "xaradiosessionimpl.h" +#include "qxacommon.h" + +QXARadioSession::QXARadioSession(QObject *parent) +:QObject(parent) +{ + QT_TRACE_FUNCTION_ENTRY; + m_impl = new XARadioSessionImpl(*this); + if (!m_impl) { + QT_TRACE1("RadioSession::RadioSession(): ERROR creating RadioSessionImpl..."); + return; + } + if (m_impl->PostConstruct() != QRadioTuner::NoError) { + QT_TRACE1("RadioSession::RadioSession(): ERROR from RadioSessionImpl::PostContstruct..."); + delete m_impl; + m_impl = NULL; + } + QT_TRACE_FUNCTION_EXIT; +} + +QXARadioSession::~QXARadioSession() +{ + delete m_impl; +} + +QRadioTuner::State QXARadioSession::state() const +{ + QRadioTuner::State state = QRadioTuner::StoppedState; + if (m_impl) + state = m_impl->State(); + return state; + } +QtMultimediaKit::AvailabilityError QXARadioSession::availabilityError() const +{ + QtMultimediaKit::AvailabilityError error = QtMultimediaKit::NoError; + if (m_impl) + error = m_impl->AvailabilityError(); + return error; +} + +QRadioTuner::Band QXARadioSession::band() const +{ + QRadioTuner::Band band = QRadioTuner::FM; + if (m_impl) + band = m_impl->Band(); + return band; +} + +void QXARadioSession::setBand(QRadioTuner::Band band) +{ + if (m_impl) + m_impl->SetBand(band); +} + +bool QXARadioSession::isBandSupported(QRadioTuner::Band band) const +{ + if (m_impl) + return m_impl->IsBandSupported(band); + return false; +} + +bool QXARadioSession::isAvailable() const +{ + if (m_impl) + return m_impl->IsAvailable(); + return false; +} + +int QXARadioSession::frequency() const +{ + TInt frequency = 0; + if (m_impl) + frequency = m_impl->GetFrequency(); + return (int)frequency; +} + +int QXARadioSession::frequencyStep(QRadioTuner::Band band) const +{ + TInt freqStep = 0; + if (m_impl) + freqStep = m_impl->FrequencyStep(band); + return (int)freqStep; +} + +QPair QXARadioSession::frequencyRange(QRadioTuner::Band /*band*/) const +{ + QPair freqRange; + freqRange.first = 0; + freqRange.second =0; + + if (m_impl) { + TInt freqRangeType = m_impl->GetFrequencyRange(); + m_impl->GetFrequencyRangeProperties(freqRangeType, freqRange.first, freqRange.second); + } + + return freqRange; +} + +void QXARadioSession::setFrequency(int frequency) +{ + if (m_impl) + m_impl->SetFrequency(frequency); +} + +bool QXARadioSession::isStereo() const +{ + bool isStereo = false; + if (m_impl) + isStereo = m_impl->IsStereo(); + return isStereo; +} + +QRadioTuner::StereoMode QXARadioSession::stereoMode() const +{ + QRadioTuner::StereoMode mode(QRadioTuner::Auto); + if (m_impl) + mode = m_impl->StereoMode(); + return mode; +} + +void QXARadioSession::setStereoMode(QRadioTuner::StereoMode mode) +{ + if (m_impl) + m_impl->SetStereoMode(mode); +} + +int QXARadioSession::signalStrength() const +{ + TInt signalStrength = 0; + if (m_impl) + signalStrength = m_impl->GetSignalStrength(); + return (int)signalStrength; +} + +int QXARadioSession::volume() const +{ + TInt volume = 0; + if (m_impl) + volume = m_impl->GetVolume(); + return volume; +} + +int QXARadioSession::setVolume(int volume) +{ + TInt newVolume = 0; + if (m_impl) { + m_impl->SetVolume(volume); + newVolume = m_impl->GetVolume(); + } + return newVolume; +} + +bool QXARadioSession::isMuted() const +{ + bool isMuted = false; + if (m_impl) + isMuted = m_impl->IsMuted(); + return isMuted; +} + +void QXARadioSession::setMuted(bool muted) +{ + if (m_impl) + m_impl->SetMuted(muted); +} + +bool QXARadioSession::isSearching() const +{ + bool isSearching = false; + if (m_impl) + isSearching = m_impl->IsSearching(); + return isSearching; +} + +void QXARadioSession::searchForward() +{ + if (m_impl) + m_impl->Seek(true); +} + +void QXARadioSession::searchBackward() +{ + if (m_impl) + m_impl->Seek(false); +} + +void QXARadioSession::cancelSearch() +{ + if (m_impl) + m_impl->StopSeeking(); +} + +void QXARadioSession::start() +{ + if (m_impl) + m_impl->Start(); +} + +void QXARadioSession::stop() +{ + if (m_impl) + m_impl->Stop(); +} + +QRadioTuner::Error QXARadioSession::error() const +{ + QRadioTuner::Error err(QRadioTuner::NoError); + if (m_impl) + err = m_impl->Error(); + return err; +} + +QString QXARadioSession::errorString() const +{ + QString str = NULL; + switch (iError) { + case QRadioTuner::ResourceError: + str = "Resource Error"; + break; + case QRadioTuner::OpenError: + str = "Open Error"; + break; + case QRadioTuner::OutOfRangeError: + str = "Out of Range Error"; + break; + default: + break; + } + + return str; +} + +// Callbacks, which will emit signals to client: +void QXARadioSession::CBStateChanged(QRadioTuner::State state) +{ + emit stateChanged(state); +} + +void QXARadioSession::CBBandChanged(QRadioTuner::Band band) +{ + emit bandChanged(band); +} + +void QXARadioSession::CBFrequencyChanged(TInt newFrequency) +{ + emit frequencyChanged(newFrequency); +} + +void QXARadioSession::CBStereoStatusChanged(bool isStereo) +{ + emit stereoStatusChanged(isStereo); +} + +void QXARadioSession::CBSignalStrengthChanged(int signalStrength) +{ + emit signalStrengthChanged(signalStrength); +} + +void QXARadioSession::CBVolumeChanged(int volume) +{ + emit volumeChanged(volume); +} + +void QXARadioSession::CBMutedChanged(bool isMuted) +{ + emit mutedChanged(isMuted); +} + +void QXARadioSession::CBSearchingChanged(bool isSearching) +{ + emit searchingChanged(isSearching); +} + +void QXARadioSession::CBError(QRadioTuner::Error err) +{ + iError = err; + emit error((int)err, errorString()); +} + + diff --git a/src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.h b/src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.h new file mode 100644 index 000000000..72f6a7a01 --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/qxaradiosession.h @@ -0,0 +1,118 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QXARADIOSESSION_H +#define QXARADIOSESSION_H + +#include +#include +#include +#include "xaradiosessionimplobserver.h" + +QT_USE_NAMESPACE + +class XARadioSessionImpl; + +class QXARadioSession : public QObject, public XARadioSessionImplObserver +{ +Q_OBJECT + +public: + QXARadioSession(QObject *parent); + virtual ~QXARadioSession(); + + QRadioTuner::State state() const; + QRadioTuner::Band band() const; + void setBand(QRadioTuner::Band band); + bool isBandSupported(QRadioTuner::Band band) const; + int frequency() const; + int frequencyStep(QRadioTuner::Band b) const; + QPair frequencyRange(QRadioTuner::Band b) const; + void setFrequency(int frequency); + bool isStereo() const; + QRadioTuner::StereoMode stereoMode() const; + void setStereoMode(QRadioTuner::StereoMode mode); + int signalStrength() const; + int volume() const; + int setVolume(int volume); + bool isMuted() const; + void setMuted(bool muted); + bool isSearching() const; + void searchForward(); + void searchBackward(); + void cancelSearch(); + void start(); + void stop(); + bool isAvailable() const; + QtMultimediaKit::AvailabilityError availabilityError() const; + QRadioTuner::Error error() const; + QString errorString() const; + + /* Callbacks from XARadioSessionImplObserver begin */ + void CBBandChanged(QRadioTuner::Band band); + void CBStateChanged(QRadioTuner::State state); + void CBFrequencyChanged(TInt newFrequency); + void CBStereoStatusChanged(bool isStereo); + void CBSignalStrengthChanged(int signalStrength); + void CBVolumeChanged(int volume); + void CBMutedChanged(bool isMuted); + void CBSearchingChanged(bool isSearching); + void CBError(QRadioTuner::Error err); + /* Callbacks from XARadioSessionImplObserver end */ + +signals: + void stateChanged(QRadioTuner::State state); + void bandChanged(QRadioTuner::Band band); + void frequencyChanged(int frequency); + void stereoStatusChanged(bool stereo); + void searchingChanged(bool stereo); + void signalStrengthChanged(int signalStrength); + void volumeChanged(int volume); + void mutedChanged(bool muted); + void error(int err, QString str); + +private: + /* Own */ + QRadioTuner::Error iError; + XARadioSessionImpl* m_impl; +}; + +#endif /*QXARADIOSESSION_H*/ diff --git a/src/plugins/symbian/openmaxal/radiotuner/radiotuner.pri b/src/plugins/symbian/openmaxal/radiotuner/radiotuner.pri new file mode 100644 index 000000000..bf83d05fc --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/radiotuner.pri @@ -0,0 +1,18 @@ +INCLUDEPATH += $$PWD + +# Input +HEADERS += \ + $$PWD/qxaradiomediaservice.h \ + $$PWD/qxaradiosession.h \ + $$PWD/qxaradiocontrol.h \ + $$PWD/xaradiosessionimpl.h \ + $$PWD/xaradiosessionimplobserver.h + +SOURCES += \ + $$PWD/qxaradiomediaservice.cpp \ + $$PWD/qxaradiosession.cpp \ + $$PWD/qxaradiocontrol.cpp \ + $$PWD/xaradiosessionimpl.cpp + +LIBS += \ + -lbafl diff --git a/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.cpp b/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.cpp new file mode 100644 index 000000000..94bebc373 --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.cpp @@ -0,0 +1,715 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "xaradiosessionimpl.h" +#include "xaradiosessionimplobserver.h" +#include +#include "xacommon.h" + +#define MAX_NUMBER_INTERFACES 20 +#define FM_STEP 100000; // Hz (.1 MHz) + +/* + * function declarations. + * */ +void EngineObjectCallback(XAObjectItf caller, const void */*pContext*/, + XAuint32 event, XAresult result, XAuint32 /*param*/, + void */*pInterface*/); + +void RadioCallback(XARadioItf caller, void* pContext, XAuint32 event, XAuint32 eventIntData, XAboolean eventBooleanData); +void NokiaVolumeExtItfCallback(XANokiaVolumeExtItf caller, void* pContext, XAuint32 event, XAboolean eventBooleanData); +void NokiaLinearVolumeItfCallback(XANokiaLinearVolumeItf caller, void* pContext, XAuint32 event, XAboolean eventBooleanData); +void PlayItfCallbackForRadio(XAPlayItf caller, void* pContext, XAuint32 event); + +XARadioSessionImpl::XARadioSessionImpl(XARadioSessionImplObserver& parent) +:iParent(parent), +iRadio(NULL), +iEngine(NULL), +iPlayer(NULL), +iSearching(EFalse), +iRadioAvailable(EFalse), +iState(QRadioTuner::StoppedState) +{ + iAvailabilityError = QtMultimediaKit::NoError; +} + +XARadioSessionImpl::~XARadioSessionImpl() +{ + if (iRadio) { + TRACE_LOG((_L("XARadioSessionImpl::~XARadioSessionImpl(): Deleting Radio Device..."))); + (*iRadio)->Destroy(iRadio); + iRadio = NULL; + TRACE_LOG((_L("XARadioSessionImpl::~XARadioSessionImpl(): Deleted Radio Device"))); + } + if (iPlayer) { + TRACE_LOG((_L("XARadioSessionImpl::~XARadioSessionImpl(): Deleting player..."))); + (*iPlayer)->Destroy(iPlayer); + iPlayer = NULL; + TRACE_LOG((_L("XARadioSessionImpl::~XARadioSessionImpl(): Deleted iPlayer"))); + } + if ( iEngine ) { + TRACE_LOG((_L("XARadioSessionImpl::~XARadioSessionImpl(): Deleting engine..."))); + (*iEngine)->Destroy(iEngine); + iEngine = NULL; + TRACE_LOG((_L("XARadioSessionImpl::~XARadioSessionImpl(): Deleted engine"))); + } +} + +QRadioTuner::Error XARadioSessionImpl::PostConstruct() +{ + XAresult res = CreateEngine(); + if (res != KErrNone) + return QRadioTuner::ResourceError; + else + return QRadioTuner::NoError; +} + +TInt XARadioSessionImpl::CreateEngine() +{ + TRACE_FUNCTION_ENTRY; + XAboolean required[MAX_NUMBER_INTERFACES]; + XAInterfaceID iidArray[MAX_NUMBER_INTERFACES]; + XAuint32 noOfInterfaces = 0; + int i; + XAresult res; + + XAEngineOption EngineOption[] = + { + { + (XAuint32) XA_ENGINEOPTION_THREADSAFE, + (XAuint32) XA_BOOLEAN_TRUE + } + }; + + /* Create XA engine */ + if (!iEngine) { + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Creating Engine..."))); + res = xaCreateEngine(&iEngine, 1, EngineOption, 0, NULL, NULL); + RET_ERR_IF_ERR(CheckErr(res)); + res = (*iEngine)->RegisterCallback(iEngine, EngineObjectCallback, NULL); + RET_ERR_IF_ERR(CheckErr(res)); + + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Realizing..."))); + res = (*iEngine)->Realize(iEngine, XA_BOOLEAN_FALSE); + RET_ERR_IF_ERR(CheckErr(res)); + + // Create Engine Interface: + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Creating Engine Interface"))); + RET_ERR_IF_ERR(CheckErr((*iEngine)->GetInterface(iEngine, XA_IID_ENGINE, (void*)&iEngineItf))); + + // Create Radio Device and interface(s): + if (!iRadio) { + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Creating Radio Device"))); + res = (*iEngineItf)->CreateRadioDevice(iEngineItf,&iRadio, 0, NULL, NULL); + RET_ERR_IF_ERR(CheckErr(res)); + + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Realize Radio Device"))); + res = (*iRadio)->Realize(iRadio, XA_BOOLEAN_FALSE); + RET_ERR_IF_ERR(CheckErr(res)); + + // Get Radio interface: + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Get Radio Interface"))); + res = (*iRadio)->GetInterface(iRadio, XA_IID_RADIO, (void*)&iRadioItf); + RET_ERR_IF_ERR(CheckErr(res)); + iRadioAvailable = ETrue; + // Register Radio Callback: + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Create Radio Callback:"))); + res = (*iRadioItf)->RegisterRadioCallback(iRadioItf, RadioCallback, (void*)this); + RET_ERR_IF_ERR(CheckErr(res)); + } + XADataSource audioSource; + XADataLocator_IODevice locatorIODevice; + XADataSink audioSink; + XADataLocator_OutputMix locator_outputmix; + + /* Init arrays required[] and iidArray[] */ + for (i = 0; i < MAX_NUMBER_INTERFACES; i++) { + required[i] = XA_BOOLEAN_FALSE; + iidArray[i] = XA_IID_NULL; + } + + iidArray[0] = XA_IID_NOKIAVOLUMEEXT; + iidArray[1] = XA_IID_NOKIALINEARVOLUME; + noOfInterfaces = 2; + + locatorIODevice.locatorType = XA_DATALOCATOR_IODEVICE; + locatorIODevice.deviceType = XA_IODEVICE_RADIO; + locatorIODevice.deviceID = 0; /* ignored */ + locatorIODevice.device = iRadio; + audioSource.pLocator = (void*) &locatorIODevice; + audioSource.pFormat = NULL; + + /* Setup the data sink structure */ + locator_outputmix.locatorType = XA_DEFAULTDEVICEID_AUDIOOUTPUT; + locator_outputmix.outputMix = NULL; + audioSink.pLocator = (void*) &locator_outputmix; + audioSink.pFormat = NULL; + + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Create Media Player:"))); + res = (*iEngineItf)->CreateMediaPlayer(iEngineItf, &iPlayer, &audioSource, NULL, &audioSink, NULL, NULL, NULL, noOfInterfaces, iidArray, required); + RET_ERR_IF_ERR(CheckErr(res)); + + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Realize Media Player:"))); + res = (*iPlayer)->Realize(iPlayer, XA_BOOLEAN_FALSE); + RET_ERR_IF_ERR(CheckErr(res)); + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Get Play Interface from player:"))); + res = (*iPlayer)->GetInterface(iPlayer, XA_IID_PLAY, (void*) &iPlayItf); + RET_ERR_IF_ERR(CheckErr(res)); + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Create PlayItf Callback:"))); + res = (*iPlayItf)->RegisterCallback(iPlayItf, PlayItfCallbackForRadio, (void*)this); + RET_ERR_IF_ERR(CheckErr(res)); + + // Get Volume Interfaces specific for Nokia impl: + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Get NokiaVolumeExt Interface"))); + res = (*iPlayer)->GetInterface(iPlayer, XA_IID_NOKIAVOLUMEEXT, (void*)&iNokiaVolumeExtItf); + RET_ERR_IF_ERR(CheckErr(res)); + + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Get NokiaLinearVolume Interface"))); + res = (*iPlayer)->GetInterface(iPlayer, XA_IID_NOKIALINEARVOLUME, (void*)&iNokiaLinearVolumeItf); + RET_ERR_IF_ERR(CheckErr(res)); + + // Register Volume Callbacks: + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Create NokiaVolumeExtItf Callback:"))); + res = (*iNokiaVolumeExtItf)->RegisterVolumeCallback(iNokiaVolumeExtItf, NokiaVolumeExtItfCallback, (void*)this); + RET_ERR_IF_ERR(CheckErr(res)); + res = (*iNokiaVolumeExtItf)->SetCallbackEventsMask(iNokiaVolumeExtItf,(XA_NOKIAVOLUMEEXT_EVENT_MUTE_CHANGED)); + RET_ERR_IF_ERR(CheckErr(res)); + TRACE_LOG((_L("XARadioSessionImpl::CreateEngine: Create NokiaLinearVolumeItf Callback:"))); + res = (*iNokiaLinearVolumeItf)->RegisterVolumeCallback(iNokiaLinearVolumeItf, NokiaLinearVolumeItfCallback, (void*)this); + RET_ERR_IF_ERR(CheckErr(res)); + res = (*iNokiaLinearVolumeItf)->SetCallbackEventsMask(iNokiaLinearVolumeItf,(XA_NOKIALINEARVOLUME_EVENT_VOLUME_CHANGED)); + RET_ERR_IF_ERR(CheckErr(res)); + } + + TRACE_FUNCTION_EXIT; + return EFalse; +} + +QRadioTuner::State XARadioSessionImpl::State() const +{ + TRACE_FUNCTION_ENTRY_EXIT; + return iState; +} + +QtMultimediaKit::AvailabilityError XARadioSessionImpl::AvailabilityError() const +{ + TRACE_FUNCTION_ENTRY_EXIT; + return iAvailabilityError; +} + + bool XARadioSessionImpl::IsAvailable() const +{ + TRACE_FUNCTION_ENTRY_EXIT; + return iRadioAvailable; +} + +QRadioTuner::Band XARadioSessionImpl::Band() const +{ + TRACE_FUNCTION_ENTRY_EXIT; + return iBand; +} + +void XARadioSessionImpl::SetBand(QRadioTuner::Band band) +{ + if (band != QRadioTuner::FM) + iParent.CBError(QRadioTuner::OpenError); + else + iBand = band; +} + +bool XARadioSessionImpl::IsBandSupported(QRadioTuner::Band band) const +{ + if (band == QRadioTuner::FM) + return ETrue; + else + return EFalse; +} + +// Returns the number of Hertz to increment the frequency by when stepping through frequencies within a given band. +TInt XARadioSessionImpl::FrequencyStep(QRadioTuner::Band /*band*/) const +{ + TInt freqStep = FM_STEP; + return (int)freqStep; +} + +bool XARadioSessionImpl::IsStereo() //const +{ + bool isStereo = EFalse; + QRadioTuner::StereoMode mode = StereoMode(); + if (mode == QRadioTuner::ForceStereo || mode == QRadioTuner::Auto) + isStereo = ETrue; + return isStereo; +} + +bool XARadioSessionImpl::IsMuted() const +{ + TRACE_FUNCTION_ENTRY; + XAboolean isMuted = EFalse; + (*iNokiaVolumeExtItf)->GetMute(iNokiaVolumeExtItf, &isMuted ); + TRACE_LOG((_L("XARadioSessionImpl::IsMuted: isMuted = %d"), isMuted)); + + TRACE_FUNCTION_EXIT; + return isMuted; +} + +bool XARadioSessionImpl::IsSearching() const +{ + //iSearching is set when seek (QT:searchForward-backward) + // iSearching is cleared when SearchingStatusChanged is called or StopSeeking is called + return iSearching; +} + +TInt XARadioSessionImpl::GetFrequency() +{ + TRACE_FUNCTION_ENTRY; + + XAuint32 freq = 0; + XAresult res = (*iRadioItf)->GetFrequency(iRadioItf, &freq ); + RET_ERR_IF_ERR(CheckErr(res)); + TRACE_LOG((_L("XARadioSessionImpl::GetFrequency: Frequency = %d"), freq)); + + TRACE_FUNCTION_EXIT; + return (int)freq; +} + +TInt XARadioSessionImpl::GetFrequencyRange() +{ + TRACE_FUNCTION_ENTRY; + XAuint8 range = 0; + + XAresult res = (*iRadioItf)->GetFreqRange(iRadioItf, &range); + RET_ERR_IF_ERR(CheckErr(res)); + TRACE_LOG((_L("XARadioSessionImpl::GetFrequencyRange: Frequency Range = %d"), range)); + + TRACE_FUNCTION_EXIT; + return (int)range; +} + +TInt XARadioSessionImpl::GetFrequencyRangeProperties(TInt range, TInt &minFreq, TInt &maxFreq) +{ + TRACE_FUNCTION_ENTRY; + XAuint32 freqInterval = 0; + XAresult res = (*iRadioItf)->GetFreqRangeProperties(iRadioItf, (XAuint8)range, (XAuint32*)&minFreq,(XAuint32*)&maxFreq, (XAuint32*)&freqInterval); + RET_ERR_IF_ERR(CheckErr(res)); + TRACE_LOG((_L("XARadioSessionImpl::GetFrequencyRangeProperties: minFreq = %d, maxFreq = %d"), minFreq, maxFreq)); + + TRACE_FUNCTION_EXIT; + return res; +} + +TInt XARadioSessionImpl::SetFrequency(TInt aFreq) +{ + TRACE_FUNCTION_ENTRY; + + TRACE_LOG((_L("XARadioSessionImpl::SetFrequency: Setting Frequency to: %d"), aFreq)); + XAresult res = (*iRadioItf)->SetFrequency(iRadioItf, aFreq ); + RET_ERR_IF_ERR(CheckErr(res)); + + TRACE_FUNCTION_EXIT; + return res; +} + +QRadioTuner::StereoMode XARadioSessionImpl::StereoMode() +{ + TRACE_FUNCTION_ENTRY; + QRadioTuner::StereoMode qtStereoMode; + XAuint32 symStereoMode; + (*iRadioItf)->GetStereoMode(iRadioItf, &symStereoMode); + + if (symStereoMode == XA_STEREOMODE_MONO) + qtStereoMode = QRadioTuner::ForceMono; + else if (symStereoMode == XA_STEREOMODE_STEREO) + qtStereoMode = QRadioTuner::ForceStereo; + else + qtStereoMode = QRadioTuner::Auto; + + TRACE_FUNCTION_EXIT; + return qtStereoMode; +} + +TInt XARadioSessionImpl::SetStereoMode(QRadioTuner::StereoMode qtStereoMode) +{ + TRACE_FUNCTION_ENTRY; + XAuint32 symStereoMode; + + if (qtStereoMode == QRadioTuner::ForceMono) + symStereoMode = XA_STEREOMODE_MONO; + else if (qtStereoMode == QRadioTuner::ForceStereo) + symStereoMode = XA_STEREOMODE_STEREO; + else + symStereoMode = XA_STEREOMODE_AUTO; + + XAresult res = (*iRadioItf)->SetStereoMode(iRadioItf, (symStereoMode)); + TRACE_FUNCTION_EXIT; + return res; +} + +TInt XARadioSessionImpl::GetSignalStrength() +{ + TRACE_FUNCTION_ENTRY; + XAuint32 signalStrength = 0; + + (*iRadioItf)->GetSignalStrength(iRadioItf, &signalStrength ); + TRACE_LOG((_L("XARadioSessionImpl::GetSignalStrength: Signal Strength = %d"), signalStrength)); + TRACE_FUNCTION_EXIT; + return (int)signalStrength; +} + +TInt XARadioSessionImpl::GetVolume() +{ + TRACE_FUNCTION_ENTRY; + XAuint32 vol; + if (iPlayer && iNokiaLinearVolumeItf) { + (*iNokiaLinearVolumeItf)->GetVolumeLevel(iNokiaLinearVolumeItf, &vol ); + TRACE_LOG((_L("XARadioSessionImpl::GetVolume: Volume = %d"), vol)); + } + TRACE_FUNCTION_EXIT; + return (TInt)vol; +} + +TInt XARadioSessionImpl::SetVolume(TInt aVolume) +{ + TRACE_FUNCTION_ENTRY; + XAuint32 newVolume = 0; + TRACE_LOG((_L("XARadioSessionImpl::SetVolume: Setting volume to: %d"), aVolume)); + if (iPlayer && iNokiaLinearVolumeItf) { + newVolume = aVolume; + XAresult res = (*iNokiaLinearVolumeItf)->SetVolumeLevel(iNokiaLinearVolumeItf, &newVolume); + } + TRACE_FUNCTION_EXIT; + return (TInt)newVolume; +} + +TInt XARadioSessionImpl::SetMuted(TBool aMuted) +{ + TRACE_FUNCTION_ENTRY; + XAresult res = (*iNokiaVolumeExtItf)->SetMute(iNokiaVolumeExtItf, aMuted); + TRACE_FUNCTION_EXIT; + return res; +} + +TInt XARadioSessionImpl::Seek(TBool aDirection) +{ + TRACE_FUNCTION_ENTRY; + iSearching = true; + XAresult res = (*iRadioItf)->Seek(iRadioItf, aDirection ); + TRACE_FUNCTION_EXIT; + return res; +} + +TInt XARadioSessionImpl::StopSeeking() +{ + TRACE_FUNCTION_ENTRY; + XAresult res = (*iRadioItf)->StopSeeking(iRadioItf); + iSearching = EFalse; + TRACE_FUNCTION_EXIT; + return res; +} + +void XARadioSessionImpl::Start() +{ + TRACE_FUNCTION_ENTRY; + if (iPlayItf) { + XAresult res = (*iPlayItf)->SetPlayState(iPlayItf, XA_PLAYSTATE_PLAYING); + // add error handling if res != 0 (call errorCB) + } + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::Stop() +{ + TRACE_FUNCTION_ENTRY; + if (iPlayItf) { + XAresult res = (*iPlayItf)->SetPlayState(iPlayItf, XA_PLAYSTATE_STOPPED); + // add error handling if res != 0 (call errorCB) + } + TRACE_FUNCTION_EXIT; +} + +QRadioTuner::Error XARadioSessionImpl::Error() +{ + TRACE_FUNCTION_ENTRY_EXIT; + return QRadioTuner::NoError; +} + +//TInt XARadioSessionImpl::ErrorString(); +// { +// TRACE_FUNCTION_ENTRY; + +// TRACE_FUNCTION_EXIT; +// } + +void XARadioSessionImpl::StateChanged(QRadioTuner::State state) +{ + TRACE_FUNCTION_ENTRY; + iState = state; + iParent.CBStateChanged(state); + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::FrequencyChanged(XAuint32 freq) +{ + TRACE_FUNCTION_ENTRY; + iParent.CBFrequencyChanged(freq); + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::SearchingChanged(TBool isSearching) +{ + TRACE_FUNCTION_ENTRY; + iSearching = EFalse; + iParent.CBSearchingChanged(isSearching); + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::StereoStatusChanged(TBool stereoStatus) +{ + TRACE_FUNCTION_ENTRY; + iParent.CBStereoStatusChanged(stereoStatus); + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::SignalStrengthChanged(TBool stereoStatus) +{ + TRACE_FUNCTION_ENTRY; + iParent.CBSignalStrengthChanged(stereoStatus); + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::VolumeChanged() +{ + TRACE_FUNCTION_ENTRY; + int vol = 0; + iParent.CBVolumeChanged(vol); + TRACE_FUNCTION_EXIT; +} + +void XARadioSessionImpl::MutedChanged(TBool mute) +{ + TRACE_FUNCTION_ENTRY; + iParent.CBMutedChanged(mute); + TRACE_FUNCTION_EXIT; +} + +void EngineObjectCallback(XAObjectItf /*caller*/, + const void */*pContext*/, +#ifdef PLUGIN_SYMBIAN_TRACE_ENABLED + XAuint32 event, +#else + XAuint32 /*event*/, +#endif /*PLUGIN_SYMBIAN_TRACE_ENABLED*/ + XAresult /*result*/, + XAuint32 /*param*/, + void */*pInterface*/) +{ +#ifdef PLUGIN_SYMBIAN_TRACE_ENABLED + TRACE_LOG((_L("Engine object event: 0x%x\n"), (int)event)); +#endif /*PLUGIN_SYMBIAN_TRACE_ENABLED*/ +} + +void RadioCallback(XARadioItf /*caller*/, + void* pContext, + XAuint32 event, + XAuint32 eventIntData, + XAboolean eventBooleanData) +{ + XAuint32 freq; + XAboolean stereoStatus(XA_BOOLEAN_FALSE); + + switch (event) { + case XA_RADIO_EVENT_ANTENNA_STATUS_CHANGED: + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_ANTENNA_STATUS_CHANGED"))); + // Qt API has no callback defined for this event. + break; + case XA_RADIO_EVENT_FREQUENCY_CHANGED: + freq = eventIntData; + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_FREQUENCY_CHANGED to: %d"), freq)); + if (pContext) + ((XARadioSessionImpl*)pContext)->FrequencyChanged(freq); + break; + case XA_RADIO_EVENT_FREQUENCY_RANGE_CHANGED: + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_FREQUENCY_RANGE_CHANGED"))); + // Qt API has no callback defined for this event. + break; + case XA_RADIO_EVENT_PRESET_CHANGED: + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_PRESET_CHANGED"))); + // Qt API has no callback defined for this event. + break; + case XA_RADIO_EVENT_SEEK_COMPLETED: + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_SEEK_COMPLETED"))); + if (pContext) + ((XARadioSessionImpl*)pContext)->SearchingChanged(false); + break; + case XA_RADIO_EVENT_STEREO_STATUS_CHANGED: + stereoStatus = eventBooleanData; + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_STEREO_STATUS_CHANGED: %d"), stereoStatus)); + if (pContext) + ((XARadioSessionImpl*)pContext)->StereoStatusChanged(stereoStatus); + break; + case XA_RADIO_EVENT_SIGNAL_STRENGTH_CHANGED: + TRACE_LOG((_L("RadioCallback: XA_RADIO_EVENT_SIGNAL_STRENGTH_CHANGED"))); + if (pContext) + ((XARadioSessionImpl*)pContext)->SignalStrengthChanged(stereoStatus); + break; + default: + TRACE_LOG((_L("RadioCallback: default"))); + break; + } +} + +void NokiaVolumeExtItfCallback(XANokiaVolumeExtItf /*caller*/, + void* pContext, + XAuint32 event, + XAboolean eventBooleanData) +{ + XAboolean mute; + switch (event) { + case XA_NOKIAVOLUMEEXT_EVENT_MUTE_CHANGED: + mute = eventBooleanData; + TRACE_LOG((_L("NokiaVolumeExtItfCallback: XA_NOKIAVOLUMEEXT_EVENT_MUTE_CHANGED to: %d"), mute)); + if (pContext) + ((XARadioSessionImpl*)pContext)->MutedChanged(mute); + break; + default: + TRACE_LOG((_L("NokiaVolumeExtItfCallback: default"))); + break; + } +} + +void NokiaLinearVolumeItfCallback(XANokiaLinearVolumeItf /*caller*/, + void* pContext, + XAuint32 event, + XAboolean /*eventBooleanData*/) +{ + switch (event) { + case XA_NOKIALINEARVOLUME_EVENT_VOLUME_CHANGED: + if (pContext) + ((XARadioSessionImpl*)pContext)->VolumeChanged(); + break; + default: + TRACE_LOG((_L("NokiaLinearVolumeItfCallback: default"))); + break; + } +} + +void PlayItfCallbackForRadio(XAPlayItf /*caller*/, + void* pContext, + XAuint32 event) +{ + switch (event) { + case XA_PLAYEVENT_HEADMOVING: + if (pContext) + ((XARadioSessionImpl*)pContext)->StateChanged(QRadioTuner::ActiveState); + break; + case XA_PLAYEVENT_HEADSTALLED: + if (pContext) + ((XARadioSessionImpl*)pContext)->StateChanged(QRadioTuner::StoppedState); + break; + default: + TRACE_LOG((_L("NokiaLinearVolumeItfCallback: default"))); + break; + } +} + +TInt XARadioSessionImpl::CheckErr(XAresult res) +{ + TInt status(KErrGeneral); + switch(res) { + case XA_RESULT_SUCCESS: + //TRACE_LOG((_L("XA_RESULT_SUCCESS"))); + status = KErrNone; + break; + case XA_RESULT_PRECONDITIONS_VIOLATED: + TRACE_LOG((_L("XA_RESULT_PRECONDITIONS_VIOLATED"))); + break; + case XA_RESULT_PARAMETER_INVALID: + TRACE_LOG((_L("XA_RESULT_PARAMETER_INVALID"))); + break; + case XA_RESULT_MEMORY_FAILURE: + TRACE_LOG((_L("XA_RESULT_MEMORY_FAILURE"))); + iAvailabilityError = QtMultimediaKit::ResourceError; + break; + case XA_RESULT_RESOURCE_ERROR: + TRACE_LOG((_L("XA_RESULT_RESOURCE_ERROR"))); + iAvailabilityError = QtMultimediaKit::ResourceError; + break; + case XA_RESULT_RESOURCE_LOST: + TRACE_LOG((_L("XA_RESULT_RESOURCE_LOST"))); + iAvailabilityError = QtMultimediaKit::ResourceError; + break; + case XA_RESULT_IO_ERROR: + TRACE_LOG((_L("XA_RESULT_IO_ERROR"))); + break; + case XA_RESULT_BUFFER_INSUFFICIENT: + TRACE_LOG((_L("XA_RESULT_BUFFER_INSUFFICIENT"))); + break; + case XA_RESULT_CONTENT_CORRUPTED: + TRACE_LOG((_L("XA_RESULT_CONTENT_CORRUPTED"))); + break; + case XA_RESULT_CONTENT_UNSUPPORTED: + TRACE_LOG((_L("XA_RESULT_CONTENT_UNSUPPORTED"))); + break; + case XA_RESULT_CONTENT_NOT_FOUND: + TRACE_LOG((_L("XA_RESULT_CONTENT_NOT_FOUND"))); + break; + case XA_RESULT_PERMISSION_DENIED: + TRACE_LOG((_L("XA_RESULT_PERMISSION_DENIED"))); + break; + case XA_RESULT_FEATURE_UNSUPPORTED: + TRACE_LOG((_L("XA_RESULT_FEATURE_UNSUPPORTED"))); + break; + case XA_RESULT_INTERNAL_ERROR: + TRACE_LOG((_L("XA_RESULT_INTERNAL_ERROR"))); + break; + case XA_RESULT_UNKNOWN_ERROR: + TRACE_LOG((_L("XA_RESULT_UNKNOWN_ERROR"))); + break; + case XA_RESULT_OPERATION_ABORTED: + TRACE_LOG((_L("XA_RESULT_OPERATION_ABORTED"))); + break; + case XA_RESULT_CONTROL_LOST: + TRACE_LOG((_L("XA_RESULT_CONTROL_LOST"))); + break; + default: + TRACE_LOG((_L("Unknown Error!!!"))); + } + return status; +} diff --git a/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.h b/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.h new file mode 100644 index 000000000..ee5f53a2f --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimpl.h @@ -0,0 +1,128 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XARADIOSESSIONIMPL_H +#define XARADIOSESSIONIMPL_H + +#include +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class XARadioSessionImplObserver; + +class XARadioSessionImpl +{ +public: + XARadioSessionImpl(XARadioSessionImplObserver& parent); + ~XARadioSessionImpl(); + QRadioTuner::Error PostConstruct(); + QRadioTuner::Band Band() const; + QRadioTuner::State State() const; + QtMultimediaKit::AvailabilityError AvailabilityError() const; + bool IsAvailable() const; + void SetBand(QRadioTuner::Band band); + bool IsBandSupported(QRadioTuner::Band band) const; + TInt FrequencyStep(QRadioTuner::Band band) const; + bool IsStereo(); //const; + bool IsMuted() const; + bool IsSearching() const; + TInt GetFrequency(); + TInt GetFrequencyRange(); + TInt GetFrequencyRangeProperties(TInt range, TInt &minFreq, TInt &maxFreq); + TInt SetFrequency(TInt aFreq); + QRadioTuner::StereoMode StereoMode(); + TInt SetStereoMode(QRadioTuner::StereoMode stereoMode); + TInt GetSignalStrength(); + TInt GetVolume(); + TInt SetVolume(TInt aVolume); + TInt SetMuted(TBool aMuted); + TInt Seek(TBool aDirection); + TInt StopSeeking(); + void Start(); + void Stop(); + QRadioTuner::Error Error(); +//TInt ErrorString(); + void StateChanged(QRadioTuner::State state); + void FrequencyChanged(XAuint32 freq); + void SearchingChanged(TBool isSearching); + void StereoStatusChanged(TBool stereoStatus); + void SignalStrengthChanged(TBool stereoStatus); + void VolumeChanged(); + void MutedChanged(TBool mute); + +private: + TInt CreateEngine(); + TInt CheckErr(XAresult res); + + +private: + XARadioSessionImplObserver& iParent; + XAObjectItf iRadio; + XAObjectItf iEngine; + XAObjectItf iPlayer; + XAEngineItf iEngineItf; + XARecordItf iRecordItf; + XAPlayItf iPlayItf; + XARadioItf iRadioItf; + XARDSItf iRdsItf; + XANokiaVolumeExtItf iNokiaVolumeExtItf; // used for mute functionality + XANokiaLinearVolumeItf iNokiaLinearVolumeItf; // used for volume functionality + + /* Audio Source */ + XADataSource iDataSource; + + /*Audio Sink*/ + XADataSink iAudioSink; + XADataLocator_OutputMix iLocator_outputmix; + + TBool iAutoFlag; + TBool iSearching; + TBool iRadioAvailable; + QtMultimediaKit::AvailabilityError iAvailabilityError; + QRadioTuner::Band iBand; + QRadioTuner::State iState; +}; + +#endif /* XARADIOSESSIONIMPL_H */ diff --git a/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimplobserver.h b/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimplobserver.h new file mode 100644 index 000000000..73c734fd3 --- /dev/null +++ b/src/plugins/symbian/openmaxal/radiotuner/xaradiosessionimplobserver.h @@ -0,0 +1,64 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XARADIOSESSIONIMPLOBSERVER_H +#define XARADIOSESSIONIMPLOBSERVER_H + +#include +#include + +QT_USE_NAMESPACE + +class XARadioSessionImplObserver +{ +public: + virtual void CBStateChanged(QRadioTuner::State state) = 0; + virtual void CBBandChanged(QRadioTuner::Band band) = 0; + virtual void CBFrequencyChanged(TInt newFrequency) = 0; + virtual void CBStereoStatusChanged(bool isStereo) = 0; + virtual void CBSignalStrengthChanged(int signalStrength) = 0; + virtual void CBVolumeChanged(int volume) = 0; + virtual void CBMutedChanged(bool isMuted) = 0; + virtual void CBSearchingChanged(bool isSearching) = 0; + virtual void CBError(QRadioTuner::Error err) = 0; +}; + +#endif /*XARADIOSESSIONIMPLOBSERVER_H*/ diff --git a/src/plugins/symbian/openmaxal/xacommon.h b/src/plugins/symbian/openmaxal/xacommon.h new file mode 100644 index 000000000..9aecbc8f5 --- /dev/null +++ b/src/plugins/symbian/openmaxal/xacommon.h @@ -0,0 +1,79 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef XACOMMON_H +#define XACOMMON_H + +#ifdef PLUGIN_SYMBIAN_TRACE_ENABLED +# include +#endif /* PLUGIN_SYMBIAN_TRACE_ENABLED */ + +#ifdef PLUGIN_SYMBIAN_TRACE_ENABLED +# define TRACE_FUNCTION_ENTRY RDebug::Printf( "%s >", __PRETTY_FUNCTION__) +# define TRACE_FUNCTION_EXIT RDebug::Printf( "%s <", __PRETTY_FUNCTION__) +# define TRACE_FUNCTION_ENTRY_EXIT RDebug::Printf( "%s ><", __PRETTY_FUNCTION__) +# define TRACE_LOG(s) RDebug::Print s +#else +# define TRACE_FUNCTION_ENTRY +# define TRACE_FUNCTION_EXIT +# define TRACE_FUNCTION_ENTRY_EXIT +# define TRACE_LOG +#endif /* PLUGIN_SYMBIAN_TRACE_ENABLED */ + +#define RET_IF_FALSE(e) \ + if (e == false) \ + { \ + return; \ + } + +#define RET_BOOL_IF_FALSE(e) \ + if (e == false) \ + { \ + return e; \ + } + +#define RET_ERR_IF_ERR(e) \ + if (e != 0) \ + { \ + return e; \ + } + +#endif /* XACOMMON_H */ diff --git a/src/plugins/symbian/symbian.pro b/src/plugins/symbian/symbian.pro new file mode 100644 index 000000000..7fc2c8690 --- /dev/null +++ b/src/plugins/symbian/symbian.pro @@ -0,0 +1,23 @@ +###################################################################### +# +# Mobility API project - Symbian backends +# +###################################################################### + +TEMPLATE = subdirs + +include (../../../config.pri) + +# The openmax-al backend is currently not supported +# we include mmf only if we are not building openmaxal based backend +#contains(openmaxal_symbian_enabled, no) { +# message("Enabling mmf mediarecording, playback and radio backend") +# symbian:SUBDIRS += mmf +# +#else { +# message("Enabling OpenMAX AL audio record, playback and radio backend") +# symbian:SUBDIRS += openmaxal +# + +symbian:SUBDIRS += ecam mmf + diff --git a/src/plugins/symbian/videooutput/s60videodisplay.cpp b/src/plugins/symbian/videooutput/s60videodisplay.cpp new file mode 100644 index 000000000..35e234e98 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videodisplay.cpp @@ -0,0 +1,179 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videodisplay.h" +#include +#include +#include +#include + +S60VideoDisplay::S60VideoDisplay(QObject *parent) +: QObject(parent) +, m_fullScreen(false) +, m_visible(true) +, m_aspectRatioMode(Qt::KeepAspectRatio) +, m_paintingEnabled(false) +, m_rotation(0.0f) +{ + connect(this, SIGNAL(displayRectChanged(QRect, QRect)), + this, SLOT(updateContentRect())); + connect(this, SIGNAL(nativeSizeChanged(QSize)), + this, SLOT(updateContentRect())); +} + +S60VideoDisplay::~S60VideoDisplay() +{ + +} + +RWindow *S60VideoDisplay::windowHandle() const +{ + return winId() ? static_cast(winId()->DrawableWindow()) : 0; +} + +QRect S60VideoDisplay::clipRect() const +{ + QRect displayableRect; +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + if (RWindow *window = windowHandle()) + displayableRect = QRect(0, 0, window->Size().iWidth, window->Size().iHeight); +#else + displayableRect = QApplication::desktop()->screenGeometry(); +#endif + return extentRect().intersected(displayableRect); +} + +QRect S60VideoDisplay::contentRect() const +{ + return m_contentRect; +} + +void S60VideoDisplay::setFullScreen(bool enabled) +{ + if (m_fullScreen != enabled) { + m_fullScreen = enabled; + emit fullScreenChanged(m_fullScreen); + } +} + +bool S60VideoDisplay::isFullScreen() const +{ + return m_fullScreen; +} + +void S60VideoDisplay::setVisible(bool enabled) +{ + if (m_visible != enabled) { + m_visible = enabled; + emit visibilityChanged(m_visible); + } +} + +bool S60VideoDisplay::isVisible() const +{ + return m_visible; +} + +void S60VideoDisplay::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + if (m_aspectRatioMode != mode) { + m_aspectRatioMode = mode; + emit aspectRatioModeChanged(m_aspectRatioMode); + } +} + +Qt::AspectRatioMode S60VideoDisplay::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void S60VideoDisplay::setNativeSize(const QSize &size) +{ + if (m_nativeSize != size) { + m_nativeSize = size; + emit nativeSizeChanged(m_nativeSize); + } +} + +const QSize& S60VideoDisplay::nativeSize() const +{ + return m_nativeSize; +} + +void S60VideoDisplay::setPaintingEnabled(bool enabled) +{ + if (m_paintingEnabled != enabled) { + m_paintingEnabled = enabled; + emit paintingEnabledChanged(m_paintingEnabled); + } +} + +bool S60VideoDisplay::isPaintingEnabled() const +{ + return m_paintingEnabled; +} + +void S60VideoDisplay::setRotation(qreal value) +{ + if (value != m_rotation) { + m_rotation = value; + emit rotationChanged(m_rotation); + } +} + +qreal S60VideoDisplay::rotation() const +{ + return m_rotation; +} + +void S60VideoDisplay::updateContentRect() +{ + if (isPaintingEnabled()) { + const int dx = qMax(0, extentRect().width() - nativeSize().width()); + const int dy = qMax(0, extentRect().height() - nativeSize().height()); + QRect contentRect(QPoint(dx/2, dy/2), nativeSize()); + if (m_contentRect != contentRect) { + m_contentRect = contentRect; + emit contentRectChanged(m_contentRect); + } + } +} + diff --git a/src/plugins/symbian/videooutput/s60videodisplay.h b/src/plugins/symbian/videooutput/s60videodisplay.h new file mode 100644 index 000000000..e16e7bb71 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videodisplay.h @@ -0,0 +1,188 @@ +/** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEODISPLAY_H +#define S60VIDEODISPLAY_H + +#include +#include +#include +#include +#include + +class CFbsBitmap; +class RWindow; + +QT_USE_NAMESPACE + +/* + * This class defines a common API used by Symbian camera and mediaplayer + * backends to render to on-screen video outputs, i.e. implementations of + * QVideoWidgetControl and QVideoWindowControl. + */ +class S60VideoDisplay : public QObject +{ + Q_OBJECT +public: + S60VideoDisplay(QObject *parent); + virtual ~S60VideoDisplay(); + + /* + * Returns native Symbian handle of the window to be used for rendering + */ + RWindow *windowHandle() const; + + /* + * Returns Qt WId (CCoeControl* on Symbian) + */ + virtual WId winId() const = 0; + + /* + * Returns video display rectangle + * + * This is the rectangle which includes both the video content itself, plus + * any border bars which added around the video. The aspect ratio of this + * rectangle therefore may differ from that of the nativeSize(). + * + * If running on a platform supporting video rendering to graphics + * surfaces (i.e. if VIDEOOUTPUT_GRAPHICS_SURFACES is defined), the return + * value is the relative to the origin of the video window. Otherwise, the + * return value is an absolute screen rectangle. + * + * Note that this rectangle can extend beyond the bounds of the screen or of + * the video window. + * + * When using QVideoWindowControl, the size of the extentRect matches the + * displayRect; if running on a platform which supports only DSA rendering, + * the origin differs as described above. + * + * See also clipRect, contentRect + */ + virtual QRect extentRect() const = 0; + + /* + * Returns video clipping rectangle + * + * This rectangle is the intersection of displayRect() with either the window + * rectangle (on platforms supporting video rendering to graphics surfaces), + * or the screen rectangle (on platforms supporting only DSA video rendering). + * + * If running on a platform supporting video rendering to graphics + * surfaces (i.e. if VIDEOOUTPUT_GRAPHICS_SURFACES is defined), the return + * value is the relative to the origin of the video window. Otherwise, the + * return value is an absolute screen rectangle. + * + * See also extentRect, contentRect + */ + QRect clipRect() const; + + /* + * Returns video content rectangle + * + * This is the rectangle in which the video content is rendered, i.e. its + * size is that of extentRect() minus border bars. The aspect ratio of this + * rectangle is therefore equal to that of the nativeSize(). + * + * This rectangle is always relative to the window in which video is rendered. + * + * See also extentRect, clipRect + */ + QRect contentRect() const; + + void setFullScreen(bool enabled); + bool isFullScreen() const; + + void setVisible(bool visible); + bool isVisible() const; + + void setAspectRatioMode(Qt::AspectRatioMode mode); + Qt::AspectRatioMode aspectRatioMode() const; + + const QSize& nativeSize() const; + + void setPaintingEnabled(bool enabled); + bool isPaintingEnabled() const; + + void setRotation(qreal value); + qreal rotation() const; + +public slots: + void setNativeSize(const QSize &size); + + /* + * Provide new video frame + * + * If setPaintingEnabled(true) has been called, the frame is rendered to + * the display. + * + * If a QWidget is available to the control (i.e. the control is a + * QVideoWidgetControl), the frame is rendered via QPainter. Otherwise, the + * frame is blitted to the window using native Symbian drawing APIs. + */ + virtual void setFrame(const CFbsBitmap &bitmap) = 0; + +signals: + void windowHandleChanged(RWindow *); + void displayRectChanged(QRect extentRect, QRect clipRect); + void fullScreenChanged(bool); + void visibilityChanged(bool); + void aspectRatioModeChanged(Qt::AspectRatioMode); + void nativeSizeChanged(QSize); + void contentRectChanged(QRect); + void paintingEnabledChanged(bool); + void rotationChanged(qreal); + void beginVideoWindowNativePaint(); + void endVideoWindowNativePaint(); + +private slots: + void updateContentRect(); + +private: + QRect m_contentRect; + bool m_fullScreen; + bool m_visible; + Qt::AspectRatioMode m_aspectRatioMode; + QSize m_nativeSize; + bool m_paintingEnabled; + qreal m_rotation; +}; + +#endif // S60VIDEODISPLAY_H + diff --git a/src/plugins/symbian/videooutput/s60videooutpututils.cpp b/src/plugins/symbian/videooutput/s60videooutpututils.cpp new file mode 100644 index 000000000..feac346d1 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videooutpututils.cpp @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videooutpututils.h" + +#ifdef PRIVATE_QTGUI_HEADERS_AVAILABLE +#if QT_VERSION >= 0x040601 && !defined(__WINSCW__) +#include +#include +#define USE_PRIVATE_QTGUI_APIS +#endif // QT_VERSION >= 0x040601 && !defined(__WINSCW__) +#endif // PRIVATE_QTGUI_HEADERS_AVAILABLE + +namespace S60VideoOutputUtils +{ + +void setIgnoreFocusChanged(QWidget *widget) +{ +#ifdef USE_PRIVATE_QTGUI_APIS + // Warning: if this flag is not set, the application may crash due to + // CGraphicsContext being called from within the context of + // QGraphicsVideoItem::paint(), when the video widget is shown. + static_cast(widget->winId())->setIgnoreFocusChanged(true); +#else + Q_UNUSED(widget) +#endif +} + +void setNativePaintMode(QWidget *widget, NativePaintMode mode) +{ +#ifdef USE_PRIVATE_QTGUI_APIS + QWidgetPrivate *widgetPrivate = qt_widget_private(widget->window()); + widgetPrivate->createExtra(); + QWExtra::NativePaintMode widgetMode = QWExtra::Default; + switch (mode) { + case Default: + break; + case ZeroFill: + widgetMode = QWExtra::ZeroFill; + break; + case BlitWriteAlpha: +#if QT_VERSION >= 0x040704 + widgetMode = QWExtra::BlitWriteAlpha; +#endif + break; + case Disable: + widgetMode = QWExtra::Disable; + break; + } + widgetPrivate->extraData()->nativePaintMode = widgetMode; +#else + Q_UNUSED(widget) + Q_UNUSED(mode) +#endif +} + +void setNativePaintMode(WId wid, NativePaintMode mode) +{ +#ifdef USE_PRIVATE_QTGUI_APIS + QWidget *window = static_cast(wid)->widget()->window(); + setNativePaintMode(window, mode); +#else + Q_UNUSED(wid) + Q_UNUSED(mode) +#endif +} + +void setReceiveNativePaintEvents(QWidget *widget, bool enabled) +{ +#ifdef USE_PRIVATE_QTGUI_APIS + QWidgetPrivate *widgetPrivate = qt_widget_private(widget); + widgetPrivate->createExtra(); + widgetPrivate->extraData()->receiveNativePaintEvents = enabled; +#else + Q_UNUSED(widget) + Q_UNUSED(enabled) +#endif +} + +} // namespace S60VideoOutputUtils + diff --git a/src/plugins/symbian/videooutput/s60videooutpututils.h b/src/plugins/symbian/videooutput/s60videooutpututils.h new file mode 100644 index 000000000..6d83062c3 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videooutpututils.h @@ -0,0 +1,71 @@ +/** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOOUTPUTUTILS_H +#define S60VIDEOOUTPUTUTILS_H + +#include +#include + +QT_FORWARD_DECLARE_CLASS(QWidget) + +/* + * Helper functions used by video output. + */ +namespace S60VideoOutputUtils +{ + +enum NativePaintMode +{ + Default, + ZeroFill, + BlitWriteAlpha, + Disable +}; + +void setIgnoreFocusChanged(QWidget *widget); +void setNativePaintMode(QWidget *widget, NativePaintMode mode); +void setNativePaintMode(WId wid, NativePaintMode mode); +void setReceiveNativePaintEvents(QWidget *widget, bool enabled); + +} // namespace S60VideoOutputUtils + +#endif // S60VIDEOOUTPUTUTILS_H + diff --git a/src/plugins/symbian/videooutput/s60videowidget.cpp b/src/plugins/symbian/videooutput/s60videowidget.cpp new file mode 100644 index 000000000..44c0919ba --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowidget.cpp @@ -0,0 +1,237 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videowidget.h" +#include "s60videooutpututils.h" + +#include +#include +#include +#include + +#include // CCoeEnv +#include // CCoeControl +#include + +using namespace S60VideoOutputUtils; + +const int NullOrdinalPosition = -1; + +S60VideoWidget::S60VideoWidget(QWidget *parent) +: QWidget(parent) +, m_pixmap(NULL) +, m_paintingEnabled(false) +, m_topWinId(0) +, m_ordinalPosition(NullOrdinalPosition) +{ + setSizePolicy(QSizePolicy::Expanding, QSizePolicy::Expanding); + setPalette(QPalette(Qt::black)); + setAutoFillBackground(false); + if (!parent) + setProperty("_q_DummyWindowSurface", true); + S60VideoOutputUtils::setIgnoreFocusChanged(this); +} + +S60VideoWidget::~S60VideoWidget() +{ + +} + +bool S60VideoWidget::event(QEvent *event) +{ + if (event->type() == QEvent::WinIdChange) + updateOrdinalPosition(); + return QWidget::event(event); +} + +void S60VideoWidget::paintEvent(QPaintEvent *event) +{ + if (m_paintingEnabled && m_pixmap) { + QPainter painter(this); + if (m_pixmap->size() != m_contentRect.size()) + qWarning("pixmap size does not match expected value"); + painter.drawPixmap(m_contentRect.topLeft(), *m_pixmap); + } +} + +void S60VideoWidget::setVisible(bool visible) +{ + queueReactivateWindow(); + QWidget::setVisible(visible); +} + + +WId S60VideoWidget::videoWinId() const +{ + WId wid = 0; + if (internalWinId()) + wid = internalWinId(); + else if (parentWidget() && effectiveWinId()) + wid = effectiveWinId(); + return wid; +} + +void S60VideoWidget::setPixmap(const QPixmap *pixmap) +{ + m_pixmap = pixmap; + update(); +} + +void S60VideoWidget::setContentRect(const QRect &rect) +{ + if (m_contentRect != rect) { + m_contentRect = rect; + update(); + } +} + +void S60VideoWidget::setWindowBackgroundColor() +{ + if (WId wid = internalWinId()) + static_cast(wid->DrawableWindow())->SetBackgroundColor(TRgb(0, 0, 0, 255)); +} + +WId S60VideoWidget::topWinId() const +{ + return m_topWinId; +} + +void S60VideoWidget::setTopWinId(WId id) +{ + m_topWinId = id; + updateOrdinalPosition(); +#ifdef VIDEOOUTPUT_GRAPHICS_SURFACES + // This function may be called from a paint event, so defer any window + // manipulation until painting is complete. + QMetaObject::invokeMethod(this, "setWindowsNonFading", Qt::QueuedConnection); +#endif +} + +void S60VideoWidget::setOrdinalPosition(int ordinalPosition) +{ + m_ordinalPosition = ordinalPosition; + updateOrdinalPosition(); +} + +int S60VideoWidget::ordinalPosition() const +{ + return m_ordinalPosition; +} + +void S60VideoWidget::updateOrdinalPosition() +{ + if ((m_ordinalPosition != NullOrdinalPosition) && m_topWinId) { + if (WId wid = videoWinId()) { + int topOrdinalPosition = m_topWinId->DrawableWindow()->OrdinalPosition(); + queueReactivateWindow(); + wid->DrawableWindow()->SetOrdinalPosition(m_ordinalPosition + topOrdinalPosition); + } + } +} + +void S60VideoWidget::queueReactivateWindow() +{ + if (!parent()) { + if (QWidget *activeWindow = QApplication::activeWindow()) + QMetaObject::invokeMethod(this, "reactivateWindow", Qt::QueuedConnection, + Q_ARG(QWidget *, activeWindow)); + } +} + +void S60VideoWidget::reactivateWindow(QWidget *widget) +{ + widget->activateWindow(); +} + +void S60VideoWidget::setWindowsNonFading() +{ + winId()->DrawableWindow()->SetNonFading(ETrue); + if (m_topWinId) + m_topWinId->DrawableWindow()->SetNonFading(ETrue); +} + +void S60VideoWidget::beginNativePaintEvent(const QRect &rect) +{ + Q_UNUSED(rect) + emit beginVideoWidgetNativePaint(); +} + +void S60VideoWidget::endNativePaintEvent(const QRect &rect) +{ + Q_UNUSED(rect) + CCoeEnv::Static()->WsSession().Flush(); + emit endVideoWidgetNativePaint(); +} + +void S60VideoWidget::setPaintingEnabled(bool enabled) +{ + if (enabled) { +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + setAttribute(Qt::WA_OpaquePaintEvent, false); + setAttribute(Qt::WA_NoSystemBackground, false); + S60VideoOutputUtils::setReceiveNativePaintEvents(this, false); + S60VideoOutputUtils::setNativePaintMode(this, Default); +#else + S60VideoOutputUtils::setNativePaintMode(this, Default); +#endif // !VIDEOOUTPUT_GRAPHICS_SURFACES + } else { +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + setAttribute(Qt::WA_OpaquePaintEvent, true); + setAttribute(Qt::WA_NoSystemBackground, true); + S60VideoOutputUtils::setReceiveNativePaintEvents(this, true); + S60VideoOutputUtils::setNativePaintMode(this, ZeroFill); +#else + S60VideoOutputUtils::setNativePaintMode(this, Disable); +#endif // !VIDEOOUTPUT_GRAPHICS_SURFACES + winId(); // Create native window handle + } + m_paintingEnabled = enabled; + setWindowBackgroundColor(); +} + +void S60VideoWidget::setFullScreen(bool enabled) +{ + if (enabled) + showFullScreen(); + else + showMaximized(); +} + diff --git a/src/plugins/symbian/videooutput/s60videowidget.h b/src/plugins/symbian/videooutput/s60videowidget.h new file mode 100644 index 000000000..30e7a3bd0 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowidget.h @@ -0,0 +1,97 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOWIDGET_H +#define S60VIDEOWIDGET_H + +#include + +QT_USE_NAMESPACE + +class S60VideoWidget : public QWidget +{ + Q_OBJECT +public: + S60VideoWidget(QWidget *parent = 0); + ~S60VideoWidget(); + + // QWidget + bool event(QEvent *event); + void paintEvent(QPaintEvent *event); + void setVisible(bool visible); + + WId videoWinId() const; + void setPixmap(const QPixmap *pixmap); + void setWindowBackgroundColor(); + void setTopWinId(WId id); + WId topWinId() const; + void setOrdinalPosition(int ordinalPosition); + int ordinalPosition() const; + +public slots: + void beginNativePaintEvent(const QRect &rect); + void endNativePaintEvent(const QRect &rect); + void setPaintingEnabled(bool enabled); + void setFullScreen(bool enabled); + void setContentRect(const QRect &rect); + +signals: + void beginVideoWidgetNativePaint(); + void endVideoWidgetNativePaint(); + +private: + void updateOrdinalPosition(); + void queueReactivateWindow(); + +private slots: + void reactivateWindow(QWidget *window); + void setWindowsNonFading(); + +private: + const QPixmap *m_pixmap; + QRect m_contentRect; + bool m_paintingEnabled; + WId m_topWinId; + int m_ordinalPosition; +}; + +#endif // S60VIDEOWIDGET_H + diff --git a/src/plugins/symbian/videooutput/s60videowidgetcontrol.cpp b/src/plugins/symbian/videooutput/s60videowidgetcontrol.cpp new file mode 100644 index 000000000..46cb2963e --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowidgetcontrol.cpp @@ -0,0 +1,171 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videowidgetcontrol.h" +#include "s60videowidgetdisplay.h" + +S60VideoWidgetControl::S60VideoWidgetControl(QObject *parent) +: QVideoWidgetControl(parent) +, m_display(new S60VideoWidgetDisplay(this)) +{ + connect(m_display, SIGNAL(nativeSizeChanged(QSize)), + this, SIGNAL(nativeSizeChanged())); +} + +S60VideoWidgetControl::~S60VideoWidgetControl() +{ + +} + +QWidget *S60VideoWidgetControl::videoWidget() +{ + return m_display->widget(); +} + +Qt::AspectRatioMode S60VideoWidgetControl::aspectRatioMode() const +{ + return m_display->aspectRatioMode(); +} + +void S60VideoWidgetControl::setAspectRatioMode(Qt::AspectRatioMode ratio) +{ + m_display->setAspectRatioMode(ratio); +} + +bool S60VideoWidgetControl::isFullScreen() const +{ + return m_display->isFullScreen(); +} + +void S60VideoWidgetControl::setFullScreen(bool fullScreen) +{ + m_display->setFullScreen(fullScreen); +} + +int S60VideoWidgetControl::brightness() const +{ + return 0; +} + +void S60VideoWidgetControl::setBrightness(int brightness) +{ + Q_UNUSED(brightness); +} + +int S60VideoWidgetControl::contrast() const +{ + return 0; +} + +void S60VideoWidgetControl::setContrast(int contrast) +{ + Q_UNUSED(contrast); +} + +int S60VideoWidgetControl::hue() const +{ + return 0; +} + +void S60VideoWidgetControl::setHue(int hue) +{ + Q_UNUSED(hue); +} + +int S60VideoWidgetControl::saturation() const +{ + return 0; +} + +void S60VideoWidgetControl::setSaturation(int saturation) +{ + Q_UNUSED(saturation); +} + +S60VideoWidgetDisplay *S60VideoWidgetControl::display() const +{ + return m_display; +} + +void S60VideoWidgetControl::setTopWinId(WId id) +{ + m_display->setTopWinId(id); +} + +WId S60VideoWidgetControl::topWinId() const +{ + return m_display->topWinId(); +} + +int S60VideoWidgetControl::ordinalPosition() const +{ + return m_display->ordinalPosition(); +} + +void S60VideoWidgetControl::setOrdinalPosition(int ordinalPosition) +{ + m_display->setOrdinalPosition(ordinalPosition); +} + +const QRect &S60VideoWidgetControl::extentRect() const +{ + return m_display->explicitExtentRect(); +} + +void S60VideoWidgetControl::setExtentRect(const QRect &rect) +{ + m_display->setExplicitExtentRect(rect); +} + +QSize S60VideoWidgetControl::nativeSize() const +{ + return m_display->nativeSize(); +} + +qreal S60VideoWidgetControl::rotation() const +{ + return m_display->rotation(); +} + +void S60VideoWidgetControl::setRotation(qreal value) +{ + m_display->setRotation(value); +} diff --git a/src/plugins/symbian/videooutput/s60videowidgetcontrol.h b/src/plugins/symbian/videooutput/s60videowidgetcontrol.h new file mode 100644 index 000000000..eb103b6b8 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowidgetcontrol.h @@ -0,0 +1,131 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOWIDGETCONTROL_H +#define S60VIDEOWIDGETCONTROL_H + +#include + +QT_USE_NAMESPACE + +class S60VideoWidgetDisplay; + +class S60VideoWidgetControl : public QVideoWidgetControl +{ + Q_OBJECT + + /** + * WId of the topmost window in the application, used to calculate the + * absolute ordinal position of the video widget. + * This is used by the "window" implementation of QGraphicsVideoItem. + */ + Q_PROPERTY(WId topWinId READ topWinId WRITE setTopWinId) + + /** + * Ordinal position of the video widget, relative to the topmost window + * in the application. If both the topWinId property and the ordinalPosition + * property are set, the absolute ordinal position of the video widget is + * the sum of the topWinId ordinal position and the value of the + * ordinalPosition property. + * This is used by the "window" implementation of QGraphicsVideoItem. + */ + Q_PROPERTY(int ordinalPosition READ ordinalPosition WRITE setOrdinalPosition) + + /** + * Extent of the video, relative to this video widget. + * This is used by the "window" implementation of QGraphicsVideoItem. + */ + Q_PROPERTY(QRect extentRect READ extentRect WRITE setExtentRect) + + /** + * Native size of video. + * This is used by the "window" implementation of QGraphicsVideoItem. + */ + Q_PROPERTY(QSize nativeSize READ nativeSize) + + /** + * Rotation to be applied to video. + * Angle is measured in degrees, with positive values counter-clockwise. + * Zero is at 12 o'clock. + */ + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) + +public: + S60VideoWidgetControl(QObject *parent); + ~S60VideoWidgetControl(); + +public: + // QVideoWidgetControl + QWidget *videoWidget(); + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode ratio); + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + int brightness() const; + void setBrightness(int brightness); + int contrast() const; + void setContrast(int contrast); + int hue() const; + void setHue(int hue); + int saturation() const; + void setSaturation(int saturation); + + S60VideoWidgetDisplay *display() const; + + WId topWinId() const; + void setTopWinId(WId id); + int ordinalPosition() const; + void setOrdinalPosition(int ordinalPosition); + const QRect &extentRect() const; + void setExtentRect(const QRect &rect); + QSize nativeSize() const; + qreal rotation() const; + void setRotation(qreal value); + +signals: + void nativeSizeChanged(); + +private: + S60VideoWidgetDisplay *m_display; +}; + +#endif // S60VIDEOWIDGETCONTROL_H + diff --git a/src/plugins/symbian/videooutput/s60videowidgetdisplay.cpp b/src/plugins/symbian/videooutput/s60videowidgetdisplay.cpp new file mode 100644 index 000000000..de7440dbf --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowidgetdisplay.cpp @@ -0,0 +1,174 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videowidget.h" +#include "s60videowidgetdisplay.h" +#include +#include +#include +#include + +S60VideoWidgetDisplay::S60VideoWidgetDisplay(QObject *parent) +: S60VideoDisplay(parent) +, m_widget(new S60VideoWidget) +{ + connect(this, SIGNAL(paintingEnabledChanged(bool)), m_widget, SLOT(setPaintingEnabled(bool))); + connect(this, SIGNAL(fullScreenChanged(bool)), m_widget, SLOT(setFullScreen(bool))); + connect(this, SIGNAL(contentRectChanged(const QRect&)), m_widget, SLOT(setContentRect(const QRect &))); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + connect(m_widget, SIGNAL(beginVideoWidgetNativePaint()), this, SIGNAL(beginVideoWindowNativePaint())); + connect(m_widget, SIGNAL(endVideoWidgetNativePaint()), this, SIGNAL(endVideoWindowNativePaint())); +#endif + m_widget->installEventFilter(this); + m_widget->setPaintingEnabled(false); +} + +S60VideoWidgetDisplay::~S60VideoWidgetDisplay() +{ + // Notify observers that window is about to be destroyed + QScopedPointer widget(m_widget); + m_widget = 0; + emit windowHandleChanged(windowHandle()); + // Widget will be deleted by QScopedPointer +} + +bool S60VideoWidgetDisplay::eventFilter(QObject *object, QEvent *e) +{ + if (object == m_widget) { + switch (e->type()) { + case QEvent::ParentChange: + if (QWidget *parent = m_widget->parentWidget()) + parent->setProperty("_q_DummyWindowSurface", true); + break; + case QEvent::WinIdChange: + m_widget->setWindowBackgroundColor(); + emit windowHandleChanged(windowHandle()); + break; + case QEvent::Resize: + emit displayRectChanged(extentRect(), clipRect()); + break; +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + case QEvent::Move: + // TODO: this is insufficient - we also need to respond to changes in + // the position of ancestor widgets + emit displayRectChanged(extentRect(), clipRect()); + break; +#endif + case QEvent::Show: + emit windowHandleChanged(windowHandle()); + emit visibilityChanged(true); + break; + case QEvent::Hide: + emit visibilityChanged(false); + break; + default: + // Do nothing + break; + } + } + return false; +} + +WId S60VideoWidgetDisplay::winId() const +{ + return m_widget ? m_widget->videoWinId() : 0; +} + +QRect S60VideoWidgetDisplay::extentRect() const +{ + QRect rect; + if (const RWindow *window = windowHandle()) { + const TSize size = window ? window->Size() : TSize(); + if (m_explicitExtentRect.isValid()) + rect = m_explicitExtentRect; + else + rect = QRect(0, 0, size.iWidth, size.iHeight); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + const TPoint pos = window ? window->AbsPosition() : TPoint(); + rect.moveTopLeft(QPoint(pos.iX, pos.iY)); +#endif + } + return rect; +} + +void S60VideoWidgetDisplay::setFrame(const CFbsBitmap &bitmap) +{ + m_pixmap = QPixmap::fromSymbianCFbsBitmap(const_cast(&bitmap)); + m_widget->setPixmap(&m_pixmap); +} + +QWidget *S60VideoWidgetDisplay::widget() const +{ + return m_widget; +} + +void S60VideoWidgetDisplay::setTopWinId(WId id) +{ + m_widget->setTopWinId(id); +} + +WId S60VideoWidgetDisplay::topWinId() const +{ + return m_widget->topWinId(); +} + +void S60VideoWidgetDisplay::setOrdinalPosition(int ordinalPosition) +{ + m_widget->setOrdinalPosition(ordinalPosition); +} + +int S60VideoWidgetDisplay::ordinalPosition() const +{ + return m_widget->ordinalPosition(); +} + +const QRect &S60VideoWidgetDisplay::explicitExtentRect() const +{ + return m_explicitExtentRect; +} + +void S60VideoWidgetDisplay::setExplicitExtentRect(const QRect &rect) +{ + if (rect != m_explicitExtentRect) { + m_explicitExtentRect = rect; + emit displayRectChanged(extentRect(), clipRect()); + } +} diff --git a/src/plugins/symbian/videooutput/s60videowidgetdisplay.h b/src/plugins/symbian/videooutput/s60videowidgetdisplay.h new file mode 100644 index 000000000..d3d92d953 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowidgetdisplay.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOWIDGETDISPLAY_H +#define S60VIDEOWIDGETDISPLAY_H + +#include "s60videodisplay.h" +#include +#include + +class CFbsBitmap; +class S60VideoWidget; +class QWidget; + +QT_USE_NAMESPACE + +class S60VideoWidgetDisplay : public S60VideoDisplay +{ + Q_OBJECT +public: + S60VideoWidgetDisplay(QObject *parent); + ~S60VideoWidgetDisplay(); + + // QObject + bool eventFilter(QObject *object, QEvent *e); + + // S60VideoDisplay + WId winId() const; + QRect extentRect() const; + void setFrame(const CFbsBitmap &bitmap); + + QWidget *widget() const; + WId topWinId() const; + void setTopWinId(WId id); + void setOrdinalPosition(int ordinalPosition); + int ordinalPosition() const; + const QRect &explicitExtentRect() const; + void setExplicitExtentRect(const QRect &rect); + +private: + S60VideoWidget *m_widget; + QPixmap m_pixmap; + QRect m_explicitExtentRect; +}; + +#endif // S60VIDEOWIDGETDISPLAY_H + diff --git a/src/plugins/symbian/videooutput/s60videowindowcontrol.cpp b/src/plugins/symbian/videooutput/s60videowindowcontrol.cpp new file mode 100644 index 000000000..db33a5f32 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowindowcontrol.cpp @@ -0,0 +1,178 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videowindowcontrol.h" +#include "s60videowindowdisplay.h" + +S60VideoWindowControl::S60VideoWindowControl(QObject *parent) +: QVideoWindowControl(parent) +, m_display(new S60VideoWindowDisplay(this)) +{ + connect(m_display, SIGNAL(nativeSizeChanged(QSize)), + this, SIGNAL(nativeSizeChanged())); + connect(m_display, SIGNAL(fullScreenChanged(bool)), + this, SIGNAL(fullScreenChanged(bool))); +} + +S60VideoWindowControl::~S60VideoWindowControl() +{ + +} + +WId S60VideoWindowControl::winId() const +{ + return m_display->winId(); +} + +void S60VideoWindowControl::setWinId(WId id) +{ + m_display->setWinId(id); +} + +QRect S60VideoWindowControl::displayRect() const +{ + return m_display->displayRect(); +} + +void S60VideoWindowControl::setDisplayRect(const QRect &rect) +{ + m_display->setDisplayRect(rect); +} + +Qt::AspectRatioMode S60VideoWindowControl::aspectRatioMode() const +{ + return m_display->aspectRatioMode(); +} + +void S60VideoWindowControl::setAspectRatioMode(Qt::AspectRatioMode ratio) +{ + m_display->setAspectRatioMode(ratio); +} + +QSize S60VideoWindowControl::customAspectRatio() const +{ + return QSize(); +} + +void S60VideoWindowControl::setCustomAspectRatio(const QSize &customRatio) +{ + Q_UNUSED(customRatio); +} + +void S60VideoWindowControl::repaint() +{ + m_display->repaint(); +} + +int S60VideoWindowControl::brightness() const +{ + return 0; +} + +void S60VideoWindowControl::setBrightness(int brightness) +{ + Q_UNUSED(brightness) +} + +int S60VideoWindowControl::contrast() const +{ + return 0; +} + +void S60VideoWindowControl::setContrast(int contrast) +{ + Q_UNUSED(contrast) +} + +int S60VideoWindowControl::hue() const +{ + return 0; +} + +void S60VideoWindowControl::setHue(int hue) +{ + Q_UNUSED(hue) +} + +int S60VideoWindowControl::saturation() const +{ + return 0; +} + +void S60VideoWindowControl::setSaturation(int saturation) +{ + Q_UNUSED(saturation) +} + +bool S60VideoWindowControl::isFullScreen() const +{ + return m_display->isFullScreen(); +} + +void S60VideoWindowControl::setFullScreen(bool fullScreen) +{ + m_display->setFullScreen(fullScreen); +} + +QSize S60VideoWindowControl::nativeSize() const +{ + return m_display->nativeSize(); +} + +void S60VideoWindowControl::refreshDisplay() +{ + m_display->refreshDisplay(); +} + +S60VideoWindowDisplay *S60VideoWindowControl::display() const +{ + return m_display; +} + +qreal S60VideoWindowControl::rotation() const +{ + return m_display->rotation(); +} + +void S60VideoWindowControl::setRotation(qreal value) +{ + m_display->setRotation(value); +} diff --git a/src/plugins/symbian/videooutput/s60videowindowcontrol.h b/src/plugins/symbian/videooutput/s60videowindowcontrol.h new file mode 100644 index 000000000..a62d29a13 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowindowcontrol.h @@ -0,0 +1,102 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOWINDOWCONTROL_H +#define S60VIDEOWINDOWCONTROL_H + +#include + +class S60VideoWindowDisplay; + +QT_USE_NAMESPACE + +class S60VideoWindowControl : public QVideoWindowControl +{ + Q_OBJECT + + /** + * Rotation to be applied to video. + * Angle is measured in degrees, with positive values counter-clockwise. + * Zero is at 12 o'clock. + */ + Q_PROPERTY(qreal rotation READ rotation WRITE setRotation) + +public: + S60VideoWindowControl(QObject *parent); + ~S60VideoWindowControl(); + +public: + // QVideoWindowControl + WId winId() const; + void setWinId(WId id); + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + void repaint(); + QSize nativeSize() const; + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + QSize customAspectRatio() const; + void setCustomAspectRatio(const QSize &customRatio); + int brightness() const; + void setBrightness(int brightness); + int contrast() const; + void setContrast(int contrast); + int hue() const; + void setHue(int hue); + int saturation() const; + void setSaturation(int saturation); + + S60VideoWindowDisplay *display() const; + + qreal rotation() const; + void setRotation(qreal value); + +public slots: + void refreshDisplay(); + +private: + S60VideoWindowDisplay *m_display; +}; + +#endif // S60VIDEOWINDOWCONTROL_H + diff --git a/src/plugins/symbian/videooutput/s60videowindowdisplay.cpp b/src/plugins/symbian/videooutput/s60videowindowdisplay.cpp new file mode 100644 index 000000000..a8bdb8b4a --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowindowdisplay.cpp @@ -0,0 +1,140 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "s60videowindowdisplay.h" +#include "s60videooutpututils.h" +#include +#include +#include + +using namespace S60VideoOutputUtils; + +S60VideoWindowDisplay::S60VideoWindowDisplay(QObject *parent) +: S60VideoDisplay(parent) +, m_winId(0) +, m_bitmap(0) +{ + parent->setProperty("colorKey", Qt::transparent); +} + +S60VideoWindowDisplay::~S60VideoWindowDisplay() +{ + +} + +WId S60VideoWindowDisplay::winId() const +{ + return m_winId; +} + +QRect S60VideoWindowDisplay::extentRect() const +{ + QRect rect = displayRect(); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + if (RWindow *window = windowHandle()) { + const TPoint windowPos = window->AbsPosition(); + rect.translate(windowPos.iX, windowPos.iY); + } +#endif // VIDEOOUTPUT_GRAPHICS_SURFACES + return rect; +} + +void S60VideoWindowDisplay::setFrame(const CFbsBitmap &bitmap) +{ + m_bitmap = const_cast(&bitmap); + if (m_winId) { + // Blit the bitmap into the native window owned by m_winId + CWindowGc &gc = m_winId->SystemGc(); + RWindow *window = windowHandle(); + gc.Activate(*window); + const QPoint offsetQ = displayRect().topLeft() + contentRect().topLeft(); + const TPoint offsetT(offsetQ.x(), offsetQ.y()); + const TRect winRect(offsetT, m_bitmap->SizeInPixels()); + window->BeginRedraw(winRect); + gc.BitBlt(offsetT, m_bitmap); + window->EndRedraw(); + gc.Deactivate(); + } +} + +void S60VideoWindowDisplay::setWinId(WId id) +{ + if (m_winId != id) { + m_winId = id; + if (m_winId) { + static_cast(m_winId->DrawableWindow())->SetBackgroundColor(TRgb(0, 0, 0, 0)); +#ifndef VIDEOOUTPUT_GRAPHICS_SURFACES + if (QSysInfo::s60Version() >= QSysInfo::SV_S60_5_0) + S60VideoOutputUtils::setNativePaintMode(m_winId, BlitWriteAlpha); +#endif // !VIDEOOUTPUT_GRAPHICS_SURFACES + } + emit windowHandleChanged(windowHandle()); + } +} + +void S60VideoWindowDisplay::setDisplayRect(const QRect &rect) +{ + if (m_displayRect != rect) { + // If QGraphicsVideoItem moves out of screen, display rect is invalidated + if (rect == QRect(QPoint(-1,-1), QSize(1,1))) + emit visibilityChanged(false); + else + emit visibilityChanged(true); + m_displayRect = rect; + emit displayRectChanged(extentRect(), clipRect()); + } +} + +QRect S60VideoWindowDisplay::displayRect() const +{ + return m_displayRect; +} + +void S60VideoWindowDisplay::repaint() +{ + // TODO +} + +void S60VideoWindowDisplay::refreshDisplay() +{ + emit displayRectChanged(extentRect(), clipRect()); +} + diff --git a/src/plugins/symbian/videooutput/s60videowindowdisplay.h b/src/plugins/symbian/videooutput/s60videowindowdisplay.h new file mode 100644 index 000000000..8e749dcf3 --- /dev/null +++ b/src/plugins/symbian/videooutput/s60videowindowdisplay.h @@ -0,0 +1,73 @@ +/**************************************************************************** +** +** Copyright (C) 2011 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef S60VIDEOWINDOWDISPLAY_H +#define S60VIDEOWINDOWDISPLAY_H + +#include "s60videodisplay.h" + +QT_USE_NAMESPACE + +class S60VideoWindowDisplay : public S60VideoDisplay +{ +public: + S60VideoWindowDisplay(QObject *parent); + ~S60VideoWindowDisplay(); + + // S60VideoDisplay + WId winId() const; + QRect extentRect() const; + void setFrame(const CFbsBitmap &bitmap); + + void setWinId(WId id); + void setDisplayRect(const QRect &rect); + QRect displayRect() const; + void repaint(); + void refreshDisplay(); + +private: + WId m_winId; + QRect m_displayRect; + CFbsBitmap *m_bitmap; +}; + +#endif // S60VIDEOWINDOWDISPLAY_H + diff --git a/src/plugins/symbian/videooutput/videooutput.pri b/src/plugins/symbian/videooutput/videooutput.pri new file mode 100644 index 000000000..13aa7a0fc --- /dev/null +++ b/src/plugins/symbian/videooutput/videooutput.pri @@ -0,0 +1,37 @@ +INCLUDEPATH += $$PWD + +message("VideoOutput: using common implementation") + +contains(surfaces_s60_enabled, yes) { + message("VideoOutput: graphics surface rendering supported") + DEFINES += VIDEOOUTPUT_GRAPHICS_SURFACES +} else { + message("VideoOutput: no graphics surface rendering support - DSA only") +} + +exists($$[QT_INSTALL_HEADERS]/QtGui/private/qwidget_p.h) { + DEFINES += PRIVATE_QTGUI_HEADERS_AVAILABLE + message("VideoOutput: private QtGui headers are available") +} else { + message("VideoOutput: private QtGui headers not available - video and viewfinder may not be rendered correctly") +} + +HEADERS += $$PWD/s60videodisplay.h \ + $$PWD/s60videooutpututils.h \ + $$PWD/s60videowidget.h \ + $$PWD/s60videowidgetcontrol.h \ + $$PWD/s60videowidgetdisplay.h \ + $$PWD/s60videowindowcontrol.h \ + $$PWD/s60videowindowdisplay.h + +SOURCES += $$PWD/s60videodisplay.cpp \ + $$PWD/s60videooutpututils.cpp \ + $$PWD/s60videowidget.cpp \ + $$PWD/s60videowidgetcontrol.cpp \ + $$PWD/s60videowidgetdisplay.cpp \ + $$PWD/s60videowindowcontrol.cpp \ + $$PWD/s60videowindowdisplay.cpp + +LIBS *= -lcone +LIBS *= -lws32 + diff --git a/src/plugins/v4l/radio/radio.pri b/src/plugins/v4l/radio/radio.pri new file mode 100644 index 000000000..04c6720c5 --- /dev/null +++ b/src/plugins/v4l/radio/radio.pri @@ -0,0 +1,29 @@ +INCLUDEPATH += $$PWD + +maemo5 { + QT += dbus + + CONFIG += link_pkgconfig + + PKGCONFIG += gstreamer-0.10 + + LIBS += -lasound + + HEADERS += \ + $$PWD/v4lradiocontrol_maemo5.h \ + $$PWD/v4lradioservice.h + + SOURCES += \ + $$PWD/v4lradiocontrol_maemo5.cpp \ + $$PWD/v4lradioservice.cpp + +} else { + +HEADERS += \ + $$PWD/v4lradiocontrol.h \ + $$PWD/v4lradioservice.h + +SOURCES += \ + $$PWD/v4lradiocontrol.cpp \ + $$PWD/v4lradioservice.cpp +} diff --git a/src/plugins/v4l/radio/v4lradiocontrol.cpp b/src/plugins/v4l/radio/v4lradiocontrol.cpp new file mode 100644 index 000000000..1b698279a --- /dev/null +++ b/src/plugins/v4l/radio/v4lradiocontrol.cpp @@ -0,0 +1,538 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "v4lradiocontrol.h" +#include "v4lradioservice.h" + +#include + +#include + +#include +#include "linux/videodev2.h" + +#include +#include +#include +#include +#include +#include + +V4LRadioControl::V4LRadioControl(QObject *parent) + :QRadioTunerControl(parent) +{ + fd = -1; + initRadio(); + muted = false; + stereo = false; + m_error = false; + sig = 0; + currentBand = QRadioTuner::FM; + step = 100000; + scanning = false; + playTime.restart(); + timer = new QTimer(this); + timer->setInterval(200); + connect(timer,SIGNAL(timeout()),this,SLOT(search())); + timer->start(); +} + +V4LRadioControl::~V4LRadioControl() +{ + timer->stop(); + + if(fd > 0) + ::close(fd); +} + +bool V4LRadioControl::isAvailable() const +{ + return available; +} + +QtMultimediaKit::AvailabilityError V4LRadioControl::availabilityError() const +{ + if (fd > 0) + return QtMultimediaKit::NoError; + else + return QtMultimediaKit::ResourceError; +} + +QRadioTuner::State V4LRadioControl::state() const +{ + return fd > 0 ? QRadioTuner::ActiveState : QRadioTuner::StoppedState; +} + +QRadioTuner::Band V4LRadioControl::band() const +{ + return currentBand; +} + +bool V4LRadioControl::isBandSupported(QRadioTuner::Band b) const +{ + QRadioTuner::Band bnd = (QRadioTuner::Band)b; + switch(bnd) { + case QRadioTuner::FM: + if(freqMin <= 87500000 && freqMax >= 108000000) + return true; + break; + case QRadioTuner::LW: + if(freqMin <= 148500 && freqMax >= 283500) + return true; + case QRadioTuner::AM: + if(freqMin <= 520000 && freqMax >= 1610000) + return true; + default: + if(freqMin <= 1711000 && freqMax >= 30000000) + return true; + } + + return false; +} + +void V4LRadioControl::setBand(QRadioTuner::Band b) +{ + if(freqMin <= 87500000 && freqMax >= 108000000 && b == QRadioTuner::FM) { + // FM 87.5 to 108.0 MHz, except Japan 76-90 MHz + currentBand = (QRadioTuner::Band)b; + step = 100000; // 100kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 148500 && freqMax >= 283500 && b == QRadioTuner::LW) { + // LW 148.5 to 283.5 kHz, 9kHz channel spacing (Europe, Africa, Asia) + currentBand = (QRadioTuner::Band)b; + step = 1000; // 1kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 520000 && freqMax >= 1610000 && b == QRadioTuner::AM) { + // AM 520 to 1610 kHz, 9 or 10kHz channel spacing, extended 1610 to 1710 kHz + currentBand = (QRadioTuner::Band)b; + step = 1000; // 1kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 1711000 && freqMax >= 30000000 && b == QRadioTuner::SW) { + // SW 1.711 to 30.0 MHz, divided into 15 bands. 5kHz channel spacing + currentBand = (QRadioTuner::Band)b; + step = 500; // 500Hz steps + emit bandChanged(currentBand); + } + playTime.restart(); +} + +int V4LRadioControl::frequency() const +{ + return currentFreq; +} + +int V4LRadioControl::frequencyStep(QRadioTuner::Band b) const +{ + int step = 0; + + if(b == QRadioTuner::FM) + step = 100000; // 100kHz steps + else if(b == QRadioTuner::LW) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::AM) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::SW) + step = 500; // 500Hz steps + + return step; +} + +QPair V4LRadioControl::frequencyRange(QRadioTuner::Band b) const +{ + if(b == QRadioTuner::AM) + return qMakePair(520000,1710000); + else if(b == QRadioTuner::FM) + return qMakePair(87500000,108000000); + else if(b == QRadioTuner::SW) + return qMakePair(1711111,30000000); + else if(b == QRadioTuner::LW) + return qMakePair(148500,283500); + + return qMakePair(0,0); +} + +void V4LRadioControl::setFrequency(int frequency) +{ + qint64 f = frequency; + + v4l2_frequency freq; + + if(frequency < freqMin) + f = freqMax; + if(frequency > freqMax) + f = freqMin; + + if(fd > 0) { + memset( &freq, 0, sizeof( freq ) ); + // Use the first tuner + freq.tuner = 0; + if ( ioctl( fd, VIDIOC_G_FREQUENCY, &freq ) >= 0 ) { + if(low) { + // For low, freq in units of 62.5Hz, so convert from Hz to units. + freq.frequency = (int)(f/62.5); + } else { + // For high, freq in units of 62.5kHz, so convert from Hz to units. + freq.frequency = (int)(f/62500); + } + ioctl( fd, VIDIOC_S_FREQUENCY, &freq ); + currentFreq = f; + playTime.restart(); + emit frequencyChanged(currentFreq); + } + } + playTime.restart(); +} + +bool V4LRadioControl::isStereo() const +{ + return stereo; +} + +QRadioTuner::StereoMode V4LRadioControl::stereoMode() const +{ + return QRadioTuner::Auto; +} + +void V4LRadioControl::setStereoMode(QRadioTuner::StereoMode mode) +{ + bool stereo = true; + + if(mode == QRadioTuner::ForceMono) + stereo = false; + + v4l2_tuner tuner; + + memset( &tuner, 0, sizeof( tuner ) ); + + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) >= 0 ) { + if(stereo) + tuner.audmode = V4L2_TUNER_MODE_STEREO; + else + tuner.audmode = V4L2_TUNER_MODE_MONO; + + if ( ioctl( fd, VIDIOC_S_TUNER, &tuner ) >= 0 ) { + emit stereoStatusChanged(stereo); + } + } +} + +int V4LRadioControl::signalStrength() const +{ + v4l2_tuner tuner; + + // Return the first tuner founds signal strength. + for ( int index = 0; index < tuners; ++index ) { + memset( &tuner, 0, sizeof( tuner ) ); + tuner.index = index; + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) < 0 ) + continue; + if ( tuner.type != V4L2_TUNER_RADIO ) + continue; + // percentage signal strength + return tuner.signal*100/65535; + } + + return 0; +} + +int V4LRadioControl::volume() const +{ + v4l2_queryctrl queryctrl; + + if(fd > 0) { + memset( &queryctrl, 0, sizeof( queryctrl ) ); + queryctrl.id = V4L2_CID_AUDIO_VOLUME; + if ( ioctl( fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) { + if(queryctrl.maximum == 0) { + return vol; + } else { + // percentage volume returned + return queryctrl.default_value*100/queryctrl.maximum; + } + } + } + return 0; +} + +void V4LRadioControl::setVolume(int volume) +{ + v4l2_queryctrl queryctrl; + + if(fd > 0) { + memset( &queryctrl, 0, sizeof( queryctrl ) ); + queryctrl.id = V4L2_CID_AUDIO_VOLUME; + if ( ioctl( fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) { + v4l2_control control; + + if(queryctrl.maximum > 0) { + memset( &control, 0, sizeof( control ) ); + control.id = V4L2_CID_AUDIO_VOLUME; + control.value = volume*queryctrl.maximum/100; + ioctl( fd, VIDIOC_S_CTRL, &control ); + } else { + setVol(volume); + } + emit volumeChanged(volume); + } + } +} + +bool V4LRadioControl::isMuted() const +{ + return muted; +} + +void V4LRadioControl::setMuted(bool muted) +{ + v4l2_queryctrl queryctrl; + + if(fd > 0) { + memset( &queryctrl, 0, sizeof( queryctrl ) ); + queryctrl.id = V4L2_CID_AUDIO_MUTE; + if ( ioctl( fd, VIDIOC_QUERYCTRL, &queryctrl ) >= 0 ) { + v4l2_control control; + memset( &control, 0, sizeof( control ) ); + control.id = V4L2_CID_AUDIO_MUTE; + control.value = (muted ? queryctrl.maximum : queryctrl.minimum ); + ioctl( fd, VIDIOC_S_CTRL, &control ); + this->muted = muted; + emit mutedChanged(muted); + } + } +} + +bool V4LRadioControl::isSearching() const +{ + return scanning; +} + +void V4LRadioControl::cancelSearch() +{ + scanning = false; + timer->stop(); +} + +void V4LRadioControl::searchForward() +{ + // Scan up + if(scanning) { + cancelSearch(); + return; + } + scanning = true; + forward = true; + timer->start(); +} + +void V4LRadioControl::searchBackward() +{ + // Scan down + if(scanning) { + cancelSearch(); + return; + } + scanning = true; + forward = false; + timer->start(); +} + +void V4LRadioControl::start() +{ +} + +void V4LRadioControl::stop() +{ +} + +QRadioTuner::Error V4LRadioControl::error() const +{ + if(m_error) + return QRadioTuner::OpenError; + + return QRadioTuner::NoError; +} + +QString V4LRadioControl::errorString() const +{ + return QString(); +} + +void V4LRadioControl::search() +{ + int signal = signalStrength(); + if(sig != signal) { + sig = signal; + emit signalStrengthChanged(sig); + } + + if(!scanning) return; + + if (signal > 25) { + cancelSearch(); + return; + } + + if(forward) { + setFrequency(currentFreq+step); + } else { + setFrequency(currentFreq-step); + } +} + +bool V4LRadioControl::initRadio() +{ + v4l2_tuner tuner; + v4l2_input input; + v4l2_frequency freq; + v4l2_capability cap; + + low = false; + available = false; + freqMin = freqMax = currentFreq = 0; + + fd = ::open("/dev/radio0", O_RDWR); + + if(fd != -1) { + // Capabilities + memset( &cap, 0, sizeof( cap ) ); + if(::ioctl(fd, VIDIOC_QUERYCAP, &cap ) >= 0) { + if(((cap.capabilities & V4L2_CAP_RADIO) == 0) && ((cap.capabilities & V4L2_CAP_AUDIO) == 0)) + available = true; + } + + // Tuners + memset( &input, 0, sizeof( input ) ); + tuners = 0; + for ( ;; ) { + memset( &input, 0, sizeof( input ) ); + input.index = tuners; + if ( ioctl( fd, VIDIOC_ENUMINPUT, &input ) < 0 ) + break; + ++tuners; + } + + // Freq bands + for ( int index = 0; index < tuners; ++index ) { + memset( &tuner, 0, sizeof( tuner ) ); + tuner.index = index; + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) < 0 ) + continue; + if ( tuner.type != V4L2_TUNER_RADIO ) + continue; + if ( ( tuner.capability & V4L2_TUNER_CAP_LOW ) != 0 ) { + // Units are 1/16th of a kHz. + low = true; + } + if(low) { + freqMin = tuner.rangelow * 62.5; + freqMax = tuner.rangehigh * 62.5; + } else { + freqMin = tuner.rangelow * 62500; + freqMax = tuner.rangehigh * 62500; + } + } + + // frequency + memset( &freq, 0, sizeof( freq ) ); + if(::ioctl(fd, VIDIOC_G_FREQUENCY, &freq ) >= 0) { + if ( ((int)freq.frequency) != -1 ) { // -1 means not set. + if(low) + currentFreq = freq.frequency * 62.5; + else + currentFreq = freq.frequency * 62500; + } + } + + // stereo + bool stereo = false; + memset( &tuner, 0, sizeof( tuner ) ); + if ( ioctl( fd, VIDIOC_G_TUNER, &tuner ) >= 0 ) { + if((tuner.rxsubchans & V4L2_TUNER_SUB_STEREO) != 0) + stereo = true; + } + + vol = getVol(); + + return true; + } + m_error = true; + emit error(); + + return false; +} + +void V4LRadioControl::setVol(int v) +{ + int fd = ::open( "/dev/mixer", O_RDWR, 0 ); + if ( fd < 0 ) + return; + int volume = v; + if ( volume < 0 ) + volume = 0; + else if ( volume > 100 ) + volume = 100; + vol = volume; + volume += volume << 8; + ::ioctl( fd, MIXER_WRITE(SOUND_MIXER_VOLUME), &volume ); + ::close( fd ); +} + +int V4LRadioControl::getVol() +{ + int fd = ::open( "/dev/mixer", O_RDWR, 0 ); + if ( fd >= 0 ) { + int volume = 0; + ::ioctl( fd, MIXER_READ(SOUND_MIXER_VOLUME), &volume ); + int left = ( volume & 0xFF ); + int right = ( ( volume >> 8 ) & 0xFF ); + if ( left > right ) + vol = left; + else + vol = right; + ::close( fd ); + return vol; + } + return 0; +} + diff --git a/src/plugins/v4l/radio/v4lradiocontrol.h b/src/plugins/v4l/radio/v4lradiocontrol.h new file mode 100644 index 000000000..3349be236 --- /dev/null +++ b/src/plugins/v4l/radio/v4lradiocontrol.h @@ -0,0 +1,134 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef V4LRADIOCONTROL_H +#define V4LRADIOCONTROL_H + +#include +#include +#include + +#include + +#include +#include +#include +#include + +QT_USE_NAMESPACE + +class V4LRadioService; + +class V4LRadioControl : public QRadioTunerControl +{ + Q_OBJECT +public: + V4LRadioControl(QObject *parent = 0); + ~V4LRadioControl(); + + bool isAvailable() const; + QtMultimediaKit::AvailabilityError availabilityError() const; + + QRadioTuner::State state() const; + + QRadioTuner::Band band() const; + void setBand(QRadioTuner::Band b); + bool isBandSupported(QRadioTuner::Band b) const; + + int frequency() const; + int frequencyStep(QRadioTuner::Band b) const; + QPair frequencyRange(QRadioTuner::Band b) const; + void setFrequency(int frequency); + + bool isStereo() const; + QRadioTuner::StereoMode stereoMode() const; + void setStereoMode(QRadioTuner::StereoMode mode); + + int signalStrength() const; + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + bool isSearching() const; + void cancelSearch(); + + void searchForward(); + void searchBackward(); + + void start(); + void stop(); + + QRadioTuner::Error error() const; + QString errorString() const; + +private slots: + void search(); + +private: + bool initRadio(); + void setVol(int v); + int getVol(); + + int fd; + + bool m_error; + bool muted; + bool stereo; + bool low; + bool available; + int tuners; + int step; + int vol; + int sig; + bool scanning; + bool forward; + QTimer* timer; + QRadioTuner::Band currentBand; + qint64 freqMin; + qint64 freqMax; + qint64 currentFreq; + QTime playTime; +}; + +#endif diff --git a/src/plugins/v4l/radio/v4lradiocontrol_maemo5.cpp b/src/plugins/v4l/radio/v4lradiocontrol_maemo5.cpp new file mode 100644 index 000000000..f3dea9d63 --- /dev/null +++ b/src/plugins/v4l/radio/v4lradiocontrol_maemo5.cpp @@ -0,0 +1,755 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "v4lradiocontrol_maemo5.h" +#include "v4lradioservice.h" + +#include "linux/videodev2.h" +#include +#include +#include +#include +#include +#include +#include + +#define HEADPHONE_STATE_FILE "/sys/devices/platform/gpio-switch/headphone/state" +#define HEADPHONE_CONNECTED_STATE "connected" +#define HEADPHONE_DISCONNECTED_STATE "disconnected" + +#define FMRXENABLER_DBUS_SERVICE "de.pycage.FMRXEnabler" +#define FMRXENABLER_DBUS_OBJ_PATH "/de/pycage/FMRXEnabler" +#define FMRXENABLER_DBUS_IFACE_NAME "de.pycage.FMRXEnabler" + +gboolean +state_file_changed(GIOChannel* source, GIOCondition /*condition*/, gpointer data) +{ + V4LRadioControl* radioControl = (V4LRadioControl*)data; + gchar* result; + + g_io_channel_seek_position(source, 0, G_SEEK_SET, NULL); + g_io_channel_read_line(source, &result, NULL, NULL, NULL); + g_strstrip(result); + + if (g_ascii_strcasecmp(result, HEADPHONE_DISCONNECTED_STATE) == 0) { + radioControl->enablePipeline(false); + } else if (g_ascii_strcasecmp(result, HEADPHONE_CONNECTED_STATE) == 0) { + // Wait 400ms until audio is routed again to headphone to prevent sound coming from speakers + QTimer::singleShot(400,radioControl,SLOT(enablePipeline())); + } + +#ifdef MULTIMEDIA_MAEMO_DEBUG + qDebug() << "Headphone is now" << result; +#endif + + g_free(result); + return true; +} + +V4LRadioControl::V4LRadioControl(QObject *parent) + : QRadioTunerControl(parent) + , fd(1) + , m_error(false) + , muted(false) + , stereo(false) + , step(100000) + , sig(0) + , scanning(false) + , currentBand(QRadioTuner::FM) + , pipeline(0) +{ + if (QDBusConnection::systemBus().isConnected()) { + FMRXEnablerIFace = new QDBusInterface(FMRXENABLER_DBUS_SERVICE, + FMRXENABLER_DBUS_OBJ_PATH, + FMRXENABLER_DBUS_IFACE_NAME, + QDBusConnection::systemBus()); + } + + createGstPipeline(); + + GIOChannel* headphoneStateFile = NULL; + headphoneStateFile = g_io_channel_new_file(HEADPHONE_STATE_FILE, "r", NULL); + if (headphoneStateFile != NULL) { + g_io_add_watch(headphoneStateFile, G_IO_PRI, state_file_changed, this); + } else { +#ifdef MULTIMEDIA_MAEMO_DEBUG + qWarning() << QString("File %1 can't be read!").arg(HEADPHONE_STATE_FILE) ; + qWarning() << "Monitoring headphone state isn't possible!"; +#endif + } + + enableFMRX(); + initRadio(); + setupHeadPhone(); + + setMuted(false); + + timer = new QTimer(this); + timer->setInterval(200); + connect(timer,SIGNAL(timeout()),this,SLOT(search())); + + tickTimer = new QTimer(this); + tickTimer->setInterval(10000); + connect(tickTimer,SIGNAL(timeout()),this,SLOT(enableFMRX())); + tickTimer->start(); +} + +V4LRadioControl::~V4LRadioControl() +{ + if (pipeline) + { + gst_element_set_state (pipeline, GST_STATE_NULL); + gst_object_unref (GST_OBJECT (pipeline)); + } + + if(fd > 0) + ::close(fd); +} + +void V4LRadioControl::enablePipeline(bool enable) +{ + if (enable == true) + gst_element_set_state (pipeline, GST_STATE_PLAYING); + else + gst_element_set_state (pipeline, GST_STATE_NULL); +} + +void V4LRadioControl::enableFMRX() +{ + if (FMRXEnablerIFace && FMRXEnablerIFace->isValid()) { + FMRXEnablerIFace->call("request"); // Return value ignored + } +} + +// Workaround to capture sound from the PGA line and play it back using gstreamer +// because N900 doesn't output sound directly to speakers +bool V4LRadioControl::createGstPipeline() +{ + GstElement *source, *sink; + + gst_init (NULL, NULL); + pipeline = gst_pipeline_new("my-pipeline"); + + source = gst_element_factory_make ("pulsesrc", "source"); + sink = gst_element_factory_make ("pulsesink", "sink"); + + gst_bin_add_many (GST_BIN (pipeline), source, sink, (char *)NULL); + + if (!gst_element_link_many (source, sink, (char *)NULL)) { + return false; + } + + gst_element_set_state (pipeline, GST_STATE_PLAYING); + + return true; +} + +bool V4LRadioControl::isAvailable() const +{ + return available; +} + +QtMultimediaKit::AvailabilityError V4LRadioControl::availabilityError() const +{ + if (fd > 0) + return QtMultimediaKit::NoError; + else + return QtMultimediaKit::ResourceError; +} + +QRadioTuner::State V4LRadioControl::state() const +{ + return fd > 0 ? QRadioTuner::ActiveState : QRadioTuner::StoppedState; +} + +QRadioTuner::Band V4LRadioControl::band() const +{ + return currentBand; +} + +bool V4LRadioControl::isBandSupported(QRadioTuner::Band b) const +{ + QRadioTuner::Band bnd = (QRadioTuner::Band)b; + switch(bnd) { + case QRadioTuner::FM: + if(freqMin <= 87500000 && freqMax >= 108000000) + return true; + break; + case QRadioTuner::LW: + if(freqMin <= 148500 && freqMax >= 283500) + return true; + case QRadioTuner::AM: + if(freqMin <= 520000 && freqMax >= 1610000) + return true; + default: + if(freqMin <= 1711000 && freqMax >= 30000000) + return true; + } + + return false; +} + +void V4LRadioControl::setBand(QRadioTuner::Band b) +{ + if(freqMin <= 87500000 && freqMax >= 108000000 && b == QRadioTuner::FM) { + // FM 87.5 to 108.0 MHz, except Japan 76-90 MHz + currentBand = (QRadioTuner::Band)b; + step = 100000; // 100kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 148500 && freqMax >= 283500 && b == QRadioTuner::LW) { + // LW 148.5 to 283.5 kHz, 9kHz channel spacing (Europe, Africa, Asia) + currentBand = (QRadioTuner::Band)b; + step = 1000; // 1kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 520000 && freqMax >= 1610000 && b == QRadioTuner::AM) { + // AM 520 to 1610 kHz, 9 or 10kHz channel spacing, extended 1610 to 1710 kHz + currentBand = (QRadioTuner::Band)b; + step = 1000; // 1kHz steps + emit bandChanged(currentBand); + + } else if(freqMin <= 1711000 && freqMax >= 30000000 && b == QRadioTuner::SW) { + // SW 1.711 to 30.0 MHz, divided into 15 bands. 5kHz channel spacing + currentBand = (QRadioTuner::Band)b; + step = 500; // 500Hz steps + emit bandChanged(currentBand); + } +} + +int V4LRadioControl::frequency() const +{ + return currentFreq; +} + +int V4LRadioControl::frequencyStep(QRadioTuner::Band b) const +{ + int step = 0; + + if(b == QRadioTuner::FM) + step = 100000; // 100kHz steps + else if(b == QRadioTuner::LW) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::AM) + step = 1000; // 1kHz steps + else if(b == QRadioTuner::SW) + step = 500; // 500Hz steps + + return step; +} + +QPair V4LRadioControl::frequencyRange(QRadioTuner::Band b) const +{ + if(b == QRadioTuner::AM) + return qMakePair(520000,1710000); + else if(b == QRadioTuner::FM) + return qMakePair(87500000,108000000); + else if(b == QRadioTuner::SW) + return qMakePair(1711111,30000000); + else if(b == QRadioTuner::LW) + return qMakePair(148500,283500); + + return qMakePair(0,0); +} + +void V4LRadioControl::setFrequency(int frequency) +{ + qint64 f = frequency; + + v4l2_frequency freq; + + if(frequency < freqMin) + f = freqMax; + if(frequency > freqMax) + f = freqMin; + + if(fd > 0) { + memset(&freq, 0, sizeof(freq)); + // Use the first tuner + freq.tuner = 0; + if (::ioctl(fd, VIDIOC_G_FREQUENCY, &freq) >= 0) { + if(low) { + // For low, freq in units of 62.5Hz, so convert from Hz to units. + freq.frequency = (int)(f/62.5); + } else { + // For high, freq in units of 62.5kHz, so convert from Hz to units. + freq.frequency = (int)(f/62500); + } + ::ioctl(fd, VIDIOC_S_FREQUENCY, &freq); + currentFreq = f; + emit frequencyChanged(currentFreq); + + int signal = signalStrength(); + if(sig != signal) { + sig = signal; + emit signalStrengthChanged(sig); + } + } + } +} + +bool V4LRadioControl::isStereo() const +{ + return stereo; +} + +QRadioTuner::StereoMode V4LRadioControl::stereoMode() const +{ + return QRadioTuner::Auto; +} + +void V4LRadioControl::setStereoMode(QRadioTuner::StereoMode mode) +{ + bool stereo = true; + + if(mode == QRadioTuner::ForceMono) + stereo = false; + + v4l2_tuner tuner; + + memset(&tuner, 0, sizeof(tuner)); + + if (ioctl(fd, VIDIOC_G_TUNER, &tuner) >= 0) { + if(stereo) + tuner.audmode = V4L2_TUNER_MODE_STEREO; + else + tuner.audmode = V4L2_TUNER_MODE_MONO; + + if (ioctl(fd, VIDIOC_S_TUNER, &tuner) >= 0) { + emit stereoStatusChanged(stereo); + } + } +} + +int V4LRadioControl::signalStrength() const +{ + v4l2_tuner tuner; + + tuner.index = 0; + if (ioctl(fd, VIDIOC_G_TUNER, &tuner) < 0 || tuner.type != V4L2_TUNER_RADIO) + return 0; + return tuner.signal*100/65535; +} + +int V4LRadioControl::vol(snd_hctl_elem_t *elem) const +{ + const QString card("hw:0"); + int err; + snd_ctl_elem_id_t *id; + snd_ctl_elem_info_t *info; + snd_ctl_elem_value_t *control; + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_value_alloca(&control); + if ((err = snd_hctl_elem_info(elem, info)) < 0) { + return 0; + } + + snd_hctl_elem_get_id(elem, id); + snd_hctl_elem_read(elem, control); + + return snd_ctl_elem_value_get_integer(control, 0); +} + +int V4LRadioControl::volume() const +{ + const QString ctlName("Line DAC Playback Volume"); + const QString card("hw:0"); + + int volume = 0; + int err; + static snd_ctl_t *handle = NULL; + snd_ctl_elem_info_t *info; + snd_ctl_elem_id_t *id; + snd_ctl_elem_value_t *control; + + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_value_alloca(&control); + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* MIXER */ + + snd_ctl_elem_id_set_name(id, ctlName.toAscii()); + + if ((err = snd_ctl_open(&handle, card.toAscii(), 0)) < 0) { + return 0; + } + + snd_ctl_elem_info_set_id(info, id); + if ((err = snd_ctl_elem_info(handle, info)) < 0) { + snd_ctl_close(handle); + handle = NULL; + return 0; + } + + snd_ctl_elem_info_get_id(info, id); /* FIXME: Remove it when hctl find works ok !!! */ + snd_ctl_elem_value_set_id(control, id); + + snd_ctl_close(handle); + handle = NULL; + + snd_hctl_t *hctl; + snd_hctl_elem_t *elem; + if ((err = snd_hctl_open(&hctl, card.toAscii(), 0)) < 0) { + return 0; + } + if ((err = snd_hctl_load(hctl)) < 0) { + return 0; + } + elem = snd_hctl_find_elem(hctl, id); + if (elem) + volume = vol(elem); + + snd_hctl_close(hctl); + + return (volume/118.0) * 100; +} + +void V4LRadioControl::setVolume(int volume) +{ + int vol = (volume / 100.0) * 118; // 118 is a headphone max setting + callAmixer("Line DAC Playback Volume", QString().setNum(vol)+QString(",")+QString().setNum(vol)); +} + +bool V4LRadioControl::isMuted() const +{ + return muted; +} + +void V4LRadioControl::setMuted(bool muted) +{ + struct v4l2_control vctrl; + vctrl.id = V4L2_CID_AUDIO_MUTE; + vctrl.value = muted ? 1 : 0; + ioctl(fd, VIDIOC_S_CTRL, &vctrl); +} + +void V4LRadioControl::setupHeadPhone() +{ + QMap settings; + + settings["Jack Function"] = "Headset"; + settings["Left DAC_L1 Mixer HP Switch"] = "off"; + settings["Right DAC_R1 Mixer HP Switch"] = "off"; + settings["Line DAC Playback Switch"] = "on"; + settings["Line DAC Playback Volume"] = "118,118"; // Volume is set to 100% + settings["HPCOM DAC Playback Switch"] = "off"; + settings["Left DAC_L1 Mixer HP Switch"] = "off"; + settings["Left DAC_L1 Mixer Line Switch"] = "on"; + settings["Right DAC_R1 Mixer HP Switch"] = "off"; + settings["Right DAC_R1 Mixer Line Switch"] = "on"; + settings["Speaker Function"] = "Off"; + + settings["Input Select"] = "ADC"; + settings["Left PGA Mixer Line1L Switch"] = "off"; + settings["Right PGA Mixer Line1L Switch"] = "off"; + settings["Right PGA Mixer Line1R Switch"] = "off"; + settings["PGA Capture Volume"] = "0,0"; + + settings["PGA Capture Switch"] = "on"; + + settings["Left PGA Mixer Line2L Switch"] = "on"; + settings["Right PGA Mixer Line2R Switch"] = "on"; + + QMapIterator i(settings); + while (i.hasNext()) { + i.next(); + callAmixer(i.key(), i.value()); + } +} + +void V4LRadioControl::callAmixer(const QString& target, const QString& value) +{ + int err; + long tmp; + unsigned int count; + static snd_ctl_t *handle = NULL; + QString card("hw:0"); + snd_ctl_elem_info_t *info; + snd_ctl_elem_id_t *id; + snd_ctl_elem_value_t *control; + snd_ctl_elem_type_t type; + + snd_ctl_elem_info_alloca(&info); + snd_ctl_elem_id_alloca(&id); + snd_ctl_elem_value_alloca(&control); + snd_ctl_elem_id_set_interface(id, SND_CTL_ELEM_IFACE_MIXER); /* MIXER */ + + // in amixer parse func + // char target[64]; + // e.g. PGA CAPTure Switch + snd_ctl_elem_id_set_name(id, target.toAscii()); + + if (handle == NULL && (err = snd_ctl_open(&handle, card.toAscii(), 0)) < 0) + { + return; + } + + snd_ctl_elem_info_set_id(info, id); + if ((err = snd_ctl_elem_info(handle, info)) < 0) + { + snd_ctl_close(handle); + handle = NULL; + return; + } + + snd_ctl_elem_info_get_id(info, id); /* FIXME: Remove it when hctl find works ok !!! */ + type = snd_ctl_elem_info_get_type(info); + count = snd_ctl_elem_info_get_count(info); + + snd_ctl_elem_value_set_id(control, id); + + tmp = 0; + for (uint idx = 0; idx < count && idx < 128; idx++) + { + switch (type) + { + case SND_CTL_ELEM_TYPE_BOOLEAN: +#ifdef MULTIMEDIA_MAEMO_DEBUG + qDebug() << "SND_CTL_ELEM_TYPE_BOOLEAN" << SND_CTL_ELEM_TYPE_BOOLEAN; +#endif + if ((value == "on") ||(value == "1")) + { + tmp = 1; + } + snd_ctl_elem_value_set_boolean(control, idx, tmp); + break; + case SND_CTL_ELEM_TYPE_ENUMERATED: + tmp = getEnumItemIndex(handle, info, value); + snd_ctl_elem_value_set_enumerated(control, idx, tmp); + break; + case SND_CTL_ELEM_TYPE_INTEGER: +#ifdef MULTIMEDIA_MAEMO_DEBUG + qDebug() << "SND_CTL_ELEM_TYPE_INTEGER" << SND_CTL_ELEM_TYPE_INTEGER; +#endif + tmp = atoi(value.toAscii()); + if (tmp < snd_ctl_elem_info_get_min(info)) + tmp = snd_ctl_elem_info_get_min(info); + else if (tmp > snd_ctl_elem_info_get_max(info)) + tmp = snd_ctl_elem_info_get_max(info); + snd_ctl_elem_value_set_integer(control, idx, tmp); + break; + default: + break; + + } + } + + if ((err = snd_ctl_elem_write(handle, control)) < 0) { + snd_ctl_close(handle); + handle = NULL; + return; + } + + snd_ctl_close(handle); + handle = NULL; +} + + +int V4LRadioControl::getEnumItemIndex(snd_ctl_t *handle, snd_ctl_elem_info_t *info, + const QString &value) +{ + int items, i; + + items = snd_ctl_elem_info_get_items(info); + if (items <= 0) + return -1; + + for (i = 0; i < items; i++) + { + snd_ctl_elem_info_set_item(info, i); + if (snd_ctl_elem_info(handle, info) < 0) + return -1; + QString name = snd_ctl_elem_info_get_item_name(info); + if(name == value) + { + return i; + } + } + return -1; +} + +bool V4LRadioControl::isSearching() const +{ + return scanning; +} + +void V4LRadioControl::cancelSearch() +{ + scanning = false; + timer->stop(); +} + +void V4LRadioControl::searchForward() +{ + // Scan up + if(scanning) { + cancelSearch(); + return; + } + scanning = true; + forward = true; + timer->start(); +} + +void V4LRadioControl::searchBackward() +{ + // Scan down + if(scanning) { + cancelSearch(); + return; + } + scanning = true; + forward = false; + timer->start(); +} + +void V4LRadioControl::start() +{ +} + +void V4LRadioControl::stop() +{ +} + +QRadioTuner::Error V4LRadioControl::error() const +{ + if(m_error) + return QRadioTuner::OpenError; + + return QRadioTuner::NoError; +} + +QString V4LRadioControl::errorString() const +{ + return QString(); +} + +void V4LRadioControl::search() +{ + if(!scanning) return; + + if(forward) { + setFrequency(currentFreq+step); + } else { + setFrequency(currentFreq-step); + } + + int signal = signalStrength(); + if(sig != signal) { + sig = signal; + emit signalStrengthChanged(sig); + } + + if (signal > 25) { + cancelSearch(); + return; + } +} + +bool V4LRadioControl::initRadio() +{ + v4l2_tuner tuner; + v4l2_frequency freq; + v4l2_capability cap; + + low = false; + available = false; + freqMin = freqMax = currentFreq = 0; + + fd = ::open("/dev/radio1", O_RDWR); + + if(fd != -1) { + // Capabilities + memset(&cap, 0, sizeof(cap)); + if(::ioctl(fd, VIDIOC_QUERYCAP, &cap ) >= 0) { + available = true; + } + + tuner.index = 0; + + if (ioctl(fd, VIDIOC_G_TUNER, &tuner) < 0) { + return false; + } + + if (tuner.type != V4L2_TUNER_RADIO) + return false; + + if ((tuner.capability & V4L2_TUNER_CAP_LOW) != 0) { + // Units are 1/16th of a kHz. + low = true; + } + + if(low) { + freqMin = tuner.rangelow * 62.5; + freqMax = tuner.rangehigh * 62.5; + } else { + freqMin = tuner.rangelow * 62500; + freqMax = tuner.rangehigh * 62500; + } + + // frequency + memset(&freq, 0, sizeof(freq)); + + if(::ioctl(fd, VIDIOC_G_FREQUENCY, &freq) >= 0) { + if (((int)freq.frequency) != -1) { // -1 means not set. + if(low) + currentFreq = freq.frequency * 62.5; + else + currentFreq = freq.frequency * 62500; + } + } + + // stereo + bool stereo = false; + memset(&tuner, 0, sizeof(tuner)); + if (ioctl(fd, VIDIOC_G_TUNER, &tuner) >= 0) { + if((tuner.rxsubchans & V4L2_TUNER_SUB_STEREO) != 0) + stereo = true; + } + + return true; + } + + m_error = true; + emit error(); + + return false; +} diff --git a/src/plugins/v4l/radio/v4lradiocontrol_maemo5.h b/src/plugins/v4l/radio/v4lradiocontrol_maemo5.h new file mode 100644 index 000000000..bbf64830c --- /dev/null +++ b/src/plugins/v4l/radio/v4lradiocontrol_maemo5.h @@ -0,0 +1,144 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef V4LRADIOCONTROL_H +#define V4LRADIOCONTROL_H + +#include +#include + +#include + +#include +#include + +#include + +QT_USE_NAMESPACE + +class V4LRadioService; + +class V4LRadioControl : public QRadioTunerControl +{ + Q_OBJECT +public: + V4LRadioControl(QObject *parent = 0); + ~V4LRadioControl(); + + bool isAvailable() const; + QtMultimediaKit::AvailabilityError availabilityError() const; + + QRadioTuner::State state() const; + + QRadioTuner::Band band() const; + void setBand(QRadioTuner::Band b); + bool isBandSupported(QRadioTuner::Band b) const; + + int frequency() const; + int frequencyStep(QRadioTuner::Band b) const; + QPair frequencyRange(QRadioTuner::Band b) const; + void setFrequency(int frequency); + + bool isStereo() const; + QRadioTuner::StereoMode stereoMode() const; + void setStereoMode(QRadioTuner::StereoMode mode); + + int signalStrength() const; + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + bool isSearching() const; + void cancelSearch(); + + void searchForward(); + void searchBackward(); + + void start(); + void stop(); + + QRadioTuner::Error error() const; + QString errorString() const; + +public slots: + void enablePipeline(bool enable = true); + +private slots: + void search(); + void enableFMRX(); + +private: + bool initRadio(); + void setupHeadPhone(); + bool createGstPipeline(); + void callAmixer(const QString& target, const QString& value); + int getEnumItemIndex(snd_ctl_t *handle, snd_ctl_elem_info_t *info, const QString &value); + int vol(snd_hctl_elem_t *elem) const; + +private: + int fd; + + bool m_error; + bool muted; + bool stereo; + bool low; + bool available; + int tuners; + int step; + int sig; + bool scanning; + bool forward; + QTimer* timer; + QTimer* tickTimer; + QRadioTuner::Band currentBand; + qint64 freqMin; + qint64 freqMax; + qint64 currentFreq; + + GstElement *pipeline; + + QDBusInterface* FMRXEnablerIFace; +}; + +#endif diff --git a/src/plugins/v4l/radio/v4lradioservice.cpp b/src/plugins/v4l/radio/v4lradioservice.cpp new file mode 100644 index 000000000..2a67c90dd --- /dev/null +++ b/src/plugins/v4l/radio/v4lradioservice.cpp @@ -0,0 +1,71 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "v4lradioservice.h" +#include "v4lradiocontrol.h" + +V4LRadioService::V4LRadioService(QObject *parent): + QMediaService(parent) +{ + m_control = new V4LRadioControl(this); +} + +V4LRadioService::~V4LRadioService() +{ +} + +QMediaControl *V4LRadioService::requestControl(const char* name) +{ + if (qstrcmp(name,QRadioTunerControl_iid) == 0) + return m_control; + + return 0; +} + + +void V4LRadioService::releaseControl(QMediaControl *) +{ +} diff --git a/src/plugins/v4l/radio/v4lradioservice.h b/src/plugins/v4l/radio/v4lradioservice.h new file mode 100644 index 000000000..f8664467d --- /dev/null +++ b/src/plugins/v4l/radio/v4lradioservice.h @@ -0,0 +1,67 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef V4LRADIOSERVICE_H +#define V4LRADIOSERVICE_H + +#include + +#include +QT_USE_NAMESPACE + +class V4LRadioControl; + +class V4LRadioService : public QMediaService +{ + Q_OBJECT + +public: + V4LRadioService(QObject *parent = 0); + ~V4LRadioService(); + + QMediaControl *requestControl(const char* name); + void releaseControl(QMediaControl *); + +private: + V4LRadioControl *m_control; +}; + +#endif diff --git a/src/plugins/v4l/v4l.pro b/src/plugins/v4l/v4l.pro new file mode 100644 index 000000000..3242d486b --- /dev/null +++ b/src/plugins/v4l/v4l.pro @@ -0,0 +1,13 @@ +load(qt_module) + +TARGET = qtmedia_v4lengine +QT += multimediakit-private +PLUGIN_TYPE = mediaservice + +load(qt_plugin) +DESTDIR = $$QT.multimediakit.plugins/$${PLUGIN_TYPE} + +HEADERS += v4lserviceplugin.h +SOURCES += v4lserviceplugin.cpp + +include(radio/radio.pri) diff --git a/src/plugins/v4l/v4lserviceplugin.cpp b/src/plugins/v4l/v4lserviceplugin.cpp new file mode 100644 index 000000000..3e02be9fa --- /dev/null +++ b/src/plugins/v4l/v4lserviceplugin.cpp @@ -0,0 +1,84 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include +#include +#include +#include + +#include "v4lserviceplugin.h" +#include "v4lradioservice.h" + +#include + + +QStringList V4LServicePlugin::keys() const +{ + return QStringList() << + QLatin1String(Q_MEDIASERVICE_RADIO); +} + +QMediaService* V4LServicePlugin::create(QString const& key) +{ + if (key == QLatin1String(Q_MEDIASERVICE_RADIO)) + return new V4LRadioService; + + return 0; +} + +void V4LServicePlugin::release(QMediaService *service) +{ + delete service; +} + +QList V4LServicePlugin::devices(const QByteArray &service) const +{ + return QList(); +} + +QString V4LServicePlugin::deviceDescription(const QByteArray &service, const QByteArray &device) +{ + return QString(); +} + + +Q_EXPORT_PLUGIN2(qtmedia_v4lengine, V4LServicePlugin); + diff --git a/src/plugins/v4l/v4lserviceplugin.h b/src/plugins/v4l/v4lserviceplugin.h new file mode 100644 index 000000000..b95d70358 --- /dev/null +++ b/src/plugins/v4l/v4lserviceplugin.h @@ -0,0 +1,63 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + + +#ifndef V4LSERVICEPLUGIN_H +#define V4LSERVICEPLUGIN_H + +#include + +QT_USE_NAMESPACE + +class V4LServicePlugin : public QMediaServiceProviderPlugin, public QMediaServiceSupportedDevicesInterface +{ + Q_OBJECT + Q_INTERFACES(QMediaServiceSupportedDevicesInterface) +public: + QStringList keys() const; + QMediaService* create(QString const& key); + void release(QMediaService *service); + + QList devices(const QByteArray &service) const; + QString deviceDescription(const QByteArray &service, const QByteArray &device); +}; + +#endif // V4LSERVICEPLUGIN_H diff --git a/src/plugins/wmp/qevrvideooverlay.cpp b/src/plugins/wmp/qevrvideooverlay.cpp new file mode 100644 index 000000000..ff301d109 --- /dev/null +++ b/src/plugins/wmp/qevrvideooverlay.cpp @@ -0,0 +1,357 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qevrvideooverlay.h" + +#include +#include + +QEvrVideoOverlay::QEvrVideoOverlay(HINSTANCE evrHwnd) + : m_ref(1) + , m_evrHwnd(evrHwnd) + , ptrMFCreateVideoPresenter(0) + , m_presenter(0) + , m_displayControl(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_winId(0) + , m_fullScreen(0) +{ + ptrMFCreateVideoPresenter = reinterpret_cast( + GetProcAddress(m_evrHwnd, "MFCreateVideoPresenter")); +} + +QEvrVideoOverlay::~QEvrVideoOverlay() +{ + FreeLibrary(m_evrHwnd); +} + +WId QEvrVideoOverlay::winId() const +{ + return m_winId; +} + +void QEvrVideoOverlay::setWinId(WId id) +{ + m_winId = id; + + if (QWidget *widget = QWidget::find(m_winId)) { + const QColor color = widget->palette().color(QPalette::Window); + + m_windowColor = RGB(color.red(), color.green(), color.blue()); + } + + if (m_displayControl) { + m_displayControl->SetVideoWindow(id); + m_displayControl->SetAspectRatioMode(m_aspectRatioMode == Qt::KeepAspectRatio + ? MFVideoARMode_PreservePicture + : MFVideoARMode_None); + m_displayControl->SetBorderColor(m_windowColor); + + setDisplayRect(m_displayRect); + } +} + +QRect QEvrVideoOverlay::displayRect() const +{ + return m_displayRect; +} + +void QEvrVideoOverlay::setDisplayRect(const QRect &rect) +{ + if (m_displayControl) { + RECT displayRect = { rect.left(), rect.top(), rect.right() + 1, rect.bottom() + 1 }; + + if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { + SIZE size; + m_displayControl->GetNativeVideoSize(&size, 0); + + QSize clippedSize = rect.size(); + clippedSize.scale(size.cx, size.cy, Qt::KeepAspectRatio); + + long x = (size.cx - clippedSize.width()) / 2; + long y = (size.cy - clippedSize.height()) / 2; + + MFVideoNormalizedRect sourceRect = + { + float(x) / size.cx, + float(y) / size.cy, + float(x + clippedSize.width()) / size.cx, + float(y + clippedSize.height()) / size.cy + }; + m_displayControl->SetVideoPosition(&sourceRect, &displayRect); + } else { + m_displayControl->SetVideoPosition(0, &displayRect); + } + } + + m_displayRect = rect; +} + +bool QEvrVideoOverlay::isFullScreen() const +{ + return m_fullScreen; +} + +void QEvrVideoOverlay::setFullScreen(bool fullScreen) +{ + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QEvrVideoOverlay::nativeSize() const +{ + SIZE size; + + if (m_displayControl && m_displayControl->GetNativeVideoSize(&size, 0) == S_OK) { + return QSize(size.cx, size.cy); + } else { + return QSize(); + } +} + +Qt::AspectRatioMode QEvrVideoOverlay::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QEvrVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + if (m_displayControl) { + m_displayControl->SetAspectRatioMode(mode == Qt::KeepAspectRatio + ? MFVideoARMode_PreservePicture + : MFVideoARMode_None); + setDisplayRect(m_displayRect); + } +} + +void QEvrVideoOverlay::repaint() +{ + PAINTSTRUCT paint; + + if (HDC dc = ::BeginPaint(m_winId, &paint)) { + if (m_displayControl) { + m_displayControl->RepaintVideo(); + } else { + HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor); + HBRUSH brush = ::CreateSolidBrush(m_windowColor); + ::SelectObject(dc, pen); + ::SelectObject(dc, brush); + + ::Rectangle( + dc, + paint.rcPaint.left, + paint.rcPaint.top, + paint.rcPaint.right, + paint.rcPaint.bottom); + + ::DeleteObject(pen); + ::DeleteObject(brush); + } + + ::EndPaint(m_winId, &paint); + } +} + +int QEvrVideoOverlay::brightness() const +{ + return 0; +} + +void QEvrVideoOverlay::setBrightness(int) +{ +} + +int QEvrVideoOverlay::contrast() const +{ + return 0; +} + +void QEvrVideoOverlay::setContrast(int) +{ +} + +int QEvrVideoOverlay::hue() const +{ + return 0; +} + +void QEvrVideoOverlay::setHue(int) +{ +} + +int QEvrVideoOverlay::saturation() const +{ + return 0; +} + +void QEvrVideoOverlay::setSaturation(int) +{ +} + +void QEvrVideoOverlay::setDisplayControl(IMFVideoDisplayControl *control) +{ + if (m_displayControl) + m_displayControl->Release(); + + m_displayControl = control; + + if (m_displayControl) { + m_displayControl->AddRef(); + m_displayControl->SetVideoWindow(m_winId); + m_displayControl->SetAspectRatioMode(m_aspectRatioMode == Qt::KeepAspectRatio + ? MFVideoARMode_PreservePicture + : MFVideoARMode_None); + m_displayControl->SetBorderColor(m_windowColor); + + setDisplayRect(m_displayRect); + } +} + +void QEvrVideoOverlay::openStateChanged(long state) +{ + if (state == wmposMediaOpen) { + setDisplayRect(m_displayRect); + + emit nativeSizeChanged(); + } +}; + +// IUnknown +HRESULT QEvrVideoOverlay::QueryInterface(REFIID riid, void **object) +{ + if (!object) { + return E_POINTER; + } else if (riid == __uuidof(IUnknown) + || riid == __uuidof(IMFAttributes) + || riid == __uuidof(IMFActivate)) { + *object = static_cast(this); + } else { + return E_NOINTERFACE; + } + + AddRef(); + + return S_OK; +} + +ULONG QEvrVideoOverlay::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG QEvrVideoOverlay::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + Q_ASSERT(ref != 0); + + return ref; +} + +// IMFActivate +HRESULT QEvrVideoOverlay::ActivateObject(REFIID riid, void **ppv) +{ + if (riid != __uuidof(IMFVideoPresenter)) { + return E_NOINTERFACE; + } else if (!ptrMFCreateVideoPresenter) { + return E_NOINTERFACE; + } else if (m_presenter) { + *ppv = m_presenter; + + return S_OK; + } else { + IMFVideoDisplayControl *displayControl = 0; + + IMFGetService *service; + HRESULT hr; + if ((hr = (*ptrMFCreateVideoPresenter)( + 0, + __uuidof(IDirect3DDevice9), + __uuidof(IMFVideoPresenter), + reinterpret_cast(&m_presenter))) != S_OK) { + qWarning("failed to create video presenter"); + } else if ((hr = m_presenter->QueryInterface( + __uuidof(IMFGetService), reinterpret_cast(&service))) != S_OK) { + qWarning("failed to query IMFGetService interface"); + } else { + if ((hr = service->GetService( + MR_VIDEO_RENDER_SERVICE, + __uuidof(IMFVideoDisplayControl), + reinterpret_cast(&displayControl))) != S_OK) { + qWarning("failed to get IMFVideoDisplayControl service"); + } + service->Release(); + } + + setDisplayControl(displayControl); + + if (m_presenter && hr != S_OK) { + m_presenter->Release(); + m_presenter = 0; + } + + *ppv = m_presenter; + + return hr; + } +} + +HRESULT QEvrVideoOverlay::ShutdownObject() +{ + setDisplayControl(0); + + if (m_presenter) { + m_presenter->Release(); + m_presenter = 0; + } + return S_OK; +} + +HRESULT QEvrVideoOverlay::DetachObject() +{ + if (m_presenter) { + m_presenter->Release(); + m_presenter = 0; + } + + return S_OK; +} diff --git a/src/plugins/wmp/qevrvideooverlay.h b/src/plugins/wmp/qevrvideooverlay.h new file mode 100644 index 000000000..247b16e96 --- /dev/null +++ b/src/plugins/wmp/qevrvideooverlay.h @@ -0,0 +1,119 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QEVRVIDEOOVERLAY_H +#define QEVRVIDEOOVERLAY_H + +#include + +#include "qmfactivate.h" + +#include + +QT_USE_NAMESPACE + +class QEvrVideoOverlay : public QVideoWindowControl, public QMFActivate +{ + Q_OBJECT +public: + QEvrVideoOverlay(HINSTANCE evrHwnd); + ~QEvrVideoOverlay(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + QSize nativeSize() const; + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + void repaint(); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + void setDisplayControl(IMFVideoDisplayControl *control); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IMFActivate + HRESULT STDMETHODCALLTYPE ActivateObject(REFIID riid, void **ppv); + HRESULT STDMETHODCALLTYPE ShutdownObject(); + HRESULT STDMETHODCALLTYPE DetachObject(); + +public Q_SLOTS: + void openStateChanged(long state); + +private: + typedef HRESULT (WINAPI *PtrMFCreateVideoPresenter)(IUnknown*, REFIID, REFIID, void**); + + volatile LONG m_ref; + HINSTANCE m_evrHwnd; + PtrMFCreateVideoPresenter ptrMFCreateVideoPresenter; + IMFVideoPresenter *m_presenter; + IMFVideoDisplayControl *m_displayControl; + Qt::AspectRatioMode m_aspectRatioMode; + QSize m_sizeHint; + QRect m_displayRect; + WId m_winId; + COLORREF m_windowColor; + bool m_fullScreen; +}; + +#endif diff --git a/src/plugins/wmp/qmfactivate.cpp b/src/plugins/wmp/qmfactivate.cpp new file mode 100644 index 000000000..8cd64b0e4 --- /dev/null +++ b/src/plugins/wmp/qmfactivate.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qmfactivate.h" + +#include + +#include + + +// IMFAttributes + +HRESULT QMFActivate::GetItem(REFGUID guidKey, PROPVARIANT *pValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pValue); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pType); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult) +{ + Q_UNUSED(guidKey); + Q_UNUSED(Value); + Q_UNUSED(pbResult); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult) +{ + Q_UNUSED(pTheirs); + Q_UNUSED(MatchType); + Q_UNUSED(pbResult); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::GetUINT32(REFGUID guidKey, UINT32 *punValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(punValue); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetUINT64(REFGUID guidKey, UINT64 *punValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(punValue); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetDouble(REFGUID guidKey, double *pfValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pfValue); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetGUID(REFGUID guidKey, GUID *pguidValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pguidValue); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetStringLength(REFGUID guidKey, UINT32 *pcchLength) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pcchLength); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pwszValue); + Q_UNUSED(cchBufSize); + Q_UNUSED(pcchLength); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength) +{ + Q_UNUSED(guidKey); + Q_UNUSED(ppwszValue); + Q_UNUSED(pcchLength); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pcbBlobSize); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pBuf); + Q_UNUSED(cbBufSize); + Q_UNUSED(pcbBlobSize); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize) +{ + Q_UNUSED(guidKey); + Q_UNUSED(ppBuf); + Q_UNUSED(pcbSize); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv) +{ + Q_UNUSED(guidKey); + Q_UNUSED(riid); + Q_UNUSED(ppv); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::SetItem(REFGUID guidKey, REFPROPVARIANT Value) +{ + Q_UNUSED(guidKey); + Q_UNUSED(Value); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::DeleteItem(REFGUID guidKey) +{ + Q_UNUSED(guidKey); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::DeleteAllItems() +{ + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetUINT32(REFGUID guidKey, UINT32 unValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(unValue); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetUINT64(REFGUID guidKey, UINT64 unValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(unValue); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetDouble(REFGUID guidKey, double fValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(fValue); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetGUID(REFGUID guidKey, REFGUID guidValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(guidValue); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetString(REFGUID guidKey, LPCWSTR wszValue) +{ + Q_UNUSED(guidKey); + Q_UNUSED(wszValue); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pBuf); + Q_UNUSED(cbBufSize); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::SetUnknown(REFGUID guidKey, IUnknown *pUnknown) +{ + Q_UNUSED(guidKey); + Q_UNUSED(pUnknown); + + return E_NOTIMPL; +} + +HRESULT QMFActivate::LockStore() +{ + m_mutex.lock(); + + return S_OK; +} + +HRESULT QMFActivate::UnlockStore() +{ + m_mutex.unlock(); + + return S_OK; +} + +HRESULT QMFActivate::GetCount(UINT32 *pcItems) +{ + if (!pcItems) { + return E_POINTER; + } else { + *pcItems = 0; + + return S_OK; + } +} + +HRESULT QMFActivate::GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue) +{ + Q_UNUSED(unIndex); + Q_UNUSED(pguidKey); + Q_UNUSED(pValue); + + return MF_E_ATTRIBUTENOTFOUND; +} + +HRESULT QMFActivate::CopyAllItems(IMFAttributes *pDest) +{ + Q_UNUSED(pDest); + + return MF_E_ATTRIBUTENOTFOUND; +} diff --git a/src/plugins/wmp/qmfactivate.h b/src/plugins/wmp/qmfactivate.h new file mode 100644 index 000000000..0e1692689 --- /dev/null +++ b/src/plugins/wmp/qmfactivate.h @@ -0,0 +1,90 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QMFACTIVATE_H +#define QMFACTIVATE_H + +#include + +#include + +class QMFActivate : public IMFActivate +{ +public: + // IMFAttributes + HRESULT STDMETHODCALLTYPE GetItem(REFGUID guidKey, PROPVARIANT *pValue); + HRESULT STDMETHODCALLTYPE GetItemType(REFGUID guidKey, MF_ATTRIBUTE_TYPE *pType); + HRESULT STDMETHODCALLTYPE CompareItem(REFGUID guidKey, REFPROPVARIANT Value, BOOL *pbResult); + HRESULT STDMETHODCALLTYPE Compare(IMFAttributes *pTheirs, MF_ATTRIBUTES_MATCH_TYPE MatchType, BOOL *pbResult); + HRESULT STDMETHODCALLTYPE GetUINT32(REFGUID guidKey, UINT32 *punValue); + HRESULT STDMETHODCALLTYPE GetUINT64(REFGUID guidKey, UINT64 *punValue); + HRESULT STDMETHODCALLTYPE GetDouble(REFGUID guidKey, double *pfValue); + HRESULT STDMETHODCALLTYPE GetGUID(REFGUID guidKey, GUID *pguidValue); + HRESULT STDMETHODCALLTYPE GetStringLength(REFGUID guidKey, UINT32 *pcchLength); + HRESULT STDMETHODCALLTYPE GetString(REFGUID guidKey, LPWSTR pwszValue, UINT32 cchBufSize, UINT32 *pcchLength); + HRESULT STDMETHODCALLTYPE GetAllocatedString(REFGUID guidKey, LPWSTR *ppwszValue, UINT32 *pcchLength); + HRESULT STDMETHODCALLTYPE GetBlobSize(REFGUID guidKey, UINT32 *pcbBlobSize); + HRESULT STDMETHODCALLTYPE GetBlob(REFGUID guidKey, UINT8 *pBuf, UINT32 cbBufSize, UINT32 *pcbBlobSize); + HRESULT STDMETHODCALLTYPE GetAllocatedBlob(REFGUID guidKey, UINT8 **ppBuf, UINT32 *pcbSize); + HRESULT STDMETHODCALLTYPE GetUnknown(REFGUID guidKey, REFIID riid, LPVOID *ppv); + HRESULT STDMETHODCALLTYPE SetItem(REFGUID guidKey, REFPROPVARIANT Value); + HRESULT STDMETHODCALLTYPE DeleteItem(REFGUID guidKey); + HRESULT STDMETHODCALLTYPE DeleteAllItems(); + HRESULT STDMETHODCALLTYPE SetUINT32(REFGUID guidKey, UINT32 unValue); + HRESULT STDMETHODCALLTYPE SetUINT64(REFGUID guidKey, UINT64 unValue); + HRESULT STDMETHODCALLTYPE SetDouble(REFGUID guidKey, double fValue); + HRESULT STDMETHODCALLTYPE SetGUID(REFGUID guidKey, REFGUID guidValue); + HRESULT STDMETHODCALLTYPE SetString(REFGUID guidKey, LPCWSTR wszValue); + HRESULT STDMETHODCALLTYPE SetBlob(REFGUID guidKey, const UINT8 *pBuf, UINT32 cbBufSize); + HRESULT STDMETHODCALLTYPE SetUnknown(REFGUID guidKey, IUnknown *pUnknown); + HRESULT STDMETHODCALLTYPE LockStore(); + HRESULT STDMETHODCALLTYPE UnlockStore(); + HRESULT STDMETHODCALLTYPE GetCount(UINT32 *pcItems); + HRESULT STDMETHODCALLTYPE GetItemByIndex(UINT32 unIndex, GUID *pguidKey, PROPVARIANT *pValue); + HRESULT STDMETHODCALLTYPE CopyAllItems(IMFAttributes *pDest); + +private: + volatile LONG m_ref; + QMutex m_mutex; +}; + + +#endif diff --git a/src/plugins/wmp/qwmpevents.cpp b/src/plugins/wmp/qwmpevents.cpp new file mode 100644 index 000000000..66df07b4a --- /dev/null +++ b/src/plugins/wmp/qwmpevents.cpp @@ -0,0 +1,114 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpevents.h" + +#include "qwmpglobal.h" + +QWmpEvents::QWmpEvents(IUnknown *source, QObject *parent) + : QObject(parent) + , m_ref(1) + , m_connectionPoint(0) + , m_adviseCookie(0) +{ + HRESULT hr; + IConnectionPointContainer *container = 0; + + if ((hr = source->QueryInterface( + IID_IConnectionPointContainer, reinterpret_cast(&container))) != S_OK) { + qWarning("No connection point container, %x: %d", hr, qwmp_error_string(hr)); + } else { + if ((hr = container->FindConnectionPoint( + __uuidof(IWMPEvents), &m_connectionPoint)) != S_OK) { + qWarning("No connection point for IWMPEvents %d", hr); + } else if ((hr = m_connectionPoint->Advise( + static_cast(this), &m_adviseCookie)) != S_OK) { + qWarning("Failed to link to connection point, %x, %s", hr, qwmp_error_string(hr)); + + m_connectionPoint->Release(); + m_connectionPoint = 0; + } + container->Release(); + } +} + +QWmpEvents::~QWmpEvents() +{ + if (m_connectionPoint) { + m_connectionPoint->Unadvise(m_adviseCookie); + m_connectionPoint->Release(); + } + + Q_ASSERT(m_ref == 1); +} + +// IUnknown +HRESULT QWmpEvents::QueryInterface(REFIID riid, void **object) +{ + if (!object) { + return E_POINTER; + } else if (riid == __uuidof(IUnknown) + || riid == __uuidof(IWMPEvents) + || riid == __uuidof(IWMPEvents2) + || riid == __uuidof(IWMPEvents3)) { + *object = static_cast(this); + + AddRef(); + + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +ULONG QWmpEvents::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG QWmpEvents::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + Q_ASSERT(ref != 0); + + return ref; +} diff --git a/src/plugins/wmp/qwmpevents.h b/src/plugins/wmp/qwmpevents.h new file mode 100644 index 000000000..1fb40309d --- /dev/null +++ b/src/plugins/wmp/qwmpevents.h @@ -0,0 +1,222 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPEVENTS_H +#define QWMPEVENTS_H + +#include + +#include + +class QWmpEvents : public QObject, public IWMPEvents3 +{ + Q_OBJECT +public: + QWmpEvents(IUnknown *source, QObject *parent = 0); + ~QWmpEvents(); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **ppvObject); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + +Q_SIGNALS: + +#ifndef Q_MOC_RUN + // IWMPEvents + void STDMETHODCALLTYPE OpenStateChange(long NewState); + void STDMETHODCALLTYPE PlayStateChange(long NewState); + void STDMETHODCALLTYPE AudioLanguageChange(long LangID); + void STDMETHODCALLTYPE StatusChange(); + void STDMETHODCALLTYPE ScriptCommand(BSTR scType, BSTR Param); + void STDMETHODCALLTYPE NewStream(); + void STDMETHODCALLTYPE Disconnect(long Result); + void STDMETHODCALLTYPE Buffering(VARIANT_BOOL Start); + void STDMETHODCALLTYPE Error(); + void STDMETHODCALLTYPE Warning(long WarningType, long Param, BSTR Description); + void STDMETHODCALLTYPE EndOfStream(long Result); + void STDMETHODCALLTYPE PositionChange(double oldPosition, double newPosition); + void STDMETHODCALLTYPE MarkerHit(long MarkerNum); + void STDMETHODCALLTYPE DurationUnitChange(long NewDurationUnit); + void STDMETHODCALLTYPE CdromMediaChange(long CdromNum); + void STDMETHODCALLTYPE PlaylistChange(IDispatch *Playlist, WMPPlaylistChangeEventType change); + void STDMETHODCALLTYPE CurrentPlaylistChange(WMPPlaylistChangeEventType change); + void STDMETHODCALLTYPE CurrentPlaylistItemAvailable(BSTR bstrItemName); + void STDMETHODCALLTYPE MediaChange(IDispatch *Item); + void STDMETHODCALLTYPE CurrentMediaItemAvailable(BSTR bstrItemName); + void STDMETHODCALLTYPE CurrentItemChange(IDispatch *pdispMedia); + void STDMETHODCALLTYPE MediaCollectionChange(); + void STDMETHODCALLTYPE MediaCollectionAttributeStringAdded( + BSTR bstrAttribName, BSTR bstrAttribVal); + void STDMETHODCALLTYPE MediaCollectionAttributeStringRemoved( + BSTR bstrAttribName, BSTR bstrAttribVal); + void STDMETHODCALLTYPE MediaCollectionAttributeStringChanged( + BSTR bstrAttribName, BSTR bstrOldAttribVal, BSTR bstrNewAttribVal); + void STDMETHODCALLTYPE PlaylistCollectionChange(); + void STDMETHODCALLTYPE PlaylistCollectionPlaylistAdded(BSTR bstrPlaylistName); + void STDMETHODCALLTYPE PlaylistCollectionPlaylistRemoved(BSTR bstrPlaylistName); + void STDMETHODCALLTYPE PlaylistCollectionPlaylistSetAsDeleted( + BSTR bstrPlaylistName, VARIANT_BOOL varfIsDeleted); + void STDMETHODCALLTYPE ModeChange(BSTR ModeName, VARIANT_BOOL NewValue); + void STDMETHODCALLTYPE MediaError(IDispatch *pMediaObject); + void STDMETHODCALLTYPE OpenPlaylistSwitch(IDispatch *pItem); + void STDMETHODCALLTYPE DomainChange(BSTR strDomain); + void STDMETHODCALLTYPE SwitchedToPlayerApplication(); + void STDMETHODCALLTYPE SwitchedToControl(); + void STDMETHODCALLTYPE PlayerDockedStateChange(); + void STDMETHODCALLTYPE PlayerReconnect(); + void STDMETHODCALLTYPE Click(short nButton, short nShiftState, long fX, long fY); + void STDMETHODCALLTYPE DoubleClick(short nButton, short nShiftState, long fX, long fY); + void STDMETHODCALLTYPE KeyDown(short nKeyCode, short nShiftState); + void STDMETHODCALLTYPE KeyPress(short nKeyAscii); + void STDMETHODCALLTYPE KeyUp(short nKeyCode, short nShiftState); + void STDMETHODCALLTYPE MouseDown(short nButton, short nShiftState, long fX, long fY); + void STDMETHODCALLTYPE MouseMove(short nButton, short nShiftState, long fX, long fY); + void STDMETHODCALLTYPE MouseUp(short nButton, short nShiftState, long fX, long fY); + + // IWMPEvents2 + void STDMETHODCALLTYPE DeviceConnect(IWMPSyncDevice *pDevice); + void STDMETHODCALLTYPE DeviceDisconnect(IWMPSyncDevice *pDevice); + void STDMETHODCALLTYPE DeviceStatusChange(IWMPSyncDevice *pDevice, WMPDeviceStatus NewStatus); + void STDMETHODCALLTYPE DeviceSyncStateChange(IWMPSyncDevice *pDevice, WMPSyncState NewState); + void STDMETHODCALLTYPE DeviceSyncError(IWMPSyncDevice *pDevice, IDispatch *pMedia); + void STDMETHODCALLTYPE CreatePartnershipComplete(IWMPSyncDevice *pDevice, HRESULT hrResult); + + // IWMPEvents3 + void STDMETHODCALLTYPE CdromRipStateChange(IWMPCdromRip *pCdromRip, WMPRipState wmprs); + void STDMETHODCALLTYPE CdromRipMediaError(IWMPCdromRip *pCdromRip, IDispatch *pMedia); + void STDMETHODCALLTYPE CdromBurnStateChange(IWMPCdromBurn *pCdromBurn, WMPBurnState wmpbs); + void STDMETHODCALLTYPE CdromBurnMediaError(IWMPCdromBurn *pCdromBurn, IDispatch *pMedia); + void STDMETHODCALLTYPE CdromBurnError(IWMPCdromBurn *pCdromBurn, HRESULT hrError); + void STDMETHODCALLTYPE LibraryConnect(IWMPLibrary *pLibrary); + void STDMETHODCALLTYPE LibraryDisconnect(IWMPLibrary *pLibrary); + void STDMETHODCALLTYPE FolderScanStateChange(WMPFolderScanState wmpfss); + void STDMETHODCALLTYPE StringCollectionChange( + IDispatch *pdispStringCollection, + WMPStringCollectionChangeEventType change, + long lCollectionIndex); + void STDMETHODCALLTYPE MediaCollectionMediaAdded(IDispatch *pdispMedia); + void STDMETHODCALLTYPE MediaCollectionMediaRemoved(IDispatch *pdispMedia); +#else + // Declare again without STDMETHODCALLTYPE for moc's benefit. + + // IWMPEvents + void OpenStateChange(long NewState); + void PlayStateChange(long NewState); + void AudioLanguageChange(long LangID); + void StatusChange(); + void ScriptCommand(BSTR scType, BSTR Param); + void NewStream(); + void Disconnect(long Result); + void Buffering(VARIANT_BOOL Start); + void Error(); + void Warning(long WarningType, long Param, BSTR Description); + void EndOfStream(long Result); + void PositionChange(double oldPosition, double newPosition); + void MarkerHit(long MarkerNum); + void DurationUnitChange(long NewDurationUnit); + void CdromMediaChange(long CdromNum); + void PlaylistChange(IDispatch *Playlist, WMPPlaylistChangeEventType change); + void CurrentPlaylistChange(WMPPlaylistChangeEventType change); + void CurrentPlaylistItemAvailable(BSTR bstrItemName); + void MediaChange(IDispatch *Item); + void CurrentMediaItemAvailable(BSTR bstrItemName); + void CurrentItemChange(IDispatch *pdispMedia); + void MediaCollectionChange(); + void MediaCollectionAttributeStringAdded( + BSTR bstrAttribName, BSTR bstrAttribVal); + void MediaCollectionAttributeStringRemoved( + BSTR bstrAttribName, BSTR bstrAttribVal); + void MediaCollectionAttributeStringChanged( + BSTR bstrAttribName, BSTR bstrOldAttribVal, BSTR bstrNewAttribVal); + void PlaylistCollectionChange(); + void PlaylistCollectionPlaylistAdded(BSTR bstrPlaylistName); + void PlaylistCollectionPlaylistRemoved(BSTR bstrPlaylistName); + void PlaylistCollectionPlaylistSetAsDeleted( + BSTR bstrPlaylistName, VARIANT_BOOL varfIsDeleted); + void ModeChange(BSTR ModeName, VARIANT_BOOL NewValue); + void MediaError(IDispatch *pMediaObject); + void OpenPlaylistSwitch(IDispatch *pItem); + void DomainChange(BSTR strDomain); + void SwitchedToPlayerApplication(); + void SwitchedToControl(); + void PlayerDockedStateChange(); + void PlayerReconnect(); + void Click(short nButton, short nShiftState, long fX, long fY); + void DoubleClick(short nButton, short nShiftState, long fX, long fY); + void KeyDown(short nKeyCode, short nShiftState); + void KeyPress(short nKeyAscii); + void KeyUp(short nKeyCode, short nShiftState); + void MouseDown(short nButton, short nShiftState, long fX, long fY); + void MouseMove(short nButton, short nShiftState, long fX, long fY); + void MouseUp(short nButton, short nShiftState, long fX, long fY); + + // IWMPEvents2 + void DeviceConnect(IWMPSyncDevice *pDevice); + void DeviceDisconnect(IWMPSyncDevice *pDevice); + void DeviceStatusChange(IWMPSyncDevice *pDevice, WMPDeviceStatus NewStatus); + void DeviceSyncStateChange(IWMPSyncDevice *pDevice, WMPSyncState NewState); + void DeviceSyncError(IWMPSyncDevice *pDevice, IDispatch *pMedia); + void CreatePartnershipComplete(IWMPSyncDevice *pDevice, HRESULT hrResult); + + // IWMPEvents3 + void CdromRipStateChange(IWMPCdromRip *pCdromRip, WMPRipState wmprs); + void CdromRipMediaError(IWMPCdromRip *pCdromRip, IDispatch *pMedia); + void CdromBurnStateChange(IWMPCdromBurn *pCdromBurn, WMPBurnState wmpbs); + void CdromBurnMediaError(IWMPCdromBurn *pCdromBurn, IDispatch *pMedia); + void CdromBurnError(IWMPCdromBurn *pCdromBurn, HRESULT hrError); + void LibraryConnect(IWMPLibrary *pLibrary); + void LibraryDisconnect(IWMPLibrary *pLibrary); + void FolderScanStateChange(WMPFolderScanState wmpfss); + void StringCollectionChange( + IDispatch *pdispStringCollection, + WMPStringCollectionChangeEventType change, + long lCollectionIndex); + void MediaCollectionMediaAdded(IDispatch *pdispMedia); + void MediaCollectionMediaRemoved(IDispatch *pdispMedia); +#endif +private: + volatile LONG m_ref; + IConnectionPoint *m_connectionPoint; + DWORD m_adviseCookie; +}; + +#endif diff --git a/src/plugins/wmp/qwmpglobal.cpp b/src/plugins/wmp/qwmpglobal.cpp new file mode 100644 index 000000000..4196bbc4f --- /dev/null +++ b/src/plugins/wmp/qwmpglobal.cpp @@ -0,0 +1,68 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpglobal.h" + +#include + +const char *qwmp_error_string(HRESULT hr) +{ + switch (hr) { + case S_OK: + return "OK"; + case E_NOINTERFACE: + return "No such interface supported"; + case E_POINTER: + return "Invalid pointer"; + case E_FAIL: + return "Unspecified error"; + case E_NOTIMPL: + return "Not implemented"; + case CLASS_E_NOAGGREGATION: + return "Class does not support aggregation (or class object is remote)"; + case CLASS_E_CLASSNOTAVAILABLE: + return "ClassFactory cannot supply requested class"; + case CLASS_E_NOTLICENSED: + return "Class is not licensed for use"; + default: + return "unknown error code"; + } +} diff --git a/src/plugins/wmp/qwmpglobal.h b/src/plugins/wmp/qwmpglobal.h new file mode 100644 index 000000000..f8152903f --- /dev/null +++ b/src/plugins/wmp/qwmpglobal.h @@ -0,0 +1,83 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPGLOBAL_H +#define QWMPGLOBAL_H + +#include + +#include +#include + +const char *qwmp_error_string(HRESULT hr); + +class QAutoBStr +{ +public: + inline QAutoBStr(const QString &string) + : m_string(::SysAllocString(static_cast(string.utf16()))) + { + } + + inline QAutoBStr(const QUrl &url) + : m_string(::SysAllocString(static_cast(url.toString().utf16()))) + { + } + + inline QAutoBStr(const wchar_t *string) + : m_string(::SysAllocString(string)) + { + } + + inline ~QAutoBStr() + { + ::SysFreeString(m_string); + } + + inline operator BSTR() const { return m_string; } + +private: + BSTR m_string; +}; + + + +#endif diff --git a/src/plugins/wmp/qwmpmetadata.cpp b/src/plugins/wmp/qwmpmetadata.cpp new file mode 100644 index 000000000..1f7cd0f88 --- /dev/null +++ b/src/plugins/wmp/qwmpmetadata.cpp @@ -0,0 +1,442 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpmetadata.h" + +#include "qwmpevents.h" +#include "qwmpglobal.h" + +#include + +#include +#include +#include +#include +#include +#include +#include + + +struct QWmpMetaDataKeyLookup +{ + QtMultimediaKit::MetaData key; + const wchar_t *token; +}; + +static const QWmpMetaDataKeyLookup qt_wmpMetaDataKeys[] = +{ + { QtMultimediaKit::Title, L"Title" }, + { QtMultimediaKit::SubTitle, L"WM/SubTitle" }, + { QtMultimediaKit::Author, L"Author" }, + { QtMultimediaKit::Comment, L"Comment" }, + { QtMultimediaKit::Description, L"Description" }, + { QtMultimediaKit::Category, L"WM/Category" }, + { QtMultimediaKit::Genre, L"WM/Genre" }, + //{ QtMultimediaKit::Date, 0 }, + { QtMultimediaKit::Year, L"WM/Year" }, + { QtMultimediaKit::UserRating, L"UserRating" }, + //{ QtMultimediaKit::MetaDatawords, 0 }, + { QtMultimediaKit::Language, L"Language" }, + { QtMultimediaKit::Publisher, L"WM/Publisher" }, + { QtMultimediaKit::Copyright, L"Copyright" }, + { QtMultimediaKit::ParentalRating, L"ParentalRating" }, + { QtMultimediaKit::RatingOrganisation, L"RatingOrganisation" }, + + // Media + { QtMultimediaKit::Size, L"FileSize" }, + { QtMultimediaKit::MediaType, L"MediaType" }, + { QtMultimediaKit::Duration, L"Duration" }, + + // Audio + { QtMultimediaKit::AudioBitRate, L"AudioBitrate" }, + { QtMultimediaKit::AudioCodec, L"AudioCodec" }, + { QtMultimediaKit::ChannelCount, L"Channels" }, + { QtMultimediaKit::SampleRate, L"Frequency" }, + + // Music + { QtMultimediaKit::AlbumTitle, L"WM/AlbumTitle" }, + { QtMultimediaKit::AlbumArtist, L"WM/AlbumArtist" }, + { QtMultimediaKit::ContributingArtist, L"Author" }, + { QtMultimediaKit::Composer, L"WM/Composer" }, + { QtMultimediaKit::Conductor, L"WM/Conductor" }, + { QtMultimediaKit::Lyrics, L"WM/Lyrics" }, + { QtMultimediaKit::Mood, L"WM/Mood" }, + { QtMultimediaKit::TrackNumber, L"WM/TrackNumber" }, + //{ QtMultimediaKit::TrackCount, 0 }, + //{ QtMultimediaKit::CoverArtUrlSmall, 0 }, + //{ QtMultimediaKit::CoverArtUrlLarge, 0 }, + + // Image/Video + //{ QtMultimediaKit::Resolution, 0 }, + //{ QtMultimediaKit::PixelAspectRatio, 0 }, + + // Video + //{ QtMultimediaKit::FrameRate, 0 }, + { QtMultimediaKit::VideoBitRate, L"VideoBitRate" }, + { QtMultimediaKit::VideoCodec, L"VideoCodec" }, + + //{ QtMultimediaKit::PosterUrl, 0 }, + + // Movie + { QtMultimediaKit::ChapterNumber, L"ChapterNumber" }, + { QtMultimediaKit::Director, L"WM/Director" }, + { QtMultimediaKit::LeadPerformer, L"LeadPerformer" }, + { QtMultimediaKit::Writer, L"WM/Writer" }, + + // Photos + { QtMultimediaKit::CameraManufacturer, L"CameraManufacturer" }, + { QtMultimediaKit::CameraModel, L"CameraModel" }, + { QtMultimediaKit::Event, L"Event" }, + { QtMultimediaKit::Subject, L"Subject" } +}; + +QWmpMetaData::QWmpMetaData(IWMPCore3 *player, QWmpEvents *events, QObject *parent) + : QMetaDataReaderControl(parent) + , m_media(0) +{ + player->get_currentMedia(&m_media); + + connect(events, SIGNAL(CurrentItemChange(IDispatch*)), + this, SLOT(currentItemChangeEvent(IDispatch*))); + connect(events, SIGNAL(MediaChange(IDispatch*)), this, SLOT(mediaChangeEvent(IDispatch*))); +} + +QWmpMetaData::~QWmpMetaData() +{ + if (m_media) + m_media->Release(); +} + +bool QWmpMetaData::isMetaDataAvailable() const +{ + return m_media != 0; +} + +bool QWmpMetaData::isWritable() const +{ + return m_media != 0; +} + +QVariant QWmpMetaData::metaData(QtMultimediaKit::MetaData key) const +{ + static const int count = sizeof(qt_wmpMetaDataKeys) / sizeof(QWmpMetaDataKeyLookup); + + switch (key) { + case QtMultimediaKit::Date: + { + QVariant day = value(m_media, QAutoBStr(L"ReleaseDateDay")); + QVariant month = value(m_media, QAutoBStr(L"ReleaseDateMonth")); + QVariant year = value(m_media, QAutoBStr(L"ReleaseDateYear")); + + if (!day.isNull() && !month.isNull() && !year.isNull()) + return QDate(year.toInt(), month.toInt(), day.toInt()); + } + break; + case QtMultimediaKit::CoverArtUrlSmall: + return albumArtUrl(m_media, "_Small.jpg"); + case QtMultimediaKit::CoverArtUrlLarge: + return albumArtUrl(m_media, "_Large.jpg"); + case QtMultimediaKit::Resolution: + { + QVariant width = value(m_media, QAutoBStr(L"WM/VideoWidth")); + QVariant height = value(m_media, QAutoBStr(L"WM/VideoHeight")); + + if (!width.isNull() && !height.isNull()) + return QSize(width.toInt(), height.toInt()); + } + break; + case QtMultimediaKit::PixelAspectRatio: + { + QVariant x = value(m_media, QAutoBStr(L"PixelAspectRatioX")); + QVariant y = value(m_media, QAutoBStr(L"PixelAspectRatioY")); + + if (!x.isNull() && !y.isNull()) + return QSize(x.toInt(), y.toInt()); + } + break; + case QtMultimediaKit::VideoFrameRate: + break; + default: + for (int i = 0; i < count; ++i) { + if (qt_wmpMetaDataKeys[i].key == key) + return value(m_media, QAutoBStr(qt_wmpMetaDataKeys[i].token)); + } + break; + } + return QVariant(); +} + +QList QWmpMetaData::availableMetaData() const +{ + QList keys; + + if (m_media) { + // WMP will return a list of all possible keys so there's no point in filtering the keys + // in the lookup table. + static const int count = sizeof(qt_wmpMetaDataKeys) / sizeof(QWmpMetaDataKeyLookup); + for (int i = 0; i < count; ++i) + keys.append(qt_wmpMetaDataKeys[i].key); + + BSTR string = 0; + if (m_media->get_sourceURL(&string) == S_OK) { + QString url = QString::fromWCharArray(static_cast(string)); + ::SysFreeString(string); + + if (m_media->getItemInfo(QAutoBStr(L"WM/WMCollectionGroupID"), &string) == S_OK) { + QString uuid = QString::fromWCharArray(static_cast(string)); + ::SysFreeString(string); + + QString albumArtLarge = QLatin1String("AlbumArt_") + uuid + QLatin1String("_Large.jpg"); + QString albumArtSmall = QLatin1String("AlbumArt_") + uuid + QLatin1String("_Small.jpg"); + + QDir dir = QFileInfo(url).absoluteDir(); + + if (dir.exists(albumArtLarge)) + keys.append(QtMultimediaKit::CoverArtUrlLarge); + if (dir.exists(albumArtSmall)) + keys.append(QtMultimediaKit::CoverArtUrlSmall); + } + } + } + return keys; +} + +QVariant QWmpMetaData::extendedMetaData(const QString &key) const +{ + return value(m_media, QAutoBStr(key)); +} + +QStringList QWmpMetaData::availableExtendedMetaData() const +{ + return keys(m_media); +} + +void QWmpMetaData::currentItemChangeEvent(IDispatch *dispatch) +{ + IWMPMedia *media = m_media; + + m_media = 0; + if (dispatch) + dispatch->QueryInterface(__uuidof(IWMPMedia), reinterpret_cast(&m_media)); + + if (media) { + if (m_media) + emit metaDataChanged(); + else + emit metaDataAvailableChanged(false); + + media->Release(); + } else { + if (m_media) + emit metaDataAvailableChanged(false); + } +} + +void QWmpMetaData::mediaChangeEvent(IDispatch *dispatch) +{ + IWMPMedia *media = 0; + if (dispatch && dispatch->QueryInterface( + __uuidof(IWMPMedia), reinterpret_cast(&media)) == S_OK) { + VARIANT_BOOL isEqual = VARIANT_FALSE; + if (media->get_isIdentical(m_media, &isEqual) == S_OK && isEqual) + emit metaDataChanged(); + media->Release(); + } +} + + +QStringList QWmpMetaData::keys(IWMPMedia *media) +{ + QStringList keys; + + long count = 0; + if (media && media->get_attributeCount(&count) == S_OK) { + for (long i = 0; i < count; ++i) { + BSTR string; + if (media->getAttributeName(i, &string) == S_OK) { + keys.append(QString::fromWCharArray(string, ::SysStringLen(string))); + + ::SysFreeString(string); + } + } + } + return keys; +} + +QVariant QWmpMetaData::value(IWMPMedia *media, BSTR key) +{ + QVariantList values; + IWMPMedia3 *media3 = 0; + if (media && media->QueryInterface( + __uuidof(IWMPMedia3), reinterpret_cast(&media3)) == S_OK) { + long count = 0; + media3->getAttributeCountByType(key, 0, &count); + + // The count appears to only be valid for static properties, dynamic properties like + // PlaylistIndex will have a count of zero but return a value for index 0. + if (count == 0) + count = 1; + + for (long i = 0; i < count; ++i) { + VARIANT var; + VariantInit(&var); + + if (media3->getItemInfoByType(key, 0, i, &var) == S_OK) { + QVariant value = convertVariant(var); + + if (!value.isNull()) + values.append(value); + + VariantClear(&var); + } + } + media3->Release(); + } + + switch (values.count()) { + case 0: + return QVariant(); + case 1: + return values.first(); + default: + return values; + } +} + +QMediaContent QWmpMetaData::resources(IWMPMedia *media) +{ + QMediaContent content; + + BSTR string = 0; + if (media->get_sourceURL(&string) == S_OK) { + QString url = QString::fromWCharArray(static_cast(string)); + ::SysFreeString(string); + + content = QMediaContent(QUrl(url)); + } + + return content; +} + +QVariant QWmpMetaData::convertVariant(const VARIANT &variant) +{ + switch (variant.vt) { + case VT_I2: + return variant.iVal; + case VT_I4: + return variant.lVal; + case VT_I8: + return variant.llVal; + case VT_UI2: + return variant.uiVal; + case VT_UI4: + return quint32(variant.ulVal); + case VT_UI8: + return variant.ullVal; + case VT_INT: + return variant.intVal; + case VT_UINT: + return variant.uintVal; + case VT_BSTR: + return QString::fromWCharArray(variant.bstrVal, ::SysStringLen(variant.bstrVal)); + case VT_DISPATCH: + { + IWMPMetadataPicture *picture = 0; + IWMPMetadataText *text = 0; + + if (variant.pdispVal->QueryInterface( + __uuidof(IWMPMetadataPicture), reinterpret_cast(&picture)) == S_OK) { + QUrl url; + BSTR string; + if (picture->get_URL(&string) == S_OK) { + url = QUrl(QString::fromWCharArray(string, ::SysStringLen(string))); + + ::SysFreeString(string); + } + picture->Release(); + return qVariantFromValue(url); + } else if (variant.pdispVal->QueryInterface( + __uuidof(IWMPMetadataText), reinterpret_cast(&text)) == S_OK) { + QString description; + BSTR string; + if (text->get_description(&string) == S_OK) { + description = QString::fromWCharArray(string, SysStringLen(string)); + + ::SysFreeString(string); + } + text->Release(); + return description; + } else { + qWarning("Unknown dispatch type"); + } + } + break; + default: + qWarning("Unsupported Type %d %x", variant.vt, variant.vt); + break; + } + + return QVariant(); +} + +QVariant QWmpMetaData::albumArtUrl(IWMPMedia *media, const char *suffix) +{ + BSTR string = 0; + if (media && media->get_sourceURL(&string) == S_OK) { + QString url = QString::fromWCharArray(static_cast(string)); + ::SysFreeString(string); + + if (media->getItemInfo(QAutoBStr(L"WM/WMCollectionGroupID"), &string) == S_OK) { + QString uuid = QString::fromWCharArray(static_cast(string)); + ::SysFreeString(string); + + QString fileName = QLatin1String("AlbumArt_") + uuid + QLatin1String(suffix); + + QDir dir = QFileInfo(url).absoluteDir(); + + if (dir.exists(fileName)) { + return qVariantFromValue( + QUrl(QLatin1String("file:///") + dir.absoluteFilePath(fileName))); + } + } + } + return QVariant(); +} diff --git a/src/plugins/wmp/qwmpmetadata.h b/src/plugins/wmp/qwmpmetadata.h new file mode 100644 index 000000000..b01a05c4e --- /dev/null +++ b/src/plugins/wmp/qwmpmetadata.h @@ -0,0 +1,88 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPMETADATA_H +#define QWMPMETADATA_H + +#include +#include + +#include + +QT_BEGIN_NAMESPACE +class QMediaContent; +QT_END_NAMESPACE + +class QWmpEvents; + +QT_USE_NAMESPACE + +class QWmpMetaData : public QMetaDataReaderControl +{ + Q_OBJECT +public: + QWmpMetaData(IWMPCore3 *player, QWmpEvents *events, QObject *parent = 0); + ~QWmpMetaData(); + + bool isMetaDataAvailable() const; + bool isWritable() const; + + QVariant metaData(QtMultimediaKit::MetaData key) const; + QList availableMetaData() const; + + QVariant extendedMetaData(const QString &key) const ; + QStringList availableExtendedMetaData() const; + + static QStringList keys(IWMPMedia *media); + static QVariant value(IWMPMedia *media, BSTR key); + static QMediaContent resources(IWMPMedia *media); + static QVariant convertVariant(const VARIANT &variant); + static QVariant albumArtUrl(IWMPMedia *media, const char *suffix); + +private Q_SLOTS: + void currentItemChangeEvent(IDispatch *dispatch); + void mediaChangeEvent(IDispatch *dispatch); + +private: + IWMPMedia *m_media; +}; + +#endif diff --git a/src/plugins/wmp/qwmpplayercontrol.cpp b/src/plugins/wmp/qwmpplayercontrol.cpp new file mode 100644 index 000000000..be8f107b8 --- /dev/null +++ b/src/plugins/wmp/qwmpplayercontrol.cpp @@ -0,0 +1,465 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpplayercontrol.h" + +#include "qwmpevents.h" +#include "qwmpglobal.h" +#include "qwmpmetadata.h" +#include "qwmpplaylist.h" + +#include +#include + +#include +#include +#include +#include + +QWmpPlayerControl::QWmpPlayerControl(IWMPCore3 *player, QWmpEvents *events, QObject *parent) + : QMediaPlayerControl(parent) + , m_player(player) + , m_controls(0) + , m_settings(0) + , m_state(QMediaPlayer::StoppedState) + , m_changes(0) + , m_buffering(false) + , m_audioAvailable(false) + , m_videoAvailable(false) +{ + m_player->get_controls(&m_controls); + m_player->get_settings(&m_settings); + m_player->get_network(&m_network); + + if (m_settings) + m_settings->put_autoStart(FALSE); + + WMPPlayState state = wmppsUndefined; + if (m_player->get_playState(&state) == S_OK) + playStateChangeEvent(state); + + connect(events, SIGNAL(Buffering(VARIANT_BOOL)), this, SLOT(bufferingEvent(VARIANT_BOOL))); + connect(events, SIGNAL(PositionChange(double,double)), + this, SLOT(positionChangeEvent(double,double))); + connect(events, SIGNAL(PlayStateChange(long)), this, SLOT(playStateChangeEvent(long))); + connect(events, SIGNAL(CurrentItemChange(IDispatch*)), + this, SLOT(currentItemChangeEvent(IDispatch*))); + connect(events, SIGNAL(MediaChange(IDispatch*)), this, SLOT(mediaChangeEvent(IDispatch*))); +} + +QWmpPlayerControl::~QWmpPlayerControl() +{ + if (m_controls) m_controls->Release(); + if (m_settings) m_settings->Release(); + if (m_network) m_network->Release(); +} + +QMediaPlayer::State QWmpPlayerControl::state() const +{ + return m_state; +} + +QMediaPlayer::MediaStatus QWmpPlayerControl::mediaStatus() const +{ + return m_status; +} + +qint64 QWmpPlayerControl::duration() const +{ + double duration = 0.; + + IWMPMedia *media = 0; + if (m_controls && m_controls->get_currentItem(&media) == S_OK) { + media->get_duration(&duration); + media->Release(); + } + + return duration * 1000; +} + +qint64 QWmpPlayerControl::position() const +{ + double position = 0.0; + + if (m_controls) + m_controls->get_currentPosition(&position); + + return position * 1000; +} + +void QWmpPlayerControl::setPosition(qint64 position) +{ + if (m_controls) + m_controls->put_currentPosition(double(position) / 1000.); +} + +int QWmpPlayerControl::volume() const +{ + long volume = 0; + + if (m_settings) + m_settings->get_volume(&volume); + + return volume; +} + +void QWmpPlayerControl::setVolume(int volume) +{ + if (m_settings && m_settings->put_volume(volume) == S_OK) + emit volumeChanged(volume); +} + +bool QWmpPlayerControl::isMuted() const +{ + VARIANT_BOOL mute = FALSE; + + if (m_settings) + m_settings->get_mute(&mute); + + return mute; +} + +void QWmpPlayerControl::setMuted(bool muted) +{ + if (m_settings && m_settings->put_mute(muted ? TRUE : FALSE) == S_OK) + emit mutedChanged(muted); + +} + +int QWmpPlayerControl::bufferStatus() const +{ + long progress = 0; + + if (m_network) + m_network->get_bufferingProgress(&progress); + + return progress; +} + +bool QWmpPlayerControl::isVideoAvailable() const +{ + return m_videoAvailable; +} + +bool QWmpPlayerControl::isAudioAvailable() const +{ + return m_audioAvailable; +} + +void QWmpPlayerControl::setAudioAvailable(bool available) +{ + if (m_audioAvailable != available) + emit audioAvailableChanged(m_audioAvailable = available); +} + +void QWmpPlayerControl::setVideoAvailable(bool available) +{ + if (m_videoAvailable != available) + emit videoAvailableChanged(m_videoAvailable = available); +} + +bool QWmpPlayerControl::isSeekable() const +{ + return true; +} + +QMediaTimeRange QWmpPlayerControl::availablePlaybackRanges() const +{ + QMediaTimeRange ranges; + + IWMPMedia *media = 0; + if (m_controls && m_controls->get_currentItem(&media) == S_OK) { + double duration = 0; + media->get_duration(&duration); + media->Release(); + + if(duration > 0) + ranges.addInterval(0, duration * 1000); + } + + return ranges; +} + +qreal QWmpPlayerControl::playbackRate() const +{ + double rate = 0.; + + if (m_settings) + m_settings->get_rate(&rate); + + return rate; +} + +void QWmpPlayerControl::setPlaybackRate(qreal rate) +{ + if (m_settings) + m_settings->put_rate(rate); +} + +void QWmpPlayerControl::play() +{ + if (m_controls) + m_controls->play(); +} + +void QWmpPlayerControl::pause() +{ + if (m_controls) + m_controls->pause(); +} + +void QWmpPlayerControl::stop() +{ + if (m_controls) + m_controls->stop(); +} + +QMediaContent QWmpPlayerControl::media() const +{ + QMediaResourceList resources; + + QUrl tmpUrl = url(); + + if (!tmpUrl.isEmpty()) + resources << QMediaResource(tmpUrl); + + return resources; +} + +const QIODevice *QWmpPlayerControl::mediaStream() const +{ + return 0; +} + +void QWmpPlayerControl::setMedia(const QMediaContent &content, QIODevice *stream) +{ + if (!content.isNull() && !stream) + setUrl(content.canonicalUrl()); + else + setUrl(QUrl()); +} + +bool QWmpPlayerControl::event(QEvent *event) +{ + if (event->type() == QEvent::UpdateRequest) { + const int changes = m_changes; + m_changes = 0; + + if (changes & DurationChanged) + emit durationChanged(duration()); + if (changes & PositionChanged) + emit positionChanged(position()); + if (changes & StatusChanged) + emit mediaStatusChanged(m_status); + if (changes & StateChanged) + emit stateChanged(m_state); + + return true; + } else { + return QMediaPlayerControl::event(event); + } +} + +void QWmpPlayerControl::scheduleUpdate(int change) +{ + if (m_changes == 0) + QCoreApplication::postEvent(this, new QEvent(QEvent::UpdateRequest)); + + m_changes |= change; +} + +void QWmpPlayerControl::bufferingEvent(VARIANT_BOOL buffering) +{ + if (m_state != QMediaPlayer::StoppedState) { + m_status = buffering + ? QMediaPlayer::BufferingMedia + : QMediaPlayer::BufferedMedia; + + scheduleUpdate(StatusChanged); + } +} + +void QWmpPlayerControl::currentItemChangeEvent(IDispatch *) +{ + scheduleUpdate(DurationChanged); +} + +void QWmpPlayerControl::mediaChangeEvent(IDispatch *dispatch) +{ + IWMPMedia *media = 0; + if (dispatch && dispatch->QueryInterface( + __uuidof(IWMPMedia), reinterpret_cast(&media)) == S_OK) { + IWMPMedia *currentMedia = 0; + if (m_controls && m_controls->get_currentItem(¤tMedia) == S_OK) { + VARIANT_BOOL isEqual = VARIANT_FALSE; + if (media->get_isIdentical(currentMedia, &isEqual) == S_OK && isEqual) + scheduleUpdate(DurationChanged); + + currentMedia->Release(); + } + media->Release(); + } +} + +void QWmpPlayerControl::positionChangeEvent(double , double) +{ + scheduleUpdate(PositionChanged); +} + +void QWmpPlayerControl::playStateChangeEvent(long state) +{ + switch (state) { + case wmppsUndefined: + m_state = QMediaPlayer::StoppedState; + m_status = QMediaPlayer::UnknownMediaStatus; + scheduleUpdate(StatusChanged | StateChanged); + break; + case wmppsStopped: + if (m_state != QMediaPlayer::StoppedState) { + m_state = QMediaPlayer::StoppedState; + scheduleUpdate(StateChanged); + + if (m_status != QMediaPlayer::EndOfMedia) { + m_status = QMediaPlayer::LoadedMedia; + scheduleUpdate(StatusChanged); + } + } + break; + case wmppsPaused: + if (m_state != QMediaPlayer::PausedState && m_status != QMediaPlayer::BufferedMedia) { + m_state = QMediaPlayer::PausedState; + m_status = QMediaPlayer::BufferedMedia; + scheduleUpdate(StatusChanged | StateChanged); + } else if (m_state != QMediaPlayer::PausedState) { + m_state = QMediaPlayer::PausedState; + scheduleUpdate(StateChanged); + } else if (m_status != QMediaPlayer::BufferedMedia) { + m_status = QMediaPlayer::BufferedMedia; + + scheduleUpdate(StatusChanged); + } + break; + case wmppsPlaying: + case wmppsScanForward: + case wmppsScanReverse: + if (m_state != QMediaPlayer::PlayingState && m_status != QMediaPlayer::BufferedMedia) { + m_state = QMediaPlayer::PlayingState; + m_status = QMediaPlayer::BufferedMedia; + scheduleUpdate(StatusChanged | StateChanged); + } else if (m_state != QMediaPlayer::PlayingState) { + m_state = QMediaPlayer::PlayingState; + scheduleUpdate(StateChanged); + } else if (m_status != QMediaPlayer::BufferedMedia) { + m_status = QMediaPlayer::BufferedMedia; + scheduleUpdate(StatusChanged); + } + + if (m_state != QMediaPlayer::PlayingState) { + m_state = QMediaPlayer::PlayingState; + scheduleUpdate(StateChanged); + } + if (m_status != QMediaPlayer::BufferedMedia) { + m_status = QMediaPlayer::BufferedMedia; + scheduleUpdate(StatusChanged); + } + break; + case wmppsBuffering: + case wmppsWaiting: + if (m_status != QMediaPlayer::StalledMedia && m_state != QMediaPlayer::StoppedState) { + m_status = QMediaPlayer::StalledMedia; + scheduleUpdate(StatusChanged); + } + break; + case wmppsMediaEnded: + if (m_status != QMediaPlayer::EndOfMedia && m_state != QMediaPlayer::StoppedState) { + m_state = QMediaPlayer::StoppedState; + m_status = QMediaPlayer::EndOfMedia; + scheduleUpdate(StatusChanged | StateChanged); + } + break; + case wmppsTransitioning: + break; + case wmppsReady: + if (m_status != QMediaPlayer::LoadedMedia) { + m_status = QMediaPlayer::LoadedMedia; + scheduleUpdate(StatusChanged); + } + + if (m_state != QMediaPlayer::StoppedState) { + m_state = QMediaPlayer::StoppedState; + scheduleUpdate(StateChanged); + } + break; + case wmppsReconnecting: + if (m_status != QMediaPlayer::StalledMedia && m_state != QMediaPlayer::StoppedState) { + m_status = QMediaPlayer::StalledMedia; + scheduleUpdate(StatusChanged); + } + break; + default: + break; + } +} + +QUrl QWmpPlayerControl::url() const +{ + BSTR string; + if (m_player && m_player->get_URL(&string) == S_OK) { + QUrl url(QString::fromWCharArray(string, SysStringLen(string)), QUrl::StrictMode); + + SysFreeString(string); + + return url; + } else { + return QUrl(); + } +} + +void QWmpPlayerControl::setUrl(const QUrl &url) +{ + if (url != QWmpPlayerControl::url() && m_player) { + BSTR string = SysAllocString(reinterpret_cast(url.toString().unicode())); + + m_player->put_URL(string); + + SysFreeString(string); + } +} diff --git a/src/plugins/wmp/qwmpplayercontrol.h b/src/plugins/wmp/qwmpplayercontrol.h new file mode 100644 index 000000000..d966b38b2 --- /dev/null +++ b/src/plugins/wmp/qwmpplayercontrol.h @@ -0,0 +1,141 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPPLAYERCONTROL_H +#define QWMPPLAYERCONTROL_H + +#include + +#include + +class QWmpEvents; +class QWmpPlaylist; + +QT_USE_NAMESPACE +class QWmpPlayerControl : public QMediaPlayerControl +{ + Q_OBJECT +public: + QWmpPlayerControl( + IWMPCore3 *player, QWmpEvents *events, QObject *parent = 0); + ~QWmpPlayerControl(); + + QMediaPlayer::State state() const; + QMediaPlayer::MediaStatus mediaStatus() const; + + QMediaPlaylist* mediaPlaylist() const; + bool setMediaPlaylist(QMediaPlaylist *playlist); + + qint64 duration() const; + + qint64 position() const; + void setPosition(qint64 position); + + int playlistPosition() const; + void setPlaylistPosition(int position); + + int volume() const; + void setVolume(int volume); + + bool isMuted() const; + void setMuted(bool muted); + + int bufferStatus() const; + + bool isAudioAvailable() const; + void setAudioAvailable(bool available); + + bool isVideoAvailable() const; + void setVideoAvailable(bool available); + + qreal playbackRate() const; + void setPlaybackRate(qreal rate); + + bool isSeekable() const; + QMediaTimeRange availablePlaybackRanges() const; + + void play(); + void pause(); + void stop(); + + QMediaContent media() const; + const QIODevice *mediaStream() const; + void setMedia(const QMediaContent &content, QIODevice *stream); + + QUrl url() const; + void setUrl(const QUrl &url); + + bool event(QEvent *event); + + using QMediaPlayerControl::positionChanged; + +private Q_SLOTS: + + void bufferingEvent(VARIANT_BOOL buffering); + void currentItemChangeEvent(IDispatch *dispatch); + void mediaChangeEvent(IDispatch *dispatch); + void positionChangeEvent(double from, double to); + void playStateChangeEvent(long state); + +private: + enum Change + { + StateChanged = 0x01, + StatusChanged = 0x02, + PositionChanged = 0x04, + DurationChanged = 0x08 + }; + + void scheduleUpdate(int change); + + IWMPCore3 *m_player; + IWMPControls *m_controls; + IWMPSettings *m_settings; + IWMPNetwork *m_network; + QMediaPlayer::State m_state; + QMediaPlayer::MediaStatus m_status; + int m_changes; + bool m_buffering; + bool m_audioAvailable; + bool m_videoAvailable; +}; + +#endif diff --git a/src/plugins/wmp/qwmpplayerservice.cpp b/src/plugins/wmp/qwmpplayerservice.cpp new file mode 100644 index 000000000..e185aaf46 --- /dev/null +++ b/src/plugins/wmp/qwmpplayerservice.cpp @@ -0,0 +1,355 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpplayerservice.h" + +#ifdef QWMP_EVR +#include "qevrvideooverlay.h" +#endif + +#include "qwmpglobal.h" +#include "qwmpmetadata.h" +#include "qwmpplayercontrol.h" +#include "qwmpplaylist.h" +#include "qwmpplaylistcontrol.h" +#include "qwmpvideooverlay.h" + +#include + +#include +#include +#include +#include + +#include +#include + +QWmpPlayerService::QWmpPlayerService(EmbedMode mode, QObject *parent) + : QMediaService(parent) + , m_ref(1) + , m_embedMode(mode) + , m_player(0) + , m_oleObject(0) + , m_events(0) + , m_control(0) + , m_metaData(0) + , m_playlist(0) + , m_activeVideoOverlay(0) + , m_oleVideoOverlay(0) +#ifdef QWMP_EVR + , m_evrVideoOverlay(0) +#endif +{ + HRESULT hr; + + if ((hr = CoCreateInstance( + __uuidof(WindowsMediaPlayer), + 0, + CLSCTX_INPROC_SERVER, + __uuidof(IWMPPlayer4), + reinterpret_cast(&m_player))) != S_OK) { + qWarning("failed to create media player control, %x: %s", hr, qwmp_error_string(hr)); + } else { + m_events = new QWmpEvents(m_player); + + if ((hr = m_player->QueryInterface( + __uuidof(IOleObject), reinterpret_cast(&m_oleObject))) != S_OK) { + qWarning("No IOleObject interface, %x: %s", hr, qwmp_error_string(hr)); + } else if ((hr = m_oleObject->SetClientSite(this)) != S_OK) { + qWarning("Failed to set site, %x: %s", hr, qwmp_error_string(hr)); + } + + if (m_embedMode == LocalEmbed) + m_oleVideoOverlay = new QWmpVideoOverlay(m_player, m_oleObject, this); + + m_metaData = new QWmpMetaData(m_player, m_events); + m_playlist = new QWmpPlaylistControl(m_player, m_events); + m_control = new QWmpPlayerControl(m_player, m_events); + } +} + +QWmpPlayerService::~QWmpPlayerService() +{ + delete m_control; + delete m_metaData; + delete m_playlist; + delete m_events; + + if (m_oleObject) { + m_oleObject->SetClientSite(0); + m_oleObject->Release(); + delete m_oleVideoOverlay; + } + +#ifdef QWMP_EVR + delete m_evrVideoOverlay; +#endif + + + if (m_player) + m_player->Release(); + + Q_ASSERT(m_ref == 1); +} + +QMediaControl *QWmpPlayerService::requestControl(const char *name) +{ + if (qstrcmp(name, QMediaPlayerControl_iid) == 0) { + return m_control; + } else if (qstrcmp(name, QMetaDataReaderControl_iid) == 0) { + return m_metaData; + } else if (qstrcmp(name, QMediaPlaylistControl_iid) == 0) { + return m_playlist; + } else if (qstrcmp(name, QVideoWindowControl_iid) == 0 + && m_embedMode == LocalEmbed + && m_player + && !m_activeVideoOverlay) { +#ifdef QWMP_EVR + IWMPVideoRenderConfig *config = 0; + if (m_player->QueryInterface( + __uuidof(IWMPVideoRenderConfig), reinterpret_cast(&config)) == S_OK) { + if (HINSTANCE evrHwnd = LoadLibrary(L"evr")) { + m_evrVideoOverlay = new QEvrVideoOverlay(evrHwnd); + + if (SUCCEEDED(config->put_presenterActivate( + static_cast(m_evrVideoOverlay)))) { + connect(m_events, SIGNAL(OpenStateChange(long)), + m_evrVideoOverlay, SLOT(openStateChanged(long))); + } else { + delete m_evrVideoOverlay; + + m_evrVideoOverlay = 0; + } + } + config->Release(); + } + + if (m_evrVideoOverlay) { + m_activeVideoOverlay = m_evrVideoOverlay; + + return m_evrVideoOverlay; + } else +#endif + if (SUCCEEDED(m_player->put_uiMode(QAutoBStr(L"none")))) { + m_activeVideoOverlay = m_oleVideoOverlay; + + return m_oleVideoOverlay; + } + } + return 0; +} + +void QWmpPlayerService::releaseControl(QMediaControl *control) +{ + if (!control) { + qWarning("QMediaService::releaseControl():" + " Attempted release of null control"); +#ifdef QWMP_EVR + } else if (control == m_evrVideoOverlay) { + + IWMPVideoRenderConfig *config = 0; + if (m_player->QueryInterface( + __uuidof(IWMPVideoRenderConfig), reinterpret_cast(&config)) == S_OK) { + config->put_presenterActivate(0); + config->Release(); + } + + delete m_evrVideoOverlay; + + m_evrVideoOverlay = 0; + m_activeVideoOverlay = 0; +#endif + } else if (control == m_oleVideoOverlay) { + m_player->put_uiMode(QAutoBStr(L"invisible")); + m_oleVideoOverlay->setWinId(0); + + m_activeVideoOverlay = 0; + } +} + +// IUnknown +HRESULT QWmpPlayerService::QueryInterface(REFIID riid, void **object) +{ + if (!object) { + return E_POINTER; + } else if (riid == __uuidof(IUnknown) + || riid == __uuidof(IOleClientSite)) { + *object = static_cast(this); + } else if (riid == __uuidof(IServiceProvider)) { + *object = static_cast(this); + } else if (riid == __uuidof(IWMPRemoteMediaServices)) { + *object = static_cast(this); + } else if (riid == __uuidof(IOleWindow) + || riid == __uuidof(IOleInPlaceSite)) { + *object = static_cast(m_oleVideoOverlay); + } else if (riid == __uuidof(IOleInPlaceUIWindow) + || riid == __uuidof(IOleInPlaceFrame)) { + *object = static_cast(m_oleVideoOverlay); + } else { + *object = 0; + } + + if (*object) { + AddRef(); + + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +ULONG QWmpPlayerService::AddRef() +{ + return InterlockedIncrement(&m_ref); +} + +ULONG QWmpPlayerService::Release() +{ + ULONG ref = InterlockedDecrement(&m_ref); + + Q_ASSERT(ref != 0); + + return ref; +} + +// IOleClientSite +HRESULT QWmpPlayerService::SaveObject() +{ + return E_NOTIMPL; +} + +HRESULT QWmpPlayerService::GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk) +{ + Q_UNUSED(dwAssign); + Q_UNUSED(dwWhichMoniker); + Q_UNUSED(ppmk); + + return E_NOTIMPL; +} + +HRESULT QWmpPlayerService::GetContainer(IOleContainer **ppContainer) +{ + if (!ppContainer) { + return E_POINTER; + } else { + *ppContainer = 0; + + return E_NOINTERFACE; + } +} + +HRESULT QWmpPlayerService::ShowObject() +{ + return S_OK; +} + +HRESULT QWmpPlayerService::OnShowWindow(BOOL fShow) +{ + Q_UNUSED(fShow); + + return S_OK; +} + +HRESULT QWmpPlayerService::RequestNewObjectLayout() +{ + return E_NOTIMPL; +} + +// IServiceProvider +HRESULT QWmpPlayerService::QueryService(REFGUID guidService, REFIID riid, void **ppvObject) +{ + Q_UNUSED(guidService); + + if (!ppvObject) { + return E_POINTER; + } else if (riid == __uuidof(IWMPRemoteMediaServices)) { + *ppvObject = static_cast(this); + + AddRef(); + + return S_OK; + } else { + return E_NOINTERFACE; + } +} + +// IWMPRemoteMediaServices +HRESULT QWmpPlayerService::GetServiceType(BSTR *pbstrType) +{ + if (!pbstrType) { + return E_POINTER; + } else if (m_embedMode == RemoteEmbed) { + *pbstrType = ::SysAllocString(L"Remote"); + + return S_OK; + } else { + *pbstrType = ::SysAllocString(L"Local"); + + return S_OK; + } +} + +HRESULT QWmpPlayerService::GetApplicationName(BSTR *pbstrName) +{ + if (!pbstrName) { + return E_POINTER; + } else { + *pbstrName = ::SysAllocString(static_cast( + QCoreApplication::applicationName().utf16())); + + return S_OK; + } +} + +HRESULT QWmpPlayerService::GetScriptableObject(BSTR *pbstrName, IDispatch **ppDispatch) +{ + Q_UNUSED(pbstrName); + Q_UNUSED(ppDispatch); + + return E_NOTIMPL; +} + +HRESULT QWmpPlayerService::GetCustomUIMode(BSTR *pbstrFile) +{ + Q_UNUSED(pbstrFile); + + return E_NOTIMPL; +} diff --git a/src/plugins/wmp/qwmpplayerservice.h b/src/plugins/wmp/qwmpplayerservice.h new file mode 100644 index 000000000..ba79a9a68 --- /dev/null +++ b/src/plugins/wmp/qwmpplayerservice.h @@ -0,0 +1,125 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPPLAYERSERVICE_H +#define QWMPPLAYERSERVICE_H + +#include "qwmpevents.h" + +#include + +QT_BEGIN_NAMESPACE +class QMediaMetaData; +class QMediaPlayerControl; +class QMediaPlaylist; +class QVideoWindowControl; +QT_END_NAMESPACE + +#ifdef QWMP_EVR +class QEvrVideoOverlay; +#endif + +class QWmpMetaData; +class QWmpPlayerControl; +class QWmpPlaylist; +class QWmpPlaylistControl; +class QWmpVideoOverlay; + +QT_USE_NAMESPACE +class QWmpPlayerService + : public QMediaService + , public IOleClientSite + , public IServiceProvider + , public IWMPRemoteMediaServices +{ + Q_OBJECT +public: + enum EmbedMode + { + LocalEmbed, + RemoteEmbed + }; + + QWmpPlayerService(EmbedMode mode, QObject *parent = 0); + ~QWmpPlayerService(); + + QMediaControl *requestControl(const char *name); + void releaseControl(QMediaControl *control); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IOleClientSite + HRESULT STDMETHODCALLTYPE SaveObject(); + HRESULT STDMETHODCALLTYPE GetMoniker(DWORD dwAssign, DWORD dwWhichMoniker, IMoniker **ppmk); + HRESULT STDMETHODCALLTYPE GetContainer(IOleContainer **ppContainer); + HRESULT STDMETHODCALLTYPE ShowObject(); + HRESULT STDMETHODCALLTYPE OnShowWindow(BOOL fShow); + HRESULT STDMETHODCALLTYPE RequestNewObjectLayout(); + + // IServiceProvider + HRESULT STDMETHODCALLTYPE QueryService(REFGUID guidService, REFIID riid, void **ppvObject); + + // IWMPRemoteMediaServices + HRESULT STDMETHODCALLTYPE GetServiceType(BSTR *pbstrType); + HRESULT STDMETHODCALLTYPE GetApplicationName(BSTR *pbstrName); + HRESULT STDMETHODCALLTYPE GetScriptableObject(BSTR *pbstrName, IDispatch **ppDispatch); + HRESULT STDMETHODCALLTYPE GetCustomUIMode(BSTR *pbstrFile); + +private: + volatile LONG m_ref; + const EmbedMode m_embedMode; + IWMPPlayer4 *m_player; + IOleObject *m_oleObject; + QWmpEvents *m_events; + QWmpPlayerControl *m_control; + QWmpMetaData *m_metaData; + QWmpPlaylistControl *m_playlist; + QVideoWindowControl *m_activeVideoOverlay; + QWmpVideoOverlay *m_oleVideoOverlay; +#ifdef QWMP_EVR + QEvrVideoOverlay *m_evrVideoOverlay; +#endif +}; + +#endif diff --git a/src/plugins/wmp/qwmpplaylist.cpp b/src/plugins/wmp/qwmpplaylist.cpp new file mode 100644 index 000000000..10ce753b1 --- /dev/null +++ b/src/plugins/wmp/qwmpplaylist.cpp @@ -0,0 +1,296 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpplaylist.h" + +#include "qwmpevents.h" +#include "qwmpmetadata.h" +#include "qwmpglobal.h" + +#include +#include +#include + +QWmpPlaylist::QWmpPlaylist(IWMPCore3 *player, QWmpEvents *events, QObject *parent) + : QMediaPlaylistProvider(parent) + , m_player(player) + , m_playlist(0) + , m_count(0) +{ + if (m_player && m_player->get_currentPlaylist(&m_playlist) == S_OK) + m_playlist->get_count(&m_count); + + connect(events, SIGNAL(CurrentPlaylistChange(WMPPlaylistChangeEventType)), + this, SLOT(currentPlaylistChangeEvent(WMPPlaylistChangeEventType))); + connect(events, SIGNAL(OpenPlaylistSwitch(IDispatch*)), + this, SLOT(openPlaylistChangeEvent(IDispatch*))); + connect(events, SIGNAL(MediaChange(IDispatch*)), this, SLOT(mediaChangeEvent(IDispatch*))); +} + +QWmpPlaylist::~QWmpPlaylist() +{ + if (m_playlist) + m_playlist->Release(); +} + +bool QWmpPlaylist::load(const QString &location, const char *format) +{ + Q_UNUSED(location); + Q_UNUSED(format); + + return false; +} + +bool QWmpPlaylist::load(QIODevice * device, const char *format) +{ + Q_UNUSED(device); + Q_UNUSED(format); + + return false; +} + +bool QWmpPlaylist::save(const QString &location, const char *format) +{ + Q_UNUSED(location); + Q_UNUSED(format); + + return false; +} + +bool QWmpPlaylist::save(QIODevice * device, const char *format) +{ + Q_UNUSED(device); + Q_UNUSED(format); + + return false; +} + +int QWmpPlaylist::mediaCount() const +{ + return m_count; +} + +QMediaContent QWmpPlaylist::media(int pos) const +{ + QMediaContent content; + + IWMPMedia *media = 0; + if (m_playlist && m_playlist->get_item(pos, &media) == S_OK) { + content = QWmpMetaData::resources(media); + + media->Release(); + } + + return content; +} + +bool QWmpPlaylist::isReadOnly() const +{ + return false; +} + +bool QWmpPlaylist::addMedia(const QMediaContent &content) +{ + bool appended = false; + + IWMPMedia *media = 0; + if (!content.isNull() && m_playlist && m_player && m_player->newMedia( + QAutoBStr(content.canonicalUrl()), &media) == S_OK) { + appended = m_playlist->appendItem(media) == S_OK; + + media->Release(); + } + + return appended; +} + +bool QWmpPlaylist::insertMedia(int pos, const QMediaContent &content) +{ + bool inserted = false; + + IWMPMedia *media = 0; + if (m_playlist && m_player && m_player->newMedia( + QAutoBStr(content.canonicalUrl()), &media) == S_OK) { + inserted = m_playlist->insertItem(pos, media) == S_OK; + + media->Release(); + } + + return inserted; +} + +bool QWmpPlaylist::removeMedia(int pos) +{ + IWMPMedia *media = 0; + if (m_playlist->get_item(pos, &media) == S_OK) { + bool removed = m_playlist->removeItem(media) == S_OK; + + media->Release(); + + return removed; + } else { + return false; + } +} + +bool QWmpPlaylist::removeMedia(int start, int end) +{ + if (!m_playlist) + return false; + + for (int i = start; i <= end; ++i) { + IWMPMedia *media = 0; + if (m_playlist->get_item(start, &media) == S_OK) { + bool removed = m_playlist->removeItem(media) == S_OK; + + media->Release(); + + if (!removed) + return false; + } + } + return true; +} + +bool QWmpPlaylist::clear() +{ + return m_playlist && m_playlist->clear() == S_OK; +} + +QStringList QWmpPlaylist::keys(int index) const +{ + QStringList keys; + + IWMPMedia *media = 0; + if (m_playlist && m_playlist->get_item(index, &media) == S_OK) { + keys = QWmpMetaData::keys(media); + + media->Release(); + } + + return keys; +} + +QVariant QWmpPlaylist::value(int index, const QString &key) const +{ + QVariant v; + + IWMPMedia *media = 0; + if (m_playlist && m_playlist->get_item(index, &media) == S_OK) { + v = QWmpMetaData::value(media, QAutoBStr(key)); + + media->Release(); + } + + return v; +} + +void QWmpPlaylist::shuffle() +{ +} + + +void QWmpPlaylist::currentPlaylistChangeEvent(WMPPlaylistChangeEventType change) +{ + Q_UNUSED(change); + + long count = 0; + if (m_playlist && m_playlist->get_count(&count) == S_OK && count > 0) { + if (count > m_count) { + emit mediaAboutToBeInserted(m_count, count - 1); + m_count = count; + emit mediaInserted(count, m_count - 1); + } else if (count < m_count) { + emit mediaAboutToBeRemoved(count, m_count - 1); + m_count = count; + emit mediaRemoved(count, m_count - 1); + } + } + if (m_count > 0) + emit mediaChanged(0, m_count - 1); +} + +void QWmpPlaylist::openPlaylistChangeEvent(IDispatch *dispatch) +{ + if (m_playlist && m_count > 0) { + emit mediaAboutToBeRemoved(0, m_count - 1); + m_playlist->Release(); + m_playlist = 0; + m_count = 0; + emit mediaRemoved(0, m_count - 1); + } else if (m_playlist) { + m_playlist->Release(); + m_playlist = 0; + } + + IWMPPlaylist *playlist = 0; + if (dispatch && dispatch->QueryInterface( + __uuidof(IWMPPlaylist), reinterpret_cast(&playlist))) { + + long count = 0; + if (playlist->get_count(&count) == S_OK && count > 0) { + emit mediaAboutToBeInserted(0, count - 1); + m_playlist = playlist; + m_count = count; + emit mediaInserted(0, count - 1); + } else { + m_playlist = playlist; + } + } +} + +void QWmpPlaylist::mediaChangeEvent(IDispatch *dispatch) +{ + + IWMPMedia *media = 0; + if (dispatch && dispatch->QueryInterface( + __uuidof(IWMPMedia), reinterpret_cast(&media)) == S_OK) { + VARIANT_BOOL isMember = VARIANT_FALSE; + + if (media->isMemberOf(m_playlist, &isMember) == S_OK && isMember) { + int index = QWmpMetaData::value(media, QAutoBStr(L"PlaylistIndex")).toInt(); + + if (index >= 0) + emit mediaChanged(index, index); + } + media->Release(); + } +} diff --git a/src/plugins/wmp/qwmpplaylist.h b/src/plugins/wmp/qwmpplaylist.h new file mode 100644 index 000000000..be168a7b5 --- /dev/null +++ b/src/plugins/wmp/qwmpplaylist.h @@ -0,0 +1,94 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPPLAYLIST_H +#define QWMPPLAYLIST_H + +#include + +#include + +#include + +class QWmpEvents; + +QT_USE_NAMESPACE +class QWmpPlaylist : public QMediaPlaylistProvider +{ + Q_OBJECT +public: + QWmpPlaylist(IWMPCore3 *player, QWmpEvents *events, QObject *parent = 0); + ~QWmpPlaylist(); + + bool load(const QString &location, const char *format = 0); + bool load(QIODevice * device, const char *format = 0); + bool save(const QString &location, const char *format = 0); + bool save(QIODevice * device, const char *format); + + int mediaCount() const; + QMediaContent media(int pos) const; + + bool isReadOnly() const; + + bool addMedia(const QMediaContent &content); + bool insertMedia(int pos, const QMediaContent &content); + bool removeMedia(int pos); + bool removeMedia(int start, int end); + bool clear(); + + QStringList keys(int index) const; + QVariant value(int index, const QString &key) const; + +public Q_SLOTS: + virtual void shuffle(); + +private Q_SLOTS: + void currentPlaylistChangeEvent(WMPPlaylistChangeEventType change); + void openPlaylistChangeEvent(IDispatch *dispatch); + void mediaChangeEvent(IDispatch *dispatch); + +private: + IWMPCore3 *m_player; + IWMPPlaylist *m_playlist; + long m_count; +}; + +#endif diff --git a/src/plugins/wmp/qwmpplaylistcontrol.cpp b/src/plugins/wmp/qwmpplaylistcontrol.cpp new file mode 100644 index 000000000..f4cc12f7f --- /dev/null +++ b/src/plugins/wmp/qwmpplaylistcontrol.cpp @@ -0,0 +1,153 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpplaylistcontrol.h" + +#include "qwmpevents.h" +#include "qwmpglobal.h" +#include "qwmpmetadata.h" +#include "qwmpplaylist.h" + +QWmpPlaylistControl::QWmpPlaylistControl(IWMPCore3 *player, QWmpEvents *events, QObject *parent) + : QMediaPlaylistControl(parent) + , m_player(player) + , m_controls(0) + , m_playlist(new QWmpPlaylist(player, events)) + , m_playbackMode(QMediaPlaylist::Sequential) +{ + m_player->get_controls(&m_controls); + + connect(events, SIGNAL(CurrentItemChange(IDispatch*)), + this, SLOT(currentItemChangeEvent(IDispatch*))); +} + +QWmpPlaylistControl::~QWmpPlaylistControl() +{ + if (m_controls) + m_controls->Release(); + + delete m_playlist; +} + +QMediaPlaylistProvider *QWmpPlaylistControl::playlistProvider() const +{ + return m_playlist; +} + +bool QWmpPlaylistControl::setPlaylistProvider(QMediaPlaylistProvider *playlist) +{ + Q_UNUSED(playlist); + + return false; +} + +int QWmpPlaylistControl::currentIndex() const +{ + int position = 0; + + IWMPMedia *media = 0; + if (m_controls && m_player->get_currentMedia(&media) == S_OK) { + position = QWmpMetaData::value(media, QAutoBStr(L"PlaylistIndex")).toInt(); + + media->Release(); + } + + return position; +} + +void QWmpPlaylistControl::setCurrentIndex(int position) +{ + + IWMPPlaylist *playlist = 0; + if (m_player->get_currentPlaylist(&playlist) == S_OK) { + IWMPMedia *media = 0; + if (playlist->get_item(position, &media) == S_OK) { + m_player->put_currentMedia(media); + + media->Release(); + } + playlist->Release(); + } +} + +int QWmpPlaylistControl::nextIndex(int steps) const +{ + return currentIndex() + steps; +} + +int QWmpPlaylistControl::previousIndex(int steps) const +{ + return currentIndex() - steps; +} + +void QWmpPlaylistControl::next() +{ + if (m_controls) + m_controls->next(); +} + +void QWmpPlaylistControl::previous() +{ + if (m_controls) + m_controls->previous(); +} + +QMediaPlaylist::PlaybackMode QWmpPlaylistControl::playbackMode() const +{ + return m_playbackMode; +} + +void QWmpPlaylistControl::setPlaybackMode(QMediaPlaylist::PlaybackMode mode) +{ + m_playbackMode = mode; +} + +void QWmpPlaylistControl::currentItemChangeEvent(IDispatch *dispatch) +{ + IWMPMedia *media = 0; + if (dispatch && dispatch->QueryInterface( + __uuidof(IWMPMedia), reinterpret_cast(&media)) == S_OK) { + int index = QWmpMetaData::value(media, QAutoBStr(L"PlaylistIndex")).toInt(); + + emit currentIndexChanged(index); + emit currentMediaChanged(m_playlist->media(index)); + } +} diff --git a/src/plugins/wmp/qwmpplaylistcontrol.h b/src/plugins/wmp/qwmpplaylistcontrol.h new file mode 100644 index 000000000..cf519aebb --- /dev/null +++ b/src/plugins/wmp/qwmpplaylistcontrol.h @@ -0,0 +1,85 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPPLAYLISTCONTROL_H +#define QWMPPLAYLISTCONTROL_H + +#include + +#include + +class QWmpEvents; +class QWmpPlaylist; + +QT_USE_NAMESPACE +class QWmpPlaylistControl : public QMediaPlaylistControl +{ + Q_OBJECT +public: + QWmpPlaylistControl(IWMPCore3 *player, QWmpEvents *events, QObject *parent = 0); + ~QWmpPlaylistControl(); + + QMediaPlaylistProvider *playlistProvider() const; + bool setPlaylistProvider(QMediaPlaylistProvider *playlist); + + int currentIndex() const; + void setCurrentIndex(int position); + + int nextIndex(int steps) const; + int previousIndex(int steps) const; + + void next(); + void previous(); + + QMediaPlaylist::PlaybackMode playbackMode() const; + void setPlaybackMode(QMediaPlaylist::PlaybackMode mode); + +private slots: + void currentItemChangeEvent(IDispatch *dispatch); + +private: + IWMPCore3 *m_player; + IWMPControls *m_controls; + QWmpPlaylist *m_playlist; + QMediaPlaylist::PlaybackMode m_playbackMode; +}; + +#endif diff --git a/src/plugins/wmp/qwmpserviceprovider.cpp b/src/plugins/wmp/qwmpserviceprovider.cpp new file mode 100644 index 000000000..eec06008e --- /dev/null +++ b/src/plugins/wmp/qwmpserviceprovider.cpp @@ -0,0 +1,74 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpserviceprovider.h" + +#include "qwmpplayerservice.h" + +QStringList QWmpServiceProviderPlugin::keys() const +{ + return QStringList() + << QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) + << QLatin1String("windowsmediaplayer"); +} + +QMediaService *QWmpServiceProviderPlugin::create(const QString &key) +{ + if (QLatin1String(Q_MEDIASERVICE_MEDIAPLAYER) == key) { + QByteArray providerKey = qgetenv("QT_MEDIAPLAYER_PROVIDER"); + if (!providerKey.isNull() && qstrcmp(providerKey.constData(), "windowsmediaplayer") == 0) + return new QWmpPlayerService(QWmpPlayerService::RemoteEmbed); + + return new QWmpPlayerService(QWmpPlayerService::LocalEmbed); + } + else if (QLatin1String("windowsmediaplayer") == key) + return new QWmpPlayerService(QWmpPlayerService::RemoteEmbed); + + return 0; +} + +void QWmpServiceProviderPlugin::release(QMediaService *service) +{ + delete service; +} + + +Q_EXPORT_PLUGIN2(qtmedia_wmp, QWmpServiceProviderPlugin); diff --git a/src/plugins/wmp/qwmpserviceprovider.h b/src/plugins/wmp/qwmpserviceprovider.h new file mode 100644 index 000000000..d58fb3bc8 --- /dev/null +++ b/src/plugins/wmp/qwmpserviceprovider.h @@ -0,0 +1,58 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPSERVICEPROVIDER_H +#define QWMPSERVICEPROVIDER_H + +#include +#include + +QT_USE_NAMESPACE +class QWmpServiceProviderPlugin : public QMediaServiceProviderPlugin +{ + Q_OBJECT +public: + QStringList keys() const; + QMediaService *create(const QString &key); + void release(QMediaService *service); +}; + +#endif diff --git a/src/plugins/wmp/qwmpvideooverlay.cpp b/src/plugins/wmp/qwmpvideooverlay.cpp new file mode 100644 index 000000000..95ff77b6b --- /dev/null +++ b/src/plugins/wmp/qwmpvideooverlay.cpp @@ -0,0 +1,462 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#include "qwmpvideooverlay.h" + +#include "qwmpglobal.h" + +QWmpVideoOverlay::QWmpVideoOverlay(IWMPPlayer4 *player, IOleObject *object, QWmpPlayerService *service) + : m_service(service) + , m_player(player) + , m_object(object) + , m_inPlaceObject(0) + , m_winId(0) + , m_aspectRatioMode(Qt::KeepAspectRatio) + , m_fullScreen(false) +{ + HRESULT hr; + + if ((hr = m_object->QueryInterface( + __uuidof(IOleInPlaceObject), + reinterpret_cast(&m_inPlaceObject))) != S_OK) { + qWarning("No IOleInPlaceObject interface, %x: %s", hr, qwmp_error_string(hr)); + } +} + +QWmpVideoOverlay::~QWmpVideoOverlay() +{ + if (m_inPlaceObject) + m_inPlaceObject->Release(); +} + +WId QWmpVideoOverlay::winId() const +{ + return m_winId; +} + +void QWmpVideoOverlay::setWinId(WId id) +{ + m_winId = id; + + if (QWidget *widget = QWidget::find(m_winId)) { + const QColor color = widget->palette().color(QPalette::Window); + + m_windowColor = RGB(color.red(), color.green(), color.blue()); + } + + if (m_inPlaceObject && m_winId) { + RECT rcPos = + { + m_displayRect.left(), + m_displayRect.top(), + m_displayRect.right(), + m_displayRect.bottom() + }; + + m_inPlaceObject->InPlaceDeactivate(); + m_object->DoVerb(OLEIVERB_INPLACEACTIVATE, 0, m_service, 0, m_winId, &rcPos); + } + + +} + +extern HDC Q_GUI_EXPORT qt_win_display_dc(); + +#define HIMETRIC_PER_INCH 2540 +#define MAP_PIX_TO_LOGHIM(x,ppli) ((HIMETRIC_PER_INCH*(x) + ((ppli)>>1)) / (ppli)) +#define MAP_LOGHIM_TO_PIX(x,ppli) (((ppli)*(x) + HIMETRIC_PER_INCH/2) / HIMETRIC_PER_INCH) + +QRect QWmpVideoOverlay::displayRect() const +{ + return m_displayRect; +} + +void QWmpVideoOverlay::setDisplayRect(const QRect &rect) +{ + m_displayRect = rect; + + if (m_inPlaceObject) { + HDC gdc = QT_PREPEND_NAMESPACE(qt_win_display_dc)(); + + SIZEL hmSize = { + MAP_PIX_TO_LOGHIM(rect.width(), GetDeviceCaps(gdc, LOGPIXELSX)), + MAP_PIX_TO_LOGHIM(rect.height(), GetDeviceCaps(gdc, LOGPIXELSY)) }; + + m_object->SetExtent(DVASPECT_CONTENT, &hmSize); + + RECT rcClip = { rect.left(), rect.top(), rect.right(), rect.bottom() }; + + if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { + QSize size = m_sizeHint; + size.scale(rect.width(), rect.height(), Qt::KeepAspectRatioByExpanding); + + QRect positionRect(QPoint(0, 0), size); + positionRect.moveCenter(rect.center()); + + RECT rcPos = + { + positionRect.left(), + positionRect.top(), + positionRect.right(), + positionRect.bottom() + }; + + m_inPlaceObject->SetObjectRects(&rcPos, &rcClip); + } else { + m_inPlaceObject->SetObjectRects(&rcClip, &rcClip); + } + } +} + +bool QWmpVideoOverlay::isFullScreen() const +{ + return m_fullScreen; +} + +void QWmpVideoOverlay::setFullScreen(bool fullScreen) +{ + m_player->put_fullScreen(fullScreen); + + emit fullScreenChanged(m_fullScreen = fullScreen); +} + +QSize QWmpVideoOverlay::nativeSize() const +{ + return m_sizeHint; +} + +void QWmpVideoOverlay::setNativeSize(const QSize &size) +{ + if (m_sizeHint != size) { + m_sizeHint = size; + + emit nativeSizeChanged(); + } +} + +Qt::AspectRatioMode QWmpVideoOverlay::aspectRatioMode() const +{ + return m_aspectRatioMode; +} + +void QWmpVideoOverlay::setAspectRatioMode(Qt::AspectRatioMode mode) +{ + m_aspectRatioMode = mode; + + m_player->put_stretchToFit(mode != Qt::KeepAspectRatio); + + setDisplayRect(m_displayRect); +} + +void QWmpVideoOverlay::repaint() +{ + PAINTSTRUCT paint; + + if (HDC dc = ::BeginPaint(m_winId, &paint)) { + HPEN pen = ::CreatePen(PS_SOLID, 1, m_windowColor); + HBRUSH brush = ::CreateSolidBrush(m_windowColor); + ::SelectObject(dc, pen); + ::SelectObject(dc, brush); + + ::Rectangle( + dc, + m_displayRect.left(), + m_displayRect.top(), + m_displayRect.right() + 1, + m_displayRect.bottom() + 1); + + ::DeleteObject(pen); + ::DeleteObject(brush); + + ::EndPaint(m_winId, &paint); + } +} + +int QWmpVideoOverlay::brightness() const +{ + return 0; +} + +void QWmpVideoOverlay::setBrightness(int) +{ +} + +int QWmpVideoOverlay::contrast() const +{ + return 0; +} + +void QWmpVideoOverlay::setContrast(int) +{ +} + +int QWmpVideoOverlay::hue() const +{ + return 0; +} + +void QWmpVideoOverlay::setHue(int) +{ +} + +int QWmpVideoOverlay::saturation() const +{ + return 0; +} + +void QWmpVideoOverlay::setSaturation(int) +{ +} + +// IUnknown +HRESULT QWmpVideoOverlay::QueryInterface(REFIID riid, void **object) +{ + return m_service->QueryInterface(riid, object); +} + +ULONG QWmpVideoOverlay::AddRef() +{ + return m_service->AddRef(); +} + +ULONG QWmpVideoOverlay::Release() +{ + return m_service->Release(); +} + +// IOleWindow +HRESULT QWmpVideoOverlay::GetWindow(HWND *phwnd) +{ + if (!phwnd) { + return E_POINTER; + } else { + *phwnd = m_winId; + return S_OK; + } +} + +HRESULT QWmpVideoOverlay::ContextSensitiveHelp(BOOL fEnterMode) +{ + Q_UNUSED(fEnterMode); + + return E_NOTIMPL; +} + +// IOleInPlaceSite +HRESULT QWmpVideoOverlay::CanInPlaceActivate() +{ + return S_OK; +} + +HRESULT QWmpVideoOverlay::OnInPlaceActivate() +{ + return S_OK; +} + +HRESULT QWmpVideoOverlay::OnUIActivate() +{ + return S_OK; +} + +HRESULT QWmpVideoOverlay::GetWindowContext( + IOleInPlaceFrame **ppFrame, + IOleInPlaceUIWindow **ppDoc, + LPRECT lprcPosRect, + LPRECT lprcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo) +{ + if (!ppFrame || !ppDoc || !lprcPosRect || !lprcClipRect || !lpFrameInfo) + return E_POINTER; + + QueryInterface(IID_IOleInPlaceFrame, reinterpret_cast(ppFrame)); + QueryInterface(IID_IOleInPlaceUIWindow, reinterpret_cast(ppDoc)); + + if (m_winId) { + SetRect(lprcClipRect, + m_displayRect.left(), + m_displayRect.top(), + m_displayRect.right(), + m_displayRect.bottom()); + + if (m_aspectRatioMode == Qt::KeepAspectRatioByExpanding) { + QSize size = m_sizeHint; + size.scale( + m_displayRect.width(), + m_displayRect.height(), + Qt::KeepAspectRatioByExpanding); + + QRect positionRect(QPoint(0, 0), size); + positionRect.moveCenter(m_displayRect.center()); + + SetRect(lprcPosRect, + positionRect.left(), + positionRect.top(), + positionRect.right(), + positionRect.bottom()); + } else { + *lprcPosRect = *lprcClipRect; + } + } else { + SetRectEmpty(lprcPosRect); + SetRectEmpty(lprcClipRect); + } + + lpFrameInfo->fMDIApp = FALSE; + lpFrameInfo->haccel = 0; + lpFrameInfo->cAccelEntries = 0; + lpFrameInfo->hwndFrame = m_winId; + + return S_OK; +} + +HRESULT QWmpVideoOverlay::Scroll(SIZE scrollExtant) +{ + Q_UNUSED(scrollExtant); + + return S_FALSE; +} + +HRESULT QWmpVideoOverlay::OnUIDeactivate(BOOL fUndoable) +{ + Q_UNUSED(fUndoable); + + return S_OK; +} + +HRESULT QWmpVideoOverlay::OnInPlaceDeactivate() +{ + return S_OK; +} + +HRESULT QWmpVideoOverlay::DiscardUndoState() +{ + return S_OK; +} + +HRESULT QWmpVideoOverlay::DeactivateAndUndo() +{ + if (m_inPlaceObject) + m_inPlaceObject->UIDeactivate(); + + return S_OK; +} + +HRESULT QWmpVideoOverlay::OnPosRectChange(LPCRECT lprcPosRect) +{ + Q_UNUSED(lprcPosRect); + + return S_OK; +} + +// IOleInPlaceUIWindow +HRESULT QWmpVideoOverlay::GetBorder(LPRECT lprectBorder) +{ + Q_UNUSED(lprectBorder); + + return INPLACE_E_NOTOOLSPACE; +} + +HRESULT QWmpVideoOverlay::RequestBorderSpace(LPCBORDERWIDTHS pborderwidths) +{ + Q_UNUSED(pborderwidths); + + return INPLACE_E_NOTOOLSPACE; +} + +HRESULT QWmpVideoOverlay::SetBorderSpace(LPCBORDERWIDTHS pborderwidths) +{ + Q_UNUSED(pborderwidths); + + return OLE_E_INVALIDRECT; +} + +HRESULT QWmpVideoOverlay::SetActiveObject( + IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName) +{ + Q_UNUSED(pActiveObject); + Q_UNUSED(pszObjName); + + return S_OK; +} + +// IOleInPlaceFrame +HRESULT QWmpVideoOverlay::InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths) +{ + Q_UNUSED(hmenuShared); + Q_UNUSED(lpMenuWidths); + + return E_NOTIMPL; +} + +HRESULT QWmpVideoOverlay::SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject) +{ + Q_UNUSED(hmenuShared); + Q_UNUSED(holemenu); + Q_UNUSED(hwndActiveObject); + + return E_NOTIMPL; +} + +HRESULT QWmpVideoOverlay::RemoveMenus(HMENU hmenuShared) +{ + Q_UNUSED(hmenuShared); + + return E_NOTIMPL; +} + +HRESULT QWmpVideoOverlay::SetStatusText(LPCOLESTR pszStatusText) +{ + Q_UNUSED(pszStatusText); + + return E_NOTIMPL; +} + +HRESULT QWmpVideoOverlay::EnableModeless(BOOL fEnable) +{ + Q_UNUSED(fEnable); + + return E_NOTIMPL; +} + +HRESULT QWmpVideoOverlay::TranslateAccelerator(LPMSG lpmsg, WORD wID) +{ + return TranslateAccelerator(lpmsg, static_cast(wID)); +} diff --git a/src/plugins/wmp/qwmpvideooverlay.h b/src/plugins/wmp/qwmpvideooverlay.h new file mode 100644 index 000000000..3bc1005a7 --- /dev/null +++ b/src/plugins/wmp/qwmpvideooverlay.h @@ -0,0 +1,145 @@ +/**************************************************************************** +** +** Copyright (C) 2010 Nokia Corporation and/or its subsidiary(-ies). +** All rights reserved. +** Contact: Nokia Corporation (qt-info@nokia.com) +** +** This file is part of the Qt Mobility Components. +** +** $QT_BEGIN_LICENSE:LGPL$ +** No Commercial Usage +** This file contains pre-release code and may not be distributed. +** You may use this file in accordance with the terms and conditions +** contained in the Technology Preview License Agreement accompanying +** this package. +** +** GNU Lesser General Public License Usage +** Alternatively, this file may be used under the terms of the GNU Lesser +** General Public License version 2.1 as published by the Free Software +** Foundation and appearing in the file LICENSE.LGPL included in the +** packaging of this file. Please review the following information to +** ensure the GNU Lesser General Public License version 2.1 requirements +** will be met: http://www.gnu.org/licenses/old-licenses/lgpl-2.1.html. +** +** In addition, as a special exception, Nokia gives you certain additional +** rights. These rights are described in the Nokia Qt LGPL Exception +** version 1.1, included in the file LGPL_EXCEPTION.txt in this package. +** +** If you have questions regarding the use of this file, please contact +** Nokia at qt-info@nokia.com. +** +** +** +** +** +** +** +** +** $QT_END_LICENSE$ +** +****************************************************************************/ + +#ifndef QWMPVIDEOOVERLAY_H +#define QWMPVIDEOOVERLAY_H + +#include + +#include "qwmpplayerservice.h" + +#include + +QT_USE_NAMESPACE +class QWmpVideoOverlay + : public QVideoWindowControl + , public IOleInPlaceSite + , public IOleInPlaceFrame +{ + Q_OBJECT +public: + QWmpVideoOverlay(IWMPPlayer4 *player, IOleObject *object, QWmpPlayerService *service); + ~QWmpVideoOverlay(); + + WId winId() const; + void setWinId(WId id); + + QRect displayRect() const; + void setDisplayRect(const QRect &rect); + + bool isFullScreen() const; + void setFullScreen(bool fullScreen); + + void repaint(); + + QSize nativeSize() const; + void setNativeSize(const QSize &size); + + Qt::AspectRatioMode aspectRatioMode() const; + void setAspectRatioMode(Qt::AspectRatioMode mode); + + int brightness() const; + void setBrightness(int brightness); + + int contrast() const; + void setContrast(int contrast); + + int hue() const; + void setHue(int hue); + + int saturation() const; + void setSaturation(int saturation); + + // IUnknown + HRESULT STDMETHODCALLTYPE QueryInterface(REFIID riid, void **object); + ULONG STDMETHODCALLTYPE AddRef(); + ULONG STDMETHODCALLTYPE Release(); + + // IOleWindow + HRESULT STDMETHODCALLTYPE GetWindow(HWND *phwnd); + HRESULT STDMETHODCALLTYPE ContextSensitiveHelp(BOOL fEnterMode); + + // IOleInPlaceSite + HRESULT STDMETHODCALLTYPE CanInPlaceActivate(); + HRESULT STDMETHODCALLTYPE OnInPlaceActivate(); + HRESULT STDMETHODCALLTYPE OnUIActivate(); + HRESULT STDMETHODCALLTYPE GetWindowContext( + IOleInPlaceFrame **ppFrame, + IOleInPlaceUIWindow **ppDoc, + LPRECT lprcPosRect, + LPRECT lprcClipRect, + LPOLEINPLACEFRAMEINFO lpFrameInfo); + HRESULT STDMETHODCALLTYPE Scroll(SIZE scrollExtant); + HRESULT STDMETHODCALLTYPE OnUIDeactivate(BOOL fUndoable); + HRESULT STDMETHODCALLTYPE OnInPlaceDeactivate(); + HRESULT STDMETHODCALLTYPE DiscardUndoState(); + HRESULT STDMETHODCALLTYPE DeactivateAndUndo(); + HRESULT STDMETHODCALLTYPE OnPosRectChange(LPCRECT lprcPosRect); + + // IOleInPlaceUIWindow + HRESULT STDMETHODCALLTYPE GetBorder(LPRECT lprectBorder); + HRESULT STDMETHODCALLTYPE RequestBorderSpace(LPCBORDERWIDTHS pborderwidths); + HRESULT STDMETHODCALLTYPE SetBorderSpace(LPCBORDERWIDTHS pborderwidths); + HRESULT STDMETHODCALLTYPE SetActiveObject( + IOleInPlaceActiveObject *pActiveObject, LPCOLESTR pszObjName); + + // IOleInPlaceFrame + HRESULT STDMETHODCALLTYPE InsertMenus(HMENU hmenuShared, LPOLEMENUGROUPWIDTHS lpMenuWidths); + HRESULT STDMETHODCALLTYPE SetMenu(HMENU hmenuShared, HOLEMENU holemenu, HWND hwndActiveObject); + HRESULT STDMETHODCALLTYPE RemoveMenus(HMENU hmenuShared); + HRESULT STDMETHODCALLTYPE SetStatusText(LPCOLESTR pszStatusText); + HRESULT STDMETHODCALLTYPE EnableModeless(BOOL fEnable); + HRESULT STDMETHODCALLTYPE TranslateAccelerator(LPMSG lpmsg, WORD wID); + +private: + QWmpPlayerService *m_service; + IWMPPlayer4 *m_player; + IOleObject *m_object; + IOleInPlaceObject *m_inPlaceObject; + WId m_winId; + COLORREF m_windowColor; + Qt::AspectRatioMode m_aspectRatioMode; + QSize m_sizeHint; + QRect m_displayRect; + bool m_fullScreen; +}; + +#endif diff --git a/src/plugins/wmp/wmp.pro b/src/plugins/wmp/wmp.pro new file mode 100644 index 000000000..1b544bbeb --- /dev/null +++ b/src/plugins/wmp/wmp.pro @@ -0,0 +1,47 @@ +TEMPLATE = lib +CONFIG += plugin +TARGET = $$qtLibraryTarget(qtmedia_wmp) + +PLUGIN_TYPE=mediaservice + +INCLUDEPATH+=../../../src/multimedia +include(../../../common.pri) + +CONFIG += mobility +MOBILITY = multimedia +LIBS += -lstrmiids -lole32 -lOleaut32 -luser32 -lgdi32 + +HEADERS = \ + qmfactivate.h \ + qwmpevents.h \ + qwmpglobal.h \ + qwmpmetadata.h \ + qwmpplayercontrol.h \ + qwmpplayerservice.h \ + qwmpplaylist.h \ + qwmpplaylistcontrol.h \ + qwmpserviceprovider.h \ + qwmpvideooverlay.h + +SOURCES = \ + qmfactivate.cpp \ + qwmpevents.cpp \ + qwmpglobal.cpp \ + qwmpmetadata.cpp \ + qwmpplayercontrol.cpp \ + qwmpplayerservice.cpp \ + qwmpplaylist.cpp \ + qwmpplaylistcontrol.cpp \ + qwmpserviceprovider.cpp \ + qwmpvideooverlay.cpp + +contains(evr_enabled, yes) { + HEADERS += \ + qevrvideooverlay.h + + SOURCES += \ + qevrvideooverlay.cpp + + DEFINES += \ + QWMP_EVR +} -- cgit v1.2.3