Autor Beitrag
Kasko
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 101

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Mi 19.06.19 01:21 
Servus,

Erstmal der Code, dann die Fragen:

ausblenden volle Höhe C#-Quelltext
1:
2:
3:
4:
5:
6:
7:
8:
9:
10:
11:
12:
13:
14:
15:
16:
17:
18:
19:
20:
21:
22:
23:
24:
25:
26:
27:
28:
29:
30:
31:
32:
33:
34:
35:
36:
37:
38:
39:
40:
41:
42:
43:
44:
45:
46:
47:
48:
49:
50:
51:
52:
53:
54:
55:
56:
57:
58:
59:
60:
61:
62:
63:
64:
65:
66:
67:
68:
69:
70:
71:
72:
73:
74:
75:
76:
77:
78:
79:
80:
81:
82:
83:
using System;
using System.Drawing;
using System.IO;
using System.Threading;
using System.Threading.Tasks;

using AForge.Video.FFMPEG;

namespace VideoPlayer {
  public class Media {
    private object _sync = new object();

    private CancellationTokenSource cts = null;
    private CancellationToken ct;
    private Task task = null;

    private Bitmap _thumbnail;
    private MediaType _type;
    private string _uri;

    public static int VideoThumbnailIndex = 15;

    public event EventHandler<BitmapEventArgs> OnThumbnailChanged = null;

    public Bitmap Thumbnail {
      get {
        lock (_sync)
          return _thumbnail;
      }
      set {
        lock (_sync) {
          _thumbnail = value;
          OnThumbnailChanged?.Invoke(thisnew BitmapEventArgs(_thumbnail));
        }
      }
    }

    public MediaType Type { get; }

    public string Uri {
      get => _uri;
      set {
        SetMediaType(value); 
        _uri = value;

        if (_type == MediaType.Video)
          StartReadingThumbnail();
      }
    }

    private void SetMediaType(string value) {
      if (!System.Uri.IsWellFormedUriString(value, UriKind.RelativeOrAbsolute))
        throw new ArgumentException("Not well formated uri");

      if (-1 != Array.IndexOf(new string[] { ".WAV"".MID"".MIDI"".WMA"".MP3"".OGG"".RMA" }, Path.GetExtension(value).ToUpper()))
        _type = MediaType.Audio;
      else if (-1 != Array.IndexOf(new string[] { ".AVI"".MP4"".DIVX"".WMV" }, Path.GetExtension(value).ToUpper()))
        _type = MediaType.Video;
      else
        throw new NotSupportedException("Not supported file extension");
    }

    private void StartReadingThumbnail() {
      if (cts != null) {
        cts.Cancel();
        task.Wait();
      }

      cts = new CancellationTokenSource();
      ct = cts.Token;

      task = Task.Factory.StartNew(() => {
        VideoFileReader reader = new VideoFileReader();
        reader.Open(_uri);

        for (int i = 1; i < VideoThumbnailIndex && !ct.IsCancellationRequested; i++)
          reader.ReadVideoFrame().Dispose();

        Thumbnail = reader.ReadVideoFrame();
      }, ct);
    }
  }
}


So zu meinen Fragen:

1. Ist der Zugriff auf die Thumbnail-Property sicher vor Multithreading-Exception?
2. Wird der EventHandler im Thread aufgerufen, in dem das Object der Klasse Media erstellt wurde oder im Task, der in der Methode StartReadingThumbnail erstellt wird?
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4279
Erhaltene Danke: 917

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Mi 19.06.19 08:44 
zu 1: Entsprechend Inconsistently synchronized property hast du korrekt lock sowohl beim Setter als auch beim Getter eingesetzt.
Bedenke aber, daß dadurch nicht der Zugriff auf Sub-Eigenschaften des Objekts (in deinem Fall Bitmap) gelockt ist, s.a. C# thread safety with get/set.

zu 2: Events sind normale Methodenaufrufe und werden immer im aktuellen Thread aufgerufen, d.h. in der StartReadingThumbnail-Methode also im Thread der neu erzeugten Task.
Kasko Threadstarter
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 101

Win 10
C# C++ (VS 2017/19), (Java, PHP)
BeitragVerfasst: Mi 19.06.19 12:23 
Okay dann zwei weitere Fragen:

1. Wie kann ich zusätzlich den Zugriff auf die Properties der Bitmap regeln?
2. Wie kann ich den EventHandler im "Erstellungsthread" des Objektes ausführen?
Th69
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Moderator
Beiträge: 4279
Erhaltene Danke: 917

Win10
C#, C++ (VS 2015/17)
BeitragVerfasst: Mi 19.06.19 13:02 
Auch das mußt du dann synchronisieren (lock).

Es wird nicht gespeichert, von welchem Thread aus ein Objekt erzeugt wurde, d.h. auch das mußt du selber regeln.
Wenn es sich um den UI-Thread handelt, so kannst du Control.Invoke() (bzw. für WPF Dispatcher.Invoke()) dafür benutzen (Die Basisklasse dafür ist SynchronizationContext, s.a. Paralleles Computing: Hier geht es allein um SynchronizationContext).
Dem aktuellen Thread kann mittels SynchronizationContext.SetSynchronizationContext ein SynchronizationContext zugewiesen werden.


Zuletzt bearbeitet von Th69 am Mi 19.06.19 13:08, insgesamt 2-mal bearbeitet
Chiyoko
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starontopic star
Beiträge: 298
Erhaltene Danke: 8

Win 98, Win Xp, Win 10
C# (VS 2017)
BeitragVerfasst: Mi 19.06.19 13:05 
Zu 1: (Eventuell) mit volatile als Schlüsselwort. Bin aber nicht 100% sicher, ob sich das auf die gesamte Klasse auswirkt.
Zu 2: z.b. mit SynchronizationContext
Ralf Jansen
ontopic starontopic starontopic starontopic starontopic starontopic starontopic starhalf ontopic star
Beiträge: 4466
Erhaltene Danke: 919


VS2010 Pro, VS2012 Pro, VS2013 Pro, VS2015 Pro, Delphi 7 Pro
BeitragVerfasst: Mi 19.06.19 13:21 
Du hast eine Referenz auf dein Bitmap rausgegeben. Über das was ab jetzt über die Referenz passiert hast du (bzw. deine VideoPlayer Klasse) keinerlei Kontrolle mehr.
Die Lösung dazu hat @TH69 im ersten Beitrag gepostet. Kurzfassung gib eine Copy des Bitmaps raus. Diese Copy kann man dann beliebig ändern und über den Setter wieder zuweisen.

Das original Bitmap solltest du auch nicht über den OnThumbnailChanged Event rausgeben. Aus dem gleichen Grund.