AtmoLight 2.1.0.0 for MediaPortal2 [2015-01-21] (1 Viewer)

Rick164

MP Donator
  • Premium Supporter
  • January 7, 2006
    1,335
    1,006
    Home Country
    Netherlands Netherlands
    Overview

    AtmoLight is a process plugin that interfaces with different ambilight solutions and brings Ambilight to MediaPortal.
    The main function is to send the needed data directly to your ambilight solution to reduce the CPU load drastically, making Ambilight and smooth playback possible.
    This is the MediaPortal2 Version of AtmoLight. You can find the version for MediaPortal here.

    Installation

    Extract the rar to C:\Program Files (x86)\Team MediaPortal\MP2-Client\Plugins


    If you have been using a test version older than Beta 1, please manually delete the AtmoLight.Settings.xml before using Beta 1 or newer. The xml is located in C:\ProgramData\Team MediaPortal\MP2-Client\Config\{USER_NAME}\.

    Beta 2 is only compatible to MP2 10th AE Update 1 or newer and all AtmoLight 2.0 version before that are only compatible to MP2 10th AE or older.

    2.0.0.0 Final is only compatible to MP2 Weekly 2014-11-15 or newer.


    Useful Links

    Changelog

    Code:
    Version 2.1.0.0
    - Fixed bug where data would be send to target handler even if handler is disconnected from target
    - Improved average color calculation for Hue handler
    - Improved VUMeter and VUMeter Rainbow effects
    - Fixed bug where GIFReader was case sensitive for the file extensions
    - Fixed a bug where connecting to AtmoWin would fail
    - Fixed bug where some effects would not be set properly on MediaPortal2 start up
    - Added possibility to use hostnames/multicast dns with Hyperion
    - Added AmbiBox handler
    
    Version 2.0.0.0
    - Added interface to support other targets (not just AtmoWin)
    - Added Hyperion as target software
    - Added Hue as target (AtmoHue needed)
    - Added Boblight as target software
    - Added possibility to use more then one target software (e.g. AtmoWin and Hyperion at the same time)
    - Added "MediaPortal exit" effect (effect that gets set when MediaPortal gets closed)
    - Added possibility to change every setting on the fly (no MediaPortal2 restart for specific settings required anymore)
    - Added VU Meter and VU Meter Rainbow effects
    - Added blackbar detection and removal
    - Fixed bug where AtmoLight would do nothing when a player ends
    - Added support for Picture-in-Picture mode
    - Various small bug fixes
    
    Version 2.0.0.0 Beta 2
    - Fixed bug where MediaPortal could crash to Desktop
    - Fixed issue where AtmoLight would always disable LEDs after reconnect
    - Fixed bug with deactivate between time
    - Added GIF Reader effect (adds support for custom effects made by users)
    
    Version 2.0.0.0 Beta 1
    - Added delay feature
    - Added AtmoLight configuration within MP2
    - Added notification if connection to AtmoWin is lost
    - Added possibility to reconnect to AtmoWin using the remote buttons
    
    Version 2.0.0.0 Alpha 5
    - Fixed bug where AtmoWin would not be stopped on MP2 exit
    - Added color button support
        * Default: Red=ContextMenu (not implemented yet), Green=Toggle LEDs, Yellow=Change AtmoWin Profile
    - Changed some types in the settings. Please delete current settings file before using this version!
    
    Version 2.0.0.0 Alpha 4
    - Changed UICapture to use player surface on videoplayback (faster and no blackbar problems)
    - Added settings
    - Added automatic mode
        * Video effect, Music effect and Menu effect
        * Timeframe where leds should be off (exclude time)
        * Manual Mode (currently no way to change effects manually)
    - Added Low CPU feature
    
    Version 2.0.0.0 Alpha 3
    - Fixed memory leak
    - Added possibility to use MediaPortal liveview mode for the whole UI
    
    Version 2.0.0.0 Alpha 2
    - Added MediaPortal liveview mode
    
    Version 2.0.0.0 Alpha 1
    - Initial Release


    Active developers

    @Rick164

    Thanks

    We would like to thank everybody who has worked on and contributed to AtmoLight in the past.
    @gemx, @angie05, @legnod, @azzuro, @BassFan, @HomeY, @Sebastiii, @Lightning303

    If i forgot someone please let me know.


    Original Post:
    Hey,
    im currently trying to make a AtmoLight plugin for MP2. At the moment its just a skeleton and im trying to get all the tools ready i need.
    I already figgured out how i can set up an event handler for PlayerStarted aswell as PlayerStopped.

    Code:
          void SubscribeToMessages()
          {
            messageQueue = new AsynchronousMessageQueue(this, new string[] { PlayerManagerMessaging.CHANNEL });
            messageQueue.MessageReceived += OnMessageReceived;
            messageQueue.Start();
          }
    
          void OnMessageReceived(AsynchronousMessageQueue queue, SystemMessage message)
          {
            if (message.ChannelName == PlayerManagerMessaging.CHANNEL)
            {
              PlayerManagerMessaging.MessageType messageType = (PlayerManagerMessaging.MessageType)message.MessageType;
              if (messageType == PlayerManagerMessaging.MessageType.PlayerStarted)
              {
                Log.Info("AtmoLight: Playback started.");
              }
              else if (messageType == PlayerManagerMessaging.MessageType.PlayerStopped)
              {
                Log.Info("AtmoLight: Playback stopped.");
              }
            }
          }

    Please let me know if there is a better way to do that.

    What i dont know at the moment is how i can figure out what kind of media is playing. In MP1 i could use something like g_Player.IsVideo, or i would get the mediatype as parameter in the PlaybackStarted eventhandler.

    Also, what i dont know yet is how to get the video frames (surfaces) to work with them and send them to AtmoWin. This would be the counter part to mp1's framegrabber. @morpheus_xx said here that this is possible, but i just dont know how :(.

    I would highly appreciate any help. And please be patient with me, i consider myself a beginner :).
     
    Last edited:

    BigGranu

    Retired Team Member
  • Premium Supporter
  • February 7, 2013
    240
    202
    53
    Home Country
    Germany Germany
    What i dont know at the moment is how i can figure out what kind of media is playing.
    e.g
    Code:
    var isVideo = ServiceRegistration.Get<IPlayerContextManager>().IsVideoContextActive
    var isAudio = ServiceRegistration.Get<IPlayerContextManager>().IsAudioContextActive

    About the Videoframes :oops:. I know nothing about it.
     

    morpheus_xx

    Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    You can access active players via IPlayerManager service. Try to cast the IPlayer to ISlimDXPlayer, it has a Surface property. Not sure what you need for processing, but it would be a start.
    I'm on smartphone now and can't link to sources...
     

    Lightning303

    MP Donator
  • Premium Supporter
  • September 12, 2009
    798
    577
    Home Country
    Germany Germany
    You can access active players via IPlayerManager service. Try to cast the IPlayer to ISlimDXPlayer, it has a Surface property. Not sure what you need for processing, but it would be a start.
    I'm on smartphone now and can't link to sources...

    To be honest, you lost me there already :(. But no hurry, maybe when you are on you pc and have a spare 5 minutes you could look at this. I will try to explain what we are using right now for MP1. However, i did not build this, i just inharited it, especially the framegrabber part.

    In MP1 we use an event handler, like this:
    Code:
        FrameGrabber.GetInstance().OnNewFrame += new FrameGrabber.NewFrameHandler(AtmolightPlugin_OnNewFrame);
    
        private void AtmolightPlugin_OnNewFrame(short width, short height, short arWidth, short arHeight, uint pSurface)
        {
          if (playbackEffect != ContentEffect.MediaPortalLiveMode || atmoOff || atmoCtrl == null || width == 0 || height == 0 || reInitializeLock)
          {
            return;
          }
    
          if (rgbSurface == null)
          {
            rgbSurface = GUIGraphicsContext.DX9Device.CreateRenderTarget(captureWidth, captureHeight, Format.A8R8G8B8,
              MultiSampleType.None, 0, true);
          }
          unsafe
          {
            try
            {
              if (AtmolightSettings.sbs3dOn)
              {
                VideoSurfaceToRGBSurfaceExt(new IntPtr(pSurface), width / 2, height, (IntPtr)rgbSurface.UnmanagedComPointer,
                  captureWidth, captureHeight);
              }
              else
              {
                VideoSurfaceToRGBSurfaceExt(new IntPtr(pSurface), width, height, (IntPtr)rgbSurface.UnmanagedComPointer,
                  captureWidth, captureHeight);
              }
    
              Microsoft.DirectX.GraphicsStream stream = SurfaceLoader.SaveToStream(ImageFileFormat.Bmp, rgbSurface);
    
              BinaryReader reader = new BinaryReader(stream);
              stream.Position = 0; // ensure that what start at the beginning of the stream.
              reader.ReadBytes(14); // skip bitmap file info header
              byte[] bmiInfoHeader = reader.ReadBytes(4 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 4);
    
              int rgbL = (int)(stream.Length - stream.Position);
              int rgb = (int)(rgbL / (captureWidth * captureHeight));
    
              byte[] pixelData = reader.ReadBytes((int)(stream.Length - stream.Position));
    
              byte[] h1pixelData = new byte[captureWidth * rgb];
              byte[] h2pixelData = new byte[captureWidth * rgb];
              //now flip horizontally, we do it always to prevent microstudder
              int i;
              for (i = 0; i < ((captureHeight / 2) - 1); i++)
              {
                Array.Copy(pixelData, i * captureWidth * rgb, h1pixelData, 0, captureWidth * rgb);
                Array.Copy(pixelData, (captureHeight - i - 1) * captureWidth * rgb, h2pixelData, 0, captureWidth * rgb);
                Array.Copy(h1pixelData, 0, pixelData, (captureHeight - i - 1) * captureWidth * rgb, captureWidth * rgb);
                Array.Copy(h2pixelData, 0, pixelData, i * captureWidth * rgb, captureWidth * rgb);
              }
              //send scaled and fliped frame to atmowin
              if (!AtmolightSettings.lowCPU ||
                  (((Win32API.GetTickCount() - lastFrame) > AtmolightSettings.lowCPUTime) && AtmolightSettings.lowCPU))
              {
                if (AtmolightSettings.lowCPU)
                {
                  lastFrame = Win32API.GetTickCount();
                }
                  atmoLiveViewCtrl.setPixelData(bmiInfoHeader, pixelData);
              }
              stream.Close();
              stream.Dispose();
            }
            catch (Exception ex)
            {
              Log.Error("AtmoLight: Error in AtmolightPlugin_OnNewFrame.");
              Log.Error("AtmoLight: Exception: {0}", ex.Message);
    
              rgbSurface.Dispose();
              rgbSurface = null;
            }
          }
        }

    That hooks into https://github.com/MediaPortal/MediaPortal-1/blob/master/mediaportal/Core/Player/FrameGrabber.cs

    I would need something similar in MP2.

    Thanks for your help :)
     

    morpheus_xx

    Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    If I read your code correctly, you render the surface into a bitmap. So I expect the atmolight logic is using this as input? Or would it be able to use the Surface directly?
     

    Lightning303

    MP Donator
  • Premium Supporter
  • September 12, 2009
    798
    577
    Home Country
    Germany Germany
    Yes, this is the input for AtmoLight, on every frame the event is called, the Surface converted into a bitmap, shrunken in size and then sent to AtmoWin. Afaik Atmowin only excepts bitmap.
     

    Lightning303

    MP Donator
  • Premium Supporter
  • September 12, 2009
    798
    577
    Home Country
    Germany Germany
    OK, after a lot of trial and error i came up with this.

    Code:
        private SharpDX.Direct3D9.Surface rgbSurface;
        private void MyThread()
        {
          while (ServiceRegistration.Get<IPlayerContextManager>().IsVideoContextActive)
          {
            try
            {
              rgbSurface = ServiceRegistration.Get<ISharpDXVideoPlayer>().Surface;
              Rectangle rect = new Rectangle(0, 0, rgbSurface.Description.Width, rgbSurface.Description.Height);
    
              DataStream stream = SharpDX.Direct3D9.Surface.ToStream(rgbSurface, SharpDX.Direct3D9.ImageFileFormat.Bmp, rect);
    
              BinaryReader reader = new BinaryReader(stream);
              stream.Position = 0; // ensure that what start at the beginning of the stream.
              reader.ReadBytes(14); // skip bitmap file info header
              byte[] bmiInfoHeader = reader.ReadBytes(4 + 4 + 4 + 2 + 2 + 4 + 4 + 4 + 4 + 4 + 4);
    
              int rgbL = (int)(stream.Length - stream.Position);
              int rgb = (int)(rgbL / (AtmoLightObject.captureWidth * AtmoLightObject.captureHeight));
    
              byte[] pixelData = reader.ReadBytes((int)(stream.Length - stream.Position));
    
              byte[] h1pixelData = new byte[AtmoLightObject.captureWidth * rgb];
              byte[] h2pixelData = new byte[AtmoLightObject.captureWidth * rgb];
              //now flip horizontally, we do it always to prevent microstudder
              int i;
              for (i = 0; i < ((AtmoLightObject.captureHeight / 2) - 1); i++)
              {
                Array.Copy(pixelData, i * AtmoLightObject.captureWidth * rgb, h1pixelData, 0, AtmoLightObject.captureWidth * rgb);
                Array.Copy(pixelData, (AtmoLightObject.captureHeight - i - 1) * AtmoLightObject.captureWidth * rgb, h2pixelData, 0, AtmoLightObject.captureWidth * rgb);
                Array.Copy(h1pixelData, 0, pixelData, (AtmoLightObject.captureHeight - i - 1) * AtmoLightObject.captureWidth * rgb, AtmoLightObject.captureWidth * rgb);
                Array.Copy(h2pixelData, 0, pixelData, i * AtmoLightObject.captureWidth * rgb, AtmoLightObject.captureWidth * rgb);
              }
    
              AtmoLightObject.SetPixelData(bmiInfoHeader, pixelData);
    
              stream.Close();
              stream.Dispose();
    
            }
            catch (Exception ex)
            {
              Log.Error("ex: {0}", ex.Message);
            }
          }
          System.Threading.Thread.Sleep(10);
        }

    Lots of red squgily lines in vs for a long time, because directx surfaces are not the same as sharpdx surfaces blabla..., after trying to convert one to another for some time i found the ToStream method in sharpdx, which looked to be my savior. However my error seems to be even before that. Running this i get this exception:

    Code:
    [2014-04-23 03:08:20,811] [21474  ] [112      ] [ERROR] - AtmoLight: ex: Could not find the MediaPortal.UI.SkinEngine.Players.ISharpDXVideoPlayer service

    its late, im tiered, if i make no sense or did obvious mistakes, please forgive me. Wanted to let this here as its still fresh and i still know what i actually did... ;).
    Maybe someone has an idea ;).
     

    morpheus_xx

    Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    In principle you are on the right way, but you cannot access the player itself via ServiceRegistration, because MP2 handles two or even more players concurrently. You need to use the IPlayerManger service to get the active instances. Look into the StatsRenderer plugin, it also enumerates players.
     

    morpheus_xx

    Retired Team Member
  • Team MediaPortal
  • March 24, 2007
    12,073
    7,459
    Home Country
    Germany Germany
    I think the StatsRenderer approach might even work better: you could get the full screen once it is rendered from backbuffer. Then it doesnt matter if one or more videos are playing, and also if fullscreen, window or PiP is used. Check the plugin code, it directly hooks into render events.
     

    Users who are viewing this thread

    Top Bottom