file saving, advanced settings, bug fixes, cats and dogs living together... mass hysteria!

This commit is contained in:
jim 2012-09-26 19:54:42 -07:00
parent 2833b6a60e
commit 71be2c937e
26 changed files with 1767 additions and 695 deletions

81
OBS.rc
View File

@ -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 "ƒXƒ^ƒeƒBƒbƒN",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 "ƒXƒ^ƒeƒBƒbƒN",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

View File

@ -410,6 +410,10 @@
RelativePath=".\Source\Encoder_x264.cpp"
>
</File>
<File
RelativePath=".\Source\FLVFileStream.cpp"
>
</File>
<File
RelativePath=".\Source\GetAudioDevices.cpp"
>
@ -434,6 +438,10 @@
RelativePath=".\Source\MMDeviceAudioSource.cpp"
>
</File>
<File
RelativePath=".\Source\MP4FileStream.cpp"
>
</File>
<File
RelativePath=".\Source\NullOutput.cpp"
>
@ -446,10 +454,6 @@
RelativePath=".\Source\RTMPPublisher.cpp"
>
</File>
<File
RelativePath=".\Source\RTMPServer.cpp"
>
</File>
<File
RelativePath=".\Source\RTMPStuff.cpp"
>

View File

@ -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;

View File

@ -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);

View File

@ -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;}

View File

@ -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:

View File

@ -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);

View File

@ -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;
};

View File

@ -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;
}

View File

@ -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;

View File

@ -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);

View File

@ -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;

View File

@ -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<UVCoord> &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();

View File

@ -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; i<paramList.Num(); i++)
{
String &strParam = paramList[i];
String strParamName = strParam.GetToken(0, '=');
String strParamVal = strParam.GetToken(1, '=');
if( strParamName.CompareI(TEXT("fps")) ||
strParamName.CompareI(TEXT("force-cfr")))
{
continue;
}
LPSTR lpParam = strParamName.CreateUTF8String();
LPSTR lpVal = strParamVal.CreateUTF8String();
x264_param_parse(&paramData, lpParam, lpVal);
Free(lpParam);
Free(lpVal);
}
}
if(bUse444) paramData.i_csp = X264_CSP_I444;
x264 = x264_encoder_open(&paramData);
@ -143,7 +172,6 @@ public:
traceIn(X264Encoder::~X264Encoder);
ClearPackets();
HeaderPacket.Clear ();
x264_encoder_close(x264);
traceOut;
@ -176,10 +204,13 @@ public:
{
x264_nal_t &nal = nalOut[i];
if(nal.i_type == NAL_SLICE_IDR || nal.i_type == NAL_SLICE)
if(nal.i_type == NAL_SLICE_IDR || nal.i_type == NAL_SLICE || nal.i_type == NAL_SEI)
{
VideoPacket *newPacket = CurrentPackets.CreateNew();
if(nal.i_type == NAL_SEI)
nop();
BYTE *skip = nal.p_payload;
while(*(skip++) != 0x1);
int skipBytes = (int)(skip-nal.p_payload);
@ -190,7 +221,7 @@ public:
newPacket->Packet[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)

119
Source/FLVFileStream.cpp Normal file
View File

@ -0,0 +1,119 @@
/********************************************************************************
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
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<QWORD*>(&doubleDuration);
outputVal = fastHtonll(outputVal);
file.Write(&outputVal, 8);
file.SetPos(metaDataPos+0x37, XFILE_BEGIN);
outputVal = *reinterpret_cast<QWORD*>(&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;
}

765
Source/MP4FileStream.cpp Normal file
View File

@ -0,0 +1,765 @@
/********************************************************************************
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
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.h>
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<MP4VideoFrameInfo> videoFrames;
List<MP4AudioFrameInfo> audioFrames;
List<UINT> IFrameIDs;
DWORD lastVideoTimestamp;
bool bStreamOpened;
bool bMP3;
List<BYTE> endBuffer;
List<UINT> 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<typename T> inline void GetChunkInfo(List<T> &data, List<UINT64> &chunks, List<SampleToChunk> &sampleToChunks)
{
UINT64 curChunkOffset;
UINT64 connectedSampleOffset;
UINT numSamples = 0;
for(UINT i=0; i<data.Num(); i++)
{
UINT64 curOffset = data[i].fileOffset;
if(i == 0)
curChunkOffset = curOffset;
else
{
if(curOffset != connectedSampleOffset)
{
chunks << curChunkOffset;
if(!sampleToChunks.Num() || sampleToChunks.Last().samplesPerChunk != numSamples)
{
SampleToChunk stc;
stc.firstChunkID = chunks.Num();
stc.samplesPerChunk = numSamples;
sampleToChunks << stc;
}
curChunkOffset = curOffset;
numSamples = 0;
}
}
numSamples++;
connectedSampleOffset = curOffset+data[i].size;
}
chunks << curChunkOffset;
if(!sampleToChunks.Num() || sampleToChunks.Last().samplesPerChunk != numSamples)
{
SampleToChunk stc;
stc.firstChunkID = chunks.Num();
stc.samplesPerChunk = numSamples;
sampleToChunks << stc;
}
}
~MP4FileStream()
{
if(!bStreamOpened)
return;
mdatStop = fileOut.GetPos();
BufferOutputSerializer output(endBuffer);
DWORD macTime = fastHtonl(DWORD(GetMacTime()));
UINT videoDuration = fastHtonl(lastVideoTimestamp + App->GetFrameTime());
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<BYTE> 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<BYTE> AACHeader;
if(!bMP3)
{
DataPacket data;
App->GetAudioHeaders(data);
AACHeader.CopyArray(data.lpPacket+2, data.size-2);
}
//-------------------------------------------
// get chunk info
List<UINT64> videoChunks, audioChunks;
List<SampleToChunk> videoSampleToChunk, audioSampleToChunk;
GetChunkInfo<MP4VideoFrameInfo>(videoFrames, videoChunks, videoSampleToChunk);
GetChunkInfo<MP4AudioFrameInfo>(audioFrames, audioChunks, audioSampleToChunk);
//-------------------------------------------
// build decode time list and composition offset list
List<OffsetVal> decodeTimes;
List<OffsetVal> compositionOffsets;
for(UINT i=0; i<videoFrames.Num(); i++)
{
UINT frameTime;
if(i == videoFrames.Num()-1)
frameTime = decodeTimes.Last().val;
else
frameTime = videoFrames[i+1].timestamp-videoFrames[i].timestamp;
if(!decodeTimes.Num() || decodeTimes.Last().val != (UINT)frameTime)
{
OffsetVal newVal;
newVal.count = 1;
newVal.val = (UINT)frameTime;
decodeTimes << newVal;
}
else
decodeTimes.Last().count++;
INT compositionOffset = videoFrames[i].compositionOffset;
if(!compositionOffsets.Num() || compositionOffsets.Last().val != (UINT)compositionOffset)
{
OffsetVal newVal;
newVal.count = 1;
newVal.val = (UINT)compositionOffset;
compositionOffsets << newVal;
}
else
compositionOffsets.Last().count++;
}
//-------------------------------------------
// sound descriptor thingy. this part made me die a little inside admittedly.
UINT maxBitRate = fastHtonl(App->GetAudioEncoder()->GetBitRate()*1024);
List<BYTE> 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<BYTE> 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<decodeTimes.Num(); i++)
{
output.OutputDword(fastHtonl(decodeTimes[i].count));
output.OutputDword(fastHtonl(decodeTimes[i].val));
}
PopBox(); //stts
PushBox(output, DWORD_BE('stss')); //list of keyframe (i-frame) IDs
output.OutputDword(0); //version and flags (none)
output.OutputDword(fastHtonl(IFrameIDs.Num()));
output.Serialize(IFrameIDs.Array(), IFrameIDs.Num()*sizeof(UINT));
PopBox(); //stss
PushBox(output, DWORD_BE('ctts')); //list of composition time offsets
output.OutputDword(DWORD_BE(0x01000000)); //version (1) and flags (none)
output.OutputDword(fastHtonl(compositionOffsets.Num()));
for(UINT i=0; i<compositionOffsets.Num(); i++)
{
output.OutputDword(fastHtonl(compositionOffsets[i].count));
output.OutputDword(fastHtonl(compositionOffsets[i].val));
}
PopBox(); //ctts
PushBox(output, DWORD_BE('stsc')); //sample to chunk list
output.OutputDword(0); //version and flags (none)
output.OutputDword(fastHtonl(videoSampleToChunk.Num()));
for(UINT i=0; i<videoSampleToChunk.Num(); i++)
{
SampleToChunk &stc = videoSampleToChunk[i];
output.OutputDword(fastHtonl(stc.firstChunkID));
output.OutputDword(fastHtonl(stc.samplesPerChunk));
output.OutputDword(DWORD_BE(1));
}
PopBox(); //stsc
PushBox(output, DWORD_BE('stsz')); //sample sizes
output.OutputDword(0); //version and flags (none)
output.OutputDword(0); //block size for all (0 if differing sizes)
output.OutputDword(fastHtonl(videoFrames.Num()));
for(UINT i=0; i<videoFrames.Num(); i++)
output.OutputDword(fastHtonl(videoFrames[i].size));
PopBox();
if(videoChunks.Num() && videoChunks.Last() > 0xFFFFFFFFLL)
{
PushBox(output, DWORD_BE('co64')); //chunk offsets
output.OutputDword(0); //version and flags (none)
output.OutputDword(fastHtonl(videoChunks.Num()));
for(UINT i=0; i<videoChunks.Num(); i++)
output.OutputQword(fastHtonll(videoChunks[i]));
PopBox(); //co64
}
else
{
PushBox(output, DWORD_BE('stco')); //chunk offsets
output.OutputDword(0); //version and flags (none)
output.OutputDword(fastHtonl(videoChunks.Num()));
for(UINT i=0; i<videoChunks.Num(); i++)
output.OutputDword(fastHtonl((DWORD)videoChunks[i]));
PopBox(); //stco
}
PopBox(); //stbl
PopBox(); //minf
PopBox(); //mdia
PopBox(); //trak
//------------------------------------------------------
// audio 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(2)); //track ID
output.OutputDword(0); //reserved
output.OutputDword(audioDuration); //duration (in time base units)
output.OutputQword(0); //reserved
output.OutputWord(0); //video layer (0)
output.OutputWord(WORD_BE(1)); //quicktime alternate track id
output.OutputWord(WORD_BE(0x0100)); //volume
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(0); //width (fixed point)
output.OutputDword(0); //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(audioDuration); //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(44100)); //time scale
output.OutputDword(fastHtonl(audioFrames.Num()*App->GetAudioEncoder()->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<audioSampleToChunk.Num(); i++)
{
SampleToChunk &stc = audioSampleToChunk[i];
output.OutputDword(fastHtonl(stc.firstChunkID));
output.OutputDword(fastHtonl(stc.samplesPerChunk));
output.OutputDword(DWORD_BE(1));
}
PopBox(); //stsc
PushBox(output, DWORD_BE('stsz')); //sample sizes
output.OutputDword(0); //version and flags (none)
output.OutputDword(0); //block size for all (0 if differing sizes)
output.OutputDword(fastHtonl(audioFrames.Num()));
for(UINT i=0; i<audioFrames.Num(); i++)
output.OutputDword(fastHtonl(audioFrames[i].size));
PopBox();
if(audioChunks.Num() && audioChunks.Last() > 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<audioChunks.Num(); i++)
output.OutputQword(fastHtonll(audioChunks[i]));
PopBox(); //co64
}
else
{
PushBox(output, DWORD_BE('stco')); //chunk offsets
output.OutputDword(0); //version and flags (none)
output.OutputDword(fastHtonl(audioChunks.Num()));
for(UINT i=0; i<audioChunks.Num(); i++)
output.OutputDword(fastHtonl((DWORD)audioChunks[i]));
PopBox(); //stco
}
PopBox(); //stbl
PopBox(); //minf
PopBox(); //mdia
PopBox(); //trak
//------------------------------------------------------
// info thingy
PushBox(output, DWORD_BE('udta'));
PushBox(output, DWORD_BE('meta'));
output.OutputDword(0); //version and flags (none)
PushBox(output, DWORD_BE('hdlr'));
output.OutputDword(0); //version and flags (none)
output.OutputDword(0); //quicktime type
output.OutputDword(DWORD_BE('mdir')); //metadata type
output.OutputDword(DWORD_BE('appl')); //quicktime manufacturer reserved thingy
output.OutputDword(0); //quicktime component reserved flag
output.OutputDword(0); //quicktime component reserved flag mask
output.OutputByte(0); //null string
PopBox(); //hdlr
PushBox(output, DWORD_BE('ilst'));
PushBox(output, DWORD_BE('\xa9too'));
PushBox(output, DWORD_BE('data'));
output.OutputDword(DWORD_BE(1)); //version (1) + flags (0)
output.OutputDword(0); //reserved
LPSTR lpVersion = OBS_VERSION_STRING_ANSI;
output.Serialize(lpVersion, (DWORD)strlen(lpVersion));
PopBox(); //data
PopBox(); //@too
PopBox(); //ilst
PopBox(); //meta
PopBox(); //udta
PopBox(); //moov
fileOut.Serialize(endBuffer.Array(), endBuffer.Num());
fileOut.Close();
XFile file;
if(file.Open(strFile, XFILE_WRITE, XFILE_OPENEXISTING))
{
#ifdef USE_64BIT_MP4
file.SetPos((INT64)mdatStart+8, XFILE_BEGIN);
UINT64 size = fastHtonll(mdatStop-mdatStart);
file.Write(&size, 8);
#else
file.SetPos((INT64)mdatStart, XFILE_BEGIN);
UINT size = fastHtonl((DWORD)(mdatStop-mdatStart));
file.Write(&size, 4);
#endif
file.Close();
}
}
virtual void AddPacket(BYTE *data, UINT size, DWORD timestamp, PacketType type)
{
UINT64 offset = fileOut.GetPos();
if(type == PacketType_Audio)
{
UINT copySize;
if(bMP3)
{
copySize = size-1;
fileOut.Serialize(data+1, copySize);
}
else
{
copySize = size-2;
fileOut.Serialize(data+2, copySize);
}
MP4AudioFrameInfo audioFrame;
audioFrame.fileOffset = offset;
audioFrame.size = copySize;
audioFrames << audioFrame;
}
else
{
UINT totalCopied = 0;
if(data[0] == 0x17 && data[1] == 0) //if SPS/PPS
{
LPBYTE lpData = data+11;
UINT spsSize = fastHtons(*(WORD*)lpData);
fileOut.OutputWord(0);
fileOut.Serialize(lpData, spsSize+2);
lpData += spsSize+3;
UINT ppsSize = fastHtons(*(WORD*)lpData);
fileOut.OutputWord(0);
fileOut.Serialize(lpData, ppsSize+2);
totalCopied = spsSize+ppsSize+8;
}
else
{
totalCopied = size-5;
fileOut.Serialize(data+5, totalCopied);
}
if(!videoFrames.Num() || timestamp != lastVideoTimestamp)
{
INT timeOffset = 0;
mcpy(((BYTE*)&timeOffset)+1, data+2, 3);
if(data[2] >= 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;
}

View File

@ -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

View File

@ -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--;

View File

@ -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<FrameAudio> pendingAudioFrames;
bool bWriteToFile;
VideoFileStream *fileStream;
String streamReport;
List<IconInfo> 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;}

View File

@ -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;
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;

View File

@ -1,549 +0,0 @@
/********************************************************************************
Copyright (C) 2012 Hugh Bailey <obs.jim@gmail.com>
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<Connection*> 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<connections.Num(); i++)
delete connections[i];
connections.Clear();
if(serverSocket)
closesocket(serverSocket);
traceOut;
}
void SendPacket(BYTE *data, UINT size, DWORD timestamp, PacketType type)
{
traceIn(RTMPServer::SendPacket);
RTMPPacket packet;
packet.m_nChannel = (type == PacketType_Audio) ? 0x5 : 0x4;
packet.m_headerType = RTMP_PACKET_SIZE_LARGE;
packet.m_packetType = (type == PacketType_Audio) ? RTMP_PACKET_TYPE_AUDIO : RTMP_PACKET_TYPE_VIDEO;
packet.m_nTimeStamp = timestamp;
packet.m_nInfoField2 = 1;
packet.m_hasAbsTimestamp = TRUE;
List<BYTE> 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; i<connections.Num(); i++)
{
Connection *connection = connections[i];
if(connection->ReadyForOutput())
{
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; i<connectObj.o_num; i++)
{
pname = connectObj.o_props[i].p_name;
pval.av_val = NULL;
pval.av_len = 0;
if (connectObj.o_props[i].p_type == AMF_STRING)
pval = connectObj.o_props[i].p_vu.p_aval;
pval.av_val = NULL;
if (AVMATCH(&pname, &av_app))
{
rtmp->Link.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<BYTE> 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;
}

View File

@ -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_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;

View File

@ -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<MonitorInfo> &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_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,15 +1350,121 @@ 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);
//--------------------------------------------
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;
}

View File

@ -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)
{

View File

@ -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();
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;

View File

@ -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