Initial community commit

This commit is contained in:
Jef
2024-09-24 14:54:57 +02:00
parent 537bcbc862
commit 20d28e80a5
16810 changed files with 4640254 additions and 2 deletions

View File

@ -0,0 +1,21 @@
#ifndef NULLSOFT_NSV_NSVPLAY_IDATAREADER_H
#define NULLSOFT_NSV_NSVPLAY_IDATAREADER_H
#include <stddef.h>
#include <bfc/platform/types.h>
class IDataReader
{
public:
virtual ~IDataReader() { }
virtual size_t read(char *buf, size_t len)=0; // returns bytes read
virtual bool iseof()=0;
virtual char *geterror()=0;
virtual char *gettitle() { return 0; }
virtual char *getheader(char *header_name) { return 0; }
virtual bool canseek() { return 0; }
virtual int seek(uint64_t newpos) { return 1; }
virtual uint64_t getsize() { return ~0; }
};
#endif

661
Src/nsv/nsvplay/about.h Normal file
View File

@ -0,0 +1,661 @@
#ifndef _ABOUT_H_
#define _ABOUT_H_
#include <math.h>
#pragma warning(disable : 4731)
static HWND about_hwnd;
#ifndef NO_ABOUT_EGG
#define BITMAP_W 100
#define BITMAP_H 64
static HDC m_hdc;
static HBITMAP m_hbm;
static char *m_dib;
static char ge_fbuf[(BITMAP_H+1)*BITMAP_W+1];
static int ge_tmp;
#define M_NUM_FX 9
static int m_effect;
#define BLOBS_NPOINTS 8
static int BLOBPOINTS[4*BLOBS_NPOINTS];
static int fire_textcnt,fire_textpos;
static char *fire_texts[]={
"nsvplay",
"Nullsoft 2003-8",
"",
"greets to",
"winamp forums",
"#nullsoft",
"britney spears",
"p.s.",
"DrO was here",
" <3",
"",
};
static HDC scrolldc;
static HBITMAP scrollbitmap;
static char *scrolldib;
static const char scrolltext[]="nullsoft presents you nSVpLAY hidden part! cracked by rOn +5 trainer by deadbeef ";
static int scrolloffs;
static char *rototmp;
#endif //NO_ABOUT_EGG
static INT_PTR CALLBACK aboutProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam)
{
switch (uMsg)
{
case WM_INITDIALOG:
#ifndef NO_ABOUT_EGG
{
HBITMAP about_bmp = NULL;
m_effect=M_NUM_FX-1;
// try to use the localised image and then revert to the normal dll image
about_bmp = (HBITMAP)LoadImage((HINSTANCE)lParam,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
if(about_bmp == NULL){
about_bmp = (HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(IDB_BITMAP1),IMAGE_BITMAP,0,0,LR_SHARED);
}
// set on control with id of -1 (0xFFFFFFFF) or 0xFFFF (not sure how/why this happened)
SendDlgItemMessage(hwndDlg,0xFFFF,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)about_bmp);
SendDlgItemMessage(hwndDlg,-1,STM_SETIMAGE,IMAGE_BITMAP,(LPARAM)about_bmp);
}
#endif
SetDlgItemText(hwndDlg,IDC_VERSION,WNDMENU_CAPTION);
break;
case WM_COMMAND:
switch (LOWORD(wParam))
{
case IDCANCEL:
case IDOK:
#ifndef MODAL_ABOUT
DestroyWindow(hwndDlg);
#else
EndDialog(hwndDlg,1);
#endif
return FALSE;
}
break;
case WM_DESTROY:
about_hwnd=NULL;
#ifndef NO_ABOUT_EGG
if (m_hbm) DeleteObject(m_hbm);
if (m_hdc) DeleteDC(m_hdc);
if (scrollbitmap) DeleteObject(scrollbitmap);
if (scrolldc) DeleteDC(scrolldc);
m_hbm=0;
m_hdc=0;
scrollbitmap=0;
scrolldc=0;
if (rototmp) free(rototmp);
rototmp=NULL;
#endif
break;
#ifndef NO_ABOUT_EGG
case WM_LBUTTONDBLCLK :
//easter eggs :)
if (++m_effect >= M_NUM_FX) m_effect=1;
KillTimer(hwndDlg,0x1234);
if (m_hbm) DeleteObject(m_hbm);
if (m_hdc) DeleteDC(m_hdc);
if (scrollbitmap) DeleteObject(scrollbitmap);
if (scrolldc) DeleteDC(scrolldc);
scrollbitmap=0;
scrolldc=0;
if (rototmp) free(rototmp);
rototmp=NULL;
{
struct
{
BITMAPINFO bmi;
RGBQUAD more_bmiColors[256];
LPVOID data;
} m_bitmap;
m_hdc = CreateCompatibleDC(NULL);
m_bitmap.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bitmap.bmi.bmiHeader.biPlanes = 1;
m_bitmap.bmi.bmiHeader.biBitCount = 8;
m_bitmap.bmi.bmiHeader.biCompression = BI_RGB;
m_bitmap.bmi.bmiHeader.biSizeImage = 0;
m_bitmap.bmi.bmiHeader.biClrUsed = 256;
m_bitmap.bmi.bmiHeader.biClrImportant = 256;
m_bitmap.bmi.bmiHeader.biWidth = BITMAP_W;
m_bitmap.bmi.bmiHeader.biHeight = -BITMAP_H;
m_bitmap.bmi.bmiHeader.biSizeImage = BITMAP_W*BITMAP_H;
memset(ge_fbuf,0,BITMAP_W*(BITMAP_H+1));
fire_textcnt=0;
if (m_effect < 3)
{
unsigned char *t=(unsigned char *)m_bitmap.bmi.bmiColors;
int x=255;
int adj=!!m_effect;
t[0]=t[1]=t[2]=0;
t+=4;
while (x)
{
if (m_effect == 2)
{
if (x > 128)
{
t[0]=0;
t[1]=((256-x)*2)/3;
t[2]=(256-x)*2;
}
else
{
t[0]=256-x*2;
t[1]=255/3 + ((256-x)*2)/3;
t[2]=255;
}
}
else
{
int a=x*2;
if (a>255) a=255;
t[0]=a;
t[2-adj]=a;
a+=a;
if (a > 255) a=255;
t[1+adj]=a;
}
t+=4;
x--;
}
}
if(m_effect==0)
{
//sine scroll
struct
{
BITMAPINFO bmi;
RGBQUAD more_bmiColors[1];
LPVOID data;
} m_bitmap;
scrolldc = CreateCompatibleDC(NULL);
m_bitmap.bmi.bmiHeader.biSize = sizeof(BITMAPINFOHEADER);
m_bitmap.bmi.bmiHeader.biPlanes = 1;
m_bitmap.bmi.bmiHeader.biBitCount = 8;
m_bitmap.bmi.bmiHeader.biCompression = BI_RGB;
m_bitmap.bmi.bmiHeader.biSizeImage = 0;
m_bitmap.bmi.bmiHeader.biClrUsed = 1;
m_bitmap.bmi.bmiHeader.biClrImportant = 1;
m_bitmap.bmi.bmiHeader.biWidth = 128;
m_bitmap.bmi.bmiHeader.biHeight = -32;
m_bitmap.bmi.bmiHeader.biSizeImage = 128*32;
scrollbitmap = CreateDIBSection(scrolldc,&m_bitmap.bmi,DIB_RGB_COLORS, &m_bitmap.data, NULL, 0);
SelectObject(scrolldc,scrollbitmap);
SetBkMode(scrolldc,TRANSPARENT);
scrolldib = (char *)m_bitmap.data;
scrolloffs = 0;
}
if (m_effect == 1)
{
int *t=BLOBPOINTS;
int a=BLOBS_NPOINTS;
while (a)
{
t[0]=(rand()&127) - 64;
t[2]=(rand()&127) - 64;
t[1]=t[3]=a;
t+=4;
a--;
}
}
if((m_effect >= 3 && m_effect <= 8)) //rotozooms
{
rototmp=(char *)malloc(65536+256);
//generate noise texture
memset(rototmp,0xd0,65536+256);
__asm
{
mov cx,0ffffh
xor ax,ax
xor ebx,ebx
xor dx,dx
mov edi,[rototmp]
TEXGEN:
mov bx,cx
add ax,cx
rol ax,cl
mov dh,al
sar dh,5
adc dl,dh
adc dl,[edi+ebx+255]
shr dl,1
mov [edi+ebx],dl
not bh
mov [edi+ebx],dl
loop TEXGEN
}
if ((!!(GetAsyncKeyState(VK_SHIFT)&0x8000)) ^ (m_effect==5)) // secondary easter egg, hah!
for (int x = 0; x < 256*256; x ++) rototmp[x] = 0x40 + ((x^(x>>8)) & 0x1F);
rototmp[0]=rototmp[1];
rototmp[0xff00]=rototmp[0xff01];
//generate palette
unsigned char *t=((unsigned char *)(&m_bitmap.bmi.bmiColors[0x40]));
for(int i=0;i<0x20;i++)
{
int r,g,b;
switch(m_effect)
{
case 3: r=i*4; g=i*5; b=i*8; break;
case 4: r=i*4; g=i*7; b=i*8; break;
case 5: r=i*8; g=i*2; b=i*2; break;
case 6: r=i*6; g=i*8; b=i*6; break;
case 7: r=i*8; g=i*6; b=i*8; break;
case 8: r=i*6; g=i*6; b=i*8; break;
}
t[0]=b;
t[1]=g;
t[2]=r;
t+=4;
}
}
m_hbm = CreateDIBSection(m_hdc,&m_bitmap.bmi,DIB_RGB_COLORS, &m_bitmap.data, NULL, 0);
SelectObject(m_hdc,m_hbm);
m_dib = (char *)m_bitmap.data;
SetTimer(hwndDlg,0x1234,35,NULL);
}
break;
case WM_TIMER:
{
int nomemcpy=0;
static float inc=0;
inc++;
if (m_effect == 0) // oldsk00l sine scroll
{
double blah=3.14/BITMAP_W;
double val1=1;
double val2=(BITMAP_H-16)/2;
static double vinc=0;
__asm
{
mov edi, offset ge_fbuf
mov ecx, BITMAP_W*BITMAP_H
xor eax, eax
rep stosb
}
for(int j=0;j<8;j++) {
for(int k=0;k<16;k++) {
int col=255-k*(256/16);
if(col<128) col=128+(128-col);
memset(ge_fbuf+(k+(int)((BITMAP_H-16)*(cos(vinc+(j*8)*3.14/50)+1)/2))*BITMAP_W,col,BITMAP_W);
}
}
__asm
{
mov edi, offset ge_fbuf
mov ecx, 0
mov esi, dword ptr [scrolldib]
SINELOOP:
mov [ge_tmp], ecx
fild dword ptr [ge_tmp]
fmul qword ptr [blah]
fadd qword ptr [vinc]
fcos
fadd qword ptr [val1]
fmul qword ptr [val2]
fistp dword ptr [ge_tmp]
mov eax, [ge_tmp]
mov ebx, eax
mov edx, BITMAP_W
mul edx
mov dh, bl
push ecx
mov ebx, 0
mov ecx, 16
SINELOOP2:
cmp byte ptr [esi+ebx],0
je SINECONT
mov dl,0ffh
sub dl,dh
sub dl,cl
mov [edi+eax], dl
SINECONT:
add eax, BITMAP_W
add ebx, 128
loop SINELOOP2
pop ecx
inc esi
inc edi
inc ecx
cmp ecx, BITMAP_W
jl SINELOOP
mov edi, [scrolldib]
mov esi, edi
inc esi
mov ebx, 32
SINESCROLL:
mov ecx, 127
rep movsb
inc edi
inc esi
dec ebx
jnz SINESCROLL
}
vinc+=0.2;
scrolloffs++;
if((scrolloffs&7)==7)
{
int o=scrolloffs/8;
if(!scrolltext[o]) scrolloffs=0;
else TextOutA(scrolldc,100,0,&scrolltext[o],1);
}
}
else if (m_effect == 1) // blobs
{
int *blobptr=BLOBPOINTS;
int i=BLOBS_NPOINTS*2;
while (i--)
{
if (blobptr[0] > 0) blobptr[1]--;
else blobptr[1]++;
int a=blobptr[1];
if (rand()&1) a++;
else a--;
blobptr[0]+=a/8;
blobptr+=2;
}
int y=BITMAP_H;
unsigned char *p=(unsigned char *)ge_fbuf;
while (y--)
{
int x=BITMAP_W;
while (x--)
{
blobptr=BLOBPOINTS;
i=BLOBS_NPOINTS;
double sum=0.0;
while (i--)
{
double as=(x-(BITMAP_W/2)) - blobptr[0];
double bs=(y-(BITMAP_H/2)) - blobptr[2];
sum+=sqrt(as*as + bs*bs);
blobptr+=4;
}
sum *= 6.0/BLOBS_NPOINTS;
int a=(int)sum;
if (a > 0xff) a= 0xff;
*p++=a;
}
}
}
else if(m_effect==2) //gayfire
{
unsigned char *p=(unsigned char *)ge_fbuf;
int x;
unsigned char *t=p + BITMAP_W*BITMAP_H;
for (x = 0; x < BITMAP_W; x ++)
{
int a=*t - 10;
if ((rand()&0x7) == 7) a+=100;
if (a < 0) a=0;
else if (a > 192) a=192;
*t++=a;//rand()&0xf0;
}
int y;
for (y = 0; y < BITMAP_H; y ++)
{
*p++=p[0]/4 + p[BITMAP_W]/2 + p[BITMAP_W+1]/4;
for (x = 1; x < BITMAP_W-1; x ++)
*p++=p[0]/4 + p[BITMAP_W]/4 + p[BITMAP_W-1]/4 + p[BITMAP_W+1]/4;
*p++=p[0]/4 + p[BITMAP_W]/2 + p[BITMAP_W-1]/4;
}
if (fire_textcnt-- <= 0)
{
memcpy(m_dib,ge_fbuf,BITMAP_W*BITMAP_H);
SetBkMode(m_hdc,TRANSPARENT);
SetTextColor(m_hdc,RGB(255,255,255));
RECT r={0,0,BITMAP_W,BITMAP_H};
DrawTextA(m_hdc,fire_texts[fire_textpos%(sizeof(fire_texts)/sizeof(fire_texts[0]))],-1,&r,DT_CENTER|DT_VCENTER|DT_SINGLELINE);
if (fire_textcnt < -30)
{
memcpy(ge_fbuf,m_dib,BITMAP_W*BITMAP_H);
fire_textpos++;
fire_textcnt=30;
}
else nomemcpy=1;
}
}
else if (m_effect == 3) //rotozoom
{
char *p=ge_fbuf;
static float angle=0;
for(int j=-32;j<32;j++)
for(int i=-50;i<50;i++)
{
//rotozoom
double x=(i*cosf(angle)-j*sinf(angle))*(2+cosf(angle*1.4f));
double y=(i*sinf(angle)+j*cosf(angle))*(2+cosf(angle*1.4f));
//slime
x+=cos(angle*x)*4;
y+=sin(angle*y)*4;
int x2=(int)x & 0xff;
int y2=(int)y & 0xff;
*p++=rototmp[256*y2+x2];
}
angle+=0.01f;
}
else if (m_effect == 4) //rotozoom 2
{
char *p=ge_fbuf;
double angle=cos(inc*0.01f)*2;
for(int j=-32;j<32;j++)
for(int i=-50;i<50;i++)
{
//position
double x=i-cos(inc*0.013f)*64;
double y=j+sin(inc*0.013f)*64;
//slime
x+=cos((angle+i)*50)*cos(angle)*4;
y+=sin((angle+j)*50)*cos(angle)*4;
//rotozoom
double x3=(x*cos(angle)-y*sin(angle))*(2+cos(angle*1.4f));
double y3=(x*sin(angle)+y*cos(angle))*(2+cos(angle*1.4f));
int x2=(int)x3 & 0xff;
int y2=(int)y3 & 0xff;
*p++=rototmp[256*y2+x2];
}
}
else if (m_effect == 5) //3d rotozoom
{
char *p=ge_fbuf;
static float angle=0;
const double b=50;
for(int j=-32;j<32;j++)
for(int i=-50;i<50;i++)
{
//rotozoom
double x=(i*cos(angle)+j*sin(angle));//*(2+cos(angle*1.4f));
double y=(i*sin(angle)-j*cos(angle));//*(2+cos(angle*1.4f));
//gay z-projection
x*=b/(((double)j+32));
y*=b/(((double)j+32));
//position
x-=cos(inc*0.013f)*64;
y+=sin(inc*0.013f)*64;
int x2=(int)x & 0xff;
int y2=(int)y & 0xff;
char c=rototmp[256*y2+x2];
*p++=0x40+((c-0x40)*(j+32)/56);
}
angle+=0.01f;
//b++;
}
else if (m_effect == 6) //tunnel
{
const double TINYNUM=1.0E-6;
const double CONE_RADIUS=128;
const double FOV=120.0;
#define sqr(a) ((a)*(a))
char *p=ge_fbuf;
for(int y=-32;y<32;y++)
for(int x=-50;x<50;x++)
{
double originx=cos(inc*0.025f)*20;
double originy=sin(inc*0.04f)*20;
double originz=inc*4;
double dirx=x/FOV;
double diry=y/FOV;
double dirz=1;
//normalize dir vector
{
double l=1.0f/sqrt(sqr(dirx)+sqr(diry)+sqr(dirz));
dirx*=l;
diry*=l;
dirz*=l;
}
//y-axis rotation
{
double rot=inc*0.015f;
double dirx2=dirx*cos(rot)+dirz*sin(rot);
dirz=dirx*sin(rot)-dirz*cos(rot);
dirx=dirx2;
}
//tunnel algo shit
double a=sqr(dirx)+sqr(diry);
double b=2*(originx*dirx + originy*diry);
double c=sqr(originx)+sqr(originy)-sqr(CONE_RADIUS);
double delta=sqrt(sqr(b)-(4*a*c));
double t1=(-b+delta)/(2*a+TINYNUM);
double t2=(-b-delta)/(2*a+TINYNUM);
double t=t1>0?t1:t2;
double intx=originx+dirx*t;
double inty=originy+diry*t;
double intz=originz+dirz*t;
//tex. coords
int u=(int)(fabs(intz)*0.6);
int v=(int)(fabs(atan2(inty,intx)*256/3.14159265));
//depth
t=20000.0/t;
int z=(int)(t>63?63:t);
u&=0xff;
v&=0xff;
z&=0xff;
{
char c=rototmp[256*u+v];
*p++=0x40+((c-0x40)*z/64);
}
}
}
else if(m_effect==7) //washing machine
{
char *p=ge_fbuf;
for(int j=-32;j<32;j++)
for(int i=-50;i<50;i++)
{
double dist=sqrt(double(sqr(i)+sqr(j))); // pythagoras rules :)
double angle=cos(dist*0.05f)*(cos(inc*0.1f)) + inc*0.07f;
//rotozoom
double x=(i*cos(angle)-j*sin(angle));
double y=(i*sin(angle)+j*cos(angle));
int x2=(int)x & 0xff;
int y2=(int)y & 0xff;
*p++=rototmp[256*y2+x2];
}
}
else if(m_effect==8) //reflection-like(?) effect
{
char *p=ge_fbuf;
for(int j=-32;j<32;j++)
for(int i=-50;i<50;i++)
{
double dist=sqrt(double(sqr(i)+sqr(j)));
double zoom=cos(dist*0.05f)*(cos(inc*0.02f)*8)+1;
//rotozoom
double x=i*zoom+inc;
double y=j*zoom+inc;
int x2=(int)x & 0xff;
int y2=(int)y & 0xff;
*p++=rototmp[256*x2+y2];
}
}
if (!nomemcpy) memcpy(m_dib,ge_fbuf,BITMAP_W*BITMAP_H);
if (hwndDlg != NULL)
{
HDC h = GetDC(hwndDlg);
BitBlt(h, 11, 11, BITMAP_W, BITMAP_H, m_hdc, 0, 0, SRCCOPY);
ReleaseDC(hwndDlg, h);
}
}
break;
#endif
}
return 0;
}
static void do_about(HWND hwnd, HINSTANCE hinst) {
#ifndef MODAL_ABOUT
if(about_hwnd) {
SetForegroundWindow(about_hwnd);
return;
}
about_hwnd=CreateDialogParam((!hinst?g_hInstance:hinst),MAKEINTRESOURCE(IDD_ABOUT),hwnd,aboutProc,(LPARAM)hinst);
ShowWindow(about_hwnd,SW_SHOW);
#else
#ifdef LOC_MODAL_ABOUT
WASABI_API_DIALOGBOXPARAMW(IDD_ABOUT,hwnd,aboutProc,(LPARAM)hinst);
#else
DialogBoxParam((!hinst?g_hInstance:hinst),MAKEINTRESOURCE(IDD_ABOUT),hwnd,aboutProc,(LPARAM)hinst);
#endif
#endif
}
#endif//_ABOUT_H_

View File

@ -0,0 +1,575 @@
#include <windows.h>
#include "audiostub.h"
#define CAPTION "NSV Player Sound Output Error"
#define MAX(x,y) (( y ) < ( x ) ? ( x ) : ( y ))
#define MIN(x,y) (( x ) < ( y ) ? ( x ) : ( y ))
#define S_MINSIZE (1<<28)
#define MAX_NUM_BLOCKS 8
#define NUM_BLOCKS 8
#define BUFSIZE_MS 1500
#define BLOCKSIZE_MAX 32768
#define BLOCKSIZE_MIN 8192
int g_audio_use_mixer=0;
class PCM_AudioOut : public IAudioOutput
{
public:
PCM_AudioOut(int samplerate, int numchannels, int bitspersamp);
~PCM_AudioOut();
int canwrite(); // returns bytes writeable
void write(void *_buf, int len);
unsigned int getpos();
unsigned int getwritepos();
void flush(unsigned int time_ms);
void pause(int pause);
int isplaying(void);
void setvolume(int volume);
void setpan(int pan);
int open_success() { return init; }
void getdescstr(char *buf)
{
*buf=0;
if (g_srate && g_nch) wsprintf(buf,"%dkHz %s",g_srate/1000,g_nch==2?"stereo":"mono");
}
private:
DWORD ThreadP();
void _setvol(void);
static DWORD WINAPI _threadproc(LPVOID p);
static void CALLBACK cbFunc(HWAVEOUT hwo,UINT uMsg,DWORD dwInstance,DWORD dwParam1,DWORD dwParam2)
{
if (uMsg == WOM_DONE) { ReleaseSemaphore((HANDLE)dwInstance,1,NULL);}
}
void do_set_blocksizes(void);
void do_samples_altvol(char *in, int blen);
int init;
int min_blocksize;
int max_blocksize;
int ispcnt;
int num_blocks;
char *g_buffer, *g_buffer_write, *g_buffer_read;
int g_buffer_length, g_buffer_valid,g_buffer_inlength;
HWAVEOUT g_hWaveOut;
WAVEHDR g_wave_headers[MAX_NUM_BLOCKS];
int g_bps,g_nch,g_srate;
volatile int g_pause, g_wavecnt, g_prebuf,g_writeall, g_quit_flag,g_writetime_bytes, g_outtime_bytes, g_outtime_interp;
HANDLE g_hSem,g_hEvent, g_hThread;
CRITICAL_SECTION g_cs;
int g_bytes_per_sec;
int a_v,a_p;
unsigned char *g_vol_table;
};
PCM_AudioOut::PCM_AudioOut(int samplerate, int numchannels, int bitspersamp)
{
init=0;
a_v=255;
a_p=0;
num_blocks=0;
g_buffer_valid=g_buffer_inlength=0;
g_hWaveOut=NULL;
memset(g_wave_headers,0,sizeof(g_wave_headers));
g_hSem=g_hEvent=g_hThread=0;
int x;
DWORD id;
MMRESULT res;
WAVEFORMATEX wfx={WAVE_FORMAT_PCM,numchannels,samplerate,samplerate*numchannels*(bitspersamp/8),
numchannels*(bitspersamp/8),bitspersamp};
g_bps=bitspersamp;
g_nch=numchannels;
g_srate=samplerate;
g_bytes_per_sec=wfx.nAvgBytesPerSec;
{
char *p=(char*)g_wave_headers;
int n=sizeof(g_wave_headers);
while (n--) *p++=0;
}
g_buffer_length = MulDiv(g_bytes_per_sec, BUFSIZE_MS, 1000);
g_buffer_length &= ~1023;
if (g_buffer_length < 4096) g_buffer_length=4096;
g_buffer=(char *)GlobalAlloc(GMEM_FIXED,g_buffer_length+min(65536,g_buffer_length));
if (g_buffer == NULL)
{
MessageBox(NULL,"Error allocating buffer", CAPTION,MB_OK|MB_ICONSTOP);
return;
}
g_prebuf=g_buffer_length/4;
g_buffer_read=g_buffer_write=g_buffer;
g_wavecnt=g_pause=g_writeall=g_buffer_valid=g_writetime_bytes=g_outtime_bytes=g_buffer_inlength=0;
g_quit_flag=0;
g_vol_table=NULL;
do_set_blocksizes();
g_hSem=CreateSemaphore(NULL,0,256,NULL);
for (x = 0; (res=waveOutOpen(&g_hWaveOut,WAVE_MAPPER,&wfx,(DWORD)cbFunc,(DWORD)g_hSem,CALLBACK_FUNCTION))==MMSYSERR_ALLOCATED && x<10; x ++)
Sleep(100);
if (res != MMSYSERR_NOERROR)
{
char t[512];
waveOutGetErrorText(res,t,sizeof(t));
MessageBox(NULL,t, CAPTION,MB_OK|MB_ICONSTOP);
GlobalFree((HGLOBAL) g_buffer);
CloseHandle(g_hSem);
g_buffer = NULL;
return;
}
ispcnt=0;
g_outtime_interp=GetTickCount();
g_hEvent=CreateEvent(NULL,FALSE,FALSE,NULL);
InitializeCriticalSection(&g_cs);
g_hThread=CreateThread(NULL,0,_threadproc,(void*)this,0,&id);
SetThreadPriority(g_hThread,THREAD_PRIORITY_HIGHEST);
init=1;
}
void PCM_AudioOut::do_set_blocksizes(void)
{
int t,t2,t4;
t=(MulDiv(BLOCKSIZE_MIN,g_bytes_per_sec,44100)+1023)&~1023;
if (t<1024) t=1024;
if (t>32768) t=32768;
t2=(MulDiv(BLOCKSIZE_MAX,g_bytes_per_sec,44100*4)+1023)&~1023;
if (t2 < t) t2 = t;
if (t2 > 65536) t2=65536;
t4 = NUM_BLOCKS;
num_blocks=t4;
max_blocksize=t2;
min_blocksize=t;
}
PCM_AudioOut::~PCM_AudioOut(void)
{
if (init)
{
int x;
g_quit_flag=1;
SetEvent(g_hEvent);
while (g_quit_flag == 1) Sleep(70);
waveOutReset(g_hWaveOut);
for (x = 0; x < MAX_NUM_BLOCKS; x++)
if (g_wave_headers[x].dwFlags & WHDR_PREPARED)
waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
if (waveOutClose(g_hWaveOut) != MMSYSERR_NOERROR)
{
MessageBox(NULL,"Error closing sound device.",CAPTION,MB_OK);
}
if (g_buffer) GlobalFree((HGLOBAL) g_buffer);
DeleteCriticalSection(&g_cs);
CloseHandle(g_hThread);
CloseHandle(g_hSem);
CloseHandle(g_hEvent);
if(g_vol_table) GlobalFree((HGLOBAL)g_vol_table);
}
}
void PCM_AudioOut::write(void *_buf, int len)
{
char *buf=(char *)_buf;
int l2;
if (len > 8192) len=8192;
l2=(g_buffer_write+len)-(g_buffer+g_buffer_length);
if (len <= 0 || !buf)
{
g_writeall=1;
//g_prebuf=0;
SetEvent(g_hEvent);
return;
}
ispcnt=0;
g_writeall=0;
if (l2 > 0)
{
int l1=len-l2;
memcpy(g_buffer_write,buf,l1);
memcpy(g_buffer,buf+l1,l2);
g_buffer_write=g_buffer+l2;
}
else
{
memcpy(g_buffer_write,buf,len);
g_buffer_write += len;
if (g_buffer_write == g_buffer+g_buffer_length) g_buffer_write=g_buffer;
}
EnterCriticalSection(&g_cs);
g_buffer_valid+=len;
LeaveCriticalSection(&g_cs);
g_writetime_bytes+=len;
if (g_wavecnt < num_blocks)
{
SetEvent(g_hEvent);
}
return;
}
int PCM_AudioOut::canwrite(void)
{
int t=(g_buffer_length-g_buffer_valid);
if (g_wavecnt==0)
{
SetEvent(g_hEvent);
}
if (t>8192) t=8192; // RG: since write() caps the # of bytes at 8192, this should reflect that! otherwise, if we call write() with too many bytes, it throws the extra away and we'll never know it.
return t;
}
int PCM_AudioOut::isplaying(void)
{
if (g_wavecnt==0)
{
SetEvent(g_hEvent);
}
if (ispcnt < 7) ispcnt++;
if (g_buffer_valid < MIN(g_buffer_length/2,min_blocksize) && ispcnt==7)
{
g_writeall=1;
g_prebuf=0;
}
return (g_wavecnt>0) || (g_buffer_valid>0);
}
void PCM_AudioOut::pause(int pause)
{
if (g_hWaveOut)
{
int lastp=g_pause;
g_pause=pause;
if (pause == lastp) return;
if (g_pause)
waveOutPause(g_hWaveOut);
else
{
waveOutRestart(g_hWaveOut);
g_outtime_interp=GetTickCount();
SetEvent(g_hEvent);
}
}
}
void PCM_AudioOut::_setvol(void)
{
DWORD vol, vo2, gv;
if (g_hWaveOut)
{
a_v=MIN(255,MAX(a_v,0));
a_p=MIN(127,MAX(a_p,-127));
vo2 = vol = (a_v*65535) / 255;
if (a_p > 0)
{
vol *= (127-a_p);
vol /= 127;
}
else if (a_p < 0)
{
vo2 *= (127+a_p);
vo2 /= 127;
}
gv=vol|(vo2<<16);
if(g_audio_use_mixer) {
if(g_vol_table) {
EnterCriticalSection(&g_cs);
GlobalFree((HGLOBAL)g_vol_table);
g_vol_table=NULL;
LeaveCriticalSection(&g_cs);
}
waveOutSetVolume(g_hWaveOut,gv);
} else {
EnterCriticalSection(&g_cs);
if(!g_vol_table) {
int l=(g_bps==8)?512:(4*32769);
g_vol_table=(unsigned char *)GlobalAlloc(GPTR,l);
}
//compute volume lookup table
int x;
if (g_bps==8) {
if (g_nch==1) for (x = 0; x < 256; x ++) g_vol_table[x] = (x*a_v)/256;
else for (x = 0; x < 256; x ++)
{
g_vol_table[x] = (x*(int)vol)/65536;
g_vol_table[x+256] = (x*(int)vo2)/65536;
}
} else {
short *vol_tab16 = (short *)g_vol_table;
if (g_nch==1) for (x = 0; x < 32769; x ++) vol_tab16[x] = -(x*a_v)/256;
else for (x = 0; x < 32769; x ++)
{
vol_tab16[x] = -(x*(int)vol)/65536;
vol_tab16[x+32769] = -(x*(int)vo2)/65536;
}
}
LeaveCriticalSection(&g_cs);
}
}
}
void PCM_AudioOut::flush(unsigned int time_ms)
{
int x;
EnterCriticalSection(&g_cs);
g_outtime_bytes=g_writetime_bytes=MulDiv(time_ms,g_bytes_per_sec,1000);
waveOutReset(g_hWaveOut);
for (x = 0; x < MAX_NUM_BLOCKS; x++)
if ((g_wave_headers[x].dwFlags & WHDR_PREPARED))
{
waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
g_wave_headers[x].dwFlags =0;
}
while (WaitForSingleObject(g_hSem,0)==WAIT_OBJECT_0);
g_prebuf=g_buffer_length/8;
g_buffer_read=g_buffer_write=g_buffer;
g_writeall=g_wavecnt=g_buffer_valid=g_buffer_inlength=0;
ispcnt=0;
LeaveCriticalSection(&g_cs);
}
unsigned int PCM_AudioOut::getwritepos(void)
{
return MulDiv(g_writetime_bytes,1000,g_bytes_per_sec);
}
unsigned int PCM_AudioOut::getpos(void)
{
unsigned int t;
if (!g_pause)
{
t=GetTickCount()-g_outtime_interp;
if (t > 1000) t=1000;
}
else t=0;
return t+MulDiv(g_outtime_bytes,1000,g_bytes_per_sec);
}
DWORD WINAPI PCM_AudioOut::_threadproc(LPVOID p)
{
return ((PCM_AudioOut *)p)->ThreadP();
}
DWORD PCM_AudioOut::ThreadP()
{
HANDLE hs[2]={g_hSem,g_hEvent};
while (1)
{
int i;
i=WaitForMultipleObjects(2,hs,FALSE,INFINITE);
if (g_quit_flag) break;
if (i == WAIT_OBJECT_0)
{
int x;
for (x = 0; x < MAX_NUM_BLOCKS && !(g_wave_headers[x].dwFlags & WHDR_DONE); x++);
if (x < MAX_NUM_BLOCKS)
{
EnterCriticalSection(&g_cs);
if (g_wave_headers[x].dwFlags & WHDR_DONE)
{
int r=g_wave_headers[x].dwBufferLength;
g_outtime_interp=GetTickCount();
g_buffer_valid -=r;
g_buffer_inlength-=r;
g_outtime_bytes +=r;
waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
g_wave_headers[x].dwFlags=0;
g_wavecnt--;
i++;
}
LeaveCriticalSection(&g_cs);
}
}
if (i == WAIT_OBJECT_0+1 && !g_pause)
{
int l;
int m;
int t=num_blocks;
EnterCriticalSection(&g_cs);
l=g_buffer_valid-g_buffer_inlength;
LeaveCriticalSection(&g_cs);
again:
if (g_quit_flag) break;
if (g_writeall && l<max_blocksize)
{
ispcnt=0;
g_writeall=0;
m=1;
}
else
{
m=MAX(MIN(g_buffer_length/2,min_blocksize),g_prebuf);
l&=~1023;
}
if (l >= m && g_wavecnt < t)
{
int x;
if (l > max_blocksize) l=max_blocksize;
for (x = 0; x < t && (g_wave_headers[x].dwFlags & WHDR_PREPARED); x++);
if (x < t)
{
int ml=(g_buffer+g_buffer_length)-g_buffer_read;
if (l > g_buffer_length) l=g_buffer_length;
if (l>ml)
{
int addlen=l-ml;
if (addlen > 65536) addlen=65536;
if (addlen > g_buffer_length) addlen=g_buffer_length;
memcpy(g_buffer+g_buffer_length,g_buffer,addlen);
}
g_wave_headers[x].dwBufferLength=l;
g_wave_headers[x].lpData=g_buffer_read;
if (waveOutPrepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0])) == MMSYSERR_NOERROR)
{
do_samples_altvol(g_wave_headers[x].lpData,g_wave_headers[x].dwBufferLength);
if (waveOutWrite(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0])) == MMSYSERR_NOERROR)
{
g_prebuf=0;
g_wavecnt++;
g_buffer_inlength += l;
g_buffer_read += l;
if (g_buffer_read >= g_buffer+g_buffer_length) g_buffer_read-=g_buffer_length;
if (g_wavecnt < t)
{
EnterCriticalSection(&g_cs);
l=g_buffer_valid-g_buffer_inlength;
LeaveCriticalSection(&g_cs);
if (l >= m) goto again;
}
}
else
{
waveOutUnprepareHeader(g_hWaveOut,&g_wave_headers[x],sizeof(g_wave_headers[0]));
g_wave_headers[x].dwFlags=0;
}
}
else g_wave_headers[x].dwFlags=0;
}
}
}
}
g_quit_flag=2;
return 0;
}
void PCM_AudioOut::do_samples_altvol(char *in, int blen)
{
if ((a_v != 255 || a_p) && g_vol_table)
{
EnterCriticalSection(&g_cs);
if (g_bps == 8)
{
unsigned char *i=(unsigned char *)in;
int x = blen;
if (g_nch==1)
{
while (x--) { *i = g_vol_table[*i]; i ++; }
}
else
{
x>>=1;
while (x--)
{
i[0] = g_vol_table[i[0]];
i[1] = g_vol_table[i[1] + 256];
i+=2;
}
}
}
else if (g_bps == 16)
{
short *i = (short *) in;
short *tab= (short *)g_vol_table;
int x = blen>>1;
if (g_nch==1)
{
while (x--)
{
int a = i[0];
if (a <= 0) i[0] = tab[-a];
else i[0] = -tab[a];
i++;
}
}
else
{
x>>=1;
while (x--)
{
int a = i[0];
if (a <= 0) i[0] = tab[-a];
else i[0] = -tab[a];
a=i[1];
if (a <= 0) i[1] = tab[32769-a];
else i[1] = -tab[32769+a];
i+=2;
}
}
}
LeaveCriticalSection(&g_cs);
}
}
void PCM_AudioOut::setvolume(int volume)
{
if (volume >= 0 && volume <= 255) a_v=volume;
_setvol();
}
void PCM_AudioOut::setpan(int pan)
{
if (pan >= -255 && pan <= 255) a_p=pan;
_setvol();
}
IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8])
{
if (outfmt[0] != NSV_MAKETYPE('P','C','M',' ') ||
!outfmt[1] || !outfmt[2] || !outfmt[3]) return NULL;
PCM_AudioOut *p=new PCM_AudioOut(outfmt[1],outfmt[2],outfmt[3]);
if (p->open_success()) return p;
delete p;
return NULL;
}

View File

@ -0,0 +1,8 @@
#ifndef _AUDIOSTUB_H_
#define _AUDIOSTUB_H_
#include "../dec_if.h"
IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8]);
#endif

View File

@ -0,0 +1,675 @@
#include <windows.h>
#include "api.h"
#include "main.h"
#include "vfw.h"
#include <api/service/services.h>
#include "../nsv/svc_nsvFactory.h"
#include <api/service/waservicefactory.h>
#include "../../Winamp/in2.h"
extern In_Module mod;
// you should probably override these in your project settings
// builtin decoders
//#define BUILTIN_MP3_SUPPORT
//#define BUILTIN_VP3_SUPPORT
//#define BUILTIN_DIVX_SUPPORT
//#define BUILTIN_PCM_SUPPORT
//#define BUILTIN_VFW_SUPPORT
// support dll decoders?
//#define DLL_DECODER_SUPPORT
//#define DLL_DECODER_SUPPORT_NOCURDIR
#ifdef WINAMP_PLUGIN
# ifndef DLL_DECODER_SUPPORT
# define DLL_DECODER_SUPPORT
# endif
# ifndef DLL_DECODER_SUPPORT_NOCURDIR
# define DLL_DECODER_SUPPORT_NOCURDIR
# endif
# ifndef DLL_DECODER_SUPPORT_IN_
# define DLL_DECODER_SUPPORT_IN_
# endif
# ifndef BUILTIN_PCM_SUPPORT
# define BUILTIN_PCM_SUPPORT
# endif
#endif
#ifdef BUILTIN_VP3_SUPPORT
#include "vp3stub.h"
#endif
#ifdef BUILTIN_VP5_SUPPORT
#include "vp5stub.h"
#endif
#ifdef BUILTIN_MP3_SUPPORT
#include "mp3stub.h"
#endif
#ifdef BUILTIN_VFW_SUPPORT
class Gen_Decoder : public IVideoDecoder {
public:
Gen_Decoder(int w, int h);
~Gen_Decoder();
int decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf);
void flush() { }
int m_err;
int width,height;
BITMAPINFO gen_bmo,gen_bmi;
HIC gen_hic;
unsigned char *vidbufdec;
};
Gen_Decoder::Gen_Decoder(int w, int h)
{
width=w;
height=h;
m_err=0;
gen_hic=0;
vidbufdec=(unsigned char*)malloc(sizeof(YV12_PLANES) + w*h*3/2);
}
Gen_Decoder::~Gen_Decoder()
{
if (gen_hic)
{
ICDecompressEnd(gen_hic);
ICClose(gen_hic);
}
free(vidbufdec);
}
int Gen_Decoder::decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf)
{
*out_type=NSV_MAKETYPE('Y','V','1','2');
gen_bmi.bmiHeader.biSizeImage = in_len;
if(ICERR_OK == ICDecompress(gen_hic,0,(BITMAPINFOHEADER *) &gen_bmi, (char*)in,(BITMAPINFOHEADER *) &gen_bmo, (char*)vidbufdec+sizeof(YV12_PLANES)))
{
//*is_kf=!(!in_len || ((unsigned char *)in)[0] > 0x7f);
*is_kf=1;
if (need_kf && !*is_kf)
{
return 0;
}
YV12_PLANES *image_vbd=(YV12_PLANES *)vidbufdec;
image_vbd->y.baseAddr=(unsigned char *)(image_vbd+1);
image_vbd->v.baseAddr=((unsigned char *)(image_vbd+1)) + width*height;
image_vbd->u.baseAddr=((unsigned char *)(image_vbd+1)) + width*height*5/4;
image_vbd->y.rowBytes=width;
image_vbd->v.rowBytes=width/2;
image_vbd->u.rowBytes=width/2;
*out=(void*)vidbufdec;
return 0;
}
return -1;
}
static IVideoDecoder *createVfw(int w, int h, double framerate, unsigned int type, int *flip)
{
HIC gen_hic = ICOpen(ICTYPE_VIDEO, type, ICMODE_DECOMPRESS);
if (!gen_hic) return 0;
BITMAPINFO gen_bmo={0,},gen_bmi={0,};
gen_bmi.bmiHeader.biSize=sizeof(gen_bmi.bmiHeader);
gen_bmi.bmiHeader.biCompression = type;
gen_bmi.bmiHeader.biHeight=h;
gen_bmi.bmiHeader.biWidth =w;
gen_bmi.bmiHeader.biPlanes=1;
gen_bmo.bmiHeader.biSize=sizeof(gen_bmo.bmiHeader);
gen_bmo.bmiHeader.biCompression = mmioFOURCC('Y','V','1','2');
gen_bmo.bmiHeader.biHeight=h;
gen_bmo.bmiHeader.biWidth =w;
gen_bmo.bmiHeader.biSizeImage=(w*h*3)/2;
gen_bmo.bmiHeader.biPlanes=1;
gen_bmo.bmiHeader.biBitCount=12;
if (ICERR_OK !=ICDecompressBegin(gen_hic, &gen_bmi, &gen_bmo))
{
ICClose(gen_hic);
return 0;
}
Gen_Decoder *t=new Gen_Decoder(w,h);
t->gen_bmi=gen_bmi;
t->gen_bmo=gen_bmo;
t->gen_hic=gen_hic;
return t;
}
#endif
#ifdef BUILTIN_DIVX_SUPPORT
#include "../../divx5/decore.h"
class CrapDivxDecoder : public IVideoDecoder {
public:
CrapDivxDecoder(int w, int h)
{
predict_keyframes=1;
divx_param.x_dim = w;
divx_param.y_dim = h;
divx_param.output_format = DEC_USER;
divx_param.codec_version = 412; // indicates that the stream is DivX 4.12 compatible
divx_param.build_number = 0; // in this case, the build field is ignored
divx_param.time_incr = 15; // time_incr default value
g_decore((long) this, DEC_OPT_MEMORY_REQS, &divx_param, &decMemReqs);
// the application allocates the data structures and the buffers
divx_param.buffers.mp4_edged_ref_buffers = malloc(decMemReqs.mp4_edged_ref_buffers_size);
divx_param.buffers.mp4_edged_for_buffers = malloc(decMemReqs.mp4_edged_for_buffers_size);
divx_param.buffers.mp4_edged_back_buffers = malloc(decMemReqs.mp4_edged_back_buffers_size);
divx_param.buffers.mp4_display_buffers = malloc(decMemReqs.mp4_display_buffers_size);
divx_param.buffers.mp4_state = malloc(decMemReqs.mp4_state_size);
divx_param.buffers.mp4_tables = malloc(decMemReqs.mp4_tables_size);
divx_param.buffers.mp4_stream = malloc(decMemReqs.mp4_stream_size);
divx_param.buffers.mp4_reference = malloc(decMemReqs.mp4_reference_size);
memset(divx_param.buffers.mp4_state, 0, decMemReqs.mp4_state_size);
memset(divx_param.buffers.mp4_tables, 0, decMemReqs.mp4_tables_size);
memset(divx_param.buffers.mp4_stream, 0, decMemReqs.mp4_stream_size);
memset(divx_param.buffers.mp4_reference, 0, decMemReqs.mp4_reference_size);
g_decore((long) this, DEC_OPT_INIT, &divx_param, NULL);
}
~CrapDivxDecoder()
{
if (g_decore)
{
g_decore((long) this,DEC_OPT_RELEASE,NULL,NULL);
free(divx_param.buffers.mp4_display_buffers);
free(divx_param.buffers.mp4_edged_for_buffers);
free(divx_param.buffers.mp4_edged_back_buffers);
free(divx_param.buffers.mp4_edged_ref_buffers);
free(divx_param.buffers.mp4_reference);
free(divx_param.buffers.mp4_state);
free(divx_param.buffers.mp4_stream);
free(divx_param.buffers.mp4_tables);
}
if (!--divx_cnt)
{
FreeModule(hDivxLib);
hDivxLib=0;
g_decore=0;
}
}
int decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf)
{
*out_type=NSV_MAKETYPE('Y','V','1','2');
*out=NULL;
int kfpredict=0;
if (predict_keyframes && in_len>3)
{
kfpredict=!((unsigned char *)in)[3];
if (need_kf && !kfpredict) return 0;
}
if (!in_len) return 0;
*is_kf=kfpredict;
DEC_PICTURE pic;
DEC_FRAME decFrame;
decFrame.bitstream = in;
decFrame.bmp = &pic;
decFrame.length = in_len;
decFrame.render_flag = 1;
DEC_FRAME_INFO fi;
if (g_decore((long) this, DEC_OPT_FRAME, &decFrame, &fi) == DEC_OK)
{
if (!kfpredict != !fi.intra) predict_keyframes=0;
*is_kf=fi.intra;
if (need_kf && !fi.intra) return 0;
image_vbd.y.baseAddr=(unsigned char *)pic.y;
image_vbd.u.baseAddr=(unsigned char *)pic.u;
image_vbd.v.baseAddr=(unsigned char *)pic.v;
image_vbd.y.rowBytes=pic.stride_y;
image_vbd.u.rowBytes=pic.stride_uv;
image_vbd.v.rowBytes=pic.stride_uv;
*out=&image_vbd;
return 0;
}
return -1;
}
void flush() { }
static int (STDCALL *g_decore)(
unsigned long handle, // handle - the handle of the calling entity, must be unique
unsigned long dec_opt, // dec_opt - the option for docoding, see below
void *param1, // param1 - the parameter 1 (it's actually meaning depends on dec_opt
void *param2); // param2 - the parameter 2 (it's actually meaning depends on dec_opt
static HINSTANCE hDivxLib;
static int divx_cnt;
private:
DEC_PARAM divx_param;
YV12_PLANES image_vbd;
DEC_MEM_REQS decMemReqs;
int predict_keyframes;
};
int (STDCALL *CrapDivxDecoder::g_decore)(
unsigned long handle, // handle - the handle of the calling entity, must be unique
unsigned long dec_opt, // dec_opt - the option for docoding, see below
void *param1, // param1 - the parameter 1 (it's actually meaning depends on dec_opt
void *param2)=0; // param2 - the parameter 2 (it's actually meaning depends on dec_opt
HINSTANCE CrapDivxDecoder::hDivxLib=0;
int CrapDivxDecoder::divx_cnt=0;
IVideoDecoder *DIVX_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip)
{
if (fmt == NSV_MAKETYPE('D','i','v','X'))
{
if (!CrapDivxDecoder::divx_cnt)
{
CrapDivxDecoder::hDivxLib=LoadLibrary("divx.dll");
if (CrapDivxDecoder::hDivxLib) *((void**)&CrapDivxDecoder::g_decore)=GetProcAddress(CrapDivxDecoder::hDivxLib,"decore");
}
CrapDivxDecoder::divx_cnt++;
if (CrapDivxDecoder::g_decore) return new CrapDivxDecoder(w,h);
}
return NULL;
}
#endif // end of divx gayness
class NullVideoDecoder : public IVideoDecoder
{
public:
NullVideoDecoder() { }
~NullVideoDecoder() { }
int decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf)
{
*out_type=NSV_MAKETYPE('Y','V','1','2');
*is_kf=1;
*out=NULL;
return 0;
}
void flush() { }
};
class NullAudioDecoder : public IAudioDecoder
{
public:
NullAudioDecoder(){}
~NullAudioDecoder(){}
int decode(void *in, int in_len,
void *out, int *out_len,
unsigned int out_fmt[8])
{
*out_len=0;
out_fmt[0]=NSV_MAKETYPE('N','O','N','E'); // no output
return 0;
}
void flush(){}
};
#ifdef BUILTIN_PCM_SUPPORT
class PCMAudioDecoder : public IAudioDecoder
{
public:
PCMAudioDecoder() { fused=4; }
~PCMAudioDecoder(){}
int decode(void *in, int in_len,
void *out, int *out_len,
unsigned int out_fmt[8])
{
if (in_len < 4)
{
*out_len=0;
out_fmt[0]=0;
return 0; // screw this frame
}
unsigned char *t=(unsigned char *)in;
int bps=t[0];
int nch=t[1];
int srate=((int)t[2] | (((int)t[3])<<8));
out_fmt[0]=NSV_MAKETYPE('P','C','M',' ');
out_fmt[1]=srate;
out_fmt[2]=nch;
out_fmt[3]=bps;
int l=in_len-fused;
if (l > *out_len) l = *out_len;
l&=~(nch*(bps/8)-1);
if (l) memcpy(out,(char *)in + fused,l);
fused+=l;
*out_len=l;
if (fused >= in_len)
{
fused=4;
return 0;
}
return 1;
}
void flush() { fused=4; }
private:
int fused;
};
#endif
#ifdef DLL_DECODER_SUPPORT
static char DLL_Dir[MAX_PATH];
static HINSTANCE DLL_Handles[512];
#endif
void Decoders_Init(char *wapluginspath)
{
#ifdef DLL_DECODER_SUPPORT
HKEY hKey;
if (!DLL_Dir[0] && RegOpenKeyExA(HKEY_LOCAL_MACHINE,"Software\\Microsoft\\Windows\\CurrentVersion",
0,KEY_READ,&hKey) == ERROR_SUCCESS)
{
DWORD l = sizeof(DLL_Dir);
DWORD t;
if (RegQueryValueExA(hKey,"CommonFilesDir",NULL,&t,(LPBYTE)DLL_Dir,&l ) != ERROR_SUCCESS || t != REG_SZ) DLL_Dir[0]=0;
DLL_Dir[sizeof(DLL_Dir)-5]=0;
CreateDirectoryA(DLL_Dir,NULL);
strcat(DLL_Dir,"\\NSV");
CreateDirectoryA(DLL_Dir,NULL);
RegCloseKey(hKey);
}
if (!DLL_Dir[0]) GetTempPathA(sizeof(DLL_Dir),DLL_Dir);
Decoders_Quit();
HANDLE h;
int x=0;
WIN32_FIND_DATAA fd = {0};
char buf[MAX_PATH*2+1] = {0};
#ifndef DLL_DECODER_SUPPORT_NOCURDIR
char curdir[MAX_PATH] = {0};
strcpy( curdir, ".\\" );
strcpy( buf, curdir );
strcat( buf, "nsvdec_*.dll" );
OutputDebugString( buf ); OutputDebugString( "\n" );
h = FindFirstFile(buf,&fd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
strcpy(buf,curdir);
strcat(buf,fd.cFileName);
DLL_Handles[x]=LoadLibrary(buf);
if (DLL_Handles[x])
{
if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
else
{
FreeLibrary(DLL_Handles[x]);
DLL_Handles[x]=0;
}
}
} while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFile(h,&fd));
FindClose(h);
}
#endif
#ifdef DLL_DECODER_SUPPORT_IN_
if (wapluginspath && wapluginspath[0])
{
lstrcpynA(buf,wapluginspath,sizeof(buf)-16);
strcat(buf,"\\in_*.dll");
h = FindFirstFileA(buf,&fd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
strncpy(buf, wapluginspath, MAX_PATH);
strncat(buf, "\\", MAX_PATH);
strncat(buf, fd.cFileName, MAX_PATH);
DLL_Handles[x]=LoadLibraryA(buf);
if (DLL_Handles[x])
{
if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
else
{
FreeLibrary(DLL_Handles[x]);
DLL_Handles[x]=0;
}
}
} while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFileA(h,&fd));
FindClose(h);
}
lstrcpynA(buf,wapluginspath,sizeof(buf)-16);
strcat(buf,"\\nsvdec_*.dll");
h = FindFirstFileA(buf,&fd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
strncpy(buf, wapluginspath, MAX_PATH);
strncat(buf, "\\", MAX_PATH);
strncat(buf, fd.cFileName, MAX_PATH);
DLL_Handles[x]=LoadLibraryA(buf);
if (DLL_Handles[x])
{
if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
else
{
FreeLibrary(DLL_Handles[x]);
DLL_Handles[x]=0;
}
}
} while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFileA(h,&fd));
FindClose(h);
}
}
#endif
#ifndef WINAMPX
strncpy(buf, DLL_Dir, MAX_PATH);
strncat(buf, "\\nsvdec_*.dll", MAX_PATH);
h = FindFirstFileA(buf,&fd);
if (h != INVALID_HANDLE_VALUE)
{
do
{
strncpy(buf, DLL_Dir, MAX_PATH);
strncat(buf, "\\", MAX_PATH);
strncat(buf, fd.cFileName, MAX_PATH);
DLL_Handles[x]=LoadLibraryA(buf);
if (DLL_Handles[x])
{
if (GetProcAddress(DLL_Handles[x],"CreateVideoDecoder") ||
GetProcAddress(DLL_Handles[x],"CreateAudioDecoder")) x++;
else
{
FreeLibrary(DLL_Handles[x]);
DLL_Handles[x]=0;
}
}
} while (x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && FindNextFileA(h,&fd));
FindClose(h);
}
#endif
#endif
}
void Decoders_Quit()
{
#ifdef DLL_DECODER_SUPPORT
int x;
for (x = 0; x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && DLL_Handles[x]; x ++)
{
FreeLibrary(DLL_Handles[x]);
DLL_Handles[x]=0;
}
#endif
}
static IAudioDecoder *CreateAudioDecoderWasabi(unsigned int type, IAudioOutput **output)
{
int n = 0;
waServiceFactory *sf = 0;
while (sf = mod.service->service_enumService(WaSvc::NSVFACTORY, n++))
{
svc_nsvFactory *factory = (svc_nsvFactory *)sf->getInterface();
if (factory)
{
IAudioDecoder *decoder = factory->CreateAudioDecoder(type, output);
sf->releaseInterface(factory);
if (decoder)
return decoder;
}
}
return 0;
}
static IVideoDecoder *CreateVideoDecoderWasabi(int w, int h, double framerate, unsigned int type, int *flip)
{
int n=0;
waServiceFactory *sf = 0;
while (sf = mod.service->service_enumService(WaSvc::NSVFACTORY, n++))
{
svc_nsvFactory *factory = (svc_nsvFactory *)sf->getInterface();
if (factory)
{
IVideoDecoder *decoder = factory->CreateVideoDecoder(w, h, framerate, type, flip);
sf->releaseInterface(factory);
if (decoder)
return decoder;
}
}
return 0;
}
IAudioDecoder *CreateAudioDecoder(unsigned int type, int *wasNotNull, IAudioOutput **output)
{
IAudioDecoder *a=NULL;
if (mod.service && !a)
a = CreateAudioDecoderWasabi(type, output);
#ifdef BUILTIN_MP3_SUPPORT
if (!a) a=MP3_CREATE(type);
#endif
#ifdef BUILTIN_PCM_SUPPORT
if (!a && type == NSV_MAKETYPE('P','C','M',' ')) a=new PCMAudioDecoder;
#endif
#ifdef BUILTIN_AAC_SUPPORT
extern IAudioDecoder *AAC_CREATE(unsigned int fmt, IAudioOutput **output);
if (!a && (type == NSV_MAKETYPE('A','A','C',' ') || type == NSV_MAKETYPE('V','L','B',' '))) a=AAC_CREATE(type,NULL);
#endif
#ifdef BUILTIN_AACP_SUPPOT
extern IAudioDecoder *AACP_CREATE(unsigned int fmt, IAudioOutput **output);
if (!a && (type == NSV_MAKETYPE('A','A','C','P') || type == NSV_MAKETYPE('A','A','C',' '))) a=AAC_CREATE(type,NULL);
#endif
#ifdef DLL_DECODER_SUPPORT
int x;
for (x = 0; !a && x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && DLL_Handles[x]; x ++)
{
IAudioDecoder *(*cad)(unsigned int type, IAudioOutput **output);
*((void**)&cad) = (void*)GetProcAddress(DLL_Handles[x],"CreateAudioDecoder");
if (cad) a=cad(type,output);
}
#endif
if (!a)
{
*wasNotNull=0;
void *mem = WASABI_API_MEMMGR->sysMalloc(sizeof(NullAudioDecoder));
a = new (mem) NullAudioDecoder();
}
else *wasNotNull=1;
return a;
}
IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int type, int *flip, int *wasNotNull)
{
IVideoDecoder *v=NULL;
if (mod.service && !v)
v = CreateVideoDecoderWasabi(w, h, framerate, type, flip);
#ifdef BUILTIN_DIVX_SUPPORT
if (!v) v=DIVX_CREATE(w,h,framerate,type,flip);
#endif
#ifdef BUILTIN_VP3_SUPPORT
if (!v) v=VP3_CREATE(w,h,framerate,type,flip);
#endif
#ifdef BUILTIN_VP5_SUPPORT
if (!v) v=VP5_CREATE(w,h,framerate,type,flip);
#endif
#ifdef BUILTIN_VP6_SUPPORT
extern IVideoDecoder *VP6_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip);
if (!v) v=VP6_CREATE(w,h,framerate,type,flip);
#endif
#ifdef DLL_DECODER_SUPPORT
int x;
for (x = 0; !v && x < sizeof(DLL_Handles)/sizeof(DLL_Handles[0]) && DLL_Handles[x]; x ++)
{
IVideoDecoder *(*cvd)(int w, int h, double framerate, unsigned int type, int *flip);
*((void**)&cvd) = (void*)GetProcAddress(DLL_Handles[x],"CreateVideoDecoder");
if (cvd) v=cvd(w,h,framerate,type,flip);
}
#endif
#ifdef BUILTIN_VFW_SUPPORT
if (!v)
{
v=createVfw(w,h,framerate,type,flip);
}
#endif
if (!v)
{
if (wasNotNull) *wasNotNull=0;
void *mem = WASABI_API_MEMMGR->sysMalloc(sizeof(NullVideoDecoder));
v = new (mem) NullVideoDecoder();
}
else if (wasNotNull) *wasNotNull=1;
return v;
}

230
Src/nsv/nsvplay/main.cpp Normal file
View File

@ -0,0 +1,230 @@
#include <windows.h>
#include "main.h"
#include "video.h"
#include "resource.h"
typedef struct
{
VideoOutput *vidOut;
NSVDecoder *decode;
int quit;
} parms;
HINSTANCE g_hInstance;
int g_bitmap_id=IDB_BITMAP1;
//#define NO_ABOUT_EGG
extern long glCounterNSVf, glSyncFrameCount, glNonSyncFrameCount;
#define WNDMENU_CAPTION "NSVPlay/0.994"
#include "about.h"
#include "wndmenu.h"
DWORD WINAPI pooThread(LPVOID p)
{
parms *parm=(parms*)p;
unsigned int last_title_check=0;
while (!parm->quit)
{
int r=parm->decode->run(&parm->quit);
if (r < 0)
{
if (parm->decode->get_error()) MessageBox(parm->vidOut->getHwnd(),parm->decode->get_error(),"NSV Player Error",MB_OK|MB_ICONSTOP);
break;
}
else if (!r)
{
if ((GetTickCount()-last_title_check) > 1000)
{
char *v=parm->decode->getTitle();
char *buf=(char*)malloc((v?strlen(v):0)+128);
int posms=parm->decode->getpos();
int lenms=parm->decode->getlen();
char *s=parm->decode->getStatus();
if (lenms != ~0)
wsprintf(buf,"%s [%d:%02d/%d:%02d] @ %dkbps %s%s%s- NSV Player",v?v:"",
posms/60000,(posms/1000)%60,
lenms/60000,(lenms/1000)%60,
parm->decode->getBitrate()/1000,
s?"[":"",s?s:"",s?"] ":""
);
else
wsprintf(buf,"%s [%d:%02d] @ %dkbps %s%s%s- NSV Player",v?v:"",
posms/60000,(posms/1000)%60,
parm->decode->getBitrate()/1000,
s?"[":"",s?s:"",s?"] ":""
);
char *p=buf;
while (*p)
{
if (*p == '_') *p=' ';
p++;
}
SetWindowText(parm->vidOut->getHwnd(),buf);
free(buf);
last_title_check=GetTickCount();
}
Sleep(1);
}
}
parm->quit++;
return 0;
}
int g_quit;
int WINAPI WinMain(HINSTANCE hInstance, HINSTANCE hPrevInst, LPSTR lpszCmdParam, int nCmdShow)
{
g_hInstance=hInstance;
BOOL bDisplayInfoAtEnd = FALSE;
char *subtitlefile=NULL;
char buf[11];
lstrcpyn(buf,lpszCmdParam,11);
if(strstr(lpszCmdParam, "/info")) {
bDisplayInfoAtEnd = TRUE;
}
if (!lstrcmpi(buf,"/subtitle="))
{
lpszCmdParam += 10;
char scanc=' ';
if (*lpszCmdParam == '\"')
{
scanc=*lpszCmdParam++;
}
subtitlefile=lpszCmdParam;
while (*lpszCmdParam && *lpszCmdParam != scanc) lpszCmdParam++;
*lpszCmdParam++=0;
while (*lpszCmdParam == ' ') lpszCmdParam++;
}
if (*lpszCmdParam == '\"')
{
lpszCmdParam++;
char *p=strstr(lpszCmdParam,"\"");
if (p) *p=0;
}
if (!*lpszCmdParam)
{
MessageBox(NULL,"Usage: nsvplay.exe [/subtitle=\"file|url\"] filename.nsv|http://url.nsv","NSV Player",MB_OK|MB_ICONINFORMATION);
return 0;
}
int xpos=CW_USEDEFAULT;
int ypos=CW_USEDEFAULT;
_ReadConfigItemInt("xpos",&xpos);
_ReadConfigItemInt("ypos",&ypos);
VideoOutput *vidOut = new VideoOutput(NULL,xpos,ypos);
Decoders_Init();
NSVDecoder *decode=new NSVDecoder(lpszCmdParam,vidOut,subtitlefile);
vidOut->setNSVDecoder(decode);
int m_pause=0;
vidOut->vid_vsync=false;
_ReadConfigItemInt("vsync",&vidOut->vid_vsync);
_ReadConfigItemInt("overlays",&vidOut->vid_overlays);
_ReadConfigItemInt("ddraw",&vidOut->vid_ddraw);
_ReadConfigItemInt("aspectadj",&vidOut->vid_aspectadj);
_ReadConfigItemInt("use_mixer",&g_audio_use_mixer);
{
int temp=1;
_ReadConfigItemInt("subtitles",&temp);
decode->enableSubs(temp);
temp=0;
_ReadConfigItemInt("subtitles_size",&temp);
decode->setSubsFontSize(temp);
}
DWORD id;
parms parm={0,};
parm.decode=decode;
parm.vidOut=vidOut;
vidOut->setcallback(my_wndcallback,&parm);
HANDLE hThread=CreateThread(NULL,0,(LPTHREAD_START_ROUTINE)pooThread,(LPVOID)&parm,0,&id);
for (;;)
{
MSG msg;
while (PeekMessage(&msg,NULL,0,0,PM_REMOVE))
{
if (msg.hwnd == vidOut->getHwnd() && msg.message == WM_KEYDOWN)
{
if (msg.wParam == VK_SPACE) decode->pause(m_pause=!m_pause);
if (msg.wParam == VK_LEFT) decode->seek(decode->getpos()-30000);
if (msg.wParam == VK_RIGHT) decode->seek(decode->getpos()+30000);
if (msg.wParam == VK_UP)
{
int vol=decode->getvolume()+16;
if (vol > 255) vol=255;
decode->setvolume(vol);
}
if (msg.wParam == VK_DOWN)
{
int vol=decode->getvolume()-16;
if (vol < 0) vol=0;
decode->setvolume(vol);
}
if (msg.wParam == VK_RETURN)
{
if (vidOut->is_fullscreen()) vidOut->remove_fullscreen();
else vidOut->fullscreen();
}
if (msg.wParam == 'v' || msg.wParam == 'V')
{
vidOut->vid_vsync=!vidOut->vid_vsync;
}
if (msg.wParam == 'a' || msg.wParam == 'A')
{
vidOut->vid_aspectadj=!vidOut->vid_aspectadj;
PostMessage(vidOut->getHwnd(),WM_TIMER,1,0);
}
}
DispatchMessage(&msg);
}
if (!IsWindow(vidOut->getHwnd()) || parm.quit) break;
Sleep(50);
}
parm.quit++;
WaitForSingleObject(hThread,INFINITE);
CloseHandle(hThread);
delete decode;
delete vidOut;
Decoders_Quit();
#if _DEBUG
// if "/info" on the command line, display some statistics about the movie
if(bDisplayInfoAtEnd) {
char szMessage[MAX_PATH];
wsprintf(szMessage, "File Header Count=%ld\nSync Frames=%ld\nNon-sync Frames=%ld", glCounterNSVf, glSyncFrameCount, glNonSyncFrameCount);
MessageBox(NULL, szMessage, WNDMENU_CAPTION, MB_OK);
}
#endif
return 0;
}

248
Src/nsv/nsvplay/main.h Normal file
View File

@ -0,0 +1,248 @@
#ifndef NSVPLAY_MAIN_H
#define NSVPLAY_MAIN_H
#include <bfc/platform/types.h>
#include "../nsvlib.h"
#include "../dec_if.h"
#define SHOW_STREAM_TITLE_AT_TOP 1
class Subtitles;
class SubsItem;
#include "IDataReader.h"
typedef struct
{
const char *language;
const char *utf8_text;
unsigned int start_frame, end_frame;
unsigned char xPos, yPos;
unsigned char colorRed, colorGreen, colorBlue;
signed char fontSize;
int extraDataSize;
const void *extraData;
} SUBTITLE_INFO;
class IVideoOutput
{
public:
virtual ~IVideoOutput() { }
virtual int open(int w, int h, int vflip, double aspectratio, unsigned int fmt)=0;
#ifdef _WIN32
virtual void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { }
#else
virtual void setcallback(void *func, void *token) { } // currently unused, here to reserve the spot in the vtable
#endif
virtual void close()=0;
virtual void draw(void *frame)=0;
virtual void drawSubtitle(SubsItem *item) { }
virtual void showStatusMsg(const char *text) { }
virtual int get_latency() { return 0; }
virtual void notifyBufferState(int bufferstate) { } /* 0-255*/
virtual intptr_t extended(intptr_t param1, intptr_t param2, intptr_t param3) { return 0; } // Dispatchable, eat this!
};
template<class T>
class ClassList {
public:
~ClassList() {
int l=getlen()-1;
for(int i=l;i>-1;i--) delete(get(i));
}
void put(T *item) {
m_buf.add(&item,sizeof(T *));
}
T *get(int n) {
if(n>=getlen()) return NULL;
return ((T **)m_buf.get())[n];
}
int getlen() {
return (int)(m_buf.getlen()/sizeof(SubsItem *));
}
private:
GrowBuf m_buf;
};
class SubtitlesItem {
public:
SubtitlesItem(const char *language, Subtitles *subs) :
m_subs(subs) {
m_language=_strdup(language);
m_subs=subs;
}
~SubtitlesItem() {
free((void *)m_language);
}
const char *m_language;
Subtitles *m_subs;
};
class NSVDecoder {
public:
NSVDecoder(const char *url, IVideoOutput *output, char *subtitleurl=NULL);
~NSVDecoder();
char *get_error();
char *get_status();
int run(int * volatile quit=NULL);
void pause(int pause);
ULONGLONG getpos();
unsigned int getpos_frames() { return framecnt; }
unsigned int getlen(); // returns 0xFFFFFFFF on unknown
void setvolume(int volume) { m_volume=volume; if (aud_output) aud_output->setvolume(volume); }
void setpan(int pan) { m_pan=pan; if (aud_output) aud_output->setpan(pan); }
int getvolume() { return m_volume; }
int getpan() { return m_pan; }
int canseek();
void seek(unsigned int newpos);
char *getFromMeta(char *name);
char *getUrl() { return m_url; }
const char *getServerHeader(char *name);
char *getTitle();
char *getStatus();
void getAudioDesc(char *buf);
void getVideoDesc(char *buf);
char *getAudioType() { return m_audio_type; }
char *getVideoType() { return m_video_type; }
unsigned int getFileSize(); // 0xFFFFFFFF if unknown
int getBitrate();
int getAudioBitrate();
int getVideoBitrate();
int getWidth() { return unpacket.getWidth(); }
int getHeight() { return unpacket.getHeight(); }
double getFrameRate() { return use_framerate_override?framerate_override:unpacket.getFrameRate(); }
int getBufferPos() { if (m_prebuffer) return m_bufstate; return 256; } // 0-256
int subsEnabled() { return m_enable_subtitles; }
void enableSubs(int e) {
m_enable_subtitles=e;
if(!e&&m_out) m_out->drawSubtitle(NULL);
}
int getSubsFontSize() { return m_subs_fontsize; }
void setSubsFontSize(int s) {
m_subs_fontsize=s;
if(m_out) m_out->drawSubtitle(NULL); //will redraw current subtitle with new size
}
void SetPreciseSeeking(int prec) { m_precise_seeking=prec; }
void SetBuffering(int total_ms, int initial_ms, int after_underrun);
void SetBufferMemoryLimit(int bytes) { m_buf_memlimit=bytes; }
const char *getSubLanguage(int index);
void setSubLanguage(int index) { m_cur_subtitle=index; }
int getCurSubLanguage() { return m_cur_subtitle; }
void CloseVideo()
{
if (m_out_opened)
m_out->close();
m_out_opened=0;
}
private:
ULONGLONG m_avresync_time;
int m_pb_init,m_pb_init_ur,m_buffer_total;
int m_prebuffer;
int m_bufstate;
int proTimerStart;
int proTimerEnd;
float profiletime;
float prostart;
float proend;
float timeref;
int m_again;
void ProcessSubtitleBlock(void *data, int len);
int m_paused;
ULONGLONG hack_l_curpos;
ULONGLONG hack_l_curpos_ot;
int m_buf_memlimit;
IVideoOutput *m_out;
int m_out_opened;
nsv_InBS inbs;
nsv_InBS audiobs,videobs;
nsv_InBS auxbs;
int video_frames_avail,audio_frames_avail;
nsv_Unpacketer unpacket;
unsigned int framecnt;
int hdrsearched;
int nsvbitstream_search;
int64_t m_audio_writepos;
unsigned int m_need_seek;
int seek_dumpframes, seek_dumpaudiosamples;
int pcm_samplerate;
int m_precise_seeking;
char *m_err;
char *m_url;
char *m_title;
int vid_decoder_isnotnull,aud_decoder_isnotnull;
IVideoDecoder *vid_decoder;
IAudioDecoder *aud_decoder;
IAudioOutput *aud_output;
IDataReader *file_reader;
int needkf;
double aspect;
double framerate_override;
int use_framerate_override;
nsv_fileHeader fileheader;
unsigned int avg_framesize_cnt,avg_framesize_tot;
unsigned int avg_framesize_cnt_v,avg_framesize_tot_v;
unsigned int avg_framesize_cnt_a,avg_framesize_tot_a;
int vidout_ready;
int vid_flip;
void *vidout;
unsigned int vidout_type;
unsigned int vidout_time;
unsigned int vidout_codec_width;
unsigned int vidout_codec_height;
char m_audio_type[5];
char m_video_type[5];
int m_volume, m_pan;
int m_enable_subtitles;
int m_subs_fontsize;
ClassList<SubtitlesItem> m_subtitles;
int m_cur_subtitle;
Subtitles *insertSubtitlesItem(const char *language, const char *subfile);
Subtitles *findSubtitles(const char *language);
};
void Decoders_Init(char *wapluginspath=NULL);
void Decoders_Quit();
IAudioDecoder *CreateAudioDecoder(unsigned int type, int *wasNotNull, IAudioOutput **output);
IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int type, int *flip, int *wasNotNull=NULL);
IDataReader *CreateReader(const char *url);
#endif //NSVPLAY_MAIN_H

View File

@ -0,0 +1,73 @@
#include "mp3stub.h"
#include "../../mp3dec/mpgadecoder.h"
int mp3_quality;
int mp3_downmix;
class MP3_Decoder : public IAudioDecoder
{
public:
MP3_Decoder() : mp3_dec(mp3_quality,0,mp3_downmix) { fused=0; pcm_buf_used=0; }
~MP3_Decoder() { };
int decode(void *in, int in_len,
void *out, int *out_len,
unsigned int out_fmt[8]);
void flush() { fused=0; pcm_buf_used=0; mp3_dec.Reset(); }
private:
CMpgaDecoder mp3_dec;
char pcm_buf[1152*4*2];
int pcm_buf_used;
int pcm_offs;
int fused;
};
int MP3_Decoder::decode(void *in, int in_len,
void *out, int *out_len,
unsigned int out_fmt[8])
{
int rval=1;
if (fused < in_len)
{
int l=mp3_dec.GetInputFree();
if (l > in_len-fused) l=in_len-fused;
if (l) mp3_dec.Fill((unsigned char *)in + fused,l);
fused+=l;
}
if (!pcm_buf_used)
{
SSC s=mp3_dec.DecodeFrame((unsigned char *)pcm_buf,sizeof(pcm_buf),&pcm_buf_used);
pcm_offs=0;
}
if (pcm_buf_used)
{
int l=*out_len;
if (l > pcm_buf_used) l=pcm_buf_used;
memcpy(out,pcm_buf+pcm_offs,l);
pcm_buf_used-=l;
pcm_offs+=l;
*out_len=l;
}
else
{
if (fused >= in_len) rval=fused=0;
*out_len=0;
}
int nch=mp3_dec.m_Info.GetEffectiveChannels();
int srate=mp3_dec.m_Info.GetEffectiveSFreq();
out_fmt[0]=(nch && srate)?NSV_MAKETYPE('P','C','M',' '):0;
out_fmt[1]=srate;
out_fmt[2]=nch;
out_fmt[3]=(nch && srate)?16:0;
return rval;
}
IAudioDecoder *MP3_CREATE(unsigned int fmt)
{
if (fmt == NSV_MAKETYPE('M','P','3',' ')) return new MP3_Decoder;
return NULL;
}

12
Src/nsv/nsvplay/mp3stub.h Normal file
View File

@ -0,0 +1,12 @@
#ifndef _MP3STUB_H_
#define _MP3STUB_H_
#include "main.h"
extern int mp3_quality;
extern int mp3_downmix;
extern int mp3_downshit;
IAudioDecoder *MP3_CREATE(unsigned int fmt);
#endif

Binary file not shown.

After

Width:  |  Height:  |  Size: 7.3 KiB

View File

@ -0,0 +1,999 @@
#include <bfc/platform/platform.h>
#include <bfc/platform/strcmp.h>
#include <stdio.h>
#include <locale.h>
#include "audiostub.h"
#include "api.h"
#include "main.h"
#include "subtitles.h"
#include "../in_nsv/resource.h"
extern int config_subtitles;
#ifdef __APPLE__
#include <sys/time.h>
#include <unistd.h>
uint32_t GetTickCount()
{
struct timeval newtime;
gettimeofday(&newtime, 0);
return newtime.tv_sec*1000 + newtime.tv_usec/1000;
}
int MulDiv(int a, int b, int d)
{
return int ((int64_t)a * (int64_t)b) / (int64_t)d;
}
#endif
class NullAudioOutput : public IAudioOutput
{
public:
NullAudioOutput() { m_pause=0; m_pause_t=0; m_start_t=GetTickCount64(); }
~NullAudioOutput(){}
int canwrite() { return m_pause ? 0 : 65536; } // returns bytes writeable
void write(void *buf, int len) { }
ULONGLONG getpos() { return m_pause?m_pause_t:(GetTickCount64()-m_start_t); }
int isplaying(void) { return -1; }
ULONGLONG getwritepos() { return getpos(); }
void flush(unsigned int newtime)
{
if (m_pause) m_pause_t=newtime;
else m_start_t = GetTickCount64()-newtime;
}
void pause(int pause)
{
if (pause && !m_pause)
{
m_pause_t=GetTickCount64()-m_start_t;
m_pause=1;
}
else if (!pause && m_pause)
{
m_start_t = GetTickCount64()-m_pause_t;
m_pause=0;
m_pause_t=0;
}
}
private:
int m_pause;
ULONGLONG m_pause_t;
ULONGLONG m_start_t;
};
void NSVDecoder::pause(int pause)
{
if (aud_output) aud_output->pause(pause);
m_paused=pause;
}
ULONGLONG NSVDecoder::getpos()
{
return aud_output ? aud_output->getpos() : 0;
}
unsigned int NSVDecoder::getlen()
{
if (!fileheader.header_size || fileheader.file_lenms == 0xFFFFFFFF)
{
if (!file_reader || !avg_framesize_cnt || !avg_framesize_tot) return 0xFFFFFFFF;
int s=(int)file_reader->getsize();
if (s == 0xFFFFFFFF) return 0xFFFFFFFF;
int bytespersec=(int)((double)avg_framesize_tot/(double)avg_framesize_cnt*getFrameRate());
if (!bytespersec) return 0xFFFFFFFF;
return MulDiv(s,1000,bytespersec);
}
return fileheader.file_lenms;
}
void NSVDecoder::seek(unsigned int newpos)
{
m_need_seek=newpos;
m_again=0;
}
int NSVDecoder::canseek()
{
return file_reader ? file_reader->canseek() : 0;
}
char *NSVDecoder::getStatus()
{
if (m_prebuffer) return "Buffering";
return NULL;
}
void NSVDecoder::SetBuffering(int total_ms, int initial_ms, int after_underrun)
{
if (m_prebuffer) m_prebuffer=initial_ms;
m_pb_init=initial_ms;
m_pb_init_ur=after_underrun;
m_buffer_total=total_ms;
}
int64_t pdReadResolution(void)
{
int64_t myfeq;
#ifdef _WIN32
LARGE_INTEGER feq;
QueryPerformanceFrequency( &feq);
myfeq = feq.QuadPart;
#else
myfeq = 1000000;
#endif
return myfeq;
}
int64_t pdReadTimer(void)
{
int64_t mynow;
#ifdef _WIN32
LARGE_INTEGER now;
QueryPerformanceCounter( &now );
mynow = now.QuadPart;
#else
struct timeval newtime;
gettimeofday(&newtime,NULL);
mynow = (newtime.tv_sec * 1000000) + newtime.tv_usec ;
#endif
return mynow;
}
NSVDecoder::NSVDecoder(const char *url, IVideoOutput *output, char *subtitleurl)
{
m_precise_seeking=1;
pcm_samplerate=0;
seek_dumpframes=0;
seek_dumpaudiosamples=0;
m_avresync_time=0;
nsvbitstream_search=0;
m_paused=0;
m_bufstate=0;
hack_l_curpos=-1;
hack_l_curpos_ot=0;
vid_decoder_isnotnull=1;
aud_decoder_isnotnull=1;
video_frames_avail=0;
audio_frames_avail=0;
m_buffer_total=1000; // bufferahead
m_pb_init=m_prebuffer=1500; // initial prebuffer
m_pb_init_ur=1500; // after underrun
m_buf_memlimit=32*1024*1024;
m_pan=0;
m_volume=192;
m_title=0;
m_audio_type[0]=m_video_type[0]=0;
m_out_opened=0;
m_out=output;
vidout_ready=0;
vidout=0;
vidout_type=0;
vidout_time=0;
vidout_codec_width=0;
vidout_codec_height=0;
m_url=_strdup(url);
m_need_seek=~0;
needkf=1;
aspect=1.0;
framerate_override=0.0;
use_framerate_override=0;
vid_decoder=NULL;
aud_decoder=NULL;
aud_output=NULL;
framecnt=0;
hdrsearched=0;
file_reader=CreateReader(url);
if (!file_reader) m_err="Error opening input";
else m_err=NULL;
m_enable_subtitles=1;
m_subs_fontsize=0;
m_cur_subtitle=0;
if (subtitleurl && *subtitleurl) insertSubtitlesItem("From subtitle file",subtitleurl);
memset(&fileheader,0,sizeof(fileheader));
avg_framesize_tot=0;
avg_framesize_cnt=0;
avg_framesize_tot_v=0;
avg_framesize_cnt_v=0;
avg_framesize_tot_a=0;
avg_framesize_cnt_a=0;
unpacket.setAudioOut(&audiobs);
unpacket.setVideoOut(&videobs);
unpacket.setAuxOut(&auxbs);
proTimerStart = 1;
proTimerEnd = 0;
profiletime = 0.00;
prostart = 0.00;
proend = 0.00;
timeref = (float)pdReadResolution();
m_again = 0;
}
NSVDecoder::~NSVDecoder()
{
if (m_out_opened) m_out->close();
if (WASABI_API_MEMMGR)
WASABI_API_MEMMGR->Delete(vid_decoder);
else
delete vid_decoder;
if (WASABI_API_MEMMGR)
WASABI_API_MEMMGR->Delete(aud_decoder);
else
delete aud_decoder;
delete aud_output;
delete file_reader;
free(fileheader.metadata);
free(fileheader.toc);
free(m_url);
free(m_title);
}
char *NSVDecoder::get_error()
{
return m_err;
}
char *NSVDecoder::get_status()
{
return NULL;
}
char *NSVDecoder::getFromMeta(char *name)
{
if (file_reader)
{
char *t=(char*)malloc(strlen(name)+8);
if (t)
{
strcpy(t,"x-nsv-");
strcat(t,name);
char *v=file_reader->getheader(t);
free(t);
if (v) return _strdup(v);
}
}
return nsv_getmetadata(fileheader.metadata,name);
}
unsigned int NSVDecoder::getFileSize()
{
if (file_reader) return (unsigned int)file_reader->getsize();
return ~0;
}
int NSVDecoder::getBitrate()
{
if (!fileheader.header_size || !fileheader.file_lenms || !fileheader.file_lenbytes ||
fileheader.file_lenms == 0xFFFFFFFF || fileheader.file_lenbytes == 0xFFFFFFFF)
{
if (!avg_framesize_cnt) return 0;
return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot / (double)avg_framesize_cnt);
}
return MulDiv(fileheader.file_lenbytes,8000,fileheader.file_lenms);
}
const char *NSVDecoder::getServerHeader(char *name)
{
return file_reader?file_reader->getheader(name):NULL;
}
void NSVDecoder::getAudioDesc(char *buf)
{
char *t=getAudioType();
if (t && *t)
{
if (strcmp(t, "VLB")==0)
sprintf(buf,"Dolby AAC "); // special case; make sure the user never sees "VLB" name
else if (strcmp(t, "AACP")==0)
sprintf(buf,"HE-AAC ");
else if (strcmp(t, "AAC")==0)
sprintf(buf,"AAC LC ");
else
sprintf(buf,"%s ",t);
char *p=buf+strlen(buf);
if (aud_output) aud_output->getdescstr(p);
if (!*p) *--p=0;
int a=(getAudioBitrate()+500)/1000;
if (a) sprintf(buf+strlen(buf)," ~%d%s",a,WASABI_API_LNGSTRING(IDS_KBPS));
}
else *buf=0;
}
void NSVDecoder::getVideoDesc(char *buf)
{
char *t=getVideoType();
if (t && *t)
{
int fr=(int)(getFrameRate()*100.0);
sprintf(buf,"%s %dx%d@%d.%02d%s",t,getWidth(),getHeight(),fr/100,fr%100,WASABI_API_LNGSTRING(IDS_FPS));
int a=(getVideoBitrate()+500)/1000;
if (a) sprintf(buf+strlen(buf)," ~%d%s",a,WASABI_API_LNGSTRING(IDS_KBPS));
}
else *buf=0;
}
int NSVDecoder::getAudioBitrate()
{
if (!avg_framesize_cnt_a) return 0;
return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot_a / (double)avg_framesize_cnt_a);
}
int NSVDecoder::getVideoBitrate()
{
if (!avg_framesize_cnt_v) return 0;
return (int) (8.0 * getFrameRate() * (double)avg_framesize_tot_v / (double)avg_framesize_cnt_v);
}
char *NSVDecoder::getTitle()
{
char *v=getFromMeta("TITLE");
if (v)
{
if (!m_title || strcmp(v,m_title))
{
free(m_title);
m_title=v;
}
else free(v);
return m_title;
}
if (file_reader)
{
v=file_reader->gettitle();
if (v) return v;
}
if (!m_url) return "";
v=m_url+strlen(m_url);
while (v >= m_url && *v != '/' && *v != '\\' && *v != '=') v--;
return ++v;
}
void NSVDecoder::ProcessSubtitleBlock(void *data, int len)
{
unsigned char *dataptr=(unsigned char *)data;
while (len > 2)
{
int len_block=(dataptr[0] | ((unsigned short)dataptr[1] << 8));
dataptr += 2;
len -= 2;
if (len_block > len) break;
SUBTITLE_INFO sti={0,};
sti.language = (char *)dataptr;
while (len_block > 0 && *dataptr)
{
dataptr++;
len_block--;
len--;
}
dataptr++;
len_block--;
len--;
if (len_block < 1) break;
sti.utf8_text = (char *)dataptr;
while (len_block > 0 && *dataptr)
{
dataptr++;
len_block--;
len--;
}
dataptr++;
len_block--;
len--;
if (len_block < 6) break;
sti.start_frame = framecnt + video_frames_avail + (dataptr[0] | ((int)dataptr[1] << 8));
dataptr+=2;
len-=2;
len_block-=2;
sti.end_frame = sti.start_frame + (dataptr[0] | ((int)dataptr[1] << 8) | ((int)dataptr[2] << 16) | ((int)dataptr[3] << 24));
dataptr+=4;
len-=4;
len_block-=4;
// set defaults for color/position
sti.xPos=128;
sti.yPos=255;
sti.colorRed=255;
sti.colorGreen=255;
sti.colorBlue=255;
sti.fontSize=0;
if (len_block >= 2)
{
sti.xPos = *dataptr++;
sti.yPos = *dataptr++;
len-=2;
len_block-=2;
if (len_block >= 3)
{
sti.colorRed=*dataptr++;
sti.colorGreen=*dataptr++;
sti.colorBlue=*dataptr++;
len-=3;
len_block-=3;
if (len_block > 0)
{
sti.fontSize=(signed char) *dataptr++;
len--;
len_block--;
}
if (len_block > 0)
{
sti.extraData=dataptr;
sti.extraDataSize=len_block;
}
}
}
Subtitles *sub=findSubtitles(sti.language);
if(!sub) sub=insertSubtitlesItem(sti.language,NULL);
sub->addSubtitlePacket(&sti);
if (len_block > 0)
{
len-=len_block;
dataptr+=len_block;
}
}
}
int NSVDecoder::run(int * volatile quit) // returns -1 on error, 0 if no frames processed, 1 if frames processed, 2 if underrun
{
int retval=0;
if (!file_reader) return -1;
if (!aud_decoder_isnotnull && !vid_decoder_isnotnull && vid_decoder && aud_decoder)
{
m_err="Codec(s) not found";
return -1;
}
if (aud_output && vidout_ready)
{
ULONGLONG curpos=aud_output->getpos();
if (!m_paused)
{
if (!audio_frames_avail && curpos == hack_l_curpos && unpacket.getEof())
{
hack_l_curpos=curpos;
curpos += GetTickCount64() - hack_l_curpos_ot;
}
else
{
hack_l_curpos_ot=GetTickCount64();
hack_l_curpos=curpos;
}
}
if (curpos >= vidout_time && !m_prebuffer)
{
if (vidout)
{
// send this to get the flip state updated so when toggled on-the-fly it will work with nsv
m_out->extended(0x1002/*VIDUSER_SET_VFLIP*/,vid_flip,0);
m_out->draw(vidout);
}
if (m_enable_subtitles)
{
SubtitlesItem *it=m_subtitles.get(m_cur_subtitle);
if(it && config_subtitles) {
it->m_subs->setFontSizeModifier(m_subs_fontsize);
m_out->drawSubtitle(it->m_subs->getSubtitle((unsigned int)aud_output->getpos(),framecnt));
}
if ( it && !config_subtitles )
{
m_out->drawSubtitle(NULL);
}
}
vidout=0;
vidout_ready=0;
}
}
if (hdrsearched && m_need_seek!=~0 && aud_output)
{
unsigned int newpos=m_need_seek;
m_need_seek=~0;
seek_dumpaudiosamples=0;
seek_dumpframes=0;
if (file_reader->canseek() && file_reader->getsize() != 0xFFFFFFFF)
{
int nbpos;
if (!fileheader.toc_size || !fileheader.toc || !fileheader.file_lenms ||
!fileheader.file_lenbytes ||
fileheader.file_lenms == 0xFFFFFFFF || fileheader.file_lenbytes == 0xFFFFFFFF)
{
int avg_framesize=avg_framesize_tot/avg_framesize_cnt;
int pos_frames=(int)(newpos*getFrameRate());
nbpos=fileheader.header_size+MulDiv(pos_frames,avg_framesize,1000);
}
else // calculate offset using TOC
{
if (fileheader.toc_ex) // use extended toc, find next earliest time closest
{
int x;
double scale;
if (unpacket.isValid() && getFrameRate() > 0.0001)
scale=1000.0 / getFrameRate();
else scale = 1000.0 / 30.0;
for (x = 0; x < (int)fileheader.toc_size; x ++)
{
if (newpos < (unsigned int) (fileheader.toc_ex[x]*scale)) break;
}
unsigned int hdr[2]={0,0};
if (--x >= 0)
{
hdr[0]=fileheader.toc[x];
hdr[1]=(unsigned int) (fileheader.toc_ex[x] * scale);
}
//hdr[1] is the time of that keyframe
if (m_precise_seeking) // precise seek
{
int timediff = newpos - hdr[1];
double fr;
if (unpacket.isValid() && getFrameRate() >= 0.0001) fr = getFrameRate() / 1000.0;
else fr = 30.0 / 1000.0;
seek_dumpframes=(int) (timediff * fr);
seek_dumpaudiosamples=MulDiv(timediff,pcm_samplerate,1000);
}
else
{
newpos=hdr[1];
}
nbpos = fileheader.header_size + hdr[0];
}
else
{
double tocpos=(newpos*(double)fileheader.toc_size)/(double)fileheader.file_lenms;
unsigned int tocidx=(unsigned int)tocpos;
if (tocidx > fileheader.toc_size)
{
nbpos=fileheader.header_size + fileheader.file_lenbytes;
}
else
{
unsigned int a,b;
if (tocidx<0) tocidx=0;
a = fileheader.toc[tocidx];
if (tocidx < fileheader.toc_size-1)
b = fileheader.toc[tocidx+1];
else b=fileheader.file_lenbytes;
double frac=tocpos-tocidx;
nbpos = fileheader.header_size + (int) (a * (1.0 - frac) + b * frac);
}
}
}
if (!file_reader->seek(nbpos))
{
framecnt=(int)(newpos*getFrameRate()/1000.0);
unpacket.reset(0);
unpacket.setEof(0);
hdrsearched=1;
needkf=1;
m_avresync_time=0;
vidout=0;
vidout_ready=0;
video_frames_avail=0;
audio_frames_avail=0;
m_prebuffer=m_pb_init;
inbs.clear();
audiobs.clear();
videobs.clear();
auxbs.clear();
if (aud_output) aud_output->flush(newpos);
if (aud_decoder) aud_decoder->flush();
if (vid_decoder) vid_decoder->flush();
}
}
} // end of seeking
if (!hdrsearched) // search for header
{
readagain_header:
if (quit && *quit) return 0;
int ret=nsv_readheader(inbs,&fileheader);
if (ret <= 0)
{
hdrsearched++;
if (!ret)
{
_locale_t C_locale = WASABI_API_LNG->Get_C_NumericLocale();
char *v=getFromMeta("ASPECT");
if (v)
{
aspect=_atof_l(v,C_locale);
}
free(v);
v=getFromMeta("FRAMERATE");
if (v)
{
framerate_override=_atof_l(v,C_locale);
if (framerate_override >= 0.01) use_framerate_override=1;
free(v);
}
}
}
else
{
char buf[8192] = {0};
size_t ret2=file_reader->read(buf,sizeof(buf));
if (file_reader->iseof()) unpacket.setEof(1);
if (file_reader->geterror())
{
m_err=file_reader->geterror();
return -1;
}
if (ret2>0)
{
inbs.add(buf, (int)ret2);
goto readagain_header;
}
}
}
if (!hdrsearched) return 0;
// end of header search
// read from source
if (m_prebuffer ||
(((!unpacket.isValid() || audio_frames_avail < (int)((getFrameRate() * m_buffer_total)/1000.0)) || !videobs.avail()) &&
(!m_buf_memlimit || ((int)(audiobs.avail()+videobs.avail()) < m_buf_memlimit*8))))
{
readagain_stream:
if (quit && *quit) return 0;
int lavail= (int)inbs.avail();
int ret=unpacket.unpacket(inbs);
inbs.compact();
if (ret)
{
if (!unpacket.isValid() && nsvbitstream_search > 8*1024*1024) // only scan for 8mb max
{
m_err="Error synching to NSV stream";
return -1;
}
if (unpacket.getEof())
{
if (!videobs.avail() && !video_frames_avail && (!aud_output || aud_output->isplaying() <= 0)) retval=-1;
// do nothing
}
else
{
char buf[8192] = {0};
size_t ret2=file_reader->read(buf,sizeof(buf));
if (ret2 == 0 && file_reader->iseof())
{
m_prebuffer=0;
unpacket.setEof(1);
}
if (file_reader->geterror())
{
m_err=file_reader->geterror();
return -1;
}
if (ret2 > 0)
{
nsvbitstream_search+= (int)ret2;
inbs.add(buf, (int)ret2);
goto readagain_stream;
}
}
}
else
{
nsvbitstream_search=0;
video_frames_avail++;
audio_frames_avail++;
nsv_type_to_string(unpacket.getAudFmt(),m_audio_type);
nsv_type_to_string(unpacket.getVidFmt(),m_video_type);
avg_framesize_cnt++;
avg_framesize_tot+=lavail- (int)((inbs.avail())/8);
if (avg_framesize_tot > 0x80000000)
{
avg_framesize_tot/=2;
avg_framesize_cnt/=2;
}
if (1)
{
//parse aux packets
while (auxbs.avail() >= 8*8)
{
size_t thislen = auxbs.getbits(32);
unsigned int thistype = auxbs.getbits(32);
if (thislen > auxbs.avail()/8) break;
if (thistype == NSV_MAKETYPE('S','U','B','T') ) ProcessSubtitleBlock(auxbs.getcurbyteptr(), (int)thislen);
else if (thistype == NSV_MAKETYPE('A','S','Y','N'))
{
//if (thislen == 0) // resyncpoint
{
audiobs.addint(-1);
audiobs.addint(framecnt+video_frames_avail);
}
//else // handle other form [todo]
//{
//}
}
auxbs.seek(thislen*8);
}
}
auxbs.clear();
}
}
if (unpacket.isValid() && !m_prebuffer && !audiobs.avail() && aud_output)
{
if (aud_output->isplaying()) aud_output->write(0,0);
else if (!unpacket.getEof())
{
m_prebuffer=m_pb_init_ur+(int)((video_frames_avail/getFrameRate())*1000.0);
if (m_prebuffer < m_pb_init_ur) m_prebuffer=m_pb_init_ur;
if (m_prebuffer > m_pb_init_ur*3) m_prebuffer=m_pb_init_ur*3;
retval=2;
}
}
if (video_frames_avail > (int)((m_prebuffer*getFrameRate())/1000.0))
{
m_bufstate=256;
m_prebuffer=0;
}
else
{
int a = (int) (video_frames_avail * 256.0 / ((m_prebuffer*getFrameRate())/1000.0));
if (a > 255) a=255;
if (a < 0) a=0;
m_bufstate=a;
if (m_out) m_out->notifyBufferState(a);
}
if (unpacket.isValid() && !vidout_ready && videobs.avail() && !m_prebuffer) // decode frame
{
if (!vid_decoder)
{
vid_flip=0;
vid_decoder = CreateVideoDecoder(unpacket.getWidth(),unpacket.getHeight(),
getFrameRate(),unpacket.getVidFmt(),&vid_flip,&vid_decoder_isnotnull);
}
int kf=0;
int l=videobs.getbits(32);
unsigned int local_vidout_type[3] = { 1, 0, 0 };
int ret=vid_decoder->decode(needkf,videobs.getcurbyteptr(),l,&vidout,local_vidout_type,&kf);
vidout_type = local_vidout_type[0];
if (ret == 0)
{
vidout_codec_width = local_vidout_type[1];
vidout_codec_height = local_vidout_type[2];
}
if (kf) needkf=0;
video_frames_avail--;
videobs.seek(l*8);
videobs.compact();
avg_framesize_cnt_v++;
avg_framesize_tot_v+=l;
if (needkf || seek_dumpframes > 0)
{
vidout=NULL;
if (seek_dumpframes>0) seek_dumpframes--;
}
else
{
if (!m_out_opened && vid_decoder_isnotnull)
{
m_out->extended(0x1008/*VIDUSER_SET_THREAD_SAFE*/, 1, 0);
unsigned int vidwidth = vidout_codec_width, vidheight = vidout_codec_height;
if (vidwidth && vidheight)
{
/* if the codec provided a width/height to use */
double aspect_adjust = (double)vidwidth / (double)vidheight / ((double)unpacket.getWidth() / (double)unpacket.getHeight());
if (m_out->open(vidwidth,vidheight,vid_flip,aspect * aspect_adjust,vidout_type))
{
m_err="Error opening video output";
return -1;
}
}
else
{
if (m_out->open(unpacket.getWidth(),unpacket.getHeight(),vid_flip,aspect,vidout_type))
{
m_err="Error opening video output";
return -1;
}
}
m_out_opened=1;
}
vidout_ready=1;
vidout_time = (unsigned int)(framecnt++ / getFrameRate() * 1000.0) + (unsigned int)m_avresync_time;
int offs=unpacket.getSyncOffset() + m_out->get_latency();
if ((int)vidout_time <= offs) vidout_time=0;
else vidout_time-=offs;
//drop the image if we're late
if(aud_output && vidout_time<aud_output->getpos()) {
vidout=0;
vidout_ready=0;
}
}
retval=1;
}
if ( ( audiobs.avail() && !m_prebuffer ) || m_again )
{
if (needkf)
{
int l=audiobs.getbits(32);
if (l == -1) audiobs.seek(32); // skip over 32 bits of our audiobs info
else
{
audiobs.seek(l*8);
audio_frames_avail--;
}
audiobs.compact();
}
else // decode some audio
{
char pcmbuf[16384] = {0};
int outbufl=512;
if (!aud_decoder)
{
aud_decoder=CreateAudioDecoder(unpacket.getAudFmt(),&aud_decoder_isnotnull,&aud_output);
if (aud_output && m_paused) aud_output->pause(1);
}
if (aud_output) outbufl=aud_output->canwrite();
if (outbufl)
{
unsigned int outfmt[8] = {0};
retval=1;
if (outbufl > sizeof(pcmbuf)) outbufl=sizeof(pcmbuf);
int inl=audiobs.getbits(32);
if (inl == -1)
{
int vidframe=audiobs.getbits(32);
if (aud_output)
{
ULONGLONG audpos = aud_output->getwritepos();
int vidpos = (int)(vidframe / getFrameRate() * 1000.0);
m_avresync_time = audpos-vidpos;
}
}
else
{
int ret=aud_decoder->decode(audiobs.getcurbyteptr(), inl, pcmbuf, &outbufl, outfmt);
if ( !outbufl ) m_again = 0;
else m_again =1;
if (ret < 1)
{
avg_framesize_cnt_a++;
avg_framesize_tot_a+=inl;
audiobs.seek(inl*8);
audiobs.compact();
audio_frames_avail--;
}
else audiobs.seek(-32);
if (outbufl || outfmt[0])
{
if (!aud_output)
{
aud_output=PCMOUT_CREATE(outfmt);
pcm_samplerate=outfmt[1];
if (!aud_output) aud_output = new NullAudioOutput;
aud_output->setvolume(m_volume);
aud_output->setpan(m_pan);
if (aud_output && m_paused) aud_output->pause(1);
}
if (outbufl)
{
if (seek_dumpaudiosamples)
{
int nch=outfmt[2];
int bps=outfmt[3];
int dump=seek_dumpaudiosamples*nch*(bps/8);
if (dump >= outbufl)
{
seek_dumpaudiosamples -= outbufl/nch/(bps/8);
}
else // partial write
{
aud_output->write(pcmbuf+dump,outbufl-dump);
seek_dumpaudiosamples=0;
}
}
else aud_output->write(pcmbuf,outbufl);
}
}
}
} // if can write
} //end of decode
} // audiobs avail
if (m_prebuffer)
#ifdef _WIN32
Sleep(10);
#else
usleep(10000);
#endif
return retval;
}
Subtitles *NSVDecoder::insertSubtitlesItem(const char *language, const char *subfile) {
Subtitles *sub=new Subtitles(subfile);
m_subtitles.put(new SubtitlesItem(language, sub));
return sub;
}
Subtitles *NSVDecoder::findSubtitles(const char *language) {
for(int i=0;i<m_subtitles.getlen();i++) {
SubtitlesItem *s=m_subtitles.get(i);
if(!_stricmp(language,s->m_language) ) return s->m_subs;
}
return NULL;
}
const char *NSVDecoder::getSubLanguage(int index) {
if(index>=m_subtitles.getlen()) return NULL;
return m_subtitles.get(index)->m_language;
}

464
Src/nsv/nsvplay/nsvplay.dsp Normal file
View File

@ -0,0 +1,464 @@
# Microsoft Developer Studio Project File - Name="nsvplay" - Package Owner=<4>
# Microsoft Developer Studio Generated Build File, Format Version 6.00
# ** DO NOT EDIT **
# TARGTYPE "Win32 (x86) Application" 0x0101
CFG=nsvplay - Win32 Debug
!MESSAGE This is not a valid makefile. To build this project using NMAKE,
!MESSAGE use the Export Makefile command and run
!MESSAGE
!MESSAGE NMAKE /f "nsvplay.mak".
!MESSAGE
!MESSAGE You can specify a configuration when running NMAKE
!MESSAGE by defining the macro CFG on the command line. For example:
!MESSAGE
!MESSAGE NMAKE /f "nsvplay.mak" CFG="nsvplay - Win32 Debug"
!MESSAGE
!MESSAGE Possible choices for configuration are:
!MESSAGE
!MESSAGE "nsvplay - Win32 Release" (based on "Win32 (x86) Application")
!MESSAGE "nsvplay - Win32 Debug" (based on "Win32 (x86) Application")
!MESSAGE
# Begin Project
# PROP AllowPerConfigDependencies 0
# PROP Scc_ProjName ""
# PROP Scc_LocalPath ""
CPP=cl.exe
MTL=midl.exe
RSC=rc.exe
!IF "$(CFG)" == "nsvplay - Win32 Release"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 0
# PROP BASE Output_Dir "Release"
# PROP BASE Intermediate_Dir "Release"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 0
# PROP Output_Dir "Release"
# PROP Intermediate_Dir "Release"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /GX /O2 /D "WIN32" /D "NDEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /c
# ADD CPP /nologo /MT /W3 /O2 /D "NDEBUG" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "USE_ASM" /D "NO_LAYER12" /D "DLL_DECODER_SUPPORT" /D "BUILTIN_MP3_SUPPORT" /D "BUILTIN_VP3_SUPPORT" /D "SUBTITLES_READER" /FD /c
# SUBTRACT CPP /YX
# ADD BASE MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "NDEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "NDEBUG"
# ADD RSC /l 0x409 /d "NDEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /machine:I386
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib winmm.lib ddraw.lib wsock32.lib vfw32.lib /nologo /subsystem:windows /machine:I386 /out:"c:\nsv\nsvplay.exe"
!ELSEIF "$(CFG)" == "nsvplay - Win32 Debug"
# PROP BASE Use_MFC 0
# PROP BASE Use_Debug_Libraries 1
# PROP BASE Output_Dir "Debug"
# PROP BASE Intermediate_Dir "Debug"
# PROP BASE Target_Dir ""
# PROP Use_MFC 0
# PROP Use_Debug_Libraries 1
# PROP Output_Dir "Debug"
# PROP Intermediate_Dir "Debug"
# PROP Ignore_Export_Lib 0
# PROP Target_Dir ""
# ADD BASE CPP /nologo /W3 /Gm /GX /ZI /Od /D "WIN32" /D "_DEBUG" /D "_WINDOWS" /D "_MBCS" /YX /FD /GZ /c
# ADD CPP /nologo /MTd /W3 /Gm /GX /ZI /Od /D "_DEBUG" /D "BUILTIN_VFW_SUPPORT" /D "WIN32" /D "_WINDOWS" /D "_MBCS" /D "USE_ASM" /D "NO_LAYER12" /D "DLL_DECODER_SUPPORT" /D "BUILTIN_MP3_SUPPORT" /D "BUILTIN_VP3_SUPPORT" /D "SUBTITLES_READER" /YX /FD /GZ /c
# ADD BASE MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD MTL /nologo /D "_DEBUG" /mktyplib203 /win32
# ADD BASE RSC /l 0x409 /d "_DEBUG"
# ADD RSC /l 0x409 /d "_DEBUG"
BSC32=bscmake.exe
# ADD BASE BSC32 /nologo
# ADD BSC32 /nologo
LINK32=link.exe
# ADD BASE LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib /nologo /subsystem:windows /debug /machine:I386 /pdbtype:sept
# ADD LINK32 kernel32.lib user32.lib gdi32.lib winspool.lib comdlg32.lib advapi32.lib shell32.lib ole32.lib oleaut32.lib uuid.lib odbc32.lib odbccp32.lib ddraw.lib winmm.lib wsock32.lib vfw32.lib /nologo /subsystem:windows /debug /machine:I386 /out:"c:\nsv\nsvplay.exe" /pdbtype:sept
!ENDIF
# Begin Target
# Name "nsvplay - Win32 Release"
# Name "nsvplay - Win32 Debug"
# Begin Group "Source Files"
# PROP Default_Filter "cpp;c;cxx;rc;def;r;odl;idl;hpj;bat"
# Begin Group "audio"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\audiostub.cpp
# End Source File
# Begin Source File
SOURCE=.\audiostub.h
# End Source File
# End Group
# Begin Group "vp3"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\vp32\include\duck_dxl.h
# End Source File
# Begin Source File
SOURCE=.\vp3stub.cpp
# End Source File
# Begin Source File
SOURCE=.\vp3stub.h
# End Source File
# Begin Source File
SOURCE=..\..\vp32\lib\win32\Release\s_cpuid.lib
# End Source File
# Begin Source File
SOURCE=..\..\vp32\lib\win32\Release\s_dxv.lib
# End Source File
# Begin Source File
SOURCE=..\..\vp32\lib\win32\Release\s_sal.lib
# End Source File
# Begin Source File
SOURCE=..\..\vp32\lib\win32\Release\s_vp31d.lib
# End Source File
# End Group
# Begin Group "mp3dec"
# PROP Default_Filter ""
# Begin Group "fhg"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\mp3dec\bitsequence.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\bitstream.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\bitstream.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\conceal.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\conceal.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\crc16.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\crc16.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\giobase.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffdec.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffdec.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffmanbitobj.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffmandecoder.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffmandecoder.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffmantable.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\huffmantable.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\l3table.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\l3table.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mdct.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mdct.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp2decode.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp2decode.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3decode.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3decode.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3quant.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3quant.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3read.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3read.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3ssc.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3ssc.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3sscdef.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3streaminfo.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3tools.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mp3tools.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpeg.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpegbitstream.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpegbitstream.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpegheader.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpegheader.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpgadecoder.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\mpgadecoder.h
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\polyphase.cpp
# End Source File
# Begin Source File
SOURCE=..\..\mp3dec\polyphase.h
# End Source File
# End Group
# Begin Source File
SOURCE=.\mp3stub.cpp
# End Source File
# Begin Source File
SOURCE=.\mp3stub.h
# End Source File
# End Group
# Begin Group "nsv"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\nsvbs.h
# End Source File
# Begin Source File
SOURCE=..\nsvlib.cpp
# End Source File
# Begin Source File
SOURCE=..\nsvlib.h
# End Source File
# End Group
# Begin Group "jnetlib"
# PROP Default_Filter ""
# Begin Source File
SOURCE=..\..\jnetlib\asyncdns.cpp
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\asyncdns.h
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\connection.cpp
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\connection.h
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\httpget.cpp
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\httpget.h
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\jnetlib.h
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\netinc.h
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\util.cpp
# End Source File
# Begin Source File
SOURCE=..\..\jnetlib\util.h
# End Source File
# End Group
# Begin Group "video"
# PROP Default_Filter ""
# Begin Source File
SOURCE=.\vid_ddraw.cpp
# End Source File
# Begin Source File
SOURCE=.\vid_ddraw.h
# End Source File
# Begin Source File
SOURCE=.\vid_overlay.cpp
# End Source File
# Begin Source File
SOURCE=.\vid_overlay.h
# End Source File
# Begin Source File
SOURCE=.\video.cpp
# End Source File
# Begin Source File
SOURCE=.\video.h
# End Source File
# End Group
# Begin Source File
SOURCE=.\about.h
# End Source File
# Begin Source File
SOURCE=.\decoders.cpp
# End Source File
# Begin Source File
SOURCE=.\main.cpp
# End Source File
# Begin Source File
SOURCE=.\nsvdecode.cpp
# End Source File
# Begin Source File
SOURCE=.\nsvplay.rc
# End Source File
# Begin Source File
SOURCE=.\readers.cpp
# End Source File
# Begin Source File
SOURCE=.\subtitles.cpp
# End Source File
# Begin Source File
SOURCE=.\wndmenu.h
# End Source File
# End Group
# Begin Group "Header Files"
# PROP Default_Filter "h;hpp;hxx;hm;inl"
# Begin Source File
SOURCE=.\main.h
# End Source File
# Begin Source File
SOURCE=.\subtitles.h
# End Source File
# End Group
# Begin Group "Resource Files"
# PROP Default_Filter "ico;cur;bmp;dlg;rc2;rct;bin;rgs;gif;jpg;jpeg;jpe"
# Begin Source File
SOURCE=.\nsv_logo.bmp
# End Source File
# End Group
# End Target
# End Project

View File

@ -0,0 +1,29 @@
Microsoft Developer Studio Workspace File, Format Version 6.00
# WARNING: DO NOT EDIT OR DELETE THIS WORKSPACE FILE!
###############################################################################
Project: "nsvplay"=.\nsvplay.dsp - Package Owner=<4>
Package=<5>
{{{
}}}
Package=<4>
{{{
}}}
###############################################################################
Global:
Package=<5>
{{{
}}}
Package=<3>
{{{
}}}
###############################################################################

106
Src/nsv/nsvplay/nsvplay.rc Normal file
View File

@ -0,0 +1,106 @@
//Microsoft Developer Studio 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 DISCARDABLE
BEGIN
"resource.h\0"
END
2 TEXTINCLUDE DISCARDABLE
BEGIN
"#include ""afxres.h""\r\n"
"\0"
END
3 TEXTINCLUDE DISCARDABLE
BEGIN
"\r\n"
"\0"
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Dialog
//
IDD_ABOUT DIALOG DISCARDABLE 0, 0, 184, 54
STYLE DS_MODALFRAME | DS_CENTER | WS_POPUP | WS_CAPTION | WS_SYSMENU
CAPTION "About..."
FONT 8, "MS Sans Serif"
BEGIN
DEFPUSHBUTTON "OK",IDOK,105,33,50,14
CTEXT "Static",IDC_VERSION,74,7,103,8
CTEXT "Copyright (C) 2003 Nullsoft",IDC_STATIC,74,16,103,8
CONTROL 102,IDC_STATIC,"Static",SS_BITMAP,7,7,67,39
END
/////////////////////////////////////////////////////////////////////////////
//
// DESIGNINFO
//
#ifdef APSTUDIO_INVOKED
GUIDELINES DESIGNINFO DISCARDABLE
BEGIN
IDD_ABOUT, DIALOG
BEGIN
LEFTMARGIN, 7
RIGHTMARGIN, 177
TOPMARGIN, 7
BOTTOMMARGIN, 47
END
END
#endif // APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Bitmap
//
IDB_BITMAP1 BITMAP DISCARDABLE "nsv_logo.bmp"
#endif // English (U.S.) resources
/////////////////////////////////////////////////////////////////////////////
#ifndef APSTUDIO_INVOKED
/////////////////////////////////////////////////////////////////////////////
//
// Generated from the TEXTINCLUDE 3 resource.
//
/////////////////////////////////////////////////////////////////////////////
#endif // not APSTUDIO_INVOKED

949
Src/nsv/nsvplay/readers.cpp Normal file
View File

@ -0,0 +1,949 @@
#include <windows.h>
#include "main.h"
#include <sys/stat.h>
#ifdef NO_WASABI
#include "../../jnetlib/httpget.h"
api_httpreceiver *CreateGet()
{
return new JNL_HTTPGet;
}
void ReleaseGet(api_httpreceiver *&get)
{
delete (JNL_HTTPGet *)get;
get=0;
}
#else
#include "../..\Components\wac_network\wac_network_http_receiver_api.h"
#include <api.h>
#include <api/service/waservicefactory.h>
#include "../../Winamp/in2.h"
extern In_Module mod;
waServiceFactory *httpFactory = 0;
api_httpreceiver *CreateGet()
{
api_httpreceiver *get = 0;
if (!httpFactory && mod.service)
httpFactory = mod.service->service_getServiceByGuid(httpreceiverGUID);
if (httpFactory)
get = (api_httpreceiver *)httpFactory->getInterface();
return get;
}
void ReleaseGet(api_httpreceiver *&get)
{
if (!get)
return ;
if (!httpFactory && mod.service)
waServiceFactory *sf = mod.service->service_getServiceByGuid(httpreceiverGUID);
if (httpFactory)
httpFactory->releaseInterface(get);
get = 0;
}
#endif
#define MAX_MULTICONNECTS 8
class HTTPReader : public IDataReader
{
public:
HTTPReader(const char *url);
~HTTPReader();
size_t read(char *buf, size_t len);
bool iseof() { return !!m_eof; }
char *gettitle() { return m_title; }
char *geterror() { return m_err; }
bool canseek() { return m_content_length != 0xFFFFFFFF && m_accept_ranges && !m_meta_interval; }
int seek(unsigned __int64 newpos)
{
if (!canseek()) return 1;
doConnect((int)newpos);
return 0;
}
unsigned __int64 getsize() { return m_content_length; }
char *getheader(char *header_name)
{
return m_get ? (char *)m_get->getheader(header_name) : NULL;
}
private:
int serialconnect( int seekto , int timeout);
void doConnect(int seekto);
int getProxyInfo(const char *url, char *out);
char *m_url;
char *m_err;
char *m_title;
int m_eof;
int m_meta_init, m_meta_interval, m_meta_pos, m_meta_size, m_meta_buf_pos;
char m_meta_buf[4096];
int m_read_headers;
unsigned int m_content_length;
int m_accept_ranges;
int m_is_uvox;
int m_uvox_readpos;
int m_uvox_enough_bytes;
char proxybuf[8400], *proxy;
api_httpreceiver *m_get;
// this structure is allocated once, and freed once
struct
{
char *url; //pointers into m_url
// these two are only active temporarily.
api_httpreceiver *get;
char *error;
}
m_cons[MAX_MULTICONNECTS];
int m_numcons;
int m_newcons;
int m_serialized;
int m_mstimeout;
int m_contryptr;
int m_serialfailed;
int m_useaproxy;
};
HTTPReader::HTTPReader(const char *url)
{
m_title = 0;
m_is_uvox = m_uvox_readpos = 0;
m_meta_init = m_meta_interval = m_meta_pos = m_meta_size = m_meta_buf_pos = 0;
m_meta_buf[0] = 0;
m_err = NULL;
m_eof = 0;
m_read_headers = 0;
m_content_length = 0xFFFFFFFF;
m_accept_ranges = 0;
m_get = NULL;
m_serialized = 0;
m_mstimeout = 0;
m_contryptr = 0;
m_newcons = 0;
m_serialfailed = 0;
m_useaproxy = 0;
// TCP multiconnect
// JF> using ; as a delimiter is vomit inducing and breaks a lot of other
// code. I petition we use <> to delimit, and I'm making it do that.
m_numcons = 0;
m_url = _strdup(url);
int allowproxy = 1;
char *tmpurl = m_url;
while (m_numcons < MAX_MULTICONNECTS)
{
char *next = strstr( tmpurl, "<>" );
if ( next ) *next = '\0';
if (tmpurl[0])
{
m_cons[m_numcons].error = NULL;
m_cons[m_numcons].get = NULL;
m_cons[m_numcons++].url = tmpurl;
if (!_strnicmp(tmpurl, "uvox:", 5)) allowproxy = 0;
if (!_strnicmp(tmpurl, "order://", 8))
{
char *p = tmpurl + 8;
// serialized mctp
m_serialized = 1;
m_numcons--;
m_mstimeout = atoi(p);
if ( m_mstimeout < 1 )
{
m_serialized = 0;
m_mstimeout = 0;
}
}
}
if (!next) break;
tmpurl = next + 2;
}
memset(proxybuf, 0, sizeof(proxybuf));
proxy = NULL;
if (allowproxy && getProxyInfo(url, proxybuf))
{
proxy = strstr(proxybuf, "http=");
if (!proxy) proxy = proxybuf;
else
{
proxy += 5;
char *tp = strstr(proxy, ";");
if (tp) *tp = 0;
}
}
m_is_uvox = 0;
if ( m_serialized && m_numcons > 1 ) // sanity check
{
int rval = 0, i;
m_newcons = 1;
// walk the list, set the url such that m_cons[0].url points to each item. try to connect
// serialconnect returns error codes -1 on error, 0 on timeout, 1 on successfull connect
for ( i = 0; i < m_numcons; i++ )
{
if ( i )
{
m_cons[0].url = m_cons[i].url;
}
rval = serialconnect(0, m_mstimeout);
if ( rval == 1 ) break;
}
if ( rval < 1 )
{
// we didnt get a connection so...
m_serialfailed = 1;
}
}
else
doConnect(0);
}
void HTTPReader::doConnect(int seekto)
{
ReleaseGet(m_get);
m_uvox_readpos = 0;
m_eof = 0;
int i;
for (i = 0; i < m_numcons; i++ )
{
free(m_cons[i].error);
m_cons[i].error = NULL;
ReleaseGet(m_cons[i].get);
m_cons[i].get = CreateGet();
if (!m_cons[i].get)
break;
m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL);
#ifdef WINAMP_PLUGIN
m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)");
#else
# ifdef WINAMPX
m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)");
# else
m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)");
# endif
#endif
m_cons[i].get->addheader("Accept:*/*");
m_cons[i].get->addheader("Connection:close");
m_cons[i].get->addheader("Ultravox-transport-type: TCP");
if (seekto)
{
char buf[64] = {0};
wsprintfA(buf, "Range:bytes=%d-", seekto);
m_cons[i].get->addheader(buf);
}
else
m_cons[i].get->addheader("icy-metadata:1");
m_cons[i].get->connect(m_cons[i].url, !!seekto);
}
m_uvox_enough_bytes = 1;
}
HTTPReader::~HTTPReader()
{
ReleaseGet(m_get);
free(m_title);
free(m_err);
free(m_url);
int i;
for (i = 0; i < m_numcons; i++)
{
ReleaseGet(m_cons[i].get);
free(m_cons[i].error);
}
}
int HTTPReader::serialconnect(int seekto , int timeout)
{
ReleaseGet(m_get);
m_uvox_readpos = 0;
m_eof = 0;
int64_t mythen, mynow , myref;
LARGE_INTEGER then, now, ref;
QueryPerformanceFrequency( &ref);
myref = ref.QuadPart;
QueryPerformanceCounter( &then );
mythen = then.QuadPart;
int timer = 0;
int i = 0;
{
ReleaseGet(m_cons[i].get);
m_cons[i].get = CreateGet();
if (m_cons[i].get == NULL)
return 0;
m_cons[i].get->open(API_DNS_AUTODNS, 65536, (proxy && proxy[0]) ? proxy : NULL);
#ifdef WINAMP_PLUGIN
m_cons[i].get->addheader("User-Agent:Winamp NSV Player/5.12 (ultravox/2.0)");
#else
# ifdef WINAMPX
m_cons[i].get->addheader("User-Agent:" UNAGI_USER_AGENT " (ultravox/2.0)");
# else
m_cons[i].get->addheader("User-Agent:NSV Player/0.0 (ultravox/2.0)");
# endif
#endif
m_cons[i].get->addheader("Accept:*/*");
m_cons[i].get->addheader("Connection:close");
m_cons[i].get->addheader("Ultravox-transport-type: TCP");
if (seekto)
{
char buf[64] = {0};
wsprintfA(buf, "Range:bytes=%d-", seekto);
m_cons[i].get->addheader(buf);
}
else m_cons[i].get->addheader("icy-metadata:1");
m_cons[i].get->connect(m_cons[i].url, !!seekto);
}
m_uvox_enough_bytes = 1;
int ret, status;
if (!m_get)
{
if (m_err) return 0;
int i;
int found = 0;
i = 0;
while ( timer < timeout )
{
if (!m_cons[i].get) return 0;
found = 1;
QueryPerformanceCounter( &now );
mynow = now.QuadPart;
float profiletime = (float)(mynow - mythen);
profiletime /= myref;
profiletime *= 1000.0;
timer = (int) profiletime;
ret = m_cons[i].get->run();
status = m_cons[i].get->get_status();
if (ret < 0 || status < 0)
{
const char *t = m_cons[i].get->geterrorstr();
if (t)
{}
ReleaseGet(m_cons[i].get);
break;
}
if ( status > 0 )
{
int code = m_cons[i].get->getreplycode();
if ( code < 200 || code > 299 )
{
ReleaseGet(m_cons[i].get);
//wsprintf( m_cons[i].error, "Error: Server returned %d", code );
break;
}
else
{
// we're in good shape, make our getter current, and delete all the gay ones
ReleaseGet(m_get); // just in case, probably zero anyway
m_get = m_cons[i].get;
m_cons[i].get = NULL;
// trash i here, but we are breaking anyway :)
/* for (i = 0; i < m_numcons; i++)
{
delete m_cons[i].get;
m_cons[i].get = NULL;
free( m_cons[i].error );
m_cons[i].error = NULL;
}*/
break;
}
}
#ifdef _WIN32
Sleep(1);
#else
usleep(1000);
#endif
} // while
if ( timer > timeout )
{
ReleaseGet(m_cons[i].get);
ReleaseGet(m_get);
return 0;
}
if (!m_get)
{
return 0;
}
}
if ( m_get ) return 1;
else return 0;
}
size_t HTTPReader::read(char *buffer, size_t len)
{
int ret, status;
if (!m_get)
{
if (m_err) return 0;
int i;
int found = 0;
for (i = 0; !m_get && i < m_numcons; i++)
{
if (!m_cons[i].get) continue;
found = 1;
ret = m_cons[i].get->run();
status = m_cons[i].get->get_status();
if (ret < 0 || status < 0)
{
const char *t = m_cons[i].get->geterrorstr();
if (t)
{
free(m_cons[i].error);
m_cons[i].error = _strdup( t );
}
ReleaseGet(m_cons[i].get);
}
if ( status > 0 )
{
int code = m_cons[i].get->getreplycode();
if ( code < 200 || code > 299 )
{
ReleaseGet(m_cons[i].get);
m_cons[i].get = NULL;
free(m_cons[i].error);
m_cons[i].error = (char *)malloc( 100 );
wsprintfA( m_cons[i].error, "Error: Server returned %d", code );
}
else
{
// we're in good shape, make our getter current, and delete all the gay ones
ReleaseGet(m_get); // just in case, probably zero anyway
m_get = m_cons[i].get;
m_cons[i].get = NULL;
// trash i here, but we are breaking anyway :)
for (i = 0; i < m_numcons; i++)
{
ReleaseGet(m_cons[i].get);
free( m_cons[i].error );
m_cons[i].error = NULL;
}
break; // exit loop of connections
}
}
} // loop of connections
if (!found) // out of attempted connections heh
{
free( m_err );
if (m_numcons > 1)
{
size_t size = 0;
for (i = 0; i < m_numcons; i++)
if ( m_cons[i].error ) size += strlen( m_cons[i].error ) + 1;
m_err = (char *)malloc(size + 100);
wsprintfA( m_err, "No Valid Multiconnect URLs (%d);", m_numcons );
for (i = 0; i < m_numcons; i++)
{
strcat( m_err, m_cons[i].error );
strcat( m_err, ";" );
free(m_cons[i].error);
m_cons[i].error = NULL;
}
}
else
{
m_err = m_cons[0].error;
m_cons[0].error = NULL;
if (!m_err) m_err = _strdup("Connection error (Invalid URL?)");
}
}
if (!m_get) return 0;
}
ret = m_get->run();
status = m_get->get_status();
if (ret > 0 && (!m_get->bytes_available() || !m_uvox_enough_bytes) && status > 1)
{
m_eof = 1;
}
if (ret < 0 || status < 0)
{
const char *t = m_get->geterrorstr();
if (t)
{
free( m_err );
m_err = (char *)malloc( strlen( t) + 16 );
wsprintfA( m_err, "Error: %s", t );
return 0;
}
}
if (status > 0)
{
if (!m_read_headers)
{
if (status > 1)
{
const char *v = m_get->getheader("Content-Length");
if (v) m_content_length = atoi(v);
v = m_get->getheader("Accept-Ranges");
if (v) while (v && *v)
{
if (!_strnicmp(v, "bytes", 5))
{
m_accept_ranges = 1;
break;
}
v++;
}
v = m_get->getheader("icy-metaint");
if (v)
{
m_meta_interval = atoi(v);
}
if (!m_title)
{
v = m_get->getheader("icy-name");
if (v)
m_title = _strdup(v);
}
#ifdef WINAMP_PLUGIN
extern void process_url(char *url);
v = m_get->getheader("icy-url");
if (v && !strstr(v, "shoutcast.com"))
{
char *p = (char *)v; while (p && *p && *p == ' ') p++;
process_url(p);
}
#endif
v = m_get->getheader("content-type");
if (v && !_stricmp(v, "misc/ultravox"))
{
v = m_get->getheader("ultravox-max-msg");
if (v) m_is_uvox = atoi(v);
if (!m_is_uvox) m_is_uvox = 16000;
}
if (!m_title)
{
v = m_get->getheader("content-disposition");
if (v) v = strstr(v, "filename=");
if (v)
{
m_title = _strdup(v + 9);
}
}
m_read_headers = 1;
}
}
size_t l = m_get->bytes_available();
if (m_is_uvox)
{
again:
if (l >= 6)
{
unsigned char buf[32768*2] = {0};
m_get->peek_bytes((char *)buf, 6);
if (buf[0] != 0x5A)
{
l--;
m_get->get_bytes((char *)buf, 1);
goto again;
}
int resqos = buf[1];
int classtype = (buf[2] << 8) | buf[3];
int msglen = (buf[4] << 8) | buf[5];
if (msglen > m_is_uvox) // length is too long
{
m_get->get_bytes((char *)buf, 1);
l--;
goto again;
}
if (msglen + 7 <= (int)l)
{
m_uvox_enough_bytes = 1;
m_get->peek_bytes((char *)buf, msglen + 7);
if (buf[msglen + 6])
{
m_get->get_bytes((char *)buf, 1);
l--;
goto again;
}
if (classtype == 0x7777) // take any data for now, ignore all other frames
{
l = msglen - m_uvox_readpos;
if (l > len) l = len;
memcpy(buffer, buf + 6 + m_uvox_readpos, l);
m_uvox_readpos += (int)l;
if (m_uvox_readpos >= msglen)
{
m_uvox_readpos = 0;
m_get->get_bytes((char *)buf, msglen + 7);
}
return l;
#ifdef WINAMP_PLUGIN
}
else if ( classtype == 0x3001 )
{
extern void process_metadata(char *buf, int size);
m_get->get_bytes((char *)buf, msglen + 7);
process_metadata((char*)buf + 6, msglen + 1);
#endif
}
else
{
m_get->get_bytes((char *)buf, msglen + 7);
}
}
else
{
m_uvox_enough_bytes = 0;
}
}
return 0;
}
else
{
if (l > len) l = len;
m_get->get_bytes(buffer, (int)l);
if (m_meta_interval)
{
int x = (int)l;
unsigned char *buf = (unsigned char *)buffer;
if (m_meta_size) // already in meta block
{
int len = min(x, m_meta_size - m_meta_buf_pos);
memcpy(m_meta_buf + m_meta_buf_pos, buf, len);
m_meta_buf_pos += len;
if (m_meta_buf_pos == m_meta_size)
{
// if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
m_meta_buf_pos = 0;
m_meta_size = 0;
m_meta_pos = 0;
}
x -= len;
if (x) memcpy(buf, buf + len, x);
}
else if (m_meta_pos + x > m_meta_interval) // block contains meta data somewhere in it, and we're not alreayd reading a block
{
int start_offs = m_meta_interval - m_meta_pos;
int len;
m_meta_size = ((unsigned char *)buf)[start_offs] * 16;
len = min(x - start_offs - 1, m_meta_size);
if (len) memcpy(m_meta_buf, buf + start_offs + 1, len);
m_meta_buf_pos = len;
if (m_meta_buf_pos == m_meta_size) // full read of metadata successful
{
x -= m_meta_size + 1;
if (x > start_offs) memcpy(buf + start_offs, buf + start_offs + 1 + m_meta_size, x - start_offs);
#ifdef WINAMP_PLUGIN
extern void process_metadata(char *buf, int size);
process_metadata(m_meta_buf, m_meta_size);
#endif
//if(metacb) metacb->metaDataReader_onData(m_meta_buf,m_meta_size);
m_meta_buf_pos = 0;
m_meta_pos = -start_offs;
m_meta_size = 0;
}
else
{
x = start_offs; // otherwise, there's only the first block of data
}
}
if (x > 0)
{
m_meta_pos += x;
}
l = x;
} // end of poopie metadata
} // !uvox
#if 0
{
FILE *fh = fopen("c:\\dump.nsv", "ab");
fwrite(buffer, 1, l, fh);
fclose(fh);
}
#endif
return l;
}
return 0;
}
static void parseURL(char *url, char *lp, char *host, int *port, char *req)
{
char *p, *np;
/* if (_strnicmp(url,"http://",4) &&
_strnicmp(url,"icy://",6) &&
_strnicmp(url,"sc://",6) &&
_strnicmp(url,"shoutcast://",12)) return;
*/
np = p = strstr(url, "://");
if (!np) np = (char*)url;
else np += 3;
if (!p) p = (char*)url;
else p += 3;
while (np && *np != '/' && *np) *np++;
if (np && *np)
{
lstrcpynA(req, np, 2048);
*np++ = 0;
}
else strcpy(req, "/");
np = p;
while (np && *np != '@' && *np) np++;
if (np && *np)
{
*np++ = 0;
lstrcpynA(lp, p, 256);
p = np;
}
else lp[0] = 0;
np = p;
while (np && *np != ':' && *np) np++;
if (*np)
{
*np++ = 0;
*port = atoi(np);
}
else *port = 80;
lstrcpynA(host, p, 256);
}
int HTTPReader::getProxyInfo(const char *url, char *out)
{
#ifndef WINAMPX
char INI_FILE[MAX_PATH] = {0};
char *p;
GetModuleFileNameA(NULL, INI_FILE, sizeof(INI_FILE));
p = INI_FILE + strlen(INI_FILE);
while (p >= INI_FILE && *p != '.') p--;
strcpy(++p, "ini");
GetPrivateProfileStringA("Winamp", "proxy", "", out, 8192, INI_FILE);
return !!out[0];
#else
DWORD v = 0;
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE, "Software\\AOL\\Unagi", 0, KEY_READ, &hKey) == ERROR_SUCCESS)
{
DWORD l = 4;
DWORD t;
if (RegQueryValueEx(hKey, "ProxyEnable", NULL, &t, (unsigned char *)&v, &l) == ERROR_SUCCESS && t == REG_DWORD)
{
if ( v != 2 )
{
l = 8192;
if (RegQueryValueEx(hKey, "ProxyServer", NULL, &t, (unsigned char *)out, &l ) != ERROR_SUCCESS || t != REG_SZ)
{
v = 0;
*out = 0;
}
}
else return 0;
}
else v = 0;
out[512 - 1] = 0;
RegCloseKey(hKey);
}
if ( !v && m_useaproxy )
{
char blah[8192] = "";
lstrcpyn(blah, url, 8192);
char plp[512] = {0};
char phost[512] = {0};
int pport = 80;
char pthereq[1024] = {0};
parseURL(blah, plp, phost, &pport, pthereq);
v = ResolvProxyFromURL(url, phost, out);
if ( v < 0 ) v = 0; // error getting proxy
}
if ( v > 0)
{
char prox[1024] = {0};
wsprintf(prox, "PROXY: %s", out);
SendMetadata(prox, 1);
}
return v;
#endif
}
class Win32FileReader : public IDataReader
{
public:
Win32FileReader(HANDLE file) { m_hFile = file; m_eof = 0; m_err = NULL; }
~Win32FileReader() { CloseHandle(m_hFile); }
size_t read(char *buf, size_t len)
{
DWORD ob = 0;
if (!len) return 0;
if (!ReadFile(m_hFile, buf, (DWORD)len, &ob, NULL))
{
m_err = "Error calling ReadFile()!";
return 0;
}
else if (!ob) m_eof = true;
return ob;
}
bool iseof() { return m_eof; }
bool canseek() { return 1; }
int seek(uint64_t newpos)
{
LARGE_INTEGER li;
li.QuadPart = newpos;
li.LowPart = SetFilePointer (m_hFile, li.LowPart, &li.HighPart, SEEK_SET);
if (li.LowPart == INVALID_SET_FILE_POINTER && GetLastError() != NO_ERROR)
{
li.QuadPart = -1;
}
return li.QuadPart== ~0;
}
uint64_t getsize()
{
LARGE_INTEGER position;
position.QuadPart=0;
position.LowPart = GetFileSize(m_hFile, (LPDWORD)&position.HighPart);
if (position.LowPart == INVALID_FILE_SIZE && GetLastError() != NO_ERROR)
return INVALID_FILE_SIZE;
else
return position.QuadPart;
}
char *geterror() { return m_err; }
private:
HANDLE m_hFile;
bool m_eof;
char *m_err;
};
#define VAR_TO_FPOS(fpos, var) (fpos) = (var)
#define FPOS_TO_VAR(fpos, typed, var) (var) = (typed)(fpos)
class FileReader : public IDataReader
{
public:
FileReader(FILE *file) { fp = file; m_err = NULL; }
~FileReader() { fclose(fp); }
size_t read(char *buf, size_t len)
{
size_t ob;
if (!len) return 0;
ob = fread(buf, 1, len, fp);
if (!ob && ferror(fp))
{
m_err = "Error calling fread()!";
return 0;
}
return ob;
}
bool iseof() { return !!feof(fp); }
bool canseek() { return 1; }
int seek(uint64_t newpos)
{
fpos_t pos= newpos;
VAR_TO_FPOS(pos, newpos);
return fsetpos(fp, &pos);
}
unsigned __int64 getsize()
{
struct stat s;
if (fstat(fileno(fp), &s) < 0)
{
m_err = "Error calling fread()!";
return 0;
}
return s.st_size;
}
char *geterror() { return m_err; }
private:
FILE *fp;
char *m_err;
};
IDataReader *CreateReader(const char *url)
{
if (strstr(url, "://")) return new HTTPReader(url);
#ifdef _WIN32
HANDLE hFile = CreateFileA(url, GENERIC_READ, FILE_SHARE_READ | FILE_SHARE_WRITE, NULL,
OPEN_EXISTING, FILE_FLAG_SEQUENTIAL_SCAN, NULL);
if (hFile != INVALID_HANDLE_VALUE)
return new Win32FileReader(hFile);
#else
FILE *fp = fopen(url, "r");
if (fp)
return new FileReader(fp);
#endif
return NULL;
}

View File

@ -0,0 +1,18 @@
//{{NO_DEPENDENCIES}}
// Microsoft Developer Studio generated include file.
// Used by nsvplay.rc
//
#define IDD_ABOUT 101
#define IDB_BITMAP1 102
#define IDC_VERSION 1000
// Next default values for new objects
//
#ifdef APSTUDIO_INVOKED
#ifndef APSTUDIO_READONLY_SYMBOLS
#define _APS_NEXT_RESOURCE_VALUE 103
#define _APS_NEXT_COMMAND_VALUE 40001
#define _APS_NEXT_CONTROL_VALUE 1001
#define _APS_NEXT_SYMED_VALUE 101
#endif
#endif

View File

@ -0,0 +1,215 @@
#include "subtitles.h"
Subtitles::Subtitles(const char *filename)
{
m_frame_based=0;
m_last_sub=-1;
m_font_size_mod=0;
#ifdef SUBTITLES_READER
if(!filename) return;
IDataReader *file_reader=CreateReader(filename);
if(file_reader) {
char *text=NULL;
int textpos=0;
int allocsize=0;
char buf[1024] = {0};
unsigned aborttime=GetTickCount()+20000;
for (;;)
{
int l=file_reader->read(buf,1024);
if (l <= 0)
{
if (file_reader->iseof()) break;
if (file_reader->geterror() || GetTickCount() > aborttime)
{
free(text);
return;
}
Sleep(100);
}
else
{
if (textpos+l+1 >= allocsize)
{
allocsize = textpos+l+1+8192;
text=(char*)realloc(text,allocsize);
}
memcpy(text+textpos,buf,l);
textpos+=l;
}
}
if (text) {
text[textpos]=0;
//fucko: check for case
if(strstr(filename,".srt")) decodeSrtFile(text);
else if(strstr(filename,".sub")) decodeSubFile(text);
free(text);
}
}
delete(file_reader);
#endif
}
#ifdef SUBTITLES_READER
void Subtitles::decodeSrtFile(char *text) {
// parse subtitle file (.srt format)
char *p=text;
//for(int i=0;;i++) {
while(1) {
unsigned int time_start,time_end;
// parse title nb
char *p2=p;
while(*p2 && *p2!='\n') p2++;
*p2++=0;
//if(atoi(p)!=i+1) break;
if(atoi(p)<=0) break;
// parse start time
p=p2;
while(*p2 && *p2!=' ') p2++;
*p2++=0;
time_start=getTimeFromSrtText(p);
// parse "-->"
while(*p2 && *p2!=' ') p2++;
p2++;
// parse end time
p=p2;
while(*p2 && *p2!='\n') p2++;
*p2++=0;
time_end=getTimeFromSrtText(p);
// parse text
p=p2;
while(*p2 && !(*p2=='\r' || *p=='\n')) {
while(*p2 && *p2!='\n') p2++;
p2++;
}
*p2++=0;
//remove trailing CR
{
int l=strlen(p);
if(l) {
if(p[l-1]=='\r' || p[l-1]=='\n') p[l-1]=0;
}
}
m_subs.put(new SubsItem(time_start,time_end,p));
if(*p2=='\n') p2++;
p=p2;
}
m_frame_based=0;
}
unsigned int Subtitles::getTimeFromSrtText(const char *text) {
int hours,mins,secs,mills;
const char *p=text;
hours=atoi(p);
while(*p && *p!=':') p++;
p++;
mins=atoi(p);
while(*p && *p!=':') p++;
p++;
secs=atoi(p);
while(*p && *p!=',') p++;
p++;
mills=atoi(p);
return mills+(secs*1000)+(mins*60000)+(hours*60*60000);
}
void Subtitles::decodeSubFile(char *text)
{
char *p=text;
while(*p=='{') {
int framestart,frameend;
p++;
char *p2=p;
while(*p2 && *p2!='}') p2++;
*p2++=0;
framestart=atoi(p);
p2+=1;
p=p2;
while(*p2 && *p2!='}') p2++;
*p2++=0;
frameend=atoi(p);
p=p2;
while(*p2 && *p2!='\r' && *p2!='\n') {
//replace pipes with CR
if(*p2=='|') *p2='\n';
p2++;
}
*p2++=0;
m_subs.put(new SubsItem(framestart,frameend,p));
if(*p2=='\n') p2++;
p=p2;
}
m_frame_based=1;
}
#endif
SubsItem *Subtitles::getSubtitle(unsigned int time, unsigned int frame)
{
unsigned int ref=m_frame_based?frame:time;
//check with lastsub
if(m_last_sub!=-1) {
SubsItem *item=m_subs.get(m_last_sub);
if(ref>=item->timestart && ref<=item->timeend)
{
item->fontSize=item->origFontSize+m_font_size_mod;
return item;
}
SubsItem *item2=m_subs.get(m_last_sub+1);
if(item2) {
if(ref>=item->timeend && ref<=item2->timestart) return NULL;
if(ref>=item2->timestart && ref<=item2->timeend) {
m_last_sub++;
item2->fontSize=item2->origFontSize+m_font_size_mod;
return item2;
}
}
}
int l= (int)m_subs.getlen();
for(int i=0;i<l;i++) {
SubsItem *item=m_subs.get(i);
if(ref<item->timestart) break;
if(ref>=item->timestart && ref<=item->timeend) {
m_last_sub=i;
item->fontSize=item->origFontSize+m_font_size_mod;
return item;
}
}
m_last_sub=-1;
return NULL;
}
void Subtitles::addSubtitlePacket(SUBTITLE_INFO *sti)
{
m_frame_based=1; //FUCKO: put this in subsitem struct
SubsItem *i=new SubsItem(sti->start_frame,sti->end_frame,sti->utf8_text);
i->xPos=sti->xPos;
i->yPos=sti->yPos;
i->colorBlue=sti->colorBlue;
i->colorGreen=sti->colorGreen;
i->colorRed=sti->colorRed;
i->extraDataSize=sti->extraDataSize;
i->origFontSize=sti->fontSize;
if(sti->extraDataSize) {
i->extraData=malloc(sti->extraDataSize);
memcpy((void *)i->extraData,sti->extraData,sti->extraDataSize);
}
i->muxed_subtitle=1;
m_subs.put(i);
}

View File

@ -0,0 +1,62 @@
#ifndef NSVPLAY_SUBTITLES_H
#define NSVPLAY_SUBTITLES_H
#include "main.h"
#include "../nsvbs.h"
class SubsItem {
public:
SubsItem(unsigned int ptimestart, unsigned int ptimeend, const char *ptext) :
timestart(ptimestart) , timeend(ptimeend) {
text=_strdup(ptext);
xPos=128;
yPos=255;
colorRed=colorGreen=colorBlue=0xff;
extraDataSize=0;
extraData=0;
muxed_subtitle=0;
fontSize=origFontSize=0;
}
~SubsItem() {
free((void*)text);
if(extraDataSize) free((void *)extraData);
}
unsigned int timestart;
unsigned int timeend;
const char *text;
unsigned char xPos, yPos;
unsigned char colorRed, colorGreen, colorBlue;
int extraDataSize;
const void *extraData;
int muxed_subtitle; //so we free it when we seek/display
int fontSize;
int origFontSize;
};
class Subtitles {
public:
Subtitles(const char *filename);
SubsItem *getSubtitle(unsigned int time, unsigned int frame); // time in ms
void addSubtitlePacket(SUBTITLE_INFO *sti);
void setFontSizeModifier(int size) { m_font_size_mod=size; }
private:
void decodeSrtFile(char *text);
unsigned int getTimeFromSrtText(const char *text);
void decodeSubFile(char *text);
ClassList<SubsItem> m_subs;
int m_frame_based;
int m_last_sub;
int m_font_size_mod;
};
#endif

View File

@ -0,0 +1,865 @@
#include "video.h"
#include <multimon.h>
#include "subtitles.h"
#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
DDrawVideoOutput::DDrawVideoOutput() {
lpDD=NULL;
lpddsOverlay=NULL;
lastresizerect.bottom=0;
lastresizerect.top=0;
lastresizerect.left=0;
lastresizerect.right=0;
lpddsPrimary=NULL;
lpddsClipper=NULL;
lpddsSTTemp=NULL;
is_fullscreen=0;
m_parent=NULL;
initing=false;
needchange=0;
m_palette=NULL;
m_lastsubtitle=NULL;
sttmp_w=sttmp_h=0;
subFont=NULL;
m_sub_needremeasure=0;
m_fontsize=0;
memset(&winRect,0,sizeof(winRect));
}
DDrawVideoOutput::~DDrawVideoOutput() {
// LPDIRECTDRAWSURFACE o=lpddsOverlay;
lpddsOverlay=NULL;
// if(o) o->Release();
// if (lpddsSTTemp) lpddsSTTemp->Release();
//if(lpddsPrimary) lpddsPrimary->Release();
// if(lpddsClipper) lpddsClipper->Release();
if (lpDD) lpDD->Release(); // BU added NULL check in response to talkback
if(subFont) DeleteObject(subFont);
if (is_fullscreen) removeFullScreen();
}
void DDrawVideoOutput::drawSubtitle(SubsItem *item)
{
m_lastsubtitle=item;
m_sub_needremeasure=1;
}
int DDrawVideoOutput::create(VideoOutput *parent, int w, int h, unsigned int ptype, int flipit, double aspectratio) {
m_lastsubtitle=NULL;
type=ptype;
width=w;
height=h;
flip=flipit;
m_parent=parent;
initing=true;
HWND hwnd=parent->getHwnd();
if (lpDD) lpDD->Release();
lpDD=NULL;
update_monitor_coords(parent);
if(!m_found_devguid) DirectDrawCreate(NULL,&lpDD,NULL);
else DirectDrawCreate(&m_devguid,&lpDD,NULL);
if(!lpDD) {
initing=false;
return 0;
}
lpDD->SetCooperativeLevel(hwnd,DDSCL_NOWINDOWCHANGES|DDSCL_NORMAL);
DDSURFACEDESC ddsd;
INIT_DIRECTDRAW_STRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL );
HRESULT v=-1;
DDSURFACEDESC DDsd={sizeof(DDsd),};
lpddsPrimary->GetSurfaceDesc(&ddsd);
DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth
DDsd.dwWidth=w;
DDsd.dwHeight=h;
DDsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
if (parent->vid_ddraw) v=lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL);
if(!parent->vid_ddraw || FAILED(v)) {
// fall back to system memory if video mem doesn't work
DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
v=lpDD->CreateSurface(&DDsd, &lpddsOverlay, NULL);
}
if(FAILED(v)) {
// this video card sucks then :)
lpddsOverlay=NULL;
initing=false;
return 0;
}
// get the depth
m_depth=8;
INIT_DIRECTDRAW_STRUCT(m_ddpf);
if(lpddsOverlay->GetPixelFormat(&m_ddpf)>=0) {
m_depth=m_ddpf.dwRGBBitCount;
if (m_depth==16 && m_ddpf.dwGBitMask==0x03e0) m_depth=15;
}
lpDD->CreateClipper(0,&lpddsClipper,NULL);
lpddsClipper->SetHWnd(0,hwnd);
lpddsPrimary->SetClipper(lpddsClipper);
initing=false;
return 1;
}
int DDrawVideoOutput::onPaint(HWND hwnd, HDC hdc) {
return 0;
}
void DDrawVideoOutput::displayFrame(const char *buf, int size, int time) {
DDSURFACEDESC dd={sizeof(dd),};
if (m_parent->vid_vsync) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);
HRESULT result;
if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) {
needchange=1;
return;
}
if(type==NSV_MAKETYPE('Y','V','1','2')) {
const YV12_PLANES *planes=(YV12_PLANES *)buf;
// convert yv12 to rgb
int bytes = m_depth >> 3;
if(m_depth==15) bytes=2;
int i, j, y00, y01, y10, y11, u, v;
unsigned char *pY = (unsigned char *)planes->y.baseAddr;
unsigned char *pU = (unsigned char *)planes->u.baseAddr;
unsigned char *pV = (unsigned char *)planes->v.baseAddr;
unsigned char *pOut = (unsigned char*)dd.lpSurface;
const int rvScale = 91881;
const int guScale = -22553;
const int gvScale = -46801;
const int buScale = 116129;
const int yScale = 65536;
int addOut=dd.lPitch*2-width*bytes;
int yrb=planes->y.rowBytes;
int addL=dd.lPitch;
/* LIMIT: convert a 16.16 fixed-point value to a byte, with clipping. */
#define LIMIT(x) ((x)>0xffffff?0xff: ((x)<=0xffff?0:((x)>>16)))
if(flip) {
pOut+=(dd.lPitch)*(height-1);
addOut=-dd.lPitch*2 - width*bytes;
addL=-addL;
}
for (j = 0; j <= height - 2; j += 2) {
for (i = 0; i <= width - 2; i += 2) {
y00 = *pY;
y01 = *(pY + 1);
y10 = *(pY + yrb);
y11 = *(pY + yrb + 1);
u = (*pU++) - 128;
v = (*pV++) - 128;
{
int r, g, b;
g = guScale * v + gvScale * u;
r = buScale * v;
b = rvScale * u;
y00 *= yScale; y01 *= yScale;
y10 *= yScale; y11 *= yScale;
switch(m_depth) {
case 15:
{
unsigned short *rgb=(unsigned short *)pOut;
rgb[0]=((LIMIT(r+y00)>>3)<<10)|((LIMIT(g+y00)>>3)<<5)|(LIMIT(b+y00)>>3);
rgb[1]=((LIMIT(r+y01)>>3)<<10)|((LIMIT(g+y01)>>3)<<5)|(LIMIT(b+y01)>>3);
rgb+=addL/2;
rgb[0]=((LIMIT(r+y10)>>3)<<10)|((LIMIT(g+y10)>>3)<<5)|(LIMIT(b+y10)>>3);
rgb[1]=((LIMIT(r+y11)>>3)<<10)|((LIMIT(g+y11)>>3)<<5)|(LIMIT(b+y11)>>3);
}
break;
case 16:
{
unsigned short *rgb=(unsigned short *)pOut;
rgb[0]=((LIMIT(r+y00)>>3)<<11)|((LIMIT(g+y00)>>2)<<5)|(LIMIT(b+y00)>>3);
rgb[1]=((LIMIT(r+y01)>>3)<<11)|((LIMIT(g+y01)>>2)<<5)|(LIMIT(b+y01)>>3);
rgb+=addL/2;
rgb[0]=((LIMIT(r+y10)>>3)<<11)|((LIMIT(g+y10)>>2)<<5)|(LIMIT(b+y10)>>3);
rgb[1]=((LIMIT(r+y11)>>3)<<11)|((LIMIT(g+y11)>>2)<<5)|(LIMIT(b+y11)>>3);
}
break;
case 24:
{
unsigned char *rgb=pOut;
/* Write out top two pixels */
rgb[0] = LIMIT(b+y00); rgb[1] = LIMIT(g+y00); rgb[2] = LIMIT(r+y00);
rgb[3] = LIMIT(b+y01); rgb[4] = LIMIT(g+y01); rgb[5] = LIMIT(r+y01);
/* Skip down to next line to write out bottom two pixels */
rgb += addL;
rgb[0] = LIMIT(b+y10); rgb[1] = LIMIT(g+y10); rgb[2] = LIMIT(r+y10);
rgb[3] = LIMIT(b+y11); rgb[4] = LIMIT(g+y11); rgb[5] = LIMIT(r+y11);
}
break;
case 32:
{
unsigned char *rgb=pOut;
/* Write out top two pixels */
rgb[0] = LIMIT(b+y00); rgb[1] = LIMIT(g+y00); rgb[2] = LIMIT(r+y00);
rgb[4] = LIMIT(b+y01); rgb[5] = LIMIT(g+y01); rgb[6] = LIMIT(r+y01);
/* Skip down to next line to write out bottom two pixels */
rgb += addL;
rgb[0] = LIMIT(b+y10); rgb[1] = LIMIT(g+y10); rgb[2] = LIMIT(r+y10);
rgb[4] = LIMIT(b+y11); rgb[5] = LIMIT(g+y11); rgb[6] = LIMIT(r+y11);
}
break;
}
}
pY += 2;
pOut += 2 * bytes;
}
pY += yrb+yrb-width;
pU += planes->u.rowBytes-width/2;
pV += planes->v.rowBytes-width/2;
pOut += addOut;
}
} else if(type==NSV_MAKETYPE('R','G','3','2')) {
//FUCKO: do we need to support 8bits depth?
switch(m_depth) {
case 15:
{ // convert RGB32 -> RGB16 (555)
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width*4,l2=dd.lPitch;
int ladj=l;
if (flip) { a+=l*(height-1); ladj=-ladj; }
for(int i=0;i<height;i++) {
short *dest=(short *)b;
int *src=(int *)a;
for(int j=0;j<width;j++) {
int c=*(src++);
int r=c>>16;
int g=(c>>8) & 0xff;
int b=(c) & 0xff;
*(dest++)=((r>>3)<<10)|((g>>3)<<5)|(b>>3);
}
a+=ladj; b+=l2;
}
}
break;
case 16:
{ // convert RGB32 -> RGB16
//FUCKO: this assumes 565
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width*4,l2=dd.lPitch;
int ladj=l;
if (flip) { a+=l*(height-1); ladj=-ladj; }
for(int i=0;i<height;i++) {
short *dest=(short *)b;
int *src=(int *)a;
for(int j=0;j<width;j++) {
//FUCKO: optimize here
int c=*(src++);
int r=c>>16;
int g=(c>>8) & 0xff;
int b=(c) & 0xff;
*(dest++)=((r>>3)<<11)|((g>>2)<<5)|(b>>3);
}
a+=ladj; b+=l2;
}
}
break;
case 24:
{ // convert RGB32 -> RGB24
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width*4,l2=dd.lPitch;
int ladj=l;
if (flip) { a+=l*(height-1); ladj=-ladj; }
for(int i=0;i<height;i++) {
char *dest=(char *)b;
int *src=(int *)a;
for(int j=0;j<width;j++) {
//FUCKO: optimize here
int c=*(src++);
int r=c>>16;
int g=(c>>8) & 0xff;
int b=(c) & 0xff;
*dest++=b;
*dest++=g;
*dest++=r;
}
a+=ladj; b+=l2;
}
}
break;
case 32:
{ // straight RGB32 copy
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width*4,l2=dd.lPitch;
int ladj=l;
if (flip) { a+=l*(height-1); ladj=-ladj; }
for(int i=0;i<height;i++) {
memcpy(b,a,l);
a+=ladj; b+=l2;
}
}
break;
}
} else if(type==NSV_MAKETYPE('Y','U','Y','2') || type==NSV_MAKETYPE('U','Y','V','Y')) {
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width*2,l2=dd.lPitch;
if(flip) {
b+=(height-1)*l2;
l2=-l2;
}
switch(m_depth) {
case 15:
{
// yuy2->rgb16 (555) conversion
unsigned char *src=(unsigned char *)buf;
unsigned short *dst=(unsigned short *)dd.lpSurface;
int line, col;//, linewidth;
int y, yy;
int u, v;
int vr, ug, vg, ub;
unsigned char *py, *pu, *pv;
//linewidth = width - (width >> 1);
py = src;
pu = src + 1;
pv = src + 3;
int pitchadd=dd.lPitch/2-width;
for (line = 0; line < height; line++) {
for (col = 0; col < width; col++) {
#undef LIMIT
#define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
y = *py;
yy = y << 8;
u = *pu - 128;
ug = 88 * u;
ub = 454 * u;
v = *pv - 128;
vg = 183 * v;
vr = 359 * v;
unsigned char b=LIMIT(yy + ub );
unsigned char g=LIMIT(yy - ug - vg);
unsigned char r=LIMIT(yy + vr);
*(dst++)=((r>>3)<<10)|((g>>3)<<5)|(b>>3);
py += 2;
if ( (col & 1) == 1) {
pu += 4; // skip yvy every second y
pv += 4; // skip yuy every second y
}
} // ..for col
dst+=pitchadd;
} /* ..for line */
}
break;
case 16:
{
// yuy2->rgb16 conversion
//FUCKO: only supports 565
unsigned char *src=(unsigned char *)buf;
unsigned short *dst=(unsigned short *)dd.lpSurface;
int line, col;//, linewidth;
int y, yy;
int u, v;
int vr, ug, vg, ub;
unsigned char *py, *pu, *pv;
//linewidth = width - (width >> 1);
py = src;
pu = src + 1;
pv = src + 3;
int pitchadd=dd.lPitch/2-width;
for (line = 0; line < height; line++) {
for (col = 0; col < width; col++) {
#undef LIMIT
#define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
y = *py;
yy = y << 8;
u = *pu - 128;
ug = 88 * u;
ub = 454 * u;
v = *pv - 128;
vg = 183 * v;
vr = 359 * v;
unsigned char b=LIMIT(yy + ub );
unsigned char g=LIMIT(yy - ug - vg);
unsigned char r=LIMIT(yy + vr);
*(dst++)=((r>>3)<<11)|((g>>2)<<5)|(b>>3);
py += 2;
if ( (col & 1) ) {
pu += 4; // skip yvy every second y
pv += 4; // skip yuy every second y
}
} // ..for col
dst+=pitchadd;
} /* ..for line */ }
break;
case 24:
{
// yuy2->rgb24 conversion
unsigned char *src=(unsigned char *)buf;
unsigned char *dst=(unsigned char *)dd.lpSurface;
int line, col;//, linewidth;
int y, yy;
int u, v;
int vr, ug, vg, ub;
unsigned char *py, *pu, *pv;
//linewidth = width - (width >> 1);
py = src;
pu = src + 1;
pv = src + 3;
int pitchadd=dd.lPitch-(width*3);
for (line = 0; line < height; line++) {
for (col = 0; col < width; col++) {
#undef LIMIT
#define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
y = *py;
yy = y << 8;
u = *pu - 128;
ug = 88 * u;
ub = 454 * u;
v = *pv - 128;
vg = 183 * v;
vr = 359 * v;
*(dst++)=LIMIT(yy + ub );
*(dst++)=LIMIT(yy - ug - vg);
*(dst++)=LIMIT(yy + vr);
py += 2;
if ( (col & 1) == 1) {
pu += 4; // skip yvy every second y
pv += 4; // skip yuy every second y
}
} // ..for col
dst+=pitchadd;
} /* ..for line */ }
break;
case 32:
{
// yuy2->rgb32 conversion
unsigned char *src=(unsigned char *)buf;
unsigned char *dst=(unsigned char *)dd.lpSurface;
int line, col;//, linewidth;
int y, yy;
int u, v;
int vr, ug, vg, ub;
unsigned char *py, *pu, *pv;
//linewidth = width - (width >> 1);
py = src;
pu = src + 1;
pv = src + 3;
int pitchadd=dd.lPitch-(width*4);
for (line = 0; line < height; line++) {
for (col = 0; col < width; col++) {
#undef LIMIT
#define LIMIT(x) ( (x) > 0xffff ? 0xff : ( (x) <= 0xff ? 0 : ( (x) >> 8 ) ) )
y = *py;
yy = y << 8;
u = *pu - 128;
ug = 88 * u;
ub = 454 * u;
v = *pv - 128;
vg = 183 * v;
vr = 359 * v;
*dst++ = LIMIT(yy + ub ); // b
*dst++ = LIMIT(yy - ug - vg); // g
*dst++ = LIMIT(yy + vr); // r
dst++;
py += 2;
if ( (col & 1) == 1) {
pu += 4; // skip yvy every second y
pv += 4; // skip yuy every second y
}
} // ..for col
dst+=pitchadd;
} /* ..for line */
}
break;
}
} else if(type==NSV_MAKETYPE('R','G','2','4')) {
//FUCKO: only ->RGB32 conversion supported
switch(m_depth) {
case 32:
{
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width,l2=dd.lPitch;
int ladj=l*3;
if (flip) { a+=(l*3)*(height-1); ladj=-(ladj+l*3); }
l2-=l*4;
for(int i=0;i<height;i++) {
//memcpy(b,a,l);
for(int j=0;j<l;j++) {
b[0]=a[0];
b[1]=a[1];
b[2]=a[2];
b+=4; a+=3;
}
a+=ladj; b+=l2;
}
}
break;
}
} else if(type==NSV_MAKETYPE('R','G','B','8') && m_palette) {
unsigned char *d=(unsigned char *)dd.lpSurface;
int pitch=dd.lPitch;
unsigned char *src=(unsigned char *)buf;
int newwidth=(width+3)&0xfffc;
src+=newwidth*height-1;
for(int j=0;j<height;j++) {
switch(m_depth) {
case 15:
case 16:
{
unsigned short *dest=(unsigned short *)d;
for(int i=0;i<newwidth;i++) {
unsigned char c=src[-newwidth+1+i];
RGBQUAD *rgb=&m_palette[c];
switch(m_depth) {
case 15: *(dest++)=((rgb->rgbRed>>3)<<10)|((rgb->rgbGreen>>3)<<5)|(rgb->rgbBlue>>3); break;
case 16: *(dest++)=((rgb->rgbRed>>3)<<11)|((rgb->rgbGreen>>2)<<5)|(rgb->rgbBlue>>3); break;
}
}
}
break;
case 24:
case 32:
{
unsigned char *dest=d;
for(int i=0;i<newwidth;i++) {
unsigned char c=src[-newwidth+1+i];
RGBQUAD *rgb=&m_palette[c];
*dest++=rgb->rgbBlue;
*dest++=rgb->rgbGreen;
*dest++=rgb->rgbRed;
if(m_depth==32) dest++;
}
}
break;
}
d+=pitch;
src-=newwidth;
}
}
lpddsOverlay->Unlock(&dd);
RECT r;
HWND hwnd=m_parent->getHwnd();
if (!IsWindow(hwnd)) return;
if(GetParent(hwnd)) hwnd=GetParent(hwnd);
GetClientRect(hwnd,&r);
RECT fullr=r;
m_parent->adjustAspect(r);
if (r.left != lastresizerect.left || r.right != lastresizerect.right || r.top != lastresizerect.top ||
r.bottom != lastresizerect.bottom)
{
if (r.left != 0)
{
RECT tmp={0,0,r.left,fullr.bottom};
InvalidateRect(hwnd,&tmp,TRUE);
}
if (r.right != fullr.right)
{
RECT tmp={r.right,0,fullr.right,fullr.bottom};
InvalidateRect(hwnd,&tmp,TRUE);
}
if (r.top != 0)
{
RECT tmp={r.left,0,r.right,r.top};
InvalidateRect(hwnd,&tmp,TRUE);
}
if (r.bottom != fullr.bottom)
{
RECT tmp={r.left,r.bottom,r.right,fullr.bottom};
InvalidateRect(hwnd,&tmp,TRUE);
}
lastresizerect=r;
}
ClientToScreen(hwnd,(LPPOINT)&r);
ClientToScreen(hwnd,((LPPOINT)&r) + 1);
// transform coords from windows desktop coords (where 0,0==upper-left corner of box encompassing all monitors)
// to the coords for the monitor we're displaying on:
r.left-=m_mon_x;
r.right-=m_mon_x;
r.top-=m_mon_y;
r.bottom-=m_mon_y;
HDC hdc = NULL;
HDC inhdc = NULL;
RECT srcrect;
RECT *pSrcRect = NULL;
if (m_parent->osdShowing() && m_parent->osdReady())
{
// squish image upward to make room for the OSD.
int vert_margin = ((fullr.bottom-fullr.top) - (r.bottom-r.top)) / 2;
int pixels_to_clip = max(0, m_parent->getOSDbarHeight() - vert_margin);
// adjust source rectangle:
int src_y0 = (int)(height*pixels_to_clip/(float)(r.bottom-r.top) + 0.5f);
int src_y1 = height - src_y0;
SetRect(&srcrect, 0, SHOW_STREAM_TITLE_AT_TOP ? src_y0 : 0, width, src_y1);
pSrcRect = &srcrect;
// adjust destination rectangle:
r.bottom -= pixels_to_clip;
#if (SHOW_STREAM_TITLE_AT_TOP)
r.top += pixels_to_clip;
#endif
}
int needst=0;
SubsItem *mlst=m_lastsubtitle;
if (mlst)
{
int curw=r.right-r.left, curh=r.bottom-r.top;
if (!lpddsSTTemp || sttmp_w != curw || sttmp_h != curh)
{
if (lpddsSTTemp) lpddsSTTemp->Release();
lpddsSTTemp=0;
HRESULT v=-1;
DDSURFACEDESC DDsd={sizeof(DDsd),};
DDSURFACEDESC ddsd;
INIT_DIRECTDRAW_STRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
lpddsPrimary->GetSurfaceDesc(&ddsd);
DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth
DDsd.dwWidth=sttmp_w=curw;
DDsd.dwHeight=sttmp_h=curh;
DDsd.ddsCaps.dwCaps = DDSCAPS_VIDEOMEMORY;
if (m_parent->vid_ddraw) v=lpDD->CreateSurface(&DDsd, &lpddsSTTemp, NULL);
if (!m_parent->vid_ddraw || FAILED(v)) {
// fall back to system memory if video mem doesn't work
DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
v=lpDD->CreateSurface(&DDsd, &lpddsSTTemp, NULL);
}
m_sub_needremeasure=1;
}
if (lpddsSTTemp) needst=1;
}
if (needst)
{
HDC tmpdc=NULL;
if (!m_parent->vid_ddraw || lpddsSTTemp->Blt(NULL,lpddsOverlay,NULL,DDBLT_WAIT,0) != DD_OK) {
// as a last resort, BitBlt().
HDC tmpdc2;
if (lpddsOverlay->GetDC(&tmpdc2)==DD_OK) {
if (lpddsSTTemp->GetDC(&tmpdc)==DD_OK) {
BitBlt(tmpdc,0,0,sttmp_w,sttmp_h,tmpdc2,0,0,SRCCOPY);
}
}
}
if (tmpdc||lpddsSTTemp->GetDC(&tmpdc)==DD_OK)
{
int m_lastsubxp=mlst->xPos;
int m_lastsubyp=mlst->yPos;
RECT oldwinRect=winRect;
GetClientRect(hwnd,&winRect);
if(!subFont || ((winRect.bottom-winRect.top)!=(oldwinRect.bottom-oldwinRect.top)) || m_fontsize!=mlst->fontSize) {
if(subFont) DeleteObject(subFont);
m_fontsize=mlst->fontSize;
subFont=CreateFont(14+m_fontsize+18*(winRect.bottom-winRect.top)/768,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
}
HWND hwnd=m_parent->getHwnd();
HGDIOBJ oldobj=SelectObject(tmpdc,subFont);
int centerflags=0;
if (m_lastsubxp < 127) centerflags |= DT_LEFT;
else if (m_lastsubxp > 127) centerflags |= DT_RIGHT;
else centerflags |= DT_CENTER;
if (m_lastsubyp < 127) centerflags |= DT_TOP;
else if (m_lastsubyp > 127) centerflags |= DT_BOTTOM;
if (m_sub_needremeasure)
{
subRect=r;
subRect.bottom-=subRect.top;
subRect.right -=subRect.left;
subRect.top=subRect.left=0;
SIZE s;
GetTextExtentPoint32(tmpdc,mlst->text,strlen(mlst->text),&s);
// calcul for multiline text
const char *p=mlst->text;
int n=0;
while(*p!=0) if(*p++=='\n') n++;
if(n) s.cy*=(n+1);
if (m_lastsubxp > 127) // towards the right
{
subRect.right -= ((subRect.right-subRect.left) * (255-m_lastsubxp)) / 256;
}
else if (m_lastsubxp < 127)
{
subRect.left += ((subRect.right-subRect.left) * m_lastsubxp) / 256;
}
subRect.top += ((subRect.bottom-s.cy-subRect.top) * m_lastsubyp)/255;
subRect.bottom=subRect.top + s.cy;
}
SetBkMode(tmpdc,TRANSPARENT);
// draw outline
SetTextColor(tmpdc,RGB(0,0,0));
int y=1;
int x=1;
RECT r2={subRect.left+x,subRect.top+y,subRect.right+x,subRect.bottom+y};
DrawText(tmpdc,mlst->text,-1,&r2,centerflags|DT_NOCLIP|DT_NOPREFIX);
// draw text
SetTextColor(tmpdc,RGB(mlst->colorRed,mlst->colorGreen,mlst->colorBlue));
DrawText(tmpdc,mlst->text,-1,&subRect,centerflags|DT_NOCLIP|DT_NOPREFIX);
SelectObject(tmpdc,oldobj);
lpddsSTTemp->ReleaseDC(tmpdc);
}
if (!m_parent->vid_ddraw || lpddsPrimary->Blt(&r,lpddsSTTemp,pSrcRect,DDBLT_WAIT,0) != DD_OK) {
// as a last resort, BitBlt().
if (lpddsSTTemp->GetDC(&inhdc)==DD_OK) {
if (lpddsPrimary->GetDC(&hdc)==DD_OK) {
int src_w = width;
int src_h = pSrcRect ? (pSrcRect->bottom - pSrcRect->top) : height;
if (r.right-r.left == src_w && r.bottom-r.top == src_h)
BitBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,SRCCOPY);
else
StretchBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,src_w,src_h,SRCCOPY);
}
}
}
}
else
{
if (!m_parent->vid_ddraw || lpddsPrimary->Blt(&r,lpddsOverlay,pSrcRect,DDBLT_WAIT,0) != DD_OK) {
// as a last resort, BitBlt().
if (lpddsOverlay->GetDC(&inhdc)==DD_OK) {
if (lpddsPrimary->GetDC(&hdc)==DD_OK) {
int src_w = width;
int src_h = pSrcRect ? (pSrcRect->bottom - pSrcRect->top) : height;
if (r.right-r.left == src_w && r.bottom-r.top == src_h)
BitBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,SRCCOPY);
else
StretchBlt(hdc,r.left,r.top,r.right-r.left,r.bottom-r.top,inhdc,0,0,src_w,src_h,SRCCOPY);
}
}
}
}
#if 0 //faster style
if (m_parent->osdShowing())
{
if (hdc || lpddsPrimary->GetDC(&hdc)==DD_OK)
m_parent->drawOSD(hdc, &r);
}
#endif
if (hdc) { lpddsPrimary->ReleaseDC(hdc); hdc = NULL; }
if (inhdc) { lpddsOverlay->ReleaseDC(inhdc); inhdc = NULL; }
#if 1 // safer style
if (m_parent->osdShowing())
{
HWND h=m_parent->getHwnd();
hdc=GetDC(h);
m_parent->drawOSD(hdc, &r);
ReleaseDC(h,hdc);
}
#endif
}
void DDrawVideoOutput::goFullScreen() {
}
void DDrawVideoOutput::removeFullScreen() {
}
void DDrawVideoOutput::timerCallback() {
}
int DDrawVideoOutput::showOSD() {
return 1;
}
void DDrawVideoOutput::hideOSD() {
// repaint the client area, to black, where there is no video
// (otherwise the OSD might be left painted there)
RECT r;
HWND hwnd=m_parent->getHwnd();
if(GetParent(hwnd)) hwnd=GetParent(hwnd);
GetClientRect(hwnd,&r);
HDC hdc = GetDC(hwnd);
if (hdc) {
HGDIOBJ oldobj1=SelectObject(hdc,CreateSolidBrush(RGB(0,0,0)));
HGDIOBJ oldobj2=SelectObject(hdc,CreatePen(PS_SOLID,0,RGB(0,0,0)));
int margin = ((r.bottom - r.top) - (lastresizerect.bottom - lastresizerect.top) + 1) / 2;
Rectangle(hdc,r.left,r.top,r.right,r.top + margin);
Rectangle(hdc,r.left,r.bottom - margin,r.right,r.bottom);
margin = ((r.right - r.left) - (lastresizerect.right - lastresizerect.left) + 1) / 2;
Rectangle(hdc,r.left,r.top,r.left + margin,r.bottom);
Rectangle(hdc,r.right - margin,r.top,r.right,r.bottom);
DeleteObject(SelectObject(hdc,oldobj2));
DeleteObject(SelectObject(hdc,oldobj1));
ReleaseDC(hwnd, hdc);
}
}
void DDrawVideoOutput::resetSubtitle()
{
m_lastsubtitle=0;
}

View File

@ -0,0 +1,63 @@
#ifndef _VIDEO_DDRAW_H
#define _VIDEO_DDRAW_H
#include <ddraw.h>
#include "video.h"
class SubsItem;
class DDrawVideoOutput : public VideoOutputChild {
public:
DDrawVideoOutput();
virtual ~DDrawVideoOutput();
int create(VideoOutput *parent, int w, int h, unsigned int type, int flipit, double aspectratio); //return 1 if ok
int needChange() { return needchange; }
int onPaint(HWND hwnd, HDC hdc);
void displayFrame(const char *buf, int size, int time);
void goFullScreen();
void removeFullScreen();
void timerCallback();
void setPalette(RGBQUAD *pal) { m_palette=pal; }
int showOSD();
void hideOSD();
void drawSubtitle(SubsItem *item);
void resetSubtitle();
private:
int width, height, flip;
int needchange;
unsigned int type;
VideoOutput *m_parent;
LPDIRECTDRAW lpDD;
LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary, lpddsSTTemp;
int sttmp_w, sttmp_h;
DDCAPS capsDrv;
unsigned int uDestSizeAlign, uSrcSizeAlign;
DWORD dwUpdateFlags;
RECT rs,rd;
RECT lastresizerect;
bool initing;
int is_fullscreen;
LPDIRECTDRAWCLIPPER lpddsClipper;
DDPIXELFORMAT m_ddpf;
int m_depth;
RGBQUAD *m_palette;
HFONT subFont;
RECT subRect;
SubsItem *m_lastsubtitle;
int m_sub_needremeasure;
RECT winRect;
int m_fontsize;
};
#endif

View File

@ -0,0 +1,654 @@
#include "video.h"
#include <multimon.h>
#include "subtitles.h"
#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
#define OV_COL_R 16
#define OV_COL_G 0
#define OV_COL_B 16
OverlayVideoOutput::OverlayVideoOutput() {
lpDD=NULL;
lpddsOverlay=NULL;
lpddsPrimary=NULL;
is_fullscreen=0;
yuy2_output=uyvy_output=0;
m_parent=NULL;
initing=false;
needchange=0;
memset(&m_oldrd,0,sizeof(m_oldrd));
memset(&winRect,0,sizeof(winRect));
subFont=NULL;
m_fontsize=0;
resetSubtitle();
}
OverlayVideoOutput::~OverlayVideoOutput() {
if(is_fullscreen) removeFullScreen();
LPDIRECTDRAWSURFACE o=lpddsOverlay;
lpddsOverlay=NULL;
if(o) o->Release();
if(lpddsPrimary) lpddsPrimary->Release();
if (lpDD) lpDD->Release(); // BU added NULL check in response to talkback
if(subFont) DeleteObject(subFont);
}
static DWORD DD_ColorMatch(LPDIRECTDRAWSURFACE pdds, COLORREF rgb)
{
COLORREF rgbT;
HDC hdc;
DWORD dw = CLR_INVALID;
DDSURFACEDESC ddsd;
HRESULT hres;
//
// use GDI SetPixel to color match for us
//
if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
{
rgbT = GetPixel(hdc, 0, 0); // save current pixel value
SetPixel(hdc, 0, 0, rgb); // set our value
pdds->ReleaseDC(hdc);
}
//
// now lock the surface so we can read back the converted color
//
ddsd.dwSize = sizeof(ddsd);
while ((hres = pdds->Lock(NULL, &ddsd, 0, NULL)) ==
DDERR_WASSTILLDRAWING)
;
if (hres == DD_OK)
{
dw = *(DWORD *)ddsd.lpSurface; // get DWORD
if(ddsd.ddpfPixelFormat.dwRGBBitCount<32)
dw &= (1 << ddsd.ddpfPixelFormat.dwRGBBitCount) - 1; // mask it to bpp
pdds->Unlock(NULL);
}
//
// now put the color that was there back.
//
if (rgb != CLR_INVALID && pdds->GetDC(&hdc) == DD_OK)
{
SetPixel(hdc, 0, 0, rgbT);
pdds->ReleaseDC(hdc);
}
return dw;
}
int OverlayVideoOutput::create(VideoOutput *parent, int w, int h, unsigned int ptype, int flipit, double aspectratio) {
type=ptype;
width=w;
height=h;
flip=flipit;
m_parent=parent;
initing=true;
HWND hwnd=parent->getHwnd();
if (lpDD) lpDD->Release();
lpDD=NULL;
update_monitor_coords(parent);
if(!m_found_devguid) DirectDrawCreate(NULL,&lpDD,NULL);
else DirectDrawCreate(&m_devguid,&lpDD,NULL);
if(!lpDD) {
initing=false;
return 0;
}
lpDD->SetCooperativeLevel(hwnd,DDSCL_NOWINDOWCHANGES|DDSCL_NORMAL);
DDSURFACEDESC ddsd;
INIT_DIRECTDRAW_STRUCT(ddsd);
ddsd.dwFlags = DDSD_CAPS;
ddsd.ddsCaps.dwCaps = DDSCAPS_PRIMARYSURFACE;
HRESULT ddrval = lpDD->CreateSurface(&ddsd, &lpddsPrimary, NULL );
// init overlay
DDSURFACEDESC ddsdOverlay;
INIT_DIRECTDRAW_STRUCT(ddsdOverlay);
ddsdOverlay.ddsCaps.dwCaps=DDSCAPS_OVERLAY | DDSCAPS_VIDEOMEMORY;
ddsdOverlay.dwFlags= DDSD_CAPS|DDSD_HEIGHT|DDSD_WIDTH|DDSD_PIXELFORMAT|DDSD_PITCH;
ddsdOverlay.dwWidth=w;
ddsdOverlay.dwHeight=h;
ddsdOverlay.lPitch=w*4;
ddsdOverlay.dwBackBufferCount=0;
DDPIXELFORMAT pf[]=
{
{sizeof(DDPIXELFORMAT),DDPF_FOURCC,MAKEFOURCC('Y','U','Y','2'),0,0,0,0,0},
{sizeof(DDPIXELFORMAT), DDPF_FOURCC,MAKEFOURCC('U','Y','V','Y'),0,0,0,0,0}, // UYVY
{sizeof(DDPIXELFORMAT),DDPF_FOURCC,MAKEFOURCC('Y','V','1','2'),0,0,0,0,0},
};
int tab[5];
if(type==NSV_MAKETYPE('Y','U','Y','2')) {
tab[0]=0; // default is YUY2
tab[1]=1;
tab[2]=-1;
} else if(type==NSV_MAKETYPE('U','Y','V','Y')) {
tab[0]=1; // make UYVY default
tab[1]=0;
tab[2]=-1;
} else if(type==NSV_MAKETYPE('Y','V','1','2')) {
/*tab[0]=2;
tab[1]=0;
tab[2]=1;
tab[3]=-1;*/
//CT> Make YUY2 default too, cause YV12 is borked on some ATI cards/drivers :(
tab[0]=0;
tab[1]=1;
tab[2]=-1;
} else {
tab[0]=-1; // default is RGB
}
int x=4096;
HRESULT v=-1;
for (x = 0; x < sizeof(tab)/sizeof(tab[0]) && tab[x]>=0; x ++) {
ddsdOverlay.ddpfPixelFormat=pf[tab[x]];
v=lpDD->CreateSurface(&ddsdOverlay, &lpddsOverlay, NULL);
if (!FAILED(v)) break;
}
if(FAILED(v)||x>=sizeof(tab)/sizeof(tab[0])||tab[x]<0) {
initing=false;
return 0;
}
yuy2_output = (tab[x] == 0);
uyvy_output = (tab[x] == 1);
INIT_DIRECTDRAW_STRUCT(capsDrv);
ddrval = lpDD->GetCaps(&capsDrv, NULL);
uDestSizeAlign = capsDrv.dwAlignSizeDest;
uSrcSizeAlign = capsDrv.dwAlignSizeSrc;
dwUpdateFlags = DDOVER_SHOW | DDOVER_KEYDESTOVERRIDE;
DEVMODE d;
d.dmSize=sizeof(d);
d.dmDriverExtra=0;
EnumDisplaySettings(NULL, ENUM_CURRENT_SETTINGS, &d);
int rv=OV_COL_R, gv=OV_COL_G, bv=OV_COL_B;
INIT_DIRECTDRAW_STRUCT(ovfx);
ovfx.dwDDFX=0;
switch(d.dmBitsPerPel) {
case 16:
ovfx.dckDestColorkey.dwColorSpaceLowValue=((rv>>3) << 11) | ((gv>>2) << 5) | (bv>>3);
break;
case 15:
ovfx.dckDestColorkey.dwColorSpaceLowValue=((rv>>3) << 10) | ((gv>>3) << 5) | (bv>>3);
break;
case 24: case 32:
ovfx.dckDestColorkey.dwColorSpaceLowValue=(rv << 16) | (gv << 8) | bv;
break;
}
//try to get the correct bit depth thru directdraw (for fucked up 16 bits displays for ie.)
{
DDSURFACEDESC DDsd={sizeof(DDsd),};
lpddsPrimary->GetSurfaceDesc(&ddsd);
DDsd.dwFlags = DDSD_CAPS|DDSD_WIDTH|DDSD_HEIGHT; //create the surface at screen depth
DDsd.dwWidth=8;
DDsd.dwHeight=8;
DDsd.ddsCaps.dwCaps = DDSCAPS_SYSTEMMEMORY;
LPDIRECTDRAWSURFACE tempsurf;
if(lpDD->CreateSurface(&DDsd, &tempsurf, NULL)==DD_OK)
{
int res=DD_ColorMatch(tempsurf, RGB(rv,gv,bv));
if(res!=CLR_INVALID) ovfx.dckDestColorkey.dwColorSpaceLowValue=res;
tempsurf->Release();
}
}
ovfx.dckDestColorkey.dwColorSpaceHighValue=ovfx.dckDestColorkey.dwColorSpaceLowValue;
getRects(&rs,&rd);
if(FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) {
initing=false;
return 0;
}
initing=false;
DDSURFACEDESC dd={sizeof(dd),};
if (lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL) != DD_OK) return 0;
unsigned char *o=(unsigned char*)dd.lpSurface;
if (uyvy_output||yuy2_output)
{
int x=dd.lPitch*height/2;
while (x--)
{
if (uyvy_output)
{
*o++=128;
*o++=0;
}
else
{
*o++=0;
*o++=-128;
}
}
}
else
{
memset(o,0,dd.lPitch*height); o+=dd.lPitch*height;
memset(o,128,dd.lPitch*height/2);
}
lpddsOverlay->Unlock(&dd);
InvalidateRect(hwnd,NULL,TRUE);
return 1;
}
void OverlayVideoOutput::getRects(RECT *drs, RECT *drd) {
HWND hwnd=m_parent->getHwnd();
if(GetParent(hwnd)) hwnd=GetParent(hwnd);
RECT rd,rs;
GetClientRect(hwnd,&rd);
ClientToScreen(hwnd,(LPPOINT)&rd);
ClientToScreen(hwnd,((LPPOINT)&rd) + 1);
m_parent->adjustAspect(rd);
rd.left-=m_mon_x;
rd.right-=m_mon_x;
rd.top-=m_mon_y;
rd.bottom-=m_mon_y;
memset(&rs,0,sizeof(rs));
rs.right=width;
rs.bottom=height;
//resize overlay for off-screen
RECT rfull;
//m_parent->getViewport(&rfull,NULL,1); //FUCKO: assume monitor 0
m_parent->getViewport(&rfull,hwnd,1); //FUCKO: okay to use this hwnd? (fixes multimon! -RG)
if(rd.right>rfull.right) {
int diff=rd.right-rfull.right;
float sc=(float)(width)/(float)(rd.right-rd.left);
rd.right=rfull.right;
rs.right=width-(int)(diff*sc);
}
if(rd.left<rfull.left) {
int diff=rfull.left-rd.left;
float sc=(float)(width)/(float)(rd.right-rd.left);
rd.left=rfull.left;
rs.left=(int)(diff*sc);
}
if(rd.bottom>rfull.bottom) {
int diff=rd.bottom-rfull.bottom;
float sc=(float)(height)/(float)(rd.bottom-rd.top);
rd.bottom=rfull.bottom;
rs.bottom=height-(int)(diff*sc);
}
if(rd.top<rfull.top) {
int diff=rfull.top-rd.top;
float sc=(float)(height)/(float)(rd.bottom-rd.top);
rd.top=rfull.top;
rs.top=(int)(diff*sc);
}
if (capsDrv.dwCaps & DDCAPS_ALIGNSIZESRC && uDestSizeAlign) {
rs.left = (int)((rs.left+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
rs.right = (int)((rs.right+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
}
if (capsDrv.dwCaps & DDCAPS_ALIGNSIZEDEST && uDestSizeAlign) {
rd.left = (int)((rd.left+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
rd.right = (int)((rd.right+uDestSizeAlign-1)/uDestSizeAlign)*uDestSizeAlign;
}
*drd=rd;
*drs=rs;
}
void OverlayVideoOutput::timerCallback() {
if(!m_parent) return;
RECT rd,rs;
getRects(&rs,&rd);
if(memcmp(&m_oldrd,&rd,sizeof(RECT))) {
m_oldrd=rd;
if(!initing && lpddsOverlay)
if(FAILED(lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx))) {
needchange=1;
}
}
}
int OverlayVideoOutput::onPaint(HWND hwnd, HDC hdc) {
if(!m_parent) return 0;
PAINTSTRUCT p;
BeginPaint(hwnd,&p);
RECT r;
GetClientRect(hwnd,&r);
LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
HBRUSH br=CreateBrushIndirect(&lb);
FillRect(p.hdc,&r,br);
DeleteObject(br);
if (curSubtitle)
{
int m_lastsubxp=curSubtitle->xPos;
int m_lastsubyp=curSubtitle->yPos;
HDC out=p.hdc;
HGDIOBJ oldobj=SelectObject(out,subFont);
SetBkMode(out,TRANSPARENT);
int centerflags=0;
if (m_lastsubxp < 127) centerflags |= DT_LEFT;
else if (m_lastsubxp > 127) centerflags |= DT_RIGHT;
else centerflags |= DT_CENTER;
if (m_lastsubyp < 127) centerflags |= DT_TOP;
else if (m_lastsubyp > 127) centerflags |= DT_BOTTOM;
// draw outline
SetTextColor(out,RGB(0,0,0));
for (int y = -1; y < 2; y++)
for (int x = -1; x < 2; x++)
{
if(!y && !x) continue;
RECT r2={subRect.left+x,subRect.top+y,subRect.right+x,subRect.bottom+y};
DrawText(out,curSubtitle->text,-1,&r2,centerflags|DT_NOCLIP|DT_NOPREFIX);
}
// draw text
SetTextColor(out,RGB(curSubtitle->colorRed,curSubtitle->colorGreen,curSubtitle->colorBlue));
DrawText(out,curSubtitle->text,-1,&subRect,centerflags|DT_NOCLIP|DT_NOPREFIX);
SelectObject(out,oldobj);
}
EndPaint(hwnd,&p);
return 1;
}
void OverlayVideoOutput::displayFrame(const char *buf, int size, int time) {
if(!m_parent) return;
DDSURFACEDESC dd={sizeof(dd),};
if (m_parent->vid_vsync) lpDD->WaitForVerticalBlank(DDWAITVB_BLOCKBEGIN,0);
HRESULT result;
if ((result=lpddsOverlay->Lock(NULL,&dd,DDLOCK_WAIT,NULL)) != DD_OK) {
//CT>FUCKO:reenable me (ctrl+alt+del on win2k)
//if(result==DDERR_SURFACELOST) width=-1; //will try to recreate the surface in the next processData() call
return;
}
if(type==NSV_MAKETYPE('Y','V','1','2')) {
const YV12_PLANES *planes=(YV12_PLANES *)buf;
if (uyvy_output||yuy2_output) { // YV12planar->UYVY or YUY2
unsigned char *o=(unsigned char*)dd.lpSurface;
const unsigned char *yi=planes->y.baseAddr;
const unsigned char *ui=planes->u.baseAddr;
const unsigned char *vi=planes->v.baseAddr;
int y=height;
if (flip) o+=dd.lPitch*(height-1);
while (y>0) {
int x=width;
unsigned char *oo=o;
if (uyvy_output) while (x>0) {
o[0]=*ui++; o[1]=*yi++; o[2]=*vi++; o[3]=*yi++;
o+=4; x-=2;
}
else while (x>0) {
o[0]=*yi++; o[1]=*ui++; o[2]=*yi++; o[3]=*vi++;
o+=4; x-=2;
}
ui-=width/2;
vi-=width/2;
yi+=planes->y.rowBytes-width;
x=width;
if (flip) o=oo-dd.lPitch;
else o+=dd.lPitch-width*2;
oo=o;
if (uyvy_output) while (x>0) {
o[0]=*ui++; o[1]=*yi++; o[2]=*vi++; o[3]=*yi++;
o+=4; x-=2;
} else while (x>0) {
o[0]=*yi++; o[1]=*ui++; o[2]=*yi++; o[3]=*vi++;
o+=4; x-=2;
}
if (flip) o=oo-dd.lPitch;
else o+=dd.lPitch-width*2;
ui+=planes->u.rowBytes-(width/2);
vi+=planes->v.rowBytes-(width/2);
yi+=planes->y.rowBytes-width;
y-=2;
}
} else { // woo native YV12 copy
int f=!!flip;
char *o=(char*)dd.lpSurface+(f*height*dd.lPitch);
const char *i=(const char*)planes->y.baseAddr;
int d_o=dd.lPitch;
if (f) d_o=-d_o;
else o-=d_o;
int h2=height;
while (h2--) {
o+=d_o; memcpy(o,i,width); i+=planes->y.rowBytes;
}
d_o/=2;
int w2=width/2;
h2=height/2;
i=(const char*)planes->v.baseAddr;
o=(char*)dd.lpSurface+(height*dd.lPitch*(f+4))/4;
if (!f) o-=d_o;
while (h2--) {
o+=d_o; memcpy(o,i,w2); i+=planes->v.rowBytes;
}
o=(char*)dd.lpSurface+(height*dd.lPitch*(f+5))/4;
i=(const char*)planes->u.baseAddr;
h2=height/2;
if (!f) o-=d_o;
while (h2--) {
o+=d_o; memcpy(o,i,w2);i+=planes->u.rowBytes;
}
}
} else if(type==NSV_MAKETYPE('Y','U','Y','2') || type==NSV_MAKETYPE('U','Y','V','Y')) {
const char *a=buf;
char *b=(char *)dd.lpSurface;
int l=width*2,l2=dd.lPitch;
if(flip) {
b+=(height-1)*l2;
l2=-l2;
}
int is_uyvy=type==NSV_MAKETYPE('U','Y','V','Y');
if (uyvy_output && !is_uyvy || (yuy2_output && is_uyvy)) // convert to uyvy
{
for(int i=0;i<height;i++) {
int x=width/2;
while (x-->0) {
b[0]=a[1];
b[1]=a[0];
b[2]=a[3];
b[3]=a[2];
a+=4;
b+=4;
}
memcpy(b,a,l);
b+=l2;
a+=l;
}
} else {
//wee straight YUY2 copy
for(int i=0;i<height;i++) {
memcpy(b,a,l);
b+=l2;
a+=l;
}
}
}
lpddsOverlay->Unlock(&dd);
if (m_parent->osdShowing())
{
RECT rs, rd;
getRects(&rs,&rd);
HDC hdc;
#if 1 // set both these 1s to 0s to put it back on ryan's mode
HWND h=m_parent->getHwnd();
hdc=GetDC(h);
#else
if (lpddsPrimary->GetDC(&hdc)==DD_OK)
{
#endif
m_parent->drawOSD(hdc, &rd);
#if 1
ReleaseDC(h,hdc);
#else
lpddsPrimary->ReleaseDC(hdc);
}
#endif
}
}
void OverlayVideoOutput::goFullScreen() {
/* fullscreen_controls = new GuiObjectWnd;
fullscreen_controls->setContent("video.fullscreen_controls");
fullscreen_controls->init(m_parent);
RECT r;
Std::getViewport(&r,m_parent->gethWnd(),1);
RECT nr = r;
nr.top = (int)(r.bottom - (r.bottom - r.top) * 0.15);
nr.bottom = (int)(r.bottom - (r.bottom - r.top) * 0.05);
fullscreen_controls->resizeToRect(&nr);
*/
is_fullscreen=1;
}
void OverlayVideoOutput::removeFullScreen() {
/* delete fullscreen_controls;
fullscreen_controls = NULL;*/
is_fullscreen=0;
}
int OverlayVideoOutput::showOSD() {
// if (fullscreen_controls != NULL) fullscreen_controls->setVisible(TRUE);
// enabling the following code will cause the top & bottom OSD bars
// to squish the image (instead of crop it):
/*if(lpddsOverlay) {
RECT rd,rs;
getRects(&rs,&rd);
HWND hwnd=m_parent->getHwnd();
if(GetParent(hwnd)) hwnd=GetParent(hwnd);
RECT temp;
GetClientRect(hwnd,&temp);
int bottom_margin = ((temp.bottom-temp.top) - (rd.bottom-rd.top)) / 2;
int pixels_to_clip = max(0, m_parent->getOSDbarHeight() - bottom_margin);
rd.bottom -= pixels_to_clip;
lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
}*/
return 1;
}
void OverlayVideoOutput::hideOSD() {
//if (fullscreen_controls != NULL) fullscreen_controls->setVisible(FALSE);
// 1) repaint the OSD area with the overlay color here
HWND hwnd = m_parent->getHwnd();
if(GetParent(hwnd)) hwnd=GetParent(hwnd);
HDC hdc = GetDC(hwnd);
if (hdc) {
RECT r;
GetClientRect(hwnd,&r);
LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
HBRUSH br=CreateBrushIndirect(&lb);
FillRect(hdc,&r,br);
DeleteObject(br);
ReleaseDC(hwnd, hdc);
}
// 2) readjust the overlay destination rectangle
/*if(lpddsOverlay) {
RECT rd,rs;
getRects(&rs,&rd);
lpddsOverlay->UpdateOverlay(&rs, lpddsPrimary, &rd, dwUpdateFlags, &ovfx);
}*/
}
void OverlayVideoOutput::drawSubtitle(SubsItem *item) {
curSubtitle=item;
HWND hwnd=m_parent->getHwnd();
RECT oldrect=subRect;
GetClientRect(hwnd,&subRect);
if(item) {
RECT oldwinRect=winRect;
GetClientRect(hwnd,&winRect);
if(!subFont || ((winRect.bottom-winRect.top)!=(oldwinRect.bottom-oldwinRect.top)) || m_fontsize!=item->fontSize) {
if(subFont) DeleteObject(subFont);
m_fontsize=item->fontSize;
subFont=CreateFont(14+item->fontSize+18*(winRect.bottom-winRect.top)/768,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
}
HDC out=GetDC(hwnd);
SelectObject(out,subFont);
SIZE s;
GetTextExtentPoint32(out,item->text,strlen(item->text),&s);
{
// calcul for multiline text
const char *p=item->text;
int n=0;
while(*p!=0) if(*p++=='\n') n++;
if(n) s.cy*=(n+1);
}
if (item->xPos > 127) // towards the right
{
subRect.right -= ((subRect.right-subRect.left) * (255-item->xPos)) / 256;
}
else if (item->xPos < 127)
{
subRect.left += ((subRect.right-subRect.left) * item->xPos) / 256;
}
subRect.top += ((subRect.bottom-s.cy-subRect.top) * item->yPos)/255;
subRect.bottom=subRect.top + s.cy;
ReleaseDC(hwnd,out);
}
//just redraw the correct portion
InvalidateRect(hwnd,&oldrect,TRUE);
InvalidateRect(hwnd,&subRect,TRUE);
}
void OverlayVideoOutput::resetSubtitle()
{
curSubtitle=NULL;
subRect.top=65536;
}

View File

@ -0,0 +1,58 @@
#ifndef _VIDEO_OVERLAY_H
#define _VIDEO_OVERLAY_H
#include <ddraw.h>
#include <multimon.h>
#include "video.h"
class SubsItem;
class OverlayVideoOutput : public VideoOutputChild {
public:
OverlayVideoOutput();
virtual ~OverlayVideoOutput();
int create(VideoOutput *parent, int w, int h, unsigned int type, int flipit, double aspectratio); //return 1 if ok
int needChange() { return needchange; }
int onPaint(HWND hwnd, HDC hdc);
void displayFrame(const char *buf, int size, int time);
void goFullScreen();
void removeFullScreen();
void timerCallback();
int showOSD();
void hideOSD();
void drawSubtitle(SubsItem *item);
virtual void resetSubtitle();
private:
int width, height, flip;
int needchange;
unsigned int type;
VideoOutput *m_parent;
LPDIRECTDRAW lpDD;
LPDIRECTDRAWSURFACE lpddsOverlay, lpddsPrimary;
DDCAPS capsDrv;
unsigned int uDestSizeAlign, uSrcSizeAlign;
DWORD dwUpdateFlags;
DDOVERLAYFX ovfx;
RECT rs,rd;
RECT m_oldrd;
RECT winRect;
bool initing;
int is_fullscreen, yuy2_output, uyvy_output;
void getRects(RECT *drs, RECT *drd);
HFONT subFont;
RECT subRect;
SubsItem *curSubtitle;
int m_fontsize;
};
#endif

964
Src/nsv/nsvplay/video.cpp Normal file
View File

@ -0,0 +1,964 @@
#include <windows.h>
#include <ddraw.h>
#include "main.h"
#include "video.h"
#include "subtitles.h"
#include "resource.h"
#undef GetSystemMetrics
#define OSD_ENABLED 1
#define INIT_DIRECTDRAW_STRUCT(x) (ZeroMemory(&x, sizeof(x)), x.dwSize=sizeof(x))
#define OV_COL_R 16
#define OV_COL_G 0
#define OV_COL_B 16
#define OSD_TEXT_SIZE 28
#define OSD_TEXT_R 192
#define OSD_TEXT_G 192
#define OSD_TEXT_B 192
#define OSD_TEXT_R_HILITE 255
#define OSD_TEXT_G_HILITE 255
#define OSD_TEXT_B_HILITE 255
#define OSD_VOL_COL_R 0
#define OSD_VOL_COL_G 0
#define OSD_VOL_COL_B 192
#define OSD_VOL_BKCOL_R 0
#define OSD_VOL_BKCOL_G 0
#define OSD_VOL_BKCOL_B 64
#define TIMER_OSD_ID 1234
#define CTRLTYPE_SYMBOL 0
#define CTRLTYPE_TEXT 1
#define CTRLTYPE_PROGRESS 2
#define CTRLTYPE_SPACER 3
#define CTRL_PROGRESSTEXT 0
#define CTRL_PROGRESS 1
#define CTRL_PROGRESSSPACER 2
#define CTRL_REW 3
#define CTRL_PLAY 4
#define CTRL_PAUSE 5
#define CTRL_STOP 6
#define CTRL_FFWD 7
#define CTRL_VOLSPACER 8
#define CTRL_VOLTEXT 9
#define CTRL_VOL 10
int g_ctrl_type[NUM_WIDGETS] = {
CTRLTYPE_TEXT,
CTRLTYPE_PROGRESS,
CTRLTYPE_SPACER,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SYMBOL,
CTRLTYPE_SPACER,
CTRLTYPE_TEXT,
CTRLTYPE_PROGRESS
};
const char *g_ctrl_text[NUM_WIDGETS] = {
"Progress ",
"",
"",
"7", // rew
"4", // play
";", // pause
"<", // stop
"8", // ffwd
"",
"Volume ",
""
};
int g_ctrl_force_width[NUM_WIDGETS] = {
0,
96, // progress bar width
32, // spacer width
0, // rew
0, // play
0, // pause
0, // stop
0, // ffwd
32, // spacer width
0,
64 // volume bar width
};
extern HINSTANCE g_hInstance;
extern int g_bitmap_id;
static BOOL WINAPI DDEnumCallbackEx(GUID FAR *lpGUID, LPSTR lpDriverDescription, LPSTR lpDriverName, LPVOID lpContext, HMONITOR hm) {
VideoOutputChild *ovo=(VideoOutputChild *)lpContext;
if(ovo->m_found_devguid) return 1;
if(hm==ovo->m_monitor_to_find) {
ovo->m_devguid=*lpGUID;
ovo->m_found_devguid=1;
}
return 1;
}
void VideoOutputChild::update_monitor_coords(VideoOutput *parent)
{
//find the correct monitor if multiple monitor support is present
HWND hwnd=parent->getHwnd();
m_found_devguid=0;
m_mon_x=0;
m_mon_y=0;
HINSTANCE h=LoadLibrary("user32.dll");
if (h) {
HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
if (Mfp && Mfr && Mfw && Gmi) {
RECT r;
GetWindowRect(hwnd,&r);
HMONITOR hm=Mfr(&r,NULL);
if(hm) {
HINSTANCE hdd = LoadLibrary("ddraw.dll");
if(hdd) {
typedef BOOL (FAR PASCAL * LPDDENUMCALLBACKEXA)(GUID FAR *, LPSTR, LPSTR, LPVOID, HMONITOR);
typedef HRESULT (WINAPI * LPDIRECTDRAWENUMERATEEX)( LPDDENUMCALLBACKEXA lpCallback, LPVOID lpContext, DWORD dwFlags);
LPDIRECTDRAWENUMERATEEX lpDDEnumEx;
lpDDEnumEx = (LPDIRECTDRAWENUMERATEEX) GetProcAddress(hdd,"DirectDrawEnumerateExA");
if (lpDDEnumEx) {
m_monitor_to_find=hm;
lpDDEnumEx(&DDEnumCallbackEx, this, DDENUM_ATTACHEDSECONDARYDEVICES|DDENUM_NONDISPLAYDEVICES);
if(m_found_devguid) {
MONITORINFOEX mi;
memset(&mi,0,sizeof(mi));
mi.cbSize=sizeof(mi);
if (Gmi(hm,&mi)) {
m_mon_x=mi.rcMonitor.left;
m_mon_y=mi.rcMonitor.top;
}
}
}
FreeLibrary(hdd);
}
}
}
FreeLibrary(h);
}
}
int VideoOutput::get_latency()
{
return vid_vsync?15:0;
}
#undef GetSystemMetrics
int VideoOutput::class_refcnt=0;
void VideoOutput::adjustAspect(RECT &rd)
{
if (vid_aspectadj)
{
int outh=rd.bottom-rd.top;
int outw=rd.right-rd.left;
int newh=(int)((aspect*height*outw)/(double)width);
int neww=(int)((width*outh)/(height*aspect));
if (outh > newh) // black bars on top and bottom
{
int d=outh - newh;
rd.top+=d/2;
rd.bottom-=d-d/2;
}
else if (outw > neww) // black bars on left and right
{
int d=outw - neww;
rd.left+=d/2;
rd.right-=d-d/2;
}
}
}
LRESULT CALLBACK VideoOutput::WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (uMsg == WM_CREATE)
{
SetWindowLong(hwnd,GWL_USERDATA,(long)((CREATESTRUCT *)lParam)->lpCreateParams);
ShowWindow(hwnd,SW_SHOW);
if (GetParent(hwnd))
{
RECT r;
GetClientRect(GetParent(hwnd),&r);
SetWindowPos(hwnd,NULL,0,0,
r.right,
r.bottom,
SWP_NOACTIVATE|SWP_NOZORDER);
}
return 0;
}
VideoOutput *_This=(VideoOutput*)GetWindowLong(hwnd,GWL_USERDATA);
if (_This) return _This->WindowProc(hwnd,uMsg,wParam,lParam);
else return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
void VideoOutput::notifyBufferState(int bufferstate) /* 0-255*/
{
m_bufferstate=bufferstate;
#ifdef ACTIVEX_CONTROL
PostMessage( video_hwnd, STATUS_MSG, STATUS_PREBUFFER, bufferstate );
#endif
if (!m_video_output) {
if(GetTickCount()-m_lastbufinvalid>500) {
InvalidateRect(video_hwnd,NULL,FALSE);
m_lastbufinvalid=GetTickCount();
}
}
}
LRESULT VideoOutput::WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
switch (uMsg)
{
case WM_TIMER:
case WM_WINDOWPOSCHANGING:
case WM_WINDOWPOSCHANGED:
case WM_SIZE:
case WM_MOVE:
case WM_MOVING:
if (uMsg == WM_TIMER && wParam == TIMER_OSD_ID) {
hideOSD();
return 0;
}
EnterCriticalSection(&m_cs);
if(m_video_output) m_video_output->timerCallback();
LeaveCriticalSection(&m_cs);
if (uMsg == WM_TIMER) return 0;
break;
case WM_LBUTTONDOWN:
if(is_fs)
osdHitTest(LOWORD(lParam),HIWORD(lParam),0);
#ifdef ACTIVEX_CONTROL
SendMessage( video_hwnd, STATUS_MSG, STATUS_MOUSEPRESS, 1 );
#endif
break;
case WM_PAINT:
{
if (m_video_output && m_video_output->onPaint(hwnd,(HDC)wParam)) return 0;
if (m_logo && !m_video_output)
{
PAINTSTRUCT p;
BeginPaint(hwnd,&p);
RECT r;
GetClientRect(hwnd,&r);
HDC out=p.hdc;
HDC dc=CreateCompatibleDC(NULL);
SelectObject(dc,m_logo);
int xp=(r.right-r.left-m_logo_w)/2;
int yp=(r.bottom-r.top-m_logo_h)/2;
BitBlt(out,xp,yp,m_logo_w,m_logo_h,dc,0,0,SRCCOPY);
int bs=m_bufferstate;
if (bs < 16) bs=16;
HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(0,0,0)));
HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(0,0,0)));
Rectangle(out,r.left,r.top,r.right,yp);
if (m_statusmsg)
Rectangle(out,r.left,yp+m_logo_h,r.right,r.bottom);
else
{
Rectangle(out,r.left,yp+m_logo_h+2+9,r.right,r.bottom);
Rectangle(out,xp + ((bs * (m_logo_w+2))>>8),yp+m_logo_h+2,r.right, yp+9+m_logo_h+2);
}
Rectangle(out,r.left,yp,xp-1,yp+m_logo_h+9+2);
Rectangle(out,xp+m_logo_w+1,yp,r.right,yp+m_logo_h+2);
DeleteObject(SelectObject(out,oldobj2));
DeleteObject(SelectObject(out,oldobj1));
if (m_statusmsg)
{
RECT subr={0,yp+m_logo_h+2,r.right,r.bottom};
SetTextColor(out,RGB(255,255,255));
SetBkMode(out,TRANSPARENT);
DrawText(out,m_statusmsg,-1,&subr,DT_TOP|DT_CENTER|DT_NOCLIP|DT_NOPREFIX);
}
else
{
yp+=m_logo_h+2;
if (bs)
{
HGDIOBJ oldobj1=SelectObject(out,CreateSolidBrush(RGB(128,128,128)));
HGDIOBJ oldobj2=SelectObject(out,CreatePen(PS_SOLID,0,RGB(255,255,255)));
Rectangle(out,xp-1,yp,xp + ((bs * (m_logo_w+2))>>8), yp+9);
DeleteObject(SelectObject(out,oldobj2));
DeleteObject(SelectObject(out,oldobj1));
}
}
DeleteDC(dc);
EndPaint(hwnd,&p);
return 0;
}
}
break;
case WM_USER+0x1:
m_need_change=1;
break;
#ifdef ACTIVEX_CONTROL
case STATUS_MSG:
SendStatus( wParam, lParam );
break;
#endif
case WM_KEYDOWN:
if(wParam==27 && is_fs) remove_fullscreen();
break;
case WM_MOUSEMOVE:
if(is_fs) {
if (ignore_mousemove_count>0) {
ignore_mousemove_count--;
}
else if (abs(osdLastMouseX - LOWORD(lParam)) + abs(osdLastMouseY - HIWORD(lParam)) > 1) {
KillTimer(hwnd, TIMER_OSD_ID);
showOSD();
SetTimer(hwnd, TIMER_OSD_ID, 2000, NULL);
if (wParam & MK_LBUTTON)
osdHitTest(LOWORD(lParam),HIWORD(lParam),1);
else
osdHitTest(LOWORD(lParam),HIWORD(lParam),-1);
}
osdLastMouseX = LOWORD(lParam);
osdLastMouseY = HIWORD(lParam);
}
break;
}
if (m_msgcallback)
{
return m_msgcallback(m_msgcallback_tok,hwnd, uMsg, wParam, lParam);
}
return (DefWindowProc(hwnd, uMsg, wParam, lParam));
}
VideoOutput::VideoOutput(HWND parent_hwnd, int initxpos, int initypos)
{
curSubtitle=NULL;
m_statusmsg=0;
m_bufferstate=0;
m_msgcallback=0;
m_msgcallback_tok=0;
video_hwnd=video_parent_hwnd=0;
decoder=0;
vid_aspectadj=true;
vid_overlays=true;
vid_ddraw=true;
vid_vsync=true;
aspect=1.0;
m_need_change=false;
width=height=flip=uyvy_output=yuy2_output=is_fs=ignore_mousemove_count=show_osd=0;
oldfsparent=0;
memset(&oldfsrect,0,sizeof(oldfsrect));
memset(&lastfsrect,0,sizeof(lastfsrect));
oldfsstyle=0;
m_video_output=NULL;
osdFontText=NULL;
osdFontSymbol=NULL;
osdProgressBrushBg=NULL;
osdProgressBrushFg=NULL;
osdProgressPenBg=NULL;
osdProgressPenFg=NULL;
osdProgressPenBgHilite=NULL;
osdBlackBrush=NULL;
osdMemDC=NULL;
osdMemBM=NULL;
osdOldBM=NULL;
osdMemBMW=0;
osdMemBMH=0;
osdLastMouseX=-1;
osdLastMouseY=-1;
for (int i=0; i<NUM_WIDGETS; i++)
SetRect(&ctrlrect[i], 0, 0, 0, 0);
ctrlrects_ready = 0;
resetSubtitle();
WNDCLASS wc={0,};
wc.hCursor=LoadCursor(NULL,IDC_ARROW);
wc.lpfnWndProc = WndProc;
wc.hInstance = GetModuleHandle(NULL);
wc.lpszClassName = "NSVplay";
LOGBRUSH lb={BS_SOLID,RGB(OV_COL_R,OV_COL_G,OV_COL_B),};
wc.hbrBackground=CreateBrushIndirect(&lb);
if (!class_refcnt) RegisterClass(&wc);
class_refcnt++;
m_logo=(HBITMAP)LoadImage(g_hInstance,MAKEINTRESOURCE(g_bitmap_id),IMAGE_BITMAP,0,0,LR_CREATEDIBSECTION);
BITMAP bm;
GetObject(m_logo, sizeof(BITMAP), &bm);
m_logo_w=bm.bmWidth;
m_logo_h=bm.bmHeight;
if(m_logo_h<0) m_logo_h=-m_logo_h;
InitializeCriticalSection(&m_cs);
video_hwnd=CreateWindowEx(0,wc.lpszClassName, "NSV Player",parent_hwnd?WS_CHILD:(WS_OVERLAPPEDWINDOW&(~WS_MAXIMIZEBOX)),
initxpos,initypos,320,200,
parent_hwnd, NULL,wc.hInstance,(void*)this);
video_parent_hwnd=parent_hwnd;
m_lastbufinvalid=0;
#ifdef ACTIVEX_CONTROL
m_firstframe = 1;
#endif
}
VideoOutputChild *VideoOutput::createVideoOutput(int n) {
if(!vid_overlays && !vid_ddraw) vid_overlays=true;
if(!vid_overlays) n++;
if(n==0) return new OverlayVideoOutput();
if(!vid_ddraw) n++;
if(n==1) return new DDrawVideoOutput();
return 0;
}
int VideoOutput::open(int w, int h, int vflip, double aspectratio, unsigned int fmt)
{
EnterCriticalSection(&m_cs);
delete(m_video_output);
m_video_output=NULL;
if (!w) w=320;
if (!h) h=240;
width=w;
height=h;
flip=vflip;
type=fmt;
is_fs=0;
ignore_mousemove_count=0;
show_osd=0;
aspect=aspectratio;
for(int i=0;m_video_output=createVideoOutput(i);i++) {
if(m_video_output->create(this,w,h,fmt,vflip,aspectratio)) {
LeaveCriticalSection(&m_cs);
if (!GetParent(video_hwnd)) {
RECT r,r2;
int ow=width,oh=height;
if (aspect > 0.001)
{
if (aspect < 1.0) ow=(int)(ow/aspect);
else oh=(int)(oh*aspect);
}
GetWindowRect(video_hwnd,&r);
GetClientRect(video_hwnd,&r2);
SetWindowPos(video_hwnd,NULL,0,0,
ow+(r.right-r.left)-(r2.right-r2.left),
oh+(r.bottom-r.top)-(r2.bottom-r2.top),
SWP_NOMOVE|SWP_NOACTIVATE|SWP_NOZORDER);
}
return 0;
}
delete(m_video_output);
}
LeaveCriticalSection(&m_cs);
return 1;
}
void VideoOutput::draw(void *frame)
{
if (!m_video_output || !frame) return;
if ((m_video_output && m_video_output->needChange()) || m_need_change) {
open(width,height,flip,aspect,type);
m_need_change=0;
}
#ifdef ACTIVEX_CONTROL
if ( m_firstframe ) {
m_firstframe = 0;
PostMessage( video_hwnd, STATUS_MSG, STATUS_FIRSTFRAME, 1 );
}
#endif
if (m_video_output) m_video_output->displayFrame((const char *)frame,0,0);
}
VideoOutput::~VideoOutput()
{
free(m_statusmsg);
delete(m_video_output);
DestroyWindow(video_hwnd);
if (!--class_refcnt) UnregisterClass("NSVplay",GetModuleHandle(NULL));
if(osdFontText) DeleteObject(osdFontText);
if(osdFontSymbol) DeleteObject(osdFontSymbol);
if(osdProgressBrushBg) DeleteObject(osdProgressBrushBg);
if(osdProgressBrushFg) DeleteObject(osdProgressBrushFg);
if(osdBlackBrush ) DeleteObject(osdBlackBrush );
if(osdProgressPenBg ) DeleteObject(osdProgressPenBg );
if(osdProgressPenFg ) DeleteObject(osdProgressPenFg );
if(osdProgressPenBgHilite) DeleteObject(osdProgressPenBgHilite);
if(osdMemDC) {
SelectObject(osdMemDC,osdOldBM); // delete our doublebuffer
DeleteDC(osdMemDC);
}
if(osdMemBM) DeleteObject(osdMemBM);
DeleteCriticalSection(&m_cs);
}
void VideoOutput::close()
{
delete(m_video_output);
m_video_output=NULL;
}
void VideoOutput::getViewport(RECT *r, HWND wnd, int full) {
POINT *p=NULL;
RECT *sr=NULL;
if (p || sr || wnd) {
HINSTANCE h=LoadLibrary("user32.dll");
if (h) {
HMONITOR (WINAPI *Mfp)(POINT pt, DWORD dwFlags) = (HMONITOR (WINAPI *)(POINT,DWORD)) GetProcAddress(h,"MonitorFromPoint");
HMONITOR (WINAPI *Mfr)(LPCRECT lpcr, DWORD dwFlags) = (HMONITOR (WINAPI *)(LPCRECT, DWORD)) GetProcAddress(h, "MonitorFromRect");
HMONITOR (WINAPI *Mfw)(HWND wnd, DWORD dwFlags)=(HMONITOR (WINAPI *)(HWND, DWORD)) GetProcAddress(h, "MonitorFromWindow");
BOOL (WINAPI *Gmi)(HMONITOR mon, LPMONITORINFO lpmi) = (BOOL (WINAPI *)(HMONITOR,LPMONITORINFO)) GetProcAddress(h,"GetMonitorInfoA");
if (Mfp && Mfr && Mfw && Gmi) {
HMONITOR hm = NULL;
if (p)
hm=Mfp(*p,MONITOR_DEFAULTTONULL);
else if (sr)
hm=Mfr(sr,MONITOR_DEFAULTTONULL);
else if (wnd)
hm=Mfw(wnd,MONITOR_DEFAULTTONULL);
if (hm) {
MONITORINFOEX mi;
memset(&mi,0,sizeof(mi));
mi.cbSize=sizeof(mi);
if (Gmi(hm,&mi)) {
if(!full) *r=mi.rcWork;
else *r=mi.rcMonitor;
FreeLibrary(h);
return;
}
}
}
FreeLibrary(h);
}
}
if (full)
{ // this might be borked =)
r->top=r->left=0;
r->right=::GetSystemMetrics(SM_CXSCREEN);
r->bottom=::GetSystemMetrics(SM_CYSCREEN);
}
else
{
SystemParametersInfo(SPI_GETWORKAREA,0,r,0);
}
}
void VideoOutput::fullscreen()
{
if (is_fs) return;
if(!m_video_output) return;
is_fs=1;
ignore_mousemove_count=2;
oldfsparent=GetParent(video_hwnd);
oldfsstyle=GetWindowLong(video_hwnd,GWL_STYLE);
if (!oldfsparent) GetWindowRect(video_hwnd,&oldfsrect);
else GetClientRect(video_hwnd,&oldfsrect);
getViewport(&lastfsrect,video_hwnd,1);
SetParent(video_hwnd,NULL);
SetWindowLong(video_hwnd,GWL_STYLE,WS_POPUP|WS_VISIBLE);
SetWindowPos(video_hwnd, HWND_TOPMOST, lastfsrect.left, lastfsrect.top, lastfsrect.right-lastfsrect.left, lastfsrect.bottom-lastfsrect.top, SWP_DRAWFRAME);
SetFocus(video_hwnd);
resetSubtitle();
//showOSD();
//SetCursor(NULL);
}
void VideoOutput::getOutputSize(int *w, int *h)
{
RECT r2;
GetClientRect(video_hwnd,&r2);
*w=r2.right-r2.left;
*h=r2.bottom-r2.top;
}
void VideoOutput::setOutputSize(int w, int h)
{
RECT r,r2;
GetWindowRect(video_hwnd,&r);
GetClientRect(video_hwnd,&r2);
SetWindowPos(video_hwnd, 0, 0,0,
w+(r.right-r.left)-(r2.right-r2.left),
h+(r.bottom-r.top)-(r2.bottom-r2.top),
SWP_NOMOVE|SWP_NOZORDER|SWP_NOACTIVATE);
}
void VideoOutput::remove_fullscreen()
{
if(!is_fs) return;
SetParent(video_hwnd,oldfsparent);
SetWindowLong(video_hwnd,GWL_STYLE,oldfsstyle);
// note: when returning from fullscreen *on a secondary monitor*,
// be careful how you set the new window Z order.
// nsvplay.exe: only HWND_NOTOPMOST works
// nsvplayX.exe: only HWND_TOP works
SetWindowPos(video_hwnd, oldfsparent ? HWND_TOP : HWND_NOTOPMOST, oldfsrect.left, oldfsrect.top, oldfsrect.right-oldfsrect.left, oldfsrect.bottom-oldfsrect.top, SWP_FRAMECHANGED);
SetFocus(oldfsparent ? oldfsparent : video_hwnd);
is_fs=0;
show_osd=0;
ctrlrects_ready=0;
resetSubtitle();
hideOSD();
}
int VideoOutput::is_fullscreen()
{
return is_fs;
}
void VideoOutput::showStatusMsg(const char *text)
{
m_statusmsg=_strdup(text);
InvalidateRect(video_hwnd,NULL,TRUE);
}
void VideoOutput::drawSubtitle(SubsItem *item)
{
if(!item) {
if(curSubtitle) {
m_video_output->drawSubtitle(NULL);
curSubtitle=NULL;
}
return;
}
if(curSubtitle==item) return;
curSubtitle=item;
m_video_output->drawSubtitle(curSubtitle);
}
void VideoOutput::resetSubtitle()
{
curSubtitle=NULL;
if(m_video_output) m_video_output->resetSubtitle();
}
void VideoOutput::showOSD() {
if(OSD_ENABLED && m_video_output) {
KillTimer(video_hwnd, TIMER_OSD_ID);
if (!show_osd)
m_video_output->showOSD();
SetTimer(video_hwnd, TIMER_OSD_ID, 2000, NULL);
show_osd = 1;
SetCursor(LoadCursor(NULL, IDC_ARROW));
}
}
void VideoOutput::hideOSD() {
if(OSD_ENABLED && m_video_output) {
KillTimer(video_hwnd, TIMER_OSD_ID);
m_video_output->hideOSD();
show_osd = 0;
SetCursor(NULL);
}
}
void VideoOutput::drawOSD(HDC hdc, RECT *rg) {
if(m_video_output && show_osd) {
if (!osdMemDC ) osdMemDC = CreateCompatibleDC(hdc);
if (!osdFontText) osdFontText=CreateFont(OSD_TEXT_SIZE,0,0,0,FW_SEMIBOLD,FALSE,FALSE,FALSE,ANSI_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH|FF_DONTCARE,"Arial");
if (!osdFontSymbol) osdFontSymbol=CreateFont(OSD_TEXT_SIZE,0,0,0,FW_NORMAL,FALSE,FALSE,FALSE,SYMBOL_CHARSET,OUT_OUTLINE_PRECIS,CLIP_DEFAULT_PRECIS,ANTIALIASED_QUALITY,DEFAULT_PITCH,"Webdings");
if (!osdProgressBrushBg) osdProgressBrushBg = CreateSolidBrush(RGB(OSD_VOL_BKCOL_R,OSD_VOL_BKCOL_G,OSD_VOL_BKCOL_B));
if (!osdProgressBrushFg) osdProgressBrushFg = CreateSolidBrush(RGB(OSD_VOL_COL_R,OSD_VOL_COL_G,OSD_VOL_COL_B));
if (!osdBlackBrush ) osdBlackBrush = CreateSolidBrush(RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
if (!osdProgressPenBg ) osdProgressPenBg = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
if (!osdProgressPenFg ) osdProgressPenFg = CreatePen(PS_NULL,0,RGB(0,0,0));
if (!osdProgressPenBgHilite) osdProgressPenBgHilite = CreatePen(PS_SOLID,0,RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE));
COLORREF fg = GetTextColor(osdMemDC);
COLORREF bg = GetBkColor(osdMemDC);
SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
SetBkColor(osdMemDC, RGB(0,0,0));//OV_COL_R,OV_COL_G,OV_COL_B));
HGDIOBJ oldfont = SelectObject(osdMemDC, osdFontText);
HGDIOBJ oldbrush = SelectObject(osdMemDC, osdProgressBrushBg);
HGDIOBJ oldpen = SelectObject(osdMemDC, osdProgressPenBg);
RECT fullr;
GetClientRect(video_hwnd,&fullr);
ClientToScreen(video_hwnd,(LPPOINT)&fullr);
ClientToScreen(video_hwnd,((LPPOINT)&fullr) + 1);
// transform coords from windows desktop coords (where 0,0==upper-left corner of the primary monitor)
// to the coords for the monitor we're displaying on:
fullr.top -= m_video_output->m_mon_y;
fullr.left -= m_video_output->m_mon_x;
fullr.right -= m_video_output->m_mon_x;
fullr.bottom -= m_video_output->m_mon_y;
if (!ctrlrects_ready) {
ctrlrects_ready = 1;
int net_width = 0;
int max_height = 0;
int streaming = (decoder && decoder->getlen()==-1) ? 1 : 0;
for (int i=0; i<NUM_WIDGETS; i++) {
if (streaming && (i==CTRL_PROGRESS || i==CTRL_PROGRESSTEXT || i==CTRL_PROGRESSSPACER || i==CTRL_FFWD || i==CTRL_REW)) {
// disable progress bar + seek arrows when the NSV is a stream
ctrlrect[i].right = -1;
continue;
}
else if (g_ctrl_force_width[i] != 0) {
SetRect(&ctrlrect[i], 0, 0, g_ctrl_force_width[i], 0);
}
else {
SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
SetRect(&ctrlrect[i], 0, 0, 256, 256);
ctrlrect[i].bottom = DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE|DT_CALCRECT);
}
net_width += ctrlrect[i].right - ctrlrect[i].left;
max_height = max(max_height, ctrlrect[i].bottom - ctrlrect[i].top);
}
// now we know the size of all the controls; now place them.
int x = (fullr.right + fullr.left)/2 - net_width/2;
SetRect(&ctrlrect_all, 0, 0, 0, 0);
for (i=0; i<NUM_WIDGETS; i++)
{
if (ctrlrect[i].right >= 0) // if control is not disabled...
{
int this_width = ctrlrect[i].right - ctrlrect[i].left;
int this_height = ctrlrect[i].bottom - ctrlrect[i].top ;
if (this_height==0) this_height = max_height*2/3;// progress bars
ctrlrect[i].top = max_height/2 - this_height/2;
ctrlrect[i].bottom = max_height/2 + this_height/2;
ctrlrect[i].left = x;
ctrlrect[i].right = x + this_width;
if (ctrlrect_all.bottom==0) {
ctrlrect_all.top = ctrlrect[i].top ;
ctrlrect_all.bottom = ctrlrect[i].bottom;
}
else {
ctrlrect_all.top = min(ctrlrect_all.top , ctrlrect[i].top );
ctrlrect_all.bottom = max(ctrlrect_all.bottom, ctrlrect[i].bottom);
}
x += this_width;
}
}
}
int w = fullr.right - fullr.left;
int h = ctrlrect_all.bottom - ctrlrect_all.top;
if (!osdMemBM || osdMemBMW != w || osdMemBMH != h) {
if (osdMemBM) {
SelectObject(osdMemDC,osdOldBM);
DeleteObject(osdMemBM);
}
osdMemBM = CreateCompatibleBitmap(hdc,w,h);
osdOldBM = (HBITMAP)SelectObject(osdMemDC, osdMemBM);
osdMemBMW = w;
osdMemBMH = h;
}
RECT temp;
SetRect(&temp, 0, 0, w, h);
FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
for (int i=0; i<NUM_WIDGETS; i++) {
if (g_ctrl_type[i] == CTRLTYPE_PROGRESS)
{
int progress = 0;
int max_progress = ctrlrect[i].right - ctrlrect[i].left;
switch(i)
{
case CTRL_VOL:
if (decoder)
progress = decoder->getvolume()*max_progress/255;
break;
case CTRL_PROGRESS:
if (decoder)
{
int len = decoder->getlen();
if (len>0)
progress = decoder->getpos()*max_progress/len;
}
break;
}
SelectObject(osdMemDC, osdProgressBrushBg);
SelectObject(osdMemDC, (i==osdLastClickItem) ? osdProgressPenBgHilite : osdProgressPenBg);
RoundRect(osdMemDC, ctrlrect[i].left, ctrlrect[i].top, ctrlrect[i].right, ctrlrect[i].bottom, 3, 3);
SelectObject(osdMemDC, osdProgressBrushFg);
SelectObject(osdMemDC, osdProgressPenFg);
Rectangle(osdMemDC, ctrlrect[i].left+1, ctrlrect[i].top+1, ctrlrect[i].left + progress, ctrlrect[i].bottom);
}
else if (g_ctrl_type[i] == CTRLTYPE_SYMBOL ||
g_ctrl_type[i] == CTRLTYPE_TEXT)
{
SelectObject(osdMemDC, (g_ctrl_type[i] == CTRLTYPE_SYMBOL) ? osdFontSymbol : osdFontText);
SetTextColor(osdMemDC, (i==osdLastClickItem) ? RGB(OSD_TEXT_R_HILITE,OSD_TEXT_G_HILITE,OSD_TEXT_B_HILITE) : RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
DrawText(osdMemDC, g_ctrl_text[i], -1, &ctrlrect[i], DT_SINGLELINE);
}
}
int x0 = fullr.left;
int y0 = fullr.bottom - (ctrlrect_all.bottom - ctrlrect_all.top);
BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
// display stream title @ the top:
#if (SHOW_STREAM_TITLE_AT_TOP)
if (decoder)
{
RECT temp;
SetRect(&temp, 0, 0, w, h);
FillRect(osdMemDC, &temp, (HBRUSH)osdBlackBrush);
SelectObject(osdMemDC, osdFontText);
SetTextColor(osdMemDC, RGB(OSD_TEXT_R,OSD_TEXT_G,OSD_TEXT_B));
char *t=decoder->getTitle();
char *buf=(char*)malloc(32+(t?strlen(t):0));
wsprintf(buf, "%s (%d kbps)", t?t:"", decoder->getBitrate()/1000);
char *p=buf;
while (*p)
{
if (*p == '_') *p=' ';
p++;
}
DrawText(osdMemDC, buf, -1, &temp, DT_SINGLELINE|DT_CENTER);
free(buf);
SelectObject(osdMemDC, osdFontSymbol);
DrawText(osdMemDC, "2r", -1, &temp, DT_SINGLELINE|DT_RIGHT);
int x0 = fullr.left;
int y0 = fullr.top;
BitBlt(hdc,x0,y0,w,h,osdMemDC,0,0,SRCCOPY);
}
SelectObject(osdMemDC, oldpen);
SelectObject(osdMemDC, oldbrush);
SelectObject(osdMemDC, oldfont);
SetTextColor(osdMemDC, fg);
SetBkColor(osdMemDC, bg);
}
#endif
}
void VideoOutput::osdHitTest(int x, int y, int dragging)
{
// dragging == -1: just a mousemove (no clicking)
// dragging == 0: user clicked
// dragging == 1: user clicked before, and is now dragging/moving mouse
if (dragging<1)
osdLastClickItem = -1;
// transform (x,y) from screen coords into coords relative to the memDC
y = y - ((lastfsrect.bottom - lastfsrect.top) - (ctrlrect_all.bottom - ctrlrect_all.top));
int i0 = 0;
int i1 = NUM_WIDGETS;
if (dragging==1) {
i0 = osdLastClickItem;
i1 = osdLastClickItem+1;
}
for (int i=i0; i<i1; i++)
{
if (dragging==1 || (x >= ctrlrect[i].left && x <= ctrlrect[i].right && y >= ctrlrect[i].top && y <= ctrlrect[i].bottom))
{
float t = (x - ctrlrect[i].left) / (float)(ctrlrect[i].right - ctrlrect[i].left);
if (t<0) t=0;
if (t>1) t=1;
if (dragging<1)
osdLastClickItem = i;
switch(i)
{
case CTRL_VOL:
if (decoder && dragging>=0) decoder->setvolume((int)(t*255));
return;
case CTRL_PROGRESS:
if (decoder && dragging>=0)
{
int len = decoder->getlen();
if (len > 0)
decoder->seek((int)(t*len));
}
return;
case CTRL_PAUSE:
if (decoder && dragging>=0) decoder->pause(1);
return;
case CTRL_PLAY:
if (decoder && dragging>=0) decoder->pause(0);
return;
case CTRL_STOP:
if (decoder && dragging>=0) {
decoder->pause(1);
remove_fullscreen();
}
return;
case CTRL_REW:
case CTRL_FFWD:
if (decoder && dragging>=0)
{
int pos = decoder->getpos();
int len = decoder->getlen();
if (len > 0)
{
if (i==CTRL_REW)
pos = max(0, pos-15000); // milliseconds to rewind
else
pos = min(len, pos+30000); // milliseconds to skip ahead
decoder->seek(pos);
}
}
return;
default:
if (dragging<1)
osdLastClickItem = -1;
break;
}
}
}
if (dragging==0)
remove_fullscreen();
}

160
Src/nsv/nsvplay/video.h Normal file
View File

@ -0,0 +1,160 @@
#ifndef _VIDEO_H
#define _VIDEO_H
#include <windows.h>
#include <ddraw.h>
#include <multimon.h>
#include "main.h"
#define NUM_WIDGETS 11
class VideoOutput;
class SubsItem;
class VideoOutputChild {
public:
VideoOutputChild() { m_mon_x=m_mon_y=0; }
virtual ~VideoOutputChild() { }
virtual int create(VideoOutput *parent, int w, int h, unsigned int type, int flipit, double aspectratio)=0; //return 1 if ok
virtual int needChange()=0; //return 1 if need to renegociate video output
virtual int onPaint(HWND hwnd, HDC hdc) { return 0; } //return 1 if override
virtual void displayFrame(const char *buf, int size, int time)=0;
virtual void goFullScreen()=0;
virtual void removeFullScreen()=0;
virtual int showOSD() { return 0; }
virtual void hideOSD() { }
virtual void timerCallback() { }
virtual void setPalette(RGBQUAD *pal) { }
virtual void drawSubtitle(SubsItem *item) { }
virtual void resetSubtitle() { }
char m_szTest[512];
void update_monitor_coords(VideoOutput *parent);
int m_mon_x;
int m_mon_y;
int m_found_devguid;
GUID m_devguid;
HMONITOR m_monitor_to_find;
};
#include "vid_overlay.h"
#include "vid_ddraw.h"
class VideoOutput : public IVideoOutput {
public:
VideoOutput(HWND parent_hwnd=NULL, int initxpos=CW_USEDEFAULT, int initypos=CW_USEDEFAULT);
~VideoOutput();
int open(int w, int h, int vflip, double aspectratio, unsigned int fmt);
void close();
void draw(void *frame);
void drawSubtitle(SubsItem *item);
void showStatusMsg(const char *text);
void notifyBufferState(int bufferstate); /* 0-255*/
int get_latency();
void setcallback(LRESULT (*msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam), void *token) { m_msgcallback_tok=token; m_msgcallback=msgcallback; }
void setNSVDecoder(NSVDecoder *nsv_decoder) { decoder = nsv_decoder; }
void fullscreen();
void remove_fullscreen();
int is_fullscreen();
HWND getHwnd() { return video_hwnd; }
void adjustAspect(RECT &rd);
int vid_vsync;
int vid_aspectadj;
int vid_overlays;
int vid_ddraw;
void getViewport(RECT *r, HWND wnd, int full);
void setOutputSize(int w, int h);
void getOutputSize(int *w, int *h);
int osdShowing() { return show_osd; }
int osdReady() { return ctrlrects_ready; }
void showOSD();
void hideOSD();
void drawOSD(HDC hdc, RECT *r);
int getOSDbarHeight() { return (show_osd && ctrlrects_ready) ? (ctrlrect_all.bottom - ctrlrect_all.top) : 0; };
private:
LRESULT WindowProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
static int class_refcnt;
static LRESULT CALLBACK WndProc(HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
HWND video_hwnd, video_parent_hwnd;
NSVDecoder *decoder;
double aspect;
int width, height, flip;
unsigned int type;
int uyvy_output,yuy2_output;
int i420_output;
int is_fs;
int ignore_mousemove_count;
int show_osd;
HWND oldfsparent;
RECT oldfsrect; // the old window rect, BEFORE fullscreen mode was entered
RECT lastfsrect; // the most recent bounding rect when in fullscreen mode
LONG oldfsstyle;
int m_bufferstate;
void resetSubtitle();
SubsItem *curSubtitle;
// ONSCREEN DISPLAY (osd):
HFONT osdFontText;
HFONT osdFontSymbol;
HGDIOBJ osdProgressBrushBg;
HGDIOBJ osdProgressBrushFg;
HGDIOBJ osdProgressPenBg;
HGDIOBJ osdProgressPenFg;
HGDIOBJ osdProgressPenBgHilite;
HGDIOBJ osdBlackBrush;
void osdHitTest(int x, int y, int dragging);
int osdLastClickItem;
HDC osdMemDC; // memory device context
HBITMAP osdMemBM; // memory bitmap (for memDC)
HBITMAP osdOldBM; // old bitmap (from memDC)
int osdMemBMW; // width of memory bitmap
int osdMemBMH; // height of memory bitmap
int osdLastMouseX; // for WM_MOUSEMOVE thresholding, so osd isn't spastic
int osdLastMouseY; // for WM_MOUSEMOVE thresholding, so osd isn't spastic
RECT ctrlrect[NUM_WIDGETS]; // relative to [i.e. (0,0) is] upper left corner of the black strip @ the bottom
RECT ctrlrect_all; // relative to [i.e. (0,0) is] upper left corner of the black strip @ the bottom
int ctrlrects_ready;
LRESULT (*m_msgcallback)(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam);
void *m_msgcallback_tok;
VideoOutputChild *m_video_output;
VideoOutputChild *createVideoOutput(int n);
CRITICAL_SECTION m_cs;
char *m_statusmsg;
int m_need_change;
HBITMAP m_logo;
int m_logo_w, m_logo_h;
DWORD m_lastbufinvalid;
#ifdef ACTIVEX_CONTROL
int m_firstframe;
#endif
};
#endif

120
Src/nsv/nsvplay/vp3stub.cpp Normal file
View File

@ -0,0 +1,120 @@
#include "main.h"
#include "../../vp32/include/duck_dxl.h"
#include "vfw.h"
extern "C" {
void GetImageBufs(DXL_XIMAGE_HANDLE x, YV12_PLANES *p);
};
int vp3_postprocess=0;
int vp3_targetcpu=0;
class VP3_Decoder : public IVideoDecoder {
public:
VP3_Decoder(int w, int h, int uvflip);
~VP3_Decoder();
int decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf);
void flush() { }
private:
int m_uvflip;
int l_tcpu, l_pp;
static int init;
DXL_XIMAGE_HANDLE xim;
YV12_PLANES vidbufdec;
};
int VP3_Decoder::init;
VP3_Decoder::VP3_Decoder(int w, int h, int uvflip)
{
l_tcpu=-1;
l_pp=-1;
if (!init)
{
init=1;
DXL_InitVideoEx(1,1);
}
m_uvflip=uvflip;
vidbufdec.y.baseAddr=0;
xim = DXL_AlterXImage( NULL, (unsigned char *)"" ,MAKEFOURCC('V','P','3','1'), DXRGBNULL,w,h);
}
VP3_Decoder::~VP3_Decoder()
{
if ( xim ) DXL_DestroyXImage( xim);
}
int VP3_Decoder::decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf)
{
BYTE *data=(BYTE*)in;
if (!xim) return -1;
*out_type=NSV_MAKETYPE('Y','V','1','2');
if (vp3_postprocess != l_pp || vp3_targetcpu != l_tcpu)
{
l_pp=vp3_postprocess;
l_tcpu=vp3_targetcpu;
if (l_pp)
{
int v=l_tcpu;
if (v < 1) v=1;
if (v > 100) v=100;
vp31_SetParameter(xim,1, v);
vp31_SetParameter(xim,0, 9);
}
else
{
vp31_SetParameter(xim,1, 0);
vp31_SetParameter(xim,0, 0);
}
}
DXL_AlterXImageData( xim, data);
DXL_SetXImageCSize(xim, in_len);
*is_kf=!(!in_len || data[0] > 0x7f);
*out=NULL;
if ((need_kf && !*is_kf) || !in_len)
{
return 0;
}
if (!DXL_dxImageToVScreen( xim, NULL))
{
GetImageBufs(xim,&vidbufdec);
if (m_uvflip)
{
YV12_PLANE tmp=vidbufdec.v;
vidbufdec.v=vidbufdec.u;
vidbufdec.u=tmp;
}
*out=&vidbufdec;
return 0;
}
return -1;
}
IVideoDecoder *VP3_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip)
{
if (fmt == NSV_MAKETYPE('V','P','3',' ') || fmt == NSV_MAKETYPE('V','P','3','1'))
{
*flip=1;
return new VP3_Decoder(w,h,fmt == NSV_MAKETYPE('V','P','3',' '));
}
return NULL;
}

11
Src/nsv/nsvplay/vp3stub.h Normal file
View File

@ -0,0 +1,11 @@
#ifndef _VP3STUB_H_
#define _VP3STUB_H_
#include "main.h"
extern int vp3_postprocess;
extern int vp3_targetcpu;
IVideoDecoder *VP3_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip);
#endif

112
Src/nsv/nsvplay/vp5stub.cpp Normal file
View File

@ -0,0 +1,112 @@
#include <windows.h>
#include "../nsvlib.h"
#include "../dec_if.h"
#include "vfw.h"
class VP5_Decoder : public IVideoDecoder {
public:
VP5_Decoder(int w, int h);
~VP5_Decoder();
int decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf);
void flush() { }
int m_err;
private:
int width,height;
BITMAPINFO vp5_bmo,vp5_bmi;
HIC vp5_hic;
unsigned char *vidbufdec;
};
VP5_Decoder::VP5_Decoder(int w, int h)
{
width=w;
height=h;
m_err=0;
vp5_hic=0;
vidbufdec=(unsigned char*)malloc(sizeof(YV12_PLANES) + w*h*3/2);
// init vp5 decode
memset((void *) &vp5_bmi,0,sizeof(BITMAPINFO));
memset((void *) &vp5_bmo,0,sizeof(BITMAPINFO));
vp5_bmi.bmiHeader.biCompression = mmioFOURCC('V','P','5','0');
vp5_bmi.bmiHeader.biHeight=h;
vp5_bmi.bmiHeader.biWidth =w;
vp5_bmo.bmiHeader.biCompression = mmioFOURCC('Y','V','1','2');
vp5_bmo.bmiHeader.biHeight=h;
vp5_bmo.bmiHeader.biWidth =w;
vp5_bmo.bmiHeader.biBitCount = 12;
vp5_hic = ICOpen(ICTYPE_VIDEO, vp5_bmi.bmiHeader.biCompression, ICMODE_DECOMPRESS);
vp5_bmo.bmiHeader.biHeight*=-1;
if(!vp5_hic || ICERR_OK !=ICDecompressBegin(vp5_hic, &vp5_bmi, &vp5_bmo))
{
m_err=1;
return;
}
}
VP5_Decoder::~VP5_Decoder()
{
if (vp5_hic)
{
ICDecompressEnd(vp5_hic);
ICClose(vp5_hic);
}
free(vidbufdec);
}
int VP5_Decoder::decode(int need_kf,
void *in, int in_len,
void **out, // out is set to a pointer to data
unsigned int *out_type, // 'Y','V','1','2' is currently defined
int *is_kf)
{
*out_type=NSV_MAKETYPE('Y','V','1','2');
vp5_bmi.bmiHeader.biSizeImage = in_len;
if(ICERR_OK == ICDecompress(vp5_hic,0,(BITMAPINFOHEADER *) &vp5_bmi, (char*)in,(BITMAPINFOHEADER *) &vp5_bmo, (char*)vidbufdec+sizeof(YV12_PLANES)))
{
*is_kf=!(!in_len || ((unsigned char *)in)[0] > 0x7f);
if (need_kf && !*is_kf)
{
return 0;
}
YV12_PLANES *image_vbd=(YV12_PLANES *)vidbufdec;
image_vbd->y.baseAddr=(unsigned char *)(image_vbd+1);
image_vbd->v.baseAddr=((unsigned char *)(image_vbd+1)) + width*height;
image_vbd->u.baseAddr=((unsigned char *)(image_vbd+1)) + width*height*5/4;
image_vbd->y.rowBytes=width;
image_vbd->v.rowBytes=width/2;
image_vbd->u.rowBytes=width/2;
*out=(void*)vidbufdec;
return 0;
}
return -1;
}
IVideoDecoder *VP5_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip)
{
if (fmt == NSV_MAKETYPE('V','P','5','0'))
{
*flip=0;
VP5_Decoder *a=new VP5_Decoder(w,h);
if (a->m_err)
{
delete a;
return NULL;
}
return a;
}
return NULL;
}

View File

@ -0,0 +1,8 @@
#ifndef _VP5STUB_H_
#define _VP5STUB_H_
#include "main.h"
IVideoDecoder *VP5_CREATE(int w, int h, double framerate, unsigned int fmt, int *flip);
#endif

388
Src/nsv/nsvplay/wndmenu.h Normal file
View File

@ -0,0 +1,388 @@
#ifndef _WNDMENU_H_
#define _WNDMENU_H_
//need to define WNDMENU_CAPTION to the caption
// you can define these to remove menu functionality
//WNDMENU_NOABOUT // no about header
//WNDMENU_NOVOLUME // no volume submenu
//WNDMENU_NOINFO // no info submenu
//WNDMENU_NOZOOM // no zoom 50/100/200/400
//WNDMENU_NOZOOMFS // no zoom fullscreen
//WNDMENU_NOOPTIONS // no options submenu
//WNDMENU_NOPOSITIONSAVE // disables saving of position
//WNDMENU_NOSUBTITLES // no subtitles submenu
#ifndef ABS
#define ABS(x) ((x)<0?-(x):(x))
#endif
static void _ReadConfigItemInt(char *name, int *value)
{
HKEY hKey;
if (RegOpenKeyEx(HKEY_LOCAL_MACHINE,"Software\\Nullsoft\\NSVPlay",0,KEY_READ,&hKey) == ERROR_SUCCESS)
{
DWORD t,l=4;
int a;
if (RegQueryValueEx(hKey,name,NULL,&t,(unsigned char *)&a,&l ) == ERROR_SUCCESS && t == REG_DWORD)
*value=a;
RegCloseKey(hKey);
}
}
static void _WriteConfigItemInt(char *name, int value)
{
HKEY hKey;
if (RegCreateKey(HKEY_LOCAL_MACHINE,"Software\\Nullsoft\\NSVPlay",&hKey) == ERROR_SUCCESS)
{
RegSetValueEx(hKey,name,0,REG_DWORD,(unsigned char*)&value,4);
RegCloseKey(hKey);
}
}
static void _InsertString(HMENU hMenu, int pos, char *string, int id, int checked=0, int disabled=0)
{
MENUITEMINFO inf={sizeof(inf),MIIM_ID|MIIM_STATE|MIIM_TYPE,MFT_STRING,
(checked?MFS_CHECKED:0) | (disabled?MFS_DISABLED:MFS_ENABLED), id,
};
inf.dwTypeData = string;
InsertMenuItem(hMenu,pos,TRUE,&inf);
}
static void _InsertSep(HMENU hMenu, int pos)
{
MENUITEMINFO inf={sizeof(inf),MIIM_TYPE,MFT_SEPARATOR };
InsertMenuItem(hMenu,pos,TRUE,&inf);
}
static HMENU _InsertSub(HMENU hMenu, int pos, char *string)
{
MENUITEMINFO inf={sizeof(inf),MIIM_TYPE|MIIM_SUBMENU,MFT_STRING };
inf.dwTypeData = string;
inf.hSubMenu=CreatePopupMenu();
InsertMenuItem(hMenu,pos,TRUE,&inf);
return inf.hSubMenu;
}
#define MENUITEM_ABOUT 1
#define MENUITEM_INFOBASE 10
#define MENUITEM_VOLUMEBASE 100
#define MENUITEM_VSYNC 120
#define MENUITEM_ASPECTADJ 121
#define MENUITEM_VOVERLAYS 122
#define MENUITEM_VDDRAW 123
#define MENUITEM_SOFTVOLMIX 124
#define MENUITEM_ENABLESUBTITLES 125
#define MENUITEM_SUBSIZEBASE 126 // 5 sizes
#define MENUITEM_ZOOM50 112
#define MENUITEM_ZOOM100 113
#define MENUITEM_ZOOM200 114
#define MENUITEM_ZOOM400 115
#define MENUITEM_ZOOMFS 116
#define MENUITEM_CLOSE 200
#define MENUITEM_SUBSLANGBASE 300
extern int g_audio_use_mixer;
LRESULT my_wndcallback(void *token, HWND hwnd, UINT uMsg, WPARAM wParam, LPARAM lParam)
{
if (token)
{
parms *parm=(parms*)token;
#ifndef WNDMENU_NOPOSITIONSAVE
if (uMsg == WM_MOVE)
{
if (!parm->vidOut->is_fullscreen())
{
RECT r;
GetWindowRect(hwnd,&r);
_WriteConfigItemInt("xpos",r.left);
_WriteConfigItemInt("ypos",r.top);
}
}
#endif
if(uMsg==WM_SETCURSOR && parm->vidOut->is_fullscreen()) {
SetCursor(NULL);
return TRUE;
}
if (uMsg == WM_RBUTTONUP && !parm->vidOut->is_fullscreen())
{
int pos=0;
HMENU hMenu=CreatePopupMenu();
#ifndef WNDMENU_NOABOUT
_InsertString(hMenu,pos++,WNDMENU_CAPTION,MENUITEM_ABOUT);
_InsertSep(hMenu,pos++);
#endif
#ifndef WNDMENU_NOINFO
if (parm->decode)
{
char buf[1024];
int subpos=0;
int tmpid=MENUITEM_INFOBASE;
HMENU sub=_InsertSub(hMenu,pos++,"Info");
{ // title
char *v=parm->decode->getTitle();
if (v && *v)
{
char *tmp=(char*)malloc(strlen(v)+32);
wsprintf(tmp,"Title: %s",v);
char *p=tmp;
while (*p)
{
if (*p == '_') *p=' ';
p++;
}
_InsertString(sub,subpos++,tmp,tmpid++);
free(tmp);
}
}
{ // length
int lenms=parm->decode->getlen();
if (lenms != ~0)
{
wsprintf(buf,"Length: %d:%02d",lenms/60000,(lenms/1000)%60);
_InsertString(sub,subpos++,buf,tmpid++);
}
}
{ // codecs
int a=(parm->decode->getBitrate()+500)/1000;
unsigned int a2=parm->decode->getFileSize();
if (a)
{
wsprintf(buf,"Bitrate: %dkbps",a);
if (a2 && a2 != ~0)
wsprintf(buf+strlen(buf)," (Total %u bytes)",a2);
_InsertString(sub,subpos++,buf,tmpid++);
}
else if (a2 && a2 != ~0)
{
wsprintf(buf,"Size: %u bytes",a2);
_InsertString(sub,subpos++,buf,tmpid++);
}
lstrcpy(buf,"Video: ");
char *p=buf+strlen(buf);
parm->decode->getVideoDesc(p);
if (*p) _InsertString(sub,subpos++,buf,tmpid++);
lstrcpy(buf,"Audio: ");
p=buf+strlen(buf);
parm->decode->getAudioDesc(p);
if (*p) _InsertString(sub,subpos++,buf,tmpid++);
}
{
const char *p=parm->decode->getServerHeader("Server");
if (p && *p)
{
lstrcpy(buf,"Server: ");
lstrcpyn(buf+strlen(buf),p,sizeof(buf)-strlen(buf)-2);
_InsertString(sub,subpos++,buf,tmpid++);
}
}
}
#endif //WNDMENU_NOINFO
#ifndef WNDMENU_NOVOLUME
if (parm->decode)
{
HMENU sub=_InsertSub(hMenu,pos++,"Volume");
int cv=(parm->decode->getvolume()*100)/255;
int x;
for (x=0;x<=10;x++)
{
char buf[64];
int a=100-x*10;
wsprintf(buf,"%d%%",a);
_InsertString(sub,x,buf,MENUITEM_VOLUMEBASE+x,cv >= a-5 && cv < a+5);
}
}
#endif//WNDMENU_NOVOLUME
#ifndef WNDMENU_NOOPTIONS
if (parm->vidOut)
{
HMENU sub=_InsertSub(hMenu,pos++,"Options");
int subpos=0;
_InsertString(sub,subpos++,"Synchronize video to refresh",MENUITEM_VSYNC,!!parm->vidOut->vid_vsync);
_InsertString(sub,subpos++,"Maintain video aspect ratio",MENUITEM_ASPECTADJ,!!parm->vidOut->vid_aspectadj);
_InsertString(sub,subpos++,"Allow video overlay",MENUITEM_VOVERLAYS,!!parm->vidOut->vid_overlays);
//_InsertString(sub,subpos++,"Allow DirectDraw acceleration",MENUITEM_VDDRAW,!!parm->vidOut->vid_ddraw);
_InsertString(sub,subpos++,"Use software volume mixing",MENUITEM_SOFTVOLMIX,!g_audio_use_mixer);
}
#endif//WNDMENU_NOOPTIONS
#if !defined(WNDMENU_NOZOOM) || !defined(WNDMENU_NOZOOMFS)
if (parm->decode && parm->vidOut)
{
HMENU sub=_InsertSub(hMenu,pos++,"Zoom");
int subpos=0;
#ifndef WNDMENU_NOZOOM
int ow,oh;
int w=parm->decode->getWidth();
int h=parm->decode->getHeight();
parm->vidOut->getOutputSize(&ow,&oh);
_InsertString(sub,subpos++,"50%",MENUITEM_ZOOM50,ABS(w/2-ow)<4);
_InsertString(sub,subpos++,"100%",MENUITEM_ZOOM100,ABS(w-ow)<4);
_InsertString(sub,subpos++,"200%",MENUITEM_ZOOM200,ABS(w*2-ow)<4);
_InsertString(sub,subpos++,"400%",MENUITEM_ZOOM400,ABS(w/4-ow)<4);
#ifndef WNDMENU_NOZOOMFS
_InsertSep(sub,subpos++);
#endif
#endif
#ifndef WNDMENU_NOZOOMFS
_InsertString(sub,subpos++,"Fullscreen",MENUITEM_ZOOMFS,!!parm->vidOut->is_fullscreen());
#endif//WNDMENU_NOZOOMFS
}
#endif//WNDMENU_NOZOOM||WNDMENU_NOZOOMFS
#ifndef WNDMENU_NOSUBTITLES
if(parm->decode)
{
HMENU sub=_InsertSub(hMenu,pos++,"Subtitles");
int subpos=0;
_InsertString(sub,subpos++,"Enabled",MENUITEM_ENABLESUBTITLES,parm->decode->subsEnabled());
_InsertSep(sub,subpos++);
HMENU sizesub=_InsertSub(sub,subpos++,"Font size");
int sizesubpos=0;
int fontsize=parm->decode->getSubsFontSize();
_InsertString(sizesub,sizesubpos++,"Largest",MENUITEM_SUBSIZEBASE,fontsize==20);
_InsertString(sizesub,sizesubpos++,"Larger",MENUITEM_SUBSIZEBASE+1,fontsize==10);
_InsertString(sizesub,sizesubpos++,"Medium",MENUITEM_SUBSIZEBASE+2,fontsize==0);
_InsertString(sizesub,sizesubpos++,"Smaller",MENUITEM_SUBSIZEBASE+3,fontsize==-4);
_InsertString(sizesub,sizesubpos++,"Smallest",MENUITEM_SUBSIZEBASE+4,fontsize==-8);
HMENU langsub=_InsertSub(sub,subpos++,"Language");
int langsubpos=0;
for(int i=0;;i++) {
const char *lang=parm->decode->getSubLanguage(i);
if(!lang) break;
_InsertString(langsub,langsubpos++,(char *)lang,MENUITEM_SUBSLANGBASE+i,i==parm->decode->getCurSubLanguage());
}
}
#endif//WNDMENU_NOSUBTITLES
#ifndef WNDMENU_NOCLOSE
_InsertSep(hMenu,pos++);
_InsertString(hMenu,pos++,"E&xit\tAlt+F4",MENUITEM_CLOSE);
#endif//WNDMENU_NOCLOSE
POINT p;
GetCursorPos(&p);
int x=TrackPopupMenu(hMenu,TPM_RETURNCMD|TPM_RIGHTBUTTON|TPM_LEFTBUTTON|TPM_NONOTIFY,p.x,p.y,0,hwnd,NULL);
DestroyMenu(hMenu);
switch (x)
{
#ifndef WNDMENU_NOABOUT
case MENUITEM_ABOUT:
#ifdef _ABOUT_H_ // dont display the about box if do_about isn't there anyway
do_about(hwnd);
#endif
break;
#endif//WNDMENU_NOABOUT
#ifndef WNDMENU_NOOPTIONS
case MENUITEM_VOVERLAYS:
_WriteConfigItemInt("overlays",parm->vidOut->vid_overlays=!parm->vidOut->vid_overlays);
PostMessage(hwnd,WM_USER+0x1,0,0); //renegotiate surface
break;
case MENUITEM_VDDRAW:
_WriteConfigItemInt("ddraw",parm->vidOut->vid_ddraw=!parm->vidOut->vid_ddraw);
PostMessage(hwnd,WM_USER+0x1,0,0); //renegotiate surface
break;
case MENUITEM_ASPECTADJ:
_WriteConfigItemInt("aspectadj",parm->vidOut->vid_aspectadj=!parm->vidOut->vid_aspectadj);
PostMessage(hwnd,WM_TIMER,1,0);
break;
case MENUITEM_VSYNC:
_WriteConfigItemInt("vsync",parm->vidOut->vid_vsync=!parm->vidOut->vid_vsync);
break;
case MENUITEM_SOFTVOLMIX:
_WriteConfigItemInt("use_mixer",g_audio_use_mixer=!g_audio_use_mixer);
parm->decode->setvolume(parm->decode->getvolume());
break;
#endif//WNDMENU_NOOPTIONS
#ifndef WNDMENU_NOZOOM
case MENUITEM_ZOOM50:
case MENUITEM_ZOOM100:
case MENUITEM_ZOOM200:
case MENUITEM_ZOOM400:
if (parm->vidOut->is_fullscreen()) parm->vidOut->remove_fullscreen();
{
int w=parm->decode->getWidth();
int h=parm->decode->getHeight();
if (x == MENUITEM_ZOOM50) { w/=2; h/=2; }
else if (x == MENUITEM_ZOOM200) { w*=2; h*=2; }
else if (x == MENUITEM_ZOOM400) { w*=4; h*=4; }
parm->vidOut->setOutputSize(w,h);
}
break;
#endif//WNDMENU_NOZOOM
#ifndef WNDMENU_NOZOOMFS
case MENUITEM_ZOOMFS:
if (parm->vidOut->is_fullscreen()) parm->vidOut->remove_fullscreen();
else parm->vidOut->fullscreen();
break;
#endif//WNDMENU_NOZOOMFS
#ifndef WNDMENU_NOCLOSE
case MENUITEM_CLOSE:
SendMessage(hwnd,WM_CLOSE,0,0);
break;
#endif//WNDMENU_NOCLOSE
#ifndef WNDMENU_NOSUBTITLES
case MENUITEM_ENABLESUBTITLES:
if(parm->decode) parm->decode->enableSubs(!parm->decode->subsEnabled());
_WriteConfigItemInt("subtitles",parm->decode->subsEnabled());
break;
case MENUITEM_SUBSIZEBASE:
case MENUITEM_SUBSIZEBASE+1:
case MENUITEM_SUBSIZEBASE+2:
case MENUITEM_SUBSIZEBASE+3:
case MENUITEM_SUBSIZEBASE+4:
if(parm->decode) {
int sizes[]={20,10,0,-4,-8};
int s=x-MENUITEM_SUBSIZEBASE;
parm->decode->setSubsFontSize(sizes[s]);
_WriteConfigItemInt("subtitles_size",parm->decode->getSubsFontSize());
}
break;
#endif//WNDMENU_NOSUBTITLES
default:
#ifndef WNDMENU_NOVOLUME
if (x >= MENUITEM_VOLUMEBASE && x <= MENUITEM_VOLUMEBASE+10)
{
int v=10-(x-MENUITEM_VOLUMEBASE);
parm->decode->setvolume((v*255)/10);
_WriteConfigItemInt("volume",parm->decode->getvolume());
}
#endif
break;
#ifndef WNDMENU_NOCLOSE
case WM_CLOSE:
DestroyWindow(hwnd);
return 0;
#endif
}
#ifndef WNDMENU_NOSUBTITLES
if(x>=MENUITEM_SUBSLANGBASE && x<=MENUITEM_SUBSLANGBASE+64) {
parm->decode->setSubLanguage(x-MENUITEM_SUBSLANGBASE);
}
#endif
}
}
return DefWindowProc(hwnd,uMsg,wParam,lParam);
}
#endif//_WNDMENU_H_