diff options
Diffstat (limited to 'Src/nsv')
37 files changed, 10467 insertions, 0 deletions
diff --git a/Src/nsv/dec_if.h b/Src/nsv/dec_if.h new file mode 100644 index 00000000..d0232327 --- /dev/null +++ b/Src/nsv/dec_if.h @@ -0,0 +1,98 @@ +/* +** dec_if.h - common decoder interface for nsvplay/nsvplayx +** +** Copyright (C) 2001-2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +** +*/ + +#ifndef _NSV_DEC_IF_H_ +#define _NSV_DEC_IF_H_ + +#ifndef _WA_IPC_H_ // these are also defined in Winamp/wa_ipc.h +struct YV12_PLANE { + unsigned char* baseAddr; + long rowBytes; +} ; + +struct YV12_PLANES { + YV12_PLANE y; + YV12_PLANE u; + YV12_PLANE v; +} ; +#endif + +class IVideoDecoder +{ + public: + virtual ~IVideoDecoder() { } + + // decode returns 0 on success + // but *out can be NULL, meaning no new frame is available, + // but all is well + virtual 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)=0; + virtual void flush()=0; +}; + +class IAudioDecoder +{ + public: + virtual ~IAudioDecoder() { } + + // returns -1 on error, 0 on success (done with data in 'in'), 1 on success + // but to pass 'in' again next time around. + virtual int decode(void *in, int in_len, + void *out, int *out_len, // out_len is read and written to + unsigned int out_fmt[8])=0; // out_fmt is written to + // ex: 'PCM ', srate, nch, bps + // or 'NONE' :) + virtual void flush()=0; +}; + +class IAudioOutput +{ + public: + virtual ~IAudioOutput() { } + virtual int canwrite()=0; // returns bytes writeable + virtual void write(void *buf, int len)=0; + virtual unsigned long long getpos()=0; + virtual unsigned long long getwritepos()=0; + virtual void flush(unsigned int newtime)=0; + virtual int isplaying(void) { return 1; } + virtual void pause(int pause) { } + virtual void setvolume(int volume) { } + virtual void setpan(int pan) { } + virtual void getdescstr(char *buf) { *buf=0; } +}; + + +/* +** The DLL must export one of these symbols (unmangled, i.e. using extern "C). +** +** IAudioDecoder *CreateAudioDecoder(unsigned int type, IAudioOutput **output); +** IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int type, int *flip); +** +** the functions should return NULL if the conversion is not possible (or +** is not the correct format) +** +** The DLL must be in <program files>\common files\nsv, and must be named +** nsvdec_*.dll. +** +*/ + +#endif//_NSV_DEC_IF_H_ diff --git a/Src/nsv/enc_if.h b/Src/nsv/enc_if.h new file mode 100644 index 00000000..65daf32f --- /dev/null +++ b/Src/nsv/enc_if.h @@ -0,0 +1,44 @@ +/* +** enc_if.h - common encoder interface +** +** Copyright (C) 2001-2003 Nullsoft, Inc. +** +** This software is provided 'as-is', without any express or implied warranty. +** In no event will the authors be held liable for any damages arising from the use of this software. +** +** Permission is granted to anyone to use this software for any purpose, including commercial +** applications, and to alter it and redistribute it freely, subject to the following restrictions: +** 1. The origin of this software must not be misrepresented; you must not claim that you wrote the +** original software. If you use this software in a product, an acknowledgment in the product +** documentation would be appreciated but is not required. +** 2. Altered source versions must be plainly marked as such, and must not be misrepresented as +** being the original software. +** 3. This notice may not be removed or altered from any source distribution. +*/ + +#ifndef _NSV_ENC_IF_H_ +#define _NSV_ENC_IF_H_ +class VideoCoder +{ + public: + VideoCoder() { } + virtual int Encode(void *in, void *out, int *iskf)=0; // returns bytes in out + virtual ~VideoCoder() { }; +}; + +class AudioCoder +{ + public: + AudioCoder() { } + virtual int Encode(int framepos, void *in, int in_avail, int *in_used, void *out, int out_avail)=0; //returns bytes in out + virtual ~AudioCoder() { }; +}; + +// unsigned int GetAudioTypes3(int idx, char *desc); +// unsigned int GetVideoTypes3(int idx, char *desc); +// AudioCoder *CreateAudio3(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile); +// VideoCoder *CreateVideo3(int w, int h, double frt, unsigned int pixt, unsigned int *outt, char *configfile); +// HWND ConfigAudio3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); +// HWND ConfigVideo3(HWND hwndParent, HINSTANCE hinst, unsigned int outt, char *configfile); + +#endif //_NSV_ENC_IF_H_ diff --git a/Src/nsv/nsvbs.h b/Src/nsv/nsvbs.h new file mode 100644 index 00000000..9ec59ef1 --- /dev/null +++ b/Src/nsv/nsvbs.h @@ -0,0 +1,202 @@ +/* +** nsvbs.h - NSV basic inline bitstream classes +** +** Copyright (C) 2001-2002 Nullsoft, Inc. +** Confidential Subject to NDA +** +** Note: these bitstream classes encode/decode everything in LSB. +** bits are stored from the lowest bit to the highest bit. +** putbits(4,0xE) will result in getbits(1)=0, getbits(1)=1, +** getbits(1)=1, getbits(1)=1 +** or of course, getbits(4) == 0xE :) +*/ + +#ifndef _NSVBS_H_ +#define _NSVBS_H_ + +#include <stdlib.h> +#include <memory.h> +#include <bfc/platform/types.h> +#include "../nu/GrowBuf.h" + +class nsv_OutBS +{ +public: + nsv_OutBS() { m_used = 0; m_curb=0; } + ~nsv_OutBS() { } + + void putbits(int nbits, unsigned int value) + { + while (nbits-- > 0) + { + m_curb|=(value&1)<<(m_used&7); + if (!((++m_used)&7)) + { + m_bits.add(&m_curb,1); + m_curb=0; + } + value>>=1; + } + } + + // lets you put in any amount of data, but does not preserve endianness. + void putdata(int nbits, void *data) + { + unsigned char *c=(unsigned char *)data; + if (!(m_used&7) && nbits >= 8) + { + m_bits.add(c,nbits/8); + c+=nbits/8; + m_used+=nbits&~7; + nbits&=7; + } + while (nbits > 0) + { + int tb=nbits; + if (tb > 8) tb=8; + putbits(tb,*c++); + nbits-=tb; + } + } + + int getlen() { return m_used; } // in bits + + void *get(int *len) // len is in bytes, forces to byte aligned. + { + if (m_used&7) + { + m_bits.add(&m_curb,1); + m_used=(m_used+7)&~7; + m_curb=0; + } + *len=m_used/8; + return m_bits.get(); + } + + void clear() + { + m_used=0; + m_curb=0; + m_bits.resize(0); + } + +private: + GrowBuf m_bits; + int m_used; // bits + unsigned char m_curb; +}; + +class nsv_InBS { +public: + nsv_InBS() { m_bitpos=0; m_eof=0; } + ~nsv_InBS() { } + + void clear() + { + m_eof=0; + m_bitpos=0; + m_bits.resize(0); + } + + void add(void *data, int len) + { + m_bits.add(data,len); + m_eof=0; + } + + void addbyte(unsigned char byte) + { + add(&byte,1); + } + + void addint(unsigned int dword) + { + addbyte(dword&0xff); + addbyte((dword>>8)&0xff); + addbyte((dword>>16)&0xff); + addbyte((dword>>24)&0xff); + } + + void compact() + { + size_t bytepos=m_bitpos/8; + if (bytepos) + { + unsigned char *t=(unsigned char *)m_bits.get(); + size_t l=m_bits.getlen()-bytepos; + memcpy(t,t+bytepos,l); + m_bits.resize(l); + m_bitpos&=7; + } + m_eof=0; + } + + void seek(ptrdiff_t nbits) + { + if (nbits < 0 && ((size_t)(-nbits)) > m_bitpos) + m_bitpos=0; + else + m_bitpos+=nbits; + m_eof=m_bits.getlen()*8 < m_bitpos; + } + + void rewind() { m_bitpos=0; m_eof=0; } + int eof() { return m_eof; } + size_t avail() { if (m_eof) return 0; return m_bits.getlen()*8 - m_bitpos; } + + unsigned int getbits(size_t nbits) + { + unsigned int ret=0; + if (!nbits) return ret; + unsigned char *t=(unsigned char *)m_bits.get(); + + if (!t || m_bits.getlen()*8 < m_bitpos+nbits) m_eof=1; + else + { + t+=m_bitpos/8; + for (size_t sh = 0; sh != nbits; sh ++) + { + ret|=((*t>>(m_bitpos&7))&1) << sh; + if (!((++m_bitpos)&7)) t++; + } + } + return ret; + } + + int getdata(size_t nbits, void *data) + { + unsigned char *t=(unsigned char *)data; + if (m_bits.getlen()*8 < m_bitpos+nbits) return 1; + if (!(m_bitpos&7) && nbits >= 8) + { + char *bitptr=(char*)m_bits.get(); + bitptr+=(m_bitpos/8); + memcpy(t,bitptr,nbits/8); + m_bitpos+=nbits&~7; + + t+=nbits/8; + nbits&=7; + } + while (nbits > 0) + { + size_t nb=nbits; + if (nb > 8) nb=8; + *t++=getbits(nb); + nbits-=nb; + } + return 0; + } + + void *getcurbyteptr() + { + char *t=(char*)m_bits.get(); + return (void *)(t+(m_bitpos/8)); + } + +private: + GrowBuf m_bits; + size_t m_bitpos; + int m_eof; +}; + +#endif//_NSVBS_H_
\ No newline at end of file diff --git a/Src/nsv/nsvencode.h b/Src/nsv/nsvencode.h new file mode 100644 index 00000000..39f1f907 --- /dev/null +++ b/Src/nsv/nsvencode.h @@ -0,0 +1,221 @@ +#ifndef _NSVENCODE_H_ +#define _NSVENCODE_H_ + +#include "../jnetlib/jnetlib.h" +#include "nsvlib.h" +#include "enc_if.h" +#include <stdio.h> + +class nsv_Encoder +{ + public: + nsv_Encoder(); + ~nsv_Encoder(); + + + static void freeCoders(); // call this before quitting if you used any nsvencoders + static void loadCoders(); + static HWND configAudio(unsigned int audfmt, HWND hwndParent, char *configfile); + static HWND configVideo(unsigned int vidfmt, HWND hwndParent, char *configfile); + static int enumAudio(int *state, char *name, unsigned int *fmt); + static int enumVideo(int *state, char *name, unsigned int *fmt); + + + char *getError() { return m_err; } // NULL on no error + + // compressor configuration (do not call these after calling openOutput()) + void setVideo(unsigned int vfmt, unsigned int w, unsigned int h, double frt, + unsigned int srcfmt=NSV_MAKETYPE('R','G','B','A'), + char *configfile=NULL); + void setAudio(unsigned int afmt, int srate, int nch, int bps, + unsigned int srcfmt=NSV_MAKETYPE('P','C','M',' '), + char *configfile=NULL); + + // stream configuration (do not call these after calling openOutput()) + void setMaxAudioSendAhead(int offs) { m_audio_ahead_of_video=offs; } + void setSyncFrameInterval(int minimum=0, int maximum=120) { m_max_syncframe_dist=maximum; m_min_syncframe_dist=minimum; } + + // setting one of these will enable file header writing on files + // do NOT call these after calling openOutput(), + int addHdrMetaData(char *name, char *data, int data_len); // returns nonzero on error + int setHdrMetaData(char *data, int data_len); // nonzero on error + void setHdrTOCSize(int tocsize); + void forceHdrWrite() { m_usefilehdr=1; } // use if you dont want meta or toc, but want a basic header + + // output (call only once, do not close and reopen (yet)) + void openOutput(char *url); // call getError to check for error + void closeOutput(); + + // encode (call only after openoutput()) + void addAudioData(void *data, int data_len); + int needAudioData(); // returns 1 if more audio data would be nice for sync, 2 if there is no audio data in buffer + + // for audio only, use compressor type NONE and call this with NULL + void addVideoFrame(void *frame); + // calling this will put the aux data in the same frame as the NEXT addVideoFrame() + void addAuxData(unsigned int fmt, void *data, int data_len); + + // setting audio eof will let the audio compressor know that it is the end of stream + // so that it can flush (via passing an empty frame to it) + void set_audioEof(int eof) { m_audio_eof=!!eof; } + int get_audioEof() { return m_audio_eof; } + + // stats + unsigned int getCurrentBitrate() + { + if (m_video_position_frames) + return (unsigned int) ((double) m_bitstreambytesout / ((double) m_video_position_frames / m_framerate) * 8.0); + return 0; + } + + + private: + + char *m_err; + + int m_usefilehdr; + unsigned int m_tocsize; + int m_audio_channels; + int m_audio_samplerate; + int m_audio_bits; + unsigned int m_audio_srcfmt; + unsigned int m_audio_coder; + char *m_audio_configfile; + + + double m_framerate; + unsigned int m_w, m_h; + unsigned int m_video_coder; + unsigned int m_video_srcfmt; + char *m_video_configfile; + + int m_audio_ahead_of_video; + + int m_max_syncframe_dist,m_min_syncframe_dist; + nsv_fileHeader m_filehdr; + + FILE *m_outfile; + JNL_Connection *m_outcon; + int m_is_uvox; // for m_outco + char *m_outcon_host,*m_outcon_headers; + int m_outcon_port; + unsigned int m_outcon_last_connect_time; + + nsv_Packeter m_packeter; + nsv_InBS m_outconbs; + __int64 m_bitstreambytesout; + + AudioCoder *m_audcoder; + VideoCoder *m_vidcoder; + + __int64 m_audio_position_bytes; + __int64 m_video_position_frames; + char m_vidobuf[NSV_MAX_VIDEO_LEN]; + char m_audobuf[NSV_MAX_AUDIO_LEN]; + nsv_InBS m_audibuf; + unsigned int m_total_frames; + nsv_GrowBuf m_tocEntries; + int m_frames_since_last_sync_frame; + int m_audio_eof; + + void send_uvoxmessage(int msgclass, int msgtype, void *msg, int msglen); + + + static VideoCoder *init_video(int w, int h, double frt, unsigned int pixt, unsigned int *outt, char *configfile); + static AudioCoder *init_audio(int nch, int srate, int bps, unsigned int srct, unsigned int *outt, char *configfile); + static int Coders_loaded; + static HINSTANCE g_Coders[256]; + static int g_numCoders; + static char g_dll_dir[MAX_PATH]; + static BOOL CALLBACK PcmProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + + + +}; + + + +class nsv_EncoderConfigFile +{ + public: + + nsv_EncoderConfigFile(char *filename=NULL) + { + m_filename=filename?_strdup(filename):0; + m_audfmt=m_vidfmt=0; + m_minsync=0; + m_maxsync=120; + m_audiosendahead=0; + m_hdrwrite=0; + m_hdrtoc=1024; + m_metadata=0; + m_crop_l=0; // ## + m_crop_r=0; // ## + m_crop_t=0; // ## + m_crop_b=0; // ## + + ReadIn(); + } + + + ~nsv_EncoderConfigFile(); //ak 7-23-03 moved so breakpoint can be set + unsigned int getVidFmt() { return m_vidfmt; } + unsigned int getAudFmt() { return m_audfmt; } + void setVidFmt(unsigned int fmt) { m_vidfmt=fmt; } + void setAudFmt(unsigned int fmt) { m_audfmt=fmt; } + + char *getFilename() { return m_filename; } + void setFilename(char *filename) + { + FlushOut(); + free(m_filename); + if (filename) + { + m_filename=_strdup(filename); + ReadIn(); + } + else m_filename=0; + } + + int getMinSyncFrameInt() { return m_minsync; } + void setMinSyncFrameInt(int minint) { m_minsync=minint; } + int getMaxSyncFrameInt() { return m_maxsync; } + void setMaxSyncFrameInt(int maxint) { m_maxsync=maxint; } + int getAudioSendAhead() { return m_audiosendahead; } + void setAudioSendAhead(int sa) { m_audiosendahead=sa; } + + char *hdrGetMetadata() { return m_metadata; } + int hdrGetDoWrite() { return m_hdrwrite; } + int hdrSetDoWrite(int dw) { m_hdrwrite=!!dw; } + int hdrGetTOCSize() { return m_hdrtoc; } + int hdrSetTOCSize(int ts) { m_hdrtoc=ts; } + + int ConfigUI(HINSTANCE hInstance, HWND hwndParent); + + private: + + void FlushOut(); + void ReadIn(); + + static BOOL CALLBACK _dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + BOOL dialogProc(HWND hwndDlg, UINT uMsg, WPARAM wParam,LPARAM lParam); + HWND audio_hwnd, video_hwnd; + void initConfigChildWindows(HWND hwndDlg, int flags); + + char *m_filename; + + unsigned int m_audfmt,m_vidfmt; + int m_minsync, m_maxsync; + int m_audiosendahead; + int m_hdrwrite; + int m_hdrtoc; + char *m_metadata; + + public: + int m_crop_l; // ## + int m_crop_r; // ## + int m_crop_t; // ## + int m_crop_b; // ## +}; + +#endif//_NSVENCODE_H_
\ No newline at end of file diff --git a/Src/nsv/nsvlib.cpp b/Src/nsv/nsvlib.cpp new file mode 100644 index 00000000..e26bef2f --- /dev/null +++ b/Src/nsv/nsvlib.cpp @@ -0,0 +1,737 @@ +/* +** nsvlib.cpp - NSV file/bitstream reading/writing code +** +** Copyright (C) 2001-2002 Nullsoft, Inc. +** +** Confidential Subject to NDA +*/ + +#include <stdio.h> +#include <stdlib.h> +#include <string.h> +#include <bfc/platform/strcmp.h> + +#include "nsvlib.h" + +#define NSV_HDR_DWORD (NSV_MAKETYPE('N','S','V','f')) + +#define NSV_SYNC_HEADERLEN_BITS 192 +#define NSV_SYNC_DWORD (NSV_MAKETYPE('N','S','V','s')) +#define NSV_NONSYNC_HEADERLEN_BITS 56 +#define NSV_NONSYNC_WORD 0xBEEF + +#define NSV_INVALID_SYNC_OFFSET 0x80000000 + +long glSyncFrameCount = 0l; +long glCounterNSVf = 0l; +long glNonSyncFrameCount = 0l; + +/* + NSV sync packet header + 32 bits: NSV_SYNC_DWORD + 32 bits: video format + 32 bits: audio format + 16 bits: width + 16 bits: height + 8 bits: framerate (see getfrate/setfrate) + + + 16 bits: audio/video sync offset + +or + + NSV nonsync packet header + 16 bits: NSV_NONSYNC_WORD + +then + +4 bits: # aux data channels present (max 15) +20 bits: video data + aux channels length +16 bits: audio data length + +-------------------------------- +sync: + 192 bit header, + 136 bits are invariant +nonsync: + 56 bit header + 16 bits are invariant + +*/ + + + + +static int is_type_char_valid(int c) +{ + c&=0xff; + return (c >= 'a' && c <= 'z') || + (c >= 'A' && c <= 'Z') || + (c >= '0' && c <= '9') || + c == ' ' || c == '-' || + c == '.' || c == '_'; +} + +static int is_type_valid(unsigned int t) +{ + return (t&0xff) != ' ' && + is_type_char_valid(t>>24) && + is_type_char_valid(t>>16) && + is_type_char_valid(t>>8) && + is_type_char_valid(t); +} + + +void nsv_type_to_string(unsigned int t, char *out) +{ + if (is_type_valid(t)) + { + out[0]=(t)&0xff; + out[1]=(t>>8)&0xff; + out[2]=(t>>16)&0xff; + out[3]=(t>>24)&0xff; + out[4]=0; + int x=3; + while (out[x]==' ' && x > 0) out[x--]=0; + } + else *out=0; +} + +unsigned int nsv_string_to_type(char *in) +{ + int n; + unsigned int ret=*in; + if (*in == ' ' || !is_type_char_valid(*in)) return 0; + in++; + for (n = 0; n < 3; n ++) + { + if (!is_type_char_valid(*in)) break; + ret|=(*in<<(8+8*n)); + in++; + } + if (*in) return 0; + return ret; +} + +// frate is specified +// XYYYYYZZ +// if !X, framerate is YYYYYZZ (1-127) +// otherwise: +// ZZ indexes base +// YYYYY is scale (0-32). +// if YYYYY < 16, then scale = 1/(YYYY+1) +// otherwise scale = YYYYY-15 + + +static double frate2double(unsigned char fr) +{ + static double fratetab[]= + { + 30.0, + 30.0*1000.0/1001.0, + 25.0, + 24.0*1000.0/1001.0, + }; + if (!(fr&0x80)) return (double)fr; + + double sc; + int d=(fr&0x7f)>>2; + if (d < 16) sc=1.0/(double)(d+1); + else sc=d-15; + + return fratetab[fr&3]*sc; +} + + + +static unsigned char double2frate(double fr) +{ + int best=0; + double best_v=1000000.0; + int x; + for (x = 0; x < 256; x ++) + { + double this_v=(fr-frate2double(x)); + + if (this_v<0) this_v=-this_v; + if (this_v < best_v) + { + best_v=this_v; + best=x; + } + } + return (unsigned char) best; +} + + + +nsv_Packeter::nsv_Packeter() +{ + vidfmt=audfmt=0; + width=height=0; + framerate_idx=0; + framerate=0.0; + syncoffset_cur=0; + video=NULL; + audio=NULL; + video_len=0; + audio_len=0; + aux_used=0; +} + +void nsv_Packeter::setVidFmt(unsigned int vfmt, unsigned int w, unsigned int h, double frt) +{ + vidfmt=vfmt; + width=w; + height=h; + framerate=frt; + framerate_idx=double2frate(frt); +} + +nsv_Packeter::~nsv_Packeter() +{ +} + +int nsv_Packeter::packet(nsv_OutBS &bs) +{ + int total_auxlen=0; + int x; + if (width >= (1<<16) || height >= (1<<16) || + !framerate_idx || framerate_idx > 255 || + !is_type_valid(audfmt) || + !is_type_valid(vidfmt) || + video_len > NSV_MAX_VIDEO_LEN || + audio_len > NSV_MAX_AUDIO_LEN || + aux_used > NSV_MAX_AUXSTREAMS || + aux_used < 0 + ) return -1; + + for (x = 0; x < aux_used; x ++) + { + if (aux_len[x] > NSV_MAX_AUX_LEN) return -1; + total_auxlen+=aux_len[x]+6; + } + + if (is_sync_frame) + { + bs.putbits(32,NSV_SYNC_DWORD); + bs.putbits(32,vidfmt); + bs.putbits(32,audfmt); + bs.putbits(16,width); + bs.putbits(16,height); + bs.putbits(8 ,framerate_idx); + bs.putbits(16,syncoffset_cur); + } + else + { + bs.putbits(16,NSV_NONSYNC_WORD); + } + + bs.putbits(4,aux_used); // no aux data channels for our streams yet + bs.putbits(20,video_len+total_auxlen); + bs.putbits(16,audio_len); + + for (x = 0; x < aux_used; x ++) + { + bs.putbits(16,aux_len[x]); // length of 0 for aux channels + bs.putbits(32,aux_types[x]); + if (aux_len[x]) bs.putdata(aux_len[x]*8,aux[x]); + } + + if (video_len) bs.putdata(video_len*8,video); + if (audio_len) bs.putdata(audio_len*8,audio); + + return 0; +} + + +void nsv_Unpacketer::reset(int full) +{ + synched=0; + is_sync_frame=0; + syncoffset_cur=0; + syncoffset=NSV_INVALID_SYNC_OFFSET; + + if (full) + { + m_auxbs=NULL; + m_audiobs=NULL; + m_videobs=NULL; + m_eof=0; + vidfmt=0; + audfmt=0; + valid=0; + width=0; + height=0; + framerate=0.0; + framerate_idx=0; + } +} + + +// returns 0 on success, >0 on needs (at least X bytes) more data, +// -1 on error (no header found in block) +int nsv_Unpacketer::unpacket(nsv_InBS &bs) +{ + int gotframe=0; + unsigned int num_aux=0; + unsigned int vl=0; + unsigned int al=0; + + while (bs.avail()>=NSV_NONSYNC_HEADERLEN_BITS) + { + if (valid && synched) + { + if (bs.avail() < NSV_NONSYNC_HEADERLEN_BITS) + return m_eof?-1:(NSV_NONSYNC_HEADERLEN_BITS- (int)(bs.avail())/8); + + unsigned int d=bs.getbits(16); + if (d == NSV_NONSYNC_WORD) + { + glNonSyncFrameCount++; + num_aux=bs.getbits(4); + vl=bs.getbits(20); + al=bs.getbits(16); + if (al >= NSV_MAX_AUDIO_LEN || + vl >= (NSV_MAX_VIDEO_LEN+num_aux*(NSV_MAX_AUX_LEN+6))) + { + bs.seek(-NSV_NONSYNC_HEADERLEN_BITS); + } + else + { + if ((unsigned int)bs.avail() < 8*(vl+al)+(m_eof?0:32)) + { + int l=(al+vl+32/8)- (int)(bs.avail()/8); + bs.seek(-NSV_NONSYNC_HEADERLEN_BITS); + return m_eof?-1:l; + } + + if ((unsigned int)bs.avail() >= 8*(vl+al)+32) + { + bs.seek(8*(vl+al)); + unsigned int a32=bs.getbits(32); + bs.seek(-32); + unsigned int a16=bs.getbits(16); + bs.seek(-16); + bs.seek(-8*(vl+al)); + if (a16 != NSV_NONSYNC_WORD && a32 != NSV_SYNC_DWORD) + { + bs.seek(-NSV_NONSYNC_HEADERLEN_BITS); + } + else gotframe=NSV_NONSYNC_HEADERLEN_BITS; + } + else gotframe=NSV_NONSYNC_HEADERLEN_BITS; + } + } + else bs.seek(-16); + } // inf.valid && inf.synched + + // gotframe is set if we successfully got a nonsync frame, otherwise + // let's see if we can't interpret this as a sync frame + + if (!gotframe) + { + if (bs.avail() < NSV_SYNC_HEADERLEN_BITS) + return (int)(m_eof?-1:(NSV_SYNC_HEADERLEN_BITS-(bs.avail())/8)); + unsigned int d=bs.getbits(32); + if (d != NSV_SYNC_DWORD) + { + bs.seek(8-32); // seek back 3 bytes + synched=0; + continue; + }else{ + // count the # of sync frames (for debugging) + glSyncFrameCount++; + } + unsigned int vfmt=bs.getbits(32); + unsigned int afmt=bs.getbits(32); + unsigned int w=bs.getbits(16); + unsigned int h=bs.getbits(16); + unsigned char frt=bs.getbits(8); + unsigned int so=bs.getbits(16); + + num_aux=bs.getbits(4); + vl=bs.getbits(20); + al=bs.getbits(16); + + if (al >= NSV_MAX_AUDIO_LEN || + vl >= (NSV_MAX_VIDEO_LEN+num_aux*(NSV_MAX_AUX_LEN+6)) || + !frt || !is_type_valid(vfmt) || !is_type_valid(afmt) || + (valid && + (width != w || height != h || + vidfmt != vfmt || audfmt != afmt || framerate_idx != frt))) + { // frame is definately not valid + bs.seek(8-NSV_SYNC_HEADERLEN_BITS); // seek back what we just read + synched=0; + continue; + } + + if ((unsigned int)bs.avail() < (al+vl)*8+((m_eof||(valid&&synched))?0:32)) + { + int l=(al+vl)*8+NSV_SYNC_HEADERLEN_BITS- (int)(bs.avail()); + bs.seek(-NSV_SYNC_HEADERLEN_BITS); + return m_eof?-1:(l/8); + } + + if (valid && synched) + { + gotframe=NSV_SYNC_HEADERLEN_BITS; + } + else // we need to do more robust sync + { + int sk=(al+vl)*8; + bs.seek(sk); + unsigned int a16=bs.getbits(16); + bs.seek(-16); + unsigned int a32=bs.getbits(32); + bs.seek(-32); + if (a16 == NSV_NONSYNC_WORD) + { + sk+=16+4+20+16; + bs.seek(16); + unsigned int _num_aux=bs.getbits(4); + unsigned int _vl=bs.getbits(20); + unsigned int _al=bs.getbits(16); + if ((unsigned int)bs.avail() < (_vl+_al)*8 + 32) + { + int l=(_al+_vl+32)- (int)(bs.avail()/8); + bs.seek(-NSV_SYNC_HEADERLEN_BITS-sk); + return m_eof?-1:l; + } + bs.seek((_vl+_al)*8); + sk+=(_vl+_al)*8; + unsigned int a16=bs.getbits(16); + bs.seek(-16); + unsigned int a32=bs.getbits(32); + bs.seek(-32); + bs.seek(-sk); + if (a16 == NSV_NONSYNC_WORD || a32 == NSV_SYNC_DWORD) + gotframe=NSV_SYNC_HEADERLEN_BITS; + } + else if (a32 == NSV_SYNC_DWORD) + { + glSyncFrameCount++; + + sk+=32+32+32+16+16+8; + + bs.seek(32); + unsigned int _vfmt=bs.getbits(32); + unsigned int _afmt=bs.getbits(32); + unsigned int _w=bs.getbits(16); + unsigned int _h=bs.getbits(16); + unsigned char _frt=bs.getbits(8); + bs.seek(-sk); + + if (_vfmt==vfmt && _afmt==afmt && _w==w && _h==h && _frt==frt) // matches + { + gotframe=NSV_SYNC_HEADERLEN_BITS; + } + } + } + if (!gotframe) + { + synched=0; + bs.seek(8-NSV_SYNC_HEADERLEN_BITS); + } + else + { + if (so & 0x8000) so|=0xFFFF0000; + syncoffset_cur=so; + if (!valid || syncoffset == NSV_INVALID_SYNC_OFFSET) syncoffset=so; + if (!valid) framerate=frate2double(frt); + framerate_idx=frt; + width=w; + height=h; + audfmt=afmt; + vidfmt=vfmt; + valid=1; + synched=1; + } + } + + if (gotframe) + { + is_sync_frame = (gotframe == NSV_SYNC_HEADERLEN_BITS); + // read aux channels + int rd=gotframe; + unsigned int x; + for (x = 0; x < num_aux; x ++) + { + unsigned int l=bs.getbits(16); + unsigned int fmt=bs.getbits(32); + vl -= 4+2; + rd += 16+32; + + if (l > NSV_MAX_AUX_LEN) break; + + if (m_auxbs) + { + m_auxbs->addint(l); + m_auxbs->addint(fmt); + m_auxbs->add(bs.getcurbyteptr(),l); + } + bs.seek(l*8); // toss aux + + vl-=l; + rd+=l*8; + + if (vl<0) break; // invalid frame (aux channels add up to more than video) + } + if (x < num_aux) // oh shit, invalid frame + { + synched=0; + bs.seek(8-rd); + gotframe=0; + continue; + } + + if (m_videobs) + { + m_videobs->addint(vl); + m_videobs->add(bs.getcurbyteptr(),vl); + } + bs.seek(vl*8); + + if (m_audiobs) + { + m_audiobs->addint(al); + m_audiobs->add(bs.getcurbyteptr(),al); + } + bs.seek(al*8); + + return 0; + } + } // while + return m_eof?-1:(NSV_NONSYNC_HEADERLEN_BITS- (int)(bs.avail())/8); +} + + + + + + + + + +/* NSV file header +4: NSV_HDR_DWORD +4: length of header in bytes + -- may not be 0 or 0xFFFFFFFF. :) +4: length of file, in bytes (including header - if this is 0 we are invalid) + -- can be 0xFFFFFFFF which means unknown length +4: length of file, in milliseconds (max file length, 24 days or so) + -- can be 0xFFFFFFFF which means unknown length +4: metadata length +4: number of TOC entries allocated +4: number of TOC entries used +mdlen: metadata +TOC_alloc*4:offset in file at time t. +*/ + +void nsv_writeheader(nsv_OutBS &bs, nsv_fileHeader *hdr, unsigned int padto) +{ + if (hdr->toc_alloc < hdr->toc_size) + hdr->toc_alloc=hdr->toc_size; + + if (hdr->toc_ex && hdr->toc_alloc <= hdr->toc_size*2) + hdr->toc_alloc=hdr->toc_size*2+1; + + hdr->header_size = 4+4+4+4+4+hdr->metadata_len+4+4+4*hdr->toc_alloc; + + bs.putbits(32,NSV_HDR_DWORD); + bs.putbits(32,hdr->header_size>padto?hdr->header_size:padto); + if (hdr->file_lenbytes == 0xFFFFFFFF) bs.putbits(32,hdr->file_lenbytes); + else bs.putbits(32,hdr->file_lenbytes+(hdr->header_size>padto?hdr->header_size:padto)); + bs.putbits(32,hdr->file_lenms); + bs.putbits(32,hdr->metadata_len); + bs.putbits(32,hdr->toc_alloc); + bs.putbits(32,hdr->toc_size); + bs.putdata(hdr->metadata_len*8,hdr->metadata); + + unsigned int numtoc=hdr->toc_alloc; + unsigned int numtocused=hdr->toc_size; + unsigned int *toc=hdr->toc; + unsigned int *toc_ex=hdr->toc_ex; + unsigned int numtocused2=(toc_ex && hdr->toc_alloc > hdr->toc_size*2) ? (hdr->toc_size + 1): 0; + + while (numtoc--) + { + if (!numtocused) + { + if (numtocused2) + { + if (--numtocused2 == hdr->toc_size) // signal extended TOC :) + bs.putbits(32,NSV_MAKETYPE('T','O','C','2')); + else + bs.putbits(32,*toc_ex++); + } + else // extra (unused by this implementation but could be used someday so we fill it with 0xFF) space + bs.putbits(32,~0); + } + else if (toc) + { + bs.putbits(32,*toc++); + numtocused--; + } + else bs.putbits(32,0); + } + + unsigned int x; + for (x = hdr->header_size; x < padto; x ++) bs.putbits(8,0); +} + + +int nsv_readheader(nsv_InBS &bs, nsv_fileHeader *hdr) +{ + int s=0; + hdr->metadata=(void*)NULL; + hdr->toc=(unsigned int *)NULL; + hdr->toc_ex=(unsigned int *)NULL; + hdr->header_size=0; + hdr->file_lenbytes=~0; + hdr->file_lenms=~0; + hdr->toc_alloc=0; + hdr->toc_size=0; + hdr->metadata_len=0; + + if (bs.avail()<64) { + return 8- (int)(bs.avail()/8); + } + s+=32; + if (bs.getbits(32) != NSV_HDR_DWORD) + { + bs.seek(-s); + return -1; + }else{ + glCounterNSVf++; + } + s+=32; + unsigned int headersize=bs.getbits(32); + + if (headersize >= 0x20000000) + { + bs.seek(-s); + return -1; + } + if ((unsigned int)bs.avail() < (headersize-4)*8) + { + int l=headersize-4- (int)(bs.avail()/8); + bs.seek(-s); + return l; + } + + s+=32; + unsigned int lenbytes=bs.getbits(32); + s+=32; + unsigned int lenms=bs.getbits(32); + s+=32; + unsigned int metadatalen=bs.getbits(32); + s+=32; + unsigned int tocalloc=bs.getbits(32); + s+=32; + unsigned int tocsize=bs.getbits(32); + + if (tocalloc < tocsize || lenbytes < headersize || tocalloc + metadatalen + s/8 > headersize) + { + bs.seek(-s); + return -1; + } + + void *metadata=NULL; + + if (metadatalen) + { + if (metadatalen > (SIZE_MAX/8)) + { + bs.seek(-s); + return -1; + } + metadata=malloc(metadatalen+1); + if (!metadata) + { + bs.seek(-s); + return -1; + } + s+=metadatalen*8; + bs.getdata(metadatalen*8,metadata); + ((char*)metadata)[metadatalen]=0; + } + + unsigned int *toc=NULL; + unsigned int *toc_ex=NULL; + + if (tocalloc && tocsize < (SIZE_MAX/8)) + { + toc=(unsigned int *)malloc(tocsize * 4 * 2); + if (!toc) + { + free(metadata); + bs.seek(-s); + return -1; + } + unsigned int x; + int bitsread=0; + for (x = 0; x < tocsize; x ++) { toc[x] = bs.getbits(32); bitsread += 32; } + + if (tocalloc > tocsize*2) + { + bitsread += 32; + if (bs.getbits(32) == NSV_MAKETYPE('T','O','C','2')) + { + toc_ex=toc + tocsize; + for (x = 0; x < tocsize; x ++) { toc_ex[x] = bs.getbits(32); bitsread += 32; } + } + } + bs.seek((tocalloc-tocsize)*32 - bitsread); + s+=tocalloc*32; + } + + hdr->header_size=headersize; + if (lenbytes == 0xFFFFFFFF) + hdr->file_lenbytes=lenbytes; + else + hdr->file_lenbytes=lenbytes-headersize; + hdr->file_lenms=lenms; + hdr->metadata=metadata; + hdr->metadata_len=metadatalen; + hdr->toc=toc; + hdr->toc_ex=toc_ex; + hdr->toc_alloc=tocalloc; + hdr->toc_size=tocsize; + + return 0; +} + +char *nsv_getmetadata(void *metadata, char *name) +{ + if (!metadata) return NULL; + char *p=(char*)metadata; + size_t ln=strlen(name); + for (;;) + { + while (p && (*p == ' ' || *p == '\t' || *p == '\n' || *p == '\r')) p++; + if (p || !*p) break; + if (!_strnicmp(p,name,ln) && p[ln]=='=' && p[ln+1] && p[ln+2]) + { + int cnt=0; + char *np=p+ln+1; + char c=*np++; + while (np[cnt] && np[cnt] != c) cnt++; + + char *s=(char*)malloc(cnt+1); + if (!s) return NULL; + memcpy(s,np,cnt); + s[cnt]=0; + return s; + } + + // advance to next item + while (p && *p && *p != '=') p++; + if (!*p++) break; + if (!*p) break; + char c=*p++; + while (p && *p && *p != c) p++; + if (*p) p++; + } + return NULL; +} diff --git a/Src/nsv/nsvlib.h b/Src/nsv/nsvlib.h new file mode 100644 index 00000000..b773d996 --- /dev/null +++ b/Src/nsv/nsvlib.h @@ -0,0 +1,364 @@ +/* +** nsvlib.h - NSV file/bitstream reading/writing interface +** +** Copyright (C) 2001-2002 Nullsoft, Inc. +** +** Confidential Subject to NDA +*/ + +#ifndef _NSVLIB_H_ +#define _NSVLIB_H_ + +/********************************************************************* +** bitstream classes +*/ + +#include "nsvbs.h" + + +/********************************************************************* +** NSV packeting limits +*/ +#define NSV_MAX_AUDIO_LEN 0x8000 // 32kb +#define NSV_MAX_VIDEO_LEN 0x80000 // 512kb +#define NSV_MAX_AUX_LEN 0x8000 // 32kb for each aux stream +#define NSV_MAX_AUXSTREAMS 15 // 15 aux streams maximum + + +/********************************************************************* +** Constants for setting certain metadata items using addHdrMetaData() +*/ +#define METADATANAME_AUTHOR "Author" +#define METADATANAME_TITLE "Title" +#define METADATANAME_COPYRIGHT "Copyright" +#define METADATANAME_COMMENT "Comment" +#define METADATANAME_PROFILE "Profile" +#define METADATANAME_FILEID "File ID" + +/********************************************************************* +** NSV type utility functions/macros +*/ + +/* +** Use NSV_MAKETYPE() to quickly make NSV audio/video/aux types. +** ex: NSV_MAKETYPE('R','G','B','A') +*/ +#define NSV_MAKETYPE(A,B,C,D) ((A) | ((B)<<8) | ((C)<<16) | ((D)<<24)) + +/* +** These functions convert types to and from strings. +*/ + +/* nsv_type_to_string() converts an NSV type to a string. + * out must be at least 5 bytes long. If 't' is not a valid type, + * then out will be set to an empty string + * ex: + * char out[5]; + * nsv_type_to_string(NSV_MAKETYPE('R','G','B','A'),out); + * strcmp(out,"RGBA") == 0 + */ +void nsv_type_to_string(unsigned int t, char *out); + +/* nsv_string_to_type() converts a string to an NSV type. + * Returns 0 if the type is not valid. + * ex: nsv_string_to_type("RGBA") == NSV_MAKETYPE('R','G','B','A') + */ +unsigned int nsv_string_to_type(char *in); + + +/********************************************************************* +** NSV bitstream packeting/unpacketing classes +*/ + + +/* nsv_Packeter is used to packet audio/video/auxiliary data into + * a bitstream. + * + * ex: + * nsv_Packeter p; + * nsv_OutBS bs; + * p.setVidFmt(NSV_MAKETYPE('R','G','B','A'),320,240,30.0); + * p.setAudFmt(NSV_MAKETYPE('P','C','M',' ')); + * for (;;) { + * doEncodeAudioAndVideo(); + * p.setSyncFrame(is_keyframe); + * p.setSyncOffset(av_sync_offset); + * p.setAudio(audio_data,audio_len); + * p.setVideo(video_data,video_len); + * p.clearAuxChannels(); // you can add aux channels if you want + * if (p.packet(bs)) error(); + * int outbuflen; + * void *outbuf=bs.get(&outbuflen); + * fwrite(outbuf,outbuflen,1,fp); // write output + * bs.clear(); // clear bitstream + * } + * + */ + +class nsv_Packeter { + public: + nsv_Packeter(); + ~nsv_Packeter(); + + // init (per file) calls + void setVidFmt(unsigned int vfmt, unsigned int w, unsigned int h, double frt); + void setAudFmt(unsigned int afmt) { audfmt=afmt; } + + // per frame calls + void setSyncFrame(int is_syncframe) { is_sync_frame=is_syncframe; } + void setSyncOffset(int syncoffs) { syncoffset_cur=syncoffs; } + void setAudio(void *a, int a_len) { audio=a; audio_len=a_len; } + void setVideo(void *v, int v_len) { video=v; video_len=v_len; } + int addAuxChannel(unsigned int fmt, void *data, int data_len) // 0 on success + { + if (aux_used >= NSV_MAX_AUXSTREAMS) return -1; + aux[aux_used]=data; + aux_len[aux_used]=data_len; + aux_types[aux_used]=fmt; + aux_used++; + return 0; + } + void clearAuxChannels() { aux_used=0; } + + int packet(nsv_OutBS &bs); // returns 0 on success + + // some utility getting functions + unsigned int getAudFmt() { return audfmt; } + unsigned int getVidFmt() { return vidfmt; } + unsigned int getWidth() { return width; } + unsigned int getHeight() { return height; } + double getFrameRate() { return framerate; } + + private: + unsigned char framerate_idx; + unsigned int vidfmt; + unsigned int audfmt; + unsigned int width; + unsigned int height; + double framerate; + int syncoffset_cur; + + int aux_used; + void *aux[NSV_MAX_AUXSTREAMS]; + int aux_len[NSV_MAX_AUXSTREAMS]; + unsigned int aux_types[NSV_MAX_AUXSTREAMS]; + int is_sync_frame; + void *audio; + int audio_len; + void *video; + int video_len; +}; + + +/* nsv_Unpacketer is used to unpacket a bitstream into audio/video/auxiliary data + * to decode, use an nsv_InBS object with data, and call unpacket(). + * ex: + * nsv_Unpacketer up; + * nsv_InBS in; + * nsv_InBS videoout, audioout; + * up.setVideoOut(&videoout); + * up.setAudioOut(&audioout); + * for (;;) { + * int ret=up.unpacket(in); + * if (ret < 0) break; // eof + * if (ret > 0) add_data_to_bitstream(&in,ret); + * if (!ret) { // got frame + * int vl=videoout.getbits(32); + * int al=videoout.getbits(32); + * char *vd=(char*)videoout.getcurbyteptr(); + * char *ad=(char*)audioout.getcurbyteptr(); + * doDecode(vd,vl,ad,al); + * videoout.seek(vl*8); + * audioout.seek(al*8); + * videoout.compact(); // free memory up + * audioout.compact(); // free memory up + * in.compact(); // free memory up + * } + * } + */ + +class nsv_Unpacketer { + public: + nsv_Unpacketer() { reset(); } + ~nsv_Unpacketer() { } + + void reset(int full=1); // if full, full reset is done. + // if not, then it is a partial reset (ie for seeking) + + // when EOF is set, the unpacketer will fail instead of requesting more data at the + // end; it will also not require that the next frame be available for sync + // (normally it looks ahead to verify data) + void setEof(int eof=1) { m_eof=eof; } + int getEof() { return m_eof; } + + // use these to set where the unpacketer writes the output of each stream + // set to NULL to ignore output of that stream + + void setAudioOut(nsv_InBS *output=NULL) { m_audiobs=output; } + // the format of the audio data written to the output is: + // 32 bits: length of frame + // ? bytes: audio data + // (to read): + // int l=output->getbits(32); + // decode_audio(output->getcurbyteptr(),l); + // output->seek(l*8); + + + void setVideoOut(nsv_InBS *output=NULL) { m_videobs=output; } + // the format of the video data written to the output is: + // 32 bits: length of frame + // ? bytes: video data + // (to read): + // int l=output->getbits(32); + // decode_video(output->getcurbyteptr(),l); + // output->seek(l*8); + + void setAuxOut(nsv_InBS *output=NULL) { m_auxbs=output; } + // the format of the aux data written to the output is: + // 32 bits: length of frame + // 32 bits: type of aux data + // ? bytes: aux data + // (to read): + // int l=output->getbits(32); + // int type=output->getbits(32); + // decode_aux(output->getcurbyteptr(),l); + // output->seek(l*8); + // aux is different than audio/video in that it includes a 32 bit + // type value that is not included in the length. + + + // returns 0 on success, >0 on needs (at least X bytes) more data, + // -1 on error (eof and no header found) + int unpacket(nsv_InBS &bs); + + + // do we have enough sync to determine formats/widths/heights/framerates + int isValid() { return valid; } + + // are we fully synched? + int isSynched() { return synched; } + + // get sync offset from when we first synched up + signed int getSyncOffset() { return (signed int) syncoffset; } + + // get sync offset from current frame (not usually used) + signed int getCurSyncOffset() { return (signed int) syncoffset_cur; } + + // get video, audio, width, height, framerate formats. + unsigned int getVidFmt() { return vidfmt; } + unsigned int getAudFmt() { return audfmt; } + unsigned int getWidth() { return width; } + unsigned int getHeight() { return height; } + double getFrameRate() { return framerate; } + unsigned char getFrameRateIdx() { return framerate_idx; } + + // is current frame a sync frame? + int isSynchFrame() { return is_sync_frame; } + + private: + nsv_InBS *m_audiobs, *m_videobs, *m_auxbs; + int valid; // contents of stream info are valid for syncing + int synched; // turns off anal packet checking + unsigned int vidfmt; + unsigned int audfmt; + unsigned int width; + unsigned int height; + double framerate; + int is_sync_frame; + unsigned char framerate_idx; + int syncoffset; + int syncoffset_cur; + + int m_eof; +}; + + +/********************************************************************* +** NSV file header reading/writing functions +*/ + + +typedef struct { + // header_size is the size of NSV header. nsv_writeheader() and nsv_readheader() + // will set this automatically + unsigned int header_size; + + // file_lenbytes is the size of the NSV bitstream (not including the header size) + // this can be 0xFFFFFFFF to signify unknown length + unsigned int file_lenbytes; + + // file_lenms is the length of the NSV bitstream in milliseconds. + // this can be 0xFFFFFFFF to signify unknown length + unsigned int file_lenms; + + // metadata_len describes the length of the metadata. + unsigned int metadata_len; + + // toc_alloc describes the allocated length of the TOC (in entries). + // set this to zero to use toc_size (recommended). + unsigned int toc_alloc; + + // toc_size describes the used size of the TOC (in entries) + // set this to zero to disable the TOC. When using toc_ex, + // this must be < toc_alloc/2 (if using nsv_writeheader, and + // toc_size is too big, toc_alloc will be grown automatically. + unsigned int toc_size; + + // buffer which contains the TOC. this will be automatically + // allocated when using nsv_readheader(), but you should allocate + // this yourself when using nsv_writeheader() + unsigned int *toc; + + // if used, contains time pairs (in frames) for the offset. this will be + // automatically allocated when using nsv_readheader(), but you should allocate + // this yourself when using nsv_writeheader() + // DO NOT FREE THIS VALUE IF IT WAS ALLOCATED FROM NSV_READHEADER. :) + // (it is just an extension of toc, which should be freed with free()) + unsigned int *toc_ex; + + // buffer which contains metadata. allocated when using nsv_readheader(), + // but you should allocate this yourself when using nsv_writeheader() + // note that nsv_readheader() will NULL terminate this buffer. + void *metadata; + +} nsv_fileHeader; + +// nsv_writeheader() writes the NSV file header to the bitstream bs. +// the NSV file header will be at LEAST padto bytes long (usually +// you will leave padto to 0) +void nsv_writeheader(nsv_OutBS &bs, nsv_fileHeader *hdr, unsigned int padto); + +// nsv_readheader() reads an NSV file header from a bitstream bs. +// if the return value is less than zero, then there is no NSV +// file header in bs. if the return value is zero, the NSV file +// header was succesfully read. if the return value is positive, +// then at least that many more bytes are needed to decode the +// header. +// ex: +// nsv_InBS bs; +// nsv_fileHeader hdr; +// for (;;) { +// int ret=nsv_readheader(bs,&hdr); +// if (ret<=0) break; +// addBytesToBs(bs,ret); +// } +// if (hdr.header_size) { we_got_valid_header(&hdr); } +// + +int nsv_readheader(nsv_InBS &bs, nsv_fileHeader *hdr); + + +// nsv_getmetadata() retrieves a metadata item from the metadata +// block. if that item is not found, NULL is returned. +// Note that the value returned by nsv_getmetadata() has been +// malloc()'d, and you must free() it when you are done. +// ex: +// char *v=nsv_getmetadata(hdr.metadata,"TITLE"); +// if (v) printf("title=%s\n",v); +// free(v); +// + +char *nsv_getmetadata(void *metadata, char *name); + + +#endif//_NSVLIB_H_ diff --git a/Src/nsv/nsvplay/IDataReader.h b/Src/nsv/nsvplay/IDataReader.h new file mode 100644 index 00000000..1ae234bf --- /dev/null +++ b/Src/nsv/nsvplay/IDataReader.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/about.h b/Src/nsv/nsvplay/about.h new file mode 100644 index 00000000..a04e48d6 --- /dev/null +++ b/Src/nsv/nsvplay/about.h @@ -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_
\ No newline at end of file diff --git a/Src/nsv/nsvplay/audiostub.cpp b/Src/nsv/nsvplay/audiostub.cpp new file mode 100644 index 00000000..83794693 --- /dev/null +++ b/Src/nsv/nsvplay/audiostub.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/audiostub.h b/Src/nsv/nsvplay/audiostub.h new file mode 100644 index 00000000..df9833fd --- /dev/null +++ b/Src/nsv/nsvplay/audiostub.h @@ -0,0 +1,8 @@ +#ifndef _AUDIOSTUB_H_ +#define _AUDIOSTUB_H_ + +#include "../dec_if.h" + +IAudioOutput *PCMOUT_CREATE(unsigned int outfmt[8]); + +#endif
\ No newline at end of file diff --git a/Src/nsv/nsvplay/decoders.cpp b/Src/nsv/nsvplay/decoders.cpp new file mode 100644 index 00000000..82cba2bd --- /dev/null +++ b/Src/nsv/nsvplay/decoders.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/main.cpp b/Src/nsv/nsvplay/main.cpp new file mode 100644 index 00000000..76f2f5c1 --- /dev/null +++ b/Src/nsv/nsvplay/main.cpp @@ -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; +} + diff --git a/Src/nsv/nsvplay/main.h b/Src/nsv/nsvplay/main.h new file mode 100644 index 00000000..a941cb01 --- /dev/null +++ b/Src/nsv/nsvplay/main.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/mp3stub.cpp b/Src/nsv/nsvplay/mp3stub.cpp new file mode 100644 index 00000000..72df533c --- /dev/null +++ b/Src/nsv/nsvplay/mp3stub.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/mp3stub.h b/Src/nsv/nsvplay/mp3stub.h new file mode 100644 index 00000000..11d6a148 --- /dev/null +++ b/Src/nsv/nsvplay/mp3stub.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/nsv_logo.bmp b/Src/nsv/nsvplay/nsv_logo.bmp Binary files differnew file mode 100644 index 00000000..bcdeaf9d --- /dev/null +++ b/Src/nsv/nsvplay/nsv_logo.bmp diff --git a/Src/nsv/nsvplay/nsvdecode.cpp b/Src/nsv/nsvplay/nsvdecode.cpp new file mode 100644 index 00000000..8d8bbcb7 --- /dev/null +++ b/Src/nsv/nsvplay/nsvdecode.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/nsvplay.dsp b/Src/nsv/nsvplay/nsvplay.dsp new file mode 100644 index 00000000..4b49c0b1 --- /dev/null +++ b/Src/nsv/nsvplay/nsvplay.dsp @@ -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 diff --git a/Src/nsv/nsvplay/nsvplay.dsw b/Src/nsv/nsvplay/nsvplay.dsw new file mode 100644 index 00000000..2fafc97e --- /dev/null +++ b/Src/nsv/nsvplay/nsvplay.dsw @@ -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> +{{{ +}}} + +############################################################################### + diff --git a/Src/nsv/nsvplay/nsvplay.rc b/Src/nsv/nsvplay/nsvplay.rc new file mode 100644 index 00000000..43e1fde4 --- /dev/null +++ b/Src/nsv/nsvplay/nsvplay.rc @@ -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 + diff --git a/Src/nsv/nsvplay/readers.cpp b/Src/nsv/nsvplay/readers.cpp new file mode 100644 index 00000000..ff048e70 --- /dev/null +++ b/Src/nsv/nsvplay/readers.cpp @@ -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; +} + diff --git a/Src/nsv/nsvplay/resource.h b/Src/nsv/nsvplay/resource.h new file mode 100644 index 00000000..57b058a7 --- /dev/null +++ b/Src/nsv/nsvplay/resource.h @@ -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 diff --git a/Src/nsv/nsvplay/subtitles.cpp b/Src/nsv/nsvplay/subtitles.cpp new file mode 100644 index 00000000..e599bf14 --- /dev/null +++ b/Src/nsv/nsvplay/subtitles.cpp @@ -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); +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/subtitles.h b/Src/nsv/nsvplay/subtitles.h new file mode 100644 index 00000000..f4951a23 --- /dev/null +++ b/Src/nsv/nsvplay/subtitles.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/vid_ddraw.cpp b/Src/nsv/nsvplay/vid_ddraw.cpp new file mode 100644 index 00000000..b4c0af68 --- /dev/null +++ b/Src/nsv/nsvplay/vid_ddraw.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/vid_ddraw.h b/Src/nsv/nsvplay/vid_ddraw.h new file mode 100644 index 00000000..0feea9f5 --- /dev/null +++ b/Src/nsv/nsvplay/vid_ddraw.h @@ -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 diff --git a/Src/nsv/nsvplay/vid_overlay.cpp b/Src/nsv/nsvplay/vid_overlay.cpp new file mode 100644 index 00000000..efe32661 --- /dev/null +++ b/Src/nsv/nsvplay/vid_overlay.cpp @@ -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; +}
\ No newline at end of file diff --git a/Src/nsv/nsvplay/vid_overlay.h b/Src/nsv/nsvplay/vid_overlay.h new file mode 100644 index 00000000..f4772e69 --- /dev/null +++ b/Src/nsv/nsvplay/vid_overlay.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/video.cpp b/Src/nsv/nsvplay/video.cpp new file mode 100644 index 00000000..8a6532c7 --- /dev/null +++ b/Src/nsv/nsvplay/video.cpp @@ -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(); +} diff --git a/Src/nsv/nsvplay/video.h b/Src/nsv/nsvplay/video.h new file mode 100644 index 00000000..535cee24 --- /dev/null +++ b/Src/nsv/nsvplay/video.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/vp3stub.cpp b/Src/nsv/nsvplay/vp3stub.cpp new file mode 100644 index 00000000..8f6776c7 --- /dev/null +++ b/Src/nsv/nsvplay/vp3stub.cpp @@ -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; +} diff --git a/Src/nsv/nsvplay/vp3stub.h b/Src/nsv/nsvplay/vp3stub.h new file mode 100644 index 00000000..dacc9663 --- /dev/null +++ b/Src/nsv/nsvplay/vp3stub.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/vp5stub.cpp b/Src/nsv/nsvplay/vp5stub.cpp new file mode 100644 index 00000000..9c49c359 --- /dev/null +++ b/Src/nsv/nsvplay/vp5stub.cpp @@ -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; +} diff --git a/Src/nsv/nsvplay/vp5stub.h b/Src/nsv/nsvplay/vp5stub.h new file mode 100644 index 00000000..55124650 --- /dev/null +++ b/Src/nsv/nsvplay/vp5stub.h @@ -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
\ No newline at end of file diff --git a/Src/nsv/nsvplay/wndmenu.h b/Src/nsv/nsvplay/wndmenu.h new file mode 100644 index 00000000..17677901 --- /dev/null +++ b/Src/nsv/nsvplay/wndmenu.h @@ -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_
\ No newline at end of file diff --git a/Src/nsv/svc_nsvFactory.cpp b/Src/nsv/svc_nsvFactory.cpp new file mode 100644 index 00000000..dda7ce0e --- /dev/null +++ b/Src/nsv/svc_nsvFactory.cpp @@ -0,0 +1 @@ +#include "svc_nsvFactory.h" diff --git a/Src/nsv/svc_nsvFactory.h b/Src/nsv/svc_nsvFactory.h new file mode 100644 index 00000000..ffc0ca66 --- /dev/null +++ b/Src/nsv/svc_nsvFactory.h @@ -0,0 +1,52 @@ +#ifndef NULLSOFT_NSV_SVC_NSVFACTORY_H +#define NULLSOFT_NSV_SVC_NSVFACTORY_H + +#include <bfc/Dispatch.h> +#include <bfc/platform/types.h> +#include <api/service/services.h> +class IAudioDecoder; +class IVideoDecoder; +class IAudioOutput; + +/* + ********************************************************************** + * Important Note! * + * * + * * + * Objects created by this class are allocated using api_memmgr * + * You MUST call api_memmgr::Delete() to delete these objects. * + * Do not use the C++ delete operator * + * as the memory was allocated with a different heap / malloc arena * + * * + ********************************************************************** + */ +class svc_nsvFactory : public Dispatchable +{ +protected: + svc_nsvFactory(){} + ~svc_nsvFactory(){} +public: + virtual IAudioDecoder *CreateAudioDecoder(FOURCC format, IAudioOutput **output); + IVideoDecoder *CreateVideoDecoder(int w, int h, double framerate, unsigned int fmt, int *flip); + +public: + DISPATCH_CODES + { + SVC_NSVFACTORY_CREATEAUDIODECODER=10, + SVC_NSVFACTORY_CREATEVIDEODECODER=20, + }; + + static FOURCC getServiceType() { return WaSvc::NSVFACTORY; } +}; + +inline IAudioDecoder *svc_nsvFactory::CreateAudioDecoder(FOURCC format, IAudioOutput **output) +{ + return _call(SVC_NSVFACTORY_CREATEAUDIODECODER, (IAudioDecoder *)0, format, output); +} + +inline IVideoDecoder *svc_nsvFactory::CreateVideoDecoder(int w, int h, double framerate, unsigned int fmt, int *flip) +{ + return _call(SVC_NSVFACTORY_CREATEVIDEODECODER, (IVideoDecoder *)0, w, h, framerate, fmt, flip); +} + +#endif |