mirror of
https://github.com/WinampDesktop/winamp.git
synced 2025-06-17 16:25:46 -04:00
Initial community commit
This commit is contained in:
238
Src/Plugins/Input/in_flv/AMFDispatch.cpp
Normal file
238
Src/Plugins/Input/in_flv/AMFDispatch.cpp
Normal file
@ -0,0 +1,238 @@
|
||||
#include "AMFDispatch.h"
|
||||
|
||||
AMFDispatch::AMFDispatch(AMFMixedArray *array)
|
||||
{
|
||||
object=array;
|
||||
if (object)
|
||||
object->AddRef();
|
||||
refCount=1;
|
||||
}
|
||||
|
||||
AMFDispatch::~AMFDispatch()
|
||||
{
|
||||
if (object)
|
||||
object->Release();
|
||||
}
|
||||
|
||||
STDMETHODIMP AMFDispatch::QueryInterface(REFIID riid, PVOID *ppvObject)
|
||||
{
|
||||
if (!ppvObject)
|
||||
return E_POINTER;
|
||||
|
||||
else if (IsEqualIID(riid, IID_IDispatch))
|
||||
*ppvObject = (IDispatch *)this;
|
||||
else if (IsEqualIID(riid, IID_IUnknown))
|
||||
*ppvObject = this;
|
||||
else
|
||||
{
|
||||
*ppvObject = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG AMFDispatch::AddRef(void)
|
||||
{
|
||||
return InterlockedIncrement((volatile LONG *)&refCount);
|
||||
}
|
||||
|
||||
ULONG AMFDispatch::Release(void)
|
||||
{
|
||||
ULONG count = InterlockedDecrement((volatile LONG *)&refCount);
|
||||
if (count == 0)
|
||||
delete this;
|
||||
return count;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
DISP_AMF_DEBUGPRINT,
|
||||
DISP_AMF_MAX,
|
||||
};
|
||||
|
||||
#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; }
|
||||
HRESULT AMFDispatch::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
|
||||
{
|
||||
bool unknowns = false;
|
||||
for (unsigned int i = 0;i != cNames;i++)
|
||||
{
|
||||
CHECK_ID("DebugPrint", DISP_AMF_DEBUGPRINT)
|
||||
if (object)
|
||||
{
|
||||
//size_t index = object->array.getPosition(rgszNames[i]);
|
||||
size_t index = 0;
|
||||
for (auto it = object->array.begin(); it != object->array.end(); it++, index++)
|
||||
{
|
||||
if (wcscmp(it->first.c_str(), rgszNames[i]) == 0)
|
||||
{
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if (index != object->array.size())
|
||||
{
|
||||
rgdispid[i] = (DISPID)index + DISP_AMF_MAX;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
|
||||
rgdispid[i] = DISPID_UNKNOWN;
|
||||
unknowns = true;
|
||||
|
||||
}
|
||||
if (unknowns)
|
||||
return DISP_E_UNKNOWNNAME;
|
||||
else
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT AMFDispatch::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT AMFDispatch::GetTypeInfoCount(unsigned int FAR * pctinfo)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
static void AMFType_To_Variant(AMFType *obj, VARIANT *pvarResult)
|
||||
{
|
||||
VariantInit(pvarResult);
|
||||
switch(obj->type)
|
||||
{
|
||||
case AMFType::TYPE_DOUBLE: // double
|
||||
{
|
||||
AMFDouble *cast_obj = static_cast<AMFDouble *>(obj);
|
||||
V_VT(pvarResult) = VT_R8;
|
||||
V_R8(pvarResult) = cast_obj->val;
|
||||
}
|
||||
break;
|
||||
case AMFType::TYPE_BOOL: // bool
|
||||
{
|
||||
AMFBoolean *cast_obj = static_cast<AMFBoolean *>(obj);
|
||||
V_VT(pvarResult) = VT_BOOL;
|
||||
V_BOOL(pvarResult) = cast_obj->boolean;
|
||||
}
|
||||
break;
|
||||
case AMFType::TYPE_MOVIE: // movie (basically just a URL)
|
||||
case AMFType::TYPE_STRING: // string
|
||||
{
|
||||
AMFString *cast_obj = static_cast<AMFString *>(obj);
|
||||
V_VT(pvarResult) = VT_BSTR;
|
||||
V_BSTR(pvarResult) = SysAllocString(cast_obj->str);
|
||||
}
|
||||
break;
|
||||
case AMFType::TYPE_LONG_STRING: // string
|
||||
{
|
||||
AMFLongString *cast_obj = static_cast<AMFLongString *>(obj);
|
||||
V_VT(pvarResult) = VT_BSTR;
|
||||
V_BSTR(pvarResult) = SysAllocString(cast_obj->str);
|
||||
}
|
||||
break;
|
||||
case AMFType::TYPE_MIXEDARRAY:
|
||||
{
|
||||
AMFMixedArray *cast_obj = static_cast<AMFMixedArray *>(obj);
|
||||
V_VT(pvarResult) = VT_DISPATCH;
|
||||
V_DISPATCH(pvarResult) = new AMFDispatch(cast_obj);
|
||||
}
|
||||
break;
|
||||
case AMFType::TYPE_DATE:
|
||||
{
|
||||
AMFTime *cast_obj = static_cast<AMFTime *>(obj);
|
||||
V_VT(pvarResult) = VT_DATE;
|
||||
V_DATE(pvarResult) = cast_obj->val;
|
||||
}
|
||||
break;
|
||||
case AMFType::TYPE_ARRAY:
|
||||
{
|
||||
AMFArray *cast_obj = static_cast<AMFArray *>(obj);
|
||||
SAFEARRAYBOUND rgsabound[1];
|
||||
rgsabound[0].lLbound = 0;
|
||||
rgsabound[0].cElements = (ULONG)cast_obj->array.size();
|
||||
SAFEARRAY *psa = SafeArrayCreate(VT_VARIANT, 1, rgsabound);
|
||||
VARIANT **data;
|
||||
SafeArrayAccessData(psa, (void **)&data);
|
||||
for (size_t i=0;i!=cast_obj->array.size();i++)
|
||||
{
|
||||
AMFType_To_Variant(cast_obj->array[i], data[i]);
|
||||
}
|
||||
SafeArrayUnaccessData(psa);
|
||||
V_VT(pvarResult) = VT_ARRAY;
|
||||
V_ARRAY(pvarResult) = psa;
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
HRESULT AMFDispatch::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
|
||||
{
|
||||
if (pvarResult)
|
||||
VariantInit(pvarResult);
|
||||
|
||||
switch(dispid)
|
||||
{
|
||||
case DISP_AMF_DEBUGPRINT:
|
||||
{
|
||||
wchar_t debugstring[4096]=L"";
|
||||
wchar_t *str = debugstring;
|
||||
size_t len = 4096;
|
||||
object->DebugPrint(1, str, len);
|
||||
V_VT(pvarResult) = VT_BSTR;
|
||||
V_BSTR(pvarResult) = SysAllocString(debugstring);
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
size_t index = dispid - DISP_AMF_MAX;
|
||||
if (index >= object->array.size())
|
||||
return DISP_E_MEMBERNOTFOUND;
|
||||
|
||||
//AMFType *obj = object->array.at(index).second;
|
||||
AMFType* obj = 0;
|
||||
auto it = object->array.begin();
|
||||
while (index--)
|
||||
{
|
||||
it++;
|
||||
}
|
||||
if (it != object->array.end())
|
||||
{
|
||||
obj = it->second;
|
||||
}
|
||||
|
||||
if (!obj)
|
||||
return S_OK;
|
||||
|
||||
switch(obj->type)
|
||||
{
|
||||
case AMFType::TYPE_DOUBLE:
|
||||
case AMFType::TYPE_BOOL:
|
||||
case AMFType::TYPE_STRING:
|
||||
case AMFType::TYPE_MIXEDARRAY:
|
||||
case AMFType::TYPE_ARRAY:
|
||||
AMFType_To_Variant(obj, pvarResult);
|
||||
return S_OK;
|
||||
|
||||
case AMFType::TYPE_OBJECT: // object
|
||||
// TODO
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
case AMFType::TYPE_NULL: // null
|
||||
return S_OK;
|
||||
case AMFType::TYPE_REFERENCE: // reference
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
case AMFType::TYPE_TERMINATOR:
|
||||
// TODO?
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
case AMFType::TYPE_DATE: // date
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
case AMFType::TYPE_LONG_STRING: // long string
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
case AMFType::TYPE_XML: // XML
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
default:
|
||||
return DISP_E_TYPEMISMATCH;
|
||||
|
||||
}
|
||||
return S_OK;
|
||||
}
|
28
Src/Plugins/Input/in_flv/AMFDispatch.h
Normal file
28
Src/Plugins/Input/in_flv/AMFDispatch.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
/*
|
||||
Ben Allison
|
||||
April 30, 2008
|
||||
|
||||
This class implements an IDispatch interface around an AMFObject
|
||||
*/
|
||||
#include <oaidl.h>
|
||||
#include "AMFObject.h"
|
||||
class AMFDispatch : public IDispatch
|
||||
{
|
||||
public:
|
||||
AMFDispatch(AMFMixedArray *array);
|
||||
~AMFDispatch();
|
||||
STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
|
||||
STDMETHOD_(ULONG, AddRef)(void);
|
||||
STDMETHOD_(ULONG, Release)(void);
|
||||
|
||||
private:
|
||||
// *** IDispatch Methods ***
|
||||
STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
|
||||
STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
|
||||
STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
|
||||
STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
|
||||
|
||||
volatile ULONG refCount;
|
||||
AMFMixedArray *object;
|
||||
};
|
319
Src/Plugins/Input/in_flv/AMFObject.cpp
Normal file
319
Src/Plugins/Input/in_flv/AMFObject.cpp
Normal file
@ -0,0 +1,319 @@
|
||||
#include "AMFObject.h"
|
||||
#include <strsafe.h>
|
||||
void AMFMixedArray::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
AMFTypeList::iterator itr;
|
||||
StringCchCopyEx(str, len, L"Mixed Array [\n", &str, &len, 0);
|
||||
for (itr=array.begin();itr!=array.end();itr++)
|
||||
{
|
||||
for (int i=0;i<spaces;i++)
|
||||
StringCchCopyEx(str, len, L" ", &str, &len,0);
|
||||
|
||||
if (itr->first.c_str() != 0 && itr->first.c_str()[0])
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%s: ", itr->first.c_str());
|
||||
if (itr->second)
|
||||
itr->second->DebugPrint(spaces+1, str, len);
|
||||
else
|
||||
StringCchCopyEx(str, len, L"(null)\n", &str, &len, 0);
|
||||
}
|
||||
StringCchCopyEx(str, len, L"]\n", &str, &len, 0);
|
||||
|
||||
}
|
||||
|
||||
size_t AMFMixedArray::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
size_t read = 0;
|
||||
uint32_t maxIndex = FLV::Read32(data);
|
||||
// TODO? array.reserve(maxIndex);
|
||||
data += 4;
|
||||
size -= 4;
|
||||
read += 4;
|
||||
|
||||
while (size)
|
||||
{
|
||||
AMFString amfString;
|
||||
size_t skip = amfString.Read(data, size);
|
||||
data += skip;
|
||||
size -= skip;
|
||||
read += skip;
|
||||
|
||||
uint8_t type = *data;
|
||||
data++;
|
||||
size--;
|
||||
read++;
|
||||
AMFType *obj = MakeObject(type);
|
||||
if (obj)
|
||||
{
|
||||
obj->type = type;
|
||||
size_t skip = obj->Read(data, size);
|
||||
data += skip;
|
||||
size -= skip;
|
||||
read += skip;
|
||||
|
||||
array[amfString.str] = obj;
|
||||
}
|
||||
else
|
||||
break;
|
||||
|
||||
if (type == TYPE_TERMINATOR)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
AMFMixedArray::~AMFMixedArray()
|
||||
{
|
||||
for (AMFTypeList::iterator itr=array.begin();itr!=array.end();itr++)
|
||||
{
|
||||
delete itr->second;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
void AMFObj::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchCopyEx(str, len, L"Object (TODO)\n", &str, &len, 0);
|
||||
}
|
||||
|
||||
size_t AMFObj::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
size_t read = 0;
|
||||
while (size)
|
||||
{
|
||||
AMFString amfString;
|
||||
size_t skip = amfString.Read(data, size);
|
||||
data += skip;
|
||||
size -= skip;
|
||||
read += skip;
|
||||
|
||||
uint8_t type = *data;
|
||||
data++;
|
||||
size--;
|
||||
read++;
|
||||
AMFType *obj = MakeObject(type);
|
||||
if (obj)
|
||||
{
|
||||
obj->type = type;
|
||||
size_t skip = obj->Read(data, size);
|
||||
data += skip;
|
||||
size -= skip;
|
||||
read += skip;
|
||||
}
|
||||
else
|
||||
return false;
|
||||
|
||||
if (type == TYPE_TERMINATOR)
|
||||
break;
|
||||
}
|
||||
return read;
|
||||
}
|
||||
|
||||
|
||||
void AMFArray::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
|
||||
StringCchCopyEx(str, len, L"Array [\n", &str, &len, 0);
|
||||
for (size_t i=0;i!=array.size();i++)
|
||||
{
|
||||
for (int s=0;s<spaces;s++)
|
||||
StringCchCopyEx(str, len, L" ", &str, &len,0);
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%u: ", i);
|
||||
array[i]->DebugPrint(spaces+1, str, len);
|
||||
}
|
||||
StringCchCopyEx(str, len, L"]\n", &str, &len, 0);
|
||||
}
|
||||
|
||||
size_t AMFArray::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
size_t read = 0;
|
||||
uint32_t arrayLength = FLV::Read32(data);
|
||||
array.reserve(arrayLength);
|
||||
data += 4;
|
||||
read += 4;
|
||||
size -= 4;
|
||||
|
||||
for (uint32_t i=0;i!=arrayLength;i++)
|
||||
{
|
||||
uint8_t type = *data;
|
||||
data++;
|
||||
read++;
|
||||
size--;
|
||||
|
||||
AMFType *obj = MakeObject(type);
|
||||
size_t skip = obj->Read(data, size);
|
||||
//array[i]=obj;
|
||||
array.push_back(obj);
|
||||
data += skip;
|
||||
read += skip;
|
||||
size -= skip;
|
||||
}
|
||||
|
||||
return read;
|
||||
}
|
||||
|
||||
AMFArray::~AMFArray()
|
||||
{
|
||||
for (size_t i=0;i!=array.size();i++)
|
||||
{
|
||||
delete array[i];
|
||||
}
|
||||
}
|
||||
|
||||
/* --- String --- */
|
||||
AMFString::AMFString() : str(0)
|
||||
{}
|
||||
AMFString::~AMFString()
|
||||
{
|
||||
free(str);
|
||||
}
|
||||
|
||||
void AMFString::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%s\n", this->str);
|
||||
}
|
||||
|
||||
size_t AMFString::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 2)
|
||||
return 0;
|
||||
|
||||
unsigned __int16 strlength = FLV::Read16(data);
|
||||
data += 2;
|
||||
size -= 2;
|
||||
|
||||
if (strlength > size)
|
||||
return 0;
|
||||
|
||||
char *utf8string = (char *)calloc(strlength, sizeof(char));
|
||||
memcpy(utf8string, data, strlength);
|
||||
|
||||
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8string, strlength, 0, 0);
|
||||
str = (wchar_t *)calloc(wideLen + 2, sizeof(wchar_t));
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8string, strlength, str, wideLen);
|
||||
str[wideLen] = 0;
|
||||
free(utf8string);
|
||||
|
||||
return strlength + 2;
|
||||
}
|
||||
|
||||
/* --- Long String --- */
|
||||
AMFLongString::AMFLongString() : str(0)
|
||||
{}
|
||||
AMFLongString::~AMFLongString()
|
||||
{
|
||||
free(str);
|
||||
}
|
||||
|
||||
void AMFLongString::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%s\n", this->str);
|
||||
}
|
||||
|
||||
size_t AMFLongString::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 4)
|
||||
return 0;
|
||||
|
||||
uint32_t strlength = FLV::Read32(data);
|
||||
data += 4;
|
||||
size -= 4;
|
||||
|
||||
if (strlength > size)
|
||||
return 0;
|
||||
|
||||
char *utf8string = (char *)calloc(strlength, sizeof(char));
|
||||
memcpy(utf8string, data, strlength);
|
||||
|
||||
int wideLen = MultiByteToWideChar(CP_UTF8, 0, utf8string, strlength, 0, 0);
|
||||
str = (wchar_t *)calloc(wideLen + 2, sizeof(wchar_t));
|
||||
|
||||
MultiByteToWideChar(CP_UTF8, 0, utf8string, strlength, str, wideLen);
|
||||
str[wideLen] = 0;
|
||||
free(utf8string);
|
||||
|
||||
return strlength + 4;
|
||||
}
|
||||
|
||||
/* --- Double --- */
|
||||
void AMFDouble::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%f\n", val);
|
||||
}
|
||||
|
||||
/* --- Boolean --- */
|
||||
void AMFBoolean::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%s\n", boolean?L"true":L"false");
|
||||
}
|
||||
|
||||
/* --- Time --- */
|
||||
static size_t MakeDateString(__time64_t convertTime, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
SYSTEMTIME sysTime;
|
||||
tm *newtime = _localtime64(&convertTime);
|
||||
dest[0] = 0; // so we can bail out easily
|
||||
if (newtime)
|
||||
{
|
||||
sysTime.wYear = (WORD)(newtime->tm_year + 1900);
|
||||
sysTime.wMonth = (WORD)(newtime->tm_mon + 1);
|
||||
sysTime.wDayOfWeek = (WORD)newtime->tm_wday;
|
||||
sysTime.wDay = (WORD)newtime->tm_mday;
|
||||
sysTime.wHour = (WORD)newtime->tm_hour;
|
||||
sysTime.wMinute = (WORD)newtime->tm_min;
|
||||
sysTime.wSecond = (WORD)newtime->tm_sec;
|
||||
sysTime.wMilliseconds = 0;
|
||||
|
||||
int charsWritten = GetDateFormatW(LOCALE_USER_DEFAULT, DATE_SHORTDATE, &sysTime, NULL, dest, (int)destlen);
|
||||
if (charsWritten)
|
||||
{
|
||||
size_t dateSize = charsWritten-1;
|
||||
dest += dateSize;
|
||||
destlen -= dateSize;
|
||||
if (destlen)
|
||||
{
|
||||
*dest++ = L' ';
|
||||
destlen--;
|
||||
dateSize++;
|
||||
}
|
||||
|
||||
int charsWritten2 = GetTimeFormatW(LOCALE_USER_DEFAULT, 0, &sysTime, NULL, dest, (int)destlen);
|
||||
if (charsWritten2)
|
||||
{
|
||||
dateSize+=(charsWritten2-1);
|
||||
}
|
||||
return dateSize;
|
||||
}
|
||||
}
|
||||
|
||||
return 1;
|
||||
|
||||
}
|
||||
|
||||
void AMFTime::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
size_t written = MakeDateString((__time64_t)val, str, len);
|
||||
str+=written;
|
||||
len-=written;
|
||||
if (len>=2)
|
||||
{
|
||||
str[0]='\n';
|
||||
str[1]=0;
|
||||
len--;
|
||||
str++;
|
||||
}
|
||||
}
|
||||
|
||||
/* --- Terminator --- */
|
||||
void AMFTerminator::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchCopyEx(str, len, L"array terminator\n", &str, &len, 0);
|
||||
}
|
||||
|
||||
/* --- Reference --- */
|
||||
void AMFReference::DebugPrint(int spaces, wchar_t *&str, size_t &len)
|
||||
{
|
||||
StringCchPrintfEx(str, len, &str, &len, 0, L"%u\n", val);
|
||||
}
|
182
Src/Plugins/Input/in_flv/AMFObject.h
Normal file
182
Src/Plugins/Input/in_flv/AMFObject.h
Normal file
@ -0,0 +1,182 @@
|
||||
#ifndef NULLSOFT_AMFOBJECT_H
|
||||
#define NULLSOFT_AMFOBJECT_H
|
||||
|
||||
#include "FLVUtil.h"
|
||||
#include <windows.h>
|
||||
#include <map>
|
||||
#include <string>
|
||||
#include <vector>
|
||||
#include <time.h>
|
||||
|
||||
|
||||
class AMFType // no, not bowling, ActionScript Message Format
|
||||
{
|
||||
public:
|
||||
AMFType() : refCount(1) {}
|
||||
virtual ~AMFType() {}
|
||||
virtual size_t Read(uint8_t *data, size_t size)=0; // returns number of bytes read, 0 on failure
|
||||
uint8_t type;
|
||||
virtual void DebugPrint(int spaces, wchar_t *&str, size_t &len)=0;
|
||||
enum
|
||||
{
|
||||
TYPE_DOUBLE = 0x0,
|
||||
TYPE_BOOL = 0x1,
|
||||
TYPE_STRING = 0x2,
|
||||
TYPE_OBJECT = 0x3,
|
||||
TYPE_MOVIE = 0x4,
|
||||
TYPE_NULL = 0x5,
|
||||
TYPE_REFERENCE = 0x7,
|
||||
TYPE_MIXEDARRAY = 0x8,
|
||||
TYPE_TERMINATOR = 0x9,
|
||||
TYPE_ARRAY = 0xA,
|
||||
TYPE_DATE = 0xB,
|
||||
TYPE_LONG_STRING = 0xC,
|
||||
TYPE_XML = 0xF,
|
||||
};
|
||||
|
||||
ULONG AddRef(void)
|
||||
{
|
||||
return InterlockedIncrement((volatile LONG *)&refCount);
|
||||
}
|
||||
|
||||
ULONG Release(void)
|
||||
{
|
||||
ULONG count = InterlockedDecrement((volatile LONG *)&refCount);
|
||||
if (count == 0)
|
||||
delete this;
|
||||
return count;
|
||||
}
|
||||
private:
|
||||
ULONG refCount;
|
||||
};
|
||||
|
||||
class AMFString : public AMFType
|
||||
{
|
||||
public:
|
||||
AMFString();
|
||||
~AMFString();
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size);
|
||||
wchar_t *str;
|
||||
};
|
||||
|
||||
class AMFLongString : public AMFType
|
||||
{
|
||||
public:
|
||||
AMFLongString();
|
||||
~AMFLongString();
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size);
|
||||
wchar_t *str;
|
||||
};
|
||||
|
||||
class AMFObj : public AMFType
|
||||
{
|
||||
public:
|
||||
size_t Read(uint8_t *data, size_t size);
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
};
|
||||
|
||||
class AMFArray : public AMFType
|
||||
{
|
||||
public:
|
||||
size_t Read(uint8_t *data, size_t size);
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
~AMFArray();
|
||||
std::vector<AMFType*> array;
|
||||
};
|
||||
|
||||
class AMFMixedArray : public AMFType
|
||||
{
|
||||
public:
|
||||
size_t Read(uint8_t *data, size_t size);
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
~AMFMixedArray();
|
||||
typedef std::map<std::wstring, AMFType *> AMFTypeList;
|
||||
AMFTypeList array;
|
||||
};
|
||||
|
||||
class AMFDouble : public AMFType
|
||||
{
|
||||
public:
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 8)
|
||||
return 0;
|
||||
|
||||
val = FLV::ReadDouble(data);
|
||||
return 8;
|
||||
}
|
||||
double val;
|
||||
};
|
||||
|
||||
class AMFTime : public AMFType
|
||||
{
|
||||
public:
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 10)
|
||||
return 0;
|
||||
|
||||
val = FLV::ReadDouble(data);
|
||||
data+=8;
|
||||
|
||||
offset = FLV::Read16(data);
|
||||
|
||||
return 10;
|
||||
}
|
||||
double val; // same epoch as time_t, just stored as a double instead of an unsigned int
|
||||
int16_t offset; // offset in minutes from UTC. presumably from the encoding machine's timezone
|
||||
};
|
||||
|
||||
class AMFBoolean : public AMFType
|
||||
{
|
||||
public:
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 1)
|
||||
return 0;
|
||||
boolean = !!data[0];
|
||||
return 1;
|
||||
}
|
||||
|
||||
bool boolean;
|
||||
};
|
||||
|
||||
class AMFTerminator : public AMFType
|
||||
{
|
||||
public:
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
class AMFReference : public AMFType
|
||||
{
|
||||
public:
|
||||
void DebugPrint(int spaces, wchar_t *&str, size_t &len);
|
||||
size_t Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 2)
|
||||
return 0;
|
||||
val = FLV::Read16(data);
|
||||
return 2;
|
||||
}
|
||||
uint16_t val;
|
||||
};
|
||||
|
||||
inline double AMFGetDouble(AMFType *obj)
|
||||
{
|
||||
if (obj->type == 0x0)
|
||||
return ((AMFDouble *)obj)->val;
|
||||
|
||||
return 0;
|
||||
}
|
||||
|
||||
AMFType *MakeObject(uint8_t type);
|
||||
#endif
|
129
Src/Plugins/Input/in_flv/BackgroundDownloader.cpp
Normal file
129
Src/Plugins/Input/in_flv/BackgroundDownloader.cpp
Normal file
@ -0,0 +1,129 @@
|
||||
#include "Main.h"
|
||||
#include "BackgroundDownloader.h"
|
||||
#include "..\..\..\Components\wac_network\wac_network_http_receiver_api.h"
|
||||
#include "api__in_flv.h"
|
||||
#include "api/service/waservicefactory.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
|
||||
#include <strsafe.h>
|
||||
|
||||
#define HTTP_BUFFER_SIZE 65536
|
||||
|
||||
// {C0A565DC-0CFE-405a-A27C-468B0C8A3A5C}
|
||||
static const GUID internetConfigGroupGUID =
|
||||
{
|
||||
0xc0a565dc, 0xcfe, 0x405a, { 0xa2, 0x7c, 0x46, 0x8b, 0xc, 0x8a, 0x3a, 0x5c }
|
||||
};
|
||||
|
||||
|
||||
static void SetUserAgent( api_httpreceiver *http )
|
||||
{
|
||||
char agent[ 256 ] = { 0 };
|
||||
StringCchPrintfA( agent, 256, "User-Agent: %S/%S", WASABI_API_APP->main_getAppName(), WASABI_API_APP->main_getVersionNumString() );
|
||||
http->addheader( agent );
|
||||
}
|
||||
|
||||
static int FeedHTTP( api_httpreceiver *http, Downloader::DownloadCallback *callback, int *downloaded )
|
||||
{
|
||||
char downloadedData[ HTTP_BUFFER_SIZE ] = { 0 };
|
||||
int result = 0;
|
||||
int downloadSize = http->get_bytes( downloadedData, HTTP_BUFFER_SIZE );
|
||||
|
||||
*downloaded = downloadSize;
|
||||
if ( downloadSize )
|
||||
{
|
||||
result = callback->OnData( downloadedData, downloadSize );
|
||||
}
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
static void RunDownload( api_httpreceiver *http, Downloader::DownloadCallback *callback )
|
||||
{
|
||||
int ret;
|
||||
int downloaded = 0;
|
||||
do
|
||||
{
|
||||
ret = http->run();
|
||||
Sleep( 55 );
|
||||
do
|
||||
{
|
||||
if ( FeedHTTP( http, callback, &downloaded ) != 0 )
|
||||
return;
|
||||
} while ( downloaded == HTTP_BUFFER_SIZE );
|
||||
} while ( ret == HTTPRECEIVER_RUN_OK );
|
||||
|
||||
// finish off the data
|
||||
do
|
||||
{
|
||||
if ( FeedHTTP( http, callback, &downloaded ) != 0 )
|
||||
return;
|
||||
} while ( downloaded );
|
||||
}
|
||||
|
||||
|
||||
bool Downloader::Download(const char *url, Downloader::DownloadCallback *callback, uint64_t startPosition)
|
||||
{
|
||||
api_httpreceiver *http = 0;
|
||||
waServiceFactory *sf = plugin.service->service_getServiceByGuid(httpreceiverGUID);
|
||||
if (sf) http = (api_httpreceiver *)sf->getInterface();
|
||||
|
||||
if (!http)
|
||||
return false;
|
||||
|
||||
int use_proxy = 1;
|
||||
bool proxy80 = AGAVE_API_CONFIG->GetBool(internetConfigGroupGUID, L"proxy80", false);
|
||||
if (proxy80 && strstr(url, ":") && (!strstr(url, ":80/") && strstr(url, ":80") != (url + strlen(url) - 3)))
|
||||
use_proxy = 0;
|
||||
|
||||
const wchar_t *proxy = use_proxy?AGAVE_API_CONFIG->GetString(internetConfigGroupGUID, L"proxy", 0):0;
|
||||
|
||||
http->AllowCompression();
|
||||
http->open(API_DNS_AUTODNS, HTTP_BUFFER_SIZE, (proxy && proxy[0]) ? (const char *)AutoChar(proxy) : NULL);
|
||||
if (startPosition > 0)
|
||||
{
|
||||
char temp[128] = {0};
|
||||
StringCchPrintfA(temp, 128, "Range: bytes=%d-", startPosition);
|
||||
http->addheader(temp);
|
||||
}
|
||||
SetUserAgent(http);
|
||||
http->connect(url);
|
||||
int ret;
|
||||
|
||||
do
|
||||
{
|
||||
Sleep(55);
|
||||
ret = http->run();
|
||||
if (ret == -1) // connection failed
|
||||
break;
|
||||
|
||||
// ---- check our reply code ----
|
||||
int replycode = http->getreplycode();
|
||||
switch (replycode)
|
||||
{
|
||||
case 0:
|
||||
case 100:
|
||||
break;
|
||||
case 200:
|
||||
{
|
||||
if (callback->OnConnect(http) != 0)
|
||||
{
|
||||
sf->releaseInterface(http);
|
||||
return false;
|
||||
}
|
||||
|
||||
RunDownload(http, callback);
|
||||
sf->releaseInterface(http);
|
||||
return true;
|
||||
}
|
||||
break;
|
||||
default:
|
||||
sf->releaseInterface(http);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
while (ret == HTTPRECEIVER_RUN_OK);
|
||||
//const char *er = http->geterrorstr();
|
||||
sf->releaseInterface(http);
|
||||
return false;
|
||||
}
|
19
Src/Plugins/Input/in_flv/BackgroundDownloader.h
Normal file
19
Src/Plugins/Input/in_flv/BackgroundDownloader.h
Normal file
@ -0,0 +1,19 @@
|
||||
#pragma once
|
||||
#include <bfc/platform/types.h>
|
||||
|
||||
class api_httpreceiver;
|
||||
|
||||
class Downloader
|
||||
{
|
||||
public:
|
||||
class DownloadCallback
|
||||
{
|
||||
public:
|
||||
virtual int OnConnect(api_httpreceiver *http)=0;
|
||||
virtual int OnData(void *buffer, size_t bufferSize)=0;
|
||||
};
|
||||
|
||||
|
||||
bool Download(const char *url, DownloadCallback *callback, uint64_t startPosition = 0);
|
||||
|
||||
};
|
29
Src/Plugins/Input/in_flv/ExtendedInfo.cpp
Normal file
29
Src/Plugins/Input/in_flv/ExtendedInfo.cpp
Normal file
@ -0,0 +1,29 @@
|
||||
#include <bfc/platform/types.h>
|
||||
#include <windows.h>
|
||||
#include <strsafe.h>
|
||||
#include "api__in_flv.h"
|
||||
#include "resource.h"
|
||||
|
||||
extern "C" __declspec(dllexport)
|
||||
int winampGetExtendedFileInfoW(const wchar_t *fn, const char *data, wchar_t *dest, size_t destlen)
|
||||
{
|
||||
if (!_stricmp(data, "type"))
|
||||
{
|
||||
dest[0]='1';
|
||||
dest[1]=0;
|
||||
return 1;
|
||||
}
|
||||
else if (!_stricmp(data, "family"))
|
||||
{
|
||||
int len;
|
||||
const wchar_t *p;
|
||||
if (!fn || !fn[0]) return 0;
|
||||
len = lstrlenW(fn);
|
||||
if (len < 4 || L'.' != fn[len - 4]) return 0;
|
||||
p = &fn[len - 3];
|
||||
if (!_wcsicmp(p, L"FLV") && S_OK == StringCchCopyW(dest, destlen, WASABI_API_LNGSTRINGW(IDS_FAMILY_STRING))) return 1;
|
||||
return 0;
|
||||
}
|
||||
|
||||
return 0;
|
||||
}
|
34
Src/Plugins/Input/in_flv/FLVAudioHeader.cpp
Normal file
34
Src/Plugins/Input/in_flv/FLVAudioHeader.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "FLVAudioHeader.h"
|
||||
|
||||
/*
|
||||
(c) 2006 Nullsoft, Inc.
|
||||
Author: Ben Allison benski@nullsoft.com
|
||||
*/
|
||||
|
||||
/*
|
||||
soundType 1 bit (byte & 0x01) >> 0 0: mono, 1: stereo
|
||||
soundSize 1 bit (byte & 0x02)>> 1 0: 8-bit, 2: 16-bit
|
||||
soundRate 2 bits (byte & 0x0C) >> 2 0: 5.5 kHz, 1: 11 kHz, 2: 22 kHz, 3: 44 kHz
|
||||
soundFormat 4 bits (byte & 0xf0) >> 4 0: Uncompressed, 1: ADPCM, 2: MP3, 5: Nellymoser 8kHz mono, 6: Nellymoser
|
||||
*/
|
||||
|
||||
bool FLVAudioHeader::Read(unsigned __int8 *data, size_t size)
|
||||
{
|
||||
if (size < 1)
|
||||
return false; // header size too small
|
||||
|
||||
|
||||
unsigned __int8 byte = data[0];
|
||||
|
||||
stereo = !!((byte & 0x01) >> 0);
|
||||
bits = ((byte & 0x02) >> 1) ? 16 : 8;
|
||||
switch ((byte & 0x0C) >> 2)
|
||||
{
|
||||
case 0: sampleRate = 5512; break;
|
||||
case 1: sampleRate = 11025; break;
|
||||
case 2: sampleRate = 22050; break;
|
||||
case 3: sampleRate = 44100; break;
|
||||
}
|
||||
format = (byte & 0xf0) >> 4;
|
||||
return true;
|
||||
}
|
31
Src/Plugins/Input/in_flv/FLVAudioHeader.h
Normal file
31
Src/Plugins/Input/in_flv/FLVAudioHeader.h
Normal file
@ -0,0 +1,31 @@
|
||||
#ifndef NULLSOFT_FLVAUDIOHEADER_H
|
||||
#define NULLSOFT_FLVAUDIOHEADER_H
|
||||
|
||||
namespace FLV
|
||||
{
|
||||
const int AUDIO_FORMAT_PCM = 0;
|
||||
const int AUDIO_FORMAT_ADPCM = 1;
|
||||
const int AUDIO_FORMAT_MP3 = 2;
|
||||
const int AUDIO_FORMAT_PCM_LE = 3; // little endian
|
||||
const int AUDIO_FORMAT_NELLYMOSER_16KHZ = 4;
|
||||
const int AUDIO_FORMAT_NELLYMOSER_8KHZ = 5;
|
||||
const int AUDIO_FORMAT_NELLYMOSER = 6;
|
||||
const int AUDIO_FORMAT_A_LAW = 7;
|
||||
const int AUDIO_FORMAT_MU_LAW = 8;
|
||||
const int AUDIO_FORMAT_AAC = 10;
|
||||
const int AUDIO_FORMAT_MP3_8KHZ = 14;
|
||||
|
||||
};
|
||||
|
||||
class FLVAudioHeader
|
||||
{
|
||||
public:
|
||||
bool Read(unsigned __int8 *data, size_t size); // size must be >=1, returns "true" if this was a valid header
|
||||
|
||||
// attributes, consider these read-only
|
||||
bool stereo;
|
||||
int bits;
|
||||
int sampleRate;
|
||||
int format;
|
||||
};
|
||||
#endif
|
189
Src/Plugins/Input/in_flv/FLVCOM.cpp
Normal file
189
Src/Plugins/Input/in_flv/FLVCOM.cpp
Normal file
@ -0,0 +1,189 @@
|
||||
#include "FLVCOM.h"
|
||||
#include "AMFDispatch.h"
|
||||
FLVCOM flvCOM;
|
||||
extern bool mute;
|
||||
|
||||
static HANDLE DuplicateCurrentThread()
|
||||
{
|
||||
HANDLE fakeHandle = GetCurrentThread();
|
||||
HANDLE copiedHandle = 0;
|
||||
HANDLE processHandle = GetCurrentProcess();
|
||||
DuplicateHandle(processHandle, fakeHandle, processHandle, &copiedHandle, 0, FALSE, DUPLICATE_SAME_ACCESS);
|
||||
return copiedHandle;
|
||||
}
|
||||
|
||||
enum
|
||||
{
|
||||
DISP_FLV_REGISTER_CALLBACK,
|
||||
DISP_FLV_UNREGISTER_CALLBACK,
|
||||
DISP_FLV_SETMUTE,
|
||||
};
|
||||
|
||||
#define CHECK_ID(str, id) if (wcscmp(rgszNames[i], L##str) == 0) { rgdispid[i] = id; continue; }
|
||||
HRESULT FLVCOM::GetIDsOfNames(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid)
|
||||
{
|
||||
bool unknowns = false;
|
||||
for (unsigned int i = 0;i != cNames;i++)
|
||||
{
|
||||
CHECK_ID("RegisterCallback", DISP_FLV_REGISTER_CALLBACK)
|
||||
CHECK_ID("UnregisterCallback", DISP_FLV_UNREGISTER_CALLBACK)
|
||||
CHECK_ID("SetMute", DISP_FLV_SETMUTE)
|
||||
rgdispid[i] = DISPID_UNKNOWN;
|
||||
unknowns = true;
|
||||
}
|
||||
if (unknowns)
|
||||
return DISP_E_UNKNOWNNAME;
|
||||
else
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
HRESULT FLVCOM::GetTypeInfo(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT FLVCOM::GetTypeInfoCount(unsigned int FAR * pctinfo)
|
||||
{
|
||||
return E_NOTIMPL;
|
||||
}
|
||||
|
||||
HRESULT FLVCOM::Invoke(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr)
|
||||
{
|
||||
if (pvarResult)
|
||||
VariantInit(pvarResult);
|
||||
|
||||
switch (dispid)
|
||||
{
|
||||
case DISP_FLV_SETMUTE:
|
||||
{
|
||||
if (pdispparams->rgvarg[0].boolVal)
|
||||
mute = true;
|
||||
else
|
||||
mute = false;
|
||||
}
|
||||
return S_OK;
|
||||
case DISP_FLV_REGISTER_CALLBACK:
|
||||
{
|
||||
IDispatch *callback = pdispparams->rgvarg[0].pdispVal;
|
||||
|
||||
callbacks.push_back(DispatchCallbackInfo(callback, GetCurrentThreadId(), DuplicateCurrentThread()));
|
||||
return S_OK;
|
||||
}
|
||||
break;
|
||||
case DISP_FLV_UNREGISTER_CALLBACK:
|
||||
{
|
||||
IDispatch *callback = pdispparams->rgvarg[0].pdispVal;
|
||||
size_t numCallbacks = callbacks.size();
|
||||
while (numCallbacks--)
|
||||
{
|
||||
if (callbacks[numCallbacks].dispatch == callback)
|
||||
{
|
||||
CloseHandle(callbacks[numCallbacks].threadHandle);
|
||||
callbacks.erase(callbacks.begin() + numCallbacks);
|
||||
}
|
||||
}
|
||||
return S_OK;
|
||||
}
|
||||
break;
|
||||
}
|
||||
return DISP_E_MEMBERNOTFOUND;
|
||||
}
|
||||
|
||||
STDMETHODIMP FLVCOM::QueryInterface(REFIID riid, PVOID *ppvObject)
|
||||
{
|
||||
if (!ppvObject)
|
||||
return E_POINTER;
|
||||
|
||||
else if (IsEqualIID(riid, IID_IDispatch))
|
||||
*ppvObject = (IDispatch *)this;
|
||||
else if (IsEqualIID(riid, IID_IUnknown))
|
||||
*ppvObject = this;
|
||||
else
|
||||
{
|
||||
*ppvObject = NULL;
|
||||
return E_NOINTERFACE;
|
||||
}
|
||||
|
||||
AddRef();
|
||||
return S_OK;
|
||||
}
|
||||
|
||||
ULONG FLVCOM::AddRef(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
ULONG FLVCOM::Release(void)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
static void CallDispatchMethod(IDispatch *dispatch, DISPPARAMS ¶ms, OLECHAR *name)
|
||||
{
|
||||
try
|
||||
{
|
||||
unsigned int ret;
|
||||
DISPID dispid;
|
||||
if (SUCCEEDED(dispatch->GetIDsOfNames(IID_NULL, &name, 1, LOCALE_SYSTEM_DEFAULT, &dispid)))
|
||||
dispatch->Invoke(dispid, GUID_NULL, 0, DISPATCH_METHOD, ¶ms, 0, 0, &ret);
|
||||
}
|
||||
catch (...)
|
||||
{}
|
||||
}
|
||||
|
||||
struct APCdata
|
||||
{
|
||||
IDispatch *disp;
|
||||
wchar_t *name;
|
||||
AMFDispatch *amf;
|
||||
};
|
||||
|
||||
static VOID CALLBACK MetadataAPC(ULONG_PTR param)
|
||||
{
|
||||
APCdata *data = (APCdata *)param;
|
||||
|
||||
DISPPARAMS params;
|
||||
VARIANT argument;
|
||||
params.cArgs = 1;
|
||||
params.cNamedArgs = 0;
|
||||
params.rgdispidNamedArgs = 0;
|
||||
params.rgvarg = &argument;
|
||||
VariantInit(&argument);
|
||||
V_VT(&argument) = VT_DISPATCH;
|
||||
V_DISPATCH(&argument) = data->amf;
|
||||
|
||||
CallDispatchMethod(data->disp, params, data->name);
|
||||
|
||||
data->disp->Release();
|
||||
data->amf->Release();
|
||||
free(data->name);
|
||||
delete data;
|
||||
}
|
||||
|
||||
void FLVCOM::MetadataCallback(FLVMetadata::Tag *tag)
|
||||
{
|
||||
if (!callbacks.empty())
|
||||
{
|
||||
AMFDispatch *disp = new AMFDispatch(tag->parameters); // we're newing this we can refcount
|
||||
|
||||
DWORD curThreadId = GetCurrentThreadId();
|
||||
for (size_t i = 0;i != callbacks.size();i++)
|
||||
{
|
||||
APCdata *data = new APCdata;
|
||||
data->disp = callbacks[i].dispatch;
|
||||
data->disp->AddRef();
|
||||
data->name = _wcsdup(tag->name.str);
|
||||
data->amf = disp;
|
||||
data->amf->AddRef();
|
||||
|
||||
if (curThreadId == callbacks[i].threadId)
|
||||
MetadataAPC((ULONG_PTR)data);
|
||||
else
|
||||
{
|
||||
if (callbacks[i].threadHandle)
|
||||
QueueUserAPC(MetadataAPC, callbacks[i].threadHandle, (ULONG_PTR)data);
|
||||
}
|
||||
}
|
||||
disp->Release();
|
||||
}
|
||||
}
|
37
Src/Plugins/Input/in_flv/FLVCOM.h
Normal file
37
Src/Plugins/Input/in_flv/FLVCOM.h
Normal file
@ -0,0 +1,37 @@
|
||||
#pragma once
|
||||
/*
|
||||
Ben Allison
|
||||
April 30, 2008
|
||||
*/
|
||||
#include <oaidl.h>
|
||||
#include <vector>
|
||||
#include "FLVMetadata.h"
|
||||
|
||||
struct DispatchCallbackInfo
|
||||
{
|
||||
DispatchCallbackInfo() : dispatch(0), threadId(0), threadHandle(0) {}
|
||||
DispatchCallbackInfo(IDispatch *d, DWORD id, HANDLE _handle) : dispatch(d), threadId(id), threadHandle(_handle) {}
|
||||
IDispatch *dispatch;
|
||||
DWORD threadId;
|
||||
HANDLE threadHandle;
|
||||
};
|
||||
|
||||
class FLVCOM : public IDispatch
|
||||
{
|
||||
public:
|
||||
STDMETHOD(QueryInterface)(REFIID riid, PVOID *ppvObject);
|
||||
STDMETHOD_(ULONG, AddRef)(void);
|
||||
STDMETHOD_(ULONG, Release)(void);
|
||||
|
||||
void MetadataCallback(FLVMetadata::Tag *tag);
|
||||
private:
|
||||
// *** IDispatch Methods ***
|
||||
STDMETHOD (GetIDsOfNames)(REFIID riid, OLECHAR FAR* FAR* rgszNames, unsigned int cNames, LCID lcid, DISPID FAR* rgdispid);
|
||||
STDMETHOD (GetTypeInfo)(unsigned int itinfo, LCID lcid, ITypeInfo FAR* FAR* pptinfo);
|
||||
STDMETHOD (GetTypeInfoCount)(unsigned int FAR * pctinfo);
|
||||
STDMETHOD (Invoke)(DISPID dispid, REFIID riid, LCID lcid, WORD wFlags, DISPPARAMS FAR *pdispparams, VARIANT FAR *pvarResult, EXCEPINFO FAR * pexecinfo, unsigned int FAR *puArgErr);
|
||||
|
||||
std::vector<DispatchCallbackInfo> callbacks;
|
||||
};
|
||||
|
||||
extern FLVCOM flvCOM;
|
41
Src/Plugins/Input/in_flv/FLVHeader.cpp
Normal file
41
Src/Plugins/Input/in_flv/FLVHeader.cpp
Normal file
@ -0,0 +1,41 @@
|
||||
#include "FLVHeader.h"
|
||||
#include "FLVUtil.h"
|
||||
/*
|
||||
(c) 2006 Nullsoft, Inc.
|
||||
Author: Ben Allison benski@nullsoft.com
|
||||
*/
|
||||
|
||||
#define FLV_BITMASK_AUDIO 0x4
|
||||
#define FLV_BITMASK_VIDEO 0x1
|
||||
|
||||
/*
|
||||
FLV Header spec
|
||||
|
||||
Signature - uint8[3] - must equal "FLV"
|
||||
Version - uint8 - only known version is 1
|
||||
Flags - uint8 - bitmask, 4 is audio, 1 is video
|
||||
Offset - uint32 - total size of header (9), big endian
|
||||
*/
|
||||
|
||||
|
||||
|
||||
|
||||
bool FLVHeader::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 9)
|
||||
return false; // too small to be an FLV header
|
||||
|
||||
if (data[0] != 'F' || data[1] != 'L' || data[2] != 'V')
|
||||
return false; // invalid signature
|
||||
|
||||
version = data[3];
|
||||
|
||||
hasAudio = !!(data[4] & FLV_BITMASK_AUDIO);
|
||||
hasVideo = data[4] & FLV_BITMASK_VIDEO;
|
||||
|
||||
headerSize = FLV::Read32(&data[5]);
|
||||
if (headerSize != 9)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
}
|
18
Src/Plugins/Input/in_flv/FLVHeader.h
Normal file
18
Src/Plugins/Input/in_flv/FLVHeader.h
Normal file
@ -0,0 +1,18 @@
|
||||
#ifndef NULLSOFT_FLVHEADER_H
|
||||
#define NULLSOFT_FLVHEADER_H
|
||||
|
||||
#include <bfc/platform/types.h>
|
||||
|
||||
class FLVHeader
|
||||
{
|
||||
public:
|
||||
FLVHeader() : version(0), hasAudio(0), hasVideo(0), headerSize(0) {}
|
||||
bool Read(uint8_t *data, size_t size); // size must be >=9, returns "true" if this was a valid header
|
||||
|
||||
// attributes, consider these read-only
|
||||
uint8_t version;
|
||||
bool hasAudio, hasVideo;
|
||||
uint32_t headerSize;
|
||||
};
|
||||
|
||||
#endif
|
112
Src/Plugins/Input/in_flv/FLVMetadata.cpp
Normal file
112
Src/Plugins/Input/in_flv/FLVMetadata.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "FLVMetadata.h"
|
||||
#include "FLVUtil.h"
|
||||
#include <windows.h>
|
||||
|
||||
/*
|
||||
(c) 2006 Nullsoft, Inc.
|
||||
Author: Ben Allison benski@nullsoft.com
|
||||
*/
|
||||
|
||||
/*
|
||||
|
||||
type - uint8 -
|
||||
length - uint16
|
||||
|
||||
*/
|
||||
|
||||
|
||||
AMFType *MakeObject(uint8_t type)
|
||||
{
|
||||
switch(type)
|
||||
{
|
||||
case AMFType::TYPE_DOUBLE: // double
|
||||
return new AMFDouble;
|
||||
case AMFType::TYPE_BOOL: // bool
|
||||
return new AMFBoolean;
|
||||
case AMFType::TYPE_STRING: // string
|
||||
return new AMFString;
|
||||
case AMFType::TYPE_OBJECT: // object
|
||||
return new AMFObj;
|
||||
case AMFType::TYPE_MOVIE: // movie (basically just a URL)
|
||||
return new AMFString;
|
||||
case AMFType::TYPE_NULL: // null
|
||||
return 0;
|
||||
case AMFType::TYPE_REFERENCE: // reference
|
||||
return 0;
|
||||
case AMFType::TYPE_MIXEDARRAY:
|
||||
return new AMFMixedArray;
|
||||
case AMFType::TYPE_TERMINATOR:
|
||||
return new AMFTerminator;
|
||||
case AMFType::TYPE_ARRAY:
|
||||
return new AMFArray;
|
||||
case AMFType::TYPE_DATE: // date
|
||||
return new AMFTime;
|
||||
case AMFType::TYPE_LONG_STRING: // long string
|
||||
return new AMFLongString;
|
||||
case AMFType::TYPE_XML: // XML
|
||||
return 0;
|
||||
default:
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
|
||||
FLVMetadata::FLVMetadata()
|
||||
{
|
||||
}
|
||||
|
||||
FLVMetadata::~FLVMetadata()
|
||||
{
|
||||
for ( FLVMetadata::Tag *tag : tags )
|
||||
delete tag;
|
||||
}
|
||||
|
||||
bool FLVMetadata::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
// TODO: there can be multiple name/value pairs so we could read them all
|
||||
while(size)
|
||||
{
|
||||
uint8_t type=*data;
|
||||
data++;
|
||||
size--;
|
||||
|
||||
if (type == 0 && size >= 2 && data[0] == 0 && data[1] == AMFType::TYPE_TERMINATOR) // check for terminator
|
||||
return true; // array is done
|
||||
|
||||
if (type != AMFType::TYPE_STRING) // first entry is a string, verify this
|
||||
return false; // malformed, lets bail
|
||||
|
||||
FLVMetadata::Tag *tag = new FLVMetadata::Tag;
|
||||
|
||||
// read name
|
||||
size_t skip = tag->name.Read(data, size);
|
||||
data+=skip;
|
||||
size-=skip;
|
||||
|
||||
type=*data;
|
||||
data++;
|
||||
size--;
|
||||
|
||||
if (type != AMFType::TYPE_MIXEDARRAY) // second entry is an associative array, verify this
|
||||
{
|
||||
delete tag;
|
||||
return false; // malformed, lets bail
|
||||
}
|
||||
|
||||
tag->parameters = new AMFMixedArray; // we're new'ing this because we need to reference count
|
||||
skip = tag->parameters->Read(data, size);
|
||||
data+=skip;
|
||||
size-=skip;
|
||||
tags.push_back(tag);
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
FLVMetadata::Tag::Tag() : parameters(0)
|
||||
{
|
||||
}
|
||||
FLVMetadata::Tag::~Tag()
|
||||
{
|
||||
if (parameters)
|
||||
parameters->Release();
|
||||
}
|
24
Src/Plugins/Input/in_flv/FLVMetadata.h
Normal file
24
Src/Plugins/Input/in_flv/FLVMetadata.h
Normal file
@ -0,0 +1,24 @@
|
||||
#ifndef NULLSOFT_FLVMETADATA_H
|
||||
#define NULLSOFT_FLVMETADATA_H
|
||||
|
||||
#include "AMFObject.h"
|
||||
#include <vector>
|
||||
|
||||
class FLVMetadata
|
||||
{
|
||||
public:
|
||||
FLVMetadata();
|
||||
~FLVMetadata();
|
||||
bool Read(uint8_t *data, size_t size);
|
||||
struct Tag
|
||||
{
|
||||
Tag();
|
||||
~Tag();
|
||||
|
||||
AMFString name;
|
||||
AMFMixedArray *parameters; // needs to be pointer so we can refcount
|
||||
};
|
||||
std::vector<Tag*> tags;
|
||||
|
||||
};
|
||||
#endif
|
1
Src/Plugins/Input/in_flv/FLVProcessor.cpp
Normal file
1
Src/Plugins/Input/in_flv/FLVProcessor.cpp
Normal file
@ -0,0 +1 @@
|
||||
#include "FLVProcessor.h"
|
40
Src/Plugins/Input/in_flv/FLVProcessor.h
Normal file
40
Src/Plugins/Input/in_flv/FLVProcessor.h
Normal file
@ -0,0 +1,40 @@
|
||||
#pragma once
|
||||
#include <bfc/platform/types.h>
|
||||
#include "FLVStreamHeader.h"
|
||||
#include "FLVHeader.h"
|
||||
|
||||
struct FrameData
|
||||
{
|
||||
FLVStreamHeader header;
|
||||
uint64_t location;
|
||||
bool keyFrame;
|
||||
};
|
||||
|
||||
enum
|
||||
{
|
||||
FLV_OK=0,
|
||||
FLV_NEED_MORE_DATA=1,
|
||||
FLV_ERROR=-1,
|
||||
|
||||
FLVPROCESSOR_WRITE_OK=0,
|
||||
FLVPROCESSOR_WRITE_ERROR=1,
|
||||
FLVPROCESSOR_WRITE_WAIT=2,
|
||||
};
|
||||
|
||||
class FLVProcessor
|
||||
{
|
||||
public:
|
||||
virtual ~FLVProcessor() {}
|
||||
virtual int Write(void *data, size_t datalen, size_t *written) = 0;
|
||||
virtual int Process()=0;
|
||||
virtual uint64_t Seek(uint64_t position)=0;
|
||||
virtual size_t Read(void *data, size_t bytes)=0;
|
||||
virtual uint64_t GetProcessedPosition()=0;
|
||||
virtual bool GetFrame(size_t frameIndex, FrameData &frameData)=0;
|
||||
virtual uint32_t GetMaxTimestamp()=0;
|
||||
virtual bool GetPosition(int time_in_ms, size_t *frameIndex, bool needsVideoKeyFrame)=0;
|
||||
virtual bool IsStreaming()=0;
|
||||
virtual FLVHeader *GetHeader() = 0;
|
||||
};
|
||||
|
||||
|
211
Src/Plugins/Input/in_flv/FLVReader.cpp
Normal file
211
Src/Plugins/Input/in_flv/FLVReader.cpp
Normal file
@ -0,0 +1,211 @@
|
||||
#include "FLVReader.h"
|
||||
#include "FLVHeader.h"
|
||||
#include "FLVStreamheader.h"
|
||||
#include "BackgroundDownloader.h"
|
||||
#include "../nu/AutoChar.h"
|
||||
#include "FileProcessor.h"
|
||||
#include "ProgressiveProcessor.h"
|
||||
#include "StreamProcessor.h"
|
||||
#include "../../..\Components\wac_network\wac_network_http_receiver_api.h"
|
||||
#include <shlwapi.h>
|
||||
|
||||
FLVReader::FLVReader(const wchar_t *_url)
|
||||
{
|
||||
processor = 0;
|
||||
url = _wcsdup(_url);
|
||||
end_of_stream=false;
|
||||
|
||||
killswitch=false;
|
||||
DWORD threadId;
|
||||
flvThread=CreateThread(NULL, NULL, ParserThreadStub, this, 0, &threadId);
|
||||
SetThreadPriority(flvThread, THREAD_PRIORITY_BELOW_NORMAL);
|
||||
}
|
||||
|
||||
FLVReader::~FLVReader()
|
||||
{
|
||||
free(url);
|
||||
delete processor;
|
||||
}
|
||||
|
||||
uint64_t FLVReader::Seek(uint64_t position)
|
||||
{
|
||||
if (processor)
|
||||
return processor->Seek(position);
|
||||
else
|
||||
return -1;
|
||||
}
|
||||
|
||||
size_t FLVReader::Read(void *data, size_t bytes)
|
||||
{
|
||||
if (processor)
|
||||
return processor->Read(data, bytes);
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint64_t FLVReader::GetProcessedPosition()
|
||||
{
|
||||
if (processor)
|
||||
return processor->GetProcessedPosition();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FLVReader::ProcessFile()
|
||||
{
|
||||
if (!processor)
|
||||
return;
|
||||
|
||||
while (processor->Process() == FLV_OK)
|
||||
{
|
||||
if (killswitch)
|
||||
return ;
|
||||
}
|
||||
}
|
||||
|
||||
int FLVReader::OnConnect(api_httpreceiver *http)
|
||||
{
|
||||
if (http->content_length())
|
||||
processor = new ProgressiveProcessor;
|
||||
else
|
||||
processor = new StreamProcessor;
|
||||
return 0;
|
||||
}
|
||||
|
||||
int FLVReader::OnData(void *data, size_t datalen)
|
||||
{
|
||||
if (!processor)
|
||||
return 1;
|
||||
|
||||
bool needSleep=false;
|
||||
while (datalen)
|
||||
{
|
||||
if (killswitch)
|
||||
return 1;
|
||||
|
||||
if (needSleep)
|
||||
{
|
||||
Sleep(10);
|
||||
needSleep=false;
|
||||
}
|
||||
|
||||
size_t written=0;
|
||||
switch (processor->Write(data, datalen, &written))
|
||||
{
|
||||
case FLVPROCESSOR_WRITE_ERROR:
|
||||
return 1;
|
||||
case FLVPROCESSOR_WRITE_WAIT:
|
||||
needSleep=true;
|
||||
break;
|
||||
}
|
||||
|
||||
datalen -= written;
|
||||
|
||||
if (written)
|
||||
{
|
||||
while (1)
|
||||
{
|
||||
if (killswitch)
|
||||
return 1;
|
||||
|
||||
int ret = processor->Process();
|
||||
if (ret == FLV_OK)
|
||||
continue;
|
||||
if (ret == FLV_NEED_MORE_DATA)
|
||||
break;
|
||||
if (ret == FLV_ERROR)
|
||||
return 1;
|
||||
}
|
||||
}
|
||||
}
|
||||
return !!killswitch;
|
||||
}
|
||||
|
||||
bool FLVReader::GetFrame(size_t frameIndex, FrameData &frameData)
|
||||
{
|
||||
if (processor)
|
||||
return processor->GetFrame(frameIndex, frameData);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
/*
|
||||
Test URLs (valid of as oct 23 2007)
|
||||
http://pdl.stream.aol.com/aol/us/aolmusic/artists/astralwerks/2220s/2220s_shootyourgun_hakjfh_700_dl.flv
|
||||
http://pdl.stream.aol.com/aol/us/aolmusic/sessions/2007/amberpacific/suc_amberpacific_fallbackintomylife_700_dl.flv
|
||||
http://pdl.stream.aol.com/aol/us/aolmusic/sessions/2007/amberpacific/suc_amberpacific_gonesoyoung_700_dl.flv
|
||||
http://pdl.stream.aol.com/aol/us/aolmusic/sessions/2007/amberpacific/suc_amberpacific_soyesterday_700_dl.flv
|
||||
http://pdl.stream.aol.com/aol/us/aolmusic/sessions/2007/amberpacific/suc_amberpacific_watchingoverme_700_dl.flv
|
||||
|
||||
a potential crasher:
|
||||
http://pdl.stream.aol.com/aol/us/aolcomvideo/TVGuide/johncmcginley_6346/johncmcginley_6346_460_700_dl.flv
|
||||
|
||||
FLV streams:
|
||||
http://208.80.52.96/CBS_R20_452P_F128?ext=.flv
|
||||
http://208.80.54.37/KROQFM?ext=.flv
|
||||
http://208.80.52.96/CBS_R20_568P_F128?ext=.flv
|
||||
*/
|
||||
DWORD FLVReader::ParserThread()
|
||||
{
|
||||
if (PathIsURL(url))
|
||||
{
|
||||
Downloader downloader;
|
||||
downloader.Download(AutoChar(url), this);
|
||||
}
|
||||
else
|
||||
{
|
||||
processor = new FileProcessor(url);
|
||||
ProcessFile();
|
||||
}
|
||||
end_of_stream=true;
|
||||
return 0;
|
||||
}
|
||||
|
||||
void FLVReader::Kill()
|
||||
{
|
||||
killswitch=true;
|
||||
WaitForSingleObject(flvThread, INFINITE);
|
||||
CloseHandle(flvThread);
|
||||
}
|
||||
|
||||
void FLVReader::SignalKill()
|
||||
{
|
||||
killswitch=true;
|
||||
}
|
||||
|
||||
bool FLVReader::IsEOF()
|
||||
{
|
||||
return end_of_stream;
|
||||
}
|
||||
|
||||
bool FLVReader::GetPosition(int time_in_ms, size_t *frameIndex, bool needVideoKeyFrame)
|
||||
{
|
||||
if (processor)
|
||||
return processor->GetPosition(time_in_ms, frameIndex, needVideoKeyFrame);
|
||||
else
|
||||
return false;
|
||||
}
|
||||
|
||||
uint32_t FLVReader::GetMaxTimestamp()
|
||||
{
|
||||
if (processor)
|
||||
return processor->GetMaxTimestamp();
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool FLVReader::IsStreaming()
|
||||
{
|
||||
if (processor)
|
||||
return processor->IsStreaming();
|
||||
else
|
||||
return true;
|
||||
}
|
||||
|
||||
FLVHeader *FLVReader::GetHeader()
|
||||
{
|
||||
if (processor)
|
||||
return processor->GetHeader();
|
||||
else
|
||||
return 0;
|
||||
}
|
49
Src/Plugins/Input/in_flv/FLVReader.h
Normal file
49
Src/Plugins/Input/in_flv/FLVReader.h
Normal file
@ -0,0 +1,49 @@
|
||||
#ifndef NULLSOFT_IN_FLV_FLVREADER_H
|
||||
#define NULLSOFT_IN_FLV_FLVREADER_H
|
||||
|
||||
#include <windows.h>
|
||||
#include <bfc/platform/types.h>
|
||||
#include "FLVStreamheader.h"
|
||||
#include "../nu/AutoLock.h"
|
||||
#include "BackgroundDownloader.h"
|
||||
#include "FLVProcessor.h"
|
||||
|
||||
class FLVReader : private Downloader::DownloadCallback
|
||||
{
|
||||
public:
|
||||
FLVReader(const wchar_t *_url);
|
||||
~FLVReader();
|
||||
|
||||
bool GetFrame(size_t frameIndex, FrameData &frameData);
|
||||
bool GetPosition(int time_in_ms, size_t *frameIndex, bool needVideoKeyFrame);
|
||||
uint64_t Seek(uint64_t position);
|
||||
size_t Read(void *data, size_t bytes);
|
||||
void Kill();
|
||||
void SignalKill();
|
||||
bool IsEOF();
|
||||
uint32_t GetMaxTimestamp();
|
||||
uint64_t GetProcessedPosition();
|
||||
bool IsStreaming();
|
||||
FLVHeader *GetHeader();
|
||||
private:
|
||||
void ProcessFile();
|
||||
int OnConnect(api_httpreceiver *http);
|
||||
int OnData(void *data, size_t datalen);
|
||||
int Process();
|
||||
DWORD CALLBACK ParserThread();
|
||||
static DWORD CALLBACK ParserThreadStub(LPVOID param) { return ((FLVReader *)param)->ParserThread(); }
|
||||
|
||||
private:
|
||||
bool killswitch;
|
||||
|
||||
HANDLE flvThread;
|
||||
bool end_of_stream;
|
||||
wchar_t *url;
|
||||
|
||||
/* because we won't know until after opening the stream whether it's progressive download
|
||||
or a real-time stream, we need have a pointer to a virtual base class to do the processing
|
||||
we'll create a different one depending on what kind of stream */
|
||||
FLVProcessor *processor;
|
||||
};
|
||||
|
||||
#endif
|
36
Src/Plugins/Input/in_flv/FLVStreamHeader.cpp
Normal file
36
Src/Plugins/Input/in_flv/FLVStreamHeader.cpp
Normal file
@ -0,0 +1,36 @@
|
||||
#include "FLVStreamHeader.h"
|
||||
#include "FLVUtil.h"
|
||||
/*
|
||||
(c) 2006 Nullsoft, Inc.
|
||||
Author: Ben Allison benski@nullsoft.com
|
||||
*/
|
||||
|
||||
/*
|
||||
PreviousTagSize - uint32 - total size of previous tag, presumably for stream continuity checking. big endian
|
||||
Type - uint8 - what does this frame contain? 0x12=meta, 0x8=audio, 0x9=video
|
||||
BodyLength - uint24 - size of the data following this header. big endian
|
||||
Timestamp - uint24 - timestamp (milliseconds). big endian
|
||||
Timestamp High - uint8 - high 8 bits of timestamp (to form 32bit timestamp)
|
||||
Stream ID - uint24 - always zero
|
||||
*/
|
||||
|
||||
bool FLVStreamHeader::Read(uint8_t *data, size_t size)
|
||||
{
|
||||
if (size < 15)
|
||||
return false; // header size too small
|
||||
|
||||
previousSize = FLV::Read32(&data[0]);
|
||||
type = data[4];
|
||||
dataSize = FLV::Read24(&data[5]);
|
||||
timestamp = FLV::Read24(&data[8]);
|
||||
uint8_t timestampHigh = FLV::Read8(&data[11]);
|
||||
timestamp |= (timestampHigh << 24);
|
||||
streamID = FLV::Read24(&data[12]);
|
||||
|
||||
if (streamID != 0)
|
||||
return false;
|
||||
|
||||
return true;
|
||||
|
||||
|
||||
}
|
27
Src/Plugins/Input/in_flv/FLVStreamHeader.h
Normal file
27
Src/Plugins/Input/in_flv/FLVStreamHeader.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef NULLSOFT_FLVSTREAMHEADER_H
|
||||
#define NULLSOFT_FLVSTREAMHEADER_H
|
||||
|
||||
#include <bfc/platform/types.h>
|
||||
namespace FLV
|
||||
{
|
||||
enum
|
||||
{
|
||||
FRAME_TYPE_AUDIO = 0x8,
|
||||
FRAME_TYPE_VIDEO = 0x9,
|
||||
FRAME_TYPE_METADATA = 0x12,
|
||||
};
|
||||
}
|
||||
class FLVStreamHeader
|
||||
{
|
||||
public:
|
||||
bool Read(unsigned __int8 *data, size_t size); // size must be >=15, returns "true" if this was a valid header
|
||||
|
||||
// attributes, consider these read-only
|
||||
uint32_t previousSize;
|
||||
uint8_t type;
|
||||
uint32_t dataSize;
|
||||
uint32_t timestamp;
|
||||
uint32_t streamID;
|
||||
};
|
||||
|
||||
#endif
|
90
Src/Plugins/Input/in_flv/FLVUtil.h
Normal file
90
Src/Plugins/Input/in_flv/FLVUtil.h
Normal file
@ -0,0 +1,90 @@
|
||||
#ifndef NULLSOFT_FLVUTIL_H
|
||||
#define NULLSOFT_FLVUTIL_H
|
||||
|
||||
#include <memory.h>
|
||||
#include <bfc/platform/types.h>
|
||||
namespace FLV
|
||||
{
|
||||
// reads 32 bits from data and converts from big endian
|
||||
inline uint32_t Read32(uint8_t *data)
|
||||
{
|
||||
uint32_t returnVal;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
returnVal = *(uint32_t *)(&data[0]);
|
||||
#else
|
||||
// need to swap endianness
|
||||
uint8_t *swap = (uint8_t *)&returnVal;
|
||||
swap[0]=data[3];
|
||||
swap[1]=data[2];
|
||||
swap[2]=data[1];
|
||||
swap[3]=data[0];
|
||||
#endif
|
||||
return returnVal;
|
||||
|
||||
}
|
||||
// reads 24 bits from data, converts from big endian, and returns as a 32bit int
|
||||
inline uint32_t Read24(uint8_t *data)
|
||||
{
|
||||
uint32_t returnVal=0;
|
||||
uint8_t *swap = (uint8_t *)&returnVal;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
swap[1]=data[0];
|
||||
swap[2]=data[1];
|
||||
swap[3]=data[2];
|
||||
returnVal = *(uint32_t *)(&data[0]);
|
||||
#else
|
||||
// need to swap endianness
|
||||
swap[0]=data[2];
|
||||
swap[1]=data[1];
|
||||
swap[2]=data[0];
|
||||
#endif
|
||||
return returnVal;
|
||||
|
||||
}
|
||||
|
||||
// reads 16 bits from data and converts from big endian
|
||||
inline uint16_t Read16(uint8_t *data)
|
||||
{
|
||||
uint16_t returnVal;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
returnVal = *(uint16_t *)(&data[0]);
|
||||
#else
|
||||
// need to swap endianness
|
||||
uint8_t *swap = (uint8_t *)&returnVal;
|
||||
swap[0]=data[1];
|
||||
swap[1]=data[0];
|
||||
#endif
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
// reads 16 bits from data and converts from big endian
|
||||
inline uint8_t Read8(uint8_t *data)
|
||||
{
|
||||
uint8_t returnVal;
|
||||
|
||||
returnVal = *(uint8_t *)(&data[0]);
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
// reads a double from data
|
||||
inline double ReadDouble(uint8_t *data)
|
||||
{
|
||||
double returnVal;
|
||||
#ifdef __BIG_ENDIAN__
|
||||
memcpy(&returnVal, data, 8);
|
||||
#else
|
||||
uint8_t *swap = (uint8_t *)&returnVal;
|
||||
swap[0]=data[7];
|
||||
swap[1]=data[6];
|
||||
swap[2]=data[5];
|
||||
swap[3]=data[4];
|
||||
swap[4]=data[3];
|
||||
swap[5]=data[2];
|
||||
swap[6]=data[1];
|
||||
swap[7]=data[0];
|
||||
#endif
|
||||
return returnVal;
|
||||
}
|
||||
|
||||
}
|
||||
#endif
|
24
Src/Plugins/Input/in_flv/FLVVideoHeader.cpp
Normal file
24
Src/Plugins/Input/in_flv/FLVVideoHeader.cpp
Normal file
@ -0,0 +1,24 @@
|
||||
#include "FLVVideoHeader.h"
|
||||
|
||||
/*
|
||||
(c) 2006 Nullsoft, Inc.
|
||||
Author: Ben Allison benski@nullsoft.com
|
||||
*/
|
||||
|
||||
/*
|
||||
codecID - 4 bits - 2: Sorensen H.263, 3: Screen video, 4: On2 VP6
|
||||
frameType - 4 bits - 1: keyframe, 2: inter frame, 3: disposable inter frame
|
||||
*/
|
||||
|
||||
bool FLVVideoHeader::Read(unsigned __int8 *data, size_t size)
|
||||
{
|
||||
if (size < 1)
|
||||
return false; // header size too small
|
||||
|
||||
unsigned __int8 byte = data[0];
|
||||
|
||||
format = (byte & 0x0f) >> 0;
|
||||
frameType = (byte & 0xf0) >> 4;
|
||||
|
||||
return true;
|
||||
}
|
32
Src/Plugins/Input/in_flv/FLVVideoHeader.h
Normal file
32
Src/Plugins/Input/in_flv/FLVVideoHeader.h
Normal file
@ -0,0 +1,32 @@
|
||||
#ifndef NULLSOFT_FLVVIDEOHEADER_H
|
||||
#define NULLSOFT_FLVVIDEOHEADER_H
|
||||
|
||||
namespace FLV
|
||||
{
|
||||
const int VIDEO_FORMAT_JPEG = 1;
|
||||
const int VIDEO_FORMAT_SORENSON = 2; // H.263
|
||||
const int VIDEO_FORMAT_SCREEN = 3;
|
||||
const int VIDEO_FORMAT_VP6 = 4;
|
||||
const int VIDEO_FORMAT_VP62 = 5;
|
||||
const int VIDEO_FORMAT_SCREEN_V2 = 6;
|
||||
const int VIDEO_FORMAT_AVC = 7; // MPEG-4 Part 10
|
||||
|
||||
const int VIDEO_FRAMETYPE_KEYFRAME = 1;
|
||||
const int VIDEO_FRAMETYPE_IFRAME = 2;
|
||||
const int VIDEO_FRAMETYPE_IFRAME_DISPOSABLE = 3;
|
||||
const int VIDEO_FRAMETYPE_GENERATED = 4;
|
||||
const int VIDEO_FRAMETYPE_INFO = 5;
|
||||
};
|
||||
|
||||
|
||||
class FLVVideoHeader
|
||||
{
|
||||
public:
|
||||
bool Read(unsigned __int8 *data, size_t size); // size must be >=1, returns "true" if this was a valid header
|
||||
|
||||
// attributes, consider these read-only
|
||||
int format;
|
||||
int frameType;
|
||||
};
|
||||
|
||||
#endif
|
201
Src/Plugins/Input/in_flv/FileProcessor.cpp
Normal file
201
Src/Plugins/Input/in_flv/FileProcessor.cpp
Normal file
@ -0,0 +1,201 @@
|
||||
#include "FileProcessor.h"
|
||||
#include "FLVHeader.h"
|
||||
#include "FLVVideoHeader.h"
|
||||
|
||||
static int64_t Seek64(HANDLE hf, int64_t distance, DWORD MoveMethod)
|
||||
{
|
||||
LARGE_INTEGER li;
|
||||
li.QuadPart = distance;
|
||||
li.LowPart = SetFilePointer(hf, li.LowPart, &li.HighPart, MoveMethod);
|
||||
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
|
||||
{
|
||||
li.QuadPart = -1;
|
||||
}
|
||||
|
||||
return li.QuadPart;
|
||||
}
|
||||
|
||||
int64_t FileSize64(HANDLE file)
|
||||
{
|
||||
LARGE_INTEGER position;
|
||||
position.QuadPart=0;
|
||||
position.LowPart = GetFileSize(file, (LPDWORD)&position.HighPart);
|
||||
|
||||
if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
|
||||
return INVALID_FILE_SIZE;
|
||||
else
|
||||
return position.QuadPart;
|
||||
}
|
||||
|
||||
FileProcessor::FileProcessor()
|
||||
{
|
||||
Init();
|
||||
}
|
||||
|
||||
FileProcessor::FileProcessor(const wchar_t *filename)
|
||||
{
|
||||
Init();
|
||||
processedCursor=CreateFile(filename, GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
readCursor=CreateFile(filename, GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
writePosition=FileSize64(readCursor);
|
||||
}
|
||||
|
||||
FileProcessor::~FileProcessor()
|
||||
{
|
||||
if (processedCursor != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(processedCursor);
|
||||
if (readCursor != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(readCursor);
|
||||
}
|
||||
|
||||
void FileProcessor::Init()
|
||||
{
|
||||
processedPosition=0;
|
||||
processedCursor=INVALID_HANDLE_VALUE;
|
||||
writePosition=0;
|
||||
readCursor=INVALID_HANDLE_VALUE;
|
||||
flen=0;
|
||||
maxTimeStamp=0;
|
||||
headerOK=false;
|
||||
}
|
||||
|
||||
int FileProcessor::Process()
|
||||
{
|
||||
int64_t oldPosition = Seek64(processedCursor, 0, FILE_CURRENT);
|
||||
|
||||
// read file header if we're at the beginning
|
||||
if (processedPosition == 0)
|
||||
{
|
||||
if (writePosition-processedPosition >= 9) // need at least nine bytes for the FLV file header
|
||||
{
|
||||
uint8_t data[9] = {0};
|
||||
DWORD bytesRead=0;
|
||||
ReadFile(processedCursor, data, 9, &bytesRead, NULL);
|
||||
if (header.Read(data, bytesRead))
|
||||
{
|
||||
headerOK=true;
|
||||
Nullsoft::Utility::AutoLock lock(frameGuard);
|
||||
processedPosition += bytesRead;
|
||||
return FLV_OK; // we'll make our logic just a little bit more sane by only processing one frame per pass.
|
||||
}
|
||||
else
|
||||
return FLV_ERROR;
|
||||
}
|
||||
|
||||
Seek64(processedCursor, oldPosition, FILE_BEGIN); // rollback
|
||||
return FLV_NEED_MORE_DATA;
|
||||
}
|
||||
|
||||
if (writePosition-processedPosition >= 15) // need at least fifteen bytes for the FLV frame header
|
||||
{
|
||||
uint8_t data[15] = {0};
|
||||
DWORD bytesRead=0;
|
||||
ReadFile(processedCursor, data, 15, &bytesRead, NULL);
|
||||
FrameData frameData;
|
||||
if (frameData.header.Read(data, bytesRead))
|
||||
{
|
||||
if (frameData.header.dataSize + processedPosition + 15 <= writePosition)
|
||||
{
|
||||
frameData.keyFrame = false;
|
||||
if (frameData.header.type == FLV::FRAME_TYPE_VIDEO)
|
||||
{
|
||||
FLVVideoHeader videoHeader;
|
||||
DWORD videoHeaderRead=0;
|
||||
ReadFile(processedCursor, data, 1, &videoHeaderRead, NULL);
|
||||
if (videoHeader.Read(data, bytesRead))
|
||||
{
|
||||
if (videoHeader.frameType == FLV::VIDEO_FRAMETYPE_KEYFRAME)
|
||||
{
|
||||
frameData.keyFrame = true;
|
||||
}
|
||||
}
|
||||
Seek64(processedCursor, -(int64_t)videoHeaderRead, FILE_CURRENT);
|
||||
// roll back file cursor from ReadFile
|
||||
}
|
||||
|
||||
{ // critsec enter
|
||||
Nullsoft::Utility::AutoLock lock(frameGuard);
|
||||
// record the offset where we found it
|
||||
frameData.location = processedPosition;
|
||||
maxTimeStamp=max(maxTimeStamp, frameData.header.timestamp);
|
||||
frames.push_back(frameData);
|
||||
} // critsec leave
|
||||
Seek64(processedCursor, frameData.header.dataSize, FILE_CURRENT);
|
||||
|
||||
Nullsoft::Utility::AutoLock lock(frameGuard);
|
||||
processedPosition+=bytesRead+frameData.header.dataSize;
|
||||
return FLV_OK;
|
||||
}
|
||||
Seek64(processedCursor, oldPosition, FILE_BEGIN); // rollback
|
||||
return FLV_NEED_MORE_DATA;
|
||||
}
|
||||
else
|
||||
{
|
||||
return FLV_ERROR;
|
||||
}
|
||||
}
|
||||
Seek64(processedCursor, oldPosition, FILE_BEGIN); // rollback
|
||||
return FLV_NEED_MORE_DATA;
|
||||
}
|
||||
|
||||
uint32_t FileProcessor::GetMaxTimestamp()
|
||||
{
|
||||
return maxTimeStamp;
|
||||
}
|
||||
|
||||
bool FileProcessor::GetFrame(size_t frameIndex, FrameData &frameData)
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(frameGuard);
|
||||
if (frameIndex < frames.size())
|
||||
{
|
||||
frameData = frames[frameIndex];
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
uint64_t FileProcessor::GetProcessedPosition()
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(frameGuard);
|
||||
return processedPosition;
|
||||
}
|
||||
|
||||
size_t FileProcessor::Read(void *data, size_t bytes)
|
||||
{
|
||||
DWORD bytesRead = 0;
|
||||
ReadFile(readCursor, data, (DWORD)bytes, &bytesRead, NULL);
|
||||
return bytesRead;
|
||||
}
|
||||
|
||||
uint64_t FileProcessor::Seek(uint64_t position)
|
||||
{
|
||||
return Seek64(readCursor, position, FILE_BEGIN);
|
||||
}
|
||||
|
||||
bool FileProcessor::GetPosition(int time_in_ms, size_t *frameIndex, bool needVideoKeyFrame)
|
||||
{
|
||||
Nullsoft::Utility::AutoLock lock(frameGuard);
|
||||
// TODO: binary search
|
||||
for (size_t f=0;f!=frames.size();f++)
|
||||
{
|
||||
if (frames[f].header.timestamp >= (uint32_t)time_in_ms)
|
||||
{
|
||||
if (needVideoKeyFrame)
|
||||
{
|
||||
while (f != 0 && !frames[f].keyFrame)
|
||||
f--;
|
||||
}
|
||||
*frameIndex = f;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
FLVHeader *FileProcessor::GetHeader()
|
||||
{
|
||||
if (headerOK)
|
||||
return &header;
|
||||
else
|
||||
return 0;
|
||||
}
|
43
Src/Plugins/Input/in_flv/FileProcessor.h
Normal file
43
Src/Plugins/Input/in_flv/FileProcessor.h
Normal file
@ -0,0 +1,43 @@
|
||||
#pragma once
|
||||
#include "FLVProcessor.h"
|
||||
#include <windows.h>
|
||||
#include <vector>
|
||||
#include "../nu/AutoLock.h"
|
||||
|
||||
class FileProcessor : public FLVProcessor
|
||||
{
|
||||
public:
|
||||
FileProcessor(const wchar_t *filename);
|
||||
~FileProcessor();
|
||||
private:
|
||||
/* FLVProcessor virtual method overrides */
|
||||
int Write(void *data, size_t datalen, size_t *written) { return 1; }
|
||||
uint64_t GetProcessedPosition();
|
||||
uint32_t GetMaxTimestamp();
|
||||
bool GetPosition(int time_in_ms, size_t *frameIndex, bool needVideoKeyFrame);
|
||||
bool IsStreaming() { return false; }
|
||||
FLVHeader *GetHeader();
|
||||
public:
|
||||
/* FLVProcessor virtual method overrides, continued
|
||||
(these are public so FileProcessor can be used standalone */
|
||||
int Process();
|
||||
uint64_t Seek(uint64_t position);
|
||||
size_t Read(void *data, size_t bytes);
|
||||
bool GetFrame(size_t frameIndex, FrameData &frameData);
|
||||
|
||||
protected:
|
||||
FileProcessor();
|
||||
uint64_t processedPosition;
|
||||
uint64_t writePosition;
|
||||
HANDLE processedCursor, readCursor;
|
||||
// file positions of samples
|
||||
std::vector<FrameData> frames;
|
||||
Nullsoft::Utility::LockGuard frameGuard;
|
||||
uint64_t flen;
|
||||
|
||||
private:
|
||||
void Init();
|
||||
uint32_t maxTimeStamp;
|
||||
FLVHeader header;
|
||||
bool headerOK;
|
||||
};
|
703
Src/Plugins/Input/in_flv/PlayThread.cpp
Normal file
703
Src/Plugins/Input/in_flv/PlayThread.cpp
Normal file
@ -0,0 +1,703 @@
|
||||
#include "main.h"
|
||||
#include <windows.h>
|
||||
#include "api__in_flv.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "FLVHeader.h"
|
||||
#include "FLVStreamHeader.h"
|
||||
#include "FLVAudioHeader.h"
|
||||
#include "FLVVideoHeader.h"
|
||||
#include "FLVMetadata.h"
|
||||
#include <malloc.h>
|
||||
#include <stdio.h>
|
||||
#include <shlwapi.h>
|
||||
#include "VideoThread.h"
|
||||
#include "FLVReader.h"
|
||||
#include "resource.h"
|
||||
#include "FLVCOM.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include "ifc_flvaudiodecoder.h"
|
||||
#include "svc_flvdecoder.h"
|
||||
#include "../nu/AudioOutput.h"
|
||||
|
||||
#define PRE_BUFFER_MS 5000.0
|
||||
#define PRE_BUFFER_MAX (1024*1024)
|
||||
|
||||
uint32_t last_timestamp=0;
|
||||
static bool audioOpened;
|
||||
int bufferCount;
|
||||
static int bits, channels, sampleRate;
|
||||
static double dataRate_audio, dataRate_video;
|
||||
static double dataRate;
|
||||
static uint64_t prebuffer;
|
||||
bool mute=false;
|
||||
uint32_t first_timestamp;
|
||||
static size_t audio_buffered=0;
|
||||
void VideoStop();
|
||||
static ifc_flvaudiodecoder *audioDecoder=0;
|
||||
static bool checked_in_swf=false;
|
||||
extern bool video_only;
|
||||
|
||||
class FLVWait
|
||||
{
|
||||
public:
|
||||
int WaitOrAbort(int len)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, len) == WAIT_OBJECT_0)
|
||||
return 1;
|
||||
|
||||
return 0;
|
||||
}
|
||||
};
|
||||
|
||||
static nu::AudioOutput<FLVWait> outputter(&plugin);
|
||||
|
||||
static void Buffering(int bufStatus, const wchar_t *displayString)
|
||||
{
|
||||
if (bufStatus < 0 || bufStatus > 100)
|
||||
return;
|
||||
|
||||
char tempdata[75*2] = {0, };
|
||||
|
||||
int csa = plugin.SAGetMode();
|
||||
if (csa & 1)
|
||||
{
|
||||
for (int x = 0; x < bufStatus*75 / 100; x ++)
|
||||
tempdata[x] = x * 16 / 75;
|
||||
}
|
||||
else if (csa&2)
|
||||
{
|
||||
int offs = (csa & 1) ? 75 : 0;
|
||||
int x = 0;
|
||||
while (x < bufStatus*75 / 100)
|
||||
{
|
||||
tempdata[offs + x++] = -6 + x * 14 / 75;
|
||||
}
|
||||
while (x < 75)
|
||||
{
|
||||
tempdata[offs + x++] = 0;
|
||||
}
|
||||
}
|
||||
else if (csa == 4)
|
||||
{
|
||||
tempdata[0] = tempdata[1] = (bufStatus * 127 / 100);
|
||||
}
|
||||
if (csa) plugin.SAAdd(tempdata, ++bufferCount, (csa == 3) ? 0x80000003 : csa);
|
||||
|
||||
/*
|
||||
TODO
|
||||
wchar_t temp[64] = {0};
|
||||
StringCchPrintf(temp, 64, L"%s: %d%%",displayString, bufStatus);
|
||||
SetStatus(temp);
|
||||
*/
|
||||
//SetVideoStatusText(temp); // TODO: find a way to set the old status back
|
||||
videoOutput->notifyBufferState(static_cast<int>(bufStatus*2.55f));
|
||||
}
|
||||
|
||||
static bool Audio_IsSupported(int type)
|
||||
{
|
||||
size_t n = 0;
|
||||
waServiceFactory *factory = NULL;
|
||||
while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
|
||||
{
|
||||
svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
|
||||
if (creator)
|
||||
{
|
||||
int supported = creator->HandlesAudio(type);
|
||||
factory->releaseInterface(creator);
|
||||
if (supported == svc_flvdecoder::CREATEDECODER_SUCCESS)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ifc_flvaudiodecoder *CreateAudioDecoder(const FLVAudioHeader &header)
|
||||
{
|
||||
ifc_flvaudiodecoder *audio_decoder=0;
|
||||
size_t n=0;
|
||||
waServiceFactory *factory = NULL;
|
||||
while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
|
||||
{
|
||||
svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
|
||||
if (creator)
|
||||
{
|
||||
if (creator->CreateAudioDecoder(header.stereo, header.bits, header.sampleRate, header.format, &audio_decoder) == FLV_AUDIO_SUCCESS)
|
||||
return audio_decoder;
|
||||
|
||||
factory->releaseInterface(creator);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
void OnStart()
|
||||
{
|
||||
Video_Init();
|
||||
|
||||
audioOpened = false;
|
||||
audioDecoder = 0;
|
||||
|
||||
bufferCount=0;
|
||||
mute = false;
|
||||
audio_buffered=0;
|
||||
|
||||
if (!videoOutput)
|
||||
videoOutput = (IVideoOutput *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
|
||||
}
|
||||
|
||||
void Audio_Close()
|
||||
{
|
||||
if (audioDecoder)
|
||||
audioDecoder->Close();
|
||||
audioDecoder=0;
|
||||
}
|
||||
|
||||
static bool OpenAudio(unsigned int sampleRate, unsigned int channels, unsigned int bits)
|
||||
{
|
||||
if (!outputter.Open(0, channels, sampleRate, bits, -666))
|
||||
return false;
|
||||
audioOpened = true;
|
||||
return true;
|
||||
}
|
||||
|
||||
static void DoBuffer(FLVReader &reader)
|
||||
{
|
||||
// TODO: we should pre-buffer after getting the first audio + video frames
|
||||
// so we can estimate bitrate
|
||||
for (;;)
|
||||
{
|
||||
uint64_t processedPosition = reader.GetProcessedPosition();
|
||||
if (processedPosition < prebuffer && !reader.IsEOF())
|
||||
{
|
||||
Buffering((int)((100ULL * processedPosition) / prebuffer), WASABI_API_LNGSTRINGW(IDS_BUFFERING));
|
||||
if (WaitForSingleObject(killswitch, 100) == WAIT_OBJECT_0)
|
||||
break;
|
||||
else
|
||||
continue;
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
char pcmdata[65536] = {0};
|
||||
size_t outlen = 32768;
|
||||
|
||||
static uint32_t audio_type;
|
||||
static void OnAudio(FLVReader &reader, void *data, size_t length, const FLVAudioHeader &header)
|
||||
{
|
||||
if (!audioDecoder)
|
||||
{
|
||||
audioDecoder = CreateAudioDecoder(header);
|
||||
audio_type = header.format;
|
||||
video_only=false;
|
||||
}
|
||||
|
||||
if (audioDecoder)
|
||||
{
|
||||
|
||||
if (!audioDecoder->Ready())
|
||||
{
|
||||
//first_timestamp = -1;
|
||||
}
|
||||
|
||||
outlen = sizeof(pcmdata)/2 - audio_buffered;
|
||||
double bitrate = 0;
|
||||
int ret = audioDecoder->DecodeSample(data, length, pcmdata+audio_buffered, &outlen, &bitrate);
|
||||
if (ret == FLV_AUDIO_SUCCESS)
|
||||
{
|
||||
outlen+=audio_buffered;
|
||||
audio_buffered=0;
|
||||
if (bitrate && dataRate_audio != bitrate)
|
||||
{
|
||||
dataRate_audio = bitrate;
|
||||
dataRate = dataRate_audio + dataRate_video;
|
||||
plugin.SetInfo((int)dataRate, -1, -1, 1);
|
||||
}
|
||||
|
||||
if (!audioOpened)
|
||||
{
|
||||
// pre-populate values for decoders that use the header info (e.g. ADPCM)
|
||||
sampleRate=header.sampleRate;
|
||||
channels = header.stereo?2:1;
|
||||
bits = header.bits;
|
||||
|
||||
if (audioDecoder->GetOutputFormat((unsigned int *)&sampleRate, (unsigned int *)&channels, (unsigned int *)&bits) == FLV_AUDIO_SUCCESS)
|
||||
{
|
||||
|
||||
// buffer (if needed)
|
||||
prebuffer = (uint64_t)(dataRate * PRE_BUFFER_MS / 8.0);
|
||||
if (prebuffer > PRE_BUFFER_MAX)
|
||||
prebuffer=PRE_BUFFER_MAX;
|
||||
DoBuffer(reader); // benski> admittedly a crappy place to call this
|
||||
if (WaitForSingleObject(killswitch, 0) == WAIT_OBJECT_0)
|
||||
return;
|
||||
|
||||
OpenAudio(sampleRate, channels, bits);
|
||||
}
|
||||
}
|
||||
|
||||
if (mute)
|
||||
{
|
||||
if (bits == 8) // 8 bit is signed so 128 is zero voltage
|
||||
memset(pcmdata, 0x80, outlen);
|
||||
else
|
||||
memset(pcmdata, 0, outlen);
|
||||
}
|
||||
|
||||
if (audioOpened && outlen)
|
||||
{
|
||||
outputter.Write(pcmdata, outlen);
|
||||
}
|
||||
else
|
||||
{
|
||||
audio_buffered=outlen;
|
||||
}
|
||||
|
||||
}
|
||||
else if (ret == FLV_AUDIO_NEEDS_MORE_INPUT)
|
||||
{
|
||||
plugin.SetInfo(-1, -1, -1, 0);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
#define PREBUFFER_BYTES 2048ULL
|
||||
|
||||
|
||||
enum
|
||||
{
|
||||
CODEC_CHECK_NONE=0,
|
||||
CODEC_CHECK_AUDIO=1,
|
||||
CODEC_CHECK_VIDEO=2,
|
||||
CODEC_CHECK_UNSURE = -1,
|
||||
};
|
||||
|
||||
static bool CheckSWF()
|
||||
{
|
||||
if (!checked_in_swf)
|
||||
{
|
||||
const wchar_t *pluginsDir = (const wchar_t *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GETPLUGINDIRECTORYW);
|
||||
wchar_t in_swf_path[MAX_PATH] = {0};
|
||||
PathCombineW(in_swf_path, pluginsDir, L"in_swf.dll");
|
||||
in_swf = LoadLibraryW(in_swf_path);
|
||||
checked_in_swf = true;
|
||||
}
|
||||
return !!in_swf;
|
||||
}
|
||||
|
||||
static void CALLBACK SWFAPC(ULONG_PTR param)
|
||||
{
|
||||
if (in_swf)
|
||||
{
|
||||
typedef In_Module *(*MODULEGETTER)();
|
||||
MODULEGETTER moduleGetter=0;
|
||||
moduleGetter = (MODULEGETTER)GetProcAddress(in_swf, "winampGetInModule2");
|
||||
if (moduleGetter)
|
||||
swf_mod = moduleGetter();
|
||||
}
|
||||
|
||||
if (swf_mod)
|
||||
{
|
||||
if (swf_mod->Play(playFile))
|
||||
swf_mod=0;
|
||||
}
|
||||
|
||||
if (!swf_mod)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
}
|
||||
}
|
||||
|
||||
static bool Audio_DecoderReady()
|
||||
{
|
||||
if (!audioDecoder)
|
||||
return true;
|
||||
|
||||
return !!audioDecoder->Ready();
|
||||
}
|
||||
|
||||
static bool DecodersReady()
|
||||
{
|
||||
return Audio_DecoderReady() && Video_DecoderReady();
|
||||
}
|
||||
|
||||
DWORD CALLBACK PlayProcedure(LPVOID param)
|
||||
{
|
||||
int needCodecCheck=CODEC_CHECK_UNSURE;
|
||||
int missingCodecs=CODEC_CHECK_NONE;
|
||||
size_t codecCheckFrame=0;
|
||||
|
||||
dataRate_audio=0;
|
||||
dataRate_video=0;
|
||||
dataRate=0;
|
||||
first_timestamp=-1;
|
||||
|
||||
outputter.Init(plugin.outMod);
|
||||
|
||||
FLVReader reader(playFile);
|
||||
size_t i=0;
|
||||
bool hasDuration=false;
|
||||
|
||||
OnStart();
|
||||
plugin.is_seekable=0;
|
||||
|
||||
prebuffer = PREBUFFER_BYTES;
|
||||
bool first_frame_parsed=false;
|
||||
|
||||
for (;;)
|
||||
{
|
||||
DoBuffer(reader);
|
||||
|
||||
if (WaitForSingleObject(killswitch, paused?100:0) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
if (paused)
|
||||
continue;
|
||||
|
||||
if (m_need_seek != -1 && DecodersReady() && first_frame_parsed)
|
||||
{
|
||||
if (reader.GetPosition(m_need_seek, &i, video_opened))
|
||||
{
|
||||
VideoFlush();
|
||||
if (audioDecoder)
|
||||
audioDecoder->Flush();
|
||||
FrameData frameData;
|
||||
reader.GetFrame(i, frameData);
|
||||
outputter.Flush(frameData.header.timestamp);
|
||||
|
||||
if (video_only)
|
||||
{
|
||||
video_clock.Seek(frameData.header.timestamp);
|
||||
}
|
||||
}
|
||||
uint32_t first_timestamp = 0;
|
||||
m_need_seek=-1;
|
||||
}
|
||||
|
||||
// update the movie length
|
||||
if (!hasDuration)
|
||||
{
|
||||
if (reader.IsStreaming())
|
||||
{
|
||||
hasDuration=true;
|
||||
g_length = -1000;
|
||||
plugin.is_seekable=0;
|
||||
}
|
||||
else
|
||||
{
|
||||
g_length=reader.GetMaxTimestamp();
|
||||
if (g_length != -1000)
|
||||
plugin.is_seekable=1;
|
||||
}
|
||||
PostMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
|
||||
}
|
||||
|
||||
/** if it's a local file or an HTTP asset with content-length
|
||||
** we verify that the FLV has codecs we can decode
|
||||
** depending on settings, we will do one of the following with unsupport assets
|
||||
** 1) Play anyway (e.g. an H.264 video might play audio only)
|
||||
** 2) Use in_swf to play back
|
||||
** 3) skip
|
||||
**/
|
||||
|
||||
if (needCodecCheck == CODEC_CHECK_UNSURE)
|
||||
{
|
||||
FLVHeader *header = reader.GetHeader();
|
||||
if (header)
|
||||
{
|
||||
needCodecCheck=CODEC_CHECK_NONE;
|
||||
if (!reader.IsStreaming())
|
||||
{
|
||||
if (header->hasVideo)
|
||||
needCodecCheck |= CODEC_CHECK_VIDEO;
|
||||
if (header->hasAudio)
|
||||
needCodecCheck |= CODEC_CHECK_AUDIO;
|
||||
|
||||
}
|
||||
if (!header->hasAudio)
|
||||
{
|
||||
video_only=true;
|
||||
video_clock.Start();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if (needCodecCheck)
|
||||
{
|
||||
FrameData frameData;
|
||||
if (reader.GetFrame(codecCheckFrame, frameData))
|
||||
{
|
||||
FLVStreamHeader &frameHeader = frameData.header;
|
||||
if ((needCodecCheck & CODEC_CHECK_AUDIO) && frameHeader.type == FLV::FRAME_TYPE_AUDIO)
|
||||
{
|
||||
reader.Seek(frameData.location+15); // TODO: check for -1 return value
|
||||
|
||||
uint8_t data[1] = {0};
|
||||
FLVAudioHeader audioHeader;
|
||||
size_t bytesRead = reader.Read(data, 1);
|
||||
if (audioHeader.Read(data, bytesRead))
|
||||
{
|
||||
if (Audio_IsSupported(audioHeader.format))
|
||||
{
|
||||
needCodecCheck &= ~CODEC_CHECK_AUDIO;
|
||||
}
|
||||
else
|
||||
{
|
||||
needCodecCheck &= ~CODEC_CHECK_AUDIO;
|
||||
missingCodecs|=CODEC_CHECK_AUDIO;
|
||||
video_only=true;
|
||||
}
|
||||
}
|
||||
}
|
||||
if ((needCodecCheck & CODEC_CHECK_VIDEO) && frameHeader.type == FLV::FRAME_TYPE_VIDEO)
|
||||
{
|
||||
reader.Seek(frameData.location+15); // TODO: check for -1 return value
|
||||
|
||||
uint8_t data[1] = {0};
|
||||
FLVVideoHeader videoHeader;
|
||||
size_t bytesRead = reader.Read(data, 1);
|
||||
if (videoHeader.Read(data, bytesRead))
|
||||
{
|
||||
if (Video_IsSupported(videoHeader.format))
|
||||
{
|
||||
needCodecCheck &= ~CODEC_CHECK_VIDEO;
|
||||
}
|
||||
else
|
||||
{
|
||||
needCodecCheck &= ~CODEC_CHECK_VIDEO;
|
||||
missingCodecs|=CODEC_CHECK_VIDEO;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
codecCheckFrame++;
|
||||
}
|
||||
else if (reader.IsEOF())
|
||||
break;
|
||||
else if (WaitForSingleObject(killswitch, 55) == WAIT_OBJECT_0)
|
||||
break;
|
||||
|
||||
}
|
||||
|
||||
if (needCodecCheck)
|
||||
continue; // don't start decoding until we've done our codec check
|
||||
|
||||
if (missingCodecs)
|
||||
{
|
||||
// use in_swf to play this one
|
||||
if (CheckSWF())
|
||||
{
|
||||
HANDLE mainThread = WASABI_API_APP->main_getMainThreadHandle();
|
||||
if (mainThread)
|
||||
{
|
||||
reader.Kill();
|
||||
Audio_Close();
|
||||
Video_Stop();
|
||||
Video_Close();
|
||||
QueueUserAPC(SWFAPC, mainThread,0);
|
||||
CloseHandle(mainThread);
|
||||
return 0;
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
FLVHeader *header = reader.GetHeader();
|
||||
if (header)
|
||||
{
|
||||
bool can_play_something = false;
|
||||
|
||||
if (header->hasVideo && !(missingCodecs & CODEC_CHECK_VIDEO))
|
||||
can_play_something = true; // we can play video
|
||||
else if (header->hasAudio && !(missingCodecs & CODEC_CHECK_AUDIO))
|
||||
can_play_something = true; // we can play audio
|
||||
|
||||
if (can_play_something)
|
||||
{
|
||||
missingCodecs=false;
|
||||
continue;
|
||||
}
|
||||
}
|
||||
break; // no header or no codecs at all, bail out
|
||||
}
|
||||
}
|
||||
|
||||
/* --- End Codec Check --- */
|
||||
FrameData frameData;
|
||||
if (reader.GetFrame(i, frameData))
|
||||
{
|
||||
i++;
|
||||
uint8_t data[2] = {0};
|
||||
FLVStreamHeader &frameHeader = frameData.header;
|
||||
reader.Seek(frameData.location+15); // TODO: check for -1 return value
|
||||
switch (frameHeader.type)
|
||||
{
|
||||
default:
|
||||
#ifdef _DEBUG
|
||||
DebugBreak();
|
||||
#endif
|
||||
break;
|
||||
case FLV::FRAME_TYPE_AUDIO: // audio
|
||||
first_frame_parsed=true;
|
||||
if (m_need_seek == -1 || !Audio_DecoderReady())
|
||||
{
|
||||
FLVAudioHeader audioHeader;
|
||||
size_t bytesRead = reader.Read(data, 1);
|
||||
if (audioHeader.Read(data, bytesRead))
|
||||
{
|
||||
size_t dataSize = frameHeader.dataSize - 1;
|
||||
|
||||
uint8_t *audiodata = (uint8_t *)calloc(dataSize, sizeof(uint8_t));
|
||||
if (audiodata)
|
||||
{
|
||||
bytesRead = reader.Read(audiodata, dataSize);
|
||||
if (bytesRead != dataSize)
|
||||
break;
|
||||
if (!reader.IsStreaming())
|
||||
{
|
||||
if (first_timestamp == -1)
|
||||
first_timestamp = frameHeader.timestamp;
|
||||
last_timestamp = frameHeader.timestamp;
|
||||
last_timestamp = plugin.outMod->GetWrittenTime();
|
||||
}
|
||||
|
||||
OnAudio(reader, audiodata, dataSize, audioHeader);
|
||||
free(audiodata);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FLV::FRAME_TYPE_VIDEO: // video
|
||||
first_frame_parsed=true;
|
||||
if (m_need_seek == -1 || !Video_DecoderReady())
|
||||
{
|
||||
|
||||
FLVVideoHeader videoHeader;
|
||||
size_t bytesRead = reader.Read(data, 1);
|
||||
if (videoHeader.Read(data, bytesRead))
|
||||
{
|
||||
size_t dataSize = frameHeader.dataSize - 1;
|
||||
|
||||
uint8_t *videodata = (uint8_t *)calloc(dataSize, sizeof(uint8_t));
|
||||
if (videodata)
|
||||
{
|
||||
bytesRead = reader.Read(videodata, dataSize);
|
||||
if (bytesRead != dataSize)
|
||||
{
|
||||
free(videodata);
|
||||
break;
|
||||
}
|
||||
if (!OnVideo(videodata, dataSize, videoHeader.format, frameHeader.timestamp))
|
||||
free(videodata);
|
||||
}
|
||||
}
|
||||
}
|
||||
break;
|
||||
case FLV::FRAME_TYPE_METADATA: // metadata
|
||||
{
|
||||
first_frame_parsed=true;
|
||||
size_t dataSize = frameHeader.dataSize;
|
||||
uint8_t *metadatadata= (uint8_t *)calloc(dataSize, sizeof(uint8_t));
|
||||
if (metadatadata)
|
||||
{
|
||||
size_t bytesRead = reader.Read(metadatadata, dataSize);
|
||||
if (bytesRead != dataSize)
|
||||
{
|
||||
free(metadatadata);
|
||||
break;
|
||||
}
|
||||
FLVMetadata metadata;
|
||||
metadata.Read(metadatadata, dataSize);
|
||||
for ( FLVMetadata::Tag *tag : metadata.tags )
|
||||
{
|
||||
if (!_wcsicmp(tag->name.str, L"onMetaData"))
|
||||
{
|
||||
AMFType *amf_stream_title;
|
||||
|
||||
amf_stream_title = tag->parameters->array[L"streamTitle"];
|
||||
if (amf_stream_title && amf_stream_title->type == AMFType::TYPE_STRING)
|
||||
{
|
||||
AMFString *stream_title_string = (AMFString *)amf_stream_title;
|
||||
Nullsoft::Utility::AutoLock stream_lock(stream_title_guard);
|
||||
free(stream_title);
|
||||
stream_title = _wcsdup(stream_title_string->str);
|
||||
PostMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
|
||||
}
|
||||
|
||||
AMFType *w, *h;
|
||||
w = tag->parameters->array[L"width"];
|
||||
h = tag->parameters->array[L"height"];
|
||||
if (w && h)
|
||||
{
|
||||
width = (int)AMFGetDouble(w);
|
||||
height = (int)AMFGetDouble(h);
|
||||
}
|
||||
|
||||
AMFType *duration;
|
||||
duration=tag->parameters->array[L"duration"];
|
||||
if (duration)
|
||||
{
|
||||
hasDuration=true;
|
||||
plugin.is_seekable=1;
|
||||
g_length = (int)(AMFGetDouble(duration)*1000.0);
|
||||
}
|
||||
|
||||
// grab the data rate. we'll need this to determine a good pre-buffer.
|
||||
AMFType *videoDataRate, *audioDataRate;
|
||||
videoDataRate=tag->parameters->array[L"videodatarate"];
|
||||
audioDataRate=tag->parameters->array[L"audiodatarate"];
|
||||
if (videoDataRate || audioDataRate)
|
||||
{
|
||||
dataRate_audio = audioDataRate?AMFGetDouble(audioDataRate):0.0;
|
||||
dataRate_video = videoDataRate?AMFGetDouble(videoDataRate):0.0;
|
||||
|
||||
dataRate = dataRate_audio + dataRate_video;
|
||||
|
||||
if (dataRate < 1.0f)
|
||||
dataRate = 720.0f;
|
||||
plugin.SetInfo((int)dataRate, -1, -1, 1);
|
||||
prebuffer = (uint64_t)(dataRate * PRE_BUFFER_MS / 8.0);
|
||||
if (prebuffer > PRE_BUFFER_MAX)
|
||||
prebuffer=PRE_BUFFER_MAX;
|
||||
|
||||
PostMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_UPDTITLE);
|
||||
}
|
||||
}
|
||||
flvCOM.MetadataCallback(tag);
|
||||
}
|
||||
free(metadatadata);
|
||||
}
|
||||
}
|
||||
break;
|
||||
}
|
||||
}
|
||||
else if (reader.IsEOF())
|
||||
break;
|
||||
else if (WaitForSingleObject(killswitch, 55) == WAIT_OBJECT_0)
|
||||
break;
|
||||
}
|
||||
|
||||
reader.SignalKill();
|
||||
|
||||
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
outputter.Write(0,0);
|
||||
outputter.WaitWhilePlaying();
|
||||
|
||||
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
|
||||
PostMessage(plugin.hMainWindow, WM_WA_MPEG_EOF, 0, 0);
|
||||
}
|
||||
|
||||
SetEvent(killswitch);
|
||||
|
||||
Video_Stop();
|
||||
Video_Close();
|
||||
|
||||
reader.Kill();
|
||||
Audio_Close();
|
||||
return 0;
|
||||
}
|
34
Src/Plugins/Input/in_flv/ProgressiveProcessor.cpp
Normal file
34
Src/Plugins/Input/in_flv/ProgressiveProcessor.cpp
Normal file
@ -0,0 +1,34 @@
|
||||
#include "ProgressiveProcessor.h"
|
||||
|
||||
ProgressiveProcessor::ProgressiveProcessor()
|
||||
{
|
||||
tempFile[0]=0;
|
||||
writeCursor=INVALID_HANDLE_VALUE;
|
||||
|
||||
wchar_t tempPath[MAX_PATH-14] = {0};
|
||||
GetTempPath(MAX_PATH-14, tempPath);
|
||||
GetTempFileName(tempPath, L"wfv", 0, tempFile);
|
||||
|
||||
writeCursor=CreateFile(tempFile, GENERIC_WRITE, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, CREATE_ALWAYS, 0, 0);
|
||||
processedCursor=CreateFile(tempFile, GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
readCursor=CreateFile(tempFile, GENERIC_READ, FILE_SHARE_WRITE|FILE_SHARE_READ, 0, OPEN_EXISTING, 0, 0);
|
||||
}
|
||||
|
||||
ProgressiveProcessor::~ProgressiveProcessor()
|
||||
{
|
||||
if (writeCursor != INVALID_HANDLE_VALUE)
|
||||
CloseHandle(writeCursor);
|
||||
|
||||
if (tempFile[0])
|
||||
DeleteFile(tempFile);
|
||||
}
|
||||
|
||||
int ProgressiveProcessor::Write(void *data, size_t datalen, size_t *written)
|
||||
{
|
||||
DWORD dw_written=0;
|
||||
WriteFile(writeCursor, data, (DWORD)datalen, &dw_written, NULL);
|
||||
*written=dw_written;
|
||||
writePosition+=dw_written;
|
||||
|
||||
return 0;
|
||||
}
|
16
Src/Plugins/Input/in_flv/ProgressiveProcessor.h
Normal file
16
Src/Plugins/Input/in_flv/ProgressiveProcessor.h
Normal file
@ -0,0 +1,16 @@
|
||||
#pragma once
|
||||
#include "FileProcessor.h"
|
||||
|
||||
class ProgressiveProcessor : public FileProcessor
|
||||
{
|
||||
public:
|
||||
ProgressiveProcessor();
|
||||
~ProgressiveProcessor();
|
||||
|
||||
private:
|
||||
/* FLVProcessor virtual method overrides */
|
||||
int Write(void *data, size_t datalen, size_t *written);
|
||||
private:
|
||||
HANDLE writeCursor;
|
||||
wchar_t tempFile[MAX_PATH];
|
||||
};
|
112
Src/Plugins/Input/in_flv/StreamProcessor.cpp
Normal file
112
Src/Plugins/Input/in_flv/StreamProcessor.cpp
Normal file
@ -0,0 +1,112 @@
|
||||
#include "StreamProcessor.h"
|
||||
#include "FLVHeader.h"
|
||||
|
||||
StreamProcessor::StreamProcessor()
|
||||
{
|
||||
buffer.reserve(1024*1024);
|
||||
bytesWritten=0;
|
||||
readHeader=false;
|
||||
}
|
||||
|
||||
int StreamProcessor::Write(void *data, size_t datalen, size_t *written)
|
||||
{
|
||||
*written = buffer.write(data, datalen);
|
||||
bytesWritten += *written;
|
||||
if (*written != datalen)
|
||||
return FLVPROCESSOR_WRITE_WAIT; // tell the FLVReader we need a break :)
|
||||
return FLVPROCESSOR_WRITE_OK;
|
||||
}
|
||||
|
||||
int StreamProcessor::Process()
|
||||
{
|
||||
// we're actually not going to parse anything until the GetFrame() call.
|
||||
// since we can't seek anyway
|
||||
// but we do need to validate that this is an FLV stream
|
||||
|
||||
if (!readHeader)
|
||||
{
|
||||
if (buffer.size() >= 9) // see if we have enough data
|
||||
{
|
||||
uint8_t data[9] = {0};
|
||||
buffer.read(data, 9);
|
||||
|
||||
if (header.Read(data, 9))
|
||||
{
|
||||
readHeader=true;
|
||||
return FLV_OK;
|
||||
}
|
||||
else // not an FLV header
|
||||
{
|
||||
return FLV_ERROR;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return FLV_NEED_MORE_DATA;
|
||||
}
|
||||
|
||||
uint64_t StreamProcessor::GetProcessedPosition()
|
||||
{
|
||||
// since we parse the bitstream on-demand, we'll just return how many bytes we've buffered so far
|
||||
if (readHeader) // make sure we've at least found the main FLV header
|
||||
return bytesWritten;
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
uint32_t StreamProcessor::GetMaxTimestamp()
|
||||
{
|
||||
return -1000; // it's a stream! no length!
|
||||
}
|
||||
|
||||
uint64_t StreamProcessor::Seek(uint64_t position)
|
||||
{
|
||||
// we can't really seek in a traditional sense, but since Seek gets called to simply advance the read pointer,
|
||||
// we'll advance within our buffer
|
||||
// we always set FrameData::location to 0, so can just call like this
|
||||
return buffer.advance((size_t)position);
|
||||
}
|
||||
|
||||
size_t StreamProcessor::Read(void *data, size_t bytes)
|
||||
{
|
||||
// easy :)
|
||||
return buffer.read(data, bytes);
|
||||
}
|
||||
|
||||
// the fun happens here
|
||||
bool StreamProcessor::GetFrame(size_t frameIndex, FrameData &frameData)
|
||||
{
|
||||
// since this is a stream, we're going to ignore frameIndex and just give them the next frame
|
||||
|
||||
if (buffer.size() >= 15)
|
||||
{
|
||||
uint8_t data[15] = {0};
|
||||
buffer.peek(data, 15);
|
||||
if (frameData.header.Read(data, 15))
|
||||
{
|
||||
// first, make sure we have enough data buffered to read the whole thing
|
||||
// because the next thing to get called after this function is Read()
|
||||
if (frameData.header.dataSize + 15 <= buffer.size())
|
||||
{
|
||||
// since we're streaming and only processing one frame at a time
|
||||
// we're going to set the returned frame's location to 0 (start of the ring buffer)
|
||||
frameData.location = 0;
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
return false; // not ready for a frame yet
|
||||
}
|
||||
|
||||
bool StreamProcessor::GetPosition(int time_in_ms, size_t *frameIndex, bool needVideoKeyFrame)
|
||||
{
|
||||
return false; // can't seek!
|
||||
}
|
||||
|
||||
FLVHeader *StreamProcessor::GetHeader()
|
||||
{
|
||||
if (readHeader)
|
||||
return &header;
|
||||
else
|
||||
return 0;
|
||||
}
|
28
Src/Plugins/Input/in_flv/StreamProcessor.h
Normal file
28
Src/Plugins/Input/in_flv/StreamProcessor.h
Normal file
@ -0,0 +1,28 @@
|
||||
#pragma once
|
||||
|
||||
#include "FLVProcessor.h"
|
||||
#include "../nu/RingBuffer.h"
|
||||
|
||||
class StreamProcessor : public FLVProcessor
|
||||
{
|
||||
public:
|
||||
StreamProcessor();
|
||||
|
||||
private:
|
||||
int Write(void *data, size_t datalen, size_t *written);
|
||||
int Process();
|
||||
uint64_t Seek(uint64_t position);
|
||||
size_t Read(void *data, size_t bytes);
|
||||
uint64_t GetProcessedPosition();
|
||||
bool GetFrame(size_t frameIndex, FrameData &frameData);
|
||||
uint32_t GetMaxTimestamp();
|
||||
bool GetPosition(int time_in_ms, size_t *frameIndex, bool needVideoKeyFrame);
|
||||
bool IsStreaming() { return true; }
|
||||
FLVHeader *GetHeader();
|
||||
private:
|
||||
RingBuffer buffer;
|
||||
uint64_t bytesWritten;
|
||||
bool readHeader;
|
||||
FLVHeader header;
|
||||
};
|
||||
|
291
Src/Plugins/Input/in_flv/VideoThread.cpp
Normal file
291
Src/Plugins/Input/in_flv/VideoThread.cpp
Normal file
@ -0,0 +1,291 @@
|
||||
#include "main.h"
|
||||
#include "VideoThread.h"
|
||||
#include "api__in_flv.h"
|
||||
#include "FLVVideoHeader.h"
|
||||
#include <shlwapi.h>
|
||||
#include <windows.h>
|
||||
#include "../nu/threadname.h"
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include "../nu/AutoLock.h"
|
||||
#include "../nu/SampleQueue.h"
|
||||
|
||||
int width, height;
|
||||
IVideoOutput *videoOutput=0;
|
||||
static HANDLE videoThread=0;
|
||||
static volatile LONG video_flush=0;
|
||||
static ifc_flvvideodecoder *videoDecoder=0;
|
||||
bool video_opened=false;
|
||||
static HANDLE coded_frames_event=0;
|
||||
static HANDLE video_flush_event=0;
|
||||
static Nullsoft::Utility::LockGuard coded_frames_guard;
|
||||
extern bool video_only;
|
||||
|
||||
void Video_Init()
|
||||
{
|
||||
video_opened=false;
|
||||
videoDecoder=0;
|
||||
videoThread=0;
|
||||
width=0;
|
||||
height=0;
|
||||
|
||||
if (coded_frames_event == 0)
|
||||
coded_frames_event = CreateEvent(NULL, FALSE, FALSE, NULL);
|
||||
if (video_flush_event == 0)
|
||||
video_flush_event = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
video_flush=0;
|
||||
}
|
||||
|
||||
struct FRAMEDATA
|
||||
{
|
||||
FRAMEDATA()
|
||||
{
|
||||
data=0;
|
||||
length=0;
|
||||
timestamp=0;
|
||||
}
|
||||
|
||||
~FRAMEDATA()
|
||||
{
|
||||
free(data);
|
||||
}
|
||||
void Reset()
|
||||
{
|
||||
free(data);
|
||||
data=0;
|
||||
length=0;
|
||||
timestamp=0;
|
||||
}
|
||||
void Set(void *_data, size_t _length, uint32_t ts)
|
||||
{
|
||||
data = _data;
|
||||
length = _length;
|
||||
timestamp = ts;
|
||||
}
|
||||
void *data;
|
||||
size_t length;
|
||||
uint32_t timestamp;
|
||||
};
|
||||
|
||||
static SampleQueue<FRAMEDATA> coded_frames;
|
||||
|
||||
extern int GetOutputTime();
|
||||
static void DecodeVideo(FRAMEDATA *framedata)
|
||||
{
|
||||
if (WaitForSingleObject(killswitch, 0) != WAIT_OBJECT_0)
|
||||
{
|
||||
int decodeResult = videoDecoder->DecodeSample(framedata->data, framedata->length, framedata->timestamp);
|
||||
|
||||
if (decodeResult == FLV_VIDEO_SUCCESS)
|
||||
{
|
||||
void *data, *decoder_data;
|
||||
uint64_t timestamp=framedata->timestamp;
|
||||
while (videoDecoder->GetPicture(&data, &decoder_data, ×tamp) == FLV_VIDEO_SUCCESS)
|
||||
{
|
||||
if (!video_opened)
|
||||
{
|
||||
int color_format;
|
||||
if (videoDecoder->GetOutputFormat(&width, &height, &color_format) == FLV_VIDEO_SUCCESS)
|
||||
{
|
||||
videoOutput->extended(VIDUSER_SET_THREAD_SAFE, 1, 0);
|
||||
videoOutput->open(width, height, 0, 1.0, color_format);
|
||||
video_opened=true;
|
||||
}
|
||||
}
|
||||
if (video_opened)
|
||||
{
|
||||
again:
|
||||
int realTime =(int)GetOutputTime();
|
||||
if (timestamp > (realTime+5))
|
||||
{
|
||||
HANDLE handles[] = {killswitch, video_flush_event};
|
||||
int ret=WaitForMultipleObjects(2, handles, FALSE, (DWORD)(timestamp-realTime));
|
||||
if (ret != WAIT_TIMEOUT)
|
||||
{
|
||||
videoDecoder->FreePicture(data, decoder_data);
|
||||
framedata->Reset();
|
||||
return ;
|
||||
}
|
||||
goto again; // TODO: handle paused state a little better than this
|
||||
}
|
||||
videoOutput->draw(data);
|
||||
}
|
||||
videoDecoder->FreePicture(data, decoder_data);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
framedata->Reset();
|
||||
}
|
||||
|
||||
DWORD CALLBACK VideoProcedure(LPVOID param)
|
||||
{
|
||||
SetThreadName(-1,"FLV_VideoProcedure");
|
||||
HANDLE wait_handles[] = { killswitch, video_flush_event, coded_frames_event};
|
||||
int ret;
|
||||
do
|
||||
{
|
||||
ret = WaitForMultipleObjects(3, wait_handles, FALSE, INFINITE);
|
||||
if (ret == WAIT_OBJECT_0 + 1)
|
||||
{
|
||||
if (video_flush)
|
||||
{
|
||||
InterlockedDecrement(&video_flush);
|
||||
if (videoDecoder)
|
||||
videoDecoder->Flush();
|
||||
}
|
||||
ResetEvent(video_flush_event);
|
||||
}
|
||||
else if (ret == WAIT_OBJECT_0 + 2)
|
||||
{
|
||||
FRAMEDATA *frame_data = 0;
|
||||
while (frame_data = coded_frames.PopProcessed())
|
||||
{
|
||||
DecodeVideo(frame_data);
|
||||
frame_data->Reset();
|
||||
coded_frames.PushFree(frame_data);
|
||||
}
|
||||
}
|
||||
} while (ret != WAIT_OBJECT_0);
|
||||
|
||||
if (video_opened && videoOutput)
|
||||
videoOutput->close();
|
||||
video_opened=false;
|
||||
return 0;
|
||||
}
|
||||
|
||||
bool Video_IsSupported(int type)
|
||||
{
|
||||
size_t n = 0;
|
||||
waServiceFactory *factory = NULL;
|
||||
while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
|
||||
{
|
||||
svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
|
||||
if (creator)
|
||||
{
|
||||
int supported = creator->HandlesVideo(type);
|
||||
factory->releaseInterface(creator);
|
||||
if (supported == svc_flvdecoder::CREATEDECODER_SUCCESS)
|
||||
return true;
|
||||
}
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
static ifc_flvvideodecoder *CreateVideoDecoder(int type)
|
||||
{
|
||||
ifc_flvvideodecoder *video_decoder = 0;
|
||||
size_t n = 0;
|
||||
waServiceFactory *factory = NULL;
|
||||
while (factory = plugin.service->service_enumService(WaSvc::FLVDECODER, n++))
|
||||
{
|
||||
svc_flvdecoder *creator = (svc_flvdecoder *)factory->getInterface();
|
||||
if (creator)
|
||||
{
|
||||
if (creator->CreateVideoDecoder(type, width, height, &video_decoder) == FLV_VIDEO_SUCCESS)
|
||||
return video_decoder;
|
||||
|
||||
factory->releaseInterface(creator);
|
||||
}
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
|
||||
static const GUID playbackConfigGroupGUID =
|
||||
{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
|
||||
bool OnVideo(void *data, size_t length, int type, unsigned __int32 timestamp)
|
||||
{
|
||||
if (!videoDecoder)
|
||||
{
|
||||
videoDecoder = CreateVideoDecoder(type);
|
||||
}
|
||||
|
||||
if (videoDecoder)
|
||||
{
|
||||
if (!video_only && !videoThread)
|
||||
{
|
||||
DWORD threadId;
|
||||
videoThread = CreateThread(0, 0, VideoProcedure, 0, 0, &threadId);
|
||||
SetThreadPriority(videoThread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
}
|
||||
|
||||
FRAMEDATA *new_frame = coded_frames.PopFree();
|
||||
if (new_frame)
|
||||
{
|
||||
new_frame->Set(data, length, timestamp);
|
||||
if (video_only)
|
||||
{
|
||||
DecodeVideo(new_frame);
|
||||
new_frame->Reset();
|
||||
coded_frames.PushFree(new_frame);
|
||||
}
|
||||
else
|
||||
{
|
||||
coded_frames.PushProcessed(new_frame);
|
||||
SetEvent(coded_frames_event);
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
void Video_Stop()
|
||||
{
|
||||
if (video_only)
|
||||
{
|
||||
ResetEvent(coded_frames_event);
|
||||
Nullsoft::Utility::AutoLock l(coded_frames_guard);
|
||||
coded_frames.Trim();
|
||||
if (video_opened && videoOutput)
|
||||
videoOutput->close();
|
||||
video_opened=false;
|
||||
}
|
||||
else
|
||||
{
|
||||
if (videoThread)
|
||||
WaitForSingleObject(videoThread, INFINITE);
|
||||
videoThread=0;
|
||||
|
||||
InterlockedIncrement(&video_flush);
|
||||
ResetEvent(coded_frames_event);
|
||||
Nullsoft::Utility::AutoLock l(coded_frames_guard);
|
||||
coded_frames.Trim();
|
||||
}
|
||||
}
|
||||
|
||||
void Video_Close()
|
||||
{
|
||||
video_opened=false;
|
||||
|
||||
if (videoDecoder)
|
||||
{
|
||||
videoDecoder->Close();
|
||||
videoDecoder=0;
|
||||
}
|
||||
}
|
||||
|
||||
void VideoFlush()
|
||||
{
|
||||
if (video_only)
|
||||
{
|
||||
if (videoDecoder)
|
||||
videoDecoder->Flush();
|
||||
}
|
||||
else if (videoThread)
|
||||
{
|
||||
InterlockedIncrement(&video_flush);
|
||||
ResetEvent(coded_frames_event);
|
||||
coded_frames.Trim();
|
||||
SetEvent(video_flush_event);
|
||||
}
|
||||
}
|
||||
|
||||
bool Video_DecoderReady()
|
||||
{
|
||||
if (!videoDecoder)
|
||||
return true;
|
||||
|
||||
return !!videoDecoder->Ready();
|
||||
}
|
21
Src/Plugins/Input/in_flv/VideoThread.h
Normal file
21
Src/Plugins/Input/in_flv/VideoThread.h
Normal file
@ -0,0 +1,21 @@
|
||||
#ifndef NULLSOFT_IN_FLV_VIDEOTHREAD_H
|
||||
#define NULLSOFT_IN_FLV_VIDEOTHREAD_H
|
||||
|
||||
#include "../nsv/dec_if.h"
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "svc_flvdecoder.h"
|
||||
#include "ifc_flvvideodecoder.h"
|
||||
|
||||
bool OnVideo(void *data, size_t length, int type, unsigned __int32 timestamp);
|
||||
void VideoFlush();
|
||||
extern int width, height;
|
||||
extern IVideoOutput *videoOutput;
|
||||
extern bool video_opened;
|
||||
|
||||
void Video_Init();
|
||||
void Video_Stop();
|
||||
void Video_Close();
|
||||
bool Video_IsSupported(int type);
|
||||
bool Video_DecoderReady();
|
||||
|
||||
#endif
|
12
Src/Plugins/Input/in_flv/api__in_flv.h
Normal file
12
Src/Plugins/Input/in_flv/api__in_flv.h
Normal file
@ -0,0 +1,12 @@
|
||||
#ifndef NULLSOFT_IN_FLV_API_H
|
||||
#define NULLSOFT_IN_FLV_API_H
|
||||
|
||||
#include "../Agave/Config/api_config.h"
|
||||
extern api_config *AGAVE_API_CONFIG;
|
||||
|
||||
#include <api/application/api_application.h>
|
||||
#define WASABI_API_APP applicationApi
|
||||
|
||||
#include "../Agave/Language/api_language.h"
|
||||
|
||||
#endif // !NULLSOFT_IN_FLV_API_H
|
62
Src/Plugins/Input/in_flv/ifc_flvaudiodecoder.h
Normal file
62
Src/Plugins/Input/in_flv/ifc_flvaudiodecoder.h
Normal file
@ -0,0 +1,62 @@
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
FLV_AUDIO_SUCCESS = 0,
|
||||
FLV_AUDIO_FAILURE = 1,
|
||||
FLV_AUDIO_NEEDS_MORE_INPUT = 2,
|
||||
};
|
||||
|
||||
class ifc_flvaudiodecoder : public Dispatchable
|
||||
{
|
||||
protected:
|
||||
ifc_flvaudiodecoder() {}
|
||||
~ifc_flvaudiodecoder() {}
|
||||
public:
|
||||
int GetOutputFormat(unsigned int *sample_rate, unsigned int *channels, unsigned int *bits);
|
||||
int DecodeSample(const void *input_buffer, size_t input_buffer_bytes, void *samples, size_t *samples_size_bytes, double *bitrate);
|
||||
void Flush();
|
||||
void Close();
|
||||
int Ready(); // returns 1 for ready [default], 0 for not ready. Some codecs in FLV use the first packet for decoder config data. return 1 from this once you've gotten it
|
||||
void SetPreferences(unsigned int max_channels, unsigned int preferred_bits);
|
||||
DISPATCH_CODES
|
||||
{
|
||||
FLV_AUDIO_GETOUTPUTFORMAT = 0,
|
||||
FLV_AUDIO_DECODE = 1,
|
||||
FLV_AUDIO_FLUSH = 2,
|
||||
FLV_AUDIO_CLOSE = 3,
|
||||
FLV_AUDIO_READY = 4,
|
||||
FLV_AUDIO_SETPREFERENCES=5,
|
||||
};
|
||||
};
|
||||
|
||||
inline int ifc_flvaudiodecoder::GetOutputFormat(unsigned int *sample_rate, unsigned int *channels, unsigned int *bits)
|
||||
{
|
||||
return _call(FLV_AUDIO_GETOUTPUTFORMAT, (int)FLV_AUDIO_FAILURE, sample_rate, channels, bits);
|
||||
}
|
||||
|
||||
inline int ifc_flvaudiodecoder::DecodeSample(const void *input_buffer, size_t input_buffer_bytes, void *samples, size_t *samples_size_bytes, double *bitrate)
|
||||
{
|
||||
return _call(FLV_AUDIO_DECODE, (int)FLV_AUDIO_FAILURE, input_buffer, input_buffer_bytes, samples, samples_size_bytes, bitrate);
|
||||
}
|
||||
|
||||
inline void ifc_flvaudiodecoder::Flush()
|
||||
{
|
||||
_voidcall(FLV_AUDIO_FLUSH);
|
||||
}
|
||||
|
||||
inline void ifc_flvaudiodecoder::Close()
|
||||
{
|
||||
_voidcall(FLV_AUDIO_CLOSE);
|
||||
}
|
||||
|
||||
inline int ifc_flvaudiodecoder::Ready()
|
||||
{
|
||||
return _call(FLV_AUDIO_READY, (int)1); // default to true so that decoders that don't implement won't block in_flv from seeking
|
||||
}
|
||||
|
||||
inline void ifc_flvaudiodecoder::SetPreferences(unsigned int max_channels, unsigned int preferred_bits)
|
||||
{
|
||||
_voidcall(FLV_AUDIO_SETPREFERENCES, max_channels, preferred_bits);
|
||||
}
|
||||
|
67
Src/Plugins/Input/in_flv/ifc_flvvideodecoder.h
Normal file
67
Src/Plugins/Input/in_flv/ifc_flvvideodecoder.h
Normal file
@ -0,0 +1,67 @@
|
||||
#pragma once
|
||||
|
||||
enum
|
||||
{
|
||||
FLV_VIDEO_SUCCESS = 0,
|
||||
FLV_VIDEO_FAILURE = 1,
|
||||
};
|
||||
|
||||
class ifc_flvvideodecoder : public Dispatchable
|
||||
{
|
||||
protected:
|
||||
ifc_flvvideodecoder() {}
|
||||
~ifc_flvvideodecoder() {}
|
||||
public:
|
||||
int GetOutputFormat(int *x, int *y, int *color_format);
|
||||
int DecodeSample(const void *inputBuffer, size_t inputBufferBytes, int32_t timestamp);
|
||||
void Flush();
|
||||
void Close();
|
||||
int GetPicture(void **data, void **decoder_data, uint64_t *timestamp);
|
||||
void FreePicture(void *data, void *decoder_data);
|
||||
int Ready(); // returns 1 for ready [default], 0 for not ready. Some codecs in FLV use the first packet for decoder config data. return 1 from this once you've gotten it
|
||||
DISPATCH_CODES
|
||||
{
|
||||
FLV_VIDEO_GETOUTPUTFORMAT = 0,
|
||||
FLV_VIDEO_DECODE = 1,
|
||||
FLV_VIDEO_FLUSH = 2,
|
||||
FLV_VIDEO_CLOSE = 3,
|
||||
FLV_VIDEO_GET_PICTURE = 4,
|
||||
FLV_VIDEO_FREE_PICTURE = 5,
|
||||
FLV_VIDEO_READY = 6,
|
||||
};
|
||||
};
|
||||
|
||||
inline int ifc_flvvideodecoder::GetOutputFormat(int *x, int *y, int *color_format)
|
||||
{
|
||||
return _call(FLV_VIDEO_GETOUTPUTFORMAT, (int)FLV_VIDEO_FAILURE, x, y, color_format);
|
||||
}
|
||||
|
||||
inline int ifc_flvvideodecoder::DecodeSample(const void *inputBuffer, size_t inputBufferBytes, int32_t timestamp)
|
||||
{
|
||||
return _call(FLV_VIDEO_DECODE, (int)FLV_VIDEO_FAILURE, inputBuffer, inputBufferBytes, timestamp);
|
||||
}
|
||||
|
||||
inline void ifc_flvvideodecoder::Flush()
|
||||
{
|
||||
_voidcall(FLV_VIDEO_FLUSH);
|
||||
}
|
||||
|
||||
inline void ifc_flvvideodecoder::Close()
|
||||
{
|
||||
_voidcall(FLV_VIDEO_CLOSE);
|
||||
}
|
||||
|
||||
inline int ifc_flvvideodecoder::GetPicture(void **data, void **decoder_data, uint64_t *timestamp)
|
||||
{
|
||||
return _call(FLV_VIDEO_GET_PICTURE, (int)FLV_VIDEO_FAILURE, data, decoder_data, timestamp);
|
||||
}
|
||||
|
||||
inline void ifc_flvvideodecoder::FreePicture(void *data, void *decoder_data)
|
||||
{
|
||||
_voidcall(FLV_VIDEO_FREE_PICTURE, data, decoder_data);
|
||||
}
|
||||
|
||||
inline int ifc_flvvideodecoder::Ready()
|
||||
{
|
||||
return _call(FLV_VIDEO_READY, (int)1); // default to true so that decoders that don't implement won't block in_flv from seeking
|
||||
}
|
85
Src/Plugins/Input/in_flv/in_flv.rc
Normal file
85
Src/Plugins/Input/in_flv/in_flv.rc
Normal file
@ -0,0 +1,85 @@
|
||||
// Microsoft Visual C++ generated resource script.
|
||||
//
|
||||
#include "resource.h"
|
||||
|
||||
#define APSTUDIO_READONLY_SYMBOLS
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 2 resource.
|
||||
//
|
||||
#include "afxres.h"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#undef APSTUDIO_READONLY_SYMBOLS
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
// English (U.S.) resources
|
||||
|
||||
#if !defined(AFX_RESOURCE_DLL) || defined(AFX_TARG_ENU)
|
||||
#ifdef _WIN32
|
||||
LANGUAGE LANG_ENGLISH, SUBLANG_ENGLISH_US
|
||||
#pragma code_page(1252)
|
||||
#endif //_WIN32
|
||||
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// TEXTINCLUDE
|
||||
//
|
||||
|
||||
1 TEXTINCLUDE
|
||||
BEGIN
|
||||
"resource.h\0"
|
||||
END
|
||||
|
||||
2 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""afxres.h""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
3 TEXTINCLUDE
|
||||
BEGIN
|
||||
"#include ""version.rc2""\r\n"
|
||||
"\0"
|
||||
END
|
||||
|
||||
#endif // APSTUDIO_INVOKED
|
||||
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// String Table
|
||||
//
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_FLV "Nullsoft Flash Video Decoder v%s"
|
||||
65535 "{EC959D43-9122-4807-B928-7B46207AFA49}"
|
||||
END
|
||||
|
||||
STRINGTABLE
|
||||
BEGIN
|
||||
IDS_NULLSOFT_FLV_OLD "Nullsoft Flash Video Decoder"
|
||||
IDS_BUFFERING "Buffering"
|
||||
IDS_FLASH_VIDEO "Flash Video"
|
||||
IDS_VP6_FLASH_VIDEO "VP6 Flash Video"
|
||||
IDS_FAMILY_STRING "Flash Video"
|
||||
IDS_ABOUT_TEXT "%s\n<> 2006-2023 Winamp SA\nWritten by: Ben Allison\nBuild date: %s"
|
||||
END
|
||||
|
||||
#endif // English (U.S.) resources
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
|
||||
|
||||
|
||||
#ifndef APSTUDIO_INVOKED
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Generated from the TEXTINCLUDE 3 resource.
|
||||
//
|
||||
#include "version.rc2"
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
#endif // not APSTUDIO_INVOKED
|
||||
|
30
Src/Plugins/Input/in_flv/in_flv.sln
Normal file
30
Src/Plugins/Input/in_flv/in_flv.sln
Normal file
@ -0,0 +1,30 @@
|
||||
Microsoft Visual Studio Solution File, Format Version 12.00
|
||||
# Visual Studio Version 16
|
||||
VisualStudioVersion = 16.0.29609.76
|
||||
MinimumVisualStudioVersion = 10.0.40219.1
|
||||
Project("{8BC9CEB8-8B4A-11D0-8D11-00A0C91BC942}") = "in_flv", "in_flv.vcxproj", "{405DF558-062D-43AB-B7FB-02C4CF8B28CE}"
|
||||
EndProject
|
||||
Global
|
||||
GlobalSection(SolutionConfigurationPlatforms) = preSolution
|
||||
Debug|Win32 = Debug|Win32
|
||||
Debug|x64 = Debug|x64
|
||||
Release|Win32 = Release|Win32
|
||||
Release|x64 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(ProjectConfigurationPlatforms) = postSolution
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Debug|Win32.ActiveCfg = Debug|Win32
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Debug|Win32.Build.0 = Debug|Win32
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Debug|x64.ActiveCfg = Debug|x64
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Debug|x64.Build.0 = Debug|x64
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Release|Win32.ActiveCfg = Release|Win32
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Release|Win32.Build.0 = Release|Win32
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Release|x64.ActiveCfg = Release|x64
|
||||
{405DF558-062D-43AB-B7FB-02C4CF8B28CE}.Release|x64.Build.0 = Release|x64
|
||||
EndGlobalSection
|
||||
GlobalSection(SolutionProperties) = preSolution
|
||||
HideSolutionNode = FALSE
|
||||
EndGlobalSection
|
||||
GlobalSection(ExtensibilityGlobals) = postSolution
|
||||
SolutionGuid = {B6180127-9081-4796-819C-0E19EBFB2BC4}
|
||||
EndGlobalSection
|
||||
EndGlobal
|
295
Src/Plugins/Input/in_flv/in_flv.vcxproj
Normal file
295
Src/Plugins/Input/in_flv/in_flv.vcxproj
Normal file
@ -0,0 +1,295 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project DefaultTargets="Build" ToolsVersion="15.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup Label="ProjectConfigurations">
|
||||
<ProjectConfiguration Include="Debug|Win32">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Debug|x64">
|
||||
<Configuration>Debug</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|Win32">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>Win32</Platform>
|
||||
</ProjectConfiguration>
|
||||
<ProjectConfiguration Include="Release|x64">
|
||||
<Configuration>Release</Configuration>
|
||||
<Platform>x64</Platform>
|
||||
</ProjectConfiguration>
|
||||
</ItemGroup>
|
||||
<PropertyGroup Label="Globals">
|
||||
<ProjectGuid>{405DF558-062D-43AB-B7FB-02C4CF8B28CE}</ProjectGuid>
|
||||
<RootNamespace>in_flv</RootNamespace>
|
||||
<WindowsTargetPlatformVersion>10.0.19041.0</WindowsTargetPlatformVersion>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.Default.props" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="Configuration">
|
||||
<ConfigurationType>DynamicLibrary</ConfigurationType>
|
||||
<PlatformToolset>v142</PlatformToolset>
|
||||
<CharacterSet>Unicode</CharacterSet>
|
||||
</PropertyGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.props" />
|
||||
<ImportGroup Label="ExtensionSettings">
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<ImportGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'" Label="PropertySheets">
|
||||
<Import Project="$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props" Condition="exists('$(UserRootDir)\Microsoft.Cpp.$(Platform).user.props')" Label="LocalAppDataPlatform" />
|
||||
</ImportGroup>
|
||||
<PropertyGroup Label="UserMacros" />
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
<IncludePath>$(IncludePath)</IncludePath>
|
||||
<LibraryPath>$(LibraryPath)</LibraryPath>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<LinkIncremental>false</LinkIncremental>
|
||||
<OutDir>$(PlatformShortName)_$(Configuration)\</OutDir>
|
||||
<IntDir>$(PlatformShortName)_$(Configuration)\</IntDir>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg">
|
||||
<VcpkgEnabled>false</VcpkgEnabled>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
</PropertyGroup>
|
||||
<PropertyGroup Label="Vcpkg" Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<VcpkgConfiguration>Debug</VcpkgConfiguration>
|
||||
</PropertyGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;_DEBUG;_WINDOWS;_USRDLL;IN_FLV_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<SmallerTypeCheck>false</SmallerTypeCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
|
||||
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Debug|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>Disabled</Optimization>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;_DEBUG;_WINDOWS;_USRDLL;IN_FLV_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<MinimalRebuild>false</MinimalRebuild>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<BasicRuntimeChecks>EnableFastChecks</BasicRuntimeChecks>
|
||||
<SmallerTypeCheck>false</SmallerTypeCheck>
|
||||
<RuntimeLibrary>MultiThreadedDebugDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<FunctionLevelLinking>true</FunctionLevelLinking>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>ProgramDatabase</DebugInformationFormat>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>true</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\
|
||||
xcopy /Y /D $(IntDir)$(TargetName).pdb ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|Win32'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN32;NDEBUG;_WINDOWS;_USRDLL;IN_FLV_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<SmallerTypeCheck>false</SmallerTypeCheck>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<TargetMachine>MachineX86</TargetMachine>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemDefinitionGroup Condition="'$(Configuration)|$(Platform)'=='Release|x64'">
|
||||
<ClCompile>
|
||||
<Optimization>MinSpace</Optimization>
|
||||
<FavorSizeOrSpeed>Size</FavorSizeOrSpeed>
|
||||
<AdditionalIncludeDirectories>..\..\..\Wasabi;%(AdditionalIncludeDirectories)</AdditionalIncludeDirectories>
|
||||
<PreprocessorDefinitions>WIN64;NDEBUG;_WINDOWS;_USRDLL;IN_FLV_EXPORTS;UNICODE_INPUT_PLUGIN;_WIN32_WINNT=0x0601;WINVER=0x0601;_WIN32_IE=0x0A00;_CRT_SECURE_NO_WARNINGS;%(PreprocessorDefinitions)</PreprocessorDefinitions>
|
||||
<StringPooling>true</StringPooling>
|
||||
<MultiProcessorCompilation>true</MultiProcessorCompilation>
|
||||
<RuntimeLibrary>MultiThreadedDLL</RuntimeLibrary>
|
||||
<BufferSecurityCheck>true</BufferSecurityCheck>
|
||||
<TreatWChar_tAsBuiltInType>true</TreatWChar_tAsBuiltInType>
|
||||
<ForceConformanceInForLoopScope>true</ForceConformanceInForLoopScope>
|
||||
<WarningLevel>Level3</WarningLevel>
|
||||
<DebugInformationFormat>None</DebugInformationFormat>
|
||||
<DisableSpecificWarnings>4244;%(DisableSpecificWarnings)</DisableSpecificWarnings>
|
||||
<ProgramDataBaseFileName>$(IntDir)$(TargetName).pdb</ProgramDataBaseFileName>
|
||||
</ClCompile>
|
||||
<Link>
|
||||
<AdditionalDependencies>shlwapi.lib;%(AdditionalDependencies)</AdditionalDependencies>
|
||||
<OutputFile>$(OutDir)$(TargetName)$(TargetExt)</OutputFile>
|
||||
<GenerateDebugInformation>false</GenerateDebugInformation>
|
||||
<ProgramDatabaseFile>$(IntDir)$(TargetName).pdb</ProgramDatabaseFile>
|
||||
<SubSystem>Windows</SubSystem>
|
||||
<OptimizeReferences>true</OptimizeReferences>
|
||||
<EnableCOMDATFolding>true</EnableCOMDATFolding>
|
||||
<RandomizedBaseAddress>false</RandomizedBaseAddress>
|
||||
<ImportLibrary>$(IntDir)$(TargetName).lib</ImportLibrary>
|
||||
<ImageHasSafeExceptionHandlers>false</ImageHasSafeExceptionHandlers>
|
||||
<AdditionalLibraryDirectories>%(AdditionalLibraryDirectories)</AdditionalLibraryDirectories>
|
||||
</Link>
|
||||
<PostBuildEvent>
|
||||
<Command>xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\ </Command>
|
||||
<Message>Post build event: 'xcopy /Y /D $(OutDir)$(TargetName)$(TargetExt) ..\..\..\..\Build\Winamp_$(PlatformShortName)_$(Configuration)\Plugins\'</Message>
|
||||
</PostBuildEvent>
|
||||
</ItemDefinitionGroup>
|
||||
<ItemGroup>
|
||||
<ClCompile Include="..\..\..\nu\RingBuffer.cpp" />
|
||||
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp" />
|
||||
<ClCompile Include="AMFDispatch.cpp" />
|
||||
<ClCompile Include="AMFObject.cpp" />
|
||||
<ClCompile Include="BackgroundDownloader.cpp" />
|
||||
<ClCompile Include="ExtendedInfo.cpp" />
|
||||
<ClCompile Include="FileProcessor.cpp" />
|
||||
<ClCompile Include="FLVAudioHeader.cpp" />
|
||||
<ClCompile Include="FLVCOM.cpp" />
|
||||
<ClCompile Include="FLVHeader.cpp" />
|
||||
<ClCompile Include="FLVMetadata.cpp" />
|
||||
<ClCompile Include="FLVProcessor.cpp" />
|
||||
<ClCompile Include="FLVReader.cpp" />
|
||||
<ClCompile Include="FLVStreamHeader.cpp" />
|
||||
<ClCompile Include="FLVVideoHeader.cpp" />
|
||||
<ClCompile Include="main.cpp" />
|
||||
<ClCompile Include="PlayThread.cpp" />
|
||||
<ClCompile Include="ProgressiveProcessor.cpp" />
|
||||
<ClCompile Include="StreamProcessor.cpp" />
|
||||
<ClCompile Include="VideoThread.cpp" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="..\..\..\nu\RingBuffer.h" />
|
||||
<ClInclude Include="..\..\..\nu\SpillBuffer.h" />
|
||||
<ClInclude Include="..\..\..\nu\VideoClock.h" />
|
||||
<ClInclude Include="AMFDispatch.h" />
|
||||
<ClInclude Include="AMFObject.h" />
|
||||
<ClInclude Include="api__in_flv.h" />
|
||||
<ClInclude Include="BackgroundDownloader.h" />
|
||||
<ClInclude Include="FileProcessor.h" />
|
||||
<ClInclude Include="FLVAudioHeader.h" />
|
||||
<ClInclude Include="FLVCOM.h" />
|
||||
<ClInclude Include="FLVHeader.h" />
|
||||
<ClInclude Include="FLVMetadata.h" />
|
||||
<ClInclude Include="FLVProcessor.h" />
|
||||
<ClInclude Include="FLVReader.h" />
|
||||
<ClInclude Include="FLVStreamHeader.h" />
|
||||
<ClInclude Include="FLVUtil.h" />
|
||||
<ClInclude Include="FLVVideoHeader.h" />
|
||||
<ClInclude Include="ifc_flvaudiodecoder.h" />
|
||||
<ClInclude Include="ifc_flvvideodecoder.h" />
|
||||
<ClInclude Include="main.h" />
|
||||
<ClInclude Include="ProgressiveProcessor.h" />
|
||||
<ClInclude Include="resource.h" />
|
||||
<ClInclude Include="StreamProcessor.h" />
|
||||
<ClInclude Include="svc_flvdecoder.h" />
|
||||
<ClInclude Include="VideoThread.h" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_flv.rc" />
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ProjectReference Include="..\..\..\Wasabi\Wasabi.vcxproj">
|
||||
<Project>{3e0bfa8a-b86a-42e9-a33f-ec294f823f7f}</Project>
|
||||
</ProjectReference>
|
||||
</ItemGroup>
|
||||
<Import Project="$(VCTargetsPath)\Microsoft.Cpp.targets" />
|
||||
<ImportGroup Label="ExtensionTargets">
|
||||
</ImportGroup>
|
||||
</Project>
|
158
Src/Plugins/Input/in_flv/in_flv.vcxproj.filters
Normal file
158
Src/Plugins/Input/in_flv/in_flv.vcxproj.filters
Normal file
@ -0,0 +1,158 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<Project ToolsVersion="4.0" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
|
||||
<ItemGroup>
|
||||
<ClCompile Include="AMFDispatch.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="AMFObject.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="BackgroundDownloader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ExtendedInfo.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FileProcessor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVAudioHeader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVCOM.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVHeader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVMetadata.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVProcessor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVReader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVStreamHeader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="FLVVideoHeader.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="main.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="PlayThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="ProgressiveProcessor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="StreamProcessor.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="VideoThread.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\RingBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
<ClCompile Include="..\..\..\nu\SpillBuffer.cpp">
|
||||
<Filter>Source Files</Filter>
|
||||
</ClCompile>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ClInclude Include="VideoThread.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="svc_flvdecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="StreamProcessor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="resource.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ProgressiveProcessor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="main.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ifc_flvvideodecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="ifc_flvaudiodecoder.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVVideoHeader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVUtil.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVStreamHeader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVReader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVProcessor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVMetadata.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVAudioHeader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVHeader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FLVCOM.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="FileProcessor.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="BackgroundDownloader.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="api__in_flv.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AMFObject.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="AMFDispatch.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\nu\RingBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\nu\SpillBuffer.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
<ClInclude Include="..\..\..\nu\VideoClock.h">
|
||||
<Filter>Header Files</Filter>
|
||||
</ClInclude>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<Filter Include="Header Files">
|
||||
<UniqueIdentifier>{9916d7e8-2b6f-493b-8618-234dec0cefeb}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Ressource Files">
|
||||
<UniqueIdentifier>{888ab1e4-8d93-40b5-92f8-35339ac6179a}</UniqueIdentifier>
|
||||
</Filter>
|
||||
<Filter Include="Source Files">
|
||||
<UniqueIdentifier>{0f72d524-22df-4752-8f22-5c1db90c62b0}</UniqueIdentifier>
|
||||
</Filter>
|
||||
</ItemGroup>
|
||||
<ItemGroup>
|
||||
<ResourceCompile Include="in_flv.rc">
|
||||
<Filter>Ressource Files</Filter>
|
||||
</ResourceCompile>
|
||||
</ItemGroup>
|
||||
</Project>
|
432
Src/Plugins/Input/in_flv/main.cpp
Normal file
432
Src/Plugins/Input/in_flv/main.cpp
Normal file
@ -0,0 +1,432 @@
|
||||
#include "main.h"
|
||||
#include <shlwapi.h>
|
||||
#include "api__in_flv.h"
|
||||
#include <stdio.h>
|
||||
#include <api/service/waservicefactory.h>
|
||||
#include "../Winamp/wa_ipc.h"
|
||||
#include "resource.h"
|
||||
#include "FileProcessor.h"
|
||||
#include "FLVCOM.h"
|
||||
#include "VideoThread.h"
|
||||
#include "../f263/flv_f263_decoder.h"
|
||||
#include <strsafe.h>
|
||||
|
||||
#define FLV_PLUGIN_VERSION L"1.47"
|
||||
|
||||
template <class api_T>
|
||||
static void ServiceBuild(api_T *&api_t, GUID factoryGUID_t)
|
||||
{
|
||||
if (plugin.service)
|
||||
{
|
||||
waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
|
||||
if (factory)
|
||||
api_t = reinterpret_cast<api_T *>( factory->getInterface() );
|
||||
}
|
||||
}
|
||||
|
||||
template <class api_T>
|
||||
static void ServiceRelease(api_T *api_t, GUID factoryGUID_t)
|
||||
{
|
||||
if (plugin.service && api_t)
|
||||
{
|
||||
waServiceFactory *factory = plugin.service->service_getServiceByGuid(factoryGUID_t);
|
||||
if (factory)
|
||||
factory->releaseInterface(api_t);
|
||||
}
|
||||
api_t = NULL;
|
||||
}
|
||||
|
||||
/* Wasabi services */
|
||||
api_application *WASABI_API_APP=0;
|
||||
api_config *AGAVE_API_CONFIG=0;
|
||||
api_language *WASABI_API_LNG = 0;
|
||||
|
||||
In_Module *swf_mod = 0;
|
||||
HMODULE in_swf = 0;
|
||||
HINSTANCE WASABI_API_LNG_HINST = 0, WASABI_API_ORIG_HINST = 0;
|
||||
wchar_t pluginName[256] = {0};
|
||||
wchar_t *playFile=0;
|
||||
HANDLE killswitch, playthread=0;
|
||||
int g_length=-1000;
|
||||
bool video_only=false;
|
||||
int paused = 0;
|
||||
nu::VideoClock video_clock;
|
||||
int m_need_seek=-1;
|
||||
extern uint32_t last_timestamp;
|
||||
int pan = 0, volume = -666;
|
||||
wchar_t *stream_title=0;
|
||||
Nullsoft::Utility::LockGuard stream_title_guard;
|
||||
|
||||
// {B6CB4A7C-A8D0-4c55-8E60-9F7A7A23DA0F}
|
||||
static const GUID playbackConfigGroupGUID =
|
||||
{ 0xb6cb4a7c, 0xa8d0, 0x4c55, { 0x8e, 0x60, 0x9f, 0x7a, 0x7a, 0x23, 0xda, 0xf } };
|
||||
|
||||
void SetFileExtensions(void)
|
||||
{
|
||||
static char fileExtensionsString[256] = {0}; // "FLV\0Flash Video\0"
|
||||
char* end = 0;
|
||||
size_t remaining;
|
||||
StringCchCopyExA(fileExtensionsString, 256, "FLV", &end, &remaining, 0);
|
||||
StringCchCopyExA(end+1, remaining-1, WASABI_API_LNGSTRING(IDS_FLASH_VIDEO), 0, 0, 0);
|
||||
plugin.FileExtensions = fileExtensionsString;
|
||||
}
|
||||
|
||||
int Init()
|
||||
{
|
||||
if (!IsWindow(plugin.hMainWindow))
|
||||
return IN_INIT_FAILURE;
|
||||
|
||||
ServiceBuild(AGAVE_API_CONFIG, AgaveConfigGUID);
|
||||
ServiceBuild(WASABI_API_APP, applicationApiServiceGuid);
|
||||
ServiceBuild(WASABI_API_LNG, languageApiGUID);
|
||||
// need to have this initialised before we try to do anything with localisation features
|
||||
WASABI_API_START_LANG(plugin.hDllInstance,InFlvLangGUID);
|
||||
|
||||
if (plugin.service->service_getServiceByGuid(flv_h263_guid))
|
||||
StringCchPrintfW(pluginName,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_FLV),FLV_PLUGIN_VERSION L" (h)");
|
||||
else
|
||||
StringCchPrintfW(pluginName,256,WASABI_API_LNGSTRINGW(IDS_NULLSOFT_FLV),FLV_PLUGIN_VERSION);
|
||||
plugin.description = (char*)pluginName;
|
||||
SetFileExtensions();
|
||||
|
||||
DispatchInfo flvDisp = { L"FLV", &flvCOM };
|
||||
SendMessage(plugin.hMainWindow, WM_WA_IPC, (WPARAM)&flvDisp, IPC_ADD_DISPATCH_OBJECT);
|
||||
|
||||
killswitch = CreateEvent(NULL, TRUE, FALSE, NULL);
|
||||
return IN_INIT_SUCCESS;
|
||||
}
|
||||
|
||||
void Quit()
|
||||
{
|
||||
CloseHandle(killswitch);
|
||||
ServiceRelease(AGAVE_API_CONFIG, AgaveConfigGUID);
|
||||
ServiceRelease(WASABI_API_APP, applicationApiServiceGuid);
|
||||
ServiceRelease(WASABI_API_LNG, languageApiGUID);
|
||||
if (in_swf)
|
||||
FreeLibrary(in_swf);
|
||||
}
|
||||
|
||||
void GetFileInfo(const wchar_t *file, wchar_t *title, int *length_in_ms)
|
||||
{
|
||||
if (!file || !*file)
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->GetFileInfo(file, title, length_in_ms);
|
||||
return;
|
||||
}
|
||||
|
||||
file = playFile;
|
||||
}
|
||||
|
||||
// no title support as it's not always stored in a standard way in FLV
|
||||
if (title)
|
||||
{
|
||||
Nullsoft::Utility::AutoLock stream_lock(stream_title_guard);
|
||||
if (stream_title && (!file || !file[0]))
|
||||
{
|
||||
lstrcpyn(title, stream_title, GETFILEINFO_TITLE_LENGTH);
|
||||
}
|
||||
else
|
||||
{
|
||||
lstrcpyn(title, file ? file : L"", GETFILEINFO_TITLE_LENGTH);
|
||||
PathStripPath(title);
|
||||
}
|
||||
}
|
||||
|
||||
// calculate length
|
||||
if (file == playFile) // currently playing song?
|
||||
*length_in_ms = g_length; // easy!
|
||||
else if (PathIsURLW(file)) // don't calculate lengths for URLs since we'd have to connect
|
||||
{
|
||||
*length_in_ms = -1;
|
||||
}
|
||||
else
|
||||
{
|
||||
// open the file and find the "duration" metadata
|
||||
FileProcessor processor(file);
|
||||
|
||||
size_t frameIndex=0;
|
||||
FrameData frameData;
|
||||
int length=-1;
|
||||
bool found=false;
|
||||
do
|
||||
{
|
||||
// enumerate the frames as we process
|
||||
// this function will fail the first few times
|
||||
// because Process() hasn't been called enough
|
||||
if (processor.GetFrame(frameIndex, frameData))
|
||||
{
|
||||
if (frameData.header.type == FLV::FRAME_TYPE_METADATA)
|
||||
{
|
||||
if (processor.Seek(frameData.location + 15) == -1)
|
||||
break;
|
||||
|
||||
size_t dataSize = frameData.header.dataSize;
|
||||
uint8_t *metadatadata= (uint8_t *)calloc(dataSize, sizeof(uint8_t));
|
||||
if (metadatadata)
|
||||
{
|
||||
size_t bytesRead = processor.Read(metadatadata, dataSize);
|
||||
if (bytesRead != dataSize)
|
||||
{
|
||||
free(metadatadata);
|
||||
break;
|
||||
}
|
||||
FLVMetadata metadata;
|
||||
metadata.Read(metadatadata, dataSize);
|
||||
|
||||
for ( FLVMetadata::Tag *tag : metadata.tags )
|
||||
{
|
||||
if ( !_wcsicmp( tag->name.str, L"onMetaData" ) )
|
||||
{
|
||||
AMFType *duration = tag->parameters->array[ L"duration" ];
|
||||
if ( duration )
|
||||
{
|
||||
length = (int)( AMFGetDouble( duration ) * 1000.0 );
|
||||
found = true;
|
||||
}
|
||||
}
|
||||
}
|
||||
free(metadatadata);
|
||||
}
|
||||
else
|
||||
break;
|
||||
}
|
||||
frameIndex++;
|
||||
}
|
||||
}
|
||||
while (!found && processor.Process() == FLV_OK); // for local files, any return value other than FLV_OK is a failure
|
||||
*length_in_ms = length;
|
||||
}
|
||||
}
|
||||
|
||||
int InfoBox(const wchar_t *file, HWND hwndParent)
|
||||
{
|
||||
return INFOBOX_UNCHANGED;
|
||||
}
|
||||
|
||||
int IsOurFile(const wchar_t *file)
|
||||
{
|
||||
return 0;
|
||||
}
|
||||
|
||||
int Play(const wchar_t *file)
|
||||
{
|
||||
{
|
||||
Nullsoft::Utility::AutoLock stream_lock(stream_title_guard);
|
||||
free(stream_title);
|
||||
stream_title=0;
|
||||
}
|
||||
if (!videoOutput)
|
||||
videoOutput = (IVideoOutput *)SendMessage(plugin.hMainWindow, WM_WA_IPC, 0, IPC_GET_IVIDEOOUTPUT);
|
||||
|
||||
video_only=false;
|
||||
m_need_seek = -1;
|
||||
video_clock.Reset();
|
||||
paused=0;
|
||||
ResetEvent(killswitch);
|
||||
free(playFile);
|
||||
playFile = _wcsdup(file);
|
||||
playthread=CreateThread(0, 0, PlayProcedure, 0, 0, 0);
|
||||
SetThreadPriority(playthread, (INT)AGAVE_API_CONFIG->GetInt(playbackConfigGroupGUID, L"priority", THREAD_PRIORITY_HIGHEST));
|
||||
return 0; // success
|
||||
}
|
||||
|
||||
|
||||
void Pause()
|
||||
{
|
||||
paused = 1;
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->Pause();
|
||||
}
|
||||
else if (video_only)
|
||||
{
|
||||
video_clock.Pause();
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.outMod->Pause(1);
|
||||
}
|
||||
}
|
||||
|
||||
void UnPause()
|
||||
{
|
||||
paused = 0;
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->UnPause();
|
||||
}
|
||||
else if (video_only)
|
||||
{
|
||||
video_clock.Unpause();
|
||||
}
|
||||
else
|
||||
{
|
||||
plugin.outMod->Pause(0);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
int IsPaused()
|
||||
{
|
||||
if (swf_mod)
|
||||
return swf_mod->IsPaused();
|
||||
|
||||
return paused;
|
||||
}
|
||||
|
||||
void Stop()
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->Stop();
|
||||
swf_mod=0;
|
||||
return;
|
||||
}
|
||||
SetEvent(killswitch);
|
||||
WaitForSingleObject(playthread, INFINITE);
|
||||
playthread=0;
|
||||
plugin.outMod->Close();
|
||||
plugin.SAVSADeInit();
|
||||
paused=0;
|
||||
}
|
||||
|
||||
int GetLength()
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
return swf_mod->GetLength();
|
||||
}
|
||||
return g_length;
|
||||
}
|
||||
|
||||
int GetOutputTime()
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
return swf_mod->GetOutputTime();
|
||||
}
|
||||
else if (video_only)
|
||||
{
|
||||
return video_clock.GetOutputTime();
|
||||
}
|
||||
else if (plugin.outMod)
|
||||
{
|
||||
return plugin.outMod->GetOutputTime();
|
||||
}
|
||||
else
|
||||
return 0;
|
||||
}
|
||||
|
||||
void SetOutputTime(int time_in_ms)
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->SetOutputTime(time_in_ms);
|
||||
return ;
|
||||
}
|
||||
m_need_seek=time_in_ms;
|
||||
}
|
||||
|
||||
|
||||
void SetVolume(int _volume)
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->SetVolume(_volume);
|
||||
return ;
|
||||
}
|
||||
volume = _volume;
|
||||
if (plugin.outMod)
|
||||
plugin.outMod->SetVolume(volume);
|
||||
}
|
||||
|
||||
void SetPan(int _pan)
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->SetPan(_pan);
|
||||
return ;
|
||||
}
|
||||
pan = _pan;
|
||||
if (plugin.outMod)
|
||||
plugin.outMod->SetPan(pan);
|
||||
}
|
||||
|
||||
void EQSet(int on, char data[10], int preamp)
|
||||
{
|
||||
if (swf_mod)
|
||||
{
|
||||
swf_mod->EQSet(on, data, preamp);
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
int DoAboutMessageBox(HWND parent, wchar_t* title, wchar_t* message)
|
||||
{
|
||||
MSGBOXPARAMS msgbx = {sizeof(MSGBOXPARAMS),0};
|
||||
msgbx.lpszText = message;
|
||||
msgbx.lpszCaption = title;
|
||||
msgbx.lpszIcon = MAKEINTRESOURCE(102);
|
||||
msgbx.hInstance = GetModuleHandle(0);
|
||||
msgbx.dwStyle = MB_USERICON;
|
||||
msgbx.hwndOwner = parent;
|
||||
return MessageBoxIndirect(&msgbx);
|
||||
}
|
||||
|
||||
void About(HWND hwndParent)
|
||||
{
|
||||
wchar_t message[1024] = {0}, text[1024] = {0};
|
||||
WASABI_API_LNGSTRINGW_BUF(IDS_NULLSOFT_FLV_OLD,text,1024);
|
||||
StringCchPrintfW(message, 1024, WASABI_API_LNGSTRINGW(IDS_ABOUT_TEXT),
|
||||
plugin.description, TEXT(__DATE__));
|
||||
DoAboutMessageBox(hwndParent,text,message);
|
||||
}
|
||||
|
||||
In_Module plugin =
|
||||
{
|
||||
IN_VER_RET,
|
||||
"nullsoft(in_flv.dll)",
|
||||
0,
|
||||
0,
|
||||
0 /*"FLV\0Flash Video\0"*/,
|
||||
1, // not seekable, for now
|
||||
1,
|
||||
About,
|
||||
About,
|
||||
Init,
|
||||
Quit,
|
||||
GetFileInfo,
|
||||
InfoBox,
|
||||
IsOurFile,
|
||||
Play,
|
||||
Pause,
|
||||
UnPause,
|
||||
IsPaused,
|
||||
Stop,
|
||||
GetLength,
|
||||
GetOutputTime,
|
||||
SetOutputTime,
|
||||
SetVolume,
|
||||
SetPan,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
0,
|
||||
EQSet,
|
||||
0,
|
||||
0
|
||||
};
|
||||
|
||||
extern "C" __declspec(dllexport) In_Module * winampGetInModule2()
|
||||
{
|
||||
return &plugin;
|
||||
}
|
27
Src/Plugins/Input/in_flv/main.h
Normal file
27
Src/Plugins/Input/in_flv/main.h
Normal file
@ -0,0 +1,27 @@
|
||||
#ifndef NULLSOFT_IN_FLV_MAIN_H
|
||||
#define NULLSOFT_IN_FLV_MAIN_H
|
||||
|
||||
#include <windows.h>
|
||||
#include "../Winamp/in2.h"
|
||||
#include "../nu/VideoClock.h"
|
||||
#include "../nu/AutoLock.h"
|
||||
|
||||
/* main.cpp */
|
||||
extern In_Module plugin;
|
||||
extern In_Module *swf_mod;
|
||||
extern HMODULE in_swf;
|
||||
extern wchar_t *playFile;
|
||||
extern HANDLE killswitch;
|
||||
extern int m_need_seek;
|
||||
extern int paused;
|
||||
extern int g_length;
|
||||
extern nu::VideoClock video_clock;
|
||||
extern wchar_t *stream_title;
|
||||
extern Nullsoft::Utility::LockGuard stream_title_guard;
|
||||
|
||||
/* PlayThread.cpp */
|
||||
|
||||
DWORD CALLBACK PlayProcedure(LPVOID param);
|
||||
|
||||
|
||||
#endif
|
23
Src/Plugins/Input/in_flv/resource.h
Normal file
23
Src/Plugins/Input/in_flv/resource.h
Normal file
@ -0,0 +1,23 @@
|
||||
//{{NO_DEPENDENCIES}}
|
||||
// Microsoft Visual C++ generated include file.
|
||||
// Used by in_flv.rc
|
||||
//
|
||||
#define IDS_NULLSOFT_FLV_OLD 0
|
||||
#define IDS_BUFFERING 2
|
||||
#define IDS_FLASH_VIDEO 3
|
||||
#define IDS_STRING4 4
|
||||
#define IDS_VP6_FLASH_VIDEO 4
|
||||
#define IDS_FAMILY_STRING 5
|
||||
#define IDS_ABOUT_TEXT 6
|
||||
#define IDS_NULLSOFT_FLV 65534
|
||||
|
||||
// Next default values for new objects
|
||||
//
|
||||
#ifdef APSTUDIO_INVOKED
|
||||
#ifndef APSTUDIO_READONLY_SYMBOLS
|
||||
#define _APS_NEXT_RESOURCE_VALUE 105
|
||||
#define _APS_NEXT_COMMAND_VALUE 40001
|
||||
#define _APS_NEXT_CONTROL_VALUE 1001
|
||||
#define _APS_NEXT_SYMED_VALUE 101
|
||||
#endif
|
||||
#endif
|
52
Src/Plugins/Input/in_flv/svc_flvdecoder.h
Normal file
52
Src/Plugins/Input/in_flv/svc_flvdecoder.h
Normal file
@ -0,0 +1,52 @@
|
||||
#pragma once
|
||||
|
||||
#include <bfc/dispatch.h>
|
||||
#include <api/service/services.h>
|
||||
|
||||
class ifc_flvaudiodecoder;
|
||||
class ifc_flvvideodecoder;
|
||||
class svc_flvdecoder : public Dispatchable
|
||||
{
|
||||
protected:
|
||||
svc_flvdecoder() {}
|
||||
~svc_flvdecoder() {}
|
||||
public:
|
||||
static FOURCC getServiceType() { return WaSvc::FLVDECODER; }
|
||||
enum
|
||||
{
|
||||
CREATEDECODER_SUCCESS = 0,
|
||||
CREATEDECODER_NOT_MINE = -1, // graceful failure
|
||||
CREATEDECODER_FAILURE = 1, // generic failure - format_type is ours but we weren't able to create the decoder
|
||||
};
|
||||
int CreateAudioDecoder(int stereo, int bits, int sample_rate, int format, ifc_flvaudiodecoder **decoder);
|
||||
int CreateVideoDecoder(int format_type, int width, int height, ifc_flvvideodecoder **decoder);
|
||||
int HandlesAudio(int format_type);
|
||||
int HandlesVideo(int format_type);
|
||||
DISPATCH_CODES
|
||||
{
|
||||
CREATE_AUDIO_DECODER = 0,
|
||||
CREATE_VIDEO_DECODER = 1,
|
||||
HANDLES_AUDIO=2,
|
||||
HANDLES_VIDEO=3,
|
||||
};
|
||||
};
|
||||
|
||||
inline int svc_flvdecoder::CreateAudioDecoder(int stereo, int bits, int sample_rate, int format, ifc_flvaudiodecoder **decoder)
|
||||
{
|
||||
return _call(CREATE_AUDIO_DECODER, (int)CREATEDECODER_NOT_MINE, stereo, bits, sample_rate, format, decoder);
|
||||
}
|
||||
|
||||
inline int svc_flvdecoder::CreateVideoDecoder(int format_type, int width, int height, ifc_flvvideodecoder **decoder)
|
||||
{
|
||||
return _call(CREATE_VIDEO_DECODER, (int)CREATEDECODER_NOT_MINE, format_type,width, height, decoder);
|
||||
}
|
||||
|
||||
inline int svc_flvdecoder::HandlesAudio(int format_type)
|
||||
{
|
||||
return _call(HANDLES_AUDIO, (int)CREATEDECODER_NOT_MINE, format_type);
|
||||
}
|
||||
|
||||
inline int svc_flvdecoder::HandlesVideo(int format_type)
|
||||
{
|
||||
return _call(HANDLES_VIDEO, (int)CREATEDECODER_NOT_MINE, format_type);
|
||||
}
|
39
Src/Plugins/Input/in_flv/version.rc2
Normal file
39
Src/Plugins/Input/in_flv/version.rc2
Normal file
@ -0,0 +1,39 @@
|
||||
|
||||
/////////////////////////////////////////////////////////////////////////////
|
||||
//
|
||||
// Version
|
||||
//
|
||||
#include "../../../Winamp/buildType.h"
|
||||
VS_VERSION_INFO VERSIONINFO
|
||||
FILEVERSION 1,47,0,0
|
||||
PRODUCTVERSION WINAMP_PRODUCTVER
|
||||
FILEFLAGSMASK 0x17L
|
||||
#ifdef _DEBUG
|
||||
FILEFLAGS 0x1L
|
||||
#else
|
||||
FILEFLAGS 0x0L
|
||||
#endif
|
||||
FILEOS 0x4L
|
||||
FILETYPE 0x2L
|
||||
FILESUBTYPE 0x0L
|
||||
BEGIN
|
||||
BLOCK "StringFileInfo"
|
||||
BEGIN
|
||||
BLOCK "040904b0"
|
||||
BEGIN
|
||||
VALUE "CompanyName", "Winamp SA"
|
||||
VALUE "FileDescription", "Winamp Input Plug-in"
|
||||
VALUE "FileVersion", "1,47,0,0"
|
||||
VALUE "InternalName", "Nullsoft Flash Video Decoder"
|
||||
VALUE "LegalCopyright", "Copyright <20> 2006-2023 Winamp SA"
|
||||
VALUE "LegalTrademarks", "Nullsoft and Winamp are trademarks of Winamp SA"
|
||||
VALUE "OriginalFilename", "in_flv.dll"
|
||||
VALUE "ProductName", "Winamp"
|
||||
VALUE "ProductVersion", STR_WINAMP_PRODUCTVER
|
||||
END
|
||||
END
|
||||
BLOCK "VarFileInfo"
|
||||
BEGIN
|
||||
VALUE "Translation", 0x409, 1200
|
||||
END
|
||||
END
|
Reference in New Issue
Block a user