diff --git a/programs/media/Fplay/audio.c b/programs/media/Fplay/audio.c new file mode 100644 index 0000000000..ed166d575a --- /dev/null +++ b/programs/media/Fplay/audio.c @@ -0,0 +1,240 @@ + +#include +#include +#include +#include + +#include +#include +#include "sound.h" +#include "fplay.h" + + +astream_t astream; + +static SNDBUF hBuff; + +extern volatile uint32_t status; + +void audio_thread(void *param); + +void spinlock_lock(volatile uint32_t *val) +{ + uint32_t tmp; + + __asm__ __volatile__ ( +"0:\n\t" + "mov %0, %1\n\t" + "testl %1, %1\n\t" + "jz 1f\n\t" + + "movl $68, %%eax\n\t" + "movl $1, %%ebx\n\t" + "int $0x40\n\t" + "jmp 0b\n\t" +"1:\n\t" + "incl %1\n\t" + "xchgl %0, %1\n\t" + "testl %1, %1\n\t" + "jnz 0b\n" + : "+m" (*val), "=&r"(tmp) + ::"eax","ebx" ); +} + +static int snd_format; +int sample_rate; + +int init_audio(int format) +{ + int err; + int version =-1; + char *errstr; + + if((err = InitSound(&version)) !=0 ) + { + errstr = "Sound service not installed\n\r"; + goto exit_whith_error; + } + printf("sound version 0x%x\n", version); + + if( (SOUND_VERSION>(version&0xFFFF)) || + (SOUND_VERSION<(version >> 16))) + { + errstr = "Sound service version mismatch\n\r"; + goto exit_whith_error; + } + + snd_format = format; + + asm volatile ( "xchgw %bx, %bx"); + + create_thread(audio_thread, 0, 163840); + + return 1; + +exit_whith_error: + + printf(errstr); + return 0; +}; + +static uint64_t samples_lost; +static double audio_delta; + +double get_master_clock() +{ + double tstamp; + + GetTimeStamp(hBuff, &tstamp); + return tstamp - audio_delta; +}; + + +void audio_thread(void *param) +{ + SND_EVENT evnt; + int buffsize; + int samples; + int err; + char *errstr; + + + if((err = CreateBuffer(snd_format|PCM_RING,0, &hBuff)) != 0) + { + errstr = "Cannot create sound buffer\n\r"; + goto exit_whith_error; + }; + + SetVolume(hBuff,-1000,-1000); + + if((err = GetBufferSize(hBuff, &buffsize)) != 0) + { + errstr = "Cannot get buffer size\n\r"; + goto exit_whith_error; + }; + + buffsize = buffsize/2; + + samples = buffsize/4; + + while( (astream.count < buffsize*2) && + (status != 0) ) + yield(); + + spinlock_lock(&astream.lock); + { + SetBuffer(hBuff, astream.buffer, 0, buffsize); + astream.count -= buffsize; + if(astream.count) + memcpy(astream.buffer, astream.buffer+buffsize, astream.count); + spinlock_unlock(&astream.lock); + }; + + if((err = PlayBuffer(hBuff, 0)) !=0 ) + { + errstr = "Cannot play buffer\n\r"; + goto exit_whith_error; + }; + + +#ifdef BLACK_MAGIC_SOUND + + while( status != 0) + { + uint32_t offset; + + GetNotify(&evnt); + + if(evnt.code != 0xFF000001) + { + printf("invalid event code %d\n\r", evnt.code); + continue; + } + + if(evnt.stream != hBuff) + { + printf("invalid stream %x hBuff= %x\n\r", + evnt.stream, hBuff); + continue; + } + + GetTimeStamp(hBuff, &audio_delta); + samples_lost = audio_delta*sample_rate/1000; + + offset = evnt.offset; + + spinlock_lock(&astream.lock); + { + SetBuffer(hBuff, astream.buffer, offset, buffsize); + astream.count -= buffsize; + if(astream.count) + memcpy(astream.buffer, astream.buffer+buffsize, astream.count); + spinlock_unlock(&astream.lock); + }; + break; + }; +#endif + + printf("initial audio delta %f\n", audio_delta); + + while( status != 0) + { + uint32_t offset; + double event_stamp, wait_stamp; + int too_late = 0; + + GetNotify(&evnt); + + if(evnt.code != 0xFF000001) + { + printf("invalid event code %d\n\r", evnt.code); + continue; + } + + if(evnt.stream != hBuff) + { + printf("invalid stream %x hBuff= %x\n\r", + evnt.stream, hBuff); + continue; + }; + + GetTimeStamp(hBuff, &event_stamp); + + offset = evnt.offset; + + while( (astream.count < buffsize) && + (status != 0) ) + { + yield(); + GetTimeStamp(hBuff, &wait_stamp); + if( (wait_stamp - event_stamp) > + samples*1500/sample_rate ) + { + samples_lost+= samples; + audio_delta = (double)samples_lost*1000/sample_rate; +// printf("audio delta %f\n", audio_delta); + too_late = 1; + break; + } + }; + + if((too_late == 1) || (status == 0)) + continue; + + spinlock_lock(&astream.lock); + SetBuffer(hBuff, astream.buffer, offset, buffsize); + astream.count -= buffsize; + if(astream.count) + memcpy(astream.buffer, astream.buffer+buffsize, astream.count); + spinlock_unlock(&astream.lock); + } + + return; + +exit_whith_error: + + printf(errstr); + return ; + +}; + diff --git a/programs/media/Fplay/fplay.c b/programs/media/Fplay/fplay.c new file mode 100644 index 0000000000..f108a433be --- /dev/null +++ b/programs/media/Fplay/fplay.c @@ -0,0 +1,256 @@ + +#include + +#include +#include +#include +#include + +#include +#include +#include +#include "sound.h" +#include "fplay.h" + +volatile uint32_t status = 1; + +uint32_t win_width, win_height; + +void decoder(); + +AVFormatContext *pFormatCtx; +AVCodecContext *pCodecCtx; +AVCodecContext *aCodecCtx; +AVCodec *pCodec; +AVCodec *aCodec; +AVFrame *pFrame; +int videoStream; +int audioStream; + +int have_sound = 0; + + +uint8_t *decoder_buffer; +extern int sample_rate; + +int main( int argc, char *argv[]) +{ + int i; + + if(argc < 2) { + printf("Please provide a movie file\n"); + return -1; + } + + /* register all codecs, demux and protocols */ + + avcodec_register_all(); + avdevice_register_all(); + av_register_all(); + + + // Open video file + if(av_open_input_file(&pFormatCtx, argv[1], NULL, 0, NULL)!=0) + { + printf("Cannot open file %s\n\r", argv[1]); + return -1; // Couldn't open file + }; + +// __asm__ __volatile__("int3"); + + // Retrieve stream information + if(av_find_stream_info(pFormatCtx)<0) + { + printf("Cannot find streams\n\r"); + return -1; + }; + + // __asm__ __volatile__("int3"); + + // dump_format(pFormatCtx, 0, argv[1], 0); + + // Find the first video stream + videoStream=-1; + audioStream=-1; + for(i=0; i < pFormatCtx->nb_streams; i++) + { + if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_VIDEO + && videoStream < 0) + { + videoStream=i; + video_time_base = pFormatCtx->streams[i]->time_base; + + } + if(pFormatCtx->streams[i]->codec->codec_type==CODEC_TYPE_AUDIO && + audioStream < 0) + { + audioStream=i; + } + } + + if(videoStream==-1) + { + printf("Video stream not detected\n\r"); + return -1; // Didn't find a video stream + } + + + // Get a pointer to the codec context for the video stream + pCodecCtx=pFormatCtx->streams[videoStream]->codec; + + aCodecCtx=pFormatCtx->streams[audioStream]->codec; + + // Find the decoder for the video stream + pCodec=avcodec_find_decoder(pCodecCtx->codec_id); + if(pCodec==NULL) { + printf("Unsupported video codec!\n"); + return -1; // Codec not found + } + // Open codec + if(avcodec_open(pCodecCtx, pCodec) < 0) + { + printf("Cannot open video codec\n\r"); + return -1; // Could not open codec + }; + + if (aCodecCtx->channels > 0) + aCodecCtx->request_channels = FFMIN(2, aCodecCtx->channels); + else + aCodecCtx->request_channels = 2; + + aCodec = avcodec_find_decoder(aCodecCtx->codec_id); + + if(aCodec) + { + if(avcodec_open(aCodecCtx, aCodec) >= 0 ) + { + WAVEHEADER whdr; + int fmt; + + printf("audio stream rate %d channels %d\n", + aCodecCtx->sample_rate, aCodecCtx->channels); + + whdr.riff_id = 0x46464952; + whdr.riff_format = 0x45564157; + whdr.wFormatTag = 0x01; + whdr.nSamplesPerSec = aCodecCtx->sample_rate; + whdr.nChannels = aCodecCtx->channels; + whdr.wBitsPerSample = 16; + + sample_rate = aCodecCtx->sample_rate; + + fmt = test_wav(&whdr); + + if( init_audio(fmt) ) + { + decoder_buffer = (uint8_t*)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE); + if( decoder_buffer != NULL ) + { + astream.lock = 0; + astream.count = 0; + astream.buffer = (char *)av_mallocz(AVCODEC_MAX_AUDIO_FRAME_SIZE*8); + if( astream.buffer != NULL ) + have_sound = 1; + else + av_free(decoder_buffer); + } + if( have_sound == 0) + { + printf("Not enough memory for audio buffers\n"); + } + } + } + else printf("Cannot open audio codec\n\r"); + } + else printf("Unsupported audio codec!\n"); + + if( !init_video(pCodecCtx)) + return 0; + + // Assign appropriate parts of buffer to image planes in pFrameRGB + // Note that pFrameRGB is an AVFrame, but AVFrame is a superset + // of AVPicture + + // __asm__ __volatile__("int3"); + + decoder(); + + status = 0; + + + // Free the YUV frame + av_free(pFrame); + +//__asm__ __volatile__("int3"); + + // Close the codec + // avcodec_close(pCodecCtx); + + // Close the video file + // av_close_input_file(pFormatCtx); + +//__asm__ __volatile__("int3"); + + return 0; +} + +void decoder() +{ + AVPacket packet; + + while(av_read_frame(pFormatCtx, &packet) >=0 ) + { + if(packet.stream_index==videoStream) + { + decode_video(pCodecCtx, &packet); + } + else if( (packet.stream_index == audioStream) && + (have_sound != 0) ) + { + uint8_t *audio_data; + int audio_size; + int len; + int data_size=0; + + audio_data = packet.data; + audio_size = packet.size; + + while(audio_size > 0) + { + data_size = AVCODEC_MAX_AUDIO_FRAME_SIZE; + + len = avcodec_decode_audio2(aCodecCtx,(int16_t*)decoder_buffer, + &data_size, audio_data, audio_size); + + if(len >= 0) + { + audio_data += len; + audio_size -= len; + + while((astream.count + data_size) > + AVCODEC_MAX_AUDIO_FRAME_SIZE*8) + { + yield(); + } + spinlock_lock(&astream.lock); + memcpy(astream.buffer+astream.count, decoder_buffer, data_size); + astream.count += data_size; + spinlock_unlock(&astream.lock); + } + else audio_size = 0; + } + } + // Free the packet that was allocated by av_read_frame + av_free_packet(&packet); + }; +}; + + +__int64 _lseeki64(int fd, __int64 offset, int origin ) +{ + int off = offset; + return lseek(fd, off, origin); +} + + + diff --git a/programs/media/Fplay/fplay.h b/programs/media/Fplay/fplay.h new file mode 100644 index 0000000000..bb40c4f8ae --- /dev/null +++ b/programs/media/Fplay/fplay.h @@ -0,0 +1,79 @@ + +#define BLACK_MAGIC_SOUND +#define BLACK_MAGIC_VIDEO + +typedef struct +{ + volatile uint32_t lock; + char *buffer; + volatile uint32_t count; +}astream_t; + +typedef struct +{ + unsigned int code; + unsigned int sender; + unsigned int stream; + unsigned int offset; + unsigned int size; + unsigned int unused[2]; +}SND_EVENT; + +extern astream_t astream; +extern AVRational video_time_base; + +int init_audio(int format); +int init_video(AVCodecContext *ctx); +int decode_video(AVCodecContext *ctx, AVPacket *pkt); +double get_master_clock(); + + +int create_thread(void (*proc)(void *param), void *param, int stack_size); + +void spinlock_lock(volatile uint32_t *val); + +static inline void spinlock_unlock(volatile uint32_t *val) +{ + *val = 0; +} + +static inline void GetNotify(void *event) +{ + __asm__ __volatile__ ( + "int $0x40" + ::"a"(68),"b"(14),"c"(event)); +} + +static inline uint32_t check_os_event() +{ + uint32_t val; + __asm__ __volatile__( + "int $0x40" + :"=a"(val) + :"a"(11)); + return val; +}; + +static inline uint32_t get_os_button() +{ + uint32_t val; + __asm__ __volatile__( + "int $0x40" + :"=a"(val) + :"a"(17)); + return val>>8; +}; + +static inline void yield(void) +{ + __asm__ __volatile__( + "int $0x40" + ::"a"(68), "b"(1)); +}; + +static inline void delay(uint32_t time) +{ + __asm__ __volatile__( + "int $0x40" + ::"a"(5), "b"(time)); +}; diff --git a/programs/media/Fplay/sound.h b/programs/media/Fplay/sound.h new file mode 100644 index 0000000000..c61d73498f --- /dev/null +++ b/programs/media/Fplay/sound.h @@ -0,0 +1,138 @@ + +#ifndef _SOUND_H_ +#define _SOUND_H_ + +#ifdef __cplusplus +extern "C" +{ +#endif + +#define SOUND_VERSION 0x0101 +#define PCM_ALL 0 + +#define PCM_OUT 0x08000000 +#define PCM_RING 0x10000000 +#define PCM_STATIC 0x20000000 +#define PCM_FLOAT 0x40000000 +#define PCM_FILTER 0x80000000 + +#define PCM_2_16_48 1 +#define PCM_1_16_48 2 +#define PCM_2_16_44 3 +#define PCM_1_16_44 4 +#define PCM_2_16_32 5 +#define PCM_1_16_32 6 +#define PCM_2_16_24 7 +#define PCM_1_16_24 8 +#define PCM_2_16_22 9 +#define PCM_1_16_22 10 +#define PCM_2_16_16 11 +#define PCM_1_16_16 12 +#define PCM_2_16_12 13 +#define PCM_1_16_12 14 +#define PCM_2_16_11 15 +#define PCM_1_16_11 16 +#define PCM_2_16_8 17 +#define PCM_1_16_8 18 +#define PCM_2_8_48 19 +#define PCM_1_8_48 20 +#define PCM_2_8_44 21 +#define PCM_1_8_44 22 +#define PCM_2_8_32 23 +#define PCM_1_8_32 24 +#define PCM_2_8_24 25 +#define PCM_1_8_24 26 +#define PCM_2_8_22 27 +#define PCM_1_8_22 28 +#define PCM_2_8_16 29 +#define PCM_1_8_16 30 +#define PCM_2_8_12 31 +#define PCM_1_8_12 32 +#define PCM_2_8_11 33 +#define PCM_1_8_11 34 +#define PCM_2_8_8 35 +#define PCM_1_8_8 36 + +#define SRV_GETVERSION 0 +#define SND_CREATE_BUFF 1 +#define SND_DESTROY_BUFF 2 +#define SND_SETFORMAT 3 +#define SND_GETFORMAT 4 +#define SND_RESET 5 +#define SND_SETPOS 6 +#define SND_GETPOS 7 +#define SND_SETBUFF 8 +#define SND_OUT 9 +#define SND_PLAY 10 +#define SND_STOP 11 +#define SND_SETVOLUME 12 +#define SND_GETVOLUME 13 +#define SND_SETPAN 14 +#define SND_GETPAN 15 +#define SND_GETBUFFSIZE 16 +#define SND_GETFREESPACE 17 +#define SND_SETTIMEBASE 18 +#define SND_GETTIMESTAMP 19 + +#define PLAY_SYNC 0x80000000 + +typedef struct +{ + unsigned int riff_id; + unsigned int riff_size; + unsigned int riff_format; + + unsigned int fmt_id; + unsigned int fmt_size; + + unsigned short int wFormatTag; + unsigned short int nChannels; + unsigned int nSamplesPerSec; + unsigned int nAvgBytesPerSec; + unsigned short int nBlockAlign; + unsigned short int wBitsPerSample; + unsigned int data_id; + unsigned int data_size; +} WAVEHEADER; + +typedef unsigned int SNDBUF; + +int _stdcall InitSound(int *version); + +int _stdcall CreateBuffer(unsigned int format,int size,SNDBUF *buf); +int _stdcall DestroyBuffer(SNDBUF hBuff); + +int _stdcall SetFormat(SNDBUF hBuff, unsigned int format); +int _stdcall GetFormat(SNDBUF hBuff, unsigned int *format); + +int _stdcall ResetBuffer(SNDBUF hBuff, unsigned int flags); +int _stdcall SetBufferPos(SNDBUF hBuff, int offset); +int _stdcall GetBufferPos(SNDBUF hBuff, int *offset); +int _stdcall GetBufferSize(SNDBUF hBuff, int *size); +int _stdcall GetBufferFree(SNDBUF hBuff, int *free); + +int _stdcall SetBuffer(SNDBUF hBuff,void* buff, + int offs, int size); +int _stdcall WaveOut(SNDBUF hBuff,void *buff, int size); +int _stdcall PlayBuffer(SNDBUF hBuff,unsigned int flags); +int _stdcall StopBuffer(SNDBUF hBuff); + +int _stdcall SetVolume(SNDBUF hBuff, int left, int right); +int _stdcall GetVolume(SNDBUF hBuff, int *left, int *right); +int _stdcall SetPan(SNDBUF hBuff, int pan); +int _stdcall GetPan(SNDBUF hBuff, int *pan); + +int _stdcall GetMasterVol(int* vol); +int _stdcall SetMasterVol(int vol); + +int _stdcall SetTimeBase(SNDBUF hBuff, double base); +int _stdcall GetTimeStamp(SNDBUF hBuff, double *stamp); + +unsigned int _stdcall test_wav(WAVEHEADER *hdr); + +#ifdef __cplusplus +extern "C" +} +#endif + +#endif //_SOUND_H_ diff --git a/programs/media/Fplay/video.c b/programs/media/Fplay/video.c new file mode 100644 index 0000000000..a18315b6fb --- /dev/null +++ b/programs/media/Fplay/video.c @@ -0,0 +1,272 @@ + +#include +#include +#include +#include + +#include "fplay.h" + +void video_thread(void *param); + +void draw_bitmap(void *bitmap, int x, int y, int w, int h) +{ + __asm__ __volatile__( + "int $0x40" + ::"a"(7), "b"(bitmap), + "c"((w << 16) | h), + "d"((x << 16) | y)); +} + +typedef struct +{ + AVFrame *frame; + uint8_t *buffer; + double pts; + volatile int ready; +}vframe_t; + +vframe_t frames[8]; + +struct SwsContext *cvt_ctx; + +int vfx = 0; +int dfx = 0; + +int width; +int height; + +AVRational video_time_base; +AVFrame *Frame; + +int init_video(AVCodecContext *ctx) +{ + uint32_t size; + int i; + + width = ctx->width; + height = ctx->height; + + printf("w = %d h = %d\n\r", width, height); + + Frame = avcodec_alloc_frame(); + if ( Frame == NULL ) + { + printf("Cannot alloc video buffer\n\r"); + return 0; + }; + + cvt_ctx = sws_getContext( + ctx->width, + ctx->height, + ctx->pix_fmt, + ctx->width, + ctx->height, + PIX_FMT_BGR24, + SWS_BILINEAR, + NULL, NULL, NULL); + if(cvt_ctx == NULL) + { + printf("Cannot initialize the conversion context!\n"); + return 0; + } + + size = avpicture_get_size(PIX_FMT_RGB24, ctx->width, ctx->height); + + for( i=0; i < 8; i++) + { + AVFrame *frame; + + frame = avcodec_alloc_frame(); + + if( frame ) + { + uint8_t *buffer = (uint8_t*)av_malloc(size); + + if( buffer ) + { + avpicture_fill((AVPicture *)frame, buffer, PIX_FMT_BGR24, + ctx->width, ctx->height); + + frames[i].frame = frame; + frames[i].buffer = buffer; + frames[i].pts = 0; + frames[i].ready = 0; + continue; + }; + }; + printf("Cannot alloc frame buffer\n\r"); + return 0; + }; + + create_thread(video_thread, 0, 163840); + + return 1; +}; + +int frameFinished=0; + +int decode_video(AVCodecContext *ctx, AVPacket *pkt) +{ + double pts; + AVPicture pict; + const uint8_t *data[4]; + double av_time; + + // __asm__("int3"); + + if(avcodec_decode_video(ctx, Frame, &frameFinished, + pkt->data, pkt->size) <= 0) + printf("decode error\n"); + + if( pkt->dts == AV_NOPTS_VALUE && + Frame->reordered_opaque != AV_NOPTS_VALUE) + pts= Frame->reordered_opaque; + else if(pkt->dts != AV_NOPTS_VALUE) + pts= pkt->dts; + else + pts= 0; + + pts *= av_q2d(video_time_base); + + if(frameFinished) + { + while( frames[dfx].ready != 0 ) + yield(); + + pict.data[0] = frames[dfx].frame->data[0]; + pict.data[1] = frames[dfx].frame->data[1]; + pict.data[2] = frames[dfx].frame->data[2]; + pict.data[3] = NULL; + + pict.linesize[0] = frames[dfx].frame->linesize[0]; + pict.linesize[1] = frames[dfx].frame->linesize[1]; + pict.linesize[2] = frames[dfx].frame->linesize[2]; + pict.linesize[3] = 0; + + data[0] = Frame->data[0]; + data[1] = Frame->data[1]; + data[2] = Frame->data[2]; + data[3] = NULL; + + sws_scale(cvt_ctx, data, Frame->linesize, 0, ctx->height, + pict.data, pict.linesize); + + frames[dfx].pts = pts*1000.0; + frames[dfx].ready = 1; + + dfx++; + dfx&= 7; + }; + + return 0; +} + +extern volatile uint32_t status; + +typedef unsigned int color_t; +typedef unsigned int count_t; +typedef unsigned int u32_t; + +static void DrawWindow(int x, int y, int w, int h, char *name, + color_t workcolor, u32_t style) +{ + + __asm__ __volatile__( + "int $0x40" + ::"a"(0), + "b"((x << 16) | (w & 0xFFFF)), + "c"((y << 16) | (h & 0xFFFF)), + "d"((style << 24) | (workcolor & 0xFFFFFF)), + "D"(name)); +}; + + +static int check_events() +{ + int ev; + + ev = check_os_event(); + + switch(ev) + { + case 1: + DrawWindow(10, 10, width+9, height+26, NULL, 0x000000,0x74); + break; + + case 3: + if(get_os_button()==1) + status = 0; + break; + }; + return 1; +} + + +extern char __cmdline[]; + +void video_thread(void *param) +{ + char *path; + + path = strrchr(__cmdline,'/')+1; + + DrawWindow(10, 10, width+9, height+26, path, 0x000000,0x74); + + while( status != 0) + { + double ctime; + double fdelay; + + check_events(); + + if(frames[vfx].ready == 1 ) + { + ctime = get_master_clock(); + fdelay = (frames[vfx].pts - ctime); +// printf("ctime %f pts %f delay %f\n", +// ctime, frames[vfx].pts, fdelay); + + if(fdelay < 0.0 ) + { + int next_vfx; + fdelay = 0; + next_vfx = (vfx+1) & 7; + if( frames[next_vfx].ready == 1 ) + { + if(frames[next_vfx].pts <= ctime) + { + frames[vfx].ready = 0; // skip this frame + vfx++; + vfx&= 7; + } + else + { + if( (frames[next_vfx].pts - ctime) < + ( ctime - frames[vfx].pts) ) + { + frames[vfx].ready = 0; // skip this frame + vfx++; + vfx&= 7; + fdelay = (frames[next_vfx].pts - ctime); + } + } + }; + }; + + if(fdelay > 10.0) + { + delay( (uint32_t)(fdelay/10.0)); + }; + + draw_bitmap(frames[vfx].buffer, 0, 0, width, height); + frames[vfx].ready = 0; + vfx++; + vfx&= 7; + } + else + { + yield(); + }; + }; +}; +