none
Avicap32 changer la résolution d'une webcam RRS feed

  • Question

  • Bonjour la communauté,

     

    Je me permets de vous solliciter car j'ai un petit soucis dasn l'utilisation de la librairie avicap32 et je pense que vous saurais me renseigner au mieux.

     

    Voilà les faits : j'utilise depuis peu la librairie avicap32 pour manipuler une webcam. J'arrive à me connecter au driver qui gère la camera, à lui faire afficher la video, prendre des snapshots sans problème (à grand renfort de SendMessage importé de user32).

    La ou ça coince, c'est que je souhaiterais modifier la résolution de ma caméra pour profiter pleinement de ces capacités et ne pas être limité en 640*480. Pour ce faire, j'ai récupéré un code VB d'un collègue d'un autre service qui arrive à modifier cette résolution. Il utilise une structure BITMAPINFO "custom" que j'ai récupérer et traduit en c sharp :

    public class RGBQUAD
        {
          public Byte rgbBlue;
          public Byte rgbGreen;
          public Byte rgbRed;
          public Byte rgbReserved;
        }
    
        public class BITMAPINFOHEADER
        {
          public Int32 biSize;
          public Int32 biWidth;
          public Int32 biHeight;
          public Int16 biPlanes;
          public Int16 biBitCount;
          public Int32 biCompression;
          public Int32 biSizeImage;
          public Int32 biXPelsPerMeter;
          public Int32 biYPelsPerMeter;
          public Int32 biClrUsed;
          public Int32 biClrImportant;
        }
    
        [StructLayout(LayoutKind.Sequential)]
        public class BITMAPINFO
        {
          public BITMAPINFOHEADER bmpInfoHeader;
          public RGBQUAD[] rgbQuad;
        }
    

    Cette classe passe sans problème dans le message car à l'appel de la méthode, je n'ai pas de problème de "marshallage".

    Le code qui est sensé modifier la résolution est le suivant (dans la méthode SetVideoFormat) :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.Runtime.InteropServices;
    using System.Windows.Forms;
    
    namespace THALES.FacialBiometrics
    {
      public class Device
      {
        private const short WM_CAP = 0x400;
        private const int  WM_CAP_DRIVER_CONNECT = 0x40a;
        private const int  WM_CAP_DRIVER_DISCONNECT = 0x40b;
        private const int  WM_CAP_GET_FRAME = 0x43c;
        private const int  WM_CAP_EDIT_COPY = 0x41e;
        private const int  WM_CAP_SET_PREVIEW = 0x432;
        private const int  WM_CAP_SET_OVERLAY = 0x433;
        private const int  WM_CAP_SET_PREVIEWRATE = 0x434;
        private const int  WM_CAP_SET_SCALE = 0x435;
        private const int  WM_CAP_GET_VIDEOFORMAT = 0x444;
        private const int  WM_CAP_SET_VIDEOFORMAT = 0x445;
        private const int  WS_CHILD = 0x40000000;
        private const int  WS_VISIBLE = 0x10000000;
    
        [DllImport("avicap32.dll")]
        protected static extern int capCreateCaptureWindowA([MarshalAs(UnmanagedType.VBByRefStr)] ref string lpszWindowName,
          int dwStyle, int x, int y, int nWidth, int nHeight, int hWndParent, int nID);
    
        [DllImport("user32", EntryPoint = "SendMessageA")]
        protected static extern int SendMessage(int hwnd, int wMsg, int wParam, [MarshalAs(UnmanagedType.AsAny)] object lParam);
    
        [DllImport("user32")]
        protected static extern int SetWindowPos(int hwnd, int hWndInsertAfter, int x, int y, int cx, int cy, int wFlags);
    
        [DllImport("user32")]
        protected static extern bool DestroyWindow(int hwnd);
    
        int _index;
        int _deviceHandle;
        System.Windows.Forms.Control _winControl;
        bool _isShowingVideo;
        string _name;
        string _version;
    
        public Device(int index)
        {
          this._index = index;
        }
    
        public string Name
        {
          get { return _name; }
          set { _name = value; }
        }
    
        public string Version
        {
          get { return _version; }
          set { _version = value; }
        }
    
        public override string ToString()
        {
          return this.Name;
        }
    
        /// <summary>
        /// Initialize the handle which support the device
        /// </summary>
        public void InitHandle(System.Windows.Forms.Control winControl)
        {
          this._winControl = winControl;
    
          string deviceIndex = Convert.ToString(_index);
          _deviceHandle = capCreateCaptureWindowA(ref deviceIndex, WS_VISIBLE | WS_CHILD, 0, 0, winControl.Width, winControl.Height, winControl.Handle.ToInt32(), 0);
        }
    
        //public void ShowVideo()
        //{
        //  if (SendMessage(_deviceHandle, WM_CAP_DRIVER_CONNECT, this._index, 0) > 0)
        //  {
        //    SendMessage(_deviceHandle, WM_CAP_SET_SCALE, -1, 0);
        //    SendMessage(_deviceHandle, WM_CAP_SET_PREVIEWRATE, 0x42, 0);
        //    SendMessage(_deviceHandle, WM_CAP_SET_PREVIEW, -1, 0);
    
        //    SetWindowPos(_deviceHandle, 1, 0, 0, _winControl.Width, _winControl.Height, 6);
        //  }
    
        //  _isShowingVideo = true;
        //}
    
        public void ShowVideo()
        {
          if (SendMessage(_deviceHandle, WM_CAP_DRIVER_CONNECT, this._index, 0) > 0)
          {
            SendMessage(_deviceHandle, WM_CAP_SET_SCALE, 0, 0);
            SendMessage(_deviceHandle, WM_CAP_SET_PREVIEWRATE, 0x42, 0);
            SendMessage(_deviceHandle, WM_CAP_SET_PREVIEW, -1, 0);
    
            SetWindowPos(_deviceHandle, 1, 0, 0, _winControl.Width, _winControl.Height, 6);
          }
    
          _isShowingVideo = true;
        }
    
        /// <summary>
        /// Capture a frame from the video stream
        /// </summary>
        /// <param name="image">Reference to the image captured</param>
        public void CaptureFrame(ref Image image)
        {
    
          if (SendMessage(_deviceHandle, WM_CAP_DRIVER_CONNECT, this._index, 0) > 0)
          {
            // get the next frame;
            SendMessage(_deviceHandle, WM_CAP_GET_FRAME, 0, 0);
    
            // copy the frame to the clipboard
            SendMessage(_deviceHandle, WM_CAP_EDIT_COPY, 0, 0);
    
            image = (System.Drawing.Bitmap)Clipboard.GetDataObject().GetData(System.Windows.Forms.DataFormats.Bitmap);
          }
    
          if (_isShowingVideo)
          {
            ShowVideo();
          }
          else
          {
            Stop();
          }
        }
    
        /// <summary>
        /// Set the video format for the current stream
        /// </summary>
        /// <param name="bmpInfo">Informations of the desired format</param>
        /// <returns></returns>
        public bool SetVideoFormat(TestCamera.BITMAPINFOCLASS.BITMAPINFO bmpInfo)
        {
          if (SendMessage(_deviceHandle, WM_CAP_DRIVER_CONNECT, this._index, 0) > 0)
          {
            SendMessage(_deviceHandle, WM_CAP_SET_VIDEOFORMAT, 44 * sizeof(byte), bmpInfo);
    
            return true;
          }
    
          return false;
        }
    
        /// <summary>
        /// Stop the webcam.
        /// </summary>
        public void Stop()
        {
          SendMessage(_deviceHandle, WM_CAP_DRIVER_DISCONNECT, this._index, 0);
          _isShowingVideo = false;
        }
    
        /// <summary>
        /// Destroy the handle on the camera, releasing the resource and authorising other applications to use it.
        /// </summary>
        public void Release()
        { 
          DestroyWindow(_deviceHandle);
        }
    
        public TestCamera.BITMAPINFOCLASS.BITMAPINFO GetVideoFormat()
        {
          TestCamera.BITMAPINFOCLASS.BITMAPINFO bmpInfo = new TestCamera.BITMAPINFOCLASS.BITMAPINFO();
          SendMessage(_deviceHandle, WM_CAP_GET_VIDEOFORMAT, 44 * sizeof(byte), bmpInfo);
          return bmpInfo;
        }
      }
    }
    
    

    Il ne me reste plus ensuite qu'à faire appelle à la méthode dans mon interface. Cet appel est fait via un objet qui vient en surcouche pour plus de commodités dans certaines manipulations de la camera dont voici le code :

    using System;
    using System.Collections.Generic;
    using System.Linq;
    using System.Text;
    using System.Drawing;
    using System.Timers;
    
    namespace THALES.FacialBiometrics
    {
      public class frameGrabber
      {
        #region Attributs
    
        Device _capDev;
        bool _isInitialized;
        bool _timeOut;
    
        #endregion
    
        #region Propriétés
    
        public bool IsInitialized
        {
          get { return _isInitialized; }
          set { _isInitialized = value; }
        }
    
        #endregion
    
        #region Constructeurs
    
        public frameGrabber(String capDevName, System.Windows.Forms.Control winControl)
        {
          foreach (Device d in DeviceManager.GetAllDevices())
          {
            if (d.Name.ToLowerInvariant().Equals(capDevName.ToLowerInvariant()))
            {
              _capDev = d;
            }
          }
          _capDev.InitHandle(winControl);
    
          _isInitialized = true;
        }
    
        public frameGrabber(String capDevName)
          : this(capDevName, new System.Windows.Forms.PictureBox())
        { }
    
        #endregion
    
        #region Méthodes
    
        public bool Initialize(String videoSource, System.Windows.Forms.Control winControl)
        {
          _isInitialized = false;
    
          if (GetRessource(videoSource))
          {
            _capDev.InitHandle(winControl);
            _isInitialized = true;
          }
    
          return _isInitialized;
        }
    
        public bool GetRessource(String s)
        {
          foreach (Device d in DeviceManager.GetAllDevices())
          {
            if (d.Name.ToUpper().Equals(s.ToUpper()))
            {
              _capDev = d;
              return true;
            }
          }
    
          return false;
        }
    
        public void ShowVideo()
        {
          _capDev.ShowVideo();
        }
    
        public bool Stop()
        {
          if (_isInitialized)
            _capDev.Stop();
    
          return _isInitialized;
        }
    
        public Image CaptureFrame()
        {
          Image image = null;
    
          _capDev.CaptureFrame(ref image);
    
          return image;
        }
    
        public List<Image> CaptureMultipleFrame(uint maxNumImages, uint timeOut)
        {
          List<Image> imagesCollection = new List<Image>();
          uint numImagesAcquired = 0;
          Timer timer = new Timer();
          timer.Elapsed += new ElapsedEventHandler(timer_Elapsed);
          timer.Interval = timeOut;
          _timeOut = false;
          ShowVideo();
    
          timer.Start();
    
          while (numImagesAcquired < maxNumImages && !_timeOut)
          {
            imagesCollection.Add(CaptureFrame());
            numImagesAcquired++;
          }
    
          timer.Stop();
          timer.Close();
    
          Stop();
    
          return imagesCollection;
        }
    
        public bool SetVideoFormat(TestCamera.BITMAPINFOCLASS.BITMAPINFO bmpInfo)
        {
          return _capDev.SetVideoFormat(bmpInfo);
        }
    
        public TestCamera.BITMAPINFOCLASS.BITMAPINFO GetVideoFormat()
        {
          return _capDev.GetVideoFormat();
        }
    
        void timer_Elapsed(object sender, ElapsedEventArgs e)
        {
          _timeOut = true;
        }
    
        public void Release()
        {
          if (_isInitialized)
          {
            _capDev.Stop();
            _capDev.Release();
            _isInitialized = false;
          }
        }
    
        #endregion
      }
    }
    
    

    Enfin dans mon interface graphique, voici le code qui me permet de faire appel à la méthode en question :

    BITMAPINFOCLASS.BITMAPINFOHEADER bmpInfoHeader = new BITMAPINFOCLASS.BITMAPINFOHEADER();
          bmpInfoHeader.biBitCount = 24;
          bmpInfoHeader.biCompression = 0;
          bmpInfoHeader.biPlanes = 1;
          bmpInfoHeader.biSize = 40;
          bmpInfoHeader.biHeight = int.Parse(textBox2.Text);
          bmpInfoHeader.biWidth = int.Parse(textBox1.Text);
          BITMAPINFOCLASS.BITMAPINFO bmpInfo = new BITMAPINFOCLASS.BITMAPINFO();
          bmpInfo.bmpInfoHeader = bmpInfoHeader;
          
          if (fg.SetVideoFormat(bmpInfo))
            MessageBox.Show("Resolution set");
    

    Je ne vois pas ou se situe l'erreur mais lorsque j'appelle la méthode, tout ce passe sans accrocs mais la résolution de la caméra ne change pas et je reste en 640*480.

    Je suis preneur de toutes pistes qui pourrais m'aider et je tiens à remercier d'ores et déjà toutes la communautés msdn pour les précieux conseils qu'elle m'a dispensé sans le savoir :).

     

    Cordialement,

     

    Christophe Sevin

    vendredi 30 juillet 2010 14:43

Réponses