diff --git a/OBS.rc b/OBS.rc
index 6e599aef..6437b244 100644
--- a/OBS.rc
+++ b/OBS.rc
@@ -57,12 +57,11 @@ STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTI
CAPTION "Settings"
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
+ LISTBOX IDC_SETTINGSLIST,3,4,118,287,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
+ CONTROL "スタティック",IDC_SUBDIALOG,"Static",SS_LEFTNOWORDWRAP | NOT WS_VISIBLE | WS_GROUP,124,5,427,287,WS_EX_STATICEDGE
DEFPUSHBUTTON "OK",IDOK,339,294,67,14
PUSHBUTTON "Cancel",IDCANCEL,412,294,67,14
PUSHBUTTON "Apply",IDC_APPLY,484,294,67,14
- LISTBOX IDC_SETTINGSLIST,3,4,118,287,LBS_NOINTEGRALHEIGHT | WS_VSCROLL | WS_TABSTOP
- CONTROL "スタティック",IDC_SUBDIALOG,"Static",SS_LEFTNOWORDWRAP | NOT WS_VISIBLE | WS_GROUP,124,5,427,287,WS_EX_STATICEDGE
- PUSHBUTTON "Settings.Defaults",IDC_DEFAULTS,238,294,67,14,NOT WS_VISIBLE
END
IDD_SETTINGS_PUBLISH DIALOGEX 0, 0, 427, 287
@@ -70,18 +69,27 @@ STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
- RTEXT "Settings.Publish.Mode",IDC_STATIC,4,12,138,8,WS_DISABLED
- COMBOBOX IDC_MODE,144,10,126,59,CBS_DROPDOWNLIST | WS_DISABLED | WS_VSCROLL | WS_TABSTOP
+ RTEXT "Settings.Publish.Mode",IDC_STATIC,4,12,138,8
+ COMBOBOX IDC_MODE,144,10,126,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
RTEXT "Settings.Publish.Service",IDC_SERVICE_STATIC,4,29,138,8
COMBOBOX IDC_SERVICE,144,27,126,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- RTEXT "Settings.Publish.Playpath",IDC_PLAYPATH_STATIC,4,64,138,8
- EDITTEXT IDC_PLAYPATH,144,61,226,14,ES_PASSWORD | ES_AUTOHSCROLL
RTEXT "Settings.Publish.ChannelName",IDC_CHANNELNAME_STATIC,4,47,138,8
EDITTEXT IDC_CHANNELNAME,144,44,226,14,ES_AUTOHSCROLL
+ RTEXT "Settings.Publish.Playpath",IDC_PLAYPATH_STATIC,4,64,138,8
+ EDITTEXT IDC_PLAYPATH,144,61,226,14,ES_PASSWORD | ES_AUTOHSCROLL
RTEXT "Settings.Publish.Server",IDC_SERVER_STATIC,4,81,138,8
- COMBOBOX IDC_SERVERLIST,144,79,226,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
- LTEXT "Settings.Info",IDC_INFO,4,119,408,37,NOT WS_VISIBLE
EDITTEXT IDC_SERVEREDIT,144,78,226,14,ES_AUTOHSCROLL
+ COMBOBOX IDC_SERVERLIST,143,78,226,59,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Settings.Publish.AutoReconnect",IDC_AUTORECONNECT,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,97,145,10,WS_EX_RIGHT
+ RTEXT "Settings.Publish.AutoReconnectTimeout",IDC_AUTORECONNECT_TIMEOUT_STATIC,7,115,133,8
+ EDITTEXT IDC_AUTORECONNECT_TIMEOUT_EDIT,143,112,40,14,ES_AUTOHSCROLL | ES_READONLY
+ CONTROL "",IDC_AUTORECONNECT_TIMEOUT,"msctls_updown32",UDS_SETBUDDYINT | UDS_ALIGNRIGHT | UDS_AUTOBUDDY | UDS_ARROWKEYS,185,112,11,14
+ LTEXT "Settings.Info",IDC_INFO,7,171,408,37,NOT WS_VISIBLE
+ CONTROL "Settings.Publish.SaveToFile",IDC_SAVETOFILE,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,7,132,145,10,WS_EX_RIGHT
+ RTEXT "Settings.Publish.SavePath",IDC_SAVEPATH_STATIC,7,150,133,8
+ EDITTEXT IDC_SAVEPATH,143,147,148,14,ES_AUTOHSCROLL
+ PUSHBUTTON "Browse",IDC_BROWSE,295,147,63,14
END
IDD_SETTINGS_ENCODING DIALOGEX 0, 0, 427, 287
@@ -149,9 +157,18 @@ STYLE DS_SETFONT | DS_FIXEDSYS | DS_CONTROL | WS_CHILD | WS_SYSMENU
EXSTYLE WS_EX_CONTROLPARENT
FONT 8, "MS Shell Dlg", 400, 0, 0x1
BEGIN
- LTEXT "Settings.Info",IDC_INFO,4,44,408,37,NOT WS_VISIBLE
+ LTEXT "Settings.Info",IDC_INFO,4,85,408,37,NOT WS_VISIBLE
RTEXT "Settings.Audio.Device",IDC_STATIC,4,12,138,8
COMBOBOX IDC_MICDEVICES,144,10,226,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ CONTROL "Settings.Audio.PushToTalkHotkey",IDC_PUSHTOTALK,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,9,31,143,10,WS_EX_RIGHT
+ CONTROL "",IDC_PUSHTOTALKHOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,159,29,80,14
+ CONTROL "",IDC_MUTEMICHOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,160,46,80,14
+ CONTROL "",IDC_MUTEDESKTOPHOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,160,64,80,14
+ RTEXT "Settings.Audio.MuteMicHotkey",IDC_STATIC,4,48,138,8
+ RTEXT "Settings.Audio.MuteDesktopHotkey",IDC_STATIC,4,66,138,8
+ PUSHBUTTON "ClearHotkey",IDC_CLEARPUSHTOTALK,240,29,73,14
+ PUSHBUTTON "ClearHotkey",IDC_CLEARMUTEMIC,240,46,73,14
+ PUSHBUTTON "ClearHotkey",IDC_CLEARMUTEDESKTOP,240,64,73,14
END
IDD_ENTERNAME DIALOGEX 0, 0, 265, 49
@@ -241,7 +258,33 @@ BEGIN
CONTROL "",IDC_HOTKEY,"msctls_hotkey32",WS_BORDER | WS_TABSTOP,94,7,80,14
DEFPUSHBUTTON "OK",IDOK,130,28,50,14
PUSHBUTTON "Cancel",IDCANCEL,183,28,50,14
- PUSHBUTTON "Scene.Hotkey.Clear",IDC_CLEAR,177,7,56,14
+ PUSHBUTTON "ClearHotkey",IDC_CLEAR,177,7,56,14
+END
+
+IDD_RECONNECTING DIALOGEX 0, 0, 179, 50
+STYLE DS_SETFONT | DS_MODALFRAME | DS_FIXEDSYS | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
+CAPTION "Reconnecting"
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ DEFPUSHBUTTON "Cancel",IDCANCEL,65,29,50,14
+ CTEXT "",IDC_RECONNECTING,7,12,165,8
+END
+
+IDD_SETTINGS_ADVANCED DIALOGEX 0, 0, 427, 287
+STYLE DS_SETFONT | DS_FIXEDSYS | WS_CHILD | WS_SYSMENU
+FONT 8, "MS Shell Dlg", 400, 0, 0x1
+BEGIN
+ GROUPBOX "Settings.Video",IDC_STATIC,4,2,418,66
+ CONTROL "Settings.Advanced.VideoEncoderSettings",IDC_USEVIDEOENCODERSETTINGS,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,14,203,10
+ EDITTEXT IDC_VIDEOENCODERSETTINGS,33,29,373,14,ES_AUTOHSCROLL
+ CONTROL "Settings.Advanced.UseSyncFix",IDC_USESYNCFIX,"Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,49,203,10
+ LTEXT "Settings.Info",IDC_INFO,4,136,418,37,NOT WS_VISIBLE
+ GROUPBOX "Settings.Advanced.Network",IDC_STATIC,4,73,418,52
+ CONTROL "Settings.Advanced.UseSendBuffer",IDC_USESENDBUFFER,
+ "Button",BS_AUTOCHECKBOX | WS_TABSTOP,22,87,203,10
+ COMBOBOX IDC_SENDBUFFERSIZE,144,101,158,30,CBS_DROPDOWNLIST | WS_VSCROLL | WS_TABSTOP
+ RTEXT "Settings.Advanced.SendBufferSize",IDC_STATIC,4,103,138,8
END
@@ -348,6 +391,22 @@ BEGIN
TOPMARGIN, 7
BOTTOMMARGIN, 41
END
+
+ IDD_RECONNECTING, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 172
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 43
+ END
+
+ IDD_SETTINGS_ADVANCED, DIALOG
+ BEGIN
+ LEFTMARGIN, 7
+ RIGHTMARGIN, 420
+ TOPMARGIN, 7
+ BOTTOMMARGIN, 280
+ END
END
#endif // APSTUDIO_INVOKED
diff --git a/OBS.vcproj b/OBS.vcproj
index 80c37a2f..c3760bea 100644
--- a/OBS.vcproj
+++ b/OBS.vcproj
@@ -410,6 +410,10 @@
RelativePath=".\Source\Encoder_x264.cpp"
>
+
+
@@ -434,6 +438,10 @@
RelativePath=".\Source\MMDeviceAudioSource.cpp"
>
+
+
@@ -446,10 +454,6 @@
RelativePath=".\Source\RTMPPublisher.cpp"
>
-
-
diff --git a/OBSApi/APIInterface.h b/OBSApi/APIInterface.h
index 8b76d1c2..01f12946 100644
--- a/OBSApi/APIInterface.h
+++ b/OBSApi/APIInterface.h
@@ -65,6 +65,10 @@ public:
virtual Vect2 GetRenderFrameSize() const=0; //get the render frame size
virtual Vect2 GetOutputSize() const=0; //get the stream output size
+ virtual void GetBaseSize(UINT &width, UINT &height) const=0;
+ virtual void GetRenderFrameSize(UINT &width, UINT &height) const=0;
+ virtual void GetOutputSize(UINT &width, UINT &height) const=0;
+
virtual CTSTR GetLanguage() const=0;
virtual HWND GetMainWindow() const=0;
diff --git a/OBSApi/Utility/ConfigFile.cpp b/OBSApi/Utility/ConfigFile.cpp
index fec1a2bc..bbd80f83 100644
--- a/OBSApi/Utility/ConfigFile.cpp
+++ b/OBSApi/Utility/ConfigFile.cpp
@@ -68,7 +68,7 @@ BOOL ConfigFile::LoadFile(DWORD dwOpenMode)
if(bOpen)
Close();
- dwLength = file.GetFileSize();
+ dwLength = (DWORD)file.GetFileSize();
LPSTR lpTempFileData = (LPSTR)Allocate(dwLength+5);
file.Read(&lpTempFileData[2], dwLength);
diff --git a/OBSApi/Utility/Serializer.h b/OBSApi/Utility/Serializer.h
index 7f390f7a..d97fc7a6 100644
--- a/OBSApi/Utility/Serializer.h
+++ b/OBSApi/Utility/Serializer.h
@@ -34,9 +34,9 @@ public:
virtual void Serialize(LPVOID lpData, DWORD length)=0;
- virtual DWORD Seek(long offset, DWORD seekType=SERIALIZE_SEEK_START)=0;
+ virtual UINT64 Seek(INT64 offset, DWORD seekType=SERIALIZE_SEEK_START)=0;
- virtual DWORD GetPos() const=0;
+ virtual UINT64 GetPos() const=0;
virtual BOOL DataPending() {return FALSE;}
diff --git a/OBSApi/Utility/Template.h b/OBSApi/Utility/Template.h
index ab6dfe77..69a4badb 100644
--- a/OBSApi/Utility/Template.h
+++ b/OBSApi/Utility/Template.h
@@ -776,14 +776,14 @@ public:
return position < bufferSize;
}
- DWORD Seek(long offset, DWORD seekType=SERIALIZE_SEEK_START)
+ UINT64 Seek(INT64 offset, DWORD seekType=SERIALIZE_SEEK_START)
{
long newPos = 0;
if(seekType == SERIALIZE_SEEK_START)
newPos = (long)offset;
else
- newPos = ((seekType == SERIALIZE_SEEK_CURRENT) ? (long)position : (long)bufferSize) + offset;
+ newPos = ((seekType == SERIALIZE_SEEK_CURRENT) ? (long)position : (long)bufferSize) + (long)offset;
if(newPos > (long)bufferSize)
{
@@ -798,12 +798,12 @@ public:
position = (DWORD)newPos;
- return abs(offset);
+ return abs((long)offset);
}
- DWORD GetPos() const
+ UINT64 GetPos() const
{
- return position;
+ return UINT64(position);
}
private:
@@ -846,13 +846,13 @@ public:
position += length;
}
- DWORD Seek(long offset, DWORD seekType=SERIALIZE_SEEK_START)
+ UINT64 Seek(INT64 offset, DWORD seekType=SERIALIZE_SEEK_START)
{
long newPos = 0;
if(seekType == SERIALIZE_SEEK_START)
- newPos = offset;
+ newPos = (long)offset;
else
- newPos = ((seekType == SERIALIZE_SEEK_CURRENT) ? (long)position : (long)Buffer.Num()) + offset;
+ newPos = ((seekType == SERIALIZE_SEEK_CURRENT) ? (long)position : (long)Buffer.Num()) + (long)offset;
if(newPos < 0)
{
@@ -862,12 +862,12 @@ public:
position = (DWORD)newPos;
- return abs(offset);
+ return abs((long)offset);
}
- DWORD GetPos() const
+ UINT64 GetPos() const
{
- return position;
+ return UINT64(position);
}
private:
diff --git a/OBSApi/Utility/XConfig.cpp b/OBSApi/Utility/XConfig.cpp
index 4d7477b2..5b3e5b0c 100644
--- a/OBSApi/Utility/XConfig.cpp
+++ b/OBSApi/Utility/XConfig.cpp
@@ -906,7 +906,7 @@ bool XConfig::Open(CTSTR lpFile)
RootElement = new XElement(this, NULL, TEXT("Root"));
strFileName = lpFile;
- DWORD dwFileSize = file.GetFileSize();
+ DWORD dwFileSize = (DWORD)file.GetFileSize();
LPSTR lpFileDataUTF8 = (LPSTR)Allocate(dwFileSize+1);
zero(lpFileDataUTF8, dwFileSize+1);
diff --git a/OBSApi/Utility/XFile.h b/OBSApi/Utility/XFile.h
index 07f5a7c1..9cdb509b 100644
--- a/OBSApi/Utility/XFile.h
+++ b/OBSApi/Utility/XFile.h
@@ -38,7 +38,7 @@
class BASE_EXPORT XFile
{
HANDLE hFile;
- DWORD dwPos;
+ QWORD qwPos;
BOOL bHasWritten;
public:
@@ -63,7 +63,7 @@ public:
return;
SetPos(0, XFILE_BEGIN);
- DWORD dwFileSize = GetFileSize();
+ DWORD dwFileSize = (DWORD)GetFileSize();
LPSTR lpFileDataUTF8 = (LPSTR)Allocate(dwFileSize+1);
lpFileDataUTF8[dwFileSize] = 0;
Read(lpFileDataUTF8, dwFileSize);
@@ -73,10 +73,9 @@ public:
}
BOOL SetFileSize(DWORD dwSize);
- DWORD GetFileSize() const;
- UINT64 GetFileSize64() const;
- DWORD SetPos(int iPos, DWORD dwMoveMethod);
- inline DWORD GetPos() const {return dwPos;}
+ QWORD GetFileSize() const;
+ QWORD SetPos(INT64 iPos, DWORD dwMoveMethod);
+ inline QWORD GetPos() const {return qwPos;}
void Close();
};
@@ -132,14 +131,14 @@ public:
}
}
- DWORD Seek(long offset, DWORD seekType=SERIALIZE_SEEK_START)
+ UINT64 Seek(INT64 offset, DWORD seekType=SERIALIZE_SEEK_START)
{
- DWORD ret;
+ QWORD ret;
if(seekType == SERIALIZE_SEEK_START)
- ret = file.SetPos(offset, XFILE_BEGIN);
+ ret = file.SetPos(INT64(offset), XFILE_BEGIN);
else if(seekType == SERIALIZE_SEEK_CURRENT)
- ret = file.SetPos(long(GetPos())+offset, XFILE_BEGIN);
+ ret = file.SetPos(INT64(GetPos())+offset, XFILE_BEGIN);
else if(seekType == SERIALIZE_SEEK_END)
ret = file.SetPos(offset, XFILE_END);
@@ -148,7 +147,7 @@ public:
return ret;
}
- DWORD GetPos() const
+ UINT64 GetPos() const
{
return (file.GetPos()-bufferSize)+bufferPos;
}
@@ -169,7 +168,8 @@ private:
XFile file;
- DWORD bufferPos, bufferSize, fileSize;
+ DWORD bufferPos, bufferSize;
+ UINT64 fileSize;
BYTE Buffer[2048];
};
@@ -181,8 +181,12 @@ public:
BOOL IsLoading() {return FALSE;}
- BOOL Open(CTSTR lpFile, DWORD dwCreationDisposition)
+ BOOL Open(CTSTR lpFile, DWORD dwCreationDisposition, DWORD bufferSize=(1024*64))
{
+ if(!bufferSize) bufferSize = 1024*64;
+
+ this->bufferSize = bufferSize;
+ Buffer = (LPBYTE)Allocate(bufferSize);
totalWritten = bufferPos = 0;
return file.Open(lpFile, XFILE_WRITE, dwCreationDisposition);
}
@@ -191,6 +195,7 @@ public:
{
Flush();
file.Close();
+ Free(Buffer);
}
void Serialize(LPVOID lpData, DWORD length)
@@ -203,10 +208,10 @@ public:
while(length)
{
- if(bufferPos == 2048)
+ if(bufferPos == bufferSize)
Flush();
- DWORD dwWriteSize = MIN(length, (2048-bufferPos));
+ DWORD dwWriteSize = MIN(length, (bufferSize-bufferPos));
assert(dwWriteSize);
@@ -222,7 +227,7 @@ public:
}
}
- DWORD Seek(long offset, DWORD seekType=SERIALIZE_SEEK_START)
+ UINT64 Seek(INT64 offset, DWORD seekType=SERIALIZE_SEEK_START)
{
Flush();
if(seekType == SERIALIZE_SEEK_START)
@@ -232,15 +237,15 @@ public:
else if(seekType == SERIALIZE_SEEK_END)
seekType = XFILE_END;
- return file.SetPos(offset, seekType);;
+ return file.SetPos(offset, seekType);
}
- DWORD GetPos() const
+ UINT64 GetPos() const
{
return file.GetPos()+bufferPos;
}
- inline DWORD GetTotalWritten() {return totalWritten;}
+ inline QWORD GetTotalWritten() {return totalWritten;}
private:
void Flush()
@@ -254,6 +259,9 @@ private:
XFile file;
- DWORD bufferPos, totalWritten;
- BYTE Buffer[2048];
+ DWORD bufferPos;
+ DWORD bufferSize;
+
+ QWORD totalWritten;
+ LPBYTE Buffer;
};
diff --git a/OBSApi/Utility/XFile_Windows.cpp b/OBSApi/Utility/XFile_Windows.cpp
index 4b39f8ad..8fcfdaf5 100644
--- a/OBSApi/Utility/XFile_Windows.cpp
+++ b/OBSApi/Utility/XFile_Windows.cpp
@@ -30,7 +30,7 @@
XFile::XFile()
{
hFile = INVALID_HANDLE_VALUE;
- dwPos = 0;
+ qwPos = 0;
bHasWritten = false;
}
@@ -48,7 +48,7 @@ BOOL XFile::Open(CTSTR lpFile, DWORD dwAccess, DWORD dwCreationDisposition)
{
traceInFast(XFile::Open);
- dwPos = 0;
+ qwPos = 0;
assert(lpFile);
if((hFile = CreateFile(lpFile, dwAccess, 0, NULL, dwCreationDisposition, 0, NULL)) == INVALID_HANDLE_VALUE)
@@ -75,7 +75,7 @@ DWORD XFile::Read(LPVOID lpBuffer, DWORD dwBytes)
if(!hFile) return XFILE_ERROR;
- dwPos += dwBytes;
+ qwPos += dwBytes;
ReadFile(hFile, lpBuffer, dwBytes, &dwRet, NULL);
return dwRet;
@@ -93,7 +93,7 @@ DWORD XFile::Write(const void *lpBuffer, DWORD dwBytes)
if(!hFile) return XFILE_ERROR;
- dwPos += dwBytes;
+ qwPos += dwBytes;
WriteFile(hFile, lpBuffer, dwBytes, &dwRet, NULL);
@@ -179,8 +179,8 @@ BOOL XFile::SetFileSize(DWORD dwSize)
if(!hFile) return 0;
- if(dwPos > dwSize)
- dwPos = dwSize;
+ if(qwPos > dwSize)
+ qwPos = dwSize;
SetPos(dwSize, XFILE_BEGIN);
return SetEndOfFile(hFile);
@@ -188,19 +188,10 @@ BOOL XFile::SetFileSize(DWORD dwSize)
traceOutFast;
}
-DWORD XFile::GetFileSize() const
+QWORD XFile::GetFileSize() const
{
traceInFast(XFile::GetFileSize);
- return ::GetFileSize(hFile, NULL);
-
- traceOutFast;
-}
-
-UINT64 XFile::GetFileSize64() const
-{
- traceInFast(XFile::GetFileSize64);
-
UINT64 size = 0;
size |= ::GetFileSize(hFile, (DWORD*)(((BYTE*)&size)+4));
@@ -209,7 +200,7 @@ UINT64 XFile::GetFileSize64() const
traceOutFast;
}
-DWORD XFile::SetPos(int iPos, DWORD dwMoveMethod) //uses the SetFilePointer 4th parameter flags
+UINT64 XFile::SetPos(INT64 iPos, DWORD dwMoveMethod) //uses the SetFilePointer 4th parameter flags
{
traceInFast(XFile::SetPos);
@@ -234,7 +225,13 @@ DWORD XFile::SetPos(int iPos, DWORD dwMoveMethod) //uses the SetFilePointer 4th
break;
}
- return (dwPos = SetFilePointer(hFile, iPos, NULL, moveValue));
+ XLARGE_INT largeInt;
+ largeInt.largeVal = iPos;
+
+ DWORD newPosLow = SetFilePointer(hFile, LONG(largeInt.lowVal), &largeInt.highVal, moveValue);
+ largeInt.lowVal = newPosLow;
+
+ return largeInt.largeVal;
traceOutFast;
}
diff --git a/OBSApi/Utility/XMath.cpp b/OBSApi/Utility/XMath.cpp
index 97c29b05..9514a1d9 100644
--- a/OBSApi/Utility/XMath.cpp
+++ b/OBSApi/Utility/XMath.cpp
@@ -1464,38 +1464,38 @@ BOOL Matrix4x4Inverse(float *destMatrix, float *M1)
return 1;
}
-void Matrix4x4Ortho(float *destMatrix, float left, float right, float bottom, float top, float near, float far)
+void Matrix4x4Ortho(float *destMatrix, double left, double right, double bottom, double top, double near, double far)
{
float matrixOut[4][4];
zero(matrixOut, 64);
- matrixOut[0][0] = 2.0f / (right-left);
- matrixOut[3][0] = (left+right) / (left-right);
+ matrixOut[0][0] = float(2.0 / (right-left));
+ matrixOut[3][0] = float((left+right) / (left-right));
- matrixOut[1][1] = 2.0f / (top-bottom);
- matrixOut[3][1] = (bottom+top) / (bottom-top);
+ matrixOut[1][1] = float(2.0 / (top-bottom));
+ matrixOut[3][1] = float((bottom+top) / (bottom-top));
- matrixOut[2][2] = 1.0f / (far-near);
- matrixOut[3][2] = near / (near-far);
+ matrixOut[2][2] = float(1.0 / (far-near));
+ matrixOut[3][2] = float(near / (near-far));
matrixOut[3][3] = 1.0f;
mcpy(destMatrix, matrixOut, 64);
}
-void Matrix4x4Frustum(float *destMatrix, float left, float right, float bottom, float top, float near, float far)
+void Matrix4x4Frustum(float *destMatrix, double left, double right, double bottom, double top, double near, double far)
{
float matrixOut[4][4];
zero(matrixOut, 64);
- matrixOut[0][0] = (2.0f*near) / (right-left);
- matrixOut[2][0] = (left+right) / (left-right);
+ matrixOut[0][0] = float((2.0*near) / (right-left));
+ matrixOut[2][0] = float((left+right) / (left-right));
- matrixOut[1][1] = (2.0f*near) / (top-bottom);
- matrixOut[2][1] = (top+bottom) / (bottom-top);
+ matrixOut[1][1] = float((2.0*near) / (top-bottom));
+ matrixOut[2][1] = float((top+bottom) / (bottom-top));
- matrixOut[2][2] = far / (far-near);
- matrixOut[3][2] = (near*far) / (near-far);
+ matrixOut[2][2] = float(far / (far-near));
+ matrixOut[3][2] = float((near*far) / (near-far));
matrixOut[2][3] = 1.0f;
diff --git a/OBSApi/Utility/XMath.h b/OBSApi/Utility/XMath.h
index 32883c5f..c1d4c397 100644
--- a/OBSApi/Utility/XMath.h
+++ b/OBSApi/Utility/XMath.h
@@ -57,6 +57,19 @@ struct Matrix;
struct Float16;
+union XLARGE_INT
+{
+ struct {DWORD lowVal; LONG highVal;};
+ INT64 largeVal;
+};
+
+union XLARGE_UINT
+{
+ struct {DWORD lowUVal; DWORD highVal;};
+ UINT64 largeVal;
+};
+
+
/*=========================================================
Optimized math functions
@@ -2157,6 +2170,6 @@ BASE_EXPORT float Matrix4x4Determinant(float *M1);
BASE_EXPORT void Matrix4x4SubMatrix(float *destMatrix, float *M1, int i, int j);
BASE_EXPORT BOOL Matrix4x4Inverse(float *destMatrix, float *M1);
BASE_EXPORT void Matrix4x4Transpose(float *destMatrix, float *srcMatrix);
-BASE_EXPORT void Matrix4x4Ortho(float *destMatrix, float left, float right, float bottom, float top, float near, float far);
-BASE_EXPORT void Matrix4x4Frustum(float *destMatrix, float left, float right, float bottom, float top, float near, float far);
+BASE_EXPORT void Matrix4x4Ortho(float *destMatrix, double left, double right, double bottom, double top, double near, double far);
+BASE_EXPORT void Matrix4x4Frustum(float *destMatrix, double left, double right, double bottom, double top, double near, double far);
BASE_EXPORT void Matrix4x4Perspective(float *destMatrix, float angle, float aspect, float near, float far);
diff --git a/Source/API.cpp b/Source/API.cpp
index a9eb2cdb..a0c539f5 100644
--- a/Source/API.cpp
+++ b/Source/API.cpp
@@ -241,7 +241,7 @@ bool OBS::SetScene(CTSTR lpScene)
//todo: cache scenes maybe? undecided. not really as necessary with global sources
OSEnterMutex(hSceneMutex);
- if (scene)
+ if(scene)
scene->EndScene();
Scene *previousScene = scene;
diff --git a/Source/D3D10System.cpp b/Source/D3D10System.cpp
index 6b84e89e..6dfbd7a0 100644
--- a/Source/D3D10System.cpp
+++ b/Source/D3D10System.cpp
@@ -631,14 +631,14 @@ void D3D10System::DrawSpriteEx(Texture *texture, float x, float y, float x2, flo
VBData *data = spriteVertexBuffer->GetData();
data->VertList[0].Set(x, y, 0.0f);
- data->VertList[1].Set(x2, y, 0.0f);
- data->VertList[2].Set(x, y2, 0.0f);
+ data->VertList[1].Set(x, y2, 0.0f);
+ data->VertList[2].Set(x2, y, 0.0f);
data->VertList[3].Set(x2, y2, 0.0f);
List &coords = data->UVList[0];
coords[0].Set(u, v);
- coords[1].Set(u2, v);
- coords[2].Set(u, v2);
+ coords[1].Set(u, v2);
+ coords[2].Set(u2, v);
coords[3].Set(u2, v2);
spriteVertexBuffer->FlushBuffers();
diff --git a/Source/Encoder_x264.cpp b/Source/Encoder_x264.cpp
index ce6cdade..c97495a5 100644
--- a/Source/Encoder_x264.cpp
+++ b/Source/Encoder_x264.cpp
@@ -102,7 +102,7 @@ public:
//paramData.i_threads = 4;
paramData.b_vfr_input = 1;
- paramData.i_keyint_max = fps*5; //keyframe every 5 sec, should make this an option
+ paramData.i_keyint_max = fps*2; //keyframe every 5 sec, should make this an option
paramData.i_width = width;
paramData.i_height = height;
paramData.rc.i_vbv_max_bitrate = maxBitRate; //vbv-maxrate
@@ -122,6 +122,35 @@ public:
//paramData.pf_log = get_x264_log;
//paramData.i_log_level = X264_LOG_INFO;
+ BOOL bUseCustomParams = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCustomSettings"));
+ if(bUseCustomParams)
+ {
+ String strCustomParams = AppConfig->GetString(TEXT("Video Encoding"), TEXT("CustomSettings"));
+
+ StringList paramList;
+ strCustomParams.GetTokenList(paramList, ' ', FALSE);
+ for(UINT i=0; iPacket[0] = ((nal.i_type == NAL_SLICE_IDR) ? 0x17 : 0x27);
newPacket->Packet[1] = 1;
mcpy(newPacket->Packet+2, timeOffsetAddr, 3);
- *(DWORD*)(newPacket->Packet+5) = htonl(nal.i_payload-skipBytes);
+ *(DWORD*)(newPacket->Packet+5) = htonl(newPayloadSize);
mcpy(newPacket->Packet+9, nal.p_payload+skipBytes, newPayloadSize);
}
else if(nal.i_type == NAL_SPS)
diff --git a/Source/FLVFileStream.cpp b/Source/FLVFileStream.cpp
new file mode 100644
index 00000000..d5abc794
--- /dev/null
+++ b/Source/FLVFileStream.cpp
@@ -0,0 +1,119 @@
+/********************************************************************************
+ Copyright (C) 2012 Hugh Bailey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+********************************************************************************/
+
+
+#include "Main.h"
+#include "RTMPStuff.h"
+
+
+
+class FLVFileStream : public VideoFileStream
+{
+ XFileOutputSerializer fileOut;
+ String strFile;
+
+ UINT64 metaDataPos;
+ DWORD lastTimeStamp;
+
+ void AppendFLVPacket(LPBYTE lpData, UINT size, BYTE type, DWORD timestamp)
+ {
+ UINT networkDataSize = fastHtonl(size);
+ UINT networkTimestamp = fastHtonl(timestamp);
+ UINT streamID = 0;
+ fileOut.OutputByte(type);
+ fileOut.Serialize(((LPBYTE)(&networkDataSize))+1, 3);
+ fileOut.Serialize(((LPBYTE)(&networkTimestamp))+1, 3);
+ fileOut.Serialize(&networkTimestamp, 1);
+ fileOut.Serialize(&streamID, 3);
+ fileOut.Serialize(lpData, size);
+ fileOut.OutputDword(fastHtonl(size+14));
+
+ lastTimeStamp = timestamp;
+ }
+
+public:
+ bool Init(CTSTR lpFile)
+ {
+ strFile = lpFile;
+
+ if(!fileOut.Open(lpFile, XFILE_CREATEALWAYS, 1024*1024))
+ return false;
+
+ fileOut.OutputByte('F');
+ fileOut.OutputByte('L');
+ fileOut.OutputByte('V');
+ fileOut.OutputByte(1);
+ fileOut.OutputByte(5); //bit 0 = (hasAudio), bit 2 = (hasAudio)
+ fileOut.OutputDword(DWORD_BE(9));
+ fileOut.OutputDword(0);
+
+ metaDataPos = fileOut.GetPos();
+
+ char metaDataBuffer[2048];
+ char *enc = metaDataBuffer;
+ char *pend = metaDataBuffer+sizeof(metaDataBuffer);
+
+ enc = AMF_EncodeString(enc, pend, &av_onMetaData);
+ char *endMetaData = App->EncMetaData(enc, pend);
+ UINT metaDataSize = endMetaData-metaDataBuffer;
+
+ AppendFLVPacket((LPBYTE)metaDataBuffer, metaDataSize, 18, 0);
+ return true;
+ }
+
+ ~FLVFileStream()
+ {
+ UINT64 fileSize = fileOut.GetPos();
+ fileOut.Close();
+
+ XFile file;
+ if(file.Open(strFile, XFILE_WRITE, XFILE_OPENEXISTING))
+ {
+ double doubleFileSize = double(fileSize);
+ double doubleDuration = double(lastTimeStamp/1000);
+
+ file.SetPos(metaDataPos+0x24, XFILE_BEGIN);
+ QWORD outputVal = *reinterpret_cast(&doubleDuration);
+ outputVal = fastHtonll(outputVal);
+ file.Write(&outputVal, 8);
+
+ file.SetPos(metaDataPos+0x37, XFILE_BEGIN);
+ outputVal = *reinterpret_cast(&doubleFileSize);
+ outputVal = fastHtonll(outputVal);
+ file.Write(&outputVal, 8);
+
+ file.Close();
+ }
+ }
+
+ virtual void AddPacket(BYTE *data, UINT size, DWORD timestamp, PacketType type)
+ {
+ AppendFLVPacket(data, size, (type == PacketType_Audio) ? 8 : 9, timestamp);
+ }
+};
+
+
+VideoFileStream* CreateFLVFileStream(CTSTR lpFile)
+{
+ FLVFileStream *fileStream = new FLVFileStream;
+ if(fileStream->Init(lpFile))
+ return fileStream;
+
+ delete fileStream;
+ return NULL;
+}
diff --git a/Source/MP4FileStream.cpp b/Source/MP4FileStream.cpp
new file mode 100644
index 00000000..16398ed5
--- /dev/null
+++ b/Source/MP4FileStream.cpp
@@ -0,0 +1,765 @@
+/********************************************************************************
+ Copyright (C) 2012 Hugh Bailey
+
+ This program is free software; you can redistribute it and/or modify
+ it under the terms of the GNU General Public License as published by
+ the Free Software Foundation; either version 2 of the License, or
+ (at your option) any later version.
+
+ This program is distributed in the hope that it will be useful,
+ but WITHOUT ANY WARRANTY; without even the implied warranty of
+ MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ GNU General Public License for more details.
+
+ You should have received a copy of the GNU General Public License
+ along with this program; if not, write to the Free Software
+ Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
+********************************************************************************/
+
+
+#include "Main.h"
+#include
+
+
+time_t GetMacTime()
+{
+ return time(0)+2082844800;
+}
+
+
+struct SampleToChunk
+{
+ UINT firstChunkID;
+ UINT samplesPerChunk;
+};
+
+struct OffsetVal
+{
+ UINT count;
+ UINT val;
+};
+
+struct MP4VideoFrameInfo
+{
+ UINT64 fileOffset;
+ UINT size;
+ UINT timestamp;
+ INT compositionOffset;
+};
+
+struct MP4AudioFrameInfo
+{
+ UINT64 fileOffset;
+ UINT size;
+};
+
+#define USE_64BIT_MP4 1
+
+
+//code annoyance rating: fairly nightmarish
+
+class MP4FileStream : public VideoFileStream
+{
+ XFileOutputSerializer fileOut;
+ String strFile;
+
+ List videoFrames;
+ List audioFrames;
+
+ List IFrameIDs;
+
+ DWORD lastVideoTimestamp;
+
+ bool bStreamOpened;
+ bool bMP3;
+
+ List endBuffer;
+ List boxOffsets;
+
+ UINT64 mdatStart, mdatStop;
+
+ void PushBox(BufferOutputSerializer &output, DWORD boxName)
+ {
+ boxOffsets.Insert(0, endBuffer.Num());
+
+ output.OutputDword(0);
+ output.OutputDword(boxName);
+ }
+
+ void PopBox()
+ {
+ DWORD boxSize = endBuffer.Num()-boxOffsets[0];
+ *(DWORD*)(endBuffer.Array()+boxOffsets[0]) = fastHtonl(boxSize);
+
+ boxOffsets.Remove(0);
+ }
+
+public:
+ bool Init(CTSTR lpFile)
+ {
+ strFile = lpFile;
+
+ if(!fileOut.Open(lpFile, XFILE_CREATEALWAYS, 1024*1024))
+ return false;
+
+ fileOut.OutputDword(DWORD_BE(0x20));
+ fileOut.OutputDword(DWORD_BE('ftyp'));
+ fileOut.OutputDword(DWORD_BE('isom'));
+ fileOut.OutputDword(DWORD_BE(0x200));
+ fileOut.OutputDword(DWORD_BE('isom'));
+ fileOut.OutputDword(DWORD_BE('iso2'));
+ fileOut.OutputDword(DWORD_BE('avc1'));
+ fileOut.OutputDword(DWORD_BE('mp41'));
+
+ fileOut.OutputDword(DWORD_BE(0x8));
+ fileOut.OutputDword(DWORD_BE('free'));
+
+ mdatStart = fileOut.GetPos();
+ fileOut.OutputDword(DWORD_BE(0x1));
+ fileOut.OutputDword(DWORD_BE('mdat'));
+#ifdef USE_64BIT_MP4
+ fileOut.OutputQword(0);
+#endif
+
+ bMP3 = scmp(App->GetAudioEncoder()->GetCodec(), TEXT("MP3")) == 0;
+
+ bStreamOpened = true;
+
+ return true;
+ }
+
+ template inline void GetChunkInfo(List &data, List &chunks, List &sampleToChunks)
+ {
+ UINT64 curChunkOffset;
+ UINT64 connectedSampleOffset;
+ UINT numSamples = 0;
+
+ for(UINT i=0; iGetFrameTime());
+ UINT audioDuration = fastHtonl(lastVideoTimestamp + DWORD(double(App->GetAudioEncoder()->GetFrameSize())/44.1));
+ UINT width, height;
+ App->GetOutputSize(width, height);
+
+ LPCSTR lpVideoTrack = "videoTrack";
+ LPCSTR lpAudioTrack = "audioTrack";
+
+ //-------------------------------------------
+ // get video headers
+ DataPacket videoHeaders;
+ App->GetVideoHeaders(videoHeaders);
+ List SPS, PPS;
+
+ LPBYTE lpHeaderData = videoHeaders.lpPacket+11;
+ SPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData));
+
+ lpHeaderData += SPS.Num()+3;
+ PPS.CopyArray(lpHeaderData+2, fastHtons(*(WORD*)lpHeaderData));
+
+ //-------------------------------------------
+ // get AAC headers if using AAC
+ List AACHeader;
+ if(!bMP3)
+ {
+ DataPacket data;
+ App->GetAudioHeaders(data);
+ AACHeader.CopyArray(data.lpPacket+2, data.size-2);
+ }
+
+ //-------------------------------------------
+ // get chunk info
+ List videoChunks, audioChunks;
+ List videoSampleToChunk, audioSampleToChunk;
+
+ GetChunkInfo(videoFrames, videoChunks, videoSampleToChunk);
+ GetChunkInfo(audioFrames, audioChunks, audioSampleToChunk);
+
+ //-------------------------------------------
+ // build decode time list and composition offset list
+ List decodeTimes;
+ List compositionOffsets;
+
+ for(UINT i=0; iGetAudioEncoder()->GetBitRate()*1024);
+
+ List esDecoderDescriptor;
+ BufferOutputSerializer esDecoderOut(esDecoderDescriptor);
+ esDecoderOut.OutputByte(bMP3 ? 107 : 64);
+ esDecoderOut.OutputByte(0x15); //stream/type flags. always 0x15 for my purposes.
+ esDecoderOut.OutputWord(0); //buffer size (seems ignorable from my testing, so 0)
+ esDecoderOut.OutputByte(0);
+ esDecoderOut.OutputDword(maxBitRate); //max bit rate (cue bill 'o reily meme for these two)
+ esDecoderOut.OutputDword(maxBitRate); //avg bit rate
+
+ if(!bMP3) //if AAC, put in headers
+ {
+ esDecoderOut.OutputByte(0x5); //decoder specific descriptor type
+ esDecoderOut.OutputByte(0x80); //some stuff that no one should probably care about
+ esDecoderOut.OutputByte(0x80);
+ esDecoderOut.OutputByte(0x80);
+ esDecoderOut.OutputByte(AACHeader.Num());
+ esDecoderOut.Serialize((LPVOID)AACHeader.Array(), AACHeader.Num());
+ }
+
+ esDecoderOut.OutputByte(0x6); //config descriptor type
+ esDecoderOut.OutputByte(0x80); //some stuff that no one should probably care about
+ esDecoderOut.OutputByte(0x80);
+ esDecoderOut.OutputByte(0x80);
+ esDecoderOut.OutputByte(1); //len
+ esDecoderOut.OutputByte(2); //SL value(? always 2)
+
+ List esDescriptor;
+ BufferOutputSerializer esOut(esDescriptor);
+ esOut.OutputWord(0); //es id
+ esOut.OutputByte(0); //stream priority
+ esOut.OutputByte(4); //descriptor type
+ esOut.OutputByte(0x80); //some stuff that no one should probably care about
+ esOut.OutputByte(0x80);
+ esOut.OutputByte(0x80);
+ esOut.OutputByte(esDecoderDescriptor.Num());
+ esOut.Serialize((LPVOID)esDecoderDescriptor.Array(), esDecoderDescriptor.Num());
+
+ //-------------------------------------------
+
+ PushBox(output, DWORD_BE('moov'));
+
+ //------------------------------------------------------
+ // header
+ PushBox(output, DWORD_BE('mvhd'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(macTime); //creation time
+ output.OutputDword(macTime); //modified time
+ output.OutputDword(DWORD_BE(1000)); //time base (milliseconds, so 1000)
+ output.OutputDword(videoDuration); //duration (in time base units)
+ output.OutputDword(DWORD_BE(0x00010000)); //fixed point playback speed 1.0
+ output.OutputWord(WORD_BE(0x0100)); //fixed point vol 1.0
+ output.OutputQword(0); //reserved (10 bytes)
+ output.OutputWord(0);
+ output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
+ output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
+ output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
+ output.OutputDword(0); //prevew start time (time base units)
+ output.OutputDword(0); //prevew duration (time base units)
+ output.OutputDword(0); //still poster frame (timestamp of frame)
+ output.OutputDword(0); //selection(?) start time (time base units)
+ output.OutputDword(0); //selection(?) duration (time base units)
+ output.OutputDword(0); //current time (0, time base units)
+ output.OutputDword(DWORD_BE(3)); //next free track id (1-based rather than 0-based)
+ PopBox(); //mvhd
+
+ //------------------------------------------------------
+ // video track
+ PushBox(output, DWORD_BE('trak'));
+ PushBox(output, DWORD_BE('tkhd')); //track header
+ output.OutputDword(DWORD_BE(0x0000000F)); //version (0) and flags (0xF)
+ output.OutputDword(macTime); //creation time
+ output.OutputDword(macTime); //modified time
+ output.OutputDword(DWORD_BE(1)); //track ID
+ output.OutputDword(0); //reserved
+ output.OutputDword(videoDuration); //duration (in time base units)
+ output.OutputQword(0); //reserved
+ output.OutputWord(0); //video layer (0)
+ output.OutputWord(0); //quicktime alternate track id (0)
+ output.OutputWord(0); //track audio volume (this is video, so 0)
+ output.OutputWord(0); //reserved
+ output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 1 (1.0, 0.0, 0.0)
+ output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00010000)); output.OutputDword(DWORD_BE(0x00000000)); //window matrix row 2 (0.0, 1.0, 0.0)
+ output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x00000000)); output.OutputDword(DWORD_BE(0x40000000)); //window matrix row 3 (0.0, 0.0, 16384.0)
+ output.OutputDword(fastHtonl(width<<16)); //width (fixed point)
+ output.OutputDword(fastHtonl(height<<16)); //height (fixed point)
+ PopBox(); //tkhd
+ PushBox(output, DWORD_BE('edts'));
+ PushBox(output, DWORD_BE('elst'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(DWORD_BE(1)); //count
+ output.OutputDword(videoDuration); //duration
+ output.OutputDword(0); //start time
+ output.OutputDword(DWORD_BE(0x00010000)); //playback speed (1.0)
+ PopBox(); //elst
+ PopBox(); //tdst
+ PushBox(output, DWORD_BE('mdia'));
+ PushBox(output, DWORD_BE('mdhd'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(macTime); //creation time
+ output.OutputDword(macTime); //modified time
+ output.OutputDword(DWORD_BE(1000)); //time scale
+ output.OutputDword(videoDuration);
+ output.OutputDword(DWORD_BE(0x55c40000));
+ PopBox(); //mdhd
+ PushBox(output, DWORD_BE('hdlr'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(0); //quicktime type (none)
+ output.OutputDword(DWORD_BE('vide')); //media type
+ output.OutputDword(0); //manufacturer reserved
+ output.OutputDword(0); //quicktime component reserved flags
+ output.OutputDword(0); //quicktime component reserved mask
+ output.Serialize((LPVOID)lpVideoTrack, (DWORD)strlen(lpVideoTrack)+1); //track name
+ PopBox(); //hdlr
+ PushBox(output, DWORD_BE('minf'));
+ PushBox(output, DWORD_BE('vmhd'));
+ output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
+ output.OutputWord(0); //quickdraw graphic mode (copy = 0)
+ output.OutputWord(0); //quickdraw red value
+ output.OutputWord(0); //quickdraw green value
+ output.OutputWord(0); //quickdraw blue value
+ PopBox(); //vdhd
+ PushBox(output, DWORD_BE('dinf'));
+ PushBox(output, DWORD_BE('dref'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(DWORD_BE(1)); //count
+ PushBox(output, DWORD_BE('url '));
+ output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
+ PopBox(); //url
+ PopBox(); //dref
+ PopBox(); //dinf
+ PushBox(output, DWORD_BE('stbl'));
+ PushBox(output, DWORD_BE('stsd'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(DWORD_BE(1)); //count
+ PushBox(output, DWORD_BE('avc1'));
+ output.OutputDword(0); //reserved 6 bytes
+ output.OutputWord(0);
+ output.OutputWord(WORD_BE(1)); //index
+ output.OutputWord(0); //encoding version
+ output.OutputWord(0); //encoding revision level
+ output.OutputDword(0); //encoding vendor
+ output.OutputDword(0); //temporal quality
+ output.OutputDword(0); //spatial quality
+ output.OutputWord(fastHtons(width)); //width
+ output.OutputWord(fastHtons(height)); //height
+ output.OutputDword(DWORD_BE(0x00480000)); //fixed point width pixel resolution (72.0)
+ output.OutputDword(DWORD_BE(0x00480000)); //fixed point height pixel resolution (72.0)
+ output.OutputDword(0); //quicktime video data size
+ output.OutputWord(WORD_BE(1)); //frame count(?)
+ for(UINT i=0; i<4; i++) //encoding name (byte 1 = string length, 31 bytes = string (whats the point of having a size here?)
+ output.OutputQword(0);
+ output.OutputWord(WORD_BE(24)); //bit depth
+ output.OutputWord(0xFFFF); //quicktime video color table id (none = -1)
+ PushBox(output, DWORD_BE('avcC'));
+ output.OutputByte(1); //version
+ output.OutputByte(100); //h264 profile ID
+ output.OutputByte(0); //h264 compatible profiles
+ output.OutputByte(0x1f); //h264 level
+ output.OutputByte(0xff); //reserved
+ output.OutputByte(0xe1); //first half-byte = no clue. second half = sps count
+ output.OutputWord(fastHtons(SPS.Num())); //sps size
+ output.Serialize(SPS.Array(), SPS.Num()); //sps data
+ output.OutputByte(1); //pps count
+ output.OutputWord(fastHtons(PPS.Num())); //pps size
+ output.Serialize(PPS.Array(), PPS.Num()); //pps data
+ PopBox(); //avcC
+ PopBox(); //avc1
+ PopBox(); //stsd
+ PushBox(output, DWORD_BE('stts')); //frame times
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(fastHtonl(decodeTimes.Num()));
+ for(UINT i=0; i 0xFFFFFFFFLL)
+ {
+ PushBox(output, DWORD_BE('co64')); //chunk offsets
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(fastHtonl(videoChunks.Num()));
+ for(UINT i=0; iGetAudioEncoder()->GetFrameSize()));
+ output.OutputDword(DWORD_BE(0x55c40000));
+ PopBox(); //mdhd
+ PushBox(output, DWORD_BE('hdlr'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(0); //quicktime type (none)
+ output.OutputDword(DWORD_BE('soun')); //media type
+ output.OutputDword(0); //manufacturer reserved
+ output.OutputDword(0); //quicktime component reserved flags
+ output.OutputDword(0); //quicktime component reserved mask
+ output.Serialize((LPVOID)lpAudioTrack, (DWORD)strlen(lpAudioTrack)+1); //track name
+ PopBox(); //hdlr
+ PushBox(output, DWORD_BE('minf'));
+ PushBox(output, DWORD_BE('smhd'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(0); //balance (fixed point)
+ PopBox(); //vdhd
+ PushBox(output, DWORD_BE('dinf'));
+ PushBox(output, DWORD_BE('dref'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(DWORD_BE(1)); //count
+ PushBox(output, DWORD_BE('url '));
+ output.OutputDword(DWORD_BE(0x00000001)); //version (0) and flags (1)
+ PopBox(); //url
+ PopBox(); //dref
+ PopBox(); //dinf
+ PushBox(output, DWORD_BE('stbl'));
+ PushBox(output, DWORD_BE('stsd'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(DWORD_BE(1)); //count
+ PushBox(output, DWORD_BE('mp4a'));
+ output.OutputDword(0); //reserved (6 bytes)
+ output.OutputWord(0);
+ output.OutputWord(WORD_BE(1)); //dref index
+ output.OutputWord(0); //quicktime encoding version
+ output.OutputWord(0); //quicktime encoding revision
+ output.OutputDword(0); //quicktime audio encoding vendor
+ output.OutputWord(WORD_BE(2)); //channels
+ output.OutputWord(WORD_BE(16)); //sample size
+ output.OutputWord(0); //quicktime audio compression id
+ output.OutputWord(0); //quicktime audio packet size
+ output.OutputDword(DWORD_BE(44100<<16)); //sample rate (fixed point)
+ PushBox(output, DWORD_BE('esds'));
+ output.OutputDword(0); //version and flags (none)
+ output.OutputByte(3); //ES descriptor type
+ output.OutputByte(0x80);
+ output.OutputByte(0x80);
+ output.OutputByte(0x80);
+ output.OutputByte(esDescriptor.Num());
+ output.Serialize((LPVOID)esDescriptor.Array(), esDescriptor.Num());
+ PopBox();
+ PopBox();
+ PopBox(); //stsd
+ PushBox(output, DWORD_BE('stts')); //list of keyframe (i-frame) IDs
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(DWORD_BE(1));
+ output.OutputDword(fastHtonl(audioFrames.Num()));
+ output.OutputDword(fastHtonl(App->GetAudioEncoder()->GetFrameSize()));
+ PopBox(); //stss
+ PushBox(output, DWORD_BE('stsc')); //sample to chunk list
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(fastHtonl(audioSampleToChunk.Num()));
+ for(UINT i=0; i 0xFFFFFFFFLL)
+ {
+ PushBox(output, DWORD_BE('co64')); //chunk offsets
+ output.OutputDword(0); //version and flags (none)
+ output.OutputDword(fastHtonl(audioChunks.Num()));
+ for(UINT i=0; i= 0x80)
+ timeOffset |= 0xFF;
+ timeOffset = (INT)fastHtonl(DWORD(timeOffset));
+
+ if(data[0] == 0x17) //i-frame
+ IFrameIDs << fastHtonl(videoFrames.Num()+1);
+
+ MP4VideoFrameInfo frameInfo;
+ frameInfo.fileOffset = offset;
+ frameInfo.size = totalCopied;
+ frameInfo.timestamp = timestamp;
+ frameInfo.compositionOffset = timeOffset;
+ videoFrames << frameInfo;
+ }
+ else
+ videoFrames.Last().size += totalCopied;
+
+ lastVideoTimestamp = timestamp;
+ }
+ }
+};
+
+
+VideoFileStream* CreateMP4FileStream(CTSTR lpFile)
+{
+ MP4FileStream *fileStream = new MP4FileStream;
+ if(fileStream->Init(lpFile))
+ return fileStream;
+
+ delete fileStream;
+ return NULL;
+}
diff --git a/Source/Main.h b/Source/Main.h
index a64bbf70..4f5c97e7 100644
--- a/Source/Main.h
+++ b/Source/Main.h
@@ -72,6 +72,16 @@ inline UINT ConvertMSTo100NanoSec(UINT ms)
return ms*1000*10; //1000 microseconds, then 10 "100nanosecond" segments
}
+//big endian conversion functions
+#define QWORD_BE(val) (((val>>56)&0xFF) | (((val>>48)&0xFF)<<8) | (((val>>40)&0xFF)<<16) | (((val>>32)&0xFF)<<24) | \
+ (((val>>24)&0xFF)<<32) | (((val>>16)&0xFF)<<40) | (((val>>8)&0xFF)<<48) | ((val&0xFF)<<56))
+#define DWORD_BE(val) (((val>>24)&0xFF) | (((val>>16)&0xFF)<<8) | (((val>>8)&0xFF)<<16) | ((val&0xFF)<<24))
+#define WORD_BE(val) (((val>>8)&0xFF) | ((val&0xFF)<<8))
+
+__forceinline QWORD fastHtonll(QWORD qw) {return QWORD_BE(qw);}
+__forceinline DWORD fastHtonl (DWORD dw) {return DWORD_BE(dw);}
+__forceinline WORD fastHtons (WORD w) {return WORD_BE(w);}
+
//-------------------------------------------
// application headers
diff --git a/Source/OBS.cpp b/Source/OBS.cpp
index 853e320a..e3320769 100644
--- a/Source/OBS.cpp
+++ b/Source/OBS.cpp
@@ -44,8 +44,8 @@ bool STDCALL ConfigureBitmapSource(XElement *element, bool bCreating);
ImageSource* STDCALL CreateGlobalSource(XElement *data);
-NetworkStream* CreateRTMPServer();
-NetworkStream* CreateRTMPPublisher();
+//NetworkStream* CreateRTMPServer();
+NetworkStream* CreateRTMPPublisher(String &failReason, bool &bCanRetry);
NetworkStream* CreateBandwidthAnalyzer();
void StartBlankSoundPlayback();
@@ -55,6 +55,10 @@ VideoEncoder* CreateNullVideoEncoder();
AudioEncoder* CreateNullAudioEncoder();
NetworkStream* CreateNullNetwork();
+VideoFileStream* CreateMP4FileStream(CTSTR lpFile);
+VideoFileStream* CreateFLVFileStream(CTSTR lpFile);
+//VideoFileStream* CreateAVIFileStream(CTSTR lpFile);
+
void Convert444to420(LPBYTE input, int width, int height, LPBYTE *output, bool bSSE2Available);
void STDCALL SceneHotkey(DWORD hotkey, UPARAM param, bool bDown);
@@ -342,6 +346,10 @@ public:
virtual Vect2 GetRenderFrameSize() const {return Vect2(float(App->renderFrameWidth), float(App->renderFrameHeight));}
virtual Vect2 GetOutputSize() const {return Vect2(float(App->outputCX), float(App->outputCY));}
+ virtual void GetBaseSize(UINT &width, UINT &height) const {App->GetBaseSize(width, height);}
+ virtual void GetRenderFrameSize(UINT &width, UINT &height) const {App->GetRenderFrameSize(width, height);}
+ virtual void GetOutputSize(UINT &width, UINT &height) const {App->GetOutputSize(width, height);}
+
virtual CTSTR GetLanguage() const {return App->strLanguage;}
virtual CTSTR GetAppDataPath() const {return lpAppDataPath;}
@@ -717,6 +725,11 @@ OBS::OBS()
//-----------------------------------------------------
+ bAutoReconnect = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnect")) != 0;
+ reconnectTimeout = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnectTimeout"), 10);
+ if(reconnectTimeout < 5)
+ reconnectTimeout = 5;
+
bRenderViewEnabled = true;
//bShowFPS = AppConfig->GetInt(TEXT("General"), TEXT("ShowFPS")) != 0;
@@ -904,19 +917,30 @@ void OBS::Start()
int networkMode = AppConfig->GetInt(TEXT("Publish"), TEXT("Mode"), 2);
+ bool bCanRetry = false;
+ String strError;
+
if(bTestStream)
network = CreateBandwidthAnalyzer();
else
{
switch(networkMode)
{
- case 0: network = CreateRTMPPublisher(); break;
- case 1: network = CreateRTMPServer(); break;
+ case 0: network = CreateRTMPPublisher(strError, bCanRetry); break;
+ case 1: network = CreateNullNetwork(); break;
}
}
if(!network)
+ {
+ if(!bReconnecting || !bCanRetry)
+ MessageBox(hwndMain, strError, NULL, MB_ICONERROR);
+ else
+ DialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwndMain, OBS::ReconnectDialogProc);
return;
+ }
+
+ bReconnecting = false;
//-------------------------------------------------------------
@@ -1097,6 +1121,27 @@ void OBS::Start()
//-------------------------------------------------------------
+ bWriteToFile = AppConfig->GetInt(TEXT("Publish"), TEXT("SaveToFile")) != 0;
+ String strOutputFile = AppConfig->GetString(TEXT("Publish"), TEXT("SavePath"));
+
+ if(OSFileExists(strOutputFile))
+ {
+ strOutputFile.FindReplace(TEXT("\\"), TEXT("/"));
+ String strFileWithoutExtension = GetPathWithoutExtension(strOutputFile);
+ String strFileExtension = GetPathExtension(strOutputFile);
+ UINT curFile = 0;
+
+ String strNewFilePath;
+ do
+ {
+ strNewFilePath.Clear() << strFileWithoutExtension << TEXT(" (") << FormattedString(TEXT("%02u"), ++curFile) << TEXT(").") << strFileExtension;
+ } while(OSFileExists(strNewFilePath));
+
+ strOutputFile = strNewFilePath;
+ }
+
+ //-------------------------------------------------------------
+
hRequestAudioEvent = CreateSemaphore(NULL, 0, 0x7FFFFFFFL, NULL);
hSoundDataMutex = OSCreateMutex();
hSoundThread = OSCreateThread((XTHREAD)OBS::MainAudioThread, NULL);
@@ -1109,6 +1154,19 @@ void OBS::Start()
videoEncoder = CreateX264Encoder(fps, outputCX, outputCY, quality, preset, bUsing444, maxBitRate, bufferSize);
+ //-------------------------------------------------------------
+
+ if(!bTestStream && bWriteToFile && strOutputFile.IsValid())
+ {
+ String strFileExtension = GetPathExtension(strOutputFile);
+ if(strFileExtension.CompareI(TEXT("flv")))
+ fileStream = CreateFLVFileStream(strOutputFile);
+ else if(strFileExtension.CompareI(TEXT("mp4")))
+ fileStream = CreateMP4FileStream(strOutputFile);
+ /*else if(strFileExtension.CompareI(TEXT("avi")))
+ fileStream = CreateAVIFileStream(strOutputFile));*/
+ }
+
hMainThread = OSCreateThread((XTHREAD)OBS::MainCaptureThread, NULL);
if(bTestStream)
@@ -1173,13 +1231,15 @@ void OBS::Stop()
delete micAudio;
delete desktopAudio;
- delete audioEncoder;
+ delete fileStream;
+ delete audioEncoder;
delete videoEncoder;
network = NULL;
micAudio = NULL;
desktopAudio = NULL;
+ fileStream = NULL;
audioEncoder = NULL;
videoEncoder = NULL;
@@ -1478,7 +1538,7 @@ void OBS::MainCaptureLoop()
SetRenderTarget(mainRenderTextures[curRenderTarget]);
- Ortho(0.0f, baseSize.x, baseSize.y, 0.0f, -1.0f, 1000.0f);
+ Ortho(0.0f, baseSize.x, baseSize.y, 0.0f, -100.0f, 100.0f);
SetViewport(0, 0, baseSize.x, baseSize.y);
if(scene)
@@ -1531,7 +1591,7 @@ void OBS::MainCaptureLoop()
LoadVertexShader(mainVertexShader);
LoadPixelShader(mainPixelShader);
- Ortho(0.0f, renderFrameSize.x, renderFrameSize.y, 0.0f, -1.0f, 1000.0f);
+ Ortho(0.0f, renderFrameSize.x, renderFrameSize.y, 0.0f, -100.0f, 100.0f);
SetViewport(0.0f, 0.0f, renderFrameSize.x, renderFrameSize.y);
if(bTransitioning)
@@ -1543,7 +1603,7 @@ void OBS::MainCaptureLoop()
DrawSprite(mainRenderTextures[curRenderTarget], 0.0f, 0.0f, renderFrameSize.x, renderFrameSize.y);
- Ortho(0.0f, renderFrameSize.x, renderFrameSize.y, 0.0f, -1.0f, 1000.0f);
+ Ortho(0.0f, renderFrameSize.x, renderFrameSize.y, 0.0f, -100.0f, 100.0f);
LoadVertexShader(solidVertexShader);
LoadPixelShader(solidPixelShader);
@@ -1552,7 +1612,7 @@ void OBS::MainCaptureLoop()
//draw selections if in edit mode
if(bEditMode && !bSizeChanging)
{
- Ortho(0.0f, baseSize.x, baseSize.y, 0.0f, -1.0f, 1000.0f);
+ Ortho(0.0f, baseSize.x, baseSize.y, 0.0f, -100.0f, 100.0f);
if(scene)
scene->RenderSelections();
@@ -1602,7 +1662,7 @@ void OBS::MainCaptureLoop()
yuvScalePixelShader->SetVector2(hScaleVal, 1.0f/baseSize);
- Ortho(0.0f, outputSize.x, outputSize.y, 0.0f, -1.0f, 1000.0f);
+ Ortho(0.0f, outputSize.x, outputSize.y, 0.0f, -100.0f, 100.0f);
SetViewport(0.0f, 0.0f, outputSize.x, outputSize.y);
//why am I using scaleSize instead of outputSize for the texture?
@@ -1615,7 +1675,7 @@ void OBS::MainCaptureLoop()
BlendFunction(GS_BLEND_FACTOR, GS_BLEND_INVFACTOR, transitionAlpha);
}
- DrawSpriteEx(mainRenderTextures[curRenderTarget], 0.0f, 0.0f, scaleSize.x, scaleSize.y, 0.0f, 0.0f, scaleSize.x, scaleSize.y);
+ DrawSpriteEx(mainRenderTextures[curRenderTarget], 0.0f, 0.0f, outputSize.x, outputSize.y, 0.0f, 0.0f, outputSize.x, outputSize.y);
//------------------------------------
@@ -1741,6 +1801,9 @@ void OBS::MainCaptureLoop()
if(audioData.Num())
{
network->SendPacket(audioData.Array(), audioData.Num(), pendingAudioFrames[0].timestamp, PacketType_Audio);
+ if(fileStream)
+ fileStream->AddPacket(audioData.Array(), audioData.Num(), pendingAudioFrames[0].timestamp, PacketType_Audio);
+
audioData.Clear();
}
@@ -1759,6 +1822,8 @@ void OBS::MainCaptureLoop()
PacketType type = videoPacketTypes[i];
network->SendPacket(packet.lpPacket, packet.size, curTimeStamp, type);
+ if(fileStream)
+ fileStream->AddPacket(packet.lpPacket, packet.size, curTimeStamp, type);
}
curPTS--;
diff --git a/Source/OBS.h b/Source/OBS.h
index 41c5ecc5..60aeb1e2 100644
--- a/Source/OBS.h
+++ b/Source/OBS.h
@@ -98,6 +98,15 @@ public:
//-------------------------------------------------------------------
+class VideoFileStream
+{
+public:
+ virtual ~VideoFileStream() {}
+ virtual void AddPacket(BYTE *data, UINT size, DWORD timestamp, PacketType type)=0;
+};
+
+//-------------------------------------------------------------------
+
enum
{
NoAudioAvailable,
@@ -250,6 +259,7 @@ enum
#define OBS_REQUESTSTOP WM_USER+1
#define OBS_CALLHOTKEY WM_USER+2
+#define OBS_RECONNECT WM_USER+3
//----------------------------
@@ -338,7 +348,7 @@ class OBS
static INT_PTR CALLBACK PublishSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK VideoSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK AudioSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
- static INT_PTR CALLBACK HotkeysSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK AdvancedSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
//---------------------------------------------------
@@ -351,6 +361,11 @@ class OBS
bool bSizeChanging;
bool bResizeRenderView;
+ bool bAutoReconnect;
+ bool bRetrying;
+ bool bReconnecting;
+ UINT reconnectTimeout;
+
bool bEditMode;
bool bRenderViewEnabled;
bool bShowFPS;
@@ -384,6 +399,9 @@ class OBS
float desktopVol, micVol;
List pendingAudioFrames;
+ bool bWriteToFile;
+ VideoFileStream *fileStream;
+
String streamReport;
List Icons;
@@ -455,6 +473,7 @@ class OBS
static INT_PTR CALLBACK EnterSourceNameDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK EnterSceneNameDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static INT_PTR CALLBACK SceneHotkeyDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
+ static INT_PTR CALLBACK ReconnectDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK ListboxHook(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK RenderFrameProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
static LRESULT CALLBACK OBSProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam);
@@ -466,8 +485,6 @@ class OBS
void ToggleCapturing();
- char* EncMetaData(char *enc, char *pend);
-
Scene* CreateScene(CTSTR lpClassName, XElement *data);
void ConfigureScene(XElement *element);
void ConfigureImageSource(XElement *element);
@@ -486,12 +503,18 @@ public:
OBS();
~OBS();
+ char* EncMetaData(char *enc, char *pend);
+
inline void PostStopMessage() {if(hwndMain) PostMessage(hwndMain, OBS_REQUESTSTOP, 0, 0);}
inline Vect2 GetBaseSize() const {return Vect2(float(baseCX), float(baseCY));}
inline Vect2 GetOutputSize() const {return Vect2(float(outputCX), float(outputCY));}
inline Vect2 GetRenderFrameSize() const {return Vect2(float(renderFrameWidth), float(renderFrameHeight));}
+ inline void GetBaseSize(UINT &width, UINT &height) const {width = baseCX; height = baseCY;}
+ inline void GetRenderFrameSize(UINT &width, UINT &height) const {width = renderFrameWidth; height = renderFrameHeight;}
+ inline void GetOutputSize(UINT &width, UINT &height) const {width = outputCX; height = outputCY;}
+
inline bool SSE2Available() const {return bSSE2Available;}
inline AudioEncoder* GetAudioEncoder() const {return audioEncoder;}
diff --git a/Source/RTMPPublisher.cpp b/Source/RTMPPublisher.cpp
index 287c561d..a12afebd 100644
--- a/Source/RTMPPublisher.cpp
+++ b/Source/RTMPPublisher.cpp
@@ -358,12 +358,23 @@ public:
rtmp = rtmpIn;
- sendBuffer.SetSize(32768);
+ BOOL bUseSendBuffer = AppConfig->GetInt(TEXT("Publish"), TEXT("UseSendBuffer"), 1);
+ UINT sendBufferSize = AppConfig->GetInt(TEXT("Publish"), TEXT("SendBufferSize"), 32768);
+
+ if(sendBufferSize > 32768)
+ sendBufferSize = 32768;
+ else if(sendBufferSize < 8192)
+ sendBufferSize = 8192;
+
+ sendBuffer.SetSize(sendBufferSize);
curSendBufferLen = 0;
- rtmp->m_customSendFunc = (CUSTOMSEND)RTMPPublisher::BufferedSend;
- rtmp->m_customSendParam = this;
- rtmp->m_bCustomSend = TRUE;
+ if(bUseSendBuffer)
+ {
+ rtmp->m_customSendFunc = (CUSTOMSEND)RTMPPublisher::BufferedSend;
+ rtmp->m_customSendParam = this;
+ rtmp->m_bCustomSend = TRUE;
+ }
hSendSempahore = CreateSemaphore(NULL, 0, 0x7FFFFFFFL, NULL);
if(!hSendSempahore)
@@ -569,13 +580,15 @@ public:
}
};
-NetworkStream* CreateRTMPPublisher()
+NetworkStream* CreateRTMPPublisher(String &failReason, bool &bCanRetry)
{
traceIn(CreateRTMPPublisher);
//------------------------------------------------------
// set up URL
+ bCanRetry = false;
+
String strURL;
int serviceID = AppConfig->GetInt (TEXT("Publish"), TEXT("Service"));
@@ -585,13 +598,13 @@ NetworkStream* CreateRTMPPublisher()
if(!strServer.IsValid())
{
- MessageBox(hwndMain, TEXT("No server specified to connect to"), NULL, MB_ICONERROR);
+ failReason = TEXT("No server specified to connect to");
return NULL;
}
if(!strChannel.IsValid())
{
- MessageBox(hwndMain, TEXT("No channel specified"), NULL, MB_ICONERROR);
+ failReason = TEXT("No channel specified");
return NULL;
}
@@ -600,35 +613,35 @@ NetworkStream* CreateRTMPPublisher()
XConfig serverData;
if(!serverData.Open(TEXT("services.xconfig")))
{
- MessageBox(hwndMain, TEXT("Could not open services.xconfig"), NULL, MB_ICONERROR);
+ failReason = TEXT("Could not open services.xconfig");
return NULL;
}
XElement *services = serverData.GetElement(TEXT("services"));
if(!services)
{
- MessageBox(hwndMain, TEXT("Could not any services in services.xconfig"), NULL, MB_ICONERROR);
+ failReason = TEXT("Could not any services in services.xconfig");
return NULL;
}
XElement *service = services->GetElementByID(serviceID-1);
if(!service)
{
- MessageBox(hwndMain, TEXT("Could not find the service specified in services.xconfig"), NULL, MB_ICONERROR);
+ failReason = TEXT("Could not find the service specified in services.xconfig");
return NULL;
}
XElement *servers = service->GetElement(TEXT("servers"));
if(!servers)
{
- MessageBox(hwndMain, TEXT("Could not find any servers for the service specified in services.xconfig"), NULL, MB_ICONERROR);
+ failReason = TEXT("Could not find any servers for the service specified in services.xconfig");
return NULL;
}
XDataItem *item = servers->GetDataItem(strServer);
if(!item)
{
- MessageBox(hwndMain, TEXT("Could not find any server specified for the service specified in services.xconfig"), NULL, MB_ICONERROR);
+ failReason = TEXT("Could not find any server specified for the service specified in services.xconfig");
return NULL;
}
@@ -650,7 +663,7 @@ NetworkStream* CreateRTMPPublisher()
if(!RTMP_SetupURL(rtmp, lpAnsiURL))
{
- MessageBox(hwndMain, Str("Connection.CouldNotParseURL"), NULL, MB_ICONERROR);
+ failReason = Str("Connection.CouldNotParseURL");
RTMP_Free(rtmp);
return NULL;
}
@@ -661,14 +674,15 @@ NetworkStream* CreateRTMPPublisher()
if(!RTMP_Connect(rtmp, NULL))
{
- MessageBox(hwndMain, Str("Connection.CouldNotConnect"), NULL, MB_ICONERROR);
+ failReason = Str("Connection.CouldNotConnect");
RTMP_Free(rtmp);
+ bCanRetry = true;
return NULL;
}
if(!RTMP_ConnectStream(rtmp, 0))
{
- MessageBox(hwndMain, Str("Connection.InvalidStream"), NULL, MB_ICONERROR);
+ failReason = Str("Connection.InvalidStream");
RTMP_Close(rtmp);
RTMP_Free(rtmp);
return NULL;
diff --git a/Source/RTMPServer.cpp b/Source/RTMPServer.cpp
deleted file mode 100644
index a5bfaf76..00000000
--- a/Source/RTMPServer.cpp
+++ /dev/null
@@ -1,549 +0,0 @@
-/********************************************************************************
- Copyright (C) 2012 Hugh Bailey
-
- This program is free software; you can redistribute it and/or modify
- it under the terms of the GNU General Public License as published by
- the Free Software Foundation; either version 2 of the License, or
- (at your option) any later version.
-
- This program is distributed in the hope that it will be useful,
- but WITHOUT ANY WARRANTY; without even the implied warranty of
- MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
- GNU General Public License for more details.
-
- You should have received a copy of the GNU General Public License
- along with this program; if not, write to the Free Software
- Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA 02111-1307, USA.
-********************************************************************************/
-
-
-#include "Main.h"
-#include "RTMPStuff.h"
-
-
-class RTMPServer;
-
-
-void rtmp_log_output(int level, const char *format, va_list vl);
-
-
-//all connections should be handled in the same thread with select, but because the rtmp server is mostly just for
-//personal or testing purposes (and because I'm lazy), I'm just putting each socket in their own threads
-class Connection
-{
- friend class RTMPServer;
-
- RTMP *rtmp;
- SOCKET socket;
- sockaddr_storage addr;
- char host[NI_MAXHOST];
- char service[NI_MAXSERV];
- bool bReadyForOutput;
- bool bDestroySocket;
- int streamChannel;
-
- RTMPServer *server;
-
- HANDLE hThread;
-
- static DWORD STDCALL SocketThread(Connection *connection);
-
- void SendMetaData();
-
-public:
- inline Connection(SOCKET socket, RTMPServer *server, sockaddr_storage *addrIn, char *host, char *service)
- {
- this->server = server;
- this->socket = socket;
- mcpy(&addr, addrIn, sizeof(addr));
- strcpy(this->host, host);
- strcpy(this->service, service);
-
- hThread = OSCreateThread((XTHREAD)SocketThread, (LPVOID)this);
- }
-
- inline ~Connection()
- {
- if(RTMP_IsConnected(rtmp) && bReadyForOutput)
- SendPlayStop(rtmp);
-
- if(hThread)
- {
- if(!bDestroySocket)
- {
- bDestroySocket = true;
- OSWaitForThread(hThread, NULL);
- }
-
- OSCloseThread(hThread);
- }
- }
-
- inline bool ReadyForOutput() const {return bReadyForOutput;}
- inline RTMP* GetRTMP() const {return rtmp;}
-
- int DoInvoke(RTMPPacket *packet, unsigned int offset);
-};
-
-
-class RTMPServer : public NetworkStream
-{
- friend class Connection;
-
- SOCKET serverSocket;
-
- bool bDestroySockets;
- List connections;
- HANDLE hListMutex, hListenThread;
-
-
- void ListenLoop()
- {
- traceIn(RTMPServer::ListenLoop);
-
- fd_set socketlist;
- timeval duration;
- sockaddr_storage addr;
- char host[NI_MAXHOST], service[NI_MAXSERV];
-
- duration.tv_sec = 0;
- duration.tv_usec = 20000;
-
- while(!bDestroySockets)
- {
- FD_ZERO(&socketlist);
- FD_SET(serverSocket, &socketlist);
-
- int ret = select(0, &socketlist, NULL, NULL, &duration);
- if(ret == -1)
- {
- int chi = WSAGetLastError();
- AppWarning(TEXT("Got error %d from a select on the server socket"), chi);
- }
- else if(ret != 0)
- {
- int addrLen = sizeof(addr);
- SOCKET clientSocket = accept(serverSocket, (sockaddr*)&addr, &addrLen);
- if(clientSocket == INVALID_SOCKET)
- continue;
-
- getnameinfo((struct sockaddr *)&addr, addrLen,
- host, sizeof(host),
- service, sizeof(service),
- NI_NUMERICHOST);
-
- OSEnterMutex(hListMutex);
- connections << new Connection(clientSocket, this, &addr, host, service);
- OSLeaveMutex(hListMutex);
- }
- }
-
- traceOut;
- }
-
- static DWORD STDCALL ListenThread(RTMPServer *server)
- {
- server->ListenLoop();
- return 0;
- }
-
-
-public:
- RTMPServer()
- {
- traceIn(RTMPServer::RTMPServer);
-
- //RTMP_LogSetCallback(rtmp_log_output);
- //RTMP_LogSetLevel(RTMP_LOGDEBUG);
-
- bDestroySockets = false;
-
- hListMutex = OSCreateMutex();
-
- addrinfo *ai_save, *local_ai, hint;
- zero(&hint, sizeof(hint));
-
- hint.ai_flags = AI_PASSIVE;
- hint.ai_family = AF_INET;//AF_UNSPEC;//
- hint.ai_socktype = SOCK_STREAM;
-
- DWORD ret = getaddrinfo(NULL, "1935", &hint, &local_ai);
- if(ret)
- return; //this should actually never happen
-
- ai_save = local_ai;
-
- serverSocket = -1;
- while(local_ai)
- {
- serverSocket = socket(local_ai->ai_family, local_ai->ai_socktype, local_ai->ai_protocol);
- int err = WSAGetLastError();
-
- if(serverSocket != INVALID_SOCKET)
- {
- if(bind(serverSocket, local_ai->ai_addr, (int)local_ai->ai_addrlen) == 0)
- break;
-
- closesocket(serverSocket);
- serverSocket = -1;
- }
-
- local_ai = local_ai->ai_next;
- }
-
- if(serverSocket != INVALID_SOCKET)
- {
- ret = listen(serverSocket, SOMAXCONN);
- if(ret)
- {
- ret = WSAGetLastError();
- AppWarning(TEXT("Failed to set up the server"));
- }
- else
- hListenThread = OSCreateThread((XTHREAD)ListenThread, (LPVOID)this);
- }
- else
- AppWarning(TEXT("Failed to get a server socket"));
-
- freeaddrinfo(ai_save);
-
- traceOut;
- }
-
- ~RTMPServer()
- {
- traceIn(RTMPServer::~RTMPServer);
-
- if(hListenThread)
- {
- bDestroySockets = true;
- OSWaitForThread(hListenThread, NULL);
- OSCloseThread(hListenThread);
- }
-
- if(hListMutex)
- OSCloseMutex(hListMutex);
-
- for(UINT i=0; i IAmNotHappyRightNow;
- IAmNotHappyRightNow.SetSize(RTMP_MAX_HEADER_SIZE);
- IAmNotHappyRightNow.AppendArray(data, size);
-
- packet.m_nBodySize = size;
- packet.m_body = (char*)IAmNotHappyRightNow.Array()+RTMP_MAX_HEADER_SIZE;
-
- OSEnterMutex(hListMutex);
- for(UINT i=0; iReadyForOutput())
- {
- if(!RTMP_SendPacket(connection->GetRTMP(), &packet, FALSE))
- {
- delete connection;
- connections.Remove(i--);
- }
- }
- }
- OSLeaveMutex(hListMutex);
-
- traceOut;
- }
-
- double GetPacketStrain() const {return 0.0;}
- QWORD GetCurrentSentBytes() {return 0;}
-};
-
-
-int Connection::DoInvoke(RTMPPacket *packet, unsigned int offset)
-{
- const char *body;
- unsigned int nBodySize;
- int ret = 0, nRes;
-
- body = packet->m_body + offset;
- nBodySize = packet->m_nBodySize - offset;
-
- if (body[0] != 0x02)
- return 0;
-
- //---------------------------------------------------
-
- AMFObject obj;
- nRes = AMF_Decode(&obj, body, nBodySize, FALSE);
- if (nRes < 0)
- return 0;
-
- //---------------------------------------------------
-
- AVal method;
- AMFProp_GetString(AMF_GetProp(&obj, NULL, 0), &method);
- double txn = AMFProp_GetNumber(AMF_GetProp(&obj, NULL, 1));
-
- if(AVMATCH(&method, &av_connect))
- {
- AMFObject connectObj;
- AVal pname, pval;
-
- AMFProp_GetObject(AMF_GetProp(&obj, NULL, 2), &connectObj);
- for(int i=0; iLink.app = pval;
- if (!rtmp->Link.app.av_val) rtmp->Link.app.av_val = "";
- }
- else if (AVMATCH(&pname, &av_flashVer))
- rtmp->Link.flashVer = pval;
- else if (AVMATCH(&pname, &av_swfUrl))
- rtmp->Link.swfUrl = pval;
- else if (AVMATCH(&pname, &av_tcUrl))
- rtmp->Link.tcUrl = pval;
- else if (AVMATCH(&pname, &av_pageUrl))
- rtmp->Link.pageUrl = pval;
- else if (AVMATCH(&pname, &av_audioCodecs))
- rtmp->m_fAudioCodecs = connectObj.o_props[i].p_vu.p_number;
- else if (AVMATCH(&pname, &av_videoCodecs))
- rtmp->m_fVideoCodecs = connectObj.o_props[i].p_vu.p_number;
- else if (AVMATCH(&pname, &av_objectEncoding))
- rtmp->m_fEncoding = connectObj.o_props[i].p_vu.p_number;
- }
-
- SendConnectResult(rtmp, txn);
- }
- else if(AVMATCH(&method, &av_createStream))
- SendResultNumber(rtmp, txn, 1.0);
- else if(AVMATCH(&method, &av_deleteStream))
- ret = 1;
- else if(AVMATCH(&method, &av_getStreamLength))
- SendResultNumber(rtmp, txn, 0.0);
- else if(AVMATCH(&method, &av_NetStream_Authenticate_UsherToken))
- {
- AVal usherToken;
- AMFProp_GetString(AMF_GetProp(&obj, NULL, 3), &usherToken);
- AVreplace(&usherToken, &av_dquote, &av_escdquote);
- rtmp->Link.usherToken = usherToken;
- }
- else if(AVMATCH(&method, &av_play))
- {
- RTMP_SendCtrl(rtmp, 0, 1, 0);
- SendPlayStart(rtmp);
-
- streamChannel = packet->m_nInfoField2;
-
- SendMetaData();
-
- //-----------------------------------------------------
- // send video headers
-
- DataPacket headers;
-
- App->GetVideoHeaders(headers);
-
- RTMPPacket headerPacket;
- headerPacket.m_nChannel = 0x04; // source channel (invoke)
- headerPacket.m_headerType = RTMP_PACKET_SIZE_LARGE;
- headerPacket.m_packetType = RTMP_PACKET_TYPE_VIDEO;
- headerPacket.m_nTimeStamp = 0;
- headerPacket.m_nInfoField2 = 1;
- headerPacket.m_hasAbsTimestamp = 1;
-
- List IAmNotHappyRightNow;
- IAmNotHappyRightNow.SetSize(RTMP_MAX_HEADER_SIZE);
- IAmNotHappyRightNow.AppendArray(headers.lpPacket, headers.size);
-
- headerPacket.m_nBodySize = headers.size;
- headerPacket.m_body = (char*)IAmNotHappyRightNow.Array()+RTMP_MAX_HEADER_SIZE;
-
- RTMP_SendPacket(rtmp, &headerPacket, FALSE);
-
- //-----------------------------------------------------
- // send audio headers
-
- headerPacket.m_nChannel = 0x05; // audio channel
- headerPacket.m_packetType = RTMP_PACKET_TYPE_AUDIO;
-
- App->GetAudioHeaders(headers);
-
- IAmNotHappyRightNow.SetSize(RTMP_MAX_HEADER_SIZE);
- IAmNotHappyRightNow.AppendArray(headers.lpPacket, headers.size);
-
- headerPacket.m_nBodySize = headers.size;
- headerPacket.m_body = (char*)IAmNotHappyRightNow.Array()+RTMP_MAX_HEADER_SIZE;
-
- RTMP_SendPacket(rtmp, &headerPacket, FALSE);
-
- //-----------------------------------------------------
-
- bReadyForOutput = true;
- }
- else if (AVMATCH(&method, &av_FCSubscribe))
- {
-
- }
-
- return ret;
-}
-
-void Connection::SendMetaData()
-{
- RTMPPacket packet;
-
- //---------------------------------------------------
-
- char pbuf[1024], *pend = pbuf+sizeof(pbuf);
-
- packet.m_nChannel = 0x03; // control channel (invoke)
- packet.m_headerType = RTMP_PACKET_SIZE_MEDIUM;
- packet.m_packetType = RTMP_PACKET_TYPE_INFO;
- packet.m_nTimeStamp = 0;
- packet.m_nInfoField2 = 0;
- packet.m_hasAbsTimestamp = 0;
- packet.m_body = pbuf + RTMP_MAX_HEADER_SIZE;
-
- char *enc = packet.m_body;
- enc = AMF_EncodeString(enc, pend, &av_onMetaData);
- enc = App->EncMetaData(enc, pend);
-
- packet.m_nBodySize = enc - packet.m_body;
- RTMP_SendPacket(rtmp, &packet, FALSE);
-
- bReadyForOutput = true;
-}
-
-
-DWORD STDCALL Connection::SocketThread(Connection *connection)
-{
- fd_set socketlist;
- timeval duration;
- RTMP *clientRtmp;
- RTMPPacket packet;
-
- clientRtmp = RTMP_Alloc();
- RTMP_Init(clientRtmp);
- zero(&packet, sizeof(packet));
-
- //-----------------------------------------------
-
- duration.tv_sec = 0;
- duration.tv_usec = 20000;
-
- FD_ZERO(&socketlist);
- FD_SET(connection->socket, &socketlist);
-
- if(select(0, &socketlist, NULL, NULL, &duration) <= 0)
- {
- DWORD chi = WSAGetLastError();
- return 0;
- }
- else
- {
- clientRtmp->m_sb.sb_socket = connection->socket;
-
- connection->rtmp = clientRtmp;
-
- if(!RTMP_Serve(clientRtmp))
- {
- RTMP_Close(clientRtmp);
- RTMP_Free(clientRtmp);
- return 0;
- }
- }
-
- //-----------------------------------------------
-
- while(!connection->server->bDestroySockets && !connection->bDestroySocket && RTMP_IsConnected(clientRtmp) && RTMP_ReadPacket(clientRtmp, &packet))
- {
- if(!RTMPPacket_IsReady(&packet))
- continue;
-
- switch(packet.m_packetType)
- {
- case RTMP_PACKET_TYPE_CHUNK_SIZE:
- //ChangeChunkSize(?);
- break;
-
- case RTMP_PACKET_TYPE_CONTROL:
- //SetControl(?);
- break;
-
- case RTMP_PACKET_TYPE_SERVER_BW:
- //ServerBandwidth(?);
- break;
- case RTMP_PACKET_TYPE_CLIENT_BW:
- //ClientBandwidth(?);
- break;
-
- case RTMP_PACKET_TYPE_FLEX_MESSAGE:
- //Invoke?
- break;
-
- case RTMP_PACKET_TYPE_INFO:
- //meta data thingy, shouldn't even get it, at least I think
- break;
-
- case RTMP_PACKET_TYPE_INVOKE:
- if(connection->DoInvoke(&packet, 0))
- connection->bDestroySocket = true;
- break;
- }
-
- RTMPPacket_Free(&packet);
- }
-
- if(!connection->bDestroySocket) //if we received a request to stop stream, automatically delete self
- {
- OSEnterMutex(connection->server->hListMutex);
-
- connection->bDestroySocket = true;
- connection->server->connections.RemoveItem(connection);
- delete connection;
-
- OSLeaveMutex(connection->server->hListMutex);
- }
-
- RTMP_Close(clientRtmp);
- RTMP_Free(clientRtmp);
-
- connection->rtmp = NULL;
-
- return 0;
-}
-
-
-NetworkStream* CreateRTMPServer()
-{
- return new RTMPServer;
-}
-
diff --git a/Source/RTMPStuff.cpp b/Source/RTMPStuff.cpp
index 4018384f..2163d173 100644
--- a/Source/RTMPStuff.cpp
+++ b/Source/RTMPStuff.cpp
@@ -222,23 +222,32 @@ char* OBS::EncMetaData(char *enc, char *pend)
int audioBitRate = GetAudioEncoder()->GetBitRate();
CTSTR lpAudioCodec = GetAudioEncoder()->GetCodec();
+ double audioCodecID;
const AVal *av_codecFourCC;
+
if(scmpi(lpAudioCodec, TEXT("AAC")) == 0)
+ {
av_codecFourCC = &av_mp4a;
+ audioCodecID = 10.0;
+ }
else
+ {
av_codecFourCC = &av_mp3;
+ audioCodecID = 2.0;
+ }
*enc++ = AMF_OBJECT;
- enc = AMF_EncodeNamedNumber(enc, pend, &av_duration, 0.0);
+ enc = AMF_EncodeNamedNumber(enc, pend, &av_duration, 0.0);
+ enc = AMF_EncodeNamedNumber(enc, pend, &av_fileSize, 0.0);
enc = AMF_EncodeNamedNumber(enc, pend, &av_width, double(outputCX));
enc = AMF_EncodeNamedNumber(enc, pend, &av_height, double(outputCY));
- enc = AMF_EncodeNamedString(enc, pend, &av_videocodecid, &av_avc1);//7.0);//
+ enc = AMF_EncodeNamedNumber(enc, pend, &av_videocodecid, 7.0);//&av_avc1);//
enc = AMF_EncodeNamedNumber(enc, pend, &av_videodatarate, double(maxBitRate));
enc = AMF_EncodeNamedNumber(enc, pend, &av_framerate, double(fps));
- enc = AMF_EncodeNamedString(enc, pend, &av_audiocodecid, av_codecFourCC);//0.0);//&justdiealready);//&av_mp4a);//2.0);//
+ enc = AMF_EncodeNamedNumber(enc, pend, &av_audiocodecid, audioCodecID);//av_codecFourCC);//
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiodatarate, double(audioBitRate)); //ex. 128kb\s
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiosamplerate, 44100.0);
@@ -246,7 +255,6 @@ char* OBS::EncMetaData(char *enc, char *pend)
enc = AMF_EncodeNamedNumber(enc, pend, &av_audiochannels, 2.0);
enc = AMF_EncodeNamedBoolean(enc, pend, &av_stereo, true);
enc = AMF_EncodeNamedString(enc, pend, &av_encoder, &av_OBSVersion);
- enc = AMF_EncodeNamedNumber(enc, pend, &av_fileSize, 0.0);
*enc++ = 0;
*enc++ = 0;
*enc++ = AMF_OBJECT_END;
diff --git a/Source/Settings.cpp b/Source/Settings.cpp
index dfe8d66e..0282acfa 100644
--- a/Source/Settings.cpp
+++ b/Source/Settings.cpp
@@ -26,7 +26,7 @@ enum SettingsSelection
Settings_Publish,
Settings_Video,
Settings_Audio,
- Settings_Hotkeys,
+ Settings_Advanced,
};
BOOL CALLBACK MonitorInfoEnumProc(HMONITOR hMonitor, HDC hdcMonitor, LPRECT lprcMonitor, List &monitors);
@@ -545,8 +545,7 @@ INT_PTR CALLBACK OBS::PublishSettingsProc(HWND hwnd, UINT message, WPARAM wParam
hwndTemp = GetDlgItem(hwnd, IDC_MODE);
SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish.Mode.LiveStream"));
- SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish.Mode.Serve"));
- //SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish.Mode.SaveToFile"));
+ SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish.Mode.FileOnly"));
int mode = LoadSettingComboInt(hwndTemp, TEXT("Publish"), TEXT("Mode"), 0, 2);
@@ -634,6 +633,30 @@ INT_PTR CALLBACK OBS::PublishSettingsProc(HWND hwnd, UINT message, WPARAM wParam
if(mode != 0) ShowWindow(hwndTemp, SW_HIDE);
+ //--------------------------------------------
+
+ hwndTemp = GetDlgItem(hwnd, IDC_AUTORECONNECT);
+
+ BOOL bAutoReconnect = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnect"));
+ SendMessage(hwndTemp, BM_SETCHECK, bAutoReconnect ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ if(mode != 0) ShowWindow(hwndTemp, SW_HIDE);
+
+ hwndTemp = GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT);
+ EnableWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), bAutoReconnect);
+ EnableWindow(hwndTemp, bAutoReconnect);
+
+ int retryTime = AppConfig->GetInt(TEXT("Publish"), TEXT("AutoReconnectTimeout"), 10);
+ if(retryTime > 60) retryTime = 60;
+ else if(retryTime < 5) retryTime = 5;
+
+ SendMessage(hwndTemp, UDM_SETRANGE32, 5, 60);
+ SendMessage(hwndTemp, UDM_SETPOS32, 0, retryTime);
+
+ if(mode != 0) ShowWindow(hwndTemp, SW_HIDE);
+
+ //--------------------------------------------
+
if(mode != 0)
{
ShowWindow(GetDlgItem(hwnd, IDC_SERVICE_STATIC), SW_HIDE);
@@ -641,14 +664,42 @@ INT_PTR CALLBACK OBS::PublishSettingsProc(HWND hwnd, UINT message, WPARAM wParam
ShowWindow(GetDlgItem(hwnd, IDC_PLAYPATH_STATIC), SW_HIDE);
ShowWindow(GetDlgItem(hwnd, IDC_CHANNELNAME_STATIC), SW_HIDE);
ShowWindow(GetDlgItem(hwnd, IDC_SERVER_STATIC), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_STATIC), SW_HIDE);
+ ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), SW_HIDE);
}
+ //--------------------------------------------
+
+ BOOL bSaveToFile = AppConfig->GetInt(TEXT("Publish"), TEXT("SaveToFile"));
+ SendMessage(GetDlgItem(hwnd, IDC_SAVETOFILE), BM_SETCHECK, bSaveToFile ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ CTSTR lpSavePath = AppConfig->GetStringPtr(TEXT("Publish"), TEXT("SavePath"));
+ SetWindowText(GetDlgItem(hwnd, IDC_SAVEPATH), lpSavePath);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_SAVEPATH), bSaveToFile);
+ EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), bSaveToFile);
+
+ //--------------------------------------------
+
ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_HIDE);
App->SetChangedSettings(false);
return TRUE;
}
+ case WM_NOTIFY:
+ {
+ NMHDR *nmHdr = (NMHDR*)lParam;
+
+ if(nmHdr->idFrom == IDC_AUTORECONNECT_TIMEOUT)
+ {
+ if(nmHdr->code == UDN_DELTAPOS)
+ App->SetChangedSettings(true);
+ }
+
+ break;
+ }
+
case WM_COMMAND:
{
bool bDataChanged = false;
@@ -685,6 +736,10 @@ INT_PTR CALLBACK OBS::PublishSettingsProc(HWND hwnd, UINT message, WPARAM wParam
ShowWindow(GetDlgItem(hwnd, IDC_PLAYPATH_STATIC), swShowControls);
ShowWindow(GetDlgItem(hwnd, IDC_CHANNELNAME_STATIC), swShowControls);
ShowWindow(GetDlgItem(hwnd, IDC_SERVER_STATIC), swShowControls);
+ ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT), swShowControls);
+ ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT), swShowControls);
+ ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_STATIC), swShowControls);
+ ShowWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), swShowControls);
bDataChanged = true;
break;
@@ -754,10 +809,94 @@ INT_PTR CALLBACK OBS::PublishSettingsProc(HWND hwnd, UINT message, WPARAM wParam
}
break;
- //case IDC_USERNAME:
+ case IDC_AUTORECONNECT:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL bAutoReconnect = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+
+ EnableWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT), bAutoReconnect);
+ EnableWindow(GetDlgItem(hwnd, IDC_AUTORECONNECT_TIMEOUT_EDIT), bAutoReconnect);
+
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_AUTORECONNECT_TIMEOUT_EDIT:
+ if(HIWORD(wParam) == EN_CHANGE)
+ App->SetChangedSettings(true);
+ break;
+
+ case IDC_SAVETOFILE:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL bSaveToFile = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+
+ EnableWindow(GetDlgItem(hwnd, IDC_SAVEPATH), bSaveToFile);
+ EnableWindow(GetDlgItem(hwnd, IDC_BROWSE), bSaveToFile);
+
+ bDataChanged = true;
+ }
+ break;
+
+ case IDC_BROWSE:
+ {
+ TCHAR lpFile[512];
+ OPENFILENAME ofn;
+ zero(&ofn, sizeof(ofn));
+ ofn.lStructSize = sizeof(ofn);
+ ofn.hwndOwner = hwnd;
+ ofn.lpstrFile = lpFile;
+ ofn.nMaxFile = 511;
+ ofn.lpstrFile[0] = 0;
+ ofn.lpstrFilter = TEXT("MP4 File (*.mp4)\0*.mp4\0Flash Video File (*.flv)\0*.flv\0");
+ ofn.lpstrFileTitle = NULL;
+ ofn.nMaxFileTitle = 0;
+ ofn.nFilterIndex = 1;
+ ofn.lpstrInitialDir = AppConfig->GetStringPtr(TEXT("Publish"), TEXT("LastSaveDir"));
+
+ ofn.Flags = OFN_PATHMUSTEXIST;
+
+ TCHAR curDirectory[512];
+ GetCurrentDirectory(511, curDirectory);
+
+ BOOL bChoseFile = GetSaveFileName(&ofn);
+ SetCurrentDirectory(curDirectory);
+
+ if(bChoseFile)
+ {
+ String strFile = lpFile;
+ strFile.FindReplace(TEXT("\\"), TEXT("/"));
+
+ String strExtension = GetPathExtension(strFile);
+ if(strExtension.IsEmpty() || (!strExtension.CompareI(TEXT("flv")) && /*!strExtension.CompareI(TEXT("avi")) &&*/ !strExtension.CompareI(TEXT("mp4"))))
+ {
+ switch(ofn.nFilterIndex)
+ {
+ case 1:
+ strFile << TEXT(".mp4"); break;
+ case 2:
+ strFile << TEXT(".flv"); break;
+ /*case 3:
+ strFile << TEXT(".avi"); break;*/
+ }
+ }
+
+ String strFilePath = GetPathDirectory(strFile).FindReplace(TEXT("/"), TEXT("\\")) << TEXT("\\");
+ AppConfig->SetString(TEXT("Publish"), TEXT("LastSaveDir"), strFilePath);
+
+ strFile.FindReplace(TEXT("/"), TEXT("\\"));
+ SetWindowText(GetDlgItem(hwnd, IDC_SAVEPATH), strFile);
+ bDataChanged = true;
+ }
+
+ break;
+ }
+
+ //case IDC_USERNAME:
case IDC_PLAYPATH:
case IDC_CHANNELNAME:
case IDC_SERVEREDIT:
+ case IDC_SAVEPATH:
if(HIWORD(wParam) == EN_CHANGE)
bDataChanged = true;
break;
@@ -945,10 +1084,10 @@ INT_PTR CALLBACK OBS::VideoSettingsProc(HWND hwnd, UINT message, WPARAM wParam,
//--------------------------------------------
hwndTemp = GetDlgItem(hwnd, IDC_FPS);
- SendMessage(hwndTemp, UDM_SETRANGE32, 5, 60);
+ SendMessage(hwndTemp, UDM_SETRANGE32, 15, 60);
int fps = AppConfig->GetInt(TEXT("Video"), TEXT("FPS"), 25);
- if(!AppConfig->HasKey(TEXT("Video"), TEXT("FPS")) || fps < 5 || fps > 60)
+ if(!AppConfig->HasKey(TEXT("Video"), TEXT("FPS")) || fps < 15 || fps > 60)
{
AppConfig->SetInt(TEXT("Video"), TEXT("FPS"), 25);
fps = 25;
@@ -1115,6 +1254,28 @@ INT_PTR CALLBACK OBS::AudioSettingsProc(HWND hwnd, UINT message, WPARAM wParam,
SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)audioDevices);
+ //--------------------------------------------
+
+ BOOL bPushToTalk = AppConfig->GetInt(TEXT("Audio"), TEXT("UsePushToTalk"));
+ SendMessage(GetDlgItem(hwnd, IDC_PUSHTOTALK), BM_SETCHECK, bPushToTalk ? BST_CHECKED : BST_UNCHECKED, 0);
+ EnableWindow(GetDlgItem(hwnd, IDC_PUSHTOTALKHOTKEY), bPushToTalk);
+ EnableWindow(GetDlgItem(hwnd, IDC_CLEARPUSHTOTALK), bPushToTalk);
+
+ DWORD hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("PushToTalkHotkey"));
+ SendMessage(GetDlgItem(hwnd, IDC_PUSHTOTALKHOTKEY), HKM_SETHOTKEY, hotkey, 0);
+
+ //--------------------------------------------
+
+ hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("MuteMicHotkey"));
+ SendMessage(GetDlgItem(hwnd, IDC_MUTEMICHOTKEY), HKM_SETHOTKEY, hotkey, 0);
+
+ //--------------------------------------------
+
+ hotkey = AppConfig->GetInt(TEXT("Audio"), TEXT("MuteDesktopHotkey"));
+ SendMessage(GetDlgItem(hwnd, IDC_MUTEDESKTOPHOTKEY), HKM_SETHOTKEY, hotkey, 0);
+
+ //--------------------------------------------
+
App->SetChangedSettings(false);
return TRUE;
}
@@ -1131,6 +1292,47 @@ INT_PTR CALLBACK OBS::AudioSettingsProc(HWND hwnd, UINT message, WPARAM wParam,
switch(LOWORD(wParam))
{
+ case IDC_PUSHTOTALK:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL bUsePushToTalk = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ EnableWindow(GetDlgItem(hwnd, IDC_PUSHTOTALKHOTKEY), bUsePushToTalk);
+ EnableWindow(GetDlgItem(hwnd, IDC_CLEARPUSHTOTALK), bUsePushToTalk);
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_PUSHTOTALKHOTKEY:
+ case IDC_MUTEMICHOTKEY:
+ case IDC_MUTEDESKTOPHOTKEY:
+ if(HIWORD(wParam) == EN_CHANGE)
+ App->SetChangedSettings(true);
+ break;
+
+ case IDC_CLEARPUSHTOTALK:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ SendMessage(GetDlgItem(hwnd, IDC_PUSHTOTALKHOTKEY), HKM_SETHOTKEY, 0, 0);
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_CLEARMUTEMIC:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ SendMessage(GetDlgItem(hwnd, IDC_MUTEMICHOTKEY), HKM_SETHOTKEY, 0, 0);
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_CLEARMUTEDESKTOP:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ SendMessage(GetDlgItem(hwnd, IDC_MUTEDESKTOPHOTKEY), HKM_SETHOTKEY, 0, 0);
+ App->SetChangedSettings(true);
+ }
+ break;
+
case IDC_MICDEVICES:
if(HIWORD(wParam) == CBN_SELCHANGE)
bDataChanged = true;
@@ -1148,14 +1350,120 @@ INT_PTR CALLBACK OBS::AudioSettingsProc(HWND hwnd, UINT message, WPARAM wParam,
return FALSE;
}
-INT_PTR CALLBACK OBS::HotkeysSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+INT_PTR CALLBACK OBS::AdvancedSettingsProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
switch(message)
{
case WM_INITDIALOG:
- LocalizeWindow(hwnd);
- App->SetChangedSettings(false);
- return TRUE;
+ {
+ LocalizeWindow(hwnd);
+
+ //--------------------------------------------
+
+ HWND hwndToolTip = CreateWindowEx(NULL, TOOLTIPS_CLASS, NULL, WS_POPUP|TTS_NOPREFIX|TTS_ALWAYSTIP,
+ CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT, CW_USEDEFAULT,
+ hwnd, NULL, hinstMain, NULL);
+
+ TOOLINFO ti;
+ zero(&ti, sizeof(ti));
+ ti.cbSize = sizeof(ti);
+ ti.uFlags = TTF_SUBCLASS|TTF_IDISHWND;
+ ti.hwnd = hwnd;
+
+ SendMessage(hwndToolTip, TTM_SETMAXTIPWIDTH, 0, 500);
+ SendMessage(hwndToolTip, TTM_SETDELAYTIME, TTDT_AUTOPOP, 14000);
+
+ //------------------------------------
+
+ bool bUseCustomX264Settings = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseCustomSettings")) != 0;
+ String strX264Settings = AppConfig->GetString(TEXT("Video Encoding"), TEXT("CustomSettings"));
+
+ SendMessage(GetDlgItem(hwnd, IDC_USEVIDEOENCODERSETTINGS), BM_SETCHECK, bUseCustomX264Settings ? BST_CHECKED : BST_UNCHECKED, 0);
+ SetWindowText(GetDlgItem(hwnd, IDC_VIDEOENCODERSETTINGS), strX264Settings);
+
+ ti.lpszText = (LPWSTR)Str("Settings.Advanced.VideoEncoderSettingsTooltip");
+ ti.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_VIDEOENCODERSETTINGS);
+ SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ ti.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_USEVIDEOENCODERSETTINGS);
+ SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ EnableWindow(GetDlgItem(hwnd, IDC_VIDEOENCODERSETTINGS), bUseCustomX264Settings);
+
+ //------------------------------------
+
+ bool bUseVideoSyncFix = AppConfig->GetInt(TEXT("Video Encoding"), TEXT("UseSyncFix")) != 0;
+ SendMessage(GetDlgItem(hwnd, IDC_USESYNCFIX), BM_SETCHECK, bUseVideoSyncFix ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ ti.lpszText = (LPWSTR)Str("Settings.Advanced.UseSyncFixTooltip");
+ ti.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_USESYNCFIX);
+ SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ //------------------------------------
+
+ BOOL bUseSendBuffer = AppConfig->GetInt(TEXT("Publish"), TEXT("UseSendBuffer"), 1) != 0;
+ SendMessage(GetDlgItem(hwnd, IDC_USESENDBUFFER), BM_SETCHECK, bUseSendBuffer ? BST_CHECKED : BST_UNCHECKED, 0);
+
+ HWND hwndTemp = GetDlgItem(hwnd, IDC_SENDBUFFERSIZE);
+ EnableWindow(hwndTemp, bUseSendBuffer);
+ SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)TEXT("32768"));
+ SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)TEXT("16384"));
+ SendMessage(hwndTemp, CB_ADDSTRING, 0, (LPARAM)TEXT("8192"));
+
+ LoadSettingTextComboString(hwndTemp, TEXT("Publish"), TEXT("SendBufferSize"), TEXT("32768"));
+
+ ti.lpszText = (LPWSTR)Str("Settings.Advanced.UseSendBufferTooltip");
+ ti.uId = (UINT_PTR)GetDlgItem(hwnd, IDC_USESENDBUFFER);
+ SendMessage(hwndToolTip, TTM_ADDTOOL, 0, (LPARAM)&ti);
+
+ //------------------------------------
+
+ App->SetChangedSettings(false);
+ return TRUE;
+ }
+
+ case WM_COMMAND:
+ switch(LOWORD(wParam))
+ {
+ case IDC_USEVIDEOENCODERSETTINGS:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL bUseVideoSyncFix = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ EnableWindow(GetDlgItem(hwnd, IDC_VIDEOENCODERSETTINGS), bUseVideoSyncFix);
+
+ ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW);
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_USESENDBUFFER:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ BOOL bUseSendBuffer = SendMessage((HWND)lParam, BM_GETCHECK, 0, 0) == BST_CHECKED;
+ EnableWindow(GetDlgItem(hwnd, IDC_SENDBUFFERSIZE), bUseSendBuffer);
+
+ ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW);
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_SENDBUFFERSIZE:
+ if(HIWORD(wParam) == CBN_SELCHANGE)
+ {
+ ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW);
+ App->SetChangedSettings(true);
+ }
+ break;
+
+ case IDC_USESYNCFIX:
+ if(HIWORD(wParam) == BN_CLICKED)
+ {
+ ShowWindow(GetDlgItem(hwnd, IDC_INFO), SW_SHOW);
+ App->SetChangedSettings(true);
+ }
+ break;
+ }
+
}
return FALSE;
}
@@ -1226,6 +1534,30 @@ void OBS::ApplySettings()
strTemp = GetCBText(GetDlgItem(hwndCurrentSettings, IDC_SERVERLIST));
AppConfig->SetString(TEXT("Publish"), TEXT("Server"), strTemp);
}
+
+ //------------------------------------------
+
+ App->bAutoReconnect = SendMessage(GetDlgItem(hwndCurrentSettings, IDC_AUTORECONNECT), BM_GETCHECK, 0, 0) == BST_CHECKED;
+
+ BOOL bError = FALSE;
+ App->reconnectTimeout = (UINT)SendMessage(GetDlgItem(hwndCurrentSettings, IDC_AUTORECONNECT_TIMEOUT), UDM_GETPOS32, 0, (LPARAM)&bError);
+ if(bError)
+ App->reconnectTimeout = 5;
+
+ AppConfig->SetInt(TEXT("Publish"), TEXT("AutoReconnect"), App->bAutoReconnect);
+ AppConfig->SetInt(TEXT("Publish"), TEXT("AutoReconnectTimeout"), App->reconnectTimeout);
+
+ //------------------------------------------
+
+ String strSavePath = GetEditText(GetDlgItem(hwndCurrentSettings, IDC_SAVEPATH));
+ BOOL bSaveToFile = SendMessage(GetDlgItem(hwndCurrentSettings, IDC_SAVETOFILE), BM_GETCHECK, 0, 0) != BST_UNCHECKED;
+
+ if(!strSavePath.IsValid())
+ bSaveToFile = FALSE;
+
+ AppConfig->SetInt (TEXT("Publish"), TEXT("SaveToFile"), bSaveToFile);
+ AppConfig->SetString(TEXT("Publish"), TEXT("SavePath"), strSavePath);
+
break;
}
@@ -1277,11 +1609,48 @@ void OBS::ApplySettings()
EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), FALSE);
else
EnableWindow(GetDlgItem(hwndMain, ID_MICVOLUME), TRUE);
+
+ //------------------------------------
+
+ BOOL bUsePushToTalk = SendMessage(GetDlgItem(hwndCurrentSettings, IDC_PUSHTOTALK), BM_GETCHECK, 0, 0) == BST_CHECKED;
+ DWORD hotkey = (DWORD)SendMessage(GetDlgItem(hwndCurrentSettings, IDC_PUSHTOTALKHOTKEY), HKM_GETHOTKEY, 0, 0);
+
+ AppConfig->SetInt(TEXT("Audio"), TEXT("UsePushToTalk"), bUsePushToTalk);
+ AppConfig->SetInt(TEXT("Audio"), TEXT("PushToTalkHotkey"), hotkey);
+
+ //------------------------------------
+
+ hotkey = (DWORD)SendMessage(GetDlgItem(hwndCurrentSettings, IDC_MUTEMICHOTKEY), HKM_GETHOTKEY, 0, 0);
+ AppConfig->SetInt(TEXT("Audio"), TEXT("MuteMicHotkey"), hotkey);
+
+ //------------------------------------
+
+ hotkey = (DWORD)SendMessage(GetDlgItem(hwndCurrentSettings, IDC_MUTEDESKTOPHOTKEY), HKM_GETHOTKEY, 0, 0);
+ AppConfig->SetInt(TEXT("Audio"), TEXT("MuteDesktopHotkey"), hotkey);
+
break;
}
- case Settings_Hotkeys:
+ case Settings_Advanced:
{
+ BOOL bUseCustomX264Settings = SendMessage(GetDlgItem(hwndCurrentSettings, IDC_USEVIDEOENCODERSETTINGS), BM_GETCHECK, 0, 0) == BST_CHECKED;
+ String strCustomX264Settings = GetEditText(GetDlgItem(hwndCurrentSettings, IDC_VIDEOENCODERSETTINGS));
+
+ AppConfig->SetInt (TEXT("Video Encoding"), TEXT("UseCustomSettings"), bUseCustomX264Settings);
+ AppConfig->SetString(TEXT("Video Encoding"), TEXT("CustomSettings"), strCustomX264Settings);
+
+ //--------------------------------------------------
+
+ BOOL bUseVideoSyncFix = SendMessage(GetDlgItem(hwndCurrentSettings, IDC_USESYNCFIX), BM_GETCHECK, 0, 0) == BST_CHECKED;
+ AppConfig->SetInt (TEXT("Video Encoding"), TEXT("UseSyncFix"), bUseVideoSyncFix);
+
+ //--------------------------------------------------
+
+ BOOL bUseSendBuffer = SendMessage(GetDlgItem(hwndCurrentSettings, IDC_USESENDBUFFER), BM_GETCHECK, 0, 0) == BST_CHECKED;
+ String strSendBufferSize = GetCBText(GetDlgItem(hwndCurrentSettings, IDC_USESENDBUFFER));
+
+ AppConfig->SetInt (TEXT("Publish"), TEXT("UseSendBuffer"), bUseSendBuffer);
+ AppConfig->SetString(TEXT("Publish"), TEXT("SendBufferSize"), strSendBufferSize);
break;
}
}
@@ -1306,7 +1675,7 @@ INT_PTR CALLBACK OBS::SettingsDialogProc(HWND hwnd, UINT message, WPARAM wParam,
SendMessage(GetDlgItem(hwnd, IDC_SETTINGSLIST), LB_ADDSTRING, 0, (LPARAM)Str("Settings.Publish"));
SendMessage(GetDlgItem(hwnd, IDC_SETTINGSLIST), LB_ADDSTRING, 0, (LPARAM)Str("Settings.Video"));
SendMessage(GetDlgItem(hwnd, IDC_SETTINGSLIST), LB_ADDSTRING, 0, (LPARAM)Str("Settings.Audio"));
- //SendMessage(GetDlgItem(hwnd, IDC_SETTINGSLIST), LB_ADDSTRING, 0, (LPARAM)Str("Settings.Hotkeys"));
+ SendMessage(GetDlgItem(hwnd, IDC_SETTINGSLIST), LB_ADDSTRING, 0, (LPARAM)Str("Settings.Advanced"));
RECT subDialogRect;
GetWindowRect(GetDlgItem(hwnd, IDC_SUBDIALOG), &subDialogRect);
@@ -1372,7 +1741,8 @@ INT_PTR CALLBACK OBS::SettingsDialogProc(HWND hwnd, UINT message, WPARAM wParam,
case Settings_Audio:
App->hwndCurrentSettings = CreateDialog(hinstMain, MAKEINTRESOURCE(IDD_SETTINGS_AUDIO), hwnd, (DLGPROC)OBS::AudioSettingsProc);
break;
- case Settings_Hotkeys:
+ case Settings_Advanced:
+ App->hwndCurrentSettings = CreateDialog(hinstMain, MAKEINTRESOURCE(IDD_SETTINGS_ADVANCED), hwnd, (DLGPROC)OBS::AdvancedSettingsProc);
break;
}
diff --git a/Source/VolumeControl.cpp b/Source/VolumeControl.cpp
index e3e81fe2..a4b6fc7d 100644
--- a/Source/VolumeControl.cpp
+++ b/Source/VolumeControl.cpp
@@ -130,17 +130,25 @@ LRESULT CALLBACK VolumeControlProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
short x = short(LOWORD(lParam));
short y = short(HIWORD(lParam));
+ UINT id = (UINT)GetWindowLongPtr(hwnd, GWLP_ID);
+
if(message == WM_LBUTTONDOWN && !control->bDisabled)
{
if(control->cy == 32 && x >= (control->cx-32))
{
if(control->curVolume < EPSILON)
+ {
+ if(control->lastUnmutedVol < EPSILON)
+ control->lastUnmutedVol = 1.0f;
control->curVolume = control->lastUnmutedVol;
+ }
else
{
control->lastUnmutedVol = control->curVolume;
control->curVolume = 0.0f;
}
+
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_FINALVALUE), (LPARAM)hwnd);
}
else
{
@@ -153,14 +161,13 @@ LRESULT CALLBACK VolumeControlProc(HWND hwnd, UINT message, WPARAM wParam, LPARA
int cxAdjust = control->cx;
if(control->bDrawIcon) cxAdjust -= 32;
control->curVolume = float(x) / cxAdjust;
+
+ SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_ADJUSTING), (LPARAM)hwnd);
}
HDC hDC = GetDC(hwnd);
control->DrawVolumeControl(hDC);
ReleaseDC(hwnd, hDC);
-
- UINT id = (UINT)GetWindowLongPtr(hwnd, GWLP_ID);
- SendMessage(GetParent(hwnd), WM_COMMAND, MAKEWPARAM(id, VOLN_ADJUSTING), (LPARAM)hwnd);
}
else if(control->bHasCapture)
{
diff --git a/Source/WindowStuff.cpp b/Source/WindowStuff.cpp
index cc182797..747c2719 100644
--- a/Source/WindowStuff.cpp
+++ b/Source/WindowStuff.cpp
@@ -1339,6 +1339,93 @@ INT_PTR CALLBACK OBS::GlobalSourcesProc(HWND hwnd, UINT message, WPARAM wParam,
//----------------------------
+struct ReconnectInfo
+{
+ UINT_PTR timerID;
+ UINT secondsLeft;
+};
+
+INT_PTR CALLBACK OBS::ReconnectDialogProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
+{
+ switch(message)
+ {
+ case WM_INITDIALOG:
+ {
+ LocalizeWindow(hwnd);
+
+ ReconnectInfo *ri = new ReconnectInfo;
+ ri->secondsLeft = App->reconnectTimeout;
+ ri->timerID = 1;
+
+ if(!SetTimer(hwnd, 1, 1000, NULL))
+ {
+ App->bReconnecting = false;
+ EndDialog(hwnd, IDCANCEL);
+ delete ri;
+ }
+
+ String strText;
+ if(App->bReconnecting)
+ strText << Str("Reconnecting.Retrying") << UIntString(ri->secondsLeft);
+ else
+ strText << Str("Reconnecting") << UIntString(ri->secondsLeft);
+
+ SetWindowText(GetDlgItem(hwnd, IDC_RECONNECTING), strText);
+
+ SetWindowLongPtr(hwnd, DWLP_USER, (LONG_PTR)ri);
+ return TRUE;
+ }
+
+ case WM_TIMER:
+ {
+ ReconnectInfo *ri = (ReconnectInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
+ if(wParam != 1)
+ break;
+
+ if(!--ri->secondsLeft)
+ {
+ SendMessage(hwndMain, OBS_RECONNECT, 0, 0);
+ EndDialog(hwnd, IDOK);
+ }
+ else
+ {
+ String strText;
+ if(App->bReconnecting)
+ strText << Str("Reconnecting.Retrying") << UIntString(ri->secondsLeft);
+ else
+ strText << Str("Reconnecting") << UIntString(ri->secondsLeft);
+
+ SetWindowText(GetDlgItem(hwnd, IDC_RECONNECTING), strText);
+ }
+ break;
+ }
+
+ case WM_COMMAND:
+ if(LOWORD(wParam) == IDCANCEL)
+ {
+ App->bReconnecting = false;
+ EndDialog(hwnd, IDCANCEL);
+ }
+ break;
+
+ case WM_CLOSE:
+ App->bReconnecting = false;
+ EndDialog(hwnd, IDCANCEL);
+ break;
+
+ case WM_DESTROY:
+ {
+ ReconnectInfo *ri = (ReconnectInfo*)GetWindowLongPtr(hwnd, DWLP_USER);
+ KillTimer(hwnd, ri->timerID);
+ delete ri;
+ }
+ }
+
+ return FALSE;
+}
+
+//----------------------------
+
LRESULT CALLBACK OBS::OBSProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lParam)
{
traceIn(OBS::OBSProc);
@@ -1527,13 +1614,24 @@ LRESULT CALLBACK OBS::OBSProc(HWND hwnd, UINT message, WPARAM wParam, LPARAM lPa
case OBS_REQUESTSTOP:
App->Stop();
- MessageBox(hwnd, Str("Connection.Disconnected"), NULL, 0);
+ if(!App->bAutoReconnect)
+ MessageBox(hwnd, Str("Connection.Disconnected"), NULL, 0);
+ else
+ {
+ App->bReconnecting = false;
+ DialogBox(hinstMain, MAKEINTRESOURCE(IDD_RECONNECTING), hwnd, OBS::ReconnectDialogProc);
+ }
break;
case OBS_CALLHOTKEY:
App->CallHotkey((DWORD)lParam, wParam != 0);
break;
+ case OBS_RECONNECT:
+ App->bReconnecting = true;
+ App->Start();
+ break;
+
case WM_CLOSE:
PostQuitMessage(0);
break;
diff --git a/resource.h b/resource.h
index 3205c7e6..2f6dcbc7 100644
--- a/resource.h
+++ b/resource.h
@@ -23,6 +23,9 @@
#define IDD_GLOBAL_SOURCES 126
#define IDD_PLUGINS 129
#define IDD_SCENEHOTKEY 130
+#define IDD_DIALOG1 131
+#define IDD_RECONNECTING 131
+#define IDD_SETTINGS_ADVANCED 132
#define IDC_SETTINGSLIST 1006
#define IDC_SUBDIALOG 1007
#define IDC_MODE 1008
@@ -58,10 +61,16 @@
#define IDC_RESETSIZE 1041
#define IDC_CAPTUREMOUSE 1041
#define IDC_BUFFERSENDS 1041
+#define IDC_AUTORECONNECT 1041
+#define IDC_PUSHTOTALK 1041
+#define IDC_USEVIDEOENCODERSETTINGS 1041
#define IDC_SERVEREDIT 1042
+#define IDC_USEVIDEOENCODERSETTINGS2 1042
+#define IDC_USESENDBUFFER 1042
#define IDC_CHANNELNAME_STATIC 1043
#define IDC_PLAYPATH_STATIC 1044
#define IDC_USERNAME_STATIC 1045
+#define IDC_SAVETOFILE 1045
#define IDC_SERVER_STATIC 1046
#define IDC_SERVICE_STATIC 1047
#define IDC_NAME 1048
@@ -91,8 +100,25 @@
#define IDC_SELECTREGION 1071
#define IDC_HOTKEY1 1073
#define IDC_HOTKEY 1073
+#define IDC_PUSHTOTALKHOTKEY 1073
#define IDC_CLEAR 1074
+#define IDC_MUTEMICHOTKEY 1074
+#define IDC_MUTEDESKTOPHOTKEY 1075
#define IDC_ADDNEW 1077
+#define IDC_EDIT1 1078
+#define IDC_AUTORECONNECT_TIMEOUT_EDIT 1078
+#define IDC_VIDEOENCODERSETTINGS 1078
+#define IDC_SPIN1 1079
+#define IDC_AUTORECONNECT_TIMEOUT 1079
+#define IDC_AUTORECONNECT_TIMEOUT_STATIC 1080
+#define IDC_RECONNECTING 1081
+#define IDC_SAVEPATH 1081
+#define IDC_SAVEPATH_STATIC 1082
+#define IDC_USESYNCFIX 1086
+#define IDC_CLEARPUSHTOTALK 1088
+#define IDC_CLEARMUTEMIC 1089
+#define IDC_SENDBUFFERSIZE 1089
+#define IDC_CLEARMUTEDESKTOP 1090
#define IDA_SOURCE_MOVEUP 40018
#define IDA_SOURCE_MOVEDOWN 40019
#define IDA_SOURCE_MOVETOTOP 40020
@@ -106,9 +132,9 @@
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
-#define _APS_NEXT_RESOURCE_VALUE 131
+#define _APS_NEXT_RESOURCE_VALUE 133
#define _APS_NEXT_COMMAND_VALUE 40028
-#define _APS_NEXT_CONTROL_VALUE 1078
+#define _APS_NEXT_CONTROL_VALUE 1090
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif