2019년 11월 14일 목요일

avformat_open_input rtsp connection timeout 주기

ffmpeg demuxer 를 사용해서 rtsp client 를 구현할때 접속이 되지않으면 avformat_open_input 에서 무한 블러킹이 걸린다. 이때 아래와 같이 타임아웃을 주면 avformat_open_input 을 빠져나올수 있다.


AVDictionary* dicts = NULL;

av_dict_set(&dicts, "stimeout", "2000000", 0);

int err = avformat_open_input(&m_pFormatCtx, filepath, NULL, &dicts);

2019년 7월 10일 수요일

c# 시스템 메모리 사용량 구하기

전체 메모리 사용량 구하는 법을 검색해봤는데 의외로 쓸만한 소스가 없었다.
겨우 찾아낸 이 방법이 제일 나은것 같다.

출처 - https://ash84.net/2012/03/03/c-wmi-eb-a5-bc--ec-9d-b4-ec-9a-a9-ed-95-9c--ed-98-84-ec-9e-ac--eb-a9-94-eb-aa-a8-eb-a6-ac--ec-82-ac-ec-9a-a9-eb-9f-89--ea-b5-ac-ed-95-98-ea-b8-b0/

프로세스 메모리 사용량이 아닌 작업관리자에 나오는 시스템 메모리 사용량 체크 소스이다.

     private uint GetTotalUsedMemory()  
     {  
       ManagementClass cls = new ManagementClass("Win32_OperatingSystem");  
       ManagementObjectCollection instances = cls.GetInstances();  
   
       foreach (ManagementObject info in instances)  
       {  
         int total_physical_memeory = int.Parse(info["TotalVisibleMemorySize"].ToString());  
         int free_physical_memeory = int.Parse(info["FreePhysicalMemory"].ToString());  
         int remain_physical_memory = total_physical_memeory - free_physical_memeory;  
   
         Console.WriteLine("Memory Information ================================");  
         Console.WriteLine("Total Physical Memory :{0:#,###} KB", info["TotalVisibleMemorySize"]);  
         Console.WriteLine("Free Physical Memory :{0:#,###} MB", info["FreePhysicalMemory"]);  
   
         Console.WriteLine("Memory Usage Percent = {0} %", 100 * remain_physical_memory / total_physical_memeory);  
         Console.WriteLine("Remain Physical Memory : {0:#,###}", remain_physical_memory);  
   
         return (uint)(remain_physical_memory / 1000);  
       }  
       return 0;  
   


2019년 7월 4일 목요일

c# unix timestamp local DateTime 으로 변환

int timestamp;    // unix timestamp
DateTime dateTimeTimestamp;
...
dateTimeTimestamp = new DateTime(1970, 1, 1).AddSeconds(timestamp);
dateTimeTimestamp += TimeZone.CurrentTimeZone.GetUtcOffset(dateTimeTimestamp);

2019년 7월 2일 화요일

c# simple thread-safe Log Writer

싱글턴 패턴으로된 로그파일을 생성해주는 클래스이다.
로그파일은 날짜별로 생성되고 각 로그 시간을 함께 찍어준다.
프로그램 시작시 StartDeleteLog / 종료시 StopDeleteLog 한다.
로그파일은 디폴트로 30일이 넘으면 삭제된다.


1:  using System;  
2:  using System.Collections.Generic;  
3:  using System.IO;  
4:  using System.Linq;  
5:  using System.Text;  
6:  using System.Threading;  
7:  using System.Threading.Tasks;  
8:    
9:  namespace MyLog  
10:  {  
11:    public enum LogType  
12:    {  
13:      Info,  
14:      Exception,  
15:      Error  
16:    }  
17:    
18:    public class LogWriter  
19:    {  
20:      public delegate void LogWriteHandlerFunc(LogType logType, DateTime logTime, string logMessage);  
21:      public LogWriteHandlerFunc LogWriteHandler;  
22:    
23:      private static LogWriter instance = new LogWriter();  
24:      public static LogWriter Instance { get { return instance; } }  
25:    
26:      private object objLogLock = new object();  
27:    
28:      private const string LOG_PATH = @"C:\Log\";  
29:      private const string LOG_FILENAME = @"log";  
30:    
31:      private Thread threadDeleteLog;  
32:      private bool bThreadDeleteLogRun = false;  
33:      private int logDeleteDays = 30;  
34:    
35:      public void StartDeleteLog(int days)  
36:      {  
37:        if (!bThreadDeleteLogRun)  
38:        {  
39:          logDeleteDays = days;  
40:          bThreadDeleteLogRun = true;  
41:          threadDeleteLog = new Thread(new ThreadStart(ThreadDeleteLog));  
42:          threadDeleteLog.IsBackground = true;  
43:          threadDeleteLog.Start();  
44:        }  
45:      }  
46:    
47:      public void StopDeleteLog()  
48:      {  
49:        if (threadDeleteLog != null)  
50:        {  
51:          bThreadDeleteLogRun = false;  
52:          threadDeleteLog.Join();  
53:        }  
54:      }  
55:    
56:      private void ThreadDeleteLog()  
57:      {  
58:        CheckLogPath(LOG_PATH);  
59:    
60:        DateTime lastTime = DateTime.MinValue;  
61:    
62:        while (bThreadDeleteLogRun)  
63:        {  
64:          DateTime now = DateTime.Now;  
65:          double diff = (now - lastTime).TotalHours;  
66:          if (diff < 0) diff = 0;  
67:    
68:          if (diff > 24)  
69:          {  
70:            try  
71:            {  
72:              string[] filepaths = Directory.GetFiles(LOG_PATH, "*.txt");  
73:              DeleteLogFiles(now, filepaths);  
74:            }  
75:            catch (Exception ex)  
76:            {  
77:              WriteLog(LogType.Exception, ex.ToString());  
78:            }  
79:    
80:            lastTime = now;  
81:          }  
82:    
83:          Thread.Sleep(100);  
84:        }  
85:      }  
86:    
87:      private void DeleteLogFiles(DateTime now, string[] filepaths)  
88:      {  
89:        foreach (string filepath in filepaths)  
90:        {  
91:          try  
92:          {  
93:            DateTime date = File.GetCreationTime(filepath);  
94:            if ((now - date).TotalDays > logDeleteDays)  
95:            {  
96:              File.Delete(filepath);  
97:            }  
98:          }  
99:          catch (Exception ex)  
100:          {  
101:            WriteLog(LogType.Exception, ex.ToString());  
102:          }  
103:        }  
104:      }  
105:    
106:      public void WriteLog(LogType logType, string log)  
107:      {  
108:        try  
109:        {  
110:          lock (objLogLock)  
111:          {  
112:            Console.WriteLine(log);  
113:    
114:            string filepath = LOG_PATH;  
115:    
116:            CheckLogPath(filepath);  
117:    
118:            DateTime dateTime = DateTime.Now;  
119:            string strDate = dateTime.ToString("yyyyMMdd");  
120:            string strDateTime = dateTime.ToString("yyyy-MM-dd HH:mm:ss.fff");  
121:    
122:            filepath = string.Format("{0}{1}_{2}.txt", filepath, LOG_FILENAME, strDate);  
123:            string logMessage = string.Format("{0} [{1}] {2}", strDateTime, logType, log);  
124:    
125:            using (StreamWriter sw = File.AppendText(filepath))  
126:            {  
127:              sw.WriteLine(logMessage);  
128:              sw.WriteLine("---------------------------------------------------------------------------------------\r");  
129:              sw.Flush();  
130:            }  
131:    
132:            if (LogWriteHandler != null) LogWriteHandler(logType, dateTime, log);  
133:          }  
134:        }  
135:        catch (Exception ex)  
136:        {  
137:          Console.WriteLine(ex.ToString());  
138:        }  
139:      }  
140:    
141:      private void CheckLogPath(string path)  
142:      {  
143:        try  
144:        {  
145:          if (!Directory.Exists(path))  
146:            Directory.CreateDirectory(path);  
147:        }  
148:        catch (Exception ex)  
149:        {  
150:          Console.WriteLine(ex.ToString());  
151:        }  
152:      }  
153:    }  
154:  }  
155:    


private LogWriter logWriter = LogWriter.Instance;
...
logWriter.StartDeleteLog(30);
...
logWriter.WriteLog(LogType.Info, "로그 시작...");
...
logWriter.StopDeleteLog();



windeployqt 사용시 주의할 점

qt 배포용 파일들 담아주는 windeployqt 사용시 반드시 32/64 비트 확인할 것.
32비트 빌드는 32비트용 windeployqt 사용 => 32비트 qt dll 들 가져옴
64비트 빌드는 64비트용 windeployqt 사용 => 64비트 qt dll 들 가져옴
(QT 5.XX 32bit for Desktop / QT 5.XX 64bit for Desktop)

2019년 6월 3일 월요일

C# BeginInvoke/Invoke 동작 원리

c#으로 개발한 UI 기반 프로젝트에서 BeginInvoke/Invoke 에 대해 알아보자.

간단한 동작원리이지만 개발자들이 의외로 잘 모르고 사용하는 경우가 많다.
먼저 아래 링크에 아주 잘 설명되어 있다.

https://www.codeproject.com/Articles/10311/What-s-up-with-BeginInvoke

위 링크에서 하는 설명의 결론을 말하자면 컨트롤에서 BeginInvoke/Invoke 를 호출하는 순간 UI 메시지큐에 사용자 정의 메시지가 push 되고 해당 메시지에 대해 실행되는 메시지 핸들러 함수가 등록되는 원리이다.

외부 쓰레드에서 UI 쓰레드에서 실행되는 delegate 를 등록하여 비동기 실행을 하도록 한다는 것이다.
아래 폼을 보자.




폼 위에 여러가지 UI 요소들이 있다. 여기에 UI 쓰레드가 몇 개가 돌까?
정답은 1개 이다. 특별히 쓰레드를 생성해서 폼을 생성하지않는한 UI 쓰레드는 언제나 한 개이다.
이 말은 win32 API 로 프로그램을 개발했다고 가정했을때 메시지 큐를 처리하는 메시지 루프가 한 개 돌고있다는 말이다.

BeginInvoke를 호출할때 굳이 btnOpen.BeginInvoke 니 textStatus.BeginInvoke 등 처럼 UI 컨트롤 변수를 지정해줄 필요가 없으며 의미도 없다는 말이된다. (WPF 의 Dispatcher 도 마찬가지이다)
그냥 this.BeginInvoke 혹은 BeginInvoke 로 호출하면된다.

Invoke 의 경우 쓰레드 동기화를 위해 이를 호출한 외부 쓰레드에서 delegate 타입의 이벤트 핸들러 함수가 끝날때까지  Sleep 상태로 기다렸다가 함수 실행이 끝나면 다음 코드가 진행되는 방식이다.

Invoke 함수의 경우 폼이 종료된 후 호출되면 UI  쓰레드가 종료된 이후이기 때문에 데드락에 빠지는 경우가 생긴다. 따라서 분명한 이유가 없으면 Invoke보다 BeginInvoke 사용을 권장한다.













2019년 5월 20일 월요일

C# 프로젝트에 ocx 수동 삽입하기 - Inserting ocx into c# project manually

c# 프로젝트(윈폼/WPF)에 ocx 파일을 삽입할때 보통 UI 에서 추가하는데 이렇게 하면 참조에 아래 2개의 dll 이 자동 추가된다.

ocx 파일명 : DXMediaPlayer.ocx
참조 : AxInterop.DXMediaPlayerLib, Interop.DXMediaPlayerLib

위 2개의 참조경로를 따라면 보통 obj\Release(Debug) 아래 경로에 두 dll을 참조하고 실행경로에 복사되는데 이것은 좋은 방법이 아니다.

보통 obj 경로는 git 으로 관리하지않기 때문에 경우에 따라 참조파일을 잃어버릴수 있고 실행경로에 두 dll 파일이 없으면 실행파일이 ocx 를 사용할 수 없는 예외가 발생한다.

따라서 아래와 같은 방법으로 ocx 를 수동으로 추가하는 방법을 사용할 수 있다.

1. 시작 -> 모든 프로그램 -> Visual Studio 20XX -> Visual Studio Tools -> Developer Command Prompt for VS 20XX 실행

2. ocx 파일 경로로 이동

3. 경로>aximp /source DXMediaPlayer.ocx 실행

4. AxDXMediaPlayerLib.cs 파일과 DXMediaPlayerLib.dll, AxDXMediaPlayerLib.dll 3개의 파일이 생성된다

5. 이 중 AxDXMediaPlayerLib.cs, DXMediaPlayerLib.dll 두 개의 파일을 삽입을 원하는 프로젝트 경로로 복사

6. 프로젝트 참조에 DXMediaPlayerLib.dll 추가, 소스에 AxDXMediaPlayerLib.cs 추가

7. 빌드 후 Assembly.cs 파일에 버전에러 발생하면 해당 라인 주석처리

이렇게 추가하면 실행경로에 dll 파일이 없어도 실행가능하다.